diff --git a/CMakeLists.txt b/CMakeLists.txt index 7c3189c..a184bea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -78,8 +78,10 @@ if (OMR_JIT) endif () if (MIR_JIT) - find_package(MIRJIT REQUIRED) - include_directories(${MIRJIT_INCLUDE_DIRS}) + add_subdirectory(mir) + include_directories(mir) + include_directories(mir/c2mir) + set(MIRJIT_LIBRARIES c2mir) add_definitions(-DUSE_MIRJIT) endif () diff --git a/mir/CMakeLists.txt b/mir/CMakeLists.txt new file mode 100644 index 0000000..e3790a5 --- /dev/null +++ b/mir/CMakeLists.txt @@ -0,0 +1,44 @@ +project(mir) + +enable_language(C) + +message(STATUS "OS type is ${CMAKE_SYSTEM_NAME}") +message(STATUS "System processor is ${CMAKE_HOST_SYSTEM_PROCESSOR}") +message(STATUS "Build type is ${CMAKE_BUILD_TYPE}") + +set(TARGET x86_64) + +set(MIR_HEADERS + mir.h + mir-gen.h + mir-varr.h + mir-dlist.h + mir-htab.h + mir-hash.h + mir-bitmap.h + ) + +set(MIR_SRCS + mir.c + mir-gen.c + ) + +set(C2MIR_SRCS + c2mir/c2mir.c + ) + +set(LIBS dl) + +add_definitions(-D${TARGET}) +add_definitions(-DMIR_IO) +add_definitions(-DMIR_SCAN) + +include_directories(".") +include_directories("./c2mir") + +add_library(c2mir + ${MIR_HEADERS} + ${MIR_SRCS} + ${C2MIR_SRCS}) +target_link_libraries(c2mir ${LIBS}) + diff --git a/mir/LICENSE b/mir/LICENSE new file mode 100644 index 0000000..4221723 --- /dev/null +++ b/mir/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Vladimir Makarov + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/mir/build/Makefile b/mir/build/Makefile new file mode 100644 index 0000000..5ed5bcc --- /dev/null +++ b/mir/build/Makefile @@ -0,0 +1,202 @@ +# CMAKE generated file: DO NOT EDIT! +# Generated by "Unix Makefiles" Generator, CMake Version 3.9 + +# Default target executed when no arguments are given to make. +default_target: all + +.PHONY : default_target + +# Allow only one "make -f Makefile2" at a time, but pass parallelism. +.NOTPARALLEL: + + +#============================================================================= +# Special targets provided by cmake. + +# Disable implicit rules so canonical targets will work. +.SUFFIXES: + + +# Remove some rules from gmake that .SUFFIXES does not remove. +SUFFIXES = + +.SUFFIXES: .hpux_make_needs_suffix_list + + +# Suppress display of executed commands. +$(VERBOSE).SILENT: + + +# A target that is always out of date. +cmake_force: + +.PHONY : cmake_force + +#============================================================================= +# Set environment variables for the build. + +# The shell in which to execute make rules. +SHELL = /bin/sh + +# The CMake executable. +CMAKE_COMMAND = /Applications/CMake.app/Contents/bin/cmake + +# The command to remove a file. +RM = /Applications/CMake.app/Contents/bin/cmake -E remove -f + +# Escaping for special characters. +EQUALS = = + +# The top-level source directory on which CMake was run. +CMAKE_SOURCE_DIR = /Users/dylan/github/ravi/mir + +# The top-level build directory on which CMake was run. +CMAKE_BINARY_DIR = /Users/dylan/github/ravi/mir/build + +#============================================================================= +# Targets provided globally by CMake. + +# Special rule for the target rebuild_cache +rebuild_cache: + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Running CMake to regenerate build system..." + /Applications/CMake.app/Contents/bin/cmake -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) +.PHONY : rebuild_cache + +# Special rule for the target rebuild_cache +rebuild_cache/fast: rebuild_cache + +.PHONY : rebuild_cache/fast + +# Special rule for the target edit_cache +edit_cache: + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Running CMake cache editor..." + /Applications/CMake.app/Contents/bin/ccmake -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) +.PHONY : edit_cache + +# Special rule for the target edit_cache +edit_cache/fast: edit_cache + +.PHONY : edit_cache/fast + +# The main all target +all: cmake_check_build_system + $(CMAKE_COMMAND) -E cmake_progress_start /Users/dylan/github/ravi/mir/build/CMakeFiles /Users/dylan/github/ravi/mir/build/CMakeFiles/progress.marks + $(MAKE) -f CMakeFiles/Makefile2 all + $(CMAKE_COMMAND) -E cmake_progress_start /Users/dylan/github/ravi/mir/build/CMakeFiles 0 +.PHONY : all + +# The main clean target +clean: + $(MAKE) -f CMakeFiles/Makefile2 clean +.PHONY : clean + +# The main clean target +clean/fast: clean + +.PHONY : clean/fast + +# Prepare targets for installation. +preinstall: all + $(MAKE) -f CMakeFiles/Makefile2 preinstall +.PHONY : preinstall + +# Prepare targets for installation. +preinstall/fast: + $(MAKE) -f CMakeFiles/Makefile2 preinstall +.PHONY : preinstall/fast + +# clear depends +depend: + $(CMAKE_COMMAND) -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 1 +.PHONY : depend + +#============================================================================= +# Target rules for targets named c2mir + +# Build rule for target. +c2mir: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 c2mir +.PHONY : c2mir + +# fast build rule for target. +c2mir/fast: + $(MAKE) -f CMakeFiles/c2mir.dir/build.make CMakeFiles/c2mir.dir/build +.PHONY : c2mir/fast + +# target to build an object file +c2mir/c2mir.o: + $(MAKE) -f CMakeFiles/c2mir.dir/build.make CMakeFiles/c2mir.dir/c2mir/c2mir.o +.PHONY : c2mir/c2mir.o + +# target to preprocess a source file +c2mir/c2mir.i: + $(MAKE) -f CMakeFiles/c2mir.dir/build.make CMakeFiles/c2mir.dir/c2mir/c2mir.i +.PHONY : c2mir/c2mir.i + +# target to generate assembly for a file +c2mir/c2mir.s: + $(MAKE) -f CMakeFiles/c2mir.dir/build.make CMakeFiles/c2mir.dir/c2mir/c2mir.s +.PHONY : c2mir/c2mir.s + +# target to build an object file +mir-gen.o: + $(MAKE) -f CMakeFiles/c2mir.dir/build.make CMakeFiles/c2mir.dir/mir-gen.o +.PHONY : mir-gen.o + +# target to preprocess a source file +mir-gen.i: + $(MAKE) -f CMakeFiles/c2mir.dir/build.make CMakeFiles/c2mir.dir/mir-gen.i +.PHONY : mir-gen.i + +# target to generate assembly for a file +mir-gen.s: + $(MAKE) -f CMakeFiles/c2mir.dir/build.make CMakeFiles/c2mir.dir/mir-gen.s +.PHONY : mir-gen.s + +# target to build an object file +mir.o: + $(MAKE) -f CMakeFiles/c2mir.dir/build.make CMakeFiles/c2mir.dir/mir.o +.PHONY : mir.o + +# target to preprocess a source file +mir.i: + $(MAKE) -f CMakeFiles/c2mir.dir/build.make CMakeFiles/c2mir.dir/mir.i +.PHONY : mir.i + +# target to generate assembly for a file +mir.s: + $(MAKE) -f CMakeFiles/c2mir.dir/build.make CMakeFiles/c2mir.dir/mir.s +.PHONY : mir.s + +# Help Target +help: + @echo "The following are some of the valid targets for this Makefile:" + @echo "... all (the default if no target is provided)" + @echo "... clean" + @echo "... depend" + @echo "... rebuild_cache" + @echo "... edit_cache" + @echo "... c2mir" + @echo "... c2mir/c2mir.o" + @echo "... c2mir/c2mir.i" + @echo "... c2mir/c2mir.s" + @echo "... mir-gen.o" + @echo "... mir-gen.i" + @echo "... mir-gen.s" + @echo "... mir.o" + @echo "... mir.i" + @echo "... mir.s" +.PHONY : help + + + +#============================================================================= +# Special targets to cleanup operation of make. + +# Special rule to run CMake to check the build system integrity. +# No rule that depends on this can have commands that come from listfiles +# because they might be regenerated. +cmake_check_build_system: + $(CMAKE_COMMAND) -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 0 +.PHONY : cmake_check_build_system + diff --git a/mir/build/libc2mir.a b/mir/build/libc2mir.a new file mode 100644 index 0000000..3e6ba66 Binary files /dev/null and b/mir/build/libc2mir.a differ diff --git a/mir/c2mir/c2mir.c b/mir/c2mir/c2mir.c new file mode 100644 index 0000000..3047e78 --- /dev/null +++ b/mir/c2mir/c2mir.c @@ -0,0 +1,12158 @@ +/* This file is a part of MIR project. + Copyright (C) 2018, 2019 Vladimir Makarov . +*/ + +/* C to MIR compiler. It is a four pass compiler: + o preprocessor pass generating tokens + o parsing pass generating AST + o context pass checking context constraints and augmenting AST + o generation pass producing MIR + + The compiler implements C11 standard w/o C11 optional features: + atomic, complex, variable size arrays. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "time.h" + +#include "c2mir.h" + +#ifdef __x86_64__ +#include "x86_64/cx86_64.h" +#else +#error "undefined or unsupported generation target for C" +#endif + +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 */ + 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 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; +} + +#ifdef __x86_64__ +#include "x86_64/cx86_64-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 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) { + return str1.len == str2.len && memcmp (str1.s, str2.s, str1.len) == 0; +} +static htab_hash_t str_hash (str_t str) { return mir_hash (str.s, str.len, 0x42); } +static int str_key_eq (str_t str1, str_t str2) { return str1.key == str2.key; } +static htab_hash_t str_key_hash (str_t str) { 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); + HTAB_CREATE (str_t, str_key_tab, 200, str_key_hash, str_key_eq); + 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 { + token_code_t code : 16; + 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 (c2m_ctx_t c2m_ctx, node_t n) { + node_t r = new_node (c2m_ctx, n->code); + + r->pos = n->pos; + r->u = n->u; + return r; +} + +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) { + token_t token = new_token (c2m_ctx, t->pos, t->repr, t->code, t->node_code); + + if (t->node != NULL) token->node = copy_node (c2m_ctx, t->node); + 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) { + pos_t pos; + + 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++) { + temp = addr[i]; + addr[i] = addr[j]; + 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': + cs->pos.ln_pos = 0; + if (comment_char < 0) { + nl_p = TRUE; + } else if (comment_char == '/') { + comment_char = -1; + nl_p = TRUE; + } + 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, cs->pos, VARR_ADDR (char, symbol_text), + nl_p ? '\n' : ' ', N_IGNORE); + } + if (header_p && (curr_c == '<' || curr_c == '\"')) { + int i, stop; + + pos = cs->pos; + VARR_TRUNC (char, temp_string, 0); + for (stop = curr_c == '<' ? '>' : '\"';;) { + VARR_PUSH (char, symbol_text, curr_c); + curr_c = cs_get (c2m_ctx); + VARR_PUSH (char, temp_string, curr_c); + if (curr_c == stop || curr_c == '\n' || curr_c == EOF) break; + } + if (curr_c == stop) { + VARR_PUSH (char, symbol_text, curr_c); + VARR_PUSH (char, symbol_text, '\0'); + VARR_POP (char, temp_string); + VARR_PUSH (char, temp_string, '\0'); + return new_node_token (c2m_ctx, pos, VARR_ADDR (char, symbol_text), T_HEADER, + new_str_node (c2m_ctx, N_STR, + uniq_cstr (c2m_ctx, VARR_ADDR (char, temp_string)), + pos)); + } else { + VARR_PUSH (char, symbol_text, curr_c); + for (i = 0; i < VARR_LENGTH (char, symbol_text); i++) + cs_unget (c2m_ctx, VARR_GET (char, symbol_text, i)); + curr_c = (stop == '>' ? '<' : '\"'); + } + } + switch (start_c = curr_c) { + case '\\': + curr_c = cs_get (c2m_ctx); + assert (curr_c != '\n'); + cs_unget (c2m_ctx, curr_c); + return new_token (c2m_ctx, cs->pos, "\\", '\\', N_IGNORE); + case '~': return new_token (c2m_ctx, cs->pos, "~", T_UNOP, N_BITWISE_NOT); + case '+': + case '-': + pos = cs->pos; + curr_c = cs_get (c2m_ctx); + if (curr_c == start_c) { + if (start_c == '+') + return new_token (c2m_ctx, pos, "++", T_INCDEC, N_INC); + else + return new_token (c2m_ctx, pos, "--", T_INCDEC, N_DEC); + } else if (curr_c == '=') { + if (start_c == '+') + return new_token (c2m_ctx, pos, "+=", T_ASSIGN, N_ADD_ASSIGN); + else + return new_token (c2m_ctx, pos, "-=", T_ASSIGN, N_SUB_ASSIGN); + } else if (start_c == '-' && curr_c == '>') { + return new_token (c2m_ctx, pos, "->", T_ARROW, N_DEREF_FIELD); + } else { + cs_unget (c2m_ctx, curr_c); + if (start_c == '+') + return new_token (c2m_ctx, pos, "+", T_ADDOP, N_ADD); + else + return new_token (c2m_ctx, pos, "-", T_ADDOP, N_SUB); + } + assert (FALSE); + case '=': + pos = cs->pos; + curr_c = cs_get (c2m_ctx); + if (curr_c == '=') { + return new_token (c2m_ctx, pos, "==", T_EQNE, N_EQ); + } else { + cs_unget (c2m_ctx, curr_c); + return new_token (c2m_ctx, pos, "=", '=', N_ASSIGN); + } + assert (FALSE); + case '<': + case '>': + pos = cs->pos; + curr_c = cs_get (c2m_ctx); + if (curr_c == start_c) { + curr_c = cs_get (c2m_ctx); + if (curr_c == '=') { + if (start_c == '<') + return new_token (c2m_ctx, pos, "<<=", T_ASSIGN, N_LSH_ASSIGN); + else + return new_token (c2m_ctx, pos, ">>=", T_ASSIGN, N_RSH_ASSIGN); + } else { + cs_unget (c2m_ctx, curr_c); + if (start_c == '<') + return new_token (c2m_ctx, pos, "<<", T_SH, N_LSH); + else + return new_token (c2m_ctx, pos, ">>", T_SH, N_RSH); + } + } else if (curr_c == '=') { + if (start_c == '<') + return new_token (c2m_ctx, pos, "<=", T_CMP, N_LE); + else + return new_token (c2m_ctx, pos, ">=", T_CMP, N_GE); + } else if (start_c == '<' && curr_c == ':') { + return new_token (c2m_ctx, pos, "<:", '[', N_IGNORE); + } else if (start_c == '<' && curr_c == '%') { + return new_token (c2m_ctx, pos, "<%", '{', N_IGNORE); + } else { + cs_unget (c2m_ctx, curr_c); + if (start_c == '<') + return new_token (c2m_ctx, pos, "<", T_CMP, N_LT); + else + return new_token (c2m_ctx, pos, ">", T_CMP, N_GT); + } + assert (FALSE); + case '*': + pos = cs->pos; + curr_c = cs_get (c2m_ctx); + if (curr_c == '=') { + return new_token (c2m_ctx, pos, "*=", T_ASSIGN, N_MUL_ASSIGN); + } else { + cs_unget (c2m_ctx, curr_c); + return new_token (c2m_ctx, pos, "*", '*', N_MUL); + } + assert (FALSE); + case '/': + pos = cs->pos; + curr_c = cs_get (c2m_ctx); + assert (curr_c != '/' && curr_c != '*'); + if (curr_c == '=') return new_token (c2m_ctx, pos, "/=", T_ASSIGN, N_DIV_ASSIGN); + assert (curr_c != '*' && curr_c != '/'); /* we already processed comments */ + cs_unget (c2m_ctx, curr_c); + return new_token (c2m_ctx, pos, "/", T_DIVOP, N_DIV); + case '%': + pos = cs->pos; + curr_c = cs_get (c2m_ctx); + if (curr_c == '=') { + return new_token (c2m_ctx, pos, "%=", T_ASSIGN, N_MOD_ASSIGN); + } else if (curr_c == '>') { + return new_token (c2m_ctx, pos, "%>", '}', N_IGNORE); + } else if (curr_c == ':') { + curr_c = cs_get (c2m_ctx); + if (curr_c != '%') { + cs_unget (c2m_ctx, curr_c); + return new_token (c2m_ctx, pos, "%:", '#', N_IGNORE); + } else { + curr_c = cs_get (c2m_ctx); + if (curr_c == ':') + return new_token (c2m_ctx, pos, "%:%:", T_DBLNO, N_IGNORE); + else { + cs_unget (c2m_ctx, '%'); + cs_unget (c2m_ctx, curr_c); + return new_token (c2m_ctx, pos, "%:", '#', N_IGNORE); + } + } + } else { + cs_unget (c2m_ctx, curr_c); + return new_token (c2m_ctx, pos, "%", T_DIVOP, N_MOD); + } + assert (FALSE); + case '&': + case '|': + pos = cs->pos; + curr_c = cs_get (c2m_ctx); + if (curr_c == '=') { + if (start_c == '&') + return new_token (c2m_ctx, pos, "&=", T_ASSIGN, N_AND_ASSIGN); + else + return new_token (c2m_ctx, pos, "|=", T_ASSIGN, N_OR_ASSIGN); + } else if (curr_c == start_c) { + if (start_c == '&') + return new_token (c2m_ctx, pos, "&&", T_ANDAND, N_ANDAND); + else + return new_token (c2m_ctx, pos, "||", T_OROR, N_OROR); + } else { + cs_unget (c2m_ctx, curr_c); + if (start_c == '&') + return new_token (c2m_ctx, pos, "&", start_c, N_AND); + else + return new_token (c2m_ctx, pos, "|", start_c, N_OR); + } + assert (FALSE); + case '^': + case '!': + pos = cs->pos; + curr_c = cs_get (c2m_ctx); + if (curr_c == '=') { + if (start_c == '^') + return new_token (c2m_ctx, pos, "^=", T_ASSIGN, N_XOR_ASSIGN); + else + return new_token (c2m_ctx, pos, "!=", T_EQNE, N_NE); + } else { + cs_unget (c2m_ctx, curr_c); + if (start_c == '^') + return new_token (c2m_ctx, pos, "^", '^', N_XOR); + else + return new_token (c2m_ctx, pos, "!", T_UNOP, N_NOT); + } + assert (FALSE); + case ';': return new_token (c2m_ctx, cs->pos, ";", curr_c, N_IGNORE); + case '?': return new_token (c2m_ctx, cs->pos, "?", curr_c, N_IGNORE); + case '(': return new_token (c2m_ctx, cs->pos, "(", curr_c, N_IGNORE); + case ')': return new_token (c2m_ctx, cs->pos, ")", curr_c, N_IGNORE); + case '{': return new_token (c2m_ctx, cs->pos, "{", curr_c, N_IGNORE); + case '}': return new_token (c2m_ctx, cs->pos, "}", curr_c, N_IGNORE); + case ']': return new_token (c2m_ctx, cs->pos, "]", curr_c, N_IGNORE); + case EOF: { + pos_t pos = cs->pos; + + assert (eof_s != cs); + if (eof_s != NULL) free_stream (eof_s); + if (cs->f != stdin && cs->f != NULL) { + fclose (cs->f); + cs->f = NULL; + } + eof_s = VARR_POP (stream_t, streams); + if (VARR_LENGTH (stream_t, streams) == 0) { + return new_token (c2m_ctx, pos, "", T_EOU, N_IGNORE); + } + cs = VARR_LAST (stream_t, streams); + if (cs->f == NULL && cs->fname != NULL && !string_stream_p (cs)) { + if ((cs->f = fopen (cs->fname, "r")) == NULL) { + if (options->message_file != NULL) + fprintf (options->message_file, "cannot reopen file %s -- good bye\n", cs->fname); + longjmp (c2m_ctx->env, 1); // ??? + } + fsetpos (cs->f, &cs->fpos); + } + return new_token (c2m_ctx, cs->pos, "", T_EOFILE, N_IGNORE); + } + case ':': + curr_c = cs_get (c2m_ctx); + if (curr_c == '>') { + return new_token (c2m_ctx, cs->pos, ":>", ']', N_IGNORE); + } else { + cs_unget (c2m_ctx, curr_c); + return new_token (c2m_ctx, cs->pos, ":", ':', N_IGNORE); + } + case '#': + curr_c = cs_get (c2m_ctx); + if (curr_c == '#') { + return new_token (c2m_ctx, cs->pos, "##", T_DBLNO, N_IGNORE); + } else { + cs_unget (c2m_ctx, curr_c); + return new_token (c2m_ctx, cs->pos, "#", '#', N_IGNORE); + } + case ',': return new_token (c2m_ctx, cs->pos, ",", ',', N_COMMA); + case '[': return new_token (c2m_ctx, cs->pos, "[", '[', N_IND); + case '.': + pos = cs->pos; + curr_c = cs_get (c2m_ctx); + if (curr_c == '.') { + curr_c = cs_get (c2m_ctx); + if (curr_c == '.') { + return new_token (c2m_ctx, pos, "...", T_DOTS, N_IGNORE); + } else { + cs_unget (c2m_ctx, '.'); + cs_unget (c2m_ctx, curr_c); + return new_token (c2m_ctx, pos, ".", '.', N_FIELD); + } + } else if (!isdigit (curr_c)) { + cs_unget (c2m_ctx, curr_c); + return new_token (c2m_ctx, pos, ".", '.', N_FIELD); + } + cs_unget (c2m_ctx, curr_c); + curr_c = '.'; + /* Fall through: */ + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': { + pos = cs->pos; + VARR_TRUNC (char, symbol_text, 0); + for (;;) { + VARR_PUSH (char, symbol_text, curr_c); + curr_c = cs_get (c2m_ctx); + if (curr_c == 'e' || curr_c == 'E' || curr_c == 'p' || curr_c == 'P') { + int c = cs_get (c2m_ctx); + + if (c == '+' || c == '-') { + VARR_PUSH (char, symbol_text, curr_c); + curr_c = c; + } else { + cs_unget (c2m_ctx, c); + } + } else if (!isdigit (curr_c) && !isalpha (curr_c) && curr_c != '_' && curr_c != '.') + break; + } + VARR_PUSH (char, symbol_text, '\0'); + cs_unget (c2m_ctx, curr_c); + return new_token_wo_uniq_repr (c2m_ctx, pos, VARR_ADDR (char, symbol_text), T_NUMBER, + N_IGNORE); + } + case '\'': + case '\"': { /* ??? unicode and wchar */ + token_t t; + int stop = curr_c; + + pos = cs->pos; + VARR_PUSH (char, symbol_text, curr_c); + for (curr_c = cs_get (c2m_ctx); curr_c != stop && curr_c != '\n' && curr_c != EOF; + curr_c = cs_get (c2m_ctx)) { + VARR_PUSH (char, symbol_text, curr_c); + if (curr_c != '\\') continue; + curr_c = cs_get (c2m_ctx); + if (curr_c == '\n' || curr_c == EOF) break; + VARR_PUSH (char, symbol_text, curr_c); + } + VARR_PUSH (char, symbol_text, curr_c); + if (curr_c == stop) { + if (stop == '\'' && VARR_LENGTH (char, symbol_text) == 1) + error (c2m_ctx, pos, "empty character"); + } else { + error (c2m_ctx, pos, "unterminated %s", stop == '"' ? "string" : "char"); + VARR_PUSH (char, symbol_text, stop); + } + VARR_PUSH (char, symbol_text, '\0'); + t = (stop == '\"' ? new_node_token (c2m_ctx, pos, VARR_ADDR (char, symbol_text), T_STR, + new_str_node (c2m_ctx, N_STR, empty_str, pos)) + : new_node_token (c2m_ctx, pos, VARR_ADDR (char, symbol_text), T_CH, + new_ch_node (c2m_ctx, ' ', pos))); + set_string_val (c2m_ctx, t, symbol_text); + return t; + } + default: + if (isalpha (curr_c) || curr_c == '_') { + pos = cs->pos; + do { + VARR_PUSH (char, symbol_text, curr_c); + curr_c = cs_get (c2m_ctx); + } while (isalnum (curr_c) || curr_c == '_'); + cs_unget (c2m_ctx, curr_c); + VARR_PUSH (char, symbol_text, '\0'); + return new_id_token (c2m_ctx, pos, VARR_ADDR (char, symbol_text)); + } else { + VARR_PUSH (char, symbol_text, curr_c); + VARR_PUSH (char, symbol_text, '\0'); + return new_token_wo_uniq_repr (c2m_ctx, pos, VARR_ADDR (char, symbol_text), curr_c, + N_IGNORE); + } + } + } +} + +static token_t get_next_pptoken (c2m_ctx_t c2m_ctx) { return get_next_pptoken_1 (c2m_ctx, FALSE); } + +static token_t get_next_include_pptoken (c2m_ctx_t c2m_ctx) { + return get_next_pptoken_1 (c2m_ctx, TRUE); +} + +#ifdef C2MIR_PREPRO_DEBUG +static const char *get_token_str (token_t t) { + switch (t->code) { + case T_EOFILE: return "EOF"; + case T_DBLNO: return "DBLNO"; + case T_PLM: return "PLM"; + case T_RDBLNO: return "RDBLNO"; + case T_BOA: return "BOA"; + case T_EOA: return "EOA"; + case T_EOR: return "EOR"; + case T_EOP: return "EOP"; + case T_EOU: return "EOU"; + default: return t->repr; + } +} +#endif + +static void unget_next_pptoken (c2m_ctx_t c2m_ctx, token_t t) { + VARR_PUSH (token_t, buffered_tokens, t); +} + +static const char *stringify (const char *str, VARR (char) * to) { + VARR_TRUNC (char, to, 0); + VARR_PUSH (char, to, '"'); + for (; *str != '\0'; str++) { + if (*str == '\"' || *str == '\\') VARR_PUSH (char, to, '\\'); + VARR_PUSH (char, to, *str); + } + VARR_PUSH (char, to, '"'); + return VARR_ADDR (char, to); +} + +static void destringify (const char *repr, VARR (char) * to) { + int i, repr_len = strlen (repr); + + VARR_TRUNC (char, to, 0); + if (repr_len == 0) return; + i = repr[0] == '"' ? 1 : 0; + if (i == 1 && repr_len == 1) return; + if (repr[repr_len - 1] == '"') repr_len--; + for (; i < repr_len; i++) + if (repr[i] != '\\' || i + 1 >= repr_len || (repr[i + 1] != '\\' && repr[i + 1] != '"')) + VARR_PUSH (char, to, repr[i]); +} + +/* TS - vector, T defines position for empty vector */ +static token_t token_stringify (c2m_ctx_t c2m_ctx, token_t t, VARR (token_t) * ts) { + int i; + + if (VARR_LENGTH (token_t, ts) != 0) t = VARR_GET (token_t, ts, 0); + t = new_node_token (c2m_ctx, t->pos, "", T_STR, new_str_node (c2m_ctx, N_STR, empty_str, t->pos)); + VARR_TRUNC (char, temp_string, 0); + for (const char *s = t->repr; *s != 0; s++) VARR_PUSH (char, temp_string, *s); + VARR_PUSH (char, temp_string, '"'); + for (i = 0; i < VARR_LENGTH (token_t, ts); i++) + if (VARR_GET (token_t, ts, i)->code == ' ' || VARR_GET (token_t, ts, i)->code == '\n') { + VARR_PUSH (char, temp_string, ' '); + } else { + for (const char *s = VARR_GET (token_t, ts, i)->repr; *s != 0; s++) { + int c = VARR_LENGTH (token_t, ts) == i + 1 ? '\0' : VARR_GET (token_t, ts, i + 1)->repr[0]; + + /* It is an implementation defined behaviour analogous GCC/Clang (see set_string_val): */ + if (*s == '\"' + || (*s == '\\' && c != '\\' && c != 'a' && c != 'b' && c != 'f' && c != 'n' && c != 'r' + && c != 'v' && c != 't' && c != '?' && c != 'e' && !('0' <= c && c <= '7') + && c != 'x' && c != 'X')) + VARR_PUSH (char, temp_string, '\\'); + VARR_PUSH (char, temp_string, *s); + } + } + VARR_PUSH (char, temp_string, '"'); + VARR_PUSH (char, temp_string, '\0'); + t->repr = uniq_cstr (c2m_ctx, VARR_ADDR (char, temp_string)).s; + set_string_val (c2m_ctx, t, temp_string); + return t; +} + +static node_t get_int_node_from_repr (c2m_ctx_t c2m_ctx, const char *repr, char **stop, int base, + int uns_p, int long_p, int llong_p, pos_t pos) { + mir_ullong ull = strtoull (repr, stop, base); + + if (llong_p) { + if (!uns_p && (base == 10 || ull <= MIR_LLONG_MAX)) return new_ll_node (c2m_ctx, ull, pos); + return new_ull_node (c2m_ctx, ull, pos); + } + if (long_p) { + if (!uns_p && ull <= MIR_LONG_MAX) return new_l_node (c2m_ctx, ull, pos); + if (ull <= MIR_ULONG_MAX) return new_ul_node (c2m_ctx, ull, pos); + if (!uns_p && (base == 10 || ull <= MIR_LLONG_MAX)) return new_ll_node (c2m_ctx, ull, pos); + return new_ull_node (c2m_ctx, ull, pos); + } + if (uns_p) { + if (ull <= MIR_UINT_MAX) return new_u_node (c2m_ctx, ull, pos); + if (ull <= MIR_ULONG_MAX) return new_ul_node (c2m_ctx, ull, pos); + return new_ull_node (c2m_ctx, ull, pos); + } + if (ull <= MIR_INT_MAX) return new_i_node (c2m_ctx, ull, pos); + if (base != 10 && ull <= MIR_UINT_MAX) return new_u_node (c2m_ctx, ull, pos); + if (ull <= MIR_LONG_MAX) return new_l_node (c2m_ctx, ull, pos); + if (ull <= MIR_ULONG_MAX) return new_ul_node (c2m_ctx, ull, pos); + if (base == 10 || ull <= MIR_LLONG_MAX) return new_ll_node (c2m_ctx, ull, pos); + return new_ull_node (c2m_ctx, ull, pos); +} + +static token_t pptoken2token (c2m_ctx_t c2m_ctx, token_t t, int id2kw_p) { + assert (t->code != T_HEADER && t->code != T_BOA && t->code != T_EOA && t->code != T_EOR + && t->code != T_EOP && t->code != T_EOFILE && t->code != T_EOU && t->code != T_PLM + && t->code != T_RDBLNO); + if (t->code == T_NO_MACRO_IDENT) t->code = T_ID; + if (t->code == T_ID && id2kw_p) { + str_t str = str_add (c2m_ctx, t->repr, strlen (t->repr) + 1, T_STR, 0, FALSE); + + if (str.key != T_STR) { + t->code = str.key; + t->node_code = N_IGNORE; + t->node = NULL; + } + return t; + } else if (t->code == ' ' || t->code == '\n') { + return NULL; + } else if (t->code == T_NUMBER) { + int i, base = 10, float_p = FALSE, double_p = FALSE, ldouble_p = FALSE; + int uns_p = FALSE, long_p = FALSE, llong_p = FALSE; + const char *repr = t->repr, *start = t->repr; + char *stop; + int last = strlen (repr) - 1; + + assert (last >= 0); + if (repr[0] == '0' && (repr[1] == 'x' || repr[1] == 'X')) { + base = 16; + } else if (repr[0] == '0' && (repr[1] == 'b' || repr[1] == 'B')) { + (options->pedantic_p ? error : warning) (c2m_ctx, t->pos, + "binary number is not a standard: %s", t->repr); + base = 2; + start += 2; + } else if (repr[0] == '0') { + base = 8; + } + for (i = 0; i <= last; i++) { + if (repr[i] == '.') { + double_p = TRUE; + } else if (repr[i] == 'p' || repr[i] == 'P') { + double_p = TRUE; + } else if ((repr[i] == 'e' || repr[i] == 'E') && base != 16) { + double_p = TRUE; + } + } + if (last >= 2 + && (strcmp (&repr[last - 2], "LLU") == 0 || strcmp (&repr[last - 2], "ULL") == 0 + || strcmp (&repr[last - 2], "llu") == 0 || strcmp (&repr[last - 2], "ull") == 0 + || strcmp (&repr[last - 2], "LLu") == 0 || strcmp (&repr[last - 2], "uLL") == 0 + || strcmp (&repr[last - 2], "llU") == 0 || strcmp (&repr[last - 2], "Ull") == 0)) { + llong_p = uns_p = TRUE; + last -= 3; + } else if (last >= 1 + && (strcmp (&repr[last - 1], "LL") == 0 || strcmp (&repr[last - 1], "ll") == 0)) { + llong_p = TRUE; + last -= 2; + } else if (last >= 1 + && (strcmp (&repr[last - 1], "LU") == 0 || strcmp (&repr[last - 1], "UL") == 0 + || strcmp (&repr[last - 1], "lu") == 0 || strcmp (&repr[last - 1], "ul") == 0 + || strcmp (&repr[last - 1], "Lu") == 0 || strcmp (&repr[last - 1], "uL") == 0 + || strcmp (&repr[last - 1], "lU") == 0 || strcmp (&repr[last - 1], "Ul") == 0)) { + long_p = uns_p = TRUE; + last -= 2; + } else if (strcmp (&repr[last], "L") == 0 || strcmp (&repr[last], "l") == 0) { + long_p = TRUE; + last--; + } else if (strcmp (&repr[last], "U") == 0 || strcmp (&repr[last], "u") == 0) { + uns_p = TRUE; + last--; + } else if (double_p && (strcmp (&repr[last], "F") == 0 || strcmp (&repr[last], "f") == 0)) { + float_p = TRUE; + double_p = FALSE; + last--; + } + if (double_p) { + if (uns_p || llong_p) { + error (c2m_ctx, t->pos, "wrong number: %s", repr); + } else if (long_p) { + ldouble_p = TRUE; + double_p = FALSE; + } + } + errno = 0; + if (float_p) { + t->node = new_f_node (c2m_ctx, strtof (start, &stop), t->pos); + } else if (double_p) { + t->node = new_d_node (c2m_ctx, strtod (start, &stop), t->pos); + } else if (ldouble_p) { + t->node = new_ld_node (c2m_ctx, strtold (start, &stop), t->pos); + } else { + t->node + = get_int_node_from_repr (c2m_ctx, start, &stop, base, uns_p, long_p, llong_p, t->pos); + } + if (stop != &repr[last + 1]) { + if (options->message_file != NULL) + fprintf (options->message_file, "%s:%s:%s\n", repr, stop, &repr[last + 1]); + error (c2m_ctx, t->pos, "wrong number: %s", t->repr); + } else if (errno) { + (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; + /* 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 should_be_pre_pos, 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 should_be_pre_pos c2m_ctx->pre_ctx->should_be_pre_pos +#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) { + return macro1->id->repr == macro2->id->repr; +} + +static htab_hash_t macro_hash (macro_t macro) { + 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_CREATE (macro_t, macros, 2048); + HTAB_CREATE (macro_t, macro_tab, 2048, macro_hash, macro_eq); + /* 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__"); +} + +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) { + macro_call_t mc = malloc (sizeof (struct macro_call)); + + mc->macro = m; + 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) { + FILE *f; + + assert (fname != NULL); + if ((f = fopen (fname, "r")) == NULL) { + if (options->message_file != NULL) fprintf (f, "error in opening file %s\n", 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); + } +} + +#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) { +#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))); + } +#ifdef C2MIR_PREPRO_DEBUG + fprintf (stderr, "\n"); + print_output_buffer (); +#endif +} + +static int file_found_p (const char *name) { + FILE *f; + + if ((f = fopen (name, "r")) == NULL) return FALSE; + fclose (f); + return TRUE; +} + +static const char *get_full_name (c2m_ctx_t c2m_ctx, const char *base, const char *name, + int dir_base_p) { + const char *str, *last; + size_t len; + + VARR_TRUNC (char, temp_string, 0); + if (base == NULL || *base == '\0') { + assert (name != NULL && name[0] != '\0'); + return name; + } + if (dir_base_p) { + len = strlen (base); + assert (len > 0); + add_to_temp_string (c2m_ctx, base); + if (base[len - 1] != '/') add_to_temp_string (c2m_ctx, "/"); + } else if ((last = strrchr (base, '/')) == NULL) { + add_to_temp_string (c2m_ctx, "./"); + } else { + for (str = base; str <= last; str++) VARR_PUSH (char, temp_string, *str); + VARR_PUSH (char, temp_string, '\0'); + } + add_to_temp_string (c2m_ctx, name); + return VARR_ADDR (char, temp_string); +} + +static const char *get_include_fname (c2m_ctx_t c2m_ctx, token_t t) { + const char *fullname, *name; + + assert (t->code == T_STR || t->code == T_HEADER); + if ((name = t->node->u.s.s)[0] != '/') { + if (t->repr[0] == '"') { + /* Search relative to the current source dir */ + if (cs->fname != NULL) { + fullname = get_full_name (c2m_ctx, cs->fname, name, FALSE); + if (file_found_p (fullname)) return uniq_cstr (c2m_ctx, fullname).s; + } + for (size_t i = 0; header_dirs[i] != NULL; i++) { + fullname = get_full_name (c2m_ctx, header_dirs[i], name, TRUE); + if (file_found_p (fullname)) return uniq_cstr (c2m_ctx, fullname).s; + } + } + for (size_t i = 0; system_header_dirs[i] != NULL; i++) { + fullname = get_full_name (c2m_ctx, system_header_dirs[i], name, TRUE); + if (file_found_p (fullname)) return uniq_cstr (c2m_ctx, fullname).s; + } + } + return name; +} + +static int digits_p (const char *str) { + while ('0' <= *str && *str <= '9') str++; + return *str == '\0'; +} + +static pos_t check_line_directive_args (c2m_ctx_t c2m_ctx, VARR (token_t) * buffer) { + size_t i, len = VARR_LENGTH (token_t, buffer); + token_t *buffer_arr = VARR_ADDR (token_t, buffer); + const char *fname; + pos_t pos; + int lno; + unsigned long long l; + + if (len == 0) return no_pos; + i = buffer_arr[0]->code == ' ' ? 1 : 0; + fname = buffer_arr[i]->pos.fname; + if (i >= len || buffer_arr[i]->code != T_NUMBER) return no_pos; + if (!digits_p (buffer_arr[i]->repr)) return no_pos; + errno = 0; + lno = l = strtoll (buffer_arr[i]->repr, NULL, 10); + if (errno || l > ((1ul << 31) - 1)) + error (c2m_ctx, buffer_arr[i]->pos, "#line with too big value: %s", buffer_arr[i]->repr); + i++; + if (i < len && buffer_arr[i]->code == ' ') i++; + if (i < len && buffer_arr[i]->code == T_STR) { + fname = buffer_arr[i]->node->u.s.s; + i++; + } + if (i == len) { + pos.fname = fname; + pos.lno = lno; + pos.ln_pos = 0; + return pos; + } + return no_pos; +} + +static void check_pragma (c2m_ctx_t c2m_ctx, token_t t, VARR (token_t) * tokens) { + token_t *tokens_arr = VARR_ADDR (token_t, tokens); + size_t i, tokens_len = VARR_LENGTH (token_t, tokens); + + i = 0; + if (i < tokens_len && tokens_arr[i]->code == ' ') i++; + if (i >= tokens_len || tokens_arr[i]->code != T_ID || strcmp (tokens_arr[i]->repr, "STDC") != 0) { + warning (c2m_ctx, t->pos, "unknown pragma"); + return; + } + i++; + if (i < tokens_len && tokens_arr[i]->code == ' ') i++; + if (i >= tokens_len || tokens_arr[i]->code != T_ID) { + error (c2m_ctx, t->pos, "wrong STDC pragma"); + return; + } + if (strcmp (tokens_arr[i]->repr, "FP_CONTRACT") != 0 + && strcmp (tokens_arr[i]->repr, "FENV_ACCESS") != 0 + && strcmp (tokens_arr[i]->repr, "CX_LIMITED_RANGE") != 0) { + error (c2m_ctx, t->pos, "unknown STDC pragma %s", tokens_arr[i]->repr); + return; + } + i++; + if (i < tokens_len && tokens_arr[i]->code == ' ') i++; + if (i >= tokens_len || tokens_arr[i]->code != T_ID) { + error (c2m_ctx, t->pos, "wrong STDC pragma value"); + return; + } + if (strcmp (tokens_arr[i]->repr, "ON") != 0 && strcmp (tokens_arr[i]->repr, "OFF") != 0 + && strcmp (tokens_arr[i]->repr, "DEFAULT") != 0) { + error (c2m_ctx, t->pos, "unknown STDC pragma value", tokens_arr[i]->repr); + return; + } + i++; + if (i < tokens_len && (tokens_arr[i]->code == ' ' || tokens_arr[i]->code == '\n')) i++; + if (i < tokens_len) error (c2m_ctx, t->pos, "garbage at STDC pragma end"); +} + +static void pop_macro_call (c2m_ctx_t c2m_ctx) { + macro_call_t mc; + + mc = VARR_POP (macro_call_t, macro_call_stack); +#ifdef C2MIR_PREPRO_DEBUG + fprintf (stderr, "finish macro %s\n", mc->macro->id->repr); +#endif + mc->macro->ignore_p = FALSE; + free_macro_call (mc); +} + +static void find_args (c2m_ctx_t c2m_ctx, macro_call_t mc) { /* we have just read a parenthesis */ + macro_t m; + token_t t; + int va_p, level = 0; + size_t params_len; + VARR (token_arr_t) * args; + VARR (token_t) * arg, *temp_arr; + + m = mc->macro; + VARR_CREATE (token_arr_t, args, 16); + VARR_CREATE (token_t, arg, 16); + params_len = VARR_LENGTH (token_t, m->params); + va_p = params_len == 1 && VARR_GET (token_t, m->params, 0)->code == T_DOTS; +#ifdef C2MIR_PREPRO_DEBUG + fprintf (stderr, "# finding args of macro %s call:\n# arg 0:", m->id->repr); +#endif + for (;;) { + t = get_next_pptoken (c2m_ctx); +#ifdef C2MIR_PREPRO_DEBUG + fprintf (stderr, " <%s>%s", get_token_str (t), t->processed_p ? "*" : ""); +#endif + if (t->code == T_EOR) { + t = get_next_pptoken (c2m_ctx); +#ifdef C2MIR_PREPRO_DEBUG + fprintf (stderr, " <%s>", get_token_str (t), t->processed_p ? "*" : ""); +#endif + pop_macro_call (c2m_ctx); + } + if (t->code == T_EOFILE || t->code == T_EOU || t->code == T_EOR || t->code == T_BOA + || t->code == T_EOA) + break; + if (level == 0 && t->code == ')') break; + if (level == 0 && !va_p && t->code == ',') { + VARR_PUSH (token_arr_t, args, arg); +#ifdef C2MIR_PREPRO_DEBUG + fprintf (stderr, "\n# arg %d:", VARR_LENGTH (token_arr_t, args)); +#endif + VARR_CREATE (token_t, arg, 16); + if (VARR_LENGTH (token_arr_t, args) == params_len - 1 + && strcmp (VARR_GET (token_t, m->params, params_len - 1)->repr, "...") == 0) + va_p = 1; + } else { + VARR_PUSH (token_t, arg, t); + if (t->code == ')') + level--; + else if (t->code == '(') + level++; + } + } +#ifdef C2MIR_PREPRO_DEBUG + fprintf (stderr, "\n"); +#endif + if (t->code != ')') error (c2m_ctx, t->pos, "unfinished call of macro %s", m->id->repr); + VARR_PUSH (token_arr_t, args, arg); + if (params_len == 0 && VARR_LENGTH (token_arr_t, args) == 1) { + token_arr_t arr = VARR_GET (token_arr_t, args, 0); + + if (VARR_LENGTH (token_t, arr) == 0 + || (VARR_LENGTH (token_t, arr) == 1 && VARR_GET (token_t, arr, 0)->code == ' ')) { + temp_arr = VARR_POP (token_arr_t, args); + VARR_DESTROY (token_t, temp_arr); + mc->args = args; + return; + } + } + if (VARR_LENGTH (token_arr_t, args) > params_len) { + t = VARR_GET (token_t, VARR_GET (token_arr_t, args, params_len), 0); + while (VARR_LENGTH (token_arr_t, args) > params_len) { + temp_arr = VARR_POP (token_arr_t, args); + VARR_DESTROY (token_t, temp_arr); + } + error (c2m_ctx, t->pos, "too many args for call of macro %s", m->id->repr); + } else if (VARR_LENGTH (token_arr_t, args) < params_len) { + for (; VARR_LENGTH (token_arr_t, args) < params_len;) { + VARR_CREATE (token_t, arg, 16); + VARR_PUSH (token_arr_t, args, arg); + } + error (c2m_ctx, t->pos, "not enough args for call of macro %s", m->id->repr); + } + mc->args = args; +} + +static token_t token_concat (c2m_ctx_t c2m_ctx, token_t t1, token_t t2) { + token_t t, next; + + VARR_TRUNC (char, temp_string, 0); + add_to_temp_string (c2m_ctx, t1->repr); + add_to_temp_string (c2m_ctx, t2->repr); + reverse (temp_string); + set_string_stream (c2m_ctx, VARR_ADDR (char, temp_string), t1->pos, NULL); + t = get_next_pptoken (c2m_ctx); + next = get_next_pptoken (c2m_ctx); + if (next->code != T_EOFILE) { + error (c2m_ctx, t1->pos, "wrong result of ##: %s", reverse (temp_string)); + remove_string_stream (c2m_ctx); + } + return t; +} + +static void add_token (VARR (token_t) * to, token_t t) { + if ((t->code != ' ' && t->code != '\n') || VARR_LENGTH (token_t, to) == 0 + || (VARR_LAST (token_t, to)->code != ' ' && VARR_LAST (token_t, to)->code != '\n')) + VARR_PUSH (token_t, to, t); +} + +static void add_arg_tokens (VARR (token_t) * to, VARR (token_t) * from) { + int start; + + for (start = VARR_LENGTH (token_t, from) - 1; start >= 0; start--) + if (VARR_GET (token_t, from, start)->code == T_BOA) break; + assert (start >= 0); + for (size_t i = start + 1; i < VARR_LENGTH (token_t, from); i++) + add_token (to, VARR_GET (token_t, from, i)); + VARR_TRUNC (token_t, from, start); +} + +static void add_tokens (VARR (token_t) * to, VARR (token_t) * from) { + for (size_t i = 0; i < VARR_LENGTH (token_t, from); i++) + add_token (to, VARR_GET (token_t, from, i)); +} + +static void del_tokens (VARR (token_t) * tokens, int from, int len) { + int diff, tokens_len = VARR_LENGTH (token_t, tokens); + token_t *addr = VARR_ADDR (token_t, tokens); + + if (len < 0) len = tokens_len - from; + assert (from + len <= tokens_len); + if ((diff = tokens_len - from - len) > 0) + memmove (addr + from, addr + from + len, diff * sizeof (token_t)); + VARR_TRUNC (token_t, tokens, tokens_len - len); +} + +static VARR (token_t) * do_concat (c2m_ctx_t c2m_ctx, VARR (token_t) * tokens) { + int i, j, k, empty_j_p, empty_k_p, len = VARR_LENGTH (token_t, tokens); + token_t t; + + for (i = len - 1; i >= 0; i--) + if ((t = VARR_GET (token_t, tokens, i))->code == T_RDBLNO) { + j = i + 1; + k = i - 1; + assert (k >= 0 && j < len); + if (VARR_GET (token_t, tokens, j)->code == ' ' || VARR_GET (token_t, tokens, j)->code == '\n') + j++; + if (VARR_GET (token_t, tokens, k)->code == ' ' || VARR_GET (token_t, tokens, k)->code == '\n') + k--; + assert (k >= 0 && j < len); + empty_j_p = VARR_GET (token_t, tokens, j)->code == T_PLM; + empty_k_p = VARR_GET (token_t, tokens, k)->code == T_PLM; + if (empty_j_p || empty_k_p) { + if (!empty_j_p) + j--; + else if (j + 1 < len + && (VARR_GET (token_t, tokens, j + 1)->code == ' ' + || VARR_GET (token_t, tokens, j + 1)->code == '\n')) + j++; + if (!empty_k_p) + k++; + else if (k != 0 + && (VARR_GET (token_t, tokens, k - 1)->code == ' ' + || VARR_GET (token_t, tokens, k - 1)->code == '\n')) + k--; + if (!empty_j_p || !empty_k_p) { + del_tokens (tokens, k, j - k + 1); + } else { + del_tokens (tokens, k, j - k); + t = new_token (c2m_ctx, t->pos, "", ' ', N_IGNORE); + VARR_SET (token_t, tokens, k, t); + } + } else { + t = token_concat (c2m_ctx, VARR_GET (token_t, tokens, k), VARR_GET (token_t, tokens, j)); + del_tokens (tokens, k + 1, j - k); + VARR_SET (token_t, tokens, k, t); + } + i = k; + len = VARR_LENGTH (token_t, tokens); + } + for (i = len - 1; i >= 0; i--) VARR_GET (token_t, tokens, i)->processed_p = TRUE; + return tokens; +} + +static void process_replacement (c2m_ctx_t c2m_ctx, macro_call_t mc) { + macro_t m; + token_t t, *m_repl; + VARR (token_t) * arg; + int i, m_repl_len, sharp_pos, copy_p; + + m = mc->macro; + sharp_pos = -1; + m_repl = VARR_ADDR (token_t, m->replacement); + m_repl_len = VARR_LENGTH (token_t, m->replacement); + for (;;) { + if (mc->repl_pos >= m_repl_len) { + t = get_next_pptoken (c2m_ctx); + unget_next_pptoken (c2m_ctx, t); +#ifdef C2MIR_PREPRO_DEBUG + fprintf (stderr, "# push back <%s>\n", get_token_str (t)); +#endif + unget_next_pptoken (c2m_ctx, new_token (c2m_ctx, t->pos, "", T_EOR, N_IGNORE)); +#ifdef C2MIR_PREPRO_DEBUG + fprintf (stderr, "# push back : mc=%lx\n", mc); +#endif + push_back (c2m_ctx, do_concat (c2m_ctx, mc->repl_buffer)); + m->ignore_p = TRUE; + return; + } + t = m_repl[mc->repl_pos++]; + copy_p = TRUE; + if (t->code == T_ID) { + i = find_param (m->params, t->repr); + if (i >= 0) { + arg = VARR_GET (token_arr_t, mc->args, i); + if (sharp_pos >= 0) { + del_tokens (mc->repl_buffer, sharp_pos, -1); + if (VARR_LENGTH (token_t, arg) != 0 + && (VARR_GET (token_t, arg, 0)->code == ' ' + || VARR_GET (token_t, arg, 0)->code == '\n')) + del_tokens (arg, 0, 1); + if (VARR_LENGTH (token_t, arg) != 0 + && (VARR_LAST (token_t, arg)->code == ' ' || VARR_LAST (token_t, arg)->code == '\n')) + VARR_POP (token_t, arg); + t = token_stringify (c2m_ctx, mc->macro->id, arg); + copy_p = FALSE; + } else if ((mc->repl_pos >= 2 && m_repl[mc->repl_pos - 2]->code == T_RDBLNO) + || (mc->repl_pos >= 3 && m_repl[mc->repl_pos - 2]->code == ' ' + && m_repl[mc->repl_pos - 3]->code == T_RDBLNO) + || (mc->repl_pos < m_repl_len && m_repl[mc->repl_pos]->code == T_RDBLNO) + || (mc->repl_pos + 1 < m_repl_len && m_repl[mc->repl_pos + 1]->code == T_RDBLNO + && m_repl[mc->repl_pos]->code == ' ')) { + if (VARR_LENGTH (token_t, arg) == 0 + || (VARR_LENGTH (token_t, arg) == 1 + && (VARR_GET (token_t, arg, 0)->code == ' ' + || VARR_GET (token_t, arg, 0)->code == '\n'))) { + t = new_token (c2m_ctx, t->pos, "", T_PLM, N_IGNORE); + copy_p = FALSE; + } else { + add_tokens (mc->repl_buffer, arg); + continue; + } + } else { + unget_next_pptoken (c2m_ctx, new_token (c2m_ctx, t->pos, "", T_EOA, N_IGNORE)); +#ifdef C2MIR_PREPRO_DEBUG + fprintf (stderr, "# push back for macro %s call\n", mc->macro->id->repr); +#endif + copy_and_push_back (c2m_ctx, arg); + unget_next_pptoken (c2m_ctx, new_token (c2m_ctx, t->pos, "", T_BOA, N_IGNORE)); +#ifdef C2MIR_PREPRO_DEBUG + fprintf (stderr, "# push back for macro %s call\n", mc->macro->id->repr); +#endif + return; + } + } + } else if (t->code == '#') { + sharp_pos = VARR_LENGTH (token_t, mc->repl_buffer); + } else if (t->code != ' ') { + sharp_pos = -1; + } + if (copy_p) t = copy_token (c2m_ctx, t); + 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 void process_directive (c2m_ctx_t c2m_ctx) { + token_t t, t1; + int i, 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); + transform_to_header (c2m_ctx, temp_buffer); + i = 0; + if (VARR_LENGTH (token_t, temp_buffer) != 0 + && VARR_GET (token_t, temp_buffer, 0)->code == ' ') + i++; + if (i != VARR_LENGTH (token_t, temp_buffer) - 1 + || (VARR_GET (token_t, temp_buffer, i)->code != T_STR + && VARR_GET (token_t, temp_buffer, i)->code != T_HEADER)) { + error (c2m_ctx, t->pos, "wrong #include"); + goto ret; + } + name = get_include_fname (c2m_ctx, VARR_GET (token_t, temp_buffer, i)); + } + 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); + } 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 (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; + + if (pre_match (c2m_ctx, T_NUMBER, NULL, NULL, &r) || pre_match (c2m_ctx, T_CH, NULL, NULL, &r)) + return r; + if (pre_match (c2m_ctx, '(', NULL, NULL, NULL)) { + if ((r = pre_cond_expr (c2m_ctx)) == NULL) return r; + 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 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, k, len; + token_t t, id, ppt; + const char *res; + struct macro macro_struct; + macro_t tab_macro; + VARR (token_t) * temp_buffer; + node_t tree; + + 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); + } + } + 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); + 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; + tree = parse_pre_expr (c2m_ctx, temp_buffer); + 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_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: assert (FALSE); + } + return res; +} + +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 { + assert (FALSE); + } + continue; + } + if (m->ignore_p) { + t->code = T_NO_MACRO_IDENT; + out_token (c2m_ctx, t); + continue; + } + if (m->params == NULL) { /* macro without parameters */ + unget_next_pptoken (c2m_ctx, new_token (c2m_ctx, t->pos, "", T_EOR, N_IGNORE)); +#ifdef C2MIR_PREPRO_DEBUG + fprintf (stderr, "# push back \n"); +#endif + mc = new_macro_call (m); + add_tokens (mc->repl_buffer, m->replacement); + copy_and_push_back (c2m_ctx, do_concat (c2m_ctx, mc->repl_buffer)); + m->ignore_p = TRUE; + VARR_PUSH (macro_call_t, macro_call_stack, mc); + } else { /* macro with parameters */ + t2 = NULL; + t1 = get_next_pptoken (c2m_ctx); + 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, t); + continue; + } + mc = new_macro_call (m); + find_args (c2m_ctx, mc); + VARR_PUSH (macro_call_t, macro_call_stack, mc); + 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; + } + pre_last_token = t; + if (!t->processed_p) should_be_pre_pos = t->pos; + if (t->code == '\n') return; + if (actual_pre_pos.fname != should_be_pre_pos.fname + || actual_pre_pos.lno != should_be_pre_pos.lno) { + if (actual_pre_pos.fname == should_be_pre_pos.fname + && actual_pre_pos.lno < should_be_pre_pos.lno + && actual_pre_pos.lno + 4 >= should_be_pre_pos.lno) { + for (; actual_pre_pos.lno != should_be_pre_pos.lno; actual_pre_pos.lno++) fprintf (f, "\n"); + } else { + if (pre_last_token != NULL) fprintf (f, "\n"); + fprintf (f, "#line %d", should_be_pre_pos.lno); + if (actual_pre_pos.fname != should_be_pre_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 < should_be_pre_pos.ln_pos - 1; i++) fprintf (f, " "); + actual_pre_pos = should_be_pre_pos; + } + fprintf (f, "%s", t->code == ' ' ? " " : t->repr); +} + +static void pre_out (c2m_ctx_t c2m_ctx, token_t t) { + if (t == NULL) { + t = new_token (c2m_ctx, pre_last_token == NULL ? no_pos : pre_last_token->pos, "", + T_EOFILE, N_IGNORE); + } else { + assert (t->code != T_EOU && t->code != EOF); + pre_last_token = t; + if ((t = pptoken2token (c2m_ctx, t, TRUE)) == NULL) return; + } + if (t->code == T_STR && VARR_LENGTH (token_t, recorded_tokens) != 0 + && VARR_LAST (token_t, recorded_tokens)->code == T_STR) { /* concat strings */ + token_t last_t = VARR_POP (token_t, recorded_tokens); + + VARR_TRUNC (char, temp_string, 0); + for (const char *s = last_t->repr; *s != 0; s++) VARR_PUSH (char, temp_string, *s); + assert (VARR_LAST (char, temp_string) == '"' && t->repr[0] == '"'); + VARR_POP (char, temp_string); + for (const char *s = &t->repr[1]; *s != 0; s++) VARR_PUSH (char, temp_string, *s); + t = last_t; + assert (VARR_LAST (char, temp_string) == '"'); + VARR_PUSH (char, temp_string, '\0'); + t->repr = uniq_cstr (c2m_ctx, VARR_ADDR (char, temp_string)).s; + set_string_val (c2m_ctx, t, temp_string); + } + VARR_PUSH (token_t, recorded_tokens, t); +} + +static void common_pre_out (c2m_ctx_t c2m_ctx, token_t t) { + pptokens_num++; + (options->prepro_only_p ? pre_text_out : pre_out) (c2m_ctx, t); +} + +static void pre (c2m_ctx_t c2m_ctx, const char *start_source_name) { + pre_last_token = NULL; + actual_pre_pos.fname = NULL; + should_be_pre_pos.fname = start_source_name; + should_be_pre_pos.lno = 0; + should_be_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) { + return tpname1.id->u.s.s == tpname2.id->u.s.s && tpname1.scope == tpname2.scope; +} + +static htab_hash_t tpname_hash (tpname_t tpname) { + 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); } + +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; */ +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 (declaration) { + int typedef_p; + node_t op, list, decl, spec, r; + pos_t 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 { + PA (declaration_specs, curr_scope == top_scope ? (node_t) 1 : NULL); + spec = r; + 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; + assert (decl->code == N_DECL); + if (typedef_p) { + op = NL_HEAD (decl->ops); + tpname_add (op, curr_scope); + } + 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)) != err_node) { + if (options->pedantic_p) + error (c2m_ctx, spec_pos, "GCC attributes are not implemented"); + else + warning (c2m_ctx, spec_pos, "GCC attributes are not implemented -- ignoring them"); + continue; + } else + break; + op_append (list, r); + } + if (prev_type_spec == NULL && arg != NULL) { + if (options->pedantic_p) warning (c2m_ctx, pos, "type defaults to int"); + r = new_pos_node (c2m_ctx, N_INT, pos); + op_append (list, r); + } + return list; +} + +D (sc_spec) { + node_t r; + pos_t pos; + + if (MP (T_TYPEDEF, pos)) { + r = new_pos_node (c2m_ctx, N_TYPEDEF, pos); + } else if (MP (T_EXTERN, pos)) { + r = new_pos_node (c2m_ctx, N_EXTERN, pos); + } else if (MP (T_STATIC, pos)) { + r = new_pos_node (c2m_ctx, N_STATIC, pos); + } else if (MP (T_AUTO, pos)) { + r = new_pos_node (c2m_ctx, N_AUTO, pos); + } else if (MP (T_REGISTER, pos)) { + r = new_pos_node (c2m_ctx, N_REGISTER, pos); + } else if (MP (T_THREAD_LOCAL, pos)) { + r = new_pos_node (c2m_ctx, N_THREAD_LOCAL, pos); + } else { + if (record_level == 0) syntax_error (c2m_ctx, "a storage specifier"); + return err_node; + } + return r; +} + +DA (type_spec) { + node_t op1, op2, op3, op4, r; + int struct_p, id_p = FALSE; + pos_t pos; + + if (MP (T_VOID, pos)) { + r = new_pos_node (c2m_ctx, N_VOID, pos); + } else if (MP (T_CHAR, pos)) { + r = new_pos_node (c2m_ctx, N_CHAR, pos); + } else if (MP (T_SHORT, pos)) { + r = new_pos_node (c2m_ctx, N_SHORT, pos); + } else if (MP (T_INT, pos)) { + r = new_pos_node (c2m_ctx, N_INT, pos); + } else if (MP (T_LONG, pos)) { + r = new_pos_node (c2m_ctx, N_LONG, pos); + } else if (MP (T_FLOAT, pos)) { + r = new_pos_node (c2m_ctx, N_FLOAT, pos); + } else if (MP (T_DOUBLE, pos)) { + r = new_pos_node (c2m_ctx, N_DOUBLE, pos); + } else if (MP (T_SIGNED, pos)) { + r = new_pos_node (c2m_ctx, N_SIGNED, pos); + } else if (MP (T_UNSIGNED, pos)) { + r = new_pos_node (c2m_ctx, N_UNSIGNED, pos); + } else if (MP (T_BOOL, pos)) { + r = new_pos_node (c2m_ctx, N_BOOL, pos); + } else if (MP (T_COMPLEX, pos)) { + if (record_level == 0) error (c2m_ctx, pos, "complex numbers are not supported"); + return err_node; + } else if (MP (T_ATOMIC, pos)) { /* atomic-type-specifier */ + PT ('('); + P (type_name); + PT (')'); + error (c2m_ctx, pos, "Atomic types are not supported"); + } else if ((struct_p = MP (T_STRUCT, pos)) || MP (T_UNION, pos)) { + /* struct-or-union-specifier, struct-or-union */ + if (!MN (T_ID, op1)) { + op1 = new_node (c2m_ctx, N_IGNORE); + } else { + id_p = TRUE; + } + if (M ('{')) { + if (!C ('}')) { + P (struct_declaration_list); + } else { + (options->pedantic_p ? error : warning) (c2m_ctx, pos, "empty struct/union"); + r = new_node (c2m_ctx, N_LIST); + } + PT ('}'); + } else if (!id_p) { + return err_node; + } else { + r = new_node (c2m_ctx, N_IGNORE); + } + r = new_pos_node2 (c2m_ctx, struct_p ? N_STRUCT : N_UNION, pos, op1, r); + } else if (MP (T_ENUM, pos)) { /* enum-specifier */ + if (!MN (T_ID, op1)) { + op1 = new_node (c2m_ctx, N_IGNORE); + } else { + id_p = TRUE; + } + op2 = new_node (c2m_ctx, N_LIST); + if (M ('{')) { /* enumerator-list */ + for (;;) { /* enumerator */ + PTN (T_ID); + op3 = r; /* enumeration-constant */ + if (!M ('=')) { + op4 = new_node (c2m_ctx, N_IGNORE); + } else { + P (const_expr); + op4 = r; + } + op_append (op2, new_node2 (c2m_ctx, N_ENUM_CONST, op3, op4)); + if (!M (',')) break; + if (C ('}')) break; + } + PT ('}'); + } else if (!id_p) { + return err_node; + } else { + op2 = new_node (c2m_ctx, N_IGNORE); + } + r = new_pos_node2 (c2m_ctx, N_ENUM, pos, op1, op2); + } else if (arg == NULL) { + P (typedef_name); + } else { + r = err_node; + } + return r; +} + +D (struct_declaration_list) { + node_t r, res, el, next_el; + + res = new_node (c2m_ctx, N_LIST); + for (;;) { + P (struct_declaration); + if (r->code != N_LIST) { + op_append (res, r); + } else { + for (el = NL_HEAD (r->ops); el != NULL; el = next_el) { + next_el = NL_NEXT (el); + NL_REMOVE (r->ops, el); + op_append (res, el); + } + } + if (C ('}')) break; + } + return res; +} + +D (struct_declaration) { + node_t list, spec, op, r; + + if (C (T_STATIC_ASSERT)) { + P (st_assert); + } else { + P (spec_qual_list); + spec = r; + list = new_node (c2m_ctx, N_LIST); + if (M (';')) { + op = new_pos_node3 (c2m_ctx, N_MEMBER, spec->pos, new_node1 (c2m_ctx, N_SHARE, spec), + new_node (c2m_ctx, N_IGNORE), new_node (c2m_ctx, N_IGNORE)); + op_append (list, op); + } else { /* struct-declarator-list */ + for (;;) { /* struct-declarator */ + if (!C (':')) { + P (declarator); + op = r; + } else { + op = new_node (c2m_ctx, N_IGNORE); + } + if (M (':')) { + P (const_expr); + } else { + r = new_node (c2m_ctx, N_IGNORE); + } + op = new_pos_node3 (c2m_ctx, N_MEMBER, op->pos, new_node1 (c2m_ctx, N_SHARE, spec), op, r); + op_append (list, op); + if (!M (',')) break; + } + PT (';'); + } + r = list; + } + return r; +} + +D (spec_qual_list) { + node_t list, op, r, arg = NULL; + int first_p; + + list = new_node (c2m_ctx, N_LIST); + for (first_p = TRUE;; first_p = FALSE) { + if (C (T_CONST) || C (T_RESTRICT) || C (T_VOLATILE) || C (T_ATOMIC)) { + P (type_qual); + op = r; + } else if ((op = TRY_A (type_spec, arg)) != err_node) { + arg = op; + } else if (first_p) { + return err_node; + } else { + break; + } + op_append (list, op); + } + return list; +} + +D (type_qual) { + node_t r; + pos_t pos; + + if (MP (T_CONST, pos)) { + r = new_pos_node (c2m_ctx, N_CONST, pos); + } else if (MP (T_RESTRICT, pos)) { + r = new_pos_node (c2m_ctx, N_RESTRICT, pos); + } else if (MP (T_VOLATILE, pos)) { + r = new_pos_node (c2m_ctx, N_VOLATILE, pos); + } else if (MP (T_ATOMIC, pos)) { + r = new_pos_node (c2m_ctx, N_ATOMIC, pos); + } else { + if (record_level == 0) syntax_error (c2m_ctx, "a type qualifier"); + r = err_node; + } + return r; +} + +D (func_spec) { + node_t r; + pos_t pos; + + if (MP (T_INLINE, pos)) { + r = new_pos_node (c2m_ctx, N_INLINE, pos); + } else if (MP (T_NO_RETURN, pos)) { + r = new_pos_node (c2m_ctx, N_NO_RETURN, pos); + } else { + if (record_level == 0) syntax_error (c2m_ctx, "a function specifier"); + r = err_node; + } + return r; +} + +D (align_spec) { + node_t r; + pos_t pos; + + PTP (T_ALIGNAS, pos); + PT ('('); + if ((r = TRY (type_name)) != err_node) { + } else { + P (const_expr); + } + PT (')'); + r = new_pos_node1 (c2m_ctx, N_ALIGNAS, pos, r); + return r; +} + +D (declarator) { + node_t list, p = NULL, r, el, next_el; + + if (C ('*')) { + P (pointer); + p = r; + } + P (direct_declarator); + if (p != NULL) { + list = NL_NEXT (NL_HEAD (r->ops)); + assert (list->code == N_LIST); + for (el = NL_HEAD (p->ops); el != NULL; el = next_el) { + next_el = NL_NEXT (el); + NL_REMOVE (p->ops, el); + op_append (list, el); + } + } + return r; +} + +D (direct_declarator) { + node_t list, tql, ae, res, r; + pos_t pos, static_pos; + + if (MN (T_ID, r)) { + res = new_node2 (c2m_ctx, N_DECL, r, new_node (c2m_ctx, N_LIST)); + } else if (M ('(')) { + P (declarator); + res = r; + PT (')'); + } else { + return err_node; + } + list = NL_NEXT (NL_HEAD (res->ops)); + assert (list->code == N_LIST); + for (;;) { + if (MP ('(', pos)) { + if ((r = TRY (param_type_list)) != err_node) { + } else { + P (id_list); + } + PT (')'); + op_append (list, new_pos_node1 (c2m_ctx, N_FUNC, pos, r)); + } else if (M ('[')) { + int static_p = FALSE; + + if (MP (T_STATIC, static_pos)) { + static_p = TRUE; + } + if (!C (T_CONST) && !C (T_RESTRICT) && !C (T_VOLATILE) && !C (T_ATOMIC)) { + tql = new_node (c2m_ctx, N_LIST); + } else { + P (type_qual_list); + tql = r; + if (!static_p && M (T_STATIC)) { + static_p = TRUE; + } + } + if (static_p) { + P (assign_expr); + ae = r; + } else if (MP ('*', pos)) { + ae = new_pos_node (c2m_ctx, N_STAR, pos); + } else if (!C (']')) { + P (assign_expr); + ae = r; + } else { + ae = new_node (c2m_ctx, N_IGNORE); + } + PT (']'); + op_append (list, new_node3 (c2m_ctx, N_ARR, + static_p ? new_pos_node (c2m_ctx, N_STATIC, static_pos) + : new_node (c2m_ctx, N_IGNORE), + tql, ae)); + } else + break; + } + return res; +} + +D (pointer) { + node_t op, r; + pos_t pos; + + PTP ('*', pos); + if (C (T_CONST) || C (T_RESTRICT) || C (T_VOLATILE) || C (T_ATOMIC)) { + P (type_qual_list); + } else { + r = new_node (c2m_ctx, N_LIST); + } + op = new_pos_node1 (c2m_ctx, N_POINTER, pos, r); + if (C ('*')) { + P (pointer); + } else { + r = new_node (c2m_ctx, N_LIST); + } + op_append (r, op); + return r; +} + +D (type_qual_list) { + node_t list, r; + + list = new_node (c2m_ctx, N_LIST); + for (;;) { + P (type_qual); + op_append (list, r); + if (!C (T_CONST) && !C (T_RESTRICT) && !C (T_VOLATILE) && !C (T_ATOMIC)) break; + } + return list; +} + +D (param_type_abstract_declarator) { + node_t r = err_node; + + P (abstract_declarator); + if (C (',') || C (')')) return r; + return err_node; +} + +D (param_type_list) { + node_t list, op1, op2, r = err_node; + int comma_p; + pos_t pos; + + list = new_node (c2m_ctx, N_LIST); + for (;;) { /* parameter-list, parameter-declaration */ + PA (declaration_specs, NULL); + op1 = r; + if (C (',') || C (')')) { + r = new_node2 (c2m_ctx, N_TYPE, op1, + new_node2 (c2m_ctx, N_DECL, new_node (c2m_ctx, N_IGNORE), + new_node (c2m_ctx, N_LIST))); + } else if ((op2 = TRY (param_type_abstract_declarator)) != err_node) { + /* Try param_type_abstract_declarator first for possible func + type case (" ()") which can conflict with declarator (" + ()") */ + r = new_node2 (c2m_ctx, N_TYPE, op1, op2); + } else { + P (declarator); + r = new_pos_node3 (c2m_ctx, N_SPEC_DECL, op2->pos, op1, r, new_node (c2m_ctx, N_IGNORE)); + } + op_append (list, r); + comma_p = FALSE; + if (!M (',')) break; + comma_p = TRUE; + if (C (T_DOTS)) break; + } + if (comma_p) { + PTP (T_DOTS, pos); + op_append (list, new_pos_node (c2m_ctx, N_DOTS, pos)); + } + return list; +} + +D (id_list) { + node_t list, r; + + list = new_node (c2m_ctx, N_LIST); + if (C (')')) return list; + for (;;) { + PTN (T_ID); + op_append (list, r); + if (!M (',')) break; + } + return list; +} + +D (abstract_declarator) { + node_t list, p = NULL, r, el, next_el; + + if (C ('*')) { + P (pointer); + p = r; + if ((r = TRY (direct_abstract_declarator)) == err_node) + r = new_pos_node2 (c2m_ctx, N_DECL, p->pos, new_node (c2m_ctx, N_IGNORE), + new_node (c2m_ctx, N_LIST)); + } else { + P (direct_abstract_declarator); + } + if (p != NULL) { + list = NL_NEXT (NL_HEAD (r->ops)); + assert (list->code == N_LIST); + for (el = NL_HEAD (p->ops); el != NULL; el = next_el) { + next_el = NL_NEXT (el); + NL_REMOVE (p->ops, el); + op_append (list, el); + } + } + return r; +} + +D (par_abstract_declarator) { + node_t r; + + PT ('('); + P (abstract_declarator); + PT (')'); + return r; +} + +D (direct_abstract_declarator) { + node_t res, list, tql, ae, r; + pos_t pos, pos2 = no_pos; + + if ((res = TRY (par_abstract_declarator)) != err_node) { + if (!C ('(') && !C ('[')) return res; + } else { + res = new_node2 (c2m_ctx, N_DECL, new_node (c2m_ctx, N_IGNORE), new_node (c2m_ctx, N_LIST)); + } + list = NL_NEXT (NL_HEAD (res->ops)); + assert (list->code == N_LIST); + for (;;) { + if (MP ('(', pos)) { + P (param_type_list); + PT (')'); + op_append (list, new_pos_node1 (c2m_ctx, N_FUNC, pos, r)); + } else { + PTP ('[', pos); + if (MP ('*', pos2)) { + r = new_pos_node3 (c2m_ctx, N_ARR, pos, new_node (c2m_ctx, N_IGNORE), + new_node (c2m_ctx, N_IGNORE), new_pos_node (c2m_ctx, N_STAR, pos2)); + } else { + int static_p = FALSE; + + if (MP (T_STATIC, pos2)) { + static_p = TRUE; + } + if (!C (T_CONST) && !C (T_RESTRICT) && !C (T_VOLATILE) && !C (T_ATOMIC)) { + tql = new_node (c2m_ctx, N_LIST); + } else { + P (type_qual_list); + tql = r; + if (!static_p && M (T_STATIC)) { + static_p = TRUE; + } + } + if (!C (']')) { + P (assign_expr); + ae = r; + } else { + ae = new_node (c2m_ctx, N_IGNORE); + } + r = new_pos_node3 (c2m_ctx, N_ARR, pos, + static_p ? new_pos_node (c2m_ctx, N_STATIC, pos2) + : new_node (c2m_ctx, N_IGNORE), + tql, ae); + } + PT (']'); + op_append (list, r); + } + if (!C ('(') && !C ('[')) break; + } + add_pos (res, list->pos); + return res; +} + +D (typedef_name) { + node_t scope, r; + + PTN (T_ID); + for (scope = curr_scope;; scope = scope->u.scope) { + if (tpname_find (r, scope, NULL)) return r; + if (scope == NULL) break; + } + return err_node; +} + +D (initializer) { + node_t r; + + if (!M ('{')) { + P (assign_expr); + } else { + P (initializer_list); + if (M (',')) + ; + PT ('}'); + } + return r; +} + +D (initializer_list) { + node_t list, list2, r; + int first_p; + + list = new_node (c2m_ctx, N_LIST); + for (;;) { /* designation */ + list2 = new_node (c2m_ctx, N_LIST); + for (first_p = TRUE;; first_p = FALSE) { /* designator-list, designator */ + if (M ('[')) { + P (const_expr); + PT (']'); + } else if (M ('.')) { + PTN (T_ID); + r = new_node1 (c2m_ctx, N_FIELD_ID, r); + } else + break; + op_append (list2, r); + } + if (!first_p) { + PT ('='); + } + P (initializer); + op_append (list, new_node2 (c2m_ctx, N_INIT, list2, r)); + if (!M (',')) break; + if (C ('}')) break; + } + return list; +} + +D (type_name) { + node_t op, r; + + P (spec_qual_list); + op = r; + if (!C (')') && !C (':')) { + P (abstract_declarator); + } else { + r = new_pos_node2 (c2m_ctx, N_DECL, op->pos, new_node (c2m_ctx, N_IGNORE), + new_node (c2m_ctx, N_LIST)); + } + return new_node2 (c2m_ctx, N_TYPE, op, r); +} + +D (st_assert) { + node_t op1, r; + pos_t pos; + + PTP (T_STATIC_ASSERT, pos); + PT ('('); + P (const_expr); + op1 = r; + PT (','); + PTN (T_STR); + PT (')'); + PT (';'); + r = new_pos_node2 (c2m_ctx, N_ST_ASSERT, pos, op1, r); + return r; +} + +/* Statements: */ +D (compound_stmt); + +D (label) { + node_t r, n; + pos_t pos; + + if (MP (T_CASE, pos)) { + P (expr); + n = new_pos_node1 (c2m_ctx, N_CASE, pos, r); + if (M (T_DOTS)) { + P (expr); + op_append (n, r); + } + r = n; + } else if (MP (T_DEFAULT, pos)) { + r = new_pos_node (c2m_ctx, N_DEFAULT, pos); + } else { + PTN (T_ID); + r = new_node1 (c2m_ctx, N_LABEL, r); + } + PT (':'); + return r; +} + +D (stmt) { + node_t l, n, op1, op2, op3, r; + pos_t pos; + + l = new_node (c2m_ctx, N_LIST); + while ((op1 = TRY (label)) != err_node) { + op_append (l, op1); + } + if (C ('{')) { + P (compound_stmt); + if (NL_HEAD (l->ops) != NULL) { /* replace empty label list */ + assert (NL_HEAD (r->ops)->code == N_LIST && NL_HEAD (NL_HEAD (r->ops)->ops) == NULL); + NL_REMOVE (r->ops, NL_HEAD (r->ops)); + NL_PREPEND (r->ops, l); + } + } else if (MP (T_IF, pos)) { /* selection-statement */ + PT ('('); + P (expr); + op1 = r; + PT (')'); + P (stmt); + op2 = r; + if (!M (T_ELSE)) { + r = new_node (c2m_ctx, N_IGNORE); + } else { + P (stmt); + } + r = new_pos_node4 (c2m_ctx, N_IF, pos, l, op1, op2, r); + } else if (MP (T_SWITCH, pos)) { /* selection-statement */ + PT ('('); + P (expr); + op1 = r; + PT (')'); + P (stmt); + r = new_pos_node3 (c2m_ctx, N_SWITCH, pos, l, op1, r); + } else if (MP (T_WHILE, pos)) { /* iteration-statement */ + PT ('('); + P (expr); + op1 = r; + PT (')'); + P (stmt); + r = new_pos_node3 (c2m_ctx, N_WHILE, pos, l, op1, r); + } else if (M (T_DO)) { /* iteration-statement */ + P (stmt); + op1 = r; + PTP (T_WHILE, pos); + PT ('('); + P (expr); + PT (')'); + PT (';'); + r = new_pos_node3 (c2m_ctx, N_DO, pos, l, r, op1); + } else if (MP (T_FOR, pos)) { /* iteration-statement */ + PT ('('); + n = new_pos_node (c2m_ctx, N_FOR, pos); + n->u.scope = curr_scope; + curr_scope = n; + if ((r = TRY (declaration)) != err_node) { + op1 = r; + curr_scope = n->u.scope; + } else { + curr_scope = n->u.scope; + if (!M (';')) { + P (expr); + op1 = r; + PT (';'); + } else { + op1 = new_node (c2m_ctx, N_IGNORE); + } + } + if (M (';')) { + op2 = new_node (c2m_ctx, N_IGNORE); + } else { + P (expr); + op2 = r; + PT (';'); + } + if (C (')')) { + op3 = new_node (c2m_ctx, N_IGNORE); + } else { + P (expr); + op3 = r; + } + PT (')'); + P (stmt); + op_append (n, l); + op_append (n, op1); + op_append (n, op2); + op_append (n, op3); + op_append (n, r); + r = n; + } else if (MP (T_GOTO, pos)) { /* jump-statement */ + PTN (T_ID); + PT (';'); + r = new_pos_node2 (c2m_ctx, N_GOTO, pos, l, r); + } else if (MP (T_CONTINUE, pos)) { /* continue-statement */ + PT (';'); + r = new_pos_node1 (c2m_ctx, N_CONTINUE, pos, l); + } else if (MP (T_BREAK, pos)) { /* break-statement */ + PT (';'); + r = new_pos_node1 (c2m_ctx, N_BREAK, pos, l); + } else if (MP (T_RETURN, pos)) { /* return-statement */ + if (M (';')) { + r = new_node (c2m_ctx, N_IGNORE); + } else { + P (expr); + PT (';'); + } + r = new_pos_node2 (c2m_ctx, N_RETURN, pos, l, r); + } else { /* expression-statement */ + if (C (';')) { + r = new_node (c2m_ctx, N_IGNORE); + } else { + P (expr); + } + PT (';'); + r = new_pos_node2 (c2m_ctx, N_EXPR, r->pos, l, r); + } + return r; +} + +static void error_recovery (c2m_ctx_t c2m_ctx, int par_lev, const char *expected) { + syntax_error (c2m_ctx, expected); + if (options->debug_p) fprintf (stderr, "error recovery: skipping"); + for (;;) { + if (curr_token->code == T_EOFILE || (par_lev == 0 && curr_token->code == ';')) break; + if (curr_token->code == '{') { + par_lev++; + } else if (curr_token->code == '}') { + if (--par_lev <= 0) break; + } + if (options->debug_p) + fprintf (stderr, " %s(%d:%d)", get_token_name (c2m_ctx, curr_token->code), + curr_token->pos.lno, curr_token->pos.ln_pos); + read_token (c2m_ctx); + } + if (options->debug_p) fprintf (stderr, " %s\n", get_token_name (c2m_ctx, curr_token->code)); + if (curr_token->code != T_EOFILE) read_token (c2m_ctx); +} + +D (compound_stmt) { + node_t n, list, r; + pos_t pos; + + PTE ('{', pos, err0); + list = new_node (c2m_ctx, N_LIST); + n = new_pos_node2 (c2m_ctx, N_BLOCK, pos, new_node (c2m_ctx, N_LIST), list); + n->u.scope = curr_scope; + curr_scope = n; + while (!C ('}') && !C (T_EOFILE)) { /* block-item-list, block_item */ + if ((r = TRY (declaration)) != err_node) { + } else { + PE (stmt, err1); + } + op_flat_append (list, r); + continue; + err1: + error_recovery (c2m_ctx, 1, ""); + } + curr_scope = n->u.scope; + if (!C (T_EOFILE)) PT ('}'); + return n; +err0: + error_recovery (c2m_ctx, 0, "{"); + return err_node; +} + +D (transl_unit) { + node_t list, ds, d, dl, r; + + // curr_token->code = ';'; /* for error recovery */ + read_token (c2m_ctx); + list = new_node (c2m_ctx, N_LIST); + while (!C (T_EOFILE)) { /* external-declaration */ + if ((r = TRY (declaration)) != err_node) { + } else { + PAE (declaration_specs, (node_t) 1, err); + ds = r; + PE (declarator, err); + d = r; + dl = new_node (c2m_ctx, N_LIST); + d->u.scope = curr_scope; + curr_scope = d; + while (!C ('{')) { /* declaration-list */ + PE (declaration, decl_err); + op_flat_append (dl, r); + } + P (compound_stmt); + r = new_pos_node4 (c2m_ctx, N_FUNC_DEF, d->pos, ds, d, dl, r); + curr_scope = d->u.scope; + } + op_flat_append (list, r); + continue; + decl_err: + curr_scope = d->u.scope; + err: + error_recovery (c2m_ctx, 0, ""); + } + return new_node1 (c2m_ctx, N_MODULE, list); +} + +static void fatal_error (c2m_ctx_t c2m_ctx, C_error_code_t code, const char *message) { + if (options->message_file != NULL) fprintf (options->message_file, "%s\n", message); + longjmp (c2m_ctx->env, 1); +} + +static void kw_add (c2m_ctx_t c2m_ctx, const char *name, token_code_t tc, size_t flags) { + str_add (c2m_ctx, name, strlen (name) + 1, tc, flags, TRUE); +} + +static void parse_init (MIR_context_t ctx) { + c2m_ctx_t c2m_ctx = *c2m_ctx_loc (ctx); + + c2m_ctx->parse_ctx = c2mir_calloc (ctx, sizeof (struct parse_ctx)); + error_func = fatal_error; + record_level = 0; + curr_uid = 0; + init_streams (c2m_ctx); + VARR_CREATE (token_t, recorded_tokens, 32); + VARR_CREATE (token_t, buffered_tokens, 32); + pre_init (ctx); + kw_add (c2m_ctx, "_Bool", T_BOOL, 0); + kw_add (c2m_ctx, "_Complex", T_COMPLEX, 0); + kw_add (c2m_ctx, "_Alignas", T_ALIGNAS, 0); + kw_add (c2m_ctx, "_Alignof", T_ALIGNOF, 0); + kw_add (c2m_ctx, "_Atomic", T_ATOMIC, 0); + kw_add (c2m_ctx, "_Generic", T_GENERIC, 0); + kw_add (c2m_ctx, "_Noreturn", T_NO_RETURN, 0); + kw_add (c2m_ctx, "_Static_assert", T_STATIC_ASSERT, 0); + kw_add (c2m_ctx, "_Thread_local", T_THREAD_LOCAL, 0); + kw_add (c2m_ctx, "auto", T_AUTO, 0); + kw_add (c2m_ctx, "break", T_BREAK, 0); + kw_add (c2m_ctx, "case", T_CASE, 0); + kw_add (c2m_ctx, "char", T_CHAR, 0); + kw_add (c2m_ctx, "const", T_CONST, 0); + kw_add (c2m_ctx, "continue", T_CONTINUE, 0); + kw_add (c2m_ctx, "default", T_DEFAULT, 0); + kw_add (c2m_ctx, "do", T_DO, 0); + kw_add (c2m_ctx, "double", T_DOUBLE, 0); + kw_add (c2m_ctx, "else", T_ELSE, 0); + kw_add (c2m_ctx, "enum", T_ENUM, 0); + kw_add (c2m_ctx, "extern", T_EXTERN, 0); + kw_add (c2m_ctx, "float", T_FLOAT, 0); + kw_add (c2m_ctx, "for", T_FOR, 0); + kw_add (c2m_ctx, "goto", T_GOTO, 0); + kw_add (c2m_ctx, "if", T_IF, 0); + kw_add (c2m_ctx, "inline", T_INLINE, FLAG_EXT89); + kw_add (c2m_ctx, "int", T_INT, 0); + kw_add (c2m_ctx, "long", T_LONG, 0); + kw_add (c2m_ctx, "register", T_REGISTER, 0); + kw_add (c2m_ctx, "restrict", T_RESTRICT, FLAG_C89); + kw_add (c2m_ctx, "return", T_RETURN, 0); + kw_add (c2m_ctx, "short", T_SHORT, 0); + kw_add (c2m_ctx, "signed", T_SIGNED, 0); + kw_add (c2m_ctx, "sizeof", T_SIZEOF, 0); + kw_add (c2m_ctx, "static", T_STATIC, 0); + kw_add (c2m_ctx, "struct", T_STRUCT, 0); + kw_add (c2m_ctx, "switch", T_SWITCH, 0); + kw_add (c2m_ctx, "typedef", T_TYPEDEF, 0); + kw_add (c2m_ctx, "typeof", T_TYPEOF, FLAG_EXT); + kw_add (c2m_ctx, "union", T_UNION, 0); + kw_add (c2m_ctx, "unsigned", T_UNSIGNED, 0); + kw_add (c2m_ctx, "void", T_VOID, 0); + kw_add (c2m_ctx, "volatile", T_VOLATILE, 0); + kw_add (c2m_ctx, "while", T_WHILE, 0); + kw_add (c2m_ctx, "__restrict", T_RESTRICT, FLAG_EXT); + kw_add (c2m_ctx, "__restrict__", T_RESTRICT, FLAG_EXT); + kw_add (c2m_ctx, "__inline", T_INLINE, FLAG_EXT); + kw_add (c2m_ctx, "__inline__", T_INLINE, FLAG_EXT); + tpname_init (); +} + +#ifndef SOURCEDIR +#define SOURCEDIR "./" +#endif + +#ifndef INSTALLDIR +#define INSTALLDIR "/usr/bin/" +#endif + +static void add_standard_includes (c2m_ctx_t c2m_ctx) { + FILE *f; + const char *str; + + for (int i = 0; i < sizeof (standard_includes) / sizeof (char *); i++) { + str = standard_includes[i]; + add_string_stream (c2m_ctx, "", str); + } +} + +static node_t parse (c2m_ctx_t c2m_ctx) { + next_token_index = 0; + return transl_unit (c2m_ctx, FALSE); +} + +static void parse_finish (c2m_ctx_t c2m_ctx) { + if (c2m_ctx == NULL || c2m_ctx->parse_ctx == NULL) return; + if (recorded_tokens != NULL) VARR_DESTROY (token_t, recorded_tokens); + if (buffered_tokens != NULL) VARR_DESTROY (token_t, buffered_tokens); + pre_finish (c2m_ctx); + tpname_finish (); + finish_streams (c2m_ctx); + free (c2m_ctx->parse_ctx); +} + +#undef P +#undef PT +#undef PTP +#undef PTN +#undef PE +#undef PTE +#undef D +#undef M +#undef MP +#undef MC +#undef MN +#undef TRY +#undef C + +/* ------------------------- Parser End ------------------------------ */ +/* New Page */ +/* ---------------------- Context Checker Start ---------------------- */ + +/* The context checker is AST traversing pass which checks C11 + constraints. It also augmenting AST nodes by type and layout + information. Here are the created node attributes: + + 1. expr nodes have attribute "struct expr", N_ID not expr context has NULL attribute. + 2. N_SWITCH has attribute "struct switch_attr" + 3. N_SPEC_DECL (only with ID), N_MEMBER, N_FUNC_DEF have attribute "struct decl" + 4. N_GOTO hash attribute node_t (target stmt) + 5. N_STRUCT, N_UNION have attribute "struct node_scope" if they have a decl list + 6. N_MODULE, N_BLOCK, N_FOR, N_FUNC have attribute "struct node_scope" + 7. declaration_specs or spec_qual_list N_LISTs have attribute "struct decl_spec", + but as a part of N_COMPOUND_LITERAL have attribute "struct decl" + 8. N_ENUM_CONST has attribute "struct enum_value" + 9. N_CASE and N_DEFAULT have attribute "struct case_attr" + +*/ + +typedef struct decl *decl_t; +DEF_VARR (decl_t); + +typedef struct case_attr *case_t; +DEF_HTAB (case_t); + +struct check_ctx { + VARR (node_t) * gotos; + node_t func_block_scope; + unsigned curr_func_scope_num; + int in_params_p; + node_t curr_unnamed_anon_struct_union_member; + node_t curr_switch; + VARR (decl_t) * func_decls_for_allocation; + node_t n_i1_node; + HTAB (case_t) * case_tab; + node_t curr_func_def, curr_loop, curr_loop_switch; + mir_size_t curr_call_arg_area_offset; + VARR (node_t) * context_stack; +}; + +#define gotos c2m_ctx->check_ctx->gotos +#define func_block_scope c2m_ctx->check_ctx->func_block_scope +#define curr_func_scope_num c2m_ctx->check_ctx->curr_func_scope_num +#define in_params_p c2m_ctx->check_ctx->in_params_p +#define curr_unnamed_anon_struct_union_member \ + c2m_ctx->check_ctx->curr_unnamed_anon_struct_union_member +#define curr_switch c2m_ctx->check_ctx->curr_switch +#define func_decls_for_allocation c2m_ctx->check_ctx->func_decls_for_allocation +#define n_i1_node c2m_ctx->check_ctx->n_i1_node +#define case_tab c2m_ctx->check_ctx->case_tab +#define curr_func_def c2m_ctx->check_ctx->curr_func_def +#define curr_loop c2m_ctx->check_ctx->curr_loop +#define curr_loop_switch c2m_ctx->check_ctx->curr_loop_switch +#define curr_call_arg_area_offset c2m_ctx->check_ctx->curr_call_arg_area_offset +#define context_stack c2m_ctx->check_ctx->context_stack + +static int supported_alignment_p (mir_llong align) { return TRUE; } // ??? + +static int symbol_eq (symbol_t s1, symbol_t s2) { + 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) { + 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) { 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); +} + +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; +} + +#define SWAP(a1, a2, t) \ + do { \ + t = a1; \ + a1 = a2; \ + a2 = t; \ + } while (0) + +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 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; + 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 = r->ops; + + r->ops = sym.def_node->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)) + 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; + type->mode = TM_PTR; + type->u.ptr_type = arr_type->el_type; + type->type_qual = arr_type->ind_type_qual; + 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; + struct expr *expr; + + 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, size_val; + size_t mark, len; + symbol_t sym; + struct expr *sexpr; + init_object_t init_object; + int addr_p; + + 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, 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, *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) && 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)) { + error (c2m_ctx, r->pos, "Division by zero"); + if (floating_type_p (&t)) + e->u.d_val = 0.0; + else if (signed_integer_type_p (&t)) + e->u.i_val = 0; + else + e->u.u_val = 0; + } 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: assert (FALSE); + } + return e; +} + +static unsigned case_hash (case_t el) { + 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) { + node_t case_expr1 = NL_HEAD (el1->case_node->ops); + node_t case_expr2 = NL_HEAD (el2->case_node->ops); + struct expr *expr1, *expr2; + + assert (el1->case_node->code == N_CASE && el2->case_node->code == N_CASE); + expr1 = case_expr1->attr; + expr2 = case_expr2->attr; + assert (expr1->const_p && expr2->const_p); + assert (signed_integer_type_p (expr1->type) == signed_integer_type_p (expr2->type)); + if (signed_integer_type_p (expr1->type)) return expr1->u.i_val == expr2->u.i_val; + return expr1->u.u_val == expr2->u.u_val; +} + +static void update_call_arg_area_offset (c2m_ctx_t c2m_ctx, struct type *type, int update_scope_p) { + node_t block = NL_EL (curr_func_def->ops, 3); + struct node_scope *ns = block->attr; + + curr_call_arg_area_offset += round_size (type_size (c2m_ctx, type), MAX_ALIGNMENT); + if (update_scope_p && ns->call_arg_area_size < curr_call_arg_area_offset) + ns->call_arg_area_size = curr_call_arg_area_offset; +} + +#define NODE_CASE(n) case N_##n: +#define REP_SEP +static void classify_node (node_t n, int *expr_attr_p, int *stmt_p) { + *expr_attr_p = *stmt_p = FALSE; + switch (n->code) { + REP8 (NODE_CASE, I, L, LL, U, UL, ULL, F, D) + REP8 (NODE_CASE, LD, CH, STR, ID, COMMA, ANDAND, OROR, EQ) + REP8 (NODE_CASE, NE, LT, LE, GT, GE, ASSIGN, BITWISE_NOT, NOT) + REP8 (NODE_CASE, AND, AND_ASSIGN, OR, OR_ASSIGN, XOR, XOR_ASSIGN, LSH, LSH_ASSIGN) + REP8 (NODE_CASE, RSH, RSH_ASSIGN, ADD, ADD_ASSIGN, SUB, SUB_ASSIGN, MUL, MUL_ASSIGN) + REP8 (NODE_CASE, DIV, DIV_ASSIGN, MOD, MOD_ASSIGN, IND, FIELD, ADDR, DEREF) + REP8 (NODE_CASE, DEREF_FIELD, COND, INC, DEC, POST_INC, POST_DEC, ALIGNOF, SIZEOF) + REP6 (NODE_CASE, EXPR_SIZEOF, CAST, COMPOUND_LITERAL, CALL, GENERIC, GENERIC_ASSOC) + *expr_attr_p = TRUE; + break; + REP8 (NODE_CASE, IF, SWITCH, WHILE, DO, FOR, GOTO, CONTINUE, BREAK) + REP4 (NODE_CASE, RETURN, EXPR, BLOCK, SPEC_DECL) /* SPEC DECL may have an initializer */ + *stmt_p = TRUE; + break; + REP8 (NODE_CASE, IGNORE, CASE, DEFAULT, LABEL, LIST, SHARE, TYPEDEF, EXTERN) + REP8 (NODE_CASE, STATIC, AUTO, REGISTER, THREAD_LOCAL, DECL, VOID, CHAR, SHORT) + REP8 (NODE_CASE, INT, LONG, FLOAT, DOUBLE, SIGNED, UNSIGNED, BOOL, STRUCT) + REP8 (NODE_CASE, UNION, ENUM, ENUM_CONST, MEMBER, CONST, RESTRICT, VOLATILE, ATOMIC) + REP8 (NODE_CASE, INLINE, NO_RETURN, ALIGNAS, FUNC, STAR, POINTER, DOTS, ARR) + REP6 (NODE_CASE, INIT, FIELD_ID, TYPE, ST_ASSERT, FUNC_DEF, MODULE) + break; + default: assert (FALSE); + } +} +#undef REP_SEP + +/* Create "static const char __func__[] = "" at the + beginning of func_block if it is necessary. */ +static void add__func__def (c2m_ctx_t c2m_ctx, node_t func_block, str_t func_name) { + static const char fdecl_name[] = "__func__"; + pos_t pos = func_block->pos; + node_t list, declarator, decl, decl_specs; + str_t str; + + if (!str_exists_p (c2m_ctx, fdecl_name, strlen (fdecl_name) + 1, &str)) return; + decl_specs = new_pos_node (c2m_ctx, N_LIST, pos); + NL_APPEND (decl_specs->ops, new_pos_node (c2m_ctx, N_STATIC, pos)); + NL_APPEND (decl_specs->ops, new_pos_node (c2m_ctx, N_CONST, pos)); + NL_APPEND (decl_specs->ops, new_pos_node (c2m_ctx, N_CHAR, pos)); + list = new_pos_node (c2m_ctx, N_LIST, pos); + NL_APPEND (list->ops, new_pos_node3 (c2m_ctx, N_ARR, pos, new_pos_node (c2m_ctx, N_IGNORE, pos), + new_pos_node (c2m_ctx, N_LIST, pos), + new_pos_node (c2m_ctx, N_IGNORE, pos))); + declarator = new_pos_node2 (c2m_ctx, N_DECL, pos, new_str_node (c2m_ctx, N_ID, str, pos), list); + decl = new_pos_node3 (c2m_ctx, N_SPEC_DECL, pos, decl_specs, declarator, + new_str_node (c2m_ctx, N_STR, func_name, pos)); + NL_PREPEND (NL_EL (func_block->ops, 1)->ops, decl); +} + +/* Sort by decl scope nesting (more nested scope has a bigger UID) and decl size. */ +static int decl_cmp (const void *v1, const void *v2) { + const decl_t d1 = *(const decl_t *) v1, d2 = *(const decl_t *) v2; + struct type *t1 = d1->decl_spec.type, *t2 = d2->decl_spec.type; + mir_size_t s1 = raw_type_size (d1->c2m_ctx, t1), s2 = raw_type_size (d2->c2m_ctx, t2); + + if (d1->scope->uid < d2->scope->uid) return -1; + if (d1->scope->uid > d2->scope->uid) return 1; + if (s1 < s2) return -1; + if (s1 > s2) return 1; + return 0; +} + +static void process_func_decls_for_allocation (c2m_ctx_t c2m_ctx) { + size_t i, j; + decl_t decl; + struct type *type; + struct node_scope *ns, *curr_ns; + node_t scope; + mir_size_t start_offset; + + /* 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)) { + 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; + 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); + VARR_CREATE (decl_t, func_decls_for_allocation, 1024); +} + +static void context_finish (MIR_context_t ctx) { + c2m_ctx_t c2m_ctx = *c2m_ctx_loc (ctx); + + if (c2m_ctx == NULL || c2m_ctx->check_ctx == NULL) return; + if (context_stack != NULL) VARR_DESTROY (node_t, context_stack); + if (gotos != NULL) VARR_DESTROY (node_t, gotos); + symbol_finish (c2m_ctx); + if (case_tab != NULL) HTAB_DESTROY (case_t, case_tab); + if (func_decls_for_allocation != NULL) VARR_DESTROY (decl_t, func_decls_for_allocation); + free (c2m_ctx->check_ctx); +} + +/* ------------------------ Context Checker Finish ---------------------------- */ + +/* New Page */ + +/* -------------------------- MIR generator start ----------------------------- */ + +static const char *FP_NAME = "fp"; +static const char *RET_ADDR_NAME = "Ret_Addr"; + +/* New attribute for non-empty label LIST is a MIR label. */ + +/* MIR var naming: + {I|U|i|u|f|d}_ -- temporary of I64, U64, I32, U32, F, D type + {I|U|i|u|f|d}_ -- variable with of original of the corresponding + type in scope with +*/ + +#if MIR_PTR64 +static const MIR_type_t MIR_POINTER_TYPE = MIR_T_I64; +#else +static const MIR_type_t MIR_POINTER_TYPE = MIR_T_I32; +#endif + +struct op { + decl_t decl; + MIR_op_t mir_op; +}; + +typedef struct op op_t; + +struct reg_var { + const char *name; + MIR_reg_t reg; +}; + +typedef struct reg_var reg_var_t; + +DEF_HTAB (reg_var_t); + +static VARR (MIR_var_t) * vars; + +struct init_el { + mir_size_t num, offset; + decl_t member_decl; /* NULL for non-member initialization */ + struct type *el_type; + node_t init; +}; + +typedef struct init_el init_el_t; +DEF_VARR (init_el_t); + +DEF_VARR (MIR_op_t); +DEF_VARR (case_t); + +struct gen_ctx { + op_t zero_op, one_op, minus_one_op; + MIR_item_t curr_func; + HTAB (reg_var_t) * reg_var_tab; + int reg_free_mark; + MIR_label_t continue_label, break_label; + VARR (node_t) * mem_params; + VARR (init_el_t) * init_els; + MIR_item_t memset_proto, memset_item; + MIR_item_t memcpy_proto, memcpy_item; + VARR (MIR_op_t) * call_ops, *switch_ops; + VARR (case_t) * switch_cases; + int curr_mir_proto_num; +}; + +#define zero_op c2m_ctx->gen_ctx->zero_op +#define one_op c2m_ctx->gen_ctx->one_op +#define minus_one_op c2m_ctx->gen_ctx->minus_one_op +#define curr_func c2m_ctx->gen_ctx->curr_func +#define reg_var_tab c2m_ctx->gen_ctx->reg_var_tab +#define reg_free_mark c2m_ctx->gen_ctx->reg_free_mark +#define continue_label c2m_ctx->gen_ctx->continue_label +#define break_label c2m_ctx->gen_ctx->break_label +#define mem_params c2m_ctx->gen_ctx->mem_params +#define init_els c2m_ctx->gen_ctx->init_els +#define memset_proto c2m_ctx->gen_ctx->memset_proto +#define memset_item c2m_ctx->gen_ctx->memset_item +#define memcpy_proto c2m_ctx->gen_ctx->memcpy_proto +#define memcpy_item c2m_ctx->gen_ctx->memcpy_item +#define call_ops c2m_ctx->gen_ctx->call_ops +#define switch_ops c2m_ctx->gen_ctx->switch_ops +#define switch_cases c2m_ctx->gen_ctx->switch_cases +#define curr_mir_proto_num c2m_ctx->gen_ctx->curr_mir_proto_num + +static op_t new_op (decl_t decl, MIR_op_t mir_op) { + op_t res; + + res.decl = decl; + res.mir_op = mir_op; + return res; +} + +static htab_hash_t reg_var_hash (reg_var_t r) { return mir_hash (r.name, strlen (r.name), 0x42); } +static int reg_var_eq (reg_var_t r1, reg_var_t r2) { 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); +} + +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 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); +} + +/* 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]; + 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; + + 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; + 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); + sh = 64 - op.decl->bit_offset - op.decl->width; + 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_insn (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_insn (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 size_val; + size_t mark; + symbol_t sym; + struct expr *sexpr; + init_el_t init_el; + int addr_p, found_p, 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.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; + + assert (var.mir_op.mode == MIR_OP_MEM); + mask = 0xffffffffffffffff >> (64 - width); + mask2 = ~(mask << var.decl->bit_offset); + 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 (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)); + } + 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 (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; + + mask = 0xffffffffffffffff >> (64 - width); + mask2 = ~(mask << bit_offset); + *u &= mask2; + v &= mask; + if (signed_integer_type_p (member_decl->decl_spec.type)) { + v <<= (64 - width); + v = (int64_t) v >> (64 - width); + } + v <<= bit_offset; + *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 (&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 (&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_insn_code_t insn_code; + MIR_type_t mir_type; + struct expr *e; + 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_insn (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_insn (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_insn (ctx, f_label); + emit2 (ctx, MIR_MOV, res.mir_op, zero_op.mir_op); + emit_insn (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_insn (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_insn (ctx, f_label); + emit2 (ctx, MIR_MOV, res.mir_op, one_op.mir_op); + emit_insn (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); + 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 (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 (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_insn (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_insn (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_insn (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); + op1 = gen (ctx, NL_EL (r->ops, 1), NULL, NULL, TRUE, NULL); + type = ((struct expr *) r->attr)->type; + 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, *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_insn (ctx, if_label); + gen (ctx, if_stmt, NULL, NULL, FALSE, NULL); + emit1 (ctx, MIR_JMP, MIR_new_label_op (ctx, end_label)); + emit_insn (ctx, else_label); + gen (ctx, else_stmt, NULL, NULL, FALSE, NULL); + emit_insn (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_insn (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_insn (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_insn (ctx, start_label); + gen (ctx, stmt, NULL, NULL, FALSE, NULL); + emit_insn (ctx, continue_label); + top_gen (ctx, expr, start_label, break_label); + emit_insn (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_insn (ctx, continue_label); + top_gen (ctx, expr, stmt_label, break_label); + emit_insn (ctx, stmt_label); + gen (ctx, stmt, NULL, NULL, FALSE, NULL); + emit1 (ctx, MIR_JMP, MIR_new_label_op (ctx, continue_label)); + emit_insn (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 start_label = MIR_new_label (ctx), 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); + emit_insn (ctx, start_label); + if (cond->code != N_IGNORE) /* non-empty condition: */ + top_gen (ctx, cond, stmt_label, break_label); + emit_insn (ctx, stmt_label); + gen (ctx, stmt, NULL, NULL, FALSE, NULL); + emit_insn (ctx, continue_label); + top_gen (ctx, iter, NULL, NULL); + emit1 (ctx, MIR_JMP, MIR_new_label_op (ctx, start_label)); + emit_insn (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); + MIR_reg_t ret_addr_reg; + + 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) { + 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) { + 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] != p1->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); + 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_DECIMAL_DIG, (long double) e->u.d_val); + } else if (signed_integer_type_p (e->type)) { + fprintf (f, "%lld", (long long) e->u.i_val); + } else { + fprintf (f, "%llu", (unsigned long long) e->u.u_val); + } + } +} + +static void print_node (MIR_context_t ctx, FILE *f, node_t n, int indent, int attr_p) { + int i; + + fprintf (f, "%6lu: ", n->uid); + for (i = 0; i < indent; i++) fprintf (f, " "); + if (n == err_node) { + fprintf (f, "\n"); + return; + } + fprintf (f, "%s (", get_node_name (n->code)); + print_pos (f, n->pos, FALSE); + fprintf (f, ")"); + switch (n->code) { + case N_IGNORE: fprintf (f, "\n"); break; + case N_I: fprintf (f, " %lld", (long long) n->u.l); goto expr; + case N_L: fprintf (f, " %lldl", (long long) n->u.l); goto expr; + case N_LL: fprintf (f, " %lldll", (long long) n->u.ll); goto expr; + case N_U: fprintf (f, " %lluu", (unsigned long long) n->u.ul); goto expr; + case N_UL: fprintf (f, " %lluul", (unsigned long long) n->u.ul); goto expr; + case N_ULL: fprintf (f, " %lluull", (unsigned long long) n->u.ull); goto expr; + case N_F: fprintf (f, " %.*g", FLT_DECIMAL_DIG, (double) n->u.f); goto expr; + case N_D: fprintf (f, " %.*g", DBL_DECIMAL_DIG, (double) n->u.d); goto expr; + case N_LD: fprintf (f, " %.*Lg", LDBL_DECIMAL_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; + + 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); + } +#ifdef __linux__ + 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)) { + c2m_ctx_t c2m_ctx = *c2m_ctx_loc (ctx); + + options = ops; + n_errors = n_warnings = 0; + c_getc = getc_func; + VARR_CREATE (char, symbol_text, 128); + VARR_CREATE (char, temp_string, 128); + parse_init (ctx); + curr_scope = NULL; + context_init (ctx); + init_include_dirs (ctx); + process_macro_commands (ctx); + VARR_CREATE (node_t, call_nodes, 128); /* used in context and gen */ + VARR_CREATE (node_t, containing_anon_members, 8); + VARR_CREATE (init_object_t, init_object_path, 8); +} + +static void compile_finish (MIR_context_t ctx) { + c2m_ctx_t c2m_ctx = *c2m_ctx_loc (ctx); + + if (symbol_text != NULL) VARR_DESTROY (char, symbol_text); + if (temp_string != NULL) VARR_DESTROY (char, temp_string); + parse_finish (c2m_ctx); + context_finish (ctx); + if (headers != NULL) VARR_DESTROY (char_ptr_t, headers); + if (system_headers != NULL) VARR_DESTROY (char_ptr_t, system_headers); + if (call_nodes != NULL) VARR_DESTROY (node_t, call_nodes); + if (containing_anon_members != NULL) VARR_DESTROY (node_t, containing_anon_members); + if (init_object_path != NULL) VARR_DESTROY (init_object_t, init_object_path); +} + +#include +#if defined(__unix__) || defined(__APPLE__) +#include +#endif + +static double real_usec_time (void) { + struct timeval tv; + + gettimeofday (&tv, NULL); + return tv.tv_usec + tv.tv_sec * 1000000.0; +} + +static const char *get_module_name (MIR_context_t ctx) { + c2m_ctx_t c2m_ctx = *c2m_ctx_loc (ctx); + static char str[50]; + + sprintf (str, "M%d", options->module_num); + return str; +} + +static int top_level_getc (c2m_ctx_t c2m_ctx) { return c_getc (); } + +int c2mir_compile (MIR_context_t ctx, struct c2mir_options *ops, int (*getc_func) (void), + 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; + const char *base_name; + + if (c2m_ctx == NULL) return 1; + if (setjmp (c2m_ctx->env)) { + compile_finish (ctx); + return 1; + } + compile_init (ctx, ops, getc_func); + 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 file %s\n", base_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: */ diff --git a/mir/c2mir/c2mir.h b/mir/c2mir/c2mir.h new file mode 100644 index 0000000..d0dccf0 --- /dev/null +++ b/mir/c2mir/c2mir.h @@ -0,0 +1,25 @@ +#include "mir.h" + +#define COMMAND_LINE_SOURCE_NAME "" +#define STDIN_SOURCE_NAME "" + +struct c2mir_macro_command { + int def_p; /* #define or #undef */ + const char *name, *def; /* def is used only when def_p is true */ +}; + +struct c2mir_options { + FILE *message_file; + int debug_p, verbose_p, no_prepro_p, prepro_only_p, syntax_only_p, pedantic_p, asm_p, object_p; + size_t module_num; + FILE *prepro_output_file; /* non-null for prepro_only_p */ + const char *output_file_name; + size_t macro_commands_num, include_dirs_num; + struct c2mir_macro_command *macro_commands; + const char **include_dirs; +}; + +void c2mir_init (MIR_context_t ctx); +void c2mir_finish (MIR_context_t ctx); +int c2mir_compile (MIR_context_t ctx, struct c2mir_options *ops, int (*getc_func) (void), + const char *source_name, FILE *output_file); diff --git a/mir/c2mir/mirc.h b/mir/c2mir/mirc.h new file mode 100644 index 0000000..ad13884 --- /dev/null +++ b/mir/c2mir/mirc.h @@ -0,0 +1,22 @@ +static const char mirc[] + = "#define __mirc__ 1\n" + "#define __STDC_HOSTED__ 1\n" + "//#define __STDC_ISO_10646__ 201103L\n" + "#define __STDC_NO_ATOMICS__ 1\n" + "#define __STDC_NO_COMPLEX__ 1\n" + "#define __STDC_NO_THREADS__ 1\n" + "#define __STDC_NO_VLA__ 1\n" + "#define __STDC_UTF_16__ 1\n" + "#define __STDC_UTF_32__ 1\n" + "#define __STDC_VERSION__ 201112L\n" + "#define __STDC__ 1\n" + "\n" + "/* Some GCC alternative keywords used but not defined in standard headers: */\n" + "#define __const const\n" + "#define __const__ const\n" + "#define __inline__ inline\n" + "#define __restrict__ restrict\n" + "#define __signed signed\n" + "#define __signed__ signed\n" + "#define __volatile volatile\n" + "#define __volatile__ volatile\n"; diff --git a/mir/c2mir/x86_64/cx86_64-code.c b/mir/c2mir/x86_64/cx86_64-code.c new file mode 100644 index 0000000..98d0905 --- /dev/null +++ b/mir/c2mir/x86_64/cx86_64-code.c @@ -0,0 +1,24 @@ +/* This file is a part of MIR project. + Copyright (C) 2018, 2019 Vladimir Makarov . +*/ + +#include "../mirc.h" +#include "mirc-x86_64-linux.h" + +static const char *standard_includes[] = {mirc, x86_64_mirc}; + +static const char *standard_include_dirs[] = {"include/mirc/", "include/mirc/x86-64/"}; + +#define MAX_ALIGNMENT 16 + +#define ADJUST_VAR_ALIGNMENT(c2m_ctx, align, type) x86_adjust_var_alignment (c2m_ctx, align, type) + +static int x86_adjust_var_alignment (c2m_ctx_t c2m_ctx, int align, struct type *type) { + /* see https://www.uclibc.org/docs/psABI-x86_64.pdf */ + if (type->mode == TM_ARR && raw_type_size (c2m_ctx, type) >= 16) return 16; + return align; +} + +static int invalid_alignment (mir_llong align) { + return align != 0 && align != 1 && align != 2 && align != 4 && align != 8 && align != 16; +} diff --git a/mir/c2mir/x86_64/cx86_64.h b/mir/c2mir/x86_64/cx86_64.h new file mode 100644 index 0000000..4464976 --- /dev/null +++ b/mir/c2mir/x86_64/cx86_64.h @@ -0,0 +1,50 @@ +/* This file is a part of MIR project. + Copyright (C) 2018, 2019 Vladimir Makarov . +*/ + +#include + +#define MIR_CHAR_BIT 8 + +typedef int8_t mir_schar; +typedef int16_t mir_short; +typedef int32_t mir_int; +typedef int64_t mir_long; +typedef int64_t mir_llong; + +#define MIR_SCHAR_MIN INT8_MIN +#define MIR_SCHAR_MAX INT8_MAX +#define MIR_SHORT_MIN INT16_MIN +#define MIR_SHORT_MAX INT16_MAX +#define MIR_INT_MIN INT32_MIN +#define MIR_INT_MAX INT32_MAX +#define MIR_LONG_MIN INT64_MIN +#define MIR_LONG_MAX INT64_MAX +#define MIR_LLONG_MIN INT64_MIN +#define MIR_LLONG_MAX INT64_MAX + +typedef uint8_t mir_uchar; +typedef uint16_t mir_ushort; +typedef uint32_t mir_uint; +typedef uint64_t mir_ulong; +typedef uint64_t mir_ullong; + +#define MIR_UCHAR_MAX UINT8_MAX +#define MIR_USHORT_MAX UINT16_MAX +#define MIR_UINT_MAX UINT32_MAX +#define MIR_ULONG_MAX UINT64_MAX +#define MIR_ULLONG_MAX UINT64_MAX + +typedef mir_schar mir_char; +#define MIR_CHAR_MIN MIR_SCHAR_MIN +#define MIR_CHAR_MAX MIR_SCHAR_MAX + +typedef float mir_float; +typedef double mir_double; +typedef long double mir_ldouble; + +typedef uint8_t mir_bool; +typedef int64_t mir_ptrdiff_t; +typedef uint64_t mir_size_t; + +#define MIR_SIZE_MAX UINT64_MAX diff --git a/mir/c2mir/x86_64/mirc-x86_64-linux.h b/mir/c2mir/x86_64/mirc-x86_64-linux.h new file mode 100644 index 0000000..bc121f0 --- /dev/null +++ b/mir/c2mir/x86_64/mirc-x86_64-linux.h @@ -0,0 +1,89 @@ +static char x86_64_mirc[] + = "#define __amd64 1\n" + "#define __amd64__ 1\n" + "#define _LP64 1\n" + "#define __LP64__ 1\n" + "#define __x86_64 1\n" + "#define __x86_64__ 1\n" + "\n" + "#define __SIZEOF_DOUBLE__ 8\n" + "#define __SIZEOF_FLOAT__ 4\n" + "#define __SIZEOF_INT__ 4\n" + "#define __SIZEOF_LONG_DOUBLE__ 8\n" + "#define __SIZEOF_LONG_LONG__ 8\n" + "#define __SIZEOF_LONG__ 8\n" + "#define __SIZEOF_POINTER__ 8\n" + "#define __SIZEOF_PTRDIFF_T__ 8\n" + "#define __SIZEOF_SHORT__ 2\n" + "#define __SIZEOF_SIZE_T__ 8\n" + "\n" + "#define __BYTE_ORDER__ 1234\n" + "#define __ORDER_LITTLE_ENDIAN__ 1234\n" + "#define __ORDER_BIG_ENDIAN__ 4321\n" + "\n" + "/* Some GCC predefined macros: */\n" + "#define __SIZE_TYPE__ unsigned long\n" + "#define __PTRDIFF_TYPE__ long\n" + "#define __INTMAX_TYPE__ long\n" + "#define __UINTMAX_TYPE__ unsigned long\n" + "#define __INT8_TYPE__ signed char\n" + "#define __INT16_TYPE__ short\n" + "#define __INT32_TYPE__ int\n" + "#define __INT64_TYPE__ long\n" + "#define __UINT8_TYPE__ unsigned char\n" + "#define __UINT16_TYPE__ unsigned short\n" + "#define __UINT32_TYPE__ unsigned int\n" + "#define __UINT64_TYPE__ unsigned long\n" + "#define __INTPTR_TYPE__ long\n" + "#define __UINTPTR_TYPE__ unsigned long\n" + "\n" + "#define __CHAR_BIT__ 8\n" + "#define __INT8_MAX__ 127\n" + "#define __INT16_MAX__ 32767\n" + "#define __INT32_MAX__ 2147483647\n" + "#define __INT64_MAX__ 9223372036854775807l\n" + "#define __UINT8_MAX__ (__INT8_MAX__ * 2u + 1u)\n" + "#define __UINT16_MAX__ (__INT16_MAX__ * 2u + 1u)\n" + "#define __UINT32_MAX__ (__INT32_MAX__ * 2u + 1u)\n" + "#define __UINT64_MAX__ (__INT64_MAX__ * 2u + 1u)\n" + "#define __SCHAR_MAX__ __INT8_MAX__\n" + "#define __SHRT_MAX__ __INT16_MAX__\n" + "#define __INT_MAX__ __INT32_MAX__\n" + "#define __LONG_MAX__ __INT64_MAX__\n" + "#define __LONG_LONG_MAX__ __INT64_MAX__\n" + "#define __SIZE_MAX__ __UINT64_MAX__\n" + "#define __PTRDIFF_MAX__ __INT64_MAX__\n" + "#define __INTMAX_MAX__ __INT64_MAX__\n" + "#define __UINTMAX_MAX__ __UINT64_MAX__\n" + "#define __INTPTR_MAX__ __INT64_MAX__\n" + "#define __UINTPTR_MAX__ __UINT64_MAX__\n" + "\n" + "#define __FLT_MIN_EXP__ (-125)\n" + "#define __FLT_MAX_EXP__ 128\n" + "#define __FLT_DIG__ 6\n" + "#define __FLT_DECIMAL_DIG__ 9\n" + "#define __FLT_MANT_DIG__ 24\n" + "#define __FLT_MIN__ 1.17549435082228750796873653722224568e-38F\n" + "#define __FLT_MAX__ 3.40282346638528859811704183484516925e+38F\n" + "#define __FLT_EPSILON__ 1.19209289550781250000000000000000000e-7F\n" + "\n" + "#define __DBL_MIN_EXP__ (-1021)\n" + "#define __DBL_MAX_EXP__ 1024\n" + "#define __DBL_DIG__ 15\n" + "#define __DBL_DECIMAL_DIG__ 17\n" + "#define __DBL_MANT_DIG__ 53\n" + "#define __DBL_MAX__ ((double) 1.79769313486231570814527423731704357e+308L)\n" + "#define __DBL_MIN__ ((double) 2.22507385850720138309023271733240406e-308L)\n" + "#define __DBL_EPSILON__ ((double) 2.22044604925031308084726333618164062e-16L)\n" + "\n" + "typedef unsigned short char16_t;\n" + "typedef unsigned int char32_t;\n" + "\n" + "#define __gnu_linux__ 1\n" + "#define __linux 1\n" + "#define __linux__ 1\n" + "#define __unix 1\n" + "#define __unix__ 1\n" + "#define linux 1\n" + "\n" + "void *alloca (unsigned long);\n"; diff --git a/mir/mir-bitmap.h b/mir/mir-bitmap.h new file mode 100644 index 0000000..fde9564 --- /dev/null +++ b/mir/mir-bitmap.h @@ -0,0 +1,286 @@ +/* This file is a part of MIR project. + Copyright (C) 2018, 2019 Vladimir Makarov . +*/ + +#ifndef MIR_BITMAP_H + +#define MIR_BITMAP_H + +#include +#include +#include +#include +#include +#include "mir-varr.h" + +#define FALSE 0 +#define TRUE 1 + +#if !defined(BITMAP_ENABLE_CHECKING) && !defined(NDEBUG) +#define BITMAP_ENABLE_CHECKING +#endif + +#ifndef BITMAP_ENABLE_CHECKING +#define BITMAP_ASSERT(EXPR, OP) ((void) (EXPR)) + +#else +static inline void mir_bitmap_assert_fail (const char *op) { + fprintf (stderr, "wrong %s for a bitmap", op); + assert (0); +} + +#define BITMAP_ASSERT(EXPR, OP) (void) ((EXPR) ? 0 : (mir_bitmap_assert_fail (#OP), 0)) + +#endif + +#define BITMAP_WORD_BITS 64 + +typedef uint64_t bitmap_el_t; + +DEF_VARR (bitmap_el_t); + +typedef VARR (bitmap_el_t) * bitmap_t; +typedef const VARR (bitmap_el_t) * const_bitmap_t; + +static inline bitmap_t bitmap_create2 (size_t init_bits_num) { + bitmap_t bm; + + VARR_CREATE (bitmap_el_t, bm, (init_bits_num + BITMAP_WORD_BITS - 1) / BITMAP_WORD_BITS); + return bm; +} + +static inline bitmap_t bitmap_create (void) { return bitmap_create2 (0); } + +static inline void bitmap_destroy (bitmap_t bm) { VARR_DESTROY (bitmap_el_t, bm); } + +static inline void bitmap_clear (bitmap_t bm) { VARR_TRUNC (bitmap_el_t, bm, 0); } + +static inline void bitmap_expand (bitmap_t bm, size_t nb) { + size_t i, len = VARR_LENGTH (bitmap_el_t, bm); + size_t new_len = (nb + BITMAP_WORD_BITS - 1) / BITMAP_WORD_BITS; + + for (i = len; i < new_len; i++) VARR_PUSH (bitmap_el_t, bm, (bitmap_el_t) 0); +} + +static inline int bitmap_bit_p (const_bitmap_t bm, size_t nb) { + size_t nw, sh, len = VARR_LENGTH (bitmap_el_t, bm); + bitmap_el_t *addr = VARR_ADDR (bitmap_el_t, bm); + + if (nb >= BITMAP_WORD_BITS * len) return 0; + nw = nb / BITMAP_WORD_BITS; + sh = nb % BITMAP_WORD_BITS; + return (addr[nw] >> sh) & 1; +} + +static inline int bitmap_set_bit_p (bitmap_t bm, size_t nb) { + size_t nw, sh; + bitmap_el_t *addr; + int res; + + bitmap_expand (bm, nb + 1); + addr = VARR_ADDR (bitmap_el_t, bm); + nw = nb / BITMAP_WORD_BITS; + sh = nb % BITMAP_WORD_BITS; + res = ((addr[nw] >> sh) & 1) == 0; + addr[nw] |= (bitmap_el_t) 1 << sh; + return res; +} + +static inline int bitmap_clear_bit_p (bitmap_t bm, size_t nb) { + size_t nw, sh, len = VARR_LENGTH (bitmap_el_t, bm); + bitmap_el_t *addr = VARR_ADDR (bitmap_el_t, bm); + int res; + + if (nb >= BITMAP_WORD_BITS * len) return 0; + nw = nb / BITMAP_WORD_BITS; + sh = nb % BITMAP_WORD_BITS; + res = (addr[nw] >> sh) & 1; + addr[nw] &= ~((bitmap_el_t) 1 << sh); + return res; +} + +static inline void bitmap_copy (bitmap_t dst, const_bitmap_t src) { + size_t dst_len = VARR_LENGTH (bitmap_el_t, dst); + size_t src_len = VARR_LENGTH (bitmap_el_t, src); + + if (dst_len >= src_len) + VARR_TRUNC (bitmap_el_t, dst, src_len); + else + bitmap_expand (dst, src_len * BITMAP_WORD_BITS); + memcpy (VARR_ADDR (bitmap_el_t, dst), VARR_ADDR (bitmap_el_t, src), + src_len * sizeof (bitmap_el_t)); +} + +static inline int bitmap_equal_p (const_bitmap_t bm1, const_bitmap_t bm2) { + const_bitmap_t temp_bm; + size_t i, temp_len, bm1_len = VARR_LENGTH (bitmap_el_t, bm1); + size_t bm2_len = VARR_LENGTH (bitmap_el_t, bm2); + bitmap_el_t *addr1, *addr2; + + if (bm1_len > bm2_len) { + temp_bm = bm1; + bm1 = bm2; + bm2 = temp_bm; + temp_len = bm1_len; + bm1_len = bm2_len; + bm2_len = temp_len; + } + addr1 = VARR_ADDR (bitmap_el_t, bm1); + addr2 = VARR_ADDR (bitmap_el_t, bm2); + if (memcmp (addr1, addr2, bm1_len * sizeof (bitmap_el_t)) != 0) return FALSE; + for (i = bm1_len; i < bm2_len; i++) + if (addr2[i] != 0) return FALSE; + return TRUE; +} + +static inline int bitmap_intersect_p (const_bitmap_t bm1, const_bitmap_t bm2) { + size_t i, min_len, bm1_len = VARR_LENGTH (bitmap_el_t, bm1); + size_t bm2_len = VARR_LENGTH (bitmap_el_t, bm2); + bitmap_el_t *addr1 = VARR_ADDR (bitmap_el_t, bm1); + bitmap_el_t *addr2 = VARR_ADDR (bitmap_el_t, bm2); + + min_len = bm1_len <= bm2_len ? bm1_len : bm2_len; + for (i = 0; i < min_len; i++) + if ((addr1[i] & addr2[i]) != 0) return TRUE; + return FALSE; +} + +static inline int bitmap_empty_p (const_bitmap_t bm) { + size_t i, len = VARR_LENGTH (bitmap_el_t, bm); + bitmap_el_t *addr = VARR_ADDR (bitmap_el_t, bm); + + for (i = 0; i < len; i++) + if (addr[i] != 0) return FALSE; + return TRUE; +} + +static inline bitmap_el_t bitmap_el_max3 (bitmap_el_t el1, bitmap_el_t el2, bitmap_el_t el3) { + if (el1 <= el2) return el2 < el3 ? el3 : el2; + return el1 < el3 ? el3 : el1; +} + +static inline bitmap_el_t bitmap_el_max4 (bitmap_el_t el1, bitmap_el_t el2, bitmap_el_t el3, + bitmap_el_t el4) { + if (el1 <= el2) return bitmap_el_max3 (el2, el3, el4); + return bitmap_el_max3 (el1, el3, el4); +} + +/* Return the number of bits set in BM. */ +static inline size_t bitmap_bit_count (const_bitmap_t bm) { + size_t i, len = VARR_LENGTH (bitmap_el_t, bm); + bitmap_el_t el, *addr = VARR_ADDR (bitmap_el_t, bm); + size_t count = 0; + + for (i = 0; i < len; i++) { + if ((el = addr[i]) != 0) { + for (; el != 0; el >>= 1) + if (el & 1) count++; + } + } + return count; +} + +static inline int bitmap_op2 (bitmap_t dst, const_bitmap_t src1, const_bitmap_t src2, + bitmap_el_t (*op) (bitmap_el_t, bitmap_el_t)) { + size_t i, len, bound, src1_len, src2_len; + bitmap_el_t old, *dst_addr, *src1_addr, *src2_addr; + int change_p = FALSE; + + src1_len = VARR_LENGTH (bitmap_el_t, src1); + src2_len = VARR_LENGTH (bitmap_el_t, src2); + len = bitmap_el_max3 (VARR_LENGTH (bitmap_el_t, dst), src1_len, src2_len); + bitmap_expand (dst, len * BITMAP_WORD_BITS); + dst_addr = VARR_ADDR (bitmap_el_t, dst); + src1_addr = VARR_ADDR (bitmap_el_t, src1); + src2_addr = VARR_ADDR (bitmap_el_t, src2); + for (bound = i = 0; i < len; i++) { + old = dst_addr[i]; + if ((dst_addr[i] = op (i >= src1_len ? 0 : src1_addr[i], i >= src2_len ? 0 : src2_addr[i])) + != 0) + bound = i + 1; + if (old != dst_addr[i]) change_p = TRUE; + } + VARR_TRUNC (bitmap_el_t, dst, bound); + return change_p; +} + +static inline bitmap_el_t bitmap_el_and (bitmap_el_t el1, bitmap_el_t el2) { return el1 & el2; } + +static inline int bitmap_and (bitmap_t dst, bitmap_t src1, bitmap_t src2) { + return bitmap_op2 (dst, src1, src2, bitmap_el_and); +} + +static inline bitmap_el_t bitmap_el_and_compl (bitmap_el_t el1, bitmap_el_t el2) { + return el1 & ~el2; +} + +static inline int bitmap_and_compl (bitmap_t dst, bitmap_t src1, bitmap_t src2) { + return bitmap_op2 (dst, src1, src2, bitmap_el_and_compl); +} + +static inline bitmap_el_t bitmap_el_ior (bitmap_el_t el1, bitmap_el_t el2) { return el1 | el2; } + +static inline int bitmap_ior (bitmap_t dst, bitmap_t src1, bitmap_t src2) { + return bitmap_op2 (dst, src1, src2, bitmap_el_ior); +} + +static inline int bitmap_op3 (bitmap_t dst, const_bitmap_t src1, const_bitmap_t src2, + const_bitmap_t src3, + bitmap_el_t (*op) (bitmap_el_t, bitmap_el_t, bitmap_el_t)) { + size_t i, len, bound, src1_len, src2_len, src3_len; + bitmap_el_t old, *dst_addr, *src1_addr, *src2_addr, *src3_addr; + int change_p = FALSE; + + src1_len = VARR_LENGTH (bitmap_el_t, src1); + src2_len = VARR_LENGTH (bitmap_el_t, src2); + src3_len = VARR_LENGTH (bitmap_el_t, src3); + len = bitmap_el_max4 (VARR_LENGTH (bitmap_el_t, dst), src1_len, src2_len, src3_len); + bitmap_expand (dst, len * BITMAP_WORD_BITS); + dst_addr = VARR_ADDR (bitmap_el_t, dst); + src1_addr = VARR_ADDR (bitmap_el_t, src1); + src2_addr = VARR_ADDR (bitmap_el_t, src2); + src3_addr = VARR_ADDR (bitmap_el_t, src3); + for (bound = i = 0; i < len; i++) { + old = dst_addr[i]; + if ((dst_addr[i] = op (i >= src1_len ? 0 : src1_addr[i], i >= src2_len ? 0 : src2_addr[i], + i >= src3_len ? 0 : src3_addr[i])) + != 0) + bound = i + 1; + if (old != dst_addr[i]) change_p = TRUE; + } + VARR_TRUNC (bitmap_el_t, dst, bound); + return change_p; +} + +static inline bitmap_el_t bitmap_el_ior_and (bitmap_el_t el1, bitmap_el_t el2, bitmap_el_t el3) { + return el1 | (el2 & el3); +} + +/* DST = SRC1 | (SRC2 & SRC3). Return true if DST changed. */ +static inline int bitmap_ior_and (bitmap_t dst, bitmap_t src1, bitmap_t src2, bitmap_t src3) { + return bitmap_op3 (dst, src1, src2, src3, bitmap_el_ior_and); +} + +static inline bitmap_el_t bitmap_el_ior_and_compl (bitmap_el_t el1, bitmap_el_t el2, + bitmap_el_t el3) { + return el1 | (el2 & ~el3); +} + +/* DST = SRC1 | (SRC2 & ~SRC3). Return true if DST changed. */ +static inline int bitmap_ior_and_compl (bitmap_t dst, bitmap_t src1, bitmap_t src2, bitmap_t src3) { + return bitmap_op3 (dst, src1, src2, src3, bitmap_el_ior_and_compl); +} + +static inline void bitmap_for_each (bitmap_t bm, void (*func) (size_t, void *), void *data) { + size_t i, nb, len = VARR_LENGTH (bitmap_el_t, bm); + bitmap_el_t el, *addr = VARR_ADDR (bitmap_el_t, bm); + + for (i = 0; i < len; i++) { + if ((el = addr[i]) != 0) { + for (nb = 0; el != 0; el >>= 1, nb++) + if (el & 1) func (i * BITMAP_WORD_BITS + nb, data); + } + } +} + +#endif /* #ifndef MIR_BITMAP_H */ diff --git a/mir/mir-dlist.h b/mir/mir-dlist.h new file mode 100644 index 0000000..6da1972 --- /dev/null +++ b/mir/mir-dlist.h @@ -0,0 +1,175 @@ +/* This file is part of MIR project. + Copyright (C) 2018, 2019 Vladimir Makarov . +*/ + +/* Typed doubly linked lists. */ + +#ifndef MIR_DLIST_H + +#define MIR_DLIST_H + +#include +#include + +#if !defined(DLIST_ENABLE_CHECKING) && !defined(NDEBUG) +#define DLIST_ENABLE_CHECKING +#endif + +#ifndef DLIST_ENABLE_CHECKING +#define DLIST_ASSERT(EXPR, OP, T) ((void) (EXPR)) + +#else +static inline void dlist_assert_fail (const char* op, const char* var) { + fprintf (stderr, "wrong %s for %s", op, var); + assert (0); +} + +#define DLIST_ASSERT(EXPR, OP, T) (void) ((EXPR) ? 0 : (dlist_assert_fail (OP, #T), 0)) + +#endif + +#define DLIST(T) DLIST_##T +#define DLIST_OP(T, OP) DLIST_##T##_##OP +#define DLIST_LINK(T) DLIST_LINK_##T + +#define DLIST_LINK_T(T) \ + typedef struct DLIST_LINK (T) { \ + T prev, next; \ + } DLIST_LINK (T) + +#define DEF_DLIST_LINK(T) DLIST_LINK_T (T); + +#define DEF_DLIST_TYPE(T) \ + typedef struct DLIST (T) { \ + T head, tail; \ + } DLIST (T) + +#define DEF_DLIST_CODE(T, LINK) \ + \ + static inline void DLIST_OP (T, init) (DLIST (T) * list) { list->head = list->tail = NULL; } \ + \ + static inline T DLIST_OP (T, head) (DLIST (T) * list) { return list->head; } \ + \ + static inline T DLIST_OP (T, tail) (DLIST (T) * list) { return list->tail; } \ + \ + static inline T DLIST_OP (T, prev) (T elem) { return elem->LINK.prev; } \ + static inline T DLIST_OP (T, next) (T elem) { return elem->LINK.next; } \ + \ + static inline T DLIST_OP (T, el) (DLIST (T) * list, int n) { \ + T e; \ + \ + if (n >= 0) { \ + for (e = list->head; e != NULL && n != 0; e = e->LINK.next, n--) \ + ; \ + } else { \ + for (e = list->tail; e != NULL && n != -1; e = e->LINK.prev, n++) \ + ; \ + } \ + return e; \ + } \ + \ + static inline void DLIST_OP (T, prepend) (DLIST (T) * list, T elem) { \ + DLIST_ASSERT (list&& elem, "prepend", T); \ + if (list->head == NULL) { \ + DLIST_ASSERT (list->tail == NULL, "prepend", T); \ + list->tail = elem; \ + } else { \ + DLIST_ASSERT (list->head->LINK.prev == NULL, "prepend", T); \ + list->head->LINK.prev = elem; \ + } \ + elem->LINK.prev = NULL; \ + elem->LINK.next = list->head; \ + list->head = elem; \ + } \ + \ + static inline void DLIST_OP (T, append) (DLIST (T) * list, T elem) { \ + DLIST_ASSERT (list&& elem, "append", T); \ + if (list->tail == NULL) { \ + DLIST_ASSERT (list->head == NULL, "append", T); \ + list->head = elem; \ + } else { \ + DLIST_ASSERT (list->tail->LINK.next == NULL, "append", T); \ + list->tail->LINK.next = elem; \ + } \ + elem->LINK.next = NULL; \ + elem->LINK.prev = list->tail; \ + list->tail = elem; \ + } \ + \ + static inline void DLIST_OP (T, insert_before) (DLIST (T) * list, T before, T elem) { \ + DLIST_ASSERT (list&& before&& elem && list->tail, "insert_before", T); \ + if (before->LINK.prev == NULL) { \ + DLIST_ASSERT (list->head == before, "insert_before", T); \ + before->LINK.prev = elem; \ + elem->LINK.next = before; \ + elem->LINK.prev = NULL; \ + list->head = elem; \ + } else { \ + DLIST_ASSERT (list->head, "insert_before", T); \ + before->LINK.prev->LINK.next = elem; \ + elem->LINK.prev = before->LINK.prev; \ + before->LINK.prev = elem; \ + elem->LINK.next = before; \ + } \ + } \ + \ + static inline void DLIST_OP (T, insert_after) (DLIST (T) * list, T after, T elem) { \ + DLIST_ASSERT (list&& after&& elem && list->head, "insert_after", T); \ + if (after->LINK.next == NULL) { \ + DLIST_ASSERT (list->tail == after, "insert_after", T); \ + after->LINK.next = elem; \ + elem->LINK.prev = after; \ + elem->LINK.next = NULL; \ + list->tail = elem; \ + } else { \ + DLIST_ASSERT (list->tail, "insert_after", T); \ + after->LINK.next->LINK.prev = elem; \ + elem->LINK.next = after->LINK.next; \ + after->LINK.next = elem; \ + elem->LINK.prev = after; \ + } \ + } \ + \ + static inline void DLIST_OP (T, remove) (DLIST (T) * list, T elem) { \ + DLIST_ASSERT (list&& elem, "remove", T); \ + if (elem->LINK.prev != NULL) { \ + elem->LINK.prev->LINK.next = elem->LINK.next; \ + } else { \ + DLIST_ASSERT (list->head == elem, "remove", T); \ + list->head = elem->LINK.next; \ + } \ + if (elem->LINK.next != NULL) { \ + elem->LINK.next->LINK.prev = elem->LINK.prev; \ + } else { \ + DLIST_ASSERT (list->tail == elem, "remove", T); \ + list->tail = elem->LINK.prev; \ + } \ + elem->LINK.prev = elem->LINK.next = NULL; \ + } \ + \ + static inline size_t DLIST_OP (T, length) (DLIST (T) * list) { \ + size_t len = 0; \ + T curr; \ + \ + for (curr = list->head; curr != NULL; curr = curr->LINK.next) len++; \ + return len; \ + } + +#define DEF_DLIST(T, LINK) \ + DEF_DLIST_TYPE (T); \ + DEF_DLIST_CODE (T, LINK) + +#define DLIST_INIT(T, L) (DLIST_OP (T, init) (&(L))) +#define DLIST_HEAD(T, L) (DLIST_OP (T, head) (&(L))) +#define DLIST_TAIL(T, L) (DLIST_OP (T, tail) (&(L))) +#define DLIST_PREV(T, E) (DLIST_OP (T, prev) (E)) +#define DLIST_NEXT(T, E) (DLIST_OP (T, next) (E)) +#define DLIST_EL(T, L, N) (DLIST_OP (T, el) (&(L), N)) +#define DLIST_PREPEND(T, L, E) (DLIST_OP (T, prepend) (&(L), (E))) +#define DLIST_APPEND(T, L, E) (DLIST_OP (T, append) (&(L), (E))) +#define DLIST_INSERT_BEFORE(T, L, B, E) (DLIST_OP (T, insert_before) (&(L), (B), (E))) +#define DLIST_INSERT_AFTER(T, L, A, E) (DLIST_OP (T, insert_after) (&(L), (A), (E))) +#define DLIST_REMOVE(T, L, E) (DLIST_OP (T, remove) (&(L), (E))) +#define DLIST_LENGTH(T, L) (DLIST_OP (T, length) (&(L))) + +#endif /* #ifndef MIR_DLIST_H */ diff --git a/mir/mir-gen-x86_64.c b/mir/mir-gen-x86_64.c new file mode 100644 index 0000000..c0c725e --- /dev/null +++ b/mir/mir-gen-x86_64.c @@ -0,0 +1,1906 @@ +/* This file is a part of MIR project. + Copyright (C) 2018, 2019 Vladimir Makarov . +*/ + +#include + +#define HREG_EL(h) h##_HARD_REG +#define REP_SEP , +enum { + REP8 (HREG_EL, AX, CX, DX, BX, SP, BP, SI, DI), + REP8 (HREG_EL, R8, R9, R10, R11, R12, R13, R14, R15), + REP8 (HREG_EL, XMM0, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6, XMM7), + REP8 (HREG_EL, XMM8, XMM9, XMM10, XMM11, XMM12, XMM13, XMM14, XMM15), + REP2 (HREG_EL, ST0, ST1), +}; +#undef REP_SEP + +static const MIR_reg_t MAX_HARD_REG = ST1_HARD_REG; +static const MIR_reg_t HARD_REG_FRAME_POINTER = BP_HARD_REG; + +static int locs_num (MIR_reg_t loc, MIR_type_t type) { + return loc > MAX_HARD_REG && type == MIR_T_LD ? 2 : 1; +} + +/* Hard regs not used in machinized code, preferably call used ones. */ +const MIR_reg_t TEMP_INT_HARD_REG1 = R10_HARD_REG, TEMP_INT_HARD_REG2 = R11_HARD_REG; +const MIR_reg_t TEMP_FLOAT_HARD_REG1 = XMM8_HARD_REG, TEMP_FLOAT_HARD_REG2 = XMM9_HARD_REG; +const MIR_reg_t TEMP_DOUBLE_HARD_REG1 = XMM8_HARD_REG, TEMP_DOUBLE_HARD_REG2 = XMM9_HARD_REG; +const MIR_reg_t TEMP_LDOUBLE_HARD_REG1 = MIR_NON_HARD_REG; +const MIR_reg_t TEMP_LDOUBLE_HARD_REG2 = MIR_NON_HARD_REG; + +static inline int hard_reg_type_ok_p (MIR_reg_t hard_reg, MIR_type_t type) { + assert (hard_reg <= MAX_HARD_REG); + /* For LD we need x87 stack regs and it is too complicated so no + hard register allocation for LD: */ + if (type == MIR_T_LD) return FALSE; + return type == MIR_T_F || type == MIR_T_D ? hard_reg >= XMM0_HARD_REG : hard_reg < XMM0_HARD_REG; +} + +static inline int fixed_hard_reg_p (MIR_reg_t hard_reg) { + assert (hard_reg <= MAX_HARD_REG); + return (hard_reg == BP_HARD_REG || hard_reg == SP_HARD_REG || hard_reg == TEMP_INT_HARD_REG1 + || hard_reg == TEMP_INT_HARD_REG2 || hard_reg == TEMP_FLOAT_HARD_REG1 + || hard_reg == TEMP_FLOAT_HARD_REG2 || hard_reg == TEMP_DOUBLE_HARD_REG1 + || hard_reg == TEMP_DOUBLE_HARD_REG2 || hard_reg == ST0_HARD_REG + || hard_reg == ST1_HARD_REG); +} + +static inline int call_used_hard_reg_p (MIR_reg_t hard_reg) { + assert (hard_reg <= MAX_HARD_REG); + return !(hard_reg == BX_HARD_REG || (hard_reg >= R12_HARD_REG && hard_reg <= R15_HARD_REG)); +} + +/* Stack layout (sp refers to the last reserved stack slot address) + from higher address to lower address memory: + + | ... | prev func stack frame (start address should be aligned to 16 bytes) + |---------------| + | return pc | value of sp before prologue = start sp hard reg + |---------------| + | old bp | bp for previous func stack frame; new bp refers for here + |---------------| + | reg save | 176 bytes + | area | optional area for vararg func reg save area + |---------------| + | slots assigned| can be absent for small functions (known only after RA) + | to pseudos | + |---------------| + | saved regs | callee saved regs used in the func (known only after RA) + |---------------| + | alloca areas | optional + |---------------| + | slots for | dynamically allocated/deallocated by caller + | passing args | + + size of slots and saved regs is multiple of 16 bytes + + */ + +static const int reg_save_area_size = 176; + +static MIR_disp_t get_stack_slot_offset (MIR_context_t ctx, MIR_type_t type, MIR_reg_t slot) { + /* slot is 0, 1, ... */ + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + + return -((MIR_disp_t) (slot + (type == MIR_T_LD ? 2 : 1)) * 8 + + (curr_func_item->u.func->vararg_p ? reg_save_area_size : 0)); +} + +static const MIR_insn_code_t io_dup_op_insn_codes[] = { + /* see possible patterns */ + MIR_FADD, MIR_DADD, MIR_LDADD, MIR_SUB, MIR_SUBS, MIR_FSUB, MIR_DSUB, MIR_LDSUB, + MIR_MUL, MIR_MULS, MIR_FMUL, MIR_DMUL, MIR_LDMUL, MIR_DIV, MIR_DIVS, MIR_UDIV, + MIR_FDIV, MIR_DDIV, MIR_LDDIV, MIR_MOD, MIR_MODS, MIR_UMOD, MIR_UMODS, MIR_AND, + MIR_ANDS, MIR_OR, MIR_ORS, MIR_XOR, MIR_XORS, MIR_LSH, MIR_LSHS, MIR_RSH, + MIR_RSHS, MIR_URSH, MIR_URSHS, MIR_NEG, MIR_NEGS, MIR_FNEG, MIR_DNEG, MIR_LDNEG, +}; + +typedef enum { GC_INSN_PUSH = MIR_INSN_BOUND, GC_INSN_BOUND } MIR_full_insn_code_t; + +static MIR_insn_code_t get_ext_code (MIR_type_t type) { + switch (type) { + case MIR_T_I8: return MIR_EXT8; + case MIR_T_U8: return MIR_UEXT8; + case MIR_T_I16: return MIR_EXT16; + case MIR_T_U16: return MIR_UEXT16; + case MIR_T_I32: return MIR_EXT32; + case MIR_T_U32: return MIR_UEXT32; + default: return MIR_INVALID_INSN; + } +} + +static MIR_reg_t get_arg_reg (MIR_type_t arg_type, size_t *int_arg_num, size_t *fp_arg_num, + MIR_insn_code_t *mov_code) { + MIR_reg_t arg_reg; + + if (arg_type == MIR_T_LD) { + arg_reg = MIR_NON_HARD_REG; + *mov_code = MIR_LDMOV; + } else if (arg_type == MIR_T_F || arg_type == MIR_T_D) { + switch (*fp_arg_num) { + case 0: + case 1: + case 2: + case 3: +#ifndef _WIN64 + case 4: + case 5: + case 6: + case 7: +#endif + arg_reg = XMM0_HARD_REG + *fp_arg_num; + break; + default: arg_reg = MIR_NON_HARD_REG; break; + } + (*fp_arg_num)++; + *mov_code = arg_type == MIR_T_F ? MIR_FMOV : MIR_DMOV; + } else { + switch (*int_arg_num +#ifdef _WIN64 + + 2 +#endif + ) { + case 0: arg_reg = DI_HARD_REG; break; + case 1: arg_reg = SI_HARD_REG; break; +#ifdef _WIN64 + case 2: arg_reg = CX_HARD_REG; break; + case 3: arg_reg = DX_HARD_REG; break; +#else + case 2: arg_reg = DX_HARD_REG; break; + case 3: arg_reg = CX_HARD_REG; break; +#endif + case 4: arg_reg = R8_HARD_REG; break; + case 5: arg_reg = R9_HARD_REG; break; + default: arg_reg = MIR_NON_HARD_REG; break; + } + (*int_arg_num)++; + *mov_code = MIR_MOV; + } + return arg_reg; +} + +static void machinize_call (MIR_context_t ctx, MIR_insn_t call_insn) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + MIR_func_t func = curr_func_item->u.func; + MIR_proto_t proto = call_insn->ops[0].u.ref->u.proto; + size_t nargs, nops = MIR_insn_nops (ctx, call_insn), start = proto->nres + 2; + size_t int_arg_num = 0, fp_arg_num = 0, mem_size = 0, xmm_args = 0; + MIR_type_t type, mem_type; + MIR_op_mode_t mode; + MIR_var_t *arg_vars = NULL; + MIR_reg_t arg_reg; + MIR_op_t arg_op, temp_op, arg_reg_op, ret_reg_op, mem_op; + MIR_insn_code_t new_insn_code, ext_code; + MIR_insn_t new_insn, prev_insn, next_insn, ext_insn; + MIR_insn_t prev_call_insn = DLIST_PREV (MIR_insn_t, call_insn); + uint32_t n_iregs, n_xregs, n_fregs; + + if (call_insn->code == MIR_INLINE) call_insn->code = MIR_CALL; + if (proto->args == NULL) { + nargs = 0; + } else { + gen_assert (nops >= VARR_LENGTH (MIR_var_t, proto->args) + && (proto->vararg_p || nops - start == VARR_LENGTH (MIR_var_t, proto->args))); + nargs = VARR_LENGTH (MIR_var_t, proto->args); + arg_vars = VARR_ADDR (MIR_var_t, proto->args); + } + if (call_insn->ops[1].mode != MIR_OP_REG && call_insn->ops[1].mode != MIR_OP_HARD_REG) { + temp_op = MIR_new_reg_op (ctx, gen_new_temp_reg (ctx, MIR_T_I64, func)); + new_insn = MIR_new_insn (ctx, MIR_MOV, temp_op, call_insn->ops[1]); + call_insn->ops[1] = temp_op; + gen_add_insn_before (ctx, call_insn, new_insn); + } + for (size_t i = start; i < nops; i++) { + arg_op = call_insn->ops[i]; + gen_assert (arg_op.mode == MIR_OP_REG || arg_op.mode == MIR_OP_HARD_REG); + if (i - start < nargs) { + type = arg_vars[i - start].type; + } else { + mode = call_insn->ops[i].value_mode; // ??? smaller ints + gen_assert (mode == MIR_OP_INT || mode == MIR_OP_UINT || mode == MIR_OP_FLOAT + || mode == MIR_OP_DOUBLE || mode == MIR_OP_LDOUBLE); + if (mode == MIR_OP_FLOAT) + (*MIR_get_error_func (ctx)) (MIR_call_op_error, + "passing float variadic arg (should be passed as double)"); + type = mode == MIR_OP_DOUBLE ? MIR_T_D : mode == MIR_OP_LDOUBLE ? MIR_T_LD : MIR_T_I64; + } + if (xmm_args < 8 && (type == MIR_T_F || type == MIR_T_D)) xmm_args++; + ext_insn = NULL; + if ((ext_code = get_ext_code (type)) != MIR_INVALID_INSN) { /* extend arg if necessary */ + temp_op = MIR_new_reg_op (ctx, gen_new_temp_reg (ctx, MIR_T_I64, func)); + ext_insn = MIR_new_insn (ctx, ext_code, temp_op, arg_op); + call_insn->ops[i] = arg_op = temp_op; + } + if ((arg_reg = get_arg_reg (type, &int_arg_num, &fp_arg_num, &new_insn_code)) + != MIR_NON_HARD_REG) { + /* put arguments to argument hard regs */ + if (ext_insn != NULL) gen_add_insn_before (ctx, call_insn, ext_insn); + arg_reg_op = _MIR_new_hard_reg_op (ctx, arg_reg); + new_insn = MIR_new_insn (ctx, new_insn_code, arg_reg_op, arg_op); + gen_add_insn_before (ctx, call_insn, new_insn); + call_insn->ops[i] = arg_reg_op; + } else { /* put arguments on the stack */ + mem_type = type == MIR_T_F || type == MIR_T_D || type == MIR_T_LD ? type : MIR_T_I64; + new_insn_code + = (type == MIR_T_F ? MIR_FMOV + : type == MIR_T_D ? MIR_DMOV : type == MIR_T_LD ? MIR_LDMOV : MIR_MOV); + mem_op = _MIR_new_hard_reg_mem_op (ctx, mem_type, mem_size, SP_HARD_REG, MIR_NON_HARD_REG, 1); + new_insn = MIR_new_insn (ctx, new_insn_code, mem_op, arg_op); + gen_assert (prev_call_insn != NULL); /* call_insn should not be 1st after simplification */ + MIR_insert_insn_after (ctx, curr_func_item, prev_call_insn, new_insn); + prev_insn = DLIST_PREV (MIR_insn_t, new_insn); + next_insn = DLIST_NEXT (MIR_insn_t, new_insn); + create_new_bb_insns (ctx, prev_insn, next_insn, call_insn); + call_insn->ops[i] = mem_op; + mem_size += type == MIR_T_LD ? 16 : 8; + if (ext_insn != NULL) gen_add_insn_after (ctx, prev_call_insn, ext_insn); + } + } + if (proto->vararg_p) { + setup_call_hard_reg_args (call_insn, AX_HARD_REG); + new_insn = MIR_new_insn (ctx, MIR_MOV, _MIR_new_hard_reg_op (ctx, AX_HARD_REG), + MIR_new_int_op (ctx, xmm_args)); + gen_add_insn_before (ctx, call_insn, new_insn); + } + n_iregs = n_xregs = n_fregs = 0; + for (size_t i = 0; i < proto->nres; i++) { + ret_reg_op = call_insn->ops[i + 2]; + gen_assert (ret_reg_op.mode == MIR_OP_REG || ret_reg_op.mode == MIR_OP_HARD_REG); + if (proto->res_types[i] == MIR_T_F && n_xregs < 2) { + new_insn + = MIR_new_insn (ctx, MIR_FMOV, ret_reg_op, + _MIR_new_hard_reg_op (ctx, n_xregs == 0 ? XMM0_HARD_REG : XMM1_HARD_REG)); + n_xregs++; + } else if (proto->res_types[i] == MIR_T_D && n_xregs < 2) { + new_insn + = MIR_new_insn (ctx, MIR_DMOV, ret_reg_op, + _MIR_new_hard_reg_op (ctx, n_xregs == 0 ? XMM0_HARD_REG : XMM1_HARD_REG)); + n_xregs++; + } else if (proto->res_types[i] == MIR_T_LD && n_fregs < 2) { + new_insn + = MIR_new_insn (ctx, MIR_LDMOV, ret_reg_op, + _MIR_new_hard_reg_op (ctx, n_fregs == 0 ? ST0_HARD_REG : ST1_HARD_REG)); + n_fregs++; + } else if (n_iregs < 2) { + new_insn + = MIR_new_insn (ctx, MIR_MOV, ret_reg_op, + _MIR_new_hard_reg_op (ctx, n_iregs == 0 ? AX_HARD_REG : DX_HARD_REG)); + n_iregs++; + } else { + (*MIR_get_error_func (ctx)) (MIR_ret_error, + "x86-64 can not handle this combination of return values"); + } + MIR_insert_insn_after (ctx, curr_func_item, call_insn, new_insn); + call_insn->ops[i + 2] = new_insn->ops[1]; + if ((ext_code = get_ext_code (proto->res_types[i])) != MIR_INVALID_INSN) { + MIR_insert_insn_after (ctx, curr_func_item, new_insn, + MIR_new_insn (ctx, ext_code, ret_reg_op, ret_reg_op)); + new_insn = DLIST_NEXT (MIR_insn_t, new_insn); + } + create_new_bb_insns (ctx, call_insn, DLIST_NEXT (MIR_insn_t, new_insn), call_insn); + } + if (mem_size != 0) { /* allocate/deallocate stack for args passed on stack */ + mem_size = (mem_size + 15) / 16 * 16; /* make it of several 16 bytes */ + new_insn + = MIR_new_insn (ctx, MIR_SUB, _MIR_new_hard_reg_op (ctx, SP_HARD_REG), + _MIR_new_hard_reg_op (ctx, SP_HARD_REG), MIR_new_int_op (ctx, mem_size)); + MIR_insert_insn_after (ctx, curr_func_item, prev_call_insn, new_insn); + next_insn = DLIST_NEXT (MIR_insn_t, new_insn); + create_new_bb_insns (ctx, prev_call_insn, next_insn, call_insn); + new_insn + = MIR_new_insn (ctx, MIR_ADD, _MIR_new_hard_reg_op (ctx, SP_HARD_REG), + _MIR_new_hard_reg_op (ctx, SP_HARD_REG), MIR_new_int_op (ctx, mem_size)); + MIR_insert_insn_after (ctx, curr_func_item, call_insn, new_insn); + next_insn = DLIST_NEXT (MIR_insn_t, new_insn); + create_new_bb_insns (ctx, call_insn, next_insn, call_insn); + } +} + +static float mir_ui2f (uint64_t i) { return i; } +static double mir_ui2d (uint64_t i) { return i; } +static long double mir_ui2ld (uint64_t i) { return i; } +static int64_t mir_ld2i (long double ld) { return ld; } +static const char *UI2F = "mir.ui2f"; +static const char *UI2D = "mir.ui2d"; +static const char *UI2LD = "mir.ui2ld"; +static const char *LD2I = "mir.ld2i"; +static const char *UI2F_P = "mir.ui2f.p"; +static const char *UI2D_P = "mir.ui2d.p"; +static const char *UI2LD_P = "mir.ui2ld.p"; +static const char *LD2I_P = "mir.ld2i.p"; + +static const char *VA_ARG_P = "mir.va_arg.p"; +static const char *VA_ARG = "mir.va_arg"; + +static void get_builtin (MIR_context_t ctx, MIR_insn_code_t code, MIR_item_t *proto_item, + MIR_item_t *func_import_item) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + MIR_type_t res_type; + + switch (code) { + case MIR_UI2F: + res_type = MIR_T_F; + *proto_item + = _MIR_builtin_proto (ctx, curr_func_item->module, UI2F_P, 1, &res_type, 1, MIR_T_I64, "v"); + *func_import_item = _MIR_builtin_func (ctx, curr_func_item->module, UI2F, mir_ui2f); + break; + case MIR_UI2D: + res_type = MIR_T_D; + *proto_item + = _MIR_builtin_proto (ctx, curr_func_item->module, UI2D_P, 1, &res_type, 1, MIR_T_I64, "v"); + *func_import_item = _MIR_builtin_func (ctx, curr_func_item->module, UI2D, mir_ui2d); + break; + case MIR_UI2LD: + res_type = MIR_T_LD; + *proto_item + = _MIR_builtin_proto (ctx, curr_func_item->module, UI2LD_P, 1, &res_type, 1, MIR_T_I64, "v"); + *func_import_item = _MIR_builtin_func (ctx, curr_func_item->module, UI2LD, mir_ui2ld); + break; + case MIR_LD2I: + res_type = MIR_T_I64; + *proto_item + = _MIR_builtin_proto (ctx, curr_func_item->module, LD2I_P, 1, &res_type, 1, MIR_T_LD, "v"); + *func_import_item = _MIR_builtin_func (ctx, curr_func_item->module, LD2I, mir_ld2i); + break; + case MIR_VA_ARG: + res_type = MIR_T_I64; + *proto_item = _MIR_builtin_proto (ctx, curr_func_item->module, VA_ARG_P, 1, &res_type, 2, + MIR_T_I64, "va", MIR_T_I64, "type"); + *func_import_item = _MIR_builtin_func (ctx, curr_func_item->module, VA_ARG, va_arg_builtin); + break; + default: assert (FALSE); + } +} + +static void gen_mov (MIR_context_t ctx, MIR_insn_t anchor, MIR_insn_code_t code, MIR_op_t dst_op, + MIR_op_t src_op) { + gen_add_insn_before (ctx, anchor, MIR_new_insn (ctx, code, dst_op, src_op)); +} + +DEF_VARR (int); +DEF_VARR (uint8_t); +DEF_VARR (uint64_t); + +struct insn_pattern_info { + int start, num; +}; + +typedef struct insn_pattern_info insn_pattern_info_t; + +DEF_VARR (insn_pattern_info_t); + +struct const_ref { + size_t pc; /* where rel32 address should be in code */ + size_t next_insn_disp; /* displacement of the next insn */ + size_t const_num; +}; + +typedef struct const_ref const_ref_t; +DEF_VARR (const_ref_t); + +struct label_ref { + int abs_addr_p; + size_t label_val_disp, next_insn_disp; + MIR_label_t label; +}; + +typedef struct label_ref label_ref_t; +DEF_VARR (label_ref_t); + +DEF_VARR (MIR_code_reloc_t); + +struct target_ctx { + unsigned char alloca_p, stack_arg_func_p, leaf_p; + int start_sp_from_bp_offset; + VARR (int) * pattern_indexes; + VARR (insn_pattern_info_t) * insn_pattern_info; + VARR (uint8_t) * result_code; + VARR (uint64_t) * const_pool; + VARR (const_ref_t) * const_refs; + VARR (label_ref_t) * label_refs; + VARR (uint64_t) * abs_address_locs; + VARR (MIR_code_reloc_t) * relocs; +}; + +#define alloca_p gen_ctx->target_ctx->alloca_p +#define stack_arg_func_p gen_ctx->target_ctx->stack_arg_func_p +#define leaf_p gen_ctx->target_ctx->leaf_p +#define start_sp_from_bp_offset gen_ctx->target_ctx->start_sp_from_bp_offset +#define pattern_indexes gen_ctx->target_ctx->pattern_indexes +#define insn_pattern_info gen_ctx->target_ctx->insn_pattern_info +#define result_code gen_ctx->target_ctx->result_code +#define const_pool gen_ctx->target_ctx->const_pool +#define const_refs gen_ctx->target_ctx->const_refs +#define label_refs gen_ctx->target_ctx->label_refs +#define abs_address_locs gen_ctx->target_ctx->abs_address_locs +#define relocs gen_ctx->target_ctx->relocs + +static void machinize (MIR_context_t ctx) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + MIR_func_t func; + MIR_type_t type, mem_type, res_type; + MIR_insn_code_t code, new_insn_code; + MIR_insn_t insn, next_insn, new_insn; + MIR_reg_t ret_reg, arg_reg; + MIR_op_t ret_reg_op, arg_reg_op, mem_op; + size_t i, int_arg_num, fp_arg_num, mem_size; + + assert (curr_func_item->item_type == MIR_func_item); + func = curr_func_item->u.func; + stack_arg_func_p = FALSE; + start_sp_from_bp_offset = 8; + for (i = int_arg_num = fp_arg_num = mem_size = 0; i < func->nargs; i++) { + /* Argument extensions is already done in simplify */ + /* Prologue: generate arg_var = hard_reg|stack mem ... */ + type = VARR_GET (MIR_var_t, func->vars, i).type; + arg_reg = get_arg_reg (type, &int_arg_num, &fp_arg_num, &new_insn_code); + if (arg_reg != MIR_NON_HARD_REG) { + arg_reg_op = _MIR_new_hard_reg_op (ctx, arg_reg); + new_insn = MIR_new_insn (ctx, new_insn_code, MIR_new_reg_op (ctx, i + 1), arg_reg_op); + MIR_prepend_insn (ctx, curr_func_item, new_insn); + create_new_bb_insns (ctx, NULL, DLIST_NEXT (MIR_insn_t, new_insn), NULL); + } else { + /* arg is on the stack */ + stack_arg_func_p = TRUE; + mem_type = type == MIR_T_F || type == MIR_T_D || type == MIR_T_LD ? type : MIR_T_I64; + new_insn_code + = (type == MIR_T_F ? MIR_FMOV + : type == MIR_T_D ? MIR_DMOV : type == MIR_T_LD ? MIR_LDMOV : MIR_MOV); + mem_op = _MIR_new_hard_reg_mem_op (ctx, mem_type, + mem_size + 8 /* ret */ + + start_sp_from_bp_offset, + BP_HARD_REG, MIR_NON_HARD_REG, 1); + new_insn = MIR_new_insn (ctx, new_insn_code, MIR_new_reg_op (ctx, i + 1), mem_op); + MIR_prepend_insn (ctx, curr_func_item, new_insn); + next_insn = DLIST_NEXT (MIR_insn_t, new_insn); + create_new_bb_insns (ctx, NULL, next_insn, NULL); + mem_size += type == MIR_T_LD ? 16 : 8; + } + } + alloca_p = FALSE; + leaf_p = TRUE; + for (insn = DLIST_HEAD (MIR_insn_t, func->insns); insn != NULL; insn = next_insn) { + next_insn = DLIST_NEXT (MIR_insn_t, insn); + code = insn->code; + if (code == MIR_UI2F || code == MIR_UI2D || code == MIR_UI2LD || code == MIR_LD2I) { + /* Use a builtin func call: mov freg, func ref; call proto, freg, res_reg, op_reg */ + MIR_item_t proto_item, func_import_item; + MIR_op_t freg_op, res_reg_op = insn->ops[0], op_reg_op = insn->ops[1], ops[4]; + + get_builtin (ctx, code, &proto_item, &func_import_item); + assert (res_reg_op.mode == MIR_OP_REG && op_reg_op.mode == MIR_OP_REG); + freg_op = MIR_new_reg_op (ctx, gen_new_temp_reg (ctx, MIR_T_I64, curr_func_item->u.func)); + next_insn = new_insn + = MIR_new_insn (ctx, MIR_MOV, freg_op, MIR_new_ref_op (ctx, func_import_item)); + gen_add_insn_before (ctx, insn, new_insn); + ops[0] = MIR_new_ref_op (ctx, proto_item); + ops[1] = freg_op; + ops[2] = res_reg_op; + ops[3] = op_reg_op; + new_insn = MIR_new_insn_arr (ctx, MIR_CALL, 4, ops); + gen_add_insn_before (ctx, insn, new_insn); + gen_delete_insn (ctx, insn); + } else if (code == MIR_VA_START) { + MIR_op_t treg_op + = MIR_new_reg_op (ctx, gen_new_temp_reg (ctx, MIR_T_I64, curr_func_item->u.func)); + MIR_op_t va_op = insn->ops[0]; + MIR_reg_t va_reg; + int gp_offset = 0, fp_offset = 48; + MIR_var_t var; + + assert (func->vararg_p && (va_op.mode == MIR_OP_REG || va_op.mode == MIR_OP_HARD_REG)); + for (uint32_t i = 0; i < func->nargs; i++) { + var = VARR_GET (MIR_var_t, func->vars, i); + if (var.type == MIR_T_F || var.type == MIR_T_D) + fp_offset += 16; + else + gp_offset += 8; + } + va_reg = va_op.mode == MIR_OP_REG ? va_op.u.reg : va_op.u.hard_reg; + /* Insns can be not simplified as soon as they match a machine insn. */ + /* mem32[va_reg] = gp_offset; mem32[va_reg] = fp_offset */ + gen_mov (ctx, insn, MIR_MOV, MIR_new_mem_op (ctx, MIR_T_U32, 0, va_reg, 0, 1), + MIR_new_int_op (ctx, gp_offset)); + next_insn = DLIST_PREV (MIR_insn_t, insn); + gen_mov (ctx, insn, MIR_MOV, MIR_new_mem_op (ctx, MIR_T_U32, 4, va_reg, 0, 1), + MIR_new_int_op (ctx, fp_offset)); + /* overflow_arg_area_reg: treg = start sp + 8; mem64[va_reg + 8] = treg */ + new_insn = MIR_new_insn (ctx, MIR_ADD, treg_op, _MIR_new_hard_reg_op (ctx, BP_HARD_REG), + MIR_new_int_op (ctx, 8 /*ret*/ + start_sp_from_bp_offset)); + gen_add_insn_before (ctx, insn, new_insn); + gen_mov (ctx, insn, MIR_MOV, MIR_new_mem_op (ctx, MIR_T_I64, 8, va_reg, 0, 1), treg_op); + /* reg_save_area: treg = start sp - reg_save_area_size; mem64[va_reg + 16] = treg */ + new_insn = MIR_new_insn (ctx, MIR_ADD, treg_op, _MIR_new_hard_reg_op (ctx, BP_HARD_REG), + MIR_new_int_op (ctx, -reg_save_area_size)); + gen_add_insn_before (ctx, insn, new_insn); + gen_mov (ctx, insn, MIR_MOV, MIR_new_mem_op (ctx, MIR_T_I64, 16, va_reg, 0, 1), treg_op); + gen_delete_insn (ctx, insn); + } else if (code == MIR_VA_END) { /* do nothing */ + gen_delete_insn (ctx, insn); + } else if (code == MIR_VA_ARG) { /* do nothing */ + /* Use a builtin func call: + mov func_reg, func ref; mov flag_reg, <0|1>; call proto, func_reg, res_reg, va_reg, + flag_reg */ + MIR_item_t proto_item, func_import_item; + MIR_op_t ops[5], func_reg_op, flag_reg_op; + MIR_op_t res_reg_op = insn->ops[0], va_reg_op = insn->ops[1], mem_op = insn->ops[2]; + + get_builtin (ctx, code, &proto_item, &func_import_item); + assert (res_reg_op.mode == MIR_OP_REG && va_reg_op.mode == MIR_OP_REG + && mem_op.mode == MIR_OP_MEM); + func_reg_op = MIR_new_reg_op (ctx, gen_new_temp_reg (ctx, MIR_T_I64, curr_func_item->u.func)); + flag_reg_op = MIR_new_reg_op (ctx, gen_new_temp_reg (ctx, MIR_T_I64, curr_func_item->u.func)); + next_insn = new_insn + = MIR_new_insn (ctx, MIR_MOV, func_reg_op, MIR_new_ref_op (ctx, func_import_item)); + gen_add_insn_before (ctx, insn, new_insn); + new_insn = MIR_new_insn (ctx, MIR_MOV, flag_reg_op, + MIR_new_int_op (ctx, (int64_t) mem_op.u.mem.type)); + gen_add_insn_before (ctx, insn, new_insn); + ops[0] = MIR_new_ref_op (ctx, proto_item); + ops[1] = func_reg_op; + ops[2] = res_reg_op; + ops[3] = va_reg_op; + ops[4] = flag_reg_op; + new_insn = MIR_new_insn_arr (ctx, MIR_CALL, 5, ops); + gen_add_insn_before (ctx, insn, new_insn); + gen_delete_insn (ctx, insn); + } else if (MIR_call_code_p (code)) { + machinize_call (ctx, insn); + leaf_p = FALSE; + } else if (code == MIR_ALLOCA) { + alloca_p = TRUE; + } else if (code == MIR_RET) { + /* In simplify we already transformed code for one return insn + and added extension in return (if any). */ + uint32_t n_iregs = 0, n_xregs = 0, n_fregs = 0; + + assert (curr_func_item->u.func->nres == MIR_insn_nops (ctx, insn)); + for (size_t i = 0; i < curr_func_item->u.func->nres; i++) { + assert (insn->ops[i].mode == MIR_OP_REG); + res_type = curr_func_item->u.func->res_types[i]; + if ((res_type == MIR_T_F || res_type == MIR_T_D) && n_xregs < 2) { + new_insn_code = res_type == MIR_T_F ? MIR_FMOV : MIR_DMOV; + ret_reg = n_xregs++ == 0 ? XMM0_HARD_REG : XMM1_HARD_REG; + } else if (res_type == MIR_T_LD && n_fregs < 2) { // ??? + new_insn_code = MIR_LDMOV; + ret_reg = n_fregs == 0 ? ST0_HARD_REG : ST1_HARD_REG; + n_fregs++; + } else if (n_iregs < 2) { + new_insn_code = MIR_MOV; + ret_reg = n_iregs++ == 0 ? AX_HARD_REG : DX_HARD_REG; + } else { + (*MIR_get_error_func (ctx)) (MIR_ret_error, + "x86-64 can not handle this combination of return values"); + } + ret_reg_op = _MIR_new_hard_reg_op (ctx, ret_reg); + new_insn = MIR_new_insn (ctx, new_insn_code, ret_reg_op, insn->ops[i]); + gen_add_insn_before (ctx, insn, new_insn); + insn->ops[i] = ret_reg_op; + } + } else if (code == MIR_LSH || code == MIR_RSH || code == MIR_URSH || code == MIR_LSHS + || code == MIR_RSHS || code == MIR_URSHS) { + /* We can access only cl as shift register: */ + MIR_op_t creg_op = _MIR_new_hard_reg_op (ctx, CX_HARD_REG); + + new_insn = MIR_new_insn (ctx, MIR_MOV, creg_op, insn->ops[2]); + gen_add_insn_before (ctx, insn, new_insn); + insn->ops[2] = creg_op; + } else if (code == MIR_DIV || code == MIR_UDIV || code == MIR_DIVS || code == MIR_UDIVS) { + /* Divide uses ax/dx as operands: */ + MIR_op_t areg_op = _MIR_new_hard_reg_op (ctx, AX_HARD_REG); + + new_insn = MIR_new_insn (ctx, MIR_MOV, areg_op, insn->ops[1]); + gen_add_insn_before (ctx, insn, new_insn); + new_insn = MIR_new_insn (ctx, MIR_MOV, insn->ops[0], areg_op); + gen_add_insn_after (ctx, insn, new_insn); + insn->ops[0] = insn->ops[1] = areg_op; + } else if (code == MIR_MOD || code == MIR_UMOD || code == MIR_MODS || code == MIR_UMODS) { + /* Divide uses ax/dx as operands: */ + MIR_op_t areg_op = _MIR_new_hard_reg_op (ctx, AX_HARD_REG); + MIR_op_t dreg_op = _MIR_new_hard_reg_op (ctx, DX_HARD_REG); + + new_insn = MIR_new_insn (ctx, MIR_MOV, areg_op, insn->ops[1]); + gen_add_insn_before (ctx, insn, new_insn); + insn->ops[1] = areg_op; + new_insn = MIR_new_insn (ctx, MIR_MOV, insn->ops[0], dreg_op); + gen_add_insn_after (ctx, insn, new_insn); + insn->ops[0] = dreg_op; + } else if (code == MIR_EQ || code == MIR_NE || code == MIR_LT || code == MIR_ULT + || code == MIR_LE || code == MIR_ULE || code == MIR_GT || code == MIR_UGT + || code == MIR_GE || code == MIR_UGE || code == MIR_EQS || code == MIR_NES + || code == MIR_LTS || code == MIR_ULTS || code == MIR_LES || code == MIR_ULES + || code == MIR_GTS || code == MIR_UGT || code == MIR_GES || code == MIR_UGES + || code == MIR_FEQ || code == MIR_FNE || code == MIR_FLT || code == MIR_FLE + || code == MIR_FGT || code == MIR_FGE || code == MIR_DEQ || code == MIR_DNE + || code == MIR_DLT || code == MIR_DLE || code == MIR_DGT || code == MIR_DGE) { + /* We can access only 4 regs in setxx -- use ax as the result: */ + MIR_op_t areg_op = _MIR_new_hard_reg_op (ctx, AX_HARD_REG); + + new_insn = MIR_new_insn (ctx, MIR_MOV, insn->ops[0], areg_op); + gen_add_insn_after (ctx, insn, new_insn); + insn->ops[0] = areg_op; + } + } +} + +static void isave (MIR_context_t ctx, MIR_insn_t anchor, int disp, MIR_reg_t hard_reg) { + gen_mov (ctx, anchor, MIR_MOV, + _MIR_new_hard_reg_mem_op (ctx, MIR_T_I64, disp, SP_HARD_REG, MIR_NON_HARD_REG, 1), + _MIR_new_hard_reg_op (ctx, hard_reg)); +} + +static void dsave (MIR_context_t ctx, MIR_insn_t anchor, int disp, MIR_reg_t hard_reg) { + gen_mov (ctx, anchor, MIR_DMOV, + _MIR_new_hard_reg_mem_op (ctx, MIR_T_D, disp, SP_HARD_REG, MIR_NON_HARD_REG, 1), + _MIR_new_hard_reg_op (ctx, hard_reg)); +} + +static void make_prolog_epilog (MIR_context_t ctx, bitmap_t used_hard_regs, + size_t stack_slots_num) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + MIR_func_t func; + MIR_insn_t anchor, new_insn; + MIR_op_t sp_reg_op, fp_reg_op; + int64_t bp_saved_reg_offset, start; + size_t i, n, service_area_size, saved_hard_regs_num, stack_slots_size, block_size; + + assert (curr_func_item->item_type == MIR_func_item); + func = curr_func_item->u.func; + for (i = saved_hard_regs_num = 0; i <= MAX_HARD_REG; i++) + if (!call_used_hard_reg_p (i) && bitmap_bit_p (used_hard_regs, i)) saved_hard_regs_num++; + if (leaf_p && !alloca_p && saved_hard_regs_num == 0 && !func->vararg_p && stack_slots_num == 0) + return; + sp_reg_op.mode = fp_reg_op.mode = MIR_OP_HARD_REG; + sp_reg_op.u.hard_reg = SP_HARD_REG; + fp_reg_op.u.hard_reg = BP_HARD_REG; + /* Prologue: */ + anchor = DLIST_HEAD (MIR_insn_t, func->insns); + new_insn + = MIR_new_insn (ctx, MIR_MOV, + _MIR_new_hard_reg_mem_op (ctx, MIR_T_I64, -8, SP_HARD_REG, MIR_NON_HARD_REG, 1), + fp_reg_op); + gen_add_insn_before (ctx, anchor, new_insn); /* -8(sp) = bp */ + /* Use add for matching LEA: */ + new_insn = MIR_new_insn (ctx, MIR_ADD, fp_reg_op, sp_reg_op, MIR_new_int_op (ctx, -8)); + gen_add_insn_before (ctx, anchor, new_insn); /* bp = sp - 8 */ + if (!func->vararg_p) { + service_area_size = 8; + } else { + service_area_size = reg_save_area_size + 8; + start = -(int64_t) service_area_size; + isave (ctx, anchor, start, DI_HARD_REG); + isave (ctx, anchor, start + 8, SI_HARD_REG); + isave (ctx, anchor, start + 16, DX_HARD_REG); + isave (ctx, anchor, start + 24, CX_HARD_REG); + isave (ctx, anchor, start + 32, R8_HARD_REG); + isave (ctx, anchor, start + 40, R9_HARD_REG); + dsave (ctx, anchor, start + 48, XMM0_HARD_REG); + dsave (ctx, anchor, start + 64, XMM1_HARD_REG); + dsave (ctx, anchor, start + 80, XMM2_HARD_REG); + dsave (ctx, anchor, start + 96, XMM3_HARD_REG); + dsave (ctx, anchor, start + 112, XMM4_HARD_REG); + dsave (ctx, anchor, start + 128, XMM5_HARD_REG); + dsave (ctx, anchor, start + 144, XMM6_HARD_REG); + dsave (ctx, anchor, start + 160, XMM7_HARD_REG); + } + stack_slots_size = stack_slots_num * 8; + /* stack slots, and saved regs as multiple of 16 bytes: */ + block_size = (stack_slots_size + 8 * saved_hard_regs_num + 15) / 16 * 16; + new_insn = MIR_new_insn (ctx, MIR_SUB, sp_reg_op, sp_reg_op, + MIR_new_int_op (ctx, block_size + service_area_size)); + gen_add_insn_before (ctx, anchor, new_insn); /* sp -= block size + service_area_size */ + bp_saved_reg_offset = block_size + (func->vararg_p ? reg_save_area_size : 0); + /* Saving callee saved hard registers: */ + for (i = n = 0; i <= MAX_HARD_REG; i++) + if (!call_used_hard_reg_p (i) && bitmap_bit_p (used_hard_regs, i)) { + assert (i <= R15_HARD_REG); /* xmm regs are always callee-clobbered */ + new_insn = MIR_new_insn (ctx, MIR_MOV, + _MIR_new_hard_reg_mem_op (ctx, MIR_T_I64, + (int64_t) (n++ * 8) - bp_saved_reg_offset, + BP_HARD_REG, MIR_NON_HARD_REG, 1), + _MIR_new_hard_reg_op (ctx, i)); + gen_add_insn_before (ctx, anchor, new_insn); /* disp(sp) = saved hard reg */ + } + /* Epilogue: */ + anchor = DLIST_TAIL (MIR_insn_t, func->insns); + /* Restoring hard registers: */ + for (i = n = 0; i <= MAX_HARD_REG; i++) + if (!call_used_hard_reg_p (i) && bitmap_bit_p (used_hard_regs, i)) { + new_insn = MIR_new_insn (ctx, MIR_MOV, _MIR_new_hard_reg_op (ctx, i), + _MIR_new_hard_reg_mem_op (ctx, MIR_T_I64, + (int64_t) (n++ * 8) - bp_saved_reg_offset, + BP_HARD_REG, MIR_NON_HARD_REG, 1)); + gen_add_insn_before (ctx, anchor, new_insn); /* hard reg = disp(sp) */ + } + new_insn = MIR_new_insn (ctx, MIR_ADD, sp_reg_op, fp_reg_op, MIR_new_int_op (ctx, 8)); + gen_add_insn_before (ctx, anchor, new_insn); /* sp = bp + 8 */ + new_insn = MIR_new_insn (ctx, MIR_MOV, fp_reg_op, + _MIR_new_hard_reg_mem_op (ctx, MIR_T_I64, -8, SP_HARD_REG, + MIR_NON_HARD_REG, 1)); + gen_add_insn_before (ctx, anchor, new_insn); /* bp = -8(sp) */ +} + +struct pattern { + MIR_insn_code_t code; + /* Pattern elements: + blank - ignore + X - match everything + $ - finish successfully matching + r - register (we don't care about bp and sp because they are fixed and used correctly) + h[0-31] - hard register with given number + z - operand is zero + i[0-3] - immediate of size 8,16,32,64-bits + p[0-3] - reference + s - immediate 1, 2, 4, or 8 (scale) + m[0-3] - int (signed or unsigned) type memory of size 8,16,32,64-bits + ms[0-3] - signed int type memory of size 8,16,32,64-bits + mu[0-3] - unsigned int type memory of size 8,16,32,64-bits + mf - memory of float + md - memory of double + mld - memory of long double + l - label which can be present by 32-bit + [0-9] - an operand matching n-th operand (n should be less than given operand number) + + Remmeber we have no float or (long) double immediate at this stage. They are represented by + a reference to data item. */ + const char *pattern; + /* Replacement elements: + blank - ignore + ; - insn separation + X - REX byte with W=1 + Y - Optional REX byte with W=0 + Z - Obligatory REX byte with W=0 + [0-9A-F]+ pairs of hexidecimal digits opcode + r[0-2] = n-th operand in ModRM:reg + R[0-2] = n-th operand in ModRM:rm with mod == 3 + m[0-2] = n-th operand is mem + mt = temp memory in red zone (-16(sp)) + mT = switch table memory (h11,r,8) + ap = 2 and 3 operand forms address by plus (1st reg to base, 2nd reg to index, disp to disp) + am = 2 and 3 operand forms address by mult (1st reg to index and mult const to scale) + ad - forms address: 1th operand is base reg and is displacement + i[0-2] - n-th operand in byte immediate (should be imm of type i8) + I[0-2] - n-th operand in 4 byte immediate (should be imm of type i32) + J[0-2] - n-th operand in 8 byte immediate + P[0-2] - n-th operand in 8 byte address + T - absolute 8-byte switch table address + l[0-2] - n-th operand-label in 32-bit + /[0-7] - opmod with given value (reg of MOD-RM) + +[0-2] - lower 3-bit part of opcode used for n-th reg operand + c - address of 32-bit or 64-bit constant in memory pool (we keep always 64-bit + in memory pool. x86_64 is LE) + h - hardware register with given number in reg of ModRM:reg; + one bit of 8-15 in REX.R + H - hardware register with given number in rm of MOD-RM with and mod=3 + (register); one bit of 8-15 in REX.B v - 8-bit immediate with given hex value V - + 32-bit immediate with given hex value + */ + const char *replacement; +}; + +// make imm always second operand (symplify for cmp and commutative op) +// make result of cmp op always a register and memory only the 2nd operand if first is reg, +// but not for FP (NAN) (simplify) +// for FP cmp first operand should be always reg (machinize) + +#define IOP0(ICODE, SUFF, PREF, RRM_CODE, MR_CODE, RMI8_CODE, RMI32_CODE) \ + {ICODE##SUFF, "r 0 r", #PREF " " RRM_CODE " r0 R2"}, /* op r0,r2*/ \ + {ICODE##SUFF, "r 0 m3", #PREF " " RRM_CODE " r0 m2"}, /* op r0,m2*/ \ + {ICODE##SUFF, "m3 0 r", #PREF " " MR_CODE " r2 m0"}, /* op m0,r2*/ \ + {ICODE##SUFF, "r 0 i0", #PREF " " RMI8_CODE " R0 i2"}, /* op r0,i2*/ \ + {ICODE##SUFF, "m3 0 i0", #PREF " " RMI8_CODE " m0 i2"}, /* op m0,i2*/ \ + {ICODE##SUFF, "r 0 i2", #PREF " " RMI32_CODE " R0 I2"}, /* op r0,i2*/ \ + {ICODE##SUFF, "m3 0 i2", #PREF " " RMI32_CODE " m0 I2"}, /* op m0,i2*/ + +#define IOP(ICODE, RRM_CODE, MR_CODE, RMI8_CODE, RMI32_CODE) \ + IOP0 (ICODE, , X, RRM_CODE, MR_CODE, RMI8_CODE, RMI32_CODE) \ + IOP0 (ICODE, S, Y, RRM_CODE, MR_CODE, RMI8_CODE, RMI32_CODE) + +#define FOP(ICODE, OP_CODE) {ICODE, "r 0 r", OP_CODE " r0 R2"}, {ICODE, "r 0 mf", OP_CODE " r0 m2"}, + +#define DOP(ICODE, OP_CODE) {ICODE, "r 0 r", OP_CODE " r0 R2"}, {ICODE, "r 0 md", OP_CODE " r0 m2"}, + +#define LDOP(ICODE, OP_CODE) \ + /* fld m1;fld m2;op;fstp m0: */ \ + {ICODE, "mld mld mld", "DB /5 m1; DB /5 m2; " OP_CODE "; DB /7 m0"}, + +#define SHOP0(ICODE, SUFF, PREF, CL_OP_CODE, I8_OP_CODE) \ + {ICODE##SUFF, "r 0 h1", #PREF " " CL_OP_CODE " R0"}, /* sh r0,cl */ \ + {ICODE##SUFF, "m3 0 h1", #PREF " " CL_OP_CODE " m0"}, /* sh m0,cl */ \ + {ICODE##SUFF, "r 0 i0", #PREF " " I8_OP_CODE " R0 i2"}, /* sh r0,i2 */ \ + {ICODE##SUFF, "m3 0 i0", #PREF " " I8_OP_CODE " m0 i2"}, /* sh m0,i2 */ + +#define SHOP(ICODE, CL_OP_CODE, I8_OP_CODE) \ + SHOP0 (ICODE, , X, CL_OP_CODE, I8_OP_CODE) \ + SHOP0 (ICODE, S, Y, CL_OP_CODE, I8_OP_CODE) + +/* cmp ...; setx r0; movzbl r0,r0: */ +#define CMP0(ICODE, SUFF, PREF, SETX) \ + {ICODE##SUFF, "r r r", #PREF " 3B r1 R2;" SETX " R0;X 0F B6 r0 R0"}, /* cmp r1,r2;...*/ \ + {ICODE##SUFF, "r r m3", #PREF " 3B r1 m2;" SETX " R0;X 0F B6 r0 R0"}, /* cmp r1,m2;...*/ \ + {ICODE##SUFF, "r r i0", #PREF " 83 /7 R1 i2;" SETX " R0;X 0F B6 r0 R0"}, /* cmp r1,i2;...*/ \ + {ICODE##SUFF, "r r i2", #PREF " 81 /7 R1 I2;" SETX " R0;X 0F B6 r0 R0"}, /* cmp r1,i2;...*/ \ + {ICODE##SUFF, "r m3 i0", #PREF " 83 /7 m1 i2;" SETX " R0;X 0F B6 r0 R0"}, /* cmp m1,i2;...*/ \ + {ICODE##SUFF, "r m3 i2", #PREF " 81 /7 m1 I2;" SETX " R0;X 0F B6 r0 R0"}, /* cmp m1,i2;...*/ + +#define CMP(ICODE, SET_OPCODE) \ + CMP0 (ICODE, , X, SET_OPCODE) \ + CMP0 (ICODE, S, Y, SET_OPCODE) + +#define FEQ(ICODE, V, SET_OPCODE) \ + /*xor %eax,%eax;ucomiss r1,{r,m2};mov V,%edx;set[n]p r0;cmovne %rdx,%rax; mov %rax,r0: */ \ + {ICODE, "r r r", \ + "33 h0 H0; 0F 2E r1 R2; BA " V "; " SET_OPCODE " H0; X 0F 45 h0 H2; X 8B r0 H0"}, \ + {ICODE, "r r md", \ + "33 h0 H0; 0F 2E r1 m2; BA " V "; " SET_OPCODE " H0; X 0F 45 h0 H2; X 8B r0 H0"}, + +#define DEQ(ICODE, V, SET_OPCODE) \ + /*xor %eax,%eax;ucomisd r1,{r,m2};mov V,%edx;set[n]p r0;cmovne %rdx,%rax; mov %rax,r0: */ \ + {ICODE, "r r r", \ + "33 h0 H0; 66 Y 0F 2E r1 R2; BA " V "; " SET_OPCODE " H0; X 0F 45 h0 H2; X 8B r0 H0"}, \ + {ICODE, "r r md", \ + "33 h0 H0; 66 Y 0F 2E r1 m2; BA " V "; " SET_OPCODE " H0; X 0F 45 h0 H2; X 8B r0 H0"}, + +#define LDEQ(ICODE, V, SET_OPCODE) \ + /*fld m2;fld m1;xor %eax,%eax;fucomip st,st(1);fstp %st;mov V,%edx; \ + set[n]p r0;cmovne %rdx,%rax;mov %rax,r0: */ \ + {ICODE, "r mld mld", \ + "DB /5 m2; DB /5 m1; 33 h0 H0; DF E9; DD D8; BA " V "; " SET_OPCODE \ + " H0; X 0F 45 h0 H2; X 8B r0 H0"}, + +#define FCMP(ICODE, SET_OPCODE) \ + /*xor %eax,%eax;ucomiss r1,r2;setx az; mov %rax,r0: */ \ + {ICODE, "r r r", "33 h0 H0; Y 0F 2E r1 R2; " SET_OPCODE " H0;X 8B r0 H0"}, \ + {ICODE, "r r mf", "33 h0 H0; Y 0F 2E r1 m2; " SET_OPCODE " H0;X 8B r0 H0"}, + +#define DCMP(ICODE, SET_OPCODE) \ + /*xor %eax,%eax;ucomisd r1,r2;setx az; mov %rax,r0: */ \ + {ICODE, "r r r", "33 h0 H0; 66 Y 0F 2F r1 R2; " SET_OPCODE " H0;X 8B r0 H0"}, \ + {ICODE, "r r md", "33 h0 H0; 66 Y 0F 2F r1 m2; " SET_OPCODE " H0;X 8B r0 H0"}, + +#define LDCMP(ICODE, SET_OPCODE) \ + /*fld m2;fld m1;xor %eax,%eax;fcomip st,st(1);fstp %st;setx az; mov %rax,r0: */ \ + {ICODE, "r mld mld", "DB /5 m2; DB /5 m1; 33 h0 H0; DF F1; DD D8; " SET_OPCODE " H0;X 8B r0 H0"}, + +#define BR0(ICODE, SUFF, PREF, LONG_JMP_OPCODE) \ + {ICODE##SUFF, "l r", #PREF " 83 /7 R1 v0;" LONG_JMP_OPCODE " l0"}, /*cmp r0,$0;jxx rel32*/ \ + {ICODE##SUFF, "l m3", #PREF " 83 /7 m1 v0;" LONG_JMP_OPCODE " l0"}, /*cmp m0,$0;jxx rel8*/ + +#define BR(ICODE, LONG_JMP_OPCODE) \ + BR0 (ICODE, , X, LONG_JMP_OPCODE) \ + BR0 (ICODE, S, Y, LONG_JMP_OPCODE) + +#define BCMP0(ICODE, SUFF, PREF, LONG_JMP_OPCODE) \ + {ICODE##SUFF, "l r r", #PREF " 3B r1 R2;" LONG_JMP_OPCODE " l0"}, /*cmp r0,r1;jxx rel32*/ \ + {ICODE##SUFF, "l r m3", #PREF " 3B r1 m2;" LONG_JMP_OPCODE " l0"}, /*cmp r0,m1;jxx rel8*/ \ + {ICODE##SUFF, "l r i0", #PREF " 83 /7 R1 i2;" LONG_JMP_OPCODE " l0"}, /*cmp r0,i1;jxx rel32*/ \ + {ICODE##SUFF, "l r i2", #PREF " 81 /7 R1 I2;" LONG_JMP_OPCODE " l0"}, /*cmp r0,i1;jxx rel32*/ \ + {ICODE##SUFF, "l m3 i0", #PREF " 83 /7 m1 i2;" LONG_JMP_OPCODE " l0"}, /*cmp m0,i1;jxx rel32*/ \ + {ICODE##SUFF, "l m3 i2", #PREF " 81 /7 m1 I2;" LONG_JMP_OPCODE " l0"}, /*cmp m0,i1;jxx rel32*/ + +#define BCMP(ICODE, LONG_JMP_OPCODE) \ + BCMP0 (ICODE, , X, LONG_JMP_OPCODE) \ + BCMP0 (ICODE, S, Y, LONG_JMP_OPCODE) + +#define FBCMP(ICODE, LONG_JMP_OPCODE) \ + {ICODE, "l r r", "Y 0F 2E r1 R2;" LONG_JMP_OPCODE " l0"}, /* ucomiss r0,r1;jxx rel32*/ + +#define DBCMP(ICODE, LONG_JMP_OPCODE) \ + {ICODE, "l r r", "66 Y 0F 2E r1 R2;" LONG_JMP_OPCODE " l0"}, /* ucomisd r0,r1;jxx rel32*/ + +#define LDBCMP(ICODE, LONG_JMP_OPCODE) \ + /* fld m2;fld m1; fcomip st,st(1); fstp st; jxx rel32*/ \ + {ICODE, "l mld mld", "DB /5 m2; DB /5 m1; DF F1; DD D8; " LONG_JMP_OPCODE " l0"}, + +static const struct pattern patterns[] = { + {MIR_MOV, "r z", "Y 33 r0 R0"}, /* xor r0,r0 -- 32 bit xor */ + {MIR_MOV, "r r", "X 8B r0 R1"}, /* mov r0,r1 */ + {MIR_MOV, "r m3", "X 8B r0 m1"}, /* mov r0,m1 */ + {MIR_MOV, "m3 r", "X 89 r1 m0"}, /* mov m0,r1 */ + {MIR_MOV, "r i2", "X C7 /0 R0 I1"}, /* mov r0,i32 */ + {MIR_MOV, "m3 i2", "X C7 /0 m0 I1"}, /* mov m0,i32 */ + {MIR_MOV, "r i3", "X B8 +0 J1"}, /* mov r0,i64 */ + {MIR_MOV, "r p", "X B8 +0 P1"}, /* mov r0,a64 */ + + {MIR_MOV, "m0 r", "Z 88 r1 m0"}, /* mov m0, r1 */ + {MIR_MOV, "m1 r", "66 Y 89 r1 m0"}, /* mov m0, r1 */ + {MIR_MOV, "m2 r", "Y 89 r1 m0"}, /* mov m0, r1 */ + + {MIR_MOV, "r ms0", "X 0F BE r0 m1"}, /* movsx r0, m1 */ + {MIR_MOV, "r ms1", "X 0F BF r0 m1"}, /* movsx r0, m1 */ + {MIR_MOV, "r ms2", "X 63 r0 m1"}, /* movsx r0, m1 */ + + {MIR_MOV, "r mu0", "X 0F B6 r0 m1"}, /* movzx r0, m1 */ + {MIR_MOV, "r mu1", "X 0F B7 r0 m1"}, /* movzx r0, m1 */ + {MIR_MOV, "r mu2", "8B r0 m1"}, /* mov r0, m1 */ + + {MIR_MOV, "m0 i0", "Y C6 /0 m0 i1"}, /* mov m0,i8 */ + {MIR_MOV, "m2 i2", "Y C7 /0 m0 I1"}, /* mov m0,i32 */ + + {MIR_FMOV, "r r", "F3 Y 0F 10 r0 R1"}, /* movss r0,r1 */ + {MIR_FMOV, "r mf", "F3 Y 0F 10 r0 m1"}, /* movss r0,m32 */ + {MIR_FMOV, "mf r", "F3 Y 0F 11 r1 m0"}, /* movss r0,m32 */ + + {MIR_DMOV, "r r", "F2 Y 0F 10 r0 R1"}, /* movsd r0,r1 */ + {MIR_DMOV, "r md", "F2 Y 0F 10 r0 m1"}, /* movsd r0,m64 */ + {MIR_DMOV, "md r", "F2 Y 0F 11 r1 m0"}, /* movsd r0,m64 */ + + {MIR_LDMOV, "mld h32", "DB /7 m0"}, /*only for ret and calls in given order: fstp m0 */ + {MIR_LDMOV, "h32 mld", "DB /5 m1"}, /*only for ret and calls in given order: fld m1 */ + {MIR_LDMOV, "mld h33", "D9 C9; DB /7 m0"}, /*only for ret and calls: fxch;fstp m0 */ + {MIR_LDMOV, "h33 mld", "DB /5 m1; D9 C9"}, /*only for ret and calls: fld m1; fxch */ + {MIR_LDMOV, "mld mld", "DB /5 m1; DB /7 m0"}, /* fld m1; fstp m0 */ + + {MIR_EXT8, "r r", "X 0F BE r0 R1"}, /* movsx r0,r1 */ + {MIR_EXT8, "r m0", "X 0F BE r0 m1"}, /* movsx r0,m1 */ + {MIR_EXT16, "r r", "X 0F BF r0 R1"}, /* movsx r0,r1 */ + {MIR_EXT16, "r m1", "X 0F BF r0 m1"}, /* movsx r0,m1 */ + {MIR_EXT32, "r r", "X 63 r0 R1"}, /* movsx r0,r1 */ + {MIR_EXT32, "r m2", "X 63 r0 m1"}, /* movsx r0,m1 */ + {MIR_UEXT8, "r r", "X 0F B6 r0 R1"}, /* movzx r0,r1 */ + {MIR_UEXT8, "r m0", "X 0F B6 r0 m1"}, /* movzx r0,m1 */ + {MIR_UEXT16, "r r", "X 0F B7 r0 R1"}, /* movzx r0,r1 */ + {MIR_UEXT16, "r m1", "X 0F B7 r0 m1"}, /* movzx r0,m1 */ + {MIR_UEXT32, "r r", "Y 8B r0 R1"}, /* mov r0,r1 */ + {MIR_UEXT32, "r m2", "Y 8B r0 m1"}, /* mov r0,m1 */ + + {MIR_I2F, "r r", "F3 X 0F 2A r0 R1"}, /* cvtsi2ss r0,r1 */ + {MIR_I2F, "r mf", "F3 X 0F 2A r0 m1"}, /* cvtsi2ss r0,m1 */ + {MIR_I2D, "r r", "F2 X 0F 2A r0 R1"}, /* cvtsi2sd r0,r1 */ + {MIR_I2D, "r md", "F2 X 0F 2A r0 m1"}, /* cvtsi2sd r0,m1 */ + {MIR_I2LD, "mld r", "X 89 r1 mt; DF /5 mt; DB /7 m0"}, /*mov -16(sp),r1;fild -16(sp);fstp m0 */ + + {MIR_F2I, "r r", "F3 X 0F 2C r0 R1"}, /* cvttss2si r0,r1 */ + {MIR_F2I, "r mf", "F3 X 0F 2C r0 m1"}, /* cvttss2si r0,m1 */ + {MIR_D2I, "r r", "F2 X 0F 2C r0 R1"}, /* cvttsd2si r0,r1 */ + {MIR_D2I, "r md", "F2 X 0F 2C r0 m1"}, /* cvttsd2si r0,m1 */ + + {MIR_F2D, "r r", "F3 0F 5A r0 R1"}, /* cvtss2sd r0,r1 */ + {MIR_F2D, "r mf", "F3 Y 0F 5A r0 m1"}, /* cvtss2sd r0,m1 */ + /* fld m1;fstpl -16(sp);movsd r0,-16(sp): */ + {MIR_LD2D, "r mld", "DB /5 m1; DD /3 mt; F2 Y 0F 10 r0 mt"}, + + {MIR_D2F, "r r", "F2 0F 5A r0 R1"}, /* cvtsd2ss r0,r1 */ + {MIR_D2F, "r md", "F2 Y 0F 5A r0 m1"}, /* cvtsd2ss r0,m1 */ + /* fld m1;fstps -16(sp);movss r0, -16(sp): */ + {MIR_LD2F, "r mld", "DB /5 m1; D9 /3 mt; F3 Y 0F 10 r0 mt"}, + + /* movss -16(sp), r1; flds -16(sp); fstp m0: */ + {MIR_F2LD, "mld r", "F3 Y 0F 11 r1 mt; D9 /0 mt; DB /7 m0"}, + {MIR_F2LD, "mld mf", "D9 /0 m1; DB /7 m0"}, /* flds m1; fstp m0 */ + /* movsd -16(sp), r1; fldl -16(sp); fstp m0: */ + {MIR_D2LD, "mld r", "F2 Y 0F 11 r1 mt; DD /0 mt; DB /7 m0"}, + {MIR_D2LD, "mld md", "DD /0 m1; DB /7 m0"}, /* fldl m1; fstp m0 */ + + /* lea r0, 15(r1); and r0, r0, -16; sub sp, r0; mov r0, sp: */ + {MIR_ALLOCA, "r r", "Y 8D r0 adF; X 81 /4 R0 VFFFFFFF0; X 2B h04 R0; X 8B r0 H04"}, + {MIR_ALLOCA, "r i2", "X 81 /5 H04 I1; X 8B r0 H04"}, /* sub sp, i2; mov r0, sp */ + + {MIR_BSTART, "r", "X 8B r0 H4"}, /* r0 = sp */ + {MIR_BEND, "r", "X 8B h4 R0"}, /* sp = r0 */ + + {MIR_NEG, "r 0", "X F7 /3 R1"}, /* neg r0 */ + {MIR_NEG, "m3 0", "X F7 /3 m1"}, /* neg m0 */ + {MIR_NEGS, "r 0", "Y F7 /3 R1"}, /* neg r0 */ + {MIR_NEGS, "m2 0", "Y F7 /3 m1"}, /* neg m0 */ + + {MIR_FNEG, "r 0", "Y 0F 57 r0 c0000000080000000"}, /* xorps r0,80000000 */ + {MIR_DNEG, "r 0", "66 Y 0F 57 r0 c8000000000000000"}, /* xorpd r0,0x8000000000000000 */ + {MIR_LDNEG, "mld mld", "DB /5 m1; D9 E0; DB /7 m0"}, /* fld m1; fchs; fstp m0 */ + + IOP (MIR_ADD, "03", "01", "83 /0", "81 /0") /* x86_64 int additions */ + + {MIR_ADD, "r r r", "X 8D r0 ap"}, /* lea r0,(r1,r2)*/ + {MIR_ADD, "r r i2", "X 8D r0 ap"}, /* lea r0,i2(r1)*/ + {MIR_ADDS, "r r r", "Y 8D r0 ap"}, /* lea r0,(r1,r2)*/ + {MIR_ADDS, "r r i2", "Y 8D r0 ap"}, /* lea r0,i2(r1)*/ + + IOP (MIR_SUB, "2B", "29", "83 /5", "81 /5") /* x86_64 int subtractions */ + + {MIR_MUL, "r 0 r", "X 0F AF r0 R2"}, /* imul r0,r1*/ + {MIR_MUL, "r 0 m3", "X 0F AF r0 m2"}, /* imul r0,m1*/ + {MIR_MUL, "r r i2", "X 69 r0 R1 I2"}, /* imul r0,r1,i32*/ + {MIR_MUL, "r m3 i2", "X 69 r0 m1 I2"}, /* imul r0,m1,i32*/ + {MIR_MUL, "r r s", "X 8D r0 ap"}, /* lea r0,(,r1,s2)*/ + {MIR_MULS, "r 0 r", "Y 0F AF r0 R2"}, /* imul r0,r1*/ + {MIR_MULS, "r 0 m2", "Y 0F AF r0 m2"}, /* imul r0,m1*/ + {MIR_MULS, "r r i2", "Y 69 r0 R1 I2"}, /* imul r0,r1,i32*/ + {MIR_MULS, "r m2 i2", "Y 69 r0 m1 I2"}, /* imul r0,m1,i32*/ + {MIR_MULS, "r r s", "Y 8D r0 ap"}, /* lea r0,(,r1,s2)*/ + + {MIR_DIV, "h0 h0 r", "X 99; X F7 /7 R2"}, /* cqo; idiv r2*/ + {MIR_DIV, "h0 h0 m3", "X 99; X F7 /7 m2"}, /* cqo; idiv m2*/ + {MIR_DIVS, "h0 h0 r", "Y 99; Y F7 /7 R2"}, /* cqo; idiv r2*/ + {MIR_DIVS, "h0 h0 m2", "Y 99; Y F7 /7 m2"}, /* cqo; idiv m2*/ + + {MIR_UDIV, "h0 h0 r", "31 D2; X F7 /6 R2"}, /* xorl edx,edx; div r2*/ + {MIR_UDIV, "h0 h0 m3", "31 D2; X F7 /6 m2"}, /* xorl edx,edx; div m2*/ + {MIR_UDIVS, "h0 h0 r", "31 D2; Y F7 /6 R2"}, /* xorl edx,edx; div r2*/ + {MIR_UDIVS, "h0 h0 m2", "31 D2; Y F7 /6 m2"}, /* xorl edx,edx; div m2*/ + + {MIR_MOD, "h2 h0 r", "X 99; X F7 /7 R2"}, /* cqo; idiv r2*/ + {MIR_MOD, "h2 h0 m3", "X 99; X F7 /7 m2"}, /* cqo; idiv m2*/ + {MIR_MODS, "h2 h0 r", "Y 99; Y F7 /7 R2"}, /* cqo; idiv r2*/ + {MIR_MODS, "h2 h0 m2", "Y 99; Y F7 /7 m2"}, /* cqo; idiv m2*/ + + {MIR_UMOD, "h2 h0 r", "31 D2; X F7 /6 R2"}, /* xorl edx,edx; div r2*/ + {MIR_UMOD, "h2 h0 m3", "31 D2; X F7 /6 m2"}, /* xorl edx,edx; div m2*/ + {MIR_UMODS, "h2 h0 r", "31 D2; Y F7 /6 R2"}, /* xorl edx,edx; div r2*/ + {MIR_UMODS, "h2 h0 m2", "31 D2; Y F7 /6 m2"}, /* xorl edx,edx; div m2*/ + + IOP (MIR_AND, "23", "21", "83 /4", "81 /4") /*ands*/ + IOP (MIR_OR, "0B", "09", "83 /1", "81 /1") IOP (MIR_XOR, "33", "31", "83 /6", "81 /6") /*(x)ors*/ + + FOP (MIR_FADD, "F3 Y 0F 58") DOP (MIR_DADD, "F2 Y 0F 58") FOP (MIR_FSUB, "F3 Y 0F 5C") /**/ + DOP (MIR_DSUB, "F2 Y 0F 5C") FOP (MIR_FMUL, "F3 Y 0F 59") DOP (MIR_DMUL, "F2 Y 0F 59") /**/ + FOP (MIR_FDIV, "F3 Y 0F 5E") DOP (MIR_DDIV, "F2 Y 0F 5E") /**/ + + LDOP (MIR_LDADD, "DE C1") LDOP (MIR_LDSUB, "DE E9") /* long double adds/subs */ + LDOP (MIR_LDMUL, "DE C9") LDOP (MIR_LDDIV, "DE F9") /* long double muls/divs */ + + SHOP (MIR_LSH, "D3 /4", "C1 /4") SHOP (MIR_RSH, "D3 /7", "C1 /7") /* arithm shifts */ + SHOP (MIR_URSH, "D3 /5", "C1 /5") /* logical shifts */ + + CMP (MIR_EQ, "0F 94") CMP (MIR_NE, "0F 95") CMP (MIR_LT, "0F 9C") /* 1.int cmps */ + CMP (MIR_ULT, "0F 92") CMP (MIR_LE, "0F 9E") CMP (MIR_ULE, "0F 96") /* 2.int cmps */ + CMP (MIR_GT, "0F 9F") CMP (MIR_UGT, "0F 97") CMP (MIR_GE, "0F 9D") /* 3.int cmps */ + CMP (MIR_UGE, "0F 93") /* 4.int cmps */ + + FEQ (MIR_FEQ, "V0", "0F 9B") DEQ (MIR_DEQ, "V0", "0F 9B") /* 1. fp cmps */ + LDEQ (MIR_LDEQ, "V0", "0F 9B") FEQ (MIR_FNE, "V1", "0F 9A") /* 2. fp cmps */ + DEQ (MIR_DNE, "V1", "0F 9A") LDEQ (MIR_LDNE, "V1", "0F 9A") /* 3. fp cmps */ + + FCMP (MIR_FLT, "0F 92") DCMP (MIR_DLT, "0F 92") LDCMP (MIR_LDLT, "0F 92") /*4*/ + FCMP (MIR_FLE, "0F 96") DCMP (MIR_DLE, "0F 96") LDCMP (MIR_LDLE, "0F 96") /*5*/ + FCMP (MIR_FGT, "0F 97") DCMP (MIR_DGT, "0F 97") LDCMP (MIR_LDGT, "0F 97") /*6*/ + FCMP (MIR_FGE, "0F 93") DCMP (MIR_DGE, "0F 93") LDCMP (MIR_LDGE, "0F 93") /*7*/ + + {MIR_JMP, "l", "E9 l0"}, /* 32-bit offset jmp */ + + /* movq TableAddress,r11; mov (r11,r,8),r11; jmp *r11; TableContent */ + {MIR_SWITCH, "r $", "49 BB T; X 8B hB mT; 41 FF E3"}, + + BR (MIR_BT, "0F 85") BR (MIR_BF, "0F 84") /* branches */ + + BCMP (MIR_BEQ, "0F 84") BCMP (MIR_BNE, "0F 85") /* 1. int compare and branch */ + BCMP (MIR_BLT, "0F 8C") BCMP (MIR_UBLT, "0F 82") /* 2. int compare and branch */ + BCMP (MIR_BLE, "0F 8E") BCMP (MIR_UBLE, "0F 86") /* 3. int compare and branch */ + BCMP (MIR_BGT, "0F 8F") BCMP (MIR_UBGT, "0F 87") /* 4. int compare and branch */ + BCMP (MIR_BGE, "0F 8D") BCMP (MIR_UBGE, "0F 83") /* 5. int compare and branch */ + + FBCMP (MIR_FBLT, "0F 82") DBCMP (MIR_DBLT, "0F 82") /* 1. fp cmp and branch */ + LDBCMP (MIR_LDBLT, "0F 82") FBCMP (MIR_FBLE, "0F 86") /* 2. fp cmp and branch */ + DBCMP (MIR_DBLE, "0F 86") LDBCMP (MIR_LDBLE, "0F 86") /* 3. fp cmp and branch */ + FBCMP (MIR_FBGT, "0F 87") DBCMP (MIR_DBGT, "0F 87") /* 4. fp cmp and branch */ + LDBCMP (MIR_LDBGT, "0F 87") FBCMP (MIR_FBGE, "0F 83") /* 5. fp cmp and branch */ + DBCMP (MIR_DBGE, "0F 83") LDBCMP (MIR_LDBGE, "0F 83") /* 6. fp cmp and branch */ + + {MIR_FBEQ, "l r r", "Y 0F 2E r1 R2; 7A v6; 0F 84 l0"}, /* ucomiss r0,r1;jp L;je rel32 L: */ + {MIR_DBEQ, "l r r", "66 Y 0F 2E r1 R2; 7A v6; 0F 84 l0"}, /* ucomisd r0,r1;jp L;je rel32 L: */ + /* fld m2;fld m1;fucomip st,st1;fstp st;jp L;je rel32 L: */ + {MIR_LDBEQ, "l mld mld", "DB /5 m2; DB /5 m1; DF E9; DD D8; 7A v6; 0F 84 l0"}, + + {MIR_FBNE, "l r r", "Y 0F 2E r1 R2; 0F 8A l0; 0F 85 l0"}, /* ucomiss r0,r1;jp rel32;jne rel32*/ + {MIR_DBNE, "l r r", "66 Y 0F 2E r1 R2; 0F 8A l0; 0F 85 l0"}, /* ucomisd r0,r1;jp rel32;jne rel32*/ + /* fld m2;fld m1;fucomip st,st1;fstp st;jp rel32;jne rel32 */ + {MIR_LDBNE, "l mld mld", "DB /5 m2; DB /5 m1; DF E9; DD D8; 0F 8A l0; 0F 85 l0"}, + + {MIR_CALL, "X r $", "Y FF /2 R1"}, /* call *r1 */ + + {MIR_RET, "$", "C3"}, /* ret ax, dx, xmm0, xmm1, st0, st1 */ +}; + +static void get_early_clobbered_hard_reg (MIR_insn_t insn, MIR_reg_t *hr1, MIR_reg_t *hr2) { + MIR_insn_code_t code = insn->code; + + *hr1 = *hr2 = MIR_NON_HARD_REG; + if (code == MIR_DIV || code == MIR_UDIV || code == MIR_DIVS || code == MIR_UDIVS + || code == MIR_MOD || code == MIR_UMOD || code == MIR_MODS || code == MIR_UMODS) { + *hr1 = DX_HARD_REG; + } else if (code == MIR_FEQ || code == MIR_FNE || code == MIR_DEQ || code == MIR_DNE + || code == MIR_LDEQ || code == MIR_LDNE) { + *hr1 = AX_HARD_REG; + *hr2 = DX_HARD_REG; + } else if (code == MIR_FLT || code == MIR_FLE || code == MIR_FGT || code == MIR_FGE + || code == MIR_DLT || code == MIR_DLE || code == MIR_DGT || code == MIR_DGE + || code == MIR_LDLT || code == MIR_LDLE || code == MIR_LDGT || code == MIR_LDGE) { + *hr1 = AX_HARD_REG; + } +} + +// constraint: esp can not be index + +static int int8_p (int64_t v) { return INT8_MIN <= v && v <= INT8_MAX; } +static int uint8_p (int64_t v) { return 0 <= v && v <= UINT8_MAX; } +static int int16_p (int64_t v) { return INT16_MIN <= v && v <= INT16_MAX; } +static int MIR_UNUSED uint16_p (int64_t v) { return 0 <= v && v <= UINT16_MAX; } +static int int32_p (int64_t v) { return INT32_MIN <= v && v <= INT32_MAX; } +static int uint32_p (int64_t v) { return 0 <= v && v <= UINT32_MAX; } + +static int pattern_index_cmp (const void *a1, const void *a2) { + int i1 = *(const int *) a1, i2 = *(const int *) a2; + int c1 = (int) patterns[i1].code, c2 = (int) patterns[i2].code; + + return c1 != c2 ? c1 - c2 : (long) i1 - (long) i2; +} + +static void patterns_init (MIR_context_t ctx) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + int i, ind, n = sizeof (patterns) / sizeof (struct pattern); + MIR_insn_code_t prev_code, code; + insn_pattern_info_t *info_addr; + insn_pattern_info_t pinfo = {0, 0}; + + VARR_CREATE (int, pattern_indexes, 0); + for (i = 0; i < n; i++) VARR_PUSH (int, pattern_indexes, i); + qsort (VARR_ADDR (int, pattern_indexes), n, sizeof (int), pattern_index_cmp); + VARR_CREATE (insn_pattern_info_t, insn_pattern_info, 0); + for (i = 0; i < MIR_INSN_BOUND; i++) VARR_PUSH (insn_pattern_info_t, insn_pattern_info, pinfo); + info_addr = VARR_ADDR (insn_pattern_info_t, insn_pattern_info); + for (prev_code = MIR_INSN_BOUND, i = 0; i < n; i++) { + ind = VARR_GET (int, pattern_indexes, i); + if ((code = patterns[ind].code) != prev_code) { + if (i != 0) info_addr[prev_code].num = i - info_addr[prev_code].start; + info_addr[code].start = i; + prev_code = code; + } + } + assert (prev_code != MIR_INSN_BOUND); + info_addr[prev_code].num = n - info_addr[prev_code].start; +} + +static int pattern_match_p (MIR_context_t ctx, const struct pattern *pat, MIR_insn_t insn) { + int nop, n; + size_t nops = MIR_insn_nops (ctx, insn); + const char *p; + char ch, start_ch; + MIR_op_mode_t mode; + MIR_op_t op, original; + MIR_reg_t hr; + + for (nop = 0, p = pat->pattern; *p != 0; p++, nop++) { + while (*p == ' ' || *p == '\t') p++; + if (*p == '$') return TRUE; + if (MIR_call_code_p (insn->code) && nop >= nops) return FALSE; + gen_assert (nop < nops); + op = insn->ops[nop]; + switch (start_ch = *p) { + case 'X': break; + case 'r': + if (op.mode != MIR_OP_HARD_REG) return FALSE; + break; + case 'h': + if (op.mode != MIR_OP_HARD_REG) return FALSE; + ch = *++p; + gen_assert ('0' <= ch && ch <= '9'); + hr = ch - '0'; + ch = *++p; + if ('0' <= ch && ch <= '9') + hr = hr * 10 + ch - '0'; + else + --p; + if (op.u.hard_reg != hr) return FALSE; + break; + case 'z': + if ((op.mode != MIR_OP_INT && op.mode != MIR_OP_UINT) || op.u.i != 0) return FALSE; + break; + case 'i': + if (op.mode != MIR_OP_INT && op.mode != MIR_OP_UINT) return FALSE; + ch = *++p; + gen_assert ('0' <= ch && ch <= '3'); + if ((ch == '0' && !int8_p (op.u.i)) || (ch == '1' && !int16_p (op.u.i)) + || (ch == '2' && !int32_p (op.u.i))) + return FALSE; + break; + case 'p': + if (op.mode != MIR_OP_REF) return FALSE; + break; + case 's': + if ((op.mode != MIR_OP_INT && op.mode != MIR_OP_UINT) + || (op.u.i != 1 && op.u.i != 2 && op.u.i != 4 && op.u.i != 8)) + return FALSE; + break; + case 'm': { + MIR_type_t type, type2, type3 = MIR_T_BOUND; + int u_p, s_p; + + if (op.mode != MIR_OP_HARD_REG_MEM) return FALSE; + u_p = s_p = TRUE; + ch = *++p; + switch (ch) { + case 'f': + type = MIR_T_F; + type2 = MIR_T_BOUND; + break; + case 'd': + type = MIR_T_D; + type2 = MIR_T_BOUND; + break; + case 'l': + ch = *++p; + gen_assert (ch == 'd'); + type = MIR_T_LD; + type2 = MIR_T_BOUND; + break; + case 'u': + case 's': + u_p = ch == 'u'; + s_p = ch == 's'; + ch = *++p; + /* Fall through: */ + default: + gen_assert ('0' <= ch && ch <= '3'); + if (ch == '0') { + type = u_p ? MIR_T_U8 : MIR_T_I8; + type2 = u_p && s_p ? MIR_T_I8 : MIR_T_BOUND; + } else if (ch == '1') { + type = u_p ? MIR_T_U16 : MIR_T_I16; + type2 = u_p && s_p ? MIR_T_I16 : MIR_T_BOUND; + } else if (ch == '2') { + type = u_p ? MIR_T_U32 : MIR_T_I32; + type2 = u_p && s_p ? MIR_T_I32 : MIR_T_BOUND; +#if MIR_PTR32 + if (u_p) type3 = MIR_T_P; +#endif + } else { + type = u_p ? MIR_T_U64 : MIR_T_I64; + type2 = u_p && s_p ? MIR_T_I64 : MIR_T_BOUND; +#if MIR_PTR64 + type3 = MIR_T_P; +#endif + } + } + if (op.u.hard_reg_mem.type != type && op.u.hard_reg_mem.type != type2 + && op.u.hard_reg_mem.type != type3) + return FALSE; + if (op.u.hard_reg_mem.index != MIR_NON_HARD_REG && op.u.hard_reg_mem.scale != 1 + && op.u.hard_reg_mem.scale != 2 && op.u.hard_reg_mem.scale != 4 + && op.u.hard_reg_mem.scale != 8) + return FALSE; + break; + } + case 'l': + if (op.mode != MIR_OP_LABEL) return FALSE; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + n = start_ch - '0'; + gen_assert (n < nop); + original = insn->ops[n]; + mode = op.mode; + if (mode == MIR_OP_UINT) mode = MIR_OP_INT; + if (original.mode != mode && (original.mode != MIR_OP_UINT || mode != MIR_OP_INT)) + return FALSE; + gen_assert (mode == MIR_OP_HARD_REG || mode == MIR_OP_INT || mode == MIR_OP_FLOAT + || mode == MIR_OP_DOUBLE || mode == MIR_OP_LDOUBLE || mode == MIR_OP_HARD_REG_MEM + || mode == MIR_OP_LABEL); + if (mode == MIR_OP_HARD_REG && op.u.hard_reg != original.u.hard_reg) + return FALSE; + else if (mode == MIR_OP_INT && op.u.i != original.u.i) + return FALSE; + else if (mode == MIR_OP_FLOAT && op.u.f != original.u.f) + return FALSE; + else if (mode == MIR_OP_DOUBLE && op.u.d != original.u.d) + return FALSE; + else if (mode == MIR_OP_LDOUBLE && op.u.ld != original.u.ld) + return FALSE; + else if (mode == MIR_OP_LABEL && op.u.label != original.u.label) + return FALSE; + else if (mode == MIR_OP_HARD_REG_MEM && op.u.hard_reg_mem.type != original.u.hard_reg_mem.type + && op.u.hard_reg_mem.scale != original.u.hard_reg_mem.scale + && op.u.hard_reg_mem.base != original.u.hard_reg_mem.base + && op.u.hard_reg_mem.index != original.u.hard_reg_mem.index + && op.u.hard_reg_mem.disp != original.u.hard_reg_mem.disp) + return FALSE; + break; + default: gen_assert (FALSE); + } + } + gen_assert (nop == nops); + return TRUE; +} + +static const char *find_insn_pattern_replacement (MIR_context_t ctx, MIR_insn_t insn) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + int i; + const struct pattern *pat; + insn_pattern_info_t info = VARR_GET (insn_pattern_info_t, insn_pattern_info, insn->code); + + for (i = 0; i < info.num; i++) { + pat = &patterns[VARR_GET (int, pattern_indexes, info.start + i)]; + if (pattern_match_p (ctx, pat, insn)) return pat->replacement; + } + return NULL; +} + +static void patterns_finish (MIR_context_t ctx) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + + VARR_DESTROY (int, pattern_indexes); + VARR_DESTROY (insn_pattern_info_t, insn_pattern_info); +} + +static int hex_value (int ch) { + return '0' <= ch && ch <= '9' ? ch - '0' : 'A' <= ch && ch <= 'F' ? ch - 'A' + 10 : -1; +} + +static uint64_t read_hex (const char **ptr) { + int v; + const char *p; + uint64_t res = 0; + + for (p = *ptr; (v = hex_value (*p)) >= 0; p++) { + gen_assert ((res >> 60) == 0); + res = res * 16 + v; + } + gen_assert (p != *ptr); + *ptr = p - 1; + return res; +} + +static void setup_r (int *rex, int *r, int v) { + gen_assert ((rex == NULL || *rex < 0) && *r < 0 && v >= 0 && v <= MAX_HARD_REG); + if (v >= 16) v -= 16; + if (v >= 8) { + if (rex != NULL) *rex = 1; + v -= 8; + } + *r = v; +} + +static void setup_reg (int *rex_reg, int *reg, int v) { setup_r (rex_reg, reg, v); } + +static void setup_rm (int *rex_b, int *rm, int v) { setup_r (rex_b, rm, v); } + +static void setup_mod (int *mod, int v) { + gen_assert (*mod < 0 && v >= 0 && v <= 3); + *mod = v; +} + +static void setup_scale (int *scale, int v) { + gen_assert (*scale < 0 && v >= 0 && v <= 3); + *scale = v; +} + +static void setup_base (int *rex_b, int *base, int v) { setup_r (rex_b, base, v); } + +static void setup_index (int *rex_i, int *index, int v) { setup_r (rex_i, index, v); } + +static void setup_rip_rel_addr (MIR_disp_t rip_disp, int *mod, int *rm, int64_t *disp32) { + gen_assert (*mod < 0 && *rm < 0 && *disp32 < 0); + setup_rm (NULL, rm, 5); + gen_assert (int32_p (rip_disp)); + setup_mod (mod, 0); + *disp32 = (uint32_t) rip_disp; +} + +static void setup_mem (MIR_mem_t mem, int *mod, int *rm, int *scale, int *base, int *rex_b, + int *index, int *rex_x, int *disp8, int64_t *disp32) { + MIR_disp_t disp = mem.disp; + + gen_assert (*disp8 < 0 && *disp32 < 0 && mem.index != SP_HARD_REG); + if (mem.index == MIR_NON_HARD_REG && mem.base == MIR_NON_HARD_REG) { /* SIB: disp only */ + setup_rm (NULL, rm, 4); + *disp32 = (uint32_t) disp; + setup_base (NULL, base, BP_HARD_REG); + setup_index (NULL, index, SP_HARD_REG); + } else if (mem.index == MIR_NON_HARD_REG && mem.base != SP_HARD_REG && mem.base != R12_HARD_REG) { + setup_rm (rex_b, rm, mem.base); + if (disp == 0 && mem.base != BP_HARD_REG && mem.base != R13_HARD_REG) { + setup_mod (mod, 0); + } else if (int8_p (disp)) { + setup_mod (mod, 1); + *disp8 = (uint8_t) disp; + } else { + setup_mod (mod, 2); + *disp32 = (uint32_t) disp; + } + } else if (mem.index == MIR_NON_HARD_REG) { /* SIB: only base = sp or r12 */ + setup_rm (NULL, rm, 4); + setup_index (NULL, index, SP_HARD_REG); + setup_base (rex_b, base, mem.base); + if (disp == 0) { + setup_mod (mod, 0); + } else if (int8_p (disp)) { + setup_mod (mod, 1); + *disp8 = (uint8_t) disp; + } else { + setup_mod (mod, 2); + *disp32 = (uint32_t) disp; + } + } else if (mem.base == MIR_NON_HARD_REG) { /* SIB: index with scale only */ + setup_rm (NULL, rm, 4); + setup_index (rex_x, index, mem.index); + setup_base (NULL, base, BP_HARD_REG); + setup_mod (mod, 0); + *disp32 = (uint32_t) disp; + setup_scale (scale, mem.scale == 1 ? 0 : mem.scale == 2 ? 1 : mem.scale == 4 ? 2 : 3); + } else { /* SIB: base and index */ + setup_rm (NULL, rm, 4); + setup_base (rex_b, base, mem.base); + setup_index (rex_x, index, mem.index); + setup_scale (scale, mem.scale == 1 ? 0 : mem.scale == 2 ? 1 : mem.scale == 4 ? 2 : 3); + if (disp == 0 && mem.base != BP_HARD_REG && mem.base != R13_HARD_REG) { + setup_mod (mod, 0); + } else if (int8_p (disp)) { + setup_mod (mod, 1); + *disp8 = (uint8_t) disp; + } else { + setup_mod (mod, 2); + *disp32 = (uint32_t) disp; + } + } +} + +static void put_byte (struct gen_ctx *gen_ctx, int byte) { VARR_PUSH (uint8_t, result_code, byte); } + +static void put_uint64 (struct gen_ctx *gen_ctx, uint64_t v, int nb) { + for (; nb > 0; nb--) { + put_byte (gen_ctx, v & 0xff); + v >>= 8; + } +} + +static void set_int64 (uint8_t *addr, int64_t v, int nb) { + for (; nb > 0; nb--) { + *addr++ = v & 0xff; + v >>= 8; + } +} + +static int64_t get_int64 (uint8_t *addr, int nb) { + int64_t v = 0; + int i, sh = (8 - nb) * 8; + + for (i = nb - 1; i >= 0; i--) v = (v << 8) | addr[i]; + if (sh > 0) v = (v << sh) >> sh; /* make it signed */ + return v; +} + +static size_t add_to_const_pool (struct gen_ctx *gen_ctx, uint64_t v) { + uint64_t *addr = VARR_ADDR (uint64_t, const_pool); + size_t n, len = VARR_LENGTH (uint64_t, const_pool); + + for (n = 0; n < len; n++) + if (addr[n] == v) return n; + VARR_PUSH (uint64_t, const_pool, v); + return len; +} + +static int setup_imm_addr (struct gen_ctx *gen_ctx, uint64_t v, int *mod, int *rm, + int64_t *disp32) { + const_ref_t cr; + size_t n; + + n = add_to_const_pool (gen_ctx, v); + setup_rip_rel_addr (0, mod, rm, disp32); + cr.pc = 0; + cr.const_num = n; + VARR_PUSH (const_ref_t, const_refs, cr); + return VARR_LENGTH (const_ref_t, const_refs) - 1; +} + +static void out_insn (MIR_context_t ctx, MIR_insn_t insn, const char *replacement) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + const char *p, *insn_str; + label_ref_t lr; + int switch_table_addr_start = -1; + + if (insn->code == MIR_ALLOCA + && (insn->ops[1].mode == MIR_OP_INT || insn->ops[1].mode == MIR_OP_UINT)) + insn->ops[1].u.u = (insn->ops[1].u.u + 15) & -16; + for (insn_str = replacement;; insn_str = p + 1) { + char ch, start_ch, d1, d2; + int opcode0 = -1, opcode1 = -1, opcode2 = -1; + int rex_w = -1, rex_r = -1, rex_x = -1, rex_b = -1, rex_0 = -1; + int mod = -1, reg = -1, rm = -1; + int scale = -1, index = -1, base = -1; + int prefix = -1, disp8 = -1, imm8 = -1, lb = -1; + int64_t disp32 = -1, imm32 = -1; + int imm64_p = FALSE; + uint64_t imm64, v; + MIR_op_t op; + int const_ref_num = -1, label_ref_num = -1, switch_table_addr_p = FALSE; + + for (p = insn_str; (ch = *p) != '\0' && ch != ';'; p++) { + if ((d1 = hex_value (ch = *p)) >= 0) { + d2 = hex_value (ch = *++p); + gen_assert (d2 >= 0); + if (opcode0 == -1) + opcode0 = d1 * 16 + d2; + else if (opcode1 == -1) + opcode1 = d1 * 16 + d2; + else { + gen_assert (opcode2 == -1); + opcode2 = d1 * 16 + d2; + } + p++; + } + if ((ch = *p) == 0 || ch == ';') break; + switch ((start_ch = ch = *p)) { + case ' ': + case '\t': break; + case 'X': + if (opcode0 >= 0) { + gen_assert (opcode1 < 0); + prefix = opcode0; + opcode0 = -1; + } + rex_w = 1; + break; + case 'Y': + if (opcode0 >= 0) { + gen_assert (opcode1 < 0); + prefix = opcode0; + opcode0 = -1; + } + rex_w = 0; + break; + case 'Z': + if (opcode0 >= 0) { + gen_assert (opcode1 < 0); + prefix = opcode0; + opcode0 = -1; + } + rex_w = 0; + rex_0 = 0; + break; + case 'r': + case 'R': + ch = *++p; + gen_assert ('0' <= ch && ch <= '2'); + op = insn->ops[ch - '0']; + gen_assert (op.mode == MIR_OP_HARD_REG); + if (start_ch == 'r') + setup_reg (&rex_r, ®, op.u.hard_reg); + else { + setup_rm (&rex_b, &rm, op.u.hard_reg); + setup_mod (&mod, 3); + } + break; + case 'm': + ch = *++p; + if (ch == 't') { /* -16(%rsp) */ + setup_rm (NULL, &rm, 4); + setup_index (NULL, &index, SP_HARD_REG); + setup_base (&rex_b, &base, SP_HARD_REG); + setup_mod (&mod, 1); + disp8 = (uint8_t) -16; + } else if (ch == 'T') { + MIR_op_t mem; + + op = insn->ops[0]; + gen_assert (op.mode == MIR_OP_HARD_REG); + mem = _MIR_new_hard_reg_mem_op (ctx, MIR_T_I64, 0, R11_HARD_REG, op.u.hard_reg, 8); + setup_mem (mem.u.hard_reg_mem, &mod, &rm, &scale, &base, &rex_b, &index, &rex_x, &disp8, + &disp32); + } else { + gen_assert ('0' <= ch && ch <= '2'); + op = insn->ops[ch - '0']; + gen_assert (op.mode == MIR_OP_HARD_REG_MEM); + setup_mem (op.u.hard_reg_mem, &mod, &rm, &scale, &base, &rex_b, &index, &rex_x, &disp8, + &disp32); + } + break; + case 'a': { + MIR_mem_t mem; + MIR_op_t op2; + + ch = *++p; + op = insn->ops[1]; + gen_assert (op.mode == MIR_OP_HARD_REG); + mem.type = MIR_T_I8; + if (ch == 'p') { + op2 = insn->ops[2]; + mem.base = op.u.hard_reg; + mem.scale = 1; + if (op2.mode == MIR_OP_HARD_REG) { + mem.index = op2.u.hard_reg; + mem.disp = 0; + } else { + gen_assert (op2.mode == MIR_OP_INT || op2.mode == MIR_OP_UINT); + mem.index = MIR_NON_HARD_REG; + mem.disp = op2.u.i; + } + } else if (ch == 'd') { + mem.base = op.u.hard_reg; + mem.index = MIR_NON_HARD_REG; + mem.scale = 1; + ++p; + mem.disp = read_hex (&p); + } else { + gen_assert (ch == 'm'); + op2 = insn->ops[2]; + mem.index = op.u.hard_reg; + mem.base = MIR_NON_HARD_REG; + mem.disp = 0; + gen_assert ((op2.mode == MIR_OP_INT || op2.mode == MIR_OP_UINT) + && (op2.u.i == 1 || op2.u.i == 2 || op2.u.i == 4 || op2.u.i == 8)); + mem.scale = op2.u.i; + } + setup_mem (mem, &mod, &rm, &scale, &base, &rex_b, &index, &rex_x, &disp8, &disp32); + break; + } + case 'i': + case 'I': + case 'J': + ch = *++p; + gen_assert ('0' <= ch && ch <= '7'); + op = insn->ops[ch - '0']; + gen_assert (op.mode == MIR_OP_INT || op.mode == MIR_OP_UINT); + if (start_ch == 'i') { + gen_assert (int8_p (op.u.i)); + imm8 = (uint8_t) op.u.i; + } else if (start_ch == 'I') { + gen_assert (int32_p (op.u.i)); + imm32 = (uint32_t) op.u.i; + } else { + imm64_p = TRUE; + imm64 = (uint64_t) op.u.i; + } + break; + case 'P': + ch = *++p; + gen_assert ('0' <= ch && ch <= '7'); + op = insn->ops[ch - '0']; + gen_assert (op.mode == MIR_OP_REF); + imm64_p = TRUE; + if (op.u.ref->item_type == MIR_data_item && op.u.ref->u.data->name != NULL + && _MIR_reserved_ref_name_p (ctx, op.u.ref->u.data->name)) + imm64 = (uint64_t) op.u.ref->u.data->u.els; + else + imm64 = (uint64_t) op.u.ref->addr; + break; + case 'T': { + gen_assert (!switch_table_addr_p && switch_table_addr_start < 0); + switch_table_addr_p = TRUE; + break; + } + case 'l': { + ch = *++p; + gen_assert ('0' <= ch && ch <= '2'); + op = insn->ops[ch - '0']; + gen_assert (op.mode == MIR_OP_LABEL); + lr.abs_addr_p = FALSE; + lr.label_val_disp = lr.next_insn_disp = 0; + lr.label = op.u.label; + gen_assert (label_ref_num < 0 && disp32 < 0); + disp32 = 0; /* To reserve the space */ + label_ref_num = VARR_LENGTH (label_ref_t, label_refs); + VARR_PUSH (label_ref_t, label_refs, lr); + break; + } + case '/': + ch = *++p; + gen_assert ('0' <= ch && ch <= '7'); + setup_reg (NULL, ®, ch - '0'); + break; + case '+': + ch = *++p; + gen_assert ('0' <= ch && ch <= '2'); + op = insn->ops[ch - '0']; + gen_assert (op.mode == MIR_OP_HARD_REG); + setup_reg (&rex_b, &lb, op.u.hard_reg); + break; + case 'c': + ++p; + v = read_hex (&p); + gen_assert (const_ref_num < 0 && disp32 < 0); + const_ref_num = setup_imm_addr (gen_ctx, v, &mod, &rm, &disp32); + break; + case 'h': + ++p; + v = read_hex (&p); + gen_assert (v <= 31); + setup_reg (&rex_r, ®, v); + break; + case 'H': + ++p; + v = read_hex (&p); + gen_assert (v <= 31); + setup_rm (&rex_b, &rm, v); + setup_mod (&mod, 3); + break; + case 'v': + case 'V': + ++p; + v = read_hex (&p); + if (start_ch == 'v') { + gen_assert (uint8_p (v)); + imm8 = v; + } else { + gen_assert (uint32_p (v)); + imm32 = v; + } + break; + default: gen_assert (FALSE); + } + } + if (prefix >= 0) put_byte (gen_ctx, prefix); + + if (rex_w > 0 || rex_r >= 0 || rex_x >= 0 || rex_b >= 0 || rex_0 >= 0) { + if (rex_w < 0) rex_w = 0; + if (rex_r < 0) rex_r = 0; + if (rex_x < 0) rex_x = 0; + if (rex_b < 0) rex_b = 0; + gen_assert (rex_w <= 1 && rex_r <= 1 && rex_x <= 1 && rex_b <= 1); + put_byte (gen_ctx, 0x40 | (rex_w << 3) | (rex_r << 2) | (rex_x << 1) | rex_b); + } + + gen_assert (opcode0 >= 0 && lb <= 7); + if (lb >= 0) opcode0 |= lb; + put_byte (gen_ctx, opcode0); + + if (opcode1 >= 0) put_byte (gen_ctx, opcode1); + if (opcode2 >= 0) put_byte (gen_ctx, opcode2); + + if (mod >= 0 || reg >= 0 || rm >= 0) { + if (mod < 0) mod = 0; + if (reg < 0) reg = 0; + if (rm < 0) rm = 0; + gen_assert (mod <= 3 && reg <= 7 && rm <= 7); + put_byte (gen_ctx, (mod << 6) | (reg << 3) | rm); + } + if (scale >= 0 || base >= 0 || index >= 0) { + if (scale < 0) scale = 0; + if (base < 0) base = 0; + if (index < 0) index = 0; + gen_assert (scale <= 3 && base <= 7 && index <= 7); + put_byte (gen_ctx, (scale << 6) | (index << 3) | base); + } + if (const_ref_num >= 0) + VARR_ADDR (const_ref_t, const_refs)[const_ref_num].pc = VARR_LENGTH (uint8_t, result_code); + if (label_ref_num >= 0) + VARR_ADDR (label_ref_t, label_refs) + [label_ref_num].label_val_disp + = VARR_LENGTH (uint8_t, result_code); + if (disp8 >= 0) put_byte (gen_ctx, disp8); + if (disp32 >= 0) put_uint64 (gen_ctx, disp32, 4); + if (imm8 >= 0) put_byte (gen_ctx, imm8); + if (imm32 >= 0) put_uint64 (gen_ctx, imm32, 4); + if (imm64_p) put_uint64 (gen_ctx, imm64, 8); + + if (switch_table_addr_p) { + switch_table_addr_start = VARR_LENGTH (uint8_t, result_code); + put_uint64 (gen_ctx, 0, 8); + } + + if (label_ref_num >= 0) + VARR_ADDR (label_ref_t, label_refs) + [label_ref_num].next_insn_disp + = VARR_LENGTH (uint8_t, result_code); + + if (const_ref_num >= 0) + VARR_ADDR (const_ref_t, const_refs) + [const_ref_num].next_insn_disp + = VARR_LENGTH (uint8_t, result_code); + if (ch == '\0') break; + } + if (switch_table_addr_start < 0) return; + gen_assert (insn->code == MIR_SWITCH); + VARR_PUSH (uint64_t, abs_address_locs, switch_table_addr_start); + set_int64 (&VARR_ADDR (uint8_t, result_code)[switch_table_addr_start], + (int64_t) VARR_LENGTH (uint8_t, result_code), 8); + for (size_t i = 1; i < insn->nops; i++) { + gen_assert (insn->ops[i].mode == MIR_OP_LABEL); + lr.abs_addr_p = TRUE; + lr.label_val_disp = VARR_LENGTH (uint8_t, result_code); + lr.label = insn->ops[i].u.label; + VARR_PUSH (label_ref_t, label_refs, lr); + put_uint64 (gen_ctx, 0, 8); + } +} + +static uint8_t MIR_UNUSED get_short_jump_opcode (uint8_t *long_jump_opcode) { + gen_assert (long_jump_opcode[0] == 0x0F && long_jump_opcode[1] > 0x10); + return long_jump_opcode[1] - 0x10; +} + +static int insn_ok_p (MIR_context_t ctx, MIR_insn_t insn) { + return find_insn_pattern_replacement (ctx, insn) != NULL; +} + +static uint8_t *target_translate (MIR_context_t ctx, size_t *len) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + size_t i; + MIR_insn_t insn; + const char *replacement; + + gen_assert (curr_func_item->item_type == MIR_func_item); + VARR_TRUNC (uint8_t, result_code, 0); + VARR_TRUNC (uint64_t, const_pool, 0); + VARR_TRUNC (const_ref_t, const_refs, 0); + VARR_TRUNC (label_ref_t, label_refs, 0); + VARR_TRUNC (uint64_t, abs_address_locs, 0); + for (insn = DLIST_HEAD (MIR_insn_t, curr_func_item->u.func->insns); insn != NULL; + insn = DLIST_NEXT (MIR_insn_t, insn)) { + if (insn->code == MIR_LABEL) { + set_label_disp (insn, VARR_LENGTH (uint8_t, result_code)); + } else { + replacement = find_insn_pattern_replacement (ctx, insn); + if (replacement == NULL) { + fprintf (stderr, "fatal failure in matching insn:"); + MIR_output_insn (ctx, stderr, insn, curr_func_item->u.func, TRUE); + exit (1); + } else { + gen_assert (replacement != NULL); + out_insn (ctx, insn, replacement); + } + } + } + /* Setting up labels */ + for (i = 0; i < VARR_LENGTH (label_ref_t, label_refs); i++) { + label_ref_t lr = VARR_GET (label_ref_t, label_refs, i); + + if (!lr.abs_addr_p) { + set_int64 (&VARR_ADDR (uint8_t, result_code)[lr.label_val_disp], + (int64_t) get_label_disp (lr.label) - (int64_t) lr.next_insn_disp, 4); + } else { + set_int64 (&VARR_ADDR (uint8_t, result_code)[lr.label_val_disp], + (int64_t) get_label_disp (lr.label), 8); + VARR_PUSH (uint64_t, abs_address_locs, lr.label_val_disp); + } + } + while (VARR_LENGTH (uint8_t, result_code) % 16 != 0) /* Align the pool */ + VARR_PUSH (uint8_t, result_code, 0); + for (i = 0; i < VARR_LENGTH (const_ref_t, const_refs); i++) { /* Add pool constants */ + const_ref_t cr = VARR_GET (const_ref_t, const_refs, i); + + set_int64 (VARR_ADDR (uint8_t, result_code) + cr.pc, + VARR_LENGTH (uint8_t, result_code) - cr.next_insn_disp, 4); + put_uint64 (gen_ctx, VARR_GET (uint64_t, const_pool, cr.const_num), 8); + put_uint64 (gen_ctx, 0, 8); /* keep 16 bytes align */ + } + *len = VARR_LENGTH (uint8_t, result_code); + return VARR_ADDR (uint8_t, result_code); +} + +static void target_rebase (MIR_context_t ctx, uint8_t *base) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + MIR_code_reloc_t reloc; + + VARR_TRUNC (MIR_code_reloc_t, relocs, 0); + for (size_t i = 0; i < VARR_LENGTH (uint64_t, abs_address_locs); i++) { + reloc.offset = VARR_GET (uint64_t, abs_address_locs, i); + reloc.value = base + get_int64 (base + reloc.offset, 8); + VARR_PUSH (MIR_code_reloc_t, relocs, reloc); + } + _MIR_update_code_arr (ctx, base, VARR_LENGTH (MIR_code_reloc_t, relocs), + VARR_ADDR (MIR_code_reloc_t, relocs)); +} + +static void target_init (MIR_context_t ctx) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + + gen_ctx->target_ctx = gen_malloc (ctx, sizeof (struct target_ctx)); + VARR_CREATE (uint8_t, result_code, 0); + VARR_CREATE (uint64_t, const_pool, 0); + VARR_CREATE (const_ref_t, const_refs, 0); + VARR_CREATE (label_ref_t, label_refs, 0); + VARR_CREATE (uint64_t, abs_address_locs, 0); + VARR_CREATE (MIR_code_reloc_t, relocs, 0); + patterns_init (ctx); +} + +static void target_finish (MIR_context_t ctx) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + + patterns_finish (ctx); + VARR_DESTROY (uint8_t, result_code); + VARR_DESTROY (uint64_t, const_pool); + VARR_DESTROY (const_ref_t, const_refs); + VARR_DESTROY (label_ref_t, label_refs); + VARR_DESTROY (uint64_t, abs_address_locs); + VARR_DESTROY (MIR_code_reloc_t, relocs); + free (gen_ctx->target_ctx); + gen_ctx->target_ctx = NULL; +} diff --git a/mir/mir-gen.c b/mir/mir-gen.c new file mode 100644 index 0000000..e7cc4e6 --- /dev/null +++ b/mir/mir-gen.c @@ -0,0 +1,4680 @@ +/* This file is a part of MIR project. + Copyright (C) 2018, 2019 Vladimir Makarov . +*/ + +/* Optimization pipeline: + + ---------------- ----------- ------------------ ------------- + MIR --->| Simplify |--->| Build CFG |--->| Common Sub-Expr |--->| Dead Code | + ---------------- ----------- | Elimination | | Elimination | + ------------------ ------------- + | + v + ------------- ------------ --------- ----------- ---------------------- + | Build Live |<---| Build Live |<---| Finding |<---| Machinize |<--| Sparse Conditional | + | Ranges | | Info | | Loops | ----------- | Constant Propagation | + ------------- ------------ --------- ---------------------- + | + v + --------- --------- --------- ------------- --------------- + | Assign |-->| Rewrite |-->| Combine |-->| Dead Code |--->| Generate |---> Machine + --------- --------- --------- | Elimination | | machine insns | Insns + ------------- --------------- + + + + Simplify: Lowering MIR (in mir.c). + Build CGF: Builing Control Flow Graph (basic blocks and CFG edges). + Common Sub-Expression Elimination: Reusing calculated values + Dead code elimination: Removing insns with unused outputs. + Sparse Conditional Constant Propagation: constant propagation and removing death paths of CFG + Machinize: Machine-dependent code (e.g. in mir-gen-x86_64.c) + transforming MIR for calls ABI, 2-op insns, etc. + Finding Loops: Building loop tree which is used in subsequent register allocation. + Building Live Info: Calculating live in and live out for the basic blocks. + Build Live Ranges: Calculating program point ranges for registers. + Assign: Priority-based assigning hard regs and stack slots to registers. + Rewrite: Transform MIR according to the assign using reserved hard regs. + Combine (code selection): Merging data-depended insns into one. + Dead code elimination: Removing insns with unused outputs. + Generate machine insns: Machine-dependent code (e.g. in + mir-gen-x86_64.c) creating machine insns. + + Terminology: + reg - MIR (pseudo-)register (their numbers are in MIR_OP_REG and MIR_OP_MEM) + hard reg - MIR hard register (their numbers are in MIR_OP_HARD_REG and MIR_OP_HARD_REG_MEM) + breg (based reg) - function pseudo registers whose numbers start with zero + var - pseudo and hard register (var numbers for psedo-registers + are based reg numbers + MAX_HARD_REG + 1) + loc - hard register and stack locations (stack slot numbers start with MAX_HARD_REG + 1). + + We don't use SSA because the optimization pipeline could use SSA is + short (2 passes) and going into / out of SSA is expensive. +*/ + +#include +#include +#include + +#include + +#ifdef NDEBUG +static inline int gen_assert (int cond) { return 0 && cond; } +#else +#define gen_assert(cond) assert (cond) +#endif + +struct MIR_context; +static void util_error (struct MIR_context *ctx, const char *message); +static void varr_error (const char *message) { util_error (NULL, message); } +#define MIR_VARR_ERROR varr_error + +#include "mir.h" +#include "mir-dlist.h" +#include "mir-bitmap.h" +#include "mir-htab.h" +#include "mir-hash.h" +#include "mir-gen.h" + +static void MIR_NO_RETURN util_error (MIR_context_t ctx, const char *message) { + (*MIR_get_error_func (ctx)) (MIR_alloc_error, message); +} + +static void *gen_malloc (MIR_context_t ctx, size_t size) { + void *res = malloc (size); + if (res == NULL) util_error (ctx, "no memory"); + return res; +} + +static MIR_reg_t gen_new_temp_reg (MIR_context_t ctx, MIR_type_t type, MIR_func_t func); +static void set_label_disp (MIR_insn_t insn, size_t disp); +static size_t get_label_disp (MIR_insn_t insn); +static void create_new_bb_insns (MIR_context_t ctx, MIR_insn_t before, MIR_insn_t after, + MIR_insn_t insn_for_bb); +static void gen_delete_insn (MIR_context_t ctx, MIR_insn_t insn); +static void gen_add_insn_before (MIR_context_t ctx, MIR_insn_t insn, MIR_insn_t before); +static void gen_add_insn_after (MIR_context_t ctx, MIR_insn_t insn, MIR_insn_t after); +static void setup_call_hard_reg_args (MIR_insn_t call_insn, MIR_reg_t hard_reg); + +#ifndef MIR_GEN_DEBUG +#define MIR_GEN_DEBUG 0 +#endif + +#ifndef MIR_GEN_CALL_TRACE +#define MIR_GEN_CALL_TRACE 0 +#endif + +typedef struct func_cfg *func_cfg_t; + +struct target_ctx; +struct data_flow_ctx; +struct cse_ctx; +struct ccp_ctx; +struct lr_ctx; +struct ra_ctx; +struct selection_ctx; + +typedef struct loop_node *loop_node_t; +DEF_VARR (loop_node_t); + +struct gen_ctx { + MIR_item_t curr_func_item; +#if MIR_GEN_DEBUG + FILE *debug_file; +#endif + bitmap_t insn_to_consider, temp_bitmap, temp_bitmap2, all_vars; + bitmap_t call_used_hard_regs; + func_cfg_t curr_cfg; + size_t curr_bb_index, curr_loop_node_index; + struct target_ctx *target_ctx; + struct data_flow_ctx *data_flow_ctx; + struct cse_ctx *cse_ctx; + struct ccp_ctx *ccp_ctx; + struct lr_ctx *lr_ctx; + struct ra_ctx *ra_ctx; + struct selection_ctx *selection_ctx; + VARR (loop_node_t) * loop_nodes, *queue_nodes, *loop_entries; /* used in building loop tree */ +}; + +static inline struct gen_ctx **gen_ctx_loc (MIR_context_t ctx) { return (struct gen_ctx **) ctx; } + +#define curr_func_item gen_ctx->curr_func_item +#define debug_file gen_ctx->debug_file +#define insn_to_consider gen_ctx->insn_to_consider +#define temp_bitmap gen_ctx->temp_bitmap +#define temp_bitmap2 gen_ctx->temp_bitmap2 +#define all_vars gen_ctx->all_vars +#define call_used_hard_regs gen_ctx->call_used_hard_regs +#define curr_cfg gen_ctx->curr_cfg +#define curr_bb_index gen_ctx->curr_bb_index +#define curr_loop_node_index gen_ctx->curr_loop_node_index +#define loop_nodes gen_ctx->loop_nodes +#define queue_nodes gen_ctx->queue_nodes +#define loop_entries gen_ctx->loop_entries + +#ifdef __x86_64__ +#include "mir-gen-x86_64.c" +#else +#error "undefined or unsupported generation target" +#endif + +#define DEFAULT_INIT_BITMAP_BITS_NUM 256 + +static void make_io_dup_op_insns (MIR_context_t ctx) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + MIR_func_t func; + MIR_insn_t insn, next_insn; + MIR_insn_code_t code; + MIR_op_t input, output, temp_op; + MIR_op_mode_t mode; + MIR_type_t type; + size_t i; + int out_p; + + gen_assert (curr_func_item->item_type == MIR_func_item); + func = curr_func_item->u.func; + for (i = 0; i < sizeof (io_dup_op_insn_codes) / sizeof (MIR_insn_code_t); i++) + bitmap_set_bit_p (insn_to_consider, io_dup_op_insn_codes[i]); + for (insn = DLIST_HEAD (MIR_insn_t, func->insns); insn != NULL; insn = next_insn) { + next_insn = DLIST_NEXT (MIR_insn_t, insn); + code = insn->code; + if (!bitmap_bit_p (insn_to_consider, code)) continue; + gen_assert (MIR_insn_nops (ctx, insn) >= 2 && !MIR_call_code_p (code) && code != MIR_RET); + mode = MIR_insn_op_mode (ctx, insn, 0, &out_p); + gen_assert (out_p && mode == MIR_insn_op_mode (ctx, insn, 1, &out_p) && !out_p); + output = insn->ops[0]; + input = insn->ops[1]; + gen_assert (input.mode == MIR_OP_REG || input.mode == MIR_OP_HARD_REG + || output.mode == MIR_OP_REG || output.mode == MIR_OP_HARD_REG); + if (input.mode == output.mode + && ((input.mode == MIR_OP_HARD_REG && input.u.hard_reg == output.u.hard_reg) + || (input.mode == MIR_OP_REG && input.u.reg == output.u.reg))) + continue; + if (mode == MIR_OP_FLOAT) { + code = MIR_FMOV; + type = MIR_T_F; + } else if (mode == MIR_OP_DOUBLE) { + code = MIR_DMOV; + type = MIR_T_D; + } else if (mode == MIR_OP_LDOUBLE) { + code = MIR_LDMOV; + type = MIR_T_LD; + } else { + code = MIR_MOV; + type = MIR_T_I64; + } + temp_op = MIR_new_reg_op (ctx, gen_new_temp_reg (ctx, type, func)); + gen_add_insn_before (ctx, insn, MIR_new_insn (ctx, code, temp_op, insn->ops[1])); + gen_add_insn_after (ctx, insn, MIR_new_insn (ctx, code, insn->ops[0], temp_op)); + insn->ops[0] = insn->ops[1] = temp_op; + } +} + +typedef struct dead_var *dead_var_t; + +DEF_DLIST_LINK (dead_var_t); + +typedef struct bb *bb_t; + +DEF_DLIST_LINK (bb_t); + +typedef struct bb_insn *bb_insn_t; + +DEF_DLIST_LINK (bb_insn_t); + +typedef struct edge *edge_t; + +typedef edge_t in_edge_t; + +typedef edge_t out_edge_t; + +DEF_DLIST_LINK (in_edge_t); +DEF_DLIST_LINK (out_edge_t); + +struct edge { + bb_t src, dst; + DLIST_LINK (in_edge_t) in_link; + DLIST_LINK (out_edge_t) out_link; + unsigned char back_edge_p; + unsigned char skipped_p; /* used for CCP */ +}; + +DEF_DLIST (in_edge_t, in_link); +DEF_DLIST (out_edge_t, out_link); + +struct dead_var { + MIR_reg_t var; + DLIST_LINK (dead_var_t) dead_var_link; +}; + +DEF_DLIST (dead_var_t, dead_var_link); + +struct bb_insn { + MIR_insn_t insn; + unsigned int flag; /* used for CPP */ + DLIST_LINK (bb_insn_t) bb_insn_link; + bb_t bb; + DLIST (dead_var_t) dead_vars; + bitmap_t call_hard_reg_args; /* non-null for calls */ + size_t label_disp; /* for label */ +}; + +DEF_DLIST (bb_insn_t, bb_insn_link); + +struct bb { + size_t index, pre, rpost; + unsigned int flag : 1; /* used for CPP */ + DLIST_LINK (bb_t) bb_link; + DLIST (in_edge_t) in_edges; + /* The out edges order: optional fall through bb, optional label bb, + optional exit bb. There is always at least one edge. */ + DLIST (out_edge_t) out_edges; + DLIST (bb_insn_t) bb_insns; + size_t freq; + bitmap_t in, out, gen, kill; /* var bitmaps for different data flow problems */ + loop_node_t loop_node; +}; + +DEF_DLIST (bb_t, bb_link); + +DEF_DLIST_LINK (loop_node_t); +DEF_DLIST_TYPE (loop_node_t); + +struct loop_node { + size_t index; + bb_t bb; /* NULL for internal tree node */ + loop_node_t entry; + loop_node_t parent; + DLIST (loop_node_t) children; + DLIST_LINK (loop_node_t) children_link; +}; + +DEF_DLIST_CODE (loop_node_t, children_link); + +DEF_DLIST_LINK (func_cfg_t); + +typedef struct mv *mv_t; +typedef mv_t dst_mv_t; +typedef mv_t src_mv_t; + +DEF_DLIST_LINK (mv_t); +DEF_DLIST_LINK (dst_mv_t); +DEF_DLIST_LINK (src_mv_t); + +struct mv { + bb_insn_t bb_insn; + size_t freq; + DLIST_LINK (mv_t) mv_link; + DLIST_LINK (dst_mv_t) dst_link; + DLIST_LINK (src_mv_t) src_link; +}; + +DEF_DLIST (mv_t, mv_link); +DEF_DLIST (dst_mv_t, dst_link); +DEF_DLIST (src_mv_t, src_link); + +struct reg_info { + long freq; + size_t calls_num; + /* The followd members are defined and used only in RA */ + long thread_freq; /* thread accumulated freq, defined for first thread breg */ + /* first/next breg of the same thread, MIR_MAX_REG_NUM is end mark */ + MIR_reg_t thread_first, thread_next; + size_t live_length; /* # of program points where breg lives */ + DLIST (dst_mv_t) dst_moves; + DLIST (src_mv_t) src_moves; +}; + +typedef struct reg_info reg_info_t; + +DEF_VARR (reg_info_t); + +typedef struct { + int uns_p; + union { + int64_t i; + uint64_t u; + } u; +} const_t; + +#if MIR_GEN_DEBUG +static void print_const (FILE *f, const_t c) { + if (c.uns_p) + fprintf (f, "%" PRIu64, c.u.u); + else + fprintf (f, "%" PRId64, c.u.i); +} +#endif + +struct func_cfg { + MIR_reg_t min_reg, max_reg; + size_t non_conflicting_moves; /* # of moves with non-conflicting regs */ + VARR (reg_info_t) * breg_info; /* bregs */ + DLIST (bb_t) bbs; + DLIST (mv_t) used_moves, free_moves; + loop_node_t root_loop_node; +}; + +static DLIST (dead_var_t) free_dead_vars; + +static void init_dead_vars (void) { DLIST_INIT (dead_var_t, free_dead_vars); } + +static void free_dead_var (dead_var_t dv) { DLIST_APPEND (dead_var_t, free_dead_vars, dv); } + +static dead_var_t get_dead_var (MIR_context_t ctx) { + dead_var_t dv; + + if ((dv = DLIST_HEAD (dead_var_t, free_dead_vars)) == NULL) + return gen_malloc (ctx, sizeof (struct dead_var)); + DLIST_REMOVE (dead_var_t, free_dead_vars, dv); + return dv; +} + +static void finish_dead_vars (void) { + dead_var_t dv; + + while ((dv = DLIST_HEAD (dead_var_t, free_dead_vars)) != NULL) { + DLIST_REMOVE (dead_var_t, free_dead_vars, dv); + free (dv); + } +} + +static void add_bb_insn_dead_var (MIR_context_t ctx, bb_insn_t bb_insn, MIR_reg_t var) { + dead_var_t dv; + + for (dv = DLIST_HEAD (dead_var_t, bb_insn->dead_vars); dv != NULL; + dv = DLIST_NEXT (dead_var_t, dv)) + if (dv->var == var) return; + dv = get_dead_var (ctx); + dv->var = var; + DLIST_APPEND (dead_var_t, bb_insn->dead_vars, dv); +} + +static dead_var_t find_bb_insn_dead_var (bb_insn_t bb_insn, MIR_reg_t var) { + dead_var_t dv; + + for (dv = DLIST_HEAD (dead_var_t, bb_insn->dead_vars); dv != NULL; + dv = DLIST_NEXT (dead_var_t, dv)) + if (dv->var == var) return dv; + return NULL; +} + +static void clear_bb_insn_dead_vars (bb_insn_t bb_insn) { + dead_var_t dv; + + while ((dv = DLIST_HEAD (dead_var_t, bb_insn->dead_vars)) != NULL) { + DLIST_REMOVE (dead_var_t, bb_insn->dead_vars, dv); + free_dead_var (dv); + } +} + +static void remove_bb_insn_dead_var (bb_insn_t bb_insn, MIR_reg_t hr) { + dead_var_t dv, next_dv; + + gen_assert (hr <= MAX_HARD_REG); + for (dv = DLIST_HEAD (dead_var_t, bb_insn->dead_vars); dv != NULL; dv = next_dv) { + next_dv = DLIST_NEXT (dead_var_t, dv); + if (dv->var != hr) continue; + DLIST_REMOVE (dead_var_t, bb_insn->dead_vars, dv); + free_dead_var (dv); + } +} + +static void move_bb_insn_dead_vars (bb_insn_t bb_insn, bb_insn_t from_bb_insn) { + dead_var_t dv; + + while ((dv = DLIST_HEAD (dead_var_t, from_bb_insn->dead_vars)) != NULL) { + DLIST_REMOVE (dead_var_t, from_bb_insn->dead_vars, dv); + DLIST_APPEND (dead_var_t, bb_insn->dead_vars, dv); + } +} + +static bb_insn_t create_bb_insn (MIR_context_t ctx, MIR_insn_t insn, bb_t bb) { + bb_insn_t bb_insn = gen_malloc (ctx, sizeof (struct bb_insn)); + + insn->data = bb_insn; + bb_insn->bb = bb; + bb_insn->insn = insn; + bb_insn->flag = FALSE; + bb_insn->call_hard_reg_args = NULL; + DLIST_INIT (dead_var_t, bb_insn->dead_vars); + if (MIR_call_code_p (insn->code)) bb_insn->call_hard_reg_args = bitmap_create2 (MAX_HARD_REG + 1); + return bb_insn; +} + +static bb_insn_t add_new_bb_insn (MIR_context_t ctx, MIR_insn_t insn, bb_t bb) { + bb_insn_t bb_insn = create_bb_insn (ctx, insn, bb); + + DLIST_APPEND (bb_insn_t, bb->bb_insns, bb_insn); + return bb_insn; +} + +static void delete_bb_insn (bb_insn_t bb_insn) { + DLIST_REMOVE (bb_insn_t, bb_insn->bb->bb_insns, bb_insn); + bb_insn->insn->data = NULL; + clear_bb_insn_dead_vars (bb_insn); + if (bb_insn->call_hard_reg_args != NULL) bitmap_destroy (bb_insn->call_hard_reg_args); + free (bb_insn); +} + +static void create_new_bb_insns (MIR_context_t ctx, MIR_insn_t before, MIR_insn_t after, + MIR_insn_t insn_for_bb) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + MIR_insn_t insn; + bb_insn_t bb_insn, new_bb_insn; + bb_t bb; + + if (insn_for_bb == NULL) /* It should be in the 1st block */ + bb = DLIST_EL (bb_t, curr_cfg->bbs, 2); /* Skip entry and exit blocks */ + else + bb = ((bb_insn_t) insn_for_bb->data)->bb; + if (before != NULL && (bb_insn = before->data)->bb == bb) { + for (insn = DLIST_NEXT (MIR_insn_t, before); insn != after; + insn = DLIST_NEXT (MIR_insn_t, insn), bb_insn = new_bb_insn) { + new_bb_insn = create_bb_insn (ctx, insn, bb); + DLIST_INSERT_AFTER (bb_insn_t, bb->bb_insns, bb_insn, new_bb_insn); + } + } else { + bb_insn = after->data; + gen_assert (after != NULL); + insn = (before == NULL ? DLIST_HEAD (MIR_insn_t, curr_func_item->u.func->insns) + : DLIST_NEXT (MIR_insn_t, before)); + for (; insn != after; insn = DLIST_NEXT (MIR_insn_t, insn)) { + new_bb_insn = create_bb_insn (ctx, insn, bb); + if (bb == bb_insn->bb) + DLIST_INSERT_BEFORE (bb_insn_t, bb->bb_insns, bb_insn, new_bb_insn); + else + DLIST_APPEND (bb_insn_t, bb->bb_insns, new_bb_insn); + } + } +} + +static void gen_delete_insn (MIR_context_t ctx, MIR_insn_t insn) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + + delete_bb_insn (insn->data); + MIR_remove_insn (ctx, curr_func_item, insn); +} + +static void gen_add_insn_before (MIR_context_t ctx, MIR_insn_t before, MIR_insn_t insn) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + + MIR_insert_insn_before (ctx, curr_func_item, before, insn); + create_new_bb_insns (ctx, DLIST_PREV (MIR_insn_t, insn), before, before); +} + +static void gen_add_insn_after (MIR_context_t ctx, MIR_insn_t after, MIR_insn_t insn) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + + MIR_insert_insn_after (ctx, curr_func_item, after, insn); + create_new_bb_insns (ctx, after, DLIST_NEXT (MIR_insn_t, insn), after); +} + +static void setup_call_hard_reg_args (MIR_insn_t call_insn, MIR_reg_t hard_reg) { + bb_insn_t bb_insn = call_insn->data; + + gen_assert (MIR_call_code_p (call_insn->code) && hard_reg <= MAX_HARD_REG); + bitmap_set_bit_p (bb_insn->call_hard_reg_args, hard_reg); +} + +static void set_label_disp (MIR_insn_t insn, size_t disp) { + gen_assert (insn->code == MIR_LABEL); + ((bb_insn_t) insn->data)->label_disp = disp; +} +static size_t get_label_disp (MIR_insn_t insn) { + gen_assert (insn->code == MIR_LABEL); + return ((bb_insn_t) insn->data)->label_disp; +} + +static bb_t create_bb (MIR_context_t ctx, MIR_insn_t insn) { + bb_t bb = gen_malloc (ctx, sizeof (struct bb)); + + bb->pre = bb->rpost = 0; + bb->flag = FALSE; + DLIST_INIT (bb_insn_t, bb->bb_insns); + DLIST_INIT (in_edge_t, bb->in_edges); + DLIST_INIT (out_edge_t, bb->out_edges); + bb->in = bitmap_create2 (DEFAULT_INIT_BITMAP_BITS_NUM); + bb->out = bitmap_create2 (DEFAULT_INIT_BITMAP_BITS_NUM); + bb->gen = bitmap_create2 (DEFAULT_INIT_BITMAP_BITS_NUM); + bb->kill = bitmap_create2 (DEFAULT_INIT_BITMAP_BITS_NUM); + if (insn != NULL) add_new_bb_insn (ctx, insn, bb); + return bb; +} + +static void add_bb (MIR_context_t ctx, bb_t bb) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + + DLIST_APPEND (bb_t, curr_cfg->bbs, bb); + bb->index = curr_bb_index++; +} + +static edge_t create_edge (MIR_context_t ctx, bb_t src, bb_t dst) { + edge_t e = gen_malloc (ctx, sizeof (struct edge)); + + e->src = src; + e->dst = dst; + DLIST_APPEND (in_edge_t, dst->in_edges, e); + DLIST_APPEND (out_edge_t, src->out_edges, e); + e->back_edge_p = e->skipped_p = FALSE; + return e; +} + +static void delete_edge (edge_t e) { + DLIST_REMOVE (out_edge_t, e->src->out_edges, e); + DLIST_REMOVE (in_edge_t, e->dst->in_edges, e); + free (e); +} + +static void delete_bb (MIR_context_t ctx, bb_t bb) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + edge_t e, next_e; + + for (e = DLIST_HEAD (out_edge_t, bb->out_edges); e != NULL; e = next_e) { + next_e = DLIST_NEXT (out_edge_t, e); + delete_edge (e); + } + for (e = DLIST_HEAD (in_edge_t, bb->in_edges); e != NULL; e = next_e) { + next_e = DLIST_NEXT (in_edge_t, e); + delete_edge (e); + } + DLIST_REMOVE (bb_t, curr_cfg->bbs, bb); + free (bb); +} + +static void DFS (bb_t bb, size_t *pre, size_t *rpost) { + edge_t e; + + bb->pre = (*pre)++; + for (e = DLIST_HEAD (out_edge_t, bb->out_edges); e != NULL; e = DLIST_NEXT (out_edge_t, e)) + if (e->dst->pre == 0) + DFS (e->dst, pre, rpost); + else if (e->dst->rpost == 0) + e->back_edge_p = TRUE; + bb->rpost = (*rpost)--; +} + +static void enumerate_bbs (MIR_context_t ctx) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + size_t pre, rpost; + + pre = 1; + rpost = DLIST_LENGTH (bb_t, curr_cfg->bbs); + DFS (DLIST_HEAD (bb_t, curr_cfg->bbs), &pre, &rpost); +} + +static loop_node_t top_loop_node (bb_t bb) { + for (loop_node_t loop_node = bb->loop_node;; loop_node = loop_node->parent) + if (loop_node->parent == NULL) return loop_node; +} + +static loop_node_t create_loop_node (MIR_context_t ctx, bb_t bb) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + loop_node_t loop_node = gen_malloc (ctx, sizeof (struct loop_node)); + + loop_node->index = curr_loop_node_index++; + loop_node->bb = bb; + if (bb != NULL) bb->loop_node = loop_node; + loop_node->parent = NULL; + loop_node->entry = NULL; + DLIST_INIT (loop_node_t, loop_node->children); + return loop_node; +} + +static void process_loop (MIR_context_t ctx, bb_t entry_bb) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + edge_t e; + loop_node_t loop_node, new_loop_node, queue_node; + bb_t loop_bb, queue_bb; + + VARR_TRUNC (loop_node_t, loop_nodes, 0); + VARR_TRUNC (loop_node_t, queue_nodes, 0); + bitmap_clear (temp_bitmap); + for (e = DLIST_HEAD (in_edge_t, entry_bb->in_edges); e != NULL; e = DLIST_NEXT (in_edge_t, e)) + if (e->back_edge_p && e->src != entry_bb) { + loop_node = top_loop_node (e->src); + if (!bitmap_set_bit_p (temp_bitmap, loop_node->index)) continue; + VARR_PUSH (loop_node_t, loop_nodes, loop_node); + VARR_PUSH (loop_node_t, queue_nodes, loop_node); + } + while (VARR_LENGTH (loop_node_t, queue_nodes) != 0) { + queue_node = VARR_POP (loop_node_t, queue_nodes); + if ((queue_bb = queue_node->bb) == NULL) queue_bb = queue_node->entry->bb; /* subloop */ + /* entry block is achieved which means multiple entry loop -- just ignore */ + if (queue_bb == DLIST_HEAD (bb_t, curr_cfg->bbs)) return; + for (e = DLIST_HEAD (in_edge_t, queue_bb->in_edges); e != NULL; e = DLIST_NEXT (in_edge_t, e)) + if (e->src != entry_bb) { + loop_node = top_loop_node (e->src); + if (!bitmap_set_bit_p (temp_bitmap, loop_node->index)) continue; + VARR_PUSH (loop_node_t, loop_nodes, loop_node); + VARR_PUSH (loop_node_t, queue_nodes, loop_node); + } + } + loop_node = entry_bb->loop_node; + VARR_PUSH (loop_node_t, loop_nodes, loop_node); + new_loop_node = create_loop_node (ctx, NULL); + new_loop_node->entry = loop_node; + while (VARR_LENGTH (loop_node_t, loop_nodes) != 0) { + loop_node = VARR_POP (loop_node_t, loop_nodes); + DLIST_APPEND (loop_node_t, new_loop_node->children, loop_node); + loop_node->parent = new_loop_node; + } +} + +static int compare_bb_loop_nodes (const void *p1, const void *p2) { + bb_t bb1 = (*(const loop_node_t *) p1)->bb, bb2 = (*(const loop_node_t *) p2)->bb; + + return bb1->rpost > bb2->rpost ? -1 : bb1->rpost < bb2->rpost ? 1 : 0; +} + +static void build_loop_tree (MIR_context_t ctx) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + loop_node_t loop_node; + edge_t e; + + curr_loop_node_index = 0; + enumerate_bbs (ctx); + VARR_TRUNC (loop_node_t, loop_entries, 0); + for (bb_t bb = DLIST_HEAD (bb_t, curr_cfg->bbs); bb != NULL; bb = DLIST_NEXT (bb_t, bb)) { + loop_node = create_loop_node (ctx, bb); + loop_node->entry = loop_node; + for (e = DLIST_HEAD (in_edge_t, bb->in_edges); e != NULL; e = DLIST_NEXT (in_edge_t, e)) + if (e->back_edge_p) { + VARR_PUSH (loop_node_t, loop_entries, loop_node); + break; + } + } + qsort (VARR_ADDR (loop_node_t, loop_entries), VARR_LENGTH (loop_node_t, loop_entries), + sizeof (loop_node_t), compare_bb_loop_nodes); + for (size_t i = 0; i < VARR_LENGTH (loop_node_t, loop_entries); i++) + process_loop (ctx, VARR_GET (loop_node_t, loop_entries, i)->bb); + curr_cfg->root_loop_node = create_loop_node (ctx, NULL); + for (bb_t bb = DLIST_HEAD (bb_t, curr_cfg->bbs); bb != NULL; bb = DLIST_NEXT (bb_t, bb)) + if ((loop_node = top_loop_node (bb)) != curr_cfg->root_loop_node) { + DLIST_APPEND (loop_node_t, curr_cfg->root_loop_node->children, loop_node); + loop_node->parent = curr_cfg->root_loop_node; + } +} + +static void update_min_max_reg (MIR_context_t ctx, MIR_reg_t reg) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + + if (reg == 0) return; + if (curr_cfg->max_reg == 0 || curr_cfg->min_reg > reg) curr_cfg->min_reg = reg; + if (curr_cfg->max_reg < reg) curr_cfg->max_reg = reg; +} + +static MIR_reg_t gen_new_temp_reg (MIR_context_t ctx, MIR_type_t type, MIR_func_t func) { + MIR_reg_t reg = _MIR_new_temp_reg (ctx, type, func); + + update_min_max_reg (ctx, reg); + return reg; +} + +static MIR_reg_t reg2breg (struct gen_ctx *gen_ctx, MIR_reg_t reg) { + return reg - curr_cfg->min_reg; +} +static MIR_reg_t breg2reg (struct gen_ctx *gen_ctx, MIR_reg_t breg) { + return breg + curr_cfg->min_reg; +} +static MIR_reg_t reg2var (struct gen_ctx *gen_ctx, MIR_reg_t reg) { + return reg2breg (gen_ctx, reg) + MAX_HARD_REG + 1; +} +static MIR_reg_t var_is_reg_p (MIR_reg_t var) { return var > MAX_HARD_REG; } +static MIR_reg_t var2reg (struct gen_ctx *gen_ctx, MIR_reg_t var) { + gen_assert (var > MAX_HARD_REG); + return breg2reg (gen_ctx, var - MAX_HARD_REG - 1); +} + +static MIR_reg_t get_nregs (MIR_context_t ctx) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + + return curr_cfg->max_reg == 0 ? 0 : curr_cfg->max_reg - curr_cfg->min_reg + 1; +} + +static MIR_reg_t get_nvars (MIR_context_t ctx) { return get_nregs (ctx) + MAX_HARD_REG + 1; } + +static int move_p (MIR_insn_t insn) { + return ((insn->code == MIR_MOV || insn->code == MIR_FMOV || insn->code == MIR_DMOV + || insn->code == MIR_LDMOV) + && (insn->ops[0].mode == MIR_OP_REG || insn->ops[0].mode == MIR_OP_HARD_REG) + && (insn->ops[1].mode == MIR_OP_REG || insn->ops[1].mode == MIR_OP_HARD_REG)); +} + +static int imm_move_p (MIR_insn_t insn) { + return ((insn->code == MIR_MOV || insn->code == MIR_FMOV || insn->code == MIR_DMOV + || insn->code == MIR_LDMOV) + && (insn->ops[0].mode == MIR_OP_REG || insn->ops[0].mode == MIR_OP_HARD_REG) + && (insn->ops[1].mode == MIR_OP_INT || insn->ops[1].mode == MIR_OP_UINT + || insn->ops[1].mode == MIR_OP_FLOAT || insn->ops[1].mode == MIR_OP_DOUBLE + || insn->ops[1].mode == MIR_OP_LDOUBLE || insn->ops[1].mode == MIR_OP_REF)); +} + +#if MIR_GEN_DEBUG +static void output_in_edges (MIR_context_t ctx, bb_t bb) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + edge_t e; + + fprintf (debug_file, " in edges:"); + for (e = DLIST_HEAD (in_edge_t, bb->in_edges); e != NULL; e = DLIST_NEXT (in_edge_t, e)) { + fprintf (debug_file, " %3lu", (unsigned long) e->src->index); + if (e->skipped_p) fprintf (debug_file, "(CCP skip)"); + } + fprintf (debug_file, "\n"); +} + +static void output_out_edges (MIR_context_t ctx, bb_t bb) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + edge_t e; + + fprintf (debug_file, " out edges:"); + for (e = DLIST_HEAD (out_edge_t, bb->out_edges); e != NULL; e = DLIST_NEXT (out_edge_t, e)) { + fprintf (debug_file, " %3lu", (unsigned long) e->dst->index); + if (e->skipped_p) fprintf (debug_file, "(CCP skip)"); + } + fprintf (debug_file, "\n"); +} + +static void output_live_element (size_t nel, void *data) { + MIR_context_t ctx = data; + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + + fprintf (debug_file, "%3lu", (unsigned long) nel); + if (var_is_reg_p (nel)) + fprintf (debug_file, "(%s:%s)", + MIR_type_str (ctx, MIR_reg_type (ctx, var2reg (gen_ctx, nel), curr_func_item->u.func)), + MIR_reg_name (ctx, var2reg (gen_ctx, nel), curr_func_item->u.func)); +} + +static void output_bitmap (MIR_context_t ctx, const char *head, bitmap_t bm) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + + if (bm == NULL || bitmap_empty_p (bm)) return; + fprintf (debug_file, "%s", head); + bitmap_for_each (bm, output_live_element, ctx); + fprintf (debug_file, "\n"); +} + +static void print_bb_insn (MIR_context_t ctx, bb_insn_t bb_insn, int with_notes_p) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + MIR_op_t op; + + MIR_output_insn (ctx, debug_file, bb_insn->insn, curr_func_item->u.func, FALSE); + if (with_notes_p) { + for (dead_var_t dv = DLIST_HEAD (dead_var_t, bb_insn->dead_vars); dv != NULL; + dv = DLIST_NEXT (dead_var_t, dv)) { + if (var_is_reg_p (dv->var)) { + op.mode = MIR_OP_REG; + op.u.reg = var2reg (gen_ctx, dv->var); + } else { + op.mode = MIR_OP_HARD_REG; + op.u.hard_reg = dv->var; + } + fprintf (debug_file, dv == DLIST_HEAD (dead_var_t, bb_insn->dead_vars) ? " # dead: " : " "); + MIR_output_op (ctx, debug_file, op, curr_func_item->u.func); + } + } + fprintf (debug_file, "\n"); +} + +static void print_CFG (MIR_context_t ctx, int bb_p, int insns_p, + void (*bb_info_print_func) (MIR_context_t, bb_t)) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + + for (bb_t bb = DLIST_HEAD (bb_t, curr_cfg->bbs); bb != NULL; bb = DLIST_NEXT (bb_t, bb)) { + if (bb_p) { + fprintf (debug_file, "BB %3lu:\n", (unsigned long) bb->index); + output_in_edges (ctx, bb); + output_out_edges (ctx, bb); + if (bb_info_print_func != NULL) { + bb_info_print_func (ctx, bb); + fprintf (debug_file, "\n"); + } + } + if (insns_p) { + for (bb_insn_t bb_insn = DLIST_HEAD (bb_insn_t, bb->bb_insns); bb_insn != NULL; + bb_insn = DLIST_NEXT (bb_insn_t, bb_insn)) + print_bb_insn (ctx, bb_insn, TRUE); + fprintf (debug_file, "\n"); + } + } +} + +static void print_loop_subtree (MIR_context_t ctx, loop_node_t root, int level) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + + for (int i = 0; i < 2 * level + 2; i++) fprintf (debug_file, " "); + if (root->bb != NULL) { + gen_assert (DLIST_HEAD (loop_node_t, root->children) == NULL); + fprintf (debug_file, "BB%-3lu\n", (unsigned long) root->bb->index); + return; + } + fprintf (debug_file, "Loop%-3lu\n", (unsigned long) root->index); + for (loop_node_t node = DLIST_HEAD (loop_node_t, root->children); node != NULL; + node = DLIST_NEXT (loop_node_t, node)) + print_loop_subtree (ctx, node, level + 1); +} + +static void print_loop_tree (MIR_context_t ctx) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + + fprintf (debug_file, "Loop Tree:\n"); + print_loop_subtree (ctx, curr_cfg->root_loop_node, 0); +} + +#endif + +static mv_t get_free_move (MIR_context_t ctx) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + mv_t mv; + + if ((mv = DLIST_HEAD (mv_t, curr_cfg->free_moves)) != NULL) + DLIST_REMOVE (mv_t, curr_cfg->free_moves, mv); + else + mv = gen_malloc (ctx, sizeof (struct mv)); + DLIST_APPEND (mv_t, curr_cfg->used_moves, mv); + return mv; +} + +static void free_move (MIR_context_t ctx, mv_t mv) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + + DLIST_REMOVE (mv_t, curr_cfg->used_moves, mv); + DLIST_APPEND (mv_t, curr_cfg->free_moves, mv); +} + +static void build_func_cfg (MIR_context_t ctx) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + MIR_insn_t insn, next_insn; + bb_insn_t bb_insn, label_bb_insn; + size_t i, nops; + MIR_op_t *op; + MIR_var_t var; + bb_t bb, prev_bb, entry_bb, exit_bb; + + DLIST_INIT (bb_t, curr_cfg->bbs); + DLIST_INIT (mv_t, curr_cfg->used_moves); + DLIST_INIT (mv_t, curr_cfg->free_moves); + curr_cfg->max_reg = 0; + curr_cfg->min_reg = 0; + curr_cfg->root_loop_node = NULL; + curr_bb_index = 0; + for (i = 0; i < VARR_LENGTH (MIR_var_t, curr_func_item->u.func->vars); i++) { + var = VARR_GET (MIR_var_t, curr_func_item->u.func->vars, i); + update_min_max_reg (ctx, MIR_reg (ctx, var.name, curr_func_item->u.func)); + } + entry_bb = create_bb (ctx, NULL); + add_bb (ctx, entry_bb); + exit_bb = create_bb (ctx, NULL); + add_bb (ctx, exit_bb); + insn = DLIST_HEAD (MIR_insn_t, curr_func_item->u.func->insns); + if (insn != NULL) { + bb = create_bb (ctx, NULL); + add_bb (ctx, bb); + if (insn->code == MIR_LABEL) { /* Create one more BB. First BB will be empty. */ + prev_bb = bb; + bb = create_bb (ctx, NULL); + add_bb (ctx, bb); + create_edge (ctx, prev_bb, bb); + } + } + for (; insn != NULL; insn = next_insn) { + next_insn = DLIST_NEXT (MIR_insn_t, insn); + if (insn->data == NULL) add_new_bb_insn (ctx, insn, bb); + nops = MIR_insn_nops (ctx, insn); + if (next_insn != NULL + && (MIR_branch_code_p (insn->code) || insn->code == MIR_RET || insn->code == MIR_SWITCH + || next_insn->code == MIR_LABEL)) { + prev_bb = bb; + if (next_insn->code == MIR_LABEL && (label_bb_insn = next_insn->data) != NULL) + bb = label_bb_insn->bb; + else + bb = create_bb (ctx, next_insn); + add_bb (ctx, bb); + if (insn->code != MIR_JMP && insn->code != MIR_RET && insn->code != MIR_SWITCH) + create_edge (ctx, prev_bb, bb); + } + for (i = 0; i < nops; i++) + if ((op = &insn->ops[i])->mode == MIR_OP_LABEL) { + if ((label_bb_insn = op->u.label->data) == NULL) { + create_bb (ctx, op->u.label); + label_bb_insn = op->u.label->data; + } + bb_insn = insn->data; + create_edge (ctx, bb_insn->bb, label_bb_insn->bb); + } else if (op->mode == MIR_OP_REG) { + update_min_max_reg (ctx, op->u.reg); + } else if (op->mode == MIR_OP_MEM) { + update_min_max_reg (ctx, op->u.mem.base); + update_min_max_reg (ctx, op->u.mem.index); + } + } + /* Add additional edges with entry and exit */ + for (bb = DLIST_HEAD (bb_t, curr_cfg->bbs); bb != NULL; bb = DLIST_NEXT (bb_t, bb)) { + if (bb != entry_bb && DLIST_HEAD (in_edge_t, bb->in_edges) == NULL) + create_edge (ctx, entry_bb, bb); + if (bb != exit_bb && DLIST_HEAD (out_edge_t, bb->out_edges) == NULL) + create_edge (ctx, bb, exit_bb); + } + enumerate_bbs (ctx); + VARR_CREATE (reg_info_t, curr_cfg->breg_info, 128); +} + +static void destroy_func_cfg (MIR_context_t ctx) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + MIR_insn_t insn; + bb_insn_t bb_insn; + bb_t bb, next_bb; + mv_t mv, next_mv; + + gen_assert (curr_func_item->item_type == MIR_func_item && curr_func_item->data != NULL); + for (insn = DLIST_HEAD (MIR_insn_t, curr_func_item->u.func->insns); insn != NULL; + insn = DLIST_NEXT (MIR_insn_t, insn)) { + bb_insn = insn->data; + gen_assert (bb_insn != NULL); + delete_bb_insn (bb_insn); + } + for (bb = DLIST_HEAD (bb_t, curr_cfg->bbs); bb != NULL; bb = next_bb) { + next_bb = DLIST_NEXT (bb_t, bb); + bitmap_destroy (bb->in); + bitmap_destroy (bb->out); + bitmap_destroy (bb->gen); + bitmap_destroy (bb->kill); + delete_bb (ctx, bb); + } + for (mv = DLIST_HEAD (mv_t, curr_cfg->used_moves); mv != NULL; mv = next_mv) { + next_mv = DLIST_NEXT (mv_t, mv); + free (mv); + } + for (mv = DLIST_HEAD (mv_t, curr_cfg->free_moves); mv != NULL; mv = next_mv) { + next_mv = DLIST_NEXT (mv_t, mv); + free (mv); + } + VARR_DESTROY (reg_info_t, curr_cfg->breg_info); + free (curr_func_item->data); + curr_func_item->data = NULL; +} + +static void add_new_bb_insns (MIR_context_t ctx) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + MIR_func_t func; + size_t i, nops; + MIR_op_t op; + bb_t bb = DLIST_EL (bb_t, curr_cfg->bbs, 2); + bb_insn_t bb_insn, last_bb_insn = NULL; + + gen_assert (curr_func_item->item_type == MIR_func_item); + func = curr_func_item->u.func; + for (MIR_insn_t insn = DLIST_HEAD (MIR_insn_t, func->insns); insn != NULL; + insn = DLIST_NEXT (MIR_insn_t, insn)) + if (insn->data != NULL) { + bb = (last_bb_insn = insn->data)->bb; + if (MIR_branch_code_p (insn->code) || insn->code == MIR_RET || insn->code == MIR_SWITCH) { + bb = DLIST_NEXT (bb_t, bb); + last_bb_insn = NULL; + } + } else { /* New insn: */ + gen_assert (bb != NULL); + bb_insn = create_bb_insn (ctx, insn, bb); + if (last_bb_insn != NULL) { + DLIST_INSERT_AFTER (bb_insn_t, bb->bb_insns, last_bb_insn, bb_insn); + } else { + gen_assert (DLIST_HEAD (bb_insn_t, bb->bb_insns) != NULL + && DLIST_HEAD (bb_insn_t, bb->bb_insns)->insn->code != MIR_LABEL); + DLIST_PREPEND (bb_insn_t, bb->bb_insns, bb_insn); + } + last_bb_insn = bb_insn; + nops = MIR_insn_nops (ctx, insn); + for (i = 0; i < nops; i++) { + op = insn->ops[i]; + if (op.mode == MIR_OP_REG) { + update_min_max_reg (ctx, op.u.reg); + } else if (op.mode == MIR_OP_MEM) { + update_min_max_reg (ctx, op.u.mem.base); + update_min_max_reg (ctx, op.u.mem.index); + } + } + } +} + +static int rpost_cmp (const void *a1, const void *a2) { + return (*(const struct bb **) a1)->rpost - (*(const struct bb **) a2)->rpost; +} + +static int post_cmp (const void *a1, const void *a2) { return -rpost_cmp (a1, a2); } + +DEF_VARR (bb_t); + +struct data_flow_ctx { + VARR (bb_t) * worklist, *pending; + bitmap_t bb_to_consider; +}; + +#define worklist gen_ctx->data_flow_ctx->worklist +#define pending gen_ctx->data_flow_ctx->pending +#define bb_to_consider gen_ctx->data_flow_ctx->bb_to_consider + +static void solve_dataflow (MIR_context_t ctx, int forward_p, void (*con_func_0) (bb_t), + int (*con_func_n) (MIR_context_t, bb_t), int (*trans_func) (bb_t)) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + size_t i, iter; + bb_t bb, *addr; + VARR (bb_t) * t; + + VARR_TRUNC (bb_t, worklist, 0); + for (bb = DLIST_HEAD (bb_t, curr_cfg->bbs); bb != NULL; bb = DLIST_NEXT (bb_t, bb)) + VARR_PUSH (bb_t, worklist, bb); + VARR_TRUNC (bb_t, pending, 0); + iter = 0; + while (VARR_LENGTH (bb_t, worklist) != 0) { + VARR_TRUNC (bb_t, pending, 0); + addr = VARR_ADDR (bb_t, worklist); + qsort (addr, VARR_LENGTH (bb_t, worklist), sizeof (bb), forward_p ? rpost_cmp : post_cmp); + bitmap_clear (bb_to_consider); + for (i = 0; i < VARR_LENGTH (bb_t, worklist); i++) { + int changed_p = iter == 0; + edge_t e; + + bb = addr[i]; + if (forward_p) { + if (DLIST_HEAD (in_edge_t, bb->in_edges) == NULL) + con_func_0 (bb); + else + changed_p |= con_func_n (ctx, bb); + } else { + if (DLIST_HEAD (out_edge_t, bb->out_edges) == NULL) + con_func_0 (bb); + else + changed_p |= con_func_n (ctx, bb); + } + if (changed_p && trans_func (bb)) { + if (forward_p) { + for (e = DLIST_HEAD (out_edge_t, bb->out_edges); e != NULL; + e = DLIST_NEXT (out_edge_t, e)) + if (bitmap_set_bit_p (bb_to_consider, e->dst->index)) VARR_PUSH (bb_t, pending, e->dst); + } else { + for (e = DLIST_HEAD (in_edge_t, bb->in_edges); e != NULL; e = DLIST_NEXT (in_edge_t, e)) + if (bitmap_set_bit_p (bb_to_consider, e->src->index)) VARR_PUSH (bb_t, pending, e->src); + } + } + } + iter++; + t = worklist; + worklist = pending; + pending = t; + } +} + +static void init_data_flow (MIR_context_t ctx) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + + gen_ctx->data_flow_ctx = gen_malloc (ctx, sizeof (struct data_flow_ctx)); + VARR_CREATE (bb_t, worklist, 0); + VARR_CREATE (bb_t, pending, 0); + bb_to_consider = bitmap_create2 (512); +} + +static void finish_data_flow (MIR_context_t ctx) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + + VARR_DESTROY (bb_t, worklist); + VARR_DESTROY (bb_t, pending); + bitmap_destroy (bb_to_consider); + free (gen_ctx->data_flow_ctx); + gen_ctx->data_flow_ctx = NULL; +} + +/* New Page */ + +/* Common Sub-expression Elimination. */ + +#define av_in in +#define av_out out +#define av_kill kill +#define av_gen gen + +typedef struct expr { + MIR_insn_t insn; /* operation and input operands are the expr keys */ + unsigned int num; /* the expression number (0, 1 ...) */ + MIR_context_t ctx; + MIR_reg_t temp_reg; /* ??? */ +} * expr_t; + +DEF_VARR (expr_t); +DEF_HTAB (expr_t); +DEF_VARR (bitmap_t); + +struct cse_ctx { + VARR (expr_t) * exprs; /* the expr number -> expression */ + /* map: var number -> bitmap of numbers of exprs with given var as an input operand. */ + VARR (bitmap_t) * var2dep_expr; + bitmap_t memory_exprs; /* expressions containing memory */ + HTAB (expr_t) * expr_tab; /* keys: insn code and input operands */ + bitmap_t curr_bb_av_gen, curr_bb_av_kill; +}; + +#define exprs gen_ctx->cse_ctx->exprs +#define var2dep_expr gen_ctx->cse_ctx->var2dep_expr +#define memory_exprs gen_ctx->cse_ctx->memory_exprs +#define expr_tab gen_ctx->cse_ctx->expr_tab +#define curr_bb_av_gen gen_ctx->cse_ctx->curr_bb_av_gen +#define curr_bb_av_kill gen_ctx->cse_ctx->curr_bb_av_kill + +static int op_eq (MIR_context_t ctx, MIR_op_t op1, MIR_op_t op2) { + return MIR_op_eq_p (ctx, op1, op2); +} + +static int expr_eq (expr_t e1, expr_t e2) { + size_t i, nops; + int out_p; + + assert (e1->ctx == e2->ctx); + if (e1->insn->code != e2->insn->code) return FALSE; + nops = MIR_insn_nops (e1->ctx, e1->insn); + for (i = 0; i < nops; i++) { + MIR_insn_op_mode (e1->ctx, e1->insn, i, &out_p); + if (out_p) continue; + if (!op_eq (e1->ctx, e1->insn->ops[i], e2->insn->ops[i])) return FALSE; + } + return TRUE; +} + +static htab_hash_t add_op_hash (MIR_context_t ctx, htab_hash_t h, MIR_op_t op) { + return MIR_op_hash_step (ctx, h, op); +} + +static htab_hash_t expr_hash (expr_t e) { + size_t i, nops; + int out_p; + htab_hash_t h = mir_hash_init (0x42); + + h = mir_hash_step (h, (uint64_t) e->insn->code); + nops = MIR_insn_nops (e->ctx, e->insn); + for (i = 0; i < nops; i++) { + MIR_insn_op_mode (e->ctx, e->insn, i, &out_p); + if (out_p) continue; + h = add_op_hash (e->ctx, h, e->insn->ops[i]); + } + return mir_hash_finish (h); +} + +static int find_expr (MIR_context_t ctx, MIR_insn_t insn, expr_t *e) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + struct expr es; + + es.insn = insn; + es.ctx = ctx; + return HTAB_DO (expr_t, expr_tab, &es, HTAB_FIND, *e); +} + +static void insert_expr (MIR_context_t ctx, expr_t e) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + expr_t e2; + + gen_assert (!find_expr (ctx, e->insn, &e2)); + HTAB_DO (expr_t, expr_tab, e, HTAB_INSERT, e); +} + +static void process_var (MIR_context_t ctx, MIR_reg_t var, expr_t e) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + bitmap_t b; + + while (var >= VARR_LENGTH (bitmap_t, var2dep_expr)) VARR_PUSH (bitmap_t, var2dep_expr, NULL); + if ((b = VARR_GET (bitmap_t, var2dep_expr, var)) == NULL) { + b = bitmap_create2 (DEFAULT_INIT_BITMAP_BITS_NUM); + VARR_SET (bitmap_t, var2dep_expr, var, b); + } + bitmap_set_bit_p (b, e->num); +} + +static void process_cse_ops (MIR_context_t ctx, MIR_op_t op, expr_t e) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + + switch (op.mode) { // ??? + case MIR_OP_REG: process_var (ctx, reg2var (gen_ctx, op.u.reg), e); break; + case MIR_OP_HARD_REG: process_var (ctx, op.u.hard_reg, e); break; + case MIR_OP_INT: + case MIR_OP_UINT: + case MIR_OP_FLOAT: + case MIR_OP_DOUBLE: + case MIR_OP_LDOUBLE: + case MIR_OP_REF: break; + case MIR_OP_MEM: + if (op.u.mem.base != 0) process_var (ctx, reg2var (gen_ctx, op.u.mem.base), e); + if (op.u.mem.index != 0) process_var (ctx, reg2var (gen_ctx, op.u.mem.index), e); + bitmap_set_bit_p (memory_exprs, e->num); + break; + case MIR_OP_HARD_REG_MEM: + if (op.u.hard_reg_mem.base != MIR_NON_HARD_REG) process_var (ctx, op.u.hard_reg_mem.base, e); + if (op.u.hard_reg_mem.index != MIR_NON_HARD_REG) process_var (ctx, op.u.hard_reg_mem.index, e); + bitmap_set_bit_p (memory_exprs, e->num); + break; + default: gen_assert (FALSE); /* we should not have all the rest operand here */ + } +} + +static expr_t add_expr (MIR_context_t ctx, MIR_insn_t insn) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + size_t i, nops; + int out_p; + MIR_op_mode_t mode; + expr_t e = gen_malloc (ctx, sizeof (struct expr)); + + gen_assert (!MIR_call_code_p (insn->code) && insn->code != MIR_RET); + e->insn = insn; + e->num = VARR_LENGTH (expr_t, exprs); + e->ctx = ctx; + mode = MIR_insn_op_mode (ctx, insn, 0, &out_p); + e->temp_reg + = gen_new_temp_reg (ctx, + mode == MIR_OP_FLOAT + ? MIR_T_F + : mode == MIR_OP_DOUBLE ? MIR_T_D + : mode == MIR_OP_LDOUBLE ? MIR_T_LD : MIR_T_I64, + curr_func_item->u.func); + VARR_PUSH (expr_t, exprs, e); + insert_expr (ctx, e); + nops = MIR_insn_nops (ctx, insn); + for (i = 0; i < nops; i++) { + MIR_insn_op_mode (ctx, insn, i, &out_p); + if (!out_p) process_cse_ops (ctx, insn->ops[i], e); + } + return e; +} + +static void cse_con_func_0 (bb_t bb) { bitmap_clear (bb->av_in); } + +static int cse_con_func_n (MIR_context_t ctx, bb_t bb) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + edge_t e, head; + bitmap_t prev_av_in = temp_bitmap; + + bitmap_copy (prev_av_in, bb->av_in); + head = DLIST_HEAD (in_edge_t, bb->in_edges); + bitmap_copy (bb->av_in, head->src->av_out); + for (e = DLIST_NEXT (in_edge_t, head); e != NULL; e = DLIST_NEXT (in_edge_t, e)) + bitmap_and (bb->av_in, bb->av_in, e->src->av_out); /* av_in &= av_out */ + return !bitmap_equal_p (bb->av_in, prev_av_in); +} + +static int cse_trans_func (bb_t bb) { + return bitmap_ior_and_compl (bb->av_out, bb->av_gen, bb->av_in, bb->av_kill); +} + +static void create_exprs (MIR_context_t ctx) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + + for (bb_t bb = DLIST_HEAD (bb_t, curr_cfg->bbs); bb != NULL; bb = DLIST_NEXT (bb_t, bb)) + for (bb_insn_t bb_insn = DLIST_HEAD (bb_insn_t, bb->bb_insns); bb_insn != NULL; + bb_insn = DLIST_NEXT (bb_insn_t, bb_insn)) { + expr_t e; + MIR_insn_t insn = bb_insn->insn; + + if (!MIR_branch_code_p (insn->code) && insn->code != MIR_RET && insn->code != MIR_SWITCH + && insn->code != MIR_LABEL && !MIR_call_code_p (insn->code) && insn->code != MIR_ALLOCA + && insn->code != MIR_BSTART && insn->code != MIR_BEND && insn->code != MIR_VA_START + && insn->code != MIR_VA_END && !move_p (insn) + && (!imm_move_p (insn) || insn->ops[1].mode == MIR_OP_REF) + /* After simplification we have only one store form: mem = reg. + It is unprofitable to add the reg as an expression. */ + && insn->ops[0].mode != MIR_OP_MEM && insn->ops[0].mode != MIR_OP_HARD_REG_MEM + && !find_expr (ctx, insn, &e)) + add_expr (ctx, insn); + } +} + +static void make_obsolete_var_exprs (size_t nel, void *data) { + MIR_context_t ctx = data; + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + MIR_reg_t var = nel; + bitmap_t b; + + if (var < VARR_LENGTH (bitmap_t, var2dep_expr) + && (b = VARR_GET (bitmap_t, var2dep_expr, var)) != NULL) { + if (curr_bb_av_gen != NULL) bitmap_and_compl (curr_bb_av_gen, curr_bb_av_gen, b); + if (curr_bb_av_kill != NULL) bitmap_ior (curr_bb_av_kill, curr_bb_av_kill, b); + } +} + +static void create_av_bitmaps (MIR_context_t ctx) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + + for (bb_t bb = DLIST_HEAD (bb_t, curr_cfg->bbs); bb != NULL; bb = DLIST_NEXT (bb_t, bb)) { + bitmap_clear (bb->av_in); + bitmap_clear (bb->av_out); + bitmap_clear (bb->av_kill); + bitmap_clear (bb->av_gen); + curr_bb_av_gen = bb->av_gen; + curr_bb_av_kill = bb->av_kill; + for (bb_insn_t bb_insn = DLIST_HEAD (bb_insn_t, bb->bb_insns); bb_insn != NULL; + bb_insn = DLIST_NEXT (bb_insn_t, bb_insn)) { + size_t i, nops; + int out_p; + MIR_reg_t early_clobbered_hard_reg1, early_clobbered_hard_reg2; + MIR_op_t op; + expr_t e; + MIR_insn_t insn = bb_insn->insn; + + if (MIR_branch_code_p (bb_insn->insn->code) || insn->code == MIR_RET + || insn->code == MIR_SWITCH || insn->code == MIR_LABEL) + continue; + if (!MIR_call_code_p (insn->code) && insn->code != MIR_ALLOCA && insn->code != MIR_BSTART + && insn->code != MIR_BEND && insn->code != MIR_VA_START && insn->code != MIR_VA_END + && !move_p (insn) + && (!imm_move_p (insn) || insn->ops[1].mode == MIR_OP_REF) + /* See create_expr comments: */ + && insn->ops[0].mode != MIR_OP_MEM && insn->ops[0].mode != MIR_OP_HARD_REG_MEM) { + if (!find_expr (ctx, insn, &e)) { + gen_assert (FALSE); + continue; + } + bitmap_set_bit_p (bb->av_gen, e->num); + } + get_early_clobbered_hard_reg (insn, &early_clobbered_hard_reg1, &early_clobbered_hard_reg2); + if (early_clobbered_hard_reg1 != MIR_NON_HARD_REG) + make_obsolete_var_exprs (early_clobbered_hard_reg1, ctx); + if (early_clobbered_hard_reg2 != MIR_NON_HARD_REG) + make_obsolete_var_exprs (early_clobbered_hard_reg2, ctx); + nops = MIR_insn_nops (ctx, insn); + for (i = 0; i < nops; i++) { + MIR_insn_op_mode (ctx, insn, i, &out_p); + op = insn->ops[i]; + if (!out_p) continue; + if (op.mode == MIR_OP_MEM || op.mode == MIR_OP_HARD_REG_MEM) { + bitmap_and_compl (bb->av_gen, bb->av_gen, memory_exprs); + bitmap_ior (bb->av_kill, bb->av_kill, memory_exprs); + } else if (op.mode == MIR_OP_REG || op.mode == MIR_OP_HARD_REG) { + make_obsolete_var_exprs (op.mode == MIR_OP_HARD_REG ? op.u.hard_reg + : reg2var (gen_ctx, op.u.reg), + ctx); + } + } + if (MIR_call_code_p (insn->code)) { + gen_assert (bb_insn->call_hard_reg_args != NULL); + bitmap_for_each (bb_insn->call_hard_reg_args, make_obsolete_var_exprs, ctx); + bitmap_for_each (call_used_hard_regs, make_obsolete_var_exprs, ctx); + bitmap_and_compl (bb->av_gen, bb->av_gen, memory_exprs); + bitmap_ior (bb->av_kill, bb->av_kill, memory_exprs); + } + } + } +} + +static void cse_modify (MIR_context_t ctx) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + bb_insn_t bb_insn, new_bb_insn, next_bb_insn; + bitmap_t av = temp_bitmap; + + curr_bb_av_gen = temp_bitmap; + curr_bb_av_kill = NULL; + for (bb_t bb = DLIST_HEAD (bb_t, curr_cfg->bbs); bb != NULL; bb = DLIST_NEXT (bb_t, bb)) { + bitmap_copy (av, bb->av_in); + for (bb_insn = DLIST_HEAD (bb_insn_t, bb->bb_insns); bb_insn != NULL; bb_insn = next_bb_insn) { + size_t i, nops; + expr_t e; + MIR_reg_t early_clobbered_hard_reg1, early_clobbered_hard_reg2; + MIR_op_t op; + int out_p; + MIR_type_t type; + MIR_insn_code_t move_code; + MIR_insn_t new_insn, insn = bb_insn->insn; + + next_bb_insn = DLIST_NEXT (bb_insn_t, bb_insn); + if (MIR_branch_code_p (insn->code) || insn->code == MIR_RET || insn->code == MIR_SWITCH + || insn->code == MIR_LABEL) + continue; + if (!MIR_call_code_p (insn->code) && insn->code != MIR_ALLOCA && insn->code != MIR_BSTART + && insn->code != MIR_BEND && insn->code != MIR_VA_START && insn->code != MIR_VA_END + && !move_p (insn) + && (!imm_move_p (insn) || insn->ops[1].mode == MIR_OP_REF) + /* See create_expr comments: */ + && insn->ops[0].mode != MIR_OP_MEM && insn->ops[0].mode != MIR_OP_HARD_REG_MEM) { + if (!find_expr (ctx, insn, &e)) { + gen_assert (FALSE); + continue; + } + op = MIR_new_reg_op (ctx, e->temp_reg); + type = MIR_reg_type (ctx, e->temp_reg, curr_func_item->u.func); + move_code + = (type == MIR_T_F ? MIR_FMOV + : type == MIR_T_D ? MIR_DMOV : type == MIR_T_LD ? MIR_LDMOV : MIR_MOV); +#ifndef NDEBUG + MIR_insn_op_mode (ctx, insn, 0, &out_p); /* result here is always 0-th op */ + gen_assert (out_p); +#endif + if (!bitmap_bit_p (av, e->num)) { + bitmap_set_bit_p (av, e->num); + new_insn = MIR_new_insn (ctx, move_code, op, insn->ops[0]); + new_bb_insn = create_bb_insn (ctx, new_insn, bb); + MIR_insert_insn_after (ctx, curr_func_item, insn, new_insn); + DLIST_INSERT_AFTER (bb_insn_t, bb->bb_insns, bb_insn, new_bb_insn); + next_bb_insn = DLIST_NEXT (bb_insn_t, new_bb_insn); +#if MIR_GEN_DEBUG + if (debug_file != NULL) { + fprintf (debug_file, " adding insn "); + MIR_output_insn (ctx, debug_file, new_insn, curr_func_item->u.func, FALSE); + fprintf (debug_file, " after def insn "); + MIR_output_insn (ctx, debug_file, insn, curr_func_item->u.func, TRUE); + } +#endif + } else { + new_insn = MIR_new_insn (ctx, move_code, insn->ops[0], op); + gen_add_insn_after (ctx, insn, new_insn); +#if MIR_GEN_DEBUG + if (debug_file != NULL) { + fprintf (debug_file, " adding insn "); + MIR_output_insn (ctx, debug_file, new_insn, curr_func_item->u.func, FALSE); + fprintf (debug_file, " after use insn "); + MIR_output_insn (ctx, debug_file, insn, curr_func_item->u.func, TRUE); + } +#endif + insn = new_insn; + } + } + get_early_clobbered_hard_reg (insn, &early_clobbered_hard_reg1, &early_clobbered_hard_reg2); + if (early_clobbered_hard_reg1 != MIR_NON_HARD_REG) + make_obsolete_var_exprs (early_clobbered_hard_reg1, ctx); + if (early_clobbered_hard_reg2 != MIR_NON_HARD_REG) + make_obsolete_var_exprs (early_clobbered_hard_reg2, ctx); + nops = MIR_insn_nops (ctx, insn); + for (i = 0; i < nops; i++) { + op = insn->ops[i]; + MIR_insn_op_mode (ctx, insn, i, &out_p); + if (!out_p) continue; + if (op.mode == MIR_OP_MEM || op.mode == MIR_OP_HARD_REG_MEM) { + bitmap_and_compl (av, av, memory_exprs); + } else if (op.mode == MIR_OP_REG || op.mode == MIR_OP_HARD_REG) { + make_obsolete_var_exprs (op.mode == MIR_OP_HARD_REG ? op.u.hard_reg + : reg2var (gen_ctx, op.u.reg), + ctx); + } + } + if (MIR_call_code_p (insn->code)) { + gen_assert (bb_insn->call_hard_reg_args != NULL); + bitmap_for_each (bb_insn->call_hard_reg_args, make_obsolete_var_exprs, ctx); + bitmap_for_each (call_used_hard_regs, make_obsolete_var_exprs, ctx); + bitmap_and_compl (av, av, memory_exprs); + } + } + } +} + +static void cse (MIR_context_t ctx) { + create_exprs (ctx); + create_av_bitmaps (ctx); + solve_dataflow (ctx, TRUE, cse_con_func_0, cse_con_func_n, cse_trans_func); + cse_modify (ctx); +} + +#if MIR_GEN_DEBUG +static void print_exprs (MIR_context_t ctx) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + + fprintf (debug_file, " Expressions:\n"); + for (size_t i = 0; i < VARR_LENGTH (expr_t, exprs); i++) { + size_t nops; + expr_t e = VARR_GET (expr_t, exprs, i); + + fprintf (debug_file, " %3lu: ", (unsigned long) i); + fprintf (debug_file, "%s _", MIR_insn_name (ctx, e->insn->code)); + nops = MIR_insn_nops (ctx, e->insn); + for (size_t j = 1; j < nops; j++) { + fprintf (debug_file, ", "); + MIR_output_op (ctx, debug_file, e->insn->ops[j], curr_func_item->u.func); + } + fprintf (debug_file, "\n"); + } +} + +static void output_bb_cse_info (MIR_context_t ctx, bb_t bb) { + output_bitmap (ctx, " av_in:", bb->av_in); + output_bitmap (ctx, " av_out:", bb->av_out); + output_bitmap (ctx, " av_gen:", bb->av_gen); + output_bitmap (ctx, " av_kill:", bb->av_kill); +} +#endif + +static void cse_clear (MIR_context_t ctx) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + + HTAB_CLEAR (expr_t, expr_tab); + while (VARR_LENGTH (expr_t, exprs) != 0) free (VARR_POP (expr_t, exprs)); + while (VARR_LENGTH (bitmap_t, var2dep_expr) != 0) { + bitmap_t b = VARR_POP (bitmap_t, var2dep_expr); + + if (b != NULL) bitmap_destroy (b); + } + bitmap_clear (memory_exprs); +} + +static void init_cse (MIR_context_t ctx) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + + gen_ctx->cse_ctx = gen_malloc (ctx, sizeof (struct cse_ctx)); + VARR_CREATE (expr_t, exprs, 512); + VARR_CREATE (bitmap_t, var2dep_expr, 512); + memory_exprs = bitmap_create2 (DEFAULT_INIT_BITMAP_BITS_NUM); + HTAB_CREATE (expr_t, expr_tab, 1024, expr_hash, expr_eq); +} + +static void finish_cse (MIR_context_t ctx) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + + VARR_DESTROY (expr_t, exprs); + bitmap_destroy (memory_exprs); + VARR_DESTROY (bitmap_t, var2dep_expr); + HTAB_DESTROY (expr_t, expr_tab); + free (gen_ctx->cse_ctx); + gen_ctx->cse_ctx = NULL; +} + +#undef av_in +#undef av_out +#undef av_kill +#undef av_gen + +/* New Page */ + +/* Sparse Conditional Constant Propagation. Live info should exist. */ + +#define live_in in +#define live_out out + +enum ccp_val_kind { CCP_CONST = 0, CCP_VARYING, CCP_UNKNOWN }; + +enum place_type { OCC_INSN, OCC_BB_START, OCC_BB_END }; + +typedef struct { + enum place_type type; + union { + MIR_insn_t insn; + bb_t bb; + } u; +} place_t; + +typedef struct var_occ *var_occ_t; + +DEF_DLIST_LINK (var_occ_t); +DEF_DLIST_TYPE (var_occ_t); + +/* Occurences at BB start are defs, ones at BB end are uses. */ +struct var_occ { + MIR_reg_t var; + enum ccp_val_kind val_kind : 8; + unsigned int flag : 8; + const_t val; + place_t place; + var_occ_t def; + DLIST (var_occ_t) uses; /* Empty for def */ + DLIST_LINK (var_occ_t) use_link; +}; + +DEF_DLIST_CODE (var_occ_t, use_link); + +typedef DLIST (var_occ_t) bb_start_occ_list_t; +DEF_VARR (bb_start_occ_list_t); + +DEF_VARR (var_occ_t); +DEF_HTAB (var_occ_t); + +typedef struct { + int producer_age, op_age; + var_occ_t producer; /* valid if producer_age == curr_producer_age */ + var_occ_t op_var_use; /* valid if op_age == curr_op_age */ +} var_producer_t; + +DEF_VARR (var_producer_t); + +DEF_VARR (bb_insn_t); + +struct ccp_ctx { + VARR (bb_start_occ_list_t) * bb_start_occ_list_varr; + bb_start_occ_list_t *bb_start_occ_lists; + VARR (var_occ_t) * var_occs; + HTAB (var_occ_t) * var_occ_tab; + int curr_producer_age, curr_op_age; + var_producer_t *producers; + VARR (var_producer_t) * producer_varr; + bb_t ccp_end_bb; + bitmap_t bb_visited; + VARR (bb_t) * ccp_bbs; + VARR (var_occ_t) * ccp_var_occs; + VARR (bb_insn_t) * ccp_insns; +}; + +#define bb_start_occ_list_varr gen_ctx->ccp_ctx->bb_start_occ_list_varr +#define bb_start_occ_lists gen_ctx->ccp_ctx->bb_start_occ_lists +#define var_occs gen_ctx->ccp_ctx->var_occs +#define var_occ_tab gen_ctx->ccp_ctx->var_occ_tab +#define curr_producer_age gen_ctx->ccp_ctx->curr_producer_age +#define curr_op_age gen_ctx->ccp_ctx->curr_op_age +#define producers gen_ctx->ccp_ctx->producers +#define producer_varr gen_ctx->ccp_ctx->producer_varr +#define ccp_end_bb gen_ctx->ccp_ctx->ccp_end_bb +#define bb_visited gen_ctx->ccp_ctx->bb_visited +#define ccp_bbs gen_ctx->ccp_ctx->ccp_bbs +#define ccp_var_occs gen_ctx->ccp_ctx->ccp_var_occs +#define ccp_insns gen_ctx->ccp_ctx->ccp_insns + +static htab_hash_t var_occ_hash (var_occ_t vo) { + gen_assert (vo->place.type != OCC_INSN); + return mir_hash_finish ( + mir_hash_step (mir_hash_step (mir_hash_step (mir_hash_init (0x54), (uint64_t) vo->var), + (uint64_t) vo->place.type), + (uint64_t) vo->place.u.bb)); +} + +static int var_occ_eq (var_occ_t vo1, var_occ_t vo2) { + return (vo1->var == vo2->var && vo1->place.type == vo2->place.type + && vo1->place.u.bb == vo2->place.u.bb); +} + +static void init_var_occ (var_occ_t var_occ, MIR_reg_t var, enum place_type type, bb_t bb, + MIR_insn_t insn) { + var_occ->var = var; + var_occ->val_kind = CCP_UNKNOWN; + var_occ->place.type = type; + if (bb == NULL) + var_occ->place.u.insn = insn; + else + var_occ->place.u.bb = bb; + var_occ->def = NULL; + var_occ->flag = FALSE; + DLIST_INIT (var_occ_t, var_occ->uses); +} + +static var_occ_t new_insn_var_occ (MIR_context_t ctx, MIR_reg_t var, MIR_insn_t insn) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + var_occ_t var_occ = gen_malloc (ctx, sizeof (struct var_occ)); + + init_var_occ (var_occ, var, OCC_INSN, NULL, insn); + VARR_PUSH (var_occ_t, var_occs, var_occ); + return var_occ; +} + +static var_occ_t get_bb_var_occ (MIR_context_t ctx, MIR_reg_t var, enum place_type type, bb_t bb) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + struct var_occ vos; + var_occ_t var_occ; + + init_var_occ (&vos, var, type, bb, NULL); + if (HTAB_DO (var_occ_t, var_occ_tab, &vos, HTAB_FIND, var_occ)) return var_occ; + var_occ = gen_malloc (ctx, sizeof (struct var_occ)); + *var_occ = vos; + VARR_PUSH (var_occ_t, var_occs, var_occ); + HTAB_DO (var_occ_t, var_occ_tab, var_occ, HTAB_INSERT, var_occ); + if (type == OCC_BB_START) { + DLIST_APPEND (var_occ_t, bb_start_occ_lists[bb->index], var_occ); + if (DLIST_EL (bb_t, curr_cfg->bbs, 0) == bb && var_is_reg_p (var) + && var2reg (gen_ctx, var) <= curr_func_item->u.func->nargs) { + var_occ->val_kind = CCP_VARYING; + } + } + return var_occ; +} + +static var_occ_t get_var_def (MIR_context_t ctx, MIR_reg_t var, bb_t bb) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + var_occ_t var_occ; + + if (producers[var].producer_age == curr_producer_age) { + var_occ = producers[var].producer; + } else { /* use w/o a producer insn in the block */ + producers[var].producer = var_occ = get_bb_var_occ (ctx, var, OCC_BB_START, bb); + producers[var].producer_age = curr_producer_age; + } + return var_occ; +} + +static void process_op_var_use (MIR_context_t ctx, MIR_reg_t var, bb_insn_t bb_insn, MIR_op_t *op) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + var_occ_t def, use; + + if (producers[var].op_age == curr_op_age) { + op->data = producers[var].op_var_use; + return; /* var was already another operand in the insn */ + } + producers[var].op_age = curr_op_age; + def = get_var_def (ctx, var, bb_insn->bb); + producers[var].op_var_use = use = new_insn_var_occ (ctx, var, bb_insn->insn); + op->data = use; + use->def = def; + DLIST_APPEND (var_occ_t, def->uses, use); +} + +static void process_op_use (MIR_context_t ctx, MIR_op_t *op, bb_insn_t bb_insn) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + + switch (op->mode) { + case MIR_OP_REG: + if (op->u.reg != 0) process_op_var_use (ctx, reg2var (gen_ctx, op->u.reg), bb_insn, op); + break; + case MIR_OP_HARD_REG: + if (op->u.hard_reg != MIR_NON_HARD_REG) process_op_var_use (ctx, op->u.hard_reg, bb_insn, op); + break; + case MIR_OP_MEM: + if (op->u.mem.base != 0) + process_op_var_use (ctx, reg2var (gen_ctx, op->u.mem.base), bb_insn, op); + if (op->u.mem.index != 0) + process_op_var_use (ctx, reg2var (gen_ctx, op->u.mem.index), bb_insn, op); + break; + case MIR_OP_HARD_REG_MEM: + if (op->u.hard_reg_mem.base != MIR_NON_HARD_REG) + process_op_var_use (ctx, op->u.hard_reg_mem.base, bb_insn, op); + if (op->u.hard_reg_mem.index != MIR_NON_HARD_REG) + process_op_var_use (ctx, op->u.hard_reg_mem.index, bb_insn, op); + break; + default: break; + } +} + +static void process_bb_end (size_t el, void *data) { + MIR_context_t ctx = data; + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + MIR_reg_t var = el; + var_occ_t use = get_bb_var_occ (ctx, var, OCC_BB_END, ccp_end_bb); + var_occ_t def = get_var_def (ctx, var, ccp_end_bb); + + use->def = def; + DLIST_APPEND (var_occ_t, def->uses, use); +} + +/* Build a web of def-use with auxiliary usages and definitions at BB + borders to emulate SSA on which the sparse conditional propagation + is usually done. We could do non-sparse CCP w/o building the web + but it is much slower algorithm. */ +static void build_var_occ_web (MIR_context_t ctx) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + MIR_op_t *op; + MIR_insn_t insn; + size_t i, nops; + int out_p; + MIR_reg_t dst_var; + var_occ_t var_occ; + var_producer_t var_producer; + bb_start_occ_list_t list; + + DLIST_INIT (var_occ_t, list); + while (VARR_LENGTH (bb_start_occ_list_t, bb_start_occ_list_varr) < curr_bb_index) + VARR_PUSH (bb_start_occ_list_t, bb_start_occ_list_varr, list); + bb_start_occ_lists = VARR_ADDR (bb_start_occ_list_t, bb_start_occ_list_varr); + var_producer.producer_age = var_producer.op_age = 0; + var_producer.producer = var_producer.op_var_use = NULL; + while (VARR_LENGTH (var_producer_t, producer_varr) < get_nvars (ctx)) + VARR_PUSH (var_producer_t, producer_varr, var_producer); + producers = VARR_ADDR (var_producer_t, producer_varr); + for (bb_t bb = DLIST_HEAD (bb_t, curr_cfg->bbs); bb != NULL; bb = DLIST_NEXT (bb_t, bb)) { + curr_producer_age++; + for (bb_insn_t bb_insn = DLIST_HEAD (bb_insn_t, bb->bb_insns); bb_insn != NULL; + bb_insn = DLIST_NEXT (bb_insn_t, bb_insn)) { + curr_op_age++; + insn = bb_insn->insn; + nops = MIR_insn_nops (ctx, insn); + for (i = 0; i < nops; i++) { /* process inputs */ + MIR_insn_op_mode (ctx, insn, i, &out_p); + op = &insn->ops[i]; + if (!out_p) process_op_use (ctx, op, bb_insn); + } + for (i = 0; i < nops; i++) { /* process outputs */ + MIR_insn_op_mode (ctx, insn, i, &out_p); + op = &insn->ops[i]; + if (out_p && (op->mode == MIR_OP_REG || op->mode == MIR_OP_HARD_REG)) { + dst_var = op->mode == MIR_OP_HARD_REG ? op->u.hard_reg : reg2var (gen_ctx, op->u.reg); + producers[dst_var].producer_age = curr_producer_age; + producers[dst_var].op_age = curr_op_age; + producers[dst_var].producer = var_occ = new_insn_var_occ (ctx, dst_var, insn); + op->data = producers[dst_var].producer; + } + } + } + ccp_end_bb = bb; + bitmap_for_each (bb->live_out, process_bb_end, ctx); + } +} + +#undef live_in +#undef live_out + +static void var_occs_clear (MIR_context_t ctx) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + + HTAB_CLEAR (var_occ_t, var_occ_tab); + while (VARR_LENGTH (var_occ_t, var_occs) != 0) free (VARR_POP (var_occ_t, var_occs)); + VARR_TRUNC (bb_start_occ_list_t, bb_start_occ_list_varr, 0); + VARR_TRUNC (var_producer_t, producer_varr, 0); +} + +static void init_var_occs (MIR_context_t ctx) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + + VARR_CREATE (bb_start_occ_list_t, bb_start_occ_list_varr, 256); + VARR_CREATE (var_occ_t, var_occs, 1024); + curr_producer_age = curr_op_age = 0; + VARR_CREATE (var_producer_t, producer_varr, 256); + HTAB_CREATE (var_occ_t, var_occ_tab, 1024, var_occ_hash, var_occ_eq); +} + +static void finish_var_occs (MIR_context_t ctx) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + + VARR_DESTROY (bb_start_occ_list_t, bb_start_occ_list_varr); + VARR_DESTROY (var_occ_t, var_occs); + VARR_DESTROY (var_producer_t, producer_varr); + HTAB_DESTROY (var_occ_t, var_occ_tab); +} + +static void initiate_ccp_info (MIR_context_t ctx) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + bb_insn_t bb_insn; + + for (bb_t bb = DLIST_HEAD (bb_t, curr_cfg->bbs); bb != NULL; bb = DLIST_NEXT (bb_t, bb)) { + if ((bb_insn = DLIST_TAIL (bb_insn_t, bb->bb_insns)) != NULL + && MIR_branch_code_p (bb_insn->insn->code) && bb_insn->insn->code != MIR_JMP + && bb_insn->insn->code != MIR_SWITCH) { + for (edge_t e = DLIST_HEAD (out_edge_t, bb->out_edges); e != NULL; + e = DLIST_NEXT (out_edge_t, e)) + if (e->dst != DLIST_EL (bb_t, curr_cfg->bbs, 1)) /* ignore exit bb */ + e->skipped_p = TRUE; + } + } + bitmap_clear (bb_visited); + VARR_TRUNC (var_occ_t, ccp_var_occs, 0); + VARR_TRUNC (bb_insn_t, ccp_insns, 0); + VARR_TRUNC (bb_t, ccp_bbs, 0); + VARR_PUSH (bb_t, ccp_bbs, DLIST_HEAD (bb_t, curr_cfg->bbs)); /* entry bb */ +} + +static int var_op_p (MIR_op_t op) { return op.mode == MIR_OP_HARD_REG || op.mode == MIR_OP_REG; } +static int var_insn_op_p (MIR_insn_t insn, size_t nop) { return var_op_p (insn->ops[nop]); } + +static enum ccp_val_kind get_op (MIR_insn_t insn, size_t nop, const_t *val) { + MIR_op_t op; + var_occ_t var_occ, def; + + if (!var_insn_op_p (insn, nop)) { + if ((op = insn->ops[nop]).mode == MIR_OP_INT) { + val->uns_p = FALSE; + val->u.i = op.u.i; + return CCP_CONST; + } else if (op.mode == MIR_OP_UINT) { + val->uns_p = TRUE; + val->u.u = op.u.u; + return CCP_CONST; + } + return CCP_VARYING; + } + var_occ = insn->ops[nop].data; + def = var_occ->def; + if (def->val_kind == CCP_CONST) *val = def->val; + return def->val_kind; +} + +static enum ccp_val_kind get_2ops (MIR_insn_t insn, const_t *val1, int out_p) { + if (out_p && !var_insn_op_p (insn, 0)) return CCP_UNKNOWN; + return get_op (insn, 1, val1); +} + +static enum ccp_val_kind get_3ops (MIR_insn_t insn, const_t *val1, const_t *val2, int out_p) { + enum ccp_val_kind res1, res2; + + if (out_p && !var_insn_op_p (insn, 0)) return CCP_UNKNOWN; + if ((res1 = get_op (insn, 1, val1)) == CCP_VARYING) return CCP_VARYING; + if ((res2 = get_op (insn, 2, val2)) == CCP_VARYING) return CCP_VARYING; + return res1 == CCP_UNKNOWN || res2 == CCP_UNKNOWN ? CCP_UNKNOWN : CCP_CONST; +} + +static enum ccp_val_kind get_2iops (MIR_insn_t insn, int64_t *p, int out_p) { + const_t val; + enum ccp_val_kind res; + + if ((res = get_2ops (insn, &val, out_p))) return res; + *p = val.u.i; + return CCP_CONST; +} + +static enum ccp_val_kind get_2isops (MIR_insn_t insn, int32_t *p, int out_p) { + const_t val; + enum ccp_val_kind res; + + if ((res = get_2ops (insn, &val, out_p))) return res; + *p = val.u.i; + return CCP_CONST; +} + +static enum ccp_val_kind get_2usops (MIR_insn_t insn, uint32_t *p, int out_p) { + const_t val; + enum ccp_val_kind res; + + if ((res = get_2ops (insn, &val, out_p))) return res; + *p = val.u.u; + return CCP_CONST; +} + +static enum ccp_val_kind get_3iops (MIR_insn_t insn, int64_t *p1, int64_t *p2, int out_p) { + const_t val1, val2; + enum ccp_val_kind res; + + if ((res = get_3ops (insn, &val1, &val2, out_p))) return res; + *p1 = val1.u.i; + *p2 = val2.u.i; + return CCP_CONST; +} + +static enum ccp_val_kind get_3isops (MIR_insn_t insn, int32_t *p1, int32_t *p2, int out_p) { + const_t val1, val2; + enum ccp_val_kind res; + + if ((res = get_3ops (insn, &val1, &val2, out_p))) return res; + *p1 = val1.u.i; + *p2 = val2.u.i; + return CCP_CONST; +} + +static enum ccp_val_kind get_3uops (MIR_insn_t insn, uint64_t *p1, uint64_t *p2, int out_p) { + const_t val1, val2; + enum ccp_val_kind res; + + if ((res = get_3ops (insn, &val1, &val2, out_p))) return res; + *p1 = val1.u.u; + *p2 = val2.u.u; + return CCP_CONST; +} + +static enum ccp_val_kind get_3usops (MIR_insn_t insn, uint32_t *p1, uint32_t *p2, int out_p) { + const_t val1, val2; + enum ccp_val_kind res; + + if ((res = get_3ops (insn, &val1, &val2, out_p))) return res; + *p1 = val1.u.u; + *p2 = val2.u.u; + return CCP_CONST; +} + +#define EXT(tp) \ + do { \ + int64_t p; \ + if ((ccp_res = get_2iops (insn, &p, TRUE)) != CCP_CONST) goto non_const; \ + val.uns_p = FALSE; \ + val.u.i = (tp) p; \ + } while (0) + +#define IOP2(op) \ + do { \ + int64_t p; \ + if ((ccp_res = get_2iops (insn, &p, TRUE)) != CCP_CONST) goto non_const; \ + val.uns_p = FALSE; \ + val.u.i = op p; \ + } while (0) + +#define IOP2S(op) \ + do { \ + int32_t p; \ + if ((ccp_res = get_2isops (insn, &p, TRUE)) != CCP_CONST) goto non_const; \ + val.uns_p = FALSE; \ + val.u.i = op p; \ + } while (0) + +#define UOP2S(op) \ + do { \ + uint32_t p; \ + if ((ccp_res = get_2usops (insn, &p, TRUE)) != CCP_CONST) goto non_const; \ + val.uns_p = FALSE; \ + val.u.u = op p; \ + } while (0) + +#define IOP3(op) \ + do { \ + int64_t p1, p2; \ + if ((ccp_res = get_3iops (insn, &p1, &p2, TRUE)) != CCP_CONST) goto non_const; \ + val.uns_p = FALSE; \ + val.u.i = p1 op p2; \ + } while (0) + +#define IOP3S(op) \ + do { \ + int32_t p1, p2; \ + if ((ccp_res = get_3isops (insn, &p1, &p2, TRUE)) != CCP_CONST) goto non_const; \ + val.uns_p = FALSE; \ + val.u.i = p1 op p2; \ + } while (0) + +#define UOP3(op) \ + do { \ + uint64_t p1, p2; \ + if ((ccp_res = get_3uops (insn, &p1, &p2, TRUE)) != CCP_CONST) goto non_const; \ + val.uns_p = TRUE; \ + val.u.u = p1 op p2; \ + } while (0) + +#define UOP3S(op) \ + do { \ + uint32_t p1, p2; \ + if ((ccp_res = get_3usops (insn, &p1, &p2, TRUE)) != CCP_CONST) goto non_const; \ + val.uns_p = TRUE; \ + val.u.u = p1 op p2; \ + } while (0) + +#define IOP30(op) \ + do { \ + if ((ccp_res = get_op (insn, 2, &val)) != CCP_CONST || val.u.i == 0) goto non_const0; \ + IOP3 (op); \ + } while (0) + +#define IOP3S0(op) \ + do { \ + if ((ccp_res = get_op (insn, 2, &val)) != CCP_CONST || val.u.i == 0) goto non_const0; \ + IOP3S (op); \ + } while (0) + +#define UOP30(op) \ + do { \ + if ((ccp_res = get_op (insn, 2, &val)) != CCP_CONST || val.u.u == 0) goto non_const0; \ + UOP3 (op); \ + } while (0) + +#define UOP3S0(op) \ + do { \ + if ((ccp_res = get_op (insn, 2, &val)) != CCP_CONST || val.u.u == 0) goto non_const0; \ + UOP3S (op); \ + } while (0) + +#define ICMP(op) \ + do { \ + int64_t p1, p2; \ + if ((ccp_res = get_3iops (insn, &p1, &p2, TRUE)) != CCP_CONST) goto non_const; \ + val.uns_p = FALSE; \ + val.u.i = p1 op p2; \ + } while (0) + +#define ICMPS(op) \ + do { \ + int32_t p1, p2; \ + if ((ccp_res = get_3isops (insn, &p1, &p2, TRUE)) != CCP_CONST) goto non_const; \ + val.uns_p = FALSE; \ + val.u.i = p1 op p2; \ + } while (0) + +#define UCMP(op) \ + do { \ + uint64_t p1, p2; \ + if ((ccp_res = get_3uops (insn, &p1, &p2, TRUE)) != CCP_CONST) goto non_const; \ + val.uns_p = FALSE; \ + val.u.i = p1 op p2; \ + } while (0) + +#define UCMPS(op) \ + do { \ + uint32_t p1, p2; \ + if ((ccp_res = get_3usops (insn, &p1, &p2, TRUE)) != CCP_CONST) goto non_const; \ + val.uns_p = FALSE; \ + val.u.i = p1 op p2; \ + } while (0) + +#define BICMP(op) \ + do { \ + int64_t p1, p2; \ + if ((ccp_res = get_3iops (insn, &p1, &p2, FALSE)) != CCP_CONST) goto non_const; \ + val.uns_p = FALSE; \ + val.u.i = p1 op p2; \ + } while (0) + +#define BICMPS(op) \ + do { \ + int32_t p1, p2; \ + if ((ccp_res = get_3isops (insn, &p1, &p2, FALSE)) != CCP_CONST) goto non_const; \ + val.uns_p = FALSE; \ + val.u.i = p1 op p2; \ + } while (0) + +#define BUCMP(op) \ + do { \ + uint64_t p1, p2; \ + if ((ccp_res = get_3uops (insn, &p1, &p2, FALSE)) != CCP_CONST) goto non_const; \ + val.uns_p = FALSE; \ + val.u.i = p1 op p2; \ + } while (0) + +#define BUCMPS(op) \ + do { \ + uint32_t p1, p2; \ + if ((ccp_res = get_3usops (insn, &p1, &p2, FALSE)) != CCP_CONST) goto non_const; \ + val.uns_p = FALSE; \ + val.u.i = p1 op p2; \ + } while (0) + +static int get_ccp_res_op (MIR_context_t ctx, MIR_insn_t insn, int out_num, MIR_op_t *op) { + int out_p; + MIR_op_t proto_op; + MIR_proto_t proto; + + if (MIR_call_code_p (insn->code)) { + proto_op = insn->ops[0]; + mir_assert (proto_op.mode == MIR_OP_REF && proto_op.u.ref->item_type == MIR_proto_item); + proto = proto_op.u.ref->u.proto; + if (out_num >= proto->nres) return FALSE; + *op = insn->ops[out_num + 2]; + return TRUE; + } + if (out_num > 0 || MIR_insn_nops (ctx, insn) < 1) return FALSE; + MIR_insn_op_mode (ctx, insn, 0, &out_p); + if (!out_p) return FALSE; + *op = insn->ops[0]; + return TRUE; +} + +static int ccp_insn_update (MIR_context_t ctx, MIR_insn_t insn, const_t *res) { + // ??? should we do CCP for FP too + MIR_op_t op; + int change_p; + enum ccp_val_kind ccp_res; + const_t val; + var_occ_t var_occ; + enum ccp_val_kind val_kind; + + switch (insn->code) { + case MIR_MOV: IOP2 (+); break; + case MIR_EXT8: EXT (int8_t); break; + case MIR_EXT16: EXT (int16_t); break; + case MIR_EXT32: EXT (int32_t); break; + case MIR_UEXT8: EXT (uint8_t); break; + case MIR_UEXT16: EXT (uint16_t); break; + case MIR_UEXT32: EXT (uint32_t); break; + + case MIR_NEG: IOP2 (-); break; + case MIR_NEGS: IOP2S (-); break; + + case MIR_ADD: IOP3 (+); break; + case MIR_ADDS: IOP3S (+); break; + + case MIR_SUB: IOP3 (-); break; + case MIR_SUBS: IOP3S (-); break; + + case MIR_MUL: IOP3 (*); break; + case MIR_MULS: IOP3S (*); break; + + case MIR_DIV: IOP30 (/); break; + case MIR_DIVS: IOP3S0 (/); break; + case MIR_UDIV: UOP30 (/); break; + case MIR_UDIVS: UOP3S0 (/); break; + + case MIR_MOD: IOP30 (%); break; + case MIR_MODS: IOP3S0 (%); break; + case MIR_UMOD: UOP30 (%); break; + case MIR_UMODS: UOP3S0 (%); break; + + case MIR_AND: IOP3 (&); break; + case MIR_ANDS: IOP3S (&); break; + case MIR_OR: IOP3 (|); break; + case MIR_ORS: IOP3S (|); break; + case MIR_XOR: IOP3 (^); break; + case MIR_XORS: IOP3S (^); break; + + case MIR_LSH: IOP3 (<<); break; + case MIR_LSHS: IOP3S (<<); break; + case MIR_RSH: IOP3 (>>); break; + case MIR_RSHS: IOP3S (>>); break; + case MIR_URSH: UOP3 (>>); break; + case MIR_URSHS: UOP3S (>>); break; + + case MIR_EQ: ICMP (==); break; + case MIR_EQS: ICMPS (==); break; + case MIR_NE: ICMP (!=); break; + case MIR_NES: ICMPS (!=); break; + + case MIR_LT: ICMP (<); break; + case MIR_LTS: ICMPS (<); break; + case MIR_ULT: UCMP (<); break; + case MIR_ULTS: UCMPS (<); break; + case MIR_LE: ICMP (<=); break; + case MIR_LES: ICMPS (<=); break; + case MIR_ULE: UCMP (<=); break; + case MIR_ULES: UCMPS (<=); break; + case MIR_GT: ICMP (>); break; + case MIR_GTS: ICMPS (>); break; + case MIR_UGT: UCMP (>); break; + case MIR_UGTS: UCMPS (>); break; + case MIR_GE: ICMP (>=); break; + case MIR_GES: ICMPS (>=); break; + case MIR_UGE: UCMP (>=); break; + case MIR_UGES: UCMPS (>=); break; + + default: ccp_res = CCP_VARYING; goto non_const; + } +#ifndef NDEBUG + { + int out_p; + + MIR_insn_op_mode (ctx, insn, 0, &out_p); /* result here is always 0-th op */ + gen_assert (out_p); + } +#endif + var_occ = insn->ops[0].data; + val_kind = var_occ->val_kind; + gen_assert (var_occ->def == NULL && (val_kind == CCP_UNKNOWN || val_kind == CCP_CONST)); + var_occ->val_kind = CCP_CONST; + var_occ->val = val; + if (res != NULL) *res = val; + return val_kind != CCP_CONST; +non_const0: + if (ccp_res == CCP_CONST && val.u.i == 0) ccp_res = CCP_VARYING; +non_const: + if (ccp_res == CCP_UNKNOWN) return FALSE; + gen_assert (ccp_res == CCP_VARYING); + change_p = FALSE; + for (int i = 0; get_ccp_res_op (ctx, insn, i, &op); i++) { + if (op.mode != MIR_OP_HARD_REG && op.mode != MIR_OP_REG) continue; + var_occ = op.data; + gen_assert (var_occ->def == NULL); + if (var_occ->val_kind != CCP_VARYING) change_p = TRUE; + var_occ->val_kind = CCP_VARYING; + } + return change_p; +} + +static enum ccp_val_kind ccp_branch_update (MIR_insn_t insn, int *res) { + enum ccp_val_kind ccp_res; + const_t val; + + switch (insn->code) { + case MIR_BT: + case MIR_BTS: + case MIR_BF: + case MIR_BFS: + if ((ccp_res = get_op (insn, 1, &val)) != CCP_CONST) return ccp_res; + if (insn->code == MIR_BTS || insn->code == MIR_BFS) + *res = val.uns_p ? (uint32_t) val.u.u != 0 : (int32_t) val.u.i != 0; + else + *res = val.uns_p ? val.u.u != 0 : val.u.i != 0; + if (insn->code == MIR_BF || insn->code == MIR_BFS) *res = !*res; + return CCP_CONST; + case MIR_BEQ: BICMP (==); break; + case MIR_BEQS: BICMPS (==); break; + case MIR_BNE: BICMP (!=); break; + case MIR_BNES: BICMPS (!=); break; + + case MIR_BLT: BICMP (<); break; + case MIR_BLTS: BICMPS (<); break; + case MIR_UBLT: BUCMP (<); break; + case MIR_UBLTS: BUCMPS (<); break; + case MIR_BLE: BICMP (<=); break; + case MIR_BLES: BICMPS (<=); break; + case MIR_UBLE: BUCMP (<=); break; + case MIR_UBLES: BUCMPS (<=); break; + case MIR_BGT: BICMP (>); break; + case MIR_BGTS: BICMPS (>); break; + case MIR_UBGT: BUCMP (>); break; + case MIR_UBGTS: BUCMPS (>); break; + case MIR_BGE: BICMP (>=); break; + case MIR_BGES: BICMPS (>=); break; + case MIR_UBGE: BUCMP (>=); break; + case MIR_UBGES: BUCMPS (>=); break; + + default: return CCP_VARYING; // ??? should we do CCP for FP BCMP too + } + *res = val.u.i; + return CCP_CONST; +non_const: + return ccp_res; +} + +static void ccp_push_used_insns (MIR_context_t ctx, var_occ_t def) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + + for (var_occ_t var_occ = DLIST_HEAD (var_occ_t, def->uses); var_occ != NULL; + var_occ = DLIST_NEXT (var_occ_t, var_occ)) + if (var_occ->place.type == OCC_INSN) { + bb_insn_t bb_insn = var_occ->place.u.insn->data; + + if (bb_insn->flag) continue; /* already in ccp_insns */ + VARR_PUSH (bb_insn_t, ccp_insns, bb_insn); +#if MIR_GEN_DEBUG + if (debug_file != NULL) { + fprintf (debug_file, " pushing bb%lu insn: ", (unsigned long) bb_insn->bb->index); + MIR_output_insn (ctx, debug_file, bb_insn->insn, curr_func_item->u.func, TRUE); + } +#endif + bb_insn->flag = TRUE; + } else { + struct var_occ vos; + var_occ_t tab_var_occ; + + gen_assert (var_occ->place.type == OCC_BB_END); + for (edge_t e = DLIST_HEAD (out_edge_t, var_occ->place.u.bb->out_edges); e != NULL; + e = DLIST_NEXT (out_edge_t, e)) { + if (e->skipped_p) continue; + vos = *var_occ; + vos.place.type = OCC_BB_START; + vos.place.u.bb = e->dst; + if (!HTAB_DO (var_occ_t, var_occ_tab, &vos, HTAB_FIND, tab_var_occ) || tab_var_occ->flag) + continue; /* var_occ at the start of BB in subsequent BB is already in ccp_var_occs */ +#if MIR_GEN_DEBUG + if (debug_file != NULL) + fprintf (debug_file, " pushing var%lu(%s) at start of bb%lu\n", + (long unsigned) vos.var, + var_is_reg_p (vos.var) + ? MIR_reg_name (ctx, var2reg (gen_ctx, vos.var), curr_func_item->u.func) + : "", + (unsigned long) e->dst->index); +#endif + VARR_PUSH (var_occ_t, ccp_var_occs, tab_var_occ); + tab_var_occ->flag = TRUE; + } + } +} + +static void ccp_process_bb_start_var_occ (MIR_context_t ctx, var_occ_t var_occ, bb_t bb, + int from_bb_process_p) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + struct var_occ vos; + var_occ_t tab_var_occ, def; + int change_p; + +#if MIR_GEN_DEBUG + if (debug_file != NULL) + fprintf (debug_file, + " %sprocessing var%lu(%s) at start of bb%lu:", from_bb_process_p ? " " : "", + (long unsigned) var_occ->var, + var_is_reg_p (var_occ->var) + ? MIR_reg_name (ctx, var2reg (gen_ctx, var_occ->var), curr_func_item->u.func) + : "", + (unsigned long) var_occ->place.u.bb->index); +#endif + gen_assert (var_occ->place.type == OCC_BB_START && bb == var_occ->place.u.bb); + if (var_occ->val_kind == CCP_VARYING) { +#if MIR_GEN_DEBUG + if (debug_file != NULL) fprintf (debug_file, " already varying\n"); +#endif + return; + } else if (bb->index == 0) { /* Non-parameter at entry BB (it means using undefined value) */ +#if MIR_GEN_DEBUG + if (debug_file != NULL) fprintf (debug_file, " making varying\n"); +#endif + var_occ->val_kind = CCP_VARYING; + } + change_p = FALSE; + for (edge_t e = DLIST_HEAD (in_edge_t, bb->in_edges); e != NULL; e = DLIST_NEXT (in_edge_t, e)) { + /* Update var_occ value: */ + if (e->skipped_p) continue; + vos.place.type = OCC_BB_END; + vos.place.u.bb = e->src; + vos.var = var_occ->var; + if (!HTAB_DO (var_occ_t, var_occ_tab, &vos, HTAB_FIND, tab_var_occ)) { + gen_assert (FALSE); + return; + } + def = tab_var_occ->def; + gen_assert (def != NULL); + if (def->val_kind == CCP_UNKNOWN) continue; + gen_assert (def->def == NULL && var_occ->def == NULL); + if (var_occ->val_kind == CCP_UNKNOWN || def->val_kind == CCP_VARYING) { + change_p = var_occ->val_kind != def->val_kind; + var_occ->val_kind = def->val_kind; + if (def->val_kind == CCP_VARYING) break; + if (def->val_kind == CCP_CONST) var_occ->val = def->val; + } else { + gen_assert (var_occ->val_kind == CCP_CONST && def->val_kind == CCP_CONST); + if (var_occ->val.uns_p != def->val.uns_p + || (var_occ->val.uns_p && var_occ->val.u.u != def->val.u.u) + || (!var_occ->val.uns_p && var_occ->val.u.i != def->val.u.i)) { + var_occ->val_kind = CCP_VARYING; + change_p = TRUE; + break; + } + } + } +#if MIR_GEN_DEBUG + if (debug_file != NULL) { + if (var_occ->val_kind != CCP_CONST) { + fprintf (debug_file, " %s%s\n", change_p ? "changed to " : "", + var_occ->val_kind == CCP_UNKNOWN ? "unknown" : "varying"); + } else { + fprintf (debug_file, " %sconst ", change_p ? "changed to " : ""); + print_const (debug_file, var_occ->val); + fprintf (debug_file, "\n"); + } + } +#endif + if (change_p) ccp_push_used_insns (ctx, var_occ); +} + +static void ccp_process_active_edge (MIR_context_t ctx, edge_t e) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + + if (e->skipped_p && !e->dst->flag) { +#if MIR_GEN_DEBUG + if (debug_file != NULL) + fprintf (debug_file, " Make edge bb%lu->bb%lu active\n", + (unsigned long) e->src->index, (unsigned long) e->dst->index); +#endif + e->dst->flag = TRUE; /* just activated edge whose dest is not in ccp_bbs */ + VARR_PUSH (bb_t, ccp_bbs, e->dst); + } + e->skipped_p = FALSE; +} + +static void ccp_make_insn_update (MIR_context_t ctx, MIR_insn_t insn) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + int i, def_p; + MIR_op_t op; + var_occ_t var_occ; + + if (!ccp_insn_update (ctx, insn, NULL)) { +#if MIR_GEN_DEBUG + if (debug_file != NULL) { + if (MIR_call_code_p (insn->code)) { + fprintf (debug_file, " -- keep all results varying"); + } else if (get_ccp_res_op (ctx, insn, 0, &op) && var_insn_op_p (insn, 0)) { + var_occ = op.data; + if (var_occ->val_kind == CCP_UNKNOWN) { + fprintf (debug_file, " -- make the result unknown"); + } else if (var_occ->val_kind == CCP_VARYING) { + fprintf (debug_file, " -- keep the result varying"); + } else { + gen_assert (var_occ->val_kind == CCP_CONST); + fprintf (debug_file, " -- keep the result a constant "); + print_const (debug_file, var_occ->val); + } + } + fprintf (debug_file, "\n"); + } +#endif + } else { + def_p = FALSE; + for (i = 0; get_ccp_res_op (ctx, insn, i, &op); i++) + if (var_op_p (op)) { + def_p = TRUE; + var_occ = op.data; + ccp_push_used_insns (ctx, var_occ); + } +#if MIR_GEN_DEBUG + if (debug_file != NULL && def_p) { + if (MIR_call_code_p (insn->code)) { + fprintf (debug_file, " -- make all results varying"); + } else if (var_occ->val_kind == CCP_VARYING) { + fprintf (debug_file, " -- make the result varying\n"); + } else { + gen_assert (var_occ->val_kind == CCP_CONST); + fprintf (debug_file, " -- make the result a constant "); + print_const (debug_file, var_occ->val); + fprintf (debug_file, "\n"); + } + } +#endif + } +} + +static void ccp_process_insn (MIR_context_t ctx, bb_insn_t bb_insn) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + int res; + enum ccp_val_kind ccp_res; + edge_t e; + bb_t bb = bb_insn->bb; + MIR_insn_t insn = bb_insn->insn; + +#if MIR_GEN_DEBUG + if (debug_file != NULL) { + fprintf (debug_file, " processing bb%lu insn: ", (unsigned long) bb_insn->bb->index); + MIR_output_insn (ctx, debug_file, bb_insn->insn, curr_func_item->u.func, FALSE); + } +#endif + if (!MIR_branch_code_p (insn->code) || insn->code == MIR_JMP || insn->code == MIR_SWITCH) { + ccp_make_insn_update (ctx, insn); + return; + } +#if MIR_GEN_DEBUG + if (debug_file != NULL) fprintf (debug_file, "\n"); +#endif + if ((ccp_res = ccp_branch_update (insn, &res)) == CCP_CONST) { + /* Remember about an edge to exit bb. First edge is always for + fall through and the 2nd edge is for jump bb. */ + gen_assert (DLIST_LENGTH (out_edge_t, bb->out_edges) >= 2); + e = res ? DLIST_EL (out_edge_t, bb->out_edges, 1) : DLIST_EL (out_edge_t, bb->out_edges, 0); + ccp_process_active_edge (ctx, e); + } else if (ccp_res == CCP_VARYING) { /* activate all edges */ + for (e = DLIST_HEAD (out_edge_t, bb->out_edges); e != NULL; e = DLIST_NEXT (out_edge_t, e)) + ccp_process_active_edge (ctx, e); + } +} + +static void ccp_process_bb (MIR_context_t ctx, bb_t bb) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + bb_insn_t bb_insn; + edge_t e; + +#if MIR_GEN_DEBUG + if (debug_file != NULL) + fprintf (debug_file, " processing bb%lu\n", (unsigned long) bb->index); +#endif + for (var_occ_t var_occ = DLIST_HEAD (var_occ_t, bb_start_occ_lists[bb->index]); var_occ != NULL; + var_occ = DLIST_NEXT (var_occ_t, var_occ)) + ccp_process_bb_start_var_occ (ctx, var_occ, bb, TRUE); + if (!bitmap_set_bit_p (bb_visited, bb->index)) return; + for (bb_insn = DLIST_HEAD (bb_insn_t, bb->bb_insns); bb_insn != NULL; + bb_insn = DLIST_NEXT (bb_insn_t, bb_insn)) { +#if MIR_GEN_DEBUG + if (debug_file != NULL) { + fprintf (debug_file, " processing insn: "); + MIR_output_insn (ctx, debug_file, bb_insn->insn, curr_func_item->u.func, FALSE); + } +#endif + ccp_make_insn_update (ctx, bb_insn->insn); + } + if ((bb_insn = DLIST_TAIL (bb_insn_t, bb->bb_insns)) == NULL + || !MIR_branch_code_p (bb_insn->insn->code) || bb_insn->insn->code == MIR_JMP + || bb_insn->insn->code == MIR_SWITCH) { + for (e = DLIST_HEAD (out_edge_t, bb->out_edges); e != NULL; e = DLIST_NEXT (out_edge_t, e)) { + gen_assert (!e->skipped_p); + if (!bitmap_bit_p (bb_visited, e->dst->index) && !e->dst->flag) { + e->dst->flag = TRUE; /* first process of dest which is not in ccp_bbs */ + VARR_PUSH (bb_t, ccp_bbs, e->dst); + } + ccp_process_active_edge (ctx, e); + } + } +} + +static void ccp_traverse (bb_t bb) { + bb->flag = TRUE; + for (edge_t e = DLIST_HEAD (out_edge_t, bb->out_edges); e != NULL; e = DLIST_NEXT (out_edge_t, e)) + if (!e->skipped_p && !e->dst->flag) + ccp_traverse (e->dst); /* visit unvisited active edge destination */ +} + +static int get_ccp_res_val (MIR_context_t ctx, MIR_insn_t insn, const_t *val) { + var_occ_t var_occ; + MIR_op_t op; + + if (MIR_call_code_p (insn->code) || !get_ccp_res_op (ctx, insn, 0, &op)) + return FALSE; /* call results always produce varying values */ + if (!var_insn_op_p (insn, 0)) return FALSE; + var_occ = op.data; + gen_assert (var_occ->def == NULL); + if (var_occ->val_kind != CCP_CONST) return FALSE; + *val = var_occ->val; + return TRUE; +} + +static int ccp_modify (MIR_context_t ctx) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + bb_t bb, next_bb; + bb_insn_t bb_insn, next_bb_insn; + const_t val; + MIR_op_t op; + MIR_insn_t insn, prev_insn, first_insn; + int res, change_p = FALSE; + +#ifndef NDEBUG + for (bb = DLIST_HEAD (bb_t, curr_cfg->bbs); bb != NULL; bb = DLIST_NEXT (bb_t, bb)) + gen_assert (!bb->flag); +#endif + ccp_traverse (DLIST_HEAD (bb_t, curr_cfg->bbs)); /* entry */ + for (bb = DLIST_HEAD (bb_t, curr_cfg->bbs); bb != NULL; bb = next_bb) { + next_bb = DLIST_NEXT (bb_t, bb); + if (!bb->flag) { + change_p = TRUE; +#if MIR_GEN_DEBUG + if (debug_file != NULL) + fprintf (debug_file, " deleting unreachable bb%lu and its edges\n", + (unsigned long) bb->index); +#endif + for (bb_insn = DLIST_HEAD (bb_insn_t, bb->bb_insns); bb_insn != NULL; + bb_insn = next_bb_insn) { + next_bb_insn = DLIST_NEXT (bb_insn_t, bb_insn); + insn = bb_insn->insn; + gen_delete_insn (ctx, insn); + } + delete_bb (ctx, bb); + continue; + } + bb->flag = FALSE; /* reset for the future use */ + for (bb_insn = DLIST_HEAD (bb_insn_t, bb->bb_insns); bb_insn != NULL; bb_insn = next_bb_insn) { + next_bb_insn = DLIST_NEXT (bb_insn_t, bb_insn); + if (get_ccp_res_val (ctx, bb_insn->insn, &val) + && (bb_insn->insn->code != MIR_MOV + || (bb_insn->insn->ops[1].mode != MIR_OP_INT + && bb_insn->insn->ops[1].mode != MIR_OP_UINT))) { + gen_assert (!MIR_call_code_p (bb_insn->insn->code)); + change_p = TRUE; +#if MIR_GEN_DEBUG + if (debug_file != NULL) { + fprintf (debug_file, " changing insn "); + MIR_output_insn (ctx, debug_file, bb_insn->insn, curr_func_item->u.func, FALSE); + } +#endif + op = val.uns_p ? MIR_new_uint_op (ctx, val.u.u) : MIR_new_int_op (ctx, val.u.i); +#ifndef NDEBUG + { + int out_p; + + MIR_insn_op_mode (ctx, bb_insn->insn, 0, &out_p); /* result here is always 0-th op */ + gen_assert (out_p); + } +#endif + insn = MIR_new_insn (ctx, MIR_MOV, bb_insn->insn->ops[0], op); + MIR_insert_insn_before (ctx, curr_func_item, bb_insn->insn, insn); + MIR_remove_insn (ctx, curr_func_item, bb_insn->insn); + insn->data = bb_insn; + bb_insn->insn = insn; +#if MIR_GEN_DEBUG + if (debug_file != NULL) { + fprintf (debug_file, " on insn "); + MIR_output_insn (ctx, debug_file, insn, curr_func_item->u.func, TRUE); + } +#endif + } + // nulify/free op.data ??? + } + if ((bb_insn = DLIST_TAIL (bb_insn_t, bb->bb_insns)) == NULL) continue; + insn = bb_insn->insn; + first_insn = DLIST_HEAD (bb_insn_t, bb->bb_insns)->insn; + if (first_insn->code == MIR_LABEL && (prev_insn = DLIST_PREV (MIR_insn_t, first_insn)) != NULL + && prev_insn->code == MIR_JMP && prev_insn->ops[0].u.label == first_insn) { +#if MIR_GEN_DEBUG + if (debug_file != NULL) { + fprintf (debug_file, " removing useless jump insn "); + MIR_output_insn (ctx, debug_file, prev_insn, curr_func_item->u.func, TRUE); + fprintf (debug_file, "\n"); + } +#endif + gen_delete_insn (ctx, prev_insn); + } + if (!MIR_branch_code_p (insn->code) || insn->code == MIR_JMP || insn->code == MIR_SWITCH + || ccp_branch_update (insn, &res) != CCP_CONST) + continue; + change_p = TRUE; + if (!res) { +#if MIR_GEN_DEBUG + if (debug_file != NULL) { + fprintf (debug_file, " removing branch insn "); + MIR_output_insn (ctx, debug_file, bb_insn->insn, curr_func_item->u.func, TRUE); + fprintf (debug_file, "\n"); + } +#endif + gen_delete_insn (ctx, insn); + delete_edge (DLIST_EL (out_edge_t, bb->out_edges, 1)); + } else { + insn = MIR_new_insn (ctx, MIR_JMP, bb_insn->insn->ops[0]); /* label is always 0-th op */ +#if MIR_GEN_DEBUG + if (debug_file != NULL) { + fprintf (debug_file, " changing branch insn "); + MIR_output_insn (ctx, debug_file, bb_insn->insn, curr_func_item->u.func, FALSE); + fprintf (debug_file, " onto jump insn "); + MIR_output_insn (ctx, debug_file, insn, curr_func_item->u.func, TRUE); + fprintf (debug_file, "\n"); + } +#endif + MIR_insert_insn_before (ctx, curr_func_item, bb_insn->insn, insn); + MIR_remove_insn (ctx, curr_func_item, bb_insn->insn); + insn->data = bb_insn; + bb_insn->insn = insn; + delete_edge (DLIST_EL (out_edge_t, bb->out_edges, 0)); + } + } + return change_p; +} + +static int ccp (MIR_context_t ctx) { /* conditional constant propagation */ + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + +#if MIR_GEN_DEBUG + if (debug_file != NULL) fprintf (debug_file, " CCP analysis:\n"); +#endif + build_var_occ_web (ctx); + bb_visited = temp_bitmap; + initiate_ccp_info (ctx); + while (VARR_LENGTH (bb_t, ccp_bbs) != 0 || VARR_LENGTH (var_occ_t, ccp_var_occs) != 0 + || VARR_LENGTH (bb_insn_t, ccp_insns) != 0) { + while (VARR_LENGTH (bb_t, ccp_bbs) != 0) { + bb_t bb = VARR_POP (bb_t, ccp_bbs); + + bb->flag = FALSE; + ccp_process_bb (ctx, bb); + } + while (VARR_LENGTH (var_occ_t, ccp_var_occs) != 0) { + var_occ_t var_occ = VARR_POP (var_occ_t, ccp_var_occs); + + var_occ->flag = FALSE; + gen_assert (var_occ->place.type == OCC_BB_START); + ccp_process_bb_start_var_occ (ctx, var_occ, var_occ->place.u.bb, FALSE); + } + while (VARR_LENGTH (bb_insn_t, ccp_insns) != 0) { + bb_insn_t bb_insn = VARR_POP (bb_insn_t, ccp_insns); + + gen_assert (bb_insn->flag); + bb_insn->flag = FALSE; + ccp_process_insn (ctx, bb_insn); + } + } +#if MIR_GEN_DEBUG + if (debug_file != NULL) fprintf (debug_file, " CCP modification:\n"); +#endif + return ccp_modify (ctx); +} + +static void ccp_clear (MIR_context_t ctx) { var_occs_clear (ctx); } + +static void init_ccp (MIR_context_t ctx) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + + gen_ctx->ccp_ctx = gen_malloc (ctx, sizeof (struct ccp_ctx)); + init_var_occs (ctx); + VARR_CREATE (bb_t, ccp_bbs, 256); + VARR_CREATE (var_occ_t, ccp_var_occs, 256); + VARR_CREATE (bb_insn_t, ccp_insns, 256); +} + +static void finish_ccp (MIR_context_t ctx) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + + finish_var_occs (ctx); + VARR_DESTROY (bb_t, ccp_bbs); + VARR_DESTROY (var_occ_t, ccp_var_occs); + VARR_DESTROY (bb_insn_t, ccp_insns); + free (gen_ctx->ccp_ctx); + gen_ctx->ccp_ctx = NULL; +} + +#undef live_in +#undef live_out + +/* New Page */ + +#define live_in in +#define live_out out +#define live_kill kill +#define live_gen gen + +/* Life analysis */ +static void live_con_func_0 (bb_t bb) { bitmap_clear (bb->live_in); } + +static int live_con_func_n (MIR_context_t ctx, bb_t bb) { + edge_t e; + int change_p = FALSE; + + for (e = DLIST_HEAD (out_edge_t, bb->out_edges); e != NULL; e = DLIST_NEXT (out_edge_t, e)) + change_p |= bitmap_ior (bb->live_out, bb->live_out, e->dst->live_in); + return change_p; +} + +static int live_trans_func (bb_t bb) { + return bitmap_ior_and_compl (bb->live_in, bb->live_gen, bb->live_out, bb->live_kill); +} + +static int bb_loop_level (bb_t bb) { + loop_node_t loop_node; + int level = -1; + + for (loop_node = bb->loop_node; loop_node->parent != NULL; loop_node = loop_node->parent) level++; + gen_assert (level >= 0); + return level; +} + +static size_t initiate_bb_live_info (MIR_context_t ctx, bb_t bb, int moves_p) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + MIR_insn_t insn; + size_t nops, i, niter, bb_freq, mvs_num = 0; + MIR_reg_t early_clobbered_hard_reg1, early_clobbered_hard_reg2; + MIR_op_t op; + int out_p; + mv_t mv; + reg_info_t *breg_infos; + + gen_assert (bb->live_in != NULL && bb->live_out != NULL && bb->live_gen != NULL + && bb->live_kill != NULL); + bitmap_clear (bb->live_in); + bitmap_clear (bb->live_out); + bitmap_clear (bb->live_gen); + bitmap_clear (bb->live_kill); + breg_infos = VARR_ADDR (reg_info_t, curr_cfg->breg_info); + bb_freq = 1; + if (moves_p) + for (int i = bb_loop_level (bb); i > 0; i--) bb_freq *= 5; + for (bb_insn_t bb_insn = DLIST_TAIL (bb_insn_t, bb->bb_insns); bb_insn != NULL; + bb_insn = DLIST_PREV (bb_insn_t, bb_insn)) { + insn = bb_insn->insn; + nops = MIR_insn_nops (ctx, insn); + if (MIR_call_code_p (insn->code)) { + bitmap_ior (bb->live_kill, bb->live_kill, call_used_hard_regs); + bitmap_and_compl (bb->live_gen, bb->live_gen, call_used_hard_regs); + } + /* Process output ops on 0-th iteration, then input ops. */ + for (niter = 0; niter <= 1; niter++) { + for (i = 0; i < nops; i++) { + op = insn->ops[i]; + MIR_insn_op_mode (ctx, insn, i, &out_p); + switch (op.mode) { + case MIR_OP_REG: + if (!out_p && niter != 0) + bitmap_set_bit_p (bb->live_gen, reg2var (gen_ctx, op.u.reg)); + else if (niter == 0) { + bitmap_clear_bit_p (bb->live_gen, reg2var (gen_ctx, op.u.reg)); + bitmap_set_bit_p (bb->live_kill, reg2var (gen_ctx, op.u.reg)); + } + breg_infos[reg2breg (gen_ctx, op.u.reg)].freq += bb_freq; + break; + case MIR_OP_HARD_REG: + if (!out_p && niter != 0) + bitmap_set_bit_p (bb->live_gen, op.u.hard_reg); + else if (niter == 0) { + bitmap_clear_bit_p (bb->live_gen, op.u.hard_reg); + bitmap_set_bit_p (bb->live_kill, op.u.hard_reg); + } + break; + case MIR_OP_MEM: + if (niter == 0) break; + if (op.u.mem.base != 0) { + bitmap_set_bit_p (bb->live_gen, reg2var (gen_ctx, op.u.mem.base)); + breg_infos[reg2breg (gen_ctx, op.u.mem.base)].freq += bb_freq; + } + if (op.u.mem.index != 0) { + bitmap_set_bit_p (bb->live_gen, reg2var (gen_ctx, op.u.mem.index)); + breg_infos[reg2breg (gen_ctx, op.u.mem.index)].freq += bb_freq; + } + break; + case MIR_OP_HARD_REG_MEM: + if (niter == 0) break; + if (op.u.hard_reg_mem.base != MIR_NON_HARD_REG) + bitmap_set_bit_p (bb->live_gen, op.u.hard_reg_mem.base); + if (op.u.hard_reg_mem.index != MIR_NON_HARD_REG) + bitmap_set_bit_p (bb->live_gen, op.u.hard_reg_mem.index); + break; + default: /* do nothing */ break; + } + } + } + get_early_clobbered_hard_reg (insn, &early_clobbered_hard_reg1, &early_clobbered_hard_reg2); + if (early_clobbered_hard_reg1 != MIR_NON_HARD_REG) { + bitmap_clear_bit_p (bb->live_gen, early_clobbered_hard_reg1); + bitmap_set_bit_p (bb->live_kill, early_clobbered_hard_reg1); + } + if (early_clobbered_hard_reg2 != MIR_NON_HARD_REG) { + bitmap_clear_bit_p (bb->live_gen, early_clobbered_hard_reg2); + bitmap_set_bit_p (bb->live_kill, early_clobbered_hard_reg2); + } + if (MIR_call_code_p (insn->code)) + bitmap_ior (bb->live_gen, bb->live_gen, bb_insn->call_hard_reg_args); + if (moves_p && move_p (insn)) { + mv = get_free_move (ctx); + mv->bb_insn = bb_insn; + mv->freq = bb_freq; + if (insn->ops[0].mode == MIR_OP_REG) + DLIST_APPEND (dst_mv_t, breg_infos[reg2breg (gen_ctx, insn->ops[0].u.reg)].dst_moves, mv); + if (insn->ops[1].mode == MIR_OP_REG) + DLIST_APPEND (src_mv_t, breg_infos[reg2breg (gen_ctx, insn->ops[1].u.reg)].src_moves, mv); + mvs_num++; +#if MIR_GEN_DEBUG + if (debug_file != NULL) { + fprintf (debug_file, " move with freq %10lu:", (unsigned long) mv->freq); + MIR_output_insn (ctx, debug_file, bb_insn->insn, curr_func_item->u.func, TRUE); + } +#endif + } + } + return mvs_num; +} + +static void initiate_live_info (MIR_context_t ctx, int moves_p) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + MIR_reg_t nregs, n; + mv_t mv, next_mv; + reg_info_t ri; + size_t mvs_num = 0; + + for (mv = DLIST_HEAD (mv_t, curr_cfg->used_moves); mv != NULL; mv = next_mv) { + next_mv = DLIST_NEXT (mv_t, mv); + free_move (ctx, mv); + } + VARR_TRUNC (reg_info_t, curr_cfg->breg_info, 0); + nregs = get_nregs (ctx); + for (n = 0; n < nregs; n++) { + ri.freq = ri.thread_freq = ri.calls_num = 0; + ri.thread_first = n; + ri.thread_next = MIR_MAX_REG_NUM; + DLIST_INIT (dst_mv_t, ri.dst_moves); + DLIST_INIT (src_mv_t, ri.src_moves); + VARR_PUSH (reg_info_t, curr_cfg->breg_info, ri); + } + for (bb_t bb = DLIST_HEAD (bb_t, curr_cfg->bbs); bb != NULL; bb = DLIST_NEXT (bb_t, bb)) + mvs_num += initiate_bb_live_info (ctx, bb, moves_p); + if (moves_p) curr_cfg->non_conflicting_moves = mvs_num; +} + +static void calculate_func_cfg_live_info (MIR_context_t ctx, int moves_p) { + initiate_live_info (ctx, moves_p); + solve_dataflow (ctx, FALSE, live_con_func_0, live_con_func_n, live_trans_func); +} + +static void add_bb_insn_dead_vars (MIR_context_t ctx) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + MIR_insn_t insn; + bb_insn_t bb_insn, prev_bb_insn; + size_t nops, i; + MIR_reg_t var, var2, early_clobbered_hard_reg1, early_clobbered_hard_reg2; + MIR_op_t op; + int out_p, live_start1_p, live_start2_p; + bitmap_t live; + + live = bitmap_create2 (DEFAULT_INIT_BITMAP_BITS_NUM); + for (bb_t bb = DLIST_HEAD (bb_t, curr_cfg->bbs); bb != NULL; bb = DLIST_NEXT (bb_t, bb)) { + bitmap_copy (live, bb->live_out); + for (bb_insn = DLIST_TAIL (bb_insn_t, bb->bb_insns); bb_insn != NULL; bb_insn = prev_bb_insn) { + prev_bb_insn = DLIST_PREV (bb_insn_t, bb_insn); + clear_bb_insn_dead_vars (bb_insn); + insn = bb_insn->insn; + nops = MIR_insn_nops (ctx, insn); + for (i = 0; i < nops; i++) { + op = insn->ops[i]; + MIR_insn_op_mode (ctx, insn, i, &out_p); + if (!out_p || (op.mode != MIR_OP_REG && op.mode != MIR_OP_HARD_REG)) continue; + var = op.mode == MIR_OP_HARD_REG ? op.u.hard_reg : reg2var (gen_ctx, op.u.reg); + bitmap_clear_bit_p (live, var); + } + if (MIR_call_code_p (insn->code)) bitmap_and_compl (live, live, call_used_hard_regs); + for (i = 0; i < nops; i++) { + op = insn->ops[i]; + MIR_insn_op_mode (ctx, insn, i, &out_p); + live_start1_p = live_start2_p = FALSE; + switch (op.mode) { + case MIR_OP_REG: + if (!out_p) live_start1_p = bitmap_set_bit_p (live, var = reg2var (gen_ctx, op.u.reg)); + break; + case MIR_OP_HARD_REG: + if (!out_p) live_start1_p = bitmap_set_bit_p (live, var = op.u.hard_reg); + break; + case MIR_OP_MEM: + if (op.u.mem.base != 0) + live_start1_p = bitmap_set_bit_p (live, var = reg2var (gen_ctx, op.u.mem.base)); + if (op.u.mem.index != 0) + live_start2_p = bitmap_set_bit_p (live, var2 = reg2var (gen_ctx, op.u.mem.index)); + break; + case MIR_OP_HARD_REG_MEM: + if (op.u.hard_reg_mem.base != MIR_NON_HARD_REG) + live_start1_p = bitmap_set_bit_p (live, var = op.u.hard_reg_mem.base); + if (op.u.hard_reg_mem.index != MIR_NON_HARD_REG) + live_start2_p = bitmap_set_bit_p (live, var2 = op.u.hard_reg_mem.index); + break; + default: break; + } + if (live_start1_p) add_bb_insn_dead_var (ctx, bb_insn, var); + if (live_start2_p) add_bb_insn_dead_var (ctx, bb_insn, var2); + } + get_early_clobbered_hard_reg (insn, &early_clobbered_hard_reg1, &early_clobbered_hard_reg2); + if (early_clobbered_hard_reg1 != MIR_NON_HARD_REG) + bitmap_clear_bit_p (live, early_clobbered_hard_reg1); + if (early_clobbered_hard_reg2 != MIR_NON_HARD_REG) + bitmap_clear_bit_p (live, early_clobbered_hard_reg2); + if (MIR_call_code_p (insn->code)) bitmap_ior (live, live, bb_insn->call_hard_reg_args); + } + } + bitmap_destroy (live); +} + +typedef struct live_range *live_range_t; /* vars */ + +struct live_range { + int start, finish; + live_range_t next; +}; + +DEF_VARR (live_range_t); + +struct lr_ctx { + int curr_point; + bitmap_t live_vars; + VARR (live_range_t) * var_live_ranges; +}; + +#define curr_point gen_ctx->lr_ctx->curr_point +#define live_vars gen_ctx->lr_ctx->live_vars +#define var_live_ranges gen_ctx->lr_ctx->var_live_ranges + +static live_range_t create_live_range (MIR_context_t ctx, int start, int finish, + live_range_t next) { + live_range_t lr = gen_malloc (ctx, sizeof (struct live_range)); + + gen_assert (finish < 0 || start <= finish); + lr->start = start; + lr->finish = finish; + lr->next = next; + return lr; +} + +static void destroy_live_range (live_range_t lr) { + live_range_t next_lr; + + for (; lr != NULL; lr = next_lr) { + next_lr = lr->next; + free (lr); + } +} + +static int make_var_dead (MIR_context_t ctx, MIR_reg_t var, int point) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + live_range_t lr; + + if (bitmap_clear_bit_p (live_vars, var)) { + lr = VARR_GET (live_range_t, var_live_ranges, var); + lr->finish = point; + } else { /* insn with unused result: result still needs a register */ + VARR_SET (live_range_t, var_live_ranges, var, + create_live_range (ctx, point, point, VARR_GET (live_range_t, var_live_ranges, var))); + } + return TRUE; +} + +static int make_var_live (MIR_context_t ctx, MIR_reg_t var, int point) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + live_range_t lr; + + if (!bitmap_set_bit_p (live_vars, var)) return FALSE; + if ((lr = VARR_GET (live_range_t, var_live_ranges, var)) == NULL + || (lr->finish != point && lr->finish + 1 != point)) + VARR_SET (live_range_t, var_live_ranges, var, create_live_range (ctx, point, -1, lr)); + return TRUE; +} + +static int make_reg_dead (MIR_context_t ctx, MIR_reg_t reg, int hard_reg_p, int point) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + + return make_var_dead (ctx, hard_reg_p ? reg : reg2var (gen_ctx, reg), point); +} + +static int make_reg_live (MIR_context_t ctx, MIR_reg_t reg, int hard_reg_p, int point) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + + return make_var_live (ctx, hard_reg_p ? reg : reg2var (gen_ctx, reg), point); +} + +static void make_live (size_t nb, void *data) { + MIR_context_t ctx = data; + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + + make_var_live ((MIR_context_t) data, nb, curr_point); +} +static void make_dead (size_t nb, void *data) { + MIR_context_t ctx = data; + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + + make_var_dead ((MIR_context_t) data, nb, curr_point); +} + +static void make_live_through_call (size_t nb, void *data) { + reg_info_t *bri; + MIR_reg_t breg; + MIR_context_t ctx = data; + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + + if (!var_is_reg_p (nb)) return; + breg = reg2breg (gen_ctx, var2reg (gen_ctx, nb)); + bri = &VARR_ADDR (reg_info_t, curr_cfg->breg_info)[breg]; + bri->calls_num++; +} + +#if MIR_GEN_DEBUG +static void print_live_ranges (MIR_context_t ctx) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + size_t i; + live_range_t lr; + + fprintf (debug_file, "+++++++++++++Live ranges:\n"); + gen_assert (get_nvars (ctx) == VARR_LENGTH (live_range_t, var_live_ranges)); + for (i = 0; i < VARR_LENGTH (live_range_t, var_live_ranges); i++) { + if ((lr = VARR_GET (live_range_t, var_live_ranges, i)) == NULL) continue; + fprintf (debug_file, "%lu", i); + if (var_is_reg_p (i)) + fprintf (debug_file, " (%s:%s)", + MIR_type_str (ctx, MIR_reg_type (ctx, var2reg (gen_ctx, i), curr_func_item->u.func)), + MIR_reg_name (ctx, var2reg (gen_ctx, i), curr_func_item->u.func)); + fprintf (debug_file, ":"); + for (; lr != NULL; lr = lr->next) fprintf (debug_file, " [%d..%d]", lr->start, lr->finish); + fprintf (debug_file, "\n"); + } +} +#endif + +static void build_live_ranges (MIR_context_t ctx) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + MIR_insn_t insn; + MIR_reg_t nvars, early_clobbered_hard_reg1, early_clobbered_hard_reg2; + size_t i, nops; + int incr_p, out_p; + MIR_op_t op; + + curr_point = 0; + nvars = get_nvars (ctx); + gen_assert (VARR_LENGTH (live_range_t, var_live_ranges) == 0); + for (i = 0; i < nvars; i++) VARR_PUSH (live_range_t, var_live_ranges, NULL); + for (bb_t bb = DLIST_HEAD (bb_t, curr_cfg->bbs); bb != NULL; bb = DLIST_NEXT (bb_t, bb)) { +#if MIR_GEN_DEBUG + if (debug_file != NULL) + fprintf (debug_file, " ------BB%u end: point=%d\n", (unsigned) bb->index, curr_point); +#endif + bitmap_clear (live_vars); + if (bb->live_out != NULL) bitmap_for_each (bb->live_out, make_live, ctx); + for (bb_insn_t bb_insn = DLIST_TAIL (bb_insn_t, bb->bb_insns); bb_insn != NULL; + bb_insn = DLIST_PREV (bb_insn_t, bb_insn)) { + insn = bb_insn->insn; +#if MIR_GEN_DEBUG + if (debug_file != NULL) { + fprintf (debug_file, " p%-5d", curr_point); + print_bb_insn (ctx, bb_insn, TRUE); + } +#endif + nops = MIR_insn_nops (ctx, insn); + incr_p = FALSE; + for (i = 0; i < nops; i++) { + op = insn->ops[i]; + MIR_insn_op_mode (ctx, insn, i, &out_p); + if (op.mode == MIR_OP_REG && out_p) + incr_p |= make_reg_dead (ctx, op.u.reg, FALSE, curr_point); + else if (op.mode == MIR_OP_HARD_REG && out_p) + incr_p |= make_reg_dead (ctx, op.u.hard_reg, TRUE, curr_point); + } + if (MIR_call_code_p (insn->code)) { + bitmap_for_each (call_used_hard_regs, make_dead, ctx); + bitmap_for_each (bb_insn->call_hard_reg_args, make_live, ctx); + bitmap_for_each (live_vars, make_live_through_call, ctx); + } + if (incr_p) curr_point++; + incr_p = FALSE; + for (i = 0; i < nops; i++) { + op = insn->ops[i]; + switch (op.mode) { + case MIR_OP_REG: + MIR_insn_op_mode (ctx, insn, i, &out_p); + if (!out_p) incr_p |= make_reg_live (ctx, op.u.reg, FALSE, curr_point); + break; + case MIR_OP_HARD_REG: + MIR_insn_op_mode (ctx, insn, i, &out_p); + if (!out_p) incr_p |= make_reg_live (ctx, op.u.hard_reg, TRUE, curr_point); + break; + case MIR_OP_MEM: + if (op.u.mem.base != 0) incr_p |= make_reg_live (ctx, op.u.mem.base, FALSE, curr_point); + if (op.u.mem.index != 0) incr_p |= make_reg_live (ctx, op.u.mem.index, FALSE, curr_point); + break; + case MIR_OP_HARD_REG_MEM: + if (op.u.hard_reg_mem.base != MIR_NON_HARD_REG) + incr_p |= make_reg_live (ctx, op.u.hard_reg_mem.base, TRUE, curr_point); + if (op.u.hard_reg_mem.index != MIR_NON_HARD_REG) + incr_p |= make_reg_live (ctx, op.u.hard_reg_mem.index, TRUE, curr_point); + break; + default: /* do nothing */ break; + } + } + get_early_clobbered_hard_reg (insn, &early_clobbered_hard_reg1, &early_clobbered_hard_reg2); + if (early_clobbered_hard_reg1 != MIR_NON_HARD_REG) { + incr_p |= make_reg_live (ctx, early_clobbered_hard_reg1, TRUE, curr_point); + incr_p |= make_reg_dead (ctx, early_clobbered_hard_reg1, TRUE, curr_point); + } + if (early_clobbered_hard_reg2 != MIR_NON_HARD_REG) { + incr_p |= make_reg_live (ctx, early_clobbered_hard_reg2, TRUE, curr_point); + incr_p |= make_reg_dead (ctx, early_clobbered_hard_reg2, TRUE, curr_point); + } + if (incr_p) curr_point++; + } + gen_assert (bitmap_equal_p (live_vars, bb->live_in)); + bitmap_for_each (live_vars, make_dead, ctx); + if (!bitmap_empty_p (bb->live_in)) curr_point++; + } +#if MIR_GEN_DEBUG + if (debug_file != NULL) print_live_ranges (ctx); +#endif +} + +static void destroy_func_live_ranges (MIR_context_t ctx) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + size_t i; + + for (i = 0; i < VARR_LENGTH (live_range_t, var_live_ranges); i++) + destroy_live_range (VARR_GET (live_range_t, var_live_ranges, i)); + VARR_TRUNC (live_range_t, var_live_ranges, 0); +} + +#if MIR_GEN_DEBUG +static void output_bb_live_info (MIR_context_t ctx, bb_t bb) { + output_bitmap (ctx, " live_in:", bb->live_in); + output_bitmap (ctx, " live_out:", bb->live_out); + output_bitmap (ctx, " live_gen:", bb->live_gen); + output_bitmap (ctx, " live_kill:", bb->live_kill); +} +#endif + +static void init_live_ranges (MIR_context_t ctx) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + + gen_ctx->lr_ctx = gen_malloc (ctx, sizeof (struct lr_ctx)); + VARR_CREATE (live_range_t, var_live_ranges, 0); + live_vars = bitmap_create2 (DEFAULT_INIT_BITMAP_BITS_NUM); +} + +static void finish_live_ranges (MIR_context_t ctx) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + + VARR_DESTROY (live_range_t, var_live_ranges); + bitmap_destroy (live_vars); + free (gen_ctx->lr_ctx); + gen_ctx->lr_ctx = NULL; +} + +#undef live_in +#undef live_out +#undef live_kill +#undef live_gen + +/* New Page */ + +/* Register allocation */ + +DEF_VARR (MIR_reg_t); +DEF_VARR (size_t); + +typedef struct breg_info { + MIR_reg_t breg; + reg_info_t *breg_infos; +} breg_info_t; + +DEF_VARR (breg_info_t); + +struct ra_ctx { + VARR (MIR_reg_t) * breg_renumber; + VARR (breg_info_t) * sorted_bregs; + VARR (bitmap_t) * point_used_locs; + bitmap_t conflict_locs; + reg_info_t *curr_breg_infos; + VARR (size_t) * loc_profits; + VARR (size_t) * loc_profit_ages; + size_t curr_age; + /* Slots num for variables. Some variable can take several slots. */ + size_t func_stack_slots_num; + bitmap_t func_assigned_hard_regs; +}; + +#define breg_renumber gen_ctx->ra_ctx->breg_renumber +#define sorted_bregs gen_ctx->ra_ctx->sorted_bregs +#define point_used_locs gen_ctx->ra_ctx->point_used_locs +#define conflict_locs gen_ctx->ra_ctx->conflict_locs +#define curr_breg_infos gen_ctx->ra_ctx->curr_breg_infos +#define loc_profits gen_ctx->ra_ctx->loc_profits +#define loc_profit_ages gen_ctx->ra_ctx->loc_profit_ages +#define curr_age gen_ctx->ra_ctx->curr_age +#define func_stack_slots_num gen_ctx->ra_ctx->func_stack_slots_num +#define func_assigned_hard_regs gen_ctx->ra_ctx->func_assigned_hard_regs + +static void process_move_to_form_thread (MIR_context_t ctx, mv_t mv) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + MIR_op_t op1 = mv->bb_insn->insn->ops[0], op2 = mv->bb_insn->insn->ops[1]; + MIR_reg_t i, breg1, breg2, breg1_first, breg2_first, last; + + if (op1.mode != MIR_OP_REG || op2.mode != MIR_OP_REG) return; + breg1 = reg2breg (gen_ctx, op1.u.reg); + breg2 = reg2breg (gen_ctx, op2.u.reg); + breg1_first = curr_breg_infos[breg1].thread_first; + breg2_first = curr_breg_infos[breg2].thread_first; + if (breg1_first != breg2_first) { + for (last = breg2_first; curr_breg_infos[last].thread_next != MIR_MAX_REG_NUM; + last = curr_breg_infos[last].thread_next) + curr_breg_infos[last].thread_first = breg1_first; + curr_breg_infos[last].thread_first = breg1_first; + curr_breg_infos[last].thread_next = curr_breg_infos[breg1_first].thread_next; + curr_breg_infos[breg1_first].thread_next = breg2_first; + curr_breg_infos[breg1_first].thread_freq += curr_breg_infos[breg2_first].thread_freq; + } + curr_breg_infos[breg1_first].thread_freq -= 2 * mv->freq; + gen_assert (curr_breg_infos[breg1_first].thread_freq >= 0); +} + +static int breg_info_compare_func (const void *a1, const void *a2) { + breg_info_t breg_info1 = *(const breg_info_t *) a1, breg_info2 = *(const breg_info_t *) a2; + MIR_reg_t breg1 = breg_info1.breg, breg2 = breg_info2.breg; + reg_info_t *breg_infos = breg_info1.breg_infos; + MIR_reg_t t1 = breg_infos[breg1].thread_first, t2 = breg_infos[breg2].thread_first; + long diff; + + gen_assert (breg_infos == breg_info2.breg_infos); + if ((diff = breg_infos[t2].thread_freq - breg_infos[t1].thread_freq) != 0) return diff; + if (t1 < t2) return -1; + if (t2 < t1) return 1; + if (breg_infos[breg2].live_length < breg_infos[breg1].live_length) return -1; + if (breg_infos[breg1].live_length < breg_infos[breg2].live_length) return 1; + return breg1 < breg2 ? -1 : 1; /* make sort stable */ +} + +static void setup_loc_profit_from_op (MIR_context_t ctx, MIR_op_t op, size_t freq) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + MIR_reg_t loc; + size_t *curr_loc_profits = VARR_ADDR (size_t, loc_profits); + size_t *curr_loc_profit_ages = VARR_ADDR (size_t, loc_profit_ages); + + if (op.mode == MIR_OP_HARD_REG) + loc = op.u.hard_reg; + else if ((loc = VARR_GET (MIR_reg_t, breg_renumber, reg2breg (gen_ctx, op.u.reg))) + == MIR_NON_HARD_REG) + return; + if (curr_loc_profit_ages[loc] == curr_age) + curr_loc_profits[loc] += freq; + else { + curr_loc_profit_ages[loc] = curr_age; + curr_loc_profits[loc] = freq; + } +} + +static void setup_loc_profits (MIR_context_t ctx, MIR_reg_t breg) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + mv_t mv; + reg_info_t *info = &curr_breg_infos[breg]; + + for (mv = DLIST_HEAD (dst_mv_t, info->dst_moves); mv != NULL; mv = DLIST_NEXT (dst_mv_t, mv)) + setup_loc_profit_from_op (ctx, mv->bb_insn->insn->ops[1], mv->freq); + for (mv = DLIST_HEAD (src_mv_t, info->src_moves); mv != NULL; mv = DLIST_NEXT (src_mv_t, mv)) + setup_loc_profit_from_op (ctx, mv->bb_insn->insn->ops[1], mv->freq); +} + +static void assign (MIR_context_t ctx) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + MIR_reg_t loc, best_loc, i, reg, breg, var, nregs = get_nregs (ctx); + MIR_type_t type; + int slots_num; + int j, k; + live_range_t lr; + bitmap_t bm; + size_t length, profit, best_profit; + bitmap_t *point_used_locs_addr; + breg_info_t breg_info; + + if (nregs == 0) return; + curr_breg_infos = VARR_ADDR (reg_info_t, curr_cfg->breg_info); + VARR_TRUNC (MIR_reg_t, breg_renumber, 0); + for (i = 0; i < nregs; i++) { + VARR_PUSH (MIR_reg_t, breg_renumber, MIR_NON_HARD_REG); + curr_breg_infos[i].thread_freq = curr_breg_infos[i].freq; + } + for (mv_t mv = DLIST_HEAD (mv_t, curr_cfg->used_moves); mv != NULL; mv = DLIST_NEXT (mv_t, mv)) + process_move_to_form_thread (ctx, mv); + /* min_reg, max_reg for func */ + VARR_TRUNC (breg_info_t, sorted_bregs, 0); + breg_info.breg_infos = curr_breg_infos; + for (i = 0; i < nregs; i++) { + breg_info.breg = i; + VARR_PUSH (breg_info_t, sorted_bregs, breg_info); + var = reg2var (gen_ctx, breg2reg (gen_ctx, i)); + for (length = 0, lr = VARR_GET (live_range_t, var_live_ranges, var); lr != NULL; lr = lr->next) + length += lr->finish - lr->start + 1; + curr_breg_infos[i].live_length = length; + } + VARR_TRUNC (size_t, loc_profits, 0); + VARR_TRUNC (size_t, loc_profit_ages, 0); + for (i = 0; i <= MAX_HARD_REG; i++) { + VARR_PUSH (size_t, loc_profits, 0); + VARR_PUSH (size_t, loc_profit_ages, 0); + } + VARR_TRUNC (bitmap_t, point_used_locs, 0); + for (i = 0; i <= curr_point; i++) { + bm = bitmap_create2 (MAX_HARD_REG + 1); + VARR_PUSH (bitmap_t, point_used_locs, bm); + } + qsort (VARR_ADDR (breg_info_t, sorted_bregs), nregs, sizeof (breg_info_t), + breg_info_compare_func); + curr_age = 0; + point_used_locs_addr = VARR_ADDR (bitmap_t, point_used_locs); + for (i = 0; i <= MAX_HARD_REG; i++) { + for (lr = VARR_GET (live_range_t, var_live_ranges, i); lr != NULL; lr = lr->next) + for (j = lr->start; j <= lr->finish; j++) bitmap_set_bit_p (point_used_locs_addr[j], i); + } + func_stack_slots_num = 0; + bitmap_clear (func_assigned_hard_regs); + for (i = 0; i < nregs; i++) { /* hard reg and stack slot assignment */ + breg = VARR_GET (breg_info_t, sorted_bregs, i).breg; + if (VARR_GET (MIR_reg_t, breg_renumber, breg) != MIR_NON_HARD_REG) continue; + reg = breg2reg (gen_ctx, breg); + var = reg2var (gen_ctx, reg); + bitmap_clear (conflict_locs); + for (lr = VARR_GET (live_range_t, var_live_ranges, var); lr != NULL; lr = lr->next) + for (j = lr->start; j <= lr->finish; j++) + bitmap_ior (conflict_locs, conflict_locs, point_used_locs_addr[j]); + curr_age++; + setup_loc_profits (ctx, breg); + best_loc = MIR_NON_HARD_REG; + best_profit = 0; + type = MIR_reg_type (ctx, reg, curr_func_item->u.func); + for (loc = 0; loc <= func_stack_slots_num + MAX_HARD_REG; loc++) { + if (loc <= MAX_HARD_REG && !hard_reg_type_ok_p (loc, type)) continue; + slots_num = locs_num (loc, type); + for (k = 0; k < slots_num; k++) + if ((loc + k <= MAX_HARD_REG + && (fixed_hard_reg_p (loc + k) + || (call_used_hard_reg_p (loc + k) && curr_breg_infos[breg].calls_num > 0))) + || bitmap_bit_p (conflict_locs, loc + k)) + break; + if (k < slots_num) continue; + if (loc > MAX_HARD_REG && loc % slots_num != 0) + continue; /* we align stack slots according to the type size */ + profit = (VARR_GET (size_t, loc_profit_ages, loc) != curr_age + ? 0 + : VARR_GET (size_t, loc_profits, loc)); + if (best_loc == MIR_NON_HARD_REG || best_profit < profit) { + best_loc = loc; + best_profit = profit; + } + if (best_loc != MIR_NON_HARD_REG && loc == MAX_HARD_REG) break; + } + slots_num = locs_num (best_loc, type); + if (best_loc <= MAX_HARD_REG) { + for (k = 0; k < slots_num; k++) bitmap_set_bit_p (func_assigned_hard_regs, best_loc + k); + } else if (best_loc == MIR_NON_HARD_REG) { /* Add stack slot ??? */ + for (k = 0; k < slots_num; k++) { + best_loc = VARR_LENGTH (size_t, loc_profits); + VARR_PUSH (size_t, loc_profits, 0); + VARR_PUSH (size_t, loc_profit_ages, 0); + } + func_stack_slots_num = best_loc - MAX_HARD_REG; + best_loc -= slots_num - 1; + } +#if MIR_GEN_DEBUG + if (debug_file != NULL) { + MIR_reg_t thread_breg = curr_breg_infos[breg].thread_first; + + fprintf (debug_file, + " Assigning to %s:var=%3u, breg=%3u (freq %-3ld), thread breg=%3u (freq %-3ld) -- " + "%lu\n", + MIR_reg_name (ctx, reg, curr_func_item->u.func), reg2var (gen_ctx, reg), breg, + curr_breg_infos[breg].freq, thread_breg, curr_breg_infos[thread_breg].thread_freq, + (unsigned long) best_loc); + } +#endif + VARR_SET (MIR_reg_t, breg_renumber, breg, best_loc); + for (lr = VARR_GET (live_range_t, var_live_ranges, var); lr != NULL; lr = lr->next) + for (j = lr->start; j <= lr->finish; j++) + for (k = 0; k < slots_num; k++) bitmap_set_bit_p (point_used_locs_addr[j], best_loc + k); + } + for (i = 0; i <= curr_point; i++) bitmap_destroy (VARR_POP (bitmap_t, point_used_locs)); +#if MIR_GEN_DEBUG + if (debug_file != NULL) { + fprintf (debug_file, "+++++++++++++Disposition after assignment:"); + for (i = 0; i < nregs; i++) { + if (i % 8 == 0) fprintf (debug_file, "\n"); + reg = breg2reg (gen_ctx, i); + fprintf (debug_file, " %3u=>%-2u", reg2var (gen_ctx, reg), + VARR_GET (MIR_reg_t, breg_renumber, i)); + } + fprintf (debug_file, "\n"); + } +#endif +} + +static MIR_reg_t change_reg (MIR_context_t ctx, MIR_op_t *mem_op, MIR_reg_t reg, + MIR_op_mode_t data_mode, int first_p, bb_insn_t bb_insn, int out_p) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + MIR_reg_t loc = VARR_GET (MIR_reg_t, breg_renumber, reg2breg (gen_ctx, reg)); + MIR_reg_t hard_reg; + MIR_disp_t offset; + MIR_insn_code_t code; + MIR_type_t type; + MIR_insn_t insn; + bb_insn_t new_bb_insn; + MIR_op_t hard_reg_op; + + gen_assert (loc != MIR_NON_HARD_REG); + if (loc <= MAX_HARD_REG) return loc; + gen_assert (data_mode == MIR_OP_INT || data_mode == MIR_OP_FLOAT || data_mode == MIR_OP_DOUBLE + || data_mode == MIR_OP_LDOUBLE); + if (data_mode == MIR_OP_INT) { + type = MIR_T_I64; + code = MIR_MOV; + hard_reg = first_p ? TEMP_INT_HARD_REG1 : TEMP_INT_HARD_REG2; + } else if (data_mode == MIR_OP_FLOAT) { + type = MIR_T_F; + code = MIR_FMOV; + hard_reg = first_p ? TEMP_FLOAT_HARD_REG1 : TEMP_FLOAT_HARD_REG2; + } else if (data_mode == MIR_OP_DOUBLE) { + type = MIR_T_D; + code = MIR_DMOV; + hard_reg = first_p ? TEMP_DOUBLE_HARD_REG1 : TEMP_DOUBLE_HARD_REG2; + } else { + type = MIR_T_LD; + code = MIR_LDMOV; + hard_reg = first_p ? TEMP_LDOUBLE_HARD_REG1 : TEMP_LDOUBLE_HARD_REG2; + } + offset = get_stack_slot_offset (ctx, type, loc - MAX_HARD_REG - 1); + *mem_op = _MIR_new_hard_reg_mem_op (ctx, type, offset, BP_HARD_REG, MIR_NON_HARD_REG, 0); + if (hard_reg == MIR_NON_HARD_REG) return hard_reg; + hard_reg_op = _MIR_new_hard_reg_op (ctx, hard_reg); + if (out_p) { + insn = MIR_new_insn (ctx, code, *mem_op, hard_reg_op); + MIR_insert_insn_after (ctx, curr_func_item, bb_insn->insn, insn); + } else { + insn = MIR_new_insn (ctx, code, hard_reg_op, *mem_op); + MIR_insert_insn_before (ctx, curr_func_item, bb_insn->insn, insn); + } + new_bb_insn = create_bb_insn (ctx, insn, bb_insn->bb); + if (out_p) + DLIST_INSERT_AFTER (bb_insn_t, bb_insn->bb->bb_insns, bb_insn, new_bb_insn); + else + DLIST_INSERT_BEFORE (bb_insn_t, bb_insn->bb->bb_insns, bb_insn, new_bb_insn); + return hard_reg; +} + +static void rewrite (MIR_context_t ctx) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + MIR_insn_t insn; + bb_insn_t bb_insn, next_bb_insn; + size_t nops, i; + MIR_op_t *op, in_op, out_op, mem_op; + MIR_mem_t mem; + MIR_op_mode_t data_mode; + MIR_reg_t hard_reg; + int out_p, first_in_p; + size_t insns_num = 0, movs_num = 0, deleted_movs_num = 0; + + for (bb_t bb = DLIST_HEAD (bb_t, curr_cfg->bbs); bb != NULL; bb = DLIST_NEXT (bb_t, bb)) { + for (bb_insn = DLIST_HEAD (bb_insn_t, bb->bb_insns); bb_insn != NULL; bb_insn = next_bb_insn) { + next_bb_insn = DLIST_NEXT (bb_insn_t, bb_insn); + insn = bb_insn->insn; + nops = MIR_insn_nops (ctx, insn); + first_in_p = TRUE; + for (i = 0; i < nops; i++) { + op = &insn->ops[i]; + data_mode = MIR_insn_op_mode (ctx, insn, i, &out_p); + if (out_p) + out_op = *op; /* we don't care about multiple call outputs here */ + else + in_op = *op; + switch (op->mode) { + case MIR_OP_REG: + hard_reg + = change_reg (ctx, &mem_op, op->u.reg, data_mode, out_p || first_in_p, bb_insn, out_p); + if (!out_p) first_in_p = FALSE; + if (hard_reg == MIR_NON_HARD_REG) { + *op = mem_op; + } else { + op->mode = MIR_OP_HARD_REG; + op->u.hard_reg = hard_reg; + } + break; + case MIR_OP_MEM: + mem = op->u.mem; + /* Always second for mov MEM[R2], R1 or mov R1, MEM[R2]. */ + if (op->u.mem.base == 0) { + mem.base = MIR_NON_HARD_REG; + } else { + mem.base = change_reg (ctx, &mem_op, op->u.mem.base, MIR_OP_INT, FALSE, bb_insn, FALSE); + gen_assert (mem.base != MIR_NON_HARD_REG); /* we can always use GP regs */ + } + gen_assert (op->u.mem.index == 0); + mem.index = MIR_NON_HARD_REG; + op->mode = MIR_OP_HARD_REG_MEM; + op->u.hard_reg_mem = mem; + break; + default: /* do nothing */ break; + } + } + insns_num++; + if (insn->code == MIR_MOV || insn->code == MIR_FMOV || insn->code == MIR_DMOV + || insn->code == MIR_LDMOV) { + movs_num++; + if (MIR_op_eq_p (ctx, insn->ops[0], insn->ops[1])) { +#if MIR_GEN_DEBUG + if (debug_file != NULL) { + fprintf (debug_file, "Deleting noop move "); + MIR_output_insn (ctx, debug_file, insn, curr_func_item->u.func, FALSE); + fprintf (debug_file, " which was "); + insn->ops[0] = out_op; + insn->ops[1] = in_op; + MIR_output_insn (ctx, debug_file, insn, curr_func_item->u.func, TRUE); + } +#endif + deleted_movs_num++; + gen_delete_insn (ctx, insn); + } + } + } + } +#if MIR_GEN_DEBUG + if (debug_file != NULL) + fprintf (debug_file, + "Deleting moves: %lu deleted noop moves out of %lu non-conflicting moves (%.1f%%), " + "out of %lu all moves (%.1f), out of %lu all insns (%.1f)\n", + (unsigned long) deleted_movs_num, (unsigned long) curr_cfg->non_conflicting_moves, + deleted_movs_num * 100.0 / curr_cfg->non_conflicting_moves, (unsigned long) movs_num, + deleted_movs_num * 100.0 / movs_num, (unsigned long) insns_num, + deleted_movs_num * 100.0 / insns_num); +#endif +} + +static void init_ra (MIR_context_t ctx) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + + gen_ctx->ra_ctx = gen_malloc (ctx, sizeof (struct ra_ctx)); + VARR_CREATE (MIR_reg_t, breg_renumber, 0); + VARR_CREATE (breg_info_t, sorted_bregs, 0); + VARR_CREATE (bitmap_t, point_used_locs, 0); + VARR_CREATE (size_t, loc_profits, 0); + VARR_CREATE (size_t, loc_profit_ages, 0); + conflict_locs = bitmap_create2 (3 * MAX_HARD_REG / 2); + func_assigned_hard_regs = bitmap_create2 (MAX_HARD_REG + 1); +} + +static void finish_ra (MIR_context_t ctx) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + + VARR_DESTROY (MIR_reg_t, breg_renumber); + VARR_DESTROY (breg_info_t, sorted_bregs); + VARR_DESTROY (bitmap_t, point_used_locs); + VARR_DESTROY (size_t, loc_profits); + VARR_DESTROY (size_t, loc_profit_ages); + bitmap_destroy (conflict_locs); + bitmap_destroy (func_assigned_hard_regs); + free (gen_ctx->ra_ctx); + gen_ctx->ra_ctx = NULL; +} + +/* New Page */ + +/* Code selection */ + +struct hreg_ref { /* We keep track of the last hard reg ref in this struct. */ + MIR_insn_t insn; + size_t insn_num; + size_t nop; + char def_p, del_p; /* def/use and deleted */ +}; + +typedef struct hreg_ref hreg_ref_t; + +DEF_VARR (hreg_ref_t); +DEF_VARR (MIR_op_t); + +struct selection_ctx { + VARR (size_t) * hreg_ref_ages; + VARR (hreg_ref_t) * hreg_refs; + hreg_ref_t *hreg_refs_addr; + size_t *hreg_ref_ages_addr; + size_t curr_bb_hreg_ref_age; + size_t last_mem_ref_insn_num; + VARR (MIR_reg_t) * insn_hard_regs; /* registers considered for substitution */ + VARR (size_t) * changed_op_numbers; + VARR (MIR_op_t) * last_right_ops; + bitmap_t hard_regs_bitmap; +}; + +#define hreg_ref_ages gen_ctx->selection_ctx->hreg_ref_ages +#define hreg_refs gen_ctx->selection_ctx->hreg_refs +#define hreg_refs_addr gen_ctx->selection_ctx->hreg_refs_addr +#define hreg_ref_ages_addr gen_ctx->selection_ctx->hreg_ref_ages_addr +#define curr_bb_hreg_ref_age gen_ctx->selection_ctx->curr_bb_hreg_ref_age +#define last_mem_ref_insn_num gen_ctx->selection_ctx->last_mem_ref_insn_num +#define insn_hard_regs gen_ctx->selection_ctx->insn_hard_regs +#define changed_op_numbers gen_ctx->selection_ctx->changed_op_numbers +#define last_right_ops gen_ctx->selection_ctx->last_right_ops +#define hard_regs_bitmap gen_ctx->selection_ctx->hard_regs_bitmap + +static MIR_insn_code_t commutative_insn_code (MIR_insn_code_t insn_code) { + switch (insn_code) { + case MIR_ADD: + case MIR_ADDS: + case MIR_FADD: + case MIR_DADD: + case MIR_LDADD: + case MIR_MUL: + case MIR_MULS: + case MIR_FMUL: + case MIR_DMUL: + case MIR_LDMUL: + case MIR_AND: + case MIR_OR: + case MIR_XOR: + case MIR_ANDS: + case MIR_ORS: + case MIR_XORS: + case MIR_EQ: + case MIR_EQS: + case MIR_FEQ: + case MIR_DEQ: + case MIR_LDEQ: + case MIR_NE: + case MIR_NES: + case MIR_FNE: + case MIR_DNE: + case MIR_LDNE: + case MIR_BEQ: + case MIR_BEQS: + case MIR_FBEQ: + case MIR_DBEQ: + case MIR_LDBEQ: + case MIR_BNE: + case MIR_BNES: + case MIR_FBNE: + case MIR_DBNE: + case MIR_LDBNE: return insn_code; break; + case MIR_LT: return MIR_GT; + case MIR_LTS: return MIR_GTS; + case MIR_ULT: return MIR_UGT; + case MIR_ULTS: return MIR_UGTS; + case MIR_LE: return MIR_GE; + case MIR_LES: return MIR_GES; + case MIR_ULE: return MIR_UGE; + case MIR_ULES: return MIR_UGES; + case MIR_GT: return MIR_LT; + case MIR_GTS: return MIR_LTS; + case MIR_UGT: return MIR_ULT; + case MIR_UGTS: return MIR_ULTS; + case MIR_GE: return MIR_LE; + case MIR_GES: return MIR_LES; + case MIR_UGE: return MIR_ULE; + case MIR_UGES: return MIR_ULES; + case MIR_BLT: return MIR_BGT; + case MIR_BLTS: return MIR_BGTS; + case MIR_UBLT: return MIR_UBGT; + case MIR_UBLTS: return MIR_UBGTS; + case MIR_BLE: return MIR_BGE; + case MIR_BLES: return MIR_BGES; + case MIR_UBLE: return MIR_UBGE; + case MIR_UBLES: return MIR_UBGES; + case MIR_BGT: return MIR_BLT; + case MIR_BGTS: return MIR_BLTS; + case MIR_UBGT: return MIR_UBLT; + case MIR_UBGTS: return MIR_UBLTS; + case MIR_BGE: return MIR_BLE; + case MIR_BGES: return MIR_BLES; + case MIR_UBGE: return MIR_UBLE; + case MIR_UBGES: return MIR_UBLES; + case MIR_FLT: return MIR_FGT; + case MIR_DLT: return MIR_DGT; + case MIR_LDLT: return MIR_LDGT; + case MIR_FLE: return MIR_FGE; + case MIR_DLE: return MIR_DGE; + case MIR_LDLE: return MIR_LDGE; + case MIR_FGT: return MIR_FLT; + case MIR_DGT: return MIR_DLT; + case MIR_LDGT: return MIR_LDLT; + case MIR_FGE: return MIR_FLE; + case MIR_DGE: return MIR_DLE; + case MIR_LDGE: return MIR_LDLE; + case MIR_FBLT: return MIR_FBGT; + case MIR_DBLT: return MIR_DBGT; + case MIR_LDBLT: return MIR_LDBGT; + case MIR_FBLE: return MIR_FBGE; + case MIR_DBLE: return MIR_DBGE; + case MIR_LDBLE: return MIR_LDBGE; + case MIR_FBGT: return MIR_FBLT; + case MIR_DBGT: return MIR_DBLT; + case MIR_LDBGT: return MIR_LDBLT; + case MIR_FBGE: return MIR_FBLE; + case MIR_DBGE: return MIR_DBLE; + case MIR_LDBGE: return MIR_LDBLE; + default: return MIR_INSN_BOUND; + } +} + +static int obsolete_hard_reg_p (MIR_context_t ctx, MIR_reg_t hard_reg, size_t def_insn_num) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + + return (hreg_ref_ages_addr[hard_reg] == curr_bb_hreg_ref_age + && hreg_refs_addr[hard_reg].insn_num > def_insn_num); +} + +static int obsolete_hard_reg_op_p (MIR_context_t ctx, MIR_op_t op, size_t def_insn_num) { + return op.mode == MIR_OP_HARD_REG && obsolete_hard_reg_p (ctx, op.u.hard_reg, def_insn_num); +} + +static int obsolete_op_p (MIR_context_t ctx, MIR_op_t op, size_t def_insn_num) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + + if (obsolete_hard_reg_op_p (ctx, op, def_insn_num)) return TRUE; + if (op.mode != MIR_OP_HARD_REG_MEM) return FALSE; + if (op.u.hard_reg_mem.base != MIR_NON_HARD_REG + && obsolete_hard_reg_p (ctx, op.u.hard_reg_mem.base, def_insn_num)) + return TRUE; + if (op.u.hard_reg_mem.index != MIR_NON_HARD_REG + && obsolete_hard_reg_p (ctx, op.u.hard_reg_mem.index, def_insn_num)) + return TRUE; + return last_mem_ref_insn_num > def_insn_num; +} + +static int safe_hreg_substitution_p (MIR_context_t ctx, MIR_reg_t hr, bb_insn_t bb_insn) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + + return (hr != MIR_NON_HARD_REG + && hreg_ref_ages_addr[hr] == curr_bb_hreg_ref_age + /* It is not safe to substitute if there is another use after def insn before + the current insn as we delete def insn after the substitution. */ + && hreg_refs_addr[hr].def_p && find_bb_insn_dead_var (bb_insn, hr) != NULL); +} + +static void combine_process_hard_reg (MIR_context_t ctx, MIR_reg_t hr, bb_insn_t bb_insn) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + + if (!safe_hreg_substitution_p (ctx, hr, bb_insn) || !bitmap_set_bit_p (hard_regs_bitmap, hr)) + return; + VARR_PUSH (MIR_reg_t, insn_hard_regs, hr); +} + +static void combine_process_op (MIR_context_t ctx, const MIR_op_t *op_ref, bb_insn_t bb_insn) { + if (op_ref->mode == MIR_OP_HARD_REG) { + combine_process_hard_reg (ctx, op_ref->u.hard_reg, bb_insn); + } else if (op_ref->mode == MIR_OP_HARD_REG_MEM) { + if (op_ref->u.hard_reg_mem.base != MIR_NON_HARD_REG) + combine_process_hard_reg (ctx, op_ref->u.hard_reg_mem.base, bb_insn); + if (op_ref->u.hard_reg_mem.index != MIR_NON_HARD_REG) + combine_process_hard_reg (ctx, op_ref->u.hard_reg_mem.index, bb_insn); + } +} + +static void combine_delete_insn (MIR_context_t ctx, MIR_insn_t def_insn, bb_insn_t bb_insn) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + MIR_reg_t hr; + + gen_assert (def_insn->ops[0].mode == MIR_OP_HARD_REG); + hr = def_insn->ops[0].u.hard_reg; + if (hreg_ref_ages_addr[hr] != curr_bb_hreg_ref_age || hreg_refs_addr[hr].del_p) return; +#if MIR_GEN_DEBUG + // deleted_insns_num++; + if (debug_file != NULL) { + fprintf (debug_file, " deleting now dead insn "); + print_bb_insn (ctx, def_insn->data, TRUE); + } +#endif + remove_bb_insn_dead_var (bb_insn, hr); + move_bb_insn_dead_vars (bb_insn, def_insn->data); + /* We should delete the def insn here because of possible + substitution of the def insn 'r0 = ... r0 ...'. We still + need valid entry for def here to find obsolete definiton, + e.g. "hr1 = hr0; hr0 = ...; hr0 = ... (deleted); ...= ...hr1..." */ + gen_delete_insn (ctx, def_insn); + hreg_refs_addr[hr].del_p = TRUE; /* to exclude repetitive deletion */ +} + +static int64_t power2 (int64_t p) { + int64_t n = 1; + + if (p < 0) return 0; + while (p-- > 0) n *= 2; + return n; +} + +static int64_t int_log2 (int64_t i) { + int64_t n; + + for (n = 0; (i & 1) == 0; n++, i >>= 1) + ; + return i == 1 ? n : -1; +} + +static int combine_substitute (MIR_context_t ctx, bb_insn_t bb_insn) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + MIR_insn_code_t code, new_code; + MIR_insn_t insn = bb_insn->insn, def_insn, new_insn; + size_t i, nops = insn->nops; + int out_p, insn_change_p, insn_hr_change_p, op_change_p, mem_reg_change_p, success_p; + MIR_op_t *op_ref, *src_op_ref, *src_op2_ref; + MIR_reg_t hr; + int64_t scale, sh; + + if (nops == 0) return FALSE; + VARR_TRUNC (MIR_op_t, last_right_ops, 0); + for (i = 0; i < nops; i++) VARR_PUSH (MIR_op_t, last_right_ops, insn->ops[i]); + VARR_TRUNC (MIR_reg_t, insn_hard_regs, 0); + bitmap_clear (hard_regs_bitmap); + for (i = 0; i < nops; i++) { + MIR_insn_op_mode (ctx, insn, i, &out_p); + if (out_p && insn->ops[i].mode != MIR_OP_HARD_REG_MEM) continue; + combine_process_op (ctx, &insn->ops[i], bb_insn); + } + insn_change_p = FALSE; + while (VARR_LENGTH (MIR_reg_t, insn_hard_regs) != 0) { + hr = VARR_POP (MIR_reg_t, insn_hard_regs); + if (!hreg_refs_addr[hr].def_p) continue; + gen_assert (!hreg_refs_addr[hr].del_p); + def_insn = hreg_refs_addr[hr].insn; + if (obsolete_op_p (ctx, def_insn->ops[1], hreg_refs_addr[hr].insn_num)) + continue; /* hr0 = ... hr1 ...; ...; hr1 = ...; ...; insn */ + insn_hr_change_p = FALSE; + for (i = 0; i < nops; i++) { /* Change all hr occurences: */ + op_ref = &insn->ops[i]; + op_change_p = FALSE; + MIR_insn_op_mode (ctx, insn, i, &out_p); + if (!out_p && op_ref->mode == MIR_OP_HARD_REG && op_ref->u.hard_reg == hr) { + if (def_insn->code != MIR_MOV && def_insn->code != MIR_FMOV && def_insn->code != MIR_DMOV + && def_insn->code != MIR_LDMOV) + break; + /* It is not safe to substitute if there is another use after def insn before + the current as we delete def insn after substitution. */ + insn->ops[i] = def_insn->ops[1]; + insn_hr_change_p = op_change_p = TRUE; + } else if (op_ref->mode == MIR_OP_HARD_REG_MEM) { + src_op_ref = &def_insn->ops[1]; + if (op_ref->u.hard_reg_mem.index == hr) { + mem_reg_change_p = FALSE; + if (src_op_ref->mode != MIR_OP_HARD_REG) { + } else if (def_insn->code == MIR_MOV) { /* index = r */ + insn->ops[i].u.hard_reg_mem.index = src_op_ref->u.hard_reg; + mem_reg_change_p = op_change_p = insn_hr_change_p = TRUE; + } else if (def_insn->code == MIR_ADD) { /* index = r + const */ + gen_assert (src_op_ref->u.hard_reg != MIR_NON_HARD_REG); + if ((src_op2_ref = &def_insn->ops[2])->mode == MIR_OP_INT) { + insn->ops[i].u.hard_reg_mem.index = src_op_ref->u.hard_reg; + insn->ops[i].u.hard_reg_mem.disp + += src_op2_ref->u.i * insn->ops[i].u.hard_reg_mem.scale; + mem_reg_change_p = op_change_p = insn_hr_change_p = TRUE; + } + } else if ((def_insn->code == MIR_MUL || def_insn->code == MIR_LSH) + && op_ref->u.mem.scale >= 1 && op_ref->u.mem.scale <= MIR_MAX_SCALE + && (src_op2_ref = &def_insn->ops[2])->mode == MIR_OP_INT) { + scale = def_insn->code == MIR_MUL ? src_op2_ref->u.i : power2 (src_op2_ref->u.i); + if (scale >= 1 && scale <= MIR_MAX_SCALE + && insn->ops[i].u.hard_reg_mem.scale * scale <= MIR_MAX_SCALE) { + /* index = r * const */ + gen_assert (src_op_ref->u.hard_reg != MIR_NON_HARD_REG); + insn->ops[i].u.hard_reg_mem.index = src_op_ref->u.hard_reg; + insn->ops[i].u.hard_reg_mem.scale *= scale; + mem_reg_change_p = op_change_p = insn_hr_change_p = TRUE; + } + } + if (!mem_reg_change_p) break; + } + if (op_ref->u.hard_reg_mem.base == hr) { + mem_reg_change_p = FALSE; + op_ref = &insn->ops[i]; + if (def_insn->code == MIR_MOV) { + if (src_op_ref->mode == MIR_OP_HARD_REG) { /* base = r */ + insn->ops[i].u.hard_reg_mem.base = src_op_ref->u.hard_reg; + mem_reg_change_p = op_change_p = insn_hr_change_p = TRUE; + } else if (src_op_ref->mode == MIR_OP_INT) { /* base = const */ + if (insn->ops[i].u.hard_reg_mem.scale != 1) { + insn->ops[i].u.hard_reg_mem.base = MIR_NON_HARD_REG; + } else { + insn->ops[i].u.hard_reg_mem.base = insn->ops[i].u.hard_reg_mem.index; + insn->ops[i].u.hard_reg_mem.index = MIR_NON_HARD_REG; + } + insn->ops[i].u.hard_reg_mem.disp += src_op_ref->u.i; + mem_reg_change_p = op_change_p = insn_hr_change_p = TRUE; + } + } else if (src_op_ref->mode != MIR_OP_HARD_REG) { /* Can do nothing */ + ; + } else if (def_insn->code == MIR_ADD) { + gen_assert (src_op_ref->u.hard_reg != MIR_NON_HARD_REG); + src_op2_ref = &def_insn->ops[2]; + if (src_op2_ref->mode == MIR_OP_HARD_REG + && op_ref->u.hard_reg_mem.index == MIR_NON_HARD_REG) { /* base = r1 + r2 */ + insn->ops[i].u.hard_reg_mem.base = src_op_ref->u.hard_reg; + insn->ops[i].u.hard_reg_mem.index = src_op2_ref->u.hard_reg; + insn->ops[i].u.hard_reg_mem.scale = 1; + mem_reg_change_p = op_change_p = insn_hr_change_p = TRUE; + } else if (src_op2_ref->mode == MIR_OP_INT) { /* base = r + const */ + insn->ops[i].u.hard_reg_mem.base = src_op_ref->u.hard_reg; + insn->ops[i].u.hard_reg_mem.disp += src_op2_ref->u.i; + mem_reg_change_p = op_change_p = insn_hr_change_p = TRUE; + } + } else if (def_insn->code == MIR_MUL && op_ref->u.hard_reg_mem.index == MIR_NON_HARD_REG + && (src_op2_ref = &def_insn->ops[2])->mode == MIR_OP_INT + && src_op2_ref->u.i >= 1 + && src_op2_ref->u.i <= MIR_MAX_SCALE) { /* base = r * const */ + gen_assert (src_op_ref->u.hard_reg != MIR_NON_HARD_REG && src_op2_ref->u.i != 1); + insn->ops[i].u.hard_reg_mem.base = MIR_NON_HARD_REG; + insn->ops[i].u.hard_reg_mem.index = src_op_ref->u.hard_reg; + insn->ops[i].u.hard_reg_mem.scale = src_op2_ref->u.i; + mem_reg_change_p = op_change_p = insn_hr_change_p = TRUE; + } + if (!mem_reg_change_p) { + if (op_change_p) VARR_PUSH (size_t, changed_op_numbers, i); /* index was changed */ + break; + } + } + } + if (op_change_p) VARR_PUSH (size_t, changed_op_numbers, i); + } + code = insn->code; + if (i >= nops && (code == MIR_MUL || code == MIR_MULS || code == MIR_UDIV || code == MIR_UDIVS) + && insn->ops[2].mode == MIR_OP_INT && (sh = int_log2 (insn->ops[2].u.i)) >= 0) { + switch (code) { + case MIR_MUL: new_code = MIR_LSH; break; + case MIR_MULS: new_code = MIR_LSHS; break; + case MIR_UDIV: new_code = MIR_URSH; break; + case MIR_UDIVS: new_code = MIR_URSHS; break; + default: gen_assert (FALSE); + } + new_insn = MIR_new_insn (ctx, new_code, insn->ops[0], insn->ops[1], MIR_new_int_op (ctx, sh)); + MIR_insert_insn_after (ctx, curr_func_item, insn, new_insn); + if (insn_ok_p (ctx, new_insn)) { + insn->code = new_insn->code; + insn->ops[0] = new_insn->ops[0]; + insn->ops[1] = new_insn->ops[1]; + insn->ops[2] = new_insn->ops[2]; + } + MIR_remove_insn (ctx, curr_func_item, new_insn); + insn_hr_change_p = TRUE; + } + if (insn_hr_change_p) { + if ((success_p = i >= nops && insn_ok_p (ctx, insn))) insn_change_p = TRUE; + while (VARR_LENGTH (size_t, changed_op_numbers)) { + i = VARR_POP (size_t, changed_op_numbers); + if (success_p) + VARR_SET (MIR_op_t, last_right_ops, i, insn->ops[i]); + else + insn->ops[i] = VARR_GET (MIR_op_t, last_right_ops, i); /* restore changed operands */ + } + if (success_p) { + gen_assert (def_insn != NULL); + combine_delete_insn (ctx, def_insn, bb_insn); +#if MIR_GEN_DEBUG + if (debug_file != NULL) { + fprintf (debug_file, " changing to "); + print_bb_insn (ctx, bb_insn, TRUE); + } +#endif + } + } + } + return insn_change_p; +} + +static MIR_insn_code_t get_combined_br_code (int true_p, MIR_insn_code_t cmp_code) { + switch (cmp_code) { + case MIR_EQ: return true_p ? MIR_BEQ : MIR_BNE; + case MIR_EQS: return true_p ? MIR_BEQS : MIR_BNES; + case MIR_NE: return true_p ? MIR_BNE : MIR_BEQ; + case MIR_NES: return true_p ? MIR_BNES : MIR_BEQS; + case MIR_LT: return true_p ? MIR_BLT : MIR_BGE; + case MIR_LTS: return true_p ? MIR_BLTS : MIR_BGES; + case MIR_ULT: return true_p ? MIR_UBLT : MIR_UBGE; + case MIR_ULTS: return true_p ? MIR_UBLTS : MIR_UBGES; + case MIR_LE: return true_p ? MIR_BLE : MIR_BGT; + case MIR_LES: return true_p ? MIR_BLES : MIR_BGTS; + case MIR_ULE: return true_p ? MIR_UBLE : MIR_UBGT; + case MIR_ULES: return true_p ? MIR_UBLES : MIR_UBGTS; + case MIR_GT: return true_p ? MIR_BGT : MIR_BLE; + case MIR_GTS: return true_p ? MIR_BGTS : MIR_BLES; + case MIR_UGT: return true_p ? MIR_UBGT : MIR_UBLE; + case MIR_UGTS: return true_p ? MIR_UBGTS : MIR_UBLES; + case MIR_GE: return true_p ? MIR_BGE : MIR_BLT; + case MIR_GES: return true_p ? MIR_BGES : MIR_BLTS; + case MIR_UGE: return true_p ? MIR_UBGE : MIR_UBLT; + case MIR_UGES: + return true_p ? MIR_UBGES : MIR_UBLTS; + /* Cannot revert in the false case for IEEE754: */ + case MIR_FEQ: return true_p ? MIR_FBEQ : MIR_INSN_BOUND; + case MIR_DEQ: return true_p ? MIR_DBEQ : MIR_INSN_BOUND; + case MIR_LDEQ: return true_p ? MIR_LDBEQ : MIR_INSN_BOUND; + case MIR_FNE: return true_p ? MIR_FBNE : MIR_INSN_BOUND; + case MIR_DNE: return true_p ? MIR_DBNE : MIR_INSN_BOUND; + case MIR_LDNE: return true_p ? MIR_LDBNE : MIR_INSN_BOUND; + case MIR_FLT: return true_p ? MIR_FBLT : MIR_INSN_BOUND; + case MIR_DLT: return true_p ? MIR_DBLT : MIR_INSN_BOUND; + case MIR_LDLT: return true_p ? MIR_LDBLT : MIR_INSN_BOUND; + case MIR_FLE: return true_p ? MIR_FBLE : MIR_INSN_BOUND; + case MIR_DLE: return true_p ? MIR_DBLE : MIR_INSN_BOUND; + case MIR_LDLE: return true_p ? MIR_LDBLE : MIR_INSN_BOUND; + case MIR_FGT: return true_p ? MIR_FBGT : MIR_INSN_BOUND; + case MIR_DGT: return true_p ? MIR_DBGT : MIR_INSN_BOUND; + case MIR_LDGT: return true_p ? MIR_LDBGT : MIR_INSN_BOUND; + case MIR_FGE: return true_p ? MIR_FBGE : MIR_INSN_BOUND; + case MIR_DGE: return true_p ? MIR_DBGE : MIR_INSN_BOUND; + case MIR_LDGE: return true_p ? MIR_LDBGE : MIR_INSN_BOUND; + default: return MIR_INSN_BOUND; + } +} + +static MIR_insn_t combine_branch_and_cmp (MIR_context_t ctx, bb_insn_t bb_insn) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + MIR_insn_t def_insn, new_insn, insn = bb_insn->insn; + MIR_insn_code_t code = insn->code; + MIR_op_t op; + + if (code != MIR_BT && code != MIR_BF && code != MIR_BTS && code != MIR_BFS) return NULL; + op = insn->ops[1]; + if (op.mode != MIR_OP_HARD_REG || !safe_hreg_substitution_p (ctx, op.u.hard_reg, bb_insn)) + return NULL; + def_insn = hreg_refs_addr[op.u.hard_reg].insn; + if ((code = get_combined_br_code (code == MIR_BT || code == MIR_BTS, def_insn->code)) + == MIR_INSN_BOUND) + return NULL; + if (obsolete_op_p (ctx, def_insn->ops[1], hreg_refs_addr[op.u.hard_reg].insn_num) + || obsolete_op_p (ctx, def_insn->ops[2], hreg_refs_addr[op.u.hard_reg].insn_num)) + return NULL; + new_insn = MIR_new_insn (ctx, code, insn->ops[0], def_insn->ops[1], def_insn->ops[2]); + MIR_insert_insn_before (ctx, curr_func_item, insn, new_insn); + if (!insn_ok_p (ctx, new_insn)) { + MIR_remove_insn (ctx, curr_func_item, new_insn); + return NULL; + } else { + MIR_remove_insn (ctx, curr_func_item, insn); + new_insn->data = bb_insn; + bb_insn->insn = new_insn; +#if MIR_GEN_DEBUG + if (debug_file != NULL) { + fprintf (debug_file, " changing to "); + print_bb_insn (ctx, bb_insn, TRUE); + } +#endif + combine_delete_insn (ctx, def_insn, bb_insn); + return new_insn; + } +} + +static void setup_hreg_ref (MIR_context_t ctx, MIR_reg_t hr, MIR_insn_t insn, size_t nop, + size_t insn_num, int def_p) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + + if (hr == MIR_NON_HARD_REG) return; + hreg_ref_ages_addr[hr] = curr_bb_hreg_ref_age; + hreg_refs_addr[hr].insn = insn; + hreg_refs_addr[hr].nop = nop; + hreg_refs_addr[hr].insn_num = insn_num; + hreg_refs_addr[hr].def_p = def_p; + hreg_refs_addr[hr].del_p = FALSE; +} + +static void combine (MIR_context_t ctx) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + MIR_insn_code_t code, new_code; + MIR_insn_t insn, new_insn, def_insn; + bb_insn_t bb_insn; + size_t iter, nops, i, curr_insn_num; + MIR_op_t temp_op, *op_ref; + MIR_reg_t hr, early_clobbered_hard_reg1, early_clobbered_hard_reg2; + int out_p, change_p, block_change_p; + int64_t p; +#if MIR_GEN_DEBUG + size_t insns_num = 0, deleted_insns_num = 0; +#endif + + hreg_refs_addr = VARR_ADDR (hreg_ref_t, hreg_refs); + hreg_ref_ages_addr = VARR_ADDR (size_t, hreg_ref_ages); + for (bb_t bb = DLIST_HEAD (bb_t, curr_cfg->bbs); bb != NULL; bb = DLIST_NEXT (bb_t, bb)) { + do { +#if MIR_GEN_DEBUG + if (debug_file != NULL) fprintf (debug_file, "Processing bb%d\n", bb->index); +#endif + block_change_p = FALSE; + curr_bb_hreg_ref_age++; + last_mem_ref_insn_num = 0; /* means undef */ + for (bb_insn = DLIST_HEAD (bb_insn_t, bb->bb_insns), curr_insn_num = 1; bb_insn != NULL; + bb_insn = DLIST_NEXT (bb_insn_t, bb_insn), curr_insn_num++) { + insn = bb_insn->insn; + nops = MIR_insn_nops (ctx, insn); +#if MIR_GEN_DEBUG + if (insn->code != MIR_LABEL) insns_num++; + if (debug_file != NULL) { + fprintf (debug_file, " Processing "); + print_bb_insn (ctx, bb_insn, TRUE); + } +#endif + get_early_clobbered_hard_reg (insn, &early_clobbered_hard_reg1, &early_clobbered_hard_reg2); + if (early_clobbered_hard_reg1 != MIR_NON_HARD_REG) + setup_hreg_ref (ctx, early_clobbered_hard_reg1, insn, 0 /* whatever */, curr_insn_num, + TRUE); + if (early_clobbered_hard_reg2 != MIR_NON_HARD_REG) + setup_hreg_ref (ctx, early_clobbered_hard_reg2, insn, 0 /* whatever */, curr_insn_num, + TRUE); + if (MIR_call_code_p (code = insn->code)) { + for (size_t hr = 0; hr <= MAX_HARD_REG; hr++) + if (bitmap_bit_p (call_used_hard_regs, hr)) { + setup_hreg_ref (ctx, hr, insn, 0 /* whatever */, curr_insn_num, TRUE); + } + } else if (code == MIR_RET) { + /* ret is transformed in machinize and should be not modified after that */ + } else if ((new_insn = combine_branch_and_cmp (ctx, bb_insn)) != NULL) { + insn = new_insn; + nops = MIR_insn_nops (ctx, insn); + block_change_p = TRUE; + } else { + change_p = combine_substitute (ctx, bb_insn); + if (!change_p && (new_code = commutative_insn_code (insn->code)) != MIR_INSN_BOUND) { + insn->code = new_code; + temp_op = insn->ops[1]; + insn->ops[1] = insn->ops[2]; + insn->ops[2] = temp_op; + if (combine_substitute (ctx, bb_insn)) + change_p = TRUE; + else { + insn->code = code; + temp_op = insn->ops[1]; + insn->ops[1] = insn->ops[2]; + insn->ops[2] = temp_op; + } + } + if (change_p) block_change_p = TRUE; + } + + for (iter = 0; iter < 2; iter++) { /* update hreg ref info: */ + for (i = 0; i < nops; i++) { + op_ref = &insn->ops[i]; + MIR_insn_op_mode (ctx, insn, i, &out_p); + if (op_ref->mode == MIR_OP_HARD_REG && !iter == !out_p) { + /* process in ops on 0th iteration and out ops on 1th iteration */ + setup_hreg_ref (ctx, op_ref->u.hard_reg, insn, i, curr_insn_num, iter == 1); + } else if (op_ref->mode == MIR_OP_HARD_REG_MEM) { + if (out_p && iter == 1) + last_mem_ref_insn_num = curr_insn_num; + else if (iter == 0) { + setup_hreg_ref (ctx, op_ref->u.hard_reg_mem.base, insn, i, curr_insn_num, FALSE); + setup_hreg_ref (ctx, op_ref->u.hard_reg_mem.index, insn, i, curr_insn_num, FALSE); + } + } + } + } + } + } while (block_change_p); + } +#if MIR_GEN_DEBUG + if (debug_file != NULL) + fprintf (debug_file, " %lu deleted out of %lu (%.1f%%)\n", deleted_insns_num, insns_num, + 100.0 * deleted_insns_num / insns_num); +#endif +} + +static void init_selection (MIR_context_t ctx) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + hreg_ref_t hreg_ref = {NULL, 0, 0}; + + gen_ctx->selection_ctx = gen_malloc (ctx, sizeof (struct selection_ctx)); + curr_bb_hreg_ref_age = 0; + VARR_CREATE (size_t, hreg_ref_ages, MAX_HARD_REG + 1); + VARR_CREATE (hreg_ref_t, hreg_refs, MAX_HARD_REG + 1); + VARR_CREATE (MIR_reg_t, insn_hard_regs, 0); + VARR_CREATE (size_t, changed_op_numbers, 16); + VARR_CREATE (MIR_op_t, last_right_ops, 16); + hard_regs_bitmap = bitmap_create2 (MAX_HARD_REG + 1); + for (MIR_reg_t i = 0; i <= MAX_HARD_REG; i++) { + VARR_PUSH (hreg_ref_t, hreg_refs, hreg_ref); + VARR_PUSH (size_t, hreg_ref_ages, 0); + } +} + +static void finish_selection (MIR_context_t ctx) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + + VARR_DESTROY (size_t, hreg_ref_ages); + VARR_DESTROY (hreg_ref_t, hreg_refs); + VARR_DESTROY (MIR_reg_t, insn_hard_regs); + VARR_DESTROY (size_t, changed_op_numbers); + VARR_DESTROY (MIR_op_t, last_right_ops); + bitmap_destroy (hard_regs_bitmap); + free (gen_ctx->selection_ctx); + gen_ctx->selection_ctx = NULL; +} + +/* New Page */ + +/* Dead code elimnination */ + +#define live_out out + +static void dead_code_elimination (MIR_context_t ctx) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + MIR_insn_t insn; + bb_insn_t bb_insn, prev_bb_insn; + size_t nops, i; + MIR_reg_t var, early_clobbered_hard_reg1, early_clobbered_hard_reg2; + MIR_op_t op; + int out_p, reg_def_p, dead_p; + bitmap_t live; + +#if MIR_GEN_DEBUG + if (debug_file != NULL) fprintf (debug_file, "+++++++++++++Dead code elimination:\n"); +#endif + live = bitmap_create2 (DEFAULT_INIT_BITMAP_BITS_NUM); + for (bb_t bb = DLIST_HEAD (bb_t, curr_cfg->bbs); bb != NULL; bb = DLIST_NEXT (bb_t, bb)) { + bitmap_copy (live, bb->live_out); + for (bb_insn = DLIST_TAIL (bb_insn_t, bb->bb_insns); bb_insn != NULL; bb_insn = prev_bb_insn) { + prev_bb_insn = DLIST_PREV (bb_insn_t, bb_insn); + insn = bb_insn->insn; + nops = MIR_insn_nops (ctx, insn); + reg_def_p = FALSE; + dead_p = TRUE; + for (i = 0; i < nops; i++) { + op = insn->ops[i]; + MIR_insn_op_mode (ctx, insn, i, &out_p); + if (!out_p || (op.mode != MIR_OP_REG && op.mode != MIR_OP_HARD_REG)) continue; + reg_def_p = TRUE; + var = op.mode == MIR_OP_HARD_REG ? op.u.hard_reg : reg2var (gen_ctx, op.u.reg); + if (bitmap_clear_bit_p (live, var)) dead_p = FALSE; + } + if (!reg_def_p) dead_p = FALSE; + if (dead_p && !MIR_call_code_p (insn->code) && insn->code != MIR_RET + && insn->code != MIR_ALLOCA && insn->code != MIR_BSTART && insn->code != MIR_BEND + && insn->code != MIR_VA_START && insn->code != MIR_VA_END + && !(insn->ops[0].mode == MIR_OP_HARD_REG + && (insn->ops[0].u.hard_reg == BP_HARD_REG + || insn->ops[0].u.hard_reg == SP_HARD_REG))) { +#if MIR_GEN_DEBUG + if (debug_file != NULL) { + fprintf (debug_file, " Removing dead insn"); + MIR_output_insn (ctx, debug_file, insn, curr_func_item->u.func, TRUE); + } +#endif + gen_delete_insn (ctx, insn); + continue; + } + if (MIR_call_code_p (insn->code)) bitmap_and_compl (live, live, call_used_hard_regs); + for (i = 0; i < nops; i++) { + op = insn->ops[i]; + MIR_insn_op_mode (ctx, insn, i, &out_p); + switch (op.mode) { + case MIR_OP_REG: + if (!out_p) bitmap_set_bit_p (live, reg2var (gen_ctx, op.u.reg)); + break; + case MIR_OP_HARD_REG: + if (!out_p) bitmap_set_bit_p (live, op.u.hard_reg); + break; + case MIR_OP_MEM: + if (op.u.mem.base != 0) bitmap_set_bit_p (live, reg2var (gen_ctx, op.u.mem.base)); + if (op.u.mem.index != 0) bitmap_set_bit_p (live, reg2var (gen_ctx, op.u.mem.index)); + break; + case MIR_OP_HARD_REG_MEM: + if (op.u.hard_reg_mem.base != MIR_NON_HARD_REG) + bitmap_set_bit_p (live, op.u.hard_reg_mem.base); + if (op.u.hard_reg_mem.index != MIR_NON_HARD_REG) + bitmap_set_bit_p (live, op.u.hard_reg_mem.index); + break; + default: break; + } + } + get_early_clobbered_hard_reg (insn, &early_clobbered_hard_reg1, &early_clobbered_hard_reg2); + if (early_clobbered_hard_reg1 != MIR_NON_HARD_REG) + bitmap_clear_bit_p (live, early_clobbered_hard_reg1); + if (early_clobbered_hard_reg2 != MIR_NON_HARD_REG) + bitmap_clear_bit_p (live, early_clobbered_hard_reg2); + if (MIR_call_code_p (insn->code)) bitmap_ior (live, live, bb_insn->call_hard_reg_args); + } + } + bitmap_destroy (live); +} + +#undef live_out + +#if MIR_GEN_DEBUG + +#include +#include + +static void print_code (MIR_context_t ctx, uint8_t *code, size_t code_len, void *start_addr) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + size_t i; + int ch; + char cfname[30]; + char command[500]; + FILE *f; + + sprintf (cfname, "_mir_%lu.c", (unsigned long) getpid ()); + if ((f = fopen (cfname, "w")) == NULL) return; + fprintf (f, "unsigned char code[] = {"); + for (i = 0; i < code_len; i++) { + if (i != 0) fprintf (f, ", "); + fprintf (f, "0x%x", code[i]); + } + fprintf (f, "};\n"); + fclose (f); + sprintf (command, + "gcc -c -o %s.o %s 2>&1 && objdump --section=.data --adjust-vma=0x%lx -D %s.o; rm -f " + "%s.o %s", + cfname, cfname, (unsigned long) start_addr, cfname, cfname, cfname); + if ((f = popen (command, "r")) == NULL) return; + while ((ch = fgetc (f)) != EOF) fprintf (debug_file, "%c", ch); + pclose (f); +} +#endif + +#if MIR_GEN_DEBUG +#include + +static double real_usec_time (void) { + struct timeval tv; + + gettimeofday (&tv, NULL); + return tv.tv_usec + tv.tv_sec * 1000000.0; +} +#endif + +#if MIR_GEN_CALL_TRACE +static void *print_and_execute_wrapper (MIR_context_t ctx, MIR_item_t called_func) { + gen_assert (called_func->item_type == MIR_func_item); + fprintf (stderr, "Calling %s\n", called_func->u.func->name); + return called_func->u.func->machine_code; +} +#endif + +void *MIR_gen (MIR_context_t ctx, MIR_item_t func_item) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + uint8_t *code; + size_t code_len; +#if MIR_GEN_DEBUG + double start_time; +#endif + + gen_assert (func_item->item_type == MIR_func_item && func_item->data == NULL); + if (func_item->u.func->machine_code != NULL) { + gen_assert (func_item->u.func->call_addr != NULL); + _MIR_redirect_thunk (ctx, func_item->addr, func_item->u.func->call_addr); +#if MIR_GEN_DEBUG + if (debug_file != NULL) + fprintf (debug_file, "+++++++++++++The code for %s has been already generated\n", + MIR_item_name (ctx, func_item)); +#endif + return func_item->addr; + } +#if MIR_GEN_DEBUG + if (debug_file != NULL) { + start_time = real_usec_time (); + fprintf (debug_file, "+++++++++++++MIR before generator:\n"); + MIR_output_item (ctx, debug_file, func_item); + } +#endif + curr_func_item = func_item; + _MIR_duplicate_func_insns (ctx, func_item); + curr_cfg = func_item->data = gen_malloc (ctx, sizeof (struct func_cfg)); + build_func_cfg (ctx); +#if MIR_GEN_DEBUG + if (debug_file != NULL) { + fprintf (debug_file, "+++++++++++++MIR after building CFG:\n"); + print_CFG (ctx, TRUE, TRUE, NULL); + } +#endif +#ifndef NO_CSE +#if MIR_GEN_DEBUG + if (debug_file != NULL) fprintf (debug_file, "+++++++++++++CSE:\n"); +#endif + cse (ctx); +#if MIR_GEN_DEBUG + if (debug_file != NULL) { + print_exprs (ctx); + fprintf (debug_file, "+++++++++++++MIR after CSE:\n"); + print_CFG (ctx, TRUE, TRUE, output_bb_cse_info); + } +#endif + cse_clear (ctx); +#endif /* #ifndef NO_CSE */ + calculate_func_cfg_live_info (ctx, FALSE); +#ifndef NO_CSE + dead_code_elimination (ctx); +#if MIR_GEN_DEBUG + if (debug_file != NULL) { + fprintf (debug_file, "+++++++++++++MIR after dead code elimination after CSE:\n"); + print_CFG (ctx, TRUE, TRUE, output_bb_live_info); + } +#endif +#endif +#ifndef NO_CCP + if (ccp (ctx)) { +#if MIR_GEN_DEBUG + if (debug_file != NULL) { + fprintf (debug_file, "+++++++++++++MIR after CCP:\n"); + print_CFG (ctx, TRUE, TRUE, NULL); + } +#endif + dead_code_elimination (ctx); +#if MIR_GEN_DEBUG + if (debug_file != NULL) { + fprintf (debug_file, "+++++++++++++MIR after dead code elimination after CCP:\n"); + print_CFG (ctx, TRUE, TRUE, output_bb_live_info); + } +#endif + } +#endif /* #ifndef NO_CCP */ + ccp_clear (ctx); + make_io_dup_op_insns (ctx); + machinize (ctx); + add_new_bb_insns (ctx); +#if MIR_GEN_DEBUG + if (debug_file != NULL) { + fprintf (debug_file, "+++++++++++++MIR after machinize:\n"); + print_CFG (ctx, FALSE, TRUE, NULL); + } +#endif + build_loop_tree (ctx); + calculate_func_cfg_live_info (ctx, TRUE); +#if MIR_GEN_DEBUG + if (debug_file != NULL) { + add_bb_insn_dead_vars (ctx); + fprintf (debug_file, "+++++++++++++MIR after building live_info:\n"); + print_loop_tree (ctx); + print_CFG (ctx, TRUE, FALSE, output_bb_live_info); + } +#endif + build_live_ranges (ctx); + assign (ctx); + rewrite (ctx); /* After rewrite the BB live info is still valid */ +#if MIR_GEN_DEBUG + if (debug_file != NULL) { + fprintf (debug_file, "+++++++++++++MIR after rewrite:\n"); + print_CFG (ctx, FALSE, TRUE, NULL); + } +#endif + calculate_func_cfg_live_info (ctx, FALSE); + add_bb_insn_dead_vars (ctx); +#if MIR_GEN_DEBUG + if (debug_file != NULL) { + fprintf (debug_file, "+++++++++++++MIR before combine:\n"); + print_CFG (ctx, FALSE, TRUE, NULL); + } +#endif +#ifndef NO_COMBINE + combine (ctx); /* After combine the BB live info is still valid */ +#if MIR_GEN_DEBUG + if (debug_file != NULL) { + fprintf (debug_file, "+++++++++++++MIR after combine:\n"); + print_CFG (ctx, FALSE, TRUE, NULL); + } +#endif + dead_code_elimination (ctx); +#if MIR_GEN_DEBUG + if (debug_file != NULL) { + fprintf (debug_file, "+++++++++++++MIR after dead code elimination after combine:\n"); + print_CFG (ctx, TRUE, TRUE, output_bb_live_info); + } +#endif +#endif /* #ifndef NO_COMBINE */ + make_prolog_epilog (ctx, func_assigned_hard_regs, func_stack_slots_num); +#if MIR_GEN_DEBUG + if (debug_file != NULL) { + fprintf (debug_file, "+++++++++++++MIR after forming prolog/epilog:\n"); + print_CFG (ctx, FALSE, TRUE, NULL); + } +#endif + code = target_translate (ctx, &code_len); + func_item->u.func->machine_code = func_item->u.func->call_addr + = _MIR_publish_code (ctx, code, code_len); + target_rebase (ctx, func_item->u.func->machine_code); +#if MIR_GEN_CALL_TRACE + func_item->u.func->call_addr = _MIR_get_wrapper (ctx, called_func, print_and_execute_wrapper); +#endif +#if MIR_GEN_DEBUG + if (debug_file != NULL) { + print_code (ctx, func_item->u.func->machine_code, code_len, func_item->u.func->machine_code); + fprintf (debug_file, "code size = %lu:\n", (unsigned long) code_len); + } +#endif + _MIR_redirect_thunk (ctx, func_item->addr, func_item->u.func->call_addr); + destroy_func_live_ranges (ctx); + destroy_func_cfg (ctx); +#if MIR_GEN_DEBUG + if (debug_file != NULL) + fprintf (debug_file, "Generation of code for %s -- time %.0f usec\n", + MIR_item_name (ctx, func_item), real_usec_time () - start_time); +#endif + _MIR_restore_func_insns (ctx, func_item); + return func_item->addr; +} + +void MIR_gen_set_debug_file (MIR_context_t ctx, FILE *f) { +#if MIR_GEN_DEBUG + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + + debug_file = f; +#endif +} + +void MIR_gen_init (MIR_context_t ctx) { + struct gen_ctx **gen_ctx_ptr = gen_ctx_loc (ctx), *gen_ctx; + MIR_reg_t i; + +#ifdef TEST_MIR_GEN + if (debug_file != NULL) fprintf (debug_file, "Page size = %lu\n", (unsigned long) page_size); +#endif + *gen_ctx_ptr = gen_ctx = gen_malloc (ctx, sizeof (struct gen_ctx)); + gen_ctx->target_ctx = NULL; + gen_ctx->data_flow_ctx = NULL; + gen_ctx->cse_ctx = NULL; + gen_ctx->ccp_ctx = NULL; + gen_ctx->lr_ctx = NULL; + gen_ctx->ra_ctx = NULL; + gen_ctx->selection_ctx = NULL; + VARR_CREATE (loop_node_t, loop_nodes, 32); + VARR_CREATE (loop_node_t, queue_nodes, 32); + VARR_CREATE (loop_node_t, loop_entries, 16); + init_dead_vars (); + init_data_flow (ctx); + init_cse (ctx); + init_ccp (ctx); + temp_bitmap = bitmap_create2 (DEFAULT_INIT_BITMAP_BITS_NUM); + temp_bitmap2 = bitmap_create2 (DEFAULT_INIT_BITMAP_BITS_NUM); + all_vars = bitmap_create2 (DEFAULT_INIT_BITMAP_BITS_NUM); + init_live_ranges (ctx); + init_ra (ctx); + init_selection (ctx); + call_used_hard_regs = bitmap_create2 (MAX_HARD_REG + 1); + for (i = 0; i <= MAX_HARD_REG; i++) + if (call_used_hard_reg_p (i)) bitmap_set_bit_p (call_used_hard_regs, i); + insn_to_consider = bitmap_create2 (1024); + target_init (ctx); +} + +void MIR_gen_finish (MIR_context_t ctx) { + struct gen_ctx *gen_ctx = *gen_ctx_loc (ctx); + + finish_data_flow (ctx); + finish_cse (ctx); + finish_ccp (ctx); + bitmap_destroy (temp_bitmap); + bitmap_destroy (temp_bitmap2); + bitmap_destroy (all_vars); + finish_live_ranges (ctx); + finish_ra (ctx); + finish_selection (ctx); + bitmap_destroy (call_used_hard_regs); + bitmap_destroy (insn_to_consider); + target_finish (ctx); + finish_dead_vars (); + free (gen_ctx->data_flow_ctx); + VARR_DESTROY (loop_node_t, loop_nodes); + VARR_DESTROY (loop_node_t, queue_nodes); + VARR_DESTROY (loop_node_t, loop_entries); + free (gen_ctx); +} + +void MIR_set_gen_interface (MIR_context_t ctx, MIR_item_t func_item) { MIR_gen (ctx, func_item); } + +static void *gen_and_redirect (MIR_context_t ctx, MIR_item_t func_item) { + MIR_gen (ctx, func_item); + return func_item->u.func->machine_code; +} + +void MIR_set_lazy_gen_interface (MIR_context_t ctx, MIR_item_t func_item) { + void *addr = _MIR_get_wrapper (ctx, func_item, gen_and_redirect); + + _MIR_redirect_thunk (ctx, func_item->addr, addr); +} + +/* Local Variables: */ +/* mode: c */ +/* page-delimiter: "/\\* New Page" */ +/* End: */ diff --git a/mir/mir-gen.h b/mir/mir-gen.h new file mode 100644 index 0000000..0b4eec3 --- /dev/null +++ b/mir/mir-gen.h @@ -0,0 +1,22 @@ +/* This file is a part of MIR project. + Copyright (C) 2018, 2019 Vladimir Makarov . +*/ + +#ifndef MIR_GEN_H + +#define MIR_GEN_H + +#include "mir.h" + +#ifndef MIR_GEN_DEBUG +#define MIR_GEN_DEBUG 0 +#endif + +extern void MIR_gen_init (MIR_context_t context); +extern void MIR_gen_set_debug_file (MIR_context_t context, FILE *f); +extern void *MIR_gen (MIR_context_t context, MIR_item_t func_item); +extern void MIR_set_gen_interface (MIR_context_t context, MIR_item_t func_item); +extern void MIR_set_lazy_gen_interface (MIR_context_t context, MIR_item_t func_item); +extern void MIR_gen_finish (MIR_context_t context); + +#endif /* #ifndef MIR_GEN_H */ diff --git a/mir/mir-hash.h b/mir/mir-hash.h new file mode 100644 index 0000000..22e7b80 --- /dev/null +++ b/mir/mir-hash.h @@ -0,0 +1,92 @@ +/* This file is a part of MIR project. + + Copyright (C) 2018, 2019 Vladimir Makarov . +*/ + +/* Simple high-quality multiplicative hash passing demerphq-smhsher, + faster than spooky, city, or xxhash for strings less 100 bytes. + Hash for the same key can be different on different architectures. + To get machine-independent hash, use mir_hash_strict which is about + 1.5 times slower than mir_hash. */ +#ifndef __MIR_HASH__ +#define __MIR_HASH__ + +#include +#include + +#if defined(__x86_64__) || defined(__i386__) || defined(__PPC64__) || defined(__s390__) \ + || defined(__m32c__) || defined(cris) || defined(__CR16__) || defined(__vax__) \ + || defined(__m68k__) || defined(__aarch64__) || defined(_M_AMD64) || defined(_M_IX86) +#define MIR_HASH_UNALIGNED_ACCESS 1 +#else +#define MIR_HASH_UNALIGNED_ACCESS 0 +#endif + +static inline uint64_t mir_get_key_part (const uint8_t *v, size_t len, int relax_p) { + size_t i, start = 0; + uint64_t tail = 0; + + if (relax_p || __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) { +#if MIR_HASH_UNALIGNED_ACCESS + if (len == sizeof (uint64_t)) return *(uint64_t *) v; + if (len >= sizeof (uint32_t)) { + tail = (uint64_t) * (uint32_t *) v << 32; + start = 4; + } +#endif + } + for (i = start; i < len; i++) tail = (tail >> 8) | ((uint64_t) v[i] << 56); + return tail; +} + +static const uint64_t p1 = 0X65862b62bdf5ef4d, p2 = 0X288eea216831e6a7; +static inline uint64_t mir_mum (uint64_t v, uint64_t c, int relax_p) { + if (relax_p) { +#if defined(__SIZEOF_INT128__) + __uint128_t r = (__uint128_t) v * (__uint128_t) c; + return (uint64_t) (r >> 64) + (uint64_t) r; +#endif + } + uint64_t v1 = v >> 32, v2 = (uint32_t) v, c1 = c >> 32, c2 = (uint32_t) c, rm = v2 * c1 + v1 * c2; + return v1 * c1 + (rm >> 32) + v2 * c2 + (rm << 32); +} + +static inline uint64_t mir_round (uint64_t state, uint64_t v, int relax_p) { + state ^= mir_mum (v, p1, relax_p); + return state ^ mir_mum (state, p2, relax_p); +} + +static inline uint64_t mir_hash_1 (const void *key, size_t len, uint64_t seed, int relax_p) { + const uint8_t *v = (const uint8_t *) key; + uint64_t r = seed + len; + + for (; len >= 16; len -= 16, v += 16) { + r ^= mir_mum (mir_get_key_part (v, 8, relax_p), p1, relax_p); + r ^= mir_mum (mir_get_key_part (v + 8, 8, relax_p), p2, relax_p); + r ^= mir_mum (r, p1, relax_p); + } + if (len >= 8) { + r ^= mir_mum (mir_get_key_part (v, 8, relax_p), p1, relax_p); + len -= 8, v += 8; + } + if (len != 0) r ^= mir_mum (mir_get_key_part (v, len, relax_p), p2, relax_p); + return mir_round (r, r, relax_p); +} + +static inline uint64_t mir_hash (const void *key, size_t len, uint64_t seed) { + return mir_hash_1 (key, len, seed, 1); +} + +static inline uint64_t mir_hash_strict (const void *key, size_t len, uint64_t seed) { + return mir_hash_1 (key, len, seed, 0); +} + +static inline uint64_t mir_hash_init (uint64_t seed) { return seed; } +static inline uint64_t mir_hash_step (uint64_t h, uint64_t key) { return mir_round (h, key, 1); } +static inline uint64_t mir_hash_finish (uint64_t h) { return mir_round (h, h, 1); } + +static inline uint64_t mir_hash64 (uint64_t key, uint64_t seed) { + return mir_hash_finish (mir_hash_step (mir_hash_init (seed), key)); +} + +#endif diff --git a/mir/mir-htab.h b/mir/mir-htab.h new file mode 100644 index 0000000..5c6e780 --- /dev/null +++ b/mir/mir-htab.h @@ -0,0 +1,221 @@ +/* This file is a part of MIR project. + Copyright (C) 2018, 2019 Vladimir Makarov . +*/ + +#ifndef MIR_HTAB_H +#define MIR_HTAB_H + +#include "mir-varr.h" + +#define FALSE 0 +#define TRUE 1 + +#if !defined(VARR_ENABLE_CHECKING) && !defined(NDEBUG) +#define VARR_ENABLE_CHECKING +#endif + +#ifndef HTAB_ENABLE_CHECKING +#define HTAB_ASSERT(EXPR, OP, T) ((void) (EXPR)) + +#else +static inline void mir_htab_assert_fail (const char *op, const char *var) { + fprintf (stderr, "wrong %s for %s\n", op, var); + assert (0); +} + +#define HTAB_ASSERT(EXPR, OP, T) (void) ((EXPR) ? 0 : (mir_htab_assert_fail (OP, #T), 0)) + +#endif + +#ifdef __GNUC__ +#define MIR_HTAB_NO_RETURN __attribute__ ((noreturn)) +#else +#define MIR_HTAB_NO_RETURN +#endif + +static inline void MIR_HTAB_NO_RETURN mir_htab_error (const char *message) { +#ifdef MIR_HTAB_ERROR + MIR_HTAB_ERROR (message); + assert (FALSE); +#else + fprintf (stderr, "%s\n", message); +#endif + exit (1); +} + +/*---------------- Typed hash table -----------------------------*/ +typedef unsigned htab_ind_t; +typedef unsigned htab_size_t; +typedef unsigned htab_hash_t; + +#define HTAB_EMPTY_IND (~(htab_ind_t) 0) +#define HTAB_DELETED_IND (HTAB_EMPTY_IND - 1) +#define HTAB_DELETED_HASH 0 + +enum htab_action { HTAB_FIND, HTAB_INSERT, HTAB_REPLACE, HTAB_DELETE }; + +#define HTAB(T) HTAB_##T +#define HTAB_OP(T, OP) HTAB_##T##_##OP + +DEF_VARR (htab_ind_t) + +#define HTAB_EL(T) HTAB_EL_##T + +#define HTAB_T(T) \ + typedef struct HTAB_EL (T) { \ + htab_hash_t hash; \ + T el; \ + } HTAB_EL (T); \ + DEF_VARR (HTAB_EL (T)) \ + typedef struct { \ + htab_size_t els_num, els_start, els_bound, collisions; \ + htab_hash_t (*hash_func) (T el); \ + int (*eq_func) (T el1, T el2); \ + void (*free_func) (T el); \ + VARR (HTAB_EL (T)) * els; \ + VARR (htab_ind_t) * entries; \ + } HTAB (T); + +#define DEF_HTAB(T) \ + HTAB_T (T) \ + \ + static inline void HTAB_OP (T, create) (HTAB (T) * *htab, htab_size_t min_size, \ + htab_hash_t (*hash_func) (T el), \ + int (*eq_func) (T el1, T el2), \ + void (*free_func) (T el)) { \ + HTAB (T) * ht; \ + htab_size_t i, size; \ + \ + for (size = 2; min_size > size; size *= 2) \ + ; \ + ht = malloc (sizeof (*ht)); \ + if (ht == NULL) mir_htab_error ("htab: no memory"); \ + VARR_CREATE (HTAB_EL (T), ht->els, size); \ + VARR_TAILOR (HTAB_EL (T), ht->els, size); \ + VARR_CREATE (htab_ind_t, ht->entries, 2 * size); \ + ht->hash_func = hash_func; \ + ht->eq_func = eq_func; \ + ht->free_func = free_func; \ + ht->els_num = ht->els_start = ht->els_bound = ht->collisions = 0; \ + for (i = 0; i < 2 * size; i++) VARR_PUSH (htab_ind_t, ht->entries, HTAB_EMPTY_IND); \ + *htab = ht; \ + } \ + \ + static inline void HTAB_OP (T, clear) (HTAB (T) * htab) { \ + htab_ind_t *addr; \ + htab_size_t i, size; \ + HTAB_EL (T) * els_addr; \ + \ + HTAB_ASSERT (htab != NULL, "clear", T); \ + if (htab->free_func != NULL) { \ + els_addr = VARR_ADDR (HTAB_EL (T), htab->els); \ + size = VARR_LENGTH (HTAB_EL (T), htab->els); \ + for (i = 0; i < htab->els_bound; i++) \ + if (els_addr[i].hash != HTAB_DELETED_HASH) htab->free_func (els_addr[i].el); \ + } \ + htab->els_num = htab->els_start = htab->els_bound = 0; \ + addr = VARR_ADDR (htab_ind_t, htab->entries); \ + size = VARR_LENGTH (htab_ind_t, htab->entries); \ + for (i = 0; i < size; i++) addr[i] = HTAB_EMPTY_IND; \ + } \ + \ + static inline void HTAB_OP (T, destroy) (HTAB (T) * *htab) { \ + HTAB_ASSERT (*htab != NULL, "destroy", T); \ + if ((*htab)->free_func != NULL) HTAB_OP (T, clear) (*htab); \ + VARR_DESTROY (HTAB_EL (T), (*htab)->els); \ + VARR_DESTROY (htab_ind_t, (*htab)->entries); \ + free (*htab); \ + *htab = NULL; \ + } \ + \ + static inline int HTAB_OP (T, do) (HTAB (T) * htab, T el, enum htab_action action, T * res) { \ + htab_ind_t ind, el_ind, *entry, *first_deleted_entry = NULL; \ + htab_hash_t hash, peterb; \ + htab_size_t els_size, size, mask, start, bound, i; \ + htab_ind_t *addr; \ + HTAB_EL (T) * els_addr; \ + \ + HTAB_ASSERT (htab != NULL, "do htab", T); \ + size = VARR_LENGTH (htab_ind_t, htab->entries); \ + els_size = VARR_LENGTH (HTAB_EL (T), htab->els); \ + HTAB_ASSERT (els_size * 2 == size, "do size", T); \ + if ((action == HTAB_INSERT || action == HTAB_REPLACE) && htab->els_bound == els_size) { \ + size *= 2; \ + VARR_TAILOR (htab_ind_t, htab->entries, size); \ + addr = VARR_ADDR (htab_ind_t, htab->entries); \ + for (i = 0; i < size; i++) addr[i] = HTAB_EMPTY_IND; \ + VARR_TAILOR (HTAB_EL (T), htab->els, els_size * 2); \ + els_addr = VARR_ADDR (HTAB_EL (T), htab->els); \ + start = htab->els_start; \ + bound = htab->els_bound; \ + htab->els_start = htab->els_bound = htab->els_num = 0; \ + for (i = start; i < bound; i++) \ + if (els_addr[i].hash != HTAB_DELETED_HASH) { \ + HTAB_OP (T, do) (htab, els_addr[i].el, HTAB_INSERT, res); \ + HTAB_ASSERT ((*htab->eq_func) (*res, els_addr[i].el), "do expand", T); \ + } \ + HTAB_ASSERT (bound - start >= htab->els_bound, "do bound", T); \ + } \ + mask = size - 1; \ + hash = (*htab->hash_func) (el); \ + if (hash == HTAB_DELETED_HASH) hash += 1; \ + peterb = hash; \ + ind = hash & mask; \ + addr = VARR_ADDR (htab_ind_t, htab->entries); \ + els_addr = VARR_ADDR (HTAB_EL (T), htab->els); \ + for (;; htab->collisions++) { \ + entry = addr + ind; \ + el_ind = *entry; \ + if (el_ind != HTAB_EMPTY_IND) { \ + if (el_ind == HTAB_DELETED_IND) { \ + first_deleted_entry = entry; \ + } else if (els_addr[el_ind].hash == hash && (*htab->eq_func) (els_addr[el_ind].el, el)) { \ + if (action == HTAB_REPLACE) { \ + if (htab->free_func != NULL) htab->free_func (els_addr[el_ind].el); \ + els_addr[el_ind].el = el; \ + } \ + if (action != HTAB_DELETE) { \ + *res = els_addr[el_ind].el; \ + } else { \ + htab->els_num--; \ + *entry = HTAB_DELETED_IND; \ + if (htab->free_func != NULL) htab->free_func (els_addr[el_ind].el); \ + els_addr[el_ind].hash = HTAB_DELETED_HASH; \ + } \ + return TRUE; \ + } \ + } else { \ + if (action == HTAB_INSERT || action == HTAB_REPLACE) { \ + htab->els_num++; \ + if (first_deleted_entry != NULL) entry = first_deleted_entry; \ + els_addr[htab->els_bound].hash = hash; \ + els_addr[htab->els_bound].el = el; \ + *entry = htab->els_bound++; \ + *res = el; \ + } \ + return FALSE; \ + } \ + peterb >>= 11; \ + ind = (5 * ind + peterb + 1) & mask; \ + } \ + } \ + \ + static inline htab_size_t HTAB_OP (T, els_num) (HTAB (T) * htab) { \ + HTAB_ASSERT (htab != NULL, "els_num", T); \ + return htab->els_num; \ + } \ + static inline htab_size_t HTAB_OP (T, collisions) (HTAB (T) * htab) { \ + HTAB_ASSERT (htab != NULL, "collisions", T); \ + return htab->collisions; \ + } + +#define HTAB_CREATE(T, V, S, H, EQ) (HTAB_OP (T, create) (&(V), S, H, EQ, NULL)) +#define HTAB_CREATE_WITH_FREE_FUNC(T, V, S, H, EQ, F) (HTAB_OP (T, create) (&(V), S, H, EQ, F)) +#define HTAB_CLEAR(T, V) (HTAB_OP (T, clear) (V)) +#define HTAB_DESTROY(T, V) (HTAB_OP (T, destroy) (&(V))) +/* It returns TRUE if the element existed in the table. */ +#define HTAB_DO(T, V, EL, A, TAB_EL) (HTAB_OP (T, do) (V, EL, A, &(TAB_EL))) +#define HTAB_ELS_NUM(T, V) (HTAB_OP (T, els_num) (V)) +#define HTAB_COLLISIONS(T, V) (HTAB_OP (T, collisions) (V)) + +#endif /* #ifndef MIR_HTAB_H */ diff --git a/mir/mir-interp.c b/mir/mir-interp.c new file mode 100644 index 0000000..21d5a01 --- /dev/null +++ b/mir/mir-interp.c @@ -0,0 +1,1602 @@ +/* This file is a part of MIR project. + Copyright (C) 2018, 2019 Vladimir Makarov . + + File contains MIR interpreter which is an obligatory part of MIR API. +*/ + +#ifndef MIR_INTERP_TRACE +#define MIR_INTERP_TRACE 0 +#endif + +#if !defined(MIR_DIRECT_DISPATCH) && defined(__GNUC__) +#define DIRECT_THREADED_DISPATCH 1 +#else +#define DIRECT_THREADED_DISPATCH 0 +#endif + +#if defined(__GNUC__) +#define ALWAYS_INLINE inline __attribute ((always_inline)) +#else +#define ALWAYS_INLINE inline +#endif + +typedef MIR_val_t *code_t; + +typedef struct func_desc { + MIR_reg_t nregs; + MIR_item_t func_item; + MIR_val_t code[1]; +} * func_desc_t; + +static MIR_reg_t get_reg (MIR_op_t op, MIR_reg_t *max_nreg) { + /* We do not interpret code with hard regs */ + mir_assert (op.mode == MIR_OP_REG); + if (*max_nreg < op.u.reg) *max_nreg = op.u.reg; + return op.u.reg; +} + +#define IC_EL(i) IC_##i +#define REP_SEP , +typedef enum { + IC_LDI8 = MIR_INSN_BOUND, + REP6 (IC_EL, LDU8, LDI16, LDU16, LDI32, LDU32, LDI64), + REP3 (IC_EL, LDF, LDD, LDLD), + REP7 (IC_EL, STI8, STU8, STI16, STU16, STI32, STU32, STI64), + REP3 (IC_EL, STF, STD, STLD), + REP7 (IC_EL, MOVI, MOVP, MOVF, MOVD, MOVLD, IMM_CALL, INSN_BOUND), +} MIR_full_insn_code_t; +#undef REP_SEP + +DEF_VARR (MIR_val_t); + +struct ff_interface { + size_t nres, nargs; + MIR_type_t *res_types, *arg_types; + void *interface_addr; +}; + +typedef struct ff_interface *ff_interface_t; +DEF_HTAB (ff_interface_t); + +struct interp_ctx { +#if DIRECT_THREADED_DISPATCH + void *dispatch_label_tab[IC_INSN_BOUND]; +#endif + VARR (MIR_val_t) * code_varr; + VARR (MIR_insn_t) * branches; + VARR (MIR_val_t) * arg_vals_varr; + MIR_val_t *arg_vals; +#if MIR_INTERP_TRACE + int trace_insn_ident; +#endif + void *(*bstart_builtin) (void); + void (*bend_builtin) (void *); + VARR (MIR_val_t) * call_res_args_varr; + MIR_val_t *call_res_args; + VARR (MIR_type_t) * call_arg_types_varr; + MIR_type_t *call_arg_types; + HTAB (ff_interface_t) * ff_interface_tab; +}; + +#define dispatch_label_tab interp_ctx->dispatch_label_tab +#define code_varr interp_ctx->code_varr +#define branches interp_ctx->branches +#define arg_vals_varr interp_ctx->arg_vals_varr +#define arg_vals interp_ctx->arg_vals +#define trace_insn_ident interp_ctx->trace_insn_ident +#define trace_ident interp_ctx->trace_ident +#define bstart_builtin interp_ctx->bstart_builtin +#define bend_builtin interp_ctx->bend_builtin +#define call_res_args_varr interp_ctx->call_res_args_varr +#define call_res_args interp_ctx->call_res_args +#define call_arg_types_varr interp_ctx->call_arg_types_varr +#define call_arg_types interp_ctx->call_arg_types +#define ff_interface_tab interp_ctx->ff_interface_tab + +static void get_icode (struct interp_ctx *interp_ctx, MIR_val_t *v, int code) { +#if DIRECT_THREADED_DISPATCH + v->a = dispatch_label_tab[code]; +#else + v->ic = code; +#endif +} + +static void push_insn_start (struct interp_ctx *interp_ctx, int code, MIR_insn_t original_insn) { + MIR_val_t v; + + get_icode (interp_ctx, &v, code); + VARR_PUSH (MIR_val_t, code_varr, v); +#if MIR_INTERP_TRACE + v.a = original_insn; + VARR_PUSH (MIR_val_t, code_varr, v); +#endif +} + +static MIR_full_insn_code_t get_int_mem_insn_code (int load_p, MIR_type_t t) { + switch (t) { + case MIR_T_I8: return load_p ? IC_LDI8 : IC_STI8; + case MIR_T_U8: return load_p ? IC_LDU8 : IC_STU8; + case MIR_T_I16: return load_p ? IC_LDI16 : IC_STI16; + case MIR_T_U16: return load_p ? IC_LDU16 : IC_STU16; + case MIR_T_I32: return load_p ? IC_LDI32 : IC_STI32; +#if MIR_PTR32 + case MIR_T_P: +#endif + case MIR_T_U32: return load_p ? IC_LDU32 : IC_STU32; +#if MIR_PTR64 + case MIR_T_P: +#endif + case MIR_T_I64: + case MIR_T_U64: return load_p ? IC_LDI64 : IC_STI64; + default: mir_assert (FALSE); return load_p ? IC_LDI64 : IC_STI64; /* to remove a warning */ + } +} + +static void push_mem (struct interp_ctx *interp_ctx, MIR_op_t op) { + MIR_val_t v; + + mir_assert (op.mode == MIR_OP_MEM && op.u.mem.disp == 0 && op.u.mem.index == 0); + v.i = op.u.mem.base; + VARR_PUSH (MIR_val_t, code_varr, v); +} + +static void redirect_interface_to_interp (MIR_context_t ctx, MIR_item_t func_item); + +static void generate_icode (MIR_context_t ctx, MIR_item_t func_item) { + struct interp_ctx *interp_ctx = ctx->interp_ctx; + int imm_call_p; + MIR_func_t func = func_item->u.func; + MIR_insn_t insn, label; + MIR_val_t v; + size_t i; + MIR_reg_t max_nreg = 0; + func_desc_t func_desc; + + VARR_TRUNC (MIR_insn_t, branches, 0); + VARR_TRUNC (MIR_val_t, code_varr, 0); + for (insn = DLIST_HEAD (MIR_insn_t, func->insns); insn != NULL; + insn = DLIST_NEXT (MIR_insn_t, insn)) { + MIR_insn_code_t code = insn->code; + size_t nops = MIR_insn_nops (ctx, insn); + MIR_op_t *ops = insn->ops; + + insn->data = (void *) VARR_LENGTH (MIR_val_t, code_varr); + switch (code) { + case MIR_MOV: /* loads, imm moves */ + if (ops[0].mode == MIR_OP_MEM) { + push_insn_start (interp_ctx, get_int_mem_insn_code (FALSE, ops[0].u.mem.type), insn); + v.i = get_reg (ops[1], &max_nreg); + VARR_PUSH (MIR_val_t, code_varr, v); + push_mem (interp_ctx, ops[0]); + } else if (ops[1].mode == MIR_OP_MEM) { + push_insn_start (interp_ctx, get_int_mem_insn_code (TRUE, ops[1].u.mem.type), insn); + v.i = get_reg (ops[0], &max_nreg); + VARR_PUSH (MIR_val_t, code_varr, v); + push_mem (interp_ctx, ops[1]); + } else if (ops[1].mode == MIR_OP_INT || ops[1].mode == MIR_OP_UINT) { + push_insn_start (interp_ctx, IC_MOVI, insn); + v.i = get_reg (ops[0], &max_nreg); + VARR_PUSH (MIR_val_t, code_varr, v); + if (ops[1].mode == MIR_OP_INT) + v.i = ops[1].u.i; + else + v.u = ops[1].u.u; + VARR_PUSH (MIR_val_t, code_varr, v); + } else if (ops[1].mode == MIR_OP_REF) { + MIR_item_t item = ops[1].u.ref; + + if (item->item_type == MIR_import_item && item->ref_def != NULL) + item->addr = item->ref_def->addr; + push_insn_start (interp_ctx, IC_MOVP, insn); + v.i = get_reg (ops[0], &max_nreg); + VARR_PUSH (MIR_val_t, code_varr, v); + v.a = item->addr; + VARR_PUSH (MIR_val_t, code_varr, v); + } else { + mir_assert (ops[1].mode == MIR_OP_REG); + push_insn_start (interp_ctx, code, insn); + v.i = get_reg (ops[0], &max_nreg); + VARR_PUSH (MIR_val_t, code_varr, v); + v.i = ops[1].u.reg; + VARR_PUSH (MIR_val_t, code_varr, v); + } + break; + case MIR_FMOV: + if (ops[0].mode == MIR_OP_MEM) { + push_insn_start (interp_ctx, IC_STF, insn); + v.i = get_reg (ops[1], &max_nreg); + VARR_PUSH (MIR_val_t, code_varr, v); + push_mem (interp_ctx, ops[0]); + } else if (ops[1].mode == MIR_OP_MEM) { + push_insn_start (interp_ctx, IC_LDF, insn); + v.i = get_reg (ops[0], &max_nreg); + VARR_PUSH (MIR_val_t, code_varr, v); + push_mem (interp_ctx, ops[1]); + } else if (ops[1].mode == MIR_OP_FLOAT) { + push_insn_start (interp_ctx, IC_MOVF, insn); + v.i = get_reg (ops[0], &max_nreg); + VARR_PUSH (MIR_val_t, code_varr, v); + v.f = ops[1].u.f; + VARR_PUSH (MIR_val_t, code_varr, v); + } else { + mir_assert (ops[1].mode == MIR_OP_REG); + push_insn_start (interp_ctx, code, insn); + v.i = get_reg (ops[0], &max_nreg); + VARR_PUSH (MIR_val_t, code_varr, v); + v.i = ops[1].u.reg; + VARR_PUSH (MIR_val_t, code_varr, v); + } + break; + case MIR_DMOV: + if (ops[0].mode == MIR_OP_MEM) { + push_insn_start (interp_ctx, IC_STD, insn); + v.i = get_reg (ops[1], &max_nreg); + VARR_PUSH (MIR_val_t, code_varr, v); + push_mem (interp_ctx, ops[0]); + } else if (ops[1].mode == MIR_OP_MEM) { + push_insn_start (interp_ctx, IC_LDD, insn); + v.i = get_reg (ops[0], &max_nreg); + VARR_PUSH (MIR_val_t, code_varr, v); + push_mem (interp_ctx, ops[1]); + } else if (ops[1].mode == MIR_OP_DOUBLE) { + push_insn_start (interp_ctx, IC_MOVD, insn); + v.i = get_reg (ops[0], &max_nreg); + VARR_PUSH (MIR_val_t, code_varr, v); + v.d = ops[1].u.d; + VARR_PUSH (MIR_val_t, code_varr, v); + } else { + mir_assert (ops[1].mode == MIR_OP_REG); + push_insn_start (interp_ctx, code, insn); + v.i = get_reg (ops[0], &max_nreg); + VARR_PUSH (MIR_val_t, code_varr, v); + v.i = ops[1].u.reg; + VARR_PUSH (MIR_val_t, code_varr, v); + } + break; + case MIR_LDMOV: + if (ops[0].mode == MIR_OP_MEM) { + push_insn_start (interp_ctx, IC_STLD, insn); + v.i = get_reg (ops[1], &max_nreg); + VARR_PUSH (MIR_val_t, code_varr, v); + push_mem (interp_ctx, ops[0]); + } else if (ops[1].mode == MIR_OP_MEM) { + push_insn_start (interp_ctx, IC_LDLD, insn); + v.i = get_reg (ops[0], &max_nreg); + VARR_PUSH (MIR_val_t, code_varr, v); + push_mem (interp_ctx, ops[1]); + } else if (ops[1].mode == MIR_OP_LDOUBLE) { + push_insn_start (interp_ctx, IC_MOVLD, insn); + v.i = get_reg (ops[0], &max_nreg); + VARR_PUSH (MIR_val_t, code_varr, v); + v.ld = ops[1].u.ld; + VARR_PUSH (MIR_val_t, code_varr, v); + } else { + mir_assert (ops[1].mode == MIR_OP_REG); + push_insn_start (interp_ctx, code, insn); + v.i = get_reg (ops[0], &max_nreg); + VARR_PUSH (MIR_val_t, code_varr, v); + v.i = ops[1].u.reg; + VARR_PUSH (MIR_val_t, code_varr, v); + } + break; + case MIR_LABEL: break; + case MIR_INVALID_INSN: + (*MIR_get_error_func (ctx)) (MIR_invalid_insn_error, "invalid insn for interpreter"); + break; + case MIR_JMP: + VARR_PUSH (MIR_insn_t, branches, insn); + push_insn_start (interp_ctx, code, insn); + v.i = 0; + VARR_PUSH (MIR_val_t, code_varr, v); + break; + case MIR_BT: + case MIR_BTS: + case MIR_BF: + case MIR_BFS: + VARR_PUSH (MIR_insn_t, branches, insn); + push_insn_start (interp_ctx, code, insn); + v.i = 0; + VARR_PUSH (MIR_val_t, code_varr, v); + v.i = get_reg (ops[1], &max_nreg); + VARR_PUSH (MIR_val_t, code_varr, v); + break; + case MIR_BEQ: + case MIR_BEQS: + case MIR_FBEQ: + case MIR_DBEQ: + case MIR_BNE: + case MIR_BNES: + case MIR_FBNE: + case MIR_DBNE: + case MIR_BLT: + case MIR_BLTS: + case MIR_UBLT: + case MIR_UBLTS: + case MIR_FBLT: + case MIR_DBLT: + case MIR_BLE: + case MIR_BLES: + case MIR_UBLE: + case MIR_UBLES: + case MIR_FBLE: + case MIR_DBLE: + case MIR_BGT: + case MIR_BGTS: + case MIR_UBGT: + case MIR_UBGTS: + case MIR_FBGT: + case MIR_DBGT: + case MIR_BGE: + case MIR_BGES: + case MIR_UBGE: + case MIR_UBGES: + case MIR_FBGE: + case MIR_DBGE: + case MIR_LDBEQ: + case MIR_LDBNE: + case MIR_LDBLT: + case MIR_LDBLE: + case MIR_LDBGT: + case MIR_LDBGE: + VARR_PUSH (MIR_insn_t, branches, insn); + push_insn_start (interp_ctx, code, insn); + v.i = 0; + VARR_PUSH (MIR_val_t, code_varr, v); + v.i = get_reg (ops[1], &max_nreg); + VARR_PUSH (MIR_val_t, code_varr, v); + v.i = get_reg (ops[2], &max_nreg); + VARR_PUSH (MIR_val_t, code_varr, v); + break; + default: + imm_call_p = FALSE; + if (MIR_call_code_p (code)) + imm_call_p = (ops[1].mode == MIR_OP_REF + && (ops[1].u.ref->item_type == MIR_import_item + || ops[1].u.ref->item_type == MIR_export_item + || ops[1].u.ref->item_type == MIR_forward_item + || ops[1].u.ref->item_type == MIR_func_item)); + push_insn_start (interp_ctx, imm_call_p ? IC_IMM_CALL : code == MIR_INLINE ? MIR_CALL : code, + insn); + if (code == MIR_SWITCH) { + VARR_PUSH (MIR_insn_t, branches, insn); + v.i = nops; + VARR_PUSH (MIR_val_t, code_varr, v); + } else if (code == MIR_RET) { + v.i = nops; + VARR_PUSH (MIR_val_t, code_varr, v); + } else if (MIR_call_code_p (code)) { + v.i = nops; + VARR_PUSH (MIR_val_t, code_varr, v); + v.a = insn; + VARR_PUSH (MIR_val_t, code_varr, v); + v.a = NULL; + VARR_PUSH (MIR_val_t, code_varr, v); /* for ffi interface */ + } + for (i = 0; i < nops; i++) { + if (i == 0 && MIR_call_code_p (code)) { /* prototype ??? */ + mir_assert (ops[i].mode == MIR_OP_REF && ops[i].u.ref->item_type == MIR_proto_item); + v.a = ops[i].u.ref; + } else if (i == 1 && imm_call_p) { + mir_assert (ops[i].u.ref->item_type == MIR_import_item + || ops[i].u.ref->item_type == MIR_func_item); + v.a = ops[i].u.ref->addr; + } else if (code == MIR_VA_ARG && i == 2) { /* type */ + mir_assert (ops[i].mode == MIR_OP_MEM); + v.i = ops[i].u.mem.type; + } else if (code == MIR_SWITCH && i > 0) { + mir_assert (ops[i].mode == MIR_OP_LABEL); + v.i = 0; + } else { + mir_assert (ops[i].mode == MIR_OP_REG); + v.i = get_reg (ops[i], &max_nreg); + } + VARR_PUSH (MIR_val_t, code_varr, v); + } + } + } + for (i = 0; i < VARR_LENGTH (MIR_insn_t, branches); i++) { + size_t start_label_nop = 0, bound_label_nop = 1, start_label_loc = 1, n; + + insn = VARR_GET (MIR_insn_t, branches, i); + if (insn->code == MIR_SWITCH) { + start_label_nop = 1; + bound_label_nop = start_label_nop + insn->nops - 1; + start_label_loc++; /* we put nops for MIR_SWITCH */ + } + for (n = start_label_nop; n < bound_label_nop; n++) { + label = insn->ops[n].u.label; + v.i = (size_t) label->data; +#if MIR_INTERP_TRACE + VARR_SET (MIR_val_t, code_varr, (size_t) insn->data + n + start_label_loc + 1, v); +#else + VARR_SET (MIR_val_t, code_varr, (size_t) insn->data + n + start_label_loc, v); +#endif + } + } + func_item->data = func_desc + = malloc (sizeof (struct func_desc) + VARR_LENGTH (MIR_val_t, code_varr) * sizeof (MIR_val_t)); + if (func_desc == NULL) + (*MIR_get_error_func (ctx)) (MIR_alloc_error, "no memory for interpreter code"); + memmove (func_desc->code, VARR_ADDR (MIR_val_t, code_varr), + VARR_LENGTH (MIR_val_t, code_varr) * sizeof (MIR_val_t)); + mir_assert (max_nreg < MIR_MAX_REG_NUM); + func_desc->nregs = max_nreg + 1; + func_desc->func_item = func_item; +} + +static void finish_func_interpretation (MIR_item_t func_item) { + mir_assert (func_item->item_type == MIR_func_item); + if (func_item->data == NULL) return; + for (MIR_insn_t insn = DLIST_HEAD (MIR_insn_t, func_item->u.func->insns); insn != NULL; + insn = DLIST_NEXT (MIR_insn_t, insn)) + insn->data = NULL; /* it was used for interpretation preparation */ + free (func_item->data); + func_item->data = NULL; +} + +static ALWAYS_INLINE void *get_a (MIR_val_t *v) { return v->a; } +static ALWAYS_INLINE int64_t get_i (MIR_val_t *v) { return v->i; } +static ALWAYS_INLINE float get_f (MIR_val_t *v) { return v->f; } +static ALWAYS_INLINE double get_d (MIR_val_t *v) { return v->d; } +static ALWAYS_INLINE long double get_ld (MIR_val_t *v) { return v->ld; } + +static ALWAYS_INLINE void **get_aop (MIR_val_t *bp, code_t c) { return &bp[get_i (c)].a; } +static ALWAYS_INLINE int64_t *get_iop (MIR_val_t *bp, code_t c) { return &bp[get_i (c)].i; } +static ALWAYS_INLINE uint64_t *get_uop (MIR_val_t *bp, code_t c) { return &bp[get_i (c)].u; } +static ALWAYS_INLINE float *get_fop (MIR_val_t *bp, code_t c) { return &bp[get_i (c)].f; } +static ALWAYS_INLINE double *get_dop (MIR_val_t *bp, code_t c) { return &bp[get_i (c)].d; } +static ALWAYS_INLINE long double *get_ldop (MIR_val_t *bp, code_t c) { return &bp[get_i (c)].ld; } + +static ALWAYS_INLINE int64_t *get_2iops (MIR_val_t *bp, code_t c, int64_t *p) { + *p = *get_iop (bp, c + 1); + return get_iop (bp, c); +} + +static ALWAYS_INLINE int64_t *get_2isops (MIR_val_t *bp, code_t c, int32_t *p) { + *p = *get_iop (bp, c + 1); + return get_iop (bp, c); +} + +static ALWAYS_INLINE int64_t *get_3iops (MIR_val_t *bp, code_t c, int64_t *p1, int64_t *p2) { + *p1 = *get_iop (bp, c + 1); + *p2 = *get_iop (bp, c + 2); + return get_iop (bp, c); +} + +static ALWAYS_INLINE int64_t *get_3isops (MIR_val_t *bp, code_t c, int32_t *p1, int32_t *p2) { + *p1 = *get_iop (bp, c + 1); + *p2 = *get_iop (bp, c + 2); + return get_iop (bp, c); +} + +static ALWAYS_INLINE uint64_t *get_3uops (MIR_val_t *bp, code_t c, uint64_t *p1, uint64_t *p2) { + *p1 = *get_uop (bp, c + 1); + *p2 = *get_uop (bp, c + 2); + return get_uop (bp, c); +} + +static ALWAYS_INLINE uint64_t *get_3usops (MIR_val_t *bp, code_t c, uint32_t *p1, uint32_t *p2) { + *p1 = *get_uop (bp, c + 1); + *p2 = *get_uop (bp, c + 2); + return get_uop (bp, c); +} + +static ALWAYS_INLINE float *get_2fops (MIR_val_t *bp, code_t c, float *p) { + *p = *get_fop (bp, c + 1); + return get_fop (bp, c); +} + +static ALWAYS_INLINE float *get_3fops (MIR_val_t *bp, code_t c, float *p1, float *p2) { + *p1 = *get_fop (bp, c + 1); + *p2 = *get_fop (bp, c + 2); + return get_fop (bp, c); +} + +static ALWAYS_INLINE int64_t *get_fcmp_ops (MIR_val_t *bp, code_t c, float *p1, float *p2) { + *p1 = *get_fop (bp, c + 1); + *p2 = *get_fop (bp, c + 2); + return get_iop (bp, c); +} + +static ALWAYS_INLINE double *get_2dops (MIR_val_t *bp, code_t c, double *p) { + *p = *get_dop (bp, c + 1); + return get_dop (bp, c); +} + +static ALWAYS_INLINE double *get_3dops (MIR_val_t *bp, code_t c, double *p1, double *p2) { + *p1 = *get_dop (bp, c + 1); + *p2 = *get_dop (bp, c + 2); + return get_dop (bp, c); +} + +static ALWAYS_INLINE int64_t *get_dcmp_ops (MIR_val_t *bp, code_t c, double *p1, double *p2) { + *p1 = *get_dop (bp, c + 1); + *p2 = *get_dop (bp, c + 2); + return get_iop (bp, c); +} + +static ALWAYS_INLINE long double *get_2ldops (MIR_val_t *bp, code_t c, long double *p) { + *p = *get_ldop (bp, c + 1); + return get_ldop (bp, c); +} + +static ALWAYS_INLINE long double *get_3ldops (MIR_val_t *bp, code_t c, long double *p1, + long double *p2) { + *p1 = *get_ldop (bp, c + 1); + *p2 = *get_ldop (bp, c + 2); + return get_ldop (bp, c); +} + +static ALWAYS_INLINE int64_t *get_ldcmp_ops (MIR_val_t *bp, code_t c, long double *p1, + long double *p2) { + *p1 = *get_ldop (bp, c + 1); + *p2 = *get_ldop (bp, c + 2); + return get_iop (bp, c); +} + +static ALWAYS_INLINE int64_t get_mem_addr (MIR_val_t *bp, code_t c) { return bp[get_i (c)].i; } + +#define EXT(tp) \ + do { \ + int64_t *r = get_iop (bp, ops); \ + tp s = *get_iop (bp, ops + 1); \ + *r = s; \ + } while (0) +#define IOP2(op) \ + do { \ + int64_t *r, p; \ + r = get_2iops (bp, ops, &p); \ + *r = op p; \ + } while (0) +#define IOP2S(op) \ + do { \ + int64_t *r; \ + int32_t p; \ + r = get_2isops (bp, ops, &p); \ + *r = op p; \ + } while (0) +#define IOP3(op) \ + do { \ + int64_t *r, p1, p2; \ + r = get_3iops (bp, ops, &p1, &p2); \ + *r = p1 op p2; \ + } while (0) +#define IOP3S(op) \ + do { \ + int64_t *r; \ + int32_t p1, p2; \ + r = get_3isops (bp, ops, &p1, &p2); \ + *r = p1 op p2; \ + } while (0) +#define ICMP(op) \ + do { \ + int64_t *r, p1, p2; \ + r = get_3iops (bp, ops, &p1, &p2); \ + *r = p1 op p2; \ + } while (0) +#define ICMPS(op) \ + do { \ + int64_t *r; \ + int32_t p1, p2; \ + r = get_3isops (bp, ops, &p1, &p2); \ + *r = p1 op p2; \ + } while (0) +#define BICMP(op) \ + do { \ + int64_t op1 = *get_iop (bp, ops + 1), op2 = *get_iop (bp, ops + 2); \ + if (op1 op op2) pc = code + get_i (ops); \ + } while (0) +#define BICMPS(op) \ + do { \ + int32_t op1 = *get_iop (bp, ops + 1), op2 = *get_iop (bp, ops + 2); \ + if (op1 op op2) pc = code + get_i (ops); \ + } while (0) +#define UOP3(op) \ + do { \ + uint64_t *r, p1, p2; \ + r = get_3uops (bp, ops, &p1, &p2); \ + *r = p1 op p2; \ + } while (0) +#define UOP3S(op) \ + do { \ + uint64_t *r; \ + uint32_t p1, p2; \ + r = get_3usops (bp, ops, &p1, &p2); \ + *r = p1 op p2; \ + } while (0) +#define UIOP3(op) \ + do { \ + uint64_t *r, p1, p2; \ + r = get_3uops (bp, ops, &p1, &p2); \ + *r = p1 op p2; \ + } while (0) +#define UIOP3S(op) \ + do { \ + uint64_t *r; \ + uint32_t p1, p2; \ + r = get_3usops (bp, ops, &p1, &p2); \ + *r = p1 op p2; \ + } while (0) +#define UCMP(op) \ + do { \ + uint64_t *r, p1, p2; \ + r = get_3uops (bp, ops, &p1, &p2); \ + *r = p1 op p2; \ + } while (0) +#define UCMPS(op) \ + do { \ + uint64_t *r; \ + uint32_t p1, p2; \ + r = get_3usops (bp, ops, &p1, &p2); \ + *r = p1 op p2; \ + } while (0) +#define BUCMP(op) \ + do { \ + uint64_t op1 = *get_uop (bp, ops + 1), op2 = *get_uop (bp, ops + 2); \ + if (op1 op op2) pc = code + get_i (ops); \ + } while (0) +#define BUCMPS(op) \ + do { \ + uint32_t op1 = *get_uop (bp, ops + 1), op2 = *get_uop (bp, ops + 2); \ + if (op1 op op2) pc = code + get_i (ops); \ + } while (0) + +#define FOP2(op) \ + do { \ + float *r, p; \ + r = get_2fops (bp, ops, &p); \ + *r = op p; \ + } while (0) +#define FOP3(op) \ + do { \ + float *r, p1, p2; \ + r = get_3fops (bp, ops, &p1, &p2); \ + *r = p1 op p2; \ + } while (0) +#define FCMP(op) \ + do { \ + int64_t *r; \ + float p1, p2; \ + r = get_fcmp_ops (bp, ops, &p1, &p2); \ + *r = p1 op p2; \ + } while (0) +#define BFCMP(op) \ + do { \ + float op1 = *get_fop (bp, ops + 1), op2 = *get_fop (bp, ops + 2); \ + if (op1 op op2) pc = code + get_i (ops); \ + } while (0) + +#define DOP2(op) \ + do { \ + double *r, p; \ + r = get_2dops (bp, ops, &p); \ + *r = op p; \ + } while (0) +#define DOP3(op) \ + do { \ + double *r, p1, p2; \ + r = get_3dops (bp, ops, &p1, &p2); \ + *r = p1 op p2; \ + } while (0) +#define DCMP(op) \ + do { \ + int64_t *r; \ + double p1, p2; \ + r = get_dcmp_ops (bp, ops, &p1, &p2); \ + *r = p1 op p2; \ + } while (0) +#define BDCMP(op) \ + do { \ + double op1 = *get_dop (bp, ops + 1), op2 = *get_dop (bp, ops + 2); \ + if (op1 op op2) pc = code + get_i (ops); \ + } while (0) + +#define LDOP2(op) \ + do { \ + long double *r, p; \ + r = get_2ldops (bp, ops, &p); \ + *r = op p; \ + } while (0) +#define LDOP3(op) \ + do { \ + long double *r, p1, p2; \ + r = get_3ldops (bp, ops, &p1, &p2); \ + *r = p1 op p2; \ + } while (0) +#define LDCMP(op) \ + do { \ + int64_t *r; \ + long double p1, p2; \ + r = get_ldcmp_ops (bp, ops, &p1, &p2); \ + *r = p1 op p2; \ + } while (0) +#define BLDCMP(op) \ + do { \ + long double op1 = *get_ldop (bp, ops + 1), op2 = *get_ldop (bp, ops + 2); \ + if (op1 op op2) pc = code + get_i (ops); \ + } while (0) + +#define LD(op, val_type, mem_type) \ + do { \ + val_type *r = get_##op (bp, ops); \ + int64_t a = get_mem_addr (bp, ops + 1); \ + *r = *((mem_type *) a); \ + } while (0) +#define ST(op, val_type, mem_type) \ + do { \ + val_type v = *get_##op (bp, ops); \ + int64_t a = get_mem_addr (bp, ops + 1); \ + *((mem_type *) a) = v; \ + } while (0) + +#if defined(__GNUC__) && !defined(__clang__) +#define OPTIMIZE __attribute__ ((__optimize__ ("O3"))) +#else +#define OPTIMIZE +#endif + +static void call (MIR_context_t ctx, MIR_val_t *bp, MIR_op_t *insn_arg_ops, code_t ffi_address_ptr, + MIR_item_t proto_item, void *addr, code_t res_ops, size_t nargs); + +#if MIR_INTERP_TRACE +static void start_insn_trace (MIR_context_t ctx, const char *name, func_desc_t func_desc, code_t pc, + size_t nops) { + struct interp_ctx *interp_ctx = ctx->interp_ctx; + MIR_insn_t insn = pc[1].a; + code_t ops = pc + 2; + + for (int i = 0; i < trace_insn_ident; i++) fprintf (stderr, " "); + fprintf (stderr, "%s", name); + for (size_t i = 0; i < nops; i++) { + fprintf (stderr, i == 0 ? "\t" : ", "); + fprintf (stderr, "%" PRId64, ops[i].i); + } + fprintf (stderr, "\t#"); + MIR_output_insn (ctx, stderr, insn, func_desc->func_item->u.func, FALSE); + fprintf (stderr, ""); +} + +static void finish_insn_trace (MIR_context_t ctx, MIR_insn_code_t code, code_t ops, MIR_val_t *bp) { + int out_p; + MIR_op_mode_t op_mode = MIR_OP_UNDEF; + + switch (code) { + case IC_LDI8: + case IC_LDU8: + case IC_LDI16: + case IC_LDU16: + case IC_LDI32: + case IC_LDU32: + case IC_LDI64: + case IC_MOVI: + case IC_MOVP: op_mode = MIR_OP_INT; break; + case IC_LDF: + case IC_MOVF: op_mode = MIR_OP_FLOAT; break; + case IC_LDD: + case IC_MOVD: op_mode = MIR_OP_DOUBLE; break; + case IC_LDLD: + case IC_MOVLD: op_mode = MIR_OP_LDOUBLE; break; + case IC_STI8: + case IC_STU8: + case IC_STI16: + case IC_STU16: + case IC_STI32: + case IC_STU32: + case IC_STI64: + case IC_STF: + case IC_STD:; + case IC_STLD: break; + case IC_IMM_CALL: break; + default: + op_mode = _MIR_insn_code_op_mode (ctx, code, 0, &out_p); + if (op_mode == MIR_OP_BOUND || !out_p) op_mode = MIR_OP_UNDEF; + break; + } + switch (op_mode) { + case MIR_OP_INT: + case MIR_OP_UINT: + fprintf (stderr, "\t# res = %" PRId64 " (%" PRIu64 "u, 0x%" PRIx64 ")", bp[ops[0].i].i, + bp[ops[0].i].u, bp[ops[0].i].u); + break; + case MIR_OP_FLOAT: fprintf (stderr, "\t# res = %.*ef", FLT_DECIMAL_DIG, bp[ops[0].i].f); break; + case MIR_OP_DOUBLE: fprintf (stderr, "\t# res = %.*e", DBL_DECIMAL_DIG, bp[ops[0].i].d); break; + case MIR_OP_LDOUBLE: + fprintf (stderr, "\t# res = %.*Le", LDBL_DECIMAL_DIG, bp[ops[0].i].ld); + break; + } + fprintf (stderr, "\n"); +} +#endif + +static code_t call_insn_execute (MIR_context_t ctx, code_t pc, MIR_val_t *bp, code_t ops, + int imm_p) { + struct interp_ctx *interp_ctx = ctx->interp_ctx; + int64_t nops = get_i (ops); /* #args w/o nop, insn, and ff interface address */ + MIR_insn_t insn = get_a (ops + 1); + MIR_item_t proto_item = get_a (ops + 3); + void *func_addr = imm_p ? get_a (ops + 4) : *get_aop (bp, ops + 4); + size_t start = proto_item->u.proto->nres + 5; + + if (VARR_EXPAND (MIR_val_t, arg_vals_varr, nops)) arg_vals = VARR_ADDR (MIR_val_t, arg_vals_varr); + + for (size_t i = start; i < nops + 3; i++) arg_vals[i - start] = bp[get_i (ops + i)]; + +#if MIR_INTERP_TRACE + trace_insn_ident += 2; +#endif + call (ctx, bp, &insn->ops[proto_item->u.proto->nres + 2] /* arg ops */, + ops + 2 /* ffi address holder */, proto_item, func_addr, ops + 5 /* results start */, + nops - start + 3 /* arg # */); +#if MIR_INTERP_TRACE + trace_insn_ident -= 2; +#endif + pc += nops + 3; /* nops itself, the call insn, add ff interface address */ + return pc; +} + +static void OPTIMIZE eval (MIR_context_t ctx, func_desc_t func_desc, MIR_val_t *bp, + MIR_val_t *results) { + struct interp_ctx *interp_ctx = ctx->interp_ctx; + code_t pc, ops, code = func_desc->code; + +#if MIR_INTERP_TRACE + MIR_full_insn_code_t trace_insn_code; +#define START_INSN(v, nops) \ + do { \ + trace_insn_code = v; \ + start_insn_trace (ctx, #v, func_desc, pc, nops); \ + ops = pc + 2; /* skip original insn too */ \ + pc += nops + 2; \ + } while (0) +#else +#define START_INSN(v, nops) \ + do { \ + ops = pc + 1; \ + pc += nops + 1; \ + } while (0) +#endif + +#if DIRECT_THREADED_DISPATCH + void **ltab = dispatch_label_tab; + +#define LAB_EL(i) ltab[i] = &&L_##i +#define REP_SEP ; + if (bp == NULL) { + REP4 (LAB_EL, MIR_MOV, MIR_FMOV, MIR_DMOV, MIR_LDMOV); + REP6 (LAB_EL, MIR_EXT8, MIR_EXT16, MIR_EXT32, MIR_UEXT8, MIR_UEXT16, MIR_UEXT32); + REP6 (LAB_EL, MIR_I2F, MIR_I2D, MIR_I2LD, MIR_UI2F, MIR_UI2D, MIR_UI2LD); + REP8 (LAB_EL, MIR_F2I, MIR_D2I, MIR_LD2I, MIR_F2D, MIR_F2LD, MIR_D2F, MIR_D2LD, MIR_LD2F); + REP8 (LAB_EL, MIR_LD2D, MIR_NEG, MIR_NEGS, MIR_FNEG, MIR_DNEG, MIR_LDNEG, MIR_ADD, MIR_ADDS); + REP8 (LAB_EL, MIR_FADD, MIR_DADD, MIR_LDADD, MIR_SUB, MIR_SUBS, MIR_FSUB, MIR_DSUB, MIR_LDSUB); + REP8 (LAB_EL, MIR_MUL, MIR_MULS, MIR_FMUL, MIR_DMUL, MIR_LDMUL, MIR_DIV, MIR_DIVS, MIR_UDIV); + REP8 (LAB_EL, MIR_UDIVS, MIR_FDIV, MIR_DDIV, MIR_LDDIV, MIR_MOD, MIR_MODS, MIR_UMOD, MIR_UMODS); + REP8 (LAB_EL, MIR_AND, MIR_ANDS, MIR_OR, MIR_ORS, MIR_XOR, MIR_XORS, MIR_LSH, MIR_LSHS); + REP8 (LAB_EL, MIR_RSH, MIR_RSHS, MIR_URSH, MIR_URSHS, MIR_EQ, MIR_EQS, MIR_FEQ, MIR_DEQ); + REP8 (LAB_EL, MIR_LDEQ, MIR_NE, MIR_NES, MIR_FNE, MIR_DNE, MIR_LDNE, MIR_LT, MIR_LTS); + REP8 (LAB_EL, MIR_ULT, MIR_ULTS, MIR_FLT, MIR_DLT, MIR_LDLT, MIR_LE, MIR_LES, MIR_ULE); + REP8 (LAB_EL, MIR_ULES, MIR_FLE, MIR_DLE, MIR_LDLE, MIR_GT, MIR_GTS, MIR_UGT, MIR_UGTS); + REP8 (LAB_EL, MIR_FGT, MIR_DGT, MIR_LDGT, MIR_GE, MIR_GES, MIR_UGE, MIR_UGES, MIR_FGE); + REP8 (LAB_EL, MIR_DGE, MIR_LDGE, MIR_JMP, MIR_BT, MIR_BTS, MIR_BF, MIR_BFS, MIR_BEQ); + REP8 (LAB_EL, MIR_BEQS, MIR_FBEQ, MIR_DBEQ, MIR_LDBEQ, MIR_BNE, MIR_BNES, MIR_FBNE, MIR_DBNE); + REP8 (LAB_EL, MIR_LDBNE, MIR_BLT, MIR_BLTS, MIR_UBLT, MIR_UBLTS, MIR_FBLT, MIR_DBLT, MIR_LDBLT); + REP8 (LAB_EL, MIR_BLE, MIR_BLES, MIR_UBLE, MIR_UBLES, MIR_FBLE, MIR_DBLE, MIR_LDBLE, MIR_BGT); + REP8 (LAB_EL, MIR_BGTS, MIR_UBGT, MIR_UBGTS, MIR_FBGT, MIR_DBGT, MIR_LDBGT, MIR_BGE, MIR_BGES); + REP5 (LAB_EL, MIR_UBGE, MIR_UBGES, MIR_FBGE, MIR_DBGE, MIR_LDBGE); + REP4 (LAB_EL, MIR_CALL, MIR_INLINE, MIR_SWITCH, MIR_RET); + REP6 (LAB_EL, MIR_ALLOCA, MIR_BSTART, MIR_BEND, MIR_VA_ARG, MIR_VA_START, MIR_VA_END); + REP8 (LAB_EL, IC_LDI8, IC_LDU8, IC_LDI16, IC_LDU16, IC_LDI32, IC_LDU32, IC_LDI64, IC_LDF); + REP8 (LAB_EL, IC_LDD, IC_LDLD, IC_STI8, IC_STU8, IC_STI16, IC_STU16, IC_STI32, IC_STU32); + REP8 (LAB_EL, IC_STI64, IC_STF, IC_STD, IC_STLD, IC_MOVI, IC_MOVP, IC_MOVF, IC_MOVD); + REP2 (LAB_EL, IC_MOVLD, IC_IMM_CALL); + return; + } +#undef REP_SEP +#define CASE(value, nops) L_##value : START_INSN (value, nops); + +#if MIR_INTERP_TRACE +#define END_INSN \ + finish_insn_trace (ctx, trace_insn_code, ops, bp); \ + goto * pc->a +#else +#define END_INSN goto * pc->a +#endif + +#else + +#define CASE(value, nops) \ + case value: \ + START_INSN (value, nops); + +#if MIR_INTERP_TRACE +#define END_INSN \ + finish_insn_trace (ctx, trace_insn_code, ops, bp); \ + break +#else +#define END_INSN break +#endif + +#endif + +#define SCASE(insn, nop, stmt) \ + CASE (insn, nop) { \ + stmt; \ + END_INSN; \ + } + + pc = code; + +#if DIRECT_THREADED_DISPATCH + goto * pc->a; +#else + for (;;) { + MIR_insn_code_t insn_code = pc->ic; + switch (insn_code) { +#endif + + CASE (MIR_MOV, 2) { + int64_t p, *r = get_2iops (bp, ops, &p); + *r = p; + END_INSN; + } + CASE (MIR_FMOV, 2) { + float p, *r = get_2fops (bp, ops, &p); + *r = p; + END_INSN; + } + CASE (MIR_DMOV, 2) { + double p, *r = get_2dops (bp, ops, &p); + *r = p; + END_INSN; + } + CASE (MIR_LDMOV, 2) { + long double p, *r = get_2ldops (bp, ops, &p); + *r = p; + END_INSN; + } + SCASE (MIR_EXT8, 2, EXT (int8_t)); + SCASE (MIR_EXT16, 2, EXT (int16_t)); + SCASE (MIR_EXT32, 2, EXT (int32_t)); + SCASE (MIR_UEXT8, 2, EXT (uint8_t)); + SCASE (MIR_UEXT16, 2, EXT (uint16_t)); + SCASE (MIR_UEXT32, 2, EXT (uint32_t)); + CASE (MIR_I2F, 2) { + float *r = get_fop (bp, ops); + int64_t i = *get_iop (bp, ops + 1); + + *r = i; + END_INSN; + } + CASE (MIR_I2D, 2) { + double *r = get_dop (bp, ops); + int64_t i = *get_iop (bp, ops + 1); + + *r = i; + END_INSN; + } + CASE (MIR_I2LD, 2) { + long double *r = get_ldop (bp, ops); + int64_t i = *get_iop (bp, ops + 1); + + *r = i; + END_INSN; + } + + CASE (MIR_UI2F, 2) { + float *r = get_fop (bp, ops); + uint64_t i = *get_iop (bp, ops + 1); + + *r = i; + END_INSN; + } + CASE (MIR_UI2D, 2) { + double *r = get_dop (bp, ops); + uint64_t i = *get_iop (bp, ops + 1); + + *r = i; + END_INSN; + } + CASE (MIR_UI2LD, 2) { + long double *r = get_ldop (bp, ops); + uint64_t i = *get_iop (bp, ops + 1); + + *r = i; + END_INSN; + } + + CASE (MIR_F2I, 2) { + int64_t *r = get_iop (bp, ops); + float f = *get_fop (bp, ops + 1); + + *r = f; + END_INSN; + } + CASE (MIR_D2I, 2) { + int64_t *r = get_iop (bp, ops); + double d = *get_dop (bp, ops + 1); + + *r = d; + END_INSN; + } + CASE (MIR_LD2I, 2) { + int64_t *r = get_iop (bp, ops); + long double ld = *get_ldop (bp, ops + 1); + + *r = ld; + END_INSN; + } + + CASE (MIR_F2D, 2) { + double *r = get_dop (bp, ops); + float f = *get_fop (bp, ops + 1); + *r = f; + END_INSN; + } + CASE (MIR_F2LD, 2) { + long double *r = get_ldop (bp, ops); + float f = *get_fop (bp, ops + 1); + + *r = f; + END_INSN; + } + CASE (MIR_D2F, 2) { + float *r = get_fop (bp, ops); + double d = *get_dop (bp, ops + 1); + + *r = d; + END_INSN; + } + CASE (MIR_D2LD, 2) { + long double *r = get_ldop (bp, ops); + double d = *get_dop (bp, ops + 1); + + *r = d; + END_INSN; + } + CASE (MIR_LD2F, 2) { + float *r = get_fop (bp, ops); + long double ld = *get_ldop (bp, ops + 1); + + *r = ld; + END_INSN; + } + + CASE (MIR_LD2D, 2) { + double *r = get_dop (bp, ops); + long double ld = *get_ldop (bp, ops + 1); + + *r = ld; + END_INSN; + } + + SCASE (MIR_NEG, 2, IOP2 (-)); + SCASE (MIR_NEGS, 2, IOP2S (-)); + SCASE (MIR_FNEG, 2, FOP2 (-)); + SCASE (MIR_DNEG, 2, DOP2 (-)); + SCASE (MIR_LDNEG, 2, LDOP2 (-)); + + SCASE (MIR_ADD, 3, IOP3 (+)); + SCASE (MIR_ADDS, 3, IOP3S (+)); + SCASE (MIR_FADD, 3, FOP3 (+)); + SCASE (MIR_DADD, 3, DOP3 (+)); + SCASE (MIR_LDADD, 3, LDOP3 (+)); + + SCASE (MIR_SUB, 3, IOP3 (-)); + SCASE (MIR_SUBS, 3, IOP3S (-)); + SCASE (MIR_FSUB, 3, FOP3 (-)); + SCASE (MIR_DSUB, 3, DOP3 (-)); + SCASE (MIR_LDSUB, 3, LDOP3 (-)); + + SCASE (MIR_MUL, 3, IOP3 (*)); + SCASE (MIR_MULS, 3, IOP3S (*)); + SCASE (MIR_FMUL, 3, FOP3 (*)); + SCASE (MIR_DMUL, 3, DOP3 (*)); + SCASE (MIR_LDMUL, 3, LDOP3 (*)); + + SCASE (MIR_DIV, 3, IOP3 (/)); + SCASE (MIR_DIVS, 3, IOP3S (/)); + SCASE (MIR_UDIV, 3, UOP3 (/)); + SCASE (MIR_UDIVS, 3, UOP3S (/)); + SCASE (MIR_FDIV, 3, FOP3 (/)); + SCASE (MIR_DDIV, 3, DOP3 (/)); + SCASE (MIR_LDDIV, 3, LDOP3 (/)); + + SCASE (MIR_MOD, 3, IOP3 (%)); + SCASE (MIR_MODS, 3, IOP3S (%)); + SCASE (MIR_UMOD, 3, UOP3 (%)); + SCASE (MIR_UMODS, 3, UOP3S (%)); + + SCASE (MIR_AND, 3, IOP3 (&)); + SCASE (MIR_ANDS, 3, IOP3S (&)); + SCASE (MIR_OR, 3, IOP3 (|)); + SCASE (MIR_ORS, 3, IOP3S (|)); + SCASE (MIR_XOR, 3, IOP3 (^)); + SCASE (MIR_XORS, 3, IOP3S (^)); + SCASE (MIR_LSH, 3, IOP3 (<<)); + SCASE (MIR_LSHS, 3, IOP3S (<<)); + + SCASE (MIR_RSH, 3, IOP3 (>>)); + SCASE (MIR_RSHS, 3, IOP3S (>>)); + SCASE (MIR_URSH, 3, UIOP3 (>>)); + SCASE (MIR_URSHS, 3, UIOP3S (>>)); + + SCASE (MIR_EQ, 3, ICMP (==)); + SCASE (MIR_EQS, 3, ICMPS (==)); + SCASE (MIR_FEQ, 3, FCMP (==)); + SCASE (MIR_DEQ, 3, DCMP (==)); + SCASE (MIR_LDEQ, 3, LDCMP (==)); + + SCASE (MIR_NE, 3, ICMP (!=)); + SCASE (MIR_NES, 3, ICMPS (!=)); + SCASE (MIR_FNE, 3, FCMP (!=)); + SCASE (MIR_DNE, 3, DCMP (!=)); + SCASE (MIR_LDNE, 3, LDCMP (!=)); + + SCASE (MIR_LT, 3, ICMP (<)); + SCASE (MIR_LTS, 3, ICMPS (<)); + SCASE (MIR_ULT, 3, UCMP (<)); + SCASE (MIR_ULTS, 3, UCMPS (<)); + SCASE (MIR_FLT, 3, FCMP (<)); + SCASE (MIR_DLT, 3, DCMP (<)); + SCASE (MIR_LDLT, 3, LDCMP (<)); + + SCASE (MIR_LE, 3, ICMP (<=)); + SCASE (MIR_LES, 3, ICMPS (<=)); + SCASE (MIR_ULE, 3, UCMP (<=)); + SCASE (MIR_ULES, 3, UCMPS (<=)); + SCASE (MIR_FLE, 3, FCMP (<=)); + SCASE (MIR_DLE, 3, DCMP (<=)); + SCASE (MIR_LDLE, 3, LDCMP (<=)); + + SCASE (MIR_GT, 3, ICMP (>)); + SCASE (MIR_GTS, 3, ICMPS (>)); + SCASE (MIR_UGT, 3, UCMP (>)); + SCASE (MIR_UGTS, 3, UCMPS (>)); + SCASE (MIR_FGT, 3, FCMP (>)); + SCASE (MIR_DGT, 3, DCMP (>)); + SCASE (MIR_LDGT, 3, LDCMP (>)); + + SCASE (MIR_GE, 3, ICMP (>=)); + SCASE (MIR_GES, 3, ICMPS (>=)); + SCASE (MIR_UGE, 3, UCMP (>=)); + SCASE (MIR_UGES, 3, UCMPS (>=)); + SCASE (MIR_FGE, 3, FCMP (>=)); + SCASE (MIR_DGE, 3, DCMP (>=)); + SCASE (MIR_LDGE, 3, LDCMP (>=)); + + SCASE (MIR_JMP, 1, pc = code + get_i (ops)); + CASE (MIR_BT, 2) { + int64_t cond = *get_iop (bp, ops + 1); + + if (cond) pc = code + get_i (ops); + END_INSN; + } + CASE (MIR_BF, 2) { + int64_t cond = *get_iop (bp, ops + 1); + + if (!cond) pc = code + get_i (ops); + END_INSN; + } + CASE (MIR_BTS, 2) { + int32_t cond = *get_iop (bp, ops + 1); + + if (cond) pc = code + get_i (ops); + END_INSN; + } + CASE (MIR_BFS, 2) { + int32_t cond = *get_iop (bp, ops + 1); + + if (!cond) pc = code + get_i (ops); + END_INSN; + } + SCASE (MIR_BEQ, 3, BICMP (==)); + SCASE (MIR_BEQS, 3, BICMPS (==)); + SCASE (MIR_FBEQ, 3, BFCMP (==)); + SCASE (MIR_DBEQ, 3, BDCMP (==)); + SCASE (MIR_LDBEQ, 3, BLDCMP (==)); + SCASE (MIR_BNE, 3, BICMP (!=)); + SCASE (MIR_BNES, 3, BICMPS (!=)); + SCASE (MIR_FBNE, 3, BFCMP (!=)); + SCASE (MIR_DBNE, 3, BDCMP (!=)); + SCASE (MIR_LDBNE, 3, BLDCMP (!=)); + SCASE (MIR_BLT, 3, BICMP (<)); + SCASE (MIR_BLTS, 3, BICMPS (<)); + SCASE (MIR_UBLT, 3, BUCMP (<)); + SCASE (MIR_UBLTS, 3, BUCMPS (<)); + SCASE (MIR_FBLT, 3, BFCMP (<)); + SCASE (MIR_DBLT, 3, BDCMP (<)); + SCASE (MIR_LDBLT, 3, BLDCMP (<)); + SCASE (MIR_BLE, 3, BICMP (<=)); + SCASE (MIR_BLES, 3, BICMPS (<=)); + SCASE (MIR_UBLE, 3, BUCMP (<=)); + SCASE (MIR_UBLES, 3, BUCMPS (<=)); + SCASE (MIR_FBLE, 3, BFCMP (<=)); + SCASE (MIR_DBLE, 3, BDCMP (<=)); + SCASE (MIR_LDBLE, 3, BLDCMP (<=)); + SCASE (MIR_BGT, 3, BICMP (>)); + SCASE (MIR_BGTS, 3, BICMPS (>)); + SCASE (MIR_UBGT, 3, BUCMP (>)); + SCASE (MIR_UBGTS, 3, BUCMPS (>)); + SCASE (MIR_FBGT, 3, BFCMP (>)); + SCASE (MIR_DBGT, 3, BDCMP (>)); + SCASE (MIR_LDBGT, 3, BLDCMP (>)); + SCASE (MIR_BGE, 3, BICMP (>=)); + SCASE (MIR_BGES, 3, BICMPS (>=)); + SCASE (MIR_UBGE, 3, BUCMP (>=)); + SCASE (MIR_UBGES, 3, BUCMPS (>=)); + SCASE (MIR_FBGE, 3, BFCMP (>=)); + SCASE (MIR_DBGE, 3, BDCMP (>=)); + SCASE (MIR_LDBGE, 3, BLDCMP (>=)); + + SCASE (MIR_CALL, 0, pc = call_insn_execute (ctx, pc, bp, ops, FALSE)); + SCASE (IC_IMM_CALL, 0, pc = call_insn_execute (ctx, pc, bp, ops, TRUE)); + SCASE (MIR_INLINE, 0, mir_assert (FALSE)); + + CASE (MIR_SWITCH, 0) { + int64_t nops = get_i (ops); /* #ops */ + int64_t index = *get_iop (bp, ops + 1); + + mir_assert (index + 1 < nops); + pc = code + get_i (ops + index + 2); + END_INSN; + } + + CASE (MIR_RET, 0) { + int64_t nops = get_i (ops); /* #ops */ + + for (int64_t i = 0; i < nops; i++) results[i] = bp[get_i (ops + i + 1)]; + pc += nops + 1; + return; + END_INSN; + } + + CASE (MIR_ALLOCA, 2) { + int64_t *r, s; + + r = get_2iops (bp, ops, &s); + *r = (uint64_t) alloca (s); + END_INSN; + } + CASE (MIR_BSTART, 1) { + void **p = get_aop (bp, ops); + + *p = bstart_builtin (); + END_INSN; + } + SCASE (MIR_BEND, 1, bend_builtin (*get_aop (bp, ops))); + CASE (MIR_VA_ARG, 3) { + int64_t *r, va, tp; + + r = get_2iops (bp, ops, &va); + tp = get_i (ops + 2); + *r = (uint64_t) va_arg_builtin ((void *) va, tp); + END_INSN; + } + SCASE (MIR_VA_START, 1, va_start_interp_builtin (ctx, bp[get_i (ops)].a, bp[-1].a)); + SCASE (MIR_VA_END, 1, va_end_interp_builtin (ctx, bp[get_i (ops)].a)); + + SCASE (IC_LDI8, 2, LD (iop, int64_t, int8_t)); + SCASE (IC_LDU8, 2, LD (uop, uint64_t, uint8_t)); + SCASE (IC_LDI16, 2, LD (iop, int64_t, int16_t)); + SCASE (IC_LDU16, 2, LD (uop, uint64_t, uint16_t)); + SCASE (IC_LDI32, 2, LD (iop, int64_t, int32_t)); + SCASE (IC_LDU32, 2, LD (uop, uint64_t, uint32_t)); + SCASE (IC_LDI64, 2, LD (iop, int64_t, int64_t)); + SCASE (IC_LDF, 2, LD (fop, float, float)); + SCASE (IC_LDD, 2, LD (dop, double, double)); + SCASE (IC_LDLD, 2, LD (ldop, long double, long double)); + CASE (IC_MOVP, 2) { + void **r = get_aop (bp, ops), *a = get_a (ops + 1); + *r = a; + END_INSN; + } + SCASE (IC_STI8, 2, ST (iop, int64_t, int8_t)); + SCASE (IC_STU8, 2, ST (iop, uint64_t, uint8_t)); + SCASE (IC_STI16, 2, ST (iop, int64_t, int16_t)); + SCASE (IC_STU16, 2, ST (iop, uint64_t, uint16_t)); + SCASE (IC_STI32, 2, ST (iop, int64_t, int32_t)); + SCASE (IC_STU32, 2, ST (iop, uint64_t, uint32_t)); + SCASE (IC_STI64, 2, ST (iop, int64_t, int64_t)); + SCASE (IC_STF, 2, ST (fop, float, float)); + SCASE (IC_STD, 2, ST (dop, double, double)); + SCASE (IC_STLD, 2, ST (ldop, long double, long double)); + CASE (IC_MOVI, 2) { + int64_t *r = get_iop (bp, ops), imm = get_i (ops + 1); + *r = imm; + END_INSN; + } + CASE (IC_MOVF, 2) { + float *r = get_fop (bp, ops), imm = get_f (ops + 1); + *r = imm; + END_INSN; + } + CASE (IC_MOVD, 2) { + double *r = get_dop (bp, ops), imm = get_d (ops + 1); + *r = imm; + END_INSN; + } + CASE (IC_MOVLD, 2) { + long double *r = get_ldop (bp, ops), imm = get_ld (ops + 1); + *r = imm; + END_INSN; + } +#if !DIRECT_THREADED_DISPATCH +default: mir_assert (FALSE); +} +} +#endif +} + +static inline func_desc_t get_func_desc (MIR_item_t func_item) { + mir_assert (func_item->item_type == MIR_func_item); + return func_item->data; +} + +static htab_hash_t ff_interface_hash (ff_interface_t i) { + return mir_hash_finish ( + mir_hash_step (mir_hash_step (mir_hash_step (mir_hash_init (0), i->nres), i->nargs), + mir_hash (i->res_types, sizeof (MIR_type_t) * i->nres, + mir_hash (i->arg_types, sizeof (MIR_type_t) * i->nargs, 42)))); +} + +static int ff_interface_eq (ff_interface_t i1, ff_interface_t i2) { + return (i1->nres == i2->nres && i1->nargs == i2->nargs + && memcmp (i1->res_types, i2->res_types, sizeof (MIR_type_t) * i1->nres) == 0 + && memcmp (i1->arg_types, i2->arg_types, sizeof (MIR_type_t) * i1->nargs) == 0); +} + +static void ff_interface_clear (ff_interface_t ffi) { free (ffi); } + +static void *get_ff_interface (MIR_context_t ctx, size_t nres, MIR_type_t *res_types, size_t nargs, + MIR_type_t *arg_types) { + struct interp_ctx *interp_ctx = ctx->interp_ctx; + struct ff_interface ffi_s; + ff_interface_t tab_ffi, ffi; + int htab_res; + + ffi_s.nres = nres; + ffi_s.nargs = nargs; + ffi_s.res_types = res_types; + ffi_s.arg_types = arg_types; + if (HTAB_DO (ff_interface_t, ff_interface_tab, &ffi_s, HTAB_FIND, tab_ffi)) + return tab_ffi->interface_addr; + ffi = malloc (sizeof (struct ff_interface) + sizeof (MIR_type_t) * (nres + nargs)); + ffi->nres = nres; + ffi->nargs = nargs; + ffi->res_types = (MIR_type_t *) ((char *) ffi + sizeof (struct ff_interface)); + ffi->arg_types = ffi->res_types + nres; + memcpy (ffi->res_types, res_types, sizeof (MIR_type_t) * nres); + memcpy (ffi->arg_types, arg_types, sizeof (MIR_type_t) * nargs); + ffi->interface_addr = _MIR_get_ff_call (ctx, nres, res_types, nargs, call_arg_types); + htab_res = HTAB_DO (ff_interface_t, ff_interface_tab, ffi, HTAB_INSERT, tab_ffi); + mir_assert (!htab_res && ffi == tab_ffi); + return ffi->interface_addr; +} + +static void call (MIR_context_t ctx, MIR_val_t *bp, MIR_op_t *insn_arg_ops, code_t ffi_address_ptr, + MIR_item_t proto_item, void *addr, code_t res_ops, size_t nargs) { + struct interp_ctx *interp_ctx = ctx->interp_ctx; + size_t i, arg_vars_num, nres; + MIR_val_t *res; + MIR_type_t type; + MIR_var_t *arg_vars = NULL; + MIR_proto_t proto = proto_item->u.proto; + MIR_op_mode_t mode; + void *ff_interface_addr; + + if (proto->args == NULL) { + mir_assert (nargs == 0 && !proto->vararg_p); + arg_vars_num = 0; + } else { + mir_assert (nargs >= VARR_LENGTH (MIR_var_t, proto->args) + && (proto->vararg_p || nargs == VARR_LENGTH (MIR_var_t, proto->args))); + arg_vars = VARR_ADDR (MIR_var_t, proto->args); + arg_vars_num = VARR_LENGTH (MIR_var_t, proto->args); + } + nres = proto->nres; + if (VARR_EXPAND (MIR_val_t, call_res_args_varr, nargs + nres) + || VARR_EXPAND (MIR_type_t, call_arg_types_varr, nargs + nres)) { + call_res_args = VARR_ADDR (MIR_val_t, call_res_args_varr); + call_arg_types = VARR_ADDR (MIR_type_t, call_arg_types_varr); + } + if ((ff_interface_addr = ffi_address_ptr->a) == NULL) { + for (i = 0; i < nargs; i++) { + if (i < arg_vars_num) { + call_arg_types[i] = arg_vars[i].type; + continue; + } + mode = insn_arg_ops[i].value_mode; + mir_assert (mode == MIR_OP_INT || mode == MIR_OP_UINT || mode == MIR_OP_FLOAT + || mode == MIR_OP_DOUBLE || mode == MIR_OP_LDOUBLE); + if (mode == MIR_OP_FLOAT) + (*MIR_get_error_func (ctx)) (MIR_call_op_error, + "passing float variadic arg (should be passed as double)"); + call_arg_types[i] + = (mode == MIR_OP_DOUBLE ? MIR_T_D : mode == MIR_OP_LDOUBLE ? MIR_T_LD : MIR_T_I64); + } + ff_interface_addr = ffi_address_ptr->a + = get_ff_interface (ctx, nres, proto->res_types, nargs, call_arg_types); + } + + for (i = 0; i < nargs; i++) { + if (i >= arg_vars_num) { + call_res_args[i + nres] = arg_vals[i]; + continue; + } + type = arg_vars[i].type; + switch (type) { + case MIR_T_I8: call_res_args[i + nres].i = (int8_t) (arg_vals[i].i); break; + case MIR_T_U8: call_res_args[i + nres].u = (uint8_t) (arg_vals[i].i); break; + case MIR_T_I16: call_res_args[i + nres].i = (int16_t) (arg_vals[i].i); break; + case MIR_T_U16: call_res_args[i + nres].u = (uint16_t) (arg_vals[i].i); break; + case MIR_T_I32: call_res_args[i + nres].i = (int32_t) (arg_vals[i].i); break; + case MIR_T_U32: call_res_args[i + nres].u = (uint32_t) (arg_vals[i].i); break; + case MIR_T_I64: call_res_args[i + nres].i = (int64_t) (arg_vals[i].i); break; + case MIR_T_U64: call_res_args[i + nres].u = (uint64_t) (arg_vals[i].i); break; + case MIR_T_F: call_res_args[i + nres].f = arg_vals[i].f; break; + case MIR_T_D: call_res_args[i + nres].d = arg_vals[i].d; break; + case MIR_T_LD: call_res_args[i + nres].ld = arg_vals[i].ld; break; + case MIR_T_P: call_res_args[i + nres].u = (uint64_t) arg_vals[i].a; break; + default: mir_assert (FALSE); + } + } + ((void (*) (void *, void *)) ff_interface_addr) (addr, call_res_args); /* call */ + for (i = 0; i < nres; i++) { + res = &bp[get_i (res_ops + i)]; + switch (proto->res_types[i]) { + case MIR_T_I8: res->i = (int8_t) (call_res_args[i].i); break; + case MIR_T_U8: res->u = (uint8_t) (call_res_args[i].u); break; + case MIR_T_I16: res->i = (int16_t) (call_res_args[i].i); break; + case MIR_T_U16: res->u = (uint16_t) (call_res_args[i].u); break; + case MIR_T_I32: res->i = (int32_t) (call_res_args[i].i); break; + case MIR_T_U32: res->u = (uint32_t) (call_res_args[i].u); break; + case MIR_T_I64: res->i = (int64_t) (call_res_args[i].i); break; + case MIR_T_U64: res->u = (uint64_t) (call_res_args[i].u); break; + case MIR_T_F: res->f = call_res_args[i].f; break; + case MIR_T_D: res->d = call_res_args[i].d; break; + case MIR_T_LD: res->ld = call_res_args[i].ld; break; + case MIR_T_P: res->a = call_res_args[i].a; break; + default: mir_assert (FALSE); + } + } +} + +static void interp_init (MIR_context_t ctx) { + struct interp_ctx *interp_ctx; + + if ((interp_ctx = ctx->interp_ctx = malloc (sizeof (struct interp_ctx))) == NULL) + (*error_func) (MIR_alloc_error, "Not enough memory for ctx"); +#if DIRECT_THREADED_DISPATCH + eval (ctx, NULL, NULL, NULL); +#endif + VARR_CREATE (MIR_insn_t, branches, 0); + VARR_CREATE (MIR_val_t, code_varr, 0); + VARR_CREATE (MIR_val_t, arg_vals_varr, 0); + arg_vals = VARR_ADDR (MIR_val_t, arg_vals_varr); + VARR_CREATE (MIR_val_t, call_res_args_varr, 0); + VARR_CREATE (MIR_type_t, call_arg_types_varr, 0); + call_res_args = VARR_ADDR (MIR_val_t, call_res_args_varr); + call_arg_types = VARR_ADDR (MIR_type_t, call_arg_types_varr); + HTAB_CREATE_WITH_FREE_FUNC (ff_interface_t, ff_interface_tab, 1000, ff_interface_hash, + ff_interface_eq, ff_interface_clear); +#if MIR_INTERP_TRACE + trace_insn_ident = 0; +#endif + bstart_builtin = _MIR_get_bstart_builtin (ctx); + bend_builtin = _MIR_get_bend_builtin (ctx); +} + +static void interp_finish (MIR_context_t ctx) { + struct interp_ctx *interp_ctx = ctx->interp_ctx; + + VARR_DESTROY (MIR_insn_t, branches); + VARR_DESTROY (MIR_val_t, code_varr); + VARR_DESTROY (MIR_val_t, arg_vals_varr); + VARR_DESTROY (MIR_val_t, call_res_args_varr); + VARR_DESTROY (MIR_type_t, call_arg_types_varr); + HTAB_DESTROY (ff_interface_t, ff_interface_tab); + /* Clear func descs??? */ + free (ctx->interp_ctx); + ctx->interp_ctx = NULL; +} + +static void interp_arr_varg (MIR_context_t ctx, MIR_item_t func_item, MIR_val_t *results, + size_t nargs, MIR_val_t *vals, va_list va) { + func_desc_t func_desc; + MIR_val_t *bp; + + mir_assert (func_item->item_type == MIR_func_item); + if (func_item->data == NULL) generate_icode (ctx, func_item); + func_desc = get_func_desc (func_item); + bp = alloca ((func_desc->nregs + 1) * sizeof (MIR_val_t)); + bp[0].a = va; + bp++; + if (func_desc->nregs < nargs + 1) nargs = func_desc->nregs - 1; + bp[0].i = 0; + memcpy (&bp[1], vals, sizeof (MIR_val_t) * nargs); + eval (ctx, func_desc, bp, results); + if (va != NULL) va_end (va); +} + +void MIR_interp (MIR_context_t ctx, MIR_item_t func_item, MIR_val_t *results, size_t nargs, ...) { + struct interp_ctx *interp_ctx = ctx->interp_ctx; + va_list argp; + size_t i; + + if (VARR_EXPAND (MIR_val_t, arg_vals_varr, nargs)) + arg_vals = VARR_ADDR (MIR_val_t, arg_vals_varr); + va_start (argp, nargs); + for (i = 0; i < nargs; i++) arg_vals[i] = va_arg (argp, MIR_val_t); + interp_arr_varg (ctx, func_item, results, nargs, arg_vals, argp); +} + +void MIR_interp_arr_varg (MIR_context_t ctx, MIR_item_t func_item, MIR_val_t *results, size_t nargs, + MIR_val_t *vals, va_list va) { + func_desc_t func_desc; + MIR_val_t *bp; + + mir_assert (func_item->item_type == MIR_func_item); + if (func_item->data == NULL) generate_icode (ctx, func_item); + func_desc = get_func_desc (func_item); + bp = alloca ((func_desc->nregs + 1) * sizeof (MIR_val_t)); + bp[0].a = va; + bp++; + if (func_desc->nregs < nargs + 1) nargs = func_desc->nregs - 1; + bp[0].i = 0; + memcpy (&bp[1], vals, sizeof (MIR_val_t) * nargs); + eval (ctx, func_desc, bp, results); +} + +void MIR_interp_arr (MIR_context_t ctx, MIR_item_t func_item, MIR_val_t *results, size_t nargs, + MIR_val_t *vals) { + interp_arr_varg (ctx, func_item, results, nargs, vals, NULL); +} + +/* C call interface to interpreter. It is based on knowledge of + common vararg implementation. For some targets it might not + work. */ +static void interp (MIR_context_t ctx, MIR_item_t func_item, va_list va, MIR_val_t *results) { + struct interp_ctx *interp_ctx = ctx->interp_ctx; + size_t nargs; + MIR_var_t *arg_vars; + MIR_func_t func = func_item->u.func; + + nargs = func->nargs; + arg_vars = VARR_ADDR (MIR_var_t, func->vars); + if (VARR_EXPAND (MIR_val_t, arg_vals_varr, nargs)) + arg_vals = VARR_ADDR (MIR_val_t, arg_vals_varr); + for (size_t i = 0; i < nargs; i++) { + MIR_type_t type = arg_vars[i].type; + switch (type) { + case MIR_T_I8: arg_vals[i].i = (int8_t) va_arg (va, int32_t); break; + case MIR_T_I16: arg_vals[i].i = (int16_t) va_arg (va, int32_t); break; + case MIR_T_I32: arg_vals[i].i = va_arg (va, int32_t); break; + case MIR_T_I64: arg_vals[i].i = va_arg (va, int64_t); break; + case MIR_T_U8: arg_vals[i].i = (uint8_t) va_arg (va, uint32_t); break; + case MIR_T_U16: arg_vals[i].i = (uint16_t) va_arg (va, uint32_t); break; + case MIR_T_U32: arg_vals[i].i = va_arg (va, uint32_t); break; + case MIR_T_U64: arg_vals[i].i = va_arg (va, uint64_t); break; + case MIR_T_F: { + union { + double d; + float f; + } u; + u.d = va_arg (va, double); + arg_vals[i].f = u.f; + break; + } + case MIR_T_D: arg_vals[i].d = va_arg (va, double); break; + case MIR_T_LD: arg_vals[i].ld = va_arg (va, long double); break; + case MIR_T_P: arg_vals[i].a = va_arg (va, void *); break; + default: mir_assert (FALSE); + } + } + interp_arr_varg (ctx, func_item, results, nargs, arg_vals, va); +} + +static void redirect_interface_to_interp (MIR_context_t ctx, MIR_item_t func_item) { + _MIR_redirect_thunk (ctx, func_item->addr, _MIR_get_interp_shim (ctx, func_item, interp)); +} + +void MIR_set_interp_interface (MIR_context_t ctx, MIR_item_t func_item) { + redirect_interface_to_interp (ctx, func_item); +} diff --git a/mir/mir-reduce.h b/mir/mir-reduce.h new file mode 100644 index 0000000..f20aec5 --- /dev/null +++ b/mir/mir-reduce.h @@ -0,0 +1,463 @@ +/* This file is a part of MIR project. + Copyright (C) 2018, 2019 Vladimir Makarov . +*/ + +#ifndef MIR_REDUCE_H +#define MIR_REDUCE_H + +/* Data compression. Major goals are simplicity, fast decompression + speed, moderate compression speed. The algorithm is tuned for + binary MIR compression and close to LZ4. Only we use a bit + different format and offsets in symbol numbers instead of just + offsets. + + A better compression (on par with LZ4) could be achieved by adding + elements for all positions (now positions inside referenced symbols + are excluded) or/and increasing the buffer or/and increasing the + table. But it would slow down the compression or/and increase the + used memory. + + Functions reduce_encode, reduce_decode, reduce_encode_start, + reduce_encode_put, reduce_encode_finish, reduce_decode_start, + reduce_decode_get, reduce_decode_finish are the only interface + functions. + + Format of compressed data: "MIR", elements*, zero byte, 8-byte check hash in little endian form + Format of element: + o 8 bits tag + (N bits for symbol length; 0 means no sym, 2^N -1 means symbol length as uint present; + (8-N) bits for reference length; 0 means no ref, 2^(8-N) - 1 means length as uint present) + o [uint for symbol lenght]*, symbol string, + o [uint for ref len]*, symbol number as uint */ + +#include +#include +#include +#include "mir-hash.h" + +#define FALSE 0 +#define TRUE 1 +#define _REDUCE_DATA_PREFIX "MIR" /* first chars of compressed data */ +#define _REDUCE_SYMB_TAG_LEN 3 /* for some application could be 4 */ +#define _REDUCE_SYMB_TAG_LONG ((1 << _REDUCE_SYMB_TAG_LEN) - 1) /* should be not changed */ +#define _REDUCE_REF_TAG_LEN (8 - _REDUCE_SYMB_TAG_LEN) +#define _REDUCE_REF_TAG_LONG ((1 << _REDUCE_REF_TAG_LEN) - 1) /* should be not changed */ +#define _REDUCE_START_LEN 4 /* Should be at least 4 */ +#define _REDUCE_BUF_LEN (1 << 18) +/* The following should be power of two. There will be no space saving if it is less than 1/4 of buf + length. */ +#define _REDUCE_TABLE_SIZE (_REDUCE_BUF_LEN / 4) +#define _REDUCE_MAX_SYMB_LEN (2047) + +typedef size_t (*reduce_reader_t) (void *start, size_t len, void *aux_data); +typedef size_t (*reduce_writer_t) (const void *start, size_t len, void *aux_data); + +struct _reduce_el { + uint32_t pos, num, next, head; +}; + +struct _reduce_encode_data { + reduce_writer_t writer; + uint32_t el_free; + uint32_t curr_symb_len; + uint8_t curr_symb[_REDUCE_MAX_SYMB_LEN]; + struct _reduce_el table[_REDUCE_TABLE_SIZE]; /* hash -> el */ +}; + +struct _reduce_decode_data { + uint8_t eof_p; + uint32_t buf_get_pos; + reduce_reader_t reader; + uint32_t ind2pos[_REDUCE_BUF_LEN]; +}; + +struct reduce_data { + union { + struct _reduce_encode_data encode; + struct _reduce_decode_data decode; + } u; + void *aux_data; + uint8_t ok_p; + uint64_t check_hash; + uint32_t curr_num, buf_bound; + uint8_t buf[_REDUCE_BUF_LEN]; +}; + +static inline uint32_t _reduce_min (uint32_t a, uint32_t b) { return a < b ? a : b; } + +static inline uint32_t _reduce_get_new_el (struct reduce_data *data) { + struct _reduce_encode_data *encode_data = &data->u.encode; + uint32_t res = encode_data->el_free; + + if (res != UINT32_MAX) encode_data->el_free = encode_data->table[res].next; + return res; +} + +static inline void _reduce_put (struct reduce_data *data, int byte) { + uint8_t u = byte; + + if (data->u.encode.writer (&u, 1, data->aux_data) != 1) data->ok_p = FALSE; +} + +static inline int _reduce_get (reduce_reader_t reader, void *aux_data) { + uint8_t u; + + if (reader (&u, 1, aux_data) != 1) return -1; + return u; +} + +static inline uint32_t _reduce_ref_offset_size (uint32_t offset) { + return offset < (1 << 7) ? 1 : offset < (1 << 14) ? 2 : offset < (1 << 21) ? 3 : 4; +} + +static inline uint32_t _reduce_ref_size (uint32_t len, uint32_t offset) { + assert (len >= _REDUCE_START_LEN); + len -= _REDUCE_START_LEN - 1; + return ((len < _REDUCE_REF_TAG_LONG ? 0 : _reduce_ref_offset_size (len)) + + _reduce_ref_offset_size (offset)); +} + +static inline void _reduce_uint_write (struct reduce_data *data, uint32_t u) { + int n; + + assert (u < (1 << 7 * 4)); + for (n = 1; n <= 4 && u >= (1 << 7 * n); n++) + ; + _reduce_put (data, (1 << (8 - n)) | (u >> (n - 1) * 8) & 0xff); /* tag */ + for (int i = 2; i <= n; i++) _reduce_put (data, (u >> (n - i) * 8) & 0xff); +} + +static inline int64_t _reduce_uint_read (reduce_reader_t reader, void *aux_data) { + int i, n, r = _reduce_get (reader, aux_data); + uint32_t u, v; + + if (r < 0) return -1; + for (u = (uint32_t) r, n = 1; n <= 4 && (u >> (8 - n)) != 1; n++) + ; + assert ((u >> (8 - n)) == 1); + v = u & (0xff >> n); + for (i = 1; i < n; i++) { + if ((r = _reduce_get (reader, aux_data)) < 0) return -1; + v = v * 256 + (uint32_t) r; + } + return v; +} + +static inline void _reduce_hash_write (struct reduce_data *data, uint64_t h) { + _reduce_put (data, 0); /* 0 tag */ + for (int i = 0; i < sizeof (uint64_t); i++) _reduce_put (data, (h >> i * 8) & 0xff); +} + +static inline uint64_t _reduce_str2hash (const uint8_t *s) { + uint64_t h = 0; + + for (int i = 0; i < sizeof (uint64_t); i++) h |= (uint64_t) s[i] << i * 8; + return h; +} + +static inline int _reduce_symb_flush (struct reduce_data *data, int ref_tag) { + uint8_t u; + struct _reduce_encode_data *encode_data = &data->u.encode; + uint32_t len = encode_data->curr_symb_len; + + if (len == 0 && ref_tag == 0) return FALSE; + u = ((len < _REDUCE_SYMB_TAG_LONG ? len : _REDUCE_SYMB_TAG_LONG) << _REDUCE_REF_TAG_LEN) + | ref_tag; + encode_data->writer (&u, 1, data->aux_data); + if (len >= _REDUCE_SYMB_TAG_LONG) _reduce_uint_write (data, len); + encode_data->writer (encode_data->curr_symb, len, data->aux_data); + encode_data->curr_symb_len = 0; + return TRUE; +} + +static inline void _reduce_output_byte (struct reduce_data *data, uint32_t pos) { + struct _reduce_encode_data *encode_data = &data->u.encode; + + if (encode_data->curr_symb_len + 1 > _REDUCE_MAX_SYMB_LEN) { + _reduce_symb_flush (data, 0); + encode_data->curr_symb_len = 0; + } + encode_data->curr_symb[encode_data->curr_symb_len++] = data->buf[pos]; +} + +static inline void _reduce_output_ref (struct reduce_data *data, uint32_t offset, uint32_t len) { + uint32_t ref_tag; + + assert (len >= _REDUCE_START_LEN); + len -= _REDUCE_START_LEN - 1; + ref_tag = len < _REDUCE_REF_TAG_LONG ? len : _REDUCE_REF_TAG_LONG; + _reduce_symb_flush (data, ref_tag); + if (len >= _REDUCE_REF_TAG_LONG) _reduce_uint_write (data, len); + _reduce_uint_write (data, offset); +} + +#define _REDUCE_HASH_SEED 24 + +static inline uint32_t _reduce_dict_find_longest (struct reduce_data *data, uint32_t pos, + uint32_t *dict_pos) { + uint32_t len, best_len, len_bound; + uint64_t hash; + uint32_t off, best_off, ref_size, best_ref_size; + uint32_t curr, prev, next; + const uint8_t *s1, *s2; + struct _reduce_el *el, *best_el = NULL; + struct _reduce_encode_data *encode_data = &data->u.encode; + + if (pos + _REDUCE_START_LEN > data->buf_bound) return 0; + /* To have the same compressed output independently of the target + and the used compiler, use strict hash even if it decreases + compression speed by 10%. */ + hash + = mir_hash_strict (&data->buf[pos], _REDUCE_START_LEN, _REDUCE_HASH_SEED) % _REDUCE_TABLE_SIZE; + for (curr = encode_data->table[hash].head, prev = UINT32_MAX; curr != UINT32_MAX; + prev = curr, curr = next) { + next = encode_data->table[curr].next; + el = &encode_data->table[curr]; + len_bound = _reduce_min (data->buf_bound - pos, pos - el->pos); + if (len_bound < _REDUCE_START_LEN) continue; + s1 = &data->buf[el->pos]; + s2 = &data->buf[pos]; +#if MIR_HASH_UNALIGNED_ACCESS + assert (_REDUCE_START_LEN >= 4); + if (*(uint32_t *) &s1[0] != *(uint32_t *) &s2[0]) continue; + len = 4; +#else + len = 0; +#endif + for (; len < len_bound; len++) + if (s1[len] != s2[len]) break; +#if !MIR_HASH_UNALIGNED_ACCESS + if (len < _REDUCE_START_LEN) continue; +#endif + off = data->curr_num - el->num; + if (best_el == NULL) { + best_len = len; + best_el = el; + best_ref_size = _reduce_ref_size (len, off); + continue; + } + best_off = data->curr_num - best_el->num; + ref_size = _reduce_ref_size (len, off); + if (best_len + ref_size < len + best_ref_size) { + best_len = len; + best_el = el; + best_ref_size = ref_size; + } + } + if (best_el == NULL) return 0; + *dict_pos = best_el->num; + return best_len; +} + +static inline void _reduce_dict_add (struct reduce_data *data, uint32_t pos) { + uint64_t hash; + struct _reduce_el *el; + uint32_t prev, curr, num = data->curr_num++; + struct _reduce_encode_data *encode_data = &data->u.encode; + + if (pos + _REDUCE_START_LEN > data->buf_bound) return; + hash + = mir_hash_strict (&data->buf[pos], _REDUCE_START_LEN, _REDUCE_HASH_SEED) % _REDUCE_TABLE_SIZE; + if ((curr = _reduce_get_new_el (data)) == UINT32_MAX) { /* rare case: use last if any */ + for (prev = UINT32_MAX, curr = encode_data->table[hash].head; + curr != UINT32_MAX && encode_data->table[curr].next != UINT32_MAX; + prev = curr, curr = encode_data->table[curr].next) + ; + if (curr == UINT32_MAX) return; /* no more free els */ + if (prev != UINT32_MAX) + encode_data->table[prev].next = encode_data->table[curr].next; + else + encode_data->table[hash].head = encode_data->table[curr].next; + } + encode_data->table[curr].pos = pos; + encode_data->table[curr].num = num; + encode_data->table[curr].next = encode_data->table[hash].head; + encode_data->table[hash].head = curr; +} + +static void _reduce_reset_next (struct reduce_data *data) { + struct _reduce_encode_data *encode_data = &data->u.encode; + + for (uint32_t i = 0; i < _REDUCE_TABLE_SIZE; i++) { + encode_data->table[i].next = i + 1; + encode_data->table[i].head = UINT32_MAX; + } + encode_data->table[_REDUCE_TABLE_SIZE - 1].next = UINT32_MAX; + encode_data->el_free = 0; +} + +#define _REDUCE_CHECK_HASH_SEED 42 + +static inline struct reduce_data *reduce_encode_start (reduce_writer_t writer, void *aux_data) { + struct reduce_data *data = malloc (sizeof (struct reduce_data)); + char prefix[] = _REDUCE_DATA_PREFIX; + size_t prefix_size = strlen (prefix); + + if (data == NULL) return data; + data->u.encode.writer = writer; + data->aux_data = aux_data; + data->check_hash = _REDUCE_CHECK_HASH_SEED; + data->buf_bound = 0; + data->ok_p = writer (prefix, prefix_size, aux_data) == prefix_size; + return data; +} + +static inline void _reduce_encode_buf (struct reduce_data *data) { + uint32_t dict_len, dict_pos, base; + + if (data->buf_bound == 0) return; + data->check_hash = mir_hash_strict (data->buf, data->buf_bound, data->check_hash); + data->curr_num = data->u.encode.curr_symb_len = 0; + _reduce_reset_next (data); + for (uint32_t pos = 0; pos < data->buf_bound;) { + dict_len = _reduce_dict_find_longest (data, pos, &dict_pos); + base = data->curr_num; + if (dict_len == 0) { + _reduce_output_byte (data, pos); + _reduce_dict_add (data, pos); + pos++; + continue; + } + _reduce_output_ref (data, base - dict_pos, dict_len); + _reduce_dict_add (data, pos); /* replace */ + pos += dict_len; + } + _reduce_symb_flush (data, 0); +} + +static inline void reduce_encode_put (struct reduce_data *data, int c) { + if (data->buf_bound < _REDUCE_BUF_LEN) { + data->buf[data->buf_bound++] = c; + return; + } + _reduce_encode_buf (data); + data->buf_bound = 0; + data->buf[data->buf_bound++] = c; +} + +static inline int reduce_encode_finish (struct reduce_data *data) { + int ok_p; + + _reduce_encode_buf (data); + _reduce_hash_write (data, data->check_hash); + ok_p = data->ok_p; + free (data); + return ok_p; +} + +static inline struct reduce_data *reduce_decode_start (reduce_reader_t reader, void *aux_data) { + struct reduce_data *data = malloc (sizeof (struct reduce_data)); + struct _reduce_decode_data *decode_data = &data->u.decode; + char prefix[] = _REDUCE_DATA_PREFIX, str[sizeof (prefix)]; + size_t prefix_size = strlen (prefix); + + if (data == NULL) return data; + decode_data->reader = reader; + data->aux_data = aux_data; + data->check_hash = _REDUCE_CHECK_HASH_SEED; + decode_data->buf_get_pos = data->buf_bound = 0; + data->ok_p + = reader (str, prefix_size, aux_data) == prefix_size && memcmp (prefix, str, prefix_size) == 0; + decode_data->eof_p = FALSE; + return data; +} + +static inline int reduce_decode_get (struct reduce_data *data) { + uint8_t tag, hash_str[sizeof (uint64_t)]; + uint32_t sym_len, ref_len, ref_ind, sym_pos, pos = 0, curr_ind = 0; + uint64_t r; + struct _reduce_decode_data *decode_data = &data->u.decode; + reduce_reader_t reader = decode_data->reader; + + if (decode_data->buf_get_pos < data->buf_bound) return data->buf[decode_data->buf_get_pos++]; + if (decode_data->eof_p) return -1; + for (;;) { + if (reader (&tag, 1, data->aux_data) == 0) break; + if (tag == 0) { /* check hash */ + if (reader (hash_str, sizeof (hash_str), data->aux_data) != sizeof (hash_str) + || reader (&tag, 1, data->aux_data) != 0) + break; + if (pos != 0) data->check_hash = mir_hash_strict (data->buf, pos, data->check_hash); + if (_reduce_str2hash (hash_str) != data->check_hash) break; + decode_data->eof_p = TRUE; + decode_data->buf_get_pos = 0; + data->buf_bound = pos; + return pos == 0 ? -1 : data->buf[decode_data->buf_get_pos++]; + } + sym_len = tag >> _REDUCE_REF_TAG_LEN; + if (sym_len != 0) { + if (sym_len == _REDUCE_SYMB_TAG_LONG) { + if ((r = _reduce_uint_read (reader, data->aux_data)) < 0) break; + sym_len = r; + } + if (sym_len > _REDUCE_MAX_SYMB_LEN || pos + sym_len > _REDUCE_BUF_LEN) break; + if (reader (&data->buf[pos], sym_len, data->aux_data) != sym_len) break; + for (uint32_t i = 0; i < sym_len; i++, pos++, curr_ind++) + decode_data->ind2pos[curr_ind] = pos; + } + ref_len = tag & _REDUCE_REF_TAG_LONG; + if (ref_len != 0) { + if (ref_len == _REDUCE_REF_TAG_LONG) { + if ((r = _reduce_uint_read (reader, data->aux_data)) < 0) break; + ref_len = r; + } + ref_len += _REDUCE_START_LEN - 1; + if ((r = _reduce_uint_read (reader, data->aux_data)) < 0) break; + ref_ind = r; + if (curr_ind < ref_ind) break; + sym_pos = decode_data->ind2pos[curr_ind - ref_ind]; + if (sym_pos + ref_len > _REDUCE_BUF_LEN) break; + memcpy (&data->buf[pos], &data->buf[sym_pos], ref_len); + decode_data->ind2pos[curr_ind++] = pos; + pos += ref_len; + } + if (pos >= _REDUCE_BUF_LEN) { + assert (pos == _REDUCE_BUF_LEN); + data->check_hash = mir_hash_strict (data->buf, pos, data->check_hash); + data->buf_bound = _REDUCE_BUF_LEN; + decode_data->buf_get_pos = 0; + return data->buf[decode_data->buf_get_pos++]; + } + } + data->ok_p = FALSE; + return -1; +} + +static inline int reduce_decode_finish (struct reduce_data *data) { + uint8_t tag; + int ok_p + = data->ok_p && data->u.decode.eof_p && data->u.decode.reader (&tag, 1, data->aux_data) == 0; + + free (data); + return ok_p; +} + +#define _REDUCE_WRITE_IO_LEN 256 +static inline int reduce_encode (reduce_reader_t reader, reduce_writer_t writer, void *aux_data) { + size_t i, size; + uint8_t buf[_REDUCE_WRITE_IO_LEN]; + struct reduce_data *data = reduce_encode_start (writer, aux_data); + + if (data == NULL) return FALSE; + for (;;) { + if ((size = reader (buf, _REDUCE_WRITE_IO_LEN, data->aux_data)) == 0) break; + for (i = 0; i < size; i++) reduce_encode_put (data, buf[i]); + } + return reduce_encode_finish (data); +} + +static inline int reduce_decode (reduce_reader_t reader, reduce_writer_t writer, void *aux_data) { + int c, i; + uint8_t buf[_REDUCE_WRITE_IO_LEN]; + struct reduce_data *data = reduce_decode_start (reader, aux_data); + + if (data == NULL) return FALSE; + for (;;) { + for (i = 0; i < _REDUCE_WRITE_IO_LEN && (c = reduce_decode_get (data)) >= 0; i++) buf[i] = c; + if (i != 0) writer (buf, i, aux_data); + if (c < 0) break; + } + return reduce_decode_finish (data); +} + +#endif /* #ifndef MIR_REDUCE_H */ diff --git a/mir/mir-varr.h b/mir/mir-varr.h new file mode 100644 index 0000000..e552ed4 --- /dev/null +++ b/mir/mir-varr.h @@ -0,0 +1,170 @@ +/* This file is a part of MIR project. + Copyright (C) 2018, 2019 Vladimir Makarov . +*/ + +#ifndef MIR_VARR_H +#define MIR_VARR_H + +#include +#include +#include + +#if !defined(VARR_ENABLE_CHECKING) && !defined(NDEBUG) +#define VARR_ENABLE_CHECKING +#endif + +#ifndef VARR_ENABLE_CHECKING +#define VARR_ASSERT(EXPR, OP, T) ((void) (EXPR)) + +#else +static inline void mir_var_assert_fail (const char *op, const char *var) { + fprintf (stderr, "wrong %s for %s", op, var); + assert (0); +} + +#define VARR_ASSERT(EXPR, OP, T) (void) ((EXPR) ? 0 : (mir_var_assert_fail (OP, #T), 0)) + +#endif + +#ifdef __GNUC__ +#define MIR_VARR_NO_RETURN __attribute__ ((noreturn)) +#else +#define MIR_VARR_NO_RETURN +#endif + +static inline void MIR_VARR_NO_RETURN mir_varr_error (const char *message) { +#ifdef MIR_VARR_ERROR + MIR_VARR_ERROR (message); + assert (0); +#else + fprintf (stderr, "%s\n", message); +#endif + exit (1); +} + +/*---------------- Typed variable length arrays -----------------------------*/ +#define VARR_CONCAT2(A, B) A##B +#define VARR_CONCAT3(A, B, C) A##B##C +#define VARR(T) VARR_CONCAT2 (VARR_, T) +#define VARR_OP(T, OP) VARR_CONCAT3 (VARR_, T, OP) + +#define VARR_T(T) \ + typedef struct VARR (T) { \ + size_t els_num; \ + size_t size; \ + T *varr; \ + } VARR (T) + +#define VARR_DEFAULT_SIZE 64 + +/* Vector of pointer to object. */ +#define DEF_VARR(T) \ + VARR_T (T); \ + \ + static inline void VARR_OP (T, create) (VARR (T) * *varr, size_t size) { \ + VARR (T) * va; \ + if (size == 0) size = VARR_DEFAULT_SIZE; \ + *varr = va = (VARR (T) *) malloc (sizeof (VARR (T))); \ + if (va == NULL) mir_varr_error ("varr: no memory"); \ + va->els_num = 0; \ + va->size = size; \ + va->varr = (T *) malloc (size * sizeof (T)); \ + } \ + \ + static inline void VARR_OP (T, destroy) (VARR (T) * *varr) { \ + VARR (T) *va = *varr; \ + VARR_ASSERT (va && va->varr, "destroy", T); \ + free (va->varr); \ + free (va); \ + *varr = NULL; \ + } \ + \ + static inline size_t VARR_OP (T, length) (const VARR (T) * varr) { \ + VARR_ASSERT (varr, "length", T); \ + return varr->els_num; \ + } \ + \ + static inline T *VARR_OP (T, addr) (const VARR (T) * varr) { \ + VARR_ASSERT (varr, "addr", T); \ + return &varr->varr[0]; \ + } \ + \ + static inline T VARR_OP (T, last) (const VARR (T) * varr) { \ + VARR_ASSERT (varr && varr->varr && varr->els_num, "last", T); \ + return varr->varr[varr->els_num - 1]; \ + } \ + \ + static inline T VARR_OP (T, get) (const VARR (T) * varr, unsigned ix) { \ + VARR_ASSERT (varr && varr->varr && ix < varr->els_num, "get", T); \ + return varr->varr[ix]; \ + } \ + \ + static inline T VARR_OP (T, set) (const VARR (T) * varr, unsigned ix, T obj) { \ + T old_obj; \ + VARR_ASSERT (varr && varr->varr && ix < varr->els_num, "set", T); \ + old_obj = varr->varr[ix]; \ + varr->varr[ix] = obj; \ + return old_obj; \ + } \ + \ + static inline void VARR_OP (T, trunc) (VARR (T) * varr, size_t size) { \ + VARR_ASSERT (varr && varr->varr && varr->els_num >= size, "trunc", T); \ + varr->els_num = size; \ + } \ + \ + static inline int VARR_OP (T, expand) (VARR (T) * varr, size_t size) { \ + VARR_ASSERT (varr && varr->varr, "expand", T); \ + if (varr->size < size) { \ + size += size / 2; \ + varr->varr = (T *) realloc (varr->varr, sizeof (T) * size); \ + varr->size = size; \ + return 1; \ + } \ + return 0; \ + } \ + \ + static inline void VARR_OP (T, tailor) (VARR (T) * varr, size_t size) { \ + VARR_ASSERT (varr && varr->varr, "tailor", T); \ + if (varr->size != size) varr->varr = (T *) realloc (varr->varr, sizeof (T) * size); \ + varr->els_num = varr->size = size; \ + } \ + \ + static inline void VARR_OP (T, push) (VARR (T) * varr, T obj) { \ + T *slot; \ + VARR_OP (T, expand) (varr, varr->els_num + 1); \ + slot = &varr->varr[varr->els_num++]; \ + *slot = obj; \ + } \ + \ + static inline void VARR_OP (T, push_arr) (VARR (T) * varr, const T *objs, size_t len) { \ + size_t i; \ + T *slot; \ + VARR_OP (T, expand) (varr, varr->els_num + len); \ + for (i = 0; i < len; i++) { \ + slot = &varr->varr[varr->els_num++]; \ + *slot = objs[i]; \ + } \ + } \ + \ + static inline T VARR_OP (T, pop) (VARR (T) * varr) { \ + T obj; \ + VARR_ASSERT (varr && varr->varr && varr->els_num, "pop", T); \ + obj = varr->varr[--varr->els_num]; \ + return obj; \ + } + +#define VARR_CREATE(T, V, L) (VARR_OP (T, create) (&(V), L)) +#define VARR_DESTROY(T, V) (VARR_OP (T, destroy) (&(V))) +#define VARR_LENGTH(T, V) (VARR_OP (T, length) (V)) +#define VARR_ADDR(T, V) (VARR_OP (T, addr) (V)) +#define VARR_LAST(T, V) (VARR_OP (T, last) (V)) +#define VARR_GET(T, V, I) (VARR_OP (T, get) (V, I)) +#define VARR_SET(T, V, I, O) (VARR_OP (T, set) (V, I, O)) +#define VARR_TRUNC(T, V, S) (VARR_OP (T, trunc) (V, S)) +#define VARR_EXPAND(T, V, S) (VARR_OP (T, expand) (V, S)) +#define VARR_TAILOR(T, V, S) (VARR_OP (T, tailor) (V, S)) +#define VARR_PUSH(T, V, O) (VARR_OP (T, push) (V, O)) +#define VARR_PUSH_ARR(T, V, A, L) (VARR_OP (T, push_arr) (V, A, L)) +#define VARR_POP(T, V) (VARR_OP (T, pop) (V)) + +#endif /* #ifndef MIR_VARR_H */ diff --git a/mir/mir-x86_64.c b/mir/mir-x86_64.c new file mode 100644 index 0000000..ac33cdb --- /dev/null +++ b/mir/mir-x86_64.c @@ -0,0 +1,355 @@ +/* This file is a part of MIR project. + Copyright (C) 2018, 2019 Vladimir Makarov . +*/ + +void *_MIR_get_bstart_builtin (MIR_context_t ctx) { + static const uint8_t bstart_code[] = { + 0x48, 0x8d, 0x44, 0x24, 0x08, /* rax = rsp + 8 (lea) */ + 0xc3, /* ret */ + }; + return _MIR_publish_code (ctx, bstart_code, sizeof (bstart_code)); +} +void *_MIR_get_bend_builtin (MIR_context_t ctx) { + static const uint8_t bend_code[] = { + 0x48, 0x8b, 0x04, 0x24, /* rax = (rsp) */ + 0x48, 0x89, 0xfc, /* rsp = rdi */ + 0xff, 0xe0, /* jmp *rax */ + }; + return _MIR_publish_code (ctx, bend_code, sizeof (bend_code)); +} + +struct x86_64_va_list { + uint32_t gp_offset, fp_offset; + uint64_t *overflow_arg_area, *reg_save_area; +}; + +void *va_arg_builtin (void *p, uint64_t t) { + struct x86_64_va_list *va = p; + MIR_type_t type = t; + int fp_p = type == MIR_T_F || type == MIR_T_D; + void *a; + + if (fp_p && va->fp_offset <= 160) { + a = (char *) va->reg_save_area + va->fp_offset; + va->fp_offset += 16; + } else if (!fp_p && type != MIR_T_LD && va->gp_offset <= 40) { + a = (char *) va->reg_save_area + va->gp_offset; + va->gp_offset += 8; + } else { + a = va->overflow_arg_area; + va->overflow_arg_area += type == MIR_T_LD ? 2 : 1; + } + return a; +} + +void va_start_interp_builtin (MIR_context_t ctx, void *p, void *a) { + struct x86_64_va_list *va = p; + va_list *vap = a; + + assert (sizeof (struct x86_64_va_list) == sizeof (va_list)); + *va = *(struct x86_64_va_list *) vap; +} + +void va_end_interp_builtin (MIR_context_t ctx, void *p) {} + +/* r11=
; jump *r11 */ +void *_MIR_get_thunk (MIR_context_t ctx) { + void *res; + static const uint8_t pattern[] = { + 0x49, 0xbb, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x0: movabsq 0, r11 */ + 0x41, 0xff, 0xe3, /* 0x14: jmpq *%r11 */ + }; + res = _MIR_publish_code (ctx, pattern, sizeof (pattern)); + return res; +} + +void _MIR_redirect_thunk (MIR_context_t ctx, void *thunk, void *to) { + _MIR_update_code (ctx, thunk, 1, 2, to); +} + +static const uint8_t save_pat[] = { + 0x48, 0x81, 0xec, 0x80, 0, 0, 0, /*sub $0x80,%rsp */ + 0xf3, 0x0f, 0x7f, 0x04, 0x24, /*movdqu %xmm0,(%rsp) */ + 0xf3, 0x0f, 0x7f, 0x4c, 0x24, 0x10, /*movdqu %xmm1,0x10(%rsp) */ + 0xf3, 0x0f, 0x7f, 0x54, 0x24, 0x20, /*movdqu %xmm2,0x20(%rsp) */ + 0xf3, 0x0f, 0x7f, 0x5c, 0x24, 0x30, /*movdqu %xmm3,0x30(%rsp) */ + 0xf3, 0x0f, 0x7f, 0x64, 0x24, 0x40, /*movdqu %xmm4,0x40(%rsp) */ + 0xf3, 0x0f, 0x7f, 0x6c, 0x24, 0x50, /*movdqu %xmm5,0x50(%rsp) */ + 0xf3, 0x0f, 0x7f, 0x74, 0x24, 0x60, /*movdqu %xmm6,0x60(%rsp) */ + 0xf3, 0x0f, 0x7f, 0x7c, 0x24, 0x70, /*movdqu %xmm7,0x70(%rsp) */ + 0x41, 0x51, /*push %r9 */ + 0x41, 0x50, /*push %r8 */ + 0x51, /*push %rcx */ + 0x52, /*push %rdx */ + 0x56, /*push %rsi */ + 0x57, /*push %rdi */ +}; + +static const uint8_t restore_pat[] = { + 0x5f, /*pop %rdi */ + 0x5e, /*pop %rsi */ + 0x5a, /*pop %rdx */ + 0x59, /*pop %rcx */ + 0x41, 0x58, /*pop %r8 */ + 0x41, 0x59, /*pop %r9 */ + 0xf3, 0x0f, 0x6f, 0x04, 0x24, /*movdqu (%rsp),%xmm0 */ + 0xf3, 0x0f, 0x6f, 0x4c, 0x24, 0x10, /*movdqu 0x10(%rsp),%xmm1 */ + 0xf3, 0x0f, 0x6f, 0x54, 0x24, 0x20, /*movdqu 0x20(%rsp),%xmm2 */ + 0xf3, 0x0f, 0x6f, 0x5c, 0x24, 0x30, /*movdqu 0x30(%rsp),%xmm3 */ + 0xf3, 0x0f, 0x6f, 0x64, 0x24, 0x40, /*movdqu 0x40(%rsp),%xmm4 */ + 0xf3, 0x0f, 0x6f, 0x6c, 0x24, 0x50, /*movdqu 0x50(%rsp),%xmm5 */ + 0xf3, 0x0f, 0x6f, 0x74, 0x24, 0x60, /*movdqu 0x60(%rsp),%xmm6 */ + 0xf3, 0x0f, 0x6f, 0x7c, 0x24, 0x70, /*movdqu 0x70(%rsp),%xmm7 */ + 0x48, 0x81, 0xc4, 0x80, 0, 0, 0, /*add $0x80,%rsp */ +}; + +VARR (uint8_t) * machine_insns; + +static uint8_t *push_insns (const uint8_t *pat, size_t pat_len) { + for (size_t i = 0; i < pat_len; i++) VARR_PUSH (uint8_t, machine_insns, pat[i]); + return VARR_ADDR (uint8_t, machine_insns) + VARR_LENGTH (uint8_t, machine_insns) - pat_len; +} + +static void gen_mov (uint32_t offset, uint32_t reg, int ld_p) { + static const uint8_t ld_gp_reg[] = {0x48, 0x8b, 0x83, 0, 0, 0, 0 /* mov (%rbx),%reg */}; + static const uint8_t st_gp_reg[] = {0x48, 0x89, 0x83, 0, 0, 0, 0 /* mov %reg,(%rbx) */}; + uint8_t *addr + = push_insns (ld_p ? ld_gp_reg : st_gp_reg, ld_p ? sizeof (ld_gp_reg) : sizeof (st_gp_reg)); + memcpy (addr + 3, &offset, sizeof (uint32_t)); + assert (reg <= 15); + addr[0] |= (reg >> 1) & 4; + addr[2] |= (reg & 7) << 3; +} + +static void gen_movxmm (uint32_t offset, uint32_t reg, int b32_p, int ld_p) { + static const uint8_t ld_xmm_reg_pat[] = { + 0xf2, 0x0f, 0x10, 0x83, 0, 0, 0, 0 /* movs[sd] (%rbx),%xmm */ + }; + static const uint8_t st_xmm_reg_pat[] = { + 0xf2, 0x0f, 0x11, 0x83, 0, 0, 0, 0 /* movs[sd] %xmm, (%rbx) */ + }; + uint8_t *addr = push_insns (ld_p ? ld_xmm_reg_pat : st_xmm_reg_pat, + ld_p ? sizeof (ld_xmm_reg_pat) : sizeof (st_xmm_reg_pat)); + memcpy (addr + 4, &offset, sizeof (uint32_t)); + assert (reg <= 7); + addr[3] |= reg << 3; + if (b32_p) addr[0] |= 1; +} + +static void gen_ldst (uint32_t sp_offset, uint32_t src_offset, int b64_p) { + static const uint8_t ldst_pat[] = { + 0x44, 0x8b, 0x93, 0, 0, 0, 0, /* mov (%rbx),%r10 */ + 0x44, 0x89, 0x94, 0x24, 0, 0, 0, 0, /* mov %r10,(%sp) */ + }; + uint8_t *addr = push_insns (ldst_pat, sizeof (ldst_pat)); + memcpy (addr + 3, &src_offset, sizeof (uint32_t)); + memcpy (addr + 11, &sp_offset, sizeof (uint32_t)); + if (b64_p) { + addr[0] |= 8; + addr[7] |= 8; + } +} + +static void gen_ldst80 (uint32_t sp_offset, uint32_t src_offset) { + static uint8_t const ldst80_pat[] = { + 0xdb, 0xab, 0, 0, 0, 0, /* fldt (%rbx) */ + 0xdb, 0xbc, 0x24, 0, 0, 0, 0, /* fstpt (%sp) */ + }; + uint8_t *addr = push_insns (ldst80_pat, sizeof (ldst80_pat)); + memcpy (addr + 2, &src_offset, sizeof (uint32_t)); + memcpy (addr + 9, &sp_offset, sizeof (uint32_t)); +} + +static void gen_st80 (uint32_t src_offset) { + static const uint8_t st80_pat[] = {0xdb, 0xbb, 0, 0, 0, 0 /* fstpt (%rbx) */}; + memcpy (push_insns (st80_pat, sizeof (st80_pat)) + 2, &src_offset, sizeof (uint32_t)); +} + +/* Generation: fun (fun_addr, res_arg_addresses): + push rbx; sp-=sp_offset; r11=fun_addr; rbx=res/arg_addrs + r10=mem[rbx,]; (arg_reg=mem[r10] or r10=mem[r10];mem[sp,sp_offset]=r10) ... + rax=8; call *r11; sp+=offset + r10=mem[rbx,]; res_reg=mem[r10]; ... + pop rbx; ret. */ +void *_MIR_get_ff_call (MIR_context_t ctx, size_t nres, MIR_type_t *res_types, size_t nargs, + MIR_type_t *arg_types) { + static const uint8_t prolog[] = { + 0x53, /* pushq %rbx */ + 0x48, 0x81, 0xec, 0, 0, 0, 0, /* subq , %rsp */ + 0x49, 0x89, 0xfb, /* mov $rdi, $r11 -- fun addr */ + 0x48, 0x89, 0xf3, /* mov $rsi, $rbx -- result/arg addresses */ + }; + static const uint8_t call_end[] = { + 0x48, 0xc7, 0xc0, 0x08, 0, 0, 0, /* mov $8, rax -- to save xmm varargs */ + 0x41, 0xff, 0xd3, /* callq *%r11 */ + 0x48, 0x81, 0xc4, 0, 0, 0, 0, /* addq , %rsp */ + }; + static const uint8_t epilog[] = { + 0x5b, /* pop %rbx */ + 0xc3, /* ret */ + }; + static const uint8_t iregs[] = {7, 6, 2, 1, 8, 9}; /* rdi, rsi, rdx, rcx, r8, r9 */ + uint32_t n_iregs = 0, n_xregs = 0, n_fregs, sp_offset = 0; + uint8_t *addr; + + VARR_TRUNC (uint8_t, machine_insns, 0); + push_insns (prolog, sizeof (prolog)); + for (size_t i = 0; i < nargs; i++) { + if ((MIR_T_I8 <= arg_types[i] && arg_types[i] <= MIR_T_U64) || arg_types[i] == MIR_T_P) { + if (n_iregs < 6) { + gen_mov ((i + nres) * sizeof (long double), iregs[n_iregs++], TRUE); + } else { + gen_ldst (sp_offset, (i + nres) * sizeof (long double), TRUE); + sp_offset += 8; + } + } else if (arg_types[i] == MIR_T_F || arg_types[i] == MIR_T_D) { + if (n_xregs < 8) { + gen_movxmm ((i + nres) * sizeof (long double), n_xregs++, arg_types[i] == MIR_T_F, TRUE); + } else { + gen_ldst (sp_offset, (i + nres) * sizeof (long double), arg_types[i] == MIR_T_D); + sp_offset += 8; + } + } else if (arg_types[i] == MIR_T_LD) { + gen_ldst80 (sp_offset, (i + nres) * sizeof (long double)); + sp_offset += 16; + } else { + (*error_func) (MIR_call_op_error, "wrong type of arg value"); + } + } + sp_offset = (sp_offset + 15) / 16 * 16; + addr = VARR_ADDR (uint8_t, machine_insns); + memcpy (addr + 4, &sp_offset, sizeof (uint32_t)); + addr = push_insns (call_end, sizeof (call_end)); + memcpy (addr + 13, &sp_offset, sizeof (uint32_t)); + n_iregs = n_xregs = n_fregs = 0; + for (size_t i = 0; i < nres; i++) { + if (((MIR_T_I8 <= res_types[i] && res_types[i] <= MIR_T_U64) || res_types[i] == MIR_T_P) + && n_iregs < 2) { + gen_mov (i * sizeof (long double), n_iregs++ == 0 ? 0 : 2, FALSE); /* rax or rdx */ + } else if ((res_types[i] == MIR_T_F || res_types[i] == MIR_T_D) && n_xregs < 2) { + gen_movxmm (i * sizeof (long double), n_xregs++, res_types[i] == MIR_T_F, FALSE); + } else if (res_types[i] == MIR_T_LD && n_fregs < 2) { + gen_st80 (i * sizeof (long double)); + } else { + (*error_func) (MIR_ret_error, "x86-64 can not handle this combination of return values"); + } + } + push_insns (epilog, sizeof (epilog)); + return _MIR_publish_code (ctx, VARR_ADDR (uint8_t, machine_insns), + VARR_LENGTH (uint8_t, machine_insns)); +} + +void *_MIR_get_interp_shim (MIR_context_t ctx, MIR_item_t func_item, void *handler) { + static const uint8_t push_rbx[] = {0x53, /*push %rbx */}; + static const uint8_t prepare_pat[] = { + /* 0: */ 0x48, 0x83, 0xec, 0x20, /* sub 32,%rsp */ + /* 4: */ 0x48, 0x89, 0xe2, /* mov %rsp,%rdx */ + /* 7: */ 0xc7, 0x02, 0, 0, 0, 0, /* movl 0,(%rdx) */ + /* d: */ 0xc7, 0x42, 0x04, 0x30, 0, 0, 0, /* movl 48, 4(%rdx) */ + /* 14: */ 0x48, 0x8d, 0x44, 0x24, 0x20, /* lea 32(%rsp),%rax */ + /* 19: */ 0x48, 0x89, 0x42, 0x10, /* mov %rax,16(%rdx) */ + /* 1d: */ 0x48, 0x8d, 0x84, 0x24, 0xe0, 0, 0, 0, /* lea 224(%rsp),%rax */ + /* 25: */ 0x48, 0x89, 0x42, 0x08, /* mov %rax,8(%rdx) */ + /* 29: */ 0x48, 0x81, 0xec, 0, 0, 0, 0, /* sub ,%rsp */ + /* 30: */ 0x48, 0x89, 0xe3, /* mov %rsp,%rbx */ + /* 33: */ 0x48, 0x89, 0xe1, /* mov %rsp,%rcx */ + /* 36: */ 0x48, 0xbf, 0, 0, 0, 0, 0, 0, 0, 0, /* movabs ,%rdi */ + /* 40: */ 0x48, 0xbe, 0, 0, 0, 0, 0, 0, 0, 0, /* movabs ,%rsi */ + /* 4a: */ 0x48, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, /* movabs ,%rax */ + /* 54: */ 0xff, 0xd0, /* callq *%rax */ + }; + static const uint8_t shim_end[] = { + /* 0: */ 0x48, 0x81, 0xc4, 0, 0, 0, 0, /*add 208+n,%rsp*/ + /* 7: */ 0x5b, /*pop %rbx*/ + /* 8: */ 0xc3, /*retq */ + }; + static const uint8_t ld_pat[] + = {0x48, 0x8b, 0x83, 0, 0, 0, 0}; /* movss (%rbx), %xmm[01] */ + static const uint8_t movss_pat[] + = {0xf3, 0x0f, 0x10, 0x83, 0, 0, 0, 0}; /* movss (%rbx), %xmm[01] */ + static const uint8_t movsd_pat[] + = {0xf2, 0x0f, 0x10, 0x83, 0, 0, 0, 0}; /* movsd (%rbx), %xmm[01] */ + static const uint8_t fldt_pat[] = {0xdb, 0xab, 0, 0, 0, 0}; /* fldt (%rbx) */ + static const uint8_t fxch_pat[] = {0xd9, 0xc9}; /* fxch */ + uint8_t *addr; + uint32_t imm, n_iregs, n_xregs, n_fregs, offset; + uint32_t nres = func_item->u.func->nres; + MIR_type_t *results = func_item->u.func->res_types; + + VARR_TRUNC (uint8_t, machine_insns, 0); + push_insns (push_rbx, sizeof (push_rbx)); + push_insns (save_pat, sizeof (save_pat)); + addr = push_insns (prepare_pat, sizeof (prepare_pat)); + imm = nres * 16; + memcpy (addr + 0x2c, &imm, sizeof (uint32_t)); + memcpy (addr + 0x38, &ctx, sizeof (void *)); + memcpy (addr + 0x42, &func_item, sizeof (void *)); + memcpy (addr + 0x4c, &handler, sizeof (void *)); + /* move results: */ + n_iregs = n_xregs = n_fregs = offset = 0; + for (uint32_t i = 0; i < nres; i++) { + if (results[i] == MIR_T_F && n_xregs < 2) { + addr = push_insns (movss_pat, sizeof (movss_pat)); + addr[3] |= n_xregs << 3; + memcpy (addr + 4, &offset, sizeof (uint32_t)); + n_xregs++; + } else if (results[i] == MIR_T_D && n_xregs < 2) { + addr = push_insns (movsd_pat, sizeof (movsd_pat)); + addr[3] |= n_xregs << 3; + memcpy (addr + 4, &offset, sizeof (uint32_t)); + n_xregs++; + } else if (results[i] == MIR_T_LD && n_fregs < 2) { + addr = push_insns (fldt_pat, sizeof (fldt_pat)); + memcpy (addr + 2, &offset, sizeof (uint32_t)); + if (n_fregs == 1) push_insns (fxch_pat, sizeof (fxch_pat)); + n_fregs++; + } else if (n_iregs < 2) { + addr = push_insns (ld_pat, sizeof (ld_pat)); + addr[2] |= n_iregs << 4; + memcpy (addr + 3, &offset, sizeof (uint32_t)); + n_iregs++; + } else { + (*error_func) (MIR_ret_error, "x86-64 can not handle this combination of return values"); + } + offset += 16; + } + addr = push_insns (shim_end, sizeof (shim_end)); + imm = 208 + nres * 16; + memcpy (addr + 3, &imm, sizeof (uint32_t)); + return _MIR_publish_code (ctx, VARR_ADDR (uint8_t, machine_insns), + VARR_LENGTH (uint8_t, machine_insns)); +} + +/* save regs; r10 = call hook_address (ctx, called_func); restore regs; jmp *r10 + */ +void *_MIR_get_wrapper (MIR_context_t ctx, MIR_item_t called_func, void *hook_address) { + static const uint8_t push_rax[] = {0x50, /*push %rax */}; + static const uint8_t wrap_end[] = { + 0x58, /*pop %rax */ + 0x41, 0xff, 0xe2, /*jmpq *%r10 */ + }; + static const uint8_t call_pat[] = { + 0x48, 0xbe, 0, 0, 0, 0, 0, 0, 0, 0, /*movabs called_func,%rsi */ + 0x48, 0xbf, 0, 0, 0, 0, 0, 0, 0, 0, /*movabs ctx,%rdi */ + 0x49, 0xba, 0, 0, 0, 0, 0, 0, 0, 0, /*movabs ,%r10 */ + 0x41, 0xff, 0xd2, /*callq *%r10 */ + 0x49, 0x89, 0xc2, /*mov %rax,%r10 */ + }; + uint8_t *addr; + + VARR_TRUNC (uint8_t, machine_insns, 0); + push_insns (push_rax, sizeof (push_rax)); + push_insns (save_pat, sizeof (save_pat)); + addr = push_insns (call_pat, sizeof (call_pat)); + memcpy (addr + 2, &called_func, sizeof (void *)); + memcpy (addr + 12, &ctx, sizeof (void *)); + memcpy (addr + 22, &hook_address, sizeof (void *)); + push_insns (restore_pat, sizeof (restore_pat)); + push_insns (wrap_end, sizeof (wrap_end)); + return _MIR_publish_code (ctx, VARR_ADDR (uint8_t, machine_insns), + VARR_LENGTH (uint8_t, machine_insns)); +} + +static void machine_init (MIR_context_t ctx) { VARR_CREATE (uint8_t, machine_insns, 1024); } + +static void machine_finish (MIR_context_t ctx) { VARR_DESTROY (uint8_t, machine_insns); } diff --git a/mir/mir.c b/mir/mir.c new file mode 100644 index 0000000..3a7ad45 --- /dev/null +++ b/mir/mir.c @@ -0,0 +1,5220 @@ +/* This file is a part of MIR project. + Copyright (C) 2018, 2019 Vladimir Makarov . +*/ + +#include "mir.h" + +DEF_VARR (MIR_insn_t); +DEF_VARR (MIR_reg_t); +DEF_VARR (MIR_op_t); +DEF_VARR (MIR_type_t); +DEF_HTAB (MIR_item_t); +DEF_VARR (MIR_module_t); +DEF_VARR (size_t); +DEF_VARR (char); +DEF_VARR (uint8_t); + +struct gen_ctx; +struct c2mir_ctx; +struct string_ctx; +struct reg_ctx; +struct simplify_ctx; +struct machine_code_ctx; +struct io_ctx; +struct scan_ctx; +struct interp_ctx; + +struct MIR_context { + struct gen_ctx *gen_ctx; /* should be the 1st member */ + struct c2mir_ctx *c2mir_ctx; /* should be the 2nd member */ + MIR_error_func_t error_func; + VARR (MIR_insn_t) * temp_insns, *temp_insns2; + VARR (MIR_op_t) * temp_insn_ops; + VARR (MIR_var_t) * temp_vars; + VARR (MIR_type_t) * temp_types; + VARR (size_t) * insn_nops; + VARR (char) * temp_string; + VARR (uint8_t) * temp_data; + char temp_buff[30]; + HTAB (MIR_item_t) * module_item_tab; + /* Module to keep items potentially used by all modules: */ + struct MIR_module environment_module; + VARR (MIR_reg_t) * inline_reg_map; + MIR_module_t curr_module; + MIR_func_t curr_func; + int curr_label_num; + DLIST (MIR_module_t) all_modules; + VARR (MIR_module_t) * modules_to_link; + struct string_ctx *string_ctx; + struct reg_ctx *reg_ctx; + struct simplify_ctx *simplify_ctx; + struct machine_code_ctx *machine_code_ctx; + struct io_ctx *io_ctx; + struct scan_ctx *scan_ctx; + struct interp_ctx *interp_ctx; + size_t inlined_calls, inline_insns_before, inline_insns_after; +}; + +#define error_func ctx->error_func +#define temp_insns ctx->temp_insns +#define temp_insns2 ctx->temp_insns2 +#define temp_insn_ops ctx->temp_insn_ops +#define temp_vars ctx->temp_vars +#define temp_types ctx->temp_types +#define insn_nops ctx->insn_nops +#define temp_string ctx->temp_string +#define temp_data ctx->temp_data +#define temp_buff ctx->temp_buff +#define module_item_tab ctx->module_item_tab +#define environment_module ctx->environment_module +#define inline_reg_map ctx->inline_reg_map +#define curr_module ctx->curr_module +#define curr_func ctx->curr_func +#define curr_label_num ctx->curr_label_num +#define all_modules ctx->all_modules +#define modules_to_link ctx->modules_to_link +#define inlined_calls ctx->inlined_calls +#define inline_insns_before ctx->inline_insns_before +#define inline_insns_after ctx->inline_insns_after + +static void util_error (MIR_context_t ctx, const char *message); +#define MIR_VARR_ERROR util_error +#define MIR_HTAB_ERROR MIR_VARR_ERROR + +#include "mir-hash.h" +#include "mir-htab.h" +#include "mir-reduce.h" +#include +#include +#include +#include +#include +#include + +static void interp_init (MIR_context_t ctx); +static void finish_func_interpretation (MIR_item_t func_item); +static void interp_finish (MIR_context_t ctx); + +static void MIR_NO_RETURN default_error (enum MIR_error_type error_type, const char *format, ...) { + va_list ap; + + va_start (ap, format); + vfprintf (stderr, format, ap); + fprintf (stderr, "\n"); + va_end (ap); + exit (1); +} + +static void MIR_NO_RETURN util_error (MIR_context_t ctx, const char *message) { + (*error_func) (MIR_alloc_error, message); +} + +#define HARD_REG_NAME_PREFIX "hr" +#define TEMP_REG_NAME_PREFIX "t" +#define TEMP_ITEM_NAME_PREFIX ".lc" + +int _MIR_reserved_ref_name_p (MIR_context_t ctx, const char *name) { + return strncmp (name, TEMP_ITEM_NAME_PREFIX, strlen (TEMP_ITEM_NAME_PREFIX)) == 0; +} + +/* Reserved names: + fp - frame pointer + hr - a hardware reg + lc - a temp item */ +int _MIR_reserved_name_p (MIR_context_t ctx, const char *name) { + size_t i, start; + + if (_MIR_reserved_ref_name_p (ctx, name)) + return TRUE; + else if (strncmp (name, HARD_REG_NAME_PREFIX, strlen (HARD_REG_NAME_PREFIX)) == 0) + start = strlen (HARD_REG_NAME_PREFIX); + else + return FALSE; + for (i = start; name[i] != '\0'; i++) + if (name[i] < '0' || name[i] > '9') return FALSE; + return TRUE; +} + +struct insn_desc { + MIR_insn_code_t code; + const char *name; + unsigned op_modes[4]; +}; + +#define OUTPUT_FLAG (1 << 31) + +static const struct insn_desc insn_descs[] = { + {MIR_MOV, "mov", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_FMOV, "fmov", {MIR_OP_FLOAT | OUTPUT_FLAG, MIR_OP_FLOAT, MIR_OP_BOUND}}, + {MIR_DMOV, "dmov", {MIR_OP_DOUBLE | OUTPUT_FLAG, MIR_OP_DOUBLE, MIR_OP_BOUND}}, + {MIR_LDMOV, "ldmov", {MIR_OP_LDOUBLE | OUTPUT_FLAG, MIR_OP_LDOUBLE, MIR_OP_BOUND}}, + {MIR_EXT8, "ext8", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_EXT16, "ext16", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_EXT32, "ext32", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_UEXT8, "uext8", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_UEXT16, "uext16", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_UEXT32, "uext32", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_I2F, "i2f", {MIR_OP_FLOAT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_I2D, "i2d", {MIR_OP_DOUBLE | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_I2LD, "i2ld", {MIR_OP_LDOUBLE | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_UI2F, "ui2f", {MIR_OP_FLOAT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_UI2D, "ui2d", {MIR_OP_DOUBLE | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_UI2LD, "ui2ld", {MIR_OP_LDOUBLE | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_F2I, "f2i", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_FLOAT, MIR_OP_BOUND}}, + {MIR_D2I, "d2i", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_DOUBLE, MIR_OP_BOUND}}, + {MIR_LD2I, "ld2i", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_LDOUBLE, MIR_OP_BOUND}}, + {MIR_F2D, "f2d", {MIR_OP_DOUBLE | OUTPUT_FLAG, MIR_OP_FLOAT, MIR_OP_BOUND}}, + {MIR_F2LD, "f2ld", {MIR_OP_LDOUBLE | OUTPUT_FLAG, MIR_OP_FLOAT, MIR_OP_BOUND}}, + {MIR_D2F, "d2f", {MIR_OP_FLOAT | OUTPUT_FLAG, MIR_OP_DOUBLE, MIR_OP_BOUND}}, + {MIR_D2LD, "d2ld", {MIR_OP_LDOUBLE | OUTPUT_FLAG, MIR_OP_DOUBLE, MIR_OP_BOUND}}, + {MIR_LD2F, "ld2f", {MIR_OP_FLOAT | OUTPUT_FLAG, MIR_OP_LDOUBLE, MIR_OP_BOUND}}, + {MIR_LD2D, "ld2d", {MIR_OP_DOUBLE | OUTPUT_FLAG, MIR_OP_LDOUBLE, MIR_OP_BOUND}}, + {MIR_NEG, "neg", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_NEGS, "negs", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_FNEG, "fneg", {MIR_OP_FLOAT | OUTPUT_FLAG, MIR_OP_FLOAT, MIR_OP_BOUND}}, + {MIR_DNEG, "dneg", {MIR_OP_DOUBLE | OUTPUT_FLAG, MIR_OP_DOUBLE, MIR_OP_BOUND}}, + {MIR_LDNEG, "ldneg", {MIR_OP_LDOUBLE | OUTPUT_FLAG, MIR_OP_LDOUBLE, MIR_OP_BOUND}}, + {MIR_ADD, "add", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_ADDS, "adds", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_FADD, "fadd", {MIR_OP_FLOAT | OUTPUT_FLAG, MIR_OP_FLOAT, MIR_OP_FLOAT, MIR_OP_BOUND}}, + {MIR_DADD, "dadd", {MIR_OP_DOUBLE | OUTPUT_FLAG, MIR_OP_DOUBLE, MIR_OP_DOUBLE, MIR_OP_BOUND}}, + {MIR_LDADD, + "ldadd", + {MIR_OP_LDOUBLE | OUTPUT_FLAG, MIR_OP_LDOUBLE, MIR_OP_LDOUBLE, MIR_OP_BOUND}}, + {MIR_SUB, "sub", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_SUBS, "subs", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_FSUB, "fsub", {MIR_OP_FLOAT | OUTPUT_FLAG, MIR_OP_FLOAT, MIR_OP_FLOAT, MIR_OP_BOUND}}, + {MIR_DSUB, "dsub", {MIR_OP_DOUBLE | OUTPUT_FLAG, MIR_OP_DOUBLE, MIR_OP_DOUBLE, MIR_OP_BOUND}}, + {MIR_LDSUB, + "ldsub", + {MIR_OP_LDOUBLE | OUTPUT_FLAG, MIR_OP_LDOUBLE, MIR_OP_LDOUBLE, MIR_OP_BOUND}}, + {MIR_MUL, "mul", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_MULS, "muls", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_FMUL, "fmul", {MIR_OP_FLOAT | OUTPUT_FLAG, MIR_OP_FLOAT, MIR_OP_FLOAT, MIR_OP_BOUND}}, + {MIR_DMUL, "dmul", {MIR_OP_DOUBLE | OUTPUT_FLAG, MIR_OP_DOUBLE, MIR_OP_DOUBLE, MIR_OP_BOUND}}, + {MIR_LDMUL, + "ldmul", + {MIR_OP_LDOUBLE | OUTPUT_FLAG, MIR_OP_LDOUBLE, MIR_OP_LDOUBLE, MIR_OP_BOUND}}, + {MIR_DIV, "div", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_DIVS, "divs", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_UDIV, "udiv", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_UDIVS, "udivs", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_FDIV, "fdiv", {MIR_OP_FLOAT | OUTPUT_FLAG, MIR_OP_FLOAT, MIR_OP_FLOAT, MIR_OP_BOUND}}, + {MIR_DDIV, "ddiv", {MIR_OP_DOUBLE | OUTPUT_FLAG, MIR_OP_DOUBLE, MIR_OP_DOUBLE, MIR_OP_BOUND}}, + {MIR_LDDIV, + "lddiv", + {MIR_OP_LDOUBLE | OUTPUT_FLAG, MIR_OP_LDOUBLE, MIR_OP_LDOUBLE, MIR_OP_BOUND}}, + {MIR_MOD, "mod", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_MODS, "mods", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_UMOD, "umod", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_UMODS, "umods", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_AND, "and", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_ANDS, "ands", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_OR, "or", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_ORS, "ors", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_XOR, "xor", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_XORS, "xors", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_LSH, "lsh", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_LSHS, "lshs", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_RSH, "rsh", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_RSHS, "rshs", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_URSH, "ursh", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_URSHS, "urshs", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_EQ, "eq", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_EQS, "eqs", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_FEQ, "feq", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_FLOAT, MIR_OP_FLOAT, MIR_OP_BOUND}}, + {MIR_DEQ, "deq", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_DOUBLE, MIR_OP_DOUBLE, MIR_OP_BOUND}}, + {MIR_LDEQ, "ldeq", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_LDOUBLE, MIR_OP_LDOUBLE, MIR_OP_BOUND}}, + {MIR_NE, "ne", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_NES, "nes", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_FNE, "fne", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_FLOAT, MIR_OP_FLOAT, MIR_OP_BOUND}}, + {MIR_DNE, "dne", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_DOUBLE, MIR_OP_DOUBLE, MIR_OP_BOUND}}, + {MIR_LDNE, "ldne", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_LDOUBLE, MIR_OP_LDOUBLE, MIR_OP_BOUND}}, + {MIR_LT, "lt", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_LTS, "lts", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_ULT, "ult", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_ULTS, "ults", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_FLT, "flt", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_FLOAT, MIR_OP_FLOAT, MIR_OP_BOUND}}, + {MIR_DLT, "dlt", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_DOUBLE, MIR_OP_DOUBLE, MIR_OP_BOUND}}, + {MIR_LDLT, "ldlt", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_LDOUBLE, MIR_OP_LDOUBLE, MIR_OP_BOUND}}, + {MIR_LE, "le", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_LES, "les", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_ULE, "ule", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_ULES, "ules", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_FLE, "fle", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_FLOAT, MIR_OP_FLOAT, MIR_OP_BOUND}}, + {MIR_DLE, "dle", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_DOUBLE, MIR_OP_DOUBLE, MIR_OP_BOUND}}, + {MIR_LDLE, "ldle", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_LDOUBLE, MIR_OP_LDOUBLE, MIR_OP_BOUND}}, + {MIR_GT, "gt", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_GTS, "gts", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_UGT, "ugt", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_UGTS, "ugts", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_FGT, "fgt", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_FLOAT, MIR_OP_FLOAT, MIR_OP_BOUND}}, + {MIR_DGT, "dgt", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_DOUBLE, MIR_OP_DOUBLE, MIR_OP_BOUND}}, + {MIR_LDGT, "ldgt", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_LDOUBLE, MIR_OP_LDOUBLE, MIR_OP_BOUND}}, + {MIR_GE, "ge", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_GES, "ges", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_UGE, "uge", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_UGES, "uges", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_FGE, "fge", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_FLOAT, MIR_OP_FLOAT, MIR_OP_BOUND}}, + {MIR_DGE, "dge", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_DOUBLE, MIR_OP_DOUBLE, MIR_OP_BOUND}}, + {MIR_LDGE, "ldge", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_LDOUBLE, MIR_OP_LDOUBLE, MIR_OP_BOUND}}, + {MIR_JMP, "jmp", {MIR_OP_LABEL, MIR_OP_BOUND}}, + {MIR_BT, "bt", {MIR_OP_LABEL, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_BTS, "bts", {MIR_OP_LABEL, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_BF, "bf", {MIR_OP_LABEL, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_BFS, "bfs", {MIR_OP_LABEL, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_BEQ, "beq", {MIR_OP_LABEL, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_BEQS, "beqs", {MIR_OP_LABEL, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_FBEQ, "fbeq", {MIR_OP_LABEL, MIR_OP_FLOAT, MIR_OP_FLOAT, MIR_OP_BOUND}}, + {MIR_DBEQ, "dbeq", {MIR_OP_LABEL, MIR_OP_DOUBLE, MIR_OP_DOUBLE, MIR_OP_BOUND}}, + {MIR_LDBEQ, "ldbeq", {MIR_OP_LABEL, MIR_OP_LDOUBLE, MIR_OP_LDOUBLE, MIR_OP_BOUND}}, + {MIR_BNE, "bne", {MIR_OP_LABEL, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_BNES, "bnes", {MIR_OP_LABEL, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_FBNE, "fbne", {MIR_OP_LABEL, MIR_OP_FLOAT, MIR_OP_FLOAT, MIR_OP_BOUND}}, + {MIR_DBNE, "dbne", {MIR_OP_LABEL, MIR_OP_DOUBLE, MIR_OP_DOUBLE, MIR_OP_BOUND}}, + {MIR_LDBNE, "ldbne", {MIR_OP_LABEL, MIR_OP_LDOUBLE, MIR_OP_LDOUBLE, MIR_OP_BOUND}}, + {MIR_BLT, "blt", {MIR_OP_LABEL, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_BLTS, "blts", {MIR_OP_LABEL, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_UBLT, "ublt", {MIR_OP_LABEL, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_UBLTS, "ublts", {MIR_OP_LABEL, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_FBLT, "fblt", {MIR_OP_LABEL, MIR_OP_FLOAT, MIR_OP_FLOAT, MIR_OP_BOUND}}, + {MIR_DBLT, "dblt", {MIR_OP_LABEL, MIR_OP_DOUBLE, MIR_OP_DOUBLE, MIR_OP_BOUND}}, + {MIR_LDBLT, "ldblt", {MIR_OP_LABEL, MIR_OP_LDOUBLE, MIR_OP_LDOUBLE, MIR_OP_BOUND}}, + {MIR_BLE, "ble", {MIR_OP_LABEL, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_BLES, "bles", {MIR_OP_LABEL, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_UBLE, "uble", {MIR_OP_LABEL, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_UBLES, "ubles", {MIR_OP_LABEL, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_FBLE, "fble", {MIR_OP_LABEL, MIR_OP_FLOAT, MIR_OP_FLOAT, MIR_OP_BOUND}}, + {MIR_DBLE, "dble", {MIR_OP_LABEL, MIR_OP_DOUBLE, MIR_OP_DOUBLE, MIR_OP_BOUND}}, + {MIR_LDBLE, "ldble", {MIR_OP_LABEL, MIR_OP_LDOUBLE, MIR_OP_LDOUBLE, MIR_OP_BOUND}}, + {MIR_BGT, "bgt", {MIR_OP_LABEL, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_BGTS, "bgts", {MIR_OP_LABEL, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_UBGT, "ubgt", {MIR_OP_LABEL, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_UBGTS, "ubgts", {MIR_OP_LABEL, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_FBGT, "fbgt", {MIR_OP_LABEL, MIR_OP_FLOAT, MIR_OP_FLOAT, MIR_OP_BOUND}}, + {MIR_DBGT, "dbgt", {MIR_OP_LABEL, MIR_OP_DOUBLE, MIR_OP_DOUBLE, MIR_OP_BOUND}}, + {MIR_LDBGT, "ldbgt", {MIR_OP_LABEL, MIR_OP_LDOUBLE, MIR_OP_LDOUBLE, MIR_OP_BOUND}}, + {MIR_BGE, "bge", {MIR_OP_LABEL, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_BGES, "bges", {MIR_OP_LABEL, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_UBGE, "ubge", {MIR_OP_LABEL, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_UBGES, "ubges", {MIR_OP_LABEL, MIR_OP_INT, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_FBGE, "fbge", {MIR_OP_LABEL, MIR_OP_FLOAT, MIR_OP_FLOAT, MIR_OP_BOUND}}, + {MIR_DBGE, "dbge", {MIR_OP_LABEL, MIR_OP_DOUBLE, MIR_OP_DOUBLE, MIR_OP_BOUND}}, + {MIR_LDBGE, "ldbge", {MIR_OP_LABEL, MIR_OP_LDOUBLE, MIR_OP_LDOUBLE, MIR_OP_BOUND}}, + {MIR_CALL, "call", {MIR_OP_BOUND}}, + {MIR_INLINE, "inline", {MIR_OP_BOUND}}, + {MIR_SWITCH, "switch", {MIR_OP_BOUND}}, + {MIR_RET, "ret", {MIR_OP_BOUND}}, + {MIR_ALLOCA, "alloca", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_BSTART, "bstart", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_BOUND}}, + {MIR_BEND, "bend", {MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_VA_ARG, "va_arg", {MIR_OP_INT | OUTPUT_FLAG, MIR_OP_INT, MIR_OP_UNDEF, MIR_OP_BOUND}}, + {MIR_VA_START, "va_start", {MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_VA_END, "va_end", {MIR_OP_INT, MIR_OP_BOUND}}, + {MIR_LABEL, "label", {MIR_OP_BOUND}}, + {MIR_INVALID_INSN, "invalid-insn", {MIR_OP_BOUND}}, +}; + +static void check_and_prepare_insn_descs (MIR_context_t ctx) { + size_t i, j; + + VARR_CREATE (size_t, insn_nops, 0); + for (i = 0; i < MIR_INSN_BOUND; i++) { + mir_assert (insn_descs[i].code == i); + for (j = 0; insn_descs[i].op_modes[j] != MIR_OP_BOUND; j++) + ; + VARR_PUSH (size_t, insn_nops, j); + } +} + +static MIR_op_mode_t type2mode (MIR_type_t type) { + return (type == MIR_T_F + ? MIR_OP_FLOAT + : type == MIR_T_D ? MIR_OP_DOUBLE : type == MIR_T_LD ? MIR_OP_LDOUBLE : MIR_OP_INT); +} + +/* New Page */ + +typedef struct string { + size_t num; /* string number starting with 1 */ + MIR_str_t str; +} string_t; + +DEF_VARR (string_t); +DEF_HTAB (string_t); + +struct string_ctx { + VARR (string_t) * strings; + HTAB (string_t) * string_tab; +}; + +#define strings ctx->string_ctx->strings +#define string_tab ctx->string_ctx->string_tab + +static htab_hash_t str_hash (string_t str) { return mir_hash (str.str.s, str.str.len, 0); } +static int str_eq (string_t str1, string_t str2) { + return str1.str.len == str2.str.len && memcmp (str1.str.s, str2.str.s, str1.str.len) == 0; +} + +static void string_init (VARR (string_t) * *strs, HTAB (string_t) * *str_tab) { + string_t string = {0, {0, NULL}}; + + VARR_CREATE (string_t, *strs, 0); + VARR_PUSH (string_t, *strs, string); /* don't use 0th string */ + HTAB_CREATE (string_t, *str_tab, 1000, str_hash, str_eq); +} + +static int string_find (VARR (string_t) * *strs, HTAB (string_t) * *str_tab, MIR_str_t str, + string_t *s) { + string_t string; + + string.str = str; + return HTAB_DO (string_t, *str_tab, string, HTAB_FIND, *s); +} + +static string_t string_store (MIR_context_t ctx, VARR (string_t) * *strs, + HTAB (string_t) * *str_tab, MIR_str_t str) { + char *heap_str; + string_t el, string; + + if (string_find (strs, str_tab, str, &el)) return el; + if ((heap_str = malloc (str.len)) == NULL) + (*error_func) (MIR_alloc_error, "Not enough memory for strings"); + memcpy (heap_str, str.s, str.len); + string.str.s = heap_str; + string.str.len = str.len; + string.num = VARR_LENGTH (string_t, *strs); + VARR_PUSH (string_t, *strs, string); + HTAB_DO (string_t, *str_tab, string, HTAB_INSERT, el); + return string; +} + +static void string_finish (VARR (string_t) * *strs, HTAB (string_t) * *str_tab) { + size_t i; + + for (i = 1; i < VARR_LENGTH (string_t, *strs); i++) + free ((char *) VARR_ADDR (string_t, *strs)[i].str.s); + VARR_DESTROY (string_t, *strs); + HTAB_DESTROY (string_t, *str_tab); +} + +/* New Page */ + +typedef struct reg_desc { + size_t name_num; /* 1st key for the namenum2rdn hash tab */ + MIR_func_t func; /* 2nd key for hash the both tabs */ + MIR_type_t type; + MIR_reg_t reg; /* 1st key reg2rdn hash tab */ +} reg_desc_t; + +DEF_VARR (reg_desc_t); + +typedef struct size_ctx { + size_t rdn; + MIR_context_t ctx; +} size_ctx_t; + +DEF_HTAB (size_ctx_t); + +struct reg_ctx { + VARR (reg_desc_t) * reg_descs; + HTAB (size_ctx_t) * namenum2rdn_tab; + HTAB (size_ctx_t) * reg2rdn_tab; +}; + +#define reg_descs ctx->reg_ctx->reg_descs +#define namenum2rdn_tab ctx->reg_ctx->namenum2rdn_tab +#define reg2rdn_tab ctx->reg_ctx->reg2rdn_tab + +static int namenum2rdn_eq (size_ctx_t sc1, size_ctx_t sc2) { + MIR_context_t ctx = sc1.ctx; + reg_desc_t *addr = VARR_ADDR (reg_desc_t, reg_descs); + + mir_assert (ctx == sc2.ctx); + return (addr[sc1.rdn].name_num == addr[sc2.rdn].name_num + && addr[sc1.rdn].func == addr[sc2.rdn].func); +} + +static htab_hash_t namenum2rdn_hash (size_ctx_t sc) { + MIR_context_t ctx = sc.ctx; + reg_desc_t *addr = VARR_ADDR (reg_desc_t, reg_descs); + + return mir_hash_finish ( + mir_hash_step (mir_hash_step (mir_hash_init (0), (uint64_t) addr[sc.rdn].func), + (uint64_t) addr[sc.rdn].name_num)); +} + +static int reg2rdn_eq (size_ctx_t sc1, size_ctx_t sc2) { + MIR_context_t ctx = sc1.ctx; + reg_desc_t *addr = VARR_ADDR (reg_desc_t, reg_descs); + + mir_assert (ctx == sc2.ctx); + return addr[sc1.rdn].reg == addr[sc2.rdn].reg && addr[sc1.rdn].func == addr[sc2.rdn].func; +} + +static htab_hash_t reg2rdn_hash (size_ctx_t sc) { + MIR_context_t ctx = sc.ctx; + reg_desc_t *addr = VARR_ADDR (reg_desc_t, reg_descs); + + return mir_hash_finish ( + mir_hash_step (mir_hash_step (mir_hash_init (0), (uint64_t) addr[sc.rdn].func), + addr[sc.rdn].reg)); +} + +static void reg_init (MIR_context_t ctx) { + reg_desc_t rd = {0, NULL, MIR_T_I64, 0}; + + if ((ctx->reg_ctx = malloc (sizeof (struct reg_ctx))) == NULL) + (*error_func) (MIR_alloc_error, "Not enough memory for ctx"); + VARR_CREATE (reg_desc_t, reg_descs, 300); + VARR_PUSH (reg_desc_t, reg_descs, rd); /* for 0 reg */ + HTAB_CREATE (size_ctx_t, namenum2rdn_tab, 300, namenum2rdn_hash, namenum2rdn_eq); + HTAB_CREATE (size_ctx_t, reg2rdn_tab, 300, reg2rdn_hash, reg2rdn_eq); +} + +static int func_reg_p (MIR_context_t ctx, MIR_func_t func, const char *name) { + size_ctx_t sc, tab_sc; + reg_desc_t rd; + int res; + + rd.name_num = string_store (ctx, &strings, &string_tab, (MIR_str_t){strlen (name) + 1, name}).num; + rd.func = func; + sc.rdn = VARR_LENGTH (reg_desc_t, reg_descs); + sc.ctx = ctx; + VARR_PUSH (reg_desc_t, reg_descs, rd); + res = HTAB_DO (size_ctx_t, namenum2rdn_tab, sc, HTAB_FIND, tab_sc); + VARR_POP (reg_desc_t, reg_descs); + return res; +} + +static MIR_reg_t create_func_reg (MIR_context_t ctx, MIR_func_t func, const char *name, + MIR_reg_t reg, MIR_type_t type, int any_p) { + reg_desc_t rd; + size_ctx_t sc, tab_sc; + int htab_res; + + if (!any_p && _MIR_reserved_name_p (ctx, name)) + (*error_func) (MIR_reserved_name_error, "redefining a reserved name %s", name); + rd.name_num = string_store (ctx, &strings, &string_tab, (MIR_str_t){strlen (name) + 1, name}).num; + rd.func = func; + rd.type = type; + rd.reg = reg; /* 0 is reserved */ + sc.rdn = VARR_LENGTH (reg_desc_t, reg_descs); + sc.ctx = ctx; + VARR_PUSH (reg_desc_t, reg_descs, rd); + if (HTAB_DO (size_ctx_t, namenum2rdn_tab, sc, HTAB_FIND, tab_sc)) { + VARR_POP (reg_desc_t, reg_descs); + (*error_func) (MIR_repeated_decl_error, "Repeated reg declaration %s", name); + } + htab_res = HTAB_DO (size_ctx_t, namenum2rdn_tab, sc, HTAB_INSERT, tab_sc); + mir_assert (!htab_res); + htab_res = HTAB_DO (size_ctx_t, reg2rdn_tab, sc, HTAB_INSERT, tab_sc); + mir_assert (!htab_res); + return reg; +} + +static void reg_finish (MIR_context_t ctx) { + VARR_DESTROY (reg_desc_t, reg_descs); + HTAB_DESTROY (size_ctx_t, namenum2rdn_tab); + HTAB_DESTROY (size_ctx_t, reg2rdn_tab); + free (ctx->reg_ctx); + ctx->reg_ctx = NULL; +} + +/* New Page */ + +static void push_data (MIR_context_t ctx, uint8_t *els, size_t size) { + for (size_t i = 0; i < size; i++) VARR_PUSH (uint8_t, temp_data, els[i]); +} + +const char *MIR_item_name (MIR_context_t ctx, MIR_item_t item) { + return (item->item_type == MIR_func_item + ? item->u.func->name + : item->item_type == MIR_proto_item + ? item->u.proto->name + : item->item_type == MIR_import_item + ? item->u.import + : item->item_type == MIR_export_item + ? item->u.export + : item->item_type == MIR_forward_item + ? item->u.forward + : item->item_type == MIR_bss_item + ? item->u.bss->name + : item->item_type == MIR_data_item + ? item->u.data->name + : item->item_type == MIR_ref_data_item + ? item->u.ref_data->name + : item->u.expr_data->name); +} + +#if !MIR_NO_IO +static void io_init (MIR_context_t ctx); +static void io_finish (MIR_context_t ctx); +#endif + +#if !MIR_NO_SCAN +static void scan_init (MIR_context_t ctx); +static void scan_finish (MIR_context_t ctx); +#endif + +static void vn_init (MIR_context_t ctx); +static void vn_finish (MIR_context_t ctx); + +MIR_error_func_t MIR_get_error_func (MIR_context_t ctx) { return error_func; } + +void MIR_set_error_func (MIR_context_t ctx, MIR_error_func_t func) { error_func = func; } + +static htab_hash_t item_hash (MIR_item_t it) { + return mir_hash_finish ( + mir_hash_step (mir_hash_step (mir_hash_init (28), (uint64_t) MIR_item_name (NULL, it)), + (uint64_t) it->module)); +} +static int item_eq (MIR_item_t it1, MIR_item_t it2) { + return it1->module == it2->module && MIR_item_name (NULL, it1) == MIR_item_name (NULL, it2); +} + +static MIR_item_t find_item (MIR_context_t ctx, const char *name, MIR_module_t module) { + MIR_item_t tab_item; + struct MIR_item item_s; + struct MIR_func func_s; + + item_s.item_type = MIR_func_item; + func_s.name = name; + item_s.module = module; + item_s.u.func = &func_s; + if (HTAB_DO (MIR_item_t, module_item_tab, &item_s, HTAB_FIND, tab_item)) return tab_item; + return NULL; +} + +static void init_module (MIR_context_t ctx, MIR_module_t m, const char *name) { + m->data = NULL; + m->last_temp_item_num = 0; + m->name = string_store (ctx, &strings, &string_tab, (MIR_str_t){strlen (name) + 1, name}).str.s; + DLIST_INIT (MIR_item_t, m->items); +} + +static void code_init (MIR_context_t ctx); +static void code_finish (MIR_context_t ctx); + +MIR_context_t MIR_init (void) { + MIR_context_t ctx; + + if ((ctx = malloc (sizeof (struct MIR_context))) == NULL) + default_error (MIR_alloc_error, "Not enough memory for ctx"); + ctx->string_ctx = NULL; + ctx->reg_ctx = NULL; + ctx->simplify_ctx = NULL; + ctx->machine_code_ctx = NULL; + ctx->io_ctx = NULL; + ctx->scan_ctx = NULL; + ctx->interp_ctx = NULL; +#ifndef NDEBUG + for (MIR_insn_code_t c = 0; c < MIR_INVALID_INSN; c++) mir_assert (c == insn_descs[c].code); +#endif + error_func = default_error; + curr_module = NULL; + curr_func = NULL; + curr_label_num = 0; + if ((ctx->string_ctx = malloc (sizeof (struct string_ctx))) == NULL) + (*error_func) (MIR_alloc_error, "Not enough memory for ctx"); + string_init (&strings, &string_tab); + reg_init (ctx); + VARR_CREATE (MIR_insn_t, temp_insns, 0); + VARR_CREATE (MIR_insn_t, temp_insns2, 0); + VARR_CREATE (MIR_op_t, temp_insn_ops, 0); + VARR_CREATE (MIR_var_t, temp_vars, 0); + VARR_CREATE (MIR_type_t, temp_types, 0); + check_and_prepare_insn_descs (ctx); + DLIST_INIT (MIR_module_t, all_modules); + vn_init (ctx); + VARR_CREATE (MIR_reg_t, inline_reg_map, 256); + VARR_CREATE (char, temp_string, 64); + VARR_CREATE (uint8_t, temp_data, 512); +#if !MIR_NO_IO + io_init (ctx); +#endif +#if !MIR_NO_SCAN + scan_init (ctx); +#endif + VARR_CREATE (MIR_module_t, modules_to_link, 0); + init_module (ctx, &environment_module, ".environment"); + HTAB_CREATE (MIR_item_t, module_item_tab, 512, item_hash, item_eq); + code_init (ctx); + interp_init (ctx); + inlined_calls = inline_insns_before = inline_insns_after = 0; + return ctx; +} + +void MIR_finish (MIR_context_t ctx) { + interp_finish (ctx); + HTAB_DESTROY (MIR_item_t, module_item_tab); + VARR_DESTROY (MIR_module_t, modules_to_link); +#if !MIR_NO_SCAN + scan_finish (ctx); +#endif +#if !MIR_NO_IO + io_finish (ctx); +#endif + VARR_DESTROY (uint8_t, temp_data); + VARR_DESTROY (char, temp_string); + VARR_DESTROY (MIR_reg_t, inline_reg_map); + reg_finish (ctx); + string_finish (&strings, &string_tab); + vn_finish (ctx); + VARR_DESTROY (MIR_var_t, temp_vars); + VARR_DESTROY (size_t, insn_nops); + VARR_DESTROY (MIR_op_t, temp_insn_ops); + VARR_DESTROY (MIR_insn_t, temp_insns2); + VARR_DESTROY (MIR_insn_t, temp_insns); + VARR_DESTROY (MIR_type_t, temp_types); + code_finish (ctx); + if (curr_func != NULL) + (*error_func) (MIR_finish_error, "finish when function %s is not finished", curr_func->name); + if (curr_module != NULL) + (*error_func) (MIR_finish_error, "finish when module %s is not finished", curr_module->name); +#if 0 + if (inlined_calls != 0) + fprintf (stderr, "inlined calls = %lu, insns before = %lu, insns_after = %lu, ratio = %.2f\n", + inlined_calls, inline_insns_before, inline_insns_after, + (double) inline_insns_after / inline_insns_before); +#endif + free (ctx->string_ctx); + free (ctx); + ctx = NULL; +} + +MIR_module_t MIR_new_module (MIR_context_t ctx, const char *name) { + if (curr_module != NULL) + (*error_func) (MIR_nested_module_error, + "Creating module when previous module %s is not finished", curr_module->name); + if ((curr_module = malloc (sizeof (struct MIR_module))) == NULL) + (*error_func) (MIR_alloc_error, "Not enough memory for module %s creation", name); + init_module (ctx, curr_module, name); + DLIST_APPEND (MIR_module_t, all_modules, curr_module); + return curr_module; +} + +DLIST (MIR_module_t) * MIR_get_module_list (MIR_context_t ctx) { return &all_modules; } + +static const char *type_str (MIR_type_t tp) { + switch (tp) { + case MIR_T_I8: return "i8"; + case MIR_T_U8: return "u8"; + case MIR_T_I16: return "i16"; + case MIR_T_U16: return "u16"; + case MIR_T_I32: return "i32"; + case MIR_T_U32: return "u32"; + case MIR_T_I64: return "i64"; + case MIR_T_U64: return "u64"; + case MIR_T_F: return "f"; + case MIR_T_D: return "d"; + case MIR_T_LD: return "ld"; + case MIR_T_P: return "p"; + default: return ""; + } +} + +const char *MIR_type_str (MIR_context_t ctx, MIR_type_t tp) { + const char *str = type_str (tp); + + if (strcmp (str, "") == 0) + (*error_func) (MIR_wrong_param_value_error, "MIR_type_str: wrong type"); + return str; +} + +static MIR_item_t add_item (MIR_context_t ctx, MIR_item_t item) { + int replace_p; + MIR_item_t tab_item; + + if ((tab_item = find_item (ctx, MIR_item_name (ctx, item), item->module)) == NULL) { + DLIST_APPEND (MIR_item_t, curr_module->items, item); + HTAB_DO (MIR_item_t, module_item_tab, item, HTAB_INSERT, item); + return item; + } + switch (tab_item->item_type) { + case MIR_import_item: + if (item->item_type != MIR_import_item) + (*error_func) (MIR_import_export_error, + "existing module definition %s already defined as import", tab_item->u.import); + item = tab_item; + break; + case MIR_export_item: + case MIR_forward_item: + replace_p = FALSE; + if (item->item_type == MIR_import_item) { + (*error_func) (MIR_import_export_error, "export/forward of import %s", item->u.import); + } else if (item->item_type != MIR_export_item && item->item_type != MIR_forward_item) { + replace_p = TRUE; + DLIST_APPEND (MIR_item_t, curr_module->items, item); + } else { + if (tab_item->item_type == item->item_type) + item = tab_item; + else + DLIST_APPEND (MIR_item_t, curr_module->items, item); + if (item->item_type == MIR_export_item && tab_item->item_type == MIR_forward_item) + replace_p = TRUE; + } + if (replace_p) { /* replace forward by export or export/forward by its definition: */ + tab_item->ref_def = item; + if (tab_item->item_type == MIR_export_item) item->export_p = TRUE; + HTAB_DO (MIR_item_t, module_item_tab, tab_item, HTAB_DELETE, tab_item); + HTAB_DO (MIR_item_t, module_item_tab, item, HTAB_INSERT, tab_item); + mir_assert (item == tab_item); + } + break; + case MIR_proto_item: + (*error_func) (MIR_repeated_decl_error, "item %s was already defined as proto", + tab_item->u.proto->name); + break; + case MIR_bss_item: + case MIR_data_item: + case MIR_ref_data_item: + case MIR_expr_data_item: + case MIR_func_item: + if (item->item_type == MIR_export_item) { + if (tab_item->export_p) { + item = tab_item; + } else { /* just keep one export: */ + tab_item->export_p = TRUE; + DLIST_APPEND (MIR_item_t, curr_module->items, item); + item->ref_def = tab_item; + } + } else if (item->item_type == MIR_forward_item) { + DLIST_APPEND (MIR_item_t, curr_module->items, item); + item->ref_def = tab_item; + } else if (item->item_type == MIR_import_item) { + (*error_func) (MIR_import_export_error, "import of local definition %s", item->u.import); + } else { + (*error_func) (MIR_repeated_decl_error, "Repeated item declaration %s", + MIR_item_name (ctx, item)); + } + break; + default: mir_assert (FALSE); + } + return item; +} + +static MIR_item_t create_item (MIR_context_t ctx, MIR_item_type_t item_type, + const char *item_name) { + MIR_item_t item; + + if (curr_module == NULL) (*error_func) (MIR_no_module_error, "%s outside module", item_name); + if ((item = malloc (sizeof (struct MIR_item))) == NULL) + (*error_func) (MIR_alloc_error, "Not enough memory for creation of item %s", item_name); + item->data = NULL; + item->module = curr_module; + item->item_type = item_type; + item->ref_def = NULL; + item->export_p = FALSE; + item->addr = NULL; + return item; +} + +static MIR_item_t new_export_import_forward (MIR_context_t ctx, const char *name, + MIR_item_type_t item_type, const char *item_name, + int create_only_p) { + MIR_item_t item, tab_item; + const char *uniq_name; + + item = create_item (ctx, item_type, item_name); + uniq_name = string_store (ctx, &strings, &string_tab, (MIR_str_t){strlen (name) + 1, name}).str.s; + if (item_type == MIR_export_item) + item->u.export = uniq_name; + else if (item_type == MIR_import_item) + item->u.import = uniq_name; + else + item->u.forward = uniq_name; + if (create_only_p) return item; + if ((tab_item = add_item (ctx, item)) != item) { + free (item); + item = tab_item; + } + return item; +} + +MIR_item_t MIR_new_export (MIR_context_t ctx, const char *name) { + return new_export_import_forward (ctx, name, MIR_export_item, "export", FALSE); +} + +MIR_item_t MIR_new_import (MIR_context_t ctx, const char *name) { + return new_export_import_forward (ctx, name, MIR_import_item, "import", FALSE); +} + +MIR_item_t MIR_new_forward (MIR_context_t ctx, const char *name) { + return new_export_import_forward (ctx, name, MIR_forward_item, "forward", FALSE); +} + +MIR_item_t MIR_new_bss (MIR_context_t ctx, const char *name, size_t len) { + MIR_item_t tab_item, item = create_item (ctx, MIR_bss_item, "bss"); + + item->u.bss = malloc (sizeof (struct MIR_bss)); + if (item->u.bss == NULL) { + free (item); + (*error_func) (MIR_alloc_error, "Not enough memory for creation of bss %s", name); + } + if (name != NULL) + name = string_store (ctx, &strings, &string_tab, (MIR_str_t){strlen (name) + 1, name}).str.s; + item->u.bss->name = name; + item->u.bss->len = len; + if (name == NULL) { + DLIST_APPEND (MIR_item_t, curr_module->items, item); + } else if ((tab_item = add_item (ctx, item)) != item) { + free (item); + item = tab_item; + } + return item; +} + +size_t _MIR_type_size (MIR_context_t ctx, MIR_type_t type) { + switch (type) { + case MIR_T_I8: return sizeof (int8_t); + case MIR_T_U8: return sizeof (uint8_t); + case MIR_T_I16: return sizeof (int16_t); + case MIR_T_U16: return sizeof (uint16_t); + case MIR_T_I32: return sizeof (int32_t); + case MIR_T_U32: return sizeof (uint32_t); + case MIR_T_I64: return sizeof (int64_t); + case MIR_T_U64: return sizeof (uint64_t); + case MIR_T_F: return sizeof (float); + case MIR_T_D: return sizeof (double); + case MIR_T_LD: return sizeof (long double); + case MIR_T_P: return sizeof (void *); + default: mir_assert (FALSE); return 1; + } +} + +MIR_item_t MIR_new_data (MIR_context_t ctx, const char *name, MIR_type_t el_type, size_t nel, + const void *els) { + MIR_item_t tab_item, item = create_item (ctx, MIR_data_item, "data"); + MIR_data_t data; + size_t el_len = _MIR_type_size (ctx, el_type); + + item->u.data = data = malloc (sizeof (struct MIR_data) + el_len * nel); + if (data == NULL) { + free (item); + (*error_func) (MIR_alloc_error, "Not enough memory for creation of data %s", + name == NULL ? "" : name); + } + if (name != NULL) + name = string_store (ctx, &strings, &string_tab, (MIR_str_t){strlen (name) + 1, name}).str.s; + data->name = name; + if (name == NULL) { + DLIST_APPEND (MIR_item_t, curr_module->items, item); + } else if ((tab_item = add_item (ctx, item)) != item) { + free (item); + item = tab_item; + } + data->el_type = el_type; + data->nel = nel; + memcpy (data->u.els, els, el_len * nel); + return item; +} + +MIR_item_t MIR_new_string_data (MIR_context_t ctx, const char *name, MIR_str_t str) { + return MIR_new_data (ctx, name, MIR_T_U8, str.len, str.s); +} + +MIR_item_t MIR_new_ref_data (MIR_context_t ctx, const char *name, MIR_item_t ref_item, + int64_t disp) { + MIR_item_t tab_item, item = create_item (ctx, MIR_ref_data_item, "ref data"); + MIR_ref_data_t ref_data; + + item->u.ref_data = ref_data = malloc (sizeof (struct MIR_ref_data)); + if (ref_data == NULL) { + free (item); + (*error_func) (MIR_alloc_error, "Not enough memory for creation of ref data %s", + name == NULL ? "" : name); + } + if (name != NULL) + name = string_store (ctx, &strings, &string_tab, (MIR_str_t){strlen (name) + 1, name}).str.s; + ref_data->name = name; + ref_data->ref_item = ref_item; + ref_data->disp = disp; + if (name == NULL) { + DLIST_APPEND (MIR_item_t, curr_module->items, item); + } else if ((tab_item = add_item (ctx, item)) != item) { + free (item); + item = tab_item; + } + return item; +} + +MIR_item_t MIR_new_expr_data (MIR_context_t ctx, const char *name, MIR_item_t expr_item) { + MIR_item_t tab_item, item = create_item (ctx, MIR_expr_data_item, "expr data"); + MIR_expr_data_t expr_data; + + item->u.expr_data = expr_data = malloc (sizeof (struct MIR_expr_data)); + if (expr_data == NULL) { + free (item); + (*error_func) (MIR_alloc_error, "Not enough memory for creation of expr data %s", + name == NULL ? "" : name); + } + if (expr_item->item_type != MIR_func_item || expr_item->u.func->vararg_p + || expr_item->u.func->nargs != 0 || expr_item->u.func->nres != 1) + (*error_func) (MIR_binary_io_error, + "%s can not be an expr which should be non-argument, one result function", + MIR_item_name (ctx, expr_item)); + if (name != NULL) + name = string_store (ctx, &strings, &string_tab, (MIR_str_t){strlen (name) + 1, name}).str.s; + expr_data->name = name; + expr_data->expr_item = expr_item; + if (name == NULL) { + DLIST_APPEND (MIR_item_t, curr_module->items, item); + } else if ((tab_item = add_item (ctx, item)) != item) { + free (item); + item = tab_item; + } + return item; +} + +static MIR_item_t new_proto_arr (MIR_context_t ctx, const char *name, size_t nres, + MIR_type_t *res_types, size_t nargs, int vararg_p, + MIR_var_t *args) { + MIR_item_t proto_item, tab_item; + MIR_proto_t proto; + size_t i; + + if (curr_module == NULL) + (*error_func) (MIR_no_module_error, "Creating proto %s outside module", name); + proto_item = create_item (ctx, MIR_proto_item, "proto"); + proto_item->u.proto = proto = malloc (sizeof (struct MIR_proto) + nres * sizeof (MIR_type_t)); + if (proto == NULL) { + free (proto_item); + (*error_func) (MIR_alloc_error, "Not enough memory for creation of proto %s", name); + } + proto->name + = string_store (ctx, &strings, &string_tab, (MIR_str_t){strlen (name) + 1, name}).str.s; + proto->res_types = (MIR_type_t *) ((char *) proto + sizeof (struct MIR_proto)); + memcpy (proto->res_types, res_types, nres * sizeof (MIR_type_t)); + proto->nres = nres; + proto->vararg_p = vararg_p != 0; + tab_item = add_item (ctx, proto_item); + mir_assert (tab_item == proto_item); + VARR_CREATE (MIR_var_t, proto->args, nargs); + for (i = 0; i < nargs; i++) VARR_PUSH (MIR_var_t, proto->args, args[i]); + return proto_item; +} + +MIR_item_t MIR_new_proto_arr (MIR_context_t ctx, const char *name, size_t nres, + MIR_type_t *res_types, size_t nargs, MIR_var_t *args) { + return new_proto_arr (ctx, name, nres, res_types, nargs, FALSE, args); +} + +MIR_item_t MIR_new_vararg_proto_arr (MIR_context_t ctx, const char *name, size_t nres, + MIR_type_t *res_types, size_t nargs, MIR_var_t *args) { + return new_proto_arr (ctx, name, nres, res_types, nargs, TRUE, args); +} + +static MIR_item_t new_proto (MIR_context_t ctx, const char *name, size_t nres, + MIR_type_t *res_types, size_t nargs, int vararg_p, va_list argp) { + MIR_var_t arg; + size_t i; + + VARR_TRUNC (MIR_var_t, temp_vars, 0); + for (i = 0; i < nargs; i++) { + arg.type = va_arg (argp, MIR_type_t); + arg.name = va_arg (argp, const char *); + VARR_PUSH (MIR_var_t, temp_vars, arg); + } + return new_proto_arr (ctx, name, nres, res_types, nargs, vararg_p, + VARR_ADDR (MIR_var_t, temp_vars)); +} + +MIR_item_t MIR_new_proto (MIR_context_t ctx, const char *name, size_t nres, MIR_type_t *res_types, + size_t nargs, ...) { + va_list argp; + MIR_item_t proto_item; + + va_start (argp, nargs); + proto_item = new_proto (ctx, name, nres, res_types, nargs, FALSE, argp); + va_end (argp); + return proto_item; +} + +MIR_item_t MIR_new_vararg_proto (MIR_context_t ctx, const char *name, size_t nres, + MIR_type_t *res_types, size_t nargs, ...) { + va_list argp; + MIR_item_t proto_item; + + va_start (argp, nargs); + proto_item = new_proto (ctx, name, nres, res_types, nargs, TRUE, argp); + va_end (argp); + return proto_item; +} + +static MIR_item_t new_func_arr (MIR_context_t ctx, const char *name, size_t nres, + MIR_type_t *res_types, size_t nargs, int vararg_p, + MIR_var_t *vars) { + MIR_item_t func_item, tab_item; + MIR_func_t func; + + if (curr_func != NULL) + (*error_func) (MIR_nested_func_error, + "Creating function when previous function %s is not finished", curr_func->name); + if (nargs == 0 && vararg_p) + (*error_func) (MIR_vararg_func_error, "Variable arg function %s w/o any mandatory argument", + name); + func_item = create_item (ctx, MIR_func_item, "function"); + curr_func = func_item->u.func = func + = malloc (sizeof (struct MIR_func) + nres * sizeof (MIR_type_t)); + if (func == NULL) { + free (func_item); + (*error_func) (MIR_alloc_error, "Not enough memory for creation of func %s", name); + } + func->name + = string_store (ctx, &strings, &string_tab, (MIR_str_t){strlen (name) + 1, name}).str.s; + func->nres = nres; + func->res_types = (MIR_type_t *) ((char *) func + sizeof (struct MIR_func)); + memcpy (func->res_types, res_types, nres * sizeof (MIR_type_t)); + tab_item = add_item (ctx, func_item); + mir_assert (tab_item == func_item); + DLIST_INIT (MIR_insn_t, func->insns); + DLIST_INIT (MIR_insn_t, func->original_insns); + VARR_CREATE (MIR_var_t, func->vars, nargs + 8); + func->nargs = nargs; + func->last_temp_num = 0; + func->vararg_p = vararg_p != 0; + func->expr_p = FALSE; + func->n_inlines = 0; + func->machine_code = func->call_addr = NULL; + for (size_t i = 0; i < nargs; i++) { + MIR_type_t type = vars[i].type; + + VARR_PUSH (MIR_var_t, func->vars, vars[i]); + create_func_reg (ctx, func, vars[i].name, i + 1, + type == MIR_T_F || type == MIR_T_D || type == MIR_T_LD ? type : MIR_T_I64, + FALSE); + } + return func_item; +} + +MIR_item_t MIR_new_func_arr (MIR_context_t ctx, const char *name, size_t nres, + MIR_type_t *res_types, size_t nargs, MIR_var_t *vars) { + return new_func_arr (ctx, name, nres, res_types, nargs, FALSE, vars); +} + +MIR_item_t MIR_new_vararg_func_arr (MIR_context_t ctx, const char *name, size_t nres, + MIR_type_t *res_types, size_t nargs, MIR_var_t *vars) { + return new_func_arr (ctx, name, nres, res_types, nargs, TRUE, vars); +} + +static MIR_item_t new_func (MIR_context_t ctx, const char *name, size_t nres, MIR_type_t *res_types, + size_t nargs, int vararg_p, va_list argp) { + MIR_var_t var; + size_t i; + + VARR_TRUNC (MIR_var_t, temp_vars, 0); + for (i = 0; i < nargs; i++) { + var.type = va_arg (argp, MIR_type_t); + var.name = va_arg (argp, const char *); + VARR_PUSH (MIR_var_t, temp_vars, var); + } + return new_func_arr (ctx, name, nres, res_types, nargs, vararg_p, + VARR_ADDR (MIR_var_t, temp_vars)); +} + +MIR_item_t MIR_new_func (MIR_context_t ctx, const char *name, size_t nres, MIR_type_t *res_types, + size_t nargs, ...) { + va_list argp; + MIR_item_t func_item; + + va_start (argp, nargs); + func_item = new_func (ctx, name, nres, res_types, nargs, FALSE, argp); + va_end (argp); + return func_item; +} + +MIR_item_t MIR_new_vararg_func (MIR_context_t ctx, const char *name, size_t nres, + MIR_type_t *res_types, size_t nargs, ...) { + va_list argp; + MIR_item_t func_item; + + va_start (argp, nargs); + func_item = new_func (ctx, name, nres, res_types, nargs, TRUE, argp); + va_end (argp); + return func_item; +} + +MIR_reg_t MIR_new_func_reg (MIR_context_t ctx, MIR_func_t func, MIR_type_t type, const char *name) { + MIR_var_t var; + + if (type != MIR_T_I64 && type != MIR_T_F && type != MIR_T_D && type != MIR_T_LD) + (*error_func) (MIR_reg_type_error, "wrong type for register %s", name); + var.type = type; + var.name = string_store (ctx, &strings, &string_tab, (MIR_str_t){strlen (name) + 1, name}).str.s; + VARR_PUSH (MIR_var_t, func->vars, var); + return create_func_reg (ctx, func, name, VARR_LENGTH (MIR_var_t, func->vars), type, FALSE); +} + +static reg_desc_t *find_rd_by_name_num (MIR_context_t ctx, size_t name_num, MIR_func_t func) { + size_ctx_t sc, temp_sc; + reg_desc_t rd; + + rd.name_num = name_num; + rd.func = func; /* keys */ + rd.type = MIR_T_I64; + rd.reg = 0; /* to eliminate warnings */ + temp_sc.rdn = VARR_LENGTH (reg_desc_t, reg_descs); + temp_sc.ctx = ctx; + VARR_PUSH (reg_desc_t, reg_descs, rd); + if (!HTAB_DO (size_ctx_t, namenum2rdn_tab, temp_sc, HTAB_FIND, sc)) { + VARR_POP (reg_desc_t, reg_descs); + return NULL; /* undeclared */ + } + VARR_POP (reg_desc_t, reg_descs); + return &VARR_ADDR (reg_desc_t, reg_descs)[sc.rdn]; +} + +static reg_desc_t *find_rd_by_reg (MIR_context_t ctx, MIR_reg_t reg, MIR_func_t func) { + size_ctx_t sc, temp_sc; + reg_desc_t rd; + + rd.reg = reg; + rd.func = func; /* keys */ + rd.name_num = 0; + rd.type = MIR_T_I64; /* to eliminate warnings */ + temp_sc.rdn = VARR_LENGTH (reg_desc_t, reg_descs); + temp_sc.ctx = ctx; + VARR_PUSH (reg_desc_t, reg_descs, rd); + if (!HTAB_DO (size_ctx_t, reg2rdn_tab, temp_sc, HTAB_FIND, sc)) { + VARR_POP (reg_desc_t, reg_descs); + (*error_func) (MIR_undeclared_func_reg_error, "undeclared reg %u of func %s", reg, func->name); + } + VARR_POP (reg_desc_t, reg_descs); + return &VARR_ADDR (reg_desc_t, reg_descs)[sc.rdn]; +} + +void MIR_finish_func (MIR_context_t ctx) { + int expr_p = TRUE; + MIR_insn_t insn; + MIR_insn_code_t code; + + if (curr_func == NULL) (*error_func) (MIR_no_func_error, "finish of non-existing function"); + if (curr_func->vararg_p || curr_func->nargs != 0 || curr_func->nres != 1) expr_p = FALSE; + for (insn = DLIST_HEAD (MIR_insn_t, curr_func->insns); insn != NULL; + insn = DLIST_NEXT (MIR_insn_t, insn)) { + size_t i, actual_nops = MIR_insn_nops (ctx, insn); + MIR_op_mode_t mode, expected_mode; + reg_desc_t *rd; + int out_p, can_be_out_p; + + code = insn->code; + if (!curr_func->vararg_p && code == MIR_VA_START) { + curr_func = NULL; + (*error_func) (MIR_vararg_func_error, "va_start is not in vararg function"); + } else if (code == MIR_RET && actual_nops != curr_func->nres) { + curr_func = NULL; + (*error_func) (MIR_vararg_func_error, + "number of operands in return does not correspond number of function returns"); + } else if (MIR_call_code_p (code)) + expr_p = FALSE; + for (i = 0; i < actual_nops; i++) { + if (MIR_call_code_p (code)) { + if (i == 0) { + mir_assert (insn->ops[i].mode == MIR_OP_REF + && insn->ops[i].u.ref->item_type == MIR_proto_item); + continue; /* We checked the operand during insn creation -- skip the prototype */ + } else if (i == 1 && insn->ops[i].mode == MIR_OP_REF) { + mir_assert (insn->ops[i].u.ref->item_type == MIR_import_item + || insn->ops[i].u.ref->item_type == MIR_export_item + || insn->ops[i].u.ref->item_type == MIR_forward_item + || insn->ops[i].u.ref->item_type == MIR_func_item); + continue; /* We checked the operand during insn creation -- skip the func */ + } + } + if (code == MIR_VA_ARG && i == 2) { + mir_assert (insn->ops[i].mode == MIR_OP_MEM); + continue; /* We checked the operand during insn creation -- skip va_arg type */ + } + if (code == MIR_SWITCH) { + out_p = FALSE; + expected_mode = i == 0 ? MIR_OP_INT : MIR_OP_LABEL; + } else if (code != MIR_RET) { + expected_mode = MIR_insn_op_mode (ctx, insn, i, &out_p); + } else { + out_p = FALSE; + expected_mode = type2mode (curr_func->res_types[i]); + } + can_be_out_p = TRUE; + switch (insn->ops[i].mode) { + case MIR_OP_REG: + rd = find_rd_by_reg (ctx, insn->ops[i].u.reg, curr_func); + mir_assert (rd != NULL && insn->ops[i].u.reg == rd->reg); + mode = type2mode (rd->type); + break; + case MIR_OP_MEM: + expr_p = FALSE; + if (insn->ops[i].u.mem.base != 0) { + rd = find_rd_by_reg (ctx, insn->ops[i].u.mem.base, curr_func); + mir_assert (rd != NULL && insn->ops[i].u.mem.base == rd->reg); + if (type2mode (rd->type) != MIR_OP_INT) { + curr_func = NULL; + (*error_func) (MIR_reg_type_error, "base reg of non-integer type"); + } + } + if (insn->ops[i].u.mem.index != 0) { + rd = find_rd_by_reg (ctx, insn->ops[i].u.mem.index, curr_func); + mir_assert (rd != NULL && insn->ops[i].u.mem.index == rd->reg); + if (type2mode (rd->type) != MIR_OP_INT) { + curr_func = NULL; + (*error_func) (MIR_reg_type_error, "index reg of non-integer type"); + } + } + mode = type2mode (insn->ops[i].u.mem.type); + break; + case MIR_OP_HARD_REG: + case MIR_OP_HARD_REG_MEM: + expr_p = FALSE; + mode = expected_mode; + mir_assert (FALSE); /* Should not be here */ + break; + default: + can_be_out_p = FALSE; + mode = insn->ops[i].mode; + if (mode == MIR_OP_REF || mode == MIR_OP_STR) mode = MIR_OP_INT; /* just an address */ + break; + } + insn->ops[i].value_mode = mode; + if (expected_mode != MIR_OP_UNDEF + && (mode == MIR_OP_UINT ? MIR_OP_INT : mode) != expected_mode) { + curr_func = NULL; + (*error_func) (MIR_op_mode_error, "unexpected operand mode"); + } + if (out_p && !can_be_out_p) { + curr_func = NULL; + (*error_func) (MIR_out_op_error, "wrong operand for insn output"); + } + } + } + curr_func->expr_p = expr_p; + curr_func = NULL; +} + +void MIR_finish_module (MIR_context_t ctx) { + if (curr_module == NULL) (*error_func) (MIR_no_module_error, "finish of non-existing module"); + curr_module = NULL; +} + +static void setup_global (MIR_context_t ctx, const char *name, void *addr, MIR_item_t def) { + MIR_item_t item, tab_item; + MIR_module_t saved = curr_module; + + curr_module = &environment_module; + /* Use import for proto representation: */ + item = new_export_import_forward (ctx, name, MIR_import_item, "import", TRUE); + if ((tab_item = find_item (ctx, MIR_item_name (ctx, item), &environment_module)) != item + && tab_item != NULL) { + free (item); + } else { + HTAB_DO (MIR_item_t, module_item_tab, item, HTAB_INSERT, tab_item); + DLIST_APPEND (MIR_item_t, environment_module.items, item); + tab_item = item; + } + tab_item->addr = addr; + tab_item->ref_def = def; + curr_module = saved; +} + +static void undefined_interface (MIR_context_t ctx) { + (*error_func) (MIR_call_op_error, "undefined call interface"); +} + +static MIR_item_t load_bss_data_section (MIR_context_t ctx, MIR_item_t item, int first_only_p) { + const char *name; + MIR_item_t curr_item, last_item, expr_item; + size_t len, section_size = 0; + uint8_t *addr; + + if (item->addr == NULL) { + /* Calculate section size: */ + for (curr_item = item; curr_item != NULL && curr_item->addr == NULL; + curr_item = first_only_p ? NULL : DLIST_NEXT (MIR_item_t, curr_item)) + if (curr_item->item_type == MIR_bss_item + && (curr_item == item || curr_item->u.bss->name == NULL)) + section_size += curr_item->u.bss->len; + else if (curr_item->item_type == MIR_data_item + && (curr_item == item || curr_item->u.data->name == NULL)) + section_size += (curr_item->u.data->nel * _MIR_type_size (ctx, curr_item->u.data->el_type)); + else if (curr_item->item_type == MIR_ref_data_item + && (curr_item == item || curr_item->u.ref_data->name == NULL)) + section_size += _MIR_type_size (ctx, MIR_T_P); + else if (curr_item->item_type == MIR_expr_data_item + && (curr_item == item || curr_item->u.expr_data->name == NULL)) { + expr_item = curr_item->u.expr_data->expr_item; + if (expr_item->item_type != MIR_func_item || !expr_item->u.func->expr_p + || expr_item->u.func->nres != 1) + (*error_func) (MIR_binary_io_error, + "%s can not be an expr which should be a func w/o calls and memory ops", + MIR_item_name (ctx, expr_item)); + section_size += _MIR_type_size (ctx, expr_item->u.func->res_types[0]); + } else + break; + if ((item->addr = malloc (section_size)) == NULL) { + name = MIR_item_name (ctx, item); + (*error_func) (MIR_alloc_error, "Not enough memory to allocate data/bss %s", + name == NULL ? "" : name); + } + } + /* Set up section memory: */ + for (last_item = item, curr_item = item, addr = item->addr; + curr_item != NULL && (curr_item == item || curr_item->addr == NULL); + last_item = curr_item, curr_item = first_only_p ? NULL : DLIST_NEXT (MIR_item_t, curr_item)) + if (curr_item->item_type == MIR_bss_item + && (curr_item == item || curr_item->u.bss->name == NULL)) { + memset (addr, 0, curr_item->u.bss->len); + curr_item->addr = addr; + addr += curr_item->u.bss->len; + } else if (curr_item->item_type == MIR_data_item + && (curr_item == item || curr_item->u.data->name == NULL)) { + len = curr_item->u.data->nel * _MIR_type_size (ctx, curr_item->u.data->el_type); + memmove (addr, curr_item->u.data->u.els, len); + curr_item->addr = addr; + addr += len; + } else if (curr_item->item_type == MIR_ref_data_item + && (curr_item == item || curr_item->u.ref_data->name == NULL)) { + curr_item->u.ref_data->load_addr = addr; + curr_item->addr = addr; + addr += _MIR_type_size (ctx, MIR_T_P); + } else if (curr_item->item_type == MIR_expr_data_item + && (curr_item == item || curr_item->u.expr_data->name == NULL)) { + expr_item = curr_item->u.expr_data->expr_item; + len = _MIR_type_size (ctx, expr_item->u.func->res_types[0]); + curr_item->u.expr_data->load_addr = addr; + curr_item->addr = addr; + addr += len; + } else { + break; + } + return last_item; +} + +void MIR_load_module (MIR_context_t ctx, MIR_module_t m) { + for (MIR_item_t item = DLIST_HEAD (MIR_item_t, m->items); item != NULL; + item = DLIST_NEXT (MIR_item_t, item)) { + if (item->item_type == MIR_bss_item || item->item_type == MIR_data_item + || item->item_type == MIR_ref_data_item || item->item_type == MIR_expr_data_item) { + item = load_bss_data_section (ctx, item, FALSE); + } else if (item->item_type == MIR_func_item) { + if (item->addr == NULL) { + item->addr = _MIR_get_thunk (ctx); +#ifdef MIR_DEBUG + fprintf (stderr, "%016lx: %s\n", (size_t) item->addr, item->u.func->name); +#endif + } + _MIR_redirect_thunk (ctx, item->addr, undefined_interface); + } + if (item->export_p) { /* update global item table */ + mir_assert (item->item_type != MIR_export_item && item->item_type != MIR_import_item + && item->item_type != MIR_forward_item); + setup_global (ctx, MIR_item_name (ctx, item), item->addr, item); + } + } + VARR_PUSH (MIR_module_t, modules_to_link, m); +} + +void MIR_load_external (MIR_context_t ctx, const char *name, void *addr) { + setup_global (ctx, name, addr, NULL); +} + +static int simplify_func (MIR_context_t ctx, MIR_item_t func_item, int mem_float_p); +static void process_inlines (MIR_context_t ctx, MIR_item_t func_item); + +void MIR_link (MIR_context_t ctx, void (*set_interface) (MIR_context_t ctx, MIR_item_t item), + void *import_resolver (const char *)) { + MIR_item_t item, tab_item, expr_item; + MIR_type_t type; + MIR_val_t res; + MIR_module_t m; + void *addr; + union { + int8_t i8; + int16_t i16; + int32_t i32; + int64_t i64; + float f; + double d; + long double ld; + void *a; + } v; + + for (size_t i = 0; i < VARR_LENGTH (MIR_module_t, modules_to_link); i++) { + m = VARR_GET (MIR_module_t, modules_to_link, i); + for (item = DLIST_HEAD (MIR_item_t, m->items); item != NULL; + item = DLIST_NEXT (MIR_item_t, item)) + if (item->item_type == MIR_func_item) { + assert (item->data == NULL); + if (simplify_func (ctx, item, TRUE)) item->data = (void *) 1; + } else if (item->item_type == MIR_import_item) { + if ((tab_item = find_item (ctx, item->u.import, &environment_module)) == NULL) { + if (import_resolver == NULL || (addr = import_resolver (item->u.import)) == NULL) + (*error_func) (MIR_undeclared_op_ref_error, "import of undefined item %s", + item->u.import); + MIR_load_external (ctx, item->u.import, addr); + tab_item = find_item (ctx, item->u.import, &environment_module); + mir_assert (tab_item != NULL); + } + item->addr = tab_item->addr; + item->ref_def = tab_item; + } else if (item->item_type == MIR_export_item) { + if ((tab_item = find_item (ctx, item->u.export, m)) == NULL) + (*error_func) (MIR_undeclared_op_ref_error, "export of undefined item %s", + item->u.export); + item->addr = tab_item->addr; + item->ref_def = tab_item; + } else if (item->item_type == MIR_forward_item) { + if ((tab_item = find_item (ctx, item->u.forward, m)) == NULL) + (*error_func) (MIR_undeclared_op_ref_error, "forward of undefined item %s", + item->u.forward); + item->addr = tab_item->addr; + item->ref_def = tab_item; + } + } + for (size_t i = 0; i < VARR_LENGTH (MIR_module_t, modules_to_link); i++) { + m = VARR_GET (MIR_module_t, modules_to_link, i); + for (item = DLIST_HEAD (MIR_item_t, m->items); item != NULL; + item = DLIST_NEXT (MIR_item_t, item)) { + if (item->item_type == MIR_func_item && item->data != NULL) { + process_inlines (ctx, item); + item->data = NULL; + } else if (item->item_type == MIR_ref_data_item) { + assert (item->u.ref_data->ref_item->addr != NULL); + addr = (char *) item->u.ref_data->ref_item->addr + item->u.ref_data->disp; + memcpy (item->u.ref_data->load_addr, &addr, _MIR_type_size (ctx, MIR_T_P)); + continue; + } + if (item->item_type != MIR_expr_data_item) continue; + expr_item = item->u.expr_data->expr_item; + MIR_interp (ctx, expr_item, &res, 0); + type = expr_item->u.func->res_types[0]; + switch (type) { + case MIR_T_I8: + case MIR_T_U8: v.i8 = (int8_t) res.i; break; + case MIR_T_I16: + case MIR_T_U16: v.i16 = (int16_t) res.i; break; + case MIR_T_I32: + case MIR_T_U32: v.i32 = (int32_t) res.i; break; + case MIR_T_I64: + case MIR_T_U64: v.i64 = (int64_t) res.i; break; + case MIR_T_F: v.f = res.f; break; + case MIR_T_D: v.d = res.d; break; + case MIR_T_LD: v.ld = res.ld; break; + case MIR_T_P: v.a = res.a; break; + default: assert (FALSE); break; + } + memcpy (item->u.expr_data->load_addr, &v, + _MIR_type_size (ctx, expr_item->u.func->res_types[0])); + } + } + if (set_interface != NULL) + while (VARR_LENGTH (MIR_module_t, modules_to_link) != 0) { + m = VARR_POP (MIR_module_t, modules_to_link); + for (item = DLIST_HEAD (MIR_item_t, m->items); item != NULL; + item = DLIST_NEXT (MIR_item_t, item)) + if (item->item_type == MIR_func_item) { + finish_func_interpretation (item); /* in case if it was used for expr data */ + set_interface (ctx, item); + } + } +} + +static const char *insn_name (MIR_insn_code_t code) { + return code < 0 || code >= MIR_INSN_BOUND ? "" : insn_descs[code].name; +} + +const char *MIR_insn_name (MIR_context_t ctx, MIR_insn_code_t code) { + if (code < 0 || code >= MIR_INSN_BOUND) + (*error_func) (MIR_wrong_param_value_error, "MIR_insn_name: wrong insn code %d", (int) code); + return insn_descs[code].name; +} + +static size_t insn_code_nops (MIR_context_t ctx, MIR_insn_code_t code) { /* 0 for calls */ + if (code < 0 || code >= MIR_INSN_BOUND) + (*error_func) (MIR_wrong_param_value_error, "insn_code_nops: wrong insn code %d", (int) code); + return VARR_GET (size_t, insn_nops, code); +} + +size_t MIR_insn_nops (MIR_context_t ctx, MIR_insn_t insn) { return insn->nops; } + +MIR_op_mode_t _MIR_insn_code_op_mode (MIR_context_t ctx, MIR_insn_code_t code, size_t nop, + int *out_p) { + unsigned mode; + + if (nop >= insn_code_nops (ctx, code)) return MIR_OP_BOUND; + mode = insn_descs[code].op_modes[nop]; + *out_p = (mode & OUTPUT_FLAG) != 0; + return *out_p ? mode ^ OUTPUT_FLAG : mode; +} + +MIR_op_mode_t MIR_insn_op_mode (MIR_context_t ctx, MIR_insn_t insn, size_t nop, int *out_p) { + MIR_insn_code_t code = insn->code; + size_t nargs, nops = MIR_insn_nops (ctx, insn); + unsigned mode; + + if (nop >= nops) return MIR_OP_BOUND; + if (code == MIR_RET || code == MIR_SWITCH) { + *out_p = FALSE; + /* should be already checked in MIR_finish_func */ + return nop == 0 && code == MIR_SWITCH ? MIR_OP_INT : insn->ops[nop].mode; + } else if (MIR_call_code_p (code)) { + MIR_op_t proto_op = insn->ops[0]; + MIR_proto_t proto; + + mir_assert (proto_op.mode == MIR_OP_REF && proto_op.u.ref->item_type == MIR_proto_item); + proto = proto_op.u.ref->u.proto; + *out_p = 2 <= nop && nop < proto->nres + 2; + nargs = proto->nres + 2 + (proto->args == NULL ? 0 : VARR_LENGTH (MIR_var_t, proto->args)); + if (proto->vararg_p && nop >= nargs) return MIR_OP_UNDEF; /* unknown */ + mir_assert (nops >= nargs && (proto->vararg_p || nops == nargs)); + return (nop == 0 + ? insn->ops[nop].mode + : nop == 1 + ? MIR_OP_INT + : 2 <= nop && nop < proto->nres + 2 + ? type2mode (proto->res_types[nop - 2]) + : type2mode (VARR_GET (MIR_var_t, proto->args, nop - 2 - proto->nres).type)); + } + mode = insn_descs[code].op_modes[nop]; + *out_p = (mode & OUTPUT_FLAG) != 0; + return *out_p ? mode ^ OUTPUT_FLAG : mode; +} + +static MIR_insn_t create_insn (MIR_context_t ctx, size_t nops, MIR_insn_code_t code) { + MIR_insn_t insn; + + if (nops == 0) nops = 1; + insn = malloc (sizeof (struct MIR_insn) + sizeof (MIR_op_t) * (nops - 1)); + if (insn == NULL) (*error_func) (MIR_alloc_error, "Not enough memory for insn creation"); + insn->code = code; + insn->data = NULL; + return insn; +} + +static MIR_insn_t new_insn1 (MIR_context_t ctx, MIR_insn_code_t code) { + return create_insn (ctx, 1, code); +} + +MIR_insn_t MIR_new_insn_arr (MIR_context_t ctx, MIR_insn_code_t code, size_t nops, MIR_op_t *ops) { + MIR_insn_t insn; + MIR_proto_t proto; + size_t i = 0, expected_nops = insn_code_nops (ctx, code); + + if (!MIR_call_code_p (code) && code != MIR_RET && code != MIR_SWITCH && nops != expected_nops) { + (*error_func) (MIR_ops_num_error, "wrong number of operands for insn %s", + insn_descs[code].name); + } else if (code == MIR_SWITCH) { + if (nops < 2) (*error_func) (MIR_ops_num_error, "number of MIR_SWITCH operands is less 2"); + } else if (MIR_call_code_p (code)) { + if (nops < 2) (*error_func) (MIR_ops_num_error, "wrong number of call operands"); + if (ops[0].mode != MIR_OP_REF || ops[0].u.ref->item_type != MIR_proto_item) + (*error_func) (MIR_call_op_error, "the 1st call operand should be a prototype"); + proto = ops[0].u.ref->u.proto; + i = proto->nres; + if (proto->args != NULL) i += VARR_LENGTH (MIR_var_t, proto->args); + if (nops < i + 2 || (nops != i + 2 && !proto->vararg_p)) + (*error_func) (MIR_call_op_error, + "number of call operands or results does not correspond to prototype %s", + proto->name); + } else if (code == MIR_VA_ARG) { + if (ops[2].mode != MIR_OP_MEM) + (*error_func) (MIR_op_mode_error, + "3rd operand of va_arg should be any memory with given type"); + } + insn = create_insn (ctx, nops, code); + insn->nops = nops; + for (i = 0; i < nops; i++) insn->ops[i] = ops[i]; + return insn; +} + +static MIR_insn_t new_insn (MIR_context_t ctx, MIR_insn_code_t code, size_t nops, va_list argp) { + VARR_TRUNC (MIR_op_t, temp_insn_ops, 0); + for (size_t i = 0; i < nops; i++) { + MIR_op_t op = va_arg (argp, MIR_op_t); + + VARR_PUSH (MIR_op_t, temp_insn_ops, op); + } + va_end (argp); + return MIR_new_insn_arr (ctx, code, nops, VARR_ADDR (MIR_op_t, temp_insn_ops)); +} + +MIR_insn_t MIR_new_insn (MIR_context_t ctx, MIR_insn_code_t code, ...) { + va_list argp; + size_t nops = insn_code_nops (ctx, code); + + if (MIR_call_code_p (code) || code == MIR_RET || code == MIR_SWITCH) + (*error_func) (MIR_call_op_error, + "Use only MIR_new_insn_arr or MIR_new_{call,ret}_insn for creating a " + "call/ret/switch insn"); + va_start (argp, code); + return new_insn (ctx, code, nops, argp); +} + +MIR_insn_t MIR_new_call_insn (MIR_context_t ctx, size_t nops, ...) { + va_list argp; + + va_start (argp, nops); + return new_insn (ctx, MIR_CALL, nops, argp); +} + +MIR_insn_t MIR_new_ret_insn (MIR_context_t ctx, size_t nops, ...) { + va_list argp; + + va_start (argp, nops); + return new_insn (ctx, MIR_RET, nops, argp); +} + +MIR_insn_t MIR_copy_insn (MIR_context_t ctx, MIR_insn_t insn) { + size_t size + = sizeof (struct MIR_insn) + sizeof (MIR_op_t) * (insn->nops == 0 ? 0 : insn->nops - 1); + MIR_insn_t new_insn = malloc (size); + + if (new_insn == NULL) + (*error_func) (MIR_alloc_error, "Not enough memory to copy insn %s", insn_name (insn->code)); + memcpy (new_insn, insn, size); + return new_insn; +} + +static MIR_insn_t create_label (MIR_context_t ctx, int64_t label_num) { + MIR_insn_t insn = new_insn1 (ctx, MIR_LABEL); + + insn->ops[0] = MIR_new_int_op (ctx, label_num); + insn->nops = 0; + return insn; +} + +MIR_insn_t MIR_new_label (MIR_context_t ctx) { return create_label (ctx, ++curr_label_num); } + +MIR_reg_t _MIR_new_temp_reg (MIR_context_t ctx, MIR_type_t type, MIR_func_t func) { + string_t string; + + if (type != MIR_T_I64 && type != MIR_T_F && type != MIR_T_D && type != MIR_T_LD) + (*error_func) (MIR_reg_type_error, "wrong type %s for temporary register", type_str (type)); + for (;;) { + func->last_temp_num++; + if (func->last_temp_num == 0) (*error_func) (MIR_unique_reg_error, "out of unique regs"); + sprintf (temp_buff, "%s%d", TEMP_REG_NAME_PREFIX, func->last_temp_num); + string + = string_store (ctx, &strings, &string_tab, (MIR_str_t){strlen (temp_buff) + 1, temp_buff}); + if (find_rd_by_name_num (ctx, string.num, func) == NULL) + return MIR_new_func_reg (ctx, func, type, string.str.s); + } +} + +static reg_desc_t *get_func_rd_by_name (MIR_context_t ctx, const char *reg_name, MIR_func_t func) { + string_t string + = string_store (ctx, &strings, &string_tab, (MIR_str_t){strlen (reg_name) + 1, reg_name}); + reg_desc_t *rd; + + rd = find_rd_by_name_num (ctx, string.num, func); + if (rd == NULL) (*error_func) (MIR_undeclared_func_reg_error, "undeclared func reg %s", reg_name); + return rd; +} + +static reg_desc_t *get_func_rd_by_reg (MIR_context_t ctx, MIR_reg_t reg, MIR_func_t func) { + reg_desc_t *rd; + + rd = find_rd_by_reg (ctx, reg, func); + return rd; +} + +MIR_reg_t MIR_reg (MIR_context_t ctx, const char *reg_name, MIR_func_t func) { + return get_func_rd_by_name (ctx, reg_name, func)->reg; +} + +MIR_type_t MIR_reg_type (MIR_context_t ctx, MIR_reg_t reg, MIR_func_t func) { + return get_func_rd_by_reg (ctx, reg, func)->type; +} + +const char *MIR_reg_name (MIR_context_t ctx, MIR_reg_t reg, MIR_func_t func) { + return VARR_ADDR (string_t, strings)[get_func_rd_by_reg (ctx, reg, func)->name_num].str.s; +} + +/* Functions to create operands. */ + +static void init_op (MIR_op_t *op, MIR_op_mode_t mode) { + op->mode = mode; + op->data = NULL; +} + +MIR_op_t MIR_new_reg_op (MIR_context_t ctx, MIR_reg_t reg) { + MIR_op_t op; + + init_op (&op, MIR_OP_REG); + op.u.reg = reg; + return op; +} + +MIR_op_t _MIR_new_hard_reg_op (MIR_context_t ctx, MIR_reg_t hard_reg) { /* used only internally */ + MIR_op_t op; + + init_op (&op, MIR_OP_HARD_REG); + op.u.hard_reg = hard_reg; + return op; +} + +MIR_op_t MIR_new_int_op (MIR_context_t ctx, int64_t i) { + MIR_op_t op; + + init_op (&op, MIR_OP_INT); + op.u.i = i; + return op; +} + +MIR_op_t MIR_new_uint_op (MIR_context_t ctx, uint64_t u) { + MIR_op_t op; + + init_op (&op, MIR_OP_UINT); + op.u.u = u; + return op; +} + +MIR_op_t MIR_new_float_op (MIR_context_t ctx, float f) { + MIR_op_t op; + + mir_assert (sizeof (float) == 4); /* IEEE single */ + init_op (&op, MIR_OP_FLOAT); + op.u.f = f; + return op; +} + +MIR_op_t MIR_new_double_op (MIR_context_t ctx, double d) { + MIR_op_t op; + + mir_assert (sizeof (double) == 8); /* IEEE double */ + init_op (&op, MIR_OP_DOUBLE); + op.u.d = d; + return op; +} + +MIR_op_t MIR_new_ldouble_op (MIR_context_t ctx, long double ld) { + MIR_op_t op; + + mir_assert (sizeof (long double) == 16); /* machine-defined 80- or 128-bit FP */ + init_op (&op, MIR_OP_LDOUBLE); + op.u.ld = ld; + return op; +} + +MIR_op_t MIR_new_ref_op (MIR_context_t ctx, MIR_item_t item) { + MIR_op_t op; + + init_op (&op, MIR_OP_REF); + op.u.ref = item; + return op; +} + +MIR_op_t MIR_new_str_op (MIR_context_t ctx, MIR_str_t str) { + MIR_op_t op; + + init_op (&op, MIR_OP_STR); + op.u.str = string_store (ctx, &strings, &string_tab, str).str; + return op; +} + +MIR_op_t MIR_new_mem_op (MIR_context_t ctx, MIR_type_t type, MIR_disp_t disp, MIR_reg_t base, + MIR_reg_t index, MIR_scale_t scale) { + MIR_op_t op; + + init_op (&op, MIR_OP_MEM); + op.u.mem.type = type; + op.u.mem.disp = disp; + op.u.mem.base = base; + op.u.mem.index = index; + op.u.mem.scale = scale; + return op; +} + +MIR_op_t _MIR_new_hard_reg_mem_op (MIR_context_t ctx, MIR_type_t type, MIR_disp_t disp, + MIR_reg_t base, MIR_reg_t index, MIR_scale_t scale) { + MIR_op_t op; + + init_op (&op, MIR_OP_HARD_REG_MEM); + op.u.hard_reg_mem.type = type; + op.u.hard_reg_mem.disp = disp; + op.u.hard_reg_mem.base = base; + op.u.hard_reg_mem.index = index; + op.u.hard_reg_mem.scale = scale; + return op; +} + +MIR_op_t MIR_new_label_op (MIR_context_t ctx, MIR_label_t label) { + MIR_op_t op; + + init_op (&op, MIR_OP_LABEL); + op.u.label = label; + return op; +} + +int MIR_op_eq_p (MIR_context_t ctx, MIR_op_t op1, MIR_op_t op2) { + if (op1.mode != op2.mode) return FALSE; + switch (op1.mode) { + case MIR_OP_REG: return op1.u.reg == op2.u.reg; + case MIR_OP_HARD_REG: return op1.u.hard_reg == op2.u.hard_reg; + case MIR_OP_INT: return op1.u.i == op2.u.i; + case MIR_OP_UINT: return op1.u.u == op2.u.u; + case MIR_OP_FLOAT: return op1.u.f == op2.u.f; + case MIR_OP_DOUBLE: return op1.u.d == op2.u.d; + case MIR_OP_LDOUBLE: return op1.u.ld == op2.u.ld; + case MIR_OP_REF: + return strcmp (MIR_item_name (ctx, op1.u.ref), MIR_item_name (ctx, op2.u.ref)) == 0; + case MIR_OP_STR: + return op1.u.str.len == op2.u.str.len && memcmp (op1.u.str.s, op2.u.str.s, op1.u.str.len) == 0; + case MIR_OP_MEM: + return (op1.u.mem.type == op2.u.mem.type && op1.u.mem.disp == op2.u.mem.disp + && op1.u.mem.base == op2.u.mem.base && op1.u.mem.index == op2.u.mem.index + && (op1.u.mem.index == 0 || op1.u.mem.scale == op2.u.mem.scale)); + case MIR_OP_HARD_REG_MEM: + return (op1.u.hard_reg_mem.type == op2.u.hard_reg_mem.type + && op1.u.hard_reg_mem.disp == op2.u.hard_reg_mem.disp + && op1.u.hard_reg_mem.base == op2.u.hard_reg_mem.base + && op1.u.hard_reg_mem.index == op2.u.hard_reg_mem.index + && (op1.u.hard_reg_mem.index == MIR_NON_HARD_REG + || op1.u.hard_reg_mem.scale == op2.u.hard_reg_mem.scale)); + case MIR_OP_LABEL: return op1.u.label == op2.u.label; + default: mir_assert (FALSE); /* we should not have other operands here */ + } + return FALSE; +} + +htab_hash_t MIR_op_hash_step (MIR_context_t ctx, htab_hash_t h, MIR_op_t op) { + h = mir_hash_step (h, (uint64_t) op.mode); + switch (op.mode) { + case MIR_OP_REG: return mir_hash_step (h, (uint64_t) op.u.reg); + case MIR_OP_HARD_REG: return mir_hash_step (h, (uint64_t) op.u.hard_reg); + case MIR_OP_INT: return mir_hash_step (h, (uint64_t) op.u.i); + case MIR_OP_UINT: return mir_hash_step (h, (uint64_t) op.u.u); + case MIR_OP_FLOAT: { + union { + double d; + uint64_t u; + } u; + + u.d = op.u.f; + return mir_hash_step (h, u.u); + } + case MIR_OP_DOUBLE: return mir_hash_step (h, op.u.u); + case MIR_OP_LDOUBLE: { + union { + long double ld; + uint64_t u[2]; + } u; + + u.ld = op.u.ld; + return mir_hash_step (mir_hash_step (h, u.u[0]), u.u[1]); + } + case MIR_OP_REF: return mir_hash_step (h, (uint64_t) MIR_item_name (ctx, op.u.ref)); + case MIR_OP_STR: return mir_hash_step (h, (uint64_t) op.u.str.s); + case MIR_OP_MEM: + h = mir_hash_step (h, (uint64_t) op.u.mem.type); + h = mir_hash_step (h, (uint64_t) op.u.mem.disp); + h = mir_hash_step (h, (uint64_t) op.u.mem.base); + h = mir_hash_step (h, (uint64_t) op.u.mem.index); + if (op.u.mem.index != 0) h = mir_hash_step (h, (uint64_t) op.u.mem.scale); + break; + case MIR_OP_HARD_REG_MEM: + h = mir_hash_step (h, (uint64_t) op.u.hard_reg_mem.type); + h = mir_hash_step (h, (uint64_t) op.u.hard_reg_mem.disp); + h = mir_hash_step (h, (uint64_t) op.u.hard_reg_mem.base); + h = mir_hash_step (h, (uint64_t) op.u.hard_reg_mem.index); + if (op.u.hard_reg_mem.index != MIR_NON_HARD_REG) + h = mir_hash_step (h, (uint64_t) op.u.hard_reg_mem.scale); + break; + case MIR_OP_LABEL: return mir_hash_step (h, (uint64_t) op.u.label); + default: mir_assert (FALSE); /* we should not have other operands here */ + } + return h; +} + +void MIR_append_insn (MIR_context_t ctx, MIR_item_t func_item, MIR_insn_t insn) { + if (func_item->item_type != MIR_func_item) + (*error_func) (MIR_wrong_param_value_error, "MIR_append_insn: wrong func item"); + DLIST_APPEND (MIR_insn_t, func_item->u.func->insns, insn); +} + +void MIR_prepend_insn (MIR_context_t ctx, MIR_item_t func_item, MIR_insn_t insn) { + if (func_item->item_type != MIR_func_item) + (*error_func) (MIR_wrong_param_value_error, "MIR_prepend_insn: wrong func item"); + DLIST_PREPEND (MIR_insn_t, func_item->u.func->insns, insn); +} + +void MIR_insert_insn_after (MIR_context_t ctx, MIR_item_t func_item, MIR_insn_t after, + MIR_insn_t insn) { + if (func_item->item_type != MIR_func_item) + (*error_func) (MIR_wrong_param_value_error, "MIR_insert_insn_after: wrong func item"); + DLIST_INSERT_AFTER (MIR_insn_t, func_item->u.func->insns, after, insn); +} + +void MIR_insert_insn_before (MIR_context_t ctx, MIR_item_t func_item, MIR_insn_t before, + MIR_insn_t insn) { + if (func_item->item_type != MIR_func_item) + (*error_func) (MIR_wrong_param_value_error, "MIR_insert_insn_before: wrong func item"); + DLIST_INSERT_BEFORE (MIR_insn_t, func_item->u.func->insns, before, insn); +} + +void MIR_remove_insn (MIR_context_t ctx, MIR_item_t func_item, MIR_insn_t insn) { + if (func_item->item_type != MIR_func_item) + (*error_func) (MIR_wrong_param_value_error, "MIR_remove_insn: wrong func item"); + DLIST_REMOVE (MIR_insn_t, func_item->u.func->insns, insn); + free (insn); +} + +static void store_labels_for_duplication (MIR_context_t ctx, MIR_insn_t insn, MIR_insn_t new_insn) { + if (MIR_branch_code_p (insn->code) || insn->code == MIR_SWITCH) { + VARR_PUSH (MIR_insn_t, temp_insns2, new_insn); + } else if (insn->code == MIR_LABEL) { + mir_assert (insn->data == NULL); + insn->data = new_insn; + VARR_PUSH (MIR_insn_t, temp_insns, insn); + } +} + +static void redirect_duplicated_labels (MIR_context_t ctx) { + MIR_insn_t insn; + + while (VARR_LENGTH (MIR_insn_t, temp_insns2) != 0) { /* redirect new label operands */ + size_t start_label_nop = 0, bound_label_nop = 1, n; + + insn = VARR_POP (MIR_insn_t, temp_insns2); + if (insn->code == MIR_SWITCH) { + start_label_nop = 1; + bound_label_nop = start_label_nop + insn->nops - 1; + } + for (n = start_label_nop; n < bound_label_nop; n++) + insn->ops[n].u.label = insn->ops[n].u.label->data; + } + while (VARR_LENGTH (MIR_insn_t, temp_insns) != 0) { /* reset data */ + insn = VARR_POP (MIR_insn_t, temp_insns); + insn->data = NULL; + } +} + +void _MIR_duplicate_func_insns (MIR_context_t ctx, MIR_item_t func_item) { + MIR_func_t func; + MIR_insn_t insn, new_insn; + + mir_assert (func_item->item_type == MIR_func_item); + func = func_item->u.func; + mir_assert (DLIST_HEAD (MIR_insn_t, func->original_insns) == NULL); + func->original_insns = func->insns; + DLIST_INIT (MIR_insn_t, func->insns); + VARR_TRUNC (MIR_insn_t, temp_insns, 0); + VARR_TRUNC (MIR_insn_t, temp_insns2, 0); + for (insn = DLIST_HEAD (MIR_insn_t, func->original_insns); insn != NULL; + insn = DLIST_NEXT (MIR_insn_t, insn)) { /* copy insns and collect label info */ + new_insn = MIR_copy_insn (ctx, insn); + DLIST_APPEND (MIR_insn_t, func->insns, new_insn); + store_labels_for_duplication (ctx, insn, new_insn); + } + redirect_duplicated_labels (ctx); +} + +void _MIR_restore_func_insns (MIR_context_t ctx, MIR_item_t func_item) { + MIR_func_t func; + MIR_insn_t insn; + + mir_assert (func_item->item_type == MIR_func_item); + func = func_item->u.func; + while ((insn = DLIST_HEAD (MIR_insn_t, func->insns)) != NULL) + MIR_remove_insn (ctx, func_item, insn); + func->insns = func->original_insns; + DLIST_INIT (MIR_insn_t, func->original_insns); +} + +static void output_type (FILE *f, MIR_type_t tp) { fprintf (f, "%s", MIR_type_str (NULL, tp)); } + +static void output_disp (FILE *f, MIR_disp_t disp) { fprintf (f, "%" PRId64, (int64_t) disp); } + +static void output_scale (FILE *f, unsigned scale) { fprintf (f, "%u", scale); } + +static void output_reg (MIR_context_t ctx, FILE *f, MIR_func_t func, MIR_reg_t reg) { + fprintf (f, "%s", MIR_reg_name (ctx, reg, func)); +} + +static void output_hard_reg (FILE *f, MIR_reg_t reg) { fprintf (f, "hr%u", reg); } + +static void output_label (MIR_context_t ctx, FILE *f, MIR_func_t func, MIR_label_t label); + +static void out_str (FILE *f, MIR_str_t str) { + fprintf (f, "\""); + for (size_t i = 0; i < str.len; i++) + if (str.s[i] == '\\') + fprintf (f, "\\\\"); + else if (str.s[i] == '"') + fprintf (f, "\\\""); + else if (isprint (str.s[i])) + fprintf (f, "%c", str.s[i]); + else if (str.s[i] == '\n') + fprintf (f, "\\n"); + else if (str.s[i] == '\t') + fprintf (f, "\\t"); + else if (str.s[i] == '\v') + fprintf (f, "\\v"); + else if (str.s[i] == '\a') + fprintf (f, "\\a"); + else if (str.s[i] == '\b') + fprintf (f, "\\b"); + else if (str.s[i] == '\f') + fprintf (f, "\\f"); + else + fprintf (f, "\\%03o", str.s[i]); + fprintf (f, "\""); +} + +void MIR_output_op (MIR_context_t ctx, FILE *f, MIR_op_t op, MIR_func_t func) { + switch (op.mode) { + case MIR_OP_REG: output_reg (ctx, f, func, op.u.reg); break; + case MIR_OP_HARD_REG: output_hard_reg (f, op.u.hard_reg); break; + case MIR_OP_INT: fprintf (f, "%" PRId64, op.u.i); break; + case MIR_OP_UINT: fprintf (f, "%" PRIu64, op.u.u); break; + case MIR_OP_FLOAT: fprintf (f, "%.*ef", FLT_DECIMAL_DIG, op.u.f); break; + case MIR_OP_DOUBLE: fprintf (f, "%.*e", DBL_DECIMAL_DIG, op.u.d); break; + case MIR_OP_LDOUBLE: fprintf (f, "%.*Le", LDBL_DECIMAL_DIG, op.u.ld); break; + case MIR_OP_MEM: + case MIR_OP_HARD_REG_MEM: { + MIR_reg_t no_reg = op.mode == MIR_OP_MEM ? 0 : MIR_NON_HARD_REG; + + output_type (f, op.u.mem.type); + fprintf (f, ":"); + if (op.u.mem.disp != 0 || (op.u.mem.base == no_reg && op.u.mem.index == no_reg)) + output_disp (f, op.u.mem.disp); + if (op.u.mem.base != no_reg || op.u.mem.index != no_reg) { + fprintf (f, "("); + if (op.u.mem.base != no_reg) { + if (op.mode == MIR_OP_MEM) + output_reg (ctx, f, func, op.u.mem.base); + else + output_hard_reg (f, op.u.hard_reg_mem.base); + } + if (op.u.mem.index != no_reg) { + fprintf (f, ", "); + if (op.mode == MIR_OP_MEM) + output_reg (ctx, f, func, op.u.mem.index); + else + output_hard_reg (f, op.u.hard_reg_mem.index); + if (op.u.mem.scale != 1) { + fprintf (f, ", "); + output_scale (f, op.u.mem.scale); + } + } + fprintf (f, ")"); + } + break; + } + case MIR_OP_REF: fprintf (f, "%s", MIR_item_name (ctx, op.u.ref)); break; + case MIR_OP_STR: out_str (f, op.u.str); break; + case MIR_OP_LABEL: output_label (ctx, f, func, op.u.label); break; + default: mir_assert (FALSE); + } +} + +static void output_label (MIR_context_t ctx, FILE *f, MIR_func_t func, MIR_label_t label) { + fprintf (f, "L"); + MIR_output_op (ctx, f, label->ops[0], func); +} + +void MIR_output_insn (MIR_context_t ctx, FILE *f, MIR_insn_t insn, MIR_func_t func, int newline_p) { + size_t i, nops; + + if (insn->code == MIR_LABEL) { + output_label (ctx, f, func, insn); + if (newline_p) fprintf (f, ":\n"); + return; + } + fprintf (f, "\t%s", MIR_insn_name (ctx, insn->code)); + nops = MIR_insn_nops (ctx, insn); + for (i = 0; i < nops; i++) { + fprintf (f, i == 0 ? "\t" : ", "); + MIR_output_op (ctx, f, insn->ops[i], func); + } + if (newline_p) fprintf (f, "\n"); +} + +static void output_func_proto (FILE *f, size_t nres, MIR_type_t *types, size_t nargs, + VARR (MIR_var_t) * args, int vararg_p) { + size_t i; + MIR_var_t var; + + for (i = 0; i < nres; i++) { + if (i != 0) fprintf (f, ", "); + fprintf (f, "%s", MIR_type_str (NULL, types[i])); + } + for (i = 0; i < nargs; i++) { + var = VARR_GET (MIR_var_t, args, i); + if (i != 0 || nres != 0) fprintf (f, ", "); + mir_assert (var.name != NULL); + fprintf (f, "%s:%s", MIR_type_str (NULL, var.type), var.name); + } + if (vararg_p) fprintf (f, nargs == 0 && nres == 0 ? "..." : ", ..."); + fprintf (f, "\n"); +} + +void MIR_output_item (MIR_context_t ctx, FILE *f, MIR_item_t item) { + MIR_insn_t insn; + MIR_func_t func; + MIR_proto_t proto; + MIR_var_t var; + MIR_data_t data; + MIR_ref_data_t ref_data; + MIR_expr_data_t expr_data; + size_t i, nlocals; + + if (item->item_type == MIR_export_item) { + fprintf (f, "\texport\t%s\n", item->u.export); + return; + } + if (item->item_type == MIR_import_item) { + fprintf (f, "\timport\t%s\n", item->u.import); + return; + } + if (item->item_type == MIR_forward_item) { + fprintf (f, "\tforward\t%s\n", item->u.forward); + return; + } + if (item->item_type == MIR_bss_item) { + if (item->u.bss->name != NULL) fprintf (f, "%s:", item->u.bss->name); + fprintf (f, "\tbss\t%" PRIu64 "\n", item->u.bss->len); + return; + } + if (item->item_type == MIR_ref_data_item) { + ref_data = item->u.ref_data; + if (ref_data->name != NULL) fprintf (f, "%s:", ref_data->name); + fprintf (f, "\tref\t%s, %" PRId64 "\n", MIR_item_name (ctx, ref_data->ref_item), + (int64_t) ref_data->disp); + return; + } + if (item->item_type == MIR_expr_data_item) { + expr_data = item->u.expr_data; + if (expr_data->name != NULL) fprintf (f, "%s:", expr_data->name); + fprintf (f, "\texpr\t%s", MIR_item_name (ctx, expr_data->expr_item)); + } + if (item->item_type == MIR_data_item) { + data = item->u.data; + if (data->name != NULL) fprintf (f, "%s:", data->name); + fprintf (f, "\t%s\t", MIR_type_str (NULL, data->el_type)); + for (size_t i = 0; i < data->nel; i++) { + switch (data->el_type) { + case MIR_T_I8: fprintf (f, "%" PRId8, ((int8_t *) data->u.els)[i]); break; + case MIR_T_U8: fprintf (f, "%" PRIu8, ((uint8_t *) data->u.els)[i]); break; + case MIR_T_I16: fprintf (f, "%" PRId16, ((int16_t *) data->u.els)[i]); break; + case MIR_T_U16: fprintf (f, "%" PRIu16, ((uint16_t *) data->u.els)[i]); break; + case MIR_T_I32: fprintf (f, "%" PRId32, ((int32_t *) data->u.els)[i]); break; + case MIR_T_U32: fprintf (f, "%" PRIu32, ((uint32_t *) data->u.els)[i]); break; + case MIR_T_I64: fprintf (f, "%" PRId64, ((int64_t *) data->u.els)[i]); break; + case MIR_T_U64: fprintf (f, "%" PRIu64, ((uint64_t *) data->u.els)[i]); break; + case MIR_T_F: fprintf (f, "%.*ef", FLT_DECIMAL_DIG, ((float *) data->u.els)[i]); break; + case MIR_T_D: fprintf (f, "%.*e", DBL_DECIMAL_DIG, ((double *) data->u.els)[i]); break; + case MIR_T_LD: + fprintf (f, "%.*Le", LDBL_DECIMAL_DIG, ((long double *) data->u.els)[i]); + break; + /* only ptr as ref ??? */ + case MIR_T_P: fprintf (f, "0x%" PRIxPTR, ((uintptr_t *) data->u.els)[i]); break; + default: mir_assert (FALSE); + } + if (i + 1 < data->nel) fprintf (f, ", "); + } + if (data->el_type == MIR_T_U8 && data->nel != 0 && data->u.els[data->nel - 1] == '\0') { + fprintf (f, " # "); /* print possible string as a comment */ + out_str (f, (MIR_str_t){data->nel, (char *) data->u.els}); + } + fprintf (f, "\n"); + return; + } + if (item->item_type == MIR_proto_item) { + proto = item->u.proto; + fprintf (f, "%s:\tproto\t", proto->name); + output_func_proto (f, proto->nres, proto->res_types, VARR_LENGTH (MIR_var_t, proto->args), + proto->args, proto->vararg_p); + return; + } + func = item->u.func; + fprintf (f, "%s:\tfunc\t", func->name); + output_func_proto (f, func->nres, func->res_types, func->nargs, func->vars, func->vararg_p); + nlocals = VARR_LENGTH (MIR_var_t, func->vars) - func->nargs; + for (i = 0; i < nlocals; i++) { + var = VARR_GET (MIR_var_t, func->vars, i + func->nargs); + if (i % 8 == 0) { + if (i != 0) fprintf (f, "\n"); + fprintf (f, "\tlocal\t"); + } + fprintf (f, i % 8 == 0 ? "%s:%s" : ", %s:%s", MIR_type_str (NULL, var.type), var.name); + } + fprintf (f, "\n# %u arg%s, %u local%s\n", func->nargs, func->nargs == 1 ? "" : "s", + (unsigned) nlocals, nlocals == 1 ? "" : "s"); + for (insn = DLIST_HEAD (MIR_insn_t, func->insns); insn != NULL; + insn = DLIST_NEXT (MIR_insn_t, insn)) + MIR_output_insn (ctx, f, insn, func, TRUE); + fprintf (f, "\tendfunc\n"); +} + +void MIR_output_module (MIR_context_t ctx, FILE *f, MIR_module_t module) { + fprintf (f, "%s:\tmodule\n", module->name); + for (MIR_item_t item = DLIST_HEAD (MIR_item_t, module->items); item != NULL; + item = DLIST_NEXT (MIR_item_t, item)) + MIR_output_item (ctx, f, item); + fprintf (f, "\tendmodule\n"); +} + +void MIR_output (MIR_context_t ctx, FILE *f) { + for (MIR_module_t module = DLIST_HEAD (MIR_module_t, all_modules); module != NULL; + module = DLIST_NEXT (MIR_module_t, module)) + MIR_output_module (ctx, f, module); +} + +static MIR_insn_t insert_op_insn (MIR_context_t ctx, int out_p, MIR_item_t func_item, + MIR_insn_t anchor, MIR_insn_t insn) { + if (!out_p) { + MIR_insert_insn_before (ctx, func_item, anchor, insn); + return anchor; + } + MIR_insert_insn_after (ctx, func_item, anchor, insn); + return insn; +} + +typedef struct { + MIR_insn_code_t code; + MIR_type_t type; + MIR_op_t op1, op2; + MIR_reg_t reg; + MIR_context_t ctx; +} val_t; + +DEF_HTAB (val_t); + +struct simplify_ctx { + HTAB (val_t) * val_tab; +}; + +#define val_tab ctx->simplify_ctx->val_tab + +static htab_hash_t val_hash (val_t v) { + htab_hash_t h; + + h = mir_hash_step (mir_hash_init (0), (uint64_t) v.code); + h = mir_hash_step (h, (uint64_t) v.type); + h = MIR_op_hash_step (v.ctx, h, v.op1); + if (v.code != MIR_INSN_BOUND) h = MIR_op_hash_step (v.ctx, h, v.op2); + return mir_hash_finish (h); +} + +static int val_eq (val_t v1, val_t v2) { + assert (v1.ctx == v2.ctx); + if (v1.code != v2.code || v1.type != v2.type || !MIR_op_eq_p (v1.ctx, v1.op1, v2.op1)) + return FALSE; + return v1.code == MIR_INSN_BOUND || MIR_op_eq_p (v1.ctx, v1.op2, v2.op2); +} + +static void vn_init (MIR_context_t ctx) { + if ((ctx->simplify_ctx = malloc (sizeof (struct simplify_ctx))) == NULL) + (*error_func) (MIR_alloc_error, "Not enough memory for ctx"); + HTAB_CREATE (val_t, val_tab, 512, val_hash, val_eq); +} + +static void vn_finish (MIR_context_t ctx) { + HTAB_DESTROY (val_t, val_tab); + free (ctx->simplify_ctx); + ctx->simplify_ctx = NULL; +} + +static void vn_empty (MIR_context_t ctx) { HTAB_CLEAR (val_t, val_tab); } + +static MIR_reg_t vn_add_val (MIR_context_t ctx, MIR_func_t func, MIR_type_t type, + MIR_insn_code_t code, MIR_op_t op1, MIR_op_t op2) { + val_t val, tab_val; + + val.type = type; + val.code = code; + val.op1 = op1; + val.op2 = op2; + val.ctx = ctx; + if (HTAB_DO (val_t, val_tab, val, HTAB_FIND, tab_val)) return tab_val.reg; + val.reg = _MIR_new_temp_reg (ctx, type, func); + HTAB_DO (val_t, val_tab, val, HTAB_INSERT, tab_val); + return val.reg; +} + +const char *_MIR_get_temp_item_name (MIR_context_t ctx, MIR_module_t module) { + module->last_temp_item_num++; + snprintf (temp_buff, sizeof (temp_buff), "%s%u", TEMP_ITEM_NAME_PREFIX, + (unsigned) module->last_temp_item_num); + return temp_buff; +} + +void MIR_simplify_op (MIR_context_t ctx, MIR_item_t func_item, MIR_insn_t insn, int nop, int out_p, + MIR_insn_code_t code, int keep_ref_p, int mem_float_p) { + MIR_op_t new_op, mem_op, *op = &insn->ops[nop]; + MIR_insn_t new_insn; + MIR_func_t func = func_item->u.func; + MIR_type_t type; + MIR_op_mode_t value_mode = op->value_mode; + int move_p = code == MIR_MOV || code == MIR_FMOV || code == MIR_DMOV || code == MIR_LDMOV; + + if (MIR_call_code_p (code)) { + if (nop == 0) return; /* do nothing: it is a prototype */ + if (nop == 1 && op->mode == MIR_OP_REF + && (op->u.ref->item_type == MIR_import_item || op->u.ref->item_type == MIR_func_item)) + return; /* do nothing: it is an immediate oeprand */ + } + if (code == MIR_VA_ARG && nop == 2) return; /* do nothing: this operand is used as a type */ + switch (op->mode) { + case MIR_OP_REF: + if (keep_ref_p) break; + case MIR_OP_INT: + case MIR_OP_UINT: + case MIR_OP_FLOAT: + case MIR_OP_DOUBLE: + case MIR_OP_LDOUBLE: + case MIR_OP_STR: + mir_assert (!out_p); + if (op->mode == MIR_OP_REF) { + for (MIR_item_t item = op->u.ref; item != NULL; item = item->ref_def) + if (item->item_type != MIR_export_item && item->item_type != MIR_forward_item) { + op->u.ref = item; + break; + } + } else if (op->mode == MIR_OP_STR + || (mem_float_p + && (op->mode == MIR_OP_FLOAT || op->mode == MIR_OP_DOUBLE + || op->mode == MIR_OP_LDOUBLE))) { + const char *name; + MIR_item_t item; + MIR_module_t m = curr_module; + + curr_module = func_item->module; + name = _MIR_get_temp_item_name (ctx, curr_module); + if (op->mode == MIR_OP_STR) { + item = MIR_new_string_data (ctx, name, op->u.str); + *op = MIR_new_ref_op (ctx, item); + } else { + if (op->mode == MIR_OP_FLOAT) + item = MIR_new_data (ctx, name, MIR_T_F, 1, (uint8_t *) &op->u.f); + else if (op->mode == MIR_OP_DOUBLE) + item = MIR_new_data (ctx, name, MIR_T_D, 1, (uint8_t *) &op->u.d); + else + item = MIR_new_data (ctx, name, MIR_T_LD, 1, (uint8_t *) &op->u.ld); + type = op->mode == MIR_OP_FLOAT ? MIR_T_F : op->mode == MIR_OP_DOUBLE ? MIR_T_D : MIR_T_LD; + *op = MIR_new_ref_op (ctx, item); + new_op = MIR_new_reg_op (ctx, vn_add_val (ctx, func, MIR_T_I64, MIR_INSN_BOUND, *op, *op)); + MIR_insert_insn_before (ctx, func_item, insn, MIR_new_insn (ctx, MIR_MOV, new_op, *op)); + *op = MIR_new_mem_op (ctx, type, 0, new_op.u.reg, 0, 1); + } + if (func_item->addr != NULL) /* The function was already loaded: we should load new data */ + load_bss_data_section (ctx, item, TRUE); + curr_module = m; + } + if (move_p) return; + type = (op->mode == MIR_OP_FLOAT ? MIR_T_F + : op->mode == MIR_OP_DOUBLE + ? MIR_T_D + : op->mode == MIR_OP_LDOUBLE + ? MIR_T_LD + : op->mode == MIR_OP_MEM ? op->u.mem.type : MIR_T_I64); + new_op = MIR_new_reg_op (ctx, vn_add_val (ctx, func, type, MIR_INSN_BOUND, *op, *op)); + MIR_insert_insn_before (ctx, func_item, insn, + MIR_new_insn (ctx, + type == MIR_T_F + ? MIR_FMOV + : type == MIR_T_D + ? MIR_DMOV + : type == MIR_T_LD ? MIR_LDMOV : MIR_MOV, + new_op, *op)); + *op = new_op; + break; + case MIR_OP_REG: + case MIR_OP_HARD_REG: + case MIR_OP_LABEL: break; /* Do nothing */ + case MIR_OP_MEM: { + MIR_reg_t addr_reg = 0; + + mem_op = *op; + type = mem_op.u.mem.type; + if (op->u.mem.base != 0 && op->u.mem.disp == 0 + && (op->u.mem.index == 0 || op->u.mem.scale == 0)) { + addr_reg = op->u.mem.base; + } else if (op->u.mem.base == 0 && op->u.mem.index != 0 && op->u.mem.scale == 1 + && op->u.mem.disp == 0) { + addr_reg = op->u.mem.index; + } else { + int after_p = !move_p && out_p; + MIR_reg_t disp_reg = 0, scale_ind_reg = op->u.mem.index; + MIR_reg_t base_reg = op->u.mem.base, base_ind_reg = 0; + + if (op->u.mem.disp != 0) { + MIR_op_t disp_op = MIR_new_int_op (ctx, op->u.mem.disp); + + disp_reg = vn_add_val (ctx, func, MIR_T_I64, MIR_INSN_BOUND, disp_op, disp_op); + insn + = insert_op_insn (ctx, after_p, func_item, insn, + MIR_new_insn (ctx, MIR_MOV, MIR_new_reg_op (ctx, disp_reg), disp_op)); + } + if (scale_ind_reg != 0 && op->u.mem.scale > 1) { + MIR_op_t ind_op = MIR_new_reg_op (ctx, op->u.mem.index); + MIR_op_t scale_reg_op, scale_int_op = MIR_new_int_op (ctx, op->u.mem.scale); + + scale_reg_op = MIR_new_reg_op (ctx, vn_add_val (ctx, func, MIR_T_I64, MIR_INSN_BOUND, + scale_int_op, scale_int_op)); + insn = insert_op_insn (ctx, after_p, func_item, insn, + MIR_new_insn (ctx, MIR_MOV, scale_reg_op, scale_int_op)); + scale_ind_reg = vn_add_val (ctx, func, MIR_T_I64, MIR_MUL, ind_op, scale_reg_op); + insn = insert_op_insn (ctx, after_p, func_item, insn, + MIR_new_insn (ctx, MIR_MUL, MIR_new_reg_op (ctx, scale_ind_reg), + ind_op, scale_reg_op)); + } + if (base_reg != 0 && scale_ind_reg != 0) { + MIR_op_t base_op = MIR_new_reg_op (ctx, base_reg), + ind_op = MIR_new_reg_op (ctx, scale_ind_reg); + + base_ind_reg = vn_add_val (ctx, func, MIR_T_I64, MIR_ADD, base_op, ind_op); + insn = insert_op_insn (ctx, after_p, func_item, insn, + MIR_new_insn (ctx, MIR_ADD, MIR_new_reg_op (ctx, base_ind_reg), + base_op, ind_op)); + } else { + base_ind_reg = base_reg != 0 ? base_reg : scale_ind_reg; + } + if (base_ind_reg == 0) { + mir_assert (disp_reg != 0); + addr_reg = disp_reg; + } else if (disp_reg == 0) { + mir_assert (base_ind_reg != 0); + addr_reg = base_ind_reg; + } else { + MIR_op_t base_ind_op = MIR_new_reg_op (ctx, base_ind_reg); + MIR_op_t disp_op = MIR_new_reg_op (ctx, disp_reg); + + addr_reg = vn_add_val (ctx, func, MIR_T_I64, MIR_ADD, base_ind_op, disp_op); + insn = insert_op_insn (ctx, after_p, func_item, insn, + MIR_new_insn (ctx, MIR_ADD, MIR_new_reg_op (ctx, addr_reg), + base_ind_op, disp_op)); + } + } + mem_op.u.mem.base = addr_reg; + mem_op.u.mem.disp = 0; + mem_op.u.mem.index = 0; + mem_op.u.mem.scale = 0; + if (move_p && (nop == 1 || insn->ops[1].mode == MIR_OP_REG)) { + *op = mem_op; + } else { + type = (mem_op.u.mem.type == MIR_T_F || mem_op.u.mem.type == MIR_T_D + || mem_op.u.mem.type == MIR_T_LD + ? mem_op.u.mem.type + : MIR_T_I64); + code + = (type == MIR_T_F ? MIR_FMOV + : type == MIR_T_D ? MIR_DMOV : type == MIR_T_LD ? MIR_LDMOV : MIR_MOV); + new_op = MIR_new_reg_op (ctx, vn_add_val (ctx, func, type, MIR_INSN_BOUND, mem_op, mem_op)); + if (out_p) + new_insn = MIR_new_insn (ctx, code, mem_op, new_op); + else + new_insn = MIR_new_insn (ctx, code, new_op, mem_op); + insn = insert_op_insn (ctx, out_p, func_item, insn, new_insn); + *op = new_op; + } + break; + } + default: + /* We don't simplify code with hard regs. */ + mir_assert (FALSE); + } + op->value_mode = value_mode; +} + +void _MIR_simplify_insn (MIR_context_t ctx, MIR_item_t func_item, MIR_insn_t insn, int keep_ref_p, + int mem_float_p) { + int out_p; + MIR_insn_code_t code = insn->code; + size_t i, nops = MIR_insn_nops (ctx, insn); + + for (i = 0; i < nops; i++) { + MIR_insn_op_mode (ctx, insn, i, &out_p); + MIR_simplify_op (ctx, func_item, insn, i, out_p, code, + (insn->code == MIR_INLINE || insn->code == MIR_CALL) && i == 1 && keep_ref_p, + mem_float_p); + } +} + +static void make_one_ret (MIR_context_t ctx, MIR_item_t func_item) { + size_t i, j; + MIR_insn_code_t mov_code, ext_code; + MIR_reg_t ret_reg; + MIR_op_t reg_op, ret_reg_op; + MIR_func_t func = func_item->u.func; + MIR_type_t *res_types = func->res_types; + MIR_insn_t ret_label, insn, first_ret_insn; + VARR (MIR_op_t) * ret_ops; + int one_last_ret_p; + + one_last_ret_p + = (VARR_LENGTH (MIR_insn_t, temp_insns) == 1 + && VARR_GET (MIR_insn_t, temp_insns, 0) == DLIST_TAIL (MIR_insn_t, func->insns)); + ret_label = NULL; + if (one_last_ret_p) { + first_ret_insn = VARR_GET (MIR_insn_t, temp_insns, 0); + } else { + ret_label = MIR_new_label (ctx); + MIR_append_insn (ctx, func_item, ret_label); + } + VARR_CREATE (MIR_op_t, ret_ops, 16); + for (i = 0; i < func->nres; i++) { + if (one_last_ret_p) { + ret_reg_op = first_ret_insn->ops[i]; + } else { + mov_code + = (res_types[i] == MIR_T_F + ? MIR_FMOV + : res_types[i] == MIR_T_D ? MIR_DMOV : res_types[i] == MIR_T_LD ? MIR_LDMOV : MIR_MOV); + ret_reg = _MIR_new_temp_reg (ctx, mov_code == MIR_MOV ? MIR_T_I64 : res_types[i], func); + ret_reg_op = MIR_new_reg_op (ctx, ret_reg); + VARR_PUSH (MIR_op_t, ret_ops, ret_reg_op); + } + switch (res_types[i]) { + case MIR_T_I8: ext_code = MIR_EXT8; break; + case MIR_T_U8: ext_code = MIR_UEXT8; break; + case MIR_T_I16: ext_code = MIR_EXT16; break; + case MIR_T_U16: ext_code = MIR_UEXT16; break; + case MIR_T_I32: ext_code = MIR_EXT32; break; + case MIR_T_U32: ext_code = MIR_UEXT32; break; + default: ext_code = MIR_INVALID_INSN; break; + } + if (ext_code == MIR_INVALID_INSN) continue; + if (one_last_ret_p) + MIR_insert_insn_before (ctx, func_item, first_ret_insn, + MIR_new_insn (ctx, ext_code, ret_reg_op, ret_reg_op)); + else + MIR_append_insn (ctx, func_item, MIR_new_insn (ctx, ext_code, ret_reg_op, ret_reg_op)); + } + if (!one_last_ret_p) { + MIR_append_insn (ctx, func_item, + MIR_new_insn_arr (ctx, MIR_RET, func->nres, VARR_ADDR (MIR_op_t, ret_ops))); + for (i = 0; i < VARR_LENGTH (MIR_insn_t, temp_insns); i++) { + insn = VARR_GET (MIR_insn_t, temp_insns, i); + mir_assert (func->nres == MIR_insn_nops (ctx, insn)); + for (j = 0; j < func->nres; j++) { + mov_code = (res_types[j] == MIR_T_F + ? MIR_FMOV + : res_types[j] == MIR_T_D ? MIR_DMOV + : res_types[j] == MIR_T_LD ? MIR_LDMOV : MIR_MOV); + reg_op = insn->ops[j]; + mir_assert (reg_op.mode == MIR_OP_REG); + ret_reg_op = VARR_GET (MIR_op_t, ret_ops, j); + MIR_insert_insn_before (ctx, func_item, insn, + MIR_new_insn (ctx, mov_code, ret_reg_op, reg_op)); + } + MIR_insert_insn_before (ctx, func_item, insn, + MIR_new_insn (ctx, MIR_JMP, MIR_new_label_op (ctx, ret_label))); + MIR_remove_insn (ctx, func_item, insn); + } + } + VARR_DESTROY (MIR_op_t, ret_ops); +} + +static void remove_unused_labels (MIR_context_t ctx, MIR_item_t func_item) { + while (VARR_LENGTH (MIR_insn_t, temp_insns2) != 0) { + MIR_insn_t label = VARR_POP (MIR_insn_t, temp_insns2); + int64_t label_num = label->ops[0].u.i; + + if (label_num < VARR_LENGTH (uint8_t, temp_data) && VARR_GET (uint8_t, temp_data, label_num)) + continue; + MIR_remove_insn (ctx, func_item, label); + } +} + +static MIR_insn_code_t reverse_branch_code (MIR_insn_code_t code) { + switch (code) { + case MIR_BT: return MIR_BF; + case MIR_BTS: return MIR_BFS; + case MIR_BF: return MIR_BT; + case MIR_BFS: return MIR_BTS; + case MIR_BEQ: return MIR_BNE; + case MIR_BEQS: return MIR_BNES; + case MIR_BNE: return MIR_BEQ; + case MIR_BNES: return MIR_BEQS; + case MIR_BLT: return MIR_BGE; + case MIR_BLTS: return MIR_BGES; + case MIR_UBLT: return MIR_UBGE; + case MIR_UBLTS: return MIR_UBGES; + case MIR_BLE: return MIR_BGT; + case MIR_BLES: return MIR_BGTS; + case MIR_UBLE: return MIR_UBGT; + case MIR_UBLES: return MIR_UBGTS; + case MIR_BGT: return MIR_BLE; + case MIR_BGTS: return MIR_BLES; + case MIR_UBGT: return MIR_UBLE; + case MIR_UBGTS: return MIR_UBLES; + case MIR_BGE: return MIR_BLT; + case MIR_BGES: return MIR_BLTS; + case MIR_UBGE: return MIR_UBLT; + case MIR_UBGES: return MIR_UBLTS; + default: assert (FALSE); return code; + } +} + +static MIR_insn_t skip_labels (MIR_label_t label, MIR_label_t stop) { + for (MIR_insn_t insn = label;; insn = DLIST_NEXT (MIR_insn_t, insn)) + if (insn == NULL || insn->code != MIR_LABEL || insn == stop) return insn; +} + +static int64_t natural_alignment (int64_t s) { return s <= 2 ? s : s <= 4 ? 4 : s <= 8 ? 8 : 16; } + +static const int MAX_JUMP_CHAIN_LEN = 32; + +static int simplify_func (MIR_context_t ctx, MIR_item_t func_item, int mem_float_p) { + MIR_func_t func = func_item->u.func; + MIR_insn_t insn, next_insn, next_next_insn, jmp_insn, new_insn; + MIR_insn_code_t ext_code; + int jmps_num = 0, inline_p = FALSE; + + if (func_item->item_type != MIR_func_item) + (*error_func) (MIR_wrong_param_value_error, "MIR_remove_simplify: wrong func item"); + vn_empty (ctx); + func = func_item->u.func; + for (size_t i = 0; i < func->nargs; i++) { + MIR_var_t var = VARR_GET (MIR_var_t, func->vars, i); + + if (var.type == MIR_T_I64 || var.type == MIR_T_U64 || var.type == MIR_T_F || var.type == MIR_T_D + || var.type == MIR_T_LD) + continue; + switch (var.type) { + case MIR_T_I8: ext_code = MIR_EXT8; break; + case MIR_T_U8: ext_code = MIR_UEXT8; break; + case MIR_T_I16: ext_code = MIR_EXT16; break; + case MIR_T_U16: ext_code = MIR_UEXT16; break; + case MIR_T_I32: ext_code = MIR_EXT32; break; + case MIR_T_U32: ext_code = MIR_UEXT32; break; + default: ext_code = MIR_INVALID_INSN; break; + } + if (ext_code != MIR_INVALID_INSN) { + MIR_reg_t reg = MIR_reg (ctx, var.name, func); + MIR_insn_t new_insn + = MIR_new_insn (ctx, ext_code, MIR_new_reg_op (ctx, reg), MIR_new_reg_op (ctx, reg)); + + MIR_prepend_insn (ctx, func_item, new_insn); + } + } + VARR_TRUNC (MIR_insn_t, temp_insns, 0); + VARR_TRUNC (MIR_insn_t, temp_insns2, 0); + VARR_TRUNC (uint8_t, temp_data, 0); + for (insn = DLIST_HEAD (MIR_insn_t, func->insns); insn != NULL; insn = next_insn) { + MIR_insn_code_t code = insn->code; + MIR_op_t temp_op; + + if ((code == MIR_MOV || code == MIR_FMOV || code == MIR_DMOV || code == MIR_LDMOV) + && insn->ops[0].mode == MIR_OP_MEM && insn->ops[1].mode == MIR_OP_MEM) { + temp_op + = MIR_new_reg_op (ctx, _MIR_new_temp_reg (ctx, + code == MIR_MOV + ? MIR_T_I64 + : code == MIR_FMOV + ? MIR_T_F + : code == MIR_DMOV ? MIR_T_D : MIR_T_LD, + func)); + MIR_insert_insn_after (ctx, func_item, insn, MIR_new_insn (ctx, code, insn->ops[0], temp_op)); + insn->ops[0] = temp_op; + } + if (code == MIR_RET) VARR_PUSH (MIR_insn_t, temp_insns, insn); + if (code == MIR_LABEL) VARR_PUSH (MIR_insn_t, temp_insns2, insn); + next_insn = DLIST_NEXT (MIR_insn_t, insn); + if (code == MIR_ALLOCA + && (insn->ops[1].mode == MIR_OP_INT || insn->ops[1].mode == MIR_OP_UINT)) { + /* Consolidate allocas */ + int64_t size, overall_size, align, max_align; + + size = insn->ops[1].u.i; + overall_size = size <= 0 ? 1 : size; + max_align = align = natural_alignment (overall_size); + overall_size = (overall_size + align - 1) / align * align; + while (next_insn != NULL && next_insn->code == MIR_ALLOCA + && (next_insn->ops[1].mode == MIR_OP_INT || next_insn->ops[1].mode == MIR_OP_UINT) + && !MIR_op_eq_p (ctx, insn->ops[0], next_insn->ops[0])) { + size = next_insn->ops[1].u.i; + size = size <= 0 ? 1 : size; + align = natural_alignment (size); + size = (size + align - 1) / align * align; + if (max_align < align) { + max_align = align; + overall_size = (overall_size + align - 1) / align * align; + } + new_insn = MIR_new_insn (ctx, MIR_PTR32 ? MIR_ADDS : MIR_ADD, next_insn->ops[0], + insn->ops[0], MIR_new_int_op (ctx, overall_size)); + overall_size += size; + MIR_insert_insn_before (ctx, func_item, next_insn, new_insn); + MIR_remove_insn (ctx, func_item, next_insn); + next_insn = DLIST_NEXT (MIR_insn_t, new_insn); + } + insn->ops[1].u.i = overall_size; + next_insn = DLIST_NEXT (MIR_insn_t, insn); /* to process the current and new insns */ + } + if (code == MIR_INLINE || code == MIR_CALL) inline_p = TRUE; + if ((MIR_int_branch_code_p (code) || code == MIR_JMP) && insn->ops[0].mode == MIR_OP_LABEL + && skip_labels (next_insn, insn->ops[0].u.label) == insn->ops[0].u.label) { + /* BR L|JMP L; L: => L: Also Remember signaling NAN*/ + MIR_remove_insn (ctx, func_item, insn); + } else if (((code == MIR_MUL || code == MIR_MULS || code == MIR_DIV || code == MIR_DIVS) + && insn->ops[2].mode == MIR_OP_INT && insn->ops[2].u.i == 1) + || ((code == MIR_ADD || code == MIR_ADDS || code == MIR_SUB || code == MIR_SUBS + || code == MIR_OR || code == MIR_ORS || code == MIR_XOR || code == MIR_XORS + || code == MIR_LSH || code == MIR_LSHS || code == MIR_RSH || code == MIR_RSHS + || code == MIR_URSH || code == MIR_URSHS) + && insn->ops[2].mode == MIR_OP_INT && insn->ops[2].u.i == 0)) { + if (!MIR_op_eq_p (ctx, insn->ops[0], insn->ops[1])) { + next_insn = MIR_new_insn (ctx, MIR_MOV, insn->ops[0], insn->ops[1]); + MIR_insert_insn_before (ctx, func_item, insn, next_insn); + } + MIR_remove_insn (ctx, func_item, insn); + } else if (MIR_int_branch_code_p (code) && next_insn != NULL && next_insn->code == MIR_JMP + && insn->ops[0].mode == MIR_OP_LABEL && next_insn->ops[0].mode == MIR_OP_LABEL + && (skip_labels (next_insn->ops[0].u.label, insn->ops[0].u.label) + == insn->ops[0].u.label + || skip_labels (insn->ops[0].u.label, next_insn->ops[0].u.label) + == next_insn->ops[0].u.label)) { + /* BR L1;JMP L2; L2:L1: or L1:L2: => JMP L2*/ + MIR_remove_insn (ctx, func_item, insn); + } else if ((code == MIR_BT || code == MIR_BTS || code == MIR_BF || code == MIR_BFS) + && insn->ops[1].mode == MIR_OP_INT + && (insn->ops[1].u.i == 0 || insn->ops[1].u.i == 1)) { + if ((code == MIR_BT || code == MIR_BTS) == (insn->ops[1].u.i == 1)) { + new_insn = MIR_new_insn (ctx, MIR_JMP, insn->ops[0]); + MIR_insert_insn_before (ctx, func_item, insn, new_insn); + next_insn = new_insn; + } + MIR_remove_insn (ctx, func_item, insn); + // ??? make imm always second, what is about mem? + } else if (MIR_int_branch_code_p (code) && next_insn != NULL && next_insn->code == MIR_JMP + && (next_next_insn = DLIST_NEXT (MIR_insn_t, next_insn)) != NULL + && next_next_insn->code == MIR_LABEL && insn->ops[0].mode == MIR_OP_LABEL + && skip_labels (next_next_insn, insn->ops[0].u.label) == insn->ops[0].u.label) { + /* BCond L;JMP L2;L: => BNCond L2;L: */ + insn->ops[0] = next_insn->ops[0]; + insn->code = reverse_branch_code (insn->code); + MIR_remove_insn (ctx, func_item, next_insn); + next_insn = insn; + } else if (MIR_branch_code_p (code) && insn->ops[0].mode == MIR_OP_LABEL + && (jmp_insn = skip_labels (insn->ops[0].u.label, NULL)) != NULL + && jmp_insn->code == MIR_JMP && ++jmps_num < MAX_JUMP_CHAIN_LEN) { + /* B L;...;L:JMP L2 => B L2; ... Also constrain processing to avoid infinite loops */ + insn->ops[0] = jmp_insn->ops[0]; + next_insn = insn; + continue; + } else { + if (MIR_branch_code_p (code) || code == MIR_SWITCH) { + int64_t label_num; + size_t start_label_nop = 0, bound_label_nop = 1, n; + + if (code == MIR_SWITCH) { + start_label_nop = 1; + bound_label_nop = start_label_nop + insn->nops - 1; + } + for (n = start_label_nop; n < bound_label_nop; n++) { + label_num = insn->ops[n].u.label->ops[0].u.i; + while (label_num >= VARR_LENGTH (uint8_t, temp_data)) + VARR_PUSH (uint8_t, temp_data, FALSE); + VARR_SET (uint8_t, temp_data, label_num, TRUE); + } + } + _MIR_simplify_insn (ctx, func_item, insn, TRUE, mem_float_p); + } + jmps_num = 0; + } + make_one_ret (ctx, func_item); + remove_unused_labels (ctx, func_item); + return inline_p; +} + +static void set_inline_reg_map (MIR_context_t ctx, MIR_reg_t old_reg, MIR_reg_t new_reg) { + while (VARR_LENGTH (MIR_reg_t, inline_reg_map) <= old_reg) + VARR_PUSH (MIR_reg_t, inline_reg_map, 0); + VARR_SET (MIR_reg_t, inline_reg_map, old_reg, new_reg); +} + +#ifndef MIR_MAX_INSNS_FOR_INLINE +#define MIR_MAX_INSNS_FOR_INLINE 200 +#endif + +#ifndef MIR_MAX_INSNS_FOR_CALL_INLINE +#define MIR_MAX_INSNS_FOR_CALL_INLINE 50 +#endif + +#ifndef MIR_MAX_FUNC_INLINE_GROWTH +#define MIR_MAX_FUNC_INLINE_GROWTH 50 +#endif + +#ifndef MIR_MAX_CALLER_SIZE_FOR_ANY_GROWTH_INLINE +#define MIR_MAX_CALLER_SIZE_FOR_ANY_GROWTH_INLINE MIR_MAX_INSNS_FOR_INLINE +#endif + +/* Only simplified code should be inlined because we need already + extensions and one return. */ +static void process_inlines (MIR_context_t ctx, MIR_item_t func_item) { + int alloca_p; + size_t i, actual_nops, nargs, nvars; + MIR_type_t type, *res_types; + MIR_var_t var; + MIR_reg_t ret_reg, old_reg, new_reg, temp_reg; + MIR_insn_t func_insn, next_func_insn, call, insn, new_insn, ret_insn, ret_label; + MIR_item_t called_func_item; + MIR_func_t func, called_func; + size_t func_insns_num, called_func_insns_num; + char buff[50]; + + mir_assert (func_item->item_type == MIR_func_item); + vn_empty (ctx); + func = func_item->u.func; + func_insns_num = DLIST_LENGTH (MIR_insn_t, func->insns); + for (func_insn = DLIST_HEAD (MIR_insn_t, func->insns); func_insn != NULL; + func_insn = next_func_insn) { + inline_insns_before++; + inline_insns_after++; + next_func_insn = DLIST_NEXT (MIR_insn_t, func_insn); + if (func_insn->code != MIR_INLINE && func_insn->code != MIR_CALL) continue; + call = func_insn; + if (call->ops[1].mode != MIR_OP_REF) { + MIR_simplify_op (ctx, func_item, func_insn, 1, FALSE, func_insn->code, FALSE, TRUE); + continue; + } + called_func_item = call->ops[1].u.ref; + while (called_func_item != NULL + && (called_func_item->item_type == MIR_import_item + || called_func_item->item_type == MIR_export_item + || called_func_item->item_type == MIR_forward_item)) + called_func_item = called_func_item->ref_def; + if (called_func_item == NULL || called_func_item->item_type != MIR_func_item + || func_item == called_func_item) { /* Simplify function operand in the inline insn */ + MIR_simplify_op (ctx, func_item, func_insn, 1, FALSE, func_insn->code, FALSE, TRUE); + continue; + } + called_func = called_func_item->u.func; + called_func_insns_num = DLIST_LENGTH (MIR_insn_t, called_func->insns); + if (called_func->vararg_p + || called_func_insns_num > (func_insn->code == MIR_CALL ? MIR_MAX_INSNS_FOR_CALL_INLINE + : MIR_MAX_INSNS_FOR_INLINE) + || (called_func_insns_num > MIR_MAX_FUNC_INLINE_GROWTH * func_insns_num / 100 + && func_insns_num > MIR_MAX_CALLER_SIZE_FOR_ANY_GROWTH_INLINE)) { + MIR_simplify_op (ctx, func_item, func_insn, 1, FALSE, func_insn->code, FALSE, TRUE); + continue; + } + func_insns_num += called_func_insns_num; + inlined_calls++; + res_types = call->ops[0].u.ref->u.proto->res_types; + ret_label = MIR_new_label (ctx); + MIR_insert_insn_after (ctx, func_item, call, ret_label); + func->n_inlines++; + nargs = called_func->nargs; + nvars = VARR_LENGTH (MIR_var_t, called_func->vars); + for (i = 0; i < nvars; i++) { + VARR_TRUNC (char, temp_string, 0); + sprintf (buff, ".c%d_", func->n_inlines); + VARR_PUSH_ARR (char, temp_string, buff, strlen (buff)); + var = VARR_GET (MIR_var_t, called_func->vars, i); + type = (var.type == MIR_T_F || var.type == MIR_T_D || var.type == MIR_T_LD ? var.type + : MIR_T_I64); + old_reg = MIR_reg (ctx, var.name, called_func); + VARR_PUSH_ARR (char, temp_string, var.name, strlen (var.name) + 1); + new_reg = MIR_new_func_reg (ctx, func, type, VARR_ADDR (char, temp_string)); + set_inline_reg_map (ctx, old_reg, new_reg); + if (i < nargs && call->nops > i + 2 + called_func->nres) { /* Parameter passing */ + new_insn + = MIR_new_insn (ctx, + type == MIR_T_F + ? MIR_FMOV + : type == MIR_T_D ? MIR_DMOV : type == MIR_T_LD ? MIR_LDMOV : MIR_MOV, + MIR_new_reg_op (ctx, new_reg), call->ops[i + 2 + called_func->nres]); + MIR_insert_insn_before (ctx, func_item, ret_label, new_insn); + } + } + /* ??? No frame only alloca */ + /* Add new insns: */ + ret_reg = 0; + alloca_p = FALSE; + VARR_TRUNC (MIR_insn_t, temp_insns, 0); + VARR_TRUNC (MIR_insn_t, temp_insns2, 0); + VARR_TRUNC (uint8_t, temp_data, 0); + for (insn = DLIST_HEAD (MIR_insn_t, called_func->insns); insn != NULL; + insn = DLIST_NEXT (MIR_insn_t, insn)) { + inline_insns_after++; + actual_nops = MIR_insn_nops (ctx, insn); + new_insn = MIR_copy_insn (ctx, insn); + mir_assert (insn->code != MIR_VA_ARG && insn->code != MIR_VA_START + && insn->code != MIR_VA_END); + if (insn->code == MIR_ALLOCA) alloca_p = TRUE; + for (i = 0; i < actual_nops; i++) switch (new_insn->ops[i].mode) { + case MIR_OP_REG: + new_insn->ops[i].u.reg = VARR_GET (MIR_reg_t, inline_reg_map, new_insn->ops[i].u.reg); + break; + case MIR_OP_MEM: + if (insn->ops[i].u.mem.base != 0) + new_insn->ops[i].u.mem.base + = VARR_GET (MIR_reg_t, inline_reg_map, new_insn->ops[i].u.mem.base); + if (insn->ops[i].u.mem.index != 0) + new_insn->ops[i].u.mem.index + = VARR_GET (MIR_reg_t, inline_reg_map, new_insn->ops[i].u.mem.index); + break; + default: /* do nothing */ break; + } + if (new_insn->code != MIR_RET) { + MIR_insert_insn_before (ctx, func_item, ret_label, new_insn); + store_labels_for_duplication (ctx, insn, new_insn); + } else { + /* should be the last insn after simplification */ + mir_assert (DLIST_NEXT (MIR_insn_t, insn) == NULL && call->ops[0].mode == MIR_OP_REF + && call->ops[0].u.ref->item_type == MIR_proto_item); + mir_assert (called_func->nres == actual_nops); + ret_insn = new_insn; + for (i = 0; i < actual_nops; i++) { + mir_assert (ret_insn->ops[i].mode == MIR_OP_REG); + ret_reg = ret_insn->ops[i].u.reg; + new_insn = MIR_new_insn (ctx, + res_types[i] == MIR_T_F + ? MIR_FMOV + : res_types[i] == MIR_T_D + ? MIR_DMOV + : res_types[i] == MIR_T_LD ? MIR_LDMOV : MIR_MOV, + call->ops[i + 2], MIR_new_reg_op (ctx, ret_reg)); + MIR_insert_insn_before (ctx, func_item, ret_label, new_insn); + } + free (ret_insn); + } + } + redirect_duplicated_labels (ctx); + if (alloca_p) { + temp_reg = _MIR_new_temp_reg (ctx, MIR_T_I64, func); + new_insn = MIR_new_insn (ctx, MIR_BSTART, MIR_new_reg_op (ctx, temp_reg)); + MIR_insert_insn_after (ctx, func_item, call, new_insn); + new_insn = MIR_new_insn (ctx, MIR_BEND, MIR_new_reg_op (ctx, temp_reg)); + MIR_insert_insn_before (ctx, func_item, ret_label, new_insn); + } + MIR_remove_insn (ctx, func_item, call); + } +} + +const char *_MIR_uniq_string (MIR_context_t ctx, const char *str) { + return string_store (ctx, &strings, &string_tab, (MIR_str_t){strlen (str) + 1, str}).str.s; +} + +/* The next two function can be called any time relative to + load/linkage. You can also call them many times for the same name + but you should always use the same prototype or/and addr for the + same proto/func name. */ +MIR_item_t _MIR_builtin_proto (MIR_context_t ctx, MIR_module_t module, const char *name, + size_t nres, MIR_type_t *res_types, size_t nargs, ...) { + size_t i; + va_list argp; + MIR_var_t arg; + MIR_item_t proto_item; + MIR_module_t saved_module = curr_module; + + va_start (argp, nargs); + VARR_TRUNC (MIR_var_t, temp_vars, 0); + for (i = 0; i < nargs; i++) { + arg.type = va_arg (argp, MIR_type_t); + arg.name = va_arg (argp, const char *); + VARR_PUSH (MIR_var_t, temp_vars, arg); + } + va_end (argp); + name = _MIR_uniq_string (ctx, name); + proto_item = find_item (ctx, name, module); + if (proto_item != NULL) { + if (proto_item->item_type == MIR_proto_item && proto_item->u.proto->nres == nres + && VARR_LENGTH (MIR_var_t, proto_item->u.proto->args) == nargs) { + for (i = 0; i < nres; i++) + if (res_types[i] != proto_item->u.proto->res_types[i]) break; + if (i >= nres) { + for (i = 0; i < nargs; i++) + if (VARR_GET (MIR_var_t, temp_vars, i).type + != VARR_GET (MIR_var_t, proto_item->u.proto->args, i).type) + break; + if (i >= nargs) return proto_item; + } + } + (*error_func) (MIR_repeated_decl_error, + "_MIR_builtin_proto: proto item %s was already defined differently", name); + } + saved_module = curr_module; + curr_module = module; + proto_item + = MIR_new_proto_arr (ctx, name, nres, res_types, nargs, VARR_ADDR (MIR_var_t, temp_vars)); + DLIST_REMOVE (MIR_item_t, curr_module->items, proto_item); + DLIST_PREPEND (MIR_item_t, curr_module->items, proto_item); /* make it first in the list */ + curr_module = saved_module; + return proto_item; +} + +MIR_item_t _MIR_builtin_func (MIR_context_t ctx, MIR_module_t module, const char *name, + void *addr) { + MIR_item_t item, ref_item; + MIR_module_t saved_module = curr_module; + + name = _MIR_uniq_string (ctx, name); + if ((ref_item = find_item (ctx, name, &environment_module)) != NULL) { + if (ref_item->item_type != MIR_import_item || ref_item->addr != addr) + (*error_func) (MIR_repeated_decl_error, + "_MIR_builtin_func: func %s has already another address", name); + } else { + curr_module = &environment_module; + /* Use import for builtin func: */ + item = new_export_import_forward (ctx, name, MIR_import_item, "import", TRUE); + HTAB_DO (MIR_item_t, module_item_tab, item, HTAB_INSERT, ref_item); + mir_assert (item == ref_item); + DLIST_APPEND (MIR_item_t, environment_module.items, item); + ref_item->addr = addr; + curr_module = saved_module; + } + if ((item = find_item (ctx, name, module)) != NULL) { + if (item->item_type != MIR_import_item || item->addr != addr || item->ref_def != ref_item) + (*error_func) (MIR_repeated_decl_error, + "_MIR_builtin_func: func name %s was already defined differently in the " + "module", + name); + } else { + curr_module = module; + item = new_export_import_forward (ctx, name, MIR_import_item, "import", FALSE); + DLIST_REMOVE (MIR_item_t, curr_module->items, item); + DLIST_PREPEND (MIR_item_t, curr_module->items, item); /* make it first in the list */ + item->addr = ref_item->addr; + item->ref_def = ref_item; + curr_module = saved_module; + } + return item; +} + +/* New Page */ + +#include +#include + +struct code_holder { + uint8_t *start, *free, *bound; +}; + +typedef struct code_holder code_holder_t; + +DEF_VARR (code_holder_t); + +struct machine_code_ctx { + VARR (code_holder_t) * code_holders; + size_t page_size; +}; + +#define code_holders ctx->machine_code_ctx->code_holders +#define page_size ctx->machine_code_ctx->page_size + +uint8_t *_MIR_publish_code (MIR_context_t ctx, const uint8_t *code, size_t code_len) { + uint8_t *start, *mem; + size_t len; + code_holder_t ch; + int new_p = TRUE; + + if ((len = VARR_LENGTH (code_holder_t, code_holders)) > 0) { + code_holder_t *ch_ptr = VARR_ADDR (code_holder_t, code_holders) + len - 1; + uint8_t *free_addr = (uint8_t *) ((uint64_t) (ch_ptr->free + 15) / 16 * 16); /* align */ + + if (free_addr + code_len < ch_ptr->bound) { + mem = free_addr; + ch_ptr->free = free_addr + code_len; + new_p = FALSE; + start = ch_ptr->start; + len = ch_ptr->bound - start; + ch = *ch_ptr; + } + } + if (new_p) { + size_t npages = (code_len + page_size - 1) / page_size; + + len = page_size * npages; + mem = (uint8_t *) mmap (NULL, len, PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (mem == MAP_FAILED) return NULL; + start = ch.start = mem; + ch.free = mem + code_len; + ch.bound = mem + len; + VARR_PUSH (code_holder_t, code_holders, ch); + } + mprotect (ch.start, ch.bound - ch.start, PROT_WRITE | PROT_EXEC); + memcpy (mem, code, code_len); + mprotect (ch.start, ch.bound - ch.start, PROT_EXEC); + return mem; +} + +void _MIR_update_code_arr (MIR_context_t ctx, uint8_t *base, size_t nloc, + const MIR_code_reloc_t *relocs) { + size_t i, len, start, max_offset = 0; + + for (i = 0; i < nloc; i++) + if (max_offset < relocs[i].offset) max_offset = relocs[i].offset; + start = (size_t) base / page_size * page_size; + len = (size_t) base + max_offset + sizeof (void *) - start; + mprotect ((uint8_t *) start, len, PROT_WRITE | PROT_EXEC); + for (i = 0; i < nloc; i++) memcpy (base + relocs[i].offset, &relocs[i].value, sizeof (void *)); + mprotect ((uint8_t *) start, len, PROT_READ | PROT_EXEC); +} + +void _MIR_update_code (MIR_context_t ctx, uint8_t *base, size_t nloc, ...) { + size_t start, len, offset, max_offset = 0; + void *value; + va_list args; + + va_start (args, nloc); + for (size_t i = 0; i < nloc; i++) { + offset = va_arg (args, size_t); + value = va_arg (args, void *); + if (max_offset < offset) max_offset = offset; + } + va_end (args); + start = (size_t) base / page_size * page_size; + len = (size_t) base + max_offset + sizeof (void *) - start; + mprotect ((uint8_t *) start, len, PROT_WRITE | PROT_EXEC); + va_start (args, nloc); + for (size_t i = 0; i < nloc; i++) { + offset = va_arg (args, size_t); + value = va_arg (args, void *); + memcpy (base + offset, &value, sizeof (void *)); + } + mprotect ((uint8_t *) start, len, PROT_READ | PROT_EXEC); + va_end (args); +} + +static void machine_init (MIR_context_t ctx); +static void machine_finish (MIR_context_t ctx); + +static void code_init (MIR_context_t ctx) { + if ((ctx->machine_code_ctx = malloc (sizeof (struct machine_code_ctx))) == NULL) + (*error_func) (MIR_alloc_error, "Not enough memory for ctx"); + page_size = sysconf (_SC_PAGE_SIZE); + VARR_CREATE (code_holder_t, code_holders, 128); + machine_init (ctx); +} + +static void code_finish (MIR_context_t ctx) { + while (VARR_LENGTH (code_holder_t, code_holders) != 0) { + code_holder_t ch = VARR_POP (code_holder_t, code_holders); + munmap (ch.start, ch.bound - ch.start); + } + VARR_DESTROY (code_holder_t, code_holders); + machine_finish (ctx); + free (ctx->machine_code_ctx); + ctx->machine_code_ctx = NULL; +} + +/* New Page */ + +#if !MIR_NO_IO + +/* Input/output of binary MIR. Major goal of binary MIR is fast + reading, not compression ratio. Text MIR major CPU time consumer + is a scanner. Mostly in reading binary MIR we skip the scanner + part by using tokens. Each token starts with a tag which describes + subsequent optional bytes. */ + +#define TAG_EL(t) TAG_##t +#define REP_SEP , +typedef enum { + TAG_EL (U0), + REP8 (TAG_EL, U1, U2, U3, U4, U5, U6, U7, U8), + REP8 (TAG_EL, I1, I2, I3, I4, I5, I6, I7, I8), + REP3 (TAG_EL, F, D, LD), /* 4, 8, 16 bytes for floating point numbers */ + REP4 (TAG_EL, REG1, REG2, REG3, REG4), /* Reg string number in 1, 2, 3, 4 bytes */ + REP4 (TAG_EL, NAME1, NAME2, NAME3, NAME4), /* Name string number in 1, 2, 3, 4 bytes */ + REP4 (TAG_EL, STR1, STR2, STR3, STR4), /* String number in 1, 2, 3, 4 bytes */ + REP4 (TAG_EL, LAB1, LAB2, LAB3, LAB4), /* Label number in 1, 2, 3, 4 bytes */ + /* Tags for memory operands. The memory address parts are the subsequent tokens */ + REP4 (TAG_EL, MEM_DISP, MEM_BASE, MEM_INDEX, MEM_DISP_BASE), + REP3 (TAG_EL, MEM_DISP_INDEX, MEM_BASE_INDEX, MEM_DISP_BASE_INDEX), + /* MIR types. The same order as MIR types: */ + REP8 (TAG_EL, TI8, TU8, TI16, TU16, TI32, TU32, TI64, TU64), + REP6 (TAG_EL, TF, TD, TP, TV, TBLOCK, EOI), + TAG_EL (EOFILE), /* end of insn with variable number operands (e.g. a call) or end of file */ + /* unsigned integer 0..127 is kept in one byte. The most significant bit of the byte is 1: */ + U0_MASK = 0x7f, + U0_FLAG = 0x80, +} bin_tag_t; +#undef REP_SEP + +/* MIR binary format: + + VERSION + NSTR + (string)* + ( ((label)* (insn code) (operand)* | STRN=(func|local|import|export|forward|) ...) EOI? + )* EOF + + where + o VERSION and NSTR are unsigned tokens + o insn code is unsigned token + o string is string number tokens + o operand is unsigned, signed, float, double, string, label, memory tokens + o EOI, EOF - tokens for end of insn (optional for most insns) and end of file +*/ + +static const int CURR_BIN_VERSION = 1; + +DEF_VARR (MIR_str_t); +DEF_VARR (uint64_t); +DEF_VARR (MIR_label_t); + +struct io_ctx { + FILE *io_file; + int (*io_writer) (MIR_context_t, uint8_t); + int (*io_reader) (MIR_context_t); + struct reduce_data *io_reduce_data; + VARR (string_t) * output_strings; + HTAB (string_t) * output_string_tab; + VARR (MIR_str_t) * bin_strings; + VARR (uint64_t) * insn_label_string_nums; + VARR (MIR_label_t) * func_labels; + size_t output_insns_len, output_labs_len; + size_t output_regs_len, output_mem_len, output_int_len, output_float_len; +}; + +#define io_file ctx->io_ctx->io_file +#define io_writer ctx->io_ctx->io_writer +#define io_reader ctx->io_ctx->io_reader +#define io_reduce_data ctx->io_ctx->io_reduce_data +#define output_strings ctx->io_ctx->output_strings +#define output_string_tab ctx->io_ctx->output_string_tab +#define bin_strings ctx->io_ctx->bin_strings +#define insn_label_string_nums ctx->io_ctx->insn_label_string_nums +#define func_labels ctx->io_ctx->func_labels +#define output_insns_len ctx->io_ctx->output_insns_len +#define output_labs_len ctx->io_ctx->output_labs_len +#define output_regs_len ctx->io_ctx->output_regs_len +#define output_mem_len ctx->io_ctx->output_mem_len +#define output_int_len ctx->io_ctx->output_int_len +#define output_float_len ctx->io_ctx->output_float_len + +typedef reduce_writer_t writer_func_t; + +static size_t put_byte (MIR_context_t ctx, writer_func_t writer, int ch) { + if (writer == NULL) return 0; +#ifdef MIR_NO_BIN_COMPRESSION + io_writer (ctx, ch); +#else + reduce_encode_put (io_reduce_data, ch); +#endif + return 1; +} + +static size_t uint_length (uint64_t u) { + size_t n; + + if (u <= 127) return 0; + for (n = 0; u != 0; n++) u >>= CHAR_BIT; + return n; +} + +static size_t put_uint (MIR_context_t ctx, writer_func_t writer, uint64_t u, int nb) { + if (writer == NULL) return 0; + for (int n = 0; n < nb; n++) { + put_byte (ctx, writer, u & 0xff); + u >>= CHAR_BIT; + } + return nb; +} + +static size_t int_length (int64_t i) { + uint64_t u = i; + size_t n = 0; + + for (n = 0; u != 0; n++) u >>= CHAR_BIT; + return n == 0 ? 1 : n; +} + +static size_t put_int (MIR_context_t ctx, writer_func_t writer, int64_t i, int nb) { + return put_uint (ctx, writer, (uint64_t) i, nb); +} + +static size_t put_float (MIR_context_t ctx, writer_func_t writer, float fl) { + union { + uint32_t u; + float f; + } u; + + if (writer == NULL) return 0; + u.f = fl; + return put_uint (ctx, writer, u.u, sizeof (uint32_t)); +} + +static size_t put_double (MIR_context_t ctx, writer_func_t writer, double d) { + union { + uint64_t u; + double d; + } u; + + if (writer == NULL) return 0; + u.d = d; + return put_uint (ctx, writer, u.u, sizeof (uint64_t)); +} + +static size_t put_ldouble (MIR_context_t ctx, writer_func_t writer, long double ld) { + union { + uint64_t u[2]; + long double ld; + } u; + size_t len; + + if (writer == NULL) return 0; + u.ld = ld; + len = put_uint (ctx, writer, u.u[0], sizeof (uint64_t)); + return put_uint (ctx, writer, u.u[1], sizeof (uint64_t)) + len; +} + +/* Write binary MIR */ + +static size_t write_int (MIR_context_t ctx, writer_func_t writer, int64_t i) { + size_t nb, len; + + if (writer == NULL) return 0; + nb = int_length (i); + assert (nb > 0); + put_byte (ctx, writer, TAG_I1 + nb - 1); + len = put_int (ctx, writer, i, nb) + 1; + output_int_len += len; + return len; +} + +static size_t write_uint (MIR_context_t ctx, writer_func_t writer, uint64_t u) { + size_t nb, len; + + if (writer == NULL) return 0; + if ((nb = uint_length (u)) == 0) { + put_byte (ctx, writer, 0x80 | u); + return 1; + } + put_byte (ctx, writer, TAG_U1 + nb - 1); + len = put_uint (ctx, writer, u, nb) + 1; + output_int_len += len; + return len; +} + +static size_t write_float (MIR_context_t ctx, writer_func_t writer, float fl) { + size_t len; + + if (writer == NULL) return 0; + put_byte (ctx, writer, TAG_F); + len = put_float (ctx, writer, fl) + 1; + output_float_len += len; + return len; +} + +static size_t write_double (MIR_context_t ctx, writer_func_t writer, double d) { + size_t len; + + if (writer == NULL) return 0; + put_byte (ctx, writer, TAG_D); + len = put_double (ctx, writer, d) + 1; + output_float_len += len; + return len; +} + +static size_t write_ldouble (MIR_context_t ctx, writer_func_t writer, long double ld) { + size_t len; + + if (writer == NULL) return 0; + put_byte (ctx, writer, TAG_LD); + len = put_ldouble (ctx, writer, ld) + 1; + output_int_len += len; + return len; +} + +static size_t write_str_tag (MIR_context_t ctx, writer_func_t writer, MIR_str_t str, + bin_tag_t start_tag) { + size_t nb, len; + int ok_p; + string_t string; + + if (writer == NULL) { + string_store (ctx, &output_strings, &output_string_tab, str); + return 0; + } + ok_p = string_find (&output_strings, &output_string_tab, str, &string); + mir_assert (ok_p && string.num >= 1); + nb = uint_length (string.num - 1); + mir_assert (nb <= 4); + if (nb == 0) nb = 1; + put_byte (ctx, writer, start_tag + nb - 1); + return put_uint (ctx, writer, string.num - 1, nb) + 1; +} + +static size_t write_str (MIR_context_t ctx, writer_func_t writer, MIR_str_t str) { + return write_str_tag (ctx, writer, str, TAG_STR1); +} +static size_t write_name (MIR_context_t ctx, writer_func_t writer, const char *name) { + return write_str_tag (ctx, writer, (MIR_str_t){strlen (name) + 1, name}, TAG_NAME1); +} + +static size_t write_reg (MIR_context_t ctx, writer_func_t writer, const char *reg_name) { + size_t len = write_str_tag (ctx, writer, (MIR_str_t){strlen (reg_name) + 1, reg_name}, TAG_REG1); + + output_regs_len += len; + return len; +} + +static size_t write_type (MIR_context_t ctx, writer_func_t writer, MIR_type_t t) { + return put_byte (ctx, writer, TAG_TI8 + (t - MIR_T_I8)); +} + +static size_t write_lab (MIR_context_t ctx, writer_func_t writer, MIR_label_t lab) { + size_t nb, len; + uint64_t lab_num; + + if (writer == NULL) return 0; + lab_num = lab->ops[0].u.u; + nb = uint_length (lab_num); + mir_assert (nb <= 4); + if (nb == 0) nb = 1; + put_byte (ctx, writer, TAG_LAB1 + nb - 1); + len = put_uint (ctx, writer, lab_num, nb) + 1; + output_labs_len += len; + return len; +} + +static size_t write_op (MIR_context_t ctx, writer_func_t writer, MIR_func_t func, MIR_op_t op) { + switch (op.mode) { + case MIR_OP_REG: return write_reg (ctx, writer, MIR_reg_name (ctx, op.u.reg, func)); + case MIR_OP_INT: return write_int (ctx, writer, op.u.i); + case MIR_OP_UINT: return write_uint (ctx, writer, op.u.u); + case MIR_OP_FLOAT: return write_float (ctx, writer, op.u.f); + case MIR_OP_DOUBLE: return write_double (ctx, writer, op.u.d); + case MIR_OP_LDOUBLE: return write_ldouble (ctx, writer, op.u.ld); + case MIR_OP_MEM: { + bin_tag_t tag; + size_t len; + + if (op.u.mem.disp != 0) { + if (op.u.mem.base != 0) + tag = op.u.mem.index != 0 ? TAG_MEM_DISP_BASE_INDEX : TAG_MEM_DISP_BASE; + else + tag = op.u.mem.index != 0 ? TAG_MEM_DISP_INDEX : TAG_MEM_DISP; + } else if (op.u.mem.base != 0) { + tag = op.u.mem.index != 0 ? TAG_MEM_BASE_INDEX : TAG_MEM_BASE; + } else if (op.u.mem.index != 0) { + tag = TAG_MEM_INDEX; + } else { + tag = TAG_MEM_DISP; + } + put_byte (ctx, writer, tag); + len = write_type (ctx, writer, op.u.mem.type) + 1; + if (op.u.mem.disp != 0 || (op.u.mem.base == 0 && op.u.mem.index == 0)) + write_int (ctx, writer, op.u.mem.disp); + if (op.u.mem.base != 0) write_reg (ctx, writer, MIR_reg_name (ctx, op.u.mem.base, func)); + if (op.u.mem.index != 0) { + len += write_reg (ctx, writer, MIR_reg_name (ctx, op.u.mem.index, func)); + len += write_uint (ctx, writer, op.u.mem.scale); + } + output_mem_len += len; + return len; + } + case MIR_OP_REF: return write_name (ctx, writer, MIR_item_name (ctx, op.u.ref)); + case MIR_OP_STR: return write_str (ctx, writer, op.u.str); + case MIR_OP_LABEL: return write_lab (ctx, writer, op.u.label); + default: mir_assert (FALSE); return 0; + } +} + +static size_t write_insn (MIR_context_t ctx, writer_func_t writer, MIR_func_t func, + MIR_insn_t insn) { + size_t i, nops; + MIR_insn_code_t code = insn->code; + size_t len; + + if (code == MIR_LABEL) return write_lab (ctx, writer, insn); + nops = MIR_insn_nops (ctx, insn); + len = write_uint (ctx, writer, code); + for (i = 0; i < nops; i++) len += write_op (ctx, writer, func, insn->ops[i]); + if (insn_descs[code].op_modes[0] == MIR_OP_BOUND) { + /* first operand mode is undefined if it is a variable operand insn */ + mir_assert (MIR_call_code_p (code) || code == MIR_RET || code == MIR_SWITCH); + put_byte (ctx, writer, TAG_EOI); + len++; + } + output_insns_len += len; + return len; +} + +static size_t write_item (MIR_context_t ctx, writer_func_t writer, MIR_item_t item) { + MIR_insn_t insn; + MIR_func_t func; + MIR_proto_t proto; + MIR_var_t var; + size_t i, nlocals, len = 0; + + if (item->item_type == MIR_import_item) { + len += write_name (ctx, writer, "import"); + len += write_name (ctx, writer, item->u.import); + return len; + } + if (item->item_type == MIR_export_item) { + len += write_name (ctx, writer, "export"); + len += write_name (ctx, writer, item->u.export); + return len; + } + if (item->item_type == MIR_forward_item) { + len += write_name (ctx, writer, "forward"); + len += write_name (ctx, writer, item->u.forward); + return len; + } + if (item->item_type == MIR_bss_item) { + if (item->u.bss->name == NULL) { + len += write_name (ctx, writer, "bss"); + } else { + len += write_name (ctx, writer, "nbss"); + len += write_name (ctx, writer, item->u.bss->name); + } + len += write_uint (ctx, writer, item->u.bss->len); + return len; + } + if (item->item_type == MIR_ref_data_item) { + if (item->u.ref_data->name == NULL) { + len += write_name (ctx, writer, "ref"); + } else { + len += write_name (ctx, writer, "nref"); + len += write_name (ctx, writer, item->u.ref_data->name); + } + len += write_name (ctx, writer, MIR_item_name (ctx, item->u.ref_data->ref_item)); + len += write_int (ctx, writer, item->u.ref_data->disp); + return len; + } + if (item->item_type == MIR_expr_data_item) { + if (item->u.expr_data->name == NULL) { + len += write_name (ctx, writer, "expr"); + } else { + len += write_name (ctx, writer, "nexpr"); + len += write_name (ctx, writer, item->u.expr_data->name); + } + len += write_name (ctx, writer, MIR_item_name (ctx, item->u.expr_data->expr_item)); + return len; + } + if (item->item_type == MIR_data_item) { + MIR_data_t data = item->u.data; + + if (data->name == NULL) { + len += write_name (ctx, writer, "data"); + } else { + len += write_name (ctx, writer, "ndata"); + len += write_name (ctx, writer, data->name); + } + write_type (ctx, writer, data->el_type); + for (i = 0; i < data->nel; i++) switch (data->el_type) { + case MIR_T_I8: len += write_int (ctx, writer, ((int8_t *) data->u.els)[i]); break; + case MIR_T_U8: len += write_uint (ctx, writer, ((uint8_t *) data->u.els)[i]); break; + case MIR_T_I16: len += write_int (ctx, writer, ((int16_t *) data->u.els)[i]); break; + case MIR_T_U16: len += write_uint (ctx, writer, ((uint16_t *) data->u.els)[i]); break; + case MIR_T_I32: len += write_int (ctx, writer, ((int32_t *) data->u.els)[i]); break; + case MIR_T_U32: len += write_uint (ctx, writer, ((uint32_t *) data->u.els)[i]); break; + case MIR_T_I64: len += write_int (ctx, writer, ((int64_t *) data->u.els)[i]); break; + case MIR_T_U64: len += write_uint (ctx, writer, ((uint64_t *) data->u.els)[i]); break; + case MIR_T_F: len += write_float (ctx, writer, ((float *) data->u.els)[i]); break; + case MIR_T_D: len += write_double (ctx, writer, ((double *) data->u.els)[i]); break; + case MIR_T_LD: + len += write_ldouble (ctx, writer, ((long double *) data->u.els)[i]); + break; + /* only ptr as ref ??? */ + case MIR_T_P: len += write_uint (ctx, writer, ((uintptr_t *) data->u.els)[i]); break; + default: mir_assert (FALSE); + } + len += put_byte (ctx, writer, TAG_EOI); + return len; + } + if (item->item_type == MIR_proto_item) { + proto = item->u.proto; + len += write_name (ctx, writer, "proto"); + len += write_name (ctx, writer, proto->name); + len += write_uint (ctx, writer, proto->vararg_p != 0); + len += write_uint (ctx, writer, proto->nres); + for (i = 0; i < proto->nres; i++) write_type (ctx, writer, proto->res_types[i]); + for (i = 0; i < VARR_LENGTH (MIR_var_t, proto->args); i++) { + var = VARR_GET (MIR_var_t, proto->args, i); + len += write_type (ctx, writer, var.type); + len += write_name (ctx, writer, var.name); + } + len += put_byte (ctx, writer, TAG_EOI); + return len; + } + func = item->u.func; + len += write_name (ctx, writer, "func"); + len += write_name (ctx, writer, func->name); + len += write_uint (ctx, writer, func->vararg_p != 0); + len += write_uint (ctx, writer, func->nres); + for (i = 0; i < func->nres; i++) len += write_type (ctx, writer, func->res_types[i]); + for (i = 0; i < func->nargs; i++) { + var = VARR_GET (MIR_var_t, func->vars, i); + len += write_type (ctx, writer, var.type); + len += write_name (ctx, writer, var.name); + } + len += put_byte (ctx, writer, TAG_EOI); + nlocals = VARR_LENGTH (MIR_var_t, func->vars) - func->nargs; + if (nlocals != 0) { + len += write_name (ctx, writer, "local"); + for (i = 0; i < nlocals; i++) { + var = VARR_GET (MIR_var_t, func->vars, i + func->nargs); + len += write_type (ctx, writer, var.type); + len += write_name (ctx, writer, var.name); + } + len += put_byte (ctx, writer, TAG_EOI); + } + for (insn = DLIST_HEAD (MIR_insn_t, func->insns); insn != NULL; + insn = DLIST_NEXT (MIR_insn_t, insn)) + len += write_insn (ctx, writer, func, insn); + len += write_name (ctx, writer, "endfunc"); + return len; +} + +static size_t write_module (MIR_context_t ctx, writer_func_t writer, MIR_module_t module) { + size_t len = write_name (ctx, writer, "module"); + + len += write_name (ctx, writer, module->name); + for (MIR_item_t item = DLIST_HEAD (MIR_item_t, module->items); item != NULL; + item = DLIST_NEXT (MIR_item_t, item)) + len += write_item (ctx, writer, item); + len += write_name (ctx, writer, "endmodule"); + return len; +} + +static size_t write_modules (MIR_context_t ctx, writer_func_t writer, MIR_module_t module) { + size_t len = 0; + + for (MIR_module_t m = DLIST_HEAD (MIR_module_t, all_modules); m != NULL; + m = DLIST_NEXT (MIR_module_t, m)) + if (module == NULL || m == module) len += write_module (ctx, writer, m); + return len; +} + +static size_t reduce_writer (const void *start, size_t len, void *aux_data) { + MIR_context_t ctx = aux_data; + size_t i, n = 0; + + for (i = n = 0; i < len; i++, n++) + if (io_writer (ctx, ((uint8_t *) start)[i]) == EOF) break; + return n; +} + +void MIR_write_module_with_func (MIR_context_t ctx, const int (*writer) (MIR_context_t, uint8_t), + MIR_module_t module) { + size_t len, str_len; + + io_writer = writer; +#ifndef MIR_NO_BIN_COMPRESSION + if ((io_reduce_data = reduce_encode_start (reduce_writer, ctx)) == NULL) + (*error_func) (MIR_binary_io_error, "can not alloc data for MIR binary compression"); +#endif + output_insns_len = output_labs_len = 0; + output_regs_len = output_mem_len = output_int_len = output_float_len = 0; + string_init (&output_strings, &output_string_tab); + write_modules (ctx, NULL, module); /* store strings */ + len = write_uint (ctx, reduce_writer, CURR_BIN_VERSION); + str_len = write_uint (ctx, reduce_writer, VARR_LENGTH (string_t, output_strings) - 1); + for (size_t i = 1; i < VARR_LENGTH (string_t, output_strings); i++) { /* output strings */ + MIR_str_t str = VARR_GET (string_t, output_strings, i).str; + + str_len += write_uint (ctx, reduce_writer, str.len); + for (size_t j = 0; j < str.len; j++) { + put_byte (ctx, reduce_writer, str.s[j]); + str_len++; + } + } + len += write_modules (ctx, reduce_writer, module) + str_len; +#if 0 + fprintf (stderr, + "Overall output length = %lu. Number of strings = %lu.\n" + "Lengths of: strings = %lu, insns = %lu, labs = %lu,\n" + " reg ops = %lu, mem ops = %lu, int ops = %lu, float ops = %lu\n", + len, VARR_LENGTH (string_t, output_strings), str_len, output_insns_len, output_labs_len, + output_regs_len, output_mem_len, output_int_len, output_float_len); +#endif + put_byte (ctx, reduce_writer, TAG_EOFILE); + string_finish (&output_strings, &output_string_tab); +#ifndef MIR_NO_BIN_COMPRESSION + if (!reduce_encode_finish (io_reduce_data)) + (*error_func) (MIR_binary_io_error, "error in writing MIR binary"); +#endif +} + +void MIR_write_with_func (MIR_context_t ctx, const int (*writer) (MIR_context_t, uint8_t)) { + MIR_write_module_with_func (ctx, writer, NULL); +} + +static int file_writer (MIR_context_t ctx, uint8_t byte) { return fputc (byte, io_file); } + +void MIR_write_module (MIR_context_t ctx, FILE *f, MIR_module_t module) { + io_file = f; + MIR_write_module_with_func (ctx, file_writer, module); +} + +void MIR_write (MIR_context_t ctx, FILE *f) { MIR_write_module (ctx, f, NULL); } + +/* New Page */ + +static int get_byte (MIR_context_t ctx) { +#ifdef MIR_NO_BIN_COMPRESSION + int c = io_reader (ctx); +#else + int c = reduce_decode_get (io_reduce_data); +#endif + + if (c == EOF) (*error_func) (MIR_binary_io_error, "unfinished binary MIR"); + return c; +} + +typedef union { + uint64_t u; + int64_t i; + float f; + double d; + long double ld; + MIR_type_t t; + MIR_reg_t reg; +} token_attr_t; + +static uint64_t get_uint (MIR_context_t ctx, int nb) { + uint64_t res = 0; + + for (int i = 0; i < nb; i++) res |= (uint64_t) get_byte (ctx) << (i * CHAR_BIT); + return res; +} + +static int64_t get_int (MIR_context_t ctx, int nb) { return (int64_t) get_uint (ctx, nb); } + +static float get_float (MIR_context_t ctx) { + union { + uint32_t u; + float f; + } u; + + u.u = get_uint (ctx, sizeof (uint32_t)); + return u.f; +} + +static double get_double (MIR_context_t ctx) { + union { + uint64_t u; + double d; + } u; + + u.u = get_uint (ctx, sizeof (uint64_t)); + return u.d; +} + +static long double get_ldouble (MIR_context_t ctx) { + union { + uint64_t u[2]; + long double ld; + } u; + + u.u[0] = get_uint (ctx, sizeof (uint64_t)); + u.u[1] = get_uint (ctx, sizeof (uint64_t)); + return u.ld; +} + +static MIR_str_t to_str (MIR_context_t ctx, uint64_t str_num) { + if (str_num >= VARR_LENGTH (MIR_str_t, bin_strings)) + (*error_func) (MIR_binary_io_error, "wrong string num %lu", str_num); + return VARR_GET (MIR_str_t, bin_strings, str_num); +} + +static void process_reserved_name (const char *s, const char *prefix, uint32_t *max_num) { + char *end; + uint32_t num; + size_t len = strlen (prefix); + + if (strncmp (s, prefix, len) != 0) return; + num = strtoul (s + len, &end, 10); + if (*end != '\0') return; + if (*max_num < num) *max_num = num; +} + +static MIR_reg_t to_reg (MIR_context_t ctx, uint64_t reg_str_num, MIR_item_t func) { + const char *s = to_str (ctx, reg_str_num).s; + + process_reserved_name (s, TEMP_REG_NAME_PREFIX, &func->u.func->last_temp_num); + return MIR_reg (ctx, s, func->u.func); +} + +static MIR_label_t to_lab (MIR_context_t ctx, uint64_t lab_num) { + MIR_label_t lab; + + while (lab_num >= VARR_LENGTH (MIR_label_t, func_labels)) + VARR_PUSH (MIR_label_t, func_labels, NULL); + if ((lab = VARR_GET (MIR_label_t, func_labels, lab_num)) != NULL) return lab; + lab = create_label (ctx, lab_num); + VARR_SET (MIR_label_t, func_labels, lab_num, lab); + return lab; +} + +static int64_t read_int (MIR_context_t ctx, const char *err_msg) { + int c = get_byte (ctx); + + if (TAG_I1 > c || c > TAG_I8) (*error_func) (MIR_binary_io_error, err_msg); + return get_int (ctx, c - TAG_I1 + 1); +} + +static uint64_t read_uint (MIR_context_t ctx, const char *err_msg) { + int c = get_byte (ctx); + + if (c & U0_FLAG) return c & U0_MASK; + if (TAG_U1 > c || c > TAG_U8) (*error_func) (MIR_binary_io_error, err_msg); + return get_uint (ctx, c - TAG_U1 + 1); +} + +static void read_all_strings (MIR_context_t ctx, uint64_t nstr) { + int c; + MIR_str_t str; + uint64_t len, l; + + VARR_TRUNC (MIR_str_t, bin_strings, 0); + for (uint64_t i = 0; i < nstr; i++) { + VARR_TRUNC (char, temp_string, 0); + len = read_uint (ctx, "wrong string length"); + for (l = 0; l < len; l++) { + c = get_byte (ctx); + VARR_PUSH (char, temp_string, c); + } + str.s = VARR_ADDR (char, temp_string); + str.len = len; + str = string_store (ctx, &strings, &string_tab, str).str; + VARR_PUSH (MIR_str_t, bin_strings, str); + } +} + +static MIR_type_t tag_type (bin_tag_t tag) { return (MIR_type_t) (tag - TAG_TI8) + MIR_T_I8; } + +static MIR_type_t read_type (MIR_context_t ctx, const char *err_msg) { + int c = get_byte (ctx); + + if (TAG_TI8 > c || c > TAG_TBLOCK) (*error_func) (MIR_binary_io_error, err_msg); + return tag_type (c); +} + +static const char *read_name (MIR_context_t ctx, MIR_module_t module, const char *err_msg) { + int c = get_byte (ctx); + const char *s; + + if (TAG_NAME1 > c || c > TAG_NAME4) (*error_func) (MIR_binary_io_error, err_msg); + s = to_str (ctx, get_uint (ctx, c - TAG_NAME1 + 1)).s; + process_reserved_name (s, TEMP_ITEM_NAME_PREFIX, &module->last_temp_item_num); + return s; +} + +#define TAG_CASE(t) case TAG_##t: +#define REP_SEP +static bin_tag_t read_token (MIR_context_t ctx, token_attr_t *attr) { + int c = get_byte (ctx); + + if (c & U0_FLAG) { + attr->u = c & U0_MASK; + return TAG_U0; + } + switch (c) { + REP8 (TAG_CASE, U1, U2, U3, U4, U5, U6, U7, U8) + attr->u = get_uint (ctx, c - TAG_U1 + 1); + break; + REP8 (TAG_CASE, I1, I2, I3, I4, I5, I6, I7, I8) + attr->i = get_int (ctx, c - TAG_I1 + 1); + break; + TAG_CASE (F) + attr->f = get_float (ctx); + break; + TAG_CASE (D) + attr->d = get_double (ctx); + break; + TAG_CASE (LD) + attr->ld = get_ldouble (ctx); + break; + REP4 (TAG_CASE, REG1, REG2, REG3, REG4) + attr->u = get_uint (ctx, c - TAG_REG1 + 1); + break; + REP4 (TAG_CASE, NAME1, NAME2, NAME3, NAME4) + attr->u = get_uint (ctx, c - TAG_NAME1 + 1); + break; + REP4 (TAG_CASE, STR1, STR2, STR3, STR4) + attr->u = get_uint (ctx, c - TAG_STR1 + 1); + break; + REP4 (TAG_CASE, LAB1, LAB2, LAB3, LAB4) + attr->u = get_uint (ctx, c - TAG_LAB1 + 1); + break; + REP6 (TAG_CASE, MEM_DISP, MEM_BASE, MEM_INDEX, MEM_DISP_BASE, MEM_DISP_INDEX, MEM_BASE_INDEX) + REP3 (TAG_CASE, MEM_DISP_BASE_INDEX, EOI, EOFILE) + break; + REP8 (TAG_CASE, TI8, TU8, TI16, TU16, TI32, TU32, TI64, TU64) + REP5 (TAG_CASE, TF, TD, TP, TV, TBLOCK) + attr->t = (MIR_type_t) (c - TAG_TI8) + MIR_T_I8; + break; + default: (*error_func) (MIR_binary_io_error, "wrong tag %d", c); + } + return c; +} + +static MIR_disp_t read_disp (MIR_context_t ctx) { + bin_tag_t tag; + token_attr_t attr; + + tag = read_token (ctx, &attr); + if (TAG_I1 > tag || tag > TAG_I8) + (*error_func) (MIR_binary_io_error, "memory disp has wrong tag %d", tag); + return attr.i; +} + +static MIR_reg_t read_reg (MIR_context_t ctx, MIR_item_t func) { + bin_tag_t tag; + token_attr_t attr; + + tag = read_token (ctx, &attr); + if (TAG_REG1 > tag || tag > TAG_REG4) + (*error_func) (MIR_binary_io_error, "register has wrong tag %d", tag); + return to_reg (ctx, attr.u, func); +} + +static int read_operand (MIR_context_t ctx, MIR_op_t *op, MIR_item_t func) { + bin_tag_t tag; + token_attr_t attr; + MIR_type_t t; + MIR_disp_t disp; + MIR_reg_t base, index; + MIR_scale_t scale; + + tag = read_token (ctx, &attr); + switch (tag) { + TAG_CASE (U0) + REP8 (TAG_CASE, U1, U2, U3, U4, U5, U6, U7, U8) *op = MIR_new_uint_op (ctx, attr.u); + break; + REP8 (TAG_CASE, I1, I2, I3, I4, I5, I6, I7, I8) + *op = MIR_new_int_op (ctx, attr.i); + break; + TAG_CASE (F) + *op = MIR_new_float_op (ctx, attr.f); + break; + TAG_CASE (D) + *op = MIR_new_double_op (ctx, attr.d); + break; + TAG_CASE (LD) + *op = MIR_new_ldouble_op (ctx, attr.ld); + break; + REP4 (TAG_CASE, REG1, REG2, REG3, REG4) + *op = MIR_new_reg_op (ctx, to_reg (ctx, attr.u, func)); + break; + REP4 (TAG_CASE, NAME1, NAME2, NAME3, NAME4) { + const char *name = to_str (ctx, attr.u).s; + MIR_item_t item = find_item (ctx, name, func->module); + + if (item == NULL) (*error_func) (MIR_binary_io_error, "not found item %s", name); + *op = MIR_new_ref_op (ctx, item); + break; + } + REP4 (TAG_CASE, STR1, STR2, STR3, STR4) + *op = MIR_new_str_op (ctx, to_str (ctx, attr.u)); + break; + REP4 (TAG_CASE, LAB1, LAB2, LAB3, LAB4) + *op = MIR_new_label_op (ctx, to_lab (ctx, attr.u)); + break; + REP7 (TAG_CASE, MEM_DISP, MEM_BASE, MEM_INDEX, MEM_DISP_BASE, MEM_DISP_INDEX, MEM_BASE_INDEX, + MEM_DISP_BASE_INDEX) + t = read_type (ctx, "wrong memory type"); + disp = (tag == TAG_MEM_DISP || tag == TAG_MEM_DISP_BASE || tag == TAG_MEM_DISP_INDEX + || tag == TAG_MEM_DISP_BASE_INDEX + ? read_disp (ctx) + : 0); + base = (tag == TAG_MEM_BASE || tag == TAG_MEM_DISP_BASE || tag == TAG_MEM_BASE_INDEX + || tag == TAG_MEM_DISP_BASE_INDEX + ? read_reg (ctx, func) + : 0); + index = 0; + scale = 0; + if (tag == TAG_MEM_INDEX || tag == TAG_MEM_DISP_INDEX || tag == TAG_MEM_BASE_INDEX + || tag == TAG_MEM_DISP_BASE_INDEX) { + index = read_reg (ctx, func); + scale = read_uint (ctx, "wrong memory index scale"); + } + *op = MIR_new_mem_op (ctx, t, disp, base, index, scale); + break; + case TAG_EOI: return FALSE; + default: mir_assert (FALSE); + } + return TRUE; +} +#undef REP_SEP + +static int func_proto_read (MIR_context_t ctx, MIR_module_t module, uint64_t *nres_ptr) { + bin_tag_t tag; + token_attr_t attr; + MIR_var_t var; + int vararg_p = read_uint (ctx, "wrong vararg flag") != 0; + uint64_t i, nres = read_uint (ctx, "wrong func nres"); + + VARR_TRUNC (MIR_type_t, temp_types, 0); + for (i = 0; i < nres; i++) { + tag = read_token (ctx, &attr); + if (TAG_TI8 > tag || tag > TAG_TBLOCK) + (*error_func) (MIR_binary_io_error, "wrong prototype result type tag %d", tag); + VARR_PUSH (MIR_type_t, temp_types, tag_type (tag)); + } + VARR_TRUNC (MIR_var_t, temp_vars, 0); + for (;;) { + tag = read_token (ctx, &attr); + if (tag == TAG_EOI) break; + if (TAG_TI8 > tag || tag > TAG_TBLOCK) + (*error_func) (MIR_binary_io_error, "wrong prototype arg type tag %d", tag); + var.type = tag_type (tag); + var.name = read_name (ctx, module, "wrong arg name"); + VARR_PUSH (MIR_var_t, temp_vars, var); + } + *nres_ptr = nres; + return vararg_p; +} + +static size_t reduce_reader (void *start, size_t len, void *data) { + MIR_context_t ctx = data; + size_t i; + int c; + + for (i = 0; i < len && (c = io_reader (ctx)) != EOF; i++) ((char *) start)[i] = c; + return i; +} + +void MIR_read_with_func (MIR_context_t ctx, const int (*reader) (MIR_context_t)) { + int version; + bin_tag_t tag; + token_attr_t attr; + MIR_label_t lab; + uint64_t nstr, nres, u; + int64_t i; + MIR_op_t op; + size_t n, nop; + const char *name, *item_name; + MIR_module_t module; + MIR_item_t func, item; + + io_reader = reader; +#ifndef MIR_NO_BIN_COMPRESSION + if ((io_reduce_data = reduce_decode_start (reduce_reader, ctx)) == NULL) + (*error_func) (MIR_binary_io_error, "can not alloc data for MIR binary decompression"); +#endif + version = read_uint (ctx, "wrong header"); + if (version > CURR_BIN_VERSION) + (*error_func) (MIR_binary_io_error, "can not read version %d MIR binary: expected %d or less", + version, CURR_BIN_VERSION); + nstr = read_uint (ctx, "wrong header"); + read_all_strings (ctx, nstr); + module = NULL; + func = NULL; + for (;;) { + VARR_TRUNC (uint64_t, insn_label_string_nums, 0); + tag = read_token (ctx, &attr); + while (TAG_LAB1 <= tag && tag <= TAG_LAB4) { + VARR_PUSH (uint64_t, insn_label_string_nums, attr.u); + tag = read_token (ctx, &attr); + } + VARR_TRUNC (MIR_op_t, temp_insn_ops, 0); + if (TAG_NAME1 <= tag && tag <= TAG_NAME4) { + name = to_str (ctx, attr.u).s; + if (strcmp (name, "module") == 0) { + name = read_name (ctx, module, "wrong module name"); + if (VARR_LENGTH (uint64_t, insn_label_string_nums) != 0) + (*error_func) (MIR_binary_io_error, "insn label before module %s", name); + if (module != NULL) (*error_func) (MIR_binary_io_error, "nested module %s", name); + module = MIR_new_module (ctx, name); + } else if (strcmp (name, "endmodule") == 0) { + if (VARR_LENGTH (uint64_t, insn_label_string_nums) != 0) + (*error_func) (MIR_binary_io_error, "endmodule should have no labels"); + if (module == NULL) (*error_func) (MIR_binary_io_error, "endmodule without module"); + MIR_finish_module (ctx); + module = NULL; + } else if (strcmp (name, "proto") == 0) { + name = read_name (ctx, module, "wrong prototype name"); + if (VARR_LENGTH (uint64_t, insn_label_string_nums) != 0) + (*error_func) (MIR_binary_io_error, "insn label before proto %s", name); + if (module == NULL) + (*error_func) (MIR_binary_io_error, "prototype %s outside module", name); + if (func_proto_read (ctx, module, &nres)) + MIR_new_vararg_proto_arr (ctx, name, nres, VARR_ADDR (MIR_type_t, temp_types), + VARR_LENGTH (MIR_var_t, temp_vars), + VARR_ADDR (MIR_var_t, temp_vars)); + else + MIR_new_proto_arr (ctx, name, nres, VARR_ADDR (MIR_type_t, temp_types), + VARR_LENGTH (MIR_var_t, temp_vars), VARR_ADDR (MIR_var_t, temp_vars)); + } else if (strcmp (name, "func") == 0) { + name = read_name (ctx, module, "wrong func name"); + if (VARR_LENGTH (uint64_t, insn_label_string_nums) != 0) + (*error_func) (MIR_binary_io_error, "insn label before func %s", name); + if (func != NULL) (*error_func) (MIR_binary_io_error, "nested func %s", name); + if (module == NULL) (*error_func) (MIR_binary_io_error, "func %s outside module", name); + if (func_proto_read (ctx, module, &nres)) + func = MIR_new_vararg_func_arr (ctx, name, nres, VARR_ADDR (MIR_type_t, temp_types), + VARR_LENGTH (MIR_var_t, temp_vars), + VARR_ADDR (MIR_var_t, temp_vars)); + else + func = MIR_new_func_arr (ctx, name, nres, VARR_ADDR (MIR_type_t, temp_types), + VARR_LENGTH (MIR_var_t, temp_vars), + VARR_ADDR (MIR_var_t, temp_vars)); + VARR_TRUNC (MIR_label_t, func_labels, 0); + } else if (strcmp (name, "endfunc") == 0) { + if (VARR_LENGTH (uint64_t, insn_label_string_nums) != 0) + (*error_func) (MIR_binary_io_error, "endfunc should have no labels"); + if (func == NULL) (*error_func) (MIR_binary_io_error, "endfunc without func"); + MIR_finish_func (ctx); + func = NULL; + } else if (strcmp (name, "export") == 0) { + name = read_name (ctx, module, "wrong export name"); + if (VARR_LENGTH (uint64_t, insn_label_string_nums) != 0) + (*error_func) (MIR_binary_io_error, "export %s should have no labels", name); + MIR_new_export (ctx, name); + } else if (strcmp (name, "import") == 0) { + name = read_name (ctx, module, "wrong import name"); + if (VARR_LENGTH (uint64_t, insn_label_string_nums) != 0) + (*error_func) (MIR_binary_io_error, "import %s should have no labels", name); + MIR_new_import (ctx, name); + } else if (strcmp (name, "forward") == 0) { + name = read_name (ctx, module, "wrong forward name"); + if (VARR_LENGTH (uint64_t, insn_label_string_nums) != 0) + (*error_func) (MIR_binary_io_error, "forward %s should have no labels", name); + MIR_new_forward (ctx, name); + } else if (strcmp (name, "nbss") == 0 || strcmp (name, "bss") == 0) { + name = strcmp (name, "nbss") == 0 ? read_name (ctx, module, "wrong bss name") : NULL; + if (VARR_LENGTH (uint64_t, insn_label_string_nums) != 0) + (*error_func) (MIR_binary_io_error, "bss %s should have no labels", + name == NULL ? "" : name); + u = read_uint (ctx, "wrong bss len"); + MIR_new_bss (ctx, name, u); + } else if (strcmp (name, "nref") == 0 || strcmp (name, "ref") == 0) { + name = strcmp (name, "nref") == 0 ? read_name (ctx, module, "wrong ref data name") : NULL; + if (VARR_LENGTH (uint64_t, insn_label_string_nums) != 0) + (*error_func) (MIR_binary_io_error, "ref data %s should have no labels", + name == NULL ? "" : name); + item_name = read_name (ctx, module, "wrong ref data item name"); + if ((item = find_item (ctx, item_name, module)) == NULL) + (*error_func) (MIR_binary_io_error, "ref data refers to non-existing item %s", item_name); + i = read_int (ctx, "wrong ref disp"); + MIR_new_ref_data (ctx, name, item, i); + } else if (strcmp (name, "nexpr") == 0 || strcmp (name, "expr") == 0) { + name = strcmp (name, "nexpr") == 0 ? read_name (ctx, module, "wrong expr name") : NULL; + if (VARR_LENGTH (uint64_t, insn_label_string_nums) != 0) + (*error_func) (MIR_binary_io_error, "expr %s should have no labels", + name == NULL ? "" : name); + item_name = read_name (ctx, module, "wrong expr func name"); + if ((item = find_item (ctx, item_name, module)) == NULL || item->item_type != MIR_func_item) + (*error_func) (MIR_binary_io_error, "expr refers to non-function %s", item_name); + MIR_new_expr_data (ctx, name, item); + } else if (strcmp (name, "ndata") == 0 || strcmp (name, "data") == 0) { + MIR_type_t type; + size_t nel; + union { + uint8_t u8; + uint16_t u16; + uint32_t u32; + uint64_t u64; + int8_t i8; + int16_t i16; + int32_t i32; + int64_t i64; + } v; + + name = strcmp (name, "ndata") == 0 ? read_name (ctx, module, "wrong data name") : NULL; + if (VARR_LENGTH (uint64_t, insn_label_string_nums) != 0) + (*error_func) (MIR_binary_io_error, "data %s should have no labels", + name == NULL ? "" : name); + tag = read_token (ctx, &attr); + if (TAG_TI8 > tag || tag > TAG_TBLOCK) + (*error_func) (MIR_binary_io_error, "wrong data type tag %d", tag); + type = tag_type (tag); + VARR_TRUNC (uint8_t, temp_data, 0); + for (nel = 0;; nel++) { + tag = read_token (ctx, &attr); + if (tag == TAG_EOI) break; + switch (tag) { + case TAG_U0: + case TAG_U1: + case TAG_U2: + case TAG_U3: + case TAG_U4: + case TAG_U5: + case TAG_U6: + case TAG_U7: + case TAG_U8: + switch (type) { + case MIR_T_U8: + v.u8 = attr.u; + push_data (ctx, &v.u8, sizeof (uint8_t)); + break; + case MIR_T_U16: + v.u16 = attr.u; + push_data (ctx, (uint8_t *) &v.u16, sizeof (uint16_t)); + break; + case MIR_T_U32: + v.u32 = attr.u; + push_data (ctx, (uint8_t *) &v.u32, sizeof (uint32_t)); + break; + case MIR_T_U64: + v.u64 = attr.u; + push_data (ctx, (uint8_t *) &v.i64, sizeof (uint64_t)); + break; + default: + (*error_func) (MIR_binary_io_error, "data type %s does not correspond value type", + type_str (type)); + } + break; + case TAG_I1: + case TAG_I2: + case TAG_I3: + case TAG_I4: + case TAG_I5: + case TAG_I6: + case TAG_I7: + case TAG_I8: + switch (type) { + case MIR_T_I8: + v.i8 = attr.i; + push_data (ctx, (uint8_t *) &v.i8, sizeof (int8_t)); + break; + case MIR_T_I16: + v.i16 = attr.i; + push_data (ctx, (uint8_t *) &v.i16, sizeof (int16_t)); + break; + case MIR_T_I32: + v.i32 = attr.i; + push_data (ctx, (uint8_t *) &v.i32, sizeof (int32_t)); + break; + case MIR_T_I64: + v.i64 = attr.i; + push_data (ctx, (uint8_t *) &v.i64, sizeof (int64_t)); + break; + default: + (*error_func) (MIR_binary_io_error, "data type %s does not correspond value type", + type_str (type)); + } + break; + case TAG_F: + if (type != MIR_T_F) + (*error_func) (MIR_binary_io_error, "data type %s does not correspond value type", + type_str (type)); + push_data (ctx, (uint8_t *) &attr.f, sizeof (float)); + break; + case TAG_D: + if (type != MIR_T_D) + (*error_func) (MIR_binary_io_error, "data type %s does not correspond value type", + type_str (type)); + push_data (ctx, (uint8_t *) &attr.d, sizeof (double)); + break; + case TAG_LD: + if (type != MIR_T_LD) + (*error_func) (MIR_binary_io_error, "data type %s does not correspond value type", + type_str (type)); + push_data (ctx, (uint8_t *) &attr.ld, sizeof (long double)); + break; + /* ??? ptr */ + default: (*error_func) (MIR_binary_io_error, "wrong data value tag %d", tag); + } + } + MIR_new_data (ctx, name, type, + VARR_LENGTH (uint8_t, temp_data) / _MIR_type_size (ctx, type), + VARR_ADDR (uint8_t, temp_data)); + } else if (strcmp (name, "local") == 0) { + if (func == NULL) (*error_func) (MIR_binary_io_error, "local outside func"); + if (VARR_LENGTH (uint64_t, insn_label_string_nums) != 0) + (*error_func) (MIR_binary_io_error, "local should have no labels"); + for (;;) { + tag = read_token (ctx, &attr); + if (tag == TAG_EOI) break; + if (TAG_TI8 > tag || tag > TAG_TBLOCK) + (*error_func) (MIR_binary_io_error, "wrong local var type tag %d", tag); + MIR_new_func_reg (ctx, func->u.func, tag_type (tag), + read_name (ctx, module, "wrong local var name")); + } + } else { + (*error_func) (MIR_binary_io_error, "unknown insn name %s", name); + } + } else if (TAG_U0 <= tag && tag <= TAG_U8) { /* insn code */ + MIR_insn_code_t insn_code = attr.u; + + if (insn_code >= MIR_LABEL) + (*error_func) (MIR_binary_io_error, "wrong insn code %d", insn_code); + for (uint64_t i = 0; i < VARR_LENGTH (uint64_t, insn_label_string_nums); i++) { + lab = to_lab (ctx, VARR_GET (uint64_t, insn_label_string_nums, i)); + MIR_append_insn (ctx, func, lab); + } + nop = insn_code_nops (ctx, insn_code); + mir_assert (nop != 0 || MIR_call_code_p (insn_code) || insn_code == MIR_RET + || insn_code == MIR_SWITCH); + for (n = 0; (nop == 0 || n < nop) && read_operand (ctx, &op, func); n++) + VARR_PUSH (MIR_op_t, temp_insn_ops, op); + if (nop != 0 && n < nop) + (*error_func) (MIR_binary_io_error, "wrong number of operands of insn %s", + insn_name (insn_code)); + MIR_append_insn (ctx, func, + MIR_new_insn_arr (ctx, insn_code, n, VARR_ADDR (MIR_op_t, temp_insn_ops))); + } else if (tag == TAG_EOFILE) { + break; + } else { + (*error_func) (MIR_binary_io_error, "wrong token %d", tag); + } + } + if (func != NULL) (*error_func) (MIR_binary_io_error, "unfinished func %s", func->u.func->name); + if (module != NULL) (*error_func) (MIR_binary_io_error, "unfinished module %s", module->name); + if (reader (ctx) != EOF) (*error_func) (MIR_binary_io_error, "garbage at the end of file"); +#ifndef MIR_NO_BIN_COMPRESSION + reduce_decode_finish (io_reduce_data); +#endif +} + +static int file_reader (MIR_context_t ctx) { return fgetc (io_file); } + +void MIR_read (MIR_context_t ctx, FILE *f) { + io_file = f; + MIR_read_with_func (ctx, file_reader); +} + +static void io_init (MIR_context_t ctx) { + if ((ctx->io_ctx = malloc (sizeof (struct io_ctx))) == NULL) + (*error_func) (MIR_alloc_error, "Not enough memory for ctx"); + VARR_CREATE (MIR_str_t, bin_strings, 512); + VARR_CREATE (uint64_t, insn_label_string_nums, 64); + VARR_CREATE (MIR_label_t, func_labels, 512); +} + +static void io_finish (MIR_context_t ctx) { + VARR_DESTROY (MIR_label_t, func_labels); + VARR_DESTROY (uint64_t, insn_label_string_nums); + VARR_DESTROY (MIR_str_t, bin_strings); + free (ctx->io_ctx); + ctx->io_ctx = NULL; +} + +#endif /* if !MIR_NO_IO */ + +/* New Page */ + +/* Reading MIR text file */ + +#if !MIR_NO_SCAN + +#include +#include +#include +#include +#include + +typedef struct insn_name { + const char *name; + MIR_insn_code_t code; +} insn_name_t; + +static int insn_name_eq (insn_name_t in1, insn_name_t in2) { + return strcmp (in1.name, in2.name) == 0; +} +static htab_hash_t insn_name_hash (insn_name_t in) { + return mir_hash (in.name, strlen (in.name), 0); +} + +#define TC_EL(t) TC_##t +#define REP_SEP , +enum token_code { + REP8 (TC_EL, INT, FLOAT, DOUBLE, LDOUBLE, NAME, STR, NL, EOFILE), + REP5 (TC_EL, LEFT_PAR, RIGHT_PAR, COMMA, SEMICOL, COL), +}; +#undef REP_SEP + +typedef struct token { + enum token_code code; + union { + int64_t i; + float f; + double d; + long double ld; + const char *name; + MIR_str_t str; + } u; +} token_t; + +DEF_HTAB (insn_name_t); +typedef const char *label_name_t; +DEF_VARR (label_name_t); + +typedef struct label_desc { + const char *name; + MIR_label_t label; +} label_desc_t; + +DEF_HTAB (label_desc_t); + +struct scan_ctx { + jmp_buf error_jmp_buf; + size_t curr_lno; + HTAB (insn_name_t) * insn_name_tab; + const char *input_string; + size_t input_string_char_num; + VARR (label_name_t) * label_names; + HTAB (label_desc_t) * label_desc_tab; +}; + +#define error_jmp_buf ctx->scan_ctx->error_jmp_buf +#define curr_lno ctx->scan_ctx->curr_lno +#define insn_name_tab ctx->scan_ctx->insn_name_tab +#define input_string ctx->scan_ctx->input_string +#define input_string_char_num ctx->scan_ctx->input_string_char_num +#define label_names ctx->scan_ctx->label_names +#define label_desc_tab ctx->scan_ctx->label_desc_tab + +static void MIR_NO_RETURN process_error (MIR_context_t ctx, enum MIR_error_type error_type, + const char *message) { + (*error_func) (error_type, "ln %lu: %s", (unsigned long) curr_lno, message); + longjmp (error_jmp_buf, TRUE); +} + +/* Read number using GET_CHAR and UNGET_CHAR and already read + character CH. It should be guaranted that the input has a righ + prefix (+|-)?[0-9]. Return base, float and double flag through + BASE, FLOAT_P, DOUBLE_P. Put number representation (0x or 0X + prefix is removed) into TEMP_STRING. */ +static void scan_number (MIR_context_t ctx, int ch, int get_char (MIR_context_t), + void unget_char (MIR_context_t, int), int *base, int *float_p, + int *double_p, int *ldouble_p) { + enum scan_number_code { NUMBER_OK, ABSENT_EXPONENT, NON_DECIMAL_FLOAT, WRONG_OCTAL_INT }; + enum scan_number_code err_code = NUMBER_OK; + int dec_p, hex_p, hex_char_p; + + *base = 10; + *ldouble_p = *double_p = *float_p = FALSE; + if (ch == '+' || ch == '-') { + VARR_PUSH (char, temp_string, ch); + ch = get_char (ctx); + } + mir_assert ('0' <= ch && ch <= '9'); + if (ch == '0') { + ch = get_char (ctx); + if (ch != 'x' && ch != 'X') { + *base = 8; + unget_char (ctx, ch); + ch = '0'; + } else { + ch = get_char (ctx); + *base = 16; + } + } + dec_p = hex_p = FALSE; + for (;;) { + if (ch != '_') VARR_PUSH (char, temp_string, ch); + ch = get_char (ctx); + if (ch == '8' || ch == '9') dec_p = TRUE; + hex_char_p = (('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F')); + if (ch != '_' && !isdigit (ch) && (*base != 16 || !hex_char_p)) break; + if (hex_char_p) hex_p = TRUE; + } + mir_assert (*base == 16 || !hex_p); + if (ch == '.') { + *double_p = TRUE; + do { + if (ch != '_') VARR_PUSH (char, temp_string, ch); + ch = get_char (ctx); + } while (isdigit (ch) || ch == '_'); + } + if (ch == 'e' || ch == 'E') { + *double_p = TRUE; + ch = get_char (ctx); + if (ch != '+' && ch != '-' && !isdigit (ch)) + err_code = ABSENT_EXPONENT; + else { + VARR_PUSH (char, temp_string, 'e'); + if (ch == '+' || ch == '-') { + VARR_PUSH (char, temp_string, ch); + ch = get_char (ctx); + if (!isdigit (ch)) err_code = ABSENT_EXPONENT; + } + if (err_code == NUMBER_OK) do { + if (ch != '_') VARR_PUSH (char, temp_string, ch); + ch = get_char (ctx); + } while (isdigit (ch) || ch == '_'); + } + } + if (*double_p) { + if (*base == 16) + err_code = NON_DECIMAL_FLOAT; + else if (ch == 'f' || ch == 'F') { + *float_p = TRUE; + *double_p = FALSE; + ch = get_char (ctx); + } else if (ch == 'l' || ch == 'L') { + *ldouble_p = TRUE; + *double_p = FALSE; + ch = get_char (ctx); + } + } else if (*base == 8 && dec_p) + err_code = WRONG_OCTAL_INT; + VARR_PUSH (char, temp_string, '\0'); + unget_char (ctx, ch); +} + +static void scan_string (MIR_context_t ctx, token_t *t, int c, int get_char (MIR_context_t), + void unget_char (MIR_context_t, int)) { + int ch_code; + + mir_assert (c == '\"'); + VARR_TRUNC (char, temp_string, 0); + for (;;) { + if ((c = get_char (ctx)) == EOF || c == '\n') + process_error (ctx, MIR_syntax_error, "unfinished string"); + if (c == '"') break; + if (c == '\\') { + if ((c = get_char (ctx)) == 'n') + c = '\n'; + else if (c == 't') + c = '\t'; + else if (c == 'v') + c = '\v'; + else if (c == 'a') + c = '\a'; + else if (c == 'b') + c = '\b'; + else if (c == 'r') + c = '\r'; + else if (c == 'f') + c = '\f'; + else if (c == '\\' || c == '\'' || c == '\"') + ; + else if (c == '\n') { + curr_lno++; + continue; + } else if (isdigit (c) && c != '8' && c != '9') { + ch_code = c - '0'; + c = get_char (ctx); + if (!isdigit (c) || c == '8' || c == '9') + unget_char (ctx, c); + else { + ch_code = ch_code * 8 + c - '0'; + c = get_char (ctx); + if (!isdigit (c) || c == '8' || c == '9') + unget_char (ctx, c); + else + ch_code = ch_code * 8 + c - '0'; + } + c = ch_code; + } else if (c == 'x') { + /* Hex escape code. */ + ch_code = 0; + for (int i = 2; i > 0; i--) { + c = get_char (ctx); + if (!isxdigit (c)) process_error (ctx, MIR_syntax_error, "wrong hexadecimal escape"); + c = '0' <= c && c <= '9' ? c - '0' : 'a' <= c && c <= 'f' ? c - 'a' + 10 : c - 'A' + 10; + ch_code = (ch_code << 4) | c; + } + c = ch_code; + } + } + VARR_PUSH (char, temp_string, c); + } + if (VARR_LENGTH (char, temp_string) > 0 && VARR_LAST (char, temp_string) != 0) + VARR_PUSH (char, temp_string, 0); + t->code = TC_STR; + t->u.str + = string_store (ctx, &strings, &string_tab, + (MIR_str_t){VARR_LENGTH (char, temp_string), VARR_ADDR (char, temp_string)}) + .str; +} + +static int get_string_char (MIR_context_t ctx) { + int ch = input_string[input_string_char_num]; + + if (ch == '\0') return EOF; + input_string_char_num++; + if (ch == '\n') curr_lno++; + return ch; +} + +static void unget_string_char (MIR_context_t ctx, int ch) { + if (input_string_char_num == 0 || ch == EOF) return; + input_string_char_num--; + mir_assert (input_string[input_string_char_num] == ch); + if (ch == '\n') curr_lno--; +} + +static void scan_token (MIR_context_t ctx, token_t *token, int (*get_char) (MIR_context_t), + void (*unget_char) (MIR_context_t, int)) { + int ch; + + for (;;) { + ch = get_char (ctx); + switch (ch) { + case EOF: token->code = TC_EOFILE; return; + case ' ': + case '\t': break; + case '#': + while ((ch = get_char (ctx)) != '\n' && ch != EOF) + ; + /* Fall through: */ + case '\n': token->code = TC_NL; return; + case '(': token->code = TC_LEFT_PAR; return; + case ')': token->code = TC_RIGHT_PAR; return; + case ',': token->code = TC_COMMA; return; + case ';': token->code = TC_SEMICOL; return; + case ':': token->code = TC_COL; return; + case '"': scan_string (ctx, token, ch, get_char, unget_char); return; + default: + VARR_TRUNC (char, temp_string, 0); + if (isalpha (ch) || ch == '_' || ch == '$' || ch == '%' || ch == '.') { + do { + VARR_PUSH (char, temp_string, ch); + ch = get_char (ctx); + } while (isalpha (ch) || isdigit (ch) || ch == '_' || ch == '$' || ch == '%' || ch == '.'); + VARR_PUSH (char, temp_string, '\0'); + unget_char (ctx, ch); + token->u.name = _MIR_uniq_string (ctx, VARR_ADDR (char, temp_string)); + token->code = TC_NAME; + return; + } else if (ch == '+' || ch == '-' || isdigit (ch)) { + const char *repr; + char *end; + int next_ch, base, float_p, double_p, ldouble_p; + + if (ch == '+' || ch == '-') { + next_ch = get_char (ctx); + if (!isdigit (next_ch)) process_error (ctx, MIR_syntax_error, "no number after a sign"); + unget_char (ctx, next_ch); + } + scan_number (ctx, ch, get_char, unget_char, &base, &float_p, &double_p, &ldouble_p); + repr = VARR_ADDR (char, temp_string); + errno = 0; + if (float_p) { + token->code = TC_FLOAT; + token->u.f = strtof (repr, &end); + } else if (double_p) { + token->code = TC_DOUBLE; + token->u.d = strtod (repr, &end); + } else if (ldouble_p) { + token->code = TC_LDOUBLE; + token->u.ld = strtold (repr, &end); + } else { + token->code = TC_INT; + token->u.i = (sizeof (long) == sizeof (int64_t) ? strtoul (repr, &end, base) + : strtoull (repr, &end, base)); + } + mir_assert (*end == '\0'); + if (errno != 0) + ; + return; + } else { + process_error (ctx, MIR_syntax_error, "wrong char"); + } + } + } +} + +static int label_eq (label_desc_t l1, label_desc_t l2) { return strcmp (l1.name, l2.name) == 0; } +static htab_hash_t label_hash (label_desc_t l) { return mir_hash (l.name, strlen (l.name), 0); } + +static MIR_label_t create_label_desc (MIR_context_t ctx, const char *name) { + MIR_label_t label; + label_desc_t label_desc; + + label_desc.name = name; + if (HTAB_DO (label_desc_t, label_desc_tab, label_desc, HTAB_FIND, label_desc)) { + label = label_desc.label; + } else { + label_desc.label = label = MIR_new_label (ctx); + HTAB_DO (label_desc_t, label_desc_tab, label_desc, HTAB_INSERT, label_desc); + } + return label; +} + +MIR_type_t MIR_str2type (MIR_context_t ctx, const char *type_name) { + if (strcmp (type_name, "i64") == 0) return MIR_T_I64; + if (strcmp (type_name, "u64") == 0) return MIR_T_U64; + if (strcmp (type_name, "f") == 0) return MIR_T_F; + if (strcmp (type_name, "d") == 0) return MIR_T_D; + if (strcmp (type_name, "ld") == 0) return MIR_T_LD; + if (strcmp (type_name, "p") == 0) return MIR_T_P; + if (strcmp (type_name, "i32") == 0) return MIR_T_I32; + if (strcmp (type_name, "u32") == 0) return MIR_T_U32; + if (strcmp (type_name, "i16") == 0) return MIR_T_I16; + if (strcmp (type_name, "u16") == 0) return MIR_T_U16; + if (strcmp (type_name, "i8") == 0) return MIR_T_I8; + if (strcmp (type_name, "u8") == 0) return MIR_T_U8; + return MIR_T_BOUND; +} + +static void read_func_proto (MIR_context_t ctx, size_t nops, MIR_op_t *ops) { + MIR_var_t var; + size_t i; + + VARR_TRUNC (MIR_type_t, temp_types, 0); + for (i = 0; i < nops; i++) { + var.name = (const char *) ops[i].u.mem.disp; + if ((var.name = (const char *) ops[i].u.mem.disp) != NULL) break; + var.type = ops[i].u.mem.type; + VARR_PUSH (MIR_type_t, temp_types, var.type); + } + VARR_TRUNC (MIR_var_t, temp_vars, 0); + for (; i < nops; i++) { + if (ops[i].mode != MIR_OP_MEM) + process_error (ctx, MIR_syntax_error, "wrong prototype/func arg"); + var.type = ops[i].u.mem.type; + var.name = (const char *) ops[i].u.mem.disp; + if (var.name == NULL) + process_error (ctx, MIR_syntax_error, "all func/prototype args should have type:name form"); + VARR_PUSH (MIR_var_t, temp_vars, var); + } +} + +/* Syntax: + program: { insn / sep } + sep : ';' | NL + insn : {label ':'}* [ code [ {op / ','} ] ] + label : name + code : name + op : name | int | float | double | long double | mem | str + mem : type ':' addr + addr : disp + | [ disp ] '(' sib ')' + sib : name | [ name ] ',' name [ ',' scale] + disp : int | name + scale : int + +*/ + +void MIR_scan_string (MIR_context_t ctx, const char *str) { + token_t t; + const char *name; + MIR_module_t module = NULL; + MIR_item_t item, func = NULL; + MIR_insn_code_t insn_code; + MIR_insn_t insn; + MIR_type_t type, data_type = MIR_T_BOUND; + MIR_op_t op, *op_addr; + MIR_label_t label; + size_t n; + int64_t i; + int module_p, end_module_p, proto_p, func_p, end_func_p, dots_p, export_p, import_p, forward_p; + int bss_p, ref_p, expr_p, string_p, local_p, push_op_p, read_p, disp_p; + insn_name_t in, el; + + curr_lno = 1; + input_string = str; + input_string_char_num = 0; + t.code = TC_NL; + for (;;) { + if (setjmp (error_jmp_buf)) { + while (t.code != TC_NL && t.code != EOF) + scan_token (ctx, &t, get_string_char, unget_string_char); + if (t.code == TC_EOFILE) break; + } + VARR_TRUNC (label_name_t, label_names, 0); + scan_token (ctx, &t, get_string_char, unget_string_char); + while (t.code == TC_NL) scan_token (ctx, &t, get_string_char, unget_string_char); + if (t.code == TC_EOFILE) break; + for (;;) { /* label_names */ + if (t.code != TC_NAME) + process_error (ctx, MIR_syntax_error, "insn should start with label or insn name"); + name = t.u.name; + scan_token (ctx, &t, get_string_char, unget_string_char); + if (t.code != TC_COL) break; + VARR_PUSH (label_name_t, label_names, name); + scan_token (ctx, &t, get_string_char, unget_string_char); + if (t.code == TC_NL) + scan_token (ctx, &t, get_string_char, unget_string_char); /* label_names without insn */ + } + module_p = end_module_p = proto_p = func_p = end_func_p = FALSE; + export_p = import_p = forward_p = bss_p = ref_p = expr_p = string_p = local_p = FALSE; + if (strcmp (name, "module") == 0) { + module_p = TRUE; + if (VARR_LENGTH (label_name_t, label_names) != 1) + process_error (ctx, MIR_syntax_error, "only one label should be used for module"); + } else if (strcmp (name, "endmodule") == 0) { + end_module_p = TRUE; + if (VARR_LENGTH (label_name_t, label_names) != 0) + process_error (ctx, MIR_syntax_error, "endmodule should have no labels"); + } else if (strcmp (name, "proto") == 0) { + proto_p = TRUE; + if (VARR_LENGTH (label_name_t, label_names) != 1) + process_error (ctx, MIR_syntax_error, "only one label should be used for proto"); + } else if (strcmp (name, "func") == 0) { + func_p = TRUE; + if (VARR_LENGTH (label_name_t, label_names) != 1) + process_error (ctx, MIR_syntax_error, "only one label should be used for func"); + } else if (strcmp (name, "endfunc") == 0) { + end_func_p = TRUE; + if (VARR_LENGTH (label_name_t, label_names) != 0) + process_error (ctx, MIR_syntax_error, "endfunc should have no labels"); + } else if (strcmp (name, "export") == 0) { + export_p = TRUE; + if (VARR_LENGTH (label_name_t, label_names) != 0) + process_error (ctx, MIR_syntax_error, "export should have no labels"); + } else if (strcmp (name, "import") == 0) { + import_p = TRUE; + if (VARR_LENGTH (label_name_t, label_names) != 0) + process_error (ctx, MIR_syntax_error, "import should have no labels"); + } else if (strcmp (name, "forward") == 0) { + forward_p = TRUE; + if (VARR_LENGTH (label_name_t, label_names) != 0) + process_error (ctx, MIR_syntax_error, "forward should have no labels"); + } else if (strcmp (name, "bss") == 0) { + bss_p = TRUE; + if (VARR_LENGTH (label_name_t, label_names) > 1) + process_error (ctx, MIR_syntax_error, "at most one label should be used for bss"); + } else if (strcmp (name, "ref") == 0) { + ref_p = TRUE; + if (VARR_LENGTH (label_name_t, label_names) > 1) + process_error (ctx, MIR_syntax_error, "at most one label should be used for ref"); + } else if (strcmp (name, "expr") == 0) { + expr_p = TRUE; + if (VARR_LENGTH (label_name_t, label_names) > 1) + process_error (ctx, MIR_syntax_error, "at most one label should be used for expr"); + } else if (strcmp (name, "string") == 0) { + string_p = TRUE; + if (VARR_LENGTH (label_name_t, label_names) > 1) + process_error (ctx, MIR_syntax_error, "at most one label should be used for string"); + } else if (strcmp (name, "local") == 0) { + local_p = TRUE; + if (func == NULL) process_error (ctx, MIR_syntax_error, "local outside func"); + if (VARR_LENGTH (label_name_t, label_names) != 0) + process_error (ctx, MIR_syntax_error, "local should have no labels"); + } else if ((data_type = MIR_str2type (ctx, name)) != MIR_T_BOUND) { + if (VARR_LENGTH (label_name_t, label_names) > 1) + process_error (ctx, MIR_syntax_error, "at most one label should be used for data"); + } else { + in.name = name; + if (!HTAB_DO (insn_name_t, insn_name_tab, in, HTAB_FIND, el)) + process_error (ctx, MIR_syntax_error, "Unknown insn"); + insn_code = el.code; + for (n = 0; n < VARR_LENGTH (label_name_t, label_names); n++) { + label = create_label_desc (ctx, VARR_GET (label_name_t, label_names, n)); + if (func != NULL) MIR_append_insn (ctx, func, label); + } + } + VARR_TRUNC (MIR_op_t, temp_insn_ops, 0); + dots_p = FALSE; + for (;;) { /* ops */ + if (t.code == TC_NL || t.code == TC_SEMICOL) { + /* insn end */ + break; + } + push_op_p = read_p = TRUE; + switch (t.code) { + case TC_NAME: { + name = t.u.name; + scan_token (ctx, &t, get_string_char, unget_string_char); + if ((func_p || proto_p) && strcmp (name, "...") == 0) { + dots_p = TRUE; + break; + } + read_p = FALSE; + if (t.code != TC_COL && !proto_p && !func_p && !local_p) { + if (export_p) { + MIR_new_export (ctx, name); + push_op_p = FALSE; + } else if (import_p) { + MIR_new_import (ctx, name); + push_op_p = FALSE; + } else if (forward_p) { + MIR_new_forward (ctx, name); + push_op_p = FALSE; + } else if (!module_p && !end_module_p && !proto_p && !func_p && !end_func_p && !local_p + && ((MIR_branch_code_p (insn_code) + && VARR_LENGTH (MIR_op_t, temp_insn_ops) == 0) + || (insn_code == MIR_SWITCH + && VARR_LENGTH (MIR_op_t, temp_insn_ops) > 0))) { + op = MIR_new_label_op (ctx, create_label_desc (ctx, name)); + } else if (!expr_p && !ref_p && func_reg_p (ctx, func->u.func, name)) { + op.mode = MIR_OP_REG; + op.u.reg = MIR_reg (ctx, name, func->u.func); + } else if ((item = find_item (ctx, name, module)) != NULL) { + op = MIR_new_ref_op (ctx, item); + } else { + process_error (ctx, MIR_syntax_error, "undeclared name"); + } + break; + } + /* Memory, type only, arg, or var */ + type = MIR_str2type (ctx, name); + if (type == MIR_T_BOUND) + process_error (ctx, MIR_syntax_error, "Unknown type"); + else if (local_p && type != MIR_T_I64 && type != MIR_T_F && type != MIR_T_D + && type != MIR_T_LD) + process_error (ctx, MIR_syntax_error, "wrong type for local var"); + op = MIR_new_mem_op (ctx, type, 0, 0, 0, 1); + if (proto_p || func_p || local_p) { + if (t.code == TC_COL) { + scan_token (ctx, &t, get_string_char, unget_string_char); + if (t.code != TC_NAME) + process_error (ctx, MIR_syntax_error, func_p ? "wrong arg" : "wrong local var"); + op.u.mem.disp = (MIR_disp_t) t.u.name; + scan_token (ctx, &t, get_string_char, unget_string_char); + } + } else { + scan_token (ctx, &t, get_string_char, unget_string_char); + disp_p = FALSE; + if (t.code == TC_INT) { + op.u.mem.disp = t.u.i; + scan_token (ctx, &t, get_string_char, unget_string_char); + disp_p = TRUE; + } else if (t.code == TC_NAME) { + op.u.mem.disp = (MIR_disp_t) t.u.name; + scan_token (ctx, &t, get_string_char, unget_string_char); + disp_p = TRUE; + } + if (t.code == TC_LEFT_PAR) { + scan_token (ctx, &t, get_string_char, unget_string_char); + if (t.code == TC_NAME) { + op.u.mem.base = MIR_reg (ctx, t.u.name, func->u.func); + scan_token (ctx, &t, get_string_char, unget_string_char); + } + if (t.code == TC_COMMA) { + scan_token (ctx, &t, get_string_char, unget_string_char); + if (t.code != TC_NAME) process_error (ctx, MIR_syntax_error, "wrong index"); + op.u.mem.index = MIR_reg (ctx, t.u.name, func->u.func); + scan_token (ctx, &t, get_string_char, unget_string_char); + if (t.code == TC_COMMA) { + scan_token (ctx, &t, get_string_char, unget_string_char); + if (t.code != TC_INT) process_error (ctx, MIR_syntax_error, "wrong scale"); + op.u.mem.scale = t.u.i; + scan_token (ctx, &t, get_string_char, unget_string_char); + } + } + if (t.code != TC_RIGHT_PAR) process_error (ctx, MIR_syntax_error, "wrong memory op"); + scan_token (ctx, &t, get_string_char, unget_string_char); + } else if (!disp_p) + process_error (ctx, MIR_syntax_error, "wrong memory"); + } + break; + } + case TC_INT: + op.mode = MIR_OP_INT; + op.u.i = t.u.i; + break; + case TC_FLOAT: + op.mode = MIR_OP_FLOAT; + op.u.f = t.u.f; + break; + case TC_DOUBLE: + op.mode = MIR_OP_DOUBLE; + op.u.d = t.u.d; + break; + case TC_LDOUBLE: + op.mode = MIR_OP_LDOUBLE; + op.u.ld = t.u.ld; + break; + case TC_STR: + op.mode = MIR_OP_STR; + op.u.str = t.u.str; + break; + default: break; + } + if (dots_p) break; + if (push_op_p) VARR_PUSH (MIR_op_t, temp_insn_ops, op); + if (read_p) scan_token (ctx, &t, get_string_char, unget_string_char); + if (t.code != TC_COMMA) break; + scan_token (ctx, &t, get_string_char, unget_string_char); + } + if (t.code != TC_NL && t.code != TC_EOFILE && t.code != TC_SEMICOL) + process_error (ctx, MIR_syntax_error, "wrong insn end"); + if (module_p) { + if (module != NULL) process_error (ctx, MIR_syntax_error, "nested module"); + if (VARR_LENGTH (MIR_op_t, temp_insn_ops) != 0) + process_error (ctx, MIR_syntax_error, "module should have no params"); + module = MIR_new_module (ctx, VARR_GET (label_name_t, label_names, 0)); + } else if (end_module_p) { + if (module == NULL) process_error (ctx, MIR_syntax_error, "standalone endmodule"); + if (VARR_LENGTH (MIR_op_t, temp_insn_ops) != 0) + process_error (ctx, MIR_syntax_error, "endmodule should have no params"); + MIR_finish_module (ctx); + module = NULL; + } else if (bss_p) { + if (VARR_LENGTH (MIR_op_t, temp_insn_ops) != 1) + process_error (ctx, MIR_syntax_error, "bss should have one operand"); + op_addr = VARR_ADDR (MIR_op_t, temp_insn_ops); + if (op_addr[0].mode != MIR_OP_INT || op_addr[0].u.i < 0) + process_error (ctx, MIR_syntax_error, "wrong bss operand type or value"); + name + = (VARR_LENGTH (label_name_t, label_names) == 0 ? NULL + : VARR_GET (label_name_t, label_names, 0)); + MIR_new_bss (ctx, name, op_addr[0].u.i); + } else if (ref_p) { + if (VARR_LENGTH (MIR_op_t, temp_insn_ops) != 2) + process_error (ctx, MIR_syntax_error, "ref should have two operands"); + op_addr = VARR_ADDR (MIR_op_t, temp_insn_ops); + if (op_addr[0].mode != MIR_OP_REF) process_error (ctx, MIR_syntax_error, "wrong ref operand"); + if (op_addr[1].mode != MIR_OP_INT) + process_error (ctx, MIR_syntax_error, "wrong ref disp operand"); + name + = (VARR_LENGTH (label_name_t, label_names) == 0 ? NULL + : VARR_GET (label_name_t, label_names, 0)); + MIR_new_ref_data (ctx, name, op_addr[0].u.ref, op_addr[1].u.i); + } else if (expr_p) { + if (VARR_LENGTH (MIR_op_t, temp_insn_ops) != 1) + process_error (ctx, MIR_syntax_error, "expr should have one operand"); + op_addr = VARR_ADDR (MIR_op_t, temp_insn_ops); + if (op_addr[0].mode != MIR_OP_REF || op_addr[0].u.ref->item_type != MIR_func_item) + process_error (ctx, MIR_syntax_error, "wrong expr operand"); + name + = (VARR_LENGTH (label_name_t, label_names) == 0 ? NULL + : VARR_GET (label_name_t, label_names, 0)); + MIR_new_expr_data (ctx, name, op_addr[0].u.ref); + } else if (string_p) { + if (VARR_LENGTH (MIR_op_t, temp_insn_ops) != 1) + process_error (ctx, MIR_syntax_error, "string should have one operand"); + op_addr = VARR_ADDR (MIR_op_t, temp_insn_ops); + if (op_addr[0].mode != MIR_OP_STR) + process_error (ctx, MIR_syntax_error, "wrong string data operand type"); + name + = (VARR_LENGTH (label_name_t, label_names) == 0 ? NULL + : VARR_GET (label_name_t, label_names, 0)); + MIR_new_string_data (ctx, name, op_addr[0].u.str); + } else if (proto_p) { + if (module == NULL) process_error (ctx, MIR_syntax_error, "prototype outside module"); + read_func_proto (ctx, VARR_LENGTH (MIR_op_t, temp_insn_ops), + VARR_ADDR (MIR_op_t, temp_insn_ops)); + if (dots_p) + MIR_new_vararg_proto_arr (ctx, VARR_GET (label_name_t, label_names, 0), + VARR_LENGTH (MIR_type_t, temp_types), + VARR_ADDR (MIR_type_t, temp_types), + VARR_LENGTH (MIR_var_t, temp_vars), + VARR_ADDR (MIR_var_t, temp_vars)); + else + MIR_new_proto_arr (ctx, VARR_GET (label_name_t, label_names, 0), + VARR_LENGTH (MIR_type_t, temp_types), VARR_ADDR (MIR_type_t, temp_types), + VARR_LENGTH (MIR_var_t, temp_vars), VARR_ADDR (MIR_var_t, temp_vars)); + } else if (func_p) { + if (module == NULL) process_error (ctx, MIR_syntax_error, "func outside module"); + if (func != NULL) process_error (ctx, MIR_syntax_error, "nested func"); + read_func_proto (ctx, VARR_LENGTH (MIR_op_t, temp_insn_ops), + VARR_ADDR (MIR_op_t, temp_insn_ops)); + if (dots_p) + func = MIR_new_vararg_func_arr (ctx, VARR_GET (label_name_t, label_names, 0), + VARR_LENGTH (MIR_type_t, temp_types), + VARR_ADDR (MIR_type_t, temp_types), + VARR_LENGTH (MIR_var_t, temp_vars), + VARR_ADDR (MIR_var_t, temp_vars)); + else + func + = MIR_new_func_arr (ctx, VARR_GET (label_name_t, label_names, 0), + VARR_LENGTH (MIR_type_t, temp_types), + VARR_ADDR (MIR_type_t, temp_types), + VARR_LENGTH (MIR_var_t, temp_vars), VARR_ADDR (MIR_var_t, temp_vars)); + HTAB_CLEAR (label_desc_t, label_desc_tab); + } else if (end_func_p) { + if (func == NULL) process_error (ctx, MIR_syntax_error, "standalone endfunc"); + if (VARR_LENGTH (MIR_op_t, temp_insn_ops) != 0) + process_error (ctx, MIR_syntax_error, "endfunc should have no params"); + func = NULL; + MIR_finish_func (ctx); + } else if (export_p || import_p || forward_p) { /* we already created items, now do nothing: */ + mir_assert (VARR_LENGTH (MIR_op_t, temp_insn_ops) == 0); + } else if (local_p) { + op_addr = VARR_ADDR (MIR_op_t, temp_insn_ops); + n = VARR_LENGTH (MIR_op_t, temp_insn_ops); + for (i = 0; i < n; i++) { + if (op_addr[i].mode != MIR_OP_MEM || (const char *) op_addr[i].u.mem.disp == NULL) + process_error (ctx, MIR_syntax_error, "wrong local var"); + MIR_new_func_reg (ctx, func->u.func, op_addr[i].u.mem.type, + (const char *) op_addr[i].u.mem.disp); + } + } else if (data_type != MIR_T_BOUND) { + union { + uint8_t u8; + uint16_t u16; + uint32_t u32; + uint64_t u64; + int8_t i8; + int16_t i16; + int32_t i32; + int64_t i64; + } v; + + n = VARR_LENGTH (MIR_op_t, temp_insn_ops); + op_addr = VARR_ADDR (MIR_op_t, temp_insn_ops); + VARR_TRUNC (uint8_t, temp_data, 0); + for (i = 0; i < n; i++) { + if (op_addr[i].mode != type2mode (data_type)) + process_error (ctx, MIR_syntax_error, "data operand is not of data type"); + switch (data_type) { + case MIR_T_I8: + v.i8 = op_addr[i].u.i; + push_data (ctx, (uint8_t *) &v.i8, sizeof (int8_t)); + break; + case MIR_T_U8: + v.u8 = op_addr[i].u.u; + push_data (ctx, (uint8_t *) &v.u8, sizeof (uint8_t)); + break; + case MIR_T_I16: + v.i16 = op_addr[i].u.i; + push_data (ctx, (uint8_t *) &v.i16, sizeof (int16_t)); + break; + case MIR_T_U16: + v.u16 = op_addr[i].u.u; + push_data (ctx, (uint8_t *) &v.u16, sizeof (uint16_t)); + break; + case MIR_T_I32: + v.i32 = op_addr[i].u.i; + push_data (ctx, (uint8_t *) &v.i32, sizeof (int32_t)); + break; + case MIR_T_U32: + v.u32 = op_addr[i].u.u; + push_data (ctx, (uint8_t *) &v.u32, sizeof (uint32_t)); + break; + case MIR_T_I64: + v.i64 = op_addr[i].u.i; + push_data (ctx, (uint8_t *) &v.i64, sizeof (int64_t)); + break; + case MIR_T_U64: + v.u64 = op_addr[i].u.u; + push_data (ctx, (uint8_t *) &v.u64, sizeof (uint64_t)); + break; + case MIR_T_F: push_data (ctx, (uint8_t *) &op_addr[i].u.f, sizeof (float)); break; + case MIR_T_D: push_data (ctx, (uint8_t *) &op_addr[i].u.d, sizeof (double)); break; + case MIR_T_LD: + push_data (ctx, (uint8_t *) &op_addr[i].u.ld, sizeof (long double)); + break; + /* ptr ??? */ + default: process_error (ctx, MIR_syntax_error, "wrong data clause"); + } + } + name + = (VARR_LENGTH (label_name_t, label_names) == 0 ? NULL + : VARR_GET (label_name_t, label_names, 0)); + MIR_new_data (ctx, name, data_type, + VARR_LENGTH (uint8_t, temp_data) / _MIR_type_size (ctx, data_type), + VARR_ADDR (uint8_t, temp_data)); + } else { + insn = MIR_new_insn_arr (ctx, insn_code, VARR_LENGTH (MIR_op_t, temp_insn_ops), + VARR_ADDR (MIR_op_t, temp_insn_ops)); + if (func != NULL) MIR_append_insn (ctx, func, insn); + } + } + if (func != NULL) process_error (ctx, MIR_syntax_error, "absent endfunc"); + if (module != NULL) process_error (ctx, MIR_syntax_error, "absent endmodule"); +} + +static void scan_init (MIR_context_t ctx) { + insn_name_t in, el; + size_t i; + + if ((ctx->scan_ctx = malloc (sizeof (struct scan_ctx))) == NULL) + (*error_func) (MIR_alloc_error, "Not enough memory for ctx"); + VARR_CREATE (label_name_t, label_names, 0); + HTAB_CREATE (label_desc_t, label_desc_tab, 100, label_hash, label_eq); + HTAB_CREATE (insn_name_t, insn_name_tab, MIR_INSN_BOUND, insn_name_hash, insn_name_eq); + for (i = 0; i < MIR_INSN_BOUND; i++) { + in.code = i; + in.name = MIR_insn_name (ctx, i); + HTAB_DO (insn_name_t, insn_name_tab, in, HTAB_INSERT, el); + } +} + +static void scan_finish (MIR_context_t ctx) { + VARR_DESTROY (label_name_t, label_names); + HTAB_DESTROY (label_desc_t, label_desc_tab); + HTAB_DESTROY (insn_name_t, insn_name_tab); + free (ctx->scan_ctx); + ctx->scan_ctx = NULL; +} + +#endif /* if !MIR_NO_SCAN */ + +/* New Page */ + +#if defined(__x86_64__) +#include "mir-x86_64.c" +#elif defined(__PPC64__) +#include "mir-ppc64.c" +#elif defined(__aarch64__) +#include "mir-aarch64.c" +#else +#error "undefined or unsupported generation target" +#endif + +/* New Page */ + +#include "mir-interp.c" + +/* Local Variables: */ +/* mode: c */ +/* page-delimiter: "/\\* New Page" */ +/* End: */ diff --git a/mir/mir.h b/mir/mir.h new file mode 100644 index 0000000..0f9ffe1 --- /dev/null +++ b/mir/mir.h @@ -0,0 +1,584 @@ +/* This file is a part of MIR project. + Copyright (C) 2018, 2019 Vladimir Makarov . +*/ + +#ifndef MIR_H + +#define MIR_H + +#include +#include +#include +#include "mir-dlist.h" +#include "mir-varr.h" +#include "mir-htab.h" + +#ifdef NDEBUG +static inline int mir_assert (int cond) { return 0 && cond; } +#else +#define mir_assert(cond) assert (cond) +#endif + +#define FALSE 0 +#define TRUE 1 + +/* Redefine MIR_NO_IO or/and MIR_NO_SCAN if you don't need the functionality they provide. */ +#ifndef MIR_NO_IO +#define MIR_NO_IO 0 +#endif + +#ifndef MIR_NO_SCAN +#define MIR_NO_SCAN 0 +#endif + +#ifdef __GNUC__ +#define MIR_UNUSED __attribute__ ((unused)) +#else +#define MIR_UNUSED +#endif + +#define REP2(M, a1, a2) M (a1) REP_SEP M (a2) +#define REP3(M, a1, a2, a3) REP2 (M, a1, a2) REP_SEP M (a3) +#define REP4(M, a1, a2, a3, a4) REP3 (M, a1, a2, a3) REP_SEP M (a4) +#define REP5(M, a1, a2, a3, a4, a5) REP4 (M, a1, a2, a3, a4) REP_SEP M (a5) +#define REP6(M, a1, a2, a3, a4, a5, a6) REP5 (M, a1, a2, a3, a4, a5) REP_SEP M (a6) +#define REP7(M, a1, a2, a3, a4, a5, a6, a7) REP6 (M, a1, a2, a3, a4, a5, a6) REP_SEP M (a7) +#define REP8(M, a1, a2, a3, a4, a5, a6, a7, a8) REP7 (M, a1, a2, a3, a4, a5, a6, a7) REP_SEP M (a8) + +#define REP_SEP , + +#define ERR_EL(e) MIR_##e##_error +typedef enum MIR_error_type { + REP8 (ERR_EL, no, syntax, binary_io, alloc, finish, no_module, nested_module, no_func), + REP4 (ERR_EL, func, vararg_func, nested_func, wrong_param_value), + REP4 (ERR_EL, reserved_name, import_export, undeclared_func_reg, repeated_decl), + REP8 (ERR_EL, reg_type, unique_reg, undeclared_op_ref, ops_num, call_op, ret, op_mode, out_op), + ERR_EL (invalid_insn) +} MIR_error_type_t; + +#ifdef __GNUC__ +#define MIR_NO_RETURN __attribute__ ((noreturn)) +#else +#define MIR_NO_RETURN +#endif + +typedef void MIR_NO_RETURN (*MIR_error_func_t) (MIR_error_type_t error_type, const char *format, + ...); + +#define INSN_EL(i) MIR_##i + +/* The most MIR insns have destination operand and one or two source + operands. The destination can be ony a register or memory. + + There are additional constraints on insn operands: + + o A register in porgram can contain only one type values: integer, + float, double, or long double. + o Operand types should be what the insn expects */ +typedef enum { + /* Abbreviations: + I - 64-bit int, S - short (32-bit), U - unsigned, F -float, D - double, LD - long double. */ + /* 2 operand insns: */ + REP4 (INSN_EL, MOV, FMOV, DMOV, LDMOV), /* Moves */ + /* Extensions. Truncation is not necessary because we can use an extension to use a part. */ + REP6 (INSN_EL, EXT8, EXT16, EXT32, UEXT8, UEXT16, UEXT32), + REP3 (INSN_EL, I2F, I2D, I2LD), /* Integer to float or (long) double conversion */ + REP3 (INSN_EL, UI2F, UI2D, UI2LD), /* Unsigned integer to float or (long) double conversion */ + REP3 (INSN_EL, F2I, D2I, LD2I), /* Float or (long) double to integer conversion */ + REP6 (INSN_EL, F2D, F2LD, D2F, D2LD, LD2F, LD2D), /* Float, (long) double conversions */ + REP5 (INSN_EL, NEG, NEGS, FNEG, DNEG, LDNEG), /* Changing sign */ + /* 3 operand insn: */ + REP5 (INSN_EL, ADD, ADDS, FADD, DADD, LDADD), /* Addition */ + REP5 (INSN_EL, SUB, SUBS, FSUB, DSUB, LDSUB), /* Subtraction */ + REP5 (INSN_EL, MUL, MULS, FMUL, DMUL, LDMUL), /* Multiplication */ + REP7 (INSN_EL, DIV, DIVS, UDIV, UDIVS, FDIV, DDIV, LDDIV), /* Division */ + REP4 (INSN_EL, MOD, MODS, UMOD, UMODS), /* Modulo */ + REP6 (INSN_EL, AND, ANDS, OR, ORS, XOR, XORS), /* Logical */ + REP6 (INSN_EL, LSH, LSHS, RSH, RSHS, URSH, URSHS), /* Right signed/unsigned shift */ + REP5 (INSN_EL, EQ, EQS, FEQ, DEQ, LDEQ), /* Equality */ + REP5 (INSN_EL, NE, NES, FNE, DNE, LDNE), /* Inequality */ + REP7 (INSN_EL, LT, LTS, ULT, ULTS, FLT, DLT, LDLT), /* Less then */ + REP7 (INSN_EL, LE, LES, ULE, ULES, FLE, DLE, LDLE), /* Less or equal */ + REP7 (INSN_EL, GT, GTS, UGT, UGTS, FGT, DGT, LDGT), /* Greater then */ + REP7 (INSN_EL, GE, GES, UGE, UGES, FGE, DGE, LDGE), /* Greater or equal */ + /* Uncoditional (1 operand) and conditional (2 operands) branch + insns. The first operand is a label. */ + REP5 (INSN_EL, JMP, BT, BTS, BF, BFS), + /* Compare and branch (3 operand) insns. The first operand is the + label. */ + REP5 (INSN_EL, BEQ, BEQS, FBEQ, DBEQ, LDBEQ), + REP5 (INSN_EL, BNE, BNES, FBNE, DBNE, LDBNE), + REP7 (INSN_EL, BLT, BLTS, UBLT, UBLTS, FBLT, DBLT, LDBLT), + REP7 (INSN_EL, BLE, BLES, UBLE, UBLES, FBLE, DBLE, LDBLE), + REP7 (INSN_EL, BGT, BGTS, UBGT, UBGTS, FBGT, DBGT, LDBGT), + REP7 (INSN_EL, BGE, BGES, UBGE, UBGES, FBGE, DBGE, LDBGE), + /* 1st operand is a prototype, 2nd one is ref or op containing func + address, 3rd and subsequent ops are optional result (if result in + the prototype is not of void type), call arguments. */ + REP2 (INSN_EL, CALL, INLINE), + /* 1st operand is an index, subsequent ops are labels to which goto + according the index (1st label has index zero). The insn + behaviour is undefined if there is no label for the index. */ + INSN_EL (SWITCH), + /* 1 operand insn: */ + INSN_EL (RET), + INSN_EL (ALLOCA), /* 2 operands: result address and size */ + REP2 (INSN_EL, BSTART, BEND), /* block start: result addr; block end: addr from block start */ + /* Special insns: */ + INSN_EL (VA_ARG), /* result is arg address, operands: va_list addr and memory */ + INSN_EL (VA_START), + INSN_EL (VA_END), /* operand is va_list */ + INSN_EL (LABEL), /* One immediate operand is unique label number */ + INSN_EL (INVALID_INSN), + INSN_EL (INSN_BOUND), /* Should be the last */ +} MIR_insn_code_t; + +#define TYPE_EL(t) MIR_T_##t + +/* Data types: */ +typedef enum { + REP8 (TYPE_EL, I8, U8, I16, U16, I32, U32, I64, U64), /* Integer types of different size: */ + REP3 (TYPE_EL, F, D, LD), /* Float or (long) double type */ + TYPE_EL (P), /* Pointer */ + REP2 (TYPE_EL, UNDEF, BOUND), +} MIR_type_t; + +#if UINTPTR_MAX == 0xffffffff +#define MIR_PTR32 1 +#define MIR_PTR64 0 +#elif UINTPTR_MAX == 0xffffffffffffffffu +#define MIR_PTR32 0 +#define MIR_PTR64 1 +#else +#error MIR can work only for 32- or 64-bit targets +#endif + +typedef uint8_t MIR_scale_t; /* Index reg scale in memory */ + +#define MIR_MAX_SCALE UINT8_MAX + +typedef int64_t MIR_disp_t; /* Address displacement in memory */ + +/* Register number (> 0). A register always contain only one type + value: integer, float, or (long) double. Register numbers in insn + operands can be changed in MIR_finish_func. */ +typedef uint32_t MIR_reg_t; + +#define MIR_MAX_REG_NUM UINT32_MAX +#define MIR_NON_HARD_REG MIR_MAX_REG_NUM + +/* Immediate in immediate moves. */ +typedef union { + int64_t i; + uint64_t u; + float f; + double d; + long double ld; +} MIR_imm_t; + +/* Memory: mem:type[base + index * scale + disp]. It also can be + memory with hard regs but such memory used only internally. An + integer type memory value expands to int64_t value when the insn is + executed. */ +typedef struct { + MIR_type_t type : 8; + MIR_scale_t scale; + /* 0 means no reg for memory. MIR_NON_HARD_REG means no reg for + hard reg memory. */ + MIR_reg_t base, index; + MIR_disp_t disp; +} MIR_mem_t; + +typedef struct MIR_insn *MIR_label_t; + +typedef const char *MIR_name_t; + +#define OP_EL(op) MIR_OP_##op + +/* Operand mode */ +typedef enum { + REP8 (OP_EL, UNDEF, REG, HARD_REG, INT, UINT, FLOAT, DOUBLE, LDOUBLE), + REP6 (OP_EL, REF, STR, MEM, HARD_REG_MEM, LABEL, BOUND), +} MIR_op_mode_t; + +typedef struct MIR_item *MIR_item_t; + +struct MIR_str { + size_t len; + const char *s; +}; + +typedef struct MIR_str MIR_str_t; + +/* An insn operand */ +typedef struct { + void *data; /* Aux data */ + MIR_op_mode_t mode; + /* Defined after MIR_func_finish. Only MIR_OP_INT, MIR_OP_UINT, + MIR_OP_FLOAT, MIR_OP_DOUBLE, MIR_OP_LDOUBLE: */ + MIR_op_mode_t value_mode; + union { + MIR_reg_t reg; + MIR_reg_t hard_reg; /* Used only internally */ + int64_t i; + uint64_t u; + float f; + double d; + long double ld; + MIR_item_t ref; /* non-export/non-forward after simplification */ + MIR_str_t str; + MIR_mem_t mem; + MIR_mem_t hard_reg_mem; /* Used only internally */ + MIR_label_t label; + } u; +} MIR_op_t; + +typedef struct MIR_insn *MIR_insn_t; + +/* Definition of link of double list of insns */ +DEF_DLIST_LINK (MIR_insn_t); + +struct MIR_insn { + void *data; /* Aux data */ + DLIST_LINK (MIR_insn_t) insn_link; + MIR_insn_code_t code : 32; + unsigned int nops : 32; /* number of operands */ + MIR_op_t ops[1]; +}; + +/* Definition of double list of insns */ +DEF_DLIST (MIR_insn_t, insn_link); + +typedef struct MIR_var { + MIR_type_t type; + const char *name; +} MIR_var_t; + +DEF_VARR (MIR_var_t); + +/* Function definition */ +typedef struct MIR_func { + const char *name; + DLIST (MIR_insn_t) insns, original_insns; + uint32_t nres, nargs, last_temp_num, n_inlines; + MIR_type_t *res_types; + char vararg_p; /* flag of variable number of arguments */ + char expr_p; /* flag of that the func can be used as a linker expression */ + VARR (MIR_var_t) * vars; /* args and locals but temps */ + void *machine_code; /* address of generated machine code or NULL */ + void *call_addr; /* address to call the function, it can be the same as machine_code */ +} * MIR_func_t; + +typedef struct MIR_proto { + const char *name; + uint32_t nres; + MIR_type_t *res_types; /* != MIR_T_UNDEF */ + char vararg_p; /* flag of variable number of arguments */ + VARR (MIR_var_t) * args; /* args name can be NULL */ +} * MIR_proto_t; + +typedef struct MIR_data { + const char *name; /* can be NULL */ + MIR_type_t el_type; + size_t nel; + union { + long double d; /* for alignment of temporary literals */ + uint8_t els[1]; + } u; +} * MIR_data_t; + +typedef struct MIR_ref_data { + const char *name; /* can be NULL */ + MIR_item_t ref_item; /* base */ + int64_t disp; /* disp relative to base */ + void *load_addr; +} * MIR_ref_data_t; + +typedef struct MIR_expr_data { + const char *name; /* can be NULL */ + MIR_item_t expr_item; /* a special function can be called during linking */ + void *load_addr; +} * MIR_expr_data_t; + +typedef struct MIR_bss { + const char *name; /* can be NULL */ + uint64_t len; +} * MIR_bss_t; + +typedef struct MIR_module *MIR_module_t; + +/* Definition of link of double list of MIR_item_t type elements */ +DEF_DLIST_LINK (MIR_item_t); + +#define ITEM_EL(i) MIR_##i##_item + +typedef enum { + REP8 (ITEM_EL, func, proto, import, export, forward, data, ref_data, expr_data), + ITEM_EL (bss), +} MIR_item_type_t; + +#undef ERR_EL +#undef INSN_EL +#undef TYPE_EL +#undef OP_EL +#undef ITEM_EL +#undef REP_SEP + +/* MIR module items (function or import): */ +struct MIR_item { + void *data; + MIR_module_t module; + DLIST_LINK (MIR_item_t) item_link; + MIR_item_type_t item_type; /* item type */ + /* Non-null only for export/forward items and import item after + linking. It forms a chain to the final definition. */ + MIR_item_t ref_def; + /* address of loaded data/bss items, function to call the function + item, imported definition or proto object */ + void *addr; + char export_p; /* true for export items (only func items) */ + union { + MIR_func_t func; + MIR_proto_t proto; + MIR_name_t import; + MIR_name_t export; + MIR_name_t forward; + MIR_data_t data; + MIR_ref_data_t ref_data; + MIR_expr_data_t expr_data; + MIR_bss_t bss; + } u; +}; + +/* Definition of double list of MIR_item_t type elements */ +DEF_DLIST (MIR_item_t, item_link); + +/* Definition of link of double list of MIR_module_t type elements */ +DEF_DLIST_LINK (MIR_module_t); + +/* MIR module: */ +struct MIR_module { + void *data; + const char *name; + DLIST (MIR_item_t) items; /* module items */ + DLIST_LINK (MIR_module_t) module_link; + uint32_t last_temp_item_num; /* Used only internally */ +}; + +/* Definition of double list of MIR_item_t type elements */ +DEF_DLIST (MIR_module_t, module_link); + +struct MIR_context; +typedef struct MIR_context *MIR_context_t; + +static inline int MIR_FP_branch_code_p (MIR_insn_code_t code) { + return (code == MIR_FBEQ || code == MIR_DBEQ || code == MIR_LDBEQ || code == MIR_FBNE + || code == MIR_DBNE || code == MIR_LDBNE || code == MIR_FBLT || code == MIR_DBLT + || code == MIR_LDBLT || code == MIR_FBLE || code == MIR_DBLE || code == MIR_LDBLE + || code == MIR_FBGT || code == MIR_DBGT || code == MIR_LDBGT || code == MIR_FBGE + || code == MIR_DBGE || code == MIR_LDBGE); +} + +static inline int MIR_call_code_p (MIR_insn_code_t code) { + return code == MIR_CALL || code == MIR_INLINE; +} + +static inline int MIR_int_branch_code_p (MIR_insn_code_t code) { + return (code == MIR_BT || code == MIR_BTS || code == MIR_BF || code == MIR_BFS || code == MIR_BEQ + || code == MIR_BEQS || code == MIR_BNE || code == MIR_BNES || code == MIR_BLT + || code == MIR_BLTS || code == MIR_UBLT || code == MIR_UBLTS || code == MIR_BLE + || code == MIR_BLES || code == MIR_UBLE || code == MIR_UBLES || code == MIR_BGT + || code == MIR_BGTS || code == MIR_UBGT || code == MIR_UBGTS || code == MIR_BGE + || code == MIR_BGES || code == MIR_UBGE || code == MIR_UBGES); +} + +static inline int MIR_branch_code_p (MIR_insn_code_t code) { + return (code == MIR_JMP || MIR_int_branch_code_p (code) || MIR_FP_branch_code_p (code)); +} + +/* Use only the following API to create MIR code. */ +extern MIR_context_t MIR_init (void); +extern void MIR_finish (MIR_context_t ctx); + +extern MIR_module_t MIR_new_module (MIR_context_t ctx, const char *name); +extern DLIST (MIR_module_t) * MIR_get_module_list (MIR_context_t ctx); +extern MIR_item_t MIR_new_import (MIR_context_t ctx, const char *name); +extern MIR_item_t MIR_new_export (MIR_context_t ctx, const char *name); +extern MIR_item_t MIR_new_forward (MIR_context_t ctx, const char *name); +extern MIR_item_t MIR_new_bss (MIR_context_t ctx, const char *name, + size_t len); /* name can be NULL */ +extern MIR_item_t MIR_new_data (MIR_context_t ctx, const char *name, MIR_type_t el_type, size_t nel, + const void *els); /* name can be NULL */ +extern MIR_item_t MIR_new_string_data (MIR_context_t ctx, const char *name, + MIR_str_t str); /* name can be NULL */ +extern MIR_item_t MIR_new_ref_data (MIR_context_t ctx, const char *name, MIR_item_t item, + int64_t disp); /* name can be NULL */ +extern MIR_item_t MIR_new_expr_data (MIR_context_t ctx, const char *name, + MIR_item_t expr_item); /* name can be NULL */ +extern MIR_item_t MIR_new_proto_arr (MIR_context_t ctx, const char *name, size_t nres, + MIR_type_t *res_types, size_t nargs, MIR_var_t *vars); +extern MIR_item_t MIR_new_proto (MIR_context_t ctx, const char *name, size_t nres, + MIR_type_t *res_types, size_t nargs, ...); +extern MIR_item_t MIR_new_vararg_proto_arr (MIR_context_t ctx, const char *name, size_t nres, + MIR_type_t *res_types, size_t nargs, MIR_var_t *vars); +extern MIR_item_t MIR_new_vararg_proto (MIR_context_t ctx, const char *name, size_t nres, + MIR_type_t *res_types, size_t nargs, ...); +extern MIR_item_t MIR_new_func_arr (MIR_context_t ctx, const char *name, size_t nres, + MIR_type_t *res_types, size_t nargs, MIR_var_t *vars); +extern MIR_item_t MIR_new_func (MIR_context_t ctx, const char *name, size_t nres, + MIR_type_t *res_types, size_t nargs, ...); +extern MIR_item_t MIR_new_vararg_func_arr (MIR_context_t ctx, const char *name, size_t nres, + MIR_type_t *res_types, size_t nargs, MIR_var_t *vars); +extern MIR_item_t MIR_new_vararg_func (MIR_context_t ctx, const char *name, size_t nres, + MIR_type_t *res_types, size_t nargs, ...); +extern const char *MIR_item_name (MIR_context_t ctx, MIR_item_t item); +extern MIR_reg_t MIR_new_func_reg (MIR_context_t ctx, MIR_func_t func, MIR_type_t type, + const char *name); +extern void MIR_finish_func (MIR_context_t ctx); +extern void MIR_finish_module (MIR_context_t ctx); + +extern MIR_error_func_t MIR_get_error_func (MIR_context_t ctx); +extern void MIR_set_error_func (MIR_context_t ctx, MIR_error_func_t func); + +extern MIR_insn_t MIR_new_insn_arr (MIR_context_t ctx, MIR_insn_code_t code, size_t nops, + MIR_op_t *ops); +extern MIR_insn_t MIR_new_insn (MIR_context_t ctx, MIR_insn_code_t code, ...); +extern MIR_insn_t MIR_new_call_insn (MIR_context_t ctx, size_t nops, ...); +extern MIR_insn_t MIR_new_ret_insn (MIR_context_t ctx, size_t nops, ...); +extern MIR_insn_t MIR_copy_insn (MIR_context_t ctx, MIR_insn_t insn); + +extern const char *MIR_insn_name (MIR_context_t ctx, MIR_insn_code_t code); +extern size_t MIR_insn_nops (MIR_context_t ctx, MIR_insn_t insn); +extern MIR_op_mode_t MIR_insn_op_mode (MIR_context_t ctx, MIR_insn_t insn, size_t nop, int *out_p); + +extern MIR_insn_t MIR_new_label (MIR_context_t ctx); + +extern MIR_reg_t MIR_reg (MIR_context_t ctx, const char *reg_name, MIR_func_t func); +extern MIR_type_t MIR_reg_type (MIR_context_t ctx, MIR_reg_t reg, MIR_func_t func); +extern const char *MIR_reg_name (MIR_context_t ctx, MIR_reg_t reg, MIR_func_t func); + +extern MIR_op_t MIR_new_reg_op (MIR_context_t ctx, MIR_reg_t reg); +extern MIR_op_t MIR_new_int_op (MIR_context_t ctx, int64_t v); +extern MIR_op_t MIR_new_uint_op (MIR_context_t ctx, uint64_t v); +extern MIR_op_t MIR_new_float_op (MIR_context_t ctx, float v); +extern MIR_op_t MIR_new_double_op (MIR_context_t ctx, double v); +extern MIR_op_t MIR_new_ldouble_op (MIR_context_t ctx, long double v); +extern MIR_op_t MIR_new_ref_op (MIR_context_t ctx, MIR_item_t item); +extern MIR_op_t MIR_new_str_op (MIR_context_t ctx, MIR_str_t str); +extern MIR_op_t MIR_new_mem_op (MIR_context_t ctx, MIR_type_t type, MIR_disp_t disp, MIR_reg_t base, + MIR_reg_t index, MIR_scale_t scale); +extern MIR_op_t MIR_new_label_op (MIR_context_t ctx, MIR_label_t label); +extern int MIR_op_eq_p (MIR_context_t ctx, MIR_op_t op1, MIR_op_t op2); +extern htab_hash_t MIR_op_hash_step (MIR_context_t ctx, htab_hash_t h, MIR_op_t op); + +extern void MIR_append_insn (MIR_context_t ctx, MIR_item_t func, MIR_insn_t insn); +extern void MIR_prepend_insn (MIR_context_t ctx, MIR_item_t func, MIR_insn_t insn); +extern void MIR_insert_insn_after (MIR_context_t ctx, MIR_item_t func, MIR_insn_t after, + MIR_insn_t insn); +extern void MIR_insert_insn_before (MIR_context_t ctx, MIR_item_t func, MIR_insn_t before, + MIR_insn_t insn); +extern void MIR_remove_insn (MIR_context_t ctx, MIR_item_t func, MIR_insn_t insn); + +extern const char *MIR_type_str (MIR_context_t ctx, MIR_type_t tp); +extern void MIR_output_op (MIR_context_t ctx, FILE *f, MIR_op_t op, MIR_func_t func); +extern void MIR_output_insn (MIR_context_t ctx, FILE *f, MIR_insn_t insn, MIR_func_t func, + int newline_p); +extern void MIR_output_item (MIR_context_t ctx, FILE *f, MIR_item_t item); +extern void MIR_output_module (MIR_context_t ctx, FILE *f, MIR_module_t module); +extern void MIR_output (MIR_context_t ctx, FILE *f); + +#if !MIR_NO_IO +extern void MIR_write (MIR_context_t ctx, FILE *f); +extern void MIR_write_module (MIR_context_t ctx, FILE *f, MIR_module_t module); +extern void MIR_read (MIR_context_t ctx, FILE *f); +extern void MIR_write_with_func (MIR_context_t ctx, + const int (*writer_func) (MIR_context_t, uint8_t)); +extern void MIR_write_module_with_func (MIR_context_t ctx, + const int (*writer_func) (MIR_context_t, uint8_t), + MIR_module_t module); +extern void MIR_read_with_func (MIR_context_t ctx, const int (*reader_func) (MIR_context_t)); +#endif + +#if !MIR_NO_SCAN +extern void MIR_scan_string (MIR_context_t ctx, const char *str); +#endif + +extern MIR_item_t MIR_get_global_item (MIR_context_t ctx, const char *name); +extern void MIR_load_module (MIR_context_t ctx, MIR_module_t m); +extern void MIR_load_external (MIR_context_t ctx, const char *name, void *addr); +extern void MIR_link (MIR_context_t ctx, void (*set_interface) (MIR_context_t ctx, MIR_item_t item), + void *(*import_resolver) (const char *) ); + +/* Interpreter: */ +typedef union { + MIR_insn_code_t ic; + void *a; + int64_t i; + uint64_t u; + float f; + double d; + long double ld; +} MIR_val_t; + +extern void MIR_interp (MIR_context_t ctx, MIR_item_t func_item, MIR_val_t *results, size_t nargs, + ...); +extern void MIR_interp_arr (MIR_context_t ctx, MIR_item_t func_item, MIR_val_t *results, + size_t nargs, MIR_val_t *vals); +extern void MIR_interp_arr_varg (MIR_context_t ctx, MIR_item_t func_item, MIR_val_t *results, + size_t nargs, MIR_val_t *vals, va_list va); +extern void MIR_set_interp_interface (MIR_context_t ctx, MIR_item_t func_item); + +/* Private: */ +extern const char *_MIR_uniq_string (MIR_context_t ctx, const char *str); +extern int _MIR_reserved_ref_name_p (MIR_context_t ctx, const char *name); +extern int _MIR_reserved_name_p (MIR_context_t ctx, const char *name); +extern MIR_reg_t _MIR_new_temp_reg (MIR_context_t ctx, MIR_type_t type, + MIR_func_t func); /* for internal use only */ +extern size_t _MIR_type_size (MIR_context_t ctx, MIR_type_t type); +extern MIR_op_mode_t _MIR_insn_code_op_mode (MIR_context_t ctx, MIR_insn_code_t code, size_t nop, + int *out_p); +extern void _MIR_duplicate_func_insns (MIR_context_t ctx, MIR_item_t func_item); +extern void _MIR_restore_func_insns (MIR_context_t ctx, MIR_item_t func_item); +extern void _MIR_simplify_insn (MIR_context_t ctx, MIR_item_t func_item, MIR_insn_t insn, + int keep_ref_p, int mem_float_p); + +extern const char *_MIR_get_temp_item_name (MIR_context_t ctx, MIR_module_t module); + +extern MIR_op_t _MIR_new_hard_reg_op (MIR_context_t ctx, MIR_reg_t hard_reg); + +extern MIR_op_t _MIR_new_hard_reg_mem_op (MIR_context_t ctx, MIR_type_t type, MIR_disp_t disp, + MIR_reg_t base, MIR_reg_t index, MIR_scale_t scale); + +extern MIR_item_t _MIR_builtin_proto (MIR_context_t ctx, MIR_module_t module, const char *name, + size_t nres, MIR_type_t *res_types, size_t nargs, ...); +extern MIR_item_t _MIR_builtin_func (MIR_context_t ctx, MIR_module_t module, const char *name, + void *addr); + +extern uint8_t *_MIR_publish_code (MIR_context_t ctx, const uint8_t *code, size_t code_len); + +struct MIR_code_reloc { + size_t offset; + void *value; +}; + +typedef struct MIR_code_reloc MIR_code_reloc_t; + +extern void _MIR_update_code_arr (MIR_context_t ctx, uint8_t *base, size_t nloc, + const MIR_code_reloc_t *relocs); +extern void _MIR_update_code (MIR_context_t ctx, uint8_t *base, size_t nloc, ...); + +extern void *va_arg_builtin (void *p, uint64_t t); +extern void va_start_interp_builtin (MIR_context_t ctx, void *p, void *a); +extern void va_end_interp_builtin (MIR_context_t ctx, void *p); + +extern void *_MIR_get_bstart_builtin (MIR_context_t ctx); +extern void *_MIR_get_bend_builtin (MIR_context_t ctx); + +extern void *_MIR_get_ff_call (MIR_context_t ctx, size_t nres, MIR_type_t *res_types, size_t nargs, + MIR_type_t *arg_types); +extern void *_MIR_get_interp_shim (MIR_context_t ctx, MIR_item_t func_item, void *handler); +extern void *_MIR_get_thunk (MIR_context_t ctx); +extern void _MIR_redirect_thunk (MIR_context_t ctx, void *thunk, void *to); +extern void *_MIR_get_wrapper (MIR_context_t ctx, MIR_item_t called_func, void *hook_address); + +#endif /* #ifndef MIR_H */