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