doc updates and some refactoring

gccjit-ravi534
Dibyendu Majumdar 8 years ago
parent 8e1630979e
commit 28b0f77edc

@ -40,7 +40,38 @@ Following points are worth bearing in mind when looking at above benchmarks.
4. Above benchmarks are primarily numerical. In real life scenarios there
are other factors that affect performance. For instance, via FFI LuaJIT
is able to make efficient calls to external C functions, but Ravi does
not have a similar FFI interface.
not have a similar FFI interface. LuaJIT can also inline Lua function calls
but Ravi does not have this ability and hence function calls go via the
Lua infrastructure and are therefore expensive. Ravi's code generation is best
when types are annotated as otherwise the dynamic type checks kill performance
as above benchmarks show. Finally LLVM is a slow compiler relative to LuaJIT's
JIT compiler which is very very fast.
5. Performance of Lua 5.3.2 is better than 5.3.0 or 5.3.1, thanks to the
table optimizations in this version.
table optimizations in this version.
In general to obtain the best performanc with Ravi, following steps are necessary.
1. Annotate types as much as possible.
2. Use fornum loops with integer counters.
3. Avoid function calls inside loop bodies.
4. Do not assume that JIT compilation is beneficial - benchmark code with and without
JIT compilation.
5. Try to compile a set of functions (in a table) preferably at program startup.
This way you pay for the JIT compilation cost only once.
6. Dump the generated Lua bytecode to see if specialised Ravi bytecodes are being
generated or not. If not you may be missing type annotations.
7. Avoid using globals.
8. Note that only functions executing in the main Lua thread are run in JIT mode.
Coroutines in particular are always interpreted.
9. Also note that tail calls are expensive in JIT mode as they are treated as
normal function calls; so it is better to avoid JIT compilation of code that
relies upon tail calls.

