implement OP_TEST and OP_TESTSET

Dibyendu Majumdar 9 years ago
parent 49883c9ea6
commit 476375db4f

@ -477,6 +477,9 @@ public:
// emit code to load the lua_Integer value from register
llvm::Instruction *emit_load_reg_i(RaviFunctionDef *def, llvm::Value *rb);
// emit code to load the boolean value from register
llvm::Instruction *emit_load_reg_b(RaviFunctionDef *def, llvm::Value *ra);
// emit code to store lua_Number value into register
void emit_store_reg_n(RaviFunctionDef *def, llvm::Value *value,
llvm::Value *dest_ptr);
@ -495,6 +498,10 @@ public:
// emit code to load the type from a register
llvm::Instruction *emit_load_type(RaviFunctionDef *def, llvm::Value *value);
void emit_assign(RaviFunctionDef *def, llvm::Value *ra, llvm::Value *rb);
llvm::Value *emit_boolean_testfalse(RaviFunctionDef *def, llvm::Value *reg, bool not);
// Look for Lua bytecodes that are jump targets and allocate
// a BasicBlock for each such target in def->jump_targets.
// The BasicBlocks are not inserted into the function until later
@ -642,6 +649,13 @@ public:
void emit_EQ(RaviFunctionDef *def, llvm::Value *L_ci, llvm::Value *proto,
int A, int B, int C, int j, int jA, llvm::Constant *callee);
void emit_TEST(RaviFunctionDef *def, llvm::Value *L_ci, llvm::Value *proto,
int A, int B, int C, int j, int jA);
void emit_TESTSET(RaviFunctionDef *def, llvm::Value *L_ci, llvm::Value *proto,
int A, int B, int C, int j, int jA);
private:
RaviJITStateImpl *jitState_;
char temp_[31]; // for name

@ -245,4 +245,54 @@ assert(ravi.compile(tabtest))
assert(tabtest({}) == 5)
print("test 18 OK")
-- test 19
function optest()
local a,b,c = 1, 5
c = a and b
return c
end
-- ravi.dumplua(optest)
assert(ravi.compile(optest))
-- ravi.dumpllvm(optest)
assert(optest() == 5)
print("test 19 OK")
-- test 20
function optest()
local a,b,c = 1, 5
c = a or b
return c
end
-- ravi.dumplua(optest)
assert(ravi.compile(optest))
-- ravi.dumpllvm(optest)
assert(optest() == 1)
print("test 20 OK")
-- test 21
function optest()
local a,b = 1, 5
if a and b then
return b
end
return a
end
-- ravi.dumplua(optest)
assert(ravi.compile(optest))
-- ravi.dumpllvm(optest)
assert(optest() == 5)
print("test 21 OK")
-- test 22
function optest()
local a,b = nil, 5
if a or b then
return b
end
return a
end
-- ravi.dumplua(optest)
assert(ravi.compile(optest))
-- ravi.dumpllvm(optest)
assert(optest() == 5)
print("test 22 OK")

@ -79,9 +79,9 @@ Note that if a Lua functions contains a bytecode that cannot be be JITed then th
+-------------------------+----------+--------------------------------------------------+
| OP_LE | YES | if ((RK(B) <= RK(C)) ~= A) then pc++ |
+-------------------------+----------+--------------------------------------------------+
| OP_TEST | NO | if not (R(A) <=> C) then pc++ |
| OP_TEST | YES | if not (R(A) <=> C) then pc++ |
+-------------------------+----------+--------------------------------------------------+
| OP_TESTSET | NO | if (R(B) <=> C) then R(A) := R(B) else pc++ |
| OP_TESTSET | YES | if (R(B) <=> C) then R(A) := R(B) else pc++ |
+-------------------------+----------+--------------------------------------------------+
| OP_CALL | YES | R(A), .. ,R(A+C-2) := R(A)(R(A+1), .. ,R(A+B-1)) |
+-------------------------+----------+--------------------------------------------------+

