issue #203 Removing LLVM support, see llvm branch for archived version
parent
05f365352c
commit
66511033fd
@ -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 ..
|
|
@ -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
@ -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,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…
Reference in new issue