mirror of
https://github.com/lua/lua.git
synced 2025-01-14 05:43:00 +08:00
d6af81084d
String literal expressions have their own kind VKSTR, instead of the generic VK. This allows strings to "cross" functions without entering their constant tables (e.g., if they are used only by some nested function).
1778 lines
48 KiB
C
1778 lines
48 KiB
C
/*
|
|
** $Id: lcode.c $
|
|
** Code generator for Lua
|
|
** See Copyright Notice in lua.h
|
|
*/
|
|
|
|
#define lcode_c
|
|
#define LUA_CORE
|
|
|
|
#include "lprefix.h"
|
|
|
|
|
|
#include <limits.h>
|
|
#include <math.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "lua.h"
|
|
|
|
#include "lcode.h"
|
|
#include "ldebug.h"
|
|
#include "ldo.h"
|
|
#include "lgc.h"
|
|
#include "llex.h"
|
|
#include "lmem.h"
|
|
#include "lobject.h"
|
|
#include "lopcodes.h"
|
|
#include "lparser.h"
|
|
#include "lstring.h"
|
|
#include "ltable.h"
|
|
#include "lvm.h"
|
|
|
|
|
|
/* Maximum number of registers in a Lua function (must fit in 8 bits) */
|
|
#define MAXREGS 255
|
|
|
|
|
|
#define hasjumps(e) ((e)->t != (e)->f)
|
|
|
|
|
|
static int codesJ (FuncState *fs, OpCode o, int sj, int k);
|
|
|
|
|
|
|
|
/* semantic error */
|
|
l_noret luaK_semerror (LexState *ls, const char *msg) {
|
|
ls->t.token = 0; /* remove "near <token>" from final message */
|
|
luaX_syntaxerror(ls, msg);
|
|
}
|
|
|
|
|
|
/*
|
|
** If expression is a numeric constant, fills 'v' with its value
|
|
** and returns 1. Otherwise, returns 0.
|
|
*/
|
|
static int tonumeral (const expdesc *e, TValue *v) {
|
|
if (hasjumps(e))
|
|
return 0; /* not a numeral */
|
|
switch (e->k) {
|
|
case VKINT:
|
|
if (v) setivalue(v, e->u.ival);
|
|
return 1;
|
|
case VKFLT:
|
|
if (v) setfltvalue(v, e->u.nval);
|
|
return 1;
|
|
default: return 0;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
** If expression is a constant, fills 'v' with its value
|
|
** and returns 1. Otherwise, returns 0.
|
|
*/
|
|
int luaK_exp2const (FuncState *fs, const expdesc *e, TValue *v) {
|
|
if (hasjumps(e))
|
|
return 0; /* not a constant */
|
|
switch (e->k) {
|
|
case VFALSE: case VTRUE:
|
|
setbvalue(v, e->k == VTRUE);
|
|
return 1;
|
|
case VNIL:
|
|
setnilvalue(v);
|
|
return 1;
|
|
case VKSTR: {
|
|
setsvalue(fs->ls->L, v, e->u.strval);
|
|
return 1;
|
|
}
|
|
default: return tonumeral(e, v);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
** Return the previous instruction of the current code. If there
|
|
** may be a jump target between the current instruction and the
|
|
** previous one, return an invalid instruction (to avoid wrong
|
|
** optimizations).
|
|
*/
|
|
static Instruction *previousinstruction (FuncState *fs) {
|
|
static const Instruction invalidinstruction = -1;
|
|
if (fs->pc > fs->lasttarget)
|
|
return &fs->f->code[fs->pc - 1]; /* previous instruction */
|
|
else
|
|
return cast(Instruction*, &invalidinstruction);
|
|
}
|
|
|
|
|
|
/*
|
|
** 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) {
|
|
int l = from + n - 1; /* last register to set nil */
|
|
Instruction *previous = previousinstruction(fs);
|
|
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 */
|
|
}
|
|
|
|
|
|
/*
|
|
** Gets the destination address of a jump instruction. Used to traverse
|
|
** a list of jumps.
|
|
*/
|
|
static int getjump (FuncState *fs, int pc) {
|
|
int offset = GETARG_sJ(fs->f->code[pc]);
|
|
if (offset == NO_JUMP) /* point to itself represents end of list */
|
|
return NO_JUMP; /* end of list */
|
|
else
|
|
return (pc+1)+offset; /* turn offset into absolute position */
|
|
}
|
|
|
|
|
|
/*
|
|
** Fix jump instruction at position 'pc' to jump to 'dest'.
|
|
** (Jump addresses are relative in Lua)
|
|
*/
|
|
static void fixjump (FuncState *fs, int pc, int dest) {
|
|
Instruction *jmp = &fs->f->code[pc];
|
|
int offset = dest - (pc + 1);
|
|
lua_assert(dest != NO_JUMP);
|
|
if (!(-OFFSET_sJ <= offset && offset <= MAXARG_sJ - OFFSET_sJ))
|
|
luaX_syntaxerror(fs->ls, "control structure too long");
|
|
lua_assert(GET_OPCODE(*jmp) == OP_JMP);
|
|
SETARG_sJ(*jmp, offset);
|
|
}
|
|
|
|
|
|
/*
|
|
** Concatenate jump-list 'l2' into jump-list 'l1'
|
|
*/
|
|
void luaK_concat (FuncState *fs, int *l1, int l2) {
|
|
if (l2 == NO_JUMP) return; /* nothing to concatenate? */
|
|
else if (*l1 == NO_JUMP) /* no original list? */
|
|
*l1 = l2; /* 'l1' points to 'l2' */
|
|
else {
|
|
int list = *l1;
|
|
int next;
|
|
while ((next = getjump(fs, list)) != NO_JUMP) /* find last element */
|
|
list = next;
|
|
fixjump(fs, list, l2); /* last element links to 'l2' */
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
** Create a jump instruction and return its position, so its destination
|
|
** can be fixed later (with 'fixjump').
|
|
*/
|
|
int luaK_jump (FuncState *fs) {
|
|
return codesJ(fs, OP_JMP, NO_JUMP, 0);
|
|
}
|
|
|
|
|
|
/*
|
|
** Code a 'return' instruction
|
|
*/
|
|
void luaK_ret (FuncState *fs, int first, int nret) {
|
|
OpCode op;
|
|
switch (nret) {
|
|
case 0: op = OP_RETURN0; break;
|
|
case 1: op = OP_RETURN1; break;
|
|
default: op = OP_RETURN; break;
|
|
}
|
|
luaK_codeABC(fs, op, first, nret + 1, 0);
|
|
}
|
|
|
|
|
|
/*
|
|
** Code a "conditional jump", that is, a test or comparison opcode
|
|
** followed by a jump. Return jump position.
|
|
*/
|
|
static int condjump (FuncState *fs, OpCode op, int A, int B, int C, int k) {
|
|
luaK_codeABCk(fs, op, A, B, C, k);
|
|
return luaK_jump(fs);
|
|
}
|
|
|
|
|
|
/*
|
|
** returns current 'pc' and marks it as a jump target (to avoid wrong
|
|
** optimizations with consecutive instructions not in the same basic block).
|
|
*/
|
|
int luaK_getlabel (FuncState *fs) {
|
|
fs->lasttarget = fs->pc;
|
|
return fs->pc;
|
|
}
|
|
|
|
|
|
/*
|
|
** Returns the position of the instruction "controlling" a given
|
|
** jump (that is, its condition), or the jump itself if it is
|
|
** unconditional.
|
|
*/
|
|
static Instruction *getjumpcontrol (FuncState *fs, int pc) {
|
|
Instruction *pi = &fs->f->code[pc];
|
|
if (pc >= 1 && testTMode(GET_OPCODE(*(pi-1))))
|
|
return pi-1;
|
|
else
|
|
return pi;
|
|
}
|
|
|
|
|
|
/*
|
|
** Patch destination register for a TESTSET instruction.
|
|
** If instruction in position 'node' is not a TESTSET, return 0 ("fails").
|
|
** Otherwise, if 'reg' is not 'NO_REG', set it as the destination
|
|
** register. Otherwise, change instruction to a simple 'TEST' (produces
|
|
** no register value)
|
|
*/
|
|
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;
|
|
change instruction to simple test */
|
|
*i = CREATE_ABCk(OP_TEST, GETARG_B(*i), 0, 0, GETARG_k(*i));
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
|
|
/*
|
|
** Traverse a list of tests ensuring no one produces a value
|
|
*/
|
|
static void removevalues (FuncState *fs, int list) {
|
|
for (; list != NO_JUMP; list = getjump(fs, list))
|
|
patchtestreg(fs, list, NO_REG);
|
|
}
|
|
|
|
|
|
/*
|
|
** Traverse a list of tests, patching their destination address and
|
|
** registers: tests producing values jump to 'vtarget' (and put their
|
|
** values in 'reg'), other tests jump to 'dtarget'.
|
|
*/
|
|
static void patchlistaux (FuncState *fs, int list, int vtarget, int reg,
|
|
int dtarget) {
|
|
while (list != NO_JUMP) {
|
|
int next = getjump(fs, list);
|
|
if (patchtestreg(fs, list, reg))
|
|
fixjump(fs, list, vtarget);
|
|
else
|
|
fixjump(fs, list, dtarget); /* jump to default target */
|
|
list = next;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
** Path all jumps in 'list' to jump to 'target'.
|
|
** (The assert means that we cannot fix a jump to a forward address
|
|
** because we only know addresses once code is generated.)
|
|
*/
|
|
void luaK_patchlist (FuncState *fs, int list, int target) {
|
|
lua_assert(target <= fs->pc);
|
|
patchlistaux(fs, list, target, NO_REG, target);
|
|
}
|
|
|
|
|
|
void luaK_patchtohere (FuncState *fs, int list) {
|
|
int hr = luaK_getlabel(fs); /* mark "here" as a jump target */
|
|
luaK_patchlist(fs, list, hr);
|
|
}
|
|
|
|
|
|
/*
|
|
** MAXimum number of successive Instructions WiTHout ABSolute line
|
|
** information.
|
|
*/
|
|
#if !defined(MAXIWTHABS)
|
|
#define MAXIWTHABS 120
|
|
#endif
|
|
|
|
|
|
/* limit for difference between lines in relative line info. */
|
|
#define LIMLINEDIFF 0x80
|
|
|
|
|
|
/*
|
|
** Save line info for a new instruction. If difference from last line
|
|
** does not fit in a byte, of after that many instructions, save a new
|
|
** absolute line info; (in that case, the special value 'ABSLINEINFO'
|
|
** in 'lineinfo' signals the existence of this absolute information.)
|
|
** Otherwise, store the difference from last line in 'lineinfo'.
|
|
*/
|
|
static void savelineinfo (FuncState *fs, Proto *f, int line) {
|
|
int linedif = line - fs->previousline;
|
|
int pc = fs->pc - 1; /* last instruction coded */
|
|
if (abs(linedif) >= LIMLINEDIFF || fs->iwthabs++ > MAXIWTHABS) {
|
|
luaM_growvector(fs->ls->L, f->abslineinfo, fs->nabslineinfo,
|
|
f->sizeabslineinfo, AbsLineInfo, MAX_INT, "lines");
|
|
f->abslineinfo[fs->nabslineinfo].pc = pc;
|
|
f->abslineinfo[fs->nabslineinfo++].line = line;
|
|
linedif = ABSLINEINFO; /* signal that there is absolute information */
|
|
fs->iwthabs = 0; /* restart counter */
|
|
}
|
|
luaM_growvector(fs->ls->L, f->lineinfo, pc, f->sizelineinfo, ls_byte,
|
|
MAX_INT, "opcodes");
|
|
f->lineinfo[pc] = linedif;
|
|
fs->previousline = line; /* last line saved */
|
|
}
|
|
|
|
|
|
/*
|
|
** Remove line information from the last instruction.
|
|
** If line information for that instruction is absolute, set 'iwthabs'
|
|
** above its max to force the new (replacing) instruction to have
|
|
** absolute line info, too.
|
|
*/
|
|
static void removelastlineinfo (FuncState *fs) {
|
|
Proto *f = fs->f;
|
|
int pc = fs->pc - 1; /* last instruction coded */
|
|
if (f->lineinfo[pc] != ABSLINEINFO) { /* relative line info? */
|
|
fs->previousline -= f->lineinfo[pc]; /* last line saved */
|
|
fs->iwthabs--;
|
|
}
|
|
else { /* absolute line information */
|
|
fs->nabslineinfo--; /* remove it */
|
|
lua_assert(f->abslineinfo[fs->nabslineinfo].pc = pc);
|
|
fs->iwthabs = MAXIWTHABS + 1; /* force next line info to be absolute */
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
** Remove the last instruction created, correcting line information
|
|
** accordingly.
|
|
*/
|
|
static void removelastinstruction (FuncState *fs) {
|
|
removelastlineinfo(fs);
|
|
fs->pc--;
|
|
}
|
|
|
|
|
|
/*
|
|
** Emit instruction 'i', checking for array sizes and saving also its
|
|
** line information. Return 'i' position.
|
|
*/
|
|
int luaK_code (FuncState *fs, Instruction i) {
|
|
Proto *f = fs->f;
|
|
/* put new instruction in code array */
|
|
luaM_growvector(fs->ls->L, f->code, fs->pc, f->sizecode, Instruction,
|
|
MAX_INT, "opcodes");
|
|
f->code[fs->pc++] = i;
|
|
savelineinfo(fs, f, fs->ls->lastline);
|
|
return fs->pc - 1; /* index of new instruction */
|
|
}
|
|
|
|
|
|
/*
|
|
** Format and emit an 'iABC' instruction. (Assertions check consistency
|
|
** of parameters versus opcode.)
|
|
*/
|
|
int luaK_codeABCk (FuncState *fs, OpCode o, int a, int b, int c, int k) {
|
|
lua_assert(getOpMode(o) == iABC);
|
|
lua_assert(a <= MAXARG_A && b <= MAXARG_B &&
|
|
c <= MAXARG_C && (k & ~1) == 0);
|
|
return luaK_code(fs, CREATE_ABCk(o, a, b, c, k));
|
|
}
|
|
|
|
|
|
/*
|
|
** Format and emit an 'iABx' instruction.
|
|
*/
|
|
int luaK_codeABx (FuncState *fs, OpCode o, int a, unsigned int bc) {
|
|
lua_assert(getOpMode(o) == iABx);
|
|
lua_assert(a <= MAXARG_A && bc <= MAXARG_Bx);
|
|
return luaK_code(fs, CREATE_ABx(o, a, bc));
|
|
}
|
|
|
|
|
|
/*
|
|
** Format and emit an 'iAsBx' instruction.
|
|
*/
|
|
int luaK_codeAsBx (FuncState *fs, OpCode o, int a, int bc) {
|
|
unsigned int b = bc + OFFSET_sBx;
|
|
lua_assert(getOpMode(o) == iAsBx);
|
|
lua_assert(a <= MAXARG_A && b <= MAXARG_Bx);
|
|
return luaK_code(fs, CREATE_ABx(o, a, b));
|
|
}
|
|
|
|
|
|
/*
|
|
** Format and emit an 'isJ' instruction.
|
|
*/
|
|
static int codesJ (FuncState *fs, OpCode o, int sj, int k) {
|
|
unsigned int j = sj + OFFSET_sJ;
|
|
lua_assert(getOpMode(o) == isJ);
|
|
lua_assert(j <= MAXARG_sJ && (k & ~1) == 0);
|
|
return luaK_code(fs, CREATE_sJ(o, j, k));
|
|
}
|
|
|
|
|
|
/*
|
|
** Emit an "extra argument" instruction (format 'iAx')
|
|
*/
|
|
static int codeextraarg (FuncState *fs, int a) {
|
|
lua_assert(a <= MAXARG_Ax);
|
|
return luaK_code(fs, CREATE_Ax(OP_EXTRAARG, a));
|
|
}
|
|
|
|
|
|
/*
|
|
** Emit a "load constant" instruction, using either 'OP_LOADK'
|
|
** (if constant index 'k' fits in 18 bits) or an 'OP_LOADKX'
|
|
** instruction with "extra argument".
|
|
*/
|
|
static int luaK_codek (FuncState *fs, int reg, int k) {
|
|
if (k <= MAXARG_Bx)
|
|
return luaK_codeABx(fs, OP_LOADK, reg, k);
|
|
else {
|
|
int p = luaK_codeABx(fs, OP_LOADKX, reg, 0);
|
|
codeextraarg(fs, k);
|
|
return p;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
** Check register-stack level, keeping track of its maximum size
|
|
** in field 'maxstacksize'
|
|
*/
|
|
void luaK_checkstack (FuncState *fs, int n) {
|
|
int newstack = fs->freereg + n;
|
|
if (newstack > fs->f->maxstacksize) {
|
|
if (newstack >= MAXREGS)
|
|
luaX_syntaxerror(fs->ls,
|
|
"function or expression needs too many registers");
|
|
fs->f->maxstacksize = cast_byte(newstack);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
** Reserve 'n' registers in register stack
|
|
*/
|
|
void luaK_reserveregs (FuncState *fs, int n) {
|
|
luaK_checkstack(fs, n);
|
|
fs->freereg += n;
|
|
}
|
|
|
|
|
|
/*
|
|
** Free register 'reg', if it is neither a constant index nor
|
|
** a local variable.
|
|
)
|
|
*/
|
|
static void freereg (FuncState *fs, int reg) {
|
|
if (reg >= luaY_nvarstack(fs)) {
|
|
fs->freereg--;
|
|
lua_assert(reg == fs->freereg);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
** Free two registers in proper order
|
|
*/
|
|
static void freeregs (FuncState *fs, int r1, int r2) {
|
|
if (r1 > r2) {
|
|
freereg(fs, r1);
|
|
freereg(fs, r2);
|
|
}
|
|
else {
|
|
freereg(fs, r2);
|
|
freereg(fs, r1);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
** Free register used by expression 'e' (if any)
|
|
*/
|
|
static void freeexp (FuncState *fs, expdesc *e) {
|
|
if (e->k == VNONRELOC)
|
|
freereg(fs, e->u.info);
|
|
}
|
|
|
|
|
|
/*
|
|
** Free registers used by expressions 'e1' and 'e2' (if any) in proper
|
|
** order.
|
|
*/
|
|
static void freeexps (FuncState *fs, expdesc *e1, expdesc *e2) {
|
|
int r1 = (e1->k == VNONRELOC) ? e1->u.info : -1;
|
|
int r2 = (e2->k == VNONRELOC) ? e2->u.info : -1;
|
|
freeregs(fs, r1, r2);
|
|
}
|
|
|
|
|
|
/*
|
|
** Add constant 'v' to prototype's list of constants (field 'k').
|
|
** Use scanner's table to cache position of constants in constant list
|
|
** and try to reuse constants. Because some values should not be used
|
|
** as keys (nil cannot be a key, integer keys can collapse with float
|
|
** keys), the caller must provide a useful 'key' for indexing the cache.
|
|
*/
|
|
static int addk (FuncState *fs, TValue *key, TValue *v) {
|
|
lua_State *L = fs->ls->L;
|
|
Proto *f = fs->f;
|
|
TValue *idx = luaH_set(L, fs->ls->h, key); /* index scanner table */
|
|
int k, oldsize;
|
|
if (ttisinteger(idx)) { /* is there an index there? */
|
|
k = cast_int(ivalue(idx));
|
|
/* correct value? (warning: must distinguish floats from integers!) */
|
|
if (k < fs->nk && ttypetag(&f->k[k]) == ttypetag(v) &&
|
|
luaV_rawequalobj(&f->k[k], v))
|
|
return k; /* reuse index */
|
|
}
|
|
/* constant not found; create a new entry */
|
|
oldsize = f->sizek;
|
|
k = fs->nk;
|
|
/* numerical value does not need GC barrier;
|
|
table has no metatable, so it does not need to invalidate cache */
|
|
setivalue(idx, k);
|
|
luaM_growvector(L, f->k, k, f->sizek, TValue, MAXARG_Ax, "constants");
|
|
while (oldsize < f->sizek) setnilvalue(&f->k[oldsize++]);
|
|
setobj(L, &f->k[k], v);
|
|
fs->nk++;
|
|
luaC_barrier(L, f, v);
|
|
return k;
|
|
}
|
|
|
|
|
|
/*
|
|
** Add a string to list of constants and return its index.
|
|
*/
|
|
static int stringK (FuncState *fs, TString *s) {
|
|
TValue o;
|
|
setsvalue(fs->ls->L, &o, s);
|
|
return addk(fs, &o, &o); /* use string itself as key */
|
|
}
|
|
|
|
|
|
/*
|
|
** Add an integer to list of constants and return its index.
|
|
** Integers use userdata as keys to avoid collision with floats with
|
|
** same value; conversion to 'void*' is used only for hashing, so there
|
|
** are no "precision" problems.
|
|
*/
|
|
static int luaK_intK (FuncState *fs, lua_Integer n) {
|
|
TValue k, o;
|
|
setpvalue(&k, cast_voidp(cast_sizet(n)));
|
|
setivalue(&o, n);
|
|
return addk(fs, &k, &o);
|
|
}
|
|
|
|
/*
|
|
** Add a float to list of constants and return its index.
|
|
*/
|
|
static int luaK_numberK (FuncState *fs, lua_Number r) {
|
|
TValue o;
|
|
setfltvalue(&o, r);
|
|
return addk(fs, &o, &o); /* use number itself as key */
|
|
}
|
|
|
|
|
|
/*
|
|
** Add a boolean to list of constants and return its index.
|
|
*/
|
|
static int boolK (FuncState *fs, int b) {
|
|
TValue o;
|
|
setbvalue(&o, b);
|
|
return addk(fs, &o, &o); /* use boolean itself as key */
|
|
}
|
|
|
|
|
|
/*
|
|
** Add nil to list of constants and return its index.
|
|
*/
|
|
static int nilK (FuncState *fs) {
|
|
TValue k, v;
|
|
setnilvalue(&v);
|
|
/* cannot use nil as key; instead use table itself to represent nil */
|
|
sethvalue(fs->ls->L, &k, fs->ls->h);
|
|
return addk(fs, &k, &v);
|
|
}
|
|
|
|
|
|
/*
|
|
** Check whether 'i' can be stored in an 'sC' operand.
|
|
** Equivalent to (0 <= i + OFFSET_sC && i + OFFSET_sC <= MAXARG_C)
|
|
** but without risk of overflows in the addition.
|
|
*/
|
|
static int fitsC (lua_Integer i) {
|
|
return (-OFFSET_sC <= i && i <= MAXARG_C - OFFSET_sC);
|
|
}
|
|
|
|
|
|
/*
|
|
** Check whether 'i' can be stored in an 'sBx' operand.
|
|
*/
|
|
static int fitsBx (lua_Integer i) {
|
|
return (-OFFSET_sBx <= i && i <= MAXARG_Bx - OFFSET_sBx);
|
|
}
|
|
|
|
|
|
void luaK_int (FuncState *fs, int reg, lua_Integer i) {
|
|
if (fitsBx(i))
|
|
luaK_codeAsBx(fs, OP_LOADI, reg, cast_int(i));
|
|
else
|
|
luaK_codek(fs, reg, luaK_intK(fs, i));
|
|
}
|
|
|
|
|
|
static int floatI (lua_Number f, lua_Integer *fi) {
|
|
return (luaV_flttointeger(f, fi, 0) && fitsBx(*fi));
|
|
}
|
|
|
|
|
|
static void luaK_float (FuncState *fs, int reg, lua_Number f) {
|
|
lua_Integer fi;
|
|
if (floatI(f, &fi))
|
|
luaK_codeAsBx(fs, OP_LOADF, reg, cast_int(fi));
|
|
else
|
|
luaK_codek(fs, reg, luaK_numberK(fs, f));
|
|
}
|
|
|
|
|
|
/*
|
|
** Convert a constant in 'v' into an expression description 'e'
|
|
*/
|
|
static void const2exp (TValue *v, expdesc *e) {
|
|
switch (ttypetag(v)) {
|
|
case LUA_TNUMINT:
|
|
e->k = VKINT; e->u.ival = ivalue(v);
|
|
break;
|
|
case LUA_TNUMFLT:
|
|
e->k = VKFLT; e->u.nval = fltvalue(v);
|
|
break;
|
|
case LUA_TBOOLEAN:
|
|
e->k = bvalue(v) ? VTRUE : VFALSE;
|
|
break;
|
|
case LUA_TNIL:
|
|
e->k = VNIL;
|
|
break;
|
|
case LUA_TSHRSTR: case LUA_TLNGSTR:
|
|
e->k = VKSTR; e->u.strval = tsvalue(v);
|
|
break;
|
|
default: lua_assert(0);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
** 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) {
|
|
Instruction *pc = &getinstruction(fs, e);
|
|
if (e->k == VCALL) /* expression is an open function call? */
|
|
SETARG_C(*pc, nresults + 1);
|
|
else if (e->k == VVARARG) {
|
|
SETARG_C(*pc, nresults + 1);
|
|
SETARG_A(*pc, fs->freereg);
|
|
luaK_reserveregs(fs, 1);
|
|
}
|
|
else lua_assert(nresults == LUA_MULTRET);
|
|
}
|
|
|
|
|
|
/*
|
|
** Convert a VKSTR to a VK
|
|
*/
|
|
static void str2K (FuncState *fs, expdesc *e) {
|
|
lua_assert(e->k == VKSTR);
|
|
e->u.info = stringK(fs, e->u.strval);
|
|
e->k = VK;
|
|
}
|
|
|
|
|
|
/*
|
|
** Fix an expression to return one result.
|
|
** If expression is not a multi-ret expression (function call or
|
|
** vararg), it already returns one result, so nothing needs to be done.
|
|
** Function calls become VNONRELOC expressions (as its result comes
|
|
** fixed in the base register of the call), while vararg expressions
|
|
** become VRELOC (as OP_VARARG puts its results where it wants).
|
|
** (Calls are created returning one result, so that does not need
|
|
** to be fixed.)
|
|
*/
|
|
void luaK_setoneret (FuncState *fs, expdesc *e) {
|
|
if (e->k == VCALL) { /* expression is an open function call? */
|
|
/* already returns 1 value */
|
|
lua_assert(GETARG_C(getinstruction(fs, e)) == 2);
|
|
e->k = VNONRELOC; /* result has fixed position */
|
|
e->u.info = GETARG_A(getinstruction(fs, e));
|
|
}
|
|
else if (e->k == VVARARG) {
|
|
SETARG_C(getinstruction(fs, e), 2);
|
|
e->k = VRELOC; /* can relocate its simple result */
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
** Ensure that expression 'e' is not a variable.
|
|
** (Expression still may have jump lists.)
|
|
*/
|
|
void luaK_dischargevars (FuncState *fs, expdesc *e) {
|
|
switch (e->k) {
|
|
case VCONST: {
|
|
TValue *val = &fs->ls->dyd->actvar.arr[e->u.info].k;
|
|
const2exp(val, e);
|
|
break;
|
|
}
|
|
case VLOCAL: { /* already in a register */
|
|
e->u.info = e->u.var.sidx;
|
|
e->k = VNONRELOC; /* becomes a non-relocatable value */
|
|
break;
|
|
}
|
|
case VUPVAL: { /* move value to some (pending) register */
|
|
e->u.info = luaK_codeABC(fs, OP_GETUPVAL, 0, e->u.info, 0);
|
|
e->k = VRELOC;
|
|
break;
|
|
}
|
|
case VINDEXUP: {
|
|
e->u.info = luaK_codeABC(fs, OP_GETTABUP, 0, e->u.ind.t, e->u.ind.idx);
|
|
e->k = VRELOC;
|
|
break;
|
|
}
|
|
case VINDEXI: {
|
|
freereg(fs, e->u.ind.t);
|
|
e->u.info = luaK_codeABC(fs, OP_GETI, 0, e->u.ind.t, e->u.ind.idx);
|
|
e->k = VRELOC;
|
|
break;
|
|
}
|
|
case VINDEXSTR: {
|
|
freereg(fs, e->u.ind.t);
|
|
e->u.info = luaK_codeABC(fs, OP_GETFIELD, 0, e->u.ind.t, e->u.ind.idx);
|
|
e->k = VRELOC;
|
|
break;
|
|
}
|
|
case VINDEXED: {
|
|
freeregs(fs, e->u.ind.t, e->u.ind.idx);
|
|
e->u.info = luaK_codeABC(fs, OP_GETTABLE, 0, e->u.ind.t, e->u.ind.idx);
|
|
e->k = VRELOC;
|
|
break;
|
|
}
|
|
case VVARARG: case VCALL: {
|
|
luaK_setoneret(fs, e);
|
|
break;
|
|
}
|
|
default: break; /* there is one value available (somewhere) */
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
** Ensures expression value is in register 'reg' (and therefore
|
|
** 'e' will become a non-relocatable expression).
|
|
** (Expression still may have jump lists.)
|
|
*/
|
|
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 VKSTR: {
|
|
str2K(fs, e);
|
|
} /* FALLTHROUGH */
|
|
case VK: {
|
|
luaK_codek(fs, reg, e->u.info);
|
|
break;
|
|
}
|
|
case VKFLT: {
|
|
luaK_float(fs, reg, e->u.nval);
|
|
break;
|
|
}
|
|
case VKINT: {
|
|
luaK_int(fs, reg, e->u.ival);
|
|
break;
|
|
}
|
|
case VRELOC: {
|
|
Instruction *pc = &getinstruction(fs, e);
|
|
SETARG_A(*pc, reg); /* instruction will put result in '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 == VJMP);
|
|
return; /* nothing to do... */
|
|
}
|
|
}
|
|
e->u.info = reg;
|
|
e->k = VNONRELOC;
|
|
}
|
|
|
|
|
|
/*
|
|
** Ensures expression value is in any register.
|
|
** (Expression still may have jump lists.)
|
|
*/
|
|
static void discharge2anyreg (FuncState *fs, expdesc *e) {
|
|
if (e->k != VNONRELOC) { /* no fixed register yet? */
|
|
luaK_reserveregs(fs, 1); /* get a register */
|
|
discharge2reg(fs, e, fs->freereg-1); /* put value there */
|
|
}
|
|
}
|
|
|
|
|
|
static int code_loadbool (FuncState *fs, int A, int b, int jump) {
|
|
luaK_getlabel(fs); /* those instructions may be jump targets */
|
|
return luaK_codeABC(fs, OP_LOADBOOL, A, b, jump);
|
|
}
|
|
|
|
|
|
/*
|
|
** check whether list has any jump that do not produce a value
|
|
** or produce an inverted value
|
|
*/
|
|
static int need_value (FuncState *fs, int list) {
|
|
for (; list != NO_JUMP; list = getjump(fs, list)) {
|
|
Instruction i = *getjumpcontrol(fs, list);
|
|
if (GET_OPCODE(i) != OP_TESTSET) return 1;
|
|
}
|
|
return 0; /* not found */
|
|
}
|
|
|
|
|
|
/*
|
|
** Ensures final expression result (which includes results from its
|
|
** jump lists) is in register 'reg'.
|
|
** If expression has jumps, need to patch these jumps either to
|
|
** its final position or to "load" instructions (for those tests
|
|
** that do not produce values).
|
|
*/
|
|
static void exp2reg (FuncState *fs, expdesc *e, int reg) {
|
|
discharge2reg(fs, e, reg);
|
|
if (e->k == VJMP) /* expression itself is a test? */
|
|
luaK_concat(fs, &e->t, e->u.info); /* put this jump in 't' list */
|
|
if (hasjumps(e)) {
|
|
int final; /* position after whole expression */
|
|
int p_f = NO_JUMP; /* position of an eventual LOAD false */
|
|
int p_t = NO_JUMP; /* position of an eventual LOAD true */
|
|
if (need_value(fs, e->t) || need_value(fs, e->f)) {
|
|
int fj = (e->k == VJMP) ? NO_JUMP : luaK_jump(fs);
|
|
p_f = code_loadbool(fs, reg, 0, 1); /* load false and skip next i. */
|
|
p_t = code_loadbool(fs, reg, 1, 0); /* load true */
|
|
/* jump around these booleans if 'e' is not a test */
|
|
luaK_patchtohere(fs, fj);
|
|
}
|
|
final = luaK_getlabel(fs);
|
|
patchlistaux(fs, e->f, final, reg, p_f);
|
|
patchlistaux(fs, e->t, final, reg, p_t);
|
|
}
|
|
e->f = e->t = NO_JUMP;
|
|
e->u.info = reg;
|
|
e->k = VNONRELOC;
|
|
}
|
|
|
|
|
|
/*
|
|
** Ensures final expression result is in next available register.
|
|
*/
|
|
void luaK_exp2nextreg (FuncState *fs, expdesc *e) {
|
|
luaK_dischargevars(fs, e);
|
|
freeexp(fs, e);
|
|
luaK_reserveregs(fs, 1);
|
|
exp2reg(fs, e, fs->freereg - 1);
|
|
}
|
|
|
|
|
|
/*
|
|
** Ensures final expression result is in some (any) register
|
|
** and return that register.
|
|
*/
|
|
int luaK_exp2anyreg (FuncState *fs, expdesc *e) {
|
|
luaK_dischargevars(fs, e);
|
|
if (e->k == VNONRELOC) { /* expression already has a register? */
|
|
if (!hasjumps(e)) /* no jumps? */
|
|
return e->u.info; /* result is already in a register */
|
|
if (e->u.info >= luaY_nvarstack(fs)) { /* reg. is not a local? */
|
|
exp2reg(fs, e, e->u.info); /* put final result in it */
|
|
return e->u.info;
|
|
}
|
|
}
|
|
luaK_exp2nextreg(fs, e); /* otherwise, use next available register */
|
|
return e->u.info;
|
|
}
|
|
|
|
|
|
/*
|
|
** Ensures final expression result is either in a register
|
|
** or in an upvalue.
|
|
*/
|
|
void luaK_exp2anyregup (FuncState *fs, expdesc *e) {
|
|
if (e->k != VUPVAL || hasjumps(e))
|
|
luaK_exp2anyreg(fs, e);
|
|
}
|
|
|
|
|
|
/*
|
|
** Ensures final expression result is either in a register
|
|
** or it is a constant.
|
|
*/
|
|
void luaK_exp2val (FuncState *fs, expdesc *e) {
|
|
if (hasjumps(e))
|
|
luaK_exp2anyreg(fs, e);
|
|
else
|
|
luaK_dischargevars(fs, e);
|
|
}
|
|
|
|
|
|
/*
|
|
** Try to make 'e' a K expression with an index in the range of R/K
|
|
** indices. Return true iff succeeded.
|
|
*/
|
|
static int luaK_exp2K (FuncState *fs, expdesc *e) {
|
|
if (!hasjumps(e)) {
|
|
int info;
|
|
switch (e->k) { /* move constants to 'k' */
|
|
case VTRUE: info = boolK(fs, 1); break;
|
|
case VFALSE: info = boolK(fs, 0); break;
|
|
case VNIL: info = nilK(fs); break;
|
|
case VKINT: info = luaK_intK(fs, e->u.ival); break;
|
|
case VKFLT: info = luaK_numberK(fs, e->u.nval); break;
|
|
case VKSTR: info = stringK(fs, e->u.strval); break;
|
|
case VK: info = e->u.info; break;
|
|
default: return 0; /* not a constant */
|
|
}
|
|
if (info <= MAXINDEXRK) { /* does constant fit in 'argC'? */
|
|
e->k = VK; /* make expression a 'K' expression */
|
|
e->u.info = info;
|
|
return 1;
|
|
}
|
|
}
|
|
/* else, expression doesn't fit; leave it unchanged */
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
** Ensures final expression result is in a valid R/K index
|
|
** (that is, it is either in a register or in 'k' with an index
|
|
** in the range of R/K indices).
|
|
** Returns 1 iff expression is K.
|
|
*/
|
|
int luaK_exp2RK (FuncState *fs, expdesc *e) {
|
|
if (luaK_exp2K(fs, e))
|
|
return 1;
|
|
else { /* not a constant in the right range: put it in a register */
|
|
luaK_exp2anyreg(fs, e);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
static void codeABRK (FuncState *fs, OpCode o, int a, int b,
|
|
expdesc *ec) {
|
|
int k = luaK_exp2RK(fs, ec);
|
|
luaK_codeABCk(fs, o, a, b, ec->u.info, k);
|
|
}
|
|
|
|
|
|
/*
|
|
** Generate code to store result of expression 'ex' into variable 'var'.
|
|
*/
|
|
void luaK_storevar (FuncState *fs, expdesc *var, expdesc *ex) {
|
|
switch (var->k) {
|
|
case VLOCAL: {
|
|
freeexp(fs, ex);
|
|
exp2reg(fs, ex, var->u.var.sidx); /* compute 'ex' into proper place */
|
|
return;
|
|
}
|
|
case VUPVAL: {
|
|
int e = luaK_exp2anyreg(fs, ex);
|
|
luaK_codeABC(fs, OP_SETUPVAL, e, var->u.info, 0);
|
|
break;
|
|
}
|
|
case VINDEXUP: {
|
|
codeABRK(fs, OP_SETTABUP, var->u.ind.t, var->u.ind.idx, ex);
|
|
break;
|
|
}
|
|
case VINDEXI: {
|
|
codeABRK(fs, OP_SETI, var->u.ind.t, var->u.ind.idx, ex);
|
|
break;
|
|
}
|
|
case VINDEXSTR: {
|
|
codeABRK(fs, OP_SETFIELD, var->u.ind.t, var->u.ind.idx, ex);
|
|
break;
|
|
}
|
|
case VINDEXED: {
|
|
codeABRK(fs, OP_SETTABLE, var->u.ind.t, var->u.ind.idx, ex);
|
|
break;
|
|
}
|
|
default: lua_assert(0); /* invalid var kind to store */
|
|
}
|
|
freeexp(fs, ex);
|
|
}
|
|
|
|
|
|
/*
|
|
** Emit SELF instruction (convert expression 'e' into 'e:key(e,').
|
|
*/
|
|
void luaK_self (FuncState *fs, expdesc *e, expdesc *key) {
|
|
int ereg;
|
|
luaK_exp2anyreg(fs, e);
|
|
ereg = e->u.info; /* register where 'e' was placed */
|
|
freeexp(fs, e);
|
|
e->u.info = fs->freereg; /* base register for op_self */
|
|
e->k = VNONRELOC; /* self expression has a fixed register */
|
|
luaK_reserveregs(fs, 2); /* function and 'self' produced by op_self */
|
|
codeABRK(fs, OP_SELF, e->u.info, ereg, key);
|
|
freeexp(fs, key);
|
|
}
|
|
|
|
|
|
/*
|
|
** Negate condition 'e' (where 'e' is a comparison).
|
|
*/
|
|
static void negatecondition (FuncState *fs, expdesc *e) {
|
|
Instruction *pc = getjumpcontrol(fs, e->u.info);
|
|
lua_assert(testTMode(GET_OPCODE(*pc)) && GET_OPCODE(*pc) != OP_TESTSET &&
|
|
GET_OPCODE(*pc) != OP_TEST);
|
|
SETARG_k(*pc, (GETARG_k(*pc) ^ 1));
|
|
}
|
|
|
|
|
|
/*
|
|
** Emit instruction to jump if 'e' is 'cond' (that is, if 'cond'
|
|
** is true, code will jump if 'e' is true.) Return jump position.
|
|
** Optimize when 'e' is 'not' something, inverting the condition
|
|
** and removing the 'not'.
|
|
*/
|
|
static int jumponcond (FuncState *fs, expdesc *e, int cond) {
|
|
if (e->k == VRELOC) {
|
|
Instruction ie = getinstruction(fs, e);
|
|
if (GET_OPCODE(ie) == OP_NOT) {
|
|
removelastinstruction(fs); /* remove previous OP_NOT */
|
|
return condjump(fs, OP_TEST, GETARG_B(ie), 0, 0, !cond);
|
|
}
|
|
/* else go through */
|
|
}
|
|
discharge2anyreg(fs, e);
|
|
freeexp(fs, e);
|
|
return condjump(fs, OP_TESTSET, NO_REG, e->u.info, 0, cond);
|
|
}
|
|
|
|
|
|
/*
|
|
** Emit code to go through if 'e' is true, jump otherwise.
|
|
*/
|
|
void luaK_goiftrue (FuncState *fs, expdesc *e) {
|
|
int pc; /* pc of new jump */
|
|
luaK_dischargevars(fs, e);
|
|
switch (e->k) {
|
|
case VJMP: { /* condition? */
|
|
negatecondition(fs, e); /* jump when it is false */
|
|
pc = e->u.info; /* save jump position */
|
|
break;
|
|
}
|
|
case VK: case VKFLT: case VKINT: case VKSTR: case VTRUE: {
|
|
pc = NO_JUMP; /* always true; do nothing */
|
|
break;
|
|
}
|
|
default: {
|
|
pc = jumponcond(fs, e, 0); /* jump when false */
|
|
break;
|
|
}
|
|
}
|
|
luaK_concat(fs, &e->f, pc); /* insert new jump in false list */
|
|
luaK_patchtohere(fs, e->t); /* true list jumps to here (to go through) */
|
|
e->t = NO_JUMP;
|
|
}
|
|
|
|
|
|
/*
|
|
** Emit code to go through if 'e' is false, jump otherwise.
|
|
*/
|
|
void luaK_goiffalse (FuncState *fs, expdesc *e) {
|
|
int pc; /* pc of new jump */
|
|
luaK_dischargevars(fs, e);
|
|
switch (e->k) {
|
|
case VJMP: {
|
|
pc = e->u.info; /* already jump if true */
|
|
break;
|
|
}
|
|
case VNIL: case VFALSE: {
|
|
pc = NO_JUMP; /* always false; do nothing */
|
|
break;
|
|
}
|
|
default: {
|
|
pc = jumponcond(fs, e, 1); /* jump if true */
|
|
break;
|
|
}
|
|
}
|
|
luaK_concat(fs, &e->t, pc); /* insert new jump in 't' list */
|
|
luaK_patchtohere(fs, e->f); /* false list jumps to here (to go through) */
|
|
e->f = NO_JUMP;
|
|
}
|
|
|
|
|
|
/*
|
|
** Code 'not e', doing constant folding.
|
|
*/
|
|
static void codenot (FuncState *fs, expdesc *e) {
|
|
switch (e->k) {
|
|
case VNIL: case VFALSE: {
|
|
e->k = VTRUE; /* true == not nil == not false */
|
|
break;
|
|
}
|
|
case VK: case VKFLT: case VKINT: case VKSTR: case VTRUE: {
|
|
e->k = VFALSE; /* false == not "x" == not 0.5 == not 1 == not true */
|
|
break;
|
|
}
|
|
case VJMP: {
|
|
negatecondition(fs, e);
|
|
break;
|
|
}
|
|
case VRELOC:
|
|
case VNONRELOC: {
|
|
discharge2anyreg(fs, e);
|
|
freeexp(fs, e);
|
|
e->u.info = luaK_codeABC(fs, OP_NOT, 0, e->u.info, 0);
|
|
e->k = VRELOC;
|
|
break;
|
|
}
|
|
default: lua_assert(0); /* cannot happen */
|
|
}
|
|
/* interchange true and false lists */
|
|
{ int temp = e->f; e->f = e->t; e->t = temp; }
|
|
removevalues(fs, e->f); /* values are useless when negated */
|
|
removevalues(fs, e->t);
|
|
}
|
|
|
|
|
|
/*
|
|
** Check whether expression 'e' is a small literal string
|
|
*/
|
|
static int isKstr (FuncState *fs, expdesc *e) {
|
|
return (e->k == VK && !hasjumps(e) && e->u.info <= MAXARG_B &&
|
|
ttisshrstring(&fs->f->k[e->u.info]));
|
|
}
|
|
|
|
/*
|
|
** Check whether expression 'e' is a literal integer.
|
|
*/
|
|
int luaK_isKint (expdesc *e) {
|
|
return (e->k == VKINT && !hasjumps(e));
|
|
}
|
|
|
|
|
|
/*
|
|
** Check whether expression 'e' is a literal integer in
|
|
** proper range to fit in register C
|
|
*/
|
|
static int isCint (expdesc *e) {
|
|
return luaK_isKint(e) && (l_castS2U(e->u.ival) <= l_castS2U(MAXARG_C));
|
|
}
|
|
|
|
|
|
/*
|
|
** Check whether expression 'e' is a literal integer in
|
|
** proper range to fit in register sC
|
|
*/
|
|
static int isSCint (expdesc *e) {
|
|
return luaK_isKint(e) && fitsC(e->u.ival);
|
|
}
|
|
|
|
|
|
/*
|
|
** Check whether expression 'e' is a literal integer or float in
|
|
** proper range to fit in a register (sB or sC).
|
|
*/
|
|
static int isSCnumber (expdesc *e, lua_Integer *i, int *isfloat) {
|
|
if (e->k == VKINT)
|
|
*i = e->u.ival;
|
|
else if (!(e->k == VKFLT && floatI(e->u.nval, i)))
|
|
return 0; /* not a number */
|
|
else
|
|
*isfloat = 1;
|
|
if (!hasjumps(e) && fitsC(*i)) {
|
|
*i += OFFSET_sC;
|
|
return 1;
|
|
}
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
** Create expression 't[k]'. 't' must have its final result already in a
|
|
** register or upvalue. Upvalues can only be indexed by literal strings.
|
|
** Keys can be literal strings in the constant table or arbitrary
|
|
** values in registers.
|
|
*/
|
|
void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k) {
|
|
if (k->k == VKSTR)
|
|
str2K(fs, k);
|
|
lua_assert(!hasjumps(t) &&
|
|
(t->k == VLOCAL || t->k == VNONRELOC || t->k == VUPVAL));
|
|
if (t->k == VUPVAL && !isKstr(fs, k)) /* upvalue indexed by non 'Kstr'? */
|
|
luaK_exp2anyreg(fs, t); /* put it in a register */
|
|
if (t->k == VUPVAL) {
|
|
t->u.ind.t = t->u.info; /* upvalue index */
|
|
t->u.ind.idx = k->u.info; /* literal string */
|
|
t->k = VINDEXUP;
|
|
}
|
|
else {
|
|
/* register index of the table */
|
|
t->u.ind.t = (t->k == VLOCAL) ? t->u.var.sidx: t->u.info;
|
|
if (isKstr(fs, k)) {
|
|
t->u.ind.idx = k->u.info; /* literal string */
|
|
t->k = VINDEXSTR;
|
|
}
|
|
else if (isCint(k)) {
|
|
t->u.ind.idx = cast_int(k->u.ival); /* int. constant in proper range */
|
|
t->k = VINDEXI;
|
|
}
|
|
else {
|
|
t->u.ind.idx = luaK_exp2anyreg(fs, k); /* register */
|
|
t->k = VINDEXED;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
** Return false if folding can raise an error.
|
|
** Bitwise operations need operands convertible to integers; division
|
|
** operations cannot have 0 as divisor.
|
|
*/
|
|
static int validop (int op, TValue *v1, TValue *v2) {
|
|
switch (op) {
|
|
case LUA_OPBAND: case LUA_OPBOR: case LUA_OPBXOR:
|
|
case LUA_OPSHL: case LUA_OPSHR: case LUA_OPBNOT: { /* conversion errors */
|
|
lua_Integer i;
|
|
return (tointegerns(v1, &i) && tointegerns(v2, &i));
|
|
}
|
|
case LUA_OPDIV: case LUA_OPIDIV: case LUA_OPMOD: /* division by 0 */
|
|
return (nvalue(v2) != 0);
|
|
default: return 1; /* everything else is valid */
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
** Try to "constant-fold" an operation; return 1 iff successful.
|
|
** (In this case, 'e1' has the final result.)
|
|
*/
|
|
static int constfolding (FuncState *fs, int op, expdesc *e1,
|
|
const expdesc *e2) {
|
|
TValue v1, v2, res;
|
|
if (!tonumeral(e1, &v1) || !tonumeral(e2, &v2) || !validop(op, &v1, &v2))
|
|
return 0; /* non-numeric operands or not safe to fold */
|
|
luaO_rawarith(fs->ls->L, op, &v1, &v2, &res); /* does operation */
|
|
if (ttisinteger(&res)) {
|
|
e1->k = VKINT;
|
|
e1->u.ival = ivalue(&res);
|
|
}
|
|
else { /* folds neither NaN nor 0.0 (to avoid problems with -0.0) */
|
|
lua_Number n = fltvalue(&res);
|
|
if (luai_numisnan(n) || n == 0)
|
|
return 0;
|
|
e1->k = VKFLT;
|
|
e1->u.nval = n;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
|
|
/*
|
|
** Emit code for unary expressions that "produce values"
|
|
** (everything but 'not').
|
|
** Expression to produce final result will be encoded in 'e'.
|
|
*/
|
|
static void codeunexpval (FuncState *fs, OpCode op, expdesc *e, int line) {
|
|
int r = luaK_exp2anyreg(fs, e); /* opcodes operate only on registers */
|
|
freeexp(fs, e);
|
|
e->u.info = luaK_codeABC(fs, op, 0, r, 0); /* generate opcode */
|
|
e->k = VRELOC; /* all those operations are relocatable */
|
|
luaK_fixline(fs, line);
|
|
}
|
|
|
|
|
|
/*
|
|
** Emit code for binary expressions that "produce values"
|
|
** (everything but logical operators 'and'/'or' and comparison
|
|
** operators).
|
|
** Expression to produce final result will be encoded in 'e1'.
|
|
** Because 'luaK_exp2anyreg' can free registers, its calls must be
|
|
** in "stack order" (that is, first on 'e2', which may have more
|
|
** recent registers to be released).
|
|
*/
|
|
static void finishbinexpval (FuncState *fs, expdesc *e1, expdesc *e2,
|
|
OpCode op, int v2, int k, int line) {
|
|
int v1 = luaK_exp2anyreg(fs, e1);
|
|
int pc = luaK_codeABCk(fs, op, 0, v1, v2, k);
|
|
freeexps(fs, e1, e2);
|
|
e1->u.info = pc;
|
|
e1->k = VRELOC; /* all those operations are relocatable */
|
|
luaK_fixline(fs, line);
|
|
}
|
|
|
|
|
|
/*
|
|
** Emit code for binary expressions that "produce values" over
|
|
** two registers.
|
|
*/
|
|
static void codebinexpval (FuncState *fs, OpCode op,
|
|
expdesc *e1, expdesc *e2, int line) {
|
|
int v2 = luaK_exp2anyreg(fs, e2); /* both operands are in registers */
|
|
finishbinexpval(fs, e1, e2, op, v2, 0, line);
|
|
}
|
|
|
|
|
|
/*
|
|
** Code binary operators ('+', '-', ...) with immediate operands.
|
|
*/
|
|
static void codebini (FuncState *fs, OpCode op,
|
|
expdesc *e1, expdesc *e2, int k, int line) {
|
|
int v2 = cast_int(e2->u.ival) + OFFSET_sC; /* immediate operand */
|
|
finishbinexpval(fs, e1, e2, op, v2, k, line);
|
|
}
|
|
|
|
|
|
static void swapexps (expdesc *e1, expdesc *e2) {
|
|
expdesc temp = *e1; *e1 = *e2; *e2 = temp; /* swap 'e1' and 'e2' */
|
|
}
|
|
|
|
|
|
/*
|
|
** Code arithmetic operators ('+', '-', ...). If second operand is a
|
|
** constant in the proper range, use variant opcodes with immediate
|
|
** operands or K operands.
|
|
*/
|
|
static void codearith (FuncState *fs, OpCode op,
|
|
expdesc *e1, expdesc *e2, int flip, int line) {
|
|
if (isSCint(e2)) /* immediate operand? */
|
|
codebini(fs, cast(OpCode, op - OP_ADD + OP_ADDI), e1, e2, flip, line);
|
|
else if (tonumeral(e2, NULL) && luaK_exp2K(fs, e2)) { /* K operand? */
|
|
int v2 = e2->u.info; /* K index */
|
|
op = cast(OpCode, op - OP_ADD + OP_ADDK);
|
|
finishbinexpval(fs, e1, e2, op, v2, flip, line);
|
|
}
|
|
else { /* 'e2' is neither an immediate nor a K operand */
|
|
if (flip)
|
|
swapexps(e1, e2); /* back to original order */
|
|
codebinexpval(fs, op, e1, e2, line); /* use standard operators */
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
** Code commutative operators ('+', '*'). If first operand is a
|
|
** numeric constant, change order of operands to try to use an
|
|
** immediate or K operator.
|
|
*/
|
|
static void codecommutative (FuncState *fs, OpCode op,
|
|
expdesc *e1, expdesc *e2, int line) {
|
|
int flip = 0;
|
|
if (tonumeral(e1, NULL)) { /* is first operand a numeric constant? */
|
|
swapexps(e1, e2); /* change order */
|
|
flip = 1;
|
|
}
|
|
codearith(fs, op, e1, e2, flip, line);
|
|
}
|
|
|
|
|
|
/*
|
|
** Code bitwise operations; they are all associative, so the function
|
|
** tries to put an integer constant as the 2nd operand (a K operand).
|
|
*/
|
|
static void codebitwise (FuncState *fs, BinOpr opr,
|
|
expdesc *e1, expdesc *e2, int line) {
|
|
int inv = 0;
|
|
int v2;
|
|
OpCode op;
|
|
if (e1->k == VKINT && luaK_exp2RK(fs, e1)) {
|
|
swapexps(e1, e2); /* 'e2' will be the constant operand */
|
|
inv = 1;
|
|
}
|
|
else if (!(e2->k == VKINT && luaK_exp2RK(fs, e2))) { /* no constants? */
|
|
op = cast(OpCode, opr - OPR_BAND + OP_BAND);
|
|
codebinexpval(fs, op, e1, e2, line); /* all-register opcodes */
|
|
return;
|
|
}
|
|
v2 = e2->u.info; /* index in K array */
|
|
op = cast(OpCode, opr - OPR_BAND + OP_BANDK);
|
|
lua_assert(ttisinteger(&fs->f->k[v2]));
|
|
finishbinexpval(fs, e1, e2, op, v2, inv, line);
|
|
}
|
|
|
|
|
|
/*
|
|
** Code shift operators. If second operand is constant, use immediate
|
|
** operand (negating it if shift is in the other direction).
|
|
*/
|
|
static void codeshift (FuncState *fs, OpCode op,
|
|
expdesc *e1, expdesc *e2, int line) {
|
|
if (isSCint(e2)) {
|
|
int changedir = 0;
|
|
if (op == OP_SHL) {
|
|
changedir = 1;
|
|
e2->u.ival = -(e2->u.ival);
|
|
}
|
|
codebini(fs, OP_SHRI, e1, e2, changedir, line);
|
|
}
|
|
else
|
|
codebinexpval(fs, op, e1, e2, line);
|
|
}
|
|
|
|
|
|
/*
|
|
** Emit code for order comparisons. When using an immediate operand,
|
|
** 'isfloat' tells whether the original value was a float.
|
|
*/
|
|
static void codeorder (FuncState *fs, OpCode op, expdesc *e1, expdesc *e2) {
|
|
int r1, r2;
|
|
lua_Integer im;
|
|
int isfloat = 0;
|
|
if (isSCnumber(e2, &im, &isfloat)) {
|
|
/* use immediate operand */
|
|
r1 = luaK_exp2anyreg(fs, e1);
|
|
r2 = cast_int(im);
|
|
op = cast(OpCode, (op - OP_LT) + OP_LTI);
|
|
}
|
|
else if (isSCnumber(e1, &im, &isfloat)) {
|
|
/* transform (A < B) to (B > A) and (A <= B) to (B >= A) */
|
|
r1 = luaK_exp2anyreg(fs, e2);
|
|
r2 = cast_int(im);
|
|
op = (op == OP_LT) ? OP_GTI : OP_GEI;
|
|
}
|
|
else { /* regular case, compare two registers */
|
|
r1 = luaK_exp2anyreg(fs, e1);
|
|
r2 = luaK_exp2anyreg(fs, e2);
|
|
}
|
|
freeexps(fs, e1, e2);
|
|
e1->u.info = condjump(fs, op, r1, r2, isfloat, 1);
|
|
e1->k = VJMP;
|
|
}
|
|
|
|
|
|
/*
|
|
** Emit code for equality comparisons ('==', '~=').
|
|
** 'e1' was already put as RK by 'luaK_infix'.
|
|
*/
|
|
static void codeeq (FuncState *fs, BinOpr opr, expdesc *e1, expdesc *e2) {
|
|
int r1, r2;
|
|
lua_Integer im;
|
|
int isfloat = 0; /* not needed here, but kept for symmetry */
|
|
OpCode op;
|
|
if (e1->k != VNONRELOC) {
|
|
lua_assert(e1->k == VK || e1->k == VKINT || e1->k == VKFLT);
|
|
swapexps(e1, e2);
|
|
}
|
|
r1 = luaK_exp2anyreg(fs, e1); /* 1nd expression must be in register */
|
|
if (isSCnumber(e2, &im, &isfloat)) {
|
|
op = OP_EQI;
|
|
r2 = cast_int(im); /* immediate operand */
|
|
}
|
|
else if (luaK_exp2RK(fs, e2)) { /* 1st expression is constant? */
|
|
op = OP_EQK;
|
|
r2 = e2->u.info; /* constant index */
|
|
}
|
|
else {
|
|
op = OP_EQ; /* will compare two registers */
|
|
r2 = luaK_exp2anyreg(fs, e2);
|
|
}
|
|
freeexps(fs, e1, e2);
|
|
e1->u.info = condjump(fs, op, r1, r2, isfloat, (opr == OPR_EQ));
|
|
e1->k = VJMP;
|
|
}
|
|
|
|
|
|
/*
|
|
** Apply prefix operation 'op' to expression 'e'.
|
|
*/
|
|
void luaK_prefix (FuncState *fs, UnOpr op, expdesc *e, int line) {
|
|
static const expdesc ef = {VKINT, {0}, NO_JUMP, NO_JUMP};
|
|
luaK_dischargevars(fs, e);
|
|
switch (op) {
|
|
case OPR_MINUS: case OPR_BNOT: /* use 'ef' as fake 2nd operand */
|
|
if (constfolding(fs, op + LUA_OPUNM, e, &ef))
|
|
break;
|
|
/* else */ /* FALLTHROUGH */
|
|
case OPR_LEN:
|
|
codeunexpval(fs, cast(OpCode, op + OP_UNM), e, line);
|
|
break;
|
|
case OPR_NOT: codenot(fs, e); break;
|
|
default: lua_assert(0);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
** Process 1st operand 'v' of binary operation 'op' before reading
|
|
** 2nd operand.
|
|
*/
|
|
void luaK_infix (FuncState *fs, BinOpr op, expdesc *v) {
|
|
luaK_dischargevars(fs, v);
|
|
switch (op) {
|
|
case OPR_AND: {
|
|
luaK_goiftrue(fs, v); /* go ahead only if 'v' is true */
|
|
break;
|
|
}
|
|
case OPR_OR: {
|
|
luaK_goiffalse(fs, v); /* go ahead only if 'v' is false */
|
|
break;
|
|
}
|
|
case OPR_CONCAT: {
|
|
luaK_exp2nextreg(fs, v); /* operand must be on the stack */
|
|
break;
|
|
}
|
|
case OPR_ADD: case OPR_SUB:
|
|
case OPR_MUL: case OPR_DIV: case OPR_IDIV:
|
|
case OPR_MOD: case OPR_POW:
|
|
case OPR_BAND: case OPR_BOR: case OPR_BXOR:
|
|
case OPR_SHL: case OPR_SHR: {
|
|
if (!tonumeral(v, NULL))
|
|
luaK_exp2anyreg(fs, v);
|
|
/* else keep numeral, which may be folded with 2nd operand */
|
|
break;
|
|
}
|
|
case OPR_EQ: case OPR_NE: {
|
|
if (!tonumeral(v, NULL))
|
|
luaK_exp2RK(fs, v);
|
|
/* else keep numeral, which may be an immediate operand */
|
|
break;
|
|
}
|
|
case OPR_LT: case OPR_LE:
|
|
case OPR_GT: case OPR_GE: {
|
|
lua_Integer dummy;
|
|
int dummy2;
|
|
if (!isSCnumber(v, &dummy, &dummy2))
|
|
luaK_exp2anyreg(fs, v);
|
|
/* else keep numeral, which may be an immediate operand */
|
|
break;
|
|
}
|
|
default: lua_assert(0);
|
|
}
|
|
}
|
|
|
|
/*
|
|
** Create code for '(e1 .. e2)'.
|
|
** For '(e1 .. e2.1 .. e2.2)' (which is '(e1 .. (e2.1 .. e2.2))',
|
|
** because concatenation is right associative), merge both CONCATs.
|
|
*/
|
|
static void codeconcat (FuncState *fs, expdesc *e1, expdesc *e2, int line) {
|
|
Instruction *ie2 = previousinstruction(fs);
|
|
if (GET_OPCODE(*ie2) == OP_CONCAT) { /* is 'e2' a concatenation? */
|
|
int n = GETARG_B(*ie2); /* # of elements concatenated in 'e2' */
|
|
lua_assert(e1->u.info + 1 == GETARG_A(*ie2));
|
|
freeexp(fs, e2);
|
|
SETARG_A(*ie2, e1->u.info); /* correct first element ('e1') */
|
|
SETARG_B(*ie2, n + 1); /* will concatenate one more element */
|
|
}
|
|
else { /* 'e2' is not a concatenation */
|
|
luaK_codeABC(fs, OP_CONCAT, e1->u.info, 2, 0); /* new concat opcode */
|
|
freeexp(fs, e2);
|
|
luaK_fixline(fs, line);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
** Finalize code for binary operation, after reading 2nd operand.
|
|
*/
|
|
void luaK_posfix (FuncState *fs, BinOpr opr,
|
|
expdesc *e1, expdesc *e2, int line) {
|
|
luaK_dischargevars(fs, e2);
|
|
switch (opr) {
|
|
case OPR_AND: {
|
|
lua_assert(e1->t == NO_JUMP); /* list closed by 'luaK_infix' */
|
|
luaK_concat(fs, &e2->f, e1->f);
|
|
*e1 = *e2;
|
|
break;
|
|
}
|
|
case OPR_OR: {
|
|
lua_assert(e1->f == NO_JUMP); /* list closed by 'luaK_infix' */
|
|
luaK_concat(fs, &e2->t, e1->t);
|
|
*e1 = *e2;
|
|
break;
|
|
}
|
|
case OPR_CONCAT: { /* e1 .. e2 */
|
|
luaK_exp2nextreg(fs, e2);
|
|
codeconcat(fs, e1, e2, line);
|
|
break;
|
|
}
|
|
case OPR_ADD: case OPR_MUL: {
|
|
if (!constfolding(fs, opr + LUA_OPADD, e1, e2))
|
|
codecommutative(fs, cast(OpCode, opr + OP_ADD), e1, e2, line);
|
|
break;
|
|
}
|
|
case OPR_SUB: case OPR_DIV:
|
|
case OPR_IDIV: case OPR_MOD: case OPR_POW: {
|
|
if (!constfolding(fs, opr + LUA_OPADD, e1, e2))
|
|
codearith(fs, cast(OpCode, opr + OP_ADD), e1, e2, 0, line);
|
|
break;
|
|
}
|
|
case OPR_BAND: case OPR_BOR: case OPR_BXOR: {
|
|
if (!constfolding(fs, opr + LUA_OPADD, e1, e2))
|
|
codebitwise(fs, opr, e1, e2, line);
|
|
break;
|
|
}
|
|
case OPR_SHL: {
|
|
if (!constfolding(fs, LUA_OPSHL, e1, e2)) {
|
|
if (isSCint(e1)) {
|
|
swapexps(e1, e2);
|
|
codebini(fs, OP_SHLI, e1, e2, 1, line);
|
|
}
|
|
else
|
|
codeshift(fs, OP_SHL, e1, e2, line);
|
|
}
|
|
break;
|
|
}
|
|
case OPR_SHR: {
|
|
if (!constfolding(fs, LUA_OPSHR, e1, e2))
|
|
codeshift(fs, OP_SHR, e1, e2, line);
|
|
break;
|
|
}
|
|
case OPR_EQ: case OPR_NE: {
|
|
codeeq(fs, opr, e1, e2);
|
|
break;
|
|
}
|
|
case OPR_LT: case OPR_LE: {
|
|
OpCode op = cast(OpCode, (opr - OPR_EQ) + OP_EQ);
|
|
codeorder(fs, op, e1, e2);
|
|
break;
|
|
}
|
|
case OPR_GT: case OPR_GE: {
|
|
/* '(a > b)' <=> '(b < a)'; '(a >= b)' <=> '(b <= a)' */
|
|
OpCode op = cast(OpCode, (opr - OPR_NE) + OP_EQ);
|
|
swapexps(e1, e2);
|
|
codeorder(fs, op, e1, e2);
|
|
break;
|
|
}
|
|
default: lua_assert(0);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
** Change line information associated with current position, by removing
|
|
** previous info and adding it again with new line.
|
|
*/
|
|
void luaK_fixline (FuncState *fs, int line) {
|
|
removelastlineinfo(fs);
|
|
savelineinfo(fs, fs->f, line);
|
|
}
|
|
|
|
|
|
void luaK_settablesize (FuncState *fs, int pc, int ra, int rc, int rb) {
|
|
Instruction *inst = &fs->f->code[pc];
|
|
int extra = 0;
|
|
int k = 0;
|
|
if (rb != 0)
|
|
rb = luaO_ceillog2(rb) + 1; /* hash size */
|
|
if (rc > MAXARG_C) { /* does it need the extra argument? */
|
|
extra = rc / (MAXARG_C + 1);
|
|
rc %= (MAXARG_C + 1);
|
|
k = 1;
|
|
}
|
|
*inst = CREATE_ABCk(OP_NEWTABLE, ra, rb, rc, k);
|
|
*(inst + 1) = CREATE_Ax(OP_EXTRAARG, extra);
|
|
}
|
|
|
|
|
|
/*
|
|
** Emit a SETLIST instruction.
|
|
** 'base' is register that keeps table;
|
|
** 'nelems' is #table plus those to be stored now;
|
|
** 'tostore' is number of values (in registers 'base + 1',...) to add to
|
|
** table (or LUA_MULTRET to add up to stack top).
|
|
*/
|
|
void luaK_setlist (FuncState *fs, int base, int nelems, int tostore) {
|
|
lua_assert(tostore != 0 && tostore <= LFIELDS_PER_FLUSH);
|
|
if (tostore == LUA_MULTRET)
|
|
tostore = 0;
|
|
if (nelems <= MAXARG_C)
|
|
luaK_codeABC(fs, OP_SETLIST, base, tostore, nelems);
|
|
else {
|
|
int extra = nelems / (MAXARG_C + 1);
|
|
nelems %= (MAXARG_C + 1);
|
|
luaK_codeABCk(fs, OP_SETLIST, base, tostore, nelems, 1);
|
|
codeextraarg(fs, extra);
|
|
}
|
|
fs->freereg = base + 1; /* free registers with list values */
|
|
}
|
|
|
|
|
|
/*
|
|
** return the final target of a jump (skipping jumps to jumps)
|
|
*/
|
|
static int finaltarget (Instruction *code, int i) {
|
|
int count;
|
|
for (count = 0; count < 100; count++) { /* avoid infinite loops */
|
|
Instruction pc = code[i];
|
|
if (GET_OPCODE(pc) != OP_JMP)
|
|
break;
|
|
else
|
|
i += GETARG_sJ(pc) + 1;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
|
|
/*
|
|
** Do a final pass over the code of a function, doing small peephole
|
|
** optimizations and adjustments.
|
|
*/
|
|
void luaK_finish (FuncState *fs) {
|
|
int i;
|
|
Proto *p = fs->f;
|
|
for (i = 0; i < fs->pc; i++) {
|
|
Instruction *pc = &p->code[i];
|
|
lua_assert(i == 0 || isOT(*(pc - 1)) == isIT(*pc));
|
|
switch (GET_OPCODE(*pc)) {
|
|
case OP_RETURN0: case OP_RETURN1: {
|
|
if (!(fs->needclose || p->is_vararg))
|
|
break; /* no extra work */
|
|
/* else use OP_RETURN to do the extra work */
|
|
SET_OPCODE(*pc, OP_RETURN);
|
|
} /* FALLTHROUGH */
|
|
case OP_RETURN: case OP_TAILCALL: {
|
|
if (fs->needclose)
|
|
SETARG_k(*pc, 1); /* signal that it needs to close */
|
|
if (p->is_vararg)
|
|
SETARG_C(*pc, p->numparams + 1); /* signal that it is vararg */
|
|
break;
|
|
}
|
|
case OP_JMP: {
|
|
int target = finaltarget(p->code, i);
|
|
fixjump(fs, i, target);
|
|
break;
|
|
}
|
|
default: break;
|
|
}
|
|
}
|
|
}
|