private static void CgForNumStat(FuncInfo fi, ForNumStat node) { fi.EnterScope(true); CgLocalVarDeclStat(fi, new LocalVarDeclStat { NameList = new List <string> { "(for index)", "(for limit)", "(for step)" }, ExpList = new List <Exp> { node.InitExp, node.LimitExp, node.StepExp } }); fi.AddLocVar(node.VarName); var a = fi.UsedRegs - 4; var pcForPrep = fi.EmitForPrep(a, 0); CgBlock(fi, node.Block); fi.CloseOpenUpvals(); var pcForLoop = fi.EmitForLoop(a, 0); fi.FixsBx(pcForPrep, pcForLoop - pcForPrep - 1); fi.FixsBx(pcForLoop, pcForPrep - pcForLoop); fi.ExitScope(); }
private static void CgForInStat(FuncInfo fi, ForInStat node) { fi.EnterScope(true); CgLocalVarDeclStat(fi, new LocalVarDeclStat { NameList = new List <string> { "(for generator)", "(for state)", "(for control)" }, ExpList = node.ExpList }); foreach (var name in node.NameList) { fi.AddLocVar(name); } var pcJmpToTfc = fi.EmitJmp(0, 0); CgBlock(fi, node.Block); fi.CloseOpenUpvals(); fi.FixsBx(pcJmpToTfc, fi.PC() - pcJmpToTfc); var rGenerator = fi.SlotOfLocVar("(for generator)"); fi.EmitTForCall(rGenerator, node.NameList.Count); fi.EmitTForLoop(rGenerator + 2, pcJmpToTfc - fi.PC() - 1); fi.ExitScope(); }
private static ProtoType ToProto(FuncInfo fi) { var proto = new ProtoType { NumParams = (byte)fi.NumParams, MaxStackSize = (byte)fi.MaxRegs, Code = fi.Insts.ToArray(), Constatns = GetConstants(fi), Upvalues = GetUpvalues(fi), Protos = ToProtos(fi.SubFuncs), LineInfo = new uint[0], LocVars = new LocVar[0], UpvalueNames = new string[0] }; if (proto.MaxStackSize < 2) { proto.MaxStackSize = 2; } if (fi.IsVararg) { proto.IsVararg = 1; } return(proto); }
private static void CgNameExp(FuncInfo fi, NameExp node, int a) { var r = fi.SlotOfLocVar(node.Name); if (r >= 0) { fi.EmitMove(a, r); } else { var idx = fi.IndexOfUpval(node.Name); if (idx >= 0) { fi.EmitGetUpval(a, idx); } else { var taExp = new TableAccessExp { PrefixExp = new NameExp { Line = 0, Name = "_ENV" }, KeyExp = new StringExp { Line = 0, Str = node.Name } }; CgTableAccessExp(fi, taExp, a); } } }
private static Upvalue[] GetUpvalues(FuncInfo fi) { var upvals = new Upvalue[fi.Upvalues.Count]; foreach (var kv in fi.Upvalues) { if (kv.Value.LocVarSlot >= 0) { upvals[kv.Value.Index] = new Upvalue { Instack = 1, Idx = (byte)kv.Value.LocVarSlot }; } else { upvals[kv.Value.Index] = new Upvalue { Instack = 0, Idx = (byte)kv.Value.UpvalIndex }; } } return(upvals); }
private static void CgFuncCallStat(FuncInfo fi, FuncCallStat node) { var r = fi.AllocReg(); CgFuncCallExp(fi, node, r, 0); fi.FreeReg(); }
private static void CgDoStat(FuncInfo fi, DoStat node) { fi.EnterScope(false); CgBlock(fi, node.Block); fi.CloseOpenUpvals(); fi.ExitScope(); }
private static void CgUnopExp(FuncInfo fi, UnopExp node, int a) { var b = fi.AllocReg(); CgExp(fi, node.Exp, b, 1); fi.EmitUnaryOp(node.GetOpCode(), a, b); fi.FreeReg(); }
private static void CgTableConstructorExp(FuncInfo fi, TableConstructorExp node, int a) { var nArr = node.KeyExps?.Count(exp => exp is null) ?? 0; var nExps = node.KeyExps?.Count ?? 0; var multRet = nExps > 0 && IsVarargOrFuncCall(node.ValExps[nExps - 1]); fi.EmitNewTable(a, nArr, nExps - nArr); var arrIdx = 0; for (var i = 0; i < nExps; i++) { var keyExp = node.KeyExps[i]; var valExp = node.ValExps[i]; if (keyExp == null) { arrIdx++; var tmp = fi.AllocReg(); if (i == nExps - 1 && multRet) { CgExp(fi, valExp, tmp, -1); } else { CgExp(fi, valExp, tmp, 1); } if (arrIdx % 50 == 0 || arrIdx == nArr) { var n = arrIdx % 50; if (n == 0) { n = 50; } fi.FreeRegs(n); var c = (arrIdx - 1) / 50 + 1; if (i == nExps - 1 && multRet) { fi.EmitSetList(a, 0, c); } else { fi.EmitSetList(a, n, c); } } continue; } var b = fi.AllocReg(); CgExp(fi, keyExp, b, 1); var d = fi.AllocReg(); CgExp(fi, valExp, d, 1); fi.FreeRegs(2); fi.EmitSetTable(a, b, d); } }
private static void CgVarargExp(FuncInfo fi, VarargExp node, int a, int n) { if (!fi.IsVararg) { Debug.Panic("cannot use '...' outside a vararg function"); return; } fi.EmitVararg(a, n); }
private static void CgStat(FuncInfo fi, Stat node) { switch (node) { case FuncCallStat fs: CgFuncCallStat(fi, fs); break; case BreakStat bs: CgBreakStat(fi, bs); break; case DoStat ds: CgDoStat(fi, ds); break; case WhileStat ws: CgWhileStat(fi, ws); break; case RepeatStat rs: CgRepeatStat(fi, rs); break; case IfStat ifs: CgIfStat(fi, ifs); break; case ForNumStat fns: CgForNumStat(fi, fns); break; case ForInStat fis: CgForInStat(fi, fis); break; case AssignStat ass: CgAssignStat(fi, ass); break; case LocalVarDeclStat lvds: CgLocalVarDeclStat(fi, lvds); break; case LocalFuncDefStat lfds: CgLocalFuncDefStat(fi, lfds); break; case LabelStat _: case GotoStat _: Debug.Panic("label and goto statements are not supported!"); break; } }
private static void CgTableAccessExp(FuncInfo fi, TableAccessExp node, int a) { var b = fi.AllocReg(); CgExp(fi, node.PrefixExp, b, 1); var c = fi.AllocReg(); CgExp(fi, node.KeyExp, c, 1); fi.EmitGetTable(a, b, c); fi.FreeRegs(2); }
private static void CgBlock(FuncInfo fi, Block node) { foreach (var stat in node.Stats) { CgStat(fi, stat); } if (node.RetExps != null) { CgRetStat(fi, node.RetExps); } }
public FuncInfo(FuncInfo parent, FuncDefExp fd) { _parent = parent; SubFuncs = new List <FuncInfo>(); _locVars = new List <LocVarInfo>(8); _locNames = new Dictionary <string, LocVarInfo>(); Upvalues = new Dictionary <string, UpvalInfo>(); Constants = new Dictionary <object, int>(); _breaks = new List <List <int> >(1); Insts = new List <uint>(8); NumParams = fd.ParList.Count; IsVararg = fd.IsVararg; }
public static ProtoType GenProto(Block chunk) { var fd = new FuncDefExp { IsVararg = true, Block = chunk, ParList = new List <string>() }; var fi = new FuncInfo(null, fd); fi.AddLocVar("_ENV"); CgFuncDefExp(fi, fd, 0); return(ToProto(fi.SubFuncs[0])); }
private static void CgConcatExp(FuncInfo fi, ConcatExp node, int a) { foreach (var subExp in node.Exps) { var tmp = fi.AllocReg(); CgExp(fi, subExp, tmp, 1); } var c = fi.UsedRegs - 1; var b = c - node.Exps.Count + 1; fi.FreeRegs(c - b + 1); fi.EmitABC(EOpCode.OP_CONCAT, a, b, c); }
private static object[] GetConstants(FuncInfo fi) { var consts = new object[fi.Constants.Count]; foreach (var kv in fi.Constants) { if (kv.Key == FuncInfo.NilObj) { consts[kv.Value] = null; } else { consts[kv.Value] = kv.Key; } } return(consts); }
private static void CgFuncDefExp(FuncInfo fi, FuncDefExp node, int a) { var subFi = new FuncInfo(fi, node); fi.SubFuncs.Add(subFi); foreach (var param in node.ParList) { subFi.AddLocVar(param); } CgBlock(subFi, node.Block); subFi.ExitScope(); subFi.EmitReturn(0, 0); var bx = fi.SubFuncs.Count - 1; fi.EmitClosure(a, bx); }
private static void CgRepeatStat(FuncInfo fi, RepeatStat node) { fi.EnterScope(true); var pcBeforeBlock = fi.PC(); CgBlock(fi, node.Block); var r = fi.AllocReg(); CgExp(fi, node.Exp, r, 1); fi.FreeReg(); fi.EmitTest(r, 0); fi.EmitJmp(fi.GetJmpArgA(), pcBeforeBlock - fi.PC() - 1); fi.CloseOpenUpvals(); fi.ExitScope(); }
private static void CgBinopExp(FuncInfo fi, BinopExp node, int a) { switch (node.Op) { case ETokenType.OpAnd: case ETokenType.OpOr: { var b = fi.AllocReg(); CgExp(fi, node.Exp1, b, 1); fi.FreeReg(); if (node.Op == ETokenType.OpAnd) { fi.EmitTestSet(a, b, 0); } else { fi.EmitTestSet(a, b, 1); } var pcOfJmp = fi.EmitJmp(0, 0); b = fi.AllocReg(); CgExp(fi, node.Exp2, b, 1); fi.FreeReg(); fi.EmitMove(a, b); fi.FixsBx(pcOfJmp, fi.PC() - pcOfJmp); break; } default: { var b = fi.AllocReg(); CgExp(fi, node.Exp1, b, 1); var c = fi.AllocReg(); CgExp(fi, node.Exp2, c, 1); fi.EmitBinaryOp(node.Op, a, b, c); fi.FreeRegs(2); break; } } }
private static int PrepFuncCall(FuncInfo fi, FuncCallExp node, int a) { var nArgs = node.Args?.Count ?? 0; var lastArgIsVarargsOrFuncCall = false; CgExp(fi, node.PrefixExp, a, 1); if (node.NameExp != null) { fi.IndexOfConstant(node.NameExp.Str, out var idx); var c = 0x100 + idx; fi.EmitSelf(a, a, c); } for (var i = 0; i < nArgs; i++) { var arg = node.Args[i]; var tmp = fi.AllocReg(); if (i == nArgs - 1 && IsVarargOrFuncCall(arg)) { lastArgIsVarargsOrFuncCall = true; CgExp(fi, arg, tmp, -1); } else { CgExp(fi, arg, tmp, 1); } } fi.FreeRegs(nArgs); if (node.NameExp != null) { nArgs++; } if (lastArgIsVarargsOrFuncCall) { nArgs = -1; } return(nArgs); }
private static void CgWhileStat(FuncInfo fi, WhileStat node) { var pcBeforeExp = fi.PC(); var r = fi.AllocReg(); CgExp(fi, node.Exp, r, 1); fi.FreeReg(); fi.EmitTest(r, 0); var pcJmpToEnd = fi.EmitJmp(0, 0); fi.EnterScope(true); CgBlock(fi, node.Block); fi.CloseOpenUpvals(); fi.EmitJmp(0, pcBeforeExp - fi.PC() - 1); fi.ExitScope(); fi.FixsBx(pcJmpToEnd, fi.PC() - pcJmpToEnd); }
private static void CgIfStat(FuncInfo fi, IfStat node) { var pcJmpToEnds = new int[node.Exps.Count]; var pcJmpToNextExp = -1; for (var i = 0; i < node.Exps.Count; i++) { var exp = node.Exps[i]; if (pcJmpToNextExp >= 0) { fi.FixsBx(pcJmpToNextExp, fi.PC() - pcJmpToNextExp); } var r = fi.AllocReg(); CgExp(fi, exp, r, 1); fi.FreeReg(); fi.EmitTest(r, 0); pcJmpToNextExp = fi.EmitJmp(0, 0); fi.EnterScope(false); CgBlock(fi, node.Blocks[i]); fi.CloseOpenUpvals(); fi.ExitScope(); if (i < node.Exps.Count - 1) { pcJmpToEnds[i] = fi.EmitJmp(0, 0); } else { pcJmpToEnds[i] = pcJmpToNextExp; } } foreach (var pc in pcJmpToEnds) { fi.FixsBx(pc, fi.PC() - pc); } }
private static void CgBreakStat(FuncInfo fi, BreakStat node) { var pc = fi.EmitJmp(0, 0); fi.AddBreakJump(pc); }
private static void CgTailCallExp(FuncInfo fi, FuncCallExp node, int a) { var nArgs = PrepFuncCall(fi, node, a); fi.EmitTailCall(a, nArgs); }
private static void CgFuncCallExp(FuncInfo fi, FuncCallExp node, int a, int n) { var nArgs = PrepFuncCall(fi, node, a); fi.EmitCall(a, nArgs, n); }
private static void CgExp(FuncInfo fi, Exp node, int a, int n) { switch (node) { case NilExp _: fi.EmitLoadNil(a, n); break; case FalseExp _: fi.EmitLoadBool(a, 0, 0); break; case TrueExp _: fi.EmitLoadBool(a, 1, 0); break; case IntegerExp integerExp: fi.EmitLoadK(a, integerExp.Val); break; case FloatExp floatExp: fi.EmitLoadK(a, floatExp.Val); break; case StringExp stringExp: fi.EmitLoadK(a, stringExp.Str); break; case ParensExp parensExp: CgExp(fi, parensExp.Exp, a, 1); break; case VarargExp varargExp: CgVarargExp(fi, varargExp, a, n); break; case FuncDefExp funcDefExp: CgFuncDefExp(fi, funcDefExp, a); break; case TableConstructorExp tableConstructorExp: CgTableConstructorExp(fi, tableConstructorExp, a); break; case UnopExp unopExp: CgUnopExp(fi, unopExp, a); break; case BinopExp binopExp: CgBinopExp(fi, binopExp, a); break; case ConcatExp concatExp: CgConcatExp(fi, concatExp, a); break; case NameExp nameExp: CgNameExp(fi, nameExp, a); break; case TableAccessExp tableAccessExp: CgTableAccessExp(fi, tableAccessExp, a); break; case FuncCallExp funcCallExp: CgFuncCallExp(fi, funcCallExp, a, n); break; } }
private static void CgAssignStat(FuncInfo fi, AssignStat node) { var exps = RemoveTailNil(node.ExpList); var nExps = exps.Count; var nVars = node.VarList.Count; var tRegs = new int[nVars]; var kRegs = new int[nVars]; var vRegs = new int[nVars]; var oldRegs = fi.UsedRegs; for (var i = 0; i < node.VarList.Count; i++) { var exp = node.VarList[i]; if (exp is TableAccessExp tableAccessExp) { tRegs[i] = fi.AllocReg(); CgExp(fi, tableAccessExp.PrefixExp, tRegs[i], 1); kRegs[i] = fi.AllocReg(); CgExp(fi, tableAccessExp.KeyExp, kRegs[i], 1); } else if (exp is NameExp nameExp) { var name = nameExp.Name; if (fi.SlotOfLocVar(name) < 0 && fi.IndexOfUpval(name) < 0) { kRegs[i] = -1; fi.IndexOfConstant(name, out var constIdx); if (constIdx > 0xff) { kRegs[i] = fi.AllocReg(); } } } } for (var i = 0; i < nVars; i++) { vRegs[i] = fi.UsedRegs + i; } if (nExps >= nVars) { for (var i = 0; i < exps.Count; i++) { var exp = exps[i]; var a = fi.AllocReg(); if (i >= nVars && i == nExps - 1 && IsVarargOrFuncCall(exp)) { CgExp(fi, exp, a, 0); } else { CgExp(fi, exp, a, 1); } } } else { var multRet = false; for (var i = 0; i < exps.Count; i++) { var exp = exps[i]; var a = fi.AllocReg(); if (i == nExps - 1 && IsVarargOrFuncCall(exp)) { multRet = true; var n = nVars - nExps + 1; CgExp(fi, exp, a, n); fi.AllocRegs(n - 1); } else { CgExp(fi, exp, a, 1); } } if (!multRet) { var n = nVars - nExps; var a = fi.AllocRegs(n); fi.EmitLoadNil(a, n); } } for (var i = 0; i < node.VarList.Count; i++) { var exp = node.VarList[i]; if (exp is NameExp nameExp) { var varName = nameExp.Name; var a = fi.SlotOfLocVar(varName); if (a >= 0) { fi.EmitMove(a, vRegs[i]); } else { var b = fi.IndexOfUpval(varName); if (b >= 0) { fi.EmitSetUpval(vRegs[i], b); } else { a = fi.SlotOfLocVar("_ENV"); if (a >= 0) { if (kRegs[i] < 0) { fi.IndexOfConstant(varName, out var constIdx); b = 0x100 + constIdx; fi.EmitSetTable(a, b, vRegs[i]); } else { fi.EmitSetTable(a, kRegs[i], vRegs[i]); } } else { a = fi.IndexOfUpval("_ENV"); if (kRegs[i] < 0) { fi.IndexOfConstant(varName, out var idx); b = 0x100 + idx; fi.EmitSetTabUp(a, b, vRegs[i]); } else { fi.EmitSetTable(a, kRegs[i], vRegs[i]); } } } } } else { fi.EmitSetTable(tRegs[i], kRegs[i], vRegs[i]); } } fi.UsedRegs = oldRegs; }
private static void CgLocalFuncDefStat(FuncInfo fi, LocalFuncDefStat node) { var r = fi.AddLocVar(node.Name); CgFuncDefExp(fi, node.Exp, r); }
private static void CgLocalVarDeclStat(FuncInfo fi, LocalVarDeclStat node) { var exps = RemoveTailNil(node.ExpList); var nExps = exps.Count; var nNames = node.NameList.Count; var oldRegs = fi.UsedRegs; if (nExps == nNames) { foreach (var exp in exps) { var a = fi.AllocReg(); CgExp(fi, exp, a, 1); } } else if (nExps > nNames) { for (var i = 0; i < exps.Count; i++) { var exp = exps[i]; var a = fi.AllocReg(); if (i == nExps - 1 && IsVarargOrFuncCall(exp)) { CgExp(fi, exp, a, 0); } else { CgExp(fi, exp, a, 1); } } } else { var multRet = false; for (var i = 0; i < exps.Count; i++) { var exp = exps[i]; var a = fi.AllocReg(); if (i == nExps - 1 && IsVarargOrFuncCall(exp)) { multRet = true; var n = nNames - nExps + 1; CgExp(fi, exp, a, n); fi.AllocRegs(n - 1); } else { CgExp(fi, exp, a, 1); } } if (!multRet) { var n = nNames - nExps; var a = fi.AllocRegs(n); fi.EmitLoadNil(a, n); } } fi.UsedRegs = oldRegs; foreach (var name in node.NameList) { fi.AddLocVar(name); } }