void luaK_setreturns (FuncState *fs, expdesc *e, int nresults) { if (e->k == VCALL) { /* expression is an open function call? */ SETARG_C(getcode(fs, e), nresults+1); } else if (e->k == VVARARG) { SETARG_B(getcode(fs, e), nresults+1); SETARG_A(getcode(fs, e), fs->freereg); luaK_reserveregs(fs, 1); } }
static int patchtestreg (FuncState *fs, int node, int reg) { Instruction *i = getjumpcontrol(fs, node); if (GET_OPCODE(*i) != OP_TESTSET) return 0; /* cannot patch other instructions */ if (reg != NO_REG && reg != GETARG_B(*i)) SETARG_A(*i, reg); else /* no register to put value or register already has the value */ *i = CREATE_ABC(OP_TEST, GETARG_B(*i), 0, GETARG_C(*i)); return 1; }
void luaK_patchclose (FuncState *fs, int list, int level) { level++; /* argument is +1 to reserve 0 as non-op */ while (list != NO_JUMP) { int next = getjump(fs, list); lua_assert(GET_OPCODE(fs->f->code[list]) == OP_JMP && (GETARG_A(fs->f->code[list]) == 0 || GETARG_A(fs->f->code[list]) >= level)); SETARG_A(fs->f->code[list], level); list = next; } }
/* ** Fix an expression to return the number of results 'nresults'. ** Either 'e' is a multi-ret expression (function call or vararg) ** or 'nresults' is LUA_MULTRET (as any expression can satisfy that). */ void luaK_setreturns (FuncState *fs, expdesc *e, int nresults) { if (e->k == VCALL) { /* expression is an open function call? */ SETARG_C(getinstruction(fs, e), nresults + 1); } else if (e->k == VVARARG) { Instruction *pc = &getinstruction(fs, e); SETARG_B(*pc, nresults + 1); SETARG_A(*pc, fs->freereg); luaK_reserveregs(fs, 1); } else lua_assert(nresults == LUA_MULTRET); }
void luaK_setreturns (FuncState *fs, expdesc *e, int nresults) { if (e->k == VCALL) { /* expression is an open function call? */ SETARG_C(getcode(fs, e), nresults+1); DEBUG_CODEGEN(raviY_printf(fs, "[%d]* %o ; set C to %d\n", e->u.info, getcode(fs,e), nresults+1)); } else if (e->k == VVARARG) { SETARG_B(getcode(fs, e), nresults+1); DEBUG_CODEGEN(raviY_printf(fs, "[%d]* %o ; set B to %d\n", e->u.info, getcode(fs,e), nresults + 1)); SETARG_A(getcode(fs, e), fs->freereg); DEBUG_CODEGEN(raviY_printf(fs, "[%d]* %o ; set A to %d\n", e->u.info, getcode(fs,e), fs->freereg)); luaK_reserveregs(fs, 1); } }
static void discharge2reg(FuncState *fs, expdesc *e, int reg) { luaK_dischargevars(fs, e); switch (e->k) { case VNIL: { luaK_nil(fs, reg, 1); break; } case VFALSE: case VTRUE: { luaK_codeABC(fs, OP_LOADBOOL, reg, e->k == VTRUE, 0); break; } case VK: { luaK_codek(fs, reg, e->u.info); break; } case VKNUM: { luaK_codek(fs, reg, luaK_numberK(fs, e->u.nval)); break; } case VRELOCABLE: { Instruction *pc = &getcode(fs, e); SETARG_A(*pc, reg); break; } case VNONRELOC: { if (reg != e->u.info) luaK_codeABC(fs, OP_MOVE, reg, e->u.info, 0); break; } default: { lua_assert(e->k == VVOID || e->k == VJMP); return; /* nothing to do... */ } } e->u.info = reg; e->k = VNONRELOC; }
/* Patch register of test instructions. */ static int patchtestreg (FuncState *fs, int node, int reg) { Instruction *i = getjumpcontrol(fs, node); if (GET_OPCODE(*i) != OP_TESTSET) return 0; /* cannot patch other instructions */ if (reg != NO_REG && reg != GETARG_B(*i)) { SETARG_A(*i, reg); DEBUG_CODEGEN(raviY_printf(fs, "[?]* %o ; set A to %d\n", *i, reg)); } else /* no register to put value or register already has the value */ { *i = CREATE_ABC(OP_TEST, GETARG_B(*i), 0, GETARG_C(*i)); DEBUG_CODEGEN(raviY_printf(fs, "[?]* %o ; generate OP_TEST\n", *i)); } return 1; }
static void discharge2reg(ktap_funcstate *fs, ktap_expdesc *e, int reg) { codegen_dischargevars(fs, e); switch (e->k) { case VNIL: { codegen_nil(fs, reg, 1); break; } case VFALSE: case VTRUE: { codegen_codeABC(fs, OP_LOADBOOL, reg, e->k == VTRUE, 0); break; } case VEVENT: codegen_codeABC(fs, OP_EVENT, reg, 0, 0); break; case VEVENTNAME: codegen_codeABC(fs, OP_EVENTNAME, reg, 0, 0); break; case VEVENTARG: codegen_codeABC(fs, OP_EVENTARG, reg, e->u.info, 0); break; case VK: { codegen_codek(fs, reg, e->u.info); break; } case VKNUM: { codegen_codek(fs, reg, codegen_numberK(fs, e->u.nval)); break; } case VRELOCABLE: { ktap_instruction *pc = &getcode(fs, e); SETARG_A(*pc, reg); break; } case VNONRELOC: { if (reg != e->u.info) codegen_codeABC(fs, OP_MOVE, reg, e->u.info, 0); break; } default: ktap_assert(e->k == VVOID || e->k == VJMP); return; /* nothing to do... */ } e->u.info = reg; e->k = VNONRELOC; }
/* ** Create a OP_LOADNIL instruction, but try to optimize: if the previous ** instruction is also OP_LOADNIL and ranges are compatible, adjust ** range of previous instruction instead of emitting a new one. (For ** instance, 'local a; local b' will generate a single opcode.) */ void luaK_nil (FuncState *fs, int from, int n) { Instruction *previous; int l = from + n - 1; /* last register to set nil */ if (fs->pc > fs->lasttarget) { /* no jumps to current position? */ previous = &fs->f->code[fs->pc-1]; if (GET_OPCODE(*previous) == OP_LOADNIL) { /* previous is LOADNIL? */ int pfrom = GETARG_A(*previous); /* get previous range */ int pl = pfrom + GETARG_B(*previous); if ((pfrom <= from && from <= pl + 1) || (from <= pfrom && pfrom <= l + 1)) { /* can connect both? */ if (pfrom < from) from = pfrom; /* from = min(from, pfrom) */ if (pl > l) l = pl; /* l = max(l, pl) */ SETARG_A(*previous, from); SETARG_B(*previous, l - from); return; } } /* else go through */ } luaK_codeABC(fs, OP_LOADNIL, from, n - 1, 0); /* else no optimization */ }
TrCallSite *TrVM::lookup(TrBlock *b, OBJ receiver, OBJ msg, TrInst *ip) { TrVM *vm = this; OBJ method = TrObject_method(this, receiver, msg); TrInst *boing = (ip - 1); /* TODO: do not prealloc TrCallSite here, every one is a memory leak and a new one is created on polymorphic calls. */ b->sites.emplace_back(); TrCallSite *s = &b->sites.back(); s->klass = TR_CLASS(receiver); s->miss = 0; s->method = method; s->message = msg; if (unlikely(method == TR_NIL)) { s->method = TrObject_method(this, receiver, tr_intern("method_missing")); s->method_missing = 1; } /* Implement Monomorphic method cache by replacing the previous instruction (BOING) w/ CACHE that uses the CallSite to find the method instead of doing a full lookup. */ if (GET_OPCODE(*boing) == TR_OP_CACHE) { /* Existing call site */ /* TODO: maybe take existing call site hit miss into consideration to replace it with this one. For now, we just don't replace it, the first one is always the cached one. */ } else { /* New call site, we cache it fo shizzly! */ SET_OPCODE(*boing, TR_OP_CACHE); SETARG_A(*boing, GETARG_A(*ip)); /* receiver register */ SETARG_B(*boing, 1); /* jmp */ SETARG_C(*boing, b->sites.size()-1); /* CallSite index */ } return s; }
/* Emit bytecode to set a range of registers to nil. */ void luaK_nil (FuncState *fs, int from, int n) { Instruction *previous; int l = from + n - 1; /* last register to set nil */ if (fs->pc > fs->lasttarget) { /* no jumps to current position? */ previous = &fs->f->code[fs->pc-1]; if (GET_OPCODE(*previous) == OP_LOADNIL) { /* Try to merge with the previous instruction. */ int pfrom = GETARG_A(*previous); int pl = pfrom + GETARG_B(*previous); if ((pfrom <= from && from <= pl + 1) || (from <= pfrom && pfrom <= l + 1)) { /* can connect both? */ if (pfrom < from) from = pfrom; /* from = min(from, pfrom) */ if (pl > l) l = pl; /* l = max(l, pl) */ SETARG_A(*previous, from); DEBUG_CODEGEN(raviY_printf(fs, "[%d]* %o ; set A to %d\n", fs->pc - 1, *previous, from)); SETARG_B(*previous, l - from); DEBUG_CODEGEN(raviY_printf(fs, "[%d]* %o ; set B to %d\n", fs->pc - 1, *previous, (l - from))); return; } } /* else go through */ } luaK_codeABC(fs, OP_LOADNIL, from, n - 1, 0); /* else no optimization */ }
static void patchtestreg (Instruction *i, int reg) { if (reg != NO_REG) SETARG_A(*i, reg); else /* no register to put value; change TESTSET to TEST */ *i = CREATE_ABC(OP_TEST, GETARG_B(*i), 0, GETARG_C(*i)); }
static void discharge2reg (FuncState *fs, expdesc *e, int reg) { luaK_dischargevars(fs, e); switch (e->k) { case VNIL: { luaK_nil(fs, reg, 1); break; } case VFALSE: case VTRUE: { luaK_codeABC(fs, OP_LOADBOOL, reg, e->k == VTRUE, 0); break; } case VK: { luaK_codek(fs, reg, e->u.info); break; } case VKFLT: { luaK_codek(fs, reg, luaK_numberK(fs, e->u.nval)); break; } case VKINT: { luaK_codek(fs, reg, luaK_intK(fs, e->u.ival)); break; } case VRELOCABLE: { Instruction *pc = &getcode(fs, e); SETARG_A(*pc, reg); DEBUG_EXPR(raviY_printf(fs, "discharge2reg (VRELOCABLE set arg A) %e\n", e)); DEBUG_CODEGEN(raviY_printf(fs, "[%d]* %o ; set A to %d\n", e->u.info, *pc, reg)); break; } case VNONRELOC: { if (reg != e->u.info) { /* code a MOVEI or MOVEF if the target register is a local typed variable */ int ravi_type = raviY_get_register_typeinfo(fs, reg); switch (ravi_type) { case RAVI_TNUMINT: luaK_codeABC(fs, OP_RAVI_MOVEI, reg, e->u.info, 0); break; case RAVI_TNUMFLT: luaK_codeABC(fs, OP_RAVI_MOVEF, reg, e->u.info, 0); break; case RAVI_TARRAYINT: luaK_codeABC(fs, OP_RAVI_MOVEAI, reg, e->u.info, 0); break; case RAVI_TARRAYFLT: luaK_codeABC(fs, OP_RAVI_MOVEAF, reg, e->u.info, 0); break; default: luaK_codeABC(fs, OP_MOVE, reg, e->u.info, 0); break; } } break; } default: { lua_assert(e->k == VVOID || e->k == VJMP); return; /* nothing to do... */ } } e->u.info = reg; e->k = VNONRELOC; }
static void patchtestreg (Instruction *i, int reg) { if (reg == NO_REG) reg = GETARG_B(*i); SETARG_A(*i, reg); }
OBJ TrCompiler_compile_node(VM, TrCompiler *c, TrBlock *b, TrNode *n, int reg) { if (!n) return TR_NIL; int start_reg = reg; REG(reg); b->line = n->line; /* TODO this shit is very repetitive, need to refactor */ switch (n->ntype) { case NODE_ROOT: case NODE_BLOCK: COMPILE_NODES(b, n->args[0], i, reg, 0); break; case NODE_VALUE: { int i = TrBlock_push_value(b, n->args[0]); PUSH_OP_ABx(b, LOADK, reg, i); } break; case NODE_STRING: { int i = TrBlock_push_string(b, TR_STR_PTR(n->args[0])); PUSH_OP_ABx(b, STRING, reg, i); } break; case NODE_ARRAY: { size_t size = 0; if (n->args[0]) { size = TR_ARRAY_SIZE(n->args[0]); /* compile args */ COMPILE_NODES(b, n->args[0], i, reg, i+1); ASSERT_NO_LOCAL_IN(Array); } PUSH_OP_AB(b, NEWARRAY, reg, size); } break; case NODE_HASH: { size_t size = 0; if (n->args[0]) { size = TR_ARRAY_SIZE(n->args[0]); /* compile args */ COMPILE_NODES(b, n->args[0], i, reg, i+1); ASSERT_NO_LOCAL_IN(Hash); } PUSH_OP_AB(b, NEWHASH, reg, size/2); } break; case NODE_RANGE: { COMPILE_NODE(b, n->args[0], reg); COMPILE_NODE(b, n->args[1], reg+1); REG(reg+1); ASSERT_NO_LOCAL_IN(Range); PUSH_OP_ABC(b, NEWRANGE, reg, reg+1, n->args[2]); } break; case NODE_ASSIGN: { OBJ name = n->args[0]; COMPILE_NODE(b, n->args[1], reg); if (TrBlock_find_upval_in_scope(b, name) != -1) { /* upval */ int i = TrBlock_push_upval(b, name); PUSH_OP_AB(b, SETUPVAL, reg, i); } else { /* local */ int i = TrBlock_push_local(b, name); TrInst *last_inst = &kv_A(b->code, kv_size(b->code) - 1); switch (GET_OPCODE(*last_inst)) { case TR_OP_ADD: /* Those instructions can load direcly into a local */ case TR_OP_SUB: case TR_OP_LT: case TR_OP_NEG: case TR_OP_NOT: SETARG_A(*last_inst, i); break; default: if (i != reg) PUSH_OP_AB(b, MOVE, i, reg); } } } break; case NODE_SETIVAR: COMPILE_NODE(b, n->args[1], reg); PUSH_OP_ABx(b, SETIVAR, reg, TrBlock_push_value(b, n->args[0])); break; case NODE_GETIVAR: PUSH_OP_ABx(b, GETIVAR, reg, TrBlock_push_value(b, n->args[0])); break; case NODE_SETCVAR: COMPILE_NODE(b, n->args[1], reg); PUSH_OP_ABx(b, SETCVAR, reg, TrBlock_push_value(b, n->args[0])); break; case NODE_GETCVAR: PUSH_OP_ABx(b, GETCVAR, reg, TrBlock_push_value(b, n->args[0])); break; case NODE_SETGLOBAL: COMPILE_NODE(b, n->args[1], reg); PUSH_OP_ABx(b, SETGLOBAL, reg, TrBlock_push_value(b, n->args[0])); break; case NODE_GETGLOBAL: PUSH_OP_ABx(b, GETGLOBAL, reg, TrBlock_push_value(b, n->args[0])); break; case NODE_SEND: /* can also be a variable access */ { TrNode *msg = (TrNode *)n->args[1]; OBJ name = msg->args[0]; assert(msg->ntype == NODE_MSG); int i; /* local */ if ((i = TrBlock_find_local(b, name)) != -1) { if (reg != i) PUSH_OP_AB(b, MOVE, reg, i); /* upval */ } else if (TrBlock_find_upval_in_scope(b, name) != -1) { i = TrBlock_push_upval(b, name); PUSH_OP_AB(b, GETUPVAL, reg, i); /* method call */ } else { /* receiver */ if (n->args[0]) COMPILE_NODE(b, n->args[0], reg); else PUSH_OP_A(b, SELF, reg); i = TrBlock_push_value(b, name); /* args */ size_t argc = 0; if (msg->args[1]) { argc = TR_ARRAY_SIZE(msg->args[1]) << 1; TR_ARRAY_EACH(msg->args[1], i, v, { TrNode *arg = (TrNode *)v; assert(arg->ntype == NODE_ARG); reg += COMPILE_NODE(b, arg->args[0], reg+i+2); if (arg->args[1]) argc |= 1; /* splat */ }); ASSERT_NO_LOCAL_IN(arguments); } /* block */ size_t blki = 0; TrBlock *blk = 0; if (n->args[2]) { blk = TrBlock_new(c, b); TrNode *blkn = (TrNode *)n->args[2]; blki = kv_size(b->blocks) + 1; blk->argc = 0; if (blkn->args[1]) { blk->argc = TR_ARRAY_SIZE(blkn->args[1]); /* add parameters as locals in block context */ TR_ARRAY_EACH(blkn->args[1], i, v, { TrNode *param = (TrNode *)v; TrBlock_push_local(blk, param->args[0]); });
static void invertjump (FuncState *fs, expdesc *e) { Instruction *pc = getjumpcontrol(fs, e->u.info); lua_assert(testTMode(GET_OPCODE(*pc)) && GET_OPCODE(*pc) != OP_TEST); SETARG_A(*pc, !(GETARG_A(*pc))); }