--- /dev/null
+/******************************************************************************
+ * @file parse.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 anonymous_aggregate_owner_id = 1;
+
+static int pending_return_jump = 0;
+static int local_static_id = 1;
+
+#define MAX_PENDING_STATEMENT_LABELS 256
+
+static int pending_statement_labels[MAX_PENDING_STATEMENT_LABELS];
+static int pending_statement_label_count = 0;
+
+static void flush_pending_statement_labels (void);
+
+#define DATA_NONE 0
+#define DATA_VOID 4
+#define DATA_PTR 4
+
+static int current_return_label = 0;
+
+#define DATA_CHAR (1 | (1 << 5))
+#define DATA_SHORT (2 | (1 << 6))
+#define DATA_INT (4 | (1 << 7))
+#define DATA_LONG (4 | (1 << 8))
+#define DATA_LLONG (8 | (1 << 9))
+#define DATA_FLOAT (4 | (1 << 10))
+#define DATA_DOUBLE (8 | (1 << 11))
+
+static int index_step_size (int size) {
+
+ if (size == DATA_CHAR || size == DATA_SHORT || size == DATA_INT ||
+ size == DATA_LONG || size == DATA_LLONG || size == DATA_FLOAT ||
+ size == DATA_DOUBLE) {
+ return size & 0x1f;
+ }
+
+ if (size <= 0) {
+ return DATA_INT & 0x1f;
+ }
+
+ return size;
+
+}
+
+static int rhs_last_pointer_depth = 0;
+static int rhs_last_pointed_size = 0;
+
+static void set_rhs_last_pointer_info (int depth, int size) {
+
+ rhs_last_pointer_depth = depth;
+ rhs_last_pointed_size = depth > 1 ? DATA_PTR : (size > 0 ? size : (DATA_INT & 0x1f));
+
+}
+
+static void clear_rhs_last_pointer_info (void) {
+
+ rhs_last_pointer_depth = 0;
+ rhs_last_pointed_size = 0;
+
+}
+
+static int current_function_has_return_statement = 0;
+static int current_parse_block_depth = 0;
+static int current_function_is_void = 0;
+static int current_function_is_floating = 0;
+static int current_function_return_size = DATA_NONE;
+static int current_function_return_is_unsigned = 0;
+static int current_function_returns_aggregate = 0;
+
+static struct local_symbol *pending_struct_return_lhs = 0;
+static const char *pending_struct_return_global_name = 0;
+
+static int pending_struct_return_stack_address = 0;
+static int pending_struct_return_stack_offset = 0;
+static int pending_struct_return_stack_top = 0;
+
+static int suppress_next_struct_return_scalar_store = 0;
+static int token_identifier_is_function_call_rhs_now (void);
+
+static int parsed_type_size = DATA_NONE;
+static int parsed_type_is_inline = 0;
+static int parsed_type_is_void = 0;
+static int parsed_type_is_unsigned = 0;
+static int parsed_type_is_floating = 0;
+static int parsed_type_only_qualifiers = 0;
+
+static int parsed_type_is_aggregate = 0;
+static int parsed_type_has_tag = 0;
+static char parsed_type_tag_name[128];
+
+static char last_cast_type_tag_name[128];
+static int last_cast_type_object_size = 0;
+
+static int parsed_type_is_array_typedef = 0;
+static int parsed_type_array_element_size = DATA_NONE;
+
+static long parsed_type_array_count = 1;
+
+static int declarator_has_function = 0;
+static int declarator_function_is_pointer = 0;
+static int declarator_function_param_count = 0;
+static int declarator_function_has_prototype = 0;
+static int declarator_function_is_variadic = 0;
+static int declarator_array_unsized = 0;
+
+static int declarator_is_pointer = 0;
+static int declarator_pointer_depth = 0;
+static int declarator_has_array = 0;
+
+static int global_initializer_accept_symbol_addresses = 0;
+
+static long declarator_array_count = 1;
+static long declarator_last_array_count = 1;
+
+#define STORAGE_NONE 0
+#define STORAGE_EXTERN 1
+#define STORAGE_STATIC 2
+#define STORAGE_TYPEDEF 3
+
+static int parsed_storage_class = STORAGE_NONE;
+
+#define MAX_GLOBAL_INIT_FIELDS 262144
+#define MAX_AGG_FIELDS 512
+
+static int parsed_field_sizes[MAX_AGG_FIELDS];
+static int parsed_field_count = 0;
+
+#define MAX_STRING_INIT_BYTES 4096
+
+static void clear_parsed_fields (void) {
+ parsed_field_count = 0;
+}
+
+static void append_parsed_field (int size) {
+
+ if (parsed_field_count < MAX_AGG_FIELDS) {
+ parsed_field_sizes[parsed_field_count++] = size;
+ }
+
+}
+
+static int fields_storage_size (const int *sizes, int count) {
+
+ int total = 0, i;
+
+ for (i = 0; i < count; i++) {
+
+ if (sizes[i] < 0) {
+ total += -sizes[i];
+ } else {
+ total += sizes[i];
+ }
+
+ }
+
+ return total;
+
+}
+
+#define MAX_MEMBER_INFOS 4096
+
+struct member_info_entry {
+
+ char *name;
+ int offset;
+ int size;
+ int elem_size;
+ int pointer_depth;
+
+ int is_array;
+ int is_floating;
+ int is_unsigned;
+
+ char *tag_name;
+ char *owner_tag_name;
+
+ int owner_size;
+
+};
+
+static struct member_info_entry member_infos[MAX_MEMBER_INFOS];
+static int member_info_count = 0;
+
+static int postfix_member_pointer_depth = 0;
+static int postfix_member_pointed_size = 0;
+static int postfix_member_seen = 0;
+static int postfix_copy_lvalue_size = 0;
+
+static const char *postfix_copy_lvalue_tag_name = 0;
+
+static const char *last_found_member_tag_name = 0;
+static int last_found_member_is_unsigned = 0;
+
+static int postfix_member_offset = 0;
+static int postfix_member_size = 0;
+
+static int postfix_member_is_floating = 0;
+static int postfix_member_is_unsigned = 0;
+
+static void remember_member_info_ex (const char *name, int offset, int size, int elem_size, int pointer_depth, int is_array, int is_floating) {
+
+ if (!name) {
+ return;
+ }
+
+ if (member_info_count >= MAX_MEMBER_INFOS) {
+ return;
+ }
+
+ member_infos[member_info_count].name = xstrdup (name);
+ member_infos[member_info_count].offset = offset;
+ member_infos[member_info_count].size = size;
+ member_infos[member_info_count].elem_size = elem_size > 0 ? elem_size : size;
+ member_infos[member_info_count].pointer_depth = pointer_depth;
+ member_infos[member_info_count].is_array = is_array;
+ member_infos[member_info_count].is_floating = is_floating ? 1 : 0;
+ member_infos[member_info_count].is_unsigned = parsed_type_is_unsigned ? 1 : 0;
+ member_infos[member_info_count].tag_name = parsed_type_tag_name[0] ? xstrdup (parsed_type_tag_name) : 0;
+ member_infos[member_info_count].owner_size = 0;
+ member_infos[member_info_count].owner_tag_name = 0;
+
+ member_info_count++;
+
+}
+
+static int find_member_info_ex (const char *name, int *offset, int *size, int *elem_size, int *pointer_depth, int *is_array, int *is_floating) {
+
+ int best;
+ int i;
+
+ last_found_member_tag_name = 0;
+ last_found_member_is_unsigned = 0;
+
+ if (!name) {
+ return 0;
+ }
+
+ best = -1;
+
+ for (i = member_info_count - 1; i >= 0; i--) {
+
+ if (member_infos[i].name && strcmp (member_infos[i].name, name) == 0) {
+
+ best = i;
+ break;
+
+ }
+
+ }
+
+ if (best < 0) {
+ return 0;
+ }
+
+ last_found_member_tag_name = member_infos[best].tag_name;
+ last_found_member_is_unsigned = member_infos[best].is_unsigned ? 1 : 0;
+
+ if (offset) {
+ *offset = member_infos[best].offset;
+ }
+
+ if (size) {
+ *size = member_infos[best].size;
+ }
+
+ if (elem_size) {
+ *elem_size = member_infos[best].elem_size;
+ }
+
+ if (pointer_depth) {
+ *pointer_depth = member_infos[best].pointer_depth;
+ }
+
+ if (is_array) {
+ *is_array = member_infos[best].is_array;
+ }
+
+ if (is_floating) {
+ *is_floating = member_infos[best].is_floating;
+ }
+
+ return 1;
+
+}
+
+static int find_member_info (const char *name, int *offset, int *size) {
+ return find_member_info_ex (name, offset, size, 0, 0, 0, 0);
+}
+
+static int find_member_info_ex_bounded (const char *name, int max_size, const char *owner_tag_name, int *offset, int *size, int *elem_size, int *pointer_depth, int *is_array, int *is_floating) {
+
+ int best;
+ int i;
+
+ last_found_member_tag_name = 0;
+ last_found_member_is_unsigned = 0;
+
+ if (!name) {
+ return 0;
+ }
+
+ if (max_size <= 0 && (!owner_tag_name || !owner_tag_name[0])) {
+ return find_member_info_ex (name, offset, size, elem_size, pointer_depth, is_array, is_floating);
+ }
+
+ best = -1;
+
+ if (owner_tag_name && owner_tag_name[0]) {
+
+ for (i = member_info_count - 1; i >= 0; i--) {
+
+ if (member_infos[i].name && strcmp (member_infos[i].name, name) == 0
+ && member_infos[i].owner_tag_name
+ && strcmp (member_infos[i].owner_tag_name, owner_tag_name) == 0) {
+
+ best = i;
+ break;
+
+ }
+
+ }
+
+ }
+
+ if (best < 0) {
+
+ for (i = member_info_count - 1; i >= 0; i--) {
+
+ if (member_infos[i].name && strcmp (member_infos[i].name, name) == 0
+ && member_infos[i].owner_size == max_size) {
+
+ if (best < 0 || member_infos[i].offset < member_infos[best].offset) {
+ best = i;
+ }
+
+ }
+
+ }
+
+ }
+
+ if (best < 0) {
+
+ for (i = member_info_count - 1; i >= 0; i--) {
+
+ if (member_infos[i].name && strcmp (member_infos[i].name, name) == 0
+ && member_infos[i].offset + member_infos[i].size <= max_size) {
+
+ if (best < 0 || member_infos[i].offset < member_infos[best].offset) {
+ best = i;
+ }
+
+ }
+
+ }
+
+ }
+
+ if (best >= 0) {
+
+ last_found_member_tag_name = member_infos[best].tag_name;
+ last_found_member_is_unsigned = member_infos[best].is_unsigned ? 1 : 0;
+
+ if (offset) {
+ *offset = member_infos[best].offset;
+ }
+
+ if (size) {
+ *size = member_infos[best].size;
+ }
+
+ if (elem_size) {
+ *elem_size = member_infos[best].elem_size;
+ }
+
+ if (pointer_depth) {
+ *pointer_depth = member_infos[best].pointer_depth;
+ }
+
+ if (is_array) {
+ *is_array = member_infos[best].is_array;
+ }
+
+ if (is_floating) {
+ *is_floating = member_infos[best].is_floating;
+ }
+
+ return 1;
+
+ }
+
+ return find_member_info_ex (name, offset, size, elem_size, pointer_depth, is_array, is_floating);
+
+}
+
+static const char *find_member_tag_name (const char *name) {
+
+ int i;
+
+ if (!name) {
+ return 0;
+ }
+
+ for (i = member_info_count - 1; i >= 0; i--) {
+
+ if (member_infos[i].name && strcmp (member_infos[i].name, name) == 0) {
+ return member_infos[i].tag_name;
+ }
+
+ }
+
+ return 0;
+
+}
+
+#define MAX_AGG_TAGS 256
+
+struct aggregate_tag_entry {
+
+ char *name;
+
+ int is_union;
+ int size;
+
+ int field_count;
+ int field_sizes[MAX_AGG_FIELDS];
+
+};
+
+static struct aggregate_tag_entry aggregate_tags[MAX_AGG_TAGS];
+static int aggregate_tag_count = 0;
+
+static struct aggregate_tag_entry *find_aggregate_tag (const char *name, int is_union) {
+
+ int i;
+
+ if (!name) {
+ return 0;
+ }
+
+ for (i = 0; i < aggregate_tag_count; i++) {
+
+ if (aggregate_tags[i].is_union == is_union && strcmp (aggregate_tags[i].name, name) == 0) {
+ return &aggregate_tags[i];
+ }
+
+ }
+
+ return 0;
+
+}
+
+static void save_aggregate_tag (const char *name, int is_union, int size, const int *field_sizes, int field_count) {
+
+ struct aggregate_tag_entry *entry;
+ int i;
+
+ if (!name) {
+ return;
+ }
+
+ entry = find_aggregate_tag (name, is_union);
+
+ if (!entry) {
+
+ if (aggregate_tag_count >= MAX_AGG_TAGS) {
+ return;
+ }
+
+ entry = &aggregate_tags[aggregate_tag_count++];
+ entry->name = xstrdup (name);
+ entry->is_union = is_union;
+
+ }
+
+ entry->size = size;
+ entry->field_count = 0;
+
+ for (i = 0; i < field_count && i < MAX_AGG_FIELDS; i++) {
+ entry->field_sizes[entry->field_count++] = field_sizes[i];
+ }
+
+}
+
+static void load_aggregate_tag_fields (struct aggregate_tag_entry *entry) {
+
+ int i;
+ clear_parsed_fields ();
+
+ if (!entry) {
+ return;
+ }
+
+ parsed_type_size = entry->size;
+ parsed_type_is_aggregate = 1;
+ parsed_type_is_void = 0;
+ parsed_type_has_tag = 1;
+
+ for (i = 0; i < entry->field_count; i++) {
+ append_parsed_field (entry->field_sizes[i]);
+ }
+
+}
+
+#define MAX_TYPEDEF_NAMES 512
+
+struct typedef_entry {
+
+ char *name;
+ int size;
+
+ int is_aggregate;
+ int is_unsigned;
+ int is_void;
+
+ int field_count;
+ int field_sizes[MAX_AGG_FIELDS];
+
+ int array_element_size;
+ int is_array;
+
+ char *tag_name;
+ long array_count;
+
+};
+
+static struct typedef_entry typedef_names[MAX_TYPEDEF_NAMES];
+static int typedef_name_count = 0;
+
+static void clear_typedef_names (void) {
+
+ int i;
+
+ for (i = 0; i < typedef_name_count; i++) {
+
+ free (typedef_names[i].name);
+
+ typedef_names[i].name = 0;
+ typedef_names[i].size = 0;
+
+ typedef_names[i].tag_name = 0;
+
+ typedef_names[i].is_aggregate = 0;
+ typedef_names[i].is_unsigned = 0;
+
+ typedef_names[i].field_count = 0;
+ typedef_names[i].is_array = 0;
+ typedef_names[i].array_count = 1;
+ typedef_names[i].array_element_size = DATA_NONE;
+
+ }
+
+ typedef_name_count = 0;
+
+}
+
+static struct typedef_entry *find_typedef_name (const char *name) {
+
+ int i;
+
+ if (!name) {
+ return 0;
+ }
+
+ for (i = 0; i < typedef_name_count; i++) {
+
+ if (strcmp (typedef_names[i].name, name) == 0) {
+ return &typedef_names[i];
+ }
+
+ }
+
+ return 0;
+
+}
+
+struct local_symbol;
+static struct local_symbol *find_local_symbol (const char *name);
+
+static int is_current_typedef_name (void) {
+
+ if (tok.kind != TOK_IDENT || !tok.ident) {
+ return 0;
+ }
+
+ if (find_local_symbol (tok.ident)) {
+ return 0;
+ }
+
+ return find_typedef_name (tok.ident) != 0;
+
+}
+
+static void save_typedef_name (const char *name, int size, int is_unsigned, int is_void, int is_aggregate, int is_array, long array_count, int array_element_size, const int *field_sizes, int field_count) {
+
+ struct typedef_entry *entry;
+ int i;
+
+ if (!name || !*name) {
+ return;
+ }
+
+ entry = find_typedef_name (name);
+
+ if (!entry) {
+
+ if (typedef_name_count >= MAX_TYPEDEF_NAMES) {
+
+ report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "too many typedef names");
+ return;
+
+ }
+
+ entry = &typedef_names[typedef_name_count++];
+ entry->name = xstrdup (name);
+ entry->tag_name = 0;
+
+ }
+
+ if (entry->tag_name) {
+
+ free (entry->tag_name);
+ entry->tag_name = 0;
+
+ }
+
+ if (parsed_type_tag_name[0]) {
+ entry->tag_name = xstrdup (parsed_type_tag_name);
+ } else if (is_aggregate && name && *name) {
+ entry->tag_name = xstrdup (name);
+ }
+
+ entry->is_void = is_void;
+ entry->size = size;
+
+ entry->is_unsigned = is_unsigned;
+ entry->is_aggregate = is_aggregate;
+
+ entry->field_count = 0;
+ entry->is_array = is_array ? 1 : 0;
+ entry->array_count = array_count > 0 ? array_count : 1;
+ entry->array_element_size = array_element_size > 0 ? array_element_size : DATA_INT;
+
+ for (i = 0; i < field_count && i < MAX_AGG_FIELDS; i++) {
+ entry->field_sizes[entry->field_count++] = field_sizes[i];
+ }
+
+ if (is_aggregate && !parsed_type_tag_name[0] && name && *name) {
+
+ int mi;
+
+ for (mi = 0; mi < member_info_count; mi++) {
+
+ if (member_infos[mi].owner_size == size
+ && member_infos[mi].owner_tag_name == 0) {
+ member_infos[mi].owner_tag_name = xstrdup (name);
+ }
+
+ }
+
+ }
+
+}
+
+static void update_typedef_name_from_aggregate_tag (const char *name, int size, const int *field_sizes, int field_count) {
+
+ struct typedef_entry *entry;
+ int i;
+
+ if (!name) {
+ return;
+ }
+
+ entry = find_typedef_name (name);
+
+ if (!entry) {
+ return;
+ }
+
+ entry->size = size;
+ entry->is_aggregate = 1;
+ entry->is_void = 0;
+ entry->is_array = 0;
+ entry->array_count = 1;
+ entry->array_element_size = DATA_NONE;
+ entry->field_count = 0;
+
+ if (entry->tag_name) {
+
+ free (entry->tag_name);
+ entry->tag_name = 0;
+
+ }
+
+ if (name && *name) {
+ entry->tag_name = xstrdup (name);
+ }
+
+ for (i = 0; i < field_count && i < MAX_AGG_FIELDS; i++) {
+ entry->field_sizes[entry->field_count++] = field_sizes[i];
+ }
+
+}
+
+static void load_typedef_name (struct typedef_entry *entry) {
+
+ int i;
+ clear_parsed_fields ();
+
+ if (!entry) {
+
+ parsed_type_size = DATA_INT & 0x1f;
+
+ parsed_type_is_aggregate = 0;
+ parsed_type_is_unsigned = 0;
+ parsed_type_is_void = 0;
+ parsed_type_is_floating = 0;
+ parsed_type_is_array_typedef = 0;
+ parsed_type_array_count = 1;
+ parsed_type_array_element_size = DATA_NONE;
+
+ append_parsed_field (DATA_INT & 0x1f);
+ return;
+
+ }
+
+ parsed_type_size = entry->size;
+
+ parsed_type_is_aggregate = entry->is_aggregate;
+ parsed_type_is_unsigned = entry->is_unsigned;
+ parsed_type_is_void = entry->is_void;
+ parsed_type_is_floating = 0;
+ parsed_type_is_array_typedef = entry->is_array;
+ parsed_type_array_count = entry->array_count > 0 ? entry->array_count : 1;
+ parsed_type_array_element_size = entry->array_element_size > 0 ? entry->array_element_size : DATA_INT;
+
+ parsed_type_has_tag = 0;
+ parsed_type_tag_name[0] = '\0';
+
+ if (entry->tag_name && entry->tag_name[0]) {
+
+ strncpy (parsed_type_tag_name, entry->tag_name, sizeof (parsed_type_tag_name) - 1);
+ parsed_type_tag_name[sizeof (parsed_type_tag_name) - 1] = '\0';
+
+ parsed_type_has_tag = 1;
+
+ }
+
+ for (i = 0; i < entry->field_count; i++) {
+ append_parsed_field (entry->field_sizes[i]);
+ }
+
+}
+
+#define MAX_ENUM_CONSTANTS 4096
+
+struct enum_const_entry {
+
+ char *name;
+ int64_s value;
+
+};
+
+static struct enum_const_entry enum_constants[MAX_ENUM_CONSTANTS];
+static int enum_constant_count = 0;
+
+static struct enum_const_entry *find_enum_constant (const char *name) {
+
+ int i;
+
+ if (!name) {
+ return 0;
+ }
+
+ for (i = 0; i < enum_constant_count; i++) {
+
+ if (strcmp (enum_constants[i].name, name) == 0) {
+ return &enum_constants[i];
+ }
+
+ }
+
+ return 0;
+
+}
+
+static void save_enum_constant (const char *name, int64_s value, const char *start, const char *caret) {
+
+ struct enum_const_entry *entry;
+
+ if (!name || !*name) {
+ return;
+ }
+
+ entry = find_enum_constant (name);
+
+ if (entry) {
+
+ report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, caret, "duplicate enum constant '%s'", name);
+ return;
+
+ }
+
+ if (enum_constant_count >= MAX_ENUM_CONSTANTS) {
+
+ report_line_at (get_filename (), get_line_number (), REPORT_ERROR, start, caret, "too many enum constants");
+ return;
+
+ }
+
+ entry = &enum_constants[enum_constant_count++];
+ entry->name = xstrdup (name);
+ entry->value = value;
+
+}
+
+int resolve_enum_constant (const char *name, int64_s *out) {
+
+ struct enum_const_entry *entry = find_enum_constant (name);
+
+ if (!entry) {
+ return 0;
+ }
+
+ if (out) {
+ *out = entry->value;
+ }
+
+ return 1;
+
+}
+
+static void clear_enum_constants (void) {
+
+ int i;
+
+ for (i = 0; i < enum_constant_count; i++) {
+
+ free (enum_constants[i].name);
+
+ enum_constants[i].name = 0;
+ enum_constants[i].value.low = 0;
+ enum_constants[i].value.high = 0;
+
+ }
+
+ enum_constant_count = 0;
+
+}
+
+#define MAX_GLOBAL_SYMBOLS 4096
+
+#define GLOBAL_SYMBOL_OBJECT 1
+#define GLOBAL_SYMBOL_FUNCTION 2
+
+struct global_symbol_entry {
+
+ char *name;
+
+ int is_unsigned, is_extern;
+ int kind, size;
+
+ int array_element_size;
+ int is_array;
+
+ long array_count;
+
+ int is_floating;
+ int pointer_depth;
+ int pointed_size;
+ int returns_void;
+
+ int param_count;
+ int has_prototype;
+ int is_variadic;
+
+ int is_implicit;
+ int extern_emitted;
+
+ char *tag_name;
+
+};
+
+static struct global_symbol_entry global_symbols[MAX_GLOBAL_SYMBOLS];
+static int global_symbol_count = 0;
+
+static const char *last_declarator_name_start = 0;
+static const char *last_declarator_name_caret = 0;
+
+static unsigned long last_declarator_name_line = 0;
+
+static int capture_declarator_name_location = 0;
+static int captured_declarator_name_location = 0;
+
+static const char *captured_declarator_name_start = 0;
+static const char *captured_declarator_name_caret = 0;
+
+static unsigned long captured_declarator_name_line = 0;
+
+static void clear_global_symbols (void) {
+
+ int i;
+
+ for (i = 0; i < global_symbol_count; i++) {
+
+ free (global_symbols[i].name);
+
+ global_symbols[i].name = 0;
+ global_symbols[i].kind = 0;
+ global_symbols[i].size = 0;
+
+ global_symbols[i].array_element_size = 0;
+ global_symbols[i].is_array = 0;
+
+ global_symbols[i].is_extern = 0;
+ global_symbols[i].is_unsigned = 0;
+ global_symbols[i].is_floating = 0;
+ global_symbols[i].returns_void = 0;
+ global_symbols[i].param_count = 0;
+ global_symbols[i].has_prototype = 0;
+ global_symbols[i].is_variadic = 0;
+ global_symbols[i].is_implicit = 0;
+ global_symbols[i].extern_emitted = 0;
+
+ if (global_symbols[i].tag_name) {
+
+ free (global_symbols[i].tag_name);
+ global_symbols[i].tag_name = 0;
+
+ }
+
+ }
+
+ global_symbol_count = 0;
+
+}
+
+static int find_global_symbol (const char *name) {
+
+ int i;
+
+ if (!name) {
+ return -1;
+ }
+
+ for (i = 0; i < global_symbol_count; i++) {
+
+ if (strcmp (global_symbols[i].name, name) == 0) {
+ return i;
+ }
+
+ }
+
+ return -1;
+
+}
+
+static int asm_symbol_is_internal (const char *name) {
+
+ if (!name || !*name) {
+ return 1;
+ }
+
+ if (name[0] == '.') {
+ return 1;
+ }
+
+ if (name[0] == 'L') {
+
+ if (name[1] >= '0' && name[1] <= '9') {
+ return 1;
+ }
+
+ if (name[1] == 'C' && name[2] >= '0' && name[2] <= '9') {
+ return 1;
+ }
+
+ }
+
+ return 0;
+
+}
+
+static const char *asm_global_symbol_name (const char *name) {
+
+ static char buffers[8][512];
+ static int index = 0;
+
+ char *out;
+ unsigned long len;
+
+ if (asm_symbol_is_internal (name)) {
+ return name;
+ }
+
+ index = (index + 1) & 7;
+
+ out = buffers[index];
+ len = strlen (name);
+
+ if (len > sizeof (buffers[0]) - 2) {
+ len = sizeof (buffers[0]) - 2;
+ }
+
+ if (!state->no_leading_underscore) {
+
+ out[0] = '_';
+
+ memcpy (out + 1, name, len);
+ out[len + 1] = 0;
+
+ } else {
+
+ memcpy (out, name, len);
+ out[len] = 0;
+
+ }
+
+ return out;
+
+}
+
+static int get_global_symbol_kind (const char *name) {
+
+ int i = find_global_symbol (name);
+
+ if (i >= 0) {
+ return global_symbols[i].kind;
+ }
+
+ return 0;
+
+}
+
+static void set_global_symbol_unsigned (const char *name, int is_unsigned) {
+
+ int i = find_global_symbol (name);
+
+ if (i >= 0) {
+ global_symbols[i].is_unsigned = is_unsigned ? 1 : 0;
+ }
+
+}
+
+static int get_global_symbol_unsigned (const char *name) {
+
+ int i = find_global_symbol (name);
+
+ if (i >= 0) {
+ return global_symbols[i].is_unsigned;
+ }
+
+ return 0;
+
+}
+
+static void set_global_symbol_array (const char *name, int is_array) {
+
+ int i = find_global_symbol (name);
+
+ if (i >= 0) {
+ global_symbols[i].is_array = is_array ? 1 : 0;
+ }
+
+}
+
+static int get_global_symbol_array (const char *name) {
+
+ int i = find_global_symbol (name);
+
+ if (i >= 0) {
+ return global_symbols[i].is_array;
+ }
+
+ return 0;
+
+}
+
+static void set_global_symbol_array_count (const char *name, long array_count) {
+
+ int i = find_global_symbol (name);
+
+ if (i >= 0) {
+ global_symbols[i].array_count = array_count;
+ }
+
+}
+
+static long get_global_symbol_array_count (const char *name) {
+
+ int i = find_global_symbol (name);
+
+ if (i >= 0) {
+ return global_symbols[i].array_count;
+ }
+
+ return 0;
+
+}
+
+static void set_global_symbol_array_element_size (const char *name, int elem_size) {
+
+ int i = find_global_symbol (name);
+
+ if (i >= 0) {
+ global_symbols[i].array_element_size = elem_size > 0 ? elem_size : 0;
+ }
+
+}
+
+static int get_global_symbol_array_element_size (const char *name) {
+
+ int i = find_global_symbol (name);
+
+ if (i >= 0) {
+
+ if (global_symbols[i].array_element_size > 0) {
+ return global_symbols[i].array_element_size;
+ }
+
+ if (global_symbols[i].tag_name && global_symbols[i].tag_name[0]) {
+
+ struct aggregate_tag_entry *entry = find_aggregate_tag (global_symbols[i].tag_name, 0);
+
+ if (entry && entry->size > 0) {
+ return entry->size;
+ }
+
+ }
+
+ }
+
+ return 0;
+
+}
+
+static void set_global_symbol_floating (const char *name, int is_floating) {
+
+ int i = find_global_symbol (name);
+
+ if (i >= 0) {
+ global_symbols[i].is_floating = is_floating ? 1 : 0;
+ }
+
+}
+
+static int get_global_symbol_floating (const char *name) {
+
+ int i = find_global_symbol (name);
+
+ if (i >= 0) {
+ return global_symbols[i].is_floating;
+ }
+
+ return 0;
+
+}
+
+static void set_global_symbol_pointer_info (const char *name, int pointer_depth, int pointed_size) {
+
+ int i = find_global_symbol (name);
+
+ if (i >= 0) {
+
+ global_symbols[i].pointer_depth = pointer_depth;
+ global_symbols[i].pointed_size = pointed_size;
+
+ }
+
+}
+
+static int get_global_symbol_pointer_depth (const char *name) {
+
+ int i = find_global_symbol (name);
+
+ if (i >= 0) {
+ return global_symbols[i].pointer_depth;
+ }
+
+ return 0;
+
+}
+
+static int get_global_symbol_pointed_size (const char *name) {
+
+ int i = find_global_symbol (name);
+
+ if (i >= 0 && global_symbols[i].pointed_size > 0) {
+ return global_symbols[i].pointed_size;
+ }
+
+ return DATA_INT & 0x1f;
+
+}
+
+static void set_global_symbol_tag_name (const char *name, const char *tag_name) {
+
+ int i = find_global_symbol (name);
+
+ if (i >= 0) {
+
+ if (global_symbols[i].tag_name) {
+
+ free (global_symbols[i].tag_name);
+ global_symbols[i].tag_name = 0;
+
+ }
+
+ if (tag_name && tag_name[0]) {
+ global_symbols[i].tag_name = xstrdup (tag_name);
+ }
+
+ }
+
+}
+
+static const char *get_global_symbol_tag_name (const char *name) {
+
+ int i = find_global_symbol (name);
+
+ if (i >= 0) {
+ return global_symbols[i].tag_name;
+ }
+
+ return 0;
+
+}
+
+static void set_global_symbol_returns_void (const char *name, int returns_void) {
+
+ int i = find_global_symbol (name);
+
+ if (i >= 0) {
+ global_symbols[i].returns_void = returns_void ? 1 : 0;
+ }
+
+}
+
+static int get_global_symbol_returns_void (const char *name) {
+
+ int i = find_global_symbol (name);
+
+ if (i >= 0) {
+ return global_symbols[i].returns_void;
+ }
+
+ return 0;
+
+}
+
+static void set_global_symbol_param_count (const char *name, int param_count, int has_prototype, int is_variadic) {
+
+ int i = find_global_symbol (name);
+
+ if (i >= 0) {
+
+ global_symbols[i].param_count = param_count < 0 ? 0 : param_count;
+ global_symbols[i].has_prototype = has_prototype ? 1 : 0;
+ global_symbols[i].is_variadic = is_variadic ? 1 : 0;
+
+ }
+
+}
+
+static int get_global_symbol_has_prototype (const char *name) {
+
+ int i = find_global_symbol (name);
+
+ if (i >= 0) {
+ return global_symbols[i].has_prototype;
+ }
+
+ return 0;
+
+}
+
+static int get_global_symbol_is_variadic (const char *name) {
+
+ int i = find_global_symbol (name);
+
+ if (i >= 0) {
+ return global_symbols[i].is_variadic;
+ }
+
+ return 0;
+
+}
+
+static int get_global_symbol_param_count (const char *name) {
+
+ int i = find_global_symbol (name);
+
+ if (i >= 0) {
+ return global_symbols[i].param_count;
+ }
+
+ return 0;
+
+}
+
+static void set_global_symbol_size (const char *name, int size) {
+
+ int i = find_global_symbol (name);
+
+ if (i >= 0) {
+ global_symbols[i].size = size;
+ }
+
+}
+
+static int get_global_symbol_size (const char *name) {
+
+ int i = find_global_symbol (name);
+
+ if (i >= 0 && global_symbols[i].size > 0) {
+ return global_symbols[i].size;
+ }
+
+ return DATA_INT & 0x1f;
+
+}
+
+static int add_global_symbol (const char *name, int kind, int is_extern, const char *line_start, const char *name_caret, unsigned long lineno) {
+
+ int old;
+
+ if (!name || !*name) {
+ return 0;
+ }
+
+ if (!line_start) {
+ line_start = tok.start;
+ }
+
+ if (!name_caret) {
+ name_caret = tok.caret;
+ }
+
+ if (lineno == 0) {
+ lineno = get_line_number ();
+ }
+
+ old = find_global_symbol (name);
+
+ if (old >= 0) {
+
+ if (global_symbols[old].is_implicit) {
+
+ global_symbols[old].is_extern = is_extern ? 1 : 0;
+ global_symbols[old].is_implicit = 0;
+
+ global_symbols[old].kind = kind;
+ return 1;
+
+ }
+
+ if (is_extern) {
+ return 0;
+ }
+
+ if (global_symbols[old].is_extern) {
+
+ global_symbols[old].kind = kind;
+ global_symbols[old].is_extern = 0;
+
+ return 1;
+
+ }
+
+ report_line_at (get_filename (), lineno, REPORT_ERROR, line_start, name_caret, "duplicate symbol '%s'", name);
+ return 0;
+
+ }
+
+ if (global_symbol_count >= MAX_GLOBAL_SYMBOLS) {
+
+ report_line_at (get_filename (), lineno, REPORT_ERROR, line_start, name_caret, "too many global symbols");
+ return 0;
+
+ }
+
+ global_symbols[global_symbol_count].name = xstrdup (name);
+ global_symbols[global_symbol_count].kind = kind;
+ global_symbols[global_symbol_count].is_extern = is_extern;
+ global_symbols[global_symbol_count].is_unsigned = 0;
+ global_symbols[global_symbol_count].is_floating = 0;
+ global_symbols[global_symbol_count].is_array = 0;
+ global_symbols[global_symbol_count].returns_void = 0;
+ global_symbols[global_symbol_count].param_count = 0;
+ global_symbols[global_symbol_count].has_prototype = 0;
+ global_symbols[global_symbol_count].is_variadic = 0;
+ global_symbols[global_symbol_count].is_implicit = 0;
+ global_symbols[global_symbol_count].extern_emitted = 0;
+ global_symbols[global_symbol_count].tag_name = 0;
+
+ global_symbol_count++;
+ return 1;
+
+}
+
+static void ensure_global_function_symbol (const char *name, const char *line_start, const char *name_caret, unsigned long lineno) {
+
+ int i;
+
+ if (!name || !*name) {
+ return;
+ }
+
+ i = find_global_symbol (name);
+
+ if (i >= 0) {
+ return;
+ }
+
+ report_line_at (get_filename (), lineno, REPORT_WARNING, line_start, name_caret, "implicit declaration of function '%s'", name);
+
+ if (add_global_symbol (name, GLOBAL_SYMBOL_FUNCTION, 1, line_start, name_caret, lineno)) {
+
+ i = find_global_symbol (name);
+
+ if (i >= 0) {
+
+ global_symbols[i].size = DATA_INT & 0x1f;
+ global_symbols[i].is_unsigned = 0;
+ global_symbols[i].is_floating = 0;
+ global_symbols[i].returns_void = 0;
+ global_symbols[i].param_count = 0;
+ global_symbols[i].has_prototype = 0;
+ global_symbols[i].is_variadic = 0;
+ global_symbols[i].is_implicit = 1;
+
+ }
+
+ }
+
+}
+
+#define MAX_INLINE_FUNCTIONS 256
+
+struct inline_function_entry {
+
+ char *name;
+ char *body;
+ char *data;
+
+ int param_count;
+ int has_prototype;
+ int returns_void;
+ int is_floating;
+ int return_size;
+ int return_label;
+ int data_emitted;
+ int expanding;
+
+ int usable;
+
+};
+
+static struct inline_function_entry inline_functions[MAX_INLINE_FUNCTIONS];
+static int inline_function_count = 0;
+
+static void clear_inline_functions (void) {
+
+ int i;
+
+ for (i = 0; i < inline_function_count; i++) {
+
+ free (inline_functions[i].name);
+ free (inline_functions[i].body);
+ free (inline_functions[i].data);
+
+ inline_functions[i].name = 0;
+ inline_functions[i].body = 0;
+ inline_functions[i].data = 0;
+ inline_functions[i].param_count = 0;
+ inline_functions[i].has_prototype = 0;
+ inline_functions[i].returns_void = 0;
+ inline_functions[i].is_floating = 0;
+ inline_functions[i].return_size = 0;
+ inline_functions[i].return_label = 0;
+ inline_functions[i].data_emitted = 0;
+ inline_functions[i].expanding = 0;
+ inline_functions[i].usable = 0;
+
+ }
+
+ inline_function_count = 0;
+
+}
+
+static int find_inline_function (const char *name) {
+
+ int i;
+
+ if (!name) {
+ return -1;
+ }
+
+ for (i = 0; i < inline_function_count; i++) {
+
+ if (strcmp (inline_functions[i].name, name) == 0) {
+ return i;
+ }
+
+ }
+
+ return -1;
+
+}
+
+static void remember_inline_function_signature (const char *name, int param_count, int has_prototype, int returns_void, int is_floating, int return_size) {
+
+ int i;
+
+ if (!name || !*name) {
+ return;
+ }
+
+ i = find_inline_function (name);
+
+ if (i < 0) {
+
+ if (inline_function_count >= MAX_INLINE_FUNCTIONS) {
+ return;
+ }
+
+ i = inline_function_count++;
+ inline_functions[i].name = xstrdup (name);
+ inline_functions[i].body = 0;
+ inline_functions[i].data = 0;
+ inline_functions[i].data_emitted = 0;
+ inline_functions[i].expanding = 0;
+
+ }
+
+ inline_functions[i].param_count = param_count < 0 ? 0 : param_count;
+ inline_functions[i].has_prototype = has_prototype ? 1 : 0;
+ inline_functions[i].returns_void = returns_void ? 1 : 0;
+ inline_functions[i].is_floating = is_floating ? 1 : 0;
+ inline_functions[i].return_size = return_size;
+ inline_functions[i].return_label = 0;
+ inline_functions[i].data_emitted = 0;
+ inline_functions[i].expanding = 0;
+ inline_functions[i].usable = 0;
+
+}
+
+static int inline_asm_body_is_safe (const char *body) {
+
+ const char *p = body;
+
+ if (!body) {
+ return 0;
+ }
+
+ while (*p) {
+
+ const char *line = p;
+ const char *eol = strchr (p, '\n');
+
+ size_t len = eol ? (size_t) (eol - line) : strlen (line);
+
+ while (len > 0 && (line[len - 1] == '\r' || line[len - 1] == '\n')) {
+ len--;
+ }
+
+ while (len > 0 && (*line == ' ' || *line == '\t')) {
+
+ line++;
+ len--;
+
+ }
+
+ if (len >= 4 && strncmp (line, "ret", 3) == 0) {
+ return 0;
+ }
+
+ if (len >= 5 && strncmp (line, "leave", 5) == 0) {
+ return 0;
+ }
+
+ if (len > 0 && line[0] == '.' && !(len >= 3 && line[1] == 'L' && line[2] >= '0' && line[2] <= '9')) {
+
+ if (!((len == 5 && strncmp (line, ".data", 5) == 0) ||
+ (len == 6 && strncmp (line, ".data?", 6) == 0) ||
+ (len == 5 && strncmp (line, ".code", 5) == 0) ||
+ (len == 5 && strncmp (line, ".text", 5) == 0) ||
+ (len == 4 && strncmp (line, ".bss", 4) == 0))) {
+
+ return 0;
+
+ }
+
+ }
+
+ if (len >= 6 && strncmp (line, "public", 6) == 0) {
+ return 0;
+ }
+
+ if (!eol) {
+ break;
+ }
+
+ p = eol + 1;
+
+ }
+
+ return 1;
+
+}
+
+static char *extract_inline_asm_body (const char *asm_text, int return_label) {
+
+ char marker[64];
+ char *body;
+
+ const char *start, *end;
+ size_t len;
+
+ if (!asm_text) {
+ return 0;
+ }
+
+ start = strstr (asm_text, " mov ebp, esp\n");
+
+ if (start) {
+ start += strlen (" mov ebp, esp\n");
+ } else {
+
+ start = strstr (asm_text, " movl %esp, %ebp\n");
+
+ if (start) {
+ start += strlen (" movl %esp, %ebp\n");
+ }
+
+ }
+
+ if (!start) {
+ return 0;
+ }
+
+ if ((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) {
+ sprintf (marker, "\nL%d:\n", return_label);
+ } else {
+ sprintf (marker, "\n.L%d:\n", return_label);
+ }
+
+ end = strstr (start, marker);
+
+ if (!end) {
+
+ if ((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) {
+ sprintf (marker, "L%d:\n", return_label);
+ } else {
+ sprintf (marker, ".L%d:\n", return_label);
+ }
+
+ if (strncmp (start, marker, strlen (marker)) == 0) {
+ end = start;
+ } else {
+ return 0;
+ }
+
+ }
+
+ if (*start == '\n') {
+ start++;
+ }
+
+ len = (size_t) (end - start);
+
+ while (len > 0 && (start[len - 1] == '\r' || start[len - 1] == '\n')) {
+ len--;
+ }
+
+ body = xmalloc (len + 2);
+ memcpy (body, start, len);
+
+ body[len++] = '\n';
+ body[len] = 0;
+
+ if (!inline_asm_body_is_safe (body)) {
+
+ free (body);
+ return 0;
+
+ }
+
+ return body;
+
+}
+
+static char *read_tmp_file_text (FILE *fp) {
+
+ long len;
+ char *text;
+
+ if (!fp) {
+ return 0;
+ }
+
+ fflush (fp);
+
+ if (fseek (fp, 0, SEEK_END) != 0) {
+ return 0;
+ }
+
+ len = ftell (fp);
+
+ if (len < 0) {
+ return 0;
+ }
+
+ if (fseek (fp, 0, SEEK_SET) != 0) {
+ return 0;
+ }
+
+ text = xmalloc ((size_t) len + 1);
+
+ if (len > 0 && fread (text, 1, (size_t) len, fp) != (size_t) len) {
+ free (text);
+ return 0;
+ }
+
+ text[len] = 0;
+ return text;
+
+}
+
+static void append_inline_text (char **dst, const char *start, size_t len) {
+
+ size_t old_len = 0;
+ char *out;
+
+ if (!dst || !start || len == 0) {
+ return;
+ }
+
+ if (*dst) {
+ old_len = strlen (*dst);
+ }
+
+ out = xmalloc (old_len + len + 1);
+
+ if (old_len) {
+ memcpy (out, *dst, old_len);
+ free (*dst);
+ }
+
+ memcpy (out + old_len, start, len);
+ out[old_len + len] = 0;
+ *dst = out;
+
+}
+
+static void split_inline_asm_sections (char **bodyp, char **datap) {
+
+ const char *p;
+
+ char *body = 0;
+ char *data = 0;
+
+ int in_data = 0;
+
+ if (!bodyp || !*bodyp) {
+ return;
+ }
+
+ p = *bodyp;
+
+ while (*p) {
+
+ const char *line = p;
+ const char *eol = strchr (p, '\n');
+ const char *next = eol ? eol + 1 : p + strlen (p);
+
+ size_t len = (size_t) (next - line);
+
+ const char *trim = line;
+ size_t tlen = eol ? (size_t) (eol - line) : strlen (line);
+
+ while (tlen > 0 && (trim[tlen - 1] == '\r' || trim[tlen - 1] == '\n')) {
+ tlen--;
+ }
+
+ while (tlen > 0 && (*trim == ' ' || *trim == '\t')) {
+
+ trim++;
+ tlen--;
+
+ }
+
+ if ((tlen == 5 && strncmp (trim, ".data", 5) == 0) ||
+ (tlen == 6 && strncmp (trim, ".data?", 6) == 0) ||
+ (tlen == 4 && strncmp (trim, ".bss", 4) == 0)) {
+
+ in_data = 1;
+ append_inline_text (&data, line, len);
+
+ } else if ((tlen == 5 && strncmp (trim, ".code", 5) == 0) ||
+ (tlen == 5 && strncmp (trim, ".text", 5) == 0)) {
+ in_data = 0;
+ } else if (in_data) {
+ append_inline_text (&data, line, len);
+ } else {
+ append_inline_text (&body, line, len);
+ }
+
+ p = next;
+
+ }
+
+ free (*bodyp);
+
+ *bodyp = body;
+ *datap = data;
+
+}
+
+static void remember_inline_function (const char *name, const char *asm_text, int return_label, int param_count, int has_prototype, int returns_void, int is_floating, int return_size) {
+
+ char *body;
+ char *data = 0;
+
+ int i;
+
+ if (!name || !*name || !asm_text) {
+ return;
+ }
+
+ body = extract_inline_asm_body (asm_text, return_label);
+
+ if (!body) {
+ return;
+ }
+
+ split_inline_asm_sections (&body, &data);
+ i = find_inline_function (name);
+
+ if (i < 0) {
+
+ if (inline_function_count >= MAX_INLINE_FUNCTIONS) {
+
+ free (body);
+ free (data);
+
+ return;
+
+ }
+
+ i = inline_function_count++;
+
+ inline_functions[i].name = xstrdup (name);
+ inline_functions[i].body = 0;
+ inline_functions[i].data = 0;
+ inline_functions[i].data_emitted = 0;
+ inline_functions[i].expanding = 0;
+
+ }
+
+ free (inline_functions[i].body);
+ free (inline_functions[i].data);
+
+ inline_functions[i].body = body;
+ inline_functions[i].data = data;
+ inline_functions[i].data_emitted = 0;
+ inline_functions[i].param_count = param_count;
+ inline_functions[i].has_prototype = has_prototype ? 1 : 0;
+ inline_functions[i].returns_void = returns_void ? 1 : 0;
+ inline_functions[i].is_floating = is_floating ? 1 : 0;
+ inline_functions[i].return_size = return_size;
+ inline_functions[i].return_label = return_label;
+ inline_functions[i].expanding = 0;
+ inline_functions[i].usable = 1;
+
+}
+
+static void emit_inline_label_reference (int label, int return_label, int call_id) {
+
+ if (label == return_label) {
+
+ if ((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) {
+ fprintf (state->ofp, "L%d", call_id);
+ } else {
+ fprintf (state->ofp, ".L%d", call_id);
+ }
+
+ } else {
+
+ if ((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) {
+ fprintf (state->ofp, "L%d_%d", label, call_id);
+ } else {
+ fprintf (state->ofp, ".L%d_%d", label, call_id);
+ }
+
+ }
+
+}
+
+static int emit_inline_parameter_reference_if_any (const char **pp, int argc, int stack_bytes) {
+
+ const char *p = *pp;
+ const char *q;
+
+ int offset = 0;
+ int param_index;
+ int stack_offset;
+
+ if (!(state->syntax & ASM_SYNTAX_MASM)) {
+ return 0;
+ }
+
+ if (*p != '[') {
+ return 0;
+ }
+
+ q = p + 1;
+
+ while (*q == ' ' || *q == '\t') {
+ q++;
+ }
+
+ if (strncmp (q, "ebp", 3) != 0) {
+ return 0;
+ }
+
+ q += 3;
+
+ while (*q == ' ' || *q == '\t') {
+ q++;
+ }
+
+ if (*q != '+') {
+ return 0;
+ }
+
+ q++;
+
+ while (*q == ' ' || *q == '\t') {
+ q++;
+ }
+
+ if (*q < '0' || *q > '9') {
+ return 0;
+ }
+
+ while (*q >= '0' && *q <= '9') {
+
+ offset = (offset * 10) + (*q - '0');
+ q++;
+
+ }
+
+ while (*q == ' ' || *q == '\t') {
+ q++;
+ }
+
+ if (*q != ']') {
+ return 0;
+ }
+
+ if (offset < 8 || ((offset - 8) % 4) != 0) {
+ return 0;
+ }
+
+ param_index = (offset - 8) / 4;
+
+ if (param_index < 0 || param_index >= argc) {
+ return 0;
+ }
+
+ /*
+ * Inline arguments are copied into a compiler-owned temporary stack
+ * area before the inline body is emitted. Parameter 0 lives at the
+ * lowest address in that area, so [ebp + 8] maps to [esp],
+ * [ebp + 12] maps to [esp + 4], [ebp + 16] maps to [esp + 8], etc.
+ * If the inlined body changes ESP temporarily, stack_bytes keeps all
+ * parameter references pointed at the same argument copies.
+ */
+ stack_offset = stack_bytes + (param_index * 4);
+
+ if (stack_offset == 0) {
+ fprintf (state->ofp, "[esp]");
+ } else if (stack_offset > 0) {
+ fprintf (state->ofp, "[esp + %d]", stack_offset);
+ } else {
+ fprintf (state->ofp, "[esp - %d]", -stack_offset);
+ }
+
+ *pp = q + 1;
+ return 1;
+
+}
+
+static void emit_inline_line_substituted (const char *line, size_t len, int return_label, int call_id, int argc, int stack_bytes) {
+
+ const char *p = line;
+ const char *end = line + len;
+
+ while (p < end) {
+
+ if ((state->syntax & ASM_SYNTAX_MASM) && *p == 'L' && p + 1 < end && p[1] >= '0' && p[1] <= '9') {
+
+ const char *q = p + 1;
+ int label = 0;
+
+ while (q < end && *q >= '0' && *q <= '9') {
+
+ label = (label * 10) + (*q - '0');
+ q++;
+
+ }
+
+ emit_inline_label_reference (label, return_label, call_id);
+ p = q;
+
+ continue;
+
+ }
+
+ if (!(state->syntax & ASM_SYNTAX_MASM) && p + 2 < end && p[0] == '.' && p[1] == 'L' && p[2] >= '0' && p[2] <= '9') {
+
+ const char *q = p + 2;
+ int label = 0;
+
+ while (q < end && *q >= '0' && *q <= '9') {
+
+ label = (label * 10) + (*q - '0');
+ q++;
+
+ }
+
+ emit_inline_label_reference (label, return_label, call_id);
+ p = q;
+
+ continue;
+
+ }
+
+ if (emit_inline_parameter_reference_if_any (&p, argc, stack_bytes)) {
+ continue;
+ }
+
+ fputc (*p++, state->ofp);
+
+ }
+
+}
+
+static int inline_line_stack_delta (const char *line, size_t len) {
+
+ const char *p = line;
+ long n = 0;
+ int neg = 0;
+
+ while (len > 0 && (*p == ' ' || *p == '\t')) {
+
+ p++;
+ len--;
+
+ }
+
+ while (len > 0 && (p[len - 1] == '\r' || p[len - 1] == '\n' || p[len - 1] == ' ' || p[len - 1] == '\t')) {
+ len--;
+ }
+
+ if (len >= 4 && strncmp (p, "push", 4) == 0 && (p[4] == ' ' || p[4] == '\t')) {
+ return 4;
+ }
+
+ if (len >= 3 && strncmp (p, "pop", 3) == 0 && (p[3] == ' ' || p[3] == '\t')) {
+ return -4;
+ }
+
+ if (len >= 8 && (strncmp (p, "sub esp,", 8) == 0 || strncmp (p, "add esp,", 8) == 0)) {
+
+ neg = (p[0] == 'a');
+
+ p += 8;
+ len -= 8;
+
+ while (len > 0 && (*p == ' ' || *p == '\t')) {
+
+ p++;
+ len--;
+
+ }
+
+ while (len > 0 && *p >= '0' && *p <= '9') {
+
+ n = (n * 10) + (*p - '0');
+
+ p++;
+ len--;
+
+ }
+
+ return neg ? -(int) n : (int) n;
+
+ }
+
+ if (len >= 10 && (strncmp (p, "subl $", 6) == 0 || strncmp (p, "addl $", 6) == 0)) {
+
+ neg = (p[0] == 'a');
+
+ p += 6;
+ len -= 6;
+
+ while (len > 0 && *p >= '0' && *p <= '9') {
+
+ n = (n * 10) + (*p - '0');
+
+ p++;
+ len--;
+
+ }
+
+ while (len > 0 && (*p == ' ' || *p == '\t')) {
+
+ p++;
+ len--;
+
+ }
+
+ if (len >= 6 && strncmp (p, ", %esp", 6) == 0) {
+ return neg ? -(int) n : (int) n;
+ }
+
+ }
+
+ return 0;
+
+}
+
+static int inline_body_stack_delta (const char *body) {
+
+ const char *p = body;
+ int stack_bytes = 0;
+
+ if (!body) {
+ return 0;
+ }
+
+ while (*p) {
+
+ const char *line = p;
+ const char *eol = strchr (p, '\n');
+ const char *next = eol ? eol + 1 : p + strlen (p);
+ size_t len = (size_t) (next - line);
+
+ stack_bytes += inline_line_stack_delta (line, len);
+ p = next;
+
+ }
+
+ return stack_bytes;
+
+}
+
+static int inline_parse_mov_eax_imm (const char *line, long *value) {
+
+ const char *p = line;
+
+ int neg = 0;
+ long v = 0;
+
+ while (*p == ' ' || *p == '\t') {
+ p++;
+ }
+
+ if (strncmp (p, "mov eax,", 8) != 0) {
+ return 0;
+ }
+
+ p += 8;
+
+ while (*p == ' ' || *p == '\t') {
+ p++;
+ }
+
+ if (*p == '-') {
+
+ neg = 1;
+ p++;
+
+ }
+
+ if (*p < '0' || *p > '9') {
+ return 0;
+ }
+
+ while (*p >= '0' && *p <= '9') {
+
+ v = (v * 10) + (*p - '0');
+ p++;
+
+ }
+
+ while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+ p++;
+ }
+
+ if (*p != '\0') {
+ return 0;
+ }
+
+ *value = neg ? -v : v;
+ return 1;
+
+}
+
+static int inline_parse_mov_edx_imm (const char *line, long *value) {
+
+ const char *p = line;
+
+ int neg = 0;
+ long v = 0;
+
+ while (*p == ' ' || *p == '\t') {
+ p++;
+ }
+
+ if (strncmp (p, "mov edx,", 8) != 0) {
+ return 0;
+ }
+
+ p += 8;
+
+ while (*p == ' ' || *p == '\t') {
+ p++;
+ }
+
+ if (*p == '-') {
+
+ neg = 1;
+ p++;
+
+ }
+
+ if (*p < '0' || *p > '9') {
+ return 0;
+ }
+
+ while (*p >= '0' && *p <= '9') {
+
+ v = (v * 10) + (*p - '0');
+ p++;
+
+ }
+
+ while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+ p++;
+ }
+
+ if (*p != '\0') {
+ return 0;
+ }
+
+ *value = neg ? -v : v;
+ return 1;
+
+}
+
+static int inline_line_is_exact (const char *line, const char *text) {
+
+ const char *p = line;
+
+ while (*p == ' ' || *p == '\t') {
+ p++;
+ }
+
+ if (strncmp (p, text, strlen (text)) != 0) {
+ return 0;
+ }
+
+ p += strlen (text);
+
+ while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+ p++;
+ }
+
+ return *p == '\0';
+
+}
+
+
+static int inline_parse_jump_label (const char *line, const char *op, char *label, size_t label_size);
+
+static unsigned long inline_u32 (long v) {
+ return ((unsigned long) v) & 0xffffffffUL;
+}
+
+static long inline_s32 (long v) {
+
+ unsigned long u = inline_u32 (v);
+
+ if (u & 0x80000000UL) {
+ return (long) (u - 0x100000000UL);
+ }
+
+ return (long) u;
+
+}
+
+static int inline_fold_binary_eax_edx (const char *line, long lhs, long rhs, long *out) {
+
+ unsigned long ulhs;
+ unsigned long urhs;
+ unsigned long ures;
+
+ if (!line || !out) {
+ return 0;
+ }
+
+ ulhs = inline_u32 (lhs);
+ urhs = inline_u32 (rhs);
+
+ if (inline_line_is_exact (line, "add eax, edx")) {
+ ures = ulhs + urhs;
+ } else if (inline_line_is_exact (line, "sub eax, edx")) {
+ ures = ulhs - urhs;
+ } else if (inline_line_is_exact (line, "imul eax, edx")) {
+ ures = (unsigned long) (inline_s32 (lhs) * inline_s32 (rhs));
+ } else if (inline_line_is_exact (line, "and eax, edx")) {
+ ures = ulhs & urhs;
+ } else if (inline_line_is_exact (line, "or eax, edx")) {
+ ures = ulhs | urhs;
+ } else if (inline_line_is_exact (line, "xor eax, edx")) {
+ ures = ulhs ^ urhs;
+ } else {
+ return 0;
+ }
+
+ *out = inline_s32 ((long) ures);
+ return 1;
+
+}
+
+static int inline_fold_shift_eax_edx (const char *line, long lhs, long rhs, long *out) {
+
+ unsigned long ulhs;
+ unsigned long ures;
+ unsigned int count;
+
+ long slhs;
+
+ if (!line || !out) {
+ return 0;
+ }
+
+ count = (unsigned int) (inline_u32 (rhs) & 31UL);
+ ulhs = inline_u32 (lhs);
+
+ if (inline_line_is_exact (line, "shl eax, cl")) {
+ ures = ulhs << count;
+ } else if (inline_line_is_exact (line, "sal eax, cl")) {
+ ures = ulhs << count;
+ } else if (inline_line_is_exact (line, "sar eax, cl")) {
+
+ slhs = inline_s32 (lhs);
+ ures = inline_u32 (slhs >> count);
+
+ } else if (inline_line_is_exact (line, "shr eax, cl")) {
+ ures = ulhs >> count;
+ } else {
+ return 0;
+ }
+
+ *out = inline_s32 ((long) ures);
+ return 1;
+
+}
+
+static int inline_fold_div_eax_edx (long lhs, long rhs, long *out) {
+
+ long slhs;
+ long srhs;
+
+ if (!out) {
+ return 0;
+ }
+
+ srhs = inline_s32 (rhs);
+
+ if (srhs == 0) {
+ return 0;
+ }
+
+ slhs = inline_s32 (lhs);
+
+ *out = inline_s32 (slhs / srhs);
+ return 1;
+
+}
+
+static int inline_fold_mod_eax_edx (long lhs, long rhs, long *out) {
+
+ long slhs;
+ long srhs;
+
+ if (!out) {
+ return 0;
+ }
+
+ srhs = inline_s32 (rhs);
+
+ if (srhs == 0) {
+ return 0;
+ }
+
+ slhs = inline_s32 (lhs);
+
+ *out = inline_s32 (slhs % srhs);
+ return 1;
+
+}
+
+static int inline_parse_cond_jump_label (const char *line, char *op, size_t op_size, char *label, size_t label_size) {
+
+ static const char *const ops[] = {
+ "jz", "jnz", "je", "jne", "ja", "jae", "jb", "jbe",
+ "jg", "jge", "jl", "jle", "js", "jns", 0
+ };
+
+ int i;
+
+ if (!op || op_size == 0) {
+ return 0;
+ }
+
+ for (i = 0; ops[i]; i++) {
+
+ if (inline_parse_jump_label (line, ops[i], label, label_size)) {
+
+ strncpy (op, ops[i], op_size - 1);
+ op[op_size - 1] = '\0';
+
+ return 1;
+
+ }
+
+ }
+
+ return 0;
+
+}
+
+static int inline_eval_test_jump (const char *op, long value) {
+
+ long svalue = inline_s32 (value);
+
+ if (strcmp (op, "jz") == 0 || strcmp (op, "je") == 0) {
+ return svalue == 0;
+ }
+
+ if (strcmp (op, "jnz") == 0 || strcmp (op, "jne") == 0) {
+ return svalue != 0;
+ }
+
+ if (strcmp (op, "js") == 0) {
+ return svalue < 0;
+ }
+
+ if (strcmp (op, "jns") == 0) {
+ return svalue >= 0;
+ }
+
+ return 0;
+
+}
+
+static int inline_eval_cmp_jump (const char *op, long lhs, long rhs) {
+
+ long slhs = inline_s32 (lhs);
+ long srhs = inline_s32 (rhs);
+
+ unsigned long ulhs = inline_u32 (lhs);
+ unsigned long urhs = inline_u32 (rhs);
+
+ if (strcmp (op, "jz") == 0 || strcmp (op, "je") == 0) {
+ return slhs == srhs;
+ }
+
+ if (strcmp (op, "jnz") == 0 || strcmp (op, "jne") == 0) {
+ return slhs != srhs;
+ }
+
+ if (strcmp (op, "jg") == 0) {
+ return slhs > srhs;
+ }
+
+ if (strcmp (op, "jge") == 0) {
+ return slhs >= srhs;
+ }
+
+ if (strcmp (op, "jl") == 0) {
+ return slhs < srhs;
+ }
+
+ if (strcmp (op, "jle") == 0) {
+ return slhs <= srhs;
+ }
+
+ if (strcmp (op, "ja") == 0) {
+ return ulhs > urhs;
+ }
+
+ if (strcmp (op, "jae") == 0) {
+ return ulhs >= urhs;
+ }
+
+ if (strcmp (op, "jb") == 0) {
+ return ulhs < urhs;
+ }
+
+ if (strcmp (op, "jbe") == 0) {
+ return ulhs <= urhs;
+ }
+
+ return 0;
+
+}
+
+static int inline_parse_jump_label (const char *line, const char *op, char *label, size_t label_size) {
+
+ const char *p = line;
+ size_t n = 0;
+
+ if (!label || label_size == 0) {
+ return 0;
+ }
+
+ while (*p == ' ' || *p == '\t') {
+ p++;
+ }
+
+ if (strncmp (p, op, strlen (op)) != 0) {
+ return 0;
+ }
+
+ p += strlen (op);
+
+ if (*p != ' ' && *p != '\t') {
+ return 0;
+ }
+
+ while (*p == ' ' || *p == '\t') {
+ p++;
+ }
+
+ if (!((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || *p == '_' || *p == '.')) {
+ return 0;
+ }
+
+ while ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') ||
+ (*p >= '0' && *p <= '9') || *p == '_' || *p == '.') {
+
+ if (n + 1 < label_size) {
+ label[n++] = *p;
+ }
+
+ p++;
+ }
+
+ label[n] = '\0';
+
+ while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+ p++;
+ }
+
+ return *p == '\0';
+
+}
+
+
+
+static int inline_parse_any_jump_label (const char *line, char *label, size_t label_size) {
+
+ static const char *const ops[] = {
+ "jmp", "jz", "jnz", "je", "jne", "jc", "jnc",
+ "ja", "jae", "jb", "jbe", "jg", "jge", "jl", "jle",
+ "js", "jns", "jo", "jno", "jp", "jpe", "jnp", "jpo",
+ 0
+ };
+
+ int i;
+
+ for (i = 0; ops[i]; i++) {
+
+ if (inline_parse_jump_label (line, ops[i], label, label_size)) {
+ return 1;
+ }
+
+ }
+
+ return 0;
+
+}
+
+static int inline_parse_label_definition (const char *line, char *label, size_t label_size) {
+
+ const char *p = line;
+ size_t n = 0;
+
+ if (!label || label_size == 0) {
+ return 0;
+ }
+
+ while (*p == ' ' || *p == '\t') {
+ p++;
+ }
+
+ if (!((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || *p == '_' || *p == '.')) {
+ return 0;
+ }
+
+ while ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') ||
+ (*p >= '0' && *p <= '9') || *p == '_' || *p == '.') {
+
+ if (n + 1 < label_size) {
+ label[n++] = *p;
+ }
+
+ p++;
+ }
+
+ label[n] = '\0';
+
+ if (*p != ':') {
+ return 0;
+ }
+
+ p++;
+
+ while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+ p++;
+ }
+
+ return *p == '\0';
+
+}
+
+static const char *inline_find_chain_target (char **from, char **to, int count, const char *label) {
+
+ int i;
+
+ if (!label) {
+ return 0;
+ }
+
+ for (i = 0; i < count; i++) {
+
+ if (from[i] && to[i] && strcmp (from[i], label) == 0) {
+ return to[i];
+ }
+
+ }
+
+ return 0;
+
+}
+
+static void inline_copy_string (char *dst, const char *src, size_t dst_size) {
+
+ size_t len;
+
+ if (!dst || dst_size == 0) {
+ return;
+ }
+
+ if (!src) {
+
+ dst[0] = '\0';
+ return;
+
+ }
+
+ len = strlen (src);
+
+ if (len >= dst_size) {
+ len = dst_size - 1;
+ }
+
+ memcpy (dst, src, len);
+ dst[len] = '\0';
+
+}
+
+static int inline_resolve_jump_chain (char **from, char **to, int count, const char *label, char *out, size_t out_size) {
+
+ const char *cur = label;
+ const char *next;
+
+ int changed = 0;
+ int depth = 0;
+
+ if (!label || !out || out_size == 0) {
+ return 0;
+ }
+
+ while (depth++ < count) {
+
+ next = inline_find_chain_target (from, to, count, cur);
+
+ if (!next || strcmp (next, cur) == 0) {
+ break;
+ }
+
+ cur = next;
+ changed = 1;
+
+ }
+
+ if (!changed || strcmp (cur, label) == 0) {
+ return 0;
+ }
+
+ if (out && cur && out_size > 0) {
+ inline_copy_string (out, cur, out_size);
+ }
+
+ return 1;
+
+}
+
+static int inline_parse_esp_reference (const char *p, int *offset) {
+
+ const char *start = p;
+
+ int off = 0;
+ int neg = 0;
+
+ if (strncmp (p, "[esp]", 5) == 0) {
+
+ *offset = 0;
+ return 5;
+
+ }
+
+ if (strncmp (p, "[esp + ", 7) == 0) {
+ p += 7;
+ } else if (strncmp (p, "[esp+", 5) == 0) {
+ p += 5;
+ } else if (strncmp (p, "[esp - ", 7) == 0) {
+ neg = 1;
+ p += 7;
+ } else if (strncmp (p, "[esp-", 5) == 0) {
+ neg = 1;
+ p += 5;
+ } else {
+ return 0;
+ }
+
+ if (*p < '0' || *p > '9') {
+ return 0;
+ }
+
+ while (*p >= '0' && *p <= '9') {
+
+ off = (off * 10) + (*p - '0');
+ p++;
+
+ }
+
+ if (*p != ']') {
+ return 0;
+ }
+
+ *offset = neg ? -off : off;
+ return (int) ((p + 1) - start);
+
+}
+
+static int inline_parse_store_eax_to_esp (const char *line, int *offset) {
+
+ const char *p = line;
+ int n;
+
+ while (*p == ' ' || *p == '\t') {
+ p++;
+ }
+
+ if (strncmp (p, "mov dword ptr ", 14) != 0) {
+ return 0;
+ }
+
+ p += 14;
+ n = inline_parse_esp_reference (p, offset);
+
+ if (!n) {
+ return 0;
+ }
+
+ p += n;
+
+ while (*p == ' ' || *p == '\t') {
+ p++;
+ }
+
+ if (strncmp (p, ", eax", 5) != 0) {
+ return 0;
+ }
+
+ p += 5;
+
+ while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+ p++;
+ }
+
+ return *p == '\0';
+
+}
+
+static int inline_parse_load_eax_from_esp (const char *line, int *offset) {
+
+ const char *p = line;
+ int n;
+
+ while (*p == ' ' || *p == '\t') {
+ p++;
+ }
+
+ if (strncmp (p, "mov eax, dword ptr ", 19) != 0) {
+ return 0;
+ }
+
+ p += 19;
+ n = inline_parse_esp_reference (p, offset);
+
+ if (!n) {
+ return 0;
+ }
+
+ p += n;
+
+ while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+ p++;
+ }
+
+ return *p == '\0';
+
+}
+
+static int inline_parse_load_edx_from_esp (const char *line, int *offset) {
+
+ const char *p = line;
+ int n;
+
+ while (*p == ' ' || *p == '\t') {
+ p++;
+ }
+
+ if (strncmp (p, "mov edx, dword ptr ", 19) != 0) {
+ return 0;
+ }
+
+ p += 19;
+ n = inline_parse_esp_reference (p, offset);
+
+ if (!n) {
+ return 0;
+ }
+
+ p += n;
+
+ while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+ p++;
+ }
+
+ return *p == '\0';
+
+}
+
+static int inline_parse_addsub_esp_imm1 (const char *line, int *offset, long *delta) {
+
+ const char *p = line;
+ int n;
+ int is_sub = 0;
+
+ while (*p == ' ' || *p == '\t') {
+ p++;
+ }
+
+ if (strncmp (p, "add dword ptr ", 14) == 0) {
+ p += 14;
+ } else if (strncmp (p, "sub dword ptr ", 14) == 0) {
+
+ is_sub = 1;
+ p += 14;
+
+ } else {
+ return 0;
+ }
+
+ n = inline_parse_esp_reference (p, offset);
+
+ if (!n) {
+ return 0;
+ }
+
+ p += n;
+
+ while (*p == ' ' || *p == '\t') {
+ p++;
+ }
+
+ if (strncmp (p, ", 1", 3) != 0) {
+ return 0;
+ }
+
+ p += 3;
+
+ while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+ p++;
+ }
+
+ if (*p != '\0') {
+ return 0;
+ }
+
+ *delta = is_sub ? -1 : 1;
+ return 1;
+
+}
+
+static char *inline_dup_text_slice (const char *start, size_t len) {
+
+ char *out = (char *) malloc (len + 1);
+
+ if (!out) {
+ return 0;
+ }
+
+ memcpy (out, start, len);
+ out[len] = '\0';
+
+ return out;
+
+}
+
+static void inline_clear_slots (int *slot_valid, long *slot_value) {
+
+ int i;
+
+ for (i = 0; i < 64; i++) {
+
+ slot_valid[i] = 0;
+ slot_value[i] = 0;
+
+ }
+
+}
+
+static void emit_inline_optimized_text (const char *text, FILE *ofp) {
+
+ const char *p = text;
+
+ char **lines;
+ char **repl;
+
+ int *skip;
+ int *store_slot;
+ int count = 0;
+ int i;
+
+ long slot_value[64];
+
+ int slot_valid[64];
+ int slot_real_read[64];
+
+ long eax_value = 0;
+
+ int eax_valid = 0;
+ int alloc_line = -1;
+ int free_line = -1;
+ int alloc_bytes = 0;
+ int can_remove_area = 0;
+
+ char **chain_from = 0;
+ char **chain_to = 0;
+
+ int *label_refs = 0;
+
+ if (!text || !ofp) {
+ return;
+ }
+
+ for (p = text; *p; p++) {
+
+ if (*p == '\n') {
+ count++;
+ }
+
+ }
+
+ if (p != text && p[-1] != '\n') {
+ count++;
+ }
+
+ if (count <= 0) {
+ return;
+ }
+
+ lines = (char **) calloc ((size_t) count, sizeof (*lines));
+ repl = (char **) calloc ((size_t) count, sizeof (*repl));
+ skip = (int *) calloc ((size_t) count, sizeof (*skip));
+ store_slot = (int *) malloc ((size_t) count * sizeof (*store_slot));
+ chain_from = (char **) calloc ((size_t) count, sizeof (*chain_from));
+ chain_to = (char **) calloc ((size_t) count, sizeof (*chain_to));
+ label_refs = (int *) calloc ((size_t) count, sizeof (*label_refs));
+
+ if (!lines || !repl || !skip || !store_slot || !chain_from || !chain_to || !label_refs) {
+
+ free (lines);
+ free (repl);
+ free (skip);
+ free (store_slot);
+ free (chain_from);
+ free (chain_to);
+ free (label_refs);
+
+ fputs (text, ofp);
+ return;
+
+ }
+
+ for (i = 0; i < count; i++) {
+ store_slot[i] = -1;
+ }
+
+ p = text;
+ i = 0;
+
+ while (*p && i < count) {
+
+ const char *line = p;
+ const char *eol = strchr (p, '\n');
+ const char *next = eol ? eol + 1 : p + strlen (p);
+ size_t len = (size_t) (next - line);
+
+ lines[i++] = inline_dup_text_slice (line, len);
+ p = next;
+
+ }
+
+ count = i;
+ inline_clear_slots (slot_valid, slot_value);
+
+ for (i = 0; i < 64; i++) {
+ slot_real_read[i] = 0;
+ }
+
+ for (i = 0; i < count; i++) {
+
+ char *buf = lines[i];
+ int offset;
+ int slot;
+ long value;
+ long mem_delta;
+ int delta;
+
+ if (!buf) {
+ continue;
+ }
+
+ delta = inline_line_stack_delta (buf, strlen (buf));
+
+ if (delta > 0 && alloc_line < 0) {
+
+ alloc_line = i;
+ alloc_bytes = delta;
+
+ eax_valid = 0;
+ continue;
+
+ }
+
+ if (delta < 0 && alloc_line >= 0 && -delta == alloc_bytes) {
+
+ free_line = i;
+ eax_valid = 0;
+
+ continue;
+
+ }
+
+ if (inline_parse_mov_eax_imm (buf, &value)) {
+
+ eax_value = value;
+ eax_valid = 1;
+
+ continue;
+
+ }
+
+ if (inline_parse_store_eax_to_esp (buf, &offset)) {
+
+ if (offset >= 0 && (offset % 4) == 0) {
+
+ slot = offset / 4;
+
+ if (slot >= 0 && slot < 64) {
+
+ store_slot[i] = slot;
+
+ if (eax_valid) {
+
+ slot_valid[slot] = 1;
+ slot_value[slot] = eax_value;
+
+ } else {
+ slot_valid[slot] = 0;
+ }
+
+ }
+
+ }
+
+ continue;
+
+ }
+
+ if (inline_parse_load_eax_from_esp (buf, &offset)) {
+
+ if (offset >= 0 && (offset % 4) == 0) {
+
+ slot = offset / 4;
+
+ if (slot >= 0 && slot < 64 && slot_valid[slot]) {
+
+ char tmp[128];
+ sprintf (tmp, " mov eax, %ld\n", slot_value[slot]);
+
+ repl[i] = inline_dup_text_slice (tmp, strlen (tmp));
+
+ eax_value = slot_value[slot];
+ eax_valid = 1;
+
+ continue;
+
+ }
+
+ if (slot >= 0 && slot < 64) {
+ slot_real_read[slot] = 1;
+ }
+
+ }
+
+ eax_valid = 0;
+ continue;
+ }
+
+ if (inline_parse_load_edx_from_esp (buf, &offset)) {
+
+ if (offset >= 0 && (offset % 4) == 0) {
+
+ slot = offset / 4;
+
+ if (slot >= 0 && slot < 64 && slot_valid[slot]) {
+
+ char tmp[128];
+ sprintf (tmp, " mov edx, %ld\n", slot_value[slot]);
+
+ repl[i] = inline_dup_text_slice (tmp, strlen (tmp));
+ continue;
+
+ }
+
+ if (slot >= 0 && slot < 64) {
+ slot_real_read[slot] = 1;
+ }
+
+ }
+
+ continue;
+
+ }
+
+ if (inline_parse_addsub_esp_imm1 (buf, &offset, &mem_delta)) {
+
+ if (offset >= 0 && (offset % 4) == 0) {
+
+ slot = offset / 4;
+
+ if (slot >= 0 && slot < 64) {
+
+ if (slot_valid[slot]) {
+ slot_value[slot] = inline_s32 (slot_value[slot] + mem_delta);
+ skip[i] = 1;
+ }
+
+ continue;
+ }
+ }
+
+ eax_valid = 0;
+ continue;
+
+ }
+
+ if (strstr (buf, "esp")) {
+
+ inline_clear_slots (slot_valid, slot_value);
+ eax_valid = 0;
+
+ } else if (strstr (buf, "eax")) {
+ eax_valid = 0;
+ }
+
+ }
+
+ if (alloc_line >= 0 && free_line > alloc_line && alloc_bytes > 0) {
+
+ int any_remaining_esp = 0;
+ int slots = alloc_bytes / 4;
+
+ for (i = 0; i < count; i++) {
+
+ if (i == alloc_line || i == free_line) {
+ continue;
+ }
+
+ if (store_slot[i] >= 0 && store_slot[i] < slots && !slot_real_read[store_slot[i]]) {
+ continue;
+ }
+
+ if (repl[i]) {
+
+ if (strstr (repl[i], "esp")) {
+ any_remaining_esp = 1;
+ break;
+ }
+
+ } else if (lines[i] && strstr (lines[i], "esp")) {
+
+ any_remaining_esp = 1;
+ break;
+
+ }
+
+ }
+
+ if (!any_remaining_esp) {
+
+ can_remove_area = 1;
+ skip[alloc_line] = 1;
+ skip[free_line] = 1;
+
+ for (i = 0; i < count; i++) {
+
+ if (store_slot[i] >= 0 && store_slot[i] < slots && !slot_real_read[store_slot[i]]) {
+ skip[i] = 1;
+ }
+
+ }
+
+ /*
+ * If an argument copy was folded away, the immediately preceding
+ * constant load was only there to feed that copy. Drop it too;
+ * otherwise multi-argument inline calls leave noise like:
+ *
+ * mov eax, 1
+ * mov eax, 2
+ *
+ * after both temporary argument stores have been removed.
+ */
+ for (i = 0; i < count; i++) {
+
+ long ignored_value;
+ int j;
+
+ if (!skip[i] || store_slot[i] < 0) {
+ continue;
+ }
+
+ for (j = i - 1; j >= 0; j--) {
+
+ if (skip[j]) {
+ continue;
+ }
+
+ if (inline_parse_mov_eax_imm (lines[j], &ignored_value)) {
+ skip[j] = 1;
+ }
+
+ break;
+
+ }
+
+ }
+
+ }
+
+ }
+
+ (void) can_remove_area;
+
+ for (i = 0; i < count; i++) {
+
+ long first_value;
+ int j;
+
+ if (skip[i] || !lines[i] || !inline_parse_mov_eax_imm (lines[i], &first_value)) {
+ continue;
+ }
+
+ for (j = i + 1; j < count; j++) {
+
+ long next_value;
+ const char *check;
+
+ if (skip[j]) {
+ continue;
+ }
+
+ check = repl[j] ? repl[j] : lines[j];
+
+ if (!check) {
+ continue;
+ }
+
+ if (!strstr (check, "eax")) {
+ continue;
+ }
+
+ if (inline_parse_mov_eax_imm (check, &next_value) && next_value == first_value) {
+ skip[i] = 1;
+ }
+
+ break;
+
+ }
+
+ }
+
+ for (i = 0; i + 3 < count; i++) {
+
+ long lhs;
+ long rhs;
+
+ char cond_label[128];
+ char opbuf[16];
+ char tmp[192];
+
+ const char *l0;
+ const char *l1;
+ const char *l2;
+ const char *l3;
+
+ int taken;
+
+ if (skip[i]) {
+ continue;
+ }
+
+ l0 = repl[i] ? repl[i] : lines[i];
+ l1 = repl[i + 1] ? repl[i + 1] : lines[i + 1];
+ l2 = repl[i + 2] ? repl[i + 2] : lines[i + 2];
+ l3 = repl[i + 3] ? repl[i + 3] : lines[i + 3];
+
+ if (!l0 || !l1 || !l2 || !l3) {
+ continue;
+ }
+
+ if (!inline_parse_mov_eax_imm (l0, &lhs)) {
+ continue;
+ }
+
+ if (!inline_parse_mov_edx_imm (l1, &rhs)) {
+ continue;
+ }
+
+ if (!inline_line_is_exact (l2, "cmp eax, edx")) {
+ continue;
+ }
+
+ if (!inline_parse_cond_jump_label (l3, opbuf, sizeof (opbuf), cond_label, sizeof (cond_label))) {
+ continue;
+ }
+
+ taken = inline_eval_cmp_jump (opbuf, lhs, rhs);
+
+ if (taken) {
+
+ sprintf (tmp, " jmp %s\n", cond_label);
+ free (repl[i]);
+
+ repl[i] = inline_dup_text_slice (tmp, strlen (tmp));
+
+ skip[i + 1] = 1;
+ skip[i + 2] = 1;
+ skip[i + 3] = 1;
+
+ } else {
+
+ skip[i] = 1;
+ skip[i + 1] = 1;
+ skip[i + 2] = 1;
+ skip[i + 3] = 1;
+
+ }
+
+ }
+
+ for (i = 0; i + 4 < count; i++) {
+
+ long lhs;
+ long rhs;
+
+ char false_label[128];
+ char true_label[128];
+ char opbuf[16];
+ char tmp[192];
+
+ const char *l0;
+ const char *l1;
+ const char *l2;
+ const char *l3;
+ const char *l4;
+
+ if (skip[i]) {
+ continue;
+ }
+
+ l0 = repl[i] ? repl[i] : lines[i];
+ l1 = repl[i + 1] ? repl[i + 1] : lines[i + 1];
+ l2 = repl[i + 2] ? repl[i + 2] : lines[i + 2];
+ l3 = repl[i + 3] ? repl[i + 3] : lines[i + 3];
+ l4 = repl[i + 4] ? repl[i + 4] : lines[i + 4];
+
+ if (!l0 || !l1 || !l2 || !l3 || !l4) {
+ continue;
+ }
+
+ if (!inline_parse_mov_eax_imm (l0, &lhs)) {
+ continue;
+ }
+
+ if (!inline_parse_mov_edx_imm (l1, &rhs)) {
+ continue;
+ }
+
+ if (!inline_line_is_exact (l2, "cmp eax, edx")) {
+ continue;
+ }
+
+ if (!inline_parse_cond_jump_label (l3, opbuf, sizeof (opbuf), false_label, sizeof (false_label))) {
+ continue;
+ }
+
+ if (!inline_parse_jump_label (l4, "jmp", true_label, sizeof (true_label))) {
+ continue;
+ }
+
+ sprintf (tmp, " jmp %s\n", inline_eval_cmp_jump (opbuf, lhs, rhs) ? false_label : true_label);
+ free (repl[i]);
+
+ repl[i] = inline_dup_text_slice (tmp, strlen (tmp));
+
+ skip[i + 1] = 1;
+ skip[i + 2] = 1;
+ skip[i + 3] = 1;
+ skip[i + 4] = 1;
+
+ }
+
+ for (i = 0; i + 4 < count; i++) {
+
+ long lhs;
+ long rhs;
+
+ char cond_label[128];
+ char opbuf[16];
+ char tmp[192];
+
+ const char *l0;
+ const char *l1;
+ const char *l2;
+ const char *l3;
+ const char *l4;
+
+ int taken;
+
+ if (skip[i]) {
+ continue;
+ }
+
+ l0 = repl[i] ? repl[i] : lines[i];
+ l1 = repl[i + 1] ? repl[i + 1] : lines[i + 1];
+ l2 = repl[i + 2] ? repl[i + 2] : lines[i + 2];
+ l3 = repl[i + 3] ? repl[i + 3] : lines[i + 3];
+ l4 = repl[i + 4] ? repl[i + 4] : lines[i + 4];
+
+ if (!l0 || !l1 || !l2 || !l3 || !l4) {
+ continue;
+ }
+
+ if (!inline_parse_mov_eax_imm (l0, &lhs)) {
+ continue;
+ }
+
+ if (!inline_parse_mov_edx_imm (l1, &rhs)) {
+ continue;
+ }
+
+ if (!inline_fold_binary_eax_edx (l2, lhs, rhs, &lhs)) {
+ continue;
+ }
+
+ if (!inline_line_is_exact (l3, "test eax, eax")) {
+ continue;
+ }
+
+ if (!inline_parse_cond_jump_label (l4, opbuf, sizeof (opbuf), cond_label, sizeof (cond_label))) {
+ continue;
+ }
+
+ taken = inline_eval_test_jump (opbuf, lhs);
+
+ if (taken) {
+
+ sprintf (tmp, " jmp %s\n", cond_label);
+ free (repl[i]);
+
+ repl[i] = inline_dup_text_slice (tmp, strlen (tmp));
+
+ skip[i + 1] = 1;
+ skip[i + 2] = 1;
+ skip[i + 3] = 1;
+ skip[i + 4] = 1;
+
+ } else {
+
+ skip[i] = 1;
+ skip[i + 1] = 1;
+ skip[i + 2] = 1;
+ skip[i + 3] = 1;
+ skip[i + 4] = 1;
+
+ }
+
+ }
+
+ for (i = 0; i + 5 < count; i++) {
+
+ long lhs;
+ long rhs;
+ long cmp_rhs;
+ long folded;
+
+ char cond_label[128];
+ char opbuf[16];
+ char tmp[192];
+
+ const char *l0;
+ const char *l1;
+ const char *l2;
+ const char *l3;
+ const char *l4;
+ const char *l5;
+
+ int taken;
+
+ if (skip[i]) {
+ continue;
+ }
+
+ l0 = repl[i] ? repl[i] : lines[i];
+ l1 = repl[i + 1] ? repl[i + 1] : lines[i + 1];
+ l2 = repl[i + 2] ? repl[i + 2] : lines[i + 2];
+ l3 = repl[i + 3] ? repl[i + 3] : lines[i + 3];
+ l4 = repl[i + 4] ? repl[i + 4] : lines[i + 4];
+ l5 = repl[i + 5] ? repl[i + 5] : lines[i + 5];
+
+ if (!l0 || !l1 || !l2 || !l3 || !l4 || !l5) {
+ continue;
+ }
+
+ if (!inline_parse_mov_eax_imm (l0, &lhs)) {
+ continue;
+ }
+
+ if (!inline_parse_mov_edx_imm (l1, &rhs)) {
+ continue;
+ }
+
+ if (!inline_fold_binary_eax_edx (l2, lhs, rhs, &folded)) {
+ continue;
+ }
+
+ if (!inline_parse_mov_edx_imm (l3, &cmp_rhs)) {
+ continue;
+ }
+
+ if (!inline_line_is_exact (l4, "cmp eax, edx")) {
+ continue;
+ }
+
+ if (!inline_parse_cond_jump_label (l5, opbuf, sizeof (opbuf), cond_label, sizeof (cond_label))) {
+ continue;
+ }
+
+ taken = inline_eval_cmp_jump (opbuf, folded, cmp_rhs);
+
+ if (taken) {
+
+ sprintf (tmp, " jmp %s\n", cond_label);
+ free (repl[i]);
+
+ repl[i] = inline_dup_text_slice (tmp, strlen (tmp));
+
+ skip[i + 1] = 1;
+ skip[i + 2] = 1;
+ skip[i + 3] = 1;
+ skip[i + 4] = 1;
+ skip[i + 5] = 1;
+
+ } else {
+
+ skip[i] = 1;
+ skip[i + 1] = 1;
+ skip[i + 2] = 1;
+ skip[i + 3] = 1;
+ skip[i + 4] = 1;
+ skip[i + 5] = 1;
+
+ }
+
+ }
+
+ for (i = 0; i + 5 < count; i++) {
+
+ long lhs;
+ long rhs;
+
+ char false_label[128];
+ char true_label[128];
+ char opbuf[16];
+ char tmp[192];
+
+ const char *l0;
+ const char *l1;
+ const char *l2;
+ const char *l3;
+ const char *l4;
+ const char *l5;
+
+ if (skip[i]) {
+ continue;
+ }
+
+ l0 = repl[i] ? repl[i] : lines[i];
+ l1 = repl[i + 1] ? repl[i + 1] : lines[i + 1];
+ l2 = repl[i + 2] ? repl[i + 2] : lines[i + 2];
+ l3 = repl[i + 3] ? repl[i + 3] : lines[i + 3];
+ l4 = repl[i + 4] ? repl[i + 4] : lines[i + 4];
+ l5 = repl[i + 5] ? repl[i + 5] : lines[i + 5];
+
+ if (!l0 || !l1 || !l2 || !l3 || !l4 || !l5) {
+ continue;
+ }
+
+ if (!inline_parse_mov_eax_imm (l0, &lhs)) {
+ continue;
+ }
+
+ if (!inline_parse_mov_edx_imm (l1, &rhs)) {
+ continue;
+ }
+
+ if (!inline_fold_binary_eax_edx (l2, lhs, rhs, &lhs)) {
+ continue;
+ }
+
+ if (!inline_line_is_exact (l3, "test eax, eax")) {
+ continue;
+ }
+
+ if (!inline_parse_cond_jump_label (l4, opbuf, sizeof (opbuf), false_label, sizeof (false_label))) {
+ continue;
+ }
+
+ if (!inline_parse_jump_label (l5, "jmp", true_label, sizeof (true_label))) {
+ continue;
+ }
+
+ sprintf (tmp, " jmp %s\n", inline_eval_test_jump (opbuf, lhs) ? false_label : true_label);
+ free (repl[i]);
+
+ repl[i] = inline_dup_text_slice (tmp, strlen (tmp));
+
+ skip[i + 1] = 1;
+ skip[i + 2] = 1;
+ skip[i + 3] = 1;
+ skip[i + 4] = 1;
+ skip[i + 5] = 1;
+
+ }
+
+ for (i = 0; i + 6 < count; i++) {
+
+ long lhs;
+ long rhs;
+ long cmp_rhs;
+ long folded;
+
+ int jump_taken = 0;
+
+ char cond_label[128];
+ char fall_label[128];
+ char opbuf[16];
+ char tmp[192];
+
+ const char *l0;
+ const char *l1;
+ const char *l2;
+ const char *l3;
+ const char *l4;
+ const char *l5;
+ const char *l6;
+
+ if (skip[i]) {
+ continue;
+ }
+
+ l0 = repl[i] ? repl[i] : lines[i];
+ l1 = repl[i + 1] ? repl[i + 1] : lines[i + 1];
+ l2 = repl[i + 2] ? repl[i + 2] : lines[i + 2];
+ l3 = repl[i + 3] ? repl[i + 3] : lines[i + 3];
+ l4 = repl[i + 4] ? repl[i + 4] : lines[i + 4];
+ l5 = repl[i + 5] ? repl[i + 5] : lines[i + 5];
+ l6 = repl[i + 6] ? repl[i + 6] : lines[i + 6];
+
+ if (!l0 || !l1 || !l2 || !l3 || !l4 || !l5 || !l6) {
+ continue;
+ }
+
+ if (!inline_parse_mov_eax_imm (l0, &lhs)) {
+ continue;
+ }
+
+ if (!inline_parse_mov_edx_imm (l1, &rhs)) {
+ continue;
+ }
+
+ if (!inline_fold_binary_eax_edx (l2, lhs, rhs, &folded)) {
+ continue;
+ }
+
+ if (!inline_parse_mov_edx_imm (l3, &cmp_rhs)) {
+ continue;
+ }
+
+ if (!inline_line_is_exact (l4, "cmp eax, edx")) {
+ continue;
+ }
+
+ if (!inline_parse_cond_jump_label (l5, opbuf, sizeof (opbuf), cond_label, sizeof (cond_label))) {
+ continue;
+ }
+
+ jump_taken = inline_eval_cmp_jump (opbuf, folded, cmp_rhs);
+
+ if (!inline_parse_jump_label (l6, "jmp", fall_label, sizeof (fall_label))) {
+ continue;
+ }
+
+ sprintf (tmp, " jmp %s\n", jump_taken ? cond_label : fall_label);
+ free (repl[i]);
+
+ repl[i] = inline_dup_text_slice (tmp, strlen (tmp));
+
+ skip[i + 1] = 1;
+ skip[i + 2] = 1;
+ skip[i + 3] = 1;
+ skip[i + 4] = 1;
+ skip[i + 5] = 1;
+ skip[i + 6] = 1;
+
+ }
+
+ for (i = 0; i + 5 < count; i++) {
+
+ long lhs;
+ long rhs;
+ long folded;
+
+ char cond_label[128];
+ char opbuf[16];
+ char tmp[192];
+
+ const char *l0;
+ const char *l1;
+ const char *l2;
+ const char *l3;
+ const char *l4;
+ const char *l5;
+
+ int taken;
+
+ if (skip[i]) {
+ continue;
+ }
+
+ l0 = repl[i] ? repl[i] : lines[i];
+ l1 = repl[i + 1] ? repl[i + 1] : lines[i + 1];
+ l2 = repl[i + 2] ? repl[i + 2] : lines[i + 2];
+ l3 = repl[i + 3] ? repl[i + 3] : lines[i + 3];
+ l4 = repl[i + 4] ? repl[i + 4] : lines[i + 4];
+ l5 = repl[i + 5] ? repl[i + 5] : lines[i + 5];
+
+ if (!l0 || !l1 || !l2 || !l3 || !l4 || !l5) {
+ continue;
+ }
+
+ if (!inline_parse_mov_eax_imm (l0, &lhs)) {
+ continue;
+ }
+
+ if (!inline_parse_mov_edx_imm (l1, &rhs)) {
+ continue;
+ }
+
+ if (!inline_line_is_exact (l2, "mov ecx, edx")) {
+ continue;
+ }
+
+ if (!inline_fold_shift_eax_edx (l3, lhs, rhs, &folded)) {
+ continue;
+ }
+
+ if (!inline_line_is_exact (l4, "test eax, eax")) {
+ continue;
+ }
+
+ if (!inline_parse_cond_jump_label (l5, opbuf, sizeof (opbuf), cond_label, sizeof (cond_label))) {
+ continue;
+ }
+
+ taken = inline_eval_test_jump (opbuf, folded);
+
+ if (taken) {
+
+ sprintf (tmp, " jmp %s\n", cond_label);
+ free (repl[i]);
+
+ repl[i] = inline_dup_text_slice (tmp, strlen (tmp));
+
+ skip[i + 1] = 1;
+ skip[i + 2] = 1;
+ skip[i + 3] = 1;
+ skip[i + 4] = 1;
+ skip[i + 5] = 1;
+
+ } else {
+
+ skip[i] = 1;
+ skip[i + 1] = 1;
+ skip[i + 2] = 1;
+ skip[i + 3] = 1;
+ skip[i + 4] = 1;
+ skip[i + 5] = 1;
+
+ }
+
+ }
+
+ for (i = 0; i + 6 < count; i++) {
+
+ long lhs;
+ long rhs;
+ long cmp_rhs;
+ long folded;
+
+ char cond_label[128];
+ char opbuf[16];
+ char tmp[192];
+
+ const char *l0;
+ const char *l1;
+ const char *l2;
+ const char *l3;
+ const char *l4;
+ const char *l5;
+ const char *l6;
+
+ int taken;
+
+ if (skip[i]) {
+ continue;
+ }
+
+ l0 = repl[i] ? repl[i] : lines[i];
+ l1 = repl[i + 1] ? repl[i + 1] : lines[i + 1];
+ l2 = repl[i + 2] ? repl[i + 2] : lines[i + 2];
+ l3 = repl[i + 3] ? repl[i + 3] : lines[i + 3];
+ l4 = repl[i + 4] ? repl[i + 4] : lines[i + 4];
+ l5 = repl[i + 5] ? repl[i + 5] : lines[i + 5];
+ l6 = repl[i + 6] ? repl[i + 6] : lines[i + 6];
+
+ if (!l0 || !l1 || !l2 || !l3 || !l4 || !l5 || !l6) {
+ continue;
+ }
+
+ if (!inline_parse_mov_eax_imm (l0, &lhs)) {
+ continue;
+ }
+
+ if (!inline_parse_mov_edx_imm (l1, &rhs)) {
+ continue;
+ }
+
+ if (!inline_line_is_exact (l2, "mov ecx, edx")) {
+ continue;
+ }
+
+ if (!inline_fold_shift_eax_edx (l3, lhs, rhs, &folded)) {
+ continue;
+ }
+
+ if (!inline_parse_mov_edx_imm (l4, &cmp_rhs)) {
+ continue;
+ }
+
+ if (!inline_line_is_exact (l5, "cmp eax, edx")) {
+ continue;
+ }
+
+ if (!inline_parse_cond_jump_label (l6, opbuf, sizeof (opbuf), cond_label, sizeof (cond_label))) {
+ continue;
+ }
+
+ taken = inline_eval_cmp_jump (opbuf, folded, cmp_rhs);
+
+ if (taken) {
+
+ sprintf (tmp, " jmp %s\n", cond_label);
+ free (repl[i]);
+
+ repl[i] = inline_dup_text_slice (tmp, strlen (tmp));
+
+ skip[i + 1] = 1;
+ skip[i + 2] = 1;
+ skip[i + 3] = 1;
+ skip[i + 4] = 1;
+ skip[i + 5] = 1;
+ skip[i + 6] = 1;
+
+ } else {
+
+ skip[i] = 1;
+ skip[i + 1] = 1;
+ skip[i + 2] = 1;
+ skip[i + 3] = 1;
+ skip[i + 4] = 1;
+ skip[i + 5] = 1;
+ skip[i + 6] = 1;
+
+ }
+
+ }
+
+ for (i = 0; i + 6 < count; i++) {
+
+ long lhs;
+ long rhs;
+ long folded;
+
+ char false_label[128];
+ char true_label[128];
+ char opbuf[16];
+ char tmp[192];
+
+ const char *l0;
+ const char *l1;
+ const char *l2;
+ const char *l3;
+ const char *l4;
+ const char *l5;
+ const char *l6;
+
+ if (skip[i]) {
+ continue;
+ }
+
+ l0 = repl[i] ? repl[i] : lines[i];
+ l1 = repl[i + 1] ? repl[i + 1] : lines[i + 1];
+ l2 = repl[i + 2] ? repl[i + 2] : lines[i + 2];
+ l3 = repl[i + 3] ? repl[i + 3] : lines[i + 3];
+ l4 = repl[i + 4] ? repl[i + 4] : lines[i + 4];
+ l5 = repl[i + 5] ? repl[i + 5] : lines[i + 5];
+ l6 = repl[i + 6] ? repl[i + 6] : lines[i + 6];
+
+ if (!l0 || !l1 || !l2 || !l3 || !l4 || !l5 || !l6) {
+ continue;
+ }
+
+ if (!inline_parse_mov_eax_imm (l0, &lhs)) {
+ continue;
+ }
+
+ if (!inline_parse_mov_edx_imm (l1, &rhs)) {
+ continue;
+ }
+
+ if (!inline_line_is_exact (l2, "mov ecx, edx")) {
+ continue;
+ }
+
+ if (!inline_fold_shift_eax_edx (l3, lhs, rhs, &folded)) {
+ continue;
+ }
+
+ if (!inline_line_is_exact (l4, "test eax, eax")) {
+ continue;
+ }
+
+ if (!inline_parse_cond_jump_label (l5, opbuf, sizeof (opbuf), false_label, sizeof (false_label))) {
+ continue;
+ }
+
+ if (!inline_parse_jump_label (l6, "jmp", true_label, sizeof (true_label))) {
+ continue;
+ }
+
+ sprintf (tmp, " jmp %s\n", inline_eval_test_jump (opbuf, folded) ? false_label : true_label);
+ free (repl[i]);
+
+ repl[i] = inline_dup_text_slice (tmp, strlen (tmp));
+
+ skip[i + 1] = 1;
+ skip[i + 2] = 1;
+ skip[i + 3] = 1;
+ skip[i + 4] = 1;
+ skip[i + 5] = 1;
+ skip[i + 6] = 1;
+
+ }
+
+ for (i = 0; i + 7 < count; i++) {
+
+ long lhs;
+ long rhs;
+ long cmp_rhs;
+ long folded;
+
+ int jump_taken = 0;
+
+ char cond_label[128];
+ char fall_label[128];
+ char opbuf[16];
+ char tmp[192];
+
+ const char *l0;
+ const char *l1;
+ const char *l2;
+ const char *l3;
+ const char *l4;
+ const char *l5;
+ const char *l6;
+ const char *l7;
+
+ if (skip[i]) {
+ continue;
+ }
+
+ l0 = repl[i] ? repl[i] : lines[i];
+ l1 = repl[i + 1] ? repl[i + 1] : lines[i + 1];
+ l2 = repl[i + 2] ? repl[i + 2] : lines[i + 2];
+ l3 = repl[i + 3] ? repl[i + 3] : lines[i + 3];
+ l4 = repl[i + 4] ? repl[i + 4] : lines[i + 4];
+ l5 = repl[i + 5] ? repl[i + 5] : lines[i + 5];
+ l6 = repl[i + 6] ? repl[i + 6] : lines[i + 6];
+ l7 = repl[i + 7] ? repl[i + 7] : lines[i + 7];
+
+ if (!l0 || !l1 || !l2 || !l3 || !l4 || !l5 || !l6 || !l7) {
+ continue;
+ }
+
+ if (!inline_parse_mov_eax_imm (l0, &lhs)) {
+ continue;
+ }
+
+ if (!inline_parse_mov_edx_imm (l1, &rhs)) {
+ continue;
+ }
+
+ if (!inline_line_is_exact (l2, "mov ecx, edx")) {
+ continue;
+ }
+
+ if (!inline_fold_shift_eax_edx (l3, lhs, rhs, &folded)) {
+ continue;
+ }
+
+ if (!inline_parse_mov_edx_imm (l4, &cmp_rhs)) {
+ continue;
+ }
+
+ if (!inline_line_is_exact (l5, "cmp eax, edx")) {
+ continue;
+ }
+
+ if (!inline_parse_cond_jump_label (l6, opbuf, sizeof (opbuf), cond_label, sizeof (cond_label))) {
+ continue;
+ }
+
+ jump_taken = inline_eval_cmp_jump (opbuf, folded, cmp_rhs);
+
+ if (!inline_parse_jump_label (l7, "jmp", fall_label, sizeof (fall_label))) {
+ continue;
+ }
+
+ sprintf (tmp, " jmp %s\n", jump_taken ? cond_label : fall_label);
+ free (repl[i]);
+
+ repl[i] = inline_dup_text_slice (tmp, strlen (tmp));
+
+ skip[i + 1] = 1;
+ skip[i + 2] = 1;
+ skip[i + 3] = 1;
+ skip[i + 4] = 1;
+ skip[i + 5] = 1;
+ skip[i + 6] = 1;
+ skip[i + 7] = 1;
+
+ }
+
+ for (i = 0; i + 6 < count; i++) {
+
+ long lhs;
+ long rhs;
+ long folded;
+
+ char cond_label[128];
+ char opbuf[16];
+ char tmp[192];
+
+ const char *l0;
+ const char *l1;
+ const char *l2;
+ const char *l3;
+ const char *l4;
+ const char *l5;
+ const char *l6;
+
+ int taken;
+
+ if (skip[i]) {
+ continue;
+ }
+
+ l0 = repl[i] ? repl[i] : lines[i];
+ l1 = repl[i + 1] ? repl[i + 1] : lines[i + 1];
+ l2 = repl[i + 2] ? repl[i + 2] : lines[i + 2];
+ l3 = repl[i + 3] ? repl[i + 3] : lines[i + 3];
+ l4 = repl[i + 4] ? repl[i + 4] : lines[i + 4];
+ l5 = repl[i + 5] ? repl[i + 5] : lines[i + 5];
+ l6 = repl[i + 6] ? repl[i + 6] : lines[i + 6];
+
+ if (!l0 || !l1 || !l2 || !l3 || !l4 || !l5 || !l6) {
+ continue;
+ }
+
+ if (!inline_parse_mov_eax_imm (l0, &lhs)) {
+ continue;
+ }
+
+ if (!inline_parse_mov_edx_imm (l1, &rhs)) {
+ continue;
+ }
+
+ if (!inline_line_is_exact (l2, "mov ecx, edx") || !inline_line_is_exact (l3, "cdq") ||
+ !inline_line_is_exact (l4, "idiv ecx")) {
+ continue;
+ }
+
+ if (!inline_fold_div_eax_edx (lhs, rhs, &folded)) {
+ continue;
+ }
+
+ if (!inline_line_is_exact (l5, "test eax, eax")) {
+ continue;
+ }
+
+ if (!inline_parse_cond_jump_label (l6, opbuf, sizeof (opbuf), cond_label, sizeof (cond_label))) {
+ continue;
+ }
+
+ taken = inline_eval_test_jump (opbuf, folded);
+
+ if (taken) {
+
+ sprintf (tmp, " jmp %s\n", cond_label);
+ free (repl[i]);
+
+ repl[i] = inline_dup_text_slice (tmp, strlen (tmp));
+
+ skip[i + 1] = 1;
+ skip[i + 2] = 1;
+ skip[i + 3] = 1;
+ skip[i + 4] = 1;
+ skip[i + 5] = 1;
+ skip[i + 6] = 1;
+
+ } else {
+
+ skip[i] = 1;
+ skip[i + 1] = 1;
+ skip[i + 2] = 1;
+ skip[i + 3] = 1;
+ skip[i + 4] = 1;
+ skip[i + 5] = 1;
+ skip[i + 6] = 1;
+
+ }
+
+ }
+
+ for (i = 0; i + 7 < count; i++) {
+
+ long lhs;
+ long rhs;
+ long cmp_rhs;
+ long folded;
+
+ char cond_label[128];
+ char opbuf[16];
+ char tmp[192];
+
+ const char *l0;
+ const char *l1;
+ const char *l2;
+ const char *l3;
+ const char *l4;
+ const char *l5;
+ const char *l6;
+ const char *l7;
+
+ int taken;
+
+ if (skip[i]) {
+ continue;
+ }
+
+ l0 = repl[i] ? repl[i] : lines[i];
+ l1 = repl[i + 1] ? repl[i + 1] : lines[i + 1];
+ l2 = repl[i + 2] ? repl[i + 2] : lines[i + 2];
+ l3 = repl[i + 3] ? repl[i + 3] : lines[i + 3];
+ l4 = repl[i + 4] ? repl[i + 4] : lines[i + 4];
+ l5 = repl[i + 5] ? repl[i + 5] : lines[i + 5];
+ l6 = repl[i + 6] ? repl[i + 6] : lines[i + 6];
+ l7 = repl[i + 7] ? repl[i + 7] : lines[i + 7];
+
+ if (!l0 || !l1 || !l2 || !l3 || !l4 || !l5 || !l6 || !l7) {
+ continue;
+ }
+
+ if (!inline_parse_mov_eax_imm (l0, &lhs)) {
+ continue;
+ }
+
+ if (!inline_parse_mov_edx_imm (l1, &rhs)) {
+ continue;
+ }
+
+ if (!inline_line_is_exact (l2, "mov ecx, edx") || !inline_line_is_exact (l3, "cdq") ||
+ !inline_line_is_exact (l4, "idiv ecx")) {
+ continue;
+ }
+
+ if (!inline_fold_div_eax_edx (lhs, rhs, &folded)) {
+ continue;
+ }
+
+ if (!inline_parse_mov_edx_imm (l5, &cmp_rhs)) {
+ continue;
+ }
+
+ if (!inline_line_is_exact (l6, "cmp eax, edx")) {
+ continue;
+ }
+
+ if (!inline_parse_cond_jump_label (l7, opbuf, sizeof (opbuf), cond_label, sizeof (cond_label))) {
+ continue;
+ }
+
+ taken = inline_eval_cmp_jump (opbuf, folded, cmp_rhs);
+
+ if (taken) {
+
+ sprintf (tmp, " jmp %s\n", cond_label);
+ free (repl[i]);
+
+ repl[i] = inline_dup_text_slice (tmp, strlen (tmp));
+
+ skip[i + 1] = 1;
+ skip[i + 2] = 1;
+ skip[i + 3] = 1;
+ skip[i + 4] = 1;
+ skip[i + 5] = 1;
+ skip[i + 6] = 1;
+ skip[i + 7] = 1;
+
+ } else {
+
+ skip[i] = 1;
+ skip[i + 1] = 1;
+ skip[i + 2] = 1;
+ skip[i + 3] = 1;
+ skip[i + 4] = 1;
+ skip[i + 5] = 1;
+ skip[i + 6] = 1;
+ skip[i + 7] = 1;
+
+ }
+
+ }
+
+ for (i = 0; i + 7 < count; i++) {
+
+ long lhs;
+ long rhs;
+ long folded;
+
+ char cond_label[128];
+ char opbuf[16];
+ char tmp[192];
+
+ const char *l0;
+ const char *l1;
+ const char *l2;
+ const char *l3;
+ const char *l4;
+ const char *l5;
+ const char *l6;
+ const char *l7;
+
+ int taken;
+
+ if (skip[i]) {
+ continue;
+ }
+
+ l0 = repl[i] ? repl[i] : lines[i];
+ l1 = repl[i + 1] ? repl[i + 1] : lines[i + 1];
+ l2 = repl[i + 2] ? repl[i + 2] : lines[i + 2];
+ l3 = repl[i + 3] ? repl[i + 3] : lines[i + 3];
+ l4 = repl[i + 4] ? repl[i + 4] : lines[i + 4];
+ l5 = repl[i + 5] ? repl[i + 5] : lines[i + 5];
+ l6 = repl[i + 6] ? repl[i + 6] : lines[i + 6];
+ l7 = repl[i + 7] ? repl[i + 7] : lines[i + 7];
+
+ if (!l0 || !l1 || !l2 || !l3 || !l4 || !l5 || !l6 || !l7) {
+ continue;
+ }
+
+ if (!inline_parse_mov_eax_imm (l0, &lhs)) {
+ continue;
+ }
+
+ if (!inline_parse_mov_edx_imm (l1, &rhs)) {
+ continue;
+ }
+
+ if (!inline_line_is_exact (l2, "mov ecx, edx") || !inline_line_is_exact (l3, "cdq") ||
+ !inline_line_is_exact (l4, "idiv ecx") || !inline_line_is_exact (l5, "mov eax, edx")) {
+ continue;
+ }
+
+ if (!inline_fold_mod_eax_edx (lhs, rhs, &folded)) {
+ continue;
+ }
+
+ if (!inline_line_is_exact (l6, "test eax, eax")) {
+ continue;
+ }
+
+ if (!inline_parse_cond_jump_label (l7, opbuf, sizeof (opbuf), cond_label, sizeof (cond_label))) {
+ continue;
+ }
+
+ taken = inline_eval_test_jump (opbuf, folded);
+
+ if (taken) {
+
+ sprintf (tmp, " jmp %s\n", cond_label);
+ free (repl[i]);
+
+ repl[i] = inline_dup_text_slice (tmp, strlen (tmp));
+
+ skip[i + 1] = 1;
+ skip[i + 2] = 1;
+ skip[i + 3] = 1;
+ skip[i + 4] = 1;
+ skip[i + 5] = 1;
+ skip[i + 6] = 1;
+ skip[i + 7] = 1;
+
+ } else {
+
+ skip[i] = 1;
+ skip[i + 1] = 1;
+ skip[i + 2] = 1;
+ skip[i + 3] = 1;
+ skip[i + 4] = 1;
+ skip[i + 5] = 1;
+ skip[i + 6] = 1;
+ skip[i + 7] = 1;
+
+ }
+
+ }
+
+ for (i = 0; i + 8 < count; i++) {
+
+ long lhs;
+ long rhs;
+ long cmp_rhs;
+ long folded;
+
+ char cond_label[128];
+ char opbuf[16];
+ char tmp[192];
+
+ const char *l0;
+ const char *l1;
+ const char *l2;
+ const char *l3;
+ const char *l4;
+ const char *l5;
+ const char *l6;
+ const char *l7;
+ const char *l8;
+
+ int taken;
+
+ if (skip[i]) {
+ continue;
+ }
+
+ l0 = repl[i] ? repl[i] : lines[i];
+ l1 = repl[i + 1] ? repl[i + 1] : lines[i + 1];
+ l2 = repl[i + 2] ? repl[i + 2] : lines[i + 2];
+ l3 = repl[i + 3] ? repl[i + 3] : lines[i + 3];
+ l4 = repl[i + 4] ? repl[i + 4] : lines[i + 4];
+ l5 = repl[i + 5] ? repl[i + 5] : lines[i + 5];
+ l6 = repl[i + 6] ? repl[i + 6] : lines[i + 6];
+ l7 = repl[i + 7] ? repl[i + 7] : lines[i + 7];
+ l8 = repl[i + 8] ? repl[i + 8] : lines[i + 8];
+
+ if (!l0 || !l1 || !l2 || !l3 || !l4 || !l5 || !l6 || !l7 || !l8) {
+ continue;
+ }
+
+ if (!inline_parse_mov_eax_imm (l0, &lhs)) {
+ continue;
+ }
+
+ if (!inline_parse_mov_edx_imm (l1, &rhs)) {
+ continue;
+ }
+
+ if (!inline_line_is_exact (l2, "mov ecx, edx") || !inline_line_is_exact (l3, "cdq") ||
+ !inline_line_is_exact (l4, "idiv ecx") || !inline_line_is_exact (l5, "mov eax, edx")) {
+ continue;
+ }
+
+ if (!inline_fold_mod_eax_edx (lhs, rhs, &folded)) {
+ continue;
+ }
+
+ if (!inline_parse_mov_edx_imm (l6, &cmp_rhs)) {
+ continue;
+ }
+
+ if (!inline_line_is_exact (l7, "cmp eax, edx")) {
+ continue;
+ }
+
+ if (!inline_parse_cond_jump_label (l8, opbuf, sizeof (opbuf), cond_label, sizeof (cond_label))) {
+ continue;
+ }
+
+ taken = inline_eval_cmp_jump (opbuf, folded, cmp_rhs);
+
+ if (taken) {
+
+ sprintf (tmp, " jmp %s\n", cond_label);
+ free (repl[i]);
+
+ repl[i] = inline_dup_text_slice (tmp, strlen (tmp));
+
+ skip[i + 1] = 1;
+ skip[i + 2] = 1;
+ skip[i + 3] = 1;
+ skip[i + 4] = 1;
+ skip[i + 5] = 1;
+ skip[i + 6] = 1;
+ skip[i + 7] = 1;
+ skip[i + 8] = 1;
+
+ } else {
+
+ skip[i] = 1;
+ skip[i + 1] = 1;
+ skip[i + 2] = 1;
+ skip[i + 3] = 1;
+ skip[i + 4] = 1;
+ skip[i + 5] = 1;
+ skip[i + 6] = 1;
+ skip[i + 7] = 1;
+ skip[i + 8] = 1;
+
+ }
+
+ }
+
+ for (i = 0; i + 2 < count; i++) {
+
+ long lhs;
+ long rhs;
+ long folded;
+
+ char tmp[128];
+
+ const char *l0;
+ const char *l1;
+ const char *l2;
+
+ if (skip[i] || skip[i + 1] || skip[i + 2]) {
+ continue;
+ }
+
+ l0 = repl[i] ? repl[i] : lines[i];
+ l1 = repl[i + 1] ? repl[i + 1] : lines[i + 1];
+ l2 = repl[i + 2] ? repl[i + 2] : lines[i + 2];
+
+ if (!l0 || !l1 || !l2) {
+ continue;
+ }
+
+ if (!inline_parse_mov_eax_imm (l0, &lhs)) {
+ continue;
+ }
+
+ if (!inline_parse_mov_edx_imm (l1, &rhs)) {
+ continue;
+ }
+
+ if (!inline_fold_binary_eax_edx (l2, lhs, rhs, &folded)) {
+ continue;
+ }
+
+ sprintf (tmp, " mov eax, %ld\n", folded);
+ free (repl[i]);
+ repl[i] = inline_dup_text_slice (tmp, strlen (tmp));
+
+ skip[i + 1] = 1;
+ skip[i + 2] = 1;
+
+ }
+
+ for (i = 0; i + 3 < count; i++) {
+
+ long lhs;
+ long rhs;
+ long folded;
+
+ char tmp[128];
+
+ const char *l0;
+ const char *l1;
+ const char *l2;
+ const char *l3;
+
+ if (skip[i] || skip[i + 1] || skip[i + 2] || skip[i + 3]) {
+ continue;
+ }
+
+ l0 = repl[i] ? repl[i] : lines[i];
+ l1 = repl[i + 1] ? repl[i + 1] : lines[i + 1];
+ l2 = repl[i + 2] ? repl[i + 2] : lines[i + 2];
+ l3 = repl[i + 3] ? repl[i + 3] : lines[i + 3];
+
+ if (!l0 || !l1 || !l2 || !l3) {
+ continue;
+ }
+
+ if (!inline_parse_mov_eax_imm (l0, &lhs)) {
+ continue;
+ }
+
+ if (!inline_parse_mov_edx_imm (l1, &rhs)) {
+ continue;
+ }
+
+ if (!inline_line_is_exact (l2, "mov ecx, edx")) {
+ continue;
+ }
+
+ if (!inline_fold_shift_eax_edx (l3, lhs, rhs, &folded)) {
+ continue;
+ }
+
+ sprintf (tmp, " mov eax, %ld\n", folded);
+ free (repl[i]);
+
+ repl[i] = inline_dup_text_slice (tmp, strlen (tmp));
+
+ skip[i + 1] = 1;
+ skip[i + 2] = 1;
+ skip[i + 3] = 1;
+
+ }
+
+ for (i = 0; i + 4 < count; i++) {
+
+ long lhs;
+ long rhs;
+ long folded;
+
+ char tmp[128];
+
+ const char *l0;
+ const char *l1;
+ const char *l2;
+ const char *l3;
+ const char *l4;
+
+ if (skip[i] || skip[i + 1] || skip[i + 2] || skip[i + 3] || skip[i + 4]) {
+ continue;
+ }
+
+ l0 = repl[i] ? repl[i] : lines[i];
+ l1 = repl[i + 1] ? repl[i + 1] : lines[i + 1];
+ l2 = repl[i + 2] ? repl[i + 2] : lines[i + 2];
+ l3 = repl[i + 3] ? repl[i + 3] : lines[i + 3];
+ l4 = repl[i + 4] ? repl[i + 4] : lines[i + 4];
+
+ if (!l0 || !l1 || !l2 || !l3 || !l4) {
+ continue;
+ }
+
+ if (!inline_parse_mov_eax_imm (l0, &lhs)) {
+ continue;
+ }
+
+ if (!inline_parse_mov_edx_imm (l1, &rhs)) {
+ continue;
+ }
+
+ if (!inline_line_is_exact (l2, "mov ecx, edx") || !inline_line_is_exact (l3, "cdq") ||
+ !inline_line_is_exact (l4, "idiv ecx")) {
+ continue;
+ }
+
+ if (!inline_fold_div_eax_edx (lhs, rhs, &folded)) {
+ continue;
+ }
+
+ sprintf (tmp, " mov eax, %ld\n", folded);
+ free (repl[i]);
+
+ repl[i] = inline_dup_text_slice (tmp, strlen (tmp));
+
+ skip[i + 1] = 1;
+ skip[i + 2] = 1;
+ skip[i + 3] = 1;
+ skip[i + 4] = 1;
+
+ }
+
+ for (i = 0; i + 5 < count; i++) {
+
+ long lhs;
+ long rhs;
+ long folded;
+
+ char tmp[128];
+
+ const char *l0;
+ const char *l1;
+ const char *l2;
+ const char *l3;
+ const char *l4;
+ const char *l5;
+
+ if (skip[i] || skip[i + 1] || skip[i + 2] || skip[i + 3] || skip[i + 4] || skip[i + 5]) {
+ continue;
+ }
+
+ l0 = repl[i] ? repl[i] : lines[i];
+ l1 = repl[i + 1] ? repl[i + 1] : lines[i + 1];
+ l2 = repl[i + 2] ? repl[i + 2] : lines[i + 2];
+ l3 = repl[i + 3] ? repl[i + 3] : lines[i + 3];
+ l4 = repl[i + 4] ? repl[i + 4] : lines[i + 4];
+ l5 = repl[i + 5] ? repl[i + 5] : lines[i + 5];
+
+ if (!l0 || !l1 || !l2 || !l3 || !l4 || !l5) {
+ continue;
+ }
+
+ if (!inline_parse_mov_eax_imm (l0, &lhs)) {
+ continue;
+ }
+
+ if (!inline_parse_mov_edx_imm (l1, &rhs)) {
+ continue;
+ }
+
+ if (!inline_line_is_exact (l2, "mov ecx, edx") || !inline_line_is_exact (l3, "cdq") ||
+ !inline_line_is_exact (l4, "idiv ecx") || !inline_line_is_exact (l5, "mov eax, edx")) {
+ continue;
+ }
+
+ if (!inline_fold_mod_eax_edx (lhs, rhs, &folded)) {
+ continue;
+ }
+
+ sprintf (tmp, " mov eax, %ld\n", folded);
+ free (repl[i]);
+
+ repl[i] = inline_dup_text_slice (tmp, strlen (tmp));
+
+ skip[i + 1] = 1;
+ skip[i + 2] = 1;
+ skip[i + 3] = 1;
+ skip[i + 4] = 1;
+ skip[i + 5] = 1;
+
+ }
+
+ /*
+ * Some compound-expression folds, such as x += 1, are reduced only by
+ * the late arithmetic pass above. Run the constant compare/branch fold
+ * again here so newly-created sequences like:
+ *
+ * mov eax, 9
+ * mov edx, 9
+ * cmp eax, edx
+ * jne Lx
+ *
+ * are folded after the arithmetic result becomes visible.
+ */
+ for (i = 0; i + 3 < count; i++) {
+
+ long lhs;
+ long rhs;
+
+ char cond_label[128];
+ char opbuf[16];
+ char tmp[192];
+
+ const char *l0;
+ const char *l1;
+ const char *l2;
+ const char *l3;
+
+ int taken;
+
+ if (skip[i] || skip[i + 1] || skip[i + 2] || skip[i + 3]) {
+ continue;
+ }
+
+ l0 = repl[i] ? repl[i] : lines[i];
+ l1 = repl[i + 1] ? repl[i + 1] : lines[i + 1];
+ l2 = repl[i + 2] ? repl[i + 2] : lines[i + 2];
+ l3 = repl[i + 3] ? repl[i + 3] : lines[i + 3];
+
+ if (!l0 || !l1 || !l2 || !l3) {
+ continue;
+ }
+
+ if (!inline_parse_mov_eax_imm (l0, &lhs)) {
+ continue;
+ }
+
+ if (!inline_parse_mov_edx_imm (l1, &rhs)) {
+ continue;
+ }
+
+ if (!inline_line_is_exact (l2, "cmp eax, edx")) {
+ continue;
+ }
+
+ if (!inline_parse_cond_jump_label (l3, opbuf, sizeof (opbuf), cond_label, sizeof (cond_label))) {
+ continue;
+ }
+
+ taken = inline_eval_cmp_jump (opbuf, lhs, rhs);
+
+ if (taken) {
+
+ sprintf (tmp, " jmp %s\n", cond_label);
+ free (repl[i]);
+
+ repl[i] = inline_dup_text_slice (tmp, strlen (tmp));
+
+ skip[i + 1] = 1;
+ skip[i + 2] = 1;
+ skip[i + 3] = 1;
+
+ } else {
+
+ skip[i] = 1;
+ skip[i + 1] = 1;
+ skip[i + 2] = 1;
+ skip[i + 3] = 1;
+
+ }
+
+ }
+
+ /*
+ * A late fold can leave useful instructions separated by skipped source
+ * lines. For example x += 1 followed by == may become:
+ *
+ * mov eax, 9
+ * ; skipped old rhs/op lines
+ * mov edx, 9
+ * cmp eax, edx
+ * jne Lx
+ *
+ * The normal adjacent compare fold cannot see through the skipped lines,
+ * so do one compacting pass over the remaining live instructions.
+ */
+ for (i = 0; i < count; i++) {
+
+ int j0;
+ int j1;
+ int j2;
+ int j3;
+
+ long lhs;
+ long rhs;
+
+ char cond_label[128];
+ char opbuf[16];
+ char tmp[192];
+
+ const char *l0;
+ const char *l1;
+ const char *l2;
+ const char *l3;
+
+ int taken;
+
+ if (skip[i]) {
+ continue;
+ }
+
+ j0 = i;
+ j1 = j0 + 1;
+
+ while (j1 < count && skip[j1]) {
+ j1++;
+ }
+
+ j2 = j1 + 1;
+
+ while (j2 < count && skip[j2]) {
+ j2++;
+ }
+
+ j3 = j2 + 1;
+
+ while (j3 < count && skip[j3]) {
+ j3++;
+ }
+
+ if (j1 >= count || j2 >= count || j3 >= count) {
+ continue;
+ }
+
+ l0 = repl[j0] ? repl[j0] : lines[j0];
+ l1 = repl[j1] ? repl[j1] : lines[j1];
+ l2 = repl[j2] ? repl[j2] : lines[j2];
+ l3 = repl[j3] ? repl[j3] : lines[j3];
+
+ if (!l0 || !l1 || !l2 || !l3) {
+ continue;
+ }
+
+ if (!inline_parse_mov_eax_imm (l0, &lhs)) {
+ continue;
+ }
+
+ if (!inline_parse_mov_edx_imm (l1, &rhs)) {
+ continue;
+ }
+
+ if (!inline_line_is_exact (l2, "cmp eax, edx")) {
+ continue;
+ }
+
+ if (!inline_parse_cond_jump_label (l3, opbuf, sizeof (opbuf), cond_label, sizeof (cond_label))) {
+ continue;
+ }
+
+ taken = inline_eval_cmp_jump (opbuf, lhs, rhs);
+
+ if (taken) {
+
+ sprintf (tmp, " jmp %s\n", cond_label);
+ free (repl[j0]);
+
+ repl[j0] = inline_dup_text_slice (tmp, strlen (tmp));
+
+ skip[j1] = 1;
+ skip[j2] = 1;
+ skip[j3] = 1;
+
+ } else {
+
+ skip[j0] = 1;
+ skip[j1] = 1;
+ skip[j2] = 1;
+ skip[j3] = 1;
+
+ }
+
+ }
+
+ for (i = 0; i < count; i++) {
+
+ char label[128];
+ char target[128];
+ const char *line;
+ int j;
+
+ if (skip[i]) {
+ continue;
+ }
+
+ line = repl[i] ? repl[i] : lines[i];
+
+ if (!line || !inline_parse_label_definition (line, label, sizeof (label))) {
+ continue;
+ }
+
+ for (j = i + 1; j < count; j++) {
+
+ const char *next_line;
+
+ if (skip[j]) {
+ continue;
+ }
+
+ next_line = repl[j] ? repl[j] : lines[j];
+
+ if (!next_line) {
+ continue;
+ }
+
+ if (inline_parse_jump_label (next_line, "jmp", target, sizeof (target))) {
+
+ if (strcmp (label, target) != 0) {
+
+ chain_from[i] = inline_dup_text_slice (label, strlen (label));
+ chain_to[i] = inline_dup_text_slice (target, strlen (target));
+
+ }
+
+ }
+
+ break;
+
+ }
+
+ }
+
+ for (i = 0; i < count; i++) {
+
+ char target[128];
+ char resolved[128];
+ char tmp[192];
+ const char *line;
+
+ if (skip[i]) {
+ continue;
+ }
+
+ line = repl[i] ? repl[i] : lines[i];
+
+ if (!line || !inline_parse_jump_label (line, "jmp", target, sizeof (target))) {
+ continue;
+ }
+
+ if (!inline_resolve_jump_chain (chain_from, chain_to, count, target, resolved, sizeof (resolved))) {
+ continue;
+ }
+
+ sprintf (tmp, " jmp %s\n", resolved);
+ free (repl[i]);
+
+ repl[i] = inline_dup_text_slice (tmp, strlen (tmp));
+
+ }
+
+ for (i = 0; i < count; i++) {
+
+ char target[128];
+ const char *line;
+ int j;
+
+ if (skip[i]) {
+ continue;
+ }
+
+ line = repl[i] ? repl[i] : lines[i];
+
+ if (!line || !inline_parse_any_jump_label (line, target, sizeof (target))) {
+ continue;
+ }
+
+ for (j = 0; j < count; j++) {
+
+ char label[128];
+ const char *def;
+
+ if (skip[j]) {
+ continue;
+ }
+
+ def = repl[j] ? repl[j] : lines[j];
+
+ if (def && inline_parse_label_definition (def, label, sizeof (label)) && strcmp (label, target) == 0) {
+
+ label_refs[j]++;
+ break;
+
+ }
+
+ }
+
+ }
+
+ for (i = 0; i < count; i++) {
+
+ char target[128];
+ const char *line;
+ int j;
+ int target_line = -1;
+ int can_delete = 1;
+
+ if (skip[i]) {
+ continue;
+ }
+
+ line = repl[i] ? repl[i] : lines[i];
+
+ if (!line || !inline_parse_jump_label (line, "jmp", target, sizeof (target))) {
+ continue;
+ }
+
+ for (j = i + 1; j < count; j++) {
+
+ char label[128];
+ const char *check;
+
+ if (skip[j]) {
+ continue;
+ }
+
+ check = repl[j] ? repl[j] : lines[j];
+
+ if (check && inline_parse_label_definition (check, label, sizeof (label)) && strcmp (label, target) == 0) {
+
+ target_line = j;
+ break;
+
+ }
+
+ }
+
+ if (target_line < 0) {
+ continue;
+ }
+
+ for (j = i + 1; j < target_line; j++) {
+
+ char label[128];
+ const char *check;
+
+ if (skip[j]) {
+ continue;
+ }
+
+ check = repl[j] ? repl[j] : lines[j];
+
+ if (check && inline_parse_label_definition (check, label, sizeof (label)) && label_refs[j] > 0) {
+
+ can_delete = 0;
+ break;
+
+ }
+
+ }
+
+ if (!can_delete) {
+ continue;
+ }
+
+ for (j = i + 1; j < target_line; j++) {
+ skip[j] = 1;
+ }
+
+ }
+
+ for (i = 0; i < count; i++) {
+
+ char target[128];
+ const char *line;
+ int j;
+ int target_line = -1;
+
+ if (skip[i]) {
+ continue;
+ }
+
+ line = repl[i] ? repl[i] : lines[i];
+
+ if (!line || !inline_parse_jump_label (line, "jmp", target, sizeof (target))) {
+ continue;
+ }
+
+ for (j = i + 1; j < count; j++) {
+
+ char label[128];
+ const char *check;
+
+ if (skip[j]) {
+ continue;
+ }
+
+ check = repl[j] ? repl[j] : lines[j];
+
+ if (!check) {
+ continue;
+ }
+
+ if (inline_parse_label_definition (check, label, sizeof (label))) {
+
+ if (strcmp (label, target) == 0) {
+ target_line = j;
+ }
+
+ break;
+
+ }
+
+ break;
+
+ }
+
+ if (target_line >= 0) {
+ skip[i] = 1;
+ }
+
+ }
+
+ /*
+ * Branch folding can create new unreachable ranges after the earlier
+ * jump cleanup has already run. Recompute label references from the
+ * current non-skipped text and remove dead fall-through ranges between
+ * an unconditional jump and its later target. This deliberately keeps
+ * referenced labels, so externally reachable or still-jumped-to blocks
+ * are not discarded.
+ */
+ {
+
+ int changed;
+ int pass;
+
+ for (pass = 0; pass < count; pass++) {
+
+ changed = 0;
+
+ for (i = 0; i < count; i++) {
+ label_refs[i] = 0;
+ }
+
+ for (i = 0; i < count; i++) {
+
+ char target[128];
+ const char *line;
+ int j;
+
+ if (skip[i]) {
+ continue;
+ }
+
+ line = repl[i] ? repl[i] : lines[i];
+
+ if (!line || !inline_parse_any_jump_label (line, target, sizeof (target))) {
+ continue;
+ }
+
+ for (j = 0; j < count; j++) {
+
+ char label[128];
+ const char *def;
+
+ if (skip[j]) {
+ continue;
+ }
+
+ def = repl[j] ? repl[j] : lines[j];
+
+ if (def && inline_parse_label_definition (def, label, sizeof (label)) && strcmp (label, target) == 0) {
+
+ label_refs[j]++;
+ break;
+
+ }
+
+ }
+
+ }
+
+ for (i = 0; i < count; i++) {
+
+ char target[128];
+ const char *line;
+ int j;
+ int target_line = -1;
+ int can_delete = 1;
+
+ if (skip[i]) {
+ continue;
+ }
+
+ line = repl[i] ? repl[i] : lines[i];
+
+ if (!line || !inline_parse_jump_label (line, "jmp", target, sizeof (target))) {
+ continue;
+ }
+
+ for (j = i + 1; j < count; j++) {
+
+ char label[128];
+ const char *check;
+
+ if (skip[j]) {
+ continue;
+ }
+
+ check = repl[j] ? repl[j] : lines[j];
+
+ if (check && inline_parse_label_definition (check, label, sizeof (label)) && strcmp (label, target) == 0) {
+
+ target_line = j;
+ break;
+
+ }
+
+ }
+
+ if (target_line < 0) {
+ continue;
+ }
+
+ for (j = i + 1; j < target_line; j++) {
+
+ char label[128];
+ const char *check;
+
+ if (skip[j]) {
+ continue;
+ }
+
+ check = repl[j] ? repl[j] : lines[j];
+
+ if (check && inline_parse_label_definition (check, label, sizeof (label)) && label_refs[j] > 0) {
+
+ can_delete = 0;
+ break;
+
+ }
+
+ }
+
+ if (!can_delete) {
+ continue;
+ }
+
+ for (j = i + 1; j < target_line; j++) {
+
+ if (!skip[j]) {
+
+ skip[j] = 1;
+ changed = 1;
+
+ }
+
+ }
+
+ }
+
+ if (!changed) {
+ break;
+ }
+
+ }
+
+ }
+
+ /*
+ * The second unreachable-code pass can expose a new fall-through jump,
+ * for example "jmp L9" immediately followed by "L9:". Run this small
+ * cleanup again at the end so later branch/unreachable optimisations do
+ * not leave the already-handled form behind.
+ */
+ for (i = 0; i < count; i++) {
+
+ char target[128];
+ const char *line;
+ int j;
+ int target_line = -1;
+
+ if (skip[i]) {
+ continue;
+ }
+
+ line = repl[i] ? repl[i] : lines[i];
+
+ if (!line || !inline_parse_jump_label (line, "jmp", target, sizeof (target))) {
+ continue;
+ }
+
+ for (j = i + 1; j < count; j++) {
+
+ char label[128];
+ const char *check;
+
+ if (skip[j]) {
+ continue;
+ }
+
+ check = repl[j] ? repl[j] : lines[j];
+
+ if (!check) {
+ continue;
+ }
+
+ if (inline_parse_label_definition (check, label, sizeof (label))) {
+
+ if (strcmp (label, target) == 0) {
+ target_line = j;
+ }
+
+ break;
+
+ }
+
+ break;
+
+ }
+
+ if (target_line >= 0) {
+ skip[i] = 1;
+ }
+
+ }
+
+ for (i = 0; i < count; i++) {
+
+ if (!skip[i]) {
+ fputs (repl[i] ? repl[i] : (lines[i] ? lines[i] : ""), ofp);
+ }
+
+ free (lines[i]);
+ free (repl[i]);
+
+ }
+
+ for (i = 0; i < count; i++) {
+
+ free (chain_from[i]);
+ free (chain_to[i]);
+
+ }
+
+ free (lines);
+ free (repl);
+ free (skip);
+ free (store_slot);
+ free (chain_from);
+ free (chain_to);
+ free (label_refs);
+
+}
+
+static void finish_inline_buffer (FILE **tmp, FILE **saved, int optimize) {
+
+ FILE *out;
+ long size;
+ char *buf;
+
+ if (!tmp || !saved || !*tmp || !*saved) {
+ return;
+ }
+
+ out = *saved;
+
+ fflush (*tmp);
+ fseek (*tmp, 0, SEEK_END);
+
+ size = ftell (*tmp);
+ fseek (*tmp, 0, SEEK_SET);
+
+ if (size > 0) {
+
+ buf = (char *) malloc ((size_t) size + 1);
+
+ if (buf) {
+
+ if (fread (buf, 1, (size_t) size, *tmp) == (size_t) size) {
+
+ buf[size] = '\0';
+
+ if (optimize) {
+ emit_inline_optimized_text (buf, out);
+ } else {
+ fwrite (buf, 1, (size_t) size, out);
+ }
+
+ }
+
+ free (buf);
+
+ }
+
+ }
+
+ fclose (*tmp);
+
+ *tmp = 0;
+ *saved = 0;
+
+ state->ofp = out;
+
+}
+
+static void emit_inline_body_substituted (const char *body, int return_label, int argc) {
+
+ const char *p = body;
+
+ int call_id = anon_label++;
+ int stack_bytes = 0;
+
+ if (!body || !state->ofp) {
+ return;
+ }
+
+ while (*p) {
+
+ const char *line = p;
+ const char *eol = strchr (p, '\n');
+ const char *next = eol ? eol + 1 : p + strlen (p);
+ size_t len = (size_t) (next - line);
+
+ emit_inline_line_substituted (line, len, return_label, call_id, argc, stack_bytes);
+ stack_bytes += inline_line_stack_delta (line, len);
+ p = next;
+
+ }
+
+ if (body[0] && body[strlen (body) - 1] != '\n') {
+ fputc ('\n', state->ofp);
+ }
+
+ if ((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) {
+ fprintf (state->ofp, "L%d:\n", call_id);
+ } else {
+ fprintf (state->ofp, ".L%d:\n", call_id);
+ }
+
+}
+
+static int emit_inline_call_if_possible (const char *name, int argc, const char *reg) {
+
+ int i = find_inline_function (name);
+
+ if (i < 0 || !inline_functions[i].usable) {
+ return 0;
+ }
+
+ if (argc != inline_functions[i].param_count) {
+ return 0;
+ }
+
+ if (inline_functions[i].is_floating) {
+ return 0;
+ }
+
+ if (inline_functions[i].expanding) {
+ return 0;
+ }
+
+ if (!state->ofp) {
+ return 1;
+ }
+
+ if (!inline_functions[i].body && !inline_functions[i].returns_void) {
+ return 0;
+ }
+
+ if (inline_functions[i].body && inline_body_stack_delta (inline_functions[i].body) != 0) {
+ return 0;
+ }
+
+ if (inline_functions[i].data && !inline_functions[i].data_emitted) {
+
+ fputs (inline_functions[i].data, state->ofp);
+
+ if (inline_functions[i].data[0] && inline_functions[i].data[strlen (inline_functions[i].data) - 1] != '\n') {
+ fputc ('\n', state->ofp);
+ }
+
+ if (state->syntax & ASM_SYNTAX_MASM) {
+ fprintf (state->ofp, ".code\n");
+ } else if (state->syntax & ASM_SYNTAX_NASM) {
+ fprintf (state->ofp, "section .text\n");
+ } else {
+ fprintf (state->ofp, ".text\n");
+ }
+
+ inline_functions[i].data_emitted = 1;
+
+ }
+
+ if (inline_functions[i].body) {
+
+ inline_functions[i].expanding = 1;
+ emit_inline_body_substituted (inline_functions[i].body, inline_functions[i].return_label, argc);
+ inline_functions[i].expanding = 0;
+
+ }
+
+ if (!inline_functions[i].returns_void && strcmp (reg, "eax") != 0) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " mov %s, eax\n", reg);
+ } else {
+ fprintf (state->ofp, " movl %%eax, %%%s\n", reg);
+ }
+
+ }
+
+ return 1;
+
+}
+
+#define MAX_LOCAL_SYMBOLS 1024
+
+struct local_symbol {
+
+ char *name;
+
+ int size;
+ int align;
+
+ int is_floating;
+ long offset;
+
+ char *static_label;
+
+ int is_static, is_unsigned;
+ int is_array;
+ int array_element_size;
+
+ int pointer_depth;
+ int pointed_size;
+
+ char *pointed_tag_name;
+ char *tag_name;
+
+};
+
+static struct local_symbol local_symbols[MAX_LOCAL_SYMBOLS];
+
+static int local_array_pointer_step_size (const struct local_symbol *sym);
+static int global_array_pointer_step_size (const char *name);
+
+static int local_symbol_count = 0;
+
+static long current_local_stack_size = 0;
+static long current_block_cleanup_bytes = 0;
+
+static long align_up_long (long value, int align) {
+
+ long mask;
+
+ if (align <= 1) {
+ return value;
+ }
+
+ mask = (long) align - 1;
+ return (value + mask) & ~mask;
+
+}
+
+static int type_alignment (int size) {
+
+ if (size <= 1) {
+ return 1;
+ }
+
+ if (size == 2) {
+ return 2;
+ }
+
+ return 4;
+
+}
+
+static void reset_local_symbols (void) {
+
+ int i;
+
+ for (i = 0; i < local_symbol_count; i++) {
+
+ if (local_symbols[i].name) {
+
+ free (local_symbols[i].name);
+ local_symbols[i].name = 0;
+
+ }
+
+ if (local_symbols[i].static_label) {
+
+ free (local_symbols[i].static_label);
+ local_symbols[i].static_label = 0;
+
+ }
+
+ if (local_symbols[i].pointed_tag_name) {
+
+ free (local_symbols[i].pointed_tag_name);
+ local_symbols[i].pointed_tag_name = 0;
+
+ }
+
+ if (local_symbols[i].tag_name) {
+
+ free (local_symbols[i].tag_name);
+ local_symbols[i].tag_name = 0;
+
+ }
+
+ }
+
+ local_symbol_count = 0;
+ current_local_stack_size = 0;
+
+}
+
+static void truncate_local_symbols (int count, long stack_size) {
+
+ int i;
+
+ if (count < 0) {
+ count = 0;
+ }
+
+ for (i = count; i < local_symbol_count; i++) {
+
+ if (local_symbols[i].name) {
+
+ free (local_symbols[i].name);
+ local_symbols[i].name = 0;
+
+ }
+
+ if (local_symbols[i].static_label) {
+
+ free (local_symbols[i].static_label);
+ local_symbols[i].static_label = 0;
+
+ }
+
+ if (local_symbols[i].pointed_tag_name) {
+
+ free (local_symbols[i].pointed_tag_name);
+ local_symbols[i].pointed_tag_name = 0;
+
+ }
+
+ if (local_symbols[i].tag_name) {
+
+ free (local_symbols[i].tag_name);
+ local_symbols[i].tag_name = 0;
+
+ }
+
+ }
+
+ local_symbol_count = count;
+ current_local_stack_size = stack_size;
+
+}
+
+static int local_symbol_exists_in_current_scope (const char *name, int start_count) {
+
+ int i;
+
+ if (!name) {
+ return 0;
+ }
+
+ for (i = start_count; i < local_symbol_count; i++) {
+
+ if (local_symbols[i].name && strcmp (local_symbols[i].name, name) == 0) {
+ return 1;
+ }
+
+ }
+
+ return 0;
+
+}
+
+static long add_local_symbol (const char *name, int size, int align, int is_unsigned, int scope_start_count, int line, const char *start, const char *caret) {
+
+ long new_size;
+
+ if (!name) {
+ return 0;
+ }
+
+ if (size < 1) {
+ size = 1;
+ }
+
+ if (align < 1) {
+ align = 1;
+ }
+
+ if (local_symbol_exists_in_current_scope (name, scope_start_count)) {
+
+ report_line_at (get_filename (), line, REPORT_ERROR, start, caret, "duplicate local symbol '%s'", name);
+ return 0;
+
+ }
+
+ if (local_symbol_count >= MAX_LOCAL_SYMBOLS) {
+
+ report_line_at (get_filename (), line, REPORT_ERROR, start, caret, "too many local symbols");
+ return 0;
+
+ }
+
+ new_size = align_up_long (current_local_stack_size + size, align);
+
+ /*
+ * EBP-relative locals must be backed by whole stack slots. Without this,
+ * a byte-sized local following two pointer locals can be assigned offset
+ * -9(%ebp), while the emitted stack adjustment has only reserved 8 bytes.
+ * That is exactly what happened in cpplib/lex.c's _cpp_skip_block_comment:
+ *
+ * cpp_mffc *mffc; -> -4
+ * const char *pos; -> -8
+ * char c; -> -9, outside the reserved frame
+ *
+ * Keep the object's real size for loads/stores, but round the frame growth
+ * so subsequent stack allocation reserves enough bytes before the local is
+ * used.
+ */
+ new_size = align_up_long (new_size, 4);
+
+ local_symbols[local_symbol_count].name = xstrdup (name);
+ local_symbols[local_symbol_count].size = size;
+ local_symbols[local_symbol_count].align = align;
+ local_symbols[local_symbol_count].offset = -(int) new_size;
+ local_symbols[local_symbol_count].is_static = 0;
+ local_symbols[local_symbol_count].static_label = 0;
+ local_symbols[local_symbol_count].is_unsigned = is_unsigned ? 1 : 0;
+ local_symbols[local_symbol_count].is_floating = 0;
+ local_symbols[local_symbol_count].is_array = 0;
+ local_symbols[local_symbol_count].array_element_size = 0;
+ local_symbols[local_symbol_count].pointer_depth = 0;
+ local_symbols[local_symbol_count].pointed_size = 0;
+ local_symbols[local_symbol_count].pointed_tag_name = 0;
+ local_symbols[local_symbol_count].tag_name = 0;
+
+ local_symbol_count++;
+ current_local_stack_size = new_size;
+
+ return -(long) new_size;
+
+}
+
+static void add_static_local_symbol (const char *name, const char *label, int size, int align, int is_unsigned, int scope_start_count, int line, const char *start, const char *caret) {
+
+ if (!name || !label) {
+ return;
+ }
+
+ if (size < 1) {
+ size = 1;
+ }
+
+ if (align < 1) {
+ align = 1;
+ }
+
+ if (local_symbol_exists_in_current_scope (name, scope_start_count)) {
+
+ report_line_at (get_filename (), line, REPORT_ERROR, start, caret, "duplicate local symbol '%s'", name);
+ return;
+
+ }
+
+ if (local_symbol_count >= MAX_LOCAL_SYMBOLS) {
+
+ report_line_at (get_filename (), line, REPORT_ERROR, start, caret, "too many local symbols");
+ return;
+
+ }
+
+ local_symbols[local_symbol_count].name = xstrdup (name);
+ local_symbols[local_symbol_count].static_label = xstrdup (label);
+ local_symbols[local_symbol_count].size = size;
+ local_symbols[local_symbol_count].align = align;
+ local_symbols[local_symbol_count].offset = 0;
+ local_symbols[local_symbol_count].is_static = 1;
+ local_symbols[local_symbol_count].is_unsigned = is_unsigned ? 1 : 0;
+ local_symbols[local_symbol_count].is_floating = 0;
+ local_symbols[local_symbol_count].is_array = 0;
+ local_symbols[local_symbol_count].array_element_size = 0;
+
+ local_symbol_count++;
+
+}
+
+static struct local_symbol *find_local_symbol (const char *name);
+
+static void set_local_symbol_array (const char *name, int is_array) {
+
+ struct local_symbol *sym = find_local_symbol (name);
+
+ if (sym) {
+ sym->is_array = is_array ? 1 : 0;
+ }
+
+}
+
+static void set_local_symbol_array_element_size (const char *name, int elem_size) {
+
+ struct local_symbol *sym = find_local_symbol (name);
+
+ if (sym) {
+ sym->array_element_size = elem_size > 0 ? elem_size : 0;
+ }
+
+}
+
+static void set_local_symbol_floating (const char *name, int is_floating) {
+
+ int i;
+
+ if (!name) {
+ return;
+ }
+
+ for (i = local_symbol_count - 1; i >= 0; i--) {
+
+ if (local_symbols[i].name && strcmp (local_symbols[i].name, name) == 0) {
+
+ local_symbols[i].is_floating = is_floating ? 1 : 0;
+ return;
+
+ }
+
+ }
+
+}
+
+#define MAX_PENDING_PARAMS 128
+
+struct pending_param {
+
+ char *name;
+
+ int align, size;
+ int is_unsigned;
+ int is_floating;
+ int pointer_depth;
+ int pointed_size;
+
+ char *pointed_tag_name;
+
+};
+
+static struct pending_param pending_params[MAX_PENDING_PARAMS];
+static int pending_param_count = 0;
+static int declarator_depth = 0;
+
+static int local_symbol_exists_in_current_scope (const char *name, int start_count);
+
+static void clear_pending_params (void) {
+
+ int i;
+
+ for (i = 0; i < pending_param_count; i++) {
+
+ if (pending_params[i].name) {
+
+ free (pending_params[i].name);
+ pending_params[i].name = 0;
+
+ }
+
+ pending_params[i].size = 0;
+ pending_params[i].align = 0;
+ pending_params[i].is_unsigned = 0;
+ pending_params[i].is_floating = 0;
+ pending_params[i].pointer_depth = 0;
+ pending_params[i].pointed_size = 0;
+
+ if (pending_params[i].pointed_tag_name) {
+
+ free (pending_params[i].pointed_tag_name);
+ pending_params[i].pointed_tag_name = 0;
+
+ }
+
+ }
+
+ pending_param_count = 0;
+
+}
+
+static void add_pending_param (const char *name, int size, int align, int is_unsigned, int is_floating, int pointer_depth, int pointed_size) {
+
+ if (!name || !*name) {
+ return;
+ }
+
+ if (pending_param_count >= MAX_PENDING_PARAMS) {
+
+ report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "too many function parameters");
+ return;
+
+ }
+
+ if (size < 1) {
+ size = DATA_INT & 0x1f;
+ }
+
+ if (align < 1) {
+ align = type_alignment (size);
+ }
+
+ pending_params[pending_param_count].name = xstrdup (name);
+ pending_params[pending_param_count].size = size;
+ pending_params[pending_param_count].align = align;
+ pending_params[pending_param_count].is_unsigned = is_unsigned ? 1 : 0;
+ pending_params[pending_param_count].is_floating = is_floating ? 1 : 0;
+ pending_params[pending_param_count].pointer_depth = pointer_depth;
+ pending_params[pending_param_count].pointed_size = pointed_size > 0 ? pointed_size : 0;
+ pending_params[pending_param_count].pointed_tag_name = (pointer_depth > 0 && parsed_type_tag_name[0]) ? xstrdup (parsed_type_tag_name) : 0;
+
+ pending_param_count++;
+
+}
+
+static struct local_symbol *find_local_symbol (const char *name) {
+
+ int i;
+
+ if (!name) {
+ return 0;
+ }
+
+ for (i = local_symbol_count - 1; i >= 0; i--) {
+
+ if (local_symbols[i].name && strcmp (local_symbols[i].name, name) == 0) {
+ return &local_symbols[i];
+ }
+
+ }
+
+ return 0;
+
+}
+
+static void set_local_symbol_pointer_info (const char *name, int pointer_depth, int pointed_size) {
+
+ struct local_symbol *sym = find_local_symbol (name);
+
+ if (sym) {
+
+ sym->pointer_depth = pointer_depth;
+ sym->pointed_size = pointed_size;
+
+ if (sym->pointed_tag_name) {
+
+ free (sym->pointed_tag_name);
+ sym->pointed_tag_name = 0;
+
+ }
+
+ if (pointer_depth > 0 && parsed_type_tag_name[0]) {
+ sym->pointed_tag_name = xstrdup (parsed_type_tag_name);
+ }
+
+ }
+
+}
+
+static void install_pending_params_as_locals (void) {
+
+ long offset = current_function_returns_aggregate ? 12 : 8;
+ int i;
+
+ for (i = 0; i < pending_param_count; i++) {
+
+ int slot_size;
+
+ if (!pending_params[i].name) {
+ continue;
+ }
+
+ if (local_symbol_count >= MAX_LOCAL_SYMBOLS) {
+
+ report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "too many local symbols");
+ break;
+
+ }
+
+ if (local_symbol_exists_in_current_scope (pending_params[i].name, 0)) {
+ report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "duplicate parameter '%s'", pending_params[i].name);
+ }
+
+ local_symbols[local_symbol_count].name = xstrdup (pending_params[i].name);
+ local_symbols[local_symbol_count].size = pending_params[i].size;
+ local_symbols[local_symbol_count].align = pending_params[i].align;
+ local_symbols[local_symbol_count].offset = (int) offset;
+ local_symbols[local_symbol_count].is_static = 0;
+ local_symbols[local_symbol_count].static_label = 0;
+ local_symbols[local_symbol_count].is_unsigned = pending_params[i].is_unsigned;
+ local_symbols[local_symbol_count].is_floating = pending_params[i].is_floating;
+ local_symbols[local_symbol_count].pointer_depth = pending_params[i].pointer_depth;
+ local_symbols[local_symbol_count].pointed_size = pending_params[i].pointed_size;
+ local_symbols[local_symbol_count].pointed_tag_name = pending_params[i].pointed_tag_name ? xstrdup (pending_params[i].pointed_tag_name) : 0;
+ local_symbols[local_symbol_count].tag_name = 0;
+
+ local_symbol_count++;
+ slot_size = pending_params[i].size;
+
+ if (slot_size < DATA_PTR) {
+ slot_size = DATA_PTR;
+ }
+
+ slot_size = (int) align_up_long (slot_size, DATA_PTR);
+ offset += slot_size;
+
+ }
+
+}
+
+#define MAX_LOCAL_INITS 1024
+
+#define LOCAL_INIT_CONST 0
+#define LOCAL_INIT_ADDRESS 1
+#define LOCAL_INIT_STACK 2
+#define LOCAL_INIT_GLOBAL 3
+
+struct local_init {
+
+ long offset;
+
+ int size;
+ int kind;
+
+ char *symbol;
+ int64_s value;
+
+ long source_offset;
+ int source_size;
+
+};
+
+static void switch_section (int sec) {
+
+ if (!state->ofp || current_section == sec) {
+ return;
+ }
+
+ if (sec == SECTION_TEXT) {
+
+ if (state->syntax & ASM_SYNTAX_NASM) {
+ fprintf (state->ofp, "section .text\n");
+ } else {
+ fprintf (state->ofp, "%s\n", (state->syntax & ASM_SYNTAX_MASM ? ".code" : ".text"));
+ }
+
+ } else if (sec == SECTION_DATA) {
+
+ if (state->syntax & ASM_SYNTAX_NASM) {
+ fprintf (state->ofp, "section .data\n");
+ } else {
+ fprintf (state->ofp, ".data\n");
+ }
+
+ } else if (sec == SECTION_BSS) {
+
+ if (state->syntax & ASM_SYNTAX_NASM) {
+ fprintf (state->ofp, "section .bss\n");
+ } else {
+ fprintf (state->ofp, "%s\n", (state->syntax & ASM_SYNTAX_MASM ? ".data?" : ".bss"));
+ }
+
+ }
+
+ current_section = sec;
+
+}
+
+static int token_is_ms_int_type_name (void) {
+
+ if (tok.kind != TOK_IDENT || !tok.ident) {
+ return 0;
+ }
+
+ return strcmp (tok.ident, "__int8") == 0 || strcmp (tok.ident, "__int16") == 0 ||
+ strcmp (tok.ident, "__int32") == 0 || strcmp (tok.ident, "__int64") == 0;
+
+}
+
+static int is_type_start (enum token_kind k) {
+
+ if (k == TOK_IDENT && token_is_ms_int_type_name ()) {
+ return 1;
+ }
+
+ switch (k) {
+
+ case TOK_AUTO: case TOK_REGISTER: case TOK_STATIC:
+ case TOK_EXTERN: case TOK_TYPEDEF: case TOK_INLINE:
+ case TOK_CONST: case TOK_VOLATILE: case TOK_RESTRICT:
+ case TOK_SIGNED: case TOK_UNSIGNED:
+ case TOK_SHORT: case TOK_LONG:
+ case TOK_CHAR: case TOK_INT: case TOK_VOID:
+ case TOK_FLOAT: case TOK_DOUBLE:
+ case TOK_STRUCT: case TOK_UNION: case TOK_ENUM:
+
+ return 1;
+
+ default:
+
+ break;
+
+ }
+
+ if (k == TOK_IDENT && is_current_typedef_name ()) {
+ return 1;
+ }
+
+ return 0;
+
+}
+
+/*static void next (void) {
+ get_token ();
+}*/
+
+static int expect (enum token_kind k, const char *what);
+
+static void parse_declarator (char **out_name);
+static void parse_type_spec (void);
+
+int token_starts_type_name (void) {
+ return is_type_start (tok.kind);
+}
+
+int parse_cast_type_name (int *out_size, int *out_is_unsigned, int *out_is_pointer) {
+
+ int saved_type_size = parsed_type_size;
+ int saved_storage_class = parsed_storage_class;
+ int saved_is_aggregate = parsed_type_is_aggregate;
+ int saved_is_void = parsed_type_is_void;
+ int saved_is_unsigned = parsed_type_is_unsigned;
+ int saved_is_floating = parsed_type_is_floating;
+ int saved_has_tag = parsed_type_has_tag;
+ int saved_is_inline = parsed_type_is_inline;
+ int saved_field_count = parsed_field_count;
+ int saved_fields[MAX_AGG_FIELDS];
+
+ int saved_declarator_is_pointer = declarator_is_pointer;
+ int saved_declarator_has_array = declarator_has_array;
+ int saved_declarator_has_function = declarator_has_function;
+ int saved_declarator_array_unsized = declarator_array_unsized;
+ long saved_declarator_array_count = declarator_array_count;
+
+ char *name = 0;
+ int size = DATA_INT & 0x1f;
+ int is_unsigned = 0;
+ int is_pointer = 0;
+ int ok = 0;
+ int i;
+
+ for (i = 0; i < saved_field_count && i < MAX_AGG_FIELDS; i++) {
+ saved_fields[i] = parsed_field_sizes[i];
+ }
+
+ if (is_type_start (tok.kind)) {
+
+ declarator_is_pointer = 0;
+ declarator_pointer_depth = 0;
+ declarator_has_array = 0;
+ declarator_has_function = 0;
+ declarator_array_unsized = 0;
+ declarator_array_count = 0;
+
+ parse_type_spec ();
+
+ size = parsed_type_size & 0x1f;
+ is_unsigned = parsed_type_is_unsigned;
+
+ if (tok.kind != TOK_RPAREN) {
+ parse_declarator (&name);
+ }
+
+ if (declarator_is_pointer) {
+
+ size = DATA_PTR;
+
+ is_unsigned = 1;
+ is_pointer = 1;
+
+ }
+
+ if (tok.kind == TOK_RPAREN) {
+
+ get_token ();
+ ok = 1;
+
+ } else {
+ expect (TOK_RPAREN, ")");
+ }
+
+ if (name) {
+ free (name);
+ }
+
+ }
+
+ last_cast_type_tag_name[0] = '\0';
+ last_cast_type_object_size = parsed_type_size & 0x1f;
+
+ if (parsed_type_tag_name[0]) {
+
+ strncpy (last_cast_type_tag_name, parsed_type_tag_name, sizeof (last_cast_type_tag_name) - 1);
+ last_cast_type_tag_name[sizeof (last_cast_type_tag_name) - 1] = '\0';
+
+ }
+
+ if (parsed_type_is_aggregate && parsed_type_size > 0) {
+ last_cast_type_object_size = parsed_type_size;
+ }
+
+ parsed_type_size = saved_type_size;
+ parsed_storage_class = saved_storage_class;
+ parsed_type_is_aggregate = saved_is_aggregate;
+ parsed_type_is_void = saved_is_void;
+ parsed_type_is_unsigned = saved_is_unsigned;
+ parsed_type_is_floating = saved_is_floating;
+ parsed_type_has_tag = saved_has_tag;
+ parsed_type_is_inline = saved_is_inline;
+
+ clear_parsed_fields ();
+
+ for (i = 0; i < saved_field_count && i < MAX_AGG_FIELDS; i++) {
+ parsed_field_sizes[i] = saved_fields[i];
+ }
+
+ parsed_field_count = saved_field_count;
+
+ declarator_is_pointer = saved_declarator_is_pointer;
+ declarator_has_array = saved_declarator_has_array;
+ declarator_has_function = saved_declarator_has_function;
+ declarator_array_unsized = saved_declarator_array_unsized;
+ declarator_array_count = saved_declarator_array_count;
+
+ if (out_size) {
+ *out_size = size;
+ }
+
+ if (out_is_unsigned) {
+ *out_is_unsigned = is_unsigned;
+ }
+
+ if (out_is_pointer) {
+ *out_is_pointer = is_pointer;
+ }
+
+ return ok;
+
+}
+
+static int parse_deref_cast_type_name (int *out_size) {
+
+ int saved_type_size = parsed_type_size;
+ int saved_storage_class = parsed_storage_class;
+ int saved_is_aggregate = parsed_type_is_aggregate;
+ int saved_is_void = parsed_type_is_void;
+ int saved_is_unsigned = parsed_type_is_unsigned;
+ int saved_is_floating = parsed_type_is_floating;
+ int saved_has_tag = parsed_type_has_tag;
+ int saved_is_inline = parsed_type_is_inline;
+ int saved_field_count = parsed_field_count;
+ int saved_fields[MAX_AGG_FIELDS];
+ int saved_declarator_is_pointer = declarator_is_pointer;
+ int saved_declarator_has_array = declarator_has_array;
+ int saved_declarator_has_function = declarator_has_function;
+ int saved_declarator_array_unsized = declarator_array_unsized;
+
+ long saved_declarator_array_count = declarator_array_count;
+ char *name = 0;
+
+ int size = DATA_INT & 0x1f;
+ int ok = 0;
+ int i;
+
+ for (i = 0; i < saved_field_count && i < MAX_AGG_FIELDS; i++) {
+ saved_fields[i] = parsed_field_sizes[i];
+ }
+
+ if (is_type_start (tok.kind)) {
+
+ declarator_is_pointer = 0;
+ declarator_pointer_depth = 0;
+ declarator_has_array = 0;
+ declarator_has_function = 0;
+ declarator_array_unsized = 0;
+ declarator_array_count = 0;
+
+ parse_type_spec ();
+ size = parsed_type_size & 0x1f;
+
+ if (tok.kind != TOK_RPAREN) {
+
+ parse_declarator (&name);
+
+ if (declarator_is_pointer) {
+ size = DATA_PTR;
+ }
+
+ }
+
+ if (tok.kind == TOK_RPAREN) {
+
+ get_token ();
+ ok = 1;
+
+ } else {
+ expect (TOK_RPAREN, ")");
+ }
+
+ if (name) {
+ free (name);
+ }
+
+ }
+
+ parsed_type_size = saved_type_size;
+ parsed_storage_class = saved_storage_class;
+ parsed_type_is_aggregate = saved_is_aggregate;
+ parsed_type_is_void = saved_is_void;
+ parsed_type_is_unsigned = saved_is_unsigned;
+ parsed_type_is_floating = saved_is_floating;
+ parsed_type_has_tag = saved_has_tag;
+ parsed_type_is_inline = saved_is_inline;
+
+ clear_parsed_fields ();
+
+ for (i = 0; i < saved_field_count && i < MAX_AGG_FIELDS; i++) {
+ parsed_field_sizes[i] = saved_fields[i];
+ }
+
+ parsed_field_count = saved_field_count;
+
+ declarator_is_pointer = saved_declarator_is_pointer;
+ declarator_has_array = saved_declarator_has_array;
+ declarator_has_function = saved_declarator_has_function;
+ declarator_array_unsized = saved_declarator_array_unsized;
+ declarator_array_count = saved_declarator_array_count;
+
+ if (out_size) {
+ *out_size = size;
+ }
+
+ return ok;
+
+}
+
+static int _accept (enum token_kind k) {
+
+ if (tok.kind == k) {
+
+ get_token ();
+ return 1;
+
+ }
+
+ return 0;
+
+}
+
+static int expect (enum token_kind k, const char *what) {
+
+ if (tok.kind == k) {
+
+ get_token ();
+ return 1;
+
+ }
+
+ report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "expected %s", what);
+ return 0;
+
+}
+
+static int token_is_ident (void) {
+ return tok.kind == TOK_IDENT;
+}
+
+static void skip_balanced_until (enum token_kind stop1, enum token_kind stop2, enum token_kind stop3);
+static void emit_load_assignment_rhs_expression_to_reg (const char *reg);
+
+static void consume_comma_expression_tail_before_semi (void) {
+
+ while (tok.kind == TOK_COMMA) {
+
+ get_token ();
+ emit_load_assignment_rhs_expression_to_reg ("eax");
+
+ }
+
+}
+
+static void expect_semi_or_recover (void) {
+
+ consume_comma_expression_tail_before_semi ();
+
+ if (expect (TOK_SEMI, ";")) {
+ return;
+ }
+
+ skip_balanced_until (TOK_SEMI, TOK_RBRACE, TOK_EOF);
+
+ if (tok.kind == TOK_SEMI) {
+ get_token ();
+ }
+
+}
+
+static int current_ident_is_known_value (void) {
+
+ int64_s ignored;
+
+ if (tok.kind != TOK_IDENT) {
+ return 0;
+ }
+
+ if (find_local_symbol (tok.ident)) {
+ return 1;
+ }
+
+ if (find_global_symbol (tok.ident) >= 0) {
+ return 1;
+ }
+
+ if (resolve_enum_constant (tok.ident, &ignored)) {
+ return 1;
+ }
+
+ return 0;
+
+}
+
+static int recover_unknown_rhs_identifier (void) {
+
+ const char *name;
+ const char *start;
+ const char *caret;
+
+ unsigned long line;
+
+ if (tok.kind != TOK_IDENT || current_ident_is_known_value ()) {
+ return 0;
+ }
+
+ name = tok.ident ? tok.ident : "";
+ start = tok.start;
+ caret = tok.caret;
+ line = get_line_number ();
+
+ report_line_at (get_filename (), line, REPORT_ERROR, start, caret, "unknown symbol '%s'", name);
+ get_token ();
+
+ if (tok.kind == TOK_LPAREN) {
+
+ get_token ();
+ skip_balanced_until (TOK_RPAREN, TOK_SEMI, TOK_EOF);
+
+ if (tok.kind == TOK_RPAREN) {
+ get_token ();
+ }
+
+ }
+
+ return 1;
+
+}
+
+static char *take_ident (void) {
+
+ char *name;
+
+ if (!token_is_ident ()) {
+ return 0;
+ }
+
+ name = xstrdup (tok.ident);
+
+ last_declarator_name_line = get_line_number ();
+ last_declarator_name_start = tok.start;
+ last_declarator_name_caret = tok.caret;
+
+ if (capture_declarator_name_location && !captured_declarator_name_location) {
+
+ captured_declarator_name_location = 1;
+ captured_declarator_name_line = last_declarator_name_line;
+ captured_declarator_name_start = last_declarator_name_start;
+ captured_declarator_name_caret = last_declarator_name_caret;
+
+ }
+
+ get_token ();
+ return name;
+
+}
+
+static void skip_balanced_until (enum token_kind stop1, enum token_kind stop2, enum token_kind stop3) {
+
+ int paren = 0, brace = 0, brack = 0;
+
+ while (tok.kind != TOK_EOF) {
+
+ if (paren == 0 && brace == 0 && brack == 0) {
+
+ if (tok.kind == stop1 || tok.kind == stop2 || tok.kind == stop3) {
+ return;
+ }
+
+ }
+
+ if (tok.kind == TOK_LPAREN) {
+ paren++;
+ } else if (tok.kind == TOK_RPAREN) {
+
+ if (paren > 0) {
+ paren--;
+ } else {
+
+ if (stop1 == TOK_RPAREN || stop2 == TOK_RPAREN || stop3 == TOK_RPAREN) {
+ return;
+ }
+
+ }
+
+ } else if (tok.kind == TOK_LBRACE) {
+ brace++;
+ } else if (tok.kind == TOK_RBRACE) {
+
+ if (brace > 0) {
+ brace--;
+ } else {
+
+ if (stop1 == TOK_RPAREN || stop2 == TOK_RPAREN || stop3 == TOK_RPAREN) {
+ return;
+ }
+
+ }
+
+ } else if (tok.kind == TOK_LBRACK) {
+ brack++;
+ } else if (tok.kind == TOK_RBRACK) {
+
+ if (brack > 0) {
+ brack--;
+ } else {
+
+ if (stop1 == TOK_RPAREN || stop2 == TOK_RPAREN || stop3 == TOK_RPAREN) {
+ return;
+ }
+
+ }
+
+ }
+
+ get_token ();
+
+ }
+
+}
+
+/*static void consume_redundant_rparens_before_statement (void) {
+
+ while (tok.kind == TOK_RPAREN) {
+ get_token ();
+ }
+
+}*/
+
+static void skip_initializer (void) {
+
+ if (tok.kind == TOK_LBRACE) {
+ skip_balanced_until (TOK_COMMA, TOK_SEMI, TOK_EOF);
+ } else {
+ skip_balanced_until (TOK_COMMA, TOK_SEMI, TOK_EOF);
+ }
+
+}
+
+static void parse_enum_body (void) {
+
+ int64_s value;
+ zext64 (&value, 0);
+
+ expect (TOK_LBRACE, "{");
+
+ while (tok.kind != TOK_EOF && tok.kind != TOK_RBRACE) {
+
+ if (!token_is_ident ()) {
+
+ report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "expected enum constant");
+
+ get_token ();
+ continue;
+
+ } else {
+
+ char *name;
+
+ const char *name_start;
+ const char *name_caret;
+
+ name = xstrdup (tok.ident);
+
+ name_start = tok.start;
+ name_caret = tok.caret;
+
+ get_token ();
+
+ if (_accept (TOK_ASSIGN)) {
+
+ enum token_kind kill[4];
+
+ kill[0] = TOK_COMMA;
+ kill[1] = TOK_RBRACE;
+ kill[2] = TOK_EOF;
+ kill[3] = 0;
+
+ value = expr_const64 (kill);
+
+ }
+
+ save_enum_constant (name, value, name_start, name_caret);
+ free (name);
+
+ inc64 (&value);
+
+ }
+
+ if (!_accept (TOK_COMMA)) {
+ break;
+ }
+
+ }
+
+ expect (TOK_RBRACE, "}");
+
+}
+
+static void parse_declarator (char **out_name);
+static void parse_declarator_inner (char **out_name);
+
+static void parse_type_spec (void) {
+
+ int saw = 0;
+
+ int saw_float = 0;
+ int saw_double = 0;
+ int saw_void = 0;
+
+ int saw_signed = 0;
+ int saw_unsigned = 0;
+ int saw_real_type = 0;
+
+ parsed_storage_class = STORAGE_NONE;
+ parsed_type_is_aggregate = 0;
+ parsed_type_is_unsigned = 0;
+ parsed_type_has_tag = 0;
+ parsed_type_tag_name[0] = '\0';
+ parsed_type_is_array_typedef = 0;
+ parsed_type_array_count = 1;
+ parsed_type_array_element_size = DATA_NONE;
+ parsed_type_is_inline = 0;
+ parsed_type_is_void = 0;
+ parsed_type_only_qualifiers = 0;
+ parsed_type_size = DATA_NONE;
+
+ clear_parsed_fields ();
+
+ while (tok.kind == TOK_AUTO || tok.kind == TOK_REGISTER || tok.kind == TOK_STATIC ||
+ tok.kind == TOK_EXTERN || tok.kind == TOK_TYPEDEF || tok.kind == TOK_CONST ||
+ tok.kind == TOK_VOLATILE || tok.kind == TOK_SIGNED || tok.kind == TOK_UNSIGNED ||
+ tok.kind == TOK_SHORT || tok.kind == TOK_LONG || tok.kind == TOK_CHAR ||
+ tok.kind == TOK_INT || tok.kind == TOK_VOID || tok.kind == TOK_FLOAT ||
+ tok.kind == TOK_DOUBLE || tok.kind == TOK_INLINE || tok.kind == TOK_RESTRICT ||
+ token_is_ms_int_type_name ()) {
+
+ saw = 1;
+
+ if (tok.kind == TOK_SIGNED || tok.kind == TOK_UNSIGNED ||
+ tok.kind == TOK_SHORT || tok.kind == TOK_LONG || tok.kind == TOK_CHAR ||
+ tok.kind == TOK_INT || tok.kind == TOK_VOID || tok.kind == TOK_FLOAT ||
+ tok.kind == TOK_DOUBLE || token_is_ms_int_type_name ()) {
+ saw_real_type = 1;
+ }
+
+ if (tok.kind == TOK_EXTERN) {
+
+ if (parsed_storage_class == STORAGE_EXTERN) {
+ report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "duplicate 'extern'");
+ } else if (parsed_storage_class != STORAGE_NONE) {
+ report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "multiple storage classes in declaration specifiers");
+ } else {
+ parsed_storage_class = STORAGE_EXTERN;
+ }
+
+ } else if (tok.kind == TOK_STATIC) {
+
+ if (parsed_storage_class == STORAGE_STATIC) {
+ report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "duplicate 'static'");
+ } else if (parsed_storage_class != STORAGE_NONE) {
+ report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "multiple storage classes in declaration specifiers");
+ } else {
+ parsed_storage_class = STORAGE_STATIC;
+ }
+
+ } else if (tok.kind == TOK_TYPEDEF) {
+
+ if (parsed_storage_class == STORAGE_TYPEDEF) {
+ report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "duplicate 'typedef'");
+ } else if (parsed_storage_class != STORAGE_NONE) {
+ report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "multiple storage classes in declaration specifiers");
+ } else {
+ parsed_storage_class = STORAGE_TYPEDEF;
+ }
+
+ } else if (tok.kind == TOK_INLINE) {
+ parsed_type_is_inline = 1;
+ } else if (tok.kind == TOK_CHAR) {
+
+ if (parsed_type_size == DATA_CHAR) {
+ report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "duplicate 'char'");
+ } else if (parsed_type_size != DATA_NONE) {
+ report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "multiple data types in declaration specifiers");
+ } else {
+ parsed_type_size = DATA_CHAR;
+ }
+
+ } else if (tok.kind == TOK_SHORT) {
+
+ if (parsed_type_size == DATA_SHORT) {
+ report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "duplicate 'short'");
+ } else if (parsed_type_size != DATA_NONE) {
+ report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "multiple data types in declaration specifiers");
+ } else {
+ parsed_type_size = DATA_SHORT;
+ }
+
+ } else if (tok.kind == TOK_INT) {
+
+ if (parsed_type_size == DATA_INT) {
+ report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "duplicate 'int'");
+ } else if (parsed_type_size != DATA_NONE) {
+ /*report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "multiple data types in declaration specifiers");*/
+ } else {
+ parsed_type_size = DATA_INT;
+ }
+
+ } else if (tok.kind == TOK_LONG) {
+
+ if (parsed_type_size == DATA_LONG) {
+ parsed_type_size = DATA_LLONG;
+ } else if (parsed_type_size == DATA_LLONG) {
+ report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "too many 'long' keywords");
+ } else if (parsed_type_size != DATA_NONE) {
+ report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "multiple data types in declaration specifiers");
+ } else {
+ parsed_type_size = DATA_LONG;
+ }
+
+ } else if (tok.kind == TOK_SIGNED) {
+ saw_signed = 1;
+ } else if (tok.kind == TOK_UNSIGNED) {
+ saw_unsigned = 1;
+ } else if (tok.kind == TOK_FLOAT) {
+
+ if (parsed_type_size == DATA_FLOAT) {
+ report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "duplicate 'float'");
+ } else if (parsed_type_size != DATA_NONE) {
+ report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "multiple data types in declaration specifiers");
+ } else {
+
+ parsed_type_size = DATA_FLOAT;
+ saw_float = 1;
+
+ }
+
+ } else if (tok.kind == TOK_DOUBLE) {
+
+ if (parsed_type_size == DATA_DOUBLE) {
+ report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "duplicate 'double'");
+ } else if (parsed_type_size != DATA_NONE) {
+ report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "multiple data types in declaration specifiers");
+ } else {
+
+ parsed_type_size = DATA_DOUBLE;
+ saw_double = 1;
+
+ }
+
+ } else if (tok.kind == TOK_VOID) {
+
+ if (parsed_type_size == DATA_VOID) {
+ report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "duplicate 'void'");
+ } else if (parsed_type_size != DATA_NONE) {
+ report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "multiple data types in declaration specifiers");
+ } else {
+
+ parsed_type_size = DATA_VOID;
+ parsed_type_is_void = 1;
+
+ saw_void = 1;
+
+ }
+
+ } else if (token_is_ms_int_type_name ()) {
+
+ if (strcmp (tok.ident, "__int8") == 0) {
+
+ if (parsed_type_size != DATA_NONE) {
+ report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "multiple data types in declaration specifiers");
+ } else {
+ parsed_type_size = DATA_CHAR & 0x1f;
+ }
+
+ } else if (strcmp (tok.ident, "__int16") == 0) {
+
+ if (parsed_type_size != DATA_NONE) {
+ report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "multiple data types in declaration specifiers");
+ } else {
+ parsed_type_size = DATA_SHORT & 0x1f;
+ }
+
+ } else if (strcmp (tok.ident, "__int32") == 0) {
+
+ if (parsed_type_size != DATA_NONE) {
+ report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "multiple data types in declaration specifiers");
+ } else {
+ parsed_type_size = DATA_INT & 0x1f;
+ }
+
+ } else {
+
+ if (parsed_type_size != DATA_NONE) {
+ report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "multiple data types in declaration specifiers");
+ } else {
+ parsed_type_size = DATA_LLONG & 0x1f;
+ }
+
+ }
+
+ }
+
+ get_token ();
+
+ }
+
+ /*
+ * Be tolerant of project typedef chains where the base typedef name is
+ * not already known to this parser. In
+ *
+ * typedef some_base new_name;
+ * typedef some_base *new_ptr;
+ *
+ * the declarator name is new_name/new_ptr, not some_base. Without
+ * consuming the unknown base identifier here, parse_declarator() records
+ * some_base as the typedef name and the real declarator is then parsed as
+ * junk.
+ */
+ if (parsed_storage_class == STORAGE_TYPEDEF && !saw_real_type &&
+ tok.kind == TOK_IDENT && !is_current_typedef_name ()) {
+
+ get_token ();
+
+ saw = 1;
+ saw_real_type = 1;
+
+ parsed_type_size = DATA_INT;
+
+ }
+
+ if (parsed_type_size == DATA_NONE) {
+
+ parsed_type_only_qualifiers = (saw && !saw_real_type) ? 1 : 0;
+ parsed_type_size = DATA_INT;
+
+ } else if (parsed_type_size == DATA_LONG && state->long64) {
+ parsed_type_size = DATA_LLONG;
+ }
+
+ parsed_type_size &= 0x1f;
+
+ if (is_current_typedef_name ()) {
+
+ struct typedef_entry *entry;
+
+ entry = find_typedef_name (tok.ident);
+ get_token ();
+
+ load_typedef_name (entry);
+
+ if (entry && entry->is_aggregate && entry->tag_name && entry->tag_name[0]) {
+
+ strncpy (parsed_type_tag_name, entry->tag_name, sizeof (parsed_type_tag_name) - 1);
+ parsed_type_tag_name[sizeof (parsed_type_tag_name) - 1] = '\0';
+
+ parsed_type_has_tag = 1;
+
+ }
+
+ parsed_type_only_qualifiers = 0;
+ return;
+
+ }
+
+ if (tok.kind == TOK_STRUCT || tok.kind == TOK_UNION) {
+
+ int is_union = (tok.kind == TOK_UNION);
+ int has_tag = 0;
+
+ int aggregate_size = 0;
+ int aggregate_align = 1;
+
+ int aggregate_fields[MAX_AGG_FIELDS];
+ int aggregate_field_count = 0;
+
+ int union_init_fields[MAX_AGG_FIELDS];
+ int union_init_field_count = 0;
+ int union_init_size = 0;
+
+ struct aggregate_tag_entry *tag_entry = 0;
+
+ int aggregate_storage_class = parsed_storage_class;
+ int aggregate_is_inline = parsed_type_is_inline;
+
+ char *tag_name = 0;
+ int member_info_start = member_info_count;
+
+ parsed_type_size = DATA_PTR;
+ parsed_type_is_void = 0;
+
+ clear_parsed_fields ();
+ get_token ();
+
+ if (token_is_ident ()) {
+
+ tag_name = xstrdup (tok.ident);
+ strncpy (parsed_type_tag_name, tok.ident, sizeof (parsed_type_tag_name) - 1);
+
+ parsed_type_tag_name[sizeof (parsed_type_tag_name) - 1] = '\0';
+ has_tag = 1;
+
+ get_token ();
+
+ }
+
+ if (tok.kind == TOK_LBRACE) {
+
+ get_token ();
+
+ while (tok.kind != TOK_EOF && tok.kind != TOK_RBRACE) {
+
+ int member_type_size;
+ int member_type_is_floating;
+ int member_is_aggregate;
+ int member_has_tag;
+
+ int member_fields[MAX_AGG_FIELDS];
+ int member_field_count;
+
+ int i;
+
+ if (!is_type_start (tok.kind)) {
+
+ report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "expected member declaration");
+
+ skip_balanced_until (TOK_SEMI, TOK_RBRACE, TOK_EOF);
+ _accept (TOK_SEMI);
+
+ continue;
+
+ }
+
+ parse_type_spec ();
+
+ member_type_size = parsed_type_size;
+ member_type_is_floating = parsed_type_is_floating;
+ member_is_aggregate = parsed_type_is_aggregate;
+ member_has_tag = parsed_type_has_tag;
+ member_field_count = parsed_field_count;
+
+ for (i = 0; i < member_field_count; i++) {
+ member_fields[i] = parsed_field_sizes[i];
+ }
+
+ if (tok.kind == TOK_SEMI) {
+
+ if (member_is_aggregate && !member_has_tag) {
+
+ int member_init_size = fields_storage_size (member_fields, member_field_count);
+
+ if (is_union) {
+
+ if (member_type_size > aggregate_size) {
+ aggregate_size = member_type_size;
+ }
+
+ if (union_init_field_count == 0) {
+
+ union_init_field_count = member_field_count;
+
+ for (i = 0; i < member_field_count; i++) {
+ union_init_fields[i] = member_fields[i];
+ }
+
+ union_init_size = member_init_size;
+
+ }
+
+ } else {
+
+ aggregate_size += member_type_size;
+
+ for (i = 0; i < member_field_count && aggregate_field_count < MAX_AGG_FIELDS; i++) {
+ aggregate_fields[aggregate_field_count++] = member_fields[i];
+ }
+
+ if (member_type_size > member_init_size && aggregate_field_count < MAX_AGG_FIELDS) {
+ aggregate_fields[aggregate_field_count++] = -(member_type_size - member_init_size);
+ }
+
+ }
+
+ }
+
+ } else {
+
+ for (;;) {
+
+ char *member = 0;
+
+ int member_size, repeat;
+ int init_size;
+
+ int member_info_elem_size;
+ int member_align;
+
+ parse_declarator (&member);
+
+ if (declarator_is_pointer) {
+
+ member_size = DATA_PTR;
+ member_field_count = 1;
+ member_fields[0] = DATA_PTR;
+
+ } else {
+
+ member_size = member_type_size;
+
+ if (member_field_count <= 0) {
+
+ member_field_count = 1;
+ member_fields[0] = member_type_size;
+
+ }
+
+ }
+
+ member_align = declarator_is_pointer ? type_alignment (DATA_PTR) : type_alignment (member_type_size);
+
+ if (member_align > aggregate_align) {
+ aggregate_align = member_align;
+ }
+
+ repeat = 1;
+
+ if (declarator_has_array) {
+
+ repeat = (int) declarator_array_count;
+ member_size *= declarator_array_count;
+
+ }
+
+ if (_accept (TOK_COLON)) {
+ skip_balanced_until (TOK_COMMA, TOK_SEMI, TOK_RBRACE);
+ }
+
+ if (declarator_has_array && declarator_is_pointer) {
+ member_info_elem_size = DATA_PTR;
+ } else {
+
+ member_info_elem_size = declarator_is_pointer ?
+ (declarator_pointer_depth > 1 ? DATA_PTR : (parsed_type_is_aggregate ? parsed_type_size : (parsed_type_size & 0x1f))) :
+ (declarator_has_array ? member_type_size : member_size);
+
+ }
+
+ init_size = fields_storage_size (member_fields, member_field_count);
+
+ if (is_union) {
+
+ remember_member_info_ex (member, 0, member_size, member_info_elem_size, declarator_is_pointer ? declarator_pointer_depth : 0, declarator_has_array, declarator_is_pointer ? 0 : member_type_is_floating);
+
+ if (member_size > aggregate_size) {
+ aggregate_size = member_size;
+ }
+
+ if (union_init_field_count == 0) {
+
+ union_init_field_count = member_field_count;
+
+ for (i = 0; i < member_field_count; i++) {
+ union_init_fields[i] = member_fields[i];
+ }
+
+ union_init_size = init_size;
+
+ }
+
+ } else {
+
+ int member_offset = (int) align_up_long (aggregate_size, member_align);
+ int padding = member_offset - aggregate_size;
+
+ int r;
+
+ if (padding > 0 && aggregate_field_count < MAX_AGG_FIELDS) {
+ aggregate_fields[aggregate_field_count++] = -padding;
+ }
+
+ aggregate_size = member_offset;
+
+ remember_member_info_ex (member, member_offset, member_size, member_info_elem_size, declarator_is_pointer ? declarator_pointer_depth : 0, declarator_has_array, declarator_is_pointer ? 0 : member_type_is_floating);
+ aggregate_size += member_size;
+
+ for (r = 0; r < repeat; r++) {
+
+ for (i = 0; i < member_field_count && aggregate_field_count < MAX_AGG_FIELDS; i++) {
+ aggregate_fields[aggregate_field_count++] = member_fields[i];
+ }
+
+ }
+
+ if (member_size > init_size * repeat && aggregate_field_count < MAX_AGG_FIELDS) {
+ aggregate_fields[aggregate_field_count++] = -(member_size - init_size * repeat);
+ }
+
+ }
+
+ if (member) {
+ free (member);
+ }
+
+ if (!_accept (TOK_COMMA)) {
+ break;
+ }
+
+ }
+
+ }
+
+ expect (TOK_SEMI, ";");
+
+ }
+
+ expect (TOK_RBRACE, "}");
+
+ if (aggregate_size <= 0) {
+ aggregate_size = DATA_CHAR & 0x1f;
+ }
+
+ aggregate_size = (int) align_up_long (aggregate_size, aggregate_align);
+
+ {
+
+ const char *owner_name = 0;
+ char anon_owner_name[64];
+
+ int mi;
+
+ if (has_tag) {
+ owner_name = tag_name;
+ } else {
+
+ sprintf (anon_owner_name, "<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)) {
+
+ long count = 1;
+ declarator_has_array = 1;
+
+ if (tok.kind != TOK_RBRACK) {
+
+ enum token_kind kill[3];
+ int v;
+
+ kill[0] = TOK_RBRACK;
+ kill[1] = TOK_EOF;
+ kill[2] = 0;
+
+ v = expr_const (kill);
+
+ if (v > 0) {
+ count = v;
+ }
+
+ } else {
+ declarator_array_unsized = 1;
+ }
+
+ if (declarator_array_count <= 0) {
+ declarator_array_count = 1;
+ }
+
+ declarator_array_count *= count;
+ declarator_last_array_count = count;
+
+ expect (TOK_RBRACK, "]");
+ continue;
+
+ }
+
+ if (_accept (TOK_LPAREN)) {
+
+ declarator_has_function = 1;
+
+ if (tok.kind != TOK_RPAREN) {
+
+ for (;;) {
+
+ if (tok.kind == TOK_ELLIPSIS) {
+
+ declarator_function_is_variadic = 1;
+ get_token ();
+
+ } else if (is_type_start (tok.kind)) {
+
+ long saved_array_count = declarator_array_count;
+
+ int saved_type_size = parsed_type_size;
+ int saved_storage_class = parsed_storage_class;
+ int saved_is_aggregate = parsed_type_is_aggregate;
+ int saved_is_void = parsed_type_is_void;
+ int saved_is_unsigned = parsed_type_is_unsigned;
+ int saved_is_floating = parsed_type_is_floating;
+ int saved_has_tag = parsed_type_has_tag;
+ int saved_is_pointer = declarator_is_pointer;
+ int saved_pointer_depth = declarator_pointer_depth;
+ int saved_has_array = declarator_has_array;
+ int saved_has_function = declarator_has_function;
+ int saved_function_is_pointer = declarator_function_is_pointer;
+ int saved_function_param_count = declarator_function_param_count;
+ int saved_function_has_prototype = declarator_function_has_prototype;
+ int saved_array_unsized = declarator_array_unsized;
+ int saved_is_inline = parsed_type_is_inline;
+
+ char *param_name = 0;
+ int param_base_size, param_size;
+
+ int count_this_param = 0;
+ int saw_void_param_list = 0;
+
+ parse_type_spec ();
+ param_base_size = parsed_type_size;
+
+ /*
+ * Parameter declarators must not inherit the outer
+ * declarator state. In particular, in declarations
+ * such as:
+ *
+ * FILE **__gtin(void);
+ *
+ * the function return type leaves declarator_is_pointer
+ * set before the parameter list is parsed. If that flag
+ * is still set while looking at the abstract void
+ * parameter list, void is mistaken for a real pointer
+ * parameter and the prototype is recorded as taking one
+ * argument.
+ */
+ declarator_is_pointer = 0;
+ declarator_pointer_depth = 0;
+ declarator_has_array = 0;
+ declarator_has_function = 0;
+ declarator_function_is_pointer = 0;
+ declarator_array_unsized = 0;
+ declarator_array_count = 1;
+ declarator_last_array_count = 1;
+
+ if (parsed_type_only_qualifiers && token_is_ident ()) {
+ get_token ();
+ }
+
+ if (tok.kind != TOK_COMMA && tok.kind != TOK_RPAREN) {
+ parse_declarator (¶m_name);
+ }
+
+ if (!parsed_type_is_void || declarator_is_pointer || declarator_has_array || declarator_has_function || param_name) {
+
+ count_this_param = 1;
+ param_size = (declarator_is_pointer || declarator_has_array || declarator_has_function || parsed_type_is_array_typedef) ? DATA_PTR : declarator_object_size (param_base_size);
+
+ if (declarator_depth == 1) {
+
+ add_pending_param (param_name, param_size, type_alignment (param_size),
+ (declarator_is_pointer || parsed_type_is_array_typedef ? 0 : parsed_type_is_unsigned),
+ (declarator_is_pointer || declarator_has_array || declarator_has_function || parsed_type_is_array_typedef) ? 0 : parsed_type_is_floating,
+ declarator_pointer_depth, parsed_type_is_aggregate ? parsed_type_size : (parsed_type_size & 0x1f));
+
+ }
+
+ } else {
+ saw_void_param_list = 1;
+ }
+
+ if (param_name) {
+ free (param_name);
+ }
+
+ parsed_type_size = saved_type_size;
+ parsed_storage_class = saved_storage_class;
+ parsed_type_is_aggregate = saved_is_aggregate;
+ parsed_type_is_void = saved_is_void;
+ parsed_type_is_unsigned = saved_is_unsigned;
+ parsed_type_is_floating = saved_is_floating;
+ parsed_type_has_tag = saved_has_tag;
+ parsed_type_is_inline = saved_is_inline;
+
+ declarator_is_pointer = saved_is_pointer;
+ declarator_pointer_depth = saved_pointer_depth;
+ declarator_has_array = saved_has_array;
+ declarator_has_function = saved_has_function;
+ declarator_function_is_pointer = saved_function_is_pointer;
+ declarator_function_param_count = saved_function_param_count + (count_this_param ? 1 : 0);
+ declarator_function_has_prototype = saved_function_has_prototype || count_this_param || saw_void_param_list;
+ declarator_array_count = saved_array_count;
+
+ declarator_array_unsized = saved_array_unsized;
+
+ } else if (token_is_ident ()) {
+
+ char *maybe_type_name = xstrdup (tok.ident);
+ int unknown_typedef_pointer = 0;
+ int consumed_as_prototype_param = 0;
+
+ get_token ();
+
+ while (tok.kind == TOK_CONST || tok.kind == TOK_VOLATILE || tok.kind == TOK_RESTRICT) {
+ get_token ();
+ }
+
+ while (tok.kind == TOK_STAR) {
+
+ unknown_typedef_pointer = 1;
+ get_token ();
+
+ while (tok.kind == TOK_CONST || tok.kind == TOK_VOLATILE || tok.kind == TOK_RESTRICT) {
+ get_token ();
+ }
+
+ }
+
+ if (token_is_ident ()) {
+
+ char *param_name = xstrdup (tok.ident);
+ int param_size = unknown_typedef_pointer ? DATA_PTR : DATA_PTR;
+
+ get_token ();
+
+ if (declarator_depth == 1) {
+ add_pending_param (param_name, param_size, type_alignment (param_size), 0, 0, unknown_typedef_pointer ? 1 : 0, DATA_INT & 0x1f);
+ }
+
+ free (param_name);
+
+ declarator_function_param_count++;
+ declarator_function_has_prototype = 1;
+ consumed_as_prototype_param = 1;
+
+ }
+
+ if (!consumed_as_prototype_param) {
+ declarator_function_param_count++;
+ }
+
+ if (maybe_type_name) {
+ free (maybe_type_name);
+ }
+
+ } else {
+ skip_balanced_until (TOK_COMMA, TOK_RPAREN, TOK_EOF);
+ }
+
+ if (!_accept (TOK_COMMA)) {
+ break;
+ }
+
+ }
+
+ }
+
+ expect (TOK_RPAREN, ")");
+ continue;
+ }
+
+ break;
+
+ }
+
+}
+
+static void parse_declarator_inner (char **out_name) {
+
+ while (tok.kind == TOK_STAR) {
+
+ declarator_is_pointer = 1;
+ declarator_pointer_depth++;
+
+ get_token ();
+
+ while (tok.kind == TOK_CONST || tok.kind == TOK_VOLATILE || tok.kind == TOK_RESTRICT) {
+ get_token ();
+ }
+
+ }
+
+ parse_direct_declarator (out_name);
+
+}
+
+static void parse_declarator (char **out_name) {
+
+ int top_level = (declarator_depth == 0);
+
+ int saved_capture_declarator_name_location = capture_declarator_name_location;
+ int saved_captured_declarator_name_location = captured_declarator_name_location;
+
+ const char *saved_captured_declarator_name_start = captured_declarator_name_start;
+ const char *saved_captured_declarator_name_caret = captured_declarator_name_caret;
+
+ unsigned long saved_captured_declarator_name_line = captured_declarator_name_line;
+
+ if (top_level) {
+
+ clear_pending_params ();
+
+ capture_declarator_name_location = 1;
+ captured_declarator_name_location = 0;
+ captured_declarator_name_start = 0;
+ captured_declarator_name_caret = 0;
+ captured_declarator_name_line = 0;
+
+ }
+
+ declarator_depth++;
+
+ declarator_is_pointer = 0;
+ declarator_pointer_depth = 0;
+ declarator_has_array = 0;
+ declarator_has_function = 0;
+ declarator_function_is_pointer = 0;
+ declarator_function_param_count = 0;
+ declarator_function_has_prototype = 0;
+ declarator_function_is_variadic = 0;
+ declarator_array_unsized = 0;
+ declarator_array_count = 1;
+ declarator_last_array_count = 1;
+
+ parse_declarator_inner (out_name);
+
+ if (top_level && parsed_type_is_array_typedef && !declarator_is_pointer && !declarator_has_array && !declarator_has_function) {
+
+ declarator_has_array = 1;
+
+ declarator_array_count = parsed_type_array_count > 0 ? parsed_type_array_count : 1;
+ declarator_last_array_count = declarator_array_count;
+
+ if (parsed_type_array_element_size > 0) {
+ parsed_type_size = parsed_type_array_element_size;
+ }
+
+ }
+
+ declarator_depth--;
+
+ if (top_level) {
+
+ if (captured_declarator_name_location) {
+
+ last_declarator_name_line = captured_declarator_name_line;
+ last_declarator_name_start = captured_declarator_name_start;
+ last_declarator_name_caret = captured_declarator_name_caret;
+
+ }
+
+ capture_declarator_name_location = saved_capture_declarator_name_location;
+ captured_declarator_name_location = saved_captured_declarator_name_location;
+ captured_declarator_name_start = saved_captured_declarator_name_start;
+ captured_declarator_name_caret = saved_captured_declarator_name_caret;
+ captured_declarator_name_line = saved_captured_declarator_name_line;
+
+ }
+
+}
+
+static void apply_typedef_array_to_declarator (void) {
+
+ if (parsed_type_is_array_typedef && !declarator_is_pointer &&
+ !declarator_has_array && !declarator_has_function) {
+
+ declarator_has_array = 1;
+ declarator_array_count = parsed_type_array_count > 0 ? parsed_type_array_count : 1;
+ declarator_last_array_count = declarator_array_count;
+
+ if (parsed_type_array_element_size > 0) {
+ parsed_type_size = parsed_type_array_element_size;
+ }
+
+ }
+
+}
+
+static void parse_old_style_param_decls (void) {
+
+ while (is_type_start (tok.kind)) {
+
+ parse_type_spec ();
+
+ for (;;) {
+
+ char *name = 0;
+ parse_declarator (&name);
+
+ if (name) {
+ free (name);
+ }
+
+ if (_accept (TOK_ASSIGN)) {
+ skip_initializer ();
+ }
+
+ if (!_accept (TOK_COMMA)) {
+ break;
+ }
+
+ }
+
+ expect (TOK_SEMI, ";");
+
+ }
+
+}
+
+static void emit_extern_symbol (const char *name, int size, int is_function);
+static void emit_extern_reference_symbol (const char *name, int size);
+
+static void emit_function_start (const char *name, int is_static) {
+
+ const char *asm_name;
+
+ if (!state->ofp) {
+ return;
+ }
+
+ asm_name = asm_global_symbol_name (name);
+ switch_section (SECTION_TEXT);
+
+ if (state->syntax & ASM_SYNTAX_MASM) {
+
+ if (!is_static) {
+ fprintf (state->ofp, "public %s\n", asm_name);
+ }
+
+ } else if (state->syntax & ASM_SYNTAX_NASM) {
+
+ if (!is_static) {
+ fprintf (state->ofp, "global %s\n", asm_name);
+ }
+
+ } else {
+
+ if (!is_static) {
+ fprintf (state->ofp, ".globl %s\n", asm_name);
+ }
+
+ }
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ fprintf (state->ofp, "%s:\n", asm_name);
+ fprintf (state->ofp, " push ebp\n");
+ fprintf (state->ofp, " mov ebp, esp\n");
+
+ } else {
+
+ fprintf (state->ofp, "%s:\n", asm_name);
+ fprintf (state->ofp, " pushl %%ebp\n");
+ fprintf (state->ofp, " movl %%esp, %%ebp\n");
+
+ }
+
+}
+
+static void emit_pending_return_jump (void) {
+
+ if (!pending_return_jump) {
+ return;
+ }
+
+ if (!state->ofp) {
+
+ pending_return_jump = 0;
+ return;
+
+ }
+
+ if ((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) {
+ fprintf (state->ofp, " jmp L%d\n", current_return_label);
+ } else {
+ fprintf (state->ofp, " jmp .L%d\n", current_return_label);
+ }
+
+ pending_return_jump = 0;
+
+}
+
+static void emit_stack_adjust (long bytes, int allocate) {
+
+ if (!state->ofp || bytes <= 0) {
+ return;
+ }
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " %s esp, %ld\n", allocate ? "sub" : "add", bytes);
+ } else {
+ fprintf (state->ofp, " %s $%ld, %%esp\n", allocate ? "subl" : "addl", bytes);
+ }
+
+}
+
+static void format_intel_ebp_offset (char *buf, size_t bufsz, long offset) {
+
+ if (!buf || bufsz == 0) {
+ return;
+ }
+
+ if (offset < 0) {
+ sprintf (buf, "[ebp - %ld]", -offset);
+ } else if (offset > 0) {
+ sprintf (buf, "[ebp + %ld]", offset);
+ } else {
+ sprintf (buf, "[ebp]");
+ }
+
+}
+
+static const char *format_nasm_memory_operand (char *buf, size_t bufsz, const char *operand) {
+
+ if (!operand) {
+ return operand;
+ }
+
+ if (!(state->syntax & ASM_SYNTAX_NASM)) {
+ return operand;
+ }
+
+ if (operand[0] == '[') {
+ return operand;
+ }
+
+ if (buf && bufsz > 0) {
+
+ sprintf (buf, "[%s]", operand);
+ return buf;
+
+ }
+
+ return operand;
+
+}
+
+static void emit_local_store_const (long offset, int size, int64_s value) {
+
+ char memref_hi[64], memref[64];
+ unsigned long high, low;
+
+ if (!state->ofp) {
+ return;
+ }
+
+ low = value.low;
+ high = value.high;
+
+ if (size > 8 && low == 0 && high == 0) {
+
+ long pos;
+
+ for (pos = 0; pos < size; pos += 4) {
+ emit_local_store_const (offset + pos, DATA_INT, value);
+ }
+
+ return;
+
+ }
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ format_intel_ebp_offset (memref, sizeof (memref), offset);
+
+ if (size == (DATA_CHAR & 0x1f)) {
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov byte %s, %lu\n" : " mov byte ptr %s, %lu\n"), memref, low & ((1 << 8) - 1));
+ } else if (size == (DATA_SHORT & 0x1f)) {
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov word %s, %lu\n" : " mov word ptr %s, %lu\n"), memref, low & ((1 << 16) - 1));
+ } else if (size == (DATA_LLONG & 0x1f) || size == (DATA_DOUBLE & 0x1f)) {
+
+ format_intel_ebp_offset (memref_hi, sizeof (memref_hi), offset + 4);
+
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov dword %s, %lu\n" : " mov dword ptr %s, %lu\n"), memref, low & U32_MASK);
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov dword %s, %lu\n" : " mov dword ptr %s, %lu\n"), memref_hi, high & U32_MASK);
+
+ } else {
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov dword %s, %lu\n" : " mov dword ptr %s, %lu\n"), memref, low & U32_MASK);
+ }
+
+ } else {
+
+ if (size == (DATA_CHAR & 0x1f)) {
+ fprintf (state->ofp, " movb $%lu, %ld(%%ebp)\n", low & 0xffUL, offset);
+ } else if (size == (DATA_SHORT & 0x1f)) {
+ fprintf (state->ofp, " movw $%lu, %ld(%%ebp)\n", low & 0xffffUL, offset);
+ } else if (size == (DATA_LLONG & 0x1f) || size == (DATA_DOUBLE & 0x1f)) {
+
+ fprintf (state->ofp, " movl $%lu, %ld(%%ebp)\n", low & U32_MASK, offset);
+ fprintf (state->ofp, " movl $%lu, %ld(%%ebp)\n", high & U32_MASK, offset + 4);
+
+ } else {
+ fprintf (state->ofp, " movl $%lu, %ld(%%ebp)\n", low & U32_MASK, offset);
+ }
+
+ }
+
+}
+
+static void emit_local_store_address (long offset, const char *symbol) {
+
+ const char *asm_symbol;
+ char memref[64];
+
+ if (!state->ofp || !symbol) {
+ return;
+ }
+
+ emit_extern_reference_symbol (symbol, DATA_PTR);
+ asm_symbol = asm_global_symbol_name (symbol);
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ format_intel_ebp_offset (memref, sizeof (memref), offset);
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov dword %s, %s\n" : " mov dword ptr %s, offset %s\n"), memref, asm_symbol);
+
+ } else {
+ fprintf (state->ofp, " movl $%s, %ld(%%ebp)\n", asm_symbol, offset);
+ }
+
+}
+
+static void emit_local_store_stack (long dst_offset, int dst_size, long src_offset, int src_size) {
+
+ char dst[64];
+ char src[64];
+
+ if (!state->ofp) {
+ return;
+ }
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ format_intel_ebp_offset (dst, sizeof (dst), dst_offset);
+ format_intel_ebp_offset (src, sizeof (src), src_offset);
+
+ if (dst_size == (DATA_CHAR & 0x1f)) {
+
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov al, byte %s\n" : " mov al, byte ptr %s\n"), src);
+ fprintf (state->ofp, " mov byte ptr %s, al\n", dst);
+
+ } else if (dst_size == (DATA_SHORT & 0x1f)) {
+
+ if (src_size == (DATA_CHAR & 0x1f)) {
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov ax, byte %s\n" : " movzx ax, byte ptr %s\n"), src);
+ } else {
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov ax, word %s\n" : " mov ax, word ptr %s\n"), src);
+ }
+
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov word %s, ax\n" : " mov word ptr %s, ax\n"), dst);
+
+ } else {
+
+ if (src_size == (DATA_CHAR & 0x1f)) {
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " movzx eax, byte %s\n" : " movzx eax, byte ptr %s\n"), src);
+ } else if (src_size == (DATA_SHORT & 0x1f)) {
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " movzx eax, word %s\n" : " movzx eax, word ptr %s\n"), src);
+ } else {
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov eax, dword %s\n" : " mov eax, dword ptr %s\n"), src);
+ }
+
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov dword %s, eax\n" : " mov dword ptr %s, eax\n"), dst);
+
+ }
+
+ } else {
+
+ if (dst_size == (DATA_CHAR & 0x1f)) {
+
+ fprintf (state->ofp, " movb %ld(%%ebp), %%al\n", src_offset);
+ fprintf (state->ofp, " movb %%al, %ld(%%ebp)\n", dst_offset);
+
+ } else if (dst_size == (DATA_SHORT & 0x1f)) {
+
+ fprintf (state->ofp, " movw %ld(%%ebp), %%ax\n", src_offset);
+ fprintf (state->ofp, " movw %%ax, %ld(%%ebp)\n", dst_offset);
+
+ } else {
+
+ fprintf (state->ofp, " movl %ld(%%ebp), %%eax\n", src_offset);
+ fprintf (state->ofp, " movl %%eax, %ld(%%ebp)\n", dst_offset);
+
+ }
+
+ }
+
+}
+
+static void emit_local_store_global (long dst_offset, int dst_size, const char *symbol) {
+
+ const char *asm_symbol;
+ char dst[64];
+
+ if (!state->ofp || !symbol) {
+ return;
+ }
+
+ emit_extern_reference_symbol (symbol, get_global_symbol_size (symbol));
+ asm_symbol = asm_global_symbol_name (symbol);
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ format_intel_ebp_offset (dst, sizeof (dst), dst_offset);
+
+ if (dst_size == (DATA_CHAR & 0x1f)) {
+
+ if (state->syntax & ASM_SYNTAX_NASM) {
+
+ fprintf (state->ofp, " mov al, byte [%s]\n", asm_symbol);
+ fprintf (state->ofp, " mov byte %s, al\n", dst);
+
+ } else {
+
+ fprintf (state->ofp, " mov al, byte ptr %s\n", asm_symbol);
+ fprintf (state->ofp, " mov byte ptr %s, al\n", dst);
+
+ }
+
+ } else if (dst_size == (DATA_SHORT & 0x1f)) {
+
+ if (state->syntax & ASM_SYNTAX_NASM) {
+
+ fprintf (state->ofp, " mov ax, word [%s]\n", asm_symbol);
+ fprintf (state->ofp, " mov word %s, ax\n", dst);
+
+ } else {
+
+ fprintf (state->ofp, " mov ax, word ptr %s\n", asm_symbol);
+ fprintf (state->ofp, " mov word ptr %s, ax\n", dst);
+
+ }
+
+ } else {
+
+ if (state->syntax & ASM_SYNTAX_NASM) {
+
+ fprintf (state->ofp, " mov eax, dword [%s]\n", asm_symbol);
+ fprintf (state->ofp, " mov dword %s, eax\n", dst);
+
+ } else {
+
+ fprintf (state->ofp, " mov eax, dword ptr %s\n", asm_symbol);
+ fprintf (state->ofp, " mov dword ptr %s, eax\n", dst);
+
+ }
+
+ }
+
+ } else {
+
+ if (dst_size == (DATA_CHAR & 0x1f)) {
+
+ fprintf (state->ofp, " movb %s, %%al\n", asm_symbol);
+ fprintf (state->ofp, " movb %%al, %ld(%%ebp)\n", dst_offset);
+
+ } else if (dst_size == (DATA_SHORT & 0x1f)) {
+
+ fprintf (state->ofp, " movw %s, %%ax\n", asm_symbol);
+ fprintf (state->ofp, " movw %%ax, %ld(%%ebp)\n", dst_offset);
+
+ } else {
+
+ fprintf (state->ofp, " movl %s, %%eax\n", asm_symbol);
+ fprintf (state->ofp, " movl %%eax, %ld(%%ebp)\n", dst_offset);
+
+ }
+
+ }
+
+}
+
+static void emit_local_initializers (struct local_init *inits, int init_count) {
+
+ int i;
+
+ for (i = 0; i < init_count; i++) {
+
+ if (inits[i].kind == LOCAL_INIT_ADDRESS) {
+ emit_local_store_address (inits[i].offset, inits[i].symbol);
+ } else if (inits[i].kind == LOCAL_INIT_STACK) {
+ emit_local_store_stack (inits[i].offset, inits[i].size, inits[i].source_offset, inits[i].source_size);
+ } else if (inits[i].kind == LOCAL_INIT_GLOBAL) {
+ emit_local_store_global (inits[i].offset, inits[i].size, inits[i].symbol);
+ } else {
+ emit_local_store_const (inits[i].offset, inits[i].size, inits[i].value);
+ }
+
+ if (inits[i].symbol) {
+
+ free (inits[i].symbol);
+ inits[i].symbol = 0;
+
+ }
+
+ }
+
+}
+
+static void emit_function_end (void) {
+
+ if (!state->ofp) {
+ return;
+ }
+
+ if ((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) {
+
+ fprintf (state->ofp, "L%d:\n", current_return_label);
+
+ if (current_function_returns_aggregate) {
+
+ if (state->syntax & ASM_SYNTAX_NASM) {
+ fprintf (state->ofp, " mov eax, dword [ebp + 8]\n");
+ } else {
+ fprintf (state->ofp, " mov eax, dword ptr [ebp + 8]\n");
+ }
+
+ }
+
+ fprintf (state->ofp, " leave\n");
+ fprintf (state->ofp, " ret\n");
+
+ } else {
+
+ fprintf (state->ofp, ".L%d:\n", current_return_label);
+
+ if (current_function_returns_aggregate) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " mov eax, dword ptr [ebp + 8]\n");
+ } else {
+ fprintf (state->ofp, " movl 8(%%ebp), %%eax\n");
+ }
+
+ }
+
+ fprintf (state->ofp, " leave\n");
+ fprintf (state->ofp, " ret\n");
+
+ }
+
+}
+
+static void emit_preserve_assignment64_regs (enum token_kind op) {
+
+ if (!state->ofp) {
+ return;
+ }
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ fprintf (state->ofp, " push ebx\n");
+
+ if (op == TOK_STAREQ || op == TOK_STAR || op == TOK_SLASHEQ || op == TOK_BSLASH || op == TOK_MODEQ || op == TOK_MOD) {
+
+ fprintf (state->ofp, " push esi\n");
+ fprintf (state->ofp, " push edi\n");
+
+ }
+
+ } else {
+
+ fprintf (state->ofp, " pushl %%ebx\n");
+
+ if (op == TOK_STAREQ || op == TOK_STAR || op == TOK_SLASHEQ || op == TOK_BSLASH || op == TOK_MODEQ || op == TOK_MOD) {
+
+ fprintf (state->ofp, " pushl %%esi\n");
+ fprintf (state->ofp, " pushl %%edi\n");
+
+ }
+
+ }
+
+}
+
+static void emit_restore_assignment64_regs (enum token_kind op) {
+
+ if (!state->ofp) {
+ return;
+ }
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ if (op == TOK_STAREQ || op == TOK_STAR || op == TOK_SLASHEQ || op == TOK_BSLASH || op == TOK_MODEQ || op == TOK_MOD) {
+
+ fprintf (state->ofp, " pop edi\n");
+ fprintf (state->ofp, " pop esi\n");
+
+ }
+
+ fprintf (state->ofp, " pop ebx\n");
+
+ } else {
+
+ if (op == TOK_STAREQ || op == TOK_STAR || op == TOK_SLASHEQ || op == TOK_BSLASH || op == TOK_MODEQ || op == TOK_MOD) {
+
+ fprintf (state->ofp, " popl %%edi\n");
+ fprintf (state->ofp, " popl %%esi\n");
+
+ }
+
+ fprintf (state->ofp, " popl %%ebx\n");
+
+ }
+
+}
+
+static int is_string_token (void);
+static void parse_string_initializer_values (int64_s *values, int max_values, int *count);
+
+static int parse_constexpr_null_member_address_after_lparen (int64_s *out) {
+
+ char current_tag_name[128];
+
+ int current_object_size;
+ unsigned long total_offset = 0;
+
+ if (tok.kind != TOK_LPAREN) {
+ return 0;
+ }
+
+ get_token ();
+
+ if (tok.kind != TOK_LPAREN) {
+ return 0;
+ }
+
+ get_token ();
+
+ if (!parse_cast_type_name (0, 0, 0)) {
+ return 0;
+ }
+
+ current_tag_name[0] = '\0';
+
+ if (last_cast_type_tag_name[0]) {
+
+ strncpy (current_tag_name, last_cast_type_tag_name, sizeof (current_tag_name) - 1);
+ current_tag_name[sizeof (current_tag_name) - 1] = '\0';
+
+ }
+
+ current_object_size = last_cast_type_object_size;
+
+ if (!((tok.kind == TOK_CINT || tok.kind == TOK_CUINT || tok.kind == TOK_CLONG ||
+ tok.kind == TOK_CULONG || tok.kind == TOK_CLLONG || tok.kind == TOK_CULLONG) &&
+ tok.val.i.low == 0 && tok.val.i.high == 0)) {
+ return 0;
+ }
+
+ get_token ();
+ expect (TOK_RPAREN, ")");
+
+ while (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) {
+
+ enum token_kind member_op = tok.kind;
+ char *member;
+
+ const char *member_start;
+ const char *member_caret;
+
+ unsigned long member_line;
+
+ int offset = 0;
+ int size = DATA_INT & 0x1f;
+ int elem_size = DATA_INT & 0x1f;
+ int pointer_depth = 0;
+ int is_array = 0;
+
+ get_token ();
+
+ member_start = tok.start;
+ member_caret = tok.caret;
+ member_line = get_line_number ();
+
+ if (tok.kind != TOK_IDENT) {
+
+ report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret,
+ "expected member name after %s", member_op == TOK_ARROW ? "->" : ".");
+ return 1;
+
+ }
+
+ member = xstrdup (tok.ident);
+ get_token ();
+
+ if (!find_member_info_ex_bounded (member, current_object_size,
+ current_tag_name[0] ? current_tag_name : 0,
+ &offset, &size, &elem_size, &pointer_depth, &is_array, 0)) {
+
+ report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret,
+ "unknown member '%s'", member);
+
+ free (member);
+ return 1;
+
+ }
+
+ free (member);
+ total_offset += (unsigned long) offset;
+
+ if (last_found_member_tag_name && last_found_member_tag_name[0]) {
+
+ strncpy (current_tag_name, last_found_member_tag_name, sizeof (current_tag_name) - 1);
+ current_tag_name[sizeof (current_tag_name) - 1] = '\0';
+
+ } else {
+ current_tag_name[0] = '\0';
+ }
+
+ while (tok.kind == TOK_LBRACK) {
+
+ enum token_kind kill[2];
+ int64_s index;
+
+ get_token ();
+
+ kill[0] = TOK_RBRACK;
+ kill[1] = 0;
+
+ index = expr_const64 (kill);
+
+ expect (TOK_RBRACK, "]");
+ total_offset += index.low * (unsigned long) (elem_size > 0 ? elem_size : (DATA_INT & 0x1f));
+
+ }
+
+ if (pointer_depth > 0) {
+ current_object_size = elem_size > 0 ? elem_size : (DATA_PTR & 0x1f);
+ } else if (is_array && elem_size > 0) {
+ current_object_size = elem_size;
+ } else {
+ current_object_size = size;
+ }
+
+ }
+
+ expect (TOK_RPAREN, ")");
+
+ if (out) {
+ zext64 (out, total_offset);
+ }
+
+ return 1;
+
+}
+
+int parse_constexpr_address_of_null_member (int64_s *out) {
+
+ if (tok.kind != TOK_AMPER) {
+ return 0;
+ }
+
+ get_token ();
+
+ if (tok.kind != TOK_LPAREN) {
+ return 0;
+ }
+
+ get_token ();
+
+ if (parse_constexpr_null_member_address_after_lparen (out)) {
+ return 1;
+ }
+
+ return 0;
+
+}
+
+int token_is_sizeof_keyword (void) {
+ return tok.kind == TOK_SIZEOF;
+}
+
+static int64_s sizeof_from_current_token (void) {
+
+ int64_s v;
+ int size;
+
+ zext64 (&v, 0);
+ get_token ();
+
+ size = parse_sizeof_value ();
+ zext64 (&v, (unsigned long) size);
+
+ return v;
+
+}
+
+static int64_s const64_from_current_expr (void) {
+
+ enum token_kind kill[6];
+ int64_s v;
+
+ kill[0] = TOK_SEMI;
+ kill[1] = TOK_COMMA;
+ kill[2] = TOK_RPAREN;
+ kill[3] = TOK_RBRACK;
+ kill[4] = TOK_RBRACE;
+ kill[5] = 0;
+
+ v = expr_const64 (kill);
+ return v;
+
+}
+
+static int64_s const64_from_current_operand (void) {
+
+ enum token_kind kill[26];
+
+ if (token_is_sizeof_keyword ()) {
+ return sizeof_from_current_token ();
+ }
+
+ kill[0] = TOK_PLUS;
+ kill[1] = TOK_MINUS;
+ kill[2] = TOK_STAR;
+ kill[3] = TOK_BSLASH;
+ kill[4] = TOK_MOD;
+ kill[5] = TOK_LSH;
+ kill[6] = TOK_RSH;
+ kill[7] = TOK_AMPER;
+ kill[8] = TOK_PIPE;
+ kill[9] = TOK_CARET;
+ kill[10] = TOK_LESS;
+ kill[11] = TOK_LTEQ;
+ kill[12] = TOK_GREATER;
+ kill[13] = TOK_GTEQ;
+ kill[14] = TOK_EQEQ;
+ kill[15] = TOK_NOTEQ;
+ kill[16] = TOK_LOGAND;
+ kill[17] = TOK_LOGOR;
+ kill[18] = TOK_QMARK;
+ kill[19] = TOK_COLON;
+ kill[20] = TOK_SEMI;
+ kill[21] = TOK_COMMA;
+ kill[22] = TOK_RPAREN;
+ kill[23] = TOK_RBRACK;
+ kill[24] = TOK_RBRACE;
+ kill[25] = 0;
+
+ return expr_const64 (kill);
+
+}
+
+static int64_s const64_from_current_foldable_expr (void) {
+
+ enum token_kind kill[17];
+
+ if (token_is_sizeof_keyword ()) {
+ return sizeof_from_current_token ();
+ }
+
+ kill[0] = TOK_LESS;
+ kill[1] = TOK_LTEQ;
+ kill[2] = TOK_GREATER;
+ kill[3] = TOK_GTEQ;
+ kill[4] = TOK_EQEQ;
+ kill[5] = TOK_NOTEQ;
+ kill[6] = TOK_LOGAND;
+ kill[7] = TOK_LOGOR;
+ kill[8] = TOK_QMARK;
+ kill[9] = TOK_COLON;
+ kill[10] = TOK_SEMI;
+ kill[11] = TOK_COMMA;
+ kill[12] = TOK_RPAREN;
+ kill[13] = TOK_RBRACK;
+ kill[14] = TOK_RBRACE;
+ kill[15] = 0;
+ kill[16] = 0;
+
+ return expr_const64 (kill);
+
+}
+
+static long const_from_current_expr (void) {
+
+ int64_s v = const64_from_current_expr ();
+ return (long) v.low;
+
+}
+
+static long const_from_current_case_expr (void) {
+
+ enum token_kind kill[7];
+ int64_s v;
+
+ if (token_is_sizeof_keyword ()) {
+
+ v = sizeof_from_current_token ();
+ return (long) v.low;
+
+ }
+
+ kill[0] = TOK_COLON;
+ kill[1] = TOK_SEMI;
+ kill[2] = TOK_COMMA;
+ kill[3] = TOK_RPAREN;
+ kill[4] = TOK_RBRACK;
+ kill[5] = TOK_RBRACE;
+ kill[6] = 0;
+
+ v = expr_const64 (kill);
+ return (long) v.low;
+
+}
+
+static int declarator_element_size_from_fields (int base_size, const int *field_sizes, int field_count);
+
+static int declarator_pointed_size_now (void) {
+
+ if (declarator_pointer_depth > 1) {
+ return DATA_PTR;
+ }
+
+ if (parsed_type_is_aggregate) {
+ return parsed_type_size;
+ }
+
+ return parsed_type_size & 0x1f;
+
+}
+
+static struct aggregate_tag_entry *hidden_typedef_pointer_entry_now (void) {
+
+ if (declarator_pointer_depth != 0 || declarator_is_pointer ||
+ declarator_has_array || declarator_has_function) {
+ return 0;
+ }
+
+ if ((parsed_type_size & 0x1f) != (DATA_PTR & 0x1f) ||
+ !parsed_type_tag_name[0]) {
+ return 0;
+ }
+
+ return find_aggregate_tag (parsed_type_tag_name, 0);
+
+}
+
+static int declarator_effective_pointer_depth_now (void) {
+
+ if (declarator_pointer_depth > 0) {
+ return declarator_pointer_depth;
+ }
+
+ return hidden_typedef_pointer_entry_now () ? 1 : 0;
+
+}
+
+static int declarator_effective_pointed_size_now (int base_size, const int *field_sizes, int field_count) {
+
+ struct aggregate_tag_entry *entry;
+
+ if (declarator_pointer_depth > 0 || declarator_is_pointer) {
+ return declarator_pointed_size_now ();
+ }
+
+ entry = hidden_typedef_pointer_entry_now ();
+
+ if (entry) {
+ return entry->size;
+ }
+
+ return declarator_element_size_from_fields (base_size, field_sizes, field_count);
+
+}
+
+static int declarator_element_size_from_fields (int base_size, const int *field_sizes, int field_count) {
+
+ int total = 0;
+ int i;
+
+ if (field_sizes && field_count > 0) {
+
+ for (i = 0; i < field_count; i++) {
+ total += field_sizes[i] & 0x1f;
+ }
+
+ if (total > 0) {
+ return total;
+ }
+
+ }
+
+ if (parsed_type_is_aggregate) {
+ return base_size;
+ }
+
+ return base_size & 0x1f;
+
+}
+
+static int declarator_object_size (int base_size) {
+
+ if (declarator_has_array) {
+
+ int elem_size = declarator_is_pointer ? DATA_PTR : base_size;
+
+ if (declarator_array_count <= 0) {
+ return elem_size;
+ }
+
+ return elem_size * declarator_array_count;
+
+ }
+
+ if (declarator_is_pointer) {
+ return DATA_PTR;
+ }
+
+ return base_size;
+
+}
+
+static int count_brace_initializer_elements_from_assign_now (void) {
+
+ const char *p;
+
+ int brace_depth = 0;
+ int paren_depth = 0;
+ int bracket_depth = 0;
+ int count = 0;
+ int saw_element = 0;
+
+ if (tok.kind != TOK_ASSIGN || !tok.start) {
+ return 0;
+ }
+
+ p = tok.start;
+
+ while (*p && *p != '=') {
+ p++;
+ }
+
+ if (*p != '=') {
+ return 0;
+ }
+
+ p++;
+
+ while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+ p++;
+ }
+
+ if (*p != '{') {
+ return 0;
+ }
+
+ p++;
+ brace_depth = 1;
+
+ while (*p && brace_depth > 0) {
+
+ if (*p == '\'' || *p == '"') {
+
+ int quote = *p++;
+ saw_element = 1;
+
+ while (*p && *p != quote) {
+
+ if (*p == '\\' && p[1]) {
+ p += 2;
+ } else {
+ p++;
+ }
+
+ }
+
+ if (*p == quote) {
+ p++;
+ }
+
+ continue;
+
+ }
+
+ if (*p == '{') {
+
+ brace_depth++;
+ saw_element = 1;
+
+ } else if (*p == '}') {
+
+ if (brace_depth == 1 && paren_depth == 0 && bracket_depth == 0) {
+ break;
+ }
+
+ brace_depth--;
+
+ } else if (*p == '(') {
+
+ paren_depth++;
+ saw_element = 1;
+
+ } else if (*p == ')') {
+
+ if (paren_depth > 0) {
+ paren_depth--;
+ }
+
+ } else if (*p == '[') {
+
+ bracket_depth++;
+ saw_element = 1;
+
+ } else if (*p == ']') {
+
+ if (bracket_depth > 0) {
+ bracket_depth--;
+ }
+
+ } else if (*p == ',' && brace_depth == 1 && paren_depth == 0 && bracket_depth == 0) {
+
+ if (saw_element) {
+
+ count++;
+ saw_element = 0;
+
+ }
+
+ } else if (!(*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n')) {
+ saw_element = 1;
+ }
+
+ p++;
+
+ }
+
+ if (saw_element) {
+ count++;
+ }
+
+ return count;
+
+}
+
+static int parse_sizeof_member_expr_size (int leading_stars, int *out_size) {
+
+ int size = DATA_INT & 0x1f;
+ int pointer_depth = 0;
+ int pointed_size = 0;
+ int is_array = 0;
+ int final_pointer_depth = 0;
+ int final_pointed_size = 0;
+ int final_is_array = 0;
+
+ struct local_symbol *local;
+
+ if (tok.kind != TOK_IDENT) {
+ return 0;
+ }
+
+ local = find_local_symbol (tok.ident);
+
+ if (local) {
+
+ size = local->size;
+
+ pointer_depth = local->pointer_depth;
+ pointed_size = local->pointed_size;
+
+ is_array = local->is_array;
+
+ } else if (find_global_symbol (tok.ident) >= 0) {
+
+ size = get_global_symbol_size (tok.ident);
+
+ pointer_depth = get_global_symbol_pointer_depth (tok.ident);
+ pointed_size = get_global_symbol_pointed_size (tok.ident);
+
+ is_array = get_global_symbol_array (tok.ident);
+
+ } else {
+ report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "unknown symbol '%s'", tok.ident ? tok.ident : "");
+ }
+
+ final_pointer_depth = pointer_depth;
+ final_pointed_size = pointed_size;
+ final_is_array = is_array;
+
+ get_token ();
+
+ while (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) {
+
+ int member_pointer_depth = 0;
+ int member_pointed_size = 0;
+ int member_offset = 0;
+ int member_size = 0;
+ int member_is_array = 0;
+
+ const char *member_tag_name = 0;
+ enum token_kind member_op = tok.kind;
+
+ get_token ();
+
+ if (tok.kind != TOK_IDENT) {
+
+ report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret,
+ "expected member name after %s", member_op == TOK_ARROW ? "->" : ".");
+ break;
+
+ }
+
+ if (find_member_info_ex (tok.ident, &member_offset, &member_size,
+ &member_pointed_size, &member_pointer_depth,
+ &member_is_array, 0)) {
+
+ member_tag_name = last_found_member_tag_name ? last_found_member_tag_name : find_member_tag_name (tok.ident);
+ size = member_size;
+
+ final_pointer_depth = member_pointer_depth;
+ final_pointed_size = member_pointed_size;
+
+ final_is_array = member_is_array;
+
+ if (final_pointer_depth > 0 && member_tag_name) {
+
+ struct aggregate_tag_entry *entry = find_aggregate_tag (member_tag_name, 0);
+
+ if (entry) {
+ final_pointed_size = entry->size;
+ }
+
+ }
+
+ } else {
+
+ report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret,
+ "unknown member '%s'", tok.ident ? tok.ident : "");
+
+ }
+
+ get_token ();
+
+ }
+
+ while (tok.kind == TOK_LBRACK) {
+
+ int depth = 1;
+ get_token ();
+
+ while (tok.kind != TOK_EOF && depth > 0) {
+
+ if (tok.kind == TOK_LBRACK) {
+ depth++;
+ } else if (tok.kind == TOK_RBRACK) {
+
+ depth--;
+
+ if (depth == 0) {
+ break;
+ }
+
+ }
+
+ get_token ();
+
+ }
+
+ if (tok.kind == TOK_RBRACK) {
+ get_token ();
+ }
+
+ if (final_pointer_depth > 0) {
+
+ size = final_pointer_depth > 1 ? DATA_PTR : final_pointed_size;
+ final_pointer_depth--;
+
+ } else if (final_is_array && final_pointed_size > 0) {
+ size = final_pointed_size;
+ }
+
+ final_is_array = 0;
+
+ }
+
+ if (leading_stars > 0 && final_is_array && final_pointer_depth == 0) {
+
+ /*
+ * In expressions, an array object decays to a pointer to its first
+ * element. sizeof(*array) must therefore return the element size,
+ * not the total array object size. This matters for tables such as
+ * static const struct builtin_macro builtin[] where sizeof(*builtin)
+ * is used to compute the element count.
+ */
+ if (final_pointed_size > 0) {
+ size = final_pointed_size;
+ }
+
+ } else if (leading_stars > 0 && final_pointer_depth >= leading_stars) {
+
+ int remaining_depth = final_pointer_depth - leading_stars;
+ size = remaining_depth > 0 ? DATA_PTR : final_pointed_size;
+
+ }
+
+ if (size < 1) {
+ size = DATA_INT & 0x1f;
+ }
+
+ *out_size = size;
+ return 1;
+
+}
+
+int parse_sizeof_value (void) {
+
+ int saved_type_size = parsed_type_size;
+ int saved_storage_class = parsed_storage_class;
+ int saved_is_aggregate = parsed_type_is_aggregate;
+ int saved_is_void = parsed_type_is_void;
+ int saved_is_unsigned = parsed_type_is_unsigned;
+ int saved_is_floating = parsed_type_is_floating;
+ int saved_has_tag = parsed_type_has_tag;
+ int saved_is_inline = parsed_type_is_inline;
+ int saved_field_count = parsed_field_count;
+ int saved_fields[MAX_AGG_FIELDS];
+
+ int saved_declarator_is_pointer = declarator_is_pointer;
+ int saved_declarator_has_array = declarator_has_array;
+ int saved_declarator_has_function = declarator_has_function;
+ int saved_declarator_array_unsized = declarator_array_unsized;
+ long saved_declarator_array_count = declarator_array_count;
+
+ int size = DATA_INT & 0x1f;
+ int i;
+
+ for (i = 0; i < saved_field_count && i < MAX_AGG_FIELDS; i++) {
+ saved_fields[i] = parsed_field_sizes[i];
+ }
+
+ if (_accept (TOK_LPAREN)) {
+
+ if (is_type_start (tok.kind)) {
+
+ char *name = 0;
+ int base_size;
+
+ parse_type_spec ();
+ base_size = parsed_type_size;
+
+ /*
+ * sizeof(type-name) may not contain a declarator, for example
+ * sizeof(FILE). parse_declarator() normally clears these flags,
+ * but when the next token is ')' it is not called. Do not let a
+ * previous declaration such as FILE **__gtin(void) make this
+ * type-name look like a pointer and collapse the size to 4.
+ */
+ declarator_is_pointer = 0;
+ declarator_pointer_depth = 0;
+ declarator_has_array = 0;
+ declarator_has_function = 0;
+ declarator_function_is_pointer = 0;
+ declarator_function_param_count = 0;
+ declarator_function_has_prototype = 0;
+ declarator_function_is_variadic = 0;
+ declarator_array_unsized = 0;
+ declarator_array_count = 1;
+ declarator_last_array_count = 1;
+
+ if (tok.kind != TOK_RPAREN) {
+ parse_declarator (&name);
+ }
+
+ size = declarator_object_size (base_size);
+
+ if (name) {
+ free (name);
+ }
+
+ expect (TOK_RPAREN, ")");
+
+ } else if (is_string_token ()) {
+
+ int64_s values[MAX_STRING_INIT_BYTES];
+ int value_count = 0;
+
+ parse_string_initializer_values (values, MAX_STRING_INIT_BYTES, &value_count);
+ size = value_count;
+ expect (TOK_RPAREN, ")");
+
+ } else {
+
+ int depth = 1;
+ int first = 1;
+ int leading_stars = 0;
+
+ while (tok.kind != TOK_EOF && depth > 0) {
+
+ if (first) {
+
+ while (tok.kind == TOK_STAR || tok.kind == TOK_AMPER || tok.kind == TOK_PLUS || tok.kind == TOK_MINUS) {
+
+ if (tok.kind == TOK_STAR) {
+ leading_stars++;
+ }
+
+ get_token ();
+
+ }
+
+ }
+
+ if (first && tok.kind == TOK_IDENT) {
+
+ parse_sizeof_member_expr_size (leading_stars, &size);
+
+ first = 0;
+ continue;
+
+ }
+
+ if (first && tok.kind == TOK_LPAREN && leading_stars > 0) {
+
+ get_token ();
+
+ if (tok.kind == TOK_IDENT) {
+ parse_sizeof_member_expr_size (leading_stars, &size);
+ }
+
+ while (tok.kind != TOK_EOF && tok.kind != TOK_RPAREN) {
+ get_token ();
+ }
+
+ if (tok.kind == TOK_RPAREN) {
+ get_token ();
+ }
+
+ first = 0;
+ continue;
+
+ }
+
+ first = 0;
+
+ if (tok.kind == TOK_LPAREN) {
+ depth++;
+ } else if (tok.kind == TOK_RPAREN) {
+
+ depth--;
+
+ if (depth == 0) {
+ break;
+ }
+
+ }
+
+ get_token ();
+
+ }
+
+ expect (TOK_RPAREN, ")");
+
+ }
+
+ } else {
+
+ int leading_stars = 0;
+
+ while (tok.kind == TOK_STAR || tok.kind == TOK_AMPER || tok.kind == TOK_PLUS || tok.kind == TOK_MINUS) {
+
+ if (tok.kind == TOK_STAR) {
+ leading_stars++;
+ }
+
+ get_token ();
+
+ }
+
+ if (is_string_token ()) {
+
+ int64_s values[MAX_STRING_INIT_BYTES];
+ int value_count = 0;
+
+ parse_string_initializer_values (values, MAX_STRING_INIT_BYTES, &value_count);
+ size = value_count;
+
+ } else if (tok.kind == TOK_IDENT) {
+ parse_sizeof_member_expr_size (leading_stars, &size);
+ } else if (tok.kind == TOK_LPAREN) {
+
+ int depth = 1;
+
+ get_token ();
+
+ while (tok.kind != TOK_EOF && depth > 0) {
+
+ if (tok.kind == TOK_LPAREN) {
+ depth++;
+ } else if (tok.kind == TOK_RPAREN) {
+
+ depth--;
+
+ if (depth == 0) {
+ break;
+ }
+
+ }
+
+ get_token ();
+
+ }
+
+ expect (TOK_RPAREN, ")");
+
+ } else if (tok.kind != TOK_EOF) {
+ get_token ();
+ }
+
+ while (tok.kind == TOK_LBRACK || tok.kind == TOK_LPAREN) {
+
+ enum token_kind close = (tok.kind == TOK_LBRACK) ? TOK_RBRACK : TOK_RPAREN;
+ int depth = 1;
+
+ get_token ();
+
+ while (tok.kind != TOK_EOF && depth > 0) {
+
+ if ((close == TOK_RBRACK && tok.kind == TOK_LBRACK) || (close == TOK_RPAREN && tok.kind == TOK_LPAREN)) {
+ depth++;
+ } else if (tok.kind == close) {
+
+ depth--;
+
+ if (depth == 0) {
+ break;
+ }
+
+ }
+
+ get_token ();
+
+ }
+
+ expect (close, (close == TOK_RBRACK) ? "]" : ")");
+
+ }
+
+ }
+
+ parsed_type_size = saved_type_size;
+ parsed_storage_class = saved_storage_class;
+ parsed_type_is_aggregate = saved_is_aggregate;
+ parsed_type_is_void = saved_is_void;
+ parsed_type_is_unsigned = saved_is_unsigned;
+ parsed_type_is_floating = saved_is_floating;
+ parsed_type_has_tag = saved_has_tag;
+ parsed_type_is_inline = saved_is_inline;
+
+ clear_parsed_fields ();
+
+ for (i = 0; i < saved_field_count && i < MAX_AGG_FIELDS; i++) {
+ append_parsed_field (saved_fields[i]);
+ }
+
+ declarator_is_pointer = saved_declarator_is_pointer;
+ declarator_has_array = saved_declarator_has_array;
+ declarator_has_function = saved_declarator_has_function;
+ declarator_array_unsized = saved_declarator_array_unsized;
+ declarator_array_count = saved_declarator_array_count;
+
+ if (size < 1) {
+ size = DATA_INT & 0x1f;
+ }
+
+ return size;
+
+}
+
+static void make_declarator_fields (int *out_fields, int *out_count, const int *base_fields, int base_count, int base_size, int base_is_aggregate) {
+
+ int i;
+ long r;
+
+ *out_count = 0;
+
+ if (declarator_is_pointer) {
+
+ out_fields[(*out_count)++] = DATA_PTR;
+ return;
+
+ }
+
+ if (declarator_has_array) {
+
+ if (base_is_aggregate && base_count > 0) {
+
+ for (r = 0; r < declarator_array_count && *out_count < MAX_AGG_FIELDS; r++) {
+
+ for (i = 0; i < base_count && *out_count < MAX_AGG_FIELDS; i++) {
+ out_fields[(*out_count)++] = base_fields[i];
+ }
+
+ }
+
+ } else {
+
+ for (r = 0; r < declarator_array_count && *out_count < MAX_AGG_FIELDS; r++) {
+ out_fields[(*out_count)++] = (base_size & 0x1f);
+ }
+
+ }
+
+ return;
+
+ }
+
+ for (i = 0; i < base_count && *out_count < MAX_AGG_FIELDS; i++) {
+ out_fields[(*out_count)++] = base_fields[i];
+ }
+
+ if (*out_count == 0) {
+ out_fields[(*out_count)++] = (base_size & 0x1f);
+ }
+
+}
+
+static void parse_statement (void);
+
+static void emit_load_assignment_rhs_expression_to_pair (const char *lo, const char *hi, int is_unsigned);
+static void emit_load_assignment_rhs_expression_to_reg (const char *reg);
+
+static void emit_incdec_symbol_now (struct local_symbol *sym, const char *name, enum token_kind op, int line, const char *start, const char *caret);
+
+static int initializer_contains_runtime_call_now (void) {
+
+ const char *p = tok.start;
+
+ int paren_depth = 0;
+ int bracket_depth = 0;
+ int brace_depth = 0;
+
+ while (*p) {
+
+ if (paren_depth == 0 && bracket_depth == 0 && brace_depth == 0 && (*p == ';' || *p == ',' || *p == '}')) {
+ break;
+ }
+
+ if ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || *p == '_') {
+
+ const char *q = p + 1;
+
+ while ((*q >= 'A' && *q <= 'Z') || (*q >= 'a' && *q <= 'z') || (*q >= '0' && *q <= '9') || *q == '_') {
+ q++;
+ }
+
+ while (*q == ' ' || *q == '\t' || *q == '\r' || *q == '\n') {
+ q++;
+ }
+
+ if (*q == '(') {
+ return 1;
+ }
+
+ p = q;
+ continue;
+
+ }
+
+ if (*p == '\'' || *p == '"') {
+
+ int quote = *p++;
+
+ while (*p && *p != quote) {
+
+ if (*p == '\\' && p[1]) {
+ p += 2;
+ } else {
+ p++;
+ }
+
+ }
+
+ if (*p == quote) {
+ p++;
+ }
+
+ continue;
+
+ }
+
+ if (*p == '(') {
+ paren_depth++;
+ } else if (*p == ')') {
+
+ if (paren_depth > 0) {
+ paren_depth--;
+ }
+
+ } else if (*p == '[') {
+ bracket_depth++;
+ } else if (*p == ']') {
+
+ if (bracket_depth > 0) {
+ bracket_depth--;
+ }
+
+ } else if (*p == '{') {
+ brace_depth++;
+ } else if (*p == '}') {
+
+ if (brace_depth > 0) {
+ brace_depth--;
+ }
+
+ }
+
+ p++;
+
+ }
+
+ return 0;
+
+}
+
+static int auto_init_ident_char_now (int ch) {
+ return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || ch == '_';
+}
+
+static int auto_init_expr_continues_after_ident_now (void) {
+
+ const char *p;
+ const char *q;
+
+ if (tok.kind != TOK_IDENT) {
+ return 1;
+ }
+
+ p = tok.start;
+ q = p;
+
+ while (auto_init_ident_char_now ((unsigned char) *q)) {
+ q++;
+ }
+
+ while (*q == ' ' || *q == '\t' || *q == '\r' || *q == '\n') {
+ q++;
+ }
+
+ return !(*q == ';' || *q == ',' || *q == '}');
+
+}
+
+static int auto_initializer_needs_runtime_now (void) {
+
+ const char *p = tok.start;
+ int paren_depth = 0;
+ int bracket_depth = 0;
+ int brace_depth = 0;
+
+ if (initializer_contains_runtime_call_now () || tok.kind == TOK_LPAREN) {
+ return 1;
+ }
+
+ if (tok.kind == TOK_IDENT) {
+ return auto_init_expr_continues_after_ident_now ();
+ }
+
+ if (tok.kind == TOK_AMPER) {
+
+ p++;
+
+ while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+ p++;
+ }
+
+ if (!((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || *p == '_')) {
+ return 1;
+ }
+
+ while (auto_init_ident_char_now ((unsigned char) *p)) {
+ p++;
+ }
+
+ while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+ p++;
+ }
+
+ return !(*p == ';' || *p == ',' || *p == '}');
+
+ }
+
+ p = tok.start;
+
+ while (*p) {
+
+ if (paren_depth == 0 && bracket_depth == 0 && brace_depth == 0 && (*p == ';' || *p == ',' || *p == '}')) {
+ break;
+ }
+
+ if (*p == '[' || *p == ']' || *p == '*') {
+ return 1;
+ }
+
+ if (*p == '(') {
+ paren_depth++;
+ } else if (*p == ')') {
+ if (paren_depth > 0) paren_depth--;
+ } else if (*p == '[') {
+ bracket_depth++;
+ } else if (*p == ']') {
+ if (bracket_depth > 0) bracket_depth--;
+ } else if (*p == '{') {
+ brace_depth++;
+ } else if (*p == '}') {
+ if (brace_depth > 0) brace_depth--;
+ }
+
+ if (((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || *p == '_') && p[0] != 'L') {
+ return 1;
+ }
+
+ p++;
+
+ }
+
+ return 0;
+
+}
+
+static void parse_local_initializer_value (struct local_init *init) {
+
+ init->kind = LOCAL_INIT_CONST;
+ init->symbol = 0;
+ init->value.low = 0;
+ init->value.high = 0;
+ init->source_offset = 0;
+ init->source_size = 0;
+
+ if (tok.kind == TOK_LBRACE) {
+
+ get_token ();
+ parse_local_initializer_value (init);
+
+ while (_accept (TOK_COMMA)) {
+ skip_initializer ();
+ }
+
+ expect (TOK_RBRACE, "}");
+ return;
+
+ }
+
+ if (tok.kind == TOK_AMPER) {
+
+ get_token ();
+
+ if (tok.kind != TOK_IDENT) {
+
+ report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "expected identifier after '&'");
+ return;
+
+ }
+
+ init->kind = LOCAL_INIT_ADDRESS;
+ init->symbol = xstrdup (tok.ident);
+
+ get_token ();
+ return;
+
+ }
+
+ if (tok.kind == TOK_IDENT) {
+
+ struct local_symbol *src = find_local_symbol (tok.ident);
+
+ if (src) {
+
+ if (src->is_static && src->static_label) {
+
+ init->kind = LOCAL_INIT_GLOBAL;
+ init->symbol = xstrdup (src->static_label);
+
+ } else {
+
+ init->kind = LOCAL_INIT_STACK;
+
+ init->source_offset = src->offset;
+ init->source_size = src->size;
+
+ }
+
+ get_token ();
+ return;
+
+ }
+
+ if (find_global_symbol (tok.ident) >= 0) {
+
+ init->kind = LOCAL_INIT_GLOBAL;
+ init->symbol = xstrdup (tok.ident);
+
+ get_token ();
+ return;
+
+ }
+
+ }
+
+ init->value = const64_from_current_expr ();
+
+}
+
+static void parse_local_string_field_initializer (struct local_init *inits, int *init_count, int max_inits, long base_offset, int field_size) {
+
+ int64_s values[MAX_STRING_INIT_BYTES];
+ int value_count = 0, i;
+
+ parse_string_initializer_values (values, MAX_STRING_INIT_BYTES, &value_count);
+
+ for (i = 0; i < field_size; i++) {
+
+ if (*init_count >= max_inits) {
+ continue;
+ }
+
+ inits[*init_count].offset = base_offset + i;
+ inits[*init_count].size = 1;
+ inits[*init_count].kind = LOCAL_INIT_CONST;
+ inits[*init_count].symbol = 0;
+ inits[*init_count].value.low = (i < value_count) ? (values[i].low & 0xffUL) : 0;
+ inits[*init_count].value.high = 0;
+ inits[*init_count].source_offset = 0;
+ inits[*init_count].source_size = 0;
+
+ (*init_count)++;
+
+ }
+
+}
+
+static void parse_local_aggregate_initializer_values (struct local_init *inits, int *init_count, int max_inits, long base_offset, const int *fields, int field_count) {
+
+ int field_index = 0;
+ long field_offset = 0;
+ int braced = 0;
+
+ if (tok.kind == TOK_LBRACE) {
+ braced = 1;
+ get_token ();
+ }
+
+ while (field_index < field_count) {
+
+ int field_size = fields[field_index];
+
+ if (field_size < 0) {
+
+ field_offset += -field_size;
+
+ field_index++;
+ continue;
+
+ }
+
+ if (braced && tok.kind == TOK_RBRACE) {
+
+ if (*init_count < max_inits) {
+
+ inits[*init_count].offset = base_offset + field_offset;
+ inits[*init_count].size = field_size;
+ inits[*init_count].kind = LOCAL_INIT_CONST;
+ inits[*init_count].symbol = 0;
+ inits[*init_count].value.low = 0;
+ inits[*init_count].value.high = 0;
+ inits[*init_count].source_offset = 0;
+ inits[*init_count].source_size = 0;
+
+ (*init_count)++;
+
+ }
+
+ field_offset += field_size;
+ field_index++;
+
+ continue;
+
+ }
+
+ if (*init_count < max_inits) {
+
+ if (is_string_token ()) {
+ parse_local_string_field_initializer (inits, init_count, max_inits, base_offset + field_offset, field_size);
+ } else {
+
+ inits[*init_count].offset = base_offset + field_offset;
+ inits[*init_count].size = field_size;
+
+ parse_local_initializer_value (&inits[*init_count]);
+ (*init_count)++;
+
+ }
+
+ } else {
+ skip_initializer ();
+ }
+
+ field_offset += field_size;
+ field_index++;
+
+ if (braced) {
+
+ if (!_accept (TOK_COMMA)) {
+ break;
+ }
+
+ if (tok.kind == TOK_RBRACE) {
+ break;
+ }
+
+ } else {
+ break;
+ }
+
+ }
+
+ if (braced) {
+
+ while (field_index < field_count) {
+
+ int field_size = fields[field_index];
+
+ if (field_size < 0) {
+
+ field_offset += -field_size;
+
+ field_index++;
+ continue;
+
+ }
+
+ if (*init_count < max_inits) {
+
+ inits[*init_count].offset = base_offset + field_offset;
+ inits[*init_count].size = field_size;
+ inits[*init_count].kind = LOCAL_INIT_CONST;
+ inits[*init_count].symbol = 0;
+ inits[*init_count].value.low = 0;
+ inits[*init_count].value.high = 0;
+ inits[*init_count].source_offset = 0;
+ inits[*init_count].source_size = 0;
+
+ (*init_count)++;
+
+ }
+
+ field_offset += field_size;
+ field_index++;
+
+ }
+
+ while (tok.kind != TOK_EOF && tok.kind != TOK_RBRACE) {
+
+ skip_initializer ();
+
+ if (!_accept (TOK_COMMA)) {
+ break;
+ }
+ }
+
+ expect (TOK_RBRACE, "}");
+
+ }
+
+}
+
+static void make_local_static_label (char *buf, size_t bufsz, const char *name) {
+
+ if (!buf || bufsz == 0) {
+ return;
+ }
+
+ if (!name) {
+ name = "anon";
+ }
+
+ if ((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) {
+ sprintf (buf, "LC%d_%s", local_static_id++, name);
+ } else {
+ sprintf (buf, ".LC%d_%s", local_static_id++, name);
+ }
+
+}
+
+static void emit_global_object (const char *name, int size, int is_array, long array_count, const int *field_sizes, int field_count, const int64_s *values, char **symbols, int value_count, int is_aggregate, int is_static);
+static void emit_extern_symbol (const char *name, int size, int is_function);
+static void emit_extern_reference_symbol (const char *name, int size);
+
+static void emit_block_static_object (const char *label, int size, int is_array, long array_count, const int *field_sizes, int field_count, const int64_s *values, char **symbols, int value_count, int is_aggregate) {
+
+ emit_global_object (label, size, is_array, array_count, field_sizes, field_count, values, symbols, value_count, is_aggregate, 1);
+
+ /**
+ * We may have switched to .data/.data? to emit the static object while
+ * parsing a function body. The following statements still belong in
+ * .code/.text.
+ */
+ switch_section (SECTION_TEXT);
+
+}
+
+static char *emit_string_literal_global (void);
+static int is_string_token (void);
+
+static void ensure_block_stack_allocated (long block_stack_start, long *block_stack_bytes, int *block_stack_emitted) {
+
+ long needed_stack_bytes = align_up_long (current_local_stack_size - block_stack_start, 4);
+
+ /*
+ * The parser can emit code while it is still inside a declaration list
+ * containing comma-separated declarators and runtime initializers. Keep
+ * one word of conservative headroom once a block frame exists, so a
+ * subsequently-discovered automatic slot in the same declaration phase is
+ * not allowed to overlap the caller frame before the next adjustment is
+ * emitted. This fixes cases like:
+ *
+ * cpp_mffc *old = reader->mffc;
+ * struct if_stack *ifs, *next_ifs;
+ * ... next_ifs = ifs->next;
+ *
+ * where generated code used -12(%ebp) after reserving only 8 bytes.
+ */
+ if (needed_stack_bytes > 0) {
+ needed_stack_bytes = align_up_long (needed_stack_bytes + 4, 4);
+ }
+
+ if (!block_stack_bytes || !block_stack_emitted) {
+ return;
+ }
+
+ if (!*block_stack_emitted) {
+
+ *block_stack_bytes = needed_stack_bytes;
+ emit_stack_adjust (*block_stack_bytes, 1);
+
+ if (current_parse_block_depth > 1) {
+ current_block_cleanup_bytes += *block_stack_bytes;
+ }
+
+ *block_stack_emitted = (*block_stack_bytes > 0);
+
+ } else if (needed_stack_bytes > *block_stack_bytes) {
+
+ long extra_stack_bytes = needed_stack_bytes - *block_stack_bytes;
+ emit_stack_adjust (extra_stack_bytes, 1);
+
+ if (current_parse_block_depth > 1) {
+ current_block_cleanup_bytes += extra_stack_bytes;
+ }
+
+ *block_stack_bytes = needed_stack_bytes;
+
+ }
+
+}
+
+static void parse_global_initializer_values (int64_s *values, char **symbols, int max_values, int *count);
+static void parse_string_initializer_values (int64_s *values, int max_values, int *count);
+
+static int aggregate_initializer_value_field_count (const int *field_sizes, int field_count);
+static void parse_global_initializer_values_padded_elements (int64_s *values, char **symbols, int max_values, int *count, int element_field_count);
+
+static void parse_char_array_initializer_values (int64_s *values, int max_values, int *count, long row_width);
+static int64_s parse_floating_const_expr_bits_now (int size);
+
+static void emit_store_pair_to_local64 (long offset, const char *lo, const char *hi);
+static void emit_store_reg_to_local (long offset, int size, const char *reg);
+
+static void emit_load_local_address_to_reg_now (const char *reg, long offset);
+static int emit_aggregate_copy_from_current_rhs_to_addr_reg_now (const char *addr_reg, int offset, int size);
+
+static void parse_block (void) {
+
+ int declaration_phase = 1, is_function_body = (current_parse_block_depth == 0);
+
+ int block_local_start = local_symbol_count;
+ int block_stack_emitted = 0;
+
+ long block_stack_start = current_local_stack_size;
+ long block_stack_bytes = 0;
+
+ struct local_init inits[MAX_LOCAL_INITS];
+ int block_scope_start, init_count = 0;
+
+ block_scope_start = is_function_body ? 0 : block_local_start;
+ current_parse_block_depth++;;
+
+ expect (TOK_LBRACE, "{");
+
+ while (tok.kind != TOK_EOF && tok.kind != TOK_RBRACE) {
+
+ if (is_type_start (tok.kind)) {
+
+ if (!declaration_phase) {
+
+ if (state->std == 90) {
+ report_line_at (get_filename (), get_line_number (), (state->pedantic ? REPORT_ERROR : REPORT_WARNING), tok.start, tok.caret, "ISO C90 forbids mixed declarations and code");
+ }
+
+ }
+
+ parse_type_spec ();
+
+ for (;;) {
+
+ long object_offset = 0;
+
+ int object_init_size = 0;
+ int object_is_auto = 0;
+
+ char *name = 0;
+ unsigned long name_line;
+
+ const char *name_start, *name_caret;
+ int object_align, object_size = 0;
+
+ int object_fields[MAX_AGG_FIELDS];
+ int object_field_count = 0;
+ int init_value_count = 0;
+
+ int64_s init_values[MAX_AGG_FIELDS];
+ int i;
+
+ char *init_symbols[MAX_AGG_FIELDS];
+ char static_label[128];
+
+ for (i = 0; i < MAX_AGG_FIELDS; i++) {
+ init_symbols[i] = 0;
+ }
+
+ parse_declarator (&name);
+ apply_typedef_array_to_declarator ();
+
+ if (declarator_has_array && declarator_array_unsized && tok.kind == TOK_ASSIGN) {
+
+ int inferred_count = count_brace_initializer_elements_from_assign_now ();
+
+ if (inferred_count > 0) {
+ declarator_array_count = inferred_count;
+ }
+
+ }
+
+ name_line = last_declarator_name_line;
+ name_start = last_declarator_name_start;
+ name_caret = last_declarator_name_caret;
+
+ make_declarator_fields (object_fields, &object_field_count, parsed_field_sizes, parsed_field_count, parsed_type_size, parsed_type_is_aggregate);
+
+ if (parsed_type_is_void && !declarator_is_pointer && !declarator_has_function) {
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "variable '%s' declared void", name ? name : "");
+ }
+
+ if (declarator_is_pointer) {
+
+ object_field_count = 1;
+ object_fields[0] = DATA_PTR;
+
+ }
+
+ if (parsed_storage_class == STORAGE_TYPEDEF && name) {
+
+ save_typedef_name (name, declarator_object_size (parsed_type_size),
+ (declarator_is_pointer ? 0 : parsed_type_is_unsigned),
+ (declarator_is_pointer ? 0 : parsed_type_is_void),
+ (!declarator_is_pointer && (parsed_type_is_aggregate || declarator_has_array)),
+ (!declarator_is_pointer && declarator_has_array),
+ declarator_array_count, parsed_type_size,
+ object_fields, object_field_count);
+
+ } else if (name && parsed_storage_class == STORAGE_EXTERN) {
+
+ if (add_global_symbol (name, (declarator_has_function && !declarator_function_is_pointer) ? GLOBAL_SYMBOL_FUNCTION : GLOBAL_SYMBOL_OBJECT, 1, name_start, name_caret, name_line)) {
+
+ set_global_symbol_size (name, (declarator_has_function || declarator_is_pointer) ? DATA_PTR : declarator_object_size (parsed_type_size));
+ set_global_symbol_pointer_info (name, declarator_effective_pointer_depth_now (),
+ declarator_effective_pointed_size_now (parsed_type_size, object_fields, object_field_count));
+ set_global_symbol_tag_name (name, parsed_type_tag_name);
+ set_global_symbol_unsigned (name, (declarator_is_pointer || declarator_has_function) ? 0 : parsed_type_is_unsigned);
+ set_global_symbol_floating (name, (declarator_is_pointer || declarator_has_function) ? 0 : parsed_type_is_floating);
+ set_global_symbol_returns_void (name, declarator_has_function && parsed_type_is_void && !declarator_is_pointer && !declarator_function_is_pointer);
+
+ if (declarator_has_function) {
+ set_global_symbol_param_count (name, declarator_function_param_count, declarator_function_has_prototype || declarator_function_param_count > 0, declarator_function_is_variadic);
+ }
+
+ switch_section (SECTION_TEXT);
+
+ }
+
+ } else if (name && parsed_storage_class != STORAGE_STATIC) {
+
+ object_size = declarator_object_size (parsed_type_size);
+ object_align = declarator_is_pointer ? type_alignment (DATA_PTR) : type_alignment (parsed_type_is_aggregate ? DATA_PTR : parsed_type_size);
+
+ object_offset = add_local_symbol (name, object_size, object_align,
+ (declarator_is_pointer ? 0 : parsed_type_is_unsigned),
+ block_scope_start, name_line, name_start, name_caret);
+
+ /*
+ * Reserve stack space as soon as an automatic local receives
+ * an EBP-relative slot. Some blocks start with declarations
+ * and then immediately execute statements that write those
+ * locals, for example:
+ *
+ * if (...) { location_t loc; unsigned int i; loc.file = ...; }
+ *
+ * Waiting until the declaration phase is exited proved too
+ * fragile after runtime initializer handling was added: code
+ * could use -4/-8/-12(%ebp) before any sub esp had been
+ * emitted. Keep block_stack_bytes in sync here so the later
+ * declaration-phase transition is a no-op unless a later
+ * unsized aggregate expands the frame.
+ */
+ ensure_block_stack_allocated (block_stack_start, &block_stack_bytes, &block_stack_emitted);
+
+ set_local_symbol_floating (name, declarator_is_pointer ? 0 : parsed_type_is_floating);
+ set_local_symbol_array (name, declarator_has_array);
+ set_local_symbol_array_element_size (name, declarator_has_array ? (int)(object_size / (declarator_array_count > 0 ? declarator_array_count : 1)) : 0);
+ set_local_symbol_pointer_info (name, declarator_effective_pointer_depth_now (),
+ declarator_effective_pointed_size_now (parsed_type_size, object_fields, object_field_count));
+
+ if (!declarator_is_pointer && parsed_type_tag_name[0]) {
+
+ struct local_symbol *lsym = find_local_symbol (name);
+
+ if (lsym) {
+ lsym->tag_name = xstrdup (parsed_type_tag_name);
+ }
+
+ }
+
+ object_init_size = declarator_is_pointer ? DATA_PTR : parsed_type_size;
+ object_is_auto = 1;
+
+ /*
+ * If an earlier automatic declaration in this block had a
+ * runtime initializer, stack space has already been emitted.
+ * Any later automatic declaration extends current_local_stack_size,
+ * and its initializer may immediately store to the new slot.
+ * Reserve the extra bytes before parsing/emitting that initializer;
+ * otherwise code can write to -8(%ebp), -12(%ebp), etc. after
+ * only the first 4 bytes were allocated.
+ */
+ if (block_stack_emitted) {
+ ensure_block_stack_allocated (block_stack_start, &block_stack_bytes, &block_stack_emitted);
+ }
+
+ }
+
+ if (_accept (TOK_ASSIGN)) {
+
+ /*
+ * Reserve the stack slot before parsing an automatic initializer.
+ *
+ * Some initializer forms which are not compile-time constants
+ * can still reach code emission through parse_local_initializer_value().
+ * If the stack adjustment is delayed until the first following
+ * statement, those stores use offsets that have not yet been
+ * reserved. In create_definition(), this affected:
+ *
+ * cpp_token *saved_cur_token = reader->cur_token;
+ *
+ * The generated code stored to -64(%ebp) while only -60 bytes
+ * had been allocated, so the following call overwrote the saved
+ * value and reader->cur_token was restored with garbage.
+ */
+ if (object_is_auto) {
+ ensure_block_stack_allocated (block_stack_start, &block_stack_bytes, &block_stack_emitted);
+ }
+
+ if (parsed_storage_class == STORAGE_EXTERN) {
+
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "extern declaration '%s' is initialized", name ? name : "");
+ skip_initializer ();
+
+ } else if (parsed_storage_class == STORAGE_STATIC && name) {
+
+ if (declarator_has_array && !declarator_is_pointer && (parsed_type_size & 0x1f) == (DATA_CHAR & 0x1f) && (is_string_token () || tok.kind == TOK_LBRACE)) {
+
+ if (is_string_token ()) {
+ parse_string_initializer_values (init_values, MAX_AGG_FIELDS, &init_value_count);
+ } else {
+ parse_char_array_initializer_values (init_values, MAX_AGG_FIELDS, &init_value_count, declarator_last_array_count);
+ }
+
+ if (declarator_array_unsized) {
+ declarator_array_count = init_value_count;
+ }
+
+ } else if (declarator_is_pointer && is_string_token ()) {
+
+ init_values[0].low = 0;
+ init_values[0].high = 0;
+
+ init_symbols[0] = emit_string_literal_global ();
+ init_value_count = 1;
+
+ } else if (!declarator_is_pointer && parsed_type_is_floating) {
+
+ init_values[0] = parse_floating_const_expr_bits_now (parsed_type_size);
+ init_symbols[0] = 0;
+ init_value_count = 1;
+
+ } else {
+
+ {
+
+ int saved_accept_symbol_addresses = global_initializer_accept_symbol_addresses;
+ global_initializer_accept_symbol_addresses = declarator_is_pointer || parsed_type_is_aggregate || declarator_has_array;
+
+ if (declarator_has_array && !declarator_is_pointer && parsed_type_is_aggregate && parsed_field_count > 0 && tok.kind == TOK_LBRACE) {
+ parse_global_initializer_values_padded_elements (init_values, init_symbols, MAX_AGG_FIELDS, &init_value_count, aggregate_initializer_value_field_count (parsed_field_sizes, parsed_field_count));
+ } else {
+ parse_global_initializer_values (init_values, init_symbols, MAX_AGG_FIELDS, &init_value_count);
+ }
+
+ global_initializer_accept_symbol_addresses = saved_accept_symbol_addresses;
+
+ }
+
+ if (declarator_has_array && declarator_array_unsized) {
+
+ if (parsed_type_is_aggregate && object_field_count > 0) {
+ declarator_array_count = (init_value_count + object_field_count - 1) / object_field_count;
+ } else {
+ declarator_array_count = init_value_count;
+ }
+
+ }
+
+ }
+
+ } else if (object_is_auto && declarator_has_array && declarator_is_pointer && tok.kind == TOK_LBRACE) {
+
+ int elem_index = 0;
+ int elem_size = DATA_PTR;
+ int first_init_index = init_count;
+
+ get_token ();
+
+ while (tok.kind != TOK_EOF && tok.kind != TOK_RBRACE) {
+
+ if (init_count < MAX_LOCAL_INITS) {
+
+ inits[init_count].offset = object_offset + (elem_index * elem_size);
+ inits[init_count].size = elem_size;
+
+ if (is_string_token ()) {
+
+ inits[init_count].kind = LOCAL_INIT_ADDRESS;
+ inits[init_count].symbol = emit_string_literal_global ();
+ inits[init_count].value.low = 0;
+ inits[init_count].value.high = 0;
+ inits[init_count].source_offset = 0;
+ inits[init_count].source_size = 0;
+
+ switch_section (SECTION_TEXT);
+
+ } else {
+ parse_local_initializer_value (&inits[init_count]);
+ }
+
+ init_count++;
+
+ } else {
+ skip_initializer ();
+ }
+
+ elem_index++;
+
+ if (!_accept (TOK_COMMA)) {
+ break;
+ }
+
+ if (tok.kind == TOK_RBRACE) {
+ break;
+ }
+
+ }
+
+ if (declarator_array_unsized && elem_index > 0 && (elem_index * elem_size) > object_size) {
+
+ long old_offset = object_offset;
+ long new_offset;
+ long delta;
+ int adj_i;
+
+ current_local_stack_size += (elem_index * elem_size) - object_size;
+ new_offset = -current_local_stack_size;
+ delta = new_offset - old_offset;
+ object_offset = new_offset;
+ object_size = elem_index * elem_size;
+
+ if (local_symbol_count > 0 && name && local_symbols[local_symbol_count - 1].name
+ && strcmp (local_symbols[local_symbol_count - 1].name, name) == 0) {
+
+ local_symbols[local_symbol_count - 1].offset = object_offset;
+ local_symbols[local_symbol_count - 1].size = object_size;
+
+ }
+
+ for (adj_i = first_init_index; adj_i < init_count; adj_i++) {
+ inits[adj_i].offset += delta;
+ }
+
+ }
+
+ expect (TOK_RBRACE, "}");
+
+ } else if (object_is_auto && !declarator_is_pointer && (parsed_type_is_aggregate || declarator_has_array) && tok.kind == TOK_LBRACE) {
+ parse_local_aggregate_initializer_values (inits, &init_count, MAX_LOCAL_INITS, object_offset, object_fields, object_field_count);
+ } else if (object_is_auto && auto_initializer_needs_runtime_now ()) {
+
+ long needed_stack_bytes = align_up_long (current_local_stack_size - block_stack_start, 4);
+
+ if (!block_stack_emitted) {
+
+ block_stack_bytes = needed_stack_bytes;
+ emit_stack_adjust (block_stack_bytes, 1);
+
+ if (!is_function_body) {
+ current_block_cleanup_bytes += block_stack_bytes;
+ }
+
+ block_stack_emitted = (block_stack_bytes > 0);
+
+ } else if (needed_stack_bytes > block_stack_bytes) {
+
+ long extra_stack_bytes = needed_stack_bytes - block_stack_bytes;
+ emit_stack_adjust (extra_stack_bytes, 1);
+
+ if (!is_function_body) {
+ current_block_cleanup_bytes += extra_stack_bytes;
+ }
+
+ block_stack_bytes = needed_stack_bytes;
+
+ }
+
+ emit_local_initializers (inits, init_count);
+ init_count = 0;
+
+ if (!declarator_is_pointer && (parsed_type_is_aggregate || declarator_has_array)) {
+
+ emit_load_local_address_to_reg_now ("edx", object_offset);
+
+ if (!emit_aggregate_copy_from_current_rhs_to_addr_reg_now ("edx", 0, object_size)) {
+
+ if (tok.kind == TOK_IDENT && token_identifier_is_function_call_rhs_now () &&
+ get_global_symbol_kind (tok.ident) == GLOBAL_SYMBOL_FUNCTION &&
+ get_global_symbol_size (tok.ident) > (DATA_LLONG & 0x1f)) {
+
+ pending_struct_return_lhs = find_local_symbol (name);
+ pending_struct_return_global_name = 0;
+
+ emit_load_assignment_rhs_expression_to_reg ("eax");
+
+ } else {
+
+ emit_load_assignment_rhs_expression_to_reg ("eax");
+ emit_store_reg_to_local (object_offset, object_init_size, "eax");
+
+ }
+
+ }
+
+ } else if (object_init_size == (DATA_LLONG & 0x1f) && !parsed_type_is_floating && !declarator_is_pointer) {
+
+ emit_load_assignment_rhs_expression_to_pair ("eax", "edx", parsed_type_is_unsigned);
+ emit_store_pair_to_local64 (object_offset, "eax", "edx");
+
+ } else {
+
+ emit_load_assignment_rhs_expression_to_reg ("eax");
+ emit_store_reg_to_local (object_offset, object_init_size, "eax");
+
+ }
+
+ } else if (object_is_auto && init_count < MAX_LOCAL_INITS) {
+
+ inits[init_count].offset = object_offset;
+ inits[init_count].size = object_init_size;
+
+ if (declarator_is_pointer && is_string_token ()) {
+
+ inits[init_count].kind = LOCAL_INIT_ADDRESS;
+ inits[init_count].symbol = emit_string_literal_global ();
+ inits[init_count].value.low = 0;
+ inits[init_count].value.high = 0;
+ inits[init_count].source_offset = 0;
+ inits[init_count].source_size = 0;
+
+ switch_section (SECTION_TEXT);
+
+ } else {
+ parse_local_initializer_value (&inits[init_count]);
+ }
+
+ init_count++;
+
+ } else {
+ skip_initializer ();
+ }
+
+ }
+
+ if (name && parsed_storage_class == STORAGE_STATIC) {
+
+ make_local_static_label (static_label, sizeof (static_label), name);
+
+ if (add_global_symbol (static_label, GLOBAL_SYMBOL_OBJECT, 0, name_start, name_caret, name_line)) {
+
+ set_global_symbol_size (static_label, declarator_is_pointer ? DATA_PTR : declarator_object_size (parsed_type_size));
+ set_global_symbol_pointer_info (static_label, declarator_pointer_depth,
+ declarator_is_pointer ? declarator_pointed_size_now () :
+ declarator_element_size_from_fields (parsed_type_size, object_fields, object_field_count));
+ set_global_symbol_unsigned (static_label, (declarator_is_pointer || declarator_has_function) ? 0 : parsed_type_is_unsigned);
+ set_global_symbol_floating (static_label, (declarator_is_pointer || declarator_has_function) ? 0 : parsed_type_is_floating);
+ set_global_symbol_array (static_label, declarator_has_array);
+ set_global_symbol_array_count (static_label, declarator_has_array ? declarator_array_count : 0);
+ set_global_symbol_array_element_size (static_label, declarator_has_array ? (int)(declarator_object_size (parsed_type_size) / (declarator_array_count > 0 ? declarator_array_count : 1)) : 0);
+
+ emit_block_static_object (static_label,
+ declarator_is_pointer ? DATA_PTR : (parsed_type_is_aggregate ? parsed_type_size : (parsed_type_size & 0x1f)),
+ declarator_has_array, declarator_array_count,
+ object_fields, object_field_count,
+ init_values, init_symbols, init_value_count,
+ (!declarator_is_pointer && parsed_type_is_aggregate));
+
+ }
+
+ add_static_local_symbol (name, static_label,
+ declarator_object_size (parsed_type_size),
+ type_alignment (declarator_is_pointer ? DATA_PTR : parsed_type_size),
+ (declarator_is_pointer ? 0 : parsed_type_is_unsigned),
+ block_scope_start, name_line, name_start, name_caret);
+
+ set_local_symbol_floating (name, declarator_is_pointer ? 0 : parsed_type_is_floating);
+ set_local_symbol_array (name, declarator_has_array);
+ set_local_symbol_array_element_size (name, declarator_has_array ? (int)(declarator_object_size (parsed_type_size) / (declarator_array_count > 0 ? declarator_array_count : 1)) : 0);
+ set_local_symbol_pointer_info (name, declarator_effective_pointer_depth_now (),
+ declarator_effective_pointed_size_now (parsed_type_size, object_fields, object_field_count));
+
+ }
+
+ for (i = 0; i < MAX_AGG_FIELDS; i++) {
+
+ if (init_symbols[i]) {
+
+ free (init_symbols[i]);
+ init_symbols[i] = 0;
+
+ }
+
+ }
+
+ if (name) {
+ free (name);
+ }
+
+ if (!_accept (TOK_COMMA)) {
+ break;
+ }
+
+ }
+
+ expect (TOK_SEMI, ";");
+
+ if (block_stack_emitted) {
+ ensure_block_stack_allocated (block_stack_start, &block_stack_bytes, &block_stack_emitted);
+ }
+
+ continue;
+
+ }
+
+ if (declaration_phase) {
+
+ declaration_phase = 0;
+
+ {
+
+ long needed_stack_bytes = align_up_long (current_local_stack_size - block_stack_start, 4);
+
+ if (!block_stack_emitted) {
+
+ block_stack_bytes = needed_stack_bytes;
+ emit_stack_adjust (block_stack_bytes, 1);
+
+ if (!is_function_body) {
+ current_block_cleanup_bytes += block_stack_bytes;
+ }
+
+ block_stack_emitted = (block_stack_bytes > 0);
+
+ } else if (needed_stack_bytes > block_stack_bytes) {
+
+ long extra_stack_bytes = needed_stack_bytes - block_stack_bytes;
+ emit_stack_adjust (extra_stack_bytes, 1);
+
+ if (!is_function_body) {
+ current_block_cleanup_bytes += extra_stack_bytes;
+ }
+
+ block_stack_bytes = needed_stack_bytes;
+
+ }
+
+ }
+
+ emit_local_initializers (inits, init_count);
+ init_count = 0;
+
+ }
+
+ parse_statement ();
+
+ }
+
+ if (declaration_phase) {
+
+ /**
+ * The block contained declarations only. We still need to reserve
+ * stack space for those automatic objects, otherwise a function like
+ *
+ * void f(void) { int a; }
+ *
+ * would produce no `sub esp, ...` at all.
+ */
+ declaration_phase = 0;
+
+ {
+
+ long needed_stack_bytes = align_up_long (current_local_stack_size - block_stack_start, 4);
+
+ if (!block_stack_emitted) {
+
+ block_stack_bytes = needed_stack_bytes;
+ emit_stack_adjust (block_stack_bytes, 1);
+ block_stack_emitted = (block_stack_bytes > 0);
+
+ } else if (needed_stack_bytes > block_stack_bytes) {
+
+ emit_stack_adjust (needed_stack_bytes - block_stack_bytes, 1);
+ block_stack_bytes = needed_stack_bytes;
+
+ }
+
+ }
+
+ emit_local_initializers (inits, init_count);
+ init_count = 0;
+
+ }
+
+ expect (TOK_RBRACE, "}");
+
+ if (!is_function_body && block_stack_emitted && block_stack_bytes > 0) {
+
+ emit_stack_adjust (block_stack_bytes, 0);
+
+ if (current_block_cleanup_bytes >= block_stack_bytes) {
+ current_block_cleanup_bytes -= block_stack_bytes;
+ } else {
+ current_block_cleanup_bytes = 0;
+ }
+
+ }
+
+ current_parse_block_depth--;
+ truncate_local_symbols (block_local_start, block_stack_start);
+
+}
+
+static int is_assignment_operator (enum token_kind k) {
+
+ switch (k) {
+
+ case TOK_ASSIGN:
+ case TOK_PLUSEQ:
+ case TOK_MINUSEQ:
+ case TOK_STAREQ:
+ case TOK_SLASHEQ:
+ case TOK_MODEQ:
+ case TOK_ANDEQ:
+ case TOK_OREQ:
+ case TOK_XOREQ:
+ case TOK_LSHEQ:
+ case TOK_RSHEQ:
+
+ return 1;
+
+ default:
+
+ return 0;
+
+ }
+
+}
+
+static void emit_load_local_to_reg (const char *reg, long offset, int size) {
+
+ char memref[64];
+
+ if (!state->ofp || !reg) {
+ return;
+ }
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ format_intel_ebp_offset (memref, sizeof (memref), offset);
+
+ if (size == (DATA_CHAR & 0x1f)) {
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " movzx %s, byte %s\n" : " movzx %s, byte ptr %s\n"), reg, memref);
+ } else if (size == (DATA_SHORT & 0x1f)) {
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " movzx %s, word %s\n" : " movzx %s, word ptr %s\n"), reg, memref);
+ } else {
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov %s, dword %s\n" : " mov %s, dword ptr %s\n"), reg, memref);
+ }
+
+ } else {
+
+ if (size == (DATA_CHAR & 0x1f)) {
+ fprintf (state->ofp, " movzbl %ld(%%ebp), %%%s\n", offset, reg);
+ } else if (size == (DATA_SHORT & 0x1f)) {
+ fprintf (state->ofp, " movzwl %ld(%%ebp), %%%s\n", offset, reg);
+ } else {
+ fprintf (state->ofp, " movl %ld(%%ebp), %%%s\n", offset, reg);
+ }
+
+ }
+
+}
+
+static void emit_load_global_to_reg (const char *reg, const char *symbol, int size) {
+
+ const char *asm_symbol;
+
+ if (!state->ofp || !reg || !symbol) {
+ return;
+ }
+
+ emit_extern_reference_symbol (symbol, size);
+ asm_symbol = asm_global_symbol_name (symbol);
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ if (size == (DATA_CHAR & 0x1f)) {
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " movzx %s, byte [%s]\n" : " movzx %s, byte ptr %s\n"), reg, asm_symbol);
+ } else if (size == (DATA_SHORT & 0x1f)) {
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " movzx %s, word [%s]\n" : " movzx %s, word ptr %s\n"), reg, asm_symbol);
+ } else {
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov %s, dword [%s]\n" : " mov %s, dword ptr %s\n"), reg, asm_symbol);
+ }
+
+ } else {
+
+ if (size == (DATA_CHAR & 0x1f)) {
+ fprintf (state->ofp, " movzbl %s, %%%s\n", asm_symbol, reg);
+ } else if (size == (DATA_SHORT & 0x1f)) {
+ fprintf (state->ofp, " movzwl %s, %%%s\n", asm_symbol, reg);
+ } else {
+ fprintf (state->ofp, " movl %s, %%%s\n", asm_symbol, reg);
+ }
+
+ }
+
+}
+
+static void emit_store_reg_to_local (long offset, int size, const char *reg) {
+
+ char memref[64];
+
+ if (!state->ofp || !reg) {
+ return;
+ }
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ format_intel_ebp_offset (memref, sizeof (memref), offset);
+
+ if (size == (DATA_CHAR & 0x1f)) {
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov byte %s, %cl\n" : " mov byte ptr %s, %cl\n"), memref, reg[1]);
+ } else if (size == (DATA_SHORT & 0x1f)) {
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov word %s, %cx\n" : " mov word ptr %s, %cx\n"), memref, reg[1]);
+ } else {
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov dword %s, %s\n" : " mov dword ptr %s, %s\n"), memref, reg);
+ }
+
+ } else {
+
+ if (size == (DATA_CHAR & 0x1f)) {
+ fprintf (state->ofp, " movb %%%cl, %ld(%%ebp)\n", reg[1], offset);
+ } else if (size == (DATA_SHORT & 0x1f)) {
+ fprintf (state->ofp, " movw %%%cx, %ld(%%ebp)\n", reg[1], offset);
+ } else {
+ fprintf (state->ofp, " movl %%%s, %ld(%%ebp)\n", reg, offset);
+ }
+
+ }
+
+}
+
+static void emit_store_reg_to_global (const char *symbol, int size, const char *reg) {
+
+ const char *asm_symbol;
+
+ if (!state->ofp || !symbol || !reg) {
+ return;
+ }
+
+ asm_symbol = asm_global_symbol_name (symbol);
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ if (size == (DATA_CHAR & 0x1f)) {
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov byte [%s], %cl\n" : " mov byte ptr %s, %cl\n"), asm_symbol, reg[1]);
+ } else if (size == (DATA_SHORT & 0x1f)) {
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov word [%s], %cx\n" : " mov word ptr %s, %cx\n"), asm_symbol, reg[1]);
+ } else {
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov dword [%s], %s\n" : " mov dword ptr %s, %s\n"), asm_symbol, reg);
+ }
+
+ } else {
+
+ if (size == (DATA_CHAR & 0x1f)) {
+ fprintf (state->ofp, " movb %%%cl, %s\n", reg[1], asm_symbol);
+ } else if (size == (DATA_SHORT & 0x1f)) {
+ fprintf (state->ofp, " movw %%%cx, %s\n", reg[1], asm_symbol);
+ } else {
+ fprintf (state->ofp, " movl %%%s, %s\n", reg, asm_symbol);
+ }
+
+ }
+
+}
+
+static void emit_load_local64_to_pair (long offset, const char *lo, const char *hi) {
+
+ char memref_lo[64], memref_hi[64];
+
+ if (!state->ofp || !lo || !hi) {
+ return;
+ }
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ format_intel_ebp_offset (memref_lo, sizeof (memref_lo), offset);
+ format_intel_ebp_offset (memref_hi, sizeof (memref_hi), offset + 4);
+
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov %s, dword %s\n" : " mov %s, dword ptr %s\n"), lo, memref_lo);
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov %s, dword %s\n" : " mov %s, dword ptr %s\n"), hi, memref_hi);
+
+ } else {
+
+ fprintf (state->ofp, " movl %ld(%%ebp), %%%s\n", offset, lo);
+ fprintf (state->ofp, " movl %ld(%%ebp), %%%s\n", offset + 4, hi);
+
+ }
+
+}
+
+static void emit_store_pair_to_local64 (long offset, const char *lo, const char *hi) {
+
+ char memref_lo[64], memref_hi[64];
+
+ if (!state->ofp || !lo || !hi) {
+ return;
+ }
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ format_intel_ebp_offset (memref_lo, sizeof (memref_lo), offset);
+ format_intel_ebp_offset (memref_hi, sizeof (memref_hi), offset + 4);
+
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov dword %s, %s\n" : " mov dword ptr %s, %s\n"), memref_lo, lo);
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov dword %s, %s\n" : " mov dword ptr %s, %s\n"), memref_hi, hi);
+
+ } else {
+
+ fprintf (state->ofp, " movl %%%s, %ld(%%ebp)\n", lo, offset);
+ fprintf (state->ofp, " movl %%%s, %ld(%%ebp)\n", hi, offset + 4);
+
+ }
+
+}
+
+static void emit_load_global64_to_pair (const char *lo, const char *hi, const char *symbol) {
+
+ const char *asm_symbol;
+
+ if (!state->ofp || !lo || !hi || !symbol) {
+ return;
+ }
+
+ emit_extern_reference_symbol (symbol, DATA_LLONG & 0x1f);
+ asm_symbol = asm_global_symbol_name (symbol);
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov %s, dword [%s]\n" : " mov %s, dword ptr %s\n"), lo, asm_symbol);
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov %s, dword [%s + 4]\n" : " mov %s, dword ptr %s + 4\n"), hi, asm_symbol);
+
+ } else {
+
+ fprintf (state->ofp, " movl %s, %%%s\n", asm_symbol, lo);
+ fprintf (state->ofp, " movl %s+4, %%%s\n", asm_symbol, hi);
+
+ }
+
+}
+
+static void emit_store_pair_to_global64 (const char *symbol, const char *lo, const char *hi) {
+
+ const char *asm_symbol;
+
+ if (!state->ofp || !symbol || !lo || !hi) {
+ return;
+ }
+
+ emit_extern_reference_symbol (symbol, DATA_LLONG & 0x1f);
+ asm_symbol = asm_global_symbol_name (symbol);
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov dword [%s], %s\n" : " mov dword ptr %s, %s\n"), asm_symbol, lo);
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov dword [%s + 4], %s\n" : " mov dword ptr %s + 4, %s\n"), asm_symbol, hi);
+
+ } else {
+
+ fprintf (state->ofp, " movl %%%s, %s\n", lo, asm_symbol);
+ fprintf (state->ofp, " movl %%%s, %s+4\n", hi, asm_symbol);
+
+ }
+
+}
+
+static void emit_call_identifier_to_reg_now (const char *name, const char *reg, const char *name_start, const char *name_caret, unsigned long name_line);
+static void emit_push_pending_struct_return_address_now (int stack_arg_bytes);
+
+static int emit_push_aggregate_from_addr_reg_now (const char *reg, int size) {
+
+ int offset;
+ int chunk;
+
+ if (!reg || size <= (DATA_PTR & 0x1f)) {
+ return 0;
+ }
+
+ if (!state->ofp) {
+ return 1;
+ }
+
+ for (offset = size; offset > 0; ) {
+
+ if (offset >= 4) {
+
+ chunk = 4;
+ offset -= 4;
+
+ } else if (offset >= 2) {
+
+ chunk = 2;
+ offset -= 2;
+
+ } else {
+
+ chunk = 1;
+ offset -= 1;
+
+ }
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ if (state->syntax & ASM_SYNTAX_NASM) {
+
+ if (chunk == 4) {
+ fprintf (state->ofp, " push dword [%s + %d]\n", reg, offset);
+ } else if (chunk == 2) {
+
+ fprintf (state->ofp, " movzx edx, word [%s + %d]\n", reg, offset);
+ fprintf (state->ofp, " push edx\n");
+
+ } else {
+
+ fprintf (state->ofp, " movzx edx, byte [%s + %d]\n", reg, offset);
+ fprintf (state->ofp, " push edx\n");
+
+ }
+
+ } else {
+
+ if (chunk == 4) {
+ fprintf (state->ofp, " push dword ptr [%s + %d]\n", reg, offset);
+ } else if (chunk == 2) {
+
+ fprintf (state->ofp, " movzx edx, word ptr [%s + %d]\n", reg, offset);
+ fprintf (state->ofp, " push edx\n");
+
+ } else {
+
+ fprintf (state->ofp, " movzx edx, byte ptr [%s + %d]\n", reg, offset);
+ fprintf (state->ofp, " push edx\n");
+
+ }
+
+ }
+
+ } else {
+
+ if (chunk == 4) {
+ fprintf (state->ofp, " pushl %d(%%%s)\n", offset, reg);
+ } else if (chunk == 2) {
+
+ fprintf (state->ofp, " movzwl %d(%%%s), %%edx\n", offset, reg);
+ fprintf (state->ofp, " pushl %%edx\n");
+
+ } else {
+
+ fprintf (state->ofp, " movzbl %d(%%%s), %%edx\n", offset, reg);
+ fprintf (state->ofp, " pushl %%edx\n");
+
+ }
+
+ }
+
+ }
+
+ return 1;
+
+}
+
+static void emit_call_pointer_in_reg_now (const char *fn_reg, const char *result_reg);
+
+static int is_arithmetic_binary_operator (enum token_kind k) {
+
+ return k == TOK_PLUS || k == TOK_MINUS || k == TOK_STAR || k == TOK_BSLASH ||
+ k == TOK_MOD || k == TOK_AMPER || k == TOK_PIPE || k == TOK_CARET ||
+ k == TOK_LSH || k == TOK_RSH;
+
+}
+
+static void emit_push_reg_now (const char *reg) {
+
+ if (!state->ofp || !reg) {
+ return;
+ }
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " push %s\n", reg);
+ } else {
+ fprintf (state->ofp, " pushl %%%s\n", reg);
+ }
+
+}
+
+static void emit_pop_reg_now (const char *reg) {
+
+ if (!state->ofp || !reg) {
+ return;
+ }
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " pop %s\n", reg);
+ } else {
+ fprintf (state->ofp, " popl %%%s\n", reg);
+ }
+
+}
+
+static void emit_mov_reg_to_reg_now (const char *dst, const char *src) {
+
+ if (!state->ofp || !dst || !src || strcmp (dst, src) == 0) {
+ return;
+ }
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " mov %s, %s\n", dst, src);
+ } else {
+ fprintf (state->ofp, " movl %%%s, %%%s\n", src, dst);
+ }
+
+}
+
+static void emit_load_indexed_pointer_to_reg_now (const char *base_reg, const char *index_reg) {
+
+ if (!state->ofp || !base_reg || !index_reg) {
+ return;
+ }
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ if (state->syntax & ASM_SYNTAX_NASM) {
+ fprintf (state->ofp, " mov %s, dword [%s + %s * 4]\n", base_reg, base_reg, index_reg);
+ } else {
+ fprintf (state->ofp, " mov %s, dword ptr [%s + %s * 4]\n", base_reg, base_reg, index_reg);
+ }
+
+ } else {
+ fprintf (state->ofp, " movl (%%%s,%%%s,4), %%%s\n", base_reg, index_reg, base_reg);
+ }
+
+}
+
+static void emit_load_indexed_char_to_reg_now (const char *base_reg, const char *index_reg, const char *dst_reg) {
+
+ if (!state->ofp || !base_reg || !index_reg || !dst_reg) {
+ return;
+ }
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ if (state->syntax & ASM_SYNTAX_NASM) {
+ fprintf (state->ofp, " movzx %s, byte [%s + %s]\n", dst_reg, base_reg, index_reg);
+ } else {
+ fprintf (state->ofp, " movzx %s, byte ptr [%s + %s]\n", dst_reg, base_reg, index_reg);
+ }
+
+ } else {
+ fprintf (state->ofp, " movzbl (%%%s,%%%s), %%%s\n", base_reg, index_reg, dst_reg);
+ }
+
+}
+
+static void emit_load_indexed_sized_to_reg_now (const char *base_reg, const char *index_reg, const char *dst_reg, int elem_size) {
+
+ int scale = 1;
+
+ const char *atype = "byte";
+ const char *gasop = "movzbl";
+
+ if (!state->ofp || !base_reg || !index_reg || !dst_reg) {
+ return;
+ }
+
+ elem_size &= 0x1f;
+
+ if (elem_size == (DATA_SHORT & 0x1f)) {
+
+ scale = 2;
+
+ atype = "word";
+ gasop = "movzwl";
+
+ } else if (elem_size == (DATA_INT & 0x1f) || elem_size == (DATA_LONG & 0x1f) || elem_size == DATA_PTR) {
+
+ scale = 4;
+
+ atype = "dword";
+ gasop = "movl";
+
+ }
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ if (elem_size == (DATA_CHAR & 0x1f)) {
+ emit_load_indexed_char_to_reg_now (base_reg, index_reg, dst_reg);
+ } else if (scale == 1) {
+
+ if (elem_size == (DATA_SHORT & 0x1f)) {
+
+ if (state->syntax & ASM_SYNTAX_NASM) {
+ fprintf (state->ofp, " movzx %s, word [%s + %s]\n", dst_reg, base_reg, index_reg);
+ } else {
+ fprintf (state->ofp, " movzx %s, word ptr [%s + %s]\n", dst_reg, base_reg, index_reg);
+ }
+
+ } else if (state->syntax & ASM_SYNTAX_NASM) {
+ fprintf (state->ofp, " mov %s, %s [%s + %s]\n", dst_reg, atype, base_reg, index_reg);
+ } else {
+ fprintf (state->ofp, " mov %s, %s ptr [%s + %s]\n", dst_reg, atype, base_reg, index_reg);
+ }
+
+ } else {
+
+ if (elem_size == (DATA_SHORT & 0x1f)) {
+
+ if (state->syntax & ASM_SYNTAX_NASM) {
+ fprintf (state->ofp, " movzx %s, word [%s + %s * %d]\n", dst_reg, base_reg, index_reg, scale);
+ } else {
+ fprintf (state->ofp, " movzx %s, word ptr [%s + %s * %d]\n", dst_reg, base_reg, index_reg, scale);
+ }
+
+ } else if (state->syntax & ASM_SYNTAX_NASM) {
+ fprintf (state->ofp, " mov %s, %s [%s + %s * %d]\n", dst_reg, atype, base_reg, index_reg, scale);
+ } else {
+ fprintf (state->ofp, " mov %s, %s ptr [%s + %s * %d]\n", dst_reg, atype, base_reg, index_reg, scale);
+ }
+
+ }
+
+ } else {
+
+ if (scale == 1) {
+ fprintf (state->ofp, " %s (%%%s,%%%s), %%%s\n", gasop, base_reg, index_reg, dst_reg);
+ } else {
+ fprintf (state->ofp, " %s (%%%s,%%%s,%d), %%%s\n", gasop, base_reg, index_reg, scale, dst_reg);
+ }
+
+ }
+
+}
+
+static void emit_load_symbol_address_to_reg_now (const char *reg, const char *symbol, long offset, int is_local) {
+
+ const char *asm_symbol;
+
+ if (!state->ofp || !reg) {
+ return;
+ }
+
+ if (is_local) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " lea %s, [ebp%+ld]\n", reg, offset);
+ } else {
+ fprintf (state->ofp, " leal %ld(%%ebp), %%%s\n", offset, reg);
+ }
+
+ return;
+
+ }
+
+ if (!symbol) {
+ return;
+ }
+
+ emit_extern_reference_symbol (symbol, DATA_PTR);
+ asm_symbol = asm_global_symbol_name (symbol);
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ if (state->syntax & ASM_SYNTAX_NASM) {
+ fprintf (state->ofp, " mov %s, %s\n", reg, asm_symbol);
+ } else {
+ fprintf (state->ofp, " mov %s, offset %s\n", reg, asm_symbol);
+ }
+
+ } else {
+ fprintf (state->ofp, " movl $%s, %%%s\n", asm_symbol, reg);
+ }
+
+}
+
+static void emit_add_indexed_scaled_address_to_reg_now (const char *base_reg, const char *index_reg, int elem_size) {
+
+ int raw_elem_size = elem_size;
+ int scale = 1;
+
+ if (!state->ofp || !base_reg || !index_reg) {
+ return;
+ }
+
+ if (raw_elem_size == DATA_SHORT || raw_elem_size == (DATA_SHORT & 0x1f)) {
+
+ elem_size = DATA_SHORT & 0x1f;
+ scale = 2;
+
+ } else if (raw_elem_size == DATA_INT || raw_elem_size == DATA_LONG || raw_elem_size == DATA_PTR || raw_elem_size == (DATA_INT & 0x1f)) {
+
+ elem_size = DATA_INT & 0x1f;
+ scale = 4;
+
+ } else if (elem_size > 1) {
+ scale = 0;
+ }
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ if (scale == 0) {
+
+ fprintf (state->ofp, " imul %s, %d\n", index_reg, elem_size);
+ fprintf (state->ofp, " lea %s, [%s + %s]\n", base_reg, base_reg, index_reg);
+
+ } else if (scale == 1) {
+ fprintf (state->ofp, " lea %s, [%s + %s]\n", base_reg, base_reg, index_reg);
+ } else {
+ fprintf (state->ofp, " lea %s, [%s + %s * %d]\n", base_reg, base_reg, index_reg, scale);
+ }
+
+ } else {
+
+ if (scale == 0) {
+
+ fprintf (state->ofp, " imull $%d, %%%s, %%%s\n", elem_size, index_reg, index_reg);
+ fprintf (state->ofp, " leal (%%%s,%%%s), %%%s\n", base_reg, index_reg, base_reg);
+
+ } else if (scale == 1) {
+ fprintf (state->ofp, " leal (%%%s,%%%s), %%%s\n", base_reg, index_reg, base_reg);
+ } else {
+ fprintf (state->ofp, " leal (%%%s,%%%s,%d), %%%s\n", base_reg, index_reg, scale, base_reg);
+ }
+
+ }
+
+}
+
+static int emit_parse_postfix_subscript_scaled_address_to_reg_now (const char *reg, int elem_size) {
+
+ const char *index_reg;
+ int saw_subscript = 0;
+
+ if (!reg) {
+ reg = "eax";
+ }
+
+ index_reg = (strcmp (reg, "ecx") == 0) ? "edx" : "ecx";
+
+ while (tok.kind == TOK_LBRACK) {
+
+ saw_subscript = 1;
+ get_token ();
+
+ emit_push_reg_now (reg);
+ emit_load_assignment_rhs_expression_to_reg (index_reg);
+ expect (TOK_RBRACK, "]");
+ emit_pop_reg_now (reg);
+
+ if (tok.kind == TOK_LBRACK) {
+ emit_load_indexed_pointer_to_reg_now (reg, index_reg);
+ } else {
+ emit_add_indexed_scaled_address_to_reg_now (reg, index_reg, elem_size);
+ }
+
+ }
+
+ return saw_subscript;
+
+}
+
+static int emit_parse_postfix_subscripts_to_reg_now (const char *reg, int elem_size, int pointer_depth, int pointed_size) {
+
+ const char *index_reg;
+ int saw_subscript = 0;
+
+ if (!reg) {
+ reg = "eax";
+ }
+
+ index_reg = (strcmp (reg, "ecx") == 0) ? "edx" : "ecx";
+
+ while (tok.kind == TOK_LBRACK) {
+
+ saw_subscript = 1;
+ get_token ();
+
+ emit_push_reg_now (reg);
+ emit_load_assignment_rhs_expression_to_reg (index_reg);
+
+ expect (TOK_RBRACK, "]");
+ emit_pop_reg_now (reg);
+
+ if (tok.kind == TOK_LBRACK) {
+
+ emit_load_indexed_pointer_to_reg_now (reg, index_reg);
+
+ if (pointer_depth > 0) {
+ pointer_depth--;
+ }
+
+ if (pointed_size > 0) {
+ elem_size = (pointer_depth > 1) ? (DATA_PTR & 0x1f) : index_step_size (pointed_size);
+ }
+
+ } else {
+
+ if ((elem_size & 0x1f) > DATA_PTR) {
+ emit_add_indexed_scaled_address_to_reg_now (reg, index_reg, elem_size);
+ } else {
+ emit_load_indexed_sized_to_reg_now (reg, index_reg, reg, elem_size);
+ }
+
+ }
+
+ }
+
+ return saw_subscript;
+
+}
+
+static int parse_incdec_identifier_now (enum token_kind *op, char **name, const char **name_start, const char **name_caret, unsigned long *name_line) {
+
+ if (tok.kind != TOK_INCR && tok.kind != TOK_DECR) {
+ return 0;
+ }
+
+ *op = tok.kind;
+ get_token ();
+
+ if (tok.kind != TOK_IDENT) {
+
+ report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "expected identifier after %s", *op == TOK_INCR ? "++" : "--");
+
+ *name = 0;
+ return 1;
+ }
+
+ *name = xstrdup (tok.ident);
+ *name_start = tok.start;
+ *name_caret = tok.caret;
+ *name_line = get_line_number ();
+
+ get_token ();
+ return 1;
+
+}
+
+static void emit_apply_postfix_member_access_to_reg_now (const char *reg);
+static void emit_apply_postfix_member_incdec_now (const char *reg, enum token_kind op);
+static void emit_load_member_from_addr_reg_now (const char *dst_reg, const char *addr_reg, int offset, int size);
+
+static struct token *clone_current_token_now (void);
+static void emit_store_reg_to_deref_reg_now (const char *addr_reg, const char *value_reg, int size);
+
+static int source_starts_prefix_incdec_parenthesized_deref_at (const char *p) {
+
+ if (!p) {
+ return 0;
+ }
+
+ if (!((p[0] == '+' && p[1] == '+') || (p[0] == '-' && p[1] == '-'))) {
+ return 0;
+ }
+
+ p += 2;
+
+ while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+ p++;
+ }
+
+ if (*p != '(') {
+ return 0;
+ }
+
+ p++;
+
+ while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+ p++;
+ }
+
+ if (*p != '*') {
+ return 0;
+ }
+
+ p++;
+
+ while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+ p++;
+ }
+
+ if (!((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || *p == '_')) {
+ return 0;
+ }
+
+ p++;
+
+ while ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || (*p >= '0' && *p <= '9') || *p == '_') {
+ p++;
+ }
+
+ while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+ p++;
+ }
+
+ return *p == ')';
+
+}
+
+static int emit_load_prefix_incdec_parenthesized_deref_to_reg_now (const char *reg) {
+
+ enum token_kind op;
+ char *name;
+
+ const char *name_start;
+ const char *name_caret;
+
+ unsigned long name_line;
+ struct local_symbol *sym;
+
+ int global_index;
+ int pointer_depth = 0;
+ int pointed_size = DATA_INT & 0x1f;
+ int lvalue_size = DATA_INT & 0x1f;
+ int step = 1;
+
+ const char *addr_reg;
+
+ if (tok.kind != TOK_INCR && tok.kind != TOK_DECR) {
+ return 0;
+ }
+
+ if (!source_starts_prefix_incdec_parenthesized_deref_at (tok.caret)) {
+ return 0;
+ }
+
+ op = tok.kind;
+ get_token ();
+
+ expect (TOK_LPAREN, "(");
+ expect (TOK_STAR, "*");
+
+ name_start = tok.start;
+ name_caret = tok.caret;
+ name_line = get_line_number ();
+
+ if (tok.kind != TOK_IDENT || !tok.ident) {
+
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "expected identifier after *");
+ return 1;
+
+ }
+
+ name = xstrdup (tok.ident);
+ get_token ();
+
+ expect (TOK_RPAREN, ")");
+
+ sym = find_local_symbol (name);
+ global_index = find_global_symbol (name);
+
+ if (sym) {
+
+ pointer_depth = sym->pointer_depth;
+ pointed_size = sym->pointed_size;
+
+ } else if (global_index >= 0) {
+
+ pointer_depth = get_global_symbol_pointer_depth (name);
+ pointed_size = get_global_symbol_pointed_size (name);
+
+ } else {
+
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+
+ free (name);
+ return 1;
+
+ }
+
+ if (pointer_depth <= 0) {
+
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "invalid indirection of non-pointer '%s'", name);
+
+ free (name);
+ return 1;
+
+ }
+
+ if (pointed_size <= 0) {
+ pointed_size = DATA_INT & 0x1f;
+ }
+
+ if (pointer_depth > 1) {
+
+ lvalue_size = DATA_PTR & 0x1f;
+ step = pointer_depth > 2 ? (DATA_PTR & 0x1f) : index_step_size (pointed_size);
+
+ } else {
+
+ lvalue_size = pointed_size & 0x1f;
+ step = 1;
+
+ }
+
+ if (!reg) {
+ reg = "eax";
+ }
+
+ addr_reg = (strcmp (reg, "edx") == 0) ? "ecx" : "edx";
+
+ if (sym) {
+
+ if (sym->is_static && sym->static_label) {
+ emit_load_global_to_reg (addr_reg, sym->static_label, DATA_PTR);
+ } else {
+ emit_load_local_to_reg (addr_reg, sym->offset, DATA_PTR);
+ }
+
+ } else {
+ emit_load_global_to_reg (addr_reg, name, DATA_PTR);
+ }
+
+ emit_load_member_from_addr_reg_now (reg, addr_reg, 0, lvalue_size);
+
+ if (state->ofp) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, op == TOK_INCR ? " add %s, %d\n" : " sub %s, %d\n", reg, step);
+ } else {
+ fprintf (state->ofp, op == TOK_INCR ? " addl $%d, %%%s\n" : " subl $%d, %%%s\n", step, reg);
+ }
+
+ }
+
+ emit_store_reg_to_deref_reg_now (addr_reg, reg, lvalue_size);
+
+ if (pointer_depth > 1) {
+ set_rhs_last_pointer_info (pointer_depth - 1, pointed_size);
+ } else {
+ clear_rhs_last_pointer_info ();
+ }
+
+ free (name);
+ return 1;
+
+}
+
+static int emit_load_prefix_incdec_deref_to_reg_now (const char *reg) {
+
+ enum token_kind op;
+
+ struct token *saved_tok;
+ char *name;
+
+ const char *name_start;
+ const char *name_caret;
+
+ unsigned long name_line;
+
+ struct local_symbol *sym;
+
+ int global_index;
+ int deref_size = DATA_INT & 0x1f;
+
+ const char *addr_reg;
+
+ if (tok.kind != TOK_INCR && tok.kind != TOK_DECR) {
+ return 0;
+ }
+
+ saved_tok = clone_current_token_now ();
+ op = tok.kind;
+
+ get_token ();
+
+ if (tok.kind != TOK_STAR) {
+
+ unget_token (saved_tok);
+ return 0;
+
+ }
+
+ get_token ();
+
+ if (tok.kind != TOK_IDENT) {
+
+ report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "expected identifier after *");
+
+ free (saved_tok->ident);
+ free ((char *) saved_tok->start);
+ free (saved_tok);
+
+ return 1;
+
+ }
+
+ name = xstrdup (tok.ident);
+ name_start = tok.start;
+ name_caret = tok.caret;
+ name_line = get_line_number ();
+
+ get_token ();
+
+ sym = find_local_symbol (name);
+ global_index = find_global_symbol (name);
+
+ if (sym) {
+
+ if (sym->pointer_depth > 1) {
+ deref_size = DATA_PTR & 0x1f;
+ } else if (sym->pointer_depth == 1 && sym->pointed_size > 0) {
+ deref_size = sym->pointed_size & 0x1f;
+ }
+
+ } else if (global_index >= 0) {
+
+ if (get_global_symbol_pointer_depth (name) > 1) {
+ deref_size = DATA_PTR & 0x1f;
+ } else if (get_global_symbol_pointer_depth (name) == 1 && get_global_symbol_pointed_size (name) > 0) {
+ deref_size = get_global_symbol_pointed_size (name) & 0x1f;
+ }
+
+ } else {
+
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+
+ free (name);
+ free (saved_tok->ident);
+
+ free ((char *) saved_tok->start);
+ free (saved_tok);
+
+ return 1;
+
+ }
+
+ if (!reg) {
+ reg = "eax";
+ }
+
+ addr_reg = (strcmp (reg, "edx") == 0) ? "ecx" : "edx";
+
+ if (sym) {
+
+ if (sym->is_static && sym->static_label) {
+ emit_load_global_to_reg (addr_reg, sym->static_label, DATA_PTR);
+ } else {
+ emit_load_local_to_reg (addr_reg, sym->offset, DATA_PTR);
+ }
+
+ } else {
+ emit_load_global_to_reg (addr_reg, name, DATA_PTR);
+ }
+
+ emit_load_member_from_addr_reg_now (reg, addr_reg, 0, deref_size);
+
+ if (state->ofp) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, op == TOK_INCR ? " add %s, 1\n" : " sub %s, 1\n", reg);
+ } else {
+ fprintf (state->ofp, op == TOK_INCR ? " addl $1, %%%s\n" : " subl $1, %%%s\n", reg);
+ }
+
+ }
+
+ emit_store_reg_to_deref_reg_now (addr_reg, reg, deref_size);
+
+ free (name);
+
+ free (saved_tok->ident);
+ free ((char *) saved_tok->start);
+
+ free (saved_tok);
+ return 1;
+
+}
+
+static int emit_load_prefix_incdec_to_reg_now (const char *reg) {
+
+ enum token_kind op;
+ char *name;
+
+ const char *name_start, *name_caret;
+ unsigned long name_line;
+
+ struct local_symbol *sym;
+
+ if (emit_load_prefix_incdec_parenthesized_deref_to_reg_now (reg)) {
+ return 1;
+ }
+
+ if (emit_load_prefix_incdec_deref_to_reg_now (reg)) {
+ return 1;
+ }
+
+ if (!parse_incdec_identifier_now (&op, &name, &name_start, &name_caret, &name_line)) {
+ return 0;
+ }
+
+ if (!name) {
+ return 1;
+ }
+
+ sym = find_local_symbol (name);
+ emit_incdec_symbol_now (sym, name, op, name_line, name_start, name_caret);
+
+ if (sym) {
+
+ if (sym->is_static && sym->static_label) {
+ emit_load_global_to_reg (reg, sym->static_label, sym->size);
+ } else {
+ emit_load_local_to_reg (reg, sym->offset, sym->size);
+ }
+
+ } else if (find_global_symbol (name) >= 0) {
+ emit_load_global_to_reg (reg, name, get_global_symbol_size (name));
+ }
+
+ free (name);
+ return 1;
+
+}
+
+static int emit_load_prefix_incdec_member_to_reg_now (const char *reg) {
+
+ struct token *saved_tok;
+
+ enum token_kind op;
+ enum token_kind member_op = TOK_EOF;
+
+ char *member;
+
+ const char *member_start;
+ const char *member_caret;
+
+ unsigned long member_line;
+
+ int offset = 0;
+ int size = DATA_INT & 0x1f;
+
+ if (tok.kind != TOK_INCR && tok.kind != TOK_DECR) {
+ return 0;
+ }
+
+ saved_tok = clone_current_token_now ();
+ get_token ();
+
+ if (tok.kind == TOK_STAR) {
+
+ unget_token (saved_tok);
+ return 0;
+
+ }
+
+ unget_token (saved_tok);
+
+ if (emit_load_prefix_incdec_parenthesized_deref_to_reg_now (reg)) {
+ return 1;
+ }
+
+ op = tok.kind;
+ get_token ();
+
+ if (tok.kind != TOK_LPAREN) {
+
+ char *name;
+
+ const char *name_start;
+ const char *name_caret;
+
+ unsigned long name_line;
+ struct local_symbol *sym;
+
+ int global_index;
+
+ if (tok.kind != TOK_IDENT) {
+
+ report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "expected identifier after %s", op == TOK_INCR ? "++" : "--");
+ return 1;
+
+ }
+
+ name = xstrdup (tok.ident);
+ name_start = tok.start;
+ name_caret = tok.caret;
+ name_line = get_line_number ();
+
+ get_token ();
+
+ sym = find_local_symbol (name);
+ global_index = find_global_symbol (name);
+
+ if (tok.kind == TOK_ARROW) {
+
+ member_op = tok.kind;
+ get_token ();
+
+ member_start = tok.start;
+ member_caret = tok.caret;
+ member_line = get_line_number ();
+
+ if (tok.kind != TOK_IDENT) {
+
+ report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "expected member name after %s", member_op == TOK_ARROW ? "->" : ".");
+
+ free (name);
+ return 1;
+
+ }
+
+ member = xstrdup (tok.ident);
+ get_token ();
+
+ if (!find_member_info (member, &offset, &size)) {
+
+ report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "unknown member '%s'", member);
+
+ free (member);
+ free (name);
+
+ return 1;
+
+ }
+
+ free (member);
+
+ if (sym) {
+
+ if (sym->is_static && sym->static_label) {
+ emit_load_global_to_reg (reg, sym->static_label, DATA_PTR);
+ } else {
+ emit_load_local_to_reg (reg, sym->offset, DATA_PTR);
+ }
+
+ } else if (global_index >= 0) {
+ emit_load_global_to_reg (reg, name, DATA_PTR);
+ } else {
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+ }
+
+ free (name);
+ goto emit_member_incdec;
+
+ }
+
+ emit_incdec_symbol_now (sym, name, op, name_line, name_start, name_caret);
+
+ if (sym) {
+
+ if (sym->is_static && sym->static_label) {
+ emit_load_global_to_reg (reg, sym->static_label, sym->size);
+ } else {
+ emit_load_local_to_reg (reg, sym->offset, sym->size);
+ }
+
+ } else if (find_global_symbol (name) >= 0) {
+ emit_load_global_to_reg (reg, name, get_global_symbol_size (name));
+ }
+
+ free (name);
+ return 1;
+
+ }
+
+ get_token ();
+
+ emit_load_assignment_rhs_expression_to_reg (reg);
+ expect (TOK_RPAREN, ")");
+
+ if (postfix_member_seen && tok.kind != TOK_ARROW && tok.kind != TOK_DOT) {
+
+ emit_apply_postfix_member_incdec_now (reg, op);
+ emit_load_member_from_addr_reg_now (reg, "edx", postfix_member_offset, postfix_member_size);
+
+ return 1;
+
+ }
+
+ if (tok.kind != TOK_ARROW && tok.kind != TOK_DOT) {
+
+ report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "expected member after %s", op == TOK_INCR ? "++" : "--");
+ return 1;
+
+ }
+
+ member_op = tok.kind;
+ get_token ();
+
+ member_start = tok.start;
+ member_caret = tok.caret;
+ member_line = get_line_number ();
+
+ if (tok.kind != TOK_IDENT) {
+
+ report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "expected member name after %s", member_op == TOK_ARROW ? "->" : ".");
+ return 1;
+
+ }
+
+ member = xstrdup (tok.ident);
+ get_token ();
+
+ if (!find_member_info (member, &offset, &size)) {
+
+ report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "unknown member '%s'", member);
+
+ free (member);
+ return 1;
+
+ }
+
+ free (member);
+
+emit_member_incdec:
+
+ if (!state->ofp) {
+ return 1;
+ }
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ const char *insn = op == TOK_INCR ? "inc" : "dec";
+ const char *opsize = "dword";
+
+ if (size == (DATA_CHAR & 0x1f)) {
+ opsize = "byte";
+ } else if (size == (DATA_SHORT & 0x1f)) {
+ opsize = "word";
+ }
+
+ if (state->syntax & ASM_SYNTAX_NASM) {
+
+ fprintf (state->ofp, " %s %s [%s + %d]\n", insn, opsize, reg, offset);
+
+ if (size == (DATA_CHAR & 0x1f)) {
+ fprintf (state->ofp, " movzx %s, byte [%s + %d]\n", reg, reg, offset);
+ } else if (size == (DATA_SHORT & 0x1f)) {
+ fprintf (state->ofp, " movzx %s, word [%s + %d]\n", reg, reg, offset);
+ } else {
+ fprintf (state->ofp, " mov %s, dword [%s + %d]\n", reg, reg, offset);
+ }
+
+ } else {
+
+ fprintf (state->ofp, " %s %s ptr [%s + %d]\n", insn, opsize, reg, offset);
+
+ if (size == (DATA_CHAR & 0x1f)) {
+ fprintf (state->ofp, " movzx %s, byte ptr [%s + %d]\n", reg, reg, offset);
+ } else if (size == (DATA_SHORT & 0x1f)) {
+ fprintf (state->ofp, " movzx %s, word ptr [%s + %d]\n", reg, reg, offset);
+ } else {
+ fprintf (state->ofp, " mov %s, dword ptr [%s + %d]\n", reg, reg, offset);
+ }
+
+ }
+
+ } else {
+
+ const char *suffix = "l";
+
+ if (size == (DATA_CHAR & 0x1f)) {
+ suffix = "b";
+ } else if (size == (DATA_SHORT & 0x1f)) {
+ suffix = "w";
+ }
+
+ fprintf (state->ofp, " %s%s %d(%%%s)\n", op == TOK_INCR ? "inc" : "dec", suffix, offset, reg);
+
+ if (size == (DATA_CHAR & 0x1f)) {
+ fprintf (state->ofp, " movzbl %d(%%%s), %%%s\n", offset, reg, reg);
+ } else if (size == (DATA_SHORT & 0x1f)) {
+ fprintf (state->ofp, " movzwl %d(%%%s), %%%s\n", offset, reg, reg);
+ } else {
+ fprintf (state->ofp, " movl %d(%%%s), %%%s\n", offset, reg, reg);
+ }
+
+ }
+
+ return 1;
+
+}
+
+static int emit_load_prefix_incdec_to_pair_now (const char *lo, const char *hi) {
+
+ enum token_kind op;
+ char *name;
+
+ const char *name_start, *name_caret;
+ unsigned long name_line;
+
+ struct local_symbol *sym;
+
+ if (!parse_incdec_identifier_now (&op, &name, &name_start, &name_caret, &name_line)) {
+ return 0;
+ }
+
+ if (!name) {
+ return 1;
+ }
+
+ sym = find_local_symbol (name);
+ emit_incdec_symbol_now (sym, name, op, name_line, name_start, name_caret);
+
+ if (sym) {
+
+ if (sym->size == (DATA_LLONG & 0x1f) && !sym->is_floating) {
+
+ if (sym->is_static && sym->static_label) {
+ emit_load_global64_to_pair (lo, hi, sym->static_label);
+ } else {
+ emit_load_local64_to_pair (sym->offset, lo, hi);
+ }
+
+ } else {
+
+ if (sym->is_static && sym->static_label) {
+ emit_load_global_to_reg (lo, sym->static_label, sym->size);
+ } else {
+ emit_load_local_to_reg (lo, sym->offset, sym->size);
+ }
+
+ if (state->ofp) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " xor %s, %s\n", hi, hi);
+ } else {
+ fprintf (state->ofp, " xorl %%%s, %%%s\n", hi, hi);
+ }
+
+ }
+
+ }
+
+ } else if (find_global_symbol (name) >= 0) {
+
+ if (get_global_symbol_size (name) == (DATA_LLONG & 0x1f) && !get_global_symbol_floating (name)) {
+ emit_load_global64_to_pair (lo, hi, name);
+ } else {
+
+ emit_load_global_to_reg (lo, name, get_global_symbol_size (name));
+
+ if (state->ofp) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " xor %s, %s\n", hi, hi);
+ } else {
+ fprintf (state->ofp, " xorl %%%s, %%%s\n", hi, hi);
+ }
+
+ }
+
+ }
+
+ }
+
+ free (name);
+ return 1;
+
+}
+
+static int expression_text_mentions_64bit_symbol (const char *p) {
+
+ char name[256];
+ int depth = 0;
+ int i;
+
+ if (!p) {
+ return 0;
+ }
+
+ while (*p) {
+
+ if (*p == '(') {
+
+ depth++;
+ p++;
+
+ continue;
+
+ }
+
+ if (*p == ')') {
+
+ if (depth <= 0) {
+ return 0;
+ }
+
+ depth--;
+ p++;
+
+ continue;
+
+ }
+
+ if (*p == ';' || *p == '{' || *p == '}') {
+ return 0;
+ }
+
+ if ((unsigned char) *p == '_' || ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z'))) {
+
+ i = 0;
+
+ while ((unsigned char) *p == '_' || ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z')) || (*p >= '0' && *p <= '9')) {
+
+ if (i + 1 < (int) sizeof (name)) {
+ name[i++] = *p;
+ }
+
+ p++;
+
+ }
+
+ name[i] = 0;
+
+ while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+ p++;
+ }
+
+ if (*p == '.' || (p[0] == '-' && p[1] == '>')) {
+ continue;
+ }
+
+ {
+
+ struct local_symbol *sym = find_local_symbol (name);
+
+ if (sym && sym->size == (DATA_LLONG & 0x1f) && !sym->is_floating) {
+ return 1;
+ }
+
+ }
+
+ if (find_global_symbol (name) >= 0 && get_global_symbol_size (name) == (DATA_LLONG & 0x1f) && !get_global_symbol_floating (name)) {
+ return 1;
+ }
+
+ continue;
+
+ }
+
+ p++;
+
+ }
+
+ return 0;
+
+}
+
+static int current_expression_mentions_64bit_symbol_now (void) {
+
+ if (expression_text_mentions_64bit_symbol (tok.start)) {
+ return 1;
+ }
+
+ if (expression_text_mentions_64bit_symbol (tok.caret)) {
+ return 1;
+ }
+
+ return 0;
+
+}
+
+static const char *reg8_name_for_32 (const char *reg) {
+
+ if (strcmp (reg, "eax") == 0) {
+ return "al";
+ }
+
+ if (strcmp (reg, "ebx") == 0) {
+ return "bl";
+ }
+
+ if (strcmp (reg, "ecx") == 0) {
+ return "cl";
+ }
+
+ if (strcmp (reg, "edx") == 0) {
+ return "dl";
+ }
+
+ return "al";
+
+}
+
+static const char *reg16_name_for_32 (const char *reg) {
+
+ if (strcmp (reg, "eax") == 0) {
+ return "ax";
+ }
+
+ if (strcmp (reg, "ebx") == 0) {
+ return "bx";
+ }
+
+ if (strcmp (reg, "ecx") == 0) {
+ return "cx";
+ }
+
+ if (strcmp (reg, "edx") == 0) {
+ return "dx";
+ }
+
+ return "ax";
+
+}
+
+static void emit_extend_pair_high_from_low (const char *lo, const char *hi, int size, int is_unsigned) {
+
+ if (!state->ofp) {
+ return;
+ }
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ if (is_unsigned) {
+
+ if (size == (DATA_CHAR & 0x1f)) {
+ fprintf (state->ofp, " movzx %s, %s\n", lo, reg8_name_for_32 (lo));
+ } else if (size == (DATA_SHORT & 0x1f)) {
+ fprintf (state->ofp, " movzx %s, %s\n", lo, reg16_name_for_32 (lo));
+ }
+
+ fprintf (state->ofp, " xor %s, %s\n", hi, hi);
+
+ } else {
+
+ if (size == (DATA_CHAR & 0x1f)) {
+ fprintf (state->ofp, " movsx %s, %s\n", lo, reg8_name_for_32 (lo));
+ } else if (size == (DATA_SHORT & 0x1f)) {
+ fprintf (state->ofp, " movsx %s, %s\n", lo, reg16_name_for_32 (lo));
+ }
+
+ fprintf (state->ofp, " mov %s, %s\n", hi, lo);
+ fprintf (state->ofp, " sar %s, 31\n", hi);
+
+ }
+
+ } else {
+
+ if (is_unsigned) {
+
+ if (size == (DATA_CHAR & 0x1f)) {
+ fprintf (state->ofp, " movzbl %%%s, %%%s\n", reg8_name_for_32 (lo), lo);
+ } else if (size == (DATA_SHORT & 0x1f)) {
+ fprintf (state->ofp, " movzwl %%%s, %%%s\n", reg16_name_for_32 (lo), lo);
+ }
+
+ fprintf (state->ofp, " xorl %%%s, %%%s\n", hi, hi);
+
+ } else {
+
+ if (size == (DATA_CHAR & 0x1f)) {
+ fprintf (state->ofp, " movsbl %%%s, %%%s\n", reg8_name_for_32 (lo), lo);
+ } else if (size == (DATA_SHORT & 0x1f)) {
+ fprintf (state->ofp, " movswl %%%s, %%%s\n", reg16_name_for_32 (lo), lo);
+ }
+
+ fprintf (state->ofp, " movl %%%s, %%%s\n", lo, hi);
+ fprintf (state->ofp, " sarl $31, %%%s\n", hi);
+
+ }
+
+ }
+
+}
+
+static void emit_apply_integer_cast_to_reg_now (const char *reg, int size, int is_unsigned) {
+
+ if (!state->ofp) {
+ return;
+ }
+
+ if (size != (DATA_CHAR & 0x1f) && size != (DATA_SHORT & 0x1f)) {
+ return;
+ }
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ if (size == (DATA_CHAR & 0x1f)) {
+ fprintf (state->ofp, is_unsigned ? " movzx %s, %s\n" : " movsx %s, %s\n", reg, reg8_name_for_32 (reg));
+ } else {
+ fprintf (state->ofp, is_unsigned ? " movzx %s, %s\n" : " movsx %s, %s\n", reg, reg16_name_for_32 (reg));
+ }
+
+ } else {
+
+ if (size == (DATA_CHAR & 0x1f)) {
+ fprintf (state->ofp, is_unsigned ? " movzbl %%%s, %%%s\n" : " movsbl %%%s, %%%s\n", reg8_name_for_32 (reg), reg);
+ } else {
+ fprintf (state->ofp, is_unsigned ? " movzwl %%%s, %%%s\n" : " movswl %%%s, %%%s\n", reg16_name_for_32 (reg), reg);
+ }
+
+ }
+
+}
+
+static int const_integer_expr_text_is_foldable_now (const char *p) {
+
+ int depth = 0;
+ int saw_token = 0;
+ int ch;
+ char word[64];
+ int i;
+
+ if (!p) {
+ return 0;
+ }
+
+ while (*p) {
+
+ ch = (unsigned char) *p;
+
+ if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n') {
+
+ p++;
+ continue;
+
+ }
+
+ if (depth == 0) {
+
+ if (*p == ';' || *p == ',' || *p == '}' || *p == ']') {
+ break;
+ }
+
+ /*
+ * This helper is only for folding a complete integer expression
+ * operand. Do not claim that a leading parenthesized constant is
+ * foldable when it is followed by a lower-precedence operator such
+ * as the '<' in:
+ *
+ * ((3) < (1 + n)) ? ...
+ *
+ * Returning true there makes the caller consume only '(3)' and then
+ * expect a closing parenthesis while the current token is '<'.
+ */
+ if (*p == ':' || *p == '?') {
+ return 0;
+ }
+
+ if (*p == '<' || *p == '>' || *p == '=' || (*p == '!' && p[1] == '=')) {
+
+ if ((p[0] == '<' && p[1] == '<') || (p[0] == '>' && p[1] == '>')) {
+
+ saw_token = 1;
+
+ p += 2;
+ continue;
+
+ } else {
+ return 0;
+ }
+ }
+
+ if ((p[0] == '&' && p[1] == '&') || (p[0] == '|' && p[1] == '|')) {
+ return 0;
+ }
+
+ }
+
+ if (*p == '(') {
+
+ depth++;
+
+ saw_token = 1;
+ p++;
+
+ continue;
+
+ }
+
+ if (*p == ')') {
+
+ if (depth == 0) {
+ break;
+ }
+
+ depth--;
+ p++;
+
+ continue;
+
+ }
+
+ if (*p >= '0' && *p <= '9') {
+
+ saw_token = 1;
+ p++;
+
+ while ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || (*p >= '0' && *p <= '9') || *p == '_') {
+ p++;
+ }
+
+ continue;
+
+ }
+
+ if (*p == '\'') {
+
+ saw_token = 1;
+ p++;
+
+ while (*p && *p != '\'') {
+
+ if (*p == '\\' && p[1]) {
+ p += 2;
+ } else {
+ p++;
+ }
+
+ }
+
+ if (*p == '\'') {
+ p++;
+ }
+
+ continue;
+
+ }
+
+ if ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || *p == '_') {
+
+ i = 0;
+
+ while ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || (*p >= '0' && *p <= '9') || *p == '_') {
+
+ if (i + 1 < (int) sizeof (word)) {
+ word[i++] = *p;
+ }
+
+ p++;
+
+ }
+
+ word[i] = 0;
+
+ if (strcmp (word, "sizeof") != 0 &&
+ strcmp (word, "char") != 0 &&
+ strcmp (word, "short") != 0 &&
+ strcmp (word, "int") != 0 &&
+ strcmp (word, "long") != 0 &&
+ strcmp (word, "signed") != 0 &&
+ strcmp (word, "unsigned") != 0 &&
+ strcmp (word, "void") != 0) {
+ return 0;
+ }
+
+ saw_token = 1;
+ continue;
+
+ }
+
+ if (*p == '+' || *p == '-' || *p == '*' || *p == '/' || *p == '%' || *p == '&' || *p == '|' || *p == '^' || *p == '~' || *p == '!' || *p == '<' || *p == '>') {
+
+ saw_token = 1;
+ p++;
+
+ continue;
+
+ }
+
+ return 0;
+
+ }
+
+ return saw_token;
+
+}
+
+static void emit_load_const64_to_pair_now (const char *lo, const char *hi, int64_s v) {
+
+ if (!state->ofp) {
+ return;
+ }
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ fprintf (state->ofp, " mov %s, %lu\n", lo, v.low & U32_MASK);
+ fprintf (state->ofp, " mov %s, %lu\n", hi, v.high & U32_MASK);
+
+ } else {
+
+ fprintf (state->ofp, " movl $%lu, %%%s\n", v.low & U32_MASK, lo);
+ fprintf (state->ofp, " movl $%lu, %%%s\n", v.high & U32_MASK, hi);
+
+ }
+
+}
+
+static void emit_load_const32_to_reg_now (const char *reg, int64_s v) {
+
+ flush_pending_statement_labels ();
+
+ if (!state->ofp) {
+ return;
+ }
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " mov %s, %lu\n", reg, v.low & U32_MASK);
+ } else {
+ fprintf (state->ofp, " movl $%lu, %%%s\n", v.low & U32_MASK, reg);
+ }
+
+}
+
+static void emit_load_address_to_reg_now (const char *reg, const char *symbol) {
+
+ const char *asm_symbol;
+ flush_pending_statement_labels ();
+
+ if (!state->ofp || !symbol || !*symbol) {
+ return;
+ }
+
+ emit_extern_reference_symbol (symbol, DATA_PTR);
+ asm_symbol = asm_global_symbol_name (symbol);
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ if (state->syntax & ASM_SYNTAX_NASM) {
+ fprintf (state->ofp, " mov %s, %s\n", reg, asm_symbol);
+ } else {
+ fprintf (state->ofp, " mov %s, offset %s\n", reg, asm_symbol);
+ }
+
+ } else {
+ fprintf (state->ofp, " movl $%s, %%%s\n", asm_symbol, reg);
+ }
+
+}
+
+static void emit_load_deref_reg_now (const char *reg, int size);
+
+static int emit_load_parenthesized_indirect_member_to_reg_now (const char *reg);
+static int emit_parse_postfix_copy_source_address_now (const char *reg, struct local_symbol *src, const char *src_name, const char *name_start, const char *name_caret, unsigned long name_line);
+
+static void emit_load_postfix_lvalue_address_to_pair_now (const char *lo, const char *hi, int size) {
+
+ if (!state->ofp) {
+ return;
+ }
+
+ if (size > (DATA_PTR & 0x1f)) {
+
+ emit_push_reg_now (lo);
+
+ emit_load_deref_reg_now (lo, DATA_PTR & 0x1f);
+ emit_pop_reg_now (hi);
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ if (state->syntax & ASM_SYNTAX_NASM) {
+ fprintf (state->ofp, " mov %s, dword [%s + 4]\n", hi, hi);
+ } else {
+ fprintf (state->ofp, " mov %s, dword ptr [%s + 4]\n", hi, hi);
+ }
+
+ } else {
+ fprintf (state->ofp, " movl 4(%%%s), %%%s\n", hi, hi);
+ }
+
+ } else {
+
+ emit_load_deref_reg_now (lo, size);
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " xor %s, %s\n", hi, hi);
+ } else {
+ fprintf (state->ofp, " xorl %%%s, %%%s\n", hi, hi);
+ }
+
+ }
+
+}
+
+static void emit_store_pair_to_deref_reg_now (const char *addr_reg, const char *lo, const char *hi) {
+
+ if (!state->ofp) {
+ return;
+ }
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ if (state->syntax & ASM_SYNTAX_NASM) {
+
+ fprintf (state->ofp, " mov dword [%s], %s\n", addr_reg, lo);
+ fprintf (state->ofp, " mov dword [%s + 4], %s\n", addr_reg, hi);
+
+ } else {
+
+ fprintf (state->ofp, " mov dword ptr [%s], %s\n", addr_reg, lo);
+ fprintf (state->ofp, " mov dword ptr [%s + 4], %s\n", addr_reg, hi);
+
+ }
+
+ } else {
+
+ fprintf (state->ofp, " movl %%%s, (%%%s)\n", lo, addr_reg);
+ fprintf (state->ofp, " movl %%%s, 4(%%%s)\n", hi, addr_reg);
+
+ }
+
+}
+
+static void emit_load_pair_from_deref_reg_now (const char *lo, const char *hi, const char *addr_reg) {
+
+ if (!state->ofp) {
+ return;
+ }
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ if (state->syntax & ASM_SYNTAX_NASM) {
+
+ fprintf (state->ofp, " mov %s, dword [%s]\n", lo, addr_reg);
+ fprintf (state->ofp, " mov %s, dword [%s + 4]\n", hi, addr_reg);
+
+ } else {
+
+ fprintf (state->ofp, " mov %s, dword ptr [%s]\n", lo, addr_reg);
+ fprintf (state->ofp, " mov %s, dword ptr [%s + 4]\n", hi, addr_reg);
+
+ }
+
+ } else {
+
+ fprintf (state->ofp, " movl (%%%s), %%%s\n", addr_reg, lo);
+ fprintf (state->ofp, " movl 4(%%%s), %%%s\n", addr_reg, hi);
+
+ }
+
+}
+
+static void emit_copy_reg_now (const char *dst, const char *src) {
+
+ if (!state->ofp || !dst || !src || strcmp (dst, src) == 0) {
+ return;
+ }
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " mov %s, %s\n", dst, src);
+ } else {
+ fprintf (state->ofp, " movl %%%s, %%%s\n", src, dst);
+ }
+
+}
+
+static void emit_load_assignment_rhs_to_pair (const char *lo, const char *hi) {
+
+ if (_accept (TOK_LPAREN)) {
+
+ if (token_starts_type_name ()) {
+
+ int cast_size = 0;
+ int cast_is_unsigned = 0;
+ int cast_is_pointer = 0;
+
+ if (parse_cast_type_name (&cast_size, &cast_is_unsigned, &cast_is_pointer)) {
+
+ emit_load_assignment_rhs_to_pair (lo, hi);
+
+ {
+
+ int applied_postfix = 0;
+
+ if (cast_is_pointer) {
+ set_rhs_last_pointer_info (1, cast_size > 0 ? cast_size : (DATA_INT & 0x1f));
+ } else {
+ emit_extend_pair_high_from_low (lo, hi, cast_size, cast_is_unsigned);
+ }
+
+ postfix_member_seen = 0;
+
+ while (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) {
+
+ applied_postfix = 1;
+ emit_apply_postfix_member_access_to_reg_now (lo);
+
+ }
+
+ if (applied_postfix && postfix_member_seen) {
+
+ if (postfix_member_size == (DATA_LLONG & 0x1f) && !postfix_member_is_floating) {
+
+ const char *addr_reg = (strcmp (lo, "ecx") != 0 && strcmp (hi, "ecx") != 0) ? "ecx" : "esi";
+
+ emit_copy_reg_now (addr_reg, lo);
+ emit_load_pair_from_deref_reg_now (lo, hi, addr_reg);
+
+ } else {
+ emit_extend_pair_high_from_low (lo, hi, postfix_member_size, postfix_member_is_unsigned);
+ }
+
+ }
+
+ }
+
+ return;
+
+ }
+
+ }
+
+ if (const_integer_expr_text_is_foldable_now (tok.caret)) {
+
+ int64_s v = const64_from_current_foldable_expr ();
+ expect (TOK_RPAREN, ")");
+
+ emit_load_const64_to_pair_now (lo, hi, v);
+ return;
+
+ }
+
+ if (emit_load_parenthesized_indirect_member_to_reg_now (lo)) {
+
+ emit_extend_pair_high_from_low (lo, hi, DATA_INT & 0x1f, 1);
+ return;
+
+ }
+
+ emit_load_assignment_rhs_expression_to_pair (lo, hi, 1);
+ expect (TOK_RPAREN, ")");
+
+ {
+
+ int applied_postfix = 0;
+ postfix_member_seen = 0;
+
+ while (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) {
+
+ applied_postfix = 1;
+ emit_apply_postfix_member_access_to_reg_now (lo);
+
+ }
+
+ if (applied_postfix && postfix_member_seen) {
+
+ if (postfix_member_size == (DATA_LLONG & 0x1f) && !postfix_member_is_floating) {
+
+ const char *addr_reg = (strcmp (lo, "ecx") != 0 && strcmp (hi, "ecx") != 0) ? "ecx" : "esi";
+
+ emit_copy_reg_now (addr_reg, lo);
+ emit_load_pair_from_deref_reg_now (lo, hi, addr_reg);
+
+ } else {
+ emit_extend_pair_high_from_low (lo, hi, postfix_member_size, postfix_member_is_unsigned);
+ }
+
+ }
+
+ }
+
+ return;
+
+ }
+
+ if (tok.kind == TOK_PLUS || tok.kind == TOK_MINUS || tok.kind == TOK_TILDE || tok.kind == TOK_XMARK) {
+
+ enum token_kind unary_op = tok.kind;
+ get_token ();
+
+ emit_load_assignment_rhs_to_pair (lo, hi);
+
+ if (state->ofp) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ if (unary_op == TOK_MINUS) {
+
+ fprintf (state->ofp, " not %s\n", lo);
+ fprintf (state->ofp, " not %s\n", hi);
+
+ fprintf (state->ofp, " add %s, 1\n", lo);
+ fprintf (state->ofp, " adc %s, 0\n", hi);
+
+ } else if (unary_op == TOK_TILDE) {
+
+ fprintf (state->ofp, " not %s\n", lo);
+ fprintf (state->ofp, " not %s\n", hi);
+
+ } else if (unary_op == TOK_XMARK) {
+
+ fprintf (state->ofp, " or %s, %s\n", lo, hi);
+ fprintf (state->ofp, " setz al\n");
+ fprintf (state->ofp, " movzx eax, al\n");
+
+ if (strcmp (lo, "eax") != 0) {
+ fprintf (state->ofp, " mov %s, eax\n", lo);
+ }
+
+ fprintf (state->ofp, " xor %s, %s\n", hi, hi);
+
+ }
+
+ } else {
+
+ if (unary_op == TOK_MINUS) {
+
+ fprintf (state->ofp, " notl %%%s\n", lo);
+ fprintf (state->ofp, " notl %%%s\n", hi);
+
+ fprintf (state->ofp, " addl $1, %%%s\n", lo);
+ fprintf (state->ofp, " adcl $0, %%%s\n", hi);
+
+ } else if (unary_op == TOK_TILDE) {
+
+ fprintf (state->ofp, " notl %%%s\n", lo);
+ fprintf (state->ofp, " notl %%%s\n", hi);
+
+ } else if (unary_op == TOK_XMARK) {
+
+ fprintf (state->ofp, " orl %%%s, %%%s\n", hi, lo);
+ fprintf (state->ofp, " setz %%al\n");
+ fprintf (state->ofp, " movzbl %%al, %%eax\n");
+
+ if (strcmp (lo, "eax") != 0) {
+ fprintf (state->ofp, " movl %%eax, %%%s\n", lo);
+ }
+
+ fprintf (state->ofp, " xorl %%%s, %%%s\n", hi, hi);
+
+ }
+
+ }
+
+ }
+
+ return;
+
+ }
+
+ if (emit_load_prefix_incdec_to_pair_now (lo, hi)) {
+ return;
+ }
+
+ if (tok.kind == TOK_STAR) {
+
+ char *name;
+
+ const char *name_start;
+ const char *name_caret;
+
+ unsigned long name_line;
+ struct local_symbol *src;
+
+ int global_index;
+
+ int deref_size = DATA_INT & 0x1f;
+ int deref_unsigned = 1;
+
+ int postfix_incdec = 0;
+
+ enum token_kind postfix_op = TOK_EOF;
+ get_token ();
+
+ name_start = tok.start;
+ name_caret = tok.caret;
+ name_line = get_line_number ();
+
+ if (tok.kind != TOK_IDENT || !tok.ident) {
+
+ int64_s zero;
+
+ zero.low = 0;
+ zero.high = 0;
+
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "expected identifier after *");
+
+ emit_load_const64_to_pair_now (lo, hi, zero);
+ return;
+
+ }
+
+ name = xstrdup (tok.ident);
+ get_token ();
+
+ if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) {
+
+ postfix_incdec = 1;
+ postfix_op = tok.kind;
+
+ get_token ();
+
+ }
+
+ src = find_local_symbol (name);
+ global_index = find_global_symbol (name);
+
+ if (src) {
+
+ if (src->pointer_depth > 1) {
+ deref_size = DATA_PTR & 0x1f;
+ } else if (src->pointer_depth == 1 && src->pointed_size > 0) {
+ deref_size = src->pointed_size & 0x1f;
+ }
+
+ if (src->is_static && src->static_label) {
+ emit_load_global_to_reg (lo, src->static_label, DATA_PTR);
+ } else {
+ emit_load_local_to_reg (lo, src->offset, DATA_PTR);
+ }
+
+ } else if (global_index >= 0) {
+
+ if (get_global_symbol_pointer_depth (name) > 1) {
+ deref_size = DATA_PTR & 0x1f;
+ } else if (get_global_symbol_pointer_depth (name) == 1 && get_global_symbol_pointed_size (name) > 0) {
+ deref_size = get_global_symbol_pointed_size (name) & 0x1f;
+ }
+
+ emit_load_global_to_reg (lo, name, DATA_PTR);
+
+ } else {
+
+ int64_s zero;
+
+ zero.low = 0;
+ zero.high = 0;
+
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+
+ emit_load_const64_to_pair_now (lo, hi, zero);
+ free (name);
+
+ return;
+
+ }
+
+ if (deref_size == (DATA_LLONG & 0x1f)) {
+
+ emit_push_reg_now (lo);
+
+ emit_load_deref_reg_now (lo, DATA_INT & 0x1f);
+ emit_pop_reg_now (hi);
+
+ if (state->ofp) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ if (state->syntax & ASM_SYNTAX_NASM) {
+ fprintf (state->ofp, " mov %s, dword [%s + 4]\n", hi, hi);
+ } else {
+ fprintf (state->ofp, " mov %s, dword ptr [%s + 4]\n", hi, hi);
+ }
+
+ } else {
+ fprintf (state->ofp, " movl 4(%%%s), %%%s\n", hi, hi);
+ }
+
+ }
+
+ } else {
+
+ emit_load_deref_reg_now (lo, deref_size);
+ emit_extend_pair_high_from_low (lo, hi, deref_size, deref_unsigned);
+
+ }
+
+ if (postfix_incdec) {
+
+ emit_push_reg_now (lo);
+ emit_push_reg_now (hi);
+
+ emit_incdec_symbol_now (src, name, postfix_op, name_line, name_start, name_caret);
+
+ emit_pop_reg_now (hi);
+ emit_pop_reg_now (lo);
+
+ }
+
+ free (name);
+ return;
+
+ }
+
+ if (token_is_sizeof_keyword ()) {
+
+ int64_s v = sizeof_from_current_token ();
+
+ if (state->ofp) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ fprintf (state->ofp, " mov %s, %lu\n", lo, v.low & U32_MASK);
+ fprintf (state->ofp, " mov %s, %lu\n", hi, v.high & U32_MASK);
+
+ } else {
+
+ fprintf (state->ofp, " movl $%lu, %%%s\n", v.low & U32_MASK, lo);
+ fprintf (state->ofp, " movl $%lu, %%%s\n", v.high & U32_MASK, hi);
+
+ }
+
+ }
+
+ return;
+
+ }
+
+ if (tok.kind == TOK_IDENT) {
+
+ enum token_kind postfix_op = TOK_EOF;
+ char *name = xstrdup (tok.ident);
+
+ const char *name_start = tok.start, *name_caret = tok.caret;
+ unsigned long name_line = get_line_number ();
+
+ int postfix_incdec = 0;
+
+ struct local_symbol *src;
+ int64_s enum_value;
+
+ get_token ();
+
+ if (!find_local_symbol (name) && find_global_symbol (name) < 0 && resolve_enum_constant (name, &enum_value)) {
+
+ emit_load_const64_to_pair_now (lo, hi, enum_value);
+
+ free (name);
+ return;
+
+ }
+
+ if (tok.kind == TOK_LPAREN) {
+
+ if (!find_local_symbol (name)) {
+ ensure_global_function_symbol (name, name_start, name_caret, name_line);
+ }
+
+ if (get_global_symbol_kind (name) == GLOBAL_SYMBOL_FUNCTION && get_global_symbol_returns_void (name)) {
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "void function '%s' used as a value", name);
+ }
+
+ emit_call_identifier_to_reg_now (name, "eax", name_start, name_caret, name_line);
+
+ if (state->ofp) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ if (strcmp (lo, "eax") != 0) {
+ fprintf (state->ofp, " mov %s, eax\n", lo);
+ }
+
+ if (strcmp (hi, "edx") != 0) {
+ fprintf (state->ofp, " mov %s, edx\n", hi);
+ }
+
+ } else {
+
+ if (strcmp (lo, "eax") != 0) {
+ fprintf (state->ofp, " movl %%eax, %%%s\n", lo);
+ }
+
+ if (strcmp (hi, "edx") != 0) {
+ fprintf (state->ofp, " movl %%edx, %%%s\n", hi);
+ }
+
+ }
+
+ }
+
+ free (name);
+ return;
+
+ }
+
+ if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) {
+
+ postfix_incdec = 1;
+ postfix_op = tok.kind;
+
+ get_token ();
+
+ }
+
+ src = find_local_symbol (name);
+
+ if (tok.kind == TOK_ARROW || tok.kind == TOK_DOT || tok.kind == TOK_LBRACK) {
+
+ int source_size;
+
+ if (src || find_global_symbol (name) >= 0) {
+
+ if (tok.kind == TOK_ARROW || tok.kind == TOK_LBRACK) {
+
+ if (src) {
+
+ postfix_copy_lvalue_size = src->pointer_depth > 0 ? src->pointed_size : src->size;
+ postfix_copy_lvalue_tag_name = src->pointer_depth > 0 ? src->pointed_tag_name : src->tag_name;
+
+ } else {
+
+ postfix_copy_lvalue_size = get_global_symbol_pointer_depth (name) > 0 ? get_global_symbol_pointed_size (name) : get_global_symbol_size (name);
+ postfix_copy_lvalue_tag_name = 0;
+
+ }
+
+ } else if (src) {
+
+ postfix_copy_lvalue_size = src->size;
+ postfix_copy_lvalue_tag_name = 0;
+
+ } else {
+
+ postfix_copy_lvalue_size = get_global_symbol_size (name);
+ postfix_copy_lvalue_tag_name = get_global_symbol_tag_name (name);
+
+ }
+
+ if (!emit_parse_postfix_copy_source_address_now (lo, src, name, name_start, name_caret, name_line)) {
+
+ free (name);
+ return;
+
+ }
+
+ source_size = postfix_copy_lvalue_size;
+
+ if (source_size <= 0) {
+ source_size = DATA_INT & 0x1f;
+ }
+
+ if (is_assignment_operator (tok.kind)) {
+
+ enum token_kind assign_op = tok.kind;
+
+ const char *addr_reg = (strcmp (lo, "ecx") != 0 && strcmp (hi, "ecx") != 0) ? "ecx" : "edx";
+ int lvalue_size = index_step_size (source_size);
+
+ get_token ();
+
+ if (assign_op == TOK_ASSIGN && lvalue_size == (DATA_LLONG & 0x1f)) {
+
+ emit_push_reg_now (lo);
+
+ emit_load_assignment_rhs_expression_to_pair (lo, hi, 1);
+ emit_pop_reg_now (addr_reg);
+
+ emit_store_pair_to_deref_reg_now (addr_reg, lo, hi);
+
+ free (name);
+ return;
+
+ }
+
+ if (assign_op == TOK_ASSIGN) {
+
+ emit_push_reg_now (lo);
+
+ emit_load_assignment_rhs_expression_to_reg (lo);
+ emit_extend_pair_high_from_low (lo, hi, lvalue_size, 1);
+
+ emit_pop_reg_now (addr_reg);
+ emit_store_reg_to_deref_reg_now (addr_reg, lo, lvalue_size);
+
+ free (name);
+ return;
+
+ }
+
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "compound member assignment expression not implemented in 64-bit context");
+
+ free (name);
+ return;
+
+ }
+
+ emit_load_postfix_lvalue_address_to_pair_now (lo, hi, source_size);
+
+ free (name);
+ return;
+
+ }
+
+ }
+
+ if (src) {
+
+ if (src->size == (DATA_LLONG & 0x1f) && !src->is_floating) {
+
+ if (src->is_static && src->static_label) {
+ emit_load_global64_to_pair (lo, hi, src->static_label);
+ } else {
+ emit_load_local64_to_pair (src->offset, lo, hi);
+ }
+
+ } else {
+
+ if (src->is_static && src->static_label) {
+ emit_load_global_to_reg (lo, src->static_label, src->size);
+ } else {
+ emit_load_local_to_reg (lo, src->offset, src->size);
+ }
+
+ emit_extend_pair_high_from_low (lo, hi, src->size, src->is_unsigned);
+
+ }
+
+ if (postfix_incdec) {
+
+ emit_push_reg_now (lo);
+ emit_push_reg_now (hi);
+
+ emit_incdec_symbol_now (src, name, postfix_op, name_line, name_start, name_caret);
+
+ emit_pop_reg_now (hi);
+ emit_pop_reg_now (lo);
+
+ }
+
+ free (name);
+ return;
+
+ }
+
+ if (find_global_symbol (name) >= 0) {
+
+ if (get_global_symbol_kind (name) == GLOBAL_SYMBOL_FUNCTION ||
+ get_global_symbol_array (name) ||
+ (!get_global_symbol_pointer_depth (name) &&
+ get_global_symbol_size (name) > (DATA_PTR & 0x1f))) {
+
+ /*
+ * In a 64-bit/pair context, an array/function/aggregate
+ * expression still decays to its address. The 32-bit RHS
+ * path already handled this, but this path was used when
+ * the caller requested eax:edx (for example a mis-sized
+ * pointer return). Loading the first word of the global
+ * table produced bogus pseudo-op table pointers such as the
+ * first string pointer instead of &table.
+ */
+ emit_load_address_to_reg_now (lo, name);
+ if (state->ofp) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " xor %s, %s\n", hi, hi);
+ } else {
+ fprintf (state->ofp, " xorl %%%s, %%%s\n", hi, hi);
+ }
+
+ }
+
+ } else if (get_global_symbol_size (name) == (DATA_LLONG & 0x1f) && !get_global_symbol_floating (name)) {
+ emit_load_global64_to_pair (lo, hi, name);
+ } else {
+
+ emit_load_global_to_reg (lo, name, get_global_symbol_size (name));
+ emit_extend_pair_high_from_low (lo, hi, get_global_symbol_size (name), get_global_symbol_unsigned (name));
+
+ }
+
+ if (postfix_incdec) {
+
+ emit_push_reg_now (lo);
+ emit_push_reg_now (hi);
+
+ emit_incdec_symbol_now (0, name, postfix_op, name_line, name_start, name_caret);
+
+ emit_pop_reg_now (hi);
+ emit_pop_reg_now (lo);
+
+ }
+
+ free (name);
+ return;
+
+ }
+
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+ free (name);
+
+ if (state->ofp) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ fprintf (state->ofp, " xor %s, %s\n", lo, lo);
+ fprintf (state->ofp, " xor %s, %s\n", hi, hi);
+
+ } else {
+
+ fprintf (state->ofp, " xorl %%%s, %%%s\n", lo, lo);
+ fprintf (state->ofp, " xorl %%%s, %%%s\n", hi, hi);
+
+ }
+
+ }
+
+ return;
+
+ }
+
+ if (recover_unknown_rhs_identifier ()) {
+
+ if (state->ofp) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ fprintf (state->ofp, " xor %s, %s\n", lo, lo);
+ fprintf (state->ofp, " xor %s, %s\n", hi, hi);
+
+ } else {
+
+ fprintf (state->ofp, " xorl %%%s, %%%s\n", lo, lo);
+ fprintf (state->ofp, " xorl %%%s, %%%s\n", hi, hi);
+
+ }
+
+ }
+
+ return;
+
+ }
+
+ {
+
+ int64_s v = const64_from_current_operand ();
+
+ if (state->ofp) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ fprintf (state->ofp, " mov %s, %lu\n", lo, v.low & U32_MASK);
+ fprintf (state->ofp, " mov %s, %lu\n", hi, v.high & U32_MASK);
+
+ } else {
+
+ fprintf (state->ofp, " movl $%lu, %%%s\n", v.low & U32_MASK, lo);
+ fprintf (state->ofp, " movl $%lu, %%%s\n", v.high & U32_MASK, hi);
+
+ }
+
+ }
+
+ }
+
+}
+
+static void emit_assignment_divmod64_intel (int is_unsigned, int want_mod) {
+
+ int loop_label, sub_label, nosub_label;
+ int lhs_pos_label, rhs_pos_label;
+ int qpos_label, rpos_label;
+
+ loop_label = anon_label++;
+ sub_label = anon_label++;
+ nosub_label = anon_label++;
+ lhs_pos_label = anon_label++;
+ rhs_pos_label = anon_label++;
+ qpos_label = anon_label++;
+ rpos_label = anon_label++;
+
+ if (state->syntax & ASM_SYNTAX_MASM) {
+
+ fprintf (state->ofp, " sub esp, 40\n");
+ fprintf (state->ofp, " mov dword ptr [esp], eax\n");
+ fprintf (state->ofp, " mov dword ptr [esp + 4], edx\n");
+ fprintf (state->ofp, " mov dword ptr [esp + 8], ebx\n");
+ fprintf (state->ofp, " mov dword ptr [esp + 12], ecx\n");
+ fprintf (state->ofp, " xor eax, eax\n");
+ fprintf (state->ofp, " mov dword ptr [esp + 16], eax\n");
+ fprintf (state->ofp, " mov dword ptr [esp + 20], eax\n");
+ fprintf (state->ofp, " mov dword ptr [esp + 24], eax\n");
+ fprintf (state->ofp, " mov dword ptr [esp + 28], eax\n");
+ fprintf (state->ofp, " mov dword ptr [esp + 32], eax\n");
+ fprintf (state->ofp, " mov dword ptr [esp + 36], eax\n");
+
+ if (!is_unsigned) {
+
+ fprintf (state->ofp, " test dword ptr [esp + 4], 80000000h\n");
+ fprintf (state->ofp, " jz L%d\n", lhs_pos_label);
+ fprintf (state->ofp, " not dword ptr [esp]\n");
+ fprintf (state->ofp, " not dword ptr [esp + 4]\n");
+ fprintf (state->ofp, " add dword ptr [esp], 1\n");
+ fprintf (state->ofp, " adc dword ptr [esp + 4], 0\n");
+ fprintf (state->ofp, " mov dword ptr [esp + 32], 1\n");
+ fprintf (state->ofp, " xor dword ptr [esp + 36], 1\n");
+ fprintf (state->ofp, "L%d:\n", lhs_pos_label);
+ fprintf (state->ofp, " test dword ptr [esp + 12], 80000000h\n");
+ fprintf (state->ofp, " jz L%d\n", rhs_pos_label);
+ fprintf (state->ofp, " not dword ptr [esp + 8]\n");
+ fprintf (state->ofp, " not dword ptr [esp + 12]\n");
+ fprintf (state->ofp, " add dword ptr [esp + 8], 1\n");
+ fprintf (state->ofp, " adc dword ptr [esp + 12], 0\n");
+ fprintf (state->ofp, " xor dword ptr [esp + 36], 1\n");
+ fprintf (state->ofp, "L%d:\n", rhs_pos_label);
+
+ }
+
+ fprintf (state->ofp, " mov esi, 64\n");
+ fprintf (state->ofp, "L%d:\n", loop_label);
+ fprintf (state->ofp, " shl dword ptr [esp], 1\n");
+ fprintf (state->ofp, " rcl dword ptr [esp + 4], 1\n");
+ fprintf (state->ofp, " rcl dword ptr [esp + 24], 1\n");
+ fprintf (state->ofp, " rcl dword ptr [esp + 28], 1\n");
+ fprintf (state->ofp, " shl dword ptr [esp + 16], 1\n");
+ fprintf (state->ofp, " rcl dword ptr [esp + 20], 1\n");
+ fprintf (state->ofp, " mov eax, dword ptr [esp + 28]\n");
+ fprintf (state->ofp, " cmp eax, dword ptr [esp + 12]\n");
+ fprintf (state->ofp, " ja L%d\n", sub_label);
+ fprintf (state->ofp, " jb L%d\n", nosub_label);
+ fprintf (state->ofp, " mov eax, dword ptr [esp + 24]\n");
+ fprintf (state->ofp, " cmp eax, dword ptr [esp + 8]\n");
+ fprintf (state->ofp, " jb L%d\n", nosub_label);
+ fprintf (state->ofp, "L%d:\n", sub_label);
+ fprintf (state->ofp, " mov eax, dword ptr [esp + 8]\n");
+ fprintf (state->ofp, " sub dword ptr [esp + 24], eax\n");
+ fprintf (state->ofp, " mov eax, dword ptr [esp + 12]\n");
+ fprintf (state->ofp, " sbb dword ptr [esp + 28], eax\n");
+ fprintf (state->ofp, " or dword ptr [esp + 16], 1\n");
+ fprintf (state->ofp, "L%d:\n", nosub_label);
+ fprintf (state->ofp, " dec esi\n");
+ fprintf (state->ofp, " jnz L%d\n", loop_label);
+
+ if (!is_unsigned) {
+
+ fprintf (state->ofp, " cmp dword ptr [esp + 36], 0\n");
+ fprintf (state->ofp, " je L%d\n", qpos_label);
+ fprintf (state->ofp, " not dword ptr [esp + 16]\n");
+ fprintf (state->ofp, " not dword ptr [esp + 20]\n");
+ fprintf (state->ofp, " add dword ptr [esp + 16], 1\n");
+ fprintf (state->ofp, " adc dword ptr [esp + 20], 0\n");
+ fprintf (state->ofp, "L%d:\n", qpos_label);
+ fprintf (state->ofp, " cmp dword ptr [esp + 32], 0\n");
+ fprintf (state->ofp, " je L%d\n", rpos_label);
+ fprintf (state->ofp, " not dword ptr [esp + 24]\n");
+ fprintf (state->ofp, " not dword ptr [esp + 28]\n");
+ fprintf (state->ofp, " add dword ptr [esp + 24], 1\n");
+ fprintf (state->ofp, " adc dword ptr [esp + 28], 0\n");
+ fprintf (state->ofp, "L%d:\n", rpos_label);
+
+ }
+
+ if (want_mod) {
+
+ fprintf (state->ofp, " mov eax, dword ptr [esp + 24]\n");
+ fprintf (state->ofp, " mov edx, dword ptr [esp + 28]\n");
+
+ } else {
+
+ fprintf (state->ofp, " mov eax, dword ptr [esp + 16]\n");
+ fprintf (state->ofp, " mov edx, dword ptr [esp + 20]\n");
+
+ }
+
+ } else if (state->syntax & ASM_SYNTAX_NASM) {
+
+ fprintf (state->ofp, " sub esp, 40\n");
+ fprintf (state->ofp, " mov dword [esp], eax\n");
+ fprintf (state->ofp, " mov dword [esp + 4], edx\n");
+ fprintf (state->ofp, " mov dword [esp + 8], ebx\n");
+ fprintf (state->ofp, " mov dword [esp + 12], ecx\n");
+ fprintf (state->ofp, " xor eax, eax\n");
+ fprintf (state->ofp, " mov dword [esp + 16], eax\n");
+ fprintf (state->ofp, " mov dword [esp + 20], eax\n");
+ fprintf (state->ofp, " mov dword [esp + 24], eax\n");
+ fprintf (state->ofp, " mov dword [esp + 28], eax\n");
+ fprintf (state->ofp, " mov dword [esp + 32], eax\n");
+ fprintf (state->ofp, " mov dword [esp + 36], eax\n");
+
+ if (!is_unsigned) {
+
+ fprintf (state->ofp, " test dword [esp + 4], 80000000h\n");
+ fprintf (state->ofp, " jz L%d\n", lhs_pos_label);
+ fprintf (state->ofp, " not dword [esp]\n");
+ fprintf (state->ofp, " not dword [esp + 4]\n");
+ fprintf (state->ofp, " add dword [esp], 1\n");
+ fprintf (state->ofp, " adc dword [esp + 4], 0\n");
+ fprintf (state->ofp, " mov dword [esp + 32], 1\n");
+ fprintf (state->ofp, " xor dword [esp + 36], 1\n");
+ fprintf (state->ofp, "L%d:\n", lhs_pos_label);
+ fprintf (state->ofp, " test dword [esp + 12], 80000000h\n");
+ fprintf (state->ofp, " jz L%d\n", rhs_pos_label);
+ fprintf (state->ofp, " not dword [esp + 8]\n");
+ fprintf (state->ofp, " not dword [esp + 12]\n");
+ fprintf (state->ofp, " add dword [esp + 8], 1\n");
+ fprintf (state->ofp, " adc dword [esp + 12], 0\n");
+ fprintf (state->ofp, " xor dword [esp + 36], 1\n");
+ fprintf (state->ofp, "L%d:\n", rhs_pos_label);
+
+ }
+
+ fprintf (state->ofp, " mov esi, 64\n");
+ fprintf (state->ofp, "L%d:\n", loop_label);
+ fprintf (state->ofp, " shl dword [esp], 1\n");
+ fprintf (state->ofp, " rcl dword [esp + 4], 1\n");
+ fprintf (state->ofp, " rcl dword [esp + 24], 1\n");
+ fprintf (state->ofp, " rcl dword [esp + 28], 1\n");
+ fprintf (state->ofp, " shl dword [esp + 16], 1\n");
+ fprintf (state->ofp, " rcl dword [esp + 20], 1\n");
+ fprintf (state->ofp, " mov eax, dword [esp + 28]\n");
+ fprintf (state->ofp, " cmp eax, dword [esp + 12]\n");
+ fprintf (state->ofp, " ja L%d\n", sub_label);
+ fprintf (state->ofp, " jb L%d\n", nosub_label);
+ fprintf (state->ofp, " mov eax, dword [esp + 24]\n");
+ fprintf (state->ofp, " cmp eax, dword [esp + 8]\n");
+ fprintf (state->ofp, " jb L%d\n", nosub_label);
+ fprintf (state->ofp, "L%d:\n", sub_label);
+ fprintf (state->ofp, " mov eax, dword [esp + 8]\n");
+ fprintf (state->ofp, " sub dword [esp + 24], eax\n");
+ fprintf (state->ofp, " mov eax, dword [esp + 12]\n");
+ fprintf (state->ofp, " sbb dword [esp + 28], eax\n");
+ fprintf (state->ofp, " or dword [esp + 16], 1\n");
+ fprintf (state->ofp, "L%d:\n", nosub_label);
+ fprintf (state->ofp, " dec esi\n");
+ fprintf (state->ofp, " jnz L%d\n", loop_label);
+
+ if (!is_unsigned) {
+
+ fprintf (state->ofp, " cmp dword [esp + 36], 0\n");
+ fprintf (state->ofp, " je L%d\n", qpos_label);
+ fprintf (state->ofp, " not dword [esp + 16]\n");
+ fprintf (state->ofp, " not dword [esp + 20]\n");
+ fprintf (state->ofp, " add dword [esp + 16], 1\n");
+ fprintf (state->ofp, " adc dword [esp + 20], 0\n");
+ fprintf (state->ofp, "L%d:\n", qpos_label);
+ fprintf (state->ofp, " cmp dword [esp + 32], 0\n");
+ fprintf (state->ofp, " je L%d\n", rpos_label);
+ fprintf (state->ofp, " not dword [esp + 24]\n");
+ fprintf (state->ofp, " not dword [esp + 28]\n");
+ fprintf (state->ofp, " add dword [esp + 24], 1\n");
+ fprintf (state->ofp, " adc dword [esp + 28], 0\n");
+ fprintf (state->ofp, "L%d:\n", rpos_label);
+
+ }
+
+ if (want_mod) {
+
+ fprintf (state->ofp, " mov eax, dword [esp + 24]\n");
+ fprintf (state->ofp, " mov edx, dword [esp + 28]\n");
+
+ } else {
+
+ fprintf (state->ofp, " mov eax, dword [esp + 16]\n");
+ fprintf (state->ofp, " mov edx, dword [esp + 20]\n");
+
+ }
+
+ } else {
+
+ fprintf (state->ofp, " sub esp, 40\n");
+ fprintf (state->ofp, " mov dword ptr [esp], eax\n");
+ fprintf (state->ofp, " mov dword ptr [esp + 4], edx\n");
+ fprintf (state->ofp, " mov dword ptr [esp + 8], ebx\n");
+ fprintf (state->ofp, " mov dword ptr [esp + 12], ecx\n");
+ fprintf (state->ofp, " xor eax, eax\n");
+ fprintf (state->ofp, " mov dword ptr [esp + 16], eax\n");
+ fprintf (state->ofp, " mov dword ptr [esp + 20], eax\n");
+ fprintf (state->ofp, " mov dword ptr [esp + 24], eax\n");
+ fprintf (state->ofp, " mov dword ptr [esp + 28], eax\n");
+ fprintf (state->ofp, " mov dword ptr [esp + 32], eax\n");
+ fprintf (state->ofp, " mov dword ptr [esp + 36], eax\n");
+
+ if (!is_unsigned) {
+
+ fprintf (state->ofp, " test dword ptr [esp + 4], 80000000h\n");
+ fprintf (state->ofp, " jz .L%d\n", lhs_pos_label);
+ fprintf (state->ofp, " not dword ptr [esp]\n");
+ fprintf (state->ofp, " not dword ptr [esp + 4]\n");
+ fprintf (state->ofp, " add dword ptr [esp], 1\n");
+ fprintf (state->ofp, " adc dword ptr [esp + 4], 0\n");
+ fprintf (state->ofp, " mov dword ptr [esp + 32], 1\n");
+ fprintf (state->ofp, " xor dword ptr [esp + 36], 1\n");
+ fprintf (state->ofp, ".L%d:\n", lhs_pos_label);
+ fprintf (state->ofp, " test dword ptr [esp + 12], 80000000h\n");
+ fprintf (state->ofp, " jz .L%d\n", rhs_pos_label);
+ fprintf (state->ofp, " not dword ptr [esp + 8]\n");
+ fprintf (state->ofp, " not dword ptr [esp + 12]\n");
+ fprintf (state->ofp, " add dword ptr [esp + 8], 1\n");
+ fprintf (state->ofp, " adc dword ptr [esp + 12], 0\n");
+ fprintf (state->ofp, " xor dword ptr [esp + 36], 1\n");
+ fprintf (state->ofp, ".L%d:\n", rhs_pos_label);
+
+ }
+
+ fprintf (state->ofp, " mov esi, 64\n");
+ fprintf (state->ofp, ".L%d:\n", loop_label);
+ fprintf (state->ofp, " shl dword ptr [esp], 1\n");
+ fprintf (state->ofp, " rcl dword ptr [esp + 4], 1\n");
+ fprintf (state->ofp, " rcl dword ptr [esp + 24], 1\n");
+ fprintf (state->ofp, " rcl dword ptr [esp + 28], 1\n");
+ fprintf (state->ofp, " shl dword ptr [esp + 16], 1\n");
+ fprintf (state->ofp, " rcl dword ptr [esp + 20], 1\n");
+ fprintf (state->ofp, " mov eax, dword ptr [esp + 28]\n");
+ fprintf (state->ofp, " cmp eax, dword ptr [esp + 12]\n");
+ fprintf (state->ofp, " ja .L%d\n", sub_label);
+ fprintf (state->ofp, " jb .L%d\n", nosub_label);
+ fprintf (state->ofp, " mov eax, dword ptr [esp + 24]\n");
+ fprintf (state->ofp, " cmp eax, dword ptr [esp + 8]\n");
+ fprintf (state->ofp, " jb .L%d\n", nosub_label);
+ fprintf (state->ofp, ".L%d:\n", sub_label);
+ fprintf (state->ofp, " mov eax, dword ptr [esp + 8]\n");
+ fprintf (state->ofp, " sub dword ptr [esp + 24], eax\n");
+ fprintf (state->ofp, " mov eax, dword ptr [esp + 12]\n");
+ fprintf (state->ofp, " sbb dword ptr [esp + 28], eax\n");
+ fprintf (state->ofp, " or dword ptr [esp + 16], 1\n");
+ fprintf (state->ofp, ".L%d:\n", nosub_label);
+ fprintf (state->ofp, " dec esi\n");
+ fprintf (state->ofp, " jnz .L%d\n", loop_label);
+
+ if (!is_unsigned) {
+
+ fprintf (state->ofp, " cmp dword ptr [esp + 36], 0\n");
+ fprintf (state->ofp, " je .L%d\n", qpos_label);
+ fprintf (state->ofp, " not dword ptr [esp + 16]\n");
+ fprintf (state->ofp, " not dword ptr [esp + 20]\n");
+ fprintf (state->ofp, " add dword ptr [esp + 16], 1\n");
+ fprintf (state->ofp, " adc dword ptr [esp + 20], 0\n");
+ fprintf (state->ofp, ".L%d:\n", qpos_label);
+ fprintf (state->ofp, " cmp dword ptr [esp + 32], 0\n");
+ fprintf (state->ofp, " je .L%d\n", rpos_label);
+ fprintf (state->ofp, " not dword ptr [esp + 24]\n");
+ fprintf (state->ofp, " not dword ptr [esp + 28]\n");
+ fprintf (state->ofp, " add dword ptr [esp + 24], 1\n");
+ fprintf (state->ofp, " adc dword ptr [esp + 28], 0\n");
+ fprintf (state->ofp, ".L%d:\n", rpos_label);
+
+ }
+
+ if (want_mod) {
+
+ fprintf (state->ofp, " mov eax, dword ptr [esp + 24]\n");
+ fprintf (state->ofp, " mov edx, dword ptr [esp + 28]\n");
+
+ } else {
+
+ fprintf (state->ofp, " mov eax, dword ptr [esp + 16]\n");
+ fprintf (state->ofp, " mov edx, dword ptr [esp + 20]\n");
+
+ }
+
+ }
+
+ fprintf (state->ofp, " add esp, 40\n");
+
+}
+
+static void emit_assignment_divmod64_att (int is_unsigned, int want_mod) {
+
+ int loop_label, sub_label, nosub_label;
+ int lhs_pos_label, rhs_pos_label;
+ int qpos_label, rpos_label;
+
+ loop_label = anon_label++;
+ sub_label = anon_label++;
+ nosub_label = anon_label++;
+ lhs_pos_label = anon_label++;
+ rhs_pos_label = anon_label++;
+ qpos_label = anon_label++;
+ rpos_label = anon_label++;
+
+ fprintf (state->ofp, " subl $40, %%esp\n");
+ fprintf (state->ofp, " movl %%eax, (%%esp)\n");
+ fprintf (state->ofp, " movl %%edx, 4(%%esp)\n");
+ fprintf (state->ofp, " movl %%ebx, 8(%%esp)\n");
+ fprintf (state->ofp, " movl %%ecx, 12(%%esp)\n");
+ fprintf (state->ofp, " xorl %%eax, %%eax\n");
+ fprintf (state->ofp, " movl %%eax, 16(%%esp)\n");
+ fprintf (state->ofp, " movl %%eax, 20(%%esp)\n");
+ fprintf (state->ofp, " movl %%eax, 24(%%esp)\n");
+ fprintf (state->ofp, " movl %%eax, 28(%%esp)\n");
+ fprintf (state->ofp, " movl %%eax, 32(%%esp)\n");
+ fprintf (state->ofp, " movl %%eax, 36(%%esp)\n");
+
+ if (!is_unsigned) {
+
+ fprintf (state->ofp, " testl $0x80000000, 4(%%esp)\n");
+ fprintf (state->ofp, " jz .L%d\n", lhs_pos_label);
+ fprintf (state->ofp, " notl (%%esp)\n");
+ fprintf (state->ofp, " notl 4(%%esp)\n");
+ fprintf (state->ofp, " addl $1, (%%esp)\n");
+ fprintf (state->ofp, " adcl $0, 4(%%esp)\n");
+ fprintf (state->ofp, " movl $1, 32(%%esp)\n");
+ fprintf (state->ofp, " xorl $1, 36(%%esp)\n");
+ fprintf (state->ofp, ".L%d:\n", lhs_pos_label);
+ fprintf (state->ofp, " testl $0x80000000, 12(%%esp)\n");
+ fprintf (state->ofp, " jz .L%d\n", rhs_pos_label);
+ fprintf (state->ofp, " notl 8(%%esp)\n");
+ fprintf (state->ofp, " notl 12(%%esp)\n");
+ fprintf (state->ofp, " addl $1, 8(%%esp)\n");
+ fprintf (state->ofp, " adcl $0, 12(%%esp)\n");
+ fprintf (state->ofp, " xorl $1, 36(%%esp)\n");
+ fprintf (state->ofp, ".L%d:\n", rhs_pos_label);
+
+ }
+
+ fprintf (state->ofp, " movl $64, %%esi\n");
+ fprintf (state->ofp, ".L%d:\n", loop_label);
+ fprintf (state->ofp, " shll $1, (%%esp)\n");
+ fprintf (state->ofp, " rcll $1, 4(%%esp)\n");
+ fprintf (state->ofp, " rcll $1, 24(%%esp)\n");
+ fprintf (state->ofp, " rcll $1, 28(%%esp)\n");
+ fprintf (state->ofp, " shll $1, 16(%%esp)\n");
+ fprintf (state->ofp, " rcll $1, 20(%%esp)\n");
+ fprintf (state->ofp, " movl 28(%%esp), %%eax\n");
+ fprintf (state->ofp, " cmpl 12(%%esp), %%eax\n");
+ fprintf (state->ofp, " ja .L%d\n", sub_label);
+ fprintf (state->ofp, " jb .L%d\n", nosub_label);
+ fprintf (state->ofp, " movl 24(%%esp), %%eax\n");
+ fprintf (state->ofp, " cmpl 8(%%esp), %%eax\n");
+ fprintf (state->ofp, " jb .L%d\n", nosub_label);
+ fprintf (state->ofp, ".L%d:\n", sub_label);
+ fprintf (state->ofp, " movl 8(%%esp), %%eax\n");
+ fprintf (state->ofp, " subl %%eax, 24(%%esp)\n");
+ fprintf (state->ofp, " movl 12(%%esp), %%eax\n");
+ fprintf (state->ofp, " sbbl %%eax, 28(%%esp)\n");
+ fprintf (state->ofp, " orl $1, 16(%%esp)\n");
+ fprintf (state->ofp, ".L%d:\n", nosub_label);
+ fprintf (state->ofp, " decl %%esi\n");
+ fprintf (state->ofp, " jnz .L%d\n", loop_label);
+
+ if (!is_unsigned) {
+
+ fprintf (state->ofp, " cmpl $0, 36(%%esp)\n");
+ fprintf (state->ofp, " je .L%d\n", qpos_label);
+ fprintf (state->ofp, " notl 16(%%esp)\n");
+ fprintf (state->ofp, " notl 20(%%esp)\n");
+ fprintf (state->ofp, " addl $1, 16(%%esp)\n");
+ fprintf (state->ofp, " adcl $0, 20(%%esp)\n");
+ fprintf (state->ofp, ".L%d:\n", qpos_label);
+ fprintf (state->ofp, " cmpl $0, 32(%%esp)\n");
+ fprintf (state->ofp, " je .L%d\n", rpos_label);
+ fprintf (state->ofp, " notl 24(%%esp)\n");
+ fprintf (state->ofp, " notl 28(%%esp)\n");
+ fprintf (state->ofp, " addl $1, 24(%%esp)\n");
+ fprintf (state->ofp, " adcl $0, 28(%%esp)\n");
+ fprintf (state->ofp, ".L%d:\n", rpos_label);
+
+ }
+
+ if (want_mod) {
+
+ fprintf (state->ofp, " movl 24(%%esp), %%eax\n");
+ fprintf (state->ofp, " movl 28(%%esp), %%edx\n");
+
+ } else {
+
+ fprintf (state->ofp, " movl 16(%%esp), %%eax\n");
+ fprintf (state->ofp, " movl 20(%%esp), %%edx\n");
+
+ }
+
+ fprintf (state->ofp, " addl $40, %%esp\n");
+
+}
+
+static void emit_assignment_divmod64 (int is_unsigned, int want_mod) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ emit_assignment_divmod64_intel (is_unsigned, want_mod);
+ } else {
+ emit_assignment_divmod64_att (is_unsigned, want_mod);
+ }
+
+}
+
+static void emit_assignment_binary_op64 (enum token_kind op, int is_unsigned) {
+
+ int l1, l2;
+
+ if (!state->ofp) {
+ return;
+ }
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ switch (op) {
+
+ case TOK_PLUS: case TOK_PLUSEQ:
+
+ fprintf (state->ofp, " add eax, ebx\n");
+ fprintf (state->ofp, " adc edx, ecx\n");
+ break;
+
+ case TOK_MINUS: case TOK_MINUSEQ:
+
+ fprintf (state->ofp, " sub eax, ebx\n");
+ fprintf (state->ofp, " sbb edx, ecx\n");
+ break;
+
+ case TOK_STAR: case TOK_STAREQ:
+
+ /**
+ * Low 64 bits of (EDX:EAX * EBX). The high half of the RHS
+ * is ignored for now; this is enough for constants/small ints.
+ */
+ fprintf (state->ofp, " mov esi, eax\n");
+ fprintf (state->ofp, " mov edi, edx\n");
+ fprintf (state->ofp, " mul ebx\n");
+ fprintf (state->ofp, " mov esi, edx\n");
+ fprintf (state->ofp, " mov edx, edi\n");
+ fprintf (state->ofp, " imul edx, ebx\n");
+ fprintf (state->ofp, " add edx, esi\n");
+ break;
+
+ case TOK_BSLASH: case TOK_SLASHEQ:
+
+ emit_assignment_divmod64 (is_unsigned, 0);
+ break;
+
+ case TOK_MOD: case TOK_MODEQ:
+
+ emit_assignment_divmod64 (is_unsigned, 1);
+ break;
+
+ case TOK_AMPER: case TOK_ANDEQ:
+
+ fprintf (state->ofp, " and eax, ebx\n");
+ fprintf (state->ofp, " and edx, ecx\n");
+ break;
+
+ case TOK_PIPE: case TOK_OREQ:
+
+ fprintf (state->ofp, " or eax, ebx\n");
+ fprintf (state->ofp, " or edx, ecx\n");
+ break;
+
+ case TOK_CARET: case TOK_XOREQ:
+
+ fprintf (state->ofp, " xor eax, ebx\n");
+ fprintf (state->ofp, " xor edx, ecx\n");
+ break;
+
+ case TOK_LSH: case TOK_LSHEQ:
+
+ l1 = anon_label++;
+ l2 = anon_label++;
+
+ fprintf (state->ofp, " test ecx, ecx\n");
+ fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? " jnz L%d\n" : " jnz .L%d\n"), l2);
+ fprintf (state->ofp, " mov ecx, ebx\n");
+ fprintf (state->ofp, " cmp ecx, 64\n");
+ fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? " jae L%d\n" : " jae .L%d\n"), l2);
+ fprintf (state->ofp, " cmp ecx, 32\n");
+ fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? " jb L%d\n" : " jb .L%d\n"), l1);
+ fprintf (state->ofp, " mov edx, eax\n");
+ fprintf (state->ofp, " xor eax, eax\n");
+ fprintf (state->ofp, " sub ecx, 32\n");
+ fprintf (state->ofp, " shl edx, cl\n");
+ fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? " jmp L%d\n" : " jmp .L%d\n"), l2 + 1);
+ fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "L%d:\n" : ".L%d:\n"), l1);
+ fprintf (state->ofp, " shld edx, eax, cl\n");
+ fprintf (state->ofp, " shl eax, cl\n");
+ fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? " jmp L%d\n" : " jmp .L%d\n"), l2 + 1);
+ fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "L%d:\n" : ".L%d:\n"), l2);
+ fprintf (state->ofp, " xor eax, eax\n");
+ fprintf (state->ofp, " xor edx, edx\n");
+ fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "L%d:\n" : ".L%d:\n"), l2 + 1);
+
+ anon_label++;
+ break;
+
+ case TOK_RSH: case TOK_RSHEQ:
+
+ l1 = anon_label++;
+ l2 = anon_label++;
+
+ fprintf (state->ofp, " test ecx, ecx\n");
+ fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? " jnz L%d\n" : " jnz .L%d\n"), l2);
+ fprintf (state->ofp, " mov ecx, ebx\n");
+ fprintf (state->ofp, " cmp ecx, 64\n");
+ fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? " jae L%d\n" : " jae .L%d\n"), l2);
+ fprintf (state->ofp, " cmp ecx, 32\n");
+ fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? " jb L%d\n" : " jb .L%d\n"), l1);
+ fprintf (state->ofp, " mov eax, edx\n");
+ fprintf (state->ofp, is_unsigned ? " xor edx, edx\n" : " sar edx, 31\n");
+ fprintf (state->ofp, " sub ecx, 32\n");
+ fprintf (state->ofp, is_unsigned ? " shr eax, cl\n" : " sar eax, cl\n");
+ fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? " jmp L%d\n" : " jmp .L%d\n"), l2 + 1);
+ fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "L%d:\n" : ".L%d:\n"), l1);
+ fprintf (state->ofp, " shrd eax, edx, cl\n");
+ fprintf (state->ofp, is_unsigned ? " shr edx, cl\n" : " sar edx, cl\n");
+ fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? " jmp L%d\n" : " jmp .L%d\n"), l2 + 1);
+ fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "L%d:\n" : ".L%d:\n"), l2);
+ fprintf (state->ofp, is_unsigned ? " xor eax, eax\n" : " mov eax, edx\n");
+ fprintf (state->ofp, is_unsigned ? " xor edx, edx\n" : " sar edx, 31\n");
+ fprintf (state->ofp, is_unsigned ? "" : " mov eax, edx\n");
+ fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "L%d:\n" : ".L%d:\n"), l2 + 1);
+
+ anon_label++;
+ break;
+
+ default:
+
+ break;
+
+ }
+
+ } else {
+
+ switch (op) {
+
+ case TOK_PLUS: case TOK_PLUSEQ:
+
+ fprintf (state->ofp, " addl %%ebx, %%eax\n");
+ fprintf (state->ofp, " adcl %%ecx, %%edx\n");
+ break;
+
+ case TOK_MINUS: case TOK_MINUSEQ:
+
+ fprintf (state->ofp, " subl %%ebx, %%eax\n");
+ fprintf (state->ofp, " sbbl %%ecx, %%edx\n");
+ break;
+
+ case TOK_STAR: case TOK_STAREQ:
+
+ fprintf (state->ofp, " movl %%eax, %%esi\n");
+ fprintf (state->ofp, " movl %%edx, %%edi\n");
+ fprintf (state->ofp, " mull %%ebx\n");
+ fprintf (state->ofp, " movl %%edx, %%esi\n");
+ fprintf (state->ofp, " movl %%edi, %%edx\n");
+ fprintf (state->ofp, " imull %%ebx, %%edx\n");
+ fprintf (state->ofp, " addl %%esi, %%edx\n");
+ break;
+
+ case TOK_BSLASH: case TOK_SLASHEQ:
+
+ emit_assignment_divmod64 (is_unsigned, 0);
+ break;
+
+ case TOK_MOD: case TOK_MODEQ:
+
+ emit_assignment_divmod64 (is_unsigned, 1);
+ break;
+
+ case TOK_AMPER: case TOK_ANDEQ:
+
+ fprintf (state->ofp, " andl %%ebx, %%eax\n");
+ fprintf (state->ofp, " andl %%ecx, %%edx\n");
+ break;
+
+ case TOK_PIPE: case TOK_OREQ:
+
+ fprintf (state->ofp, " orl %%ebx, %%eax\n");
+ fprintf (state->ofp, " orl %%ecx, %%edx\n");
+ break;
+
+ case TOK_CARET: case TOK_XOREQ:
+
+ fprintf (state->ofp, " xorl %%ebx, %%eax\n");
+ fprintf (state->ofp, " xorl %%ecx, %%edx\n");
+ break;
+
+ case TOK_LSH: case TOK_LSHEQ:
+
+ l1 = anon_label++;
+ l2 = anon_label++;
+
+ fprintf (state->ofp, " testl %%ecx, %%ecx\n");
+ fprintf (state->ofp, " jnz .L%d\n", l2);
+ fprintf (state->ofp, " movl %%ebx, %%ecx\n");
+ fprintf (state->ofp, " cmpl $64, %%ecx\n");
+ fprintf (state->ofp, " jae .L%d\n", l2);
+ fprintf (state->ofp, " cmpl $32, %%ecx\n");
+ fprintf (state->ofp, " jb .L%d\n", l1);
+ fprintf (state->ofp, " movl %%eax, %%edx\n");
+ fprintf (state->ofp, " xorl %%eax, %%eax\n");
+ fprintf (state->ofp, " subl $32, %%ecx\n");
+ fprintf (state->ofp, " shll %%cl, %%edx\n");
+ fprintf (state->ofp, " jmp .L%d\n", l2 + 1);
+ fprintf (state->ofp, ".L%d:\n", l1);
+ fprintf (state->ofp, " shldl %%cl, %%eax, %%edx\n");
+ fprintf (state->ofp, " shll %%cl, %%eax\n");
+ fprintf (state->ofp, " jmp .L%d\n", l2 + 1);
+ fprintf (state->ofp, ".L%d:\n", l2);
+ fprintf (state->ofp, " xorl %%eax, %%eax\n");
+ fprintf (state->ofp, " xorl %%edx, %%edx\n");
+ fprintf (state->ofp, ".L%d:\n", l2 + 1);
+
+ anon_label++;
+ break;
+
+ case TOK_RSH: case TOK_RSHEQ:
+
+ l1 = anon_label++;
+ l2 = anon_label++;
+
+ fprintf (state->ofp, " testl %%ecx, %%ecx\n");
+ fprintf (state->ofp, " jnz .L%d\n", l2);
+ fprintf (state->ofp, " movl %%ebx, %%ecx\n");
+ fprintf (state->ofp, " cmpl $64, %%ecx\n");
+ fprintf (state->ofp, " jae .L%d\n", l2);
+ fprintf (state->ofp, " cmpl $32, %%ecx\n");
+ fprintf (state->ofp, " jb .L%d\n", l1);
+ fprintf (state->ofp, " movl %%edx, %%eax\n");
+ fprintf (state->ofp, is_unsigned ? " xorl %%edx, %%edx\n" : " sarl $31, %%edx\n");
+ fprintf (state->ofp, " subl $32, %%ecx\n");
+ fprintf (state->ofp, is_unsigned ? " shrl %%cl, %%eax\n" : " sarl %%cl, %%eax\n");
+ fprintf (state->ofp, " jmp .L%d\n", l2 + 1);
+ fprintf (state->ofp, ".L%d:\n", l1);
+ fprintf (state->ofp, " shrdl %%cl, %%edx, %%eax\n");
+ fprintf (state->ofp, is_unsigned ? " shrl %%cl, %%edx\n" : " sarl %%cl, %%edx\n");
+ fprintf (state->ofp, " jmp .L%d\n", l2 + 1);
+ fprintf (state->ofp, ".L%d:\n", l2);
+ fprintf (state->ofp, is_unsigned ? " xorl %%eax, %%eax\n" : " movl %%edx, %%eax\n");
+ fprintf (state->ofp, is_unsigned ? " xorl %%edx, %%edx\n" : " sarl $31, %%edx\n");
+ fprintf (state->ofp, is_unsigned ? "" : " movl %%edx, %%eax\n");
+ fprintf (state->ofp, ".L%d:\n", l2 + 1);
+
+ anon_label++;
+ break;
+
+ default:
+
+ break;
+
+ }
+
+ }
+
+}
+
+static void emit_call_identifier_to_reg_now (const char *name, const char *reg, const char *name_start, const char *name_caret, unsigned long name_line);
+static void emit_assignment_binary_op (enum token_kind op, int is_unsigned);
+static void emit_store_member_to_addr_reg_now (const char *addr_reg, int offset, const char *value_reg, int size);
+static void emit_store_reg_to_deref_reg_now (const char *addr_reg, const char *value_reg, int size);
+static void emit_load_deref_reg_now (const char *reg, int size);
+
+static void emit_store_floating_member_to_addr_reg_now (const char *addr_reg, int offset, int size);
+static void emit_load_floating_rhs_expression_now (int result_size);
+
+static int emit_load_parenthesized_assignment_expression_to_reg_now (const char *reg, int *out_is_unsigned);
+static int token_is_floating_constant_now (void);
+
+static void emit_load_local_address_to_reg_now (const char *reg, long offset) {
+
+ char memref[64];
+
+ if (!state->ofp || !reg) {
+ return;
+ }
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ format_intel_ebp_offset (memref, sizeof (memref), offset);
+ fprintf (state->ofp, " lea %s, %s\n", reg, memref);
+
+ } else {
+ fprintf (state->ofp, " leal %ld(%%ebp), %%%s\n", offset, reg);
+ }
+
+}
+
+static void emit_apply_postfix_member_access_to_reg_now (const char *reg) {
+
+ postfix_member_pointer_depth = 0;
+ postfix_member_pointed_size = 0;
+ postfix_member_seen = 0;
+ postfix_member_offset = 0;
+ postfix_member_size = 0;
+ postfix_member_is_floating = 0;
+ postfix_member_is_unsigned = 0;
+
+ {
+
+ const char *current_object_tag_name = postfix_copy_lvalue_tag_name;
+ int current_object_size = postfix_copy_lvalue_size;
+
+ if (current_object_size <= 0 && rhs_last_pointed_size > 0) {
+ current_object_size = rhs_last_pointed_size;
+ }
+
+ while (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) {
+
+ enum token_kind op = tok.kind;
+ char *member;
+
+ const char *member_start;
+ const char *member_caret;
+
+ unsigned long member_line;
+
+ int offset = 0;
+ int size = DATA_INT & 0x1f;
+ int elem_size = DATA_INT & 0x1f;
+ int pointer_depth = 0;
+ int is_array = 0;
+ int is_floating = 0;
+ int is_unsigned = 0;
+
+ get_token ();
+
+ member_start = tok.start;
+ member_caret = tok.caret;
+ member_line = get_line_number ();
+
+ if (tok.kind != TOK_IDENT) {
+
+ report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "expected member name after %s", op == TOK_ARROW ? "->" : ".");
+ return;
+
+ }
+
+ member = xstrdup (tok.ident);
+ get_token ();
+
+ if (!find_member_info_ex_bounded (member, current_object_size, current_object_tag_name, &offset, &size, &elem_size, &pointer_depth, &is_array, &is_floating)) {
+
+ report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "unknown member '%s'", member);
+
+ free (member);
+ return;
+
+ }
+
+ is_unsigned = last_found_member_is_unsigned;
+
+ {
+
+ const char *member_tag_name = last_found_member_tag_name;
+
+ /*
+ * For an array member whose element type is a pointer, the
+ * subscript step is always one pointer. Keep this independent
+ * of the pointed aggregate size; otherwise expressions like
+ * ic->heads[i] scale by sizeof(*heads[i]) and are later passed
+ * as an aggregate instead of one pointer.
+ */
+ if (is_array && pointer_depth > 0) {
+ elem_size = DATA_PTR;
+ } else if (pointer_depth > 1) {
+ elem_size = DATA_PTR;
+ } else if (pointer_depth == 1 && member_tag_name) {
+
+ struct aggregate_tag_entry *entry = find_aggregate_tag (member_tag_name, 0);
+
+ if (entry) {
+ elem_size = entry->size;
+ }
+
+ }
+
+ current_object_tag_name = member_tag_name;
+
+ }
+
+ free (member);
+
+ postfix_member_seen = 1;
+ postfix_member_pointer_depth = is_array ? 1 : pointer_depth;
+ postfix_member_pointed_size = elem_size;
+ postfix_member_offset = offset;
+ postfix_member_size = size;
+ postfix_member_is_floating = is_floating;
+ postfix_member_is_unsigned = is_unsigned;
+
+ if (pointer_depth > 0 || is_array) {
+ current_object_size = elem_size;
+ } else {
+ current_object_size = size;
+ }
+
+ if (tok.kind == TOK_LBRACK) {
+
+ enum token_kind assign_op;
+ int subscript_elem_size;
+
+ if (!is_array && pointer_depth > 0) {
+ emit_load_member_from_addr_reg_now (reg, reg, offset, DATA_PTR & 0x1f);
+ } else if (state->ofp && offset != 0) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " add %s, %d\n", reg, offset);
+ } else {
+ fprintf (state->ofp, " addl $%d, %%%s\n", offset, reg);
+ }
+
+ }
+
+ subscript_elem_size = index_step_size (elem_size);
+
+ if (subscript_elem_size <= 0) {
+ subscript_elem_size = DATA_INT & 0x1f;
+ }
+
+ emit_parse_postfix_subscript_scaled_address_to_reg_now (reg, subscript_elem_size);
+
+ if (tok.kind == TOK_ARROW && subscript_elem_size <= (DATA_PTR & 0x1f)) {
+
+ emit_load_deref_reg_now (reg, DATA_PTR & 0x1f);
+
+ postfix_member_pointer_depth = 0;
+ postfix_member_size = DATA_PTR & 0x1f;
+
+ continue;
+
+ }
+
+ if (is_assignment_operator (tok.kind)) {
+
+ assign_op = tok.kind;
+ get_token ();
+
+ if (assign_op == TOK_ASSIGN && subscript_elem_size > (DATA_LLONG & 0x1f)
+ && tok.kind == TOK_IDENT && tok.ident
+ && get_global_symbol_kind (tok.ident) == GLOBAL_SYMBOL_FUNCTION) {
+
+ char *rhs_name = xstrdup (tok.ident);
+
+ const char *rhs_start = tok.start;
+ const char *rhs_caret = tok.caret;
+
+ unsigned long rhs_line = get_line_number ();
+ emit_push_reg_now (reg);
+
+ pending_struct_return_lhs = 0;
+ pending_struct_return_global_name = 0;
+ pending_struct_return_stack_address = 1;
+ pending_struct_return_stack_offset = 0;
+
+ get_token ();
+ emit_call_identifier_to_reg_now (rhs_name, reg, rhs_start, rhs_caret, rhs_line);
+
+ pending_struct_return_stack_address = 0;
+ pending_struct_return_stack_offset = 0;
+
+ emit_pop_reg_now ("edx");
+ free (rhs_name);
+
+ postfix_member_seen = 0;
+ return;
+
+ }
+
+ emit_push_reg_now (reg);
+
+ if (assign_op == TOK_ASSIGN) {
+
+ if (subscript_elem_size > (DATA_LLONG & 0x1f)) {
+
+ pending_struct_return_lhs = 0;
+ pending_struct_return_global_name = 0;
+ pending_struct_return_stack_address = 1;
+ pending_struct_return_stack_offset = 0;
+
+ emit_load_assignment_rhs_expression_to_reg (reg);
+
+ pending_struct_return_stack_address = 0;
+ pending_struct_return_stack_offset = 0;
+
+ emit_pop_reg_now ("edx");
+
+ postfix_member_seen = 0;
+ return;
+
+ }
+
+ emit_load_assignment_rhs_expression_to_reg (reg);
+
+ } else {
+
+ emit_load_deref_reg_now (reg, subscript_elem_size);
+ emit_push_reg_now (reg);
+ emit_load_assignment_rhs_expression_to_reg ("edx");
+ emit_pop_reg_now (reg);
+ emit_assignment_binary_op (assign_op, 0);
+
+ }
+
+ emit_pop_reg_now ("edx");
+ emit_store_reg_to_deref_reg_now ("edx", reg, subscript_elem_size);
+
+ postfix_member_seen = 0;
+ return;
+
+ }
+
+ if (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) {
+
+ current_object_size = elem_size;
+ continue;
+
+ }
+
+ /**
+ * The expression now denotes the subscripted element, not the
+ * whole member array. Keep the postfix-member metadata in
+ * step with the value in REG so argument passing does not
+ * mistake e.g. ic->heads[i] for the entire heads[] aggregate
+ * and push multiple words.
+ *
+ * If the element itself is an aggregate, REG must remain the
+ * address of that element. Loading *(REG) would fetch the
+ * first word of the struct and later aggregate argument
+ * passing would treat that word as a pointer, as happened for
+ * instruction.types[instruction.operands].
+ */
+ postfix_member_size = subscript_elem_size;
+
+ /*
+ * Subscript of an array member whose element type is a pointer
+ * yields the pointer stored in that array slot. The generic
+ * aggregate-subscript path deliberately keeps struct elements as
+ * addresses, but pointer elements must still be loaded before a
+ * following -> member access. Otherwise expressions such as
+ * instruction.regs[operand]->type.reg_rex treat the address of
+ * the regs[] slot as if it were a struct reg_entry *.
+ */
+ if (is_array && (pointer_depth > 0 || (tok.kind == TOK_ARROW && subscript_elem_size == (DATA_PTR & 0x1f)))) {
+
+ emit_load_deref_reg_now (reg, DATA_PTR & 0x1f);
+
+ postfix_member_pointer_depth = pointer_depth > 0 ? pointer_depth - 1 : 0;
+ postfix_member_size = DATA_PTR & 0x1f;
+
+ if (postfix_member_pointer_depth == 0 && current_object_tag_name) {
+
+ struct aggregate_tag_entry *entry = find_aggregate_tag (current_object_tag_name, 0);
+
+ if (entry) {
+ current_object_size = entry->size;
+ }
+
+ }
+
+ continue;
+
+ }
+
+ if (postfix_member_pointer_depth > 0) {
+ postfix_member_pointer_depth--;
+ }
+
+ if (postfix_member_pointer_depth != 0
+ || postfix_member_size <= (DATA_PTR & 0x1f)) {
+ emit_load_deref_reg_now (reg, subscript_elem_size);
+ }
+
+ continue;
+
+ }
+
+ if (is_array && tok.kind != TOK_ARROW && tok.kind != TOK_DOT) {
+
+ if (state->ofp && offset != 0) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " add %s, %d\n", reg, offset);
+ } else {
+ fprintf (state->ofp, " addl $%d, %%%s\n", offset, reg);
+ }
+
+ }
+
+ /*
+ * A bare array member expression decays to a pointer when used
+ * as an rvalue/function argument. Keep REG as the element
+ * address, but do not leave aggregate element metadata behind or
+ * the call argument path will push the first element by value.
+ * This broke calls such as _cpp_add_unknown2_direct(...,
+ * macro->tokens, token_count), where tokens[0] is a struct.
+ */
+ postfix_member_size = DATA_PTR & 0x1f;
+ postfix_member_pointer_depth = 1;
+ postfix_member_pointed_size = elem_size;
+
+ continue;
+
+ }
+
+ if ((tok.kind == TOK_ARROW || tok.kind == TOK_DOT) && pointer_depth == 0) {
+
+ if (state->ofp && offset != 0) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " add %s, %d\n", reg, offset);
+ } else {
+ fprintf (state->ofp, " addl $%d, %%%s\n", offset, reg);
+ }
+
+ }
+
+ continue;
+
+ }
+
+ if (size > (DATA_PTR & 0x1f) && tok.kind != TOK_ARROW && tok.kind != TOK_DOT) {
+
+ if (state->ofp && offset != 0) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " add %s, %d\n", reg, offset);
+ } else {
+ fprintf (state->ofp, " addl $%d, %%%s\n", offset, reg);
+ }
+
+ }
+
+ continue;
+
+ }
+
+ if (state->ofp) {
+
+ if (strcmp (reg, "edx") != 0) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " mov edx, %s\n", reg);
+ } else {
+ fprintf (state->ofp, " movl %%%s, %%edx\n", reg);
+ }
+
+ }
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ const char *opsize = "dword";
+
+ if (size == 1) {
+ opsize = "byte";
+ } else if (size == 2) {
+ opsize = "word";
+ }
+
+ if (state->syntax & ASM_SYNTAX_NASM) {
+
+ if (size == 1) {
+ fprintf (state->ofp, " movzx %s, byte [%s + %d]\n", reg, reg, offset);
+ } else if (size == 2) {
+ fprintf (state->ofp, " movzx %s, word [%s + %d]\n", reg, reg, offset);
+ } else {
+ fprintf (state->ofp, " mov %s, %s [%s + %d]\n", reg, opsize, reg, offset);
+ }
+
+ } else {
+
+ if (size == 1) {
+ fprintf (state->ofp, " movzx %s, byte ptr [%s + %d]\n", reg, reg, offset);
+ } else if (size == 2) {
+ fprintf (state->ofp, " movzx %s, word ptr [%s + %d]\n", reg, reg, offset);
+ } else {
+ fprintf (state->ofp, " mov %s, %s ptr [%s + %d]\n", reg, opsize, reg, offset);
+ }
+
+ }
+
+ } else {
+
+ if (size == 1) {
+ fprintf (state->ofp, " movzbl %d(%%%s), %%%s\n", offset, reg, reg);
+ } else if (size == 2) {
+ fprintf (state->ofp, " movzwl %d(%%%s), %%%s\n", offset, reg, reg);
+ } else {
+ fprintf (state->ofp, " movl %d(%%%s), %%%s\n", offset, reg, reg);
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+}
+
+static void emit_apply_postfix_member_incdec_now (const char *reg, enum token_kind op) {
+
+ if (!postfix_member_seen) {
+ return;
+ }
+
+ if (state->ofp) {
+
+ const char *insn = op == TOK_INCR ? "inc" : "dec";
+ (void) reg;
+
+ if (postfix_member_pointer_depth > 0) {
+
+ int step = postfix_member_pointed_size;
+ const char *arith = op == TOK_INCR ? "add" : "sub";
+
+ if (step <= 0) {
+ step = 1;
+ }
+
+ if (step == 1) {
+ /* Falling through keeps the compact inc/dec form for char *. */
+ } else if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ if (state->syntax & ASM_SYNTAX_NASM) {
+ fprintf (state->ofp, " %s dword [edx + %d], %d\n", arith, postfix_member_offset, step);
+ } else {
+ fprintf (state->ofp, " %s dword ptr [edx + %d], %d\n", arith, postfix_member_offset, step);
+ }
+
+ return;
+
+ } else {
+
+ fprintf (state->ofp, " %sl $%d, %d(%%edx)\n", arith, step, postfix_member_offset);
+ return;
+ }
+
+ }
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ const char *opsize = "dword";
+
+ if (postfix_member_size == 1) {
+ opsize = "byte";
+ } else if (postfix_member_size == 2) {
+ opsize = "word";
+ }
+
+ if (state->syntax & ASM_SYNTAX_NASM) {
+ fprintf (state->ofp, " %s %s [edx + %d]\n", insn, opsize, postfix_member_offset);
+ } else {
+ fprintf (state->ofp, " %s %s ptr [edx + %d]\n", insn, opsize, postfix_member_offset);
+ }
+
+ } else {
+
+ if (postfix_member_size == 1) {
+ fprintf (state->ofp, " %sb %d(%%edx)\n", insn, postfix_member_offset);
+ } else if (postfix_member_size == 2) {
+ fprintf (state->ofp, " %sw %d(%%edx)\n", insn, postfix_member_offset);
+ } else {
+ fprintf (state->ofp, " %sl %d(%%edx)\n", insn, postfix_member_offset);
+ }
+
+ }
+
+ }
+
+}
+
+static int rhs_text_is_plain_aggregate_lvalue_now (const char *p) {
+
+ int paren_depth = 0;
+ int bracket_depth = 0;
+ int saw_ident = 0;
+
+ if (!p) {
+ return 1;
+ }
+
+ while (*p) {
+
+ unsigned char ch = (unsigned char) *p;
+
+ if (ch == '\'' || ch == '"') {
+
+ int quote = ch;
+ p++;
+
+ while (*p && *p != quote) {
+
+ if (*p == '\\' && p[1]) {
+ p += 2;
+ } else {
+ p++;
+ }
+
+ }
+
+ if (*p == quote) {
+ p++;
+ }
+
+ continue;
+
+ }
+
+ if (ch == '(') {
+
+ paren_depth++;
+ p++;
+
+ continue;
+
+ }
+
+ if (ch == ')') {
+
+ if (paren_depth == 0 && bracket_depth == 0) {
+ break;
+ }
+
+ if (paren_depth > 0) {
+ paren_depth--;
+ }
+
+ p++;
+ continue;
+
+ }
+
+ if (ch == '[') {
+
+ bracket_depth++;
+ p++;
+
+ continue;
+
+ }
+
+ if (ch == ']') {
+
+ if (bracket_depth > 0) {
+ bracket_depth--;
+ }
+
+ p++;
+ continue;
+
+ }
+
+ if (paren_depth == 0 && bracket_depth == 0) {
+
+ if (ch == ';' || ch == ',') {
+ break;
+ }
+
+ if (ch == '-' && p[1] == '>') {
+
+ p += 2;
+ continue;
+
+ }
+
+ if (ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '%' ||
+ ch == '&' || ch == '|' || ch == '^' || ch == '?' || ch == ':') {
+ return 0;
+ }
+
+ }
+
+ if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || ch == '_') {
+ saw_ident = 1;
+ }
+
+ p++;
+
+ }
+
+ return saw_ident;
+
+}
+
+static int emit_aggregate_copy_from_current_rhs_to_addr_reg_now (const char *addr_reg, int offset, int size);
+static int emit_parse_postfix_copy_source_address_now (const char *reg, struct local_symbol *src, const char *src_name, const char *name_start, const char *name_caret, unsigned long name_line);
+
+static int emit_store_assignment_to_aggregate_address_now (const char *addr_reg, int size, const char *name_start, const char *name_caret, unsigned long name_line) {
+
+ enum token_kind op;
+
+ if (!is_assignment_operator (tok.kind) || size <= (DATA_LLONG & 0x1f)) {
+ return 0;
+ }
+
+ op = tok.kind;
+ get_token ();
+
+ if (op == TOK_ASSIGN && tok.kind == TOK_IDENT && tok.ident && token_identifier_is_function_call_rhs_now ()) {
+
+ char *rhs_name = xstrdup (tok.ident);
+
+ const char *rhs_start = tok.start ? tok.start : name_start;
+ const char *rhs_caret = tok.caret ? tok.caret : name_caret;
+
+ unsigned long rhs_line = get_line_number ();
+ emit_push_reg_now (addr_reg);
+
+ pending_struct_return_lhs = 0;
+ pending_struct_return_global_name = 0;
+ pending_struct_return_stack_address = 1;
+ pending_struct_return_stack_offset = 0;
+
+ get_token ();
+ emit_call_identifier_to_reg_now (rhs_name, "eax", rhs_start, rhs_caret, rhs_line);
+
+ pending_struct_return_stack_address = 0;
+ pending_struct_return_stack_offset = 0;
+
+ emit_pop_reg_now ("edx");
+ free (rhs_name);
+
+ return 1;
+
+ }
+
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "aggregate assignment expression not implemented");
+
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+ return 1;
+
+}
+
+static int emit_store_assignment_to_postfix_member_now (const char *reg) {
+
+ enum token_kind op;
+ int assign_member_offset;
+ int assign_member_size;
+
+ if (!postfix_member_seen || !is_assignment_operator (tok.kind)) {
+ return 0;
+ }
+
+ op = tok.kind;
+ assign_member_offset = postfix_member_offset;
+ assign_member_size = postfix_member_size;
+
+ get_token ();
+
+ if (state->ofp) {
+
+ if (op == TOK_ASSIGN) {
+
+ if (postfix_member_is_floating || token_is_floating_constant_now ()) {
+
+ emit_push_reg_now ("edx");
+ emit_load_floating_rhs_expression_now (assign_member_size);
+
+ emit_pop_reg_now ("edx");
+ emit_store_floating_member_to_addr_reg_now ("edx", assign_member_offset, assign_member_size);
+
+ return 1;
+
+ }
+
+ if (assign_member_size > (DATA_LLONG & 0x1f) &&
+ tok.kind == TOK_IDENT && tok.ident && token_identifier_is_function_call_rhs_now ()) {
+
+ char *rhs_name = xstrdup (tok.ident);
+
+ const char *rhs_start = tok.start;
+ const char *rhs_caret = tok.caret;
+
+ unsigned long rhs_line = get_line_number ();
+ emit_push_reg_now ("edx");
+
+ pending_struct_return_lhs = 0;
+ pending_struct_return_global_name = 0;
+ pending_struct_return_stack_address = 1;
+ pending_struct_return_stack_offset = assign_member_offset;
+
+ get_token ();
+ emit_call_identifier_to_reg_now (rhs_name, reg, rhs_start, rhs_caret, rhs_line);
+
+ pending_struct_return_stack_address = 0;
+ pending_struct_return_stack_offset = 0;
+
+ emit_pop_reg_now ("edx");
+ free (rhs_name);
+
+ return 1;
+
+ }
+
+ if (emit_aggregate_copy_from_current_rhs_to_addr_reg_now ("edx", assign_member_offset, assign_member_size)) {
+ return 1;
+ }
+
+ emit_push_reg_now ("edx");
+
+ emit_load_assignment_rhs_expression_to_reg (reg);
+ emit_pop_reg_now ("edx");
+
+ } else {
+
+ emit_push_reg_now ("edx");
+ emit_push_reg_now (reg);
+
+ emit_load_assignment_rhs_expression_to_reg ("edx");
+ emit_pop_reg_now (reg);
+
+ emit_assignment_binary_op (op, 0);
+ emit_pop_reg_now ("edx");
+
+ }
+
+ emit_store_member_to_addr_reg_now ("edx", assign_member_offset, reg, assign_member_size);
+
+ } else {
+ emit_load_assignment_rhs_expression_to_reg (reg);
+ }
+
+ return 1;
+
+}
+
+static int parenthesized_function_designator_call_now (void) {
+
+ const char *p;
+
+ if (tok.kind != TOK_IDENT || !tok.caret || !tok.ident) {
+ return 0;
+ }
+
+ p = tok.caret + strlen (tok.ident);
+
+ while (*p == ' ' || *p == '\t') {
+ p++;
+ }
+
+ if (*p != ')') {
+ return 0;
+ }
+
+ p++;
+
+ while (*p == ' ' || *p == '\t') {
+ p++;
+ }
+
+ return *p == '(';
+
+}
+
+static void emit_load_deref_reg_now (const char *reg, int size) {
+
+ if (!state->ofp) {
+ return;
+ }
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ if (state->syntax & ASM_SYNTAX_NASM) {
+
+ if (size == (DATA_CHAR & 0x1f)) {
+ fprintf (state->ofp, " movzx %s, byte [%s]\n", reg, reg);
+ } else if (size == (DATA_SHORT & 0x1f)) {
+ fprintf (state->ofp, " movzx %s, word [%s]\n", reg, reg);
+ } else {
+ fprintf (state->ofp, " mov %s, dword [%s]\n", reg, reg);
+ }
+
+ } else {
+
+ if (size == (DATA_CHAR & 0x1f)) {
+ fprintf (state->ofp, " movzx %s, byte ptr [%s]\n", reg, reg);
+ } else if (size == (DATA_SHORT & 0x1f)) {
+ fprintf (state->ofp, " movzx %s, word ptr [%s]\n", reg, reg);
+ } else {
+ fprintf (state->ofp, " mov %s, dword ptr [%s]\n", reg, reg);
+ }
+
+ }
+
+ } else {
+
+ if (size == (DATA_CHAR & 0x1f)) {
+ fprintf (state->ofp, " movzbl (%%%s), %%%s\n", reg, reg);
+ } else if (size == (DATA_SHORT & 0x1f)) {
+ fprintf (state->ofp, " movzwl (%%%s), %%%s\n", reg, reg);
+ } else {
+ fprintf (state->ofp, " movl (%%%s), %%%s\n", reg, reg);
+ }
+
+ }
+
+}
+
+static void emit_store_reg_to_deref_reg_now (const char *addr_reg, const char *value_reg, int size) {
+
+ if (suppress_next_struct_return_scalar_store) {
+
+ suppress_next_struct_return_scalar_store = 0;
+ return;
+
+ }
+
+ if (!state->ofp) {
+ return;
+ }
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ if (state->syntax & ASM_SYNTAX_NASM) {
+
+ if (size == (DATA_CHAR & 0x1f)) {
+ fprintf (state->ofp, " mov byte [%s], %cl\n", addr_reg, value_reg[1]);
+ } else if (size == (DATA_SHORT & 0x1f)) {
+ fprintf (state->ofp, " mov word [%s], %cx\n", addr_reg, value_reg[1]);
+ } else {
+ fprintf (state->ofp, " mov dword [%s], %s\n", addr_reg, value_reg);
+ }
+
+ } else {
+
+ if (size == (DATA_CHAR & 0x1f)) {
+ fprintf (state->ofp, " mov byte ptr [%s], %cl\n", addr_reg, value_reg[1]);
+ } else if (size == (DATA_SHORT & 0x1f)) {
+ fprintf (state->ofp, " mov word ptr [%s], %cx\n", addr_reg, value_reg[1]);
+ } else {
+ fprintf (state->ofp, " mov dword ptr [%s], %s\n", addr_reg, value_reg);
+ }
+
+ }
+
+ } else {
+
+ if (size == (DATA_CHAR & 0x1f)) {
+ fprintf (state->ofp, " movb %%%cl, (%%%s)\n", value_reg[1], addr_reg);
+ } else if (size == (DATA_SHORT & 0x1f)) {
+ fprintf (state->ofp, " movw %%%cx, (%%%s)\n", value_reg[1], addr_reg);
+ } else {
+ fprintf (state->ofp, " movl %%%s, (%%%s)\n", value_reg, addr_reg);
+ }
+
+ }
+
+}
+
+static int emit_handle_subscript_after_loaded_pointer_to_reg_now (const char *reg, int pointer_depth, int pointed_size) {
+
+ int subscript_elem_size;
+
+ if (tok.kind != TOK_LBRACK) {
+ return 0;
+ }
+
+ subscript_elem_size = pointer_depth > 1 ? (DATA_PTR & 0x1f) : pointed_size;
+
+ if (subscript_elem_size <= 0) {
+ subscript_elem_size = DATA_INT & 0x1f;
+ }
+
+ emit_parse_postfix_subscript_scaled_address_to_reg_now (reg, subscript_elem_size);
+
+ if (is_assignment_operator (tok.kind)) {
+
+ enum token_kind assign_op = tok.kind;
+
+ get_token ();
+ emit_push_reg_now (reg);
+
+ if (assign_op == TOK_ASSIGN) {
+ emit_load_assignment_rhs_expression_to_reg (reg);
+ } else {
+
+ emit_load_deref_reg_now (reg, subscript_elem_size);
+ emit_push_reg_now (reg);
+
+ emit_load_assignment_rhs_expression_to_reg ("edx");
+ emit_pop_reg_now (reg);
+
+ emit_assignment_binary_op (assign_op, 0);
+
+ }
+
+ emit_pop_reg_now ("edx");
+
+ emit_store_reg_to_deref_reg_now ("edx", reg, subscript_elem_size);
+ clear_rhs_last_pointer_info ();
+
+ return 1;
+
+ }
+
+ emit_load_deref_reg_now (reg, subscript_elem_size);
+
+ if (pointer_depth > 1) {
+ set_rhs_last_pointer_info (pointer_depth - 1, pointed_size);
+ } else {
+ clear_rhs_last_pointer_info ();
+ }
+
+ return 1;
+
+}
+
+static void emit_load_member_from_addr_reg_now (const char *dst_reg, const char *addr_reg, int offset, int size) {
+
+ if (!state->ofp) {
+ return;
+ }
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ if (state->syntax & ASM_SYNTAX_NASM) {
+
+ if (size == (DATA_CHAR & 0x1f)) {
+ fprintf (state->ofp, " movzx %s, byte [%s + %d]\n", dst_reg, addr_reg, offset);
+ } else if (size == (DATA_SHORT & 0x1f)) {
+ fprintf (state->ofp, " movzx %s, word [%s + %d]\n", dst_reg, addr_reg, offset);
+ } else {
+ fprintf (state->ofp, " mov %s, dword [%s + %d]\n", dst_reg, addr_reg, offset);
+ }
+
+ } else {
+
+ if (size == (DATA_CHAR & 0x1f)) {
+ fprintf (state->ofp, " movzx %s, byte ptr [%s + %d]\n", dst_reg, addr_reg, offset);
+ } else if (size == (DATA_SHORT & 0x1f)) {
+ fprintf (state->ofp, " movzx %s, word ptr [%s + %d]\n", dst_reg, addr_reg, offset);
+ } else {
+ fprintf (state->ofp, " mov %s, dword ptr [%s + %d]\n", dst_reg, addr_reg, offset);
+ }
+
+ }
+
+ } else {
+
+ if (size == (DATA_CHAR & 0x1f)) {
+ fprintf (state->ofp, " movzbl %d(%%%s), %%%s\n", offset, addr_reg, dst_reg);
+ } else if (size == (DATA_SHORT & 0x1f)) {
+ fprintf (state->ofp, " movzwl %d(%%%s), %%%s\n", offset, addr_reg, dst_reg);
+ } else {
+ fprintf (state->ofp, " movl %d(%%%s), %%%s\n", offset, addr_reg, dst_reg);
+ }
+
+ }
+
+}
+
+static int emit_load_address_of_parenthesized_postfix_to_reg_now (const char *reg) {
+
+ char *name;
+
+ enum token_kind member_op;
+ struct local_symbol *src;
+
+ int global_index;
+
+ const char *current_object_tag_name = 0;
+ int current_object_size = 0;
+
+ if (tok.kind != TOK_LPAREN) {
+ return 0;
+ }
+
+ get_token ();
+
+ if (tok.kind == TOK_LPAREN) {
+
+ int64_s offset_value;
+
+ if (parse_constexpr_null_member_address_after_lparen (&offset_value)) {
+
+ emit_load_const32_to_reg_now (reg, offset_value);
+ return 1;
+
+ }
+
+ }
+
+ if (tok.kind != TOK_IDENT) {
+
+ report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "expected identifier after '&('");
+
+ expect (TOK_RPAREN, ")");
+ return 1;
+
+ }
+
+ name = xstrdup (tok.ident);
+ get_token ();
+
+ src = find_local_symbol (name);
+ global_index = find_global_symbol (name);
+
+ if (src) {
+
+ if (src->is_array) {
+
+ current_object_size = src->pointed_size > 0 ? src->pointed_size : src->size;
+ current_object_tag_name = src->pointed_tag_name;
+
+ if (src->is_static && src->static_label) {
+ emit_load_symbol_address_to_reg_now (reg, src->static_label, 0, 0);
+ } else {
+ emit_load_symbol_address_to_reg_now (reg, 0, src->offset, 1);
+ }
+
+ } else if (src->pointer_depth > 0) {
+
+ current_object_size = src->pointed_size > 0 ? src->pointed_size : DATA_PTR;
+ current_object_tag_name = src->pointed_tag_name;
+
+ if (src->is_static && src->static_label) {
+ emit_load_global_to_reg (reg, src->static_label, DATA_PTR);
+
+ } else {
+ emit_load_local_to_reg (reg, src->offset, DATA_PTR);
+ }
+
+ } else if (src->is_static && src->static_label) {
+
+ current_object_size = src->size;
+ emit_load_address_to_reg_now (reg, src->static_label);
+
+ } else {
+
+ current_object_size = src->size;
+
+ /**
+ * A typedef such as section_t can hide the pointer depth from this
+ * older parser. For &(p->member), the base must be the pointer
+ * value stored in the local, not the address of the local slot.
+ *
+ * Keep the pointed aggregate tag as well. Without that, address-of
+ * member expressions such as &symbol->next can fall back to an
+ * unqualified member-name lookup and pick another struct's "next"
+ * field. That emitted symbol + 24 instead of symbol + 36 for
+ * struct symbol::next, corrupting symbol->section in pdas.
+ */
+ if (tok.kind == TOK_ARROW && src->size == (DATA_PTR & 0x1f)) {
+
+ if (src->pointed_tag_name) {
+
+ struct aggregate_tag_entry *entry = find_aggregate_tag (src->pointed_tag_name, 0);
+ current_object_tag_name = src->pointed_tag_name;
+
+ if (entry) {
+ current_object_size = entry->size;
+ }
+
+ }
+
+ emit_load_local_to_reg (reg, src->offset, DATA_PTR);
+
+ } else {
+ emit_load_local_address_to_reg_now (reg, src->offset);
+ }
+
+ }
+
+ } else if (global_index >= 0) {
+
+ if (get_global_symbol_array (name)) {
+
+ current_object_size = get_global_symbol_pointed_size (name);
+ emit_load_symbol_address_to_reg_now (reg, name, 0, 0);
+
+ } else if (get_global_symbol_pointer_depth (name) > 0 || (tok.kind == TOK_ARROW && get_global_symbol_size (name) == (DATA_PTR & 0x1f))) {
+
+ current_object_size = get_global_symbol_pointed_size (name);
+
+ if (current_object_size <= 0) {
+ current_object_size = DATA_PTR & 0x1f;
+ }
+
+ emit_load_global_to_reg (reg, name, DATA_PTR);
+
+ } else {
+
+ current_object_size = get_global_symbol_size (name);
+ emit_load_address_to_reg_now (reg, name);
+
+ }
+
+ } else {
+
+ report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "unknown symbol '%s'", name);
+ free (name);
+
+ expect (TOK_RPAREN, ")");
+ return 1;
+
+ }
+
+ if (tok.kind == TOK_LBRACK) {
+
+ int elem_size = DATA_INT & 0x1f;
+
+ if (src) {
+
+ elem_size = src->is_array ? (src->pointer_depth ? DATA_PTR : (src->array_element_size > 0 ? src->array_element_size : src->pointed_size)) :
+ (src->pointer_depth > 1 ? DATA_PTR : src->pointed_size);
+
+ } else if (global_index >= 0) {
+
+ elem_size = get_global_symbol_array (name) ?
+ (get_global_symbol_pointer_depth (name) ? DATA_PTR : (get_global_symbol_array_element_size (name) > 0 ? get_global_symbol_array_element_size (name) : get_global_symbol_pointed_size (name))) :
+ (get_global_symbol_pointer_depth (name) > 1 ? DATA_PTR : get_global_symbol_pointed_size (name));
+
+ }
+
+ if (elem_size <= 0) {
+ elem_size = DATA_INT & 0x1f;
+ }
+
+ emit_parse_postfix_subscript_scaled_address_to_reg_now (reg, elem_size);
+
+ }
+
+ free (name);
+
+ while (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) {
+
+ char *member;
+
+ const char *member_start;
+ const char *member_caret;
+
+ unsigned long member_line;
+
+ int offset;
+ int size;
+ int elem_size;
+ int pointer_depth;
+
+ member_op = tok.kind;
+ get_token ();
+
+ member_start = tok.start;
+ member_caret = tok.caret;
+ member_line = get_line_number ();
+
+ if (tok.kind != TOK_IDENT) {
+
+ report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "expected member name after %s", member_op == TOK_ARROW ? "->" : ".");
+
+ expect (TOK_RPAREN, ")");
+ return 1;
+
+ }
+
+ member = xstrdup (tok.ident);
+ get_token ();
+
+ offset = 0;
+ size = DATA_INT & 0x1f;
+ elem_size = DATA_INT & 0x1f;
+ pointer_depth = 0;
+
+ if (!find_member_info_ex_bounded (member, current_object_size, current_object_tag_name, &offset, &size, &elem_size, &pointer_depth, 0, 0)) {
+
+ report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "unknown member '%s'", member);
+
+ free (member);
+ expect (TOK_RPAREN, ")");
+
+ return 1;
+
+ }
+
+ current_object_tag_name = last_found_member_tag_name;
+ free (member);
+
+ if (tok.kind == TOK_LBRACK) {
+
+ if (pointer_depth > 0) {
+
+ emit_load_member_from_addr_reg_now (reg, reg, offset, DATA_PTR & 0x1f);
+ emit_parse_postfix_subscript_scaled_address_to_reg_now (reg, elem_size);
+
+ } else {
+
+ if (state->ofp && offset != 0) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " add %s, %d\n", reg, offset);
+ } else {
+ fprintf (state->ofp, " addl $%d, %%%s\n", offset, reg);
+ }
+
+ }
+
+ emit_parse_postfix_subscript_scaled_address_to_reg_now (reg, elem_size);
+
+ }
+
+ } else {
+
+ if (state->ofp && offset != 0) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " add %s, %d\n", reg, offset);
+ } else {
+ fprintf (state->ofp, " addl $%d, %%%s\n", offset, reg);
+ }
+
+ }
+
+ }
+
+ if (pointer_depth > 0) {
+ current_object_size = elem_size > 0 ? elem_size : DATA_PTR;
+ } else {
+ current_object_size = size;
+ }
+
+ }
+
+ expect (TOK_RPAREN, ")");
+ return 1;
+
+}
+
+static void emit_store_member_to_addr_reg_now (const char *addr_reg, int offset, const char *value_reg, int size) {
+
+ if (!state->ofp) {
+ return;
+ }
+
+ if (offset != 0) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " add %s, %d\n", addr_reg, offset);
+ } else {
+ fprintf (state->ofp, " addl $%d, %%%s\n", offset, addr_reg);
+ }
+
+ }
+
+ emit_store_reg_to_deref_reg_now (addr_reg, value_reg, size);
+
+}
+
+static void emit_store_floating_member_to_addr_reg_now (const char *addr_reg, int offset, int size) {
+
+ if (!state->ofp) {
+ return;
+ }
+
+ if (offset != 0) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " add %s, %d\n", addr_reg, offset);
+ } else {
+ fprintf (state->ofp, " addl $%d, %%%s\n", offset, addr_reg);
+ }
+
+ }
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " fstp %s [%s]\n" : " fstp %s ptr [%s]\n", size == (DATA_DOUBLE & 0x1f) ? "qword" : "dword", addr_reg);
+ } else {
+ fprintf (state->ofp, " fstp%s (%%%s)\n", size == (DATA_DOUBLE & 0x1f) ? "l" : "s", addr_reg);
+ }
+
+}
+
+static void emit_load_symbol_address_for_copy_now (const char *reg, struct local_symbol *sym, const char *name) {
+
+ if (sym) {
+
+ if (sym->is_static && sym->static_label) {
+ emit_load_address_to_reg_now (reg, sym->static_label);
+ } else {
+ emit_load_local_address_to_reg_now (reg, sym->offset);
+ }
+
+ } else {
+ emit_load_address_to_reg_now (reg, name);
+ }
+
+}
+
+static void emit_copy_fixed_size_now (int size) {
+
+ int offset = 0;
+
+ if (!state->ofp) {
+ return;
+ }
+
+ while (size - offset >= 4) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " mov ecx, dword [eax + %d]\n" : " mov ecx, dword ptr [eax + %d]\n", offset);
+ fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " mov dword [edx + %d], ecx\n" : " mov dword ptr [edx + %d], ecx\n", offset);
+
+ } else {
+
+ fprintf (state->ofp, " movl %d(%%eax), %%ecx\n", offset);
+ fprintf (state->ofp, " movl %%ecx, %d(%%edx)\n", offset);
+
+ }
+
+ offset += 4;
+
+ }
+
+ while (size - offset >= 2) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " mov cx, word [eax + %d]\n" : " mov cx, word ptr [eax + %d]\n", offset);
+ fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " mov word [edx + %d], cx\n" : " mov word ptr [edx + %d], cx\n", offset);
+
+ } else {
+
+ fprintf (state->ofp, " movw %d(%%eax), %%cx\n", offset);
+ fprintf (state->ofp, " movw %%cx, %d(%%edx)\n", offset);
+
+ }
+
+ offset += 2;
+
+ }
+
+ while (size - offset >= 1) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " mov cl, byte [eax + %d]\n" : " mov cl, byte ptr [eax + %d]\n", offset);
+ fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " mov byte [edx + %d], cl\n" : " mov byte ptr [edx + %d], cl\n", offset);
+
+ } else {
+
+ fprintf (state->ofp, " movb %d(%%eax), %%cl\n", offset);
+ fprintf (state->ofp, " movb %%cl, %d(%%edx)\n", offset);
+
+ }
+
+ offset++;
+
+ }
+
+}
+
+static void emit_memcpy_symbol_to_symbol_now (struct local_symbol *dst, const char *dst_name, struct local_symbol *src, const char *src_name, int size) {
+
+ emit_load_symbol_address_for_copy_now ("eax", src, src_name);
+ emit_load_symbol_address_for_copy_now ("edx", dst, dst_name);
+ emit_copy_fixed_size_now (size);
+
+}
+
+static int token_identifier_is_function_call_rhs_now (void) {
+
+ struct token *saved_tok;
+ int is_call;
+
+ if (tok.kind != TOK_IDENT || !tok.ident) {
+ return 0;
+ }
+
+ /**
+ * Do not inspect tok.start/tok.caret to decide whether this identifier is
+ * followed by a call. After macro substitution the spelling in the source
+ * can be a different length from tok.ident, e.g.
+ *
+ * #define cc_parse_type ccpartype
+ * member.type = cc_parse_type(reader);
+ *
+ * tok.ident is "ccpartype" but tok.caret still points into the original
+ * text "cc_parse_type(reader)". The old spelling-based test missed the
+ * '(' and the aggregate-copy fast path treated the function symbol as an
+ * object, leaving the argument list unconsumed and producing "expected ;".
+ */
+ saved_tok = xmalloc (sizeof (*saved_tok));
+ *saved_tok = tok;
+
+ if (tok.ident) {
+ saved_tok->ident = xstrdup (tok.ident);
+ }
+
+ if (tok.start) {
+
+ const char *old_start = tok.start;
+ const char *old_caret = tok.caret;
+
+ saved_tok->start = xstrdup (old_start);
+
+ if (old_caret && old_caret >= old_start) {
+ saved_tok->caret = saved_tok->start + (old_caret - old_start);
+ } else {
+ saved_tok->caret = saved_tok->start;
+ }
+
+ }
+
+ get_token ();
+
+ is_call = (tok.kind == TOK_LPAREN);
+ unget_token (saved_tok);
+
+ return is_call;
+
+}
+
+static struct token *clone_current_token_now (void) {
+
+ struct token *saved_tok = xmalloc (sizeof (*saved_tok));
+ *saved_tok = tok;
+
+ if (tok.ident) {
+ saved_tok->ident = xstrdup (tok.ident);
+ }
+
+ if (tok.start) {
+
+ const char *old_start = tok.start;
+ const char *old_caret = tok.caret;
+
+ saved_tok->start = xstrdup (old_start);
+
+ if (old_caret && old_caret >= old_start) {
+ saved_tok->caret = saved_tok->start + (old_caret - old_start);
+ } else {
+ saved_tok->caret = saved_tok->start;
+ }
+
+ }
+
+ return saved_tok;
+
+}
+
+static int emit_aggregate_copy_from_current_rhs_to_addr_reg_now (const char *addr_reg, int offset, int size) {
+
+ struct local_symbol *rhs_sym;
+ char *rhs_name;
+
+ const char *rhs_start;
+ const char *rhs_caret;
+
+ unsigned long rhs_line;
+
+ int rhs_global_index;
+ int rhs_size;
+
+ int source_size;
+ int source_ready;
+
+ /*
+ * Only use the aggregate-copy fast path when the destination is
+ * itself an aggregate object. Pointer members can legally be assigned
+ * an array object, e.g. fp->intBuffer = buffer1; in pdpclib. The RHS
+ * symbol then has a large array size, but the LHS is only a pointer, so
+ * emitting a fixed-size aggregate copy corrupts the FILE object.
+ */
+ if (size < (DATA_LLONG & 0x1f)) {
+ return 0;
+ }
+
+ if (tok.kind == TOK_STAR) {
+
+ struct token *saved_tok = clone_current_token_now ();
+ struct local_symbol *ptr_sym;
+
+ char *ptr_name;
+
+ int ptr_global_index;
+ int ptr_depth;
+ int ptr_pointed_size;
+
+ get_token ();
+
+ if (tok.kind != TOK_IDENT || !tok.ident) {
+
+ unget_token (saved_tok);
+ return 0;
+
+ }
+
+ ptr_name = xstrdup (tok.ident);
+ ptr_sym = find_local_symbol (ptr_name);
+ ptr_global_index = find_global_symbol (ptr_name);
+
+ if (!ptr_sym && ptr_global_index < 0) {
+
+ free (ptr_name);
+
+ unget_token (saved_tok);
+ return 0;
+
+ }
+
+ ptr_depth = ptr_sym ? ptr_sym->pointer_depth : get_global_symbol_pointer_depth (ptr_name);
+ ptr_pointed_size = ptr_sym ? ptr_sym->pointed_size : get_global_symbol_pointed_size (ptr_name);
+
+ if (!ptr_sym && ptr_global_index >= 0
+ && get_global_symbol_kind (ptr_name) == GLOBAL_SYMBOL_FUNCTION) {
+
+ const char *call_start = tok.start;
+ const char *call_caret = tok.caret;
+ unsigned long call_line = get_line_number ();
+
+ get_token ();
+
+ if (tok.kind != TOK_LPAREN || ptr_depth <= 0
+ || (ptr_depth == 1 && ptr_pointed_size > 0 && ptr_pointed_size < size)) {
+
+ free (ptr_name);
+
+ unget_token (saved_tok);
+ return 0;
+
+ }
+
+ emit_push_reg_now (addr_reg);
+ emit_call_identifier_to_reg_now (ptr_name, "eax", call_start, call_caret, call_line);
+ emit_pop_reg_now ("edx");
+
+ if (offset != 0 && state->ofp) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " add edx, %d\n", offset);
+ } else {
+ fprintf (state->ofp, " addl $%d, %%edx\n", offset);
+ }
+
+ }
+
+ emit_copy_fixed_size_now (size);
+
+ free (ptr_name);
+ return 1;
+
+ }
+
+ /*
+ * For aggregate initialization from a dereferenced pointer, keep the
+ * full pointed-to object size. The old code masked pointed_size with
+ * 0x1f as if it were a DATA_* scalar type. That turns e.g.
+ *
+ * struct hashtab old_hashtab = *hashtab;
+ *
+ * from a 44-byte copy into a rejected aggregate fast path because
+ * 44 & 0x1f == 12. The initializer then falls back to scalar code
+ * and copies only the first word, leaving old_hashtab mostly
+ * uninitialized and crashing in rehash().
+ */
+ source_size = ptr_depth > 1 ? (DATA_PTR & 0x1f) : ptr_pointed_size;
+
+ if (ptr_depth <= 0 || source_size < size) {
+
+ free (ptr_name);
+
+ unget_token (saved_tok);
+ return 0;
+
+ }
+
+ emit_push_reg_now (addr_reg);
+
+ if (ptr_sym) {
+
+ if (ptr_sym->is_static && ptr_sym->static_label) {
+ emit_load_global_to_reg ("eax", ptr_sym->static_label, DATA_PTR);
+ } else {
+ emit_load_local_to_reg ("eax", ptr_sym->offset, DATA_PTR);
+ }
+
+ } else {
+ emit_load_global_to_reg ("eax", ptr_name, DATA_PTR);
+ }
+
+ get_token ();
+
+ if (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) {
+
+ enum token_kind member_op = tok.kind;
+ char *member;
+
+ int member_offset = 0;
+ int member_size = DATA_INT & 0x1f;
+ int member_elem_size = DATA_INT & 0x1f;
+ int member_pointer_depth = 0;
+ int member_is_array = 0;
+ int member_incdec = 0;
+
+ enum token_kind member_incdec_op = TOK_EOF;
+ const char *object_tag_name = ptr_sym ? ptr_sym->pointed_tag_name : 0;
+
+ int object_size = ptr_sym ? ptr_sym->pointed_size : get_global_symbol_pointed_size (ptr_name);
+ get_token ();
+
+ if (tok.kind != TOK_IDENT || !tok.ident) {
+
+ free (ptr_name);
+
+ unget_token (saved_tok);
+ return 0;
+
+ }
+
+ member = xstrdup (tok.ident);
+ get_token ();
+
+ if (!find_member_info_ex_bounded (member, object_size, object_tag_name,
+ &member_offset, &member_size, &member_elem_size,
+ &member_pointer_depth, &member_is_array, 0)) {
+
+ free (member);
+ free (ptr_name);
+
+ unget_token (saved_tok);
+ return 0;
+
+ }
+
+ if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) {
+
+ member_incdec = 1;
+ member_incdec_op = tok.kind;
+
+ get_token ();
+
+ }
+
+ if (member_op != TOK_ARROW || member_pointer_depth <= 0 || member_elem_size < size) {
+
+ free (member);
+ free (ptr_name);
+
+ unget_token (saved_tok);
+ return 0;
+
+ }
+
+ emit_push_reg_now (addr_reg);
+
+ if (ptr_sym) {
+
+ if (ptr_sym->is_static && ptr_sym->static_label) {
+ emit_load_global_to_reg ("ecx", ptr_sym->static_label, DATA_PTR);
+ } else {
+ emit_load_local_to_reg ("ecx", ptr_sym->offset, DATA_PTR);
+ }
+
+ } else {
+ emit_load_global_to_reg ("ecx", ptr_name, DATA_PTR);
+ }
+
+ if (state->ofp) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ?
+ " mov eax, dword [ecx + %d]\n" :
+ " mov eax, dword ptr [ecx + %d]\n", member_offset);
+
+ if (member_incdec) {
+
+ fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ?
+ " %s dword [ecx + %d], %d\n" :
+ " %s dword ptr [ecx + %d], %d\n",
+ member_incdec_op == TOK_INCR ? "add" : "sub",
+ member_offset, member_elem_size);
+
+ }
+
+ } else {
+
+ fprintf (state->ofp, " movl %d(%%ecx), %%eax\n", member_offset);
+
+ if (member_incdec) {
+
+ fprintf (state->ofp, " %sl $%d, %d(%%ecx)\n",
+ member_incdec_op == TOK_INCR ? "add" : "sub",
+ member_elem_size, member_offset);
+
+ }
+
+ }
+
+ }
+
+ emit_pop_reg_now ("edx");
+
+ if (offset != 0 && state->ofp) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " add edx, %d\n", offset);
+ } else {
+ fprintf (state->ofp, " addl $%d, %%edx\n", offset);
+ }
+
+ }
+
+ emit_copy_fixed_size_now (size);
+
+ free (member);
+ free (ptr_name);
+
+ return 1;
+
+ }
+
+ emit_pop_reg_now ("edx");
+
+ if (offset != 0 && state->ofp) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " add edx, %d\n", offset);
+ } else {
+ fprintf (state->ofp, " addl $%d, %%edx\n", offset);
+ }
+
+ }
+
+ emit_copy_fixed_size_now (size);
+
+ free (ptr_name);
+ return 1;
+
+ }
+
+ if (tok.kind == TOK_LPAREN) {
+
+ struct token *saved_tok = clone_current_token_now ();
+ struct local_symbol *ptr_sym;
+
+ char *ptr_name;
+
+ const char *ptr_start;
+ const char *ptr_caret;
+
+ unsigned long ptr_line;
+
+ int ptr_global_index;
+ int ptr_depth;
+
+ const char *ptr_tag_name;
+
+ int current_object_size;
+ int have_direct_object_pointer;
+
+ get_token ();
+
+ if (tok.kind != TOK_STAR) {
+
+ unget_token (saved_tok);
+ return 0;
+
+ }
+
+ get_token ();
+
+ if (tok.kind != TOK_IDENT || !tok.ident) {
+
+ unget_token (saved_tok);
+ return 0;
+
+ }
+
+ ptr_name = xstrdup (tok.ident);
+ ptr_start = tok.start;
+ ptr_caret = tok.caret;
+ ptr_line = get_line_number ();
+ ptr_sym = find_local_symbol (ptr_name);
+ ptr_global_index = find_global_symbol (ptr_name);
+
+ if (!ptr_sym && ptr_global_index < 0) {
+
+ free (ptr_name);
+
+ unget_token (saved_tok);
+ return 0;
+
+ }
+
+ ptr_depth = ptr_sym ? ptr_sym->pointer_depth : get_global_symbol_pointer_depth (ptr_name);
+ ptr_tag_name = ptr_sym ? ptr_sym->pointed_tag_name : 0;
+
+ current_object_size = ptr_sym ? ptr_sym->pointed_size : get_global_symbol_pointed_size (ptr_name);
+ source_size = current_object_size;
+
+ if (ptr_tag_name) {
+
+ struct aggregate_tag_entry *entry = find_aggregate_tag (ptr_tag_name, 0);
+
+ if (entry) {
+
+ current_object_size = entry->size;
+ source_size = current_object_size;
+
+ }
+
+ }
+
+ get_token ();
+
+ if (tok.kind != TOK_RPAREN || ptr_depth <= 0) {
+
+ free (ptr_name);
+
+ unget_token (saved_tok);
+ return 0;
+
+ }
+
+ get_token ();
+
+ if (tok.kind != TOK_ARROW && tok.kind != TOK_DOT) {
+
+ free (ptr_name);
+
+ unget_token (saved_tok);
+ return 0;
+
+ }
+
+ emit_push_reg_now (addr_reg);
+
+ if (ptr_sym) {
+
+ if (ptr_sym->is_static && ptr_sym->static_label) {
+ emit_load_global_to_reg ("eax", ptr_sym->static_label, DATA_PTR);
+ } else {
+ emit_load_local_to_reg ("eax", ptr_sym->offset, DATA_PTR);
+ }
+
+ } else {
+ emit_load_global_to_reg ("eax", ptr_name, DATA_PTR);
+ }
+
+ emit_load_deref_reg_now ("eax", DATA_PTR);
+ have_direct_object_pointer = 1;
+
+ while (tok.kind == TOK_ARROW || tok.kind == TOK_DOT || tok.kind == TOK_LBRACK) {
+
+ if (tok.kind == TOK_LBRACK) {
+
+ emit_parse_postfix_subscript_scaled_address_to_reg_now ("eax", DATA_PTR);
+
+ current_object_size = DATA_PTR;
+ continue;
+
+ }
+
+ {
+
+ enum token_kind member_op = tok.kind;
+ char *member;
+
+ int member_offset = 0;
+ int member_size = DATA_INT & 0x1f;
+ int member_elem_size = DATA_INT & 0x1f;
+ int member_pointer_depth = 0;
+ int member_is_array = 0;
+
+ get_token ();
+
+ if (tok.kind != TOK_IDENT || !tok.ident) {
+
+ report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret,
+ "expected member name after %s", member_op == TOK_ARROW ? "->" : ".");
+
+ free (ptr_name);
+ return 0;
+
+ }
+
+ member = xstrdup (tok.ident);
+ get_token ();
+
+ if (!find_member_info_ex_bounded (member, current_object_size, ptr_tag_name,
+ &member_offset, &member_size, &member_elem_size,
+ &member_pointer_depth, &member_is_array, 0)) {
+
+ report_line_at (get_filename (), ptr_line, REPORT_ERROR, ptr_start, ptr_caret,
+ "unknown member '%s'", member);
+
+ free (member);
+ free (ptr_name);
+
+ return 0;
+
+ }
+
+ ptr_tag_name = last_found_member_tag_name;
+
+ if (member_op == TOK_ARROW) {
+
+ if (have_direct_object_pointer) {
+ have_direct_object_pointer = 0;
+ } else {
+ emit_load_deref_reg_now ("eax", DATA_PTR);
+ }
+
+ } else {
+ have_direct_object_pointer = 0;
+ }
+
+ if (member_offset != 0 && state->ofp) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " add eax, %d\n", member_offset);
+ } else {
+ fprintf (state->ofp, " addl $%d, %%eax\n", member_offset);
+ }
+
+ }
+
+ current_object_size = member_size;
+ source_size = member_size;
+
+ free (member);
+
+ }
+
+ }
+
+ if (source_size < size) {
+
+ free (ptr_name);
+ return 0;
+
+ }
+
+ emit_pop_reg_now ("edx");
+
+ if (offset != 0 && state->ofp) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " add edx, %d\n", offset);
+ } else {
+ fprintf (state->ofp, " addl $%d, %%edx\n", offset);
+ }
+
+ }
+
+ emit_copy_fixed_size_now (size);
+
+ free (ptr_name);
+ return 1;
+
+ }
+
+ if (tok.kind != TOK_IDENT) {
+ return 0;
+ }
+
+ /*
+ * A function-call RHS that returns a struct/union must be parsed by
+ * the normal expression code. The aggregate-copy fast path is only
+ * for copying an already-existing aggregate object by address.
+ */
+ if (token_identifier_is_function_call_rhs_now ()) {
+ return 0;
+ }
+
+ if (!rhs_text_is_plain_aggregate_lvalue_now (tok.caret ? tok.caret : tok.start)) {
+ return 0;
+ }
+
+ rhs_name = xstrdup (tok.ident);
+ rhs_start = tok.start;
+ rhs_caret = tok.caret;
+ rhs_line = get_line_number ();
+
+ rhs_sym = find_local_symbol (rhs_name);
+ rhs_global_index = find_global_symbol (rhs_name);
+
+ if (!rhs_sym && rhs_global_index < 0) {
+
+ free (rhs_name);
+ return 0;
+
+ }
+
+ rhs_size = rhs_sym ? rhs_sym->size : get_global_symbol_size (rhs_name);
+
+ {
+
+ struct token *saved_rhs_tok = clone_current_token_now ();
+ get_token ();
+
+ if (rhs_size <= (DATA_LLONG & 0x1f) && tok.kind != TOK_DOT && tok.kind != TOK_ARROW && tok.kind != TOK_LBRACK) {
+
+ free (rhs_name);
+
+ unget_token (saved_rhs_tok);
+ return 0;
+
+ }
+
+ unget_token (saved_rhs_tok);
+
+ }
+
+ emit_push_reg_now (addr_reg);
+ get_token ();
+
+ source_ready = 0;
+ source_size = rhs_size;
+
+ if (tok.kind == TOK_DOT || tok.kind == TOK_ARROW || tok.kind == TOK_LBRACK) {
+
+ if (!emit_parse_postfix_copy_source_address_now ("eax", rhs_sym, rhs_name, rhs_start, rhs_caret, rhs_line)) {
+
+ free (rhs_name);
+ return 0;
+
+ }
+
+ source_size = postfix_copy_lvalue_size;
+ source_ready = 1;
+
+ }
+
+ if (source_size < size) {
+
+ free (rhs_name);
+ return 0;
+
+ }
+
+ if (!source_ready) {
+ emit_load_symbol_address_for_copy_now ("eax", rhs_sym, rhs_name);
+ }
+
+ emit_pop_reg_now ("edx");
+
+ if (offset != 0 && state->ofp) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " add edx, %d\n", offset);
+ } else {
+ fprintf (state->ofp, " addl $%d, %%edx\n", offset);
+ }
+
+ }
+
+ emit_copy_fixed_size_now (size);
+
+ free (rhs_name);
+ return 1;
+
+}
+
+static void emit_load_member_address_for_copy_now (const char *reg, struct local_symbol *sym, const char *name, enum token_kind member_op, int offset) {
+
+ if (sym) {
+
+ if (member_op == TOK_DOT || sym->is_array) {
+
+ if (sym->is_static && sym->static_label) {
+ emit_load_address_to_reg_now (reg, sym->static_label);
+ } else {
+ emit_load_local_address_to_reg_now (reg, sym->offset);
+ }
+
+ } else if (sym->is_static && sym->static_label) {
+ emit_load_global_to_reg (reg, sym->static_label, DATA_PTR);
+ } else {
+ emit_load_local_to_reg (reg, sym->offset, DATA_PTR);
+ }
+
+ } else {
+
+ if (member_op == TOK_DOT || get_global_symbol_array (name)) {
+ emit_load_address_to_reg_now (reg, name);
+ } else {
+ emit_load_global_to_reg (reg, name, DATA_PTR);
+ }
+
+ }
+
+ if (offset != 0 && state->ofp) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " add %s, %d\n", reg, offset);
+ } else {
+ fprintf (state->ofp, " addl $%d, %%%s\n", offset, reg);
+ }
+
+ }
+
+}
+
+static int emit_parse_postfix_copy_source_address_now (const char *reg, struct local_symbol *src, const char *src_name, const char *name_start, const char *name_caret, unsigned long name_line) {
+
+ int have_address = 0;
+ int have_direct_object_pointer = 0;
+ int last_elem_size = DATA_INT & 0x1f;
+ int last_lvalue_size = DATA_INT & 0x1f;
+
+ const char *current_object_tag_name = 0;
+ int current_object_size = 0;
+
+ postfix_copy_lvalue_size = DATA_INT & 0x1f;
+
+ if (!reg) {
+ reg = "eax";
+ }
+
+ if (tok.kind == TOK_LPAREN) {
+
+ if (src) {
+ return 0;
+ }
+
+ ensure_global_function_symbol (src_name, name_start, name_caret, name_line);
+ emit_call_identifier_to_reg_now (src_name, reg, name_start, name_caret, name_line);
+
+ have_address = 1;
+ have_direct_object_pointer = 1;
+
+ }
+
+ if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) {
+
+ enum token_kind postfix_op = tok.kind;
+
+ int pointer_depth;
+ int pointed_size;
+
+ get_token ();
+
+ if (tok.kind != TOK_LBRACK) {
+ return 0;
+ }
+
+ if (src) {
+
+ pointer_depth = src->pointer_depth;
+ pointed_size = src->pointed_size;
+
+ if (src->is_static && src->static_label) {
+ emit_load_global_to_reg (reg, src->static_label, DATA_PTR);
+ } else {
+ emit_load_local_to_reg (reg, src->offset, DATA_PTR);
+ }
+
+ } else if (find_global_symbol (src_name) >= 0) {
+
+ pointer_depth = get_global_symbol_pointer_depth (src_name);
+ pointed_size = get_global_symbol_pointed_size (src_name);
+
+ emit_load_global_to_reg (reg, src_name, DATA_PTR);
+
+ } else {
+
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", src_name);
+ return 0;
+
+ }
+
+ if (pointer_depth <= 0) {
+
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "subscripted value is not a pointer");
+ return 0;
+ }
+
+ last_elem_size = pointer_depth > 1 ? (DATA_PTR & 0x1f) : (pointed_size > 0 ? (pointed_size & 0x1f) : (DATA_INT & 0x1f));
+ last_lvalue_size = last_elem_size;
+
+ have_address = 1;
+ have_direct_object_pointer = 0;
+
+ emit_incdec_symbol_now (src, src_name, postfix_op, name_line, name_start, name_caret);
+
+ }
+
+ while (tok.kind == TOK_ARROW || tok.kind == TOK_DOT || tok.kind == TOK_LBRACK) {
+
+ if (tok.kind == TOK_LBRACK) {
+
+ int elem_size = last_elem_size;
+
+ if (!have_address) {
+
+ if (src) {
+
+ if (src->is_array) {
+ emit_load_symbol_address_for_copy_now (reg, src, src_name);
+ } else if (src->is_static && src->static_label) {
+ emit_load_global_to_reg (reg, src->static_label, DATA_PTR);
+ } else {
+ emit_load_local_to_reg (reg, src->offset, DATA_PTR);
+ }
+
+ elem_size = src->is_array ? (src->pointer_depth ? DATA_PTR : (src->array_element_size > 0 ? src->array_element_size : src->pointed_size)) :
+ (src->pointer_depth > 1 ? DATA_PTR : src->pointed_size);
+
+ } else {
+
+ if (get_global_symbol_array (src_name)) {
+ emit_load_address_to_reg_now (reg, src_name);
+ } else {
+ emit_load_global_to_reg (reg, src_name, DATA_PTR);
+ }
+
+ elem_size = get_global_symbol_array (src_name) ?
+ (get_global_symbol_pointer_depth (src_name) ? DATA_PTR : (get_global_symbol_array_element_size (src_name) > 0 ? get_global_symbol_array_element_size (src_name) : get_global_symbol_pointed_size (src_name))) :
+ (get_global_symbol_pointer_depth (src_name) > 1 ? DATA_PTR : get_global_symbol_pointed_size (src_name));
+
+ }
+
+ if ((elem_size & 0x1f) == 0) {
+ elem_size = DATA_INT & 0x1f;
+ }
+
+ have_address = 1;
+
+ }
+
+ emit_parse_postfix_subscript_scaled_address_to_reg_now (reg, elem_size);
+
+ last_elem_size = elem_size;
+ last_lvalue_size = elem_size;
+
+ current_object_size = elem_size;
+ continue;
+
+ }
+
+ {
+
+ enum token_kind member_op = tok.kind;
+ char *member;
+
+ const char *member_start;
+ const char *member_caret;
+
+ unsigned long member_line;
+
+ int member_offset = 0;
+ int member_size = DATA_INT & 0x1f;
+ int member_elem_size = DATA_INT & 0x1f;
+ int member_pointer_depth = 0;
+ int member_is_array = 0;
+
+ get_token ();
+
+ member_start = tok.start;
+ member_caret = tok.caret;
+ member_line = get_line_number ();
+
+ if (tok.kind != TOK_IDENT) {
+
+ report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret,
+ "expected member name after %s", member_op == TOK_ARROW ? "->" : ".");
+
+ return 0;
+
+ }
+
+ member = xstrdup (tok.ident);
+ get_token ();
+
+ if (current_object_size <= 0) {
+
+ if (src) {
+
+ if (member_op == TOK_ARROW && src->pointer_depth > 0) {
+
+ current_object_size = src->pointed_size;
+ current_object_tag_name = src->pointed_tag_name;
+
+ } else {
+
+ current_object_size = src->size;
+ current_object_tag_name = src->tag_name;
+
+ }
+
+ } else {
+
+ if (member_op == TOK_ARROW && get_global_symbol_pointer_depth (src_name) > 0) {
+
+ current_object_size = get_global_symbol_pointed_size (src_name);
+ current_object_tag_name = get_global_symbol_tag_name (src_name);
+
+ } else {
+
+ current_object_size = get_global_symbol_size (src_name);
+ current_object_tag_name = get_global_symbol_tag_name (src_name);
+
+ }
+
+ }
+
+ }
+
+ if (!find_member_info_ex_bounded (member, current_object_size, current_object_tag_name, &member_offset, &member_size, &member_elem_size, &member_pointer_depth, &member_is_array, 0)) {
+
+ report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret,
+ "unknown member '%s'", member);
+
+ free (member);
+ return 0;
+
+ }
+
+ {
+
+ const char *member_tag_name = last_found_member_tag_name;
+
+ if (member_is_array && member_pointer_depth > 0) {
+
+ /*
+ * For an array member whose element type is a pointer,
+ * the subscript step is one pointer. Do not replace it
+ * with the pointed aggregate size from the member tag.
+ */
+ member_elem_size = DATA_PTR;
+
+ } else if (member_pointer_depth > 1) {
+ member_elem_size = DATA_PTR;
+ } else if (member_pointer_depth == 1 && member_tag_name) {
+
+ struct aggregate_tag_entry *entry = find_aggregate_tag (member_tag_name, 0);
+
+ if (entry) {
+ member_elem_size = entry->size;
+ }
+
+ }
+
+ current_object_tag_name = member_tag_name;
+
+ }
+
+ free (member);
+
+ if (!have_address) {
+
+ emit_load_member_address_for_copy_now (reg, src, src_name, member_op, 0);
+ have_address = 1;
+
+ } else if (member_op == TOK_ARROW) {
+
+ if (have_direct_object_pointer) {
+ have_direct_object_pointer = 0;
+ } else {
+ emit_load_deref_reg_now (reg, DATA_PTR);
+ }
+
+ } else {
+ have_direct_object_pointer = 0;
+ }
+
+ if (tok.kind == TOK_LBRACK && member_pointer_depth > 0 && !member_is_array) {
+ emit_load_member_from_addr_reg_now (reg, reg, member_offset, DATA_PTR & 0x1f);
+ } else if (state->ofp && member_offset != 0) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " add %s, %d\n", reg, member_offset);
+ } else {
+ fprintf (state->ofp, " addl $%d, %%%s\n", member_offset, reg);
+ }
+
+ }
+
+ last_elem_size = member_elem_size;
+ last_lvalue_size = member_size;
+
+ if (member_pointer_depth > 0 || member_is_array) {
+ current_object_size = member_elem_size;
+ } else {
+ current_object_size = member_size;
+ }
+
+ }
+
+ }
+
+ postfix_copy_lvalue_size = last_lvalue_size;
+ return have_address;
+
+}
+
+static void emit_load_assignment_rhs_to_reg (const char *reg);
+
+static void emit_load_floating_deref_reg_now (const char *reg, int size) {
+
+ if (!state->ofp) {
+ return;
+ }
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ if (size == (DATA_FLOAT & 0x1f)) {
+ fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " fld dword [%s]\n" : " fld dword ptr [%s]\n", reg);
+ } else {
+ fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " fld qword [%s]\n" : " fld qword ptr [%s]\n", reg);
+ }
+
+ } else {
+
+ if (size == (DATA_FLOAT & 0x1f)) {
+ fprintf (state->ofp, " flds (%%%s)\n", reg);
+ } else {
+ fprintf (state->ofp, " fldl (%%%s)\n", reg);
+ }
+
+ }
+
+}
+
+static void emit_load_floating_member_from_addr_reg_now (const char *reg, int offset, int size) {
+
+ if (!state->ofp) {
+ return;
+ }
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ if (size == (DATA_FLOAT & 0x1f)) {
+ fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " fld dword [%s + %d]\n" : " fld dword ptr [%s + %d]\n", reg, offset);
+ } else {
+ fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " fld qword [%s + %d]\n" : " fld qword ptr [%s + %d]\n", reg, offset);
+ }
+
+ } else {
+
+ if (size == (DATA_FLOAT & 0x1f)) {
+ fprintf (state->ofp, " flds %d(%%%s)\n", offset, reg);
+ } else {
+ fprintf (state->ofp, " fldl %d(%%%s)\n", offset, reg);
+ }
+
+ }
+
+}
+
+static int expression_text_has_pluseq_before_delim_now (const char *p) {
+
+ int depth = 0;
+ int quote;
+
+ if (!p) {
+ return 0;
+ }
+
+ while (*p) {
+
+ if (depth == 0 && (*p == ',' || *p == ')' || *p == ';' || *p == ']' || *p == '}')) {
+ return 0;
+ }
+
+ if (*p == '\'' || *p == '"') {
+
+ quote = *p++;
+
+ while (*p && *p != quote) {
+
+ if (*p == '\\' && p[1]) {
+ p += 2;
+ } else {
+ p++;
+ }
+
+ }
+
+ if (*p == quote) {
+ p++;
+ }
+
+ continue;
+
+ }
+
+ if (p[0] == '+' && p[1] == '=') {
+ return 1;
+ }
+
+ if (*p == '(' || *p == '[' || *p == '{') {
+ depth++;
+ } else if (*p == ')' || *p == ']' || *p == '}') {
+
+ if (depth > 0) {
+ depth--;
+ }
+
+ }
+
+ p++;
+
+ }
+
+ return 0;
+
+}
+
+static int emit_parse_va_arg_address_to_reg_now (const char *reg, int size) {
+
+ char *name;
+
+ const char *name_start;
+ const char *name_caret;
+
+ unsigned long name_line;
+ struct local_symbol *src;
+
+ int global_index;
+ int64_s inc;
+
+ int outer_paren = 0;
+ int deref_lvalue = 0;
+
+ if (tok.kind == TOK_LPAREN) {
+
+ outer_paren = 1;
+ get_token ();
+
+ }
+
+ if (tok.kind == TOK_LPAREN) {
+
+ get_token ();
+
+ if (tok.kind == TOK_STAR) {
+
+ deref_lvalue = 1;
+ get_token ();
+
+ }
+
+ if (tok.kind != TOK_IDENT) {
+ return 0;
+ }
+
+ name = xstrdup (tok.ident);
+ name_start = tok.start;
+ name_caret = tok.caret;
+ name_line = get_line_number ();
+
+ get_token ();
+ expect (TOK_RPAREN, ")");
+
+ } else {
+
+ if (tok.kind == TOK_STAR) {
+
+ deref_lvalue = 1;
+ get_token ();
+
+ }
+
+ if (tok.kind != TOK_IDENT) {
+ return 0;
+ }
+
+ name = xstrdup (tok.ident);
+ name_start = tok.start;
+ name_caret = tok.caret;
+ name_line = get_line_number ();
+
+ get_token ();
+
+ }
+
+ if (tok.kind != TOK_PLUSEQ) {
+
+ free (name);
+ return 0;
+
+ }
+
+ get_token ();
+
+ if (token_is_sizeof_keyword ()) {
+ inc = sizeof_from_current_token ();
+ } else {
+
+ inc.low = (unsigned long) size;
+ inc.high = 0;
+
+ emit_load_assignment_rhs_to_reg (reg);
+
+ }
+
+ src = find_local_symbol (name);
+ global_index = find_global_symbol (name);
+
+ if (!src && global_index < 0) {
+
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+
+ free (name);
+ return 1;
+
+ }
+
+ if (deref_lvalue) {
+
+ if (src) {
+
+ if (src->is_static && src->static_label) {
+ emit_load_global_to_reg ("edx", src->static_label, DATA_PTR);
+ } else {
+ emit_load_local_to_reg ("edx", src->offset, DATA_PTR);
+ }
+
+ } else {
+ emit_load_global_to_reg ("edx", name, DATA_PTR);
+ }
+
+ if (state->ofp) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " mov %s, dword [edx]\n" : " mov %s, dword ptr [edx]\n", reg);
+ } else {
+ fprintf (state->ofp, " movl (%%edx), %%%s\n", reg);
+ }
+
+ }
+
+ if (state->ofp) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ fprintf (state->ofp, " add %s, %lu\n", reg, inc.low & U32_MASK);
+ fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " mov dword [edx], %s\n" : " mov dword ptr [edx], %s\n", reg);
+
+ } else {
+
+ fprintf (state->ofp, " addl $%lu, %%%s\n", inc.low & U32_MASK, reg);
+ fprintf (state->ofp, " movl %%%s, (%%edx)\n", reg);
+
+ }
+
+ }
+
+ } else {
+
+ if (src) {
+
+ if (src->is_static && src->static_label) {
+ emit_load_global_to_reg (reg, src->static_label, DATA_PTR);
+ } else {
+ emit_load_local_to_reg (reg, src->offset, DATA_PTR);
+ }
+
+ } else {
+ emit_load_global_to_reg (reg, name, DATA_PTR);
+ }
+
+ if (state->ofp) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " add %s, %lu\n", reg, inc.low & U32_MASK);
+ } else {
+ fprintf (state->ofp, " addl $%lu, %%%s\n", inc.low & U32_MASK, reg);
+ }
+
+ }
+
+ if (src) {
+
+ if (src->is_static && src->static_label) {
+ emit_store_reg_to_global (src->static_label, DATA_PTR, reg);
+ } else {
+ emit_store_reg_to_local (src->offset, DATA_PTR, reg);
+ }
+
+ } else {
+ emit_store_reg_to_global (name, DATA_PTR, reg);
+ }
+
+ }
+
+ if (tok.kind == TOK_COMMA) {
+
+ get_token ();
+
+ if (_accept (TOK_LPAREN)) {
+
+ if (tok.kind == TOK_STAR) {
+ get_token ();
+ }
+
+ if (tok.kind == TOK_IDENT) {
+ get_token ();
+ }
+
+ expect (TOK_RPAREN, ")");
+
+ } else {
+
+ if (tok.kind == TOK_STAR) {
+ get_token ();
+ }
+
+ if (tok.kind == TOK_IDENT) {
+ get_token ();
+ }
+
+ }
+
+ if (tok.kind == TOK_MINUS) {
+
+ get_token ();
+
+ if (token_is_sizeof_keyword ()) {
+ (void) sizeof_from_current_token ();
+ } else {
+ skip_balanced_until (outer_paren ? TOK_RPAREN : TOK_SEMI, TOK_COMMA, TOK_EOF);
+ }
+
+ }
+
+ }
+
+ if (outer_paren) {
+ expect (TOK_RPAREN, ")");
+ }
+
+ if (state->ofp) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " sub %s, %lu\n", reg, inc.low & U32_MASK);
+ } else {
+ fprintf (state->ofp, " subl $%lu, %%%s\n", inc.low & U32_MASK, reg);
+ }
+
+ }
+
+ free (name);
+ return 1;
+
+}
+
+static int emit_load_parenthesized_indirect_member_to_reg_now (const char *reg) {
+
+ const char *p;
+ char *name;
+
+ const char *name_start;
+ const char *name_caret;
+
+ unsigned long name_line;
+ struct local_symbol *src;
+
+ int global_index;
+ int current_object_size;
+
+ const char *current_tag_name;
+
+ int first_member;
+ int last_member_size;
+ int last_member_pointer_depth;
+ int last_member_elem_size;
+
+ if (tok.kind != TOK_STAR || !tok.caret) {
+ return 0;
+ }
+
+ p = tok.caret + 1;
+
+ while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+ p++;
+ }
+
+ if (!((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || *p == '_')) {
+ return 0;
+ }
+
+ p++;
+
+ while ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || (*p >= '0' && *p <= '9') || *p == '_') {
+ p++;
+ }
+
+ while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+ p++;
+ }
+
+ if (*p != ')') {
+ return 0;
+ }
+
+ p++;
+
+ while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+ p++;
+ }
+
+ if (*p != '.' && !(*p == '-' && p[1] == '>')) {
+ return 0;
+ }
+
+ get_token ();
+
+ name_start = tok.start;
+ name_caret = tok.caret;
+ name_line = get_line_number ();
+
+ if (tok.kind != TOK_IDENT || !tok.ident) {
+
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "expected identifier after *");
+ return 1;
+
+ }
+
+ name = xstrdup (tok.ident);
+ get_token ();
+
+ expect (TOK_RPAREN, ")");
+
+ src = find_local_symbol (name);
+ global_index = find_global_symbol (name);
+
+ if (src) {
+
+ if (src->is_static && src->static_label) {
+ emit_load_global_to_reg (reg, src->static_label, DATA_PTR);
+ } else {
+ emit_load_local_to_reg (reg, src->offset, DATA_PTR);
+ }
+
+ current_object_size = src->pointed_size;
+ current_tag_name = src->pointed_tag_name;
+
+ } else if (global_index >= 0) {
+
+ emit_load_global_to_reg (reg, name, DATA_PTR);
+
+ current_object_size = get_global_symbol_pointed_size (name);
+ current_tag_name = 0;
+
+ } else {
+
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+
+ free (name);
+ return 1;
+
+ }
+
+ free (name);
+
+ emit_load_deref_reg_now (reg, DATA_PTR);
+ first_member = 1;
+
+ last_member_size = DATA_INT & 0x1f;
+ last_member_pointer_depth = 0;
+ last_member_elem_size = DATA_INT & 0x1f;
+
+ while (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) {
+
+ enum token_kind member_op = tok.kind;
+ char *member;
+
+ const char *member_start;
+ const char *member_caret;
+
+ unsigned long member_line;
+
+ int member_offset = 0;
+ int member_size = DATA_INT & 0x1f;
+ int member_elem_size = DATA_INT & 0x1f;
+ int member_pointer_depth = 0;
+ int member_is_array = 0;
+
+ get_token ();
+
+ member_start = tok.start;
+ member_caret = tok.caret;
+ member_line = get_line_number ();
+
+ if (tok.kind != TOK_IDENT || !tok.ident) {
+
+ report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "expected member name after %s", member_op == TOK_ARROW ? "->" : ".");
+ return 1;
+
+ }
+
+ member = xstrdup (tok.ident);
+ get_token ();
+
+ if (!find_member_info_ex_bounded (member, current_object_size, current_tag_name, &member_offset, &member_size, &member_elem_size, &member_pointer_depth, &member_is_array, 0)) {
+
+ report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "unknown member '%s'", member);
+
+ free (member);
+ return 1;
+
+ }
+
+ free (member);
+
+ if (!first_member && member_op == TOK_ARROW) {
+ emit_load_deref_reg_now (reg, DATA_PTR);
+ }
+
+ if (member_offset != 0 && state->ofp) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " add %s, %d\n", reg, member_offset);
+ } else {
+ fprintf (state->ofp, " addl $%d, %%%s\n", member_offset, reg);
+ }
+
+ }
+
+ current_object_size = member_size;
+ current_tag_name = last_found_member_tag_name;
+
+ last_member_size = member_size;
+ last_member_pointer_depth = member_pointer_depth;
+ last_member_elem_size = member_elem_size;
+
+ first_member = 0;
+
+ }
+
+ emit_load_deref_reg_now (reg, last_member_size);
+
+ if (last_member_pointer_depth > 0) {
+ set_rhs_last_pointer_info (last_member_pointer_depth, last_member_elem_size);
+ } else {
+ clear_rhs_last_pointer_info ();
+ }
+
+ return 1;
+
+}
+
+static int source_starts_parenthesized_deref_postfix_incdec_subscript_at (const char *p) {
+
+ if (!p || *p != '*') {
+ return 0;
+ }
+
+ p++;
+
+ while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+ p++;
+ }
+
+ if (!((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || *p == '_')) {
+ return 0;
+ }
+
+ p++;
+
+ while ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || (*p >= '0' && *p <= '9') || *p == '_') {
+ p++;
+ }
+
+ while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+ p++;
+ }
+
+ if (*p != ')') {
+ return 0;
+ }
+
+ p++;
+
+ while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+ p++;
+ }
+
+ if (!((p[0] == '+' && p[1] == '+') || (p[0] == '-' && p[1] == '-'))) {
+ return 0;
+ }
+
+ p += 2;
+
+ while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+ p++;
+ }
+
+ return 1;
+
+}
+
+static int source_starts_lparen_deref_subscript_at (const char *p) {
+
+ if (!p || *p != '(') {
+ return 0;
+ }
+
+ p++;
+
+ while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+ p++;
+ }
+
+ if (*p != '*') {
+ return 0;
+ }
+
+ p++;
+
+ while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+ p++;
+ }
+
+ if (!((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || *p == '_')) {
+ return 0;
+ }
+
+ p++;
+
+ while ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || (*p >= '0' && *p <= '9') || *p == '_') {
+ p++;
+ }
+
+ while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+ p++;
+ }
+
+ if (*p != ')') {
+ return 0;
+ }
+
+ p++;
+
+ while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+ p++;
+ }
+
+ return *p == '[';
+
+}
+
+static int source_starts_lparen_deref_postfix_incdec_at (const char *p) {
+
+ if (!p) {
+ return 0;
+ }
+
+ /*
+ * Most callers pass the caret at the opening parenthesis, but some
+ * token paths leave it just after the '('. Accept both positions so
+ * a statement such as:
+ *
+ * (*parameter_count)++;
+ *
+ * is always routed through the dereferenced-object inc/dec emitter, not
+ * the ordinary pointer-variable inc/dec path. The latter scales by the
+ * pointed-to size and would emit addl $4 for int *, corrupting counts.
+ */
+ if (*p == '(') {
+ p++;
+ }
+
+ while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+ p++;
+ }
+
+ if (*p != '*') {
+ return 0;
+ }
+
+ return source_starts_parenthesized_deref_postfix_incdec_subscript_at (p);
+
+}
+
+static int emit_load_parenthesized_deref_postfix_incdec_subscript_to_reg_now (const char *reg) {
+
+ enum token_kind op;
+ char *name;
+
+ const char *name_start;
+ const char *name_caret;
+
+ unsigned long name_line;
+ struct local_symbol *src;
+
+ int global_index;
+ int pointer_depth = 0;
+ int pointed_size = DATA_INT & 0x1f;
+ int elem_size = DATA_INT & 0x1f;
+ int lvalue_size = DATA_INT & 0x1f;
+ int step = 1;
+
+ const char *addr_reg;
+
+ if (tok.kind != TOK_STAR) {
+ return 0;
+ }
+
+ if (!source_starts_parenthesized_deref_postfix_incdec_subscript_at (tok.caret)) {
+ return 0;
+ }
+
+ get_token ();
+
+ name_start = tok.start;
+ name_caret = tok.caret;
+ name_line = get_line_number ();
+
+ if (tok.kind != TOK_IDENT || !tok.ident) {
+
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "expected identifier after *");
+ return 1;
+
+ }
+
+ name = xstrdup (tok.ident);
+ get_token ();
+
+ if (tok.kind != TOK_RPAREN) {
+
+ free (name);
+ return 0;
+
+ }
+
+ get_token ();
+
+ if (tok.kind != TOK_INCR && tok.kind != TOK_DECR) {
+
+ free (name);
+ return 0;
+
+ }
+
+ op = tok.kind;
+ get_token ();
+
+ src = find_local_symbol (name);
+ global_index = find_global_symbol (name);
+
+ if (src) {
+
+ pointer_depth = src->pointer_depth;
+ pointed_size = src->pointed_size;
+
+ } else if (global_index >= 0) {
+
+ pointer_depth = get_global_symbol_pointer_depth (name);
+ pointed_size = get_global_symbol_pointed_size (name);
+
+ } else {
+
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+
+ free (name);
+ return 1;
+
+ }
+
+ if (pointer_depth <= 0) {
+
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "invalid indirection of non-pointer '%s'", name);
+
+ free (name);
+ return 1;
+
+ }
+
+ if (pointed_size <= 0) {
+ pointed_size = DATA_INT & 0x1f;
+ }
+
+ if (pointer_depth > 1) {
+
+ lvalue_size = DATA_PTR & 0x1f;
+
+ elem_size = (pointer_depth > 2) ? (DATA_PTR & 0x1f) : index_step_size (pointed_size);
+ step = elem_size;
+
+ } else {
+
+ lvalue_size = pointed_size & 0x1f;
+
+ elem_size = pointed_size & 0x1f;
+ step = 1;
+
+ }
+
+ if (!reg) {
+ reg = "eax";
+ }
+
+ addr_reg = (strcmp (reg, "edx") == 0) ? "ecx" : "edx";
+
+ if (src) {
+
+ if (src->is_static && src->static_label) {
+ emit_load_global_to_reg (addr_reg, src->static_label, DATA_PTR);
+ } else {
+ emit_load_local_to_reg (addr_reg, src->offset, DATA_PTR);
+ }
+
+ } else {
+ emit_load_global_to_reg (addr_reg, name, DATA_PTR);
+ }
+
+ emit_load_member_from_addr_reg_now (reg, addr_reg, 0, lvalue_size);
+ emit_push_reg_now (reg);
+
+ if (state->ofp) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, op == TOK_INCR ? " add %s, %d\n" : " sub %s, %d\n", reg, step);
+ } else {
+ fprintf (state->ofp, op == TOK_INCR ? " addl $%d, %%%s\n" : " subl $%d, %%%s\n", step, reg);
+ }
+
+ }
+
+ emit_store_reg_to_deref_reg_now (addr_reg, reg, lvalue_size);
+ emit_pop_reg_now (reg);
+
+ if (tok.kind == TOK_LBRACK) {
+ emit_parse_postfix_subscripts_to_reg_now (reg, elem_size, pointer_depth - 1, pointed_size);
+ }
+
+ if (pointer_depth > 1) {
+ set_rhs_last_pointer_info (pointer_depth - 1, pointed_size);
+ } else {
+ clear_rhs_last_pointer_info ();
+ }
+
+ free (name);
+ return 1;
+
+}
+
+static int source_starts_deref_parenthesized_deref_postfix_incdec_assignment_at (const char *p) {
+
+ if (!p) {
+ return 0;
+ }
+
+ if (*p == '*') {
+ p++;
+ }
+
+ while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+ p++;
+ }
+
+ if (*p != '(') {
+ return 0;
+ }
+
+ p++;
+
+ while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+ p++;
+ }
+
+ if (*p != '*') {
+ return 0;
+ }
+
+ p++;
+
+ while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+ p++;
+ }
+
+ if (!((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || *p == '_')) {
+ return 0;
+ }
+
+ p++;
+
+ while ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || (*p >= '0' && *p <= '9') || *p == '_') {
+ p++;
+ }
+
+ while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+ p++;
+ }
+
+ if (*p != ')') {
+ return 0;
+ }
+
+ p++;
+
+ while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+ p++;
+ }
+
+ if (!((p[0] == '+' && p[1] == '+') || (p[0] == '-' && p[1] == '-'))) {
+ return 0;
+ }
+
+ p += 2;
+
+ while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+ p++;
+ }
+
+ return *p == '=';
+
+}
+
+static int source_starts_deref_parenthesized_deref_postfix_incdec_value_at (const char *p) {
+
+ if (!p || *p != '*') {
+ return 0;
+ }
+
+ p++;
+
+ while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+ p++;
+ }
+
+ if (*p != '(') {
+ return 0;
+ }
+
+ p++;
+
+ while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+ p++;
+ }
+
+ if (*p != '(') {
+ return 0;
+ }
+
+ p++;
+
+ while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+ p++;
+ }
+
+ if (*p != '*') {
+ return 0;
+ }
+
+ p++;
+
+ while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+ p++;
+ }
+
+ if (!((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || *p == '_')) {
+ return 0;
+ }
+
+ p++;
+
+ while ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || (*p >= '0' && *p <= '9') || *p == '_') {
+ p++;
+ }
+
+ while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+ p++;
+ }
+
+ if (*p != ')') {
+ return 0;
+ }
+
+ p++;
+
+ while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+ p++;
+ }
+
+ if (!((p[0] == '+' && p[1] == '+') || (p[0] == '-' && p[1] == '-'))) {
+ return 0;
+ }
+
+ p += 2;
+
+ while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+ p++;
+ }
+
+ return *p == ')';
+
+}
+
+static int emit_load_deref_parenthesized_deref_postfix_incdec_to_reg_now (const char *reg) {
+
+ enum token_kind op;
+ char *name;
+
+ const char *name_start;
+ const char *name_caret;
+
+ unsigned long name_line;
+ struct local_symbol *src;
+
+ int global_index;
+ int pointer_depth = 0;
+ int pointed_size = DATA_INT & 0x1f;
+ int deref_size = DATA_INT & 0x1f;
+ int step = DATA_PTR & 0x1f;
+
+ if (tok.kind != TOK_STAR) {
+ return 0;
+ }
+
+ if (!source_starts_deref_parenthesized_deref_postfix_incdec_value_at (tok.caret)) {
+ return 0;
+ }
+
+ expect (TOK_STAR, "*");
+ expect (TOK_LPAREN, "(");
+ expect (TOK_LPAREN, "(");
+ expect (TOK_STAR, "*");
+
+ name_start = tok.start;
+ name_caret = tok.caret;
+ name_line = get_line_number ();
+
+ if (tok.kind != TOK_IDENT || !tok.ident) {
+
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "expected identifier after *");
+ return 1;
+
+ }
+
+ name = xstrdup (tok.ident);
+ get_token ();
+
+ expect (TOK_RPAREN, ")");
+
+ if (tok.kind != TOK_INCR && tok.kind != TOK_DECR) {
+
+ free (name);
+ return 0;
+
+ }
+
+ op = tok.kind;
+ get_token ();
+
+ expect (TOK_RPAREN, ")");
+
+ src = find_local_symbol (name);
+ global_index = find_global_symbol (name);
+
+ if (src) {
+
+ pointer_depth = src->pointer_depth;
+ pointed_size = src->pointed_size;
+
+ } else if (global_index >= 0) {
+
+ pointer_depth = get_global_symbol_pointer_depth (name);
+ pointed_size = get_global_symbol_pointed_size (name);
+
+ } else {
+
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+
+ free (name);
+ return 1;
+
+ }
+
+ if (pointer_depth <= 0) {
+
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "invalid indirection of non-pointer '%s'", name);
+
+ free (name);
+ return 1;
+
+ }
+
+ if (pointed_size <= 0) {
+ pointed_size = DATA_INT & 0x1f;
+ }
+
+ deref_size = pointer_depth > 1 ? (pointed_size & 0x1f) : (DATA_INT & 0x1f);
+ step = pointer_depth > 2 ? (DATA_PTR & 0x1f) : index_step_size (pointed_size);
+
+ if (deref_size <= 0) {
+ deref_size = DATA_INT & 0x1f;
+ }
+
+ if (step <= 0) {
+ step = DATA_PTR & 0x1f;
+ }
+
+ if (!reg) {
+ reg = "eax";
+ }
+
+ if (src) {
+
+ if (src->is_static && src->static_label) {
+ emit_load_global_to_reg ("ecx", src->static_label, DATA_PTR);
+ } else {
+ emit_load_local_to_reg ("ecx", src->offset, DATA_PTR);
+ }
+
+ } else {
+ emit_load_global_to_reg ("ecx", name, DATA_PTR);
+ }
+
+ emit_load_member_from_addr_reg_now (reg, "ecx", 0, DATA_PTR & 0x1f);
+ emit_push_reg_now (reg);
+
+ if (state->ofp) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, op == TOK_INCR ? " add %s, %d\n" : " sub %s, %d\n", reg, step);
+ } else {
+ fprintf (state->ofp, op == TOK_INCR ? " addl $%d, %%%s\n" : " subl $%d, %%%s\n", step, reg);
+ }
+
+ }
+
+ emit_store_reg_to_deref_reg_now ("ecx", reg, DATA_PTR & 0x1f);
+ emit_pop_reg_now (reg);
+ emit_load_deref_reg_now (reg, deref_size);
+
+ if (pointer_depth > 2) {
+ set_rhs_last_pointer_info (pointer_depth - 2, pointed_size);
+ } else {
+ clear_rhs_last_pointer_info ();
+ }
+
+ free (name);
+ return 1;
+
+}
+
+static int emit_store_to_deref_parenthesized_deref_postfix_incdec_now (const char *reg) {
+
+ enum token_kind op;
+ char *name;
+
+ const char *name_start;
+ const char *name_caret;
+
+ unsigned long name_line;
+ struct local_symbol *src;
+
+ int global_index;
+ int pointer_depth = 0;
+ int pointed_size = DATA_INT & 0x1f;
+ int store_size = DATA_INT & 0x1f;
+ int step = DATA_PTR & 0x1f;
+
+ if (tok.kind != TOK_STAR) {
+ return 0;
+ }
+
+ if (!source_starts_deref_parenthesized_deref_postfix_incdec_assignment_at (tok.caret)) {
+ return 0;
+ }
+
+ get_token ();
+
+ expect (TOK_LPAREN, "(");
+ expect (TOK_STAR, "*");
+
+ name_start = tok.start;
+ name_caret = tok.caret;
+ name_line = get_line_number ();
+
+ if (tok.kind != TOK_IDENT || !tok.ident) {
+
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "expected identifier after *");
+ return 1;
+
+ }
+
+ name = xstrdup (tok.ident);
+ get_token ();
+
+ expect (TOK_RPAREN, ")");
+
+ if (tok.kind != TOK_INCR && tok.kind != TOK_DECR) {
+
+ free (name);
+ return 0;
+
+ }
+
+ op = tok.kind;
+ get_token ();
+
+ expect (TOK_ASSIGN, "=");
+
+ src = find_local_symbol (name);
+ global_index = find_global_symbol (name);
+
+ if (src) {
+
+ pointer_depth = src->pointer_depth;
+ pointed_size = src->pointed_size;
+
+ } else if (global_index >= 0) {
+
+ pointer_depth = get_global_symbol_pointer_depth (name);
+ pointed_size = get_global_symbol_pointed_size (name);
+
+ } else {
+
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+
+ free (name);
+ return 1;
+
+ }
+
+ if (pointer_depth <= 0) {
+
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "invalid indirection of non-pointer '%s'", name);
+
+ free (name);
+ return 1;
+
+ }
+
+ if (pointed_size <= 0) {
+ pointed_size = DATA_INT & 0x1f;
+ }
+
+ store_size = pointed_size & 0x1f;
+ step = pointed_size & 0x1f;
+
+ if (step <= 0) {
+ step = DATA_PTR & 0x1f;
+ }
+
+ if (!reg) {
+ reg = "eax";
+ }
+
+ if (src) {
+
+ if (src->is_static && src->static_label) {
+ emit_load_global_to_reg ("ecx", src->static_label, DATA_PTR);
+ } else {
+ emit_load_local_to_reg ("ecx", src->offset, DATA_PTR);
+ }
+
+ } else {
+ emit_load_global_to_reg ("ecx", name, DATA_PTR);
+ }
+
+ emit_load_member_from_addr_reg_now ("edx", "ecx", 0, DATA_PTR & 0x1f);
+ emit_push_reg_now ("edx");
+
+ if (state->ofp) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, op == TOK_INCR ? " add edx, %d\n" : " sub edx, %d\n", step);
+ } else {
+ fprintf (state->ofp, op == TOK_INCR ? " addl $%d, %%edx\n" : " subl $%d, %%edx\n", step);
+ }
+
+ }
+
+ emit_store_reg_to_deref_reg_now ("ecx", "edx", DATA_PTR & 0x1f);
+
+ emit_load_assignment_rhs_expression_to_reg (reg);
+ emit_pop_reg_now ("edx");
+ emit_store_reg_to_deref_reg_now ("edx", reg, store_size);
+
+ free (name);
+ return 1;
+
+}
+
+static void emit_load_assignment_rhs_to_reg (const char *reg) {
+
+ clear_rhs_last_pointer_info ();
+
+ if (tok.kind == TOK_LPAREN && source_starts_lparen_deref_postfix_incdec_at (tok.caret)) {
+
+ get_token ();
+
+ if (emit_load_parenthesized_deref_postfix_incdec_subscript_to_reg_now (reg)) {
+ return;
+ }
+
+ }
+
+ /*
+ * Do not special-case parenthesized assignment expressions here.
+ *
+ * The normal parenthesized-expression path below already recurses back
+ * into emit_load_assignment_rhs_expression_to_reg(), and the identifier
+ * operand parser below already _accepts assignment operators. Using the
+ * old source-text detector here was wrong for nested grouping such as:
+ *
+ * ((((reta <<= 1)) & 0x10))
+ *
+ * because it greedily treated all leading '(' tokens as belonging to the
+ * assignment expression, including parentheses that actually enclose the
+ * later binary '&' expression.
+ */
+ if (_accept (TOK_LPAREN)) {
+
+ char *paren_call_name = 0;
+
+ const char *paren_call_start = 0;
+ const char *paren_call_caret = 0;
+
+ unsigned long paren_call_line = 0;
+
+ /*
+ * Function pointer call designator: (*fp)(args).
+ *
+ * If this is left to the normal parenthesized-expression path, the
+ * inner unary * path treats *fp as a data dereference and emits:
+ *
+ * movl off(%ebp), %eax
+ * movl (%eax), %eax
+ *
+ * That is correct for reading *p as an object, but wrong for a
+ * function designator. In a call, the value of fp is already the
+ * target address, so load fp and call through it without the extra
+ * data load. Keep this narrow: only handle the exact token form
+ * (*identifier)(...), and let every other parenthesized expression use
+ * the existing parser.
+ */
+ if (tok.kind == TOK_STAR && tok.caret) {
+
+ const char *p = tok.caret + 1;
+ const char *name_start;
+ const char *name_caret;
+
+ unsigned long name_line;
+ char *name;
+
+ struct local_symbol *src;
+
+ int global_index;
+ int looks_like_call = 0;
+
+ if (emit_load_parenthesized_deref_postfix_incdec_subscript_to_reg_now (reg)) {
+ return;
+ }
+
+ while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+ p++;
+ }
+
+ if ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || *p == '_') {
+
+ p++;
+
+ while ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || (*p >= '0' && *p <= '9') || *p == '_') {
+ p++;
+ }
+
+ while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+ p++;
+ }
+
+ if (*p == ')') {
+
+ p++;
+
+ while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+ p++;
+ }
+
+ looks_like_call = (*p == '(');
+
+ }
+
+ }
+
+ if (looks_like_call) {
+
+ get_token ();
+
+ name_start = tok.start;
+ name_caret = tok.caret;
+ name_line = get_line_number ();
+
+ if (tok.kind != TOK_IDENT || !tok.ident) {
+
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "expected identifier after *");
+ return;
+
+ }
+
+ name = xstrdup (tok.ident);
+ get_token ();
+
+ expect (TOK_RPAREN, ")");
+
+ src = find_local_symbol (name);
+ global_index = find_global_symbol (name);
+
+ if (src) {
+
+ if (src->is_static && src->static_label) {
+ emit_load_global_to_reg (reg, src->static_label, DATA_PTR);
+ } else {
+ emit_load_local_to_reg (reg, src->offset, DATA_PTR);
+ }
+
+ } else if (global_index >= 0) {
+ emit_load_global_to_reg (reg, name, DATA_PTR);
+ } else {
+
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+
+ free (name);
+ return;
+
+ }
+
+ free (name);
+
+ emit_call_pointer_in_reg_now (reg, reg);
+ clear_rhs_last_pointer_info ();
+
+ return;
+
+ }
+
+ }
+
+ if (emit_load_parenthesized_indirect_member_to_reg_now (reg)) {
+ return;
+ }
+
+ if (tok.kind == TOK_IDENT && tok.ident &&
+
+ find_global_symbol (tok.ident) >= 0 &&
+ get_global_symbol_kind (tok.ident) == GLOBAL_SYMBOL_FUNCTION) {
+
+ paren_call_name = xstrdup (tok.ident);
+ paren_call_start = tok.start;
+ paren_call_caret = tok.caret;
+ paren_call_line = get_line_number ();
+
+ }
+
+ if (!is_type_start (tok.kind) && parenthesized_function_designator_call_now ()) {
+
+ char *call_name = xstrdup (tok.ident);
+
+ const char *call_start = tok.start;
+ const char *call_caret = tok.caret;
+
+ unsigned long call_line = get_line_number ();
+ get_token ();
+
+ expect (TOK_RPAREN, ")");
+
+ if (!find_local_symbol (call_name)) {
+ ensure_global_function_symbol (call_name, call_start, call_caret, call_line);
+ }
+
+ emit_call_identifier_to_reg_now (call_name, reg, call_start, call_caret, call_line);
+ free (call_name);
+
+ return;
+
+ }
+
+ if (is_type_start (tok.kind)) {
+
+ int saved_type_size = parsed_type_size;
+ int saved_storage_class = parsed_storage_class;
+ int saved_is_aggregate = parsed_type_is_aggregate;
+ int saved_is_void = parsed_type_is_void;
+ int saved_is_unsigned = parsed_type_is_unsigned;
+ int saved_is_floating = parsed_type_is_floating;
+ int saved_has_tag = parsed_type_has_tag;
+ int saved_is_inline = parsed_type_is_inline;
+ int saved_field_count = parsed_field_count;
+ int saved_fields[MAX_AGG_FIELDS];
+ int saved_declarator_is_pointer = declarator_is_pointer;
+ int saved_declarator_pointer_depth = declarator_pointer_depth;
+ int saved_declarator_has_array = declarator_has_array;
+ int saved_declarator_has_function = declarator_has_function;
+ int saved_declarator_array_unsized = declarator_array_unsized;
+ long saved_declarator_array_count = declarator_array_count;
+
+ char *cast_name = 0;
+
+ int cast_base_size;
+ int cast_is_unsigned = 0;
+ int cast_pointer_depth = 0;
+ int cast_pointed_size = 0;
+
+ int i;
+
+ for (i = 0; i < saved_field_count && i < MAX_AGG_FIELDS; i++) {
+ saved_fields[i] = parsed_field_sizes[i];
+ }
+
+ declarator_is_pointer = 0;
+ declarator_pointer_depth = 0;
+ declarator_has_array = 0;
+ declarator_has_function = 0;
+ declarator_array_unsized = 0;
+ declarator_array_count = 0;
+
+ parse_type_spec ();
+
+ cast_base_size = parsed_type_size & 0x1f;
+ cast_is_unsigned = parsed_type_is_unsigned;
+
+ if (tok.kind != TOK_RPAREN) {
+ parse_declarator (&cast_name);
+ }
+
+ if (declarator_is_pointer) {
+
+ cast_pointer_depth = declarator_pointer_depth > 0 ? declarator_pointer_depth : 1;
+ cast_pointed_size = cast_pointer_depth > 1 ? (DATA_PTR & 0x1f) : cast_base_size;
+
+ if (cast_pointed_size <= 0) {
+ cast_pointed_size = DATA_INT & 0x1f;
+ }
+
+ }
+
+ if (cast_name) {
+ free (cast_name);
+ }
+
+ expect (TOK_RPAREN, ")");
+
+ parsed_type_size = saved_type_size;
+ parsed_storage_class = saved_storage_class;
+ parsed_type_is_aggregate = saved_is_aggregate;
+ parsed_type_is_void = saved_is_void;
+ parsed_type_is_unsigned = saved_is_unsigned;
+ parsed_type_is_floating = saved_is_floating;
+ parsed_type_has_tag = saved_has_tag;
+ parsed_type_is_inline = saved_is_inline;
+
+ clear_parsed_fields ();
+
+ for (i = 0; i < saved_field_count && i < MAX_AGG_FIELDS; i++) {
+ parsed_field_sizes[i] = saved_fields[i];
+ }
+
+ parsed_field_count = saved_field_count;
+
+ declarator_is_pointer = saved_declarator_is_pointer;
+ declarator_pointer_depth = saved_declarator_pointer_depth;
+ declarator_has_array = saved_declarator_has_array;
+ declarator_has_function = saved_declarator_has_function;
+ declarator_array_unsized = saved_declarator_array_unsized;
+ declarator_array_count = saved_declarator_array_count;
+
+ emit_load_assignment_rhs_to_reg (reg);
+
+ while (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) {
+ emit_apply_postfix_member_access_to_reg_now (reg);
+ }
+
+ if (tok.kind == TOK_LBRACK) {
+
+ int cast_subscript_elem_size = cast_pointer_depth > 1 ? (DATA_PTR & 0x1f) : cast_pointed_size;
+
+ if (cast_subscript_elem_size <= 0) {
+ cast_subscript_elem_size = DATA_INT & 0x1f;
+ }
+
+ emit_parse_postfix_subscript_scaled_address_to_reg_now (reg, cast_subscript_elem_size);
+ emit_load_deref_reg_now (reg, cast_subscript_elem_size);
+
+ }
+
+ if (cast_pointer_depth > 0) {
+ set_rhs_last_pointer_info (cast_pointer_depth, cast_pointed_size);
+ } else {
+
+ emit_apply_integer_cast_to_reg_now (reg, cast_base_size, cast_is_unsigned);
+ clear_rhs_last_pointer_info ();
+
+ }
+
+ return;
+
+ }
+
+ if (const_integer_expr_text_is_foldable_now (tok.caret)) {
+
+ int64_s v = const64_from_current_foldable_expr ();
+ expect (TOK_RPAREN, ")");
+
+ emit_load_const32_to_reg_now (reg, v);
+ return;
+
+ }
+
+ emit_load_assignment_rhs_expression_to_reg (reg);
+
+ if (postfix_member_seen && (tok.kind == TOK_INCR || tok.kind == TOK_DECR)) {
+
+ enum token_kind postfix_op = tok.kind;
+ get_token ();
+ emit_apply_postfix_member_incdec_now (reg, postfix_op);
+
+ }
+
+ while (tok.kind == TOK_COMMA) {
+
+ get_token ();
+ emit_load_assignment_rhs_expression_to_reg (reg);
+
+ }
+
+ if (is_assignment_operator (tok.kind)) {
+
+ get_token ();
+ emit_load_assignment_rhs_expression_to_reg (reg);
+
+ }
+
+ if (paren_call_name && tok.kind == TOK_RPAREN) {
+
+ get_token ();
+
+ if (tok.kind == TOK_LPAREN) {
+
+ if (!find_local_symbol (paren_call_name)) {
+ ensure_global_function_symbol (paren_call_name, paren_call_start, paren_call_caret, paren_call_line);
+ }
+
+ emit_call_identifier_to_reg_now (paren_call_name, reg, paren_call_start, paren_call_caret, paren_call_line);
+ free (paren_call_name);
+
+ return;
+
+ }
+
+ } else {
+ expect (TOK_RPAREN, ")");
+ }
+
+ if (paren_call_name) {
+ free (paren_call_name);
+ }
+
+ if (tok.kind == TOK_LBRACK) {
+
+ int subscript_pointer_depth = rhs_last_pointer_depth;
+ int subscript_elem_size = rhs_last_pointed_size;
+
+ if (subscript_elem_size <= 0) {
+ subscript_elem_size = DATA_INT & 0x1f;
+ }
+
+ emit_parse_postfix_subscript_scaled_address_to_reg_now (reg, subscript_elem_size);
+
+ if (is_assignment_operator (tok.kind)) {
+
+ enum token_kind assign_op = tok.kind;
+
+ get_token ();
+ emit_push_reg_now (reg);
+
+ if (assign_op == TOK_ASSIGN && subscript_elem_size > (DATA_LLONG & 0x1f) &&
+ tok.kind == TOK_IDENT && tok.ident && token_identifier_is_function_call_rhs_now ()) {
+
+ char *rhs_name = xstrdup (tok.ident);
+
+ const char *rhs_start = tok.start;
+ const char *rhs_caret = tok.caret;
+
+ unsigned long rhs_line = get_line_number ();
+
+ pending_struct_return_lhs = 0;
+ pending_struct_return_global_name = 0;
+ pending_struct_return_stack_address = 1;
+ pending_struct_return_stack_offset = 0;
+
+ get_token ();
+ emit_call_identifier_to_reg_now (rhs_name, reg, rhs_start, rhs_caret, rhs_line);
+
+ pending_struct_return_stack_address = 0;
+ pending_struct_return_stack_offset = 0;
+
+ emit_pop_reg_now ("edx");
+ free (rhs_name);
+
+ set_rhs_last_pointer_info (0, 0);
+ return;
+
+ } else if (assign_op == TOK_ASSIGN) {
+
+ if (subscript_elem_size > (DATA_LLONG & 0x1f)) {
+
+ pending_struct_return_lhs = 0;
+ pending_struct_return_global_name = 0;
+ pending_struct_return_stack_address = 1;
+ pending_struct_return_stack_offset = 0;
+
+ emit_load_assignment_rhs_expression_to_reg (reg);
+
+ pending_struct_return_stack_address = 0;
+ pending_struct_return_stack_offset = 0;
+
+ emit_pop_reg_now ("edx");
+ set_rhs_last_pointer_info (0, 0);
+
+ return;
+
+ }
+
+ emit_load_assignment_rhs_expression_to_reg (reg);
+
+ } else {
+
+ emit_load_deref_reg_now (reg, subscript_elem_size);
+ emit_push_reg_now (reg);
+
+ emit_load_assignment_rhs_expression_to_reg ("edx");
+ emit_pop_reg_now (reg);
+
+ emit_assignment_binary_op (assign_op, 0);
+
+ }
+
+ emit_pop_reg_now ("edx");
+
+ emit_store_reg_to_deref_reg_now ("edx", reg, subscript_elem_size);
+ set_rhs_last_pointer_info (0, 0);
+
+ return;
+
+ }
+
+ emit_load_deref_reg_now (reg, subscript_elem_size);
+
+ if (subscript_pointer_depth > 0) {
+ subscript_pointer_depth--;
+ }
+
+ if (subscript_pointer_depth > 0) {
+ set_rhs_last_pointer_info (subscript_pointer_depth, subscript_elem_size);
+ } else {
+ set_rhs_last_pointer_info (0, 0);
+ }
+
+ }
+
+ if (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) {
+ emit_apply_postfix_member_access_to_reg_now (reg);
+ }
+
+ if (postfix_member_seen && tok.kind == TOK_LPAREN) {
+
+ emit_call_pointer_in_reg_now (reg, reg);
+
+ set_rhs_last_pointer_info (0, 0);
+ return;
+
+ }
+
+ if (postfix_member_seen) {
+
+ set_rhs_last_pointer_info (postfix_member_pointer_depth, postfix_member_pointed_size);
+
+ if (is_assignment_operator (tok.kind)) {
+
+ enum token_kind op = tok.kind;
+
+ int assign_member_offset = postfix_member_offset;
+ int assign_member_size = postfix_member_size;
+
+ get_token ();
+
+ if (state->ofp) {
+
+ if (op == TOK_ASSIGN) {
+
+ if (postfix_member_is_floating || token_is_floating_constant_now ()) {
+
+ emit_push_reg_now ("edx");
+ emit_load_floating_rhs_expression_now (assign_member_size);
+
+ emit_pop_reg_now ("edx");
+ emit_store_floating_member_to_addr_reg_now ("edx", assign_member_offset, assign_member_size);
+
+ return;
+
+ }
+
+ if (assign_member_size > (DATA_LLONG & 0x1f) &&
+ tok.kind == TOK_IDENT && tok.ident && token_identifier_is_function_call_rhs_now ()) {
+
+ char *rhs_name = xstrdup (tok.ident);
+
+ const char *rhs_start = tok.start;
+ const char *rhs_caret = tok.caret;
+
+ unsigned long rhs_line = get_line_number ();
+ emit_push_reg_now ("edx");
+
+ pending_struct_return_lhs = 0;
+ pending_struct_return_global_name = 0;
+ pending_struct_return_stack_address = 1;
+ pending_struct_return_stack_offset = assign_member_offset;
+
+ get_token ();
+ emit_call_identifier_to_reg_now (rhs_name, reg, rhs_start, rhs_caret, rhs_line);
+
+ pending_struct_return_stack_address = 0;
+ pending_struct_return_stack_offset = 0;
+
+ emit_pop_reg_now ("edx");
+ free (rhs_name);
+
+ return;
+
+ }
+
+ if (emit_aggregate_copy_from_current_rhs_to_addr_reg_now ("edx", assign_member_offset, assign_member_size)) {
+ return;
+ }
+
+ emit_push_reg_now ("edx");
+
+ emit_load_assignment_rhs_expression_to_reg (reg);
+ emit_pop_reg_now ("edx");
+
+ } else {
+
+ /*
+ * emit_apply_postfix_member_access_to_reg_now() has already
+ * loaded the member value into reg and left the containing
+ * object address in edx. Do not load the member again from
+ * reg: for p->m |= x that treats the old value of m as a
+ * pointer and dereferences it, which corrupts/segfaults code
+ * such as section->symbol->flags |= SYMBOL_FLAG_SECTION_SYMBOL.
+ */
+ emit_push_reg_now ("edx");
+ emit_push_reg_now (reg);
+
+ emit_load_assignment_rhs_expression_to_reg ("edx");
+ emit_pop_reg_now (reg);
+
+ emit_assignment_binary_op (op, 0);
+ emit_pop_reg_now ("edx");
+
+ }
+
+ emit_store_member_to_addr_reg_now ("edx", assign_member_offset, reg, assign_member_size);
+
+ } else {
+ emit_load_assignment_rhs_expression_to_reg (reg);
+ }
+
+ }
+
+ }
+
+ /*
+ * This routine parses one primary operand for the outer binary
+ * expression parser. After returning, the caller can still consume
+ * any trailing operator, e.g. the / b in:
+ *
+ * (a + b - 1) / b
+ */
+ return;
+
+ } else if (tok.kind == TOK_AMPER) {
+
+ char *name;
+
+ const char *name_start;
+ const char *name_caret;
+
+ unsigned long name_line;
+ struct local_symbol *src;
+
+ get_token ();
+
+ if (tok.kind == TOK_LPAREN) {
+
+ if (emit_load_address_of_parenthesized_postfix_to_reg_now (reg)) {
+ return;
+ }
+
+ }
+
+ name_start = tok.start;
+ name_caret = tok.caret;
+ name_line = get_line_number ();
+
+ if (tok.kind != TOK_IDENT) {
+
+ int64_s zero;
+
+ zero.low = 0;
+ zero.high = 0;
+
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "expected identifier after '&'");
+
+ emit_load_const32_to_reg_now (reg, zero);
+ return;
+
+ }
+
+ name = xstrdup (tok.ident);
+ get_token ();
+
+ src = find_local_symbol (name);
+
+ if (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) {
+
+ int global_index = find_global_symbol (name);
+
+ const char *current_object_tag_name = 0;
+ int current_object_size = 0;
+
+ if (src) {
+
+ if (tok.kind == TOK_DOT || src->is_array) {
+
+ current_object_size = src->is_array && src->pointed_size > 0 ? src->pointed_size : src->size;
+ current_object_tag_name = src->is_array ? src->pointed_tag_name : 0;
+
+ if (src->is_static && src->static_label) {
+ emit_load_address_to_reg_now (reg, src->static_label);
+ } else {
+ emit_load_local_address_to_reg_now (reg, src->offset);
+ }
+
+ } else if (src->is_static && src->static_label) {
+
+ current_object_size = src->pointed_size > 0 ? src->pointed_size : DATA_PTR;
+ current_object_tag_name = src->pointed_tag_name;
+
+ emit_load_global_to_reg (reg, src->static_label, DATA_PTR);
+
+ } else {
+
+ current_object_size = src->pointed_size > 0 ? src->pointed_size : DATA_PTR;
+ current_object_tag_name = src->pointed_tag_name;
+
+ emit_load_local_to_reg (reg, src->offset, DATA_PTR);
+
+ }
+
+ } else if (global_index >= 0) {
+
+ if (tok.kind == TOK_DOT || get_global_symbol_array (name)) {
+
+ current_object_size = get_global_symbol_array (name) && get_global_symbol_pointed_size (name) > 0 ? get_global_symbol_pointed_size (name) : get_global_symbol_size (name);
+ current_object_tag_name = get_global_symbol_tag_name (name);
+
+ emit_load_address_to_reg_now (reg, name);
+
+ } else {
+
+ current_object_size = get_global_symbol_pointed_size (name) > 0 ? get_global_symbol_pointed_size (name) : DATA_PTR;
+ current_object_tag_name = get_global_symbol_tag_name (name);
+
+ emit_load_global_to_reg (reg, name, DATA_PTR);
+
+ }
+
+ } else {
+
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+
+ free (name);
+ return;
+
+ }
+
+ while (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) {
+
+ enum token_kind member_op = tok.kind;
+ char *member;
+
+ const char *member_start;
+ const char *member_caret;
+
+ unsigned long member_line;
+
+ int offset = 0;
+ int member_size = DATA_INT & 0x1f;
+ int elem_size = DATA_INT & 0x1f;
+ int pointer_depth = 0;
+
+ get_token ();
+
+ member_start = tok.start;
+ member_caret = tok.caret;
+ member_line = get_line_number ();
+
+ if (tok.kind != TOK_IDENT) {
+
+ report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "expected member name after %s", member_op == TOK_ARROW ? "->" : ".");
+
+ free (name);
+ return;
+
+ }
+
+ member = xstrdup (tok.ident);
+ get_token ();
+
+ if (!find_member_info_ex_bounded (member, current_object_size, current_object_tag_name, &offset, &member_size, &elem_size, &pointer_depth, 0, 0)) {
+
+ report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "unknown member '%s'", member);
+
+ free (member);
+ free (name);
+
+ return;
+
+ }
+
+ current_object_tag_name = last_found_member_tag_name;
+ free (member);
+
+ if (state->ofp && offset != 0) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " add %s, %d\n", reg, offset);
+ } else {
+ fprintf (state->ofp, " addl $%d, %%%s\n", offset, reg);
+ }
+
+ }
+
+ if (tok.kind == TOK_LBRACK) {
+
+ if (pointer_depth > 0) {
+
+ emit_load_member_from_addr_reg_now (reg, reg, 0, DATA_PTR & 0x1f);
+ emit_parse_postfix_subscript_scaled_address_to_reg_now (reg, elem_size);
+
+ } else {
+ emit_parse_postfix_subscript_scaled_address_to_reg_now (reg, elem_size);
+ }
+
+ } else if (pointer_depth > 0 && (tok.kind == TOK_ARROW || tok.kind == TOK_DOT)) {
+ emit_load_member_from_addr_reg_now (reg, reg, 0, DATA_PTR & 0x1f);
+ }
+
+ if (pointer_depth > 0) {
+ current_object_size = elem_size > 0 ? elem_size : DATA_PTR;
+ } else {
+ current_object_size = member_size;
+ }
+
+ }
+
+ free (name);
+ return;
+
+ }
+
+ if (tok.kind == TOK_LBRACK) {
+
+ if (src) {
+
+ int elem_size = src->is_array ? (src->pointer_depth ? DATA_PTR : (src->array_element_size > 0 ? src->array_element_size : src->pointed_size)) :
+ (src->pointer_depth > 1 ? DATA_PTR : src->pointed_size);
+
+ if (elem_size <= 0) {
+ elem_size = DATA_INT & 0x1f;
+ }
+
+ if (src->is_array) {
+
+ if (src->is_static && src->static_label) {
+ emit_load_address_to_reg_now (reg, src->static_label);
+ } else {
+ emit_load_local_address_to_reg_now (reg, src->offset);
+ }
+
+ } else if (src->is_static && src->static_label) {
+ emit_load_global_to_reg (reg, src->static_label, DATA_PTR);
+ } else {
+ emit_load_local_to_reg (reg, src->offset, DATA_PTR);
+ }
+
+ emit_parse_postfix_subscript_scaled_address_to_reg_now (reg, elem_size);
+
+ if (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) {
+
+ const char *current_object_tag_name = src->pointed_tag_name;
+ int current_object_size = elem_size;
+
+ while (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) {
+
+ enum token_kind member_op = tok.kind;
+ char *member;
+
+ const char *member_start;
+ const char *member_caret;
+
+ unsigned long member_line;
+
+ int offset = 0;
+ int member_size = DATA_INT & 0x1f;
+ int member_elem_size = DATA_INT & 0x1f;
+ int member_pointer_depth = 0;
+
+ get_token ();
+
+ member_start = tok.start;
+ member_caret = tok.caret;
+ member_line = get_line_number ();
+
+ if (tok.kind != TOK_IDENT) {
+
+ report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "expected member name after %s", member_op == TOK_ARROW ? "->" : ".");
+
+ free (name);
+ return;
+
+ }
+
+ member = xstrdup (tok.ident);
+ get_token ();
+
+ if (!find_member_info_ex_bounded (member, current_object_size, current_object_tag_name, &offset, &member_size, &member_elem_size, &member_pointer_depth, 0, 0)) {
+
+ report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "unknown member '%s'", member);
+
+ free (member);
+ free (name);
+
+ return;
+
+ }
+
+ current_object_tag_name = last_found_member_tag_name;
+ free (member);
+
+ if (state->ofp && offset != 0) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " add %s, %d\n", reg, offset);
+ } else {
+ fprintf (state->ofp, " addl $%d, %%%s\n", offset, reg);
+ }
+
+ }
+
+ if (tok.kind == TOK_LBRACK) {
+
+ if (member_pointer_depth > 0) {
+ emit_load_member_from_addr_reg_now (reg, reg, 0, DATA_PTR & 0x1f);
+ }
+
+ emit_parse_postfix_subscript_scaled_address_to_reg_now (reg, member_elem_size > 0 ? member_elem_size : DATA_INT & 0x1f);
+
+ } else if (member_pointer_depth > 0 && (tok.kind == TOK_ARROW || tok.kind == TOK_DOT)) {
+ emit_load_member_from_addr_reg_now (reg, reg, 0, DATA_PTR & 0x1f);
+ }
+
+ if (member_pointer_depth > 0) {
+ current_object_size = member_elem_size > 0 ? member_elem_size : DATA_PTR;
+ } else {
+ current_object_size = member_size;
+ }
+
+ }
+
+ }
+
+ free (name);
+ return;
+
+ }
+
+ if (find_global_symbol (name) >= 0) {
+
+ int elem_size = get_global_symbol_array (name) ?
+ (get_global_symbol_pointer_depth (name) ? DATA_PTR : (get_global_symbol_array_element_size (name) > 0 ? get_global_symbol_array_element_size (name) : get_global_symbol_pointed_size (name))) :
+ (get_global_symbol_pointer_depth (name) > 1 ? DATA_PTR : get_global_symbol_pointed_size (name));
+
+ if (elem_size <= 0) {
+ elem_size = DATA_INT & 0x1f;
+ }
+
+ if (get_global_symbol_array (name)) {
+ emit_load_address_to_reg_now (reg, name);
+ } else {
+ emit_load_global_to_reg (reg, name, DATA_PTR);
+ }
+
+ emit_parse_postfix_subscript_scaled_address_to_reg_now (reg, elem_size);
+
+ free (name);
+ return;
+
+ }
+
+ }
+
+ if (src) {
+
+ if (src->is_static && src->static_label) {
+ emit_load_address_to_reg_now (reg, src->static_label);
+ } else {
+ emit_load_local_address_to_reg_now (reg, src->offset);
+ }
+
+ free (name);
+ return;
+
+ }
+
+ if (find_global_symbol (name) >= 0) {
+
+ emit_load_address_to_reg_now (reg, name);
+
+ free (name);
+ return;
+
+ }
+
+ {
+
+ int64_s zero;
+
+ zero.low = 0;
+ zero.high = 0;
+
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+
+ free (name);
+ emit_load_const32_to_reg_now (reg, zero);
+
+ }
+
+ return;
+
+ } else if (tok.kind == TOK_STAR) {
+
+ int deref_had_parens = 0;
+ int deref_size = DATA_CHAR & 0x1f;
+ int deref_pointer_depth = 0;
+ int deref_pointed_size = 0;
+
+ if (emit_load_deref_parenthesized_deref_postfix_incdec_to_reg_now (reg)) {
+ return;
+ }
+
+ if (emit_store_to_deref_parenthesized_deref_postfix_incdec_now (reg)) {
+ return;
+ }
+
+ get_token ();
+
+ if (tok.kind == TOK_IDENT) {
+
+ char *lhs_name = xstrdup (tok.ident);
+
+ const char *lhs_start = tok.start;
+ const char *lhs_caret = tok.caret;
+
+ unsigned long lhs_line = get_line_number ();
+ struct local_symbol *lhs_sym;
+
+ enum token_kind lhs_postfix_op = TOK_EOF;
+ enum token_kind member_op = TOK_EOF;
+
+ char *member;
+
+ const char *member_start;
+ const char *member_caret;
+
+ unsigned long member_line;
+
+ int lhs_has_postfix = 0;
+ int offset = 0;
+ int member_size = DATA_PTR & 0x1f;
+ int member_elem_size = DATA_INT & 0x1f;
+ int member_pointer_depth = 0;
+
+ get_token ();
+
+ if (tok.kind == TOK_LPAREN &&
+ find_global_symbol (lhs_name) >= 0 &&
+ get_global_symbol_kind (lhs_name) == GLOBAL_SYMBOL_FUNCTION) {
+
+ int fptr_depth = get_global_symbol_pointer_depth (lhs_name);
+ int fpointed_size = get_global_symbol_pointed_size (lhs_name);
+
+ emit_call_identifier_to_reg_now (lhs_name, reg, lhs_start, lhs_caret, lhs_line);
+
+ if (fptr_depth > 1) {
+ deref_size = DATA_PTR;
+ } else if (fptr_depth == 1 && fpointed_size > 0) {
+ deref_size = fpointed_size;
+ }
+
+ emit_load_deref_reg_now (reg, deref_size);
+
+ free (lhs_name);
+ return;
+
+ }
+
+ if (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) {
+
+ member_op = tok.kind;
+ get_token ();
+
+ member_start = tok.start;
+ member_caret = tok.caret;
+ member_line = get_line_number ();
+
+ if (tok.kind != TOK_IDENT) {
+
+ report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "expected member name after %s", member_op == TOK_ARROW ? "->" : ".");
+
+ free (lhs_name);
+ return;
+
+ }
+
+ member = xstrdup (tok.ident);
+ get_token ();
+
+ if (!find_member_info_ex (member, &offset, &member_size, &member_elem_size, &member_pointer_depth, 0, 0)) {
+
+ report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "unknown member '%s'", member);
+
+ free (member);
+ free (lhs_name);
+
+ return;
+
+ }
+
+ free (member);
+
+ if (member_pointer_depth > 1) {
+ deref_size = DATA_PTR;
+ } else if (member_pointer_depth == 1 && member_elem_size > 0) {
+ deref_size = member_elem_size;
+ }
+
+ if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) {
+
+ lhs_has_postfix = 1;
+ lhs_postfix_op = tok.kind;
+
+ get_token ();
+
+ }
+
+ if (tok.kind == TOK_ASSIGN) {
+
+ lhs_sym = find_local_symbol (lhs_name);
+
+ if (lhs_sym) {
+
+ if (lhs_sym->is_static && lhs_sym->static_label) {
+ emit_load_global_to_reg ("edx", lhs_sym->static_label, DATA_PTR);
+ } else {
+ emit_load_local_to_reg ("edx", lhs_sym->offset, DATA_PTR);
+ }
+
+ } else if (find_global_symbol (lhs_name) >= 0) {
+ emit_load_global_to_reg ("edx", lhs_name, DATA_PTR);
+ } else {
+ report_line_at (get_filename (), lhs_line, REPORT_ERROR, lhs_start, lhs_caret, "unknown symbol '%s'", lhs_name);
+ }
+
+ if (state->ofp) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ if (state->syntax & ASM_SYNTAX_NASM) {
+
+ fprintf (state->ofp, " mov ecx, dword [edx + %d]\n", offset);
+
+ if (lhs_has_postfix) {
+ fprintf (state->ofp, " %s dword [edx + %d]\n", lhs_postfix_op == TOK_INCR ? "inc" : "dec", offset);
+ }
+
+ } else {
+
+ fprintf (state->ofp, " mov ecx, dword ptr [edx + %d]\n", offset);
+
+ if (lhs_has_postfix) {
+ fprintf (state->ofp, " %s dword ptr [edx + %d]\n", lhs_postfix_op == TOK_INCR ? "inc" : "dec", offset);
+ }
+
+ }
+
+ } else {
+
+ fprintf (state->ofp, " movl %d(%%edx), %%ecx\n", offset);
+
+ if (lhs_has_postfix) {
+ fprintf (state->ofp, " %sl %d(%%edx)\n", lhs_postfix_op == TOK_INCR ? "inc" : "dec", offset);
+ }
+
+ }
+
+ }
+
+ emit_push_reg_now ("ecx");
+ get_token ();
+
+ emit_load_assignment_rhs_expression_to_reg (reg);
+ emit_pop_reg_now ("edx");
+ emit_store_reg_to_deref_reg_now ("edx", reg, deref_size);
+
+ free (lhs_name);
+ return;
+
+ }
+
+ lhs_sym = find_local_symbol (lhs_name);
+
+ if (lhs_sym) {
+
+ if (member_op == TOK_DOT) {
+
+ if (lhs_sym->is_static && lhs_sym->static_label) {
+ emit_load_address_to_reg_now (reg, lhs_sym->static_label);
+ } else {
+ emit_load_local_address_to_reg_now (reg, lhs_sym->offset);
+ }
+
+ } else if (lhs_sym->is_static && lhs_sym->static_label) {
+ emit_load_global_to_reg (reg, lhs_sym->static_label, DATA_PTR);
+ } else {
+ emit_load_local_to_reg (reg, lhs_sym->offset, DATA_PTR);
+ }
+
+ } else if (find_global_symbol (lhs_name) >= 0) {
+
+ if (member_op == TOK_DOT) {
+ emit_load_address_to_reg_now (reg, lhs_name);
+ } else {
+ emit_load_global_to_reg (reg, lhs_name, DATA_PTR);
+ }
+
+ } else {
+ report_line_at (get_filename (), lhs_line, REPORT_ERROR, lhs_start, lhs_caret, "unknown symbol '%s'", lhs_name);
+ }
+
+ if (state->ofp) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ if (state->syntax & ASM_SYNTAX_NASM) {
+ fprintf (state->ofp, " mov %s, dword [%s + %d]\n", reg, reg, offset);
+ } else {
+ fprintf (state->ofp, " mov %s, dword ptr [%s + %d]\n", reg, reg, offset);
+ }
+
+ } else {
+ fprintf (state->ofp, " movl %d(%%%s), %%%s\n", offset, reg, reg);
+ }
+
+ }
+
+ if (lhs_has_postfix) {
+ /* Already applied above only for assignment forms. */
+ }
+
+ emit_load_deref_reg_now (reg, deref_size);
+
+ free (lhs_name);
+ return;
+
+ }
+
+ if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) {
+
+ lhs_has_postfix = 1;
+ lhs_postfix_op = tok.kind;
+
+ get_token ();
+
+ }
+
+ if (tok.kind == TOK_ASSIGN) {
+
+ int lhs_deref_is_unsigned = 1;
+ lhs_sym = find_local_symbol (lhs_name);
+
+ if (lhs_sym) {
+
+ if (lhs_sym->pointer_depth > 1) {
+ deref_size = DATA_PTR & 0x1f;
+ } else if (lhs_sym->pointer_depth == 1 && lhs_sym->pointed_size > 0) {
+
+ /**
+ * Keep the full pointed-to object size here. This
+ * assignment path also handles aggregate lvalues such
+ * as *hashtab = old_hashtab; Masking with 0x1f turns
+ * a 44-byte struct hashtab into 12 and emits a partial
+ * copy, corrupting pdas hashtab state during rehash().
+ */
+ deref_size = lhs_sym->pointed_size;
+
+ }
+
+ if (member_op == TOK_DOT) {
+
+ if (lhs_sym->is_static && lhs_sym->static_label) {
+ emit_load_address_to_reg_now ("edx", lhs_sym->static_label);
+ } else {
+ emit_load_local_address_to_reg_now ("edx", lhs_sym->offset);
+ }
+
+ } else if (lhs_sym->is_static && lhs_sym->static_label) {
+ emit_load_global_to_reg ("edx", lhs_sym->static_label, DATA_PTR);
+ } else {
+ emit_load_local_to_reg ("edx", lhs_sym->offset, DATA_PTR);
+ }
+
+ } else if (find_global_symbol (lhs_name) >= 0) {
+
+ if (get_global_symbol_pointer_depth (lhs_name) > 1) {
+ deref_size = DATA_PTR & 0x1f;
+ } else if (get_global_symbol_pointer_depth (lhs_name) == 1 && get_global_symbol_pointed_size (lhs_name) > 0) {
+ deref_size = get_global_symbol_pointed_size (lhs_name);
+ }
+
+ if (member_op == TOK_DOT) {
+ emit_load_address_to_reg_now ("edx", lhs_name);
+ } else {
+ emit_load_global_to_reg ("edx", lhs_name, DATA_PTR);
+ }
+
+ } else {
+ report_line_at (get_filename (), lhs_line, REPORT_ERROR, lhs_start, lhs_caret, "unknown symbol '%s'", lhs_name);
+ }
+
+ emit_push_reg_now ("edx");
+
+ if (lhs_has_postfix) {
+ emit_incdec_symbol_now (lhs_sym, lhs_name, lhs_postfix_op, lhs_line, lhs_start, lhs_caret);
+ }
+
+ get_token ();
+
+ if (deref_size > (DATA_LLONG & 0x1f)) {
+
+ emit_pop_reg_now ("edx");
+
+ if (!emit_store_assignment_to_aggregate_address_now ("edx", deref_size, lhs_start, lhs_caret, lhs_line)) {
+ report_line_at (get_filename (), lhs_line, REPORT_ERROR, lhs_start, lhs_caret, "aggregate assignment expression not implemented");
+ }
+
+ } else if (deref_size == (DATA_LLONG & 0x1f)) {
+
+ emit_load_assignment_rhs_expression_to_pair ("eax", "edx", lhs_deref_is_unsigned);
+ emit_pop_reg_now ("ecx");
+
+ emit_store_pair_to_deref_reg_now ("ecx", "eax", "edx");
+
+ } else {
+
+ emit_load_assignment_rhs_expression_to_reg (reg);
+ emit_pop_reg_now ("edx");
+
+ emit_store_reg_to_deref_reg_now ("edx", reg, deref_size);
+
+ }
+
+ free (lhs_name);
+ return;
+
+ }
+
+ lhs_sym = find_local_symbol (lhs_name);
+
+ if (lhs_sym) {
+
+ if (lhs_sym->is_static && lhs_sym->static_label) {
+ emit_load_global_to_reg (reg, lhs_sym->static_label, DATA_PTR);
+ } else {
+ emit_load_local_to_reg (reg, lhs_sym->offset, DATA_PTR);
+ }
+
+ if (lhs_has_postfix) {
+
+ emit_push_reg_now (reg);
+ emit_incdec_symbol_now (lhs_sym, lhs_name, lhs_postfix_op, lhs_line, lhs_start, lhs_caret);
+ emit_pop_reg_now (reg);
+
+ }
+
+ if (lhs_sym->pointer_depth > 1) {
+ deref_size = DATA_PTR;
+ } else if (lhs_sym->pointer_depth == 1) {
+ deref_size = lhs_sym->pointed_size;
+ }
+
+ emit_load_deref_reg_now (reg, deref_size);
+
+ if (lhs_sym->pointer_depth > 1) {
+ set_rhs_last_pointer_info (lhs_sym->pointer_depth - 1, lhs_sym->pointed_size);
+ } else {
+ clear_rhs_last_pointer_info ();
+ }
+
+ if (emit_handle_subscript_after_loaded_pointer_to_reg_now (reg, lhs_sym->pointer_depth > 0 ? lhs_sym->pointer_depth - 1 : 0, lhs_sym->pointed_size)) {
+
+ free (lhs_name);
+ return;
+
+ }
+
+ free (lhs_name);
+ return;
+
+ }
+
+ if (find_global_symbol (lhs_name) >= 0) {
+
+ emit_load_global_to_reg (reg, lhs_name, DATA_PTR);
+
+ if (lhs_has_postfix) {
+
+ emit_push_reg_now (reg);
+ emit_incdec_symbol_now (0, lhs_name, lhs_postfix_op, lhs_line, lhs_start, lhs_caret);
+ emit_pop_reg_now (reg);
+
+ }
+
+ if (get_global_symbol_pointer_depth (lhs_name) > 1) {
+ deref_size = DATA_PTR;
+ } else if (get_global_symbol_pointer_depth (lhs_name) == 1) {
+ deref_size = get_global_symbol_pointed_size (lhs_name);
+ }
+
+ emit_load_deref_reg_now (reg, deref_size);
+
+ if (get_global_symbol_pointer_depth (lhs_name) > 1) {
+ set_rhs_last_pointer_info (get_global_symbol_pointer_depth (lhs_name) - 1, get_global_symbol_pointed_size (lhs_name));
+ } else {
+ clear_rhs_last_pointer_info ();
+ }
+
+ if (emit_handle_subscript_after_loaded_pointer_to_reg_now (reg, get_global_symbol_pointer_depth (lhs_name) > 0 ? get_global_symbol_pointer_depth (lhs_name) - 1 : 0, get_global_symbol_pointed_size (lhs_name))) {
+
+ free (lhs_name);
+ return;
+
+ }
+
+ free (lhs_name);
+ return;
+
+ }
+
+ report_line_at (get_filename (), lhs_line, REPORT_ERROR, lhs_start, lhs_caret, "unknown symbol '%s'", lhs_name);
+ free (lhs_name);
+
+ return;
+
+ }
+
+ if (tok.kind == TOK_LPAREN) {
+
+ deref_had_parens = 1;
+ get_token ();
+
+ if (is_type_start (tok.kind)) {
+
+ if (parse_deref_cast_type_name (&deref_size)) {
+
+ int handled_va_arg = 0;
+
+ if (expression_text_has_pluseq_before_delim_now (tok.start)) {
+ handled_va_arg = emit_parse_va_arg_address_to_reg_now (reg, deref_size);
+ }
+
+ if (!handled_va_arg) {
+
+ /*
+ * This is the address operand of a casted dereference,
+ * e.g. *(size_t *)ptr. Do not parse a full binary
+ * expression here: in
+ *
+ * *(size_t *)ptr + sizeof(size_t)
+ *
+ * the + belongs to the outer expression after the
+ * dereference, not to the address being dereferenced.
+ */
+ emit_load_assignment_rhs_to_reg (reg);
+
+ }
+
+
+ /*
+ * parse_deref_cast_type_name() consumes the cast parentheses
+ * only. Do not consume a following ')' here: in expressions
+ * such as:
+ *
+ * __munmap(ptr, *(size_t *)ptr + sizeof(size_t))
+ *
+ * that ')' belongs to the surrounding call. Consuming it
+ * here makes the caller report a false "expected )".
+ */
+
+ if (tok.kind == TOK_ASSIGN) {
+
+ get_token ();
+
+ emit_push_reg_now (reg);
+ emit_load_assignment_rhs_expression_to_reg (reg);
+
+ emit_pop_reg_now ("edx");
+ emit_store_reg_to_deref_reg_now ("edx", reg, deref_size);
+
+ return;
+
+ }
+
+ goto dereference_loaded_address;
+
+ }
+
+ }
+
+ if (tok.kind == TOK_AMPER) {
+
+ char *name;
+
+ const char *name_start;
+ const char *name_caret;
+
+ unsigned long name_line;
+ struct local_symbol *dst;
+
+ int global_index;
+ int dst_size;
+
+ get_token ();
+
+ name_start = tok.start;
+ name_caret = tok.caret;
+ name_line = get_line_number ();
+
+ if (tok.kind == TOK_IDENT) {
+
+ name = xstrdup (tok.ident);
+
+ get_token ();
+ expect (TOK_RPAREN, ")");
+
+ if (is_assignment_operator (tok.kind)) {
+
+ get_token ();
+ emit_load_assignment_rhs_expression_to_reg (reg);
+
+ dst = find_local_symbol (name);
+ global_index = find_global_symbol (name);
+
+ if (dst) {
+
+ dst_size = dst->size;
+
+ if (dst->is_static && dst->static_label) {
+ emit_store_reg_to_global (dst->static_label, dst_size, reg);
+ } else {
+ emit_store_reg_to_local (dst->offset, dst_size, reg);
+ }
+
+ } else if (global_index >= 0) {
+
+ dst_size = get_global_symbol_size (name);
+ emit_store_reg_to_global (name, dst_size, reg);
+
+ } else {
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+ }
+
+ free (name);
+ return;
+
+ }
+
+ if (find_local_symbol (name) || find_global_symbol (name) >= 0) {
+
+ dst = find_local_symbol (name);
+
+ if (dst) {
+
+ if (dst->is_static && dst->static_label) {
+ emit_load_address_to_reg_now (reg, dst->static_label);
+ } else {
+ emit_load_local_address_to_reg_now (reg, dst->offset);
+ }
+
+ } else {
+ emit_load_address_to_reg_now (reg, name);
+ }
+
+ free (name);
+ goto dereference_loaded_address;
+
+ }
+
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+ free (name);
+
+ return;
+
+ }
+
+ }
+
+ }
+
+ if (deref_had_parens) {
+
+ emit_load_assignment_rhs_expression_to_reg (reg);
+
+ if (rhs_last_pointer_depth > 1) {
+ deref_size = DATA_PTR;
+ } else if (rhs_last_pointer_depth == 1) {
+ deref_size = rhs_last_pointed_size;
+ }
+
+ expect (TOK_RPAREN, ")");
+
+ /*
+ * A parenthesized dereferenced function pointer is still a function
+ * designator when it is followed by an argument list. For example:
+ *
+ * (*generate_func)(outfile, pos)
+ *
+ * The inner expression already loaded the function pointer value into
+ * reg. Do not dereference that value as data before the call; call
+ * through it directly.
+ */
+ if (tok.kind == TOK_LPAREN) {
+
+ emit_call_pointer_in_reg_now (reg, reg);
+
+ clear_rhs_last_pointer_info ();
+ return;
+
+ }
+
+ if (tok.kind == TOK_ASSIGN) {
+
+ emit_push_reg_now (reg);
+ get_token ();
+
+ emit_load_assignment_rhs_expression_to_reg (reg);
+ emit_pop_reg_now ("edx");
+ emit_store_reg_to_deref_reg_now ("edx", reg, deref_size);
+
+ return;
+
+ }
+
+ if (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) {
+
+ enum token_kind member_op = tok.kind;
+ enum token_kind postfix_op = TOK_EOF;
+
+ char *member;
+
+ const char *member_start;
+ const char *member_caret;
+
+ unsigned long member_line;
+
+ int offset = 0;
+ int member_size = DATA_PTR & 0x1f;
+ int member_elem_size = DATA_INT & 0x1f;
+ int member_pointer_depth = 0;
+
+ get_token ();
+
+ member_start = tok.start;
+ member_caret = tok.caret;
+ member_line = get_line_number ();
+
+ if (tok.kind != TOK_IDENT) {
+
+ report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "expected member name after %s", member_op == TOK_ARROW ? "->" : ".");
+ return;
+
+ }
+
+ member = xstrdup (tok.ident);
+ get_token ();
+
+ if (!find_member_info_ex (member, &offset, &member_size, &member_elem_size, &member_pointer_depth, 0, 0)) {
+
+ report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "unknown member '%s'", member);
+
+ free (member);
+ return;
+
+ }
+
+ free (member);
+
+ if (member_pointer_depth > 1) {
+ deref_size = DATA_PTR;
+ } else if (member_pointer_depth == 1 && member_elem_size > 0) {
+ deref_size = member_elem_size;
+ }
+
+ if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) {
+
+ postfix_op = tok.kind;
+ get_token ();
+
+ }
+
+ if (state->ofp) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ if (state->syntax & ASM_SYNTAX_NASM) {
+ fprintf (state->ofp, " mov edx, dword [%s + %d]\n", reg, offset);
+ } else {
+ fprintf (state->ofp, " mov edx, dword ptr [%s + %d]\n", reg, offset);
+ }
+
+ if (postfix_op == TOK_INCR || postfix_op == TOK_DECR) {
+
+ if (state->syntax & ASM_SYNTAX_NASM) {
+ fprintf (state->ofp, " %s dword [%s + %d]\n", postfix_op == TOK_INCR ? "inc" : "dec", reg, offset);
+ } else {
+ fprintf (state->ofp, " %s dword ptr [%s + %d]\n", postfix_op == TOK_INCR ? "inc" : "dec", reg, offset);
+ }
+
+ }
+
+ } else {
+
+ fprintf (state->ofp, " movl %d(%%%s), %%edx\n", offset, reg);
+
+ if (postfix_op == TOK_INCR || postfix_op == TOK_DECR) {
+ fprintf (state->ofp, " %sl %d(%%%s)\n", postfix_op == TOK_INCR ? "inc" : "dec", offset, reg);
+ }
+
+ }
+
+ }
+
+ if (tok.kind == TOK_ASSIGN) {
+
+ emit_push_reg_now ("edx");
+ get_token ();
+
+ emit_load_assignment_rhs_expression_to_reg (reg);
+ emit_pop_reg_now ("edx");
+ emit_store_reg_to_deref_reg_now ("edx", reg, deref_size);
+
+ return;
+
+ }
+
+ if (state->ofp && strcmp (reg, "edx") != 0) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " mov %s, edx\n", reg);
+ } else {
+ fprintf (state->ofp, " movl %%edx, %%%s\n", reg);
+ }
+
+ }
+
+ goto dereference_loaded_address;
+
+ }
+
+ } else {
+ emit_load_assignment_rhs_to_reg (reg);
+ }
+
+ dereference_loaded_address:
+
+ deref_pointer_depth = rhs_last_pointer_depth;
+ deref_pointed_size = rhs_last_pointed_size;
+
+ emit_load_deref_reg_now (reg, deref_size);
+
+ if (deref_pointer_depth > 1) {
+ set_rhs_last_pointer_info (deref_pointer_depth - 1, deref_pointed_size);
+ } else {
+ clear_rhs_last_pointer_info ();
+ }
+
+ if (tok.kind == TOK_LBRACK) {
+
+ int subscript_pointer_depth = deref_pointer_depth > 0 ? deref_pointer_depth - 1 : 0;
+ int subscript_elem_size = subscript_pointer_depth > 1 ? (DATA_PTR & 0x1f) : deref_pointed_size;
+
+ if (subscript_elem_size <= 0) {
+ subscript_elem_size = DATA_INT & 0x1f;
+ }
+
+ emit_parse_postfix_subscripts_to_reg_now (reg, subscript_elem_size, subscript_pointer_depth, deref_pointed_size);
+
+ if (is_assignment_operator (tok.kind)) {
+
+ enum token_kind assign_op = tok.kind;
+
+ get_token ();
+ emit_push_reg_now (reg);
+
+ if (assign_op == TOK_ASSIGN && subscript_elem_size > (DATA_LLONG & 0x1f) &&
+ tok.kind == TOK_IDENT && tok.ident && token_identifier_is_function_call_rhs_now ()) {
+
+ char *rhs_name = xstrdup (tok.ident);
+
+ const char *rhs_start = tok.start;
+ const char *rhs_caret = tok.caret;
+
+ unsigned long rhs_line = get_line_number ();
+
+ pending_struct_return_lhs = 0;
+ pending_struct_return_global_name = 0;
+ pending_struct_return_stack_address = 1;
+ pending_struct_return_stack_offset = 0;
+
+ get_token ();
+ emit_call_identifier_to_reg_now (rhs_name, reg, rhs_start, rhs_caret, rhs_line);
+
+ pending_struct_return_stack_address = 0;
+ pending_struct_return_stack_offset = 0;
+
+ emit_pop_reg_now ("edx");
+ free (rhs_name);
+
+ set_rhs_last_pointer_info (0, 0);
+ return;
+
+ } else if (assign_op == TOK_ASSIGN) {
+
+ if (subscript_elem_size > (DATA_LLONG & 0x1f)) {
+
+ pending_struct_return_lhs = 0;
+ pending_struct_return_global_name = 0;
+ pending_struct_return_stack_address = 1;
+ pending_struct_return_stack_offset = 0;
+
+ emit_load_assignment_rhs_expression_to_reg (reg);
+
+ pending_struct_return_stack_address = 0;
+ pending_struct_return_stack_offset = 0;
+
+ emit_pop_reg_now ("edx");
+ set_rhs_last_pointer_info (0, 0);
+ return;
+
+ }
+
+ emit_load_assignment_rhs_expression_to_reg (reg);
+
+ } else {
+
+ emit_load_deref_reg_now (reg, subscript_elem_size);
+ emit_push_reg_now (reg);
+
+ emit_load_assignment_rhs_expression_to_reg ("edx");
+ emit_pop_reg_now (reg);
+
+ emit_assignment_binary_op (assign_op, 0);
+
+ }
+
+ emit_pop_reg_now ("edx");
+
+ emit_store_reg_to_deref_reg_now ("edx", reg, subscript_elem_size);
+ clear_rhs_last_pointer_info ();
+
+ return;
+
+ }
+
+ emit_load_deref_reg_now (reg, subscript_elem_size);
+
+ if (subscript_pointer_depth > 1) {
+ set_rhs_last_pointer_info (subscript_pointer_depth - 1, deref_pointed_size);
+ } else {
+ clear_rhs_last_pointer_info ();
+ }
+
+ }
+
+ return;
+
+ } else if (tok.kind == TOK_TILDE) {
+
+ get_token ();
+ emit_load_assignment_rhs_to_reg (reg);
+
+ if (state->ofp) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " not %s\n", reg);
+ } else {
+ fprintf (state->ofp, " notl %%%s\n", reg);
+ }
+
+ }
+
+ return;
+
+ } else if (tok.kind == TOK_XMARK) {
+
+ get_token ();
+ emit_load_assignment_rhs_to_reg (reg);
+
+ if (state->ofp) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ fprintf (state->ofp, " test %s, %s\n", reg, reg);
+
+ if (strcmp (reg, "eax") != 0) {
+
+ fprintf (state->ofp, " setz al\n");
+ fprintf (state->ofp, " movzx eax, al\n");
+ fprintf (state->ofp, " mov %s, eax\n", reg);
+
+ } else {
+
+ fprintf (state->ofp, " setz al\n");
+ fprintf (state->ofp, " movzx eax, al\n");
+
+ }
+
+ } else {
+
+ fprintf (state->ofp, " testl %%%s, %%%s\n", reg, reg);
+ fprintf (state->ofp, " setz %%al\n");
+ fprintf (state->ofp, " movzbl %%al, %%eax\n");
+
+ if (strcmp (reg, "eax") != 0) {
+ fprintf (state->ofp, " movl %%eax, %%%s\n", reg);
+ }
+
+ }
+
+ }
+
+ return;
+
+ } else if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) {
+
+ if (emit_load_prefix_incdec_member_to_reg_now (reg)) {
+ return;
+ }
+
+ if (emit_load_prefix_incdec_to_reg_now (reg)) {
+ return;
+ }
+
+ }
+
+ if (tok.kind == TOK_PLUS || tok.kind == TOK_MINUS) {
+
+ int negate = 0;
+
+ while (tok.kind == TOK_PLUS || tok.kind == TOK_MINUS) {
+
+ if (tok.kind == TOK_MINUS) {
+ negate = !negate;
+ }
+
+ get_token ();
+
+ }
+
+ emit_load_assignment_rhs_to_reg (reg);
+
+ if (negate && state->ofp) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " neg %s\n", reg);
+ } else {
+ fprintf (state->ofp, " negl %%%s\n", reg);
+ }
+
+ }
+
+ return;
+
+ }
+
+ if (token_is_sizeof_keyword ()) {
+
+ int64_s v = sizeof_from_current_token ();
+
+ if (state->ofp) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " mov %s, %lu\n", reg, v.low & U32_MASK);
+ } else {
+ fprintf (state->ofp, " movl $%lu, %%%s\n", v.low & U32_MASK, reg);
+ }
+
+ }
+
+ return;
+
+ }
+
+ if (is_string_token ()) {
+
+ char *label = emit_string_literal_global ();
+
+ switch_section (SECTION_TEXT);
+ emit_load_address_to_reg_now (reg, label);
+
+ free (label);
+
+ /*
+ * A string literal is a primary expression and may still have
+ * postfix operators applied to it. In particular, macro-expanded
+ * string constants are commonly subscripted in calls, e.g.
+ *
+ * tebc (LINKAGE_EDITOR_PROGRAM_NAME[i])
+ *
+ * where LINKAGE_EDITOR_PROGRAM_NAME expands to a string literal.
+ * The old path returned immediately after loading the literal
+ * address, leaving the '[' token for the caller and causing a false
+ * "expected )". Treat the literal as a char array here and consume
+ * any following subscripts.
+ */
+ if (tok.kind == TOK_LBRACK) {
+
+ emit_parse_postfix_subscript_scaled_address_to_reg_now (reg, DATA_CHAR & 0x1f);
+ emit_load_deref_reg_now (reg, DATA_CHAR & 0x1f);
+
+ set_rhs_last_pointer_info (0, 0);
+
+ } else {
+ set_rhs_last_pointer_info (1, DATA_CHAR & 0x1f);
+ }
+
+ return;
+
+ }
+
+ switch (tok.kind) {
+
+ case TOK_CCHAR: case TOK_CINT: case TOK_CUINT: case TOK_CULONG:
+ case TOK_CLONG: case TOK_CLLONG: case TOK_CULLONG: case TOK_LCHAR:
+ {
+
+ int64_s v;
+
+ int trailing_op = 0;
+ size_t len = tok.ident ? strlen (tok.ident) : 0;
+
+ v.high = tok.val.i.high;
+ v.low = tok.val.i.low;
+
+ if (len > 1 && tok.ident[len - 1] == '+') {
+ trailing_op = TOK_PLUS;
+ } else if (len > 1 && tok.ident[len - 1] == '-') {
+ trailing_op = TOK_MINUS;
+ }
+
+ if (trailing_op) {
+
+ free (tok.ident);
+
+ tok.ident = xstrdup (trailing_op == TOK_PLUS ? "+" : "-");
+ tok.kind = (enum token_kind) trailing_op;
+
+ if (tok.caret) {
+ tok.caret--;
+ }
+
+ } else {
+ get_token ();
+ }
+
+ emit_load_const32_to_reg_now (reg, v);
+ return;
+
+ }
+
+ case TOK_CFLOAT: case TOK_CDOUBLE: case TOK_CLDOUBLE:
+ {
+
+ int trailing_op = 0;
+ size_t len = tok.ident ? strlen (tok.ident) : 0;
+ int64_s v;
+
+ v.high = 0;
+ v.low = tok.val.ld != 0.0L ? 1 : 0;
+
+ if (len > 1 && tok.ident[len - 1] == '+') {
+ trailing_op = TOK_PLUS;
+ } else if (len > 1 && tok.ident[len - 1] == '-') {
+ trailing_op = TOK_MINUS;
+ }
+
+ if (trailing_op) {
+
+ free (tok.ident);
+
+ tok.ident = xstrdup (trailing_op == TOK_PLUS ? "+" : "-");
+ tok.kind = (enum token_kind) trailing_op;
+
+ if (tok.caret) {
+ tok.caret--;
+ }
+
+ } else {
+ get_token ();
+ }
+
+ emit_load_const32_to_reg_now (reg, v);
+ return;
+
+ }
+
+ default:
+
+ break;
+
+ }
+
+ if (tok.kind == TOK_IDENT) {
+
+ enum token_kind postfix_op = TOK_EOF;
+ char *name = xstrdup (tok.ident);
+
+ const char *name_start = tok.start, *name_caret = tok.caret;
+ unsigned long name_line = get_line_number ();
+
+ struct local_symbol *src;
+ int postfix_incdec = 0;
+
+ get_token ();
+
+ {
+
+ int64_s enum_value;
+
+ if (!find_local_symbol (name) && find_global_symbol (name) < 0 && resolve_enum_constant (name, &enum_value)) {
+
+ emit_load_const32_to_reg_now (reg, enum_value);
+
+ set_rhs_last_pointer_info (0, DATA_INT & 0x1f);
+ free (name);
+
+ return;
+
+ }
+
+ }
+
+ if (tok.kind == TOK_LBRACK) {
+
+ src = find_local_symbol (name);
+
+ if (src) {
+
+ int elem_size;
+
+ {
+
+ elem_size = src->is_array ? (src->pointer_depth ? DATA_PTR : (src->array_element_size > 0 ? src->array_element_size : src->pointed_size)) :
+ (src->pointer_depth > 1 ? DATA_PTR : src->pointed_size);
+
+ if (src->is_array) {
+
+ if (src->is_static && src->static_label) {
+ emit_load_symbol_address_to_reg_now (reg, src->static_label, 0, 0);
+ } else {
+ emit_load_symbol_address_to_reg_now (reg, 0, src->offset, 1);
+ }
+
+ } else if (src->is_static && src->static_label) {
+ emit_load_global_to_reg (reg, src->static_label, DATA_PTR);
+ } else {
+ emit_load_local_to_reg (reg, src->offset, DATA_PTR);
+ }
+
+ emit_parse_postfix_subscripts_to_reg_now (reg, elem_size, src->pointer_depth, src->pointed_size);
+ postfix_copy_lvalue_size = index_step_size (elem_size);
+
+ }
+
+ emit_apply_postfix_member_access_to_reg_now (reg);
+
+ if (!postfix_member_seen && emit_store_assignment_to_aggregate_address_now (reg, elem_size, name_start, name_caret, name_line)) {
+
+ free (name);
+ return;
+
+ }
+
+ if (postfix_member_seen) {
+ set_rhs_last_pointer_info (postfix_member_pointer_depth, postfix_member_pointed_size);
+ } else {
+ set_rhs_last_pointer_info (src->pointer_depth > 0 ? src->pointer_depth - 1 : 0, src->pointed_size);
+ }
+
+ free (name);
+ return;
+
+ }
+
+ if (find_global_symbol (name) >= 0) {
+
+ int elem_size;
+
+ {
+
+ elem_size = get_global_symbol_array (name) ?
+ (get_global_symbol_pointer_depth (name) ? DATA_PTR : get_global_symbol_pointed_size (name)) :
+ (get_global_symbol_pointer_depth (name) > 1 ? DATA_PTR : get_global_symbol_pointed_size (name));
+
+ if (get_global_symbol_array (name)) {
+ emit_load_symbol_address_to_reg_now (reg, name, 0, 0);
+ } else {
+ emit_load_global_to_reg (reg, name, DATA_PTR);
+ }
+
+ emit_parse_postfix_subscripts_to_reg_now (reg, elem_size, get_global_symbol_pointer_depth (name), get_global_symbol_pointed_size (name));
+ postfix_copy_lvalue_size = index_step_size (elem_size);
+
+ }
+
+ emit_apply_postfix_member_access_to_reg_now (reg);
+
+ if (!postfix_member_seen && emit_store_assignment_to_aggregate_address_now (reg, elem_size, name_start, name_caret, name_line)) {
+
+ free (name);
+ return;
+
+ }
+
+ free (name);
+
+ return;
+
+ }
+
+ }
+
+ if (tok.kind == TOK_LPAREN) {
+
+ if (!find_local_symbol (name)) {
+ ensure_global_function_symbol (name, name_start, name_caret, name_line);
+ }
+
+ if (get_global_symbol_kind (name) == GLOBAL_SYMBOL_FUNCTION && get_global_symbol_returns_void (name)) {
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "void function '%s' used as a value", name);
+ }
+
+ emit_call_identifier_to_reg_now (name, reg, name_start, name_caret, name_line);
+
+ if (tok.kind == TOK_LBRACK) {
+
+ int subscript_pointer_depth = get_global_symbol_pointer_depth (name);
+ int subscript_elem_size = get_global_symbol_pointed_size (name);
+
+ if (subscript_elem_size <= 0) {
+ subscript_elem_size = DATA_INT & 0x1f;
+ }
+
+ emit_parse_postfix_subscript_scaled_address_to_reg_now (reg, subscript_elem_size);
+
+ if (is_assignment_operator (tok.kind)) {
+
+ enum token_kind assign_op = tok.kind;
+
+ get_token ();
+ emit_push_reg_now (reg);
+
+ if (assign_op == TOK_ASSIGN) {
+
+ if (subscript_elem_size > (DATA_LLONG & 0x1f)) {
+
+ pending_struct_return_lhs = 0;
+ pending_struct_return_global_name = 0;
+ pending_struct_return_stack_address = 1;
+ pending_struct_return_stack_offset = 0;
+
+ emit_load_assignment_rhs_expression_to_reg (reg);
+
+ pending_struct_return_stack_address = 0;
+ pending_struct_return_stack_offset = 0;
+
+ emit_pop_reg_now ("edx");
+ postfix_member_seen = 0;
+
+ return;
+
+ }
+
+ emit_load_assignment_rhs_expression_to_reg (reg);
+
+ } else {
+
+ emit_load_deref_reg_now (reg, subscript_elem_size);
+ emit_push_reg_now (reg);
+
+ emit_load_assignment_rhs_expression_to_reg ("edx");
+ emit_pop_reg_now (reg);
+
+ emit_assignment_binary_op (assign_op, 0);
+
+ }
+
+ emit_pop_reg_now ("edx");
+ emit_store_member_to_addr_reg_now ("edx", 0, reg, subscript_elem_size);
+ set_rhs_last_pointer_info (0, 0);
+
+ free (name);
+ return;
+
+ }
+
+ emit_load_deref_reg_now (reg, subscript_elem_size);
+
+ if (subscript_pointer_depth > 0) {
+ subscript_pointer_depth--;
+ }
+
+ if (subscript_pointer_depth > 0) {
+ set_rhs_last_pointer_info (subscript_pointer_depth, subscript_elem_size);
+ } else {
+ set_rhs_last_pointer_info (0, 0);
+ }
+
+ }
+
+ emit_apply_postfix_member_access_to_reg_now (reg);
+
+ if (postfix_member_seen && tok.kind == TOK_LPAREN) {
+
+ emit_call_pointer_in_reg_now (reg, reg);
+
+ set_rhs_last_pointer_info (0, 0);
+ free (name);
+
+ return;
+
+ }
+
+ if (postfix_member_seen) {
+ set_rhs_last_pointer_info (postfix_member_pointer_depth, postfix_member_pointed_size);
+ }
+
+ free (name);
+ return;
+
+ }
+
+ if (is_assignment_operator (tok.kind)) {
+
+ enum token_kind assign_op = tok.kind;
+ struct local_symbol *dst;
+ int global_index;
+ int dst_size;
+ int dst_is_floating;
+
+ get_token ();
+
+ dst = find_local_symbol (name);
+ global_index = find_global_symbol (name);
+
+ if (!dst && global_index < 0) {
+
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+
+ if (state->ofp) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " xor %s, %s\n", reg, reg);
+ } else {
+ fprintf (state->ofp, " xorl %%%s, %%%s\n", reg, reg);
+ }
+
+ }
+
+ free (name);
+ return;
+
+ }
+
+ dst_size = dst ? dst->size : get_global_symbol_size (name);
+ dst_is_floating = dst ? dst->is_floating : get_global_symbol_floating (name);
+
+ if (dst_is_floating) {
+
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "floating assignment expression not implemented");
+ skip_balanced_until (TOK_RPAREN, TOK_SEMI, TOK_EOF);
+
+ free (name);
+ return;
+
+ }
+
+ if (dst_size == (DATA_LLONG & 0x1f)) {
+
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "64-bit assignment expression not implemented");
+ skip_balanced_until (TOK_RPAREN, TOK_SEMI, TOK_EOF);
+
+ free (name);
+ return;
+
+ }
+
+ if (assign_op == TOK_ASSIGN) {
+ emit_load_assignment_rhs_expression_to_reg (reg);
+ } else {
+
+ if (dst) {
+
+ if (dst->is_static && dst->static_label) {
+ emit_load_global_to_reg (reg, dst->static_label, dst->size);
+ } else {
+ emit_load_local_to_reg (reg, dst->offset, dst->size);
+ }
+
+ } else {
+ emit_load_global_to_reg (reg, name, dst_size);
+ }
+
+ emit_load_assignment_rhs_expression_to_reg ("edx");
+
+ if (strcmp (reg, "eax") != 0 && state->ofp) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " mov eax, %s\n", reg);
+ } else {
+ fprintf (state->ofp, " movl %%%s, %%eax\n", reg);
+ }
+
+ }
+
+ emit_assignment_binary_op (assign_op, dst ? dst->is_unsigned : get_global_symbol_unsigned (name));
+
+ if (strcmp (reg, "eax") != 0 && state->ofp) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " mov %s, eax\n", reg);
+ } else {
+ fprintf (state->ofp, " movl %%eax, %%%s\n", reg);
+ }
+
+ }
+
+ }
+
+ if (dst) {
+
+ if (dst->is_static && dst->static_label) {
+ emit_store_reg_to_global (dst->static_label, dst->size, reg);
+ } else {
+ emit_store_reg_to_local (dst->offset, dst->size, reg);
+ }
+
+ if (dst->is_array || dst->pointer_depth > 0) {
+ set_rhs_last_pointer_info (dst->is_array ? 1 : dst->pointer_depth, dst->is_array ? local_array_pointer_step_size (dst) : dst->pointed_size);
+ } else {
+ clear_rhs_last_pointer_info ();
+ }
+
+ } else {
+
+ emit_store_reg_to_global (name, dst_size, reg);
+
+ if (get_global_symbol_array (name) || get_global_symbol_pointer_depth (name) > 0) {
+ set_rhs_last_pointer_info (get_global_symbol_array (name) ? 1 : get_global_symbol_pointer_depth (name), get_global_symbol_array (name) ? global_array_pointer_step_size (name) : get_global_symbol_pointed_size (name));
+ } else {
+ clear_rhs_last_pointer_info ();
+ }
+
+ }
+
+ free (name);
+ return;
+
+ }
+
+ if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) {
+
+ postfix_incdec = 1;
+ postfix_op = tok.kind;
+
+ get_token ();
+
+ }
+
+ src = find_local_symbol (name);
+
+ if (postfix_incdec && tok.kind == TOK_LBRACK) {
+
+ int pointer_depth = 0;
+ int pointed_size = 0;
+ int elem_size = DATA_INT & 0x1f;
+ int is_unsigned = 1;
+ int known = 0;
+
+ if (src) {
+
+ known = 1;
+
+ pointer_depth = src->pointer_depth;
+ pointed_size = src->pointed_size;
+
+ is_unsigned = src->is_unsigned;
+
+ if (src->is_static && src->static_label) {
+ emit_load_global_to_reg (reg, src->static_label, DATA_PTR);
+ } else {
+ emit_load_local_to_reg (reg, src->offset, DATA_PTR);
+ }
+
+ } else if (find_global_symbol (name) >= 0) {
+
+ known = 1;
+
+ pointer_depth = get_global_symbol_pointer_depth (name);
+ pointed_size = get_global_symbol_pointed_size (name);
+
+ is_unsigned = get_global_symbol_unsigned (name);
+ emit_load_global_to_reg (reg, name, DATA_PTR);
+
+ }
+
+ if (!known) {
+
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+
+ free (name);
+ return;
+
+ }
+
+ if (pointer_depth <= 0) {
+
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "subscripted value is not a pointer");
+
+ free (name);
+ return;
+
+ }
+
+ if (pointer_depth > 1) {
+ elem_size = DATA_PTR & 0x1f;
+ } else if (pointed_size > 0) {
+ elem_size = pointed_size & 0x1f;
+ }
+
+ emit_push_reg_now (reg);
+
+ emit_incdec_symbol_now (src, name, postfix_op, name_line, name_start, name_caret);
+ emit_pop_reg_now (reg);
+
+ emit_parse_postfix_subscript_scaled_address_to_reg_now (reg, elem_size);
+
+ if (is_assignment_operator (tok.kind)) {
+
+ enum token_kind assign_op = tok.kind;
+ get_token ();
+
+ emit_push_reg_now (reg);
+
+ if (assign_op == TOK_ASSIGN) {
+ emit_load_assignment_rhs_expression_to_reg (reg);
+ } else {
+
+ emit_load_deref_reg_now (reg, elem_size);
+ emit_push_reg_now (reg);
+
+ emit_load_assignment_rhs_expression_to_reg ("edx");
+ emit_pop_reg_now (reg);
+
+ emit_assignment_binary_op (assign_op, is_unsigned);
+
+ }
+
+ emit_pop_reg_now ("edx");
+ emit_store_reg_to_deref_reg_now ("edx", reg, elem_size);
+
+ clear_rhs_last_pointer_info ();
+ free (name);
+
+ return;
+
+ }
+
+ emit_load_deref_reg_now (reg, elem_size);
+
+ if (pointer_depth > 1) {
+ set_rhs_last_pointer_info (pointer_depth - 1, pointed_size);
+ } else {
+ clear_rhs_last_pointer_info ();
+ }
+
+ free (name);
+ return;
+
+ }
+
+ if (src) {
+
+ if (src->is_array) {
+
+ if (src->is_static && src->static_label) {
+ emit_load_address_to_reg_now (reg, src->static_label);
+ } else {
+ emit_load_local_address_to_reg_now (reg, src->offset);
+ }
+
+ } else if (tok.kind == TOK_DOT) {
+
+ if (src->is_static && src->static_label) {
+ emit_load_address_to_reg_now (reg, src->static_label);
+ } else {
+ emit_load_local_address_to_reg_now (reg, src->offset);
+ }
+
+ } else if (src->is_static && src->static_label) {
+ emit_load_global_to_reg (reg, src->static_label, src->size);
+ } else {
+ emit_load_local_to_reg (reg, src->offset, src->size);
+ }
+
+ if (src->pointer_depth > 0) {
+
+ /*
+ * Keep the pointed-to aggregate information even when the
+ * arrow is not immediately visible. Macro expansions commonly
+ * parenthesize pointer operands, e.g. ((unknown)->type).
+ * The inner expression sees ')' after 'unknown', and the
+ * outer parenthesized path sees the later '->'. If we clear
+ * the tag here, that later member lookup falls back to an
+ * unrelated member named 'type' and emits offset 0 instead of
+ * the struct cpp_unknown offset.
+ */
+ postfix_copy_lvalue_size = src->pointed_size;
+ postfix_copy_lvalue_tag_name = src->pointed_tag_name;
+
+ } else {
+
+ postfix_copy_lvalue_size = src->size;
+ postfix_copy_lvalue_tag_name = src->tag_name;
+
+ }
+
+ emit_apply_postfix_member_access_to_reg_now (reg);
+
+ if (postfix_member_seen && tok.kind == TOK_LPAREN) {
+
+ emit_call_pointer_in_reg_now (reg, reg);
+
+ set_rhs_last_pointer_info (0, 0);
+ free (name);
+
+ return;
+
+ }
+
+ if (emit_store_assignment_to_postfix_member_now (reg)) {
+
+ set_rhs_last_pointer_info (postfix_member_pointer_depth, postfix_member_pointed_size);
+
+ free (name);
+ return;
+
+ }
+
+ if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) {
+
+ postfix_incdec = 1;
+ postfix_op = tok.kind;
+
+ get_token ();
+
+ }
+
+ if (postfix_incdec) {
+
+ if (postfix_member_seen) {
+ emit_apply_postfix_member_incdec_now (reg, postfix_op);
+ } else {
+
+ emit_push_reg_now (reg);
+ emit_incdec_symbol_now (src, name, postfix_op, name_line, name_start, name_caret);
+ emit_pop_reg_now (reg);
+
+ }
+
+ }
+
+ if (postfix_member_seen) {
+ set_rhs_last_pointer_info (postfix_member_pointer_depth, postfix_member_pointed_size);
+ } else {
+ set_rhs_last_pointer_info (src->is_array ? 1 : src->pointer_depth, src->is_array ? local_array_pointer_step_size (src) : src->pointed_size);
+ }
+
+ free (name);
+ return;
+
+ }
+
+ if (find_global_symbol (name) >= 0) {
+
+ if (get_global_symbol_kind (name) == GLOBAL_SYMBOL_FUNCTION ||
+ get_global_symbol_array (name) ||
+ (!get_global_symbol_pointer_depth (name) &&
+ get_global_symbol_size (name) > (DATA_PTR & 0x1f)) ||
+ tok.kind == TOK_DOT) {
+ emit_load_address_to_reg_now (reg, name);
+ } else {
+ emit_load_global_to_reg (reg, name, get_global_symbol_size (name));
+ }
+
+ if (tok.kind == TOK_ARROW && get_global_symbol_pointer_depth (name) > 0) {
+
+ postfix_copy_lvalue_size = get_global_symbol_pointed_size (name);
+ postfix_copy_lvalue_tag_name = get_global_symbol_tag_name (name);
+
+ } else {
+
+ postfix_copy_lvalue_size = get_global_symbol_size (name);
+ postfix_copy_lvalue_tag_name = 0;
+
+ }
+
+ emit_apply_postfix_member_access_to_reg_now (reg);
+
+ if (postfix_member_seen && tok.kind == TOK_LPAREN) {
+
+ emit_call_pointer_in_reg_now (reg, reg);
+
+ set_rhs_last_pointer_info (0, 0);
+ free (name);
+
+ return;
+
+ }
+
+ if (emit_store_assignment_to_postfix_member_now (reg)) {
+
+ set_rhs_last_pointer_info (postfix_member_pointer_depth, postfix_member_pointed_size);
+
+ free (name);
+ return;
+
+ }
+
+ if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) {
+
+ postfix_incdec = 1;
+ postfix_op = tok.kind;
+
+ get_token ();
+
+ }
+
+ if (postfix_incdec) {
+
+ if (postfix_member_seen) {
+ emit_apply_postfix_member_incdec_now (reg, postfix_op);
+ } else {
+
+ emit_push_reg_now (reg);
+ emit_incdec_symbol_now (0, name, postfix_op, name_line, name_start, name_caret);
+ emit_pop_reg_now (reg);
+
+ }
+
+ }
+
+ if (postfix_member_seen) {
+ set_rhs_last_pointer_info (postfix_member_pointer_depth, postfix_member_pointed_size);
+ } else {
+ set_rhs_last_pointer_info (get_global_symbol_array (name) ? 1 : get_global_symbol_pointer_depth (name), get_global_symbol_array (name) ? global_array_pointer_step_size (name) : get_global_symbol_pointed_size (name));
+ }
+
+ free (name);
+ return;
+
+ }
+
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+ free (name);
+
+ if (state->ofp) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " xor %s, %s\n", reg, reg);
+ } else {
+ fprintf (state->ofp, " xorl %%%s, %%%s\n", reg, reg);
+ }
+
+ }
+
+ return;
+
+ }
+
+ if (recover_unknown_rhs_identifier ()) {
+
+ if (state->ofp) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " xor %s, %s\n", reg, reg);
+ } else {
+ fprintf (state->ofp, " xorl %%%s, %%%s\n", reg, reg);
+ }
+
+ }
+
+ return;
+
+ }
+
+ {
+
+ int64_s v = const64_from_current_operand ();
+
+ if (state->ofp) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " mov %s, %lu\n", reg, v.low & U32_MASK);
+ } else {
+ fprintf (state->ofp, " movl $%lu, %%%s\n", v.low & U32_MASK, reg);
+ }
+
+ }
+
+ }
+
+}
+
+static int local_array_pointer_step_size (const struct local_symbol *sym) {
+
+ if (!sym || !sym->is_array) {
+ return 0;
+ }
+
+ if (sym->array_element_size > 0) {
+ return index_step_size (sym->array_element_size);
+ }
+
+ if (sym->pointed_size > 0 && sym->pointed_size < sym->size) {
+ return index_step_size (sym->pointed_size);
+ }
+
+ return index_step_size (sym->size);
+
+}
+
+static int global_array_pointer_step_size (const char *name) {
+
+ long count;
+ int pointed_size;
+
+ if (!name || !get_global_symbol_array (name)) {
+ return 0;
+ }
+
+ if (get_global_symbol_array_element_size (name) > 0) {
+ return get_global_symbol_array_element_size (name);
+ }
+
+ count = get_global_symbol_array_count (name);
+
+ if (count > 0) {
+ return (int) (get_global_symbol_size (name) / count);
+ }
+
+ pointed_size = get_global_symbol_pointed_size (name);
+
+ if (pointed_size > 0 && pointed_size < get_global_symbol_size (name)) {
+ return index_step_size (pointed_size);
+ }
+
+ return index_step_size (get_global_symbol_size (name));
+
+}
+
+static void emit_scale_reg_by_const_now (const char *reg, int scale) {
+
+ if (!state->ofp || scale <= 1) {
+ return;
+ }
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " imul %s, %d\n", reg, scale);
+ } else {
+ fprintf (state->ofp, " imull $%d, %%%s, %%%s\n", scale, reg, reg);
+ }
+
+}
+
+static void emit_divide_eax_by_const_now (int divisor) {
+
+ if (!state->ofp || divisor <= 1) {
+ return;
+ }
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ fprintf (state->ofp, " mov ecx, %d\n", divisor);
+ fprintf (state->ofp, " cdq\n");
+ fprintf (state->ofp, " idiv ecx\n");
+
+ } else {
+
+ fprintf (state->ofp, " movl $%d, %%ecx\n", divisor);
+ fprintf (state->ofp, " cdq\n");
+ fprintf (state->ofp, " idivl %%ecx\n");
+
+ }
+
+}
+
+static void emit_assignment_binary_op (enum token_kind op, int is_unsigned) {
+
+ if (!state->ofp) {
+ return;
+ }
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ switch (op) {
+
+ case TOK_PLUS: case TOK_PLUSEQ:
+
+ fprintf (state->ofp, " add eax, edx\n");
+ break;
+
+ case TOK_MINUS: case TOK_MINUSEQ:
+
+ fprintf (state->ofp, " sub eax, edx\n");
+ break;
+
+ case TOK_STAR: case TOK_STAREQ:
+
+ fprintf (state->ofp, " imul eax, edx\n");
+ break;
+
+ case TOK_BSLASH: case TOK_SLASHEQ:
+
+ fprintf (state->ofp, " mov ecx, edx\n");
+
+ if (is_unsigned) {
+
+ fprintf (state->ofp, " xor edx, edx\n");
+ fprintf (state->ofp, " div ecx\n");
+
+ } else {
+
+ fprintf (state->ofp, " cdq\n");
+ fprintf (state->ofp, " idiv ecx\n");
+
+ }
+
+ break;
+
+ case TOK_MOD:
+
+ fprintf (state->ofp, " mov ecx, edx\n");
+
+ if (is_unsigned) {
+
+ fprintf (state->ofp, " xor edx, edx\n");
+ fprintf (state->ofp, " div ecx\n");
+
+ } else {
+
+ fprintf (state->ofp, " cdq\n");
+ fprintf (state->ofp, " idiv ecx\n");
+
+ }
+
+ fprintf (state->ofp, " mov eax, edx\n");
+ break;
+
+ case TOK_MODEQ:
+
+ fprintf (state->ofp, " mov ecx, edx\n");
+
+ if (is_unsigned) {
+
+ fprintf (state->ofp, " xor edx, edx\n");
+ fprintf (state->ofp, " div ecx\n");
+
+ } else {
+
+ fprintf (state->ofp, " cdq\n");
+ fprintf (state->ofp, " idiv ecx\n");
+
+ }
+
+ fprintf (state->ofp, " mov eax, edx\n");
+ break;
+
+ case TOK_AMPER: case TOK_ANDEQ:
+
+ fprintf (state->ofp, " and eax, edx\n");
+ break;
+
+ case TOK_PIPE: case TOK_OREQ:
+
+ fprintf (state->ofp, " or eax, edx\n");
+ break;
+
+ case TOK_CARET: case TOK_XOREQ:
+
+ fprintf (state->ofp, " xor eax, edx\n");
+ break;
+
+ case TOK_LSH: case TOK_LSHEQ:
+
+ fprintf (state->ofp, " mov ecx, edx\n");
+ fprintf (state->ofp, " shl eax, cl\n");
+
+ break;
+
+ case TOK_RSH: case TOK_RSHEQ:
+
+ fprintf (state->ofp, " mov ecx, edx\n");
+ fprintf (state->ofp, is_unsigned ? " shr eax, cl\n" : " sar eax, cl\n");
+
+ break;
+
+ default:
+
+ break;
+
+ }
+
+ } else {
+
+ switch (op) {
+
+ case TOK_PLUS: case TOK_PLUSEQ:
+
+ fprintf (state->ofp, " addl %%edx, %%eax\n");
+ break;
+
+ case TOK_MINUS: case TOK_MINUSEQ:
+
+ fprintf (state->ofp, " subl %%edx, %%eax\n");
+ break;
+
+ case TOK_STAR: case TOK_STAREQ:
+
+ fprintf (state->ofp, " imull %%edx, %%eax\n");
+ break;
+
+ case TOK_BSLASH: case TOK_SLASHEQ:
+
+ fprintf (state->ofp, " movl %%edx, %%ecx\n");
+
+ if (is_unsigned) {
+
+ fprintf (state->ofp, " xorl %%edx, %%edx\n");
+ fprintf (state->ofp, " divl %%ecx\n");
+
+ } else {
+
+ fprintf (state->ofp, " cdq\n");
+ fprintf (state->ofp, " idivl %%ecx\n");
+
+ }
+
+ break;
+
+ case TOK_MOD:
+
+ fprintf (state->ofp, " movl %%edx, %%ecx\n");
+
+ if (is_unsigned) {
+
+ fprintf (state->ofp, " xorl %%edx, %%edx\n");
+ fprintf (state->ofp, " divl %%ecx\n");
+
+ } else {
+
+ fprintf (state->ofp, " cdq\n");
+ fprintf (state->ofp, " idivl %%ecx\n");
+
+ }
+
+ fprintf (state->ofp, " movl %%edx, %%eax\n");
+ break;
+
+ case TOK_MODEQ:
+
+ fprintf (state->ofp, " movl %%edx, %%ecx\n");
+
+ if (is_unsigned) {
+
+ fprintf (state->ofp, " xorl %%edx, %%edx\n");
+ fprintf (state->ofp, " divl %%ecx\n");
+
+ } else {
+
+ fprintf (state->ofp, " cdq\n");
+ fprintf (state->ofp, " idivl %%ecx\n");
+
+ }
+
+ fprintf (state->ofp, " movl %%edx, %%eax\n");
+ break;
+
+ case TOK_AMPER: case TOK_ANDEQ:
+
+ fprintf (state->ofp, " andl %%edx, %%eax\n");
+ break;
+
+ case TOK_PIPE: case TOK_OREQ:
+
+ fprintf (state->ofp, " orl %%edx, %%eax\n");
+ break;
+
+ case TOK_CARET: case TOK_XOREQ:
+
+ fprintf (state->ofp, " xorl %%edx, %%eax\n");
+ break;
+
+ case TOK_LSH: case TOK_LSHEQ:
+
+ fprintf (state->ofp, " movl %%edx, %%ecx\n");
+ fprintf (state->ofp, " sall %%cl, %%eax\n");
+
+ break;
+
+ case TOK_RSH: case TOK_RSHEQ:
+
+ fprintf (state->ofp, " movl %%edx, %%ecx\n");
+ fprintf (state->ofp, is_unsigned ? " shrl %%cl, %%eax\n" : " sarl %%cl, %%eax\n");
+
+ break;
+
+ default:
+
+ break;
+
+ }
+
+ }
+
+}
+
+static int token_is_floating_constant_now (void) {
+ return tok.kind == TOK_CFLOAT || tok.kind == TOK_CDOUBLE || tok.kind == TOK_CLDOUBLE;
+}
+
+static int64_s floating_constant_to_bits_now (int size) {
+
+ int64_s r;
+
+ unsigned long bits32;
+ unsigned char bytes[8];
+
+ int i;
+
+ r.low = 0;
+ r.high = 0;
+
+ if (size == (DATA_FLOAT & 0x1f)) {
+
+ float f;
+
+ if (tok.kind == TOK_CFLOAT) {
+ f = tok.val.f;
+ } else if (tok.kind == TOK_CLDOUBLE) {
+ f = (float) tok.val.ld;
+ } else {
+ f = (float) tok.val.d;
+ }
+
+ memcpy (&bits32, &f, sizeof (f));
+
+ r.low = bits32;
+ get_token ();
+
+ return r;
+
+ }
+
+ {
+
+ double d;
+
+ if (tok.kind == TOK_CFLOAT) {
+ d = (double) tok.val.f;
+ } else if (tok.kind == TOK_CLDOUBLE) {
+ d = (double) tok.val.ld;
+ } else {
+ d = tok.val.d;
+ }
+
+ memset (bytes, 0, sizeof (bytes));
+ memcpy (bytes, &d, sizeof (d));
+
+ for (i = 0; i < 4; i++) {
+ r.low |= ((unsigned long) bytes[i]) << (i * 8);
+ }
+
+ for (i = 0; i < 4; i++) {
+ r.high |= ((unsigned long) bytes[i + 4]) << (i * 8);
+ }
+
+ get_token ();
+ return r;
+
+ }
+
+}
+
+static long double floating_constant_to_ld_now (void) {
+
+ long double v;
+
+ if (tok.kind == TOK_CFLOAT) {
+ v = (long double) tok.val.f;
+ } else if (tok.kind == TOK_CLDOUBLE) {
+ v = tok.val.ld;
+ } else {
+ v = (long double) tok.val.d;
+ }
+
+ get_token ();
+ return v;
+
+}
+
+static long double parse_floating_const_expr_value_now (void);
+static long double parse_floating_const_term_now (void);
+static long double parse_floating_const_primary_now (void);
+
+static long double parse_floating_const_primary_now (void) {
+
+ long double v;
+ int64_s iv;
+
+ if (_accept (TOK_LPAREN)) {
+ v = parse_floating_const_expr_value_now ();
+ expect (TOK_RPAREN, ")");
+ return v;
+ }
+
+ if (_accept (TOK_PLUS)) {
+ return parse_floating_const_primary_now ();
+ }
+
+ if (_accept (TOK_MINUS)) {
+ return -parse_floating_const_primary_now ();
+ }
+
+ if (token_is_floating_constant_now ()) {
+ return floating_constant_to_ld_now ();
+ }
+
+ iv = const64_from_current_operand ();
+ v = (long double) iv.low;
+
+ if (iv.high != 0) {
+ v += ((long double) iv.high) * 4294967296.0L;
+ }
+
+ return v;
+
+}
+
+static long double parse_floating_const_term_now (void) {
+
+ long double v;
+ enum token_kind op;
+ long double rhs;
+
+ v = parse_floating_const_primary_now ();
+
+ while (tok.kind == TOK_STAR || tok.kind == TOK_BSLASH) {
+
+ op = tok.kind;
+ get_token ();
+ rhs = parse_floating_const_primary_now ();
+
+ if (op == TOK_STAR) {
+ v *= rhs;
+ } else {
+ v /= rhs;
+ }
+
+ }
+
+ return v;
+
+}
+
+static long double parse_floating_const_expr_value_now (void) {
+
+ long double v;
+ enum token_kind op;
+ long double rhs;
+
+ v = parse_floating_const_term_now ();
+
+ while (tok.kind == TOK_PLUS || tok.kind == TOK_MINUS) {
+
+ op = tok.kind;
+ get_token ();
+ rhs = parse_floating_const_term_now ();
+
+ if (op == TOK_PLUS) {
+ v += rhs;
+ } else {
+ v -= rhs;
+ }
+
+ }
+
+ return v;
+
+}
+
+static int64_s parse_floating_const_expr_bits_now (int size) {
+
+ long double acc;
+ int64_s r;
+
+ acc = parse_floating_const_expr_value_now ();
+ r.low = 0;
+ r.high = 0;
+
+ if (size == (DATA_FLOAT & 0x1f)) {
+
+ float f;
+ unsigned long bits32;
+
+ f = (float) acc;
+ bits32 = 0;
+ memcpy (&bits32, &f, sizeof (f));
+ r.low = bits32;
+ return r;
+
+ }
+
+ {
+
+ double d;
+ unsigned char bytes[8];
+ int i;
+
+ d = (double) acc;
+ memset (bytes, 0, sizeof (bytes));
+ memcpy (bytes, &d, sizeof (d));
+
+ for (i = 0; i < 4; i++) {
+ r.low |= ((unsigned long) bytes[i]) << (i * 8);
+ }
+
+ for (i = 0; i < 4; i++) {
+ r.high |= ((unsigned long) bytes[i + 4]) << (i * 8);
+ }
+
+ return r;
+
+ }
+
+}
+
+static void emit_load_floating_const_bits_now (int size, int64_s v) {
+
+ int lab;
+
+ if (!state->ofp) {
+ return;
+ }
+
+ lab = anon_label++;
+
+ if (state->syntax & ASM_SYNTAX_MASM) {
+
+ fprintf (state->ofp, ".data\n");
+ fprintf (state->ofp, "LC%d_flt:\n", lab);
+ fprintf (state->ofp, " dd %lu\n", v.low & U32_MASK);
+
+ if (size == (DATA_DOUBLE & 0x1f)) {
+ fprintf (state->ofp, " dd %lu\n", v.high & U32_MASK);
+ }
+
+ fprintf (state->ofp, ".code\n");
+ fprintf (state->ofp, " fld %s ptr LC%d_flt\n", size == (DATA_DOUBLE & 0x1f) ? "qword" : "dword", lab);
+
+ } else {
+
+ if (state->syntax & ASM_SYNTAX_NASM) {
+
+ fprintf (state->ofp, "section .data\n");
+ fprintf (state->ofp, "LC%d_flt:\n", lab);
+ fprintf (state->ofp, " dd %lu\n", v.low & U32_MASK);
+
+ if (size == (DATA_DOUBLE & 0x1f)) {
+ fprintf (state->ofp, " dd %lu\n", v.high & U32_MASK);
+ }
+
+ fprintf (state->ofp, "section .text\n");
+ fprintf (state->ofp, " fld %s [LC%d_flt]\n", size == (DATA_DOUBLE & 0x1f) ? "qword" : "dword", lab);
+
+ } else {
+
+ fprintf (state->ofp, ".data\n");
+ fprintf (state->ofp, ".LC%d_flt:\n", lab);
+ fprintf (state->ofp, " .long %lu\n", v.low & U32_MASK);
+
+ if (size == (DATA_DOUBLE & 0x1f)) {
+ fprintf (state->ofp, " .long %lu\n", v.high & U32_MASK);
+ }
+
+ fprintf (state->ofp, ".text\n");
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " fld %s ptr .LC%d_flt\n", size == (DATA_DOUBLE & 0x1f) ? "qword" : "dword", lab);
+ } else {
+ fprintf (state->ofp, " fld%s .LC%d_flt\n", size == (DATA_DOUBLE & 0x1f) ? "l" : "s", lab);
+ }
+
+ }
+
+ }
+
+}
+
+static void emit_load_floating_symbol_now (struct local_symbol *sym, const char *name, int size) {
+
+ char memref[64];
+ const char *label;
+ const char *asm_name;
+
+ if (!state->ofp) {
+ return;
+ }
+
+ asm_name = asm_global_symbol_name (name);
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ if (sym) {
+
+ if (sym->is_static && sym->static_label) {
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " fld %s [%s]\n" : " fld %s ptr %s\n"), size == (DATA_DOUBLE & 0x1f) ? "qword" : "dword", sym->static_label);
+ } else {
+
+ format_intel_ebp_offset (memref, sizeof (memref), sym->offset);
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " fld %s %s\n" : " fld %s ptr %s\n"), size == (DATA_DOUBLE & 0x1f) ? "qword" : "dword", memref);
+
+ }
+
+ } else {
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " fld %s [%s]\n" : " fld %s ptr %s\n"), size == (DATA_DOUBLE & 0x1f) ? "qword" : "dword", asm_name);
+ }
+
+ } else {
+
+ if (sym) {
+
+ label = (sym->is_static && sym->static_label) ? sym->static_label : 0;
+
+ if (label) {
+ fprintf (state->ofp, " fld%s %s\n", size == (DATA_DOUBLE & 0x1f) ? "l" : "s", label);
+ } else {
+ fprintf (state->ofp, " fld%s %ld(%%ebp)\n", size == (DATA_DOUBLE & 0x1f) ? "l" : "s", sym->offset);
+ }
+
+ } else {
+ fprintf (state->ofp, " fld%s %s\n", size == (DATA_DOUBLE & 0x1f) ? "l" : "s", asm_name);
+ }
+
+ }
+
+}
+
+static void emit_load_floating_member_symbol_now (struct local_symbol *sym, const char *name, int offset, int size) {
+
+ char memref[64];
+ char labelref[256];
+
+ const char *label;
+ const char *asm_name;
+ const char *opsize;
+
+ if (!state->ofp) {
+ return;
+ }
+
+ opsize = size == (DATA_FLOAT & 0x1f) ? "dword" : "qword";
+ asm_name = asm_global_symbol_name (name);
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ if (sym) {
+
+ if (sym->is_static && sym->static_label) {
+
+ if (offset) {
+
+ sprintf (labelref, "%s + %d", sym->static_label, offset);
+ label = labelref;
+
+ } else {
+ label = sym->static_label;
+ }
+
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " fld %s %s\n" : " fld %s ptr %s\n"), opsize, label);
+
+ } else {
+
+ format_intel_ebp_offset (memref, sizeof (memref), sym->offset + offset);
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " fld %s %s\n" : " fld %s ptr %s\n"), opsize, memref);
+
+ }
+
+ } else {
+
+ if (offset) {
+
+ sprintf (labelref, "%s + %d", asm_name, offset);
+ label = labelref;
+
+ } else {
+ label = asm_name;
+ }
+
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " fld %s %s\n" : " fld %s ptr %s\n"), opsize, label);
+
+ }
+
+ } else {
+
+ if (sym) {
+
+ label = (sym->is_static && sym->static_label) ? sym->static_label : 0;
+
+ if (label) {
+
+ if (offset) {
+
+ sprintf (labelref, "%s+%d", label, offset);
+ label = labelref;
+
+ }
+
+ fprintf (state->ofp, " fld%s %s\n", size == (DATA_DOUBLE & 0x1f) ? "l" : "s", label);
+
+ } else {
+ fprintf (state->ofp, " fld%s %ld(%%ebp)\n", size == (DATA_DOUBLE & 0x1f) ? "l" : "s", sym->offset + offset);
+ }
+
+ } else {
+
+ if (offset) {
+
+ sprintf (labelref, "%s+%d", asm_name, offset);
+ asm_name = labelref;
+
+ }
+
+ fprintf (state->ofp, " fld%s %s\n", size == (DATA_DOUBLE & 0x1f) ? "l" : "s", asm_name);
+
+ }
+
+ }
+
+}
+
+static void emit_duplicate_floating_stack_top_now (void) {
+
+ if (!state->ofp) {
+ return;
+ }
+
+ if (state->syntax & ASM_SYNTAX_NASM) {
+ fprintf (state->ofp, " fld st0\n");
+ } else if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " fld st(0)\n");
+ } else {
+ fprintf (state->ofp, " fld %%st(0)\n");
+ }
+
+}
+
+static void emit_store_floating_symbol_now (struct local_symbol *sym, const char *name, int size) {
+
+ char memref[64];
+ const char *label;
+ const char *asm_name;
+
+ if (!state->ofp) {
+ return;
+ }
+
+ asm_name = asm_global_symbol_name (name);
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ if (sym) {
+
+ if (sym->is_static && sym->static_label) {
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " fstp %s %s\n" : " fstp %s ptr %s\n"), size == (DATA_DOUBLE & 0x1f) ? "qword" : "dword", sym->static_label);
+ } else {
+
+ format_intel_ebp_offset (memref, sizeof (memref), sym->offset);
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " fstp %s %s\n" : " fstp %s ptr %s\n"), size == (DATA_DOUBLE & 0x1f) ? "qword" : "dword", memref);
+
+ }
+
+ } else {
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " fstp %s %s\n" : " fstp %s ptr %s\n"), size == (DATA_DOUBLE & 0x1f) ? "qword" : "dword", asm_name);
+ }
+
+ } else {
+
+ if (sym) {
+
+ label = (sym->is_static && sym->static_label) ? sym->static_label : 0;
+
+ if (label) {
+ fprintf (state->ofp, " fstp%s %s\n", size == (DATA_DOUBLE & 0x1f) ? "l" : "s", label);
+ } else {
+ fprintf (state->ofp, " fstp%s %ld(%%ebp)\n", size == (DATA_DOUBLE & 0x1f) ? "l" : "s", sym->offset);
+ }
+
+ } else {
+ fprintf (state->ofp, " fstp%s %s\n", size == (DATA_DOUBLE & 0x1f) ? "l" : "s", asm_name);
+ }
+
+ }
+
+}
+
+static void emit_load_any_symbol_as_floating_now (struct local_symbol *src, const char *name, int size, int is_floating) {
+
+ if (is_floating) {
+
+ emit_load_floating_symbol_now (src, name, size);
+ return;
+
+ }
+
+ if (src) {
+
+ if (src->is_static && src->static_label) {
+ emit_load_global_to_reg ("eax", src->static_label, src->size);
+ } else {
+ emit_load_local_to_reg ("eax", src->offset, src->size);
+ }
+
+ } else {
+ emit_load_global_to_reg ("eax", name, size);
+ }
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ fprintf (state->ofp, " sub esp, 4\n");
+
+ if (state->syntax & ASM_SYNTAX_NASM) {
+
+ fprintf (state->ofp, " mov dword [esp], eax\n");
+ fprintf (state->ofp, " fild dword [esp]\n");
+
+ } else {
+
+ fprintf (state->ofp, " mov dword ptr [esp], eax\n");
+ fprintf (state->ofp, " fild dword ptr [esp]\n");
+
+ }
+
+ fprintf (state->ofp, " add esp, 4\n");
+
+ } else {
+
+ fprintf (state->ofp, " subl $4, %%esp\n");
+ fprintf (state->ofp, " movl %%eax, (%%esp)\n");
+ fprintf (state->ofp, " fildl (%%esp)\n");
+ fprintf (state->ofp, " addl $4, %%esp\n");
+
+ }
+
+}
+
+static int emit_load_floating_prefix_incdec_now (void) {
+
+ enum token_kind op;
+ char *name;
+
+ const char *name_start, *name_caret;
+ unsigned long name_line;
+
+ struct local_symbol *sym;
+ int size;
+ int is_floating;
+
+ if (!parse_incdec_identifier_now (&op, &name, &name_start, &name_caret, &name_line)) {
+ return 0;
+ }
+
+ if (!name) {
+ return 1;
+ }
+
+ sym = find_local_symbol (name);
+ emit_incdec_symbol_now (sym, name, op, name_line, name_start, name_caret);
+
+ if (sym) {
+
+ size = sym->size;
+ is_floating = sym->is_floating;
+
+ emit_load_any_symbol_as_floating_now (sym, name, size, is_floating);
+
+ } else if (find_global_symbol (name) >= 0) {
+
+ size = get_global_symbol_size (name);
+ is_floating = get_global_symbol_floating (name);
+
+ emit_load_any_symbol_as_floating_now (0, name, size, is_floating);
+
+ }
+
+ free (name);
+ return 1;
+
+}
+
+static void emit_load_floating_rhs_expression_now (int result_size);
+static void emit_floating_binary_now (enum token_kind k);
+
+static int floating_assignment_operator_supported_now (enum token_kind op);
+static int is_value_compare_operator (enum token_kind k);
+
+static void emit_statement_label (int label);
+static void emit_statement_label_raw (int label);
+static void emit_statement_jump (int label);
+
+static int floating_rhs_result_in_eax_bool = 0;
+
+static void emit_eax_bool_to_floating_stack_now (void) {
+
+ if (!state->ofp) {
+ return;
+ }
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ fprintf (state->ofp, " sub esp, 4\n");
+
+ if (state->syntax & ASM_SYNTAX_NASM) {
+
+ fprintf (state->ofp, " mov dword [esp], eax\n");
+ fprintf (state->ofp, " fild dword [esp]\n");
+
+ } else {
+
+ fprintf (state->ofp, " mov dword ptr [esp], eax\n");
+ fprintf (state->ofp, " fild dword ptr [esp]\n");
+
+ }
+
+ fprintf (state->ofp, " add esp, 4\n");
+
+ } else {
+
+ fprintf (state->ofp, " subl $4, %%esp\n");
+ fprintf (state->ofp, " movl %%eax, (%%esp)\n");
+ fprintf (state->ofp, " fildl (%%esp)\n");
+ fprintf (state->ofp, " addl $4, %%esp\n");
+
+ }
+
+ floating_rhs_result_in_eax_bool = 0;
+
+}
+
+static const char *floating_compare_true_setcc_now (enum token_kind op) {
+
+ switch (op) {
+
+ case TOK_LESS:
+
+ return "setb";
+
+ case TOK_LTEQ:
+
+ return "setbe";
+
+ case TOK_GREATER:
+
+ return "seta";
+
+ case TOK_GTEQ:
+
+ return "setae";
+
+ case TOK_EQEQ:
+
+ return "sete";
+
+ case TOK_NOTEQ:
+
+ return "setne";
+
+ default:
+
+ return "setz";
+
+ }
+
+}
+
+static void emit_floating_compare_to_eax_now (enum token_kind op) {
+
+ const char *setcc;
+
+ if (!state->ofp) {
+ floating_rhs_result_in_eax_bool = 1;
+ return;
+ }
+
+ setcc = floating_compare_true_setcc_now (op);
+
+ if (state->syntax & ASM_SYNTAX_NASM) {
+
+ fprintf (state->ofp, " fxch st1\n");
+ fprintf (state->ofp, " fcompp\n");
+ fprintf (state->ofp, " fnstsw ax\n");
+ fprintf (state->ofp, " sahf\n");
+ fprintf (state->ofp, " %s al\n", setcc);
+ fprintf (state->ofp, " movzx eax, al\n");
+
+ } else if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ fprintf (state->ofp, " fxch st(1)\n");
+ fprintf (state->ofp, " fcompp\n");
+ fprintf (state->ofp, " fnstsw ax\n");
+ fprintf (state->ofp, " sahf\n");
+ fprintf (state->ofp, " %s al\n", setcc);
+ fprintf (state->ofp, " movzx eax, al\n");
+
+ } else {
+
+ fprintf (state->ofp, " fxch %%st(1)\n");
+ fprintf (state->ofp, " fcompp\n");
+ fprintf (state->ofp, " fnstsw %%ax\n");
+ fprintf (state->ofp, " sahf\n");
+ fprintf (state->ofp, " %s %%al\n", setcc);
+ fprintf (state->ofp, " movzbl %%al, %%eax\n");
+
+ }
+
+ floating_rhs_result_in_eax_bool = 1;
+
+}
+
+static void emit_load_floating_rhs_operand_now (int result_size) {
+
+ if (tok.kind == TOK_PLUS || tok.kind == TOK_MINUS) {
+
+ enum token_kind unary_op = tok.kind;
+ get_token ();
+
+ emit_load_floating_rhs_operand_now (result_size);
+
+ if (unary_op == TOK_MINUS) {
+ fprintf (state->ofp, " fchs\n");
+ }
+
+ return;
+
+ }
+
+ if (tok.kind == TOK_STAR) {
+
+ int deref_size = result_size;
+ get_token ();
+
+ if (tok.kind == TOK_LPAREN) {
+
+ get_token ();
+
+ if (is_type_start (tok.kind) && parse_deref_cast_type_name (&deref_size)) {
+
+ if (!expression_text_has_pluseq_before_delim_now (tok.start) ||
+ !emit_parse_va_arg_address_to_reg_now ("eax", deref_size)) {
+ emit_load_assignment_rhs_to_reg ("eax");
+ }
+
+ emit_load_floating_deref_reg_now ("eax", deref_size);
+ return;
+
+ }
+
+ emit_load_assignment_rhs_expression_to_reg ("eax");
+ expect (TOK_RPAREN, ")");
+
+ emit_load_floating_deref_reg_now ("eax", deref_size);
+ return;
+
+ }
+
+ emit_load_assignment_rhs_to_reg ("eax");
+ emit_load_floating_deref_reg_now ("eax", deref_size);
+
+ return;
+
+ }
+
+ if (_accept (TOK_LPAREN)) {
+
+ if (token_starts_type_name ()) {
+
+ int cast_size = 0;
+ int cast_is_unsigned = 0;
+ int cast_is_pointer = 0;
+
+ if (parse_cast_type_name (&cast_size, &cast_is_unsigned, &cast_is_pointer)) {
+
+ emit_load_floating_rhs_operand_now (result_size);
+ return;
+
+ }
+
+ }
+
+ emit_load_floating_rhs_expression_now (result_size);
+ expect (TOK_RPAREN, ")");
+
+ if (floating_rhs_result_in_eax_bool && tok.kind != TOK_QMARK) {
+ emit_eax_bool_to_floating_stack_now ();
+ }
+
+ return;
+
+ }
+
+ if (emit_load_floating_prefix_incdec_now ()) {
+ return;
+ }
+
+ if (token_is_sizeof_keyword ()) {
+
+ int64_s v = sizeof_from_current_token ();
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ fprintf (state->ofp, " sub esp, 4\n");
+
+ if (state->syntax & ASM_SYNTAX_NASM) {
+
+ fprintf (state->ofp, " mov dword [esp], %lu\n", v.low & U32_MASK);
+ fprintf (state->ofp, " fild dword [esp]\n");
+
+ } else {
+
+ fprintf (state->ofp, " mov dword ptr [esp], %lu\n", v.low & U32_MASK);
+ fprintf (state->ofp, " fild dword ptr [esp]\n");
+
+ }
+
+ fprintf (state->ofp, " add esp, 4\n");
+
+ } else {
+
+ fprintf (state->ofp, " subl $4, %%esp\n");
+ fprintf (state->ofp, " movl $%lu, (%%esp)\n", v.low & U32_MASK);
+ fprintf (state->ofp, " fildl (%%esp)\n");
+ fprintf (state->ofp, " addl $4, %%esp\n");
+
+ }
+
+ return;
+
+ }
+
+ if (tok.kind == TOK_IDENT) {
+
+ char *name = xstrdup (tok.ident);
+
+ const char *name_start = tok.start, *name_caret = tok.caret;
+ unsigned long name_line = get_line_number ();
+
+ struct local_symbol *src = find_local_symbol (name);
+ get_token ();
+
+ if (is_assignment_operator (tok.kind)) {
+
+ enum token_kind assign_op = tok.kind;
+
+ int dst_size = 0;
+ int dst_is_floating = 0;
+ int have_dst = 0;
+
+ if (src) {
+
+ dst_size = src->size;
+
+ dst_is_floating = src->is_floating;
+ have_dst = 1;
+
+ } else if (find_global_symbol (name) >= 0) {
+
+ dst_size = get_global_symbol_size (name);
+
+ dst_is_floating = get_global_symbol_floating (name);
+ have_dst = 1;
+
+ }
+
+ if (have_dst && dst_is_floating && floating_assignment_operator_supported_now (assign_op)) {
+
+ get_token ();
+
+ if (assign_op == TOK_ASSIGN) {
+ emit_load_floating_rhs_expression_now (dst_size);
+ } else {
+
+ emit_load_floating_symbol_now (src, name, dst_size);
+ emit_load_floating_rhs_expression_now (dst_size);
+
+ emit_floating_binary_now (assign_op);
+
+ }
+
+ emit_duplicate_floating_stack_top_now ();
+ emit_store_floating_symbol_now (src, name, dst_size);
+
+ free (name);
+ return;
+
+ }
+
+ }
+
+ if (tok.kind == TOK_LPAREN) {
+
+ if (!find_local_symbol (name)) {
+ ensure_global_function_symbol (name, name_start, name_caret, name_line);
+ }
+
+ if (get_global_symbol_kind (name) == GLOBAL_SYMBOL_FUNCTION && get_global_symbol_returns_void (name)) {
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "void function '%s' used as a value", name);
+ }
+
+ emit_call_identifier_to_reg_now (name, "eax", name_start, name_caret, name_line);
+
+ if (!get_global_symbol_floating (name)) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ fprintf (state->ofp, " sub esp, 4\n");
+
+ if (state->syntax & ASM_SYNTAX_NASM) {
+
+ fprintf (state->ofp, " mov dword [esp], eax\n");
+ fprintf (state->ofp, " fild dword [esp]\n");
+
+ } else {
+
+ fprintf (state->ofp, " mov dword ptr [esp], eax\n");
+ fprintf (state->ofp, " fild dword ptr [esp]\n");
+
+ }
+
+ fprintf (state->ofp, " add esp, 4\n");
+
+ } else {
+
+ fprintf (state->ofp, " subl $4, %%esp\n");
+ fprintf (state->ofp, " movl %%eax, (%%esp)\n");
+ fprintf (state->ofp, " fildl (%%esp)\n");
+ fprintf (state->ofp, " addl $4, %%esp\n");
+
+ }
+
+ }
+
+ free (name);
+ return;
+
+ }
+
+ if (tok.kind == TOK_ARROW && (src || find_global_symbol (name) >= 0)) {
+
+ char *member;
+
+ const char *member_start;
+ const char *member_caret;
+
+ unsigned long member_line;
+
+ int member_offset = 0;
+ int member_size = result_size;
+ int member_elem_size = result_size;
+ int member_pointer_depth = 0;
+ int member_is_array = 0;
+ int member_is_floating = 0;
+ int base_size;
+
+ const char *base_tag_name;
+
+ if (src) {
+
+ base_size = src->pointed_size;
+ base_tag_name = src->pointed_tag_name;
+
+ } else {
+
+ base_size = get_global_symbol_pointed_size (name);
+ base_tag_name = get_global_symbol_tag_name (name);
+
+ }
+
+ get_token ();
+
+ member_start = tok.start;
+ member_caret = tok.caret;
+ member_line = get_line_number ();
+
+ if (tok.kind != TOK_IDENT) {
+
+ report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "expected member name after ->");
+
+ free (name);
+ return;
+
+ }
+
+ member = xstrdup (tok.ident);
+ get_token ();
+
+ if (!find_member_info_ex_bounded (member, base_size, base_tag_name, &member_offset, &member_size, &member_elem_size, &member_pointer_depth, &member_is_array, &member_is_floating)) {
+
+ report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "unknown member '%s'", member);
+
+ free (member);
+ free (name);
+
+ return;
+
+ }
+
+ free (member);
+
+ if (src) {
+
+ if (src->is_static && src->static_label) {
+ emit_load_global_to_reg ("eax", src->static_label, DATA_PTR & 0x1f);
+ } else {
+ emit_load_local_to_reg ("eax", src->offset, DATA_PTR & 0x1f);
+ }
+
+ } else {
+ emit_load_global_to_reg ("eax", name, DATA_PTR & 0x1f);
+ }
+
+ if (member_is_floating) {
+ emit_load_floating_member_from_addr_reg_now ("eax", member_offset, member_size);
+ } else {
+
+ emit_load_member_from_addr_reg_now ("eax", "eax", member_offset, member_size);
+ emit_eax_bool_to_floating_stack_now ();
+
+ }
+
+ free (name);
+ return;
+
+ }
+
+ if (tok.kind == TOK_DOT && (src || find_global_symbol (name) >= 0)) {
+
+ char *member;
+
+ const char *member_start;
+ const char *member_caret;
+
+ unsigned long member_line;
+
+ int member_offset = 0;
+ int member_size = result_size;
+
+ get_token ();
+
+ member_start = tok.start;
+ member_caret = tok.caret;
+ member_line = get_line_number ();
+
+ if (tok.kind != TOK_IDENT) {
+
+ report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "expected member name after .");
+
+ free (name);
+ return;
+
+ }
+
+ member = xstrdup (tok.ident);
+ get_token ();
+
+ if (!find_member_info (member, &member_offset, &member_size)) {
+
+ report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "unknown member '%s'", member);
+
+ free (member);
+ free (name);
+
+ return;
+
+ }
+
+ free (member);
+
+ if (member_size == (DATA_FLOAT & 0x1f) || member_size == (DATA_DOUBLE & 0x1f)) {
+ emit_load_floating_member_symbol_now (src, name, member_offset, member_size);
+ } else {
+
+ if (src) {
+
+ if (src->is_static && src->static_label) {
+ emit_load_global_to_reg ("eax", src->static_label, src->size);
+ } else {
+ emit_load_local_to_reg ("eax", src->offset, src->size);
+ }
+
+ } else {
+ emit_load_global_to_reg ("eax", name, get_global_symbol_size (name));
+ }
+
+ emit_apply_postfix_member_access_to_reg_now ("eax");
+ emit_eax_bool_to_floating_stack_now ();
+
+ }
+
+ free (name);
+ return;
+
+ }
+
+ if (src) {
+
+ if (src->is_floating) {
+ emit_load_floating_symbol_now (src, name, src->size);
+ } else {
+
+ if (src->is_static && src->static_label) {
+ emit_load_global_to_reg ("eax", src->static_label, src->size);
+ } else {
+ emit_load_local_to_reg ("eax", src->offset, src->size);
+ }
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ fprintf (state->ofp, " sub esp, 4\n");
+
+ if (state->syntax & ASM_SYNTAX_NASM) {
+
+ fprintf (state->ofp, " mov dword [esp], eax\n");
+ fprintf (state->ofp, " fild dword [esp]\n");
+
+ } else {
+
+ fprintf (state->ofp, " mov dword ptr [esp], eax\n");
+ fprintf (state->ofp, " fild dword ptr [esp]\n");
+
+ }
+
+ fprintf (state->ofp, " add esp, 4\n");
+
+ } else {
+
+ fprintf (state->ofp, " subl $4, %%esp\n");
+ fprintf (state->ofp, " movl %%eax, (%%esp)\n");
+ fprintf (state->ofp, " fildl (%%esp)\n");
+ fprintf (state->ofp, " addl $4, %%esp\n");
+
+ }
+
+ }
+
+ if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) {
+
+ enum token_kind postfix_op = tok.kind;
+ get_token ();
+
+ emit_incdec_symbol_now (src, name, postfix_op, name_line, name_start, name_caret);
+
+ }
+
+ free (name);
+ return;
+
+ }
+
+ if (find_global_symbol (name) >= 0) {
+
+ if (get_global_symbol_floating (name)) {
+ emit_load_floating_symbol_now (0, name, get_global_symbol_size (name));
+ } else {
+
+ emit_load_global_to_reg ("eax", name, get_global_symbol_size (name));
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ fprintf (state->ofp, " sub esp, 4\n");
+
+ if (state->syntax & ASM_SYNTAX_NASM) {
+
+ fprintf (state->ofp, " mov dword [esp], eax\n");
+ fprintf (state->ofp, " fild dword [esp]\n");
+
+ } else {
+
+ fprintf (state->ofp, " mov dword ptr [esp], eax\n");
+ fprintf (state->ofp, " fild dword ptr [esp]\n");
+
+ }
+
+ fprintf (state->ofp, " add esp, 4\n");
+
+ } else {
+
+ fprintf (state->ofp, " subl $4, %%esp\n");
+ fprintf (state->ofp, " movl %%eax, (%%esp)\n");
+ fprintf (state->ofp, " fildl (%%esp)\n");
+ fprintf (state->ofp, " addl $4, %%esp\n");
+
+ }
+
+ }
+
+ if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) {
+
+ enum token_kind postfix_op = tok.kind;
+ get_token ();
+
+ emit_incdec_symbol_now (0, name, postfix_op, name_line, name_start, name_caret);
+
+ }
+
+ free (name);
+ return;
+
+ }
+
+ free (name);
+
+ }
+
+ if (token_is_floating_constant_now ()) {
+
+ emit_load_floating_const_bits_now (result_size, floating_constant_to_bits_now (result_size));
+ return;
+
+ }
+
+ if (recover_unknown_rhs_identifier ()) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " fldz\n");
+ } else {
+ fprintf (state->ofp, " fldz\n");
+ }
+ return;
+
+ }
+
+ {
+
+ int64_s v = const64_from_current_operand ();
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ fprintf (state->ofp, " sub esp, 4\n");
+
+ if (state->syntax & ASM_SYNTAX_NASM) {
+
+ fprintf (state->ofp, " mov dword [esp], %lu\n", v.low & U32_MASK);
+ fprintf (state->ofp, " fild dword [esp]\n");
+
+ } else {
+
+ fprintf (state->ofp, " mov dword ptr [esp], %lu\n", v.low & U32_MASK);
+ fprintf (state->ofp, " fild dword ptr [esp]\n");
+
+ }
+
+ fprintf (state->ofp, " add esp, 4\n");
+
+ } else {
+
+ fprintf (state->ofp, " subl $4, %%esp\n");
+ fprintf (state->ofp, " movl $%lu, (%%esp)\n", v.low & U32_MASK);
+ fprintf (state->ofp, " fildl (%%esp)\n");
+ fprintf (state->ofp, " addl $4, %%esp\n");
+
+ }
+
+ }
+
+}
+
+static int token_is_floating_binary_now (enum token_kind k) {
+ return k == TOK_PLUS || k == TOK_MINUS || k == TOK_STAR || k == TOK_BSLASH;
+}
+
+static void emit_floating_binary_now (enum token_kind k) {
+
+ const char *st1;
+ const char *st0;
+
+ if (!state->ofp) {
+ return;
+ }
+
+ if (state->syntax & ASM_SYNTAX_NASM) {
+
+ st1 = "st1";
+ st0 = "st0";
+
+ } else if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ st1 = "st(1)";
+ st0 = "st(0)";
+
+ } else {
+
+ st1 = "%st(1)";
+ st0 = "%st";
+
+ }
+
+ switch (k) {
+
+ case TOK_PLUS: case TOK_PLUSEQ:
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " faddp %s, %s\n", st1, st0);
+ } else {
+ fprintf (state->ofp, " faddp %s, %s\n", st0, st1);
+ }
+ break;
+
+ case TOK_MINUS: case TOK_MINUSEQ:
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " fsubp %s, %s\n", st1, st0);
+ } else {
+ fprintf (state->ofp, " fsubp %s, %s\n", st0, st1);
+ }
+ break;
+
+ case TOK_STAR: case TOK_STAREQ:
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " fmulp %s, %s\n", st1, st0);
+ } else {
+ fprintf (state->ofp, " fmulp %s, %s\n", st0, st1);
+ }
+ break;
+
+ case TOK_BSLASH: case TOK_SLASHEQ:
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " fdivp %s, %s\n", st1, st0);
+ } else {
+ fprintf (state->ofp, " fdivp %s, %s\n", st0, st1);
+ }
+ break;
+
+ default:
+
+ break;
+
+ }
+
+}
+
+static void emit_scale_reg_for_pointer_compound_assignment_now (const char *reg, struct local_symbol *lhs, const char *name, enum token_kind op) {
+
+ int pointer_depth;
+ int pointed_size;
+ int elem_size;
+
+ if (op != TOK_PLUSEQ && op != TOK_MINUSEQ) {
+ return;
+ }
+
+ pointer_depth = lhs ? lhs->pointer_depth : get_global_symbol_pointer_depth (name);
+ pointed_size = lhs ? lhs->pointed_size : get_global_symbol_pointed_size (name);
+
+ if (pointer_depth <= 0) {
+ return;
+ }
+
+ elem_size = pointer_depth > 1 ? DATA_PTR : pointed_size;
+
+ if (elem_size > 1) {
+ emit_scale_reg_by_const_now (reg, elem_size);
+ }
+
+}
+
+static int floating_assignment_operator_supported_now (enum token_kind op) {
+ return op == TOK_ASSIGN || op == TOK_PLUSEQ || op == TOK_MINUSEQ || op == TOK_STAREQ || op == TOK_SLASHEQ;
+}
+
+static void emit_load_floating_rhs_expression_now (int result_size) {
+
+ enum token_kind op;
+
+ int false_label;
+ int end_label;
+
+ floating_rhs_result_in_eax_bool = 0;
+ emit_load_floating_rhs_operand_now (result_size);
+
+ while (token_is_floating_binary_now (tok.kind)) {
+
+ if (floating_rhs_result_in_eax_bool) {
+ emit_eax_bool_to_floating_stack_now ();
+ }
+
+ op = tok.kind;
+ get_token ();
+
+ emit_load_floating_rhs_operand_now (result_size);
+
+ if (floating_rhs_result_in_eax_bool) {
+ emit_eax_bool_to_floating_stack_now ();
+ }
+
+ emit_floating_binary_now (op);
+
+ }
+
+ if (is_value_compare_operator (tok.kind)) {
+
+ op = tok.kind;
+ get_token ();
+
+ emit_load_floating_rhs_operand_now (result_size);
+
+ while (token_is_floating_binary_now (tok.kind)) {
+
+ enum token_kind rhs_op = tok.kind;
+ get_token ();
+
+ emit_load_floating_rhs_operand_now (result_size);
+ emit_floating_binary_now (rhs_op);
+
+ }
+
+ emit_floating_compare_to_eax_now (op);
+
+ }
+
+ if (tok.kind == TOK_QMARK) {
+
+ false_label = anon_label++;
+ end_label = anon_label++;
+
+ if (!floating_rhs_result_in_eax_bool) {
+
+ if (state->ofp) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ fprintf (state->ofp, " sub esp, 4\n");
+
+ if (state->syntax & ASM_SYNTAX_NASM) {
+
+ fprintf (state->ofp, " fstp dword [esp]\n");
+ fprintf (state->ofp, " mov eax, dword [esp]\n");
+
+ } else {
+
+ fprintf (state->ofp, " fstp dword ptr [esp]\n");
+ fprintf (state->ofp, " mov eax, dword ptr [esp]\n");
+
+ }
+
+ fprintf (state->ofp, " add esp, 4\n");
+ fprintf (state->ofp, " test eax, eax\n");
+
+ } else {
+
+ fprintf (state->ofp, " subl $4, %%esp\n");
+ fprintf (state->ofp, " fstps (%%esp)\n");
+ fprintf (state->ofp, " movl (%%esp), %%eax\n");
+ fprintf (state->ofp, " addl $4, %%esp\n");
+ fprintf (state->ofp, " testl %%eax, %%eax\n");
+
+ }
+
+ }
+
+ } else if (state->ofp) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " test eax, eax\n");
+ } else {
+ fprintf (state->ofp, " testl %%eax, %%eax\n");
+ }
+
+ }
+
+ if (state->ofp) {
+
+ if ((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) {
+ fprintf (state->ofp, " jz L%d\n", false_label);
+ } else {
+ fprintf (state->ofp, " jz .L%d\n", false_label);
+ }
+
+ }
+
+ floating_rhs_result_in_eax_bool = 0;
+ get_token ();
+
+ emit_load_floating_rhs_expression_now (result_size);
+ expect (TOK_COLON, ":");
+
+ emit_statement_jump (end_label);
+ emit_statement_label (false_label);
+
+ emit_load_floating_rhs_expression_now (result_size);
+ emit_statement_label (end_label);
+
+ floating_rhs_result_in_eax_bool = 0;
+
+ }
+
+}
+
+static void emit_load_assignment_rhs_expression_to_reg (const char *reg);
+static int rhs_current_operand_is_floating_now (void);
+
+static int current_argument_is_bare_identifier_now (void) {
+
+ const char *p;
+
+ if (tok.kind != TOK_IDENT || !tok.caret) {
+ return 0;
+ }
+
+ p = tok.caret + tok.len;
+
+ while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+ p++;
+ }
+
+ return *p == ',' || *p == ')';
+
+}
+
+static int emit_push_aggregate_argument_now (const char *name, struct local_symbol *sym) {
+
+ int size;
+ int offset;
+ int chunk;
+
+ char memref[64];
+
+ if (!name || !sym || sym->is_array || sym->pointer_depth > 0 || sym->is_floating) {
+ return 0;
+ }
+
+ /**
+ * Struct/union locals keep their real byte size here. Masking with
+ * 0x1f is only valid for scalar DATA_* encodings; it turns e.g. a
+ * 72-byte struct cpu_flags argument into an 8-byte argument.
+ */
+ size = sym->size;
+
+ /*
+ * Do not treat plain 64-bit scalar locals as aggregate arguments.
+ * The old test used only size > 4, so a call such as:
+ *
+ * bytearray_write_4_bytes (..., result, endianess)
+ *
+ * where result is uint_fast64_t/address_type pushed both halves of
+ * result. The callee expects an unsigned long here, so the extra high
+ * word shifted the following arguments and pdld wrote broken relocation
+ * bytes. Real structs/unions either carry an aggregate tag here, or are
+ * larger than the built-in long long scalar size.
+ */
+ if (size <= (DATA_PTR & 0x1f) || (size <= (DATA_LLONG & 0x1f) && !sym->tag_name)) {
+ return 0;
+ }
+
+ if (!state->ofp) {
+ return 1;
+ }
+
+ for (offset = size; offset > 0; ) {
+
+ if (offset >= 4) {
+
+ chunk = 4;
+ offset -= 4;
+
+ } else if (offset >= 2) {
+
+ chunk = 2;
+ offset -= 2;
+
+ } else {
+
+ chunk = 1;
+ offset -= 1;
+
+ }
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ if (sym->is_static && sym->static_label) {
+
+ if (chunk == 4) {
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " push dword [%s + %d]\n" : " push dword ptr %s + %d\n"), asm_global_symbol_name (sym->static_label), offset);
+ } else if (chunk == 2) {
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " movzx eax, word [%s + %d]\n" : " movzx eax, word ptr %s + %d\n"), asm_global_symbol_name (sym->static_label), offset); fprintf (state->ofp, " push eax\n");
+ } else {
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " movzx eax, byte [%s + %d]\n" : " movzx eax, byte ptr %s + %d\n"), asm_global_symbol_name (sym->static_label), offset); fprintf (state->ofp, " push eax\n");
+ }
+
+ } else {
+
+ format_intel_ebp_offset (memref, sizeof (memref), sym->offset + offset);
+
+ if (chunk == 4) {
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " push dword %s\n" : " push dword ptr %s\n"), memref);
+ } else if (chunk == 2) {
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " movzx eax, word %s\n" : " movzx eax, word ptr %s\n"), memref); fprintf (state->ofp, " push eax\n");
+ } else {
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " movzx eax, byte %s\n" : " movzx eax, byte ptr %s\n"), memref); fprintf (state->ofp, " push eax\n");
+ }
+
+ }
+
+ } else {
+
+ if (sym->is_static && sym->static_label) {
+
+ if (chunk == 4) {
+ fprintf (state->ofp, " pushl %s+%d\n", asm_global_symbol_name (sym->static_label), offset);
+ } else if (chunk == 2) {
+ fprintf (state->ofp, " movzwl %s+%d, %%eax\n", asm_global_symbol_name (sym->static_label), offset); fprintf (state->ofp, " pushl %%eax\n");
+ } else {
+ fprintf (state->ofp, " movzbl %s+%d, %%eax\n", asm_global_symbol_name (sym->static_label), offset); fprintf (state->ofp, " pushl %%eax\n");
+ }
+
+ } else {
+
+ if (chunk == 4) {
+ fprintf (state->ofp, " pushl %ld(%%ebp)\n", sym->offset + offset);
+ } else if (chunk == 2) {
+ fprintf (state->ofp, " movzwl %ld(%%ebp), %%eax\n", sym->offset + offset); fprintf (state->ofp, " pushl %%eax\n");
+ } else {
+ fprintf (state->ofp, " movzbl %ld(%%ebp), %%eax\n", sym->offset + offset); fprintf (state->ofp, " pushl %%eax\n");
+ }
+
+ }
+
+ }
+
+ }
+
+ return 1;
+
+}
+
+static int emit_push_global_aggregate_argument_now (const char *name) {
+
+ int size;
+ int offset;
+ int chunk;
+
+ if (!name || get_global_symbol_kind (name) != GLOBAL_SYMBOL_OBJECT ||
+ get_global_symbol_array (name) || get_global_symbol_pointer_depth (name) > 0 ||
+ get_global_symbol_floating (name)) {
+ return 0;
+ }
+
+ size = get_global_symbol_size (name);
+
+ if (size <= (DATA_PTR & 0x1f)) {
+ return 0;
+ }
+
+ if (!state->ofp) {
+ return 1;
+ }
+
+ for (offset = size; offset > 0; ) {
+
+ if (offset >= 4) {
+
+ chunk = 4;
+ offset -= 4;
+
+ } else if (offset >= 2) {
+
+ chunk = 2;
+ offset -= 2;
+
+ } else {
+
+ chunk = 1;
+ offset -= 1;
+
+ }
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ if (chunk == 4) {
+
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ?
+ " push dword [%s + %d]\n" :
+ " push dword ptr %s + %d\n"), asm_global_symbol_name (name), offset);
+
+ } else if (chunk == 2) {
+
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ?
+ " movzx eax, word [%s + %d]\n" :
+ " movzx eax, word ptr %s + %d\n"), asm_global_symbol_name (name), offset);
+ fprintf (state->ofp, " push eax\n");
+
+ } else {
+
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ?
+ " movzx eax, byte [%s + %d]\n" :
+ " movzx eax, byte ptr %s + %d\n"), asm_global_symbol_name (name), offset);
+ fprintf (state->ofp, " push eax\n");
+
+ }
+
+ } else {
+
+ if (chunk == 4) {
+ fprintf (state->ofp, " pushl %s+%d\n", asm_global_symbol_name (name), offset);
+ } else if (chunk == 2) {
+
+ fprintf (state->ofp, " movzwl %s+%d, %%eax\n", asm_global_symbol_name (name), offset);
+ fprintf (state->ofp, " pushl %%eax\n");
+
+ } else {
+
+ fprintf (state->ofp, " movzbl %s+%d, %%eax\n", asm_global_symbol_name (name), offset);
+ fprintf (state->ofp, " pushl %%eax\n");
+
+ }
+
+ }
+
+ }
+
+ return 1;
+
+}
+
+static void emit_call_pointer_in_reg_now (const char *fn_reg, const char *result_reg) {
+
+ int argc = 0;
+ int total_arg_bytes = 0;
+ int arg_bytes;
+ int arg_is_floating;
+ int i;
+ int ch;
+
+ FILE **arg_tmp_ofps = 0;
+ FILE **new_arg_tmp_ofps = 0;
+ FILE *arg_saved_ofp = 0;
+ FILE *arg_tmp_ofp = 0;
+
+ if (tok.kind != TOK_LPAREN) {
+ return;
+ }
+
+ if (state->ofp) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " push %s\n", fn_reg);
+ } else {
+ fprintf (state->ofp, " pushl %%%s\n", fn_reg);
+ }
+
+ }
+
+ get_token ();
+
+ if (tok.kind != TOK_RPAREN) {
+
+ for (;;) {
+
+ arg_saved_ofp = 0;
+ arg_tmp_ofp = 0;
+
+ if (state->ofp) {
+
+ arg_tmp_ofp = tmpfile ();
+
+ if (arg_tmp_ofp) {
+
+ arg_saved_ofp = state->ofp;
+ state->ofp = arg_tmp_ofp;
+
+ }
+
+ }
+
+ postfix_member_seen = 0;
+ postfix_member_size = 0;
+ postfix_member_pointer_depth = 0;
+ postfix_member_pointed_size = 0;
+
+ if (tok.kind == TOK_IDENT && tok.ident && current_argument_is_bare_identifier_now () && find_local_symbol (tok.ident) && emit_push_aggregate_argument_now (tok.ident, find_local_symbol (tok.ident))) {
+
+ struct local_symbol *arg_sym = find_local_symbol (tok.ident);
+
+ arg_bytes = arg_sym ? (arg_sym->size & 0x1f) : DATA_PTR;
+ arg_is_floating = 0;
+
+ get_token ();
+
+ } else {
+
+ arg_is_floating = rhs_current_operand_is_floating_now ();
+ arg_bytes = arg_is_floating ? (DATA_DOUBLE & 0x1f) : DATA_PTR;
+
+ if (arg_is_floating) {
+ emit_load_floating_rhs_expression_now (DATA_DOUBLE & 0x1f);
+ } else {
+ emit_load_assignment_rhs_expression_to_reg ("eax");
+ }
+
+ if (state->ofp) {
+
+ if (!arg_is_floating && postfix_member_seen && postfix_member_pointer_depth == 0 && postfix_member_size > (DATA_PTR & 0x1f)) {
+
+ arg_bytes = postfix_member_size;
+ emit_push_aggregate_from_addr_reg_now ("eax", arg_bytes);
+
+ } else if (!arg_is_floating && postfix_member_size > (DATA_PTR & 0x1f)) {
+
+ arg_bytes = postfix_member_size;
+ emit_push_aggregate_from_addr_reg_now ("eax", arg_bytes);
+
+ } else if (arg_is_floating) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ fprintf (state->ofp, " sub esp, 8\n");
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " fstp qword [esp]\n" : " fstp qword ptr [esp]\n"));
+
+ } else {
+
+ fprintf (state->ofp, " subl $8, %%esp\n");
+ fprintf (state->ofp, " fstpl (%%esp)\n");
+
+ }
+
+ } else {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " push eax\n");
+ } else {
+ fprintf (state->ofp, " pushl %%eax\n");
+ }
+
+ }
+
+ }
+
+ }
+
+ total_arg_bytes += arg_bytes;
+
+ if (arg_saved_ofp) {
+
+ fflush (arg_tmp_ofp);
+
+ state->ofp = arg_saved_ofp;
+ new_arg_tmp_ofps = (FILE **) realloc (arg_tmp_ofps, sizeof (*arg_tmp_ofps) * (argc + 1));
+
+ if (new_arg_tmp_ofps) {
+
+ arg_tmp_ofps = new_arg_tmp_ofps;
+ arg_tmp_ofps[argc] = arg_tmp_ofp;
+ arg_tmp_ofp = 0;
+
+ }
+
+ }
+
+ if (arg_tmp_ofp) {
+
+ fclose (arg_tmp_ofp);
+ arg_tmp_ofp = 0;
+
+ }
+
+ argc++;
+
+ if (!_accept (TOK_COMMA)) {
+ break;
+ }
+
+ }
+
+ }
+
+ expect (TOK_RPAREN, ")");
+
+ if (state->ofp) {
+
+ for (i = argc - 1; i >= 0; i--) {
+
+ if (arg_tmp_ofps && arg_tmp_ofps[i]) {
+
+ fseek (arg_tmp_ofps[i], 0, SEEK_SET);
+
+ while ((ch = fgetc (arg_tmp_ofps[i])) != EOF) {
+ fputc (ch, state->ofp);
+ }
+
+ fclose (arg_tmp_ofps[i]);
+ arg_tmp_ofps[i] = 0;
+
+ }
+
+ }
+
+ if (arg_tmp_ofps) {
+
+ free (arg_tmp_ofps);
+ arg_tmp_ofps = 0;
+
+ }
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ if (state->syntax & ASM_SYNTAX_NASM) {
+ fprintf (state->ofp, " mov ecx, dword [esp + %d]\n", total_arg_bytes);
+ } else {
+ fprintf (state->ofp, " mov ecx, dword ptr [esp + %d]\n", total_arg_bytes);
+ }
+
+ fprintf (state->ofp, " call ecx\n");
+ fprintf (state->ofp, " add esp, %d\n", total_arg_bytes + (DATA_PTR & 0x1f));
+
+ if (strcmp (result_reg, "eax") != 0) {
+ fprintf (state->ofp, " mov %s, eax\n", result_reg);
+ }
+
+ } else {
+
+ fprintf (state->ofp, " movl %d(%%esp), %%ecx\n", total_arg_bytes);
+ fprintf (state->ofp, " call *%%ecx\n");
+ fprintf (state->ofp, " addl $%d, %%esp\n", total_arg_bytes + (DATA_PTR & 0x1f));
+
+ if (strcmp (result_reg, "eax") != 0) {
+ fprintf (state->ofp, " movl %%eax, %%%s\n", result_reg);
+ }
+
+ }
+
+ }
+
+ if (arg_tmp_ofps) {
+
+ for (i = 0; i < argc; i++) {
+
+ if (arg_tmp_ofps[i]) {
+ fclose (arg_tmp_ofps[i]);
+ }
+
+ }
+
+ free (arg_tmp_ofps);
+
+ }
+
+}
+
+static void emit_sub_esp_now (int bytes) {
+
+ if (!state->ofp || bytes <= 0) {
+ return;
+ }
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " sub esp, %d\n", bytes);
+ } else {
+ fprintf (state->ofp, " subl $%d, %%esp\n", bytes);
+ }
+
+}
+
+static void emit_push_pending_struct_return_address_now (int stack_arg_bytes) {
+
+ if (!state->ofp || (!pending_struct_return_lhs && !pending_struct_return_global_name && !pending_struct_return_stack_address && !pending_struct_return_stack_top)) {
+ return;
+ }
+
+ if (pending_struct_return_stack_top) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ if (state->syntax & ASM_SYNTAX_NASM) {
+ fprintf (state->ofp, " lea eax, [esp + %d]\n", stack_arg_bytes);
+ } else {
+ fprintf (state->ofp, " lea eax, dword ptr [esp + %d]\n", stack_arg_bytes);
+ }
+
+ } else {
+ fprintf (state->ofp, " leal %d(%%esp), %%eax\n", stack_arg_bytes);
+ }
+
+ } else if (pending_struct_return_stack_address) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ if (state->syntax & ASM_SYNTAX_NASM) {
+ fprintf (state->ofp, " mov eax, dword [esp + %d]\n", stack_arg_bytes);
+ } else {
+ fprintf (state->ofp, " mov eax, dword ptr [esp + %d]\n", stack_arg_bytes);
+ }
+
+ if (pending_struct_return_stack_offset) {
+ fprintf (state->ofp, " add eax, %d\n", pending_struct_return_stack_offset);
+ }
+
+ } else {
+
+ fprintf (state->ofp, " movl %d(%%esp), %%eax\n", stack_arg_bytes);
+
+ if (pending_struct_return_stack_offset) {
+ fprintf (state->ofp, " addl $%d, %%eax\n", pending_struct_return_stack_offset);
+ }
+
+ }
+
+ } else if (pending_struct_return_lhs) {
+
+ if (pending_struct_return_lhs->is_static && pending_struct_return_lhs->static_label) {
+ emit_load_address_to_reg_now ("eax", pending_struct_return_lhs->static_label);
+ } else {
+ emit_load_local_address_to_reg_now ("eax", pending_struct_return_lhs->offset);
+ }
+
+ } else {
+ emit_load_address_to_reg_now ("eax", pending_struct_return_global_name);
+ }
+
+ emit_push_reg_now ("eax");
+
+}
+
+static void emit_call_identifier_to_reg_now (const char *name, const char *reg, const char *name_start, const char *name_caret, unsigned long name_line) {
+
+ int argc = 0;
+ int inline_index;
+ int use_inline = 0;
+ int expected_inline_args = 0;
+ int inline_arg_bytes = 0;
+ int total_arg_bytes = 0;
+ int arg_bytes;
+ int arg_is_floating;
+ int i;
+ int ch;
+
+ struct local_symbol *saved_pending_struct_return_lhs = pending_struct_return_lhs;
+ struct local_symbol *call_sym = 0;
+
+ const char *saved_pending_struct_return_global_name = pending_struct_return_global_name;
+ const char *asm_name;
+
+ int saved_pending_struct_return_stack_address = pending_struct_return_stack_address;
+ int saved_pending_struct_return_stack_offset = pending_struct_return_stack_offset;
+ int saved_pending_struct_return_stack_top = pending_struct_return_stack_top;
+
+ FILE *inline_saved_ofp = 0;
+ FILE *inline_tmp_ofp = 0;
+ FILE **arg_tmp_ofps = 0;
+ FILE **new_arg_tmp_ofps = 0;
+ FILE *arg_saved_ofp = 0;
+ FILE *arg_tmp_ofp = 0;
+
+ if (tok.kind != TOK_LPAREN) {
+ return;
+ }
+
+ inline_index = find_inline_function (name);
+
+ if (inline_index >= 0 &&
+ inline_functions[inline_index].usable &&
+ !inline_functions[inline_index].is_floating &&
+ !inline_functions[inline_index].expanding &&
+ (inline_functions[inline_index].body || inline_functions[inline_index].returns_void) &&
+ (!inline_functions[inline_index].body || inline_body_stack_delta (inline_functions[inline_index].body) == 0)) {
+
+ use_inline = 1;
+ expected_inline_args = inline_functions[inline_index].param_count;
+ inline_arg_bytes = expected_inline_args * 4;
+
+ if (state->ofp) {
+
+ inline_tmp_ofp = tmpfile ();
+
+ if (inline_tmp_ofp) {
+
+ inline_saved_ofp = state->ofp;
+ state->ofp = inline_tmp_ofp;
+
+ }
+
+ if (inline_arg_bytes > 0) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " sub esp, %d\n", inline_arg_bytes);
+ } else {
+ fprintf (state->ofp, " subl $%d, %%esp\n", inline_arg_bytes);
+ }
+
+ }
+
+ }
+
+ }
+
+ get_token ();
+
+ if (tok.kind != TOK_RPAREN) {
+
+ for (;;) {
+
+ arg_saved_ofp = 0;
+ arg_tmp_ofp = 0;
+
+ /*
+ * cdecl wants the right-most argument nearest the call site and
+ * the left-most argument at [ebp + 8] in the callee. The old
+ * code emitted each push immediately while parsing left-to-right,
+ * which reversed the parameter slots for normal calls. Capture
+ * each non-inline argument's evaluation/push code and replay the
+ * completed argument blocks right-to-left just before CALL.
+ *
+ * Inline calls keep using the temporary argument frame below: that
+ * frame intentionally stores argument 0 at [esp], argument 1 at
+ * [esp + 4], etc., so do not reverse inline argument copies here.
+ */
+ if (!use_inline && state->ofp) {
+
+ arg_tmp_ofp = tmpfile ();
+
+ if (arg_tmp_ofp) {
+ arg_saved_ofp = state->ofp;
+ state->ofp = arg_tmp_ofp;
+ }
+
+ }
+
+ postfix_member_seen = 0;
+ postfix_member_size = 0;
+ postfix_member_pointer_depth = 0;
+ postfix_member_pointed_size = 0;
+
+ if (!use_inline && tok.kind == TOK_IDENT && tok.ident && current_argument_is_bare_identifier_now () && find_local_symbol (tok.ident) && emit_push_aggregate_argument_now (tok.ident, find_local_symbol (tok.ident))) {
+
+ struct local_symbol *arg_sym = find_local_symbol (tok.ident);
+
+ arg_bytes = arg_sym ? arg_sym->size : DATA_PTR;
+ arg_is_floating = 0;
+
+ get_token ();
+
+ } else if (!use_inline && tok.kind == TOK_IDENT && tok.ident && current_argument_is_bare_identifier_now () && emit_push_global_aggregate_argument_now (tok.ident)) {
+
+ arg_bytes = get_global_symbol_size (tok.ident);
+ arg_is_floating = 0;
+
+ get_token ();
+
+ } else if (!use_inline && tok.kind == TOK_IDENT && tok.ident && token_identifier_is_function_call_rhs_now () &&
+ get_global_symbol_kind (tok.ident) == GLOBAL_SYMBOL_FUNCTION &&
+ get_global_symbol_size (tok.ident) > (DATA_LLONG & 0x1f)) {
+
+ char *arg_call_name = xstrdup (tok.ident);
+
+ const char *arg_call_start = tok.start;
+ const char *arg_call_caret = tok.caret;
+
+ unsigned long arg_call_line = get_line_number ();
+
+ arg_bytes = get_global_symbol_size (arg_call_name);
+ arg_is_floating = 0;
+
+ emit_sub_esp_now (arg_bytes);
+
+ pending_struct_return_lhs = 0;
+ pending_struct_return_global_name = 0;
+ pending_struct_return_stack_address = 0;
+ pending_struct_return_stack_offset = 0;
+ pending_struct_return_stack_top = 1;
+
+ get_token ();
+ emit_call_identifier_to_reg_now (arg_call_name, "eax", arg_call_start, arg_call_caret, arg_call_line);
+
+ pending_struct_return_stack_top = 0;
+ free (arg_call_name);
+
+ } else {
+
+ arg_is_floating = rhs_current_operand_is_floating_now ();
+ arg_bytes = arg_is_floating ? (DATA_DOUBLE & 0x1f) : DATA_PTR;
+
+ if (arg_is_floating) {
+ emit_load_floating_rhs_expression_now (DATA_DOUBLE & 0x1f);
+ } else {
+ emit_load_assignment_rhs_expression_to_reg ("eax");
+ }
+
+ if (state->ofp) {
+
+ if (!use_inline && !arg_is_floating && postfix_member_seen && postfix_member_pointer_depth == 0 && postfix_member_size > (DATA_PTR & 0x1f)) {
+
+ arg_bytes = postfix_member_size;
+ emit_push_aggregate_from_addr_reg_now ("eax", arg_bytes);
+
+ } else if (!use_inline && !arg_is_floating && postfix_member_size > (DATA_PTR & 0x1f)) {
+
+ arg_bytes = postfix_member_size;
+ emit_push_aggregate_from_addr_reg_now ("eax", arg_bytes);
+
+ } else if (use_inline) {
+
+ if (argc < expected_inline_args) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov dword [esp + %d], eax\n" : " mov dword ptr [esp + %d], eax\n"), argc * 4);
+ } else {
+ fprintf (state->ofp, " movl %%eax, %d(%%esp)\n", argc * 4);
+ }
+
+ }
+
+ } else if (arg_is_floating) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ fprintf (state->ofp, " sub esp, 8\n");
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " fstp qword [esp]\n" : " fstp qword ptr [esp]\n"));
+
+ } else {
+
+ fprintf (state->ofp, " subl $8, %%esp\n");
+ fprintf (state->ofp, " fstpl (%%esp)\n");
+
+ }
+
+ } else {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " push eax\n");
+ } else {
+ fprintf (state->ofp, " pushl %%eax\n");
+ }
+
+ }
+
+ }
+
+ }
+
+ if (!use_inline) {
+ total_arg_bytes += arg_bytes;
+ }
+
+ if (arg_saved_ofp) {
+
+ fflush (arg_tmp_ofp);
+ state->ofp = arg_saved_ofp;
+
+ new_arg_tmp_ofps = (FILE **) realloc (arg_tmp_ofps, sizeof (*arg_tmp_ofps) * (argc + 1));
+
+ if (new_arg_tmp_ofps) {
+
+ arg_tmp_ofps = new_arg_tmp_ofps;
+ arg_tmp_ofps[argc] = arg_tmp_ofp;
+ arg_tmp_ofp = 0;
+
+ }
+
+ }
+
+ if (arg_tmp_ofp) {
+
+ fclose (arg_tmp_ofp);
+ arg_tmp_ofp = 0;
+
+ }
+
+ argc++;
+
+ if (!_accept (TOK_COMMA)) {
+ break;
+ }
+
+ }
+
+ }
+
+ expect (TOK_RPAREN, ")");
+
+ if (get_global_symbol_kind (name) == GLOBAL_SYMBOL_FUNCTION && get_global_symbol_has_prototype (name) && ((get_global_symbol_is_variadic (name) && argc < get_global_symbol_param_count (name)) || (!get_global_symbol_is_variadic (name) && argc != get_global_symbol_param_count (name)))) {
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "wrong number of arguments to function '%s'", name);
+ }
+
+ if (use_inline) {
+
+ if (argc == expected_inline_args && emit_inline_call_if_possible (name, argc, reg)) {
+
+ if (state->ofp && inline_arg_bytes > 0) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " add esp, %d\n", inline_arg_bytes);
+ } else {
+ fprintf (state->ofp, " addl $%d, %%esp\n", inline_arg_bytes);
+ }
+
+ }
+
+ /*
+ * The peephole inline optimiser currently understands the
+ * single-argument case well, but its stack-slot liveness pass is
+ * too aggressive for multi-argument inline calls. It can fold
+ * constants correctly in simple examples, but it may also remove
+ * the temporary argument frame and leave confusing label-only
+ * fragments. Keep multi-argument inline expansion conservative:
+ * emit the substituted inline body exactly as generated, with the
+ * argument copies still present.
+ */
+ finish_inline_buffer (&inline_tmp_ofp, &inline_saved_ofp, expected_inline_args <= 1);
+ return;
+
+ }
+
+ if (state->ofp && inline_arg_bytes > 0) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " add esp, %d\n", inline_arg_bytes);
+ } else {
+ fprintf (state->ofp, " addl $%d, %%esp\n", inline_arg_bytes);
+ }
+
+ }
+
+ finish_inline_buffer (&inline_tmp_ofp, &inline_saved_ofp, 0);
+ return;
+
+ }
+
+ if (emit_inline_call_if_possible (name, argc, reg)) {
+
+ if (arg_tmp_ofps) {
+
+ for (i = 0; i < argc; i++) {
+
+ if (arg_tmp_ofps[i]) {
+ fclose (arg_tmp_ofps[i]);
+ }
+
+ }
+
+ free (arg_tmp_ofps);
+
+ }
+
+ return;
+ }
+
+ call_sym = find_local_symbol (name);
+
+ if (!call_sym) {
+
+ if (get_global_symbol_kind (name) == GLOBAL_SYMBOL_OBJECT) {
+ emit_extern_reference_symbol (name, DATA_PTR);
+ } else {
+ emit_extern_symbol (name, DATA_PTR, 1);
+ }
+
+ asm_name = asm_global_symbol_name (name);
+
+ } else {
+ asm_name = 0;
+ }
+
+ if (state->ofp) {
+
+ for (i = argc - 1; i >= 0; i--) {
+
+ if (arg_tmp_ofps && arg_tmp_ofps[i]) {
+
+ fseek (arg_tmp_ofps[i], 0, SEEK_SET);
+
+ while ((ch = fgetc (arg_tmp_ofps[i])) != EOF) {
+ fputc (ch, state->ofp);
+ }
+
+ fclose (arg_tmp_ofps[i]);
+ arg_tmp_ofps[i] = 0;
+
+ }
+
+ }
+
+ if (arg_tmp_ofps) {
+
+ free (arg_tmp_ofps);
+ arg_tmp_ofps = 0;
+
+ }
+
+ if (saved_pending_struct_return_lhs || saved_pending_struct_return_global_name ||
+ saved_pending_struct_return_stack_address || saved_pending_struct_return_stack_top) {
+
+ pending_struct_return_lhs = saved_pending_struct_return_lhs;
+ pending_struct_return_global_name = saved_pending_struct_return_global_name;
+ pending_struct_return_stack_address = saved_pending_struct_return_stack_address;
+ pending_struct_return_stack_offset = saved_pending_struct_return_stack_offset;
+ pending_struct_return_stack_top = saved_pending_struct_return_stack_top;
+
+ emit_push_pending_struct_return_address_now (total_arg_bytes);
+ total_arg_bytes += DATA_PTR & 0x1f;
+
+ }
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ if (call_sym) {
+
+ emit_load_local_to_reg ("ecx", call_sym->offset, DATA_PTR);
+ fprintf (state->ofp, " call ecx\n");
+
+ } else if (get_global_symbol_kind (name) == GLOBAL_SYMBOL_OBJECT) {
+
+ emit_load_global_to_reg ("ecx", name, DATA_PTR);
+ fprintf (state->ofp, " call ecx\n");
+
+ } else {
+ fprintf (state->ofp, " call %s\n", asm_name);
+ }
+
+ if (total_arg_bytes > 0) {
+ fprintf (state->ofp, " add esp, %d\n", total_arg_bytes);
+ }
+
+ if (strcmp (reg, "eax") != 0) {
+ fprintf (state->ofp, " mov %s, eax\n", reg);
+ }
+
+ } else {
+
+ if (call_sym) {
+
+ emit_load_local_to_reg ("ecx", call_sym->offset, DATA_PTR);
+ fprintf (state->ofp, " call *%%ecx\n");
+
+ } else if (get_global_symbol_kind (name) == GLOBAL_SYMBOL_OBJECT) {
+
+ emit_load_global_to_reg ("ecx", name, DATA_PTR);
+ fprintf (state->ofp, " call *%%ecx\n");
+
+ } else {
+ fprintf (state->ofp, " call %s\n", asm_name);
+ }
+
+ if (total_arg_bytes > 0) {
+ fprintf (state->ofp, " addl $%d, %%esp\n", total_arg_bytes);
+ }
+
+ if (strcmp (reg, "eax") != 0) {
+ fprintf (state->ofp, " movl %%eax, %%%s\n", reg);
+ }
+
+ }
+
+ }
+
+ set_rhs_last_pointer_info (get_global_symbol_pointer_depth (name), get_global_symbol_pointed_size (name));
+
+ pending_struct_return_lhs = 0;
+ pending_struct_return_global_name = 0;
+ pending_struct_return_stack_address = 0;
+ pending_struct_return_stack_offset = 0;
+ pending_struct_return_stack_top = 0;
+
+ if (arg_tmp_ofps) {
+
+ for (i = 0; i < argc; i++) {
+
+ if (arg_tmp_ofps[i]) {
+ fclose (arg_tmp_ofps[i]);
+ }
+
+ }
+
+ free (arg_tmp_ofps);
+
+ }
+
+}
+
+static void emit_statement_label (int label);
+static void emit_statement_jump (int label);
+
+#define MAX_GOTO_LABELS 256
+#define MAX_GOTO_REFS 512
+
+struct goto_label_entry {
+
+ char *name;
+
+ int label;
+ int defined;
+ int referenced;
+
+ long defined_stack_size;
+ unsigned long line;
+
+ const char *start;
+ const char *caret;
+
+};
+
+static struct goto_label_entry goto_labels[MAX_GOTO_LABELS];
+static int goto_label_count = 0;
+
+struct goto_ref_entry {
+
+ int label_index;
+ int ref_label;
+
+ long stack_size;
+
+};
+
+static struct goto_ref_entry goto_refs[MAX_GOTO_REFS];
+static int goto_ref_count = 0;
+
+static int current_break_label = -1;
+static int current_continue_label = -1;
+
+static long current_break_cleanup_base = 0;
+static long current_continue_cleanup_base = 0;
+
+#define MAX_SWITCH_CASES 256
+
+struct switch_case_entry {
+
+ long value;
+ int label;
+
+};
+
+struct switch_context {
+
+ struct switch_case_entry cases[MAX_SWITCH_CASES];
+ int case_count;
+ int default_label;
+ int break_label;
+
+};
+
+static struct switch_context *current_switch_context = 0;
+static int statement_ends_control_flow;
+
+static void reset_goto_labels (void) {
+
+ int i;
+
+ for (i = 0; i < goto_label_count; i++) {
+
+ if (goto_labels[i].name) {
+ free (goto_labels[i].name);
+ }
+
+ goto_labels[i].name = 0;
+ goto_labels[i].label = 0;
+ goto_labels[i].defined = 0;
+ goto_labels[i].referenced = 0;
+ goto_labels[i].defined_stack_size = 0;
+ goto_labels[i].line = 0;
+ goto_labels[i].start = 0;
+ goto_labels[i].caret = 0;
+
+ }
+
+ goto_label_count = 0;
+ goto_ref_count = 0;
+
+}
+
+static int find_goto_label (const char *name) {
+
+ int i;
+
+ for (i = 0; i < goto_label_count; i++) {
+
+ if (goto_labels[i].name && strcmp (goto_labels[i].name, name) == 0) {
+ return i;
+ }
+
+ }
+
+ return -1;
+
+}
+
+static int get_goto_label (const char *name, unsigned long line, const char *start, const char *caret) {
+
+ int i = find_goto_label (name);
+
+ if (i >= 0) {
+ return i;
+ }
+
+ if (goto_label_count >= MAX_GOTO_LABELS) {
+
+ report_line_at (get_filename (), line, REPORT_ERROR, start, caret, "too many goto labels");
+ return -1;
+
+ }
+
+ i = goto_label_count++;
+
+ goto_labels[i].name = xstrdup (name);
+ goto_labels[i].label = anon_label++;
+ goto_labels[i].defined = 0;
+ goto_labels[i].referenced = 0;
+ goto_labels[i].defined_stack_size = 0;
+ goto_labels[i].line = line;
+ goto_labels[i].start = start;
+ goto_labels[i].caret = caret;
+
+ return i;
+
+}
+
+static void define_goto_label (const char *name, unsigned long line, const char *start, const char *caret) {
+
+ int i = get_goto_label (name, line, start, caret);
+
+ if (i < 0) {
+ return;
+ }
+
+ if (goto_labels[i].defined) {
+
+ report_line_at (get_filename (), line, REPORT_ERROR, start, caret, "duplicate label '%s'", name);
+ return;
+
+ }
+
+ goto_labels[i].defined = 1;
+ goto_labels[i].defined_stack_size = current_local_stack_size;
+
+ /**
+ * A C label marks the next statement. If the previous statement was a
+ * return, the compiler may have deferred the jump to the common return
+ * label so it can avoid redundant jumps. Flush it before emitting the
+ * user label, otherwise the user label will point at the deferred return
+ * jump instead of the labelled statement.
+ */
+ if (pending_return_jump) {
+ emit_pending_return_jump ();
+ }
+
+ emit_statement_label (goto_labels[i].label);
+
+}
+
+static void reference_goto_label (const char *name, unsigned long line, const char *start, const char *caret) {
+
+ int i = get_goto_label (name, line, start, caret);
+
+ if (i < 0) {
+ return;
+ }
+
+ goto_labels[i].referenced = 1;
+
+ /*
+ * If this is a forward goto, do not jump directly to the final C
+ * label. The target may be after automatic declarations in an inner
+ * block. Because SCC emits stack allocation when those declarations are
+ * parsed, a direct branch can bypass the allocation and then use invalid
+ * EBP-relative locals. Emit a per-reference trampoline after the function
+ * epilogue once the target stack depth is known.
+ */
+ if (!goto_labels[i].defined) {
+
+ if (goto_ref_count < MAX_GOTO_REFS) {
+
+ int ref_label = anon_label++;
+
+ goto_refs[goto_ref_count].label_index = i;
+ goto_refs[goto_ref_count].ref_label = ref_label;
+ goto_refs[goto_ref_count].stack_size = current_local_stack_size;
+ goto_ref_count++;
+
+ emit_statement_jump (ref_label);
+ return;
+
+ }
+
+ }
+
+ emit_statement_jump (goto_labels[i].label);
+
+}
+
+static void check_goto_labels (void) {
+
+ int i;
+
+ for (i = 0; i < goto_label_count; i++) {
+
+ if (goto_labels[i].referenced && !goto_labels[i].defined) {
+ report_line_at (get_filename (), goto_labels[i].line, REPORT_ERROR, goto_labels[i].start, goto_labels[i].caret, "undefined label '%s'", goto_labels[i].name);
+ }
+
+ }
+
+}
+
+static void emit_goto_trampolines (void) {
+
+ int i;
+
+ if (!state->ofp) {
+ return;
+ }
+
+ for (i = 0; i < goto_ref_count; i++) {
+
+ int label_index = goto_refs[i].label_index;
+ long delta;
+
+ if (label_index < 0 || label_index >= goto_label_count) {
+ continue;
+ }
+
+ if (!goto_labels[label_index].defined) {
+ continue;
+ }
+
+ emit_statement_label_raw (goto_refs[i].ref_label);
+ delta = goto_labels[label_index].defined_stack_size - goto_refs[i].stack_size;
+
+ if (delta > 0) {
+ emit_stack_adjust (delta, 1);
+ } else if (delta < 0) {
+ emit_stack_adjust (-delta, 0);
+ }
+
+ emit_statement_jump (goto_labels[label_index].label);
+
+ }
+
+}
+
+static void queue_pending_statement_label (int label) {
+
+ if (label < 0) {
+ return;
+ }
+
+ if (pending_statement_label_count >= MAX_PENDING_STATEMENT_LABELS) {
+ flush_pending_statement_labels ();
+ }
+
+ if (pending_statement_label_count < MAX_PENDING_STATEMENT_LABELS) {
+ pending_statement_labels[pending_statement_label_count++] = label;
+ } else {
+ emit_statement_label (label);
+ }
+
+}
+
+static void add_switch_case_label (long value, unsigned long line, const char *start, const char *caret) {
+
+ int i;
+
+ if (!current_switch_context) {
+
+ report_line_at (get_filename (), line, REPORT_ERROR, start, caret, "case label not within a switch statement");
+ return;
+
+ }
+
+ for (i = 0; i < current_switch_context->case_count; i++) {
+
+ if (current_switch_context->cases[i].value == value) {
+
+ report_line_at (get_filename (), line, REPORT_ERROR, start, caret, "duplicate case value");
+ return;
+
+ }
+
+ }
+
+ if (current_switch_context->case_count >= MAX_SWITCH_CASES) {
+
+ report_line_at (get_filename (), line, REPORT_ERROR, start, caret, "too many case labels in switch statement");
+ return;
+
+ }
+
+ current_switch_context->cases[current_switch_context->case_count].value = value;
+ current_switch_context->cases[current_switch_context->case_count].label = anon_label++;
+
+ queue_pending_statement_label (current_switch_context->cases[current_switch_context->case_count].label);
+ current_switch_context->case_count++;
+
+}
+
+static void set_switch_default_label (unsigned long line, const char *start, const char *caret) {
+
+ if (!current_switch_context) {
+
+ report_line_at (get_filename (), line, REPORT_ERROR, start, caret, "default label not within a switch statement");
+ return;
+
+ }
+
+ if (current_switch_context->default_label >= 0) {
+
+ report_line_at (get_filename (), line, REPORT_ERROR, start, caret, "duplicate default label");
+ return;
+
+ }
+
+ current_switch_context->default_label = anon_label++;
+ queue_pending_statement_label (current_switch_context->default_label);
+
+}
+
+static void emit_switch_dispatch (struct switch_context *sw) {
+
+ int i;
+ int target;
+
+ if (!state->ofp || !sw) {
+ return;
+ }
+
+ for (i = 0; i < sw->case_count; i++) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ fprintf (state->ofp, " cmp eax, %ld\n", sw->cases[i].value);
+
+ if ((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) {
+ fprintf (state->ofp, " je L%d\n", sw->cases[i].label);
+ } else {
+ fprintf (state->ofp, " je .L%d\n", sw->cases[i].label);
+ }
+
+ } else {
+
+ fprintf (state->ofp, " cmpl $%ld, %%eax\n", sw->cases[i].value);
+ fprintf (state->ofp, " je .L%d\n", sw->cases[i].label);
+
+ }
+
+ }
+
+ target = (sw->default_label >= 0) ? sw->default_label : sw->break_label;
+ emit_statement_jump (target);
+
+}
+
+static void parse_switch_statement (void) {
+
+ struct switch_context sw;
+ struct switch_context *old_switch_context;
+
+ int old_break_label;
+ int old_continue_label;
+
+ long old_break_cleanup_base;
+ long old_continue_cleanup_base;
+
+ int saved_ends_control_flow;
+
+ FILE *saved_ofp;
+ FILE *body_tmp = 0;
+
+ char *body_text = 0;
+ int body_pending_return_jump = 0;
+
+ sw.case_count = 0;
+ sw.default_label = -1;
+ sw.break_label = anon_label++;
+
+ get_token ();
+ expect (TOK_LPAREN, "(");
+
+ emit_load_assignment_rhs_expression_to_reg ("eax");
+
+ if (tok.kind != TOK_RPAREN) {
+ skip_balanced_until (TOK_RPAREN, TOK_EOF, TOK_EOF);
+ }
+
+ expect (TOK_RPAREN, ")");
+
+ old_switch_context = current_switch_context;
+ old_break_label = current_break_label;
+ old_continue_label = current_continue_label;
+ old_break_cleanup_base = current_break_cleanup_base;
+ old_continue_cleanup_base = current_continue_cleanup_base;
+
+ current_switch_context = &sw;
+ current_break_label = sw.break_label;
+ current_break_cleanup_base = current_block_cleanup_bytes;
+
+ saved_ofp = state->ofp;
+
+ if (saved_ofp) {
+
+ body_tmp = tmpfile ();
+
+ if (body_tmp) {
+ state->ofp = body_tmp;
+ }
+
+ }
+
+ parse_statement ();
+ saved_ends_control_flow = statement_ends_control_flow;
+
+ if (body_tmp) {
+
+ body_text = read_tmp_file_text (body_tmp);
+
+ fclose (body_tmp);
+ body_tmp = 0;
+
+ state->ofp = saved_ofp;
+
+ body_pending_return_jump = pending_return_jump;
+ pending_return_jump = 0;
+
+ emit_switch_dispatch (&sw);
+
+ if (body_text) {
+
+ fputs (body_text, state->ofp);
+ free (body_text);
+
+ }
+
+ pending_return_jump = body_pending_return_jump;
+
+ } else {
+ state->ofp = saved_ofp;
+ }
+
+ emit_statement_label (sw.break_label);
+
+ current_switch_context = old_switch_context;
+ current_break_label = old_break_label;
+ current_continue_label = old_continue_label;
+ current_break_cleanup_base = old_break_cleanup_base;
+ current_continue_cleanup_base = old_continue_cleanup_base;
+
+ /*
+ * A break inside the switch only leaves the switch. Do not propagate the
+ * break statement's statement_ends_control_flow flag to the enclosing
+ * statement, otherwise code like:
+ *
+ * if (x) { switch (y) { case 1: break; } } else { ... }
+ *
+ * is compiled without the jump over the else block and the true branch
+ * falls through into the else body.
+ */
+ (void)saved_ends_control_flow;
+ statement_ends_control_flow = 0;
+
+}
+
+static int is_value_compare_operator (enum token_kind k) {
+
+ return k == TOK_LESS || k == TOK_LTEQ || k == TOK_GREATER ||
+ k == TOK_GTEQ || k == TOK_EQEQ || k == TOK_NOTEQ;
+
+}
+
+static const char *value_compare_set_mnemonic (enum token_kind op, int is_unsigned) {
+
+ switch (op) {
+
+ case TOK_LESS:
+
+ return is_unsigned ? "setb" : "setl";
+
+ case TOK_LTEQ:
+
+ return is_unsigned ? "setbe" : "setle";
+
+ case TOK_GREATER:
+
+ return is_unsigned ? "seta" : "setg";
+
+ case TOK_GTEQ:
+
+ return is_unsigned ? "setae" : "setge";
+
+ case TOK_EQEQ:
+
+ return "sete";
+
+ case TOK_NOTEQ:
+
+ return "setne";
+
+ default:
+
+ return "setne";
+
+ }
+
+}
+
+static void emit_compare_eax_edx_to_reg (enum token_kind op, const char *reg, int is_unsigned) {
+
+ const char *setcc;
+
+ if (!state->ofp) {
+ return;
+ }
+
+ setcc = value_compare_set_mnemonic (op, is_unsigned);
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ fprintf (state->ofp, " cmp eax, edx\n");
+ fprintf (state->ofp, " %s al\n", setcc);
+ fprintf (state->ofp, " movzx eax, al\n");
+
+ if (strcmp (reg, "eax") != 0) {
+ fprintf (state->ofp, " mov %s, eax\n", reg);
+ }
+
+ } else {
+
+ fprintf (state->ofp, " cmpl %%edx, %%eax\n");
+ fprintf (state->ofp, " %s %%al\n", setcc);
+ fprintf (state->ofp, " movzbl %%al, %%eax\n");
+
+ if (strcmp (reg, "eax") != 0) {
+ fprintf (state->ofp, " movl %%eax, %%%s\n", reg);
+ }
+
+ }
+
+}
+
+static int rhs_current_operand_is_unsigned_now (void);
+
+static int source_lhs_has_char_pointer_cast_before_now (const char *p) {
+
+ const char *q;
+ int limit = 160;
+
+ if (!p) {
+ return 0;
+ }
+
+ q = p;
+
+ while (limit-- > 0 && q > tok.start - 4096) {
+
+ q--;
+
+ if (*q != '(') {
+ continue;
+ }
+
+ if ((strncmp (q, "(char", 5) == 0 || strncmp (q, "(unsigned char", 14) == 0 || strncmp (q, "(signed char", 12) == 0)
+ && strchr (q, '*') && strchr (q, ')') && strchr (q, '*') < p && strchr (q, ')') < p) {
+ return 1;
+ }
+
+ if (*q == ';' || *q == ',' || *q == '\n') {
+ break;
+ }
+
+ }
+
+ return 0;
+
+}
+
+static int arithmetic_operator_precedence_now (enum token_kind op) {
+
+ if (op == TOK_PIPE) {
+ return 1;
+ }
+
+ if (op == TOK_CARET) {
+ return 2;
+ }
+
+ if (op == TOK_AMPER) {
+ return 3;
+ }
+
+ if (op == TOK_LSH || op == TOK_RSH) {
+ return 4;
+ }
+
+ if (op == TOK_PLUS || op == TOK_MINUS) {
+ return 5;
+ }
+
+ if (op == TOK_STAR || op == TOK_BSLASH || op == TOK_MOD) {
+ return 6;
+ }
+
+ return 0;
+
+}
+
+static int emit_load_assignment_binary_expression_prec_to_reg (const char *reg, int min_prec) {
+
+ int is_unsigned;
+ int expr_pointer_depth;
+ int expr_pointed_size;
+
+ is_unsigned = rhs_current_operand_is_unsigned_now ();
+ emit_load_assignment_rhs_to_reg (reg);
+
+ /*
+ * Some statement-condition paths can leave a postfix member chain after
+ * the primary operand, e.g. inside parenthesized logical RHS terms such as
+ * && (reg->type.dword || reg->type.debug)
+ * Consume that postfix here before the binary/logical expression parser
+ * decides whether the operand is complete. Otherwise the enclosing
+ * parenthesized-expression code sees the still-pending "->"/"." token and
+ * reports a false "expected )".
+ */
+ while (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) {
+ emit_apply_postfix_member_access_to_reg_now (reg);
+ }
+
+ /*
+ * The postfix member helper deliberately leaves aggregates and 64-bit
+ * scalar members as an address because the pair/aggregate paths may need
+ * the full object. This single-register expression path, however, only
+ * has EAX/EDX-style 32-bit arithmetic available. When such a member is
+ * used inside ordinary arithmetic/comparison code (for example the
+ * CHECK_READ macro in pdld with address_type fields), load the low word
+ * from the member address instead of adding the member address itself.
+ */
+ if (postfix_member_seen
+ && postfix_member_pointer_depth == 0
+ && postfix_member_size == (DATA_LLONG & 0x1f)
+ && !postfix_member_is_floating) {
+
+ emit_load_deref_reg_now (reg, DATA_INT & 0x1f);
+ postfix_member_size = DATA_INT & 0x1f;
+
+ }
+
+ if (postfix_member_seen && postfix_member_is_unsigned) {
+ is_unsigned = 1;
+ }
+
+ expr_pointer_depth = rhs_last_pointer_depth;
+ expr_pointed_size = rhs_last_pointed_size;
+
+ while (is_arithmetic_binary_operator (tok.kind) && arithmetic_operator_precedence_now (tok.kind) >= min_prec) {
+
+ enum token_kind op;
+
+ int prec;
+ int rhs_is_unsigned;
+ int lhs_pointer_depth;
+ int lhs_pointed_size;
+ int rhs_pointer_depth;
+ int rhs_pointed_size;
+ int scale_rhs = 0;
+ int scale_lhs = 0;
+
+ op = tok.kind;
+ prec = arithmetic_operator_precedence_now (op);
+
+ lhs_pointer_depth = expr_pointer_depth;
+ lhs_pointed_size = expr_pointed_size;
+
+ if ((op == TOK_PLUS || op == TOK_MINUS) && lhs_pointer_depth > 0 &&
+ source_lhs_has_char_pointer_cast_before_now (tok.caret)) {
+ lhs_pointed_size = DATA_CHAR & 0x1f;
+ }
+
+ get_token ();
+ rhs_is_unsigned = rhs_current_operand_is_unsigned_now ();
+
+ if (rhs_is_unsigned) {
+ is_unsigned = 1;
+ }
+
+ if (strcmp (reg, "eax") != 0 && state->ofp) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " mov eax, %s\n", reg);
+ } else {
+ fprintf (state->ofp, " movl %%%s, %%eax\n", reg);
+ }
+
+ }
+
+ emit_push_reg_now ("eax");
+ rhs_is_unsigned = emit_load_assignment_binary_expression_prec_to_reg ("edx", prec + 1);
+
+ if (rhs_is_unsigned) {
+ is_unsigned = 1;
+ }
+
+ rhs_pointer_depth = rhs_last_pointer_depth;
+ rhs_pointed_size = rhs_last_pointed_size;
+
+ if ((op == TOK_PLUS || op == TOK_MINUS) && lhs_pointer_depth > 0 && rhs_pointer_depth == 0) {
+ scale_rhs = index_step_size (lhs_pointed_size);
+ } else if (op == TOK_PLUS && lhs_pointer_depth == 0 && rhs_pointer_depth > 0) {
+ scale_lhs = index_step_size (rhs_pointed_size);
+ }
+
+ if (scale_rhs > 1) {
+ emit_scale_reg_by_const_now ("edx", scale_rhs);
+ }
+
+ emit_pop_reg_now ("eax");
+
+ if (scale_lhs > 1) {
+ emit_scale_reg_by_const_now ("eax", scale_lhs);
+ }
+
+ emit_assignment_binary_op (op, is_unsigned);
+
+ if (op == TOK_MINUS && lhs_pointer_depth > 0 && rhs_pointer_depth > 0 && index_step_size (lhs_pointed_size) > 1) {
+ emit_divide_eax_by_const_now (index_step_size (lhs_pointed_size));
+ }
+
+ if (op == TOK_PLUS && lhs_pointer_depth == 0 && rhs_pointer_depth > 0) {
+
+ expr_pointer_depth = rhs_pointer_depth;
+ expr_pointed_size = rhs_pointed_size;
+
+ } else if (op == TOK_MINUS && lhs_pointer_depth > 0 && rhs_pointer_depth > 0) {
+
+ expr_pointer_depth = 0;
+ expr_pointed_size = 0;
+
+ } else if (op != TOK_PLUS && op != TOK_MINUS) {
+
+ expr_pointer_depth = 0;
+ expr_pointed_size = 0;
+
+ }
+
+ if (strcmp (reg, "eax") != 0 && state->ofp) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " mov %s, eax\n", reg);
+ } else {
+ fprintf (state->ofp, " movl %%eax, %%%s\n", reg);
+ }
+
+ }
+
+ }
+
+ set_rhs_last_pointer_info (expr_pointer_depth, expr_pointed_size);
+ return is_unsigned;
+
+}
+
+static void emit_load_assignment_binary_expression_to_reg (const char *reg) {
+
+ if (const_integer_expr_text_is_foldable_now (tok.caret)) {
+
+ int64_s v = const64_from_current_foldable_expr ();
+ emit_load_const32_to_reg_now (reg, v);
+
+ return;
+
+ }
+
+ emit_load_assignment_binary_expression_prec_to_reg (reg, 1);
+
+}
+
+static void emit_load_assignment_compare_expression_to_reg (const char *reg) {
+
+ enum token_kind op;
+
+ int lhs_pointer_depth;
+ int is_unsigned;
+
+ is_unsigned = rhs_current_operand_is_unsigned_now ();
+
+ if (emit_load_assignment_binary_expression_prec_to_reg (reg, 1)) {
+ is_unsigned = 1;
+ }
+
+ lhs_pointer_depth = rhs_last_pointer_depth;
+
+ if (is_value_compare_operator (tok.kind)) {
+
+ op = tok.kind;
+ get_token ();
+
+ if (strcmp (reg, "eax") != 0 && state->ofp) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " mov eax, %s\n", reg);
+ } else {
+ fprintf (state->ofp, " movl %%%s, %%eax\n", reg);
+ }
+
+ }
+
+ emit_push_reg_now ("eax");
+
+ if (rhs_current_operand_is_unsigned_now ()) {
+ is_unsigned = 1;
+ }
+
+ emit_load_assignment_binary_expression_to_reg ("edx");
+
+ if (lhs_pointer_depth > 0 || rhs_last_pointer_depth > 0) {
+ is_unsigned = 1;
+ }
+
+ emit_pop_reg_now ("eax");
+ emit_compare_eax_edx_to_reg (op, reg, is_unsigned);
+
+ }
+
+}
+
+static void emit_test_reg_jump_zero_now (const char *reg, int label) {
+
+ if (!state->ofp) {
+ return;
+ }
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ fprintf (state->ofp, " test %s, %s\n", reg, reg);
+ fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? " jz L%d\n" : " jz .L%d\n"), label);
+
+ } else {
+
+ fprintf (state->ofp, " testl %%%s, %%%s\n", reg, reg);
+ fprintf (state->ofp, " jz .L%d\n", label);
+
+ }
+
+}
+
+static void emit_test_reg_jump_nonzero_now (const char *reg, int label) {
+
+ if (!state->ofp) {
+ return;
+ }
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ fprintf (state->ofp, " test %s, %s\n", reg, reg);
+ fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? " jnz L%d\n" : " jnz .L%d\n"), label);
+
+ } else {
+
+ fprintf (state->ofp, " testl %%%s, %%%s\n", reg, reg);
+ fprintf (state->ofp, " jnz .L%d\n", label);
+
+ }
+
+}
+
+static void emit_mov_imm_to_reg_now (const char *reg, long value) {
+
+ if (!state->ofp) {
+ return;
+ }
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " mov %s, %ld\n", reg, value);
+ } else {
+ fprintf (state->ofp, " movl $%ld, %%%s\n", value, reg);
+ }
+
+}
+
+static void emit_floating_stack_to_int_reg_now (const char *reg) {
+
+ if (!state->ofp) {
+ return;
+ }
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ fprintf (state->ofp, " sub esp, 4\n");
+ fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " fistp dword [esp]\n" : " fistp dword ptr [esp]\n");
+ fprintf (state->ofp, state->syntax & ASM_SYNTAX_NASM ? " mov %s, dword [esp]\n" : " mov %s, dword ptr [esp]\n", reg);
+ fprintf (state->ofp, " add esp, 4\n");
+
+ } else {
+
+ fprintf (state->ofp, " subl $4, %%esp\n");
+ fprintf (state->ofp, " fistpl (%%esp)\n");
+ fprintf (state->ofp, " movl (%%esp), %%%s\n", reg);
+ fprintf (state->ofp, " addl $4, %%esp\n");
+
+ }
+
+}
+
+static void emit_load_assignment_rhs_expression_to_reg (const char *reg) {
+
+ int false_label;
+ int true_label;
+ int end_label;
+
+ enum token_kind logop;
+
+ if (rhs_current_operand_is_floating_now ()) {
+
+ emit_load_floating_rhs_expression_now (DATA_DOUBLE & 0x1f);
+ emit_floating_stack_to_int_reg_now (reg);
+
+ return;
+
+ }
+
+ emit_load_assignment_compare_expression_to_reg (reg);
+
+ while (tok.kind == TOK_LOGAND || tok.kind == TOK_LOGOR) {
+
+ logop = tok.kind;
+ false_label = anon_label++;
+ true_label = anon_label++;
+ end_label = anon_label++;
+
+ get_token ();
+
+ if (logop == TOK_LOGAND) {
+
+ emit_test_reg_jump_zero_now (reg, false_label);
+ emit_load_assignment_rhs_expression_to_reg (reg);
+ emit_test_reg_jump_zero_now (reg, false_label);
+ emit_mov_imm_to_reg_now (reg, 1);
+ emit_statement_jump (end_label);
+ emit_statement_label (false_label);
+ emit_mov_imm_to_reg_now (reg, 0);
+ emit_statement_label (end_label);
+
+ } else {
+
+ emit_test_reg_jump_nonzero_now (reg, true_label);
+ emit_load_assignment_rhs_expression_to_reg (reg);
+ emit_test_reg_jump_nonzero_now (reg, true_label);
+ emit_mov_imm_to_reg_now (reg, 0);
+ emit_statement_jump (end_label);
+ emit_statement_label (true_label);
+ emit_mov_imm_to_reg_now (reg, 1);
+ emit_statement_label (end_label);
+
+ }
+
+ }
+
+ if (tok.kind == TOK_QMARK) {
+
+ false_label = anon_label++;
+ end_label = anon_label++;
+
+ get_token ();
+
+ if (state->ofp) {
+
+ if (strcmp (reg, "eax") != 0) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " mov eax, %s\n", reg);
+ } else {
+ fprintf (state->ofp, " movl %%%s, %%eax\n", reg);
+ }
+
+ }
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ fprintf (state->ofp, " test eax, eax\n");
+
+ if ((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) {
+ fprintf (state->ofp, " jz L%d\n", false_label);
+ } else {
+ fprintf (state->ofp, " jz .L%d\n", false_label);
+ }
+
+ } else {
+
+ fprintf (state->ofp, " testl %%eax, %%eax\n");
+ fprintf (state->ofp, " jz .L%d\n", false_label);
+
+ }
+
+ }
+
+ emit_load_assignment_rhs_expression_to_reg (reg);
+ expect (TOK_COLON, ":");
+ emit_statement_jump (end_label);
+ emit_statement_label (false_label);
+ emit_load_assignment_rhs_expression_to_reg (reg);
+ emit_statement_label (end_label);
+
+ }
+
+}
+
+static void emit_statement_cmp64_to_eax (enum token_kind op, int is_unsigned);
+
+static int is_assignment64_binary_operator (enum token_kind k) {
+
+ return is_arithmetic_binary_operator (k) ||
+ k == TOK_LESS || k == TOK_LTEQ || k == TOK_GREATER ||
+ k == TOK_GTEQ || k == TOK_EQEQ || k == TOK_NOTEQ ||
+ k == TOK_LOGAND || k == TOK_LOGOR;
+
+}
+
+static void emit_assignment64_bool_result_to_pair_now (const char *lo, const char *hi) {
+
+ if (!state->ofp) {
+ return;
+ }
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ if (strcmp (lo, "eax") != 0) {
+ fprintf (state->ofp, " mov %s, eax\n", lo);
+ }
+
+ fprintf (state->ofp, " xor %s, %s\n", hi, hi);
+
+ } else {
+
+ if (strcmp (lo, "eax") != 0) {
+ fprintf (state->ofp, " movl %%eax, %%%s\n", lo);
+ }
+
+ fprintf (state->ofp, " xorl %%%s, %%%s\n", hi, hi);
+
+ }
+
+}
+
+static void emit_assignment64_logical_op_to_pair_now (enum token_kind op, const char *lo, const char *hi) {
+
+ int true_label;
+ int false_label;
+ int end_label;
+
+ if (!state->ofp) {
+ return;
+ }
+
+ true_label = anon_label++;
+ false_label = anon_label++;
+ end_label = anon_label++;
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ fprintf (state->ofp, " test edx, edx\n");
+ fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? " jnz L%d\n" : " jnz .L%d\n"), op == TOK_LOGOR ? true_label : end_label);
+ fprintf (state->ofp, " test eax, eax\n");
+ fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? " jnz L%d\n" : " jnz .L%d\n"), op == TOK_LOGOR ? true_label : end_label);
+
+ if (op == TOK_LOGAND) {
+
+ fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? " jmp L%d\n" : " jmp .L%d\n"), false_label);
+ fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "L%d:\n" : ".L%d:\n"), end_label);
+ fprintf (state->ofp, " test ecx, ecx\n");
+ fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? " jnz L%d\n" : " jnz .L%d\n"), true_label);
+ fprintf (state->ofp, " test ebx, ebx\n");
+ fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? " jnz L%d\n" : " jnz .L%d\n"), true_label);
+ fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "L%d:\n" : ".L%d:\n"), false_label);
+ fprintf (state->ofp, " xor eax, eax\n");
+ fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? " jmp L%d\n" : " jmp .L%d\n"), end_label + 1);
+ fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "L%d:\n" : ".L%d:\n"), true_label);
+ fprintf (state->ofp, " mov eax, 1\n");
+ fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "L%d:\n" : ".L%d:\n"), end_label + 1);
+
+ } else {
+
+ fprintf (state->ofp, " test ecx, ecx\n");
+ fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? " jnz L%d\n" : " jnz .L%d\n"), true_label);
+ fprintf (state->ofp, " test ebx, ebx\n");
+ fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? " jnz L%d\n" : " jnz .L%d\n"), true_label);
+ fprintf (state->ofp, " xor eax, eax\n");
+ fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? " jmp L%d\n" : " jmp .L%d\n"), end_label);
+ fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "L%d:\n" : ".L%d:\n"), true_label);
+ fprintf (state->ofp, " mov eax, 1\n");
+ fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "L%d:\n" : ".L%d:\n"), end_label);
+
+ }
+
+ } else {
+
+ fprintf (state->ofp, " testl %%edx, %%edx\n");
+ fprintf (state->ofp, op == TOK_LOGOR ? " jnz .L%d\n" : " jnz .L%d\n", op == TOK_LOGOR ? true_label : end_label);
+ fprintf (state->ofp, " testl %%eax, %%eax\n");
+ fprintf (state->ofp, op == TOK_LOGOR ? " jnz .L%d\n" : " jnz .L%d\n", op == TOK_LOGOR ? true_label : end_label);
+
+ if (op == TOK_LOGAND) {
+
+ fprintf (state->ofp, " jmp .L%d\n", false_label);
+ fprintf (state->ofp, ".L%d:\n", end_label);
+ fprintf (state->ofp, " testl %%ecx, %%ecx\n");
+ fprintf (state->ofp, " jnz .L%d\n", true_label);
+ fprintf (state->ofp, " testl %%ebx, %%ebx\n");
+ fprintf (state->ofp, " jnz .L%d\n", true_label);
+ fprintf (state->ofp, ".L%d:\n", false_label);
+ fprintf (state->ofp, " xorl %%eax, %%eax\n");
+ fprintf (state->ofp, " jmp .L%d\n", end_label + 1);
+ fprintf (state->ofp, ".L%d:\n", true_label);
+ fprintf (state->ofp, " movl $1, %%eax\n");
+ fprintf (state->ofp, ".L%d:\n", end_label + 1);
+
+ } else {
+
+ fprintf (state->ofp, " testl %%ecx, %%ecx\n");
+ fprintf (state->ofp, " jnz .L%d\n", true_label);
+ fprintf (state->ofp, " testl %%ebx, %%ebx\n");
+ fprintf (state->ofp, " jnz .L%d\n", true_label);
+ fprintf (state->ofp, " xorl %%eax, %%eax\n");
+ fprintf (state->ofp, " jmp .L%d\n", end_label);
+ fprintf (state->ofp, ".L%d:\n", true_label);
+ fprintf (state->ofp, " movl $1, %%eax\n");
+ fprintf (state->ofp, ".L%d:\n", end_label);
+
+ }
+
+ }
+
+ anon_label++;
+ emit_assignment64_bool_result_to_pair_now (lo, hi);
+
+}
+
+static void emit_assignment64_compare_op_to_pair_now (enum token_kind op, const char *lo, const char *hi, int is_unsigned) {
+
+ emit_statement_cmp64_to_eax (op, is_unsigned);
+ emit_assignment64_bool_result_to_pair_now (lo, hi);
+
+}
+
+static void emit_load_assignment_rhs_expression_to_pair (const char *lo, const char *hi, int is_unsigned) {
+
+ enum token_kind op;
+
+ int result_pair_is_eax_edx = (strcmp (lo, "eax") == 0 && strcmp (hi, "edx") == 0);
+ int current_pair_is_eax_edx = result_pair_is_eax_edx;
+
+ if (const_integer_expr_text_is_foldable_now (tok.start)) {
+
+ int64_s v = const64_from_current_foldable_expr ();
+ emit_load_const64_to_pair_now (lo, hi, v);
+
+ return;
+
+ }
+
+ emit_load_assignment_rhs_to_pair (lo, hi);
+
+ while (is_assignment64_binary_operator (tok.kind)) {
+
+ op = tok.kind;
+ get_token ();
+
+ if (!current_pair_is_eax_edx) {
+
+ emit_mov_reg_to_reg_now ("eax", lo);
+ emit_mov_reg_to_reg_now ("edx", hi);
+
+ current_pair_is_eax_edx = 1;
+
+ }
+
+ emit_preserve_assignment64_regs (op);
+
+ /*
+ * The right operand of a 64-bit shift is a plain integer shift
+ * count, not a 64-bit value. Loading it through the 64-bit primary
+ * path loses precedence for cases such as:
+ *
+ * ((address_type)1) << (CHAR_BIT * rel->howto->size)
+ *
+ * Worse, the RHS loader uses EAX internally for nested expressions;
+ * preserve the 64-bit LHS in EDX:EAX while the count is evaluated
+ * into EBX.
+ */
+ if (op == TOK_LSH || op == TOK_RSH || op == TOK_LSHEQ || op == TOK_RSHEQ) {
+
+ emit_push_reg_now ("eax");
+ emit_push_reg_now ("edx");
+
+ emit_load_assignment_binary_expression_to_reg ("ebx");
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " xor ecx, ecx\n");
+ } else {
+ fprintf (state->ofp, " xorl %%ecx, %%ecx\n");
+ }
+
+ emit_pop_reg_now ("edx");
+ emit_pop_reg_now ("eax");
+
+ } else {
+
+ /*
+ * The generic 64-bit RHS loader uses EAX:EDX as scratch even
+ * when asked to leave the final value in EBX:ECX. Preserve the
+ * left operand around RHS evaluation; otherwise expressions such
+ * as:
+ *
+ * result &= (((address_type)1) << n) - 1
+ *
+ * end up applying the operator to the RHS twice, because the
+ * computed mask clobbers the original result in EAX:EDX.
+ */
+ emit_push_reg_now ("eax");
+ emit_push_reg_now ("edx");
+
+ emit_load_assignment_rhs_to_pair ("ebx", "ecx");
+
+ emit_pop_reg_now ("edx");
+ emit_pop_reg_now ("eax");
+
+ }
+
+ if (op == TOK_LOGAND || op == TOK_LOGOR) {
+
+ emit_assignment64_logical_op_to_pair_now (op, lo, hi);
+ current_pair_is_eax_edx = result_pair_is_eax_edx;
+
+ } else if (is_value_compare_operator (op)) {
+
+ emit_assignment64_compare_op_to_pair_now (op, lo, hi, is_unsigned);
+ current_pair_is_eax_edx = result_pair_is_eax_edx;
+
+ } else {
+
+ emit_assignment_binary_op64 (op, is_unsigned);
+ current_pair_is_eax_edx = 1;
+
+ }
+
+ emit_restore_assignment64_regs (op);
+
+ }
+
+ if (current_pair_is_eax_edx && !result_pair_is_eax_edx) {
+
+ emit_mov_reg_to_reg_now (lo, "eax");
+ emit_mov_reg_to_reg_now (hi, "edx");
+
+ current_pair_is_eax_edx = 0;
+
+ }
+
+ if (tok.kind == TOK_QMARK) {
+
+ int false_label = anon_label++;
+ int end_label = anon_label++;
+
+ get_token ();
+
+ if (state->ofp) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ fprintf (state->ofp, " test %s, %s\n", hi, hi);
+ fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? " jnz L%d\n" : " jnz .L%d\n"), false_label + 2);
+ fprintf (state->ofp, " test %s, %s\n", lo, lo);
+ fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? " jz L%d\n" : " jz .L%d\n"), false_label);
+ fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "L%d:\n" : ".L%d:\n"), false_label + 2);
+
+ } else {
+
+ fprintf (state->ofp, " testl %%%s, %%%s\n", hi, hi);
+ fprintf (state->ofp, " jnz .L%d\n", false_label + 2);
+ fprintf (state->ofp, " testl %%%s, %%%s\n", lo, lo);
+ fprintf (state->ofp, " jz .L%d\n", false_label);
+ fprintf (state->ofp, ".L%d:\n", false_label + 2);
+
+ }
+
+ }
+
+ anon_label++;
+
+ emit_load_assignment_rhs_expression_to_pair (lo, hi, is_unsigned);
+ expect (TOK_COLON, ":");
+
+ emit_statement_jump (end_label);
+ emit_statement_label (false_label);
+ emit_load_assignment_rhs_expression_to_pair (lo, hi, is_unsigned);
+ emit_statement_label (end_label);
+
+ }
+
+}
+
+static void emit_incdec_integral_symbol_now (struct local_symbol *sym, const char *name, int size, enum token_kind op) {
+
+ char memref[64];
+ char nasm_memref[256];
+
+ const char *symbol;
+ const char *mnemonic;
+
+ if (!state->ofp) {
+ return;
+ }
+
+ if (size == (DATA_LLONG & 0x1f)) {
+
+ if (sym) {
+
+ if (sym->is_static && sym->static_label) {
+ emit_load_global64_to_pair ("eax", "edx", sym->static_label);
+ } else {
+ emit_load_local64_to_pair (sym->offset, "eax", "edx");
+ }
+
+ } else {
+ emit_load_global64_to_pair ("eax", "edx", name);
+ }
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ if (op == TOK_INCR) {
+
+ fprintf (state->ofp, " add eax, 1\n");
+ fprintf (state->ofp, " adc edx, 0\n");
+
+ } else {
+
+ fprintf (state->ofp, " sub eax, 1\n");
+ fprintf (state->ofp, " sbb edx, 0\n");
+
+ }
+
+ } else {
+
+ if (op == TOK_INCR) {
+
+ fprintf (state->ofp, " addl $1, %%eax\n");
+ fprintf (state->ofp, " adcl $0, %%edx\n");
+
+ } else {
+
+ fprintf (state->ofp, " subl $1, %%eax\n");
+ fprintf (state->ofp, " sbbl $0, %%edx\n");
+
+ }
+
+ }
+
+ if (sym) {
+
+ if (sym->is_static && sym->static_label) {
+ emit_store_pair_to_global64 (sym->static_label, "eax", "edx");
+ } else {
+ emit_store_pair_to_local64 (sym->offset, "eax", "edx");
+ }
+
+ } else {
+ emit_store_pair_to_global64 (name, "eax", "edx");
+ }
+
+ return;
+
+ }
+
+ if (size != (DATA_CHAR & 0x1f) && size != (DATA_SHORT & 0x1f) && size != (DATA_INT & 0x1f) && size != DATA_PTR) {
+ return;
+ }
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ mnemonic = op == TOK_INCR ? "add" : "sub";
+
+ if (sym && !sym->is_static) {
+
+ format_intel_ebp_offset (memref, sizeof (memref), sym->offset);
+ symbol = memref;
+
+ } else if (sym && sym->static_label) {
+ symbol = asm_global_symbol_name (sym->static_label);
+ } else {
+ symbol = asm_global_symbol_name (name);
+ }
+
+ if (state->syntax & ASM_SYNTAX_NASM) {
+ symbol = format_nasm_memory_operand (nasm_memref, sizeof (nasm_memref), symbol);
+ }
+
+ if (size == (DATA_CHAR & 0x1f)) {
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " %s byte %s, 1\n" : " %s byte ptr %s, 1\n"), mnemonic, symbol);
+ } else if (size == (DATA_SHORT & 0x1f)) {
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " %s word %s, 1\n" : " %s word ptr %s, 1\n"), mnemonic, symbol);
+ } else {
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " %s dword %s, 1\n" : " %s dword ptr %s, 1\n"), mnemonic, symbol);
+ }
+
+ } else {
+
+ if (sym && !sym->is_static) {
+
+ if (size == (DATA_CHAR & 0x1f)) {
+ fprintf (state->ofp, op == TOK_INCR ? " incb %ld(%%ebp)\n" : " decb %ld(%%ebp)\n", sym->offset);
+ } else if (size == (DATA_SHORT & 0x1f)) {
+ fprintf (state->ofp, op == TOK_INCR ? " incw %ld(%%ebp)\n" : " decw %ld(%%ebp)\n", sym->offset);
+ } else {
+ fprintf (state->ofp, op == TOK_INCR ? " incl %ld(%%ebp)\n" : " decl %ld(%%ebp)\n", sym->offset);
+ }
+
+ } else {
+
+ symbol = asm_global_symbol_name ((sym && sym->static_label) ? sym->static_label : name);
+
+ if (size == (DATA_CHAR & 0x1f)) {
+ fprintf (state->ofp, op == TOK_INCR ? " incb %s\n" : " decb %s\n", symbol);
+ } else if (size == (DATA_SHORT & 0x1f)) {
+ fprintf (state->ofp, op == TOK_INCR ? " incw %s\n" : " decw %s\n", symbol);
+ } else {
+ fprintf (state->ofp, op == TOK_INCR ? " incl %s\n" : " decl %s\n", symbol);
+ }
+
+ }
+
+ }
+
+}
+
+static void emit_incdec_pointer_symbol_now (struct local_symbol *sym, const char *name, enum token_kind op, int step) {
+
+ const char *mnemonic = op == TOK_INCR ? "add" : "sub";
+ const char *symbol;
+
+ char memref[64];
+ char nasm_memref[128];
+
+ if (step <= 0) {
+ step = 1;
+ }
+
+ if (!state->ofp) {
+ return;
+ }
+
+ if (step == 1) {
+
+ emit_incdec_integral_symbol_now (sym, name, DATA_PTR, op);
+ return;
+
+ }
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ if (sym && !sym->is_static) {
+
+ format_intel_ebp_offset (memref, sizeof (memref), sym->offset);
+ symbol = memref;
+
+ } else if (sym && sym->static_label) {
+ symbol = asm_global_symbol_name (sym->static_label);
+ } else {
+ symbol = asm_global_symbol_name (name);
+ }
+
+ if (state->syntax & ASM_SYNTAX_NASM) {
+
+ symbol = format_nasm_memory_operand (nasm_memref, sizeof (nasm_memref), symbol);
+ fprintf (state->ofp, " %s dword %s, %d\n", mnemonic, symbol, step);
+
+ } else {
+ fprintf (state->ofp, " %s dword ptr %s, %d\n", mnemonic, symbol, step);
+ }
+
+ } else {
+
+ if (sym && !sym->is_static) {
+ fprintf (state->ofp, " %sl $%d, %ld(%%ebp)\n", mnemonic, step, sym->offset);
+ } else {
+
+ symbol = asm_global_symbol_name ((sym && sym->static_label) ? sym->static_label : name);
+ fprintf (state->ofp, " %sl $%d, %s\n", mnemonic, step, symbol);
+
+ }
+
+ }
+
+}
+
+static void emit_incdec_symbol_now (struct local_symbol *sym, const char *name, enum token_kind op, int line, const char *start, const char *caret) {
+
+ int global_index;
+ int is_floating;
+ int pointer_depth;
+ int pointed_size;
+
+ int size;
+
+ if (!sym) {
+
+ global_index = find_global_symbol (name);
+
+ if (global_index < 0) {
+
+ report_line_at (get_filename (), line, REPORT_ERROR, start, caret, "unknown symbol '%s'", name);
+ return;
+
+ }
+
+ if (get_global_symbol_kind (name) == GLOBAL_SYMBOL_FUNCTION) {
+
+ report_line_at (get_filename (), line, REPORT_ERROR, start, caret, "function '%s' cannot be incremented or decremented", name);
+ return;
+
+ }
+
+ }
+
+ size = sym ? sym->size : get_global_symbol_size (name);
+ is_floating = sym ? sym->is_floating : get_global_symbol_floating (name);
+
+ pointer_depth = sym ? sym->pointer_depth : get_global_symbol_pointer_depth (name);
+ pointed_size = sym ? sym->pointed_size : get_global_symbol_pointed_size (name);
+
+ if (!state->ofp) {
+ return;
+ }
+
+ if (is_floating) {
+
+ emit_load_floating_symbol_now (sym, name, size);
+ fprintf (state->ofp, " fld1\n");
+
+ if (op == TOK_INCR) {
+ emit_floating_binary_now (TOK_PLUS);
+ } else {
+ emit_floating_binary_now (TOK_MINUS);
+ }
+
+ emit_store_floating_symbol_now (sym, name, size);
+ return;
+
+ }
+
+ if (pointer_depth > 0) {
+
+ emit_incdec_pointer_symbol_now (sym, name, op, pointed_size);
+ return;
+
+ }
+
+ emit_incdec_integral_symbol_now (sym, name, size, op);
+
+}
+
+static int parse_prefix_incdec_statement (void) {
+
+ enum token_kind op;
+ char *name;
+
+ const char *name_start, *name_caret;
+ unsigned long name_line;
+
+ struct local_symbol *sym;
+
+ int deref_size = DATA_INT & 0x1f;
+ int indirect = 0;
+
+ if (emit_load_prefix_incdec_parenthesized_deref_to_reg_now ("eax")) {
+
+ expect_semi_or_recover ();
+ return 1;
+
+ }
+
+ if (tok.kind != TOK_INCR && tok.kind != TOK_DECR) {
+ return 0;
+ }
+
+ op = tok.kind;
+ get_token ();
+
+ if (tok.kind == TOK_STAR) {
+
+ indirect = 1;
+ get_token ();
+
+ }
+
+ if (tok.kind != TOK_IDENT) {
+
+ if (indirect) {
+ report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "expected identifier after *");
+ } else {
+ report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "expected identifier after %s", op == TOK_INCR ? "++" : "--");
+ }
+
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+ expect_semi_or_recover ();
+
+ return 1;
+
+ }
+
+ name = xstrdup (tok.ident);
+ name_start = tok.start;
+ name_caret = tok.caret;
+ name_line = get_line_number ();
+
+ get_token ();
+
+ sym = find_local_symbol (name);
+
+ if (indirect) {
+
+ if (sym) {
+
+ if (sym->pointer_depth > 1) {
+ deref_size = DATA_PTR & 0x1f;
+ } else if (sym->pointer_depth == 1 && sym->pointed_size > 0) {
+ deref_size = sym->pointed_size & 0x1f;
+ }
+
+ if (sym->is_static && sym->static_label) {
+ emit_load_global_to_reg ("edx", sym->static_label, DATA_PTR);
+ } else {
+ emit_load_local_to_reg ("edx", sym->offset, DATA_PTR);
+ }
+
+ } else if (find_global_symbol (name) >= 0) {
+
+ if (get_global_symbol_pointer_depth (name) > 1) {
+ deref_size = DATA_PTR & 0x1f;
+ } else if (get_global_symbol_pointer_depth (name) == 1 && get_global_symbol_pointed_size (name) > 0) {
+ deref_size = get_global_symbol_pointed_size (name) & 0x1f;
+ }
+
+ emit_load_global_to_reg ("edx", name, DATA_PTR);
+
+ } else {
+
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+ expect_semi_or_recover ();
+
+ free (name);
+ return 1;
+
+ }
+
+ emit_push_reg_now ("edx");
+ emit_load_member_from_addr_reg_now ("eax", "edx", 0, deref_size);
+
+ if (state->ofp) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, op == TOK_INCR ? " add eax, 1\n" : " sub eax, 1\n");
+ } else {
+ fprintf (state->ofp, op == TOK_INCR ? " addl $1, %%eax\n" : " subl $1, %%eax\n");
+ }
+
+ }
+
+ emit_pop_reg_now ("edx");
+ emit_store_reg_to_deref_reg_now ("edx", "eax", deref_size);
+
+ expect_semi_or_recover ();
+
+ free (name);
+ return 1;
+
+ }
+
+ emit_incdec_symbol_now (sym, name, op, name_line, name_start, name_caret);
+
+ expect_semi_or_recover ();
+ free (name);
+
+ return 1;
+
+}
+
+static int parse_parenthesized_pointer_member_indirect_assignment_statement (void) {
+
+ char *name = 0;
+ char *member = 0;
+
+ struct local_symbol *sym;
+
+ int global_index;
+ int parens = 0;
+ int offset = 0;
+ int member_size = DATA_PTR & 0x1f;
+ int member_elem_size = DATA_INT & 0x1f;
+ int member_pointer_depth = 0;
+ int deref_size = DATA_INT & 0x1f;
+ int step = 1;
+
+ enum token_kind postfix_op = TOK_EOF;
+ enum token_kind op;
+
+ const char *name_start;
+ const char *name_caret;
+ const char *member_start;
+ const char *member_caret;
+
+ unsigned long name_line;
+ unsigned long member_line;
+
+ if (tok.kind != TOK_LPAREN) {
+ return 0;
+ }
+
+ while (tok.kind == TOK_LPAREN) {
+ ++parens;
+ get_token ();
+ }
+
+ if (tok.kind != TOK_STAR) {
+
+ if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) {
+
+ enum token_kind prefix_op = tok.kind;
+ char *prefix_name;
+
+ const char *prefix_start;
+ const char *prefix_caret;
+
+ unsigned long prefix_line;
+
+ struct local_symbol *prefix_sym;
+
+ int prefix_global_index;
+ int prefix_deref_size = DATA_INT & 0x1f;
+
+ enum token_kind prefix_assign_op;
+ get_token ();
+
+ if (tok.kind != TOK_IDENT) {
+
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+
+ expect_semi_or_recover ();
+ return 1;
+
+ }
+
+ prefix_name = xstrdup (tok.ident);
+ prefix_start = tok.start;
+ prefix_caret = tok.caret;
+ prefix_line = get_line_number ();
+
+ get_token ();
+
+ while (tok.kind == TOK_RPAREN && parens > 0) {
+
+ --parens;
+ get_token ();
+
+ }
+
+ if (parens != 0 || !is_assignment_operator (tok.kind)) {
+
+ free (prefix_name);
+
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+ expect_semi_or_recover ();
+
+ return 1;
+
+ }
+
+ prefix_assign_op = tok.kind;
+ get_token ();
+
+ prefix_sym = find_local_symbol (prefix_name);
+ prefix_global_index = find_global_symbol (prefix_name);
+
+ if (!prefix_sym && prefix_global_index < 0) {
+
+ report_line_at (get_filename (), prefix_line, REPORT_ERROR, prefix_start, prefix_caret, "unknown symbol '%s'", prefix_name);
+
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+ expect_semi_or_recover ();
+
+ free (prefix_name);
+ return 1;
+
+ }
+
+ if (prefix_sym) {
+
+ if (prefix_sym->pointer_depth > 1) {
+ prefix_deref_size = DATA_PTR & 0x1f;
+ } else if (prefix_sym->pointer_depth == 1 && prefix_sym->pointed_size > 0) {
+ prefix_deref_size = prefix_sym->pointed_size & 0x1f;
+ }
+
+ } else {
+
+ if (get_global_symbol_pointer_depth (prefix_name) > 1) {
+ prefix_deref_size = DATA_PTR & 0x1f;
+ } else if (get_global_symbol_pointer_depth (prefix_name) == 1 && get_global_symbol_pointed_size (prefix_name) > 0) {
+ prefix_deref_size = get_global_symbol_pointed_size (prefix_name) & 0x1f;
+ }
+
+ }
+
+ if (prefix_deref_size == 0) {
+ prefix_deref_size = DATA_INT & 0x1f;
+ }
+
+ emit_incdec_symbol_now (prefix_sym, prefix_name, prefix_op, prefix_line, prefix_start, prefix_caret);
+
+ if (state->ofp) {
+
+ if (prefix_sym) {
+
+ if (prefix_sym->is_static && prefix_sym->static_label) {
+ emit_load_global_to_reg ("ecx", prefix_sym->static_label, DATA_PTR);
+ } else {
+ emit_load_local_to_reg ("ecx", prefix_sym->offset, DATA_PTR);
+ }
+
+ } else {
+ emit_load_global_to_reg ("ecx", prefix_name, DATA_PTR);
+ }
+
+ emit_push_reg_now ("ecx");
+
+ if (prefix_assign_op == TOK_ASSIGN) {
+ emit_load_assignment_rhs_expression_to_reg ("eax");
+ } else {
+
+ emit_pop_reg_now ("edx");
+ emit_push_reg_now ("edx");
+
+ emit_load_member_from_addr_reg_now ("eax", "edx", 0, prefix_deref_size);
+ emit_push_reg_now ("eax");
+
+ emit_load_assignment_rhs_expression_to_reg ("edx");
+ emit_pop_reg_now ("eax");
+
+ emit_assignment_binary_op (prefix_assign_op, 0);
+
+ }
+
+ emit_pop_reg_now ("edx");
+ emit_store_reg_to_deref_reg_now ("edx", "eax", prefix_deref_size);
+
+ } else {
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+ }
+
+ expect_semi_or_recover ();
+
+ free (prefix_name);
+ return 1;
+
+ }
+
+ if (tok.kind == TOK_IDENT) {
+
+ name = xstrdup (tok.ident);
+ name_start = tok.start;
+ name_caret = tok.caret;
+ name_line = get_line_number ();
+
+ get_token ();
+
+ if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) {
+
+ postfix_op = tok.kind;
+ get_token ();
+
+ }
+
+ while (tok.kind == TOK_RPAREN && parens > 0) {
+
+ --parens;
+ get_token ();
+
+ }
+
+ if (parens != 0 || !is_assignment_operator (tok.kind)) {
+
+ free (name);
+
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+ expect_semi_or_recover ();
+
+ return 1;
+
+ }
+
+ op = tok.kind;
+ get_token ();
+
+ sym = find_local_symbol (name);
+ global_index = find_global_symbol (name);
+
+ if (!sym && global_index < 0) {
+
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+ expect_semi_or_recover ();
+
+ free (name);
+ return 1;
+
+ }
+
+ if (sym) {
+
+ if (sym->pointer_depth > 1) {
+
+ deref_size = DATA_PTR & 0x1f;
+ step = DATA_PTR & 0x1f;
+
+ } else if (sym->pointer_depth == 1) {
+
+ deref_size = sym->pointed_size & 0x1f;
+ step = sym->pointed_size > 0 ? sym->pointed_size : 1;
+
+ }
+
+ } else {
+
+ if (get_global_symbol_pointer_depth (name) > 1) {
+
+ deref_size = DATA_PTR & 0x1f;
+ step = DATA_PTR & 0x1f;
+
+ } else if (get_global_symbol_pointer_depth (name) == 1) {
+
+ deref_size = get_global_symbol_pointed_size (name) & 0x1f;
+ step = get_global_symbol_pointed_size (name) > 0 ? get_global_symbol_pointed_size (name) : 1;
+
+ }
+
+ }
+
+ if (deref_size == 0) {
+ deref_size = DATA_INT & 0x1f;
+ }
+
+ if (state->ofp) {
+
+ if (sym) {
+
+ if (sym->is_static && sym->static_label) {
+ emit_load_global_to_reg ("ecx", sym->static_label, DATA_PTR);
+ } else {
+ emit_load_local_to_reg ("ecx", sym->offset, DATA_PTR);
+ }
+
+ } else {
+ emit_load_global_to_reg ("ecx", name, DATA_PTR);
+ }
+
+ emit_push_reg_now ("ecx");
+
+ if (postfix_op == TOK_INCR || postfix_op == TOK_DECR) {
+
+ if (sym) {
+
+ if (sym->is_static && sym->static_label) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " %s dword [%s], %d\n" : " %s dword ptr [%s], %d\n"), postfix_op == TOK_INCR ? "add" : "sub", sym->static_label, step);
+ } else {
+ fprintf (state->ofp, " %sl $%d, %s\n", postfix_op == TOK_INCR ? "add" : "sub", step, sym->static_label);
+ }
+
+ } else {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ char memref[64];
+
+ format_intel_ebp_offset (memref, sizeof (memref), sym->offset);
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " %s dword %s, %d\n" : " %s dword ptr %s, %d\n"), postfix_op == TOK_INCR ? "add" : "sub", memref, step);
+
+ } else {
+ fprintf (state->ofp, " %sl $%d, %ld(%%ebp)\n", postfix_op == TOK_INCR ? "add" : "sub", step, sym->offset);
+ }
+
+ }
+
+ } else {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " %s dword [%s], %d\n" : " %s dword ptr [%s], %d\n"), postfix_op == TOK_INCR ? "add" : "sub", name, step);
+ } else {
+ fprintf (state->ofp, " %sl $%d, %s\n", postfix_op == TOK_INCR ? "add" : "sub", step, name);
+ }
+
+ }
+
+ }
+
+ if (op == TOK_ASSIGN) {
+ emit_load_assignment_rhs_expression_to_reg ("eax");
+ } else {
+
+ emit_pop_reg_now ("edx");
+ emit_push_reg_now ("edx");
+ emit_load_member_from_addr_reg_now ("eax", "edx", 0, deref_size);
+ emit_push_reg_now ("eax");
+ emit_load_assignment_rhs_expression_to_reg ("edx");
+ emit_pop_reg_now ("eax");
+ emit_assignment_binary_op (op, 0);
+
+ }
+
+ emit_pop_reg_now ("edx");
+ emit_store_reg_to_deref_reg_now ("edx", "eax", deref_size);
+
+ } else {
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+ }
+
+ expect_semi_or_recover ();
+
+ free (name);
+ return 1;
+
+ }
+
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+
+ expect_semi_or_recover ();
+ return 1;
+
+ }
+
+ get_token ();
+
+ while (tok.kind == TOK_LPAREN) {
+
+ ++parens;
+ get_token ();
+
+ }
+
+ if (tok.kind != TOK_IDENT) {
+
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+
+ expect_semi_or_recover ();
+ return 1;
+
+ }
+
+ name = xstrdup (tok.ident);
+ name_start = tok.start;
+ name_caret = tok.caret;
+ name_line = get_line_number ();
+
+ get_token ();
+
+ if (state->ofp) {
+
+ if (tok.kind == TOK_LPAREN) {
+ emit_call_identifier_to_reg_now (name, "eax", name_start, name_caret, name_line);
+ } else {
+
+ sym = find_local_symbol (name);
+ global_index = find_global_symbol (name);
+
+ if (sym) {
+
+ if (sym->is_static && sym->static_label) {
+ emit_load_global_to_reg ("eax", sym->static_label, DATA_PTR);
+ } else {
+ emit_load_local_to_reg ("eax", sym->offset, DATA_PTR);
+ }
+
+ } else if (global_index >= 0) {
+ emit_load_global_to_reg ("eax", name, DATA_PTR);
+ } else {
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+ }
+
+ }
+
+ emit_load_deref_reg_now ("eax", DATA_PTR);
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " mov edx, eax\n");
+ } else {
+ fprintf (state->ofp, " movl %%eax, %%edx\n");
+ }
+
+ } else if (tok.kind == TOK_LPAREN) {
+ emit_call_identifier_to_reg_now (name, "eax", name_start, name_caret, name_line);
+ }
+
+ while (tok.kind == TOK_RPAREN && parens > 0) {
+
+ --parens;
+ get_token ();
+
+ }
+
+ if (tok.kind != TOK_ARROW && tok.kind != TOK_DOT) {
+
+ if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) {
+
+ postfix_op = tok.kind;
+ get_token ();
+
+ sym = find_local_symbol (name);
+ global_index = find_global_symbol (name);
+
+ if (sym) {
+
+ if (sym->pointer_depth > 1) {
+
+ deref_size = DATA_PTR & 0x1f;
+ step = DATA_PTR & 0x1f;
+
+ } else if (sym->pointer_depth == 1) {
+
+ deref_size = sym->pointed_size & 0x1f;
+ step = 1;
+
+ }
+
+ if (deref_size == 0) {
+ deref_size = DATA_INT & 0x1f;
+ }
+
+ if (sym->is_static && sym->static_label) {
+ emit_load_global_to_reg ("edx", sym->static_label, DATA_PTR);
+ } else {
+ emit_load_local_to_reg ("edx", sym->offset, DATA_PTR);
+ }
+
+ } else if (global_index >= 0) {
+
+ if (get_global_symbol_pointer_depth (name) > 1) {
+
+ deref_size = DATA_PTR & 0x1f;
+ step = DATA_PTR & 0x1f;
+
+ } else if (get_global_symbol_pointer_depth (name) == 1) {
+
+ deref_size = get_global_symbol_pointed_size (name) & 0x1f;
+ step = 1;
+
+ }
+
+ if (deref_size == 0) {
+ deref_size = DATA_INT & 0x1f;
+ }
+
+ emit_load_global_to_reg ("edx", name, DATA_PTR);
+
+ } else {
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+ }
+
+ if (state->ofp) {
+
+ emit_load_deref_reg_now ("edx", deref_size);
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " %s edx, %d\n", postfix_op == TOK_INCR ? "add" : "sub", step);
+ } else {
+ fprintf (state->ofp, " %sl $%d, %%edx\n", postfix_op == TOK_INCR ? "add" : "sub", step);
+ }
+
+ if (sym) {
+
+ if (sym->is_static && sym->static_label) {
+ emit_load_global_to_reg ("eax", sym->static_label, DATA_PTR);
+ } else {
+ emit_load_local_to_reg ("eax", sym->offset, DATA_PTR);
+ }
+
+ } else if (global_index >= 0) {
+ emit_load_global_to_reg ("eax", name, DATA_PTR);
+ }
+
+ emit_store_reg_to_deref_reg_now ("eax", "edx", deref_size);
+
+ }
+
+ expect_semi_or_recover ();
+
+ free (name);
+ return 1;
+
+ }
+
+ free (name);
+
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+ expect_semi_or_recover ();
+
+ return 1;
+
+ }
+
+ get_token ();
+
+ member_start = tok.start;
+ member_caret = tok.caret;
+ member_line = get_line_number ();
+
+ if (tok.kind != TOK_IDENT) {
+
+ report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "expected member name");
+ free (name);
+
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+
+ expect_semi_or_recover ();
+ return 1;
+
+ }
+
+ member = xstrdup (tok.ident);
+ get_token ();
+
+ if (!find_member_info_ex (member, &offset, &member_size, &member_elem_size, &member_pointer_depth, 0, 0)) {
+
+ report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "unknown member '%s'", member);
+
+ free (member);
+ free (name);
+
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+ expect_semi_or_recover ();
+
+ return 1;
+
+ }
+
+ free (member);
+
+ if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) {
+
+ postfix_op = tok.kind;
+ get_token ();
+
+ }
+
+ if (!is_assignment_operator (tok.kind)) {
+
+ free (name);
+
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+ expect_semi_or_recover ();
+
+ return 1;
+
+ }
+
+ op = tok.kind;
+ get_token ();
+
+ if (member_pointer_depth > 1) {
+ deref_size = DATA_PTR & 0x1f;
+ } else if (member_pointer_depth == 1 && member_elem_size > 0) {
+ deref_size = member_elem_size & 0x1f;
+ } else if (member_size > 0) {
+ deref_size = member_size & 0x1f;
+ }
+
+ if (deref_size == 0) {
+ deref_size = DATA_INT & 0x1f;
+ }
+
+ step = member_elem_size > 0 ? member_elem_size : 1;
+
+ if (state->ofp) {
+
+ emit_load_member_from_addr_reg_now ("ecx", "edx", offset, DATA_PTR & 0x1f);
+
+ if (postfix_op == TOK_INCR || postfix_op == TOK_DECR) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ if (state->syntax & ASM_SYNTAX_NASM) {
+ fprintf (state->ofp, " %s dword [edx + %d], %d\n", postfix_op == TOK_INCR ? "add" : "sub", offset, step);
+ } else {
+ fprintf (state->ofp, " %s dword ptr [edx + %d], %d\n", postfix_op == TOK_INCR ? "add" : "sub", offset, step);
+ }
+
+ } else {
+ fprintf (state->ofp, " %sl $%d, %d(%%edx)\n", postfix_op == TOK_INCR ? "add" : "sub", step, offset);
+ }
+
+ }
+
+ emit_push_reg_now ("ecx");
+
+ if (op == TOK_ASSIGN) {
+ emit_load_assignment_rhs_expression_to_reg ("eax");
+ } else {
+
+ emit_load_member_from_addr_reg_now ("eax", "edx", 0, deref_size);
+ emit_push_reg_now ("eax");
+ emit_load_assignment_rhs_expression_to_reg ("edx");
+ emit_pop_reg_now ("eax");
+ emit_assignment_binary_op (op, 0);
+
+ }
+
+ emit_pop_reg_now ("edx");
+ emit_store_reg_to_deref_reg_now ("edx", "eax", deref_size);
+
+ } else {
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+ }
+
+ expect_semi_or_recover ();
+
+ free (name);
+ return 1;
+
+}
+
+static int paren_text_starts_type_name_now (void) {
+
+ const char *p;
+
+ char name[128];
+ int n = 0;
+
+ if (tok.caret) {
+ p = tok.caret;
+ } else if (tok.start) {
+ p = tok.start;
+ } else {
+ return 0;
+ }
+
+ if (*p != '(') {
+ return 0;
+ }
+
+ p++;
+
+ while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+ p++;
+ }
+
+ if (*p == '(') {
+
+ p++;
+
+ while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+ p++;
+ }
+
+ }
+
+ if (!((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || *p == '_')) {
+ return 0;
+ }
+
+ while (((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || (*p >= '0' && *p <= '9') || *p == '_') && n < (int) sizeof (name) - 1) {
+ name[n++] = *p++;
+ }
+
+ name[n] = '\0';
+
+ if (strcmp (name, "char") == 0 || strcmp (name, "short") == 0 ||
+ strcmp (name, "int") == 0 || strcmp (name, "long") == 0 ||
+ strcmp (name, "signed") == 0 || strcmp (name, "unsigned") == 0 ||
+ strcmp (name, "void") == 0 || strcmp (name, "struct") == 0 ||
+ strcmp (name, "union") == 0 || strcmp (name, "enum") == 0) {
+ return 1;
+ }
+
+ return find_typedef_name (name) != 0;
+
+}
+
+static int parse_cast_indirect_assignment_statement (void) {
+
+ int saved_type_size = parsed_type_size;
+ int saved_storage_class = parsed_storage_class;
+ int saved_is_aggregate = parsed_type_is_aggregate;
+ int saved_is_void = parsed_type_is_void;
+ int saved_is_unsigned = parsed_type_is_unsigned;
+ int saved_is_floating = parsed_type_is_floating;
+ int saved_has_tag = parsed_type_has_tag;
+ int saved_is_inline = parsed_type_is_inline;
+ int saved_field_count = parsed_field_count;
+ int saved_fields[MAX_AGG_FIELDS];
+ int saved_declarator_is_pointer = declarator_is_pointer;
+ int saved_declarator_pointer_depth = declarator_pointer_depth;
+ int saved_declarator_has_array = declarator_has_array;
+ int saved_declarator_has_function = declarator_has_function;
+ int saved_declarator_array_unsized = declarator_array_unsized;
+ long saved_declarator_array_count = declarator_array_count;
+
+ char *cast_name = 0;
+ int base_size;
+ int deref_size;
+ int pointer_depth;
+ int i;
+
+ enum token_kind op;
+ int has_outer_paren = 0;
+
+ if (tok.kind != TOK_LPAREN) {
+ return 0;
+ }
+
+ get_token ();
+
+ /*
+ * Accept the common casted-dereference lvalue spelling:
+ *
+ * *((char *)ptr + n) = v;
+ *
+ * parse_indirect_assignment_statement() enters here at the outer '(';
+ * the actual cast type begins after the inner '('.
+ */
+ if (tok.kind == TOK_LPAREN) {
+
+ has_outer_paren = 1;
+ get_token ();
+
+ }
+
+ if (!is_type_start (tok.kind)) {
+ return 0;
+ }
+
+ for (i = 0; i < saved_field_count && i < MAX_AGG_FIELDS; i++) {
+ saved_fields[i] = parsed_field_sizes[i];
+ }
+
+ parse_type_spec ();
+ base_size = parsed_type_size & 0x1f;
+
+ if (tok.kind != TOK_RPAREN) {
+ parse_declarator (&cast_name);
+ }
+
+ pointer_depth = declarator_is_pointer ? (declarator_pointer_depth > 0 ? declarator_pointer_depth : 1) : 0;
+ deref_size = pointer_depth > 1 ? (DATA_PTR & 0x1f) : base_size;
+
+ if (deref_size <= 0) {
+ deref_size = DATA_INT & 0x1f;
+ }
+
+ if (cast_name) {
+ free (cast_name);
+ }
+
+ expect (TOK_RPAREN, ")");
+
+ parsed_type_size = saved_type_size;
+ parsed_storage_class = saved_storage_class;
+ parsed_type_is_aggregate = saved_is_aggregate;
+ parsed_type_is_void = saved_is_void;
+ parsed_type_is_unsigned = saved_is_unsigned;
+ parsed_type_is_floating = saved_is_floating;
+ parsed_type_has_tag = saved_has_tag;
+ parsed_type_is_inline = saved_is_inline;
+
+ clear_parsed_fields ();
+
+ for (i = 0; i < saved_field_count && i < MAX_AGG_FIELDS; i++) {
+ parsed_field_sizes[i] = saved_fields[i];
+ }
+
+ parsed_field_count = saved_field_count;
+
+ declarator_is_pointer = saved_declarator_is_pointer;
+ declarator_pointer_depth = saved_declarator_pointer_depth;
+ declarator_has_array = saved_declarator_has_array;
+ declarator_has_function = saved_declarator_has_function;
+ declarator_array_unsized = saved_declarator_array_unsized;
+ declarator_array_count = saved_declarator_array_count;
+
+ if (state->ofp) {
+
+ /*
+ * Parse the address expression inside the outer dereference without
+ * allowing the first operand parser to consume the assignment operator.
+ *
+ * For:
+ *
+ * *(size_t *)ptr = size;
+ *
+ * emit_load_assignment_rhs_to_reg() sees the identifier "ptr" followed
+ * by '=' and treats it as an assignment expression, generating
+ * "ptr = size" instead of using ptr as the destination address. Load a
+ * plain identifier operand directly, then let the explicit '+'/'-' loop
+ * below handle the casted pointer arithmetic case:
+ *
+ * *((char *)ptr + *actualRead) = '\n';
+ */
+ if (tok.kind == TOK_IDENT) {
+
+ char *addr_name = xstrdup (tok.ident);
+
+ const char *addr_start = tok.start;
+ const char *addr_caret = tok.caret;
+
+ unsigned long addr_line = get_line_number ();
+
+ struct local_symbol *addr_sym;
+ int addr_global_index;
+
+ get_token ();
+
+ addr_sym = find_local_symbol (addr_name);
+ addr_global_index = find_global_symbol (addr_name);
+
+ if (addr_sym) {
+
+ if (addr_sym->is_static && addr_sym->static_label) {
+ emit_load_global_to_reg ("edx", addr_sym->static_label, DATA_PTR);
+ } else {
+ emit_load_local_to_reg ("edx", addr_sym->offset, DATA_PTR);
+ }
+
+ } else if (addr_global_index >= 0) {
+ emit_load_global_to_reg ("edx", addr_name, DATA_PTR);
+ } else {
+ report_line_at (get_filename (), addr_line, REPORT_ERROR, addr_start, addr_caret, "unknown symbol '%s'", addr_name);
+ }
+
+ free (addr_name);
+
+ } else {
+ emit_load_assignment_rhs_to_reg ("edx");
+ }
+
+ while (tok.kind == TOK_PLUS || tok.kind == TOK_MINUS) {
+
+ enum token_kind addr_op = tok.kind;
+ get_token ();
+
+ emit_push_reg_now ("edx");
+ emit_load_assignment_rhs_to_reg ("eax");
+
+ if (deref_size > 1) {
+ emit_scale_reg_by_const_now ("eax", deref_size);
+ }
+
+ emit_pop_reg_now ("edx");
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " %s edx, eax\n", addr_op == TOK_PLUS ? "add" : "sub");
+ } else {
+ fprintf (state->ofp, " %sl %%eax, %%edx\n", addr_op == TOK_PLUS ? "add" : "sub");
+ }
+
+ }
+
+ } else {
+ skip_balanced_until (TOK_RPAREN, TOK_SEMI, TOK_EOF);
+ }
+
+ if (has_outer_paren) {
+ expect (TOK_RPAREN, ")");
+ }
+
+ if (!is_assignment_operator (tok.kind)) {
+
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+
+ expect_semi_or_recover ();
+ return 1;
+
+ }
+
+ op = tok.kind;
+ get_token ();
+
+ if (op == TOK_ASSIGN) {
+
+ emit_push_reg_now ("edx");
+ emit_load_assignment_rhs_expression_to_reg ("eax");
+ emit_pop_reg_now ("edx");
+
+ } else {
+
+ emit_push_reg_now ("edx");
+ emit_load_deref_reg_now ("eax", deref_size);
+ emit_push_reg_now ("eax");
+ emit_load_assignment_rhs_expression_to_reg ("edx");
+ emit_pop_reg_now ("eax");
+ emit_assignment_binary_op (op, 0);
+ emit_pop_reg_now ("edx");
+
+ }
+
+ emit_store_reg_to_deref_reg_now ("edx", "eax", deref_size);
+ expect_semi_or_recover ();
+
+ return 1;
+
+}
+
+static int parse_indirect_assignment_statement (void) {
+
+ char *name;
+
+ const char *name_start;
+ const char *name_caret;
+
+ unsigned long name_line;
+ struct local_symbol *lhs;
+
+ int global_index;
+ int deref_size = DATA_INT & 0x1f;
+
+ enum token_kind op;
+
+ if (tok.kind != TOK_STAR) {
+ return 0;
+ }
+
+ if (emit_store_to_deref_parenthesized_deref_postfix_incdec_now ("eax")) {
+
+ expect_semi_or_recover ();
+ return 1;
+
+ }
+
+ get_token ();
+
+ if (tok.kind == TOK_STAR) {
+
+ get_token ();
+
+ if (tok.kind == TOK_IDENT) {
+
+ name = xstrdup (tok.ident);
+
+ name_start = tok.start;
+ name_caret = tok.caret;
+
+ name_line = get_line_number ();
+ get_token ();
+
+ if (is_assignment_operator (tok.kind)) {
+
+ op = tok.kind;
+ get_token ();
+
+ lhs = find_local_symbol (name);
+ global_index = find_global_symbol (name);
+
+ if (!lhs && global_index < 0) {
+
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+ expect_semi_or_recover ();
+
+ free (name);
+ return 1;
+
+ }
+
+ if (lhs) {
+
+ if (lhs->pointer_depth > 2) {
+ deref_size = DATA_PTR & 0x1f;
+ } else if (lhs->pointer_depth == 2) {
+ deref_size = lhs->pointed_size & 0x1f;
+ }
+
+ } else {
+
+ if (get_global_symbol_pointer_depth (name) > 2) {
+ deref_size = DATA_PTR & 0x1f;
+ } else if (get_global_symbol_pointer_depth (name) == 2) {
+ deref_size = get_global_symbol_pointed_size (name) & 0x1f;
+ }
+
+ }
+
+ if (deref_size <= 0) {
+ deref_size = DATA_INT & 0x1f;
+ }
+
+ if (state->ofp) {
+
+ if (lhs) {
+
+ if (lhs->is_static && lhs->static_label) {
+ emit_load_global_to_reg ("ecx", lhs->static_label, DATA_PTR);
+ } else {
+ emit_load_local_to_reg ("ecx", lhs->offset, DATA_PTR);
+ }
+
+ } else {
+ emit_load_global_to_reg ("ecx", name, DATA_PTR);
+ }
+
+ emit_load_deref_reg_now ("ecx", DATA_PTR & 0x1f);
+
+ if (op == TOK_ASSIGN) {
+
+ emit_push_reg_now ("ecx");
+
+ emit_load_assignment_rhs_expression_to_reg ("eax");
+ emit_pop_reg_now ("ecx");
+
+ } else {
+
+ emit_push_reg_now ("ecx");
+
+ emit_load_member_from_addr_reg_now ("eax", "ecx", 0, deref_size);
+ emit_push_reg_now ("eax");
+
+ emit_load_assignment_rhs_expression_to_reg ("edx");
+ emit_pop_reg_now ("eax");
+
+ emit_assignment_binary_op (op, 0);
+ emit_pop_reg_now ("ecx");
+
+ }
+
+ emit_store_reg_to_deref_reg_now ("ecx", "eax", deref_size);
+
+ } else {
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+ }
+
+ expect_semi_or_recover ();
+
+ free (name);
+ return 1;
+
+ }
+
+ free (name);
+
+ }
+
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+
+ expect_semi_or_recover ();
+ return 1;
+
+ }
+
+ if (tok.kind != TOK_IDENT) {
+
+ if (tok.kind == TOK_LPAREN) {
+
+ if (paren_text_starts_type_name_now () ||
+ (tok.start && tok.start[0] == '(' && tok.start[1] == '(')) {
+ return parse_cast_indirect_assignment_statement ();
+ }
+
+ return parse_parenthesized_pointer_member_indirect_assignment_statement ();
+
+ }
+
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+ expect_semi_or_recover ();
+
+ return 1;
+
+ }
+
+ name = xstrdup (tok.ident);
+ name_start = tok.start;
+ name_caret = tok.caret;
+ name_line = get_line_number ();
+
+ get_token ();
+
+ if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) {
+
+ enum token_kind postfix_op = tok.kind;
+ int step = 1;
+
+ get_token ();
+
+ if (!is_assignment_operator (tok.kind)) {
+
+ free (name);
+
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+ expect_semi_or_recover ();
+
+ return 1;
+
+ }
+
+ op = tok.kind;
+ get_token ();
+
+ lhs = find_local_symbol (name);
+ global_index = find_global_symbol (name);
+
+ if (!lhs && global_index < 0) {
+
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+ expect_semi_or_recover ();
+
+ free (name);
+ return 1;
+
+ }
+
+ if (lhs) {
+
+ if (lhs->pointer_depth > 1) {
+
+ deref_size = DATA_PTR & 0x1f;
+ step = DATA_PTR & 0x1f;
+
+ } else if (lhs->pointer_depth == 1) {
+
+ deref_size = lhs->pointed_size & 0x1f;
+ step = lhs->pointed_size > 0 ? lhs->pointed_size : 1;
+
+ }
+
+ } else {
+
+ if (get_global_symbol_pointer_depth (name) > 1) {
+
+ deref_size = DATA_PTR & 0x1f;
+ step = DATA_PTR & 0x1f;
+
+ } else if (get_global_symbol_pointer_depth (name) == 1) {
+
+ deref_size = get_global_symbol_pointed_size (name) & 0x1f;
+ step = get_global_symbol_pointed_size (name) > 0 ? get_global_symbol_pointed_size (name) : 1;
+
+ }
+
+ }
+
+ if (deref_size == 0) {
+ deref_size = DATA_INT & 0x1f;
+ }
+
+ if (state->ofp) {
+
+ if (lhs) {
+
+ if (lhs->is_static && lhs->static_label) {
+ emit_load_global_to_reg ("ecx", lhs->static_label, DATA_PTR);
+ } else {
+ emit_load_local_to_reg ("ecx", lhs->offset, DATA_PTR);
+ }
+
+ } else {
+ emit_load_global_to_reg ("ecx", name, DATA_PTR);
+ }
+
+ emit_push_reg_now ("ecx");
+
+ if (lhs) {
+
+ if (lhs->is_static && lhs->static_label) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " %s dword [%s], %d\n" : " %s dword ptr [%s], %d\n"), postfix_op == TOK_INCR ? "add" : "sub", lhs->static_label, step);
+ } else {
+ fprintf (state->ofp, " %sl $%d, %s\n", postfix_op == TOK_INCR ? "add" : "sub", step, lhs->static_label);
+ }
+
+ } else {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ char memref[64];
+
+ format_intel_ebp_offset (memref, sizeof (memref), lhs->offset);
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " %s dword %s, %d\n" : " %s dword ptr %s, %d\n"), postfix_op == TOK_INCR ? "add" : "sub", memref, step);
+
+ } else {
+ fprintf (state->ofp, " %sl $%d, %ld(%%ebp)\n", postfix_op == TOK_INCR ? "add" : "sub", step, lhs->offset);
+ }
+
+ }
+
+ } else {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " %s dword [%s], %d\n" : " %s dword ptr [%s], %d\n"), postfix_op == TOK_INCR ? "add" : "sub", name, step);
+ } else {
+ fprintf (state->ofp, " %sl $%d, %s\n", postfix_op == TOK_INCR ? "add" : "sub", step, name);
+ }
+
+ }
+
+ if (op == TOK_ASSIGN) {
+ emit_load_assignment_rhs_expression_to_reg ("eax");
+ } else {
+
+ emit_pop_reg_now ("edx");
+ emit_push_reg_now ("edx");
+ emit_load_member_from_addr_reg_now ("eax", "edx", 0, deref_size);
+ emit_push_reg_now ("eax");
+ emit_load_assignment_rhs_expression_to_reg ("edx");
+ emit_pop_reg_now ("eax");
+ emit_assignment_binary_op (op, 0);
+
+ }
+
+ emit_pop_reg_now ("edx");
+ emit_store_reg_to_deref_reg_now ("edx", "eax", deref_size);
+
+ } else {
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+ }
+
+ expect_semi_or_recover ();
+
+ free (name);
+ return 1;
+
+ }
+
+ if (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) {
+
+ enum token_kind member_op = tok.kind;
+ enum token_kind postfix_op = TOK_EOF;
+
+ char *member;
+
+ const char *member_start;
+ const char *member_caret;
+
+ unsigned long member_line;
+
+ int offset = 0;
+ int member_size = DATA_PTR & 0x1f;
+ int member_elem_size = DATA_INT & 0x1f;
+ int member_pointer_depth = 0;
+ int step;
+
+ get_token ();
+
+ member_start = tok.start;
+ member_caret = tok.caret;
+ member_line = get_line_number ();
+
+ if (tok.kind != TOK_IDENT) {
+
+ report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "expected member name after %s", member_op == TOK_ARROW ? "->" : ".");
+
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+ expect_semi_or_recover ();
+
+ free (name);
+ return 1;
+
+ }
+
+ member = xstrdup (tok.ident);
+ get_token ();
+
+ if (!find_member_info_ex (member, &offset, &member_size, &member_elem_size, &member_pointer_depth, 0, 0)) {
+
+ report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "unknown member '%s'", member);
+
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+ expect_semi_or_recover ();
+
+ free (member);
+ free (name);
+
+ return 1;
+
+ }
+
+ free (member);
+
+ if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) {
+
+ postfix_op = tok.kind;
+ get_token ();
+
+ }
+
+ if (!is_assignment_operator (tok.kind)) {
+
+ free (name);
+
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+ expect_semi_or_recover ();
+
+ return 1;
+
+ }
+
+ op = tok.kind;
+ get_token ();
+
+ lhs = find_local_symbol (name);
+ global_index = find_global_symbol (name);
+
+ if (!lhs && global_index < 0) {
+
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+ expect_semi_or_recover ();
+
+ free (name);
+ return 1;
+
+ }
+
+ if (member_pointer_depth > 1) {
+ deref_size = DATA_PTR & 0x1f;
+ } else if (member_pointer_depth == 1 && member_elem_size > 0) {
+ deref_size = member_elem_size & 0x1f;
+ }
+
+ if (deref_size == 0) {
+ deref_size = DATA_INT & 0x1f;
+ }
+
+ step = member_elem_size > 0 ? member_elem_size : 1;
+
+ if (state->ofp) {
+
+ if (lhs) {
+
+ if (member_op == TOK_ARROW) {
+
+ if (lhs->is_static && lhs->static_label) {
+ emit_load_global_to_reg ("edx", lhs->static_label, DATA_PTR);
+ } else {
+ emit_load_local_to_reg ("edx", lhs->offset, DATA_PTR);
+ }
+
+ } else {
+
+ if (lhs->is_static && lhs->static_label) {
+ emit_load_address_to_reg_now ("edx", lhs->static_label);
+ } else {
+ emit_load_local_address_to_reg_now ("edx", lhs->offset);
+ }
+
+ }
+
+ } else {
+
+ if (member_op == TOK_ARROW) {
+ emit_load_global_to_reg ("edx", name, DATA_PTR);
+ } else {
+ emit_load_address_to_reg_now ("edx", name);
+ }
+
+ }
+
+ emit_load_member_from_addr_reg_now ("ecx", "edx", offset, DATA_PTR & 0x1f);
+
+ if (postfix_op == TOK_INCR || postfix_op == TOK_DECR) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ if (state->syntax & ASM_SYNTAX_NASM) {
+ fprintf (state->ofp, " %s dword [edx + %d], %d\n", postfix_op == TOK_INCR ? "add" : "sub", offset, step);
+ } else {
+ fprintf (state->ofp, " %s dword ptr [edx + %d], %d\n", postfix_op == TOK_INCR ? "add" : "sub", offset, step);
+ }
+
+ } else {
+ fprintf (state->ofp, " %sl $%d, %d(%%edx)\n", postfix_op == TOK_INCR ? "add" : "sub", step, offset);
+ }
+
+ }
+
+ emit_push_reg_now ("ecx");
+
+ if (op == TOK_ASSIGN) {
+ emit_load_assignment_rhs_expression_to_reg ("eax");
+ } else {
+
+ emit_load_member_from_addr_reg_now ("eax", "edx", 0, deref_size);
+ emit_push_reg_now ("eax");
+ emit_load_assignment_rhs_expression_to_reg ("edx");
+ emit_pop_reg_now ("eax");
+ emit_assignment_binary_op (op, 0);
+
+ }
+
+ emit_pop_reg_now ("edx");
+ emit_store_reg_to_deref_reg_now ("edx", "eax", deref_size);
+
+ } else {
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+ }
+
+ expect_semi_or_recover ();
+
+ free (name);
+ return 1;
+
+ }
+
+ if (!is_assignment_operator (tok.kind)) {
+
+ free (name);
+
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+ expect_semi_or_recover ();
+
+ return 1;
+
+ }
+
+ op = tok.kind;
+ get_token ();
+
+ lhs = find_local_symbol (name);
+ global_index = find_global_symbol (name);
+
+ if (!lhs && global_index < 0) {
+
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+ expect_semi_or_recover ();
+
+ free (name);
+ return 1;
+
+ }
+
+ if (lhs) {
+
+ if (lhs->pointer_depth > 1) {
+ deref_size = DATA_PTR & 0x1f;
+ } else if (lhs->pointer_depth == 1) {
+
+ /**
+ * Keep aggregate pointed-to sizes intact for assignments like
+ * *hashtab = old_hashtab; Masking with 0x1f corrupts struct
+ * copies whose size is greater than 31 bytes.
+ */
+ deref_size = lhs->pointed_size;
+
+ }
+
+ } else {
+
+ if (get_global_symbol_pointer_depth (name) > 1) {
+ deref_size = DATA_PTR & 0x1f;
+ } else if (get_global_symbol_pointer_depth (name) == 1) {
+ deref_size = get_global_symbol_pointed_size (name);
+ }
+
+ }
+
+ if (deref_size == 0) {
+ deref_size = DATA_INT & 0x1f;
+ }
+
+ if (state->ofp) {
+
+ if (lhs) {
+
+ if (lhs->is_static && lhs->static_label) {
+ emit_load_global_to_reg ("edx", lhs->static_label, DATA_PTR);
+ } else {
+ emit_load_local_to_reg ("edx", lhs->offset, DATA_PTR);
+ }
+
+ } else {
+ emit_load_global_to_reg ("edx", name, DATA_PTR);
+ }
+
+ if (op == TOK_ASSIGN) {
+
+ if (deref_size != (DATA_LLONG & 0x1f) &&
+ emit_aggregate_copy_from_current_rhs_to_addr_reg_now ("edx", 0, deref_size)) {
+
+ expect_semi_or_recover ();
+
+ free (name);
+ return 1;
+
+ }
+
+ emit_push_reg_now ("edx");
+
+ if (deref_size == (DATA_LLONG & 0x1f)) {
+
+ emit_load_assignment_rhs_expression_to_pair ("eax", "edx", 1);
+ emit_pop_reg_now ("ecx");
+
+ } else {
+
+ emit_load_assignment_rhs_expression_to_reg ("eax");
+ emit_pop_reg_now ("edx");
+
+ }
+
+ } else {
+
+ emit_load_member_from_addr_reg_now ("eax", "edx", 0, deref_size);
+
+ emit_push_reg_now ("edx");
+ emit_push_reg_now ("eax");
+
+ emit_load_assignment_rhs_expression_to_reg ("edx");
+ emit_pop_reg_now ("eax");
+
+ emit_assignment_binary_op (op, 0);
+ emit_pop_reg_now ("edx");
+
+ }
+
+ if (deref_size == (DATA_LLONG & 0x1f)) {
+ emit_store_pair_to_deref_reg_now ("ecx", "eax", "edx");
+ } else {
+ emit_store_reg_to_deref_reg_now ("edx", "eax", deref_size);
+ }
+
+ } else {
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+ }
+
+ expect_semi_or_recover ();
+
+ free (name);
+ return 1;
+
+}
+
+static int source_starts_parenthesized_star_now (void) {
+
+ const char *p = tok.caret ? tok.caret : tok.start;
+
+ if (!p) {
+ return 0;
+ }
+
+ while (*p == ' ' || *p == '\t') {
+ p++;
+ }
+
+ if (*p != '(') {
+ return 0;
+ }
+
+ p++;
+
+ while (*p == ' ' || *p == '\t') {
+ p++;
+ }
+
+ return *p == '*';
+
+}
+
+static int parse_parenthesized_indirect_member_assignment_statement (void) {
+
+ enum token_kind member_op;
+ enum token_kind op;
+
+ char *member;
+
+ const char *member_start;
+ const char *member_caret;
+
+ unsigned long member_line;
+
+ int member_offset = 0;
+ int member_size = DATA_INT & 0x1f;
+ int saw_close = 0;
+
+ if (tok.kind != TOK_LPAREN || !source_starts_parenthesized_star_now ()) {
+ return 0;
+ }
+
+ get_token ();
+
+ if (tok.kind != TOK_STAR) {
+ return 0;
+ }
+
+ get_token ();
+
+ /*
+ * Parse only the object expression inside the parenthesized dereference.
+ * For macro-expanded lvalues such as
+ *
+ * (*(__gtin()))->field = value;
+ *
+ * the normal assignment-expression loader can consume the complete
+ * "->field = value" tail as a value expression. That emits only
+ * member loads and drops the store.
+ */
+ if (tok.kind == TOK_LPAREN) {
+
+ int parens = 0;
+ char *inner_name = 0;
+
+ const char *inner_start = 0;
+ const char *inner_caret = 0;
+
+ unsigned long inner_line = 0;
+ struct local_symbol *inner_lhs;
+
+ while (tok.kind == TOK_LPAREN) {
+
+ parens++;
+ get_token ();
+
+ }
+
+ if (tok.kind == TOK_IDENT) {
+
+ inner_name = xstrdup (tok.ident);
+ inner_start = tok.start;
+ inner_caret = tok.caret;
+ inner_line = get_line_number ();
+
+ get_token ();
+
+ if (tok.kind == TOK_LPAREN) {
+ emit_call_identifier_to_reg_now (inner_name, "edx", inner_start, inner_caret, inner_line);
+ } else {
+
+ inner_lhs = find_local_symbol (inner_name);
+
+ if (inner_lhs) {
+
+ if (inner_lhs->is_static && inner_lhs->static_label) {
+ emit_load_global_to_reg ("edx", inner_lhs->static_label, DATA_PTR);
+ } else {
+ emit_load_local_to_reg ("edx", inner_lhs->offset, DATA_PTR);
+ }
+
+ } else {
+ emit_load_global_to_reg ("edx", inner_name, DATA_PTR);
+ }
+
+ }
+
+ free (inner_name);
+
+ while (parens > 0 && tok.kind == TOK_RPAREN) {
+
+ saw_close = 1;
+ parens--;
+
+ get_token ();
+
+ }
+
+ } else {
+ emit_load_assignment_rhs_expression_to_reg ("edx");
+ }
+
+ } else {
+ emit_load_assignment_rhs_expression_to_reg ("edx");
+ }
+
+ while (tok.kind == TOK_RPAREN) {
+
+ saw_close = 1;
+ get_token ();
+
+ }
+
+ if (!saw_close) {
+
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+ expect_semi_or_recover ();
+
+ return 1;
+
+ }
+
+ if (tok.kind != TOK_ARROW && tok.kind != TOK_DOT) {
+
+ int deref_size = DATA_INT & 0x1f;
+ int step = 1;
+
+ if (rhs_last_pointer_depth > 1) {
+
+ deref_size = DATA_PTR & 0x1f;
+ step = DATA_PTR & 0x1f;
+
+ } else if (rhs_last_pointer_depth == 1 && rhs_last_pointed_size > 0) {
+ deref_size = rhs_last_pointed_size & 0x1f;
+ }
+
+ if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) {
+
+ op = tok.kind;
+ get_token ();
+
+ if (state->ofp) {
+
+ emit_load_member_from_addr_reg_now ("eax", "edx", 0, deref_size);
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " %s eax, %d\n", op == TOK_INCR ? "add" : "sub", step);
+ } else {
+ fprintf (state->ofp, " %sl $%d, %%eax\n", op == TOK_INCR ? "add" : "sub", step);
+ }
+
+ emit_store_reg_to_deref_reg_now ("edx", "eax", deref_size);
+
+ }
+
+ expect_semi_or_recover ();
+ return 1;
+
+ }
+
+ if (!is_assignment_operator (tok.kind)) {
+
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+ expect_semi_or_recover ();
+
+ return 1;
+
+ }
+
+ op = tok.kind;
+ get_token ();
+
+ if (state->ofp) {
+
+ if (op == TOK_ASSIGN) {
+
+ emit_push_reg_now ("edx");
+ emit_load_assignment_rhs_expression_to_reg ("eax");
+ emit_pop_reg_now ("edx");
+
+ } else {
+
+ emit_load_member_from_addr_reg_now ("eax", "edx", 0, deref_size);
+ emit_push_reg_now ("edx");
+ emit_push_reg_now ("eax");
+ emit_load_assignment_rhs_expression_to_reg ("edx");
+ emit_pop_reg_now ("eax");
+ emit_assignment_binary_op (op, 0);
+ emit_pop_reg_now ("edx");
+
+ }
+
+ emit_store_reg_to_deref_reg_now ("edx", "eax", deref_size);
+
+ } else {
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+ }
+
+ expect_semi_or_recover ();
+ return 1;
+
+ }
+
+ if (state->ofp) {
+ emit_load_deref_reg_now ("edx", DATA_PTR);
+ }
+
+ member_op = tok.kind;
+ get_token ();
+
+ member_start = tok.start;
+ member_caret = tok.caret;
+ member_line = get_line_number ();
+
+ if (tok.kind != TOK_IDENT) {
+
+ report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "expected member name after %s", member_op == TOK_ARROW ? "->" : ".");
+
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+ expect_semi_or_recover ();
+
+ return 1;
+
+ }
+
+ member = xstrdup (tok.ident);
+ get_token ();
+
+ if (!find_member_info (member, &member_offset, &member_size)) {
+
+ report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "unknown member '%s'", member);
+ free (member);
+
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+ expect_semi_or_recover ();
+
+ return 1;
+
+ }
+
+ free (member);
+
+ if (!is_assignment_operator (tok.kind)) {
+
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+ expect_semi_or_recover ();
+
+ return 1;
+
+ }
+
+ op = tok.kind;
+ get_token ();
+
+ if (state->ofp) {
+
+ if (op == TOK_ASSIGN) {
+
+ emit_push_reg_now ("edx");
+ emit_load_assignment_rhs_expression_to_reg ("eax");
+ emit_pop_reg_now ("edx");
+
+ } else {
+
+ emit_load_member_from_addr_reg_now ("eax", "edx", member_offset, member_size);
+ emit_push_reg_now ("edx");
+ emit_push_reg_now ("eax");
+ emit_load_assignment_rhs_expression_to_reg ("edx");
+ emit_pop_reg_now ("eax");
+ emit_assignment_binary_op (op, 0);
+ emit_pop_reg_now ("edx");
+
+ }
+
+ emit_store_member_to_addr_reg_now ("edx", member_offset, "eax", member_size);
+
+ } else {
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+ }
+
+ expect_semi_or_recover ();
+ return 1;
+
+}
+
+static int token_text_looks_like_postfix_call_now (void) {
+
+ const char *p;
+
+ int saw_postfix;
+ int paren_depth;
+ int bracket_depth;
+
+ if (tok.caret) {
+ p = tok.caret;
+ } else if (tok.start) {
+ p = tok.start;
+ } else {
+ return 0;
+ }
+
+ saw_postfix = 0;
+ paren_depth = 0;
+ bracket_depth = 0;
+
+ while (*p && *p != ';' && *p != '\n') {
+
+ if (*p == '(') {
+
+ if (saw_postfix && paren_depth == 0 && bracket_depth == 0) {
+ return 1;
+ }
+
+ paren_depth++;
+
+ } else if (*p == ')') {
+
+ if (paren_depth > 0) {
+ paren_depth--;
+ }
+
+ } else if (*p == '[') {
+ bracket_depth++;
+ } else if (*p == ']') {
+
+ if (bracket_depth > 0) {
+ bracket_depth--;
+ }
+
+ } else if (paren_depth == 0 && bracket_depth == 0 && *p == '.') {
+ saw_postfix = 1;
+ } else if (paren_depth == 0 && bracket_depth == 0 && *p == '-' && p[1] == '>') {
+
+ saw_postfix = 1;
+ p++;
+
+ } else if (paren_depth == 0 && bracket_depth == 0 && *p == '=') {
+ return 0;
+ }
+
+ p++;
+
+ }
+
+ return 0;
+
+}
+
+static int parse_parenthesized_deref_subscript_statement (void) {
+
+ char *name;
+
+ const char *name_start;
+ const char *name_caret;
+
+ unsigned long name_line;
+ struct local_symbol *src;
+
+ int global_index;
+ int pointer_depth;
+ int pointed_size;
+
+ if (tok.kind != TOK_LPAREN || !source_starts_lparen_deref_subscript_at (tok.caret)) {
+ return 0;
+ }
+
+ get_token ();
+
+ if (tok.kind != TOK_STAR) {
+ return 0;
+ }
+
+ get_token ();
+
+ name_start = tok.start;
+ name_caret = tok.caret;
+ name_line = get_line_number ();
+
+ if (tok.kind != TOK_IDENT || !tok.ident) {
+
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "expected identifier after *");
+ return 1;
+
+ }
+
+ name = xstrdup (tok.ident);
+ get_token ();
+
+ expect (TOK_RPAREN, ")");
+
+ src = find_local_symbol (name);
+ global_index = find_global_symbol (name);
+
+ pointer_depth = 0;
+ pointed_size = DATA_INT & 0x1f;
+
+ if (src) {
+
+ pointer_depth = src->pointer_depth;
+ pointed_size = src->pointed_size;
+
+ if (src->is_static && src->static_label) {
+ emit_load_global_to_reg ("eax", src->static_label, DATA_PTR);
+ } else {
+ emit_load_local_to_reg ("eax", src->offset, DATA_PTR);
+ }
+
+ } else if (global_index >= 0) {
+
+ pointer_depth = get_global_symbol_pointer_depth (name);
+ pointed_size = get_global_symbol_pointed_size (name);
+
+ emit_load_global_to_reg ("eax", name, DATA_PTR);
+
+ } else {
+
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+
+ free (name);
+ return 1;
+
+ }
+
+ if (pointer_depth <= 0) {
+
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "invalid indirection of non-pointer '%s'", name);
+
+ free (name);
+ return 1;
+
+ }
+
+ emit_load_deref_reg_now ("eax", DATA_PTR & 0x1f);
+ emit_handle_subscript_after_loaded_pointer_to_reg_now ("eax", pointer_depth - 1, pointed_size);
+
+ free (name);
+ return 1;
+
+}
+
+static int parse_identifier_assignment_statement (void) {
+
+ char *name;
+ int global_index;
+
+ const char *name_start, *name_caret;
+ unsigned long name_line;
+
+ enum token_kind op;
+ struct local_symbol *lhs;
+
+ int lhs_size;
+ int lhs_is_floating;
+
+ if (tok.kind != TOK_IDENT) {
+ return 0;
+ }
+
+ if (token_text_looks_like_postfix_call_now ()) {
+ return 0;
+ }
+
+ name = xstrdup (tok.ident);
+ name_start = tok.start;
+ name_caret = tok.caret;
+ name_line = get_line_number ();
+
+ get_token ();
+
+ if (tok.kind == TOK_COLON) {
+
+ define_goto_label (name, name_line, name_start, name_caret);
+
+ get_token ();
+ free (name);
+
+ parse_statement ();
+ return 1;
+
+ }
+
+ if (tok.kind == TOK_DOT || tok.kind == TOK_ARROW) {
+
+ enum token_kind member_op = tok.kind;
+ char *member;
+
+ const char *member_start;
+ const char *member_caret;
+
+ unsigned long member_line;
+
+ int member_offset = 0;
+ int member_size = DATA_INT & 0x1f;
+ int member_elem_size = DATA_INT & 0x1f;
+ int member_pointer_depth = 0;
+ int member_is_floating = 0;
+
+ get_token ();
+
+ member_start = tok.start;
+ member_caret = tok.caret;
+ member_line = get_line_number ();
+
+ if (tok.kind != TOK_IDENT) {
+
+ report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "expected member name after %s", member_op == TOK_ARROW ? "->" : ".");
+
+ free (name);
+ return 1;
+
+ }
+
+ member = xstrdup (tok.ident);
+ get_token ();
+
+ lhs = find_local_symbol (name);
+ global_index = find_global_symbol (name);
+
+ if (!find_member_info_ex_bounded (member,
+ member_op == TOK_DOT
+ ? (lhs ? lhs->size : (global_index >= 0 ? get_global_symbol_size (name) : 0))
+ : (lhs ? lhs->pointed_size : (global_index >= 0 ? get_global_symbol_pointed_size (name) : 0)),
+ member_op == TOK_DOT
+ ? (lhs ? lhs->tag_name : (global_index >= 0 ? get_global_symbol_tag_name (name) : 0))
+ : (lhs ? lhs->pointed_tag_name : 0),
+ &member_offset, &member_size, &member_elem_size, &member_pointer_depth, 0, &member_is_floating)) {
+
+ report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "unknown member '%s'", member);
+
+ free (member);
+ free (name);
+
+ return 1;
+
+ }
+
+ free (member);
+
+ if (!is_assignment_operator (tok.kind)) {
+
+ if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) {
+
+ enum token_kind postfix_op = tok.kind;
+ int step = member_pointer_depth > 0 && member_elem_size > 0 ? member_elem_size : 1;
+
+ get_token ();
+
+ lhs = find_local_symbol (name);
+ global_index = find_global_symbol (name);
+
+ if (!lhs && global_index < 0) {
+
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+ expect (TOK_SEMI, ";");
+
+ free (name);
+ return 1;
+
+ }
+
+ if (state->ofp) {
+
+ if (member_op == TOK_ARROW) {
+
+ if (lhs) {
+
+ if (lhs->is_static && lhs->static_label) {
+ emit_load_global_to_reg ("edx", lhs->static_label, DATA_PTR);
+ } else {
+ emit_load_local_to_reg ("edx", lhs->offset, DATA_PTR);
+ }
+
+ } else {
+ emit_load_global_to_reg ("edx", name, DATA_PTR);
+ }
+
+ } else {
+
+ if (lhs) {
+
+ if (lhs->is_static && lhs->static_label) {
+ emit_load_address_to_reg_now ("edx", lhs->static_label);
+ } else {
+ emit_load_local_address_to_reg_now ("edx", lhs->offset);
+ }
+
+ } else {
+ emit_load_address_to_reg_now ("edx", name);
+ }
+
+ }
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ const char *opname = postfix_op == TOK_INCR ? "add" : "sub";
+ const char *opsize = member_size == 1 ? "byte" : (member_size == 2 ? "word" : "dword");
+
+ if (state->syntax & ASM_SYNTAX_NASM) {
+ fprintf (state->ofp, " %s %s [edx + %d], %d\n", opname, opsize, member_offset, step);
+ } else {
+ fprintf (state->ofp, " %s %s ptr [edx + %d], %d\n", opname, opsize, member_offset, step);
+ }
+
+ } else {
+
+ const char *suffix = member_size == 1 ? "b" : (member_size == 2 ? "w" : "l");
+ fprintf (state->ofp, " %s%s $%d, %d(%%edx)\n", postfix_op == TOK_INCR ? "add" : "sub", suffix, step, member_offset);
+
+ }
+
+ }
+
+ expect_semi_or_recover ();
+
+ free (name);
+ return 1;
+
+ }
+
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+ expect (TOK_SEMI, ";");
+
+ free (name);
+ return 1;
+
+ }
+
+ op = tok.kind;
+ get_token ();
+
+ if (!lhs && global_index < 0) {
+
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+ expect (TOK_SEMI, ";");
+
+ free (name);
+ return 1;
+
+ }
+
+ if (state->ofp) {
+
+ if (member_op == TOK_ARROW) {
+
+ if (lhs) {
+
+ if (lhs->is_static && lhs->static_label) {
+ emit_load_global_to_reg ("edx", lhs->static_label, DATA_PTR);
+ } else {
+ emit_load_local_to_reg ("edx", lhs->offset, DATA_PTR);
+ }
+
+ } else {
+ emit_load_global_to_reg ("edx", name, DATA_PTR);
+ }
+
+ } else {
+
+ if (lhs) {
+
+ if (lhs->is_static && lhs->static_label) {
+ emit_load_address_to_reg_now ("edx", lhs->static_label);
+ } else {
+ emit_load_local_address_to_reg_now ("edx", lhs->offset);
+ }
+
+ } else {
+ emit_load_address_to_reg_now ("edx", name);
+ }
+
+ }
+
+ if (op == TOK_ASSIGN) {
+
+ emit_push_reg_now ("edx");
+
+ if (member_is_floating || token_is_floating_constant_now ()) {
+ emit_load_floating_rhs_expression_now (member_size);
+ } else {
+ emit_load_assignment_rhs_expression_to_reg ("eax");
+ }
+
+ emit_pop_reg_now ("edx");
+
+ } else {
+
+ emit_load_member_from_addr_reg_now ("eax", "edx", member_offset, member_size);
+ emit_push_reg_now ("edx");
+ emit_push_reg_now ("eax");
+
+ if (tok.kind == TOK_TILDE) {
+
+ int64_s rhs_const;
+ get_token ();
+
+ if (tok.kind == TOK_LPAREN) {
+
+ get_token ();
+
+ rhs_const = const64_from_current_foldable_expr ();
+ expect (TOK_RPAREN, ")");
+
+ } else {
+ rhs_const = const64_from_current_foldable_expr ();
+ }
+
+ rhs_const.low = (~rhs_const.low) & U32_MASK;
+ rhs_const.high = (~rhs_const.high) & U32_MASK;
+
+ emit_load_const32_to_reg_now ("edx", rhs_const);
+
+ } else if (const_integer_expr_text_is_foldable_now (tok.start) || const_integer_expr_text_is_foldable_now (tok.caret)) {
+
+ int64_s rhs_const = const64_from_current_foldable_expr ();
+ emit_load_const32_to_reg_now ("edx", rhs_const);
+
+ } else {
+ emit_load_assignment_rhs_expression_to_reg ("edx");
+ }
+
+ emit_pop_reg_now ("eax");
+ emit_assignment_binary_op (op, 0);
+ emit_pop_reg_now ("edx");
+
+ }
+
+ if ((member_is_floating || token_is_floating_constant_now ()) && op == TOK_ASSIGN) {
+ emit_store_floating_member_to_addr_reg_now ("edx", member_offset, member_size);
+ } else {
+ emit_store_member_to_addr_reg_now ("edx", member_offset, "eax", member_size);
+ }
+
+ } else {
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+ }
+
+ expect_semi_or_recover ();
+
+ free (name);
+ return 1;
+
+ }
+
+ if (tok.kind == TOK_LBRACK) {
+
+ int elem_size = DATA_INT & 0x1f;
+ int elem_pointer_depth = 0;
+ int elem_pointed_size = DATA_INT & 0x1f;
+
+ lhs = find_local_symbol (name);
+ global_index = find_global_symbol (name);
+
+ if (!lhs && global_index < 0) {
+
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+ expect_semi_or_recover ();
+
+ free (name);
+ return 1;
+
+ }
+
+ if (lhs) {
+
+ elem_size = lhs->is_array ? (lhs->pointer_depth ? DATA_PTR : lhs->pointed_size) :
+ (lhs->pointer_depth > 1 ? DATA_PTR : lhs->pointed_size);
+
+ elem_pointer_depth = lhs->pointer_depth > 0 ? lhs->pointer_depth - 1 : 0;
+ elem_pointed_size = lhs->pointed_size;
+
+ } else {
+
+ elem_size = get_global_symbol_array (name) ?
+ (get_global_symbol_pointer_depth (name) ? DATA_PTR : (get_global_symbol_array_element_size (name) > 0 ? get_global_symbol_array_element_size (name) : get_global_symbol_pointed_size (name))) :
+ (get_global_symbol_pointer_depth (name) > 1 ? DATA_PTR : get_global_symbol_pointed_size (name));
+
+ elem_pointer_depth = get_global_symbol_pointer_depth (name) > 0 ? get_global_symbol_pointer_depth (name) - 1 : 0;
+ elem_pointed_size = get_global_symbol_pointed_size (name);
+
+ }
+
+ if ((elem_size & 0x1f) == 0) {
+ elem_size = DATA_INT & 0x1f;
+ }
+
+ if (state->ofp) {
+
+ if (lhs) {
+
+ if (lhs->is_array) {
+
+ if (lhs->is_static && lhs->static_label) {
+ emit_load_symbol_address_to_reg_now ("edx", lhs->static_label, 0, 0);
+ } else {
+ emit_load_symbol_address_to_reg_now ("edx", 0, lhs->offset, 1);
+ }
+
+ } else if (lhs->is_static && lhs->static_label) {
+ emit_load_global_to_reg ("edx", lhs->static_label, DATA_PTR);
+ } else {
+ emit_load_local_to_reg ("edx", lhs->offset, DATA_PTR);
+ }
+
+ } else if (get_global_symbol_array (name)) {
+ emit_load_symbol_address_to_reg_now ("edx", name, 0, 0);
+ } else {
+ emit_load_global_to_reg ("edx", name, DATA_PTR);
+ }
+
+ emit_parse_postfix_subscript_scaled_address_to_reg_now ("edx", elem_size);
+
+ } else {
+ emit_parse_postfix_subscript_scaled_address_to_reg_now ("edx", elem_size);
+ }
+
+ if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) {
+
+ op = tok.kind;
+ get_token ();
+
+ if (state->ofp) {
+
+ int inc_amount = (elem_pointer_depth > 0 && (elem_pointed_size & 0x1f) > 0) ?
+ (elem_pointed_size & 0x1f) : 1;
+
+ emit_load_member_from_addr_reg_now ("eax", "edx", 0, elem_size);
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " %s eax, %d\n", op == TOK_INCR ? "add" : "sub", inc_amount);
+ } else {
+ fprintf (state->ofp, " %sl $%d, %%eax\n", op == TOK_INCR ? "add" : "sub", inc_amount);
+ }
+
+ emit_store_reg_to_deref_reg_now ("edx", "eax", elem_size);
+
+ }
+
+ expect_semi_or_recover ();
+
+ free (name);
+ return 1;
+
+ }
+
+ if (!is_assignment_operator (tok.kind)) {
+
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+ expect_semi_or_recover ();
+
+ free (name);
+ return 1;
+
+ }
+
+ op = tok.kind;
+ get_token ();
+
+ if (state->ofp) {
+
+ if (op == TOK_ASSIGN) {
+
+ emit_push_reg_now ("edx");
+ emit_load_assignment_rhs_expression_to_reg ("eax");
+ emit_pop_reg_now ("edx");
+
+ } else {
+
+ emit_load_member_from_addr_reg_now ("eax", "edx", 0, elem_size);
+ emit_push_reg_now ("edx");
+ emit_push_reg_now ("eax");
+ emit_load_assignment_rhs_expression_to_reg ("edx");
+ emit_pop_reg_now ("eax");
+ emit_assignment_binary_op (op, 0);
+ emit_pop_reg_now ("edx");
+
+ }
+
+ emit_store_reg_to_deref_reg_now ("edx", "eax", elem_size);
+
+ } else {
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+ }
+
+ expect_semi_or_recover ();
+
+ free (name);
+ return 1;
+
+ }
+
+ if (!is_assignment_operator (tok.kind)) {
+
+ if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) {
+
+ op = tok.kind;
+ get_token ();
+
+ lhs = find_local_symbol (name);
+ emit_incdec_symbol_now (lhs, name, op, name_line, name_start, name_caret);
+
+ expect_semi_or_recover ();
+ free (name);
+
+ return 1;
+
+ }
+
+ if (tok.kind == TOK_LPAREN) {
+
+ if (!find_local_symbol (name)) {
+ ensure_global_function_symbol (name, name_start, name_caret, name_line);
+ }
+
+ emit_call_identifier_to_reg_now (name, "eax", name_start, name_caret, name_line);
+
+ if (tok.kind == TOK_LBRACK) {
+
+ int elem_size = get_global_symbol_pointed_size (name);
+
+ if ((elem_size & 0x1f) == 0) {
+ elem_size = DATA_INT & 0x1f;
+ }
+
+ if (state->ofp) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " mov edx, eax\n");
+ } else {
+ fprintf (state->ofp, " movl %%eax, %%edx\n");
+ }
+
+ }
+
+ emit_parse_postfix_subscript_scaled_address_to_reg_now ("edx", elem_size);
+
+ if (is_assignment_operator (tok.kind)) {
+
+ op = tok.kind;
+ get_token ();
+
+ if (state->ofp) {
+
+ if (op == TOK_ASSIGN) {
+
+ emit_push_reg_now ("edx");
+ emit_load_assignment_rhs_expression_to_reg ("eax");
+ emit_pop_reg_now ("edx");
+
+ } else {
+
+ emit_load_member_from_addr_reg_now ("eax", "edx", 0, elem_size);
+ emit_push_reg_now ("edx");
+ emit_push_reg_now ("eax");
+ emit_load_assignment_rhs_expression_to_reg ("edx");
+ emit_pop_reg_now ("eax");
+ emit_assignment_binary_op (op, 0);
+ emit_pop_reg_now ("edx");
+
+ }
+
+ emit_store_reg_to_deref_reg_now ("edx", "eax", elem_size);
+
+ } else {
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+ }
+
+ expect_semi_or_recover ();
+
+ free (name);
+ return 1;
+
+ }
+
+ }
+
+ if (tok.kind == TOK_ARROW || tok.kind == TOK_DOT) {
+
+ enum token_kind member_op = tok.kind;
+ char *member;
+
+ const char *member_start;
+ const char *member_caret;
+
+ unsigned long member_line;
+
+ int member_offset = 0;
+ int member_size = DATA_PTR & 0x1f;
+
+ get_token ();
+
+ member_start = tok.start;
+ member_caret = tok.caret;
+ member_line = get_line_number ();
+
+ if (tok.kind != TOK_IDENT) {
+
+ report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "expected member name after %s", member_op == TOK_ARROW ? "->" : ".");
+
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+ expect_semi_or_recover ();
+
+ free (name);
+ return 1;
+
+ }
+
+ member = xstrdup (tok.ident);
+ get_token ();
+
+ if (!find_member_info (member, &member_offset, &member_size)) {
+
+ report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "unknown member '%s'", member);
+ free (member);
+
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+ expect_semi_or_recover ();
+
+ free (name);
+ return 1;
+
+ }
+
+ free (member);
+
+ if (member_op == TOK_DOT && state->ofp) {
+ emit_load_deref_reg_now ("eax", DATA_PTR);
+ }
+
+ if (is_assignment_operator (tok.kind)) {
+
+ op = tok.kind;
+ get_token ();
+
+ if (state->ofp) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " mov edx, eax\n");
+ } else {
+ fprintf (state->ofp, " movl %%eax, %%edx\n");
+ }
+
+ if (op == TOK_ASSIGN) {
+
+ emit_push_reg_now ("edx");
+ emit_load_assignment_rhs_expression_to_reg ("eax");
+ emit_pop_reg_now ("edx");
+
+ } else {
+
+ emit_load_member_from_addr_reg_now ("eax", "edx", member_offset, member_size);
+ emit_push_reg_now ("edx");
+ emit_push_reg_now ("eax");
+ emit_load_assignment_rhs_expression_to_reg ("edx");
+ emit_pop_reg_now ("eax");
+ emit_assignment_binary_op (op, 0);
+ emit_pop_reg_now ("edx");
+
+ }
+
+ emit_store_member_to_addr_reg_now ("edx", member_offset, "eax", member_size);
+
+ } else {
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+ }
+
+ expect_semi_or_recover ();
+ free (name);
+
+ return 1;
+
+ }
+
+ if (tok.kind == TOK_LPAREN) {
+
+ FILE **arg_tmp_ofps = 0;
+ FILE **new_arg_tmp_ofps;
+ FILE *arg_tmp_ofp;
+ FILE *arg_saved_ofp;
+
+ int argc = 0;
+ int i;
+ int ch;
+ int total_arg_bytes = 0;
+
+ if (state->ofp) {
+
+ emit_load_member_from_addr_reg_now ("ecx", "eax", member_offset, DATA_PTR & 0x1f);
+ emit_push_reg_now ("ecx");
+
+ }
+
+ get_token ();
+
+ if (tok.kind != TOK_RPAREN) {
+
+ for (;;) {
+
+ arg_tmp_ofp = 0;
+ arg_saved_ofp = 0;
+
+ if (state->ofp) {
+
+ arg_tmp_ofp = tmpfile ();
+
+ if (arg_tmp_ofp) {
+ arg_saved_ofp = state->ofp;
+ state->ofp = arg_tmp_ofp;
+ }
+
+ }
+
+ emit_load_assignment_rhs_expression_to_reg ("eax");
+
+ if (state->ofp) {
+ emit_push_reg_now ("eax");
+ }
+
+ if (arg_saved_ofp) {
+
+ fflush (arg_tmp_ofp);
+ state->ofp = arg_saved_ofp;
+
+ new_arg_tmp_ofps = (FILE **) realloc (arg_tmp_ofps, sizeof (*arg_tmp_ofps) * (argc + 1));
+
+ if (new_arg_tmp_ofps) {
+
+ arg_tmp_ofps = new_arg_tmp_ofps;
+ arg_tmp_ofps[argc] = arg_tmp_ofp;
+ arg_tmp_ofp = 0;
+
+ }
+
+ }
+
+ if (arg_tmp_ofp) {
+ fclose (arg_tmp_ofp);
+ }
+
+ argc++;
+ total_arg_bytes += DATA_PTR & 0x1f;
+
+ if (!_accept (TOK_COMMA)) {
+ break;
+ }
+
+ }
+
+ }
+
+ expect (TOK_RPAREN, ")");
+
+ if (state->ofp) {
+
+ for (i = argc - 1; i >= 0; i--) {
+
+ if (arg_tmp_ofps && arg_tmp_ofps[i]) {
+
+ fseek (arg_tmp_ofps[i], 0, SEEK_SET);
+
+ while ((ch = fgetc (arg_tmp_ofps[i])) != EOF) {
+ fputc (ch, state->ofp);
+ }
+
+ fclose (arg_tmp_ofps[i]);
+ arg_tmp_ofps[i] = 0;
+
+ }
+
+ }
+
+ if (arg_tmp_ofps) {
+
+ free (arg_tmp_ofps);
+ arg_tmp_ofps = 0;
+
+ }
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ if (state->syntax & ASM_SYNTAX_NASM) {
+ fprintf (state->ofp, " mov ecx, dword [esp + %d]\n", total_arg_bytes);
+ } else {
+ fprintf (state->ofp, " mov ecx, dword ptr [esp + %d]\n", total_arg_bytes);
+ }
+
+ fprintf (state->ofp, " call ecx\n");
+ fprintf (state->ofp, " add esp, %d\n", total_arg_bytes + (DATA_PTR & 0x1f));
+
+ } else {
+
+ fprintf (state->ofp, " movl %d(%%esp), %%ecx\n", total_arg_bytes);
+ fprintf (state->ofp, " call *%%ecx\n");
+ fprintf (state->ofp, " addl $%d, %%esp\n", total_arg_bytes + (DATA_PTR & 0x1f));
+
+ }
+
+ }
+
+ expect_semi_or_recover ();
+ free (name);
+
+ return 1;
+
+ }
+
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+
+ expect_semi_or_recover ();
+ free (name);
+
+ return 1;
+
+ }
+
+ expect_semi_or_recover ();
+ free (name);
+
+ return 1;
+
+ }
+
+ if (!find_local_symbol (name) && find_global_symbol (name) < 0) {
+
+ int64_s ignored;
+
+ if (!resolve_enum_constant (name, &ignored)) {
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+ }
+
+ }
+
+ free (name);
+
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+ expect (TOK_SEMI, ";");
+
+ return 1;
+
+ }
+
+ op = tok.kind;
+ get_token ();
+
+ lhs = find_local_symbol (name);
+ global_index = find_global_symbol (name);
+
+ if (!lhs && global_index < 0) {
+
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+ expect (TOK_SEMI, ";");
+
+ free (name);
+ return 1;
+
+ }
+
+ if (state->ofp) {
+
+ lhs_size = lhs ? lhs->size : get_global_symbol_size (name);
+ lhs_is_floating = lhs ? lhs->is_floating : get_global_symbol_floating (name);
+
+ if (op == TOK_ASSIGN && lhs_size > (DATA_LLONG & 0x1f) && tok.kind == TOK_IDENT) {
+
+ char *rhs_name = xstrdup (tok.ident);
+
+ struct local_symbol *rhs_sym;
+ int rhs_global_index;
+
+ get_token ();
+
+ rhs_sym = find_local_symbol (rhs_name);
+ rhs_global_index = find_global_symbol (rhs_name);
+
+ if (rhs_sym || rhs_global_index >= 0) {
+
+ if (tok.kind == TOK_LPAREN && rhs_global_index >= 0 && get_global_symbol_kind (rhs_name) == GLOBAL_SYMBOL_FUNCTION) {
+
+ pending_struct_return_lhs = lhs;
+ pending_struct_return_global_name = lhs ? 0 : name;
+
+ emit_call_identifier_to_reg_now (rhs_name, "eax", name_start, name_caret, name_line);
+
+ } else if (tok.kind == TOK_ARROW || tok.kind == TOK_DOT || tok.kind == TOK_LBRACK) {
+
+ if (emit_parse_postfix_copy_source_address_now ("eax", rhs_sym, rhs_name, name_start, name_caret, name_line)) {
+
+ emit_load_symbol_address_for_copy_now ("edx", lhs, name);
+ emit_copy_fixed_size_now (lhs_size);
+
+ }
+
+ } else {
+ emit_memcpy_symbol_to_symbol_now (lhs, name, rhs_sym, rhs_name, lhs_size);
+ }
+
+ } else {
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", rhs_name);
+ }
+
+ free (rhs_name);
+ expect_semi_or_recover ();
+
+ free (name);
+ return 1;
+
+ }
+
+ if (lhs_is_floating) {
+
+ if (!floating_assignment_operator_supported_now (op)) {
+
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "invalid operands to floating assignment operator");
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+
+ } else {
+
+ if (op == TOK_ASSIGN) {
+ emit_load_floating_rhs_expression_now (lhs_size);
+ } else {
+
+ emit_load_floating_symbol_now (lhs, name, lhs_size);
+ emit_load_floating_rhs_expression_now (lhs_size);
+ emit_floating_binary_now (op);
+
+ }
+
+ emit_store_floating_symbol_now (lhs, name, lhs_size);
+
+ }
+
+ } else if (lhs_size == (DATA_LLONG & 0x1f)) {
+
+ if (op == TOK_ASSIGN) {
+ emit_load_assignment_rhs_expression_to_pair ("eax", "edx", lhs ? lhs->is_unsigned : get_global_symbol_unsigned (name));
+ } else {
+
+ if (lhs) {
+
+ if (lhs->is_static && lhs->static_label) {
+ emit_load_global64_to_pair ("eax", "edx", lhs->static_label);
+ } else {
+ emit_load_local64_to_pair (lhs->offset, "eax", "edx");
+ }
+
+ } else {
+ emit_load_global64_to_pair ("eax", "edx", name);
+ }
+
+ /*
+ * Compound assignments need the complete RHS expression.
+ * Using emit_load_assignment_rhs_to_pair() only consumes one
+ * primary operand, so e.g.
+ *
+ * final_value += symbol->frag->address + left_value;
+ *
+ * leaves the second + operand for the statement parser and
+ * reports "expected ;". Evaluate the full RHS in eax:edx,
+ * copy it to the RHS pair ebx:ecx, then restore the original
+ * LHS value before applying the compound operator.
+ */
+ emit_push_reg_now ("eax");
+ emit_push_reg_now ("edx");
+
+ emit_load_assignment_rhs_expression_to_pair ("eax", "edx", lhs ? lhs->is_unsigned : get_global_symbol_unsigned (name));
+
+ emit_mov_reg_to_reg_now ("ebx", "eax");
+ emit_mov_reg_to_reg_now ("ecx", "edx");
+
+ emit_pop_reg_now ("edx");
+ emit_pop_reg_now ("eax");
+
+ emit_preserve_assignment64_regs (op);
+ emit_assignment_binary_op64 (op, lhs ? lhs->is_unsigned : get_global_symbol_unsigned (name));
+ emit_restore_assignment64_regs (op);
+
+ }
+
+ if (lhs) {
+
+ if (lhs->is_static && lhs->static_label) {
+ emit_store_pair_to_global64 (lhs->static_label, "eax", "edx");
+ } else {
+ emit_store_pair_to_local64 (lhs->offset, "eax", "edx");
+ }
+
+ } else {
+ emit_store_pair_to_global64 (name, "eax", "edx");
+ }
+
+ } else {
+
+ if (op == TOK_ASSIGN) {
+ emit_load_assignment_rhs_expression_to_reg ("eax");
+ } else {
+
+ if (lhs) {
+
+ if (lhs->is_static && lhs->static_label) {
+ emit_load_global_to_reg ("eax", lhs->static_label, lhs->size);
+ } else {
+ emit_load_local_to_reg ("eax", lhs->offset, lhs->size);
+ }
+
+ } else {
+ emit_load_global_to_reg ("eax", name, lhs_size);
+ }
+
+ /*
+ * Compound assignments still need the full RHS expression,
+ * not just a single operand. Otherwise cases such as:
+ *
+ * processed += (int)(t - stream->upto) - 1;
+ *
+ * leave the trailing "- 1" unconsumed and the statement
+ * parser reports "expected ;". Preserve the current LHS
+ * value in eax while the RHS expression is parsed, because
+ * RHS binary-expression code may use eax internally even
+ * when the requested result register is edx.
+ */
+ emit_push_reg_now ("eax");
+ emit_load_assignment_rhs_expression_to_reg ("edx");
+
+ emit_scale_reg_for_pointer_compound_assignment_now ("edx", lhs, name, op);
+ emit_pop_reg_now ("eax");
+
+ emit_assignment_binary_op (op, lhs ? lhs->is_unsigned : get_global_symbol_unsigned (name));
+
+ }
+
+ if (lhs) {
+
+ if (lhs->is_static && lhs->static_label) {
+ emit_store_reg_to_global (lhs->static_label, lhs->size, "eax");
+ } else {
+ emit_store_reg_to_local (lhs->offset, lhs->size, "eax");
+ }
+
+ } else {
+ emit_store_reg_to_global (name, lhs_size, "eax");
+ }
+
+ }
+
+ } else {
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+ }
+
+ expect_semi_or_recover ();
+
+ free (name);
+ return 1;
+
+}
+
+static void emit_statement_label_raw (int label) {
+
+ if (!state->ofp) {
+ return;
+ }
+
+ if ((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) {
+ fprintf (state->ofp, "L%d:\n", label);
+ } else {
+ fprintf (state->ofp, ".L%d:\n", label);
+ }
+
+}
+
+static void flush_pending_statement_labels (void) {
+
+ int i;
+ int count = pending_statement_label_count;
+
+ /**
+ * A queued case/default/C label marks the next statement. If the
+ * previous statement was a return, its jump to the common epilogue may
+ * still be pending. Emit that jump before placing the next label,
+ * otherwise the label lands between the return value setup and the
+ * deferred jump.
+ *
+ * Also flush the deferred return before any other emitted control-flow
+ * boundary even when there are no queued labels. A switch body is emitted
+ * into a temporary stream and then replayed before the switch break label;
+ * if the last case returns, there may be no following case/default label to
+ * force this flush.
+ */
+ if (pending_return_jump) {
+ emit_pending_return_jump ();
+ }
+
+ if (count <= 0) {
+ return;
+ }
+
+ pending_statement_label_count = 0;
+
+ for (i = 0; i < count; i++) {
+ emit_statement_label_raw (pending_statement_labels[i]);
+ }
+
+}
+
+static void emit_statement_label (int label) {
+
+ flush_pending_statement_labels ();
+ emit_statement_label_raw (label);
+
+}
+
+static void emit_statement_jump (int label) {
+
+ flush_pending_statement_labels ();
+
+ if (!state->ofp) {
+ return;
+ }
+
+ if ((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) {
+ fprintf (state->ofp, " jmp L%d\n", label);
+ } else {
+ fprintf (state->ofp, " jmp .L%d\n", label);
+ }
+
+}
+
+static int token_is_integer_constant_now (enum token_kind k) {
+ return k == TOK_CINT || k == TOK_CLONG || k == TOK_CLLONG || k == (enum token_kind) (TOK_CINT + 1) || k == (enum token_kind) (TOK_CLONG + 1) || k == (enum token_kind) (TOK_CLLONG + 1);
+}
+
+static int token_is_integer_unsigned_constant_now (enum token_kind k) {
+ return k == (enum token_kind) (TOK_CINT + 1) || k == (enum token_kind) (TOK_CLONG + 1) || k == (enum token_kind) (TOK_CLLONG + 1);
+}
+
+#define MAX_INLINE_ASM_INPUTS 16
+
+struct inline_asm_input_operand {
+
+ char constraint[16];
+ char subst[64];
+ int restore_reg;
+ const char *restore_name;
+
+};
+
+static void inline_asm_copy_trimmed_text (char *dst, size_t dst_size, const char *start, const char *end) {
+
+ size_t len;
+
+ if (!dst || dst_size == 0) {
+ return;
+ }
+
+ dst[0] = 0;
+
+ if (!start || !end || end < start) {
+ return;
+ }
+
+ while (start < end && (*start == ' ' || *start == '\t' || *start == '\r' || *start == '\n')) {
+ start++;
+ }
+
+ while (end > start && (end[-1] == ' ' || end[-1] == '\t' || end[-1] == '\r' || end[-1] == '\n')) {
+ end--;
+ }
+
+ len = (size_t) (end - start);
+
+ if (len >= dst_size) {
+ len = dst_size - 1;
+ }
+
+ memcpy (dst, start, len);
+ dst[len] = 0;
+
+}
+
+static void inline_asm_unquote_string_token (char *dst, size_t dst_size) {
+
+ const char *s;
+ char quote;
+ size_t n = 0;
+
+ if (!dst || dst_size == 0) {
+ return;
+ }
+
+ dst[0] = 0;
+
+ if (!is_string_token ()) {
+ return;
+ }
+
+ s = tok.ident;
+
+ if (tok.kind == TOK_LSTR && *s == 'L') {
+ s++;
+ }
+
+ quote = *s;
+
+ if (quote != '"') {
+ return;
+ }
+
+ s++;
+
+ while (*s && *s != quote && n + 1 < dst_size) {
+
+ if (*s == '\\' && s[1]) {
+
+ s++;
+
+ switch (*s) {
+
+ case 'n':
+
+ dst[n++] = '\n'; s++;
+ break;
+
+ case 'r':
+
+ dst[n++] = '\r'; s++;
+ break;
+
+ case 't':
+
+ dst[n++] = '\t'; s++;
+ break;
+
+ case '\\':
+
+ dst[n++] = '\\'; s++;
+ break;
+
+ case '"':
+
+ dst[n++] = '"'; s++;
+ break;
+
+ default:
+
+ dst[n++] = *s++;
+ break;
+
+ }
+
+ } else {
+ dst[n++] = *s++;
+ }
+
+ }
+
+ dst[n] = 0;
+
+}
+
+static const char *inline_asm_constraint_reg32 (const char *constraint) {
+
+ if (!constraint) {
+ return 0;
+ }
+
+ if (strchr (constraint, 'a')) {
+ return "eax";
+ }
+
+ if (strchr (constraint, 'b')) {
+ return "ebx";
+ }
+
+ if (strchr (constraint, 'c')) {
+ return "ecx";
+ }
+
+ if (strchr (constraint, 'd')) {
+ return "edx";
+ }
+
+ if (strchr (constraint, 'S')) {
+ return "esi";
+ }
+
+ if (strchr (constraint, 'D')) {
+ return "edi";
+ }
+
+ return 0;
+
+}
+
+static const char *inline_asm_reg_for_template (const char *reg32) {
+
+ if (!reg32) {
+ return "";
+ }
+
+ if (strcmp (reg32, "eax") == 0) {
+ return "al";
+ }
+
+ if (strcmp (reg32, "ebx") == 0) {
+ return "bl";
+ }
+
+ if (strcmp (reg32, "ecx") == 0) {
+ return "cl";
+ }
+
+ if (strcmp (reg32, "edx") == 0) {
+ return "dx";
+ }
+
+ if (strcmp (reg32, "esi") == 0) {
+ return "esi";
+ }
+
+ if (strcmp (reg32, "edi") == 0) {
+ return "edi";
+ }
+
+ return reg32;
+
+}
+
+static int inline_asm_reg_needs_restore (const char *reg32) {
+ return reg32 && (strcmp (reg32, "ebx") == 0 || strcmp (reg32, "esi") == 0 || strcmp (reg32, "edi") == 0);
+}
+
+static int inline_asm_template_is_out (const char *templ) {
+
+ if (!templ) {
+ return 0;
+ }
+
+ while (*templ == ' ' || *templ == '\t') {
+ templ++;
+ }
+
+ return strncmp (templ, "outb ", 5) == 0 || strncmp (templ, "outw ", 5) == 0 ||
+ strncmp (templ, "outl ", 5) == 0 || strncmp (templ, "out ", 4) == 0;
+
+}
+
+static int inline_asm_is_reg32_name (const char *s, size_t len) {
+
+ return (len == 3 && strncmp (s, "eax", 3) == 0) ||
+ (len == 3 && strncmp (s, "ebx", 3) == 0) ||
+ (len == 3 && strncmp (s, "ecx", 3) == 0) ||
+ (len == 3 && strncmp (s, "edx", 3) == 0) ||
+ (len == 3 && strncmp (s, "esi", 3) == 0) ||
+ (len == 3 && strncmp (s, "edi", 3) == 0);
+
+}
+
+static int inline_asm_rewrite_narrow_movzx (char *line, size_t line_size) {
+
+ char rewritten[512];
+
+ const char *p;
+ const char *reg_start;
+ const char *reg_end;
+ const char *src;
+
+ size_t reg_len;
+ size_t indent_len;
+
+ if (!line || line_size == 0 || !(state->syntax & ASM_SYNTAX_INTEL)) {
+ return 0;
+ }
+
+ p = line;
+
+ while (*p == ' ' || *p == '\t') {
+ p++;
+ }
+
+ indent_len = (size_t) (p - line);
+
+ if (strncmp (p, "mov", 3) != 0 || (p[3] != ' ' && p[3] != '\t')) {
+ return 0;
+ }
+
+ p += 3;
+
+ while (*p == ' ' || *p == '\t') {
+ p++;
+ }
+
+ reg_start = p;
+
+ while ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') || (*p >= '0' && *p <= '9')) {
+ p++;
+ }
+
+ reg_end = p;
+ reg_len = (size_t) (reg_end - reg_start);
+
+ if (!inline_asm_is_reg32_name (reg_start, reg_len)) {
+ return 0;
+ }
+
+ while (*p == ' ' || *p == '\t') {
+ p++;
+ }
+
+ if (*p != ',') {
+ return 0;
+ }
+
+ p++;
+
+ while (*p == ' ' || *p == '\t') {
+ p++;
+ }
+
+ src = p;
+
+ /**
+ * Only change true narrow memory loads. This deliberately does not
+ * touch register/register moves, immediates, or lines that have already
+ * been rewritten.
+ */
+ if (strncmp (src, "byte ptr ", 9) != 0 && strncmp (src, "word ptr ", 9) != 0) {
+ return 0;
+ }
+
+ if (indent_len + 6 + reg_len + 2 + strlen (src) + 1 >= sizeof (rewritten)) {
+ return 0;
+ }
+
+ memcpy (rewritten, line, indent_len);
+ rewritten[indent_len] = 0;
+
+ strcat (rewritten, "movzx ");
+ strncat (rewritten, reg_start, reg_len);
+ strcat (rewritten, ", ");
+ strcat (rewritten, src);
+
+ inline_copy_string (line, rewritten, line_size);
+ return 1;
+
+}
+
+static const char *inline_asm_intel_ptr_name (int size) {
+
+ if (size <= 1) {
+ return "byte";
+ }
+
+ if (size == 2) {
+ return "word";
+ }
+
+ if (size == 8) {
+ return "qword";
+ }
+
+ return "dword";
+
+}
+
+static int inline_asm_format_identifier_operand (char *dst, size_t dst_size, const char *name) {
+
+ struct local_symbol *sym;
+ char memref[64];
+ const char *asm_name;
+
+ if (!dst || dst_size == 0 || !name || !*name) {
+ return 0;
+ }
+
+ dst[0] = 0;
+ sym = find_local_symbol (name);
+
+ if (sym) {
+
+ if (sym->is_static && sym->static_label) {
+
+ asm_name = asm_global_symbol_name (sym->static_label);
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ sprintf (dst, (state->syntax & ASM_SYNTAX_NASM ? "%s %s" : "%s ptr %s"), inline_asm_intel_ptr_name (sym->size), asm_name);
+ } else {
+ sprintf (dst, "%s", asm_name);
+ }
+
+ return 1;
+
+ }
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ format_intel_ebp_offset (memref, sizeof (memref), sym->offset);
+ sprintf (dst, (state->syntax & ASM_SYNTAX_NASM ? "%s %s" : "%s ptr %s"), inline_asm_intel_ptr_name (sym->size), memref);
+
+ } else {
+ sprintf (dst, "%ld(%%ebp)", sym->offset);
+ }
+
+ return 1;
+
+ }
+
+ if (find_global_symbol (name) >= 0) {
+
+ asm_name = asm_global_symbol_name (name);
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ sprintf (dst, (state->syntax & ASM_SYNTAX_NASM ? "%s %s" : "%s ptr %s"), inline_asm_intel_ptr_name (get_global_symbol_size (name)), asm_name);
+ } else {
+ sprintf (dst, "%s", asm_name);
+ }
+
+ return 1;
+
+ }
+
+ return 0;
+
+}
+
+static void inline_asm_emit_input_load (const char *constraint, int input_index, struct inline_asm_input_operand *inputs, const char *templ) {
+
+ char expr_text[64];
+
+ const char *expr_start;
+ const char *expr_end;
+ const char *reg32;
+ const char *subst;
+
+ expr_text[0] = 0;
+ expr_start = tok.caret;
+
+ inputs[input_index].restore_reg = 0;
+ inputs[input_index].restore_name = 0;
+
+ if (strchr (constraint, 'N')) {
+
+ if (token_is_integer_constant_now (tok.kind)) {
+
+ skip_balanced_until (TOK_RPAREN, TOK_EOF, TOK_EOF);
+ expr_end = tok.caret;
+
+ inline_asm_copy_trimmed_text (expr_text, sizeof (expr_text), expr_start, expr_end);
+ inline_copy_string (inputs[input_index].subst, expr_text, sizeof (inputs[input_index].subst));
+
+ return;
+
+ }
+
+ if (inline_asm_template_is_out (templ)) {
+
+ /**
+ * GCC's "N" constraint is really an 8-bit immediate port.
+ * For the OUT templates, allow a non-constant here as a
+ * convenience and lower it through DX, because x86 OUT cannot
+ * encode a variable port as an immediate.
+ */
+ emit_load_assignment_rhs_expression_to_reg ("edx");
+ inline_copy_string (inputs[input_index].subst, "dx", sizeof (inputs[input_index].subst));
+
+ return;
+
+ }
+
+ if (tok.kind == TOK_IDENT && tok.ident && inline_asm_format_identifier_operand (expr_text, sizeof (expr_text), tok.ident)) {
+
+ skip_balanced_until (TOK_RPAREN, TOK_EOF, TOK_EOF);
+ inline_copy_string (inputs[input_index].subst, expr_text, sizeof (inputs[input_index].subst));
+
+ return;
+
+ }
+
+ skip_balanced_until (TOK_RPAREN, TOK_EOF, TOK_EOF);
+ expr_end = tok.caret;
+
+ inline_asm_copy_trimmed_text (expr_text, sizeof (expr_text), expr_start, expr_end);
+ inline_copy_string (inputs[input_index].subst, expr_text, sizeof (inputs[input_index].subst));
+
+ return;
+
+ }
+
+ reg32 = inline_asm_constraint_reg32 (constraint);
+
+ if (reg32) {
+
+ subst = inline_asm_reg_for_template (reg32);
+
+ if (inline_asm_reg_needs_restore (reg32) && state->ofp) {
+
+ fprintf (state->ofp, " push %s\n", reg32);
+
+ inputs[input_index].restore_reg = 1;
+ inputs[input_index].restore_name = reg32;
+
+ }
+
+ if (token_is_integer_constant_now (tok.kind)) {
+
+ skip_balanced_until (TOK_RPAREN, TOK_EOF, TOK_EOF);
+ expr_end = tok.caret;
+
+ inline_asm_copy_trimmed_text (expr_text, sizeof (expr_text), expr_start, expr_end);
+
+ if (state->ofp) {
+ fprintf (state->ofp, " mov %s, %s\n", subst, expr_text[0] ? expr_text : "0");
+ }
+
+ } else {
+ emit_load_assignment_rhs_expression_to_reg (reg32);
+ }
+
+ inline_copy_string (inputs[input_index].subst, subst, sizeof (inputs[input_index].subst));
+ return;
+
+ }
+
+ if (tok.kind == TOK_IDENT && tok.ident && inline_asm_format_identifier_operand (expr_text, sizeof (expr_text), tok.ident)) {
+
+ skip_balanced_until (TOK_RPAREN, TOK_EOF, TOK_EOF);
+ inline_copy_string (inputs[input_index].subst, expr_text, sizeof (inputs[input_index].subst));
+
+ return;
+
+ }
+
+ skip_balanced_until (TOK_RPAREN, TOK_EOF, TOK_EOF);
+ expr_end = tok.caret;
+
+ inline_asm_copy_trimmed_text (expr_text, sizeof (expr_text), expr_start, expr_end);
+ inline_copy_string (inputs[input_index].subst, expr_text, sizeof (inputs[input_index].subst));
+
+}
+
+static void inline_asm_emit_template (const char *templ, struct inline_asm_input_operand *inputs, int input_count) {
+
+ char line[512];
+ size_t n = 0;
+
+ const char *p;
+
+ if (!state->ofp || !templ) {
+ return;
+ }
+
+ for (p = templ; *p && n + 1 < sizeof (line); p++) {
+
+ if (*p == '%' && p[1] == '%') {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ p++;
+ } else {
+
+ line[n++] = '%';
+ p++;
+
+ }
+
+ } else if (*p == '%' && p[1] >= '0' && p[1] <= '9') {
+
+ int index = p[1] - '0';
+ const char *subst = "";
+
+ if (index >= 0 && index < input_count) {
+ subst = inputs[index].subst;
+ }
+
+ while (*subst && n + 1 < sizeof (line)) {
+ line[n++] = *subst++;
+ }
+
+ p++;
+
+ } else {
+ line[n++] = *p;
+ }
+
+ }
+
+ line[n] = 0;
+
+ if ((strncmp (line, "outb ", 5) == 0 || strncmp (line, "outw ", 5) == 0 || strncmp (line, "outl ", 5) == 0 || strncmp (line, "out ", 4) == 0)) {
+
+ char *args;
+ char *comma;
+ char *lhs;
+ char *rhs;
+
+ args = strchr (line, ' ');
+
+ if (args) {
+
+ args++;
+ comma = strchr (args, ',');
+
+ if (comma) {
+
+ *comma = 0;
+
+ lhs = args;
+ rhs = comma + 1;
+
+ while (*lhs == ' ' || *lhs == '\t') {
+ lhs++;
+ }
+
+ while (*rhs == ' ' || *rhs == '\t') {
+ rhs++;
+ }
+
+ fprintf (state->ofp, " out %s, %s\n", rhs, lhs);
+
+ while (input_count-- > 0) {
+
+ if (inputs[input_count].restore_reg && inputs[input_count].restore_name) {
+ fprintf (state->ofp, " pop %s\n", inputs[input_count].restore_name);
+ }
+
+ }
+
+ return;
+
+ }
+
+ }
+
+ }
+
+ if (line[0]) {
+
+ inline_asm_rewrite_narrow_movzx (line, sizeof (line));
+ fprintf (state->ofp, " %s\n", line);
+
+ }
+
+ while (input_count-- > 0) {
+
+ if (inputs[input_count].restore_reg && inputs[input_count].restore_name) {
+ fprintf (state->ofp, " pop %s\n", inputs[input_count].restore_name);
+ }
+
+ }
+
+}
+
+static int parse_inline_asm_statement (void) {
+
+ char templ[256];
+
+ struct inline_asm_input_operand inputs[MAX_INLINE_ASM_INPUTS];
+ int input_count = 0;
+
+ if (tok.kind != TOK_ASM) {
+ return 0;
+ }
+
+ memset (inputs, 0, sizeof (inputs));
+
+ get_token ();
+ expect (TOK_LPAREN, "(");
+
+ if (!is_string_token ()) {
+
+ report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "expected asm template string");
+
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+ expect (TOK_SEMI, ";");
+
+ return 1;
+
+ }
+
+ inline_asm_unquote_string_token (templ, sizeof (templ));
+ get_token ();
+
+ if (_accept (TOK_COLON)) {
+
+ if (tok.kind != TOK_COLON) {
+ skip_balanced_until (TOK_COLON, TOK_RPAREN, TOK_EOF);
+ }
+
+ if (_accept (TOK_COLON)) {
+
+ while (tok.kind != TOK_RPAREN && tok.kind != TOK_EOF) {
+
+ char constraint[16];
+
+ if (!is_string_token ()) {
+
+ report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "expected asm input constraint string");
+
+ skip_balanced_until (TOK_RPAREN, TOK_EOF, TOK_EOF);
+ break;
+
+ }
+
+ inline_asm_unquote_string_token (constraint, sizeof (constraint));
+ get_token ();
+
+ expect (TOK_LPAREN, "(");
+
+ if (input_count < MAX_INLINE_ASM_INPUTS) {
+
+ inline_copy_string (inputs[input_count].constraint, constraint, sizeof (inputs[input_count].constraint));
+ inline_asm_emit_input_load (constraint, input_count, inputs, templ);
+
+ input_count++;
+
+ } else {
+ skip_balanced_until (TOK_RPAREN, TOK_EOF, TOK_EOF);
+ }
+
+ expect (TOK_RPAREN, ")");
+
+ if (!_accept (TOK_COMMA)) {
+ break;
+ }
+
+ }
+
+ }
+
+ }
+
+ expect (TOK_RPAREN, ")");
+ expect (TOK_SEMI, ";");
+
+ inline_asm_emit_template (templ, inputs, input_count);
+ return 1;
+
+}
+
+static int token_is_const_condition_operand_now (void) {
+ return token_is_sizeof_keyword () || token_is_integer_constant_now (tok.kind);
+}
+
+static int token_is_const_floating_condition_operand_now (void) {
+ return token_is_floating_constant_now ();
+}
+
+static int ident_char_now (int ch) {
+ return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || ch == '_';
+}
+
+static int ident_start_now (int ch) {
+ return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || ch == '_';
+}
+
+static int prefix_incdec_target_is_floating_now (void) {
+
+ const char *p;
+ const char *q;
+
+ char *name;
+ int len;
+
+ struct local_symbol *src;
+ int ret = 0;
+
+ if (tok.kind != TOK_INCR && tok.kind != TOK_DECR) {
+ return 0;
+ }
+
+ p = tok.caret;
+
+ while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+ p++;
+ }
+
+ if (!ident_start_now ((unsigned char) *p)) {
+ return 0;
+ }
+
+ q = p + 1;
+
+ while (ident_char_now ((unsigned char) *q)) {
+ q++;
+ }
+
+ len = (int) (q - p);
+ name = xmalloc ((unsigned long) len + 1);
+
+ memcpy (name, p, (unsigned long) len);
+ name[len] = 0;
+
+ src = find_local_symbol (name);
+
+ if (src) {
+ ret = src->is_floating ? 1 : 0;
+ } else if (find_global_symbol (name) >= 0) {
+ ret = get_global_symbol_floating (name) ? 1 : 0;
+ }
+
+ free (name);
+ return ret;
+
+}
+
+static int source_parenthesized_floating_condition_now (const char *p) {
+
+ char name[128];
+ int i;
+
+ if (!p) {
+ return 0;
+ }
+
+ while (*p == ' ' || *p == '\t') {
+ p++;
+ }
+
+ if (*p != '(') {
+ return 0;
+ }
+
+ while (*p == '(' || *p == ' ' || *p == '\t') {
+ p++;
+ }
+
+ if (!((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || *p == '_')) {
+ return 0;
+ }
+
+ i = 0;
+
+ while (((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || (*p >= '0' && *p <= '9') || *p == '_') && i < (int) sizeof (name) - 1) {
+ name[i++] = *p++;
+ }
+
+ name[i] = 0;
+
+ while (*p == ' ' || *p == '\t') {
+ p++;
+ }
+
+ if (*p == '.') {
+ return 0;
+ }
+
+ if (*p == '(' && find_global_symbol (name) >= 0 && get_global_symbol_floating (name)) {
+ return 1;
+ }
+
+ return 0;
+
+}
+
+static int rhs_current_operand_is_floating_now (void) {
+
+ if (token_is_floating_constant_now ()) {
+ return 1;
+ }
+
+ if (tok.kind == TOK_LPAREN && (source_parenthesized_floating_condition_now (tok.caret) || source_parenthesized_floating_condition_now (tok.start))) {
+ return 1;
+ }
+
+ if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) {
+ return prefix_incdec_target_is_floating_now ();
+ }
+
+ if (tok.kind == TOK_IDENT && tok.ident) {
+
+ struct local_symbol *src = find_local_symbol (tok.ident);
+ const char *p = tok.caret ? tok.caret + tok.len : 0;
+
+ while (p && (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n')) {
+ p++;
+ }
+
+ if (p && *p == '(' && find_global_symbol (tok.ident) >= 0) {
+ return get_global_symbol_floating (tok.ident) ? 1 : 0;
+ }
+
+ if (p && ((p[0] == '-' && p[1] == '>') || p[0] == '.')) {
+
+ const char *q = p + (p[0] == '-' ? 2 : 1);
+ char member[128];
+
+ int n = 0;
+ int offset = 0;
+ int size = 0;
+ int elem_size = 0;
+ int pointer_depth = 0;
+ int is_array = 0;
+ int is_floating = 0;
+ int base_size = 0;
+
+ const char *base_tag_name = 0;
+
+ while (*q == ' ' || *q == '\t' || *q == '\r' || *q == '\n') {
+ q++;
+ }
+
+ while (((*q >= 'A' && *q <= 'Z') || (*q >= 'a' && *q <= 'z') || (*q >= '0' && *q <= '9') || *q == '_') && n < (int) sizeof (member) - 1) {
+ member[n++] = *q++;
+ }
+
+ member[n] = '\0';
+
+ if (n > 0) {
+
+ if (p[0] == '-') {
+
+ if (src) {
+
+ base_size = src->pointed_size;
+ base_tag_name = src->pointed_tag_name;
+
+ } else if (find_global_symbol (tok.ident) >= 0) {
+
+ base_size = get_global_symbol_pointed_size (tok.ident);
+ base_tag_name = get_global_symbol_tag_name (tok.ident);
+
+ }
+
+ } else {
+
+ if (src) {
+
+ base_size = src->size;
+ base_tag_name = src->tag_name;
+
+ } else if (find_global_symbol (tok.ident) >= 0) {
+
+ base_size = get_global_symbol_size (tok.ident);
+ base_tag_name = get_global_symbol_tag_name (tok.ident);
+
+ }
+
+ }
+
+ if (find_member_info_ex_bounded (member, base_size, base_tag_name, &offset, &size, &elem_size, &pointer_depth, &is_array, &is_floating)) {
+ return is_floating ? 1 : 0;
+ }
+
+ }
+
+ }
+
+ if (src) {
+ return src->is_floating ? 1 : 0;
+ }
+
+ if (find_global_symbol (tok.ident) >= 0) {
+ return get_global_symbol_floating (tok.ident) ? 1 : 0;
+ }
+
+ }
+
+ return 0;
+
+}
+
+static int64_s floating_ld_to_bits_now (int size, long double value) {
+
+ int64_s r;
+
+ unsigned long bits32;
+ unsigned char bytes[8];
+
+ int i;
+
+ r.low = 0;
+ r.high = 0;
+
+ if (size == (DATA_FLOAT & 0x1f)) {
+
+ float f = (float) value;
+
+ bits32 = 0;
+
+ memcpy (&bits32, &f, sizeof (f));
+ r.low = bits32;
+
+ return r;
+
+ }
+
+ {
+
+ double d = (double) value;
+
+ memset (bytes, 0, sizeof (bytes));
+ memcpy (bytes, &d, sizeof (d));
+
+ for (i = 0; i < 4; i++) {
+ r.low |= ((unsigned long) bytes[i]) << (i * 8);
+ }
+
+ for (i = 0; i < 4; i++) {
+ r.high |= ((unsigned long) bytes[i + 4]) << (i * 8);
+ }
+
+ return r;
+
+ }
+
+}
+
+static void emit_load_floating_ld_now (int size, long double value) {
+ emit_load_floating_const_bits_now (size, floating_ld_to_bits_now (size, value));
+}
+
+static int int64_statement_truth_value (int64_s v) {
+ return v.low != 0 || v.high != 0;
+}
+
+static long statement_int64_signed_high (int64_s v) {
+
+ unsigned long h = v.high & U32_MASK;
+
+ if (h & 0x80000000UL) {
+ return -((long) ((~h + 1UL) & U32_MASK));
+ }
+
+ return (long) h;
+
+}
+
+static int statement_cmp_const64_unsigned (int64_s left, int64_s right) {
+
+ unsigned long lh = left.high & U32_MASK;
+ unsigned long rh = right.high & U32_MASK;
+ unsigned long ll = left.low & U32_MASK;
+ unsigned long rl = right.low & U32_MASK;
+
+ if (lh < rh) return -1;
+ if (lh > rh) return 1;
+ if (ll < rl) return -1;
+ if (ll > rl) return 1;
+
+ return 0;
+
+}
+
+static int statement_cmp_const64_signed (int64_s left, int64_s right) {
+
+ long lh = statement_int64_signed_high (left);
+ long rh = statement_int64_signed_high (right);
+
+ unsigned long ll = left.low & U32_MASK;
+ unsigned long rl = right.low & U32_MASK;
+
+ if (lh < rh) return -1;
+ if (lh > rh) return 1;
+ if (ll < rl) return -1;
+ if (ll > rl) return 1;
+
+ return 0;
+
+}
+
+static int statement_compare_const64_true (int64_s left, enum token_kind op, int64_s right, int is_unsigned) {
+
+ int c = is_unsigned ? statement_cmp_const64_unsigned (left, right) : statement_cmp_const64_signed (left, right);
+
+ switch (op) {
+
+ case TOK_LESS:
+
+ return c < 0;
+
+ case TOK_LTEQ:
+
+ return c <= 0;
+
+ case TOK_GREATER:
+
+ return c > 0;
+
+ case TOK_GTEQ:
+
+ return c >= 0;
+
+ case TOK_EQEQ:
+
+ return c == 0;
+
+ case TOK_NOTEQ:
+
+ return c != 0;
+
+ default:
+
+ return int64_statement_truth_value (left);
+
+ }
+
+}
+
+static int rhs_current_operand_is_unsigned_now (void) {
+
+ if (token_is_sizeof_keyword ()) {
+ return 1;
+ }
+
+ if (token_is_integer_unsigned_constant_now (tok.kind)) {
+ return 1;
+ }
+
+ if (tok.kind == TOK_IDENT && tok.ident) {
+
+ struct local_symbol *src = find_local_symbol (tok.ident);
+
+ if (src) {
+ return src->is_unsigned ? 1 : 0;
+ }
+
+ if (find_global_symbol (tok.ident) >= 0) {
+ return get_global_symbol_unsigned (tok.ident) ? 1 : 0;
+ }
+
+ }
+
+ return 0;
+
+}
+
+static int token_is_statement_compare_operator (enum token_kind k) {
+ return k == TOK_LESS || k == TOK_LTEQ || k == TOK_GREATER || k == TOK_GTEQ || k == TOK_EQEQ || k == TOK_NOTEQ;
+}
+
+static int statement_condition_ident_call_at (const char *p) {
+
+ int len;
+
+ if (tok.kind != TOK_IDENT || !tok.ident || !p) {
+ return 0;
+ }
+
+ len = (int) strlen (tok.ident);
+
+ while (*p && *p != '\n') {
+
+ if ((p == tok.start || !ident_char_now ((unsigned char) p[-1])) && strncmp (p, tok.ident, (unsigned long) len) == 0 && !ident_char_now ((unsigned char) p[len])) {
+
+ p += len;
+
+ while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+ p++;
+ }
+
+ return *p == '(';
+
+ }
+
+ p++;
+
+ }
+
+ return 0;
+
+}
+
+static int statement_condition_starts_with_ident_call_now (void) {
+
+ if (statement_condition_ident_call_at (tok.caret)) {
+ return 1;
+ }
+
+ if (statement_condition_ident_call_at (tok.start)) {
+ return 1;
+ }
+
+ return 0;
+
+}
+
+static const char *statement_false_jump_mnemonic (enum token_kind op, int is_unsigned) {
+
+ switch (op) {
+
+ case TOK_LESS:
+
+ return is_unsigned ? "jae" : "jge";
+
+ case TOK_LTEQ:
+
+ return is_unsigned ? "ja" : "jg";
+
+ case TOK_GREATER:
+
+ return is_unsigned ? "jbe" : "jle";
+
+ case TOK_GTEQ:
+
+ return is_unsigned ? "jb" : "jl";
+
+ case TOK_EQEQ:
+
+ return "jne";
+
+ case TOK_NOTEQ:
+
+ return "je";
+
+ default:
+
+ return "jz";
+
+ }
+
+}
+
+static void emit_statement_const32_to_eax (int64_s v) {
+
+ if (!state->ofp) {
+ return;
+ }
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " mov eax, %lu\n", v.low & U32_MASK);
+ } else {
+ fprintf (state->ofp, " movl $%lu, %%eax\n", v.low & U32_MASK);
+ }
+
+}
+
+static void emit_statement_cmp_eax_edx_jump_if_false (enum token_kind op, int is_unsigned, int label) {
+
+ const char *jmp;
+
+ if (!state->ofp) {
+ return;
+ }
+
+ jmp = statement_false_jump_mnemonic (op, is_unsigned);
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ fprintf (state->ofp, " cmp eax, edx\n");
+ fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? " %s L%d\n" : " %s .L%d\n"), jmp, label);
+
+ } else {
+
+ fprintf (state->ofp, " cmpl %%edx, %%eax\n");
+ fprintf (state->ofp, " %s .L%d\n", jmp, label);
+
+ }
+
+}
+
+static void emit_statement_cmp64_to_eax (enum token_kind op, int is_unsigned) {
+
+ int true_label;
+ int false_label;
+ int end_label;
+
+ if (!state->ofp) {
+ return;
+ }
+
+ true_label = anon_label++;
+ false_label = anon_label++;
+ end_label = anon_label++;
+
+ if ((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) {
+
+ switch (op) {
+
+ case TOK_LESS:
+
+ fprintf (state->ofp, " cmp edx, ecx\n");
+ fprintf (state->ofp, is_unsigned ? " jb L%d\n" : " jl L%d\n", true_label);
+ fprintf (state->ofp, is_unsigned ? " ja L%d\n" : " jg L%d\n", false_label);
+ fprintf (state->ofp, " cmp eax, ebx\n");
+ fprintf (state->ofp, " jb L%d\n", true_label);
+ break;
+
+ case TOK_LTEQ:
+
+ fprintf (state->ofp, " cmp edx, ecx\n");
+ fprintf (state->ofp, is_unsigned ? " jb L%d\n" : " jl L%d\n", true_label);
+ fprintf (state->ofp, is_unsigned ? " ja L%d\n" : " jg L%d\n", false_label);
+ fprintf (state->ofp, " cmp eax, ebx\n");
+ fprintf (state->ofp, " jbe L%d\n", true_label);
+ break;
+
+ case TOK_GREATER:
+
+ fprintf (state->ofp, " cmp edx, ecx\n");
+ fprintf (state->ofp, is_unsigned ? " ja L%d\n" : " jg L%d\n", true_label);
+ fprintf (state->ofp, is_unsigned ? " jb L%d\n" : " jl L%d\n", false_label);
+ fprintf (state->ofp, " cmp eax, ebx\n");
+ fprintf (state->ofp, " ja L%d\n", true_label);
+ break;
+
+ case TOK_GTEQ:
+
+ fprintf (state->ofp, " cmp edx, ecx\n");
+ fprintf (state->ofp, is_unsigned ? " ja L%d\n" : " jg L%d\n", true_label);
+ fprintf (state->ofp, is_unsigned ? " jb L%d\n" : " jl L%d\n", false_label);
+ fprintf (state->ofp, " cmp eax, ebx\n");
+ fprintf (state->ofp, " jae L%d\n", true_label);
+ break;
+
+ case TOK_EQEQ:
+
+ fprintf (state->ofp, " cmp edx, ecx\n");
+ fprintf (state->ofp, " jne L%d\n", false_label);
+ fprintf (state->ofp, " cmp eax, ebx\n");
+ fprintf (state->ofp, " je L%d\n", true_label);
+ break;
+
+ case TOK_NOTEQ:
+
+ fprintf (state->ofp, " cmp edx, ecx\n");
+ fprintf (state->ofp, " jne L%d\n", true_label);
+ fprintf (state->ofp, " cmp eax, ebx\n");
+ fprintf (state->ofp, " jne L%d\n", true_label);
+ break;
+
+ default:
+
+ fprintf (state->ofp, " test edx, edx\n");
+ fprintf (state->ofp, " jnz L%d\n", true_label);
+ fprintf (state->ofp, " test eax, eax\n");
+ fprintf (state->ofp, " jnz L%d\n", true_label);
+ break;
+
+ }
+
+ fprintf (state->ofp, "L%d:\n", false_label);
+ fprintf (state->ofp, " xor eax, eax\n");
+ fprintf (state->ofp, " jmp L%d\n", end_label);
+ fprintf (state->ofp, "L%d:\n", true_label);
+ fprintf (state->ofp, " mov eax, 1\n");
+ fprintf (state->ofp, "L%d:\n", end_label);
+
+ } else if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ switch (op) {
+
+ case TOK_LESS:
+
+ fprintf (state->ofp, " cmp edx, ecx\n");
+ fprintf (state->ofp, is_unsigned ? " jb .L%d\n" : " jl .L%d\n", true_label);
+ fprintf (state->ofp, is_unsigned ? " ja .L%d\n" : " jg .L%d\n", false_label);
+ fprintf (state->ofp, " cmp eax, ebx\n");
+ fprintf (state->ofp, " jb .L%d\n", true_label);
+ break;
+
+ case TOK_LTEQ:
+
+ fprintf (state->ofp, " cmp edx, ecx\n");
+ fprintf (state->ofp, is_unsigned ? " jb .L%d\n" : " jl .L%d\n", true_label);
+ fprintf (state->ofp, is_unsigned ? " ja .L%d\n" : " jg .L%d\n", false_label);
+ fprintf (state->ofp, " cmp eax, ebx\n");
+ fprintf (state->ofp, " jbe .L%d\n", true_label);
+ break;
+
+ case TOK_GREATER:
+
+ fprintf (state->ofp, " cmp edx, ecx\n");
+ fprintf (state->ofp, is_unsigned ? " ja .L%d\n" : " jg .L%d\n", true_label);
+ fprintf (state->ofp, is_unsigned ? " jb .L%d\n" : " jl .L%d\n", false_label);
+ fprintf (state->ofp, " cmp eax, ebx\n");
+ fprintf (state->ofp, " ja .L%d\n", true_label);
+ break;
+
+ case TOK_GTEQ:
+
+ fprintf (state->ofp, " cmp edx, ecx\n");
+ fprintf (state->ofp, is_unsigned ? " ja .L%d\n" : " jg .L%d\n", true_label);
+ fprintf (state->ofp, is_unsigned ? " jb .L%d\n" : " jl .L%d\n", false_label);
+ fprintf (state->ofp, " cmp eax, ebx\n");
+ fprintf (state->ofp, " jae .L%d\n", true_label);
+ break;
+
+ case TOK_EQEQ:
+
+ fprintf (state->ofp, " cmp edx, ecx\n");
+ fprintf (state->ofp, " jne .L%d\n", false_label);
+ fprintf (state->ofp, " cmp eax, ebx\n");
+ fprintf (state->ofp, " je .L%d\n", true_label);
+ break;
+
+ case TOK_NOTEQ:
+
+ fprintf (state->ofp, " cmp edx, ecx\n");
+ fprintf (state->ofp, " jne .L%d\n", true_label);
+ fprintf (state->ofp, " cmp eax, ebx\n");
+ fprintf (state->ofp, " jne .L%d\n", true_label);
+ break;
+
+ default:
+
+ fprintf (state->ofp, " test edx, edx\n");
+ fprintf (state->ofp, " jnz .L%d\n", true_label);
+ fprintf (state->ofp, " test eax, eax\n");
+ fprintf (state->ofp, " jnz .L%d\n", true_label);
+ break;
+
+ }
+
+ fprintf (state->ofp, ".L%d:\n", false_label);
+ fprintf (state->ofp, " xor eax, eax\n");
+ fprintf (state->ofp, " jmp .L%d\n", end_label);
+ fprintf (state->ofp, ".L%d:\n", true_label);
+ fprintf (state->ofp, " mov eax, 1\n");
+ fprintf (state->ofp, ".L%d:\n", end_label);
+
+ } else {
+
+ switch (op) {
+
+ case TOK_LESS:
+
+ fprintf (state->ofp, " cmpl %%ecx, %%edx\n");
+ fprintf (state->ofp, is_unsigned ? " jb .L%d\n" : " jl .L%d\n", true_label);
+ fprintf (state->ofp, is_unsigned ? " ja .L%d\n" : " jg .L%d\n", false_label);
+ fprintf (state->ofp, " cmpl %%ebx, %%eax\n");
+ fprintf (state->ofp, " jb .L%d\n", true_label);
+ break;
+
+ case TOK_LTEQ:
+
+ fprintf (state->ofp, " cmpl %%ecx, %%edx\n");
+ fprintf (state->ofp, is_unsigned ? " jb .L%d\n" : " jl .L%d\n", true_label);
+ fprintf (state->ofp, is_unsigned ? " ja .L%d\n" : " jg .L%d\n", false_label);
+ fprintf (state->ofp, " cmpl %%ebx, %%eax\n");
+ fprintf (state->ofp, " jbe .L%d\n", true_label);
+ break;
+
+ case TOK_GREATER:
+
+ fprintf (state->ofp, " cmpl %%ecx, %%edx\n");
+ fprintf (state->ofp, is_unsigned ? " ja .L%d\n" : " jg .L%d\n", true_label);
+ fprintf (state->ofp, is_unsigned ? " jb .L%d\n" : " jl .L%d\n", false_label);
+ fprintf (state->ofp, " cmpl %%ebx, %%eax\n");
+ fprintf (state->ofp, " ja .L%d\n", true_label);
+ break;
+
+ case TOK_GTEQ:
+
+ fprintf (state->ofp, " cmpl %%ecx, %%edx\n");
+ fprintf (state->ofp, is_unsigned ? " ja .L%d\n" : " jg .L%d\n", true_label);
+ fprintf (state->ofp, is_unsigned ? " jb .L%d\n" : " jl .L%d\n", false_label);
+ fprintf (state->ofp, " cmpl %%ebx, %%eax\n");
+ fprintf (state->ofp, " jae .L%d\n", true_label);
+ break;
+
+ case TOK_EQEQ:
+
+ fprintf (state->ofp, " cmpl %%ecx, %%edx\n");
+ fprintf (state->ofp, " jne .L%d\n", false_label);
+ fprintf (state->ofp, " cmpl %%ebx, %%eax\n");
+ fprintf (state->ofp, " je .L%d\n", true_label);
+ break;
+
+ case TOK_NOTEQ:
+
+ fprintf (state->ofp, " cmpl %%ecx, %%edx\n");
+ fprintf (state->ofp, " jne .L%d\n", true_label);
+ fprintf (state->ofp, " cmpl %%ebx, %%eax\n");
+ fprintf (state->ofp, " jne .L%d\n", true_label);
+ break;
+
+ default:
+
+ fprintf (state->ofp, " testl %%edx, %%edx\n");
+ fprintf (state->ofp, " jnz .L%d\n", true_label);
+ fprintf (state->ofp, " testl %%eax, %%eax\n");
+ fprintf (state->ofp, " jnz .L%d\n", true_label);
+ break;
+
+ }
+
+ fprintf (state->ofp, ".L%d:\n", false_label);
+ fprintf (state->ofp, " xorl %%eax, %%eax\n");
+ fprintf (state->ofp, " jmp .L%d\n", end_label);
+ fprintf (state->ofp, ".L%d:\n", true_label);
+ fprintf (state->ofp, " movl $1, %%eax\n");
+ fprintf (state->ofp, ".L%d:\n", end_label);
+
+ }
+
+}
+
+static const char *statement_float_false_jump_mnemonic (enum token_kind op) {
+
+ switch (op) {
+
+ case TOK_LESS:
+
+ return "jae";
+
+ case TOK_LTEQ:
+
+ return "ja";
+
+ case TOK_GREATER:
+
+ return "jbe";
+
+ case TOK_GTEQ:
+
+ return "jb";
+
+ case TOK_EQEQ:
+
+ return "jne";
+
+ case TOK_NOTEQ:
+
+ return "je";
+
+ default:
+
+ return "jz";
+
+ }
+
+}
+
+static void emit_statement_floating_compare_jump_if_false (enum token_kind op, int label) {
+
+ const char *jmp;
+
+ if (!state->ofp) {
+ return;
+ }
+
+ jmp = statement_float_false_jump_mnemonic (op);
+
+ if (state->syntax & ASM_SYNTAX_NASM) {
+
+ fprintf (state->ofp, " fxch st1\n");
+ fprintf (state->ofp, " fcompp\n");
+ fprintf (state->ofp, " fnstsw ax\n");
+ fprintf (state->ofp, " sahf\n");
+ fprintf (state->ofp, " %s L%d\n", jmp, label);
+
+ } else if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ fprintf (state->ofp, " fxch st(1)\n");
+ fprintf (state->ofp, " fcompp\n");
+ fprintf (state->ofp, " fnstsw ax\n");
+ fprintf (state->ofp, " sahf\n");
+ fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? " %s L%d\n" : " %s .L%d\n"), jmp, label);
+
+ } else {
+
+ fprintf (state->ofp, " fxch %%st(1)\n");
+ fprintf (state->ofp, " fcompp\n");
+ fprintf (state->ofp, " fnstsw %%ax\n");
+ fprintf (state->ofp, " sahf\n");
+ fprintf (state->ofp, " %s .L%d\n", jmp, label);
+
+ }
+
+}
+
+static int statement_compare_floating_const_true (long double left, enum token_kind op, long double right) {
+
+ switch (op) {
+
+ case TOK_LESS:
+
+ return left < right;
+
+ case TOK_LTEQ:
+
+ return left <= right;
+
+ case TOK_GREATER:
+
+ return left > right;
+
+ case TOK_GTEQ:
+
+ return left >= right;
+
+ case TOK_EQEQ:
+
+ return left == right;
+
+ case TOK_NOTEQ:
+
+ return left != right;
+
+ default:
+
+ return left != 0.0L;
+
+ }
+
+}
+
+static void emit_statement_test_eax_jump_if_false (int label) {
+
+ if (!state->ofp) {
+ return;
+ }
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ fprintf (state->ofp, " test eax, eax\n");
+ fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? " jz L%d\n" : " jz .L%d\n"), label);
+
+ } else {
+
+ fprintf (state->ofp, " testl %%eax, %%eax\n");
+ fprintf (state->ofp, " jz .L%d\n", label);
+
+ }
+
+}
+
+static void emit_statement_test_pair_jump_if_false (const char *lo, const char *hi, int label) {
+
+ int nonzero_label;
+
+ if (!state->ofp) {
+ return;
+ }
+
+ nonzero_label = anon_label++;
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ fprintf (state->ofp, " test %s, %s\n", hi, hi);
+ fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? " jnz L%d\n" : " jnz .L%d\n"), nonzero_label);
+ fprintf (state->ofp, " test %s, %s\n", lo, lo);
+ fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? " jz L%d\n" : " jz .L%d\n"), label);
+ fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "L%d:\n" : ".L%d:\n"), nonzero_label);
+
+ } else {
+
+ fprintf (state->ofp, " testl %%%s, %%%s\n", hi, hi);
+ fprintf (state->ofp, " jnz .L%d\n", nonzero_label);
+ fprintf (state->ofp, " testl %%%s, %%%s\n", lo, lo);
+ fprintf (state->ofp, " jz .L%d\n", label);
+ fprintf (state->ofp, ".L%d:\n", nonzero_label);
+
+ }
+
+}
+
+static int statement_condition_constant_known = 0;
+static int statement_condition_constant_value = 0;
+static int statement_ends_control_flow = 0;
+
+static void parse_statement (void);
+static void parse_statement_suppressed (void);
+static void emit_statement_jump_if_false (int label);
+
+static void replay_tmp_file_to_output (FILE *tmp, FILE *out) {
+
+ int ch;
+
+ if (!tmp || !out) {
+ return;
+ }
+
+ fflush (tmp);
+ fseek (tmp, 0, SEEK_SET);
+
+ while ((ch = fgetc (tmp)) != EOF) {
+ fputc (ch, out);
+ }
+
+}
+
+static void parse_for_header_expression_until (enum token_kind end_token) {
+
+ char *name;
+ int global_index;
+
+ const char *name_start, *name_caret;
+ unsigned long name_line;
+
+ enum token_kind op;
+ struct local_symbol *lhs;
+
+ int lhs_size;
+ int lhs_is_floating;
+
+#define FINISH_FOR_HEADER_EXPR(free_name) \
+ do { \
+ if (tok.kind == TOK_COMMA) { \
+ get_token (); \
+ if (free_name) { \
+ free (name); \
+ } \
+ parse_for_header_expression_until (end_token); \
+ return; \
+ } \
+ if (tok.kind != end_token) { \
+ skip_balanced_until (end_token, TOK_EOF, TOK_EOF); \
+ } \
+ expect (end_token, end_token == TOK_SEMI ? ";" : ")"); \
+ if (free_name) { \
+ free (name); \
+ } \
+ return; \
+ } while (0)
+
+ if (tok.kind == end_token) {
+ return;
+ }
+
+ if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) {
+
+ op = tok.kind;
+ get_token ();
+
+ if (tok.kind == TOK_IDENT) {
+
+ name = xstrdup (tok.ident);
+ name_start = tok.start;
+ name_caret = tok.caret;
+ name_line = get_line_number ();
+ get_token ();
+
+ lhs = find_local_symbol (name);
+ emit_incdec_symbol_now (lhs, name, op, name_line, name_start, name_caret);
+ free (name);
+
+ } else {
+ report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "expected identifier after %s", op == TOK_INCR ? "++" : "--");
+ }
+
+ FINISH_FOR_HEADER_EXPR (0);
+
+ }
+
+ if (tok.kind != TOK_IDENT) {
+
+ if (state->ofp) {
+ emit_load_assignment_rhs_expression_to_reg ("eax");
+ } else {
+ skip_balanced_until (end_token, TOK_EOF, TOK_EOF);
+ }
+
+ FINISH_FOR_HEADER_EXPR (0);
+
+ }
+
+ name = xstrdup (tok.ident);
+ name_start = tok.start;
+ name_caret = tok.caret;
+ name_line = get_line_number ();
+
+ get_token ();
+
+ if (tok.kind == TOK_INCR || tok.kind == TOK_DECR) {
+
+ op = tok.kind;
+ get_token ();
+
+ lhs = find_local_symbol (name);
+ emit_incdec_symbol_now (lhs, name, op, name_line, name_start, name_caret);
+
+ FINISH_FOR_HEADER_EXPR (1);
+
+ }
+
+ if (tok.kind == TOK_LPAREN) {
+
+ if (!find_local_symbol (name)) {
+ ensure_global_function_symbol (name, name_start, name_caret, name_line);
+ }
+
+ emit_call_identifier_to_reg_now (name, "eax", name_start, name_caret, name_line);
+ FINISH_FOR_HEADER_EXPR (1);
+
+ }
+
+ if (!is_assignment_operator (tok.kind)) {
+
+ if (!find_local_symbol (name) && find_global_symbol (name) < 0) {
+
+ int64_s ignored;
+
+ if (!resolve_enum_constant (name, &ignored)) {
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+ }
+
+ }
+
+ if (tok.kind == TOK_COMMA) {
+
+ get_token ();
+ free (name);
+
+ parse_for_header_expression_until (end_token);
+ return;
+
+ }
+
+ skip_balanced_until (end_token, TOK_EOF, TOK_EOF);
+ expect (end_token, end_token == TOK_SEMI ? ";" : ")");
+
+ free (name);
+ return;
+
+ }
+
+ op = tok.kind;
+ get_token ();
+
+ lhs = find_local_symbol (name);
+ global_index = find_global_symbol (name);
+
+ if (!lhs && global_index < 0) {
+
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+ FINISH_FOR_HEADER_EXPR (1);
+
+ }
+
+ if (state->ofp) {
+
+ lhs_size = lhs ? lhs->size : get_global_symbol_size (name);
+ lhs_is_floating = lhs ? lhs->is_floating : get_global_symbol_floating (name);
+
+ if (lhs_is_floating) {
+
+ if (!floating_assignment_operator_supported_now (op)) {
+
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "invalid operands to floating assignment operator");
+ skip_balanced_until (end_token, TOK_EOF, TOK_EOF);
+
+ } else {
+
+ if (op == TOK_ASSIGN) {
+ emit_load_floating_rhs_expression_now (lhs_size);
+ } else {
+
+ emit_load_floating_symbol_now (lhs, name, lhs_size);
+ emit_load_floating_rhs_expression_now (lhs_size);
+ emit_floating_binary_now (op);
+
+ }
+
+ emit_store_floating_symbol_now (lhs, name, lhs_size);
+
+ }
+
+ } else if (lhs_size == (DATA_LLONG & 0x1f)) {
+
+ if (op == TOK_ASSIGN) {
+ emit_load_assignment_rhs_expression_to_pair ("eax", "edx", lhs ? lhs->is_unsigned : get_global_symbol_unsigned (name));
+ } else {
+
+ if (lhs) {
+
+ if (lhs->is_static && lhs->static_label) {
+ emit_load_global64_to_pair ("eax", "edx", lhs->static_label);
+ } else {
+ emit_load_local64_to_pair (lhs->offset, "eax", "edx");
+ }
+
+ } else {
+ emit_load_global64_to_pair ("eax", "edx", name);
+ }
+
+ /*
+ * Compound assignments need the complete RHS expression.
+ * Using emit_load_assignment_rhs_to_pair() only consumes one
+ * primary operand, so e.g.
+ *
+ * final_value += symbol->frag->address + left_value;
+ *
+ * leaves the second + operand for the statement parser and
+ * reports "expected ;". Evaluate the full RHS in eax:edx,
+ * copy it to the RHS pair ebx:ecx, then restore the original
+ * LHS value before applying the compound operator.
+ */
+ emit_push_reg_now ("eax");
+ emit_push_reg_now ("edx");
+
+ emit_load_assignment_rhs_expression_to_pair ("eax", "edx", lhs ? lhs->is_unsigned : get_global_symbol_unsigned (name));
+
+ emit_mov_reg_to_reg_now ("ebx", "eax");
+ emit_mov_reg_to_reg_now ("ecx", "edx");
+
+ emit_pop_reg_now ("edx");
+ emit_pop_reg_now ("eax");
+
+ emit_preserve_assignment64_regs (op);
+ emit_assignment_binary_op64 (op, lhs ? lhs->is_unsigned : get_global_symbol_unsigned (name));
+ emit_restore_assignment64_regs (op);
+
+ }
+
+ if (lhs) {
+
+ if (lhs->is_static && lhs->static_label) {
+ emit_store_pair_to_global64 (lhs->static_label, "eax", "edx");
+ } else {
+ emit_store_pair_to_local64 (lhs->offset, "eax", "edx");
+ }
+
+ } else {
+ emit_store_pair_to_global64 (name, "eax", "edx");
+ }
+
+ } else {
+
+ if (op == TOK_ASSIGN) {
+ emit_load_assignment_rhs_expression_to_reg ("eax");
+ } else {
+
+ if (lhs) {
+
+ if (lhs->is_static && lhs->static_label) {
+ emit_load_global_to_reg ("eax", lhs->static_label, lhs->size);
+ } else {
+ emit_load_local_to_reg ("eax", lhs->offset, lhs->size);
+ }
+
+ } else {
+ emit_load_global_to_reg ("eax", name, lhs_size);
+ }
+
+ emit_load_assignment_rhs_to_reg ("edx");
+ emit_assignment_binary_op (op, lhs ? lhs->is_unsigned : get_global_symbol_unsigned (name));
+
+ }
+
+ if (lhs) {
+
+ if (lhs->is_static && lhs->static_label) {
+ emit_store_reg_to_global (lhs->static_label, lhs->size, "eax");
+ } else {
+ emit_store_reg_to_local (lhs->offset, lhs->size, "eax");
+ }
+
+ } else {
+ emit_store_reg_to_global (name, lhs_size, "eax");
+ }
+
+ }
+
+ } else {
+ skip_balanced_until (end_token, TOK_EOF, TOK_EOF);
+ }
+
+ FINISH_FOR_HEADER_EXPR (1);
+
+#undef FINISH_FOR_HEADER_EXPR
+
+}
+
+static void parse_for_statement (void) {
+
+ int loop_label = anon_label++;
+ int body_label = anon_label++;
+ int continue_label = anon_label++;
+ int break_label = anon_label++;
+ int old_break_label = current_break_label;
+ int old_continue_label = current_continue_label;
+
+ long old_break_cleanup_base = current_break_cleanup_base;
+ long old_continue_cleanup_base = current_continue_cleanup_base;
+
+ int cond_known = 0;
+ int cond_value = 1;
+
+ FILE *saved_ofp;
+ FILE *step_tmp = 0;
+
+ get_token ();
+ expect (TOK_LPAREN, "(");
+
+ if (tok.kind == TOK_SEMI) {
+ get_token ();
+ } else {
+ parse_for_header_expression_until (TOK_SEMI);
+ }
+
+ emit_statement_label (loop_label);
+
+ if (tok.kind == TOK_SEMI) {
+ get_token ();
+ } else {
+
+ emit_statement_jump_if_false (break_label);
+
+ cond_known = statement_condition_constant_known;
+ cond_value = statement_condition_constant_value;
+
+ if (tok.kind != TOK_SEMI) {
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+ }
+
+ expect (TOK_SEMI, ";");
+
+ }
+
+ if (tok.kind == TOK_RPAREN) {
+ get_token ();
+ } else {
+
+ if (state->ofp) {
+ step_tmp = tmpfile ();
+ }
+
+ if (step_tmp) {
+
+ saved_ofp = state->ofp;
+
+ state->ofp = step_tmp;
+ parse_for_header_expression_until (TOK_RPAREN);
+
+ state->ofp = saved_ofp;
+
+ } else {
+ parse_for_header_expression_until (TOK_RPAREN);
+ }
+
+ }
+
+ current_break_label = break_label;
+ current_continue_label = continue_label;
+ current_break_cleanup_base = current_block_cleanup_bytes;
+ current_continue_cleanup_base = current_block_cleanup_bytes;
+
+ if (cond_known && !cond_value) {
+ parse_statement_suppressed ();
+ } else {
+
+ emit_statement_jump (body_label);
+ emit_statement_label (body_label);
+
+ parse_statement ();
+ emit_statement_label (continue_label);
+
+ if (step_tmp) {
+
+ replay_tmp_file_to_output (step_tmp, state->ofp);
+ fclose (step_tmp);
+
+ step_tmp = 0;
+
+ }
+
+ emit_statement_jump (loop_label);
+
+ }
+
+ if (step_tmp) {
+ fclose (step_tmp);
+ }
+
+ emit_statement_label (break_label);
+
+ current_break_label = old_break_label;
+ current_continue_label = old_continue_label;
+ current_break_cleanup_base = old_break_cleanup_base;
+ current_continue_cleanup_base = old_continue_cleanup_base;
+
+ statement_ends_control_flow = 0;
+
+}
+
+static void parse_while_statement (void) {
+
+ int loop_label = anon_label++;
+ int body_label = anon_label++;
+ int break_label = anon_label++;
+ int old_break_label = current_break_label;
+ int old_continue_label = current_continue_label;
+
+ long old_break_cleanup_base = current_break_cleanup_base;
+ long old_continue_cleanup_base = current_continue_cleanup_base;
+
+ int cond_known;
+ int cond_value;
+
+ get_token ();
+ expect (TOK_LPAREN, "(");
+
+ emit_statement_label (loop_label);
+ emit_statement_jump_if_false (break_label);
+
+ cond_known = statement_condition_constant_known;
+ cond_value = statement_condition_constant_value;
+
+ if (tok.kind != TOK_RPAREN) {
+ skip_balanced_until (TOK_RPAREN, TOK_EOF, TOK_EOF);
+ }
+
+ expect (TOK_RPAREN, ")");
+
+ current_break_label = break_label;
+ current_continue_label = loop_label;
+ current_break_cleanup_base = current_block_cleanup_bytes;
+ current_continue_cleanup_base = current_block_cleanup_bytes;
+
+ if (cond_known && !cond_value) {
+ parse_statement_suppressed ();
+ } else {
+
+ emit_statement_jump (body_label);
+ emit_statement_label (body_label);
+
+ parse_statement ();
+
+ if (!statement_ends_control_flow) {
+ emit_statement_jump (loop_label);
+ }
+
+ }
+
+ emit_statement_label (break_label);
+
+ current_break_label = old_break_label;
+ current_continue_label = old_continue_label;
+ current_break_cleanup_base = old_break_cleanup_base;
+ current_continue_cleanup_base = old_continue_cleanup_base;
+
+ statement_ends_control_flow = 0;
+
+}
+
+static void parse_do_statement (void) {
+
+ int body_label = anon_label++;
+ int cond_label = anon_label++;
+ int break_label = anon_label++;
+ int old_break_label = current_break_label;
+ int old_continue_label = current_continue_label;
+
+ long old_break_cleanup_base = current_break_cleanup_base;
+ long old_continue_cleanup_base = current_continue_cleanup_base;
+
+ int cond_known = 0;
+ int cond_value = 0;
+
+ get_token ();
+
+ current_break_label = break_label;
+ current_continue_label = cond_label;
+ current_break_cleanup_base = current_block_cleanup_bytes;
+ current_continue_cleanup_base = current_block_cleanup_bytes;
+
+ emit_statement_jump (body_label);
+
+ emit_statement_label (body_label);
+ parse_statement ();
+
+ emit_statement_label (cond_label);
+
+ if (_accept (TOK_WHILE)) {
+
+ expect (TOK_LPAREN, "(");
+ emit_statement_jump_if_false (break_label);
+
+ cond_known = statement_condition_constant_known;
+ cond_value = statement_condition_constant_value;
+
+ if (tok.kind != TOK_RPAREN) {
+ skip_balanced_until (TOK_RPAREN, TOK_EOF, TOK_EOF);
+ }
+
+ expect (TOK_RPAREN, ")");
+ expect (TOK_SEMI, ";");
+
+ if (!cond_known || cond_value) {
+ emit_statement_jump (body_label);
+ }
+
+ } else {
+ report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "expected while after do statement");
+ }
+
+ emit_statement_label (break_label);
+
+ current_break_label = old_break_label;
+ current_continue_label = old_continue_label;
+ current_break_cleanup_base = old_break_cleanup_base;
+ current_continue_cleanup_base = old_continue_cleanup_base;
+
+ statement_ends_control_flow = 0;
+
+}
+
+static int source_parenthesized_condition_has_logical_now (void) {
+
+ const char *p = tok.caret;
+ int depth = 0;
+
+ if (!p || *p != '(') {
+ return 0;
+ }
+
+ for (; *p; p++) {
+
+ if (*p == '(') {
+
+ depth++;
+ continue;
+
+ }
+
+ if (*p == ')') {
+
+ depth--;
+
+ if (depth == 0) {
+ return 0;
+ }
+
+ continue;
+
+ }
+
+ if (depth == 1 && ((*p == '&' && p[1] == '&') || (*p == '|' && p[1] == '|'))) {
+ return 1;
+ }
+
+ if (*p == '"' || *p == '\'') {
+
+ int quote = *p++;
+
+ while (*p && *p != quote) {
+
+ if (*p == '\\' && p[1]) {
+ p++;
+ }
+
+ p++;
+
+ }
+
+ if (!*p) {
+ return 0;
+ }
+
+ }
+
+ }
+
+ return 0;
+
+}
+
+static int statement_condition_emit_logical_tail (int label) {
+
+ int skip_label;
+
+ if (tok.kind == TOK_LOGAND) {
+
+ get_token ();
+
+ emit_statement_jump_if_false (label);
+ return 1;
+
+ }
+
+ if (tok.kind == TOK_LOGOR) {
+
+ skip_label = anon_label++;
+ get_token ();
+
+ if (state->ofp) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " test eax, eax\n");
+ fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? " jnz L%d\n" : " jnz .L%d\n"), skip_label);
+ } else {
+ fprintf (state->ofp, " testl %%eax, %%eax\n");
+ fprintf (state->ofp, " jnz .L%d\n", skip_label);
+ }
+
+ }
+
+ emit_statement_jump_if_false (label);
+ emit_statement_label (skip_label);
+
+ return 1;
+
+ }
+
+ return 0;
+
+}
+
+static int statement_condition_fold_logical_tail (int label) {
+
+ enum token_kind logop;
+
+ while (tok.kind == TOK_LOGOR || tok.kind == TOK_LOGAND) {
+
+ logop = tok.kind;
+
+ /*
+ * Constant short-circuit cases can discard the rest of the condition.
+ *
+ * true || anything -> true
+ * false && anything -> false
+ *
+ * Leave tok on ')' so the caller's expect(TOK_RPAREN) still works.
+ */
+ if ((logop == TOK_LOGOR && statement_condition_constant_value) || (logop == TOK_LOGAND && !statement_condition_constant_value)) {
+
+ skip_balanced_until (TOK_RPAREN, TOK_EOF, TOK_EOF);
+ return 1;
+
+ }
+
+ /*
+ * The left side is non-decisive:
+ *
+ * false || rhs -> rhs
+ * true && rhs -> rhs
+ *
+ * So consume the logical operator and emit/fold the RHS normally.
+ */
+ get_token ();
+
+ statement_condition_constant_known = 0;
+ statement_condition_constant_value = 0;
+
+ emit_statement_jump_if_false (label);
+ return 1;
+
+ }
+
+ return 0;
+
+}
+
+static int source_starts_with_parenthesized_assignment_at (const char *p) {
+
+ int parens = 0;
+
+ if (!p || *p != '(') {
+ return 0;
+ }
+
+ while (*p == '(') {
+
+ parens++;
+ p++;
+
+ while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+ p++;
+ }
+
+ }
+
+ if (parens <= 0 || !((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || *p == '_')) {
+ return 0;
+ }
+
+ p++;
+
+ while ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || (*p >= '0' && *p <= '9') || *p == '_') {
+ p++;
+ }
+
+ while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+ p++;
+ }
+
+ while (parens > 0 && *p == ')') {
+
+ parens--;
+ p++;
+
+ while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+ p++;
+ }
+
+ }
+
+ /*
+ * A condition assignment normally looks like:
+ *
+ * if ((x = y))
+ *
+ * At this point the scanner has consumed the opening paren(s) and the
+ * identifier. For the common form above, the assignment operator appears
+ * before the closing paren, so `parens' is still non-zero. The previous
+ * guard returned 0 before checking for '=', which made the statement
+ * condition parser treat the expression as just `(x' and then report a
+ * false "expected )" at the assignment operator. Check for assignment
+ * operators first, while still rejecting comparison operators.
+ */
+ if (*p == '=') {
+ return p[1] != '=';
+ }
+
+ if (parens != 0) {
+ return 0;
+ }
+
+ if ((*p == '+' || *p == '-' || *p == '*' || *p == '/' || *p == '%' ||
+ *p == '&' || *p == '|' || *p == '^') && p[1] == '=') {
+ return 1;
+ }
+
+ if ((*p == '<' && p[1] == '<' && p[2] == '=') ||
+ (*p == '>' && p[1] == '>' && p[2] == '=')) {
+ return 1;
+ }
+
+ return 0;
+
+}
+
+static int source_starts_with_parenthesized_assignment_now (void) {
+
+ /**
+ * Only test the current token text. tok.start can point at the start of
+ * the whole source line, so scanning the whole line misclassifies a for
+ * condition such as:
+ *
+ * for (len = 0; (len < max_len) && str[len]; len++);
+ *
+ * as a parenthesized assignment because of the earlier ``(len = 0`` in the
+ * same line. This helper is a look-ahead guard; it must not match any
+ * parenthesized assignment except the one beginning at the current token.
+ */
+ if (source_starts_with_parenthesized_assignment_at (tok.caret)) {
+ return 1;
+ }
+
+ return 0;
+
+}
+
+static int parenthesized_assignment_open_parens = 0;
+static int parenthesized_assignment_closed_parens = 0;
+
+static void consume_parenthesized_assignment_remaining_closes (void) {
+
+ while (parenthesized_assignment_closed_parens < parenthesized_assignment_open_parens && tok.kind == TOK_RPAREN) {
+
+ get_token ();
+ parenthesized_assignment_closed_parens++;
+
+ }
+
+}
+
+static int emit_load_parenthesized_assignment_expression_to_reg_now (const char *reg, int *out_is_unsigned) {
+
+ char *name;
+ int global_index;
+
+ const char *name_start, *name_caret;
+ unsigned long name_line;
+
+ enum token_kind op;
+ struct local_symbol *lhs;
+
+ int lhs_size;
+ int lhs_is_floating;
+ int lhs_is_unsigned;
+ int paren_count = 0;
+
+ parenthesized_assignment_open_parens = 0;
+ parenthesized_assignment_closed_parens = 0;
+
+ if (out_is_unsigned) {
+ *out_is_unsigned = 0;
+ }
+
+ if (tok.kind != TOK_LPAREN || !source_starts_with_parenthesized_assignment_now ()) {
+ return 0;
+ }
+
+ while (tok.kind == TOK_LPAREN) {
+
+ paren_count++;
+ get_token ();
+
+ }
+
+ if (tok.kind != TOK_IDENT) {
+ return 0;
+ }
+
+ parenthesized_assignment_open_parens = paren_count;
+
+ name = xstrdup (tok.ident);
+ name_start = tok.start;
+ name_caret = tok.caret;
+ name_line = get_line_number ();
+
+ get_token ();
+
+ while (parenthesized_assignment_closed_parens < parenthesized_assignment_open_parens && tok.kind == TOK_RPAREN) {
+
+ get_token ();
+ parenthesized_assignment_closed_parens++;
+
+ }
+
+ if (tok.kind == TOK_DOT || tok.kind == TOK_ARROW) {
+
+ enum token_kind member_op = tok.kind;
+ char *member;
+
+ const char *member_start;
+ const char *member_caret;
+
+ unsigned long member_line;
+
+ int member_offset = 0;
+ int member_size = DATA_INT & 0x1f;
+
+ get_token ();
+
+ member_start = tok.start;
+ member_caret = tok.caret;
+ member_line = get_line_number ();
+
+ if (tok.kind != TOK_IDENT) {
+
+ report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "expected member name after %s", member_op == TOK_ARROW ? "->" : ".");
+
+ free (name);
+ return 1;
+
+ }
+
+ member = xstrdup (tok.ident);
+ get_token ();
+
+ if (!find_member_info (member, &member_offset, &member_size)) {
+
+ report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "unknown member '%s'", member);
+
+ free (member);
+ free (name);
+
+ return 1;
+
+ }
+
+ free (member);
+
+ if (!is_assignment_operator (tok.kind)) {
+
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+ expect (TOK_SEMI, ";");
+
+ free (name);
+ return 1;
+
+ }
+
+ op = tok.kind;
+ get_token ();
+
+ lhs = find_local_symbol (name);
+ global_index = find_global_symbol (name);
+
+ if (!lhs && global_index < 0) {
+
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+ expect (TOK_SEMI, ";");
+
+ free (name);
+ return 1;
+
+ }
+
+ if (state->ofp) {
+
+ if (member_op == TOK_ARROW) {
+
+ if (lhs) {
+
+ if (lhs->is_static && lhs->static_label) {
+ emit_load_global_to_reg ("edx", lhs->static_label, DATA_PTR);
+ } else {
+ emit_load_local_to_reg ("edx", lhs->offset, DATA_PTR);
+ }
+
+ } else {
+ emit_load_global_to_reg ("edx", name, DATA_PTR);
+ }
+
+ } else {
+
+ if (lhs) {
+
+ if (lhs->is_static && lhs->static_label) {
+ emit_load_address_to_reg_now ("edx", lhs->static_label);
+ } else {
+ emit_load_local_address_to_reg_now ("edx", lhs->offset);
+ }
+
+ } else {
+ emit_load_address_to_reg_now ("edx", name);
+ }
+
+ }
+
+ if (op == TOK_ASSIGN) {
+
+ emit_push_reg_now ("edx");
+ emit_load_assignment_rhs_expression_to_reg ("eax");
+ emit_pop_reg_now ("edx");
+
+ } else {
+
+ emit_load_member_from_addr_reg_now ("eax", "edx", member_offset, member_size);
+ emit_push_reg_now ("edx");
+ emit_push_reg_now ("eax");
+
+ if (tok.kind == TOK_TILDE) {
+
+ int64_s rhs_const;
+ get_token ();
+
+ if (tok.kind == TOK_LPAREN) {
+
+ get_token ();
+
+ rhs_const = const64_from_current_foldable_expr ();
+ expect (TOK_RPAREN, ")");
+
+ } else {
+ rhs_const = const64_from_current_foldable_expr ();
+ }
+
+ rhs_const.low = (~rhs_const.low) & U32_MASK;
+ rhs_const.high = (~rhs_const.high) & U32_MASK;
+
+ emit_load_const32_to_reg_now ("edx", rhs_const);
+
+ } else if (const_integer_expr_text_is_foldable_now (tok.start) || const_integer_expr_text_is_foldable_now (tok.caret)) {
+
+ int64_s rhs_const = const64_from_current_foldable_expr ();
+ emit_load_const32_to_reg_now ("edx", rhs_const);
+
+ } else {
+ emit_load_assignment_rhs_expression_to_reg ("edx");
+ }
+
+ emit_pop_reg_now ("eax");
+ emit_assignment_binary_op (op, 0);
+ emit_pop_reg_now ("edx");
+
+ }
+
+ emit_store_member_to_addr_reg_now ("edx", member_offset, "eax", member_size);
+
+ } else {
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+ }
+
+ expect_semi_or_recover ();
+
+ free (name);
+ return 1;
+
+ }
+
+ if (!is_assignment_operator (tok.kind)) {
+
+ free (name);
+ return 0;
+
+ }
+
+ op = tok.kind;
+ get_token ();
+
+ lhs = find_local_symbol (name);
+ global_index = find_global_symbol (name);
+
+ if (!lhs && global_index < 0) {
+
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "unknown symbol '%s'", name);
+
+ skip_balanced_until (TOK_RPAREN, TOK_EOF, TOK_EOF);
+ expect (TOK_RPAREN, ")");
+
+ free (name);
+ return 1;
+
+ }
+
+ lhs_size = lhs ? lhs->size : get_global_symbol_size (name);
+ lhs_is_floating = lhs ? lhs->is_floating : get_global_symbol_floating (name);
+ lhs_is_unsigned = lhs ? lhs->is_unsigned : get_global_symbol_unsigned (name);
+
+ if (out_is_unsigned) {
+ *out_is_unsigned = lhs_is_unsigned;
+ }
+
+ if (state->ofp) {
+
+ if (lhs_is_floating) {
+
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "floating assignment expression in condition not implemented");
+ skip_balanced_until (TOK_RPAREN, TOK_EOF, TOK_EOF);
+
+ } else if (lhs_size == (DATA_LLONG & 0x1f)) {
+
+ if (op == TOK_ASSIGN) {
+ emit_load_assignment_rhs_expression_to_pair ("eax", "edx", lhs_is_unsigned);
+ } else {
+
+ if (lhs) {
+
+ if (lhs->is_static && lhs->static_label) {
+ emit_load_global64_to_pair ("eax", "edx", lhs->static_label);
+ } else {
+ emit_load_local64_to_pair (lhs->offset, "eax", "edx");
+ }
+
+ } else {
+ emit_load_global64_to_pair ("eax", "edx", name);
+ }
+
+ /*
+ * Compound assignments need the complete RHS expression.
+ * Using emit_load_assignment_rhs_to_pair() only consumes one
+ * primary operand, so e.g.
+ *
+ * final_value += symbol->frag->address + left_value;
+ *
+ * leaves the second + operand for the statement parser and
+ * reports "expected ;". Evaluate the full RHS in eax:edx,
+ * copy it to the RHS pair ebx:ecx, then restore the original
+ * LHS value before applying the compound operator.
+ */
+ emit_push_reg_now ("eax");
+ emit_push_reg_now ("edx");
+
+ emit_load_assignment_rhs_expression_to_pair ("eax", "edx", lhs_is_unsigned);
+
+ emit_mov_reg_to_reg_now ("ebx", "eax");
+ emit_mov_reg_to_reg_now ("ecx", "edx");
+
+ emit_pop_reg_now ("edx");
+ emit_pop_reg_now ("eax");
+
+ emit_preserve_assignment64_regs (op);
+ emit_assignment_binary_op64 (op, lhs_is_unsigned);
+ emit_restore_assignment64_regs (op);
+
+ }
+
+ if (lhs) {
+
+ if (lhs->is_static && lhs->static_label) {
+ emit_store_pair_to_global64 (lhs->static_label, "eax", "edx");
+ } else {
+ emit_store_pair_to_local64 (lhs->offset, "eax", "edx");
+ }
+
+ } else {
+ emit_store_pair_to_global64 (name, "eax", "edx");
+ }
+
+ } else {
+
+ if (op == TOK_ASSIGN) {
+ emit_load_assignment_rhs_expression_to_reg (reg);
+ } else {
+
+ if (lhs) {
+
+ if (lhs->is_static && lhs->static_label) {
+ emit_load_global_to_reg (reg, lhs->static_label, lhs->size);
+ } else {
+ emit_load_local_to_reg (reg, lhs->offset, lhs->size);
+ }
+
+ } else {
+ emit_load_global_to_reg (reg, name, lhs_size);
+ }
+
+ emit_load_assignment_rhs_to_reg ("edx");
+
+ if (strcmp (reg, "eax") != 0 && state->ofp) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " mov eax, %s\n", reg);
+ } else {
+ fprintf (state->ofp, " movl %%%s, %%eax\n", reg);
+ }
+
+ }
+
+ emit_assignment_binary_op (op, lhs_is_unsigned);
+
+ if (strcmp (reg, "eax") != 0 && state->ofp) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " mov %s, eax\n", reg);
+ } else {
+ fprintf (state->ofp, " movl %%eax, %%%s\n", reg);
+ }
+
+ }
+
+ }
+
+ if (lhs) {
+
+ if (lhs->is_static && lhs->static_label) {
+ emit_store_reg_to_global (lhs->static_label, lhs->size, reg);
+ } else {
+ emit_store_reg_to_local (lhs->offset, lhs->size, reg);
+ }
+
+ } else {
+ emit_store_reg_to_global (name, lhs_size, reg);
+ }
+
+ }
+
+ } else {
+ skip_balanced_until (TOK_RPAREN, TOK_EOF, TOK_EOF);
+ }
+
+ consume_parenthesized_assignment_remaining_closes ();
+
+ free (name);
+ return 1;
+
+}
+
+static void emit_statement_jump_if_false (int label) {
+
+ enum token_kind op;
+ int is_unsigned;
+
+ flush_pending_statement_labels ();
+
+ statement_condition_constant_known = 0;
+ statement_condition_constant_value = 0;
+
+ if (tok.kind == TOK_LPAREN && source_parenthesized_condition_has_logical_now ()) {
+
+ /*
+ * Parse the whole parenthesized logical expression as an expression.
+ * The older recursive condition parser stopped after the first operand
+ * of nested logical groups such as:
+ *
+ * a && (b || c || d)
+ *
+ * and then the outer caller immediately expected the closing ')',
+ * leaving the inner '||' tokens unconsumed and reporting a false
+ * "expected )" at the first member expression.
+ */
+ get_token ();
+
+ emit_load_assignment_rhs_expression_to_reg ("eax");
+ expect (TOK_RPAREN, ")");
+
+ if (tok.kind == TOK_LOGOR) {
+
+ int skip_label = anon_label++;
+
+ if (state->ofp) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ fprintf (state->ofp, " test eax, eax\n");
+ fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? " jnz L%d\n" : " jnz .L%d\n"), skip_label);
+
+ } else {
+
+ fprintf (state->ofp, " testl %%eax, %%eax\n");
+ fprintf (state->ofp, " jnz .L%d\n", skip_label);
+
+ }
+
+ }
+
+ get_token ();
+
+ emit_statement_jump_if_false (label);
+ emit_statement_label (skip_label);
+
+ return;
+
+ }
+
+ emit_statement_test_eax_jump_if_false (label);
+ statement_condition_emit_logical_tail (label);
+
+ return;
+
+ }
+
+ if (emit_load_parenthesized_assignment_expression_to_reg_now ("eax", &is_unsigned)) {
+
+ while (is_arithmetic_binary_operator (tok.kind)) {
+
+ op = tok.kind;
+ get_token ();
+
+ if (rhs_current_operand_is_unsigned_now ()) {
+ is_unsigned = 1;
+ }
+
+ emit_load_assignment_rhs_to_reg ("edx");
+ emit_assignment_binary_op (op, is_unsigned);
+
+ }
+
+ if (token_is_statement_compare_operator (tok.kind)) {
+
+ op = tok.kind;
+ get_token ();
+
+ if (rhs_current_operand_is_unsigned_now ()) {
+ is_unsigned = 1;
+ }
+
+ emit_load_assignment_rhs_expression_to_reg ("edx");
+ consume_parenthesized_assignment_remaining_closes ();
+ emit_statement_cmp_eax_edx_jump_if_false (op, is_unsigned, label);
+
+ return;
+
+ }
+
+ consume_parenthesized_assignment_remaining_closes ();
+ emit_statement_test_eax_jump_if_false (label);
+ return;
+
+ }
+
+ if (statement_condition_starts_with_ident_call_now ()) {
+
+ is_unsigned = rhs_current_operand_is_unsigned_now ();
+ emit_load_assignment_rhs_expression_to_reg ("eax");
+
+ if (token_is_statement_compare_operator (tok.kind)) {
+
+ op = tok.kind;
+ get_token ();
+
+ if (rhs_current_operand_is_unsigned_now ()) {
+ is_unsigned = 1;
+ }
+
+ emit_load_assignment_rhs_expression_to_reg ("edx");
+ emit_statement_cmp_eax_edx_jump_if_false (op, is_unsigned, label);
+ statement_condition_emit_logical_tail (label);
+
+ return;
+
+ }
+
+ emit_statement_test_eax_jump_if_false (label);
+ statement_condition_emit_logical_tail (label);
+
+ return;
+
+ }
+
+ /*
+ * Fold simple constant conditions at parse time. This avoids emitting:
+ *
+ * mov eax, 1
+ * test eax, eax
+ * jz ...
+ *
+ * for things like if (1), and avoids cmp/jcc for things like
+ * if (1 < 10), if (5 > 2), if (1ULL < 10LL), etc.
+ */
+ if (token_is_const_floating_condition_operand_now () || rhs_current_operand_is_floating_now ()) {
+
+ long double left_float = 0.0L;
+
+ int left_float_constant = 0;
+ int float_size = DATA_DOUBLE & 0x1f;
+
+ if (token_is_const_floating_condition_operand_now ()) {
+
+ left_float = parse_floating_const_expr_value_now ();
+ left_float_constant = 1;
+
+ } else {
+ emit_load_floating_rhs_expression_now (float_size);
+ }
+
+ if (token_is_statement_compare_operator (tok.kind)) {
+
+ op = tok.kind;
+ get_token ();
+
+ if (left_float_constant && (token_is_floating_constant_now () || token_is_const_condition_operand_now ())) {
+
+ long double right_float;
+
+ if (token_is_floating_constant_now ()) {
+ right_float = parse_floating_const_expr_value_now ();
+ } else {
+
+ int64_s right_int = const64_from_current_operand ();
+
+ right_float = (long double) right_int.low;
+
+ if (right_int.high != 0) {
+ right_float += ((long double) right_int.high) * 4294967296.0L;
+ }
+
+ }
+
+ statement_condition_constant_known = 1;
+ statement_condition_constant_value = statement_compare_floating_const_true (left_float, op, right_float) ? 1 : 0;
+
+ if (statement_condition_fold_logical_tail (label)) {
+ return;
+ }
+
+ return;
+
+ }
+
+ if (left_float_constant) {
+ emit_load_floating_ld_now (float_size, left_float);
+ }
+
+ emit_load_floating_rhs_expression_now (float_size);
+ emit_statement_floating_compare_jump_if_false (op, label);
+
+ return;
+
+ }
+
+ if (left_float_constant) {
+
+ statement_condition_constant_known = 1;
+ statement_condition_constant_value = left_float != 0.0L ? 1 : 0;
+
+ if (statement_condition_fold_logical_tail (label)) {
+ return;
+ }
+
+ return;
+
+ }
+
+ emit_load_floating_ld_now (float_size, 0.0L);
+ emit_statement_floating_compare_jump_if_false (TOK_NOTEQ, label);
+
+ return;
+
+ }
+
+ if (token_is_const_condition_operand_now () || const_integer_expr_text_is_foldable_now (tok.caret) || const_integer_expr_text_is_foldable_now (tok.start)) {
+
+ int fold_whole_expr = const_integer_expr_text_is_foldable_now (tok.caret) || const_integer_expr_text_is_foldable_now (tok.start);
+ int left_unsigned = rhs_current_operand_is_unsigned_now ();
+
+ int64_s left;
+
+ if (fold_whole_expr) {
+ left = const64_from_current_foldable_expr ();
+ } else {
+ left = const64_from_current_operand ();
+ }
+
+ if (is_arithmetic_binary_operator (tok.kind)) {
+
+ emit_statement_const32_to_eax (left);
+
+ while (is_arithmetic_binary_operator (tok.kind)) {
+
+ op = tok.kind;
+ get_token ();
+
+ if (rhs_current_operand_is_unsigned_now ()) {
+ left_unsigned = 1;
+ }
+
+ emit_load_assignment_rhs_to_reg ("edx");
+ emit_assignment_binary_op (op, left_unsigned);
+
+ }
+
+ if (token_is_statement_compare_operator (tok.kind)) {
+
+ op = tok.kind;
+ get_token ();
+
+ is_unsigned = left_unsigned;
+
+ if (rhs_current_operand_is_unsigned_now ()) {
+ is_unsigned = 1;
+ }
+
+ emit_load_assignment_rhs_expression_to_reg ("edx");
+ emit_statement_cmp_eax_edx_jump_if_false (op, is_unsigned, label);
+
+ return;
+
+ }
+
+ emit_statement_test_eax_jump_if_false (label);
+ return;
+
+ }
+
+ if (token_is_statement_compare_operator (tok.kind)) {
+
+ op = tok.kind;
+ get_token ();
+
+ is_unsigned = left_unsigned;
+
+ if (rhs_current_operand_is_unsigned_now ()) {
+ is_unsigned = 1;
+ }
+
+ if (token_is_floating_constant_now ()) {
+
+ long double left_float;
+ long double right_float;
+
+ left_float = (long double) left.low;
+
+ if (left.high != 0) {
+ left_float += ((long double) left.high) * 4294967296.0L;
+ }
+
+ right_float = parse_floating_const_expr_value_now ();
+
+ statement_condition_constant_known = 1;
+ statement_condition_constant_value = statement_compare_floating_const_true (left_float, op, right_float) ? 1 : 0;
+
+ if (statement_condition_fold_logical_tail (label)) {
+ return;
+ }
+
+ return;
+
+ }
+
+ if (token_is_const_condition_operand_now ()) {
+
+ int64_s right;
+
+ if (const_integer_expr_text_is_foldable_now (tok.start)) {
+ right = const64_from_current_foldable_expr ();
+ } else {
+ right = const64_from_current_operand ();
+ }
+
+ statement_condition_constant_known = 1;
+ statement_condition_constant_value = statement_compare_const64_true (left, op, right, is_unsigned) ? 1 : 0;
+
+ if (statement_condition_fold_logical_tail (label)) {
+ return;
+ }
+
+ return;
+
+ }
+
+ if (rhs_current_operand_is_floating_now ()) {
+
+ long double left_float;
+
+ left_float = (long double) left.low;
+
+ if (left.high != 0) {
+ left_float += ((long double) left.high) * 4294967296.0L;
+ }
+
+ emit_load_floating_ld_now (DATA_DOUBLE & 0x1f, left_float);
+ emit_load_floating_rhs_expression_now (DATA_DOUBLE & 0x1f);
+ emit_statement_floating_compare_jump_if_false (op, label);
+
+ return;
+
+ }
+
+ emit_statement_const32_to_eax (left);
+ emit_load_assignment_rhs_expression_to_reg ("edx");
+
+ emit_statement_cmp_eax_edx_jump_if_false (op, is_unsigned, label);
+ statement_condition_emit_logical_tail (label);
+
+ return;
+
+ }
+
+ statement_condition_constant_known = 1;
+ statement_condition_constant_value = int64_statement_truth_value (left) ? 1 : 0;
+
+ if (statement_condition_fold_logical_tail (label)) {
+ return;
+ }
+
+ return;
+
+ }
+
+ /*
+ * A leading unary ! always yields an int truth value. Do not route it
+ * through the 64-bit condition path just because the operand text happens
+ * to mention a symbol that the coarse scanner thinks is 64-bit; that path
+ * expects an unconverted 64-bit value and will compare EDX:EAX against
+ * uninitialised ECX:EBX for a plain if (!p).
+ */
+ if (tok.kind == TOK_XMARK) {
+
+ emit_load_assignment_rhs_expression_to_reg ("eax");
+ emit_statement_test_eax_jump_if_false (label);
+
+ statement_condition_emit_logical_tail (label);
+ return;
+
+ }
+
+ if (current_expression_mentions_64bit_symbol_now ()) {
+
+ is_unsigned = rhs_current_operand_is_unsigned_now ();
+ emit_load_assignment_rhs_expression_to_pair ("eax", "edx", is_unsigned);
+
+ if (token_is_statement_compare_operator (tok.kind)) {
+
+ op = tok.kind;
+ get_token ();
+
+ if (rhs_current_operand_is_unsigned_now ()) {
+ is_unsigned = 1;
+ }
+
+ if (state->ofp) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ fprintf (state->ofp, " push ebx\n");
+ fprintf (state->ofp, " push eax\n");
+ fprintf (state->ofp, " push edx\n");
+
+ } else {
+
+ fprintf (state->ofp, " pushl %%ebx\n");
+ fprintf (state->ofp, " pushl %%eax\n");
+ fprintf (state->ofp, " pushl %%edx\n");
+
+ }
+
+ }
+
+ emit_load_assignment_rhs_expression_to_pair ("eax", "edx", is_unsigned);
+
+ if (state->ofp) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ fprintf (state->ofp, " mov ebx, eax\n");
+ fprintf (state->ofp, " mov ecx, edx\n");
+ fprintf (state->ofp, " pop edx\n");
+ fprintf (state->ofp, " pop eax\n");
+
+ } else {
+
+ fprintf (state->ofp, " movl %%eax, %%ebx\n");
+ fprintf (state->ofp, " movl %%edx, %%ecx\n");
+ fprintf (state->ofp, " popl %%edx\n");
+ fprintf (state->ofp, " popl %%eax\n");
+
+ }
+
+ }
+
+ emit_statement_cmp64_to_eax (op, is_unsigned);
+
+ if (state->ofp) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " pop ebx\n");
+ } else {
+ fprintf (state->ofp, " popl %%ebx\n");
+ }
+
+ }
+
+ emit_statement_test_eax_jump_if_false (label);
+ return;
+
+ }
+
+ /*
+ * No explicit comparison follows, so this is just the truth value of
+ * the 64-bit expression in EDX:EAX. Do not route it through the
+ * generic 64-bit comparison helper as TOK_NOTEQ: that helper compares
+ * EDX:EAX against ECX:EBX, and there is no RHS pair here. This broke
+ * conditions such as:
+ *
+ * if ((entry_point = coff_calculate_entry_point ())) return;
+ *
+ * after the assignment stored the result and left ECX holding the
+ * destination address instead of zero.
+ */
+ emit_statement_test_pair_jump_if_false ("eax", "edx", label);
+ statement_condition_emit_logical_tail (label);
+
+ return;
+
+ }
+
+ is_unsigned = rhs_current_operand_is_unsigned_now ();
+ emit_load_assignment_rhs_expression_to_reg ("eax");
+
+ if (token_is_statement_compare_operator (tok.kind)) {
+
+ op = tok.kind;
+ get_token ();
+
+ if (rhs_current_operand_is_unsigned_now ()) {
+ is_unsigned = 1;
+ }
+
+ emit_load_assignment_rhs_expression_to_reg ("edx");
+ emit_statement_cmp_eax_edx_jump_if_false (op, is_unsigned, label);
+ statement_condition_emit_logical_tail (label);
+
+ return;
+
+ }
+
+ emit_statement_test_eax_jump_if_false (label);
+ statement_condition_emit_logical_tail (label);
+
+}
+
+static int token_text_starts_void_cast_now (void) {
+
+ const char *p = tok.caret;
+
+ if (!p || *p != '(') {
+ return 0;
+ }
+
+ p++;
+
+ while (*p == ' ' || *p == '\t') {
+ p++;
+ }
+
+ if (strncmp (p, "void", 4) != 0) {
+ return 0;
+ }
+
+ p += 4;
+
+ if ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || (*p >= '0' && *p <= '9') || *p == '_') {
+ return 0;
+ }
+
+ while (*p == ' ' || *p == '\t') {
+ p++;
+ }
+
+ return *p == ')';
+
+}
+
+static int parse_void_cast_discard_statement (void) {
+
+ if (tok.kind != TOK_LPAREN || !token_text_starts_void_cast_now ()) {
+ return 0;
+ }
+
+ get_token ();
+
+ expect (TOK_VOID, "void");
+ expect (TOK_RPAREN, ")");
+
+ if (tok.kind != TOK_SEMI) {
+
+ emit_load_assignment_rhs_expression_to_reg ("eax");
+
+ if (tok.kind != TOK_SEMI) {
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+ }
+
+ }
+
+ expect (TOK_SEMI, ";");
+ return 1;
+
+}
+
+static int token_text_looks_like_postfix_assignment_now (void) {
+
+ const char *p;
+ int saw_postfix;
+ int paren_depth;
+ int bracket_depth;
+
+ if (tok.caret) {
+ p = tok.caret;
+ } else if (tok.start) {
+ p = tok.start;
+ } else {
+ return 0;
+ }
+
+ saw_postfix = 0;
+ paren_depth = 0;
+ bracket_depth = 0;
+
+ while (*p && *p != ';' && *p != '\n') {
+
+ if (*p == '(') {
+
+ /**
+ * A postfix/member expression followed by a top-level '(' is a
+ * function call through that member, not a postfix assignment.
+ * This pre-scan must reject it before the destructive postfix
+ * assignment parser consumes the tokens.
+ */
+ if (saw_postfix && paren_depth == 0 && bracket_depth == 0) {
+ return 0;
+ }
+
+ paren_depth++;
+
+ } else if (*p == ')') {
+ if (paren_depth > 0) paren_depth--;
+ } else if (*p == '[') {
+
+ saw_postfix = 1;
+ bracket_depth++;
+
+ } else if (*p == ']') {
+ if (bracket_depth > 0) bracket_depth--;
+ } else if (paren_depth == 0 && bracket_depth == 0 && *p == ',') {
+ return 0;
+ } else if (paren_depth == 0 && bracket_depth == 0 && *p == '.') {
+ saw_postfix = 1;
+ } else if (paren_depth == 0 && bracket_depth == 0 && *p == '-' && p[1] == '>') {
+
+ saw_postfix = 1;
+ p++;
+
+ } else if (paren_depth == 0 && bracket_depth == 0 && *p == '=') {
+
+ if (p[1] == '=') {
+ return 0;
+ }
+
+ return saw_postfix;
+
+ }
+
+ p++;
+
+ }
+
+ return 0;
+
+}
+
+static int source_starts_with_parenthesized_indirect_call_now (void) {
+
+ const char *p = tok.caret;
+
+ int member_depth = 1;
+ int saw_member = 0;
+ int depth;
+
+ if (!p || *p != '(') {
+ return 0;
+ }
+
+ p++;
+
+ while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+ p++;
+ }
+
+ if (*p != '*') {
+ return 0;
+ }
+
+ depth = 1;
+ p++;
+
+ while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+ p++;
+ }
+
+ /*
+ * Accept both
+ *
+ * (*obj->member)(...)
+ * (*(obj->member))(...)
+ *
+ * but keep this recognizer deliberately narrow. It must not claim an
+ * arbitrary parenthesized expression, otherwise normal statement parsing
+ * and C labels can be pulled into this special call path.
+ */
+ if (*p == '(') {
+
+ depth++;
+
+ member_depth = 2;
+ p++;
+
+ }
+
+ while (*p && depth > 0) {
+
+ if (*p == '(') {
+ depth++;
+ } else if (*p == ')') {
+
+ depth--;
+
+ if (depth == 0) {
+ break;
+ }
+
+ } else if (depth == member_depth && *p == '-' && p[1] == '>') {
+
+ saw_member = 1;
+ p++;
+
+ } else if (depth == member_depth && *p == '.') {
+ saw_member = 1;
+ }
+
+ p++;
+
+ }
+
+ if (depth != 0 || !saw_member) {
+ return 0;
+ }
+
+ p++;
+
+ while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+ p++;
+ }
+
+ return *p == '(';
+
+}
+
+static int parse_postfix_assignment_statement_now (void) {
+
+ char *name;
+
+ struct local_symbol *src;
+ int global_index;
+
+ enum token_kind assign_op;
+ int lvalue_size;
+
+ if (tok.kind != TOK_IDENT || !token_text_looks_like_postfix_assignment_now ()) {
+ return 0;
+ }
+
+ name = xstrdup (tok.ident);
+
+ {
+
+ const char *name_start = tok.start;
+ const char *name_caret = tok.caret;
+
+ unsigned long name_line = get_line_number ();
+ get_token ();
+
+ src = find_local_symbol (name);
+ global_index = find_global_symbol (name);
+
+ if (!src && global_index < 0) {
+
+ free (name);
+ return 0;
+
+ }
+
+ if (!emit_parse_postfix_copy_source_address_now ("edx", src, name, name_start, name_caret, name_line)) {
+
+ free (name);
+ return 0;
+
+ }
+
+ }
+
+ free (name);
+
+ if (!is_assignment_operator (tok.kind)) {
+ return 0;
+ }
+
+ assign_op = tok.kind;
+ lvalue_size = index_step_size (postfix_copy_lvalue_size);
+
+ if (lvalue_size <= 0) {
+ lvalue_size = DATA_INT & 0x1f;
+ }
+
+ get_token ();
+
+ if (assign_op == TOK_ASSIGN) {
+
+ if (token_is_floating_constant_now ()) {
+
+ emit_push_reg_now ("edx");
+ emit_load_floating_rhs_expression_now (lvalue_size);
+
+ emit_pop_reg_now ("edx");
+ emit_store_floating_member_to_addr_reg_now ("edx", 0, lvalue_size);
+
+ return 1;
+
+ }
+
+ if (lvalue_size == (DATA_LLONG & 0x1f)) {
+
+ emit_push_reg_now ("edx");
+
+ emit_load_assignment_rhs_expression_to_pair ("eax", "edx", 1);
+ emit_pop_reg_now ("ecx");
+
+ emit_store_pair_to_deref_reg_now ("ecx", "eax", "edx");
+ return 1;
+
+ }
+
+ if (lvalue_size > (DATA_LLONG & 0x1f)
+ && tok.kind == TOK_IDENT && tok.ident && token_identifier_is_function_call_rhs_now ()
+ && get_global_symbol_kind (tok.ident) == GLOBAL_SYMBOL_FUNCTION
+ && get_global_symbol_size (tok.ident) > (DATA_LLONG & 0x1f)) {
+
+ char *rhs_name = xstrdup (tok.ident);
+ const char *rhs_start = tok.start;
+ const char *rhs_caret = tok.caret;
+ unsigned long rhs_line = get_line_number ();
+
+ emit_push_reg_now ("edx");
+
+ pending_struct_return_lhs = 0;
+ pending_struct_return_global_name = 0;
+ pending_struct_return_stack_address = 1;
+ pending_struct_return_stack_offset = 0;
+
+ get_token ();
+ emit_call_identifier_to_reg_now (rhs_name, "eax", rhs_start, rhs_caret, rhs_line);
+
+ pending_struct_return_stack_address = 0;
+ pending_struct_return_stack_offset = 0;
+
+ emit_pop_reg_now ("edx");
+
+ free (rhs_name);
+ return 1;
+
+ }
+
+ if (emit_aggregate_copy_from_current_rhs_to_addr_reg_now ("edx", 0, lvalue_size)) {
+ return 1;
+ }
+
+ /**
+ * Keep the computed lvalue address across the RHS evaluation.
+ * The RHS emitter is free to use edx for indexing, calls, and
+ * arithmetic, so storing through edx after it can corrupt memory.
+ */
+ emit_push_reg_now ("edx");
+
+ emit_load_assignment_rhs_expression_to_reg ("eax");
+ emit_pop_reg_now ("edx");
+
+ } else {
+
+ emit_push_reg_now ("edx");
+
+ emit_load_member_from_addr_reg_now ("eax", "edx", 0, lvalue_size);
+ emit_push_reg_now ("eax");
+
+ emit_load_assignment_rhs_expression_to_reg ("edx");
+ emit_pop_reg_now ("eax");
+
+ emit_assignment_binary_op (assign_op, 0);
+ emit_pop_reg_now ("edx");
+
+ }
+
+ emit_store_reg_to_deref_reg_now ("edx", "eax", lvalue_size);
+ return 1;
+
+}
+
+static int parse_parenthesized_member_function_pointer_call_statement (void) {
+
+ struct local_symbol *src;
+ char *name;
+
+ int global_index;
+ int wrapped_member_expr = 0;
+
+ const char *name_start;
+ const char *name_caret;
+
+ unsigned long name_line;
+ int base_size = DATA_INT & 0x1f;
+
+ const char *base_tag_name = 0;
+
+ if (tok.kind != TOK_LPAREN || !source_starts_with_parenthesized_indirect_call_now ()) {
+ return 0;
+ }
+
+ get_token ();
+
+ if (tok.kind != TOK_STAR) {
+ return 0;
+ }
+
+ get_token ();
+
+ if (tok.kind == TOK_LPAREN) {
+
+ wrapped_member_expr = 1;
+ get_token ();
+
+ }
+
+ if (tok.kind != TOK_IDENT || !tok.ident) {
+ return 0;
+ }
+
+ name = xstrdup (tok.ident);
+ name_start = tok.start;
+ name_caret = tok.caret;
+ name_line = get_line_number ();
+
+ get_token ();
+
+ src = find_local_symbol (name);
+ global_index = find_global_symbol (name);
+
+ if (!src && global_index < 0) {
+
+ free (name);
+ return 0;
+
+ }
+
+ if (tok.kind == TOK_ARROW || tok.kind == TOK_LBRACK) {
+
+ if (src) {
+
+ base_size = src->pointer_depth > 0 ? src->pointed_size : src->size;
+ base_tag_name = src->pointer_depth > 0 ? src->pointed_tag_name : 0;
+
+ } else {
+
+ base_size = get_global_symbol_pointer_depth (name) > 0 ? get_global_symbol_pointed_size (name) : get_global_symbol_size (name);
+ base_tag_name = 0;
+
+ }
+
+ } else if (tok.kind == TOK_DOT) {
+
+ if (src) {
+
+ base_size = src->size;
+ base_tag_name = 0;
+
+ } else {
+
+ base_size = get_global_symbol_size (name);
+ base_tag_name = 0;
+
+ }
+
+ } else {
+
+ free (name);
+ return 0;
+
+ }
+
+ postfix_copy_lvalue_size = base_size;
+ postfix_copy_lvalue_tag_name = base_tag_name;
+
+ if (!emit_parse_postfix_copy_source_address_now ("ecx", src, name, name_start, name_caret, name_line)) {
+
+ free (name);
+ return 0;
+
+ }
+
+ free (name);
+
+ if (wrapped_member_expr) {
+ expect (TOK_RPAREN, ")");
+ }
+
+ expect (TOK_RPAREN, ")");
+
+ if (tok.kind != TOK_LPAREN) {
+ return 0;
+ }
+
+ emit_load_deref_reg_now ("ecx", DATA_PTR & 0x1f);
+ emit_call_pointer_in_reg_now ("ecx", "eax");
+
+ expect_semi_or_recover ();
+ return 1;
+
+}
+
+static void parse_statement_suppressed (void) {
+
+ FILE *save_ofp = state->ofp;
+
+ state->ofp = 0;
+ parse_statement ();
+ state->ofp = save_ofp;
+
+}
+
+static void parse_statement (void) {
+
+ statement_ends_control_flow = 0;
+ flush_pending_statement_labels ();
+
+ if (parse_inline_asm_statement ()) {
+ return;
+ }
+
+ if (parse_prefix_incdec_statement ()) {
+ return;
+ }
+
+ if (parse_void_cast_discard_statement ()) {
+ return;
+ }
+
+
+ if (tok.kind == TOK_LPAREN && source_starts_lparen_deref_postfix_incdec_at (tok.caret)) {
+
+ get_token ();
+
+ if (emit_load_parenthesized_deref_postfix_incdec_subscript_to_reg_now ("eax")) {
+
+ expect_semi_or_recover ();
+ return;
+
+ }
+
+ }
+
+ if (parse_parenthesized_deref_subscript_statement ()) {
+
+ expect_semi_or_recover ();
+ return;
+
+ }
+
+ if (parse_parenthesized_member_function_pointer_call_statement ()) {
+ return;
+ }
+
+ if (tok.kind == TOK_LPAREN) {
+
+ int parenthesized_assignment_is_unsigned;
+
+ if (emit_load_parenthesized_assignment_expression_to_reg_now ("eax", &parenthesized_assignment_is_unsigned)) {
+
+ expect_semi_or_recover ();
+ return;
+
+ }
+
+ }
+
+ if (tok.kind == TOK_LPAREN && parse_parenthesized_indirect_member_assignment_statement ()) {
+ return;
+ }
+
+ if (parse_indirect_assignment_statement ()) {
+ return;
+ }
+
+ if (parse_postfix_assignment_statement_now ()) {
+
+ expect_semi_or_recover ();
+ return;
+
+ }
+
+ if (parse_identifier_assignment_statement ()) {
+ return;
+ }
+
+ if (tok.kind == TOK_RETURN) {
+
+ int has_value = 0;
+ long v = 0;
+
+ const char *ret_start = tok.start;
+ const char *ret_caret = tok.caret;
+
+ int ret_line = get_line_number ();
+
+ current_function_has_return_statement = 1;
+ get_token ();
+
+ if (tok.kind != TOK_SEMI) {
+
+ if (current_function_is_void) {
+
+ FILE *saved_return_ofp = state->ofp;
+ state->ofp = 0;
+
+ emit_load_assignment_rhs_expression_to_reg ("eax");
+ state->ofp = saved_return_ofp;
+
+ } else if (current_function_is_floating) {
+
+ if (state->ofp && pending_return_jump) {
+ emit_pending_return_jump ();
+ }
+
+ emit_load_floating_rhs_expression_now (current_function_return_size);
+
+ } else {
+
+ if (state->ofp && pending_return_jump) {
+ emit_pending_return_jump ();
+ }
+
+ if (state->ofp) {
+
+ if (current_function_returns_aggregate) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, (state->syntax & ASM_SYNTAX_NASM ? " mov edx, dword [ebp + 8]\n" : " mov edx, dword ptr [ebp + 8]\n"));
+ } else {
+ fprintf (state->ofp, " movl 8(%%ebp), %%edx\n");
+ }
+
+ if (!emit_aggregate_copy_from_current_rhs_to_addr_reg_now ("edx", 0, current_function_return_size)) {
+ emit_load_assignment_rhs_expression_to_reg ("eax");
+ }
+
+ } else if (current_function_return_size == (DATA_LLONG & 0x1f)) {
+ emit_load_assignment_rhs_expression_to_pair ("eax", "edx", current_function_return_is_unsigned);
+ } else if (current_expression_mentions_64bit_symbol_now ()) {
+ emit_load_assignment_rhs_expression_to_pair ("eax", "edx", current_function_return_is_unsigned);
+ } else {
+ emit_load_assignment_rhs_expression_to_reg ("eax");
+ }
+
+ } else {
+ v = const_from_current_expr ();
+ }
+
+ }
+
+ has_value = 1;
+
+ }
+
+ if (current_function_is_void && has_value) {
+ report_line_at (get_filename (), ret_line, REPORT_WARNING, ret_start, ret_caret, "return with a value in void function");
+ } else if (!current_function_is_void && !has_value) {
+ report_line_at (get_filename (), ret_line, REPORT_WARNING, ret_start, ret_caret, "return with no value in non-void function");
+ }
+
+ if (state->ofp) {
+
+ if (pending_return_jump) {
+ emit_pending_return_jump ();
+ }
+
+ if (!current_function_is_floating && !has_value) {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+
+ fprintf (state->ofp, " mov eax, %ld\n", v);
+
+ if (current_function_return_size == (DATA_LLONG & 0x1f)) {
+ fprintf (state->ofp, " cdq\n");
+ }
+
+ } else {
+
+ fprintf (state->ofp, " movl $%ld, %%eax\n", v);
+
+ if (current_function_return_size == (DATA_LLONG & 0x1f)) {
+ fprintf (state->ofp, " cdq\n");
+ }
+
+ }
+
+ }
+
+ pending_return_jump = 1;
+ statement_ends_control_flow = 1;
+
+ }
+
+ expect (TOK_SEMI, ";");
+ return;
+
+ }
+
+ if (tok.kind == TOK_LBRACE) {
+
+ parse_block ();
+ return;
+
+ }
+
+ if (tok.kind == TOK_IF) {
+
+ int else_label = anon_label++;
+ int end_label = anon_label++;
+
+ get_token ();
+ expect (TOK_LPAREN, "(");
+
+ /*
+ * Compile the full if-condition as an expression before testing it.
+ * The older specialised condition emitter jumps to the false label as
+ * soon as the left side of a logical OR is false, which skips the RHS.
+ * That miscompiled expressions such as:
+ *
+ * if ((base && !base->dword) || (index && ...))
+ *
+ * and made pdas reject valid 32-bit AT&T base/index operands.
+ */
+ emit_statement_jump_if_false (else_label);
+
+ if (tok.kind != TOK_RPAREN) {
+ skip_balanced_until (TOK_RPAREN, TOK_EOF, TOK_EOF);
+ }
+
+ expect (TOK_RPAREN, ")");
+
+ if (statement_condition_constant_known) {
+
+ if (statement_condition_constant_value) {
+
+ parse_statement ();
+
+ if (_accept (TOK_ELSE)) {
+ parse_statement_suppressed ();
+ }
+
+ } else {
+
+ parse_statement_suppressed ();
+
+ if (_accept (TOK_ELSE)) {
+ parse_statement ();
+ }
+
+ }
+
+ return;
+
+ }
+
+ parse_statement ();
+
+ {
+
+ int then_ends_control_flow = statement_ends_control_flow;
+
+ if (_accept (TOK_ELSE)) {
+
+ if (pending_return_jump) {
+
+ emit_pending_return_jump ();
+ then_ends_control_flow = 1;
+
+ } else if (!then_ends_control_flow) {
+ emit_statement_jump (end_label);
+ }
+
+ emit_statement_label (else_label);
+ parse_statement ();
+
+ if (pending_return_jump) {
+
+ emit_pending_return_jump ();
+ statement_ends_control_flow = 1;
+
+ }
+
+ emit_statement_label (end_label);
+ statement_ends_control_flow = then_ends_control_flow && statement_ends_control_flow;
+
+ } else {
+
+ if (pending_return_jump) {
+ emit_pending_return_jump ();
+ }
+
+ emit_statement_label (else_label);
+ statement_ends_control_flow = 0;
+
+ }
+
+ }
+
+ return;
+
+ }
+
+ if (tok.kind == TOK_SWITCH) {
+
+ parse_switch_statement ();
+ return;
+
+ }
+
+ if (tok.kind == TOK_WHILE) {
+
+ parse_while_statement ();
+ return;
+
+ }
+
+ if (tok.kind == TOK_DO) {
+
+ parse_do_statement ();
+ return;
+
+ }
+
+ if (tok.kind == TOK_FOR) {
+
+ parse_for_statement ();
+ return;
+
+ }
+
+ if (tok.kind == TOK_GOTO) {
+
+ const char *label_start;
+ const char *label_caret;
+
+ unsigned long label_line;
+ char *label_name = 0;
+
+ get_token ();
+
+ label_start = tok.start;
+ label_caret = tok.caret;
+ label_line = get_line_number ();
+
+ if (tok.kind == TOK_IDENT) {
+
+ label_name = xstrdup (tok.ident);
+ get_token ();
+
+ /*
+ * Do not eagerly pop block-local temporary storage for a forward
+ * goto. A label can legally be later in the same block after
+ * nested statements, and the earlier text scan was too
+ * conservative: it treated the first nested '}' as the end of the
+ * current block. That produced an extra add to esp before
+ * `goto skip;` in i386_gen.c's process_bitfield_init(), corrupting
+ * the stack before the local label was reached.
+ */
+
+ reference_goto_label (label_name, label_line, label_start, label_caret);
+ statement_ends_control_flow = 1;
+
+ free (label_name);
+
+ } else {
+ report_line_at (get_filename (), label_line, REPORT_ERROR, label_start, label_caret, "expected label name after goto");
+ }
+
+ expect (TOK_SEMI, ";");
+ return;
+
+ }
+
+ if (tok.kind == TOK_BREAK || tok.kind == TOK_CONTINUE) {
+
+ enum token_kind jump_kind = tok.kind;
+ int target_label = -1;
+
+ get_token ();
+
+ if (jump_kind == TOK_BREAK) {
+ target_label = current_break_label;
+ } else {
+ target_label = current_continue_label;
+ }
+
+ if (target_label >= 0) {
+
+ {
+
+ long cleanup_base = (jump_kind == TOK_BREAK) ? current_break_cleanup_base : current_continue_cleanup_base;
+ long cleanup_bytes = current_block_cleanup_bytes - cleanup_base;
+
+ if (cleanup_bytes > 0) {
+ emit_stack_adjust (cleanup_bytes, 0);
+ }
+
+ }
+
+ emit_statement_jump (target_label);
+ statement_ends_control_flow = 1;
+
+ }
+
+ if (tok.kind != TOK_SEMI) {
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+ }
+
+ expect (TOK_SEMI, ";");
+ return;
+
+ }
+
+ if (tok.kind == TOK_CASE) {
+
+ long value;
+ unsigned long case_line = get_line_number ();
+
+ const char *case_start = tok.start;
+ const char *case_caret = tok.caret;
+
+ get_token ();
+
+ value = const_from_current_case_expr ();
+ add_switch_case_label (value, case_line, case_start, case_caret);
+
+ if (tok.kind != TOK_COLON) {
+ skip_balanced_until (TOK_COLON, TOK_EOF, TOK_EOF);
+ }
+
+ expect (TOK_COLON, ":");
+ statement_ends_control_flow = 0;
+
+ return;
+
+ }
+
+ if (tok.kind == TOK_DEFAULT) {
+
+ unsigned long default_line = get_line_number ();
+
+ const char *default_start = tok.start;
+ const char *default_caret = tok.caret;
+
+ get_token ();
+
+ set_switch_default_label (default_line, default_start, default_caret);
+ expect (TOK_COLON, ":");
+
+ statement_ends_control_flow = 0;
+ return;
+
+ }
+
+ if (tok.kind != TOK_SEMI && tok.kind != TOK_RBRACE && tok.kind != TOK_EOF) {
+
+ if (parse_postfix_assignment_statement_now ()) {
+
+ if (tok.kind == TOK_SEMI) {
+
+ get_token ();
+ return;
+
+ }
+
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+
+ expect (TOK_SEMI, ";");
+ return;
+
+ }
+
+ emit_load_assignment_rhs_expression_to_reg ("eax");
+
+ if (tok.kind == TOK_SEMI) {
+
+ get_token ();
+ return;
+
+ }
+
+ skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
+
+ expect (TOK_SEMI, ";");
+ return;
+
+ }
+
+ expect (TOK_SEMI, ";");
+
+}
+
+static void parse_function_body (const char *name, int storage_class, int is_inline, int return_is_void, int return_is_floating, int return_is_unsigned, int return_size, unsigned long function_line, const char *function_start, const char *function_caret) {
+
+ int old_return_label = current_return_label;
+ int should_emit = 1;
+ int emit_body = 1;
+ int emit_public = 1;
+ int emit_inline_definition_to_output = 1;
+
+ FILE *saved_ofp;
+ FILE *inline_tmp = 0;
+
+ char *inline_asm_text = 0;
+ int capture_inline_body = 0;
+
+ int saved_declarator_function_param_count;
+ int saved_declarator_function_has_prototype;
+ int saved_declarator_function_is_variadic;
+ int saved_current_section = current_section;
+
+ int old_function_is_void;
+ int old_function_is_floating;
+ int old_function_return_size;
+ int old_function_return_is_unsigned;
+ int old_function_returns_aggregate;
+ int old_function_has_return_statement;
+
+ char *function_filename_copy = 0;
+
+ /**
+ * Inline definitions are compiled into a temporary assembler buffer so
+ * calls in this translation unit can be expanded at the call site.
+ *
+ * inline int f(...) { ... }
+ * static inline int f(...) { ... }
+ * Remember the generated body for inline expansion. Do not emit an
+ * out-of-line copy unless a later rule explicitly asks for one.
+ *
+ * extern inline int f(...) { ... }
+ * Remember the generated body and also emit the normal public
+ * definition.
+ *
+ * int f(...) { ... }
+ * Normal external definition. Emit it as public.
+ */
+ if (storage_class == STORAGE_STATIC) {
+ emit_public = 0;
+ } else if (is_inline && storage_class != STORAGE_EXTERN) {
+ emit_public = 0;
+ }
+
+ if (is_inline && storage_class != STORAGE_EXTERN && !return_is_floating) {
+ emit_inline_definition_to_output = 0;
+ }
+
+ saved_ofp = state->ofp;
+
+ saved_declarator_function_param_count = declarator_function_param_count;
+ saved_declarator_function_has_prototype = declarator_function_has_prototype;
+ saved_declarator_function_is_variadic = declarator_function_is_variadic;
+
+ if (get_filename ()) {
+ function_filename_copy = xstrdup (get_filename ());
+ }
+
+ capture_inline_body = is_inline && saved_ofp != 0;
+
+ if (capture_inline_body) {
+
+ inline_tmp = tmpfile ();
+
+ if (!inline_tmp) {
+ capture_inline_body = 0;
+ }
+
+ }
+
+ if (emit_body) {
+
+ should_emit = add_global_symbol (name, GLOBAL_SYMBOL_FUNCTION, 0, last_declarator_name_start, last_declarator_name_caret, last_declarator_name_line);
+
+ set_global_symbol_size (name, return_is_void ? DATA_VOID : return_size);
+ set_global_symbol_unsigned (name, 0);
+ set_global_symbol_floating (name, return_is_floating);
+ set_global_symbol_returns_void (name, return_is_void);
+ set_global_symbol_param_count (name, saved_declarator_function_param_count, saved_declarator_function_has_prototype || saved_declarator_function_param_count > 0, saved_declarator_function_is_variadic);
+
+ if (is_inline) {
+
+ remember_inline_function_signature (name, saved_declarator_function_param_count, saved_declarator_function_has_prototype || saved_declarator_function_param_count > 0,
+ return_is_void, return_is_floating, return_size);
+
+ }
+
+ }
+
+ old_function_has_return_statement = current_function_has_return_statement;
+ old_function_is_void = current_function_is_void;
+ old_function_is_floating = current_function_is_floating;
+ old_function_return_size = current_function_return_size;
+ old_function_return_is_unsigned = current_function_return_is_unsigned;
+ old_function_returns_aggregate = current_function_returns_aggregate;
+
+ current_function_is_void = return_is_void;
+ current_function_is_floating = return_is_floating;
+ current_function_return_is_unsigned = return_is_unsigned;
+ current_function_return_size = return_size;
+ current_function_returns_aggregate = (!return_is_void && !return_is_floating && return_size > (DATA_LLONG & 0x1f));
+ current_function_has_return_statement = 0;
+
+ reset_local_symbols ();
+ reset_goto_labels ();
+
+ install_pending_params_as_locals ();
+ clear_pending_params ();
+
+ if (!emit_body || !should_emit) {
+ state->ofp = 0;
+ } else if (capture_inline_body) {
+ state->ofp = inline_tmp;
+ }
+
+ current_return_label = anon_label++;
+ pending_return_jump = 0;
+
+ emit_function_start (name, !emit_public);
+
+ parse_old_style_param_decls ();
+ parse_block ();
+ check_goto_labels ();
+
+ if (!current_function_is_void && !current_function_has_return_statement) {
+ report_line_at (function_filename_copy ? function_filename_copy : get_filename (), function_line, REPORT_WARNING, function_start, function_caret, "control reaches end of non-void function");
+ }
+
+ pending_return_jump = 0;
+
+ emit_function_end ();
+ emit_goto_trampolines ();
+
+ if (capture_inline_body && inline_tmp) {
+
+ inline_asm_text = read_tmp_file_text (inline_tmp);
+
+ if (inline_asm_text) {
+
+ if (emit_inline_definition_to_output) {
+ fputs (inline_asm_text, saved_ofp);
+ }
+
+ remember_inline_function (name, inline_asm_text, current_return_label,
+ saved_declarator_function_param_count, saved_declarator_function_has_prototype || saved_declarator_function_param_count > 0,
+ return_is_void, return_is_floating, return_size);
+
+ }
+
+ fclose (inline_tmp);
+ inline_tmp = 0;
+
+ }
+
+ reset_local_symbols ();
+ reset_goto_labels ();
+
+ current_function_has_return_statement = old_function_has_return_statement;
+ current_function_is_void = old_function_is_void;
+ current_function_is_floating = old_function_is_floating;
+ current_function_return_size = old_function_return_size;
+ current_function_return_is_unsigned = old_function_return_is_unsigned;
+ current_function_returns_aggregate = old_function_returns_aggregate;
+
+ current_return_label = old_return_label;
+ state->ofp = saved_ofp;
+
+ if (capture_inline_body) {
+ current_section = saved_current_section;
+ }
+
+ if (function_filename_copy) {
+ free (function_filename_copy);
+ }
+
+ if (inline_asm_text) {
+ free (inline_asm_text);
+ }
+
+}
+
+static int parse_possible_knr_function (void) {
+
+ char *name;
+
+ const char *name_start;
+ const char *name_caret;
+
+ unsigned long name_line;
+
+ if (!token_is_ident ()) {
+ return 0;
+ }
+
+ name_line = get_line_number ();
+
+ name_start = tok.start;
+ name_caret = tok.caret;
+
+ name = take_ident ();
+
+ if (!_accept (TOK_LPAREN)) {
+
+ free (name);
+ return 0;
+
+ }
+
+ if (tok.kind != TOK_RPAREN) {
+
+ for (;;) {
+
+ if (token_is_ident ()) {
+
+ add_pending_param (tok.ident, DATA_INT & 0x1f, type_alignment (DATA_INT & 0x1f), 0, 0, 0, 0);
+ get_token ();
+
+ } else {
+ skip_balanced_until (TOK_COMMA, TOK_RPAREN, TOK_EOF);
+ }
+
+ if (!_accept (TOK_COMMA)) {
+ break;
+ }
+
+ }
+
+ }
+
+ expect (TOK_RPAREN, ")");
+
+ if (is_type_start (tok.kind) || tok.kind == TOK_LBRACE) {
+
+ parse_function_body (name, STORAGE_NONE, 0, 0, 0, 0, DATA_INT & 0x1f, name_line, name_start, name_caret);
+
+ free (name);
+ return 1;
+
+ }
+
+ free (name);
+ return 0;
+
+}
+
+static int is_string_token (void) {
+ return tok.kind == TOK_PPSTR || tok.kind == TOK_LSTR;
+}
+
+static void append_global_init_byte (int64_s *values, int max_values, int *count, unsigned int value) {
+
+ if (*count < max_values) {
+ zext64 (&values[*count], value & 0xffU);
+ }
+
+ (*count)++;
+
+}
+
+static int parse_octal_escape_value (const char **ps) {
+
+ const char *s = *ps;
+ int value = 0, i;
+
+ for (i = 0; i < 3; i++) {
+
+ if (*s < '0' || *s > '7') {
+ break;
+ }
+
+ value = value * 8 + (*s - '0');
+ s++;
+
+ }
+
+ *ps = s;
+ return value;
+
+}
+
+static int parse_hex_escape_value (const char **ps) {
+
+ const char *s = *ps;
+
+ int value = 0;
+ int any = 0;
+
+ while ((*s >= '0' && *s <= '9') || (*s >= 'a' && *s <= 'f') || (*s >= 'A' && *s <= 'F')) {
+
+ int digit;
+
+ if (*s >= '0' && *s <= '9') {
+ digit = *s - '0';
+ } else if (*s >= 'a' && *s <= 'f') {
+ digit = *s - 'a' + 10;
+ } else {
+ digit = *s - 'A' + 10;
+ }
+
+ value = (value << 4) + digit;
+ any = 1;
+
+ s++;
+
+ }
+
+ if (!any) {
+ value = 'x';
+ }
+
+ *ps = s;
+ return value;
+
+}
+
+static void parse_string_initializer_values (int64_s *values, int max_values, int *count) {
+
+ while (is_string_token ()) {
+
+ const char *s;
+ int quote;
+
+ s = tok.ident;
+
+ if (tok.kind == TOK_LSTR && *s == 'L') {
+ s++;
+ }
+
+ quote = *s;
+
+ if (quote != '"') {
+
+ get_token ();
+ continue;
+
+ }
+
+ s++;
+
+ while (*s && *s != '"') {
+
+ unsigned int ch;
+
+ if (*s == '\\') {
+
+ s++;
+
+ switch (*s) {
+
+ case 'a':
+
+ ch = '\a';
+
+ s++;
+ break;
+
+ case 'b':
+
+ ch = '\b';
+
+ s++;
+ break;
+
+ case 'f':
+
+ ch = '\f';
+
+ s++;
+ break;
+
+ case 'n':
+
+ ch = '\n';
+
+ s++;
+ break;
+
+ case 'r':
+
+ ch = '\r';
+
+ s++;
+ break;
+
+ case 't':
+
+ ch = '\t';
+
+ s++;
+ break;
+
+ case 'v':
+
+ ch = '\v';
+
+ s++;
+ break;
+
+ case '\\':
+
+ ch = '\\';
+
+ s++;
+ break;
+
+ case '\'':
+
+ ch = '\'';
+
+ s++;
+ break;
+
+ case '"':
+
+ ch = '"';
+
+ s++;
+ break;
+
+ case '?':
+
+ ch = '?';
+
+ s++;
+ break;
+
+ case 'x':
+
+ s++;
+
+ ch = (unsigned int) parse_hex_escape_value (&s);
+ break;
+
+ case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+
+ ch = (unsigned int) parse_octal_escape_value (&s);
+ break;
+
+ case '\0':
+
+ ch = 0;
+ break;
+
+ default:
+
+ ch = (unsigned char) *s;
+
+ if (*s) {
+ s++;
+ }
+
+ break;
+
+ }
+
+ } else {
+ ch = (unsigned char) *s++;
+ }
+
+ append_global_init_byte (values, max_values, count, ch);
+
+ }
+
+ get_token ();
+
+ }
+
+ append_global_init_byte (values, max_values, count, 0);
+
+}
+
+static void parse_char_array_initializer_values (int64_s *values, int max_values, int *count, long row_width) {
+
+ if (tok.kind == TOK_LBRACE) {
+
+ get_token ();
+
+ while (tok.kind != TOK_EOF && tok.kind != TOK_RBRACE) {
+
+ parse_char_array_initializer_values (values, max_values, count, row_width);
+
+ if (!_accept (TOK_COMMA)) {
+ break;
+ }
+
+ }
+
+ expect (TOK_RBRACE, "}");
+ return;
+
+ }
+
+ if (is_string_token ()) {
+
+ int64_s tmp[MAX_STRING_INIT_BYTES];
+ int tmp_count = 0;
+
+ int i;
+ int n;
+
+ parse_string_initializer_values (tmp, MAX_STRING_INIT_BYTES, &tmp_count);
+
+ if (row_width <= 0) {
+ n = tmp_count;
+ } else {
+ n = (tmp_count < row_width) ? tmp_count : (int) row_width;
+ }
+
+ for (i = 0; i < n; i++) {
+ append_global_init_byte (values, max_values, count, (unsigned int) (tmp[i].low & 0xff));
+ }
+
+ while (row_width > 0 && i < row_width) {
+
+ append_global_init_byte (values, max_values, count, 0);
+ i++;
+
+ }
+
+ return;
+
+ }
+
+ if (tok.kind != TOK_COMMA && tok.kind != TOK_SEMI && tok.kind != TOK_RBRACE && tok.kind != TOK_EOF) {
+
+ if (*count < max_values) {
+ values[*count] = const64_from_current_expr ();
+ } else {
+ const64_from_current_expr ();
+ }
+
+ (*count)++;
+
+ }
+
+}
+
+static int global_initializer_parenthesized_string_now (void) {
+
+ const char *p = tok.caret;
+
+ if (!p || *p != '(') {
+ return 0;
+ }
+
+ p++;
+
+ while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+ p++;
+ }
+
+ if (*p == '"') {
+ return 1;
+ }
+
+ if ((*p == 'L' || *p == 'u' || *p == 'U') && p[1] == '"') {
+ return 1;
+ }
+
+ if (*p == 'u' && p[1] == '8' && p[2] == '"') {
+ return 1;
+ }
+
+ return 0;
+
+}
+
+static int global_initializer_cast_before_string_now (void) {
+
+ const char *p = tok.caret;
+ int depth;
+
+ if (!p || *p != '(') {
+ return 0;
+ }
+
+ p++;
+
+ while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+ p++;
+ }
+
+ if (!(ident_start_now ((unsigned char) *p))) {
+ return 0;
+ }
+
+ depth = 1;
+
+ while (*p && depth > 0) {
+
+ if (*p == '(') {
+ depth++;
+ } else if (*p == ')') {
+ depth--;
+ }
+
+ p++;
+
+ }
+
+ if (depth != 0) {
+ return 0;
+ }
+
+ while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
+ p++;
+ }
+
+ if (*p == '"') {
+ return 1;
+ }
+
+ if ((*p == 'L' || *p == 'u' || *p == 'U') && p[1] == '"') {
+ return 1;
+ }
+
+ if (*p == 'u' && p[1] == '8' && p[2] == '"') {
+ return 1;
+ }
+
+ return 0;
+
+}
+
+static void append_global_zero_initializer_value (int64_s *values, char **symbols, int max_values, int *count) {
+
+ if (*count < max_values) {
+
+ values[*count].low = 0;
+ values[*count].high = 0;
+
+ if (symbols) {
+ symbols[*count] = 0;
+ }
+
+ }
+
+ (*count)++;
+
+}
+
+static int aggregate_initializer_value_field_count (const int *field_sizes, int field_count) {
+
+ int count = 0;
+ int i;
+
+ for (i = 0; i < field_count; i++) {
+
+ if (field_sizes[i] > 0) {
+ count++;
+ }
+
+ }
+
+ return count;
+
+}
+
+static void parse_global_initializer_values_padded_elements (int64_s *values, char **symbols, int max_values, int *count, int element_field_count) {
+
+ if (element_field_count <= 0 || tok.kind != TOK_LBRACE) {
+
+ parse_global_initializer_values (values, symbols, max_values, count);
+ return;
+
+ }
+
+ get_token ();
+
+ while (tok.kind != TOK_EOF && tok.kind != TOK_RBRACE) {
+
+ int before = *count;
+ parse_global_initializer_values (values, symbols, max_values, count);
+
+ if (*count > before) {
+
+ while ((*count - before) < element_field_count) {
+ append_global_zero_initializer_value (values, symbols, max_values, count);
+ }
+
+ }
+
+ if (!_accept (TOK_COMMA)) {
+ break;
+ }
+
+ }
+
+ expect (TOK_RBRACE, "}");
+
+}
+
+static void parse_global_initializer_values (int64_s *values, char **symbols, int max_values, int *count) {
+
+ if (tok.kind == TOK_LBRACE) {
+
+ get_token ();
+
+ while (tok.kind != TOK_EOF && tok.kind != TOK_RBRACE) {
+
+ parse_global_initializer_values (values, symbols, max_values, count);
+
+ if (!_accept (TOK_COMMA)) {
+ break;
+ }
+
+ }
+
+ expect (TOK_RBRACE, "}");
+ return;
+
+ }
+
+ if (tok.kind != TOK_COMMA && tok.kind != TOK_SEMI && tok.kind != TOK_RBRACE && tok.kind != TOK_EOF) {
+
+ if (is_string_token ()) {
+
+ if (*count < max_values) {
+
+ values[*count].low = 0;
+ values[*count].high = 0;
+
+ if (symbols) {
+ symbols[*count] = emit_string_literal_global ();
+ }
+
+ } else {
+ get_token ();
+ }
+
+ (*count)++;
+
+ } else if (tok.kind == TOK_LPAREN && global_initializer_parenthesized_string_now ()) {
+
+ get_token ();
+
+ if (*count < max_values) {
+
+ values[*count].low = 0;
+ values[*count].high = 0;
+
+ if (symbols) {
+ symbols[*count] = emit_string_literal_global ();
+ } else {
+
+ int64_s ignored_values[MAX_AGG_FIELDS];
+ int ignored_count = 0;
+
+ parse_string_initializer_values (ignored_values, MAX_AGG_FIELDS, &ignored_count);
+
+ }
+
+ } else {
+
+ int64_s ignored_values[MAX_AGG_FIELDS];
+ int ignored_count = 0;
+
+ parse_string_initializer_values (ignored_values, MAX_AGG_FIELDS, &ignored_count);
+
+ }
+
+ expect (TOK_RPAREN, ")");
+ (*count)++;
+
+ } else if (tok.kind == TOK_LPAREN && global_initializer_cast_before_string_now ()) {
+
+ int cast_size = 0;
+ int cast_is_unsigned = 0;
+ int cast_is_pointer = 0;
+
+ get_token ();
+
+ if (token_starts_type_name () && parse_cast_type_name (&cast_size, &cast_is_unsigned, &cast_is_pointer) && is_string_token ()) {
+
+ if (*count < max_values) {
+
+ values[*count].low = 0;
+ values[*count].high = 0;
+
+ if (symbols) {
+ symbols[*count] = emit_string_literal_global ();
+ }
+
+ } else {
+ get_token ();
+ }
+
+ (*count)++;
+
+ } else {
+
+ if (*count < max_values) {
+
+ values[*count] = const64_from_current_expr ();
+
+ if (symbols) {
+ symbols[*count] = 0;
+ }
+
+ } else {
+ const64_from_current_expr ();
+ }
+
+ (*count)++;
+
+ }
+
+ } else if (tok.kind == TOK_AMPER) {
+
+ get_token ();
+
+ if (tok.kind != TOK_IDENT) {
+ report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "expected identifier after '&'");
+ } else {
+
+ char addr_symbol[256];
+ char *base_name = xstrdup (tok.ident);
+ long addr_offset = 0;
+ int sym_index = find_global_symbol (base_name);
+ long array_count = get_global_symbol_array_count (base_name);
+ long base_size = sym_index >= 0 ? global_symbols[sym_index].size : 0;
+ long elem_size = 0;
+
+ if (array_count > 0 && base_size > 0) {
+ elem_size = base_size / array_count;
+ }
+
+ get_token ();
+
+ while (tok.kind == TOK_LBRACK) {
+
+ int64_s index_value;
+ long index;
+
+ get_token ();
+
+ index_value.low = 0;
+ index_value.high = 0;
+
+ if (tok.kind != TOK_RBRACK) {
+ index_value = const64_from_current_expr ();
+ }
+
+ index = (long) index_value.low;
+
+ if (elem_size > 0) {
+ addr_offset += index * elem_size;
+ }
+
+ expect (TOK_RBRACK, "]");
+
+ }
+
+ if (*count < max_values) {
+
+ values[*count].low = 0;
+ values[*count].high = 0;
+
+ if (symbols) {
+
+ if (addr_offset != 0) {
+
+ sprintf (addr_symbol, "%s+%ld", base_name, addr_offset);
+ symbols[*count] = xstrdup (addr_symbol);
+
+ } else {
+ symbols[*count] = xstrdup (base_name);
+ }
+
+ }
+
+ }
+
+ (*count)++;
+ free (base_name);
+
+ }
+
+ } else if (global_initializer_accept_symbol_addresses && tok.kind == TOK_IDENT && find_global_symbol (tok.ident) >= 0) {
+
+ if (*count < max_values) {
+
+ values[*count].low = 0;
+ values[*count].high = 0;
+
+ if (symbols) {
+ symbols[*count] = xstrdup (tok.ident);
+ }
+
+ }
+
+ (*count)++;
+ get_token ();
+
+ } else if (*count < max_values) {
+
+ values[*count] = const64_from_current_expr ();
+
+ if (symbols) {
+ symbols[*count] = 0;
+ }
+
+ (*count)++;
+
+ } else {
+ const64_from_current_expr ();
+ }
+
+ }
+
+}
+
+static int int64_is_zero_value (int64_s value) {
+ return value.low == 0 && value.high == 0;
+}
+
+static void emit_global_scalar (int64_s value, int size) {
+
+ unsigned long high = value.high;
+ unsigned long low = value.low;
+
+ if ((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) {
+
+ if (size == (DATA_CHAR & 0x1f)) {
+ fprintf (state->ofp, " db %ld\n", low & 0xFFUL);
+ } else if (size == (DATA_SHORT & 0x1f)) {
+ fprintf (state->ofp, " dw %ld\n", low & 0xFFFFUL);
+ } else if (size == (DATA_LLONG & 0x1f) || size == (DATA_DOUBLE & 0x1f)) {
+
+ fprintf (state->ofp, " dd %lu\n", low & U32_MASK);
+ fprintf (state->ofp, " dd %lu\n", high & U32_MASK);
+
+ } else {
+ fprintf (state->ofp, " dd %lu\n", low & U32_MASK);
+ }
+
+ } else {
+
+ if (size == (DATA_CHAR & 0x1f)) {
+ fprintf (state->ofp, " .byte %ld\n", low & 0xFFUL);
+ } else if (size == (DATA_SHORT & 0x1f)) {
+ fprintf (state->ofp, " .word %ld\n", low & 0xFFFFUL);
+ } else if (size == (DATA_LLONG & 0x1f) || size == (DATA_DOUBLE & 0x1f)) {
+
+ fprintf (state->ofp, " .long %lu\n", low & U32_MASK);
+ fprintf (state->ofp, " .long %lu\n", high & U32_MASK);
+
+ } else {
+ fprintf (state->ofp, " .long %lu\n", low & U32_MASK);
+ }
+
+ }
+
+}
+
+static void emit_global_address (const char *symbol, int size) {
+
+ const char *asm_symbol;
+ int64_s zero;
+
+ if (!symbol || !*symbol) {
+
+ zero.low = 0;
+ zero.high = 0;
+
+ emit_global_scalar (zero, size);
+ return;
+
+ }
+
+ if (size != DATA_PTR) {
+ report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "address initializer requires pointer-sized object");
+ }
+
+ emit_extern_reference_symbol (symbol, DATA_PTR);
+ asm_symbol = asm_global_symbol_name (symbol);
+
+ if (state->syntax & ASM_SYNTAX_MASM) {
+ fprintf (state->ofp, " dd offset %s\n", asm_symbol);
+ } else if (state->syntax & ASM_SYNTAX_NASM) {
+ fprintf (state->ofp, " dd %s\n", asm_symbol);
+ } else {
+
+ if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, " .long offset %s\n", asm_symbol);
+ } else {
+ fprintf (state->ofp, " .long %s\n", asm_symbol);
+ }
+
+ }
+
+}
+
+static void emit_global_value (const int64_s *value, const char *symbol, int size) {
+
+ int64_s zero;
+
+ if (symbol) {
+
+ emit_global_address (symbol, size);
+ return;
+
+ }
+
+ if (value) {
+
+ emit_global_scalar (*value, size);
+ return;
+
+ }
+
+ zero.low = 0;
+ zero.high = 0;
+
+ emit_global_scalar (zero, size);
+
+}
+
+static void emit_global_space (long bytes) {
+
+ if (bytes <= 0) {
+ return;
+ }
+
+ if (state->syntax & ASM_SYNTAX_MASM) {
+ fprintf (state->ofp, " db %ld dup (0)\n", bytes);
+ } else if (state->syntax & ASM_SYNTAX_NASM) {
+ fprintf (state->ofp, " resb %ld\n", bytes);
+ } else {
+ fprintf (state->ofp, " .space %ld\n", bytes);
+ }
+
+}
+
+static int field_size_bytes (int field_size) {
+
+ if (field_size < 0) {
+ return -field_size;
+ }
+
+ return field_size;
+
+}
+
+static int global_initializer_is_all_zero (const int64_s *values, char **symbols, int value_count) {
+
+ int i;
+
+ if (symbols) {
+ for (i = 0; i < value_count; i++) {
+ if (symbols[i]) {
+ return 0;
+ }
+ }
+ }
+
+ if (!values || value_count <= 0) {
+ return 1;
+ }
+
+ for (i = 0; i < value_count; i++) {
+ if (!int64_is_zero_value (values[i])) {
+ return 0;
+ }
+ }
+
+ return 1;
+
+}
+
+static void emit_global_label (const char *name, int is_static) {
+
+ const char *asm_name = asm_global_symbol_name (name);
+
+ if (state->syntax & ASM_SYNTAX_MASM) {
+
+ if (!is_static) {
+ fprintf (state->ofp, "public %s\n", asm_name);
+ }
+
+ fprintf (state->ofp, "%s:\n", asm_name);
+
+ } else if (state->syntax & ASM_SYNTAX_NASM) {
+
+ if (!is_static) {
+ fprintf (state->ofp, "global %s\n", asm_name);
+ }
+
+ fprintf (state->ofp, "%s:\n", asm_name);
+
+ } else {
+
+ if (!is_static) {
+ fprintf (state->ofp, ".globl %s\n", asm_name);
+ }
+
+ fprintf (state->ofp, "%s:\n", asm_name);
+
+ }
+
+}
+
+static void emit_global_object (const char *name, int size, int is_array, long array_count, const int *field_sizes, int field_count, const int64_s *values, char **symbols, int value_count, int is_aggregate, int is_static) {
+
+ int64_s zero;
+
+ int value_index, use_bss, i;
+ long emitted, total;
+
+ zero.low = 0;
+ zero.high = 0;
+
+ if (!state->ofp || !name || !*name) {
+ return;
+ }
+
+ if (size <= 0) {
+ size = DATA_INT & 0x1f;
+ }
+
+ if (array_count <= 0) {
+ array_count = 1;
+ }
+
+ total = size * array_count;
+ use_bss = global_initializer_is_all_zero (values, symbols, value_count);
+
+ if (use_bss) {
+
+ switch_section (SECTION_BSS);
+
+ emit_global_label (name, is_static);
+ emit_global_space (total);
+
+ return;
+
+ }
+
+ switch_section (SECTION_DATA);
+ emit_global_label (name, is_static);
+
+ value_index = 0;
+ emitted = 0;
+
+ if (!is_array && !is_aggregate) {
+
+ if (value_count > 0) {
+ emit_global_value (&values[0], symbols ? symbols[0] : 0, size);
+ } else {
+ emit_global_value (0, 0, size);
+ }
+
+ return;
+
+ }
+
+ if (is_aggregate && field_count > 0) {
+
+ while (emitted < total) {
+
+ for (i = 0; i < field_count && emitted < total; i++) {
+
+ int fsize = field_sizes[i];
+
+ if (fsize < 0) {
+
+ emit_global_space (-fsize);
+
+ emitted += -fsize;
+ continue;
+
+ }
+
+ if (value_index < value_count) {
+
+ emit_global_value (&values[value_index], symbols ? symbols[value_index] : 0, fsize);
+ value_index++;
+
+ } else {
+ emit_global_scalar (zero, fsize);
+ }
+
+ emitted += field_size_bytes (fsize);
+
+ }
+
+ }
+
+ emit_global_space (total - emitted);
+ return;
+
+ }
+
+ if (is_array) {
+
+ int elem_size = size;
+
+ if (field_count > 0 && field_sizes[0] > 0) {
+ elem_size = field_sizes[0];
+ }
+
+ while (emitted + elem_size <= total) {
+
+ if (value_index < value_count) {
+
+ emit_global_value (&values[value_index], symbols ? symbols[value_index] : 0, elem_size);
+ value_index++;
+
+ } else {
+ emit_global_scalar (zero, elem_size);
+ }
+
+ emitted += elem_size;
+
+ }
+
+ emit_global_space (total - emitted);
+ return;
+
+ }
+
+ emit_global_space (total);
+
+}
+
+static char *emit_string_literal_global (void) {
+
+ int64_s values[MAX_AGG_FIELDS];
+
+ char label[64];
+ int value_count = 0, i;
+
+ if ((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) {
+ sprintf (label, "L%d", anon_label++);
+ } else {
+ sprintf (label, ".L%d", anon_label++);
+ }
+
+ parse_string_initializer_values (values, MAX_AGG_FIELDS, &value_count);
+
+ /*
+ * Dead statement parsing suppresses output by temporarily clearing
+ * state->ofp. Still consume the literal so the token stream remains
+ * correct, but do not try to emit a .data label through a NULL FILE *.
+ * This is needed for optimised-away bodies such as:
+ *
+ * while (0) { printf ("Hello\n"); }
+ */
+ if (!state->ofp) {
+ return xstrdup (label);
+ }
+
+ if (current_section == SECTION_TEXT) {
+
+ char skip_label[64];
+
+ if ((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) {
+ sprintf (skip_label, "L%d", anon_label++);
+ } else {
+ sprintf (skip_label, ".L%d", anon_label++);
+ }
+
+ fprintf (state->ofp, " jmp %s\n", skip_label);
+
+ switch_section (SECTION_DATA);
+ emit_global_label (label, 1);
+
+ for (i = 0; i < value_count; i++) {
+ emit_global_scalar (values[i], DATA_CHAR & 0x1f);
+ }
+
+ switch_section (SECTION_TEXT);
+ emit_global_label (skip_label, 1);
+
+ return xstrdup (label);
+
+ }
+
+ switch_section (SECTION_DATA);
+ emit_global_label (label, 1);
+
+ for (i = 0; i < value_count; i++) {
+ emit_global_scalar (values[i], DATA_CHAR & 0x1f);
+ }
+
+ return xstrdup (label);
+
+}
+
+static const char *masm_extern_type_name (int size, int is_function) {
+
+ if (is_function) {
+ return "PROC";
+ }
+
+ if (size == (DATA_CHAR & 0x1f)) {
+ return "BYTE";
+ }
+
+ if (size == (DATA_SHORT & 0x1f)) {
+ return "WORD";
+ }
+
+ if (size == (DATA_LLONG & 0x1f) || size == (DATA_DOUBLE & 0x1f)) {
+ return "QWORD";
+ }
+
+ return "DWORD";
+
+}
+
+static void emit_extern_line (const char *name, int size, int is_function) {
+
+ const char *asm_name = asm_global_symbol_name (name);
+
+ if (state->syntax & ASM_SYNTAX_MASM) {
+ fprintf (state->ofp, "extrn %s:%s\n", asm_name, masm_extern_type_name (size, is_function));
+ } else if (state->syntax & ASM_SYNTAX_NASM) {
+ fprintf (state->ofp, "extern %s\n", asm_name);
+ } else {
+ fprintf (state->ofp, ".extern %s\n", asm_name);
+ }
+
+}
+
+static void emit_extern_symbol (const char *name, int size, int is_function) {
+
+ int i;
+
+ (void) size;
+ (void) is_function;
+
+ if (!state->ofp || !name || !*name) {
+ return;
+ }
+
+ i = find_global_symbol (name);
+
+ /**
+ * Do not write assembler externs at the point of use. A symbol can be
+ * declared/called before its real definition appears later in the same
+ * translation unit; NASM then rejects "extern foo" followed by "foo:" as
+ * an inconsistent redefinition. Just mark that generated code referenced
+ * the external-looking symbol, and emit the actual assembler externs once
+ * the whole file has been parsed and we know which names stayed external.
+ */
+ if (i < 0 || !global_symbols[i].is_extern) {
+ return;
+ }
+
+ global_symbols[i].extern_emitted = 1;
+
+}
+
+static void emit_pending_extern_symbols (void) {
+
+ int i;
+
+ if (!state->ofp) {
+ return;
+ }
+
+ for (i = 0; i < global_symbol_count; i++) {
+
+ if (!global_symbols[i].is_extern || !global_symbols[i].extern_emitted) {
+ continue;
+ }
+
+ emit_extern_line (global_symbols[i].name,
+ global_symbols[i].size > 0 ? global_symbols[i].size : (DATA_INT & 0x1f),
+ global_symbols[i].kind == GLOBAL_SYMBOL_FUNCTION);
+
+ }
+
+}
+
+static void emit_extern_reference_symbol (const char *name, int size) {
+ emit_extern_symbol (name, size, get_global_symbol_kind (name) == GLOBAL_SYMBOL_FUNCTION);
+}
+
+static void parse_external_after_type (void) {
+
+ for (;;) {
+
+ char **init_symbols = 0;
+ int64_s *init_values = 0;
+
+ char *name = 0;
+ int init_value_count = 0, i;
+
+ int object_fields[MAX_AGG_FIELDS];
+ int object_field_count = 0;
+
+ const char *name_start, *name_caret;
+ unsigned long name_line;
+
+ int saved_field_count = parsed_field_count;
+
+ int declaration_is_inline;
+ int declaration_storage;
+
+ int decl_is_pointer;
+ int decl_pointer_depth;
+ int decl_has_array;
+ int decl_has_function;
+ int decl_function_is_pointer;
+ int decl_function_param_count;
+ int decl_function_has_prototype;
+ int decl_function_is_variadic;
+ int decl_array_unsized;
+
+ long decl_array_count;
+ long decl_last_array_count;
+
+ for (i = 0; i < saved_field_count; i++) {
+ object_fields[i] = parsed_field_sizes[i];
+ }
+
+ object_field_count = saved_field_count;
+
+ declaration_is_inline = parsed_type_is_inline;
+ declaration_storage = parsed_storage_class;
+
+ parse_declarator (&name);
+ apply_typedef_array_to_declarator ();
+
+ decl_is_pointer = declarator_is_pointer;
+ decl_pointer_depth = declarator_pointer_depth;
+ decl_has_array = declarator_has_array;
+ decl_has_function = declarator_has_function;
+ decl_function_is_pointer = declarator_function_is_pointer;
+ decl_function_param_count = declarator_function_param_count;
+ decl_function_has_prototype = declarator_function_has_prototype;
+ decl_function_is_variadic = declarator_function_is_variadic;
+ decl_array_unsized = declarator_array_unsized;
+ decl_array_count = declarator_array_count;
+ decl_last_array_count = declarator_last_array_count;
+
+ name_start = last_declarator_name_start;
+ name_caret = last_declarator_name_caret;
+
+ name_line = last_declarator_name_line;
+
+ if (declaration_storage == STORAGE_TYPEDEF) {
+
+ if (name) {
+
+ make_declarator_fields (object_fields, &object_field_count, parsed_field_sizes, parsed_field_count, parsed_type_size, parsed_type_is_aggregate);
+
+ save_typedef_name (name, declarator_object_size (parsed_type_size),
+ (declarator_is_pointer ? 0 : parsed_type_is_unsigned),
+ (declarator_is_pointer ? 0 : parsed_type_is_void),
+ (!declarator_is_pointer && (parsed_type_is_aggregate || declarator_has_array)),
+ (!declarator_is_pointer && declarator_has_array),
+ declarator_array_count, parsed_type_size,
+ object_fields, object_field_count);
+
+ }
+
+ if (_accept (TOK_ASSIGN)) {
+
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "typedef '%s' is initialized", name ? name : "");
+ skip_initializer ();
+
+ }
+
+ if (name) {
+ free (name);
+ }
+
+ if (!_accept (TOK_COMMA)) {
+ break;
+ }
+
+ continue;
+
+ }
+
+ if (name && tok.kind == TOK_LBRACE) {
+
+ parse_function_body (name, declaration_storage, declaration_is_inline,
+ (parsed_type_is_void && !declarator_is_pointer),
+ (declarator_is_pointer ? 0 : parsed_type_is_floating),
+ (declarator_is_pointer ? 0 : parsed_type_is_unsigned),
+ (declarator_is_pointer ? DATA_PTR : parsed_type_size),
+ name_line, name_start, name_caret);
+
+ free (name);
+ return;
+
+ }
+
+ if (parsed_type_is_void && !declarator_is_pointer && !declarator_has_function) {
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "variable '%s' declared void", name ? name : "");
+ }
+
+ if (declarator_is_pointer) {
+
+ object_field_count = 1;
+ object_fields[0] = DATA_PTR;
+
+ }
+
+ init_values = xmalloc (sizeof (*init_values) * MAX_GLOBAL_INIT_FIELDS);
+ init_symbols = xmalloc (sizeof (*init_symbols) * MAX_GLOBAL_INIT_FIELDS);
+
+ for (i = 0; i < MAX_GLOBAL_INIT_FIELDS; i++) {
+ init_symbols[i] = 0;
+ }
+
+ if (_accept (TOK_ASSIGN)) {
+
+ if (declarator_has_array && !declarator_is_pointer && (parsed_type_size & 0x1f) == (DATA_CHAR & 0x1f) && (is_string_token () || tok.kind == TOK_LBRACE)) {
+
+ if (is_string_token ()) {
+ parse_string_initializer_values (init_values, MAX_GLOBAL_INIT_FIELDS, &init_value_count);
+ } else {
+ parse_char_array_initializer_values (init_values, MAX_GLOBAL_INIT_FIELDS, &init_value_count, declarator_last_array_count);
+ }
+
+ if (decl_array_unsized) {
+ decl_array_count = init_value_count;
+ }
+
+ } else if (declarator_is_pointer && is_string_token ()) {
+
+ init_values[0].low = 0;
+ init_values[0].high = 0;
+
+ init_symbols[0] = emit_string_literal_global ();
+ init_value_count = 1;
+
+ } else if (!declarator_is_pointer && parsed_type_is_floating) {
+
+ init_values[0] = parse_floating_const_expr_bits_now (parsed_type_size);
+ init_symbols[0] = 0;
+ init_value_count = 1;
+
+ } else {
+
+ {
+
+ int saved_accept_symbol_addresses = global_initializer_accept_symbol_addresses;
+ global_initializer_accept_symbol_addresses = declarator_is_pointer || declarator_has_function || parsed_type_is_aggregate || declarator_has_array;
+
+ if (declarator_has_array && !declarator_is_pointer && parsed_type_is_aggregate && parsed_field_count > 0 && tok.kind == TOK_LBRACE) {
+ parse_global_initializer_values_padded_elements (init_values, init_symbols, MAX_GLOBAL_INIT_FIELDS, &init_value_count, aggregate_initializer_value_field_count (parsed_field_sizes, parsed_field_count));
+ } else {
+ parse_global_initializer_values (init_values, init_symbols, MAX_GLOBAL_INIT_FIELDS, &init_value_count);
+ }
+
+ global_initializer_accept_symbol_addresses = saved_accept_symbol_addresses;
+
+ }
+
+ if (decl_has_array && decl_array_unsized) {
+
+ if (parsed_type_is_aggregate && object_field_count > 0) {
+ decl_array_count = (init_value_count + object_field_count - 1) / object_field_count;
+ } else {
+ decl_array_count = init_value_count;
+ }
+
+ }
+
+ }
+
+ }
+
+ if (init_value_count > MAX_GLOBAL_INIT_FIELDS) {
+
+ report_line_at (get_filename (), name_line, REPORT_ERROR, name_start, name_caret, "too many initializer values for '%s'", name ? name : "");
+ init_value_count = MAX_GLOBAL_INIT_FIELDS;
+
+ }
+
+ declarator_is_pointer = decl_is_pointer;
+ declarator_pointer_depth = decl_pointer_depth;
+ declarator_has_array = decl_has_array;
+ declarator_has_function = decl_has_function;
+ declarator_function_is_pointer = decl_function_is_pointer;
+ declarator_function_param_count = decl_function_param_count;
+ declarator_function_has_prototype = decl_function_has_prototype;
+ declarator_function_is_variadic = decl_function_is_variadic;
+ declarator_array_unsized = decl_array_unsized;
+ declarator_array_count = decl_array_count;
+ declarator_last_array_count = decl_last_array_count;
+
+ if (name) {
+
+ if (declarator_has_function && !declarator_function_is_pointer && !declarator_has_array) {
+
+ /*
+ * A file-scope function declaration is a declaration even
+ * without an explicit extern storage class. Keep it in the
+ * global symbol table so a later call does not fall back to
+ * the implicit-function-declaration path.
+ *
+ * Store prototypes as extern-like declarations here. A real
+ * function body will later turn the symbol into a definition
+ * through add_global_symbol(), which already handles replacing
+ * an extern declaration with the definition.
+ */
+ if (add_global_symbol (name, GLOBAL_SYMBOL_FUNCTION, 1, name_start, name_caret, name_line)) {
+
+ set_global_symbol_size (name, declarator_is_pointer ? DATA_PTR : (parsed_type_is_void ? DATA_VOID : parsed_type_size));
+ set_global_symbol_pointer_info (name, declarator_effective_pointer_depth_now (),
+ declarator_effective_pointed_size_now (parsed_type_size, object_fields, object_field_count));
+ set_global_symbol_tag_name (name, parsed_type_tag_name);
+ set_global_symbol_unsigned (name, 0);
+ set_global_symbol_floating (name, declarator_is_pointer ? 0 : parsed_type_is_floating);
+ set_global_symbol_returns_void (name, parsed_type_is_void && !declarator_is_pointer && !declarator_function_is_pointer);
+ set_global_symbol_param_count (name, declarator_function_param_count, declarator_function_has_prototype || declarator_function_param_count > 0, declarator_function_is_variadic);
+
+ }
+
+ } else if (declaration_storage == STORAGE_EXTERN && init_value_count == 0) {
+
+ if (add_global_symbol (name, GLOBAL_SYMBOL_OBJECT, 1, name_start, name_caret, name_line)) {
+
+ set_global_symbol_size (name, declarator_is_pointer ? DATA_PTR : declarator_object_size (parsed_type_size));
+ set_global_symbol_pointer_info (name, declarator_effective_pointer_depth_now (),
+ declarator_effective_pointed_size_now (parsed_type_size, object_fields, object_field_count));
+ set_global_symbol_tag_name (name, parsed_type_tag_name);
+ set_global_symbol_unsigned (name, declarator_is_pointer ? 0 : parsed_type_is_unsigned);
+ set_global_symbol_array (name, declarator_has_array);
+ set_global_symbol_array_count (name, declarator_has_array ? declarator_array_count : 0);
+ set_global_symbol_array_element_size (name, declarator_has_array ? (int)(declarator_object_size (parsed_type_size) / (declarator_array_count > 0 ? declarator_array_count : 1)) : 0);
+
+ }
+
+ } else {
+
+ if (add_global_symbol (name, GLOBAL_SYMBOL_OBJECT, 0, name_start, name_caret, name_line)) {
+
+ set_global_symbol_size (name, declarator_is_pointer ? DATA_PTR : declarator_object_size (parsed_type_size));
+ set_global_symbol_pointer_info (name, declarator_effective_pointer_depth_now (),
+ declarator_effective_pointed_size_now (parsed_type_size, object_fields, object_field_count));
+ set_global_symbol_tag_name (name, parsed_type_tag_name);
+ set_global_symbol_unsigned (name, declarator_is_pointer ? 0 : parsed_type_is_unsigned);
+ set_global_symbol_floating (name, declarator_is_pointer ? 0 : parsed_type_is_floating);
+ set_global_symbol_array (name, declarator_has_array);
+ set_global_symbol_array_count (name, declarator_has_array ? declarator_array_count : 0);
+ set_global_symbol_array_element_size (name, declarator_has_array ? (int)(declarator_object_size (parsed_type_size) / (declarator_array_count > 0 ? declarator_array_count : 1)) : 0);
+
+ emit_global_object (name, declarator_is_pointer ? DATA_PTR : (declarator_has_array ? parsed_type_size : (parsed_type_is_aggregate ? parsed_type_size : (parsed_type_size & 0x1f))),
+ declarator_has_array, declarator_array_count, object_fields, object_field_count,
+ init_values, init_symbols, init_value_count, (!declarator_is_pointer && parsed_type_is_aggregate),
+ declaration_storage == STORAGE_STATIC);
+
+ }
+
+ }
+
+ free (name);
+
+ }
+
+ for (i = 0; i < MAX_GLOBAL_INIT_FIELDS; i++) {
+
+ if (init_symbols[i]) {
+
+ free (init_symbols[i]);
+ init_symbols[i] = 0;
+
+ }
+
+ }
+
+ free (init_symbols);
+ free (init_values);
+
+ if (!_accept (TOK_COMMA)) {
+ break;
+ }
+
+ }
+
+ if (tok.kind == TOK_SEMI) {
+ get_token ();
+ } else if (is_type_start (tok.kind)) {
+ report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "missing ';' after declaration");
+ } else {
+ expect (TOK_SEMI, ";");
+ }
+
+}
+
+void compile_translation_unit (void) {
+
+ if (state->ofp) {
+
+ if (state->syntax & ASM_SYNTAX_MASM) {
+
+ fprintf (state->ofp, ".386\n");
+ fprintf (state->ofp, ".model flat, c\n");
+
+ } else if (state->syntax & ASM_SYNTAX_NASM) {
+
+ fprintf (state->ofp, "cpu 386\n");
+ fprintf (state->ofp, "bits 32\n");
+
+ } else if (state->syntax & ASM_SYNTAX_INTEL) {
+ fprintf (state->ofp, ".intel_syntax noprefix\n");
+ }
+
+ }
+
+ current_section = SECTION_NONE;
+
+ clear_global_symbols ();
+ clear_inline_functions ();
+ clear_enum_constants ();
+ clear_typedef_names ();
+
+ get_token ();
+
+ while (tok.kind != TOK_EOF) {
+
+ if (is_type_start (tok.kind)) {
+
+ parse_type_spec ();
+ parse_external_after_type ();
+
+ continue;
+
+ }
+
+ if (parse_possible_knr_function ()) {
+ continue;
+ }
+
+ report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "expected external declaration");
+ get_token ();
+
+ }
+
+ emit_pending_extern_symbols ();
+
+ if (state->ofp) {
+
+ if (state->syntax & ASM_SYNTAX_MASM) {
+ fprintf (state->ofp, "end\n");
+ }
+
+ }
+
+}