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.
2132 lines
79 KiB
2132 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 <ravi_jit.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;
|
|
}
|
|
|
|
#if 0
|
|
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));
|
|
}
|
|
#endif
|
|
|
|
// 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");
|
|
#ifdef RAVI_DEFER_STATEMENT
|
|
def->raviV_op_deferF = def->raviF->addExternFunction(
|
|
def->types->raviV_op_deferT, reinterpret_cast<void *>(&raviV_op_defer),
|
|
"raviV_op_defer");
|
|
#endif
|
|
#if 0
|
|
// DEBUG routines
|
|
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");
|
|
#endif
|
|
def->ravi_debug_traceF = def->raviF->addExternFunction(
|
|
def->types->ravi_debug_traceT,
|
|
reinterpret_cast<void *>(&raviV_debug_trace), "raviV_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) {
|
|
#ifdef RAVI_DEFER_STATEMENT
|
|
return emit_gep(def, "value", pupval, 0, 3);
|
|
#else
|
|
return emit_gep(def, "value", pupval, 0, 2);
|
|
#endif
|
|
}
|
|
|
|
// 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;
|
|
#ifdef RAVI_DEFER_STATEMENT
|
|
case OP_RAVI_DEFER: {
|
|
emit_DEFER(def, A, pc);
|
|
} break;
|
|
#endif
|
|
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
|