@ -86,6 +86,9 @@ void RaviCodeGenerator::emit_JMP(RaviFunctionDef *def, int j) {
def->builder->SetInsertPoint(jmp_block);
}
def->builder->CreateBr(def->jmp_targets[j].jmp1);
llvm::BasicBlock *block =
llvm::BasicBlock::Create(def->jitState->context(), "postjump", def->f);
def->builder->SetInsertPoint(block);
}
llvm::Instruction *RaviCodeGenerator::emit_load_base(RaviFunctionDef *def) {
@ -127,6 +130,15 @@ llvm::Instruction *RaviCodeGenerator::emit_load_reg_i(RaviFunctionDef *def,
return lhs;
}
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;
}
void RaviCodeGenerator::emit_store_reg_n(RaviFunctionDef *def,
llvm::Value *result,
llvm::Value *dest_ptr) {
@ -222,6 +234,8 @@ bool RaviCodeGenerator::canCompile(Proto *p) {
case OP_EQ:
case OP_LT:
case OP_LE:
case OP_TEST:
case OP_TESTSET:
case OP_FORPREP:
case OP_FORLOOP:
case OP_MOVE:
@ -528,6 +542,35 @@ void RaviCodeGenerator::compile(lua_State *L, Proto *p) {
int j = sbx + pc + 1;
emit_EQ(&def, L_ci, proto, A, B, C, j, GETARG_A(i), comparison_function);
} 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, L_ci, proto, A, B, C, j, GETARG_A(i));
} 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, L_ci, proto, A, B, C, j, GETARG_A(i));
} break;
case OP_JMP: {
int sbx = GETARG_sBx(i);
int j = sbx + pc + 1;

@ -89,4 +89,177 @@ void RaviCodeGenerator::emit_EQ(RaviFunctionDef *def, llvm::Value *L_ci,
def->f->getBasicBlockList().push_back(else_block);
def->builder->SetInsertPoint(else_block);
}
llvm::Value *RaviCodeGenerator::emit_boolean_testfalse(RaviFunctionDef *def,
llvm::Value *reg,
bool not) {
// (isnil() || isbool() && b == 0)
llvm::IRBuilder<> TmpB(def->entry, def->entry->begin());
llvm::Value *var = TmpB.CreateAlloca(
llvm::Type::getInt1Ty(def->jitState->context()), nullptr, "b");
llvm::Value *type = emit_load_type(def, reg);
// Test if type == LUA_TNIL (0)
llvm::Value *isnil = def->builder->CreateICmpEQ(type, def->types->kInt[0]);
llvm::BasicBlock *then_block =
llvm::BasicBlock::Create(def->jitState->context(), "if.nil", def->f);
llvm::BasicBlock *else_block =
llvm::BasicBlock::Create(def->jitState->context(), "not.nil");
llvm::BasicBlock *end_block =
llvm::BasicBlock::Create(def->jitState->context(), "end");
def->builder->CreateCondBr(isnil, then_block, else_block);
def->builder->SetInsertPoint(then_block);
auto ins = def->builder->CreateStore(isnil, var);
ins->setMetadata(llvm::LLVMContext::MD_tbaa, def->types->tbaa_intT);
def->builder->CreateBr(end_block);
def->f->getBasicBlockList().push_back(else_block);
def->builder->SetInsertPoint(else_block);
// value is not nil
// so check if bool and b == 0
// Test if type == LUA_TBOOLEAN
llvm::Value *isbool = def->builder->CreateICmpEQ(type, def->types->kInt[1]);
// Test if bool value == 0
llvm::Value *bool_value = emit_load_reg_b(def, reg);
llvm::Value *boolzero =
def->builder->CreateICmpEQ(bool_value, def->types->kInt[0]);
// Test type == LUA_TBOOLEAN && bool value == 0
llvm::Value *andvalue = def->builder->CreateAnd(isbool, boolzero);
auto ins2 = def->builder->CreateStore(andvalue, var);
ins2->setMetadata(llvm::LLVMContext::MD_tbaa, def->types->tbaa_intT);
def->builder->CreateBr(end_block);
def->f->getBasicBlockList().push_back(end_block);
def->builder->SetInsertPoint(end_block);
llvm::Value *result = nullptr;
if (not) {
auto ins = def->builder->CreateLoad(var);
ins->setMetadata(llvm::LLVMContext::MD_tbaa, def->types->tbaa_intT);
result = def->builder->CreateNot(ins);
}
else {
auto ins = def->builder->CreateLoad(var);
ins->setMetadata(llvm::LLVMContext::MD_tbaa, def->types->tbaa_intT);
result = ins;
}
return result;
}
void RaviCodeGenerator::emit_TEST(RaviFunctionDef *def, llvm::Value *L_ci,
llvm::Value *proto, int A, int B, int C,
int j, int jA) {
// case OP_TEST: {
// if (GETARG_C(i) ? l_isfalse(ra) : !l_isfalse(ra))
// ci->u.l.savedpc++;
// else
// donextjump(ci);
// } break;
// 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);
// Get pointer to register A
llvm::Value *ra = emit_gep_ra(def, base_ptr, A);
// v = C ? is_false(ra) : !is_false(ra)
llvm::Value *v = C ? emit_boolean_testfalse(def, ra, false)
: emit_boolean_testfalse(def, ra, true);
// Test NOT v
llvm::Value *result = def->builder->CreateNot(v);
// If !v then we need to execute the next statement which is a jump
llvm::BasicBlock *then_block =
llvm::BasicBlock::Create(def->jitState->context(), "if.then", def->f);
llvm::BasicBlock *else_block =
llvm::BasicBlock::Create(def->jitState->context(), "if.else");
def->builder->CreateCondBr(result, then_block, else_block);
def->builder->SetInsertPoint(then_block);
// if (a > 0) luaF_close(L, ci->u.l.base + a - 1);
if (jA > 0) {
// jA is the A operand of the Jump instruction
// base + a - 1
llvm::Value *val =
jA == 1 ? base_ptr : emit_array_get(def, base_ptr, jA - 1);
// Call luaF_close
def->builder->CreateCall2(def->luaF_closeF, def->L, val);
}
// Do the jump
def->builder->CreateBr(def->jmp_targets[j].jmp1);
// Add the else block and make it current so that the next instruction flows
// here
def->f->getBasicBlockList().push_back(else_block);
def->builder->SetInsertPoint(else_block);
}
void RaviCodeGenerator::emit_TESTSET(RaviFunctionDef *def, llvm::Value *L_ci,
llvm::Value *proto, int A, int B, int C,
int j, int jA) {
// case OP_TESTSET: {
// TValue *rb = RB(i);
// if (GETARG_C(i) ? l_isfalse(rb) : !l_isfalse(rb))
// ci->u.l.savedpc++;
// else {
// setobjs2s(L, ra, rb);
// donextjump(ci);
// }
// } break;
// 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);
// Get pointer to register B
llvm::Value *rb = emit_gep_ra(def, base_ptr, B);
// v = C ? is_false(ra) : !is_false(ra)
llvm::Value *v = C ? emit_boolean_testfalse(def, rb, false)
: emit_boolean_testfalse(def, rb, true);
// Test NOT v
llvm::Value *result = def->builder->CreateNot(v);
// If !v then we need to execute the next statement which is a jump
llvm::BasicBlock *then_block =
llvm::BasicBlock::Create(def->jitState->context(), "if.then", def->f);
llvm::BasicBlock *else_block =
llvm::BasicBlock::Create(def->jitState->context(), "if.else");
def->builder->CreateCondBr(result, then_block, else_block);
def->builder->SetInsertPoint(then_block);
// Get pointer to register A
llvm::Value *ra = emit_gep_ra(def, base_ptr, A);
emit_assign(def, ra, rb);
// if (a > 0) luaF_close(L, ci->u.l.base + a - 1);
if (jA > 0) {
// jA is the A operand of the Jump instruction
// base + a - 1
llvm::Value *val =
jA == 1 ? base_ptr : emit_array_get(def, base_ptr, jA - 1);
// Call luaF_close
def->builder->CreateCall2(def->luaF_closeF, def->L, val);
}
// Do the jump
def->builder->CreateBr(def->jmp_targets[j].jmp1);
// Add the else block and make it current so that the next instruction flows
// here
def->f->getBasicBlockList().push_back(else_block);
def->builder->SetInsertPoint(else_block);
}
}