@ -66,7 +66,7 @@ Note that if a Lua functions contains a bytecode that cannot be be JITed then th
+-------------------------+----------+--------------------------------------------------+
| OP_LOADBOOL | YES | R(A) := (Bool)B; if (C) pc++ |
+-------------------------+----------+--------------------------------------------------+
| OP_LOADNIL | YES | R(A), R(A+1), ..., R(A+B) := nil |
| OP_LOADNIL | YES (1) | R(A), R(A+1), ..., R(A+B) := nil |
+-------------------------+----------+--------------------------------------------------+
| OP_GETUPVAL | YES | R(A) := UpValue[B] |
+-------------------------+----------+--------------------------------------------------+
@ -80,9 +80,9 @@ Note that if a Lua functions contains a bytecode that cannot be be JITed then th
+-------------------------+----------+--------------------------------------------------+
| OP_SETTABLE | YES | R(A)[RK(B)] := RK(C) |
+-------------------------+----------+--------------------------------------------------+
| OP_NEWTABLE | YES | R(A) := {} (size = B,C) |
| OP_NEWTABLE | YES (1) | R(A) := {} (size = B,C) |
+-------------------------+----------+--------------------------------------------------+
| OP_SELF | YES | R(A+1) := R(B); R(A) := R(B)[RK(C)] |
| OP_SELF | YES (1) | R(A+1) := R(B); R(A) := R(B)[RK(C)] |
+-------------------------+----------+--------------------------------------------------+
| OP_ADD | YES | R(A) := RK(B) + RK(C) |
+-------------------------+----------+--------------------------------------------------+
@ -104,9 +104,9 @@ Note that if a Lua functions contains a bytecode that cannot be be JITed then th
+-------------------------+----------+--------------------------------------------------+
| OP_BXOR | NO | R(A) := RK(B) ~ RK(C) |
+-------------------------+----------+--------------------------------------------------+
| OP_SHL | YES | R(A) := RK(B) << RK(C) |
| OP_SHL | YES (1) | R(A) := RK(B) << RK(C) |
+-------------------------+----------+--------------------------------------------------+
| OP_SHR | YES | R(A) := RK(B) >> RK(C) |
| OP_SHR | YES (1) | R(A) := RK(B) >> RK(C) |
+-------------------------+----------+--------------------------------------------------+
| OP_UNM | YES | R(A) := -R(B) |
+-------------------------+----------+--------------------------------------------------+
@ -114,17 +114,17 @@ Note that if a Lua functions contains a bytecode that cannot be be JITed then th
+-------------------------+----------+--------------------------------------------------+
| OP_NOT | YES | R(A) := not R(B) |
+-------------------------+----------+--------------------------------------------------+
| OP_LEN | YES | R(A) := length of R(B) |
| OP_LEN | YES (1) | R(A) := length of R(B) |
+-------------------------+----------+--------------------------------------------------+
| OP_CONCAT | YES | R(A) := R(B).. ... ..R(C) |
| OP_CONCAT | YES (1) | R(A) := R(B).. ... ..R(C) |
+-------------------------+----------+--------------------------------------------------+
| OP_JMP | YES | c+=sBx; if (A) close all upvalues >= R(A - 1) |
+-------------------------+----------+--------------------------------------------------+
| OP_EQ | YES | if ((RK(B) == RK(C)) ~= A) then pc++ |
| OP_EQ | YES (1) | if ((RK(B) == RK(C)) ~= A) then pc++ |
+-------------------------+----------+--------------------------------------------------+
| OP_LT | YES | if ((RK(B) < RK(C)) ~= A) then pc++ |
| OP_LT | YES (1) | if ((RK(B) < RK(C)) ~= A) then pc++ |
+-------------------------+----------+--------------------------------------------------+
| OP_LE | YES | if ((RK(B) <= RK(C)) ~= A) then pc++ |
| OP_LE | YES (1) | if ((RK(B) <= RK(C)) ~= A) then pc++ |
+-------------------------+----------+--------------------------------------------------+
| OP_TEST | YES | if not (R(A) <=> C) then pc++ |
+-------------------------+----------+--------------------------------------------------+
@ -132,7 +132,7 @@ Note that if a Lua functions contains a bytecode that cannot be be JITed then th
+-------------------------+----------+--------------------------------------------------+
| OP_CALL | YES | R(A), .. ,R(A+C-2) := R(A)(R(A+1), .. ,R(A+B-1)) |
+-------------------------+----------+--------------------------------------------------+
| OP_TAILCALL | YES | return R(A)(R(A+1), ... ,R(A+B-1)) |
| OP_TAILCALL | YES (2) | return R(A)(R(A+1), ... ,R(A+B-1)) |
| | | Compiled as OP_CALL so no tail call optimization |
+-------------------------+----------+--------------------------------------------------+
| OP_RETURN | YES | return R(A), ... ,R(A+B-2) (see note) |
@ -146,11 +146,11 @@ Note that if a Lua functions contains a bytecode that cannot be be JITed then th
+-------------------------+----------+--------------------------------------------------+
| OP_TFORLOOP | YES | if R(A+1) ~= nil then { R(A)=R(A+1); pc += sBx } |
+-------------------------+----------+--------------------------------------------------+
| OP_SETLIST | YES | R(A)[(C-1)*FPF+i] := R(A+i), 1 <= i <= B |
| OP_SETLIST | YES (1) | R(A)[(C-1)*FPF+i] := R(A+i), 1 <= i <= B |
+-------------------------+----------+--------------------------------------------------+
| OP_CLOSURE | YES | R(A) := closure(KPROTO[Bx]) |
| OP_CLOSURE | YES (1) | R(A) := closure(KPROTO[Bx]) |
+-------------------------+----------+--------------------------------------------------+
| OP_VARARG | YES | R(A), R(A+1), ..., R(A+B-2) = vararg |
| OP_VARARG | YES (1) | R(A), R(A+1), ..., R(A+B-2) = vararg |
+-------------------------+----------+--------------------------------------------------+
| OP_EXTRAARG | N/A | extra (larger) argument for previous opcode |
+-------------------------+----------+--------------------------------------------------+
@ -232,13 +232,13 @@ Note that if a Lua functions contains a bytecode that cannot be be JITed then th
| OP_RAVI_FORPREP_I1 | YES | R(A)-=R(A+2); pc+=sBx |
| | | Specialization for integer step == 1 |
+-------------------------+----------+--------------------------------------------------+
| OP_RAVI_SETUPVALI | NO | UpValue[B] := tointeger(R(A)) |
| OP_RAVI_SETUPVALI | YES (1) | UpValue[B] := tointeger(R(A)) |
+-------------------------+----------+--------------------------------------------------+
| OP_RAVI_SETUPVALF | NO | UpValue[B] := tonumber(R(A)) |
| OP_RAVI_SETUPVALF | YES (1) | UpValue[B] := tonumber(R(A)) |
+-------------------------+----------+--------------------------------------------------+
| OP_RAVI_SETUPVALAI | NO | UpValue[B] := toarrayint(R(A)) |
| OP_RAVI_SETUPVALAI | YES (1) | UpValue[B] := toarrayint(R(A)) |
+-------------------------+----------+--------------------------------------------------+
| OP_RAVI_SETUPVALAF | NO | UpValue[B] := toarrayflt(R(A)) |
| OP_RAVI_SETUPVALAF | YES (1) | UpValue[B] := toarrayflt(R(A)) |
+-------------------------+----------+--------------------------------------------------+
| OP_RAVI_SETTABLE_AII | YES | R(A)[RK(B)] := RK(C) where RK(B) is an integer |
| | | R(A) is array of integers, and RK(C) is an int |
@ -254,9 +254,9 @@ Note that if a Lua functions contains a bytecode that cannot be be JITed then th
+-------------------------+----------+--------------------------------------------------+
| OP_RAVI_BXOR_II | YES | R(A) := RK(B) ~ RK(C), operands are int |
+-------------------------+----------+--------------------------------------------------+
| OP_RAVI_SHL_II | YES | R(A) := RK(B) << RK(C), operands are int |
| OP_RAVI_SHL_II | YES (5) | R(A) := RK(B) << RK(C), operands are int |
+-------------------------+----------+--------------------------------------------------+
| OP_RAVI_SHR_II | YES | R(A) := RK(B) >> RK(C), operands are int |
| OP_RAVI_SHR_II | YES (5) | R(A) := RK(B) >> RK(C), operands are int |
+-------------------------+----------+--------------------------------------------------+
| OP_RAVI_BNOT_I | YES | R(A) := ~R(B), int operand |
+-------------------------+----------+--------------------------------------------------+
@ -276,16 +276,32 @@ Note that if a Lua functions contains a bytecode that cannot be be JITed then th
+-------------------------+----------+--------------------------------------------------+
| OP_RAVI_GETTABLE_S | YES | R(A) := R(B)[RK(C)], string key |
+-------------------------+----------+--------------------------------------------------+
| OP_RAVI_GETTABLE_SK | YES | R(A) := R(B)[RK(C)], string key |
+-------------------------+----------+--------------------------------------------------+
| OP_RAVI_SETTABLE_I | YES | R(A)[RK(B)] := RK(C), integer key |
+-------------------------+----------+--------------------------------------------------+
| OP_RAVI_SETTABLE_S | YES | R(A)[RK(B)] := RK(C), string key |
| OP_RAVI_SETTABLE_S | YES (3) | R(A)[RK(B)] := RK(C), string key |
+-------------------------+----------+--------------------------------------------------+
| OP_RAVI_SETTABLE_SK | YES | R(A)[RK(B)] := RK(C), string key |
+-------------------------+----------+--------------------------------------------------+
| OP_RAVI_TOTAB | YES | R(A) := to_table(R(A)) |
+-------------------------+----------+--------------------------------------------------+
| OP_RAVI_MOVETAB | YES | R(A) := R(B), check R(B) is a table |
+-------------------------+----------+--------------------------------------------------+
| OP_RAVI_SETUPVALT | NO | UpValue[B] := to_table(R(A)) |
| OP_RAVI_SETUPVALT | YES (1) | UpValue[B] := to_table(R(A)) |
+-------------------------+----------+--------------------------------------------------+
| OP_RAVI_SELF_SK | YES | R(A+1) := R(B); R(A) := R(B)[RK(C)] |
+-------------------------+----------+--------------------------------------------------+
| OP_RAVI_SELF_S | YES | R(A+1) := R(B); R(A) := R(B)[RK(C)] |
+-------------------------+----------+--------------------------------------------------+
| OP_RAVI_GETTABUP_SK | YES | R(A) := UpValue[B][RK(C)] |
+-------------------------+----------+--------------------------------------------------+
1. These bytecoes are handled via function calls rather than inline code generation
2. Tail calls are the same as ordinary calls.
3. The _SK variant is generated
4. Generates generic SETTABLE
5. Inline code is generated only when operand is a constant integer
Ravi's LLVM JIT compiler source
-------------------------------

