You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
12393 lines
440 KiB
12393 lines
440 KiB
/* This file is a part of MIR project.
|
|
Copyright (C) 2018-2020 Vladimir Makarov <vmakarov.gcc@gmail.com>.
|
|
*/
|
|
|
|
/* 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 <assert.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <float.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <stdarg.h>
|
|
#include <errno.h>
|
|
#include <setjmp.h>
|
|
#include <math.h>
|
|
#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, "<EOU>", 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, "<EOF>", 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 <EOR>: 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 <EOA> 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 <BOA> 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 <EOR>\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, "<EOF>",
|
|
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 ("<res_type> (<typedef_name>)") which can conflict with declarator ("<res_type>
|
|
(<new decl identifier>)") */
|
|
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, "<statement>");
|
|
}
|
|
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, "<declarator>");
|
|
}
|
|
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, "<environment>", 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__[] = "<func name>" 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}_<integer> -- temporary of I64, U64, I32, U32, F, D type
|
|
{I|U|i|u|f|d}<scope_number>_<name> -- variable with of original <name> of the corresponding
|
|
type in scope with <scope_number>
|
|
*/
|
|
|
|
#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, "<error>\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 <sys/time.h>
|
|
#if defined(__unix__) || defined(__APPLE__)
|
|
#include <sys/stat.h>
|
|
#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: */
|