@ -471,4 +471,25 @@ void RaviCodeGenerator::emit_LOADK(RaviFunctionDef *def, llvm::Value *L_ci,
store = def->builder->CreateStore(load, desttype);
store->setMetadata(llvm::LLVMContext::MD_tbaa, def->types->tbaa_TValue_ttT);
}
void RaviCodeGenerator::emit_assign(RaviFunctionDef *def, llvm::Value *dest, llvm::Value *src) {
// Below is more efficient that memcpy()
// destvalue->value->i = srcvalue->value->i;
// destvalue->value->i = srcvalue->value->i;
llvm::Value *srcvalue = emit_gep(def, "srcvalue", src, 0, 0, 0);
llvm::Value *destvalue = emit_gep(def, "destvalue", dest, 0, 0, 0);
llvm::Instruction *load = def->builder->CreateLoad(srcvalue);
load->setMetadata(llvm::LLVMContext::MD_tbaa, def->types->tbaa_TValue_nT);
llvm::Instruction *store = def->builder->CreateStore(load, destvalue);
store->setMetadata(llvm::LLVMContext::MD_tbaa, def->types->tbaa_TValue_nT);
// destvalue->type = srcvalue->type
llvm::Value *srctype = emit_gep(def, "srctype", src, 0, 1);
llvm::Value *desttype = emit_gep(def, "desttype", dest, 0, 1);
load = def->builder->CreateLoad(srctype);
load->setMetadata(llvm::LLVMContext::MD_tbaa, def->types->tbaa_TValue_ttT);
store = def->builder->CreateStore(load, desttype);
store->setMetadata(llvm::LLVMContext::MD_tbaa, def->types->tbaa_TValue_ttT);
}
}

@ -1,4 +1,4 @@
The tests in ths folder are run via the CMake CTest capability.
The tests in this folder are run via the CMake CTest capability.
On UNIX systems, just execute::

Loading…
Cancel
Save