commit
3c7828439f
@ -1,5 +1,23 @@
|
||||
Most of Ravi is under MIT License
|
||||
There are components that are under GPL - please see relevant
|
||||
source files. These are optional components that are part of the
|
||||
libgccjit implementation of the JIT compiler. You can remove these
|
||||
components if you do not want GPL license.
|
||||
/******************************************************************************
|
||||
* Copyright (C) 1994-2019 Lua.org, PUC-Rio.
|
||||
* Portions Copyright (C) 2015-2020 Dibyendu Majumdar
|
||||
*
|
||||
* 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 @@
|
||||
rmdir /s llvm8
|
||||
mkdir llvm8
|
||||
cd llvm8
|
||||
cmake -DCMAKE_INSTALL_PREFIX=c:\Software\ravi -G "Visual Studio 15 2017 Win64" -DCMAKE_BUILD_TYPE=Release -DLLVM_JIT=ON -DLLVM_DIR=c:\Software\llvm801\lib\cmake\llvm ..
|
||||
cd ..
|
@ -0,0 +1,5 @@
|
||||
rmdir /s llvm9d
|
||||
mkdir llvm9d
|
||||
cd llvm9d
|
||||
cmake -DCMAKE_INSTALL_PREFIX=c:\Software\ravi -G "Visual Studio 15 2017 Win64" -DLLVM_JIT=ON -DLLVM_DIR=c:\Software\llvm900\lib\cmake\llvm ..
|
||||
cd ..
|
@ -0,0 +1,6 @@
|
||||
rmdir /s llvm9r
|
||||
mkdir llvm9r
|
||||
cd llvm9r
|
||||
rem cmake -DCMAKE_INSTALL_PREFIX=c:\Software\ravi -G "Visual Studio 15 2017 Win64" -DLLVM_JIT=ON -DLLVM_DIR=c:\Software\llvm900r\lib\cmake\llvm ..
|
||||
cmake -DCMAKE_INSTALL_PREFIX=c:\Software\ravi -G "Visual Studio 16 2019" -DLLVM_JIT=ON -DLLVM_DIR=c:\Software\llvm900r\lib\cmake\llvm ..
|
||||
cd ..
|
@ -1,5 +1,6 @@
|
||||
rm -rf buildllvm
|
||||
mkdir buildllvm
|
||||
cd buildllvm
|
||||
#cmake -DCMAKE_BUILD_TYPE=Release -DLLVM_JIT=ON -DCMAKE_INSTALL_PREFIX=$HOME/ravi -DLLVM_DIR=$HOME/LLVM/share/llvm/cmake ..
|
||||
#cmake -DCMAKE_BUILD_TYPE=Release -DLLVM_JIT=ON -DCMAKE_INSTALL_PREFIX=$HOME/ravi -DLLVM_DIR=$HOME/LLVM5/lib/cmake/llvm ..
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DLLVM_JIT=ON -DCMAKE_INSTALL_PREFIX=$HOME/Software/ravi -DLLVM_DIR=$HOME/Software/llvm801/lib/cmake/llvm ..
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DSTATIC_BUILD=ON -DLLVM_JIT=ON -DCMAKE_INSTALL_PREFIX=$HOME/Software/ravi -DLLVM_DIR=$HOME/Software/llvm801/lib/cmake/llvm ..
|
||||
|
@ -0,0 +1,16 @@
|
||||
find_path(MIRJIT_INCLUDE_DIR c2mir.h
|
||||
PATHS
|
||||
c:/Software/mir/include/mir
|
||||
~/Software/mir/include/mir
|
||||
NO_DEFAULT_PATH
|
||||
)
|
||||
|
||||
find_library(MIRJIT_LIBRARY
|
||||
NAMES libc2mir.a
|
||||
PATHS
|
||||
c:/Software/mir/lib
|
||||
~/Software/mir/lib
|
||||
)
|
||||
|
||||
set( MIRJIT_INCLUDE_DIRS "${MIRJIT_INCLUDE_DIR}" )
|
||||
set( MIRJIT_LIBRARIES "${MIRJIT_LIBRARY}" )
|
@ -0,0 +1,35 @@
|
||||
FROM ubuntu:18.04
|
||||
|
||||
RUN set -x \
|
||||
&& apt-get update \
|
||||
&& apt-get install -y libreadline-dev \
|
||||
&& apt-get install -y build-essential \
|
||||
&& apt-get install -y git wget \
|
||||
&& apt-get install -y zlib1g-dev \
|
||||
&& apt-get clean \
|
||||
&& mkdir -p /Software \
|
||||
&& wget -O "/Software/cmake-3.14.5-Linux-x86_64.tar.gz" "https://github.com/Kitware/CMake/releases/download/v3.14.5/cmake-3.14.5-Linux-x86_64.tar.gz" \
|
||||
&& cd /Software \
|
||||
&& tar xvf "cmake-3.14.5-Linux-x86_64.tar.gz" \
|
||||
&& rm -rf "/Software/cmake-3.14.5-Linux-x86_64.tar.gz" \
|
||||
&& wget -O "/Software/clang+llvm-8.0.0-x86_64-linux-gnu-ubuntu-18.04.tar.xz" "http://releases.llvm.org/8.0.0/clang+llvm-8.0.0-x86_64-linux-gnu-ubuntu-18.04.tar.xz" \
|
||||
&& cd /Software \
|
||||
&& tar xvf "clang+llvm-8.0.0-x86_64-linux-gnu-ubuntu-18.04.tar.xz" \
|
||||
&& rm -rf "/Software/clang+llvm-8.0.0-x86_64-linux-gnu-ubuntu-18.04.tar.xz" \
|
||||
&& mkdir -p /sources \
|
||||
&& cd /sources \
|
||||
&& git clone https://github.com/dibyendumajumdar/ravi.git \
|
||||
&& cd /sources/ravi \
|
||||
&& mkdir build \
|
||||
&& cd build \
|
||||
&& /Software/cmake-3.14.5-Linux-x86_64/bin/cmake -DSTATIC_BUILD=ON -DCMAKE_INSTALL_PREFIX=/Software/ravi -DLLVM_JIT=ON -DCMAKE_BUILD_TYPE=Release -DLLVM_DIR=/Software/clang+llvm-8.0.0-x86_64-linux-gnu-ubuntu-18.04/lib/cmake/llvm .. \
|
||||
&& make install \
|
||||
&& rm -rf /Software/cmake-3.14.5-Linux-x86_64 \
|
||||
&& rm -rf /Software/clang+llvm-8.0.0-x86_64-linux-gnu-ubuntu-18.04 \
|
||||
&& rm -rf /sources \
|
||||
&& apt-get autoremove \
|
||||
&& apt-get remove -y --purge git wget build-essential \
|
||||
&& apt-get clean
|
||||
|
||||
ENV PATH /Software/ravi/bin:${PATH}
|
||||
|
@ -1,15 +1,289 @@
|
||||
#ifndef ravi_ast_h
|
||||
#define ravi_ast_h
|
||||
|
||||
/*
|
||||
** $Id: lparser.h,v 1.74 2014/10/25 11:50:46 roberto Exp $
|
||||
** Lua Parser
|
||||
** See Copyright Notice in lua.h
|
||||
A parser and syntax tree builder for Ravi. This is work in progress.
|
||||
Once ready it will be used to create a new byte code generator for Ravi.
|
||||
|
||||
The parser will perform following actions:
|
||||
|
||||
a) Generate syntax tree
|
||||
b) Perform type checking (Ravi enhancement)
|
||||
*/
|
||||
|
||||
#ifndef ravi_ast_h
|
||||
#define ravi_ast_h
|
||||
#define LUA_CORE
|
||||
#include "lprefix.h"
|
||||
#include "lua.h"
|
||||
|
||||
#include "lcode.h"
|
||||
#include "ldo.h"
|
||||
#include "lstring.h"
|
||||
#include "ltable.h"
|
||||
#include "lauxlib.h"
|
||||
|
||||
#include "ravi_ast.h"
|
||||
#include "ravi_membuf.h"
|
||||
|
||||
#include "allocate.h"
|
||||
#include "ptrlist.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define MAXVARS 125
|
||||
|
||||
//////////////////////////
|
||||
|
||||
struct lua_symbol_list;
|
||||
|
||||
/*
|
||||
* Userdata object to hold the abstract syntax tree;
|
||||
* All memory is held by this object. Memory is freed when
|
||||
* the object is GC collected; or when
|
||||
* ast_container:release() method is called
|
||||
* by user.
|
||||
*/
|
||||
struct ast_container {
|
||||
struct allocator ast_node_allocator;
|
||||
struct allocator ptrlist_allocator;
|
||||
struct allocator block_scope_allocator;
|
||||
struct allocator symbol_allocator;
|
||||
struct ast_node *main_function;
|
||||
bool killed; /* flag to check if this is already destroyed */
|
||||
};
|
||||
|
||||
struct ast_node;
|
||||
DECLARE_PTR_LIST(ast_node_list, struct ast_node);
|
||||
|
||||
struct var_type;
|
||||
DECLARE_PTR_LIST(var_type_list, struct var_type);
|
||||
|
||||
/* Lua type info. We need to support user defined types too which are known by name */
|
||||
struct var_type {
|
||||
ravitype_t type_code;
|
||||
const TString *type_name; /* type name for user defined types; used to lookup metatable in registry, only set when
|
||||
type_code is RAVI_TUSERDATA */
|
||||
};
|
||||
|
||||
struct lua_symbol;
|
||||
DECLARE_PTR_LIST(lua_symbol_list, struct lua_symbol);
|
||||
|
||||
struct block_scope;
|
||||
|
||||
/* Types of symbols */
|
||||
enum symbol_type {
|
||||
SYM_LOCAL,
|
||||
SYM_UPVALUE,
|
||||
SYM_GLOBAL, /* Global symbols are never added to a scope so they are always looked up */
|
||||
SYM_LABEL
|
||||
};
|
||||
|
||||
/* A symbol is a name recognised in Ravi/Lua code*/
|
||||
struct lua_symbol {
|
||||
enum symbol_type symbol_type;
|
||||
struct var_type value_type;
|
||||
union {
|
||||
struct {
|
||||
const TString *var_name; /* name of the variable */
|
||||
struct block_scope *block; /* NULL if global symbol, as globals are never added to a scope */
|
||||
} var;
|
||||
struct {
|
||||
const TString *label_name;
|
||||
struct block_scope *block;
|
||||
} label;
|
||||
struct {
|
||||
struct lua_symbol *var; /* variable reference */
|
||||
struct ast_node *function; /* Where the upvalue lives */
|
||||
} upvalue;
|
||||
};
|
||||
};
|
||||
|
||||
struct block_scope {
|
||||
struct ast_node *function; /* function owning this block - of type FUNCTION_EXPR */
|
||||
struct block_scope *parent; /* parent block, may belong to parent function */
|
||||
struct lua_symbol_list *symbol_list; /* symbols defined in this block */
|
||||
};
|
||||
|
||||
enum ast_node_type {
|
||||
AST_NONE, /* Used when the node doesn't represent an AST such as test_then_block. */
|
||||
AST_RETURN_STMT,
|
||||
AST_GOTO_STMT,
|
||||
AST_LABEL_STMT,
|
||||
AST_DO_STMT,
|
||||
AST_LOCAL_STMT,
|
||||
AST_FUNCTION_STMT,
|
||||
AST_IF_STMT,
|
||||
AST_WHILE_STMT,
|
||||
AST_FORIN_STMT,
|
||||
AST_FORNUM_STMT,
|
||||
AST_REPEAT_STMT,
|
||||
AST_EXPR_STMT, /* Also used for assignment statements */
|
||||
AST_LITERAL_EXPR,
|
||||
AST_SYMBOL_EXPR,
|
||||
AST_Y_INDEX_EXPR, /* [] operator */
|
||||
AST_FIELD_SELECTOR_EXPR, /* table field access - '.' or ':' operator */
|
||||
AST_INDEXED_ASSIGN_EXPR, /* table value assign in table constructor */
|
||||
AST_SUFFIXED_EXPR,
|
||||
AST_UNARY_EXPR,
|
||||
AST_BINARY_EXPR,
|
||||
AST_FUNCTION_EXPR, /* function literal */
|
||||
AST_TABLE_EXPR, /* table constructor */
|
||||
AST_FUNCTION_CALL_EXPR
|
||||
};
|
||||
|
||||
/* The parse tree is made up of ast_node objects. Some of the ast_nodes reference the appropriate block
|
||||
scopes but not all scopes may be referenced. The tree captures Lua syntax tree - i.e. statements such as
|
||||
while, repeat, and for are captured in the way user uses them and not the way Lua generates code. Potentially
|
||||
we can have a transformation step to convert to a tree that is more like the code generation */
|
||||
struct ast_node {
|
||||
enum ast_node_type type;
|
||||
union {
|
||||
struct {
|
||||
struct ast_node_list *expr_list;
|
||||
} return_stmt;
|
||||
struct {
|
||||
struct lua_symbol *symbol;
|
||||
} label_stmt;
|
||||
struct {
|
||||
const TString *name; /* target label, used to resolve the goto destination */
|
||||
struct ast_node *label_stmt; /* Initially this will be NULL; set by a separate pass */
|
||||
} goto_stmt;
|
||||
struct {
|
||||
struct lua_symbol_list *var_list;
|
||||
struct ast_node_list *expr_list;
|
||||
} local_stmt; /* local declarations */
|
||||
struct {
|
||||
struct ast_node_list *var_expr_list; /* Optional var expressions, comma separated */
|
||||
struct ast_node_list *expr_list; /* Comma separated expressions */
|
||||
} expression_stmt; /* Also covers assignments */
|
||||
struct {
|
||||
struct ast_node *name; /* base symbol to be looked up */
|
||||
struct ast_node_list *selectors; /* Optional */
|
||||
struct ast_node *method_name; /* Optional */
|
||||
struct ast_node *function_expr; /* Function's AST */
|
||||
} function_stmt;
|
||||
struct {
|
||||
struct block_scope *scope; /* The do statement only creates a new scope */
|
||||
struct ast_node_list *do_statement_list; /* statements in this block */
|
||||
} do_stmt;
|
||||
struct {
|
||||
struct ast_node *condition;
|
||||
struct block_scope *test_then_scope;
|
||||
struct ast_node_list *test_then_statement_list; /* statements in this block */
|
||||
} test_then_block; /* Used internally in if_stmt, not an independent AST node */
|
||||
struct {
|
||||
struct ast_node_list *if_condition_list; /* Actually a list of test_then_blocks */
|
||||
struct block_scope *else_block;
|
||||
struct ast_node_list *else_statement_list; /* statements in this block */
|
||||
} if_stmt;
|
||||
struct {
|
||||
struct ast_node *condition;
|
||||
struct block_scope *loop_scope;
|
||||
struct ast_node_list *loop_statement_list; /* statements in this block */
|
||||
} while_or_repeat_stmt;
|
||||
struct {
|
||||
struct lua_symbol_list *symbols;
|
||||
struct ast_node_list *expr_list;
|
||||
struct block_scope *for_body;
|
||||
struct ast_node_list *for_statement_list; /* statements in this block */
|
||||
} for_stmt; /* Used for both generic and numeric for loops */
|
||||
struct {
|
||||
struct var_type type;
|
||||
} common_expr; /* To access the type field common to all expr objects */
|
||||
/* all expr types must be compatible with common_expr */
|
||||
struct {
|
||||
struct var_type type;
|
||||
union {
|
||||
lua_Integer i;
|
||||
lua_Number n;
|
||||
const TString *s;
|
||||
} u;
|
||||
} literal_expr;
|
||||
struct {
|
||||
struct var_type type;
|
||||
struct lua_symbol *var;
|
||||
} symbol_expr;
|
||||
struct {
|
||||
struct var_type type;
|
||||
struct ast_node *expr; /* '[' expr ']' */
|
||||
} index_expr;
|
||||
struct {
|
||||
struct var_type type;
|
||||
UnOpr unary_op;
|
||||
struct ast_node *expr;
|
||||
} unary_expr;
|
||||
struct {
|
||||
struct var_type type;
|
||||
BinOpr binary_op;
|
||||
struct ast_node *expr_left;
|
||||
struct ast_node *expr_right;
|
||||
} binary_expr;
|
||||
struct {
|
||||
struct var_type type;
|
||||
unsigned int is_vararg : 1;
|
||||
unsigned int is_method : 1;
|
||||
struct ast_node *parent_function; /* parent function or NULL if main chunk */
|
||||
struct block_scope *main_block; /* the function's main block */
|
||||
struct ast_node_list *function_statement_list; /* statements in this block */
|
||||
struct lua_symbol_list *args; /* arguments, also must be part of the function block's symbol list */
|
||||
struct ast_node_list *child_functions; /* child functions declared in this function */
|
||||
struct lua_symbol_list *upvalues; /* List of upvalues */
|
||||
struct lua_symbol_list *locals; /* List of locals */
|
||||
} function_expr; /* a literal expression whose result is a value of type function */
|
||||
struct {
|
||||
struct var_type type;
|
||||
struct ast_node *
|
||||
index_expr; /* If NULL means this is a list field with next available index, else specfies index expression */
|
||||
struct ast_node *value_expr;
|
||||
} indexed_assign_expr; /* Assign values in table constructor */
|
||||
struct {
|
||||
struct var_type type;
|
||||
struct ast_node_list *expr_list;
|
||||
} table_expr; /* table constructor expression */
|
||||
struct {
|
||||
struct var_type type;
|
||||
struct ast_node *primary_expr;
|
||||
struct ast_node_list *suffix_list;
|
||||
} suffixed_expr;
|
||||
struct {
|
||||
/* Note that in Ravi the results from a function call must be type asserted during assignment to variables.
|
||||
* This is not explicit in the AST but is required to ensure that function return values do not
|
||||
* overwrite the type of the variables in an inconsistent way.
|
||||
*/
|
||||
struct var_type type;
|
||||
TString *method_name; /* Optional method_name */
|
||||
struct ast_node_list *arg_list; /* Call arguments */
|
||||
} function_call_expr;
|
||||
};
|
||||
};
|
||||
|
||||
#include "lparser.h"
|
||||
#define set_typecode(vt, t) \
|
||||
(vt).type_code = t
|
||||
#define set_type(vt, t) \
|
||||
(vt).type_code = t, \
|
||||
(vt).type_name = NULL
|
||||
#define set_typename(vt, t, name) \
|
||||
(vt).type_code = t, \
|
||||
(vt).type_name = (name)
|
||||
#define is_type_same(a, b) ((a).type_code == (b).type_code && (a).type_name == (b).type_name)
|
||||
#define copy_type(a, b) \
|
||||
(a).type_code = (b).type_code, \
|
||||
(a).type_name = (b).type_name
|
||||
|
||||
struct parser_state {
|
||||
LexState *ls;
|
||||
struct ast_container *container;
|
||||
struct ast_node *current_function;
|
||||
struct block_scope *current_scope;
|
||||
};
|
||||
|
||||
LUAMOD_API int raviopen_ast_library(lua_State *L);
|
||||
|
||||
#endif
|
||||
void raviA_print_ast_node(membuff_t *buf, struct ast_node *node, int level); /* output the AST structure recusrively */
|
||||
void raviA_ast_typecheck(struct ast_container *container); /* Perform type checks and assign types to AST */
|
||||
|
||||
#endif
|
@ -0,0 +1,53 @@
|
||||
/******************************************************************************
|
||||
* Copyright (C) 2019-2020 Dibyendu Majumdar
|
||||
*
|
||||
* 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.
|
||||
******************************************************************************/
|
||||
#ifndef RAVI_MIRJIT_H
|
||||
#define RAVI_MIRJIT_H
|
||||
|
||||
#include <ravi_jitshared.h>
|
||||
|
||||
#ifdef USE_MIRJIT
|
||||
#include "c2mir.h"
|
||||
#include "mir-gen.h"
|
||||
|
||||
struct ravi_State {
|
||||
MIR_context_t jit;
|
||||
unsigned long long id; // counter to generate function names
|
||||
unsigned int verbosity_ : 3;
|
||||
unsigned int auto_ : 1; /* Should we auto compile what we can? */
|
||||
unsigned int enabled_ : 1; /* is JIT enabled */
|
||||
unsigned int opt_level_ : 3; /* optimization level */
|
||||
unsigned int tracehook_enabled_ : 1; /* enable calls to luaG_traceexec() at every bytecode, this is expensive ! */
|
||||
unsigned int validation_ : 1; /* Enable extra validation such as IL verification */
|
||||
unsigned int compiling_; /* flag to help avoid recursion */
|
||||
int min_code_size_; /* min code size for compilation */
|
||||
int min_exec_count_; /* min execution count for compilation */
|
||||
struct c2mir_options options; /* MIR options */
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* USE_MIRJIT */
|
||||
|
||||
#endif /* RAVI_MIRJIT_H */
|
@ -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
|
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 *),
|
||||
void *getc_data, const char *source_name, FILE *output_file);
|
@ -0,0 +1,23 @@
|
||||
static const char mirc[]
|
||||
= "#define __mirc__ 1\n"
|
||||
"#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,354 @@
|
||||
/* 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 */
|
||||
};
|
||||
|
||||
static uint8_t *push_insns (MIR_context_t ctx, 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 (MIR_context_t ctx, 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 (ctx, 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 (MIR_context_t ctx, 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 (ctx, 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 (MIR_context_t ctx, 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 (ctx, 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 (MIR_context_t ctx, 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 (ctx, 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 (MIR_context_t ctx, uint32_t src_offset) {
|
||||
static const uint8_t st80_pat[] = {0xdb, 0xbb, 0, 0, 0, 0 /* fstpt <src_offset>(%rbx) */};
|
||||
memcpy (push_insns (ctx, 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 (ctx, 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 (ctx, (i + nres) * sizeof (long double), iregs[n_iregs++], TRUE);
|
||||
} else {
|
||||
gen_ldst (ctx, 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 (ctx, (i + nres) * sizeof (long double), n_xregs++, arg_types[i] == MIR_T_F,
|
||||
TRUE);
|
||||
} else {
|
||||
gen_ldst (ctx, 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 (ctx, 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 (ctx, 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 (ctx, 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 (ctx, 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 (ctx, i * sizeof (long double));
|
||||
} else {
|
||||
(*error_func) (MIR_ret_error, "x86-64 can not handle this combination of return values");
|
||||
}
|
||||
}
|
||||
push_insns (ctx, 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 (ctx, push_rbx, sizeof (push_rbx));
|
||||
push_insns (ctx, save_pat, sizeof (save_pat));
|
||||
addr = push_insns (ctx, 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 (ctx, 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 (ctx, 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 (ctx, fldt_pat, sizeof (fldt_pat));
|
||||
memcpy (addr + 2, &offset, sizeof (uint32_t));
|
||||
if (n_fregs == 1) push_insns (ctx, fxch_pat, sizeof (fxch_pat));
|
||||
n_fregs++;
|
||||
} else if (n_iregs < 2) {
|
||||
addr = push_insns (ctx, 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 (ctx, 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 (ctx, push_rax, sizeof (push_rax));
|
||||
push_insns (ctx, save_pat, sizeof (save_pat));
|
||||
addr = push_insns (ctx, 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 (ctx, restore_pat, sizeof (restore_pat));
|
||||
push_insns (ctx, 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); }
|
@ -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 */
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,13 @@
|
||||
Instructions for Building With MIR JIT support
|
||||
==============================================
|
||||
|
||||
Please note that currently `MIR <https://github.com/vnmakarov/mir>`_ JIT support is only available on Linux X86-64 platforms.
|
||||
|
||||
Building with MIR support is straightforward as MIR is included in Ravi::
|
||||
|
||||
mkdir buildmir
|
||||
cd buildmir
|
||||
cmake -DCMAKE_INSTALL_PREFIX=$HOME/ravi -DCMAKE_BUILD_TYPE=Release -G "Unix Makefiles" -DMIR_JIT=ON ..
|
||||
make install
|
||||
|
||||
That's it.
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,520 @@
|
||||
/*
|
||||
Copyright (C) 2018-2020 Dibyendu Majumdar
|
||||
*/
|
||||
|
||||
#include <ravi_ast.h>
|
||||
#include "ravi_ast.h"
|
||||
|
||||
static const char *type_name(ravitype_t tt) {
|
||||
switch (tt) {
|
||||
case RAVI_TANY:
|
||||
return "any";
|
||||
case RAVI_TNIL:
|
||||
return "nil";
|
||||
case RAVI_TBOOLEAN:
|
||||
return "boolean";
|
||||
case RAVI_TNUMFLT:
|
||||
return "number";
|
||||
case RAVI_TNUMINT:
|
||||
return "integer";
|
||||
case RAVI_TTABLE:
|
||||
return "table";
|
||||
case RAVI_TSTRING:
|
||||
return "string";
|
||||
case RAVI_TARRAYINT:
|
||||
return "integer[]";
|
||||
case RAVI_TARRAYFLT:
|
||||
return "number[]";
|
||||
case RAVI_TFUNCTION:
|
||||
return "closure";
|
||||
case RAVI_TUSERDATA:
|
||||
return "";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
static void printf_buf(membuff_t *buf, const char *format, ...) {
|
||||
static const char *PADDING = " ";
|
||||
char tbuf[128] = {0};
|
||||
va_list ap;
|
||||
const char *cp;
|
||||
va_start(ap, format);
|
||||
for (cp = format; *cp; cp++) {
|
||||
if (cp[0] == '%' && cp[1] == 'p') { /* padding */
|
||||
int level = va_arg(ap, int);
|
||||
snprintf(tbuf, sizeof tbuf, "%.*s", level, PADDING);
|
||||
membuff_add_string(buf, tbuf);
|
||||
cp++;
|
||||
}
|
||||
else if (cp[0] == '%' && cp[1] == 't') { /* TString */
|
||||
const TString *ts;
|
||||
ts = va_arg(ap, const TString *);
|
||||
const char *s = getstr(ts);
|
||||
membuff_add_string(buf, s);
|
||||
cp++;
|
||||
}
|
||||
else if (cp[0] == '%' && cp[1] == 'T') { /* struct var_type */
|
||||
const struct var_type *type;
|
||||
type = va_arg(ap, const struct var_type *);
|
||||
if (type->type_code == RAVI_TUSERDATA) {
|
||||
const char *s = getstr(type->type_name);
|
||||
membuff_add_string(buf, s);
|
||||
}
|
||||
else {
|
||||
membuff_add_string(buf, type_name(type->type_code));
|
||||
}
|
||||
cp++;
|
||||
}
|
||||
else if (cp[0] == '%' && cp[1] == 's') { /* const char * */
|
||||
const char *s;
|
||||
s = va_arg(ap, const char *);
|
||||
membuff_add_string(buf, s);
|
||||
cp++;
|
||||
}
|
||||
else if (cp[0] == '%' && cp[1] == 'c') { /* comment */
|
||||
const char *s;
|
||||
s = va_arg(ap, const char *);
|
||||
membuff_add_fstring(buf, "--%s", s);
|
||||
cp++;
|
||||
}
|
||||
else if (cp[0] == '%' && cp[1] == 'i') { /* integer */
|
||||
lua_Integer i;
|
||||
i = va_arg(ap, lua_Integer);
|
||||
membuff_add_longlong(buf, i);
|
||||
cp++;
|
||||
}
|
||||
else if (cp[0] == '%' && cp[1] == 'f') { /* float */
|
||||
double d;
|
||||
d = va_arg(ap, double);
|
||||
membuff_add_fstring(buf, "%.16f", d);
|
||||
cp++;
|
||||
}
|
||||
else if (cp[0] == '%' && cp[1] == 'b') { /* boolean */
|
||||
lua_Integer i;
|
||||
i = va_arg(ap, lua_Integer);
|
||||
membuff_add_bool(buf, i != 0);
|
||||
cp++;
|
||||
}
|
||||
else {
|
||||
membuff_add_char(buf, *cp);
|
||||
}
|
||||
}
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
|
||||
static void print_ast_node_list(membuff_t *buf, struct ast_node_list *list, int level, const char *delimiter) {
|
||||
struct ast_node *node;
|
||||
bool is_first = true;
|
||||
FOR_EACH_PTR(list, node) {
|
||||
if (is_first)
|
||||
is_first = false;
|
||||
else if (delimiter)
|
||||
printf_buf(buf, "%p%s\n", level, delimiter);
|
||||
raviA_print_ast_node(buf, node, level + 1);
|
||||
}
|
||||
END_FOR_EACH_PTR(node);
|
||||
}
|
||||
|
||||
static void print_statement_list(membuff_t *buf, struct ast_node_list *statement_list, int level) {
|
||||
print_ast_node_list(buf, statement_list, level + 1, NULL);
|
||||
}
|
||||
|
||||
static inline const char *get_as_str(const TString *ts) { return ts ? getstr(ts) : ""; }
|
||||
|
||||
static void print_symbol(membuff_t *buf, struct lua_symbol *sym, int level) {
|
||||
switch (sym->symbol_type) {
|
||||
case SYM_GLOBAL: {
|
||||
printf_buf(buf, "%p%t %c %s %s\n", level, sym->var.var_name, "global symbol",
|
||||
raviY_typename(sym->value_type.type_code), get_as_str(sym->value_type.type_name));
|
||||
break;
|
||||
}
|
||||
case SYM_LOCAL: {
|
||||
printf_buf(buf, "%p%t %c %s %s\n", level, sym->var.var_name, "local symbol",
|
||||
raviY_typename(sym->value_type.type_code), get_as_str(sym->value_type.type_name));
|
||||
break;
|
||||
}
|
||||
case SYM_UPVALUE: {
|
||||
printf_buf(buf, "%p%t %c %s %s\n", level, sym->upvalue.var->var.var_name, "upvalue",
|
||||
raviY_typename(sym->upvalue.var->value_type.type_code),
|
||||
get_as_str(sym->upvalue.var->value_type.type_name));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void print_symbol_name(membuff_t *buf, struct lua_symbol *sym) {
|
||||
switch (sym->symbol_type) {
|
||||
case SYM_LOCAL:
|
||||
case SYM_GLOBAL: {
|
||||
printf_buf(buf, "%t", sym->var.var_name);
|
||||
break;
|
||||
}
|
||||
case SYM_UPVALUE: {
|
||||
printf_buf(buf, "%t", sym->upvalue.var->var.var_name);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void print_symbol_list(membuff_t *buf, struct lua_symbol_list *list, int level, const char *delimiter) {
|
||||
struct lua_symbol *node;
|
||||
bool is_first = true;
|
||||
FOR_EACH_PTR(list, node) {
|
||||
if (is_first)
|
||||
is_first = false;
|
||||
else if (delimiter)
|
||||
printf_buf(buf, "%p%s\n", level, delimiter);
|
||||
print_symbol(buf, node, level + 1);
|
||||
}
|
||||
END_FOR_EACH_PTR(node);
|
||||
}
|
||||
|
||||
static void print_symbol_names(membuff_t *buf, struct lua_symbol_list *list) {
|
||||
struct lua_symbol *node;
|
||||
bool is_first = true;
|
||||
FOR_EACH_PTR(list, node) {
|
||||
if (is_first)
|
||||
is_first = false;
|
||||
else
|
||||
printf_buf(buf, ", ");
|
||||
print_symbol_name(buf, node);
|
||||
}
|
||||
END_FOR_EACH_PTR(node);
|
||||
}
|
||||
|
||||
static const char *get_unary_opr_str(UnOpr op) {
|
||||
switch (op) {
|
||||
case OPR_NOT:
|
||||
return "not";
|
||||
case OPR_MINUS:
|
||||
return "-";
|
||||
case OPR_BNOT:
|
||||
return "~";
|
||||
case OPR_LEN:
|
||||
return "#";
|
||||
case OPR_TO_INTEGER:
|
||||
return "@integer";
|
||||
case OPR_TO_NUMBER:
|
||||
return "@number";
|
||||
case OPR_TO_INTARRAY:
|
||||
return "@integer[]";
|
||||
case OPR_TO_NUMARRAY:
|
||||
return "@number[]";
|
||||
case OPR_TO_TABLE:
|
||||
return "@table";
|
||||
case OPR_TO_CLOSURE:
|
||||
return "@closure";
|
||||
case OPR_TO_STRING:
|
||||
return "@string";
|
||||
case OPR_TO_TYPE:
|
||||
return "@<usertype>";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
static const char *get_binary_opr_str(BinOpr op) {
|
||||
switch (op) {
|
||||
case OPR_ADD:
|
||||
return "+";
|
||||
case OPR_SUB:
|
||||
return "-";
|
||||
case OPR_MUL:
|
||||
return "*";
|
||||
case OPR_MOD:
|
||||
return "%";
|
||||
case OPR_POW:
|
||||
return "^";
|
||||
case OPR_DIV:
|
||||
return "/";
|
||||
case OPR_IDIV:
|
||||
return "//";
|
||||
case OPR_BAND:
|
||||
return "&";
|
||||
case OPR_BOR:
|
||||
return "|";
|
||||
case OPR_BXOR:
|
||||
return "~";
|
||||
case OPR_SHL:
|
||||
return "<<";
|
||||
case OPR_SHR:
|
||||
return ">>";
|
||||
case OPR_CONCAT:
|
||||
return "..";
|
||||
case OPR_NE:
|
||||
return "~=";
|
||||
case OPR_EQ:
|
||||
return "==";
|
||||
case OPR_LT:
|
||||
return "<";
|
||||
case OPR_LE:
|
||||
return "<=";
|
||||
case OPR_GT:
|
||||
return ">";
|
||||
case OPR_GE:
|
||||
return ">=";
|
||||
case OPR_AND:
|
||||
return "and";
|
||||
case OPR_OR:
|
||||
return "or";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
void raviA_print_ast_node(membuff_t *buf, struct ast_node *node, int level) {
|
||||
switch (node->type) {
|
||||
case AST_FUNCTION_EXPR: {
|
||||
if (node->function_expr.args) {
|
||||
printf_buf(buf, "%pfunction(\n", level);
|
||||
print_symbol_list(buf, node->function_expr.args, level + 1, ",");
|
||||
printf_buf(buf, "%p)\n", level);
|
||||
}
|
||||
else {
|
||||
printf_buf(buf, "%pfunction()\n", level);
|
||||
}
|
||||
if (node->function_expr.locals) {
|
||||
printf_buf(buf, "%p%c ", level, "locals ");
|
||||
print_symbol_names(buf, node->function_expr.locals);
|
||||
printf_buf(buf, "\n");
|
||||
}
|
||||
if (node->function_expr.upvalues) {
|
||||
printf_buf(buf, "%p%c ", level, "upvalues ");
|
||||
print_symbol_names(buf, node->function_expr.upvalues);
|
||||
printf_buf(buf, "\n");
|
||||
}
|
||||
print_statement_list(buf, node->function_expr.function_statement_list, level);
|
||||
printf_buf(buf, "%pend\n", level);
|
||||
break;
|
||||
}
|
||||
case AST_NONE:
|
||||
break;
|
||||
case AST_RETURN_STMT: {
|
||||
printf_buf(buf, "%preturn\n", level);
|
||||
print_ast_node_list(buf, node->return_stmt.expr_list, level + 1, ",");
|
||||
break;
|
||||
}
|
||||
case AST_LOCAL_STMT: {
|
||||
printf_buf(buf, "%plocal\n", level);
|
||||
printf_buf(buf, "%p%c\n", level, "[symbols]");
|
||||
print_symbol_list(buf, node->local_stmt.var_list, level + 1, ",");
|
||||
if (node->local_stmt.expr_list) {
|
||||
printf_buf(buf, "%p%c\n", level, "[expressions]");
|
||||
print_ast_node_list(buf, node->local_stmt.expr_list, level + 1, ",");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AST_FUNCTION_STMT: {
|
||||
raviA_print_ast_node(buf, node->function_stmt.name, level);
|
||||
if (node->function_stmt.selectors) {
|
||||
printf_buf(buf, "%p%c\n", level + 1, "[selectors]");
|
||||
print_ast_node_list(buf, node->function_stmt.selectors, level + 2, NULL);
|
||||
}
|
||||
if (node->function_stmt.method_name) {
|
||||
printf_buf(buf, "%p%c\n", level + 1, "[method name]");
|
||||
raviA_print_ast_node(buf, node->function_stmt.method_name, level + 2);
|
||||
}
|
||||
printf_buf(buf, "%p=\n", level + 1);
|
||||
raviA_print_ast_node(buf, node->function_stmt.function_expr, level + 2);
|
||||
break;
|
||||
}
|
||||
case AST_LABEL_STMT: {
|
||||
printf_buf(buf, "%p::%t::\n", level, node->label_stmt.symbol->label.label_name);
|
||||
break;
|
||||
}
|
||||
case AST_GOTO_STMT: {
|
||||
printf_buf(buf, "%pgoto %t\n", level, node->goto_stmt.name);
|
||||
break;
|
||||
}
|
||||
case AST_DO_STMT: {
|
||||
printf_buf(buf, "%pdo\n", level);
|
||||
print_ast_node_list(buf, node->do_stmt.do_statement_list, level + 1, NULL);
|
||||
printf_buf(buf, "%pend\n", level);
|
||||
break;
|
||||
}
|
||||
case AST_EXPR_STMT: {
|
||||
printf_buf(buf, "%p%c\n", level, "[expression statement start]");
|
||||
if (node->expression_stmt.var_expr_list) {
|
||||
printf_buf(buf, "%p%c\n", level + 1, "[var list start]");
|
||||
print_ast_node_list(buf, node->expression_stmt.var_expr_list, level + 2, ",");
|
||||
printf_buf(buf, "%p= %c\n", level + 1, "[var list end]");
|
||||
}
|
||||
printf_buf(buf, "%p%c\n", level + 1, "[expression list start]");
|
||||
print_ast_node_list(buf, node->expression_stmt.expr_list, level + 2, ",");
|
||||
printf_buf(buf, "%p%c\n", level + 1, "[expression list end]");
|
||||
printf_buf(buf, "%p%c\n", level, "[expression statement end]");
|
||||
break;
|
||||
}
|
||||
case AST_IF_STMT: {
|
||||
struct ast_node *test_then_block;
|
||||
bool is_first = true;
|
||||
FOR_EACH_PTR(node->if_stmt.if_condition_list, test_then_block) {
|
||||
if (is_first) {
|
||||
is_first = false;
|
||||
printf_buf(buf, "%pif\n", level);
|
||||
}
|
||||
else
|
||||
printf_buf(buf, "%pelseif\n", level);
|
||||
raviA_print_ast_node(buf, test_then_block->test_then_block.condition, level + 1);
|
||||
printf_buf(buf, "%pthen\n", level);
|
||||
print_ast_node_list(buf, test_then_block->test_then_block.test_then_statement_list, level + 1, NULL);
|
||||
}
|
||||
END_FOR_EACH_PTR(node);
|
||||
if (node->if_stmt.else_block) {
|
||||
printf_buf(buf, "%pelse\n", level);
|
||||
print_ast_node_list(buf, node->if_stmt.else_statement_list, level + 1, NULL);
|
||||
}
|
||||
printf_buf(buf, "%pend\n", level);
|
||||
break;
|
||||
}
|
||||
case AST_WHILE_STMT: {
|
||||
printf_buf(buf, "%pwhile\n", level);
|
||||
raviA_print_ast_node(buf, node->while_or_repeat_stmt.condition, level + 1);
|
||||
printf_buf(buf, "%pdo\n", level);
|
||||
print_ast_node_list(buf, node->while_or_repeat_stmt.loop_statement_list, level + 1, NULL);
|
||||
printf_buf(buf, "%pend\n", level);
|
||||
break;
|
||||
}
|
||||
case AST_REPEAT_STMT: {
|
||||
printf_buf(buf, "%prepeat\n", level);
|
||||
print_ast_node_list(buf, node->while_or_repeat_stmt.loop_statement_list, level + 1, NULL);
|
||||
printf_buf(buf, "%puntil\n", level);
|
||||
raviA_print_ast_node(buf, node->while_or_repeat_stmt.condition, level + 1);
|
||||
printf_buf(buf, "%p%c\n", level, "[repeat end]");
|
||||
break;
|
||||
}
|
||||
case AST_FORIN_STMT: {
|
||||
printf_buf(buf, "%pfor\n", level);
|
||||
print_symbol_list(buf, node->for_stmt.symbols, level + 1, ",");
|
||||
printf_buf(buf, "%pin\n", level);
|
||||
print_ast_node_list(buf, node->for_stmt.expr_list, level + 1, ",");
|
||||
printf_buf(buf, "%pdo\n", level);
|
||||
print_statement_list(buf, node->for_stmt.for_statement_list, level + 1);
|
||||
printf_buf(buf, "%pend\n", level);
|
||||
break;
|
||||
}
|
||||
case AST_FORNUM_STMT: {
|
||||
printf_buf(buf, "%pfor\n", level);
|
||||
print_symbol_list(buf, node->for_stmt.symbols, level + 1, NULL);
|
||||
printf_buf(buf, "%p=\n", level);
|
||||
print_ast_node_list(buf, node->for_stmt.expr_list, level + 1, ",");
|
||||
printf_buf(buf, "%pdo\n", level);
|
||||
print_statement_list(buf, node->for_stmt.for_statement_list, level + 1);
|
||||
printf_buf(buf, "%pend\n", level);
|
||||
break;
|
||||
}
|
||||
case AST_SUFFIXED_EXPR: {
|
||||
printf_buf(buf, "%p%c %T\n", level, "[suffixed expr start]", &node->suffixed_expr.type);
|
||||
printf_buf(buf, "%p%c %T\n", level+1, "[primary start]", &node->suffixed_expr.primary_expr->common_expr.type);
|
||||
raviA_print_ast_node(buf, node->suffixed_expr.primary_expr, level + 2);
|
||||
printf_buf(buf, "%p%c\n", level+1, "[primary end]");
|
||||
if (node->suffixed_expr.suffix_list) {
|
||||
printf_buf(buf, "%p%c\n", level+1, "[suffix list start]");
|
||||
print_ast_node_list(buf, node->suffixed_expr.suffix_list, level + 2, NULL);
|
||||
printf_buf(buf, "%p%c\n", level+1, "[suffix list end]");
|
||||
}
|
||||
printf_buf(buf, "%p%c\n", level, "[suffixed expr end]");
|
||||
break;
|
||||
}
|
||||
case AST_FUNCTION_CALL_EXPR: {
|
||||
printf_buf(buf, "%p%c %T\n", level, "[function call start]", &node->function_call_expr.type);
|
||||
if (node->function_call_expr.method_name) {
|
||||
printf_buf(buf, "%p: %t (\n", level + 1, node->function_call_expr.method_name);
|
||||
}
|
||||
else {
|
||||
printf_buf(buf, "%p(\n", level + 1);
|
||||
}
|
||||
print_ast_node_list(buf, node->function_call_expr.arg_list, level + 2, ",");
|
||||
printf_buf(buf, "%p)\n", level + 1);
|
||||
printf_buf(buf, "%p%c\n", level, "[function call end]");
|
||||
break;
|
||||
}
|
||||
case AST_SYMBOL_EXPR: {
|
||||
print_symbol(buf, node->symbol_expr.var, level + 1);
|
||||
break;
|
||||
}
|
||||
case AST_BINARY_EXPR: {
|
||||
printf_buf(buf, "%p%c %T\n", level, "[binary expr start]", &node->binary_expr.type);
|
||||
raviA_print_ast_node(buf, node->binary_expr.expr_left, level + 1);
|
||||
printf_buf(buf, "%p%s\n", level, get_binary_opr_str(node->binary_expr.binary_op));
|
||||
raviA_print_ast_node(buf, node->binary_expr.expr_right, level + 1);
|
||||
printf_buf(buf, "%p%c\n", level, "[binary expr end]");
|
||||
break;
|
||||
}
|
||||
case AST_UNARY_EXPR: {
|
||||
printf_buf(buf, "%p%c %T\n", level, "[unary expr start]", &node->unary_expr.type);
|
||||
printf_buf(buf, "%p%s\n", level, get_unary_opr_str(node->unary_expr.unary_op));
|
||||
raviA_print_ast_node(buf, node->unary_expr.expr, level + 1);
|
||||
printf_buf(buf, "%p%c\n", level, "[unary expr end]");
|
||||
break;
|
||||
}
|
||||
case AST_LITERAL_EXPR: {
|
||||
printf_buf(buf, "%p", level);
|
||||
switch (node->literal_expr.type.type_code) {
|
||||
case RAVI_TNIL:
|
||||
printf_buf(buf, "nil");
|
||||
break;
|
||||
case RAVI_TBOOLEAN:
|
||||
printf_buf(buf, "%b", node->literal_expr.u.i);
|
||||
break;
|
||||
case RAVI_TNUMINT:
|
||||
printf_buf(buf, "%i", node->literal_expr.u.i);
|
||||
break;
|
||||
case RAVI_TNUMFLT:
|
||||
printf_buf(buf, "%f", node->literal_expr.u.n);
|
||||
break;
|
||||
case RAVI_TSTRING:
|
||||
printf_buf(buf, "'%t'", node->literal_expr.u.s);
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
printf_buf(buf, "\n");
|
||||
break;
|
||||
}
|
||||
case AST_FIELD_SELECTOR_EXPR: {
|
||||
printf_buf(buf, "%p%c %T\n", level, "[field selector start]", &node->index_expr.type);
|
||||
printf_buf(buf, "%p.\n", level + 1);
|
||||
raviA_print_ast_node(buf, node->index_expr.expr, level + 2);
|
||||
printf_buf(buf, "%p%c\n", level, "[field selector end]");
|
||||
break;
|
||||
}
|
||||
case AST_Y_INDEX_EXPR: {
|
||||
printf_buf(buf, "%p%c %T\n", level, "[Y index start]", &node->index_expr.type);
|
||||
printf_buf(buf, "%p[\n", level + 1);
|
||||
raviA_print_ast_node(buf, node->index_expr.expr, level + 2);
|
||||
printf_buf(buf, "%p]\n", level + 1);
|
||||
printf_buf(buf, "%p%c\n", level, "[Y index end]");
|
||||
break;
|
||||
}
|
||||
case AST_INDEXED_ASSIGN_EXPR: {
|
||||
printf_buf(buf, "%p%c %T\n", level, "[indexed assign start]", &node->indexed_assign_expr.type);
|
||||
if (node->indexed_assign_expr.index_expr) {
|
||||
printf_buf(buf, "%p%c\n", level, "[index start]");
|
||||
raviA_print_ast_node(buf, node->indexed_assign_expr.index_expr, level + 1);
|
||||
printf_buf(buf, "%p%c\n", level, "[index end]");
|
||||
}
|
||||
printf_buf(buf, "%p%c\n", level, "[value start]");
|
||||
raviA_print_ast_node(buf, node->indexed_assign_expr.value_expr, level + 1);
|
||||
printf_buf(buf, "%p%c\n", level, "[value end]");
|
||||
printf_buf(buf, "%p%c\n", level, "[indexed assign end]");
|
||||
break;
|
||||
}
|
||||
case AST_TABLE_EXPR: {
|
||||
printf_buf(buf, "%p{ %c %T\n", level, "[table constructor start]", &node->table_expr.type);
|
||||
print_ast_node_list(buf, node->table_expr.expr_list, level + 1, ",");
|
||||
printf_buf(buf, "%p} %c\n", level, "[table constructor end]");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
printf_buf(buf, "%pUnsupported node type %d\n", level, node->type);
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,476 @@
|
||||
/*
|
||||
Copyright (C) 2018-2020 Dibyendu Majumdar
|
||||
*/
|
||||
#include <ravi_ast.h>
|
||||
#include "ravi_ast.h"
|
||||
|
||||
/* Type checker - WIP */
|
||||
static void typecheck_ast_node(struct ast_container *container, struct ast_node *function, struct ast_node *node);
|
||||
|
||||
/* Type checker - WIP */
|
||||
static void typecheck_ast_list(struct ast_container *container, struct ast_node *function, struct ast_node_list *list) {
|
||||
struct ast_node *node;
|
||||
FOR_EACH_PTR(list, node) { typecheck_ast_node(container, function, node); }
|
||||
END_FOR_EACH_PTR(node);
|
||||
}
|
||||
|
||||
/* Type checker - WIP */
|
||||
static void typecheck_unaryop(struct ast_container *container, struct ast_node *function, struct ast_node *node) {
|
||||
UnOpr op = node->unary_expr.unary_op;
|
||||
typecheck_ast_node(container, function, node->unary_expr.expr);
|
||||
ravitype_t subexpr_type = node->unary_expr.expr->common_expr.type.type_code;
|
||||
switch (op) {
|
||||
case OPR_MINUS:
|
||||
if (subexpr_type == RAVI_TNUMINT) {
|
||||
set_type(node->unary_expr.type, RAVI_TNUMINT);
|
||||
}
|
||||
else if (subexpr_type == RAVI_TNUMFLT) {
|
||||
set_type(node->unary_expr.type, RAVI_TNUMFLT);
|
||||
}
|
||||
break;
|
||||
case OPR_LEN:
|
||||
if (subexpr_type == RAVI_TARRAYINT || subexpr_type == RAVI_TARRAYFLT) {
|
||||
set_type(node->unary_expr.type, RAVI_TNUMINT);
|
||||
}
|
||||
break;
|
||||
case OPR_TO_INTEGER:
|
||||
set_type(node->unary_expr.type, RAVI_TNUMINT);
|
||||
break;
|
||||
case OPR_TO_NUMBER:
|
||||
set_type(node->unary_expr.type, RAVI_TNUMFLT);
|
||||
break;
|
||||
case OPR_TO_CLOSURE:
|
||||
set_type(node->unary_expr.type, RAVI_TFUNCTION);
|
||||
break;
|
||||
case OPR_TO_STRING:
|
||||
set_type(node->unary_expr.type, RAVI_TSTRING);
|
||||
break;
|
||||
case OPR_TO_INTARRAY:
|
||||
set_type(node->unary_expr.type, RAVI_TARRAYINT);
|
||||
if (node->unary_expr.expr->type == AST_TABLE_EXPR) {
|
||||
set_type(node->unary_expr.expr->table_expr.type, RAVI_TARRAYINT);
|
||||
}
|
||||
break;
|
||||
case OPR_TO_NUMARRAY:
|
||||
set_type(node->unary_expr.type, RAVI_TARRAYFLT);
|
||||
if (node->unary_expr.expr->type == AST_TABLE_EXPR) {
|
||||
set_type(node->unary_expr.expr->table_expr.type, RAVI_TARRAYFLT);
|
||||
}
|
||||
break;
|
||||
case OPR_TO_TABLE:
|
||||
set_type(node->unary_expr.type, RAVI_TTABLE);
|
||||
break;
|
||||
case OPR_TO_TYPE:
|
||||
lua_assert(node->unary_expr.type.type_name != NULL); // Should already be set by the parser
|
||||
set_typecode(node->unary_expr.type, RAVI_TUSERDATA);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Type checker - WIP */
|
||||
static void typecheck_binaryop(struct ast_container *container, struct ast_node *function, struct ast_node *node) {
|
||||
BinOpr op = node->binary_expr.binary_op;
|
||||
struct ast_node *e1 = node->binary_expr.expr_left;
|
||||
struct ast_node *e2 = node->binary_expr.expr_right;
|
||||
typecheck_ast_node(container, function, e1);
|
||||
typecheck_ast_node(container, function, e2);
|
||||
switch (op) {
|
||||
case OPR_ADD:
|
||||
case OPR_SUB:
|
||||
case OPR_MUL:
|
||||
case OPR_DIV:
|
||||
if (e1->common_expr.type.type_code == RAVI_TNUMFLT && e2->common_expr.type.type_code == RAVI_TNUMFLT)
|
||||
set_typecode(node->binary_expr.type, RAVI_TNUMFLT);
|
||||
else if (e1->common_expr.type.type_code == RAVI_TNUMFLT && e2->common_expr.type.type_code == RAVI_TNUMINT)
|
||||
set_typecode(node->binary_expr.type, RAVI_TNUMFLT);
|
||||
else if (e1->common_expr.type.type_code == RAVI_TNUMINT && e2->common_expr.type.type_code == RAVI_TNUMFLT)
|
||||
set_typecode(node->binary_expr.type, RAVI_TNUMFLT);
|
||||
else if (op != OPR_DIV && e1->common_expr.type.type_code == RAVI_TNUMINT &&
|
||||
e2->common_expr.type.type_code == RAVI_TNUMINT)
|
||||
set_typecode(node->binary_expr.type, RAVI_TNUMINT);
|
||||
else if (op == OPR_DIV && e1->common_expr.type.type_code == RAVI_TNUMINT &&
|
||||
e2->common_expr.type.type_code == RAVI_TNUMINT)
|
||||
set_typecode(node->binary_expr.type, RAVI_TNUMFLT);
|
||||
break;
|
||||
case OPR_IDIV:
|
||||
if (e1->common_expr.type.type_code == RAVI_TNUMINT && e2->common_expr.type.type_code == RAVI_TNUMINT)
|
||||
set_typecode(node->binary_expr.type, RAVI_TNUMINT);
|
||||
// FIXME missing cases
|
||||
break;
|
||||
case OPR_BAND:
|
||||
case OPR_BOR:
|
||||
case OPR_BXOR:
|
||||
case OPR_SHL:
|
||||
case OPR_SHR:
|
||||
if ((e1->common_expr.type.type_code == RAVI_TNUMFLT || e1->common_expr.type.type_code == RAVI_TNUMINT) &&
|
||||
(e2->common_expr.type.type_code == RAVI_TNUMFLT || e2->common_expr.type.type_code == RAVI_TNUMINT))
|
||||
set_typecode(node->binary_expr.type, RAVI_TNUMINT);
|
||||
break;
|
||||
case OPR_EQ:
|
||||
case OPR_NE:
|
||||
case OPR_GE:
|
||||
case OPR_GT:
|
||||
case OPR_LE:
|
||||
case OPR_LT:
|
||||
/* This case is not handled in default parser - why? */
|
||||
if ((e1->common_expr.type.type_code == RAVI_TNUMINT || e1->common_expr.type.type_code == RAVI_TNUMFLT ||
|
||||
e1->common_expr.type.type_code == RAVI_TBOOLEAN) &&
|
||||
(e2->common_expr.type.type_code == RAVI_TNUMFLT || e2->common_expr.type.type_code == RAVI_TNUMINT ||
|
||||
e2->common_expr.type.type_code == RAVI_TBOOLEAN))
|
||||
set_typecode(node->binary_expr.type, RAVI_TBOOLEAN);
|
||||
break;
|
||||
case OPR_POW:
|
||||
if ((e1->common_expr.type.type_code == RAVI_TNUMFLT || e1->common_expr.type.type_code == RAVI_TNUMINT) &&
|
||||
(e2->common_expr.type.type_code == RAVI_TNUMFLT || e2->common_expr.type.type_code == RAVI_TNUMINT))
|
||||
set_typecode(node->binary_expr.type, RAVI_TNUMFLT);
|
||||
break;
|
||||
case OPR_MOD:
|
||||
if (e1->common_expr.type.type_code == RAVI_TNUMINT && e2->common_expr.type.type_code == RAVI_TNUMINT)
|
||||
set_typecode(node->binary_expr.type, RAVI_TNUMINT);
|
||||
else if ((e1->common_expr.type.type_code == RAVI_TNUMINT && e2->common_expr.type.type_code == RAVI_TNUMFLT) ||
|
||||
(e1->common_expr.type.type_code == RAVI_TNUMFLT && e2->common_expr.type.type_code == RAVI_TNUMINT))
|
||||
set_typecode(node->binary_expr.type, RAVI_TNUMFLT);
|
||||
break;
|
||||
default:
|
||||
set_typecode(node->binary_expr.type, RAVI_TANY);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static bool is_unindexable_type(struct var_type *type) {
|
||||
switch (type->type_code) {
|
||||
case RAVI_TNUMFLT:
|
||||
case RAVI_TNUMINT:
|
||||
case RAVI_TBOOLEAN:
|
||||
case RAVI_TNIL:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Suffixed expression examples:
|
||||
* f()[1]
|
||||
* x[1][2]
|
||||
* x.y[1]
|
||||
*/
|
||||
static void typecheck_suffixedexpr(struct ast_container *container, struct ast_node *function, struct ast_node *node) {
|
||||
typecheck_ast_node(container, function, node->suffixed_expr.primary_expr);
|
||||
struct ast_node *prev_node = node->suffixed_expr.primary_expr;
|
||||
struct ast_node *this_node;
|
||||
FOR_EACH_PTR(node->suffixed_expr.suffix_list, this_node) {
|
||||
typecheck_ast_node(container, function, this_node);
|
||||
if (this_node->type == AST_Y_INDEX_EXPR) {
|
||||
if (prev_node->common_expr.type.type_code == RAVI_TARRAYFLT) {
|
||||
if (this_node->index_expr.expr->common_expr.type.type_code == RAVI_TNUMINT) {
|
||||
set_typecode(this_node->index_expr.type, RAVI_TNUMFLT);
|
||||
}
|
||||
else {
|
||||
// FIXME Error
|
||||
}
|
||||
}
|
||||
else if (prev_node->common_expr.type.type_code == RAVI_TARRAYINT) {
|
||||
if (this_node->index_expr.expr->common_expr.type.type_code == RAVI_TNUMINT) {
|
||||
set_typecode(this_node->index_expr.type, RAVI_TNUMINT);
|
||||
}
|
||||
else {
|
||||
// FIXME Error
|
||||
}
|
||||
}
|
||||
else if (is_unindexable_type(&prev_node->common_expr.type)) {
|
||||
// FIXME Error
|
||||
}
|
||||
}
|
||||
prev_node = this_node;
|
||||
}
|
||||
END_FOR_EACH_PTR(node);
|
||||
copy_type(node->suffixed_expr.type, prev_node->common_expr.type);
|
||||
}
|
||||
|
||||
static void insert_cast(struct ast_container *container, struct ast_node *expr, UnOpr opcode, ravitype_t target_type) {
|
||||
/* convert the node to @integer node, the original content of node goes into the subexpr */
|
||||
struct ast_node *copy_expr = dmrC_allocator_allocate(&container->ast_node_allocator, 0);
|
||||
*copy_expr = *expr;
|
||||
expr->type = AST_UNARY_EXPR;
|
||||
expr->unary_expr.expr = copy_expr;
|
||||
expr->unary_expr.unary_op = opcode;
|
||||
set_typecode(expr->unary_expr.type, target_type);
|
||||
}
|
||||
|
||||
static void typecheck_var_assignment(struct ast_container *container, struct var_type *var_type, struct ast_node *expr,
|
||||
const char *var_name) {
|
||||
if (var_type->type_code == RAVI_TANY)
|
||||
// Any value can be assigned to type ANY
|
||||
return;
|
||||
|
||||
struct var_type *expr_type = &expr->common_expr.type;
|
||||
|
||||
if (var_type->type_code == RAVI_TNUMINT) {
|
||||
/* if the expr is of type number or # operator then insert @integer operator */
|
||||
if (expr_type->type_code == RAVI_TNUMFLT ||
|
||||
(expr->type == AST_UNARY_EXPR && expr->unary_expr.unary_op == OPR_LEN)) {
|
||||
insert_cast(container, expr, OPR_TO_INTEGER, RAVI_TNUMINT);
|
||||
}
|
||||
else if (expr_type->type_code != RAVI_TNUMINT) {
|
||||
fprintf(stderr, "Assignment to local symbol %s is not type compatible\n", var_name);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (var_type->type_code == RAVI_TNUMFLT) {
|
||||
if (expr_type->type_code == RAVI_TNUMINT) {
|
||||
/* cast to number */
|
||||
insert_cast(container, expr, OPR_TO_NUMBER, RAVI_TNUMFLT);
|
||||
}
|
||||
else if (expr_type->type_code != RAVI_TNUMFLT) {
|
||||
fprintf(stderr, "Assignment to local symbol %s is not type compatible\n", var_name);
|
||||
}
|
||||
return;
|
||||
}
|
||||
// all other types must strictly match
|
||||
if (!is_type_same(*var_type, *expr_type)) { // We should probably check type convert-ability here
|
||||
fprintf(stderr, "Assignment to local symbol %s is not type compatible\n", var_name);
|
||||
}
|
||||
}
|
||||
|
||||
static void typecheck_local_statement(struct ast_container *container, struct ast_node *function,
|
||||
struct ast_node *node) {
|
||||
// The local vars should already be annotated
|
||||
// We need to typecheck the expressions to the right of =
|
||||
// Then we need to ensure that the assignments are valid
|
||||
// We can perhaps insert type assertions where we have a mismatch?
|
||||
|
||||
typecheck_ast_list(container, function, node->local_stmt.expr_list);
|
||||
|
||||
struct lua_symbol *var;
|
||||
struct ast_node *expr;
|
||||
PREPARE_PTR_LIST(node->local_stmt.var_list, var);
|
||||
PREPARE_PTR_LIST(node->local_stmt.expr_list, expr);
|
||||
|
||||
for (;;) {
|
||||
if (!var || !expr)
|
||||
break;
|
||||
|
||||
struct var_type *var_type = &var->value_type;
|
||||
const char *var_name = getstr(var->var.var_name);
|
||||
|
||||
typecheck_var_assignment(container, var_type, expr, var_name);
|
||||
|
||||
NEXT_PTR_LIST(var);
|
||||
NEXT_PTR_LIST(expr);
|
||||
}
|
||||
}
|
||||
|
||||
static void typecheck_expr_statement(struct ast_container *container, struct ast_node *function,
|
||||
struct ast_node *node) {
|
||||
if (node->expression_stmt.var_expr_list)
|
||||
typecheck_ast_list(container, function, node->expression_stmt.var_expr_list);
|
||||
typecheck_ast_list(container, function, node->expression_stmt.expr_list);
|
||||
|
||||
if (!node->expression_stmt.var_expr_list)
|
||||
return;
|
||||
|
||||
struct ast_node *var;
|
||||
struct ast_node *expr;
|
||||
PREPARE_PTR_LIST(node->expression_stmt.var_expr_list, var);
|
||||
PREPARE_PTR_LIST(node->local_stmt.expr_list, expr);
|
||||
|
||||
for (;;) {
|
||||
if (!var || !expr)
|
||||
break;
|
||||
|
||||
struct var_type *var_type = &var->common_expr.type;
|
||||
const char *var_name = ""; // FIXME how do we get this?
|
||||
|
||||
typecheck_var_assignment(container, var_type, expr, var_name);
|
||||
|
||||
NEXT_PTR_LIST(var);
|
||||
NEXT_PTR_LIST(expr);
|
||||
}
|
||||
}
|
||||
|
||||
static void typecheck_for_in_statment(struct ast_container *container, struct ast_node *function,
|
||||
struct ast_node *node) {
|
||||
typecheck_ast_list(container, function, node->for_stmt.expr_list);
|
||||
typecheck_ast_list(container, function, node->for_stmt.for_statement_list);
|
||||
}
|
||||
|
||||
static void typecheck_for_num_statment(struct ast_container *container, struct ast_node *function,
|
||||
struct ast_node *node) {
|
||||
typecheck_ast_list(container, function, node->for_stmt.expr_list);
|
||||
struct ast_node *expr;
|
||||
enum { I = 1, F = 2, A = 4 }; /* bits representing integer, number, any */
|
||||
int index_type = 0;
|
||||
FOR_EACH_PTR(node->for_stmt.expr_list, expr) {
|
||||
switch (expr->common_expr.type.type_code) {
|
||||
case RAVI_TNUMFLT:
|
||||
index_type |= F;
|
||||
break;
|
||||
case RAVI_TNUMINT:
|
||||
index_type |= I;
|
||||
break;
|
||||
default:
|
||||
index_type |= A;
|
||||
break;
|
||||
}
|
||||
if ((index_type & A) != 0)
|
||||
break;
|
||||
}
|
||||
END_FOR_EACH_PTR(expr);
|
||||
if ((index_type & A) == 0) { /* not any */
|
||||
/* for I+F we use F */
|
||||
ravitype_t symbol_type = index_type == I ? RAVI_TNUMINT : RAVI_TNUMFLT;
|
||||
struct lua_symbol_list *symbols = node->for_stmt.symbols;
|
||||
struct lua_symbol *sym;
|
||||
/* actually there will be only index variable */
|
||||
FOR_EACH_PTR(symbols, sym) {
|
||||
if (sym->symbol_type == SYM_LOCAL) {
|
||||
set_typecode(sym->value_type, symbol_type);
|
||||
}
|
||||
else {
|
||||
assert(0); /* cannot happen */
|
||||
}
|
||||
}
|
||||
END_FOR_EACH_PTR(sym);
|
||||
}
|
||||
typecheck_ast_list(container, function, node->for_stmt.for_statement_list);
|
||||
}
|
||||
|
||||
static void typecheck_if_statement(struct ast_container *container, struct ast_node *function, struct ast_node *node) {
|
||||
struct ast_node *test_then_block;
|
||||
FOR_EACH_PTR(node->if_stmt.if_condition_list, test_then_block) {
|
||||
typecheck_ast_node(container, function, test_then_block->test_then_block.condition);
|
||||
typecheck_ast_list(container, function, test_then_block->test_then_block.test_then_statement_list);
|
||||
}
|
||||
END_FOR_EACH_PTR(node);
|
||||
if (node->if_stmt.else_statement_list) {
|
||||
typecheck_ast_list(container, function, node->if_stmt.else_statement_list);
|
||||
}
|
||||
}
|
||||
|
||||
static void typecheck_while_or_repeat_statement(struct ast_container *container, struct ast_node *function,
|
||||
struct ast_node *node) {
|
||||
typecheck_ast_node(container, function, node->while_or_repeat_stmt.condition);
|
||||
if (node->while_or_repeat_stmt.loop_statement_list) {
|
||||
typecheck_ast_list(container, function, node->while_or_repeat_stmt.loop_statement_list);
|
||||
}
|
||||
}
|
||||
|
||||
/* Type checker - WIP */
|
||||
static void typecheck_ast_node(struct ast_container *container, struct ast_node *function, struct ast_node *node) {
|
||||
switch (node->type) {
|
||||
case AST_FUNCTION_EXPR: {
|
||||
typecheck_ast_list(container, function, node->function_expr.function_statement_list);
|
||||
break;
|
||||
}
|
||||
case AST_NONE: {
|
||||
break;
|
||||
}
|
||||
case AST_RETURN_STMT: {
|
||||
typecheck_ast_list(container, function, node->return_stmt.expr_list);
|
||||
break;
|
||||
}
|
||||
case AST_LOCAL_STMT: {
|
||||
typecheck_local_statement(container, function, node);
|
||||
break;
|
||||
}
|
||||
case AST_FUNCTION_STMT: {
|
||||
typecheck_ast_node(container, function, node->function_stmt.function_expr);
|
||||
break;
|
||||
}
|
||||
case AST_LABEL_STMT: {
|
||||
break;
|
||||
}
|
||||
case AST_GOTO_STMT: {
|
||||
break;
|
||||
}
|
||||
case AST_DO_STMT: {
|
||||
break;
|
||||
}
|
||||
case AST_EXPR_STMT: {
|
||||
typecheck_expr_statement(container, function, node);
|
||||
break;
|
||||
}
|
||||
case AST_IF_STMT: {
|
||||
typecheck_if_statement(container, function, node);
|
||||
break;
|
||||
}
|
||||
case AST_WHILE_STMT:
|
||||
case AST_REPEAT_STMT: {
|
||||
typecheck_while_or_repeat_statement(container, function, node);
|
||||
break;
|
||||
}
|
||||
case AST_FORIN_STMT: {
|
||||
typecheck_for_in_statment(container, function, node);
|
||||
break;
|
||||
}
|
||||
case AST_FORNUM_STMT: {
|
||||
typecheck_for_num_statment(container, function, node);
|
||||
break;
|
||||
}
|
||||
case AST_SUFFIXED_EXPR: {
|
||||
typecheck_suffixedexpr(container, function, node);
|
||||
break;
|
||||
}
|
||||
case AST_FUNCTION_CALL_EXPR: {
|
||||
if (node->function_call_expr.method_name) {
|
||||
}
|
||||
else {
|
||||
}
|
||||
typecheck_ast_list(container, function, node->function_call_expr.arg_list);
|
||||
break;
|
||||
}
|
||||
case AST_SYMBOL_EXPR: {
|
||||
/* symbol type should have been set when symbol was created */
|
||||
copy_type(node->symbol_expr.type, node->symbol_expr.var->value_type);
|
||||
break;
|
||||
}
|
||||
case AST_BINARY_EXPR: {
|
||||
typecheck_binaryop(container, function, node);
|
||||
break;
|
||||
}
|
||||
case AST_UNARY_EXPR: {
|
||||
typecheck_unaryop(container, function, node);
|
||||
break;
|
||||
}
|
||||
case AST_LITERAL_EXPR: {
|
||||
/* type set during parsing */
|
||||
break;
|
||||
}
|
||||
case AST_FIELD_SELECTOR_EXPR: {
|
||||
typecheck_ast_node(container, function, node->index_expr.expr);
|
||||
break;
|
||||
}
|
||||
case AST_Y_INDEX_EXPR: {
|
||||
typecheck_ast_node(container, function, node->index_expr.expr);
|
||||
break;
|
||||
}
|
||||
case AST_INDEXED_ASSIGN_EXPR: {
|
||||
if (node->indexed_assign_expr.index_expr) {
|
||||
typecheck_ast_node(container, function, node->indexed_assign_expr.index_expr);
|
||||
}
|
||||
typecheck_ast_node(container, function, node->indexed_assign_expr.value_expr);
|
||||
copy_type(node->indexed_assign_expr.type, node->indexed_assign_expr.value_expr->common_expr.type);
|
||||
break;
|
||||
}
|
||||
case AST_TABLE_EXPR: {
|
||||
typecheck_ast_list(container, function, node->table_expr.expr_list);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Type checker - WIP */
|
||||
static void typecheck_function(struct ast_container *container, struct ast_node *func) {
|
||||
typecheck_ast_list(container, func, func->function_expr.function_statement_list);
|
||||
}
|
||||
|
||||
/* Type checker - WIP */
|
||||
void raviA_ast_typecheck(struct ast_container *container) {
|
||||
struct ast_node *main_function = container->main_function;
|
||||
typecheck_function(container, main_function);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue