Files
CMLeonOS/interpreter/UniLua/Coder.cs
2026-02-04 17:50:50 +08:00

1284 lines
30 KiB
C#

using NotImplementedException = System.NotImplementedException;
namespace UniLua
{
using InstructionPtr = Pointer<Instruction>;
using Math = System.Math;
using Exception = System.Exception;
public struct Instruction
{
public uint Value;
public Instruction( uint val )
{
Value = val;
}
public static explicit operator Instruction( uint val )
{
return new Instruction(val);
}
public static explicit operator uint( Instruction i )
{
return i.Value;
}
public override string ToString()
{
var op = GET_OPCODE();
var a = GETARG_A();
var b = GETARG_B();
var c = GETARG_C();
var ax = GETARG_Ax();
var bx = GETARG_Bx();
var sbx = GETARG_sBx();
var mode = OpCodeInfo.GetMode( op );
switch( mode.OpMode )
{
case OpMode.iABC:
{
string ret = string.Format( "{0,-9} {1}"
, op
, a );
if( mode.BMode != OpArgMask.OpArgN )
ret += " " + (ISK(b) ? MYK(INDEXK(b)) : b);
if( mode.CMode != OpArgMask.OpArgN )
ret += " " + (ISK(c) ? MYK(INDEXK(c)) : c);
return ret;
}
case OpMode.iABx:
{
string ret = string.Format( "{0,-9} {1}"
, op
, a );
if( mode.BMode == OpArgMask.OpArgK )
ret += " " + MYK(bx);
else if( mode.BMode == OpArgMask.OpArgU )
ret += " " + bx;
return ret;
}
case OpMode.iAsBx:
{
return string.Format( "{0,-9} {1} {2}"
, op
, a
, sbx );
}
case OpMode.iAx:
{
return string.Format( "{0,-9} {1}"
, op
, MYK(ax) );
}
default:
throw new System.NotImplementedException();
}
}
public const int SIZE_C = 9;
public const int SIZE_B = 9;
public const int SIZE_Bx = (SIZE_C + SIZE_B);
public const int SIZE_A = 8;
public const int SIZE_Ax = (SIZE_C + SIZE_B + SIZE_A);
public const int SIZE_OP = 6;
public const int POS_OP = 0;
public const int POS_A = (POS_OP + SIZE_OP);
public const int POS_C = (POS_A + SIZE_A);
public const int POS_B = (POS_C + SIZE_C);
public const int POS_Bx = POS_C;
public const int POS_Ax = POS_A;
#pragma warning disable 0429
public const int MAXARG_Bx = SIZE_Bx<LuaConf.LUAI_BITSINT
? ((1<<SIZE_Bx)-1)
: LuaLimits.MAX_INT
;
public const int MAXARG_sBx = SIZE_Bx<LuaConf.LUAI_BITSINT
? (MAXARG_Bx>>1)
: LuaLimits.MAX_INT
;
#pragma warning restore 0429
public const int MAXARG_Ax = ((1<<SIZE_Ax) - 1);
public const int MAXARG_A = ((1<<SIZE_A) - 1);
public const int MAXARG_B = ((1<<SIZE_B) - 1);
public const int MAXARG_C = ((1<<SIZE_C) - 1);
public const int BITRK = (1 << (SIZE_B - 1));
public const int MAXINDEXRK = (BITRK - 1);
public static int RKASK( int x )
{
return (x | BITRK );
}
public static bool ISK( int x )
{
return ((x) & BITRK ) != 0;
}
public static int INDEXK( int r )
{
return ((int)r & ~BITRK );
}
public static int MYK( int x )
{
return (-1-x);
}
public static uint MASK1( int size, int pos )
{
return ((~((~((uint)0)) << size)) << pos);
}
public static uint MASK0( int size, int pos )
{
return (~MASK1(size, pos));
}
public OpCode GET_OPCODE()
{
return (OpCode)( (Value >> POS_OP) & MASK1(SIZE_OP, 0) );
}
public Instruction SET_OPCODE( OpCode op )
{
Value = (Value & MASK0(SIZE_OP, POS_OP)) |
((((uint)op) << POS_OP) & MASK1(SIZE_OP, POS_OP));
return this;
}
public int GETARG( int pos, int size )
{
return (int)( (Value >> pos) & MASK1(size, 0) );
}
public Instruction SETARG( int value, int pos, int size )
{
Value = ((Value & MASK0(size, pos)) |
(((uint)value << pos) & MASK1(size, pos)));
return this;
}
public int GETARG_A() { return GETARG( POS_A, SIZE_A ); }
public Instruction SETARG_A(int value) {
return SETARG( value, POS_A, SIZE_A );
}
public int GETARG_B() { return GETARG( POS_B, SIZE_B ); }
public Instruction SETARG_B(int value) {
return SETARG( value, POS_B, SIZE_B );
}
public int GETARG_C() { return GETARG( POS_C, SIZE_C ); }
public Instruction SETARG_C(int value) {
return SETARG( value, POS_C, SIZE_C );
}
public int GETARG_Bx() { return GETARG( POS_Bx, SIZE_Bx ); }
public Instruction SETARG_Bx(int value) {
return SETARG( value, POS_Bx, SIZE_Bx );
}
public int GETARG_Ax() { return GETARG( POS_Ax, SIZE_Ax ); }
public Instruction SETARG_Ax(int value) {
return SETARG( value, POS_Ax, SIZE_Ax );
}
public int GETARG_sBx() { return GETARG_Bx() - MAXARG_sBx; }
public Instruction SETARG_sBx(int value) {
return SETARG_Bx(value+MAXARG_sBx);
}
public static Instruction CreateABC( OpCode op, int a, int b, int c )
{
return (Instruction)( (((uint)op) << POS_OP)
| ((uint)a << POS_A)
| ((uint)b << POS_B)
| ((uint)c << POS_C));
}
public static Instruction CreateABx( OpCode op, int a, uint bc )
{
return (Instruction)( (((uint)op) << POS_OP)
| ((uint)a << POS_A)
| ((uint)bc << POS_Bx));
}
public static Instruction CreateAx( OpCode op, int a )
{
return (Instruction)( (((uint)op) << POS_OP)
| ((uint)a << POS_Ax));
}
}
public static class Coder
{
public const int NO_JUMP = -1;
private const int NO_REG = ((1<<Instruction.SIZE_A) - 1);
private static void FreeReg( FuncState fs, int reg )
{
if( !Instruction.ISK(reg) && reg >= fs.NumActVar )
{
fs.FreeReg--;
Utl.Assert( reg == fs.FreeReg );
}
}
private static void FreeExp( FuncState fs, ExpDesc e )
{
if( e.Kind == ExpKind.VNONRELOC )
{
FreeReg( fs, e.Info );
}
}
private static bool IsNumeral( ExpDesc e )
{
return e.Kind == ExpKind.VKNUM
&& e.ExitTrue == NO_JUMP
&& e.ExitFalse == NO_JUMP;
}
private static bool ConstFolding( OpCode op, ExpDesc e1, ExpDesc e2 )
{
if( !IsNumeral(e1) || !IsNumeral(e2) )
return false;
if( (op == OpCode.OP_DIV || op == OpCode.OP_MOD)
&& e2.NumberValue == 0.0 )
{
return false; // do not attempt to divide by 0
}
switch( op )
{
case OpCode.OP_ADD:
e1.NumberValue = e1.NumberValue + e2.NumberValue;
break;
case OpCode.OP_SUB:
e1.NumberValue = e1.NumberValue - e2.NumberValue;
break;
case OpCode.OP_MUL:
e1.NumberValue = e1.NumberValue * e2.NumberValue;
break;
case OpCode.OP_DIV:
e1.NumberValue = e1.NumberValue / e2.NumberValue;
break;
case OpCode.OP_MOD:
var v1 = e1.NumberValue;
var v2 = e2.NumberValue;
e1.NumberValue = v1 - Math.Floor(v1/v2) * v2;
break;
case OpCode.OP_POW:
e1.NumberValue = Math.Pow( e1.NumberValue, e2.NumberValue );
break;
case OpCode.OP_UNM:
e1.NumberValue = -e1.NumberValue;
break;
default:
throw new Exception("ConstFolding unknown op" + op);
}
return true;
}
public static void FixLine( FuncState fs, int line )
{
fs.Proto.LineInfo[ fs.Pc-1 ] = line;
}
private static void CodeArith( FuncState fs, OpCode op,
ExpDesc e1, ExpDesc e2, int line )
{
if( ConstFolding( op, e1, e2 ) )
return;
int o2 = ( op != OpCode.OP_UNM && op != OpCode.OP_LEN )
? Exp2RK( fs, e2 ) : 0;
int o1 = Exp2RK( fs, e1 );
if( o1 > o2 )
{
FreeExp( fs, e1 );
FreeExp( fs, e2 );
}
else
{
FreeExp( fs, e2 );
FreeExp( fs, e1 );
}
e1.Info = CodeABC( fs, op, 0, o1, o2 );
e1.Kind = ExpKind.VRELOCABLE;
FixLine( fs, line );
}
public static bool TestTMode( OpCode op )
{
return OpCodeInfo.GetMode( op ).TMode;
}
public static bool TestAMode( OpCode op )
{
return OpCodeInfo.GetMode( op ).AMode;
}
private static void FixJump( FuncState fs, int pc, int dest )
{
Instruction jmp = fs.Proto.Code[pc];
int offset = dest - (pc + 1);
Utl.Assert( dest != NO_JUMP );
if( Math.Abs(offset) > Instruction.MAXARG_sBx )
fs.Lexer.SyntaxError("control structure too long");
jmp.SETARG_sBx( offset );
fs.Proto.Code[pc] = jmp;
}
// returns current `pc' and mark it as a jump target
// (to avoid wrong optimizations with consecutive
// instructions not in the same basic block)
public static int GetLabel( FuncState fs )
{
fs.LastTarget = fs.Pc;
return fs.Pc;
}
private static int GetJump( FuncState fs, int pc )
{
int offset = fs.Proto.Code[pc].GETARG_sBx();
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
}
private static InstructionPtr GetJumpControl( FuncState fs, int pc )
{
InstructionPtr pi = new InstructionPtr( fs.Proto.Code, pc );
if( pc >= 1 && TestTMode( (pi-1).Value.GET_OPCODE() ))
return (pi-1);
else
return pi;
}
// check whether list has any jump that do not produce a value
// (or produce an inverted value)
private static bool NeedValue( FuncState fs, int list )
{
for( ; list != NO_JUMP; list = GetJump( fs, list ) )
{
Instruction i = GetJumpControl( fs, list ).Value;
if( i.GET_OPCODE() != OpCode.OP_TESTSET )
return true;
}
return false;
}
private static bool PatchTestReg( FuncState fs, int node, int reg )
{
InstructionPtr pi = GetJumpControl( fs, node );
if( pi.Value.GET_OPCODE() != OpCode.OP_TESTSET )
return false; // cannot patch other instructions
if( reg != NO_REG && reg != pi.Value.GETARG_B() )
pi.Value = pi.Value.SETARG_A( reg );
else
pi.Value = Instruction.CreateABC( OpCode.OP_TEST,
pi.Value.GETARG_B(), 0, pi.Value.GETARG_C() );
return true;
}
private static void RemoveValues( FuncState fs, int list )
{
for(; list != NO_JUMP; list = GetJump( fs, list ) )
PatchTestReg( fs, list, NO_REG );
}
private 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;
}
}
private static void DischargeJpc( FuncState fs )
{
PatchListAux( fs, fs.Jpc, fs.Pc, NO_REG, fs.Pc );
fs.Jpc = NO_JUMP;
}
private static void InvertJump( FuncState fs, ExpDesc e )
{
InstructionPtr pc = GetJumpControl( fs, e.Info );
Utl.Assert( TestTMode( pc.Value.GET_OPCODE() )
&& pc.Value.GET_OPCODE() != OpCode.OP_TESTSET
&& pc.Value.GET_OPCODE() != OpCode.OP_TEST );
pc.Value = pc.Value.SETARG_A( pc.Value.GETARG_A() == 0 ? 1 : 0 );
}
private static int JumpOnCond( FuncState fs, ExpDesc e, bool cond )
{
if( e.Kind == ExpKind.VRELOCABLE )
{
Instruction ie = fs.GetCode( e ).Value;
if( ie.GET_OPCODE() == OpCode.OP_NOT )
{
fs.Pc--; // remove previous OP_NOT
return CondJump( fs, OpCode.OP_TEST, ie.GETARG_B(), 0,
(cond ? 0 : 1) );
}
// else go through
}
Discharge2AnyReg( fs, e );
FreeExp( fs, e );
return CondJump( fs, OpCode.OP_TESTSET, NO_REG, e.Info,
(cond ? 1 : 0) );
}
public static void GoIfTrue( FuncState fs, ExpDesc e )
{
int pc; // pc of last jump
DischargeVars( fs, e );
switch( e.Kind )
{
case ExpKind.VJMP:
InvertJump( fs, e );
pc = e.Info;
break;
case ExpKind.VK:
case ExpKind.VKNUM:
case ExpKind.VTRUE:
pc = NO_JUMP;
break;
default:
pc = JumpOnCond( fs, e, false );
break;
}
// insert last jump in `f' list
e.ExitFalse = Concat( fs, e.ExitFalse, pc );
PatchToHere( fs, e.ExitTrue );
e.ExitTrue = NO_JUMP;
}
public static void GoIfFalse( FuncState fs, ExpDesc e )
{
int pc; // pc of last jump
DischargeVars( fs, e );
switch( e.Kind )
{
case ExpKind.VJMP:
pc = e.Info;
break;
case ExpKind.VNIL:
case ExpKind.VFALSE:
pc = NO_JUMP;
break;
default:
pc = JumpOnCond( fs, e, true );
break;
}
// insert last jump in `t' list
e.ExitTrue = Concat( fs, e.ExitTrue, pc );
PatchToHere( fs, e.ExitFalse );
e.ExitFalse = NO_JUMP;
}
private static void CodeNot( FuncState fs, ExpDesc e )
{
DischargeVars( fs, e );
switch( e.Kind )
{
case ExpKind.VNIL:
case ExpKind.VFALSE:
e.Kind = ExpKind.VTRUE;
break;
case ExpKind.VK:
case ExpKind.VKNUM:
case ExpKind.VTRUE:
e.Kind = ExpKind.VFALSE;
break;
case ExpKind.VJMP:
InvertJump( fs, e );
break;
case ExpKind.VRELOCABLE:
case ExpKind.VNONRELOC:
Discharge2AnyReg( fs, e );
FreeExp( fs, e );
e.Info = CodeABC( fs, OpCode.OP_NOT, 0, e.Info, 0 );
e.Kind = ExpKind.VRELOCABLE;
break;
default:
throw new Exception("CodeNot unknown e.Kind:" + e.Kind);
}
// interchange true and false lists
{ int temp = e.ExitFalse; e.ExitFalse = e.ExitTrue; e.ExitTrue = temp; }
RemoveValues( fs, e.ExitFalse );
RemoveValues( fs, e.ExitTrue );
}
private static void CodeComp( FuncState fs, OpCode op, int cond,
ExpDesc e1, ExpDesc e2 )
{
int o1 = Exp2RK( fs, e1 );
int o2 = Exp2RK( fs, e2 );
FreeExp( fs, e2 );
FreeExp( fs, e1 );
// exchange args to replace by `<' or `<='
if( cond == 0 && op != OpCode.OP_EQ ) {
int temp;
temp = o1; o1 = o2; o2 = temp; // o1 <==> o2
cond = 1;
}
e1.Info = CondJump( fs, op, cond, o1, o2 );
e1.Kind = ExpKind.VJMP;
}
public static void Prefix( FuncState fs, UnOpr op, ExpDesc e, int line )
{
ExpDesc e2 = new ExpDesc();
e2.ExitTrue = NO_JUMP;
e2.ExitFalse = NO_JUMP;
e2.Kind = ExpKind.VKNUM;
e2.NumberValue = 0.0;
switch( op )
{
case UnOpr.MINUS: {
if( IsNumeral( e ) ) // minus constant?
{
e.NumberValue = -e.NumberValue;
}
else
{
Exp2AnyReg( fs, e );
CodeArith( fs, OpCode.OP_UNM, e, e2, line );
}
} break;
case UnOpr.NOT: {
CodeNot( fs, e );
} break;
case UnOpr.LEN: {
Exp2AnyReg( fs, e ); // cannot operate on constants
CodeArith( fs, OpCode.OP_LEN, e, e2, line );
} break;
default:
throw new Exception("[Coder]Prefix Unknown UnOpr:" + op);
}
}
public static void Infix( FuncState fs, BinOpr op, ExpDesc e )
{
switch( op )
{
case BinOpr.AND: {
GoIfTrue( fs, e );
} break;
case BinOpr.OR: {
GoIfFalse( fs, e );
} break;
case BinOpr.CONCAT: {
Exp2NextReg( fs, e ); // operand must be on the `stack'
} break;
case BinOpr.ADD:
case BinOpr.SUB:
case BinOpr.MUL:
case BinOpr.DIV:
case BinOpr.MOD:
case BinOpr.POW: {
if( !IsNumeral(e) )
Exp2RK( fs, e );
} break;
default: {
Exp2RK( fs, e );
} break;
}
}
public static void Posfix( FuncState fs, BinOpr op,
ExpDesc e1, ExpDesc e2, int line )
{
switch( op )
{
case BinOpr.AND: {
Utl.Assert( e1.ExitTrue == NO_JUMP );
DischargeVars( fs, e2 );
e2.ExitFalse = Concat( fs, e2.ExitFalse, e1.ExitFalse );
e1.CopyFrom( e2 );
break;
}
case BinOpr.OR: {
Utl.Assert( e1.ExitFalse == NO_JUMP );
DischargeVars( fs, e2 );
e2.ExitTrue = Concat( fs, e2.ExitTrue, e1.ExitTrue );
e1.CopyFrom( e2 );
break;
}
case BinOpr.CONCAT: {
Exp2Val( fs, e2 );
var pe2 = fs.GetCode( e2 );
if( e2.Kind == ExpKind.VRELOCABLE &&
pe2.Value.GET_OPCODE() == OpCode.OP_CONCAT )
{
Utl.Assert( e1.Info == pe2.Value.GETARG_B()-1 );
FreeExp( fs, e1 );
pe2.Value = pe2.Value.SETARG_B( e1.Info );
e1.Kind = ExpKind.VRELOCABLE;
e1.Info = e2.Info;
}
else
{
// operand must be on the `stack'
Exp2NextReg( fs, e2 );
CodeArith( fs, OpCode.OP_CONCAT, e1, e2, line );
}
break;
}
case BinOpr.ADD: {
CodeArith( fs, OpCode.OP_ADD, e1, e2, line);
break;
}
case BinOpr.SUB: {
CodeArith( fs, OpCode.OP_SUB, e1, e2, line);
break;
}
case BinOpr.MUL: {
CodeArith( fs, OpCode.OP_MUL, e1, e2, line);
break;
}
case BinOpr.DIV: {
CodeArith( fs, OpCode.OP_DIV, e1, e2, line);
break;
}
case BinOpr.MOD: {
CodeArith( fs, OpCode.OP_MOD, e1, e2, line);
break;
}
case BinOpr.POW: {
CodeArith( fs, OpCode.OP_POW, e1, e2, line);
break;
}
case BinOpr.EQ: {
CodeComp( fs, OpCode.OP_EQ, 1, e1, e2 );
break;
}
case BinOpr.LT: {
CodeComp( fs, OpCode.OP_LT, 1, e1, e2 );
break;
}
case BinOpr.LE: {
CodeComp( fs, OpCode.OP_LE, 1, e1, e2 );
break;
}
case BinOpr.NE: {
CodeComp( fs, OpCode.OP_EQ, 0, e1, e2 );
break;
}
case BinOpr.GT: {
CodeComp( fs, OpCode.OP_LT, 0, e1, e2 );
break;
}
case BinOpr.GE: {
CodeComp( fs, OpCode.OP_LE, 0, e1, e2 );
break;
}
default: Utl.Assert(false); break;
}
}
public static int Jump( FuncState fs )
{
int jpc = fs.Jpc; // save list of jumps to here
fs.Jpc = NO_JUMP;
int j = CodeAsBx( fs, OpCode.OP_JMP, 0, NO_JUMP );
j = Concat( fs, j, jpc );
return j;
}
public static void JumpTo( FuncState fs, int target )
{
PatchList( fs, Jump(fs), target );
}
public static void Ret( FuncState fs, int first, int nret )
{
CodeABC( fs, OpCode.OP_RETURN, first, nret+1, 0 );
}
private static int CondJump( FuncState fs, OpCode op, int a, int b, int c )
{
CodeABC( fs, op, a, b, c );
return Jump( fs );
}
public static void PatchList( FuncState fs, int list, int target )
{
if( target == fs.Pc )
PatchToHere( fs, list );
else
{
Utl.Assert( target < fs.Pc );
PatchListAux( fs, list, target, NO_REG, target );
}
}
public static void 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 );
var pi = new InstructionPtr( fs.Proto.Code, list );;
Utl.Assert( pi.Value.GET_OPCODE() == OpCode.OP_JMP &&
( pi.Value.GETARG_A() == 0 ||
pi.Value.GETARG_A() >= level ) );
pi.Value = pi.Value.SETARG_A( level );
list = next;
}
}
public static void PatchToHere( FuncState fs, int list )
{
GetLabel( fs );
fs.Jpc = Concat( fs, fs.Jpc, list );
}
public static int Concat( FuncState fs, int l1, int l2 )
{
if( l2 == NO_JUMP )
return l1;
else if( l1 == NO_JUMP )
return l2;
else
{
int list = l1;
int next = GetJump( fs, list );
// find last element
while( next != NO_JUMP )
{
list = next;
next = GetJump( fs, list );
}
FixJump( fs, list, l2 );
return l1;
}
}
public static int StringK( FuncState fs, string s )
{
var o = new TValue();
o.SetSValue(s);
return AddK( fs, ref o, ref o );
}
public static int NumberK( FuncState fs, double r )
{
var o = new TValue();
o.SetNValue(r);
return AddK( fs, ref o, ref o );
}
private static int BoolK( FuncState fs, bool b )
{
var o = new TValue();
o.SetBValue(b);
return AddK( fs, ref o, ref o );
}
private static int NilK( FuncState fs )
{
// // cannot use nil as key;
// // instead use table itself to represent nil
// var k = fs.H;
// var o = new LuaNil();
// return AddK( fs, k, o );
var o = new TValue();
o.SetNilValue();
return AddK( fs, ref o, ref o );
}
public static int AddK(FuncState fs, ref TValue key, ref TValue v)
{
int idx;
int keyHash = key.GetHashCode();
if (fs.H.TryGetValue(keyHash, out idx))
return idx;
idx = fs.Proto.K.Count;
fs.H.Add(keyHash, idx);
var newItem = new StkId();
newItem.V.SetObj(ref v);
fs.Proto.K.Add(newItem);
return idx;
}
public static void Indexed( FuncState fs, ExpDesc t, ExpDesc k )
{
t.Ind.T = t.Info;
t.Ind.Idx = Exp2RK( fs, k );
t.Ind.Vt = (t.Kind == ExpKind.VUPVAL) ? ExpKind.VUPVAL
: ExpKind.VLOCAL; // FIXME
t.Kind = ExpKind.VINDEXED;
}
private static bool HasJumps( ExpDesc e )
{
return e.ExitTrue != e.ExitFalse;
}
private static int CodeLabel( FuncState fs, int a, int b, int jump )
{
GetLabel( fs ); // those instructions may be jump targets
return CodeABC( fs, OpCode.OP_LOADBOOL, a, b, jump );
}
private static void Discharge2Reg( FuncState fs, ExpDesc e, int reg )
{
DischargeVars( fs, e );
switch( e.Kind )
{
case ExpKind.VNIL: {
CodeNil( fs, reg, 1 );
break;
}
case ExpKind.VFALSE:
case ExpKind.VTRUE: {
CodeABC( fs, OpCode.OP_LOADBOOL, reg,
(e.Kind == ExpKind.VTRUE ? 1 : 0), 0 );
break;
}
case ExpKind.VK: {
CodeK( fs, reg, e.Info );
break;
}
case ExpKind.VKNUM: {
CodeK( fs, reg, NumberK( fs, e.NumberValue ) );
break;
}
case ExpKind.VRELOCABLE: {
InstructionPtr pi = fs.GetCode(e);
pi.Value = pi.Value.SETARG_A(reg);
break;
}
case ExpKind.VNONRELOC: {
if( reg != e.Info )
CodeABC( fs, OpCode.OP_MOVE, reg, e.Info, 0 );
break;
}
default: {
Utl.Assert( e.Kind == ExpKind.VVOID || e.Kind == ExpKind.VJMP );
return; // nothing to do...
}
}
e.Info = reg;
e.Kind = ExpKind.VNONRELOC;
}
public static void CheckStack( FuncState fs, int n )
{
int newStack = fs.FreeReg + n;
if( newStack > fs.Proto.MaxStackSize )
{
if( newStack >= LuaLimits.MAXSTACK )
{
fs.Lexer.SyntaxError("function or expression too complex");
}
fs.Proto.MaxStackSize = (byte)newStack;
}
}
public static void ReserveRegs( FuncState fs, int n )
{
CheckStack( fs, n );
fs.FreeReg += n;
}
private static void Discharge2AnyReg( FuncState fs, ExpDesc e )
{
if( e.Kind != ExpKind.VNONRELOC )
{
ReserveRegs( fs, 1 );
Discharge2Reg( fs, e, fs.FreeReg-1 );
}
}
private static void Exp2Reg( FuncState fs, ExpDesc e, int reg )
{
Discharge2Reg( fs, e, reg );
if( e.Kind == ExpKind.VJMP )
{
e.ExitTrue = Concat( fs, e.ExitTrue, e.Info );
}
if( HasJumps(e) )
{
int p_f = NO_JUMP;
int p_t = NO_JUMP;
if( NeedValue( fs, e.ExitTrue ) || NeedValue( fs, e.ExitFalse ) )
{
int fj = (e.Kind == ExpKind.VJMP) ? NO_JUMP : Jump( fs );
p_f = CodeLabel( fs, reg, 0, 1 );
p_t = CodeLabel( fs, reg, 1, 0 );
PatchToHere( fs, fj );
}
// position after whole expression
int final = GetLabel( fs );
PatchListAux( fs, e.ExitFalse, final, reg, p_f );
PatchListAux( fs, e.ExitTrue, final, reg, p_t );
}
e.ExitFalse = NO_JUMP;
e.ExitTrue = NO_JUMP;
e.Info = reg;
e.Kind = ExpKind.VNONRELOC;
}
public static void Exp2NextReg( FuncState fs, ExpDesc e )
{
DischargeVars( fs, e );
FreeExp( fs, e );
ReserveRegs( fs, 1 );
Exp2Reg( fs, e, fs.FreeReg-1 );
}
public static void Exp2Val( FuncState fs, ExpDesc e )
{
if( HasJumps(e) )
Exp2AnyReg( fs, e );
else
DischargeVars( fs, e );
}
public static int Exp2RK( FuncState fs, ExpDesc e )
{
Exp2Val( fs, e );
switch( e.Kind )
{
case ExpKind.VTRUE:
case ExpKind.VFALSE:
case ExpKind.VNIL: {
// constant fits in RK operand?
if( fs.Proto.K.Count <= Instruction.MAXINDEXRK )
{
e.Info = (e.Kind == ExpKind.VNIL) ? NilK(fs)
: BoolK( fs, (e.Kind == ExpKind.VTRUE ) );
e.Kind = ExpKind.VK;
return Instruction.RKASK( e.Info );
}
else break;
}
case ExpKind.VKNUM:
case ExpKind.VK:
{
if( e.Kind == ExpKind.VKNUM )
{
e.Info = NumberK( fs, e.NumberValue );
e.Kind = ExpKind.VK;
}
if( e.Info <= Instruction.MAXINDEXRK )
return Instruction.RKASK( e.Info );
else break;
}
default: break;
}
return Exp2AnyReg( fs, e );
}
public static int Exp2AnyReg( FuncState fs, ExpDesc e )
{
DischargeVars( fs, e );
if( e.Kind == ExpKind.VNONRELOC )
{
// exp is already in a register
if( ! HasJumps( e ) )
return e.Info;
// reg. is not a local?
if( e.Info >= fs.NumActVar )
{
Exp2Reg( fs, e, e.Info );
return e.Info;
}
}
Exp2NextReg( fs, e ); // default
return e.Info;
}
public static void Exp2AnyRegUp( FuncState fs, ExpDesc e )
{
if( e.Kind != ExpKind.VUPVAL || HasJumps( e ) )
{
Exp2AnyReg( fs, e );
}
}
public static void DischargeVars( FuncState fs, ExpDesc e )
{
switch( e.Kind )
{
case ExpKind.VLOCAL:
e.Kind = ExpKind.VNONRELOC;
break;
case ExpKind.VUPVAL:
e.Info = CodeABC( fs, OpCode.OP_GETUPVAL, 0, e.Info, 0 );
e.Kind = ExpKind.VRELOCABLE;
break;
case ExpKind.VINDEXED:
OpCode op = OpCode.OP_GETTABUP;
FreeReg( fs, e.Ind.Idx );
if( e.Ind.Vt == ExpKind.VLOCAL )
{
FreeReg( fs, e.Ind.T );
op = OpCode.OP_GETTABLE;
}
e.Info = CodeABC( fs, op, 0, e.Ind.T, e.Ind.Idx );
e.Kind = ExpKind.VRELOCABLE;
break;
case ExpKind.VVARARG:
case ExpKind.VCALL:
SetOneRet( fs, e );
break;
default: break;
}
}
public static void SetReturns( FuncState fs, ExpDesc e, int nResults )
{
if( e.Kind == ExpKind.VCALL ) { // expression is an open function call?
var pi = fs.GetCode(e);
pi.Value = pi.Value.SETARG_C( nResults+1 );
}
else if( e.Kind == ExpKind.VVARARG ) {
var pi = fs.GetCode(e);
pi.Value = pi.Value.SETARG_B( nResults+1 ).SETARG_A( fs.FreeReg );
ReserveRegs( fs, 1 );
}
}
public static void SetMultiRet( FuncState fs, ExpDesc e )
{
SetReturns( fs, e, LuaDef.LUA_MULTRET );
}
public static void SetOneRet( FuncState fs, ExpDesc e )
{
// expression is an open function call?
if( e.Kind == ExpKind.VCALL )
{
e.Kind = ExpKind.VNONRELOC;
e.Info = ( fs.GetCode( e ) ).Value.GETARG_A();
}
else if( e.Kind == ExpKind.VVARARG )
{
var pi = fs.GetCode( e );
pi.Value = pi.Value.SETARG_B( 2 );
e.Kind = ExpKind.VRELOCABLE; // can relocate its simple result
}
}
public static void StoreVar( FuncState fs, ExpDesc v, ExpDesc e )
{
switch( v.Kind )
{
case ExpKind.VLOCAL: {
FreeExp( fs, e );
Exp2Reg( fs, e, v.Info );
break;
}
case ExpKind.VUPVAL: {
int c = Exp2AnyReg( fs, e );
CodeABC( fs, OpCode.OP_SETUPVAL, c, v.Info, 0 );
break;
}
case ExpKind.VINDEXED: {
OpCode op = (v.Ind.Vt == ExpKind.VLOCAL)
? OpCode.OP_SETTABLE
: OpCode.OP_SETTABUP;
int c = Exp2RK( fs, e );
CodeABC( fs, op, v.Ind.T, v.Ind.Idx, c );
break;
}
default:
{
throw new NotImplementedException("invalid var kind to store");
}
}
FreeExp( fs, e );
}
public static void Self( FuncState fs, ExpDesc e, ExpDesc key )
{
Exp2AnyReg( fs, e );
int ereg = e.Info; // register where `e' is placed
FreeExp( fs, e );
e.Info = fs.FreeReg; // base register for op_self
e.Kind = ExpKind.VNONRELOC;
ReserveRegs( fs, 2 );
CodeABC( fs, OpCode.OP_SELF, e.Info, ereg, Coder.Exp2RK(fs, key) );
FreeExp( fs, key );
}
public static void SetList( FuncState fs, int t, int nelems, int tostore )
{
int c = (nelems - 1) / LuaDef.LFIELDS_PER_FLUSH + 1;
int b = (tostore == LuaDef.LUA_MULTRET) ? 0 : tostore;
Utl.Assert( tostore != 0 );
if( c <= Instruction.MAXARG_C )
{
CodeABC( fs, OpCode.OP_SETLIST, t, b, c );
}
else if( c <= Instruction.MAXARG_Ax )
{
CodeABC( fs, OpCode.OP_SETLIST, t, b, 0 );
CodeExtraArg( fs, c );
}
else
{
fs.Lexer.SyntaxError("constructor too long");
}
// free registers with list values
fs.FreeReg = t + 1;
}
public static void CodeNil( FuncState fs, int from, int n )
{
int l = from + n - 1; // last register to set nil
if( fs.Pc > fs.LastTarget ) // no jumps to current position?
{
var previous = new InstructionPtr( fs.Proto.Code, fs.Pc-1 );
if( previous.Value.GET_OPCODE() == OpCode.OP_LOADNIL )
{
int pfrom = previous.Value.GETARG_A();
int pl = pfrom + previous.Value.GETARG_B();
// can connect both?
if( (pfrom <= from && from <= pl + 1) ||
(from <= pfrom && pfrom <= l + 1))
{
if( pfrom < from ) from = pfrom; // from=min(from,pfrom)
if( pl > l ) l = pl; // l=max(l,pl)
previous.Value = previous.Value.SETARG_A( from );
previous.Value = previous.Value.SETARG_B( l - from );
return;
}
}
// else go through
}
// else no optimization
CodeABC( fs, OpCode.OP_LOADNIL, from, n-1, 0 );
}
private static int CodeExtraArg( FuncState fs, int a )
{
Utl.Assert( a <= Instruction.MAXARG_Ax );
return Code( fs, Instruction.CreateAx( OpCode.OP_EXTRAARG, a ) );
}
public static int CodeK( FuncState fs, int reg, int k )
{
if( k <= Instruction.MAXARG_Bx )
return CodeABx( fs, OpCode.OP_LOADK, reg, (uint)k );
else
{
int p = CodeABx( fs, OpCode.OP_LOADKX, reg, 0 );
CodeExtraArg( fs, k );
return p;
}
}
public static int CodeAsBx( FuncState fs, OpCode op, int a, int sBx )
{
return CodeABx( fs, op, a, ((uint)sBx)+Instruction.MAXARG_sBx);
}
public static int CodeABx( FuncState fs, OpCode op, int a, uint bc )
{
var mode = OpCodeInfo.GetMode(op);
Utl.Assert( mode.OpMode == OpMode.iABx
|| mode.OpMode == OpMode.iAsBx );
Utl.Assert( mode.CMode == OpArgMask.OpArgN );
Utl.Assert( a < Instruction.MAXARG_A & bc <= Instruction.MAXARG_Bx );
return Code( fs, Instruction.CreateABx( op, a, bc ) );
}
public static int CodeABC( FuncState fs, OpCode op, int a, int b, int c )
{
return Code( fs, Instruction.CreateABC( op, a, b, c ) );
}
public static int Code( FuncState fs, Instruction i )
{
DischargeJpc( fs ); // `pc' will change
while( fs.Proto.Code.Count <= fs.Pc )
{
fs.Proto.Code.Add( new Instruction(LuaLimits.MAX_INT) );
}
fs.Proto.Code[ fs.Pc ] = i;
while( fs.Proto.LineInfo.Count <= fs.Pc )
{
fs.Proto.LineInfo.Add( LuaLimits.MAX_INT );
}
fs.Proto.LineInfo[ fs.Pc ] = fs.Lexer.LastLine;
return fs.Pc++;
}
}
}