/****************************************************************************** * 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 #include #include #include 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 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 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 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 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 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 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 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 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 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 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<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<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 RaviCodeGenerator::create_function( Proto *p, std::shared_ptr module, llvm::IRBuilder<> &builder, RaviFunctionDef *def) { LuaLLVMTypes *types = jitState_->types(); std::unique_ptr func = std::unique_ptr(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(&luaT_trybinTM), "luaT_trybinTM"); def->luaD_callF = def->raviF->addExternFunction( def->types->luaD_callT, reinterpret_cast(&luaD_call), "luaD_call"); def->luaD_poscallF = def->raviF->addExternFunction( def->types->luaD_poscallT, reinterpret_cast(&luaD_poscall), "luaD_poscall"); def->luaD_precallF = def->raviF->addExternFunction( def->types->luaD_precallT, reinterpret_cast(&luaD_precall), "luaD_precall"); def->luaF_closeF = def->raviF->addExternFunction( def->types->luaF_closeT, reinterpret_cast(&luaF_close), "luaF_close"); def->luaG_runerrorF = def->raviF->addExternFunction( def->types->luaG_runerrorT, reinterpret_cast(&luaG_runerror), "luaG_runerror"); def->luaG_runerrorF->setDoesNotReturn(); def->luaV_equalobjF = def->raviF->addExternFunction( def->types->luaV_equalobjT, reinterpret_cast(&luaV_equalobj), "luaV_equalobj"); def->luaV_lessthanF = def->raviF->addExternFunction( def->types->luaV_lessthanT, reinterpret_cast(&luaV_lessthan), "luaV_lessthan"); def->luaV_lessequalF = def->raviF->addExternFunction( def->types->luaV_lessequalT, reinterpret_cast(&luaV_lessequal), "luaV_lessequal"); def->luaV_forlimitF = def->raviF->addExternFunction( def->types->luaV_forlimitT, reinterpret_cast(&luaV_forlimit), "luaV_forlimit"); def->luaV_tonumberF = def->raviF->addExternFunction( def->types->luaV_tonumberT, reinterpret_cast(&luaV_tonumber_), "luaV_tonumber_"); def->luaV_tointegerF = def->raviF->addExternFunction( def->types->luaV_tointegerT, reinterpret_cast(&luaV_tointeger_), "luaV_tointeger_"); def->luaV_executeF = def->raviF->addExternFunction( def->types->luaV_executeT, reinterpret_cast(&luaV_execute), "luaV_execute"); def->luaV_settableF = def->raviF->addExternFunction( def->types->luaV_settableT, reinterpret_cast(&luaV_settable), "luaV_settable"); def->luaV_gettableF = def->raviF->addExternFunction( def->types->luaV_gettableT, reinterpret_cast(&luaV_gettable), "luaV_gettable"); def->luaH_getintF = def->raviF->addExternFunction( def->types->luaH_getintT, reinterpret_cast(&luaH_getint), "luaH_getint"); def->luaH_setintF = def->raviF->addExternFunction( def->types->luaH_setintT, reinterpret_cast(&luaH_setint), "luaH_setint"); def->luaH_getstrF = def->raviF->addExternFunction( def->types->luaH_getstrT, reinterpret_cast(&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(&luaV_finishget), "luaV_finishget"); def->raviV_op_loadnilF = def->raviF->addExternFunction( def->types->raviV_op_loadnilT, reinterpret_cast(&raviV_op_loadnil), "raviV_op_loadnil"); def->raviV_op_newarrayintF = def->raviF->addExternFunction( def->types->raviV_op_newarrayintT, reinterpret_cast(&raviV_op_newarrayint), "raviV_op_newarrayint"); def->raviV_op_newarrayfloatF = def->raviF->addExternFunction( def->types->raviV_op_newarrayfloatT, reinterpret_cast(&raviV_op_newarrayfloat), "raviV_op_newarrayfloat"); def->raviV_op_newtableF = def->raviF->addExternFunction( def->types->raviV_op_newtableT, reinterpret_cast(&raviV_op_newtable), "raviV_op_newtable"); def->raviV_op_setlistF = def->raviF->addExternFunction( def->types->raviV_op_setlistT, reinterpret_cast(&raviV_op_setlist), "raviV_op_setlist"); def->luaV_modF = def->raviF->addExternFunction( def->types->luaV_modT, reinterpret_cast(&luaV_mod), "luaV_mod"); def->luaV_divF = def->raviF->addExternFunction( def->types->luaV_divT, reinterpret_cast(&luaV_div), "luaV_div"); def->luaV_objlenF = def->raviF->addExternFunction( def->types->luaV_objlenT, reinterpret_cast(&luaV_objlen), "luaV_objlen"); def->luaC_upvalbarrierF = def->raviF->addExternFunction( def->types->luaC_upvalbarrierT, reinterpret_cast(&luaC_upvalbarrier_), "luaC_upvalbarrier_"); def->raviV_op_concatF = def->raviF->addExternFunction( def->types->raviV_op_concatT, reinterpret_cast(&raviV_op_concat), "raviV_op_concat"); def->raviV_op_closureF = def->raviF->addExternFunction( def->types->raviV_op_closureT, reinterpret_cast(&raviV_op_closure), "raviV_op_closure"); def->raviV_op_varargF = def->raviF->addExternFunction( def->types->raviV_op_varargT, reinterpret_cast(&raviV_op_vararg), "raviV_op_vararg"); def->raviH_set_intF = def->raviF->addExternFunction( def->types->raviH_set_intT, reinterpret_cast(&raviH_set_int), "raviH_set_int"); def->raviH_set_floatF = def->raviF->addExternFunction( def->types->raviH_set_floatT, reinterpret_cast(&raviH_set_float), "raviH_set_float"); def->raviV_op_shlF = def->raviF->addExternFunction( def->types->raviV_op_shlT, reinterpret_cast(&raviV_op_shl), "raviV_op_shl"); def->raviV_op_shrF = def->raviF->addExternFunction( def->types->raviV_op_shrT, reinterpret_cast(&raviV_op_shr), "raviV_op_shr"); def->raviV_op_borF = def->raviF->addExternFunction( def->types->raviV_op_borT, reinterpret_cast(&raviV_op_bor), "raviV_op_bor"); def->raviV_op_bxorF = def->raviF->addExternFunction( def->types->raviV_op_bxorT, reinterpret_cast(&raviV_op_bxor), "raviV_op_bxor"); def->raviV_op_bandF = def->raviF->addExternFunction( def->types->raviV_op_bandT, reinterpret_cast(&raviV_op_band), "raviV_op_band"); def->raviV_op_bnotF = def->raviF->addExternFunction( def->types->raviV_op_bnotT, reinterpret_cast(&raviV_op_bnot), "raviV_op_bnot"); def->raviV_op_addF = def->raviF->addExternFunction( def->types->raviV_op_addT, reinterpret_cast(&raviV_op_add), "raviV_op_add"); def->raviV_op_subF = def->raviF->addExternFunction( def->types->raviV_op_subT, reinterpret_cast(&raviV_op_sub), "raviV_op_sub"); def->raviV_op_mulF = def->raviF->addExternFunction( def->types->raviV_op_mulT, reinterpret_cast(&raviV_op_mul), "raviV_op_mul"); def->raviV_op_divF = def->raviF->addExternFunction( def->types->raviV_op_divT, reinterpret_cast(&raviV_op_div), "raviV_op_div"); def->raviV_op_setupvaliF = def->raviF->addExternFunction( def->types->raviV_op_setupvaliT, reinterpret_cast(&raviV_op_setupvali), "raviV_op_setupvali"); def->raviV_op_setupvalfF = def->raviF->addExternFunction( def->types->raviV_op_setupvalfT, reinterpret_cast(&raviV_op_setupvalf), "raviV_op_setupvalf"); def->raviV_op_setupvalaiF = def->raviF->addExternFunction( def->types->raviV_op_setupvalaiT, reinterpret_cast(&raviV_op_setupvalai), "raviV_op_setupvalai"); def->raviV_op_setupvalafF = def->raviF->addExternFunction( def->types->raviV_op_setupvalafT, reinterpret_cast(&raviV_op_setupvalaf), "raviV_op_setupvalaf"); def->raviV_op_setupvaltF = def->raviF->addExternFunction( def->types->raviV_op_setupvaltT, reinterpret_cast(&raviV_op_setupvalt), "raviV_op_setupvalt"); def->raviV_settable_sskeyF = def->raviF->addExternFunction( def->types->raviV_settable_sskeyT, reinterpret_cast(&raviV_settable_sskey), "raviV_settable_sskey"); def->raviV_gettable_sskeyF = def->raviF->addExternFunction( def->types->raviV_gettable_sskeyT, reinterpret_cast(&raviV_gettable_sskey), "raviV_gettable_sskey"); def->raviV_settable_iF = def->raviF->addExternFunction( def->types->raviV_settable_iT, reinterpret_cast(&raviV_settable_i), "raviV_settable_i"); def->raviV_gettable_iF = def->raviF->addExternFunction( def->types->raviV_gettable_iT, reinterpret_cast(&raviV_gettable_i), "raviV_gettable_i"); def->raviV_op_totypeF = def->raviF->addExternFunction( def->types->raviV_op_totypeT, reinterpret_cast(&raviV_op_totype), "raviV_op_totype"); def->raviV_op_deferF = def->raviF->addExternFunction( def->types->raviV_op_deferT, reinterpret_cast(&raviV_op_defer), "raviV_op_defer"); def->ravi_dump_valueF = def->raviF->addExternFunction( def->types->ravi_dump_valueT, reinterpret_cast(&ravi_dump_value), "ravi_dump_value"); def->ravi_dump_stackF = def->raviF->addExternFunction( def->types->ravi_dump_stackT, reinterpret_cast(&ravi_dump_stack), "ravi_dump_stack"); def->ravi_dump_stacktopF = def->raviF->addExternFunction( def->types->ravi_dump_stacktopT, reinterpret_cast(&ravi_dump_stacktop), "ravi_dump_stacktop"); def->ravi_debug_traceF = def->raviF->addExternFunction( def->types->ravi_debug_traceT, reinterpret_cast(&ravi_debug_trace), "ravi_debug_trace"); // Create printf declaration std::vector 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 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 func = std::unique_ptr(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(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 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(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