issue #203 Removing LLVM support, see llvm branch for archived version

cmake
Dibyendu Majumdar 3 years ago
parent 05f365352c
commit 66511033fd

@ -3,6 +3,7 @@ os:
- linux - linux
arch: arch:
- amd64 - amd64
- arm64
compiler: compiler:
- gcc - gcc
cache: ccache cache: ccache
@ -10,16 +11,9 @@ dist: bionic
addons: addons:
apt: apt:
packages: packages:
- g++
- gcc - gcc
- ccache - ccache
install:
- curl https://releases.llvm.org/6.0.1/clang+llvm-6.0.1-x86_64-linux-gnu-ubuntu-16.04.tar.xz | tar -xJf -
script: script:
- mkdir $TRAVIS_BUILD_DIR/build
- cd $TRAVIS_BUILD_DIR/build && cmake -DCMAKE_BUILD_TYPE=Debug -DLLVM_DIR=$TRAVIS_BUILD_DIR/clang+llvm-6.0.1-x86_64-linux-gnu-ubuntu-16.04/lib/cmake/llvm -G "Unix Makefiles" -DLLVM_JIT=ON ..
- cd $TRAVIS_BUILD_DIR/build && make
- cd $TRAVIS_BUILD_DIR/tests && sh ./run_tests.sh $TRAVIS_BUILD_DIR/build/ravi
- mkdir $TRAVIS_BUILD_DIR/buildmir - mkdir $TRAVIS_BUILD_DIR/buildmir
- cd $TRAVIS_BUILD_DIR/buildmir && cmake -DCMAKE_BUILD_TYPE=Debug -DLTESTS=ON -DMIR_JIT=ON -G "Unix Makefiles" .. - cd $TRAVIS_BUILD_DIR/buildmir && cmake -DCMAKE_BUILD_TYPE=Debug -DLTESTS=ON -DMIR_JIT=ON -G "Unix Makefiles" ..
- cd $TRAVIS_BUILD_DIR/buildmir && make - cd $TRAVIS_BUILD_DIR/buildmir && make

@ -1,12 +1,9 @@
cmake_minimum_required(VERSION 3.12) cmake_minimum_required(VERSION 3.12)
project(Ravi VERSION 1.0.4 LANGUAGES C CXX) project(Ravi VERSION 1.0.4 LANGUAGES C)
# By default on platforms where MIR is supported (right now Linux X86_64) # By default MIR JIT backend is automatically enabled. To disable user must specify
# MIR JIT backend is automatically enabled. To disable user must specify
# NO_JIT=ON # NO_JIT=ON
# LLVM has to be explicitly specified using LLVM_JIT=ON
option(LLVM_JIT "Controls whether LLVM JIT compilation will be enabled, default is OFF" OFF)
option(NO_JIT "Controls whether JIT should be disabled, default is OFF" OFF) option(NO_JIT "Controls whether JIT should be disabled, default is OFF" OFF)
option(STATIC_BUILD "Build static version of Ravi, default is OFF" OFF) 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(COMPUTED_GOTO "Controls whether the interpreter switch will use computed gotos on gcc/clang, default is ON" ON)
@ -16,14 +13,12 @@ option(RAVICOMP "Controls whether to link in RaviComp" OFF)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake") set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake")
# By default on non-Windows platforms we enable MIR JIT # By we enable MIR JIT
if (NOT LLVM_JIT if (NOT NO_JIT)
AND NOT NO_JIT)
set(MIR_JIT ON) set(MIR_JIT ON)
endif () endif ()
if (MIR_JIT) if (MIR_JIT)
set(LLVM_JIT OFF)
set(STATIC_BUILD OFF) # Because we need to expose the symbols in the library set(STATIC_BUILD OFF) # Because we need to expose the symbols in the library
endif () endif ()
@ -51,12 +46,6 @@ set(LUA_CORE_SRCS src/lapi.c src/lcode.c src/lctype.c src/ldebug.c src/ldo.c src
set(LUA_LIB_SRCS src/lauxlib.c src/lbaselib.c src/lbitlib.c src/lcorolib.c src/ldblib.c src/liolib.c set(LUA_LIB_SRCS src/lauxlib.c src/lbaselib.c src/lbitlib.c src/lcorolib.c src/ldblib.c src/liolib.c
src/lmathlib.c src/loslib.c src/ltablib.c src/lstrlib.c src/loadlib.c src/linit.c src/lutf8lib.c) src/lmathlib.c src/loslib.c src/ltablib.c src/lstrlib.c src/loadlib.c src/linit.c src/lutf8lib.c)
set(LUA_HEADERS include/lua.h include/luaconf.h include/lualib.h include/lauxlib.h) set(LUA_HEADERS include/lua.h include/luaconf.h include/lualib.h include/lauxlib.h)
# LLVM code gen
set(LLVM_JIT_SRCS src/ravi_llvmjit.cpp src/ravi_llvmtypes.cpp
src/ravi_llvmcodegen.cpp src/ravi_llvmforprep.cpp src/ravi_llvmcomp.cpp
src/ravi_llvmreturn.cpp src/ravi_llvmload.cpp src/ravi_llvmforloop.cpp
src/ravi_llvmarith1.cpp src/ravi_llvmcall.cpp src/ravi_llvmtable.cpp
src/ravi_llvmarith2.cpp src/ravi_llvmtforcall.cpp src/ravi_llvmrest.cpp)
set(MIR_HEADERS mir/mir.h mir/mir-gen.h mir/mir-varr.h mir/mir-dlist.h mir/mir-htab.h set(MIR_HEADERS mir/mir.h mir/mir-gen.h mir/mir-varr.h mir/mir-dlist.h mir/mir-htab.h
mir/mir-hash.h mir/mir-bitmap.h) mir/mir-hash.h mir/mir-bitmap.h)
set(MIR_SRCS mir/mir.c mir/mir-gen.c) set(MIR_SRCS mir/mir.c mir/mir-gen.c)
@ -89,56 +78,26 @@ if (ASAN)
endif () endif ()
endif () endif ()
if (LLVM_JIT) if (MIR_JIT)
find_package(LLVM REQUIRED CONFIG) message(STATUS "MIRJIT enabled")
message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") set(JIT_SRCS ${MIR_SRCS} ${C2MIR_SRCS} ${MIR_JIT_SRCS})
message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") set(ALL_MIR_SRCS ${MIR_SRCS} ${C2MIR_SRCS})
message(STATUS "LLVM Definitions ${LLVM_DEFINITIONS}")
message(STATUS "LLVMJIT enabled")
set(JIT_SRCS ${LLVM_JIT_SRCS})
if (NOT MSVC) if (NOT MSVC)
set_source_files_properties(${LLVM_JIT_SRCS} PROPERTIES set_source_files_properties(${ALL_MIR_SRCS} PROPERTIES
COMPILE_FLAGS "-fno-rtti -fno-exceptions ${LLVM_DEFINITIONS}") COMPILE_FLAGS "-fsigned-char")
endif () endif ()
set_property( set_property(SOURCE ${MIR_SRCS} ${C2MIR_SRCS} ${MIR_JIT_SRCS}
SOURCE ${LLVM_JIT_SRCS}
APPEND APPEND
PROPERTY INCLUDE_DIRECTORIES ${LLVM_INCLUDE_DIRS} PROPERTY INCLUDE_DIRECTORIES "${CMAKE_SOURCE_DIR}/mir;${CMAKE_SOURCE_DIR}/mir/c2mir")
) set_property(SOURCE ${MIR_SRCS} ${C2MIR_SRCS} ${MIR_JIT_SRCS}
# FIXME get rid of this dependency
set_property(
SOURCE ${LUA_CMD_SRCS}
APPEND APPEND
PROPERTY INCLUDE_DIRECTORIES ${LLVM_INCLUDE_DIRS}) PROPERTY COMPILE_DEFINITIONS "MIR_NO_IO=0;MIR_NO_SCAN=1;MIR_NO_INTERP=1")
if ($ENV{CLION_IDE}) if ($ENV{CLION_IDE})
# CLion seems unable to handle include paths set on sources # CLion seems unable to handle include paths set on sources
include_directories(${LLVM_INCLUDE_DIRS}) include_directories("${CMAKE_SOURCE_DIR}/mir;${CMAKE_SOURCE_DIR}/mir/c2mir")
endif () endif ()
else () else ()
if (MIR_JIT) set(JIT_SRCS ${NO_JIT_SRCS})
message(STATUS "MIRJIT enabled")
set(JIT_SRCS ${MIR_SRCS} ${C2MIR_SRCS} ${MIR_JIT_SRCS})
set(ALL_MIR_SRCS ${MIR_SRCS} ${C2MIR_SRCS})
if (NOT MSVC)
set_source_files_properties(${ALL_MIR_SRCS} PROPERTIES
COMPILE_FLAGS "-fsigned-char")
endif ()
set_property(SOURCE ${MIR_SRCS} ${C2MIR_SRCS} ${MIR_JIT_SRCS}
APPEND
PROPERTY INCLUDE_DIRECTORIES "${CMAKE_SOURCE_DIR}/mir;${CMAKE_SOURCE_DIR}/mir/c2mir")
set_property(SOURCE ${MIR_SRCS} ${C2MIR_SRCS} ${MIR_JIT_SRCS}
APPEND
PROPERTY COMPILE_DEFINITIONS "MIR_NO_IO=0;MIR_NO_SCAN=1;MIR_NO_INTERP=1")
if ($ENV{CLION_IDE})
# CLion seems unable to handle include paths set on sources
include_directories("${CMAKE_SOURCE_DIR}/mir;${CMAKE_SOURCE_DIR}/mir/c2mir")
endif ()
else ()
set(JIT_SRCS ${NO_JIT_SRCS})
endif ()
endif () endif ()
if (RAVICOMP) if (RAVICOMP)
@ -177,64 +136,6 @@ elseif (NOT WIN32)
set(EXTRA_LIBRARIES m dl readline) set(EXTRA_LIBRARIES m dl readline)
endif () endif ()
# Enable minimal required LLVM components so that the
# the size of the resulting binary is manageable
if (LLVM_JIT)
if (${LLVM_PACKAGE_VERSION} VERSION_LESS "3.8")
set(LLVM_EXTRA_LIBS ipa)
endif ()
if (NOT ${LLVM_PACKAGE_VERSION} VERSION_LESS "5.0.0")
set(LLVM_EXTRA_LIBS orcjit)
endif ()
message(STATUS "SYSTEM_PROCESSOR ${CMAKE_SYSTEM_PROCESSOR}")
if (CMAKE_SYSTEM_PROCESSOR MATCHES "(x86)|(X86)|(amd64)|(AMD64)")
set(LLVM_LIBS_PROCESSOR
X86CodeGen
X86AsmParser
X86Disassembler
X86Desc
X86Info
X86Utils
)
if (${LLVM_PACKAGE_VERSION} VERSION_LESS "9.0.0")
list(APPEND LLVM_LIBS_PROCESSOR X86AsmPrinter)
endif ()
elseif (CMAKE_SYSTEM_PROCESSOR MATCHES "arm")
set(LLVM_LIBS_PROCESSOR
ARMCodeGen
ARMAsmParser
ARMDisassembler
ARMAsmPrinter
ARMDesc
ARMInfo
)
endif ()
llvm_map_components_to_libnames(LLVM_LIBS
Analysis
AsmParser
AsmPrinter
BitReader
Core
CodeGen
ExecutionEngine
InstCombine
${LLVM_EXTRA_LIBS}
ipo
MC
MCJIT
MCParser
Object
RuntimeDyld
ScalarOpts
Support
Target
TransformUtils
${LLVM_LIBS_PROCESSOR}
)
message(STATUS "LLVM_LIBS ${LLVM_LIBS}")
endif ()
set(LIBRAVI_NAME libravi) set(LIBRAVI_NAME libravi)
#Main library #Main library
@ -244,7 +145,7 @@ add_library(${LIBRAVI_NAME} ${LIBRAVI_BUILD_TYPE}
${LUA_CORE_SRCS} ${LUA_CORE_SRCS}
${JIT_SRCS} ${JIT_SRCS}
${ADDON_SRCS}) ${ADDON_SRCS})
target_link_libraries(${LIBRAVI_NAME} ${EXTRA_LIBRARIES} ${LLVM_LIBS} ${MIRJIT_LIBRARIES} ${RAVICOMP_LIBRARIES}) target_link_libraries(${LIBRAVI_NAME} ${EXTRA_LIBRARIES} ${MIRJIT_LIBRARIES} ${RAVICOMP_LIBRARIES})
# Main Ravi executable # Main Ravi executable
add_executable(ravi ${LUA_CMD_SRCS}) add_executable(ravi ${LUA_CMD_SRCS})
@ -281,13 +182,7 @@ if (NOT LTESTS)
PROPERTY COMPILE_DEFINITIONS NO_LUA_DEBUG) PROPERTY COMPILE_DEFINITIONS NO_LUA_DEBUG)
set(NO_LUA_DEBUG 1) set(NO_LUA_DEBUG 1)
endif () endif ()
if (LLVM_JIT) if (MIR_JIT)
set_property(
TARGET ${LIBRAVI_NAME} ravi
APPEND
PROPERTY COMPILE_DEFINITIONS "USE_LLVM=1")
set(USE_LLVM 1)
elseif (MIR_JIT)
set_property( set_property(
TARGET ${LIBRAVI_NAME} ravi TARGET ${LIBRAVI_NAME} ravi
APPEND APPEND
@ -325,8 +220,6 @@ elseif (UNIX)
APPEND APPEND
PROPERTY COMPILE_DEFINITIONS "LUA_USE_LINUX=1") PROPERTY COMPILE_DEFINITIONS "LUA_USE_LINUX=1")
endif () endif ()
set_property(TARGET ${LIBRAVI_NAME} PROPERTY CXX_STANDARD 14)
set_property(TARGET ${LIBRAVI_NAME} PROPERTY CXX_EXTENSIONS OFF)
include(GNUInstallDirs) include(GNUInstallDirs)
configure_file(ravi-config.h.in ravi-config.h @ONLY) configure_file(ravi-config.h.in ravi-config.h @ONLY)

