You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ravi/src/ravi_llvmcodegen.cpp

2120 lines
79 KiB

/******************************************************************************
* 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 <ravijit.h>
#include <ravi_llvmcodegen.h>
#include <ravi_jitshared.h>
#include <dmr_c.h>
namespace ravi {
RaviBranchDef::RaviBranchDef()
: jmp1(nullptr),
jmp2(nullptr),
jmp3(nullptr),
jmp4(nullptr),
ilimit(nullptr),
istep(nullptr),
iidx(nullptr),
flimit(nullptr),
fstep(nullptr),
fidx(nullptr) {}
RaviCodeGenerator::RaviCodeGenerator(RaviJITState *jitState)
: jitState_(jitState), id_(1) {
temp_[0] = 0;
}
const char *RaviCodeGenerator::unique_function_name() {
// Create unique name for the compiled function
// Lua functions are nameless so a compiled function has no name
// as far as Lua is concerned
snprintf(temp_, sizeof temp_, "ravif%d", id_++);
return temp_;
}
llvm::CallInst *RaviCodeGenerator::CreateCall1(llvm::IRBuilder<> *builder,
llvm::Value *func,
llvm::Value *arg1) {
llvm::SmallVector<llvm::Value *, 1> values;
values.push_back(arg1);
return builder->CreateCall(func, values);
}
llvm::CallInst *RaviCodeGenerator::CreateCall2(llvm::IRBuilder<> *builder,
llvm::Value *func,
llvm::Value *arg1,
llvm::Value *arg2) {
llvm::SmallVector<llvm::Value *, 2> values;
values.push_back(arg1);
values.push_back(arg2);
return builder->CreateCall(func, values);
}
llvm::CallInst *RaviCodeGenerator::CreateCall3(llvm::IRBuilder<> *builder,
llvm::Value *func,
llvm::Value *arg1,
llvm::Value *arg2,
llvm::Value *arg3) {
llvm::SmallVector<llvm::Value *, 3> values;
values.push_back(arg1);
values.push_back(arg2);
values.push_back(arg3);
return builder->CreateCall(func, values);
}
llvm::CallInst *RaviCodeGenerator::CreateCall4(
llvm::IRBuilder<> *builder, llvm::Value *func, llvm::Value *arg1,
llvm::Value *arg2, llvm::Value *arg3, llvm::Value *arg4) {
llvm::SmallVector<llvm::Value *, 4> values;
values.push_back(arg1);
values.push_back(arg2);
values.push_back(arg3);
values.push_back(arg4);
return builder->CreateCall(func, values);
}
llvm::CallInst *RaviCodeGenerator::CreateCall5(
llvm::IRBuilder<> *builder, llvm::Value *func, llvm::Value *arg1,
llvm::Value *arg2, llvm::Value *arg3, llvm::Value *arg4,
llvm::Value *arg5) {
llvm::SmallVector<llvm::Value *, 5> values;
values.push_back(arg1);
values.push_back(arg2);
values.push_back(arg3);
values.push_back(arg4);
values.push_back(arg5);
return builder->CreateCall(func, values);
}
void RaviCodeGenerator::attach_branch_weights(RaviFunctionDef *def,
llvm::Instruction *ins,
uint32_t true_branch,
uint32_t false_branch) {
#if RAVI_USE_LLVM_BRANCH_WEIGHTS
ins->setMetadata(llvm::LLVMContext::MD_prof,
def->jitState->types()->mdbuilder.createBranchWeights(
true_branch, false_branch));
#else
(void)def;
(void)ins;
(void)true_branch;
(void)false_branch;
#endif
}
llvm::Value *RaviCodeGenerator::emit_gep(RaviFunctionDef *def, const char *name,
llvm::Value *s, int arg1) {
llvm::SmallVector<llvm::Value *, 1> values;
values.push_back(def->types->kInt[arg1]);
return def->builder->CreateInBoundsGEP(s, values, name);
}
llvm::Value *RaviCodeGenerator::emit_gep(RaviFunctionDef *def, const char *name,
llvm::Value *s, int arg1, int arg2) {
llvm::SmallVector<llvm::Value *, 2> values;
values.push_back(def->types->kInt[arg1]);
values.push_back(def->types->kInt[arg2]);
return def->builder->CreateInBoundsGEP(s, values, name);
}
llvm::Value *RaviCodeGenerator::emit_gep(RaviFunctionDef *def, const char *name,
llvm::Value *s, int arg1, int arg2,
int arg3) {
llvm::SmallVector<llvm::Value *, 3> values;
values.push_back(def->types->kInt[arg1]);
values.push_back(def->types->kInt[arg2]);
values.push_back(def->types->kInt[arg3]);
return def->builder->CreateInBoundsGEP(s, values, name);
}
llvm::Value *RaviCodeGenerator::emit_gep(RaviFunctionDef *def, const char *name,
llvm::Value *ptr, llvm::Value *offset,
int arg1) {
llvm::SmallVector<llvm::Value *, 2> values;
values.push_back(offset);
values.push_back(def->types->kInt[arg1]);
return def->builder->CreateInBoundsGEP(ptr, values, name);
}
llvm::Value *RaviCodeGenerator::emit_gep(RaviFunctionDef *def, const char *name,
llvm::Value *ptr, llvm::Value *offset,
int arg1, int arg2) {
llvm::SmallVector<llvm::Value *, 3> values;
values.push_back(offset);
values.push_back(def->types->kInt[arg1]);
values.push_back(def->types->kInt[arg2]);
return def->builder->CreateInBoundsGEP(ptr, values, name);
}
llvm::Value *RaviCodeGenerator::emit_array_get(RaviFunctionDef *def,
llvm::Value *ptr, int offset) {
// emit code for &ptr[offset]
return def->builder->CreateInBoundsGEP(
ptr, llvm::ConstantInt::get(def->types->C_intT, offset));
}
// emit code for LClosure *cl = clLvalue(ci->func);
llvm::Instruction *RaviCodeGenerator::emit_gep_ci_func_value_gc_asLClosure(
RaviFunctionDef *def) {
// clLvalue() is a macros that expands to (LClosure *)ci->func->value_.gc
// via a complex series of macros and unions
// fortunately as func is at the beginning of the CallInfo
// structure we can just cast ci to LClosure***
// This insight is based on code generated by Clang
llvm::Value *pppLuaClosure =
def->builder->CreateBitCast(def->ci_val, def->types->pppLClosureT);
// Load pointer to LClosure**
llvm::Instruction *ppLuaClosure = def->builder->CreateLoad(pppLuaClosure);
ppLuaClosure->setMetadata(llvm::LLVMContext::MD_tbaa,
def->types->tbaa_CallInfo_funcT);
// Load pointer to LClosure*
llvm::Instruction *cl_ptr = def->builder->CreateLoad(ppLuaClosure);
cl_ptr->setMetadata(llvm::LLVMContext::MD_tbaa,
def->types->tbaa_CallInfo_func_LClosureT);
return cl_ptr;
}
// Retrieve the proto->sizep
// sizep is a count of the number of closures defined
// in the function
llvm::Instruction *RaviCodeGenerator::emit_load_proto_sizep(
RaviFunctionDef *def) {
llvm::Value *psize_ptr = emit_gep(def, "sizep", def->proto_ptr, 0, 10);
llvm::Instruction *psize = def->builder->CreateLoad(psize_ptr);
psize->setMetadata(llvm::LLVMContext::MD_tbaa, def->types->tbaa_Proto_sizepT);
return psize;
}
// Updates the savedpc pointer in the call frame
// The savedpc is unimportant for the JIT but it is relied upon
// by the debug interface. So we need to set this in order for the
// debug api to work. Rather than setting it on every bytecode instruction
// we have a dual approach. By default the JIT code only sets this prior to
// function calls - this enables better stack traces for example, and ad-hoc
// calls to debug api. An optional setting in the JIT compiler also
// enables this to be updated per bytecode instruction - this is only
// required if someone wishes to set a line hook. The second option
// is very expensive and will inhibit optimizations, hence it is optional
// See issue #15
void RaviCodeGenerator::emit_update_savedpc(RaviFunctionDef *def, int pc) {
// Get proto->code
llvm::Value *proto_code_ptr = emit_gep(def, "code", def->proto_ptr, 0, 15);
llvm::Instruction *code_ptr = def->builder->CreateLoad(proto_code_ptr);
code_ptr->setMetadata(llvm::LLVMContext::MD_tbaa,
def->types->tbaa_Proto_codeT);
// Need to set savedpc to the next instruction (pc+1) rather than current as
// that is
// what the VM does in interpreted mode
llvm::Value *code_offset_ptr = emit_array_get(def, code_ptr, pc + 1);
llvm::Value *savedpc_ptr = emit_gep(def, "savedpc", def->ci_val, 0, 4, 1);
llvm::Instruction *ins =
def->builder->CreateStore(code_offset_ptr, savedpc_ptr);
ins->setMetadata(llvm::LLVMContext::MD_tbaa,
def->types->tbaa_CallInfo_savedpcT);
}
// Generate code to reload the function's stack 'base'
// pointer - the VM needs to do this after any bytecode that
// may invoke a Lua function and thereby resize the stack; which
// would invalidate the current 'base' pointer
void RaviCodeGenerator::emit_load_base(RaviFunctionDef *def) {
// Load pointer to base
llvm::Instruction *base_ptr = def->builder->CreateLoad(def->Ci_base);
base_ptr->setMetadata(llvm::LLVMContext::MD_tbaa,
def->types->tbaa_luaState_ci_baseT);
def->base_ptr = base_ptr;
}
// emit code to obtain address of register at location A
llvm::Value *RaviCodeGenerator::emit_gep_register(RaviFunctionDef *def, int A) {
llvm::Value *dest;
if (A == 0) {
// If A is 0 we can use the base pointer which is &base[0]
dest = def->base_ptr;
}
else {
// emit &base[A]
dest = emit_array_get(def, def->base_ptr, A);
}
return dest;
}
// Emit code to obtain address of a constant
llvm::Value *RaviCodeGenerator::emit_gep_constant(RaviFunctionDef *def,
int Bx) {
// Load pointer to k
llvm::Value *k_ptr = def->k_ptr;
llvm::Value *src;
if (Bx == 0) {
// If Bx is 0 we can use the base pointer which is &k[0]
src = k_ptr;
}
else {
// emit &k[Bx]
src = emit_array_get(def, k_ptr, Bx);
}
return src;
}
// Loads the value at register or constant at location B -
// if the value is an integer constant
// then returns a constant literal
llvm::Value *RaviCodeGenerator::emit_load_register_or_constant_i(
RaviFunctionDef *def, int B) {
if (ISK(B) && def->p->k[INDEXK(B)].tt_ == LUA_TNUMINT) {
TValue *Konst = &def->p->k[INDEXK(B)];
return llvm::ConstantInt::get(def->types->lua_IntegerT, Konst->value_.i);
}
else {
llvm::Value *rb = emit_gep_register_or_constant(def, B);
return emit_load_reg_i(def, rb);
}
}
// Loads the value at register B - if the value is an number constant
// then returns a constant literal
llvm::Value *RaviCodeGenerator::emit_load_register_or_constant_n(
RaviFunctionDef *def, int B) {
if (ISK(B) && def->p->k[INDEXK(B)].tt_ == LUA_TNUMFLT) {
TValue *Konst = &def->p->k[INDEXK(B)];
return llvm::ConstantFP::get(def->types->lua_NumberT, Konst->value_.n);
}
else {
llvm::Value *rb = emit_gep_register_or_constant(def, B);
return emit_load_reg_n(def, rb);
}
}
// emit code to load the lua_Number value from register (TValue)
llvm::Instruction *RaviCodeGenerator::emit_load_reg_n(RaviFunctionDef *def,
llvm::Value *rb) {
llvm::Value *rb_n = def->builder->CreateBitCast(rb, def->types->plua_NumberT);
// llvm::Value *rb_n = emit_gep(def, "value.value_.n", rb, 0, 0, 0);
llvm::Instruction *lhs = def->builder->CreateLoad(rb_n);
lhs->setMetadata(llvm::LLVMContext::MD_tbaa, def->types->tbaa_TValue_nT);
return lhs;
}
// emit code to load the lua_Integer value from register (TValue)
llvm::Instruction *RaviCodeGenerator::emit_load_reg_i(RaviFunctionDef *def,
llvm::Value *rb) {
llvm::Value *rb_n =
def->builder->CreateBitCast(rb, def->types->plua_IntegerT);
llvm::Instruction *lhs = def->builder->CreateLoad(rb_n);
lhs->setMetadata(llvm::LLVMContext::MD_tbaa, def->types->tbaa_TValue_nT);
return lhs;
}
// emit code to load the boolean value from register (TValue)
llvm::Instruction *RaviCodeGenerator::emit_load_reg_b(RaviFunctionDef *def,
llvm::Value *rb) {
llvm::Value *rb_n = def->builder->CreateBitCast(rb, def->types->C_pintT);
llvm::Instruction *lhs = def->builder->CreateLoad(rb_n);
lhs->setMetadata(llvm::LLVMContext::MD_tbaa, def->types->tbaa_TValue_nT);
return lhs;
}
// emit code to load the table value from register (TValue)
llvm::Instruction *RaviCodeGenerator::emit_load_reg_h(RaviFunctionDef *def,
llvm::Value *rb) {
llvm::Value *rb_h = def->builder->CreateBitCast(rb, def->types->ppTableT);
llvm::Instruction *h = def->builder->CreateLoad(rb_h);
h->setMetadata(llvm::LLVMContext::MD_tbaa, def->types->tbaa_TValue_hT);
return h;
}
// Gets the size of the hash table
// This is the sizenode() macro in lobject.h
llvm::Value *RaviCodeGenerator::emit_table_get_hashsize(RaviFunctionDef *def,
llvm::Value *t) {
// Obtain the lsizenode of the hash table
llvm::Value *lsizenode_ptr = emit_gep(def, "lsizenode", t, 0, 4);
llvm::Instruction *lsizenode = def->builder->CreateLoad(lsizenode_ptr);
lsizenode->setMetadata(llvm::LLVMContext::MD_tbaa,
def->types->tbaa_Table_lsizenode);
// convert to integer (lsizenode is a byte)
llvm::Value *intsize =
def->builder->CreateZExt(lsizenode, def->types->C_intT);
// #define twoto(x) (1<<(x))
// #define sizenode(t) (twoto((t)->lsizenode))
llvm::Value *size = def->builder->CreateShl(def->types->kInt[1], intsize);
return size;
}
// Gets the location of the hash node for given string key
// return value is the offset into the node array
llvm::Value *RaviCodeGenerator::emit_table_get_hashstr(RaviFunctionDef *def,
llvm::Value *table,
TString *key) {
#if RAVI_USE_NEWHASH
unsigned int hash = key->hash;
llvm::Value *hmask_ptr = emit_gep(def, "hmask", table, 0, 12);
llvm::Instruction *hmask = def->builder->CreateLoad(hmask_ptr);
hmask->setMetadata(llvm::LLVMContext::MD_tbaa, def->types->tbaa_Table_hmask);
llvm::Value *offset = def->builder->CreateAnd(
llvm::ConstantInt::get(def->types->C_intT, hash), hmask);
#else
llvm::Value *size = emit_table_get_hashsize(def, table);
unsigned int hash = key->hash;
// #define lmod(s,size) (cast(int, (s) & ((size)-1)))
llvm::Value *sizeminusone =
def->builder->CreateNSWSub(size, def->types->kInt[1]);
llvm::Value *offset = def->builder->CreateAnd(
llvm::ConstantInt::get(def->types->C_intT, hash), sizeminusone);
#endif
return offset;
}
// Gets access to the Table's node array (t->node)
llvm::Value *RaviCodeGenerator::emit_table_get_nodearray(RaviFunctionDef *def,
llvm::Value *table) {
// Get access to the node array
llvm::Value *node_ptr = emit_gep(def, "node", table, 0, 7);
llvm::Instruction *node = def->builder->CreateLoad(node_ptr);
node->setMetadata(llvm::LLVMContext::MD_tbaa, def->types->tbaa_Table_node);
return node;
}
// Given a pointer to table's node array (node = t->node) and
// the location of the hashed key (index), this method retrieves the
// type of the value stored at the node - return value is of type int
// and is the type information stored in TValue->tt field.
llvm::Value *RaviCodeGenerator::emit_table_get_keytype(RaviFunctionDef *def,
llvm::Value *node,
llvm::Value *index) {
llvm::Value *ktype_ptr = emit_gep(def, "keytype", node, index, 1, 1);
llvm::Instruction *ktype = def->builder->CreateLoad(ktype_ptr);
ktype->setMetadata(llvm::LLVMContext::MD_tbaa, def->types->tbaa_TValue_ttT);
return ktype;
}
// Given a pointer to table's node array (node = t->node) and
// the location of the hashed key (index), this method retrieves the
// the string value stored at the node - return value is of type TString*
llvm::Value *RaviCodeGenerator::emit_table_get_strkey(RaviFunctionDef *def,
llvm::Value *node,
llvm::Value *index) {
llvm::Value *value_ptr = emit_gep(def, "keyvalue", node, index, 1, 0);
llvm::Value *sptr =
def->builder->CreateBitCast(value_ptr, def->types->ppTStringT);
llvm::Instruction *keyvalue = def->builder->CreateLoad(sptr);
keyvalue->setMetadata(llvm::LLVMContext::MD_tbaa, def->types->tbaa_ppointerT);
return keyvalue;
}
// Given a pointer to table's node array (node = t->node) and
// the location of the hashed key (index), this method retrieves the
// the pointer to value stored at the node - return value is of type TValue*
llvm::Value *RaviCodeGenerator::emit_table_get_value(RaviFunctionDef *def,
llvm::Value *node,
llvm::Value *index) {
return emit_gep(def, "nodeval", node, index, 0);
}
// Gets the size of the table's array part
llvm::Value *RaviCodeGenerator::emit_table_get_arraysize(RaviFunctionDef *def,
llvm::Value *table) {
// Obtain the lsizenode of the hash table
llvm::Value *sizearray_ptr = emit_gep(def, "sizearray", table, 0, 5);
llvm::Instruction *sizearray = def->builder->CreateLoad(sizearray_ptr);
sizearray->setMetadata(llvm::LLVMContext::MD_tbaa,
def->types->tbaa_Table_sizearray);
return sizearray;
}
llvm::Value *RaviCodeGenerator::emit_table_get_array(RaviFunctionDef *def,
llvm::Value *table) {
// Get access to the node array
llvm::Value *array_ptr = emit_gep(def, "array", table, 0, 6);
llvm::Instruction *arry = def->builder->CreateLoad(array_ptr);
arry->setMetadata(llvm::LLVMContext::MD_tbaa, def->types->tbaa_Table_array);
return arry;
}
llvm::Instruction *RaviCodeGenerator::emit_load_reg_s(RaviFunctionDef *def,
llvm::Value *rb) {
llvm::Value *rb_s = def->builder->CreateBitCast(rb, def->types->ppTStringT);
llvm::Instruction *s = def->builder->CreateLoad(rb_s);
// Following TBAA is okay as the field type is a pointer
s->setMetadata(llvm::LLVMContext::MD_tbaa, def->types->tbaa_TValue_hT);
return s;
}
llvm::Instruction *RaviCodeGenerator::emit_load_reg_h_floatarray(
RaviFunctionDef *def, llvm::Instruction *h) {
llvm::Value *data_ptr = emit_gep(def, "data_ptr", h, 0, 11, 0);
llvm::Value *darray_ptr =
def->builder->CreateBitCast(data_ptr, def->types->pplua_NumberT);
llvm::Instruction *darray = def->builder->CreateLoad(darray_ptr);
darray->setMetadata(llvm::LLVMContext::MD_tbaa,
def->types->tbaa_RaviArray_dataT);
return darray;
}
llvm::Instruction *RaviCodeGenerator::emit_load_reg_h_intarray(
RaviFunctionDef *def, llvm::Instruction *h) {
llvm::Value *data_ptr = emit_gep(def, "data_ptr", h, 0, 11, 0);
llvm::Value *darray_ptr =
def->builder->CreateBitCast(data_ptr, def->types->pplua_IntegerT);
llvm::Instruction *darray = def->builder->CreateLoad(darray_ptr);
darray->setMetadata(llvm::LLVMContext::MD_tbaa,
def->types->tbaa_RaviArray_dataT);
return darray;
}
void RaviCodeGenerator::emit_store_reg_n(RaviFunctionDef *def,
llvm::Value *result,
llvm::Value *dest_ptr) {
llvm::Value *ra_n =
def->builder->CreateBitCast(dest_ptr, def->types->plua_NumberT);
// llvm::Value *ra_n = emit_gep(def, "value.value_.n", dest_ptr, 0, 0, 0);
llvm::Instruction *store = def->builder->CreateStore(result, ra_n);
store->setMetadata(llvm::LLVMContext::MD_tbaa, def->types->tbaa_TValue_nT);
}
void RaviCodeGenerator::emit_store_reg_n_withtype(RaviFunctionDef *def,
llvm::Value *result,
llvm::Value *dest_ptr) {
emit_store_reg_n(def, result, dest_ptr);
emit_store_type_(def, dest_ptr, LUA_TNUMFLT);
}
void RaviCodeGenerator::emit_store_reg_i(RaviFunctionDef *def,
llvm::Value *result,
llvm::Value *dest_ptr) {
llvm::Value *ra_n =
def->builder->CreateBitCast(dest_ptr, def->types->plua_IntegerT);
llvm::Instruction *store = def->builder->CreateStore(result, ra_n);
store->setMetadata(llvm::LLVMContext::MD_tbaa, def->types->tbaa_TValue_nT);
}
void RaviCodeGenerator::emit_store_reg_i_withtype(RaviFunctionDef *def,
llvm::Value *result,
llvm::Value *dest_ptr) {
emit_store_reg_i(def, result, dest_ptr);
emit_store_type_(def, dest_ptr, LUA_TNUMINT);
}
void RaviCodeGenerator::emit_store_reg_b(RaviFunctionDef *def,
llvm::Value *result,
llvm::Value *dest_ptr) {
llvm::Value *ra_n =
def->builder->CreateBitCast(dest_ptr, def->types->C_pintT);
llvm::Instruction *store = def->builder->CreateStore(result, ra_n);
store->setMetadata(llvm::LLVMContext::MD_tbaa, def->types->tbaa_TValue_nT);
}
void RaviCodeGenerator::emit_store_reg_b_withtype(RaviFunctionDef *def,
llvm::Value *result,
llvm::Value *dest_ptr) {
emit_store_reg_b(def, result, dest_ptr);
emit_store_type_(def, dest_ptr, LUA_TBOOLEAN);
}
void RaviCodeGenerator::emit_store_type_(RaviFunctionDef *def,
llvm::Value *value, int type) {
lua_assert(type == LUA_TNUMFLT || type == LUA_TNUMINT ||
type == LUA_TBOOLEAN || type == LUA_TNIL);
llvm::Value *desttype = emit_gep(def, "dest.tt", value, 0, 1);
llvm::Instruction *store = def->builder->CreateStore(
llvm::ConstantInt::get(def->types->lua_LuaTypeT, type),
desttype);
store->setMetadata(llvm::LLVMContext::MD_tbaa, def->types->tbaa_TValue_ttT);
}
llvm::Instruction *RaviCodeGenerator::emit_load_type(RaviFunctionDef *def,
llvm::Value *value) {
llvm::Value *tt_ptr = emit_gep(def, "value.tt.ptr", value, 0, 1);
llvm::Instruction *tt = def->builder->CreateLoad(tt_ptr, "value.tt");
tt->setMetadata(llvm::LLVMContext::MD_tbaa, def->types->tbaa_TValue_ttT);
return tt;
}
llvm::Value *RaviCodeGenerator::emit_is_value_of_type(RaviFunctionDef *def,
llvm::Value *value_type,
LuaTypeCode lua_type,
const char *varname) {
return def->builder->CreateICmpEQ(
value_type, llvm::ConstantInt::get(def->types->lua_LuaTypeT, lua_type),
varname);
}
llvm::Value *RaviCodeGenerator::emit_is_not_value_of_type(
RaviFunctionDef *def, llvm::Value *value_type, LuaTypeCode lua_type,
const char *varname) {
return def->builder->CreateICmpNE(
value_type, llvm::ConstantInt::get(def->types->lua_LuaTypeT, lua_type),
varname);
}
// Compare without variants i.e. ttnov(value_type) == lua_type
llvm::Value *RaviCodeGenerator::emit_is_not_value_of_type_class(
RaviFunctionDef *def, llvm::Value *value_type, int lua_type,
const char *varname) {
llvm::Value *novariant_type = def->builder->CreateAnd(
value_type, llvm::ConstantInt::get(def->types->lua_LuaTypeT, 0x000F));
return def->builder->CreateICmpNE(
novariant_type,
llvm::ConstantInt::get(def->types->lua_LuaTypeT, lua_type), varname);
}
llvm::Instruction *RaviCodeGenerator::emit_load_ravi_arraytype(
RaviFunctionDef *def, llvm::Value *value) {
llvm::Value *tt_ptr = emit_gep(def, "raviarray.type_ptr", value, 0, 11, 3);
llvm::Instruction *tt = def->builder->CreateLoad(tt_ptr, "raviarray.type");
tt->setMetadata(llvm::LLVMContext::MD_tbaa, def->types->tbaa_RaviArray_typeT);
return tt;
}
llvm::Instruction *RaviCodeGenerator::emit_load_ravi_arraylength(
RaviFunctionDef *def, llvm::Value *value) {
llvm::Value *tt_ptr = emit_gep(def, "raviarray.len_ptr", value, 0, 11, 1);
llvm::Instruction *tt = def->builder->CreateLoad(tt_ptr, "raviarray.len");
tt->setMetadata(llvm::LLVMContext::MD_tbaa, def->types->tbaa_RaviArray_lenT);
return tt;
}
// Tests following:
// ((t) == NULL) || ((t)->flags & (1u<<e)) != 0)
llvm::Value *RaviCodeGenerator::emit_table_no_metamethod(RaviFunctionDef *def,
llvm::Value *table,
TMS event) {
// Is metatable NULL?
llvm::Value *metatable_ptr =
emit_gep(def, "table.metatable_ptr", table, 0, 9);
llvm::Instruction *metatable =
def->builder->CreateLoad(metatable_ptr, "table.metatable");
metatable->setMetadata(llvm::LLVMContext::MD_tbaa,
def->types->tbaa_Table_metatable);
llvm::Value *null_constant =
llvm::ConstantPointerNull::get(def->types->pTableT);
llvm::Value *is_null = def->builder->CreateICmpEQ(
def->builder->CreatePtrToInt(null_constant, def->types->C_intptr_t),
def->builder->CreatePtrToInt(metatable, def->types->C_intptr_t));
// Is metatable->flags & (1<<event) set?
llvm::Value *flags_ptr = emit_gep(def, "table.flags_ptr", metatable, 0, 3);
llvm::Instruction *flags = def->builder->CreateLoad(flags_ptr, "table.flags");
flags->setMetadata(llvm::LLVMContext::MD_tbaa, def->types->tbaa_Table_flags);
llvm::Value *no_event = def->builder->CreateICmpNE(
def->builder->CreateAnd(
flags, llvm::ConstantInt::get(def->types->lu_byteT, 1u << event)),
llvm::ConstantInt::get(def->types->lu_byteT, 0));
llvm::Value *metaabsent =
def->builder->CreateOr(is_null, no_event, "metatable.isnull.or.no.event");
return metaabsent;
}
// Store lua_Number or lua_Integer
llvm::Instruction *RaviCodeGenerator::emit_store_local_n(RaviFunctionDef *def,
llvm::Value *src,
llvm::Value *dest) {
llvm::Instruction *ins = def->builder->CreateStore(src, dest);
ins->setMetadata(llvm::LLVMContext::MD_tbaa, def->types->tbaa_plonglongT);
return ins;
}
// Load lua_Number or lua_Integer
llvm::Instruction *RaviCodeGenerator::emit_load_local_n(RaviFunctionDef *def,
llvm::Value *src) {
llvm::Instruction *ins = def->builder->CreateLoad(src);
ins->setMetadata(llvm::LLVMContext::MD_tbaa, def->types->tbaa_plonglongT);
return ins;
}
// Store int
llvm::Instruction *RaviCodeGenerator::emit_store_local_int(RaviFunctionDef *def,
llvm::Value *src,
llvm::Value *dest) {
llvm::Instruction *ins = def->builder->CreateStore(src, dest);
ins->setMetadata(llvm::LLVMContext::MD_tbaa, def->types->tbaa_pintT);
return ins;
}
// Load int
llvm::Instruction *RaviCodeGenerator::emit_load_local_int(RaviFunctionDef *def,
llvm::Value *src) {
llvm::Instruction *ins = def->builder->CreateLoad(src);
ins->setMetadata(llvm::LLVMContext::MD_tbaa, def->types->tbaa_pintT);
return ins;
}
// emit code to obtain address of register or constant at location B
llvm::Value *RaviCodeGenerator::emit_gep_register_or_constant(
RaviFunctionDef *def, int B) {
// Load pointer to k
llvm::Value *k_ptr = def->k_ptr;
llvm::Value *rb;
// Get pointer to register B
llvm::Value *base_or_k = ISK(B) ? k_ptr : def->base_ptr;
int b = ISK(B) ? INDEXK(B) : B;
if (b == 0) { rb = base_or_k; }
else {
rb = emit_array_get(def, base_or_k, b);
}
return rb;
}
#if 0
// Test if ci->jistatus is true, ci is of type CallInfo
llvm::Value *RaviCodeGenerator::emit_is_jit_call(RaviFunctionDef *def,
llvm::Value *ci) {
// Get pointer to ci->jitstatus
llvm::Value *ci_jitstatus_ptr = emit_gep(def, "ci_jit_status_ptr", ci, 0, 9);
// Load ci->jitstatus
llvm::Instruction *ci_jitstatus = def->builder->CreateLoad(ci_jitstatus_ptr);
ci_jitstatus->setMetadata(llvm::LLVMContext::MD_tbaa,
def->types->tbaa_CallInfo_jitstatusT);
return def->builder->CreateICmpNE(
ci_jitstatus, llvm::ConstantInt::get(def->types->lu_byteT, 0),
"jit_call");
}
// Return (ci->callstatus & CIST_LUA) != 0
llvm::Value *RaviCodeGenerator::emit_ci_is_Lua(RaviFunctionDef *def,
llvm::Value *ci) {
// Get pointer to ci->callstatus
llvm::Value *ci_callstatus_ptr =
emit_gep(def, "ci_call_status_ptr", ci, 0, 7);
// Load ci->callstatus
llvm::Instruction *ci_callstatus =
def->builder->CreateLoad(ci_callstatus_ptr, "ci_call_status");
ci_callstatus->setMetadata(llvm::LLVMContext::MD_tbaa,
def->types->tbaa_CallInfo_callstatusT);
llvm::Value *isLua = def->builder->CreateAnd(
ci_callstatus, llvm::ConstantInt::get(def->types->C_shortT, CIST_LUA),
"isLua");
return def->builder->CreateICmpNE(
isLua, llvm::ConstantInt::get(def->types->C_shortT, 0));
}
#endif
llvm::Value *RaviCodeGenerator::emit_load_ci(RaviFunctionDef *def) {
llvm::Value *L_ci = emit_gep(def, "L_ci", def->L, 0, 6);
llvm::Instruction *ci_val = def->builder->CreateLoad(L_ci);
ci_val->setMetadata(llvm::LLVMContext::MD_tbaa, def->types->tbaa_CallInfoT);
return ci_val;
}
// L->top = ci->top
void RaviCodeGenerator::emit_refresh_L_top(RaviFunctionDef *def,
llvm::Value *ci_val) {
// Get pointer to ci->top
llvm::Value *citop = emit_gep(def, "ci_top", ci_val, 0, 1);
// Load ci->top
llvm::Instruction *citop_val = def->builder->CreateLoad(citop);
citop_val->setMetadata(llvm::LLVMContext::MD_tbaa,
def->types->tbaa_CallInfo_topT);
// Get L->top
llvm::Value *top = emit_gep(def, "L_top", def->L, 0, 4);
// Assign ci>top to L->top
auto ins = def->builder->CreateStore(citop_val, top);
ins->setMetadata(llvm::LLVMContext::MD_tbaa, def->types->tbaa_luaState_topT);
}
llvm::Value *RaviCodeGenerator::emit_gep_L_top(RaviFunctionDef *def) {
// Get pointer to L->top
return emit_gep(def, "L.top", def->L, 0, 4);
}
// L->top = R(B)
void RaviCodeGenerator::emit_set_L_top_toreg(RaviFunctionDef *def, int B) {
// Get pointer to register at R(B)
llvm::Value *ptr = emit_gep_register(def, B);
// Get pointer to L->top
llvm::Value *top = emit_gep_L_top(def);
// Assign to L->top
llvm::Instruction *ins = def->builder->CreateStore(ptr, top);
ins->setMetadata(llvm::LLVMContext::MD_tbaa, def->types->tbaa_luaState_topT);
}
llvm::Instruction *RaviCodeGenerator::emit_tointeger(RaviFunctionDef *def,
llvm::Value *reg) {
llvm::IRBuilder<> TmpB(def->entry, def->entry->begin());
llvm::Value *value =
TmpB.CreateAlloca(def->types->lua_IntegerT, nullptr, "value");
llvm::Value *reg_type = emit_load_type(def, reg);
// Is reg an integer?
llvm::Value *cmp1 =
emit_is_value_of_type(def, reg_type, LUA__TNUMINT, "reg.is.integer");
llvm::BasicBlock *convert_reg =
llvm::BasicBlock::Create(def->jitState->context(), "convert.reg");
llvm::BasicBlock *copy_reg =
llvm::BasicBlock::Create(def->jitState->context(), "copy.reg");
llvm::BasicBlock *load_val =
llvm::BasicBlock::Create(def->jitState->context(), "load.val");
llvm::BasicBlock *failed_conversion = llvm::BasicBlock::Create(
def->jitState->context(), "if.conversion.failed");
// If reg is integer then copy reg, else convert reg
auto brinst1 = def->builder->CreateCondBr(cmp1, copy_reg, convert_reg);
attach_branch_weights(def, brinst1, 100, 0);
// Convert RB
def->f->getBasicBlockList().push_back(convert_reg);
def->builder->SetInsertPoint(convert_reg);
llvm::Value *var_isint =
CreateCall2(def->builder, def->luaV_tointegerF, reg, value);
llvm::Value *tobool = def->builder->CreateICmpEQ(
var_isint, def->types->kInt[0], "conversion.failed");
// Did conversion fail?
def->builder->CreateCondBr(tobool, failed_conversion, load_val);
// Conversion failed, so raise error
def->f->getBasicBlockList().push_back(failed_conversion);
def->builder->SetInsertPoint(failed_conversion);
emit_raise_lua_error(def, "integer expected");
def->builder->CreateBr(load_val);
// Conversion OK
def->f->getBasicBlockList().push_back(copy_reg);
def->builder->SetInsertPoint(copy_reg);
llvm::Value *i = emit_load_reg_i(def, reg);
emit_store_local_int(def, i, value);
def->builder->CreateBr(load_val);
def->f->getBasicBlockList().push_back(load_val);
def->builder->SetInsertPoint(load_val);
return emit_load_local_int(def, value);
}
llvm::Instruction *RaviCodeGenerator::emit_tofloat(RaviFunctionDef *def,
llvm::Value *reg) {
llvm::IRBuilder<> TmpB(def->entry, def->entry->begin());
llvm::Value *value =
TmpB.CreateAlloca(def->types->lua_NumberT, nullptr, "value");
llvm::Value *reg_type = emit_load_type(def, reg);
// Is reg an number?
llvm::Value *cmp1 =
emit_is_value_of_type(def, reg_type, LUA__TNUMFLT, "reg.is.float");
llvm::BasicBlock *convert_reg =
llvm::BasicBlock::Create(def->jitState->context(), "convert.reg");
llvm::BasicBlock *copy_reg =
llvm::BasicBlock::Create(def->jitState->context(), "copy.reg");
llvm::BasicBlock *load_val =
llvm::BasicBlock::Create(def->jitState->context(), "load.val");
llvm::BasicBlock *failed_conversion = llvm::BasicBlock::Create(
def->jitState->context(), "if.conversion.failed");
// If reg is integer then copy reg, else convert reg
auto brinst1 = def->builder->CreateCondBr(cmp1, copy_reg, convert_reg);
attach_branch_weights(def, brinst1, 100, 0);
// Convert RB
def->f->getBasicBlockList().push_back(convert_reg);
def->builder->SetInsertPoint(convert_reg);
llvm::Value *var_isfloat =
CreateCall2(def->builder, def->luaV_tonumberF, reg, value);
llvm::Value *tobool = def->builder->CreateICmpEQ(
var_isfloat, def->types->kInt[0], "conversion.failed");
// Did conversion fail?
def->builder->CreateCondBr(tobool, failed_conversion, load_val);
// Conversion failed, so raise error
def->f->getBasicBlockList().push_back(failed_conversion);
def->builder->SetInsertPoint(failed_conversion);
emit_raise_lua_error(def, "number expected");
def->builder->CreateBr(load_val);
// Conversion OK
def->f->getBasicBlockList().push_back(copy_reg);
def->builder->SetInsertPoint(copy_reg);
llvm::Value *i = emit_load_reg_n(def, reg);
emit_store_local_n(def, i, value);
def->builder->CreateBr(load_val);
def->f->getBasicBlockList().push_back(load_val);
def->builder->SetInsertPoint(load_val);
return emit_load_local_n(def, value);
}
// (int)(L->top - ra)
llvm::Value *RaviCodeGenerator::emit_num_stack_elements(RaviFunctionDef *def,
llvm::Value *ra) {
llvm::Value *L_top = emit_gep_L_top(def);
llvm::Instruction *top_ptr = def->builder->CreateLoad(L_top, "L_top_ptr");
llvm::Value *top_asint = def->builder->CreatePtrToInt(
top_ptr, def->types->C_intptr_t, "L_top_ptr_as_int");
llvm::Value *ra_asint =
def->builder->CreatePtrToInt(ra, def->types->C_intptr_t, "ra_ptr_as_int");
llvm::Value *diff =
def->builder->CreateSub(top_asint, ra_asint, "L_top_minus_ra");
// Because each value object is 16 bytes we need to divide by 16
llvm::Value *n_elements = def->builder->CreateSDiv(
diff, llvm::ConstantInt::get(def->types->C_intptr_t, sizeof(TValue)),
"num_tvalue_elements", true);
return def->builder->CreateTrunc(n_elements, def->types->C_intT,
"num_stack_elements");
}
// Check if we can compile
// The cases we can compile will increase over time
bool RaviCodeGenerator::canCompile(Proto *p) {
if (p->ravi_jit.jit_status == RAVI_JIT_CANT_COMPILE) return false;
if (jitState_ == nullptr) {
p->ravi_jit.jit_status = RAVI_JIT_CANT_COMPILE;
return false;
}
// const Instruction *code = p->code;
// int pc, n = p->sizecode;
// Loop over the byte codes; as Lua compiler inserts
// an extra RETURN op we need to ignore the last op
// for (pc = 0; pc < n; pc++) {
// Instruction i = code[pc];
// OpCode o = GET_OPCODE(i);
// if (o == OP_EXTRAARG)
// return false;
// else if (o == OP_RAVI_UNMF || o == OP_RAVI_UNMI) {
// fprintf(stderr, "Unexpected bytecode %d\n", o);
// abort();
// }
//}
return true;
}
std::unique_ptr<RaviJITFunction> RaviCodeGenerator::create_function(
Proto *p, std::shared_ptr<RaviJITModule> module, llvm::IRBuilder<> &builder,
RaviFunctionDef *def) {
LuaLLVMTypes *types = jitState_->types();
std::unique_ptr<ravi::RaviJITFunction> func =
std::unique_ptr<RaviJITFunction>(new RaviJITFunction(
&p->ravi_jit.jit_function, module, types->jitFunctionT,
llvm::Function::ExternalLinkage, unique_function_name()));
if (!func) return func;
llvm::Function *mainFunc = func->function();
llvm::BasicBlock *entry =
llvm::BasicBlock::Create(jitState_->context(), "entry", mainFunc);
builder.SetInsertPoint(entry);
auto argiter = mainFunc->arg_begin();
llvm::Value *arg1 = &(*argiter);
arg1->setName("L");
def->jitState = jitState_;
def->f = mainFunc;
def->entry = entry;
def->L = arg1;
def->raviF = func.get();
def->types = types;
def->builder = &builder;
return func;
}
void RaviCodeGenerator::emit_dump_stack(RaviFunctionDef *def, const char *str) {
CreateCall2(def->builder, def->ravi_dump_stackF, def->L,
def->builder->CreateGlobalStringPtr(str));
}
void RaviCodeGenerator::emit_dump_stacktop(RaviFunctionDef *def,
const char *str) {
CreateCall2(def->builder, def->ravi_dump_stacktopF, def->L,
def->builder->CreateGlobalStringPtr(str));
}
// Emit a call to ravi_debug_trace() function.
// This function will set savedpc and also invoke
// "line" hook if defined
bool RaviCodeGenerator::emit_debug_trace(RaviFunctionDef *def, int opCode,
int pc) {
if (def->jitState->is_tracehook_enabled()) {
CreateCall3(def->builder, def->ravi_debug_traceF, def->L,
llvm::ConstantInt::get(def->types->C_intT, opCode),
llvm::ConstantInt::get(def->types->C_intT, pc));
return true;
}
return false;
}
void RaviCodeGenerator::emit_raise_lua_error(RaviFunctionDef *def,
const char *str) {
CreateCall2(def->builder, def->luaG_runerrorF, def->L,
def->builder->CreateGlobalStringPtr(str));
}
void RaviCodeGenerator::debug_printf(RaviFunctionDef *def, const char *str) {
#if LLVM_VERSION_MAJOR >= 9
CreateCall1(def->builder, def->printfFunc.getCallee(),
def->builder->CreateGlobalStringPtr(str));
#else
CreateCall1(def->builder, def->printfFunc,
def->builder->CreateGlobalStringPtr(str));
#endif
}
void RaviCodeGenerator::debug_printf1(RaviFunctionDef *def, const char *str,
llvm::Value *arg1) {
#if LLVM_VERSION_MAJOR >= 9
CreateCall2(def->builder, def->printfFunc.getCallee(),
def->builder->CreateGlobalStringPtr(str), arg1);
#else
CreateCall2(def->builder, def->printfFunc,
def->builder->CreateGlobalStringPtr(str), arg1);
#endif
}
void RaviCodeGenerator::debug_printf2(RaviFunctionDef *def, const char *str,
llvm::Value *arg1, llvm::Value *arg2) {
#if LLVM_VERSION_MAJOR >= 9
CreateCall3(def->builder, def->printfFunc.getCallee(),
def->builder->CreateGlobalStringPtr(str), arg1, arg2);
#else
CreateCall3(def->builder, def->printfFunc,
def->builder->CreateGlobalStringPtr(str), arg1, arg2);
#endif
}
void RaviCodeGenerator::debug_printf3(RaviFunctionDef *def, const char *str,
llvm::Value *arg1, llvm::Value *arg2,
llvm::Value *arg3) {
#if LLVM_VERSION_MAJOR >= 9
CreateCall4(def->builder, def->printfFunc.getCallee(),
def->builder->CreateGlobalStringPtr(str), arg1, arg2, arg3);
#else
CreateCall4(def->builder, def->printfFunc,
def->builder->CreateGlobalStringPtr(str), arg1, arg2, arg3);
#endif
}
void RaviCodeGenerator::debug_printf4(RaviFunctionDef *def, const char *str,
llvm::Value *arg1, llvm::Value *arg2,
llvm::Value *arg3, llvm::Value *arg4) {
#if LLVM_VERSION_MAJOR >= 9
CreateCall5(def->builder, def->printfFunc.getCallee(),
def->builder->CreateGlobalStringPtr(str), arg1, arg2, arg3, arg4);
#else
CreateCall5(def->builder, def->printfFunc,
def->builder->CreateGlobalStringPtr(str), arg1, arg2, arg3, arg4);
#endif
}
void RaviCodeGenerator::emit_extern_declarations(RaviFunctionDef *def) {
// Add extern declarations for Lua functions that we need to call
def->luaT_trybinTMF = def->raviF->addExternFunction(
def->types->luaT_trybinTMT, reinterpret_cast<void *>(&luaT_trybinTM),
"luaT_trybinTM");
def->luaD_callF = def->raviF->addExternFunction(
def->types->luaD_callT, reinterpret_cast<void *>(&luaD_call),
"luaD_call");
def->luaD_poscallF = def->raviF->addExternFunction(
def->types->luaD_poscallT, reinterpret_cast<void *>(&luaD_poscall),
"luaD_poscall");
def->luaD_precallF = def->raviF->addExternFunction(
def->types->luaD_precallT, reinterpret_cast<void *>(&luaD_precall),
"luaD_precall");
def->luaF_closeF = def->raviF->addExternFunction(
def->types->luaF_closeT, reinterpret_cast<void *>(&luaF_close),
"luaF_close");
def->luaG_runerrorF = def->raviF->addExternFunction(
def->types->luaG_runerrorT, reinterpret_cast<void *>(&luaG_runerror),
"luaG_runerror");
def->luaG_runerrorF->setDoesNotReturn();
def->luaV_equalobjF = def->raviF->addExternFunction(
def->types->luaV_equalobjT, reinterpret_cast<void *>(&luaV_equalobj),
"luaV_equalobj");
def->luaV_lessthanF = def->raviF->addExternFunction(
def->types->luaV_lessthanT, reinterpret_cast<void *>(&luaV_lessthan),
"luaV_lessthan");
def->luaV_lessequalF = def->raviF->addExternFunction(
def->types->luaV_lessequalT, reinterpret_cast<void *>(&luaV_lessequal),
"luaV_lessequal");
def->luaV_forlimitF = def->raviF->addExternFunction(
def->types->luaV_forlimitT, reinterpret_cast<void *>(&luaV_forlimit),
"luaV_forlimit");
def->luaV_tonumberF = def->raviF->addExternFunction(
def->types->luaV_tonumberT, reinterpret_cast<void *>(&luaV_tonumber_),
"luaV_tonumber_");
def->luaV_tointegerF = def->raviF->addExternFunction(
def->types->luaV_tointegerT, reinterpret_cast<void *>(&luaV_tointeger_),
"luaV_tointeger_");
def->luaV_executeF = def->raviF->addExternFunction(
def->types->luaV_executeT, reinterpret_cast<void *>(&luaV_execute),
"luaV_execute");
def->luaV_settableF = def->raviF->addExternFunction(
def->types->luaV_settableT, reinterpret_cast<void *>(&luaV_settable),
"luaV_settable");
def->luaV_gettableF = def->raviF->addExternFunction(
def->types->luaV_gettableT, reinterpret_cast<void *>(&luaV_gettable),
"luaV_gettable");
def->luaH_getintF = def->raviF->addExternFunction(
def->types->luaH_getintT, reinterpret_cast<void *>(&luaH_getint),
"luaH_getint");
def->luaH_setintF = def->raviF->addExternFunction(
def->types->luaH_setintT, reinterpret_cast<void *>(&luaH_setint),
"luaH_setint");
def->luaH_getstrF = def->raviF->addExternFunction(
def->types->luaH_getstrT, reinterpret_cast<void *>(&luaH_getstr),
"luaH_getstr");
def->luaH_getstrF->addFnAttr(llvm::Attribute::AttrKind::ReadOnly);
// def->luaH_getstrF->setDoesNotAlias(1);
// def->luaH_getstrF->setDoesNotCapture(1);
// def->luaH_getstrF->setDoesNotAlias(2);
// def->luaH_getstrF->setDoesNotCapture(2);
def->luaV_finishgetF = def->raviF->addExternFunction(
def->types->luaV_finishgetT, reinterpret_cast<void *>(&luaV_finishget),
"luaV_finishget");
def->raviV_op_loadnilF = def->raviF->addExternFunction(
def->types->raviV_op_loadnilT,
reinterpret_cast<void *>(&raviV_op_loadnil), "raviV_op_loadnil");
def->raviV_op_newarrayintF = def->raviF->addExternFunction(
def->types->raviV_op_newarrayintT,
reinterpret_cast<void *>(&raviV_op_newarrayint), "raviV_op_newarrayint");
def->raviV_op_newarrayfloatF = def->raviF->addExternFunction(
def->types->raviV_op_newarrayfloatT,
reinterpret_cast<void *>(&raviV_op_newarrayfloat),
"raviV_op_newarrayfloat");
def->raviV_op_newtableF = def->raviF->addExternFunction(
def->types->raviV_op_newtableT,
reinterpret_cast<void *>(&raviV_op_newtable), "raviV_op_newtable");
def->raviV_op_setlistF = def->raviF->addExternFunction(
def->types->raviV_op_setlistT,
reinterpret_cast<void *>(&raviV_op_setlist), "raviV_op_setlist");
def->luaV_modF = def->raviF->addExternFunction(
def->types->luaV_modT, reinterpret_cast<void *>(&luaV_mod), "luaV_mod");
def->luaV_divF = def->raviF->addExternFunction(
def->types->luaV_divT, reinterpret_cast<void *>(&luaV_div), "luaV_div");
def->luaV_objlenF = def->raviF->addExternFunction(
def->types->luaV_objlenT, reinterpret_cast<void *>(&luaV_objlen),
"luaV_objlen");
def->luaC_upvalbarrierF = def->raviF->addExternFunction(
def->types->luaC_upvalbarrierT,
reinterpret_cast<void *>(&luaC_upvalbarrier_), "luaC_upvalbarrier_");
def->raviV_op_concatF = def->raviF->addExternFunction(
def->types->raviV_op_concatT, reinterpret_cast<void *>(&raviV_op_concat),
"raviV_op_concat");
def->raviV_op_closureF = def->raviF->addExternFunction(
def->types->raviV_op_closureT,
reinterpret_cast<void *>(&raviV_op_closure), "raviV_op_closure");
def->raviV_op_varargF = def->raviF->addExternFunction(
def->types->raviV_op_varargT, reinterpret_cast<void *>(&raviV_op_vararg),
"raviV_op_vararg");
def->raviH_set_intF = def->raviF->addExternFunction(
def->types->raviH_set_intT, reinterpret_cast<void *>(&raviH_set_int),
"raviH_set_int");
def->raviH_set_floatF = def->raviF->addExternFunction(
def->types->raviH_set_floatT, reinterpret_cast<void *>(&raviH_set_float),
"raviH_set_float");
def->raviV_op_shlF = def->raviF->addExternFunction(
def->types->raviV_op_shlT, reinterpret_cast<void *>(&raviV_op_shl),
"raviV_op_shl");
def->raviV_op_shrF = def->raviF->addExternFunction(
def->types->raviV_op_shrT, reinterpret_cast<void *>(&raviV_op_shr),
"raviV_op_shr");
def->raviV_op_borF = def->raviF->addExternFunction(
def->types->raviV_op_borT, reinterpret_cast<void *>(&raviV_op_bor),
"raviV_op_bor");
def->raviV_op_bxorF = def->raviF->addExternFunction(
def->types->raviV_op_bxorT, reinterpret_cast<void *>(&raviV_op_bxor),
"raviV_op_bxor");
def->raviV_op_bandF = def->raviF->addExternFunction(
def->types->raviV_op_bandT, reinterpret_cast<void *>(&raviV_op_band),
"raviV_op_band");
def->raviV_op_bnotF = def->raviF->addExternFunction(
def->types->raviV_op_bnotT, reinterpret_cast<void *>(&raviV_op_bnot),
"raviV_op_bnot");
def->raviV_op_addF = def->raviF->addExternFunction(
def->types->raviV_op_addT, reinterpret_cast<void *>(&raviV_op_add),
"raviV_op_add");
def->raviV_op_subF = def->raviF->addExternFunction(
def->types->raviV_op_subT, reinterpret_cast<void *>(&raviV_op_sub),
"raviV_op_sub");
def->raviV_op_mulF = def->raviF->addExternFunction(
def->types->raviV_op_mulT, reinterpret_cast<void *>(&raviV_op_mul),
"raviV_op_mul");
def->raviV_op_divF = def->raviF->addExternFunction(
def->types->raviV_op_divT, reinterpret_cast<void *>(&raviV_op_div),
"raviV_op_div");
def->raviV_op_setupvaliF = def->raviF->addExternFunction(
def->types->raviV_op_setupvaliT,
reinterpret_cast<void *>(&raviV_op_setupvali), "raviV_op_setupvali");
def->raviV_op_setupvalfF = def->raviF->addExternFunction(
def->types->raviV_op_setupvalfT,
reinterpret_cast<void *>(&raviV_op_setupvalf), "raviV_op_setupvalf");
def->raviV_op_setupvalaiF = def->raviF->addExternFunction(
def->types->raviV_op_setupvalaiT,
reinterpret_cast<void *>(&raviV_op_setupvalai), "raviV_op_setupvalai");
def->raviV_op_setupvalafF = def->raviF->addExternFunction(
def->types->raviV_op_setupvalafT,
reinterpret_cast<void *>(&raviV_op_setupvalaf), "raviV_op_setupvalaf");
def->raviV_op_setupvaltF = def->raviF->addExternFunction(
def->types->raviV_op_setupvaltT,
reinterpret_cast<void *>(&raviV_op_setupvalt), "raviV_op_setupvalt");
def->raviV_settable_sskeyF = def->raviF->addExternFunction(
def->types->raviV_settable_sskeyT,
reinterpret_cast<void *>(&raviV_settable_sskey), "raviV_settable_sskey");
def->raviV_gettable_sskeyF = def->raviF->addExternFunction(
def->types->raviV_gettable_sskeyT,
reinterpret_cast<void *>(&raviV_gettable_sskey), "raviV_gettable_sskey");
def->raviV_settable_iF = def->raviF->addExternFunction(
def->types->raviV_settable_iT,
reinterpret_cast<void *>(&raviV_settable_i), "raviV_settable_i");
def->raviV_gettable_iF = def->raviF->addExternFunction(
def->types->raviV_gettable_iT,
reinterpret_cast<void *>(&raviV_gettable_i), "raviV_gettable_i");
def->raviV_op_totypeF = def->raviF->addExternFunction(
def->types->raviV_op_totypeT, reinterpret_cast<void *>(&raviV_op_totype),
"raviV_op_totype");
def->raviV_op_deferF = def->raviF->addExternFunction(
def->types->raviV_op_deferT, reinterpret_cast<void *>(&raviV_op_defer),
"raviV_op_defer");
def->ravi_dump_valueF = def->raviF->addExternFunction(
def->types->ravi_dump_valueT, reinterpret_cast<void *>(&ravi_dump_value),
"ravi_dump_value");
def->ravi_dump_stackF = def->raviF->addExternFunction(
def->types->ravi_dump_stackT, reinterpret_cast<void *>(&ravi_dump_stack),
"ravi_dump_stack");
def->ravi_dump_stacktopF = def->raviF->addExternFunction(
def->types->ravi_dump_stacktopT,
reinterpret_cast<void *>(&ravi_dump_stacktop), "ravi_dump_stacktop");
def->ravi_debug_traceF = def->raviF->addExternFunction(
def->types->ravi_debug_traceT,
reinterpret_cast<void *>(&ravi_debug_trace), "ravi_debug_trace");
// Create printf declaration
std::vector<llvm::Type *> args;
args.push_back(def->types->C_pcharT);
// accepts a char*, is vararg, and returns int
llvm::FunctionType *printfType =
llvm::FunctionType::get(def->types->C_intT, args, true);
def->printfFunc =
def->raviF->module()->getOrInsertFunction("printf", printfType);
// stdc fmod declaration
args.clear();
args.push_back(def->types->lua_NumberT);
args.push_back(def->types->lua_NumberT);
llvm::FunctionType *fmodType =
llvm::FunctionType::get(def->types->lua_NumberT, args, false);
#ifdef LUA_32BITS
def->fmodFunc = def->raviF->module()->getOrInsertFunction("fmodf", fmodType);
#else
def->fmodFunc = def->raviF->module()->getOrInsertFunction("fmod", fmodType);
#endif
llvm::FunctionType *powType =
llvm::FunctionType::get(def->types->lua_NumberT, args, false);
#ifdef LUA_32BITS
def->powFunc = def->raviF->module()->getOrInsertFunction("powf", powType);
#else
def->powFunc = def->raviF->module()->getOrInsertFunction("pow", powType);
#endif
// stdc floor declaration
args.clear();
args.push_back(def->types->lua_NumberT);
llvm::FunctionType *floorType =
llvm::FunctionType::get(def->types->lua_NumberT, args, false);
#ifdef LUA_32BITS
def->floorFunc =
def->raviF->module()->getOrInsertFunction("floorf", floorType);
#else
def->floorFunc =
def->raviF->module()->getOrInsertFunction("floor", floorType);
#endif
}
void RaviCodeGenerator::link_block(RaviFunctionDef *def, int pc) {
// If we are on a jump target then check if this is a forloop
// target. Forloop targets are special as they use computed goto
// to branch to one of 4 possible labels. That is why we test for
// jmp2 below.
// We need to check whether current block is already terminated -
// this can be because of a return or break or goto within the forloop
// body
if (def->jmp_targets[pc].jmp2 &&
!def->builder->GetInsertBlock()->getTerminator()) {
// Handle special case for body of FORLOOP
auto b = def->builder->CreateLoad(def->jmp_targets[pc].forloop_branch);
auto idb = def->builder->CreateIndirectBr(b, 4);
idb->addDestination(def->jmp_targets[pc].jmp1);
idb->addDestination(def->jmp_targets[pc].jmp2);
idb->addDestination(def->jmp_targets[pc].jmp3);
idb->addDestination(def->jmp_targets[pc].jmp4);
// As we have terminated the block the code below will not
// add the branch instruction, but we still need to create the new
// block which is handled below.
}
// If the current bytecode offset pc is on a jump target
// then we need to insert the block we previously created in
// scan_jump_targets()
// and make it the current insert block; also if the previous block
// is unterminated then we simply provide a branch from previous block to the
// new block
if (def->jmp_targets[pc].jmp1) {
// We are on a jump target
// Get the block we previously created scan_jump_targets
llvm::BasicBlock *block = def->jmp_targets[pc].jmp1;
if (!def->builder->GetInsertBlock()->getTerminator()) {
// Previous block not terminated so branch to the
// new block
def->builder->CreateBr(block);
}
// Now add the new block and make it current
def->f->getBasicBlockList().push_back(block);
def->builder->SetInsertPoint(block);
}
}
llvm::Value *RaviCodeGenerator::emit_gep_upvals(RaviFunctionDef *def,
int offset) {
return emit_gep(def, "upvals", def->p_LClosure, 0, 6, offset);
}
llvm::Instruction *RaviCodeGenerator::emit_load_pupval(RaviFunctionDef *def,
llvm::Value *ppupval) {
llvm::Instruction *ins = def->builder->CreateLoad(ppupval);
ins->setMetadata(llvm::LLVMContext::MD_tbaa, def->types->tbaa_ppointerT);
return ins;
}
// Load upval->v
llvm::Instruction *RaviCodeGenerator::emit_load_upval_v(
RaviFunctionDef *def, llvm::Instruction *pupval) {
llvm::Value *p_v = emit_gep_upval_v(def, pupval);
llvm::Instruction *v = def->builder->CreateLoad(p_v);
v->setMetadata(llvm::LLVMContext::MD_tbaa, def->types->tbaa_UpVal_vT);
return v;
}
// Get &upval->v
llvm::Value *RaviCodeGenerator::emit_gep_upval_v(RaviFunctionDef *def,
llvm::Instruction *pupval) {
return emit_gep(def, "v", pupval, 0, 0);
}
// Get &upval->value -> result is TValue *
llvm::Value *RaviCodeGenerator::emit_gep_upval_value(
RaviFunctionDef *def, llvm::Instruction *pupval) {
return emit_gep(def, "value", pupval, 0, 3);
}
// Alternative code generator uses dmrC based C front-end
// That the codegen emits C code that is then JIT compiled
// via dmrC and LLVM.
bool RaviCodeGenerator::alt_compile(lua_State *L, Proto *p,
std::shared_ptr<RaviJITModule> module,
ravi_compile_options_t *options) {
if (p->ravi_jit.jit_status != RAVI_JIT_NOT_COMPILED) {
return false;
}
if (module->owner()->get_compiling_flag()) return false;
if (!raviJ_cancompile(p)) {
p->ravi_jit.jit_status = RAVI_JIT_CANT_COMPILE;
return false;
}
auto M = module->module();
LLVMModuleRef moduleRef = llvm::wrap(M);
// Set flag so we can avoid recursive calls
module->owner()->set_compiling_flag(true);
membuff_t buf;
membuff_init(&buf, 4096);
const char *fname = unique_function_name();
if (!raviJ_codegen(L, p, options, fname, &buf)) {
p->ravi_jit.jit_status = RAVI_JIT_CANT_COMPILE;
}
else {
if (options->manual_request && module->owner()->get_verbosity()) {
ravi_writestring(L, buf.buf, strlen(buf.buf));
ravi_writeline(L);
}
char *argv[] = {(char *)fname, NULL};
if (!dmrC_llvmcompile(2, argv, moduleRef, buf.buf)) {
p->ravi_jit.jit_status = RAVI_JIT_CANT_COMPILE;
}
else {
p->ravi_jit.jit_function = nullptr;
std::unique_ptr<ravi::RaviJITFunction> func =
std::unique_ptr<RaviJITFunction>(new RaviJITFunction(&p->ravi_jit.jit_function, module, fname));
if (func->function() == nullptr) {
fprintf(stderr, "LLVM Compilation failed\n");
exit(1);
}
bool doVerify = module->owner()->get_validation() != 0;
if (doVerify && llvm::verifyFunction(*func->function(), &llvm::errs())) {
func->dump();
fprintf(stderr, "LLVM Code Verification failed\n");
exit(1);
}
ravi::RaviJITFunction *llvm_func = func.release();
p->ravi_jit.jit_data = reinterpret_cast<void *>(llvm_func);
p->ravi_jit.jit_status = RAVI_JIT_COMPILED;
}
}
membuff_free(&buf);
module->owner()->set_compiling_flag(false);
return p->ravi_jit.jit_status == RAVI_JIT_COMPILED;
}
bool RaviCodeGenerator::compile(lua_State *L, Proto *p,
std::shared_ptr<RaviJITModule> module,
ravi_compile_options_t *options) {
if (p->ravi_jit.jit_status == RAVI_JIT_COMPILED) return true;
// Avoid recursive calls
if (module->owner()->get_compiling_flag()) return false;
bool doVerify = module->owner()->get_validation() != 0;
bool omitArrayGetRangeCheck =
options ? options->omit_array_get_range_check != 0 : 0;
if (p->ravi_jit.jit_status != RAVI_JIT_NOT_COMPILED || !canCompile(p)) {
// fprintf(stderr, "failed to compile!\n");
return false;
}
llvm::LLVMContext &context = jitState_->context();
llvm::IRBuilder<> builder(context);
RaviFunctionDef definition = {0};
RaviFunctionDef *def = &definition;
auto f = create_function(p, module, builder, def);
if (!f) {
p->ravi_jit.jit_status = RAVI_JIT_CANT_COMPILE; // can't compile
return false;
}
// Set flag so we can avoid recursive calls
module->owner()->set_compiling_flag(true);
// The functions constants
TValue *k = p->k;
// Add extern declarations for Lua functions we need to call
emit_extern_declarations(def);
def->p = p;
// Create BasicBlocks for all the jump targets in the Lua bytecode
scan_jump_targets(def, p);
// Get pointer to L->ci
def->L_ci = emit_gep(def, "L_ci", def->L, 0, 6);
// Load L->ci
// This is the function's activation record / stack frame
def->ci_val = builder.CreateLoad(def->L_ci);
def->ci_val->setMetadata(llvm::LLVMContext::MD_tbaa,
def->types->tbaa_luaState_ciT);
// Get pointer to function's 'base' stack location
// 'base' is the address relative to which all the
// function registers are located
def->Ci_base = emit_gep(def, "base", def->ci_val, 0, 4, 0);
// We need to get hold of the constants table
// which is located in the function definition
// This is the line in lvm.c that says:
// LClosure *cl = clLvalue(ci->func)
def->p_LClosure = emit_gep_ci_func_value_gc_asLClosure(def);
// Get pointer to the Proto* which is cl->p
def->proto = emit_gep(def, "Proto", def->p_LClosure, 0, 5);
// Load pointer to proto
def->proto_ptr = builder.CreateLoad(def->proto);
def->proto_ptr->setMetadata(llvm::LLVMContext::MD_tbaa,
def->types->tbaa_LClosure_pT);
// Obtain pointer to Proto->k
def->proto_k = emit_gep(def, "k", def->proto_ptr, 0, 14);
// Load pointer to k
def->k_ptr = builder.CreateLoad(def->proto_k);
def->k_ptr->setMetadata(llvm::LLVMContext::MD_tbaa,
def->types->tbaa_Proto_kT);
// emit_dump_stack(def, "Function entry-->");
const Instruction *code = p->code;
int pc, n = p->sizecode;
for (pc = 0; pc < n; pc++) {
link_block(def, pc);
Instruction i = code[pc];
OpCode op = GET_OPCODE(i);
int A = GETARG_A(i);
switch (op) {
case OP_LOADK: {
int Bx = GETARG_Bx(i);
emit_LOADK(def, A, Bx, pc);
} break;
case OP_LOADKX: {
// OP_LOADKX is followed by OP_EXTRAARG
Instruction inst = code[++pc];
int Ax = GETARG_Ax(inst);
lua_assert(GET_OPCODE(inst) == OP_EXTRAARG);
emit_LOADK(def, A, Ax, pc - 1);
} break;
case OP_CONCAT: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_CONCAT(def, A, B, C, pc);
} break;
case OP_CLOSURE: {
int Bx = GETARG_Bx(i);
emit_CLOSURE(def, A, Bx, pc);
} break;
case OP_VARARG: {
int B = GETARG_B(i);
emit_VARARG(def, A, B, pc);
} break;
case OP_LOADBOOL: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_LOADBOOL(def, A, B, C, pc + 2, pc);
} break;
case OP_MOVE: {
int B = GETARG_B(i);
emit_MOVE(def, A, B, pc);
} break;
case OP_RAVI_MOVEI: {
int B = GETARG_B(i);
emit_MOVEI(def, A, B, pc);
} break;
case OP_RAVI_MOVEF: {
int B = GETARG_B(i);
emit_MOVEF(def, A, B, pc);
} break;
case OP_RAVI_TOINT: {
emit_TOINT(def, A, pc);
} break;
case OP_RAVI_TOFLT: {
emit_TOFLT(def, A, pc);
} break;
case OP_RAVI_NEW_IARRAY: {
emit_NEWARRAYINT(def, A, pc);
} break;
case OP_RAVI_NEW_FARRAY: {
emit_NEWARRAYFLOAT(def, A, pc);
} break;
case OP_NEWTABLE: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_NEWTABLE(def, A, B, C, pc);
} break;
case OP_SETLIST: {
int B = GETARG_B(i);
int C = GETARG_C(i);
if (C == 0) {
// OP_SETLIST is followed by OP_EXTRAARG
Instruction inst = code[++pc];
C = GETARG_Ax(inst);
lua_assert(GET_OPCODE(inst) == OP_EXTRAARG);
}
emit_SETLIST(def, A, B, C, pc);
} break;
case OP_SELF:
case OP_RAVI_SELF_SK: {
int B = GETARG_B(i);
int C = GETARG_C(i);
if (op == OP_RAVI_SELF_SK) { emit_SELF_SK(def, A, B, C, pc); }
else {
emit_SELF(def, A, B, C, pc);
}
} break;
case OP_LEN: {
int B = GETARG_B(i);
emit_LEN(def, A, B, pc);
} break;
case OP_RETURN: {
int B = GETARG_B(i);
emit_RETURN(def, A, B, pc);
} break;
case OP_RAVI_EQ_II:
case OP_RAVI_EQ_FF:
case OP_RAVI_LT_II:
case OP_RAVI_LT_FF:
case OP_RAVI_LE_II:
case OP_RAVI_LE_FF:
case OP_LT:
case OP_LE:
case OP_EQ: {
int B = GETARG_B(i);
int C = GETARG_C(i);
OpCode compOperator = op;
llvm::Constant *comparison_function =
((op == OP_EQ || op == OP_RAVI_EQ_II || op == OP_RAVI_EQ_FF)
? def->luaV_equalobjF
: ((op == OP_LT || op == OP_RAVI_LT_II || op == OP_RAVI_LT_FF)
? def->luaV_lessthanF
: def->luaV_lessequalF));
// OP_EQ is followed by OP_JMP - we process this
// along with OP_EQ
pc++;
i = code[pc];
op = GET_OPCODE(i);
lua_assert(op == OP_JMP);
int sbx = GETARG_sBx(i);
// j below is the jump target
int j = sbx + pc + 1;
emit_EQ(def, A, B, C, j, GETARG_A(i), comparison_function, compOperator,
pc - 1);
} break;
case OP_TFORCALL: {
int B = GETARG_B(i);
int C = GETARG_C(i);
// OP_TFORCALL is followed by OP_TFORLOOP - we process this
// along with OP_TFORCALL
pc++;
i = code[pc];
op = GET_OPCODE(i);
lua_assert(op == OP_TFORLOOP);
int sbx = GETARG_sBx(i);
// j below is the jump target
int j = sbx + pc + 1;
emit_TFORCALL(def, A, B, C, j, GETARG_A(i), pc - 1);
} break;
case OP_TFORLOOP: {
int sbx = GETARG_sBx(i);
int j = sbx + pc + 1;
emit_TFORLOOP(def, A, j, pc);
} break;
case OP_NOT: {
int B = GETARG_B(i);
emit_NOT(def, A, B, pc);
} break;
case OP_RAVI_BNOT_I: {
int B = GETARG_B(i);
emit_BNOT_I(def, A, B, pc);
} break;
case OP_BNOT: {
int B = GETARG_B(i);
emit_BNOT(def, A, B, pc);
} break;
case OP_TEST: {
int B = GETARG_B(i);
int C = GETARG_C(i);
// OP_TEST is followed by OP_JMP - we process this
// along with OP_EQ
pc++;
i = code[pc];
op = GET_OPCODE(i);
lua_assert(op == OP_JMP);
int sbx = GETARG_sBx(i);
// j below is the jump target
int j = sbx + pc + 1;
emit_TEST(def, A, B, C, j, GETARG_A(i), pc - 1);
} break;
case OP_TESTSET: {
int B = GETARG_B(i);
int C = GETARG_C(i);
// OP_TESTSET is followed by OP_JMP - we process this
// along with OP_EQ
pc++;
i = code[pc];
op = GET_OPCODE(i);
lua_assert(op == OP_JMP);
int sbx = GETARG_sBx(i);
// j below is the jump target
int j = sbx + pc + 1;
emit_TESTSET(def, A, B, C, j, GETARG_A(i), pc - 1);
} break;
case OP_JMP: {
int sbx = GETARG_sBx(i);
int j = sbx + pc + 1;
emit_JMP(def, A, j, pc);
} break;
case OP_RAVI_FORPREP_I1:
case OP_RAVI_FORPREP_IP: {
int sbx = GETARG_sBx(i);
int j = sbx + pc + 1;
emit_iFORPREP(def, A, j, op == OP_RAVI_FORPREP_I1, pc);
} break;
case OP_FORPREP: {
int sbx = GETARG_sBx(i);
int j = sbx + pc + 1;
#if RAVI_CODEGEN_FORPREP2
emit_FORPREP2(def, A, j, pc);
#else
emit_FORPREP(def, A, j, pc);
#endif
} break;
case OP_RAVI_FORLOOP_I1:
case OP_RAVI_FORLOOP_IP: {
int sbx = GETARG_sBx(i);
int j = sbx + pc + 1;
emit_iFORLOOP(def, A, j, def->jmp_targets[pc], op == OP_RAVI_FORLOOP_I1,
pc);
} break;
case OP_FORLOOP: {
int sbx = GETARG_sBx(i);
int j = sbx + pc + 1;
#if RAVI_CODEGEN_FORPREP2
emit_FORLOOP2(def, A, j, def->jmp_targets[pc], pc);
#else
emit_FORLOOP(def, A, j, pc);
#endif
} break;
case OP_LOADNIL: {
int B = GETARG_B(i);
emit_LOADNIL(def, A, B, pc);
} break;
case OP_RAVI_LOADFZ: {
emit_LOADFZ(def, A, pc);
} break;
case OP_RAVI_LOADIZ: {
emit_LOADIZ(def, A, pc);
} break;
case OP_TAILCALL:
case OP_CALL: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_CALL(def, A, B, C, pc);
} break;
case OP_RAVI_SETFIELD:
case OP_RAVI_TABLE_SETFIELD: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_SETFIELD(def, A, B, C, pc);
} break;
case OP_RAVI_SETI: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_SETI(def, A, B, C, pc);
} break;
case OP_SETTABLE: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_SETTABLE(def, A, B, C, pc);
} break;
case OP_RAVI_TABLE_GETFIELD: {
int C = GETARG_C(i);
int B = GETARG_B(i);
lua_assert(ISK(C));
TValue *kv = k + INDEXK(C);
TString *key = tsvalue(kv);
lua_assert(key->tt == LUA_TSHRSTR);
emit_GETTABLE_S(def, A, B, C, pc, key);
} break;
case OP_RAVI_TABLE_SELF_SK: {
int C = GETARG_C(i);
int B = GETARG_B(i);
lua_assert(ISK(C));
TValue *kv = k + INDEXK(C);
TString *key = tsvalue(kv);
lua_assert(key->tt == LUA_TSHRSTR);
emit_TABLE_SELF_SK(def, A, B, C, pc, key);
} break;
case OP_RAVI_GETI: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_GETI(def, A, B, C, pc);
} break;
case OP_RAVI_GETFIELD: {
int B = GETARG_B(i);
int C = GETARG_C(i);
lua_assert(ISK(C));
TValue *kv = k + INDEXK(C);
TString *key = tsvalue(kv);
lua_assert(key->tt == LUA_TSHRSTR);
emit_GETFIELD(def, A, B, C, pc, key);
} break;
case OP_GETTABLE: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_GETTABLE(def, A, B, C, pc);
} break;
case OP_RAVI_IARRAY_GET: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_IARRAY_GET(def, A, B, C, omitArrayGetRangeCheck, pc);
} break;
case OP_RAVI_IARRAY_SETI:
case OP_RAVI_IARRAY_SET: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_IARRAY_SET(def, A, B, C, op == OP_RAVI_IARRAY_SETI, pc);
} break;
case OP_RAVI_FARRAY_GET: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_FARRAY_GET(def, A, B, C, omitArrayGetRangeCheck, pc);
} break;
case OP_RAVI_FARRAY_SETF:
case OP_RAVI_FARRAY_SET: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_FARRAY_SET(def, A, B, C, op == OP_RAVI_FARRAY_SETF, pc);
} break;
case OP_RAVI_TOTAB: {
emit_TOARRAY(def, A, RAVI_TTABLE, "table expected", pc);
} break;
case OP_RAVI_TOIARRAY: {
emit_TOARRAY(def, A, RAVI_TARRAYINT, "integer[] expected", pc);
} break;
case OP_RAVI_TOFARRAY: {
emit_TOARRAY(def, A, RAVI_TARRAYFLT, "number[] expected", pc);
} break;
case OP_RAVI_TOSTRING: {
emit_TOSTRING(def, A, pc);
break;
}
case OP_RAVI_TOCLOSURE: {
emit_TOCLOSURE(def, A, pc);
break;
}
case OP_RAVI_TOTYPE: {
int Bx = GETARG_Bx(i);
emit_TOTYPE(def, A, Bx, pc);
break;
}
case OP_RAVI_MOVEIARRAY: {
int B = GETARG_B(i);
emit_MOVEIARRAY(def, A, B, pc);
} break;
case OP_RAVI_MOVEFARRAY: {
int B = GETARG_B(i);
emit_MOVEFARRAY(def, A, B, pc);
} break;
case OP_RAVI_MOVETAB: {
int B = GETARG_B(i);
emit_MOVETAB(def, A, B, pc);
} break;
case OP_RAVI_GETTABUP_SK: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_GETTABUP_SK(def, A, B, C, pc);
} break;
case OP_GETTABUP: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_GETTABUP(def, A, B, C, pc);
} break;
case OP_GETUPVAL: {
int B = GETARG_B(i);
emit_GETUPVAL(def, A, B, pc);
} break;
case OP_SETTABUP: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_SETTABUP(def, A, B, C, pc);
} break;
case OP_SETUPVAL: {
int B = GETARG_B(i);
emit_SETUPVAL(def, A, B, pc);
} break;
case OP_RAVI_SETUPVALI: {
int B = GETARG_B(i);
emit_SETUPVAL_Specific(def, A, B, pc, OP_RAVI_SETUPVALI,
def->raviV_op_setupvaliF);
} break;
case OP_RAVI_SETUPVALF: {
int B = GETARG_B(i);
emit_SETUPVAL_Specific(def, A, B, pc, OP_RAVI_SETUPVALF,
def->raviV_op_setupvalfF);
} break;
case OP_RAVI_SETUPVAL_IARRAY: {
int B = GETARG_B(i);
emit_SETUPVAL_Specific(def, A, B, pc, OP_RAVI_SETUPVAL_IARRAY,
def->raviV_op_setupvalaiF);
} break;
case OP_RAVI_SETUPVAL_FARRAY: {
int B = GETARG_B(i);
emit_SETUPVAL_Specific(def, A, B, pc, OP_RAVI_SETUPVAL_FARRAY,
def->raviV_op_setupvalafF);
} break;
case OP_RAVI_SETUPVALT: {
int B = GETARG_B(i);
emit_SETUPVAL_Specific(def, A, B, pc, OP_RAVI_SETUPVALT,
def->raviV_op_setupvaltF);
} break;
case OP_RAVI_BXOR_II:
case OP_RAVI_BOR_II:
case OP_RAVI_BAND_II: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_BITWISE_BINARY_OP(def, op, A, B, C, pc);
} break;
case OP_BAND:
case OP_BOR:
case OP_BXOR: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_BOR_BXOR_BAND(def, op, A, B, C, pc);
} break;
case OP_SHR:
case OP_SHL:
case OP_RAVI_SHL_II:
case OP_RAVI_SHR_II: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_BITWISE_SHIFT_OP(def, op, A, B, C, pc);
} break;
case OP_ADD: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_ARITH(def, A, B, C, OP_ADD, TM_ADD, pc);
} break;
case OP_RAVI_ADDFF: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_ADDFF(def, A, B, C, pc);
} break;
case OP_RAVI_ADDFI: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_ADDFI(def, A, B, C, pc);
} break;
case OP_RAVI_ADDII: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_ADDII(def, A, B, C, pc);
} break;
case OP_SUB: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_ARITH(def, A, B, C, OP_SUB, TM_SUB, pc);
} break;
case OP_RAVI_SUBFF: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_SUBFF(def, A, B, C, pc);
} break;
case OP_RAVI_SUBFI: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_SUBFI(def, A, B, C, pc);
} break;
case OP_RAVI_SUBIF: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_SUBIF(def, A, B, C, pc);
} break;
case OP_RAVI_SUBII: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_SUBII(def, A, B, C, pc);
} break;
case OP_MUL: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_ARITH(def, A, B, C, OP_MUL, TM_MUL, pc);
} break;
case OP_RAVI_MULFF: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_MULFF(def, A, B, C, pc);
} break;
case OP_RAVI_MULFI: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_MULFI(def, A, B, C, pc);
} break;
case OP_RAVI_MULII: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_MULII(def, A, B, C, pc);
} break;
case OP_DIV: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_ARITH(def, A, B, C, OP_DIV, TM_DIV, pc);
} break;
case OP_RAVI_DIVFF: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_DIVFF(def, A, B, C, pc);
} break;
case OP_RAVI_DIVFI: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_DIVFI(def, A, B, C, pc);
} break;
case OP_RAVI_DIVIF: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_DIVIF(def, A, B, C, pc);
} break;
case OP_RAVI_DIVII: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_DIVII(def, A, B, C, pc);
} break;
case OP_MOD: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_MOD(def, A, B, C, pc);
} break;
case OP_IDIV: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_IDIV(def, A, B, C, pc);
} break;
case OP_POW: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_POW(def, A, B, C, pc);
} break;
case OP_UNM: {
int B = GETARG_B(i);
emit_UNM(def, A, B, pc);
} break;
case OP_RAVI_DEFER: {
emit_DEFER(def, A, pc);
} break;
default: {
fprintf(stderr, "Unexpected bytecode %d\n", op);
abort();
}
}
}
if (doVerify && llvm::verifyFunction(*f->function(), &llvm::errs())) {
f->dump();
fprintf(stderr, "LLVM Code Verification failed\n");
exit(1);
}
ravi::RaviJITFunction *llvm_func = f.release();
p->ravi_jit.jit_data = reinterpret_cast<void *>(llvm_func);
p->ravi_jit.jit_function = nullptr;
p->ravi_jit.jit_status = RAVI_JIT_COMPILED;
module->owner()->set_compiling_flag(false);
return llvm_func != nullptr;
}
void RaviCodeGenerator::scan_jump_targets(RaviFunctionDef *def, Proto *p) {
// We need to pre-create blocks for jump targets so that we
// can generate branch instructions in the code
def->jmp_targets.clear();
const Instruction *code = p->code;
int pc, n = p->sizecode;
def->jmp_targets.resize(n);
for (pc = 0; pc < n; pc++) {
Instruction i = code[pc];
OpCode op = GET_OPCODE(i);
switch (op) {
case OP_LOADBOOL: {
int C = GETARG_C(i);
int j = pc + 2; // jump target
if (C && !def->jmp_targets[j].jmp1)
def->jmp_targets[j].jmp1 =
llvm::BasicBlock::Create(def->jitState->context(), "loadbool");
} break;
case OP_JMP:
case OP_RAVI_FORPREP_IP:
case OP_RAVI_FORPREP_I1:
case OP_RAVI_FORLOOP_IP:
case OP_RAVI_FORLOOP_I1:
case OP_FORLOOP:
case OP_FORPREP:
case OP_TFORLOOP: {
const char *targetname = nullptr;
char temp[80];
if (op == OP_JMP)
targetname = "jmp";
else if (op == OP_FORLOOP || op == OP_RAVI_FORLOOP_IP ||
op == OP_RAVI_FORLOOP_I1)
targetname = "forbody";
else if (op == OP_FORPREP || op == OP_RAVI_FORPREP_IP ||
op == OP_RAVI_FORPREP_I1)
#if RAVI_CODEGEN_FORPREP2
targetname = "forloop_ilt";
#else
targetname = "forloop";
#endif
else
targetname = "tforbody";
int sbx = GETARG_sBx(i);
int j = sbx + pc + 1;
// We append the Lua bytecode location to help debug the IR
snprintf(temp, sizeof temp, "%s%d_", targetname, j + 1);
//
if (!def->jmp_targets[j].jmp1) {
def->jmp_targets[j].jmp1 =
llvm::BasicBlock::Create(def->jitState->context(), temp);
}
#if RAVI_CODEGEN_FORPREP2
if (op == OP_FORPREP) {
lua_assert(def->jmp_targets[j].jmp2 == nullptr);
// first target (created above) is for int < limit
// Second target is for int > limit
snprintf(temp, sizeof temp, "%s%d_", "forloop_igt", j + 1);
def->jmp_targets[j].jmp2 =
llvm::BasicBlock::Create(def->jitState->context(), temp);
// Third target is for float < limit
snprintf(temp, sizeof temp, "%s%d_", "forloop_flt", j + 1);
def->jmp_targets[j].jmp3 =
llvm::BasicBlock::Create(def->jitState->context(), temp);
// Fourth target is for float > limit
snprintf(temp, sizeof temp, "%s%d_", "forloop_fgt", j + 1);
def->jmp_targets[j].jmp4 =
llvm::BasicBlock::Create(def->jitState->context(), temp);
}
#endif
} break;
default: break;
}
}
}
} // namespace ravi