issue #198 Include Ravi Compiler

pull/212/head
Dibyendu Majumdar 3 years ago
parent 31774723ff
commit 2aeeea4dd3

@ -9,7 +9,7 @@ option(STATIC_BUILD "Build static version of Ravi, default is OFF" OFF)
option(COMPUTED_GOTO "Controls whether the interpreter switch will use computed gotos on gcc/clang, default is ON" ON)
option(LTESTS "Controls whether ltests are enabled in Debug mode; note requires Debug build" ON)
option(ASAN "Controls whether address sanitizer should be enabled" OFF)
option(RAVICOMP "Controls whether to link in RaviComp" OFF)
option(RAVICOMP "Controls whether to link in RaviComp" ON)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake")
@ -105,20 +105,22 @@ endif ()
if (RAVICOMP)
# Need MIR_JIT for the compiler add-on
find_package(RaviComp REQUIRED)
#find_package(RaviComp REQUIRED)
set(ADDON_SRCS ${RAVICOMP_SRCS})
set_property(SOURCE ${RAVICOMP_SRCS}
APPEND
PROPERTY INCLUDE_DIRECTORIES ${RAVICOMP_INCLUDE_DIRS})
if (MIR_JIT)
set_property(SOURCE ${RAVICOMP_SRCS}
APPEND
PROPERTY INCLUDE_DIRECTORIES "${CMAKE_SOURCE_DIR}/mir;${CMAKE_SOURCE_DIR}/mir/c2mir")
endif ()
if ($ENV{CLION_IDE})
# CLion seems unable to handle include paths set on sources
include_directories(${RAVICOMP_INCLUDE_DIRS})
endif ()
#set_property(SOURCE ${RAVICOMP_SRCS}
# APPEND
# PROPERTY INCLUDE_DIRECTORIES ${RAVICOMP_INCLUDE_DIRS})
#if (MIR_JIT)
# set_property(SOURCE ${RAVICOMP_SRCS}
# APPEND
# PROPERTY INCLUDE_DIRECTORIES "${CMAKE_SOURCE_DIR}/mir;${CMAKE_SOURCE_DIR}/mir/c2mir")
#endif ()
#if ($ENV{CLION_IDE})
# # CLion seems unable to handle include paths set on sources
# include_directories(${RAVICOMP_INCLUDE_DIRS})
#endif ()
add_subdirectory(ravicomp)
set(RAVICOMP_LIBRARIES ravicomp)
endif ()
# IDE stuff

@ -0,0 +1,7 @@
BasedOnStyle: LLVM
IndentWidth: 8
UseTab: Always
BreakBeforeBraces: Linux
AllowShortIfStatementsOnASingleLine: false
IndentCaseLabels: false
ColumnLimit: 120

@ -0,0 +1,110 @@
cmake_minimum_required(VERSION 3.12)
project(RaviCompiler VERSION 0.0.1 LANGUAGES C)
option(ASAN "Controls whether address sanitizer should be enabled" OFF)
set(PUBLIC_HEADERS
include/ravi_compiler.h
include/ravi_api.h)
set(HEADERS
${PUBLIC_HEADERS}
src/allocate.h
src/bitset.h
src/ptrlist.h
src/fnv_hash.h
src/graph.h
src/hash_table.h
src/set.h
src/membuf.h
src/cfg.h
src/dominator.h
src/linearizer.h
src/common.h
src/dataflow_framework.h
src/optimizer.h
src/parser.h
src/codegen.h)
set(SRCS
src/allocate.c
src/ast_walker.c
src/ast_simplify.c
src/bitset.c
src/ptrlist.c
src/fnv_hash.c
src/graph.c
src/cfg.c
src/dominator.c
src/hash_table.c
src/set.c
src/lexer.c
src/parser.c
src/ast_printer.c
src/typechecker.c
src/linearizer.c
src/dataflow_framework.c
src/opt_unusedcode.c
src/membuf.c
src/df_liveness.c
src/codegen.c
src/ravi_binding.c
)
message("SOURCE dir is ${RaviCompiler_SOURCE_DIR}")
if ($ENV{CLION_IDE})
# CLion seems unable to handle include paths set on sources
include_directories("${RaviCompiler_SOURCE_DIR}/include")
endif ()
if (WIN32)
# disable warnings about C string functions
add_compile_definitions(_CRT_SECURE_NO_WARNINGS)
endif()
include(CheckCCompilerFlag)
if (NOT MSVC)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wmissing-prototypes -Wstrict-prototypes -Werror=return-type")
if (ASAN)
set(CMAKE_REQUIRED_FLAGS "-fsanitize=address")
check_c_compiler_flag("-fsanitize=address" COMPILER_ASAN_SUPPORTED)
if (COMPILER_ASAN_SUPPORTED AND NOT CMAKE_C_FLAGS_DEBUG MATCHES "-fsanitize=address")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -fsanitize=address")
endif ()
endif ()
endif()
include(GNUInstallDirs)
set(CMAKE_VISIBILITY_INLINES_HIDDEN YES)
if (NOT WIN32)
set(EXTRA_LIBRARIES m)
endif ()
if (WIN32)
set(LIBTYPE STATIC)
else()
set(LIBTYPE SHARED)
endif()
add_library(ravicomp ${LIBTYPE}
${HEADERS}
${SRCS})
target_include_directories(ravicomp
PUBLIC "${CMAKE_CURRENT_BINARY_DIR}"
PUBLIC "${RaviCompiler_SOURCE_DIR}/include"
PRIVATE "${RaviCompiler_SOURCE_DIR}/src")
target_link_libraries(ravicomp ${EXTRA_LIBRARIES})
include(GenerateExportHeader)
generate_export_header(ravicomp)
install(FILES ${PUBLIC_HEADERS}
DESTINATION include/ravicomp)
install(TARGETS ravicomp
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT RaviCompiler_Runtime
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT RaviCompiler_Development
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT RaviCompiler_Runtime)
install(FILES
${PROJECT_BINARY_DIR}/ravicomp_export.h DESTINATION include/ravicomp
)

@ -0,0 +1,22 @@
MIT License
Copyright (c) 2019-2020 Dibyendu Majumdar
Portions Copyright (c) 19942019 Lua.org, PUC-Rio.
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,6 @@
# ravi-compiler
A compiler for Ravi and Lua that processes Lua/Ravi source code and generates C code.
This is a mirror of the project at [https://github.com/dibyendumajumdar/ravi-compiler](https://github.com/dibyendumajumdar/ravi-compiler).
Note that the command line tools are not included here.

@ -0,0 +1,36 @@
#ifndef ravicomp_RAVIAPI_H
#define ravicomp_RAVIAPI_H
#include "ravicomp_export.h"
#include "ravi_compiler.h"
#include <stdlib.h>
struct Ravi_CompilerInterface {
/* ------------------------ Inputs ------------------------------ */
void *context; /* Ravi supplied context */
const char *source; /* Source code to be compiled - managed by Ravi */
size_t source_len; /* Size of source code */
const char *source_name; /* Name of the source */
const char *compiler_options; /* flags to be passed to compiler */
char main_func_name[31]; /* Name of the generated function that when called will set up the Lua closure */
/* ------------------------- Outputs ------------------------------ */
const char* generated_code; /* Output of the compiler, must be freed by caller. */
/* ------------------------ Debugging and error handling ----------------------------------------- */
void (*debug_message)(void *context, const char *filename, long long line, const char *message);
void (*error_message)(void *context, const char *message);
};
/**
* This is the API exposed by the Compiler itself. This function is invoked by
* Ravi when it is necessary to compile some Ravi code.
* @param compiler_interface The interface expected by the compiler must be setup
* @return 0 for success, non-zero for failure
*/
RAVICOMP_EXPORT int raviX_compile(struct Ravi_CompilerInterface *compiler_interface);
#endif

@ -0,0 +1,589 @@
/*
A compiler for Ravi and Lua 5.3. This is work in progress.
Once ready it will be used to create a JIT compiler for Ravi.
This header file defines the public api
Copyright 2018-2020 Dibyendu Majumdar
*/
#ifndef ravicomp_COMPILER_H
#define ravicomp_COMPILER_H
#include "ravicomp_export.h"
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
typedef struct CompilerState CompilerState;
typedef struct LexerState LexerState;
typedef struct LinearizerState LinearizerState;
typedef struct VariableType VariableType;
typedef long long lua_Integer;
typedef double lua_Number;
/* Initialize the compiler state */
/* During compilation all data structures are stored in the compiler state */
RAVICOMP_EXPORT CompilerState *raviX_init_compiler(void);
/* Destroy the compiler state */
RAVICOMP_EXPORT void raviX_destroy_compiler(CompilerState *compiler);
/* ------------------------ LEXICAL ANALYZER API -------------------------------*/
/* Note: following enum was generate using utils/tokenenum.h */
enum TokenType {
TOK_OFS = 256,
TOK_and,
TOK_break,
TOK_do,
TOK_else,
TOK_elseif,
TOK_end,
TOK_false,
TOK_for,
TOK_function,
TOK_goto,
TOK_if,
TOK_in,
TOK_local,
TOK_defer,
TOK_nil,
TOK_not,
TOK_or,
TOK_repeat,
TOK_return,
TOK_then,
TOK_true,
TOK_until,
TOK_while,
TOK_IDIV,
TOK_CONCAT,
TOK_DOTS,
TOK_EQ,
TOK_GE,
TOK_LE,
TOK_NE,
TOK_SHL,
TOK_SHR,
TOK_DBCOLON,
TOK_TO_INTEGER,
TOK_TO_NUMBER,
TOK_TO_INTARRAY,
TOK_TO_NUMARRAY,
TOK_TO_TABLE,
TOK_TO_STRING,
TOK_TO_CLOSURE,
TOK_EOS,
TOK_FLT,
TOK_INT,
TOK_NAME,
TOK_STRING,
FIRST_RESERVED = TOK_OFS + 1,
LAST_RESERVED = TOK_while - TOK_OFS
};
/*
* Lua strings can have embedded 0 bytes therefore we
* need a string type that has a length associated with it.
*
* The compiler stores a single copy of each string so that strings
* can be compared by equality.
*/
typedef struct StringObject {
uint32_t len; /* length of the string */
int32_t reserved; /* if is this a keyword then token id else -1 */
uint32_t hash; /* hash value of the string */
const char *str; /* string data */
} StringObject;
/*
* Lua literals
*/
typedef union {
lua_Number r;
lua_Integer i;
const StringObject *ts;
} SemInfo;
typedef struct Token {
int token; /* Token value or character value; token values start from FIRST_RESERVED which is 257, values < 256
are characters */
SemInfo seminfo; /* Literal associated with the token, only valid when token is a literal or an identifier, i.e.
token is > TOK_EOS */
} Token;
/*
* Everything below should be treated as readonly; for efficiency these fields are exposed, however treat them
* as fields managed by the lexer.
*/
typedef struct {
int current; /* current character (char value as int) */
int linenumber; /* current input line counter */
int lastline; /* line number of the last token 'consumed' */
Token t; /* current token, set after call to raviX_next() */
Token lookahead; /* look ahead token, set after call to raviX_lookahead() */
} LexerInfo;
/* Following is a dynamic buffer implementation that is not strictly part of the
* compiler api but is relied upon by various compiler parts. We should perhaps avoid
* exposing it.
*
* The reason for exposing this is that we use it for getting the token string in one of the
* api calls.
*/
typedef struct {
char *buf; /* pointer to allocated memory, can be reallocated */
size_t capacity; /* allocated size */
size_t pos; /* current position in the buffer */
} TextBuffer;
/* all strings are interned and stored in a hash set, strings may have embedded
* 0 bytes therefore explicit length is necessary
*/
RAVICOMP_EXPORT const StringObject *raviX_create_string(CompilerState *compiler_state, const char *s,
uint32_t len);
/* Initialize lexical analyser. Takes as input a buffer containing Lua/Ravi source and the source name */
RAVICOMP_EXPORT LexerState *raviX_init_lexer(CompilerState *compiler_state, const char *buf,
size_t buflen, const char *source_name);
/* Gets the public part of the lexer data structure to allow access the current token. Note that the returned
* value should be treated as readonly data structure
*/
RAVICOMP_EXPORT const LexerInfo *raviX_get_lexer_info(LexerState *ls);
/* Retrieves the next token and saves it is LexState structure. If a lookahead was set then that is retrieved
* (and reset to EOS) else the next token is retrieved
*/
RAVICOMP_EXPORT void raviX_next(LexerState *ls);
/* Retrieves the next token and sets it as the lookahead. This means that a next call will get the lookahead.
* Returns the token id.
*/
RAVICOMP_EXPORT int raviX_lookahead(LexerState *ls);
/* Convert a token to text format. The token will be written to current position in mb. */
RAVICOMP_EXPORT void raviX_token2str(int token, TextBuffer *mb);
/* Release all data structures used by the lexer */
RAVICOMP_EXPORT void raviX_destroy_lexer(LexerState *);
/* ---------------- PARSER API -------------------------- */
/*
* Parse a Lua chunk (i.e. script).
* The Lua chunk will be wrapped in an anonymous Lua function (the 'main' function), so all the code
* in the chunk will be part of that function. Any functions defined in the chunk will become child functions
* of the 'main' function.
*
* Each Lua chunk / script therefore has an anonymous 'main' function. The name 'main' is just to refer
* to this function as it has no name in reality.
*
* Note that at present a new compiler state should be created when processing a Lua chunk.
*
* Returns 0 on success, non-zero on failure.
*/
RAVICOMP_EXPORT int raviX_parse(CompilerState *compiler_state, const char *buffer, size_t buflen,
const char *source_name);
/* Prints out the AST to the file */
RAVICOMP_EXPORT void raviX_output_ast(CompilerState *compiler_state, FILE *fp);
/* Performs type checks on the AST and annotates types of expressions nad variables where possible.
* As a result the AST will be modified.
*
* Returns 0 on success, non-zero on failure.
*/
RAVICOMP_EXPORT int
raviX_ast_typecheck(CompilerState *compiler_state); /* Perform type checks and assign types to AST */
/* ---------------------------- LINEARIZER API --------------------------------------- */
/* linear IR generator.
* The goal of this component is to convert the AST to a linear IR.
* This is work in progress, therefore the IR is not yet publicly exposed.
*/
RAVICOMP_EXPORT LinearizerState *raviX_init_linearizer(CompilerState *compiler_state);
/* Attempts to create linear IR for given AST.
* Returns 0 on success.
*/
RAVICOMP_EXPORT int raviX_ast_linearize(LinearizerState *linearizer);
/* Prints out the content of the linear IR */
RAVICOMP_EXPORT void raviX_output_linearizer(LinearizerState *linearizer, FILE *fp);
/* Cleanup the linearizer */
RAVICOMP_EXPORT void raviX_destroy_linearizer(LinearizerState *linearizer);
/* utilies */
RAVICOMP_EXPORT const char *raviX_get_last_error(CompilerState *compiler_state);
/* ----------------------- AST WALKING API ------------------------ */
/* Binary operators */
typedef enum BinaryOperatorType {
BINOPR_ADD,
BINOPR_SUB,
BINOPR_MUL,
BINOPR_MOD,
BINOPR_POW,
BINOPR_DIV,
BINOPR_IDIV,
BINOPR_BAND,
BINOPR_BOR,
BINOPR_BXOR,
BINOPR_SHL,
BINOPR_SHR,
BINOPR_CONCAT,
BINOPR_EQ,
BINOPR_LT,
BINOPR_LE,
BINOPR_NE,
BINOPR_GT,
BINOPR_GE,
BINOPR_AND,
BINOPR_OR,
BINOPR_NOBINOPR
} BinaryOperatorType;
RAVICOMP_EXPORT const char *raviX_get_binary_opr_str(BinaryOperatorType op);
/* Unary operators */
typedef enum UnaryOperatorType {
UNOPR_MINUS = BINOPR_NOBINOPR + 1,
UNOPR_BNOT,
UNOPR_NOT,
UNOPR_LEN,
UNOPR_TO_INTEGER,
UNOPR_TO_NUMBER,
UNOPR_TO_INTARRAY,
UNOPR_TO_NUMARRAY,
UNOPR_TO_TABLE,
UNOPR_TO_STRING,
UNOPR_TO_CLOSURE,
UNOPR_TO_TYPE,
UNOPR_NOUNOPR
} UnaryOperatorType;
RAVICOMP_EXPORT const char *raviX_get_unary_opr_str(UnaryOperatorType op);
/* Types of AST nodes */
enum AstNodeType {
AST_NONE, /* Will never be set on a properly initialized node */
STMT_RETURN,
STMT_GOTO,
STMT_LABEL,
STMT_DO,
STMT_LOCAL,
STMT_FUNCTION,
STMT_IF,
STMT_TEST_THEN,
STMT_WHILE,
STMT_FOR_IN,
STMT_FOR_NUM,
STMT_REPEAT,
STMT_EXPR, /* Also used for assignment statements */
EXPR_LITERAL,
EXPR_SYMBOL,
EXPR_Y_INDEX, /* [] operator */
EXPR_FIELD_SELECTOR, /* table field access - '.' or ':' operator */
EXPR_TABLE_ELEMENT_ASSIGN, /* table element assignment in table constructor */
EXPR_SUFFIXED,
EXPR_UNARY,
EXPR_BINARY,
EXPR_FUNCTION, /* function literal */
EXPR_TABLE_LITERAL, /* table constructor */
EXPR_FUNCTION_CALL
};
typedef struct Statement Statement;
typedef struct ReturnStatement ReturnStatement;
typedef struct LabelStatement LabelStatement;
typedef struct GotoStatement GotoStatement;
typedef struct LocalStatement LocalStatement;
typedef struct ExpressionStatement ExpressionStatement;
typedef struct FunctionStatement FunctionStatement;
typedef struct DoStatement DoStatement;
typedef struct TestThenStatement TestThenStatement;
typedef struct IfStatement IfStatement;
typedef struct WhileOrRepeatStatement WhileOrRepeatStatement;
typedef struct ForStatement ForStatement;
typedef struct Expression Expression;
typedef struct LiteralExpression LiteralExpression;
typedef struct SymbolExpression SymbolExpression;
typedef struct IndexExpression IndexExpression;
typedef struct UnaryExpression UnaryExpression;
typedef struct BinaryExpression BinaryExpression;
typedef struct FunctionExpression FunctionExpression;
typedef struct TableElementAssignmentExpression TableElementAssignmentExpression;
typedef struct TableLiteralExpression TableLiteralExpression;
typedef struct SuffixedExpression SuffixedExpression;
typedef struct FunctionCallExpression FunctionCallExpression;
typedef struct Scope Scope;
/* Types of symbols */
enum SymbolType {
SYM_LOCAL, /* lua_variable_symbol */
SYM_UPVALUE, /* lua_upvalue_symbol */
SYM_GLOBAL, /* lua_variable_symbol, Global symbols are never added to a scope so they are always looked up */
SYM_LABEL, /* lua_label_symbol */
SYM_ENV /* Special symbol type for _ENV */
};
typedef struct LuaSymbol LuaSymbol;
typedef struct LuaUpvalueSymbol LuaUpvalueSymbol;
typedef struct LuaVariableSymbol LuaVariableSymbol;
typedef struct LuaLabelSymbol LuaLabelSymbol;
/* As described before each parsed Lua script or chunk is wrapped in an anonymous 'main'
* function hence the AST root is this function.
*/
RAVICOMP_EXPORT const FunctionExpression *
raviX_ast_get_main_function(const CompilerState *compiler_state);
/* return statement walking */
RAVICOMP_EXPORT void raviX_return_statement_foreach_expression(const ReturnStatement *statement, void *userdata,
void (*callback)(void *, const Expression *expr));
/* label statement walking */
RAVICOMP_EXPORT const StringObject *raviX_label_statement_label_name(const LabelStatement *statement);
RAVICOMP_EXPORT const Scope *raviX_label_statement_label_scope(const LabelStatement *statement);
/* goto statement walking */
RAVICOMP_EXPORT const StringObject *raviX_goto_statement_label_name(const GotoStatement *statement);
RAVICOMP_EXPORT const Scope *raviX_goto_statement_scope(const GotoStatement *statement);
RAVICOMP_EXPORT bool raviX_goto_statement_is_break(const GotoStatement *statement);
/* local statement walking */
RAVICOMP_EXPORT void raviX_local_statement_foreach_expression(const LocalStatement *statement, void *userdata,
void (*callback)(void *, const Expression *expr));
RAVICOMP_EXPORT void raviX_local_statement_foreach_symbol(const LocalStatement *statement, void *userdata,
void (*callback)(void *,
const LuaVariableSymbol *expr));
/* expression or assignment statement walking */
RAVICOMP_EXPORT void
raviX_expression_statement_foreach_lhs_expression(const ExpressionStatement *statement, void *userdata,
void (*callback)(void *, const Expression *expr));
RAVICOMP_EXPORT void
raviX_expression_statement_foreach_rhs_expression(const ExpressionStatement *statement, void *userdata,
void (*callback)(void *, const Expression *expr));
/* function statement walking */
RAVICOMP_EXPORT const SymbolExpression *
raviX_function_statement_name(const FunctionStatement *statement);
RAVICOMP_EXPORT bool raviX_function_statement_is_method(const FunctionStatement *statement);
RAVICOMP_EXPORT const IndexExpression *
raviX_function_statement_method_name(const FunctionStatement *statement);
RAVICOMP_EXPORT bool raviX_function_statement_has_selectors(const FunctionStatement *statement);
RAVICOMP_EXPORT void
raviX_function_statement_foreach_selector(const FunctionStatement *statement, void *userdata,
void (*callback)(void *, const IndexExpression *expr));
RAVICOMP_EXPORT const FunctionExpression *raviX_function_ast(const FunctionStatement *statement);
/* do statement walking */
RAVICOMP_EXPORT const Scope *raviX_do_statement_scope(const DoStatement *statement);
RAVICOMP_EXPORT void raviX_do_statement_foreach_statement(const DoStatement *statement, void *userdata,
void (*callback)(void *userdata,
const Statement *statement));
/* if statement walking */
/* Lua if statements are a mix of select/case and if/else statements in
* other languages. The AST represents the initial if condition block and all subsequent
* elseif blocks as test_then_statments. The final else block is treated as an optional
* else block.
*/
RAVICOMP_EXPORT void
raviX_if_statement_foreach_test_then_statement(const IfStatement *statement, void *userdata,
void (*callback)(void *, const TestThenStatement *stmt));
RAVICOMP_EXPORT const Scope *raviX_if_then_statement_else_scope(const IfStatement *statement);
RAVICOMP_EXPORT void raviX_if_statement_foreach_else_statement(const IfStatement *statement, void *userdata,
void (*callback)(void *userdata,
const Statement *statement));
RAVICOMP_EXPORT const Scope *raviX_test_then_statement_scope(const TestThenStatement *statement);
RAVICOMP_EXPORT void
raviX_test_then_statement_foreach_statement(const TestThenStatement *statement, void *userdata,
void (*callback)(void *userdata, const Statement *statement));
RAVICOMP_EXPORT const Expression *
raviX_test_then_statement_condition(const TestThenStatement *statement);
/* while or repeat statement walking */
RAVICOMP_EXPORT const Expression *
raviX_while_or_repeat_statement_condition(const WhileOrRepeatStatement *statement);
RAVICOMP_EXPORT const Scope *
raviX_while_or_repeat_statement_scope(const WhileOrRepeatStatement *statement);
RAVICOMP_EXPORT void
raviX_while_or_repeat_statement_foreach_statement(const WhileOrRepeatStatement *statement, void *userdata,
void (*callback)(void *userdata, const Statement *statement));
/* for statement walking */
RAVICOMP_EXPORT const Scope *raviX_for_statement_scope(const ForStatement *statement);
RAVICOMP_EXPORT void raviX_for_statement_foreach_symbol(const ForStatement *statement, void *userdata,
void (*callback)(void *,
const LuaVariableSymbol *expr));
RAVICOMP_EXPORT void raviX_for_statement_foreach_expression(const ForStatement *statement, void *userdata,
void (*callback)(void *, const Expression *expr));
RAVICOMP_EXPORT const Scope *raviX_for_statement_body_scope(const ForStatement *statement);
RAVICOMP_EXPORT void raviX_for_statement_body_foreach_statement(const ForStatement *statement, void *userdata,
void (*callback)(void *userdata,
const Statement *statement));
/* literal expression */
/* Note: '...' value has type RAVI_TVARARGS and no associated SemInfo. */
RAVICOMP_EXPORT const VariableType *raviX_literal_expression_type(const LiteralExpression *expression);
RAVICOMP_EXPORT const SemInfo *raviX_literal_expression_literal(const LiteralExpression *expression);
/* symbol expression */
RAVICOMP_EXPORT const VariableType *raviX_symbol_expression_type(const SymbolExpression *expression);
RAVICOMP_EXPORT const LuaSymbol *raviX_symbol_expression_symbol(const SymbolExpression *expression);
/* index expression */
RAVICOMP_EXPORT const VariableType *raviX_index_expression_type(const IndexExpression *expression);
RAVICOMP_EXPORT const Expression *raviX_index_expression_expression(const IndexExpression *expression);
/* unary expression */
RAVICOMP_EXPORT const VariableType *raviX_unary_expression_type(const UnaryExpression *expression);
RAVICOMP_EXPORT const Expression *raviX_unary_expression_expression(const UnaryExpression *expression);
RAVICOMP_EXPORT UnaryOperatorType raviX_unary_expression_operator(const UnaryExpression *expression);
/* binary expression */
RAVICOMP_EXPORT const VariableType *raviX_binary_expression_type(const BinaryExpression *expression);
RAVICOMP_EXPORT const Expression *
raviX_binary_expression_left_expression(const BinaryExpression *expression);
RAVICOMP_EXPORT const Expression *
raviX_binary_expression_right_expression(const BinaryExpression *expression);
RAVICOMP_EXPORT BinaryOperatorType raviX_binary_expression_operator(const BinaryExpression *expression);
/* function expression */
RAVICOMP_EXPORT const VariableType *raviX_function_type(const FunctionExpression *function_expression);
RAVICOMP_EXPORT bool raviX_function_is_vararg(const FunctionExpression *function_expression);
RAVICOMP_EXPORT bool raviX_function_is_method(const FunctionExpression *function_expression);
RAVICOMP_EXPORT const FunctionExpression *
raviX_function_parent(const FunctionExpression *function_expression);
RAVICOMP_EXPORT void
raviX_function_foreach_child(const FunctionExpression *function_expression, void *userdata,
void (*callback)(void *userdata, const FunctionExpression *function_expression));
RAVICOMP_EXPORT const Scope *raviX_function_scope(const FunctionExpression *function_expression);
RAVICOMP_EXPORT void
raviX_function_foreach_statement(const FunctionExpression *function_expression, void *userdata,
void (*callback)(void *userdata, const Statement *statement));
RAVICOMP_EXPORT void
raviX_function_foreach_argument(const FunctionExpression *function_expression, void *userdata,
void (*callback)(void *userdata, const LuaVariableSymbol *symbol));
RAVICOMP_EXPORT void raviX_function_foreach_local(const FunctionExpression *function_expression, void *userdata,
void (*callback)(void *userdata,
const LuaVariableSymbol *lua_local_symbol));
RAVICOMP_EXPORT void
raviX_function_foreach_upvalue(const FunctionExpression *function_expression, void *userdata,
void (*callback)(void *userdata, const LuaUpvalueSymbol *symbol));
/* table element assignment expression */
RAVICOMP_EXPORT const VariableType *
raviX_table_element_assignment_expression_type(const TableElementAssignmentExpression *expression);
RAVICOMP_EXPORT const Expression *
raviX_table_element_assignment_expression_key(const TableElementAssignmentExpression *expression);
RAVICOMP_EXPORT const Expression *
raviX_table_element_assignment_expression_value(const TableElementAssignmentExpression *expression);
/* table_literal_expression */
RAVICOMP_EXPORT const VariableType *
raviX_table_literal_expression_type(const TableLiteralExpression *expression);
RAVICOMP_EXPORT void raviX_table_literal_expression_foreach_element(
const TableLiteralExpression *expression, void *userdata,
void (*callback)(void *, const TableElementAssignmentExpression *expr));
/* suffixed_expression */
RAVICOMP_EXPORT const VariableType *raviX_suffixed_expression_type(const SuffixedExpression *expression);
RAVICOMP_EXPORT const Expression *
raviX_suffixed_expression_primary(const SuffixedExpression *expression);
RAVICOMP_EXPORT void raviX_suffixed_expression_foreach_suffix(const SuffixedExpression *expression,
void *userdata,
void (*callback)(void *, const Expression *expr));
/* function call expression */
RAVICOMP_EXPORT const VariableType *
raviX_function_call_expression_type(const FunctionCallExpression *expression);
// can return NULL
RAVICOMP_EXPORT const StringObject *
raviX_function_call_expression_method_name(const FunctionCallExpression *expression);
RAVICOMP_EXPORT void
raviX_function_call_expression_foreach_argument(const FunctionCallExpression *expression, void *userdata,
void (*callback)(void *, const Expression *expr));
/* Convert a statement to the correct type */
RAVICOMP_EXPORT enum AstNodeType raviX_statement_type(const Statement *statement);
RAVICOMP_EXPORT const ReturnStatement *raviX_return_statement(const Statement *stmt);
RAVICOMP_EXPORT const LabelStatement *raviX_label_statement(const Statement *stmt);
RAVICOMP_EXPORT const GotoStatement *raviX_goto_statement(const Statement *stmt);
RAVICOMP_EXPORT const LocalStatement *raviX_local_statement(const Statement *stmt);
RAVICOMP_EXPORT const ExpressionStatement *raviX_expression_statement(const Statement *stmt);
RAVICOMP_EXPORT const FunctionStatement *raviX_function_statement(const Statement *stmt);
RAVICOMP_EXPORT const DoStatement *raviX_do_statement(const Statement *stmt);
RAVICOMP_EXPORT const TestThenStatement *raviX_test_then_statement(const Statement *stmt);
RAVICOMP_EXPORT const IfStatement *raviX_if_statement(const Statement *stmt);
RAVICOMP_EXPORT const WhileOrRepeatStatement *raviX_while_or_repeat_statement(const Statement *stmt);
RAVICOMP_EXPORT const ForStatement *raviX_for_statement(const Statement *stmt);
/* Convert an expression to the correct type */
RAVICOMP_EXPORT enum AstNodeType raviX_expression_type(const Expression *expression);
RAVICOMP_EXPORT const LiteralExpression *raviX_literal_expression(const Expression *expr);
RAVICOMP_EXPORT const SymbolExpression *raviX_symbol_expression(const Expression *expr);
RAVICOMP_EXPORT const IndexExpression *raviX_index_expression(const Expression *expr);
RAVICOMP_EXPORT const UnaryExpression *raviX_unary_expression(const Expression *expr);
RAVICOMP_EXPORT const BinaryExpression *raviX_binary_expression(const Expression *expr);
RAVICOMP_EXPORT const FunctionExpression *raviX_function_expression(const Expression *expr);
RAVICOMP_EXPORT const TableElementAssignmentExpression *
raviX_table_element_assignment_expression(const Expression *expr);
RAVICOMP_EXPORT const TableLiteralExpression *raviX_table_literal_expression(const Expression *expr);
RAVICOMP_EXPORT const SuffixedExpression *raviX_suffixed_expression(const Expression *expr);
RAVICOMP_EXPORT const FunctionCallExpression *raviX_function_call_expression(const Expression *expr);
RAVICOMP_EXPORT const FunctionExpression *raviX_scope_owning_function(const Scope *scope);
RAVICOMP_EXPORT const Scope *raviX_scope_parent_scope(const Scope *scope);
RAVICOMP_EXPORT void raviX_scope_foreach_symbol(const Scope *scope, void *userdata,
void (*callback)(void *userdata, const LuaSymbol *symbol));
RAVICOMP_EXPORT enum SymbolType raviX_symbol_type(const LuaSymbol *symbol);
/* symbol downcast */
RAVICOMP_EXPORT const LuaVariableSymbol *raviX_symbol_variable(const LuaSymbol *symbol);
RAVICOMP_EXPORT const LuaUpvalueSymbol *raviX_symbol_upvalue(const LuaSymbol *symbol);
RAVICOMP_EXPORT const LuaLabelSymbol *raviX_symbol_label(const LuaSymbol *symbol);
/* variable symbol - local and global variables */
RAVICOMP_EXPORT const StringObject *
raviX_variable_symbol_name(const LuaVariableSymbol *lua_local_symbol);
RAVICOMP_EXPORT const VariableType *raviX_variable_symbol_type(const LuaVariableSymbol *lua_local_symbol);
// NULL if global
RAVICOMP_EXPORT const Scope *
raviX_variable_symbol_scope(const LuaVariableSymbol *lua_local_symbol);
/* label symbol */
RAVICOMP_EXPORT const StringObject *raviX_label_name(const LuaLabelSymbol *symbol);
RAVICOMP_EXPORT const Scope *raviX_label_scope(const LuaLabelSymbol *symbol);
/* upvalue symbol */
RAVICOMP_EXPORT const VariableType *raviX_upvalue_symbol_type(const LuaUpvalueSymbol *symbol);
RAVICOMP_EXPORT const LuaVariableSymbol *
raviX_upvalue_target_variable(const LuaUpvalueSymbol *symbol);
RAVICOMP_EXPORT const FunctionExpression *
raviX_upvalue_target_function(const LuaUpvalueSymbol *symbol);
RAVICOMP_EXPORT unsigned raviX_upvalue_index(const LuaUpvalueSymbol *symbol);
/* Utilities */
#ifdef __GNUC__
#define FORMAT_ATTR(pos) __attribute__((__format__(__printf__, pos, pos + 1)))
#else
#define FORMAT_ATTR(pos)
#endif
RAVICOMP_EXPORT void raviX_buffer_init(TextBuffer *mb, size_t initial_size);
RAVICOMP_EXPORT void raviX_buffer_resize(TextBuffer *mb, size_t new_size);
RAVICOMP_EXPORT void raviX_buffer_reserve(TextBuffer *mb, size_t n);
RAVICOMP_EXPORT void raviX_buffer_free(TextBuffer *mb);
static inline char *raviX_buffer_data(const TextBuffer *mb) { return mb->buf; }
static inline size_t raviX_buffer_size(const TextBuffer *mb) { return mb->capacity; }
static inline size_t raviX_buffer_len(const TextBuffer *mb) { return mb->pos; }
static inline void raviX_buffer_reset(TextBuffer *mb) { mb->pos = 0; }
/* following convert input to string before adding */
RAVICOMP_EXPORT void raviX_buffer_add_string(TextBuffer *mb, const char *str);
RAVICOMP_EXPORT void raviX_buffer_add_bytes(TextBuffer *mb, const char *str, size_t len);
RAVICOMP_EXPORT void raviX_buffer_add_fstring(TextBuffer *mb, const char *str, ...) FORMAT_ATTR(2);
/* strncpy() replacement with guaranteed 0 termination */
RAVICOMP_EXPORT void raviX_string_copy(char *buf, const char *src, size_t buflen);
#endif

@ -0,0 +1,25 @@
# Sources
* `lexer.c` - derived from Lua 5.3 lexer but modified to work as a standalone lexer
* `parser.c` - responsible for generating abstract syntax tree (AST) - consumes lexer output.
* `ast_printer.c` - responsible for printing out the AST
* `ast_walker.c` - API for walking the AST
* `ast_simplify.c` - responsible for simplifications done on AST such as constant folding
* `typechecker.c` - responsible for performing typechecking and assigning types to various things. Runs on the AST.
* `linearizer.c` (WIP) - responsible for generating linear intermediate code (linear IR).
* `cfg.c` - responsible for constructing a control flow graph from the output of the linearizer.
* `dominator.c` - implementation of dominator tree calculation - this is not used yet
* `dataflow_framework.c` - a framework for calculating dataflow equations - not used yet
* `opt_unusedcode.c` - a simple optimization pass that deletes unreachable basic blocks
* `codegen.c` - responsible for generating C code from the linear IR
## Utilities
* `allocate.c` - memory allocator
* `fnv_hash.c` - string hashing function
* `hash_table.c` - hash table
* `set.c` - set data structure
* `ptrlist.c` - a hybrid array/linked list data structure
* `membuf.c` - dynamic memory buffer that supports formatted input - used to build strings incrementally
* `graph.c` - simple graph data structure used to generate control flow graph.
* `bitset.c` - bitset data structure

@ -0,0 +1,275 @@
/*
* allocate.c - simple space-efficient blob allocator.
*
* Copyright (C) 2003 Transmeta Corp.
* 2003-2004 Linus Torvalds
*
* 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.
*
* Simple allocator for data that doesn't get partially free'd.
* The tokenizer and parser allocate a _lot_ of small data structures
* (often just two-three bytes for things like small integers),
* and since they all depend on each other you can't free them
* individually _anyway_. So do something that is very space-
* efficient: allocate larger "blobs", and give out individual
* small bits and pieces of it with no maintenance overhead.
*/
/*
* This version is part of the Ravi Compiler project.
* Copyright (C) 2017-2020 Dibyendu Majumdar
*/
#include <allocate.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static void *blob_alloc(size_t size)
{
void *ptr;
ptr = malloc(size);
if (ptr != NULL)
memset(ptr, 0, size);
return ptr;
}
static void blob_free(void *addr, size_t size)
{
(void)size;
free(addr);
}
void raviX_allocator_init(Allocator *A, const char *name, size_t size, unsigned int alignment,
unsigned int chunking)
{
A->name_ = name;
A->blobs_ = NULL;
A->size_ = size;
A->alignment_ = alignment;
A->chunking_ = chunking;
A->freelist_ = NULL;
A->allocations = 0;
A->total_bytes = 0;
A->useful_bytes = 0;
}
void *raviX_allocator_allocate(Allocator *A, size_t extra)
{
size_t size = extra + A->size_;
size_t alignment = A->alignment_;
AllocationBlob *blob = A->blobs_;
void *retval;
if (size > A->chunking_) {
fprintf(stderr, "allocation failure: requested size %lld is larger than maximum chunk size %lld\n",
(long long)size, (long long) A->chunking_);
exit(1);
}
/*
* NOTE! The freelist only works with things that are
* (a) sufficiently aligned
* (b) use a constant size
* Don't try to free allocators that don't follow
* these rules.
*/
if (A->freelist_) {
void **p = (void **)A->freelist_;
retval = p;
A->freelist_ = *p;
memset(retval, 0, size);
return retval;
}
A->allocations++;
A->useful_bytes += size;
size = (size + alignment - 1) & ~(alignment - 1);
if (!blob || blob->left < size) {
size_t offset, chunking = A->chunking_;
AllocationBlob *newblob = (AllocationBlob *)blob_alloc(chunking);
if (!newblob) {
fprintf(stderr, "out of memory\n");
exit(1);
}
A->total_bytes += chunking;
newblob->next = blob;
blob = newblob;
A->blobs_ = newblob;
offset = offsetof(AllocationBlob, data);
offset = (offset + alignment - 1) & ~(alignment - 1);
blob->left = chunking - offset;
blob->offset = offset - offsetof(AllocationBlob, data);
}
retval = blob->data + blob->offset;
blob->offset += size;
blob->left -= size;
return retval;
}
void raviX_allocator_free(Allocator *A, void *entry)
{
void **p = (void **)entry;
*p = A->freelist_;
A->freelist_ = p;
}
void raviX_allocator_show_allocations(Allocator *A)
{
fprintf(stderr,
"%s: %d allocations, %d bytes (%d total bytes, "
"%6.2f%% usage, %6.2f average size)\n",
A->name_, (int)A->allocations, (int)A->useful_bytes, (int)A->total_bytes,
100 * (double)A->useful_bytes / A->total_bytes, (double)A->useful_bytes / A->allocations);
}
void raviX_allocator_drop_all_allocations(Allocator *A)
{
AllocationBlob *blob = A->blobs_;
A->blobs_ = NULL;
A->allocations = 0;
A->total_bytes = 0;
A->useful_bytes = 0;
A->freelist_ = NULL;
while (blob) {
AllocationBlob *next = blob->next;
blob_free(blob, A->chunking_);
blob = next;
}
}
void raviX_allocator_destroy(Allocator *A)
{
raviX_allocator_drop_all_allocations(A);
A->blobs_ = NULL;
A->allocations = 0;
A->total_bytes = 0;
A->useful_bytes = 0;
A->freelist_ = NULL;
}
void raviX_allocator_transfer(Allocator *A, Allocator *transfer_to)
{
assert(transfer_to->blobs_ == NULL);
assert(transfer_to->freelist_ == NULL);
transfer_to->blobs_ = A->blobs_;
transfer_to->allocations = A->allocations;
transfer_to->total_bytes = A->total_bytes;
transfer_to->useful_bytes = A->useful_bytes;
transfer_to->freelist_ = A->freelist_;
transfer_to->alignment_ = A->alignment_;
transfer_to->chunking_ = A->chunking_;
transfer_to->size_ = A->size_;
A->blobs_ = NULL;
A->allocations = 0;
A->total_bytes = 0;
A->useful_bytes = 0;
A->freelist_ = NULL;
}
/*
Reallocate array from old_n to new_n. If new_n is 0 then array memeory is freed.
If new_n is greater than old_n then old data is copied across and the
additional allocated space is zeroed out so caller can rely on the extra space being
initialized to zeros.
*/
void *raviX_realloc_array(void *oldp, size_t element_size, size_t old_n, size_t new_n)
{
if (new_n == 0) {
free(oldp);
return NULL;
}
assert (new_n > old_n);
size_t newsize = element_size * new_n;
void *newp = realloc(oldp, newsize);
if (!newp) {
fprintf(stderr, "out of memory\n");
abort();
}
size_t oldsize = old_n * element_size;
char *p = newp;
memset(p + oldsize, 0, newsize - oldsize);
return newp;
}
/*
Delete n elements starting at i from array a of size array_size, where sizeof(each element) is element_size.
The freed up space will be zero initialized.
*/
size_t raviX_del_array_element(void *a, size_t element_size, size_t array_size, size_t i, size_t n)
{
assert(i + n <= array_size);
char *p = (char *)a;
char *dest = p + i * element_size;
char *src = p + (i + n) * element_size;
size_t count = element_size * (array_size - n - i);
memmove(dest, src, count);
size_t new_array_size = array_size - n;
size_t newsize = element_size * new_array_size;
size_t oldsize = element_size * array_size;
memset(p + newsize, 0, oldsize - newsize);
return new_array_size;
}
#if 0
struct foo {
int a, b;
};
int raviX_test_allocator() {
Allocator alloc;
raviX_allocator_init(&alloc, "foo", sizeof(struct foo), __alignof__(struct foo),
sizeof(AllocationBlob) + sizeof(struct foo) * 2);
struct foo *t1 = (struct foo *)raviX_allocator_allocate(&alloc, 0);
if (t1 == NULL)
return 1;
if (alloc.alignment_ != __alignof__(struct foo))
return 1;
if (alloc.allocations != 1)
return 1;
if (alloc.freelist_ != NULL)
return 1;
struct foo *t2 = (struct foo *)raviX_allocator_allocate(&alloc, 0);
if (t2 != t1 + 1)
return 1;
//dmrC_allocator_show_allocations(&alloc);
raviX_allocator_free(&alloc, t1);
raviX_allocator_free(&alloc, t2);
struct foo *t3 = (struct foo *)raviX_allocator_allocate(&alloc, 0);
if (t3 != t2)
return 1;
struct foo *t4 = (struct foo *)raviX_allocator_allocate(&alloc, 0);
if (t4 != t1)
return 1;
struct foo *t5 = (struct foo *)raviX_allocator_allocate(&alloc, 0);
(void)t5;
if (alloc.total_bytes !=
(sizeof(AllocationBlob) + sizeof(struct foo) * 2) * 2)
return 1;
Allocator alloc2;
memset(&alloc2, 0, sizeof alloc2);
AllocationBlob *saved = alloc.blobs_;
raviX_allocator_transfer(&alloc, &alloc2);
if (alloc.blobs_ != NULL)
return 1;
if (alloc2.blobs_ != saved)
return 1;
raviX_allocator_destroy(&alloc2);
printf("allocator tests okay\n");
return 0;
}
#endif

@ -0,0 +1,130 @@
#ifndef ravicomp_ALLOCATOR_H
#define ravicomp_ALLOCATOR_H
/*
* allocate.c - simple space-efficient blob allocator.
*
* Copyright (C) 2003 Transmeta Corp.
* 2003-2004 Linus Torvalds
*
* 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.
*
* Simple allocator for data that doesn't get partially free'd.
* The tokenizer and parser allocate a _lot_ of small data structures
* (often just two-three bytes for things like small integers),
* and since they all depend on each other you can't free them
* individually _anyway_. So do something that is very space-
* efficient: allocate larger "blobs", and give out individual
* small bits and pieces of it with no maintenance overhead.
*/
/*
* Portions Copyright (C) 2017-2020 Dibyendu Majumdar
*/
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct AllocationBlob AllocationBlob;
struct AllocationBlob {
AllocationBlob *next;
size_t left, offset;
unsigned char data[];
};
/*
* Our "blob" allocator works on chunks that are multiples
* of this size (the underlying allocator may be a mmap that
* cannot handle smaller chunks, for example, so trying to
* allocate blobs that aren't aligned is not going to work).
*/
#define CHUNK 32768
typedef struct Allocator {
const char *name_;
AllocationBlob *blobs_;
size_t size_;
unsigned int alignment_;
unsigned int chunking_;
void *freelist_;
size_t allocations, total_bytes, useful_bytes;
} Allocator;
extern void raviX_allocator_init(Allocator *A, const char *name, size_t size, unsigned int alignment,
unsigned int chunking);
extern void *raviX_allocator_allocate(Allocator *A, size_t extra);
extern void raviX_allocator_free(Allocator *A, void *entry);
extern void raviX_allocator_show_allocations(Allocator *A);
extern void raviX_allocator_drop_all_allocations(Allocator *A);
extern void raviX_allocator_destroy(Allocator *A);
extern void raviX_allocator_transfer(Allocator *A, Allocator *transfer_to);
/*
Reallocate array from old_n to new_n. If new_n is 0 then array memory is freed.
If new_n is greater than old_n then old data is copied across and the
additional allocated space is zeroed out so caller can rely on the extra space being
initialized to zeros.
*/
extern void *raviX_realloc_array(void *oldp, size_t element_size, size_t old_n, size_t new_n);
/*
Delete n elements starting at i from array a of size array_size, where sizeof(each element) is element_size.
The freed up space will be zero initialized. Returns the new array_size.
*/
extern size_t raviX_del_array_element(void *p, size_t element_size, size_t array_size, size_t i, size_t n);
/* structure of a node */
#define DECLARE_ARRAY(array_type, TYPE) \
typedef struct array_type { \
unsigned allocated; \
unsigned count; \
TYPE *data; \
} array_type
#define array_push(A, value) \
{ \
if ((A)->count == (A)->allocated) { \
unsigned newsize = (A)->allocated += 10; \
(A)->data = raviX_realloc_array((A)->data, sizeof((A)->data[0]), (A)->allocated, newsize); \
(A)->allocated = newsize; \
} \
(A)->data[(A)->count++] = value; \
}
#define array_clearmem(A) \
{ \
raviX_realloc_array((A)->data, sizeof((A)->data[0]), (A)->allocated, 0); \
(A)->data = NULL; \
(A)->allocated = 0; \
(A)->count = 0; \
}
#ifdef __cplusplus
}
#endif
#endif

