Dibyendu Majumdar 4 years ago
commit 2c13ac8ee8

@ -80,8 +80,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)
message(STATUS "MIRJIT enabled")
endif ()

@ -40,6 +40,7 @@ Features
Documentation
=============
* For the Lua extensions in Ravi see the `Reference Manual <https://the-ravi-programming-language.readthedocs.io/en/latest/ravi-reference.html>`_.
* `MIR JIT Build instructions <https://the-ravi-programming-language.readthedocs.io/en/latest/ravi-mir-instructions.html>`_.
* `OMR JIT Build instructions <https://the-ravi-programming-language.readthedocs.io/en/latest/ravi-omr-instructions.html>`_.
* `LLVM JIT Build instructions <https://the-ravi-programming-language.readthedocs.io/en/latest/ravi-llvm-instructions.html>`_.
* Also see `Ravi Documentation <http://the-ravi-programming-language.readthedocs.org/en/latest/index.html>`_.
@ -92,11 +93,15 @@ History
* 2018
- Implemented Eclipse OMR JIT backend
- Created `Ravi with batteries <https://github.com/dibyendumajumdar/Suravi>`_.
* 2019 (Plan)
- New parser, type checker and code generator
* 2019
- New parser, type checker for Ravi - work in progress
- New language feature - `defer` statement
- New JIT backend `MIR <https://github.com/vnmakarov/mir>`_.
- Release Ravi 1.0
* 2020 (Plan)
- New optimizing byte code generator based on new parser / type checker
- Ravi 1.0 release
License
=======
MIT License for LLVM version.
MIT License

@ -823,7 +823,10 @@
#endif
/* Use LuaJIT style hashing */
#define RAVI_USE_NEWHASH 1
/* Following cause the first hash lookup to be inlined,
and if value is 2 then the second hash lookup is also inlined. */
#define RAVI_USE_INLINE_SHORTSTR_TGET 1
#define RAVI_USE_LLVM_BRANCH_WEIGHTS 1
/* If following is defined as true then LLVM instructions emitted for arithmetic ops

@ -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})

@ -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.

@ -0,0 +1,5 @@
This is a private copy of the [MIR](https://github.com/vnmakarov/mir) project for use by Ravi. The code will be occasionally refreshed from the upstream
project. Following changes have been made:
* A CMake build script added to create a library
* Unused files / tests have been removed to avoid clutter

@ -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

Binary file not shown.

File diff suppressed because it is too large Load Diff

@ -0,0 +1,25 @@
#include "mir.h"
#define COMMAND_LINE_SOURCE_NAME "<command-line>"
#define STDIN_SOURCE_NAME "<stdin>"
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);

@ -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";

@ -0,0 +1,24 @@
/* This file is a part of MIR project.
Copyright (C) 2018, 2019 Vladimir Makarov <vmakarov.gcc@gmail.com>.
*/
#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;
}

@ -0,0 +1,50 @@
/* This file is a part of MIR project.
Copyright (C) 2018, 2019 Vladimir Makarov <vmakarov.gcc@gmail.com>.
*/
#include <stdint.h>
#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

@ -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";

@ -0,0 +1,286 @@
/* This file is a part of MIR project.
Copyright (C) 2018, 2019 Vladimir Makarov <vmakarov.gcc@gmail.com>.
*/
#ifndef MIR_BITMAP_H
#define MIR_BITMAP_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <stdint.h>
#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 */

@ -0,0 +1,175 @@
/* This file is part of MIR project.
Copyright (C) 2018, 2019 Vladimir Makarov <vmakarov.gcc@gmail.com>.
*/
/* Typed doubly linked lists. */
#ifndef MIR_DLIST_H
#define MIR_DLIST_H
#include <stdio.h>
#include <assert.h>
#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 */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -0,0 +1,22 @@
/* This file is a part of MIR project.
Copyright (C) 2018, 2019 Vladimir Makarov <vmakarov.gcc@gmail.com>.
*/
#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 */

@ -0,0 +1,92 @@
/* This file is a part of MIR project.
Copyright (C) 2018, 2019 Vladimir Makarov <vmakarov.gcc@gmail.com>.
*/
/* 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 <stddef.h>
#include <stdint.h>
#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

@ -0,0 +1,221 @@
/* This file is a part of MIR project.
Copyright (C) 2018, 2019 Vladimir Makarov <vmakarov.gcc@gmail.com>.
*/
#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 */

File diff suppressed because it is too large Load Diff

@ -0,0 +1,463 @@
/* This file is a part of MIR project.
Copyright (C) 2018, 2019 Vladimir Makarov <vmakarov.gcc@gmail.com>.
*/
#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 <stdlib.h>
#include <string.h>
#include <assert.h>
#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 */

@ -0,0 +1,170 @@
/* This file is a part of MIR project.
Copyright (C) 2018, 2019 Vladimir Makarov <vmakarov.gcc@gmail.com>.
*/
#ifndef MIR_VARR_H
#define MIR_VARR_H
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#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 */

@ -0,0 +1,355 @@
/* This file is a part of MIR project.
Copyright (C) 2018, 2019 Vladimir Makarov <vmakarov.gcc@gmail.com>.
*/
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=<address to go to>; 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 <offset>(%rbx),%reg */};
static const uint8_t st_gp_reg[] = {0x48, 0x89, 0x83, 0, 0, 0, 0 /* mov %reg,<offset>(%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] <offset>(%rbx),%xmm */
};
static const uint8_t st_xmm_reg_pat[] = {
0xf2, 0x0f, 0x11, 0x83, 0, 0, 0, 0 /* movs[sd] %xmm, <offset>(%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 <src_offset>(%rbx),%r10 */
0x44, 0x89, 0x94, 0x24, 0, 0, 0, 0, /* mov %r10,<sp_offset>(%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 <src_offset>(%rbx) */
0xdb, 0xbc, 0x24, 0, 0, 0, 0, /* fstpt <sp_offset>(%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 <src_offset>(%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,<offset>]; (arg_reg=mem[r10] or r10=mem[r10];mem[sp,sp_offset]=r10) ...
rax=8; call *r11; sp+=offset
r10=mem[rbx,<offset>]; 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 <sp_offset>, %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 <sp_offset>, %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 <n>,%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 <ctx>,%rdi */
/* 40: */ 0x48, 0xbe, 0, 0, 0, 0, 0, 0, 0, 0, /* movabs <func_item>,%rsi */
/* 4a: */ 0x48, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, /* movabs <handler>,%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 <offset>(%rbx), %xmm[01] */
static const uint8_t movss_pat[]
= {0xf3, 0x0f, 0x10, 0x83, 0, 0, 0, 0}; /* movss <offset>(%rbx), %xmm[01] */
static const uint8_t movsd_pat[]
= {0xf2, 0x0f, 0x10, 0x83, 0, 0, 0, 0}; /* movsd <offset>(%rbx), %xmm[01] */
static const uint8_t fldt_pat[] = {0xdb, 0xab, 0, 0, 0, 0}; /* fldt <offset>(%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 <hook_address>,%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); }

File diff suppressed because it is too large Load Diff

@ -0,0 +1,584 @@
/* This file is a part of MIR project.
Copyright (C) 2018, 2019 Vladimir Makarov <vmakarov.gcc@gmail.com>.
*/
#ifndef MIR_H
#define MIR_H
#include <stdio.h>
#include <stdint.h>
#include <assert.h>
#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 */

@ -13,6 +13,7 @@ Contents:
ravi-overview
ravi-reference
ravi-mir-instructions
ravi-omr-instructions
ravi-llvm-instructions
ravi-dmrc

@ -0,0 +1,12 @@
Instructions for Building With MIR JIT support
==============================================
Please note that currently MIR JIT support is only available on Linux X86-64 platform.
Building with MIR support is very simple::
mkdir buildmir
cmake -DMIR_JIT=ON ..
make
That's it.

@ -33,12 +33,14 @@ Features
* Compatibility with Lua 5.3 (see Compatibility section below)
* `LLVM <http://www.llvm.org/>`_ powered JIT compiler
* `Eclipse OMR <https://github.com/dibyendumajumdar/nj>`_ powered JIT compiler
* New (wip) small JIT backend based on `MIR <https://github.com/vnmakarov/mir>`_; only Linux and x86-64 supported.
* Built-in C pre-processor, parser and JIT compiler
* A `distribution with batteries <https://github.com/dibyendumajumdar/Suravi>`_.
Documentation
=============
* For the Lua extensions in Ravi see the `Reference Manual <https://the-ravi-programming-language.readthedocs.io/en/latest/ravi-reference.html>`_.
* `MIR JIT Build instructions <https://the-ravi-programming-language.readthedocs.io/en/latest/ravi-mir-instructions.html>`_.
* `OMR JIT Build instructions <https://the-ravi-programming-language.readthedocs.io/en/latest/ravi-omr-instructions.html>`_.
* `LLVM JIT Build instructions <https://the-ravi-programming-language.readthedocs.io/en/latest/ravi-llvm-instructions.html>`_.
* Also see `Ravi Documentation <http://the-ravi-programming-language.readthedocs.org/en/latest/index.html>`_.
@ -47,17 +49,17 @@ Documentation
Lua Goodies
===========
* `An Introduction to Lua <http://the-ravi-programming-language.readthedocs.io/en/latest/lua-introduction.html>`_ attempts to provide a quick overview of Lua for folks coming from other languages.
* `Lua 5.3 Bytecode Reference <http://the-ravi-programming-language.readthedocs.io/en/latest/lua_bytecode_reference.html>`_ is my attempt to bring up to date the `Lua 5.1 Bytecode Reference <http://luaforge.net/docman/83/98/ANoFrillsIntroToLua51VMInstructions.pdf>`_.
* `Lua 5.3 Bytecode Reference <http://the-ravi-programming-language.readthedocs.io/en/latest/lua_bytecode_reference.html>`_ is my attempt to bring up to date the `Lua 5.1 Bytecode Reference <http://luaforge.net/docman/83/98/ANoFrillsIntroToLua51VMInstructions.pdf>`_.
Compatibility with Lua
======================
Ravi should be able to run all Lua 5.3 programs in interpreted mode, but following should be noted:
Ravi should be able to run all Lua 5.3 programs in interpreted mode, but following should be noted:
* Ravi supports optional typing and enhanced types such as arrays (described above). Programs using these features cannot be run by standard Lua. However all types in Ravi can be passed to Lua functions; operations on Ravi arrays within Lua code will be subject to restrictions as described in the section above on arrays.
* Ravi supports optional typing and enhanced types such as arrays (described above). Programs using these features cannot be run by standard Lua. However all types in Ravi can be passed to Lua functions; operations on Ravi arrays within Lua code will be subject to restrictions as described in the section above on arrays.
* Values crossing from Lua to Ravi will be subjected to typechecks should these values be assigned to typed variables.
* Upvalues cannot subvert the static typing of local variables (issue #26) when types are annotated.
* Certain Lua limits are reduced due to changed byte code structure. These are described below.
* Ravi uses an extended bytecode which means it is not compatible with Lua 5.3 bytecode.
* Ravi uses an extended bytecode which means it is not compatible with Lua 5.3 bytecode.
+-----------------+-------------+-------------+
| Limit name | Lua value | Ravi value |
@ -80,22 +82,26 @@ When JIT compilation is enabled there are following additional constraints:
History
=======
* 2015
* 2015
- Implemented JIT compilation using LLVM
- Implemented libgccjit based alternative JIT (now discontinued)
* 2016
- Implemented debugger for Ravi and Lua 5.3 for `Visual Studio Code <https://github.com/dibyendumajumdar/ravi/tree/master/vscode-debugger>`_
* 2017
- Embedded C compiler using dmrC project (C JIT compiler)
* 2016
- Implemented debugger for Ravi and Lua 5.3 for `Visual Studio Code <https://github.com/dibyendumajumdar/ravi/tree/master/vscode-debugger>`_
* 2017
- Embedded C compiler using dmrC project (C JIT compiler)
- Additional type-annotations
* 2018
* 2018
- Implemented Eclipse OMR JIT backend
- Created `Ravi with batteries <https://github.com/dibyendumajumdar/Suravi>`_.
* 2019 (Plan)
- New parser, type checker and code generator
- Release Ravi 1.0
* 2019
- New parser, type checker for Ravi - work in progress
- New language feature - `defer` statement
- New JIT backend `MIR <https://github.com/vnmakarov/mir>`_.
* 2020 (Plan)
- New optimizing byte code generator based on new parser / type checker
- Ravi 1.0 release
License
=======
MIT License for LLVM version.
MIT License

@ -228,8 +228,8 @@ Each slice holds an internal reference to the underlying array to ensure that th
For an example use of slices please see the `matmul1_ravi.lua <https://github.com/dibyendumajumdar/ravi/blob/master/ravi-tests/matmul1_ravi.lua>`_ benchmark program in the repository. Note that this feature is highly experimental and not very well tested.
Examples
--------
Type Annotation Examples
------------------------
Example of code that works - you can copy this to the command line input::
function tryme()
@ -275,3 +275,32 @@ Another example using arrays. Here the function receives a parameter ``arr`` of
print(sum(table.numarray(10, 2.0)))
The ``table.numarray(n, initial_value)`` creates a ``number[]`` of specified size and initializes the array with the given initial value.
``defer`` statement
-------------------
A new addition to Ravi is the ``defer`` statement. The statement has the form::
defer
block
end
Where ``block`` is a set of Lua statements.
The ``defer`` statement creates an anonymous ``closure`` that will be invoked when the enclosing scope is exited, whether
normally or because of an error.
Example::
y = 0
function x()
defer y = y + 1 end
defer y = y + 1 end
end
x()
assert(y == 2)
``defer`` statements are meant to be used for releasing resources in a deterministic manner. The syntax and functionality is
inspired by the similar statement in the Go language. The implementation is based upon Lua 5.4.
Note that the ``defer`` statement should be considered a beta feature not yet ready for production use as it is undergoing testing.

@ -29,3 +29,26 @@ The LLVM JIT implementation is in following sources:
* ravi_llvmcall.cpp - implements OP_CALL, OP_JMP
* ravi_llvmtable.cpp - implements OP_GETTABLE, OP_SETTABLE and various other table operations, OP_SELF, and also upvalue operations
* ravi_llvmrest.cpp - OP_CLOSURE, OP_VARARG, OP_CONCAT
Apart from LLVM backend, we also have MIR and OMR JIT backends.
These backends use C as the intermediate language. The common C code generator is in:
* ravi_jitshared.c - this is the C code generator for a given Ravi / Lua function
The MIR JIT implementation is in:
* ravi_mirjit.c - defines the driver functions. The MIR backend has its own C preprocessor, parser and code generator.
The OMR JIT driver is in:
* ravi_omrjit.c - defines the driver functions. The OMR JIT backend uses the dmrC project as the C preprocessor, parser and code generator.
The new Ravi Parser and Code Generator implementation is in:
* ravi_ast_parse.c - contains the parser that builds AST
* ravi_ast_print.c - contains utilities for printing out the AST
* ravi_ast_typecheck.c - contains the type checking phase of the parser
Ravi also uses Doug Lea's malloc implementation. The implementation is in:
* ravi_alloc.c - Doug Lea's malloc implementation, adapted for Ravi.

@ -1425,14 +1425,6 @@ newframe: /* reentry point when frame changes (call/return
checkGC(L, ra + 1);
vmbreak;
}
vmcase(OP_RAVI_SELF_SK) {
StkId rb = RB(i); /* variable - may not be a table */
/* we know that the key a short string constant */
TValue *rc = RKC(i);
setobjs2s(L, ra + 1, rb);
GETTABLE_INLINE_SSKEY_PROTECTED(L, rb, rc, ra);
vmbreak;
}
vmcase(OP_SELF) {
const TValue *aux;
StkId rb = RB(i);
@ -1917,6 +1909,14 @@ newframe: /* reentry point when frame changes (call/return
vmcase(OP_SETLIST) {
int n = GETARG_B(i);
int c = GETARG_C(i);
#if 1
if (c == 0) {
lua_assert(GET_OPCODE(*pc) == OP_EXTRAARG);
c = GETARG_Ax(*pc++);
}
savepc(L); /* in case of allocation errors */
raviV_op_setlist(L, ci, ra, n, c);
#else
unsigned int last;
Table *h;
if (n == 0)
@ -1978,6 +1978,7 @@ newframe: /* reentry point when frame changes (call/return
}
}
L->top = ci->top; /* correct top (in case of previous open call) */
#endif
vmbreak;
}
vmcase(OP_CLOSURE) {
@ -1993,6 +1994,9 @@ newframe: /* reentry point when frame changes (call/return
vmbreak;
}
vmcase(OP_VARARG) {
#if 1
Protect_base(raviV_op_vararg(L, ci, cl, GETARG_A(i), GETARG_B(i)));
#else
int b = GETARG_B(i) - 1; /* required results */
int j;
int n = cast_int(base - ci->func) - cl->p->numparams - 1;
@ -2008,6 +2012,7 @@ newframe: /* reentry point when frame changes (call/return
setobjs2s(L, ra + j, base - n + j);
for (; j < b; j++) /* complete required results with nil */
setnilvalue(ra + j);
#endif
vmbreak;
}
vmcase(OP_EXTRAARG) {
@ -2176,6 +2181,25 @@ newframe: /* reentry point when frame changes (call/return
short string but the variable may or may not be
a table
*/
#if 1
vmcase(OP_RAVI_SELF_SK) vmcase(OP_RAVI_GETTABUP_SK) vmcase(OP_RAVI_GETFIELD) {
StkId rb = (op == OP_RAVI_GETTABUP_SK)
? cl->upvals[GETARG_B(i)]->v
: RB(i); /* variable - may not be a table */
TValue* rc = RKC(i);
if (op == OP_RAVI_SELF_SK) setobjs2s(L, ra + 1, rb);
Protect(raviV_gettable_sskey(L, rb, rc, ra));
vmbreak;
}
#else
vmcase(OP_RAVI_SELF_SK) {
StkId rb = RB(i); /* variable - may not be a table */
/* we know that the key a short string constant */
TValue *rc = RKC(i);
setobjs2s(L, ra + 1, rb);
GETTABLE_INLINE_SSKEY_PROTECTED(L, rb, rc, ra);
vmbreak;
}
vmcase(OP_RAVI_GETTABUP_SK) {
StkId rb = cl->upvals[GETARG_B(i)]->v; /* variable - may not be a table */
lua_assert(ISK(GETARG_C(i)));
@ -2192,6 +2216,7 @@ newframe: /* reentry point when frame changes (call/return
GETTABLE_INLINE_SSKEY_PROTECTED(L, rb, rc, ra);
vmbreak;
}
#endif
vmcase(OP_RAVI_TABLE_SELF_SK) vmcase(OP_RAVI_TABLE_GETFIELD) {
/* This opcode is used when the key is known to be
short string and the variable is known to be

@ -58,7 +58,7 @@ static void raise_error(lua_State *L, int errorcode) {
}
#if !RAVI_TARGET_X64
#error OMRJIT is currently only supported on X64 architecture
#error MIRJIT is currently only supported on X64 architecture
#endif
typedef struct LuaFunc {
@ -180,7 +180,6 @@ int raviV_initjit(struct lua_State *L) {
jit->opt_level_ = 1;
// The parameter true means we will be dumping stuff as we compile
jit->jit = MIR_init();
// TODO create context
G->ravi_state = jit;
return 0;
}
@ -461,8 +460,6 @@ int raviV_compile(struct lua_State *L, struct Proto *p, ravi_compile_options_t *
int (*fp)(lua_State * L) = NULL;
char fname[30];
snprintf(fname, sizeof fname, "jit%lld", G->ravi_state->id++);
char *argv[] = { fname, "-O1", NULL };
if (!raviJ_codegen(L, p, options, fname, &buf)) {
p->ravi_jit.jit_status = RAVI_JIT_CANT_COMPILE;
goto Lerror;

Loading…
Cancel
Save