@ -5,7 +5,7 @@ Ravi Programming Language
:target: https://travis-ci.org/dibyendumajumdar/ravi :target: https://travis-ci.org/dibyendumajumdar/ravi
Ravi is a dialect of `Lua <http://www.lua.org/>`_ with limited optional static typing and Ravi is a dialect of `Lua <http://www.lua.org/>`_ with limited optional static typing and
features `MIR <https://github.com/vnmakarov/mir>`_ and `LLVM <http://www.llvm.org/>`_ powered JIT compilers. features `MIR <https://github.com/vnmakarov/mir>`_ powered JIT compilers.
The name Ravi comes from the Sanskrit word for the Sun. The name Ravi comes from the Sanskrit word for the Sun.
Interestingly a precursor to Lua was `Sol <http://www.lua.org/history.html>`_ which had support for Interestingly a precursor to Lua was `Sol <http://www.lua.org/history.html>`_ which had support for
static types; Sol means the Sun in Portugese. static types; Sol means the Sun in Portugese.
@ -35,8 +35,7 @@ Features
* Compatibility with Lua 5.3 (see Compatibility section below) * Compatibility with Lua 5.3 (see Compatibility section below)
* Generational GC from Lua 5.4 * Generational GC from Lua 5.4
* ``defer`` statement for releasing resources * ``defer`` statement for releasing resources
* Compact JIT backend `MIR <https://github.com/vnmakarov/mir>`_; only Linux and x86-64 supported for now. * Compact JIT backend `MIR <https://github.com/vnmakarov/mir>`_.
* `LLVM <http://www.llvm.org/>`_ supported as alternative JIT backend.
* A `distribution with batteries <https://github.com/dibyendumajumdar/Suravi>`_. * A `distribution with batteries <https://github.com/dibyendumajumdar/Suravi>`_.
* A `Visual Studio Code debugger extension <https://marketplace.visualstudio.com/items?itemName=ravilang.ravi-debug>`_ - interpreted mode debugger. * A `Visual Studio Code debugger extension <https://marketplace.visualstudio.com/items?itemName=ravilang.ravi-debug>`_ - interpreted mode debugger.
@ -44,7 +43,6 @@ Documentation
============= =============
* For the Lua extensions in Ravi see the `Reference Manual <https://the-ravi-programming-language.readthedocs.io/en/latest/ravi-reference.html>`_. * For the Lua extensions in Ravi see the `Reference Manual <https://the-ravi-programming-language.readthedocs.io/en/latest/ravi-reference.html>`_.
* `MIR JIT Build instructions <https://the-ravi-programming-language.readthedocs.io/en/latest/ravi-mir-instructions.html>`_. * `MIR JIT Build instructions <https://the-ravi-programming-language.readthedocs.io/en/latest/ravi-mir-instructions.html>`_.
* `LLVM JIT Build instructions <https://the-ravi-programming-language.readthedocs.io/en/latest/ravi-llvm-instructions.html>`_.
* Also see `Ravi Documentation <http://the-ravi-programming-language.readthedocs.org/en/latest/index.html>`_. * Also see `Ravi Documentation <http://the-ravi-programming-language.readthedocs.org/en/latest/index.html>`_.
* and the slides I presented at the `Lua 2015 Workshop <http://www.lua.org/wshop15.html>`_. * and the slides I presented at the `Lua 2015 Workshop <http://www.lua.org/wshop15.html>`_.
@ -99,6 +97,7 @@ When JIT compilation is enabled there are following additional constraints:
* Ravi will only execute JITed code from the main Lua thread; any secondary threads (coroutines) execute in interpreter mode. * Ravi will only execute JITed code from the main Lua thread; any secondary threads (coroutines) execute in interpreter mode.
* In JITed code tailcalls are implemented as regular calls so unlike the interpreter VM which supports infinite tail recursion JIT compiled code only supports tail recursion to a depth of about 110 (issue #17) * In JITed code tailcalls are implemented as regular calls so unlike the interpreter VM which supports infinite tail recursion JIT compiled code only supports tail recursion to a depth of about 110 (issue #17)
* Debug api and hooks are not supported in JIT mode
History History
======= =======
@ -115,11 +114,13 @@ History
- Created `Ravi with batteries <https://github.com/dibyendumajumdar/Suravi>`_. - Created `Ravi with batteries <https://github.com/dibyendumajumdar/Suravi>`_.
* 2019 * 2019
- New language feature - `defer` statement - New language feature - `defer` statement
- New JIT backend `MIR <https://github.com/vnmakarov/mir>`_. - New JIT backend `MIR <https://github.com/vnmakarov/mir>`_.
* 2020
* 2020 (Plan) - `New parser / type checker / compiler <https://github.com/dibyendumajumdar/ravi-compiler>`_
- `New optimizing byte code generator based on new parser / type checker <https://github.com/dibyendumajumdar/ravi-compiler>`_
- Generational GC back-ported from Lua 5.4 - Generational GC back-ported from Lua 5.4
- Support for `LLVM backend <https://github.com/dibyendumajumdar/ravi/tree/llvm>`_ archived
* 2021 (Plan)
- Integrated AOT and JIT compilation support
- Ravi 1.0 release - Ravi 1.0 release
License License

@ -5,4 +5,4 @@ The scripts here are unsupported - these are just my personal
build scripts. build scripts.
The unix LLVM debug builds enable 'ltests' and address sanitizer. The debug builds enable 'ltests' and address sanitizer.

@ -1,16 +0,0 @@
# Run this on LLVM 10 source dir
mkdir build
cd build
cmake3 -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=$HOME/Software/llvm10 \
-DLLVM_TARGETS_TO_BUILD="X86" \
-DLLVM_BUILD_TOOLS=OFF \
-DLLVM_INCLUDE_TOOLS=OFF \
-DLLVM_BUILD_EXAMPLES=OFF \
-DLLVM_INCLUDE_EXAMPLES=OFF \
-DLLVM_BUILD_TESTS=OFF \
-DLLVM_INCLUDE_TESTS=OFF \
-DLLVM_OPTIMIZED_TABLEGEN=ON \
..
make install

@ -1,5 +0,0 @@
mkdir llvm32
cd llvm32
rem cmake -DCMAKE_INSTALL_PREFIX=\d\ravi32 -G "Visual Studio 14" -DLLVM_JIT=ON -DLLVM_DIR=\d\LLVM37_32\share\llvm\cmake -DBUILD_STATIC=OFF ..
cmake -DCMAKE_INSTALL_PREFIX=\d\ravi32 -G "Visual Studio 14" -DLLVM_JIT=ON -DLLVM_DIR=\d\LLVM39_32\lib\cmake\llvm -DSTATIC_BUILD=OFF ..
cd ..

@ -1,5 +0,0 @@
mkdir llvm32
cd llvm32
rem cmake -DCMAKE_INSTALL_PREFIX=\d\ravi32 -G "Visual Studio 14" -DLLVM_JIT=ON -DLLVM_DIR=\d\LLVM37_32\share\llvm\cmake ..
cmake -DCMAKE_INSTALL_PREFIX=\d\ravi32 -G "Visual Studio 14" -DLLVM_JIT=ON -DLLVM_DIR=\d\LLVM39\lib\cmake\llvm -DSTATIC_BUILD=ON ..
cd ..

@ -1,5 +0,0 @@
mkdir llvm32d
cd llvm32d
rem cmake -DCMAKE_INSTALL_PREFIX=\d\ravi32 -G "Visual Studio 14" -DLLVM_JIT=ON -DLLVM_DIR=\d\LLVM37_32\share\llvm\cmake ..
cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=\d\ravi32 -G "Visual Studio 14" -DLLVM_JIT=ON -DLLVM_DIR=\d\LLVM39D_32\lib\cmake\llvm -DSTATIC_BUILD=ON ..
cd ..

@ -1,6 +0,0 @@
mkdir llvm64d
cd llvm64d
rem cmake -DCMAKE_INSTALL_PREFIX=c:\ravi64llvmd -G "Visual Studio 14 Win64" -DLLVM_JIT=ON -DLLVM_DIR=c:\LLVM37debug\share\llvm\cmake ..
rem cmake -DSTATIC_BUILD=ON -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=c:\d\ravi64llvmd -G "Visual Studio 15 2017 Win64" -DLLVM_JIT=ON -DEMBEDDED_DMRC=ON -DLLVM_DIR=c:\d\LLVM39D64\lib\cmake\llvm ..
cmake -DCMAKE_INSTALL_PREFIX=c:\Software\ravi -G "Visual Studio 15 2017 Win64" -DCMAKE_BUILD_TYPE=Debug -DLLVM_JIT=ON -DLLVM_DIR=c:\Software\llvm501d\lib\cmake\llvm ..
cd ..

@ -1,7 +0,0 @@
mkdir llvm64
cd llvm64
rem pre LLVM 3.9
rem cmake -DCMAKE_INSTALL_PREFIX=c:\ravi -G "Visual Studio 14 Win64" -DLLVM_JIT=ON -DLLVM_DIR=c:\LLVM37\share\llvm\cmake ..
rem cmake -DCMAKE_INSTALL_PREFIX=c:\ravi -G "Visual Studio 15 2017 Win64" -DLLVM_JIT=ON -DLLVM_DIR=c:\d\LLVM40_64\lib\cmake\llvm ..
cmake -DCMAKE_INSTALL_PREFIX=c:\Software\ravi -G "Visual Studio 15 2017 Win64" -DSTATIC_BUILD=ON -DLLVM_JIT=ON -DLLVM_DIR=c:\Software\llvm601r\lib\cmake\llvm ..
cd ..

@ -1,5 +0,0 @@
rmdir /s llvm8
mkdir llvm8
cd llvm8
cmake -DCMAKE_INSTALL_PREFIX=c:\Software\ravi -G "Visual Studio 15 2017 Win64" -DCMAKE_BUILD_TYPE=Release -DLLVM_JIT=ON -DLLVM_DIR=c:\Software\llvm801\lib\cmake\llvm ..
cd ..

@ -1,5 +0,0 @@
rmdir /s llvm10d
mkdir llvm10d
cd llvm10d
cmake -DCMAKE_INSTALL_PREFIX=c:\Software\ravi -G "Visual Studio 16 2019" -DLLVM_JIT=ON -DLLVM_DIR=c:\Software\llvm10d\lib\cmake\llvm ..
cd ..

@ -1,6 +0,0 @@
rmdir /s llvm9r
mkdir llvm9r
cd llvm9r
rem cmake -DCMAKE_INSTALL_PREFIX=c:\Software\ravi -G "Visual Studio 15 2017 Win64" -DLLVM_JIT=ON -DLLVM_DIR=c:\Software\llvm900r\lib\cmake\llvm ..
cmake -DCMAKE_INSTALL_PREFIX=c:\Software\ravi -G "Visual Studio 16 2019" -DLLVM_JIT=ON -DLLVM_DIR=c:\Software\llvm900r\lib\cmake\llvm ..
cd ..

@ -1,4 +0,0 @@
mkdir omrjit
cd omrjit
cmake -DCMAKE_INSTALL_PREFIX=c:\Software\ravi -G "Visual Studio 15 2017 Win64" -DCMAKE_BUILD_TYPE=Debug -DOMR_JIT=ON ..
cd ..

@ -1,4 +0,0 @@
mkdir xcodellvm
cd xcodellvm
#cmake -DCMAKE_BUILD_TYPE=Debug -G Xcode -DLLVM_JIT=ON -DCMAKE_INSTALL_PREFIX=$HOME/ravi -DLLVM_DIR=$HOME/LLVM/share/llvm/cmake ..
cmake -DCMAKE_BUILD_TYPE=Debug -G Xcode -DLLVM_JIT=ON -DCMAKE_INSTALL_PREFIX=$HOME/ravi -DLLVM_DIR=$HOME/LLVM5/lib/cmake/llvm ..

@ -1,5 +0,0 @@
mkdir buildllvmd
cd buildllvmd
#cmake -DCMAKE_BUILD_TYPE=Debug -DLLVM_JIT=ON -DCMAKE_INSTALL_PREFIX=$HOME/ravi -DLLVM_DIR=$HOME/LLVM/share/llvm/cmake ..
#cmake -DCMAKE_BUILD_TYPE=Debug -DLLVM_JIT=ON -DLTESTS=ON -DCMAKE_INSTALL_PREFIX=$HOME/ravi -DLLVM_DIR=$HOME/LLVM5/lib/cmake/llvm ..
cmake -DCMAKE_BUILD_TYPE=Debug -DLLVM_JIT=ON -DLTESTS=ON -DCMAKE_INSTALL_PREFIX=$HOME/ravillvm -DLLVM_DIR=$HOME/Software/llvm600/lib/cmake/llvm ..

@ -1,7 +0,0 @@
rm -rf buildllvm
mkdir buildllvm
cd buildllvm
#cmake -DCMAKE_BUILD_TYPE=Release -DLLVM_JIT=ON -DCMAKE_INSTALL_PREFIX=$HOME/ravi -DLLVM_DIR=$HOME/LLVM/share/llvm/cmake ..
#cmake -DCMAKE_BUILD_TYPE=Release -DLLVM_JIT=ON -DCMAKE_INSTALL_PREFIX=$HOME/ravi -DLLVM_DIR=$HOME/LLVM5/lib/cmake/llvm ..
#cmake -DCMAKE_BUILD_TYPE=Release -DSTATIC_BUILD=ON -DLLVM_JIT=ON -DCMAKE_INSTALL_PREFIX=$HOME/Software/ravi -DLLVM_DIR=$HOME/Software/llvm801/lib/cmake/llvm ..
cmake3 -DCMAKE_BUILD_TYPE=Release -DSTATIC_BUILD=ON -DLLVM_JIT=ON -DCMAKE_INSTALL_PREFIX=$HOME/Software/ravi -DLLVM_DIR=$HOME/Software/llvm10/lib/cmake/llvm ..

@ -1,3 +0,0 @@
mkdir omrjit
cd omrjit
cmake -DCMAKE_BUILD_TYPE=Release -DOMR_JIT=ON -DCMAKE_INSTALL_PREFIX=$HOME/ravi ..

@ -12,20 +12,15 @@ RUN set -x \
&& cd /Software \ && cd /Software \
&& tar xvf "cmake-3.14.5-Linux-x86_64.tar.gz" \ && tar xvf "cmake-3.14.5-Linux-x86_64.tar.gz" \
&& rm -rf "/Software/cmake-3.14.5-Linux-x86_64.tar.gz" \ && rm -rf "/Software/cmake-3.14.5-Linux-x86_64.tar.gz" \
&& wget -O "/Software/clang+llvm-8.0.0-x86_64-linux-gnu-ubuntu-18.04.tar.xz" "http://releases.llvm.org/8.0.0/clang+llvm-8.0.0-x86_64-linux-gnu-ubuntu-18.04.tar.xz" \
&& cd /Software \
&& tar xvf "clang+llvm-8.0.0-x86_64-linux-gnu-ubuntu-18.04.tar.xz" \
&& rm -rf "/Software/clang+llvm-8.0.0-x86_64-linux-gnu-ubuntu-18.04.tar.xz" \
&& mkdir -p /sources \ && mkdir -p /sources \
&& cd /sources \ && cd /sources \
&& git clone https://github.com/dibyendumajumdar/ravi.git \ && git clone https://github.com/dibyendumajumdar/ravi.git \
&& cd /sources/ravi \ && cd /sources/ravi \
&& mkdir build \ && mkdir build \
&& cd build \ && cd build \
&& /Software/cmake-3.14.5-Linux-x86_64/bin/cmake -DSTATIC_BUILD=ON -DCMAKE_INSTALL_PREFIX=/Software/ravi -DLLVM_JIT=ON -DCMAKE_BUILD_TYPE=Release -DLLVM_DIR=/Software/clang+llvm-8.0.0-x86_64-linux-gnu-ubuntu-18.04/lib/cmake/llvm .. \ && /Software/cmake-3.14.5-Linux-x86_64/bin/cmake -DCMAKE_INSTALL_PREFIX=/Software/ravi -DCMAKE_BUILD_TYPE=Release .. \
&& make install \ && make install \
&& rm -rf /Software/cmake-3.14.5-Linux-x86_64 \ && rm -rf /Software/cmake-3.14.5-Linux-x86_64 \
&& rm -rf /Software/clang+llvm-8.0.0-x86_64-linux-gnu-ubuntu-18.04 \
&& rm -rf /sources \ && rm -rf /sources \
&& apt-get autoremove \ && apt-get autoremove \
&& apt-get remove -y --purge git wget build-essential \ && apt-get remove -y --purge git wget build-essential \

@ -1,121 +0,0 @@
/******************************************************************************
* Copyright (C) 2015 Dibyendu Majumdar
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
******************************************************************************/
#ifndef RAVI_LLVM_H
#define RAVI_LLVM_H
#ifdef USE_LLVM
#include "llvm/Config/llvm-config.h"
#if (LLVM_VERSION_MAJOR < 3 || LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR < 5 || LLVM_VERSION_MAJOR == 7)
#error Unsupported LLVM version
#endif
#if LLVM_VERSION_MAJOR >= 5
#define USE_ORC_JIT 1
#else
#define USE_ORC_JIT 0
#endif
#if LLVM_VERSION_MAJOR >= 8 && !defined(_WIN32)
#define USE_ORCv2_JIT 0
#else
#define USE_ORCv2_JIT 0
#endif
#if LLVM_VERSION_MAJOR >= 10
#undef USE_ORCv2_JIT
#define USE_ORCv2_JIT 1
#endif
// In lua.c we include this just to get version numbers
// We cannot have C++ headers in that case
#ifdef __cplusplus
#include "llvm/ADT/Triple.h"
#include "llvm/Analysis/Passes.h"
#include "llvm/ExecutionEngine/ExecutionEngine.h"
#include "llvm/ExecutionEngine/MCJIT.h"
#include "llvm/ExecutionEngine/SectionMemoryManager.h"
#include "llvm/ExecutionEngine/GenericValue.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/Intrinsics.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/MDBuilder.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Verifier.h"
#include "llvm/IR/Metadata.h"
#if LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR < 7
#include "llvm/PassManager.h"
#else
#include "llvm/IR/LegacyPassManager.h"
#endif
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
#include "llvm/Transforms/Instrumentation.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/Host.h"
#include "llvm/Support/DynamicLibrary.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Transforms/Scalar.h"
#include "llvm/Support/FormattedStream.h"
#if USE_ORC_JIT || USE_ORCv2_JIT
#include "llvm/ADT/STLExtras.h"
#include "llvm/ExecutionEngine/JITSymbol.h"
#include "llvm/ExecutionEngine/RTDyldMemoryManager.h"
#include "llvm/ExecutionEngine/Orc/CompileUtils.h"
#include "llvm/ExecutionEngine/Orc/IndirectionUtils.h"
#include "llvm/ExecutionEngine/Orc/IRCompileLayer.h"
#include "llvm/ExecutionEngine/Orc/IRTransformLayer.h"
#include "llvm/ExecutionEngine/Orc/LambdaResolver.h"
#include "llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h"
#include "llvm/ExecutionEngine/Orc/CompileOnDemandLayer.h"
#include "llvm/ExecutionEngine/Orc/CompileUtils.h"
#include "llvm/IR/Mangler.h"
#include "llvm/Support/Error.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Transforms/Scalar/GVN.h"
#if LLVM_VERSION_MAJOR >= 8
#include "llvm/ExecutionEngine/Orc/JITTargetMachineBuilder.h"
#include "llvm/ExecutionEngine/Orc/Legacy.h"
#include "llvm/ExecutionEngine/Orc/ExecutionUtils.h"
#endif
#endif
#include <algorithm>
#include <cassert>
#include <cstdlib>
#include <memory>
#include <string>
#include <cstdio>
#include <vector>
#endif //__cplusplus
#endif //USE_LLVM
#endif

File diff suppressed because it is too large Load Diff

@ -14,16 +14,11 @@ Contents:
ravi-overview ravi-overview
ravi-reference ravi-reference
ravi-mir-instructions ravi-mir-instructions
ravi-llvm-instructions
lua-introduction lua-introduction
lua_bytecode_reference lua_bytecode_reference
lua-parser lua-parser
ravi-internals ravi-internals
ravi-jit-compilation-hook ravi-jit-compilation-hook
ravi-lua-types
llvm-tbaa
ravi-benchmarks
ravi-jit-status
Indices and tables Indices and tables

@ -1,169 +0,0 @@
LLVM Notes
==========
The notes below apply to LLVM 3.5.1 unless noted otherwise. All reflect my understanding - so if anything here is incorrect please log an issue and I will correct.
Structs and Unions
------------------
LLVM does not support defining union types so we need to basically use a ``struct`` of appropriate size and cast it as we need. The main thing to be careful about is to ensure that the ``struct`` is of the same size as the ``union``.
An example is::
///*
//** Information about a call.
//** When a thread yields, 'func' is adjusted to pretend that the
//** top function has only the yielded values in its stack; in that
//** case, the actual 'func' value is saved in field 'extra'.
//** When a function calls another with a continuation, 'extra' keeps
//** the function index so that, in case of errors, the continuation
//** function can be called with the correct top.
//*/
// typedef struct CallInfo {
// StkId func; /* function index in the stack */
// StkId top; /* top for this function */
// struct CallInfo *previous, *next; /* dynamic call link */
// union {
// struct { /* only for Lua functions */
// StkId base; /* base for this function */
// const Instruction *savedpc;
// } l;
// struct { /* only for C functions */
// lua_KFunction k; /* continuation in case of yields */
// ptrdiff_t old_errfunc;
// lua_KContext ctx; /* context info. in case of yields */
// } c;
// } u;
// ptrdiff_t extra;
// short nresults; /* expected number of results from this function */
// lu_byte callstatus;
//} CallInfo;
Above the union ``u`` has two members of unequal size. To handle this I created the following two sub-types of equal size - note the extra dummy field in the first type::
elements.clear();
elements.push_back(StkIdT); /* base */
elements.push_back(pInstructionT); /* savedpc */
elements.push_back(
C_ptrdiff_t); /* dummy to make this same size as the other member */
CallInfo_lT = llvm::StructType::create(elements);
elements.clear();
elements.push_back(plua_KFunctionT); /* k */
elements.push_back(C_ptrdiff_t); /* old_errfunc */
elements.push_back(lua_KContextT); /* ctx */
CallInfo_cT = llvm::StructType::create(elements);
Then as I intend to use the ``u.l`` field more often, I used the following definition for ``CallInfo``::
CallInfoT = llvm::StructType::create(context, "ravi.CallInfo");
pCallInfoT = llvm::PointerType::get(CallInfoT, 0);
elements.clear();
elements.push_back(StkIdT); /* func */
elements.push_back(StkIdT); /* top */
elements.push_back(pCallInfoT); /* previous */
elements.push_back(pCallInfoT); /* next */
elements.push_back(
CallInfo_lT); /* u.l - as we will typically access the lua call details
*/
elements.push_back(C_ptrdiff_t); /* extra */
elements.push_back(llvm::Type::getInt16Ty(context)); /* nresults */
elements.push_back(lu_byteT); /* callstatus */
CallInfoT->setBody(elements);
JIT Compilation Error on Windows
--------------------------------
Note: The latest LLVM version from LLVM source repository appears to support COEFF format. So below
applies to version 3.6.x and 3.5.x.
On Windows when we attempt to JIT compile we get an error saying incompatible object format
Reading posts on mailing lists I found that the issue is that COEFF
format is not supported and therefore we need to set -elf as the object
format::
#include "llvm/Support/Host.h"
/* some code */
#ifdef _WIN32
auto triple = llvm::sys::getProcessTriple();
module->setTargetTriple(triple + "-elf");
#endif
Memory Management
-----------------
It appears that most things in LLVM are owned by the parent object and when the parent object is deleted the children go too. So in my code the main objects I delete are the ``ExecutionEngine`` and ``Module``. Once a module is associated with an engine then only the engine needs to be explicitly deleted is my understanding.
It doesn't help that the tutorial available does not attempt to delete objects / release memory!
MCJIT Engines, Modules and Functions
------------------------------------
Functions live inside Modules but once a Module is finalized (compiled) then no further functions can be added to it. Although an ``MCJIT`` instance (engine) can support multiples modules, the recommendation is to ensure each module is assigned its own engine. The rationale for this is not explained.
Struct Assign
-------------
My understanding is that to perform assignment of a struct value, one must call the intrinsic ``memcpy`` function. Example of code that does this::
llvm::Value *src;
llvm::Value *dest;
// First get the declaration for the inttrinsic memcpy
llvm::SmallVector<llvm::Type *, 3> vec;
vec.push_back(def->types->C_pcharT); /* i8 */
vec.push_back(def->types->C_pcharT); /* i8 */
vec.push_back(def->types->C_intT);
llvm::Function *f = llvm::Intrinsic::getDeclaration(
def->raviF->module(), llvm::Intrinsic::memcpy, vec);
lua_assert(f);
// Cast src and dest to i8*
llvm::Value *dest_ptr =
def->builder->CreateBitCast(dest, def->types->C_pcharT);
llvm::Value *src_ptr = def->builder->CreateBitCast(src, def->types->C_pcharT);
// Create call to intrinsic memcpy
values_.clear();
values_.push_back(dest_ptr);
values_.push_back(src_ptr);
values_.push_back(llvm::ConstantInt::get(def->types->C_intT, sizeof(TValue)));
values_.push_back(
llvm::ConstantInt::get(def->types->C_intT, sizeof(L_Umaxalign)));
values_.push_back(def->types->kFalse);
def->builder->CreateCall(f, values_);
Note that the call to memcpy supply an alignment.
Accessing ``extern`` functions from JIT compiled code
-----------------------------------------------------
If the JITed function needs to access ``extern`` functions that are statically linked and not exported as dynamic symbols (e.g. in Visual C++) then we need some extra steps.
From reading posts on the subject it appears that the way to do this is to add a global mapping in the ``ExecutionEngine`` by calling the
``addGlobalMapping()`` method. However this doesn't work with MCJIT due to a bug! So we need to use a workaround. Apparently there are two
solutions:
* Create a custom memory manager that resolves the ``extern`` functions.
* Add the symbol to the global symbols by calling ``llvm::sys::DynamicLibrary::AddSymbol()``.
I am using the latter approach for now.
GEP instruction
---------------
The GEP instruction cannot compute addresses of fields in a pointer member - as the pointer needs to be 'loaded' first. This is explained in the `GEP FAQ <http://llvm.org/docs/GetElementPtr.html#id6>`_.
Hooking up Optimization Passes
------------------------------
The LLVM documentation does not provide guidance on how the optimization passes should be hooked up. There are descriptions of what the passes do, but if you are new to LLVM and trying to work out which passes to use and in what order, then there is not much help available. The `Kaleidoscope Sample <http://www.llvm.org/docs/tutorial/LangImpl4.html>`_ shows a small example of how optimization passes may be hooked up.
Fortunately it seems that there is a `PassManagerBuilder <http://llvm.org/docs/doxygen/html/classllvm_1_1PassManagerBuilder.html>`_ component that allows easy setup of the standard passes for a C like language. Unfortunately there isn't much guidance on how to use this either. The best source of information I found was an example toy compiler by `David Chisnall <http://cs.swan.ac.uk/~csdavec/FOSDEM12/compiler.cc.html>`_.
Links
-----
* `Mapping High Level Constructs to LLVM IR <http://llvm.lyngvig.org/Articles/Mapping-High-Level-Constructs-to-LLVM-IR>`_
* `IRBuilder Sample <https://github.com/eliben/llvm-clang-samples/blob/master/src_llvm/experimental/build_llvm_ir.cpp>`_
* `Using MCJIT with Kaleidoscope <http://blog.llvm.org/2013/07/using-mcjit-with-kaleidoscope-tutorial.html>`_
* `Object format issue on Windows <http://lists.cs.uiuc.edu/pipermail/llvmdev/2013-December/068407.html>`_
* `ExecutionEngine::addGlobalMapping() bug in MCJIT <http://llvm.org/bugs/show_bug.cgi?id=20656>`_
* `LLVM Notes <http://nondot.org/sabre/LLVMNotes/>`_
* `Implementing Domain-Specific Languages with LLVM <http://cs.swan.ac.uk/~csdavec/FOSDEM12/DSLsWithLLVM.pdf>`_.

@ -1,167 +0,0 @@
LLVM Type Based Alias Analysis
==============================
When a Lua opcode involves a call to a Lua function, the Lua stack may be reallocated. So then the base pointer which points to the function's
base stack position must be refreshed.
To keep compilation simple I coded the compiler so that at the beginning of each opcode the base pointer is reloaded. My assumption was that
the LLVM optimizer will realise that the base pointer hasn't changed and so the loads are redundant and can be removed. However to my surprise I found that this
is not the case.
The main difference between the IR I was generating and that produced by Clang was that Clang generated IR appeared to be decorated
by tbaa metadata. Example::
%base2 = getelementptr inbounds %struct.CallInfoLua* %0, i32 0, i32 4, i32 0
%1 = load %struct.TValue** %base2, align 4, !tbaa !12
Here the ``!tbaa !12`` refers to a tbaa metadata entry.
I won't show the Clang generated tbaa metadata here, but here is how I added similar support in Ravi. The required steps are:
1. Create tbaa metadata mappings for the types in the system.
2. Annotate Load and Store instructions with tbaa references.
Creating TBAA Metadata
----------------------
Firstly you need an MDBuilder instance. So you need to include following headers::
#include "llvm/IR/MDBuilder.h"
#include "llvm/IR/Metadata.h"
We can create an MDBuilder instance like this::
llvm::MDBuilder mdbuilder(llvm::getGlobalContext());
The TBAA nodes hang off a root node. So we create that next::
llvm::MDNode *tbaa_root;
// Do what Clang does
tbaa_root = mdbuilder.createTBAARoot("Simple C / C++ TBAA");
Next we need to create some simple scalar types. We only need one type per size, so that means we don't need
``long long`` and ``double`` - either one will do. We create these scalar types as follows::
llvm::MDNode *tbaa_charT;
llvm::MDNode *tbaa_shortT;
llvm::MDNode *tbaa_intT;
llvm::MDNode *tbaa_longlongT;
llvm::MDNode *tbaa_pointerT;
//!4 = metadata !{metadata !"omnipotent char", metadata !5, i64 0}
tbaa_charT = mdbuilder.createTBAAScalarTypeNode("omnipotent char", tbaa_root, 0);
//!3 = metadata !{metadata !"any pointer", metadata !4, i64 0}
tbaa_pointerT = mdbuilder.createTBAAScalarTypeNode("any pointer", tbaa_charT, 0);
//!10 = metadata !{metadata !"short", metadata !4, i64 0}
tbaa_shortT = mdbuilder.createTBAAScalarTypeNode("short", tbaa_charT, 0);
//!11 = metadata !{metadata !"int", metadata !4, i64 0}
tbaa_intT = mdbuilder.createTBAAScalarTypeNode("int", tbaa_charT, 0);
//!9 = metadata !{metadata !"long long", metadata !4, i64 0}
tbaa_longlongT = mdbuilder.createTBAAScalarTypeNode("long long", tbaa_charT, 0);
The second argument to ``createTBAAScalarTypeNode()`` is the parent node. Note the hierarchy here::
+ root
|
+--+ char
|
+--+-- any pointer
|
+-- short
|
+-- int
|
+-- long long
This is how Clang has it defined.
Next we need to define aggregate (struct) types. The API we need for this is ``createTBAAStructTypeNode()``.
This method accepts a vector of ``std::pair<llvm::MDNode *, uint64_t>`` objects - each element in the vector defines
a field in the struct. The integer parameter needs to be the offset of the field within the struct. Interestingly
Clang generates offsets that indicate pointers are being treated as 32-bit quantities - even though I ran this on
a 64-bit machine. So I guess that as long as we consistently use the size then this doesn't matter. The sizes used
by Clang are:
* char - 1 byte
* short - 2 bytes
* int - 4 bytes
* pointer - 4 bytes
* long long - 8 bytes
Another interesting thing is that padding needs to be accounted for.
So now lets look at how to map following struct::
struct CallInfoL { /* only for Lua functions */
struct TValue *base; /* base for this function */
const unsigned int *savedpc;
ptrdiff_t dummy;
};
We map this as::
llvm::MDNode *tbaa_CallInfo_lT;
//!14 = metadata !{metadata !"CallInfoL", metadata !3, i64 0, metadata !3, i64 4, metadata !9, i64 8}
std::vector<std::pair<llvm::MDNode *, uint64_t> > nodes;
nodes.push_back(std::pair<llvm::MDNode*, uint64_t>(tbaa_pointerT, 0));
nodes.push_back(std::pair<llvm::MDNode*, uint64_t>(tbaa_pointerT, 4));
nodes.push_back(std::pair<llvm::MDNode*, uint64_t>(tbaa_longlongT, 8));
tbaa_CallInfo_lT = mdbuilder.createTBAAStructTypeNode("CallInfo_l", nodes);
To illustrate how a structure is referenced as a field in another lets also look at::
struct CallInfo {
struct TValue *func; /* function index in the stack */
struct TValue *top; /* top for this function */
struct CallInfo *previous, *next; /* dynamic call link */
struct CallInfoL l;
ptrdiff_t extra;
short nresults; /* expected number of results from this function */
unsigned char callstatus;
};
We have a ``CallInfoL`` as the type of a field within the struct. Therefore::
llvm::MDNode *tbaa_CallInfoT;
//!13 = metadata !{metadata !"CallInfo",
// metadata !3, i64 0, metadata !3, i64 4, metadata !3, i64 8,
// metadata !3, i64 12, metadata !14, i64 16, metadata !9, i64 32,
// metadata !10, i64 40, metadata !4, i64 42}
nodes.clear();
nodes.push_back(std::pair<llvm::MDNode*, uint64_t>(tbaa_pointerT, 0));
nodes.push_back(std::pair<llvm::MDNode*, uint64_t>(tbaa_pointerT, 4));
nodes.push_back(std::pair<llvm::MDNode*, uint64_t>(tbaa_pointerT, 8));
nodes.push_back(std::pair<llvm::MDNode*, uint64_t>(tbaa_pointerT, 12));
nodes.push_back(std::pair<llvm::MDNode*, uint64_t>(tbaa_CallInfo_lT, 16));
nodes.push_back(std::pair<llvm::MDNode*, uint64_t>(tbaa_longlongT, 32));
nodes.push_back(std::pair<llvm::MDNode*, uint64_t>(tbaa_shortT, 40));
nodes.push_back(std::pair<llvm::MDNode*, uint64_t>(tbaa_charT, 42));
tbaa_CallInfoT = mdbuilder.createTBAAStructTypeNode("CallInfo", nodes);
Decorating Load and Store instructions
--------------------------------------
So now we have created TBAA metadata for two struct types.
Next we need to see how we use these in Load and Store instructions. Lets assume we need to load the pointer
stored in ``Callinfo.top``. In order to decorate the Load instruction with tbaa we need to create
a Struct Tag Node - which is like a path node. Here it is::
llvm::MDNode *tbaa_CallInfo_topT;
tbaa_CallInfo_topT = mdbuilder.createTBAAStructTagNode(tbaa_CallInfoT, tbaa_pointerT, 4);
Above is saying that the field ``top`` in struct ``CallInfo`` is a pointer at offset 4.
Armed with this we can code::
llvm::Value *callinfo_top = /* GEP instruction */
llvm::Instruction *top = Builder.CreateLoad(callinfo_top);
top->setMetadata(llvm::LLVMContext::MD_tbaa, tbaa_CallInfo_topT);
Links
-----
* `TypeBasedAliasAnalysis code <http://llvm.org/docs/doxygen/html/TypeBasedAliasAnalysis_8cpp_source.html>`_.
* `IR documentation on tbaa metadata <http://llvm.org/docs/LangRef.html#tbaa-metadata>`_.
* `Embedded metadata <http://nondot.org/sabre/LLVMNotes/EmbeddedMetadata.txt>`_.

@ -1,222 +0,0 @@
LLVM First Steps
================
Note that the discussion below is for LLVM 3.5.1.
Although there appears to be a lot of documentation in the LLVM site surprisingly some basic information is hard to find.
The main source of guidance for creating a JIT is in the example toy language
`Kaleidoscope <https://github.com/llvm-mirror/llvm/blob/master/examples/Kaleidoscope/MCJIT/complete/toy.cpp>`_. But here too
there are several versions - so you have to pick the right version that is compatible with the LLVM version you
are using.
A Lua JITed function will execute in the context of Lua. So it needs to be able to access the ``lua_State`` and its various
structures. So I wanted a sample that demonstrates passing a pointer to a structure and accessing it within the JITed
function.
The initial test program I created is meant to be a "hello world" type test but covering the functionility described above.
The test I want to run is::
// Creating a function that takes pointer to struct as argument
// The function gets value from one of the fields in the struct
// And returns it
// The equivalent C program is:
//
// extern int printf(const char *, ...);
//
// struct GCObject {
// struct GCObject *next;
// unsigned char a;
// unsigned char b;
// };
//
// int testfunc(struct GCObject *obj) {
// printf("value = %d\n", obj->a);
// return obj->a;
// }
You can view the test program at `test_llvm.cpp <https://github.com/dibyendumajumdar/ravi/blob/master/tests/test_llvm.cpp>`_.
It is also reproduced below.
I used the new ``MCJIT`` engine in my test. It seems that this engine compiles modules rather than individual
functions - and once compiled a module cannot be modified. So in the Lua context we need to create a new module
everytime we JIT compile a function - or alternatively we JIT compile a whole Lua source file including all its
functions into a single module.
I found the blog post `Using MCJIT with Kaleidoscope
<http://blog.llvm.org/2013/07/using-mcjit-with-kaleidoscope-tutorial.html>`_ useful in understanding some
finer points about using ``MCJIT``.
The Lua GCObject structure in lobject.h we need is::
typedef struct RaviGCObject {
struct RaviGCObject *next;
unsigned char b1;
unsigned char b2;
} RaviGCObject;
Our prototype for the JITted function::
typedef int (*myfunc_t)(RaviGCObject *);
Get global context - not sure what the impact is of sharing::
llvm::LLVMContext &context = llvm::getGlobalContext();
Module is the translation unit::
std::unique_ptr<llvm::Module> theModule =
std::unique_ptr<llvm::Module>(new llvm::Module("ravi", context));
llvm::Module *module = theModule.get();
llvm::IRBuilder<> builder(context);
On Windows we get error saying incompatible object format
Reading posts on mailining lists I found that the issue is that COEFF
format is not supported and therefore we need to set -elf as the object
format::
#ifdef _WIN32
auto triple = llvm::sys::getProcessTriple();
module->setTargetTriple(triple + "-elf");
#endif
create a GCObject structure as defined in lobject.h::
llvm::StructType *structType =
llvm::StructType::create(context, "RaviGCObject");
llvm::PointerType *pstructType =
llvm::PointerType::get(structType, 0); // pointer to RaviGCObject
std::vector<llvm::Type *> elements;
elements.push_back(pstructType);
elements.push_back(llvm::Type::getInt8Ty(context));
elements.push_back(llvm::Type::getInt8Ty(context));
structType->setBody(elements);
structType->dump();
Create printf declaration::
std::vector<llvm::Type *> args;
args.push_back(llvm::Type::getInt8PtrTy(context));
// accepts a char*, is vararg, and returns int
llvm::FunctionType *printfType =
llvm::FunctionType::get(builder.getInt32Ty(), args, true);
llvm::Constant *printfFunc =
module->getOrInsertFunction("printf", printfType);
Create the testfunc()::
args.clear();
args.push_back(pstructType);
llvm::FunctionType *funcType =
llvm::FunctionType::get(builder.getInt32Ty(), args, false);
llvm::Function *mainFunc = llvm::Function::Create(
funcType, llvm::Function::ExternalLinkage, "testfunc", module);
llvm::BasicBlock *entry =
llvm::BasicBlock::Create(context, "entrypoint", mainFunc);
builder.SetInsertPoint(entry);
The printf format string::
llvm::Value *formatStr = builder.CreateGlobalStringPtr("value = %d\n");
Get the first argument which is RaviGCObject *::
auto argiter = mainFunc->arg_begin();
llvm::Value *arg1 = argiter++;
arg1->setName("obj");
Now we need a GEP for the second field in RaviGCObject::
std::vector<llvm::Value *> values;
llvm::APInt zero(32, 0);
llvm::APInt one(32, 1);
// This is the array offset into RaviGCObject*
values.push_back(
llvm::Constant::getIntegerValue(llvm::Type::getInt32Ty(context), zero));
// This is the field offset
values.push_back(
llvm::Constant::getIntegerValue(llvm::Type::getInt32Ty(context), one));
Create the GEP value::
llvm::Value *arg1_a = builder.CreateGEP(arg1, values, "ptr");
Now retrieve the data from the pointer address::
llvm::Value *tmp1 = builder.CreateLoad(arg1_a, "a");
As the retrieved value is a byte - convert to int i::
llvm::Value *tmp2 =
builder.CreateZExt(tmp1, llvm::Type::getInt32Ty(context), "i");
Call the printf function::
values.clear();
values.push_back(formatStr);
values.push_back(tmp2);
builder.CreateCall(printfFunc, values);
return i::
builder.CreateRet(tmp2);
module->dump();
Lets create the MCJIT engine::
std::string errStr;
auto engine = llvm::EngineBuilder(module)
.setErrorStr(&errStr)
.setEngineKind(llvm::EngineKind::JIT)
.setUseMCJIT(true)
.create();
if (!engine) {
llvm::errs() << "Failed to construct MCJIT ExecutionEngine: " << errStr
<< "\n";
return 1;
}
Now lets compile our function into machine code::
std::string funcname = "testfunc";
myfunc_t funcptr = (myfunc_t)engine->getFunctionAddress(funcname);
if (funcptr == nullptr) {
llvm::errs() << "Failed to obtain compiled function\n";
return 1;
}
Run the function and test results::
RaviGCObject obj = {NULL, 42, 65};
int ans = funcptr(&obj);
printf("The answer is %d\n", ans);
return ans == 42 ? 0 : 1;
Accessing ``extern`` functions from JIT compiled code
-----------------------------------------------------
The JITed function needs to access ``extern`` Lua functions. We need a way to map these to make these visible to the JITed code. Simply declaring
the functions ``extern`` only appears to work if the functios are available as exported symbols in dynamic libraries, e.g. the call to
``printf`` above.
From reading posts on the subject it appears that the way to do this is to add a global mapping in the ``ExecutionEngine`` by calling the
``addGlobalMapping()`` method. However this doesn't work with MCJIT due to a bug! So we need to use a workaround. Apparently there are two
solutions:
* Create a custom memory manager that resolves the ``extern`` functions.
* Add the symbol to the global symbols by calling ``llvm::sys::DynamicLibrary::AddSymbol()``.
I am using the latter approach for now.
Memory Management in LLVM
-------------------------
Curiously LLVM docs do not say much about how memory should be managed. I am still trying to figure this out, but in general it seems that there is
hierarchy of ownership. Example: ``ExecutionEngine`` owns the ``Module``. By deleting the parent the 'owned' objects are automatically
deleted.
Links
-----
* `Object format issue on Windows <http://lists.cs.uiuc.edu/pipermail/llvmdev/2013-December/068407.html>`_
* `ExecutionEngine::addGlobalMapping() bug in MCJIT <http://llvm.org/bugs/show_bug.cgi?id=20656>`_
* `LLVM Notes <http://nondot.org/sabre/LLVMNotes/>`_

@ -1,160 +0,0 @@
===================================
Building Ravi with LLVM JIT backend
===================================
.. contents:: Table of Contents
:depth: 2
:backlinks: top
Quick build without JIT
=======================
A Makefile is supplied for a simple build without the JIT on Unix platforms. Just run ``make`` and follow instructions. You may need to customize the Makefiles.
For building Ravi with JIT options please read on.
Build Dependencies
==================
* `CMake <https://cmake.org/>`_ is required
* On Windows you will need Visual Studio 2017 Community edition
* LLVM versions >= 3.5
LLVM JIT Backend
================
Following versions of LLVM work with Ravi.
* LLVM 3.7, 3.8, 3.9, 4.0, 5.0, 6.0, 8.0.1, 9.0.1, 10.0
* LLVM 7.0 was skipped because of unstable ORC api changes
* LLVM 3.5 and 3.6 should also work but have not been recently tested
Unless otherwise noted the instructions below should work for LLVM 3.9 and later.
Since LLVM 5.0 Ravi has begun to use the new ORC JIT apis. These apis are more memory efficient
compared to the MCJIT apis because they release the Module IR as early as possible, whereas with
MCJIT the Module IR hangs around as long as the compiled code is held. Because of this significant
improvement, I recommend using the latest version of LLVM.
Building LLVM on Windows
------------------------
I built LLVM from source. I used the following sequence from the VS2017 command window::
mkdir build
cd build
cmake -DCMAKE_INSTALL_PREFIX=c:\Software\llvm801 -DLLVM_TARGETS_TO_BUILD="X86" -G "Visual Studio 15 2017 Win64" ..
I then opened the generated solution in VS2017 and performed a INSTALL build from there. Above will build the 64-bit version of LLVM libraries. To build a 32-bit version omit the ``Win64`` parameter.
.. note:: Note that if you perform a **Release** build of LLVM then you will also need to do a **Release** build of Ravi otherwise you will get link errors. Ditto for **Debug** builds.
Building LLVM on Ubuntu or Redhat
---------------------------------
The approach is similar to that described for MAC OS X below.
Building LLVM on MAC OS X
-------------------------
I am using Max OSX Mojave. Pre-requisites are XCode and CMake.
Ensure cmake is on the path.
Assuming that LLVM source has been extracted to ``$HOME/llvm-8.0.1.src`` I follow these steps::
cd $HOME/llvm-8.0.1.src
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=$HOME/Software//llvm801 -DLLVM_TARGETS_TO_BUILD="X86" ..
make install
Building Ravi with LLVM JIT backend enabled
-------------------------------------------
I am developing Ravi using Visual Studio 2017 Community Edition on Windows 10 64bit, gcc on Linux 64-bit, and clang/Xcode on MAC OS X. I was also able to successfully build a Ubuntu version on Windows 10 using the newly released Ubuntu/Linux sub-system for Windows 10.
.. note:: Location of cmake files prior to LLVM 3.9 was ``$LLVM_INSTALL_DIR/share/llvm/cmake``.
Assuming that LLVM has been installed as described above, then on Windows I invoke the cmake config as follows::
cd build
cmake -DLLVM_JIT=ON -DCMAKE_INSTALL_PREFIX=c:\Software\ravi -DLLVM_DIR=c:\Software\llvm801\lib\cmake\llvm -G "Visual Studio 15 2017 Win64" ..
I then open the solution in VS2017 and do a build from there.
On Ubuntu I use::
cd build
cmake -DLLVM_JIT=ON -DCMAKE_INSTALL_PREFIX=$HOME/Software/ravi -DLLVM_DIR=$HOME/Software/llvm801/lib/cmake/llvm -DCMAKE_BUILD_TYPE=Release -G "Unix Makefiles" ..
make install
Note that on a clean install of Ubuntu 15.10 I had to install following packages:
* cmake
* git
* libreadline-dev
On MAC OS X I use::
cd build
cmake -DLLVM_JIT=ON -DCMAKE_INSTALL_PREFIX=$HOME/Software/ravi -DLLVM_DIR=$HOME/Software/llvm801/lib/cmake/llvm -DCMAKE_BUILD_TYPE=Release ..
make install
Building without JIT
====================
You can omit ``-DLLVM_JIT=ON`` options to build Ravi with a null JIT implementation.
Building Static Libraries
=========================
By default the build generates a shared library for Ravi. You can choose to create a static library and statically linked executables by supplying the argument ``-DSTATIC_BUILD=ON`` to CMake.
JIT API
-------
auto mode
in this mode the compiler decides when to compile a Lua function. The current implementation is very simple -
any Lua function call is checked to see if the bytecodes contained in it can be compiled. If this is true then
the function is compiled provided either a) function has a fornum loop, or b) it is largish (greater than 150 bytecodes)
or c) it is being executed many times (> 50). Because of the simplistic behaviour performance the benefit of JIT
compilation is only available if the JIT compiled functions will be executed many times so that the cost of JIT
compilation can be amortized.
manual mode
in this mode user must explicitly request compilation. This is the default mode. This mode is suitable for library
developers who can pre compile the functions in library module table.
A JIT api is available with following functions:
``ravi.jit([b])``
returns enabled setting of JIT compiler; also enables/disables the JIT compiler; defaults to true
``ravi.auto([b [, min_size [, min_executions]]])``
returns setting of auto compilation and compilation thresholds; also sets the new settings if values are supplied; defaults are false, 150, 50.
``ravi.compile(func_or_table[, options])``
compiles a Lua function (or functions if a table is supplied) if possible, returns ``true`` if compilation was
successful for at least one function. ``options`` is an optional table with compilation options - in particular
``omitArrayGetRangeCheck`` - which disables range checks in array get operations to improve performance in some cases.
Note that at present if the first argument is a table of functions and has more than 100 functions then only the
first 100 will be compiled. You can invoke compile() repeatedly on the table until it returns false. Each
invocation leads to a new module being created; any functions already compiled are skipped.
``ravi.iscompiled(func)``
returns the JIT status of a function
``ravi.dumplua(func)``
dumps the Lua bytecode of the function
``ravi.dumpir(func)``
dumps the IR of the compiled function (only if function was compiled; only available in LLVM 4.0 and earlier)
``ravi.dumpasm(func)``
(deprecated) dumps the machine code using the currently set optimization level (only if function was compiled; only available in LLVM version 4.0 and earlier)
``ravi.optlevel([n])``
sets LLVM optimization level (0, 1, 2, 3); defaults to 2. These levels are handled by reusing LLVMs default pass definitions which are geared towards C/C++ programs, but appear to work well here. If level is set to 0, then an attempt is made to use fast instruction selection to further speed up compilation.
``ravi.sizelevel([n])``
sets LLVM size level (0, 1, 2); defaults to 0
``ravi.tracehook([b])``
Enables support for line hooks via the debug api. Note that enabling this option will result in inefficient JIT as a call to a C function will be inserted at beginning of every Lua bytecode boundary; use this option only when you want to use the debug api to step through code line by line
``ravi.verbosity([b])``
Controls the amount of verbose messages generated during compilation.
Performance
===========
For performance benchmarks please visit the `Ravi Performance Benchmarks <http://the-ravi-programming-language.readthedocs.org/en/latest/ravi-benchmarks.html>`_ page.
To obtain the best possible performance, types must be annotated so that Ravi's JIT compiler can generate efficient code.
Additionally function calls are expensive - as the JIT compiler cannot inline function calls, all function calls go via the Lua call protocol which has a large overhead. This is true for both Lua functions and C functions. For best performance avoid function calls inside loops.
Testing
=======
I test the build by running a modified version of Lua 5.3.3 test suite. These tests are located in the ``lua-tests`` folder. Additionally I have ravi specific tests in the ``ravi-tests`` folder. There is a also a travis build that occurs upon commits - this build runs the tests as well.
.. note:: To thoroughly test changes, you need to invoke CMake with ``-DCMAKE_BUILD_TYPE=Debug`` option. This turns on assertions, memory checking, and also enables an internal module used by Lua tests.