@ -1004,7 +1004,6 @@ int luaV_execute (lua_State *L) {
setobj2s(L, ra, cl->upvals[b]->v);
} break;
/* case OP_RAVI_GETTABUP_SK: */
case OP_GETTABUP: {
TValue *upval = cl->upvals[GETARG_B(i)]->v; /* table */
TValue *rc = RKC(i); /* key */
@ -1728,7 +1727,7 @@ int luaV_execute (lua_State *L) {
lua_assert(key->tt == LUA_TSHRSTR);
Table *h = hvalue(rb);
const TValue *v = luaH_getshortstr(h, key);
if (!ttisnil(v) /* || metamethod_absent(h->metatable, TM_INDEX) */) {
if (!ttisnil(v)) {
setobj2s(L, ra, v);
}
else {
@ -1856,10 +1855,12 @@ int luaV_execute (lua_State *L) {
} break;
case OP_RAVI_UNMF: {
lua_assert(0);
TValue *rb = RB(i);
setfltvalue(ra, -fltvalue(rb));
} break;
case OP_RAVI_UNMI: {
lua_assert(0);
TValue *rb = RB(i);
setivalue(ra, -ivalue(rb));
} break;

@ -514,15 +514,16 @@ llvm::Value *RaviCodeGenerator::emit_is_not_value_of_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, LuaTypeCode lua_type,
const char *varname) {
llvm::Value *novariant_type = def->builder->CreateAnd(value_type, def->types->kInt[0x0F]);
return def->builder->CreateICmpNE(novariant_type, def->types->kInt[int(lua_type)],
varname);
RaviFunctionDef *def, llvm::Value *value_type, LuaTypeCode lua_type,
const char *varname) {
llvm::Value *novariant_type =
def->builder->CreateAnd(value_type, def->types->kInt[0x0F]);
return def->builder->CreateICmpNE(novariant_type,
def->types->kInt[int(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);
@ -845,114 +846,11 @@ bool RaviCodeGenerator::canCompile(Proto *p) {
for (pc = 0; pc < n; pc++) {
Instruction i = code[pc];
OpCode o = GET_OPCODE(i);
switch (o) {
case OP_LOADK:
case OP_LOADKX:
case OP_LOADNIL:
case OP_LOADBOOL:
case OP_CALL:
case OP_TAILCALL:
case OP_RETURN:
case OP_JMP:
case OP_EQ:
case OP_RAVI_EQ_II:
case OP_RAVI_EQ_FF:
case OP_LT:
case OP_RAVI_LT_II:
case OP_RAVI_LT_FF:
case OP_LE:
case OP_RAVI_LE_II:
case OP_RAVI_LE_FF:
case OP_NOT:
case OP_TEST:
case OP_TESTSET:
case OP_FORPREP:
case OP_FORLOOP:
case OP_TFORCALL:
case OP_TFORLOOP:
case OP_MOVE:
case OP_ADD:
case OP_SUB:
case OP_MUL:
case OP_DIV:
case OP_MOD:
case OP_IDIV:
case OP_POW:
case OP_UNM:
case OP_LEN:
case OP_VARARG:
case OP_CONCAT:
case OP_CLOSURE:
case OP_SETTABLE:
case OP_GETTABLE:
case OP_GETUPVAL:
case OP_SETUPVAL:
case OP_GETTABUP:
case OP_SETTABUP:
case OP_NEWTABLE:
case OP_SETLIST:
case OP_SELF:
case OP_RAVI_NEWARRAYI:
case OP_RAVI_NEWARRAYF:
case OP_RAVI_MOVEI:
case OP_RAVI_MOVEF:
case OP_RAVI_TOINT:
case OP_RAVI_TOFLT:
case OP_RAVI_LOADFZ:
case OP_RAVI_LOADIZ:
case OP_RAVI_ADDFF:
case OP_RAVI_ADDFI:
case OP_RAVI_ADDII:
case OP_RAVI_SUBFF:
case OP_RAVI_SUBFI:
case OP_RAVI_SUBIF:
case OP_RAVI_SUBII:
case OP_RAVI_MULFF:
case OP_RAVI_MULFI:
case OP_RAVI_MULII:
case OP_RAVI_DIVFF:
case OP_RAVI_DIVFI:
case OP_RAVI_DIVIF:
case OP_RAVI_DIVII:
case OP_RAVI_GETTABLE_AI:
case OP_RAVI_GETTABLE_AF:
case OP_RAVI_SETTABLE_AI:
case OP_RAVI_SETTABLE_AII:
case OP_RAVI_SETTABLE_AF:
case OP_RAVI_SETTABLE_AFF:
case OP_RAVI_TOARRAYI:
case OP_RAVI_TOARRAYF:
case OP_RAVI_MOVEAI:
case OP_RAVI_MOVEAF:
case OP_RAVI_FORLOOP_IP:
case OP_RAVI_FORLOOP_I1:
case OP_RAVI_FORPREP_IP:
case OP_RAVI_FORPREP_I1:
case OP_RAVI_BAND_II:
case OP_RAVI_BOR_II:
case OP_RAVI_BXOR_II:
case OP_RAVI_BNOT_I:
case OP_RAVI_SHL_II:
case OP_RAVI_SHR_II:
case OP_SHR:
case OP_SHL:
case OP_RAVI_GETTABLE_I:
case OP_RAVI_GETTABLE_S:
case OP_RAVI_GETTABLE_SK:
case OP_RAVI_SETTABLE_I:
case OP_RAVI_SETTABLE_S:
case OP_RAVI_SETTABLE_SK:
case OP_RAVI_TOTAB:
case OP_RAVI_MOVETAB:
case OP_RAVI_SETUPVALI:
case OP_RAVI_SETUPVALF:
case OP_RAVI_SETUPVALAI:
case OP_RAVI_SETUPVALAF:
case OP_RAVI_SETUPVALT:
case OP_RAVI_SELF_S:
case OP_RAVI_SELF_SK:
case OP_RAVI_GETTABUP_SK: break;
default: return false;
if (o == OP_BNOT || o == OP_BAND || o == OP_BOR || o == OP_BXOR || 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;
@ -1492,31 +1390,14 @@ bool RaviCodeGenerator::compile(lua_State *L, Proto *p,
emit_SETLIST(def, A, B, C, pc);
} break;
case OP_SELF:
case OP_RAVI_SELF_SK:{
case OP_RAVI_SELF_SK: {
int B = GETARG_B(i);
int C = GETARG_C(i);
/* key must be string */
// if (ISK(C)) {
// TValue *kv = k + INDEXK(C);
// if (ttisshrstring(kv))
// emit_SELF_SK(def, A, B, C, pc);
// else
// emit_SELF(def, A, B, C, pc);
// }
if (op == OP_RAVI_SELF_SK) {
emit_SELF_SK(def, A, B, C, pc);
}
if (op == OP_RAVI_SELF_SK) { emit_SELF_SK(def, A, B, C, pc); }
else {
emit_SELF(def, A, B, C, pc);
}
} break;
#if 0
case OP_SELF: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_SELF(def, A, B, C, pc);
} break;
#endif
case OP_LEN: {
int B = GETARG_B(i);
emit_LEN(def, A, B, pc);
@ -1674,11 +1555,15 @@ bool RaviCodeGenerator::compile(lua_State *L, Proto *p,
emit_SETTABLE_SK(def, A, B, C, pc);
} break;
case OP_RAVI_SETTABLE_I: /*{
case OP_RAVI_SETTABLE_I: /* {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_SETTABLE_I(def, A, B, C, pc);
} break; */
/* The SETTABLE_I code generation is broken as it
doesn't handle metamethods etc. Needs to be done
similar to SETTABLE_SK
*/
case OP_SETTABLE: {
int B = GETARG_B(i);
int C = GETARG_C(i);
@ -1708,19 +1593,6 @@ bool RaviCodeGenerator::compile(lua_State *L, Proto *p,
int C = GETARG_C(i);
emit_GETTABLE_I(def, A, B, C, pc);
} break;
#if 0
// There is a BUG in the code being emitted
// See the emit method for details
case OP_RAVI_GETTABLE_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_GETTABLE_SK(def, A, B, C, pc, key);
} break;
#endif
case OP_RAVI_GETTABLE_SK: {
int B = GETARG_B(i);
int C = GETARG_C(i);
@ -1775,19 +1647,6 @@ bool RaviCodeGenerator::compile(lua_State *L, Proto *p,
emit_MOVETAB(def, A, B, pc);
} break;
#if 0
// There is a BUG in the code being emitted
// Also crashes the tests so code generation is incorrect
case OP_RAVI_GETTABUP_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_GETTABUP_SK(def, A, B, C, pc, key);
} break;
#endif
case OP_RAVI_GETTABUP_SK: {
int B = GETARG_B(i);
int C = GETARG_C(i);
@ -1969,11 +1828,17 @@ bool RaviCodeGenerator::compile(lua_State *L, Proto *p,
emit_UNM(def, A, B, pc);
} break;
default: break;
default: {
fprintf(stderr, "Unexpected bytecode %d\n", op);
abort();
}
}
}
if (doVerify && llvm::verifyFunction(*f->function(), &llvm::errs())) abort();
if (doVerify && llvm::verifyFunction(*f->function(), &llvm::errs())) {
fprintf(stderr, "LLVM Code Verification failed\n");
abort();
}
ravi::RaviJITFunction *llvm_func = f.release();
p->ravi_jit.jit_data = reinterpret_cast<void *>(llvm_func);
p->ravi_jit.jit_function = nullptr;

@ -97,6 +97,9 @@ void RaviCodeGenerator::emit_LEN(RaviFunctionDef *def, int A, int B, int pc) {
// b) we know the key is an integer
void RaviCodeGenerator::emit_SETTABLE_I(RaviFunctionDef *def, int A, int B,
int C, int pc) {
// This is broken as we need to handle meta methods etc.
lua_assert(false);
#if 0
emit_debug_trace(def, OP_RAVI_SETTABLE_I, pc);
emit_load_base(def);
llvm::Value *ra = emit_gep_register(def, A);
@ -105,6 +108,7 @@ void RaviCodeGenerator::emit_SETTABLE_I(RaviFunctionDef *def, int A, int B,
llvm::Instruction *t = emit_load_reg_h(def, ra);
llvm::Instruction *key = emit_load_reg_i(def, rb);
CreateCall4(def->builder, def->luaH_setintF, def->L, t, key, rc);
#endif
}
// R(A)[RK(B)] := RK(C)

Loading…
Cancel
Save