Browse Source

Added opcodes for arithmetic with K operands

Added opcodes for all seven arithmetic operators with K operands
(that is, operands that are numbers in the array of constants of
the function). They cover the cases of constant float operands
(e.g., 'x + .0.0', 'x^0.5') and large integer operands (e.g.,
'x % 10000').
pull/21/head
Roberto Ierusalimschy 6 years ago
parent
commit
84e32ad2eb
  1. 126
      lcode.c
  2. 10
      ldebug.c
  3. 7
      ljumptab.h
  4. 7
      lopcodes.c
  5. 10
      lopcodes.h
  6. 7
      lopnames.h
  7. 1
      ltests.h
  8. 61
      lvm.c
  9. 82
      testes/code.lua
  10. 2
      testes/db.lua
  11. 2
      testes/events.lua

126
lcode.c

@ -371,10 +371,6 @@ int luaK_codeABCk (FuncState *fs, OpCode o, int a, int b, int c, int k) {
}
#define codeABsC(fs,o,a,b,c,k) luaK_codeABCk(fs,o,a,b,((c) + OFFSET_sC),k)
/*
** Format and emit an 'iABx' instruction.
*/
@ -884,31 +880,46 @@ void luaK_exp2val (FuncState *fs, expdesc *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 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 if expression is K, 0 otherwise.
** Returns 1 iff expression is K.
*/
int luaK_exp2RK (FuncState *fs, expdesc *e) {
luaK_exp2val(fs, e);
switch (e->k) { /* move constants to 'k' */
case VTRUE: e->u.info = boolK(fs, 1); goto vk;
case VFALSE: e->u.info = boolK(fs, 0); goto vk;
case VNIL: e->u.info = nilK(fs); goto vk;
case VKINT: e->u.info = luaK_intK(fs, e->u.ival); goto vk;
case VKFLT: e->u.info = luaK_numberK(fs, e->u.nval); goto vk;
case VK:
vk:
e->k = VK;
if (e->u.info <= MAXINDEXRK) /* constant fits in 'argC'? */
return 1;
else break;
default: break;
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;
}
/* not a constant in the right range: put it in a register */
luaK_exp2anyreg(fs, e);
return 0;
}
@ -1232,8 +1243,19 @@ static void codeunexpval (FuncState *fs, OpCode op, expdesc *e, int 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,
int pc, int line) {
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 */
@ -1242,20 +1264,13 @@ static void finishbinexpval (FuncState *fs, expdesc *e1, expdesc *e2,
/*
** 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).
** 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 */
int v1 = luaK_exp2anyreg(fs, e1);
int pc = luaK_codeABC(fs, op, 0, v1, v2); /* generate opcode */
finishbinexpval(fs, e1, e2, pc, line);
finishbinexpval(fs, e1, e2, op, v2, 0, line);
}
@ -1264,41 +1279,48 @@ static void codebinexpval (FuncState *fs, OpCode op,
*/
static void codebini (FuncState *fs, OpCode op,
expdesc *e1, expdesc *e2, int k, int line) {
int v2 = cast_int(e2->u.ival); /* immediate operand */
int v1 = luaK_exp2anyreg(fs, e1);
int pc = codeABsC(fs, op, 0, v1, v2, k); /* generate opcode */
finishbinexpval(fs, e1, e2, pc, 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.
** operands or K operands.
*/
static void codearith (FuncState *fs, OpCode op,
expdesc *e1, expdesc *e2, int flip, int line) {
if (!isSCint(e2))
codebinexpval(fs, op, e1, e2, line); /* use standard operators */
else /* use immediate operators */
if (isSCint(e2)) /* immediate operand? */
codebini(fs, cast(OpCode, op - OP_ADD + OP_ADDI), e1, e2, flip, line);
}
static void swapexps (expdesc *e1, expdesc *e2) {
expdesc temp = *e1; *e1 = *e2; *e2 = temp; /* swap 'e1' and 'e2' */
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
** constant, change order of operands to use immediate operator.
** 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 (isSCint(e1)) {
swapexps(e1, e2);
if (tonumeral(e1, NULL)) { /* is first operand a numeric constant? */
swapexps(e1, e2); /* change order */
flip = 1;
}
codearith(fs, op, e1, e2, flip, line);
@ -1312,7 +1334,7 @@ static void codecommutative (FuncState *fs, OpCode op,
static void codebitwise (FuncState *fs, BinOpr opr,
expdesc *e1, expdesc *e2, int line) {
int inv = 0;
int v1, v2, pc;
int v2;
OpCode op;
if (e1->k == VKINT && luaK_exp2RK(fs, e1)) {
swapexps(e1, e2); /* 'e2' will be the constant operand */
@ -1323,12 +1345,10 @@ static void codebitwise (FuncState *fs, BinOpr opr,
codebinexpval(fs, op, e1, e2, line); /* all-register opcodes */
return;
}
v1 = luaK_exp2anyreg(fs, e1);
v2 = e2->u.info; /* index in K array */
op = cast(OpCode, opr - OPR_BAND + OP_BANDK);
lua_assert(ttisinteger(&fs->f->k[v2]));
pc = luaK_codeABCk(fs, op, 0, v1, v2, inv);
finishbinexpval(fs, e1, e2, pc, line);
finishbinexpval(fs, e1, e2, op, v2, inv, line);
}

10
ldebug.c

@ -610,12 +610,18 @@ static const char *funcnamefromcode (lua_State *L, CallInfo *ci,
tm = TM_NEWINDEX;
break;
case OP_ADDI: case OP_SUBI: case OP_MULI: case OP_MODI:
case OP_POWI: case OP_DIVI: case OP_IDIVI:
case OP_BANDK: case OP_BORK: case OP_BXORK: {
case OP_POWI: case OP_DIVI: case OP_IDIVI: {
int offset = GET_OPCODE(i) - OP_ADDI; /* ORDER OP */
tm = cast(TMS, offset + TM_ADD); /* ORDER TM */
break;
}
case OP_ADDK: case OP_SUBK: case OP_MULK: case OP_MODK:
case OP_POWK: case OP_DIVK: case OP_IDIVK:
case OP_BANDK: case OP_BORK: case OP_BXORK: {
int offset = GET_OPCODE(i) - OP_ADDK; /* ORDER OP */
tm = cast(TMS, offset + TM_ADD); /* ORDER TM */
break;
}
case OP_ADD: case OP_SUB: case OP_MUL: 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: {

7
ljumptab.h

@ -51,6 +51,13 @@ static void *disptab[] = {
&&L_OP_POWI,
&&L_OP_DIVI,
&&L_OP_IDIVI,
&&L_OP_ADDK,
&&L_OP_SUBK,
&&L_OP_MULK,
&&L_OP_MODK,
&&L_OP_POWK,
&&L_OP_DIVK,
&&L_OP_IDIVK,
&&L_OP_BANDK,
&&L_OP_BORK,
&&L_OP_BXORK,

7
lopcodes.c

@ -45,6 +45,13 @@ LUAI_DDEF const lu_byte luaP_opmodes[NUM_OPCODES] = {
,opmode(0, 0, 0, 1, iABC) /* OP_POWI */
,opmode(0, 0, 0, 1, iABC) /* OP_DIVI */
,opmode(0, 0, 0, 1, iABC) /* OP_IDIVI */
,opmode(0, 0, 0, 1, iABC) /* OP_ADDK */
,opmode(0, 0, 0, 1, iABC) /* OP_SUBK */
,opmode(0, 0, 0, 1, iABC) /* OP_MULK */
,opmode(0, 0, 0, 1, iABC) /* OP_MODK */
,opmode(0, 0, 0, 1, iABC) /* OP_POWK */
,opmode(0, 0, 0, 1, iABC) /* OP_DIVK */
,opmode(0, 0, 0, 1, iABC) /* OP_IDIVK */
,opmode(0, 0, 0, 1, iABC) /* OP_BANDK */
,opmode(0, 0, 0, 1, iABC) /* OP_BORK */
,opmode(0, 0, 0, 1, iABC) /* OP_BXORK */

10
lopcodes.h

@ -221,6 +221,14 @@ OP_POWI,/* A B sC R(A) := R(B) ^ C */
OP_DIVI,/* A B sC R(A) := R(B) / C */
OP_IDIVI,/* A B sC R(A) := R(B) // C */
OP_ADDK,/* A B C R(A) := R(B) + K(C) */
OP_SUBK,/* A B C R(A) := R(B) - K(C) */
OP_MULK,/* A B C R(A) := R(B) * K(C) */
OP_MODK,/* A B C R(A) := R(B) % K(C) */
OP_POWK,/* A B C R(A) := R(B) ^ K(C) */
OP_DIVK,/* A B C R(A) := R(B) / K(C) */
OP_IDIVK,/* A B C R(A) := R(B) // K(C) */
OP_BANDK,/* A B C R(A) := R(B) & K(C):integer */
OP_BORK,/* A B C R(A) := R(B) | K(C):integer */
OP_BXORK,/* A B C R(A) := R(B) ~ K(C):integer */
@ -235,11 +243,13 @@ OP_MOD,/* A B C R(A) := R(B) % R(C) */
OP_POW,/* A B C R(A) := R(B) ^ R(C) */
OP_DIV,/* A B C R(A) := R(B) / R(C) */
OP_IDIV,/* A B C R(A) := R(B) // R(C) */
OP_BAND,/* A B C R(A) := R(B) & R(C) */
OP_BOR,/* A B C R(A) := R(B) | R(C) */
OP_BXOR,/* A B C R(A) := R(B) ~ R(C) */
OP_SHL,/* A B C R(A) := R(B) << R(C) */
OP_SHR,/* A B C R(A) := R(B) >> R(C) */
OP_UNM,/* A B R(A) := -R(B) */
OP_BNOT,/* A B R(A) := ~R(B) */
OP_NOT,/* A B R(A) := not R(B) */

7
lopnames.h

@ -36,6 +36,13 @@ static const char *const opnames[] = {
"POWI",
"DIVI",
"IDIVI",
"ADDK",
"SUBK",
"MULK",
"MODK",
"POWK",
"DIVK",
"IDIVK",
"BANDK",
"BORK",
"BXORK",

1
ltests.h

@ -119,7 +119,6 @@ LUA_API void *debug_realloc (void *ud, void *block,
#undef LUAL_BUFFERSIZE
#define LUAL_BUFFERSIZE 23
#define MINSTRTABSIZE 2
#define MAXINDEXRK 1
#define MAXIWTHABS 3

61
lvm.c

@ -855,6 +855,39 @@ void luaV_finishOp (lua_State *L) {
else op_arithf_aux(L, v1, v2, fop, tm); }
/*
** Arithmetic operations with K operands.
*/
#define op_arithK(L,iop,fop,tm,flip) { \
TValue *v1 = vRB(i); \
TValue *v2 = KC(i); \
if (ttisinteger(v1) && ttisinteger(v2)) { \
lua_Integer i1 = ivalue(v1); lua_Integer i2 = ivalue(v2); \
setivalue(s2v(ra), iop(L, i1, i2)); \
} \
else { \
lua_Number n1; lua_Number n2; \
if (tonumberns(v1, n1) && tonumberns(v2, n2)) { \
setfltvalue(s2v(ra), fop(L, n1, n2)); \
} \
else \
Protect(luaT_trybinassocTM(L, v1, v2, ra, flip, tm)); } }
/*
** Arithmetic operations with K operands for floats.
*/
#define op_arithfK(L,fop,tm) { \
TValue *v1 = vRB(i); \
TValue *v2 = KC(i); \
lua_Number n1; lua_Number n2; \
if (tonumberns(v1, n1) && tonumberns(v2, n2)) { \
setfltvalue(s2v(ra), fop(L, n1, n2)); \
} \
else \
Protect(luaT_trybinTM(L, v1, v2, ra, tm)); }
/*
** Bitwise operations with constant operand.
*/
@ -1219,6 +1252,34 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
op_arithI(L, luaV_idiv, luai_numidiv, TM_IDIV, 0);
vmbreak;
}
vmcase(OP_ADDK) {
op_arithK(L, l_addi, luai_numadd, TM_ADD, GETARG_k(i));
vmbreak;
}
vmcase(OP_SUBK) {
op_arithK(L, l_subi, luai_numsub, TM_SUB, 0);
vmbreak;
}
vmcase(OP_MULK) {
op_arithK(L, l_muli, luai_nummul, TM_MUL, GETARG_k(i));
vmbreak;
}
vmcase(OP_MODK) {
op_arithK(L, luaV_mod, luaV_modf, TM_MOD, 0);
vmbreak;
}
vmcase(OP_POWK) {
op_arithfK(L, luai_numpow, TM_POW);
vmbreak;
}
vmcase(OP_DIVK) {
op_arithfK(L, luai_numdiv, TM_DIV);
vmbreak;
}
vmcase(OP_IDIVK) {
op_arithK(L, luaV_idiv, luai_numidiv, TM_IDIV, 0);
vmbreak;
}
vmcase(OP_ADD) {
op_arith(L, l_addi, luai_numadd, TM_ADD);
vmbreak;

82
testes/code.lua

@ -40,6 +40,7 @@ checkKlist(foo, {3.78/4, -3.78/4, -3.79/4})
-- testing opcodes
-- check that 'f' opcodes match '...'
function check (f, ...)
local arg = {...}
local c = T.listcode(f)
@ -52,9 +53,19 @@ function check (f, ...)
end
-- check that 'f' opcodes match '...' and that 'f(p) == r'.
function checkR (f, p, r, ...)
local r1 = f(p)
assert(r == r1 and math.type(r) == math.type(r1))
check(f, ...)
end
-- check that 'a' and 'b' has the same opcodes
function checkequal (a, b)
a = T.listcode(a)
b = T.listcode(b)
assert(#a == #b)
for i = 1, #a do
a[i] = string.gsub(a[i], '%b()', '') -- remove line number
b[i] = string.gsub(b[i], '%b()', '') -- remove line number
@ -165,65 +176,64 @@ end,
-- equalities
check(function (a) if a == 1 then return 2 end end,
checkR(function (a) if a == 1 then return 2 end end, 1, 2,
'EQI', 'JMP', 'LOADI', 'RETURN1')
check(function (a) if -4.0 == a then return 2 end end,
checkR(function (a) if -4.0 == a then return 2 end end, -4, 2,
'EQI', 'JMP', 'LOADI', 'RETURN1')
check(function (a) if a == "hi" then return 2 end end,
checkR(function (a) if a == "hi" then return 2 end end, 10, nil,
'EQK', 'JMP', 'LOADI', 'RETURN1')
check(function (a) if a == 10000 then return 2 end end,
checkR(function (a) if a == 10000 then return 2 end end, 1, nil,
'EQK', 'JMP', 'LOADI', 'RETURN1') -- number too large
check(function (a) if -10000 == a then return 2 end end,
checkR(function (a) if -10000 == a then return 2 end end, -10000, 2,
'EQK', 'JMP', 'LOADI', 'RETURN1') -- number too large
-- comparisons
check(function (a) if -10 <= a then return 2 end end,
checkR(function (a) if -10 <= a then return 2 end end, -10, 2,
'GEI', 'JMP', 'LOADI', 'RETURN1')
check(function (a) if 128.0 > a then return 2 end end,
checkR(function (a) if 128.0 > a then return 2 end end, 129, nil,
'LTI', 'JMP', 'LOADI', 'RETURN1')
check(function (a) if -127.0 < a then return 2 end end,
checkR(function (a) if -127.0 < a then return 2 end end, -127, nil,
'GTI', 'JMP', 'LOADI', 'RETURN1')
check(function (a) if 10 < a then return 2 end end,
checkR(function (a) if 10 < a then return 2 end end, 11, 2,
'GTI', 'JMP', 'LOADI', 'RETURN1')
check(function (a) if 129 < a then return 2 end end,
checkR(function (a) if 129 < a then return 2 end end, 130, 2,
'LOADI', 'LT', 'JMP', 'LOADI', 'RETURN1')
check(function (a) if a >= 23.0 then return 2 end end,
checkR(function (a) if a >= 23.0 then return 2 end end, 25, 2,
'GEI', 'JMP', 'LOADI', 'RETURN1')
check(function (a) if a >= 23.1 then return 2 end end,
checkR(function (a) if a >= 23.1 then return 2 end end, 0, nil,
'LOADK', 'LE', 'JMP', 'LOADI', 'RETURN1')
check(function (a) if a > 2300.0 then return 2 end end,
checkR(function (a) if a > 2300.0 then return 2 end end, 0, nil,
'LOADF', 'LT', 'JMP', 'LOADI', 'RETURN1')
-- constant folding
local function checkK (func, val)
check(func, 'LOADK', 'RETURN1')
local k = T.listk(func)
assert(#k == 1 and k[1] == val and math.type(k[1]) == math.type(val))
checkKlist(func, {val})
assert(func() == val)
end
local function checkI (func, val)
check(func, 'LOADI', 'RETURN1')
assert(#T.listk(func) == 0)
checkKlist(func, {})
assert(func() == val)
end
local function checkF (func, val)
check(func, 'LOADF', 'RETURN1')
assert(#T.listk(func) == 0)
checkKlist(func, {})
assert(func() == val)
end
@ -258,20 +268,30 @@ checkK(function () return -65536.0 end, -(sbx + 1.0))
-- immediate operands
check(function (x) return x + 1 end, 'ADDI', 'RETURN1')
check(function (x) return 128 + x end, 'ADDI', 'RETURN1')
check(function (x) return x * -127 end, 'MULI', 'RETURN1')
check(function (x) return 20 * x end, 'MULI', 'RETURN1')
check(function (x) return x ^ -2 end, 'POWI', 'RETURN1')
check(function (x) return x / 40 end, 'DIVI', 'RETURN1')
check(function (x) return x // 1 end, 'IDIVI', 'RETURN1')
check(function (x) return x % (100 - 10) end, 'MODI', 'RETURN1')
check(function (x) return 1 << x end, 'SHLI', 'RETURN1')
check(function (x) return x << 2 end, 'SHRI', 'RETURN1')
check(function (x) return x >> 2 end, 'SHRI', 'RETURN1')
check(function (x) return x & 1 end, 'BANDK', 'RETURN1')
check(function (x) return 10 | x end, 'BORK', 'RETURN1')
check(function (x) return -10 ~ x end, 'BXORK', 'RETURN1')
checkR(function (x) return x + 1 end, 10, 11, 'ADDI', 'RETURN1')
checkR(function (x) return 128 + x end, 0.0, 128.0, 'ADDI', 'RETURN1')
checkR(function (x) return x * -127 end, -1.0, 127.0, 'MULI', 'RETURN1')
checkR(function (x) return 20 * x end, 2, 40, 'MULI', 'RETURN1')
checkR(function (x) return x ^ -2 end, 2, 0.25, 'POWI', 'RETURN1')
checkR(function (x) return x / 40 end, 40, 1.0, 'DIVI', 'RETURN1')
checkR(function (x) return x // 1 end, 10.0, 10.0, 'IDIVI', 'RETURN1')
checkR(function (x) return x % (100 - 10) end, 91, 1, 'MODI', 'RETURN1')
checkR(function (x) return 1 << x end, 3, 8, 'SHLI', 'RETURN1')
checkR(function (x) return x << 2 end, 10, 40, 'SHRI', 'RETURN1')
checkR(function (x) return x >> 2 end, 8, 2, 'SHRI', 'RETURN1')
checkR(function (x) return x & 1 end, 9, 1, 'BANDK', 'RETURN1')
checkR(function (x) return 10 | x end, 1, 11, 'BORK', 'RETURN1')
checkR(function (x) return -10 ~ x end, -1, 9, 'BXORK', 'RETURN1')
-- K operands in arithmetic operations
checkR(function (x) return x + 0.0 end, 1, 1.0, 'ADDK', 'RETURN1')
-- check(function (x) return 128 + x end, 'ADDK', 'RETURN1')
checkR(function (x) return x * -10000 end, 2, -20000, 'MULK', 'RETURN1')
-- check(function (x) return 20 * x end, 'MULK', 'RETURN1')
checkR(function (x) return x ^ 0.5 end, 4, 2.0, 'POWK', 'RETURN1')
checkR(function (x) return x / 2.0 end, 4, 2.0, 'DIVK', 'RETURN1')
checkR(function (x) return x // 10000 end, 10000, 1, 'IDIVK', 'RETURN1')
checkR(function (x) return x % (100.0 - 10) end, 91, 1.0, 'MODK', 'RETURN1')
-- no foldings (and immediate operands)
check(function () return -0.0 end, 'LOADF', 'UNM', 'RETURN1')

2
testes/db.lua

@ -794,6 +794,8 @@ assert(a[3] == "index" and a^3 == "pow" and a..a == "concat")
assert(a/3 == "div" and 3%a == "mod")
assert(a+3 == "add" and 3-a == "sub" and a*3 == "mul" and
-a == "unm" and #a == "len" and a&3 == "band")
assert(a + 30000 == "add" and a - 3.0 == "sub" and a * 3.0 == "mul" and
-a == "unm" and #a == "len" and a & 3 == "band")
assert(a|3 == "bor" and 3~a == "bxor" and a<<3 == "shift" and
a>>1 == "shift")
assert (a==b and a.op == "eq")

2
testes/events.lua

@ -144,6 +144,8 @@ t.__bnot = f("bnot")
-- when the constant table is very small.
assert(b+5 == b)
assert(cap[0] == "add" and cap[1] == b and cap[2] == 5 and cap[3]==undef)
assert(5.2 + b == 5.2)
assert(cap[0] == "add" and cap[1] == 5.2 and cap[2] == b and cap[3]==undef)
assert(b+'5' == b)
assert(cap[0] == "add" and cap[1] == b and cap[2] == '5' and cap[3]==undef)
assert(5+b == 5)

Loading…
Cancel
Save