@ -1,707 +0,0 @@
Lua Types in LLVM
=================
We need to map Lua types to equivalent type definitions in LLVM. In Ravi we do hold all the type definitions in a struct as shown below::
struct LuaLLVMTypes {
llvm::Type *C_intptr_t;
llvm::Type *C_size_t;
llvm::Type *C_ptrdiff_t;
llvm::Type *lua_NumberT;
llvm::Type *lua_IntegerT;
llvm::Type *lua_UnsignedT;
llvm::Type *lua_KContextT;
llvm::FunctionType *lua_CFunctionT;
llvm::PointerType *plua_CFunctionT;
llvm::FunctionType *lua_KFunctionT;
llvm::PointerType *plua_KFunctionT;
llvm::FunctionType *lua_HookT;
llvm::PointerType *plua_HookT;
llvm::FunctionType *lua_AllocT;
llvm::PointerType *plua_AllocT;
llvm::Type *l_memT;
llvm::Type *lu_memT;
llvm::Type *lu_byteT;
llvm::Type *L_UmaxalignT;
llvm::Type *C_pcharT;
llvm::Type *C_intT;
llvm::StructType *lua_StateT;
llvm::PointerType *plua_StateT;
llvm::StructType *global_StateT;
llvm::PointerType *pglobal_StateT;
llvm::StructType *ravi_StateT;
llvm::PointerType *pravi_StateT;
llvm::StructType *GCObjectT;
llvm::PointerType *pGCObjectT;
llvm::StructType *ValueT;
llvm::StructType *TValueT;
llvm::PointerType *pTValueT;
llvm::StructType *TStringT;
llvm::PointerType *pTStringT;
llvm::PointerType *ppTStringT;
llvm::StructType *UdataT;
llvm::StructType *TableT;
llvm::PointerType *pTableT;
llvm::StructType *UpvaldescT;
llvm::PointerType *pUpvaldescT;
llvm::Type *ravitype_tT;
llvm::StructType *LocVarT;
llvm::PointerType *pLocVarT;
llvm::Type *InstructionT;
llvm::PointerType *pInstructionT;
llvm::StructType *LClosureT;
llvm::PointerType *pLClosureT;
llvm::PointerType *ppLClosureT;
llvm::PointerType *pppLClosureT;
llvm::StructType *RaviJITProtoT;
llvm::PointerType *pRaviJITProtoT;
llvm::StructType *ProtoT;
llvm::PointerType *pProtoT;
llvm::PointerType *ppProtoT;
llvm::StructType *UpValT;
llvm::PointerType *pUpValT;
llvm::StructType *CClosureT;
llvm::PointerType *pCClosureT;
llvm::StructType *TKeyT;
llvm::PointerType *pTKeyT;
llvm::StructType *NodeT;
llvm::PointerType *pNodeT;
llvm::StructType *lua_DebugT;
llvm::PointerType *plua_DebugT;
llvm::StructType *lua_longjumpT;
llvm::PointerType *plua_longjumpT;
llvm::StructType *MbufferT;
llvm::StructType *stringtableT;
llvm::PointerType *StkIdT;
llvm::StructType *CallInfoT;
llvm::StructType *CallInfo_cT;
llvm::StructType *CallInfo_lT;
llvm::PointerType *pCallInfoT;
llvm::FunctionType *jitFunctionT;
llvm::FunctionType *luaD_poscallT;
};
The actual definition of the types above is shown below::
static_assert(std::is_floating_point<lua_Number>::value &&
sizeof(lua_Number) == sizeof(double),
"lua_Number is not a double");
lua_NumberT = llvm::Type::getDoubleTy(context);
static_assert(std::is_integral<lua_Integer>::value,
"lua_Integer is not an integer type");
lua_IntegerT = llvm::Type::getIntNTy(context, sizeof(lua_Integer) * 8);
static_assert(sizeof(lua_Integer) == sizeof(lua_Unsigned),
"lua_Integer and lua_Unsigned are of different size");
lua_UnsignedT = lua_IntegerT;
C_intptr_t = llvm::Type::getIntNTy(context, sizeof(intptr_t) * 8);
C_size_t = llvm::Type::getIntNTy(context, sizeof(size_t) * 8);
C_ptrdiff_t = llvm::Type::getIntNTy(context, sizeof(ptrdiff_t) * 8);
C_intT = llvm::Type::getIntNTy(context, sizeof(int) * 8);
static_assert(sizeof(size_t) == sizeof(lu_mem),
"lu_mem size is not same as size_t");
lu_memT = C_size_t;
static_assert(sizeof(ptrdiff_t) == sizeof(l_mem),
"l_mem size is not same as ptrdiff_t");
l_memT = C_ptrdiff_t;
static_assert(sizeof(L_Umaxalign) == sizeof(double),
"L_Umaxalign is not same size as double");
L_UmaxalignT = llvm::Type::getDoubleTy(context);
lu_byteT = llvm::Type::getInt8Ty(context);
C_pcharT = llvm::Type::getInt8PtrTy(context);
InstructionT = C_intT;
pInstructionT = llvm::PointerType::get(InstructionT, 0);
lua_StateT = llvm::StructType::create(context, "ravi.lua_State");
plua_StateT = llvm::PointerType::get(lua_StateT, 0);
lua_KContextT = C_ptrdiff_t;
std::vector<llvm::Type *> elements;
elements.push_back(plua_StateT);
lua_CFunctionT = llvm::FunctionType::get(C_intT, elements, false);
plua_CFunctionT = llvm::PointerType::get(lua_CFunctionT, 0);
jitFunctionT = lua_CFunctionT;
elements.clear();
elements.push_back(plua_StateT);
elements.push_back(C_intT);
elements.push_back(lua_KContextT);
lua_KFunctionT = llvm::FunctionType::get(C_intT, elements, false);
plua_KFunctionT = llvm::PointerType::get(lua_KFunctionT, 0);
elements.clear();
elements.push_back(llvm::Type::getInt8PtrTy(context));
elements.push_back(llvm::Type::getInt8PtrTy(context));
elements.push_back(C_size_t);
elements.push_back(C_size_t);
lua_AllocT = llvm::FunctionType::get(llvm::Type::getInt8PtrTy(context),
elements, false);
plua_AllocT = llvm::PointerType::get(lua_AllocT, 0);
lua_DebugT = llvm::StructType::create(context, "ravi.lua_Debug");
plua_DebugT = llvm::PointerType::get(lua_DebugT, 0);
elements.clear();
elements.push_back(plua_StateT);
elements.push_back(plua_DebugT);
lua_HookT = llvm::FunctionType::get(llvm::Type::getInt8PtrTy(context),
elements, false);
plua_HookT = llvm::PointerType::get(lua_HookT, 0);
// struct GCObject {
// GCObject *next;
// lu_byte tt;
// lu_byte marked
// };
GCObjectT = llvm::StructType::create(context, "ravi.GCObject");
pGCObjectT = llvm::PointerType::get(GCObjectT, 0);
elements.clear();
elements.push_back(pGCObjectT);
elements.push_back(lu_byteT);
elements.push_back(lu_byteT);
GCObjectT->setBody(elements);
static_assert(sizeof(Value) == sizeof(lua_Number),
"Value type is larger than lua_Number");
// In LLVM unions should be set to the largest member
// So in the case of a Value this is the double type
// union Value {
// GCObject *gc; /* collectable objects */
// void *p; /* light userdata */
// int b; /* booleans */
// lua_CFunction f; /* light C functions */
// lua_Integer i; /* integer numbers */
// lua_Number n; /* float numbers */
// };
ValueT = llvm::StructType::create(context, "ravi.Value");
elements.clear();
elements.push_back(lua_NumberT);
ValueT->setBody(elements);
// struct TValue {
// union Value value_;
// int tt_;
// };
TValueT = llvm::StructType::create(context, "ravi.TValue");
elements.clear();
elements.push_back(ValueT);
elements.push_back(C_intT);
TValueT->setBody(elements);
pTValueT = llvm::PointerType::get(TValueT, 0);
StkIdT = pTValueT;
///*
//** Header for string value; string bytes follow the end of this structure
//** (aligned according to 'UTString'; see next).
//*/
// typedef struct TString {
// GCObject *next;
// lu_byte tt;
// lu_byte marked
// lu_byte extra; /* reserved words for short strings; "has hash" for longs
// */
// unsigned int hash;
// size_t len; /* number of characters in string */
// struct TString *hnext; /* linked list for hash table */
// } TString;
///*
//** Ensures that address after this type is always fully aligned.
//*/
// typedef union UTString {
// L_Umaxalign dummy; /* ensures maximum alignment for strings */
// TString tsv;
//} UTString;
TStringT = llvm::StructType::create(context, "ravi.TString");
pTStringT = llvm::PointerType::get(TStringT, 0);
ppTStringT = llvm::PointerType::get(pTStringT, 0);
elements.clear();
elements.push_back(pGCObjectT);
elements.push_back(lu_byteT);
elements.push_back(lu_byteT);
elements.push_back(lu_byteT); /* extra */
elements.push_back(C_intT); /* hash */
elements.push_back(C_size_t); /* len */
elements.push_back(pTStringT); /* hnext */
TStringT->setBody(elements);
// Table
TableT = llvm::StructType::create(context, "ravi.Table");
pTableT = llvm::PointerType::get(TableT, 0);
///*
//** Header for userdata; memory area follows the end of this structure
//** (aligned according to 'UUdata'; see next).
//*/
// typedef struct Udata {
// GCObject *next;
// lu_byte tt;
// lu_byte marked
// lu_byte ttuv_; /* user value's tag */
// struct Table *metatable;
// size_t len; /* number of bytes */
// union Value user_; /* user value */
//} Udata;
UdataT = llvm::StructType::create(context, "ravi.Udata");
elements.clear();
elements.push_back(pGCObjectT);
elements.push_back(lu_byteT);
elements.push_back(lu_byteT);
elements.push_back(lu_byteT); /* ttuv_ */
elements.push_back(pTableT); /* metatable */
elements.push_back(C_size_t); /* len */
elements.push_back(ValueT); /* user_ */
UdataT->setBody(elements);
///*
//** Description of an upvalue for function prototypes
//*/
// typedef struct Upvaldesc {
// TString *name; /* upvalue name (for debug information) */
// lu_byte instack; /* whether it is in stack */
// lu_byte idx; /* index of upvalue (in stack or in outer function's list)
// */
//}Upvaldesc;
UpvaldescT = llvm::StructType::create(context, "ravi.Upvaldesc");
elements.clear();
elements.push_back(pTStringT);
elements.push_back(lu_byteT);
elements.push_back(lu_byteT);
UpvaldescT->setBody(elements);
pUpvaldescT = llvm::PointerType::get(UpvaldescT, 0);
///*
//** Description of a local variable for function prototypes
//** (used for debug information)
//*/
// typedef struct LocVar {
// TString *varname;
// int startpc; /* first point where variable is active */
// int endpc; /* first point where variable is dead */
// ravitype_t ravi_type; /* RAVI type of the variable - RAVI_TANY if unknown
// */
//} LocVar;
ravitype_tT = llvm::Type::getIntNTy(context, sizeof(ravitype_t) * 8);
LocVarT = llvm::StructType::create(context, "ravi.LocVar");
elements.clear();
elements.push_back(pTStringT); /* varname */
elements.push_back(C_intT); /* startpc */
elements.push_back(C_intT); /* endpc */
elements.push_back(ravitype_tT); /* ravi_type */
LocVarT->setBody(elements);
pLocVarT = llvm::PointerType::get(LocVarT, 0);
LClosureT = llvm::StructType::create(context, "ravi.LClosure");
pLClosureT = llvm::PointerType::get(LClosureT, 0);
ppLClosureT = llvm::PointerType::get(pLClosureT, 0);
pppLClosureT = llvm::PointerType::get(ppLClosureT, 0);
RaviJITProtoT = llvm::StructType::create(context, "ravi.RaviJITProto");
pRaviJITProtoT = llvm::PointerType::get(RaviJITProtoT, 0);
///*
//** Function Prototypes
//*/
// typedef struct Proto {
// CommonHeader;
// lu_byte numparams; /* number of fixed parameters */
// lu_byte is_vararg;
// lu_byte maxstacksize; /* maximum stack used by this function */
// int sizeupvalues; /* size of 'upvalues' */
// int sizek; /* size of 'k' */
// int sizecode;
// int sizelineinfo;
// int sizep; /* size of 'p' */
// int sizelocvars;
// int linedefined;
// int lastlinedefined;
// TValue *k; /* constants used by the function */
// Instruction *code;
// struct Proto **p; /* functions defined inside the function */
// int *lineinfo; /* map from opcodes to source lines (debug information) */
// LocVar *locvars; /* information about local variables (debug information)
// */
// Upvaldesc *upvalues; /* upvalue information */
// struct LClosure *cache; /* last created closure with this prototype */
// TString *source; /* used for debug information */
// GCObject *gclist;
// /* RAVI */
// RaviJITProto *ravi_jit;
//} Proto;
ProtoT = llvm::StructType::create(context, "ravi.Proto");
pProtoT = llvm::PointerType::get(ProtoT, 0);
ppProtoT = llvm::PointerType::get(pProtoT, 0);
elements.clear();
elements.push_back(pGCObjectT);
elements.push_back(lu_byteT);
elements.push_back(lu_byteT);
elements.push_back(lu_byteT); /* numparams */
elements.push_back(lu_byteT); /* is_vararg */
elements.push_back(lu_byteT); /* maxstacksize */
elements.push_back(C_intT); /* sizeupvalues */
elements.push_back(C_intT); /* sizek */
elements.push_back(C_intT); /* sizecode */
elements.push_back(C_intT); /* sizelineinfo */
elements.push_back(C_intT); /* sizep */
elements.push_back(C_intT); /* sizelocvars */
elements.push_back(C_intT); /* linedefined */
elements.push_back(C_intT); /* lastlinedefined */
elements.push_back(pTValueT); /* k */
elements.push_back(pInstructionT); /* code */
elements.push_back(ppProtoT); /* p */
elements.push_back(llvm::PointerType::get(C_intT, 0)); /* lineinfo */
elements.push_back(pLocVarT); /* locvars */
elements.push_back(pUpvaldescT); /* upvalues */
elements.push_back(pLClosureT); /* cache */
elements.push_back(pTStringT); /* source */
elements.push_back(pGCObjectT); /* gclist */
elements.push_back(pRaviJITProtoT); /* ravi_jit */
ProtoT->setBody(elements);
///*
//** Lua Upvalues
//*/
// typedef struct UpVal UpVal;
UpValT = llvm::StructType::create(context, "ravi.UpVal");
pUpValT = llvm::PointerType::get(UpValT, 0);
///*
//** Closures
//*/
//#define ClosureHeader \
//CommonHeader; lu_byte nupvalues; GCObject *gclist
// typedef struct CClosure {
// ClosureHeader;
// lua_CFunction f;
// TValue upvalue[1]; /* list of upvalues */
//} CClosure;
CClosureT = llvm::StructType::create(context, "ravi.CClosure");
elements.clear();
elements.push_back(pGCObjectT);
elements.push_back(lu_byteT);
elements.push_back(lu_byteT);
elements.push_back(lu_byteT); /* nupvalues */
elements.push_back(pGCObjectT); /* gclist */
elements.push_back(plua_CFunctionT); /* f */
elements.push_back(llvm::ArrayType::get(TValueT, 1));
CClosureT->setBody(elements);
pCClosureT = llvm::PointerType::get(CClosureT, 0);
// typedef struct LClosure {
// ClosureHeader;
// struct Proto *p;
// UpVal *upvals[1]; /* list of upvalues */
//} LClosure;
elements.clear();
elements.push_back(pGCObjectT);
elements.push_back(lu_byteT);
elements.push_back(lu_byteT);
elements.push_back(lu_byteT); /* nupvalues */
elements.push_back(pGCObjectT); /* gclist */
elements.push_back(pProtoT); /* p */
elements.push_back(llvm::ArrayType::get(pUpValT, 1));
LClosureT->setBody(elements);
///*
//** Tables
//*/
// typedef union TKey {
// struct {
// TValuefields;
// int next; /* for chaining (offset for next node) */
// } nk;
// TValue tvk;
//} TKey;
TKeyT = llvm::StructType::create(context, "ravi.TKey");
elements.clear();
elements.push_back(ValueT);
elements.push_back(C_intT);
elements.push_back(C_intT); /* next */
TKeyT->setBody(elements);
pTKeyT = llvm::PointerType::get(TKeyT, 0);
// typedef struct Node {
// TValue i_val;
// TKey i_key;
//} Node;
NodeT = llvm::StructType::create(context, "ravi.Node");
elements.clear();
elements.push_back(TValueT); /* i_val */
elements.push_back(TKeyT); /* i_key */
NodeT->setBody(elements);
pNodeT = llvm::PointerType::get(NodeT, 0);
// typedef struct Table {
// CommonHeader;
// lu_byte flags; /* 1<<p means tagmethod(p) is not present */
// lu_byte lsizenode; /* log2 of size of 'node' array */
// unsigned int sizearray; /* size of 'array' array */
// TValue *array; /* array part */
// Node *node;
// Node *lastfree; /* any free position is before this position */
// struct Table *metatable;
// GCObject *gclist;
// ravitype_t ravi_array_type; /* RAVI specialization */
// unsigned int ravi_array_len; /* RAVI len specialization */
//} Table;
elements.clear();
elements.push_back(pGCObjectT);
elements.push_back(lu_byteT);
elements.push_back(lu_byteT);
elements.push_back(lu_byteT); /* flags */
elements.push_back(lu_byteT); /* lsizenode */
elements.push_back(C_intT); /* sizearray */
elements.push_back(pTValueT); /* array part */
elements.push_back(pNodeT); /* node */
elements.push_back(pNodeT); /* lastfree */
elements.push_back(pTableT); /* metatable */
elements.push_back(pGCObjectT); /* gclist */
elements.push_back(ravitype_tT); /* ravi_array_type */
elements.push_back(C_intT); /* ravi_array_len */
TableT->setBody(elements);
// struct lua_longjmp; /* defined in ldo.c */
lua_longjumpT = llvm::StructType::create(context, "ravi.lua_longjmp");
plua_longjumpT = llvm::PointerType::get(lua_longjumpT, 0);
// lzio.h
// typedef struct Mbuffer {
// char *buffer;
// size_t n;
// size_t buffsize;
//} Mbuffer;
MbufferT = llvm::StructType::create(context, "ravi.Mbuffer");
elements.clear();
elements.push_back(llvm::Type::getInt8PtrTy(context)); /* buffer */
elements.push_back(C_size_t); /* n */
elements.push_back(C_size_t); /* buffsize */
MbufferT->setBody(elements);
// typedef struct stringtable {
// TString **hash;
// int nuse; /* number of elements */
// int size;
//} stringtable;
stringtableT = llvm::StructType::create(context, "ravi.stringtable");
elements.clear();
elements.push_back(ppTStringT); /* hash */
elements.push_back(C_intT); /* nuse */
elements.push_back(C_intT); /* size */
stringtableT->setBody(elements);
///*
//** Information about a call.
//** When a thread yields, 'func' is adjusted to pretend that the
//** top function has only the yielded values in its stack; in that
//** case, the actual 'func' value is saved in field 'extra'.
//** When a function calls another with a continuation, 'extra' keeps
//** the function index so that, in case of errors, the continuation
//** function can be called with the correct top.
//*/
// typedef struct CallInfo {
// StkId func; /* function index in the stack */
// StkId top; /* top for this function */
// struct CallInfo *previous, *next; /* dynamic call link */
// union {
// struct { /* only for Lua functions */
// StkId base; /* base for this function */
// const Instruction *savedpc;
// } l;
// struct { /* only for C functions */
// lua_KFunction k; /* continuation in case of yields */
// ptrdiff_t old_errfunc;
// lua_KContext ctx; /* context info. in case of yields */
// } c;
// } u;
// ptrdiff_t extra;
// short nresults; /* expected number of results from this function */
// lu_byte callstatus;
//} CallInfo;
elements.clear();
elements.push_back(StkIdT); /* base */
elements.push_back(pInstructionT); /* savedpc */
elements.push_back(
C_ptrdiff_t); /* dummy to make this same size as the other member */
CallInfo_lT = llvm::StructType::create(elements);
elements.clear();
elements.push_back(plua_KFunctionT); /* k */
elements.push_back(C_ptrdiff_t); /* old_errfunc */
elements.push_back(lua_KContextT); /* ctx */
CallInfo_cT = llvm::StructType::create(elements);
CallInfoT = llvm::StructType::create(context, "ravi.CallInfo");
pCallInfoT = llvm::PointerType::get(CallInfoT, 0);
elements.clear();
elements.push_back(StkIdT); /* func */
elements.push_back(StkIdT); /* top */
elements.push_back(pCallInfoT); /* previous */
elements.push_back(pCallInfoT); /* next */
elements.push_back(
CallInfo_lT); /* u.l - as we will typically access the lua call details
*/
elements.push_back(C_ptrdiff_t); /* extra */
elements.push_back(llvm::Type::getInt16Ty(context)); /* nresults */
elements.push_back(lu_byteT); /* callstatus */
CallInfoT->setBody(elements);
// typedef struct ravi_State ravi_State;
ravi_StateT = llvm::StructType::create(context, "ravi.ravi_State");
pravi_StateT = llvm::PointerType::get(ravi_StateT, 0);
///*
//** 'global state', shared by all threads of this state
//*/
// typedef struct global_State {
// lua_Alloc frealloc; /* function to reallocate memory */
// void *ud; /* auxiliary data to 'frealloc' */
// lu_mem totalbytes; /* number of bytes currently allocated - GCdebt */
// l_mem GCdebt; /* bytes allocated not yet compensated by the collector */
// lu_mem GCmemtrav; /* memory traversed by the GC */
// lu_mem GCestimate; /* an estimate of the non-garbage memory in use */
// stringtable strt; /* hash table for strings */
// TValue l_registry;
// unsigned int seed; /* randomized seed for hashes */
// lu_byte currentwhite;
// lu_byte gcstate; /* state of garbage collector */
// lu_byte gckind; /* kind of GC running */
// lu_byte gcrunning; /* true if GC is running */
// GCObject *allgc; /* list of all collectable objects */
// GCObject **sweepgc; /* current position of sweep in list */
// GCObject *finobj; /* list of collectable objects with finalizers */
// GCObject *gray; /* list of gray objects */
// GCObject *grayagain; /* list of objects to be traversed atomically */
// GCObject *weak; /* list of tables with weak values */
// GCObject *ephemeron; /* list of ephemeron tables (weak keys) */
// GCObject *allweak; /* list of all-weak tables */
// GCObject *tobefnz; /* list of userdata to be GC */
// GCObject *fixedgc; /* list of objects not to be collected */
// struct lua_State *twups; /* list of threads with open upvalues */
// Mbuffer buff; /* temporary buffer for string concatenation */
// unsigned int gcfinnum; /* number of finalizers to call in each GC step */
// int gcpause; /* size of pause between successive GCs */
// int gcstepmul; /* GC 'granularity' */
// lua_CFunction panic; /* to be called in unprotected errors */
// struct lua_State *mainthread;
// const lua_Number *version; /* pointer to version number */
// TString *memerrmsg; /* memory-error message */
// TString *tmname[TM_N]; /* array with tag-method names */
// struct Table *mt[LUA_NUMTAGS]; /* metatables for basic types */
// /* RAVI */
// ravi_State *ravi_state;
//} global_State;
global_StateT = llvm::StructType::create(context, "ravi.global_State");
pglobal_StateT = llvm::PointerType::get(global_StateT, 0);
///*
//** 'per thread' state
//*/
// struct lua_State {
// CommonHeader;
// lu_byte status;
// StkId top; /* first free slot in the stack */
// global_State *l_G;
// CallInfo *ci; /* call info for current function */
// const Instruction *oldpc; /* last pc traced */
// StkId stack_last; /* last free slot in the stack */
// StkId stack; /* stack base */
// UpVal *openupval; /* list of open upvalues in this stack */
// GCObject *gclist;
// struct lua_State *twups; /* list of threads with open upvalues */
// struct lua_longjmp *errorJmp; /* current error recover point */
// CallInfo base_ci; /* CallInfo for first level (C calling Lua) */
// lua_Hook hook;
// ptrdiff_t errfunc; /* current error handling function (stack index) */
// int stacksize;
// int basehookcount;
// int hookcount;
// unsigned short nny; /* number of non-yieldable calls in stack */
// unsigned short nCcalls; /* number of nested C calls */
// lu_byte hookmask;
// lu_byte allowhook;
//};
elements.clear();
elements.push_back(pGCObjectT);
elements.push_back(lu_byteT);
elements.push_back(lu_byteT);
elements.push_back(lu_byteT); /* status */
elements.push_back(StkIdT); /* top */
elements.push_back(pglobal_StateT); /* l_G */
elements.push_back(pCallInfoT); /* ci */
elements.push_back(pInstructionT); /* oldpc */
elements.push_back(StkIdT); /* stack_last */
elements.push_back(StkIdT); /* stack */
elements.push_back(pUpValT); /* openupval */
elements.push_back(pGCObjectT); /* gclist */
elements.push_back(plua_StateT); /* twups */
elements.push_back(plua_longjumpT); /* errorJmp */
elements.push_back(CallInfoT); /* base_ci */
elements.push_back(plua_HookT); /* hook */
elements.push_back(C_ptrdiff_t); /* errfunc */
elements.push_back(C_intT); /* stacksize */
elements.push_back(C_intT); /* basehookcount */
elements.push_back(C_intT); /* hookcount */
elements.push_back(llvm::Type::getInt16Ty(context)); /* nny */
elements.push_back(llvm::Type::getInt16Ty(context)); /* nCcalls */
elements.push_back(lu_byteT); /* hookmask */
elements.push_back(lu_byteT); /* allowhook */
lua_StateT->setBody(elements);
// int luaD_poscall (lua_State *L, StkId firstResult)
elements.clear();
elements.push_back(plua_StateT);
elements.push_back(StkIdT);
luaD_poscallT = llvm::FunctionType::get(C_intT, elements, false);

@ -1,8 +1,6 @@
Instructions for Building With MIR JIT support Instructions for Building With MIR JIT support
============================================== ==============================================
Please note that currently `MIR <https://github.com/vnmakarov/mir>`_ JIT support is only available on Linux X86-64 platforms.
Building with MIR support is straightforward as MIR is included in Ravi:: Building with MIR support is straightforward as MIR is included in Ravi::
mkdir buildmir mkdir buildmir
@ -11,3 +9,10 @@ Building with MIR support is straightforward as MIR is included in Ravi::
make install make install
That's it. That's it.
For Windows, try this::
mkdir buildmir
cd buildmir
cmake -DCMAKE_INSTALL_PREFIX=/Software/ravi -DCMAKE_BUILD_TYPE=Release -DMIR_JIT=ON ..
cmake --build . --config Release

