You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ravi/src/ravi_jitshared.c

2417 lines
91 KiB

/******************************************************************************
* Copyright (C) 2015-2021 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_jitshared.h"
#include "ravi_membuf.h"
#include <stddef.h>
#include <assert.h>
#define GOTO_ON_ERROR 1
/*
* Only 64-bits supported right now
* Following must be kept in sync with changes in the actual header files
*/
static const char Lua_header[] =
//"typedef __SIZE_TYPE__ size_t;\n"
"typedef long long size_t;\n"
"typedef long long ptrdiff_t;\n"
"typedef long long intptr_t;\n"
"typedef long long int64_t;\n"
"typedef unsigned long long uint64_t;\n"
"typedef int int32_t;\n"
"typedef unsigned int uint32_t;\n"
"typedef short int16_t;\n"
"typedef unsigned short uint16_t;\n"
"typedef char int8_t;\n"
"typedef unsigned char uint8_t;\n"
"typedef size_t lu_mem;\n"
"typedef ptrdiff_t l_mem;\n"
"typedef unsigned char lu_byte;\n"
"typedef uint16_t LuaType;\n"
"#define NULL ((void *)0)\n"
"typedef struct lua_State lua_State;\n"
"#define LUA_TNONE (-1)\n"
"#define LUA_TNIL 0\n"
"#define LUA_TBOOLEAN 1\n"
"#define LUA_TLIGHTUSERDATA 2\n"
"#define LUA_TNUMBER 3\n"
"#define LUA_TSTRING 4\n"
"#define LUA_TTABLE 5\n"
"#define LUA_TFUNCTION 6\n"
"#define LUA_TUSERDATA 7\n"
"#define LUA_TTHREAD 8\n"
"#define LUA_OK 0\n"
"typedef enum {TM_INDEX,TM_NEWINDEX,TM_GC,\n"
" TM_MODE,TM_LEN,TM_EQ,TM_ADD,TM_SUB,TM_MUL,\n"
" TM_MOD,TM_POW,TM_DIV,TM_IDIV,TM_BAND,TM_BOR,\n"
" TM_BXOR,TM_SHL,TM_SHR,TM_UNM,TM_BNOT,TM_LT,\n"
" TM_LE,TM_CONCAT,TM_CALL,TM_N\n"
"} TMS;\n"
"typedef double lua_Number;\n"
"typedef int64_t lua_Integer;\n"
"typedef uint64_t lua_Unsigned;\n"
"typedef int (*lua_CFunction) (lua_State *L);\n"
"typedef union {\n"
" lua_Number n;\n"
" double u;\n"
" void *s;\n"
" lua_Integer i;\n"
" long l;\n"
"} L_Umaxalign;\n"
"#define lua_assert(c) ((void)0)\n"
"#define check_exp(c,e) (e)\n"
"#define lua_longassert(c) ((void)0)\n"
"#define luai_apicheck(l,e) lua_assert(e)\n"
"#define api_check(l,e,msg) luai_apicheck(l,(e) && msg)\n"
"#define UNUSED(x) ((void)(x))\n"
"#define cast(t, exp) ((t)(exp))\n"
"#define cast_void(i) cast(void, (i))\n"
"#define cast_byte(i) cast(lu_byte, (i))\n"
"#define cast_num(i) cast(lua_Number, (i))\n"
"#define cast_int(i) cast(int, (i))\n"
"#define cast_uchar(i) cast(unsigned char, (i))\n"
"#define l_castS2U(i) ((lua_Unsigned)(i))\n"
"#define l_castU2S(i) ((lua_Integer)(i))\n"
"#define l_noret void\n"
"typedef unsigned int Instruction;\n"
"#define luai_numidiv(L,a,b) ((void)L, l_floor(luai_numdiv(L,a,b)))\n"
"#define luai_numdiv(L,a,b) ((a)/(b))\n"
"#define luai_nummod(L,a,b,m) \\\n"
" { (m) = l_mathop(fmod)(a,b); if ((m)*(b) < 0) (m) += (b); }\n"
"#define LUA_TLCL (LUA_TFUNCTION | (0 << 4))\n"
"#define LUA_TLCF (LUA_TFUNCTION | (1 << 4))\n"
"#define LUA_TCCL (LUA_TFUNCTION | (2 << 4))\n"
"#define RAVI_TFCF (LUA_TFUNCTION | (4 << 4))\n"
"#define LUA_TSHRSTR (LUA_TSTRING | (0 << 4))\n"
"#define LUA_TLNGSTR (LUA_TSTRING | (1 << 4))\n"
"#define LUA_TNUMFLT (LUA_TNUMBER | (0 << 4))\n"
"#define LUA_TNUMINT (LUA_TNUMBER | (1 << 4))\n"
"#define RAVI_TIARRAY (LUA_TTABLE | (1 << 4))\n"
"#define RAVI_TFARRAY (LUA_TTABLE | (2 << 4))\n"
"#define BIT_ISCOLLECTABLE (1 << 15)\n"
"#define ctb(t) ((t) | BIT_ISCOLLECTABLE)\n"
"typedef struct GCObject GCObject;\n"
"#define CommonHeader GCObject *next; lu_byte tt; lu_byte marked\n"
"struct GCObject {\n"
" CommonHeader;\n"
"};\n"
"typedef union Value {\n"
" GCObject *gc;\n"
" void *p;\n"
" int b;\n"
" lua_CFunction f;\n"
" lua_Integer i;\n"
" lua_Number n;\n"
"} Value;\n"
"#define TValuefields Value value_; LuaType tt_\n"
"typedef struct lua_TValue {\n"
" TValuefields;\n"
"} TValue;\n"
"#define NILCONSTANT {NULL}, LUA_TNIL\n"
"#define val_(o) ((o)->value_)\n"
"#define rttype(o) ((o)->tt_)\n"
"#define novariant(x) ((x) & 0x0F)\n"
"#define ttype(o) (rttype(o) & 0x7F)\n"
"#define ttnov(o) (novariant(rttype(o)))\n"
"#define checktag(o,t) (rttype(o) == (t))\n"
"#define checktype(o,t) (ttnov(o) == (t))\n"
"#define ttisnumber(o) checktype((o), LUA_TNUMBER)\n"
"#define ttisfloat(o) checktag((o), LUA_TNUMFLT)\n"
"#define ttisinteger(o) checktag((o), LUA_TNUMINT)\n"
"#define ttisnil(o) checktag((o), LUA_TNIL)\n"
"#define ttisboolean(o) checktag((o), LUA_TBOOLEAN)\n"
"#define ttislightuserdata(o) checktag((o), LUA_TLIGHTUSERDATA)\n"
"#define ttisstring(o) checktype((o), LUA_TSTRING)\n"
"#define ttisshrstring(o) checktag((o), ctb(LUA_TSHRSTR))\n"
"#define ttislngstring(o) checktag((o), ctb(LUA_TLNGSTR))\n"
"#define ttistable(o) checktype((o), LUA_TTABLE)\n"
"#define ttisiarray(o) checktag((o), ctb(RAVI_TIARRAY))\n"
"#define ttisfarray(o) checktag((o), ctb(RAVI_TFARRAY))\n"
"#define ttisarray(o) (ttisiarray(o) || ttisfarray(o))\n"
"#define ttisLtable(o) checktag((o), ctb(LUA_TTABLE))\n"
"#define ttisfunction(o) checktype(o, LUA_TFUNCTION)\n"
"#define ttisclosure(o) ((rttype(o) & 0x1F) == LUA_TFUNCTION)\n"
"#define ttisCclosure(o) checktag((o), ctb(LUA_TCCL))\n"
"#define ttisLclosure(o) checktag((o), ctb(LUA_TLCL))\n"
"#define ttislcf(o) checktag((o), LUA_TLCF)\n"
"#define ttisfcf(o) (ttype(o) == RAVI_TFCF)\n"
"#define ttisfulluserdata(o) checktag((o), ctb(LUA_TUSERDATA))\n"
"#define ttisthread(o) checktag((o), ctb(LUA_TTHREAD))\n"
"#define ttisdeadkey(o) checktag((o), LUA_TDEADKEY)\n"
"#define ivalue(o) check_exp(ttisinteger(o), val_(o).i)\n"
"#define fltvalue(o) check_exp(ttisfloat(o), val_(o).n)\n"
"#define nvalue(o) check_exp(ttisnumber(o), \\\n"
" (ttisinteger(o) ? cast_num(ivalue(o)) : fltvalue(o)))\n"
"#define gcvalue(o) check_exp(iscollectable(o), val_(o).gc)\n"
"#define pvalue(o) check_exp(ttislightuserdata(o), val_(o).p)\n"
"#define tsvalue(o) check_exp(ttisstring(o), gco2ts(val_(o).gc))\n"
"#define uvalue(o) check_exp(ttisfulluserdata(o), gco2u(val_(o).gc))\n"
"#define clvalue(o) check_exp(ttisclosure(o), gco2cl(val_(o).gc))\n"
"#define clLvalue(o) check_exp(ttisLclosure(o), gco2lcl(val_(o).gc))\n"
"#define clCvalue(o) check_exp(ttisCclosure(o), gco2ccl(val_(o).gc))\n"
"#define fvalue(o) check_exp(ttislcf(o), val_(o).f)\n"
"#define fcfvalue(o) check_exp(ttisfcf(o), val_(o).p)\n"
"#define hvalue(o) check_exp(ttistable(o), gco2t(val_(o).gc))\n"
"#define arrvalue(o) check_exp(ttisarray(o), gco2array(val_(o).gc))\n"
"#define arrvalue(o) check_exp(ttisarray(o), gco2array(val_(o).gc))\n"
"#define bvalue(o) check_exp(ttisboolean(o), val_(o).b)\n"
"#define thvalue(o) check_exp(ttisthread(o), gco2th(val_(o).gc))\n"
"#define deadvalue(o) check_exp(ttisdeadkey(o), cast(void *, val_(o).gc))\n"
"#define l_isfalse(o) (ttisnil(o) || (ttisboolean(o) && bvalue(o) == 0))\n"
"#define iscollectable(o) (rttype(o) & BIT_ISCOLLECTABLE)\n"
"#define righttt(obj) (ttype(obj) == gcvalue(obj)->tt)\n"
"#define checkliveness(L,obj) \\\n"
" lua_longassert(!iscollectable(obj) || \\\n"
" (righttt(obj) && (L == NULL || !isdead(G(L),gcvalue(obj)))))\n"
"#define settt_(o,t) ((o)->tt_=(t))\n"
"#define setfltvalue(obj,x) \\\n"
" { TValue *io=(obj); val_(io).n=(x); settt_(io, LUA_TNUMFLT); }\n"
"#define chgfltvalue(obj,x) \\\n"
" { TValue *io=(obj); lua_assert(ttisfloat(io)); val_(io).n=(x); }\n"
"#define setivalue(obj,x) \\\n"
" { TValue *io=(obj); val_(io).i=(x); settt_(io, LUA_TNUMINT); }\n"
"#define chgivalue(obj,x) \\\n"
" { TValue *io=(obj); lua_assert(ttisinteger(io)); val_(io).i=(x); }\n"
"#define setnilvalue(obj) settt_(obj, LUA_TNIL)\n"
"#define setfvalue(obj,x) \\\n"
" { TValue *io=(obj); val_(io).f=(x); settt_(io, LUA_TLCF); }\n"
"#define setfvalue_fastcall(obj, x, tag) \\\n"
"{ \\\n"
" TValue *io = (obj); \\\n"
" lua_assert(tag >= 1 && tag < 0x80); \\\n"
" val_(io).p = (x); \\\n"
" settt_(io, ((tag << 8) | RAVI_TFCF)); \\\n"
"}\n"
"#define setpvalue(obj,x) \\\n"
" { TValue *io=(obj); val_(io).p=(x); settt_(io, LUA_TLIGHTUSERDATA); }\n"
"#define setbvalue(obj,x) \\\n"
" { TValue *io=(obj); val_(io).b=(x); settt_(io, LUA_TBOOLEAN); }\n"
"#define setgcovalue(L,obj,x) \\\n"
" { TValue *io = (obj); GCObject *i_g=(x); \\\n"
" val_(io).gc = i_g; settt_(io, ctb(i_g->tt)); }\n"
"#define setsvalue(L,obj,x) \\\n"
" { TValue *io = (obj); TString *x_ = (x); \\\n"
" val_(io).gc = obj2gco(x_); settt_(io, ctb(x_->tt)); \\\n"
" checkliveness(L,io); }\n"
"#define setuvalue(L,obj,x) \\\n"
" { TValue *io = (obj); Udata *x_ = (x); \\\n"
" val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_TUSERDATA)); \\\n"
" checkliveness(L,io); }\n"
"#define setthvalue(L,obj,x) \\\n"
" { TValue *io = (obj); lua_State *x_ = (x); \\\n"
" val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_TTHREAD)); \\\n"
" checkliveness(L,io); }\n"
"#define setclLvalue(L,obj,x) \\\n"
" { TValue *io = (obj); LClosure *x_ = (x); \\\n"
" val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_TLCL)); \\\n"
" checkliveness(L,io); }\n"
"#define setclCvalue(L,obj,x) \\\n"
" { TValue *io = (obj); CClosure *x_ = (x); \\\n"
" val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_TCCL)); \\\n"
" checkliveness(L,io); }\n"
"#define sethvalue(L,obj,x) \\\n"
" { TValue *io = (obj); Table *x_ = (x); \\\n"
" val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_TTABLE)); \\\n"
" checkliveness(L,io); }\n"
"#define setiarrayvalue(L,obj,x) \\\n"
" { TValue *io = (obj); Table *x_ = (x); \\\n"
" val_(io).gc = obj2gco(x_); settt_(io, ctb(RAVI_TIARRAY)); \\\n"
" checkliveness(L,io); }\n"
"#define setfarrayvalue(L,obj,x) \\\n"
" { TValue *io = (obj); Table *x_ = (x); \\\n"
" val_(io).gc = obj2gco(x_); settt_(io, ctb(RAVI_TFARRAY)); \\\n"
" checkliveness(L,io); }\n"
"#define setdeadvalue(obj) settt_(obj, LUA_TDEADKEY)\n"
"#define setobj(L,obj1,obj2) \\\n"
// NOTE we cannot use aggregate assign so following assigns by field but assumes
// n covers all value types
" { TValue *io1=(obj1); const TValue *io2=(obj2); io1->tt_ = io2->tt_; val_(io1).n = val_(io2).n; \\\n"
" (void)L; checkliveness(L,io1); }\n"
"#define setobjs2s setobj\n"
"#define setobj2s setobj\n"
"#define setsvalue2s setsvalue\n"
"#define sethvalue2s sethvalue\n"
"#define setptvalue2s setptvalue\n"
"#define setobjt2t setobj\n"
"#define setobj2n setobj\n"
"#define setsvalue2n setsvalue\n"
"#define setobj2t setobj\n"
"typedef TValue *StkId;\n"
"typedef struct TString {\n"
" CommonHeader;\n"
" lu_byte extra;\n"
" lu_byte shrlen;\n"
" unsigned int hash;\n"
" union {\n"
" size_t lnglen;\n"
" struct TString *hnext;\n"
" } u;\n"
"} TString;\n"
"typedef union UTString {\n"
" L_Umaxalign dummy;\n"
" TString tsv;\n"
"} UTString;\n"
"#define getstr(ts) \\\n"
" check_exp(sizeof((ts)->extra), cast(char *, (ts)) + sizeof(UTString))\n"
"#define svalue(o) getstr(tsvalue(o))\n"
"#define tsslen(s) ((s)->tt == LUA_TSHRSTR ? (s)->shrlen : (s)->u.lnglen)\n"
"#define vslen(o) tsslen(tsvalue(o))\n"
"typedef struct Udata {\n"
" CommonHeader;\n"
" LuaType ttuv_;\n"
" struct Table *metatable;\n"
" size_t len;\n"
" union Value user_;\n"
"} Udata;\n"
"typedef union UUdata {\n"
" L_Umaxalign dummy;\n"
" Udata uv;\n"
"} UUdata;\n"
"#define getudatamem(u) \\\n"
" check_exp(sizeof((u)->ttuv_), (cast(char*, (u)) + sizeof(UUdata)))\n"
"#define setuservalue(L,u,o) \\\n"
" { const TValue *io=(o); Udata *iu = (u); \\\n"
" iu->user_ = io->value_; iu->ttuv_ = rttype(io); \\\n"
" checkliveness(L,io); }\n"
"#define getuservalue(L,u,o) \\\n"
" { TValue *io=(o); const Udata *iu = (u); \\\n"
" io->value_ = iu->user_; settt_(io, iu->ttuv_); \\\n"
" checkliveness(L,io); }\n"
"typedef enum {\n"
"RAVI_TI_NIL,\n"
"RAVI_TI_FALSE,\n"
"RAVI_TI_TRUE,\n"
"RAVI_TI_INTEGER,\n"
"RAVI_TI_FLOAT,\n"
"RAVI_TI_INTEGER_ARRAY,\n"
"RAVI_TI_FLOAT_ARRAY,\n"
"RAVI_TI_TABLE,\n"
"RAVI_TI_STRING,\n"
"RAVI_TI_FUNCTION,\n"
"RAVI_TI_USERDATA,\n"
"RAVI_TI_OTHER\n"
"} ravi_type_index;\n"
"typedef uint32_t ravi_type_map;\n"
"#define RAVI_TM_NIL (((ravi_type_map)1)<<RAVI_TI_NIL)\n"
"#define RAVI_TM_FALSE (((ravi_type_map)1)<<RAVI_TI_FALSE)\n"
"#define RAVI_TM_TRUE (((ravi_type_map)1)<<RAVI_TI_TRUE)\n"
"#define RAVI_TM_INTEGER (((ravi_type_map)1)<<RAVI_TI_INTEGER)\n"
"#define RAVI_TM_FLOAT (((ravi_type_map)1)<<RAVI_TI_FLOAT)\n"
"#define RAVI_TM_INTEGER_ARRAY (((ravi_type_map)1)<<RAVI_TI_INTEGER_ARRAY)\n"
"#define RAVI_TM_FLOAT_ARRAY (((ravi_type_map)1)<<RAVI_TI_FLOAT_ARRAY)\n"
"#define RAVI_TM_TABLE (((ravi_type_map)1)<<RAVI_TI_TABLE)\n"
"#define RAVI_TM_STRING (((ravi_type_map)1)<<RAVI_TI_STRING)\n"
"#define RAVI_TM_FUNCTION (((ravi_type_map)1)<<RAVI_TI_FUNCTION)\n"
"#define RAVI_TM_USERDATA (((ravi_type_map)1)<<RAVI_TI_USERDATA)\n"
"#define RAVI_TM_OTHER (((ravi_type_map)1)<<RAVI_TI_OTHER)\n"
"#define RAVI_TM_FALSISH (RAVI_TM_NIL | RAVI_TM_FALSE)\n"
"#define RAVI_TM_TRUISH (~RAVI_TM_FALSISH)\n"
"#define RAVI_TM_BOOLEAN (RAVI_TM_FALSE | RAVI_TM_TRUE)\n"
"#define RAVI_TM_NUMBER (RAVI_TM_INTEGER | RAVI_TM_FLOAT)\n"
"#define RAVI_TM_STRING_OR_NIL (RAVI_TM_STRING | RAVI_TM_NIL)\n"
"#define RAVI_TM_FUNCTION_OR_NIL (RAVI_TM_FUNCTION | RAVI_TM_NIL)\n"
"#define RAVI_TM_BOOLEAN_OR_NIL (RAVI_TM_BOOLEAN | RAVI_TM_NIL)\n"
"#define RAVI_TM_USERDATA_OR_NIL (RAVI_TM_USERDATA | RAVI_TM_NIL)\n"
"#define RAVI_TM_INTEGER_OR_NIL (RAVI_TM_INTEGER | RAVI_TM_NIL)\n"
"#define RAVI_TM_FLOAT_OR_NIL (RAVI_TM_FLOAT | RAVI_TM_NIL)"
"#define RAVI_TM_TABLE_OR_NIL (RAVI_TM_TABLE | RAVI_TM_NIL)"
"#define RAVI_TM_INDEXABLE (RAVI_TM_INTEGER_ARRAY | RAVI_TM_FLOAT_ARRAY | RAVI_TM_TABLE)\n"
"#define RAVI_TM_ANY (~0)\n"
"typedef enum {\n"
"RAVI_TNIL = RAVI_TM_NIL, /* NIL */\n"
"RAVI_TNUMINT = RAVI_TM_INTEGER, /* integer number */\n"
"RAVI_TNUMFLT = RAVI_TM_FLOAT, /* floating point number */\n"
"RAVI_TNUMBER = RAVI_TM_NUMBER,\n"
"RAVI_TARRAYINT = RAVI_TM_INTEGER_ARRAY, /* array of ints */\n"
"RAVI_TARRAYFLT = RAVI_TM_FLOAT_ARRAY, /* array of doubles */\n"
"RAVI_TTABLE = RAVI_TM_TABLE, /* Lua table */\n"
"RAVI_TSTRING = RAVI_TM_STRING, /* string */\n"
"RAVI_TFUNCTION = RAVI_TM_FUNCTION, /* Lua or C Function */\n"
"RAVI_TBOOLEAN = RAVI_TM_BOOLEAN, /* boolean */\n"
"RAVI_TTRUE = RAVI_TM_TRUE,\n"
"RAVI_TFALSE = RAVI_TM_FALSE,\n"
"RAVI_TUSERDATA = RAVI_TM_USERDATA, /* userdata or lightuserdata */\n"
"RAVI_TANY = RAVI_TM_ANY, /* Lua dynamic type */\n"
"} ravitype_t;\n"
"typedef struct Upvaldesc {\n"
" TString *name;\n"
" TString *usertype;\n"
" ravi_type_map ravi_type;\n"
" lu_byte instack;\n"
" lu_byte idx;\n"
"} Upvaldesc;\n"
"typedef struct LocVar {\n"
" TString *varname;\n"
" TString *usertype;\n"
" int startpc;\n"
" int endpc;\n"
" ravi_type_map ravi_type;\n"
"} LocVar;\n"
"typedef enum {\n"
" RAVI_JIT_NOT_COMPILED = 0,\n"
" RAVI_JIT_CANT_COMPILE = 1,\n"
" RAVI_JIT_COMPILED = 2\n"
"} ravi_jit_status_t;\n"
"typedef enum {\n"
" RAVI_JIT_FLAG_NONE = 0,\n"
" RAVI_JIT_FLAG_HASFORLOOP = 1\n"
"} ravi_jit_flag_t;\n"
"typedef struct RaviJITProto {\n"
" lu_byte jit_status;\n"
" lu_byte jit_flags;\n"
" unsigned short execution_count;\n"
" void *jit_data;\n"
" lua_CFunction jit_function;\n"
"} RaviJITProto;\n"
"typedef struct Proto {\n"
" CommonHeader;\n"
" lu_byte numparams;\n"
" lu_byte is_vararg;\n"
" lu_byte maxstacksize;\n"
" int sizeupvalues;\n"
" int sizek;\n"
" int sizecode;\n"
" int sizelineinfo;\n"
" int sizep;\n"
" int sizelocvars;\n"
" int linedefined;\n"
" int lastlinedefined;\n"
" TValue *k;\n"
" Instruction *code;\n"
" struct Proto **p;\n"
" int *lineinfo;\n"
" LocVar *locvars;\n"
" Upvaldesc *upvalues;\n"
" struct LClosure *cache;\n"
" TString *source;\n"
" GCObject *gclist;\n"
" RaviJITProto ravi_jit;\n"
"} Proto;\n"
"typedef struct UpVal UpVal;\n"
"#define ClosureHeader \\\n"
" CommonHeader; lu_byte nupvalues; GCObject *gclist\n"
"typedef struct CClosure {\n"
" ClosureHeader;\n"
" lua_CFunction f;\n"
" TValue upvalue[1];\n"
"} CClosure;\n"
"typedef struct LClosure {\n"
" ClosureHeader;\n"
" struct Proto *p;\n"
" UpVal *upvals[1];\n"
"} LClosure;\n"
"typedef union Closure {\n"
" CClosure c;\n"
" LClosure l;\n"
"} Closure;\n"
"#define isLfunction(o) ttisLclosure(o)\n"
"#define getproto(o) (clLvalue(o)->p)\n"
"typedef union TKey {\n"
" struct {\n"
" TValuefields;\n"
" int next;\n"
" } nk;\n"
" TValue tvk;\n"
"} TKey;\n"
"#define setnodekey(L,key,obj) \\\n"
" { TKey *k_=(key); const TValue *io_=(obj); \\\n"
" k_->nk.value_ = io_->value_; k_->nk.tt_ = io_->tt_; \\\n"
" (void)L; checkliveness(L,io_); }\n"
"typedef struct Node {\n"
" TValue i_val;\n"
" TKey i_key;\n"
"} Node;\n"
"typedef enum RaviArrayModifer {\n"
" RAVI_ARRAY_SLICE = 1,\n"
" RAVI_ARRAY_FIXEDSIZE = 2,\n"
" RAVI_ARRAY_ALLOCATED = 4,\n"
" RAVI_ARRAY_ISFLOAT = 8\n"
"} RaviArrayModifier;\n"
"enum {\n"
" RAVI_ARRAY_MAX_INLINE = 3,\n"
"};\n"
"typedef struct RaviArray {\n"
" CommonHeader;\n"
" lu_byte flags;\n"
" unsigned int len;\n"
" unsigned int size;\n"
" union {\n"
" lua_Number numarray[RAVI_ARRAY_MAX_INLINE];\n"
" lua_Integer intarray[RAVI_ARRAY_MAX_INLINE];\n"
" struct RaviArray* parent;\n"
" };\n"
" char *data;\n"
" struct Table *metatable;\n"
"} RaviArray;\n"
"typedef struct Table {\n"
" CommonHeader;\n"
" lu_byte flags;\n"
" lu_byte lsizenode;\n"
" unsigned int sizearray;\n"
" TValue *array;\n"
" Node *node;\n"
" Node *lastfree;\n"
" struct Table *metatable;\n"
" GCObject *gclist;\n"
" unsigned int hmask;\n"
"} Table;\n"
"typedef struct Mbuffer {\n"
" char *buffer;\n"
" size_t n;\n"
" size_t buffsize;\n"
"} Mbuffer;\n"
"typedef struct stringtable {\n"
" TString **hash;\n"
" int nuse;\n"
" int size;\n"
"} stringtable;\n"
"struct lua_Debug;\n"
"typedef intptr_t lua_KContext;\n"
"typedef int(*lua_KFunction)(struct lua_State *L, int status, lua_KContext ctx);\n"
"typedef void *(*lua_Alloc)(void *ud, void *ptr, size_t osize,\n"
" size_t nsize);\n"
"typedef void(*lua_Hook)(struct lua_State *L, struct lua_Debug *ar);\n"
"typedef struct CallInfo {\n"
" StkId func;\n"
" StkId top;\n"
" struct CallInfo *previous, *next;\n"
" union {\n"
" struct {\n"
" StkId base;\n"
" const Instruction *savedpc;\n"
" } l;\n"
" struct {\n"
" lua_KFunction k;\n"
" ptrdiff_t old_errfunc;\n"
" lua_KContext ctx;\n"
" } c;\n"
" } u;\n"
" ptrdiff_t extra;\n"
" short nresults;\n"
" unsigned short callstatus;\n"
" unsigned short stacklevel;\n"
" lu_byte jitstatus;\n"
" lu_byte magic;\n"
"} CallInfo;\n"
"#define CIST_OAH (1<<0)\n"
"#define CIST_LUA (1<<1)\n"
"#define CIST_HOOKED (1<<2)\n"
"#define CIST_FRESH (1<<3)\n"
"#define CIST_YPCALL (1<<4)\n"
"#define CIST_TAIL (1<<5)\n"
"#define CIST_HOOKYIELD (1<<6)\n"
"#define CIST_LEQ (1<<7)\n"
"#define CIST_FIN (1<<8)\n"
"#define isLua(ci) ((ci)->callstatus & CIST_LUA)\n"
"#define isJITed(ci) ((ci)->jitstatus)\n"
"#define setoah(st,v) ((st) = ((st) & ~CIST_OAH) | (v))\n"
"#define getoah(st) ((st) & CIST_OAH)\n"
"typedef struct global_State global_State;\n"
"struct lua_State {\n"
" CommonHeader;\n"
" lu_byte status;\n"
" StkId top;\n"
" global_State *l_G;\n"
" CallInfo *ci;\n"
" const Instruction *oldpc;\n"
" StkId stack_last;\n"
" StkId stack;\n"
" UpVal *openupval;\n"
" GCObject *gclist;\n"
" struct lua_State *twups;\n"
" struct lua_longjmp *errorJmp;\n"
" CallInfo base_ci;\n"
" volatile lua_Hook hook;\n"
" ptrdiff_t errfunc;\n"
" int stacksize;\n"
" int basehookcount;\n"
" int hookcount;\n"
" unsigned short nny;\n"
" unsigned short nCcalls;\n"
" lu_byte hookmask;\n"
" lu_byte allowhook;\n"
" unsigned short nci;\n"
" lu_byte magic;\n"
"};\n"
"#define G(L) (L->l_G)\n"
"union GCUnion {\n"
" GCObject gc;\n"
" struct TString ts;\n"
" struct Udata u;\n"
" union Closure cl;\n"
" struct Table h;\n"
" struct RaviArray arr;\n"
" struct Proto p;\n"
" struct lua_State th;\n"
"};\n"
"struct UpVal {\n"
" TValue *v;\n"
" unsigned int refcount;\n"
" unsigned int flags;\n"
" union {\n"
" struct {\n"
" UpVal *next;\n"
" int touched;\n"
" } open;\n"
" TValue value;\n"
" } u;\n"
"};\n"
"#define cast_u(o) cast(union GCUnion *, (o))\n"
"#define gco2ts(o) \\\n"
" check_exp(novariant((o)->tt) == LUA_TSTRING, &((cast_u(o))->ts))\n"
"#define gco2u(o) check_exp((o)->tt == LUA_TUSERDATA, &((cast_u(o))->u))\n"
"#define gco2lcl(o) check_exp((o)->tt == LUA_TLCL, &((cast_u(o))->cl.l))\n"
"#define gco2ccl(o) check_exp((o)->tt == LUA_TCCL, &((cast_u(o))->cl.c))\n"
"#define gco2cl(o) \\\n"
" check_exp(novariant((o)->tt) == LUA_TFUNCTION, &((cast_u(o))->cl))\n"
"#define gco2t(o) check_exp((o)->tt == LUA_TTABLE, &((cast_u(o))->h))\n"
"#define gco2array(o) check_exp(((o)->tt == RAVI_TIARRAY || (o)->tt == RAVI_TFARRAY), &((cast_u(o))->arr))\n"
"#define gco2p(o) check_exp((o)->tt == LUA_TPROTO, &((cast_u(o))->p))\n"
"#define gco2th(o) check_exp((o)->tt == LUA_TTHREAD, &((cast_u(o))->th))\n"
"#define obj2gco(v) \\\n"
" check_exp(novariant((v)->tt) < LUA_TDEADKEY, (&(cast_u(v)->gc)))\n"
"#define LUA_FLOORN2I 0\n"
"#define tonumber(o,n) \\\n"
" (ttisfloat(o) ? (*(n) = fltvalue(o), 1) : luaV_tonumber_(o,n))\n"
"#define tointeger(o,i) \\\n"
" (ttisinteger(o) ? (*(i) = ivalue(o), 1) : luaV_tointeger(o,i,LUA_FLOORN2I))\n"
"extern int luaV_tonumber_(const TValue *obj, lua_Number *n);\n"
"extern int luaV_tointeger(const TValue *obj, lua_Integer *p, int mode);\n"
"extern int luaF_close (lua_State *L, StkId level, int status);\n"
"extern int luaD_poscall (lua_State *L, CallInfo *ci, StkId firstResult, int nres);\n"
"extern int luaV_equalobj(lua_State *L, const TValue *t1, const TValue *t2);\n"
"extern int luaV_lessthan(lua_State *L, const TValue *l, const TValue *r);\n"
"extern int luaV_lessequal(lua_State *L, const TValue *l, const TValue *r);\n"
"extern void luaV_gettable (lua_State *L, const TValue *t, TValue *key, StkId val);\n"
"extern void luaV_settable (lua_State *L, const TValue *t, TValue *key, StkId val);\n"
"extern int luaV_execute(lua_State *L);\n"
"extern int luaD_precall (lua_State *L, StkId func, int nresults, int op_call);\n"
"extern void raviV_op_newtable(lua_State *L, CallInfo *ci, TValue *ra, int b, int c);\n"
"extern void raviV_op_newarrayint(lua_State *L, CallInfo *ci, TValue *ra);\n"
"extern void raviV_op_newarrayfloat(lua_State *L, CallInfo *ci, TValue *ra);\n"
"extern void luaO_arith (lua_State *L, int op, const TValue *p1, const TValue *p2, TValue *res);\n"
"extern void raviV_op_setlist(lua_State *L, CallInfo *ci, TValue *ra, int b, int c);\n"
"extern void raviV_op_concat(lua_State *L, CallInfo *ci, int a, int b, int c);\n"
"extern void raviV_op_closure(lua_State *L, CallInfo *ci, LClosure *cl, int a, int Bx);\n"
"extern void raviV_op_vararg(lua_State *L, CallInfo *ci, LClosure *cl, int a, int b);\n"
"extern void luaV_objlen (lua_State *L, StkId ra, const TValue *rb);\n"
"extern int luaV_forlimit(const TValue *obj, lua_Integer *p, lua_Integer step, int *stopnow);\n"
"extern void raviV_op_setupval(lua_State *L, LClosure *cl, TValue *ra, int b);\n"
"extern void raviV_op_setupvali(lua_State *L, LClosure *cl, TValue *ra, int b);\n"
"extern void raviV_op_setupvalf(lua_State *L, LClosure *cl, TValue *ra, int b);\n"
"extern void raviV_op_setupvalai(lua_State *L, LClosure *cl, TValue *ra, int b);\n"
"extern void raviV_op_setupvalaf(lua_State *L, LClosure *cl, TValue *ra, int b);\n"
"extern void raviV_op_setupvalt(lua_State *L, LClosure *cl, TValue *ra, int b);\n"
"extern void raviV_raise_error(lua_State *L, int errorcode);\n"
"extern void raviV_raise_error_with_info(lua_State *L, int errorcode, const char *info);\n"
"extern void luaD_call (lua_State *L, StkId func, int nResults);\n"
"extern void raviH_set_int(lua_State *L, RaviArray *t, lua_Unsigned key, lua_Integer value);\n"
"extern void raviH_set_float(lua_State *L, RaviArray *t, lua_Unsigned key, lua_Number value);\n"
"extern int raviV_check_usertype(lua_State *L, TString *name, const TValue *o);\n"
"extern void luaT_trybinTM (lua_State *L, const TValue *p1, const TValue *p2, TValue *res, TMS event);\n"
"extern void raviV_gettable_sskey(lua_State *L, const TValue *t, TValue *key, TValue *val);\n"
"extern void raviV_settable_sskey(lua_State *L, const TValue *t, TValue *key, TValue *val);\n"
"extern void raviV_gettable_i(lua_State *L, const TValue *t, TValue *key, TValue *val);\n"
"extern void raviV_settable_i(lua_State *L, const TValue *t, TValue *key, TValue *val);\n"
"extern void raviV_op_defer(lua_State *L, TValue *ra);\n"
"extern lua_Integer luaV_shiftl(lua_Integer x, lua_Integer y);\n"
"extern void ravi_dump_value(lua_State *L, const struct lua_TValue *v);\n"
"extern void raviV_op_bnot(lua_State *L, TValue *ra, TValue *rb);\n"
"#define R(i) (base + i)\n"
"#define K(i) (k + i)\n"
"#define tonumberns(o,n) \\\n"
" (ttisfloat(o) ? ((n) = fltvalue(o), 1) : \\\n"
" (ttisinteger(o) ? ((n) = cast_num(ivalue(o)), 1) : 0))\n"
"#define intop(op,v1,v2) l_castU2S(l_castS2U(v1) op l_castS2U(v2))\n"
"#define nan (0./0.)\n"
"#define inf (1./0.)\n"
"#define luai_numunm(L,a) (-(a))\n";
// FIXME - 'inf' and 'nan' above need to be sorted!
// We can only compile a subset of op codes
// and not all features are supported
bool raviJ_cancompile(Proto *p) { (void)p; return true; }
struct function {
struct lua_State *L;
struct Proto *p;
char fname[30];
int var;
// flags to mark instructions
unsigned char *jmps;
// Flags related to locals
unsigned char *locals;
membuff_t prologue;
membuff_t body;
struct ravi_compile_options_t *options;
};
// Identify Ravi bytecode instructions that are jump
// targets - we need this so that when generating code
// we can emit labels for gotos
static void scan_jump_targets(struct function *fn) {
const Instruction *code = fn->p->code;
int pc, n = fn->p->sizecode;
assert(fn->jmps != NULL);
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);
if (C) {
int j = pc + 2; // jump target
assert(j < n);
fn->jmps[j] = true;
}
} 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: {
int sbx = GETARG_sBx(i);
int j = sbx + pc + 1;
assert(j < n);
fn->jmps[j] = true;
} break;
default:
break;
}
}
}
static void emit_reg(struct function *fn, const char *name, int regnum) {
assert(!ISK(regnum));
membuff_add_fstring(&fn->body, "%s = R(%d);\n", name, regnum);
}
static void emit_reg_or_k(struct function *fn, const char *name, int regnum) {
if (ISK(regnum)) {
membuff_add_fstring(&fn->body, "%s = K(%d);\n", name, INDEXK(regnum));
}
else {
membuff_add_fstring(&fn->body, "%s = R(%d);\n", name, regnum);
}
}
// 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 only set this prior to
// function calls - this enables better stack traces for example, and ad-hoc
// calls to debug api.
static void emit_update_savedpc(struct function *fn, int pc) {
// Need to set savedpc to the next instruction (pc+1) rather than current as
// that is
// what the VM does in interpreted mode
membuff_add_fstring(&fn->body, "ci->u.l.savedpc = &cl->p->code[%d+1];\n", pc);
}
static void emit_IARRAY_GET(struct function *fn, int A, int B, int C, bool omitArrayGetRangeCheck, int pc) {
(void)pc;
emit_reg(fn, "ra", A);
emit_reg(fn, "rb", B);
emit_reg_or_k(fn, "rc", C);
membuff_add_string(&fn->body, "ukey = (lua_Unsigned)(ivalue(rc));\n");
membuff_add_string(&fn->body, "t = arrvalue(rb);");
membuff_add_string(&fn->body, "iptr = (lua_Integer *)t->data;\n");
if (!omitArrayGetRangeCheck) {
membuff_add_string(&fn->body, "if (ukey < (lua_Unsigned)(t->len)) {\n");
}
membuff_add_string(&fn->body, " setivalue(ra, iptr[ukey]);\n");
if (!omitArrayGetRangeCheck) {
membuff_add_string(&fn->body, "} else {\n");
#if GOTO_ON_ERROR
membuff_add_fstring(&fn->body, " error_code = %d;\n", Error_array_out_of_bounds);
membuff_add_string(&fn->body, " goto Lraise_error;\n");
#else
membuff_add_fstring(&fn->body, " raviV_raise_error(L, %d);\n", Error_array_out_of_bounds);
#endif
membuff_add_string(&fn->body, "}\n");
}
}
static void emit_FARRAY_GET(struct function *fn, int A, int B, int C, bool omitArrayGetRangeCheck, int pc) {
(void)pc;
emit_reg(fn, "ra", A);
emit_reg(fn, "rb", B);
emit_reg_or_k(fn, "rc", C);
membuff_add_string(&fn->body, "ukey = (lua_Unsigned)(ivalue(rc));\n");
membuff_add_string(&fn->body, "t = arrvalue(rb);");
membuff_add_string(&fn->body, "nptr = (lua_Number *)t->data;\n");
if (!omitArrayGetRangeCheck) {
membuff_add_string(&fn->body, "if (ukey < (lua_Unsigned)(t->len)) {\n");
}
membuff_add_string(&fn->body, " setfltvalue(ra, nptr[ukey]);\n");
if (!omitArrayGetRangeCheck) {
membuff_add_string(&fn->body, "} else {\n");
#if GOTO_ON_ERROR
membuff_add_fstring(&fn->body, " error_code = %d;\n", Error_array_out_of_bounds);
membuff_add_string(&fn->body, " goto Lraise_error;\n");
#else
membuff_add_fstring(&fn->body, " raviV_raise_error(L, %d);\n", Error_array_out_of_bounds);
#endif
membuff_add_string(&fn->body, "}\n");
}
}
static void emit_IARRAY_SETI(struct function *fn, int A, int B, int C, bool known_int, int pc) {
(void)pc;
(void)known_int;
emit_reg(fn, "ra", A);
emit_reg_or_k(fn, "rb", B);
emit_reg_or_k(fn, "rc", C);
membuff_add_string(&fn->body, "t = arrvalue(ra);\n");
membuff_add_string(&fn->body, "ukey = (lua_Unsigned)(ivalue(rb));\n");
membuff_add_string(&fn->body, "iptr = (lua_Integer *)t->data;\n");
membuff_add_string(&fn->body, "if (ukey < (lua_Unsigned)(t->len)) {\n");
membuff_add_string(&fn->body, " iptr[ukey] = ivalue(rc);\n");
membuff_add_string(&fn->body, "} else {\n");
membuff_add_fstring(&fn->body, " raviH_set_int(L, t, ukey, ivalue(rc));\n");
membuff_add_string(&fn->body, "}\n");
}
static void emit_IARRAY_SET(struct function *fn, int A, int B, int C, bool known_int, int pc) {
(void)pc;
(void)known_int;
emit_reg(fn, "ra", A);
emit_reg_or_k(fn, "rb", B);
emit_reg_or_k(fn, "rc", C);
membuff_add_string(&fn->body, "t = arrvalue(ra);\n");
membuff_add_string(&fn->body, "ukey = (lua_Unsigned)(ivalue(rb));\n");
membuff_add_string(&fn->body, "iptr = (lua_Integer *)t->data;\n");
membuff_add_string(&fn->body, "if (!ttisinteger(rc)) {\n");
#if GOTO_ON_ERROR
membuff_add_fstring(&fn->body, " error_code = %d;\n", Error_integer_expected);
membuff_add_string(&fn->body, " goto Lraise_error;\n");
#else
membuff_add_fstring(&fn->body, " raviV_raise_error(L, %d);\n", Error_integer_expected);
#endif
membuff_add_string(&fn->body, "}\n");
membuff_add_string(&fn->body, "i = ivalue(rc);\n");
membuff_add_string(&fn->body, "if (ukey < (lua_Unsigned)(t->len)) {\n");
membuff_add_string(&fn->body, " iptr[ukey] = i;\n");
membuff_add_string(&fn->body, "} else {\n");
membuff_add_fstring(&fn->body, " raviH_set_int(L, t, ukey, i);\n");
membuff_add_string(&fn->body, "}\n");
}
static void emit_FARRAY_SETF(struct function *fn, int A, int B, int C, bool known_int, int pc) {
(void)pc;
(void)known_int;
emit_reg(fn, "ra", A);
emit_reg_or_k(fn, "rb", B);
emit_reg_or_k(fn, "rc", C);
membuff_add_string(&fn->body, "t = arrvalue(ra);\n");
membuff_add_string(&fn->body, "ukey = (lua_Unsigned)(ivalue(rb));\n");
membuff_add_string(&fn->body, "nptr = (lua_Number *)t->data;\n");
membuff_add_string(&fn->body, "if (ukey < (lua_Unsigned)(t->len)) {\n");
membuff_add_string(&fn->body, " nptr[ukey] = fltvalue(rc);\n");
membuff_add_string(&fn->body, "} else {\n");
membuff_add_fstring(&fn->body, " raviH_set_float(L, t, ukey, fltvalue(rc));\n");
membuff_add_string(&fn->body, "}\n");
}
static void emit_FARRAY_SET(struct function *fn, int A, int B, int C, bool known_int, int pc) {
(void)pc;
(void)known_int;
emit_reg(fn, "ra", A);
emit_reg_or_k(fn, "rb", B);
emit_reg_or_k(fn, "rc", C);
membuff_add_string(&fn->body, "t = arrvalue(ra);\n");
membuff_add_string(&fn->body, "ukey = (lua_Unsigned)(ivalue(rb));\n");
membuff_add_string(&fn->body, "nptr = (lua_Number *)t->data;\n");
membuff_add_string(&fn->body, "if (!ttisnumber(rc)) {\n");
#if GOTO_ON_ERROR
membuff_add_fstring(&fn->body, " error_code = %d;\n", Error_number_expected);
membuff_add_string(&fn->body, " goto Lraise_error;\n");
#else
membuff_add_fstring(&fn->body, " raviV_raise_error(L, %d);\n", Error_number_expected);
#endif
membuff_add_string(&fn->body, "}\n");
membuff_add_string(&fn->body, "n = (ttisinteger(rc) ? (double)ivalue(rc) : fltvalue(rc));\n");
membuff_add_string(&fn->body, "if (ukey < (lua_Unsigned)(t->len)) {\n");
membuff_add_string(&fn->body, " nptr[ukey] = n;\n");
membuff_add_string(&fn->body, "} else {\n");
membuff_add_fstring(&fn->body, " raviH_set_float(L, t, ukey, n);\n");
membuff_add_string(&fn->body, "}\n");
}
/* Handle OP_ADD, OP_SUB, OP_MUL */
static void emit_op_arithslow(struct function *fn, int A, int B, int C, OpCode op, int pc, const char *opchar,
const char *tm) {
(void)op;
emit_reg(fn, "ra", A);
emit_reg_or_k(fn, "rb", B);
emit_reg_or_k(fn, "rc", C);
membuff_add_string(&fn->body, "if (ttisinteger(rb) && ttisinteger(rc)) {\n");
membuff_add_string(&fn->body, " i = ivalue(rb);\n");
membuff_add_string(&fn->body, " ic = ivalue(rc);\n");
membuff_add_fstring(&fn->body, " setivalue(ra, (i %s ic));\n", opchar);
membuff_add_string(&fn->body, "} else if (tonumberns(rb, n) && tonumberns(rc, nc)) {\n");
membuff_add_fstring(&fn->body, " setfltvalue(ra, (n %s nc));\n", opchar);
membuff_add_string(&fn->body, "} else {\n");
emit_update_savedpc(fn, pc);
membuff_add_fstring(&fn->body, " luaT_trybinTM(L, rb, rc, ra, %s);\n", tm);
membuff_add_string(&fn->body, " base = ci->u.l.base;\n");
membuff_add_string(&fn->body, "}\n");
}
static void emit_op_unm(struct function *fn, int A, int B, int pc) {
emit_reg(fn, "ra", A);
emit_reg(fn, "rb", B);
membuff_add_string(&fn->body, "if (ttisinteger(rb)) {\n");
membuff_add_string(&fn->body, " i = ivalue(rb);\n");
membuff_add_string(&fn->body, " setivalue(ra, intop(-, 0, i));\n");
membuff_add_string(&fn->body, "} else if (tonumberns(rb, n)) {\n");
membuff_add_string(&fn->body, " setfltvalue(ra, luai_numunm(L, n));\n");
membuff_add_string(&fn->body, "} else {\n");
emit_update_savedpc(fn, pc);
membuff_add_string(&fn->body, " luaT_trybinTM(L, rb, rb, ra, TM_UNM);\n");
membuff_add_string(&fn->body, " base = ci->u.l.base;\n");
membuff_add_string(&fn->body, "}\n");
}
static void emit_op_unmf(struct function *fn, int A, int B, int pc) {
(void)pc;
emit_reg(fn, "ra", A);
emit_reg(fn, "rb", B);
membuff_add_string(&fn->body, "setfltvalue(ra, -fltvalue(rb));\n");
}
static void emit_op_unmi(struct function *fn, int A, int B, int pc) {
(void)pc;
emit_reg(fn, "ra", A);
emit_reg(fn, "rb", B);
membuff_add_string(&fn->body, "setivalue(ra, -ivalue(rb));\n");
}
static void emit_op_bnot(struct function *fn, int A, int B, int pc) {
emit_reg(fn, "ra", A);
emit_reg(fn, "rb", B);
emit_update_savedpc(fn, pc);
membuff_add_string(&fn->body, "raviV_op_bnot(L, ra, rb);\n");
membuff_add_string(&fn->body, "base = ci->u.l.base;\n");
}
static void emit_int_bitop(struct function *fn, int A, int B, int C, OpCode opCode, int pc) {
(void)pc;
emit_reg(fn, "ra", A);
emit_reg_or_k(fn, "rb", B);
emit_reg_or_k(fn, "rc", C);
switch (opCode) {
case OP_RAVI_BAND_II:
membuff_add_string(&fn->body, "setivalue(ra, intop(&, ivalue(rb), ivalue(rc)));\n");
break;
case OP_RAVI_BOR_II:
membuff_add_string(&fn->body, "setivalue(ra, intop(|, ivalue(rb), ivalue(rc)));\n");
break;
case OP_RAVI_BXOR_II:
membuff_add_string(&fn->body, "setivalue(ra, intop(^, ivalue(rb), ivalue(rc)));\n");
break;
case OP_RAVI_SHL_II:
membuff_add_string(&fn->body, "setivalue(ra, luaV_shiftl(ivalue(rb), ivalue(rc)));\n");
break;
case OP_RAVI_SHR_II:
membuff_add_string(&fn->body, "ic = ivalue(rc);\n");
membuff_add_string(&fn->body, "setivalue(ra, luaV_shiftl(ivalue(rb), -ic));\n");
break;
default:
abort();
}
}
static void emit_op_BNOT_I(struct function *fn, int A, int B, int pc) {
(void)pc;
emit_reg(fn, "ra", A);
emit_reg_or_k(fn, "rb", B);
membuff_add_string(&fn->body, "i = ivalue(rb);\n");
membuff_add_string(&fn->body, "setivalue(ra, intop (^, ~l_castS2U(0), i));\n");
}
static void emit_comparison(struct function *fn, int A, int B, int C, int j, int jA, const char *compfunc,
OpCode opCode, int pc) {
const char *oper = "==";
switch (opCode) {
case OP_RAVI_LT_II:
oper = "<";
goto Lemitint;
case OP_RAVI_LE_II:
oper = "<=";
/* no break */
case OP_RAVI_EQ_II:
Lemitint:
if (ISK(B) && ISK(C)) {
TValue *Konst1 = &fn->p->k[INDEXK(B)];
TValue *Konst2 = &fn->p->k[INDEXK(C)];
membuff_add_fstring(&fn->body, "result = (%lld %s %lld);\n", Konst1->value_.i, oper, Konst2->value_.i);
}
else if (ISK(B)) {
TValue *Konst1 = &fn->p->k[INDEXK(B)];
emit_reg_or_k(fn, "rc", C);
membuff_add_fstring(&fn->body, "result = (%lld %s ivalue(rc));\n", Konst1->value_.i, oper);
}
else if (ISK(C)) {
TValue *Konst2 = &fn->p->k[INDEXK(C)];
emit_reg_or_k(fn, "rb", B);
membuff_add_fstring(&fn->body, "result = (ivalue(rb) %s %lld);\n", oper, Konst2->value_.i);
}
else {
emit_reg_or_k(fn, "rb", B);
emit_reg_or_k(fn, "rc", C);
membuff_add_fstring(&fn->body, "result = (ivalue(rb) %s ivalue(rc));\n", oper);
}
break;
case OP_RAVI_LT_FF:
oper = "<";
goto Lemitflt;
case OP_RAVI_LE_FF:
oper = "<=";
/* no break */
case OP_RAVI_EQ_FF:
Lemitflt:
if (ISK(B) && ISK(C)) {
TValue *Konst1 = &fn->p->k[INDEXK(B)];
TValue *Konst2 = &fn->p->k[INDEXK(C)];
membuff_add_fstring(&fn->body, "result = (%.17g %s %.17g);\n", Konst1->value_.n, oper, Konst2->value_.n);
}
else if (ISK(B)) {
TValue *Konst1 = &fn->p->k[INDEXK(B)];
emit_reg_or_k(fn, "rc", C);
membuff_add_fstring(&fn->body, "result = (%.17g %s fltvalue(rc));\n", Konst1->value_.n, oper);
}
else if (ISK(C)) {
TValue *Konst2 = &fn->p->k[INDEXK(C)];
emit_reg_or_k(fn, "rb", B);
membuff_add_fstring(&fn->body, "result = (fltvalue(rb) %s %.17g);\n", oper, Konst2->value_.n);
}
else {
emit_reg_or_k(fn, "rb", B);
emit_reg_or_k(fn, "rc", C);
membuff_add_fstring(&fn->body, "result = (fltvalue(rb) %s fltvalue(rc));\n", oper);
}
break;
case OP_LT:
oper = "<";
goto Lemit;
case OP_LE:
oper = "<=";
Lemit:
emit_reg_or_k(fn, "rb", B);
emit_reg_or_k(fn, "rc", C);
membuff_add_string(&fn->body, "if (ttisinteger(rb) && ttisinteger(rc))\n");
membuff_add_fstring(&fn->body, " result = (ivalue(rb) %s ivalue(rc));\n", oper);
membuff_add_string(&fn->body, "else {\n");
emit_update_savedpc(fn, pc);
membuff_add_fstring(&fn->body, " result = %s(L, rb, rc);\n", compfunc);
// Reload pointer to base as the call to luaV_equalobj() may
// have invoked a Lua function and as a result the stack may have
// been reallocated - so the previous base pointer could be stale
membuff_add_string(&fn->body, " base = ci->u.l.base;\n");
membuff_add_string(&fn->body, "}\n");
break;
default:
emit_reg_or_k(fn, "rb", B);
emit_reg_or_k(fn, "rc", C);
emit_update_savedpc(fn, pc);
membuff_add_fstring(&fn->body, "result = %s(L, rb, rc);\n", compfunc);
// Reload pointer to base as the call to luaV_equalobj() may
// have invoked a Lua function and as a result the stack may have
// been reallocated - so the previous base pointer could be stale
membuff_add_string(&fn->body, "base = ci->u.l.base;\n");
break;
}
membuff_add_fstring(&fn->body, "if (result == %d) {\n", A);
if (jA > 0) {
membuff_add_fstring(&fn->body, " ra = R(%d);\n", jA - 1);
membuff_add_string(&fn->body, " luaF_close(L, ra, LUA_OK);\n");
membuff_add_string(&fn->body, " base = ci->u.l.base;\n");
}
membuff_add_fstring(&fn->body, " goto Lbc_%d;\n", j);
membuff_add_string(&fn->body, "}\n");
}
static void emit_jump_label(struct function *fn, int pc) {
if (fn->jmps[pc]) {
char labelstmt[80];
snprintf(labelstmt, sizeof labelstmt, "Lbc_%d:\n", pc);
membuff_add_string(&fn->body, labelstmt);
}
}
static void emit_op_loadk(struct function *fn, int A, int Bx, int pc) {
emit_reg(fn, "ra", A);
(void)pc;
TValue *Konst = &fn->p->k[Bx];
switch (Konst->tt_) {
case LUA_TNUMINT:
membuff_add_fstring(&fn->body, "setivalue(ra, %lld);\n", Konst->value_.i);
break;
case LUA_TNUMFLT:
membuff_add_fstring(&fn->body, "setfltvalue(ra, %.17g);\n", Konst->value_.n);
break;
case LUA_TBOOLEAN:
membuff_add_fstring(&fn->body, "setbvalue(ra, %d);\n", Konst->value_.b);
break;
default: {
membuff_add_fstring(&fn->body, "rb = K(%d);\n", Bx);
membuff_add_fstring(&fn->body, "setobj2s(L, ra, rb);\n");
break;
}
}
}
static void emit_op_return(struct function *fn, int A, int B, int pc) {
(void)pc;
membuff_add_string(&fn->body, "if (cl->p->sizep > 0) {\n luaF_close(L, base, LUA_OK);\n");
membuff_add_string(&fn->body, " base = ci->u.l.base;\n");
membuff_add_string(&fn->body, "}\n");
emit_reg(fn, "ra", A);
membuff_add_fstring(&fn->body, "result = (%d != 0 ? %d - 1 : cast_int(L->top - ra));\n", B, B);
membuff_add_string(&fn->body, "return luaD_poscall(L, ci, ra, result);\n");
}
static void emit_op_move(struct function *fn, int A, int B, int pc) {
(void)pc;
emit_reg(fn, "ra", A);
membuff_add_fstring(&fn->body, "rb = R(%d);\n", B);
membuff_add_fstring(&fn->body, "setobj2s(L, ra, rb);\n");
}
static void emit_op_loadnil(struct function *fn, int A, int B, int pc) {
(void)pc;
int b = B;
emit_reg(fn, "ra", A);
do {
membuff_add_fstring(&fn->body, "setnilvalue(ra++);\n");
} while (b--);
}
static void emit_op_jmp(struct function *fn, int A, int sBx, int pc) {
(void)pc;
if (A > 0) {
membuff_add_fstring(&fn->body, "ra = R(%d);\n", A - 1);
membuff_add_string(&fn->body, "luaF_close(L, ra, LUA_OK);\n");
membuff_add_string(&fn->body, "base = ci->u.l.base;\n");
}
membuff_add_fstring(&fn->body, "goto Lbc_%d;\n", sBx);
}
static void emit_op_loadbool(struct function *fn, int A, int B, int C, int j, int pc) {
(void)pc;
membuff_add_fstring(&fn->body, "ra = R(%d);\n", A);
membuff_add_fstring(&fn->body, "setbvalue(ra, %d);\n", B);
if (C) {
membuff_add_fstring(&fn->body, "goto Lbc_%d;\n", j);
}
}
static void emit_op_test(struct function *fn, int A, int B, int C, int j, int jA, int pc) {
(void)pc;
(void)B;
emit_reg(fn, "ra", A);
if (C) {
membuff_add_string(&fn->body, "result = l_isfalse(ra);\n");
}
else {
membuff_add_string(&fn->body, "result = !l_isfalse(ra);\n");
}
membuff_add_fstring(&fn->body, "if (!result) {\n", A);
if (jA > 0) {
membuff_add_fstring(&fn->body, " ra = R(%d);\n", jA - 1);
membuff_add_string(&fn->body, " luaF_close(L, ra, LUA_OK);\n");
membuff_add_string(&fn->body, " base = ci->u.l.base;\n");
}
membuff_add_fstring(&fn->body, " goto Lbc_%d;\n", j);
membuff_add_string(&fn->body, " }\n");
}
static void emit_op_testset(struct function *fn, int A, int B, int C, int j, int jA, int pc) {
(void)pc;
membuff_add_fstring(&fn->body, "rb = R(%d);\n", B);
if (C) {
membuff_add_string(&fn->body, "result = l_isfalse(rb);\n");
}
else {
membuff_add_string(&fn->body, "result = !l_isfalse(rb);\n");
}
membuff_add_fstring(&fn->body, "if (!result) {\n", A);
membuff_add_fstring(&fn->body, " ra = R(%d);\n", A);
membuff_add_string(&fn->body, " setobjs2s(L, ra, rb);");
if (jA > 0) {
membuff_add_fstring(&fn->body, " ra = R(%d);\n", jA - 1);
membuff_add_string(&fn->body, " luaF_close(L, ra, LUA_OK);\n");
membuff_add_string(&fn->body, " base = ci->u.l.base;\n");
}
membuff_add_fstring(&fn->body, " goto Lbc_%d;\n", j);
membuff_add_string(&fn->body, " }\n");
}
static void emit_endf(struct function *fn) {
membuff_add_string(&fn->body, "Lraise_error:\n");
membuff_add_string(&fn->body, "raviV_raise_error(L, error_code); /* does not return */\n");
membuff_add_string(&fn->body, "return 0;\n");
membuff_add_string(&fn->body, "}\n");
}
static void initfn(struct function *fn, struct lua_State *L, struct Proto *p, const char *fname,
struct ravi_compile_options_t *options) {
fn->L = L;
fn->p = p;
fn->var = 0;
fn->options = options;
snprintf(fn->fname, sizeof fn->fname, "%s", fname);
fn->jmps = calloc(p->sizecode, sizeof fn->jmps[0]);
if (p->maxstacksize)
fn->locals = calloc(p->maxstacksize, sizeof fn->locals[0]);
else
fn->locals = NULL;
membuff_init(&fn->prologue, strlen(Lua_header) + 4096);
membuff_init(&fn->body, 4096);
if (!(options->codegen_type == RAVI_CODEGEN_FUNCTION_ONLY))
membuff_add_string(&fn->prologue, Lua_header);
membuff_add_fstring(&fn->prologue, "extern int %s(lua_State *L);\n", fn->fname);
membuff_add_fstring(&fn->prologue, "int %s(lua_State *L) {\n", fn->fname);
membuff_add_string(&fn->prologue, "int error_code = 0;\n");
membuff_add_string(&fn->prologue, "lua_Integer i = 0;\n");
membuff_add_string(&fn->prologue, "lua_Integer ic = 0;\n");
membuff_add_string(&fn->prologue, "lua_Number n = 0.0;\n");
membuff_add_string(&fn->prologue, "lua_Number nc = 0.0;\n");
membuff_add_string(&fn->prologue, "int result = 0;\n");
membuff_add_string(&fn->prologue, "StkId ra = NULL;\n");
membuff_add_string(&fn->prologue, "StkId rb = NULL;\n");
membuff_add_string(&fn->prologue, "StkId rc = NULL;\n");
membuff_add_string(&fn->prologue, "lua_Unsigned ukey = 0;\n");
membuff_add_string(&fn->prologue, "lua_Integer *iptr = NULL;\n");
membuff_add_string(&fn->prologue, "lua_Number *nptr = NULL;\n");
membuff_add_string(&fn->prologue, "RaviArray *t = NULL;\n");
// TODO we never set this???
// ci->callstatus |= CIST_FRESH; /* fresh invocation of 'luaV_execute" */
// lua_assert(ci == L->ci);
membuff_add_string(&fn->prologue, "CallInfo *ci = L->ci;\n");
membuff_add_string(&fn->prologue, "LClosure *cl = clLvalue(ci->func);\n");
membuff_add_string(&fn->prologue, "TValue *k = cl->p->k;\n");
membuff_add_string(&fn->prologue, "StkId base = ci->u.l.base;\n");
}
// Handle OP_CALL
// Note that Lua assumes that functions called via OP_CALL
// are Lua functions and secondly that once OP_CALL completes the
// current function will continue within the same luaV_execute()
// call. However in a JIT case each JIT function is a different call
// so we need to take care of the behaviour differences between
// OP_CALL and external calls
static void emit_op_call(struct function *fn, int A, int B, int C, int pc) {
int nresults = C - 1;
if (B != 0) {
membuff_add_fstring(&fn->body, "L->top = R(%d);\n", A + B);
}
emit_reg(fn, "ra", A);
emit_update_savedpc(fn, pc);
membuff_add_fstring(&fn->body, "result = luaD_precall(L, ra, %d, 1);\n", nresults);
membuff_add_string(&fn->body, "if (result) {\n");
membuff_add_fstring(&fn->body, " if (result == 1 && %d >= 0)\n", nresults);
membuff_add_string(&fn->body, " L->top = ci->top;\n");
membuff_add_string(&fn->body, "}\n");
membuff_add_string(&fn->body, "else { /* Lua function */\n");
membuff_add_string(&fn->body, " result = luaV_execute(L);\n");
membuff_add_string(&fn->body, " if (result) L->top = ci->top;\n");
membuff_add_string(&fn->body, "}\n");
membuff_add_string(&fn->body, "base = ci->u.l.base;\n");
}
// R(A) := UpValue[B][RK(C)]
static void emit_op_gettabup(struct function *fn, int A, int B, int C, int pc, OpCode op) {
emit_reg(fn, "ra", A);
emit_reg_or_k(fn, "rc", C);
emit_update_savedpc(fn, pc);
if (op == OP_RAVI_GETTABUP_SK) {
membuff_add_fstring(&fn->body, "raviV_gettable_sskey(L, cl->upvals[%d]->v, rc, ra);\n", B);
}
else {
membuff_add_fstring(&fn->body, "luaV_gettable(L, cl->upvals[%d]->v, rc, ra);\n", B);
}
membuff_add_string(&fn->body, "base = ci->u.l.base;\n");
}
// R(A) := UpValue[B][RK(C)]
static void emit_op_settabup(struct function *fn, int A, int B, int C, int pc) {
emit_reg_or_k(fn, "rb", B);
emit_reg_or_k(fn, "rc", C);
emit_update_savedpc(fn, pc);
membuff_add_fstring(&fn->body, "luaV_settable(L, cl->upvals[%d]->v, rb, rc);\n", A);
membuff_add_string(&fn->body, "base = ci->u.l.base;\n");
}
// R(A) := R(B)[RK(C)]
static void emit_op_gettable(struct function *fn, int A, int B, int C, int pc, OpCode op) {
(void)pc;
emit_reg(fn, "ra", A);
emit_reg(fn, "rb", B);
emit_reg_or_k(fn, "rc", C);
emit_update_savedpc(fn, pc);
if (op == OP_RAVI_GETFIELD) {
membuff_add_string(&fn->body, "raviV_gettable_sskey(L, rb, rc, ra);\n");
}
else if (op == OP_RAVI_GETI) {
membuff_add_string(&fn->body, "raviV_gettable_i(L, rb, rc, ra);\n");
}
else {
membuff_add_string(&fn->body, "luaV_gettable(L, rb, rc, ra);\n");
}
membuff_add_string(&fn->body, "base = ci->u.l.base;\n");
}
static void emit_op_self(struct function *fn, int A, int B, int C, int pc, OpCode op) {
emit_reg(fn, "ra", A);
emit_reg(fn, "rb", B);
membuff_add_string(&fn->body, "setobjs2s(L, ra + 1, rb);\n");
emit_reg_or_k(fn, "rc", C);
emit_update_savedpc(fn, pc);
if (op == OP_RAVI_SELF_SK) {
membuff_add_string(&fn->body, "raviV_gettable_sskey(L, rb, rc, ra);\n");
}
else {
membuff_add_string(&fn->body, "luaV_gettable(L, rb, rc, ra);\n");
}
membuff_add_string(&fn->body, "base = ci->u.l.base;\n");
}
static void emit_op_len(struct function *fn, int A, int B, int pc) {
emit_reg(fn, "ra", A);
emit_reg(fn, "rb", B);
emit_update_savedpc(fn, pc);
membuff_add_string(&fn->body, "luaV_objlen(L, ra, rb);\n");
membuff_add_string(&fn->body, "base = ci->u.l.base;\n");
}
// R(A)[RK(B)] := RK(C)
static void emit_op_settable(struct function *fn, int A, int B, int C, int pc, OpCode op) {
emit_reg(fn, "ra", A);
emit_reg_or_k(fn, "rb", B);
emit_reg_or_k(fn, "rc", C);
emit_update_savedpc(fn, pc);
if (op == OP_RAVI_SETFIELD) {
membuff_add_string(&fn->body, "raviV_settable_sskey(L, ra, rb, rc);\n");
}
else if (op == OP_RAVI_SETI) {
membuff_add_string(&fn->body, "raviV_settable_i(L, ra, rb, rc);\n");
}
else {
membuff_add_fstring(&fn->body, "luaV_settable(L, ra, rb, rc);\n", B);
}
membuff_add_string(&fn->body, "base = ci->u.l.base;\n");
}
// R(A) := UpValue[B]
static void emit_op_getupval(struct function *fn, int A, int B, int pc) {
(void)pc;
emit_reg(fn, "ra", A);
membuff_add_fstring(&fn->body, "setobjs2s(L, ra, cl->upvals[%d]->v);\n", B);
}
static void emit_op_newtable(struct function *fn, int A, int B, int C, int pc) {
(void)pc;
emit_reg(fn, "ra", A);
membuff_add_fstring(&fn->body, "raviV_op_newtable(L, ci, ra, %d, %d);\n", B, C);
membuff_add_string(&fn->body, "base = ci->u.l.base;\n");
}
static void emit_op_newarrayint(struct function *fn, int A, int pc) {
(void)pc;
emit_reg(fn, "ra", A);
membuff_add_string(&fn->body, "raviV_op_newarrayint(L, ci, ra);\n");
membuff_add_string(&fn->body, "base = ci->u.l.base;\n");
}
static void emit_op_newarrayfloat(struct function *fn, int A, int pc) {
(void)pc;
emit_reg(fn, "ra", A);
membuff_add_string(&fn->body, "raviV_op_newarrayfloat(L, ci, ra);\n");
membuff_add_string(&fn->body, "base = ci->u.l.base;\n");
}
// Default implementation for binary ops
// FIXME replace with more specialized ops
static void emit_binary_op(struct function *fn, int A, int B, int C, OpCode op, int pc) {
emit_reg(fn, "ra", A);
emit_reg_or_k(fn, "rb", B);
emit_reg_or_k(fn, "rc", C);
emit_update_savedpc(fn, pc);
membuff_add_fstring(&fn->body, "luaO_arith(L, %d, rb, rc, ra);\n", cast_int(op - OP_ADD));
membuff_add_string(&fn->body, "base = ci->u.l.base;\n");
}
void emit_ff_op(struct function *fn, int A, int B, int C, int pc, const char *op) {
(void)pc;
emit_reg(fn, "ra", A);
if (ISK(B)) {
TValue *Konst1 = &fn->p->k[INDEXK(B)];
emit_reg_or_k(fn, "rc", C);
membuff_add_fstring(&fn->body, "setfltvalue(ra, %.17g %s fltvalue(rc));\n", Konst1->value_.n, op);
}
else if (ISK(C)) {
TValue *Konst1 = &fn->p->k[INDEXK(C)];
emit_reg_or_k(fn, "rb", B);
membuff_add_fstring(&fn->body, "setfltvalue(ra, fltvalue(rb) %s %.17g);\n", op, Konst1->value_.n);
}
else {
emit_reg_or_k(fn, "rb", B);
emit_reg_or_k(fn, "rc", C);
membuff_add_fstring(&fn->body, "setfltvalue(ra, fltvalue(rb) %s fltvalue(rc));\n", op);
}
}
static void emit_fi_op(struct function *fn, int A, int B, int C, int pc, const char *op) {
(void)pc;
emit_reg(fn, "ra", A);
emit_reg_or_k(fn, "rb", B);
emit_reg_or_k(fn, "rc", C);
membuff_add_fstring(&fn->body, "setfltvalue(ra, fltvalue(rb) %s ivalue(rc));\n", op);
}
static void emit_if_op(struct function *fn, int A, int B, int C, int pc, const char *op) {
(void)pc;
emit_reg(fn, "ra", A);
emit_reg_or_k(fn, "rb", B);
emit_reg_or_k(fn, "rc", C);
membuff_add_fstring(&fn->body, "setfltvalue(ra, ivalue(rb) %s fltvalue(rc));\n", op);
}
static void emit_ii_op(struct function *fn, int A, int B, int C, int pc, const char *op) {
(void)pc;
emit_reg(fn, "ra", A);
if (ISK(B)) {
TValue *Konst1 = &fn->p->k[INDEXK(B)];
emit_reg_or_k(fn, "rc", C);
membuff_add_fstring(&fn->body, "setivalue(ra, %lld %s ivalue(rc));\n", Konst1->value_.i, op);
}
else if (ISK(C)) {
TValue *Konst1 = &fn->p->k[INDEXK(C)];
emit_reg_or_k(fn, "rb", B);
membuff_add_fstring(&fn->body, "setivalue(ra, ivalue(rb) %s %lld);\n", op, Konst1->value_.i);
}
else {
emit_reg_or_k(fn, "rb", B);
emit_reg_or_k(fn, "rc", C);
membuff_add_fstring(&fn->body, "setivalue(ra, ivalue(rb) %s ivalue(rc));\n", op);
}
}
static void emit_op_divii(struct function *fn, int A, int B, int C, int pc) {
(void)pc;
emit_reg(fn, "ra", A);
emit_reg_or_k(fn, "rb", B);
emit_reg_or_k(fn, "rc", C);
membuff_add_string(&fn->body,
"setfltvalue(ra, (lua_Number)(ivalue(rb)) / "
"(lua_Number)(ivalue(rc)));\n");
}
static void emit_op_defer(struct function *fn, int A, int pc) {
(void)pc;
emit_reg(fn, "ra", A);
membuff_add_string(&fn->body, "raviV_op_defer(L, ra);\n");
}
static void emit_op_loadfz(struct function *fn, int A, int pc) {
(void)pc;
emit_reg(fn, "ra", A);
membuff_add_string(&fn->body, "setfltvalue(ra, 0.0);\n");
}
static void emit_op_loadiz(struct function *fn, int A, int pc) {
(void)pc;
emit_reg(fn, "ra", A);
membuff_add_string(&fn->body, "setivalue(ra, 0);\n");
}
static void emit_op_movei(struct function *fn, int A, int B, int pc) {
(void)pc;
emit_reg(fn, "ra", A);
emit_reg(fn, "rb", B);
membuff_add_string(&fn->body, "i = 0;\n");
membuff_add_string(&fn->body, "if (ttisinteger(rb)) { i = ivalue(rb); setivalue(ra, i); }\n");
membuff_add_string(&fn->body, "else {\n");
#if GOTO_ON_ERROR
membuff_add_fstring(&fn->body, " error_code = %d;\n", Error_integer_expected);
membuff_add_string(&fn->body, " goto Lraise_error;\n");
#else
membuff_add_fstring(&fn->body, " raviV_raise_error(L, %d);\n", Error_integer_expected);
#endif
membuff_add_string(&fn->body, "}\n");
}
static void emit_op_movef(struct function *fn, int A, int B, int pc) {
(void)pc;
emit_reg(fn, "ra", A);
emit_reg(fn, "rb", B);
membuff_add_string(&fn->body, "n = 0.0;\n");
membuff_add_string(
&fn->body,
"if (ttisnumber(rb)) { n = (ttisfloat(rb) ? fltvalue(rb) : (double)ivalue(rb)); setfltvalue(ra, n); }\n");
membuff_add_string(&fn->body, "else {\n");
#if GOTO_ON_ERROR
membuff_add_fstring(&fn->body, " error_code = %d;\n", Error_number_expected);
membuff_add_string(&fn->body, " goto Lraise_error;\n");
#else
membuff_add_fstring(&fn->body, " raviV_raise_error(L, %d);\n", Error_number_expected);
#endif
membuff_add_string(&fn->body, "}\n");
}
static void emit_op_MOVEIARRAY(struct function *fn, int A, int B, int pc) {
(void)pc;
emit_reg(fn, "ra", A);
emit_reg(fn, "rb", B);
membuff_add_string(&fn->body, "if (ttisiarray(rb)) { setobjs2s(L, ra, rb); }\n");
membuff_add_string(&fn->body, "else {\n");
#if GOTO_ON_ERROR
membuff_add_fstring(&fn->body, " error_code = %d;\n", Error_integer_array_expected);
membuff_add_string(&fn->body, " goto Lraise_error;\n");
#else
membuff_add_fstring(&fn->body, " raviV_raise_error(L, %d);\n", Error_integer_array_expected);
#endif
membuff_add_string(&fn->body, "}\n");
}
static void emit_op_MOVEFARRAY(struct function *fn, int A, int B, int pc) {
(void)pc;
emit_reg(fn, "ra", A);
emit_reg(fn, "rb", B);
membuff_add_string(&fn->body, "if (ttisfarray(rb)) { setobjs2s(L, ra, rb); }\n");
membuff_add_string(&fn->body, "else {\n");
#if GOTO_ON_ERROR
membuff_add_fstring(&fn->body, " error_code = %d;\n", Error_number_array_expected);
membuff_add_string(&fn->body, " goto Lraise_error;\n");
#else
membuff_add_fstring(&fn->body, " raviV_raise_error(L, %d);\n", Error_number_array_expected);
#endif
membuff_add_string(&fn->body, "}\n");
}
static void emit_op_movetab(struct function *fn, int A, int B, int pc) {
(void)pc;
emit_reg(fn, "ra", A);
emit_reg(fn, "rb", B);
membuff_add_string(&fn->body, "if (ttisLtable(rb)) { setobjs2s(L, ra, rb); }\n");
membuff_add_string(&fn->body, "else {\n");
#if GOTO_ON_ERROR
membuff_add_fstring(&fn->body, " error_code = %d;\n", Error_table_expected);
membuff_add_string(&fn->body, " goto Lraise_error;\n");
#else
membuff_add_fstring(&fn->body, " raviV_raise_error(L, %d);\n", Error_table_expected);
#endif
membuff_add_string(&fn->body, "}\n");
}
static void emit_op_toint(struct function *fn, int A, int pc, int allow_nil) {
(void)pc;
emit_reg(fn, "ra", A);
membuff_add_string(&fn->body, "i = 0;\n");
if(allow_nil)
membuff_add_string(&fn->body, "if(!ttisnil(ra)) { \n");
membuff_add_string(&fn->body, "if (ttisinteger(ra)) {i = ivalue(ra); setivalue(ra, i); }\n");
membuff_add_string(&fn->body, "else {\n");
#if GOTO_ON_ERROR
membuff_add_fstring(&fn->body, " error_code = %d;\n", Error_integer_expected);
membuff_add_string(&fn->body, " goto Lraise_error;\n");
#else
membuff_add_fstring(&fn->body, " raviV_raise_error(L, %d);\n", Error_integer_expected);
#endif
membuff_add_string(&fn->body, "}\n");
if(allow_nil)
membuff_add_string(&fn->body, "}\n");
}
static void emit_op_toflt(struct function *fn, int A, int pc, int allow_nil) {
(void)pc;
emit_reg(fn, "ra", A);
membuff_add_string(&fn->body, "n = 0.0;\n");
if(allow_nil)
membuff_add_string(&fn->body, "if(!ttisnil(ra)) { \n");
membuff_add_string(&fn->body, "if (ttisnumber(ra)) { n = (ttisinteger(ra) ? (double) ivalue(ra) : fltvalue(ra)); setfltvalue(ra, n); }\n");
membuff_add_string(&fn->body, "else {\n");
#if GOTO_ON_ERROR
membuff_add_fstring(&fn->body, " error_code = %d;\n", Error_number_expected);
membuff_add_string(&fn->body, " goto Lraise_error;\n");
#else
membuff_add_fstring(&fn->body, " raviV_raise_error(L, %d);\n", Error_number_expected);
#endif
membuff_add_string(&fn->body, "}\n");
if(allow_nil)
membuff_add_string(&fn->body, "}\n");
}
static void emit_op_totab(struct function *fn, int A, int pc, int allow_nil) {
(void)pc;
emit_reg(fn, "ra", A);
if(allow_nil)
membuff_add_string(&fn->body, "if(!ttisnil(ra)) { \n");
membuff_add_string(&fn->body, "if (!ttisLtable(ra)) {\n");
#if GOTO_ON_ERROR
membuff_add_fstring(&fn->body, " error_code = %d;\n", Error_table_expected);
membuff_add_string(&fn->body, " goto Lraise_error;\n");
#else
membuff_add_fstring(&fn->body, " raviV_raise_error(L, %d);\n", Error_table_expected);
#endif
membuff_add_string(&fn->body, "}\n");
if(allow_nil)
membuff_add_string(&fn->body, "}\n");
}
static void emit_op_toclosure(struct function *fn, int A, int pc, int allow_nil) {
(void)pc;
emit_reg(fn, "ra", A);
if(allow_nil)
membuff_add_string(&fn->body, "if(!ttisnil(ra)) { \n");
membuff_add_string(&fn->body, "if (!ttisclosure(ra)) {\n");
#if GOTO_ON_ERROR
membuff_add_fstring(&fn->body, " error_code = %d;\n", Error_closure_expected);
membuff_add_string(&fn->body, " goto Lraise_error;\n");
#else
membuff_add_fstring(&fn->body, " raviV_raise_error(L, %d);\n", Error_closure_expected);
#endif
membuff_add_string(&fn->body, "}\n");
if(allow_nil)
membuff_add_string(&fn->body, "}\n");
}
static void emit_op_tostring(struct function *fn, int A, int pc, int allow_nil) {
(void)pc;
emit_reg(fn, "ra", A);
if(allow_nil)
membuff_add_string(&fn->body, "if(!ttisnil(ra)) { \n");
membuff_add_string(&fn->body, "if (!ttisstring(ra)) {\n");
#if GOTO_ON_ERROR
membuff_add_fstring(&fn->body, " error_code = %d;\n", Error_string_expected);
membuff_add_string(&fn->body, " goto Lraise_error;\n");
#else
membuff_add_fstring(&fn->body, " raviV_raise_error(L, %d);\n", Error_string_expected);
#endif
membuff_add_string(&fn->body, "}\n");
if(allow_nil)
membuff_add_string(&fn->body, "}\n");
}
static void emit_op_totype(struct function *fn, int A, int Bx, int pc, int allow_nil) {
(void)pc;
emit_reg(fn, "ra", A);
if(allow_nil)
membuff_add_string(&fn->body, "if(!ttisnil(ra)) { \n");
membuff_add_fstring(&fn->body, " rb = K(%d);\n", Bx);
membuff_add_string(&fn->body, " if (!ttisshrstring(rb) || !raviV_check_usertype(L, tsvalue(rb), ra)) {\n");
#if 0 // GOTO_ON_ERROR
membuff_add_fstring(&fn->body, " error_code = %d;\n", Error_type_mismatch);
membuff_add_string(&fn->body, " goto Lraise_error;\n");
#else
membuff_add_fstring(&fn->body, " raviV_raise_error_with_info(L, %d, getstr(tsvalue(rb)));\n", Error_type_mismatch);
#endif
membuff_add_string(&fn->body, " }\n");
if(allow_nil)
membuff_add_string(&fn->body, "}\n");
}
static void emit_op_toai(struct function *fn, int A, int pc) {
(void)pc;
emit_reg(fn, "ra", A);
membuff_add_string(&fn->body, "if (!ttisiarray(ra)) {\n");
#if GOTO_ON_ERROR
membuff_add_fstring(&fn->body, " error_code = %d;\n", Error_integer_array_expected);
membuff_add_string(&fn->body, " goto Lraise_error;\n");
#else
membuff_add_fstring(&fn->body, " raviV_raise_error(L, %d);\n", Error_integer_array_expected);
#endif
membuff_add_string(&fn->body, "}\n");
}
static void emit_op_toaf(struct function *fn, int A, int pc) {
(void)pc;
emit_reg(fn, "ra", A);
membuff_add_string(&fn->body, "if (!ttisfarray(ra)) {\n");
#if GOTO_ON_ERROR
membuff_add_fstring(&fn->body, " error_code = %d;\n", Error_number_array_expected);
membuff_add_string(&fn->body, " goto Lraise_error;\n");
#else
membuff_add_fstring(&fn->body, " raviV_raise_error(L, %d);\n", Error_number_array_expected);
#endif
membuff_add_string(&fn->body, "}\n");
}
static void emit_op_setlist(struct function *fn, int A, int B, int C, int pc) {
(void)pc;
emit_reg(fn, "ra", A);
membuff_add_fstring(&fn->body, "raviV_op_setlist(L, ci, ra, %d, %d);\n", B, C);
}
static void emit_op_concat(struct function *fn, int A, int B, int C, int pc) {
emit_update_savedpc(fn, pc);
membuff_add_fstring(&fn->body, "raviV_op_concat(L, ci, %d, %d, %d);\n", A, B, C);
membuff_add_string(&fn->body, "base = ci->u.l.base;\n");
}
static void emit_op_closure(struct function *fn, int A, int Bx, int pc) {
(void)pc;
membuff_add_fstring(&fn->body, "raviV_op_closure(L, ci, cl, %d, %d);\n", A, Bx);
membuff_add_string(&fn->body, "base = ci->u.l.base;\n");
}
static void emit_op_vararg(struct function *fn, int A, int B, int pc) {
(void)pc;
membuff_add_fstring(&fn->body, "raviV_op_vararg(L, ci, cl, %d, %d);\n", A, B);
membuff_add_string(&fn->body, "base = ci->u.l.base;\n");
}
static void emit_op_not(struct function *fn, int A, int B, int pc) {
(void)pc;
emit_reg(fn, "ra", A);
emit_reg(fn, "rb", B);
membuff_add_string(&fn->body, "result = l_isfalse(rb);\n");
membuff_add_string(&fn->body, "setbvalue(ra, result);\n");
}
static void emit_op_setupval(struct function *fn, int A, int B, int pc, const char *suffix) {
(void)pc;
emit_reg(fn, "ra", A);
membuff_add_fstring(&fn->body, "raviV_op_setupval%s(L, cl, ra, %d);\n", suffix, B);
}
static void emit_op_iforprep(struct function *fn, int A, int pc, int step_one, int pc1) {
(void)pc1;
if (!fn->locals[A]) {
fn->locals[A] = 1; // Lua can reuse the same forloop vars if loop isn't nested
// Although in IFOR instructions we do not need all the vars below
// it can happen that the same slot is used by normal FOR loop
// The optimizer should get rid of unused vars anyway
membuff_add_fstring(&fn->prologue, "lua_Integer i_%d = 0;\n", A);
membuff_add_fstring(&fn->prologue, "lua_Integer limit_%d = 0;\n", A);
membuff_add_fstring(&fn->prologue, "lua_Integer step_%d = 0;\n", A);
membuff_add_fstring(&fn->prologue, "lua_Number ninit_%d = 0.0;\n", A);
membuff_add_fstring(&fn->prologue, "lua_Number nlimit_%d = 0.0;\n", A);
membuff_add_fstring(&fn->prologue, "lua_Number nstep_%d = 0.0;\n", A);
membuff_add_fstring(&fn->prologue, "int intloop_%d = 0;\n", A);
}
emit_reg(fn, "ra", A);
membuff_add_fstring(&fn->body, "i_%d = ivalue(ra);\n", A);
membuff_add_fstring(&fn->body, "ra = R(%d);\n", A + 1);
membuff_add_fstring(&fn->body, "limit_%d = ivalue(ra);\n", A);
if (!step_one) {
membuff_add_fstring(&fn->body, "ra = R(%d);\n", A + 2);
membuff_add_fstring(&fn->body, "step_%d = ivalue(ra);\n", A);
membuff_add_fstring(&fn->body, "i_%d -= step_%d;\n", A, A);
}
else {
membuff_add_fstring(&fn->body, "i_%d -= 1;\n", A);
}
membuff_add_fstring(&fn->body, "goto Lbc_%d;\n", pc);
}
static void emit_op_forprep(struct function *fn, int A, int pc, int pc1) {
(void)pc1;
if (!fn->locals[A]) {
fn->locals[A] = 1; // Lua can reuse the same forloop vars if loop isn't nested
membuff_add_fstring(&fn->prologue, "lua_Integer i_%d = 0;\n", A);
membuff_add_fstring(&fn->prologue, "lua_Integer limit_%d = 0;\n", A);
membuff_add_fstring(&fn->prologue, "lua_Integer step_%d = 0;\n", A);
membuff_add_fstring(&fn->prologue, "lua_Number ninit_%d = 0.0;\n", A);
membuff_add_fstring(&fn->prologue, "lua_Number nlimit_%d = 0.0;\n", A);
membuff_add_fstring(&fn->prologue, "lua_Number nstep_%d = 0.0;\n", A);
membuff_add_fstring(&fn->prologue, "int intloop_%d = 0;\n", A);
}
emit_reg(fn, "ra", A); // init
membuff_add_string(&fn->body, "rb = ra+1; /*limit*/\n");
membuff_add_string(&fn->body, "rc = ra+2; /*step*/\n");
membuff_add_fstring(&fn->body, "if (ttisinteger(ra) && ttisinteger(rc) && ttisinteger(rb)) {\n", A);
membuff_add_fstring(&fn->body, " limit_%d = ivalue(rb);\n", A);
membuff_add_fstring(&fn->body, " i_%d = ivalue(ra);\n", A);
membuff_add_fstring(&fn->body, " step_%d = ivalue(rc);\n", A);
membuff_add_fstring(&fn->body, " i_%d -= step_%d;\n", A, A);
membuff_add_fstring(&fn->body, " intloop_%d = 1;\n", A);
membuff_add_string(&fn->body, "}\n");
membuff_add_string(&fn->body, "else {\n");
membuff_add_fstring(&fn->body, " if (!ttisnumber(rb)) {\n", A);
#if GOTO_ON_ERROR
// membuff_add_string(&fn->body, " ravi_dump_value(L, rb);\n");
membuff_add_fstring(&fn->body, " error_code = %d;\n", Error_for_limit_must_be_number);
membuff_add_string(&fn->body, " goto Lraise_error;\n");
#else
membuff_add_fstring(&fn->body, " raviV_raise_error(L, %d);\n", Error_for_limit_must_be_number);
#endif
membuff_add_string(&fn->body, " }\n");
membuff_add_fstring(&fn->body, " if (!ttisnumber(rc)) {\n", A);
#if GOTO_ON_ERROR
membuff_add_fstring(&fn->body, " error_code = %d;\n", Error_for_step_must_be_number);
membuff_add_string(&fn->body, " goto Lraise_error;\n");
#else
membuff_add_fstring(&fn->body, " raviV_raise_error(L, %d);\n", Error_for_step_must_be_number);
#endif
membuff_add_string(&fn->body, " }\n");
membuff_add_fstring(&fn->body, " if (!ttisnumber(ra)) {\n", A);
#if GOTO_ON_ERROR
membuff_add_fstring(&fn->body, " error_code = %d;\n", Error_for_initial_value_must_be_number);
membuff_add_string(&fn->body, " goto Lraise_error;\n");
#else
membuff_add_fstring(&fn->body, " raviV_raise_error(L, %d);\n", Error_for_initial_value_must_be_number);
#endif
membuff_add_string(&fn->body, " }\n");
membuff_add_fstring(&fn->body, " tonumberns(rb, nlimit_%d);\n", A);
membuff_add_fstring(&fn->body, " tonumberns(ra, ninit_%d);\n", A);
membuff_add_fstring(&fn->body, " tonumberns(rc, nstep_%d);\n", A);
membuff_add_fstring(&fn->body, " ninit_%d -= nstep_%d;\n", A, A);
membuff_add_fstring(&fn->body, " intloop_%d = 0;\n", A);
membuff_add_string(&fn->body, "}\n");
membuff_add_fstring(&fn->body, "goto Lbc_%d;\n", pc);
}
static void emit_op_iforloop(struct function *fn, int A, int pc, int step_one, int pc1) {
(void)pc1;
if (!step_one) {
membuff_add_fstring(&fn->body, "i_%d += step_%d;\n", A, A);
}
else {
membuff_add_fstring(&fn->body, "i_%d += 1;\n", A);
}
membuff_add_fstring(&fn->body,
"if (i_%d <= limit_%d) {\n ra = R(%d);\n setivalue(ra, "
"i_%d);\n goto Lbc_%d;\n}\n",
A, A, A + 3, A, pc);
}
static void emit_op_forloop(struct function *fn, int A, int pc, int pc1) {
(void)pc1;
membuff_add_fstring(&fn->body, "if (intloop_%d) {\n", A);
membuff_add_fstring(&fn->body, " i_%d += step_%d;\n", A, A);
membuff_add_fstring(&fn->body, " if ((0 < step_%d) ? (i_%d <= limit_%d) : (limit_%d <= i_%d)) {\n", A, A, A, A, A);
membuff_add_fstring(&fn->body, " ra = R(%d);\n setivalue(ra, i_%d);\n goto Lbc_%d;\n", A + 3, A, pc);
membuff_add_string(&fn->body, " }\n");
membuff_add_string(&fn->body, "}\n");
membuff_add_string(&fn->body, "else {\n");
membuff_add_fstring(&fn->body, " ninit_%d += nstep_%d;\n", A, A);
membuff_add_fstring(&fn->body, " if ((0.0 < nstep_%d) ? (ninit_%d <= nlimit_%d) : (nlimit_%d <= ninit_%d)) {\n", A,
A, A, A, A);
membuff_add_fstring(&fn->body, " ra = R(%d);\n setfltvalue(ra, ninit_%d);\n goto Lbc_%d;\n", A + 3, A, pc);
membuff_add_string(&fn->body, " }\n");
membuff_add_string(&fn->body, "}\n");
}
static void emit_op_tforcall(struct function *fn, int A, int B, int C, int j, int jA, int pc) {
(void)B;
emit_update_savedpc(fn, pc);
emit_reg(fn, "ra", A);
membuff_add_string(&fn->body, "rb = ra + 3 + 2;\n"); /*rb = cb*/
membuff_add_string(&fn->body, "rc = ra + 2;\n"); /*rb = cb*/
membuff_add_string(&fn->body, "setobjs2s(L, rb, rc);\n");
membuff_add_string(&fn->body, "rb = ra + 3 + 1;\n"); /*rb = cb*/
membuff_add_string(&fn->body, "rc = ra + 1;\n"); /*rb = cb*/
membuff_add_string(&fn->body, "setobjs2s(L, rb, rc);\n");
membuff_add_string(&fn->body, "rb = ra + 3;\n"); /*rb = cb*/
membuff_add_string(&fn->body, "setobjs2s(L, rb, ra);\n");
membuff_add_string(&fn->body, "L->top = rb + 3;\n");
membuff_add_fstring(&fn->body, "luaD_call(L, rb, %d);\n", C);
membuff_add_string(&fn->body, "base = ci->u.l.base;\n");
membuff_add_string(&fn->body, "L->top = ci->top;\n");
emit_reg(fn, "ra", jA);
membuff_add_string(&fn->body, "if (!ttisnil(ra + 1)) {\n");
membuff_add_string(&fn->body, " rb = ra + 1;\n");
membuff_add_string(&fn->body, " setobjs2s(L, ra, rb);\n");
membuff_add_fstring(&fn->body, " goto Lbc_%d;\n", j);
membuff_add_string(&fn->body, "}\n");
}
static void emit_op_tforloop(struct function *fn, int A, int j, int pc) {
(void)pc;
emit_reg(fn, "ra", A);
membuff_add_string(&fn->body, "if (!ttisnil(ra + 1)) {\n");
membuff_add_string(&fn->body, " rb = ra + 1;\n");
membuff_add_string(&fn->body, " setobjs2s(L, ra, rb);\n");
membuff_add_fstring(&fn->body, " goto Lbc_%d;\n", j);
membuff_add_string(&fn->body, "}\n");
}
static void cleanup(struct function *fn) {
free(fn->jmps);
if (fn->locals)
free(fn->locals);
membuff_free(&fn->prologue);
membuff_free(&fn->body);
}
// Compile a Lua function
// Returns true if compilation was successful
bool raviJ_codegen(struct lua_State *L, struct Proto *p, struct ravi_compile_options_t *options, const char *fname,
membuff_t *buf) {
if (options->codegen_type == RAVI_CODEGEN_HEADER_ONLY) {
membuff_rewindpos(buf);
membuff_add_string(buf, Lua_header);
return true;
}
if (!raviJ_cancompile(p))
return false;
struct function fn;
initfn(&fn, L, p, fname, options);
scan_jump_targets(&fn);
const Instruction *code = p->code;
int pc, n = p->sizecode;
for (pc = 0; pc < n; pc++) {
emit_jump_label(&fn, 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_op_loadk(&fn, A, Bx, pc);
} break;
case OP_LOADKX: {
Instruction inst = code[++pc];
int Ax = GETARG_Ax(inst);
lua_assert(GET_OPCODE(inst) == OP_EXTRAARG);
emit_op_loadk(&fn, A, Ax, pc - 1);
} break;
case OP_LOADNIL: {
int B = GETARG_B(i);
emit_op_loadnil(&fn, A, B, pc);
} break;
case OP_RETURN: {
int B = GETARG_B(i);
emit_op_return(&fn, A, B, pc);
} break;
case OP_RAVI_FORPREP_I1:
case OP_RAVI_FORPREP_IP: {
int sbx = GETARG_sBx(i);
int j = sbx + pc + 1;
emit_op_iforprep(&fn, A, j, op == OP_RAVI_FORPREP_I1, pc);
} break;
case OP_RAVI_FORLOOP_I1:
case OP_RAVI_FORLOOP_IP: {
int sbx = GETARG_sBx(i);
int j = sbx + pc + 1;
emit_op_iforloop(&fn, A, j, op == OP_RAVI_FORLOOP_I1, pc);
} break;
case OP_FORPREP: {
int sbx = GETARG_sBx(i);
int j = sbx + pc + 1;
emit_op_forprep(&fn, A, j, pc);
} break;
case OP_FORLOOP: {
int sbx = GETARG_sBx(i);
int j = sbx + pc + 1;
emit_op_forloop(&fn, A, j, pc);
} 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_op_tforcall(&fn, A, B, C, j, GETARG_A(i), pc - 1);
} break;
case OP_TFORLOOP: {
int sbx = GETARG_sBx(i);
int j = sbx + pc + 1;
emit_op_tforloop(&fn, A, j, pc);
} break;
case OP_MOVE: {
int B = GETARG_B(i);
emit_op_move(&fn, A, B, pc);
} break;
case OP_JMP: {
int sbx = GETARG_sBx(i);
int j = sbx + pc + 1;
emit_op_jmp(&fn, A, j, pc);
} break;
case OP_LOADBOOL: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_op_loadbool(&fn, A, B, C, pc + 2, 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;
const char *comparison_function =
((op == OP_EQ || op == OP_RAVI_EQ_II || op == OP_RAVI_EQ_FF)
? "luaV_equalobj"
: ((op == OP_LT || op == OP_RAVI_LT_II || op == OP_RAVI_LT_FF) ? "luaV_lessthan" : "luaV_lessequal"));
// OP_EQ etc is followed by OP_JMP - we process this
// along with OP_EQ etc
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_comparison(&fn, A, B, C, j, GETARG_A(i), comparison_function, compOperator, pc - 1);
} break;
case OP_NOT: {
int B = GETARG_B(i);
emit_op_not(&fn, 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_TEST
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_op_test(&fn, 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_TESTSET
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_op_testset(&fn, A, B, C, j, GETARG_A(i), pc - 1);
} break;
case OP_RAVI_TABLE_GETFIELD:
case OP_RAVI_GETFIELD:
case OP_RAVI_GETI:
case OP_GETTABLE: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_op_gettable(&fn, A, B, C, pc, op);
} break;
case OP_RAVI_IARRAY_GET: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_IARRAY_GET(&fn, A, B, C, options->omit_array_get_range_check, pc);
} break;
case OP_RAVI_FARRAY_GET: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_FARRAY_GET(&fn, A, B, C, options->omit_array_get_range_check, pc);
} break;
case OP_RAVI_SETFIELD:
case OP_RAVI_TABLE_SETFIELD:
case OP_RAVI_SETI:
case OP_SETTABLE: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_op_settable(&fn, A, B, C, pc, op);
} break;
case OP_RAVI_IARRAY_SETI: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_IARRAY_SETI(&fn, A, B, C, true, pc);
} break;
case OP_RAVI_IARRAY_SET: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_IARRAY_SET(&fn, A, B, C, true, pc);
} break;
case OP_RAVI_FARRAY_SETF: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_FARRAY_SETF(&fn, A, B, C, true, pc);
} break;
case OP_RAVI_FARRAY_SET: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_FARRAY_SET(&fn, A, B, C, true, pc);
} break;
case OP_RAVI_GETTABUP_SK:
case OP_GETTABUP: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_op_gettabup(&fn, A, B, C, pc, op);
} break;
case OP_SETTABUP: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_op_settabup(&fn, A, B, C, pc);
} break;
case OP_TAILCALL:
case OP_CALL: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_op_call(&fn, A, B, C, pc);
} break;
case OP_GETUPVAL: {
int B = GETARG_B(i);
emit_op_getupval(&fn, A, B, pc);
} break;
case OP_SETUPVAL: {
int B = GETARG_B(i);
emit_op_setupval(&fn, A, B, pc, "");
} break;
case OP_RAVI_SETUPVALI: {
int B = GETARG_B(i);
emit_op_setupval(&fn, A, B, pc, "i");
} break;
case OP_RAVI_SETUPVALF: {
int B = GETARG_B(i);
emit_op_setupval(&fn, A, B, pc, "f");
} break;
case OP_RAVI_SETUPVAL_IARRAY: {
int B = GETARG_B(i);
emit_op_setupval(&fn, A, B, pc, "ai");
} break;
case OP_RAVI_SETUPVAL_FARRAY: {
int B = GETARG_B(i);
emit_op_setupval(&fn, A, B, pc, "af");
} break;
case OP_RAVI_SETUPVALT: {
int B = GETARG_B(i);
emit_op_setupval(&fn, A, B, pc, "t");
} break;
case OP_NEWTABLE: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_op_newtable(&fn, A, B, C, pc);
} break;
case OP_BNOT: {
int B = GETARG_B(i);
emit_op_bnot(&fn, A, B, pc);
} break;
case OP_ADD: {
int B = GETARG_B(i);
int C = GETARG_C(i);
if (options->inline_lua_arithmetic_operators) {
emit_op_arithslow(&fn, A, B, C, op, pc, "+", "TM_ADD");
}
else {
emit_binary_op(&fn, A, B, C, op, pc);
}
} break;
case OP_SUB: {
int B = GETARG_B(i);
int C = GETARG_C(i);
if (options->inline_lua_arithmetic_operators) {
emit_op_arithslow(&fn, A, B, C, op, pc, "-", "TM_SUB");
}
else {
emit_binary_op(&fn, A, B, C, op, pc);
}
} break;
case OP_MUL: {
int B = GETARG_B(i);
int C = GETARG_C(i);
if (options->inline_lua_arithmetic_operators) {
emit_op_arithslow(&fn, A, B, C, op, pc, "*", "TM_MUL");
}
else {
emit_binary_op(&fn, A, B, C, op, pc);
}
} break;
case OP_MOD:
case OP_POW:
case OP_DIV:
case OP_IDIV:
case OP_BAND:
case OP_BOR:
case OP_BXOR:
case OP_SHL:
case OP_SHR: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_binary_op(&fn, A, B, C, op, pc);
} break;
case OP_RAVI_ADDFF: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_ff_op(&fn, A, B, C, pc, "+");
} break;
case OP_RAVI_ADDFI: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_fi_op(&fn, A, B, C, pc, "+");
} break;
case OP_RAVI_ADDII: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_ii_op(&fn, A, B, C, pc, "+");
} break;
case OP_RAVI_SUBFF: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_ff_op(&fn, A, B, C, pc, "-");
} break;
case OP_RAVI_SUBFI: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_fi_op(&fn, A, B, C, pc, "-");
} break;
case OP_RAVI_SUBIF: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_if_op(&fn, A, B, C, pc, "-");
} break;
case OP_RAVI_SUBII: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_ii_op(&fn, A, B, C, pc, "-");
} break;
case OP_RAVI_MULFF: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_ff_op(&fn, A, B, C, pc, "*");
} break;
case OP_RAVI_MULFI: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_fi_op(&fn, A, B, C, pc, "*");
} break;
case OP_RAVI_MULII: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_ii_op(&fn, A, B, C, pc, "*");
} break;
case OP_RAVI_DIVFF: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_ff_op(&fn, A, B, C, pc, "/");
} break;
case OP_RAVI_DIVFI: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_fi_op(&fn, A, B, C, pc, "/");
} break;
case OP_RAVI_DIVIF: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_if_op(&fn, A, B, C, pc, "/");
} break;
case OP_RAVI_DIVII: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_op_divii(&fn, A, B, C, pc);
} break;
case OP_RAVI_LOADFZ: {
emit_op_loadfz(&fn, A, pc);
} break;
case OP_RAVI_LOADIZ: {
emit_op_loadiz(&fn, A, pc);
} break;
// mandatory types
case OP_RAVI_TOINT: {
emit_op_toint(&fn, A, pc, 0);
} break;
case OP_RAVI_TOFLT: {
emit_op_toflt(&fn, A, pc, 0);
} break;
case OP_RAVI_TOTAB: {
emit_op_totab(&fn, A, pc, 0);
} break;
case OP_RAVI_TOSTRING: {
emit_op_tostring(&fn, A, pc, 0);
} break;
case OP_RAVI_TOCLOSURE: {
emit_op_toclosure(&fn, A, pc, 0);
} break;
case OP_RAVI_TOTYPE: {
emit_op_totype(&fn, A, GETARG_Bx(i), pc, 0);
} break;
// optional types
case OP_RAVI_TOINT_NIL: {
emit_op_toint(&fn, A, pc, 1);
} break;
case OP_RAVI_TOFLT_NIL: {
emit_op_toflt(&fn, A, pc, 1);
} break;
case OP_RAVI_TOTAB_NIL: {
emit_op_totab(&fn, A, pc, 1);
} break;
case OP_RAVI_TOSTRING_NIL: {
emit_op_tostring(&fn, A, pc, 1);
} break;
case OP_RAVI_TOCLOSURE_NIL: {
emit_op_toclosure(&fn, A, pc, 1);
} break;
case OP_RAVI_TOTYPE_NIL: {
emit_op_totype(&fn, A, GETARG_Bx(i), pc, 1);
} break;
// tables
case OP_RAVI_TOIARRAY: {
emit_op_toai(&fn, A, pc);
} break;
case OP_RAVI_TOFARRAY: {
emit_op_toaf(&fn, A, pc);
} break;
case OP_RAVI_MOVEI: {
int B = GETARG_B(i);
emit_op_movei(&fn, A, B, pc);
} break;
case OP_RAVI_MOVEF: {
int B = GETARG_B(i);
emit_op_movef(&fn, A, B, pc);
} break;
case OP_RAVI_MOVEIARRAY: {
int B = GETARG_B(i);
emit_op_MOVEIARRAY(&fn, A, B, pc);
} break;
case OP_RAVI_MOVEFARRAY: {
int B = GETARG_B(i);
emit_op_MOVEFARRAY(&fn, A, B, pc);
} break;
case OP_RAVI_MOVETAB: {
int B = GETARG_B(i);
emit_op_movetab(&fn, A, B, pc);
} break;
case OP_RAVI_NEW_IARRAY: {
emit_op_newarrayint(&fn, A, pc);
} break;
case OP_RAVI_NEW_FARRAY: {
emit_op_newarrayfloat(&fn, A, pc);
} break;
case OP_CONCAT: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_op_concat(&fn, A, B, C, pc);
} break;
case OP_CLOSURE: {
int Bx = GETARG_Bx(i);
emit_op_closure(&fn, A, Bx, pc);
} break;
case OP_VARARG: {
int B = GETARG_B(i);
emit_op_vararg(&fn, A, B, 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_op_setlist(&fn, A, B, C, pc);
} break;
case OP_SELF:
case OP_RAVI_TABLE_SELF_SK:
case OP_RAVI_SELF_SK: {
int B = GETARG_B(i);
int C = GETARG_C(i);
emit_op_self(&fn, A, B, C, pc, op);
} break;
case OP_LEN: {
int B = GETARG_B(i);
emit_op_len(&fn, A, B, pc);
} break;
case OP_RAVI_DEFER: {
emit_op_defer(&fn, A, pc);
} break;
case OP_RAVI_SHR_II:
case OP_RAVI_SHL_II:
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_int_bitop(&fn, A, B, C, op, pc);
} break;
case OP_RAVI_BNOT_I: {
int B = GETARG_B(i);
emit_op_BNOT_I(&fn, A, B, pc);
} break;
case OP_UNM: {
int B = GETARG_B(i);
emit_op_unm(&fn, A, B, pc);
} break;
case OP_RAVI_UNMF: {
int B = GETARG_B(i);
emit_op_unmf(&fn, A, B, pc);
} break;
case OP_RAVI_UNMI: {
int B = GETARG_B(i);
emit_op_unmi(&fn, A, B, pc);
} break;
default:
abort();
}
}
emit_endf(&fn);
membuff_rewindpos(buf);
membuff_add_string(buf, fn.prologue.buf);
membuff_add_string(buf, fn.body.buf);
cleanup(&fn);
return true;
}
static const char *errortext[] = {"integer expected",
"number expected",
"integer[] expected",
"number[] expected",
"table expected",
"upvalue of integer type, cannot be set to non integer value",
"upvalue of number type, cannot be set to non number value",
"upvalue of integer[] type, cannot be set to non integer[] value",
"upvalue of number[] type, cannot be set to non number[] value",
"upvalue of table type, cannot be set to non table value",
"for llimit must be a number",
"for step must be a number",
"for initial value must be a number",
"array index is out of bounds",
"string expected",
"closure expected",
"type mismatch: wrong userdata type",
NULL};
void raviV_raise_error(lua_State *L, int errorcode) {
assert(errorcode >= 0 && errorcode < Error_type_mismatch);
luaG_runerror(L, errortext[errorcode]);
}
void raviV_raise_error_with_info(lua_State *L, int errorcode, const char *info) {
assert(errorcode == Error_type_mismatch);
luaG_runerror(L, "type mismatch: expected %s", info);
}