@ -0,0 +1,543 @@
/*
Copyright (C) 2018-2020 Dibyendu Majumdar
*/
#include <parser.h>
const char *raviX_get_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 "userdata";
default:
return "?";
}
}
static void printf_buf(TextBuffer *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);
raviX_buffer_add_string(buf, tbuf);
cp++;
} else if (cp[0] == '%' && cp[1] == 't') { /* string_object */
const StringObject *s = va_arg(ap, const StringObject *);
raviX_buffer_add_string(buf, s->str);
cp++;
} else if (cp[0] == '%' && cp[1] == 'T') { /* VariableType */
const VariableType *type;
type = va_arg(ap, const VariableType *);
if (type->type_code == RAVI_TUSERDATA) {
const StringObject *s = type->type_name;
raviX_buffer_add_string(buf, s->str);
} else {
raviX_buffer_add_string(buf, raviX_get_type_name(type->type_code));
}
cp++;
} else if (cp[0] == '%' && cp[1] == 's') { /* const char * */
const char *s;
s = va_arg(ap, const char *);
raviX_buffer_add_string(buf, s);
cp++;
} else if (cp[0] == '%' && cp[1] == 'c') { /* comment */
const char *s;
s = va_arg(ap, const char *);
raviX_buffer_add_fstring(buf, "--%s", s);
cp++;
} else if (cp[0] == '%' && cp[1] == 'i') { /* integer */
lua_Integer i;
i = va_arg(ap, lua_Integer);
raviX_buffer_add_longlong(buf, i);
cp++;
} else if (cp[0] == '%' && cp[1] == 'f') { /* float */
double d;
d = va_arg(ap, double);
raviX_buffer_add_fstring(buf, "%.16f", d);
cp++;
} else if (cp[0] == '%' && cp[1] == 'b') { /* boolean */
lua_Integer i;
i = va_arg(ap, lua_Integer);
raviX_buffer_add_bool(buf, i != 0);
cp++;
} else {
raviX_buffer_add_char(buf, *cp);
}
}
va_end(ap);
}
static void print_ast_node_list(TextBuffer *buf, AstNodeList *list, int level, const char *delimiter)
{
AstNode *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);
raviX_print_ast_node(buf, node, level + 1);
}
END_FOR_EACH_PTR(node);
}
static void print_statement_list(TextBuffer *buf, AstNodeList *statement_list, int level)
{
print_ast_node_list(buf, statement_list, level + 1, NULL);
}
static inline const char *get_as_str(const StringObject *ts) { return ts ? ts->str : ""; }
static void print_symbol(TextBuffer *buf, LuaSymbol *sym, int level)
{
switch (sym->symbol_type) {
case SYM_ENV: {
printf_buf(buf, "%p%t %c %s %s\n", level, sym->variable.var_name, "_ENV",
raviX_get_type_name(sym->variable.value_type.type_code), get_as_str(sym->variable.value_type.type_name));
break;
}
case SYM_GLOBAL: {
printf_buf(buf, "%p%t %c %s %s\n", level, sym->variable.var_name, "global symbol",
raviX_get_type_name(sym->variable.value_type.type_code), get_as_str(sym->variable.value_type.type_name));
break;
}
case SYM_LOCAL: {
printf_buf(buf, "%p%t %c %s %s\n", level, sym->variable.var_name, "local symbol",
raviX_get_type_name(sym->variable.value_type.type_code), get_as_str(sym->variable.value_type.type_name));
break;
}
case SYM_UPVALUE: {
printf_buf(buf, "%p%t %c %s %s\n", level, sym->upvalue.target_variable->variable.var_name, "upvalue",
raviX_get_type_name(sym->upvalue.target_variable->variable.value_type.type_code),
get_as_str(sym->upvalue.target_variable->variable.value_type.type_name));
break;
}
default:
assert(0);
}
}
static void print_symbol_name(TextBuffer *buf, LuaSymbol *sym)
{
switch (sym->symbol_type) {
case SYM_LOCAL:
case SYM_ENV:
case SYM_GLOBAL: {
printf_buf(buf, "%t", sym->variable.var_name);
break;
}
case SYM_UPVALUE: {
if (sym->upvalue.target_variable->symbol_type == SYM_ENV) {
printf_buf(buf, "%t*", sym->upvalue.target_variable->variable.var_name);
}
else {
printf_buf(buf, "%t", sym->upvalue.target_variable->variable.var_name);
}
break;
}
default:
assert(0);
}
}
static void print_symbol_list(TextBuffer *buf, LuaSymbolList *list, int level, const char *delimiter)
{
LuaSymbol *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(TextBuffer *buf, LuaSymbolList *list)
{
LuaSymbol *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);
}
const char *raviX_get_unary_opr_str(UnaryOperatorType op)
{
switch (op) {
case UNOPR_NOT:
return "not";
case UNOPR_MINUS:
return "-";
case UNOPR_BNOT:
return "~";
case UNOPR_LEN:
return "#";
case UNOPR_TO_INTEGER:
return "@integer";
case UNOPR_TO_NUMBER:
return "@number";
case UNOPR_TO_INTARRAY:
return "@integer[]";
case UNOPR_TO_NUMARRAY:
return "@number[]";
case UNOPR_TO_TABLE:
return "@table";
case UNOPR_TO_CLOSURE:
return "@closure";
case UNOPR_TO_STRING:
return "@string";
case UNOPR_TO_TYPE:
return "@<usertype>";
default:
return "";
}
}
const char *raviX_get_binary_opr_str(BinaryOperatorType op)
{
switch (op) {
case BINOPR_ADD:
return "+";
case BINOPR_SUB:
return "-";
case BINOPR_MUL:
return "*";
case BINOPR_MOD:
return "%";
case BINOPR_POW:
return "^";
case BINOPR_DIV:
return "/";
case BINOPR_IDIV:
return "//";
case BINOPR_BAND:
return "&";
case BINOPR_BOR:
return "|";
case BINOPR_BXOR:
return "~";
case BINOPR_SHL:
return "<<";
case BINOPR_SHR:
return ">>";
case BINOPR_CONCAT:
return "..";
case BINOPR_NE:
return "~=";
case BINOPR_EQ:
return "==";
case BINOPR_LT:
return "<";
case BINOPR_LE:
return "<=";
case BINOPR_GT:
return ">";
case BINOPR_GE:
return ">=";
case BINOPR_AND:
return "and";
case BINOPR_OR:
return "or";
default:
return "";
}
}
void raviX_print_ast_node(TextBuffer *buf, AstNode *node, int level)
{
switch (node->type) {
case EXPR_FUNCTION: {
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 STMT_RETURN: {
printf_buf(buf, "%preturn\n", level);
print_ast_node_list(buf, node->return_stmt.expr_list, level + 1, ",");
break;
}
case STMT_LOCAL: {
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 STMT_FUNCTION: {
raviX_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]");
raviX_print_ast_node(buf, node->function_stmt.method_name, level + 2);
}
printf_buf(buf, "%p=\n", level + 1);
raviX_print_ast_node(buf, node->function_stmt.function_expr, level + 2);
break;
}
case STMT_LABEL: {
printf_buf(buf, "%p::%t::\n", level, node->label_stmt.symbol->label.label_name);
break;
}
case STMT_GOTO: {
printf_buf(buf, "%pgoto %t\n", level, node->goto_stmt.name);
break;
}
case STMT_DO: {
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 STMT_EXPR: {
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 STMT_IF: {
AstNode *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);
raviX_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 STMT_WHILE: {
printf_buf(buf, "%pwhile\n", level);
raviX_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 STMT_REPEAT: {
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);
raviX_print_ast_node(buf, node->while_or_repeat_stmt.condition, level + 1);
printf_buf(buf, "%p%c\n", level, "[repeat end]");
break;
}
case STMT_FOR_IN: {
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 STMT_FOR_NUM: {
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 EXPR_SUFFIXED: {
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);
raviX_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 EXPR_FUNCTION_CALL: {
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 EXPR_SYMBOL: {
print_symbol(buf, node->symbol_expr.var, level + 1);
break;
}
case EXPR_BINARY: {
printf_buf(buf, "%p%c %T\n", level, "[binary expr start]", &node->binary_expr.type);
raviX_print_ast_node(buf, node->binary_expr.expr_left, level + 1);
printf_buf(buf, "%p%s\n", level, raviX_get_binary_opr_str(node->binary_expr.binary_op));
raviX_print_ast_node(buf, node->binary_expr.expr_right, level + 1);
printf_buf(buf, "%p%c\n", level, "[binary expr end]");
break;
}
case EXPR_UNARY: {
printf_buf(buf, "%p%c %T\n", level, "[unary expr start]", &node->unary_expr.type);
printf_buf(buf, "%p%s\n", level, raviX_get_unary_opr_str(node->unary_expr.unary_op));
raviX_print_ast_node(buf, node->unary_expr.expr, level + 1);
printf_buf(buf, "%p%c\n", level, "[unary expr end]");
break;
}
case EXPR_LITERAL: {
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.r);
break;
case RAVI_TSTRING:
printf_buf(buf, "'%t'", node->literal_expr.u.ts);
break;
case RAVI_TVARARGS:
printf_buf(buf, "...");
break;
default:
assert(0);
}
printf_buf(buf, "\n");
break;
}
case EXPR_FIELD_SELECTOR: {
printf_buf(buf, "%p%c %T\n", level, "[field selector start]", &node->index_expr.type);
printf_buf(buf, "%p.\n", level + 1);
raviX_print_ast_node(buf, node->index_expr.expr, level + 2);
printf_buf(buf, "%p%c\n", level, "[field selector end]");
break;
}
case EXPR_Y_INDEX: {
printf_buf(buf, "%p%c %T\n", level, "[Y index start]", &node->index_expr.type);
printf_buf(buf, "%p[\n", level + 1);
raviX_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 EXPR_TABLE_ELEMENT_ASSIGN: {
printf_buf(buf, "%p%c %T\n", level, "[indexed assign start]", &node->table_elem_assign_expr.type);
if (node->table_elem_assign_expr.key_expr) {
printf_buf(buf, "%p%c\n", level, "[index start]");
raviX_print_ast_node(buf, node->table_elem_assign_expr.key_expr, level + 1);
printf_buf(buf, "%p%c\n", level, "[index end]");
}
printf_buf(buf, "%p%c\n", level, "[value start]");
raviX_print_ast_node(buf, node->table_elem_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 EXPR_TABLE_LITERAL: {
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);
}
}
void raviX_output_ast(CompilerState *container, FILE *fp)
{
TextBuffer mbuf;
raviX_buffer_init(&mbuf, 1024);
raviX_print_ast_node(&mbuf, container->main_function, 0);
fputs(mbuf.buf, fp);
raviX_buffer_free(&mbuf);
}

@ -0,0 +1,516 @@
/* Replace constant expressions with constants, and simply any other expressions if possible */
/* Portions Copyright (C) 1994-2019 Lua.org, PUC-Rio.*/
#include <parser.h>
#include <math.h>
#include <string.h>
static void process_expression_list(CompilerState *container, AstNodeList *node);
static void process_statement_list(CompilerState *container, AstNodeList *node);
static void process_statement(CompilerState *container, AstNode *node);
#define l_mathop(op) op
#ifndef CHAR_BIT
#define CHAR_BIT 8
#endif
/* number of bits in an integer */
#define NBITS ((int)(sizeof(lua_Integer) * CHAR_BIT))
/*
@@ lua_numbertointeger converts a float number with an integral value
** to an integer, or returns 0 if float is not within the range of
** a lua_Integer. (The range comparisons are tricky because of
** rounding. The tests here assume a two-complement representation,
** where MININTEGER always has an exact representation as a float;
** MAXINTEGER may not have one, and therefore its conversion to float
** may have an ill-defined value.)
*/
#define lua_numbertointeger(n, p) \
((n) >= (lua_Number)(LUA_MININTEGER) && (n) < -(lua_Number)(LUA_MININTEGER) && (*(p) = (lua_Integer)(n), 1))
/*
** Rounding modes for float->integer coercion
*/
typedef enum {
F2Ieq, /* no rounding; accepts only integral values */
F2Ifloor, /* takes the floor of the number */
F2Iceil /* takes the ceil of the number */
} F2Imod;
#if !defined(LUA_FLOORN2I)
#define LUA_FLOORN2I F2Ieq
#endif
/*
** The luai_num* macros define the primitive operations over numbers.
*/
/* floor division (defined as 'floor(a/b)') */
#if !defined(luai_numidiv)
#define luai_numidiv(a, b) (l_floor(luai_numdiv(a, b)))
#endif
/* float division */
#if !defined(luai_numdiv)
#define luai_numdiv(a, b) ((a) / (b))
#endif
/*
** modulo: defined as 'a - floor(a/b)*b'; this definition gives NaN when
** 'b' is huge, but the result should be 'a'. 'fmod' gives the result of
** 'a - trunc(a/b)*b', and therefore must be corrected when 'trunc(a/b)
** ~= floor(a/b)'. That happens when the division has a non-integer
** negative result, which is equivalent to the test below.
*/
#if !defined(luai_nummod)
#define luai_nummod(a, b, m) \
{ \
(m) = l_mathop(fmod)(a, b); \
if ((m) * (b) < 0) \
(m) += (b); \
}
#endif
#define l_floor(x) (l_mathop(floor)(x))
/* exponentiation */
#if !defined(luai_numpow)
#define luai_numpow(a, b) (l_mathop(pow)(a, b))
#endif
/* the others are quite standard operations */
#if !defined(luai_numadd)
#define luai_numadd(a, b) ((a) + (b))
#define luai_numsub(a, b) ((a) - (b))
#define luai_nummul(a, b) ((a) * (b))
#define luai_numunm(a) (-(a))
#define luai_numeq(a, b) ((a) == (b))
#define luai_numlt(a, b) ((a) < (b))
#define luai_numle(a, b) ((a) <= (b))
#define luai_numisnan(a) (!luai_numeq((a), (a)))
#endif
/* cast a signed lua_Integer to lua_Unsigned */
#if !defined(l_castS2U)
#define l_castS2U(i) ((lua_Unsigned)(i))
#endif
/*
** cast a lua_Unsigned to a signed lua_Integer; this cast is
** not strict ISO C, but two-complement architectures should
** work fine.
*/
#if !defined(l_castU2S)
#define l_castU2S(i) ((lua_Integer)(i))
#endif
/*
** macros to improve jump prediction (used mainly for error handling)
*/
#if !defined(likely)
#if defined(__GNUC__)
#define likely(x) (__builtin_expect(((x) != 0), 1))
#define unlikely(x) (__builtin_expect(((x) != 0), 0))
#else
#define likely(x) (x)
#define unlikely(x) (x)
#endif
#endif
#define cast(t, exp) ((t)(exp))
#define cast_num(i) cast(lua_Number, (i))
#define cast_int(i) cast(int, (i))
#define ttisfloat(o) (o->type.type_code == RAVI_TNUMFLT)
#define ttisinteger(o) (o->type.type_code == RAVI_TNUMINT)
#define fltvalue(o) (o->u.r)
#define ivalue(o) (o->u.i)
#define setivalue(o, v) (o->type.type_code = RAVI_TNUMINT, o->u.i = (v))
#define setfltvalue(o, v) (o->type.type_code = RAVI_TNUMFLT, o->u.r = (v))
/* convert an object to a float (without string coercion) */
#define tonumberns(o, n) (ttisfloat(o) ? ((n) = fltvalue(o), 1) : (ttisinteger(o) ? ((n) = cast_num(ivalue(o)), 1) : 0))
/* convert an object to an integer (including string coercion) */
#define tointeger(o, i) (RAVI_LIKELY(ttisinteger(o)) ? (*(i) = ivalue(o), 1) : luaV_tointeger(o, i, LUA_FLOORN2I))
/* convert an object to an integer (without string coercion) */
#define tointegerns(o, i) (ttisinteger(o) ? (*(i) = ivalue(o), 1) : luaV_tointegerns(o, i, LUA_FLOORN2I))
static void handle_error(CompilerState *container, const char *msg)
{
// TODO source and line number
raviX_buffer_add_string(&container->error_message, msg);
longjmp(container->env, 1);
}
/*
** Integer division; return 'm // n', that is, floor(m/n).
** C division truncates its result (rounds towards zero).
** 'floor(q) == trunc(q)' when 'q >= 0' or when 'q' is integer,
** otherwise 'floor(q) == trunc(q) - 1'.
*/
static lua_Integer luaV_idiv(CompilerState *compiler_state, lua_Integer m, lua_Integer n)
{
if (unlikely(l_castS2U(n) + 1u <= 1u)) { /* special cases: -1 or 0 */
if (n == 0)
handle_error(compiler_state, "attempt to divide by zero");
return 0 - m; /* n==-1; avoid overflow with 0x80000...//-1 */
} else {
lua_Integer q = m / n; /* perform C division */
if ((m ^ n) < 0 && m % n != 0) /* 'm/n' would be negative non-integer? */
q -= 1; /* correct result for different rounding */
return q;
}
}
/*
** Integer modulus; return 'm % n'. (Assume that C '%' with
** negative operands follows C99 behavior. See previous comment
** about luaV_idiv.)
*/
static lua_Integer luaV_mod(CompilerState *compiler_state, lua_Integer m, lua_Integer n)
{
if (unlikely(l_castS2U(n) + 1u <= 1u)) { /* special cases: -1 or 0 */
if (n == 0)
handle_error(compiler_state, "attempt to perform 'n%%0'");
return 0; /* m % -1 == 0; avoid overflow with 0x80000...%-1 */
} else {
lua_Integer r = m % n;
if (r != 0 && (r ^ n) < 0) /* 'm/n' would be non-integer negative? */
r += n; /* correct result for different rounding */
return r;
}
}
/*
** Float modulus
*/
static lua_Number luaV_modf(lua_Number m, lua_Number n)
{
lua_Number r;
luai_nummod(m, n, r);
return r;
}
/*
** Shift left operation. (Shift right just negates 'y'.)
*/
static lua_Integer luaV_shiftl(lua_Integer x, lua_Integer y)
{
if (y < 0) { /* shift right? */
if (y <= -NBITS)
return 0;
else
return x >> (-y);
} else { /* shift left */
if (y >= NBITS)
return 0;
else
return x << y;
}
}
static lua_Integer intarith(CompilerState *compiler_state, int op, lua_Integer v1, lua_Integer v2)
{
switch (op) {
case BINOPR_ADD:
return v1 + v2;
case BINOPR_SUB:
return v1 - v2;
case BINOPR_MUL:
return v1 * v2;
case BINOPR_MOD:
return luaV_mod(compiler_state, v1, v2);
case BINOPR_IDIV:
return luaV_idiv(compiler_state, v1, v2);
case BINOPR_BAND:
return v1 & v2;
case BINOPR_BOR:
return v1 | v2;
case BINOPR_BXOR:
return v1 ^ v2;
case BINOPR_SHL:
return luaV_shiftl(v1, v2);
case BINOPR_SHR:
return luaV_shiftl(v1, -v2);
case UNOPR_MINUS:
return 0 - v1;
case UNOPR_BNOT:
return ~l_castS2U(0) ^ v1;
default:
assert(0);
return 0;
}
}
static lua_Number numarith(CompilerState *compiler_state, int op, lua_Number v1, lua_Number v2)
{
switch (op) {
case BINOPR_ADD:
return luai_numadd(v1, v2);
case BINOPR_SUB:
return luai_numsub(v1, v2);
case BINOPR_MUL:
return luai_nummul(v1, v2);
case BINOPR_DIV:
return luai_numdiv(v1, v2);
case BINOPR_POW:
return luai_numpow(v1, v2);
case BINOPR_IDIV:
return luai_numidiv(v1, v2);
case UNOPR_MINUS:
return luai_numunm(v1);
case BINOPR_MOD:
return luaV_modf(v1, v2);
default:
assert(0);
return 0;
}
}
/*
** try to convert a float to an integer, rounding according to 'mode'.
*/
static int luaV_flttointeger(lua_Number n, lua_Integer *p, F2Imod mode)
{
lua_Number f = l_floor(n);
if (n != f) { /* not an integral value? */
if (mode == F2Ieq)
return 0; /* fails if mode demands integral value */
else if (mode == F2Iceil) /* needs ceil? */
f += 1; /* convert floor to ceil (remember: n != f) */
}
return lua_numbertointeger(f, p);
}
/*
** try to convert a value to an integer, rounding according to 'mode',
** without string coercion.
** ("Fast track" handled by macro 'tointegerns'.)
*/
static int luaV_tointegerns(const LiteralExpression *obj, lua_Integer *p, F2Imod mode)
{
if (ttisfloat(obj))
return luaV_flttointeger(fltvalue(obj), p, mode);
else if (ttisinteger(obj)) {
*p = ivalue(obj);
return 1;
} else
return 0;
}
static int luaO_rawarith(CompilerState *compiler_state, int op, const LiteralExpression *p1,
const LiteralExpression *p2, LiteralExpression *res)
{
switch (op) {
case BINOPR_BAND:
case BINOPR_BOR:
case BINOPR_BXOR:
case BINOPR_SHL:
case BINOPR_SHR:
case UNOPR_BNOT: { /* operate only on integers */
lua_Integer i1;
lua_Integer i2;
if (tointegerns(p1, &i1) && tointegerns(p2, &i2)) {
setivalue(res, intarith(compiler_state, op, i1, i2));
return 1;
} else
return 0; /* fail */
}
case BINOPR_DIV:
case BINOPR_POW: { /* operate only on floats */
lua_Number n1;
lua_Number n2;
if (tonumberns(p1, n1) && tonumberns(p2, n2)) {
setfltvalue(res, numarith(compiler_state, op, n1, n2));
return 1;
} else
return 0; /* fail */
}
default: { /* other operations */
lua_Number n1;
lua_Number n2;
if (ttisinteger(p1) && ttisinteger(p2)) {
setivalue(res, intarith(compiler_state, op, ivalue(p1), ivalue(p2)));
return 1;
} else if (tonumberns(p1, n1) && tonumberns(p2, n2)) {
setfltvalue(res, numarith(compiler_state, op, n1, n2));
return 1;
} else
return 0; /* fail */
}
}
}
static void process_expression(CompilerState *container, AstNode *node)
{
switch (node->type) {
case EXPR_FUNCTION:
process_statement_list(container, node->function_expr.function_statement_list);
break;
case EXPR_SUFFIXED:
process_expression(container, node->suffixed_expr.primary_expr);
if (node->suffixed_expr.suffix_list) {
process_expression_list(container, node->suffixed_expr.suffix_list);
} else {
// We can simplify and get rid of the suffixed expr
// TODO free primary_expr
memcpy(node, node->suffixed_expr.primary_expr, sizeof(AstNode));
}
break;
case EXPR_FUNCTION_CALL:
process_expression_list(container, node->function_call_expr.arg_list);
break;
case EXPR_SYMBOL:
break;
case EXPR_BINARY:
process_expression(container, node->binary_expr.expr_left);
process_expression(container, node->binary_expr.expr_right);
if (node->binary_expr.expr_left->type == EXPR_LITERAL &&
node->binary_expr.expr_right->type == EXPR_LITERAL &&
node->binary_expr.binary_op >= BINOPR_ADD &&
node->binary_expr.binary_op <= BINOPR_SHR) {
LiteralExpression result = {.type.type_code = RAVI_TANY};
if (luaO_rawarith(container, node->binary_expr.binary_op,
&node->binary_expr.expr_left->literal_expr,
&node->binary_expr.expr_right->literal_expr, &result)) {
node->type = EXPR_LITERAL;
node->literal_expr.type.type_code = result.type.type_code;
if (node->literal_expr.type.type_code == RAVI_TNUMFLT)
node->literal_expr.u.r = result.u.r;
else {
assert(node->literal_expr.type.type_code == RAVI_TNUMINT);
node->literal_expr.u.i = result.u.i;
}
// TODO free expr_left and expr_right
}
}
break;
case EXPR_UNARY:
process_expression(container, node->unary_expr.expr);
if (node->unary_expr.expr->type == EXPR_LITERAL &&
(node->unary_expr.unary_op == UNOPR_BNOT || node->unary_expr.unary_op == UNOPR_MINUS)) {
LiteralExpression result = {.type.type_code = RAVI_TANY};
if (luaO_rawarith(container, node->unary_expr.unary_op, &node->unary_expr.expr->literal_expr,
&node->unary_expr.expr->literal_expr, &result)) {
node->type = EXPR_LITERAL;
node->literal_expr.type.type_code = result.type.type_code;
if (node->literal_expr.type.type_code == RAVI_TNUMFLT)
node->literal_expr.u.r = result.u.r;
else {
assert(node->literal_expr.type.type_code == RAVI_TNUMINT);
node->literal_expr.u.i = result.u.i;
}
// TODO free unary_expr.expr
}
}
break;
case EXPR_LITERAL:
break;
case EXPR_FIELD_SELECTOR:
process_expression(container, node->index_expr.expr);
break;
case EXPR_Y_INDEX:
process_expression(container, node->index_expr.expr);
break;
case EXPR_TABLE_ELEMENT_ASSIGN:
if (node->table_elem_assign_expr.key_expr) {
process_expression(container, node->table_elem_assign_expr.key_expr);
}
process_expression(container, node->table_elem_assign_expr.value_expr);
break;
case EXPR_TABLE_LITERAL:
process_expression_list(container, node->table_expr.expr_list);
break;
default:
assert(0);
break;
}
}
static void process_expression_list(CompilerState *container, AstNodeList *list)
{
AstNode *node;
FOR_EACH_PTR(list, node) { process_expression(container, node); }
END_FOR_EACH_PTR(node);
}
static void process_statement_list(CompilerState *container, AstNodeList *list)
{
AstNode *node;
FOR_EACH_PTR(list, node) { process_statement(container, node); }
END_FOR_EACH_PTR(node);
}
static void process_statement(CompilerState *container, AstNode *node)
{
switch (node->type) {
case AST_NONE:
break;
case STMT_RETURN:
process_expression_list(container, node->return_stmt.expr_list);
break;
case STMT_LOCAL:
process_expression_list(container, node->local_stmt.expr_list);
break;
case STMT_FUNCTION:
process_expression(container, node->function_stmt.function_expr);
break;
case STMT_LABEL:
case STMT_GOTO:
break;
case STMT_DO:
process_statement_list(container, node->do_stmt.do_statement_list);
break;
case STMT_EXPR:
if (node->expression_stmt.var_expr_list) {
process_expression_list(container, node->expression_stmt.var_expr_list);
}
process_expression_list(container, node->expression_stmt.expr_list);
break;
case STMT_IF: {
AstNode *test_then_block;
FOR_EACH_PTR(node->if_stmt.if_condition_list, test_then_block)
{
process_expression(container, test_then_block->test_then_block.condition);
process_statement_list(container, test_then_block->test_then_block.test_then_statement_list);
}
END_FOR_EACH_PTR(node);
if (node->if_stmt.else_block) {
process_statement_list(container, node->if_stmt.else_statement_list);
}
break;
}
case STMT_WHILE:
process_expression(container, node->while_or_repeat_stmt.condition);
process_statement_list(container, node->while_or_repeat_stmt.loop_statement_list);
break;
case STMT_REPEAT:
process_statement_list(container, node->while_or_repeat_stmt.loop_statement_list);
process_expression(container, node->while_or_repeat_stmt.condition);
break;
case STMT_FOR_IN:
case STMT_FOR_NUM:
process_expression_list(container, node->for_stmt.expr_list);
process_statement_list(container, node->for_stmt.for_statement_list);
break;
default:
fprintf(stderr, "AST = %d\n", node->type);
assert(0);
break;
}
}
int raviX_ast_simplify(CompilerState *container)
{
int rc = setjmp(container->env);
if (rc == 0) {
process_expression(container, container->main_function);
} else {
// dump it?
}
return rc;
}

@ -0,0 +1,617 @@
#include <ravi_compiler.h>
#include <parser.h>
const FunctionExpression *raviX_ast_get_main_function(const CompilerState *compiler_state)
{
return &compiler_state->main_function->function_expr;
}
const VariableType *raviX_function_type(const FunctionExpression *function_expression)
{
return &function_expression->type;
}
bool raviX_function_is_vararg(const FunctionExpression *function_expression)
{
return function_expression->is_vararg;
}
bool raviX_function_is_method(const FunctionExpression *function_expression)
{
return function_expression->is_method;
}
const FunctionExpression *raviX_function_parent(const FunctionExpression *function_expression)
{
if (function_expression->parent_function == NULL)
return NULL;
else
return &function_expression->parent_function->function_expr;
}
void raviX_function_foreach_child(const FunctionExpression *function_expression, void *userdata,
void (*callback)(void *userdata,
const FunctionExpression *function_expression))
{
AstNode *node;
FOR_EACH_PTR(function_expression->child_functions, node) { callback(userdata, &node->function_expr); }
END_FOR_EACH_PTR(node)
}
const Scope *raviX_function_scope(const FunctionExpression *function_expression)
{
return function_expression->main_block;
}
void raviX_function_foreach_statement(const FunctionExpression *function_expression, void *userdata,
void (*callback)(void *userdata, const Statement *statement))
{
AstNode *node;
FOR_EACH_PTR(function_expression->function_statement_list, node)
{
assert(node->type <= STMT_EXPR);
callback(userdata, (Statement *)node);
}
END_FOR_EACH_PTR(node)
}
enum AstNodeType raviX_statement_type(const Statement *statement) { return statement->type; }
void raviX_function_foreach_argument(const FunctionExpression *function_expression, void *userdata,
void (*callback)(void *userdata, const LuaVariableSymbol *symbol))
{
LuaSymbol *symbol;
FOR_EACH_PTR(function_expression->args, symbol) { callback(userdata, &symbol->variable); }
END_FOR_EACH_PTR(symbol)
}
void raviX_function_foreach_local(const FunctionExpression *function_expression, void *userdata,
void (*callback)(void *userdata, const LuaVariableSymbol *lua_local_symbol))
{
LuaSymbol *symbol;
FOR_EACH_PTR(function_expression->locals, symbol) { callback(userdata, &symbol->variable); }
END_FOR_EACH_PTR(symbol)
}
void raviX_function_foreach_upvalue(const FunctionExpression *function_expression, void *userdata,
void (*callback)(void *userdata, const LuaUpvalueSymbol *symbol))
{
LuaSymbol *symbol;
FOR_EACH_PTR(function_expression->upvalues, symbol) { callback(userdata, &symbol->upvalue); }
END_FOR_EACH_PTR(symbol)
}
const StringObject *raviX_variable_symbol_name(const LuaVariableSymbol *lua_local_symbol)
{
return lua_local_symbol->var_name;
}
const VariableType *raviX_variable_symbol_type(const LuaVariableSymbol *lua_local_symbol)
{
return &lua_local_symbol->value_type;
}
const Scope *raviX_variable_symbol_scope(const LuaVariableSymbol *lua_local_symbol)
{
return lua_local_symbol->block;
}
#define n(v) ((AstNode *)v)
const ReturnStatement *raviX_return_statement(const Statement *stmt)
{
assert(stmt->type == STMT_RETURN);
return &n(stmt)->return_stmt;
}
const LabelStatement *raviX_label_statement(const Statement *stmt)
{
assert(stmt->type == STMT_LABEL);
return &n(stmt)->label_stmt;
}
const GotoStatement *raviX_goto_statement(const Statement *stmt)
{
assert(stmt->type == STMT_GOTO);
return &n(stmt)->goto_stmt;
}
const LocalStatement *raviX_local_statement(const Statement *stmt)
{
assert(stmt->type == STMT_LOCAL);
return &n(stmt)->local_stmt;
}
const ExpressionStatement *raviX_expression_statement(const Statement *stmt)
{
assert(stmt->type == STMT_EXPR);
return &n(stmt)->expression_stmt;
}
const FunctionStatement *raviX_function_statement(const Statement *stmt)
{
assert(stmt->type == STMT_FUNCTION);
return &n(stmt)->function_stmt;
}
const DoStatement *raviX_do_statement(const Statement *stmt)
{
assert(stmt->type == STMT_DO);
return &n(stmt)->do_stmt;
}
const TestThenStatement *raviX_test_then_statement(const Statement *stmt)
{
assert(stmt->type == STMT_TEST_THEN);
return &n(stmt)->test_then_block;
}
const IfStatement *raviX_if_statement(const Statement *stmt)
{
assert(stmt->type == STMT_IF);
return &n(stmt)->if_stmt;
}
const WhileOrRepeatStatement *raviX_while_or_repeat_statement(const Statement *stmt)
{
assert(stmt->type == STMT_WHILE || stmt->type == STMT_REPEAT);
return &n(stmt)->while_or_repeat_stmt;
}
const ForStatement *raviX_for_statement(const Statement *stmt)
{
assert(stmt->type == STMT_FOR_IN || stmt->type == STMT_FOR_NUM);
return &n(stmt)->for_stmt;
}
enum AstNodeType raviX_expression_type(const Expression *expression) { return expression->type; }
const LiteralExpression *raviX_literal_expression(const Expression *expr)
{
assert(expr->type == EXPR_LITERAL);
return &n(expr)->literal_expr;
}
const SymbolExpression *raviX_symbol_expression(const Expression *expr)
{
assert(expr->type == EXPR_SYMBOL);
return &n(expr)->symbol_expr;
}
const IndexExpression *raviX_index_expression(const Expression *expr)
{
assert(expr->type == EXPR_Y_INDEX || expr->type == EXPR_FIELD_SELECTOR);
return &n(expr)->index_expr;
}
const UnaryExpression *raviX_unary_expression(const Expression *expr)
{
assert(expr->type == EXPR_UNARY);
return &n(expr)->unary_expr;
}
const BinaryExpression *raviX_binary_expression(const Expression *expr)
{
assert(expr->type == EXPR_BINARY);
return &n(expr)->binary_expr;
}
const FunctionExpression *raviX_function_expression(const Expression *expr)
{
assert(expr->type == EXPR_FUNCTION);
return &n(expr)->function_expr;
}
const TableElementAssignmentExpression *
raviX_table_element_assignment_expression(const Expression *expr)
{
assert(expr->type == EXPR_TABLE_ELEMENT_ASSIGN);
return &n(expr)->table_elem_assign_expr;
}
const TableLiteralExpression *raviX_table_literal_expression(const Expression *expr)
{
assert(expr->type == EXPR_TABLE_LITERAL);
return &n(expr)->table_expr;
}
const SuffixedExpression *raviX_suffixed_expression(const Expression *expr)
{
assert(expr->type == EXPR_SUFFIXED);
return &n(expr)->suffixed_expr;
}
const FunctionCallExpression *raviX_function_call_expression(const Expression *expr)
{
assert(expr->type == EXPR_FUNCTION_CALL);
return &n(expr)->function_call_expr;
}
#undef n
void raviX_return_statement_foreach_expression(const ReturnStatement *statement, void *userdata,
void (*callback)(void *, const Expression *expr))
{
AstNode *node;
FOR_EACH_PTR(statement->expr_list, node)
{
assert(node->type >= EXPR_LITERAL && node->type <= EXPR_FUNCTION_CALL);
callback(userdata, (Expression *)node);
}
END_FOR_EACH_PTR(node)
}
const StringObject *raviX_label_statement_label_name(const LabelStatement *statement)
{
return statement->symbol->label.label_name;
}
const Scope *raviX_label_statement_label_scope(const LabelStatement *statement)
{
return statement->symbol->label.block;
}
const StringObject *raviX_goto_statement_label_name(const GotoStatement *statement)
{
return statement->name;
}
const Scope *raviX_goto_statement_scope(const GotoStatement *statement)
{
return statement->goto_scope;
}
bool raviX_goto_statement_is_break(const GotoStatement *statement) { return statement->is_break; }
void raviX_local_statement_foreach_expression(const LocalStatement *statement, void *userdata,
void (*callback)(void *, const Expression *expr))
{
AstNode *node;
FOR_EACH_PTR(statement->expr_list, node)
{
assert(node->type >= EXPR_LITERAL && node->type <= EXPR_FUNCTION_CALL);
callback(userdata, (Expression *)node);
}
END_FOR_EACH_PTR(node)
}
void raviX_local_statement_foreach_symbol(const LocalStatement *statement, void *userdata,
void (*callback)(void *, const LuaVariableSymbol *expr))
{
LuaSymbol *symbol;
FOR_EACH_PTR(statement->var_list, symbol)
{
assert(symbol->symbol_type == SYM_LOCAL);
callback(userdata, &symbol->variable);
}
END_FOR_EACH_PTR(node)
}
void raviX_expression_statement_foreach_lhs_expression(const ExpressionStatement *statement, void *userdata,
void (*callback)(void *, const Expression *expr))
{
AstNode *node;
FOR_EACH_PTR(statement->var_expr_list, node)
{
assert(node->type >= EXPR_LITERAL && node->type <= EXPR_FUNCTION_CALL);
callback(userdata, (Expression *)node);
}
END_FOR_EACH_PTR(node)
}
void raviX_expression_statement_foreach_rhs_expression(const ExpressionStatement *statement, void *userdata,
void (*callback)(void *, const Expression *expr))
{
AstNode *node;
FOR_EACH_PTR(statement->expr_list, node)
{
assert(node->type >= EXPR_LITERAL && node->type <= EXPR_FUNCTION_CALL);
callback(userdata, (Expression *)node);
}
END_FOR_EACH_PTR(node)
}
const SymbolExpression *raviX_function_statement_name(const FunctionStatement *statement)
{
assert(statement->name->type == EXPR_SYMBOL);
return &statement->name->symbol_expr;
}
bool raviX_function_statement_is_method(const FunctionStatement *statement)
{
return statement->method_name != NULL;
}
const IndexExpression *raviX_function_statement_method_name(const FunctionStatement *statement)
{
assert(statement->method_name->type == EXPR_Y_INDEX || statement->method_name->type == EXPR_FIELD_SELECTOR);
return &statement->method_name->index_expr;
}
bool raviX_function_statement_has_selectors(const FunctionStatement *statement)
{
return statement->selectors != NULL;
}
void raviX_function_statement_foreach_selector(const FunctionStatement *statement, void *userdata,
void (*callback)(void *, const IndexExpression *expr))
{
AstNode *node;
FOR_EACH_PTR(statement->selectors, node)
{
assert(node->type == EXPR_Y_INDEX || node->type == EXPR_FIELD_SELECTOR);
callback(userdata, &node->index_expr);
}
END_FOR_EACH_PTR(node)
}
const FunctionExpression *raviX_function_ast(const FunctionStatement *statement)
{
assert(statement->function_expr->type == EXPR_FUNCTION);
return &statement->function_expr->function_expr;
}
const Scope *raviX_do_statement_scope(const DoStatement *statement) { return statement->scope; }
void raviX_do_statement_foreach_statement(const DoStatement *statement, void *userdata,
void (*callback)(void *userdata, const Statement *statement))
{
AstNode *node;
FOR_EACH_PTR(statement->do_statement_list, node)
{
assert(node->type <= STMT_EXPR);
callback(userdata, (Statement *)node);
}
END_FOR_EACH_PTR(node)
}
const Scope *raviX_test_then_statement_scope(const TestThenStatement *statement)
{
return statement->test_then_scope;
}
void raviX_test_then_statement_foreach_statement(const TestThenStatement *statement, void *userdata,
void (*callback)(void *userdata, const Statement *statement))
{
AstNode *node;
FOR_EACH_PTR(statement->test_then_statement_list, node)
{
assert(node->type <= STMT_EXPR);
callback(userdata, (Statement *)node);
}
END_FOR_EACH_PTR(node)
}
const Expression *raviX_test_then_statement_condition(const TestThenStatement *statement)
{
assert(statement->condition->type >= EXPR_LITERAL && statement->condition->type <= EXPR_FUNCTION_CALL);
return (Expression *)statement->condition;
}
void raviX_if_statement_foreach_test_then_statement(const IfStatement *statement, void *userdata,
void (*callback)(void *, const TestThenStatement *stmt))
{
AstNode *node;
FOR_EACH_PTR(statement->if_condition_list, node)
{
assert(node->type == STMT_TEST_THEN);
callback(userdata, &node->test_then_block);
}
END_FOR_EACH_PTR(node)
}
const Scope *raviX_if_then_statement_else_scope(const IfStatement *statement)
{
return statement->else_block;
}
void raviX_if_statement_foreach_else_statement(const IfStatement *statement, void *userdata,
void (*callback)(void *userdata, const Statement *statement))
{
AstNode *node;
FOR_EACH_PTR(statement->else_statement_list, node)
{
assert(node->type <= STMT_EXPR);
callback(userdata, (Statement *)node);
}
END_FOR_EACH_PTR(node)
}
const Expression *raviX_while_or_repeat_statement_condition(const WhileOrRepeatStatement *statement)
{
assert(statement->condition->type >= EXPR_LITERAL && statement->condition->type <= EXPR_FUNCTION_CALL);
return (Expression *)statement->condition;
}
const Scope *raviX_while_or_repeat_statement_scope(const WhileOrRepeatStatement *statement)
{
return statement->loop_scope;
}
void raviX_while_or_repeat_statement_foreach_statement(const WhileOrRepeatStatement *statement,
void *userdata,
void (*callback)(void *userdata,
const Statement *statement))
{
AstNode *node;
FOR_EACH_PTR(statement->loop_statement_list, node)
{
assert(node->type <= STMT_EXPR);
callback(userdata, (Statement *)node);
}
END_FOR_EACH_PTR(node)
}
const Scope *raviX_for_statement_scope(const ForStatement *statement)
{
return statement->for_scope;
}
void raviX_for_statement_foreach_symbol(const ForStatement *statement, void *userdata,
void (*callback)(void *, const LuaVariableSymbol *expr))
{
LuaSymbol *symbol;
FOR_EACH_PTR(statement->symbols, symbol)
{
assert(symbol->symbol_type == SYM_LOCAL);
callback(userdata, &symbol->variable);
}
END_FOR_EACH_PTR(node)
}
void raviX_for_statement_foreach_expression(const ForStatement *statement, void *userdata,
void (*callback)(void *, const Expression *expr))
{
AstNode *node;
FOR_EACH_PTR(statement->expr_list, node)
{
assert(node->type >= EXPR_LITERAL && node->type <= EXPR_FUNCTION_CALL);
callback(userdata, (Expression *)node);
}
END_FOR_EACH_PTR(node)
}
const Scope *raviX_for_statement_body_scope(const ForStatement *statement)
{
return statement->for_body;
}
void raviX_for_statement_body_foreach_statement(const ForStatement *statement, void *userdata,
void (*callback)(void *userdata, const Statement *statement))
{
AstNode *node;
FOR_EACH_PTR(statement->for_statement_list, node)
{
assert(node->type <= STMT_EXPR);
callback(userdata, (Statement *)node);
}
END_FOR_EACH_PTR(node)
}
const VariableType *raviX_literal_expression_type(const LiteralExpression *expression)
{
return &expression->type;
}
const SemInfo *raviX_literal_expression_literal(const LiteralExpression *expression) { return &expression->u; }
const VariableType *raviX_symbol_expression_type(const SymbolExpression *expression)
{
return &expression->type;
}
const LuaSymbol *raviX_symbol_expression_symbol(const SymbolExpression *expression)
{
return expression->var;
}
const VariableType *raviX_index_expression_type(const IndexExpression *expression)
{
return &expression->type;
}
const Expression *raviX_index_expression_expression(const IndexExpression *expression)
{
assert(expression->expr->type >= EXPR_LITERAL && expression->expr->type <= EXPR_FUNCTION_CALL);
return (const Expression *)expression->expr;
}
const VariableType *raviX_unary_expression_type(const UnaryExpression *expression)
{
return &expression->type;
}
const Expression *raviX_unary_expression_expression(const UnaryExpression *expression)
{
assert(expression->expr->type >= EXPR_LITERAL && expression->expr->type <= EXPR_FUNCTION_CALL);
return (const Expression *)expression->expr;
}
UnaryOperatorType raviX_unary_expression_operator(const UnaryExpression *expression)
{
return expression->unary_op;
}
const VariableType *raviX_binary_expression_type(const BinaryExpression *expression)
{
return &expression->type;
}
const Expression *raviX_binary_expression_left_expression(const BinaryExpression *expression)
{
assert(expression->expr_left->type >= EXPR_LITERAL && expression->expr_left->type <= EXPR_FUNCTION_CALL);
return (const Expression *)expression->expr_left;
}
const Expression *raviX_binary_expression_right_expression(const BinaryExpression *expression)
{
assert(expression->expr_right->type >= EXPR_LITERAL && expression->expr_right->type <= EXPR_FUNCTION_CALL);
return (const Expression *)expression->expr_right;
}
BinaryOperatorType raviX_binary_expression_operator(const BinaryExpression *expression)
{
return expression->binary_op;
}
const VariableType *
raviX_table_element_assignment_expression_type(const TableElementAssignmentExpression *expression)
{
return &expression->type;
}
const Expression *
raviX_table_element_assignment_expression_key(const TableElementAssignmentExpression *expression)
{
if (!expression->key_expr)
return NULL;
assert(expression->key_expr->type >= EXPR_LITERAL && expression->key_expr->type <= EXPR_FUNCTION_CALL);
return (const Expression *)expression->key_expr;
}
const Expression *
raviX_table_element_assignment_expression_value(const TableElementAssignmentExpression *expression)
{
assert(expression->value_expr->type >= EXPR_LITERAL && expression->value_expr->type <= EXPR_FUNCTION_CALL);
return (const Expression *)expression->value_expr;
}
const VariableType *raviX_table_literal_expression_type(const TableLiteralExpression *expression)
{
return &expression->type;
}
void raviX_table_literal_expression_foreach_element(
const TableLiteralExpression *expression, void *userdata,
void (*callback)(void *, const TableElementAssignmentExpression *expr))
{
AstNode *node;
FOR_EACH_PTR(expression->expr_list, node)
{
assert(node->type == EXPR_TABLE_ELEMENT_ASSIGN);
callback(userdata, &node->table_elem_assign_expr);
}
END_FOR_EACH_PTR(node)
}
const VariableType *raviX_suffixed_expression_type(const SuffixedExpression *expression)
{
return &expression->type;
}
const Expression *raviX_suffixed_expression_primary(const SuffixedExpression *expression)
{
assert(expression->primary_expr->type >= EXPR_LITERAL && expression->primary_expr->type <= EXPR_FUNCTION_CALL);
return (const Expression *)expression->primary_expr;
}
void raviX_suffixed_expression_foreach_suffix(const SuffixedExpression *expression, void *userdata,
void (*callback)(void *, const Expression *expr))
{
AstNode *node;
FOR_EACH_PTR(expression->suffix_list, node)
{
assert(node->type >= EXPR_LITERAL && node->type <= EXPR_FUNCTION_CALL);
callback(userdata, (Expression *)node);
}
END_FOR_EACH_PTR(node)
}
const VariableType *raviX_function_call_expression_type(const FunctionCallExpression *expression)
{
return &expression->type;
}
// Can return NULL
const StringObject *
raviX_function_call_expression_method_name(const FunctionCallExpression *expression)
{
return expression->method_name;
}
void raviX_function_call_expression_foreach_argument(const FunctionCallExpression *expression, void *userdata,
void (*callback)(void *, const Expression *expr))
{
AstNode *node;
FOR_EACH_PTR(expression->arg_list, node)
{
assert(node->type >= EXPR_LITERAL && node->type <= EXPR_FUNCTION_CALL);
callback(userdata, (Expression *)node);
}
END_FOR_EACH_PTR(node)
}
const FunctionExpression *raviX_scope_owning_function(const Scope *scope)
{
assert(scope->function->type == EXPR_FUNCTION);
return &scope->function->function_expr;
}
RAVICOMP_EXPORT const Scope *raviX_scope_parent_scope(const Scope *scope)
{
return scope->parent;
}
RAVICOMP_EXPORT void raviX_scope_foreach_symbol(const Scope *scope, void *userdata,
void (*callback)(void *userdata, const LuaSymbol *symbol))
{
LuaSymbol *symbol;
FOR_EACH_PTR(scope->symbol_list, symbol) { callback(userdata, symbol); }
END_FOR_EACH_PTR(node)
}
enum SymbolType raviX_symbol_type(const LuaSymbol *symbol) { return symbol->symbol_type; }
const LuaVariableSymbol *raviX_symbol_variable(const LuaSymbol *symbol)
{
assert(symbol->symbol_type == SYM_GLOBAL || symbol->symbol_type == SYM_LOCAL);
return &symbol->variable;
}
const LuaUpvalueSymbol *raviX_symbol_upvalue(const LuaSymbol *symbol)
{
assert(symbol->symbol_type == SYM_UPVALUE);
return &symbol->upvalue;
}
const LuaLabelSymbol *raviX_symbol_label(const LuaSymbol *symbol)
{
assert(symbol->symbol_type == SYM_LABEL);
return &symbol->label;
}
const StringObject *raviX_label_name(const LuaLabelSymbol *symbol) { return symbol->label_name; }
const Scope *raviX_label_scope(const LuaLabelSymbol *symbol) { return symbol->block; }
const VariableType *raviX_upvalue_symbol_type(const LuaUpvalueSymbol *symbol)
{
return &symbol->value_type;
}
const LuaVariableSymbol *raviX_upvalue_target_variable(const LuaUpvalueSymbol *symbol)
{
if (symbol->target_variable->symbol_type == SYM_ENV) {
assert(symbol->target_function == NULL);
return NULL;
}
assert(symbol->target_variable->symbol_type == SYM_LOCAL);
return &symbol->target_variable->variable;
}
const FunctionExpression *raviX_upvalue_target_function(const LuaUpvalueSymbol *symbol)
{
if (symbol->target_variable->symbol_type == SYM_ENV) {
assert(symbol->target_function == NULL);
return NULL;
}
assert(symbol->target_function->type == EXPR_FUNCTION);
return &symbol->target_function->function_expr;
}
unsigned raviX_upvalue_index(const LuaUpvalueSymbol *symbol) { return symbol->upvalue_index; }

@ -0,0 +1,274 @@
/* This file is a part of MIR project.
Copyright (C) 2018-2020 Vladimir Makarov <vmakarov.gcc@gmail.com>.
*/
/*
* Adapted for Ravi Compiler project
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <stdint.h>
#include <limits.h>
#include <stdbool.h>
#include <allocate.h>
#include <bitset.h>
#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_bitset_assert_fail (const char *op) {
fprintf (stderr, "wrong %s for a bitset", op);
assert (0);
}
#define BITMAP_ASSERT(EXPR, OP) (void) ((EXPR) ? 0 : (mir_bitset_assert_fail (#OP), 0))
#endif
#define BITMAP_WORD_BITS 64
void raviX_bitset_create2(BitSet *bm, size_t init_bits_num) {
bm->els_num = 0;
bm->size = (init_bits_num + BITMAP_WORD_BITS - 1) / BITMAP_WORD_BITS;
bm->varr = calloc(bm->size, sizeof(bitset_el_t));
}
void raviX_bitset_destroy(BitSet * bm)
{
free(bm->varr);
}
static void bitset_expand (BitSet * bm, size_t nb) {
size_t new_len = (nb + BITMAP_WORD_BITS - 1) / BITMAP_WORD_BITS;
if (new_len > bm->els_num) {
if (new_len > bm->size) {
bm->varr = raviX_realloc_array(bm->varr, sizeof(bitset_el_t), bm->size, new_len);
bm->size = new_len;
}
bm->els_num = new_len;
}
}
int raviX_bitset_bit_p(const BitSet * bm, size_t nb) {
size_t nw, sh, len = bm->els_num;
bitset_el_t *addr = bm->varr;
if (nb >= BITMAP_WORD_BITS * len) return 0;
nw = nb / BITMAP_WORD_BITS;
sh = nb % BITMAP_WORD_BITS;
return (addr[nw] >> sh) & 1;
}
/* Set the given bit to 1, and return true if the bit was previously unset, i.e.
* this set caused bit to change from 0 to 1
*/
int raviX_bitset_set_bit_p(BitSet * bm, size_t bit) {
size_t nw, sh;
bitset_el_t *addr;
int res;
bitset_expand (bm, bit + 1);
addr = bm->varr;
nw = bit / BITMAP_WORD_BITS;
sh = bit % BITMAP_WORD_BITS;
res = ((addr[nw] >> sh) & 1) == 0; /* Was this bit previously unset? */
assert(nw < bm->els_num);
addr[nw] |= (bitset_el_t) 1 << sh;
return res;
}
int raviX_bitset_clear_bit_p(BitSet * bm, size_t nb) {
size_t nw, sh, len = bm->els_num;
bitset_el_t *addr = bm->varr;
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] &= ~((bitset_el_t) 1 << sh);
return res;
}
int raviX_bitset_set_or_clear_bit_range_p(BitSet * bm, size_t nb, size_t len, int set_p) {
size_t nw, lsh, rsh, range_len;
bitset_el_t mask, *addr;
int res = 0;
bitset_expand (bm, nb + len);
addr = bm->varr;
while (len > 0) {
nw = nb / BITMAP_WORD_BITS;
lsh = nb % BITMAP_WORD_BITS;
rsh = len >= BITMAP_WORD_BITS - lsh ? 0 : BITMAP_WORD_BITS - (nb + len) % BITMAP_WORD_BITS;
mask = ((~(bitset_el_t) 0) >> (rsh + lsh)) << lsh;
if (set_p) {
res |= (~addr[nw] & mask) != 0;
addr[nw] |= mask;
} else {
res |= (addr[nw] & mask) != 0;
addr[nw] &= ~mask;
}
range_len = BITMAP_WORD_BITS - rsh - lsh;
len -= range_len;
nb += range_len;
}
return res;
}
void raviX_bitset_copy(BitSet * dst, const BitSet * src) {
size_t dst_len = dst->els_num;
size_t src_len = src->els_num;
if (dst_len >= src_len)
dst->els_num = src_len;
else
bitset_expand (dst, src_len * BITMAP_WORD_BITS);
memcpy (dst->varr, src->varr,
src_len * sizeof (bitset_el_t));
}
int raviX_bitset_equal_p(const BitSet * bm1, const BitSet * bm2) {
const BitSet * temp_bm;
size_t i, temp_len, bm1_len = bm1->els_num;
size_t bm2_len = bm2->els_num;
bitset_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 = bm1->varr;
addr2 = bm2->varr;
if (memcmp (addr1, addr2, bm1_len * sizeof (bitset_el_t)) != 0) return false;
for (i = bm1_len; i < bm2_len; i++)
if (addr2[i] != 0) return false;
return true;
}
int raviX_bitset_intersect_p(const BitSet * bm1, const BitSet * bm2) {
size_t i, min_len, bm1_len = bm1->els_num;
size_t bm2_len = bm2->els_num;
bitset_el_t *addr1 = bm1->varr;
bitset_el_t *addr2 = bm2->varr;
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;
}
int raviX_bitset_empty_p(const BitSet * bm) {
size_t i, len = bm->els_num;
bitset_el_t *addr = bm->varr;
for (i = 0; i < len; i++)
if (addr[i] != 0) return false;
return true;
}
static bitset_el_t bitset_el_max2 (bitset_el_t el1, bitset_el_t el2) {
return el1 < el2 ? el2 : el1;
}
static bitset_el_t bitset_el_max3 (bitset_el_t el1, bitset_el_t el2, bitset_el_t el3) {
if (el1 <= el2) return el2 < el3 ? el3 : el2;
return el1 < el3 ? el3 : el1;
}
/* Return the number of bits set in BM. */
size_t raviX_bitset_bit_count(const BitSet * bm) {
size_t i, len = bm->els_num;
bitset_el_t el, *addr = bm->varr;
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;
}
int raviX_bitset_op2(BitSet * dst, const BitSet * src1, const BitSet * src2,
bitset_el_t (*op) (bitset_el_t, bitset_el_t)) {
size_t i, len, bound, src1_len, src2_len;
bitset_el_t old, *dst_addr, *src1_addr, *src2_addr;
int change_p = false;
src1_len = src1->els_num;
src2_len = src2->els_num;
len = bitset_el_max2 (src1_len, src2_len);
bitset_expand (dst, len * BITMAP_WORD_BITS);
dst_addr = dst->varr;
src1_addr = src1->varr;
src2_addr = src2->varr;
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;
}
dst->els_num = bound;
return change_p;
}
int raviX_bitset_op3(BitSet * dst, const BitSet * src1, const BitSet * src2,
const BitSet * src3, bitset_el_t (*op) (bitset_el_t, bitset_el_t, bitset_el_t)) {
size_t i, len, bound, src1_len, src2_len, src3_len;
bitset_el_t old, *dst_addr, *src1_addr, *src2_addr, *src3_addr;
int change_p = false;
src1_len = src1->els_num;
src2_len = src2->els_num;
src3_len = src3->els_num;
len = bitset_el_max3 (src1_len, src2_len, src3_len);
bitset_expand (dst, len * BITMAP_WORD_BITS);
dst_addr = dst->varr;
src1_addr = src1->varr;
src2_addr = src2->varr;
src3_addr = src3->varr;
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;
}
dst->els_num = bound;
return change_p;
}
int raviX_bitset_iterator_next(BitSetIterator *iter, size_t *nbit) {
const size_t el_bits_num = sizeof (bitset_el_t) * CHAR_BIT;
size_t curr_nel = iter->nbit / el_bits_num, len = iter->bitset->els_num;
bitset_el_t el, *addr = iter->bitset->varr;
for (; curr_nel < len; curr_nel++, iter->nbit = curr_nel * el_bits_num)
if ((el = addr[curr_nel]) != 0)
for (el >>= iter->nbit % el_bits_num; el != 0; el >>= 1, iter->nbit++)
if (el & 1) {
*nbit = iter->nbit++;
return true;
}
return false;
}

@ -0,0 +1,107 @@
/* This file is a part of MIR project.
Copyright (C) 2018-2020 Vladimir Makarov <vmakarov.gcc@gmail.com>.
*/
/*
* Adapted for Ravi Compiler project
*/
#ifndef ravicomp_BITSET_H
#define ravicomp_BITSET_H
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef uint64_t bitset_el_t;
typedef struct BitSet {
size_t els_num;
size_t size;
bitset_el_t *varr;
} BitSet;
extern void raviX_bitset_create2(BitSet *, size_t init_bits_num);
static inline void raviX_bitset_create(BitSet *bm)
{
raviX_bitset_create2(bm, 0);
}
extern void raviX_bitset_destroy(BitSet * bm);
static inline void raviX_bitset_clear(BitSet * bm)
{
bm->els_num = 0;
}
extern int raviX_bitset_bit_p(const BitSet * bm, size_t nb);
/* Sets a bit ON and returns true if previously bit was not set */
extern int raviX_bitset_set_bit_p(BitSet * bm, size_t bit);
extern int raviX_bitset_clear_bit_p(BitSet * bm, size_t nb);
extern int raviX_bitset_set_or_clear_bit_range_p(BitSet * bm, size_t nb, size_t len, int set_p);
static inline int raviX_bitset_set_bit_range_p(BitSet * bm, size_t nb, size_t len) {
return raviX_bitset_set_or_clear_bit_range_p(bm, nb, len, true);
}
static inline int raviX_bitset_clear_bit_range_p(BitSet * bm, size_t nb, size_t len) {
return raviX_bitset_set_or_clear_bit_range_p(bm, nb, len, false);
}
extern void raviX_bitset_copy(BitSet * dst, const BitSet * src);
extern int raviX_bitset_equal_p(const BitSet * bm1, const BitSet * bm2);
extern int raviX_bitset_intersect_p(const BitSet * bm1, const BitSet * bm2);
extern int raviX_bitset_empty_p(const BitSet * bm);
/* Return the number of bits set in BM. */
extern size_t raviX_bitset_bit_count(const BitSet * bm);
extern int raviX_bitset_op2(BitSet * dst, const BitSet * src1, const BitSet * src2,
bitset_el_t (*op) (bitset_el_t, bitset_el_t));
static inline bitset_el_t raviX_bitset_el_and(bitset_el_t el1, bitset_el_t el2) { return el1 & el2; }
static inline int raviX_bitset_and(BitSet * dst, BitSet * src1, BitSet * src2) {
return raviX_bitset_op2(dst, src1, src2, raviX_bitset_el_and);
}
static inline bitset_el_t raviX_bitset_el_and_compl(bitset_el_t el1, bitset_el_t el2) {
return el1 & ~el2;
}
static inline int raviX_bitset_and_compl(BitSet * dst, BitSet * src1, BitSet * src2) {
return raviX_bitset_op2(dst, src1, src2, raviX_bitset_el_and_compl);
}
static inline bitset_el_t raviX_bitset_el_ior(bitset_el_t el1, bitset_el_t el2) { return el1 | el2; }
static inline int raviX_bitset_ior(BitSet * dst, BitSet * src1, BitSet * src2) {
return raviX_bitset_op2(dst, src1, src2, raviX_bitset_el_ior);
}
int raviX_bitset_op3(BitSet * dst, const BitSet * src1, const BitSet * src2,
const BitSet * src3, bitset_el_t (*op) (bitset_el_t, bitset_el_t, bitset_el_t));
static inline bitset_el_t raviX_bitset_el_ior_and(bitset_el_t el1, bitset_el_t el2, bitset_el_t el3) {
return el1 | (el2 & el3);
}
/* DST = SRC1 | (SRC2 & SRC3). Return true if DST changed. */
static inline int raviX_bitset_ior_and(BitSet * dst, BitSet * src1, BitSet * src2, BitSet * src3) {
return raviX_bitset_op3(dst, src1, src2, src3, raviX_bitset_el_ior_and);
}
static inline bitset_el_t raviX_bitset_el_ior_and_compl(bitset_el_t el1, bitset_el_t el2, bitset_el_t el3) {
return el1 | (el2 & ~el3);
}
/* DST = SRC1 | (SRC2 & ~SRC3). Return true if DST changed. */
static inline int raviX_bitset_ior_and_compl(BitSet * dst, BitSet * src1, BitSet * src2, BitSet * src3) {
return raviX_bitset_op3(dst, src1, src2, src3, raviX_bitset_el_ior_and_compl);
}
typedef struct {
BitSet * bitset;
size_t nbit;
} BitSetIterator;
static inline void raviX_bitset_iterator_init(BitSetIterator *iter, BitSet * bitset) {
iter->bitset = bitset;
iter->nbit = 0;
}
extern int raviX_bitset_iterator_next(BitSetIterator *iter, size_t *nbit);
#define FOREACH_BITSET_BIT(iter, bitset, nbit) \
for (raviX_bitset_iterator_init (&iter, bitset); raviX_bitset_iterator_next (&iter, &nbit);)
#ifdef __cplusplus
} /* extern C */
#endif
#endif

@ -0,0 +1,81 @@
/* Build CFG */
#include "graph.h"
#include "cfg.h"
#include <assert.h>
/* Recursively create control flow graph for each proc
* Return 0 on success
*/
int raviX_construct_cfg(Proc *proc)
{
Graph *g = raviX_init_graph(ENTRY_BLOCK, EXIT_BLOCK, proc);
for (unsigned i = 0; i < proc->node_count; i++) {
BasicBlock *block = proc->nodes[i];
Instruction *insn = raviX_last_instruction(block);
if (insn == NULL)
continue;
if (insn->opcode == op_br || insn->opcode == op_cbr || insn->opcode == op_ret) {
Pseudo *pseudo;
FOR_EACH_PTR(insn->targets, pseudo)
{
assert(pseudo->type == PSEUDO_BLOCK);
raviX_add_edge(g, block->index, pseudo->block->index);
}
END_FOR_EACH_PTR(pseudo)
} else {
return 1;
}
}
proc->cfg = g;
Proc *childproc;
FOR_EACH_PTR(proc->procs, childproc)
{
if (raviX_construct_cfg(childproc) != 0)
return 1;
}
END_FOR_EACH_PTR(childproc)
return 0;
}
struct CfgArg {
FILE *fp;
Proc *proc;
};
static void output_node(void *arg, Graph *g, uint32_t nodeid)
{
struct CfgArg *myargs = (struct CfgArg *)arg;
FILE *fp = myargs->fp;
Proc *proc = myargs->proc;
GraphNodeList *successors = raviX_successors(raviX_graph_node(g, nodeid));
if (!successors)
return;
BasicBlock *block = proc->nodes[nodeid];
if (raviX_ptrlist_size((const struct ptr_list *)block->insns) > 0) {
TextBuffer buf;
raviX_buffer_init(&buf, 1024);
raviX_output_basic_block_as_table(proc, block, &buf);
fprintf(fp, "L%d [shape=none, margin=0, label=<%s>];\n", nodeid, raviX_buffer_data(&buf));
raviX_buffer_free(&buf);
}
for (unsigned i = 0; i < raviX_node_list_size(successors); i++) {
fprintf(fp, "L%d -> L%d\n", nodeid, raviX_node_list_at(successors, i));
}
Proc *childproc;
FOR_EACH_PTR(proc->procs, childproc) { raviX_output_cfg(childproc, fp); }
END_FOR_EACH_PTR(childproc)
}
void raviX_output_cfg(Proc *proc, FILE *fp)
{
Graph *g = proc->cfg;
if (!g)
return;
fprintf(fp, "digraph Proc%d {\n", proc->id);
struct CfgArg args = {.proc = proc, .fp = fp};
raviX_for_each_node(g, output_node, &args);
fprintf(fp, "}\n");
}

@ -0,0 +1,11 @@
#ifndef ravicomp_CFG_H
#define ravicomp_CFG_H
#include "linearizer.h"
#include <stdio.h>
int raviX_construct_cfg(Proc *proc);
void raviX_output_cfg(Proc *proc, FILE *fp);
#endif

File diff suppressed because it is too large Load Diff

@ -0,0 +1,11 @@
#ifndef ravicomp_CODEGEN_H
#define ravicomp_CODEGEN_H
#include "ravi_compiler.h"
#include "ravi_api.h"
#include "linearizer.h"
RAVICOMP_EXPORT int raviX_generate_C(LinearizerState *linearizer, TextBuffer *mb, struct Ravi_CompilerInterface *ravi_interface);
RAVICOMP_EXPORT void raviX_generate_C_tofile(LinearizerState *linearizer, const char *mainfunc, FILE *fp);
#endif

@ -0,0 +1,14 @@
#ifndef ravicomp_COMMON_H
#define ravicomp_COMMON_H
#include <stdint.h>
typedef uint32_t nodeId_t; /* The type used to identify nodes in CFG */
/* We have two distinguished basic blocks in every proc */
enum {
ENTRY_BLOCK = 0,
EXIT_BLOCK = 1
};
#endif

@ -0,0 +1,94 @@
/**
* A framework for performing data flow analysis.
* The framework is based upon similar framework in MIR project (https://github.com/vnmakarov/mir)
*/
#include "dataflow_framework.h"
#include "allocate.h"
#include "graph.h"
#include "bitset.h"
#include <string.h>
DECLARE_ARRAY(GraphNodeArray, GraphNode *);
struct dataflow_context {
Graph *g;
GraphNodeArray worklist;
GraphNodeArray pending;
BitSet bb_to_consider;
void *userdata;
};
static void init_data_flow(struct dataflow_context *dataflow_context, Graph *g)
{
memset(dataflow_context, 0, sizeof *dataflow_context);
raviX_bitset_create2(&dataflow_context->bb_to_consider, 512);
dataflow_context->g = g;
}
static void finish_data_flow(struct dataflow_context *dataflow_context)
{
array_clearmem(&dataflow_context->worklist);
array_clearmem(&dataflow_context->pending);
raviX_bitset_destroy(&dataflow_context->bb_to_consider);
}
void raviX_solve_dataflow(Graph *g, bool forward_p,
int (*join_function)(void *, nodeId_t, bool),
int (*transfer_function)(void *, nodeId_t), void *userdata)
{
unsigned iter;
struct dataflow_context ctx;
GraphNodeArray *worklist;
GraphNodeArray *pending;
init_data_flow(&ctx, g);
worklist = &ctx.worklist;
pending = &ctx.pending;
/* ensure that the graph has RPO calculated */
raviX_classify_edges(ctx.g);
worklist->count = 0;
/* Initially the basic blocks are added to the worklist */
for (uint32_t i = 0; i < raviX_graph_size(ctx.g); i++) {
array_push(worklist, raviX_graph_node(ctx.g, i));
}
iter = 0;
while (worklist->count != 0) {
GraphNode **addr = worklist->data;
raviX_sort_nodes_by_RPO(addr, worklist->count, forward_p);
raviX_bitset_clear(&ctx.bb_to_consider);
pending->count = 0;
for (unsigned i = 0; i < worklist->count; i++) {
int changed_p = iter == 0;
GraphNode *bb = addr[i];
GraphNodeList *nodes = forward_p ? raviX_predecessors(bb) : raviX_successors(bb);
// TODO should we pass the nodes array to the join function?
if (raviX_node_list_size(nodes) == 0)
join_function(ctx.userdata, raviX_node_index(bb), true);
else
changed_p |= join_function(ctx.userdata, raviX_node_index(bb), false);
if (changed_p && transfer_function(ctx.userdata, raviX_node_index(bb))) {
GraphNodeList *list = forward_p ? raviX_successors(bb) : raviX_predecessors(bb);
for (unsigned i = 0; i < raviX_node_list_size(list); i++) {
nodeId_t index = raviX_node_list_at(list, i);
/* If this bb is not already been added to pending then add it */
if (raviX_bitset_set_bit_p(&ctx.bb_to_consider, index)) {
array_push(pending, raviX_graph_node(ctx.g, index));
}
}
}
}
iter++;
{
/* Swap worklist and pending */
GraphNodeArray *t = worklist;
worklist = pending;
pending = t;
}
}
finish_data_flow(&ctx);
}

@ -0,0 +1,19 @@
#ifndef ravicomp_DATAFLOW_FRAMEWORK_H
#define ravicomp_DATAFLOW_FRAMEWORK_H
#include "graph.h"
#include <stdbool.h>
/*
* Data Flow Analysis framework.
* The Join/Transfer functions should return 1 if they made any changes else 0.
*/
extern void raviX_solve_dataflow(
Graph *g,
bool forward_p, /* Set to true for forward data flow */
int (*join_function)(void *userdata, nodeId_t, bool init), /* Join/Meet operator - if init is true reset the bitsets */
int (*transfer_function)(void *userdata, nodeId_t), /* transfer function */
void *userdata); /* pointer to user data, will be passed to join/transfer functions */
#endif

@ -0,0 +1,99 @@
/*
* Calculate variable liveness
* This will use the Dataflow Framework.
* Implementation inspired by one in MIR
*/
#include "bitset.h"
#include "dataflow_framework.h"
#include "linearizer.h"
struct liveness_info {
nodeId_t node_id;
BitSet in;
BitSet out;
BitSet use;
BitSet def;
};
DECLARE_ARRAY(liveness_info_array, struct liveness_info *);
struct liveness_data {
Proc *proc;
struct liveness_info_array lives;
};
static void init_liveness_data(Proc *proc, struct liveness_data *liveness_data)
{
memset(liveness_data, 0, sizeof(*liveness_data));
for (unsigned i = 0; i < proc->node_count; i++) {
struct liveness_info *liveness_info = (struct liveness_info *)calloc(1, sizeof(struct liveness_info));
liveness_info->node_id = i;
raviX_bitset_create(&liveness_info->use);
raviX_bitset_create(&liveness_info->def);
raviX_bitset_create(&liveness_info->in);
raviX_bitset_create(&liveness_info->out);
array_push(&liveness_data->lives, liveness_info);
}
}
static void destroy_liveness_data(struct liveness_data *liveness_data)
{
for (unsigned i = 0; i < liveness_data->lives.count; i++) {
raviX_bitset_create(&liveness_data->lives.data[i]->use);
raviX_bitset_create(&liveness_data->lives.data[i]->def);
raviX_bitset_create(&liveness_data->lives.data[i]->in);
raviX_bitset_create(&liveness_data->lives.data[i]->out);
}
array_clearmem(&liveness_data->lives);
}
static inline struct liveness_info *get_liveness_info(struct liveness_data *liveness_data, nodeId_t id)
{
return liveness_data->lives.data[id];
}
/* Life analysis */
static int live_join_func(void *userdata, nodeId_t id, bool init)
{
struct liveness_data *liveness_data = (struct liveness_data *)userdata;
struct liveness_info *liveness_info = get_liveness_info(liveness_data, id);
if (init) {
raviX_bitset_clear(&liveness_info->in);
return 0;
} else {
GraphNodeList *successors = raviX_successors(raviX_graph_node(liveness_data->proc->cfg, id));
int changed = 0;
// out[n] = Union of in[s] where s in succ[n]
for (unsigned i = 0; i < raviX_node_list_size(successors); i++) {
nodeId_t succ_id = raviX_node_list_at(successors, i);
struct liveness_info *successor_liveness_info = get_liveness_info(liveness_data, succ_id);
changed |=
raviX_bitset_ior(&liveness_info->out, &liveness_info->out, &successor_liveness_info->in);
}
return changed;
}
}
static int live_transfer_func(void *userdata, nodeId_t id)
{
struct liveness_data *liveness_data = (struct liveness_data *)userdata;
struct liveness_info *liveness_info = get_liveness_info(liveness_data, id);
// out[n] = use[n] U (out[n] - def[n])
// In bitset terms out[n] = use[n] | (out[n] & ~def[n])
return raviX_bitset_ior_and_compl(&liveness_info->in, &liveness_info->use, &liveness_info->out,
&liveness_info->def);
}
// TODO
// Compute use/def sets of each node
// If a reg appears as the target of an instruction that's a def
// If a reg is used as operand then its a use
// Need to handle ranges / var args too
// Or should we restrict analysis to certain types of regs?
// Right now we have disjoint sets for temps / locals - to do this efficiently we need a merged set of regs for each proc
// Liveness analysis is a backward data flow problem
// see calculate_func_cfg_live_info in mir_genc.c

@ -0,0 +1,149 @@
#include "dominator.h"
#include "ravi_compiler.h"
#include "graph.h"
#include <assert.h>
/*
* The dominator tree construction algorithm is based on figure 9.24,
* chapter 9, p 532, of Engineering a Compiler.
*
* The algorithm is also described in the paper 'A Simple, Fast
* Dominance Algorithm' by Keith D. Cooper, Timothy J. Harvey and
* Ken Kennedy.
*/
/*
Some terminology:
DOM(b): A node n in the CFG dominates b if n lies on every path from the entry node of the CFG to b.
DOM9b) contains every node n that dominates b.
IDOM(b): For a node b, the set IDOM(b) contains exactly one node, the immediate dominator of b.
If n is b's immediate dominator then every node in {DOM(b) - b} is also in DOM(n).
The dominator tree algorithm is an optimised version of forward data flow solver. The
algorithm iterates until a fixed point is reached. The output of the algorithm is the IDOM
array that describes the dominator tree.
*/
struct dominator_tree {
Graph *g;
GraphNode **IDOM; /* IDOM[] - array of immediate dominators, one per node in the graph, indexed by node id */
uint32_t N; /* sizeof IDOM */
};
struct dominator_tree *raviX_new_dominator_tree(Graph *g)
{
struct dominator_tree *state = (struct dominator_tree *)calloc(1, sizeof(struct dominator_tree));
state->N = raviX_graph_size(g);
state->IDOM = (GraphNode **)calloc(state->N, sizeof(GraphNode *));
state->g = g;
return state;
}
void raviX_destroy_dominator_tree(struct dominator_tree *state)
{
free(state->IDOM);
free(state);
}
/* Finds nearest common ancestor */
/* The algorithm starts at the two nodes whose sets are being intersected, and walks
* upward from each toward the root. By comparing the nodes with their RPO numbers
* the algorithm finds the common ancestor - the immediate dominator of i and j.
*/
static GraphNode *intersect(struct dominator_tree *state, GraphNode *i, GraphNode *j)
{
GraphNode *finger1 = i;
GraphNode *finger2 = j;
while (finger1 != finger2) {
while (raviX_node_RPO(finger1) > raviX_node_RPO(finger2)) {
finger1 = state->IDOM[raviX_node_index(finger1)];
assert(finger1);
}
while (raviX_node_RPO(finger2) > raviX_node_RPO(finger1)) {
finger2 = state->IDOM[raviX_node_index(finger2)];
assert(finger2);
}
}
return finger1;
}
/* Look for the first predecessor whose immediate dominator has been calculated.
* Because of the order in which this search occurs, we will always find at least 1
* such predecessor.
*/
static GraphNode *find_first_predecessor_with_idom(struct dominator_tree *state, GraphNodeList *predlist)
{
for (uint32_t i = 0; i < raviX_node_list_size(predlist); i++) {
nodeId_t id = raviX_node_list_at(predlist, i);
if (state->IDOM[id])
return raviX_graph_node(state->g, id);
}
return NULL;
}
/**
* Calculates the dominator tree.
* Before this is called the graph links should have been numbered in
* reverse post order.
*/
void raviX_calculate_dominator_tree(struct dominator_tree *state)
{
/*
Some implementation details:
The graph and links reference nodes by node ids.
However the IDOM array references the node objects - i.e.
pointers to 'GraphNode'. So we have some conversion from node id
to the node, and vice versa at various points.
*/
uint32_t N = raviX_graph_size(state->g);
GraphNode **nodes_in_reverse_postorder = raviX_graph_nodes_sorted_by_RPO(state->g, false);
for (uint32_t i = 0; i < state->N; i++) {
state->IDOM[i] = NULL; /* undefined - set to a invalid value */
}
// Set IDom entry for root to itself
state->IDOM[ENTRY_BLOCK] = raviX_graph_node(state->g, ENTRY_BLOCK);
bool changed = true;
while (changed) {
changed = false;
// for all nodes, b, in reverse postorder (except root)
for (uint32_t i = 0; i < N; i++) {
GraphNode *b = nodes_in_reverse_postorder[i];
nodeId_t bid = raviX_node_index(b);
if (bid == ENTRY_BLOCK) // skip root
continue;
GraphNodeList *predecessors = raviX_predecessors(b); // Predecessors of b
// NewIDom = first (processed) predecessor of b, pick one
GraphNode *firstpred = find_first_predecessor_with_idom(state, predecessors);
assert(firstpred != NULL);
GraphNode *NewIDom = firstpred;
// for all other predecessors, p, of b
for (uint32_t k = 0; k < raviX_node_list_size(predecessors); k++) {
nodeId_t pid = raviX_node_list_at(predecessors, k);
GraphNode *p = raviX_graph_node(state->g, pid);
if (p == firstpred)
continue; // all other predecessors
if (state->IDOM[raviX_node_index(p)] != NULL) {
// i.e. IDoms[p] calculated
NewIDom = intersect(state, p, NewIDom);
}
}
if (state->IDOM[bid] != NewIDom) {
state->IDOM[bid] = NewIDom;
changed = true;
}
}
}
free(nodes_in_reverse_postorder);
}
void raviX_dominator_tree_output(struct dominator_tree *tree, FILE *fp)
{
for (uint32_t i = 0; i < tree->N; i++) {
fprintf(stdout, "IDOM[%d] = %d\n", i, raviX_node_index(tree->IDOM[i]));
}
}

@ -0,0 +1,16 @@
#ifndef ravicomp_DOMINATOR_H
#define ravicomp_DOMINATOR_H
#include "graph.h"
#include <stdio.h>
struct dominator_tree;
struct dominator_tree *raviX_new_dominator_tree(Graph *g);
void raviX_calculate_dominator_tree(struct dominator_tree *state);
void raviX_destroy_dominator_tree(struct dominator_tree *state);
void raviX_dominator_tree_output(struct dominator_tree *tree, FILE *fp);
#endif

@ -0,0 +1,72 @@
/*
* Copyright © 2009 Intel Corporation
*
* 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 (including the next
* paragraph) 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.
*
* Authors:
* Eric Anholt <eric@anholt.net>
*/
/* Quick FNV-1a hash implementation based on:
* http://www.isthe.com/chongo/tech/comp/fnv/
*
* FNV-1a may not be the best hash out there -- Jenkins's lookup3 is supposed
* to be quite good, and it may beat FNV. But FNV has the advantage that it
* involves almost no code.
*/
#include <fnv_hash.h>
#include <string.h>
uint32_t
fnv1_hash_string(const char *key)
{
uint32_t hash = 2166136261ul;
const uint8_t *bytes = (uint8_t *)key;
while (*bytes != 0) {
hash ^= *bytes;
hash = hash * 0x01000193;
bytes++;
}
return hash;
}
uint32_t
fnv1_hash_data(const void *data, size_t size)
{
uint32_t hash = 2166136261ul;
const uint8_t *bytes = (uint8_t *)data;
while (size-- != 0) {
hash ^= *bytes;
hash = hash * 0x01000193;
bytes++;
}
return hash;
}
int
string_key_equals(const void *a, const void *b)
{
return strcmp(a, b) == 0;
}

@ -0,0 +1,51 @@
/*
* Copyright © 2009 Intel Corporation
* Copyright © 2014 Broadcom
*
* 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 (including the next
* paragraph) 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.
*
* Authors:
* Eric Anholt <eric@anholt.net>
*/
/* Quick FNV-1 hash implementation based on:
* http://www.isthe.com/chongo/tech/comp/fnv/
*/
#ifndef ravicomp_FNV_HASH_H
#define ravicomp_FNV_HASH_H
#include <inttypes.h>
#include <stdlib.h>
uint32_t fnv1_hash_string(const char *key);
uint32_t fnv1_hash_data(const void *data, size_t size);
int string_key_equals(const void *a, const void *b);
#define hash_table_create_for_string() \
raviX_hash_table_create((uint32_t (*)(const void *key))fnv1_hash_string, \
string_key_equals)
#define set_create_for_string() \
set_create((uint32_t (*)(const void *key))fnv1_hash_string, \
string_key_equals)
#endif

@ -0,0 +1,389 @@
#include "graph.h"
#include <assert.h>
#include <stdlib.h>
struct Graph {
unsigned allocated; /* tracks allocated size of nodes */
GraphNode **nodes; /* array[allocated] indexed by nodeId_t, note user must check if nodes[i] != NULL */
Allocator node_allocator;
nodeId_t entry, exit; /* entry and exit nodes */
void *userdata;
};
struct GraphNodeLink {
nodeId_t node_index;
unsigned char edge_type;
};
/* A node_list is simply a dynamic array/vector of node ids.
*/
struct GraphNodeList {
unsigned count : 16; /* in use */
unsigned allocated : 16; /* tracks allocated size of links array */
struct GraphNodeLink *links; /* array[allocated] of links, populated [0..count) */
};
/* A node in the graph. For each node we maintain a list of predecessor nodes and successor nodes.
*/
struct GraphNode {
nodeId_t index; /* the id of the basic_block */
uint32_t pre; /* preorder */
uint32_t rpost; /* reverse postorder */
GraphNodeList preds; /* predecessor nodes */
GraphNodeList succs; /* successor nodes */
};
static GraphNode *raviX_add_node(Graph *g, nodeId_t index);
static void node_list_init(GraphNodeList *node_list)
{
node_list->count = 0;
node_list->allocated = 0;
node_list->links = NULL;
}
static void node_list_destroy(GraphNodeList *node_list)
{
node_list->count = 0;
node_list->allocated = 0;
free(node_list->links);
node_list->links = NULL;
}
/* Gets the offset of the node or -1 if not found */
static int64_t node_list_search(const GraphNodeList *node_list, nodeId_t index)
{
for (unsigned i = 0; i < node_list->count; i++) {
if (node_list->links[i].node_index == index) {
return i;
}
}
return -1;
}
/* Gets the given node or NULL if node does not exist */
static inline struct GraphNodeLink *node_list_get(const GraphNodeList *node_list, nodeId_t index)
{
int64_t i = node_list_search(node_list, index);
if (i < 0)
return NULL;
return &node_list->links[i];
}
/* Grows the node list array */
static void node_list_grow(GraphNodeList *node_list)
{
unsigned new_size = node_list->allocated + 8u;
struct GraphNodeLink *edges = raviX_realloc_array(node_list->links, sizeof(struct GraphNodeLink), node_list->allocated,
new_size);
node_list->allocated = new_size;
node_list->links = edges;
}
/* add an node to the node_list if not already added */
static void node_list_add(GraphNodeList *node_list, nodeId_t index)
{
if (node_list_search(node_list, index) != -1)
return;
if (node_list->count >= node_list->allocated) {
node_list_grow(node_list);
}
assert(node_list->count < node_list->allocated);
node_list->links[node_list->count].node_index = index;
assert(node_list->links[node_list->count].edge_type == 0);
node_list->count++;
}
/* delete an node from the node_list if it exists */
static void node_list_delete(GraphNodeList *node_list, nodeId_t index)
{
int64_t i = node_list_search(node_list, index);
if (i < 0)
return;
node_list->count = (unsigned) raviX_del_array_element(node_list->links, sizeof node_list->links[0], node_list->count, i, 1);
}
uint32_t raviX_node_list_size(GraphNodeList *list) { return list->count; }
nodeId_t raviX_node_list_at(GraphNodeList *list, uint32_t i)
{
if (i < list->count)
return list->links[i].node_index;
assert(false);
return (nodeId_t)-1;
}
Graph *raviX_init_graph(nodeId_t entry, nodeId_t exit, void *userdata)
{
Graph *g = (Graph *)calloc(1, sizeof(Graph));
g->allocated = 0;
g->nodes = NULL;
raviX_allocator_init(&g->node_allocator, "node_allocator", sizeof(GraphNode), sizeof(double),
sizeof(GraphNode) * 32);
raviX_add_node(g, entry);
raviX_add_node(g, exit);
g->entry = entry;
g->exit = exit;
g->userdata = userdata;
return g;
}
static void raviX_destroy_node(GraphNode *n)
{
if (n == NULL)
return;
node_list_destroy(&n->preds);
node_list_destroy(&n->succs);
}
void raviX_destroy_graph(Graph *g)
{
for (unsigned i = 0; i < g->allocated; i++) {
raviX_destroy_node(g->nodes[i]);
}
raviX_allocator_destroy(&g->node_allocator);
free(g->nodes);
free(g);
}
static GraphNode *raviX_get_node(const Graph *g, nodeId_t index)
{
if (index < g->allocated && g->nodes[index] != NULL) {
// already allocated
return g->nodes[index];
}
return NULL;
}
static void raviX_graph_grow(Graph *g, nodeId_t needed)
{
unsigned new_size = needed + 8;
GraphNode **new_data =
raviX_realloc_array(g->nodes, sizeof(GraphNode*), g->allocated, new_size);
g->allocated = new_size;
g->nodes = new_data;
}
static GraphNode *raviX_add_node(Graph *g, nodeId_t index)
{
if (index < g->allocated && g->nodes[index] != NULL) {
// already allocated
return g->nodes[index];
}
if (index >= g->allocated) {
raviX_graph_grow(g, index);
}
assert(index < g->allocated);
GraphNode *n = raviX_allocator_allocate(&g->node_allocator, 0);
assert(n->pre == 0);
assert(n->rpost == 0);
node_list_init(&n->preds);
node_list_init(&n->succs);
/* note that each node must have an index such that n = nodes[index] */
n->index = index;
g->nodes[index] = n;
return n;
}
void raviX_add_edge(Graph *g, nodeId_t from, nodeId_t to)
{
GraphNode *prednode = raviX_add_node(g, from);
GraphNode *succnode = raviX_add_node(g, to);
node_list_add(&prednode->succs, to);
node_list_add(&succnode->preds, from);
}
void raviX_delete_edge(Graph *g, nodeId_t a, nodeId_t b)
{
GraphNodeList *successors_of_a = raviX_successors(raviX_graph_node(g, a));
GraphNodeList *predecessors_of_b = raviX_predecessors(raviX_graph_node(g, b));
assert(successors_of_a);
assert(predecessors_of_b);
if (successors_of_a == NULL || predecessors_of_b == NULL)
return;
node_list_delete(successors_of_a, b);
node_list_delete(predecessors_of_b, a);
}
bool raviX_has_edge(Graph *g, nodeId_t from, nodeId_t to)
{
GraphNode *prednode = raviX_get_node(g, from);
if (prednode == NULL)
return false;
return node_list_search(&prednode->succs, to) != -1;
}
enum EdgeType raviX_get_edge_type(Graph *g, nodeId_t from, nodeId_t to)
{
GraphNode *prednode = raviX_get_node(g, from);
if (prednode == NULL)
return EDGE_TYPE_UNCLASSIFIED;
struct GraphNodeLink *node_link = node_list_get(&prednode->succs, to);
if (node_link == NULL)
return EDGE_TYPE_UNCLASSIFIED;
return node_link->edge_type;
}
void raviX_for_each_node(Graph *g, void (*callback)(void *arg, Graph *g, nodeId_t nodeid), void *arg)
{
for (unsigned i = 0; i < g->allocated; i++) {
if (g->nodes[i] != NULL) {
callback(arg, g, g->nodes[i]->index);
}
}
}
/* says how many nodes are in the graph */
uint32_t raviX_graph_size(Graph *g)
{
uint32_t count = 0;
for (unsigned i = 0; i < g->allocated; i++) {
if (g->nodes[i] != NULL) {
count++;
}
}
return count;
}
uint32_t raviX_node_RPO(GraphNode *n)
{
assert(n);
return n->rpost;
}
nodeId_t raviX_node_index(GraphNode *n)
{
assert(n);
return n->index;
}
GraphNode *raviX_graph_node(Graph *g, nodeId_t index)
{
assert(index < g->allocated);
return g->nodes[index];
}
GraphNodeList *raviX_predecessors(GraphNode *n)
{
assert(n);
return &n->preds;
}
GraphNodeList *raviX_successors(GraphNode *n)
{
assert(n);
return &n->succs;
}
struct classifier_state {
uint32_t preorder;
uint32_t rpostorder;
};
/*
* Do a recursive depth first search and mark nodes with pre/reverse post order sequence, as well
* as classify links. Algorithm from figure 3.2 in Building and Optimizing Compiler
*/
static void DFS_classify(Graph *g, GraphNode *n, struct classifier_state *state)
{
assert(n);
n->pre = state->preorder;
state->preorder++;
/* For each successor node */
for (unsigned i = 0; i < n->succs.count; i++) {
struct GraphNodeLink *E = &n->succs.links[i];
GraphNode *S = g->nodes[E->node_index];
if (S->pre == 0) {
E->edge_type = EDGE_TYPE_TREE;
DFS_classify(g, S, state);
} else if (S->rpost == 0) {
E->edge_type = EDGE_TYPE_BACKWARD;
} else if (n->pre < S->pre) {
E->edge_type = EDGE_TYPE_FORWARD;
} else {
E->edge_type = EDGE_TYPE_CROSS;
}
}
n->rpost = state->rpostorder;
state->rpostorder--;
}
/*
Classify links in the graph. Implements algorithm described in
figure 3.2 - Building an Optimizing Compiler. This algorithm is also implemented
in MIR.
*/
void raviX_classify_edges(Graph *g)
{
uint32_t N = raviX_graph_size(g);
if (N == 0)
return;
struct classifier_state state = {.preorder = 1, .rpostorder = N};
/* reset all data we will be computing */
for (unsigned i = 0; i < g->allocated; i++) {
if (g->nodes[i] != NULL) {
g->nodes[i]->pre = 0;
g->nodes[i]->rpost = 0;
for (unsigned i = 0; i < g->nodes[i]->succs.count; i++) {
struct GraphNodeLink *E = &g->nodes[i]->succs.links[i];
E->edge_type = 0;
}
}
}
DFS_classify(g, g->nodes[g->entry], &state);
}
static int rpost_cmp(const void *a1, const void *a2)
{
const GraphNode *n1 = *((const GraphNode **)a1);
const GraphNode *n2 = *((const GraphNode **)a2);
int result = n1->rpost - n2->rpost;
return result;
}
static int post_cmp(const void *a1, const void *a2) { return -rpost_cmp(a1, a2); }
void raviX_sort_nodes_by_RPO(GraphNode **nodes, size_t count, bool forward)
{
qsort(nodes, count, sizeof(GraphNode *), forward ? post_cmp : rpost_cmp);
}
GraphNode **raviX_graph_nodes_sorted_by_RPO(Graph *g, bool forward)
{
uint32_t N = raviX_graph_size(g);
GraphNode **nodes = calloc(N, sizeof(GraphNode *));
unsigned j = 0;
for (unsigned i = 0; i < g->allocated; i++) {
if (g->nodes[i] == NULL)
continue;
nodes[j++] = g->nodes[i];
}
assert(j == N);
raviX_sort_nodes_by_RPO(nodes, N, forward);
return nodes;
}
static void draw_node(void *arg, Graph *g, uint32_t nodeid)
{
FILE *fp = (FILE *)arg;
GraphNodeList *successors = raviX_successors(raviX_graph_node(g, nodeid));
if (!successors)
return;
for (unsigned i = 0; i < raviX_node_list_size(successors); i++) {
fprintf(fp, "L%d -> L%d\n", nodeid, raviX_node_list_at(successors, i));
}
}
void raviX_draw_graph(Graph *g, FILE *fp)
{
fprintf(fp, "digraph {\n");
raviX_for_each_node(g, draw_node, fp);
fprintf(fp, "}\n");
}

@ -0,0 +1,99 @@
#ifndef ravicomp_GRAPH_H
#define ravicomp_GRAPH_H
#include "allocate.h"
#include "common.h"
#include <stdint.h>
#include <stdbool.h>
/*
* Various graph manipulation routines.
* The graph is designed to manage nodes that are just integer ids.
* Node ids range from [0..n) - hence one can simply represent nodes as arrays.
*
* The graph structure does not care what the node represents and
* knows nothing about it. The benefit of this approach is that we can make
* the graph algorithms reusable. There may be some performance cost as we
* need to map node ids to nodes.
*
* The assumption here is that each node corresponds to a basic block in
* the program intermediate code. And each basic block is identified by a node
* id which can be used to construct the control flow graph.
*/
/* nodeId_t is declared elsewhere */
typedef struct Graph Graph;
typedef struct GraphNode GraphNode;
typedef struct GraphNodeList GraphNodeList;
enum EdgeType {
EDGE_TYPE_UNCLASSIFIED = 0,
EDGE_TYPE_TREE = 1,
EDGE_TYPE_BACKWARD = 2,
EDGE_TYPE_FORWARD = 4,
EDGE_TYPE_CROSS = 8
};
/* Initialize the graph data structure and associate some userdata with it. */
Graph *raviX_init_graph(nodeId_t entry, nodeId_t exit, void *userdata);
/* Destroy the graph data structure */
void raviX_destroy_graph(Graph *g);
/* Add an edge from one node a to b. Both nodes a and b will be implicitly added
* to the graph if they do not already exist.
*/
void raviX_add_edge(Graph *g, nodeId_t a, nodeId_t b);
/* Check if an edge exists from one node a to b */
bool raviX_has_edge(Graph *g, nodeId_t a, nodeId_t b);
/* Delete an edge from a to b */
void raviX_delete_edge(Graph *g, nodeId_t a, nodeId_t b);
/* Get the edge classification for edge from a to b; this is only available if graph has been
* analyzed for edges. */
enum EdgeType raviX_get_edge_type(Graph *g, nodeId_t a, nodeId_t b);
/* Get node identified by index */
GraphNode *raviX_graph_node(Graph *g, nodeId_t index);
/* Get the RPO - reverse post order index of the node */
uint32_t raviX_node_RPO(GraphNode *n);
/* Get the node's id */
nodeId_t raviX_node_index(GraphNode *n);
/* Get list of predecessors */
GraphNodeList *raviX_predecessors(GraphNode *n);
/* Get list of successors */
GraphNodeList *raviX_successors(GraphNode *n);
/* Number of entries in the node_list */
uint32_t raviX_node_list_size(GraphNodeList *list);
/* Get the nodeId at given node_link position */
nodeId_t raviX_node_list_at(GraphNodeList *list, uint32_t i);
void raviX_for_each_node(Graph *g, void (*callback)(void *arg, Graph *g, nodeId_t nodeid), void *arg);
/*
* Classifies links in the graph and also computes the
* reverse post order value.
*/
void raviX_classify_edges(Graph *g);
/*
* Returns a sorted array (allocated).
* Sorted by reverse postorder value.
* If forward=true then
* it will be the opposite direction, so to get reverse postorder,
* set forward=false.
* You must deallocate the array when done.
* The array size will be equal to raviX_graph_size(g).
* Before attempting to sort, you must have called
* raviX_classify_edges(g).
*/
GraphNode **raviX_graph_nodes_sorted_by_RPO(Graph *g, bool forward);
void raviX_sort_nodes_by_RPO(GraphNode **nodes, size_t count, bool forward);
/* says how many nodes are in the graph */
uint32_t raviX_graph_size(Graph *g);
/* Generates GraphViz (dot) output */
void raviX_draw_graph(Graph *g, FILE *fp);
#endif

@ -0,0 +1,427 @@
/*
* Copyright © 2009 Intel Corporation
* Copyright © 1988-2004 Keith Packard and Bart Massey.
*
* 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 (including the next
* paragraph) 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.
*
* Except as contained in this notice, the names of the authors
* or their institutions shall not be used in advertising or
* otherwise to promote the sale, use or other dealings in this
* Software without prior written authorization from the
* authors.
*
* Authors:
* Eric Anholt <eric@anholt.net>
* Keith Packard <keithp@keithp.com>
*/
#include <assert.h>
#include <stdlib.h>
#include <hash_table.h>
#define ARRAY_SIZE(array) ((int)(sizeof(array) / sizeof(array[0])))
/*
* From Knuth -- a good choice for hash/rehash values is p, p-2 where
* p and p-2 are both prime. These tables are sized to have an extra 10%
* free to avoid exponential performance degradation as the hash table fills
*/
static const uint32_t deleted_key_value;
static const void *deleted_key = &deleted_key_value;
static const struct {
uint32_t max_entries, size, rehash;
} hash_sizes[] = {
{ 2, 5, 3 },
{ 4, 7, 5 },
{ 8, 13, 11 },
{ 16, 19, 17 },
{ 32, 43, 41 },
{ 64, 73, 71 },
{ 128, 151, 149 },
{ 256, 283, 281 },
{ 512, 571, 569 },
{ 1024, 1153, 1151 },
{ 2048, 2269, 2267 },
{ 4096, 4519, 4517 },
{ 8192, 9013, 9011 },
{ 16384, 18043, 18041 },
{ 32768, 36109, 36107 },
{ 65536, 72091, 72089 },
{ 131072, 144409, 144407 },
{ 262144, 288361, 288359 },
{ 524288, 576883, 576881 },
{ 1048576, 1153459, 1153457 },
{ 2097152, 2307163, 2307161 },
{ 4194304, 4613893, 4613891 },
{ 8388608, 9227641, 9227639 },
{ 16777216, 18455029, 18455027 },
{ 33554432, 36911011, 36911009 },
{ 67108864, 73819861, 73819859 },
{ 134217728, 147639589, 147639587 },
{ 268435456, 295279081, 295279079 },
{ 536870912, 590559793, 590559791 },
{ 1073741824, 1181116273, 1181116271},
{ 2147483648ul, 2362232233ul, 2362232231ul}
};
static int
entry_is_free(const HashEntry *entry)
{
return entry->key == NULL;
}
static int
entry_is_deleted(const HashEntry *entry)
{
return entry->key == deleted_key;
}
static int
entry_is_present(const HashEntry *entry)
{
return entry->key != NULL && entry->key != deleted_key;
}
HashTable *
raviX_hash_table_create(uint32_t (*hash_function)(const void *key),
int (*key_equals_function)(const void *a,
const void *b))
{
HashTable *ht;
ht = malloc(sizeof(*ht));
if (ht == NULL)
return NULL;
ht->size_index = 0;
ht->size = hash_sizes[ht->size_index].size;
ht->rehash = hash_sizes[ht->size_index].rehash;
ht->max_entries = hash_sizes[ht->size_index].max_entries;
ht->hash_function = hash_function;
ht->key_equals_function = key_equals_function;
ht->table = calloc(ht->size, sizeof(*ht->table));
ht->entries = 0;
ht->deleted_entries = 0;
if (ht->table == NULL) {
free(ht);
return NULL;
}
return ht;
}
/**
* Frees the given hash table.
*
* If delete_function is passed, it gets called on each entry present before
* freeing.
*/
void
raviX_hash_table_destroy(HashTable *ht,
void (*delete_function)(HashEntry *entry))
{
if (!ht)
return;
if (delete_function) {
HashEntry *entry;
hash_table_foreach(ht, entry) {
delete_function(entry);
}
}
free(ht->table);
free(ht);
}
/**
* Finds a hash table entry with the given key.
*
* Returns NULL if no entry is found. Note that the data pointer may be
* modified by the user.
*/
HashEntry *
raviX_hash_table_search(HashTable *ht, const void *key)
{
uint32_t hash = ht->hash_function(key);
return raviX_hash_table_search_pre_hashed(ht, hash, key);
}
/**
* Finds a hash table entry with the given key and hash of that key.
*
* Returns NULL if no entry is found. Note that the data pointer may be
* modified by the user.
*/
HashEntry *
raviX_hash_table_search_pre_hashed(HashTable *ht, uint32_t hash,
const void *key)
{
uint32_t start_hash_address = hash % ht->size;
uint32_t hash_address = start_hash_address;
do {
uint32_t double_hash;
HashEntry *entry = ht->table + hash_address;
if (entry_is_free(entry)) {
return NULL;
} else if (entry_is_present(entry) && entry->hash == hash) {
if (ht->key_equals_function(key, entry->key)) {
return entry;
}
}
double_hash = 1 + hash % ht->rehash;
hash_address = (hash_address + double_hash) % ht->size;
} while (hash_address != start_hash_address);
return NULL;
}
static void
hash_table_rehash(HashTable *ht, int new_size_index)
{
HashTable old_ht;
HashEntry *table, *entry;
if (new_size_index >= ARRAY_SIZE(hash_sizes))
return;
table = calloc(hash_sizes[new_size_index].size, sizeof(*ht->table));
if (table == NULL)
return;
old_ht = *ht;
ht->table = table;
ht->size_index = new_size_index;
ht->size = hash_sizes[ht->size_index].size;
ht->rehash = hash_sizes[ht->size_index].rehash;
ht->max_entries = hash_sizes[ht->size_index].max_entries;
ht->entries = 0;
ht->deleted_entries = 0;
hash_table_foreach(&old_ht, entry) {
raviX_hash_table_insert_pre_hashed(ht, entry->hash,
entry->key, entry->data);
}
free(old_ht.table);
}
/**
* Inserts the key into the table.
*
* Note that insertion may rearrange the table on a resize or rehash,
* so previously found hash_entries are no longer valid after this function.
*/
HashEntry *
raviX_hash_table_insert(HashTable *ht, const void *key, void *data)
{
uint32_t hash = ht->hash_function(key);
/* Make sure nobody tries to add one of the magic values as a
* key. If you need to do so, either do so in a wrapper, or
* store keys with the magic values separately in the struct
* hash_table.
*/
assert(key != NULL);
return raviX_hash_table_insert_pre_hashed(ht, hash, key, data);
}
/**
* Inserts the key with the given hash into the table.
*
* Note that insertion may rearrange the table on a resize or rehash,
* so previously found hash_entries are no longer valid after this function.
*/
HashEntry *
raviX_hash_table_insert_pre_hashed(HashTable *ht, uint32_t hash,
const void *key, void *data)
{
uint32_t start_hash_address, hash_address;
HashEntry *available_entry = NULL;
if (ht->entries >= ht->max_entries) {
hash_table_rehash(ht, ht->size_index + 1);
} else if (ht->deleted_entries + ht->entries >= ht->max_entries) {
hash_table_rehash(ht, ht->size_index);
}
start_hash_address = hash % ht->size;
hash_address = start_hash_address;
do {
HashEntry *entry = ht->table + hash_address;
uint32_t double_hash;
if (!entry_is_present(entry)) {
/* Stash the first available entry we find */
if (available_entry == NULL)
available_entry = entry;
if (entry_is_free(entry))
break;
}
/* Implement replacement when another insert happens
* with a matching key. This is a relatively common
* feature of hash tables, with the alternative
* generally being "insert the new value as well, and
* return it first when the key is searched for".
*
* Note that the hash table doesn't have a delete
* callback. If freeing of old data pointers is
* required to avoid memory leaks, perform a search
* before inserting.
*/
if (!entry_is_deleted(entry) &&
entry->hash == hash &&
ht->key_equals_function(key, entry->key)) {
entry->key = key;
entry->data = data;
return entry;
}
double_hash = 1 + hash % ht->rehash;
hash_address = (hash_address + double_hash) % ht->size;
} while (hash_address != start_hash_address);
if (available_entry) {
if (entry_is_deleted(available_entry))
ht->deleted_entries--;
available_entry->hash = hash;
available_entry->key = key;
available_entry->data = data;
ht->entries++;
return available_entry;
}
/* We could hit here if a required resize failed. An unchecked-malloc
* application could ignore this result.
*/
return NULL;
}
/**
* This function searches for, and removes an entry from the hash table.
*
* If the caller has previously found a HashEntry pointer,
* (from calling hash_table_search or remembering it from
* hash_table_insert), then hash_table_remove_entry can be called
* instead to avoid an extra search.
*/
void
raviX_hash_table_remove(HashTable *ht, const void *key)
{
HashEntry *entry;
entry = raviX_hash_table_search(ht, key);
raviX_hash_table_remove_entry(ht, entry);
}
/**
* This function deletes the given hash table entry.
*
* Note that deletion doesn't otherwise modify the table, so an iteration over
* the table deleting entries is safe.
*/
void
raviX_hash_table_remove_entry(HashTable *ht, HashEntry *entry)
{
if (!entry)
return;
entry->key = deleted_key;
ht->entries--;
ht->deleted_entries++;
}
/**
* This function is an iterator over the hash table.
*
* Pass in NULL for the first entry, as in the start of a for loop. Note that
* an iteration over the table is O(table_size) not O(entries).
*/
HashEntry *
raviX_hash_table_next_entry(HashTable *ht, HashEntry *entry)
{
if (entry == NULL)
entry = ht->table;
else
entry = entry + 1;
for (; entry != ht->table + ht->size; entry++) {
if (entry_is_present(entry)) {
return entry;
}
}
return NULL;
}
#if 0
#ifndef _WIN32
/**
* Returns a random entry from the hash table.
*
* This may be useful in implementing random replacement (as opposed
* to just removing everything) in caches based on this hash table
* implementation. @predicate may be used to filter entries, or may
* be set to NULL for no filtering.
*/
HashEntry *
hash_table_random_entry(HashTable *ht,
int (*predicate)(HashEntry *entry))
{
HashEntry *entry;
uint32_t i = random() % ht->size;
if (ht->entries == 0)
return NULL;
for (entry = ht->table + i; entry != ht->table + ht->size; entry++) {
if (entry_is_present(entry) &&
(!predicate || predicate(entry))) {
return entry;
}
}
for (entry = ht->table; entry != ht->table + i; entry++) {
if (entry_is_present(entry) &&
(!predicate || predicate(entry))) {
return entry;
}
}
return NULL;
}
#endif
#endif

@ -0,0 +1,109 @@
/*
* Copyright © 2009 Intel Corporation
*
* 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 (including the next
* paragraph) 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.
*
* Authors:
* Eric Anholt <eric@anholt.net>
*
*/
#ifndef ravicomp_HASH_TABLE_H
#define ravicomp_HASH_TABLE_H
#ifdef __cplusplus
extern "C" {
#endif
#include <inttypes.h>
typedef struct HashEntry {
uint32_t hash;
const void *key;
void *data;
} HashEntry;
typedef struct HashTable {
HashEntry *table;
uint32_t (*hash_function)(const void *key);
int (*key_equals_function)(const void *a, const void *b);
uint32_t size;
uint32_t rehash;
uint32_t max_entries;
uint32_t size_index;
uint32_t entries;
uint32_t deleted_entries;
} HashTable;
HashTable *
raviX_hash_table_create(uint32_t (*hash_function)(const void *key),
int (*key_equals_function)(const void *a,
const void *b));
void
raviX_hash_table_destroy(HashTable *ht,
void (*delete_function)(HashEntry *entry));
HashEntry *
raviX_hash_table_insert(HashTable *ht, const void *key, void *data);
HashEntry *
raviX_hash_table_search(HashTable *ht, const void *key);
void
raviX_hash_table_remove(HashTable *ht, const void *key);
void
raviX_hash_table_remove_entry(HashTable *ht, HashEntry *entry);
HashEntry *
raviX_hash_table_next_entry(HashTable *ht,
HashEntry *entry);
//HashEntry *
//hash_table_random_entry(HashTable *ht,
// int (*predicate)(HashEntry *entry));
/**
* This foreach function is safe against deletion (which just replaces
* an entry's data with the deleted marker), but not against insertion
* (which may rehash the table, making entry a dangling pointer).
*/
#define hash_table_foreach(ht, entry) \
for (entry = raviX_hash_table_next_entry(ht, NULL); \
entry != NULL; \
entry = raviX_hash_table_next_entry(ht, entry))
/* Alternate interfaces to reduce repeated calls to hash function. */
HashEntry *
raviX_hash_table_search_pre_hashed(HashTable *ht,
uint32_t hash,
const void *key);
HashEntry *
raviX_hash_table_insert_pre_hashed(HashTable *ht,
uint32_t hash,
const void *key, void *data);
#ifdef __cplusplus
} /* extern C */
#endif
#endif

@ -0,0 +1,990 @@
/**
* The lexer is basically a hacked version of Lua 5.3 lexer.
* Copyright (C) 1994-2019 Lua.org, PUC-Rio.
*/
#include "fnv_hash.h"
#include "parser.h"
#include <limits.h>
#include <locale.h>
#include <math.h>
enum { EOZ = -1 }; /* end of stream */
#define cast(t, v) ((t)v)
#define cast_int(v) cast(int, v)
#define cast_uchar(c) cast(unsigned char, c)
#define cast_num(n) cast(lua_Number, n)
#define l_castU2S(i) ((lua_Integer)(i))
static inline int zgetc(LexerState *z) { return z->n-- > 0 ? cast_uchar(*z->p++) : EOZ; }
static inline void next(LexerState *ls) { ls->current = zgetc(ls); }
static inline bool currIsNewline(LexerState *ls) { return ls->current == '\n' || ls->current == '\r'; }
static inline char lua_getlocaledecpoint(void) { return localeconv()->decimal_point[0]; }
#define ARRAY_SIZE(array) ((int)(sizeof(array) / sizeof(array[0])))
/*Note: Following array was generated using utils/tokenstr.h */
static const char *const luaX_tokens[] = {
"and",
"break",
"do",
"else",
"elseif",
"end",
"false",
"for",
"function",
"goto",
"if",
"in",
"local",
"defer",
"nil",
"not",
"or",
"repeat",
"return",
"then",
"true",
"until",
"while",
"/"
"/",
"..",
"...",
"==",
">=",
"<=",
"~=",
"<<",
">>",
"::",
"@integer",
"@number",
"@integer[]",
"@number[]",
"@table",
"@string",
"@closure",
"<eof>",
"<number>",
"<integer>",
"<name>",
"<string>",
};
/* Says whether the given string represents a Lua/Ravi keyword i.e. reserved word */
static inline int is_reserved(const StringObject *s) { return s->reserved; }
enum { ALPHABIT = 0, DIGITBIT = 1, PRINTBIT = 2, SPACEBIT = 3, XDIGITBIT = 4 };
#define MASK(B) (1 << (B))
static const lu_byte luai_ctype_[UCHAR_MAX + 2] = {
0x00, /* EOZ */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0. */
0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 1. */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, /* 2. */
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, /* 3. */
0x16, 0x16, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x05, /* 4. */
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, /* 5. */
0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x05, 0x04, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x05, /* 6. */
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, /* 7. */
0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 8. */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 9. */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* a. */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* b. */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* c. */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* d. */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* e. */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* f. */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
/*
** add 1 to char to allow index -1 (EOZ)
*/
#define testprop(c, p) (luai_ctype_[(c) + 1] & (p))
/*
** 'lalpha' (Lua alphabetic) and 'lalnum' (Lua alphanumeric) both include '_'
*/
static inline bool lislalpha(int c) { return testprop(c, MASK(ALPHABIT)); }
static inline bool lislalnum(int c) { return testprop(c, (MASK(ALPHABIT) | MASK(DIGITBIT))); }
static inline bool lisdigit(int c) { return testprop(c, MASK(DIGITBIT)); }
static inline bool lisspace(int c) { return testprop(c, MASK(SPACEBIT)); }
// static inline bool lisprint(int c) { return testprop(c, MASK(PRINTBIT)); }
static inline bool lisxdigit(int c) { return testprop(c, MASK(XDIGITBIT)); }
/*
** this 'ltolower' only works for alphabetic characters
*/
static inline int ltolower(int c) { return ((c) | ('A' ^ 'a')); }
/*
Creates a new string object. string objects are interned in a hash set.
If the string matches a keyword then the reserved attribute will be set to the token id associated
with the keyword else this attribute will be -1. The hash value of the string is stored the 'hash'
attribute. Note that we need to allow strings that have embedded 0 character hence the length
is explicit. But all tokens and reserved keywords are expected to be standard C strings.
*/
const StringObject *raviX_create_string(CompilerState *compiler_state, const char *input, uint32_t len)
{
StringObject temp = {.len = len, .str = input, .hash = fnv1_hash_data(input, len), .reserved = -1};
SetEntry *entry = raviX_set_search_pre_hashed(compiler_state->strings, temp.hash, &temp);
if (entry != NULL)
/* found the string */
return (StringObject *)entry->key;
else {
StringObject *new_string = raviX_allocator_allocate(&compiler_state->string_object_allocator, 0);
char *s = raviX_allocator_allocate(&compiler_state->string_allocator, len + 1); /* allow for 0 terminator */
memcpy(s, input, len);
s[len] = 0; /* 0 terminate string, however string may contain embedded 0 characters */
new_string->str = s;
new_string->len = len;
new_string->hash = temp.hash;
new_string->reserved = -1;
/* Check if this is a keyword, linear search is okay as we do this only when we first
* encounter a keyword
*/
for (int i = 0; i < ARRAY_SIZE(luaX_tokens); i++) {
if (lislalpha(luaX_tokens[i][0]) || luaX_tokens[i][0] == '@') {
if (strcmp(luaX_tokens[i], s) == 0) {
new_string->reserved = i; /* save index of the keyword */
break;
}
}
}
raviX_set_add_pre_hashed(compiler_state->strings, temp.hash, new_string);
return new_string;
}
}
#define lua_str2number(s, p) ((lua_Number)strtod((s), (p)))
static void save(LexerState *ls, int c);
void raviX_token2str(int token, TextBuffer *mb)
{
if (token < FIRST_RESERVED) { /* single-byte symbols? */
assert(token == cast_uchar(token));
raviX_buffer_add_fstring(mb, "'%c'", token);
} else {
const char *s = luaX_tokens[token - FIRST_RESERVED];
if (token < TOK_EOS) /* fixed format - reserved keywords */
raviX_buffer_add_fstring(mb, "'%s'", s);
else /* names, strings, and numerals - note that @<usertype> is covered here */
raviX_buffer_add_string(mb, s);
}
}
static void txtToken(LexerState *ls, int token)
{
switch (token) {
case TOK_NAME:
case TOK_STRING:
case TOK_FLT:
case TOK_INT:
save(ls, '\0');
raviX_buffer_add_fstring(&ls->container->error_message, "'%s'", raviX_buffer_data(ls->buff));
break;
default:
raviX_token2str(token, &ls->container->error_message);
}
}
static void lexerror(LexerState *ls, const char *msg, int token)
{
raviX_buffer_add_fstring(&ls->container->error_message, "%s(%d): %s", ls->source, ls->linenumber, msg);
if (token) {
raviX_buffer_add_string(&ls->container->error_message, " near ");
txtToken(ls, token);
}
longjmp(ls->container->env, 1);
}
void raviX_syntaxerror(LexerState *ls, const char *msg) { lexerror(ls, msg, ls->t.token); }
static void save(LexerState *ls, int c)
{
TextBuffer *b = ls->buff;
if (raviX_buffer_len(b) + 1 > raviX_buffer_size(b)) {
size_t newsize;
if (raviX_buffer_size(b) >= INT_MAX / 2)
lexerror(ls, "lexical element too long", 0);
size_t oldsize = raviX_buffer_size(b);
if (oldsize == 0)
newsize = 32;
else
newsize = oldsize * 2;
raviX_buffer_resize(b, newsize);
}
raviX_buffer_addc(b, c);
}
static inline void save_and_next(LexerState *ls)
{
save(ls, ls->current);
next(ls);
}
/*
** creates a new interned string.
*/
static const StringObject *luaX_newstring(LexerState *ls, const char *str, uint32_t l)
{
return raviX_create_string(ls->container, str, l);
}
/*
** increment line number and skips newline sequence (any of
** \n, \r, \n\r, or \r\n)
*/
static void inclinenumber(LexerState *ls)
{
int old = ls->current;
assert(currIsNewline(ls));
next(ls); /* skip '\n' or '\r' */
if (currIsNewline(ls) && ls->current != old)
next(ls); /* skip '\n\r' or '\r\n' */
if (++ls->linenumber >= INT_MAX)
lexerror(ls, "chunk has too many lines", 0);
}
LexerState *raviX_init_lexer(CompilerState *container, const char *buf, size_t buflen,
const char *source)
{
LexerState *ls = (LexerState *)calloc(1, sizeof(LexerState));
ls->container = container;
ls->t.token = 0;
ls->buf = buf;
ls->bufsize = buflen;
ls->n = ls->bufsize;
ls->p = ls->buf;
ls->current = zgetc(ls);
ls->lookahead.token = TOK_EOS; /* no look-ahead token */
ls->linenumber = 1;
ls->lastline = 1;
ls->source = source;
ls->envn = raviX_create_string(ls->container, LUA_ENV, (uint32_t)strlen(LUA_ENV))->str; /* get env name */
ls->buff = &container->buff;
for (int i = 0; i < NUM_RESERVED; i++) {
raviX_create_string(ls->container, luaX_tokens[i], (uint32_t)strlen(luaX_tokens[i]));
}
return ls;
}
void raviX_destroy_lexer(LexerState *ls)
{
if (ls == NULL)
return;
free(ls);
}
const LexerInfo *raviX_get_lexer_info(LexerState *ls) { return (LexerInfo *)ls; }
/*
** =======================================================
** LEXICAL ANALYZER
** =======================================================
*/
static int check_next1(LexerState *ls, int c)
{
if (ls->current == c) {
next(ls);
return 1;
} else
return 0;
}
static int check_save_next1(LexerState *ls, int c)
{
if (ls->current == c) {
save_and_next(ls);
return 1;
} else
return 0;
}
/*
** Check whether current char is in set 'set' (with two chars) and
** saves it
*/
static int check_next2(LexerState *ls, const char *set)
{
assert(set[2] == '\0');
if (ls->current == set[0] || ls->current == set[1]) {
save_and_next(ls);
return 1;
} else
return 0;
}
static int luaO_hexavalue(int c)
{
if (lisdigit(c))
return c - '0';
else
return (ltolower(c) - 'a') + 10;
}
static int isneg(const char **s)
{
if (**s == '-') {
(*s)++;
return 1;
} else if (**s == '+')
(*s)++;
return 0;
}
/*
** {==================================================================
** Lua's implementation for 'lua_strx2number'
** ===================================================================
*/
#if !defined(lua_strx2number)
/* maximum number of significant digits to read (to avoid overflows
even with single floats) */
#define MAXSIGDIG 30
/*
** convert an hexadecimal numeric string to a number, following
** C99 specification for 'strtod'
*/
static lua_Number lua_strx2number(const char *s, char **endptr)
{
int dot = lua_getlocaledecpoint();
lua_Number r = 0.0; /* result (accumulator) */
int sigdig = 0; /* number of significant digits */
int nosigdig = 0; /* number of non-significant digits */
int e = 0; /* exponent correction */
int neg; /* 1 if number is negative */
int hasdot = 0; /* true after seen a dot */
*endptr = (char *)s; /* nothing is valid yet */
while (lisspace(cast_uchar(*s)))
s++; /* skip initial spaces */
neg = isneg(&s); /* check signal */
if (!(*s == '0' && (*(s + 1) == 'x' || *(s + 1) == 'X'))) /* check '0x' */
return 0.0; /* invalid format (no '0x') */
for (s += 2;; s++) { /* skip '0x' and read numeral */
if (*s == dot) {
if (hasdot)
break; /* second dot? stop loop */
else
hasdot = 1;
} else if (lisxdigit(cast_uchar(*s))) {
if (sigdig == 0 && *s == '0') /* non-significant digit (zero)? */
nosigdig++;
else if (++sigdig <= MAXSIGDIG) /* can read it without overflow? */
r = (r * cast_num(16.0)) + luaO_hexavalue(*s);
else
e++; /* too many digits; ignore, but still count for exponent */
if (hasdot)
e--; /* decimal digit? correct exponent */
} else
break; /* neither a dot nor a digit */
}
if (nosigdig + sigdig == 0) /* no digits? */
return 0.0; /* invalid format */
*endptr = (char *)s; /* valid up to here */
e *= 4; /* each digit multiplies/divides value by 2^4 */
if (*s == 'p' || *s == 'P') { /* exponent part? */
int exp1 = 0; /* exponent value */
int neg1; /* exponent signal */
s++; /* skip 'p' */
neg1 = isneg(&s); /* signal */
if (!lisdigit(cast_uchar(*s)))
return 0.0; /* invalid; must have at least one digit */
while (lisdigit(cast_uchar(*s))) /* read exponent */
exp1 = exp1 * 10 + *(s++) - '0';
if (neg1)
exp1 = -exp1;
e += exp1;
*endptr = (char *)s; /* valid up to here */
}
if (neg)
r = -r;
return (lua_Number)ldexp(r, e);
}
#endif
/* }====================================================== */
/* maximum length of a numeral */
#if !defined(L_MAXLENNUM)
#define L_MAXLENNUM 200
#endif
static const char *l_str2dloc(const char *s, lua_Number *result, int mode)
{
char *endptr;
*result = (mode == 'x') ? lua_strx2number(s, &endptr) /* try to convert */
: lua_str2number(s, &endptr);
if (endptr == s)
return NULL; /* nothing recognized? */
while (lisspace(cast_uchar(*endptr)))
endptr++; /* skip trailing spaces */
return (*endptr == '\0') ? endptr : NULL; /* OK if no trailing characters */
}
/*
** Convert string 's' to a Lua number (put in 'result'). Return NULL
** on fail or the address of the ending '\0' on success.
** 'pmode' points to (and 'mode' contains) special things in the string:
** - 'x'/'X' means an hexadecimal numeral
** - 'n'/'N' means 'inf' or 'nan' (which should be rejected)
** - '.' just optimizes the search for the common case (nothing special)
** This function accepts both the current locale or a dot as the radix
** mark. If the convertion fails, it may mean number has a dot but
** locale accepts something else. In that case, the code copies 's'
** to a buffer (because 's' is read-only), changes the dot to the
** current locale radix mark, and tries to convert again.
*/
static const char *l_str2d(const char *s, lua_Number *result)
{
const char *endptr;
const char *pmode = strpbrk(s, ".xXnN");
int mode = pmode ? ltolower(cast_uchar(*pmode)) : 0;
if (mode == 'n') /* reject 'inf' and 'nan' */
return NULL;
endptr = l_str2dloc(s, result, mode); /* try to convert */
if (endptr == NULL) { /* failed? may be a different locale */
char buff[L_MAXLENNUM + 1];
const char *pdot = strchr(s, '.');
if (strlen(s) > L_MAXLENNUM || pdot == NULL)
return NULL; /* string too long or no dot; fail */
strcpy(buff, s); /* copy string to buffer */
buff[pdot - s] = lua_getlocaledecpoint(); /* correct decimal point */
endptr = l_str2dloc(buff, result, mode); /* try again */
if (endptr != NULL)
endptr = s + (endptr - buff); /* make relative to 's' */
}
return endptr;
}
#define MAXBY10 cast(lua_Unsigned, LUA_MAXINTEGER / 10)
#define MAXLASTD cast_int(LUA_MAXINTEGER % 10)
static const char *l_str2int(const char *s, lua_Integer *result)
{
lua_Unsigned a = 0;
int empty = 1;
int neg;
while (lisspace(cast_uchar(*s)))
s++; /* skip initial spaces */
neg = isneg(&s);
if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) { /* hex? */
s += 2; /* skip '0x' */
for (; lisxdigit(cast_uchar(*s)); s++) {
a = a * 16 + luaO_hexavalue(*s);
empty = 0;
}
} else { /* decimal */
for (; lisdigit(cast_uchar(*s)); s++) {
int d = *s - '0';
if (a >= MAXBY10 && (a > MAXBY10 || d > MAXLASTD + neg)) /* overflow? */
return NULL; /* do not accept it (as integer) */
a = a * 10 + d;
empty = 0;
}
}
while (lisspace(cast_uchar(*s)))
s++; /* skip trailing spaces */
if (empty || *s != '\0')
return NULL; /* something wrong in the numeral */
else {
*result = l_castU2S((neg) ? 0u - a : a);
return s;
}
}
struct konst {
uint8_t type;
union {
lua_Integer i;
lua_Number n;
};
};
static size_t luaO_str2num(const char *s, struct konst *o)
{
lua_Integer i;
lua_Number n;
const char *e;
if ((e = l_str2int(s, &i)) != NULL) { /* try as an integer */
o->i = i;
o->type = 1;
} else if ((e = l_str2d(s, &n)) != NULL) { /* else try as a float */
o->n = n;
o->type = 2;
} else
return 0; /* conversion failed */
return (e - s) + 1; /* success; return string size */
}
/* LUA_NUMBER */
/*
** this function is quite liberal in what it accepts, as 'luaO_str2num'
** will reject ill-formed numerals.
*/
static int read_numeral(LexerState *ls, SemInfo *seminfo)
{
struct konst obj;
const char *expo = "Ee";
int first = ls->current;
assert(lisdigit(ls->current));
save_and_next(ls);
if (first == '0' && check_next2(ls, "xX")) /* hexadecimal? */
expo = "Pp";
for (;;) {
if (check_next2(ls, expo)) /* exponent part? */
check_next2(ls, "-+"); /* optional exponent sign */
if (lisxdigit(ls->current))
save_and_next(ls);
else if (ls->current == '.')
save_and_next(ls);
else
break;
}
save(ls, '\0');
if (luaO_str2num(raviX_buffer_data(ls->buff), &obj) == 0) /* format error? */
lexerror(ls, "malformed number", TOK_FLT);
if (obj.type == 1) {
seminfo->i = obj.i;
return TOK_INT;
} else {
assert(obj.type == 2);
seminfo->r = obj.n;
return TOK_FLT;
}
}
/*
** skip a sequence '[=*[' or ']=*]'; if sequence is well formed, return
** its number of '='s; otherwise, return a negative number (-1 iff there
** are no '='s after initial bracket)
*/
static int skip_sep(LexerState *ls)
{
int count = 0;
int s = ls->current;
assert(s == '[' || s == ']');
save_and_next(ls);
while (ls->current == '=') {
save_and_next(ls);
count++;
}
return (ls->current == s) ? count : (-count) - 1;
}
static void read_long_string(LexerState *ls, SemInfo *seminfo, int sep)
{
int line = ls->linenumber; /* initial line (for error message) */
(void)line;
save_and_next(ls); /* skip 2nd '[' */
if (currIsNewline(ls)) /* string starts with a newline? */
inclinenumber(ls); /* skip it */
for (;;) {
switch (ls->current) {
case EOZ: { /* error */
const char *what = (seminfo ? "string" : "comment");
const char *msg = "";
(void)what;
// luaO_pushfstring(ls->L, "unfinished long %s (starting at line %d)",
// what, line);
lexerror(ls, msg, TOK_EOS);
break; /* to avoid warnings */
}
case ']': {
if (skip_sep(ls) == sep) {
save_and_next(ls); /* skip 2nd ']' */
goto endloop;
}
break;
}
case '\n':
case '\r': {
save(ls, '\n');
inclinenumber(ls);
if (!seminfo)
raviX_buffer_reset(ls->buff); /* avoid wasting space */
break;
}
default: {
if (seminfo)
save_and_next(ls);
else
next(ls);
}
}
}
endloop:
if (seminfo)
seminfo->ts = luaX_newstring(ls, raviX_buffer_data(ls->buff) + (2 + sep),
(uint32_t)(raviX_buffer_len(ls->buff) - 2 * (2 + sep)));
}
static void esccheck(LexerState *ls, int c, const char *msg)
{
if (!c) {
if (ls->current != EOZ)
save_and_next(ls); /* add current to buffer for error message */
lexerror(ls, msg, TOK_STRING);
}
}
static int gethexa(LexerState *ls)
{
save_and_next(ls);
esccheck(ls, lisxdigit(ls->current), "hexadecimal digit expected");
return luaO_hexavalue(ls->current);
}
static int readhexaesc(LexerState *ls)
{
int r = gethexa(ls);
r = (r << 4) + gethexa(ls);
raviX_buffer_remove(ls->buff, 2); /* remove saved chars from buffer */
return r;
}
// static unsigned long readutf8esc (LexerState *ls) {
// unsigned long r;
// int i = 4; /* chars to be removed: '\', 'u', '{', and first digit */
// save_and_next(ls); /* skip 'u' */
// esccheck(ls, ls->current == '{', "missing '{'");
// r = gethexa(ls); /* must have at least one digit */
// while ((save_and_next(ls), lisxdigit(ls->current))) {
// i++;
// r = (r << 4) + luaO_hexavalue(ls->current);
// esccheck(ls, r <= 0x10FFFF, "UTF-8 value too large");
// }
// esccheck(ls, ls->current == '}', "missing '}'");
// next(ls); /* skip '}' */
// raviX_buffer_remove(ls->buff, i); /* remove saved chars from buffer */
// return r;
//}
//
//
// static void utf8esc (LexerState *ls) {
// char buff[UTF8BUFFSZ];
// int n = luaO_utf8esc(buff, readutf8esc(ls));
// for (; n > 0; n--) /* add 'buff' to string */
// save(ls, buff[UTF8BUFFSZ - n]);
//}
static int readdecesc(LexerState *ls)
{
int i;
int r = 0; /* result accumulator */
for (i = 0; i < 3 && lisdigit(ls->current); i++) { /* read up to 3 digits */
r = 10 * r + ls->current - '0';
save_and_next(ls);
}
esccheck(ls, r <= UCHAR_MAX, "decimal escape too large");
raviX_buffer_remove(ls->buff, i); /* remove read digits from buffer */
return r;
}
static void read_string(LexerState *ls, int del, SemInfo *seminfo)
{
save_and_next(ls); /* keep delimiter (for error messages) */
while (ls->current != del) {
switch (ls->current) {
case EOZ:
lexerror(ls, "unfinished string", TOK_EOS);
break; /* to avoid warnings */
case '\n':
case '\r':
lexerror(ls, "unfinished string", TOK_STRING);
break; /* to avoid warnings */
case '\\': { /* escape sequences */
int c; /* final character to be saved */
save_and_next(ls); /* keep '\\' for error messages */
switch (ls->current) {
case 'a':
c = '\a';
goto read_save;
case 'b':
c = '\b';
goto read_save;
case 'f':
c = '\f';
goto read_save;
case 'n':
c = '\n';
goto read_save;
case 'r':
c = '\r';
goto read_save;
case 't':
c = '\t';
goto read_save;
case 'v':
c = '\v';
goto read_save;
case 'x':
c = readhexaesc(ls);
goto read_save;
// TODO - FIXME
// case 'u': utf8esc(ls); goto no_save;
case '\n':
case '\r':
inclinenumber(ls);
c = '\n';
goto only_save;
case '\\':
case '\"':
case '\'':
c = ls->current;
goto read_save;
case EOZ:
goto no_save; /* will raise an error next loop */
case 'z': { /* zap following span of spaces */
raviX_buffer_remove(ls->buff, 1); /* remove '\\' */
next(ls); /* skip the 'z' */
while (lisspace(ls->current)) {
if (currIsNewline(ls))
inclinenumber(ls);
else
next(ls);
}
goto no_save;
}
default: {
esccheck(ls, lisdigit(ls->current), "invalid escape sequence");
c = readdecesc(ls); /* digital escape '\ddd' */
goto only_save;
}
}
read_save:
next(ls);
/* go through */
only_save:
raviX_buffer_remove(ls->buff, 1); /* remove '\\' */
save(ls, c);
/* go through */
no_save:
break;
}
default:
save_and_next(ls);
}
}
save_and_next(ls); /* skip delimiter */
seminfo->ts = luaX_newstring(ls, raviX_buffer_data(ls->buff) + 1, (uint32_t)(raviX_buffer_len(ls->buff) - 2));
}
/*
** RAVI extension: generate a token for the cast operators -
** @number, @number[], @integer, @integer[], @table, @string, @closure
*/
static int casttoken(LexerState *ls, SemInfo *seminfo)
{
size_t n = raviX_buffer_len(ls->buff);
const char *s = raviX_buffer_data(ls->buff);
int tok;
/* @integer or @integer[] */
if (strncmp(s, "@integer", n) == 0)
tok = TOK_TO_INTEGER;
else if (strncmp(s, "@integer[]", n) == 0)
tok = TOK_TO_INTARRAY;
/* @number or @number[] */
else if (strncmp(s, "@number", n) == 0)
tok = TOK_TO_NUMBER;
else if (strncmp(s, "@number[]", n) == 0)
tok = TOK_TO_NUMARRAY;
/* @table */
else if (strncmp(s, "@table", n) == 0)
tok = TOK_TO_TABLE;
else if (strncmp(s, "@string", n) == 0)
tok = TOK_TO_STRING;
else if (strncmp(s, "@closure", n) == 0)
tok = TOK_TO_CLOSURE;
else {
seminfo->ts = luaX_newstring(ls, s + 1, (uint32_t)(n - 1)); /* omit @ */
tok = '@';
}
raviX_buffer_remove(ls->buff, (int)n); /* rewind but buffer still holds the saved characters */
return tok;
}
static int llex(LexerState *ls, SemInfo *seminfo)
{
raviX_buffer_reset(ls->buff);
for (;;) {
switch (ls->current) {
case '\n':
case '\r': { /* line breaks */
inclinenumber(ls);
break;
}
case ' ':
case '\f':
case '\t':
case '\v': { /* spaces */
next(ls);
break;
}
case '-': { /* '-' or '--' (comment) */
next(ls);
if (ls->current != '-')
return '-';
/* else is a comment */
next(ls);
if (ls->current == '[') { /* long comment? */
int sep = skip_sep(ls);
raviX_buffer_reset(ls->buff); /* 'skip_sep' may dirty the buffer */
if (sep >= 0) {
read_long_string(ls, NULL, sep); /* skip long comment */
raviX_buffer_reset(ls->buff); /* previous call may dirty the buff. */
break;
}
}
/* else short comment */
while (!currIsNewline(ls) && ls->current != EOZ)
next(ls); /* skip until end of line (or end of file) */
break;
}
case '[': { /* long string or simply '[' */
int sep = skip_sep(ls);
if (sep >= 0) {
read_long_string(ls, seminfo, sep);
return TOK_STRING;
} else if (sep != -1) /* '[=...' missing second bracket */
lexerror(ls, "invalid long string delimiter", TOK_STRING);
return '[';
}
case '=': {
next(ls);
if (check_next1(ls, '='))
return TOK_EQ;
else
return '=';
}
case '<': {
next(ls);
if (check_next1(ls, '='))
return TOK_LE;
else if (check_next1(ls, '<'))
return TOK_SHL;
else
return '<';
}
case '>': {
next(ls);
if (check_next1(ls, '='))
return TOK_GE;
else if (check_next1(ls, '>'))
return TOK_SHR;
else
return '>';
}
case '/': {
next(ls);
if (check_next1(ls, '/'))
return TOK_IDIV;
else
return '/';
}
case '~': {
next(ls);
if (check_next1(ls, '='))
return TOK_NE;
else
return '~';
}
case ':': {
next(ls);
if (check_next1(ls, ':'))
return TOK_DBCOLON;
else
return ':';
}
case '"':
case '\'': { /* short literal strings */
read_string(ls, ls->current, seminfo);
return TOK_STRING;
}
case '.': { /* '.', '..', '...', or number */
save_and_next(ls);
if (check_next1(ls, '.')) {
if (check_next1(ls, '.'))
return TOK_DOTS; /* '...' */
else
return TOK_CONCAT; /* '..' */
} else if (!lisdigit(ls->current))
return '.';
else
return read_numeral(ls, seminfo);
}
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9': {
return read_numeral(ls, seminfo);
}
case EOZ: {
return TOK_EOS;
}
case '@': {
/* RAVI change: @ introduces a type assertion operator */
save_and_next(ls);
while (lislalnum(ls->current)) {
save_and_next(ls);
}
check_save_next1(ls, '[');
check_save_next1(ls, ']');
return casttoken(ls, seminfo);
}
default: {
if (lislalpha(ls->current)) { /* identifier or reserved word? */
const StringObject *ts;
do {
save_and_next(ls);
} while (lislalnum(ls->current));
ts = raviX_create_string(ls->container, raviX_buffer_data(ls->buff),
(int32_t)raviX_buffer_len(ls->buff));
seminfo->ts = ts;
int tok = is_reserved(ts);
if (tok != -1) /* reserved word? */
return tok + FIRST_RESERVED;
else {
return TOK_NAME;
}
} else { /* single-char tokens (+ - / ...) */
int c = ls->current;
next(ls);
return c;
}
}
}
}
}
void raviX_next(LexerState *ls)
{
ls->lastline = ls->linenumber;
if (ls->lookahead.token != TOK_EOS) { /* is there a look-ahead token? */
ls->t = ls->lookahead; /* use this one */
ls->lookahead.token = TOK_EOS; /* and discharge it */
} else
ls->t.token = llex(ls, &ls->t.seminfo); /* read next token */
}
int raviX_lookahead(LexerState *ls)
{
assert(ls->lookahead.token == TOK_EOS);
ls->lookahead.token = llex(ls, &ls->lookahead.seminfo);
return ls->lookahead.token;
}
const char *raviX_get_last_error(CompilerState *container) { return container->error_message.buf; }

File diff suppressed because it is too large Load Diff

@ -0,0 +1,244 @@
#ifndef ravicomp_LINEARIZER_H
#define ravicomp_LINEARIZER_H
#include "ravi_compiler.h"
#include "common.h"
#include "parser.h"
#include "allocate.h"
#include "membuf.h"
#include "ptrlist.h"
/*
Linearizer component is responsible for translating the abstract syntax tree to
a Linear intermediate representation (IR).
*/
typedef struct Instruction Instruction;
typedef struct BasicBlock BasicBlock;
typedef struct Proc Proc;
typedef struct Constant Constant;
typedef struct Graph Graph;
DECLARE_PTR_LIST(InstructionList, Instruction);
DECLARE_PTR_LIST(PseudoList, Pseudo);
DECLARE_PTR_LIST(ProcList, Proc);
#define container_of(ptr, type, member) ((type *)((char *)(ptr)-offsetof(type, member)))
/* order is important here ! */
enum opcode {
op_nop,
op_ret,
op_add,
op_addff,
op_addfi,
op_addii,
op_sub,
op_subff,
op_subfi,
op_subif,
op_subii,
op_mul,
op_mulff,
op_mulfi,
op_mulii,
op_div,
op_divff,
op_divfi,
op_divif,
op_divii,
op_idiv,
op_band,
op_bandii,
op_bor,
op_borii,
op_bxor,
op_bxorii,
op_shl,
op_shlii,
op_shr,
op_shrii,
op_eq,
op_eqii,
op_eqff,
op_lt,
op_ltii,
op_ltff,
op_le,
op_leii,
op_leff,
op_mod,
op_pow,
op_closure,
op_unm,
op_unmi,
op_unmf,
op_len,
op_leni,
op_toint,
op_toflt,
op_toclosure,
op_tostring,
op_toiarray,
op_tofarray,
op_totable,
op_totype,
op_not,
op_bnot,
op_loadglobal,
op_newtable,
op_newiarray,
op_newfarray,
op_put, /* target is any */
op_put_ikey,
op_put_skey,
op_tput, /* target is table */
op_tput_ikey,
op_tput_skey,
op_iaput, /* target is integer[]*/
op_iaput_ival,
op_faput, /* target is number[] */
op_faput_fval,
op_cbr,
op_br,
op_mov,
op_movi,
op_movif, /* int to float if compatible else error */
op_movf,
op_movfi, /* float to int if compatible else error */
op_call,
op_get,
op_get_ikey,
op_get_skey,
op_tget,
op_tget_ikey,
op_tget_skey,
op_iaget,
op_iaget_ikey,
op_faget,
op_faget_ikey,
op_storeglobal,
op_close,
op_string_concat
};
/*
* The IR instructions use operands and targets of type pseudo, which
* is a way of referencing several different types of objects.
*/
enum PseudoType {
PSEUDO_SYMBOL, /* An object of type lua_symbol representing local var or upvalue, always refers to Lua stack relative to 'base' */
PSEUDO_TEMP_FLT, /* A floating point temp - may also be used for locals that don't escape - refers to C var */
PSEUDO_TEMP_INT, /* An integer temp - may also be used for locals that don't escape - refers to C var */
PSEUDO_TEMP_BOOL, /* An integer temp but restricted to 1 and 0 - refers to C var, shares the virtual C stack with PSEUDO_TEMP_INT */
PSEUDO_TEMP_ANY, /* A temp of any type - will always be on Lua stack relative to 'base' */
PSEUDO_CONSTANT, /* A literal value */
PSEUDO_PROC, /* A proc / function */
PSEUDO_NIL, /* Literal */
PSEUDO_TRUE, /* Literal */
PSEUDO_FALSE, /* Literal */
PSEUDO_BLOCK, /* Points to a basic block, used as targets for jumps */
PSEUDO_RANGE, /* Represents a range of registers from a certain starting register on Lua stack relative to 'base' */
PSEUDO_RANGE_SELECT, /* Picks a certain register from a range, resolves to register on Lua stack, relative to 'base' */
/* TODO we need a type for var args */
PSEUDO_LUASTACK /* Specifies a Lua stack position - not used by linearizer - for use by codegen. This is relative to CI->func rather than 'base' */
};
/* pseudo represents a pseudo (virtual) register */
struct Pseudo {
unsigned type : 4, regnum : 16, freed : 1;
Instruction *insn; /* instruction that created this pseudo */
union {
LuaSymbol *symbol; /* PSEUDO_SYMBOL */
const Constant *constant; /* PSEUDO_CONSTANT */
LuaSymbol *temp_for_local; /* PSEUDO_TEMP - if the temp represents a local */
Proc *proc; /* PSEUDO_PROC */
BasicBlock *block; /* PSEUDO_BLOCK */
Pseudo *range_pseudo; /* PSEUDO_RANGE_SELECT */
int stackidx; /* PSEUDO_LUASTACK */
};
};
/* single instruction */
struct Instruction {
unsigned opcode : 8;
PseudoList *operands;
PseudoList *targets;
BasicBlock *block; /* owning block */
};
/* Basic block */
struct BasicBlock {
nodeId_t index; /* The index of the block is a key to enable retrieving the block from its container */
InstructionList *insns; /* Note that if number of instructions is 0 then the block was logically deleted */
};
DECLARE_PTR_LIST(BasicBlockList, BasicBlock);
typedef struct PseudoGenerator {
uint8_t next_reg; /* Next register if no free registers, initially 0 */
int16_t free_pos; /* number of values in free_regs */
uint8_t free_regs[256]; /* list of free registers */
} PseudoGenerator;
struct Constant {
uint8_t type; /* ravitype_t RAVI_TNUMINT, RAVI_TNUMFLT or RAVI_TSTRING */
uint16_t index; /* index number starting from 0 assigned to each constant - acts like a reg num.
* Each type will be assigned separate range */
union {
lua_Integer i;
lua_Number n;
const StringObject *s;
};
};
/* proc is a type of cfg */
struct Proc {
unsigned node_count;
unsigned allocated;
BasicBlock **nodes;
uint32_t id; /* ID for the proc */
LinearizerState *linearizer;
ProcList *procs; /* procs defined in this proc */
Proc *parent; /* enclosing proc */
AstNode *function_expr; /* function ast that we are compiling */
Scope *current_scope;
BasicBlock *current_bb;
BasicBlock *current_break_target; /* track the current break target, previous target must be saved /
restored in stack discipline */
Scope *current_break_scope; /* as above track the block scope */
PseudoGenerator local_pseudos; /* locals */
PseudoGenerator temp_int_pseudos; /* temporaries known to be integer type */
PseudoGenerator temp_flt_pseudos; /* temporaries known to be number type */
PseudoGenerator temp_pseudos; /* All other temporaries */
Set *constants; /* constants used by this proc */
uint16_t num_intconstants;
uint16_t num_fltconstants;
uint16_t num_strconstants;
Graph *cfg; /* place holder for control flow graph; the linearizer does not create this */
char funcname[30]; /* Each proc needs a name inside a module - name is a short string */
void *userdata; /* For use by code generator */
};
struct LinearizerState {
Allocator instruction_allocator;
Allocator pseudo_allocator;
Allocator ptrlist_allocator;
Allocator basic_block_allocator;
Allocator proc_allocator;
Allocator unsized_allocator;
Allocator constant_allocator;
CompilerState *ast_container;
Proc *main_proc; /* The root of the compiled chunk of code */
ProcList *all_procs; /* All procs allocated by the linearizer */
Proc *current_proc; /* proc being compiled */
uint32_t proc_id;
};
void raviX_show_linearizer(LinearizerState *linearizer, TextBuffer *mb);
void raviX_output_basic_block_as_table(Proc *proc, BasicBlock *bb, TextBuffer *mb);
Instruction *raviX_last_instruction(BasicBlock *block);
Pseudo* raviX_allocate_stack_pseudo(Proc* proc, unsigned reg);
const char *raviX_opcode_name(unsigned int opcode);
#endif

@ -0,0 +1,117 @@
/*
Copyright (C) 2018-2020 Dibyendu Majumdar
*/
#include "membuf.h"
#include <assert.h>
#include <inttypes.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void raviX_string_copy(char *buf, const char *src, size_t buflen)
{
if (buflen == 0)
return;
strncpy(buf, src, buflen);
buf[buflen - 1] = 0;
}
void raviX_buffer_init(TextBuffer *mb, size_t initial_size)
{
if (initial_size > 0) {
mb->buf = (char *)calloc(1, initial_size);
if (mb->buf == NULL) {
fprintf(stderr, "out of memory\n");
exit(1);
}
} else
mb->buf = NULL;
mb->pos = 0;
mb->capacity = initial_size;
}
void raviX_buffer_resize(TextBuffer *mb, size_t new_size)
{
if (new_size <= mb->capacity)
return;
char *newmem = (char *)realloc(mb->buf, new_size);
if (newmem == NULL) {
fprintf(stderr, "Out of memory\n");
exit(1);
}
mb->buf = newmem;
mb->capacity = new_size;
}
void raviX_buffer_reserve(TextBuffer *mb, size_t n)
{
if (mb->capacity < mb->pos + n) {
size_t new_size = (((mb->pos + n) * 3 + 30) / 2) & ~15;
raviX_buffer_resize(mb, new_size);
assert(mb->capacity > mb->pos + n);
}
}
void raviX_buffer_free(TextBuffer *mb) { free(mb->buf); }
void raviX_buffer_add_bytes(TextBuffer *mb, const char *str, size_t len)
{
size_t required_size = mb->pos + len + 1; /* extra byte for NULL terminator */
raviX_buffer_resize(mb, required_size);
assert(mb->capacity - mb->pos > len);
raviX_string_copy(&mb->buf[mb->pos], str, mb->capacity - mb->pos);
mb->pos += len;
}
void raviX_buffer_add_string(TextBuffer *mb, const char *str)
{
size_t len = strlen(str);
raviX_buffer_add_bytes(mb, str, len);
}
void raviX_buffer_add_fstring(TextBuffer *mb, const char *fmt, ...)
{
va_list args;
int estimated_size = 128;
for (int i = 0; i < 2; i++) {
raviX_buffer_reserve(mb, estimated_size); // ensure we have at least estimated_size free space
va_start(args, fmt);
int n = vsnprintf(mb->buf + mb->pos, estimated_size, fmt, args);
va_end(args);
if (n > estimated_size) {
estimated_size = n + 1; // allow for 0 byte
} else if (n < 0) {
fprintf(stderr, "Buffer conversion error\n");
assert(false);
break;
} else {
mb->pos += n;
break;
}
}
}
void raviX_buffer_add_bool(TextBuffer *mb, bool value)
{
if (value)
raviX_buffer_add_string(mb, "true");
else
raviX_buffer_add_string(mb, "false");
}
void raviX_buffer_add_int(TextBuffer *mb, int value)
{
char temp[100];
snprintf(temp, sizeof temp, "%d", value);
raviX_buffer_add_string(mb, temp);
}
void raviX_buffer_add_longlong(TextBuffer *mb, int64_t value)
{
char temp[100];
snprintf(temp, sizeof temp, "%" PRId64 "", value);
raviX_buffer_add_string(mb, temp);
}
void raviX_buffer_add_char(TextBuffer *mb, char c)
{
char temp[2] = {c, '\0'};
raviX_buffer_add_string(mb, temp);
}

@ -0,0 +1,27 @@
#ifndef ravicomp_MEMBUF_H
#define ravicomp_MEMBUF_H
#include "ravi_compiler.h"
#include <assert.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
extern void raviX_buffer_add_bool(TextBuffer *mb, bool value);
extern void raviX_buffer_add_int(TextBuffer *mb, int value);
extern void raviX_buffer_add_longlong(TextBuffer *mb, int64_t value);
extern void raviX_buffer_add_char(TextBuffer *mb, char c);
/* Following add and remove raw bytes */
/* Unchecked - user must first resize */
static inline void raviX_buffer_addc(TextBuffer *mb, int c)
{
mb->buf[mb->pos++] = (char)c;
assert(mb->pos < mb->capacity);
}
static inline void raviX_buffer_remove(TextBuffer *mb, int i) { mb->pos -= i; }
#endif

@ -0,0 +1,80 @@
/* A pass over linearized code to eliminate unused code.
* Initially only tackle unreachable basic blocks
*/
#include "linearizer.h"
#include "cfg.h"
#include "graph.h"
#include "allocate.h"
#include "optimizer.h"
/**
* Check if a basic block has 0 predecessors. If so we can remove it from the CFG.
* We also remove all the instructions in the basic block
*/
static int process_block(LinearizerState *linearizer, Proc *proc, BasicBlock *bb)
{
GraphNode *node = raviX_graph_node(proc->cfg, bb->index);
GraphNodeList *predecessors = raviX_predecessors(node);
if (raviX_node_list_size(predecessors) != 0) {
// Has predecessors so nothing to do
return 0;
}
// No predecessor blocks, so we can remove this block
GraphNodeList *successors = raviX_successors(node);
uint32_t count = raviX_node_list_size(successors);
if (count == 0) {
// Nothing to do, but odd?
// FIXME maybe assert?
return 0;
}
// Make a copy of the successor node list as we need to change the CFG
nodeId_t *nodes = raviX_realloc_array(NULL, sizeof(nodeId_t), 0, count);
for (uint32_t i = 0; i < count; i++) {
nodes[i] = raviX_node_list_at(successors, i);
}
for (uint32_t i = 0; i < count; i++) {
// Remove edge from bb to the successor node
raviX_delete_edge(proc->cfg, bb->index, nodes[i]);
}
free(nodes);
assert(raviX_node_list_size(successors) == 0); // All should be gone
// Now clear out this bb
// FIXME deallocate instructions
raviX_ptrlist_remove_all((struct ptr_list **)&bb->insns);
// FIXME do we deallocate bb?
return 1; // We changed something
}
static int process_proc(LinearizerState *linearizer, Proc *proc)
{
if (proc->cfg == NULL) {
if (raviX_construct_cfg(proc) != 0) {
return 1;
}
}
int changed = 1;
while (changed) {
changed = 0;
BasicBlock *bb;
for (int i = 0; i < (int)proc->node_count; i++) {
bb = proc->nodes[i];
if (bb->index == ENTRY_BLOCK || bb->index == EXIT_BLOCK)
continue;
changed |= process_block(linearizer, proc, bb);
}
}
return 0;
}
int raviX_remove_unreachable_blocks(LinearizerState *linearizer)
{
Proc *proc;
FOR_EACH_PTR(linearizer->all_procs, proc)
{
if (process_proc(linearizer, proc) != 0)
return 1;
}
END_FOR_EACH_PTR(proc)
return 0;
}

@ -0,0 +1,10 @@
#ifndef ravicomp_OPTIMIZER_H
#define ravicomp_OPTIMIZER_H
/**
* Remove blocks that are unreachable. Blocks ae logically deleted by removing
* all instructions, rather than being physically removed.
*/
extern int raviX_remove_unreachable_blocks(LinearizerState *linearizer);
#endif

File diff suppressed because it is too large Load Diff

@ -0,0 +1,376 @@
#ifndef ravicomp_IMPLEMENTATION_H
#define ravicomp_IMPLEMENTATION_H
/*
* Internal header file for the implementation.
* The data structures defined here are private.
*/
#include "ravi_compiler.h"
#include "allocate.h"
#include "membuf.h"
#include "ptrlist.h"
#include "set.h"
#include <assert.h>
#include <limits.h>
#include <setjmp.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
enum { MAXVARS = 125 };
#define LUA_ENV "_ENV"
#define LUA_MAXINTEGER LLONG_MAX
#define LUA_MININTEGER LLONG_MIN
typedef unsigned long long lua_Unsigned;
typedef unsigned char lu_byte;
//////////////////////////
typedef struct AstNode AstNode;
/*
* Encapsulate all the compiler state.
* All memory is held by this object or sub-objects. Memory is freed when
* the object is destroyed.
*/
struct CompilerState {
Allocator ast_node_allocator;
Allocator ptrlist_allocator;
Allocator block_scope_allocator;
Allocator symbol_allocator;
Allocator string_allocator;
Allocator string_object_allocator;
Set *strings;
AstNode *main_function;
LinearizerState *linearizer;
int (*error_handler)(const char *fmt, ...);
TextBuffer buff; /* temp storage for literals, used by the lexer and parser */
jmp_buf env; /* For error handling */
TextBuffer error_message; /* For error handling, error message is saved here */
bool killed; /* flag to check if this is already destroyed */
const StringObject *_ENV; /* name of the env variable */
};
/* number of reserved words */
#define NUM_RESERVED ((int)(TOK_while - FIRST_RESERVED + 1))
/* state of the lexer plus state of the parser when shared by all
functions */
struct LexerState {
int current; /* current character (charint) */
int linenumber; /* input line counter */
int lastline; /* line of last token 'consumed' */
Token t; /* current token */
Token lookahead; /* look ahead token */
CompilerState *container;
const char *buf;
size_t bufsize;
size_t n;
const char *p;
TextBuffer *buff; /* buffer for tokens, points to the buffer in compiler_state */
const char *source; /* current source name */
const char *envn; /* environment variable name */
};
void raviX_syntaxerror(LexerState *ls, const char *msg);
DECLARE_PTR_LIST(AstNodeList, AstNode);
/* RAVI: Following are the types we will use
** use in parsing. The rationale for types is
** performance - as of now these are the only types that
** we care about from a performance point of view - if any
** other types appear then they are all treated as ANY
**/
typedef enum {
RAVI_TANY = 0, /* Lua dynamic type */
RAVI_TNUMINT = 1, /* integer number */
RAVI_TNUMFLT, /* floating point number */
RAVI_TARRAYINT, /* array of ints */
RAVI_TARRAYFLT, /* array of doubles */
RAVI_TFUNCTION, /* Lua or C Function */
RAVI_TTABLE, /* Lua table */
RAVI_TSTRING, /* string */
RAVI_TNIL, /* NIL */
RAVI_TBOOLEAN, /* boolean */
RAVI_TUSERDATA, /* userdata or lightuserdata */
RAVI_TVARARGS /* Not a real type - represents ... */
} ravitype_t;
/* Lua type info. We need to support user defined types too which are known by name */
struct VariableType {
ravitype_t type_code;
/* type name for user defined types; used to lookup metatable in registry, only set when type_code is
* RAVI_TUSERDATA */
const StringObject *type_name;
};
typedef struct Pseudo Pseudo;
DECLARE_PTR_LIST(LuaSymbolList, LuaSymbol);
struct LuaVariableSymbol {
VariableType value_type;
const StringObject *var_name; /* name of the variable */
Scope *block; /* NULL if global symbol, as globals are never added to a scope */
LuaSymbol *env; /* Only applicable for global symbols - this should point to _ENV */
unsigned escaped: 1, /* Has one or more up-value references */
function_parameter: 1; /* Is a function parameter */
Pseudo *pseudo; /* backend data for the symbol */
};
struct LuaLabelSymbol {
const StringObject *label_name;
Scope *block;
Pseudo* pseudo; /* backend data for the symbol */
};
struct LuaUpvalueSymbol {
VariableType value_type;
LuaSymbol *target_variable; /* variable reference */
AstNode *target_function; /* Where the upvalue lives */
unsigned upvalue_index : 16, /* index of the upvalue in the function where this upvalue occurs */
is_in_parent_stack : 1, /* 1 if yes - populated by code generator only */
parent_upvalue_index : 15; /* if !is_in_parent_stack then upvalue index in parent - populated by code generator only */
/*TODO add pseudo ?*/
};
/* A symbol is a name recognised in Ravi/Lua code*/
struct LuaSymbol {
enum SymbolType symbol_type;
union {
LuaVariableSymbol variable;
LuaLabelSymbol label;
LuaUpvalueSymbol upvalue;
};
};
struct Scope {
AstNode *function; /* function owning this block - of type FUNCTION_EXPR */
Scope *parent; /* parent block, may belong to parent function */
LuaSymbolList *symbol_list; /* symbols defined in this block */
unsigned need_close: 1; /* When we exit scope of this block the upvalues need to be closed */
};
/*STMT_RETURN */
struct ReturnStatement {
AstNodeList *expr_list;
};
/* STMT_LABEL */
struct LabelStatement {
LuaSymbol *symbol;
};
/* STMT_GOTO */
struct GotoStatement {
unsigned is_break : 1; /* is this a break statement */
const StringObject *name; /* target label, used to resolve the goto destination */
Scope* goto_scope; /* The scope of the goto statement */
};
/* STMT_LOCAL local variable declarations */
struct LocalStatement {
LuaSymbolList *var_list;
AstNodeList *expr_list;
};
/* STMT_EXPR: Also covers assignments */
struct ExpressionStatement {
AstNodeList *var_expr_list; /* Optional var expressions, comma separated */
AstNodeList *expr_list; /* Comma separated expressions */
};
struct FunctionStatement {
AstNode *name; /* base symbol to be looked up - symbol_expression */
AstNodeList *selectors; /* Optional list of index_expression(s) */
AstNode *method_name; /* Optional - index_expression */
AstNode *function_expr; /* Function's AST - function_expression */
};
struct DoStatement {
Scope *scope; /* The do statement only creates a new scope */
AstNodeList *do_statement_list; /* statements in this block */
};
/* Used internally in if_stmt, not an independent AST node */
struct TestThenStatement {
AstNode *condition;
Scope *test_then_scope;
AstNodeList *test_then_statement_list; /* statements in this block */
};
struct IfStatement {
AstNodeList *if_condition_list; /* Actually a list of test_then_blocks */
Scope *else_block;
AstNodeList *else_statement_list; /* statements in this block */
};
struct WhileOrRepeatStatement {
AstNode *condition;
Scope *loop_scope;
AstNodeList *loop_statement_list; /* statements in this block */
};
/* Used for both generic and numeric for loops */
struct ForStatement {
Scope* for_scope; /* encapsulates the entire for statement */
LuaSymbolList *symbols;
AstNodeList *expr_list;
Scope *for_body;
AstNodeList *for_statement_list; /* statements in this block */
};
/* To access the type field common to all expr objects */
/* all expr types must be compatible with base_expression */
#define BASE_EXPRESSION_FIELDS VariableType type; unsigned truncate_results: 1
typedef struct BaseExpression {
BASE_EXPRESSION_FIELDS;
} BaseExpression;
struct LiteralExpression {
BASE_EXPRESSION_FIELDS;
SemInfo u;
};
/* primaryexp -> NAME | '(' expr ')', NAME is parsed as EXPR_SYMBOL */
struct SymbolExpression {
BASE_EXPRESSION_FIELDS;
LuaSymbol *var;
};
/* EXPR_Y_INDEX or EXPR_FIELD_SELECTOR */
struct IndexExpression {
BASE_EXPRESSION_FIELDS;
AstNode *expr; /* '[' expr ']' */
};
/* EXPR_UNARY */
struct UnaryExpression {
BASE_EXPRESSION_FIELDS;
UnaryOperatorType unary_op;
AstNode *expr;
};
struct BinaryExpression {
BASE_EXPRESSION_FIELDS;
BinaryOperatorType binary_op;
AstNode *expr_left;
AstNode *expr_right;
};
struct FunctionExpression {
BASE_EXPRESSION_FIELDS;
unsigned is_vararg : 1;
unsigned is_method : 1;
unsigned need_close : 1;
uint32_t proc_id; /* Backend allocated id */
AstNode *parent_function; /* parent function or NULL if main chunk */
Scope *main_block; /* the function's main block */
AstNodeList *function_statement_list; /* statements in this block */
LuaSymbolList
*args; /* arguments, also must be part of the function block's symbol list */
AstNodeList *child_functions; /* child functions declared in this function */
LuaSymbolList *upvalues; /* List of upvalues */
LuaSymbolList *locals; /* List of locals */
};
/* Assign values in table constructor */
/* EXPR_TABLE_ELEMENT_ASSIGN - used in table constructor */
struct TableElementAssignmentExpression {
BASE_EXPRESSION_FIELDS;
AstNode *key_expr; /* If NULL means this is a list field with next available index,
else specifies index expression */
AstNode *value_expr;
};
/* constructor -> '{' [ field { sep field } [sep] ] '}' where sep -> ',' | ';' */
/* table constructor expression EXPR_TABLE_LITERAL occurs in function call and simple expr */
struct TableLiteralExpression {
BASE_EXPRESSION_FIELDS;
AstNodeList *expr_list;
};
/* suffixedexp -> primaryexp { '.' NAME | '[' exp ']' | ':' NAME funcargs | funcargs } */
/* suffix_list may have EXPR_FIELD_SELECTOR, EXPR_Y_INDEX, EXPR_FUNCTION_CALL */
struct SuffixedExpression {
BASE_EXPRESSION_FIELDS;
AstNode *primary_expr;
AstNodeList *suffix_list;
};
struct FunctionCallExpression {
/* 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.
*/
BASE_EXPRESSION_FIELDS;
const StringObject *method_name; /* Optional method_name */
AstNodeList *arg_list; /* Call arguments */
int num_results; /* How many results do we expect, -1 means all available results */
};
#undef BASE_EXPRESSION_FIELDS
/* ALL AST nodes start with following fields */
#define BASE_AST_FIELDS enum AstNodeType type; int line_number
/* Statement AST nodes have following common fields.
*/
struct Statement {
BASE_AST_FIELDS;
};
/* Expression AST nodes have following common fields
*/
struct Expression {
BASE_AST_FIELDS;
BaseExpression common_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
The ast_node must be aligned with Expression for expressions, and with Statement for statements.
*/
struct AstNode {
BASE_AST_FIELDS;
union {
ReturnStatement return_stmt; /*STMT_RETURN */
LabelStatement label_stmt; /* STMT_LABEL */
GotoStatement goto_stmt; /* STMT_GOTO */
LocalStatement local_stmt; /* STMT_LOCAL local variable declarations */
ExpressionStatement expression_stmt;
FunctionStatement function_stmt;
DoStatement do_stmt;
TestThenStatement test_then_block;
IfStatement if_stmt;
WhileOrRepeatStatement while_or_repeat_stmt;
ForStatement for_stmt;
BaseExpression common_expr;
LiteralExpression literal_expr;
SymbolExpression symbol_expr;
IndexExpression index_expr;
UnaryExpression unary_expr;
BinaryExpression binary_expr;
FunctionExpression function_expr; /* a literal expression whose result is a value of type function */
TableElementAssignmentExpression table_elem_assign_expr;
TableLiteralExpression table_expr;
SuffixedExpression suffixed_expr;
FunctionCallExpression function_call_expr;
};
};
#undef BASE_AST_FIELDS
static inline void set_typecode(VariableType *vt, ravitype_t t) { vt->type_code = t; }
static inline void set_type(VariableType *vt, ravitype_t t)
{
vt->type_code = t;
vt->type_name = NULL;
}
static inline void set_typename(VariableType *vt, ravitype_t t, const StringObject *name)
{
vt->type_code = t;
vt->type_name = name;
}
static inline void copy_type(VariableType *a, const VariableType *b)
{
a->type_code = b->type_code;
a->type_name = b->type_name;
}
struct parser_state {
LexerState *ls;
CompilerState *container;
AstNode *current_function;
Scope *current_scope;
};
void raviX_print_ast_node(TextBuffer *buf, AstNode *node, int level); /* output the AST structure recursively */
const char *raviX_get_type_name(ravitype_t tt);
int raviX_ast_simplify(CompilerState* container);
#endif

@ -0,0 +1,987 @@
/*
* ptrlist.c
*
* Pointer ptrlist_t manipulation
*
* (C) Copyright Linus Torvalds 2003-2005
*/
/*
* This version is part of the dmr_c project.
* Copyright (C) 2017 Dibyendu Majumdar
*/
#define PARANOIA 1
#include <ptrlist.h>
#include <stdio.h>
#include <stdlib.h>
/* The ptr list */
/* For testing we change this */
static int N_ = LIST_NODE_NR;
void raviX_ptrlist_split_node(struct ptr_list *head)
{
int old = head->nr_, nr = old / 2;
Allocator *alloc = head->allocator_;
assert(alloc);
struct ptr_list *newlist = (struct ptr_list *)raviX_allocator_allocate(alloc, 0);
struct ptr_list *next = head->next_;
newlist->allocator_ = alloc;
old -= nr;
head->nr_ = old;
newlist->next_ = next;
next->prev_ = newlist;
newlist->prev_ = head;
head->next_ = newlist;
newlist->nr_ = nr;
memcpy(newlist->list_, head->list_ + old, nr * sizeof(void *));
memset(head->list_ + old, 0xf0, nr * sizeof(void *));
}
PtrListIterator raviX_ptrlist_forward_iterator(struct ptr_list *head)
{
PtrListIterator iter;
iter.__head = iter.__list = head;
iter.__nr = -1;
return iter;
}
// Reverse iterator has to start from previous node not previous entry
// in the given head
PtrListIterator raviX_ptrlist_reverse_iterator(struct ptr_list *head)
{
PtrListIterator iter;
iter.__head = iter.__list = head ? head->prev_ : NULL;
iter.__nr = iter.__head ? iter.__head->nr_ : 0;
return iter;
}
void *raviX_ptrlist_iter_next(PtrListIterator *self)
{
if (self->__head == NULL)
return NULL;
self->__nr++;
Lretry:
if (self->__nr < self->__list->nr_) {
void *ptr = self->__list->list_[self->__nr];
if (self->__list->rm_ && !ptr) {
self->__nr++;
goto Lretry;
}
return ptr;
} else if (self->__list->next_ != self->__head) {
self->__list = self->__list->next_;
self->__nr = 0;
goto Lretry;
}
return NULL;
}
void *raviX_ptrlist_nth_entry(struct ptr_list *list, unsigned int idx)
{
struct ptr_list *head = list;
if (!head)
return NULL;
do {
unsigned int nr = list->nr_;
if (idx < nr)
return list->list_[idx];
else
idx -= nr;
} while ((list = list->next_) != head);
return NULL;
}
void *raviX_ptrlist_iter_prev(PtrListIterator *self)
{
if (self->__head == NULL)
return NULL;
self->__nr--;
Lretry:
if (self->__nr >= 0 && self->__nr < self->__list->nr_) {
void *ptr = self->__list->list_[self->__nr];
if (self->__list->rm_ && !ptr) {
self->__nr--;
goto Lretry;
}
return ptr;
} else if (self->__list->prev_ != self->__head) {
self->__list = self->__list->prev_;
self->__nr = self->__list->nr_ - 1;
goto Lretry;
}
return NULL;
}
void raviX_ptrlist_iter_split_current(PtrListIterator *self)
{
if (self->__list->nr_ == N_) {
/* full so split */
raviX_ptrlist_split_node(self->__list);
if (self->__nr >= self->__list->nr_) {
self->__nr -= self->__list->nr_;
self->__list = self->__list->next_;
}
}
}
void raviX_ptrlist_iter_insert(PtrListIterator *self, void *newitem)
{
assert(self->__nr >= 0);
raviX_ptrlist_iter_split_current(self);
void **__this = self->__list->list_ + self->__nr;
void **__last = self->__list->list_ + self->__list->nr_ - 1;
while (__last >= __this) {
__last[1] = __last[0];
__last--;
}
*__this = newitem;
self->__list->nr_++;
}
void raviX_ptrlist_iter_remove(PtrListIterator *self)
{
assert(self->__nr >= 0);
void **__this = self->__list->list_ + self->__nr;
void **__last = self->__list->list_ + self->__list->nr_ - 1;
while (__this < __last) {
__this[0] = __this[1];
__this++;
}
*__this = (void *)((uintptr_t)0xf0f0f0f0);
self->__list->nr_--;
self->__nr--;
}
void raviX_ptrlist_iter_set(PtrListIterator *self, void *ptr)
{
assert(self->__list && self->__nr >= 0 && self->__nr < self->__list->nr_);
self->__list->list_[self->__nr] = ptr;
}
void raviX_ptrlist_iter_mark_deleted(PtrListIterator *self)
{
raviX_ptrlist_iter_set(self, NULL);
self->__list->rm_++;
}
int raviX_ptrlist_size(const struct ptr_list *self)
{
int nr = 0;
if (self) {
const struct ptr_list *list = self;
do {
nr += list->nr_ - list->rm_;
} while ((list = list->next_) != self);
}
return nr;
}
void **raviX_ptrlist_add(struct ptr_list **self, void *ptr, Allocator *ptr_list_allocator)
{
struct ptr_list *list = *self;
struct ptr_list *last = NULL;
void **ret;
int nr;
if (!list || (nr = (last = list->prev_)->nr_) >= N_) {
struct ptr_list *newlist = (struct ptr_list *)raviX_allocator_allocate(ptr_list_allocator, 0);
newlist->allocator_ = ptr_list_allocator;
if (!list) {
newlist->next_ = newlist;
newlist->prev_ = newlist;
*self = newlist;
} else {
newlist->prev_ = last;
newlist->next_ = list;
list->prev_ = newlist;
last->next_ = newlist;
}
last = newlist;
nr = 0;
}
ret = last->list_ + nr;
*ret = ptr;
nr++;
last->nr_ = nr;
return ret;
}
void *raviX_ptrlist_first(struct ptr_list *list)
{
if (!list)
return NULL;
return list->list_[0];
}
void *raviX_ptrlist_last(struct ptr_list *list)
{
if (!list)
return NULL;
list = list->prev_;
return list->list_[list->nr_ - 1];
}
/*
* Linearize the entries of a list up to a total of 'max',
* and return the nr of entries linearized.
*
* The array to linearize into (second argument) should really
* be "void *x[]", but we want to let people fill in any kind
* of pointer array, so let's just call it "void **".
*/
int raviX_ptrlist_linearize(struct ptr_list *head, void **arr, int max)
{
int nr = 0;
if (head && max > 0) {
struct ptr_list *list = head;
do {
int i = list->nr_;
if (i > max)
i = max;
memcpy(arr, list->list_, i * sizeof(void *));
arr += i;
nr += i;
max -= i;
if (!max)
break;
} while ((list = list->next_) != head);
}
return nr;
}
/*
* When we've walked the list and deleted entries,
* we may need to re-pack it so that we don't have
* any empty blocks left (empty blocks upset the
* walking code
*/
void raviX_ptrlist_pack(struct ptr_list **self)
{
struct ptr_list *head = *self;
if (head) {
struct ptr_list *entry = head;
do {
struct ptr_list *next;
restart:
next = entry->next_;
if (!entry->nr_) {
struct ptr_list *prev;
if (next == entry) {
raviX_allocator_free(entry->allocator_, entry);
*self = NULL;
return;
}
prev = entry->prev_;
prev->next_ = next;
next->prev_ = prev;
raviX_allocator_free(entry->allocator_, entry);
if (entry == head) {
*self = next;
head = next;
entry = next;
goto restart;
}
}
entry = next;
} while (entry != head);
}
}
void raviX_ptrlist_remove_all(struct ptr_list **self)
{
struct ptr_list *tmp, *list = *self;
if (!list)
return;
list->prev_->next_ = NULL;
while (list) {
tmp = list;
list = list->next_;
raviX_allocator_free(tmp->allocator_, tmp);
}
*self = NULL;
}
int raviX_ptrlist_remove(struct ptr_list **self, void *entry, int count)
{
PtrListIterator iter = raviX_ptrlist_forward_iterator(*self);
for (void *ptr = raviX_ptrlist_iter_next(&iter); ptr != NULL; ptr = raviX_ptrlist_iter_next(&iter)) {
if (ptr == entry) {
raviX_ptrlist_iter_remove(&iter);
if (!--count)
goto out;
}
}
assert(count <= 0);
out:
raviX_ptrlist_pack(self);
return count;
}
int raviX_ptrlist_replace(struct ptr_list **self, void *old_ptr, void *new_ptr, int count)
{
PtrListIterator iter = raviX_ptrlist_forward_iterator(*self);
for (void *ptr = raviX_ptrlist_iter_next(&iter); ptr != NULL; ptr = raviX_ptrlist_iter_next(&iter)) {
if (ptr == old_ptr) {
raviX_ptrlist_iter_set(&iter, new_ptr);
if (!--count)
goto out;
}
}
assert(count <= 0);
out:
return count;
}
/* This removes the last entry, but doesn't pack the ptr list */
void *raviX_ptrlist_undo_last(struct ptr_list **self)
{
struct ptr_list *last, *first = *self;
if (!first)
return NULL;
last = first;
do {
last = last->prev_;
if (last->nr_) {
void *ptr;
int nr = --last->nr_;
ptr = last->list_[nr];
last->list_[nr] = (void *)((intptr_t)0xf1f1f1f1);
return ptr;
}
} while (last != first);
return NULL;
}
void *raviX_ptrlist_delete_last(struct ptr_list **self)
{
void *ptr = NULL;
struct ptr_list *last, *first = *self;
if (!first)
return NULL;
last = first->prev_;
if (last->nr_)
ptr = last->list_[--last->nr_];
if (last->nr_ <= 0) {
first->prev_ = last->prev_;
last->prev_->next_ = first;
if (last == first)
*self = NULL;
raviX_allocator_free(last->allocator_, last);
}
return ptr;
}
void raviX_ptrlist_concat(struct ptr_list *a, struct ptr_list **self)
{
Allocator *alloc = NULL;
PtrListIterator iter = raviX_ptrlist_forward_iterator(a);
if (a)
alloc = a->allocator_;
else if (*self)
alloc = (*self)->allocator_;
else
return;
for (void *ptr = raviX_ptrlist_iter_next(&iter); ptr != NULL; ptr = raviX_ptrlist_iter_next(&iter)) {
raviX_ptrlist_add(self, ptr, alloc);
}
}
/*
* sort_list: a stable sort for lists.
*
* Time complexity: O(n*log n)
* [assuming limited zero-element fragments]
*
* Space complexity: O(1).
*
* Stable: yes.
*/
static void array_sort(void **ptr, int nr, void *userdata, int (*cmp)(void *, const void *, const void *))
{
int i;
for (i = 1; i < nr; i++) {
void *p = ptr[i];
if (cmp(userdata, ptr[i - 1], p) > 0) {
int j = i;
do {
ptr[j] = ptr[j - 1];
if (!--j)
break;
} while (cmp(userdata, ptr[j - 1], p) > 0);
ptr[j] = p;
}
}
}
static void verify_sorted(struct ptr_list *l, int n, void *userdata, int (*cmp)(void *, const void *, const void *))
{
int i = 0;
const void *a;
struct ptr_list *head = l;
while (l->nr_ == 0) {
l = l->next_;
if (--n == 0)
return;
assert(l != head);
}
a = l->list_[0];
while (n > 0) {
const void *b;
if (++i >= l->nr_) {
i = 0;
l = l->next_;
n--;
assert(l != head || n == 0);
continue;
}
b = l->list_[i];
assert(cmp(userdata, a, b) <= 0);
a = b;
}
}
static void flush_to(struct ptr_list *b, void **buffer, int *nbuf)
{
int nr = b->nr_;
assert(*nbuf >= nr);
memcpy(b->list_, buffer, nr * sizeof(void *));
*nbuf = *nbuf - nr;
memmove(buffer, buffer + nr, *nbuf * sizeof(void *));
}
static void dump_to(struct ptr_list *b, void **buffer, int nbuf)
{
assert(nbuf <= b->nr_);
memcpy(b->list_, buffer, nbuf * sizeof(void *));
}
// Merge two already-sorted sequences of blocks:
// (b1_1, ..., b1_n) and (b2_1, ..., b2_m)
// Since we may be moving blocks around, we return the new head
// of the merged list.
static struct ptr_list *merge_block_seqs(struct ptr_list *b1, int n, struct ptr_list *b2, int m, void *userdata,
int (*cmp)(void *, const void *, const void *))
{
int i1 = 0, i2 = 0;
void *buffer[2 * LIST_NODE_NR];
int nbuf = 0;
struct ptr_list *newhead = b1;
// printf ("Merging %d blocks at %p with %d blocks at %p\n", n, b1, m, b2);
// Skip empty blocks in b2.
while (b2->nr_ == 0) {
// BEEN_THERE('F');
b2 = b2->next_;
if (--m == 0) {
// BEEN_THERE('G');
return newhead;
}
}
// Do a quick skip in case entire blocks from b1 are
// already less than smallest element in b2.
while (b1->nr_ == 0 || cmp(userdata, PTR_ENTRY(b1, b1->nr_ - 1), PTR_ENTRY(b2, 0)) < 0) {
// printf ("Skipping whole block.\n");
// BEEN_THERE('H');
b1 = b1->next_;
if (--n == 0) {
// BEEN_THERE('I');
return newhead;
}
}
while (1) {
void *d1 = PTR_ENTRY(b1, i1);
void *d2 = PTR_ENTRY(b2, i2);
assert(i1 >= 0 && i1 < b1->nr_);
assert(i2 >= 0 && i2 < b2->nr_);
assert(b1 != b2);
assert(n > 0);
assert(m > 0);
if (cmp(userdata, d1, d2) <= 0) {
// BEEN_THERE('J');
buffer[nbuf++] = d1;
// Element from b1 is smaller
if (++i1 >= b1->nr_) {
// BEEN_THERE('L');
flush_to(b1, buffer, &nbuf);
do {
b1 = b1->next_;
if (--n == 0) {
// BEEN_THERE('O');
while (b1 != b2) {
// BEEN_THERE('P');
flush_to(b1, buffer, &nbuf);
b1 = b1->next_;
}
assert(nbuf == i2);
dump_to(b2, buffer, nbuf);
return newhead;
}
} while (b1->nr_ == 0);
i1 = 0;
}
} else {
// BEEN_THERE('K');
// Element from b2 is smaller
buffer[nbuf++] = d2;
if (++i2 >= b2->nr_) {
struct ptr_list *l = b2;
// BEEN_THERE('M');
// OK, we finished with b2. Pull it out
// and plug it in before b1.
b2 = b2->next_;
b2->prev_ = l->prev_;
b2->prev_->next_ = b2;
l->next_ = b1;
l->prev_ = b1->prev_;
l->next_->prev_ = l;
l->prev_->next_ = l;
if (b1 == newhead) {
// BEEN_THERE('N');
newhead = l;
}
flush_to(l, buffer, &nbuf);
b2 = b2->prev_;
do {
b2 = b2->next_;
if (--m == 0) {
// BEEN_THERE('Q');
assert(nbuf == i1);
dump_to(b1, buffer, nbuf);
return newhead;
}
} while (b2->nr_ == 0);
i2 = 0;
}
}
}
}
void raviX_ptrlist_sort(struct ptr_list **plist, void *userdata, int (*cmp)(void *, const void *, const void *))
{
struct ptr_list *head = *plist, *list = head;
int blocks = 1;
assert(N_ == LIST_NODE_NR);
if (!head)
return;
// Sort all the sub-lists
do {
array_sort(list->list_, list->nr_, userdata, cmp);
#ifdef PARANOIA
verify_sorted(list, 1, userdata, cmp);
#endif
list = list->next_;
} while (list != head);
// Merge the damn things together
while (1) {
struct ptr_list *block1 = head;
do {
struct ptr_list *block2 = block1;
struct ptr_list *next, *newhead;
int i;
for (i = 0; i < blocks; i++) {
block2 = block2->next_;
if (block2 == head) {
if (block1 == head) {
// BEEN_THERE('A');
*plist = head;
return;
}
// BEEN_THERE('B');
goto next_pass;
}
}
next = block2;
for (i = 0; i < blocks;) {
next = next->next_;
i++;
if (next == head) {
// BEEN_THERE('C');
break;
}
// BEEN_THERE('D');
}
newhead = merge_block_seqs(block1, blocks, block2, i, userdata, cmp);
#ifdef PARANOIA
verify_sorted(newhead, blocks + i, userdata, cmp);
#endif
if (block1 == head) {
// BEEN_THERE('E');
head = newhead;
}
block1 = next;
} while (block1 != head);
next_pass:
blocks <<= 1;
}
}
#if 0
static int int_cmp(void *ud, const void *_a, const void *_b)
{
(void)ud;
const int *a = (const int *)_a;
const int *b = (const int *)_b;
return *a - *b;
}
#define MIN(_x, _y) ((_x) < (_y) ? (_x) : (_y))
static int test_sort()
{
int i, *e;
const int N = 10000;
srand(N);
for (i = 0; i < 1000; i++)
(void)rand();
Allocator ptrlist_allocator;
raviX_allocator_init(&ptrlist_allocator, "ptrlist_nodes", sizeof(struct ptr_list), __alignof__(struct ptr_list),
CHUNK);
Allocator int_allocator;
raviX_allocator_init(&int_allocator, "ints", sizeof(int), __alignof__(int), CHUNK);
struct ptr_list *int_list = NULL;
for (i = 0; i < N; i++) {
e = (int *)raviX_allocator_allocate(&int_allocator, 0);
*e = rand();
raviX_ptrlist_add(&int_list, e, &ptrlist_allocator);
}
if (raviX_ptrlist_size(int_list) != N)
return 1;
raviX_ptrlist_sort(&int_list, NULL, int_cmp);
// Sort already sorted stuff.
raviX_ptrlist_sort(&int_list, NULL, int_cmp);
int *p = NULL;
PtrListIterator iter = raviX_ptrlist_forward_iterator(int_list);
int count = 0;
for (int *k = (int *)raviX_ptrlist_iter_next(&iter); k != NULL; k = (int *)raviX_ptrlist_iter_next(&iter)) {
if (p != NULL) {
if (*k < *p)
return 1;
}
p = k;
count++;
}
if (count != N)
return 1;
struct ptr_list *l = int_list, *l2;
l2 = l;
int expected_count = 0;
do {
l2->nr_ = MIN(l2->nr_, rand() % 3);
for (i = 0; i < l2->nr_; i++) {
*((int *)(l2->list_[i])) = rand();
expected_count++;
}
l2 = l2->next_;
} while (l2 != l);
raviX_ptrlist_sort(&int_list, NULL, int_cmp);
p = NULL;
iter = raviX_ptrlist_forward_iterator(int_list);
count = 0;
for (int *k = (int *)raviX_ptrlist_iter_next(&iter); k != NULL; k = (int *)raviX_ptrlist_iter_next(&iter)) {
if (p != NULL) {
if (*k < *p)
return 1;
}
p = k;
count++;
}
if (count != expected_count)
return 1;
raviX_ptrlist_remove_all(&int_list);
raviX_allocator_destroy(&int_allocator);
raviX_allocator_destroy(&ptrlist_allocator);
return 0;
}
struct mystruct {
int i;
};
struct mytoken {
const char *a;
};
static int test_ptrlist_basics()
{
Allocator ptrlist_allocator;
raviX_allocator_init(&ptrlist_allocator, "ptrlist_nodes", sizeof(struct ptr_list), __alignof__(struct ptr_list),
CHUNK);
Allocator token_allocator;
raviX_allocator_init(&token_allocator, "ptr_list_tokens", sizeof(struct mytoken), __alignof__(struct mytoken),
CHUNK);
struct ptr_list *token_list = NULL;
if (raviX_ptrlist_size(token_list) != 0)
return 1;
struct mytoken *tok1 = (struct mytoken *)raviX_allocator_allocate(&token_allocator, 0);
struct mytoken **tok1p = (struct mytoken **)raviX_ptrlist_add(&token_list, tok1, &ptrlist_allocator);
if (raviX_ptrlist_size(token_list) != 1)
return 1;
if (tok1 != *tok1p)
return 1;
if (raviX_ptrlist_first(token_list) != tok1)
return 1;
if (raviX_ptrlist_last(token_list) != tok1)
return 1;
struct mytoken *tok2 = (struct mytoken *)raviX_allocator_allocate(&token_allocator, 0);
struct mytoken **tok2p = (struct mytoken **)raviX_ptrlist_add(&token_list, tok2, &ptrlist_allocator);
if (raviX_ptrlist_size(token_list) != 2)
return 1;
struct mytoken *tok3 = (struct mytoken *)raviX_allocator_allocate(&token_allocator, 0);
raviX_ptrlist_add(&token_list, tok3, &ptrlist_allocator);
if (raviX_ptrlist_size(token_list) != 3)
return 1;
struct mytoken *tok4 = (struct mytoken *)raviX_allocator_allocate(&token_allocator, 0);
raviX_ptrlist_add(&token_list, tok4, &ptrlist_allocator);
if (raviX_ptrlist_size(token_list) != 4)
return 1;
struct mytoken *tok5 = (struct mytoken *)raviX_allocator_allocate(&token_allocator, 0);
struct mytoken **tok5p = (struct mytoken **)raviX_ptrlist_add(&token_list, tok5, &ptrlist_allocator);
if (raviX_ptrlist_size(token_list) != 5)
return 1;
if (tok2 != *tok2p)
return 1;
if (tok5 != *tok5p)
return 1;
if (raviX_ptrlist_first(token_list) != tok1)
return 1;
if (raviX_ptrlist_last(token_list) != tok5)
return 1;
struct mytoken *toks[5];
int lin1 = raviX_ptrlist_linearize(token_list, (void **)toks, 5);
if (lin1 != 5)
return 1;
if (toks[0] != tok1)
return 1;
if (toks[1] != tok2)
return 1;
if (toks[2] != tok3)
return 1;
if (toks[3] != tok4)
return 1;
if (toks[4] != tok5)
return 1;
if (raviX_ptrlist_size(token_list) != 5)
return 1;
raviX_ptrlist_pack(&token_list);
if (raviX_ptrlist_size(token_list) != 5)
return 1;
if (raviX_ptrlist_first(token_list) != tok1)
return 1;
if (raviX_ptrlist_last(token_list) != tok5)
return 1;
const int X = 5 + 1;
const int Y = X - 1;
const int Z = Y - 1;
PtrListIterator iter1 = raviX_ptrlist_forward_iterator(token_list);
for (int i = 0; i < X; i++) {
struct mytoken *tk = (struct mytoken *)raviX_ptrlist_iter_next(&iter1);
if (tk == NULL) {
if (i == Y)
break;
return 1;
}
if (tk != toks[i])
return 1;
}
PtrListIterator iter2 = raviX_ptrlist_reverse_iterator(token_list);
for (int i = 0; i < X; i++) {
struct mytoken *tk = (struct mytoken *)raviX_ptrlist_iter_prev(&iter2);
if (tk == NULL) {
if (i == Y)
break;
return 1;
}
if (tk != toks[Z - i])
return 1;
}
struct mytoken *tok0 = (struct mytoken *)raviX_allocator_allocate(&token_allocator, 0);
PtrListIterator iter3 = raviX_ptrlist_forward_iterator(token_list);
if (!raviX_ptrlist_iter_next(&iter3))
return 1;
raviX_ptrlist_iter_insert(&iter3, tok0);
if (raviX_ptrlist_size(token_list) != 6)
return 1;
if (raviX_ptrlist_first(token_list) != tok0)
return 1;
if (raviX_ptrlist_last(token_list) != tok5)
return 1;
Allocator mystruct_allocator;
raviX_allocator_init(&mystruct_allocator, "mystructs", sizeof(struct mystruct), __alignof__(struct mystruct),
CHUNK);
struct ptr_list *mystruct_list = NULL;
struct mystruct *s1 = (struct mystruct *)raviX_allocator_allocate(&mystruct_allocator, 0);
s1->i = 1;
struct mystruct *s2 = (struct mystruct *)raviX_allocator_allocate(&mystruct_allocator, 0);
s2->i = 2;
struct mystruct *s3 = (struct mystruct *)raviX_allocator_allocate(&mystruct_allocator, 0);
s3->i = 3;
struct mystruct *s4 = (struct mystruct *)raviX_allocator_allocate(&mystruct_allocator, 0);
s4->i = 4;
struct mystruct *s5 = (struct mystruct *)raviX_allocator_allocate(&mystruct_allocator, 0);
s5->i = 5;
struct mystruct *s6 = (struct mystruct *)raviX_allocator_allocate(&mystruct_allocator, 0);
s6->i = 6;
raviX_ptrlist_add(&mystruct_list, s1, &ptrlist_allocator);
raviX_ptrlist_add(&mystruct_list, s2, &ptrlist_allocator);
raviX_ptrlist_add(&mystruct_list, s3, &ptrlist_allocator);
raviX_ptrlist_add(&mystruct_list, s4, &ptrlist_allocator);
raviX_ptrlist_add(&mystruct_list, s5, &ptrlist_allocator);
raviX_ptrlist_add(&mystruct_list, s6, &ptrlist_allocator);
struct mystruct *serial1_expected[6] = {s1, s2, s3, s4, s5, s6};
struct mystruct *serial1_got[6];
raviX_ptrlist_linearize(mystruct_list, (void **)serial1_got, 6);
for (int i = 0; i < 6; i++) {
if (serial1_expected[i] != serial1_got[i])
return 1;
}
if (raviX_ptrlist_remove(&mystruct_list, s3, 1) != 0)
return 1;
PtrListIterator iter4 = raviX_ptrlist_forward_iterator(mystruct_list);
for (struct mystruct *p = (struct mystruct *)raviX_ptrlist_iter_next(&iter4); p != NULL;
p = (struct mystruct *)raviX_ptrlist_iter_next(&iter4)) {
if (p->i == 4)
raviX_ptrlist_iter_remove(&iter4);
}
if (raviX_ptrlist_size(mystruct_list) != 4)
return 1;
struct mystruct *serial3_expected[4] = {s1, s2, s5, s6};
struct mystruct *serial3_got[4];
int reverse_expected[2] = {2, 1};
int i = 0;
struct mystruct *p;
FOR_EACH_PTR(mystruct_list, p)
{
if (i == 4)
return 1;
serial3_got[i++] = p;
if (i == 3) {
struct mystruct *p2;
int j = 0;
RECURSE_PTR_REVERSE(p, p2)
{
if (j >= 2 || reverse_expected[j] != p2->i)
return 1;
j++;
}
END_FOR_EACH_PTR_REVERSE(p2);
}
}
END_FOR_EACH_PTR(p);
if (i != 4)
return 1;
for (i = 0; i < 4; i++) {
if (serial3_expected[i] != serial3_got[i])
return 1;
}
i = 0;
PREPARE_PTR_LIST(mystruct_list, p);
while (p != NULL) {
if (i == 4)
return 1;
serial3_got[i++] = p;
NEXT_PTR_LIST(p);
}
FINISH_PTR_LIST(p);
if (i != 4)
return 1;
for (i = 0; i < 4; i++) {
if (serial3_expected[i] != serial3_got[i])
return 1;
}
i = 0;
FOR_EACH_PTR_REVERSE(mystruct_list, p)
{
if (i == 4)
return 1;
serial3_got[i++] = p;
if (i == 2) {
struct mystruct *p3;
int j = 0;
RECURSE_PTR_REVERSE(p, p3)
{
if (j >= 2 || reverse_expected[j] != p3->i)
return 1;
j++;
}
END_FOR_EACH_PTR_REVERSE(p3);
}
}
END_FOR_EACH_PTR_REVERSE(p);
if (i != 4)
return 1;
for (int i = 0; i < 4; i++) {
if (serial3_expected[3 - i] != serial3_got[i])
return 1;
}
raviX_ptrlist_remove_all(&token_list);
raviX_ptrlist_remove_all(&mystruct_list);
raviX_allocator_destroy(&token_allocator);
raviX_allocator_destroy(&mystruct_allocator);
raviX_allocator_destroy(&ptrlist_allocator);
return 0;
}
int test_ptrlist()
{
if (test_sort() != 0)
return 1;
/* For testing we set N_ temporarily */
N_ = 2;
int failure_count = test_ptrlist_basics();
N_ = LIST_NODE_NR;
if (failure_count == 0)
printf("ptrlist test okay\n");
return failure_count;
}
#endif

@ -0,0 +1,149 @@
#ifndef ravicomp_PTRLIST_H
#define ravicomp_PTRLIST_H
/*
* Generic pointer list manipulation code.
*
* (C) Copyright Linus Torvalds 2003-2005
*/
/*
* This version is part of the dmr_c project.
* Copyright (C) 2017 Dibyendu Majumdar
*/
#include <allocate.h>
#include <assert.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/*
* The ptrlist data structure is copied from the Linux Sparse project.
* It is essentially a dynamic array of pointers but the array is split up
* into nodes that are linked together. Each node contains a small number of array entries.
*
* The ptr list data structure is like a train - with cars linked to each other.
* Just as in a train each car has many seats, so in ptr list each "node" has
* several entries. Unlike a train however, the ptr list is arranged as a ring,
* i.e. the the front and back nodes are linked to each other. Hence there is no
* such thing as a 'head' of the list - i.e. any node can be the head!
*
* The disadvantage of the ptrlist structure compared to a dynamic array is
* that it consumes more memory to maintain the linked list data structure.
*
* The main advantage is that it is well suited to fixed sized memory
* allocators as there is no resizing of memory already allocated, which happens
* with dynamic arrays. The ptrlist is made up of fixed size nodes.
*/
/* number of array entries per node */
#ifndef LIST_NODE_NR
#define LIST_NODE_NR (7)
#endif
/* structure of a node */
#define DECLARE_PTR_LIST(listname, type) \
typedef struct listname { \
int nr_ : 8; \
int rm_ : 8; \
struct listname *prev_; \
struct listname *next_; \
Allocator *allocator_; \
type *list_[LIST_NODE_NR]; \
} listname
/* Each node in the list */
DECLARE_PTR_LIST(ptr_list, void);
/* The iterator strucure is used for looping */
typedef struct PtrListIterator {
struct ptr_list *__head;
struct ptr_list *__list;
int __nr;
} PtrListIterator;
/* The ptr list */
extern int raviX_ptrlist_size(const struct ptr_list *self);
extern void **raviX_ptrlist_add(struct ptr_list **self, void *ptr, Allocator *ptr_list_allocator);
extern void *raviX_ptrlist_nth_entry(struct ptr_list *list, unsigned int idx);
extern void *raviX_ptrlist_first(struct ptr_list *list);
extern void *raviX_ptrlist_last(struct ptr_list *list);
extern int raviX_ptrlist_linearize(struct ptr_list *head, void **arr, int max);
extern void raviX_ptrlist_split_node(struct ptr_list *head);
extern void raviX_ptrlist_pack(struct ptr_list **self);
extern void raviX_ptrlist_remove_all(struct ptr_list **self);
extern int raviX_ptrlist_remove(struct ptr_list **self, void *entry, int count);
extern int raviX_ptrlist_replace(struct ptr_list **self, void *old_ptr, void *new_ptr,
int count);
extern void *raviX_ptrlist_undo_last(struct ptr_list **self);
extern void *raviX_ptrlist_delete_last(struct ptr_list **self);
extern void raviX_ptrlist_concat(struct ptr_list *a, struct ptr_list **self);
extern void raviX_ptrlist_sort(struct ptr_list **self, void *,
int (*cmp)(void *, const void *, const void *));
/* iterator functions */
extern PtrListIterator raviX_ptrlist_forward_iterator(struct ptr_list *self);
extern PtrListIterator raviX_ptrlist_reverse_iterator(struct ptr_list *self);
extern void *raviX_ptrlist_iter_next(PtrListIterator *self);
extern void *raviX_ptrlist_iter_prev(PtrListIterator *self);
extern void raviX_ptrlist_iter_split_current(PtrListIterator *self);
extern void raviX_ptrlist_iter_insert(PtrListIterator *self, void *newitem);
extern void raviX_ptrlist_iter_remove(PtrListIterator *self);
extern void raviX_ptrlist_iter_set(PtrListIterator *self, void *ptr);
extern void raviX_ptrlist_iter_mark_deleted(PtrListIterator *self);
static inline void **raviX_ptrlist_iter_this_address(PtrListIterator *self) {
return &self->__list->list_[self->__nr];
}
#define ptr_list_empty(x) ((x) == NULL)
#define PTR_ENTRY_NOTAG(h,i) ((h)->list_[i])
#define PTR_ENTRY(h,i) (void *)(PTR_ENTRY_NOTAG(h,i))
#define FOR_EACH_PTR(list, var) \
{ PtrListIterator var##iter__ = raviX_ptrlist_forward_iterator((struct ptr_list *)list); \
for (var = raviX_ptrlist_iter_next(&var##iter__); var != NULL; var = raviX_ptrlist_iter_next(&var##iter__))
#define END_FOR_EACH_PTR(var) }
#define FOR_EACH_PTR_REVERSE(list, var) \
{ PtrListIterator var##iter__ = raviX_ptrlist_reverse_iterator((struct ptr_list *)list); \
for (var = raviX_ptrlist_iter_prev(&var##iter__); var != NULL; var = raviX_ptrlist_iter_prev(&var##iter__))
#define END_FOR_EACH_PTR_REVERSE(var) }
#define RECURSE_PTR_REVERSE(list, var) \
{ PtrListIterator var##iter__ = list##iter__; \
for (var = raviX_ptrlist_iter_prev(&var##iter__); var != NULL; var = raviX_ptrlist_iter_prev(&var##iter__))
#define PREPARE_PTR_LIST(list, var) \
PtrListIterator var##iter__ = raviX_ptrlist_forward_iterator((struct ptr_list *)list); \
var = raviX_ptrlist_iter_next(&var##iter__)
#define NEXT_PTR_LIST(var) \
var = raviX_ptrlist_iter_next(&var##iter__)
#define FINISH_PTR_LIST(var)
#define THIS_ADDRESS(type, var) \
(type *)raviX_ptrlist_iter_this_address(&var##iter__)
#define DELETE_CURRENT_PTR(var) \
raviX_ptrlist_iter_remove(&var##iter__)
#define REPLACE_CURRENT_PTR(type, var, replacement) \
raviX_ptrlist_iter_set(&var##iter__, replacement)
#define INSERT_CURRENT(newval, var) \
raviX_ptrlist_iter_insert(&var##iter__, newval)
#define MARK_CURRENT_DELETED(PTR_TYPE, var) \
raviX_ptrlist_iter_mark_deleted(&var##iter__)
#ifdef __cplusplus
}
#endif
#endif

@ -0,0 +1,66 @@
/* This will contain Lua bindings */
#include "ravi_api.h"
#include "ravi_compiler.h"
#include "cfg.h"
#include "codegen.h"
#include "optimizer.h"
#include "parser.h"
int raviX_compile(struct Ravi_CompilerInterface *compiler_interface)
{
int rc = 0;
int dump_ir = 0;
if (compiler_interface->compiler_options != NULL) {
dump_ir = strstr(compiler_interface->compiler_options, "--dump-ir") != NULL;
}
compiler_interface->generated_code = NULL;
CompilerState *container = raviX_init_compiler();
rc = raviX_parse(container, compiler_interface->source, compiler_interface->source_len,
compiler_interface->source_name);
if (rc != 0) {
compiler_interface->error_message(compiler_interface->context, raviX_get_last_error(container));
goto L_exit;
}
rc = raviX_ast_typecheck(container);
if (rc != 0) {
compiler_interface->error_message(compiler_interface->context, raviX_get_last_error(container));
goto L_exit;
}
rc = raviX_ast_simplify(container);
if (rc != 0) {
compiler_interface->error_message(compiler_interface->context, raviX_get_last_error(container));
goto L_exit;
}
LinearizerState *linearizer = raviX_init_linearizer(container);
rc = raviX_ast_linearize(linearizer);
if (rc != 0) {
compiler_interface->error_message(compiler_interface->context, raviX_get_last_error(container));
goto L_linend;
}
raviX_construct_cfg(linearizer->main_proc);
raviX_remove_unreachable_blocks(linearizer);
TextBuffer buf;
raviX_buffer_init(&buf, 4096);
if (dump_ir) {
raviX_buffer_add_string(&buf, "/* Following is an IR Dump from the compiler\n");
raviX_show_linearizer(linearizer, &buf);
raviX_buffer_add_string(&buf, "\nEnd of IR dump*/\n");
}
rc = raviX_generate_C(linearizer, &buf, compiler_interface);
if (rc != 0) {
raviX_buffer_free(&buf);
} else {
compiler_interface->generated_code = buf.buf;
}
L_linend:
raviX_destroy_linearizer(linearizer);
L_exit:
raviX_destroy_compiler(container);
return rc;
}

@ -0,0 +1,409 @@
/*
* Copyright © 2009 Intel Corporation
* Copyright © 1988-2004 Keith Packard and Bart Massey.
*
* 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 (including the next
* paragraph) 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.
*
* Except as contained in this notice, the names of the authors
* or their institutions shall not be used in advertising or
* otherwise to promote the sale, use or other dealings in this
* Software without prior written authorization from the
* authors.
*
* Authors:
* Eric Anholt <eric@anholt.net>
* Keith Packard <keithp@keithp.com>
*/
#include "set.h"
#include <assert.h>
#include <stdlib.h>
#define ARRAY_SIZE(array) ((int)(sizeof(array) / sizeof(array[0])))
/*
* From Knuth -- a good choice for hash/rehash values is p, p-2 where
* p and p-2 are both prime. These tables are sized to have an extra 10%
* free to avoid exponential performance degradation as the hash table fills
*/
static const uint32_t deleted_key_value;
static const void *deleted_key = &deleted_key_value;
static const struct {
uint32_t max_entries, size, rehash;
} hash_sizes[] = {
{ 2, 5, 3 },
{ 4, 7, 5 },
{ 8, 13, 11 },
{ 16, 19, 17 },
{ 32, 43, 41 },
{ 64, 73, 71 },
{ 128, 151, 149 },
{ 256, 283, 281 },
{ 512, 571, 569 },
{ 1024, 1153, 1151 },
{ 2048, 2269, 2267 },
{ 4096, 4519, 4517 },
{ 8192, 9013, 9011 },
{ 16384, 18043, 18041 },
{ 32768, 36109, 36107 },
{ 65536, 72091, 72089 },
{ 131072, 144409, 144407 },
{ 262144, 288361, 288359 },
{ 524288, 576883, 576881 },
{ 1048576, 1153459, 1153457 },
{ 2097152, 2307163, 2307161 },
{ 4194304, 4613893, 4613891 },
{ 8388608, 9227641, 9227639 },
{ 16777216, 18455029, 18455027 },
{ 33554432, 36911011, 36911009 },
{ 67108864, 73819861, 73819859 },
{ 134217728, 147639589, 147639587 },
{ 268435456, 295279081, 295279079 },
{ 536870912, 590559793, 590559791 },
{ 1073741824, 1181116273, 1181116271},
{ 2147483648ul, 2362232233ul, 2362232231ul}
};
static int
entry_is_free(const SetEntry *entry)
{
return entry->key == NULL;
}
static int
entry_is_deleted(const SetEntry *entry)
{
return entry->key == deleted_key;
}
static int
entry_is_present(const SetEntry *entry)
{
return entry->key != NULL && entry->key != deleted_key;
}
Set *raviX_set_create(uint32_t (*hash_function)(const void *key),
int (*key_equals_function)(const void *a,
const void *b))
{
Set *set;
set = malloc(sizeof(*set));
if (set == NULL)
return NULL;
set->size_index = 0;
set->size = hash_sizes[set->size_index].size;
set->rehash = hash_sizes[set->size_index].rehash;
set->max_entries = hash_sizes[set->size_index].max_entries;
set->hash_function = hash_function;
set->key_equals_function = key_equals_function;
set->table = calloc(set->size, sizeof(*set->table));
set->entries = 0;
set->deleted_entries = 0;
if (set->table == NULL) {
free(set);
return NULL;
}
return set;
}
/**
* Frees the given set.
*
* If delete_function is passed, it gets called on each entry present before
* freeing.
*/
void raviX_set_destroy(Set *set, void (*delete_function)(SetEntry *entry))
{
if (!set)
return;
if (delete_function) {
SetEntry *entry;
set_foreach(set, entry) {
delete_function(entry);
}
}
free(set->table);
free(set);
}
/* Does the set contain an entry with the given key.
*/
bool raviX_set_contains(Set *set, const void *key)
{
SetEntry *entry;
entry = raviX_set_search(set, key);
return entry != NULL;
}
/**
* Finds a set entry with the given key.
*
* Returns NULL if no entry is found.
*/
SetEntry *raviX_set_search(Set *set, const void *key)
{
uint32_t hash = set->hash_function(key);
return raviX_set_search_pre_hashed(set, hash, key);
}
/**
* Finds a set entry with the given key and hash of that key.
*
* Returns NULL if no entry is found.
*/
SetEntry *raviX_set_search_pre_hashed(Set *set, uint32_t hash, const void *key)
{
uint32_t hash_address;
hash_address = hash % set->size;
do {
uint32_t double_hash;
SetEntry *entry = set->table + hash_address;
if (entry_is_free(entry)) {
return NULL;
} else if (entry_is_present(entry) && entry->hash == hash) {
if (set->key_equals_function(key, entry->key)) {
return entry;
}
}
double_hash = 1 + hash % set->rehash;
hash_address = (hash_address + double_hash) % set->size;
} while (hash_address != hash % set->size);
return NULL;
}
static void
set_rehash(Set *set, int new_size_index)
{
Set old_set;
SetEntry *table, *entry;
if (new_size_index >= ARRAY_SIZE(hash_sizes))
return;
table = calloc(hash_sizes[new_size_index].size, sizeof(*set->table));
if (table == NULL)
return;
old_set = *set;
set->table = table;
set->size_index = new_size_index;
set->size = hash_sizes[set->size_index].size;
set->rehash = hash_sizes[set->size_index].rehash;
set->max_entries = hash_sizes[set->size_index].max_entries;
set->entries = 0;
set->deleted_entries = 0;
set_foreach(&old_set, entry) { raviX_set_add_pre_hashed(set, entry->hash, entry->key);
}
free(old_set.table);
}
/**
* Inserts the key into the set.
*
* Note that insertion may rearrange the set on a resize or rehash, so
* previously found set_entry pointers are no longer valid after this
* function.
*/
SetEntry *raviX_set_add(Set *set, const void *key)
{
uint32_t hash = set->hash_function(key);
/* Make sure nobody tries to add one of the magic values as a
* key. If you need to do so, either do so in a wrapper, or
* store keys with the magic values separately in the struct
* set.
*/
assert(key != NULL);
return raviX_set_add_pre_hashed(set, hash, key);
}
/**
* Inserts the key with the given hash into the set.
*
* Note that insertion may rearrange the set on a resize or rehash, so
* previously found set_entry pointers are no longer valid after this
* function.
*/
SetEntry *raviX_set_add_pre_hashed(Set *set, uint32_t hash, const void *key)
{
uint32_t hash_address;
SetEntry *available_entry = NULL;
if (set->entries >= set->max_entries) {
set_rehash(set, set->size_index + 1);
} else if (set->deleted_entries + set->entries >= set->max_entries) {
set_rehash(set, set->size_index);
}
hash_address = hash % set->size;
do {
SetEntry *entry = set->table + hash_address;
uint32_t double_hash;
if (!entry_is_present(entry)) {
/* Stash the first available entry we find */
if (available_entry == NULL)
available_entry = entry;
if (entry_is_free(entry))
break;
}
/* Implement replacement when another insert happens
* with a matching key. This is a relatively common
* feature of hash tables, with the alternative
* generally being "insert the new value as well, and
* return it first when the key is searched for".
*
* Note that the set doesn't have a delete callback.
* If freeing of old keys is required to avoid memory leaks,
* perform a search before inserting.
*/
if (!entry_is_deleted(entry) &&
entry->hash == hash &&
set->key_equals_function(key, entry->key)) {
entry->key = key;
return entry;
}
double_hash = 1 + hash % set->rehash;
hash_address = (hash_address + double_hash) % set->size;
} while (hash_address != hash % set->size);
if (available_entry) {
if (entry_is_deleted(available_entry))
set->deleted_entries--;
available_entry->hash = hash;
available_entry->key = key;
set->entries++;
return available_entry;
}
/* We could hit here if a required resize failed. An unchecked-malloc
* application could ignore this result.
*/
return NULL;
}
/**
* This function searches for, and removes an entry from the set.
*
* If the caller has previously found a SetEntry pointer,
* (from calling raviX_set_search or remembering it from raviX_set_add), then
* raviX_set_remove_entry can be called instead to avoid an extra search.
*/
void raviX_set_remove(Set *set, const void *key)
{
SetEntry *entry;
entry = raviX_set_search(set, key);
raviX_set_remove_entry(set, entry);
}
/**
* This function deletes the set given set entry.
*
* Note that deletion doesn't otherwise modify the set, so an
* iteration over the set deleting entries is safe.
*/
void raviX_set_remove_entry(Set *set, SetEntry *entry)
{
if (!entry)
return;
entry->key = deleted_key;
set->entries--;
set->deleted_entries++;
}
/**
* This function is an iterator over the set.
*
* Pass in NULL for the first entry, as in the start of a for loop.
* Note that an iteration over the set is O(table_size) not
* O(entries).
*/
SetEntry *raviX_set_next_entry(Set *set, SetEntry *entry)
{
if (entry == NULL)
entry = set->table;
else
entry = entry + 1;
for (; entry != set->table + set->size; entry++) {
if (entry_is_present(entry)) {
return entry;
}
}
return NULL;
}
#ifndef _WIN32
SetEntry *raviX_set_random_entry(Set *set,
int (*predicate)(SetEntry *entry))
{
SetEntry *entry;
uint32_t i = random() % set->size;
if (set->entries == 0)
return NULL;
for (entry = set->table + i; entry != set->table + set->size; entry++) {
if (entry_is_present(entry) &&
(!predicate || predicate(entry))) {
return entry;
}
}
for (entry = set->table; entry != set->table + i; entry++) {
if (entry_is_present(entry) &&
(!predicate || predicate(entry))) {
return entry;
}
}
return NULL;
}
#endif

@ -0,0 +1,87 @@
/*
* Copyright © 2009 Intel Corporation
*
* 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 (including the next
* paragraph) 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.
*
* Authors:
* Eric Anholt <eric@anholt.net>
*
*/
#ifndef ravicomp_SET_H
#define ravicomp_SET_H
#include <inttypes.h>
#include <stdbool.h>
typedef struct SetEntry {
uint32_t hash;
const void *key;
} SetEntry;
typedef struct Set {
SetEntry *table;
uint32_t (*hash_function)(const void *key);
int (*key_equals_function)(const void *a, const void *b);
uint32_t size;
uint32_t rehash;
uint32_t max_entries;
uint32_t size_index;
uint32_t entries;
uint32_t deleted_entries;
} Set;
Set *raviX_set_create(uint32_t (*hash_function)(const void *key),
int (*key_equals_function)(const void *a,
const void *b));
void raviX_set_destroy(Set *set,
void (*delete_function)(SetEntry *entry));
SetEntry *raviX_set_add(Set *set, const void *key);
bool raviX_set_contains(Set *set, const void *key);
void raviX_set_remove(Set *set, const void *key);
SetEntry *raviX_set_search(Set *set, const void *key);
void raviX_set_remove_entry(Set *set, SetEntry *entry);
SetEntry *raviX_set_next_entry(Set *set, SetEntry *entry);
SetEntry *raviX_set_random_entry(Set *set,
int (*predicate)(SetEntry *entry));
/**
* This foreach function is safe against deletion (which just replaces
* an entry's data with the deleted marker), but not against insertion
* (which may rehash the table, making entry a dangling pointer).
*/
#define set_foreach(ht, entry) \
for (entry = raviX_set_next_entry(ht, NULL); \
entry != NULL; \
entry = raviX_set_next_entry(ht, entry))
/* Alternate interfaces to reduce repeated calls to hash function. */
SetEntry *raviX_set_search_pre_hashed(Set *set, uint32_t hash, const void *key);
SetEntry *raviX_set_add_pre_hashed(Set *set, uint32_t hash, const void *key);
#endif

@ -0,0 +1,522 @@
/*
Copyright (C) 2018-2020 Dibyendu Majumdar
*/
#include <parser.h>
/*
The Type checker walks through the AST and annotates nodes with type information.
It also checks that the operations are valid.
There are following assumptions made about the code generator backend.
a) Function arguments that have type info must be asserted at runtime
b) Local variable assignments in 'local' or expression statements must be asserted by the backend
c) We allow assigning integer value to number and vice versa in the AST but the code generator must assert this is valid
d) Any unassigned local vars that have type info must be set to valid initial values.
None of these operations are explicit in the AST.
*/
static bool is_type_same(const VariableType *a, const VariableType *b)
{
// String comparion of type_name relies upon strings being interned
return a->type_code == b->type_code && a->type_name == b->type_name;
}
static void handle_error(CompilerState *container, const char *msg)
{
// TODO source and line number
raviX_buffer_add_string(&container->error_message, msg);
longjmp(container->env, 1);
}
/* Type checker - WIP */
static void typecheck_ast_node(CompilerState *container, AstNode *function, AstNode *node);
/* Type checker - WIP */
static void typecheck_ast_list(CompilerState *container, AstNode *function, AstNodeList *list)
{
AstNode *node;
FOR_EACH_PTR(list, node) { typecheck_ast_node(container, function, node); }
END_FOR_EACH_PTR(node);
}
/* Type checker - WIP */
static void typecheck_unary_operator(CompilerState *container, AstNode *function, AstNode *node)
{
UnaryOperatorType 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 UNOPR_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 UNOPR_LEN:
if (subexpr_type == RAVI_TARRAYINT || subexpr_type == RAVI_TARRAYFLT) {
set_type(&node->unary_expr.type, RAVI_TNUMINT);
}
break;
case UNOPR_TO_INTEGER:
set_type(&node->unary_expr.type, RAVI_TNUMINT);
break;
case UNOPR_TO_NUMBER:
set_type(&node->unary_expr.type, RAVI_TNUMFLT);
break;
case UNOPR_TO_CLOSURE:
set_type(&node->unary_expr.type, RAVI_TFUNCTION);
break;
case UNOPR_TO_STRING:
set_type(&node->unary_expr.type, RAVI_TSTRING);
break;
case UNOPR_TO_INTARRAY:
set_type(&node->unary_expr.type, RAVI_TARRAYINT);
if (node->unary_expr.expr->type == EXPR_TABLE_LITERAL) {
set_type(&node->unary_expr.expr->table_expr.type, RAVI_TARRAYINT);
}
break;
case UNOPR_TO_NUMARRAY:
set_type(&node->unary_expr.type, RAVI_TARRAYFLT);
if (node->unary_expr.expr->type == EXPR_TABLE_LITERAL) {
set_type(&node->unary_expr.expr->table_expr.type, RAVI_TARRAYFLT);
}
break;
case UNOPR_TO_TABLE:
set_type(&node->unary_expr.type, RAVI_TTABLE);
break;
case UNOPR_TO_TYPE:
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_binary_operator(CompilerState *container, AstNode *function,
AstNode *node)
{
BinaryOperatorType op = node->binary_expr.binary_op;
AstNode *e1 = node->binary_expr.expr_left;
AstNode *e2 = node->binary_expr.expr_right;
typecheck_ast_node(container, function, e1);
typecheck_ast_node(container, function, e2);
switch (op) {
case BINOPR_ADD:
case BINOPR_SUB:
case BINOPR_MUL:
case BINOPR_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 != BINOPR_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 == BINOPR_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 BINOPR_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 BINOPR_BAND:
case BINOPR_BOR:
case BINOPR_BXOR:
case BINOPR_SHL:
case BINOPR_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 BINOPR_EQ:
case BINOPR_NE:
case BINOPR_GE:
case BINOPR_GT:
case BINOPR_LE:
case BINOPR_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 BINOPR_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 BINOPR_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(VariableType *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(CompilerState *container, AstNode *function, AstNode *node)
{
typecheck_ast_node(container, function, node->suffixed_expr.primary_expr);
AstNode *prev_node = node->suffixed_expr.primary_expr;
AstNode *this_node;
FOR_EACH_PTR(node->suffixed_expr.suffix_list, this_node)
{
typecheck_ast_node(container, function, this_node);
if (this_node->type == EXPR_Y_INDEX) {
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 {
handle_error(container, "invalid type in index");
}
} 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 {
handle_error(container, "invalid type in index");
}
} else if (is_unindexable_type(&prev_node->common_expr.type)) {
handle_error(container, "invalid type in index");
}
}
prev_node = this_node;
}
END_FOR_EACH_PTR(node);
copy_type(&node->suffixed_expr.type, &prev_node->common_expr.type);
}
static void typecheck_var_assignment(CompilerState *container, VariableType *var_type, AstNode *expr,
const StringObject *var_name)
{
if (var_type->type_code == RAVI_TANY)
// Any value can be assigned to type ANY
return;
const char *variable_name = var_name ? var_name->str : "unknown-TODO";
VariableType *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 == EXPR_UNARY && expr->unary_expr.unary_op == UNOPR_LEN)) {
/* Okay, but backend must do appropriate conversion */
;
} else if (expr_type->type_code != RAVI_TNUMINT) {
fprintf(stderr, "Assignment to local symbol %s is not type compatible\n", variable_name);
}
return;
}
if (var_type->type_code == RAVI_TNUMFLT) {
if (expr_type->type_code == RAVI_TNUMINT) {
/* Okay, but backend must do appropriate conversion */
;
} else if (expr_type->type_code != RAVI_TNUMFLT) {
fprintf(stderr, "Assignment to local symbol %s is not type compatible\n", variable_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", variable_name);
}
}
static void typecheck_local_statement(CompilerState *container, AstNode *function,
AstNode *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);
LuaSymbol *var;
AstNode *expr;
PREPARE_PTR_LIST(node->local_stmt.var_list, var);
PREPARE_PTR_LIST(node->local_stmt.expr_list, expr);
for (;;) {
if (!var || !expr)
break;
VariableType *var_type = &var->variable.value_type;
const StringObject *var_name = var->variable.var_name;
typecheck_var_assignment(container, var_type, expr, var_name);
NEXT_PTR_LIST(var);
NEXT_PTR_LIST(expr);
}
}
static void typecheck_expr_statement(CompilerState *container, AstNode *function, AstNode *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;
AstNode *var;
AstNode *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;
VariableType *var_type = &var->common_expr.type;
const StringObject *var_name = NULL; // 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(CompilerState *container, AstNode *function,
AstNode *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(CompilerState *container, AstNode *function,
AstNode *node)
{
typecheck_ast_list(container, function, node->for_stmt.expr_list);
AstNode *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;
LuaSymbolList *symbols = node->for_stmt.symbols;
LuaSymbol *sym;
/* actually there will be only index variable */
FOR_EACH_PTR(symbols, sym)
{
if (sym->symbol_type == SYM_LOCAL) {
set_typecode(&sym->variable.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(CompilerState *container, AstNode *function, AstNode *node)
{
AstNode *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(CompilerState *container, AstNode *function,
AstNode *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(CompilerState *container, AstNode *function, AstNode *node)
{
switch (node->type) {
case EXPR_FUNCTION: {
/* args need type assertions but those have no ast - i.e. code gen should do it */
typecheck_ast_list(container, function, node->function_expr.function_statement_list);
break;
}
case AST_NONE: {
break;
}
case STMT_RETURN: {
typecheck_ast_list(container, function, node->return_stmt.expr_list);
break;
}
case STMT_LOCAL: {
typecheck_local_statement(container, function, node);
break;
}
case STMT_FUNCTION: {
typecheck_ast_node(container, function, node->function_stmt.function_expr);
break;
}
case STMT_LABEL: {
break;
}
case STMT_GOTO: {
break;
}
case STMT_DO: {
break;
}
case STMT_EXPR: {
typecheck_expr_statement(container, function, node);
break;
}
case STMT_IF: {
typecheck_if_statement(container, function, node);
break;
}
case STMT_WHILE:
case STMT_REPEAT: {
typecheck_while_or_repeat_statement(container, function, node);
break;
}
case STMT_FOR_IN: {
typecheck_for_in_statment(container, function, node);
break;
}
case STMT_FOR_NUM: {
typecheck_for_num_statment(container, function, node);
break;
}
case EXPR_SUFFIXED: {
typecheck_suffixedexpr(container, function, node);
break;
}
case EXPR_FUNCTION_CALL: {
if (node->function_call_expr.method_name) {
} else {
}
typecheck_ast_list(container, function, node->function_call_expr.arg_list);
break;
}
case EXPR_SYMBOL: {
/* symbol type should have been set when symbol was created */
if (node->symbol_expr.var->symbol_type != SYM_LABEL) {
copy_type(&node->symbol_expr.type, &node->symbol_expr.var->variable.value_type);
}
else {
// TODO can this happen?
node->symbol_expr.type.type_code = RAVI_TANY;
}
break;
}
case EXPR_BINARY: {
typecheck_binary_operator(container, function, node);
break;
}
case EXPR_UNARY: {
typecheck_unary_operator(container, function, node);
break;
}
case EXPR_LITERAL: {
/* type set during parsing */
break;
}
case EXPR_FIELD_SELECTOR: {
typecheck_ast_node(container, function, node->index_expr.expr);
break;
}
case EXPR_Y_INDEX: {
typecheck_ast_node(container, function, node->index_expr.expr);
break;
}
case EXPR_TABLE_ELEMENT_ASSIGN: {
if (node->table_elem_assign_expr.key_expr) {
typecheck_ast_node(container, function, node->table_elem_assign_expr.key_expr);
}
typecheck_ast_node(container, function, node->table_elem_assign_expr.value_expr);
copy_type(&node->table_elem_assign_expr.type, &node->table_elem_assign_expr.value_expr->common_expr.type);
break;
}
case EXPR_TABLE_LITERAL: {
typecheck_ast_list(container, function, node->table_expr.expr_list);
break;
}
default:
assert(0);
}
}
/* Type checker - WIP */
static void typecheck_function(CompilerState *container, AstNode *func)
{
typecheck_ast_list(container, func, func->function_expr.function_statement_list);
}
/* Type checker - WIP */
int raviX_ast_typecheck(CompilerState *container)
{
AstNode *main_function = container->main_function;
raviX_buffer_reset(&container->error_message);
int rc = setjmp(container->env);
if (rc == 0) {
typecheck_function(container, main_function);
}
return rc;
}
Loading…
Cancel
Save