@ -5,7 +5,7 @@ Ravi Programming Language
:target: https://travis-ci.org/dibyendumajumdar/ravi :target: https://travis-ci.org/dibyendumajumdar/ravi
Ravi is a dialect of `Lua <http://www.lua.org/>`_ with limited optional static typing and Ravi is a dialect of `Lua <http://www.lua.org/>`_ with limited optional static typing and
features `MIR <https://github.com/vnmakarov/mir>`_ and `LLVM <http://www.llvm.org/>`_ powered JIT compilers. features `MIR <https://github.com/vnmakarov/mir>`_ powered JIT compilers.
The name Ravi comes from the Sanskrit word for the Sun. The name Ravi comes from the Sanskrit word for the Sun.
Interestingly a precursor to Lua was `Sol <http://www.lua.org/history.html>`_ which had support for Interestingly a precursor to Lua was `Sol <http://www.lua.org/history.html>`_ which had support for
static types; Sol means the Sun in Portugese. static types; Sol means the Sun in Portugese.
@ -35,8 +35,7 @@ Features
* Compatibility with Lua 5.3 (see Compatibility section below) * Compatibility with Lua 5.3 (see Compatibility section below)
* Generational GC from Lua 5.4 * Generational GC from Lua 5.4
* ``defer`` statement for releasing resources * ``defer`` statement for releasing resources
* Compact JIT backend `MIR <https://github.com/vnmakarov/mir>`_; only Linux and x86-64 supported for now. * Compact JIT backend `MIR <https://github.com/vnmakarov/mir>`_.
* `LLVM <http://www.llvm.org/>`_ supported as alternative JIT backend.
* A `distribution with batteries <https://github.com/dibyendumajumdar/Suravi>`_. * A `distribution with batteries <https://github.com/dibyendumajumdar/Suravi>`_.
* A `Visual Studio Code debugger extension <https://marketplace.visualstudio.com/items?itemName=ravilang.ravi-debug>`_ - interpreted mode debugger. * A `Visual Studio Code debugger extension <https://marketplace.visualstudio.com/items?itemName=ravilang.ravi-debug>`_ - interpreted mode debugger.
@ -44,7 +43,6 @@ Documentation
============= =============
* For the Lua extensions in Ravi see the `Reference Manual <https://the-ravi-programming-language.readthedocs.io/en/latest/ravi-reference.html>`_. * For the Lua extensions in Ravi see the `Reference Manual <https://the-ravi-programming-language.readthedocs.io/en/latest/ravi-reference.html>`_.
* `MIR JIT Build instructions <https://the-ravi-programming-language.readthedocs.io/en/latest/ravi-mir-instructions.html>`_. * `MIR JIT Build instructions <https://the-ravi-programming-language.readthedocs.io/en/latest/ravi-mir-instructions.html>`_.
* `LLVM JIT Build instructions <https://the-ravi-programming-language.readthedocs.io/en/latest/ravi-llvm-instructions.html>`_.
* Also see `Ravi Documentation <http://the-ravi-programming-language.readthedocs.org/en/latest/index.html>`_. * Also see `Ravi Documentation <http://the-ravi-programming-language.readthedocs.org/en/latest/index.html>`_.
* and the slides I presented at the `Lua 2015 Workshop <http://www.lua.org/wshop15.html>`_. * and the slides I presented at the `Lua 2015 Workshop <http://www.lua.org/wshop15.html>`_.
@ -99,6 +97,7 @@ When JIT compilation is enabled there are following additional constraints:
* Ravi will only execute JITed code from the main Lua thread; any secondary threads (coroutines) execute in interpreter mode. * Ravi will only execute JITed code from the main Lua thread; any secondary threads (coroutines) execute in interpreter mode.
* In JITed code tailcalls are implemented as regular calls so unlike the interpreter VM which supports infinite tail recursion JIT compiled code only supports tail recursion to a depth of about 110 (issue #17) * In JITed code tailcalls are implemented as regular calls so unlike the interpreter VM which supports infinite tail recursion JIT compiled code only supports tail recursion to a depth of about 110 (issue #17)
* Debug api and hooks are not supported in JIT mode
History History
======= =======
@ -115,11 +114,13 @@ History
- Created `Ravi with batteries <https://github.com/dibyendumajumdar/Suravi>`_. - Created `Ravi with batteries <https://github.com/dibyendumajumdar/Suravi>`_.
* 2019 * 2019
- New language feature - `defer` statement - New language feature - `defer` statement
- New JIT backend `MIR <https://github.com/vnmakarov/mir>`_. - New JIT backend `MIR <https://github.com/vnmakarov/mir>`_.
* 2020
* 2020 (Plan) - `New parser / type checker / compiler <https://github.com/dibyendumajumdar/ravi-compiler>`_
- `New optimizing byte code generator based on new parser / type checker <https://github.com/dibyendumajumdar/ravi-compiler>`_
- Generational GC back-ported from Lua 5.4 - Generational GC back-ported from Lua 5.4
- Support for `LLVM backend <https://github.com/dibyendumajumdar/ravi/tree/llvm>`_ archived
* 2021 (Plan)
- Integrated AOT and JIT compilation support
- Ravi 1.0 release - Ravi 1.0 release
License License

@ -1,374 +0,0 @@
/******************************************************************************
* Copyright (C) 2015-2020 Dibyendu Majumdar
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
******************************************************************************/
#include "ravi_llvmcodegen.h"
// This file contains LLVM IR generation for Ravi's arithmetic
// op codes - these are enhanced type specific op codes not part of
// standard Lua
namespace ravi {
// R(A) := -R(B), floating point
// NOT yet used
void RaviCodeGenerator::emit_UNMF(RaviFunctionDef *def, int A, int B, int pc) {
(void)pc;
emit_load_base(def);
llvm::Value *ra = emit_gep_register(def, A);
llvm::Value *rb = emit_gep_register_or_constant(def, B);
llvm::Instruction *lhs = emit_load_reg_n(def, rb);
llvm::Value *result = def->builder->CreateFNeg(lhs);
emit_store_reg_n_withtype(def, result, ra);
}
// R(A) := -R(B), integer
// NOT yet used
void RaviCodeGenerator::emit_UNMI(RaviFunctionDef *def, int A, int B, int pc) {
(void)pc;
emit_load_base(def);
llvm::Value *ra = emit_gep_register(def, A);
llvm::Value *rb = emit_gep_register_or_constant(def, B);
llvm::Instruction *lhs = emit_load_reg_i(def, rb);
llvm::Value *result = def->builder->CreateNeg(lhs, "", false, true);
emit_store_reg_i_withtype(def, result, ra);
}
// R(A) := RK(B) + RK(C), all floating
void RaviCodeGenerator::emit_ADDFF(RaviFunctionDef *def, int A, int B, int C,
int pc) {
emit_debug_trace(def, OP_RAVI_ADDFF, pc);
emit_load_base(def);
llvm::Value *ra = emit_gep_register(def, A);
llvm::Value *rb = emit_load_register_or_constant_n(def, B);
llvm::Value *rc = emit_load_register_or_constant_n(def, C);
llvm::Value *result = def->builder->CreateFAdd(rb, rc);
emit_store_reg_n_withtype(def, result, ra);
}
// R(A) := RK(B) + RK(C), float+int
void RaviCodeGenerator::emit_ADDFI(RaviFunctionDef *def, int A, int B, int C,
int pc) {
emit_debug_trace(def, OP_RAVI_ADDFI, pc);
emit_load_base(def);
llvm::Value *ra = emit_gep_register(def, A);
llvm::Value *rb = emit_load_register_or_constant_n(def, B);
llvm::Value *rc = emit_load_register_or_constant_i(def, C);
llvm::Value *result = def->builder->CreateFAdd(
rb, def->builder->CreateSIToFP(rc, def->types->lua_NumberT));
emit_store_reg_n_withtype(def, result, ra);
}
// R(A) := RK(B) + RK(C), int+int
void RaviCodeGenerator::emit_ADDII(RaviFunctionDef *def, int A, int B, int C,
int pc) {
emit_debug_trace(def, OP_RAVI_ADDII, pc);
emit_load_base(def);
llvm::Value *ra = emit_gep_register(def, A);
llvm::Value *rb = emit_load_register_or_constant_i(def, B);
llvm::Value *rc = emit_load_register_or_constant_i(def, C);
llvm::Value *result =
def->builder->CreateAdd(rb, rc, "OP_RAVI_ADDII_result", false, true);
emit_store_reg_i_withtype(def, result, ra);
}
// R(A) := RK(B) - RK(C), float-float
void RaviCodeGenerator::emit_SUBFF(RaviFunctionDef *def, int A, int B, int C,
int pc) {
emit_debug_trace(def, OP_RAVI_SUBFF, pc);
emit_load_base(def);
llvm::Value *ra = emit_gep_register(def, A);
llvm::Value *rb = emit_load_register_or_constant_n(def, B);
llvm::Value *rc = emit_load_register_or_constant_n(def, C);
llvm::Value *result = def->builder->CreateFSub(rb, rc);
emit_store_reg_n_withtype(def, result, ra);
}
// R(A) := RK(B) - RK(C), float-int
void RaviCodeGenerator::emit_SUBFI(RaviFunctionDef *def, int A, int B, int C,
int pc) {
emit_debug_trace(def, OP_RAVI_SUBFI, pc);
emit_load_base(def);
llvm::Value *ra = emit_gep_register(def, A);
llvm::Value *rb = emit_load_register_or_constant_n(def, B);
llvm::Value *rc = emit_load_register_or_constant_i(def, C);
llvm::Value *result = def->builder->CreateFSub(
rb, def->builder->CreateSIToFP(rc, def->types->lua_NumberT));
emit_store_reg_n_withtype(def, result, ra);
}
// R(A) := RK(B) - RK(C), int-float
void RaviCodeGenerator::emit_SUBIF(RaviFunctionDef *def, int A, int B, int C,
int pc) {
emit_debug_trace(def, OP_RAVI_SUBIF, pc);
emit_load_base(def);
llvm::Value *ra = emit_gep_register(def, A);
llvm::Value *rb = emit_load_register_or_constant_i(def, B);
llvm::Value *rc = emit_load_register_or_constant_n(def, C);
llvm::Value *result = def->builder->CreateFSub(
def->builder->CreateSIToFP(rb, def->types->lua_NumberT), rc);
emit_store_reg_n_withtype(def, result, ra);
}
// R(A) := RK(B) - RK(C), int-int
void RaviCodeGenerator::emit_SUBII(RaviFunctionDef *def, int A, int B, int C,
int pc) {
emit_debug_trace(def, OP_RAVI_SUBII, pc);
emit_load_base(def);
llvm::Value *ra = emit_gep_register(def, A);
llvm::Value *rb = emit_load_register_or_constant_i(def, B);
llvm::Value *rc = emit_load_register_or_constant_i(def, C);
llvm::Value *result =
def->builder->CreateSub(rb, rc, "OP_RAVI_SUBII_result", false, true);
emit_store_reg_i_withtype(def, result, ra);
}
// R(A) := RK(B) * RK(C), float*float
void RaviCodeGenerator::emit_MULFF(RaviFunctionDef *def, int A, int B, int C,
int pc) {
emit_debug_trace(def, OP_RAVI_MULFF, pc);
emit_load_base(def);
llvm::Value *ra = emit_gep_register(def, A);
llvm::Value *rb = emit_load_register_or_constant_n(def, B);
llvm::Value *rc = emit_load_register_or_constant_n(def, C);
llvm::Value *result = def->builder->CreateFMul(rb, rc);
emit_store_reg_n_withtype(def, result, ra);
}
// R(A) := RK(B) * RK(C), float*int
void RaviCodeGenerator::emit_MULFI(RaviFunctionDef *def, int A, int B, int C,
int pc) {
emit_debug_trace(def, OP_RAVI_MULFI, pc);
emit_load_base(def);
llvm::Value *ra = emit_gep_register(def, A);
llvm::Value *rb = emit_load_register_or_constant_n(def, B);
llvm::Value *rc = emit_load_register_or_constant_i(def, C);
llvm::Value *result = def->builder->CreateFMul(
rb, def->builder->CreateSIToFP(rc, def->types->lua_NumberT));
emit_store_reg_n_withtype(def, result, ra);
}
// R(A) := RK(B) * RK(C), int*int
void RaviCodeGenerator::emit_MULII(RaviFunctionDef *def, int A, int B, int C,
int pc) {
emit_debug_trace(def, OP_RAVI_MULII, pc);
emit_load_base(def);
llvm::Value *ra = emit_gep_register(def, A);
llvm::Value *rb = emit_load_register_or_constant_i(def, B);
llvm::Value *rc = emit_load_register_or_constant_i(def, C);
llvm::Value *result =
def->builder->CreateMul(rb, rc, "OP_RAVI_MULII_result", false, true);
emit_store_reg_i_withtype(def, result, ra);
}
// R(A) := RK(B) / RK(C), float/float
void RaviCodeGenerator::emit_DIVFF(RaviFunctionDef *def, int A, int B, int C,
int pc) {
emit_debug_trace(def, OP_RAVI_DIVFF, pc);
emit_load_base(def);
llvm::Value *ra = emit_gep_register(def, A);
llvm::Value *rb = emit_load_register_or_constant_n(def, B);
llvm::Value *rc = emit_load_register_or_constant_n(def, C);
llvm::Value *result = def->builder->CreateFDiv(rb, rc);
emit_store_reg_n_withtype(def, result, ra);
}
// R(A) := RK(B) / RK(C), float/int
void RaviCodeGenerator::emit_DIVFI(RaviFunctionDef *def, int A, int B, int C,
int pc) {
emit_debug_trace(def, OP_RAVI_DIVFI, pc);
emit_load_base(def);
llvm::Value *ra = emit_gep_register(def, A);
llvm::Value *rb = emit_load_register_or_constant_n(def, B);
llvm::Value *rc = emit_load_register_or_constant_i(def, C);
llvm::Value *result = def->builder->CreateFDiv(
rb, def->builder->CreateSIToFP(rc, def->types->lua_NumberT));
emit_store_reg_n_withtype(def, result, ra);
}
// R(A) := RK(B) / RK(C), int/float
void RaviCodeGenerator::emit_DIVIF(RaviFunctionDef *def, int A, int B, int C,
int pc) {
emit_debug_trace(def, OP_RAVI_DIVIF, pc);
emit_load_base(def);
llvm::Value *ra = emit_gep_register(def, A);
llvm::Value *rb = emit_load_register_or_constant_i(def, B);
llvm::Value *rc = emit_load_register_or_constant_n(def, C);
llvm::Value *result = def->builder->CreateFDiv(
def->builder->CreateSIToFP(rb, def->types->lua_NumberT), rc);
emit_store_reg_n_withtype(def, result, ra);
}
// R(A) := RK(B) / RK(C), int/int but result is float
void RaviCodeGenerator::emit_DIVII(RaviFunctionDef *def, int A, int B, int C,
int pc) {
emit_debug_trace(def, OP_RAVI_DIVII, pc);
emit_load_base(def);
llvm::Value *ra = emit_gep_register(def, A);
llvm::Value *rb = emit_load_register_or_constant_i(def, B);
llvm::Value *rc = emit_load_register_or_constant_i(def, C);
llvm::Value *result = def->builder->CreateFDiv(
def->builder->CreateSIToFP(rb, def->types->lua_NumberT),
def->builder->CreateSIToFP(rc, def->types->lua_NumberT));
emit_store_reg_n_withtype(def, result, ra);
}
// Bitwise AND, OR and XOR when both operands are known to be
// integers
void RaviCodeGenerator::emit_BITWISE_BINARY_OP(RaviFunctionDef *def, OpCode op,
int A, int B, int C, int pc) {
emit_debug_trace(def, op, pc);
emit_load_base(def);
llvm::Value *ra = emit_gep_register(def, A);
llvm::Value *lhs = emit_load_register_or_constant_i(def, B);
llvm::Value *rhs = emit_load_register_or_constant_i(def, C);
llvm::Value *result = NULL;
switch (op) {
case OP_RAVI_BAND_II:
result = def->builder->CreateAnd(lhs, rhs, "OP_RAVI_BAND_II_result");
break;
case OP_RAVI_BOR_II:
result = def->builder->CreateOr(lhs, rhs, "OP_RAVI_BOR_II_result");
break;
case OP_RAVI_BXOR_II:
result = def->builder->CreateXor(lhs, rhs, "OP_RAVI_BXOR_II_result");
break;
default:
fprintf(stderr, "unexpected value of opcode %d\n", (int)op);
abort();
}
emit_store_reg_i_withtype(def, result, ra);
}
/* number of bits in an integer */
#define NBITS cast_int(sizeof(lua_Integer) * CHAR_BIT)
// Handle the case when we have a constant RHS and the LHS is an integer
void RaviCodeGenerator::emit_bitwise_shiftl(RaviFunctionDef *def,
llvm::Value *ra, int B,
lua_Integer y) {
if (y < 0) { /* shift right? */
if (y <= -NBITS)
emit_store_reg_i_withtype(
def, llvm::ConstantInt::get(def->types->lua_IntegerT, 0), ra);
else {
llvm::Value *rb = emit_load_register_or_constant_i(def, B);
llvm::Value *result = def->builder->CreateLShr(
rb, llvm::ConstantInt::get(def->types->lua_IntegerT, -y));
emit_store_reg_i_withtype(def, result, ra);
}
}
else {
if (y >= NBITS)
emit_store_reg_i_withtype(
def, llvm::ConstantInt::get(def->types->lua_IntegerT, 0), ra);
else {
llvm::Value *rb = emit_load_register_or_constant_i(def, B);
llvm::Value *result = def->builder->CreateShl(
rb, llvm::ConstantInt::get(def->types->lua_IntegerT, y));
emit_store_reg_i_withtype(def, result, ra);
}
}
}
void RaviCodeGenerator::emit_BITWISE_SHIFT_OP(RaviFunctionDef *def, OpCode op,
int A, int B, int C, int pc) {
bool traced = emit_debug_trace(def, op, pc);
emit_load_base(def);
llvm::Value *ra = emit_gep_register(def, A);
// If the RHS is a constant and we know that LHS is
// and integer then we can optimize the code generation
if (op == OP_RAVI_SHL_II && ISK(C)) {
lua_Integer y = def->p->k[INDEXK(C)].value_.i;
emit_bitwise_shiftl(def, ra, B, y);
}
else if (op == OP_RAVI_SHR_II && ISK(C)) {
lua_Integer y = def->p->k[INDEXK(C)].value_.i;
emit_bitwise_shiftl(def, ra, B, -y);
}
else {
// RHS is not a constant
llvm::Value *rc = emit_gep_register_or_constant(def, C);
llvm::Value *rb = emit_gep_register_or_constant(def, B);
// Since the Lua OP_SHL and OP_SHR bytecodes
// could invoke metamethods we need to set
// 'savedpc'
switch (op) {
case OP_SHL:
if (!traced) emit_update_savedpc(def, pc);
case OP_RAVI_SHL_II:
CreateCall4(def->builder, def->raviV_op_shlF, def->L, ra, rb, rc);
break;
case OP_SHR:
if (!traced) emit_update_savedpc(def, pc);
case OP_RAVI_SHR_II:
CreateCall4(def->builder, def->raviV_op_shrF, def->L, ra, rb, rc);
break;
default:
fprintf(stderr, "unexpected value of opcode %d\n", (int)op);
abort();
}
}
}
// R(A) := ~R(B); known integer operand
void RaviCodeGenerator::emit_BNOT_I(RaviFunctionDef *def, int A, int B,
int pc) {
emit_debug_trace(def, OP_RAVI_BNOT_I, pc);
emit_load_base(def);
llvm::Value *ra = emit_gep_register(def, A);
llvm::Value *lhs = emit_load_register_or_constant_i(def, B);
llvm::Value *rhs = llvm::ConstantInt::get(def->types->lua_IntegerT, -1);
llvm::Value *result = def->builder->CreateXor(lhs, rhs, "");
emit_store_reg_i_withtype(def, result, ra);
}
void RaviCodeGenerator::emit_BOR_BXOR_BAND(RaviFunctionDef *def, OpCode op,
int A, int B, int C, int pc) {
bool traced = emit_debug_trace(def, op, pc);
// Below may invoke metamethod so we set savedpc
if (!traced) emit_update_savedpc(def, pc);
emit_load_base(def);
llvm::Value *ra = emit_gep_register(def, A);
llvm::Value *rb = emit_gep_register_or_constant(def, B);
llvm::Value *rc = emit_gep_register_or_constant(def, C);
CreateCall4(def->builder,
op == OP_BOR
? def->raviV_op_borF
: (op == OP_BAND ? def->raviV_op_bandF : def->raviV_op_bxorF),
def->L, ra, rb, rc);
}
void RaviCodeGenerator::emit_BNOT(RaviFunctionDef *def, int A, int B, int pc) {
bool traced = emit_debug_trace(def, OP_BNOT, pc);
// Below may invoke metamethod so we set savedpc
if (!traced) emit_update_savedpc(def, pc);
emit_load_base(def);
llvm::Value *ra = emit_gep_register(def, A);
llvm::Value *rb = emit_gep_register(def, B);
CreateCall3(def->builder, def->raviV_op_bnotF, def->L, ra, rb);
}
}

File diff suppressed because it is too large Load Diff

@ -1,189 +0,0 @@
/******************************************************************************
* Copyright (C) 2015-2020 Dibyendu Majumdar
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
******************************************************************************/
#include "ravi_llvmcodegen.h"
namespace ravi {
// OP_JMP
void RaviCodeGenerator::emit_JMP(RaviFunctionDef *def, int A, int sBx, int pc) {
//#define dojump(ci,i,e)
// { int a = GETARG_A(i);
// if (a > 0) luaF_close(L, ci->u.l.base + a - 1);
// ci->u.l.savedpc += GETARG_sBx(i) + e; }
//
// dojump(ci, i, 0);
assert(def->jmp_targets[sBx].jmp1);
// If the current block is already terminated we
// need to create a new block
if (def->builder->GetInsertBlock()->getTerminator()) {
llvm::BasicBlock *jmp_block = llvm::BasicBlock::Create(
def->jitState->context(), "OP_JMP_bridge", def->f);
def->builder->SetInsertPoint(jmp_block);
}
bool traced = emit_debug_trace(def, OP_JMP, pc);
// if (a > 0) luaF_close(L, ci->u.l.base + a - 1);
if (A > 0) {
emit_load_base(def);
// base + a - 1
llvm::Value *val = emit_gep_register(def, A - 1);
#ifdef RAVI_DEFER_STATEMENT
if (!traced)
emit_update_savedpc(def, pc);
// Call luaF_close
CreateCall3(def->builder, def->luaF_closeF, def->L, val, def->types->kInt[LUA_OK]);
#else
CreateCall2(def->builder, def->luaF_closeF, def->L, val);
#endif
}
// Do the actual jump
def->builder->CreateBr(def->jmp_targets[sBx].jmp1);
// Start new block
llvm::BasicBlock *block = llvm::BasicBlock::Create(def->jitState->context(),
"OP_JMP_postjmp", def->f);
def->builder->SetInsertPoint(block);
}
// Handle OP_CALL
// Note that Lua assumes that functions called via OP_CALL
// are Lua functions and secondly that once OP_CALL completes the
// current function will continue within the same luaV_execute()
// call. However in a JIT case each JIT function is a different call
// so we need to take care of the behaviour differences between
// OP_CALL and external calls
void RaviCodeGenerator::emit_CALL(RaviFunctionDef *def, int A, int B, int C,
int pc) {
// int nresults = c - 1;
// if (b != 0)
// L->top = ra + b; /* else previous instruction set top */
// int c = luaD_precall(L, ra, nresults, 1); /* C or JITed function? */
// if (c) {
// if (c == 1 && nresults >= 0)
// L->top = ci->top; /* adjust results if C function */
// }
// else { /* Lua function */
// int b = luaV_execute(L);
// if (b) L->top = ci->top;
// }
bool traced = emit_debug_trace(def, OP_CALL, pc);
// Set savedpc before the call
if (!traced) emit_update_savedpc(def, pc);
emit_load_base(def);
// int nresults = c - 1;
int nresults = C - 1;
// if (b != 0)
if (B != 0) {
// L->top = ra + b; /* else previous instruction set top */
emit_set_L_top_toreg(def, A + B);
}
// luaD_precall() returns following
// 1 - C function called, results to be adjusted
// 2 - JITed Lua function called, no action
// 0 - Run interpreter on Lua function, if returns != 0 then update L->top
// int c = luaD_precall(L, ra, nresults, op_call); /* C or JITed function? */
// The last parameter to luaD_precall() tells it that
// this is an OP_CALL
llvm::Value *ra = emit_gep_register(def, A);
llvm::Value *precall_result =
CreateCall4(def->builder, def->luaD_precallF, def->L, ra,
llvm::ConstantInt::get(def->types->C_intT, nresults),
def->types->kInt[1]);
// If luaD_precall() returns 0 then we need to interpret the
// Lua function
llvm::Value *do_Lua_interp =
def->builder->CreateICmpEQ(precall_result, def->types->kInt[0]);
llvm::BasicBlock *then_block = llvm::BasicBlock::Create(
def->jitState->context(), "OP_CALL_if_Lua_interp_function", def->f);
llvm::BasicBlock *else_block = llvm::BasicBlock::Create(
def->jitState->context(), "OP_CALL_if_not_Lua_interp_function");
llvm::BasicBlock *end_block =
llvm::BasicBlock::Create(def->jitState->context(), "OP_CALL_done");
def->builder->CreateCondBr(do_Lua_interp, then_block, else_block);
def->builder->SetInsertPoint(then_block);
// Lua function, not compiled, so call luaV_execute
llvm::Value *b = def->builder->CreateCall(def->luaV_executeF, def->L);
// If the return value is non zero then we need to refresh L->top = ci->top
llvm::Value *b_not_zero = def->builder->CreateICmpNE(b, def->types->kInt[0]);
llvm::BasicBlock *if_b_block = llvm::BasicBlock::Create(
def->jitState->context(), "OP_CALL_if_need_reset_L_top", def->f);
def->builder->CreateCondBr(b_not_zero, if_b_block, end_block);
def->builder->SetInsertPoint(if_b_block);
// Set L->top = ci->top
emit_refresh_L_top(def, def->ci_val);
// We are done
def->builder->CreateBr(end_block);
// Handle the C function or JIT case
// function already executed by luaD_precall()
def->f->getBasicBlockList().push_back(else_block);
def->builder->SetInsertPoint(else_block);
if (nresults >= 0) {
// In case the precall returned 1 then a C function was
// called so we need to update L->top
// if (c == 1 && nresults >= 0)
// L->top = ci->top; /* adjust results if C function */
llvm::Value *precall_C =
def->builder->CreateICmpEQ(precall_result, def->types->kInt[1]);
llvm::BasicBlock *then1_block = llvm::BasicBlock::Create(
def->jitState->context(), "OP_CALL_if_C_function_returned_values",
def->f);
def->builder->CreateCondBr(precall_C, then1_block, end_block);
def->builder->SetInsertPoint(then1_block);
emit_refresh_L_top(def, def->ci_val);
}
def->builder->CreateBr(end_block);
def->f->getBasicBlockList().push_back(end_block);
def->builder->SetInsertPoint(end_block);
}
#ifdef RAVI_DEFER_STATEMENT
void RaviCodeGenerator::emit_DEFER(RaviFunctionDef *def, int A, int pc) {
emit_debug_trace(def, OP_RAVI_DEFER, pc);
emit_load_base(def);
llvm::Value *ra = emit_gep_register(def, A);
CreateCall2(def->builder, def->raviV_op_deferF, def->L, ra);
}
#endif
}

File diff suppressed because it is too large Load Diff

@ -1,339 +0,0 @@
/******************************************************************************
* Copyright (C) 2015-2020 Dibyendu Majumdar
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
******************************************************************************/
#include "ravi_llvmcodegen.h"
namespace ravi {
// Although the name is EQ this actually
// implements EQ, LE and LT - by using the supplied lua function to call.
void RaviCodeGenerator::emit_EQ(RaviFunctionDef *def, int A, int B, int C,
int j, int jA, llvm::Constant *callee,
OpCode opCode, int pc) {
// case OP_EQ: {
// TValue *rb = RKB(i);
// TValue *rc = RKC(i);
// Protect(
// if (cast_int(luaV_equalobj(L, rb, rc)) != GETARG_A(i))
// ci->u.l.savedpc++;
// else
// donextjump(ci);
// )
// } break;
bool traced = emit_debug_trace(def, opCode, pc);
// Load pointer to base
emit_load_base(def);
// Get pointer to register B
llvm::Value *regB = emit_gep_register_or_constant(def, B);
// Get pointer to register C
llvm::Value *regC = emit_gep_register_or_constant(def, C);
llvm::Value *result = NULL;
switch (opCode) {
case OP_RAVI_LT_II:
case OP_RAVI_LE_II:
case OP_RAVI_EQ_II: {
llvm::Instruction *p1 = emit_load_reg_i(def, regB);
llvm::Instruction *p2 = emit_load_reg_i(def, regC);
switch (opCode) {
case OP_RAVI_EQ_II:
result = def->builder->CreateICmpEQ(p1, p2, "EQ_II_result");
break;
case OP_RAVI_LT_II:
result = def->builder->CreateICmpSLT(p1, p2, "LT_II_result");
break;
case OP_RAVI_LE_II:
result = def->builder->CreateICmpSLE(p1, p2, "LE_II_result");
break;
default:
assert(0);
}
result =
def->builder->CreateZExt(result, def->types->C_intT, "II_result_int");
} break;
case OP_RAVI_LT_FF:
case OP_RAVI_LE_FF:
case OP_RAVI_EQ_FF: {
llvm::Instruction *p1 = emit_load_reg_n(def, regB);
llvm::Instruction *p2 = emit_load_reg_n(def, regC);
switch (opCode) {
case OP_RAVI_EQ_FF:
result = def->builder->CreateFCmpOEQ(p1, p2, "EQ_FF_result");
break;
case OP_RAVI_LT_FF:
result = def->builder->CreateFCmpULT(p1, p2, "LT_FF_result");
break;
case OP_RAVI_LE_FF:
result = def->builder->CreateFCmpULE(p1, p2, "LE_FF_result");
break;
default:
assert(0);
}
result =
def->builder->CreateZExt(result, def->types->C_intT, "FF_result_int");
} break;
default:
// As below may invoke metamethod we need to set savedpc
if (!traced) emit_update_savedpc(def, pc);
// Call luaV_equalobj with register B and C
result = CreateCall3(def->builder, callee, def->L, regB, regC);
}
// Test if result is equal to operand A
llvm::Value *result_eq_A = def->builder->CreateICmpEQ(
result, llvm::ConstantInt::get(def->types->C_intT, A));
// If result == A then we need to execute the next statement which is a jump
llvm::BasicBlock *then_block =
llvm::BasicBlock::Create(def->jitState->context(), "if.then", def->f);
llvm::BasicBlock *else_block =
llvm::BasicBlock::Create(def->jitState->context(), "if.else");
def->builder->CreateCondBr(result_eq_A, then_block, else_block);
def->builder->SetInsertPoint(then_block);
// if (a > 0) luaF_close(L, ci->u.l.base + a - 1);
if (jA > 0) {
// jA is the A operand of the Jump instruction
// Reload pointer to base as the call to luaV_equalobj() may
// have invoked a Lua function and as a result the stack may have
// been reallocated - so the previous base pointer could be stale
emit_load_base(def);
// base + a - 1
llvm::Value *val = emit_gep_register(def, jA - 1);
#ifdef RAVI_DEFER_STATEMENT
if (!traced)
emit_update_savedpc(def, pc);
// Call luaF_close
CreateCall3(def->builder, def->luaF_closeF, def->L, val, def->types->kInt[LUA_OK]);
#else
CreateCall2(def->builder, def->luaF_closeF, def->L, val);
#endif
}
// Do the jump
def->builder->CreateBr(def->jmp_targets[j].jmp1);
// Add the else block and make it current so that the next instruction flows
// here
def->f->getBasicBlockList().push_back(else_block);
def->builder->SetInsertPoint(else_block);
}
llvm::Value *RaviCodeGenerator::emit_boolean_testfalse(RaviFunctionDef *def,
llvm::Value *reg,
bool donot) {
// (isnil() || isbool() && b == 0)
llvm::IRBuilder<> TmpB(def->entry, def->entry->begin());
llvm::Value *var = TmpB.CreateAlloca(
llvm::Type::getInt1Ty(def->jitState->context()), nullptr, "b");
llvm::Value *type = emit_load_type(def, reg);
// Test if type == LUA_TNIL (0)
llvm::Value *isnil = emit_is_value_of_type(def, type, LUA__TNIL, "is.nil");
llvm::BasicBlock *then_block =
llvm::BasicBlock::Create(def->jitState->context(), "if.nil", def->f);
llvm::BasicBlock *else_block =
llvm::BasicBlock::Create(def->jitState->context(), "not.nil");
llvm::BasicBlock *end_block =
llvm::BasicBlock::Create(def->jitState->context(), "end");
def->builder->CreateCondBr(isnil, then_block, else_block);
def->builder->SetInsertPoint(then_block);
emit_store_local_int(def, isnil, var);
def->builder->CreateBr(end_block);
def->f->getBasicBlockList().push_back(else_block);
def->builder->SetInsertPoint(else_block);
// value is not nil
// so check if bool and b == 0
// Test if type == LUA_TBOOLEAN
llvm::Value *isbool =
emit_is_value_of_type(def, type, LUA__TBOOLEAN, "is.boolean");
// Test if bool value == 0
llvm::Value *bool_value = emit_load_reg_b(def, reg);
llvm::Value *boolzero =
def->builder->CreateICmpEQ(bool_value, def->types->kInt[0]);
// Test type == LUA_TBOOLEAN && bool value == 0
llvm::Value *andvalue = def->builder->CreateAnd(isbool, boolzero);
emit_store_local_int(def, andvalue, var);
def->builder->CreateBr(end_block);
def->f->getBasicBlockList().push_back(end_block);
def->builder->SetInsertPoint(end_block);
llvm::Value *result = nullptr;
if (donot) {
auto ins = emit_load_local_int(def, var);
result = def->builder->CreateNot(ins);
} else {
auto ins = emit_load_local_int(def, var);
result = ins;
}
return result;
}
void RaviCodeGenerator::emit_TEST(RaviFunctionDef *def, int A, int B, int C,
int j, int jA, int pc) {
// case OP_TEST: {
// if (GETARG_C(i) ? l_isfalse(ra) : !l_isfalse(ra))
// ci->u.l.savedpc++;
// else
// donextjump(ci);
// } break;
bool traced = emit_debug_trace(def, OP_TEST, pc);
// Load pointer to base
emit_load_base(def);
// Get pointer to register A
llvm::Value *ra = emit_gep_register(def, A);
// v = C ? is_false(ra) : !is_false(ra)
llvm::Value *v = C ? emit_boolean_testfalse(def, ra, false)
: emit_boolean_testfalse(def, ra, true);
// Test NOT v
llvm::Value *result = def->builder->CreateNot(v);
// If !v then we need to execute the next statement which is a jump
llvm::BasicBlock *then_block =
llvm::BasicBlock::Create(def->jitState->context(), "if.then", def->f);
llvm::BasicBlock *else_block =
llvm::BasicBlock::Create(def->jitState->context(), "if.else");
def->builder->CreateCondBr(result, then_block, else_block);
def->builder->SetInsertPoint(then_block);
// if (a > 0) luaF_close(L, ci->u.l.base + a - 1);
if (jA > 0) {
// jA is the A operand of the Jump instruction
// base + a - 1
llvm::Value *val = emit_gep_register(def, jA - 1);
#ifdef RAVI_DEFER_STATEMENT
if (!traced)
emit_update_savedpc(def, pc);
// Call luaF_close
CreateCall3(def->builder, def->luaF_closeF, def->L, val, def->types->kInt[LUA_OK]);
#else
CreateCall2(def->builder, def->luaF_closeF, def->L, val);
#endif
}
// Do the jump
def->builder->CreateBr(def->jmp_targets[j].jmp1);
// Add the else block and make it current so that the next instruction flows
// here
def->f->getBasicBlockList().push_back(else_block);
def->builder->SetInsertPoint(else_block);
}
void RaviCodeGenerator::emit_NOT(RaviFunctionDef *def, int A, int B, int pc) {
// case OP_NOT: {
// TValue *rb = RB(i);
// int res = l_isfalse(rb); /* next assignment may change this value */
// setbvalue(ra, res);
// } break;
emit_debug_trace(def, OP_NOT, pc);
emit_load_base(def);
// Get pointer to register B
llvm::Value *rb = emit_gep_register(def, B);
llvm::Value *v = emit_boolean_testfalse(def, rb, false);
llvm::Value *result = def->builder->CreateZExt(v, def->types->C_intT, "i");
llvm::Value *ra = emit_gep_register(def, A);
emit_store_reg_b_withtype(def, result, ra);
}
void RaviCodeGenerator::emit_TESTSET(RaviFunctionDef *def, int A, int B, int C,
int j, int jA, int pc) {
// case OP_TESTSET: {
// TValue *rb = RB(i);
// if (GETARG_C(i) ? l_isfalse(rb) : !l_isfalse(rb))
// ci->u.l.savedpc++;
// else {
// setobjs2s(L, ra, rb);
// donextjump(ci);
// }
// } break;
bool traced = emit_debug_trace(def, OP_TESTSET, pc);
// Load pointer to base
emit_load_base(def);
// Get pointer to register B
llvm::Value *rb = emit_gep_register(def, B);
// v = C ? is_false(ra) : !is_false(ra)
llvm::Value *v = C ? emit_boolean_testfalse(def, rb, false)
: emit_boolean_testfalse(def, rb, true);
// Test NOT v
llvm::Value *result = def->builder->CreateNot(v);
// If !v then we need to execute the next statement which is a jump
llvm::BasicBlock *then_block =
llvm::BasicBlock::Create(def->jitState->context(), "if.then", def->f);
llvm::BasicBlock *else_block =
llvm::BasicBlock::Create(def->jitState->context(), "if.else");
def->builder->CreateCondBr(result, then_block, else_block);
def->builder->SetInsertPoint(then_block);
// Get pointer to register A
llvm::Value *ra = emit_gep_register(def, A);
emit_assign(def, ra, rb);
// if (a > 0) luaF_close(L, ci->u.l.base + a - 1);
if (jA > 0) {
// jA is the A operand of the Jump instruction
// base + a - 1
llvm::Value *val = emit_gep_register(def, jA - 1);
#ifdef RAVI_DEFER_STATEMENT
if (!traced)
emit_update_savedpc(def, pc);
// Call luaF_close
CreateCall3(def->builder, def->luaF_closeF, def->L, val, def->types->kInt[LUA_OK]);
#else
CreateCall2(def->builder, def->luaF_closeF, def->L, val);
#endif
}
// Do the jump
def->builder->CreateBr(def->jmp_targets[j].jmp1);
// Add the else block and make it current so that the next instruction flows
// here
def->f->getBasicBlockList().push_back(else_block);
def->builder->SetInsertPoint(else_block);
}
}

@ -1,454 +0,0 @@
/******************************************************************************
* Copyright (C) 2015-2020 Dibyendu Majumdar
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
******************************************************************************/
#include "ravi_llvmcodegen.h"
namespace ravi {
void RaviCodeGenerator::emit_FORLOOP2(RaviFunctionDef *def, int A, int pc,
RaviBranchDef &b, int pc1) {
// 7[1] FORLOOP 1 - 2; to 6
// if (ttisinteger(ra)) { /* integer loop? */
// lua_Integer step = ivalue(ra + 2);
// lua_Integer idx = ivalue(ra) + step; /* increment index */
// lua_Integer limit = ivalue(ra + 1);
// if ((0 < step) ? (idx <= limit) : (limit <= idx)) {
// ci->u.l.savedpc += GETARG_sBx(i); /* jump back */
// setivalue(ra, idx); /* update internal index... */
// setivalue(ra + 3, idx); /* ...and external index */
// }
//}
// else { /* floating loop */
// lua_Number step = fltvalue(ra + 2);
// lua_Number idx = luai_numadd(L, fltvalue(ra), step); /* inc. index */
// lua_Number limit = fltvalue(ra + 1);
// if (luai_numlt(0, step) ? luai_numle(idx, limit)
// : luai_numle(limit, idx)) {
// ci->u.l.savedpc += GETARG_sBx(i); /* jump back */
// setfltvalue(ra, idx); /* update internal index... */
// setfltvalue(ra + 3, idx); /* ...and external index */
// }
//}
emit_debug_trace(def, OP_FORLOOP, pc1);
// We are in b.jmp1 as this is already the current block
lua_assert(def->builder->GetInsertBlock() == b.jmp1);
// Obtain pointers to the value.i field
llvm::Value *step_int_ptr = b.istep;
llvm::Value *idx_int_ptr = b.iidx;
llvm::Value *limit_int_ptr = b.ilimit;
// Obtain pointers to the value.n field
llvm::Value *step_double_ptr = b.fstep;
llvm::Value *idx_double_ptr = b.fidx;
llvm::Value *limit_double_ptr = b.flimit;
// Create the done block
llvm::BasicBlock *exit_block =
llvm::BasicBlock::Create(def->jitState->context(), "exit_iforloop");
// INTEGER CASE
// lua_Integer step = ivalue(ra + 2);
llvm::Instruction *step_int_value = emit_load_local_n(def, step_int_ptr);
// lua_Integer idx = ivalue(ra) + step; /* increment index */
llvm::Instruction *idx_int_value = emit_load_local_n(def, idx_int_ptr);
llvm::Value *new_idx = def->builder->CreateAdd(step_int_value, idx_int_value,
"next.idx", false, true);
emit_store_local_n(def, new_idx, idx_int_ptr);
// lua_Integer limit = ivalue(ra + 1);
llvm::Instruction *limit_int_value = emit_load_local_n(def, limit_int_ptr);
// idx > limit?
llvm::Value *new_idx_gt_limit =
def->builder->CreateICmpSGT(new_idx, limit_int_value, "idx.gt.limit");
// If idx > limit we are done
llvm::BasicBlock *update_block =
llvm::BasicBlock::Create(def->jitState->context(), "updatei");
def->builder->CreateCondBr(new_idx_gt_limit, exit_block, update_block);
// NOW INTEGER step < 0
def->f->getBasicBlockList().push_back(b.jmp2);
def->builder->SetInsertPoint(b.jmp2);
step_int_value = emit_load_local_n(def, step_int_ptr);
// lua_Integer idx = ivalue(ra) + step; /* increment index */
idx_int_value = emit_load_local_n(def, idx_int_ptr);
new_idx = def->builder->CreateAdd(step_int_value, idx_int_value, "next.idx",
false, true);
emit_store_local_n(def, new_idx, idx_int_ptr);
// lua_Integer limit = ivalue(ra + 1);
limit_int_value = emit_load_local_n(def, limit_int_ptr);
// limit > idx?
llvm::Value *limit_gt_idx =
def->builder->CreateICmpSGT(limit_int_value, new_idx, "limit.gt.idx");
// If limit > idx we are done
def->builder->CreateCondBr(limit_gt_idx, exit_block, update_block);
// Merge into update block
def->f->getBasicBlockList().push_back(update_block);
def->builder->SetInsertPoint(update_block);
emit_load_base(def);
llvm::Value *rvar = emit_gep_register(def, A + 3);
// setivalue(ra + 3, idx); /* ...and external index */
idx_int_value = emit_load_local_n(def, idx_int_ptr);
emit_store_reg_i_withtype(def, idx_int_value, rvar);
// ci->u.l.savedpc += GETARG_sBx(i); /* jump back */
def->builder->CreateBr(def->jmp_targets[pc].jmp1);
// FLOATING CASE
def->f->getBasicBlockList().push_back(b.jmp3);
def->builder->SetInsertPoint(b.jmp3);
// lua_Number step = fltvalue(ra + 2);
llvm::Instruction *step_double_value =
emit_load_local_n(def, step_double_ptr);
// lua_Number idx = luai_numadd(L, fltvalue(ra), step); /* inc. index */
llvm::Instruction *idx_double_value = emit_load_local_n(def, idx_double_ptr);
new_idx =
def->builder->CreateFAdd(step_double_value, idx_double_value, "next.idx");
emit_store_local_n(def, new_idx, idx_double_ptr);
// lua_Number limit = fltvalue(ra + 1);
llvm::Instruction *limit_double_value =
emit_load_local_n(def, limit_double_ptr);
// step > 0?
// idx > limit?
new_idx_gt_limit =
def->builder->CreateFCmpOGT(new_idx, limit_double_value, "idx.gt.limit");
// If idx > limit we are done
update_block = llvm::BasicBlock::Create(def->jitState->context(), "updatef");
def->builder->CreateCondBr(new_idx_gt_limit, exit_block, update_block);
def->f->getBasicBlockList().push_back(b.jmp4);
def->builder->SetInsertPoint(b.jmp4);
// lua_Number step = fltvalue(ra + 2);
step_double_value = emit_load_local_n(def, step_double_ptr);
// lua_Number idx = luai_numadd(L, fltvalue(ra), step); /* inc. index */
idx_double_value = emit_load_local_n(def, idx_double_ptr);
new_idx =
def->builder->CreateFAdd(step_double_value, idx_double_value, "next.idx");
emit_store_local_n(def, new_idx, idx_double_ptr);
// lua_Number limit = fltvalue(ra + 1);
limit_double_value = emit_load_local_n(def, limit_double_ptr);
// limit > idx?
limit_gt_idx =
def->builder->CreateFCmpOGT(limit_double_value, new_idx, "limit.gt.idx");
// If limit > idx we are done
def->builder->CreateCondBr(limit_gt_idx, exit_block, update_block);
// Merge into update block
def->f->getBasicBlockList().push_back(update_block);
def->builder->SetInsertPoint(update_block);
emit_load_base(def);
rvar = emit_gep_register(def, A + 3);
// setfltvalue(ra + 3, idx); /* ...and external index */
idx_double_value = emit_load_local_n(def, idx_double_ptr);
emit_store_reg_n_withtype(def, idx_double_value, rvar);
// ci->u.l.savedpc += GETARG_sBx(i); /* jump back */
def->builder->CreateBr(def->jmp_targets[pc].jmp1);
def->f->getBasicBlockList().push_back(exit_block);
def->builder->SetInsertPoint(exit_block);
}
void RaviCodeGenerator::emit_FORLOOP(RaviFunctionDef *def, int A, int pc,
int pc1) {
// 7[1] FORLOOP 1 - 2; to 6
// if (ttisinteger(ra)) { /* integer loop? */
// lua_Integer step = ivalue(ra + 2);
// lua_Integer idx = ivalue(ra) + step; /* increment index */
// lua_Integer limit = ivalue(ra + 1);
// if ((0 < step) ? (idx <= limit) : (limit <= idx)) {
// ci->u.l.savedpc += GETARG_sBx(i); /* jump back */
// setivalue(ra, idx); /* update internal index... */
// setivalue(ra + 3, idx); /* ...and external index */
// }
//}
// else { /* floating loop */
// lua_Number step = fltvalue(ra + 2);
// lua_Number idx = luai_numadd(L, fltvalue(ra), step); /* inc. index */
// lua_Number limit = fltvalue(ra + 1);
// if (luai_numlt(0, step) ? luai_numle(idx, limit)
// : luai_numle(limit, idx)) {
// ci->u.l.savedpc += GETARG_sBx(i); /* jump back */
// setfltvalue(ra, idx); /* update internal index... */
// setfltvalue(ra + 3, idx); /* ...and external index */
// }
//}
emit_debug_trace(def, OP_FORLOOP, pc1);
// Load pointer to base
emit_load_base(def);
// TValue *rinit = ra;
// TValue *rlimit = ra + 1;
// TValue *rstep = ra + 2;
// TValue *rvar = ra + 3
llvm::Value *rinit = emit_gep_register(def, A);
llvm::Value *rlimit = emit_gep_register(def, A + 1);
llvm::Value *rstep = emit_gep_register(def, A + 2);
llvm::Value *rvar = emit_gep_register(def, A + 3);
// Create the done block
llvm::BasicBlock *exit_block =
llvm::BasicBlock::Create(def->jitState->context(), "exit_forloop");
// Is index an integer?
llvm::Instruction *rinit_tt = emit_load_type(def, rinit);
llvm::Value *cmp1 =
emit_is_value_of_type(def, rinit_tt, LUA__TNUMINT, "init.is.integer");
// Setup if then else branch for integer
llvm::BasicBlock *if_integer =
llvm::BasicBlock::Create(def->jitState->context(), "if.integer", def->f);
llvm::BasicBlock *else_integer =
llvm::BasicBlock::Create(def->jitState->context(), "if.not.integer");
def->builder->CreateCondBr(cmp1, if_integer, else_integer);
def->builder->SetInsertPoint(if_integer);
// INTEGER CASE
// lua_Integer step = ivalue(ra + 2);
llvm::Instruction *step_int_value = emit_load_reg_i(def, rstep);
// lua_Integer idx = ivalue(ra) + step; /* increment index */
llvm::Instruction *idx_int_value = emit_load_reg_i(def, rinit);
llvm::Value *new_idx = def->builder->CreateAdd(step_int_value, idx_int_value,
"next.idx", false, true);
// lua_Integer limit = ivalue(ra + 1);
llvm::Instruction *limit_int_value = emit_load_reg_i(def, rlimit);
// step > 0?
llvm::Value *step_gt_zero = def->builder->CreateICmpSGT(
step_int_value, def->types->kluaInteger[0], "step.gt.zero");
llvm::BasicBlock *step_gt_zero_true = llvm::BasicBlock::Create(
def->jitState->context(), "step.gt.zero.true", def->f);
llvm::BasicBlock *step_gt_zero_false =
llvm::BasicBlock::Create(def->jitState->context(), "step.gt.zero.false");
def->builder->CreateCondBr(step_gt_zero, step_gt_zero_true,
step_gt_zero_false);
def->builder->SetInsertPoint(step_gt_zero_true);
// idx > limit?
llvm::Value *new_idx_gt_limit =
def->builder->CreateICmpSGT(new_idx, limit_int_value, "idx.gt.limit");
// If idx > limit we are done
llvm::BasicBlock *update_block =
llvm::BasicBlock::Create(def->jitState->context(), "update");
def->builder->CreateCondBr(new_idx_gt_limit, exit_block, update_block);
def->f->getBasicBlockList().push_back(step_gt_zero_false);
def->builder->SetInsertPoint(step_gt_zero_false);
// limit > idx?
llvm::Value *limit_gt_idx =
def->builder->CreateICmpSGT(limit_int_value, new_idx, "limit.gt.idx");
// If limit > idx we are done
def->builder->CreateCondBr(limit_gt_idx, exit_block, update_block);
// Merge into update block
def->f->getBasicBlockList().push_back(update_block);
def->builder->SetInsertPoint(update_block);
// setivalue(ra, idx); /* update internal index... */
emit_store_reg_i(def, new_idx, rinit);
// setivalue(ra + 3, idx); /* ...and external index */
emit_store_reg_i_withtype(def, new_idx, rvar);
// ci->u.l.savedpc += GETARG_sBx(i); /* jump back */
def->builder->CreateBr(def->jmp_targets[pc].jmp1);
// FLOATING CASE
def->f->getBasicBlockList().push_back(else_integer);
def->builder->SetInsertPoint(else_integer);
// lua_Number step = fltvalue(ra + 2);
llvm::Instruction *step_double_value = emit_load_reg_n(def, rstep);
// lua_Number idx = luai_numadd(L, fltvalue(ra), step); /* inc. index */
llvm::Instruction *idx_double_value = emit_load_reg_n(def, rinit);
new_idx =
def->builder->CreateFAdd(step_double_value, idx_double_value, "next.idx");
// lua_Number limit = fltvalue(ra + 1);
llvm::Instruction *limit_double_value = emit_load_reg_n(def, rlimit);
// step > 0?
step_gt_zero = def->builder->CreateFCmpOGT(
step_double_value, llvm::ConstantFP::get(def->types->lua_NumberT, 0.0),
"step.gt.zero");
step_gt_zero_true = llvm::BasicBlock::Create(def->jitState->context(),
"step.gt.zero.true", def->f);
step_gt_zero_false =
llvm::BasicBlock::Create(def->jitState->context(), "step.gt.zero.false");
def->builder->CreateCondBr(step_gt_zero, step_gt_zero_true,
step_gt_zero_false);
def->builder->SetInsertPoint(step_gt_zero_true);
// idx > limit?
new_idx_gt_limit =
def->builder->CreateFCmpOGT(new_idx, limit_double_value, "idx.gt.limit");
// If idx > limit we are done
update_block = llvm::BasicBlock::Create(def->jitState->context(), "update");
def->builder->CreateCondBr(new_idx_gt_limit, exit_block, update_block);
def->f->getBasicBlockList().push_back(step_gt_zero_false);
def->builder->SetInsertPoint(step_gt_zero_false);
// limit > idx?
limit_gt_idx =
def->builder->CreateFCmpOGT(limit_double_value, new_idx, "limit.gt.idx");
// If limit > idx we are done
def->builder->CreateCondBr(limit_gt_idx, exit_block, update_block);
// Merge into update block
def->f->getBasicBlockList().push_back(update_block);
def->builder->SetInsertPoint(update_block);
// setfltvalue(ra, idx); /* update internal index... */
emit_store_reg_n(def, new_idx, rinit);
// setfltvalue(ra + 3, idx); /* ...and external index */
emit_store_reg_n_withtype(def, new_idx, rvar);
// ci->u.l.savedpc += GETARG_sBx(i); /* jump back */
def->builder->CreateBr(def->jmp_targets[pc].jmp1);
def->f->getBasicBlockList().push_back(exit_block);
def->builder->SetInsertPoint(exit_block);
}
void RaviCodeGenerator::emit_iFORLOOP(RaviFunctionDef *def, int A, int pc,
RaviBranchDef &b, int step_one, int pc1) {
// lua_Integer step = ivalue(ra + 2);
// lua_Integer idx = ivalue(ra) + step; /* increment index */
// lua_Integer limit = ivalue(ra + 1);
// if (idx <= limit) {
// ci->u.l.savedpc += GETARG_sBx(i); /* jump back */
// setivalue(ra, idx); /* update internal index... */
// setivalue(ra + 3, idx); /* ...and external index */
// }
emit_debug_trace(def, step_one ? OP_RAVI_FORLOOP_I1 : OP_RAVI_FORLOOP_IP,
pc1);
// We are in b.jmp1 as this is already the current block
lua_assert(def->builder->GetInsertBlock() == b.jmp1);
// Obtain pointers to the value.i field
llvm::Value *idx_int_ptr = b.iidx;
llvm::Value *limit_int_ptr = b.ilimit;
// Create the done block
llvm::BasicBlock *exit_block =
llvm::BasicBlock::Create(def->jitState->context(), "exit_iforloop");
// lua_Integer idx = ivalue(ra) + step; /* increment index */
llvm::Instruction *idx_int_value = emit_load_local_n(def, idx_int_ptr);
llvm::Value *new_idx;
if (!step_one) {
// lua_Integer step = ivalue(ra + 2);
llvm::Value *step_int_ptr = b.istep;
llvm::Instruction *step_int_value = emit_load_local_n(def, step_int_ptr);
new_idx = def->builder->CreateAdd(step_int_value, idx_int_value, "next.idx",
false, true);
} else
new_idx = def->builder->CreateAdd(def->types->kluaInteger[1], idx_int_value,
"next.idx", false, true);
// save new index
emit_store_local_n(def, new_idx, idx_int_ptr);
// lua_Integer limit = ivalue(ra + 1);
llvm::Instruction *limit_int_value = emit_load_local_n(def, limit_int_ptr);
// idx > limit?
llvm::Value *new_idx_gt_limit =
def->builder->CreateICmpSGT(new_idx, limit_int_value, "idx.gt.limit");
// If idx > limit we are done
llvm::BasicBlock *update_block =
llvm::BasicBlock::Create(def->jitState->context(), "updatei");
def->builder->CreateCondBr(new_idx_gt_limit, exit_block, update_block);
// Merge into update block
def->f->getBasicBlockList().push_back(update_block);
def->builder->SetInsertPoint(update_block);
// Load pointer to base
emit_load_base(def);
llvm::Value *rvar = emit_gep_register(def, A + 3);
// setivalue(ra + 3, idx); /* ...and external index */
idx_int_value = emit_load_local_n(def, idx_int_ptr);
emit_store_reg_i_withtype(def, idx_int_value, rvar);
// ci->u.l.savedpc += GETARG_sBx(i); /* jump back */
def->builder->CreateBr(def->jmp_targets[pc].jmp1);
def->f->getBasicBlockList().push_back(exit_block);
def->builder->SetInsertPoint(exit_block);
}
}

@ -1,764 +0,0 @@
/******************************************************************************
* Copyright (C) 2015-2020 Dibyendu Majumdar
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
******************************************************************************/
#include "ravi_llvmcodegen.h"
namespace ravi {
void RaviCodeGenerator::emit_FORPREP2(RaviFunctionDef *def, int A, int pc,
int pc1) {
// Create additional forloop targets
// First target is for int < limit
RaviBranchDef &forloop_target = def->jmp_targets[pc];
// case OP_FORPREP: {
// if (ttisinteger(init) && ttisinteger(pstep) &&
// forlimit(plimit, &ilimit, ivalue(pstep), &stopnow)) {
// /* all values are integer */
// lua_Integer initv = (stopnow ? 0 : ivalue(init));
// setivalue(plimit, ilimit);
// setivalue(init, initv - ivalue(pstep));
// }
// else { /* try making all values floats */
// if (!tonumber(plimit, &nlimit))
// luaG_runerror(L, "'for' limit must be a number");
// setfltvalue(plimit, nlimit);
// if (!tonumber(pstep, &nstep))
// luaG_runerror(L, "'for' step must be a number");
// setfltvalue(pstep, nstep);
// if (!tonumber(init, &ninit))
// luaG_runerror(L, "'for' initial value must be a number");
// setfltvalue(init, luai_numsub(L, ninit, nstep));
// }
// ci->u.l.savedpc += GETARG_sBx(i);
//} break;
emit_debug_trace(def, OP_FORPREP, pc1);
// Load pointer to base
emit_load_base(def);
// lua_Integer ilimit;
// int stopnow;
// lua_Number ninit; lua_Number nlimit; lua_Number nstep;
// Setup local vars on C stack and link them
// to the forloop_target so that the forloop code can access these
llvm::IRBuilder<> TmpB(def->entry, def->entry->begin());
llvm::Value *stopnow =
TmpB.CreateAlloca(def->types->C_intT, nullptr, "stopnow");
forloop_target.ilimit =
TmpB.CreateAlloca(def->types->lua_IntegerT, nullptr, "ilimit");
forloop_target.istep =
TmpB.CreateAlloca(def->types->lua_IntegerT, nullptr, "istep");
forloop_target.iidx =
TmpB.CreateAlloca(def->types->lua_IntegerT, nullptr, "iidx");
forloop_target.flimit =
TmpB.CreateAlloca(def->types->lua_NumberT, nullptr, "nlimit");
forloop_target.fidx =
TmpB.CreateAlloca(def->types->lua_NumberT, nullptr, "ninit");
forloop_target.fstep =
TmpB.CreateAlloca(def->types->lua_NumberT, nullptr, "nstep");
forloop_target.forloop_branch =
TmpB.CreateAlloca(def->types->C_pcharT, nullptr, "brnch");
llvm::Value *isinc = nullptr;
// TValue *init = ra;
// TValue *plimit = ra + 1;
// TValue *pstep = ra + 2;
llvm::Value *init = emit_gep_register(def, A);
llvm::Value *plimit = emit_gep_register(def, A + 1);
llvm::Value *pstep = emit_gep_register(def, A + 2);
// if (ttisinteger(init) && ttisinteger(pstep) &&
// forlimit(plimit, &ilimit, ivalue(pstep), &stopnow)) {
// Get init->tt
llvm::Instruction *pinit_tt = emit_load_type(def, init);
// Compare init->tt == LUA_TNUMINT
llvm::Value *cmp1 =
emit_is_value_of_type(def, pinit_tt, LUA__TNUMINT, "init.is.integer");
// Get pstep->tt
llvm::Instruction *pstep_tt = emit_load_type(def, pstep);
// Compare pstep->tt == LUA_TNUMINT
llvm::Value *icmp2 =
emit_is_value_of_type(def, pstep_tt, LUA__TNUMINT, "step.is.integer");
// Get ivalue(pstep)
llvm::Instruction *pstep_ivalue = emit_load_reg_i(def, pstep);
// Call forlimit()
llvm::Value *forlimit_ret =
CreateCall4(def->builder, def->luaV_forlimitF, plimit,
forloop_target.ilimit, pstep_ivalue, stopnow);
// Is init->tt == LUA_TNUMINT && pstep->tt == LUA_TNUMINT
llvm::Value *and1 =
def->builder->CreateAnd(cmp1, icmp2, "init.and.step.are.integers");
// Convert result from forlimit() to bool
llvm::Value *tobool =
def->builder->CreateICmpNE(forlimit_ret, def->types->kInt[0]);
// Are all vars integers?
// init->tt == LUA_TNUMINT && pstep->tt == LUA_TNUMINT && forlimit()
llvm::Value *and2 = def->builder->CreateAnd(and1, tobool, "all.integers");
// Create if then else branch
llvm::BasicBlock *then1 = llvm::BasicBlock::Create(def->jitState->context(),
"if.all.integers", def->f);
llvm::BasicBlock *else1 =
llvm::BasicBlock::Create(def->jitState->context(), "if.not.all.integers");
def->builder->CreateCondBr(and2, then1, else1);
def->builder->SetInsertPoint(then1);
// all values are integers
// lua_Integer initv = (stopnow ? 0 : ivalue(init));
// Save step
emit_store_local_n(def, pstep_ivalue, forloop_target.istep);
// Get stopnow
llvm::Instruction *stopnow_val = emit_load_local_int(def, stopnow);
// Test if stopnow is 0
llvm::Value *stopnow_is_zero = def->builder->CreateICmpEQ(
stopnow_val, def->types->kInt[0], "stopnow.is.zero");
// Get ptr to init->i
// Setup if then else branch for stopnow
llvm::BasicBlock *then1_iffalse = llvm::BasicBlock::Create(
def->jitState->context(), "if.stopnow.iszero", def->f);
llvm::BasicBlock *then1_iftrue =
llvm::BasicBlock::Create(def->jitState->context(), "if.stopnow.notzero");
def->builder->CreateCondBr(stopnow_is_zero, then1_iffalse, then1_iftrue);
def->builder->SetInsertPoint(then1_iffalse);
// stopnow is 0
// Get init->i
llvm::Instruction *init_ivalue = emit_load_reg_i(def, init);
// Join after the branch
def->builder->CreateBr(then1_iftrue);
def->f->getBasicBlockList().push_back(then1_iftrue);
def->builder->SetInsertPoint(then1_iftrue);
// Set initv to 0 if !stopnow else init->i
auto phi1 = def->builder->CreatePHI(def->types->lua_IntegerT, 2, "initv");
phi1->addIncoming(init_ivalue, then1_iffalse);
phi1->addIncoming(def->types->kluaInteger[0], then1);
// setivalue(init, initv - ivalue(pstep));
// we aleady know init is LUA_TNUMINT
llvm::Value *sub =
def->builder->CreateSub(phi1, pstep_ivalue, "initv-pstep.i", false, true);
emit_store_local_n(def, sub, forloop_target.iidx);
// Ok so now we need to decide which jump target
isinc = def->builder->CreateICmpSGT(pstep_ivalue, def->types->kluaInteger[0],
"step.gt.zero");
// Create if then else branch
llvm::BasicBlock *b1 =
llvm::BasicBlock::Create(def->jitState->context(), "b1", def->f);
llvm::BasicBlock *b2 =
llvm::BasicBlock::Create(def->jitState->context(), "b2");
llvm::BasicBlock *b3 =
llvm::BasicBlock::Create(def->jitState->context(), "b3");
def->builder->CreateCondBr(isinc, b1, b2);
def->builder->SetInsertPoint(b1);
// TODO tbaa?
def->builder->CreateStore(
llvm::BlockAddress::get(def->f, forloop_target.jmp1),
forloop_target.forloop_branch);
def->builder->CreateBr(b3);
def->f->getBasicBlockList().push_back(b2);
def->builder->SetInsertPoint(b2);
// TODO tbaa?
def->builder->CreateStore(
llvm::BlockAddress::get(def->f, forloop_target.jmp2),
forloop_target.forloop_branch);
def->builder->CreateBr(b3);
def->f->getBasicBlockList().push_back(b3);
def->builder->SetInsertPoint(b3);
// Create branch
def->builder->CreateCondBr(isinc, forloop_target.jmp1, forloop_target.jmp2);
// NOW the non-integer case
def->f->getBasicBlockList().push_back(else1);
def->builder->SetInsertPoint(else1);
// ************ PLIMIT - Convert plimit to float
llvm::Instruction *plimit_tt = emit_load_type(def, plimit);
// Test if already a float
cmp1 = emit_is_value_of_type(def, plimit_tt, LUA__TNUMFLT, "limit.is.float");
llvm::BasicBlock *else1_plimit_ifnum = llvm::BasicBlock::Create(
def->jitState->context(), "if.limit.isfloat", def->f);
llvm::BasicBlock *else1_plimit_elsenum =
llvm::BasicBlock::Create(def->jitState->context(), "if.limit.notfloat");
def->builder->CreateCondBr(cmp1, else1_plimit_ifnum, else1_plimit_elsenum);
def->builder->SetInsertPoint(else1_plimit_ifnum);
// Already a float - copy to nlimit
llvm::Instruction *plimit_nvalue_load = emit_load_reg_n(def, plimit);
emit_store_local_n(def, plimit_nvalue_load, forloop_target.flimit);
// Go to the PSTEP section
llvm::BasicBlock *else1_pstep =
llvm::BasicBlock::Create(def->jitState->context(), "if.else.step");
def->builder->CreateBr(else1_pstep);
// If plimit was not already a float we need to convert
def->f->getBasicBlockList().push_back(else1_plimit_elsenum);
def->builder->SetInsertPoint(else1_plimit_elsenum);
// Call luaV_tonumber_()
llvm::Value *plimit_isnum = CreateCall2(def->builder, def->luaV_tonumberF,
plimit, forloop_target.flimit);
llvm::Value *plimit_isnum_bool = def->builder->CreateICmpEQ(
plimit_isnum, def->types->kInt[0], "limit.float.ok");
// Did conversion fail?
llvm::BasicBlock *else1_plimit_tonum_elsenum = llvm::BasicBlock::Create(
def->jitState->context(), "if.limit.float.failed", def->f);
def->builder->CreateCondBr(plimit_isnum_bool, else1_plimit_tonum_elsenum,
else1_pstep);
// Conversion failed, so raise error
def->builder->SetInsertPoint(else1_plimit_tonum_elsenum);
emit_raise_lua_error(def, "'for' limit must be a number");
def->builder->CreateBr(else1_pstep);
// Conversion OK
// Update plimit
def->f->getBasicBlockList().push_back(else1_pstep);
def->builder->SetInsertPoint(else1_pstep);
// *********** PSTEP - convert pstep to float
// Test if already a float
pstep_tt = emit_load_type(def, pstep);
cmp1 = emit_is_value_of_type(def, pstep_tt, LUA__TNUMFLT, "step.is.float");
llvm::BasicBlock *else1_pstep_ifnum = llvm::BasicBlock::Create(
def->jitState->context(), "if.step.isfloat", def->f);
llvm::BasicBlock *else1_pstep_elsenum =
llvm::BasicBlock::Create(def->jitState->context(), "if.step.notfloat");
def->builder->CreateCondBr(cmp1, else1_pstep_ifnum, else1_pstep_elsenum);
def->builder->SetInsertPoint(else1_pstep_ifnum);
// We float then copy to nstep
llvm::Instruction *pstep_nvalue_load = emit_load_reg_n(def, pstep);
emit_store_local_n(def, pstep_nvalue_load, forloop_target.fstep);
// Now go to handle initial value
llvm::BasicBlock *else1_pinit =
llvm::BasicBlock::Create(def->jitState->context(), "if.else.init");
def->builder->CreateBr(else1_pinit);
// If pstep was not already a float then we need to convert
def->f->getBasicBlockList().push_back(else1_pstep_elsenum);
def->builder->SetInsertPoint(else1_pstep_elsenum);
// call luaV_tonumber_()
llvm::Value *pstep_isnum = CreateCall2(def->builder, def->luaV_tonumberF,
pstep, forloop_target.fstep);
llvm::Value *pstep_isnum_bool = def->builder->CreateICmpEQ(
pstep_isnum, def->types->kInt[0], "step.float.ok");
llvm::BasicBlock *else1_pstep_tonum_elsenum = llvm::BasicBlock::Create(
def->jitState->context(), "if.step.float.failed", def->f);
def->builder->CreateCondBr(pstep_isnum_bool, else1_pstep_tonum_elsenum,
else1_pinit);
// If conversion failed raise error
def->builder->SetInsertPoint(else1_pstep_tonum_elsenum);
emit_raise_lua_error(def, "'for' step must be a number");
def->builder->CreateBr(else1_pinit);
// Conversion okay so update pstep
def->f->getBasicBlockList().push_back(else1_pinit);
def->builder->SetInsertPoint(else1_pinit);
// *********** PINIT finally handle initial value
// Check if it is already a float
pinit_tt = emit_load_type(def, init);
cmp1 = emit_is_value_of_type(def, pinit_tt, LUA__TNUMFLT, "init.is.float");
llvm::BasicBlock *else1_pinit_ifnum = llvm::BasicBlock::Create(
def->jitState->context(), "if.init.is.float", def->f);
llvm::BasicBlock *else1_pinit_elsenum =
llvm::BasicBlock::Create(def->jitState->context(), "if.init.not.float");
def->builder->CreateCondBr(cmp1, else1_pinit_ifnum, else1_pinit_elsenum);
def->builder->SetInsertPoint(else1_pinit_ifnum);
// Already float so copy to ninit
llvm::Instruction *pinit_nvalue_load = emit_load_reg_n(def, init);
emit_store_local_n(def, pinit_nvalue_load, forloop_target.fidx);
// Go to final section
llvm::BasicBlock *else1_pdone =
llvm::BasicBlock::Create(def->jitState->context(), "if.else.done");
def->builder->CreateBr(else1_pdone);
// Not a float so we need to convert
def->f->getBasicBlockList().push_back(else1_pinit_elsenum);
def->builder->SetInsertPoint(else1_pinit_elsenum);
// Call luaV_tonumber_()
llvm::Value *pinit_isnum =
CreateCall2(def->builder, def->luaV_tonumberF, init, forloop_target.fidx);
llvm::Value *pinit_isnum_bool = def->builder->CreateICmpEQ(
pinit_isnum, def->types->kInt[0], "init.float.ok");
llvm::BasicBlock *else1_pinit_tonum_elsenum = llvm::BasicBlock::Create(
def->jitState->context(), "if.init.float.failed", def->f);
def->builder->CreateCondBr(pinit_isnum_bool, else1_pinit_tonum_elsenum,
else1_pdone);
// Conversion failed so raise error
def->builder->SetInsertPoint(else1_pinit_tonum_elsenum);
emit_raise_lua_error(def, "'for' initial value must be a number");
def->builder->CreateBr(else1_pdone);
// Conversion OK so we are nearly done
def->f->getBasicBlockList().push_back(else1_pdone);
def->builder->SetInsertPoint(else1_pdone);
llvm::Instruction *ninit_load = emit_load_local_n(def, forloop_target.fidx);
llvm::Instruction *nstep_load = emit_load_local_n(def, forloop_target.fstep);
// setfltvalue(init, luai_numsub(L, ninit, nstep));
llvm::Value *init_n =
def->builder->CreateFSub(ninit_load, nstep_load, "ninit-nstep");
emit_store_local_n(def, init_n, forloop_target.fidx);
// Done so jump to forloop
llvm::Value *fstep_gt_zero = def->builder->CreateFCmpOGT(
nstep_load, llvm::ConstantFP::get(def->types->lua_NumberT, 0.0),
"step.gt.zero");
// Create if then else branch
b1 = llvm::BasicBlock::Create(def->jitState->context(), "b1", def->f);
b2 = llvm::BasicBlock::Create(def->jitState->context(), "b2");
b3 = llvm::BasicBlock::Create(def->jitState->context(), "b3");
def->builder->CreateCondBr(fstep_gt_zero, b1, b2);
def->builder->SetInsertPoint(b1);
def->builder->CreateStore(
llvm::BlockAddress::get(def->f, forloop_target.jmp3),
forloop_target.forloop_branch);
def->builder->CreateBr(b3);
def->f->getBasicBlockList().push_back(b2);
def->builder->SetInsertPoint(b2);
def->builder->CreateStore(
llvm::BlockAddress::get(def->f, forloop_target.jmp4),
forloop_target.forloop_branch);
def->builder->CreateBr(b3);
def->f->getBasicBlockList().push_back(b3);
def->builder->SetInsertPoint(b3);
def->builder->CreateCondBr(fstep_gt_zero, forloop_target.jmp3,
forloop_target.jmp4);
}
void RaviCodeGenerator::emit_FORPREP(RaviFunctionDef *def, int A, int pc,
int pc1) {
// case OP_FORPREP: {
// if (ttisinteger(init) && ttisinteger(pstep) &&
// forlimit(plimit, &ilimit, ivalue(pstep), &stopnow)) {
// /* all values are integer */
// lua_Integer initv = (stopnow ? 0 : ivalue(init));
// setivalue(plimit, ilimit);
// setivalue(init, initv - ivalue(pstep));
// }
// else { /* try making all values floats */
// if (!tonumber(plimit, &nlimit))
// luaG_runerror(L, "'for' limit must be a number");
// setfltvalue(plimit, nlimit);
// if (!tonumber(pstep, &nstep))
// luaG_runerror(L, "'for' step must be a number");
// setfltvalue(pstep, nstep);
// if (!tonumber(init, &ninit))
// luaG_runerror(L, "'for' initial value must be a number");
// setfltvalue(init, luai_numsub(L, ninit, nstep));
// }
// ci->u.l.savedpc += GETARG_sBx(i);
//} break;
emit_debug_trace(def, OP_FORPREP, pc1);
// Load pointer to base
emit_load_base(def);
// lua_Integer ilimit;
// int stopnow;
// lua_Number ninit; lua_Number nlimit; lua_Number nstep;
llvm::IRBuilder<> TmpB(def->entry, def->entry->begin());
llvm::Value *ilimit =
TmpB.CreateAlloca(def->types->lua_IntegerT, nullptr, "ilimit");
llvm::Value *stopnow =
TmpB.CreateAlloca(def->types->C_intT, nullptr, "stopnow");
llvm::Value *nlimit =
TmpB.CreateAlloca(def->types->lua_NumberT, nullptr, "nlimit");
llvm::Value *ninit =
TmpB.CreateAlloca(def->types->lua_NumberT, nullptr, "ninit");
llvm::Value *nstep =
TmpB.CreateAlloca(def->types->lua_NumberT, nullptr, "nstep");
// TValue *init = ra;
// TValue *plimit = ra + 1;
// TValue *pstep = ra + 2;
llvm::Value *init = emit_gep_register(def, A);
llvm::Value *plimit = emit_gep_register(def, A + 1);
llvm::Value *pstep = emit_gep_register(def, A + 2);
// if (ttisinteger(init) && ttisinteger(pstep) &&
// forlimit(plimit, &ilimit, ivalue(pstep), &stopnow)) {
// Get init->tt_
llvm::Instruction *pinit_tt = emit_load_type(def, init);
// Compare init->tt_ == LUA_TNUMINT
llvm::Value *cmp1 =
emit_is_value_of_type(def, pinit_tt, LUA__TNUMINT, "init.is.integer");
// Get pstep->tt_
llvm::Instruction *pstep_tt = emit_load_type(def, pstep);
// Compare pstep->tt_ == LUA_TNUMINT
llvm::Value *icmp2 =
emit_is_value_of_type(def, pstep_tt, LUA__TNUMINT, "step.is.integer");
// Get ivalue(pstep)
llvm::Instruction *pstep_ivalue = emit_load_reg_i(def, pstep);
// Call forlimit()
llvm::Value *forlimit_ret = CreateCall4(
def->builder, def->luaV_forlimitF, plimit, ilimit, pstep_ivalue, stopnow);
// init->tt_ == LUA_TNUMINT && pstep->tt_ == LUA_TNUMINT
llvm::Value *and1 =
def->builder->CreateAnd(cmp1, icmp2, "init.and.step.are.integers");
// Convert result from forlimit() to bool
llvm::Value *tobool =
def->builder->CreateICmpNE(forlimit_ret, def->types->kInt[0]);
// init->tt_ == LUA_TNUMINT && pstep->tt_ == LUA_TNUMINT && forlimit()
llvm::Value *and2 = def->builder->CreateAnd(and1, tobool, "all.integers");
// Create if then else branch
llvm::BasicBlock *then1 = llvm::BasicBlock::Create(def->jitState->context(),
"if.all.integers", def->f);
llvm::BasicBlock *else1 =
llvm::BasicBlock::Create(def->jitState->context(), "if.not.all.integers");
def->builder->CreateCondBr(and2, then1, else1);
def->builder->SetInsertPoint(then1);
// all values are integers
// lua_Integer initv = (stopnow ? 0 : ivalue(init));
// Get stopnow
llvm::Instruction *stopnow_val = emit_load_local_int(def, stopnow);
// Test if stopnow is 0
llvm::Value *stopnow_is_zero = def->builder->CreateICmpEQ(
stopnow_val, def->types->kInt[0], "stopnow.is.zero");
// Setup if then else branch for stopnow
llvm::BasicBlock *then1_iffalse = llvm::BasicBlock::Create(
def->jitState->context(), "if.stopnow.iszero", def->f);
llvm::BasicBlock *then1_iftrue =
llvm::BasicBlock::Create(def->jitState->context(), "if.stopnow.notzero");
def->builder->CreateCondBr(stopnow_is_zero, then1_iffalse, then1_iftrue);
def->builder->SetInsertPoint(then1_iffalse);
// stopnow is 0
// Get init->i
llvm::Instruction *init_ivalue = emit_load_reg_i(def, init);
// Join after the branch
def->builder->CreateBr(then1_iftrue);
def->f->getBasicBlockList().push_back(then1_iftrue);
def->builder->SetInsertPoint(then1_iftrue);
// Set initv to 0 if !stopnow else init->i
auto phi1 = def->builder->CreatePHI(def->types->lua_IntegerT, 2, "initv");
phi1->addIncoming(init_ivalue, then1_iffalse);
phi1->addIncoming(def->types->kluaInteger[0], then1);
// setivalue(plimit, ilimit);
llvm::Instruction *ilimit_val = emit_load_local_n(def, ilimit);
emit_store_reg_i_withtype(def, ilimit_val, plimit);
// setivalue(init, initv - ivalue(pstep));
// we aleady know init is LUA_TNUMINT
pstep_ivalue = emit_load_reg_i(def, pstep);
llvm::Value *sub =
def->builder->CreateSub(phi1, pstep_ivalue, "initv-pstep.i", false, true);
emit_store_reg_i(def, sub, init);
// We are done so jump to forloop
lua_assert(def->jmp_targets[pc].jmp1);
def->builder->CreateBr(def->jmp_targets[pc].jmp1);
// NOW the non-integer case
def->f->getBasicBlockList().push_back(else1);
def->builder->SetInsertPoint(else1);
// ************ PLIMIT - Convert plimit to float
llvm::Instruction *plimit_tt = emit_load_type(def, plimit);
// Test if already a float
cmp1 = emit_is_value_of_type(def, plimit_tt, LUA__TNUMFLT, "limit.is.float");
llvm::BasicBlock *else1_plimit_ifnum = llvm::BasicBlock::Create(
def->jitState->context(), "if.limit.isfloat", def->f);
llvm::BasicBlock *else1_plimit_elsenum =
llvm::BasicBlock::Create(def->jitState->context(), "if.limit.notfloat");
def->builder->CreateCondBr(cmp1, else1_plimit_ifnum, else1_plimit_elsenum);
def->builder->SetInsertPoint(else1_plimit_ifnum);
// Already a float - copy to nlimit
llvm::Instruction *plimit_nvalue_load = emit_load_reg_n(def, plimit);
emit_store_local_n(def, plimit_nvalue_load, nlimit);
// Go to the PSTEP section
llvm::BasicBlock *else1_pstep =
llvm::BasicBlock::Create(def->jitState->context(), "if.else.step");
def->builder->CreateBr(else1_pstep);
// If plimit was not already a float we need to convert
def->f->getBasicBlockList().push_back(else1_plimit_elsenum);
def->builder->SetInsertPoint(else1_plimit_elsenum);
// Call luaV_tonumber_()
llvm::Value *plimit_isnum =
CreateCall2(def->builder, def->luaV_tonumberF, plimit, nlimit);
llvm::Value *plimit_isnum_bool = def->builder->CreateICmpEQ(
plimit_isnum, def->types->kInt[0], "limit.float.ok");
// Did conversion fail?
llvm::BasicBlock *else1_plimit_tonum_elsenum = llvm::BasicBlock::Create(
def->jitState->context(), "if.limit.float.failed", def->f);
def->builder->CreateCondBr(plimit_isnum_bool, else1_plimit_tonum_elsenum,
else1_pstep);
// Conversion failed, so raise error
def->builder->SetInsertPoint(else1_plimit_tonum_elsenum);
emit_raise_lua_error(def, "'for' limit must be a number");
def->builder->CreateBr(else1_pstep);
// Conversion OK
// Update plimit
def->f->getBasicBlockList().push_back(else1_pstep);
def->builder->SetInsertPoint(else1_pstep);
llvm::Instruction *nlimit_load = emit_load_local_n(def, nlimit);
emit_store_reg_n_withtype(def, nlimit_load, plimit);
// *********** PSTEP - convert pstep to float
// Test if already a float
pstep_tt = emit_load_type(def, pstep);
cmp1 = emit_is_value_of_type(def, pstep_tt, LUA__TNUMFLT, "step.is.float");
llvm::BasicBlock *else1_pstep_ifnum = llvm::BasicBlock::Create(
def->jitState->context(), "if.step.isfloat", def->f);
llvm::BasicBlock *else1_pstep_elsenum =
llvm::BasicBlock::Create(def->jitState->context(), "if.step.notfloat");
def->builder->CreateCondBr(cmp1, else1_pstep_ifnum, else1_pstep_elsenum);
def->builder->SetInsertPoint(else1_pstep_ifnum);
// We float then copy to nstep
llvm::Instruction *pstep_nvalue_load = emit_load_reg_n(def, pstep);
emit_store_local_n(def, pstep_nvalue_load, nstep);
// Now go to handle initial value
llvm::BasicBlock *else1_pinit =
llvm::BasicBlock::Create(def->jitState->context(), "if.else.init");
def->builder->CreateBr(else1_pinit);
// If pstep was not already a float then we need to convert
def->f->getBasicBlockList().push_back(else1_pstep_elsenum);
def->builder->SetInsertPoint(else1_pstep_elsenum);
// call luaV_tonumber_()
llvm::Value *pstep_isnum =
CreateCall2(def->builder, def->luaV_tonumberF, pstep, nstep);
llvm::Value *pstep_isnum_bool = def->builder->CreateICmpEQ(
pstep_isnum, def->types->kInt[0], "step.float.ok");
llvm::BasicBlock *else1_pstep_tonum_elsenum = llvm::BasicBlock::Create(
def->jitState->context(), "if.step.float.failed", def->f);
def->builder->CreateCondBr(pstep_isnum_bool, else1_pstep_tonum_elsenum,
else1_pinit);
// If conversion failed raise error
def->builder->SetInsertPoint(else1_pstep_tonum_elsenum);
emit_raise_lua_error(def, "'for' step must be a number");
def->builder->CreateBr(else1_pinit);
// Conversion okay so update pstep
def->f->getBasicBlockList().push_back(else1_pinit);
def->builder->SetInsertPoint(else1_pinit);
llvm::Instruction *nstep_load = emit_load_local_n(def, nstep);
emit_store_reg_n_withtype(def, nstep_load, pstep);
// *********** PINIT finally handle initial value
// Check if it is already a float
pinit_tt = emit_load_type(def, init);
cmp1 = emit_is_value_of_type(def, pinit_tt, LUA__TNUMFLT, "init.is.float");
llvm::BasicBlock *else1_pinit_ifnum = llvm::BasicBlock::Create(
def->jitState->context(), "if.init.is.float", def->f);
llvm::BasicBlock *else1_pinit_elsenum =
llvm::BasicBlock::Create(def->jitState->context(), "if.init.not.float");
def->builder->CreateCondBr(cmp1, else1_pinit_ifnum, else1_pinit_elsenum);
def->builder->SetInsertPoint(else1_pinit_ifnum);
// Already float so copy to ninit
llvm::Instruction *pinit_nvalue_load = emit_load_reg_n(def, init);
emit_store_local_n(def, pinit_nvalue_load, ninit);
// Go to final section
llvm::BasicBlock *else1_pdone =
llvm::BasicBlock::Create(def->jitState->context(), "if.else.done");
def->builder->CreateBr(else1_pdone);
// Not a float so we need to convert
def->f->getBasicBlockList().push_back(else1_pinit_elsenum);
def->builder->SetInsertPoint(else1_pinit_elsenum);
// Call luaV_tonumber_()
llvm::Value *pinit_isnum =
CreateCall2(def->builder, def->luaV_tonumberF, init, ninit);
llvm::Value *pinit_isnum_bool = def->builder->CreateICmpEQ(
pinit_isnum, def->types->kInt[0], "init.float.ok");
llvm::BasicBlock *else1_pinit_tonum_elsenum = llvm::BasicBlock::Create(
def->jitState->context(), "if.init.float.failed", def->f);
def->builder->CreateCondBr(pinit_isnum_bool, else1_pinit_tonum_elsenum,
else1_pdone);
// Conversion failed so raise error
def->builder->SetInsertPoint(else1_pinit_tonum_elsenum);
emit_raise_lua_error(def, "'for' initial value must be a number");
def->builder->CreateBr(else1_pdone);
// Conversion OK so we are nearly done
def->f->getBasicBlockList().push_back(else1_pdone);
def->builder->SetInsertPoint(else1_pdone);
llvm::Instruction *ninit_load = emit_load_local_n(def, ninit);
nstep_load = emit_load_local_n(def, nstep);
// setfltvalue(init, luai_numsub(L, ninit, nstep));
llvm::Value *init_n =
def->builder->CreateFSub(ninit_load, nstep_load, "ninit-nstep");
emit_store_reg_n_withtype(def, init_n, init);
// Done so jump to forloop
def->builder->CreateBr(def->jmp_targets[pc].jmp1);
}
void RaviCodeGenerator::emit_iFORPREP(RaviFunctionDef *def, int A, int pc,
int step_one, int pc1) {
RaviBranchDef &forloop_target = def->jmp_targets[pc];
llvm::IRBuilder<> TmpB(def->entry, def->entry->begin());
forloop_target.ilimit =
TmpB.CreateAlloca(def->types->lua_IntegerT, nullptr, "ilimit");
if (!step_one) {
forloop_target.istep =
TmpB.CreateAlloca(def->types->lua_IntegerT, nullptr, "istep");
}
forloop_target.iidx =
TmpB.CreateAlloca(def->types->lua_IntegerT, nullptr, "iidx");
// lua_Integer initv = ivalue(init);
// setivalue(init, initv - ivalue(pstep));
emit_debug_trace(def, step_one ? OP_RAVI_FORPREP_I1 : OP_RAVI_FORPREP_IP,
pc1);
// Load pointer to base
emit_load_base(def);
// TValue *init = ra;
// TValue *pstep = ra + 2;
llvm::Value *init = emit_gep_register(def, A);
llvm::Value *plimit = emit_gep_register(def, A + 1);
llvm::Value *pstep = nullptr;
if (!step_one)
pstep = emit_gep_register(def, A + 2);
// Get ivalue(pstep)
llvm::Instruction *limit_ivalue = emit_load_reg_i(def, plimit);
llvm::Instruction *init_ivalue = emit_load_reg_i(def, init);
if (!step_one) {
// setivalue(init, initv - ivalue(pstep));
llvm::Instruction *step_ivalue = emit_load_reg_i(def, pstep);
llvm::Value *idx = def->builder->CreateSub(init_ivalue, step_ivalue,
"initv-pstep.i", false, true);
// Save idx
emit_store_local_n(def, idx, forloop_target.iidx);
// Save step
emit_store_local_n(def, step_ivalue, forloop_target.istep);
} else {
// setivalue(init, initv - ivalue(pstep));
llvm::Value *idx = def->builder->CreateSub(
init_ivalue, def->types->kluaInteger[1], "initv-pstep.i", false, true);
// Save idx
emit_store_local_n(def, idx, forloop_target.iidx);
}
// Save limit
emit_store_local_n(def, limit_ivalue, forloop_target.ilimit);
// We are done so jump to forloop
lua_assert(def->jmp_targets[pc].jmp1);
def->builder->CreateBr(def->jmp_targets[pc].jmp1);
}
}

File diff suppressed because it is too large Load Diff

@ -1,492 +0,0 @@
/******************************************************************************
* Copyright (C) 2015-2020 Dibyendu Majumdar
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
******************************************************************************/
#include "ravi_llvmcodegen.h"
namespace ravi {
// R(A+1), ..., R(A+B) := nil
void RaviCodeGenerator::emit_LOADNIL(RaviFunctionDef *def, int A, int B,
int pc) {
emit_debug_trace(def, OP_LOADNIL, pc);
#if 1
// Inline version, we unroll the loop
emit_load_base(def);
do {
llvm::Value *dest = emit_gep_register(def, A);
emit_store_type_(def, dest, LUA_TNIL);
A++;
} while (B--);
#else
CreateCall3(def->builder, def->raviV_op_loadnilF, def->ci_val,
llvm::ConstantInt::get(def->types->C_intT, A),
llvm::ConstantInt::get(def->types->C_intT, B));
#endif
}
// R(A) := tonumber(0)
void RaviCodeGenerator::emit_LOADFZ(RaviFunctionDef *def, int A, int pc) {
emit_debug_trace(def, OP_RAVI_LOADFZ, pc);
emit_load_base(def);
llvm::Value *dest = emit_gep_register(def, A);
// destvalue->n = 0.0
emit_store_reg_n_withtype(
def, llvm::ConstantFP::get(def->types->lua_NumberT, 0.0), dest);
}
// R(A) := tointeger(0)
void RaviCodeGenerator::emit_LOADIZ(RaviFunctionDef *def, int A, int pc) {
emit_debug_trace(def, OP_RAVI_LOADIZ, pc);
emit_load_base(def);
llvm::Value *dest = emit_gep_register(def, A);
// dest->i = 0
emit_store_reg_i_withtype(def, def->types->kluaInteger[0], dest);
}
// R(A) := (Bool)B; if (C) pc++
void RaviCodeGenerator::emit_LOADBOOL(RaviFunctionDef *def, int A, int B, int C,
int j, int pc) {
// setbvalue(ra, GETARG_B(i));
// if (GETARG_C(i)) ci->u.l.savedpc++; /* skip next instruction (if C) */
emit_debug_trace(def, OP_LOADBOOL, pc);
emit_load_base(def);
llvm::Value *dest = emit_gep_register(def, A);
// dest->i = 0
emit_store_reg_b_withtype(def, llvm::ConstantInt::get(def->types->C_intT, B),
dest);
if (C) {
// Skip next instruction if C
def->builder->CreateBr(def->jmp_targets[j].jmp1);
llvm::BasicBlock *block =
llvm::BasicBlock::Create(def->jitState->context(), "nextblock", def->f);
def->builder->SetInsertPoint(block);
}
}
// R(A) := R(B)
void RaviCodeGenerator::emit_MOVE(RaviFunctionDef *def, int A, int B, int pc) {
// setobjs2s(L, ra, RB(i));
emit_debug_trace(def, OP_MOVE, pc);
// Load pointer to base
emit_load_base(def);
lua_assert(A != B);
llvm::Value *src = emit_gep_register(def, B);
llvm::Value *dest = emit_gep_register(def, A);
emit_assign(def, dest, src);
}
// R(A) := R(B), check R(B) is int
void RaviCodeGenerator::emit_MOVEI(RaviFunctionDef *def, int A, int B, int pc) {
// TValue *rb = RB(i);
// lua_Integer j;
// if (tointeger(rb, &j)) {
// setivalue(ra, j);
// }
// else
// luaG_runerror(L, "integer expected");
llvm::IRBuilder<> TmpB(def->entry, def->entry->begin());
llvm::Value *var = TmpB.CreateAlloca(def->types->lua_IntegerT, nullptr, "i");
emit_debug_trace(def, OP_RAVI_MOVEI, pc);
// Load pointer to base
emit_load_base(def);
llvm::Value *dest = emit_gep_register(def, A);
llvm::Value *src = emit_gep_register(def, B);
llvm::Value *src_type = emit_load_type(def, src);
// Compare src->tt == LUA_TNUMINT
llvm::Value *cmp1 =
emit_is_value_of_type(def, src_type, LUA__TNUMINT, "is.src.integer");
llvm::BasicBlock *then1 =
llvm::BasicBlock::Create(def->jitState->context(), "if.integer", def->f);
llvm::BasicBlock *else1 =
llvm::BasicBlock::Create(def->jitState->context(), "if.not.integer");
llvm::BasicBlock *end1 =
llvm::BasicBlock::Create(def->jitState->context(), "done");
def->builder->CreateCondBr(cmp1, then1, else1);
def->builder->SetInsertPoint(then1);
// Already a int - move
llvm::Instruction *tmp = emit_load_reg_i(def, src);
emit_store_local_n(def, tmp, var);
def->builder->CreateBr(end1);
// we need to convert
def->f->getBasicBlockList().push_back(else1);
def->builder->SetInsertPoint(else1);
// Call luaV_tointeger_()
llvm::Value *var_isint =
CreateCall2(def->builder, def->luaV_tointegerF, src, var);
llvm::Value *tobool = def->builder->CreateICmpEQ(
var_isint, def->types->kInt[0], "int.conversion.failed");
// Did conversion fail?
llvm::BasicBlock *else2 = llvm::BasicBlock::Create(
def->jitState->context(), "if.conversion.failed", def->f);
def->builder->CreateCondBr(tobool, else2, end1);
// Conversion failed, so raise error
def->builder->SetInsertPoint(else2);
emit_raise_lua_error(def, "integer expected");
def->builder->CreateBr(end1);
// Conversion OK
def->f->getBasicBlockList().push_back(end1);
def->builder->SetInsertPoint(end1);
auto load_var = emit_load_local_n(def, var);
emit_store_reg_i_withtype(def, load_var, dest);
}
void RaviCodeGenerator::emit_MOVEF(RaviFunctionDef *def, int A, int B, int pc) {
// case OP_RAVI_MOVEF: {
// TValue *rb = RB(i);
// lua_Number j;
// if (tonumber(rb, &j)) {
// setfltvalue(ra, j);
// }
// else
// luaG_runerror(L, "float expected");
// } break;
llvm::IRBuilder<> TmpB(def->entry, def->entry->begin());
llvm::Value *var = TmpB.CreateAlloca(def->types->lua_NumberT, nullptr, "n");
emit_debug_trace(def, OP_RAVI_MOVEF, pc);
// Load pointer to base
emit_load_base(def);
llvm::Value *dest = emit_gep_register(def, A);
llvm::Value *src = emit_gep_register(def, B);
llvm::Value *src_type = emit_load_type(def, src);
// Compare src->tt == LUA_TNUMFLT
llvm::Value *cmp1 =
emit_is_value_of_type(def, src_type, LUA__TNUMFLT, "is.src.float");
llvm::BasicBlock *then1 =
llvm::BasicBlock::Create(def->jitState->context(), "if.float", def->f);
llvm::BasicBlock *else1 =
llvm::BasicBlock::Create(def->jitState->context(), "if.not.float");
llvm::BasicBlock *end1 =
llvm::BasicBlock::Create(def->jitState->context(), "done");
def->builder->CreateCondBr(cmp1, then1, else1);
def->builder->SetInsertPoint(then1);
// Already a float - copy to var
llvm::Instruction *tmp = emit_load_reg_n(def, src);
emit_store_local_n(def, tmp, var);
def->builder->CreateBr(end1);
// we need to convert
def->f->getBasicBlockList().push_back(else1);
def->builder->SetInsertPoint(else1);
// Call luaV_tonumber()
llvm::Value *var_isflt =
CreateCall2(def->builder, def->luaV_tonumberF, src, var);
llvm::Value *tobool = def->builder->CreateICmpEQ(
var_isflt, def->types->kInt[0], "float.conversion.failed");
// Did conversion fail?
llvm::BasicBlock *else2 = llvm::BasicBlock::Create(
def->jitState->context(), "if.conversion.failed", def->f);
def->builder->CreateCondBr(tobool, else2, end1);
// Conversion failed, so raise error
def->builder->SetInsertPoint(else2);
emit_raise_lua_error(def, "number expected");
def->builder->CreateBr(end1);
// Conversion OK
def->f->getBasicBlockList().push_back(end1);
def->builder->SetInsertPoint(end1);
// Set R(A)
auto load_var = emit_load_local_n(def, var);
emit_store_reg_n_withtype(def, load_var, dest);
}
void RaviCodeGenerator::emit_TOSTRING(RaviFunctionDef *def, int A, int pc) {
emit_debug_trace(def, OP_RAVI_TOSTRING, pc);
emit_load_base(def);
llvm::Value *ra = emit_gep_register(def, A);
llvm::Instruction *type = emit_load_type(def, ra);
llvm::Value *isnotnil = emit_is_not_value_of_type(def, type, LUA__TNIL);
// check if string type
llvm::Value *cmp1 = emit_is_not_value_of_type_class(def, type, LUA_TSTRING);
cmp1 = def->builder->CreateAnd(isnotnil, cmp1, "OP_RAVI_TOSTRING_is.not.expected.type");
llvm::BasicBlock *raise_error = llvm::BasicBlock::Create(
def->jitState->context(), "OP_RAVI_TOSTRING_if.not.expected_type", def->f);
llvm::BasicBlock *done =
llvm::BasicBlock::Create(def->jitState->context(), "OP_RAVI_TOSTRING_done");
auto brinst1 = def->builder->CreateCondBr(cmp1, raise_error, done);
attach_branch_weights(def, brinst1, 0, 100);
def->builder->SetInsertPoint(raise_error);
// Conversion failed, so raise error
emit_raise_lua_error(def, "string expected");
def->builder->CreateBr(done);
def->f->getBasicBlockList().push_back(done);
def->builder->SetInsertPoint(done);
}
void RaviCodeGenerator::emit_TOCLOSURE(RaviFunctionDef *def, int A, int pc) {
emit_debug_trace(def, OP_RAVI_TOCLOSURE, pc);
emit_load_base(def);
llvm::Value *ra = emit_gep_register(def, A);
llvm::Instruction *type = emit_load_type(def, ra);
llvm::Value *isnotnil = emit_is_not_value_of_type(def, type, LUA__TNIL);
// check if function type
llvm::Value *cmp1 = emit_is_not_value_of_type_class(
def, type, LUA_TFUNCTION);
cmp1 = def->builder->CreateAnd(isnotnil, cmp1, "OP_RAVI_TOCLOSURE_is.not.expected.type");
llvm::BasicBlock *raise_error = llvm::BasicBlock::Create(
def->jitState->context(), "OP_RAVI_TOCLOSURE_if.not.expected_type",
def->f);
llvm::BasicBlock *done = llvm::BasicBlock::Create(def->jitState->context(),
"OP_RAVI_TOCLOSURE_done");
auto brinst1 = def->builder->CreateCondBr(cmp1, raise_error, done);
attach_branch_weights(def, brinst1, 0, 100);
def->builder->SetInsertPoint(raise_error);
// Conversion failed, so raise error
emit_raise_lua_error(def, "closure expected");
def->builder->CreateBr(done);
def->f->getBasicBlockList().push_back(done);
def->builder->SetInsertPoint(done);
}
void RaviCodeGenerator::emit_TOTYPE(RaviFunctionDef *def, int A, int Bx,
int pc) {
emit_debug_trace(def, OP_RAVI_TOTYPE, pc);
// Load pointer to base
emit_load_base(def);
llvm::Value *ra = emit_gep_register(def, A);
llvm::Value *rb = emit_gep_constant(def, Bx);
CreateCall3(def->builder, def->raviV_op_totypeF, def->L, ra, rb);
}
void RaviCodeGenerator::emit_TOINT(RaviFunctionDef *def, int A, int pc) {
// case OP_RAVI_TOINT: {
// lua_Integer j;
// if (tointeger(ra, &j)) {
// setivalue(ra, j);
// }
// else
// luaG_runerror(L, "integer expected");
// } break;
llvm::IRBuilder<> TmpB(def->entry, def->entry->begin());
llvm::Value *var = TmpB.CreateAlloca(def->types->lua_IntegerT, nullptr, "i");
emit_debug_trace(def, OP_RAVI_TOINT, pc);
// Load pointer to base
emit_load_base(def);
llvm::Value *dest = emit_gep_register(def, A);
llvm::Value *src = dest;
llvm::Value *src_type = emit_load_type(def, src);
// Is src->tt != LUA_TNUMINT?
llvm::Value *cmp1 =
emit_is_not_value_of_type(def, src_type, LUA__TNUMINT, "is.not.integer");
llvm::BasicBlock *then1 = llvm::BasicBlock::Create(def->jitState->context(),
"if.not.integer", def->f);
llvm::BasicBlock *end1 =
llvm::BasicBlock::Create(def->jitState->context(), "done");
def->builder->CreateCondBr(cmp1, then1, end1);
def->builder->SetInsertPoint(then1);
// Call luaV_tointeger_()
llvm::Value *var_isint =
CreateCall2(def->builder, def->luaV_tointegerF, src, var);
llvm::Value *tobool = def->builder->CreateICmpEQ(
var_isint, def->types->kInt[0], "int.conversion.failed");
// Did conversion fail?
llvm::BasicBlock *then2 = llvm::BasicBlock::Create(
def->jitState->context(), "if.conversion.failed", def->f);
llvm::BasicBlock *else2 =
llvm::BasicBlock::Create(def->jitState->context(), "conversion.ok");
def->builder->CreateCondBr(tobool, then2, else2);
def->builder->SetInsertPoint(then2);
// Conversion failed, so raise error
emit_raise_lua_error(def, "integer expected");
def->builder->CreateBr(else2);
// Conversion OK
def->f->getBasicBlockList().push_back(else2);
def->builder->SetInsertPoint(else2);
auto load_var = emit_load_local_n(def, var);
emit_store_reg_i_withtype(def, load_var, dest);
def->builder->CreateBr(end1);
def->f->getBasicBlockList().push_back(end1);
def->builder->SetInsertPoint(end1);
}
void RaviCodeGenerator::emit_TOFLT(RaviFunctionDef *def, int A, int pc) {
// case OP_RAVI_TOFLT: {
// lua_Number j;
// if (tonumber(ra, &j)) {
// setfltvalue(ra, j);
// }
// else
// luaG_runerror(L, "float expected");
// } break;
llvm::IRBuilder<> TmpB(def->entry, def->entry->begin());
llvm::Value *var = TmpB.CreateAlloca(def->types->lua_NumberT, nullptr, "n");
emit_debug_trace(def, OP_RAVI_TOFLT, pc);
// Load pointer to base
emit_load_base(def);
llvm::Value *dest = emit_gep_register(def, A);
llvm::Value *src = dest;
llvm::Value *src_type = emit_load_type(def, src);
// Is src->tt != LUA_TNUMFLT?
llvm::Value *cmp1 =
emit_is_not_value_of_type(def, src_type, LUA__TNUMFLT, "is.not.float");
llvm::BasicBlock *then1 = llvm::BasicBlock::Create(def->jitState->context(),
"if.not.float", def->f);
llvm::BasicBlock *end1 =
llvm::BasicBlock::Create(def->jitState->context(), "done");
def->builder->CreateCondBr(cmp1, then1, end1);
def->builder->SetInsertPoint(then1);
// Call luaV_tonumber()
llvm::Value *var_isflt =
CreateCall2(def->builder, def->luaV_tonumberF, src, var);
llvm::Value *tobool = def->builder->CreateICmpEQ(
var_isflt, def->types->kInt[0], "float.conversion.failed");
// Did conversion fail?
llvm::BasicBlock *then2 = llvm::BasicBlock::Create(
def->jitState->context(), "if.conversion.failed", def->f);
llvm::BasicBlock *else2 =
llvm::BasicBlock::Create(def->jitState->context(), "conversion.ok");
def->builder->CreateCondBr(tobool, then2, else2);
def->builder->SetInsertPoint(then2);
// Conversion failed, so raise error
emit_raise_lua_error(def, "number expected");
def->builder->CreateBr(else2);
// Conversion OK
def->f->getBasicBlockList().push_back(else2);
def->builder->SetInsertPoint(else2);
auto load_var = emit_load_local_n(def, var);
emit_store_reg_n_withtype(def, load_var, dest);
def->builder->CreateBr(end1);
def->f->getBasicBlockList().push_back(end1);
def->builder->SetInsertPoint(end1);
}
void RaviCodeGenerator::emit_LOADK(RaviFunctionDef *def, int A, int Bx,
int pc) {
// TValue *rb = k + GETARG_Bx(i);
// setobj2s(L, ra, rb);
emit_debug_trace(def, OP_LOADK, pc);
// Load pointer to base
emit_load_base(def);
// LOADK requires a structure assignment
// in LLVM as far as I can tell this requires a call to
// an intrinsic memcpy
llvm::Value *dest = emit_gep_register(def, A);
TValue *Konst = &def->p->k[Bx];
switch (Konst->tt_) {
case LUA_TNUMINT:
emit_store_reg_i_withtype(
def,
llvm::ConstantInt::get(def->types->lua_IntegerT, Konst->value_.i),
dest);
break;
case LUA_TNUMFLT:
emit_store_reg_n_withtype(
def, llvm::ConstantFP::get(def->types->lua_NumberT, Konst->value_.n),
dest);
break;
case LUA_TBOOLEAN:
emit_store_reg_b_withtype(
def, llvm::ConstantInt::get(def->types->C_intT, Konst->value_.b),
dest);
break;
default: {
// rb
llvm::Value *src = emit_gep_constant(def, Bx);
// *ra = *rb
emit_assign(def, dest, src);
}
}
}
void RaviCodeGenerator::emit_assign(RaviFunctionDef *def, llvm::Value *dest,
llvm::Value *src) {
// Below is more efficient that memcpy()
// destvalue->value->i = srcvalue->value->i;
// destvalue->value->tt = srcvalue->value->tt;
llvm::Value *srcvalue = emit_gep(def, "srcvalue", src, 0, 0, 0);
llvm::Value *destvalue = emit_gep(def, "destvalue", dest, 0, 0, 0);
llvm::Instruction *load = def->builder->CreateLoad(srcvalue);
load->setMetadata(llvm::LLVMContext::MD_tbaa, def->types->tbaa_TValue_nT);
llvm::Instruction *store = def->builder->CreateStore(load, destvalue);
store->setMetadata(llvm::LLVMContext::MD_tbaa, def->types->tbaa_TValue_nT);
// destvalue->type = srcvalue->type
llvm::Value *srctype = emit_gep(def, "srctype", src, 0, 1);
llvm::Value *desttype = emit_gep(def, "desttype", dest, 0, 1);
load = def->builder->CreateLoad(srctype);
load->setMetadata(llvm::LLVMContext::MD_tbaa, def->types->tbaa_TValue_ttT);
store = def->builder->CreateStore(load, desttype);
store->setMetadata(llvm::LLVMContext::MD_tbaa, def->types->tbaa_TValue_ttT);
}
}

@ -1,53 +0,0 @@
/******************************************************************************
* Copyright (C) 2015-2020 Dibyendu Majumdar
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
******************************************************************************/
#include "ravi_llvmcodegen.h"
namespace ravi {
void RaviCodeGenerator::emit_CONCAT(RaviFunctionDef *def, int A, int B, int C,
int pc) {
bool traced = emit_debug_trace(def, OP_CONCAT, pc);
// below may invoke metamethod so we need to set savedpc
if (!traced) emit_update_savedpc(def, pc);
CreateCall5(def->builder, def->raviV_op_concatF, def->L, def->ci_val,
llvm::ConstantInt::get(def->types->C_intT, A),
llvm::ConstantInt::get(def->types->C_intT, B),
llvm::ConstantInt::get(def->types->C_intT, C));
}
void RaviCodeGenerator::emit_CLOSURE(RaviFunctionDef *def, int A, int Bx,
int pc) {
emit_debug_trace(def, OP_CLOSURE, pc);
CreateCall5(def->builder, def->raviV_op_closureF, def->L, def->ci_val,
def->p_LClosure, llvm::ConstantInt::get(def->types->C_intT, A),
llvm::ConstantInt::get(def->types->C_intT, Bx));
}
void RaviCodeGenerator::emit_VARARG(RaviFunctionDef *def, int A, int B,
int pc) {
emit_debug_trace(def, OP_VARARG, pc);
CreateCall5(def->builder, def->raviV_op_varargF, def->L, def->ci_val,
def->p_LClosure, llvm::ConstantInt::get(def->types->C_intT, A),
llvm::ConstantInt::get(def->types->C_intT, B));
}
}

@ -1,114 +0,0 @@
/******************************************************************************
* Copyright (C) 2015-2020 Dibyendu Majumdar
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
******************************************************************************/
#include "ravi_llvmcodegen.h"
namespace ravi {
void RaviCodeGenerator::emit_RETURN(RaviFunctionDef *def, int A, int B,
int pc) {
// Here is what OP_RETURN looks like. We only compile steps
// marked with //*. This is because the rest is only relevant in the
// interpreter
// case OP_RETURN: {
// int b = GETARG_B(i);
//* if (cl->p->sizep > 0) luaF_close(L, base);
//* b = luaD_poscall(L, ra, (b != 0 ? b - 1 : L->top - ra));
// if (!(ci->callstatus & CIST_REENTRY)) /* 'ci' still the called one */
//* return b; /* external invocation: return */
// else { /* invocation via reentry: continue execution */
// ci = L->ci;
// if (b) L->top = ci->top;
// goto newframe; /* restart luaV_execute over new Lua function */
// }
// }
// As Lua inserts redundant OP_RETURN instructions it is
// possible that this is one of them. If this is the case then the
// current block may already be terminated - so we have to insert
// a new block
if (def->builder->GetInsertBlock()->getTerminator()) {
llvm::BasicBlock *return_block = llvm::BasicBlock::Create(
def->jitState->context(), "OP_RETURN_bridge", def->f);
def->builder->SetInsertPoint(return_block);
}
bool traced = emit_debug_trace(def, OP_RETURN, pc);
#ifndef RAVI_DEFER_STATEMENT
// Load pointer to base
emit_load_base(def);
// Get pointer to register A
llvm::Value *ra_ptr = emit_gep_register(def, A);
#endif
// if (cl->p->sizep > 0) luaF_close(L, base);
// Get pointer to Proto->sizep
llvm::Instruction *psize = emit_load_proto_sizep(def);
// Test if psize > 0
llvm::Value *psize_gt_0 =
def->builder->CreateICmpSGT(psize, def->types->kInt[0]);
llvm::BasicBlock *then_block = llvm::BasicBlock::Create(
def->jitState->context(), "OP_RETURN_if_closing", def->f);
llvm::BasicBlock *else_block = llvm::BasicBlock::Create(
def->jitState->context(), "OP_RETURN_else_closing");
def->builder->CreateCondBr(psize_gt_0, then_block, else_block);
def->builder->SetInsertPoint(then_block);
#ifdef RAVI_DEFER_STATEMENT
// Load pointer to base
emit_load_base(def);
// Get pointer to register A
llvm::Value *ra_ptr = emit_gep_register(def, A);
if (!traced)
emit_update_savedpc(def, pc);
#endif
// Call luaF_close
#ifdef RAVI_DEFER_STATEMENT
CreateCall3(def->builder, def->luaF_closeF, def->L, def->base_ptr, def->types->kInt[LUA_OK]);
#else
CreateCall2(def->builder, def->luaF_closeF, def->L, def->base_ptr);
#endif
def->builder->CreateBr(else_block);
def->f->getBasicBlockList().push_back(else_block);
def->builder->SetInsertPoint(else_block);
#ifdef RAVI_DEFER_STATEMENT
emit_load_base(def); // As luaF_close() may have changed the stack
ra_ptr = emit_gep_register(def, A); // load RA
#endif
//* b = luaD_poscall(L, ra, (b != 0 ? b - 1 : L->top - ra));
llvm::Value *nresults = NULL;
if (B != 0)
nresults = llvm::ConstantInt::get(def->types->C_intT, B - 1);
else
nresults = emit_num_stack_elements(def, ra_ptr);
llvm::Value *b =
CreateCall4(def->builder, def->luaD_poscallF, def->L, def->ci_val, ra_ptr, nresults);
def->builder->CreateRet(b);
}
}

@ -1,981 +0,0 @@
/******************************************************************************
* Copyright (C) 2015-2020 Dibyendu Majumdar
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
******************************************************************************/
#include "ravi_llvmcodegen.h"
namespace ravi {
// R(A+1) := R(B); R(A) := R(B)[RK(C)]
void RaviCodeGenerator::emit_SELF(RaviFunctionDef *def, int A, int B, int C,
int pc) {
// StkId rb = RB(i);
// setobjs2s(L, ra + 1, rb);
// Protect(luaV_gettable(L, rb, RKC(i), ra));
bool traced = emit_debug_trace(def, OP_SELF, pc);
// Below may invoke metamethod so we set savedpc
if (!traced) emit_update_savedpc(def, pc);
emit_load_base(def);
llvm::Value *rb = emit_gep_register(def, B);
llvm::Value *ra1 = emit_gep_register(def, A + 1);
emit_assign(def, ra1, rb);
llvm::Value *ra = emit_gep_register(def, A);
llvm::Value *rc = emit_gep_register_or_constant(def, C);
CreateCall4(def->builder, def->luaV_gettableF, def->L, rb, rc, ra);
}
// R(A+1) := R(B); R(A) := R(B)[RK(C)]
void RaviCodeGenerator::emit_SELF_SK(RaviFunctionDef *def, int A, int B, int C,
int pc) {
// StkId rb = RB(i);
// setobjs2s(L, ra + 1, rb);
// Protect(raviV_gettable_sskey(L, rb, RKC(i), ra));
bool traced = emit_debug_trace(def, OP_RAVI_SELF_SK, pc);
// Below may invoke metamethod so we set savedpc
if (!traced) emit_update_savedpc(def, pc);
emit_load_base(def);
llvm::Value *rb = emit_gep_register(def, B);
llvm::Value *ra1 = emit_gep_register(def, A + 1);
emit_assign(def, ra1, rb);
llvm::Value *ra = emit_gep_register(def, A);
llvm::Value *rc = emit_gep_register_or_constant(def, C);
CreateCall4(def->builder, def->raviV_gettable_sskeyF, def->L, rb, rc, ra);
}
// R(A+1) := R(B); R(A) := R(B)[RK(C)]
void RaviCodeGenerator::emit_TABLE_SELF_SK(RaviFunctionDef *def, int A, int B, int C,
int pc, TString *key) {
// StkId rb = RB(i);
// setobjs2s(L, ra + 1, rb);
// TValue *kv = k + INDEXK(GETARG_C(i));
// TString *key = tsvalue(kv);
// const TValue *v = luaH_getstr(hvalue(rb), key);
// setobj2s(L, ra, v);
emit_debug_trace(def, OP_RAVI_TABLE_SELF_SK, pc);
emit_load_base(def);
llvm::Value *rb = emit_gep_register(def, B);
llvm::Value *ra1 = emit_gep_register(def, A + 1);
emit_assign(def, ra1, rb);
emit_common_GETTABLE_S(def, A, B, C, key);
}
// R(A) := length of R(B)
void RaviCodeGenerator::emit_LEN(RaviFunctionDef *def, int A, int B, int pc) {
// Protect(luaV_objlen(L, ra, RB(i)));
bool traced = emit_debug_trace(def, OP_LEN, pc);
// Below may invoke metamethod so we set savedpc
if (!traced) emit_update_savedpc(def, pc);
emit_load_base(def);
llvm::Value *ra = emit_gep_register(def, A);
llvm::Value *rb = emit_gep_register(def, B);
CreateCall3(def->builder, def->luaV_objlenF, def->L, ra, rb);
}
// R(A)[RK(B)] := RK(C)
// This is a more optimized version that calls
// luaH_setint() instead of luaV_settable().
// This relies on two things:
// a) we know we have a table
// b) we know the key is an integer
void RaviCodeGenerator::emit_SETI(RaviFunctionDef *def, int A, int B,
int C, int pc) {
bool traced = emit_debug_trace(def, OP_RAVI_SETI, pc);
if (!traced) emit_update_savedpc(def, pc);
emit_load_base(def);
llvm::Value *ra = emit_gep_register(def, A);
llvm::Value *rb = emit_gep_register_or_constant(def, B);
llvm::Value *rc = emit_gep_register_or_constant(def, C);
CreateCall4(def->builder, def->raviV_settable_iF, def->L, ra, rb, rc);
}
// R(A)[RK(B)] := RK(C)
void RaviCodeGenerator::emit_SETTABLE(RaviFunctionDef *def, int A, int B, int C,
int pc) {
// Protect(luaV_settable(L, ra, RKB(i), RKC(i)));
bool traced = emit_debug_trace(def, OP_SETTABLE, pc);
// Below may invoke metamethod so we set savedpc
if (!traced) emit_update_savedpc(def, pc);
emit_load_base(def);
llvm::Value *ra = emit_gep_register(def, A);
llvm::Value *rb = emit_gep_register_or_constant(def, B);
llvm::Value *rc = emit_gep_register_or_constant(def, C);
CreateCall4(def->builder, def->luaV_settableF, def->L, ra, rb, rc);
}
// R(A)[RK(B)] := RK(C)
void RaviCodeGenerator::emit_SETFIELD(RaviFunctionDef *def, int A, int B,
int C, int pc) {
// Protect(raviV_settable_sskey(L, ra, RKB(i), RKC(i)));
bool traced = emit_debug_trace(def, OP_RAVI_SETFIELD, pc);
// Below may invoke metamethod so we set savedpc
if (!traced) emit_update_savedpc(def, pc);
emit_load_base(def);
llvm::Value *ra = emit_gep_register(def, A);
llvm::Value *rb = emit_gep_register_or_constant(def, B);
llvm::Value *rc = emit_gep_register_or_constant(def, C);
CreateCall4(def->builder, def->raviV_settable_sskeyF, def->L, ra, rb, rc);
}
// R(A) := R(B)[RK(C)]
void RaviCodeGenerator::emit_GETTABLE(RaviFunctionDef *def, int A, int B, int C,
int pc) {
// Protect(luaV_gettable(L, RB(i), RKC(i), ra));
bool traced = emit_debug_trace(def, OP_GETTABLE, pc);
// Below may invoke metamethod so we set savedpc
if (!traced) emit_update_savedpc(def, pc);
emit_load_base(def);
llvm::Value *ra = emit_gep_register(def, A);
llvm::Value *rb = emit_gep_register(def, B);
llvm::Value *rc = emit_gep_register_or_constant(def, C);
CreateCall4(def->builder, def->luaV_gettableF, def->L, rb, rc, ra);
}
// R(A) := R(B)[RK(C)]
void RaviCodeGenerator::emit_GETFIELD(RaviFunctionDef *def, int A, int B,
int C, int pc, TString *key) {
bool traced = emit_debug_trace(def, OP_RAVI_GETFIELD, pc);
// Below may invoke metamethod so we set savedpc
if (!traced) emit_update_savedpc(def, pc);
emit_load_base(def);
llvm::Value *rb = emit_gep_register(def, B);
#if 0
// Protect(raviV_gettable_sskey(L, RB(i), RKC(i), ra));
llvm::Value *ra = emit_gep_register(def, A);
llvm::Value *rc = emit_gep_register_or_constant(def, C);
CreateCall4(def->builder, def->raviV_gettable_sskeyF, def->L, rb, rc, ra);
#else
llvm::Instruction *type = emit_load_type(def, rb);
// if table type try fast path
llvm::Value *cmp1 = emit_is_value_of_type(def, type, RAVI__TLTABLE,
"GETFIELD_is_table_type");
llvm::BasicBlock *is_table = llvm::BasicBlock::Create(
def->jitState->context(), "GETFIELD_is_table", def->f);
llvm::BasicBlock *not_table = llvm::BasicBlock::Create(
def->jitState->context(), "GETFIELD_is_not_table");
llvm::BasicBlock *done =
llvm::BasicBlock::Create(def->jitState->context(), "GETFIELD_done");
auto brinst1 = def->builder->CreateCondBr(cmp1, is_table, not_table);
attach_branch_weights(def, brinst1, 100, 0);
def->builder->SetInsertPoint(is_table);
emit_common_GETTABLE_S_(def, A, rb, C, key);
def->builder->CreateBr(done);
def->f->getBasicBlockList().push_back(not_table);
def->builder->SetInsertPoint(not_table);
llvm::Value *ra = emit_gep_register(def, A);
llvm::Value *rc = emit_gep_register_or_constant(def, C);
CreateCall4(def->builder, def->raviV_gettable_sskeyF, def->L, rb, rc, ra);
def->builder->CreateBr(done);
def->f->getBasicBlockList().push_back(done);
def->builder->SetInsertPoint(done);
#endif
}
// R(A) := R(B)[RK(C)]
// This is a more optimized version that attempts to do an inline
// array get first and only if that fails it falls back on calling
// luaH_getint(). This relies on two things:
// a) we know we have a table
// b) we know the key is an integer
void RaviCodeGenerator::emit_GETI(RaviFunctionDef *def, int A, int B,
int C, int pc) {
// changed to that target may not be a table
bool traced = emit_debug_trace(def, OP_RAVI_GETI, pc);
if (!traced) emit_update_savedpc(def, pc);
emit_load_base(def);
llvm::Value *ra = emit_gep_register(def, A);
llvm::Value *rb = emit_gep_register(def, B);
llvm::Value *rc = emit_gep_register_or_constant(def, C);
CreateCall4(def->builder, def->raviV_gettable_iF, def->L, rb, rc, ra);
// FIXME following is no longer valid as RB may not be a table
// TValue *rb = RB(i);
// TValue *rc = RKC(i);
// lua_Integer idx = ivalue(rc);
// Table *t = hvalue(rb);
// const TValue *v;
// if (l_castS2U(idx - 1) < t->sizearray)
// v = &t->array[idx - 1];
// else
// v = luaH_getint(t, idx);
// if (!ttisnil(v) || metamethod_absent(t->metatable, TM_INDEX)) {
// setobj2s(L, ra, v);
//}
// else {
// Protect(luaV_finishget(L, rb, rc, ra, v));
//}
//emit_debug_trace(def, OP_RAVI_GETTABLE_I, pc);
//emit_load_base(def);
//llvm::Value *ra = emit_gep_register(def, A);
//llvm::Value *rb = emit_gep_register(def, B);
//llvm::Value *rc = emit_gep_register_or_constant(def, C);
//llvm::Instruction *key = emit_load_reg_i(def, rc);
//llvm::Instruction *t = emit_load_reg_h(def, rb);
//llvm::Value *data = emit_table_get_array(def, t);
//llvm::Value *len = emit_table_get_arraysize(def, t);
//// As Lua arrays are 1 based we need to subtract by 1
//llvm::Value *key_minus_1 =
// def->builder->CreateSub(key, def->types->kluaInteger[1]);
//// As len is unsigned int we need to truncate
//llvm::Value *ukey =
// def->builder->CreateTrunc(key_minus_1, def->types->C_intT);
//// Do an unsigned comparison with length
//llvm::Value *cmp = def->builder->CreateICmpULT(ukey, len);
//llvm::BasicBlock *then_block =
// llvm::BasicBlock::Create(def->jitState->context(), "if.in.range", def->f);
//llvm::BasicBlock *else_block =
// llvm::BasicBlock::Create(def->jitState->context(), "if.not.in.range");
//llvm::BasicBlock *end_block =
// llvm::BasicBlock::Create(def->jitState->context(), "if.end");
//def->builder->CreateCondBr(cmp, then_block, else_block);
//def->builder->SetInsertPoint(then_block);
//// Key is in range so array access possible
//llvm::Value *value1 = def->builder->CreateGEP(data, ukey);
//def->builder->CreateBr(end_block);
//// Out of range so fall back to luaH_getint()
//def->f->getBasicBlockList().push_back(else_block);
//def->builder->SetInsertPoint(else_block);
//llvm::Value *value2 = CreateCall2(def->builder, def->luaH_getintF, t, key);
//def->builder->CreateBr(end_block);
//// Merge results from the two branches above
//def->f->getBasicBlockList().push_back(end_block);
//def->builder->SetInsertPoint(end_block);
//llvm::PHINode *phi = def->builder->CreatePHI(def->types->pTValueT, 2);
//phi->addIncoming(value1, then_block);
//phi->addIncoming(value2, else_block);
//emit_finish_GETTABLE(def, phi, t, ra, rb, rc);
}
void RaviCodeGenerator::emit_finish_GETTABLE(RaviFunctionDef *def,
llvm::Value *phi, llvm::Value *t,
llvm::Value *ra, llvm::Value *rb,
llvm::Value *rc) {
// We need to test if value is not nil
// or table has no metatable
// or if the metatable cached flags indicate metamethod absent
llvm::Value *value_type = emit_load_type(def, phi);
llvm::Value *isnotnil = emit_is_not_value_of_type(def, value_type, LUA__TNIL);
// llvm::Value *metamethod_absent = emit_table_no_metamethod(def, t,
// TM_INDEX);
// llvm::Value *cond = def->builder->CreateOr(isnotnil, metamethod_absent);
llvm::Value *cond = isnotnil;
llvm::BasicBlock *if_true_block =
llvm::BasicBlock::Create(def->jitState->context(), "if.not.nil", def->f);
llvm::BasicBlock *if_false_block =
llvm::BasicBlock::Create(def->jitState->context(), "if.try.metamethod");
llvm::BasicBlock *if_end_block =
llvm::BasicBlock::Create(def->jitState->context(), "if.end");
auto brinst1 =
def->builder->CreateCondBr(cond, if_true_block, if_false_block);
attach_branch_weights(def, brinst1, 100, 0);
def->builder->SetInsertPoint(if_true_block);
// Fast path
emit_assign(def, ra, phi);
def->builder->CreateBr(if_end_block);
def->f->getBasicBlockList().push_back(if_false_block);
def->builder->SetInsertPoint(if_false_block);
// If value is nil Lua requires that an index event be
// generated - so we fall back on slow path for that
CreateCall5(def->builder, def->luaV_finishgetF, def->L, rb, rc, ra, phi);
def->builder->CreateBr(if_end_block);
// Merge results from the two branches above
def->f->getBasicBlockList().push_back(if_end_block);
def->builder->SetInsertPoint(if_end_block);
}
// R(A) := R(B)[RK(C)]
// Emit inline code for accessing a table element using a string key
// We try to access the element using the hash part but if the
// key is not in the main position then we fall back on luaH_getstr().
// IMPORTANT - this emitter should only be called when key is known to
// to be short string
// NOTE: To add support for GETTABUP_SK we now let caller supply the
// rb register as it may be a register or an upvalue reference
// See emit_GETTABUP_SK
void RaviCodeGenerator::emit_common_GETTABLE_S_(RaviFunctionDef *def, int A,
llvm::Value *rb, int C,
TString *key) {
// The code we want to generate is this:
// struct Node *n = hashstr(t, key);
// const struct TValue *k = gkey(n);
// TValue *v;
// if (ttisshrstring(k) && eqshrstr(tsvalue(k), key))
// v = gval(n);
// else
// v = luaH_getstr(t, key);
// if (!ttisnil(v) || metamethod_absent(t->metatable, TM_INDEX)) {
// setobj2s(L, ra, v);
// }
// else {
// Protect(luaV_finishget(L, rb, rc, ra, v));
// }
// A number of macros are involved above do the
// the generated code is somewhat more complex
llvm::Value *ra = emit_gep_register(def, A);
// Get the hash table
llvm::Instruction *t = emit_load_reg_h(def, rb);
// Obtain the offset where the key should be
llvm::Value *offset = emit_table_get_hashstr(def, t, key);
// Get access to the node array
llvm::Value *node = emit_table_get_nodearray(def, t);
// Now we need to get to the right element in the node array
// and retrieve the type information which is held there
llvm::Value *ktype = emit_table_get_keytype(def, node, offset);
// We need to check that the key type is also short string
llvm::Value *is_shortstring =
emit_is_value_of_type(def, ktype, LUA__TSHRSTR, "is_shortstring");
llvm::BasicBlock *testkey =
llvm::BasicBlock::Create(def->jitState->context(), "testkey");
llvm::BasicBlock *testok =
llvm::BasicBlock::Create(def->jitState->context(), "testok");
llvm::BasicBlock *testfail =
llvm::BasicBlock::Create(def->jitState->context(), "testfail");
llvm::BasicBlock *testend =
llvm::BasicBlock::Create(def->jitState->context(), "testend");
auto brinst1 = def->builder->CreateCondBr(is_shortstring, testkey, testfail);
attach_branch_weights(def, brinst1, 100, 0);
// Now we need to compare the keys
def->f->getBasicBlockList().push_back(testkey);
def->builder->SetInsertPoint(testkey);
// Get the key from the node
llvm::Value *keyvalue = emit_table_get_strkey(def, node, offset);
// Cast the pointer to a intptr so we can compare
llvm::Value *intptr =
def->builder->CreatePtrToInt(keyvalue, def->types->C_intptr_t);
llvm::Value *ourptr =
llvm::ConstantInt::get(def->types->C_intptr_t, (intptr_t)key);
// Compare the two pointers
// If they match then we found the element
llvm::Value *same = def->builder->CreateICmpEQ(intptr, ourptr);
auto brinst2 = def->builder->CreateCondBr(same, testok, testfail);
attach_branch_weights(def, brinst2, 5, 2);
// If key found return the value
def->f->getBasicBlockList().push_back(testok);
def->builder->SetInsertPoint(testok);
// Get the value
llvm::Value *value1 = emit_table_get_value(def, node, offset);
llvm::Value *rc1 = emit_gep_register_or_constant(def, C);
def->builder->CreateBr(testend);
// Not found so call luaH_getstr
def->f->getBasicBlockList().push_back(testfail);
def->builder->SetInsertPoint(testfail);
llvm::Value *rc2 = emit_gep_register_or_constant(def, C);
llvm::Value *value2 = CreateCall2(def->builder, def->luaH_getstrF, t,
emit_load_reg_s(def, rc2));
def->builder->CreateBr(testend);
// merge
def->f->getBasicBlockList().push_back(testend);
def->builder->SetInsertPoint(testend);
llvm::PHINode *phi = def->builder->CreatePHI(def->types->pTValueT, 2);
phi->addIncoming(value1, testok);
phi->addIncoming(value2, testfail);
llvm::PHINode *phi2 = def->builder->CreatePHI(def->types->pTValueT, 2);
phi2->addIncoming(rc1, testok);
phi2->addIncoming(rc2, testfail);
emit_finish_GETTABLE(def, phi, t, ra, rb, phi2);
}
// R(A) := R(B)[RK(C)]
// Emit inline code for accessing a table element using a string key
// We try to access the element using the hash part but if the
// key is not in the main position then we fall back on luaH_getstr().
// IMPORTANT - this emitter should only be called when key is known to
// to be short string
void RaviCodeGenerator::emit_common_GETTABLE_S(RaviFunctionDef *def, int A,
int B, int C, TString *key) {
// The code we want to generate is this:
// struct Node *n = hashstr(t, key);
// const struct TValue *k = gkey(n);
// TValue *v;
// if (ttisshrstring(k) && eqshrstr(tsvalue(k), key))
// v = gval(n);
// else
// v = luaH_getstr(t, key);
// if (!ttisnil(v) || metamethod_absent(t->metatable, TM_INDEX)) {
// setobj2s(L, ra, v);
// }
// else {
// Protect(luaV_finishget(L, rb, rc, ra, v));
// }
// A number of macros are involved above do the
// the generated code is somewhat more complex
llvm::Value *rb = emit_gep_register(def, B);
emit_common_GETTABLE_S_(def, A, rb, C, key);
}
// R(A) := R(B)[RK(C)]
// Emit inline code for accessing a table element using a string key
// We try to access the element using the hash part but if the
// key is not in the main position then we fall back on luaH_getstr().
// IMPORTANT - this emitter should only be called when key is known to
// to be short string
void RaviCodeGenerator::emit_GETTABLE_S(RaviFunctionDef *def, int A, int B,
int C, int pc, TString *key) {
// The code we want to generate is this:
// struct Node *n = hashstr(t, key);
// const struct TValue *k = gkey(n);
// if (ttisshrstring(k) && eqshrstr(tsvalue(k), key))
// return gval(n);
// return luaH_getstr(t, key);
emit_debug_trace(def, OP_RAVI_TABLE_GETFIELD, pc);
emit_load_base(def);
emit_common_GETTABLE_S(def, A, B, C, key);
}
void RaviCodeGenerator::emit_FARRAY_GET(RaviFunctionDef *def, int A, int B,
int C, bool omitArrayGetRangeCheck,
int pc) {
//#define raviH_get_float_inline(L, t, key, v)
//{ unsigned ukey = (unsigned)((key));
// lua_Number *data = (lua_Number *)t->data;
// if (ukey < t->len) {
// setfltvalue(v, data[ukey]);
// }else
// luaG_runerror(L, "array out of bounds");
//}
// TValue *rb = RB(i);
// TValue *rc = RKC(i);
// lua_Integer idx = ivalue(rc);
// Table *t = hvalue(rb);
// raviH_get_float_inline(L, t, idx, ra);
emit_debug_trace(def, OP_RAVI_FARRAY_GET, pc);
emit_load_base(def);
llvm::Value *ra = emit_gep_register(def, A);
llvm::Value *rb = emit_gep_register(def, B);
llvm::Value *rc = emit_gep_register_or_constant(def, C);
llvm::Instruction *key = emit_load_reg_i(def, rc);
llvm::Instruction *t = emit_load_reg_arr(def, rb);
llvm::Instruction *data = emit_load_reg_h_floatarray(def, t);
llvm::BasicBlock *then_block = nullptr;
llvm::BasicBlock *else_block = nullptr;
llvm::BasicBlock *end_block = nullptr;
if (!omitArrayGetRangeCheck) {
llvm::Instruction *len = emit_load_ravi_arraylength(def, t);
llvm::Value *ulen =
def->builder->CreateZExt(len, def->types->lua_UnsignedT, "ulen");
llvm::Value *cmp = def->builder->CreateICmpULT(key, ulen);
then_block = llvm::BasicBlock::Create(def->jitState->context(),
"if.in.range", def->f);
else_block =
llvm::BasicBlock::Create(def->jitState->context(), "if.not.in.range");
end_block = llvm::BasicBlock::Create(def->jitState->context(), "if.end");
auto brinst1 = def->builder->CreateCondBr(cmp, then_block, else_block);
attach_branch_weights(def, brinst1, 100, 0);
def->builder->SetInsertPoint(then_block);
}
llvm::Value *ptr = def->builder->CreateGEP(data, key);
llvm::Instruction *value = def->builder->CreateLoad(ptr);
value->setMetadata(llvm::LLVMContext::MD_tbaa, def->types->tbaa_pdoubleT);
emit_store_reg_n_withtype(def, value, ra);
if (!omitArrayGetRangeCheck) {
def->builder->CreateBr(end_block);
def->f->getBasicBlockList().push_back(else_block);
def->builder->SetInsertPoint(else_block);
emit_raise_lua_error(def, "array out of bounds");
def->builder->CreateBr(end_block);
def->f->getBasicBlockList().push_back(end_block);
def->builder->SetInsertPoint(end_block);
}
}
void RaviCodeGenerator::emit_IARRAY_GET(RaviFunctionDef *def, int A, int B,
int C, bool omitArrayGetRangeCheck,
int pc) {
//#define raviH_get_int_inline(L, t, key, v)
//{ unsigned ukey = (unsigned)((key));
// lua_Integer *data = (lua_Integer *)t->data;
// if (ukey < t->len) {
// setivalue(v, data[ukey]);
// } else
// luaG_runerror(L, "array out of bounds");
//}
// TValue *rb = RB(i);
// TValue *rc = RKC(i);
// lua_Integer idx = ivalue(rc);
// Table *t = hvalue(rb);
// raviH_get_int_inline(L, t, idx, ra);
emit_debug_trace(def, OP_RAVI_IARRAY_GET, pc);
emit_load_base(def);
llvm::Value *ra = emit_gep_register(def, A);
llvm::Value *rb = emit_gep_register(def, B);
llvm::Value *rc = emit_gep_register_or_constant(def, C);
llvm::Instruction *key = emit_load_reg_i(def, rc);
llvm::Instruction *t = emit_load_reg_arr(def, rb);
llvm::Instruction *data = emit_load_reg_h_intarray(def, t);
llvm::BasicBlock *then_block = nullptr;
llvm::BasicBlock *else_block = nullptr;
llvm::BasicBlock *end_block = nullptr;
if (!omitArrayGetRangeCheck) {
llvm::Instruction *len = emit_load_ravi_arraylength(def, t);
llvm::Value *ulen =
def->builder->CreateZExt(len, def->types->lua_UnsignedT, "ulen");
llvm::Value *cmp = def->builder->CreateICmpULT(key, ulen);
then_block = llvm::BasicBlock::Create(def->jitState->context(),
"if.in.range", def->f);
else_block =
llvm::BasicBlock::Create(def->jitState->context(), "if.not.in.range");
end_block = llvm::BasicBlock::Create(def->jitState->context(), "if.end");
auto brinst1 = def->builder->CreateCondBr(cmp, then_block, else_block);
attach_branch_weights(def, brinst1, 100, 0);
def->builder->SetInsertPoint(then_block);
}
llvm::Value *ptr = def->builder->CreateGEP(data, key);
llvm::Instruction *value = def->builder->CreateLoad(ptr);
value->setMetadata(llvm::LLVMContext::MD_tbaa, def->types->tbaa_plonglongT);
emit_store_reg_i_withtype(def, value, ra);
if (!omitArrayGetRangeCheck) {
def->builder->CreateBr(end_block);
def->f->getBasicBlockList().push_back(else_block);
def->builder->SetInsertPoint(else_block);
emit_raise_lua_error(def, "array out of bounds");
def->builder->CreateBr(end_block);
def->f->getBasicBlockList().push_back(end_block);
def->builder->SetInsertPoint(end_block);
}
}
void RaviCodeGenerator::emit_IARRAY_SET(RaviFunctionDef *def, int A, int B,
int C, bool known_int, int pc) {
//#define raviH_set_int_inline(L, t, key, value)
//{ unsigned ukey = (unsigned)((key));
// lua_Integer *data = (lua_Integer *)t->data;
// if (ukey < t->len) {
// data[ukey] = value;
// } else
// raviH_set_int(L, t, ukey, value);
//}
// Table *t = hvalue(ra);
// TValue *rb = RKB(i);
// TValue *rc = RKC(i);
// lua_Integer idx = ivalue(rb);
// lua_Integer value = ivalue(rc);
// raviH_set_int_inline(L, t, idx, value);
emit_debug_trace(def, known_int ? OP_RAVI_IARRAY_SETI : OP_RAVI_IARRAY_SET,
pc);
emit_load_base(def);
llvm::Value *ra = emit_gep_register(def, A);
llvm::Value *rb = emit_gep_register_or_constant(def, B);
llvm::Value *rc = emit_gep_register_or_constant(def, C);
llvm::Instruction *key = emit_load_reg_i(def, rb);
llvm::Instruction *value =
known_int ? emit_load_reg_i(def, rc) : emit_tointeger(def, rc);
llvm::Instruction *t = emit_load_reg_arr(def, ra);
llvm::Instruction *data = emit_load_reg_h_intarray(def, t);
llvm::Instruction *len = emit_load_ravi_arraylength(def, t);
llvm::Value *ulen =
def->builder->CreateZExt(len, def->types->lua_UnsignedT, "ulen");
llvm::Value *cmp = def->builder->CreateICmpULT(key, ulen);
llvm::BasicBlock *then_block =
llvm::BasicBlock::Create(def->jitState->context(), "if.in.range", def->f);
llvm::BasicBlock *else_block =
llvm::BasicBlock::Create(def->jitState->context(), "if.not.in.range");
llvm::BasicBlock *end_block =
llvm::BasicBlock::Create(def->jitState->context(), "if.end");
def->builder->CreateCondBr(cmp, then_block, else_block);
def->builder->SetInsertPoint(then_block);
llvm::Value *ptr = def->builder->CreateGEP(data, key);
llvm::Instruction *storeinst = def->builder->CreateStore(value, ptr);
storeinst->setMetadata(llvm::LLVMContext::MD_tbaa,
def->types->tbaa_plonglongT);
def->builder->CreateBr(end_block);
def->f->getBasicBlockList().push_back(else_block);
def->builder->SetInsertPoint(else_block);
CreateCall4(def->builder, def->raviH_set_intF, def->L, t, key, value);
def->builder->CreateBr(end_block);
def->f->getBasicBlockList().push_back(end_block);
def->builder->SetInsertPoint(end_block);
}
void RaviCodeGenerator::emit_FARRAY_SET(RaviFunctionDef *def, int A, int B,
int C, bool known_float, int pc) {
//#define raviH_set_float_inline(L, t, key, value)
//{ unsigned ukey = (unsigned)((key));
// lua_Number *data = (lua_Number *)t->data;
// if (ukey < t->len) {
// data[ukey] = value;
// } else
// raviH_set_float(L, t, ukey, value);
// }
// Table *t = hvalue(ra);
// TValue *rb = RKB(i);
// TValue *rc = RKC(i);
// lua_Integer idx = ivalue(rb);
// if (ttisfloat(rc)) {
// raviH_set_float_inline(L, t, idx, fltvalue(rc));
//}
// else {
// raviH_set_float_inline(L, t, idx, ((lua_Number)ivalue(rc)));
//}
emit_debug_trace(
def, known_float ? OP_RAVI_FARRAY_SETF : OP_RAVI_FARRAY_SET, pc);
emit_load_base(def);
llvm::Value *ra = emit_gep_register(def, A);
llvm::Value *rb = emit_gep_register_or_constant(def, B);
llvm::Value *rc = emit_gep_register_or_constant(def, C);
llvm::Instruction *key = emit_load_reg_i(def, rb);
llvm::Instruction *value =
known_float ? emit_load_reg_n(def, rc) : emit_tofloat(def, rc);
llvm::Instruction *t = emit_load_reg_arr(def, ra);
llvm::Instruction *data = emit_load_reg_h_floatarray(def, t);
llvm::Instruction *len = emit_load_ravi_arraylength(def, t);
llvm::Value *ulen =
def->builder->CreateZExt(len, def->types->lua_UnsignedT, "ulen");
llvm::Value *cmp = def->builder->CreateICmpULT(key, ulen);
llvm::BasicBlock *then_block =
llvm::BasicBlock::Create(def->jitState->context(), "if.in.range", def->f);
llvm::BasicBlock *else_block =
llvm::BasicBlock::Create(def->jitState->context(), "if.not.in.range");
llvm::BasicBlock *end_block =
llvm::BasicBlock::Create(def->jitState->context(), "if.end");
def->builder->CreateCondBr(cmp, then_block, else_block);
def->builder->SetInsertPoint(then_block);
llvm::Value *ptr = def->builder->CreateGEP(data, key);
llvm::Instruction *storeinst = def->builder->CreateStore(value, ptr);
storeinst->setMetadata(llvm::LLVMContext::MD_tbaa, def->types->tbaa_pdoubleT);
def->builder->CreateBr(end_block);
def->f->getBasicBlockList().push_back(else_block);
def->builder->SetInsertPoint(else_block);
CreateCall4(def->builder, def->raviH_set_floatF, def->L, t, key, value);
def->builder->CreateBr(end_block);
def->f->getBasicBlockList().push_back(end_block);
def->builder->SetInsertPoint(end_block);
}
// R(A) := UpValue[B]
void RaviCodeGenerator::emit_GETUPVAL(RaviFunctionDef *def, int A, int B,
int pc) {
// int b = GETARG_B(i);
// setobj2s(L, ra, cl->upvals[b]->v);
emit_debug_trace(def, OP_GETUPVAL, pc);
emit_load_base(def);
llvm::Value *ra = emit_gep_register(def, A);
llvm::Value *upval_ptr = emit_gep_upvals(def, B);
llvm::Instruction *upval = emit_load_pupval(def, upval_ptr);
llvm::Value *v = emit_load_upval_v(def, upval);
emit_assign(def, ra, v);
}
// UpValue[B] := R(A)
void RaviCodeGenerator::emit_SETUPVAL_Specific(RaviFunctionDef *def, int A,
int B, int pc, OpCode op,
llvm::Function *f) {
emit_debug_trace(def, op, pc);
emit_load_base(def);
llvm::Value *ra = emit_gep_register(def, A);
CreateCall4(def->builder, f, def->L, def->p_LClosure, ra,
llvm::ConstantInt::get(def->types->C_intT, B));
}
// UpValue[B] := R(A)
void RaviCodeGenerator::emit_SETUPVAL(RaviFunctionDef *def, int A, int B,
int pc) {
// UpVal *uv = cl->upvals[GETARG_B(i)];
// setobj(L, uv->v, ra);
// luaC_upvalbarrier(L, uv);
emit_debug_trace(def, OP_SETUPVAL, pc);
emit_load_base(def);
llvm::Value *ra = emit_gep_register(def, A);
llvm::Value *upval_ptr = emit_gep_upvals(def, B);
llvm::Instruction *upval = emit_load_pupval(def, upval_ptr);
llvm::Value *v = emit_load_upval_v(def, upval);
emit_assign(def, v, ra);
llvm::Value *type = emit_load_type(def, v);
llvm::Value *is_collectible =
def->builder->CreateAnd(type,
llvm::ConstantInt::get(def->types->lua_LuaTypeT, BIT_ISCOLLECTABLE));
llvm::Value *value = emit_gep_upval_value(def, upval);
llvm::Value *cmp = def->builder->CreateICmpNE(v, value, "v.ne.value");
llvm::Value *tobool = def->builder->CreateICmpEQ(
is_collectible,
llvm::ConstantInt::get(def->types->lua_LuaTypeT, 0),
"not.collectible");
llvm::Value *orcond =
def->builder->CreateOr(cmp, tobool, "v.ne.value.or.not.collectible");
llvm::BasicBlock *then =
llvm::BasicBlock::Create(def->jitState->context(), "if.then", def->f);
llvm::BasicBlock *end =
llvm::BasicBlock::Create(def->jitState->context(), "if.end");
def->builder->CreateCondBr(orcond, end, then);
def->builder->SetInsertPoint(then);
CreateCall2(def->builder, def->luaC_upvalbarrierF, def->L, upval);
def->builder->CreateBr(end);
def->f->getBasicBlockList().push_back(end);
def->builder->SetInsertPoint(end);
}
// R(A) := UpValue[B][RK(C)]
void RaviCodeGenerator::emit_GETTABUP(RaviFunctionDef *def, int A, int B, int C,
int pc) {
// int b = GETARG_B(i);
// Protect(luaV_gettable(L, cl->upvals[b]->v, RKC(i), ra));
bool traced = emit_debug_trace(def, OP_GETTABUP, pc);
// Below may invoke metamethod so we set savedpc
if (!traced) emit_update_savedpc(def, pc);
emit_load_base(def);
llvm::Value *ra = emit_gep_register(def, A);
llvm::Value *rc = emit_gep_register_or_constant(def, C);
llvm::Value *upval_ptr = emit_gep_upvals(def, B);
llvm::Instruction *upval = emit_load_pupval(def, upval_ptr);
llvm::Value *v = emit_load_upval_v(def, upval);
CreateCall4(def->builder, def->luaV_gettableF, def->L, v, rc, ra);
}
// R(A) := UpValue[B][RK(C)]
void RaviCodeGenerator::emit_GETTABUP_SK(RaviFunctionDef *def, int A, int B,
int C, int pc) {
// int b = GETARG_B(i);
// Protect(luaV_gettable(L, cl->upvals[b]->v, RKC(i), ra));
bool traced = emit_debug_trace(def, OP_RAVI_GETTABUP_SK, pc);
// Below may invoke metamethod so we set savedpc
if (!traced) emit_update_savedpc(def, pc);
emit_load_base(def);
llvm::Value *ra = emit_gep_register(def, A);
llvm::Value *rc = emit_gep_register_or_constant(def, C);
llvm::Value *upval_ptr = emit_gep_upvals(def, B);
llvm::Instruction *upval = emit_load_pupval(def, upval_ptr);
llvm::Value *v = emit_load_upval_v(def, upval);
CreateCall4(def->builder, def->raviV_gettable_sskeyF, def->L, v, rc, ra);
}
// UpValue[A][RK(B)] := RK(C)
void RaviCodeGenerator::emit_SETTABUP(RaviFunctionDef *def, int A, int B, int C,
int pc) {
// int a = GETARG_A(i);
// Protect(luaV_settable(L, cl->upvals[a]->v, RKB(i), RKC(i)));
bool traced = emit_debug_trace(def, OP_SETTABUP, pc);
if (!traced) emit_update_savedpc(def, pc);
emit_load_base(def);
llvm::Value *rb = emit_gep_register_or_constant(def, B);
llvm::Value *rc = emit_gep_register_or_constant(def, C);
llvm::Value *upval_ptr = emit_gep_upvals(def, A);
llvm::Instruction *upval = emit_load_pupval(def, upval_ptr);
llvm::Value *v = emit_load_upval_v(def, upval);
CreateCall4(def->builder, def->luaV_settableF, def->L, v, rb, rc);
}
// UpValue[A][RK(B)] := RK(C)
void RaviCodeGenerator::emit_SETTABUP_SK(RaviFunctionDef *def, int A, int B,
int C, int pc) {
// int a = GETARG_A(i);
// Protect(luaV_settable(L, cl->upvals[a]->v, RKB(i), RKC(i)));
bool traced = emit_debug_trace(def, OP_RAVI_SETFIELD, pc);
if (!traced) emit_update_savedpc(def, pc);
emit_load_base(def);
llvm::Value *rb = emit_gep_register_or_constant(def, B);
llvm::Value *rc = emit_gep_register_or_constant(def, C);
llvm::Value *upval_ptr = emit_gep_upvals(def, A);
llvm::Instruction *upval = emit_load_pupval(def, upval_ptr);
llvm::Value *v = emit_load_upval_v(def, upval);
CreateCall4(def->builder, def->raviV_settable_sskeyF, def->L, v, rb, rc);
}
void RaviCodeGenerator::emit_NEWARRAYINT(RaviFunctionDef *def, int A, int pc) {
emit_debug_trace(def, OP_RAVI_NEW_IARRAY, pc);
emit_load_base(def);
llvm::Value *ra = emit_gep_register(def, A);
CreateCall3(def->builder, def->raviV_op_newarrayintF, def->L, def->ci_val,
ra);
}
void RaviCodeGenerator::emit_NEWARRAYFLOAT(RaviFunctionDef *def, int A,
int pc) {
emit_debug_trace(def, OP_RAVI_NEW_FARRAY, pc);
emit_load_base(def);
llvm::Value *ra = emit_gep_register(def, A);
CreateCall3(def->builder, def->raviV_op_newarrayfloatF, def->L, def->ci_val,
ra);
}
void RaviCodeGenerator::emit_NEWTABLE(RaviFunctionDef *def, int A, int B, int C,
int pc) {
// case OP_NEWTABLE: {
// int b = GETARG_B(i);
// int c = GETARG_C(i);
// Table *t = luaH_new(L);
// sethvalue(L, ra, t);
// if (b != 0 || c != 0)
// luaH_resize(L, t, luaO_fb2int(b), luaO_fb2int(c));
// checkGC(L, ra + 1);
// } break;
emit_debug_trace(def, OP_NEWTABLE, pc);
emit_load_base(def);
llvm::Value *ra = emit_gep_register(def, A);
CreateCall5(def->builder, def->raviV_op_newtableF, def->L, def->ci_val, ra,
def->types->kInt[B], def->types->kInt[C]);
}
void RaviCodeGenerator::emit_SETLIST(RaviFunctionDef *def, int A, int B, int C,
int pc) {
emit_debug_trace(def, OP_SETLIST, pc);
emit_load_base(def);
llvm::Value *ra = emit_gep_register(def, A);
CreateCall5(def->builder, def->raviV_op_setlistF, def->L, def->ci_val, ra,
def->types->kInt[B], def->types->kInt[C]);
}
void RaviCodeGenerator::emit_TOARRAY(RaviFunctionDef *def, int A,
int array_type_expected,
const char *errmsg, int pc) {
OpCode op = OP_RAVI_TOTAB;
LuaTypeCode expectedType = RAVI__TLTABLE;
switch (array_type_expected) {
case RAVI_TARRAYINT:
op = OP_RAVI_TOIARRAY;
expectedType = RAVI__TIARRAY;
break;
case RAVI_TARRAYFLT:
op = OP_RAVI_TOFARRAY;
expectedType = RAVI__TFARRAY;
break;
case RAVI_TTABLE:
default: break;
}
emit_debug_trace(def, op, pc);
emit_load_base(def);
llvm::Value *ra = emit_gep_register(def, A);
llvm::Instruction *type = emit_load_type(def, ra);
// type != expectedType
llvm::Value *cmp1 = emit_is_not_value_of_type(def, type, expectedType,
"is.not.expected.type");
llvm::BasicBlock *raise_error = llvm::BasicBlock::Create(
def->jitState->context(), "if.not.expected_type", def->f);
llvm::BasicBlock *done =
llvm::BasicBlock::Create(def->jitState->context(), "done");
auto brinst1 = def->builder->CreateCondBr(cmp1, raise_error, done);
attach_branch_weights(def, brinst1, 0, 100);
def->builder->SetInsertPoint(raise_error);
// Conversion failed, so raise error
emit_raise_lua_error(def, errmsg);
def->builder->CreateBr(done);
def->f->getBasicBlockList().push_back(done);
def->builder->SetInsertPoint(done);
}
void RaviCodeGenerator::emit_MOVEIARRAY(RaviFunctionDef *def, int A, int B,
int pc) {
emit_debug_trace(def, OP_RAVI_MOVEIARRAY, pc);
emit_TOARRAY(def, B, RAVI_TARRAYINT, "integer[] expected", pc);
llvm::Value *src = emit_gep_register(def, B);
llvm::Value *dest = emit_gep_register(def, A);
emit_assign(def, dest, src);
}
void RaviCodeGenerator::emit_MOVEFARRAY(RaviFunctionDef *def, int A, int B,
int pc) {
emit_debug_trace(def, OP_RAVI_MOVEFARRAY, pc);
emit_TOARRAY(def, B, RAVI_TARRAYFLT, "number[] expected", pc);
llvm::Value *src = emit_gep_register(def, B);
llvm::Value *dest = emit_gep_register(def, A);
emit_assign(def, dest, src);
}
void RaviCodeGenerator::emit_MOVETAB(RaviFunctionDef *def, int A, int B,
int pc) {
emit_debug_trace(def, OP_RAVI_MOVETAB, pc);
emit_TOARRAY(def, B, RAVI_TTABLE, "table expected", pc);
llvm::Value *src = emit_gep_register(def, B);
llvm::Value *dest = emit_gep_register(def, A);
emit_assign(def, dest, src);
}
}

@ -1,140 +0,0 @@
/******************************************************************************
* Copyright (C) 2015-2020 Dibyendu Majumdar
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
******************************************************************************/
#include "ravi_llvmcodegen.h"
namespace ravi {
void RaviCodeGenerator::emit_TFORCALL(RaviFunctionDef *def, int A, int B, int C,
int j, int jA, int pc) {
// case OP_TFORCALL: {
// StkId cb = ra + 3; /* call base */
// setobjs2s(L, cb + 2, ra + 2);
// setobjs2s(L, cb + 1, ra + 1);
// setobjs2s(L, cb, ra);
// L->top = cb + 3; /* func. + 2 args (state and index) */
// Protect(luaD_call(L, cb, GETARG_C(i), 1));
// L->top = ci->top;
// i = *(ci->u.l.savedpc++); /* go to next instruction */
// ra = RA(i);
// lua_assert(GET_OPCODE(i) == OP_TFORLOOP);
// goto l_tforloop;
// }
// case OP_TFORLOOP: {
// l_tforloop:
// if (!ttisnil(ra + 1)) { /* continue loop? */
// setobjs2s(L, ra, ra + 1); /* save control variable */
// ci->u.l.savedpc += GETARG_sBx(i); /* jump back */
// }
// } break;
bool traced = emit_debug_trace(def, OP_TFORCALL, pc);
// Below may invoke metamethod so we set savedpc
if (!traced)emit_update_savedpc(def, pc);
// Load pointer to base
emit_load_base(def);
// Get pointer to register A
llvm::Value *ra = emit_gep_register(def, A);
llvm::Value *ra1 = emit_gep_register(def, A + 1);
llvm::Value *ra2 = emit_gep_register(def, A + 2);
llvm::Value *cb = emit_gep_register(def, A + 3);
llvm::Value *cb1 = emit_gep_register(def, A + 4);
llvm::Value *cb2 = emit_gep_register(def, A + 5);
emit_assign(def, cb2, ra2);
emit_assign(def, cb1, ra1);
emit_assign(def, cb, ra);
// L->top = cb + 3; /* func. + 2 args (state and index) */
emit_set_L_top_toreg(def, A + 6);
// Protect(luaD_call(L, cb, GETARG_C(i)));
CreateCall3(def->builder, def->luaD_callF, def->L, cb, def->types->kInt[C]);
// reload base
emit_load_base(def);
// L->top = ci->top;
emit_refresh_L_top(def, def->ci_val);
ra = emit_gep_register(def, jA);
ra1 = emit_gep_register(def, jA + 1);
llvm::Value *type = emit_load_type(def, ra1);
// Test if type != LUA_TNIL (0)
llvm::Value *isnotnil =
emit_is_not_value_of_type(def, type, LUA__TNIL, "is.not.nil");
llvm::BasicBlock *then_block =
llvm::BasicBlock::Create(def->jitState->context(), "if.not.nil", def->f);
llvm::BasicBlock *else_block =
llvm::BasicBlock::Create(def->jitState->context(), "if.nil");
def->builder->CreateCondBr(isnotnil, then_block, else_block);
def->builder->SetInsertPoint(then_block);
emit_assign(def, ra, ra1);
// Do the jump
def->builder->CreateBr(def->jmp_targets[j].jmp1);
// Add the else block and make it current so that the next instruction flows
// here
def->f->getBasicBlockList().push_back(else_block);
def->builder->SetInsertPoint(else_block);
}
void RaviCodeGenerator::emit_TFORLOOP(RaviFunctionDef *def, int A, int j,
int pc) {
// case OP_TFORLOOP: {
// l_tforloop:
// if (!ttisnil(ra + 1)) { /* continue loop? */
// setobjs2s(L, ra, ra + 1); /* save control variable */
// ci->u.l.savedpc += GETARG_sBx(i); /* jump back */
// }
// } break;
emit_debug_trace(def, OP_TFORLOOP, pc);
// Load pointer to base
emit_load_base(def);
// Get pointer to register A
llvm::Value *ra = emit_gep_register(def, A);
llvm::Value *ra1 = emit_gep_register(def, A + 1);
llvm::Value *type = emit_load_type(def, ra1);
// Test if type != LUA_TNIL (0)
llvm::Value *isnotnil =
emit_is_not_value_of_type(def, type, LUA__TNIL, "is.not.nil");
llvm::BasicBlock *then_block =
llvm::BasicBlock::Create(def->jitState->context(), "if.not.nil", def->f);
llvm::BasicBlock *else_block =
llvm::BasicBlock::Create(def->jitState->context(), "if.nil");
def->builder->CreateCondBr(isnotnil, then_block, else_block);
def->builder->SetInsertPoint(then_block);
emit_assign(def, ra, ra1);
// Do the jump
def->builder->CreateBr(def->jmp_targets[j].jmp1);
// Add the else block and make it current so that the next instruction flows
// here
def->f->getBasicBlockList().push_back(else_block);
def->builder->SetInsertPoint(else_block);
}
}

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save