/* This file is a part of MIR project. Copyright (C) 2018-2020 Vladimir Makarov . */ /* C to MIR compiler. It is a four pass compiler: o preprocessor pass generating tokens o parsing pass generating AST o context pass checking context constraints and augmenting AST o generation pass producing MIR The compiler implements C11 standard w/o C11 optional features: atomic, complex, variable size arrays. */ #include #include #include #include #include #include #include #include #include #include #include "time.h" #include "c2mir.h" #if defined(__x86_64__) #include "x86_64/cx86_64.h" #elif defined(__aarch64__) #include "aarch64/caarch64.h" #elif defined(__PPC64__) #include "ppc64/cppc64.h" #else #error "undefined or unsupported generation target for C" #endif #define SWAP(a1, a2, t) \ do { \ t = a1; \ a1 = a2; \ a2 = t; \ } while (0) typedef enum { C_alloc_error, C_unfinished_comment, C_out_of_range_number, C_invalid_char_constant, C_no_string_end, C_invalid_str_constant, C_invalid_char, } C_error_code_t; DEF_VARR (char); typedef struct pos { const char *fname; int lno, ln_pos; } pos_t; static const pos_t no_pos = {NULL, -1, -1}; typedef struct c2m_ctx *c2m_ctx_t; typedef struct stream { FILE *f; /* the current file, NULL for top-level or string stream */ const char *fname; /* NULL only for preprocessor string stream */ int (*getc_func) (c2m_ctx_t); /* get function for top-level or string stream */ VARR (char) * ln; /* stream current line in reverse order */ pos_t pos; /* includes file name used for reports */ fpos_t fpos; /* file pos to resume file stream */ const char *start, *curr; /* non NULL only for string stream */ int ifs_length_at_stream_start; /* length of ifs at the stream start */ } * stream_t; DEF_VARR (stream_t); typedef const char *char_ptr_t; DEF_VARR (char_ptr_t); typedef void *void_ptr_t; DEF_VARR (void_ptr_t); typedef struct { const char *s; size_t len, key, flags; } str_t; DEF_HTAB (str_t); typedef struct token *token_t; DEF_VARR (token_t); typedef struct node *node_t; enum symbol_mode { S_REGULAR, S_TAG, S_LABEL }; DEF_VARR (node_t); typedef struct { enum symbol_mode mode; node_t id; node_t scope; node_t def_node, aux_node; VARR (node_t) * defs; } symbol_t; DEF_HTAB (symbol_t); struct init_object { struct type *container_type; int designator_p; union { mir_llong curr_index; node_t curr_member; } u; }; typedef struct init_object init_object_t; DEF_VARR (init_object_t); struct pre_ctx; struct parse_ctx; struct check_ctx; struct gen_ctx; struct c2m_ctx { jmp_buf env; struct c2mir_options *options; VARR (char_ptr_t) * headers; VARR (char_ptr_t) * system_headers; const char **header_dirs, **system_header_dirs; void (*error_func) (c2m_ctx_t, C_error_code_t code, const char *message); VARR (void_ptr_t) * reg_memory; VARR (stream_t) * streams; /* stack of streams */ stream_t cs, eof_s; /* current stream and stream corresponding the last EOF */ HTAB (str_t) * str_tab; HTAB (str_t) * str_key_tab; str_t empty_str; unsigned long curr_uid; int (*c_getc) (void *); /* c2mir interface get function */ void *c_getc_data; unsigned n_errors, n_warnings; VARR (char) * symbol_text, *temp_string; VARR (token_t) * recorded_tokens, *buffered_tokens; node_t top_scope; HTAB (symbol_t) * symbol_tab; VARR (node_t) * call_nodes; VARR (node_t) * containing_anon_members; VARR (init_object_t) * init_object_path; struct pre_ctx *pre_ctx; struct parse_ctx *parse_ctx; struct check_ctx *check_ctx; struct gen_ctx *gen_ctx; }; typedef struct c2m_ctx *c2m_ctx_t; #define options c2m_ctx->options #define headers c2m_ctx->headers #define system_headers c2m_ctx->system_headers #define header_dirs c2m_ctx->header_dirs #define system_header_dirs c2m_ctx->system_header_dirs #define error_func c2m_ctx->error_func #define reg_memory c2m_ctx->reg_memory #define str_tab c2m_ctx->str_tab #define streams c2m_ctx->streams #define cs c2m_ctx->cs #define eof_s c2m_ctx->eof_s #define str_key_tab c2m_ctx->str_key_tab #define empty_str c2m_ctx->empty_str #define curr_uid c2m_ctx->curr_uid #define c_getc c2m_ctx->c_getc #define c_getc_data c2m_ctx->c_getc_data #define n_errors c2m_ctx->n_errors #define n_warnings c2m_ctx->n_warnings #define symbol_text c2m_ctx->symbol_text #define temp_string c2m_ctx->temp_string #define recorded_tokens c2m_ctx->recorded_tokens #define buffered_tokens c2m_ctx->buffered_tokens #define top_scope c2m_ctx->top_scope #define symbol_tab c2m_ctx->symbol_tab #define call_nodes c2m_ctx->call_nodes #define containing_anon_members c2m_ctx->containing_anon_members #define init_object_path c2m_ctx->init_object_path static inline c2m_ctx_t *c2m_ctx_loc (MIR_context_t ctx) { return (c2m_ctx_t *) ((void **) ctx + 1); } static void alloc_error (c2m_ctx_t c2m_ctx, const char *message) { error_func (c2m_ctx, C_alloc_error, message); } static const int max_nested_includes = 32; #define MIR_VARR_ERROR alloc_error #define MIR_HTAB_ERROR MIR_VARR_ERROR #define FALSE 0 #define TRUE 1 #include "mir-varr.h" #include "mir-dlist.h" #include "mir-hash.h" #include "mir-htab.h" static mir_size_t round_size (mir_size_t size, mir_size_t round) { return (size + round - 1) / round * round; } /* Some abbreviations: */ #define NL_HEAD(list) DLIST_HEAD (node_t, list) #define NL_TAIL(list) DLIST_TAIL (node_t, list) #define NL_LENGTH(list) DLIST_LENGTH (node_t, list) #define NL_NEXT(el) DLIST_NEXT (node_t, el) #define NL_PREV(el) DLIST_PREV (node_t, el) #define NL_REMOVE(list, el) DLIST_REMOVE (node_t, list, el) #define NL_APPEND(list, el) DLIST_APPEND (node_t, list, el) #define NL_PREPEND(list, el) DLIST_PREPEND (node_t, list, el) #define NL_EL(list, n) DLIST_EL (node_t, list, n) enum basic_type { TP_UNDEF, TP_VOID, /* Integer types: the first should be BOOL and the last should be ULLONG. The order is important -- do not change it. */ TP_BOOL, TP_CHAR, TP_SCHAR, TP_UCHAR, TP_SHORT, TP_USHORT, TP_INT, TP_UINT, TP_LONG, TP_ULONG, TP_LLONG, TP_ULLONG, TP_FLOAT, TP_DOUBLE, TP_LDOUBLE, }; #define ENUM_BASIC_INT_TYPE TP_INT #define ENUM_MIR_INT mir_int struct type_qual { unsigned int const_p : 1, restrict_p : 1, volatile_p : 1, atomic_p : 1; /* Type qualifiers */ }; static const struct type_qual zero_type_qual = {0, 0, 0, 0}; struct arr_type { unsigned int static_p : 1; struct type *el_type; struct type_qual ind_type_qual; node_t size; }; struct func_type { unsigned int dots_p : 1; struct type *ret_type; node_t param_list; /* w/o N_DOTS */ MIR_item_t proto_item; }; enum type_mode { TM_UNDEF, TM_BASIC, TM_ENUM, TM_PTR, TM_STRUCT, TM_UNION, TM_ARR, TM_FUNC, }; struct type { struct type_qual type_qual; node_t pos_node; /* set up and used only for checking type correctness */ struct type *arr_type; /* NULL or array type before its adjustment */ /* Raw type size (w/o alignment type itself requirement but with element alignment requirements), undefined if mir_size_max. */ mir_size_t raw_size; int align; /* type align, undefined if < 0 */ enum type_mode mode; char unnamed_anon_struct_union_member_type_p; union { enum basic_type basic_type; node_t tag_type; /* struct/union/enum */ struct type *ptr_type; struct arr_type *arr_type; struct func_type *func_type; } u; }; static const struct type ENUM_INT_TYPE = {.raw_size = MIR_SIZE_MAX, .align = -1, .mode = TM_BASIC, .u = {.basic_type = ENUM_BASIC_INT_TYPE}}; /*!*/ static struct type VOID_TYPE = {.raw_size = MIR_SIZE_MAX, .align = -1, .mode = TM_BASIC, .u = {.basic_type = TP_VOID}}; static void set_type_layout (c2m_ctx_t c2m_ctx, struct type *type); static mir_size_t raw_type_size (c2m_ctx_t c2m_ctx, struct type *type) { if (type->raw_size == MIR_SIZE_MAX) set_type_layout (c2m_ctx, type); assert (type->raw_size != MIR_SIZE_MAX); return type->raw_size; } #if defined(__x86_64__) #include "x86_64/cx86_64-code.c" #elif defined(__aarch64__) #include "aarch64/caarch64-code.c" #elif defined(__PPC64__) #include "ppc64/cppc64-code.c" #else #error "undefined or unsupported generation target for C" #endif static void *reg_malloc (c2m_ctx_t c2m_ctx, size_t s) { void *mem = malloc (s); if (mem == NULL) alloc_error (c2m_ctx, "no memory"); VARR_PUSH (void_ptr_t, reg_memory, mem); return mem; } static void reg_memory_pop (c2m_ctx_t c2m_ctx, size_t mark) { while (VARR_LENGTH (void_ptr_t, reg_memory) > mark) free (VARR_POP (void_ptr_t, reg_memory)); } static size_t MIR_UNUSED reg_memory_mark (c2m_ctx_t c2m_ctx) { return VARR_LENGTH (void_ptr_t, reg_memory); } static void reg_memory_finish (c2m_ctx_t c2m_ctx) { reg_memory_pop (c2m_ctx, 0); VARR_DESTROY (void_ptr_t, reg_memory); } static void reg_memory_init (c2m_ctx_t c2m_ctx) { VARR_CREATE (void_ptr_t, reg_memory, 4096); } static int char_is_signed_p (void) { return MIR_CHAR_MAX == MIR_SCHAR_MAX; } enum str_flag { FLAG_EXT = 1, FLAG_C89, FLAG_EXT89 }; static int str_eq (str_t str1, str_t str2, void *arg) { return str1.len == str2.len && memcmp (str1.s, str2.s, str1.len) == 0; } static htab_hash_t str_hash (str_t str, void *arg) { return mir_hash (str.s, str.len, 0x42); } static int str_key_eq (str_t str1, str_t str2, void *arg) { return str1.key == str2.key; } static htab_hash_t str_key_hash (str_t str, void *arg) { return mir_hash64 (str.key, 0x24); } static str_t uniq_cstr (c2m_ctx_t c2m_ctx, const char *str); static void str_init (c2m_ctx_t c2m_ctx) { HTAB_CREATE (str_t, str_tab, 1000, str_hash, str_eq, NULL); HTAB_CREATE (str_t, str_key_tab, 200, str_key_hash, str_key_eq, NULL); empty_str = uniq_cstr (c2m_ctx, ""); } static int str_exists_p (c2m_ctx_t c2m_ctx, const char *s, size_t len, str_t *tab_str) { str_t el, str; str.s = s; str.len = len; if (!HTAB_DO (str_t, str_tab, str, HTAB_FIND, el)) return FALSE; *tab_str = el; return TRUE; } static str_t str_add (c2m_ctx_t c2m_ctx, const char *s, size_t len, size_t key, size_t flags, int key_p) { char *heap_s; str_t el, str; if (str_exists_p (c2m_ctx, s, len, &el)) return el; heap_s = reg_malloc (c2m_ctx, len); memcpy (heap_s, s, len); str.s = heap_s; str.len = len; str.key = key; str.flags = flags; HTAB_DO (str_t, str_tab, str, HTAB_INSERT, el); if (key_p) HTAB_DO (str_t, str_key_tab, str, HTAB_INSERT, el); return str; } static const char *str_find_by_key (c2m_ctx_t c2m_ctx, size_t key) { str_t el, str; str.key = key; if (!HTAB_DO (str_t, str_key_tab, str, HTAB_FIND, el)) return NULL; return el.s; } static void str_finish (c2m_ctx_t c2m_ctx) { HTAB_DESTROY (str_t, str_tab); HTAB_DESTROY (str_t, str_key_tab); } static void *c2mir_calloc (MIR_context_t ctx, size_t size) { void *res = calloc (1, size); if (res == NULL) (*MIR_get_error_func (ctx)) (MIR_alloc_error, "no memory"); return res; } void c2mir_init (MIR_context_t ctx) { c2m_ctx_t c2m_ctx, *c2m_ctx_ptr = c2m_ctx_loc (ctx); *c2m_ctx_ptr = c2m_ctx = c2mir_calloc (ctx, sizeof (struct c2m_ctx)); reg_memory_init (c2m_ctx); str_init (c2m_ctx); } void c2mir_finish (MIR_context_t ctx) { c2m_ctx_t c2m_ctx = *c2m_ctx_loc (ctx); str_finish (c2m_ctx); reg_memory_finish (c2m_ctx); free (c2m_ctx); } /* New Page */ /* ------------------------- Parser Start ------------------------------ */ /* Parser is manually written parser with back-tracing to keep original grammar close to C11 standard grammar as possible. It has a rudimentary syntax error recovery based on stop symbols ';' and '}'. The input is parse tokens and the output is the following AST nodes (the AST root is transl_unit): expr : N_I | N_L | N_LL | N_U | N_UL | N_ULL | N_F | N_D | N_LD | N_CH | N_STR | N_ID | N_ADD (expr) | N_SUB (expr) | N_ADD (expr, expr) | N_SUB (expr, expr) | N_MUL (expr, expr) | N_DIV (expr, expr) | N_MOD (expr, expr) | N_LSH (expr, expr) | N_RSH (expr, expr) | N_NOT (expr) | N_BITWISE_NOT (expr) | N_INC (expr) | N_DEC (expr) | N_POST_INC (expr)| N_POST_DEC (expr) | N_ALIGNOF (type_name?) | N_SIZEOF (type_name) | N_EXPR_SIZEOF (expr) | N_CAST (type_name, expr) | N_COMMA (expr, expr) | N_ANDAND (expr, expr) | N_OROR (expr, expr) | N_EQ (expr, expr) | N_NE (expr, expr) | N_LT (expr, expr) | N_LE (expr, expr) | N_GT (expr, expr) | N_GE (expr, expr) | N_AND (expr, expr) | N_OR (expr, expr) | N_XOR (expr, expr) | N_ASSIGN (expr, expr) | N_ADD_ASSIGN (expr, expr) | N_SUB_ASSIGN (expr, expr) | N_MUL_ASSIGN (expr, expr) | N_DIV_ASSIGN (expr, expr) | N_MOD_ASSIGN (expr, expr) | N_LSH_ASSIGN (expr, expr) | N_RSH_ASSIGN (expr, expr) | N_AND_ASSIGN (expr, expr) | N_OR_ASSIGN (expr, expr) | N_XOR_ASSIGN (expr, expr) | N_DEREF (expr) | | N_ADDR (expr) | N_IND (expr, expr) | N_FIELD (expr, N_ID) | N_DEREF_FIELD (expr, N_ID) | N_COND (expr, expr, expr) | N_COMPOUND_LITERAL (type_name, initializer) | N_CALL (expr, N_LIST:(expr)*) | N_GENERIC (expr, N_LIST:(N_GENERIC_ASSOC (type_name?, expr))+ ) label: N_CASE(expr) | N_CASE(expr,expr) | N_DEFAULT | N_LABEL(N_ID) stmt: compound_stmt | N_IF(N_LIST:(label)*, expr, stmt, stmt?) | N_SWITCH(N_LIST:(label)*, expr, stmt) | (N_WHILE|N_DO) (N_LIST:(label)*, expr, stmt) | N_FOR(N_LIST:(label)*,(N_LIST: declaration+ | expr)?, expr?, expr?, stmt) | N_GOTO(N_LIST:(label)*, N_ID) | (N_CONTINUE|N_BREAK) (N_LIST:(label)*) | N_RETURN(N_LIST:(label)*, expr?) | N_EXPR(N_LIST:(label)*, expr) compound_stmt: N_BLOCK(N_LIST:(label)*, N_LIST:(declaration | stmt)*) declaration: N_SPEC_DECL(N_SHARE(declaration_specs), declarator?, initializer?) | st_assert st_assert: N_ST_ASSERT(const_expr, N_STR) declaration_specs: N_LIST:(align_spec|sc_spec|type_qual|func_spec|type_spec)* align_spec: N_ALIGNAS(type_name|const_expr) sc_spec: N_TYPEDEF|N_EXTERN|N_STATIC|N_AUTO|N_REGISTER|N_THREAD_LOCAL type_qual: N_CONST|N_RESTRICT|N_VOLATILE|N_ATOMIC func_spec: N_INLINE|N_NO_RETURN type_spec: N_VOID|N_CHAR|N_SHORT|N_INT|N_LONG|N_FLOAT|N_DOUBLE|N_SIGNED|N_UNSIGNED|N_BOOL | (N_STRUCT|N_UNION) (N_ID?, struct_declaration_list?) | N_ENUM(N_ID?, N_LIST?: N_ENUM_COST(N_ID, const_expr?)*) | typedef_name struct_declaration_list: N_LIST: struct_declaration* struct_declaration: st_assert | N_MEMBER(N_SHARE(spec_qual_list), declarator?, const_expr?) spec_qual_list: N_LIST:(type_qual|type_spec)* declarator: the same as direct declarator direct_declarator: N_DECL(N_ID, N_LIST:(N_POINTER(type_qual_list) | N_FUNC(id_list|parameter_list) | N_ARR(N_STATIC?, type_qual_list, (assign_expr|N_STAR)?))*) pointer: N_LIST: N_POINTER(type_qual_list)* type_qual_list : N_LIST: type_qual* parameter_type_list: N_LIST:(N_SPEC_DECL(declaration_specs, declarator, ignore) | N_TYPE(declaration_specs, abstract_declarator))+ [N_DOTS] id_list: N_LIST: N_ID* initializer: assign_expr | initialize_list initialize_list: N_LIST: N_INIT(N_LIST:(const_expr | N_FIELD_ID (N_ID))* initializer)+ type_name: N_TYPE(spec_qual_list, abstract_declarator) abstract_declarator: the same as abstract direct declarator abstract_direct_declarator: N_DECL(ignore, N_LIST:(N_POINTER(type_qual_list) | N_FUNC(parameter_list) | N_ARR(N_STATIC?, type_qual_list, (assign_expr|N_STAR)?))*) typedef_name: N_ID transl_unit: N_MODULE(N_LIST:(declaration | N_FUNC_DEF(declaration_specs, declarator, N_LIST: declaration*, compound_stmt))*) Here ? means it can be N_IGNORE, * means 0 or more elements in the list, + means 1 or more. */ #define REP_SEP , #define T_EL(t) T_##t typedef enum { T_NUMBER = 256, REP8 (T_EL, CH, STR, ID, ASSIGN, DIVOP, ADDOP, SH, CMP), REP8 (T_EL, EQNE, ANDAND, OROR, INCDEC, ARROW, UNOP, DOTS, BOOL), REP8 (T_EL, COMPLEX, ALIGNOF, ALIGNAS, ATOMIC, GENERIC, NO_RETURN, STATIC_ASSERT, THREAD_LOCAL), REP8 (T_EL, THREAD, AUTO, BREAK, CASE, CHAR, CONST, CONTINUE, DEFAULT), REP8 (T_EL, DO, DOUBLE, ELSE, ENUM, EXTERN, FLOAT, FOR, GOTO), REP8 (T_EL, IF, INLINE, INT, LONG, REGISTER, RESTRICT, RETURN, SHORT), REP8 (T_EL, SIGNED, SIZEOF, STATIC, STRUCT, SWITCH, TYPEDEF, TYPEOF, UNION), REP5 (T_EL, UNSIGNED, VOID, VOLATILE, WHILE, EOFILE), /* tokens existing in preprocessor only: */ T_HEADER, /* include header */ T_NO_MACRO_IDENT, /* ??? */ T_DBLNO, /* ## */ T_PLM, T_RDBLNO, /* placemarker, ## in replacement list */ T_BOA, /* begin of argument */ T_EOA, T_EOR, /* end of argument and macro replacement */ T_EOP, /* end of processing */ T_EOU, /* end of translation unit */ } token_code_t; static token_code_t FIRST_KW = T_BOOL, LAST_KW = T_WHILE; #define NODE_EL(n) N_##n typedef enum { REP8 (NODE_EL, IGNORE, I, L, LL, U, UL, ULL, F), REP8 (NODE_EL, D, LD, CH, STR, ID, COMMA, ANDAND, OROR), REP8 (NODE_EL, EQ, NE, LT, LE, GT, GE, ASSIGN, BITWISE_NOT), REP8 (NODE_EL, NOT, AND, AND_ASSIGN, OR, OR_ASSIGN, XOR, XOR_ASSIGN, LSH), REP8 (NODE_EL, LSH_ASSIGN, RSH, RSH_ASSIGN, ADD, ADD_ASSIGN, SUB, SUB_ASSIGN, MUL), REP8 (NODE_EL, MUL_ASSIGN, DIV, DIV_ASSIGN, MOD, MOD_ASSIGN, IND, FIELD, ADDR), REP8 (NODE_EL, DEREF, DEREF_FIELD, COND, INC, DEC, POST_INC, POST_DEC, ALIGNOF), REP8 (NODE_EL, SIZEOF, EXPR_SIZEOF, CAST, COMPOUND_LITERAL, CALL, GENERIC, GENERIC_ASSOC, IF), REP8 (NODE_EL, SWITCH, WHILE, DO, FOR, GOTO, CONTINUE, BREAK, RETURN), REP8 (NODE_EL, EXPR, BLOCK, CASE, DEFAULT, LABEL, LIST, SPEC_DECL, SHARE), REP8 (NODE_EL, TYPEDEF, EXTERN, STATIC, AUTO, REGISTER, THREAD_LOCAL, DECL, VOID), REP8 (NODE_EL, CHAR, SHORT, INT, LONG, FLOAT, DOUBLE, SIGNED, UNSIGNED), REP8 (NODE_EL, BOOL, STRUCT, UNION, ENUM, ENUM_CONST, MEMBER, CONST, RESTRICT), REP8 (NODE_EL, VOLATILE, ATOMIC, INLINE, NO_RETURN, ALIGNAS, FUNC, STAR, POINTER), REP8 (NODE_EL, DOTS, ARR, INIT, FIELD_ID, TYPE, ST_ASSERT, FUNC_DEF, MODULE), } node_code_t; #undef REP_SEP DEF_DLIST_LINK (node_t); DEF_DLIST_TYPE (node_t); struct node { node_code_t code; unsigned long uid; pos_t pos; DLIST_LINK (node_t) op_link; DLIST (node_t) ops; union { str_t s; mir_char ch; mir_long l; mir_llong ll; mir_ulong ul; mir_ullong ull; mir_float f; mir_double d; mir_ldouble ld; node_t scope; } u; void *attr; }; DEF_DLIST_CODE (node_t, op_link); struct token { int code : 16; /* token_code_t and EOF */ int processed_p : 16; pos_t pos; node_code_t node_code; node_t node; const char *repr; }; static node_t add_pos (node_t n, pos_t p) { if (n->pos.lno < 0) n->pos = p; return n; } static node_t op_append (node_t n, node_t op) { NL_APPEND (n->ops, op); return add_pos (n, op->pos); } static node_t op_prepend (node_t n, node_t op) { NL_PREPEND (n->ops, op); return add_pos (n, op->pos); } static void op_flat_append (node_t n, node_t op) { if (op->code != N_LIST) { op_append (n, op); return; } for (node_t next_el, el = NL_HEAD (op->ops); el != NULL; el = next_el) { next_el = NL_NEXT (el); NL_REMOVE (op->ops, el); op_append (n, el); } } static node_t new_node (c2m_ctx_t c2m_ctx, node_code_t nc) { node_t n = reg_malloc (c2m_ctx, sizeof (struct node)); n->code = nc; n->uid = curr_uid++; DLIST_INIT (node_t, n->ops); n->attr = NULL; n->pos = no_pos; return n; } static node_t copy_node_with_pos (c2m_ctx_t c2m_ctx, node_t n, pos_t pos) { node_t r = new_node (c2m_ctx, n->code); r->pos = pos; r->u = n->u; return r; } static node_t copy_node (c2m_ctx_t c2m_ctx, node_t n) { return copy_node_with_pos (c2m_ctx, n, n->pos); } static node_t new_pos_node (c2m_ctx_t c2m_ctx, node_code_t nc, pos_t p) { return add_pos (new_node (c2m_ctx, nc), p); } static node_t new_node1 (c2m_ctx_t c2m_ctx, node_code_t nc, node_t op1) { return op_append (new_node (c2m_ctx, nc), op1); } static node_t new_pos_node1 (c2m_ctx_t c2m_ctx, node_code_t nc, pos_t p, node_t op1) { return add_pos (new_node1 (c2m_ctx, nc, op1), p); } static node_t new_node2 (c2m_ctx_t c2m_ctx, node_code_t nc, node_t op1, node_t op2) { return op_append (new_node1 (c2m_ctx, nc, op1), op2); } static node_t new_pos_node2 (c2m_ctx_t c2m_ctx, node_code_t nc, pos_t p, node_t op1, node_t op2) { return add_pos (new_node2 (c2m_ctx, nc, op1, op2), p); } static node_t new_node3 (c2m_ctx_t c2m_ctx, node_code_t nc, node_t op1, node_t op2, node_t op3) { return op_append (new_node2 (c2m_ctx, nc, op1, op2), op3); } static node_t new_pos_node3 (c2m_ctx_t c2m_ctx, node_code_t nc, pos_t p, node_t op1, node_t op2, node_t op3) { return add_pos (new_node3 (c2m_ctx, nc, op1, op2, op3), p); } static node_t new_node4 (c2m_ctx_t c2m_ctx, node_code_t nc, node_t op1, node_t op2, node_t op3, node_t op4) { return op_append (new_node3 (c2m_ctx, nc, op1, op2, op3), op4); } static node_t new_pos_node4 (c2m_ctx_t c2m_ctx, node_code_t nc, pos_t p, node_t op1, node_t op2, node_t op3, node_t op4) { return add_pos (new_node4 (c2m_ctx, nc, op1, op2, op3, op4), p); } static node_t new_ch_node (c2m_ctx_t c2m_ctx, int ch, pos_t p) { node_t n = new_pos_node (c2m_ctx, N_CH, p); n->u.ch = ch; return n; } static node_t new_i_node (c2m_ctx_t c2m_ctx, long l, pos_t p) { node_t n = new_pos_node (c2m_ctx, N_I, p); n->u.l = l; return n; } static node_t new_l_node (c2m_ctx_t c2m_ctx, long l, pos_t p) { node_t n = new_pos_node (c2m_ctx, N_L, p); n->u.l = l; return n; } static node_t new_ll_node (c2m_ctx_t c2m_ctx, long long ll, pos_t p) { node_t n = new_pos_node (c2m_ctx, N_LL, p); n->u.ll = ll; return n; } static node_t new_u_node (c2m_ctx_t c2m_ctx, unsigned long ul, pos_t p) { node_t n = new_pos_node (c2m_ctx, N_U, p); n->u.ul = ul; return n; } static node_t new_ul_node (c2m_ctx_t c2m_ctx, unsigned long ul, pos_t p) { node_t n = new_pos_node (c2m_ctx, N_UL, p); n->u.ul = ul; return n; } static node_t new_ull_node (c2m_ctx_t c2m_ctx, unsigned long long ull, pos_t p) { node_t n = new_pos_node (c2m_ctx, N_ULL, p); n->u.ull = ull; return n; } static node_t new_f_node (c2m_ctx_t c2m_ctx, float f, pos_t p) { node_t n = new_pos_node (c2m_ctx, N_F, p); n->u.f = f; return n; } static node_t new_d_node (c2m_ctx_t c2m_ctx, double d, pos_t p) { node_t n = new_pos_node (c2m_ctx, N_D, p); n->u.d = d; return n; } static node_t new_ld_node (c2m_ctx_t c2m_ctx, long double ld, pos_t p) { node_t n = new_pos_node (c2m_ctx, N_LD, p); n->u.ld = ld; return n; } static node_t new_str_node (c2m_ctx_t c2m_ctx, node_code_t nc, str_t s, pos_t p) { node_t n = new_pos_node (c2m_ctx, nc, p); n->u.s = s; return n; } static node_t get_op (node_t n, int nop) { n = NL_HEAD (n->ops); for (; nop > 0; nop--) n = NL_NEXT (n); return n; } static str_t uniq_cstr (c2m_ctx_t c2m_ctx, const char *str) { return str_add (c2m_ctx, str, strlen (str) + 1, T_STR, 0, FALSE); } static str_t uniq_str (c2m_ctx_t c2m_ctx, const char *str, size_t len) { return str_add (c2m_ctx, str, len, T_STR, 0, FALSE); } static token_t new_token (c2m_ctx_t c2m_ctx, pos_t pos, const char *repr, int token_code, node_code_t node_code) { token_t token = reg_malloc (c2m_ctx, sizeof (struct token)); token->code = token_code; token->processed_p = FALSE; token->pos = pos; token->repr = repr; token->node_code = node_code; token->node = NULL; return token; } static token_t copy_token (c2m_ctx_t c2m_ctx, token_t t, pos_t pos) { token_t token = new_token (c2m_ctx, pos, t->repr, t->code, t->node_code); if (t->node != NULL) token->node = copy_node_with_pos (c2m_ctx, t->node, pos); return token; } static token_t new_token_wo_uniq_repr (c2m_ctx_t c2m_ctx, pos_t pos, const char *repr, int token_code, node_code_t node_code) { return new_token (c2m_ctx, pos, uniq_cstr (c2m_ctx, repr).s, token_code, node_code); } static token_t new_node_token (c2m_ctx_t c2m_ctx, pos_t pos, const char *repr, int token_code, node_t node) { token_t token = new_token_wo_uniq_repr (c2m_ctx, pos, repr, token_code, N_IGNORE); token->node = node; return token; } static void print_pos (FILE *f, pos_t pos, int col_p) { if (pos.lno < 0) return; fprintf (f, "%s:%d", pos.fname, pos.lno); if (col_p) fprintf (f, ":%d: ", pos.ln_pos); } static const char *get_token_name (c2m_ctx_t c2m_ctx, int token_code) { static char buf[30]; const char *s; switch (token_code) { case T_NUMBER: return "number"; case T_CH: return "char constant"; case T_STR: return "string"; case T_ID: return "identifier"; case T_ASSIGN: return "assign op"; case T_DIVOP: return "/ or %"; case T_ADDOP: return "+ or -"; case T_SH: return "shift op"; case T_CMP: return "comparison op"; case T_EQNE: return "equality op"; case T_ANDAND: return "&&"; case T_OROR: return "||"; case T_INCDEC: return "++ or --"; case T_ARROW: return "->"; case T_UNOP: return "unary op"; case T_DOTS: return "..."; default: if ((s = str_find_by_key (c2m_ctx, token_code)) != NULL) return s; if (isprint (token_code)) sprintf (buf, "%c", token_code); else sprintf (buf, "%d", token_code); return buf; } } static void error (c2m_ctx_t c2m_ctx, pos_t pos, const char *format, ...) { va_list args; FILE *f; if ((f = options->message_file) == NULL) return; n_errors++; va_start (args, format); print_pos (f, pos, TRUE); vfprintf (f, format, args); va_end (args); fprintf (f, "\n"); } static void warning (c2m_ctx_t c2m_ctx, pos_t pos, const char *format, ...) { va_list args; FILE *f; if ((f = options->message_file) == NULL) return; n_warnings++; va_start (args, format); print_pos (f, pos, TRUE); fprintf (f, "warning -- "); vfprintf (f, format, args); va_end (args); fprintf (f, "\n"); } #define TAB_STOP 8 static void init_streams (c2m_ctx_t c2m_ctx) { cs = eof_s = NULL; VARR_CREATE (stream_t, streams, 32); } static void free_stream (stream_t s) { VARR_DESTROY (char, s->ln); free (s); } static void finish_streams (c2m_ctx_t c2m_ctx) { if (eof_s != NULL) free_stream (eof_s); if (streams == NULL) return; while (VARR_LENGTH (stream_t, streams) != 0) free_stream (VARR_POP (stream_t, streams)); VARR_DESTROY (stream_t, streams); } static stream_t new_stream (FILE *f, const char *fname, int (*getc_func) (c2m_ctx_t)) { stream_t s = malloc (sizeof (struct stream)); VARR_CREATE (char, s->ln, 128); s->f = f; s->fname = s->pos.fname = fname; s->pos.lno = 0; s->pos.ln_pos = 0; s->ifs_length_at_stream_start = 0; s->start = s->curr = NULL; s->getc_func = getc_func; return s; } static void add_stream (c2m_ctx_t c2m_ctx, FILE *f, const char *fname, int (*getc_func) (c2m_ctx_t)) { assert (fname != NULL); if (cs != NULL && cs->f != NULL && cs->f != stdin) { fgetpos (cs->f, &cs->fpos); fclose (cs->f); cs->f = NULL; } cs = new_stream (f, fname, getc_func); VARR_PUSH (stream_t, streams, cs); } static int str_getc (c2m_ctx_t c2m_ctx) { if (*cs->curr == '\0') return EOF; return *cs->curr++; } static void add_string_stream (c2m_ctx_t c2m_ctx, const char *pos_fname, const char *str) { add_stream (c2m_ctx, NULL, pos_fname, str_getc); cs->start = cs->curr = str; } static int string_stream_p (stream_t s) { return s->getc_func != NULL; } static void change_stream_pos (c2m_ctx_t c2m_ctx, pos_t pos) { cs->pos = pos; } static void remove_trigraphs (c2m_ctx_t c2m_ctx) { int len = VARR_LENGTH (char, cs->ln); char *addr = VARR_ADDR (char, cs->ln); int i, start, to, ch; for (i = to = 0; i < len; i++, to++) { addr[to] = addr[i]; for (start = i; i < len && addr[i] == '?'; i++, to++) addr[to] = addr[i]; if (i >= len) break; if (i < start + 2) { addr[to] = addr[i]; continue; } switch (addr[i]) { case '=': ch = '#'; break; case '(': ch = '['; break; case '/': ch = '\\'; break; case ')': ch = ']'; break; case '\'': ch = '^'; break; case '<': ch = '{'; break; case '!': ch = '|'; break; case '>': ch = '}'; break; case '-': ch = '~'; break; default: addr[to] = addr[i]; continue; } to -= 2; addr[to] = ch; } VARR_TRUNC (char, cs->ln, to); } static int ln_get (c2m_ctx_t c2m_ctx) { if (cs->f == NULL) return cs->getc_func (c2m_ctx); /* top level */ return fgetc (cs->f); } static char *reverse (VARR (char) * v) { char *addr = VARR_ADDR (char, v); int i, j, temp, last = (int) VARR_LENGTH (char, v) - 1; if (last >= 0 && addr[last] == '\0') last--; for (i = last, j = 0; i > j; i--, j++) SWAP (addr[i], addr[j], temp); return addr; } static int get_line (c2m_ctx_t c2m_ctx) { /* translation phase 1 and 2 */ int c, eof_p = 0; VARR_TRUNC (char, cs->ln, 0); for (c = ln_get (c2m_ctx); c != EOF && c != '\n'; c = ln_get (c2m_ctx)) VARR_PUSH (char, cs->ln, c); eof_p = c == EOF; if (eof_p) { if (VARR_LENGTH (char, cs->ln) == 0) return FALSE; if (c != '\n') (options->pedantic_p ? error : warning) (c2m_ctx, cs->pos, "no end of line at file end"); } remove_trigraphs (c2m_ctx); VARR_PUSH (char, cs->ln, '\n'); reverse (cs->ln); return TRUE; } static int cs_get (c2m_ctx_t c2m_ctx) { size_t len = VARR_LENGTH (char, cs->ln); for (;;) { if (len == 2 && VARR_GET (char, cs->ln, 1) == '\\') { assert (VARR_GET (char, cs->ln, 0) == '\n'); } else if (len > 0) { cs->pos.ln_pos++; return VARR_POP (char, cs->ln); } if (cs->fname == NULL || !get_line (c2m_ctx)) return EOF; len = VARR_LENGTH (char, cs->ln); assert (len > 0); cs->pos.ln_pos = 0; cs->pos.lno++; } } static void cs_unget (c2m_ctx_t c2m_ctx, int c) { cs->pos.ln_pos--; VARR_PUSH (char, cs->ln, c); } static void set_string_stream (c2m_ctx_t c2m_ctx, const char *str, pos_t pos, void (*transform) (const char *, VARR (char) *)) { /* read from string str */ cs = new_stream (NULL, NULL, NULL); VARR_PUSH (stream_t, streams, cs); cs->pos = pos; if (transform != NULL) { transform (str, cs->ln); } else { for (; *str != '\0'; str++) VARR_PUSH (char, cs->ln, *str); } } static void remove_string_stream (c2m_ctx_t c2m_ctx) { assert (cs->f == NULL && cs->f == NULL); free_stream (VARR_POP (stream_t, streams)); cs = VARR_LAST (stream_t, streams); } static void set_string_val (c2m_ctx_t c2m_ctx, token_t t, VARR (char) * temp) { int i, str_len, curr_c; const char *str; assert (t->code == T_STR || t->code == T_CH); str = t->repr; VARR_TRUNC (char, temp, 0); str_len = strlen (str); assert (str_len >= 2 && (str[0] == '"' || str[0] == '\'') && str[0] == str[str_len - 1]); for (i = 1; i < str_len - 1; i++) { curr_c = str[i]; if (curr_c != '\\') { VARR_PUSH (char, temp, curr_c); continue; } curr_c = str[++i]; switch (curr_c) { case 'a': curr_c = '\a'; break; case 'b': curr_c = '\b'; break; case 'n': curr_c = '\n'; break; case 'f': curr_c = '\f'; break; case 'r': curr_c = '\r'; break; case 't': curr_c = '\t'; break; case 'v': curr_c = '\v'; break; case '\\': case '\'': case '\?': case '\"': break; case 'e': (options->pedantic_p ? error : warning) (c2m_ctx, t->pos, "non-standard escape sequence \\e"); curr_c = '\033'; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': { unsigned long v = curr_c - '0'; curr_c = str[++i]; if (!isdigit (curr_c) || curr_c == '8' || curr_c == '9') { i--; } else { v = v * 8 + curr_c - '0'; curr_c = str[++i]; if (!isdigit (curr_c) || curr_c == '8' || curr_c == '9') i--; else v = v * 8 + curr_c - '0'; } curr_c = v; break; } case 'x': case 'X': { int first_p = TRUE; unsigned long v = 0; for (i++;; i++) { curr_c = str[i]; if (!isxdigit (curr_c)) break; first_p = FALSE; v *= 16; v += (isdigit (curr_c) ? curr_c - '0' : islower (curr_c) ? curr_c - 'a' + 10 : curr_c - 'A' + 10); } if (first_p) error (c2m_ctx, t->pos, "wrong hexadecimal char %c", curr_c); else if (v > MIR_UCHAR_MAX) (options->pedantic_p ? error : warning) (c2m_ctx, t->pos, "too big hexadecimal char 0x%x", v); curr_c = v; i--; break; } default: error (c2m_ctx, t->pos, "wrong escape char 0x%x", curr_c); curr_c = -1; } if (t->repr[0] == '\'' || curr_c >= 0) VARR_PUSH (char, temp, curr_c); } VARR_PUSH (char, temp, '\0'); if (t->repr[0] == '"') t->node->u.s = uniq_str (c2m_ctx, VARR_ADDR (char, temp), VARR_LENGTH (char, temp)); else if (VARR_LENGTH (char, temp) == 1) error (c2m_ctx, t->pos, "empty char constant"); else t->node->u.ch = VARR_GET (char, temp, 0); } static token_t new_id_token (c2m_ctx_t c2m_ctx, pos_t pos, const char *id_str) { token_t token; str_t str = uniq_cstr (c2m_ctx, id_str); token = new_token (c2m_ctx, pos, str.s, T_ID, N_IGNORE); token->node = new_str_node (c2m_ctx, N_ID, str, pos); return token; } static token_t get_next_pptoken_1 (c2m_ctx_t c2m_ctx, int header_p) { int start_c, curr_c, nl_p, comment_char; pos_t pos; if (cs->fname != NULL && VARR_LENGTH (token_t, buffered_tokens) != 0) return VARR_POP (token_t, buffered_tokens); VARR_TRUNC (char, symbol_text, 0); for (;;) { curr_c = cs_get (c2m_ctx); /* Process sequence of white spaces/comments: */ for (comment_char = -1, nl_p = FALSE;; curr_c = cs_get (c2m_ctx)) { switch (curr_c) { case '\t': cs->pos.ln_pos = round_size (cs->pos.ln_pos, TAB_STOP); /* fall through */ case ' ': case '\f': case '\r': case '\v': break; case '\n': if (comment_char < 0) { nl_p = TRUE; pos = cs->pos; } else if (comment_char == '/') { comment_char = -1; nl_p = TRUE; pos = cs->pos; } cs->pos.ln_pos = 0; break; case '/': if (comment_char >= 0) break; curr_c = cs_get (c2m_ctx); if (curr_c == '/' || curr_c == '*') { VARR_PUSH (char, symbol_text, '/'); comment_char = curr_c; break; } cs_unget (c2m_ctx, curr_c); curr_c = '/'; goto end_ws; case '*': if (comment_char < 0) goto end_ws; if (comment_char != '*') break; curr_c = cs_get (c2m_ctx); if (curr_c == '/') { comment_char = -1; VARR_PUSH (char, symbol_text, '*'); } else { cs_unget (c2m_ctx, curr_c); curr_c = '*'; } break; default: if (comment_char < 0) goto end_ws; if (curr_c == EOF) { error_func (c2m_ctx, C_unfinished_comment, "unfinished comment"); goto end_ws; } break; } VARR_PUSH (char, symbol_text, curr_c); } end_ws: if (VARR_LENGTH (char, symbol_text) != 0) { cs_unget (c2m_ctx, curr_c); VARR_PUSH (char, symbol_text, '\0'); return new_token_wo_uniq_repr (c2m_ctx, nl_p ? pos : cs->pos, VARR_ADDR (char, symbol_text), nl_p ? '\n' : ' ', N_IGNORE); } if (header_p && (curr_c == '<' || curr_c == '\"')) { int i, stop; pos = cs->pos; VARR_TRUNC (char, temp_string, 0); for (stop = curr_c == '<' ? '>' : '\"';;) { VARR_PUSH (char, symbol_text, curr_c); curr_c = cs_get (c2m_ctx); VARR_PUSH (char, temp_string, curr_c); if (curr_c == stop || curr_c == '\n' || curr_c == EOF) break; } if (curr_c == stop) { VARR_PUSH (char, symbol_text, curr_c); VARR_PUSH (char, symbol_text, '\0'); VARR_POP (char, temp_string); VARR_PUSH (char, temp_string, '\0'); return new_node_token (c2m_ctx, pos, VARR_ADDR (char, symbol_text), T_HEADER, new_str_node (c2m_ctx, N_STR, uniq_cstr (c2m_ctx, VARR_ADDR (char, temp_string)), pos)); } else { VARR_PUSH (char, symbol_text, curr_c); for (i = 0; i < VARR_LENGTH (char, symbol_text); i++) cs_unget (c2m_ctx, VARR_GET (char, symbol_text, i)); curr_c = (stop == '>' ? '<' : '\"'); } } switch (start_c = curr_c) { case '\\': curr_c = cs_get (c2m_ctx); assert (curr_c != '\n'); cs_unget (c2m_ctx, curr_c); return new_token (c2m_ctx, cs->pos, "\\", '\\', N_IGNORE); case '~': return new_token (c2m_ctx, cs->pos, "~", T_UNOP, N_BITWISE_NOT); case '+': case '-': pos = cs->pos; curr_c = cs_get (c2m_ctx); if (curr_c == start_c) { if (start_c == '+') return new_token (c2m_ctx, pos, "++", T_INCDEC, N_INC); else return new_token (c2m_ctx, pos, "--", T_INCDEC, N_DEC); } else if (curr_c == '=') { if (start_c == '+') return new_token (c2m_ctx, pos, "+=", T_ASSIGN, N_ADD_ASSIGN); else return new_token (c2m_ctx, pos, "-=", T_ASSIGN, N_SUB_ASSIGN); } else if (start_c == '-' && curr_c == '>') { return new_token (c2m_ctx, pos, "->", T_ARROW, N_DEREF_FIELD); } else { cs_unget (c2m_ctx, curr_c); if (start_c == '+') return new_token (c2m_ctx, pos, "+", T_ADDOP, N_ADD); else return new_token (c2m_ctx, pos, "-", T_ADDOP, N_SUB); } assert (FALSE); case '=': pos = cs->pos; curr_c = cs_get (c2m_ctx); if (curr_c == '=') { return new_token (c2m_ctx, pos, "==", T_EQNE, N_EQ); } else { cs_unget (c2m_ctx, curr_c); return new_token (c2m_ctx, pos, "=", '=', N_ASSIGN); } assert (FALSE); case '<': case '>': pos = cs->pos; curr_c = cs_get (c2m_ctx); if (curr_c == start_c) { curr_c = cs_get (c2m_ctx); if (curr_c == '=') { if (start_c == '<') return new_token (c2m_ctx, pos, "<<=", T_ASSIGN, N_LSH_ASSIGN); else return new_token (c2m_ctx, pos, ">>=", T_ASSIGN, N_RSH_ASSIGN); } else { cs_unget (c2m_ctx, curr_c); if (start_c == '<') return new_token (c2m_ctx, pos, "<<", T_SH, N_LSH); else return new_token (c2m_ctx, pos, ">>", T_SH, N_RSH); } } else if (curr_c == '=') { if (start_c == '<') return new_token (c2m_ctx, pos, "<=", T_CMP, N_LE); else return new_token (c2m_ctx, pos, ">=", T_CMP, N_GE); } else if (start_c == '<' && curr_c == ':') { return new_token (c2m_ctx, pos, "<:", '[', N_IGNORE); } else if (start_c == '<' && curr_c == '%') { return new_token (c2m_ctx, pos, "<%", '{', N_IGNORE); } else { cs_unget (c2m_ctx, curr_c); if (start_c == '<') return new_token (c2m_ctx, pos, "<", T_CMP, N_LT); else return new_token (c2m_ctx, pos, ">", T_CMP, N_GT); } assert (FALSE); case '*': pos = cs->pos; curr_c = cs_get (c2m_ctx); if (curr_c == '=') { return new_token (c2m_ctx, pos, "*=", T_ASSIGN, N_MUL_ASSIGN); } else { cs_unget (c2m_ctx, curr_c); return new_token (c2m_ctx, pos, "*", '*', N_MUL); } assert (FALSE); case '/': pos = cs->pos; curr_c = cs_get (c2m_ctx); assert (curr_c != '/' && curr_c != '*'); if (curr_c == '=') return new_token (c2m_ctx, pos, "/=", T_ASSIGN, N_DIV_ASSIGN); assert (curr_c != '*' && curr_c != '/'); /* we already processed comments */ cs_unget (c2m_ctx, curr_c); return new_token (c2m_ctx, pos, "/", T_DIVOP, N_DIV); case '%': pos = cs->pos; curr_c = cs_get (c2m_ctx); if (curr_c == '=') { return new_token (c2m_ctx, pos, "%=", T_ASSIGN, N_MOD_ASSIGN); } else if (curr_c == '>') { return new_token (c2m_ctx, pos, "%>", '}', N_IGNORE); } else if (curr_c == ':') { curr_c = cs_get (c2m_ctx); if (curr_c != '%') { cs_unget (c2m_ctx, curr_c); return new_token (c2m_ctx, pos, "%:", '#', N_IGNORE); } else { curr_c = cs_get (c2m_ctx); if (curr_c == ':') return new_token (c2m_ctx, pos, "%:%:", T_DBLNO, N_IGNORE); else { cs_unget (c2m_ctx, '%'); cs_unget (c2m_ctx, curr_c); return new_token (c2m_ctx, pos, "%:", '#', N_IGNORE); } } } else { cs_unget (c2m_ctx, curr_c); return new_token (c2m_ctx, pos, "%", T_DIVOP, N_MOD); } assert (FALSE); case '&': case '|': pos = cs->pos; curr_c = cs_get (c2m_ctx); if (curr_c == '=') { if (start_c == '&') return new_token (c2m_ctx, pos, "&=", T_ASSIGN, N_AND_ASSIGN); else return new_token (c2m_ctx, pos, "|=", T_ASSIGN, N_OR_ASSIGN); } else if (curr_c == start_c) { if (start_c == '&') return new_token (c2m_ctx, pos, "&&", T_ANDAND, N_ANDAND); else return new_token (c2m_ctx, pos, "||", T_OROR, N_OROR); } else { cs_unget (c2m_ctx, curr_c); if (start_c == '&') return new_token (c2m_ctx, pos, "&", start_c, N_AND); else return new_token (c2m_ctx, pos, "|", start_c, N_OR); } assert (FALSE); case '^': case '!': pos = cs->pos; curr_c = cs_get (c2m_ctx); if (curr_c == '=') { if (start_c == '^') return new_token (c2m_ctx, pos, "^=", T_ASSIGN, N_XOR_ASSIGN); else return new_token (c2m_ctx, pos, "!=", T_EQNE, N_NE); } else { cs_unget (c2m_ctx, curr_c); if (start_c == '^') return new_token (c2m_ctx, pos, "^", '^', N_XOR); else return new_token (c2m_ctx, pos, "!", T_UNOP, N_NOT); } assert (FALSE); case ';': return new_token (c2m_ctx, cs->pos, ";", curr_c, N_IGNORE); case '?': return new_token (c2m_ctx, cs->pos, "?", curr_c, N_IGNORE); case '(': return new_token (c2m_ctx, cs->pos, "(", curr_c, N_IGNORE); case ')': return new_token (c2m_ctx, cs->pos, ")", curr_c, N_IGNORE); case '{': return new_token (c2m_ctx, cs->pos, "{", curr_c, N_IGNORE); case '}': return new_token (c2m_ctx, cs->pos, "}", curr_c, N_IGNORE); case ']': return new_token (c2m_ctx, cs->pos, "]", curr_c, N_IGNORE); case EOF: { pos_t pos = cs->pos; assert (eof_s != cs); if (eof_s != NULL) free_stream (eof_s); if (cs->f != stdin && cs->f != NULL) { fclose (cs->f); cs->f = NULL; } eof_s = VARR_POP (stream_t, streams); if (VARR_LENGTH (stream_t, streams) == 0) { return new_token (c2m_ctx, pos, "", T_EOU, N_IGNORE); } cs = VARR_LAST (stream_t, streams); if (cs->f == NULL && cs->fname != NULL && !string_stream_p (cs)) { if ((cs->f = fopen (cs->fname, "r")) == NULL) { if (options->message_file != NULL) fprintf (options->message_file, "cannot reopen file %s -- good bye\n", cs->fname); longjmp (c2m_ctx->env, 1); // ??? } fsetpos (cs->f, &cs->fpos); } return new_token (c2m_ctx, cs->pos, "", T_EOFILE, N_IGNORE); } case ':': curr_c = cs_get (c2m_ctx); if (curr_c == '>') { return new_token (c2m_ctx, cs->pos, ":>", ']', N_IGNORE); } else { cs_unget (c2m_ctx, curr_c); return new_token (c2m_ctx, cs->pos, ":", ':', N_IGNORE); } case '#': curr_c = cs_get (c2m_ctx); if (curr_c == '#') { return new_token (c2m_ctx, cs->pos, "##", T_DBLNO, N_IGNORE); } else { cs_unget (c2m_ctx, curr_c); return new_token (c2m_ctx, cs->pos, "#", '#', N_IGNORE); } case ',': return new_token (c2m_ctx, cs->pos, ",", ',', N_COMMA); case '[': return new_token (c2m_ctx, cs->pos, "[", '[', N_IND); case '.': pos = cs->pos; curr_c = cs_get (c2m_ctx); if (curr_c == '.') { curr_c = cs_get (c2m_ctx); if (curr_c == '.') { return new_token (c2m_ctx, pos, "...", T_DOTS, N_IGNORE); } else { cs_unget (c2m_ctx, '.'); cs_unget (c2m_ctx, curr_c); return new_token (c2m_ctx, pos, ".", '.', N_FIELD); } } else if (!isdigit (curr_c)) { cs_unget (c2m_ctx, curr_c); return new_token (c2m_ctx, pos, ".", '.', N_FIELD); } cs_unget (c2m_ctx, curr_c); curr_c = '.'; /* Fall through: */ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { pos = cs->pos; VARR_TRUNC (char, symbol_text, 0); for (;;) { VARR_PUSH (char, symbol_text, curr_c); curr_c = cs_get (c2m_ctx); if (curr_c == 'e' || curr_c == 'E' || curr_c == 'p' || curr_c == 'P') { int c = cs_get (c2m_ctx); if (c == '+' || c == '-') { VARR_PUSH (char, symbol_text, curr_c); curr_c = c; } else { cs_unget (c2m_ctx, c); } } else if (!isdigit (curr_c) && !isalpha (curr_c) && curr_c != '_' && curr_c != '.') break; } VARR_PUSH (char, symbol_text, '\0'); cs_unget (c2m_ctx, curr_c); return new_token_wo_uniq_repr (c2m_ctx, pos, VARR_ADDR (char, symbol_text), T_NUMBER, N_IGNORE); } case '\'': case '\"': { /* ??? unicode and wchar */ token_t t; int stop = curr_c; pos = cs->pos; VARR_PUSH (char, symbol_text, curr_c); for (curr_c = cs_get (c2m_ctx); curr_c != stop && curr_c != '\n' && curr_c != EOF; curr_c = cs_get (c2m_ctx)) { VARR_PUSH (char, symbol_text, curr_c); if (curr_c != '\\') continue; curr_c = cs_get (c2m_ctx); if (curr_c == '\n' || curr_c == EOF) break; VARR_PUSH (char, symbol_text, curr_c); } VARR_PUSH (char, symbol_text, curr_c); if (curr_c == stop) { if (stop == '\'' && VARR_LENGTH (char, symbol_text) == 1) error (c2m_ctx, pos, "empty character"); } else { error (c2m_ctx, pos, "unterminated %s", stop == '"' ? "string" : "char"); VARR_PUSH (char, symbol_text, stop); } VARR_PUSH (char, symbol_text, '\0'); t = (stop == '\"' ? new_node_token (c2m_ctx, pos, VARR_ADDR (char, symbol_text), T_STR, new_str_node (c2m_ctx, N_STR, empty_str, pos)) : new_node_token (c2m_ctx, pos, VARR_ADDR (char, symbol_text), T_CH, new_ch_node (c2m_ctx, ' ', pos))); set_string_val (c2m_ctx, t, symbol_text); return t; } default: if (isalpha (curr_c) || curr_c == '_') { pos = cs->pos; do { VARR_PUSH (char, symbol_text, curr_c); curr_c = cs_get (c2m_ctx); } while (isalnum (curr_c) || curr_c == '_'); cs_unget (c2m_ctx, curr_c); VARR_PUSH (char, symbol_text, '\0'); return new_id_token (c2m_ctx, pos, VARR_ADDR (char, symbol_text)); } else { VARR_PUSH (char, symbol_text, curr_c); VARR_PUSH (char, symbol_text, '\0'); return new_token_wo_uniq_repr (c2m_ctx, pos, VARR_ADDR (char, symbol_text), curr_c, N_IGNORE); } } } } static token_t get_next_pptoken (c2m_ctx_t c2m_ctx) { return get_next_pptoken_1 (c2m_ctx, FALSE); } static token_t get_next_include_pptoken (c2m_ctx_t c2m_ctx) { return get_next_pptoken_1 (c2m_ctx, TRUE); } #ifdef C2MIR_PREPRO_DEBUG static const char *get_token_str (token_t t) { switch (t->code) { case T_EOFILE: return "EOF"; case T_DBLNO: return "DBLNO"; case T_PLM: return "PLM"; case T_RDBLNO: return "RDBLNO"; case T_BOA: return "BOA"; case T_EOA: return "EOA"; case T_EOR: return "EOR"; case T_EOP: return "EOP"; case T_EOU: return "EOU"; default: return t->repr; } } #endif static void unget_next_pptoken (c2m_ctx_t c2m_ctx, token_t t) { VARR_PUSH (token_t, buffered_tokens, t); } static const char *stringify (const char *str, VARR (char) * to) { VARR_TRUNC (char, to, 0); VARR_PUSH (char, to, '"'); for (; *str != '\0'; str++) { if (*str == '\"' || *str == '\\') VARR_PUSH (char, to, '\\'); VARR_PUSH (char, to, *str); } VARR_PUSH (char, to, '"'); return VARR_ADDR (char, to); } static void destringify (const char *repr, VARR (char) * to) { int i, repr_len = strlen (repr); VARR_TRUNC (char, to, 0); if (repr_len == 0) return; i = repr[0] == '"' ? 1 : 0; if (i == 1 && repr_len == 1) return; if (repr[repr_len - 1] == '"') repr_len--; for (; i < repr_len; i++) if (repr[i] != '\\' || i + 1 >= repr_len || (repr[i + 1] != '\\' && repr[i + 1] != '"')) VARR_PUSH (char, to, repr[i]); } /* TS - vector, T defines position for empty vector */ static token_t token_stringify (c2m_ctx_t c2m_ctx, token_t t, VARR (token_t) * ts) { int i; if (VARR_LENGTH (token_t, ts) != 0) t = VARR_GET (token_t, ts, 0); t = new_node_token (c2m_ctx, t->pos, "", T_STR, new_str_node (c2m_ctx, N_STR, empty_str, t->pos)); VARR_TRUNC (char, temp_string, 0); for (const char *s = t->repr; *s != 0; s++) VARR_PUSH (char, temp_string, *s); VARR_PUSH (char, temp_string, '"'); for (i = 0; i < VARR_LENGTH (token_t, ts); i++) if (VARR_GET (token_t, ts, i)->code == ' ' || VARR_GET (token_t, ts, i)->code == '\n') { VARR_PUSH (char, temp_string, ' '); } else { for (const char *s = VARR_GET (token_t, ts, i)->repr; *s != 0; s++) { int c = VARR_LENGTH (token_t, ts) == i + 1 ? '\0' : VARR_GET (token_t, ts, i + 1)->repr[0]; /* It is an implementation defined behaviour analogous GCC/Clang (see set_string_val): */ if (*s == '\"' || (*s == '\\' && c != '\\' && c != 'a' && c != 'b' && c != 'f' && c != 'n' && c != 'r' && c != 'v' && c != 't' && c != '?' && c != 'e' && !('0' <= c && c <= '7') && c != 'x' && c != 'X')) VARR_PUSH (char, temp_string, '\\'); VARR_PUSH (char, temp_string, *s); } } VARR_PUSH (char, temp_string, '"'); VARR_PUSH (char, temp_string, '\0'); t->repr = uniq_cstr (c2m_ctx, VARR_ADDR (char, temp_string)).s; set_string_val (c2m_ctx, t, temp_string); return t; } static node_t get_int_node_from_repr (c2m_ctx_t c2m_ctx, const char *repr, char **stop, int base, int uns_p, int long_p, int llong_p, pos_t pos) { mir_ullong ull = strtoull (repr, stop, base); if (llong_p) { if (!uns_p && (base == 10 || ull <= MIR_LLONG_MAX)) return new_ll_node (c2m_ctx, ull, pos); return new_ull_node (c2m_ctx, ull, pos); } if (long_p) { if (!uns_p && ull <= MIR_LONG_MAX) return new_l_node (c2m_ctx, ull, pos); if (ull <= MIR_ULONG_MAX) return new_ul_node (c2m_ctx, ull, pos); if (!uns_p && (base == 10 || ull <= MIR_LLONG_MAX)) return new_ll_node (c2m_ctx, ull, pos); return new_ull_node (c2m_ctx, ull, pos); } if (uns_p) { if (ull <= MIR_UINT_MAX) return new_u_node (c2m_ctx, ull, pos); if (ull <= MIR_ULONG_MAX) return new_ul_node (c2m_ctx, ull, pos); return new_ull_node (c2m_ctx, ull, pos); } if (ull <= MIR_INT_MAX) return new_i_node (c2m_ctx, ull, pos); if (base != 10 && ull <= MIR_UINT_MAX) return new_u_node (c2m_ctx, ull, pos); if (ull <= MIR_LONG_MAX) return new_l_node (c2m_ctx, ull, pos); if (ull <= MIR_ULONG_MAX) return new_ul_node (c2m_ctx, ull, pos); if (base == 10 || ull <= MIR_LLONG_MAX) return new_ll_node (c2m_ctx, ull, pos); return new_ull_node (c2m_ctx, ull, pos); } static token_t pptoken2token (c2m_ctx_t c2m_ctx, token_t t, int id2kw_p) { assert (t->code != T_HEADER && t->code != T_BOA && t->code != T_EOA && t->code != T_EOR && t->code != T_EOP && t->code != T_EOFILE && t->code != T_EOU && t->code != T_PLM && t->code != T_RDBLNO); if (t->code == T_NO_MACRO_IDENT) t->code = T_ID; if (t->code == T_ID && id2kw_p) { str_t str = str_add (c2m_ctx, t->repr, strlen (t->repr) + 1, T_STR, 0, FALSE); if (str.key != T_STR) { t->code = str.key; t->node_code = N_IGNORE; t->node = NULL; } return t; } else if (t->code == ' ' || t->code == '\n') { return NULL; } else if (t->code == T_NUMBER) { int i, base = 10, float_p = FALSE, double_p = FALSE, ldouble_p = FALSE; int uns_p = FALSE, long_p = FALSE, llong_p = FALSE; const char *repr = t->repr, *start = t->repr; char *stop; int last = strlen (repr) - 1; assert (last >= 0); if (repr[0] == '0' && (repr[1] == 'x' || repr[1] == 'X')) { base = 16; } else if (repr[0] == '0' && (repr[1] == 'b' || repr[1] == 'B')) { (options->pedantic_p ? error : warning) (c2m_ctx, t->pos, "binary number is not a standard: %s", t->repr); base = 2; start += 2; } else if (repr[0] == '0') { base = 8; } for (i = 0; i <= last; i++) { if (repr[i] == '.') { double_p = TRUE; } else if (repr[i] == 'p' || repr[i] == 'P') { double_p = TRUE; } else if ((repr[i] == 'e' || repr[i] == 'E') && base != 16) { double_p = TRUE; } } if (last >= 2 && (strcmp (&repr[last - 2], "LLU") == 0 || strcmp (&repr[last - 2], "ULL") == 0 || strcmp (&repr[last - 2], "llu") == 0 || strcmp (&repr[last - 2], "ull") == 0 || strcmp (&repr[last - 2], "LLu") == 0 || strcmp (&repr[last - 2], "uLL") == 0 || strcmp (&repr[last - 2], "llU") == 0 || strcmp (&repr[last - 2], "Ull") == 0)) { llong_p = uns_p = TRUE; last -= 3; } else if (last >= 1 && (strcmp (&repr[last - 1], "LL") == 0 || strcmp (&repr[last - 1], "ll") == 0)) { llong_p = TRUE; last -= 2; } else if (last >= 1 && (strcmp (&repr[last - 1], "LU") == 0 || strcmp (&repr[last - 1], "UL") == 0 || strcmp (&repr[last - 1], "lu") == 0 || strcmp (&repr[last - 1], "ul") == 0 || strcmp (&repr[last - 1], "Lu") == 0 || strcmp (&repr[last - 1], "uL") == 0 || strcmp (&repr[last - 1], "lU") == 0 || strcmp (&repr[last - 1], "Ul") == 0)) { long_p = uns_p = TRUE; last -= 2; } else if (strcmp (&repr[last], "L") == 0 || strcmp (&repr[last], "l") == 0) { long_p = TRUE; last--; } else if (strcmp (&repr[last], "U") == 0 || strcmp (&repr[last], "u") == 0) { uns_p = TRUE; last--; } else if (double_p && (strcmp (&repr[last], "F") == 0 || strcmp (&repr[last], "f") == 0)) { float_p = TRUE; double_p = FALSE; last--; } if (double_p) { if (uns_p || llong_p) { error (c2m_ctx, t->pos, "wrong number: %s", repr); } else if (long_p) { ldouble_p = TRUE; double_p = FALSE; } } errno = 0; if (float_p) { t->node = new_f_node (c2m_ctx, strtof (start, &stop), t->pos); } else if (double_p) { t->node = new_d_node (c2m_ctx, strtod (start, &stop), t->pos); } else if (ldouble_p) { t->node = new_ld_node (c2m_ctx, strtold (start, &stop), t->pos); } else { t->node = get_int_node_from_repr (c2m_ctx, start, &stop, base, uns_p, long_p, llong_p, t->pos); } if (stop != &repr[last + 1]) { if (options->message_file != NULL) fprintf (options->message_file, "%s:%s:%s\n", repr, stop, &repr[last + 1]); error (c2m_ctx, t->pos, "wrong number: %s", t->repr); } else if (errno) { if (float_p || double_p || ldouble_p) { warning (c2m_ctx, t->pos, "number %s is out of range -- using IEEE infinity", t->repr); } else { (options->pedantic_p ? error : warning) (c2m_ctx, t->pos, "number %s is out of range", t->repr); } } } return t; } /* --------------------------- Preprocessor -------------------------------- */ typedef struct macro { /* macro definition: */ token_t id; /* T_ID */ VARR (token_t) * params; /* (T_ID)* [N_DOTS], NULL means no params */ VARR (token_t) * replacement; /* token*, NULL means a standard macro */ int ignore_p; } * macro_t; DEF_VARR (macro_t); DEF_HTAB (macro_t); typedef struct ifstate { int skip_p, true_p, else_p; /* ??? flags that we are in a else part and in a false part */ pos_t if_pos; /* pos for #if and last #else, #elif */ } * ifstate_t; DEF_VARR (ifstate_t); typedef VARR (token_t) * token_arr_t; DEF_VARR (token_arr_t); typedef struct macro_call { macro_t macro; pos_t pos; /* Var array of arguments, each arg is var array of tokens, NULL for args absence: */ VARR (token_arr_t) * args; int repl_pos; /* position in macro replacement */ VARR (token_t) * repl_buffer; /* LIST:(token nodes)* */ } * macro_call_t; DEF_VARR (macro_call_t); struct pre_ctx { VARR (token_t) * temp_tokens; HTAB (macro_t) * macro_tab; VARR (macro_t) * macros; VARR (ifstate_t) * ifs; /* stack of ifstates */ int no_out_p; /* don't output lexs -- put them into buffer */ int skip_if_part_p; token_t if_id; /* last processed token #if or #elif: used for error messages */ char date_str[50], time_str[50], date_str_repr[50], time_str_repr[50]; VARR (token_t) * output_buffer; VARR (macro_call_t) * macro_call_stack; VARR (token_t) * pre_expr; token_t pre_last_token; pos_t actual_pre_pos; unsigned long pptokens_num; }; #define temp_tokens c2m_ctx->pre_ctx->temp_tokens #define macro_tab c2m_ctx->pre_ctx->macro_tab #define macros c2m_ctx->pre_ctx->macros #define ifs c2m_ctx->pre_ctx->ifs #define no_out_p c2m_ctx->pre_ctx->no_out_p #define skip_if_part_p c2m_ctx->pre_ctx->skip_if_part_p #define if_id c2m_ctx->pre_ctx->if_id #define date_str c2m_ctx->pre_ctx->date_str #define time_str c2m_ctx->pre_ctx->time_str #define date_str_repr c2m_ctx->pre_ctx->date_str_repr #define time_str_repr c2m_ctx->pre_ctx->time_str_repr #define output_buffer c2m_ctx->pre_ctx->output_buffer #define macro_call_stack c2m_ctx->pre_ctx->macro_call_stack #define pre_expr c2m_ctx->pre_ctx->pre_expr #define pre_last_token c2m_ctx->pre_ctx->pre_last_token #define actual_pre_pos c2m_ctx->pre_ctx->actual_pre_pos #define pptokens_num c2m_ctx->pre_ctx->pptokens_num /* It is a token based prerpocessor. It is input preprocessor tokens and output is (parser) tokens */ static void add_to_temp_string (c2m_ctx_t c2m_ctx, const char *str) { size_t i, len; if ((len = VARR_LENGTH (char, temp_string)) != 0 && VARR_GET (char, temp_string, len - 1) == '\0') { VARR_POP (char, temp_string); } len = strlen (str); for (i = 0; i < len; i++) VARR_PUSH (char, temp_string, str[i]); VARR_PUSH (char, temp_string, '\0'); } static int macro_eq (macro_t macro1, macro_t macro2, void *arg) { return macro1->id->repr == macro2->id->repr; } static htab_hash_t macro_hash (macro_t macro, void *arg) { return mir_hash (macro->id->repr, strlen (macro->id->repr), 0x42); } static macro_t new_macro (c2m_ctx_t c2m_ctx, token_t id, VARR (token_t) * params, VARR (token_t) * replacement); static void new_std_macro (c2m_ctx_t c2m_ctx, const char *id_str) { new_macro (c2m_ctx, new_id_token (c2m_ctx, no_pos, id_str), NULL, NULL); } static void init_macros (c2m_ctx_t c2m_ctx) { VARR (token_t) * params; VARR_CREATE (macro_t, macros, 2048); HTAB_CREATE (macro_t, macro_tab, 2048, macro_hash, macro_eq, NULL); /* Standard macros : */ new_std_macro (c2m_ctx, "__DATE__"); new_std_macro (c2m_ctx, "__TIME__"); new_std_macro (c2m_ctx, "__FILE__"); new_std_macro (c2m_ctx, "__LINE__"); VARR_CREATE (token_t, params, 1); VARR_PUSH (token_t, params, new_id_token (c2m_ctx, no_pos, "$")); if (!options->pedantic_p) new_macro (c2m_ctx, new_id_token (c2m_ctx, no_pos, "__has_include"), params, NULL); } static macro_t new_macro (c2m_ctx_t c2m_ctx, token_t id, VARR (token_t) * params, VARR (token_t) * replacement) { macro_t tab_m, m = malloc (sizeof (struct macro)); m->id = id; m->params = params; m->replacement = replacement; m->ignore_p = FALSE; assert (!HTAB_DO (macro_t, macro_tab, m, HTAB_FIND, tab_m)); HTAB_DO (macro_t, macro_tab, m, HTAB_INSERT, tab_m); VARR_PUSH (macro_t, macros, m); return m; } static void finish_macros (c2m_ctx_t c2m_ctx) { if (macros != NULL) { while (VARR_LENGTH (macro_t, macros) != 0) { macro_t m = VARR_POP (macro_t, macros); if (m->params != NULL) VARR_DESTROY (token_t, m->params); if (m->replacement != NULL) VARR_DESTROY (token_t, m->replacement); free (m); } VARR_DESTROY (macro_t, macros); } if (macro_tab != NULL) HTAB_DESTROY (macro_t, macro_tab); } static macro_call_t new_macro_call (macro_t m, pos_t pos) { macro_call_t mc = malloc (sizeof (struct macro_call)); mc->macro = m; mc->pos = pos; mc->repl_pos = 0; mc->args = NULL; VARR_CREATE (token_t, mc->repl_buffer, 64); return mc; } static void free_macro_call (macro_call_t mc) { VARR_DESTROY (token_t, mc->repl_buffer); if (mc->args != NULL) { while (VARR_LENGTH (token_arr_t, mc->args) != 0) { VARR (token_t) *arg = VARR_POP (token_arr_t, mc->args); VARR_DESTROY (token_t, arg); } VARR_DESTROY (token_arr_t, mc->args); } free (mc); } static ifstate_t new_ifstate (int skip_p, int true_p, int else_p, pos_t if_pos) { ifstate_t ifstate = malloc (sizeof (struct ifstate)); ifstate->skip_p = skip_p; ifstate->true_p = true_p; ifstate->else_p = else_p; ifstate->if_pos = if_pos; return ifstate; } static void pop_ifstate (c2m_ctx_t c2m_ctx) { free (VARR_POP (ifstate_t, ifs)); } static void (*pre_out_token_func) (c2m_ctx_t c2m_ctx, token_t); static void pre_init (MIR_context_t ctx) { c2m_ctx_t c2m_ctx = *c2m_ctx_loc (ctx); time_t t; struct tm *tm; c2m_ctx->pre_ctx = c2mir_calloc (ctx, sizeof (struct pre_ctx)); no_out_p = skip_if_part_p = FALSE; t = time (NULL); tm = localtime (&t); if (tm == NULL) { strcpy (date_str_repr, "\"Unknown date\""); strcpy (time_str_repr, "\"Unknown time\""); } else { strftime (date_str_repr, sizeof (date_str), "\"%b %d %Y\"", tm); strftime (time_str_repr, sizeof (time_str), "\"%H:%M:%S\"", tm); } strcpy (date_str, date_str_repr + 1); date_str[strlen (date_str) - 1] = '\0'; strcpy (time_str, time_str_repr + 1); time_str[strlen (time_str) - 1] = '\0'; VARR_CREATE (token_t, temp_tokens, 128); VARR_CREATE (token_t, output_buffer, 2048); init_macros (c2m_ctx); VARR_CREATE (ifstate_t, ifs, 512); VARR_CREATE (macro_call_t, macro_call_stack, 512); } static void pre_finish (c2m_ctx_t c2m_ctx) { if (c2m_ctx == NULL || c2m_ctx->pre_ctx == NULL) return; if (temp_tokens != NULL) VARR_DESTROY (token_t, temp_tokens); if (output_buffer != NULL) VARR_DESTROY (token_t, output_buffer); finish_macros (c2m_ctx); if (ifs != NULL) { while (VARR_LENGTH (ifstate_t, ifs) != 0) pop_ifstate (c2m_ctx); VARR_DESTROY (ifstate_t, ifs); } if (macro_call_stack != NULL) { while (VARR_LENGTH (macro_call_t, macro_call_stack) != 0) free_macro_call (VARR_POP (macro_call_t, macro_call_stack)); VARR_DESTROY (macro_call_t, macro_call_stack); } free (c2m_ctx->pre_ctx); } static void add_include_stream (c2m_ctx_t c2m_ctx, const char *fname, pos_t err_pos) { FILE *f; assert (fname != NULL); if ((f = fopen (fname, "r")) == NULL) { if (options->message_file != NULL) error (c2m_ctx, err_pos, "error in opening file %s", fname); longjmp (c2m_ctx->env, 1); // ??? } add_stream (c2m_ctx, f, fname, NULL); cs->ifs_length_at_stream_start = VARR_LENGTH (ifstate_t, ifs); } static void skip_nl (c2m_ctx_t c2m_ctx, token_t t, VARR (token_t) * buffer) { /* skip until new line */ if (t == NULL) t = get_next_pptoken (c2m_ctx); for (; t->code != '\n'; t = get_next_pptoken (c2m_ctx)) // ??> if (buffer != NULL) VARR_PUSH (token_t, buffer, t); unget_next_pptoken (c2m_ctx, t); } static const char *varg = "__VA_ARGS__"; static int find_param (VARR (token_t) * params, const char *name) { size_t len; token_t param; len = VARR_LENGTH (token_t, params); if (strcmp (name, varg) == 0 && len != 0 && VARR_LAST (token_t, params)->code == T_DOTS) return len - 1; for (int i = 0; i < len; i++) { param = VARR_GET (token_t, params, i); if (strcmp (param->repr, name) == 0) return i; } return -1; } static int params_eq_p (VARR (token_t) * params1, VARR (token_t) * params2) { token_t param1, param2; if (params1 == NULL || params2 == NULL) return params1 == params2; if (VARR_LENGTH (token_t, params1) != VARR_LENGTH (token_t, params2)) return FALSE; for (int i = 0; i < VARR_LENGTH (token_t, params1); i++) { param1 = VARR_GET (token_t, params1, i); param2 = VARR_GET (token_t, params2, i); if (strcmp (param1->repr, param2->repr) != 0) return FALSE; } return TRUE; } static int replacement_eq_p (VARR (token_t) * r1, VARR (token_t) * r2) { token_t el1, el2; if (VARR_LENGTH (token_t, r1) != VARR_LENGTH (token_t, r2)) return FALSE; for (int i = 0; i < VARR_LENGTH (token_t, r1); i++) { el1 = VARR_GET (token_t, r1, i); el2 = VARR_GET (token_t, r2, i); if (el1->code == ' ' && el2->code == ' ') return TRUE; if (el1->node_code != el2->node_code) return FALSE; if (strcmp (el1->repr, el2->repr) != 0) return FALSE; } return TRUE; } static void define (c2m_ctx_t c2m_ctx) { VARR (token_t) * repl, *params; token_t id, t; const char *name; macro_t m; struct macro macro_struct; t = get_next_pptoken (c2m_ctx); // ??? if (t->code == ' ') t = get_next_pptoken (c2m_ctx); // ?? if (t->code != T_ID) { error (c2m_ctx, t->pos, "no ident after #define: %s", t->repr); skip_nl (c2m_ctx, t, NULL); return; } id = t; t = get_next_pptoken (c2m_ctx); VARR_CREATE (token_t, repl, 64); params = NULL; if (t->code == '(') { VARR_CREATE (token_t, params, 16); t = get_next_pptoken (c2m_ctx); /* skip '(' */ if (t->code == ' ') t = get_next_pptoken (c2m_ctx); if (t->code != ')') { for (;;) { if (t->code == ' ') t = get_next_pptoken (c2m_ctx); if (t->code == T_ID) { if (find_param (params, t->repr) >= 0) error (c2m_ctx, t->pos, "repeated macro parameter %s", t->repr); VARR_PUSH (token_t, params, t); } else if (t->code == T_DOTS) { VARR_PUSH (token_t, params, t); } else { error (c2m_ctx, t->pos, "macro parameter is expected"); break; } t = get_next_pptoken (c2m_ctx); if (t->code == ' ') t = get_next_pptoken (c2m_ctx); if (t->code == ')') break; if (VARR_LAST (token_t, params)->code == T_DOTS) { error (c2m_ctx, t->pos, "... is not the last parameter"); break; } if (t->code == T_DOTS) continue; if (t->code != ',') { error (c2m_ctx, t->pos, "missed ,"); continue; } t = get_next_pptoken (c2m_ctx); } } for (; t->code != '\n' && t->code != ')';) t = get_next_pptoken (c2m_ctx); if (t->code == ')') t = get_next_pptoken (c2m_ctx); } if (t->code == ' ') t = get_next_pptoken (c2m_ctx); for (; t->code != '\n'; t = get_next_pptoken (c2m_ctx)) { if (t->code == T_DBLNO) t->code = T_RDBLNO; VARR_PUSH (token_t, repl, t); } unget_next_pptoken (c2m_ctx, t); name = id->repr; macro_struct.id = id; if (!HTAB_DO (macro_t, macro_tab, ¯o_struct, HTAB_FIND, m)) { if (strcmp (name, "defined") == 0) { error (c2m_ctx, id->pos, "macro definition of %s", name); } else { new_macro (c2m_ctx, id, params, repl); } } else if (m->replacement == NULL) { error (c2m_ctx, id->pos, "standard macro %s redefinition", name); } else { if (!params_eq_p (m->params, params) || !replacement_eq_p (m->replacement, repl)) error (c2m_ctx, id->pos, "different macro redefinition of %s", name); VARR_DESTROY (token_t, repl); } } #ifdef C2MIR_PREPRO_DEBUG static void print_output_buffer (void) { fprintf (stderr, "output buffer:"); for (size_t i = 0; i < (int) VARR_LENGTH (token_t, output_buffer); i++) { fprintf (stderr, " <%s>", get_token_str (VARR_GET (token_t, output_buffer, i))); } fprintf (stderr, "\n"); } #endif static void push_back (c2m_ctx_t c2m_ctx, VARR (token_t) * tokens) { #ifdef C2MIR_PREPRO_DEBUG fprintf (stderr, "# push back (macro call depth %d):", VARR_LENGTH (macro_call_t, macro_call_stack)); #endif for (int i = (int) VARR_LENGTH (token_t, tokens) - 1; i >= 0; i--) { #ifdef C2MIR_PREPRO_DEBUG fprintf (stderr, " <%s>", get_token_str (VARR_GET (token_t, tokens, i))); #endif unget_next_pptoken (c2m_ctx, VARR_GET (token_t, tokens, i)); } #ifdef C2MIR_PREPRO_DEBUG fprintf (stderr, "\no"); print_output_buffer (); #endif } static void copy_and_push_back (c2m_ctx_t c2m_ctx, VARR (token_t) * tokens, pos_t pos) { #ifdef C2MIR_PREPRO_DEBUG fprintf (stderr, "# copy & push back (macro call depth %d):", VARR_LENGTH (macro_call_t, macro_call_stack)); #endif for (int i = (int) VARR_LENGTH (token_t, tokens) - 1; i >= 0; i--) { #ifdef C2MIR_PREPRO_DEBUG fprintf (stderr, " <%s>", get_token_str (VARR_GET (token_t, tokens, i))); #endif unget_next_pptoken (c2m_ctx, copy_token (c2m_ctx, VARR_GET (token_t, tokens, i), pos)); } #ifdef C2MIR_PREPRO_DEBUG fprintf (stderr, "\n"); print_output_buffer (); #endif } static int file_found_p (const char *name) { FILE *f; if ((f = fopen (name, "r")) == NULL) return FALSE; fclose (f); return TRUE; } static const char *get_full_name (c2m_ctx_t c2m_ctx, const char *base, const char *name, int dir_base_p) { const char *str, *last; size_t len; VARR_TRUNC (char, temp_string, 0); if (base == NULL || *base == '\0') { assert (name != NULL && name[0] != '\0'); return name; } if (dir_base_p) { len = strlen (base); assert (len > 0); add_to_temp_string (c2m_ctx, base); if (base[len - 1] != '/') add_to_temp_string (c2m_ctx, "/"); } else if ((last = strrchr (base, '/')) == NULL) { add_to_temp_string (c2m_ctx, "./"); } else { for (str = base; str <= last; str++) VARR_PUSH (char, temp_string, *str); VARR_PUSH (char, temp_string, '\0'); } add_to_temp_string (c2m_ctx, name); return VARR_ADDR (char, temp_string); } static const char *get_include_fname (c2m_ctx_t c2m_ctx, token_t t) { const char *fullname, *name; assert (t->code == T_STR || t->code == T_HEADER); if ((name = t->node->u.s.s)[0] != '/') { if (t->repr[0] == '"') { /* Search relative to the current source dir */ if (cs->fname != NULL) { fullname = get_full_name (c2m_ctx, cs->fname, name, FALSE); if (file_found_p (fullname)) return uniq_cstr (c2m_ctx, fullname).s; } for (size_t i = 0; header_dirs[i] != NULL; i++) { fullname = get_full_name (c2m_ctx, header_dirs[i], name, TRUE); if (file_found_p (fullname)) return uniq_cstr (c2m_ctx, fullname).s; } } for (size_t i = 0; system_header_dirs[i] != NULL; i++) { fullname = get_full_name (c2m_ctx, system_header_dirs[i], name, TRUE); if (file_found_p (fullname)) return uniq_cstr (c2m_ctx, fullname).s; } } return name; } static int digits_p (const char *str) { while ('0' <= *str && *str <= '9') str++; return *str == '\0'; } static pos_t check_line_directive_args (c2m_ctx_t c2m_ctx, VARR (token_t) * buffer) { size_t i, len = VARR_LENGTH (token_t, buffer); token_t *buffer_arr = VARR_ADDR (token_t, buffer); const char *fname; pos_t pos; int lno; unsigned long long l; if (len == 0) return no_pos; i = buffer_arr[0]->code == ' ' ? 1 : 0; fname = buffer_arr[i]->pos.fname; if (i >= len || buffer_arr[i]->code != T_NUMBER) return no_pos; if (!digits_p (buffer_arr[i]->repr)) return no_pos; errno = 0; lno = l = strtoll (buffer_arr[i]->repr, NULL, 10); if (errno || l > ((1ul << 31) - 1)) error (c2m_ctx, buffer_arr[i]->pos, "#line with too big value: %s", buffer_arr[i]->repr); i++; if (i < len && buffer_arr[i]->code == ' ') i++; if (i < len && buffer_arr[i]->code == T_STR) { fname = buffer_arr[i]->node->u.s.s; i++; } if (i == len) { pos.fname = fname; pos.lno = lno; pos.ln_pos = 0; return pos; } return no_pos; } static void check_pragma (c2m_ctx_t c2m_ctx, token_t t, VARR (token_t) * tokens) { token_t *tokens_arr = VARR_ADDR (token_t, tokens); size_t i, tokens_len = VARR_LENGTH (token_t, tokens); i = 0; if (i < tokens_len && tokens_arr[i]->code == ' ') i++; if (i >= tokens_len || tokens_arr[i]->code != T_ID || strcmp (tokens_arr[i]->repr, "STDC") != 0) { warning (c2m_ctx, t->pos, "unknown pragma"); return; } i++; if (i < tokens_len && tokens_arr[i]->code == ' ') i++; if (i >= tokens_len || tokens_arr[i]->code != T_ID) { error (c2m_ctx, t->pos, "wrong STDC pragma"); return; } if (strcmp (tokens_arr[i]->repr, "FP_CONTRACT") != 0 && strcmp (tokens_arr[i]->repr, "FENV_ACCESS") != 0 && strcmp (tokens_arr[i]->repr, "CX_LIMITED_RANGE") != 0) { error (c2m_ctx, t->pos, "unknown STDC pragma %s", tokens_arr[i]->repr); return; } i++; if (i < tokens_len && tokens_arr[i]->code == ' ') i++; if (i >= tokens_len || tokens_arr[i]->code != T_ID) { error (c2m_ctx, t->pos, "wrong STDC pragma value"); return; } if (strcmp (tokens_arr[i]->repr, "ON") != 0 && strcmp (tokens_arr[i]->repr, "OFF") != 0 && strcmp (tokens_arr[i]->repr, "DEFAULT") != 0) { error (c2m_ctx, t->pos, "unknown STDC pragma value", tokens_arr[i]->repr); return; } i++; if (i < tokens_len && (tokens_arr[i]->code == ' ' || tokens_arr[i]->code == '\n')) i++; if (i < tokens_len) error (c2m_ctx, t->pos, "garbage at STDC pragma end"); } static void pop_macro_call (c2m_ctx_t c2m_ctx) { macro_call_t mc; mc = VARR_POP (macro_call_t, macro_call_stack); #ifdef C2MIR_PREPRO_DEBUG fprintf (stderr, "finish macro %s\n", mc->macro->id->repr); #endif mc->macro->ignore_p = FALSE; free_macro_call (mc); } static void find_args (c2m_ctx_t c2m_ctx, macro_call_t mc) { /* we have just read a parenthesis */ macro_t m; token_t t; int va_p, level = 0; size_t params_len; VARR (token_arr_t) * args; VARR (token_t) * arg, *temp_arr; m = mc->macro; VARR_CREATE (token_arr_t, args, 16); VARR_CREATE (token_t, arg, 16); params_len = VARR_LENGTH (token_t, m->params); va_p = params_len == 1 && VARR_GET (token_t, m->params, 0)->code == T_DOTS; #ifdef C2MIR_PREPRO_DEBUG fprintf (stderr, "# finding args of macro %s call:\n# arg 0:", m->id->repr); #endif for (;;) { t = get_next_pptoken (c2m_ctx); #ifdef C2MIR_PREPRO_DEBUG fprintf (stderr, " <%s>%s", get_token_str (t), t->processed_p ? "*" : ""); #endif if (t->code == T_EOR) { t = get_next_pptoken (c2m_ctx); #ifdef C2MIR_PREPRO_DEBUG fprintf (stderr, " <%s>", get_token_str (t), t->processed_p ? "*" : ""); #endif pop_macro_call (c2m_ctx); } if (t->code == T_EOFILE || t->code == T_EOU || t->code == T_EOR || t->code == T_BOA || t->code == T_EOA) break; if (level == 0 && t->code == ')') break; if (level == 0 && !va_p && t->code == ',') { VARR_PUSH (token_arr_t, args, arg); #ifdef C2MIR_PREPRO_DEBUG fprintf (stderr, "\n# arg %d:", VARR_LENGTH (token_arr_t, args)); #endif VARR_CREATE (token_t, arg, 16); if (VARR_LENGTH (token_arr_t, args) == params_len - 1 && strcmp (VARR_GET (token_t, m->params, params_len - 1)->repr, "...") == 0) va_p = 1; } else { VARR_PUSH (token_t, arg, t); if (t->code == ')') level--; else if (t->code == '(') level++; } } #ifdef C2MIR_PREPRO_DEBUG fprintf (stderr, "\n"); #endif if (t->code != ')') error (c2m_ctx, t->pos, "unfinished call of macro %s", m->id->repr); VARR_PUSH (token_arr_t, args, arg); if (params_len == 0 && VARR_LENGTH (token_arr_t, args) == 1) { token_arr_t arr = VARR_GET (token_arr_t, args, 0); if (VARR_LENGTH (token_t, arr) == 0 || (VARR_LENGTH (token_t, arr) == 1 && VARR_GET (token_t, arr, 0)->code == ' ')) { temp_arr = VARR_POP (token_arr_t, args); VARR_DESTROY (token_t, temp_arr); mc->args = args; return; } } if (VARR_LENGTH (token_arr_t, args) > params_len) { t = VARR_GET (token_t, VARR_GET (token_arr_t, args, params_len), 0); while (VARR_LENGTH (token_arr_t, args) > params_len) { temp_arr = VARR_POP (token_arr_t, args); VARR_DESTROY (token_t, temp_arr); } error (c2m_ctx, t->pos, "too many args for call of macro %s", m->id->repr); } else if (VARR_LENGTH (token_arr_t, args) < params_len) { for (; VARR_LENGTH (token_arr_t, args) < params_len;) { VARR_CREATE (token_t, arg, 16); VARR_PUSH (token_arr_t, args, arg); } error (c2m_ctx, t->pos, "not enough args for call of macro %s", m->id->repr); } mc->args = args; } static token_t token_concat (c2m_ctx_t c2m_ctx, token_t t1, token_t t2) { token_t t, next; VARR_TRUNC (char, temp_string, 0); add_to_temp_string (c2m_ctx, t1->repr); add_to_temp_string (c2m_ctx, t2->repr); reverse (temp_string); set_string_stream (c2m_ctx, VARR_ADDR (char, temp_string), t1->pos, NULL); t = get_next_pptoken (c2m_ctx); next = get_next_pptoken (c2m_ctx); if (next->code != T_EOFILE) { error (c2m_ctx, t1->pos, "wrong result of ##: %s", reverse (temp_string)); remove_string_stream (c2m_ctx); } return t; } static void add_token (VARR (token_t) * to, token_t t) { if ((t->code != ' ' && t->code != '\n') || VARR_LENGTH (token_t, to) == 0 || (VARR_LAST (token_t, to)->code != ' ' && VARR_LAST (token_t, to)->code != '\n')) VARR_PUSH (token_t, to, t); } static void add_arg_tokens (VARR (token_t) * to, VARR (token_t) * from) { int start; for (start = VARR_LENGTH (token_t, from) - 1; start >= 0; start--) if (VARR_GET (token_t, from, start)->code == T_BOA) break; assert (start >= 0); for (size_t i = start + 1; i < VARR_LENGTH (token_t, from); i++) add_token (to, VARR_GET (token_t, from, i)); VARR_TRUNC (token_t, from, start); } static void add_tokens (VARR (token_t) * to, VARR (token_t) * from) { for (size_t i = 0; i < VARR_LENGTH (token_t, from); i++) add_token (to, VARR_GET (token_t, from, i)); } static void del_tokens (VARR (token_t) * tokens, int from, int len) { int diff, tokens_len = VARR_LENGTH (token_t, tokens); token_t *addr = VARR_ADDR (token_t, tokens); if (len < 0) len = tokens_len - from; assert (from + len <= tokens_len); if ((diff = tokens_len - from - len) > 0) memmove (addr + from, addr + from + len, diff * sizeof (token_t)); VARR_TRUNC (token_t, tokens, tokens_len - len); } static VARR (token_t) * do_concat (c2m_ctx_t c2m_ctx, VARR (token_t) * tokens) { int i, j, k, empty_j_p, empty_k_p, len = VARR_LENGTH (token_t, tokens); token_t t; for (i = len - 1; i >= 0; i--) if ((t = VARR_GET (token_t, tokens, i))->code == T_RDBLNO) { j = i + 1; k = i - 1; assert (k >= 0 && j < len); if (VARR_GET (token_t, tokens, j)->code == ' ' || VARR_GET (token_t, tokens, j)->code == '\n') j++; if (VARR_GET (token_t, tokens, k)->code == ' ' || VARR_GET (token_t, tokens, k)->code == '\n') k--; assert (k >= 0 && j < len); empty_j_p = VARR_GET (token_t, tokens, j)->code == T_PLM; empty_k_p = VARR_GET (token_t, tokens, k)->code == T_PLM; if (empty_j_p || empty_k_p) { if (!empty_j_p) j--; else if (j + 1 < len && (VARR_GET (token_t, tokens, j + 1)->code == ' ' || VARR_GET (token_t, tokens, j + 1)->code == '\n')) j++; if (!empty_k_p) k++; else if (k != 0 && (VARR_GET (token_t, tokens, k - 1)->code == ' ' || VARR_GET (token_t, tokens, k - 1)->code == '\n')) k--; if (!empty_j_p || !empty_k_p) { del_tokens (tokens, k, j - k + 1); } else { del_tokens (tokens, k, j - k); t = new_token (c2m_ctx, t->pos, "", ' ', N_IGNORE); VARR_SET (token_t, tokens, k, t); } } else { t = token_concat (c2m_ctx, VARR_GET (token_t, tokens, k), VARR_GET (token_t, tokens, j)); del_tokens (tokens, k + 1, j - k); VARR_SET (token_t, tokens, k, t); } i = k; len = VARR_LENGTH (token_t, tokens); } for (i = len - 1; i >= 0; i--) VARR_GET (token_t, tokens, i)->processed_p = TRUE; return tokens; } static void process_replacement (c2m_ctx_t c2m_ctx, macro_call_t mc) { macro_t m; token_t t, *m_repl; VARR (token_t) * arg; int i, m_repl_len, sharp_pos, copy_p; m = mc->macro; sharp_pos = -1; m_repl = VARR_ADDR (token_t, m->replacement); m_repl_len = VARR_LENGTH (token_t, m->replacement); for (;;) { if (mc->repl_pos >= m_repl_len) { t = get_next_pptoken (c2m_ctx); unget_next_pptoken (c2m_ctx, t); #ifdef C2MIR_PREPRO_DEBUG fprintf (stderr, "# push back <%s>\n", get_token_str (t)); #endif unget_next_pptoken (c2m_ctx, new_token (c2m_ctx, t->pos, "", T_EOR, N_IGNORE)); #ifdef C2MIR_PREPRO_DEBUG fprintf (stderr, "# push back : mc=%lx\n", mc); #endif push_back (c2m_ctx, do_concat (c2m_ctx, mc->repl_buffer)); m->ignore_p = TRUE; return; } t = m_repl[mc->repl_pos++]; copy_p = TRUE; if (t->code == T_ID) { i = find_param (m->params, t->repr); if (i >= 0) { arg = VARR_GET (token_arr_t, mc->args, i); if (sharp_pos >= 0) { del_tokens (mc->repl_buffer, sharp_pos, -1); if (VARR_LENGTH (token_t, arg) != 0 && (VARR_GET (token_t, arg, 0)->code == ' ' || VARR_GET (token_t, arg, 0)->code == '\n')) del_tokens (arg, 0, 1); if (VARR_LENGTH (token_t, arg) != 0 && (VARR_LAST (token_t, arg)->code == ' ' || VARR_LAST (token_t, arg)->code == '\n')) VARR_POP (token_t, arg); t = token_stringify (c2m_ctx, mc->macro->id, arg); copy_p = FALSE; } else if ((mc->repl_pos >= 2 && m_repl[mc->repl_pos - 2]->code == T_RDBLNO) || (mc->repl_pos >= 3 && m_repl[mc->repl_pos - 2]->code == ' ' && m_repl[mc->repl_pos - 3]->code == T_RDBLNO) || (mc->repl_pos < m_repl_len && m_repl[mc->repl_pos]->code == T_RDBLNO) || (mc->repl_pos + 1 < m_repl_len && m_repl[mc->repl_pos + 1]->code == T_RDBLNO && m_repl[mc->repl_pos]->code == ' ')) { if (VARR_LENGTH (token_t, arg) == 0 || (VARR_LENGTH (token_t, arg) == 1 && (VARR_GET (token_t, arg, 0)->code == ' ' || VARR_GET (token_t, arg, 0)->code == '\n'))) { t = new_token (c2m_ctx, t->pos, "", T_PLM, N_IGNORE); copy_p = FALSE; } else { add_tokens (mc->repl_buffer, arg); continue; } } else { unget_next_pptoken (c2m_ctx, new_token (c2m_ctx, t->pos, "", T_EOA, N_IGNORE)); #ifdef C2MIR_PREPRO_DEBUG fprintf (stderr, "# push back for macro %s call\n", mc->macro->id->repr); #endif copy_and_push_back (c2m_ctx, arg, mc->pos); unget_next_pptoken (c2m_ctx, new_token (c2m_ctx, t->pos, "", T_BOA, N_IGNORE)); #ifdef C2MIR_PREPRO_DEBUG fprintf (stderr, "# push back for macro %s call\n", mc->macro->id->repr); #endif return; } } } else if (t->code == '#') { sharp_pos = VARR_LENGTH (token_t, mc->repl_buffer); } else if (t->code != ' ') { sharp_pos = -1; } if (copy_p) t = copy_token (c2m_ctx, t, mc->pos); add_token (mc->repl_buffer, t); } } static void prepare_pragma_string (const char *repr, VARR (char) * to) { destringify (repr, to); reverse (to); } static int process_pragma (c2m_ctx_t c2m_ctx, token_t t) { token_t t1, t2; if (strcmp (t->repr, "_Pragma") != 0) return FALSE; VARR_TRUNC (token_t, temp_tokens, 0); t1 = get_next_pptoken (c2m_ctx); VARR_PUSH (token_t, temp_tokens, t1); if (t1->code == ' ' || t1->code == '\n') { t1 = get_next_pptoken (c2m_ctx); VARR_PUSH (token_t, temp_tokens, t1); } if (t1->code != '(') { push_back (c2m_ctx, temp_tokens); return FALSE; } t1 = get_next_pptoken (c2m_ctx); VARR_PUSH (token_t, temp_tokens, t1); if (t1->code == ' ' || t1->code == '\n') { t1 = get_next_pptoken (c2m_ctx); VARR_PUSH (token_t, temp_tokens, t1); } if (t1->code != T_STR) { push_back (c2m_ctx, temp_tokens); return FALSE; } t2 = t1; t1 = get_next_pptoken (c2m_ctx); VARR_PUSH (token_t, temp_tokens, t1); if (t1->code == ' ' || t1->code == '\n') { t1 = get_next_pptoken (c2m_ctx); VARR_PUSH (token_t, temp_tokens, t1); } if (t1->code != ')') { push_back (c2m_ctx, temp_tokens); return FALSE; } set_string_stream (c2m_ctx, t2->repr, t2->pos, prepare_pragma_string); VARR_TRUNC (token_t, temp_tokens, 0); for (t1 = get_next_pptoken (c2m_ctx); t1->code != T_EOFILE; t1 = get_next_pptoken (c2m_ctx)) VARR_PUSH (token_t, temp_tokens, t1); check_pragma (c2m_ctx, t2, temp_tokens); return TRUE; } static void flush_buffer (c2m_ctx_t c2m_ctx) { for (size_t i = 0; i < VARR_LENGTH (token_t, output_buffer); i++) pre_out_token_func (c2m_ctx, VARR_GET (token_t, output_buffer, i)); VARR_TRUNC (token_t, output_buffer, 0); } static void out_token (c2m_ctx_t c2m_ctx, token_t t) { if (no_out_p || VARR_LENGTH (macro_call_t, macro_call_stack) != 0) { VARR_PUSH (token_t, output_buffer, t); return; } flush_buffer (c2m_ctx); pre_out_token_func (c2m_ctx, t); } struct val { int uns_p; union { mir_llong i_val; mir_ullong u_val; } u; }; static void move_tokens (VARR (token_t) * to, VARR (token_t) * from) { VARR_TRUNC (token_t, to, 0); for (int i = 0; i < VARR_LENGTH (token_t, from); i++) VARR_PUSH (token_t, to, VARR_GET (token_t, from, i)); VARR_TRUNC (token_t, from, 0); } static void reverse_move_tokens (c2m_ctx_t c2m_ctx, VARR (token_t) * to, VARR (token_t) * from) { VARR_TRUNC (token_t, to, 0); while (VARR_LENGTH (token_t, from) != 0) VARR_PUSH (token_t, to, VARR_POP (token_t, from)); } static void transform_to_header (c2m_ctx_t c2m_ctx, VARR (token_t) * buffer) { int i, j, k; token_t t; pos_t pos; for (i = 0; i < VARR_LENGTH (token_t, buffer) && VARR_GET (token_t, buffer, i)->code == ' '; i++) ; if (i >= VARR_LENGTH (token_t, buffer)) return; if ((t = VARR_GET (token_t, buffer, i))->node_code != N_LT) return; pos = t->pos; for (j = i + 1; j < VARR_LENGTH (token_t, buffer) && VARR_GET (token_t, buffer, j)->node_code != N_GT; j++) ; if (j >= VARR_LENGTH (token_t, buffer)) return; VARR_TRUNC (char, symbol_text, 0); VARR_TRUNC (char, temp_string, 0); VARR_PUSH (char, symbol_text, '<'); for (k = i + 1; k < j; k++) { t = VARR_GET (token_t, buffer, k); for (const char *s = t->repr; *s != 0; s++) { VARR_PUSH (char, symbol_text, *s); VARR_PUSH (char, temp_string, *s); } } VARR_PUSH (char, symbol_text, '>'); VARR_PUSH (char, symbol_text, '\0'); VARR_PUSH (char, temp_string, '\0'); del_tokens (buffer, i, j - i); t = new_node_token (c2m_ctx, pos, VARR_ADDR (char, symbol_text), T_HEADER, new_str_node (c2m_ctx, N_STR, uniq_cstr (c2m_ctx, VARR_ADDR (char, temp_string)), pos)); VARR_SET (token_t, buffer, i, t); } static void processing (c2m_ctx_t c2m_ctx, int ignore_directive_p); static struct val eval_expr (c2m_ctx_t c2m_ctx, VARR (token_t) * buffer, token_t if_token); static const char *get_header_name (c2m_ctx_t c2m_ctx, VARR (token_t) * buffer, pos_t err_pos) { int i; transform_to_header (c2m_ctx, buffer); i = 0; if (VARR_LENGTH (token_t, buffer) != 0 && VARR_GET (token_t, buffer, 0)->code == ' ') i++; if (i != VARR_LENGTH (token_t, buffer) - 1 || (VARR_GET (token_t, buffer, i)->code != T_STR && VARR_GET (token_t, buffer, i)->code != T_HEADER)) { error (c2m_ctx, err_pos, "wrong #include"); return NULL; } return get_include_fname (c2m_ctx, VARR_GET (token_t, buffer, i)); } static void process_directive (c2m_ctx_t c2m_ctx) { token_t t, t1; int true_p; VARR (token_t) * temp_buffer; pos_t pos; struct macro macro; macro_t tab_macro; const char *name; t = get_next_pptoken (c2m_ctx); if (t->code == '\n') return; if (t->code == ' ') t = get_next_pptoken (c2m_ctx); if (t->code != T_ID) { if (!skip_if_part_p) error (c2m_ctx, t->pos, "wrong directive name %s", t->repr); skip_nl (c2m_ctx, NULL, NULL); return; } VARR_CREATE (token_t, temp_buffer, 64); if (strcmp (t->repr, "ifdef") == 0 || strcmp (t->repr, "ifndef") == 0) { t1 = t; if (VARR_LENGTH (ifstate_t, ifs) != 0 && VARR_LAST (ifstate_t, ifs)->skip_p) { skip_if_part_p = true_p = TRUE; skip_nl (c2m_ctx, NULL, NULL); } else { t = get_next_pptoken (c2m_ctx); skip_if_part_p = FALSE; if (t->code == ' ') t = get_next_pptoken (c2m_ctx); if (t->code != T_ID) { error (c2m_ctx, t->pos, "wrong #%s", t1->repr); } else { macro.id = t; skip_if_part_p = HTAB_DO (macro_t, macro_tab, ¯o, HTAB_FIND, tab_macro); } t = get_next_pptoken (c2m_ctx); if (t->code != '\n') { error (c2m_ctx, t1->pos, "garbage at the end of #%s", t1->repr); skip_nl (c2m_ctx, NULL, NULL); } if (strcmp (t1->repr, "ifdef") == 0) skip_if_part_p = !skip_if_part_p; true_p = !skip_if_part_p; } VARR_PUSH (ifstate_t, ifs, new_ifstate (skip_if_part_p, true_p, FALSE, t1->pos)); } else if (strcmp (t->repr, "endif") == 0 || strcmp (t->repr, "else") == 0) { t1 = t; t = get_next_pptoken (c2m_ctx); if (t->code != '\n') { error (c2m_ctx, t1->pos, "garbage at the end of #%s", t1->repr); skip_nl (c2m_ctx, NULL, NULL); } if (VARR_LENGTH (ifstate_t, ifs) < cs->ifs_length_at_stream_start) error (c2m_ctx, t1->pos, "unmatched #%s", t1->repr); else if (strcmp (t1->repr, "endif") == 0) { pop_ifstate (c2m_ctx); skip_if_part_p = VARR_LENGTH (ifstate_t, ifs) == 0 ? 0 : VARR_LAST (ifstate_t, ifs)->skip_p; } else if (VARR_LAST (ifstate_t, ifs)->else_p) { error (c2m_ctx, t1->pos, "repeated #else"); VARR_LAST (ifstate_t, ifs)->skip_p = 1; skip_if_part_p = TRUE; } else { skip_if_part_p = VARR_LAST (ifstate_t, ifs)->true_p; VARR_LAST (ifstate_t, ifs)->true_p = TRUE; VARR_LAST (ifstate_t, ifs)->skip_p = skip_if_part_p; VARR_LAST (ifstate_t, ifs)->else_p = FALSE; } } else if (strcmp (t->repr, "if") == 0 || strcmp (t->repr, "elif") == 0) { if_id = t; if (strcmp (t->repr, "elif") == 0 && VARR_LENGTH (ifstate_t, ifs) == 0) { error (c2m_ctx, t->pos, "#elif without #if"); } else if (strcmp (t->repr, "elif") == 0 && VARR_LAST (ifstate_t, ifs)->else_p) { error (c2m_ctx, t->pos, "#elif after #else"); skip_if_part_p = TRUE; } else if (strcmp (t->repr, "if") == 0 && VARR_LENGTH (ifstate_t, ifs) != 0 && VARR_LAST (ifstate_t, ifs)->skip_p) { skip_if_part_p = true_p = TRUE; skip_nl (c2m_ctx, NULL, NULL); VARR_PUSH (ifstate_t, ifs, new_ifstate (skip_if_part_p, true_p, FALSE, t->pos)); } else if (strcmp (t->repr, "elif") == 0 && VARR_LAST (ifstate_t, ifs)->true_p) { VARR_LAST (ifstate_t, ifs)->skip_p = skip_if_part_p = TRUE; skip_nl (c2m_ctx, NULL, NULL); } else { struct val val; skip_if_part_p = FALSE; /* for eval expr */ skip_nl (c2m_ctx, NULL, temp_buffer); val = eval_expr (c2m_ctx, temp_buffer, t); true_p = val.uns_p ? val.u.u_val != 0 : val.u.i_val != 0; skip_if_part_p = !true_p; if (strcmp (t->repr, "if") == 0) VARR_PUSH (ifstate_t, ifs, new_ifstate (skip_if_part_p, true_p, FALSE, t->pos)); else { VARR_LAST (ifstate_t, ifs)->skip_p = skip_if_part_p; VARR_LAST (ifstate_t, ifs)->true_p = true_p; } } } else if (skip_if_part_p) { skip_nl (c2m_ctx, NULL, NULL); } else if (strcmp (t->repr, "define") == 0) { define (c2m_ctx); } else if (strcmp (t->repr, "include") == 0) { t = get_next_include_pptoken (c2m_ctx); if (t->code == ' ') t = get_next_include_pptoken (c2m_ctx); t1 = get_next_pptoken (c2m_ctx); if ((t->code == T_STR || t->code == T_HEADER) && t1->code == '\n') name = get_include_fname (c2m_ctx, t); else { VARR_PUSH (token_t, temp_buffer, t); skip_nl (c2m_ctx, t1, temp_buffer); unget_next_pptoken (c2m_ctx, new_token (c2m_ctx, t->pos, "", T_EOP, N_IGNORE)); push_back (c2m_ctx, temp_buffer); assert (VARR_LENGTH (macro_call_t, macro_call_stack) == 0 && !no_out_p); no_out_p = TRUE; processing (c2m_ctx, TRUE); no_out_p = FALSE; move_tokens (temp_buffer, output_buffer); if ((name = get_header_name (c2m_ctx, temp_buffer, t->pos)) == NULL) { error (c2m_ctx, t->pos, "wrong #include"); goto ret; } } if (VARR_LENGTH (stream_t, streams) >= max_nested_includes + 1) { error (c2m_ctx, t->pos, "more %d include levels", VARR_LENGTH (stream_t, streams) - 1); goto ret; } add_include_stream (c2m_ctx, name, t->pos); } else if (strcmp (t->repr, "line") == 0) { skip_nl (c2m_ctx, NULL, temp_buffer); unget_next_pptoken (c2m_ctx, new_token (c2m_ctx, t->pos, "", T_EOP, N_IGNORE)); push_back (c2m_ctx, temp_buffer); assert (VARR_LENGTH (macro_call_t, macro_call_stack) == 0 && !no_out_p); no_out_p = 1; processing (c2m_ctx, TRUE); no_out_p = 0; move_tokens (temp_buffer, output_buffer); pos = check_line_directive_args (c2m_ctx, temp_buffer); if (pos.lno < 0) { error (c2m_ctx, t->pos, "wrong #line"); } else { change_stream_pos (c2m_ctx, pos); } } else if (strcmp (t->repr, "error") == 0) { VARR_TRUNC (char, temp_string, 0); add_to_temp_string (c2m_ctx, "#error"); for (t1 = get_next_pptoken (c2m_ctx); t1->code != '\n'; t1 = get_next_pptoken (c2m_ctx)) add_to_temp_string (c2m_ctx, t1->repr); error (c2m_ctx, t->pos, "%s", VARR_ADDR (char, temp_string)); } else if (!options->pedantic_p && strcmp (t->repr, "warning") == 0) { VARR_TRUNC (char, temp_string, 0); add_to_temp_string (c2m_ctx, "#warning"); for (t1 = get_next_pptoken (c2m_ctx); t1->code != '\n'; t1 = get_next_pptoken (c2m_ctx)) add_to_temp_string (c2m_ctx, t1->repr); warning (c2m_ctx, t->pos, "%s", VARR_ADDR (char, temp_string)); } else if (strcmp (t->repr, "pragma") == 0) { skip_nl (c2m_ctx, NULL, temp_buffer); check_pragma (c2m_ctx, t, temp_buffer); } else if (strcmp (t->repr, "undef") == 0) { t = get_next_pptoken (c2m_ctx); if (t->code == ' ') t = get_next_pptoken (c2m_ctx); if (t->code == '\n') { error (c2m_ctx, t->pos, "no ident after #undef"); goto ret; } if (t->code != T_ID) { error (c2m_ctx, t->pos, "no ident after #undef"); skip_nl (c2m_ctx, t, NULL); goto ret; } if (strcmp (t->repr, "defined") == 0) { error (c2m_ctx, t->pos, "#undef of %s", t->repr); } else { macro.id = t; if (HTAB_DO (macro_t, macro_tab, ¯o, HTAB_FIND, tab_macro)) { if (tab_macro->replacement == NULL) error (c2m_ctx, t->pos, "#undef of standard macro %s", t->repr); else HTAB_DO (macro_t, macro_tab, ¯o, HTAB_DELETE, tab_macro); } } } ret: VARR_DESTROY (token_t, temp_buffer); } static int pre_match (c2m_ctx_t c2m_ctx, int c, pos_t *pos, node_code_t *node_code, node_t *node) { token_t t; if (VARR_LENGTH (token_t, pre_expr) == 0) return FALSE; t = VARR_LAST (token_t, pre_expr); if (t->code != c) return FALSE; if (pos != NULL) *pos = t->pos; if (node_code != NULL) *node_code = t->node_code; if (node != NULL) *node = t->node; VARR_POP (token_t, pre_expr); return TRUE; } static node_t pre_cond_expr (c2m_ctx_t c2m_ctx); /* Expressions: */ static node_t pre_primary_expr (c2m_ctx_t c2m_ctx) { node_t r, n; if (pre_match (c2m_ctx, T_CH, NULL, NULL, &r)) return r; if (pre_match (c2m_ctx, T_NUMBER, NULL, NULL, &n)) { if (!pre_match (c2m_ctx, '(', NULL, NULL, NULL)) return n; if (!pre_match (c2m_ctx, ')', NULL, NULL, NULL)) { for (;;) { if ((r = pre_cond_expr (c2m_ctx)) == NULL) return NULL; if (pre_match (c2m_ctx, ')', NULL, NULL, NULL)) break; if (!pre_match (c2m_ctx, ',', NULL, NULL, NULL)) return NULL; } } return new_pos_node (c2m_ctx, N_IGNORE, n->pos); /* error only during evaluation */ } if (pre_match (c2m_ctx, '(', NULL, NULL, NULL)) { if ((r = pre_cond_expr (c2m_ctx)) == NULL) return NULL; if (pre_match (c2m_ctx, ')', NULL, NULL, NULL)) return r; } return NULL; } static node_t pre_unary_expr (c2m_ctx_t c2m_ctx) { node_t r; node_code_t code; pos_t pos; if (!pre_match (c2m_ctx, T_UNOP, &pos, &code, NULL) && !pre_match (c2m_ctx, T_ADDOP, &pos, &code, NULL)) return pre_primary_expr (c2m_ctx); if ((r = pre_unary_expr (c2m_ctx)) == NULL) return r; r = new_pos_node1 (c2m_ctx, code, pos, r); return r; } static node_t pre_left_op (c2m_ctx_t c2m_ctx, int token, int token2, node_t (*f) (c2m_ctx_t c2m_ctx)) { node_code_t code; node_t r, n; pos_t pos; if ((r = f (c2m_ctx)) == NULL) return r; while (pre_match (c2m_ctx, token, &pos, &code, NULL) || (token2 >= 0 && pre_match (c2m_ctx, token2, &pos, &code, NULL))) { n = new_pos_node1 (c2m_ctx, code, pos, r); if ((r = f (c2m_ctx)) == NULL) return r; op_append (n, r); r = n; } return r; } static node_t pre_mul_expr (c2m_ctx_t c2m_ctx) { return pre_left_op (c2m_ctx, T_DIVOP, '*', pre_unary_expr); } static node_t pre_add_expr (c2m_ctx_t c2m_ctx) { return pre_left_op (c2m_ctx, T_ADDOP, -1, pre_mul_expr); } static node_t pre_sh_expr (c2m_ctx_t c2m_ctx) { return pre_left_op (c2m_ctx, T_SH, -1, pre_add_expr); } static node_t pre_rel_expr (c2m_ctx_t c2m_ctx) { return pre_left_op (c2m_ctx, T_CMP, -1, pre_sh_expr); } static node_t pre_eq_expr (c2m_ctx_t c2m_ctx) { return pre_left_op (c2m_ctx, T_EQNE, -1, pre_rel_expr); } static node_t pre_and_expr (c2m_ctx_t c2m_ctx) { return pre_left_op (c2m_ctx, '&', -1, pre_eq_expr); } static node_t pre_xor_expr (c2m_ctx_t c2m_ctx) { return pre_left_op (c2m_ctx, '^', -1, pre_and_expr); } static node_t pre_or_expr (c2m_ctx_t c2m_ctx) { return pre_left_op (c2m_ctx, '|', -1, pre_xor_expr); } static node_t pre_land_expr (c2m_ctx_t c2m_ctx) { return pre_left_op (c2m_ctx, T_ANDAND, -1, pre_or_expr); } static node_t pre_lor_expr (c2m_ctx_t c2m_ctx) { return pre_left_op (c2m_ctx, T_OROR, -1, pre_land_expr); } static node_t pre_cond_expr (c2m_ctx_t c2m_ctx) { node_t r, n; pos_t pos; if ((r = pre_lor_expr (c2m_ctx)) == NULL) return r; if (!pre_match (c2m_ctx, '?', &pos, NULL, NULL)) return r; n = new_pos_node1 (c2m_ctx, N_COND, pos, r); if ((r = pre_cond_expr (c2m_ctx)) == NULL) return r; op_append (n, r); if (!pre_match (c2m_ctx, ':', NULL, NULL, NULL)) return NULL; if ((r = pre_cond_expr (c2m_ctx)) == NULL) return r; op_append (n, r); return n; } static node_t parse_pre_expr (c2m_ctx_t c2m_ctx, VARR (token_t) * expr) { node_t r; token_t t; pre_expr = expr; t = VARR_LAST (token_t, expr); if ((r = pre_cond_expr (c2m_ctx)) != NULL && VARR_LENGTH (token_t, expr) == 0) return r; if (VARR_LENGTH (token_t, expr) != 0) t = VARR_POP (token_t, expr); error (c2m_ctx, t->pos, "wrong preprocessor expression"); return NULL; } static void replace_defined (c2m_ctx_t c2m_ctx, VARR (token_t) * expr_buffer) { int i, j, k, len; token_t t, id; const char *res; struct macro macro_struct; macro_t tab_macro; for (i = 0; i < VARR_LENGTH (token_t, expr_buffer); i++) { /* Change defined ident and defined (ident) */ t = VARR_GET (token_t, expr_buffer, i); if (t->code == T_ID && strcmp (t->repr, "defined") == 0) { j = i + 1; len = VARR_LENGTH (token_t, expr_buffer); if (j < len && VARR_GET (token_t, expr_buffer, j)->code == ' ') j++; if (j >= len) continue; if ((id = VARR_GET (token_t, expr_buffer, j))->code == T_ID) { macro_struct.id = id; res = HTAB_DO (macro_t, macro_tab, ¯o_struct, HTAB_FIND, tab_macro) ? "1" : "0"; VARR_SET (token_t, expr_buffer, i, new_token (c2m_ctx, t->pos, res, T_NUMBER, N_IGNORE)); // ??? del_tokens (expr_buffer, i + 1, j - i); continue; } if (j >= len || VARR_GET (token_t, expr_buffer, j)->code != '(') continue; j++; if (j < len && VARR_GET (token_t, expr_buffer, j)->code == ' ') j++; if (j >= len || VARR_GET (token_t, expr_buffer, j)->code != T_ID) continue; k = j; j++; if (j < len && VARR_GET (token_t, expr_buffer, j)->code == ' ') j++; if (j >= len || VARR_GET (token_t, expr_buffer, j)->code != ')') continue; macro_struct.id = VARR_GET (token_t, expr_buffer, k); res = HTAB_DO (macro_t, macro_tab, ¯o_struct, HTAB_FIND, tab_macro) ? "1" : "0"; VARR_SET (token_t, expr_buffer, i, new_token (c2m_ctx, t->pos, res, T_NUMBER, N_IGNORE)); // ??? del_tokens (expr_buffer, i + 1, j - i); } } } static struct val eval (c2m_ctx_t c2m_ctx, node_t tree); static struct val eval_expr (c2m_ctx_t c2m_ctx, VARR (token_t) * expr_buffer, token_t if_token) { int i, j; token_t t, ppt; VARR (token_t) * temp_buffer; node_t tree; replace_defined (c2m_ctx, expr_buffer); if (VARR_LENGTH (macro_call_t, macro_call_stack) != 0) error (c2m_ctx, if_token->pos, "#if/#elif inside a macro call"); assert (VARR_LENGTH (token_t, output_buffer) == 0 && !no_out_p); /* macro substitution */ unget_next_pptoken (c2m_ctx, new_token (c2m_ctx, if_token->pos, "", T_EOP, N_IGNORE)); push_back (c2m_ctx, expr_buffer); no_out_p = TRUE; processing (c2m_ctx, TRUE); replace_defined (c2m_ctx, output_buffer); no_out_p = FALSE; reverse_move_tokens (c2m_ctx, expr_buffer, output_buffer); VARR_CREATE (token_t, temp_buffer, VARR_LENGTH (token_t, expr_buffer)); for (i = j = 0; i < VARR_LENGTH (token_t, expr_buffer); i++) { int change_p = TRUE; /* changing PP tokens to tokens and idents to "0" */ ppt = VARR_GET (token_t, expr_buffer, i); t = pptoken2token (c2m_ctx, ppt, FALSE); if (t == NULL || t->code == ' ' || t->code == '\n') continue; if (t->code == T_NUMBER && (t->node->code == N_F || t->node->code == N_D || t->node->code == N_LD)) { error (c2m_ctx, ppt->pos, "floating point in #if/#elif: %s", ppt->repr); } else if (t->code == T_STR) { error (c2m_ctx, ppt->pos, "string in #if/#elif: %s", ppt->repr); } else if (t->code != T_ID) { change_p = FALSE; } if (change_p) t = new_node_token (c2m_ctx, ppt->pos, "0", T_NUMBER, new_ll_node (c2m_ctx, 0, ppt->pos)); VARR_PUSH (token_t, temp_buffer, t); } no_out_p = TRUE; if (VARR_LENGTH (token_t, temp_buffer) != 0) { tree = parse_pre_expr (c2m_ctx, temp_buffer); } else { error (c2m_ctx, if_token->pos, "empty preprocessor expression"); tree = NULL; } no_out_p = FALSE; VARR_DESTROY (token_t, temp_buffer); if (tree == NULL) { struct val val; val.uns_p = FALSE; val.u.i_val = 0; return val; } return eval (c2m_ctx, tree); } static int eval_binop_operands (c2m_ctx_t c2m_ctx, node_t tree, struct val *v1, struct val *v2) { *v1 = eval (c2m_ctx, NL_HEAD (tree->ops)); *v2 = eval (c2m_ctx, NL_EL (tree->ops, 1)); if (v1->uns_p && !v2->uns_p) { v2->uns_p = TRUE; v2->u.u_val = v2->u.i_val; } else if (!v1->uns_p && v2->uns_p) { v1->uns_p = TRUE; v1->u.u_val = v1->u.i_val; } return v1->uns_p; } static struct val eval (c2m_ctx_t c2m_ctx, node_t tree) { int cond; struct val res, v1, v2; #define UNOP(op) \ do { \ v1 = eval (c2m_ctx, NL_HEAD (tree->ops)); \ res = v1; \ if (res.uns_p) \ res.u.u_val = op res.u.u_val; \ else \ res.u.i_val = op res.u.i_val; \ } while (0) #define BINOP(op) \ do { \ res.uns_p = eval_binop_operands (c2m_ctx, tree, &v1, &v2); \ if (res.uns_p) \ res.u.u_val = v1.u.u_val op v2.u.u_val; \ else \ res.u.i_val = v1.u.i_val op v2.u.i_val; \ } while (0) switch (tree->code) { case N_IGNORE: error (c2m_ctx, tree->pos, "wrong preprocessor expression"); res.uns_p = FALSE; res.u.i_val = 0; break; case N_CH: res.uns_p = !char_is_signed_p () || MIR_CHAR_MAX > MIR_INT_MAX; if (res.uns_p) res.u.u_val = tree->u.ch; else res.u.i_val = tree->u.ch; break; case N_I: case N_L: res.uns_p = FALSE; res.u.i_val = tree->u.l; break; case N_LL: res.uns_p = FALSE; res.u.i_val = tree->u.ll; break; case N_U: case N_UL: res.uns_p = TRUE; res.u.u_val = tree->u.ul; break; case N_ULL: res.uns_p = TRUE; res.u.u_val = tree->u.ull; break; case N_BITWISE_NOT: UNOP (~); break; case N_NOT: UNOP (!); break; case N_EQ: BINOP (==); break; case N_NE: BINOP (!=); break; case N_LT: BINOP (<); break; case N_LE: BINOP (<=); break; case N_GT: BINOP (>); break; case N_GE: BINOP (>=); break; case N_ADD: if (NL_EL (tree->ops, 1) == NULL) { UNOP (+); } else { BINOP (+); } break; case N_SUB: if (NL_EL (tree->ops, 1) == NULL) { UNOP (-); } else { BINOP (-); } break; case N_AND: BINOP (&); break; case N_OR: BINOP (|); break; case N_XOR: BINOP (^); break; case N_LSH: BINOP (<<); break; case N_RSH: BINOP (>>); break; case N_MUL: BINOP (*); break; case N_DIV: case N_MOD: { int zero_p; res.uns_p = eval_binop_operands (c2m_ctx, tree, &v1, &v2); if (res.uns_p) { res.u.u_val = ((zero_p = v2.u.u_val == 0) ? 1 : tree->code == N_DIV ? v1.u.u_val / v2.u.u_val : v1.u.u_val % v2.u.u_val); } else { res.u.i_val = ((zero_p = v2.u.i_val == 0) ? 1 : tree->code == N_DIV ? v1.u.i_val / v2.u.i_val : v1.u.i_val % v2.u.i_val); } if (zero_p) error (c2m_ctx, tree->pos, "division (%s) by zero in preporocessor", tree->code == N_DIV ? "/" : "%"); break; } case N_ANDAND: case N_OROR: v1 = eval (c2m_ctx, NL_HEAD (tree->ops)); cond = v1.uns_p ? v1.u.u_val != 0 : v1.u.i_val != 0; if (tree->code == N_ANDAND ? cond : !cond) { v2 = eval (c2m_ctx, NL_EL (tree->ops, 1)); cond = v2.uns_p ? v2.u.u_val != 0 : v2.u.i_val != 0; } res.uns_p = FALSE; res.u.i_val = cond; break; case N_COND: v1 = eval (c2m_ctx, NL_HEAD (tree->ops)); cond = v1.uns_p ? v1.u.u_val != 0 : v1.u.i_val != 0; res = eval (c2m_ctx, NL_EL (tree->ops, cond ? 1 : 2)); break; default: res.uns_p = FALSE; res.u.i_val = 0; assert (FALSE); } return res; } static macro_call_t try_param_macro_call (c2m_ctx_t c2m_ctx, macro_t m, token_t macro_id) { macro_call_t mc; token_t t1 = get_next_pptoken (c2m_ctx), t2 = NULL; if (t1->code == T_EOR) { pop_macro_call (c2m_ctx); t1 = get_next_pptoken (c2m_ctx); } if (t1->code == ' ' || t1->code == '\n') { t2 = t1; t1 = get_next_pptoken (c2m_ctx); } if (t1->code != '(') { /* no args: it is not a macro call */ unget_next_pptoken (c2m_ctx, t1); if (t2 != NULL) unget_next_pptoken (c2m_ctx, t2); out_token (c2m_ctx, macro_id); return NULL; } mc = new_macro_call (m, macro_id->pos); find_args (c2m_ctx, mc); VARR_PUSH (macro_call_t, macro_call_stack, mc); return mc; } static void processing (c2m_ctx_t c2m_ctx, int ignore_directive_p) { token_t t, t1, t2; struct macro macro_struct; macro_t m; macro_call_t mc; int newln_p; for (newln_p = TRUE;;) { /* Main loop. */ t = get_next_pptoken (c2m_ctx); if (t->code == T_EOP) return; /* end of processing */ if (newln_p && !ignore_directive_p && t->code == '#') { process_directive (c2m_ctx); continue; } if (t->code == '\n') { newln_p = TRUE; out_token (c2m_ctx, t); continue; } else if (t->code == ' ') { out_token (c2m_ctx, t); continue; } else if (t->code == T_EOFILE || t->code == T_EOU) { if (VARR_LENGTH (ifstate_t, ifs) > eof_s->ifs_length_at_stream_start) { error (c2m_ctx, VARR_LAST (ifstate_t, ifs)->if_pos, "unfinished #if"); } if (t->code == T_EOU) return; while (VARR_LENGTH (ifstate_t, ifs) > eof_s->ifs_length_at_stream_start) pop_ifstate (c2m_ctx); skip_if_part_p = VARR_LENGTH (ifstate_t, ifs) == 0 ? 0 : VARR_LAST (ifstate_t, ifs)->skip_p; newln_p = TRUE; continue; } else if (skip_if_part_p) { skip_nl (c2m_ctx, t, NULL); newln_p = TRUE; continue; } newln_p = FALSE; if (t->code == T_EOR) { // finish macro call pop_macro_call (c2m_ctx); continue; } else if (t->code == T_EOA) { /* arg end: add the result to repl_buffer */ mc = VARR_LAST (macro_call_t, macro_call_stack); add_arg_tokens (mc->repl_buffer, output_buffer); #ifdef C2MIR_PREPRO_DEBUG fprintf (stderr, "adding processed arg to output buffer\n"); #endif process_replacement (c2m_ctx, mc); continue; } else if (t->code != T_ID) { out_token (c2m_ctx, t); continue; } macro_struct.id = t; if (!HTAB_DO (macro_t, macro_tab, ¯o_struct, HTAB_FIND, m)) { if (!process_pragma (c2m_ctx, t)) out_token (c2m_ctx, t); continue; } if (m->replacement == NULL) { /* standard macro */ if (strcmp (t->repr, "__STDC__") == 0) { out_token (c2m_ctx, new_node_token (c2m_ctx, t->pos, "1", T_NUMBER, new_i_node (c2m_ctx, 1, t->pos))); } else if (strcmp (t->repr, "__STDC_HOSTED__") == 0) { out_token (c2m_ctx, new_node_token (c2m_ctx, t->pos, "1", T_NUMBER, new_i_node (c2m_ctx, 1, t->pos))); } else if (strcmp (t->repr, "__STDC_VERSION__") == 0) { out_token (c2m_ctx, new_node_token (c2m_ctx, t->pos, "201112L", T_NUMBER, new_l_node (c2m_ctx, 201112, t->pos))); // ??? } else if (strcmp (t->repr, "__FILE__") == 0) { stringify (t->pos.fname, temp_string); VARR_PUSH (char, temp_string, '\0'); t = new_node_token (c2m_ctx, t->pos, uniq_cstr (c2m_ctx, VARR_ADDR (char, temp_string)).s, T_STR, new_str_node (c2m_ctx, N_STR, empty_str, t->pos)); set_string_val (c2m_ctx, t, temp_string); out_token (c2m_ctx, t); } else if (strcmp (t->repr, "__LINE__") == 0) { char str[50]; sprintf (str, "%d", t->pos.lno); out_token (c2m_ctx, new_node_token (c2m_ctx, t->pos, uniq_cstr (c2m_ctx, str).s, T_NUMBER, new_i_node (c2m_ctx, t->pos.lno, t->pos))); } else if (strcmp (t->repr, "__DATE__") == 0) { t = new_node_token (c2m_ctx, t->pos, date_str_repr, T_STR, new_str_node (c2m_ctx, N_STR, uniq_cstr (c2m_ctx, date_str), t->pos)); out_token (c2m_ctx, t); } else if (strcmp (t->repr, "__TIME__") == 0) { t = new_node_token (c2m_ctx, t->pos, time_str_repr, T_STR, new_str_node (c2m_ctx, N_STR, uniq_cstr (c2m_ctx, time_str), t->pos)); out_token (c2m_ctx, t); } else if (strcmp (t->repr, "__has_include") == 0) { int res; VARR (token_t) * arg; const char *name; FILE *f; if ((mc = try_param_macro_call (c2m_ctx, m, t)) != NULL) { unget_next_pptoken (c2m_ctx, new_token (c2m_ctx, t->pos, "", T_EOR, N_IGNORE)); if (VARR_LENGTH (token_arr_t, mc->args) != 1) { res = 0; } else { arg = VARR_LAST (token_arr_t, mc->args); if ((name = get_header_name (c2m_ctx, arg, t->pos)) != NULL) { res = ((f = fopen (name, "r")) != NULL && !fclose (f)) ? 1 : 0; } else { error (c2m_ctx, t->pos, "wrong arg of predefined __has_include"); res = 0; } } m->ignore_p = TRUE; unget_next_pptoken (c2m_ctx, new_node_token (c2m_ctx, t->pos, uniq_cstr (c2m_ctx, res ? "1" : "0").s, T_NUMBER, new_i_node (c2m_ctx, res, t->pos))); } } else { assert (FALSE); } continue; } if (m->ignore_p) { t->code = T_NO_MACRO_IDENT; out_token (c2m_ctx, t); continue; } if (m->params == NULL) { /* macro without parameters */ unget_next_pptoken (c2m_ctx, new_token (c2m_ctx, t->pos, "", T_EOR, N_IGNORE)); #ifdef C2MIR_PREPRO_DEBUG fprintf (stderr, "# push back \n"); #endif mc = new_macro_call (m, t->pos); add_tokens (mc->repl_buffer, m->replacement); copy_and_push_back (c2m_ctx, do_concat (c2m_ctx, mc->repl_buffer), mc->pos); m->ignore_p = TRUE; VARR_PUSH (macro_call_t, macro_call_stack, mc); } else if ((mc = try_param_macro_call (c2m_ctx, m, t)) != NULL) { /* macro with parameters */ process_replacement (c2m_ctx, mc); } } } static void pre_text_out (c2m_ctx_t c2m_ctx, token_t t) { /* NULL means end of output */ int i; FILE *f = options->prepro_output_file; if (t == NULL && pre_last_token != NULL && pre_last_token->code == '\n') { fprintf (f, "\n"); return; } if (t->code == '\n') { pre_last_token = t; return; } if (actual_pre_pos.fname != t->pos.fname || actual_pre_pos.lno != t->pos.lno) { if (actual_pre_pos.fname == t->pos.fname && actual_pre_pos.lno < t->pos.lno && actual_pre_pos.lno + 4 >= t->pos.lno) { for (; actual_pre_pos.lno != t->pos.lno; actual_pre_pos.lno++) fprintf (f, "\n"); } else { if (pre_last_token != NULL) fprintf (f, "\n"); fprintf (f, "#line %d", t->pos.lno); if (actual_pre_pos.fname != t->pos.fname) { stringify (t->pos.fname, temp_string); VARR_PUSH (char, temp_string, '\0'); fprintf (f, " %s", VARR_ADDR (char, temp_string)); } fprintf (f, "\n"); } for (i = 0; i < t->pos.ln_pos - 1; i++) fprintf (f, " "); actual_pre_pos = t->pos; } fprintf (f, "%s", t->code == ' ' ? " " : t->repr); pre_last_token = t; } static void pre_out (c2m_ctx_t c2m_ctx, token_t t) { if (t == NULL) { t = new_token (c2m_ctx, pre_last_token == NULL ? no_pos : pre_last_token->pos, "", T_EOFILE, N_IGNORE); } else { assert (t->code != T_EOU && t->code != EOF); pre_last_token = t; if ((t = pptoken2token (c2m_ctx, t, TRUE)) == NULL) return; } if (t->code == T_STR && VARR_LENGTH (token_t, recorded_tokens) != 0 && VARR_LAST (token_t, recorded_tokens)->code == T_STR) { /* concat strings */ token_t last_t = VARR_POP (token_t, recorded_tokens); VARR_TRUNC (char, temp_string, 0); for (const char *s = last_t->repr; *s != 0; s++) VARR_PUSH (char, temp_string, *s); assert (VARR_LAST (char, temp_string) == '"' && t->repr[0] == '"'); VARR_POP (char, temp_string); for (const char *s = &t->repr[1]; *s != 0; s++) VARR_PUSH (char, temp_string, *s); t = last_t; assert (VARR_LAST (char, temp_string) == '"'); VARR_PUSH (char, temp_string, '\0'); t->repr = uniq_cstr (c2m_ctx, VARR_ADDR (char, temp_string)).s; set_string_val (c2m_ctx, t, temp_string); } VARR_PUSH (token_t, recorded_tokens, t); } static void common_pre_out (c2m_ctx_t c2m_ctx, token_t t) { pptokens_num++; (options->prepro_only_p ? pre_text_out : pre_out) (c2m_ctx, t); } static void pre (c2m_ctx_t c2m_ctx, const char *start_source_name) { pre_last_token = NULL; actual_pre_pos.fname = NULL; actual_pre_pos.lno = 0; actual_pre_pos.ln_pos = 0; pre_out_token_func = common_pre_out; pptokens_num = 0; if (!options->no_prepro_p) { processing (c2m_ctx, FALSE); } else { for (;;) { token_t t = get_next_pptoken (c2m_ctx); if (t->code == T_EOFILE || t->code == T_EOU) break; pre_out_token_func (c2m_ctx, t); } } pre_out_token_func (c2m_ctx, NULL); if (options->verbose_p && options->message_file != NULL) fprintf (options->message_file, " preprocessor tokens -- %lu, parse tokens -- %lu\n", pptokens_num, (unsigned long) VARR_LENGTH (token_t, recorded_tokens)); } /* ------------------------- Preprocessor End ------------------------------ */ struct parse_ctx { int record_level; size_t next_token_index; token_t curr_token; node_t curr_scope; }; #define record_level c2m_ctx->parse_ctx->record_level #define next_token_index c2m_ctx->parse_ctx->next_token_index #define next_token_index c2m_ctx->parse_ctx->next_token_index #define curr_token c2m_ctx->parse_ctx->curr_token #define curr_scope c2m_ctx->parse_ctx->curr_scope static struct node err_struct; static const node_t err_node = &err_struct; static void read_token (c2m_ctx_t c2m_ctx) { curr_token = VARR_GET (token_t, recorded_tokens, next_token_index); next_token_index++; } static size_t record_start (c2m_ctx_t c2m_ctx) { assert (next_token_index > 0 && record_level >= 0); record_level++; return next_token_index - 1; } static void record_stop (c2m_ctx_t c2m_ctx, size_t mark, int restore_p) { assert (record_level > 0); record_level--; if (!restore_p) return; next_token_index = mark; read_token (c2m_ctx); } static void syntax_error (c2m_ctx_t c2m_ctx, const char *expected_name) { FILE *f; if ((f = options->message_file) == NULL) return; print_pos (f, curr_token->pos, TRUE); fprintf (f, "syntax error on %s", get_token_name (c2m_ctx, curr_token->code)); fprintf (f, " (expected '%s'):", expected_name); #if 0 { static const int context_len = 5; for (int i = 0; i < context_len && curr_token->code != T_EOFILE; i++) { fprintf (f, " %s", curr_token->repr); } } #endif fprintf (f, "\n"); n_errors++; } typedef struct { node_t id, scope; } tpname_t; DEF_HTAB (tpname_t); static HTAB (tpname_t) * tpname_tab; static int tpname_eq (tpname_t tpname1, tpname_t tpname2, void *arg) { return tpname1.id->u.s.s == tpname2.id->u.s.s && tpname1.scope == tpname2.scope; } static htab_hash_t tpname_hash (tpname_t tpname, void *arg) { return (mir_hash_finish ( mir_hash_step (mir_hash_step (mir_hash_init (0x42), (uint64_t) tpname.id->u.s.s), (uint64_t) tpname.scope))); } static void tpname_init (void) { HTAB_CREATE (tpname_t, tpname_tab, 1000, tpname_hash, tpname_eq, NULL); } static int tpname_find (node_t id, node_t scope, tpname_t *res) { int found_p; tpname_t el, tpname; tpname.id = id; tpname.scope = scope; found_p = HTAB_DO (tpname_t, tpname_tab, tpname, HTAB_FIND, el); if (res != NULL && found_p) *res = el; return found_p; } static tpname_t tpname_add (node_t id, node_t scope) { tpname_t el, tpname; tpname.id = id; tpname.scope = scope; if (HTAB_DO (tpname_t, tpname_tab, tpname, HTAB_FIND, el)) return el; HTAB_DO (tpname_t, tpname_tab, tpname, HTAB_INSERT, el); return el; } static void tpname_finish (void) { if (tpname_tab != NULL) HTAB_DESTROY (tpname_t, tpname_tab); } #define P(f) \ do { \ if ((r = (f) (c2m_ctx, no_err_p)) == err_node) return r; \ } while (0) #define PA(f, a) \ do { \ if ((r = (f) (c2m_ctx, no_err_p, a)) == err_node) return r; \ } while (0) #define PTFAIL(t) \ do { \ if (record_level == 0) syntax_error (c2m_ctx, get_token_name (c2m_ctx, t)); \ return err_node; \ } while (0) #define PT(t) \ do { \ if (!M (t)) PTFAIL (t); \ } while (0) #define PTP(t, pos) \ do { \ if (!MP (t, pos)) PTFAIL (t); \ } while (0) #define PTN(t) \ do { \ if (!MN (t, r)) PTFAIL (t); \ } while (0) #define PE(f, l) \ do { \ if ((r = (f) (c2m_ctx, no_err_p)) == err_node) goto l; \ } while (0) #define PAE(f, a, l) \ do { \ if ((r = (f) (c2m_ctx, no_err_p, a)) == err_node) goto l; \ } while (0) #define PTE(t, pos, l) \ do { \ if (!MP (t, pos)) goto l; \ } while (0) typedef node_t (*nonterm_func_t) (c2m_ctx_t c2m_ctx, int); typedef node_t (*nonterm_arg_func_t) (c2m_ctx_t c2m_ctx, int, node_t); #define D(f) static node_t f (c2m_ctx_t c2m_ctx, int no_err_p) #define DA(f) static node_t f (c2m_ctx_t c2m_ctx, int no_err_p, node_t arg) #define C(c) (curr_token->code == c) static int match (c2m_ctx_t c2m_ctx, int c, pos_t *pos, node_code_t *node_code, node_t *node) { if (curr_token->code != c) return FALSE; if (pos != NULL) *pos = curr_token->pos; if (node_code != NULL) *node_code = curr_token->node_code; if (node != NULL) *node = curr_token->node; read_token (c2m_ctx); return TRUE; } #define M(c) match (c2m_ctx, c, NULL, NULL, NULL) #define MP(c, pos) match (c2m_ctx, c, &(pos), NULL, NULL) #define MC(c, pos, code) match (c2m_ctx, c, &(pos), &(code), NULL) #define MN(c, node) match (c2m_ctx, c, NULL, NULL, &(node)) static node_t try_f (c2m_ctx_t c2m_ctx, nonterm_func_t f) { size_t mark = record_start (c2m_ctx); node_t r = (f) (c2m_ctx, TRUE); record_stop (c2m_ctx, mark, r == err_node); return r; } static node_t try_arg_f (c2m_ctx_t c2m_ctx, nonterm_arg_func_t f, node_t arg) { size_t mark = record_start (c2m_ctx); node_t r = (f) (c2m_ctx, TRUE, arg); record_stop (c2m_ctx, mark, r == err_node); return r; } #define TRY(f) try_f (c2m_ctx, f) #define TRY_A(f, arg) try_arg_f (c2m_ctx, f, arg) /* Expressions: */ D (type_name); D (expr); D (assign_expr); D (initializer_list); D (par_type_name) { node_t r; PT ('('); P (type_name); PT (')'); return r; } D (primary_expr) { node_t r, n, op, gn, list; pos_t pos; if (MN (T_ID, r) || MN (T_NUMBER, r) || MN (T_CH, r) || MN (T_STR, r)) { return r; } else if (M ('(')) { P (expr); if (M (')')) return r; } else if (MP (T_GENERIC, pos)) { PT ('('); P (assign_expr); PT (','); list = new_node (c2m_ctx, N_LIST); n = new_pos_node2 (c2m_ctx, N_GENERIC, pos, r, list); for (;;) { /* generic-assoc-list , generic-association */ if (MP (T_DEFAULT, pos)) { op = new_node (c2m_ctx, N_IGNORE); } else { P (type_name); op = r; pos = op->pos; } PT (':'); P (assign_expr); gn = new_pos_node2 (c2m_ctx, N_GENERIC_ASSOC, pos, op, r); op_append (list, gn); if (!M (',')) break; } PT (')'); return n; } return err_node; } DA (post_expr_part) { node_t r, n, op, list; node_code_t code; pos_t pos; r = arg; for (;;) { if (MC (T_INCDEC, pos, code)) { code = code == N_INC ? N_POST_INC : N_POST_DEC; op = r; r = NULL; } else if (MC ('.', pos, code) || MC (T_ARROW, pos, code)) { op = r; if (!MN (T_ID, r)) return err_node; } else if (MC ('[', pos, code)) { op = r; P (expr); PT (']'); } else if (!MP ('(', pos)) { break; } else { op = r; r = NULL; code = N_CALL; list = new_node (c2m_ctx, N_LIST); if (!C (')')) { for (;;) { P (assign_expr); op_append (list, r); if (!M (',')) break; } } r = list; PT (')'); } n = new_pos_node1 (c2m_ctx, code, pos, op); if (r != NULL) op_append (n, r); r = n; } return r; } D (post_expr) { node_t r; P (primary_expr); PA (post_expr_part, r); return r; } D (unary_expr) { node_t r, t; node_code_t code; pos_t pos; if ((r = TRY (par_type_name)) != err_node) { t = r; if (!MP ('{', pos)) { P (unary_expr); r = new_node2 (c2m_ctx, N_CAST, t, r); } else { P (initializer_list); if (!M ('}')) return err_node; r = new_pos_node2 (c2m_ctx, N_COMPOUND_LITERAL, pos, t, r); PA (post_expr_part, r); } return r; } else if (MP (T_SIZEOF, pos)) { if ((r = TRY (par_type_name)) != err_node) { r = new_pos_node1 (c2m_ctx, N_SIZEOF, pos, r); return r; } code = N_EXPR_SIZEOF; } else if (MP (T_ALIGNOF, pos)) { if ((r = TRY (par_type_name)) != err_node) { r = new_pos_node1 (c2m_ctx, N_ALIGNOF, pos, r); } else { P (unary_expr); /* error recovery */ r = new_pos_node1 (c2m_ctx, N_ALIGNOF, pos, new_node (c2m_ctx, N_IGNORE)); } return r; } else if (!MC (T_INCDEC, pos, code) && !MC (T_UNOP, pos, code) && !MC (T_ADDOP, pos, code) && !MC ('*', pos, code) && !MC ('&', pos, code)) { P (post_expr); return r; } else if (code == N_AND) { code = N_ADDR; } else if (code == N_MUL) { code = N_DEREF; } P (unary_expr); r = new_pos_node1 (c2m_ctx, code, pos, r); return r; } static node_t left_op (c2m_ctx_t c2m_ctx, int no_err_p, int token, int token2, nonterm_func_t f) { node_code_t code; node_t r, n; pos_t pos; P (f); while (MC (token, pos, code) || (token2 >= 0 && MC (token2, pos, code))) { n = new_pos_node1 (c2m_ctx, code, pos, r); P (f); op_append (n, r); r = n; } return r; } static node_t right_op (c2m_ctx_t c2m_ctx, int no_err_p, int token, int token2, nonterm_func_t left, nonterm_func_t right) { node_code_t code; node_t r, n; pos_t pos; P (left); if (MC (token, pos, code) || (token2 >= 0 && MC (token2, pos, code))) { n = new_pos_node1 (c2m_ctx, code, pos, r); P (right); op_append (n, r); r = n; } return r; } D (mul_expr) { return left_op (c2m_ctx, no_err_p, T_DIVOP, '*', unary_expr); } D (add_expr) { return left_op (c2m_ctx, no_err_p, T_ADDOP, -1, mul_expr); } D (sh_expr) { return left_op (c2m_ctx, no_err_p, T_SH, -1, add_expr); } D (rel_expr) { return left_op (c2m_ctx, no_err_p, T_CMP, -1, sh_expr); } D (eq_expr) { return left_op (c2m_ctx, no_err_p, T_EQNE, -1, rel_expr); } D (and_expr) { return left_op (c2m_ctx, no_err_p, '&', -1, eq_expr); } D (xor_expr) { return left_op (c2m_ctx, no_err_p, '^', -1, and_expr); } D (or_expr) { return left_op (c2m_ctx, no_err_p, '|', -1, xor_expr); } D (land_expr) { return left_op (c2m_ctx, no_err_p, T_ANDAND, -1, or_expr); } D (lor_expr) { return left_op (c2m_ctx, no_err_p, T_OROR, -1, land_expr); } D (cond_expr) { node_t r, n; pos_t pos; P (lor_expr); if (!MP ('?', pos)) return r; n = new_pos_node1 (c2m_ctx, N_COND, pos, r); P (expr); op_append (n, r); if (!M (':')) return err_node; P (cond_expr); op_append (n, r); return n; } #define const_expr cond_expr D (assign_expr) { return right_op (c2m_ctx, no_err_p, T_ASSIGN, '=', cond_expr, assign_expr); } D (expr) { return right_op (c2m_ctx, no_err_p, ',', -1, assign_expr, expr); } /* Declarations: */ D (attr_spec); DA (declaration_specs); D (sc_spec); DA (type_spec); D (struct_declaration_list); D (struct_declaration); D (spec_qual_list); D (type_qual); D (func_spec); D (align_spec); D (declarator); D (direct_declarator); D (pointer); D (type_qual_list); D (param_type_list); D (id_list); D (abstract_declarator); D (direct_abstract_declarator); D (typedef_name); D (initializer); D (st_assert); D (asm_spec) { node_t r; PTN (T_ID); if (strcmp (r->u.s.s, "__asm") != 0) PTFAIL (T_ID); PT ('('); while (!C (')')) { PT (T_STR); } PT (')'); return NULL; } static node_t try_attr_spec (c2m_ctx_t c2m_ctx, pos_t pos) { node_t r; if (options->pedantic_p) return NULL; if ((r = TRY (attr_spec)) != err_node) { if (options->pedantic_p) error (c2m_ctx, pos, "GCC attributes are not implemented"); else /*warning (c2m_ctx, pos, "GCC attributes are not implemented -- ignoring them")*/; } else if ((r = TRY (asm_spec)) != err_node) { if (options->pedantic_p) error (c2m_ctx, pos, "asm is not implemented"); else /*warning (c2m_ctx, pos, "asm is not implemented -- ignoring it")*/; } return r; } D (declaration) { int typedef_p; node_t op, list, decl, spec, r; pos_t pos, last_pos; if (C (T_STATIC_ASSERT)) { P (st_assert); } else if (MP (';', pos)) { r = new_node (c2m_ctx, N_LIST); if (curr_scope == top_scope && options->pedantic_p) warning (c2m_ctx, pos, "extra ; outside of a function"); } else { try_attr_spec (c2m_ctx, curr_token->pos); PA (declaration_specs, curr_scope == top_scope ? (node_t) 1 : NULL); spec = r; last_pos = spec->pos; list = new_node (c2m_ctx, N_LIST); if (C (';')) { op_append (list, new_node3 (c2m_ctx, N_SPEC_DECL, spec, new_node (c2m_ctx, N_IGNORE), new_node (c2m_ctx, N_IGNORE))); } else { /* init-declarator-list */ for (op = NL_HEAD (spec->ops); op != NULL; op = NL_NEXT (op)) if (op->code == N_TYPEDEF) break; typedef_p = op != NULL; for (;;) { /* init-declarator */ P (declarator); decl = r; last_pos = decl->pos; assert (decl->code == N_DECL); if (typedef_p) { op = NL_HEAD (decl->ops); tpname_add (op, curr_scope); } try_attr_spec (c2m_ctx, last_pos); if (M ('=')) { P (initializer); } else { r = new_node (c2m_ctx, N_IGNORE); } op_append (list, new_pos_node3 (c2m_ctx, N_SPEC_DECL, decl->pos, new_node1 (c2m_ctx, N_SHARE, spec), decl, r)); if (!M (',')) break; } } r = list; PT (';'); } return r; } D (attr) { if (C (')') || C (',')) /* empty */ return NULL; if (FIRST_KW <= curr_token->code && curr_token->code <= LAST_KW) PT (curr_token->code); else PT (T_ID); if (C ('(')) { PT ('('); while (!C (')')) { if (C (T_NUMBER) || C (T_CH) || C (T_STR)) PT (curr_token->code); else PT (T_ID); if (!C (')')) PT (','); } PT (')'); } return NULL; } D (attr_spec) { node_t r; PTN (T_ID); if (strcmp (r->u.s.s, "__attribute__") != 0) PTFAIL (T_ID); PT ('('); PT ('('); for (;;) { P (attr); if (C (')')) break; PT (','); } PT (')'); PT (')'); return NULL; } DA (declaration_specs) { node_t list, r, prev_type_spec = NULL; int first_p; pos_t pos = curr_token->pos, spec_pos; list = new_node (c2m_ctx, N_LIST); for (first_p = arg == NULL;; first_p = FALSE) { spec_pos = curr_token->pos; if (C (T_ALIGNAS)) { P (align_spec); } else if ((r = TRY (sc_spec)) != err_node) { } else if ((r = TRY (type_qual)) != err_node) { } else if ((r = TRY (func_spec)) != err_node) { } else if (first_p) { PA (type_spec, prev_type_spec); prev_type_spec = r; } else if ((r = TRY_A (type_spec, prev_type_spec)) != err_node) { prev_type_spec = r; } else if ((r = try_attr_spec (c2m_ctx, spec_pos)) != err_node) { continue; } else break; op_append (list, r); } if (prev_type_spec == NULL && arg != NULL) { if (options->pedantic_p) warning (c2m_ctx, pos, "type defaults to int"); r = new_pos_node (c2m_ctx, N_INT, pos); op_append (list, r); } return list; } D (sc_spec) { node_t r; pos_t pos; if (MP (T_TYPEDEF, pos)) { r = new_pos_node (c2m_ctx, N_TYPEDEF, pos); } else if (MP (T_EXTERN, pos)) { r = new_pos_node (c2m_ctx, N_EXTERN, pos); } else if (MP (T_STATIC, pos)) { r = new_pos_node (c2m_ctx, N_STATIC, pos); } else if (MP (T_AUTO, pos)) { r = new_pos_node (c2m_ctx, N_AUTO, pos); } else if (MP (T_REGISTER, pos)) { r = new_pos_node (c2m_ctx, N_REGISTER, pos); } else if (MP (T_THREAD_LOCAL, pos)) { r = new_pos_node (c2m_ctx, N_THREAD_LOCAL, pos); } else { if (record_level == 0) syntax_error (c2m_ctx, "a storage specifier"); return err_node; } return r; } DA (type_spec) { node_t op1, op2, op3, op4, r; int struct_p, id_p = FALSE; pos_t pos; if (MP (T_VOID, pos)) { r = new_pos_node (c2m_ctx, N_VOID, pos); } else if (MP (T_CHAR, pos)) { r = new_pos_node (c2m_ctx, N_CHAR, pos); } else if (MP (T_SHORT, pos)) { r = new_pos_node (c2m_ctx, N_SHORT, pos); } else if (MP (T_INT, pos)) { r = new_pos_node (c2m_ctx, N_INT, pos); } else if (MP (T_LONG, pos)) { r = new_pos_node (c2m_ctx, N_LONG, pos); } else if (MP (T_FLOAT, pos)) { r = new_pos_node (c2m_ctx, N_FLOAT, pos); } else if (MP (T_DOUBLE, pos)) { r = new_pos_node (c2m_ctx, N_DOUBLE, pos); } else if (MP (T_SIGNED, pos)) { r = new_pos_node (c2m_ctx, N_SIGNED, pos); } else if (MP (T_UNSIGNED, pos)) { r = new_pos_node (c2m_ctx, N_UNSIGNED, pos); } else if (MP (T_BOOL, pos)) { r = new_pos_node (c2m_ctx, N_BOOL, pos); } else if (MP (T_COMPLEX, pos)) { if (record_level == 0) error (c2m_ctx, pos, "complex numbers are not supported"); return err_node; } else if (MP (T_ATOMIC, pos)) { /* atomic-type-specifier */ PT ('('); P (type_name); PT (')'); error (c2m_ctx, pos, "Atomic types are not supported"); } else if ((struct_p = MP (T_STRUCT, pos)) || MP (T_UNION, pos)) { /* struct-or-union-specifier, struct-or-union */ if (!MN (T_ID, op1)) { op1 = new_node (c2m_ctx, N_IGNORE); } else { id_p = TRUE; } if (M ('{')) { if (!C ('}')) { P (struct_declaration_list); } else { (options->pedantic_p ? error : warning) (c2m_ctx, pos, "empty struct/union"); r = new_node (c2m_ctx, N_LIST); } PT ('}'); } else if (!id_p) { return err_node; } else { r = new_node (c2m_ctx, N_IGNORE); } r = new_pos_node2 (c2m_ctx, struct_p ? N_STRUCT : N_UNION, pos, op1, r); } else if (MP (T_ENUM, pos)) { /* enum-specifier */ if (!MN (T_ID, op1)) { op1 = new_node (c2m_ctx, N_IGNORE); } else { id_p = TRUE; } op2 = new_node (c2m_ctx, N_LIST); if (M ('{')) { /* enumerator-list */ for (;;) { /* enumerator */ PTN (T_ID); op3 = r; /* enumeration-constant */ if (!M ('=')) { op4 = new_node (c2m_ctx, N_IGNORE); } else { P (const_expr); op4 = r; } op_append (op2, new_node2 (c2m_ctx, N_ENUM_CONST, op3, op4)); if (!M (',')) break; if (C ('}')) break; } PT ('}'); } else if (!id_p) { return err_node; } else { op2 = new_node (c2m_ctx, N_IGNORE); } r = new_pos_node2 (c2m_ctx, N_ENUM, pos, op1, op2); } else if (arg == NULL) { P (typedef_name); } else { r = err_node; } return r; } D (struct_declaration_list) { node_t r, res, el, next_el; res = new_node (c2m_ctx, N_LIST); for (;;) { P (struct_declaration); if (r->code != N_LIST) { op_append (res, r); } else { for (el = NL_HEAD (r->ops); el != NULL; el = next_el) { next_el = NL_NEXT (el); NL_REMOVE (r->ops, el); op_append (res, el); } } if (C ('}')) break; } return res; } D (struct_declaration) { node_t list, spec, op, r; if (C (T_STATIC_ASSERT)) { P (st_assert); } else { P (spec_qual_list); spec = r; list = new_node (c2m_ctx, N_LIST); if (M (';')) { op = new_pos_node3 (c2m_ctx, N_MEMBER, spec->pos, new_node1 (c2m_ctx, N_SHARE, spec), new_node (c2m_ctx, N_IGNORE), new_node (c2m_ctx, N_IGNORE)); op_append (list, op); } else { /* struct-declarator-list */ for (;;) { /* struct-declarator */ if (!C (':')) { P (declarator); op = r; } else { op = new_node (c2m_ctx, N_IGNORE); } if (M (':')) { P (const_expr); } else { r = new_node (c2m_ctx, N_IGNORE); } op = new_pos_node3 (c2m_ctx, N_MEMBER, op->pos, new_node1 (c2m_ctx, N_SHARE, spec), op, r); op_append (list, op); if (!M (',')) break; } PT (';'); } r = list; } return r; } D (spec_qual_list) { node_t list, op, r, arg = NULL; int first_p; list = new_node (c2m_ctx, N_LIST); for (first_p = TRUE;; first_p = FALSE) { if (C (T_CONST) || C (T_RESTRICT) || C (T_VOLATILE) || C (T_ATOMIC)) { P (type_qual); op = r; } else if ((op = TRY_A (type_spec, arg)) != err_node) { arg = op; } else if (first_p) { return err_node; } else { break; } op_append (list, op); } return list; } D (type_qual) { node_t r; pos_t pos; if (MP (T_CONST, pos)) { r = new_pos_node (c2m_ctx, N_CONST, pos); } else if (MP (T_RESTRICT, pos)) { r = new_pos_node (c2m_ctx, N_RESTRICT, pos); } else if (MP (T_VOLATILE, pos)) { r = new_pos_node (c2m_ctx, N_VOLATILE, pos); } else if (MP (T_ATOMIC, pos)) { r = new_pos_node (c2m_ctx, N_ATOMIC, pos); } else { if (record_level == 0) syntax_error (c2m_ctx, "a type qualifier"); r = err_node; } return r; } D (func_spec) { node_t r; pos_t pos; if (MP (T_INLINE, pos)) { r = new_pos_node (c2m_ctx, N_INLINE, pos); } else if (MP (T_NO_RETURN, pos)) { r = new_pos_node (c2m_ctx, N_NO_RETURN, pos); } else { if (record_level == 0) syntax_error (c2m_ctx, "a function specifier"); r = err_node; } return r; } D (align_spec) { node_t r; pos_t pos; PTP (T_ALIGNAS, pos); PT ('('); if ((r = TRY (type_name)) != err_node) { } else { P (const_expr); } PT (')'); r = new_pos_node1 (c2m_ctx, N_ALIGNAS, pos, r); return r; } D (declarator) { node_t list, p = NULL, r, el, next_el; if (C ('*')) { P (pointer); p = r; } P (direct_declarator); if (p != NULL) { list = NL_NEXT (NL_HEAD (r->ops)); assert (list->code == N_LIST); for (el = NL_HEAD (p->ops); el != NULL; el = next_el) { next_el = NL_NEXT (el); NL_REMOVE (p->ops, el); op_append (list, el); } } return r; } D (direct_declarator) { node_t list, tql, ae, res, r; pos_t pos, static_pos; if (MN (T_ID, r)) { res = new_node2 (c2m_ctx, N_DECL, r, new_node (c2m_ctx, N_LIST)); } else if (M ('(')) { P (declarator); res = r; PT (')'); } else { return err_node; } list = NL_NEXT (NL_HEAD (res->ops)); assert (list->code == N_LIST); for (;;) { if (MP ('(', pos)) { if ((r = TRY (param_type_list)) != err_node) { } else { P (id_list); } PT (')'); op_append (list, new_pos_node1 (c2m_ctx, N_FUNC, pos, r)); } else if (M ('[')) { int static_p = FALSE; if (MP (T_STATIC, static_pos)) { static_p = TRUE; } if (!C (T_CONST) && !C (T_RESTRICT) && !C (T_VOLATILE) && !C (T_ATOMIC)) { tql = new_node (c2m_ctx, N_LIST); } else { P (type_qual_list); tql = r; if (!static_p && M (T_STATIC)) { static_p = TRUE; } } if (static_p) { P (assign_expr); ae = r; } else if (MP ('*', pos)) { ae = new_pos_node (c2m_ctx, N_STAR, pos); } else if (!C (']')) { P (assign_expr); ae = r; } else { ae = new_node (c2m_ctx, N_IGNORE); } PT (']'); op_append (list, new_node3 (c2m_ctx, N_ARR, static_p ? new_pos_node (c2m_ctx, N_STATIC, static_pos) : new_node (c2m_ctx, N_IGNORE), tql, ae)); } else break; } return res; } D (pointer) { node_t op, r; pos_t pos; PTP ('*', pos); if (C (T_CONST) || C (T_RESTRICT) || C (T_VOLATILE) || C (T_ATOMIC)) { P (type_qual_list); } else { r = new_node (c2m_ctx, N_LIST); } op = new_pos_node1 (c2m_ctx, N_POINTER, pos, r); if (C ('*')) { P (pointer); } else { r = new_node (c2m_ctx, N_LIST); } op_append (r, op); return r; } D (type_qual_list) { node_t list, r; list = new_node (c2m_ctx, N_LIST); for (;;) { P (type_qual); op_append (list, r); if (!C (T_CONST) && !C (T_RESTRICT) && !C (T_VOLATILE) && !C (T_ATOMIC)) break; } return list; } D (param_type_abstract_declarator) { node_t r = err_node; P (abstract_declarator); if (C (',') || C (')')) return r; return err_node; } D (param_type_list) { node_t list, op1, op2, r = err_node; int comma_p; pos_t pos; list = new_node (c2m_ctx, N_LIST); for (;;) { /* parameter-list, parameter-declaration */ PA (declaration_specs, NULL); op1 = r; if (C (',') || C (')')) { r = new_node2 (c2m_ctx, N_TYPE, op1, new_node2 (c2m_ctx, N_DECL, new_node (c2m_ctx, N_IGNORE), new_node (c2m_ctx, N_LIST))); } else if ((op2 = TRY (param_type_abstract_declarator)) != err_node) { /* Try param_type_abstract_declarator first for possible func type case (" ()") which can conflict with declarator (" ()") */ r = new_node2 (c2m_ctx, N_TYPE, op1, op2); } else { P (declarator); r = new_pos_node3 (c2m_ctx, N_SPEC_DECL, op2->pos, op1, r, new_node (c2m_ctx, N_IGNORE)); } op_append (list, r); comma_p = FALSE; if (!M (',')) break; comma_p = TRUE; if (C (T_DOTS)) break; } if (comma_p) { PTP (T_DOTS, pos); op_append (list, new_pos_node (c2m_ctx, N_DOTS, pos)); } return list; } D (id_list) { node_t list, r; list = new_node (c2m_ctx, N_LIST); if (C (')')) return list; for (;;) { PTN (T_ID); op_append (list, r); if (!M (',')) break; } return list; } D (abstract_declarator) { node_t list, p = NULL, r, el, next_el; if (C ('*')) { P (pointer); p = r; if ((r = TRY (direct_abstract_declarator)) == err_node) r = new_pos_node2 (c2m_ctx, N_DECL, p->pos, new_node (c2m_ctx, N_IGNORE), new_node (c2m_ctx, N_LIST)); } else { P (direct_abstract_declarator); } if (p != NULL) { list = NL_NEXT (NL_HEAD (r->ops)); assert (list->code == N_LIST); for (el = NL_HEAD (p->ops); el != NULL; el = next_el) { next_el = NL_NEXT (el); NL_REMOVE (p->ops, el); op_append (list, el); } } return r; } D (par_abstract_declarator) { node_t r; PT ('('); P (abstract_declarator); PT (')'); return r; } D (direct_abstract_declarator) { node_t res, list, tql, ae, r; pos_t pos, pos2 = no_pos; if ((res = TRY (par_abstract_declarator)) != err_node) { if (!C ('(') && !C ('[')) return res; } else { res = new_node2 (c2m_ctx, N_DECL, new_node (c2m_ctx, N_IGNORE), new_node (c2m_ctx, N_LIST)); } list = NL_NEXT (NL_HEAD (res->ops)); assert (list->code == N_LIST); for (;;) { if (MP ('(', pos)) { P (param_type_list); PT (')'); op_append (list, new_pos_node1 (c2m_ctx, N_FUNC, pos, r)); } else { PTP ('[', pos); if (MP ('*', pos2)) { r = new_pos_node3 (c2m_ctx, N_ARR, pos, new_node (c2m_ctx, N_IGNORE), new_node (c2m_ctx, N_IGNORE), new_pos_node (c2m_ctx, N_STAR, pos2)); } else { int static_p = FALSE; if (MP (T_STATIC, pos2)) { static_p = TRUE; } if (!C (T_CONST) && !C (T_RESTRICT) && !C (T_VOLATILE) && !C (T_ATOMIC)) { tql = new_node (c2m_ctx, N_LIST); } else { P (type_qual_list); tql = r; if (!static_p && M (T_STATIC)) { static_p = TRUE; } } if (!C (']')) { P (assign_expr); ae = r; } else { ae = new_node (c2m_ctx, N_IGNORE); } r = new_pos_node3 (c2m_ctx, N_ARR, pos, static_p ? new_pos_node (c2m_ctx, N_STATIC, pos2) : new_node (c2m_ctx, N_IGNORE), tql, ae); } PT (']'); op_append (list, r); } if (!C ('(') && !C ('[')) break; } add_pos (res, list->pos); return res; } D (typedef_name) { node_t scope, r; PTN (T_ID); for (scope = curr_scope;; scope = scope->u.scope) { if (tpname_find (r, scope, NULL)) return r; if (scope == NULL) break; } return err_node; } D (initializer) { node_t r; if (!M ('{')) { P (assign_expr); } else { P (initializer_list); if (M (',')) ; PT ('}'); } return r; } D (initializer_list) { node_t list, list2, r; int first_p; list = new_node (c2m_ctx, N_LIST); for (;;) { /* designation */ list2 = new_node (c2m_ctx, N_LIST); for (first_p = TRUE;; first_p = FALSE) { /* designator-list, designator */ if (M ('[')) { P (const_expr); PT (']'); } else if (M ('.')) { PTN (T_ID); r = new_node1 (c2m_ctx, N_FIELD_ID, r); } else break; op_append (list2, r); } if (!first_p) { PT ('='); } P (initializer); op_append (list, new_node2 (c2m_ctx, N_INIT, list2, r)); if (!M (',')) break; if (C ('}')) break; } return list; } D (type_name) { node_t op, r; P (spec_qual_list); op = r; if (!C (')') && !C (':')) { P (abstract_declarator); } else { r = new_pos_node2 (c2m_ctx, N_DECL, op->pos, new_node (c2m_ctx, N_IGNORE), new_node (c2m_ctx, N_LIST)); } return new_node2 (c2m_ctx, N_TYPE, op, r); } D (st_assert) { node_t op1, r; pos_t pos; PTP (T_STATIC_ASSERT, pos); PT ('('); P (const_expr); op1 = r; PT (','); PTN (T_STR); PT (')'); PT (';'); r = new_pos_node2 (c2m_ctx, N_ST_ASSERT, pos, op1, r); return r; } /* Statements: */ D (compound_stmt); D (label) { node_t r, n; pos_t pos; if (MP (T_CASE, pos)) { P (expr); n = new_pos_node1 (c2m_ctx, N_CASE, pos, r); if (M (T_DOTS)) { P (expr); op_append (n, r); } r = n; } else if (MP (T_DEFAULT, pos)) { r = new_pos_node (c2m_ctx, N_DEFAULT, pos); } else { PTN (T_ID); r = new_node1 (c2m_ctx, N_LABEL, r); } PT (':'); return r; } D (stmt) { node_t l, n, op1, op2, op3, r; pos_t pos; l = new_node (c2m_ctx, N_LIST); while ((op1 = TRY (label)) != err_node) { op_append (l, op1); } if (C ('{')) { P (compound_stmt); if (NL_HEAD (l->ops) != NULL) { /* replace empty label list */ assert (NL_HEAD (r->ops)->code == N_LIST && NL_HEAD (NL_HEAD (r->ops)->ops) == NULL); NL_REMOVE (r->ops, NL_HEAD (r->ops)); NL_PREPEND (r->ops, l); } } else if (MP (T_IF, pos)) { /* selection-statement */ PT ('('); P (expr); op1 = r; PT (')'); P (stmt); op2 = r; if (!M (T_ELSE)) { r = new_node (c2m_ctx, N_IGNORE); } else { P (stmt); } r = new_pos_node4 (c2m_ctx, N_IF, pos, l, op1, op2, r); } else if (MP (T_SWITCH, pos)) { /* selection-statement */ PT ('('); P (expr); op1 = r; PT (')'); P (stmt); r = new_pos_node3 (c2m_ctx, N_SWITCH, pos, l, op1, r); } else if (MP (T_WHILE, pos)) { /* iteration-statement */ PT ('('); P (expr); op1 = r; PT (')'); P (stmt); r = new_pos_node3 (c2m_ctx, N_WHILE, pos, l, op1, r); } else if (M (T_DO)) { /* iteration-statement */ P (stmt); op1 = r; PTP (T_WHILE, pos); PT ('('); P (expr); PT (')'); PT (';'); r = new_pos_node3 (c2m_ctx, N_DO, pos, l, r, op1); } else if (MP (T_FOR, pos)) { /* iteration-statement */ PT ('('); n = new_pos_node (c2m_ctx, N_FOR, pos); n->u.scope = curr_scope; curr_scope = n; if ((r = TRY (declaration)) != err_node) { op1 = r; curr_scope = n->u.scope; } else { curr_scope = n->u.scope; if (!M (';')) { P (expr); op1 = r; PT (';'); } else { op1 = new_node (c2m_ctx, N_IGNORE); } } if (M (';')) { op2 = new_node (c2m_ctx, N_IGNORE); } else { P (expr); op2 = r; PT (';'); } if (C (')')) { op3 = new_node (c2m_ctx, N_IGNORE); } else { P (expr); op3 = r; } PT (')'); P (stmt); op_append (n, l); op_append (n, op1); op_append (n, op2); op_append (n, op3); op_append (n, r); r = n; } else if (MP (T_GOTO, pos)) { /* jump-statement */ PTN (T_ID); PT (';'); r = new_pos_node2 (c2m_ctx, N_GOTO, pos, l, r); } else if (MP (T_CONTINUE, pos)) { /* continue-statement */ PT (';'); r = new_pos_node1 (c2m_ctx, N_CONTINUE, pos, l); } else if (MP (T_BREAK, pos)) { /* break-statement */ PT (';'); r = new_pos_node1 (c2m_ctx, N_BREAK, pos, l); } else if (MP (T_RETURN, pos)) { /* return-statement */ if (M (';')) { r = new_node (c2m_ctx, N_IGNORE); } else { P (expr); PT (';'); } r = new_pos_node2 (c2m_ctx, N_RETURN, pos, l, r); } else { /* expression-statement */ if (C (';')) { r = new_node (c2m_ctx, N_IGNORE); } else { P (expr); } PT (';'); r = new_pos_node2 (c2m_ctx, N_EXPR, r->pos, l, r); } return r; } static void error_recovery (c2m_ctx_t c2m_ctx, int par_lev, const char *expected) { syntax_error (c2m_ctx, expected); if (options->debug_p) fprintf (stderr, "error recovery: skipping"); for (;;) { if (curr_token->code == T_EOFILE || (par_lev == 0 && curr_token->code == ';')) break; if (curr_token->code == '{') { par_lev++; } else if (curr_token->code == '}') { if (--par_lev <= 0) break; } if (options->debug_p) fprintf (stderr, " %s(%d:%d)", get_token_name (c2m_ctx, curr_token->code), curr_token->pos.lno, curr_token->pos.ln_pos); read_token (c2m_ctx); } if (options->debug_p) fprintf (stderr, " %s\n", get_token_name (c2m_ctx, curr_token->code)); if (curr_token->code != T_EOFILE) read_token (c2m_ctx); } D (compound_stmt) { node_t n, list, r; pos_t pos; PTE ('{', pos, err0); list = new_node (c2m_ctx, N_LIST); n = new_pos_node2 (c2m_ctx, N_BLOCK, pos, new_node (c2m_ctx, N_LIST), list); n->u.scope = curr_scope; curr_scope = n; while (!C ('}') && !C (T_EOFILE)) { /* block-item-list, block_item */ if ((r = TRY (declaration)) != err_node) { } else { PE (stmt, err1); } op_flat_append (list, r); continue; err1: error_recovery (c2m_ctx, 1, ""); } curr_scope = n->u.scope; if (!C (T_EOFILE)) PT ('}'); return n; err0: error_recovery (c2m_ctx, 0, "{"); return err_node; } D (transl_unit) { node_t list, ds, d, dl, r; // curr_token->code = ';'; /* for error recovery */ read_token (c2m_ctx); list = new_node (c2m_ctx, N_LIST); while (!C (T_EOFILE)) { /* external-declaration */ if ((r = TRY (declaration)) != err_node) { } else { PAE (declaration_specs, (node_t) 1, err); ds = r; PE (declarator, err); d = r; dl = new_node (c2m_ctx, N_LIST); d->u.scope = curr_scope; curr_scope = d; while (!C ('{')) { /* declaration-list */ PE (declaration, decl_err); op_flat_append (dl, r); } P (compound_stmt); r = new_pos_node4 (c2m_ctx, N_FUNC_DEF, d->pos, ds, d, dl, r); curr_scope = d->u.scope; } op_flat_append (list, r); continue; decl_err: curr_scope = d->u.scope; err: error_recovery (c2m_ctx, 0, ""); } return new_node1 (c2m_ctx, N_MODULE, list); } static void fatal_error (c2m_ctx_t c2m_ctx, C_error_code_t code, const char *message) { if (options->message_file != NULL) fprintf (options->message_file, "%s\n", message); longjmp (c2m_ctx->env, 1); } static void kw_add (c2m_ctx_t c2m_ctx, const char *name, token_code_t tc, size_t flags) { str_add (c2m_ctx, name, strlen (name) + 1, tc, flags, TRUE); } static void parse_init (MIR_context_t ctx) { c2m_ctx_t c2m_ctx = *c2m_ctx_loc (ctx); c2m_ctx->parse_ctx = c2mir_calloc (ctx, sizeof (struct parse_ctx)); error_func = fatal_error; record_level = 0; curr_uid = 0; init_streams (c2m_ctx); VARR_CREATE (token_t, recorded_tokens, 32); VARR_CREATE (token_t, buffered_tokens, 32); pre_init (ctx); kw_add (c2m_ctx, "_Bool", T_BOOL, 0); kw_add (c2m_ctx, "_Complex", T_COMPLEX, 0); kw_add (c2m_ctx, "_Alignas", T_ALIGNAS, 0); kw_add (c2m_ctx, "_Alignof", T_ALIGNOF, 0); kw_add (c2m_ctx, "_Atomic", T_ATOMIC, 0); kw_add (c2m_ctx, "_Generic", T_GENERIC, 0); kw_add (c2m_ctx, "_Noreturn", T_NO_RETURN, 0); kw_add (c2m_ctx, "_Static_assert", T_STATIC_ASSERT, 0); kw_add (c2m_ctx, "_Thread_local", T_THREAD_LOCAL, 0); kw_add (c2m_ctx, "auto", T_AUTO, 0); kw_add (c2m_ctx, "break", T_BREAK, 0); kw_add (c2m_ctx, "case", T_CASE, 0); kw_add (c2m_ctx, "char", T_CHAR, 0); kw_add (c2m_ctx, "const", T_CONST, 0); kw_add (c2m_ctx, "continue", T_CONTINUE, 0); kw_add (c2m_ctx, "default", T_DEFAULT, 0); kw_add (c2m_ctx, "do", T_DO, 0); kw_add (c2m_ctx, "double", T_DOUBLE, 0); kw_add (c2m_ctx, "else", T_ELSE, 0); kw_add (c2m_ctx, "enum", T_ENUM, 0); kw_add (c2m_ctx, "extern", T_EXTERN, 0); kw_add (c2m_ctx, "float", T_FLOAT, 0); kw_add (c2m_ctx, "for", T_FOR, 0); kw_add (c2m_ctx, "goto", T_GOTO, 0); kw_add (c2m_ctx, "if", T_IF, 0); kw_add (c2m_ctx, "inline", T_INLINE, FLAG_EXT89); kw_add (c2m_ctx, "int", T_INT, 0); kw_add (c2m_ctx, "long", T_LONG, 0); kw_add (c2m_ctx, "register", T_REGISTER, 0); kw_add (c2m_ctx, "restrict", T_RESTRICT, FLAG_C89); kw_add (c2m_ctx, "return", T_RETURN, 0); kw_add (c2m_ctx, "short", T_SHORT, 0); kw_add (c2m_ctx, "signed", T_SIGNED, 0); kw_add (c2m_ctx, "sizeof", T_SIZEOF, 0); kw_add (c2m_ctx, "static", T_STATIC, 0); kw_add (c2m_ctx, "struct", T_STRUCT, 0); kw_add (c2m_ctx, "switch", T_SWITCH, 0); kw_add (c2m_ctx, "typedef", T_TYPEDEF, 0); kw_add (c2m_ctx, "typeof", T_TYPEOF, FLAG_EXT); kw_add (c2m_ctx, "union", T_UNION, 0); kw_add (c2m_ctx, "unsigned", T_UNSIGNED, 0); kw_add (c2m_ctx, "void", T_VOID, 0); kw_add (c2m_ctx, "volatile", T_VOLATILE, 0); kw_add (c2m_ctx, "while", T_WHILE, 0); kw_add (c2m_ctx, "__restrict", T_RESTRICT, FLAG_EXT); kw_add (c2m_ctx, "__restrict__", T_RESTRICT, FLAG_EXT); kw_add (c2m_ctx, "__inline", T_INLINE, FLAG_EXT); kw_add (c2m_ctx, "__inline__", T_INLINE, FLAG_EXT); tpname_init (); } #ifndef SOURCEDIR #define SOURCEDIR "./" #endif #ifndef INSTALLDIR #define INSTALLDIR "/usr/bin/" #endif static void add_standard_includes (c2m_ctx_t c2m_ctx) { const char *str; for (int i = 0; i < sizeof (standard_includes) / sizeof (char *); i++) { str = standard_includes[i]; add_string_stream (c2m_ctx, "", str); } } static node_t parse (c2m_ctx_t c2m_ctx) { next_token_index = 0; return transl_unit (c2m_ctx, FALSE); } static void parse_finish (c2m_ctx_t c2m_ctx) { if (c2m_ctx == NULL || c2m_ctx->parse_ctx == NULL) return; if (recorded_tokens != NULL) VARR_DESTROY (token_t, recorded_tokens); if (buffered_tokens != NULL) VARR_DESTROY (token_t, buffered_tokens); pre_finish (c2m_ctx); tpname_finish (); finish_streams (c2m_ctx); free (c2m_ctx->parse_ctx); } #undef P #undef PT #undef PTP #undef PTN #undef PE #undef PTE #undef D #undef M #undef MP #undef MC #undef MN #undef TRY #undef C /* ------------------------- Parser End ------------------------------ */ /* New Page */ /* ---------------------- Context Checker Start ---------------------- */ /* The context checker is AST traversing pass which checks C11 constraints. It also augmenting AST nodes by type and layout information. Here are the created node attributes: 1. expr nodes have attribute "struct expr", N_ID not expr context has NULL attribute. 2. N_SWITCH has attribute "struct switch_attr" 3. N_SPEC_DECL (only with ID), N_MEMBER, N_FUNC_DEF have attribute "struct decl" 4. N_GOTO hash attribute node_t (target stmt) 5. N_STRUCT, N_UNION have attribute "struct node_scope" if they have a decl list 6. N_MODULE, N_BLOCK, N_FOR, N_FUNC have attribute "struct node_scope" 7. declaration_specs or spec_qual_list N_LISTs have attribute "struct decl_spec", but as a part of N_COMPOUND_LITERAL have attribute "struct decl" 8. N_ENUM_CONST has attribute "struct enum_value" 9. N_CASE and N_DEFAULT have attribute "struct case_attr" */ typedef struct decl *decl_t; DEF_VARR (decl_t); typedef struct case_attr *case_t; DEF_HTAB (case_t); struct check_ctx { VARR (node_t) * gotos; node_t func_block_scope; unsigned curr_func_scope_num; int in_params_p; node_t curr_unnamed_anon_struct_union_member; node_t curr_switch; VARR (decl_t) * func_decls_for_allocation; node_t n_i1_node; HTAB (case_t) * case_tab; node_t curr_func_def, curr_loop, curr_loop_switch; mir_size_t curr_call_arg_area_offset; VARR (node_t) * context_stack; }; #define gotos c2m_ctx->check_ctx->gotos #define func_block_scope c2m_ctx->check_ctx->func_block_scope #define curr_func_scope_num c2m_ctx->check_ctx->curr_func_scope_num #define in_params_p c2m_ctx->check_ctx->in_params_p #define curr_unnamed_anon_struct_union_member \ c2m_ctx->check_ctx->curr_unnamed_anon_struct_union_member #define curr_switch c2m_ctx->check_ctx->curr_switch #define func_decls_for_allocation c2m_ctx->check_ctx->func_decls_for_allocation #define n_i1_node c2m_ctx->check_ctx->n_i1_node #define case_tab c2m_ctx->check_ctx->case_tab #define curr_func_def c2m_ctx->check_ctx->curr_func_def #define curr_loop c2m_ctx->check_ctx->curr_loop #define curr_loop_switch c2m_ctx->check_ctx->curr_loop_switch #define curr_call_arg_area_offset c2m_ctx->check_ctx->curr_call_arg_area_offset #define context_stack c2m_ctx->check_ctx->context_stack static int supported_alignment_p (mir_llong align) { return TRUE; } // ??? static int symbol_eq (symbol_t s1, symbol_t s2, void *arg) { return s1.mode == s2.mode && s1.id->u.s.s == s2.id->u.s.s && s1.scope == s2.scope; } static htab_hash_t symbol_hash (symbol_t s, void *arg) { return (mir_hash_finish ( mir_hash_step (mir_hash_step (mir_hash_step (mir_hash_init (0x42), (uint64_t) s.mode), (uint64_t) s.id->u.s.s), (uint64_t) s.scope))); } static void symbol_clear (symbol_t sym, void *arg) { VARR_DESTROY (node_t, sym.defs); } static void symbol_init (c2m_ctx_t c2m_ctx) { HTAB_CREATE_WITH_FREE_FUNC (symbol_t, symbol_tab, 5000, symbol_hash, symbol_eq, symbol_clear, NULL); } static int symbol_find (c2m_ctx_t c2m_ctx, enum symbol_mode mode, node_t id, node_t scope, symbol_t *res) { int found_p; symbol_t el, symbol; symbol.mode = mode; symbol.id = id; symbol.scope = scope; found_p = HTAB_DO (symbol_t, symbol_tab, symbol, HTAB_FIND, el); if (res != NULL && found_p) *res = el; return found_p; } static void symbol_insert (c2m_ctx_t c2m_ctx, enum symbol_mode mode, node_t id, node_t scope, node_t def_node, node_t aux_node) { symbol_t el, symbol; symbol.mode = mode; symbol.id = id; symbol.scope = scope; symbol.def_node = def_node; symbol.aux_node = aux_node; VARR_CREATE (node_t, symbol.defs, 4); HTAB_DO (symbol_t, symbol_tab, symbol, HTAB_INSERT, el); } static void symbol_finish (c2m_ctx_t c2m_ctx) { if (symbol_tab != NULL) HTAB_DESTROY (symbol_t, symbol_tab); } enum basic_type get_int_basic_type (size_t s) { return (s == sizeof (mir_int) ? TP_INT : s == sizeof (mir_short) ? TP_SHORT : s == sizeof (mir_long) ? TP_LONG : s == sizeof (mir_schar) ? TP_SCHAR : TP_LLONG); } static int type_qual_eq_p (const struct type_qual *tq1, const struct type_qual *tq2) { return (tq1->const_p == tq2->const_p && tq1->restrict_p == tq2->restrict_p && tq1->volatile_p == tq2->volatile_p && tq1->atomic_p == tq2->atomic_p); } static void clear_type_qual (struct type_qual *tq) { tq->const_p = tq->restrict_p = tq->volatile_p = tq->atomic_p = FALSE; } static int type_qual_subset_p (const struct type_qual *tq1, const struct type_qual *tq2) { return (tq1->const_p <= tq2->const_p && tq1->restrict_p <= tq2->restrict_p && tq1->volatile_p <= tq2->volatile_p && tq1->atomic_p <= tq2->atomic_p); } static struct type_qual type_qual_union (const struct type_qual *tq1, const struct type_qual *tq2) { struct type_qual res; res.const_p = tq1->const_p || tq2->const_p; res.restrict_p = tq1->restrict_p || tq2->restrict_p; res.volatile_p = tq1->volatile_p || tq2->volatile_p; res.atomic_p = tq1->atomic_p || tq2->atomic_p; return res; } static void init_type (struct type *type) { clear_type_qual (&type->type_qual); type->pos_node = NULL; type->arr_type = NULL; type->align = -1; type->raw_size = MIR_SIZE_MAX; type->unnamed_anon_struct_union_member_type_p = FALSE; } static void set_type_pos_node (struct type *type, node_t n) { if (type->pos_node == NULL) type->pos_node = n; } static int char_type_p (const struct type *type) { return (type->mode == TM_BASIC && (type->u.basic_type == TP_CHAR || type->u.basic_type == TP_SCHAR || type->u.basic_type == TP_UCHAR)); } static int standard_integer_type_p (const struct type *type) { return (type->mode == TM_BASIC && type->u.basic_type >= TP_BOOL && type->u.basic_type <= TP_ULLONG); } static int integer_type_p (const struct type *type) { return standard_integer_type_p (type) || type->mode == TM_ENUM; } static int signed_integer_type_p (const struct type *type) { if (standard_integer_type_p (type)) { enum basic_type tp = type->u.basic_type; return ((tp == TP_CHAR && char_is_signed_p ()) || tp == TP_SCHAR || tp == TP_SHORT || tp == TP_INT || tp == TP_LONG || tp == TP_LLONG); } if (type->mode == TM_ENUM) return signed_integer_type_p (&ENUM_INT_TYPE); return FALSE; } static int floating_type_p (const struct type *type) { return type->mode == TM_BASIC && (type->u.basic_type == TP_FLOAT || type->u.basic_type == TP_DOUBLE || type->u.basic_type == TP_LDOUBLE); } static int arithmetic_type_p (const struct type *type) { return integer_type_p (type) || floating_type_p (type); } static int scalar_type_p (const struct type *type) { return arithmetic_type_p (type) || type->mode == TM_PTR; } static struct type get_ptr_int_type (int signed_p) { struct type res; init_type (&res); res.mode = TM_BASIC; if (sizeof (mir_int) == sizeof (mir_size_t)) { res.u.basic_type = signed_p ? TP_INT : TP_UINT; return res; } assert (sizeof (mir_long) == sizeof (mir_size_t)); res.u.basic_type = signed_p ? TP_LONG : TP_ULONG; return res; } static struct type integer_promotion (const struct type *type) { struct type res; assert (integer_type_p (type)); init_type (&res); res.mode = TM_BASIC; if (type->mode == TM_BASIC && TP_LONG <= type->u.basic_type && type->u.basic_type <= TP_ULLONG) { res.u.basic_type = type->u.basic_type; return res; } if (type->mode == TM_BASIC && ((type->u.basic_type == TP_CHAR && MIR_CHAR_MAX > MIR_INT_MAX) || (type->u.basic_type == TP_UCHAR && MIR_UCHAR_MAX > MIR_INT_MAX) || (type->u.basic_type == TP_USHORT && MIR_USHORT_MAX > MIR_INT_MAX))) res.u.basic_type = TP_UINT; else if (type->mode == TM_ENUM) res.u.basic_type = ENUM_BASIC_INT_TYPE; else if (type->mode == TM_BASIC && type->u.basic_type == TP_UINT) res.u.basic_type = TP_UINT; else res.u.basic_type = TP_INT; return res; } static struct type arithmetic_conversion (const struct type *type1, const struct type *type2) { struct type res, t1, t2; assert (arithmetic_type_p (type1) && arithmetic_type_p (type2)); init_type (&res); res.mode = TM_BASIC; if (floating_type_p (type1) || floating_type_p (type2)) { if ((type1->mode == TM_BASIC && type1->u.basic_type == TP_LDOUBLE) || (type2->mode == TM_BASIC && type2->u.basic_type == TP_LDOUBLE)) { res.u.basic_type = TP_LDOUBLE; } else if ((type1->mode == TM_BASIC && type1->u.basic_type == TP_DOUBLE) || (type2->mode == TM_BASIC && type2->u.basic_type == TP_DOUBLE)) { res.u.basic_type = TP_DOUBLE; } else if ((type1->mode == TM_BASIC && type1->u.basic_type == TP_FLOAT) || (type2->mode == TM_BASIC && type2->u.basic_type == TP_FLOAT)) { res.u.basic_type = TP_FLOAT; } return res; } t1 = integer_promotion (type1); t2 = integer_promotion (type2); if (signed_integer_type_p (&t1) == signed_integer_type_p (&t2)) { res.u.basic_type = t1.u.basic_type < t2.u.basic_type ? t2.u.basic_type : t1.u.basic_type; } else { struct type t; if (signed_integer_type_p (&t1)) SWAP (t1, t2, t); assert (!signed_integer_type_p (&t1) && signed_integer_type_p (&t2)); if ((t1.u.basic_type == TP_ULONG && t2.u.basic_type < TP_LONG) || (t1.u.basic_type == TP_ULLONG && t2.u.basic_type < TP_LLONG)) { res.u.basic_type = t1.u.basic_type; } else if ((t1.u.basic_type == TP_UINT && t2.u.basic_type >= TP_LONG && MIR_LONG_MAX >= MIR_UINT_MAX) || (t1.u.basic_type == TP_ULONG && t2.u.basic_type >= TP_LLONG && MIR_LLONG_MAX >= MIR_ULONG_MAX)) { res.u.basic_type = t2.u.basic_type; } else { res.u.basic_type = t1.u.basic_type; } } return res; } struct expr { unsigned int const_p : 1, const_addr_p : 1, builtin_call_p : 1; node_t lvalue_node; node_t def_node; /* defined for id or const address (ref) */ struct type *type; /* type of the result */ struct type *type2; /* used for assign expr type */ union { /* defined for const or const addr (i_val is offset) */ mir_llong i_val; mir_ullong u_val; mir_ldouble d_val; } u; }; struct decl_spec { unsigned int typedef_p : 1, extern_p : 1, static_p : 1; unsigned int auto_p : 1, register_p : 1, thread_local_p : 1; unsigned int inline_p : 1, no_return_p : 1; /* func specifiers */ int align; // negative value means undefined node_t align_node; // strictest valid N_ALIGNAS node node_code_t linkage; // N_IGNORE - none, N_STATIC - internal, N_EXTERN - external struct type *type; }; struct enum_value { mir_int val; }; struct node_scope { unsigned char stack_var_p; /* necessity for frame */ unsigned func_scope_num; mir_size_t size, offset, call_arg_area_size; node_t scope; }; struct decl { /* true if address is taken, reg can be used or is used: */ unsigned addr_p : 1, reg_p : 1, used_p : 1; int bit_offset, width; /* for bitfields, -1 bit_offset for non bitfields. */ mir_size_t offset; /* var offset in frame or bss */ node_t scope; /* declaration scope */ struct decl_spec decl_spec; /* Unnamed member if this scope is anon struct/union for the member, NULL otherwise: */ node_t containing_unnamed_anon_struct_union_member; MIR_item_t item; /* MIR_item for some declarations */ c2m_ctx_t c2m_ctx; }; static struct decl_spec *get_param_decl_spec (node_t param) { node_t MIR_UNUSED declarator; if (param->code == N_TYPE) return param->attr; declarator = NL_EL (param->ops, 1); assert (param->code == N_SPEC_DECL && declarator != NULL && declarator->code == N_DECL); return &((decl_t) param->attr)->decl_spec; } static int type_eq_p (struct type *type1, struct type *type2) { if (type1->mode != type2->mode) return FALSE; if (!type_qual_eq_p (&type1->type_qual, &type2->type_qual)) return FALSE; switch (type1->mode) { case TM_BASIC: return type1->u.basic_type == type2->u.basic_type; case TM_ENUM: case TM_STRUCT: case TM_UNION: return type1->u.tag_type == type2->u.tag_type; case TM_PTR: return type_eq_p (type1->u.ptr_type, type2->u.ptr_type); case TM_ARR: { struct expr *cexpr1, *cexpr2; struct arr_type *at1 = type1->u.arr_type, *at2 = type2->u.arr_type; return (at1->static_p == at2->static_p && type_eq_p (at1->el_type, at2->el_type) && type_qual_eq_p (&at1->ind_type_qual, &at2->ind_type_qual) && at1->size->code != N_IGNORE && at2->size->code != N_IGNORE && (cexpr1 = at1->size->attr)->const_p && (cexpr2 = at2->size->attr)->const_p && integer_type_p (cexpr2->type) && integer_type_p (cexpr2->type) && cexpr1->u.i_val == cexpr2->u.i_val); } case TM_FUNC: { struct func_type *ft1 = type1->u.func_type, *ft2 = type2->u.func_type; struct decl_spec *ds1, *ds2; if (ft1->dots_p != ft2->dots_p || !type_eq_p (ft1->ret_type, ft2->ret_type) || NL_LENGTH (ft1->param_list->ops) != NL_LENGTH (ft2->param_list->ops)) return FALSE; for (node_t p1 = NL_HEAD (ft1->param_list->ops), p2 = NL_HEAD (ft2->param_list->ops); p1 != NULL; p1 = NL_NEXT (p1), p2 = NL_NEXT (p2)) { ds1 = get_param_decl_spec (p1); ds2 = get_param_decl_spec (p2); if (!type_eq_p (ds1->type, ds2->type)) return FALSE; // ??? other qualifiers } return TRUE; } default: return FALSE; } } static int compatible_types_p (struct type *type1, struct type *type2, int ignore_quals_p) { if (type1->mode != type2->mode) { if (!ignore_quals_p && !type_qual_eq_p (&type1->type_qual, &type2->type_qual)) return FALSE; if (type1->mode == TM_ENUM && type2->mode == TM_BASIC) return type2->u.basic_type == ENUM_BASIC_INT_TYPE; if (type2->mode == TM_ENUM && type1->mode == TM_BASIC) return type1->u.basic_type == ENUM_BASIC_INT_TYPE; return FALSE; } if (type1->mode == TM_BASIC) { return (type1->u.basic_type == type2->u.basic_type && (ignore_quals_p || type_qual_eq_p (&type1->type_qual, &type2->type_qual))); } else if (type1->mode == TM_PTR) { return ((ignore_quals_p || type_qual_eq_p (&type1->type_qual, &type2->type_qual)) && compatible_types_p (type1->u.ptr_type, type2->u.ptr_type, ignore_quals_p)); } else if (type1->mode == TM_ARR) { struct expr *cexpr1, *cexpr2; struct arr_type *at1 = type1->u.arr_type, *at2 = type2->u.arr_type; if (!compatible_types_p (at1->el_type, at2->el_type, ignore_quals_p)) return FALSE; if (at1->size->code == N_IGNORE || at2->size->code == N_IGNORE) return TRUE; if ((cexpr1 = at1->size->attr)->const_p && (cexpr2 = at2->size->attr)->const_p && integer_type_p (cexpr2->type) && integer_type_p (cexpr2->type)) return cexpr1->u.i_val == cexpr2->u.i_val; return TRUE; } else if (type1->mode == TM_FUNC) { struct func_type *ft1 = type1->u.func_type, *ft2 = type2->u.func_type; if (NL_HEAD (ft1->param_list->ops) != NULL && NL_HEAD (ft2->param_list->ops) != NULL && NL_LENGTH (ft1->param_list->ops) != NL_LENGTH (ft2->param_list->ops)) return FALSE; // ??? check parameter types } else { assert (type1->mode == TM_STRUCT || type1->mode == TM_UNION || type1->mode == TM_ENUM); return (type1->u.tag_type == type2->u.tag_type && (ignore_quals_p || type_qual_eq_p (&type1->type_qual, &type2->type_qual))); } return TRUE; } static struct type composite_type (c2m_ctx_t c2m_ctx, struct type *tp1, struct type *tp2) { struct type t = *tp1; assert (compatible_types_p (tp1, tp2, TRUE)); if (tp1->mode == TM_ARR) { struct arr_type *arr_type; t.u.arr_type = arr_type = reg_malloc (c2m_ctx, sizeof (struct arr_type)); *arr_type = *tp1->u.arr_type; if (arr_type->size == N_IGNORE) arr_type->size = tp2->u.arr_type->size; *arr_type->el_type = composite_type (c2m_ctx, tp1->u.arr_type->el_type, tp2->u.arr_type->el_type); } else if (tp1->mode == TM_FUNC) { /* ??? */ } return t; } static struct type *create_type (c2m_ctx_t c2m_ctx, struct type *copy) { struct type *res = reg_malloc (c2m_ctx, sizeof (struct type)); if (copy == NULL) init_type (res); else *res = *copy; return res; } DEF_DLIST_LINK (case_t); struct case_attr { node_t case_node, case_target_node; DLIST_LINK (case_t) case_link; }; DEF_DLIST (case_t, case_link); struct switch_attr { struct type type; /* integer promoted type */ int ranges_p; case_t min_val_case, max_val_case; DLIST (case_t) case_labels; /* default case is always a tail */ }; static int basic_type_size (enum basic_type bt) { switch (bt) { case TP_BOOL: return sizeof (mir_bool); case TP_CHAR: return sizeof (mir_char); case TP_SCHAR: return sizeof (mir_schar); case TP_UCHAR: return sizeof (mir_uchar); case TP_SHORT: return sizeof (mir_short); case TP_USHORT: return sizeof (mir_ushort); case TP_INT: return sizeof (mir_int); case TP_UINT: return sizeof (mir_uint); case TP_LONG: return sizeof (mir_long); case TP_ULONG: return sizeof (mir_ulong); case TP_LLONG: return sizeof (mir_llong); case TP_ULLONG: return sizeof (mir_ullong); case TP_FLOAT: return sizeof (mir_float); case TP_DOUBLE: return sizeof (mir_double); case TP_LDOUBLE: return sizeof (mir_ldouble); case TP_VOID: return 1; // ??? default: abort (); } } static int basic_type_align (enum basic_type bt) { return basic_type_size (bt); } static int type_align (struct type *type) { assert (type->align >= 0); return type->align; } static int incomplete_type_p (c2m_ctx_t c2m_ctx, struct type *type); static void aux_set_type_align (c2m_ctx_t c2m_ctx, struct type *type) { /* Should be called only from set_type_layout. */ int align, member_align; if (type->align >= 0) return; if (type->mode == TM_BASIC) { align = basic_type_align (type->u.basic_type); } else if (type->mode == TM_PTR) { align = sizeof (mir_size_t); } else if (type->mode == TM_ENUM) { align = basic_type_align (ENUM_BASIC_INT_TYPE); } else if (type->mode == TM_FUNC) { align = sizeof (mir_size_t); } else if (type->mode == TM_ARR) { align = type_align (type->u.arr_type->el_type); } else if (type->mode == TM_UNDEF) { align = 0; /* error type */ } else { assert (type->mode == TM_STRUCT || type->mode == TM_UNION); if (incomplete_type_p (c2m_ctx, type)) { align = -1; } else { align = 1; for (node_t member = NL_HEAD (NL_EL (type->u.tag_type->ops, 1)->ops); member != NULL; member = NL_NEXT (member)) if (member->code == N_MEMBER) { decl_t decl = member->attr; node_t width = NL_EL (member->ops, 2); struct expr *expr; if (type->mode == TM_UNION && width->code != N_IGNORE && (expr = width->attr)->const_p && expr->u.u_val == 0) continue; member_align = type_align (decl->decl_spec.type); if (align < member_align) align = member_align; } } } type->align = align; } static mir_size_t type_size (c2m_ctx_t c2m_ctx, struct type *type) { mir_size_t size = raw_type_size (c2m_ctx, type); return round_size (size, type->align); } static mir_size_t var_align (c2m_ctx_t c2m_ctx, struct type *type) { int align; raw_type_size (c2m_ctx, type); /* check */ align = type->align; assert (align >= 0); #ifdef ADJUST_VAR_ALIGNMENT align = ADJUST_VAR_ALIGNMENT (c2m_ctx, align, type); #endif return align; } static mir_size_t var_size (c2m_ctx_t c2m_ctx, struct type *type) { mir_size_t size = raw_type_size (c2m_ctx, type); return round_size (size, var_align (c2m_ctx, type)); } /* BOUND_BIT is used only if BF_P. */ static void update_field_layout (int *bf_p, mir_size_t *overall_size, mir_size_t *offset, int *bound_bit, mir_size_t prev_size, mir_size_t size, int align, int bits) { mir_size_t prev_field_offset = *offset, bytes = 0; int start_bit, diff; assert (size > 0); if (!*bf_p) { /* transition from field to bit field or field */ if (bits >= 0 && size > prev_size) { *bound_bit = prev_size * MIR_CHAR_BIT; } else { prev_field_offset += prev_size; *offset = prev_field_offset / align * align; *bound_bit = (prev_field_offset - *offset) * MIR_CHAR_BIT; prev_field_offset = *offset; } } *bf_p = bits >= 0; if (bits < 0) { bytes = size - 1; bits = MIR_CHAR_BIT; } *offset = prev_field_offset / align * align; diff = prev_field_offset - *offset; for (;;) { start_bit = *bound_bit + diff * MIR_CHAR_BIT; if (start_bit < 0) start_bit = 0; if ((start_bit + bits - 1) / MIR_CHAR_BIT + 1 + bytes <= size) { *bound_bit = start_bit + bits; break; } *offset += align; diff -= align; if (bytes >= align) bytes -= align; } if (*overall_size < *offset + size) *overall_size = *offset + size; } /* Update offsets inside unnamed anonymous struct/union member. */ static void update_members_offset (struct type *type, mir_size_t offset) { assert ((type->mode == TM_STRUCT || type->mode == TM_UNION) && type->unnamed_anon_struct_union_member_type_p); assert (offset != MIR_SIZE_MAX || type->raw_size == MIR_SIZE_MAX); for (node_t el = NL_HEAD (NL_EL (type->u.tag_type->ops, 1)->ops); el != NULL; el = NL_NEXT (el)) if (el->code == N_MEMBER) { decl_t decl = el->attr; decl->offset = offset == MIR_SIZE_MAX ? 0 : decl->offset + offset; if (decl->decl_spec.type->unnamed_anon_struct_union_member_type_p) update_members_offset (decl->decl_spec.type, offset == MIR_SIZE_MAX ? offset : decl->offset); } } static void set_type_layout (c2m_ctx_t c2m_ctx, struct type *type) { mir_size_t overall_size = 0; if (type->raw_size != MIR_SIZE_MAX) return; /* defined */ if (type->mode == TM_BASIC) { overall_size = basic_type_size (type->u.basic_type); } else if (type->mode == TM_PTR) { overall_size = sizeof (mir_size_t); } else if (type->mode == TM_ENUM) { overall_size = basic_type_size (ENUM_BASIC_INT_TYPE); } else if (type->mode == TM_FUNC) { overall_size = sizeof (mir_size_t); } else if (type->mode == TM_ARR) { struct arr_type *arr_type = type->u.arr_type; struct expr *cexpr = arr_type->size->attr; mir_size_t nel = (arr_type->size->code == N_IGNORE || !cexpr->const_p ? 1 : cexpr->u.i_val); set_type_layout (c2m_ctx, arr_type->el_type); overall_size = type_size (c2m_ctx, arr_type->el_type) * nel; } else if (type->mode == TM_UNDEF) { overall_size = sizeof (int); /* error type */ } else { int bf_p = FALSE, bits = -1, bound_bit = 0; mir_size_t offset = 0, prev_size = 0; assert (type->mode == TM_STRUCT || type->mode == TM_UNION); if (incomplete_type_p (c2m_ctx, type)) { overall_size = MIR_SIZE_MAX; } else { for (node_t el = NL_HEAD (NL_EL (type->u.tag_type->ops, 1)->ops); el != NULL; el = NL_NEXT (el)) if (el->code == N_MEMBER) { decl_t decl = el->attr; int member_align; mir_size_t member_size; node_t width = NL_EL (el->ops, 2); struct expr *expr; int anon_process_p = (!type->unnamed_anon_struct_union_member_type_p && decl->decl_spec.type->unnamed_anon_struct_union_member_type_p && decl->decl_spec.type->raw_size == MIR_SIZE_MAX); if (anon_process_p) update_members_offset (decl->decl_spec.type, MIR_SIZE_MAX); set_type_layout (c2m_ctx, decl->decl_spec.type); member_size = type_size (c2m_ctx, decl->decl_spec.type); member_align = type_align (decl->decl_spec.type); bits = width->code == N_IGNORE || !(expr = width->attr)->const_p ? -1 : expr->u.u_val; if (bits != 0) { update_field_layout (&bf_p, &overall_size, &offset, &bound_bit, prev_size, member_size, member_align, bits); prev_size = member_size; decl->offset = offset; decl->bit_offset = bits < 0 ? -1 : bound_bit - bits; } else { /* Finish the last unit */ bf_p = FALSE; offset = (offset + member_align - 1) / member_align * member_align; /* The offset and bit_offset do not matter, but make bit_offset less member_size in bits */ decl->offset = offset + bound_bit / (member_size * MIR_CHAR_BIT); decl->bit_offset = bound_bit % (member_size * MIR_CHAR_BIT); } decl->width = bits; if (type->mode == TM_UNION) { offset = prev_size = 0; bf_p = FALSE; bits = -1; bound_bit = 0; } if (anon_process_p) update_members_offset (decl->decl_spec.type, decl->offset); } } } /* we might need raw_size for alignment calculations */ type->raw_size = overall_size; aux_set_type_align (c2m_ctx, type); if (type->mode == TM_PTR) /* Visit the pointed but after setting size to avoid looping */ set_type_layout (c2m_ctx, type->u.ptr_type); } static int int_bit_size (struct type *type) { assert (type->mode == TM_BASIC || type->mode == TM_ENUM); return (basic_type_size (type->mode == TM_ENUM ? ENUM_BASIC_INT_TYPE : type->u.basic_type) * MIR_CHAR_BIT); } static int void_type_p (struct type *type) { return type->mode == TM_BASIC && type->u.basic_type == TP_VOID; } static int void_ptr_p (struct type *type) { return type->mode == TM_PTR && void_type_p (type->u.ptr_type); } static int incomplete_type_p (c2m_ctx_t c2m_ctx, struct type *type) { switch (type->mode) { case TM_BASIC: return type->u.basic_type == TP_VOID; case TM_ENUM: case TM_STRUCT: case TM_UNION: { node_t scope, n = type->u.tag_type; if (NL_EL (n->ops, 1)->code == N_IGNORE) return TRUE; for (scope = curr_scope; scope != NULL && scope != top_scope && scope != n; scope = ((struct node_scope *) scope->attr)->scope) ; return scope == n; } case TM_PTR: return FALSE; case TM_ARR: { struct arr_type *arr_type = type->u.arr_type; return (arr_type->size->code == N_IGNORE || incomplete_type_p (c2m_ctx, arr_type->el_type)); } case TM_FUNC: return ((type = type->u.func_type->ret_type) == NULL || (!void_type_p (type) && incomplete_type_p (c2m_ctx, type))); default: return FALSE; } } static int null_const_p (struct expr *expr, struct type *type) { return ((integer_type_p (type) && expr->const_p && expr->u.u_val == 0) || (void_ptr_p (type) && expr->const_p && expr->u.u_val == 0 && type_qual_eq_p (&type->type_qual, &zero_type_qual))); } static void cast_value (struct expr *to_e, struct expr *from_e, struct type *to) { assert (to_e->const_p && from_e->const_p); struct type *from = from_e->type; #define CONV(TP, cast, mto, mfrom) \ case TP: \ to_e->u.mto = (cast) from_e->u.mfrom; \ break; #define BASIC_FROM_CONV(mfrom) \ switch (to->u.basic_type) { \ CONV (TP_BOOL, mir_bool, u_val, mfrom) CONV (TP_UCHAR, mir_uchar, u_val, mfrom); \ CONV (TP_USHORT, mir_ushort, u_val, mfrom) CONV (TP_UINT, mir_uint, u_val, mfrom); \ CONV (TP_ULONG, mir_ulong, u_val, mfrom) CONV (TP_ULLONG, mir_ullong, u_val, mfrom); \ CONV (TP_SCHAR, mir_char, i_val, mfrom); \ CONV (TP_SHORT, mir_short, i_val, mfrom) CONV (TP_INT, mir_int, i_val, mfrom); \ CONV (TP_LONG, mir_long, i_val, mfrom) CONV (TP_LLONG, mir_llong, i_val, mfrom); \ CONV (TP_FLOAT, mir_float, d_val, mfrom) CONV (TP_DOUBLE, mir_double, d_val, mfrom); \ CONV (TP_LDOUBLE, mir_ldouble, d_val, mfrom); \ case TP_CHAR: \ if (char_is_signed_p ()) \ to_e->u.i_val = (mir_char) from_e->u.mfrom; \ else \ to_e->u.u_val = (mir_char) from_e->u.mfrom; \ break; \ default: assert (FALSE); \ } #define BASIC_TO_CONV(cast, mto) \ switch (from->u.basic_type) { \ case TP_BOOL: \ case TP_UCHAR: \ case TP_USHORT: \ case TP_UINT: \ case TP_ULONG: \ case TP_ULLONG: to_e->u.mto = (cast) from_e->u.u_val; break; \ case TP_CHAR: \ if (!char_is_signed_p ()) { \ to_e->u.mto = (cast) from_e->u.u_val; \ break; \ } \ /* Fall through: */ \ case TP_SCHAR: \ case TP_SHORT: \ case TP_INT: \ case TP_LONG: \ case TP_LLONG: to_e->u.mto = (cast) from_e->u.i_val; break; \ case TP_FLOAT: \ case TP_DOUBLE: \ case TP_LDOUBLE: to_e->u.mto = (cast) from_e->u.d_val; break; \ default: assert (FALSE); \ } if (to->mode == from->mode && (from->mode == TM_PTR || from->mode == TM_ENUM)) { to_e->u = from_e->u; } else if (from->mode == TM_PTR) { if (to->mode == TM_ENUM) { to_e->u.i_val = (ENUM_MIR_INT) from_e->u.u_val; } else { BASIC_FROM_CONV (u_val); } } else if (from->mode == TM_ENUM) { if (to->mode == TM_PTR) { to_e->u.u_val = (mir_size_t) from_e->u.i_val; } else { BASIC_FROM_CONV (i_val); } } else if (to->mode == TM_PTR) { BASIC_TO_CONV (mir_size_t, u_val); } else if (to->mode == TM_ENUM) { BASIC_TO_CONV (ENUM_MIR_INT, i_val); } else { switch (from->u.basic_type) { case TP_BOOL: case TP_UCHAR: case TP_USHORT: case TP_UINT: case TP_ULONG: case TP_ULLONG: BASIC_FROM_CONV (u_val); break; case TP_CHAR: if (!char_is_signed_p ()) { BASIC_FROM_CONV (u_val); break; } /* Fall through: */ case TP_SCHAR: case TP_SHORT: case TP_INT: case TP_LONG: case TP_LLONG: BASIC_FROM_CONV (i_val); break; case TP_FLOAT: case TP_DOUBLE: case TP_LDOUBLE: BASIC_FROM_CONV (d_val); break; default: assert (FALSE); } } #undef CONV #undef BASIC_FROM_CONV #undef BASIC_TO_CONV } static void convert_value (struct expr *e, struct type *to) { cast_value (e, e, to); } static int non_reg_decl_spec_p (struct decl_spec *ds) { return (ds->typedef_p || ds->extern_p || ds->static_p || ds->auto_p || ds->thread_local_p || ds->inline_p || ds->no_return_p || ds->align_node); } static void create_node_scope (c2m_ctx_t c2m_ctx, node_t node) { struct node_scope *ns = reg_malloc (c2m_ctx, sizeof (struct node_scope)); assert (node != curr_scope); ns->func_scope_num = curr_func_scope_num++; ns->stack_var_p = FALSE; ns->offset = ns->size = ns->call_arg_area_size = 0; node->attr = ns; ns->scope = curr_scope; curr_scope = node; } static void finish_scope (c2m_ctx_t c2m_ctx) { curr_scope = ((struct node_scope *) curr_scope->attr)->scope; } static void set_type_qual (c2m_ctx_t c2m_ctx, node_t r, struct type_qual *tq, enum type_mode tmode) { for (node_t n = NL_HEAD (r->ops); n != NULL; n = NL_NEXT (n)) switch (n->code) { /* Type qualifiers: */ case N_CONST: tq->const_p = TRUE; break; case N_RESTRICT: tq->restrict_p = TRUE; if (tmode != TM_PTR && tmode != TM_UNDEF) error (c2m_ctx, n->pos, "restrict requires a pointer"); break; case N_VOLATILE: tq->volatile_p = TRUE; break; case N_ATOMIC: tq->atomic_p = TRUE; if (tmode == TM_ARR) error (c2m_ctx, n->pos, "_Atomic qualifying array"); else if (tmode == TM_FUNC) error (c2m_ctx, n->pos, "_Atomic qualifying function"); break; default: break; /* Ignore */ } } static void check_type_duplication (c2m_ctx_t c2m_ctx, struct type *type, node_t n, const char *name, int size, int sign) { if (type->mode != TM_BASIC || type->u.basic_type != TP_UNDEF) error (c2m_ctx, n->pos, "%s with another type", name); else if (type->mode != TM_BASIC && size != 0) error (c2m_ctx, n->pos, "size with non-numeric type"); else if (type->mode != TM_BASIC && sign != 0) error (c2m_ctx, n->pos, "sign attribute with non-integer type"); } static node_t find_def (c2m_ctx_t c2m_ctx, enum symbol_mode mode, node_t id, node_t scope, node_t *aux_node) { symbol_t sym; for (;;) { if (!symbol_find (c2m_ctx, mode, id, scope, &sym)) { if (scope == NULL) return NULL; scope = ((struct node_scope *) scope->attr)->scope; } else { if (aux_node) *aux_node = sym.aux_node; return sym.def_node; } } } static node_t process_tag (c2m_ctx_t c2m_ctx, node_t r, node_t id, node_t decl_list) { symbol_t sym; int found_p; node_t scope, tab_decl_list; if (id->code != N_ID) return r; scope = curr_scope; while (scope != top_scope && (scope->code == N_STRUCT || scope->code == N_UNION)) scope = ((struct node_scope *) scope->attr)->scope; sym.def_node = NULL; /* to remove uninitialized warning */ if (decl_list->code != N_IGNORE) { found_p = symbol_find (c2m_ctx, S_TAG, id, scope, &sym); } else { sym.def_node = find_def (c2m_ctx, S_TAG, id, scope, NULL); found_p = sym.def_node != NULL; } if (!found_p) { symbol_insert (c2m_ctx, S_TAG, id, scope, r, NULL); } else if (sym.def_node->code != r->code) { error (c2m_ctx, id->pos, "kind of tag %s is unmatched with previous declaration", id->u.s.s); } else if ((tab_decl_list = NL_EL (sym.def_node->ops, 1))->code != N_IGNORE && decl_list->code != N_IGNORE) { error (c2m_ctx, id->pos, "tag %s redeclaration", id->u.s.s); } else { if (decl_list->code != N_IGNORE) { /* swap decl lists */ DLIST (node_t) temp; SWAP (r->ops, sym.def_node->ops, temp); } r = sym.def_node; } return r; } static void def_symbol (c2m_ctx_t c2m_ctx, enum symbol_mode mode, node_t id, node_t scope, node_t def_node, node_code_t linkage) { symbol_t sym; struct decl_spec tab_decl_spec, decl_spec; if (id->code == N_IGNORE) return; assert (id->code == N_ID && scope != NULL); assert (scope->code == N_MODULE || scope->code == N_BLOCK || scope->code == N_STRUCT || scope->code == N_UNION || scope->code == N_FUNC || scope->code == N_FOR); decl_spec = ((decl_t) def_node->attr)->decl_spec; if (decl_spec.thread_local_p && !decl_spec.static_p && !decl_spec.extern_p) error (c2m_ctx, id->pos, "auto %s is declared as thread local", id->u.s.s); if (!symbol_find (c2m_ctx, mode, id, scope, &sym)) { symbol_insert (c2m_ctx, mode, id, scope, def_node, NULL); return; } tab_decl_spec = ((decl_t) sym.def_node->attr)->decl_spec; if (linkage == N_IGNORE) { if (!decl_spec.typedef_p || !tab_decl_spec.typedef_p || !type_eq_p (decl_spec.type, tab_decl_spec.type)) #ifdef __APPLE__ /* a hack to use our definition instead of macosx for non-GNU compiler */ if (strcmp (id->u.s.s, "__darwin_va_list") != 0) #endif error (c2m_ctx, id->pos, "repeated declaration %s", id->u.s.s); } else if (!compatible_types_p (decl_spec.type, tab_decl_spec.type, FALSE)) { error (c2m_ctx, id->pos, "incompatible types of %s declarations", id->u.s.s); } if (tab_decl_spec.thread_local_p != decl_spec.thread_local_p) { error (c2m_ctx, id->pos, "thread local and non-thread local declarations of %s", id->u.s.s); } if ((decl_spec.linkage == N_EXTERN && linkage == N_STATIC) || (decl_spec.linkage == N_STATIC && linkage == N_EXTERN)) warning (c2m_ctx, id->pos, "%s defined with external and internal linkage", id->u.s.s); VARR_PUSH (node_t, sym.defs, def_node); } static void make_type_complete (c2m_ctx_t c2m_ctx, struct type *type) { if (incomplete_type_p (c2m_ctx, type)) return; /* The type may become complete: recalculate size: */ type->raw_size = MIR_SIZE_MAX; set_type_layout (c2m_ctx, type); } static void check (c2m_ctx_t c2m_ctx, node_t node, node_t context); static struct decl_spec check_decl_spec (c2m_ctx_t c2m_ctx, node_t r, node_t decl) { int n_sc = 0, sign = 0, size = 0, func_p = FALSE; struct decl_spec *res; struct type *type; if (r->attr != NULL) return *(struct decl_spec *) r->attr; if (decl->code == N_FUNC_DEF) { func_p = TRUE; } else if (decl->code == N_SPEC_DECL) { node_t declarator = NL_EL (decl->ops, 1); node_t list = NL_EL (declarator->ops, 1); func_p = list != NULL && NL_HEAD (list->ops) != NULL && NL_HEAD (list->ops)->code == N_FUNC; } r->attr = res = reg_malloc (c2m_ctx, sizeof (struct decl_spec)); res->typedef_p = res->extern_p = res->static_p = FALSE; res->auto_p = res->register_p = res->thread_local_p = FALSE; res->inline_p = res->no_return_p = FALSE; res->align = -1; res->align_node = NULL; res->linkage = N_IGNORE; res->type = type = create_type (c2m_ctx, NULL); type->pos_node = r; type->mode = TM_BASIC; type->u.basic_type = TP_UNDEF; for (node_t n = NL_HEAD (r->ops); n != NULL; n = NL_NEXT (n)) if (n->code == N_SIGNED || n->code == N_UNSIGNED) { if (sign != 0) error (c2m_ctx, n->pos, "more than one sign qualifier"); else sign = n->code == N_SIGNED ? 1 : -1; } else if (n->code == N_SHORT) { if (size != 0) error (c2m_ctx, n->pos, "more than one type"); else size = 1; } else if (n->code == N_LONG) { if (size == 2) size = 3; else if (size == 3) error (c2m_ctx, n->pos, "more than two long"); else if (size == 1) error (c2m_ctx, n->pos, "short with long"); else size = 2; } for (node_t n = NL_HEAD (r->ops); n != NULL; n = NL_NEXT (n)) switch (n->code) { /* Type qualifiers are already processed. */ case N_CONST: case N_RESTRICT: case N_VOLATILE: case N_ATOMIC: break; /* Func specifiers: */ case N_INLINE: if (!func_p) error (c2m_ctx, n->pos, "non-function declaration with inline"); else res->inline_p = TRUE; break; case N_NO_RETURN: if (!func_p) error (c2m_ctx, n->pos, "non-function declaration with _Noreturn"); else res->no_return_p = TRUE; break; /* Storage specifiers: */ case N_TYPEDEF: case N_AUTO: case N_REGISTER: if (n_sc != 0) error (c2m_ctx, n->pos, "more than one storage specifier"); else if (n->code == N_TYPEDEF) res->typedef_p = TRUE; else if (n->code == N_AUTO) res->auto_p = TRUE; else res->register_p = TRUE; n_sc++; break; case N_EXTERN: case N_STATIC: if (n_sc != 0 && (n_sc != 1 || !res->thread_local_p)) error (c2m_ctx, n->pos, "more than one storage specifier"); else if (n->code == N_EXTERN) res->extern_p = TRUE; else res->static_p = TRUE; n_sc++; break; case N_THREAD_LOCAL: if (n_sc != 0 && (n_sc != 1 || (!res->extern_p && !res->static_p))) error (c2m_ctx, n->pos, "more than one storage specifier"); else res->thread_local_p = TRUE; n_sc++; break; case N_VOID: set_type_pos_node (type, n); if (type->mode != TM_BASIC || type->u.basic_type != TP_UNDEF) error (c2m_ctx, n->pos, "void with another type"); else if (sign != 0) error (c2m_ctx, n->pos, "void with sign qualifier"); else if (size != 0) error (c2m_ctx, n->pos, "void with short or long"); else type->u.basic_type = TP_VOID; break; case N_UNSIGNED: case N_SIGNED: case N_SHORT: case N_LONG: set_type_pos_node (type, n); break; case N_CHAR: case N_INT: set_type_pos_node (type, n); if (type->mode != TM_BASIC || type->u.basic_type != TP_UNDEF) { error (c2m_ctx, n->pos, "char or int with another type"); } else if (n->code == N_CHAR) { if (size != 0) error (c2m_ctx, n->pos, "char with short or long"); else type->u.basic_type = sign == 0 ? TP_CHAR : sign < 0 ? TP_UCHAR : TP_SCHAR; } else if (size == 0) type->u.basic_type = sign >= 0 ? TP_INT : TP_UINT; else if (size == 1) type->u.basic_type = sign >= 0 ? TP_SHORT : TP_USHORT; else if (size == 2) type->u.basic_type = sign >= 0 ? TP_LONG : TP_ULONG; else type->u.basic_type = sign >= 0 ? TP_LLONG : TP_ULLONG; break; case N_BOOL: set_type_pos_node (type, n); if (type->mode != TM_BASIC || type->u.basic_type != TP_UNDEF) error (c2m_ctx, n->pos, "_Bool with another type"); else if (sign != 0) error (c2m_ctx, n->pos, "_Bool with sign qualifier"); else if (size != 0) error (c2m_ctx, n->pos, "_Bool with short or long"); type->u.basic_type = TP_BOOL; break; case N_FLOAT: set_type_pos_node (type, n); if (type->mode != TM_BASIC || type->u.basic_type != TP_UNDEF) error (c2m_ctx, n->pos, "float with another type"); else if (sign != 0) error (c2m_ctx, n->pos, "float with sign qualifier"); else if (size != 0) error (c2m_ctx, n->pos, "float with short or long"); else type->u.basic_type = TP_FLOAT; break; case N_DOUBLE: set_type_pos_node (type, n); if (type->mode != TM_BASIC || type->u.basic_type != TP_UNDEF) error (c2m_ctx, n->pos, "double with another type"); else if (sign != 0) error (c2m_ctx, n->pos, "double with sign qualifier"); else if (size == 0) type->u.basic_type = TP_DOUBLE; else if (size == 2) type->u.basic_type = TP_LDOUBLE; else error (c2m_ctx, n->pos, "double with short"); break; case N_ID: { node_t def = find_def (c2m_ctx, S_REGULAR, n, curr_scope, NULL); decl_t decl; set_type_pos_node (type, n); if (def == NULL) { error (c2m_ctx, n->pos, "unknown type %s", n->u.s.s); init_type (type); type->mode = TM_BASIC; type->u.basic_type = TP_INT; } else { assert (def->code == N_SPEC_DECL); decl = def->attr; decl->used_p = TRUE; assert (decl->decl_spec.typedef_p); *type = *decl->decl_spec.type; } break; } case N_STRUCT: case N_UNION: { int new_scope_p; node_t res, id = NL_HEAD (n->ops); node_t decl_list = NL_NEXT (id); node_t saved_unnamed_anon_struct_union_member = curr_unnamed_anon_struct_union_member; set_type_pos_node (type, n); res = process_tag (c2m_ctx, n, id, decl_list); check_type_duplication (c2m_ctx, type, n, n->code == N_STRUCT ? "struct" : "union", size, sign); type->mode = n->code == N_STRUCT ? TM_STRUCT : TM_UNION; type->u.tag_type = res; new_scope_p = (id->code != N_IGNORE || decl->code != N_MEMBER || NL_EL (decl->ops, 1)->code != N_IGNORE); type->unnamed_anon_struct_union_member_type_p = !new_scope_p; curr_unnamed_anon_struct_union_member = new_scope_p ? NULL : decl; if (decl_list->code != N_IGNORE) { if (new_scope_p) create_node_scope (c2m_ctx, res); check (c2m_ctx, decl_list, n); if (new_scope_p) finish_scope (c2m_ctx); if (res != n) make_type_complete (c2m_ctx, type); /* recalculate size */ } curr_unnamed_anon_struct_union_member = saved_unnamed_anon_struct_union_member; break; } case N_ENUM: { node_t res, id = NL_HEAD (n->ops); node_t enum_list = NL_NEXT (id); set_type_pos_node (type, n); res = process_tag (c2m_ctx, n, id, enum_list); check_type_duplication (c2m_ctx, type, n, "enum", size, sign); type->mode = TM_ENUM; type->u.tag_type = res; if (enum_list->code == N_IGNORE) { if (incomplete_type_p (c2m_ctx, type)) error (c2m_ctx, n->pos, "enum storage size is unknown"); } else { mir_int curr_val = 0; for (node_t en = NL_HEAD (enum_list->ops); en != NULL; en = NL_NEXT (en)) { // ??? id node_t id, const_expr; symbol_t sym; struct enum_value *enum_value; assert (en->code == N_ENUM_CONST); id = NL_HEAD (en->ops); const_expr = NL_NEXT (id); check (c2m_ctx, const_expr, n); if (symbol_find (c2m_ctx, S_REGULAR, id, curr_scope, &sym)) { error (c2m_ctx, id->pos, "enum constant %s redeclaration", id->u.s.s); } else { symbol_insert (c2m_ctx, S_REGULAR, id, curr_scope, en, n); } if (const_expr->code != N_IGNORE) { struct expr *cexpr = const_expr->attr; if (!cexpr->const_p) error (c2m_ctx, const_expr->pos, "non-constant value in enum const expression"); else if (!integer_type_p (cexpr->type)) error (c2m_ctx, const_expr->pos, "enum const expression is not of an integer type"); else if ((signed_integer_type_p (cexpr->type) && cexpr->u.i_val > MIR_INT_MAX) || (!signed_integer_type_p (cexpr->type) && cexpr->u.u_val > MIR_INT_MAX)) error (c2m_ctx, const_expr->pos, "enum const expression is not represented by int"); else curr_val = cexpr->u.i_val; } en->attr = enum_value = reg_malloc (c2m_ctx, sizeof (struct enum_value)); enum_value->val = curr_val; curr_val++; } } break; } case N_ALIGNAS: { node_t el; int align = -1; if (decl->code == N_FUNC_DEF) { error (c2m_ctx, n->pos, "_Alignas for function"); } else if (decl->code == N_MEMBER && (el = NL_EL (decl->ops, 3)) != NULL && el->code != N_IGNORE) { error (c2m_ctx, n->pos, "_Alignas for a bit-field"); } else if (decl->code == N_SPEC_DECL && in_params_p) { error (c2m_ctx, n->pos, "_Alignas for a function parameter"); } else { node_t op = NL_HEAD (n->ops); check (c2m_ctx, op, n); if (op->code == N_TYPE) { struct decl_spec *decl_spec = op->attr; align = type_align (decl_spec->type); } else { struct expr *cexpr = op->attr; if (!cexpr->const_p) { error (c2m_ctx, op->pos, "non-constant value in _Alignas"); } else if (!integer_type_p (cexpr->type)) { error (c2m_ctx, op->pos, "constant value in _Alignas is not of an integer type"); } else if (!signed_integer_type_p (cexpr->type) || !supported_alignment_p (cexpr->u.i_val)) { error (c2m_ctx, op->pos, "constant value in _Alignas specifies unspported alignment"); } else if (invalid_alignment (cexpr->u.i_val)) { error (c2m_ctx, op->pos, "unsupported alignmnent"); } else { align = cexpr->u.i_val; } } if (align != 0 && res->align < align) { res->align = align; res->align_node = n; } } break; } default: abort (); } if (type->mode == TM_BASIC && type->u.basic_type == TP_UNDEF) { if (size == 0 && sign == 0) { (options->pedantic_p ? error (c2m_ctx, r->pos, "no any type specifier") : warning (c2m_ctx, r->pos, "type defaults to int")); type->u.basic_type = TP_INT; } else if (size == 0) { type->u.basic_type = sign >= 0 ? TP_INT : TP_UINT; } else if (size == 1) { type->u.basic_type = sign >= 0 ? TP_SHORT : TP_USHORT; } else if (size == 2) { type->u.basic_type = sign >= 0 ? TP_LONG : TP_ULONG; } else { type->u.basic_type = sign >= 0 ? TP_LLONG : TP_ULLONG; } } set_type_qual (c2m_ctx, r, &type->type_qual, type->mode); if (res->align_node) { if (res->typedef_p) error (c2m_ctx, res->align_node->pos, "_Alignas in typedef"); else if (res->register_p) error (c2m_ctx, res->align_node->pos, "_Alignas with register"); } return *res; } static struct type *append_type (struct type *head, struct type *el) { struct type **holder; if (head == NULL) return el; if (head->mode == TM_PTR) { holder = &head->u.ptr_type; } else if (head->mode == TM_ARR) { holder = &head->u.arr_type->el_type; } else { assert (head->mode == TM_FUNC); holder = &head->u.func_type->ret_type; } *holder = append_type (*holder, el); return head; } static int void_param_p (node_t param) { struct decl_spec *decl_spec; struct type *type; if (param != NULL && param->code == N_TYPE) { decl_spec = param->attr; type = decl_spec->type; if (void_type_p (type)) return TRUE; } return FALSE; } static void adjust_param_type (c2m_ctx_t c2m_ctx, struct type **type_ptr) { struct type *par_type, *type = *type_ptr; struct arr_type *arr_type; if (type->mode == TM_ARR) { // ??? static, old type qual arr_type = type->u.arr_type; par_type = create_type (c2m_ctx, NULL); par_type->mode = TM_PTR; par_type->pos_node = type->pos_node; par_type->u.ptr_type = arr_type->el_type; par_type->type_qual = arr_type->ind_type_qual; par_type->arr_type = type; *type_ptr = type = par_type; make_type_complete (c2m_ctx, type); } else if (type->mode == TM_FUNC) { par_type = create_type (c2m_ctx, NULL); par_type->mode = TM_PTR; par_type->pos_node = type->pos_node; par_type->u.ptr_type = type; *type_ptr = type = par_type; make_type_complete (c2m_ctx, type); } } static struct type *check_declarator (c2m_ctx_t c2m_ctx, node_t r, int func_def_p) { struct type *type, *res = NULL; node_t list = NL_EL (r->ops, 1); assert (r->code == N_DECL); if (NL_HEAD (list->ops) == NULL) return NULL; for (node_t n = NL_HEAD (list->ops); n != NULL; n = NL_NEXT (n)) { type = create_type (c2m_ctx, NULL); type->pos_node = n; switch (n->code) { case N_POINTER: { node_t type_qual = NL_HEAD (n->ops); type->mode = TM_PTR; type->pos_node = n; type->u.ptr_type = NULL; set_type_qual (c2m_ctx, type_qual, &type->type_qual, TM_PTR); break; } case N_ARR: { struct arr_type *arr_type; node_t static_node = NL_HEAD (n->ops); node_t type_qual = NL_NEXT (static_node); node_t size = NL_NEXT (type_qual); type->mode = TM_ARR; type->pos_node = n; type->u.arr_type = arr_type = reg_malloc (c2m_ctx, sizeof (struct arr_type)); clear_type_qual (&arr_type->ind_type_qual); set_type_qual (c2m_ctx, type_qual, &arr_type->ind_type_qual, TM_UNDEF); check (c2m_ctx, size, n); arr_type->size = size; arr_type->static_p = static_node->code == N_STATIC; arr_type->el_type = NULL; break; } case N_FUNC: { struct func_type *func_type; node_t first_param, param_list = NL_HEAD (n->ops); node_t last = NL_TAIL (param_list->ops); int saved_in_params_p = in_params_p; type->mode = TM_FUNC; type->pos_node = n; type->u.func_type = func_type = reg_malloc (c2m_ctx, sizeof (struct func_type)); func_type->ret_type = NULL; func_type->proto_item = NULL; if ((func_type->dots_p = last != NULL && last->code == N_DOTS)) NL_REMOVE (param_list->ops, last); if (!func_def_p) create_node_scope (c2m_ctx, n); func_type->param_list = param_list; in_params_p = TRUE; first_param = NL_HEAD (param_list->ops); if (first_param != NULL && first_param->code != N_ID) check (c2m_ctx, first_param, n); if (void_param_p (first_param)) { struct decl_spec *ds = first_param->attr; if (non_reg_decl_spec_p (ds) || ds->register_p || !type_qual_eq_p (&ds->type->type_qual, &zero_type_qual)) { error (c2m_ctx, first_param->pos, "qualified void parameter"); } if (NL_NEXT (first_param) != NULL) { error (c2m_ctx, first_param->pos, "void must be the only parameter"); } } else { for (node_t p = first_param; p != NULL; p = NL_NEXT (p)) { struct decl_spec *decl_spec_ptr; if (p->code == N_ID) { if (!func_def_p) error (c2m_ctx, p->pos, "parameters identifier list can be only in function definition"); break; } else { if (p != first_param) check (c2m_ctx, p, n); decl_spec_ptr = get_param_decl_spec (p); adjust_param_type (c2m_ctx, &decl_spec_ptr->type); } } } in_params_p = saved_in_params_p; if (!func_def_p) finish_scope (c2m_ctx); break; } default: abort (); } res = append_type (res, type); } return res; } static int check_case_expr (c2m_ctx_t c2m_ctx, node_t case_expr, struct type *type, node_t target) { struct expr *expr; check (c2m_ctx, case_expr, target); expr = case_expr->attr; if (!expr->const_p) { error (c2m_ctx, case_expr->pos, "case-expr is not a constant expression"); return FALSE; } else if (!integer_type_p (expr->type)) { error (c2m_ctx, case_expr->pos, "case-expr is not an integer type expression"); return FALSE; } else { convert_value (expr, type); return TRUE; } } static void check_labels (c2m_ctx_t c2m_ctx, node_t labels, node_t target) { for (node_t l = NL_HEAD (labels->ops); l != NULL; l = NL_NEXT (l)) { if (l->code == N_LABEL) { symbol_t sym; node_t id = NL_HEAD (l->ops); if (symbol_find (c2m_ctx, S_LABEL, id, func_block_scope, &sym)) { error (c2m_ctx, id->pos, "label %s redeclaration", id->u.s.s); } else { symbol_insert (c2m_ctx, S_LABEL, id, func_block_scope, target, NULL); } } else if (curr_switch == NULL) { error (c2m_ctx, l->pos, "%s not within a switch-stmt", l->code == N_CASE ? "case label" : "default label"); } else { struct switch_attr *switch_attr = curr_switch->attr; struct type *type = &switch_attr->type; node_t case_expr = l->code == N_CASE ? NL_HEAD (l->ops) : NULL; node_t case_expr2 = l->code == N_CASE ? NL_EL (l->ops, 1) : NULL; case_t case_attr, tail = DLIST_TAIL (case_t, switch_attr->case_labels); int ok_p = FALSE, default_p = tail != NULL && tail->case_node->code == N_DEFAULT; if (case_expr == NULL) { if (default_p) { error (c2m_ctx, l->pos, "multiple default labels in one switch"); } else { ok_p = TRUE; } } else { ok_p = check_case_expr (c2m_ctx, case_expr, type, target); if (case_expr2 != NULL) { ok_p = check_case_expr (c2m_ctx, case_expr2, type, target) && ok_p; (options->pedantic_p ? error : warning) (c2m_ctx, l->pos, "range cases are not a part of C standard"); } } if (ok_p) { case_attr = reg_malloc (c2m_ctx, sizeof (struct case_attr)); case_attr->case_node = l; case_attr->case_target_node = target; if (default_p) { DLIST_INSERT_BEFORE (case_t, switch_attr->case_labels, tail, case_attr); } else { DLIST_APPEND (case_t, switch_attr->case_labels, case_attr); } } } } } static node_code_t get_id_linkage (c2m_ctx_t c2m_ctx, int func_p, node_t id, node_t scope, struct decl_spec decl_spec) { node_code_t linkage; node_t def = find_def (c2m_ctx, S_REGULAR, id, scope, NULL); if (decl_spec.typedef_p) return N_IGNORE; // p6: no linkage if (decl_spec.static_p && scope == top_scope) return N_STATIC; // p3: internal linkage if (decl_spec.extern_p && def != NULL && (linkage = ((decl_t) def->attr)->decl_spec.linkage) != N_IGNORE) return linkage; // p4: previous linkage if (decl_spec.extern_p && (def == NULL || ((decl_t) def->attr)->decl_spec.linkage == N_IGNORE)) return N_EXTERN; // p4: external linkage if (!decl_spec.static_p && !decl_spec.extern_p && (scope == top_scope || func_p)) return N_EXTERN; // p5 if (!decl_spec.extern_p && scope != top_scope && !func_p) return N_IGNORE; // p6: no linkage return N_IGNORE; } static void check_type (c2m_ctx_t c2m_ctx, struct type *type, int level, int func_def_p) { switch (type->mode) { case TM_PTR: check_type (c2m_ctx, type->u.ptr_type, level + 1, FALSE); break; case TM_STRUCT: case TM_UNION: break; case TM_ARR: { struct arr_type *arr_type = type->u.arr_type; node_t size_node = arr_type->size; struct type *el_type = arr_type->el_type; if (size_node->code == N_STAR) { error (c2m_ctx, size_node->pos, "variable size arrays are not supported"); } else if (size_node->code != N_IGNORE) { struct expr *cexpr = size_node->attr; if (!integer_type_p (cexpr->type)) { error (c2m_ctx, size_node->pos, "non-integer array size type"); } else if (!cexpr->const_p) { error (c2m_ctx, size_node->pos, "variable size arrays are not supported"); } else if ((signed_integer_type_p (cexpr->type) && cexpr->u.i_val <= 0) || (!signed_integer_type_p (cexpr->type) && cexpr->u.u_val == 0)) { error (c2m_ctx, size_node->pos, "array size should be positive"); } } check_type (c2m_ctx, el_type, level + 1, FALSE); if (el_type->mode == TM_FUNC) { error (c2m_ctx, type->pos_node->pos, "array of functions"); } else if (incomplete_type_p (c2m_ctx, el_type)) { error (c2m_ctx, type->pos_node->pos, "incomplete array element type"); } else if (!in_params_p || level != 0) { if (arr_type->static_p) error (c2m_ctx, type->pos_node->pos, "static should be only in parameter outermost"); else if (!type_qual_eq_p (&arr_type->ind_type_qual, &zero_type_qual)) error (c2m_ctx, type->pos_node->pos, "type qualifiers should be only in parameter outermost array"); } break; } case TM_FUNC: { struct decl_spec decl_spec; struct func_type *func_type = type->u.func_type; struct type *ret_type = func_type->ret_type; node_t first_param, param_list = func_type->param_list; check_type (c2m_ctx, ret_type, level + 1, FALSE); if (ret_type->mode == TM_FUNC) { error (c2m_ctx, ret_type->pos_node->pos, "function returning a function"); } else if (ret_type->mode == TM_ARR) { error (c2m_ctx, ret_type->pos_node->pos, "function returning an array"); } first_param = NL_HEAD (param_list->ops); if (!void_param_p (first_param)) { for (node_t p = first_param; p != NULL; p = NL_NEXT (p)) { if (p->code == N_TYPE) { decl_spec = *((struct decl_spec *) p->attr); check_type (c2m_ctx, decl_spec.type, level + 1, FALSE); } else if (p->code == N_SPEC_DECL) { decl_spec = ((decl_t) p->attr)->decl_spec; check_type (c2m_ctx, decl_spec.type, level + 1, FALSE); } else { assert (p->code == N_ID); break; } if (non_reg_decl_spec_p (&decl_spec)) { error (c2m_ctx, p->pos, "prohibited specifier in a function parameter"); } else if (func_def_p) { if (p->code == N_TYPE) error (c2m_ctx, p->pos, "parameter type without a name in function definition"); else if (incomplete_type_p (c2m_ctx, decl_spec.type)) error (c2m_ctx, p->pos, "incomplete parameter type in function definition"); } } } break; } default: break; // ??? } } static void check_assignment_types (c2m_ctx_t c2m_ctx, struct type *left, struct type *right, struct expr *expr, node_t assign_node) { node_code_t code = assign_node->code; pos_t pos = assign_node->pos; const char *msg; if (right == NULL) right = expr->type; if (arithmetic_type_p (left)) { if (!arithmetic_type_p (right) && !(left->mode == TM_BASIC && left->u.basic_type == TP_BOOL && right->mode == TM_PTR)) { if (integer_type_p (left) && right->mode == TM_PTR) { msg = (code == N_CALL ? "using pointer without cast for integer type parameter" : code == N_RETURN ? "returning pointer without cast for integer result" : "assigning pointer without cast to integer"); (options->pedantic_p ? error : warning) (c2m_ctx, pos, "%s", msg); } else { msg = (code == N_CALL ? "incompatible argument type for arithemtic type parameter" : code != N_RETURN ? "incompatible types in assignment to an arithemtic type lvalue" : "incompatible return-expr type in function returning an arithemtic value"); error (c2m_ctx, pos, "%s", msg); } } } else if (left->mode == TM_STRUCT || left->mode == TM_UNION) { if ((right->mode != TM_STRUCT && right->mode != TM_UNION) || !compatible_types_p (left, right, TRUE)) { msg = (code == N_CALL ? "incompatible argument type for struct/union type parameter" : code != N_RETURN ? "incompatible types in assignment to struct/union" : "incompatible return-expr type in function returning a struct/union"); error (c2m_ctx, pos, "%s", msg); } } else if (left->mode == TM_PTR) { if (null_const_p (expr, right)) { } else if (right->mode != TM_PTR || (!compatible_types_p (left->u.ptr_type, right->u.ptr_type, TRUE) && !void_ptr_p (left) && !void_ptr_p (right))) { if (right->mode == TM_PTR && left->u.ptr_type->mode == TM_BASIC && right->u.ptr_type->mode == TM_BASIC) { msg = (code == N_CALL ? "incompatible pointer types of argument and parameter" : code == N_RETURN ? "incompatible pointer types of return-expr and function result" : "incompatible pointer types in assignment"); (options->pedantic_p ? error : warning) (c2m_ctx, pos, "%s", msg); } else if (integer_type_p (right)) { msg = (code == N_CALL ? "using integer without cast for pointer type parameter" : code == N_RETURN ? "returning integer without cast for pointer result" : "assigning integer without cast to pointer"); (options->pedantic_p ? error : warning) (c2m_ctx, pos, "%s", msg); } else { msg = (code == N_CALL ? "incompatible argument type for pointer type parameter" : code == N_RETURN ? "incompatible return-expr type in function returning a pointer" : "incompatible types in assignment to a pointer"); (options->pedantic_p || right->mode != TM_PTR ? error : warning) (c2m_ctx, pos, "%s", msg); } } else if (right->u.ptr_type->type_qual.atomic_p) { msg = (code == N_CALL ? "passing a pointer of an atomic type" : code == N_RETURN ? "returning a pointer of an atomic type" : "assignment of pointer of an atomic type"); error (c2m_ctx, pos, "%s", msg); } else if (!type_qual_subset_p (&right->u.ptr_type->type_qual, &left->u.ptr_type->type_qual)) { msg = (code == N_CALL ? "discarding type qualifiers in passing argument" : code == N_RETURN ? "return discards a type qualifier from a pointer" : "assignment discards a type qualifier from a pointer"); (options->pedantic_p ? error : warning) (c2m_ctx, pos, "%s", msg); } } } static int anon_struct_union_type_member_p (node_t member) { decl_t decl = member->attr; return decl != NULL && decl->decl_spec.type->unnamed_anon_struct_union_member_type_p; } static node_t get_adjacent_member (node_t member, int next_p) { assert (member->code == N_MEMBER); while ((member = next_p ? NL_NEXT (member) : NL_PREV (member)) != NULL) if (member->code == N_MEMBER && (NL_EL (member->ops, 1)->code != N_IGNORE || anon_struct_union_type_member_p (member))) break; return member; } static int update_init_object_path (c2m_ctx_t c2m_ctx, size_t mark, struct type *value_type, int list_p) { init_object_t init_object; struct type *el_type; node_t size_node; mir_llong size_val; struct expr *sexpr; for (;;) { for (;;) { if (mark == VARR_LENGTH (init_object_t, init_object_path)) return FALSE; init_object = VARR_LAST (init_object_t, init_object_path); if (init_object.container_type->mode == TM_ARR) { el_type = init_object.container_type->u.arr_type->el_type; size_node = init_object.container_type->u.arr_type->size; sexpr = size_node->attr; size_val = (size_node->code != N_IGNORE && sexpr->const_p && integer_type_p (sexpr->type) ? sexpr->u.i_val : -1); init_object.u.curr_index++; if (size_val < 0 || init_object.u.curr_index < size_val) break; VARR_POP (init_object_t, init_object_path); } else { assert (init_object.container_type->mode == TM_STRUCT || init_object.container_type->mode == TM_UNION); if (init_object.u.curr_member == NULL) { /* finding the first named member */ node_t declaration_list = NL_EL (init_object.container_type->u.tag_type->ops, 1); assert (declaration_list != NULL && declaration_list->code == N_LIST); for (init_object.u.curr_member = NL_HEAD (declaration_list->ops); init_object.u.curr_member != NULL && (init_object.u.curr_member->code != N_MEMBER || (NL_EL (init_object.u.curr_member->ops, 1)->code == N_IGNORE && !anon_struct_union_type_member_p (init_object.u.curr_member))); init_object.u.curr_member = NL_NEXT (init_object.u.curr_member)) ; } else if (init_object.container_type->mode == TM_UNION && !init_object.designator_p) { /* no next union member: */ init_object.u.curr_member = NULL; } else { /* finding the next named struct member: */ init_object.u.curr_member = get_adjacent_member (init_object.u.curr_member, TRUE); } if (init_object.u.curr_member != NULL) { init_object.designator_p = FALSE; el_type = ((decl_t) init_object.u.curr_member->attr)->decl_spec.type; break; } VARR_POP (init_object_t, init_object_path); } } VARR_SET (init_object_t, init_object_path, VARR_LENGTH (init_object_t, init_object_path) - 1, init_object); if (list_p || scalar_type_p (el_type)) return TRUE; assert (el_type->mode == TM_ARR || el_type->mode == TM_STRUCT || el_type->mode == TM_UNION); if (el_type->mode != TM_ARR && value_type != NULL && el_type->u.tag_type == value_type->u.tag_type) return TRUE; init_object.container_type = el_type; init_object.designator_p = FALSE; if (el_type->mode == TM_ARR) { init_object.u.curr_index = -1; } else { init_object.u.curr_member = NULL; } VARR_PUSH (init_object_t, init_object_path, init_object); } } static int update_path_and_do (c2m_ctx_t c2m_ctx, void (*action) (c2m_ctx_t c2m_ctx, decl_t member_decl, struct type **type_ptr, node_t initializer, int const_only_p, int top_p), size_t mark, node_t value, int const_only_p, mir_llong *max_index, pos_t pos, const char *detail) { init_object_t init_object; mir_llong index; struct type *el_type; struct expr *value_expr = value->attr; if (!update_init_object_path (c2m_ctx, mark, value_expr == NULL ? NULL : value_expr->type, value->code == N_LIST || value->code == N_COMPOUND_LITERAL)) { error (c2m_ctx, pos, "excess elements in %s initializer", detail); return FALSE; } init_object = VARR_LAST (init_object_t, init_object_path); if (init_object.container_type->mode == TM_ARR) { el_type = init_object.container_type->u.arr_type->el_type; action (c2m_ctx, NULL, (value->code == N_STR && char_type_p (el_type) ? &init_object.container_type : &init_object.container_type->u.arr_type->el_type), value, const_only_p, FALSE); } else if (init_object.container_type->mode == TM_STRUCT || init_object.container_type->mode == TM_UNION) { action (c2m_ctx, (decl_t) init_object.u.curr_member->attr, &((decl_t) init_object.u.curr_member->attr)->decl_spec.type, value, const_only_p, FALSE); } if (max_index != NULL) { init_object = VARR_GET (init_object_t, init_object_path, mark); if (init_object.container_type->mode == TM_ARR && *max_index < (index = init_object.u.curr_index)) *max_index = index; } return TRUE; } static int check_const_addr_p (c2m_ctx_t c2m_ctx, node_t r, node_t *base, mir_llong *offset, int *deref) { struct expr *e = r->attr; struct type *type; node_t op1, op2, temp; decl_t decl; struct decl_spec *decl_spec; mir_size_t size; if (e->const_p && integer_type_p (e->type)) { *base = NULL; *offset = (mir_size_t) e->u.u_val; *deref = 0; return TRUE; } switch (r->code) { case N_STR: *base = r; *offset = 0; *deref = 0; return curr_scope == top_scope; case N_ID: if (e->def_node == NULL) return FALSE; else if (e->def_node->code == N_FUNC_DEF || (e->def_node->code == N_SPEC_DECL && ((decl_t) e->def_node->attr)->decl_spec.type->mode == TM_FUNC)) { *base = e->def_node; *deref = 0; } else if (e->lvalue_node == NULL || ((decl = e->lvalue_node->attr)->scope != top_scope && decl->decl_spec.linkage != N_IGNORE)) { return FALSE; } else { *base = e->def_node; *deref = e->type->arr_type == NULL; } *offset = 0; return TRUE; case N_DEREF: case N_ADDR: { node_t op = NL_HEAD (r->ops); struct expr *e = op->attr; if (!check_const_addr_p (c2m_ctx, op, base, offset, deref)) return FALSE; if (op->code != N_ID || (e->def_node->code != N_FUNC_DEF && (e->def_node->code != N_SPEC_DECL || ((decl_t) e->def_node->attr)->decl_spec.type->mode != TM_FUNC))) r->code == N_DEREF ? (*deref)++ : (*deref)--; return TRUE; } case N_FIELD: case N_DEREF_FIELD: if (!check_const_addr_p (c2m_ctx, NL_HEAD (r->ops), base, offset, deref)) return FALSE; if (*deref != (r->code == N_FIELD ? 1 : 0)) return FALSE; *deref = 1; e = r->attr; decl = e->lvalue_node->attr; *offset += decl->offset; return TRUE; case N_IND: if (((struct expr *) NL_HEAD (r->ops)->attr)->type->mode != TM_PTR) return FALSE; if (!check_const_addr_p (c2m_ctx, NL_HEAD (r->ops), base, offset, deref)) return FALSE; if (!(e = NL_EL (r->ops, 1)->attr)->const_p) return FALSE; type = ((struct expr *) r->attr)->type; size = type_size (c2m_ctx, type->arr_type != NULL ? type->arr_type : type); *deref = 1; *offset += e->u.i_val * size; return TRUE; case N_ADD: case N_SUB: if ((op2 = NL_EL (r->ops, 1)) == NULL) return FALSE; op1 = NL_HEAD (r->ops); if (r->code == N_ADD && (e = op1->attr)->const_p) SWAP (op1, op2, temp); if (!check_const_addr_p (c2m_ctx, op1, base, offset, deref)) return FALSE; if (*deref != 0 && ((struct expr *) op1->attr)->type->arr_type == NULL) return FALSE; if (!(e = op2->attr)->const_p) return FALSE; type = ((struct expr *) r->attr)->type; assert (type->mode == TM_BASIC || type->mode == TM_PTR); size = (type->mode == TM_BASIC || type->u.ptr_type->mode == TM_FUNC ? 1 : type_size (c2m_ctx, type->u.ptr_type->arr_type != NULL ? type->u.ptr_type->arr_type : type->u.ptr_type)); if (r->code == N_ADD) *offset += e->u.i_val * size; else *offset -= e->u.i_val * size; return TRUE; case N_CAST: decl_spec = NL_HEAD (r->ops)->attr; if (type_size (c2m_ctx, decl_spec->type) != sizeof (mir_size_t)) return FALSE; return check_const_addr_p (c2m_ctx, NL_EL (r->ops, 1), base, offset, deref); default: return FALSE; } } static void setup_const_addr_p (c2m_ctx_t c2m_ctx, node_t r) { node_t base; mir_llong offset; int deref; struct expr *e; if (!check_const_addr_p (c2m_ctx, r, &base, &offset, &deref) || deref != 0) return; e = r->attr; e->const_addr_p = TRUE; e->def_node = base; e->u.i_val = offset; } static void process_init_field_designator (c2m_ctx_t c2m_ctx, node_t designator_member, struct type *container_type) { decl_t decl; init_object_t init_object; node_t curr_member; assert (designator_member->code == N_MEMBER); /* We can have *partial* path of containing anon members: pop them */ while (VARR_LENGTH (init_object_t, init_object_path) != 0) { init_object = VARR_LAST (init_object_t, init_object_path); if ((decl = init_object.u.curr_member->attr) == NULL || !decl->decl_spec.type->unnamed_anon_struct_union_member_type_p) { break; } container_type = init_object.container_type; VARR_POP (init_object_t, init_object_path); } /* Now add *full* path to designator_member of containing anon members */ assert (VARR_LENGTH (node_t, containing_anon_members) == 0); decl = designator_member->attr; for (curr_member = decl->containing_unnamed_anon_struct_union_member; curr_member != NULL; curr_member = decl->containing_unnamed_anon_struct_union_member) { decl = curr_member->attr; VARR_PUSH (node_t, containing_anon_members, curr_member); } while (VARR_LENGTH (node_t, containing_anon_members) != 0) { init_object.u.curr_member = VARR_POP (node_t, containing_anon_members); init_object.container_type = container_type; init_object.designator_p = FALSE; VARR_PUSH (init_object_t, init_object_path, init_object); container_type = (decl = init_object.u.curr_member->attr)->decl_spec.type; } init_object.u.curr_member = get_adjacent_member (designator_member, FALSE); init_object.container_type = container_type; init_object.designator_p = TRUE; VARR_PUSH (init_object_t, init_object_path, init_object); } static node_t get_compound_literal (node_t n, int *addr_p) { for (int addr = 0; n != NULL; n = NL_HEAD (n->ops)) { switch (n->code) { case N_ADDR: addr++; break; case N_DEREF: addr--; break; case N_CAST: break; // ??? case N_STR: case N_COMPOUND_LITERAL: if (addr < 0) return NULL; *addr_p = addr > 0; return n; break; default: return NULL; } if (addr != -1 && addr != 0 && addr != 1) return NULL; } return NULL; } static void check_initializer (c2m_ctx_t c2m_ctx, decl_t member_decl, struct type **type_ptr, node_t initializer, int const_only_p, int top_p) { struct type *type = *type_ptr; struct expr *cexpr; node_t literal, des_list, curr_des, init, str, value, size_node, temp; mir_llong max_index; mir_llong size_val = 0; /* to remove an uninitialized warning */ size_t mark, len; symbol_t sym; struct expr *sexpr; init_object_t init_object; int addr_p = FALSE; /* to remove an uninitialized warning */ literal = get_compound_literal (initializer, &addr_p); if (literal != NULL && !addr_p && initializer->code != N_STR) { cexpr = initializer->attr; check_assignment_types (c2m_ctx, type, NULL, cexpr, initializer); initializer = NL_EL (literal->ops, 1); } check_one_value: if (initializer->code != N_LIST && !(initializer->code == N_STR && type->mode == TM_ARR && char_type_p (type->u.arr_type->el_type))) { if ((cexpr = initializer->attr)->const_p || initializer->code == N_STR || !const_only_p) { check_assignment_types (c2m_ctx, type, NULL, cexpr, initializer); } else { setup_const_addr_p (c2m_ctx, initializer); if ((cexpr = initializer->attr)->const_addr_p || (literal != NULL && addr_p)) check_assignment_types (c2m_ctx, type, NULL, cexpr, initializer); else error (c2m_ctx, initializer->pos, "initializer of non-auto or thread local object" " should be a constant expression or address"); } return; } init = NL_HEAD (initializer->ops); if (((str = initializer)->code == N_STR /* string or string in parentheses */ || (init->code == N_INIT && NL_EL (initializer->ops, 1) == NULL && (des_list = NL_HEAD (init->ops))->code == N_LIST && NL_HEAD (des_list->ops) == NULL && NL_EL (init->ops, 1) != NULL && (str = NL_EL (init->ops, 1))->code == N_STR)) && type->mode == TM_ARR && char_type_p (type->u.arr_type->el_type)) { len = str->u.s.len; if (incomplete_type_p (c2m_ctx, type)) { assert (len < MIR_INT_MAX); type->u.arr_type->size = new_i_node (c2m_ctx, len, type->u.arr_type->size->pos); check (c2m_ctx, type->u.arr_type->size, NULL); make_type_complete (c2m_ctx, type); } else if (len > ((struct expr *) type->u.arr_type->size->attr)->u.i_val + 1) { error (c2m_ctx, initializer->pos, "string is too long for array initializer"); } return; } assert (init->code == N_INIT); des_list = NL_HEAD (init->ops); assert (des_list->code == N_LIST); if (type->mode != TM_ARR && type->mode != TM_STRUCT && type->mode != TM_UNION) { if ((temp = NL_NEXT (init)) != NULL) { error (c2m_ctx, temp->pos, "excess elements in scalar initializer"); return; } if ((temp = NL_HEAD (des_list->ops)) != NULL) { error (c2m_ctx, temp->pos, "designator in scalar initializer"); return; } initializer = NL_NEXT (des_list); if (!top_p) { error (c2m_ctx, init->pos, "braces around scalar initializer"); return; } top_p = FALSE; goto check_one_value; } mark = VARR_LENGTH (init_object_t, init_object_path); init_object.container_type = type; init_object.designator_p = FALSE; if (type->mode == TM_ARR) { size_node = type->u.arr_type->size; sexpr = size_node->attr; size_val = (size_node->code != N_IGNORE && sexpr->const_p && integer_type_p (sexpr->type) ? sexpr->u.i_val : -1); init_object.u.curr_index = -1; } else { init_object.u.curr_member = NULL; } VARR_PUSH (init_object_t, init_object_path, init_object); max_index = -1; for (; init != NULL; init = NL_NEXT (init)) { assert (init->code == N_INIT); des_list = NL_HEAD (init->ops); value = NL_NEXT (des_list); if ((value->code == N_LIST || value->code == N_COMPOUND_LITERAL) && type->mode != TM_ARR && type->mode != TM_STRUCT && type->mode != TM_UNION) { error (c2m_ctx, init->pos, value->code == N_LIST ? "braces around scalar initializer" : "compound literal for scalar initializer"); break; } if ((curr_des = NL_HEAD (des_list->ops)) == NULL) { if (!update_path_and_do (c2m_ctx, check_initializer, mark, value, const_only_p, &max_index, init->pos, "array/struct/union")) break; } else { for (; curr_des != NULL; curr_des = NL_NEXT (curr_des)) { VARR_TRUNC (init_object_t, init_object_path, mark + 1); init_object = VARR_POP (init_object_t, init_object_path); if (curr_des->code == N_FIELD_ID) { node_t id = NL_HEAD (curr_des->ops); if (type->mode != TM_STRUCT && type->mode != TM_UNION) { error (c2m_ctx, curr_des->pos, "field name not in struct or union initializer"); } else if (!symbol_find (c2m_ctx, S_REGULAR, id, type->u.tag_type, &sym)) { error (c2m_ctx, curr_des->pos, "unknown field %s in initializer", id->u.s.s); } else { process_init_field_designator (c2m_ctx, sym.def_node, init_object.container_type); if (!update_path_and_do (c2m_ctx, check_initializer, mark, value, const_only_p, NULL, init->pos, "struct/union")) break; } } else if (type->mode != TM_ARR) { error (c2m_ctx, curr_des->pos, "array index in initializer for non-array"); } else if (!(cexpr = curr_des->attr)->const_p) { error (c2m_ctx, curr_des->pos, "nonconstant array index in initializer"); } else if (!integer_type_p (cexpr->type)) { error (c2m_ctx, curr_des->pos, "array index in initializer not of integer type"); } else if (incomplete_type_p (c2m_ctx, type) && signed_integer_type_p (cexpr->type) && cexpr->u.i_val < 0) { error (c2m_ctx, curr_des->pos, "negative array index in initializer for array without size"); } else if (size_val >= 0 && size_val <= cexpr->u.u_val) { error (c2m_ctx, curr_des->pos, "array index in initializer exceeds array bounds"); } else { init_object.u.curr_index = cexpr->u.i_val - 1; /* previous el */ init_object.designator_p = FALSE; VARR_PUSH (init_object_t, init_object_path, init_object); if (!update_path_and_do (c2m_ctx, check_initializer, mark, value, const_only_p, &max_index, init->pos, "array")) break; } } } } if (type->mode == TM_ARR && size_val < 0 && max_index >= 0) { /* Array w/o size: define it. Copy the type as the incomplete type can be shared by declarations with different length initializers. We need only one level of copying as sub-array can not have incomplete type with an initializer. */ struct arr_type *arr_type = reg_malloc (c2m_ctx, sizeof (struct arr_type)); type = create_type (c2m_ctx, type); assert (incomplete_type_p (c2m_ctx, type)); *arr_type = *type->u.arr_type; type->u.arr_type = arr_type; size_node = type->u.arr_type->size; type->u.arr_type->size = (max_index < MIR_INT_MAX ? new_i_node (c2m_ctx, max_index + 1, size_node->pos) : max_index < MIR_LONG_MAX ? new_l_node (c2m_ctx, max_index + 1, size_node->pos) : new_ll_node (c2m_ctx, max_index + 1, size_node->pos)); check (c2m_ctx, type->u.arr_type->size, NULL); make_type_complete (c2m_ctx, type); } VARR_TRUNC (init_object_t, init_object_path, mark); *type_ptr = type; return; } static void check_decl_align (c2m_ctx_t c2m_ctx, struct decl_spec *decl_spec) { if (decl_spec->align < 0) return; if (decl_spec->align < type_align (decl_spec->type)) error (c2m_ctx, decl_spec->align_node->pos, "requested alignment is less than minimum alignment for the type"); } static void init_decl (c2m_ctx_t c2m_ctx, decl_t decl) { decl->addr_p = FALSE; decl->reg_p = decl->used_p = FALSE; decl->offset = 0; decl->bit_offset = -1; decl->scope = curr_scope; decl->containing_unnamed_anon_struct_union_member = curr_unnamed_anon_struct_union_member; decl->item = NULL; decl->c2m_ctx = c2m_ctx; } static void create_decl (c2m_ctx_t c2m_ctx, node_t scope, node_t decl_node, struct decl_spec decl_spec, node_t width, node_t initializer, int param_p) { int func_def_p = decl_node->code == N_FUNC_DEF, func_p = FALSE; node_t id = NULL; /* to remove an uninitialized warning */ node_t list_head, declarator; struct type *type; decl_t decl = reg_malloc (c2m_ctx, sizeof (struct decl)); assert (decl_node->code == N_MEMBER || decl_node->code == N_SPEC_DECL || decl_node->code == N_FUNC_DEF); init_decl (c2m_ctx, decl); decl->scope = scope; decl->decl_spec = decl_spec; decl_node->attr = decl; declarator = NL_EL (decl_node->ops, 1); if (declarator->code == N_IGNORE) { assert (decl_node->code == N_MEMBER); decl->decl_spec.linkage = N_IGNORE; } else { assert (declarator->code == N_DECL); type = check_declarator (c2m_ctx, declarator, func_def_p); decl->decl_spec.type = append_type (type, decl->decl_spec.type); } check_type (c2m_ctx, decl->decl_spec.type, 0, func_def_p); if (declarator->code == N_DECL) { id = NL_HEAD (declarator->ops); list_head = NL_HEAD (NL_NEXT (id)->ops); func_p = !param_p && list_head && list_head->code == N_FUNC; decl->decl_spec.linkage = get_id_linkage (c2m_ctx, func_p, id, scope, decl->decl_spec); } if (declarator->code == N_DECL) { def_symbol (c2m_ctx, S_REGULAR, id, scope, decl_node, decl->decl_spec.linkage); if (scope != top_scope && decl->decl_spec.linkage == N_EXTERN) def_symbol (c2m_ctx, S_REGULAR, id, top_scope, decl_node, N_EXTERN); if (func_p && decl->decl_spec.thread_local_p) { error (c2m_ctx, id->pos, "thread local function declaration"); if (options->message_file != NULL) { if (id->code != N_IGNORE) fprintf (options->message_file, " of %s", id->u.s.s); fprintf (options->message_file, "\n"); } } } if (decl_node->code != N_MEMBER) { set_type_layout (c2m_ctx, decl->decl_spec.type); check_decl_align (c2m_ctx, &decl->decl_spec); if (!decl->decl_spec.typedef_p && decl->scope != top_scope && decl->scope->code != N_FUNC) VARR_PUSH (decl_t, func_decls_for_allocation, decl); } if (initializer == NULL || initializer->code == N_IGNORE) return; if (incomplete_type_p (c2m_ctx, decl->decl_spec.type) && (decl->decl_spec.type->mode != TM_ARR || incomplete_type_p (c2m_ctx, decl->decl_spec.type->u.arr_type->el_type))) { if (decl->decl_spec.type->mode == TM_ARR && decl->decl_spec.type->u.arr_type->el_type->mode == TM_ARR) error (c2m_ctx, initializer->pos, "initialization of incomplete sub-array"); else error (c2m_ctx, initializer->pos, "initialization of incomplete type variable"); return; } if (decl->decl_spec.linkage != N_IGNORE && scope != top_scope) { error (c2m_ctx, initializer->pos, "initialization of %s in block scope with external or internal linkage", id->u.s.s); return; } check (c2m_ctx, initializer, decl_node); check_initializer (c2m_ctx, NULL, &decl->decl_spec.type, initializer, decl->decl_spec.linkage == N_STATIC || decl->decl_spec.linkage == N_EXTERN || decl->decl_spec.thread_local_p || decl->decl_spec.static_p, TRUE); if (decl_node->code != N_MEMBER && !decl->decl_spec.typedef_p && decl->scope != top_scope && decl->scope->code != N_FUNC) /* Process after initilizer because we can make type complete by it. */ VARR_PUSH (decl_t, func_decls_for_allocation, decl); } static struct type *adjust_type (c2m_ctx_t c2m_ctx, struct type *type) { struct type *res; if (type->mode != TM_ARR && type->mode != TM_FUNC) return type; res = create_type (c2m_ctx, NULL); res->mode = TM_PTR; res->pos_node = type->pos_node; if (type->mode == TM_FUNC) { res->u.ptr_type = type; } else { res->arr_type = type; res->u.ptr_type = type->u.arr_type->el_type; res->type_qual = type->u.arr_type->ind_type_qual; } set_type_layout (c2m_ctx, res); return res; } static void process_unop (c2m_ctx_t c2m_ctx, node_t r, node_t *op, struct expr **e, struct type **t, node_t context) { *op = NL_HEAD (r->ops); check (c2m_ctx, *op, context); *e = (*op)->attr; *t = (*e)->type; } static void process_bin_ops (c2m_ctx_t c2m_ctx, node_t r, node_t *op1, node_t *op2, struct expr **e1, struct expr **e2, struct type **t1, struct type **t2, node_t context) { *op1 = NL_HEAD (r->ops); *op2 = NL_NEXT (*op1); check (c2m_ctx, *op1, context); check (c2m_ctx, *op2, context); *e1 = (*op1)->attr; *e2 = (*op2)->attr; *t1 = (*e1)->type; *t2 = (*e2)->type; } static void process_type_bin_ops (c2m_ctx_t c2m_ctx, node_t r, node_t *op1, node_t *op2, struct expr **e2, struct type **t2, node_t context) { *op1 = NL_HEAD (r->ops); *op2 = NL_NEXT (*op1); check (c2m_ctx, *op1, context); check (c2m_ctx, *op2, context); *e2 = (*op2)->attr; *t2 = (*e2)->type; } static struct expr *create_expr (c2m_ctx_t c2m_ctx, node_t r) { struct expr *e = reg_malloc (c2m_ctx, sizeof (struct expr)); r->attr = e; e->type = create_type (c2m_ctx, NULL); e->type2 = NULL; e->type->pos_node = r; e->lvalue_node = NULL; e->const_p = e->const_addr_p = e->builtin_call_p = FALSE; return e; } static struct expr *create_basic_type_expr (c2m_ctx_t c2m_ctx, node_t r, enum basic_type bt) { struct expr *e = create_expr (c2m_ctx, r); e->type->mode = TM_BASIC; e->type->u.basic_type = bt; return e; } static void get_int_node (c2m_ctx_t c2m_ctx, node_t *op, struct expr **e, struct type **t, mir_size_t i) { if (i == 1) { *op = n_i1_node; } else { *op = new_i_node (c2m_ctx, i, no_pos); check (c2m_ctx, *op, NULL); } *e = (*op)->attr; *t = (*e)->type; init_type (*t); (*e)->type->mode = TM_BASIC; (*e)->type->u.basic_type = TP_INT; (*e)->u.i_val = i; // ??? } static struct expr *check_assign_op (c2m_ctx_t c2m_ctx, node_t r, node_t op1, node_t op2, struct expr *e1, struct expr *e2, struct type *t1, struct type *t2) { struct expr *e = NULL; struct expr *te; struct type t, *tt; switch (r->code) { case N_AND: case N_OR: case N_XOR: case N_AND_ASSIGN: case N_OR_ASSIGN: case N_XOR_ASSIGN: e = create_expr (c2m_ctx, r); e->type->mode = TM_BASIC; e->type->u.basic_type = TP_INT; if (!integer_type_p (t1) || !integer_type_p (t2)) { error (c2m_ctx, r->pos, "bitwise operation operands should be of an integer type"); } else { t = arithmetic_conversion (t1, t2); e->type->u.basic_type = t.u.basic_type; if (e1->const_p && e2->const_p) { convert_value (e1, &t); convert_value (e2, &t); e->const_p = TRUE; if (signed_integer_type_p (&t)) e->u.i_val = (r->code == N_AND ? e1->u.i_val & e2->u.i_val : r->code == N_OR ? e1->u.i_val | e2->u.i_val : e1->u.i_val ^ e2->u.i_val); else e->u.u_val = (r->code == N_AND ? e1->u.u_val & e2->u.u_val : r->code == N_OR ? e1->u.u_val | e2->u.u_val : e1->u.u_val ^ e2->u.u_val); } } break; case N_LSH: case N_RSH: case N_LSH_ASSIGN: case N_RSH_ASSIGN: e = create_expr (c2m_ctx, r); e->type->mode = TM_BASIC; e->type->u.basic_type = TP_INT; if (!integer_type_p (t1) || !integer_type_p (t2)) { error (c2m_ctx, r->pos, "shift operands should be of an integer type"); } else { t = integer_promotion (t1); e->type->u.basic_type = t.u.basic_type; if (e1->const_p && e2->const_p) { struct type rt = integer_promotion (t2); convert_value (e1, &t); convert_value (e2, &rt); e->const_p = TRUE; if (signed_integer_type_p (&t)) { if (signed_integer_type_p (&rt)) e->u.i_val = r->code == N_LSH ? e1->u.i_val << e2->u.i_val : e1->u.i_val >> e2->u.i_val; else e->u.i_val = r->code == N_LSH ? e1->u.i_val << e2->u.u_val : e1->u.i_val >> e2->u.u_val; } else if (signed_integer_type_p (&rt)) { e->u.u_val = r->code == N_LSH ? e1->u.u_val << e2->u.i_val : e1->u.u_val >> e2->u.i_val; } else { e->u.u_val = r->code == N_LSH ? e1->u.u_val << e2->u.u_val : e1->u.u_val >> e2->u.u_val; } } } break; case N_INC: case N_DEC: case N_POST_INC: case N_POST_DEC: case N_ADD: case N_SUB: case N_ADD_ASSIGN: case N_SUB_ASSIGN: { mir_size_t size; int add_p = (r->code == N_ADD || r->code == N_ADD_ASSIGN || r->code == N_INC || r->code == N_POST_INC); e = create_expr (c2m_ctx, r); e->type->mode = TM_BASIC; e->type->u.basic_type = TP_INT; if (arithmetic_type_p (t1) && arithmetic_type_p (t2)) { t = arithmetic_conversion (t1, t2); e->type->u.basic_type = t.u.basic_type; if (e1->const_p && e2->const_p) { e->const_p = TRUE; convert_value (e1, &t); convert_value (e2, &t); if (floating_type_p (&t)) e->u.d_val = (add_p ? e1->u.d_val + e2->u.d_val : e1->u.d_val - e2->u.d_val); else if (signed_integer_type_p (&t)) e->u.i_val = (add_p ? e1->u.i_val + e2->u.i_val : e1->u.i_val - e2->u.i_val); else e->u.u_val = (add_p ? e1->u.u_val + e2->u.u_val : e1->u.u_val - e2->u.u_val); } } else if (add_p) { if (t2->mode == TM_PTR) { SWAP (t1, t2, tt); SWAP (e1, e2, te); } if (t1->mode != TM_PTR || !integer_type_p (t2)) { error (c2m_ctx, r->pos, "invalid operand types of +"); } else if (incomplete_type_p (c2m_ctx, t1->u.ptr_type)) { error (c2m_ctx, r->pos, "pointer to incomplete type as an operand of +"); } else { *e->type = *t1; if (e1->const_p && e2->const_p) { size = type_size (c2m_ctx, t1->u.ptr_type); e->const_p = TRUE; e->u.u_val = (signed_integer_type_p (t2) ? e1->u.u_val + e2->u.i_val * size : e1->u.u_val + e2->u.u_val * size); } } } else if (t1->mode == TM_PTR && integer_type_p (t2)) { if (incomplete_type_p (c2m_ctx, t1->u.ptr_type)) { error (c2m_ctx, r->pos, "pointer to incomplete type as an operand of -"); } else { *e->type = *t1; if (e1->const_p && e2->const_p) { size = type_size (c2m_ctx, t1->u.ptr_type); e->const_p = TRUE; e->u.u_val = (signed_integer_type_p (t2) ? e1->u.u_val - e2->u.i_val * size : e1->u.u_val - e2->u.u_val * size); } } } else if (t1->mode == TM_PTR && t2->mode == TM_PTR && compatible_types_p (t1, t2, TRUE)) { if (incomplete_type_p (c2m_ctx, t1->u.ptr_type) && incomplete_type_p (c2m_ctx, t2->u.ptr_type)) { error (c2m_ctx, r->pos, "pointer to incomplete type as an operand of -"); } else if (t1->u.ptr_type->type_qual.atomic_p || t2->u.ptr_type->type_qual.atomic_p) { error (c2m_ctx, r->pos, "pointer to atomic type as an operand of -"); } else { e->type->mode = TM_BASIC; e->type->u.basic_type = get_int_basic_type (sizeof (mir_ptrdiff_t)); set_type_layout (c2m_ctx, e->type); if (e1->const_p && e2->const_p) { size = type_size (c2m_ctx, t1->u.ptr_type); e->const_p = TRUE; e->u.i_val = (e1->u.u_val > e2->u.u_val ? (mir_ptrdiff_t) ((e1->u.u_val - e2->u.u_val) / size) : -(mir_ptrdiff_t) ((e2->u.u_val - e1->u.u_val) / size)); } } } else { error (c2m_ctx, r->pos, "invalid operand types of -"); } break; } case N_MUL: case N_DIV: case N_MOD: case N_MUL_ASSIGN: case N_DIV_ASSIGN: case N_MOD_ASSIGN: e = create_expr (c2m_ctx, r); e->type->mode = TM_BASIC; e->type->u.basic_type = TP_INT; if (r->code == N_MOD && (!integer_type_p (t1) || !integer_type_p (t2))) { error (c2m_ctx, r->pos, "invalid operand types of %%"); } else if (r->code != N_MOD && (!arithmetic_type_p (t1) || !arithmetic_type_p (t2))) { error (c2m_ctx, r->pos, "invalid operand types of %s", r->code == N_MUL ? "*" : "/"); } else { t = arithmetic_conversion (t1, t2); e->type->u.basic_type = t.u.basic_type; if (e1->const_p && e2->const_p) { e->const_p = TRUE; convert_value (e1, &t); convert_value (e2, &t); if (r->code == N_MUL) { if (floating_type_p (&t)) e->u.d_val = e1->u.d_val * e2->u.d_val; else if (signed_integer_type_p (&t)) e->u.i_val = e1->u.i_val * e2->u.i_val; else e->u.u_val = e1->u.u_val * e2->u.u_val; } else if ((floating_type_p (&t) && e1->u.d_val == 0.0 && e2->u.d_val == 0.0) || (signed_integer_type_p (&t) && e2->u.i_val == 0) || (integer_type_p (&t) && !signed_integer_type_p (&t) && e2->u.u_val == 0)) { if (floating_type_p (&t)) { e->u.d_val = nanl (""); /* Use NaN */ } else { if (signed_integer_type_p (&t)) e->u.i_val = 0; else e->u.u_val = 0; error (c2m_ctx, r->pos, "Division by zero"); } } else if (r->code != N_MOD && floating_type_p (&t)) { e->u.d_val = e1->u.d_val / e2->u.d_val; } else if (signed_integer_type_p (&t)) { // ??? zero e->u.i_val = r->code == N_DIV ? e1->u.i_val / e2->u.i_val : e1->u.i_val % e2->u.i_val; } else { e->u.u_val = r->code == N_DIV ? e1->u.u_val / e2->u.u_val : e1->u.u_val % e2->u.u_val; } } } break; default: e = NULL; assert (FALSE); } return e; } static unsigned case_hash (case_t el, void *arg) { node_t case_expr = NL_HEAD (el->case_node->ops); struct expr *expr; assert (el->case_node->code == N_CASE); expr = case_expr->attr; assert (expr->const_p); if (signed_integer_type_p (expr->type)) return mir_hash (&expr->u.i_val, sizeof (expr->u.i_val), 0x42); return mir_hash (&expr->u.u_val, sizeof (expr->u.u_val), 0x42); } static int case_eq (case_t el1, case_t el2, void *arg) { node_t case_expr1 = NL_HEAD (el1->case_node->ops); node_t case_expr2 = NL_HEAD (el2->case_node->ops); struct expr *expr1, *expr2; assert (el1->case_node->code == N_CASE && el2->case_node->code == N_CASE); expr1 = case_expr1->attr; expr2 = case_expr2->attr; assert (expr1->const_p && expr2->const_p); assert (signed_integer_type_p (expr1->type) == signed_integer_type_p (expr2->type)); if (signed_integer_type_p (expr1->type)) return expr1->u.i_val == expr2->u.i_val; return expr1->u.u_val == expr2->u.u_val; } static void update_call_arg_area_offset (c2m_ctx_t c2m_ctx, struct type *type, int update_scope_p) { node_t block = NL_EL (curr_func_def->ops, 3); struct node_scope *ns = block->attr; curr_call_arg_area_offset += round_size (type_size (c2m_ctx, type), MAX_ALIGNMENT); if (update_scope_p && ns->call_arg_area_size < curr_call_arg_area_offset) ns->call_arg_area_size = curr_call_arg_area_offset; } #define NODE_CASE(n) case N_##n: #define REP_SEP static void classify_node (node_t n, int *expr_attr_p, int *stmt_p) { *expr_attr_p = *stmt_p = FALSE; switch (n->code) { REP8 (NODE_CASE, I, L, LL, U, UL, ULL, F, D) REP8 (NODE_CASE, LD, CH, STR, ID, COMMA, ANDAND, OROR, EQ) REP8 (NODE_CASE, NE, LT, LE, GT, GE, ASSIGN, BITWISE_NOT, NOT) REP8 (NODE_CASE, AND, AND_ASSIGN, OR, OR_ASSIGN, XOR, XOR_ASSIGN, LSH, LSH_ASSIGN) REP8 (NODE_CASE, RSH, RSH_ASSIGN, ADD, ADD_ASSIGN, SUB, SUB_ASSIGN, MUL, MUL_ASSIGN) REP8 (NODE_CASE, DIV, DIV_ASSIGN, MOD, MOD_ASSIGN, IND, FIELD, ADDR, DEREF) REP8 (NODE_CASE, DEREF_FIELD, COND, INC, DEC, POST_INC, POST_DEC, ALIGNOF, SIZEOF) REP6 (NODE_CASE, EXPR_SIZEOF, CAST, COMPOUND_LITERAL, CALL, GENERIC, GENERIC_ASSOC) *expr_attr_p = TRUE; break; REP8 (NODE_CASE, IF, SWITCH, WHILE, DO, FOR, GOTO, CONTINUE, BREAK) REP4 (NODE_CASE, RETURN, EXPR, BLOCK, SPEC_DECL) /* SPEC DECL may have an initializer */ *stmt_p = TRUE; break; REP8 (NODE_CASE, IGNORE, CASE, DEFAULT, LABEL, LIST, SHARE, TYPEDEF, EXTERN) REP8 (NODE_CASE, STATIC, AUTO, REGISTER, THREAD_LOCAL, DECL, VOID, CHAR, SHORT) REP8 (NODE_CASE, INT, LONG, FLOAT, DOUBLE, SIGNED, UNSIGNED, BOOL, STRUCT) REP8 (NODE_CASE, UNION, ENUM, ENUM_CONST, MEMBER, CONST, RESTRICT, VOLATILE, ATOMIC) REP8 (NODE_CASE, INLINE, NO_RETURN, ALIGNAS, FUNC, STAR, POINTER, DOTS, ARR) REP6 (NODE_CASE, INIT, FIELD_ID, TYPE, ST_ASSERT, FUNC_DEF, MODULE) break; default: assert (FALSE); } } #undef REP_SEP /* Create "static const char __func__[] = "" at the beginning of func_block if it is necessary. */ static void add__func__def (c2m_ctx_t c2m_ctx, node_t func_block, str_t func_name) { static const char fdecl_name[] = "__func__"; pos_t pos = func_block->pos; node_t list, declarator, decl, decl_specs; str_t str; if (!str_exists_p (c2m_ctx, fdecl_name, strlen (fdecl_name) + 1, &str)) return; decl_specs = new_pos_node (c2m_ctx, N_LIST, pos); NL_APPEND (decl_specs->ops, new_pos_node (c2m_ctx, N_STATIC, pos)); NL_APPEND (decl_specs->ops, new_pos_node (c2m_ctx, N_CONST, pos)); NL_APPEND (decl_specs->ops, new_pos_node (c2m_ctx, N_CHAR, pos)); list = new_pos_node (c2m_ctx, N_LIST, pos); NL_APPEND (list->ops, new_pos_node3 (c2m_ctx, N_ARR, pos, new_pos_node (c2m_ctx, N_IGNORE, pos), new_pos_node (c2m_ctx, N_LIST, pos), new_pos_node (c2m_ctx, N_IGNORE, pos))); declarator = new_pos_node2 (c2m_ctx, N_DECL, pos, new_str_node (c2m_ctx, N_ID, str, pos), list); decl = new_pos_node3 (c2m_ctx, N_SPEC_DECL, pos, decl_specs, declarator, new_str_node (c2m_ctx, N_STR, func_name, pos)); NL_PREPEND (NL_EL (func_block->ops, 1)->ops, decl); } /* Sort by decl scope nesting (more nested scope has a bigger UID) and decl size. */ static int decl_cmp (const void *v1, const void *v2) { const decl_t d1 = *(const decl_t *) v1, d2 = *(const decl_t *) v2; struct type *t1 = d1->decl_spec.type, *t2 = d2->decl_spec.type; mir_size_t s1 = raw_type_size (d1->c2m_ctx, t1), s2 = raw_type_size (d2->c2m_ctx, t2); if (d1->scope->uid < d2->scope->uid) return -1; if (d1->scope->uid > d2->scope->uid) return 1; if (s1 < s2) return -1; if (s1 > s2) return 1; return 0; } static void process_func_decls_for_allocation (c2m_ctx_t c2m_ctx) { size_t i, j; decl_t decl; struct type *type; struct node_scope *ns, *curr_ns; node_t scope; mir_size_t start_offset = 0; /* to remove an uninitialized warning */ /* Exclude decls which will be in regs: */ for (i = j = 0; i < VARR_LENGTH (decl_t, func_decls_for_allocation); i++) { decl = VARR_GET (decl_t, func_decls_for_allocation, i); type = decl->decl_spec.type; ns = decl->scope->attr; if (scalar_type_p (type) && !decl->addr_p) { decl->reg_p = TRUE; continue; } VARR_SET (decl_t, func_decls_for_allocation, j, decl); j++; } VARR_TRUNC (decl_t, func_decls_for_allocation, j); qsort (VARR_ADDR (decl_t, func_decls_for_allocation), j, sizeof (decl_t), decl_cmp); scope = NULL; for (i = 0; i < VARR_LENGTH (decl_t, func_decls_for_allocation); i++) { decl = VARR_GET (decl_t, func_decls_for_allocation, i); type = decl->decl_spec.type; ns = decl->scope->attr; if (decl->scope != scope) { /* new scope: process upper scopes */ for (scope = ns->scope; scope != top_scope; scope = curr_ns->scope) { curr_ns = scope->attr; ns->offset += curr_ns->size; curr_ns->stack_var_p = TRUE; } scope = decl->scope; ns->stack_var_p = TRUE; start_offset = ns->offset; } ns->offset = round_size (ns->offset, var_align (c2m_ctx, type)); decl->offset = ns->offset; ns->offset += var_size (c2m_ctx, type); ns->size = ns->offset - start_offset; } scope = NULL; for (i = 0; i < VARR_LENGTH (decl_t, func_decls_for_allocation); i++) { /* update scope sizes: */ decl = VARR_GET (decl_t, func_decls_for_allocation, i); ns = decl->scope->attr; if (decl->scope == scope) continue; /* new scope: update upper scope sizes */ for (scope = ns->scope; scope != top_scope; scope = curr_ns->scope) { curr_ns = scope->attr; if (curr_ns->size < ns->offset) curr_ns->size = ns->offset; if (ns->stack_var_p) curr_ns->stack_var_p = TRUE; } } } #define BUILTIN_VA_START "__builtin_va_start" #define BUILTIN_VA_ARG "__builtin_va_arg" #define ALLOCA "alloca" static void check (c2m_ctx_t c2m_ctx, node_t r, node_t context) { node_t op1, op2; struct expr *e = NULL, *e1, *e2; struct type t, *t1, *t2, *assign_expr_type; int expr_attr_p, stmt_p; VARR_PUSH (node_t, context_stack, context); classify_node (r, &expr_attr_p, &stmt_p); switch (r->code) { case N_IGNORE: case N_STAR: case N_FIELD_ID: break; /* do nothing */ case N_LIST: { for (node_t n = NL_HEAD (r->ops); n != NULL; n = NL_NEXT (n)) check (c2m_ctx, n, r); break; } case N_I: case N_L: e = create_basic_type_expr (c2m_ctx, r, r->code == N_I ? TP_INT : TP_LONG); e->const_p = TRUE; e->u.i_val = r->u.l; break; case N_LL: e = create_basic_type_expr (c2m_ctx, r, TP_LLONG); e->const_p = TRUE; e->u.i_val = r->u.ll; break; case N_U: case N_UL: e = create_basic_type_expr (c2m_ctx, r, r->code == N_U ? TP_UINT : TP_ULONG); e->const_p = TRUE; e->u.u_val = r->u.ul; break; case N_ULL: e = create_basic_type_expr (c2m_ctx, r, TP_ULLONG); e->const_p = TRUE; e->u.u_val = r->u.ull; break; case N_F: e = create_basic_type_expr (c2m_ctx, r, TP_FLOAT); e->const_p = TRUE; e->u.d_val = r->u.f; break; case N_D: e = create_basic_type_expr (c2m_ctx, r, TP_DOUBLE); e->const_p = TRUE; e->u.d_val = r->u.d; break; case N_LD: e = create_basic_type_expr (c2m_ctx, r, TP_LDOUBLE); e->const_p = TRUE; e->u.d_val = r->u.ld; break; case N_CH: e = create_basic_type_expr (c2m_ctx, r, TP_CHAR); e->const_p = TRUE; if (char_is_signed_p ()) e->u.i_val = r->u.ch; else e->u.u_val = r->u.ch; break; case N_STR: { struct arr_type *arr_type; e = create_expr (c2m_ctx, r); e->lvalue_node = r; e->type->mode = TM_ARR; e->type->pos_node = r; e->type->u.arr_type = arr_type = reg_malloc (c2m_ctx, sizeof (struct arr_type)); clear_type_qual (&arr_type->ind_type_qual); arr_type->static_p = FALSE; arr_type->el_type = create_type (c2m_ctx, NULL); arr_type->el_type->pos_node = r; arr_type->el_type->mode = TM_BASIC; arr_type->el_type->u.basic_type = TP_CHAR; arr_type->size = new_i_node (c2m_ctx, r->u.s.len, r->pos); check (c2m_ctx, arr_type->size, NULL); break; } case N_ID: { node_t aux_node = NULL; decl_t decl; op1 = find_def (c2m_ctx, S_REGULAR, r, curr_scope, &aux_node); e = create_expr (c2m_ctx, r); e->def_node = op1; if (op1 == NULL) { error (c2m_ctx, r->pos, "undeclared identifier %s", r->u.s.s); } else if (op1->code == N_IGNORE) { e->type->mode = TM_BASIC; e->type->u.basic_type = TP_INT; } else if (op1->code == N_SPEC_DECL) { decl = op1->attr; if (decl->decl_spec.typedef_p) error (c2m_ctx, r->pos, "typedef name %s as an operand", r->u.s.s); decl->used_p = TRUE; *e->type = *decl->decl_spec.type; if (e->type->mode != TM_FUNC) e->lvalue_node = op1; } else if (op1->code == N_FUNC_DEF) { decl = op1->attr; decl->used_p = TRUE; assert (decl->decl_spec.type->mode == TM_FUNC); *e->type = *decl->decl_spec.type; } else { assert (op1->code == N_ENUM_CONST && aux_node && aux_node->code == N_ENUM); e->type->mode = TM_ENUM; e->type->pos_node = r; e->type->u.tag_type = aux_node; e->const_p = TRUE; e->u.i_val = ((struct enum_value *) op1->attr)->val; } break; } case N_COMMA: process_bin_ops (c2m_ctx, r, &op1, &op2, &e1, &e2, &t1, &t2, r); e = create_expr (c2m_ctx, r); *e->type = *e2->type; break; case N_ANDAND: case N_OROR: process_bin_ops (c2m_ctx, r, &op1, &op2, &e1, &e2, &t1, &t2, r); e = create_expr (c2m_ctx, r); e->type->mode = TM_BASIC; e->type->u.basic_type = TP_INT; if (!scalar_type_p (t1) || !scalar_type_p (t2)) { error (c2m_ctx, r->pos, "invalid operand types of %s", r->code == N_ANDAND ? "&&" : "||"); } else if (e1->const_p) { int v; if (floating_type_p (t1)) v = e1->u.d_val != 0.0; else if (signed_integer_type_p (t1)) v = e1->u.i_val != 0; else v = e1->u.u_val != 0; if (v && r->code == N_OROR) { e->const_p = TRUE; e->u.i_val = v; } else if (!v && r->code == N_ANDAND) { e->const_p = TRUE; e->u.i_val = v; } else if (e2->const_p) { e->const_p = TRUE; if (floating_type_p (t2)) v = e2->u.d_val != 0.0; else if (signed_integer_type_p (t2)) v = e2->u.i_val != 0; else v = e2->u.u_val != 0; e->u.i_val = v; } } break; case N_EQ: case N_NE: case N_LT: case N_LE: case N_GT: case N_GE: process_bin_ops (c2m_ctx, r, &op1, &op2, &e1, &e2, &t1, &t2, r); e = create_expr (c2m_ctx, r); e->type->mode = TM_BASIC; e->type->u.basic_type = TP_INT; if ((r->code == N_EQ || r->code == N_NE) && ((t1->mode == TM_PTR && null_const_p (e2, t2)) || (t2->mode == TM_PTR && null_const_p (e1, t1)))) ; else if (t1->mode == TM_PTR && t2->mode == TM_PTR) { if (!compatible_types_p (t1, t2, TRUE) && ((r->code != N_EQ && r->code != N_NE) || (!void_ptr_p (t1) && !void_ptr_p (t2)))) { error (c2m_ctx, r->pos, "incompatible pointer types in comparison"); } else if (t1->u.ptr_type->type_qual.atomic_p || t2->u.ptr_type->type_qual.atomic_p) { error (c2m_ctx, r->pos, "pointer to atomic type as a comparison operand"); } else if (e1->const_p && e2->const_p) { e->const_p = TRUE; e->u.i_val = (r->code == N_EQ ? e1->u.u_val == e2->u.u_val : r->code == N_NE ? e1->u.u_val != e2->u.u_val : r->code == N_LT ? e1->u.u_val < e2->u.u_val : r->code == N_LE ? e1->u.u_val <= e2->u.u_val : r->code == N_GT ? e1->u.u_val > e2->u.u_val : e1->u.u_val >= e2->u.u_val); } } else if (arithmetic_type_p (t1) && arithmetic_type_p (t2)) { if (e1->const_p && e2->const_p) { t = arithmetic_conversion (t1, t2); convert_value (e1, &t); convert_value (e2, &t); e->const_p = TRUE; if (floating_type_p (&t)) e->u.i_val = (r->code == N_EQ ? e1->u.d_val == e2->u.d_val : r->code == N_NE ? e1->u.d_val != e2->u.d_val : r->code == N_LT ? e1->u.d_val < e2->u.d_val : r->code == N_LE ? e1->u.d_val <= e2->u.d_val : r->code == N_GT ? e1->u.d_val > e2->u.d_val : e1->u.d_val >= e2->u.d_val); else if (signed_integer_type_p (&t)) e->u.i_val = (r->code == N_EQ ? e1->u.i_val == e2->u.i_val : r->code == N_NE ? e1->u.i_val != e2->u.i_val : r->code == N_LT ? e1->u.i_val < e2->u.i_val : r->code == N_LE ? e1->u.i_val <= e2->u.i_val : r->code == N_GT ? e1->u.i_val > e2->u.i_val : e1->u.i_val >= e2->u.i_val); else e->u.i_val = (r->code == N_EQ ? e1->u.u_val == e2->u.u_val : r->code == N_NE ? e1->u.u_val != e2->u.u_val : r->code == N_LT ? e1->u.u_val < e2->u.u_val : r->code == N_LE ? e1->u.u_val <= e2->u.u_val : r->code == N_GT ? e1->u.u_val > e2->u.u_val : e1->u.u_val >= e2->u.u_val); } } else { error (c2m_ctx, r->pos, "invalid types of comparison operands"); } break; case N_BITWISE_NOT: case N_NOT: process_unop (c2m_ctx, r, &op1, &e1, &t1, r); e = create_expr (c2m_ctx, r); e->type->mode = TM_BASIC; e->type->u.basic_type = TP_INT; if (r->code == N_BITWISE_NOT && !integer_type_p (t1)) { error (c2m_ctx, r->pos, "bitwise-not operand should be of an integer type"); } else if (r->code == N_NOT && !scalar_type_p (t1)) { error (c2m_ctx, r->pos, "not operand should be of a scalar type"); } else if (r->code == N_BITWISE_NOT) { t = integer_promotion (t1); e->type->u.basic_type = t.u.basic_type; if (e1->const_p) { convert_value (e1, &t); e->const_p = TRUE; if (signed_integer_type_p (&t)) e->u.i_val = ~e1->u.i_val; else e->u.u_val = ~e1->u.u_val; } } else if (e1->const_p) { e->const_p = TRUE; if (floating_type_p (t1)) e->u.i_val = e1->u.d_val == 0.0; else if (signed_integer_type_p (t1)) e->u.i_val = e1->u.i_val == 0; else e->u.i_val = e1->u.u_val == 0; } break; case N_INC: case N_DEC: case N_POST_INC: case N_POST_DEC: { struct expr saved_expr; process_unop (c2m_ctx, r, &op1, &e1, &t1, NULL); saved_expr = *e1; t1 = e1->type = adjust_type (c2m_ctx, e1->type); get_int_node (c2m_ctx, &op2, &e2, &t2, t1->mode != TM_PTR ? 1 : type_size (c2m_ctx, t1->u.ptr_type)); e = check_assign_op (c2m_ctx, r, op1, op2, e1, e2, t1, t2); t2 = ((struct expr *) r->attr)->type; *e1 = saved_expr; t1 = e1->type; assign_expr_type = create_type (c2m_ctx, NULL); *assign_expr_type = *e->type; goto assign; break; } case N_ADD: case N_SUB: if (NL_NEXT (NL_HEAD (r->ops)) == NULL) { /* unary */ process_unop (c2m_ctx, r, &op1, &e1, &t1, r); e = create_expr (c2m_ctx, r); e->type->mode = TM_BASIC; e->type->u.basic_type = TP_INT; if (!arithmetic_type_p (t1)) { error (c2m_ctx, r->pos, "unary + or - operand should be of an arithmentic type"); } else { if (e1->const_p) e->const_p = TRUE; if (floating_type_p (t1)) { e->type->u.basic_type = t1->u.basic_type; if (e->const_p) e->u.d_val = (r->code == N_ADD ? +e1->u.d_val : -e1->u.d_val); } else { t = integer_promotion (t1); e->type->u.basic_type = t.u.basic_type; if (e1->const_p) { convert_value (e1, &t); if (signed_integer_type_p (&t)) e->u.i_val = (r->code == N_ADD ? +e1->u.i_val : -e1->u.i_val); else e->u.u_val = (r->code == N_ADD ? +e1->u.u_val : -e1->u.u_val); } } } break; } /* Fall through: */ case N_AND: case N_OR: case N_XOR: case N_LSH: case N_RSH: case N_MUL: case N_DIV: case N_MOD: process_bin_ops (c2m_ctx, r, &op1, &op2, &e1, &e2, &t1, &t2, r); e = check_assign_op (c2m_ctx, r, op1, op2, e1, e2, t1, t2); break; case N_AND_ASSIGN: case N_OR_ASSIGN: case N_XOR_ASSIGN: case N_LSH_ASSIGN: case N_RSH_ASSIGN: case N_ADD_ASSIGN: case N_SUB_ASSIGN: case N_MUL_ASSIGN: case N_DIV_ASSIGN: case N_MOD_ASSIGN: { struct expr saved_expr; process_bin_ops (c2m_ctx, r, &op1, &op2, &e1, &e2, &t1, &t2, NULL); saved_expr = *e1; t1 = e1->type = adjust_type (c2m_ctx, e1->type); t2 = e2->type = adjust_type (c2m_ctx, e2->type); e = check_assign_op (c2m_ctx, r, op1, op2, e1, e2, t1, t2); assign_expr_type = create_type (c2m_ctx, NULL); *assign_expr_type = *e->type; t2 = ((struct expr *) r->attr)->type; *e1 = saved_expr; t1 = e1->type; goto assign; break; } case N_ASSIGN: process_bin_ops (c2m_ctx, r, &op1, &op2, &e1, &e2, &t1, &t2, NULL); t2 = e2->type = adjust_type (c2m_ctx, e2->type); assign_expr_type = NULL; assign: e = create_expr (c2m_ctx, r); if (!e1->lvalue_node) { error (c2m_ctx, r->pos, "lvalue required as left operand of assignment"); } check_assignment_types (c2m_ctx, t1, t2, e2, r); *e->type = *t1; if ((e->type2 = assign_expr_type) != NULL) set_type_layout (c2m_ctx, assign_expr_type); break; case N_IND: process_bin_ops (c2m_ctx, r, &op1, &op2, &e1, &e2, &t1, &t2, r); if (t1->mode != TM_PTR && t1->mode != TM_ARR && (t2->mode == TM_PTR || t2->mode == TM_ARR)) { struct type *temp; node_t op; SWAP (t1, t2, temp); SWAP (e1, e2, e); SWAP (op1, op2, op); NL_REMOVE (r->ops, op1); NL_APPEND (r->ops, op1); } e = create_expr (c2m_ctx, r); e->lvalue_node = r; e->type->mode = TM_BASIC; e->type->u.basic_type = TP_INT; if (t1->mode != TM_PTR && t1->mode != TM_ARR) { error (c2m_ctx, r->pos, "subscripted value is neither array nor pointer"); } else if (t1->mode == TM_PTR) { *e->type = *t1->u.ptr_type; if (incomplete_type_p (c2m_ctx, t1->u.ptr_type)) { error (c2m_ctx, r->pos, "pointer to incomplete type in array subscription"); } } else { *e->type = *t1->u.arr_type->el_type; e->type->type_qual = t1->u.arr_type->ind_type_qual; if (incomplete_type_p (c2m_ctx, e->type)) { error (c2m_ctx, r->pos, "array type has incomplete element type"); } } if (!integer_type_p (t2)) { error (c2m_ctx, r->pos, "array subscript is not an integer"); } break; case N_ADDR: process_unop (c2m_ctx, r, &op1, &e1, &t1, r); e = create_expr (c2m_ctx, r); if (op1->code == N_DEREF) { node_t deref_op = NL_HEAD (op1->ops); *e->type = *((struct expr *) deref_op->attr)->type; break; } else if (e1->type->mode == TM_PTR && e1->type->u.ptr_type->mode == TM_FUNC) { *e->type = *e1->type; break; } else if (!e1->lvalue_node) { e->type->mode = TM_BASIC; e->type->u.basic_type = TP_INT; error (c2m_ctx, r->pos, "lvalue required as unary & operand"); break; } if (op1->code == N_IND) { t2 = create_type (c2m_ctx, t1); } else if (op1->code == N_ID) { node_t decl_node = e1->lvalue_node; decl_t decl = decl_node->attr; decl->addr_p = TRUE; if (decl->decl_spec.register_p) error (c2m_ctx, r->pos, "address of register variable %s requested", op1->u.s.s); t2 = create_type (c2m_ctx, decl->decl_spec.type); } else if (e1->lvalue_node->code == N_MEMBER) { node_t declarator = NL_EL (e1->lvalue_node->ops, 1); node_t width = NL_NEXT (declarator); decl_t decl = e1->lvalue_node->attr; assert (declarator->code == N_DECL); if (width->code != N_IGNORE) { error (c2m_ctx, r->pos, "cannot take address of bit-field %s", NL_HEAD (declarator->ops)->u.s.s); } t2 = create_type (c2m_ctx, decl->decl_spec.type); if (op1->code == N_DEREF_FIELD && (e2 = NL_HEAD (op1->ops)->attr)->const_p) { e->const_p = TRUE; e->u.u_val = e2->u.u_val + decl->offset; } } else if (e1->lvalue_node->code == N_COMPOUND_LITERAL) { t2 = t1; } else { assert (e1->lvalue_node->code == N_STR); t2 = t1; } e->type->mode = TM_PTR; e->type->u.ptr_type = t2; break; case N_DEREF: process_unop (c2m_ctx, r, &op1, &e1, &t1, r); e = create_expr (c2m_ctx, r); e->type->mode = TM_BASIC; e->type->u.basic_type = TP_INT; if (t1->mode != TM_PTR) { error (c2m_ctx, r->pos, "invalid type argument of unary *"); } else { *e->type = *t1->u.ptr_type; e->lvalue_node = r; } break; case N_FIELD: case N_DEREF_FIELD: { symbol_t sym; decl_t decl; node_t width; struct expr *width_expr; process_unop (c2m_ctx, r, &op1, &e1, &t1, r); e = create_expr (c2m_ctx, r); e->type->mode = TM_BASIC; e->type->u.basic_type = TP_INT; op2 = NL_NEXT (op1); assert (op2->code == N_ID); if (r->code == N_DEREF_FIELD && t1->mode == TM_PTR) { t1 = t1->u.ptr_type; } if (t1->mode != TM_STRUCT && t1->mode != TM_UNION) { error (c2m_ctx, r->pos, "request for member %s in something not a structure or union", op2->u.s.s); } else if (!symbol_find (c2m_ctx, S_REGULAR, op2, t1->u.tag_type, &sym)) { error (c2m_ctx, r->pos, "%s has no member %s", t1->mode == TM_STRUCT ? "struct" : "union", op2->u.s.s); } else { assert (sym.def_node->code == N_MEMBER); decl = sym.def_node->attr; *e->type = *decl->decl_spec.type; e->lvalue_node = sym.def_node; if ((width = NL_EL (sym.def_node->ops, 2))->code != N_IGNORE && e->type->mode == TM_BASIC && (width_expr = width->attr)->const_p && width_expr->u.i_val < sizeof (mir_int) * MIR_CHAR_BIT) e->type->u.basic_type = TP_INT; } break; } case N_COND: { node_t op3; struct expr *e3; struct type *t3; int v = 0; process_bin_ops (c2m_ctx, r, &op1, &op2, &e1, &e2, &t1, &t2, r); op3 = NL_NEXT (op2); check (c2m_ctx, op3, r); e3 = op3->attr; e3->type = adjust_type (c2m_ctx, e3->type); t3 = e3->type; e = create_expr (c2m_ctx, r); e->type->mode = TM_BASIC; e->type->u.basic_type = TP_INT; if (!scalar_type_p (t1)) { error (c2m_ctx, r->pos, "condition should be of a scalar type"); break; } if (e1->const_p) { if (floating_type_p (t1)) v = e1->u.d_val != 0.0; else if (signed_integer_type_p (t1)) v = e1->u.i_val != 0; else v = e1->u.u_val != 0; } if (arithmetic_type_p (t2) && arithmetic_type_p (t3)) { t = arithmetic_conversion (t2, t3); *e->type = t; if (e1->const_p) { if (v && e2->const_p) { e->const_p = TRUE; convert_value (e2, &t); e->u = e2->u; } else if (!v && e3->const_p) { e->const_p = TRUE; convert_value (e3, &t); e->u = e3->u; } } break; } if (void_type_p (t2) && void_type_p (t3)) { e->type->u.basic_type = TP_VOID; } else if ((t2->mode == TM_STRUCT || t2->mode == TM_UNION) && (t3->mode == TM_STRUCT || t3->mode == TM_UNION) && t2->u.tag_type == t3->u.tag_type) { *e->type = *t2; } else if ((t2->mode == TM_PTR && null_const_p (e3, t3)) || (t3->mode == TM_PTR && null_const_p (e2, t2))) { e->type->mode = TM_PTR; e->type->pos_node = r; e->type->u.ptr_type = create_type (c2m_ctx, NULL); e->type->u.ptr_type->pos_node = r; e->type->u.ptr_type->mode = TM_BASIC; e->type->u.ptr_type->u.basic_type = TP_VOID; } else if (t2->mode != TM_PTR && t3->mode != TM_PTR) { error (c2m_ctx, r->pos, "incompatible types in true and false parts of cond-expression"); break; } else if (compatible_types_p (t2, t3, TRUE)) { t = composite_type (c2m_ctx, t2->u.ptr_type, t3->u.ptr_type); e->type->mode = TM_PTR; e->type->pos_node = r; e->type->u.ptr_type = create_type (c2m_ctx, &t); e->type->u.ptr_type->type_qual = type_qual_union (&t2->u.ptr_type->type_qual, &t3->u.ptr_type->type_qual); if ((t2->u.ptr_type->type_qual.atomic_p || t3->u.ptr_type->type_qual.atomic_p) && !null_const_p (e2, t2) && !null_const_p (e3, t3)) { error (c2m_ctx, r->pos, "pointer to atomic type in true or false parts of cond-expression"); } } else if (void_ptr_p (t2) || void_ptr_p (t3)) { e->type->mode = TM_PTR; e->type->pos_node = r; e->type->u.ptr_type = create_type (c2m_ctx, NULL); e->type->u.ptr_type->pos_node = r; if (null_const_p (e2, t2)) { e->type->u.ptr_type = e2->type->u.ptr_type; } else if (null_const_p (e3, t3)) { e->type->u.ptr_type = e3->type->u.ptr_type; } else { if (t2->u.ptr_type->type_qual.atomic_p || t3->u.ptr_type->type_qual.atomic_p) { error (c2m_ctx, r->pos, "pointer to atomic type in true or false parts of cond-expression"); } e->type->u.ptr_type->mode = TM_BASIC; e->type->u.ptr_type->u.basic_type = TP_VOID; } e->type->u.ptr_type->type_qual = type_qual_union (&t2->u.ptr_type->type_qual, &t3->u.ptr_type->type_qual); } else { error (c2m_ctx, r->pos, "incompatible pointer types in true and false parts of cond-expression"); break; } if (e1->const_p) { if (v && e2->const_p) { e->const_p = TRUE; e->u = e2->u; } else if (!v && e3->const_p) { e->const_p = TRUE; e->u = e3->u; } } break; } case N_ALIGNOF: case N_SIZEOF: { struct decl_spec *decl_spec; op1 = NL_HEAD (r->ops); check (c2m_ctx, op1, r); e = create_expr (c2m_ctx, r); *e->type = get_ptr_int_type (FALSE); if (r->code == N_ALIGNOF && op1->code == N_IGNORE) { error (c2m_ctx, r->pos, "_Alignof of non-type"); break; } assert (op1->code == N_TYPE); decl_spec = op1->attr; if (incomplete_type_p (c2m_ctx, decl_spec->type)) { error (c2m_ctx, r->pos, "%s of incomplete type requested", r->code == N_ALIGNOF ? "_Alignof" : "sizeof"); } else if (decl_spec->type->mode == TM_FUNC) { error (c2m_ctx, r->pos, "%s of function type requested", r->code == N_ALIGNOF ? "_Alignof" : "sizeof"); } else { e->const_p = TRUE; e->u.i_val = (r->code == N_SIZEOF ? type_size (c2m_ctx, decl_spec->type) : type_align (decl_spec->type)); } break; } case N_EXPR_SIZEOF: process_unop (c2m_ctx, r, &op1, &e1, &t1, r); e = create_expr (c2m_ctx, r); e->type->mode = TM_BASIC; e->type->u.basic_type = TP_INT; // ??? if (incomplete_type_p (c2m_ctx, t1)) { error (c2m_ctx, r->pos, "sizeof of incomplete type requested"); } else if (t1->mode == TM_FUNC) { error (c2m_ctx, r->pos, "sizeof of function type requested"); } else if (e1->lvalue_node && e1->lvalue_node->code == N_MEMBER) { node_t declarator = NL_EL (e1->lvalue_node->ops, 1); node_t width = NL_NEXT (declarator); assert (declarator->code == N_DECL); if (width->code != N_IGNORE) { error (c2m_ctx, r->pos, "sizeof applied to a bit-field %s", NL_HEAD (declarator->ops)->u.s.s); } } e->const_p = TRUE; e->u.i_val = type_size (c2m_ctx, t1); break; case N_CAST: { struct decl_spec *decl_spec; int void_p; process_type_bin_ops (c2m_ctx, r, &op1, &op2, &e2, &t2, r); e = create_expr (c2m_ctx, r); assert (op1->code == N_TYPE); decl_spec = op1->attr; *e->type = *decl_spec->type; void_p = void_type_p (decl_spec->type); if (!void_p && !scalar_type_p (decl_spec->type)) { error (c2m_ctx, r->pos, "conversion to non-scalar type requested"); } else if (!scalar_type_p (t2) && !void_type_p (t2)) { error (c2m_ctx, r->pos, "conversion of non-scalar value requested"); } else if (t2->mode == TM_PTR && floating_type_p (decl_spec->type)) { error (c2m_ctx, r->pos, "conversion of a pointer to floating value requested"); } else if (decl_spec->type->mode == TM_PTR && floating_type_p (t2)) { error (c2m_ctx, r->pos, "conversion of floating point value to a pointer requested"); } else if (e2->const_p && !void_p) { e->const_p = TRUE; cast_value (e, e2, decl_spec->type); } break; } case N_COMPOUND_LITERAL: { node_t list, n; decl_t decl; int n_spec_index, addr_p; op1 = NL_HEAD (r->ops); list = NL_NEXT (op1); assert (op1->code == N_TYPE && list->code == N_LIST); check (c2m_ctx, op1, r); decl = op1->attr; t1 = decl->decl_spec.type; check (c2m_ctx, list, r); decl->addr_p = TRUE; if (incomplete_type_p (c2m_ctx, t1) && (t1->mode != TM_ARR || t1->u.arr_type->size->code != N_IGNORE || incomplete_type_p (c2m_ctx, t1->u.arr_type->el_type))) { error (c2m_ctx, r->pos, "compound literal of incomplete type"); break; } for (n_spec_index = VARR_LENGTH (node_t, context_stack) - 1; n_spec_index >= 0 && (n = VARR_GET (node_t, context_stack, n_spec_index)) != NULL && n->code != N_SPEC_DECL; n_spec_index--) ; if (n_spec_index < VARR_LENGTH (node_t, context_stack) - 1 && (n_spec_index < 0 || !get_compound_literal (VARR_GET (node_t, context_stack, n_spec_index + 1), &addr_p) || addr_p)) check_initializer (c2m_ctx, NULL, &t1, list, curr_scope == top_scope || decl->decl_spec.static_p || decl->decl_spec.thread_local_p, FALSE); decl->decl_spec.type = t1; e = create_expr (c2m_ctx, r); e->lvalue_node = r; *e->type = *t1; if (curr_scope != top_scope) VARR_PUSH (decl_t, func_decls_for_allocation, decl); break; } case N_CALL: { struct func_type *func_type = NULL; /* to remove an uninitialized warning */ struct type *ret_type; node_t list, spec_list, decl, param_list, start_param, param, arg_list, arg; node_t saved_scope = curr_scope; struct decl_spec *decl_spec; mir_size_t saved_call_arg_area_offset_before_args; struct type res_type; int builtin_call_p, alloca_p, va_arg_p = FALSE, va_start_p = FALSE; op1 = NL_HEAD (r->ops); alloca_p = op1->code == N_ID && strcmp (op1->u.s.s, ALLOCA) == 0; if (op1->code == N_ID && find_def (c2m_ctx, S_REGULAR, op1, curr_scope, NULL) == NULL) { va_arg_p = strcmp (op1->u.s.s, BUILTIN_VA_ARG) == 0; va_start_p = strcmp (op1->u.s.s, BUILTIN_VA_START) == 0; if (!va_arg_p && !va_start_p && !alloca_p) { /* N_SPEC_DECL (N_SHARE (N_LIST (N_INT)), N_DECL (N_ID, N_FUNC (N_LIST)), N_IGNORE) */ spec_list = new_node (c2m_ctx, N_LIST); op_append (spec_list, new_node (c2m_ctx, N_INT)); list = new_node (c2m_ctx, N_LIST); op_append (list, new_node1 (c2m_ctx, N_FUNC, new_node (c2m_ctx, N_LIST))); decl = new_pos_node3 (c2m_ctx, N_SPEC_DECL, op1->pos, new_node1 (c2m_ctx, N_SHARE, spec_list), new_node2 (c2m_ctx, N_DECL, copy_node (c2m_ctx, op1), list), new_node (c2m_ctx, N_IGNORE)); curr_scope = top_scope; check (c2m_ctx, decl, NULL); curr_scope = saved_scope; assert (top_scope->code == N_MODULE); list = NL_HEAD (top_scope->ops); assert (list->code == N_LIST); op_prepend (list, decl); } } builtin_call_p = alloca_p || va_arg_p || va_start_p; if (!builtin_call_p) VARR_PUSH (node_t, call_nodes, r); arg_list = NL_NEXT (op1); if (builtin_call_p) { for (arg = NL_HEAD (arg_list->ops); arg != NULL; arg = NL_NEXT (arg)) check (c2m_ctx, arg, r); init_type (&res_type); if (alloca_p) { res_type.mode = TM_PTR; res_type.u.ptr_type = &VOID_TYPE; } else { res_type.mode = TM_BASIC; res_type.u.basic_type = va_arg_p ? TP_INT : TP_VOID; } ret_type = &res_type; if (va_start_p && NL_LENGTH (arg_list->ops) != 1) { error (c2m_ctx, op1->pos, "wrong number of arguments in %s call", BUILTIN_VA_START); } else if (alloca_p && NL_LENGTH (arg_list->ops) != 1) { error (c2m_ctx, op1->pos, "wrong number of arguments in %s call", ALLOCA); } else if (va_arg_p && NL_LENGTH (arg_list->ops) != 2) { error (c2m_ctx, op1->pos, "wrong number of arguments in %s call", BUILTIN_VA_ARG); } else { /* first argument type ??? */ if (va_arg_p) { arg = NL_EL (arg_list->ops, 1); e2 = arg->attr; t2 = e2->type; if (t2->mode != TM_PTR) error (c2m_ctx, arg->pos, "wrong type of 2nd argument of %s call", BUILTIN_VA_ARG); else ret_type = t2->u.ptr_type; } } } else { check (c2m_ctx, op1, r); e1 = op1->attr; t1 = e1->type; if (t1->mode != TM_PTR || (t1 = t1->u.ptr_type)->mode != TM_FUNC) { error (c2m_ctx, r->pos, "called object is not a function or function pointer"); break; } func_type = t1->u.func_type; ret_type = func_type->ret_type; } e = create_expr (c2m_ctx, r); *e->type = *ret_type; e->builtin_call_p = builtin_call_p; if ((ret_type->mode != TM_BASIC || ret_type->u.basic_type != TP_VOID) && incomplete_type_p (c2m_ctx, ret_type)) { error (c2m_ctx, r->pos, "function return type is incomplete"); } if (ret_type->mode == TM_STRUCT || ret_type->mode == TM_UNION) { set_type_layout (c2m_ctx, ret_type); if (!builtin_call_p) update_call_arg_area_offset (c2m_ctx, ret_type, TRUE); } if (builtin_call_p) break; param_list = func_type->param_list; param = start_param = NL_HEAD (param_list->ops); if (void_param_p (start_param)) { /* f(void) */ if ((arg = NL_HEAD (arg_list->ops)) != NULL) error (c2m_ctx, arg->pos, "too many arguments"); break; } saved_call_arg_area_offset_before_args = curr_call_arg_area_offset; for (arg = NL_HEAD (arg_list->ops); arg != NULL; arg = NL_NEXT (arg)) { check (c2m_ctx, arg, r); e2 = arg->attr; if (start_param == NULL || start_param->code == N_ID) continue; /* no params or ident list */ if (param == NULL) { if (!func_type->dots_p) error (c2m_ctx, arg->pos, "too many arguments"); start_param = NULL; /* ignore the rest args */ continue; } assert (param->code == N_SPEC_DECL || param->code == N_TYPE); decl_spec = get_param_decl_spec (param); check_assignment_types (c2m_ctx, decl_spec->type, NULL, e2, r); param = NL_NEXT (param); } curr_call_arg_area_offset = saved_call_arg_area_offset_before_args; if (param != NULL) error (c2m_ctx, r->pos, "too few arguments"); break; } case N_GENERIC: { node_t list, ga, ga2, type_name, type_name2, expr; node_t default_case = NULL, ga_case = NULL; struct decl_spec *decl_spec; op1 = NL_HEAD (r->ops); check (c2m_ctx, op1, r); e1 = op1->attr; t = *e1->type; if (integer_type_p (&t)) t = integer_promotion (&t); list = NL_NEXT (op1); for (ga = NL_HEAD (list->ops); ga != NULL; ga = NL_NEXT (ga)) { assert (ga->code == N_GENERIC_ASSOC); type_name = NL_HEAD (ga->ops); expr = NL_NEXT (type_name); check (c2m_ctx, type_name, r); check (c2m_ctx, expr, r); if (type_name->code == N_IGNORE) { if (default_case) error (c2m_ctx, ga->pos, "duplicate default case in _Generic"); default_case = ga; continue; } assert (type_name->code == N_TYPE); decl_spec = type_name->attr; if (incomplete_type_p (c2m_ctx, decl_spec->type)) { error (c2m_ctx, ga->pos, "_Generic case has incomplete type"); } else if (compatible_types_p (&t, decl_spec->type, TRUE)) { if (ga_case) error (c2m_ctx, ga_case->pos, "_Generic expr type is compatible with more than one generic association type"); ga_case = ga; } else { for (ga2 = NL_HEAD (list->ops); ga2 != ga; ga2 = NL_NEXT (ga2)) { type_name2 = NL_HEAD (ga2->ops); if (type_name2->code != N_IGNORE && !(incomplete_type_p (c2m_ctx, t2 = ((struct decl_spec *) type_name2->attr)->type)) && compatible_types_p (t2, decl_spec->type, TRUE)) { error (c2m_ctx, ga->pos, "two or more compatible generic association types"); break; } } } } e = create_expr (c2m_ctx, r); if (default_case == NULL && ga_case == NULL) { error (c2m_ctx, r->pos, "expression type is not compatible with generic association"); } else { /* make compatible association a list head */ if (ga_case == NULL) ga_case = default_case; NL_REMOVE (list->ops, ga_case); NL_PREPEND (list->ops, ga_case); t2 = e->type; *e = *(struct expr *) NL_EL (ga_case->ops, 1)->attr; *t2 = *e->type; e->type = t2; } break; } case N_SPEC_DECL: { node_t specs = NL_HEAD (r->ops); node_t declarator = NL_NEXT (specs); node_t initializer = NL_NEXT (declarator); node_t unshared_specs = specs->code != N_SHARE ? specs : NL_HEAD (specs->ops); struct decl_spec decl_spec = check_decl_spec (c2m_ctx, unshared_specs, r); if (declarator->code != N_IGNORE) { create_decl (c2m_ctx, curr_scope, r, decl_spec, NULL, initializer, context != NULL && context->code == N_FUNC); } else if (decl_spec.type->mode == TM_STRUCT || decl_spec.type->mode == TM_UNION) { if (NL_HEAD (decl_spec.type->u.tag_type->ops)->code != N_ID) error (c2m_ctx, r->pos, "unnamed struct/union with no instances"); } else if (decl_spec.type->mode != TM_ENUM) { error (c2m_ctx, r->pos, "useless declaration"); } /* We have at least one enum constant according to the syntax. */ break; } case N_ST_ASSERT: { int ok_p; op1 = NL_HEAD (r->ops); check (c2m_ctx, op1, r); e1 = op1->attr; t1 = e1->type; if (!e1->const_p) { error (c2m_ctx, r->pos, "expression in static assertion is not constant"); } else if (!integer_type_p (t1)) { error (c2m_ctx, r->pos, "expression in static assertion is not an integer"); } else { if (signed_integer_type_p (t1)) ok_p = e1->u.i_val != 0; else ok_p = e1->u.u_val != 0; if (!ok_p) { assert (NL_NEXT (op1) != NULL && NL_NEXT (op1)->code == N_STR); error (c2m_ctx, r->pos, "static assertion failed: \"%s\"", NL_NEXT (op1)->u.s.s); // ??? } } break; } case N_MEMBER: { struct type *type; node_t specs = NL_HEAD (r->ops); node_t declarator = NL_NEXT (specs); node_t const_expr = NL_NEXT (declarator); node_t unshared_specs = specs->code != N_SHARE ? specs : NL_HEAD (specs->ops); struct decl_spec decl_spec = check_decl_spec (c2m_ctx, unshared_specs, r); create_decl (c2m_ctx, curr_scope, r, decl_spec, const_expr, NULL, FALSE); type = ((decl_t) r->attr)->decl_spec.type; if (const_expr->code != N_IGNORE) { struct expr *cexpr; check (c2m_ctx, const_expr, r); cexpr = const_expr->attr; if (cexpr != NULL) { if (type->type_qual.atomic_p) error (c2m_ctx, const_expr->pos, "bit field with _Atomic"); if (!cexpr->const_p) { error (c2m_ctx, const_expr->pos, "bit field is not a constant expr"); } else if (!integer_type_p (type) && (type->mode != TM_BASIC || type->u.basic_type != TP_BOOL)) { error (c2m_ctx, const_expr->pos, "bit field type should be _Bool, a signed integer, or an unsigned integer type"); } else if (!integer_type_p (cexpr->type) && (cexpr->type->mode != TM_BASIC || cexpr->type->u.basic_type != TP_BOOL)) { error (c2m_ctx, const_expr->pos, "bit field width is not of an integer type"); } else if (signed_integer_type_p (cexpr->type) && cexpr->u.i_val < 0) { error (c2m_ctx, const_expr->pos, "bit field width is negative"); } else if (cexpr->u.i_val == 0 && declarator->code == N_DECL) { error (c2m_ctx, const_expr->pos, "zero bit field width for %s", NL_HEAD (declarator->ops)->u.s.s); } else if ((!signed_integer_type_p (cexpr->type) && cexpr->u.u_val > int_bit_size (type)) || (signed_integer_type_p (cexpr->type) && cexpr->u.i_val > int_bit_size (type))) { error (c2m_ctx, const_expr->pos, "bit field width exceeds its type"); } } } if (declarator->code == N_IGNORE) { if (((decl_spec.type->mode != TM_STRUCT && decl_spec.type->mode != TM_UNION) || NL_HEAD (decl_spec.type->u.tag_type->ops)->code != N_IGNORE) && const_expr->code == N_IGNORE) error (c2m_ctx, r->pos, "no declarator in struct or union declaration"); } else { node_t id = NL_HEAD (declarator->ops); if (type->mode == TM_FUNC) { error (c2m_ctx, id->pos, "field %s is declared as a function", id->u.s.s); } else if (incomplete_type_p (c2m_ctx, type)) { /* el_type is checked on completness in N_ARR */ if (type->mode != TM_ARR || type->u.arr_type->size->code != N_IGNORE) error (c2m_ctx, id->pos, "field %s has incomplete type", id->u.s.s); } } break; } case N_INIT: { node_t des_list = NL_HEAD (r->ops), initializer = NL_NEXT (des_list); check (c2m_ctx, des_list, r); check (c2m_ctx, initializer, r); break; } case N_FUNC_DEF: { node_t specs = NL_HEAD (r->ops); node_t declarator = NL_NEXT (specs); node_t declarations = NL_NEXT (declarator); node_t block = NL_NEXT (declarations); node_t id = NL_HEAD (declarator->ops); struct decl_spec decl_spec = check_decl_spec (c2m_ctx, specs, r); node_t decl_node, p, next_p, param_list, param_id, param_declarator, func; symbol_t sym; struct node_scope *ns; if (strcmp (id->u.s.s, ALLOCA) == 0 || strcmp (id->u.s.s, BUILTIN_VA_START) == 0 || strcmp (id->u.s.s, BUILTIN_VA_ARG) == 0) { error (c2m_ctx, id->pos, "%s is a builtin function", id->u.s.s); break; } curr_func_scope_num = 0; create_node_scope (c2m_ctx, block); func_block_scope = curr_scope; curr_func_def = r; curr_switch = curr_loop = curr_loop_switch = NULL; curr_call_arg_area_offset = 0; VARR_TRUNC (decl_t, func_decls_for_allocation, 0); create_decl (c2m_ctx, top_scope, r, decl_spec, NULL, NULL, FALSE); curr_scope = func_block_scope; check (c2m_ctx, declarations, r); /* Process parameter identifier list: */ assert (declarator->code == N_DECL); func = NL_HEAD (NL_EL (declarator->ops, 1)->ops); assert (func != NULL && func->code == N_FUNC); param_list = NL_HEAD (func->ops); for (p = NL_HEAD (param_list->ops); p != NULL; p = next_p) { next_p = NL_NEXT (p); if (p->code != N_ID) break; NL_REMOVE (param_list->ops, p); if (!symbol_find (c2m_ctx, S_REGULAR, p, curr_scope, &sym)) { if (options->pedantic_p) { error (c2m_ctx, p->pos, "parameter %s has no type", p->u.s.s); } else { warning (c2m_ctx, p->pos, "type of parameter %s defaults to int", p->u.s.s); decl_node = new_pos_node3 (c2m_ctx, N_SPEC_DECL, p->pos, new_node1 (c2m_ctx, N_SHARE, new_node1 (c2m_ctx, N_LIST, new_pos_node (c2m_ctx, N_INT, p->pos))), new_pos_node2 (c2m_ctx, N_DECL, p->pos, new_str_node (c2m_ctx, N_ID, p->u.s, p->pos), new_node (c2m_ctx, N_LIST)), new_node (c2m_ctx, N_IGNORE)); NL_APPEND (param_list->ops, decl_node); check (c2m_ctx, decl_node, r); } } else { struct decl_spec decl_spec, *decl_spec_ptr; decl_node = sym.def_node; assert (decl_node->code == N_SPEC_DECL); NL_REMOVE (declarations->ops, decl_node); NL_APPEND (param_list->ops, decl_node); param_declarator = NL_EL (decl_node->ops, 1); assert (param_declarator->code == N_DECL); param_id = NL_HEAD (param_declarator->ops); if (NL_NEXT (param_declarator)->code != N_IGNORE) { error (c2m_ctx, p->pos, "initialized parameter %s", param_id->u.s.s); } decl_spec_ptr = &((decl_t) decl_node->attr)->decl_spec; adjust_param_type (c2m_ctx, &decl_spec_ptr->type); decl_spec = *decl_spec_ptr; if (decl_spec.typedef_p || decl_spec.extern_p || decl_spec.static_p || decl_spec.auto_p || decl_spec.thread_local_p) { error (c2m_ctx, param_id->pos, "storage specifier in a function parameter %s", param_id->u.s.s); } } } /* Process the rest declarations: */ for (p = NL_HEAD (declarations->ops); p != NULL; p = NL_NEXT (p)) { if (p->code == N_ST_ASSERT) continue; assert (p->code == N_SPEC_DECL); param_declarator = NL_EL (p->ops, 1); assert (param_declarator->code == N_DECL); param_id = NL_HEAD (param_declarator->ops); assert (param_id->code == N_ID); error (c2m_ctx, param_id->pos, "declaration for parameter %s but no such parameter", param_id->u.s.s); } add__func__def (c2m_ctx, block, id->u.s); check (c2m_ctx, block, r); /* Process all gotos: */ for (size_t i = 0; i < VARR_LENGTH (node_t, gotos); i++) { symbol_t sym; node_t n = VARR_GET (node_t, gotos, i); node_t id = NL_NEXT (NL_HEAD (n->ops)); if (!symbol_find (c2m_ctx, S_LABEL, id, func_block_scope, &sym)) { error (c2m_ctx, id->pos, "undefined label %s", id->u.s.s); } else { n->attr = sym.def_node; } } VARR_TRUNC (node_t, gotos, 0); assert (curr_scope == top_scope); /* set up in the block */ func_block_scope = top_scope; process_func_decls_for_allocation (c2m_ctx); /* Add call arg area */ ns = block->attr; ns->size = round_size (ns->size, MAX_ALIGNMENT); ns->size += ns->call_arg_area_size; break; } case N_TYPE: { struct type *type; node_t specs = NL_HEAD (r->ops); node_t abstract_declarator = NL_NEXT (specs); struct decl_spec decl_spec = check_decl_spec (c2m_ctx, specs, r); /* only spec_qual_list here */ type = check_declarator (c2m_ctx, abstract_declarator, FALSE); assert (NL_HEAD (abstract_declarator->ops)->code == N_IGNORE); decl_spec.type = append_type (type, decl_spec.type); if (context && context->code == N_COMPOUND_LITERAL) { r->attr = reg_malloc (c2m_ctx, sizeof (struct decl)); init_decl (c2m_ctx, r->attr); ((struct decl *) r->attr)->decl_spec = decl_spec; check_type (c2m_ctx, decl_spec.type, 0, FALSE); set_type_layout (c2m_ctx, decl_spec.type); check_decl_align (c2m_ctx, &((struct decl *) r->attr)->decl_spec); } else { r->attr = reg_malloc (c2m_ctx, sizeof (struct decl_spec)); *((struct decl_spec *) r->attr) = decl_spec; check_type (c2m_ctx, decl_spec.type, 0, FALSE); set_type_layout (c2m_ctx, decl_spec.type); check_decl_align (c2m_ctx, r->attr); } break; } case N_BLOCK: check_labels (c2m_ctx, NL_HEAD (r->ops), r); if (curr_scope != r) create_node_scope (c2m_ctx, r); /* it happens if it is the top func block */ check (c2m_ctx, NL_EL (r->ops, 1), r); finish_scope (c2m_ctx); break; case N_MODULE: create_node_scope (c2m_ctx, r); top_scope = curr_scope; check (c2m_ctx, NL_HEAD (r->ops), r); finish_scope (c2m_ctx); break; case N_IF: { node_t labels = NL_HEAD (r->ops); node_t expr = NL_NEXT (labels); node_t if_stmt = NL_NEXT (expr); node_t else_stmt = NL_NEXT (if_stmt); check_labels (c2m_ctx, labels, r); check (c2m_ctx, expr, r); e1 = expr->attr; t1 = e1->type; if (!scalar_type_p (t1)) { error (c2m_ctx, expr->pos, "if-expr should be of a scalar type"); } check (c2m_ctx, if_stmt, r); check (c2m_ctx, else_stmt, r); break; } case N_SWITCH: { node_t saved_switch = curr_switch; node_t saved_loop_switch = curr_loop_switch; node_t labels = NL_HEAD (r->ops); node_t expr = NL_NEXT (labels); node_t stmt = NL_NEXT (expr); struct type t, *type; struct switch_attr *switch_attr; case_t el; node_t case_expr, case_expr2, another_case_expr, another_case_expr2; struct expr *e, *e2, *another_e, *another_e2; int signed_p, skip_range_p; check_labels (c2m_ctx, labels, r); check (c2m_ctx, expr, r); type = ((struct expr *) expr->attr)->type; if (!integer_type_p (type)) { init_type (&t); t.mode = TM_BASIC; t.u.basic_type = TP_INT; error (c2m_ctx, expr->pos, "switch-expr is of non-integer type"); } else { t = integer_promotion (type); } signed_p = signed_integer_type_p (type); curr_switch = curr_loop_switch = r; switch_attr = curr_switch->attr = reg_malloc (c2m_ctx, sizeof (struct switch_attr)); switch_attr->type = t; switch_attr->ranges_p = FALSE; switch_attr->min_val_case = switch_attr->max_val_case = NULL; DLIST_INIT (case_t, ((struct switch_attr *) curr_switch->attr)->case_labels); check (c2m_ctx, stmt, r); for (case_t c = DLIST_HEAD (case_t, switch_attr->case_labels); c != NULL; c = DLIST_NEXT (case_t, c)) { /* process simple cases */ if (c->case_node->code == N_DEFAULT || NL_EL (c->case_node->ops, 1) != NULL) continue; if (HTAB_DO (case_t, case_tab, c, HTAB_FIND, el)) { error (c2m_ctx, c->case_node->pos, "duplicate case value"); continue; } HTAB_DO (case_t, case_tab, c, HTAB_INSERT, el); if (switch_attr->min_val_case == NULL) { switch_attr->min_val_case = switch_attr->max_val_case = c; continue; } e = NL_HEAD (c->case_node->ops)->attr; e2 = NL_HEAD (switch_attr->min_val_case->case_node->ops)->attr; if (signed_p ? e->u.i_val < e2->u.i_val : e->u.u_val < e2->u.u_val) switch_attr->min_val_case = c; e2 = NL_HEAD (switch_attr->max_val_case->case_node->ops)->attr; if (signed_p ? e->u.i_val > e2->u.i_val : e->u.u_val > e2->u.u_val) switch_attr->max_val_case = c; } HTAB_CLEAR (case_t, case_tab); /* Check range cases against *all* simple cases or range cases *before* it. */ for (case_t c = DLIST_HEAD (case_t, switch_attr->case_labels); c != NULL; c = DLIST_NEXT (case_t, c)) { if (c->case_node->code == N_DEFAULT || (case_expr2 = NL_EL (c->case_node->ops, 1)) == NULL) continue; switch_attr->ranges_p = TRUE; case_expr = NL_HEAD (c->case_node->ops); e = case_expr->attr; e2 = case_expr2->attr; skip_range_p = FALSE; for (case_t c2 = DLIST_HEAD (case_t, switch_attr->case_labels); c2 != NULL; c2 = DLIST_NEXT (case_t, c2)) { if (c2->case_node->code == N_DEFAULT) continue; if (c2 == c) { skip_range_p = TRUE; continue; } another_case_expr = NL_HEAD (c2->case_node->ops); another_case_expr2 = NL_EL (c2->case_node->ops, 1); if (skip_range_p && another_case_expr2 != NULL) continue; another_e = another_case_expr->attr; assert (another_e->const_p && integer_type_p (another_e->type)); if (another_case_expr2 == NULL) { if ((signed_p && e->u.i_val <= another_e->u.i_val && another_e->u.i_val <= e2->u.i_val) || (!signed_p && e->u.u_val <= another_e->u.u_val && another_e->u.u_val <= e2->u.u_val)) { error (c2m_ctx, c->case_node->pos, "duplicate value in a range case"); break; } } else { another_e2 = another_case_expr2->attr; assert (another_e2->const_p && integer_type_p (another_e2->type)); if ((signed_p && ((e->u.i_val <= another_e->u.i_val && another_e->u.i_val <= e2->u.i_val) || (e->u.i_val <= another_e2->u.i_val && another_e2->u.i_val <= e2->u.i_val))) || (!signed_p && ((e->u.u_val <= another_e->u.u_val && another_e->u.u_val <= e2->u.u_val) || (e->u.u_val <= another_e2->u.u_val && another_e2->u.u_val <= e2->u.u_val)))) { error (c2m_ctx, c->case_node->pos, "duplicate value in a range case"); break; } } } } curr_switch = saved_switch; curr_loop_switch = saved_loop_switch; break; } case N_DO: case N_WHILE: { node_t labels = NL_HEAD (r->ops); node_t expr = NL_NEXT (labels); node_t stmt = NL_NEXT (expr); node_t saved_loop = curr_loop; node_t saved_loop_switch = curr_loop_switch; check_labels (c2m_ctx, labels, r); check (c2m_ctx, expr, r); e1 = expr->attr; t1 = e1->type; if (!scalar_type_p (t1)) { error (c2m_ctx, expr->pos, "while-expr should be of a scalar type"); } curr_loop = curr_loop_switch = r; check (c2m_ctx, stmt, r); curr_loop_switch = saved_loop_switch; curr_loop = saved_loop; break; } case N_FOR: { node_t labels = NL_HEAD (r->ops); node_t init = NL_NEXT (labels); node_t cond = NL_NEXT (init); node_t iter = NL_NEXT (cond); node_t stmt = NL_NEXT (iter); decl_t decl; node_t saved_loop = curr_loop; node_t saved_loop_switch = curr_loop_switch; check_labels (c2m_ctx, labels, r); create_node_scope (c2m_ctx, r); curr_loop = curr_loop_switch = r; check (c2m_ctx, init, r); if (init->code == N_LIST) { for (node_t spec_decl = NL_HEAD (init->ops); spec_decl != NULL; spec_decl = NL_NEXT (spec_decl)) { assert (spec_decl->code == N_SPEC_DECL); decl = spec_decl->attr; if (decl->decl_spec.typedef_p || decl->decl_spec.extern_p || decl->decl_spec.static_p || decl->decl_spec.thread_local_p) { error (c2m_ctx, spec_decl->pos, "wrong storage specifier of for-loop initial declaration"); break; } } } if (cond->code != N_IGNORE) { /* non-empty condition: */ check (c2m_ctx, cond, r); e1 = cond->attr; t1 = e1->type; if (!scalar_type_p (t1)) { error (c2m_ctx, cond->pos, "for-condition should be of a scalar type"); } } check (c2m_ctx, iter, r); check (c2m_ctx, stmt, r); finish_scope (c2m_ctx); curr_loop_switch = saved_loop_switch; curr_loop = saved_loop; break; } case N_GOTO: { node_t labels = NL_HEAD (r->ops); check_labels (c2m_ctx, labels, r); VARR_PUSH (node_t, gotos, r); break; } case N_CONTINUE: case N_BREAK: { node_t labels = NL_HEAD (r->ops); if (r->code == N_BREAK && curr_loop_switch == NULL) { error (c2m_ctx, r->pos, "break statement not within loop or switch"); } else if (r->code == N_CONTINUE && curr_loop == NULL) { error (c2m_ctx, r->pos, "continue statement not within a loop"); } check_labels (c2m_ctx, labels, r); break; } case N_RETURN: { node_t labels = NL_HEAD (r->ops); node_t expr = NL_NEXT (labels); decl_t decl = curr_func_def->attr; struct type *ret_type, *type = decl->decl_spec.type; assert (type->mode == TM_FUNC); check_labels (c2m_ctx, labels, r); check (c2m_ctx, expr, r); ret_type = type->u.func_type->ret_type; if (expr->code != N_IGNORE && void_type_p (ret_type)) { error (c2m_ctx, r->pos, "return with a value in function returning void"); } else if (expr->code == N_IGNORE && (ret_type->mode != TM_BASIC || ret_type->u.basic_type != TP_VOID)) { error (c2m_ctx, r->pos, "return with no value in function returning non-void"); } else if (expr->code != N_IGNORE) { check_assignment_types (c2m_ctx, ret_type, NULL, expr->attr, r); } break; } case N_EXPR: { node_t labels = NL_HEAD (r->ops); node_t expr = NL_NEXT (labels); check_labels (c2m_ctx, labels, r); check (c2m_ctx, expr, r); break; } default: abort (); } if (e != NULL) { node_t base; mir_llong offset; int deref; assert (!stmt_p); if (context && context->code != N_ALIGNOF && context->code != N_SIZEOF && context->code != N_EXPR_SIZEOF) e->type = adjust_type (c2m_ctx, e->type); set_type_layout (c2m_ctx, e->type); if (!e->const_p && check_const_addr_p (c2m_ctx, r, &base, &offset, &deref) && deref == 0 && base == NULL) { e->const_p = TRUE; e->u.i_val = offset; } if (e->const_p) convert_value (e, e->type); } else if (stmt_p) { curr_call_arg_area_offset = 0; } else if (expr_attr_p) { /* it is an error -- define any expr and type: */ assert (!stmt_p); e = create_expr (c2m_ctx, r); e->type->mode = TM_BASIC; e->type->u.basic_type = TP_INT; } VARR_POP (node_t, context_stack); } static void do_context (c2m_ctx_t c2m_ctx, node_t r) { VARR_TRUNC (node_t, call_nodes, 0); check (c2m_ctx, r, NULL); } static void context_init (MIR_context_t ctx) { c2m_ctx_t c2m_ctx = *c2m_ctx_loc (ctx); c2m_ctx->check_ctx = c2mir_calloc (ctx, sizeof (struct check_ctx)); n_i1_node = new_i_node (c2m_ctx, 1, no_pos); VARR_CREATE (node_t, context_stack, 64); check (c2m_ctx, n_i1_node, NULL); func_block_scope = curr_scope = NULL; VARR_CREATE (node_t, gotos, 0); symbol_init (c2m_ctx); in_params_p = FALSE; curr_unnamed_anon_struct_union_member = NULL; HTAB_CREATE (case_t, case_tab, 100, case_hash, case_eq, NULL); VARR_CREATE (decl_t, func_decls_for_allocation, 1024); } static void context_finish (MIR_context_t ctx) { c2m_ctx_t c2m_ctx = *c2m_ctx_loc (ctx); if (c2m_ctx == NULL || c2m_ctx->check_ctx == NULL) return; if (context_stack != NULL) VARR_DESTROY (node_t, context_stack); if (gotos != NULL) VARR_DESTROY (node_t, gotos); symbol_finish (c2m_ctx); if (case_tab != NULL) HTAB_DESTROY (case_t, case_tab); if (func_decls_for_allocation != NULL) VARR_DESTROY (decl_t, func_decls_for_allocation); free (c2m_ctx->check_ctx); } /* ------------------------ Context Checker Finish ---------------------------- */ /* New Page */ /* -------------------------- MIR generator start ----------------------------- */ static const char *FP_NAME = "fp"; static const char *RET_ADDR_NAME = "Ret_Addr"; /* New attribute for non-empty label LIST is a MIR label. */ /* MIR var naming: {I|U|i|u|f|d}_ -- temporary of I64, U64, I32, U32, F, D type {I|U|i|u|f|d}_ -- variable with of original of the corresponding type in scope with */ #if MIR_PTR64 static const MIR_type_t MIR_POINTER_TYPE = MIR_T_I64; #else static const MIR_type_t MIR_POINTER_TYPE = MIR_T_I32; #endif struct op { decl_t decl; MIR_op_t mir_op; }; typedef struct op op_t; struct reg_var { const char *name; MIR_reg_t reg; }; typedef struct reg_var reg_var_t; DEF_HTAB (reg_var_t); static VARR (MIR_var_t) * vars; struct init_el { mir_size_t num, offset; decl_t member_decl; /* NULL for non-member initialization */ struct type *el_type; node_t init; }; typedef struct init_el init_el_t; DEF_VARR (init_el_t); DEF_VARR (MIR_op_t); DEF_VARR (case_t); struct gen_ctx { op_t zero_op, one_op, minus_one_op; MIR_item_t curr_func; HTAB (reg_var_t) * reg_var_tab; int reg_free_mark; MIR_label_t continue_label, break_label; VARR (node_t) * mem_params; VARR (init_el_t) * init_els; MIR_item_t memset_proto, memset_item; MIR_item_t memcpy_proto, memcpy_item; VARR (MIR_op_t) * call_ops, *switch_ops; VARR (case_t) * switch_cases; int curr_mir_proto_num; }; #define zero_op c2m_ctx->gen_ctx->zero_op #define one_op c2m_ctx->gen_ctx->one_op #define minus_one_op c2m_ctx->gen_ctx->minus_one_op #define curr_func c2m_ctx->gen_ctx->curr_func #define reg_var_tab c2m_ctx->gen_ctx->reg_var_tab #define reg_free_mark c2m_ctx->gen_ctx->reg_free_mark #define continue_label c2m_ctx->gen_ctx->continue_label #define break_label c2m_ctx->gen_ctx->break_label #define mem_params c2m_ctx->gen_ctx->mem_params #define init_els c2m_ctx->gen_ctx->init_els #define memset_proto c2m_ctx->gen_ctx->memset_proto #define memset_item c2m_ctx->gen_ctx->memset_item #define memcpy_proto c2m_ctx->gen_ctx->memcpy_proto #define memcpy_item c2m_ctx->gen_ctx->memcpy_item #define call_ops c2m_ctx->gen_ctx->call_ops #define switch_ops c2m_ctx->gen_ctx->switch_ops #define switch_cases c2m_ctx->gen_ctx->switch_cases #define curr_mir_proto_num c2m_ctx->gen_ctx->curr_mir_proto_num static op_t new_op (decl_t decl, MIR_op_t mir_op) { op_t res; res.decl = decl; res.mir_op = mir_op; return res; } static htab_hash_t reg_var_hash (reg_var_t r, void *arg) { return mir_hash (r.name, strlen (r.name), 0x42); } static int reg_var_eq (reg_var_t r1, reg_var_t r2, void *arg) { return strcmp (r1.name, r2.name) == 0; } static void init_reg_vars (MIR_context_t ctx) { c2m_ctx_t c2m_ctx = *c2m_ctx_loc (ctx); reg_free_mark = 0; HTAB_CREATE (reg_var_t, reg_var_tab, 128, reg_var_hash, reg_var_eq, NULL); } static void finish_curr_func_reg_vars (MIR_context_t ctx) { c2m_ctx_t c2m_ctx = *c2m_ctx_loc (ctx); reg_free_mark = 0; HTAB_CLEAR (reg_var_t, reg_var_tab); } static void finish_reg_vars (MIR_context_t ctx) { c2m_ctx_t c2m_ctx = *c2m_ctx_loc (ctx); if (reg_var_tab != NULL) HTAB_DESTROY (reg_var_t, reg_var_tab); } static reg_var_t get_reg_var (MIR_context_t ctx, MIR_type_t t, const char *reg_name) { c2m_ctx_t c2m_ctx = *c2m_ctx_loc (ctx); reg_var_t reg_var, el; char *str; MIR_reg_t reg; reg_var.name = reg_name; if (HTAB_DO (reg_var_t, reg_var_tab, reg_var, HTAB_FIND, el)) return el; t = t == MIR_T_I32 || t == MIR_T_U32 || t == MIR_T_U64 ? MIR_T_I64 : t; reg = (t != MIR_T_UNDEF ? MIR_new_func_reg (ctx, curr_func->u.func, t, reg_name) : MIR_reg (ctx, reg_name, curr_func->u.func)); str = reg_malloc (c2m_ctx, (strlen (reg_name) + 1) * sizeof (char)); strcpy (str, reg_name); reg_var.name = str; reg_var.reg = reg; HTAB_DO (reg_var_t, reg_var_tab, reg_var, HTAB_INSERT, el); return reg_var; } static int temp_reg_p (MIR_context_t ctx, MIR_op_t op) { c2m_ctx_t c2m_ctx = *c2m_ctx_loc (ctx); return op.mode == MIR_OP_REG && MIR_reg_name (ctx, op.u.reg, curr_func->u.func)[1] == '_'; } static MIR_type_t reg_type (MIR_context_t ctx, MIR_reg_t reg) { c2m_ctx_t c2m_ctx = *c2m_ctx_loc (ctx); const char *n = MIR_reg_name (ctx, reg, curr_func->u.func); MIR_type_t res; if (strcmp (n, FP_NAME) == 0 || strcmp (n, RET_ADDR_NAME) == 0) return MIR_POINTER_TYPE; res = (n[0] == 'I' ? MIR_T_I64 : n[0] == 'U' ? MIR_T_U64 : n[0] == 'i' ? MIR_T_I32 : n[0] == 'u' ? MIR_T_U32 : n[0] == 'f' ? MIR_T_F : n[0] == 'd' ? MIR_T_D : n[0] == 'D' ? MIR_T_LD : MIR_T_BOUND); assert (res != MIR_T_BOUND); return res; } static op_t get_new_temp (MIR_context_t ctx, MIR_type_t t) { c2m_ctx_t c2m_ctx = *c2m_ctx_loc (ctx); static char reg_name[50]; MIR_reg_t reg; assert (t == MIR_T_I64 || t == MIR_T_U64 || t == MIR_T_I32 || t == MIR_T_U32 || t == MIR_T_F || t == MIR_T_D || t == MIR_T_LD); sprintf (reg_name, t == MIR_T_I64 ? "I_%u" : t == MIR_T_U64 ? "U_%u" : t == MIR_T_I32 ? "i_%u" : t == MIR_T_U32 ? "u_%u" : t == MIR_T_F ? "f_%u" : t == MIR_T_D ? "d_%u" : "D_%u", reg_free_mark++); reg = get_reg_var (ctx, t, reg_name).reg; return new_op (NULL, MIR_new_reg_op (ctx, reg)); } static MIR_type_t get_int_mir_type (size_t size) { return size == 1 ? MIR_T_I8 : size == 2 ? MIR_T_I16 : size == 4 ? MIR_T_I32 : MIR_T_I64; } static int MIR_UNUSED get_int_mir_type_size (MIR_type_t t) { return (t == MIR_T_I8 || t == MIR_T_U8 ? 1 : t == MIR_T_I16 || t == MIR_T_U16 ? 2 : t == MIR_T_I32 || t == MIR_T_U32 ? 4 : 8); } static MIR_type_t get_mir_type (MIR_context_t ctx, struct type *type) { c2m_ctx_t c2m_ctx = *c2m_ctx_loc (ctx); size_t size = raw_type_size (c2m_ctx, type); int int_p = !floating_type_p (type), signed_p = signed_integer_type_p (type); if (!scalar_type_p (type)) return MIR_T_UNDEF; assert (type->mode == TM_BASIC || type->mode == TM_PTR || type->mode == TM_ENUM); if (!int_p) { assert (size == 4 || size == 8 || size == sizeof (mir_ldouble)); return size == 4 ? MIR_T_F : size == 8 ? MIR_T_D : MIR_T_LD; } assert (size <= 2 || size == 4 || size == 8); if (signed_p) return get_int_mir_type (size); return size == 1 ? MIR_T_U8 : size == 2 ? MIR_T_U16 : size == 4 ? MIR_T_U32 : MIR_T_U64; } static MIR_type_t promote_mir_int_type (MIR_type_t t) { return (t == MIR_T_I8 || t == MIR_T_I16 ? MIR_T_I32 : t == MIR_T_U8 || t == MIR_T_U16 ? MIR_T_U32 : t); } static MIR_type_t get_op_type (MIR_context_t ctx, op_t op) { switch (op.mir_op.mode) { case MIR_OP_MEM: return op.mir_op.u.mem.type; case MIR_OP_REG: return reg_type (ctx, op.mir_op.u.reg); case MIR_OP_INT: return MIR_T_I64; case MIR_OP_UINT: return MIR_T_U64; case MIR_OP_FLOAT: return MIR_T_F; case MIR_OP_DOUBLE: return MIR_T_D; case MIR_OP_LDOUBLE: return MIR_T_LD; default: assert (FALSE); return MIR_T_I64; } } static op_t gen (MIR_context_t ctx, node_t r, MIR_label_t true_label, MIR_label_t false_label, int val_p, op_t *desirable_dest); static int push_const_val (MIR_context_t ctx, node_t r, op_t *res) { struct expr *e = (struct expr *) r->attr; MIR_type_t mir_type; if (!e->const_p) return FALSE; if (floating_type_p (e->type)) { /* MIR support only IEEE float and double */ mir_type = get_mir_type (ctx, e->type); *res = new_op (NULL, (mir_type == MIR_T_F ? MIR_new_float_op (ctx, e->u.d_val) : mir_type == MIR_T_D ? MIR_new_double_op (ctx, e->u.d_val) : MIR_new_ldouble_op (ctx, e->u.d_val))); } else { assert (integer_type_p (e->type) || e->type->mode == TM_PTR); *res = new_op (NULL, (signed_integer_type_p (e->type) ? MIR_new_int_op (ctx, e->u.i_val) : MIR_new_uint_op (ctx, e->u.u_val))); } return TRUE; } static MIR_insn_code_t tp_mov (MIR_type_t t) { return t == MIR_T_F ? MIR_FMOV : t == MIR_T_D ? MIR_DMOV : t == MIR_T_LD ? MIR_LDMOV : MIR_MOV; } static void emit_insn (MIR_context_t ctx, MIR_insn_t insn) { c2m_ctx_t c2m_ctx = *c2m_ctx_loc (ctx); MIR_append_insn (ctx, curr_func, insn); } /* BCOND T, L1; JMP L2; L1: => BNCOND T, L2; L1: JMP L; L: => L: */ static void emit_label_insn_opt (MIR_context_t ctx, MIR_insn_t insn) { c2m_ctx_t c2m_ctx = *c2m_ctx_loc (ctx); MIR_insn_code_t rev_code; MIR_insn_t last, prev; assert (insn->code == MIR_LABEL); if ((last = DLIST_TAIL (MIR_insn_t, curr_func->u.func->insns)) != NULL && (prev = DLIST_PREV (MIR_insn_t, last)) != NULL && last->code == MIR_JMP && (rev_code = MIR_reverse_branch_code (prev->code)) != MIR_INSN_BOUND && prev->ops[0].mode == MIR_OP_LABEL && prev->ops[0].u.label == insn) { prev->ops[0] = last->ops[0]; prev->code = rev_code; MIR_remove_insn (ctx, curr_func, last); } if ((last = DLIST_TAIL (MIR_insn_t, curr_func->u.func->insns)) != NULL && last->code == MIR_JMP && last->ops[0].mode == MIR_OP_LABEL && last->ops[0].u.label == insn) { MIR_remove_insn (ctx, curr_func, last); } MIR_append_insn (ctx, curr_func, insn); } /* Change t1 = expr; v = t1 to v = expr */ static void emit_insn_opt (MIR_context_t ctx, MIR_insn_t insn) { c2m_ctx_t c2m_ctx = *c2m_ctx_loc (ctx); MIR_insn_t tail; int out_p; if ((insn->code == MIR_MOV || insn->code == MIR_FMOV || insn->code == MIR_DMOV || insn->code == MIR_LDMOV) && (tail = DLIST_TAIL (MIR_insn_t, curr_func->u.func->insns)) != NULL && MIR_insn_nops (ctx, tail) > 0 && temp_reg_p (ctx, insn->ops[1]) && !temp_reg_p (ctx, insn->ops[0]) && temp_reg_p (ctx, tail->ops[0]) && insn->ops[1].u.reg == tail->ops[0].u.reg) { MIR_insn_op_mode (ctx, tail, 0, &out_p); if (out_p) { tail->ops[0] = insn->ops[0]; MIR_append_insn (ctx, curr_func, insn); MIR_remove_insn (ctx, curr_func, insn); return; } } MIR_append_insn (ctx, curr_func, insn); } static void emit1 (MIR_context_t ctx, MIR_insn_code_t code, MIR_op_t op1) { emit_insn_opt (ctx, MIR_new_insn (ctx, code, op1)); } static void emit2 (MIR_context_t ctx, MIR_insn_code_t code, MIR_op_t op1, MIR_op_t op2) { emit_insn_opt (ctx, MIR_new_insn (ctx, code, op1, op2)); } static void emit3 (MIR_context_t ctx, MIR_insn_code_t code, MIR_op_t op1, MIR_op_t op2, MIR_op_t op3) { emit_insn_opt (ctx, MIR_new_insn (ctx, code, op1, op2, op3)); } static void emit2_noopt (MIR_context_t ctx, MIR_insn_code_t code, MIR_op_t op1, MIR_op_t op2) { emit_insn (ctx, MIR_new_insn (ctx, code, op1, op2)); } static op_t cast (MIR_context_t ctx, op_t op, MIR_type_t t, int new_op_p) { op_t res, interm; MIR_type_t op_type; MIR_insn_code_t insn_code = MIR_INSN_BOUND, insn_code2 = MIR_INSN_BOUND; assert (t == MIR_T_I8 || t == MIR_T_U8 || t == MIR_T_I16 || t == MIR_T_U16 || t == MIR_T_I32 || t == MIR_T_U32 || t == MIR_T_I64 || t == MIR_T_U64 || t == MIR_T_F || t == MIR_T_D || t == MIR_T_LD); switch (op.mir_op.mode) { case MIR_OP_MEM: op_type = op.mir_op.u.mem.type; if (op_type == MIR_T_UNDEF) { /* ??? it is an array */ } else if (op_type == MIR_T_I8 || op_type == MIR_T_U8 || op_type == MIR_T_I16 || op_type == MIR_T_U16 || op_type == MIR_T_I32 || op_type == MIR_T_U32) op_type = MIR_T_I64; goto all_types; case MIR_OP_REG: op_type = reg_type (ctx, op.mir_op.u.reg); all_types: if (op_type == MIR_T_F) goto float_val; if (op_type == MIR_T_D) goto double_val; if (op_type == MIR_T_LD) goto ldouble_val; if (t == MIR_T_I64) { insn_code = (op_type == MIR_T_I32 ? MIR_EXT32 : op_type == MIR_T_U32 ? MIR_UEXT32 : op_type == MIR_T_F ? MIR_F2I : op_type == MIR_T_D ? MIR_D2I : op_type == MIR_T_LD ? MIR_LD2I : MIR_INSN_BOUND); } else if (t == MIR_T_U64) { insn_code = (op_type == MIR_T_I32 ? MIR_EXT32 : op_type == MIR_T_U32 ? MIR_UEXT32 : op_type == MIR_T_F ? MIR_F2I : op_type == MIR_T_D ? MIR_D2I : op_type == MIR_T_LD ? MIR_LD2I : MIR_INSN_BOUND); } else if (t == MIR_T_I32) { insn_code = (op_type == MIR_T_F ? MIR_F2I : op_type == MIR_T_D ? MIR_D2I : op_type == MIR_T_LD ? MIR_LD2I : MIR_INSN_BOUND); } else if (t == MIR_T_U32) { insn_code = (op_type == MIR_T_F ? MIR_F2I : op_type == MIR_T_D ? MIR_D2I : op_type == MIR_T_LD ? MIR_LD2I : MIR_INSN_BOUND); } else if (t == MIR_T_I16) { insn_code = (op_type == MIR_T_F ? MIR_F2I : op_type == MIR_T_D ? MIR_D2I : op_type == MIR_T_LD ? MIR_LD2I : MIR_INSN_BOUND); insn_code2 = MIR_EXT16; } else if (t == MIR_T_U16) { insn_code = (op_type == MIR_T_F ? MIR_F2I : op_type == MIR_T_D ? MIR_D2I : op_type == MIR_T_LD ? MIR_LD2I : MIR_INSN_BOUND); insn_code2 = MIR_UEXT16; } else if (t == MIR_T_I8) { insn_code = (op_type == MIR_T_F ? MIR_F2I : op_type == MIR_T_D ? MIR_D2I : op_type == MIR_T_LD ? MIR_LD2I : MIR_INSN_BOUND); insn_code2 = MIR_EXT8; } else if (t == MIR_T_U8) { insn_code = (op_type == MIR_T_F ? MIR_F2I : op_type == MIR_T_D ? MIR_D2I : op_type == MIR_T_LD ? MIR_LD2I : MIR_INSN_BOUND); insn_code2 = MIR_UEXT8; } else if (t == MIR_T_F) { insn_code = (op_type == MIR_T_I32 ? MIR_EXT32 : op_type == MIR_T_U32 ? MIR_UEXT32 : MIR_INSN_BOUND); insn_code2 = (op_type == MIR_T_I64 || op_type == MIR_T_I32 ? MIR_I2F : op_type == MIR_T_U64 || op_type == MIR_T_U32 ? MIR_UI2F : MIR_INSN_BOUND); } else if (t == MIR_T_D) { insn_code = (op_type == MIR_T_I32 ? MIR_EXT32 : op_type == MIR_T_U32 ? MIR_UEXT32 : MIR_INSN_BOUND); insn_code2 = (op_type == MIR_T_I64 || op_type == MIR_T_I32 ? MIR_I2D : op_type == MIR_T_U64 || op_type == MIR_T_U32 ? MIR_UI2D : MIR_INSN_BOUND); } else if (t == MIR_T_LD) { insn_code = (op_type == MIR_T_I32 ? MIR_EXT32 : op_type == MIR_T_U32 ? MIR_UEXT32 : MIR_INSN_BOUND); insn_code2 = (op_type == MIR_T_I64 || op_type == MIR_T_I32 ? MIR_I2LD : op_type == MIR_T_U64 || op_type == MIR_T_U32 ? MIR_UI2LD : MIR_INSN_BOUND); } break; case MIR_OP_INT: insn_code = (t == MIR_T_I8 ? MIR_EXT8 : t == MIR_T_U8 ? MIR_UEXT8 : t == MIR_T_I16 ? MIR_EXT16 : t == MIR_T_U16 ? MIR_UEXT16 : t == MIR_T_F ? MIR_I2F : t == MIR_T_D ? MIR_I2D : t == MIR_T_LD ? MIR_I2LD : MIR_INSN_BOUND); break; case MIR_OP_UINT: insn_code = (t == MIR_T_I8 ? MIR_EXT8 : t == MIR_T_U8 ? MIR_UEXT8 : t == MIR_T_I16 ? MIR_EXT16 : t == MIR_T_U16 ? MIR_UEXT16 : t == MIR_T_F ? MIR_UI2F : t == MIR_T_D ? MIR_UI2D : t == MIR_T_LD ? MIR_UI2LD : MIR_INSN_BOUND); break; case MIR_OP_FLOAT: float_val: insn_code = (t == MIR_T_I8 || t == MIR_T_U8 || t == MIR_T_I16 || t == MIR_T_U16 || t == MIR_T_I32 || t == MIR_T_U32 || t == MIR_T_I64 || t == MIR_T_U64 ? MIR_F2I : t == MIR_T_D ? MIR_F2D : t == MIR_T_LD ? MIR_F2LD : MIR_INSN_BOUND); insn_code2 = (t == MIR_T_I8 ? MIR_EXT8 : t == MIR_T_U8 ? MIR_UEXT8 : t == MIR_T_I16 ? MIR_EXT16 : t == MIR_T_U16 ? MIR_UEXT16 : MIR_INSN_BOUND); break; case MIR_OP_DOUBLE: double_val: insn_code = (t == MIR_T_I8 || t == MIR_T_U8 || t == MIR_T_I16 || t == MIR_T_U16 || t == MIR_T_I32 || t == MIR_T_U32 || t == MIR_T_I64 || t == MIR_T_U64 ? MIR_D2I : t == MIR_T_F ? MIR_D2F : t == MIR_T_LD ? MIR_D2LD : MIR_INSN_BOUND); insn_code2 = (t == MIR_T_I8 ? MIR_EXT8 : t == MIR_T_U8 ? MIR_UEXT8 : t == MIR_T_I16 ? MIR_EXT16 : t == MIR_T_U16 ? MIR_UEXT16 : MIR_INSN_BOUND); break; case MIR_OP_LDOUBLE: ldouble_val: insn_code = (t == MIR_T_I8 || t == MIR_T_U8 || t == MIR_T_I16 || t == MIR_T_U16 || t == MIR_T_I32 || t == MIR_T_U32 || t == MIR_T_I64 || t == MIR_T_U64 ? MIR_LD2I : t == MIR_T_F ? MIR_LD2F : t == MIR_T_D ? MIR_LD2D : MIR_INSN_BOUND); insn_code2 = (t == MIR_T_I8 ? MIR_EXT8 : t == MIR_T_U8 ? MIR_UEXT8 : t == MIR_T_I16 ? MIR_EXT16 : t == MIR_T_U16 ? MIR_UEXT16 : MIR_INSN_BOUND); break; default: break; } if (!new_op_p && insn_code == MIR_INSN_BOUND && insn_code2 == MIR_INSN_BOUND) return op; res = get_new_temp (ctx, t == MIR_T_I8 || t == MIR_T_U8 || t == MIR_T_I16 || t == MIR_T_U16 ? MIR_T_I64 : t); if (insn_code == MIR_INSN_BOUND && insn_code2 == MIR_INSN_BOUND) { emit2 (ctx, tp_mov (t), res.mir_op, op.mir_op); } else if (insn_code == MIR_INSN_BOUND) { emit2 (ctx, insn_code2, res.mir_op, op.mir_op); } else if (insn_code2 == MIR_INSN_BOUND) { emit2 (ctx, insn_code, res.mir_op, op.mir_op); } else { interm = get_new_temp (ctx, MIR_T_I64); emit2 (ctx, insn_code, interm.mir_op, op.mir_op); emit2 (ctx, insn_code2, res.mir_op, interm.mir_op); } return res; } static op_t promote (MIR_context_t ctx, op_t op, MIR_type_t t, int new_op_p) { assert (t == MIR_T_I64 || t == MIR_T_U64 || t == MIR_T_I32 || t == MIR_T_U32 || t == MIR_T_F || t == MIR_T_D || t == MIR_T_LD); return cast (ctx, op, t, new_op_p); } static op_t mem_to_address (MIR_context_t ctx, op_t mem) { op_t temp; if (mem.mir_op.mode == MIR_OP_STR) return mem; assert (mem.mir_op.mode == MIR_OP_MEM); if (mem.mir_op.u.mem.base == 0 && mem.mir_op.u.mem.index == 0) { mem.mir_op.mode = MIR_OP_INT; mem.mir_op.u.i = mem.mir_op.u.mem.disp; } else if (mem.mir_op.u.mem.index == 0 && mem.mir_op.u.mem.disp == 0) { mem.mir_op.mode = MIR_OP_REG; mem.mir_op.u.reg = mem.mir_op.u.mem.base; } else if (mem.mir_op.u.mem.index == 0) { temp = get_new_temp (ctx, MIR_T_I64); emit3 (ctx, MIR_ADD, temp.mir_op, MIR_new_reg_op (ctx, mem.mir_op.u.mem.base), MIR_new_int_op (ctx, mem.mir_op.u.mem.disp)); mem = temp; } else { temp = get_new_temp (ctx, MIR_T_I64); if (mem.mir_op.u.mem.scale != 1) emit3 (ctx, MIR_MUL, temp.mir_op, MIR_new_reg_op (ctx, mem.mir_op.u.mem.index), MIR_new_int_op (ctx, mem.mir_op.u.mem.scale)); else emit2 (ctx, MIR_MOV, temp.mir_op, MIR_new_reg_op (ctx, mem.mir_op.u.mem.index)); if (mem.mir_op.u.mem.base != 0) emit3 (ctx, MIR_ADD, temp.mir_op, temp.mir_op, MIR_new_reg_op (ctx, mem.mir_op.u.mem.base)); if (mem.mir_op.u.mem.disp != 0) emit3 (ctx, MIR_ADD, temp.mir_op, temp.mir_op, MIR_new_int_op (ctx, mem.mir_op.u.mem.disp)); mem = temp; } mem.mir_op.value_mode = MIR_OP_INT; return mem; } static op_t force_val (MIR_context_t ctx, op_t op, int arr_p) { op_t temp_op; int sh; c2m_ctx_t c2m_ctx; if (arr_p && op.mir_op.mode == MIR_OP_MEM) { /* an array -- use a pointer: */ return mem_to_address (ctx, op); } if (op.decl == NULL || op.decl->bit_offset < 0) return op; c2m_ctx = *c2m_ctx_loc (ctx); assert (op.mir_op.mode == MIR_OP_MEM); temp_op = get_new_temp (ctx, MIR_T_I64); emit2 (ctx, MIR_MOV, temp_op.mir_op, op.mir_op); #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ sh = 64 - op.decl->bit_offset - op.decl->width; #else sh = op.decl->bit_offset + (64 - type_size (c2m_ctx, op.decl->decl_spec.type) * MIR_CHAR_BIT); #endif if (sh != 0) emit3 (ctx, MIR_LSH, temp_op.mir_op, temp_op.mir_op, MIR_new_int_op (ctx, sh)); emit3 (ctx, signed_integer_type_p (op.decl->decl_spec.type) && (op.decl->decl_spec.type->mode != TM_ENUM || op.decl->width >= sizeof (mir_int) * MIR_CHAR_BIT) ? MIR_RSH : MIR_URSH, temp_op.mir_op, temp_op.mir_op, MIR_new_int_op (ctx, 64 - op.decl->width)); return temp_op; } static void gen_unary_op (MIR_context_t ctx, node_t r, op_t *op, op_t *res) { MIR_type_t t; assert (!((struct expr *) r->attr)->const_p); *op = gen (ctx, NL_HEAD (r->ops), NULL, NULL, TRUE, NULL); t = get_mir_type (ctx, ((struct expr *) r->attr)->type); *op = promote (ctx, *op, t, FALSE); *res = get_new_temp (ctx, t); } static void gen_assign_bin_op (MIR_context_t ctx, node_t r, struct type *assign_expr_type, op_t *op1, op_t *op2, op_t *var) { MIR_type_t t; node_t e = NL_HEAD (r->ops); assert (!((struct expr *) r->attr)->const_p); t = get_mir_type (ctx, assign_expr_type); *op1 = gen (ctx, e, NULL, NULL, FALSE, NULL); *op2 = gen (ctx, NL_NEXT (e), NULL, NULL, TRUE, NULL); *op2 = promote (ctx, *op2, t, FALSE); *var = *op1; *op1 = force_val (ctx, *op1, ((struct expr *) e->attr)->type->arr_type != NULL); *op1 = promote (ctx, *op1, t, TRUE); } static void gen_bin_op (MIR_context_t ctx, node_t r, op_t *op1, op_t *op2, op_t *res) { struct expr *e = (struct expr *) r->attr; MIR_type_t t = get_mir_type (ctx, e->type); assert (!e->const_p); *op1 = gen (ctx, NL_HEAD (r->ops), NULL, NULL, TRUE, NULL); *op2 = gen (ctx, NL_EL (r->ops, 1), NULL, NULL, TRUE, NULL); *op1 = promote (ctx, *op1, t, FALSE); *op2 = promote (ctx, *op2, t, FALSE); *res = get_new_temp (ctx, t); } static void gen_cmp_op (MIR_context_t ctx, node_t r, struct type *type, op_t *op1, op_t *op2, op_t *res) { MIR_type_t t = get_mir_type (ctx, type), res_t = get_int_mir_type (sizeof (mir_int)); assert (!((struct expr *) r->attr)->const_p); *op1 = gen (ctx, NL_HEAD (r->ops), NULL, NULL, TRUE, NULL); *op2 = gen (ctx, NL_EL (r->ops, 1), NULL, NULL, TRUE, NULL); *op1 = promote (ctx, *op1, t, FALSE); *op2 = promote (ctx, *op2, t, FALSE); *res = get_new_temp (ctx, res_t); } static MIR_insn_code_t get_mir_type_insn_code (MIR_context_t ctx, struct type *type, node_t r) { MIR_type_t t = get_mir_type (ctx, type); switch (r->code) { case N_INC: case N_POST_INC: case N_ADD: case N_ADD_ASSIGN: return (t == MIR_T_F ? MIR_FADD : t == MIR_T_D ? MIR_DADD : t == MIR_T_LD ? MIR_LDADD : t == MIR_T_I64 || t == MIR_T_U64 ? MIR_ADD : MIR_ADDS); case N_DEC: case N_POST_DEC: case N_SUB: case N_SUB_ASSIGN: return (t == MIR_T_F ? MIR_FSUB : t == MIR_T_D ? MIR_DSUB : t == MIR_T_LD ? MIR_LDSUB : t == MIR_T_I64 || t == MIR_T_U64 ? MIR_SUB : MIR_SUBS); case N_MUL: case N_MUL_ASSIGN: return (t == MIR_T_F ? MIR_FMUL : t == MIR_T_D ? MIR_DMUL : t == MIR_T_LD ? MIR_LDMUL : t == MIR_T_I64 || t == MIR_T_U64 ? MIR_MUL : MIR_MULS); case N_DIV: case N_DIV_ASSIGN: return (t == MIR_T_F ? MIR_FDIV : t == MIR_T_D ? MIR_DDIV : t == MIR_T_LD ? MIR_LDDIV : t == MIR_T_I64 ? MIR_DIV : t == MIR_T_U64 ? MIR_UDIV : t == MIR_T_I32 ? MIR_DIVS : MIR_UDIVS); case N_MOD: case N_MOD_ASSIGN: return (t == MIR_T_I64 ? MIR_MOD : t == MIR_T_U64 ? MIR_UMOD : t == MIR_T_I32 ? MIR_MODS : MIR_UMODS); case N_AND: case N_AND_ASSIGN: return (t == MIR_T_I64 || t == MIR_T_U64 ? MIR_AND : MIR_ANDS); case N_OR: case N_OR_ASSIGN: return (t == MIR_T_I64 || t == MIR_T_U64 ? MIR_OR : MIR_ORS); case N_XOR: case N_XOR_ASSIGN: return (t == MIR_T_I64 || t == MIR_T_U64 ? MIR_XOR : MIR_XORS); case N_LSH: case N_LSH_ASSIGN: return (t == MIR_T_I64 || t == MIR_T_U64 ? MIR_LSH : MIR_LSHS); case N_RSH: case N_RSH_ASSIGN: return (t == MIR_T_I64 ? MIR_RSH : t == MIR_T_U64 ? MIR_URSH : t == MIR_T_I32 ? MIR_RSHS : MIR_URSHS); case N_EQ: return (t == MIR_T_F ? MIR_FEQ : t == MIR_T_D ? MIR_DEQ : t == MIR_T_LD ? MIR_LDEQ : t == MIR_T_I64 || t == MIR_T_U64 ? MIR_EQ : MIR_EQS); case N_NE: return (t == MIR_T_F ? MIR_FNE : t == MIR_T_D ? MIR_DNE : t == MIR_T_LD ? MIR_LDNE : t == MIR_T_I64 || t == MIR_T_U64 ? MIR_NE : MIR_NES); case N_LT: return (t == MIR_T_F ? MIR_FLT : t == MIR_T_D ? MIR_DLT : t == MIR_T_LD ? MIR_LDLT : t == MIR_T_I64 ? MIR_LT : t == MIR_T_U64 ? MIR_ULT : t == MIR_T_I32 ? MIR_LTS : MIR_ULTS); case N_LE: return (t == MIR_T_F ? MIR_FLE : t == MIR_T_D ? MIR_DLE : t == MIR_T_LD ? MIR_LDLE : t == MIR_T_I64 ? MIR_LE : t == MIR_T_U64 ? MIR_ULE : t == MIR_T_I32 ? MIR_LES : MIR_ULES); case N_GT: return (t == MIR_T_F ? MIR_FGT : t == MIR_T_D ? MIR_DGT : t == MIR_T_LD ? MIR_LDGT : t == MIR_T_I64 ? MIR_GT : t == MIR_T_U64 ? MIR_UGT : t == MIR_T_I32 ? MIR_GTS : MIR_UGTS); case N_GE: return (t == MIR_T_F ? MIR_FGE : t == MIR_T_D ? MIR_DGE : t == MIR_T_LD ? MIR_LDGE : t == MIR_T_I64 ? MIR_GE : t == MIR_T_U64 ? MIR_UGE : t == MIR_T_I32 ? MIR_GES : MIR_UGES); default: assert (FALSE); return MIR_INSN_BOUND; } } static MIR_insn_code_t get_mir_insn_code (MIR_context_t ctx, node_t r) { /* result type is the same as op types */ return get_mir_type_insn_code (ctx, ((struct expr *) r->attr)->type, r); } static MIR_insn_code_t get_compare_branch_code (MIR_insn_code_t code) { #define B(n) \ case MIR_##n: return MIR_B##n; \ case MIR_##n##S: return MIR_B##n##S; \ case MIR_F##n: return MIR_FB##n; \ case MIR_D##n: return MIR_DB##n; \ case MIR_LD##n: \ return MIR_LDB##n; #define BCMP(n) \ B (n) \ case MIR_U##n: return MIR_UB##n; \ case MIR_U##n##S: return MIR_UB##n##S; switch (code) { B (EQ) B (NE) BCMP (LT) BCMP (LE) BCMP (GT) BCMP (GE) default : assert (FALSE); return MIR_INSN_BOUND; } #undef B #undef BCMP } static op_t force_reg (MIR_context_t ctx, op_t op, MIR_type_t t) { op_t res; if (op.mir_op.mode == MIR_OP_REG) return op; res = get_new_temp (ctx, promote_mir_int_type (t)); emit2 (ctx, MIR_MOV, res.mir_op, op.mir_op); return res; } static op_t force_reg_or_mem (MIR_context_t ctx, op_t op, MIR_type_t t) { if (op.mir_op.mode == MIR_OP_REG || op.mir_op.mode == MIR_OP_MEM) return op; assert (op.mir_op.mode == MIR_OP_REF || op.mir_op.mode == MIR_OP_STR); return force_reg (ctx, op, t); } static void emit_label (MIR_context_t ctx, node_t r) { node_t labels = NL_HEAD (r->ops); assert (labels->code == N_LIST); if (NL_HEAD (labels->ops) == NULL) return; if (labels->attr == NULL) labels->attr = MIR_new_label (ctx); emit_label_insn_opt (ctx, labels->attr); } static MIR_label_t get_label (MIR_context_t ctx, node_t target) { node_t labels = NL_HEAD (target->ops); assert (labels->code == N_LIST && NL_HEAD (labels->ops) != NULL); if (labels->attr != NULL) return labels->attr; return labels->attr = MIR_new_label (ctx); } static void top_gen (MIR_context_t ctx, node_t r, MIR_label_t true_label, MIR_label_t false_label) { gen (ctx, r, true_label, false_label, FALSE, NULL); } static op_t modify_for_block_move (MIR_context_t ctx, op_t mem, op_t index) { op_t base; assert (mem.mir_op.u.mem.base != 0 && mem.mir_op.mode == MIR_OP_MEM && index.mir_op.mode == MIR_OP_REG); if (mem.mir_op.u.mem.index == 0) { mem.mir_op.u.mem.index = index.mir_op.u.reg; mem.mir_op.u.mem.scale = 1; } else { base = get_new_temp (ctx, MIR_T_I64); if (mem.mir_op.u.mem.scale != 1) emit3 (ctx, MIR_MUL, base.mir_op, MIR_new_reg_op (ctx, mem.mir_op.u.mem.index), MIR_new_int_op (ctx, mem.mir_op.u.mem.scale)); else emit2 (ctx, MIR_MOV, base.mir_op, MIR_new_reg_op (ctx, mem.mir_op.u.mem.index)); emit3 (ctx, MIR_ADD, base.mir_op, base.mir_op, MIR_new_reg_op (ctx, mem.mir_op.u.mem.base)); mem.mir_op.u.mem.base = base.mir_op.u.reg; mem.mir_op.u.mem.index = index.mir_op.u.reg; mem.mir_op.u.mem.scale = 1; } return mem; } static void gen_memcpy (MIR_context_t ctx, MIR_disp_t disp, MIR_reg_t base, op_t val, mir_size_t len); static void block_move (MIR_context_t ctx, op_t var, op_t val, mir_size_t size) { c2m_ctx_t c2m_ctx = *c2m_ctx_loc (ctx); MIR_label_t repeat_label; op_t index; if (MIR_op_eq_p (ctx, var.mir_op, val.mir_op)) return; if (size > 5) { var = mem_to_address (ctx, var); assert (var.mir_op.mode == MIR_OP_REG); gen_memcpy (ctx, 0, var.mir_op.u.reg, val, size); } else { repeat_label = MIR_new_label (ctx); index = get_new_temp (ctx, MIR_T_I64); emit2 (ctx, MIR_MOV, index.mir_op, MIR_new_int_op (ctx, size)); val = modify_for_block_move (ctx, val, index); var = modify_for_block_move (ctx, var, index); emit_label_insn_opt (ctx, repeat_label); emit3 (ctx, MIR_SUB, index.mir_op, index.mir_op, one_op.mir_op); assert (var.mir_op.mode == MIR_OP_MEM && val.mir_op.mode == MIR_OP_MEM); val.mir_op.u.mem.type = var.mir_op.u.mem.type = MIR_T_I8; emit2 (ctx, MIR_MOV, var.mir_op, val.mir_op); emit3 (ctx, MIR_BGT, MIR_new_label_op (ctx, repeat_label), index.mir_op, zero_op.mir_op); } } static const char *get_reg_var_name (MIR_context_t ctx, MIR_type_t promoted_type, const char *suffix, unsigned func_scope_num) { c2m_ctx_t c2m_ctx = *c2m_ctx_loc (ctx); char prefix[50]; sprintf (prefix, promoted_type == MIR_T_I64 ? "I%u_" : promoted_type == MIR_T_U64 ? "U%u_" : promoted_type == MIR_T_I32 ? "i%u_" : promoted_type == MIR_T_U32 ? "u%u_" : promoted_type == MIR_T_F ? "f%u_" : promoted_type == MIR_T_D ? "d%u_" : "D%u_", func_scope_num); VARR_TRUNC (char, temp_string, 0); add_to_temp_string (c2m_ctx, prefix); add_to_temp_string (c2m_ctx, suffix); return uniq_cstr (c2m_ctx, VARR_ADDR (char, temp_string)).s; } static const char *get_func_var_name (MIR_context_t ctx, const char *prefix, const char *suffix) { c2m_ctx_t c2m_ctx = *c2m_ctx_loc (ctx); assert (curr_func != NULL); VARR_TRUNC (char, temp_string, 0); add_to_temp_string (c2m_ctx, prefix); add_to_temp_string (c2m_ctx, curr_func->u.func->name); add_to_temp_string (c2m_ctx, "_"); add_to_temp_string (c2m_ctx, suffix); return uniq_cstr (c2m_ctx, VARR_ADDR (char, temp_string)).s; } static const char *get_func_static_var_name (MIR_context_t ctx, const char *suffix, decl_t decl) { char prefix[50]; unsigned func_scope_num = ((struct node_scope *) decl->scope->attr)->func_scope_num; sprintf (prefix, "S%u_", func_scope_num); return get_func_var_name (ctx, prefix, suffix); } static const char *get_param_name (MIR_context_t ctx, MIR_type_t *type, struct type *param_type, const char *name) { *type = (param_type->mode == TM_STRUCT || param_type->mode == TM_UNION ? MIR_POINTER_TYPE : get_mir_type (ctx, param_type)); return get_reg_var_name (ctx, promote_mir_int_type (*type), name, 0); } static void collect_args_and_func_types (MIR_context_t ctx, struct func_type *func_type, MIR_type_t *ret_type) { c2m_ctx_t c2m_ctx = *c2m_ctx_loc (ctx); node_t declarator, id, first_param, p; struct type *param_type; MIR_var_t var; MIR_type_t type; first_param = NL_HEAD (func_type->param_list->ops); VARR_TRUNC (MIR_var_t, vars, 0); VARR_TRUNC (node_t, mem_params, 0); if (func_type->ret_type->mode == TM_STRUCT || func_type->ret_type->mode == TM_UNION) { var.name = RET_ADDR_NAME; var.type = MIR_POINTER_TYPE; VARR_PUSH (MIR_var_t, vars, var); } if (first_param != NULL && !void_param_p (first_param)) { for (p = first_param; p != NULL; p = NL_NEXT (p)) { if (p->code == N_TYPE) { var.name = "p"; param_type = ((struct decl_spec *) p->attr)->type; type = (param_type->mode == TM_STRUCT || param_type->mode == TM_UNION ? MIR_POINTER_TYPE : get_mir_type (ctx, param_type)); } else { declarator = NL_EL (p->ops, 1); assert (p->code == N_SPEC_DECL && declarator != NULL && declarator->code == N_DECL); id = NL_HEAD (declarator->ops); param_type = ((decl_t) p->attr)->decl_spec.type; var.name = get_param_name (ctx, &type, param_type, id->u.s.s); if (param_type->mode == TM_STRUCT || param_type->mode == TM_UNION || !((decl_t) p->attr)->reg_p) VARR_PUSH (node_t, mem_params, p); } var.type = type; VARR_PUSH (MIR_var_t, vars, var); } } set_type_layout (c2m_ctx, func_type->ret_type); *ret_type = get_mir_type (ctx, func_type->ret_type); } static mir_size_t get_object_path_offset (c2m_ctx_t c2m_ctx) { init_object_t init_object; size_t offset = 0; for (size_t i = 0; i < VARR_LENGTH (init_object_t, init_object_path); i++) { init_object = VARR_GET (init_object_t, init_object_path, i); if (init_object.container_type->mode == TM_ARR) { // ??? index < 0 offset += (init_object.u.curr_index * type_size (c2m_ctx, init_object.container_type->u.arr_type->el_type)); } else { assert (init_object.container_type->mode == TM_STRUCT || init_object.container_type->mode == TM_UNION); assert (init_object.u.curr_member->code == N_MEMBER); if (!anon_struct_union_type_member_p (init_object.u.curr_member)) /* Members inside anon struct/union already have adjusted offset */ offset += ((decl_t) init_object.u.curr_member->attr)->offset; } } return offset; } /* The function has the same structure as check_initializer. Keep it this way. */ static void collect_init_els (c2m_ctx_t c2m_ctx, decl_t member_decl, struct type **type_ptr, node_t initializer, int const_only_p, int top_p) { struct type *type = *type_ptr; struct expr *cexpr; node_t literal, des_list, curr_des, str, init, value, size_node; mir_llong MIR_UNUSED size_val = 0; /* to remove an uninitialized warning */ size_t mark; symbol_t sym; struct expr *sexpr; init_el_t init_el; int addr_p = FALSE; /* to remove an uninitialized warning */ int MIR_UNUSED found_p, MIR_UNUSED ok_p; init_object_t init_object; literal = get_compound_literal (initializer, &addr_p); if (literal != NULL && !addr_p && initializer->code != N_STR) initializer = NL_EL (literal->ops, 1); check_one_value: if (initializer->code != N_LIST && !(initializer->code == N_STR && type->mode == TM_ARR && char_type_p (type->u.arr_type->el_type))) { cexpr = initializer->attr; /* static or thread local object initialization should be const expr or addr: */ assert (initializer->code == N_STR || !const_only_p || cexpr->const_p || cexpr->const_addr_p || (literal != NULL && addr_p)); init_el.num = VARR_LENGTH (init_el_t, init_els); init_el.offset = get_object_path_offset (c2m_ctx); init_el.member_decl = member_decl; init_el.el_type = type; init_el.init = initializer; VARR_PUSH (init_el_t, init_els, init_el); return; } init = NL_HEAD (initializer->ops); if (((str = initializer)->code == N_STR /* string or string in parentheses */ || (init->code == N_INIT && NL_EL (initializer->ops, 1) == NULL && (des_list = NL_HEAD (init->ops))->code == N_LIST && NL_HEAD (des_list->ops) == NULL && NL_EL (init->ops, 1) != NULL && (str = NL_EL (init->ops, 1))->code == N_STR)) && type->mode == TM_ARR && char_type_p (type->u.arr_type->el_type)) { init_el.num = VARR_LENGTH (init_el_t, init_els); init_el.offset = get_object_path_offset (c2m_ctx); init_el.member_decl = NULL; init_el.el_type = type; init_el.init = str; VARR_PUSH (init_el_t, init_els, init_el); return; } assert (init->code == N_INIT); des_list = NL_HEAD (init->ops); assert (des_list->code == N_LIST); if (type->mode != TM_ARR && type->mode != TM_STRUCT && type->mode != TM_UNION) { assert (NL_NEXT (init) == NULL && NL_HEAD (des_list->ops) == NULL); initializer = NL_NEXT (des_list); assert (top_p); top_p = FALSE; goto check_one_value; } mark = VARR_LENGTH (init_object_t, init_object_path); init_object.container_type = type; init_object.designator_p = FALSE; if (type->mode == TM_ARR) { size_node = type->u.arr_type->size; sexpr = size_node->attr; /* we already figured out the array size during check: */ assert (size_node->code != N_IGNORE && sexpr->const_p && integer_type_p (sexpr->type)); size_val = sexpr->u.i_val; init_object.u.curr_index = -1; } else { init_object.u.curr_member = NULL; } VARR_PUSH (init_object_t, init_object_path, init_object); for (; init != NULL; init = NL_NEXT (init)) { assert (init->code == N_INIT); des_list = NL_HEAD (init->ops); value = NL_NEXT (des_list); assert ((value->code != N_LIST && value->code != N_COMPOUND_LITERAL) || type->mode == TM_ARR || type->mode == TM_STRUCT || type->mode == TM_UNION); if ((curr_des = NL_HEAD (des_list->ops)) == NULL) { ok_p = update_path_and_do (c2m_ctx, collect_init_els, mark, value, const_only_p, NULL, init->pos, ""); assert (ok_p); } else { for (; curr_des != NULL; curr_des = NL_NEXT (curr_des)) { VARR_TRUNC (init_object_t, init_object_path, mark + 1); init_object = VARR_POP (init_object_t, init_object_path); if (curr_des->code == N_FIELD_ID) { node_t id = NL_HEAD (curr_des->ops); /* field should be only in struct/union initializer */ assert (type->mode == TM_STRUCT || type->mode == TM_UNION); found_p = symbol_find (c2m_ctx, S_REGULAR, id, type->u.tag_type, &sym); assert (found_p); /* field should present */ process_init_field_designator (c2m_ctx, sym.def_node, init_object.container_type); ok_p = update_path_and_do (c2m_ctx, collect_init_els, mark, value, const_only_p, NULL, init->pos, ""); assert (ok_p); } else { cexpr = curr_des->attr; /* index should be in array initializer and const expr of right type and value: */ assert (type->mode == TM_ARR && cexpr->const_p && integer_type_p (cexpr->type) && !incomplete_type_p (c2m_ctx, type) && size_val >= 0 && size_val > cexpr->u.u_val); init_object.u.curr_index = cexpr->u.i_val - 1; init_object.designator_p = FALSE; VARR_PUSH (init_object_t, init_object_path, init_object); ok_p = update_path_and_do (c2m_ctx, collect_init_els, mark, value, const_only_p, NULL, init->pos, ""); assert (ok_p); } } } } VARR_TRUNC (init_object_t, init_object_path, mark); } static int cmp_init_el (const void *p1, const void *p2) { const init_el_t *el1 = p1, *el2 = p2; if (el1->offset < el2->offset) return -1; else if (el1->offset > el2->offset) return 1; else if (el1->member_decl != NULL && el2->member_decl != NULL && el1->member_decl->bit_offset < el2->member_decl->bit_offset) return -1; else if (el1->member_decl != NULL && el2->member_decl != NULL && el1->member_decl->bit_offset > el2->member_decl->bit_offset) return 1; else if (el1->num < el2->num) return 1; else if (el1->num > el2->num) return -1; else return 0; } static void move_item_to_module_start (MIR_module_t module, MIR_item_t item) { DLIST_REMOVE (MIR_item_t, module->items, item); DLIST_PREPEND (MIR_item_t, module->items, item); } static void move_item_forward (MIR_context_t ctx, MIR_item_t item) { c2m_ctx_t c2m_ctx = *c2m_ctx_loc (ctx); assert (curr_func != NULL); if (DLIST_TAIL (MIR_item_t, curr_func->module->items) != item) return; DLIST_REMOVE (MIR_item_t, curr_func->module->items, item); DLIST_INSERT_BEFORE (MIR_item_t, curr_func->module->items, curr_func, item); } static void gen_memset (MIR_context_t ctx, MIR_disp_t disp, MIR_reg_t base, mir_size_t len) { c2m_ctx_t c2m_ctx = *c2m_ctx_loc (ctx); MIR_type_t ret_type; MIR_var_t vars[3]; MIR_op_t treg_op, args[6]; MIR_module_t module; if (memset_item == NULL) { ret_type = get_int_mir_type (sizeof (mir_size_t)); vars[0].name = "s"; vars[0].type = get_int_mir_type (sizeof (mir_size_t)); vars[1].name = "c"; vars[1].type = get_int_mir_type (sizeof (mir_int)); vars[2].name = "n"; vars[2].type = get_int_mir_type (sizeof (mir_size_t)); module = curr_func->module; memset_proto = MIR_new_proto_arr (ctx, "memset_p", 1, &ret_type, 3, vars); memset_item = MIR_new_import (ctx, "memset"); move_item_to_module_start (module, memset_proto); move_item_to_module_start (module, memset_item); } args[0] = MIR_new_ref_op (ctx, memset_proto); args[1] = MIR_new_ref_op (ctx, memset_item); args[2] = get_new_temp (ctx, get_int_mir_type (sizeof (mir_size_t))).mir_op; if (disp == 0) { treg_op = MIR_new_reg_op (ctx, base); } else { treg_op = get_new_temp (ctx, get_int_mir_type (sizeof (mir_size_t))).mir_op; emit3 (ctx, MIR_ADD, treg_op, MIR_new_reg_op (ctx, base), MIR_new_int_op (ctx, disp)); } args[3] = treg_op; args[4] = MIR_new_int_op (ctx, 0); args[5] = MIR_new_uint_op (ctx, len); emit_insn (ctx, MIR_new_insn_arr (ctx, MIR_CALL, 6 /* args + proto + func + res */, args)); } static void gen_memcpy (MIR_context_t ctx, MIR_disp_t disp, MIR_reg_t base, op_t val, mir_size_t len) { c2m_ctx_t c2m_ctx = *c2m_ctx_loc (ctx); MIR_type_t ret_type; MIR_var_t vars[3]; MIR_op_t treg_op, args[6]; MIR_module_t module; if (val.mir_op.u.mem.index == 0 && val.mir_op.u.mem.disp == disp && val.mir_op.u.mem.base == base) return; if (memcpy_item == NULL) { ret_type = get_int_mir_type (sizeof (mir_size_t)); vars[0].name = "dest"; vars[0].type = get_int_mir_type (sizeof (mir_size_t)); vars[1].name = "src"; vars[1].type = get_int_mir_type (sizeof (mir_size_t)); vars[2].name = "n"; vars[2].type = get_int_mir_type (sizeof (mir_size_t)); module = curr_func->module; memcpy_proto = MIR_new_proto_arr (ctx, "memcpy_p", 1, &ret_type, 3, vars); memcpy_item = MIR_new_import (ctx, "memcpy"); move_item_to_module_start (module, memcpy_proto); move_item_to_module_start (module, memcpy_item); } args[0] = MIR_new_ref_op (ctx, memcpy_proto); args[1] = MIR_new_ref_op (ctx, memcpy_item); args[2] = get_new_temp (ctx, get_int_mir_type (sizeof (mir_size_t))).mir_op; if (disp == 0) { treg_op = MIR_new_reg_op (ctx, base); } else { treg_op = get_new_temp (ctx, get_int_mir_type (sizeof (mir_size_t))).mir_op; emit3 (ctx, MIR_ADD, treg_op, MIR_new_reg_op (ctx, base), MIR_new_int_op (ctx, disp)); } args[3] = treg_op; args[4] = mem_to_address (ctx, val).mir_op; args[5] = MIR_new_uint_op (ctx, len); emit_insn (ctx, MIR_new_insn_arr (ctx, MIR_CALL, 6 /* args + proto + func + res */, args)); } static void emit_scalar_assign (MIR_context_t ctx, op_t var, op_t *val, MIR_type_t t, int ignore_others_p) { if (var.decl == NULL || var.decl->bit_offset < 0) { emit2_noopt (ctx, tp_mov (t), var.mir_op, val->mir_op); } else { int width = var.decl->width; uint64_t mask, mask2; op_t temp_op1, temp_op2, temp_op3, temp_op4; c2m_ctx_t c2m_ctx = *c2m_ctx_loc (ctx); size_t size = type_size (c2m_ctx, var.decl->decl_spec.type) * MIR_CHAR_BIT; assert (var.mir_op.mode == MIR_OP_MEM); mask = 0xffffffffffffffff >> (64 - width); #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ mask2 = ~(mask << var.decl->bit_offset); #else mask2 = ~(mask << (size - var.decl->bit_offset - width)); #endif temp_op1 = get_new_temp (ctx, MIR_T_I64); temp_op2 = get_new_temp (ctx, MIR_T_I64); temp_op3 = get_new_temp (ctx, MIR_T_I64); if (!ignore_others_p) { emit2_noopt (ctx, MIR_MOV, temp_op2.mir_op, var.mir_op); emit3 (ctx, MIR_AND, temp_op2.mir_op, temp_op2.mir_op, MIR_new_uint_op (ctx, mask2)); } if (!signed_integer_type_p (var.decl->decl_spec.type)) { emit2 (ctx, MIR_MOV, temp_op1.mir_op, val->mir_op); *val = temp_op3; } else { emit3 (ctx, MIR_LSH, temp_op1.mir_op, val->mir_op, MIR_new_int_op (ctx, 64 - width)); emit3 (ctx, MIR_RSH, temp_op1.mir_op, temp_op1.mir_op, MIR_new_int_op (ctx, 64 - width)); *val = temp_op1; } emit3 (ctx, MIR_AND, temp_op3.mir_op, temp_op1.mir_op, MIR_new_uint_op (ctx, mask)); temp_op4 = get_new_temp (ctx, MIR_T_I64); #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ if (var.decl->bit_offset == 0) { temp_op4 = temp_op3; } else { emit3 (ctx, MIR_LSH, temp_op4.mir_op, temp_op3.mir_op, MIR_new_int_op (ctx, var.decl->bit_offset)); } #else if (size - var.decl->bit_offset - width == 0) { temp_op4 = temp_op3; } else { emit3 (ctx, MIR_LSH, temp_op4.mir_op, temp_op3.mir_op, MIR_new_int_op (ctx, size - var.decl->bit_offset - width)); } #endif if (!ignore_others_p) { emit3 (ctx, MIR_OR, temp_op4.mir_op, temp_op4.mir_op, temp_op2.mir_op); } emit2 (ctx, MIR_MOV, var.mir_op, temp_op4.mir_op); } } static void add_bit_field (MIR_context_t ctx, uint64_t *u, uint64_t v, decl_t member_decl) { uint64_t mask, mask2; int bit_offset = member_decl->bit_offset, width = member_decl->width; c2m_ctx_t c2m_ctx = *c2m_ctx_loc (ctx); size_t size = type_size (c2m_ctx, member_decl->decl_spec.type) * MIR_CHAR_BIT; mask = 0xffffffffffffffff >> (64 - width); #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ mask2 = ~(mask << bit_offset); #else mask2 = ~(mask << (size - bit_offset - width)); #endif *u &= mask2; if (signed_integer_type_p (member_decl->decl_spec.type)) { v <<= (64 - width); v = (int64_t) v >> (64 - width); } v &= mask; #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ v <<= bit_offset; #else v <<= size - bit_offset - width; #endif *u |= v; } static void gen_initializer (MIR_context_t ctx, size_t init_start, op_t var, const char *global_name, mir_size_t size, int local_p) { c2m_ctx_t c2m_ctx = *c2m_ctx_loc (ctx); op_t val; size_t str_len; mir_size_t data_size, offset = 0, rel_offset = 0; init_el_t init_el, next_init_el; MIR_reg_t base; MIR_type_t t; MIR_item_t data; MIR_module_t module; struct expr *e; if (var.mir_op.mode == MIR_OP_REG) { /* scalar initialization: */ assert (local_p && offset == 0 && VARR_LENGTH (init_el_t, init_els) - init_start == 1); init_el = VARR_GET (init_el_t, init_els, init_start); val = gen (ctx, init_el.init, NULL, NULL, TRUE, NULL); t = get_op_type (ctx, var); val = cast (ctx, val, get_mir_type (ctx, init_el.el_type), FALSE); emit_scalar_assign (ctx, var, &val, t, FALSE); } else if (local_p) { /* local variable initialization: */ assert (var.mir_op.mode == MIR_OP_MEM && var.mir_op.u.mem.index == 0); offset = var.mir_op.u.mem.disp; base = var.mir_op.u.mem.base; for (size_t i = init_start; i < VARR_LENGTH (init_el_t, init_els); i++) { init_el = VARR_GET (init_el_t, init_els, i); t = get_mir_type (ctx, init_el.el_type); if (rel_offset < init_el.offset) { /* fill the gap: */ gen_memset (ctx, offset + rel_offset, base, init_el.offset - rel_offset); rel_offset = init_el.offset; } if (t == MIR_T_UNDEF) val = new_op (NULL, MIR_new_mem_op (ctx, t, offset + rel_offset, base, 0, 1)); val = gen (ctx, init_el.init, NULL, NULL, t != MIR_T_UNDEF, t != MIR_T_UNDEF ? NULL : &val); if (!scalar_type_p (init_el.el_type)) { mir_size_t s = init_el.init->code == N_STR ? init_el.init->u.s.len : raw_type_size (c2m_ctx, init_el.el_type); gen_memcpy (ctx, offset + rel_offset, base, val, s); rel_offset = init_el.offset + s; } else { val = cast (ctx, val, get_mir_type (ctx, init_el.el_type), FALSE); emit_scalar_assign (ctx, new_op (init_el.member_decl, MIR_new_mem_op (ctx, t, offset + init_el.offset, base, 0, 1)), &val, t, i == init_start || rel_offset == init_el.offset); rel_offset = init_el.offset + _MIR_type_size (ctx, t); } } if (rel_offset < size) /* fill the tail: */ gen_memset (ctx, offset + rel_offset, base, size - rel_offset); } else { assert (var.mir_op.mode == MIR_OP_REF); for (size_t i = init_start; i < VARR_LENGTH (init_el_t, init_els); i++) { init_el = VARR_GET (init_el_t, init_els, i); if (i != init_start && init_el.offset == VARR_GET (init_el_t, init_els, i - 1).offset) continue; e = init_el.init->attr; if (!e->const_addr_p) { if (e->const_p) { convert_value (e, init_el.el_type); e->type = init_el.el_type; /* to get the right value in the subsequent gen call */ } val = gen (ctx, init_el.init, NULL, NULL, TRUE, NULL); assert (val.mir_op.mode == MIR_OP_INT || val.mir_op.mode == MIR_OP_UINT || val.mir_op.mode == MIR_OP_FLOAT || val.mir_op.mode == MIR_OP_DOUBLE || val.mir_op.mode == MIR_OP_LDOUBLE || val.mir_op.mode == MIR_OP_STR || val.mir_op.mode == MIR_OP_REF); } if (rel_offset < init_el.offset) { /* fill the gap: */ data = MIR_new_bss (ctx, global_name, init_el.offset - rel_offset); if (global_name != NULL) var.decl->item = data; global_name = NULL; } t = get_mir_type (ctx, init_el.el_type); if (e->const_addr_p) { node_t def; if ((def = e->def_node) == NULL) { /* constant address */ mir_size_t s = e->u.i_val; data = MIR_new_data (ctx, global_name, MIR_T_P, 1, &s); data_size = _MIR_type_size (ctx, MIR_T_P); } else { if (def->code != N_STR) { data = ((decl_t) def->attr)->item; } else { module = DLIST_TAIL (MIR_module_t, *MIR_get_module_list (ctx)); data = MIR_new_string_data (ctx, _MIR_get_temp_item_name (ctx, module), (MIR_str_t){def->u.s.len, def->u.s.s}); move_item_to_module_start (module, data); } data = MIR_new_ref_data (ctx, global_name, data, e->u.i_val); data_size = _MIR_type_size (ctx, t); } } else if (val.mir_op.mode == MIR_OP_REF) { data = MIR_new_ref_data (ctx, global_name, val.mir_op.u.ref, 0); data_size = _MIR_type_size (ctx, t); } else if (val.mir_op.mode != MIR_OP_STR) { union { int8_t i8; uint8_t u8; int16_t i16; uint16_t u16; int32_t i32; uint32_t u32; int64_t i64; uint64_t u64; float f; double d; long double ld; } u; if (init_el.member_decl != NULL && init_el.member_decl->bit_offset >= 0) { uint64_t u = 0; assert (val.mir_op.mode == MIR_OP_INT || val.mir_op.mode == MIR_OP_UINT); add_bit_field (ctx, &u, val.mir_op.u.u, init_el.member_decl); for (; i + 1 < VARR_LENGTH (init_el_t, init_els); i++, init_el = next_init_el) { next_init_el = VARR_GET (init_el_t, init_els, i + 1); if (next_init_el.offset != init_el.offset) break; if (next_init_el.member_decl->bit_offset == init_el.member_decl->bit_offset) continue; val = gen (ctx, next_init_el.init, NULL, NULL, TRUE, NULL); assert (val.mir_op.mode == MIR_OP_INT || val.mir_op.mode == MIR_OP_UINT); add_bit_field (ctx, &u, val.mir_op.u.u, next_init_el.member_decl); } val.mir_op.u.u = u; } switch (t) { case MIR_T_I8: u.i8 = val.mir_op.u.i; break; case MIR_T_U8: u.u8 = val.mir_op.u.u; break; case MIR_T_I16: u.i16 = val.mir_op.u.i; break; case MIR_T_U16: u.u16 = val.mir_op.u.u; break; case MIR_T_I32: u.i32 = val.mir_op.u.i; break; case MIR_T_U32: u.u32 = val.mir_op.u.u; break; case MIR_T_I64: u.i64 = val.mir_op.u.i; break; case MIR_T_U64: u.u64 = val.mir_op.u.u; break; case MIR_T_F: u.f = val.mir_op.u.f; break; case MIR_T_D: u.d = val.mir_op.u.d; break; case MIR_T_LD: u.ld = val.mir_op.u.ld; break; default: assert (FALSE); } data = MIR_new_data (ctx, global_name, t, 1, &u); data_size = _MIR_type_size (ctx, t); } else if (init_el.el_type->mode == TM_ARR) { data_size = raw_type_size (c2m_ctx, init_el.el_type); str_len = val.mir_op.u.str.len; if (data_size < str_len) { data = MIR_new_data (ctx, global_name, MIR_T_U8, data_size, val.mir_op.u.str.s); } else { data = MIR_new_string_data (ctx, global_name, val.mir_op.u.str); if (data_size > str_len) MIR_new_bss (ctx, NULL, data_size - str_len); } } else { module = DLIST_TAIL (MIR_module_t, *MIR_get_module_list (ctx)); data = MIR_new_string_data (ctx, _MIR_get_temp_item_name (ctx, module), val.mir_op.u.str); move_item_to_module_start (module, data); data = MIR_new_ref_data (ctx, global_name, data, 0); data_size = _MIR_type_size (ctx, t); } if (global_name != NULL) var.decl->item = data; global_name = NULL; rel_offset = init_el.offset + data_size; } if (rel_offset < size) { /* fill the tail: */ data = MIR_new_bss (ctx, global_name, size - rel_offset); if (global_name != NULL) var.decl->item = data; } } } static MIR_item_t get_ref_item (MIR_context_t ctx, node_t def, const char *name) { c2m_ctx_t c2m_ctx = *c2m_ctx_loc (ctx); struct decl *decl = def->attr; if (def->code == N_FUNC_DEF || (def->code == N_SPEC_DECL && NL_EL (def->ops, 1)->code == N_DECL && decl->scope == top_scope && decl->decl_spec.type->mode != TM_FUNC && !decl->decl_spec.typedef_p && !decl->decl_spec.extern_p)) return (decl->decl_spec.linkage == N_EXTERN ? MIR_new_export (ctx, name) : MIR_new_forward (ctx, name)); return NULL; } static void emit_bin_op (MIR_context_t ctx, node_t r, struct type *type, op_t res, op_t op1, op_t op2) { c2m_ctx_t c2m_ctx = *c2m_ctx_loc (ctx); op_t temp; if (type->mode == TM_PTR) { /* ptr +/- int */ assert (r->code == N_ADD || r->code == N_SUB || r->code == N_ADD_ASSIGN || r->code == N_SUB_ASSIGN); if (((struct expr *) NL_HEAD (r->ops)->attr)->type->mode != TM_PTR) /* int + ptr */ SWAP (op1, op2, temp); if (op2.mir_op.mode == MIR_OP_INT || op2.mir_op.mode == MIR_OP_UINT) { op2 = new_op (NULL, MIR_new_int_op (ctx, op2.mir_op.u.i * type_size (c2m_ctx, type->u.ptr_type))); } else { temp = get_new_temp (ctx, get_mir_type (ctx, type)); emit3 (ctx, sizeof (mir_size_t) == 8 ? MIR_MUL : MIR_MULS, temp.mir_op, op2.mir_op, MIR_new_int_op (ctx, type_size (c2m_ctx, type->u.ptr_type))); op2 = temp; } } emit3 (ctx, get_mir_type_insn_code (ctx, type, r), res.mir_op, op1.mir_op, op2.mir_op); if (type->mode != TM_PTR && (type = ((struct expr *) NL_HEAD (r->ops)->attr)->type)->mode == TM_PTR) { /* ptr - ptr */ assert (r->code == N_SUB || r->code == N_SUB_ASSIGN); emit3 (ctx, sizeof (mir_size_t) == 8 ? MIR_DIV : MIR_DIVS, res.mir_op, res.mir_op, MIR_new_int_op (ctx, type_size (c2m_ctx, type->u.ptr_type))); } } static int signed_case_compare (const void *v1, const void *v2) { case_t c1 = *(const case_t *) v1, c2 = *(const case_t *) v2; struct expr *e1 = NL_HEAD (c1->case_node->ops)->attr; struct expr *e2 = NL_HEAD (c2->case_node->ops)->attr; assert (e1->u.i_val != e2->u.i_val); return e1->u.i_val < e2->u.i_val ? -1 : 1; } static int unsigned_case_compare (const void *v1, const void *v2) { case_t c1 = *(const case_t *) v1, c2 = *(const case_t *) v2; struct expr *e1 = NL_HEAD (c1->case_node->ops)->attr; struct expr *e2 = NL_HEAD (c2->case_node->ops)->attr; assert (e1->u.u_val != e2->u.u_val); return e1->u.u_val < e2->u.u_val ? -1 : 1; } static op_t gen (MIR_context_t ctx, node_t r, MIR_label_t true_label, MIR_label_t false_label, int val_p, op_t *desirable_dest) { c2m_ctx_t c2m_ctx = *c2m_ctx_loc (ctx); op_t res, op1, op2, var, val; MIR_type_t t = MIR_T_UNDEF; /* to remove an uninitialized warning */ MIR_insn_code_t insn_code; MIR_type_t mir_type; struct expr *e = NULL; /* to remove an uninitialized warning */ struct type *type; decl_t decl; long double ld; long long ll; unsigned long long ull; int expr_attr_p, stmt_p; classify_node (r, &expr_attr_p, &stmt_p); assert ((true_label == NULL && false_label == NULL) || (true_label != NULL && false_label != NULL)); assert (!val_p || desirable_dest == NULL); if (r->code != N_ANDAND && r->code != N_OROR && expr_attr_p && push_const_val (ctx, r, &res)) goto finish; switch (r->code) { case N_LIST: for (node_t n = NL_HEAD (r->ops); n != NULL; n = NL_NEXT (n)) gen (ctx, n, true_label, false_label, val_p, NULL); break; case N_IGNORE: break; /* do nothing */ case N_I: case N_L: ll = r->u.l; goto int_val; case N_LL: ll = r->u.ll; int_val: res = new_op (NULL, MIR_new_int_op (ctx, ll)); break; case N_U: case N_UL: ull = r->u.ul; goto uint_val; case N_ULL: ull = r->u.ull; uint_val: res = new_op (NULL, MIR_new_uint_op (ctx, ull)); break; case N_F: ld = r->u.f; goto float_val; case N_D: ld = r->u.d; goto float_val; case N_LD: ld = r->u.ld; float_val: mir_type = get_mir_type (ctx, ((struct expr *) r->attr)->type); res = new_op (NULL, (mir_type == MIR_T_F ? MIR_new_float_op (ctx, ld) : mir_type == MIR_T_D ? MIR_new_double_op (ctx, ld) : MIR_new_ldouble_op (ctx, ld))); break; case N_CH: ll = r->u.ch; goto int_val; case N_STR: res = new_op (NULL, MIR_new_str_op (ctx, (MIR_str_t){r->u.s.len, r->u.s.s})); //???what to do with decl // and str in initializer break; case N_COMMA: gen (ctx, NL_HEAD (r->ops), NULL, NULL, FALSE, NULL); res = gen (ctx, NL_EL (r->ops, 1), true_label, false_label, TRUE, NULL); break; case N_ANDAND: case N_OROR: if (!push_const_val (ctx, r, &res)) { MIR_label_t temp_label = MIR_new_label (ctx), t_label = true_label, f_label = false_label; int make_val_p = t_label == NULL; if (make_val_p) { t_label = MIR_new_label (ctx); f_label = MIR_new_label (ctx); } assert (t_label != NULL && f_label != NULL); gen (ctx, NL_HEAD (r->ops), r->code == N_ANDAND ? temp_label : t_label, r->code == N_ANDAND ? f_label : temp_label, FALSE, NULL); emit_label_insn_opt (ctx, temp_label); gen (ctx, NL_EL (r->ops, 1), t_label, f_label, FALSE, NULL); if (make_val_p) { MIR_label_t end_label = MIR_new_label (ctx); type = ((struct expr *) r->attr)->type; res = get_new_temp (ctx, get_mir_type (ctx, type)); emit_label_insn_opt (ctx, t_label); emit2 (ctx, MIR_MOV, res.mir_op, one_op.mir_op); emit1 (ctx, MIR_JMP, MIR_new_label_op (ctx, end_label)); emit_label_insn_opt (ctx, f_label); emit2 (ctx, MIR_MOV, res.mir_op, zero_op.mir_op); emit_label_insn_opt (ctx, end_label); } true_label = false_label = NULL; } else if (true_label != NULL) { int true_p; assert (res.mir_op.mode == MIR_OP_INT || res.mir_op.mode == MIR_OP_UINT || res.mir_op.mode == MIR_OP_FLOAT || res.mir_op.mode == MIR_OP_DOUBLE); true_p = ((res.mir_op.mode == MIR_OP_INT && res.mir_op.u.i != 0) || (res.mir_op.mode == MIR_OP_UINT && res.mir_op.u.u != 0) || (res.mir_op.mode == MIR_OP_FLOAT && res.mir_op.u.f != 0.0f) || (res.mir_op.mode == MIR_OP_DOUBLE && res.mir_op.u.d != 0.0)); emit1 (ctx, MIR_JMP, MIR_new_label_op (ctx, true_p ? true_label : false_label)); true_label = false_label = NULL; } break; case N_BITWISE_NOT: gen_unary_op (ctx, r, &op1, &res); t = get_mir_type (ctx, ((struct expr *) r->attr)->type); emit3 (ctx, t == MIR_T_I64 || t == MIR_T_U64 ? MIR_XOR : MIR_XORS, res.mir_op, op1.mir_op, minus_one_op.mir_op); break; case N_NOT: if (true_label != NULL) { gen (ctx, NL_HEAD (r->ops), false_label, true_label, FALSE, NULL); true_label = false_label = NULL; } else { MIR_label_t end_label = MIR_new_label (ctx); MIR_label_t t_label = MIR_new_label (ctx), f_label = MIR_new_label (ctx); res = get_new_temp (ctx, MIR_T_I64); gen (ctx, NL_HEAD (r->ops), t_label, f_label, FALSE, NULL); emit_label_insn_opt (ctx, t_label); emit2 (ctx, MIR_MOV, res.mir_op, zero_op.mir_op); emit1 (ctx, MIR_JMP, MIR_new_label_op (ctx, end_label)); emit_label_insn_opt (ctx, f_label); emit2 (ctx, MIR_MOV, res.mir_op, one_op.mir_op); emit_label_insn_opt (ctx, end_label); } break; case N_ADD: case N_SUB: if (NL_NEXT (NL_HEAD (r->ops)) == NULL) { /* unary */ MIR_insn_code_t ic = get_mir_insn_code (ctx, r); gen_unary_op (ctx, r, &op1, &res); if (r->code == N_ADD) { ic = (ic == MIR_FADD ? MIR_FMOV : ic == MIR_DADD ? MIR_DMOV : ic == MIR_LDADD ? MIR_LDMOV : MIR_MOV); emit2 (ctx, ic, res.mir_op, op1.mir_op); } else { ic = (ic == MIR_FSUB ? MIR_FNEG : ic == MIR_DSUB ? MIR_DNEG : ic == MIR_LDSUB ? MIR_LDNEG : ic == MIR_SUB ? MIR_NEG : MIR_NEGS); emit2 (ctx, ic, res.mir_op, op1.mir_op); } break; } /* Fall through: */ case N_AND: case N_OR: case N_XOR: case N_LSH: case N_RSH: case N_MUL: case N_DIV: case N_MOD: gen_bin_op (ctx, r, &op1, &op2, &res); emit_bin_op (ctx, r, ((struct expr *) r->attr)->type, res, op1, op2); break; case N_EQ: case N_NE: case N_LT: case N_LE: case N_GT: case N_GE: { struct type *type1 = ((struct expr *) NL_HEAD (r->ops)->attr)->type; struct type *type2 = ((struct expr *) NL_EL (r->ops, 1)->attr)->type; struct type type, ptr_type = get_ptr_int_type (FALSE); type = arithmetic_conversion (type1->mode == TM_PTR ? &ptr_type : type1, type2->mode == TM_PTR ? &ptr_type : type2); set_type_layout (c2m_ctx, &type); gen_cmp_op (ctx, r, &type, &op1, &op2, &res); insn_code = get_mir_type_insn_code (ctx, &type, r); if (true_label == NULL) { emit3 (ctx, insn_code, res.mir_op, op1.mir_op, op2.mir_op); } else { insn_code = get_compare_branch_code (insn_code); emit3 (ctx, insn_code, MIR_new_label_op (ctx, true_label), op1.mir_op, op2.mir_op); emit1 (ctx, MIR_JMP, MIR_new_label_op (ctx, false_label)); true_label = false_label = NULL; } break; } case N_POST_INC: case N_POST_DEC: { struct type *type = ((struct expr *) r->attr)->type2; t = get_mir_type (ctx, type); var = gen (ctx, NL_HEAD (r->ops), NULL, NULL, FALSE, NULL); op1 = force_val (ctx, var, FALSE); if (val_p || true_label != NULL) { res = get_new_temp (ctx, t); emit2 (ctx, tp_mov (t), res.mir_op, op1.mir_op); } val = promote (ctx, op1, t, TRUE); op2 = promote (ctx, type->mode != TM_PTR ? one_op : new_op (NULL, MIR_new_int_op (ctx, type_size (c2m_ctx, type->u.ptr_type))), t, FALSE); emit3 (ctx, get_mir_insn_code (ctx, r), val.mir_op, val.mir_op, op2.mir_op); t = promote_mir_int_type (t); goto assign; } case N_INC: case N_DEC: { struct type *type = ((struct expr *) r->attr)->type2; t = get_mir_type (ctx, type); var = gen (ctx, NL_HEAD (r->ops), NULL, NULL, FALSE, NULL); val = promote (ctx, force_val (ctx, var, FALSE), t, TRUE); op2 = promote (ctx, type->mode != TM_PTR ? one_op : new_op (NULL, MIR_new_int_op (ctx, type_size (c2m_ctx, type->u.ptr_type))), t, FALSE); t = promote_mir_int_type (t); res = get_new_temp (ctx, t); emit3 (ctx, get_mir_insn_code (ctx, r), val.mir_op, val.mir_op, op2.mir_op); goto assign; } case N_AND_ASSIGN: case N_OR_ASSIGN: case N_XOR_ASSIGN: case N_LSH_ASSIGN: case N_RSH_ASSIGN: case N_ADD_ASSIGN: case N_SUB_ASSIGN: case N_MUL_ASSIGN: case N_DIV_ASSIGN: case N_MOD_ASSIGN: gen_assign_bin_op (ctx, r, ((struct expr *) r->attr)->type2, &val, &op2, &var); emit_bin_op (ctx, r, ((struct expr *) r->attr)->type2, val, val, op2); t = get_op_type (ctx, var); t = promote_mir_int_type (t); res = get_new_temp (ctx, t); goto assign; break; case N_ASSIGN: var = gen (ctx, NL_HEAD (r->ops), NULL, NULL, FALSE, NULL); t = get_op_type (ctx, var); op2 = gen (ctx, NL_EL (r->ops, 1), NULL, NULL, t != MIR_T_UNDEF, t != MIR_T_UNDEF ? NULL : &var); if ((!val_p && true_label == NULL) || t == MIR_T_UNDEF) { res = var; val = op2; } else { t = promote_mir_int_type (t); val = promote (ctx, op2, t, TRUE); res = get_new_temp (ctx, t); } assign: /* t/val is promoted type/new value of assign expression */ if (scalar_type_p (((struct expr *) r->attr)->type)) { assert (t != MIR_T_UNDEF); val = cast (ctx, val, get_mir_type (ctx, ((struct expr *) r->attr)->type), FALSE); emit_scalar_assign (ctx, var, &val, t, FALSE); if ((val_p || true_label != NULL) && r->code != N_POST_INC && r->code != N_POST_DEC) emit2_noopt (ctx, tp_mov (t), res.mir_op, val.mir_op); } else { /* block move */ mir_size_t size = type_size (c2m_ctx, ((struct expr *) r->attr)->type); assert (r->code == N_ASSIGN); block_move (ctx, var, val, size); } break; case N_ID: { e = r->attr; assert (!e->const_p); if (e->lvalue_node == NULL) { res = new_op (NULL, MIR_new_ref_op (ctx, ((decl_t) e->def_node->attr)->item)); } else if ((decl = e->lvalue_node->attr)->scope == top_scope || decl->decl_spec.static_p || decl->decl_spec.linkage != N_IGNORE) { t = get_mir_type (ctx, e->type); res = get_new_temp (ctx, MIR_T_I64); emit2 (ctx, MIR_MOV, res.mir_op, MIR_new_ref_op (ctx, decl->item)); res = new_op (decl, MIR_new_mem_op (ctx, t, 0, res.mir_op.u.reg, 0, 1)); } else if (!decl->reg_p) { t = get_mir_type (ctx, e->type); res = new_op (decl, MIR_new_mem_op (ctx, t, decl->offset, MIR_reg (ctx, FP_NAME, curr_func->u.func), 0, 1)); } else { const char *name; reg_var_t reg_var; t = get_mir_type (ctx, e->type); assert (t != MIR_T_UNDEF); t = promote_mir_int_type (t); name = get_reg_var_name (ctx, t, r->u.s.s, ((struct node_scope *) decl->scope->attr)->func_scope_num); reg_var = get_reg_var (ctx, t, name); res = new_op (decl, MIR_new_reg_op (ctx, reg_var.reg)); } break; } case N_IND: { MIR_type_t ind_t; node_t arr = NL_HEAD (r->ops); mir_size_t size = type_size (c2m_ctx, ((struct expr *) r->attr)->type); t = get_mir_type (ctx, ((struct expr *) r->attr)->type); op1 = gen (ctx, arr, NULL, NULL, TRUE, NULL); op2 = gen (ctx, NL_EL (r->ops, 1), NULL, NULL, TRUE, NULL); ind_t = get_mir_type (ctx, ((struct expr *) NL_EL (r->ops, 1)->attr)->type); op2 = force_reg (ctx, op2, ind_t); if (((struct expr *) arr->attr)->type->arr_type != NULL) { /* it was an array */ size = type_size (c2m_ctx, ((struct expr *) arr->attr)->type->arr_type->u.arr_type->el_type); op1 = force_reg_or_mem (ctx, op1, MIR_T_I64); assert (op1.mir_op.mode == MIR_OP_REG || op1.mir_op.mode == MIR_OP_MEM); } else { op1 = force_reg (ctx, op1, MIR_T_I64); assert (op1.mir_op.mode == MIR_OP_REG); } res = op1; res.decl = NULL; if (res.mir_op.mode == MIR_OP_REG) res.mir_op = MIR_new_mem_op (ctx, t, 0, res.mir_op.u.reg, 0, 1); if (res.mir_op.u.mem.base == 0 && size == 1) { res.mir_op.u.mem.base = op2.mir_op.u.reg; } else if (res.mir_op.u.mem.index == 0 && size <= MIR_MAX_SCALE) { res.mir_op.u.mem.index = op2.mir_op.u.reg; res.mir_op.u.mem.scale = size; } else { op_t temp_op; temp_op = get_new_temp (ctx, MIR_T_I64); emit3 (ctx, MIR_MUL, temp_op.mir_op, op2.mir_op, MIR_new_int_op (ctx, size)); if (res.mir_op.u.mem.base != 0) emit3 (ctx, MIR_ADD, temp_op.mir_op, temp_op.mir_op, MIR_new_reg_op (ctx, res.mir_op.u.mem.base)); res.mir_op.u.mem.base = temp_op.mir_op.u.reg; } res.mir_op.u.mem.type = t; break; } case N_ADDR: { int add_p = FALSE; op1 = gen (ctx, NL_HEAD (r->ops), NULL, NULL, FALSE, NULL); if (op1.mir_op.mode == MIR_OP_REG || op1.mir_op.mode == MIR_OP_REF || op1.mir_op.mode == MIR_OP_STR) { /* array or func */ res = op1; res.decl = NULL; break; } assert (op1.mir_op.mode == MIR_OP_MEM); t = get_mir_type (ctx, ((struct expr *) r->attr)->type); res = get_new_temp (ctx, t); if (op1.mir_op.u.mem.index != 0) { emit3 (ctx, MIR_MUL, res.mir_op, MIR_new_reg_op (ctx, op1.mir_op.u.mem.index), MIR_new_int_op (ctx, op1.mir_op.u.mem.scale)); add_p = TRUE; } if (op1.mir_op.u.mem.disp != 0) { if (add_p) emit3 (ctx, MIR_ADD, res.mir_op, res.mir_op, MIR_new_int_op (ctx, op1.mir_op.u.mem.disp)); else emit2 (ctx, MIR_MOV, res.mir_op, MIR_new_int_op (ctx, op1.mir_op.u.mem.disp)); add_p = TRUE; } if (op1.mir_op.u.mem.base != 0) { if (add_p) emit3 (ctx, MIR_ADD, res.mir_op, res.mir_op, MIR_new_reg_op (ctx, op1.mir_op.u.mem.base)); else emit2 (ctx, MIR_MOV, res.mir_op, MIR_new_reg_op (ctx, op1.mir_op.u.mem.base)); } break; } case N_DEREF: op1 = gen (ctx, NL_HEAD (r->ops), NULL, NULL, TRUE, NULL); op1 = force_reg (ctx, op1, MIR_T_I64); assert (op1.mir_op.mode == MIR_OP_REG); if ((type = ((struct expr *) r->attr)->type)->mode == TM_PTR && type->u.ptr_type->mode == TM_FUNC) { res = op1; } else { t = get_mir_type (ctx, type); op1.mir_op = MIR_new_mem_op (ctx, t, 0, op1.mir_op.u.reg, 0, 1); res = new_op (NULL, op1.mir_op); } break; case N_FIELD: case N_DEREF_FIELD: { node_t def_node; e = r->attr; def_node = e->lvalue_node; assert (def_node != NULL && def_node->code == N_MEMBER); decl = def_node->attr; op1 = gen (ctx, NL_HEAD (r->ops), NULL, NULL, r->code == N_DEREF_FIELD, NULL); t = get_mir_type (ctx, decl->decl_spec.type); if (r->code == N_FIELD) { assert (op1.mir_op.mode == MIR_OP_MEM); op1.mir_op = MIR_new_mem_op (ctx, t, op1.mir_op.u.mem.disp + decl->offset, op1.mir_op.u.mem.base, op1.mir_op.u.mem.index, op1.mir_op.u.mem.scale); } else { op1 = force_reg (ctx, op1, MIR_T_I64); assert (op1.mir_op.mode == MIR_OP_REG); op1.mir_op = MIR_new_mem_op (ctx, t, decl->offset, op1.mir_op.u.reg, 0, 1); } res = new_op (decl, op1.mir_op); break; } case N_COND: { node_t cond = NL_HEAD (r->ops); node_t true_expr = NL_NEXT (cond); node_t false_expr = NL_NEXT (true_expr); MIR_label_t true_label = MIR_new_label (ctx), false_label = MIR_new_label (ctx); MIR_label_t end_label = MIR_new_label (ctx); struct type *type = ((struct expr *) r->attr)->type; op_t addr; int void_p = void_type_p (type); mir_size_t size = type_size (c2m_ctx, ((struct expr *) r->attr)->type); if (!void_p) t = get_mir_type (ctx, type); gen (ctx, cond, true_label, false_label, FALSE, NULL); emit_label_insn_opt (ctx, true_label); op1 = gen (ctx, true_expr, NULL, NULL, !void_p && t != MIR_T_UNDEF, NULL); if (!void_p) { if (t != MIR_T_UNDEF) { res = get_new_temp (ctx, t); emit2 (ctx, tp_mov (t), res.mir_op, op1.mir_op); } else if (desirable_dest == NULL) { res = get_new_temp (ctx, MIR_T_I64); addr = mem_to_address (ctx, op1); emit2 (ctx, MIR_MOV, res.mir_op, addr.mir_op); } else { block_move (ctx, *desirable_dest, op1, size); res = *desirable_dest; } } emit1 (ctx, MIR_JMP, MIR_new_label_op (ctx, end_label)); emit_label_insn_opt (ctx, false_label); op1 = gen (ctx, false_expr, NULL, NULL, !void_p && t != MIR_T_UNDEF, NULL); if (!void_p) { if (t != MIR_T_UNDEF) { emit2 (ctx, tp_mov (t), res.mir_op, op1.mir_op); } else if (desirable_dest == NULL) { addr = mem_to_address (ctx, op1); emit2 (ctx, MIR_MOV, res.mir_op, addr.mir_op); res = new_op (NULL, MIR_new_mem_op (ctx, MIR_T_I8, 0, res.mir_op.u.reg, 0, 1)); } else { block_move (ctx, res, op1, size); } } emit_label_insn_opt (ctx, end_label); break; } case N_ALIGNOF: case N_SIZEOF: case N_EXPR_SIZEOF: assert (FALSE); break; case N_CAST: assert (!((struct expr *) r->attr)->const_p); type = ((struct expr *) r->attr)->type; op1 = gen (ctx, NL_EL (r->ops, 1), NULL, NULL, !void_type_p (type), NULL); if (void_type_p (type)) { res = op1; res.decl = NULL; res.mir_op.mode = MIR_OP_UNDEF; } else { t = get_mir_type (ctx, type); res = cast (ctx, op1, t, TRUE); } break; case N_COMPOUND_LITERAL: { const char *global_name = NULL; node_t type_name = NL_HEAD (r->ops); decl_t decl = type_name->attr; struct expr *expr = r->attr; MIR_module_t module = DLIST_TAIL (MIR_module_t, *MIR_get_module_list (ctx)); size_t init_start; if (decl->scope == top_scope) { assert (decl->item == NULL); global_name = _MIR_get_temp_item_name (ctx, module); } init_start = VARR_LENGTH (init_el_t, init_els); collect_init_els (c2m_ctx, NULL, &decl->decl_spec.type, NL_EL (r->ops, 1), decl->scope == top_scope || decl->decl_spec.linkage == N_STATIC || decl->decl_spec.linkage == N_EXTERN || decl->decl_spec.static_p || decl->decl_spec.thread_local_p, TRUE); if (decl->scope == top_scope) qsort (VARR_ADDR (init_el_t, init_els) + init_start, VARR_LENGTH (init_el_t, init_els) - init_start, sizeof (init_el_t), cmp_init_el); if (decl->scope == top_scope || decl->decl_spec.static_p) { var = new_op (decl, MIR_new_ref_op (ctx, NULL)); } else { t = get_mir_type (ctx, expr->type); var = new_op (decl, MIR_new_mem_op (ctx, t, decl->offset, MIR_reg (ctx, FP_NAME, curr_func->u.func), 0, 1)); } gen_initializer (ctx, init_start, var, global_name, raw_type_size (c2m_ctx, decl->decl_spec.type), decl->scope != top_scope && !decl->decl_spec.static_p); VARR_TRUNC (init_el_t, init_els, init_start); if (var.mir_op.mode == MIR_OP_REF) var.mir_op.u.ref = var.decl->item; res = var; break; } case N_CALL: { node_t func = NL_HEAD (r->ops), param_list, param, args = NL_EL (r->ops, 1); struct decl_spec *decl_spec; size_t ops_start; struct expr *call_expr = r->attr, *func_expr; struct type *func_type = NULL; /* to remove an uninitialized warning */ struct type *type = call_expr->type; MIR_item_t proto_item; mir_size_t saved_call_arg_area_offset_before_args; int va_arg_p = call_expr->builtin_call_p && strcmp (func->u.s.s, BUILTIN_VA_ARG) == 0; int va_start_p = call_expr->builtin_call_p && strcmp (func->u.s.s, BUILTIN_VA_START) == 0; int alloca_p = call_expr->builtin_call_p && strcmp (func->u.s.s, ALLOCA) == 0; int builtin_call_p = alloca_p || va_arg_p || va_start_p, inline_p = FALSE; int struct_p; ops_start = VARR_LENGTH (MIR_op_t, call_ops); if (!builtin_call_p) { func_expr = func->attr; func_type = func_expr->type; assert (func_type->mode == TM_PTR && func_type->u.ptr_type->mode == TM_FUNC); func_type = func_type->u.ptr_type; proto_item = func_type->u.func_type->proto_item; // ??? VARR_PUSH (MIR_op_t, call_ops, MIR_new_ref_op (ctx, proto_item)); op1 = gen (ctx, func, NULL, NULL, TRUE, NULL); if (op1.mir_op.mode == MIR_OP_REF && func->code == N_ID && ((decl_t) func_expr->def_node->attr)->decl_spec.inline_p) inline_p = TRUE; VARR_PUSH (MIR_op_t, call_ops, op1.mir_op); } if (scalar_type_p (type)) { t = get_mir_type (ctx, type); t = promote_mir_int_type (t); res = get_new_temp (ctx, t); VARR_PUSH (MIR_op_t, call_ops, res.mir_op); } else if (type->mode == TM_STRUCT || type->mode == TM_UNION) { node_t block = NL_EL (curr_func_def->ops, 3); struct node_scope *ns = block->attr; res = get_new_temp (ctx, MIR_T_I64); emit3 (ctx, MIR_ADD, res.mir_op, MIR_new_reg_op (ctx, MIR_reg (ctx, FP_NAME, curr_func->u.func)), MIR_new_int_op (ctx, curr_call_arg_area_offset + ns->size - ns->call_arg_area_size)); if (!builtin_call_p) update_call_arg_area_offset (c2m_ctx, type, FALSE); VARR_PUSH (MIR_op_t, call_ops, res.mir_op); res.mir_op = MIR_new_mem_op (ctx, MIR_T_UNDEF, 0, res.mir_op.u.reg, 0, 1); t = MIR_T_I64; } saved_call_arg_area_offset_before_args = curr_call_arg_area_offset; if (va_arg_p) { op1 = get_new_temp (ctx, MIR_T_I64); op2 = gen (ctx, NL_HEAD (args->ops), NULL, NULL, TRUE, NULL); MIR_append_insn (ctx, curr_func, MIR_new_insn (ctx, MIR_VA_ARG, op1.mir_op, op2.mir_op, MIR_new_mem_op (ctx, t, 0, 0, 0, 1))); op2 = get_new_temp (ctx, t); MIR_append_insn (ctx, curr_func, MIR_new_insn (ctx, tp_mov (t), op2.mir_op, MIR_new_mem_op (ctx, t, 0, op1.mir_op.u.reg, 0, 1))); if (res.mir_op.mode == MIR_OP_REG) { res = op2; } else { assert (res.mir_op.mode == MIR_OP_MEM); res.mir_op.u.mem.base = op2.mir_op.u.reg; } } else if (va_start_p) { op1 = gen (ctx, NL_HEAD (args->ops), NULL, NULL, TRUE, NULL); MIR_append_insn (ctx, curr_func, MIR_new_insn (ctx, MIR_VA_START, op1.mir_op)); } else if (alloca_p) { res = get_new_temp (ctx, t); op1 = gen (ctx, NL_HEAD (args->ops), NULL, NULL, TRUE, NULL); MIR_append_insn (ctx, curr_func, MIR_new_insn (ctx, MIR_ALLOCA, res.mir_op, op1.mir_op)); } else { param_list = func_type->u.func_type->param_list; param = NL_HEAD (param_list->ops); for (node_t arg = NL_HEAD (args->ops); arg != NULL; arg = NL_NEXT (arg)) { e = arg->attr; struct_p = e->type->mode == TM_STRUCT || e->type->mode == TM_UNION; op2 = gen (ctx, arg, NULL, NULL, !struct_p, NULL); assert (param != NULL || NL_HEAD (param_list->ops) == NULL || func_type->u.func_type->dots_p); if (struct_p) { /* pass an adress of struct/union: */ assert (op2.mir_op.mode == MIR_OP_MEM); op2 = mem_to_address (ctx, op2); } else if (param != NULL) { assert (param->code == N_SPEC_DECL || param->code == N_TYPE); decl_spec = get_param_decl_spec (param); t = get_mir_type (ctx, decl_spec->type); t = promote_mir_int_type (t); op2 = promote (ctx, op2, t, FALSE); } else { t = get_mir_type (ctx, e->type); t = promote_mir_int_type (t); op2 = promote (ctx, op2, t == MIR_T_F ? MIR_T_D : t, FALSE); } VARR_PUSH (MIR_op_t, call_ops, op2.mir_op); if (param != NULL) param = NL_NEXT (param); } MIR_append_insn (ctx, curr_func, MIR_new_insn_arr (ctx, (inline_p ? MIR_INLINE : MIR_CALL), VARR_LENGTH (MIR_op_t, call_ops) - ops_start, VARR_ADDR (MIR_op_t, call_ops) + ops_start)); } curr_call_arg_area_offset = saved_call_arg_area_offset_before_args; VARR_TRUNC (MIR_op_t, call_ops, ops_start); break; } case N_GENERIC: { node_t list = NL_EL (r->ops, 1); node_t ga_case = NL_HEAD (list->ops); /* first element is now a compatible generic association case */ op1 = gen (ctx, NL_EL (ga_case->ops, 1), NULL, NULL, TRUE, NULL); t = get_mir_type (ctx, ((struct expr *) r->attr)->type); res = promote (ctx, op1, t, TRUE); break; } case N_SPEC_DECL: { // ??? export and defintion with external declaration node_t specs = NL_HEAD (r->ops); node_t declarator = NL_NEXT (specs); node_t initializer = NL_NEXT (declarator); node_t id, curr_node; symbol_t sym; decl_t curr_decl; size_t i, init_start; const char *name; decl = (decl_t) r->attr; if (declarator != NULL && declarator->code != N_IGNORE && decl->item == NULL) { id = NL_HEAD (declarator->ops); name = (decl->scope != top_scope && decl->decl_spec.static_p ? get_func_static_var_name (ctx, id->u.s.s, decl) : id->u.s.s); if (decl->used_p && decl->scope != top_scope && decl->decl_spec.linkage == N_STATIC) { decl->item = MIR_new_forward (ctx, name); move_item_forward (ctx, decl->item); } else if (decl->used_p && decl->decl_spec.linkage != N_IGNORE) { if (symbol_find (c2m_ctx, S_REGULAR, id, decl->decl_spec.linkage == N_EXTERN ? top_scope : decl->scope, &sym) && (decl->item = get_ref_item (ctx, sym.def_node, name)) == NULL) { for (i = 0; i < VARR_LENGTH (node_t, sym.defs); i++) if ((decl->item = get_ref_item (ctx, VARR_GET (node_t, sym.defs, i), name)) != NULL) break; } if (decl->item == NULL) decl->item = MIR_new_import (ctx, name); if (decl->scope != top_scope) move_item_forward (ctx, decl->item); } if (declarator->code == N_DECL && decl->decl_spec.type->mode != TM_FUNC && !decl->decl_spec.typedef_p && !decl->decl_spec.extern_p) { if (initializer->code == N_IGNORE) { if (decl->scope != top_scope && decl->decl_spec.static_p) { decl->item = MIR_new_bss (ctx, name, raw_type_size (c2m_ctx, decl->decl_spec.type)); } else if (decl->scope == top_scope && symbol_find (c2m_ctx, S_REGULAR, id, top_scope, &sym) && ((curr_decl = sym.def_node->attr)->item == NULL || curr_decl->item->item_type != MIR_bss_item)) { for (i = 0; i < VARR_LENGTH (node_t, sym.defs); i++) { curr_node = VARR_GET (node_t, sym.defs, i); curr_decl = curr_node->attr; if ((curr_decl->item != NULL && curr_decl->item->item_type == MIR_bss_item) || NL_EL (curr_node->ops, 2)->code != N_IGNORE) break; } if (i >= VARR_LENGTH (node_t, sym.defs)) /* No item yet or no decl with intializer: */ decl->item = MIR_new_bss (ctx, name, raw_type_size (c2m_ctx, decl->decl_spec.type)); } } else if (initializer->code != N_IGNORE) { // ??? general code init_start = VARR_LENGTH (init_el_t, init_els); collect_init_els (c2m_ctx, NULL, &decl->decl_spec.type, initializer, decl->decl_spec.linkage == N_STATIC || decl->decl_spec.linkage == N_EXTERN || decl->decl_spec.static_p || decl->decl_spec.thread_local_p, TRUE); if (decl->scope == top_scope) qsort (VARR_ADDR (init_el_t, init_els) + init_start, VARR_LENGTH (init_el_t, init_els) - init_start, sizeof (init_el_t), cmp_init_el); if (id->attr == NULL) { node_t saved_scope = curr_scope; curr_scope = decl->scope; check (c2m_ctx, id, NULL); curr_scope = saved_scope; } if (decl->scope == top_scope || decl->decl_spec.static_p) { var = new_op (decl, MIR_new_ref_op (ctx, NULL)); } else { var = gen (ctx, id, NULL, NULL, FALSE, NULL); assert (var.decl != NULL && (var.mir_op.mode == MIR_OP_REG || (var.mir_op.mode == MIR_OP_MEM && var.mir_op.u.mem.index == 0))); } gen_initializer (ctx, init_start, var, name, raw_type_size (c2m_ctx, decl->decl_spec.type), decl->scope != top_scope && !decl->decl_spec.static_p); VARR_TRUNC (init_el_t, init_els, init_start); } if (decl->item != NULL && decl->scope == top_scope && !decl->decl_spec.static_p) { MIR_new_export (ctx, name); } else if (decl->item != NULL && decl->scope != top_scope && decl->decl_spec.static_p) { MIR_item_t item = MIR_new_forward (ctx, name); move_item_forward (ctx, item); } } } break; } case N_ST_ASSERT: /* do nothing */ break; case N_INIT: break; // ??? case N_FUNC_DEF: { node_t decl_specs = NL_HEAD (r->ops); node_t declarator = NL_NEXT (decl_specs); node_t decls = NL_NEXT (declarator); node_t stmt = NL_NEXT (decls); struct node_scope *ns = stmt->attr; decl_t param_decl, decl = r->attr; struct type *decl_type = decl->decl_spec.type; node_t param, param_declarator, param_id; struct type *param_type; MIR_insn_t insn; MIR_type_t res_type, param_mir_type; MIR_reg_t fp_reg, param_reg; const char *name; assert (declarator != NULL && declarator->code == N_DECL && NL_HEAD (declarator->ops)->code == N_ID); assert (decl_type->mode == TM_FUNC); reg_free_mark = 0; curr_func_def = r; curr_call_arg_area_offset = 0; collect_args_and_func_types (ctx, decl_type->u.func_type, &res_type); curr_func = ((decl_type->u.func_type->dots_p ? MIR_new_vararg_func_arr : MIR_new_func_arr) (ctx, NL_HEAD (declarator->ops)->u.s.s, res_type == MIR_T_UNDEF ? 0 : 1, &res_type, VARR_LENGTH (MIR_var_t, vars), VARR_ADDR (MIR_var_t, vars))); decl->item = curr_func; if (ns->stack_var_p /* we can have empty struct only with size 0 and still need a frame: */ || ns->size > 0) { fp_reg = MIR_new_func_reg (ctx, curr_func->u.func, MIR_T_I64, FP_NAME); MIR_append_insn (ctx, curr_func, MIR_new_insn (ctx, MIR_ALLOCA, MIR_new_reg_op (ctx, fp_reg), MIR_new_int_op (ctx, ns->size))); } for (size_t i = 0; i < VARR_LENGTH (MIR_var_t, vars); i++) get_reg_var (ctx, MIR_T_UNDEF, VARR_GET (MIR_var_t, vars, i).name); for (size_t i = 0; i < VARR_LENGTH (node_t, mem_params); i++) { param = VARR_GET (node_t, mem_params, i); param_declarator = NL_EL (param->ops, 1); param_decl = param->attr; assert (param_declarator != NULL && param_declarator->code == N_DECL); param_id = NL_HEAD (param_declarator->ops); param_type = param_decl->decl_spec.type; name = get_param_name (ctx, ¶m_mir_type, param_type, param_id->u.s.s); if (param_type->mode == TM_STRUCT || param_type->mode == TM_UNION) { param_reg = get_reg_var (ctx, MIR_POINTER_TYPE, name).reg; val = new_op (NULL, MIR_new_mem_op (ctx, MIR_T_UNDEF, 0, param_reg, 0, 1)); var = new_op (param_decl, MIR_new_mem_op (ctx, MIR_T_UNDEF, param_decl->offset, MIR_reg (ctx, FP_NAME, curr_func->u.func), 0, 1)); block_move (ctx, var, val, type_size (c2m_ctx, param_type)); } else { assert (!param_decl->reg_p); emit2 (ctx, tp_mov (param_mir_type), MIR_new_mem_op (ctx, param_mir_type, param_decl->offset, MIR_reg (ctx, FP_NAME, curr_func->u.func), 0, 1), MIR_new_reg_op (ctx, get_reg_var (ctx, MIR_T_UNDEF, name).reg)); } } gen (ctx, stmt, NULL, NULL, FALSE, NULL); if ((insn = DLIST_TAIL (MIR_insn_t, curr_func->u.func->insns)) == NULL || (insn->code != MIR_RET && insn->code != MIR_JMP)) { if (res_type == MIR_T_UNDEF) emit_insn (ctx, MIR_new_ret_insn (ctx, 0)); else if (res_type == MIR_T_D) emit_insn (ctx, MIR_new_ret_insn (ctx, 1, MIR_new_double_op (ctx, 0.0))); else if (res_type == MIR_T_LD) emit_insn (ctx, MIR_new_ret_insn (ctx, 1, MIR_new_ldouble_op (ctx, 0.0))); else if (res_type == MIR_T_F) emit_insn (ctx, MIR_new_ret_insn (ctx, 1, MIR_new_float_op (ctx, 0.0))); else if (scalar_type_p (adjust_type (c2m_ctx, decl->decl_spec.type->u.func_type->ret_type))) emit_insn (ctx, MIR_new_ret_insn (ctx, 1, MIR_new_int_op (ctx, 0))); else assert (FALSE); /* ??? not implemented */ } MIR_finish_func (ctx); if (decl->decl_spec.linkage == N_EXTERN) MIR_new_export (ctx, NL_HEAD (declarator->ops)->u.s.s); finish_curr_func_reg_vars (ctx); break; } case N_BLOCK: emit_label (ctx, r); gen (ctx, NL_EL (r->ops, 1), NULL, NULL, FALSE, NULL); break; case N_MODULE: gen (ctx, NL_HEAD (r->ops), NULL, NULL, FALSE, NULL); break; // ??? case N_IF: { node_t expr = NL_EL (r->ops, 1); node_t if_stmt = NL_NEXT (expr); node_t else_stmt = NL_NEXT (if_stmt); MIR_label_t if_label = MIR_new_label (ctx), else_label = MIR_new_label (ctx); MIR_label_t end_label = MIR_new_label (ctx); assert (false_label == NULL && true_label == NULL); emit_label (ctx, r); top_gen (ctx, expr, if_label, else_label); emit_label_insn_opt (ctx, if_label); gen (ctx, if_stmt, NULL, NULL, FALSE, NULL); emit1 (ctx, MIR_JMP, MIR_new_label_op (ctx, end_label)); emit_label_insn_opt (ctx, else_label); gen (ctx, else_stmt, NULL, NULL, FALSE, NULL); emit_label_insn_opt (ctx, end_label); break; } case N_SWITCH: { node_t expr = NL_EL (r->ops, 1); node_t stmt = NL_NEXT (expr); struct switch_attr *switch_attr = r->attr; op_t case_reg_op; struct expr *e2; case_t c; MIR_label_t saved_break_label = break_label; int signed_p, short_p; size_t len; mir_ullong range = 0; assert (false_label == NULL && true_label == NULL); emit_label (ctx, r); break_label = MIR_new_label (ctx); case_reg_op = gen (ctx, expr, NULL, NULL, TRUE, NULL); type = ((struct expr *) expr->attr)->type; signed_p = signed_integer_type_p (type); mir_type = get_mir_type (ctx, type); short_p = mir_type != MIR_T_I64 && mir_type != MIR_T_U64; case_reg_op = force_reg (ctx, case_reg_op, mir_type); if (switch_attr->min_val_case != NULL) { e = NL_HEAD (switch_attr->min_val_case->case_node->ops)->attr; e2 = NL_HEAD (switch_attr->max_val_case->case_node->ops)->attr; range = signed_p ? e2->u.i_val - e->u.i_val : e2->u.u_val - e->u.u_val; } len = DLIST_LENGTH (case_t, switch_attr->case_labels); if (!switch_attr->ranges_p && len > 4 && range != 0 && range / len < 3) { /* use MIR_SWITCH */ mir_ullong curr_val, prev_val, n; op_t index = get_new_temp (ctx, MIR_T_I64); MIR_label_t label = break_label; c = DLIST_TAIL (case_t, switch_attr->case_labels); if (c->case_node->code == N_DEFAULT) { assert (DLIST_NEXT (case_t, c) == NULL); label = get_label (ctx, c->case_target_node); } emit3 (ctx, short_p ? MIR_SUBS : MIR_SUB, index.mir_op, case_reg_op.mir_op, signed_p ? MIR_new_int_op (ctx, e->u.i_val) : MIR_new_uint_op (ctx, e->u.u_val)); emit3 (ctx, short_p ? MIR_UBGTS : MIR_UBGT, MIR_new_label_op (ctx, label), index.mir_op, MIR_new_uint_op (ctx, range)); VARR_TRUNC (case_t, switch_cases, 0); for (c = DLIST_HEAD (case_t, switch_attr->case_labels); c != NULL && c->case_node->code != N_DEFAULT; c = DLIST_NEXT (case_t, c)) VARR_PUSH (case_t, switch_cases, c); qsort (VARR_ADDR (case_t, switch_cases), VARR_LENGTH (case_t, switch_cases), sizeof (case_t), signed_p ? signed_case_compare : unsigned_case_compare); VARR_TRUNC (MIR_op_t, switch_ops, 0); VARR_PUSH (MIR_op_t, switch_ops, index.mir_op); for (size_t i = 0; i < VARR_LENGTH (case_t, switch_cases); i++) { c = VARR_GET (case_t, switch_cases, i); e2 = NL_HEAD (c->case_node->ops)->attr; curr_val = signed_p ? e2->u.i_val - e->u.i_val : e2->u.u_val - e->u.u_val; if (i != 0) { for (n = prev_val + 1; n < curr_val; n++) VARR_PUSH (MIR_op_t, switch_ops, MIR_new_label_op (ctx, label)); } VARR_PUSH (MIR_op_t, switch_ops, MIR_new_label_op (ctx, get_label (ctx, c->case_target_node))); prev_val = curr_val; } emit_insn (ctx, MIR_new_insn_arr (ctx, MIR_SWITCH, VARR_LENGTH (MIR_op_t, switch_ops), VARR_ADDR (MIR_op_t, switch_ops))); } else { for (c = DLIST_HEAD (case_t, switch_attr->case_labels); c != NULL; c = DLIST_NEXT (case_t, c)) { MIR_label_t cont_label, label = get_label (ctx, c->case_target_node); node_t case_expr, case_expr2; if (c->case_node->code == N_DEFAULT) { assert (DLIST_NEXT (case_t, c) == NULL); emit1 (ctx, MIR_JMP, MIR_new_label_op (ctx, label)); break; } case_expr = NL_HEAD (c->case_node->ops); case_expr2 = NL_NEXT (case_expr); e = case_expr->attr; assert (e->const_p && integer_type_p (e->type)); if (case_expr2 == NULL) { emit3 (ctx, short_p ? MIR_BEQS : MIR_BEQ, MIR_new_label_op (ctx, label), case_reg_op.mir_op, MIR_new_int_op (ctx, e->u.i_val)); } else { e2 = case_expr2->attr; assert (e2->const_p && integer_type_p (e2->type)); cont_label = MIR_new_label (ctx); if (signed_p) { emit3 (ctx, short_p ? MIR_BLTS : MIR_BLT, MIR_new_label_op (ctx, cont_label), case_reg_op.mir_op, MIR_new_int_op (ctx, e->u.i_val)); emit3 (ctx, short_p ? MIR_BLES : MIR_BLE, MIR_new_label_op (ctx, label), case_reg_op.mir_op, MIR_new_int_op (ctx, e2->u.i_val)); } else { emit3 (ctx, short_p ? MIR_UBLTS : MIR_UBLT, MIR_new_label_op (ctx, cont_label), case_reg_op.mir_op, MIR_new_int_op (ctx, e->u.i_val)); emit3 (ctx, short_p ? MIR_UBLES : MIR_UBLE, MIR_new_label_op (ctx, label), case_reg_op.mir_op, MIR_new_int_op (ctx, e2->u.i_val)); } emit_label_insn_opt (ctx, cont_label); } } if (c == NULL) /* no default: */ emit1 (ctx, MIR_JMP, MIR_new_label_op (ctx, break_label)); } top_gen (ctx, stmt, NULL, NULL); emit_label_insn_opt (ctx, break_label); break_label = saved_break_label; break; } case N_DO: { node_t expr = NL_EL (r->ops, 1); node_t stmt = NL_NEXT (expr); MIR_label_t saved_continue_label = continue_label, saved_break_label = break_label; MIR_label_t start_label = MIR_new_label (ctx); assert (false_label == NULL && true_label == NULL); continue_label = MIR_new_label (ctx); break_label = MIR_new_label (ctx); emit_label (ctx, r); emit_label_insn_opt (ctx, start_label); gen (ctx, stmt, NULL, NULL, FALSE, NULL); emit_label_insn_opt (ctx, continue_label); top_gen (ctx, expr, start_label, break_label); emit_label_insn_opt (ctx, break_label); continue_label = saved_continue_label; break_label = saved_break_label; break; } case N_WHILE: { node_t expr = NL_EL (r->ops, 1); node_t stmt = NL_NEXT (expr); MIR_label_t stmt_label = MIR_new_label (ctx); MIR_label_t saved_continue_label = continue_label, saved_break_label = break_label; assert (false_label == NULL && true_label == NULL); continue_label = MIR_new_label (ctx); break_label = MIR_new_label (ctx); emit_label (ctx, r); emit_label_insn_opt (ctx, continue_label); top_gen (ctx, expr, stmt_label, break_label); emit_label_insn_opt (ctx, stmt_label); gen (ctx, stmt, NULL, NULL, FALSE, NULL); top_gen (ctx, expr, stmt_label, break_label); emit_label_insn_opt (ctx, break_label); continue_label = saved_continue_label; break_label = saved_break_label; break; } case N_FOR: { node_t init = NL_EL (r->ops, 1); node_t cond = NL_NEXT (init); node_t iter = NL_NEXT (cond); node_t stmt = NL_NEXT (iter); MIR_label_t stmt_label = MIR_new_label (ctx); MIR_label_t saved_continue_label = continue_label, saved_break_label = break_label; assert (false_label == NULL && true_label == NULL); continue_label = MIR_new_label (ctx); break_label = MIR_new_label (ctx); emit_label (ctx, r); top_gen (ctx, init, NULL, NULL); if (cond->code != N_IGNORE) /* non-empty condition: */ top_gen (ctx, cond, stmt_label, break_label); emit_label_insn_opt (ctx, stmt_label); gen (ctx, stmt, NULL, NULL, FALSE, NULL); emit_label_insn_opt (ctx, continue_label); top_gen (ctx, iter, NULL, NULL); if (cond->code == N_IGNORE) { /* empty condition: */ emit1 (ctx, MIR_JMP, MIR_new_label_op (ctx, stmt_label)); } else { top_gen (ctx, cond, stmt_label, break_label); } emit_label_insn_opt (ctx, break_label); continue_label = saved_continue_label; break_label = saved_break_label; break; } case N_GOTO: { node_t target = r->attr; assert (false_label == NULL && true_label == NULL); emit_label (ctx, r); emit1 (ctx, MIR_JMP, MIR_new_label_op (ctx, get_label (ctx, target))); break; } case N_CONTINUE: assert (false_label == NULL && true_label == NULL); emit_label (ctx, r); emit1 (ctx, MIR_JMP, MIR_new_label_op (ctx, continue_label)); break; case N_BREAK: assert (false_label == NULL && true_label == NULL); emit_label (ctx, r); emit1 (ctx, MIR_JMP, MIR_new_label_op (ctx, break_label)); break; case N_RETURN: { decl_t func_decl = curr_func_def->attr; struct type *func_type = func_decl->decl_spec.type; struct type *ret_type = func_type->u.func_type->ret_type; int scalar_p = scalar_type_p (ret_type); mir_size_t size = type_size (c2m_ctx, ret_type); assert (false_label == NULL && true_label == NULL); emit_label (ctx, r); if (NL_EL (r->ops, 1)->code == N_IGNORE) { emit_insn (ctx, MIR_new_ret_insn (ctx, 0)); break; } if (!scalar_p) { MIR_reg_t ret_addr_reg = MIR_reg (ctx, RET_ADDR_NAME, curr_func->u.func); var = new_op (NULL, MIR_new_mem_op (ctx, MIR_T_I8, 0, ret_addr_reg, 0, 1)); } val = gen (ctx, NL_EL (r->ops, 1), NULL, NULL, scalar_p, scalar_p ? NULL : &var); if (scalar_p) { t = get_mir_type (ctx, ret_type); t = promote_mir_int_type (t); val = promote (ctx, val, t, FALSE); emit_insn (ctx, MIR_new_ret_insn (ctx, 1, val.mir_op)); } else { /* block return */ block_move (ctx, var, val, size); emit_insn (ctx, MIR_new_ret_insn (ctx, 0)); } break; } case N_EXPR: assert (false_label == NULL && true_label == NULL); emit_label (ctx, r); top_gen (ctx, NL_EL (r->ops, 1), NULL, NULL); break; default: abort (); } finish: if (true_label != NULL) { MIR_op_t lab_op = MIR_new_label_op (ctx, true_label); type = ((struct expr *) r->attr)->type; if (!floating_type_p (type)) { res = promote (ctx, force_val (ctx, res, type->arr_type != NULL), MIR_T_I64, FALSE); emit2 (ctx, MIR_BT, lab_op, res.mir_op); } else if (type->u.basic_type == TP_FLOAT) { emit3 (ctx, MIR_FBNE, lab_op, res.mir_op, MIR_new_float_op (ctx, 0.0)); } else if (type->u.basic_type == TP_DOUBLE) { emit3 (ctx, MIR_DBNE, lab_op, res.mir_op, MIR_new_double_op (ctx, 0.0)); } else { assert (type->u.basic_type == TP_LDOUBLE); emit3 (ctx, MIR_LDBNE, lab_op, res.mir_op, MIR_new_ldouble_op (ctx, 0.0)); } emit1 (ctx, MIR_JMP, MIR_new_label_op (ctx, false_label)); } else if (val_p) { res = force_val (ctx, res, ((struct expr *) r->attr)->type->arr_type != NULL); } if (stmt_p) curr_call_arg_area_offset = 0; return res; } DEF_HTAB (MIR_item_t); static HTAB (MIR_item_t) * proto_tab; static htab_hash_t proto_hash (MIR_item_t pi, void *arg) { MIR_proto_t p = pi->u.proto; MIR_var_t *args = VARR_ADDR (MIR_var_t, p->args); uint64_t h = mir_hash_init (42); h = mir_hash_step (h, p->nres); h = mir_hash_step (h, p->vararg_p); for (uint32_t i = 0; i < p->nres; i++) h = mir_hash_step (h, p->res_types[i]); for (size_t i = 0; i < VARR_LENGTH (MIR_var_t, p->args); i++) { h = mir_hash_step (h, args[i].type); h = mir_hash_step (h, mir_hash (args[i].name, strlen (args[i].name), 24)); } return mir_hash_finish (h); } static int proto_eq (MIR_item_t pi1, MIR_item_t pi2, void *arg) { MIR_proto_t p1 = pi1->u.proto, p2 = pi2->u.proto; if (p1->nres != p2->nres || p1->vararg_p != p2->vararg_p || VARR_LENGTH (MIR_var_t, p1->args) != VARR_LENGTH (MIR_var_t, p2->args)) return FALSE; for (uint32_t i = 0; i < p1->nres; i++) if (p1->res_types[i] != p2->res_types[i]) return FALSE; MIR_var_t *args1 = VARR_ADDR (MIR_var_t, p1->args), *args2 = VARR_ADDR (MIR_var_t, p2->args); for (size_t i = 0; i < VARR_LENGTH (MIR_var_t, p1->args); i++) if (args1[i].type != args2[i].type || strcmp (args1[i].name, args2[i].name) != 0) return FALSE; return TRUE; } static MIR_item_t get_mir_proto (MIR_context_t ctx, int vararg_p, MIR_type_t ret_type, VARR (MIR_var_t) * vars) { c2m_ctx_t c2m_ctx = *c2m_ctx_loc (ctx); struct MIR_item pi, *el; struct MIR_proto p; char buf[30]; pi.u.proto = &p; p.vararg_p = vararg_p; p.nres = ret_type == MIR_T_UNDEF ? 0 : 1; p.res_types = &ret_type; p.args = vars; if (HTAB_DO (MIR_item_t, proto_tab, &pi, HTAB_FIND, el)) return el; sprintf (buf, "proto%d", curr_mir_proto_num++); el = (vararg_p ? MIR_new_vararg_proto_arr : MIR_new_proto_arr) (ctx, buf, p.nres, &ret_type, VARR_LENGTH (MIR_var_t, vars), VARR_ADDR (MIR_var_t, vars)); HTAB_DO (MIR_item_t, proto_tab, el, HTAB_INSERT, el); return el; } static void gen_mir_protos (MIR_context_t ctx) { c2m_ctx_t c2m_ctx = *c2m_ctx_loc (ctx); node_t call, func; struct type *type; struct func_type *func_type; MIR_type_t ret_type; curr_mir_proto_num = 0; HTAB_CREATE (MIR_item_t, proto_tab, 512, proto_hash, proto_eq, NULL); for (size_t i = 0; i < VARR_LENGTH (node_t, call_nodes); i++) { call = VARR_GET (node_t, call_nodes, i); assert (call->code == N_CALL); func = NL_HEAD (call->ops); type = ((struct expr *) func->attr)->type; assert (type->mode == TM_PTR && type->u.ptr_type->mode == TM_FUNC); set_type_layout (c2m_ctx, type); func_type = type->u.ptr_type->u.func_type; assert (func_type->param_list->code == N_LIST); collect_args_and_func_types (ctx, func_type, &ret_type); func_type->proto_item = get_mir_proto (ctx, func_type->dots_p || NL_HEAD (func_type->param_list->ops) == NULL, ret_type, vars); } HTAB_DESTROY (MIR_item_t, proto_tab); } static void gen_finish (MIR_context_t ctx) { c2m_ctx_t c2m_ctx = *c2m_ctx_loc (ctx); if (c2m_ctx == NULL || c2m_ctx->gen_ctx == NULL) return; finish_reg_vars (ctx); if (vars != NULL) VARR_DESTROY (MIR_var_t, vars); if (mem_params != NULL) VARR_DESTROY (node_t, mem_params); if (call_ops != NULL) VARR_DESTROY (MIR_op_t, call_ops); if (switch_ops != NULL) VARR_DESTROY (MIR_op_t, switch_ops); if (switch_cases != NULL) VARR_DESTROY (case_t, switch_cases); if (init_els != NULL) VARR_DESTROY (init_el_t, init_els); free (c2m_ctx->gen_ctx); } static void gen_mir (MIR_context_t ctx, node_t r) { c2m_ctx_t c2m_ctx = *c2m_ctx_loc (ctx); c2m_ctx->gen_ctx = c2mir_calloc (ctx, sizeof (struct gen_ctx)); zero_op = new_op (NULL, MIR_new_int_op (ctx, 0)); one_op = new_op (NULL, MIR_new_int_op (ctx, 1)); minus_one_op = new_op (NULL, MIR_new_int_op (ctx, -1)); init_reg_vars (ctx); VARR_CREATE (MIR_var_t, vars, 32); VARR_CREATE (node_t, mem_params, 16); gen_mir_protos (ctx); VARR_CREATE (MIR_op_t, call_ops, 32); VARR_CREATE (MIR_op_t, switch_ops, 128); VARR_CREATE (case_t, switch_cases, 64); VARR_CREATE (init_el_t, init_els, 128); memset_proto = memset_item = memcpy_proto = memcpy_item = NULL; top_gen (ctx, r, NULL, NULL); gen_finish (ctx); } /* ------------------------- MIR generator finish ----------------------------- */ /* New Page */ static const char *get_node_name (node_code_t code) { #define REP_SEP ; #define C(n) \ case N_##n: return #n switch (code) { C (IGNORE); REP8 (C, I, L, LL, U, UL, ULL, F, D); REP8 (C, LD, CH, STR, ID, COMMA, ANDAND, OROR, EQ); REP8 (C, NE, LT, LE, GT, GE, ASSIGN, BITWISE_NOT, NOT); REP8 (C, AND, AND_ASSIGN, OR, OR_ASSIGN, XOR, XOR_ASSIGN, LSH, LSH_ASSIGN); REP8 (C, RSH, RSH_ASSIGN, ADD, ADD_ASSIGN, SUB, SUB_ASSIGN, MUL, MUL_ASSIGN); REP8 (C, DIV, DIV_ASSIGN, MOD, MOD_ASSIGN, IND, FIELD, ADDR, DEREF); REP8 (C, DEREF_FIELD, COND, INC, DEC, POST_INC, POST_DEC, ALIGNOF, SIZEOF); REP8 (C, EXPR_SIZEOF, CAST, COMPOUND_LITERAL, CALL, GENERIC, GENERIC_ASSOC, IF, SWITCH); REP8 (C, WHILE, DO, FOR, GOTO, CONTINUE, BREAK, RETURN, EXPR); REP8 (C, BLOCK, CASE, DEFAULT, LABEL, LIST, SPEC_DECL, SHARE, TYPEDEF); REP8 (C, EXTERN, STATIC, AUTO, REGISTER, THREAD_LOCAL, DECL, VOID, CHAR); REP8 (C, SHORT, INT, LONG, FLOAT, DOUBLE, SIGNED, UNSIGNED, BOOL); REP8 (C, STRUCT, UNION, ENUM, ENUM_CONST, MEMBER, CONST, RESTRICT, VOLATILE); REP8 (C, ATOMIC, INLINE, NO_RETURN, ALIGNAS, FUNC, STAR, POINTER, DOTS); REP7 (C, ARR, INIT, FIELD_ID, TYPE, ST_ASSERT, FUNC_DEF, MODULE); default: abort (); } #undef C #undef REP_SEP } static void print_char (FILE *f, int ch) { assert (ch >= 0); if (ch == '"' || ch == '\"' || ch == '\\') fprintf (f, "\\"); if (isprint (ch)) fprintf (f, "%c", ch); else fprintf (f, "\\%o", ch); } static void print_chars (FILE *f, const char *str, size_t len) { for (size_t i = 0; i < len; i++) print_char (f, str[i]); } static void print_node (MIR_context_t ctx, FILE *f, node_t n, int indent, int attr_p); void debug_node (MIR_context_t ctx, node_t n) { print_node (ctx, stderr, n, 0, TRUE); } static void print_ops (MIR_context_t ctx, FILE *f, node_t n, int indent, int attr_p) { int i; node_t op; for (i = 0; (op = get_op (n, i)) != NULL; i++) print_node (ctx, f, op, indent + 2, attr_p); } static void print_qual (FILE *f, struct type_qual type_qual) { if (type_qual.const_p) fprintf (f, ", const"); if (type_qual.restrict_p) fprintf (f, ", restrict"); if (type_qual.volatile_p) fprintf (f, ", volatile"); if (type_qual.atomic_p) fprintf (f, ", atomic"); } static void print_type (MIR_context_t ctx, FILE *f, struct type *type) { c2m_ctx_t c2m_ctx = *c2m_ctx_loc (ctx); switch (type->mode) { case TM_UNDEF: fprintf (f, "undef type mode"); break; case TM_BASIC: switch (type->u.basic_type) { case TP_UNDEF: fprintf (f, "undef type"); break; case TP_VOID: fprintf (f, "void"); break; case TP_BOOL: fprintf (f, "bool"); break; case TP_CHAR: fprintf (f, "char"); break; case TP_SCHAR: fprintf (f, "signed char"); break; case TP_UCHAR: fprintf (f, "unsigned char"); break; case TP_SHORT: fprintf (f, "short"); break; case TP_USHORT: fprintf (f, "unsigned short"); break; case TP_INT: fprintf (f, "int"); break; case TP_UINT: fprintf (f, "unsigned int"); break; case TP_LONG: fprintf (f, "long"); break; case TP_ULONG: fprintf (f, "unsigned long"); break; case TP_LLONG: fprintf (f, "long long"); break; case TP_ULLONG: fprintf (f, "unsigned long long"); break; case TP_FLOAT: fprintf (f, "float"); break; case TP_DOUBLE: fprintf (f, "double"); break; case TP_LDOUBLE: fprintf (f, "long double"); break; default: assert (FALSE); } break; case TM_ENUM: fprintf (f, "enum node %lu", type->u.tag_type->uid); break; case TM_PTR: fprintf (f, "ptr ("); print_type (ctx, f, type->u.ptr_type); fprintf (f, ")"); break; case TM_STRUCT: fprintf (f, "struct node %lu", type->u.tag_type->uid); break; case TM_UNION: fprintf (f, "union node %lu", type->u.tag_type->uid); break; case TM_ARR: fprintf (f, "array [%s", type->u.arr_type->static_p ? "static " : ""); print_qual (f, type->u.arr_type->ind_type_qual); fprintf (f, "size node %lu] (", type->u.arr_type->size->uid); print_type (ctx, f, type->u.arr_type->el_type); fprintf (f, ")"); break; case TM_FUNC: fprintf (f, "func "); print_type (ctx, f, type->u.func_type->ret_type); fprintf (f, "(params node %lu", type->u.func_type->param_list->uid); fprintf (f, type->u.func_type->dots_p ? ", ...)" : ")"); break; default: assert (FALSE); } print_qual (f, type->type_qual); if (incomplete_type_p (c2m_ctx, type)) fprintf (f, ", incomplete"); if (type->raw_size != MIR_SIZE_MAX) fprintf (f, ", raw size = %llu", (unsigned long long) type->raw_size); if (type->align >= 0) fprintf (f, ", align = %d", type->align); fprintf (f, " "); } static void print_decl_spec (MIR_context_t ctx, FILE *f, struct decl_spec *decl_spec) { if (decl_spec->typedef_p) fprintf (f, " typedef, "); if (decl_spec->extern_p) fprintf (f, " extern, "); if (decl_spec->static_p) fprintf (f, " static, "); if (decl_spec->auto_p) fprintf (f, " auto, "); if (decl_spec->register_p) fprintf (f, " register, "); if (decl_spec->thread_local_p) fprintf (f, " thread local, "); if (decl_spec->inline_p) fprintf (f, " inline, "); if (decl_spec->no_return_p) fprintf (f, " no return, "); if (decl_spec->align >= 0) fprintf (f, " align = %d, ", decl_spec->align); if (decl_spec->align_node != NULL) fprintf (f, " strictest align node %lu, ", decl_spec->align_node->uid); if (decl_spec->linkage != N_IGNORE) fprintf (f, " %s linkage, ", decl_spec->linkage == N_STATIC ? "static" : "extern"); print_type (ctx, f, decl_spec->type); } static void print_decl (MIR_context_t ctx, FILE *f, decl_t decl) { if (decl == NULL) return; fprintf (f, ": "); if (decl->scope != NULL) fprintf (f, "scope node = %lu, ", decl->scope->uid); print_decl_spec (ctx, f, &decl->decl_spec); if (decl->addr_p) fprintf (f, ", addressable"); if (decl->used_p) fprintf (f, ", used"); if (decl->reg_p) fprintf (f, ", reg"); else { fprintf (f, ", offset = %llu", (unsigned long long) decl->offset); if (decl->bit_offset >= 0) fprintf (f, ", bit offset = %d", decl->bit_offset); } } static void print_expr (MIR_context_t ctx, FILE *f, struct expr *e) { if (e == NULL) return; /* e.g. N_ID which is not an expr */ fprintf (f, ": "); if (e->lvalue_node) fprintf (f, "lvalue, "); print_type (ctx, f, e->type); if (e->const_p) { fprintf (f, ", const = "); if (!integer_type_p (e->type)) { fprintf (f, " %.*Lg\n", LDBL_MANT_DIG, (long double) e->u.d_val); } else if (signed_integer_type_p (e->type)) { fprintf (f, "%lld", (long long) e->u.i_val); } else { fprintf (f, "%llu", (unsigned long long) e->u.u_val); } } } static void print_node (MIR_context_t ctx, FILE *f, node_t n, int indent, int attr_p) { int i; fprintf (f, "%6lu: ", n->uid); for (i = 0; i < indent; i++) fprintf (f, " "); if (n == err_node) { fprintf (f, "\n"); return; } fprintf (f, "%s (", get_node_name (n->code)); print_pos (f, n->pos, FALSE); fprintf (f, ")"); switch (n->code) { case N_IGNORE: fprintf (f, "\n"); break; case N_I: fprintf (f, " %lld", (long long) n->u.l); goto expr; case N_L: fprintf (f, " %lldl", (long long) n->u.l); goto expr; case N_LL: fprintf (f, " %lldll", (long long) n->u.ll); goto expr; case N_U: fprintf (f, " %lluu", (unsigned long long) n->u.ul); goto expr; case N_UL: fprintf (f, " %lluul", (unsigned long long) n->u.ul); goto expr; case N_ULL: fprintf (f, " %lluull", (unsigned long long) n->u.ull); goto expr; case N_F: fprintf (f, " %.*g", FLT_MANT_DIG, (double) n->u.f); goto expr; case N_D: fprintf (f, " %.*g", DBL_MANT_DIG, (double) n->u.d); goto expr; case N_LD: fprintf (f, " %.*Lg", LDBL_MANT_DIG, (long double) n->u.ld); goto expr; case N_CH: fprintf (f, " '"); print_char (f, n->u.ch); fprintf (f, "'"); goto expr; case N_STR: fprintf (f, " \""); print_chars (f, n->u.s.s, n->u.s.len); fprintf (f, "\""); goto expr; case N_ID: fprintf (f, " %s", n->u.s.s); expr: if (attr_p && n->attr != NULL) print_expr (ctx, f, n->attr); fprintf (f, "\n"); break; case N_COMMA: case N_ANDAND: case N_OROR: case N_EQ: case N_NE: case N_LT: case N_LE: case N_GT: case N_GE: case N_ASSIGN: case N_BITWISE_NOT: case N_NOT: case N_AND: case N_AND_ASSIGN: case N_OR: case N_OR_ASSIGN: case N_XOR: case N_XOR_ASSIGN: case N_LSH: case N_LSH_ASSIGN: case N_RSH: case N_RSH_ASSIGN: case N_ADD: case N_ADD_ASSIGN: case N_SUB: case N_SUB_ASSIGN: case N_MUL: case N_MUL_ASSIGN: case N_DIV: case N_DIV_ASSIGN: case N_MOD: case N_MOD_ASSIGN: case N_IND: case N_FIELD: case N_ADDR: case N_DEREF: case N_DEREF_FIELD: case N_COND: case N_INC: case N_DEC: case N_POST_INC: case N_POST_DEC: case N_ALIGNOF: case N_SIZEOF: case N_EXPR_SIZEOF: case N_CAST: case N_COMPOUND_LITERAL: case N_CALL: case N_GENERIC: if (attr_p && n->attr != NULL) print_expr (ctx, f, n->attr); fprintf (f, "\n"); print_ops (ctx, f, n, indent, attr_p); break; case N_GENERIC_ASSOC: case N_IF: case N_WHILE: case N_DO: case N_CONTINUE: case N_BREAK: case N_RETURN: case N_EXPR: case N_CASE: case N_DEFAULT: case N_LABEL: case N_SHARE: case N_TYPEDEF: case N_EXTERN: case N_STATIC: case N_AUTO: case N_REGISTER: case N_THREAD_LOCAL: case N_DECL: case N_VOID: case N_CHAR: case N_SHORT: case N_INT: case N_LONG: case N_FLOAT: case N_DOUBLE: case N_SIGNED: case N_UNSIGNED: case N_BOOL: case N_ENUM: case N_CONST: case N_RESTRICT: case N_VOLATILE: case N_ATOMIC: case N_INLINE: case N_NO_RETURN: case N_ALIGNAS: case N_STAR: case N_POINTER: case N_DOTS: case N_ARR: case N_INIT: case N_FIELD_ID: case N_TYPE: case N_ST_ASSERT: fprintf (f, "\n"); print_ops (ctx, f, n, indent, attr_p); break; case N_LIST: if (attr_p && n->attr != NULL) { fprintf (f, ": "); print_decl_spec (ctx, f, (struct decl_spec *) n->attr); } fprintf (f, "\n"); print_ops (ctx, f, n, indent, attr_p); break; case N_SPEC_DECL: case N_MEMBER: case N_FUNC_DEF: if (attr_p && n->attr != NULL) print_decl (ctx, f, (decl_t) n->attr); fprintf (f, "\n"); print_ops (ctx, f, n, indent, attr_p); break; case N_FUNC: if (!attr_p || n->attr == NULL) { fprintf (f, "\n"); print_ops (ctx, f, n, indent, attr_p); break; } /* fall through: */ case N_STRUCT: case N_UNION: case N_MODULE: case N_BLOCK: case N_FOR: if (!attr_p || ((n->code == N_STRUCT || n->code == N_UNION) && (NL_EL (n->ops, 1) == NULL || NL_EL (n->ops, 1)->code == N_IGNORE))) fprintf (f, "\n"); else if (n->code == N_MODULE) fprintf (f, ": the top scope"); else if (n->attr != NULL) fprintf (f, ": higher scope node %lu", ((struct node_scope *) n->attr)->scope->uid); if (n->code == N_STRUCT || n->code == N_UNION) fprintf (f, "\n"); else if (attr_p && n->attr != NULL) fprintf (f, ", size = %llu, offset = %llu\n", (unsigned long long) ((struct node_scope *) n->attr)->size, (unsigned long long) ((struct node_scope *) n->attr)->offset); print_ops (ctx, f, n, indent, attr_p); break; case N_SWITCH: if (attr_p && n->attr != NULL) { fprintf (f, ": "); print_type (ctx, f, &((struct switch_attr *) n->attr)->type); } fprintf (f, "\n"); print_ops (ctx, f, n, indent, attr_p); break; case N_GOTO: if (attr_p && n->attr != NULL) fprintf (f, ": target node %lu\n", ((node_t) n->attr)->uid); print_ops (ctx, f, n, indent, attr_p); break; case N_ENUM_CONST: if (attr_p && n->attr != NULL) fprintf (f, ": val = %lld\n", (long long) ((struct enum_value *) n->attr)->val); print_ops (ctx, f, n, indent, attr_p); break; default: abort (); } } static void init_include_dirs (MIR_context_t ctx) { c2m_ctx_t c2m_ctx = *c2m_ctx_loc (ctx); const char *str; int added_p = FALSE; VARR_CREATE (char_ptr_t, headers, 0); VARR_CREATE (char_ptr_t, system_headers, 0); for (size_t i = 0; i < options->include_dirs_num; i++) { VARR_PUSH (char_ptr_t, headers, options->include_dirs[i]); VARR_PUSH (char_ptr_t, system_headers, options->include_dirs[i]); } VARR_PUSH (char_ptr_t, headers, NULL); for (size_t i = 0; i < sizeof (standard_include_dirs) / sizeof (char *); i++) { VARR_TRUNC (char, temp_string, 0); add_to_temp_string (c2m_ctx, SOURCEDIR); add_to_temp_string (c2m_ctx, standard_include_dirs[i]); str = uniq_cstr (c2m_ctx, VARR_ADDR (char, temp_string)).s; VARR_PUSH (char_ptr_t, system_headers, str); VARR_TRUNC (char, temp_string, 0); add_to_temp_string (c2m_ctx, INSTALLDIR); add_to_temp_string (c2m_ctx, "../"); add_to_temp_string (c2m_ctx, standard_include_dirs[i]); str = uniq_cstr (c2m_ctx, VARR_ADDR (char, temp_string)).s; VARR_PUSH (char_ptr_t, system_headers, str); } #if defined(__APPLE__) || defined(__unix__) VARR_PUSH (char_ptr_t, system_headers, "/usr/local/include"); #endif #ifdef ADDITIONAL_INCLUDE_PATH if (ADDITIONAL_INCLUDE_PATH[0] != 0) { added_p = TRUE; VARR_PUSH (char_ptr_t, system_headers, ADDITIONAL_INCLUDE_PATH); } #endif #if defined(__APPLE__) if (!added_p) VARR_PUSH (char_ptr_t, system_headers, "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include"); #endif #if defined(__linux__) && defined(__x86_64__) VARR_PUSH (char_ptr_t, system_headers, "/usr/include/x86_64-linux-gnu"); #elif defined(__linux__) && defined(__aarch64__) VARR_PUSH (char_ptr_t, system_headers, "/usr/include/aarch64-linux-gnu"); #endif #if defined(__APPLE__) || defined(__unix__) VARR_PUSH (char_ptr_t, system_headers, "/usr/include"); #endif VARR_PUSH (char_ptr_t, system_headers, NULL); header_dirs = (const char **) VARR_ADDR (char_ptr_t, headers); system_header_dirs = (const char **) VARR_ADDR (char_ptr_t, system_headers); } static int check_id_p (c2m_ctx_t c2m_ctx, const char *str) { int ok_p; if ((ok_p = isalpha (str[0]) || str[0] == '_')) { for (size_t i = 1; str[i] != '\0'; i++) if (!isalnum (str[i]) && str[i] != '_') { ok_p = FALSE; break; } } if (!ok_p && options->message_file != NULL) fprintf (options->message_file, "macro name %s is not an identifier\n", str); return ok_p; } static void define_cmd_macro (c2m_ctx_t c2m_ctx, const char *name, const char *def) { pos_t pos; token_t t, id; struct macro macro; macro_t tab_m; VARR (token_t) * repl; pos.fname = COMMAND_LINE_SOURCE_NAME; pos.lno = 1; pos.ln_pos = 0; VARR_CREATE (token_t, repl, 16); id = new_id_token (c2m_ctx, pos, name); VARR_TRUNC (char, temp_string, 0); for (; *def != '\0'; def++) VARR_PUSH (char, temp_string, *def); VARR_PUSH (char, temp_string, '\0'); reverse (temp_string); set_string_stream (c2m_ctx, VARR_ADDR (char, temp_string), pos, NULL); while ((t = get_next_pptoken (c2m_ctx))->code != T_EOFILE && t->code != T_EOU) VARR_PUSH (token_t, repl, t); if (check_id_p (c2m_ctx, id->repr)) { macro.id = id; if (HTAB_DO (macro_t, macro_tab, ¯o, HTAB_FIND, tab_m)) { if (!replacement_eq_p (tab_m->replacement, repl) && options->message_file != NULL) fprintf (options->message_file, "warning -- redefinition of macro %s on the command line\n", id->repr); HTAB_DO (macro_t, macro_tab, ¯o, HTAB_DELETE, tab_m); } new_macro (c2m_ctx, macro.id, NULL, repl); } } static void undefine_cmd_macro (c2m_ctx_t c2m_ctx, const char *name) { pos_t pos; token_t id; struct macro macro; macro_t tab_m; pos.fname = COMMAND_LINE_SOURCE_NAME; pos.lno = 1; pos.ln_pos = 0; id = new_id_token (c2m_ctx, pos, name); if (check_id_p (c2m_ctx, id->repr)) { macro.id = id; HTAB_DO (macro_t, macro_tab, ¯o, HTAB_DELETE, tab_m); } } static void process_macro_commands (MIR_context_t ctx) { c2m_ctx_t c2m_ctx = *c2m_ctx_loc (ctx); for (size_t i = 0; i < options->macro_commands_num; i++) if (options->macro_commands[i].def) define_cmd_macro (c2m_ctx, options->macro_commands[i].name, options->macro_commands[i].def); else undefine_cmd_macro (c2m_ctx, options->macro_commands[i].name); } static void compile_init (MIR_context_t ctx, struct c2mir_options *ops, int (*getc_func) (void *), void *getc_data) { c2m_ctx_t c2m_ctx = *c2m_ctx_loc (ctx); options = ops; n_errors = n_warnings = 0; c_getc = getc_func; c_getc_data = getc_data; VARR_CREATE (char, symbol_text, 128); VARR_CREATE (char, temp_string, 128); parse_init (ctx); curr_scope = NULL; context_init (ctx); init_include_dirs (ctx); process_macro_commands (ctx); VARR_CREATE (node_t, call_nodes, 128); /* used in context and gen */ VARR_CREATE (node_t, containing_anon_members, 8); VARR_CREATE (init_object_t, init_object_path, 8); } static void compile_finish (MIR_context_t ctx) { c2m_ctx_t c2m_ctx = *c2m_ctx_loc (ctx); if (symbol_text != NULL) VARR_DESTROY (char, symbol_text); if (temp_string != NULL) VARR_DESTROY (char, temp_string); parse_finish (c2m_ctx); context_finish (ctx); if (headers != NULL) VARR_DESTROY (char_ptr_t, headers); if (system_headers != NULL) VARR_DESTROY (char_ptr_t, system_headers); if (call_nodes != NULL) VARR_DESTROY (node_t, call_nodes); if (containing_anon_members != NULL) VARR_DESTROY (node_t, containing_anon_members); if (init_object_path != NULL) VARR_DESTROY (init_object_t, init_object_path); } #include #if defined(__unix__) || defined(__APPLE__) #include #endif static double real_usec_time (void) { struct timeval tv; gettimeofday (&tv, NULL); return tv.tv_usec + tv.tv_sec * 1000000.0; } static const char *get_module_name (MIR_context_t ctx) { c2m_ctx_t c2m_ctx = *c2m_ctx_loc (ctx); static char str[50]; sprintf (str, "M%ld", (long) options->module_num); return str; } static int top_level_getc (c2m_ctx_t c2m_ctx) { return c_getc (c_getc_data); } int c2mir_compile (MIR_context_t ctx, struct c2mir_options *ops, int (*getc_func) (void *), void *getc_data, const char *source_name, FILE *output_file) { c2m_ctx_t c2m_ctx = *c2m_ctx_loc (ctx); double start_time = real_usec_time (); node_t r; unsigned n_error_before; MIR_module_t m; if (c2m_ctx == NULL) return 0; if (setjmp (c2m_ctx->env)) { compile_finish (ctx); return 0; } compile_init (ctx, ops, getc_func, getc_data); if (options->verbose_p && options->message_file != NULL) fprintf (options->message_file, "C2MIR init end -- %.0f usec\n", real_usec_time () - start_time); add_stream (c2m_ctx, NULL, source_name, top_level_getc); if (!options->no_prepro_p) add_standard_includes (c2m_ctx); pre (c2m_ctx, source_name); if (options->verbose_p && options->message_file != NULL) fprintf (options->message_file, " C2MIR preprocessor end -- %.0f usec\n", real_usec_time () - start_time); if (!options->prepro_only_p) { r = parse (c2m_ctx); if (options->verbose_p && options->message_file != NULL) fprintf (options->message_file, " C2MIR parser end -- %.0f usec\n", real_usec_time () - start_time); if (options->verbose_p && options->message_file != NULL && n_errors) fprintf (options->message_file, "parser - FAIL\n"); if (!options->syntax_only_p) { n_error_before = n_errors; do_context (c2m_ctx, r); if (n_errors > n_error_before) { if (options->debug_p) print_node (ctx, options->message_file, r, 0, FALSE); if (options->verbose_p && options->message_file != NULL) fprintf (options->message_file, "C2MIR context checker - FAIL\n"); } else { if (options->debug_p) print_node (ctx, options->message_file, r, 0, TRUE); if (options->verbose_p && options->message_file != NULL) fprintf (options->message_file, " C2MIR context checker end -- %.0f usec\n", real_usec_time () - start_time); m = MIR_new_module (ctx, get_module_name (ctx)); gen_mir (ctx, r); if ((options->asm_p || options->object_p) && n_errors == 0) { if (strcmp (source_name, COMMAND_LINE_SOURCE_NAME) == 0) { MIR_output_module (ctx, options->message_file, m); } else if (output_file != NULL) { (options->asm_p ? MIR_output_module : MIR_write_module) (ctx, output_file, m); if (ferror (output_file) || fclose (output_file)) { fprintf (options->message_file, "C2MIR error in writing mir for source file %s\n", source_name); n_errors++; } } } MIR_finish_module (ctx); if (options->verbose_p && options->message_file != NULL) fprintf (options->message_file, " C2MIR generator end -- %.0f usec\n", real_usec_time () - start_time); } } } compile_finish (ctx); if (options->verbose_p && options->message_file != NULL) fprintf (options->message_file, "C2MIR compiler end -- %.0f usec\n", real_usec_time () - start_time); return n_errors == 0; } /* Local Variables: */ /* mode: c */ /* page-delimiter: "/\\* New Page" */ /* End: */