From 1621ac50ab63d8e17d928c2962191c3141c0f917 Mon Sep 17 00:00:00 2001 From: Dibyendu Majumdar Date: Fri, 27 Dec 2019 00:24:12 +0000 Subject: [PATCH] Add MIR project --- CMakeLists.txt | 6 +- mir/CMakeLists.txt | 44 + mir/LICENSE | 21 + mir/build/Makefile | 202 + mir/build/libc2mir.a | Bin 0 -> 1116096 bytes mir/c2mir/c2mir.c | 12158 +++++++++++++++++++++++++ mir/c2mir/c2mir.h | 25 + mir/c2mir/mirc.h | 22 + mir/c2mir/x86_64/cx86_64-code.c | 24 + mir/c2mir/x86_64/cx86_64.h | 50 + mir/c2mir/x86_64/mirc-x86_64-linux.h | 89 + mir/mir-bitmap.h | 286 + mir/mir-dlist.h | 175 + mir/mir-gen-x86_64.c | 1906 ++++ mir/mir-gen.c | 4680 ++++++++++ mir/mir-gen.h | 22 + mir/mir-hash.h | 92 + mir/mir-htab.h | 221 + mir/mir-interp.c | 1602 ++++ mir/mir-reduce.h | 463 + mir/mir-varr.h | 170 + mir/mir-x86_64.c | 355 + mir/mir.c | 5220 +++++++++++ mir/mir.h | 584 ++ 24 files changed, 28415 insertions(+), 2 deletions(-) create mode 100644 mir/CMakeLists.txt create mode 100644 mir/LICENSE create mode 100644 mir/build/Makefile create mode 100644 mir/build/libc2mir.a create mode 100644 mir/c2mir/c2mir.c create mode 100644 mir/c2mir/c2mir.h create mode 100644 mir/c2mir/mirc.h create mode 100644 mir/c2mir/x86_64/cx86_64-code.c create mode 100644 mir/c2mir/x86_64/cx86_64.h create mode 100644 mir/c2mir/x86_64/mirc-x86_64-linux.h create mode 100644 mir/mir-bitmap.h create mode 100644 mir/mir-dlist.h create mode 100644 mir/mir-gen-x86_64.c create mode 100644 mir/mir-gen.c create mode 100644 mir/mir-gen.h create mode 100644 mir/mir-hash.h create mode 100644 mir/mir-htab.h create mode 100644 mir/mir-interp.c create mode 100644 mir/mir-reduce.h create mode 100644 mir/mir-varr.h create mode 100644 mir/mir-x86_64.c create mode 100644 mir/mir.c create mode 100644 mir/mir.h 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 0000000000000000000000000000000000000000..3e6ba660d90660a7be57cd20312f4d59548a91a5 GIT binary patch literal 1116096 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Νca2}?h&^yb3^ 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≪Sm!~_ocF1;8Rqhdr1i2>0Q6Nf6j7#E{rL=1@m(GwGg zD7_dLqhdr1i2>0Q6GN3=jEhk*B8J3(=!uD3r5EF3RE&rrF(7(k;$Wp0<6=~dh#@f` zdSYUT(u;91Dn`VR7!W-%5mb6HE=I+O7!m`bCngS3dND3W#fTUZ1EMD;4pe$EE=I+O z7!m`bCngS1dND3W#fTUZ1EMD;_E&l_E=I+O7!m`bCnm6AxbZK>#i$q≪Sm#Ke9| zFUG~F7!gBaK=j1KzDh5~#i$q≪Sm#Kb;IFUG~F7!gBaK=j1KV5Jx1VpNQXAu%9& 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 literal 0 HcmV?d00001 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 */