吉里吉里2中TJS2 VM的dispatch loop
稍微在吉里吉里2.28的源代码里找了下TJS2 VM的执行机制,主要着眼于dispatch loop的实现,并且找到了下面的代码:
kirikiri2\src\core\tjs2\tjsInterCodeExec.cpp/972:
tjs_int tTJSInterCodeContext::ExecuteCode(tTJSVariant *ra_org, tjs_int startip, tTJSVariant **args, tjs_int numargs, tTJSVariant *result){ // execute VM codes tjs_int32 *codesave; try { tjs_int32 *code = codesave = CodeArea + startip;
if(TJSStackTracerEnabled()) TJSStackTracerSetCodePointer(CodeArea, &codesave);
tTJSVariant *ra = ra_org; tTJSVariant *da = DataArea;
bool flag = false;
while(true) { codesave = code; switch(*code) { case VM_NOP: code ++; break;
case VM_CONST: TJS_GET_VM_REG(ra, code[1]).CopyRef(TJS_GET_VM_REG(da, code[2])); code += 3; break;
case VM_CP: TJS_GET_VM_REG(ra, code[1]).CopyRef(TJS_GET_VM_REG(ra, code[2])); code += 3; break;
case VM_CL: TJS_GET_VM_REG(ra, code[1]).Clear(); code += 2; break;
case VM_CCL: ContinuousClear(ra, code); code += 3; break;
case VM_TT: flag = TJS_GET_VM_REG(ra, code[1]).operator bool(); code += 2; break;
case VM_TF: flag = !(TJS_GET_VM_REG(ra, code[1]).operator bool()); code += 2; break;
case VM_CEQ: flag = TJS_GET_VM_REG(ra, code[1]).NormalCompare( TJS_GET_VM_REG(ra, code[2])); code += 3; break;
case VM_CDEQ: flag = TJS_GET_VM_REG(ra, code[1]).DiscernCompare( TJS_GET_VM_REG(ra, code[2])); code += 3; break;
case VM_CLT: flag = TJS_GET_VM_REG(ra, code[1]).GreaterThan( TJS_GET_VM_REG(ra, code[2])); code += 3; break;
case VM_CGT: flag = TJS_GET_VM_REG(ra, code[1]).LittlerThan( TJS_GET_VM_REG(ra, code[2])); code += 3; break;
case VM_SETF: TJS_GET_VM_REG(ra, code[1]) = flag; code += 2; break;
case VM_SETNF: TJS_GET_VM_REG(ra, code[1]) = !flag; code += 2; break;
case VM_LNOT: TJS_GET_VM_REG(ra, code[1]).logicalnot(); code += 2; break;
case VM_NF: flag = !flag; code ++; break;
case VM_JF: if(flag) TJS_ADD_VM_CODE_ADDR(code, code[1]); else code += 2; break;
case VM_JNF: if(!flag) TJS_ADD_VM_CODE_ADDR(code, code[1]); else code += 2; break;
case VM_JMP: TJS_ADD_VM_CODE_ADDR(code, code[1]); break;
case VM_INC: TJS_GET_VM_REG(ra, code[1]).increment(); code += 2; break;
case VM_INCPD: OperatePropertyDirect0(ra, code, TJS_OP_INC); code += 4; break;
case VM_INCPI: OperatePropertyIndirect0(ra, code, TJS_OP_INC); code += 4; break;
case VM_INCP: OperateProperty0(ra, code, TJS_OP_INC); code += 3; break;
case VM_DEC: TJS_GET_VM_REG(ra, code[1]).decrement(); code += 2; break;
case VM_DECPD: OperatePropertyDirect0(ra, code, TJS_OP_DEC); code += 4; break;
case VM_DECPI: OperatePropertyIndirect0(ra, code, TJS_OP_DEC); code += 4; break;
case VM_DECP: OperateProperty0(ra, code, TJS_OP_DEC); code += 3; break;
#define TJS_DEF_VM_P(vmcode, rope) \ case VM_##vmcode: \ TJS_GET_VM_REG(ra, code[1]).rope(TJS_GET_VM_REG(ra, code[2])); \ code += 3; \ break; \ case VM_##vmcode##PD: \ OperatePropertyDirect(ra, code, TJS_OP_##vmcode); \ code += 5; \ break; \ case VM_##vmcode##PI: \ OperatePropertyIndirect(ra, code, TJS_OP_##vmcode); \ code += 5; \ break; \ case VM_##vmcode##P: \ OperateProperty(ra, code, TJS_OP_##vmcode); \ code += 4; \ break
TJS_DEF_VM_P(LOR, logicalorequal); TJS_DEF_VM_P(LAND, logicalandequal); TJS_DEF_VM_P(BOR, operator |=); TJS_DEF_VM_P(BXOR, operator ^=); TJS_DEF_VM_P(BAND, operator &=); TJS_DEF_VM_P(SAR, operator >>=); TJS_DEF_VM_P(SAL, operator <<=); TJS_DEF_VM_P(SR, rbitshiftequal); TJS_DEF_VM_P(ADD, operator +=); TJS_DEF_VM_P(SUB, operator -=); TJS_DEF_VM_P(MOD, operator %=); TJS_DEF_VM_P(DIV, operator /=); TJS_DEF_VM_P(IDIV, idivequal); TJS_DEF_VM_P(MUL, operator *=);
#undef TJS_DEF_VM_P
case VM_BNOT: TJS_GET_VM_REG(ra, code[1]).bitnot(); code += 2; break;
case VM_ASC: CharacterCodeOf(TJS_GET_VM_REG(ra, code[1])); code += 2; break;
case VM_CHR: CharacterCodeFrom(TJS_GET_VM_REG(ra, code[1])); code += 2; break;
case VM_NUM: TJS_GET_VM_REG(ra, code[1]).tonumber(); code += 2; break;
case VM_CHS: TJS_GET_VM_REG(ra, code[1]).changesign(); code += 2; break;
case VM_INV: TJS_GET_VM_REG(ra, code[1]) = (TJS_GET_VM_REG(ra, code[1]).AsObjectClosureNoAddRef().Invalidate(0, NULL, NULL, ra[-1].AsObjectNoAddRef()) == TJS_S_TRUE); code += 2; break;
case VM_CHKINV: TJS_GET_VM_REG(ra, code[1]) = TJSIsObjectValid(TJS_GET_VM_REG(ra, code[1]).AsObjectClosureNoAddRef().IsValid(0, NULL, NULL, ra[-1].AsObjectNoAddRef())); code += 2; break;
case VM_INT: TJS_GET_VM_REG(ra, code[1]).ToInteger(); code += 2; break;
case VM_REAL: TJS_GET_VM_REG(ra, code[1]).ToReal(); code += 2; break;
case VM_STR: TJS_GET_VM_REG(ra, code[1]).ToString(); code += 2; break;
case VM_OCTET: TJS_GET_VM_REG(ra, code[1]).ToOctet(); code += 2; break;
case VM_TYPEOF: TypeOf(TJS_GET_VM_REG(ra, code[1])); code += 2; break;
case VM_TYPEOFD: TypeOfMemberDirect(ra, code, TJS_MEMBERMUSTEXIST); code += 4; break;
case VM_TYPEOFI: TypeOfMemberIndirect(ra, code, TJS_MEMBERMUSTEXIST); code += 4; break;
case VM_EVAL: Eval(TJS_GET_VM_REG(ra, code[1]), TJSEvalOperatorIsOnGlobal ? NULL : ra[-1].AsObjectNoAddRef(), true); code += 2; break;
case VM_EEXP: Eval(TJS_GET_VM_REG(ra, code[1]), TJSEvalOperatorIsOnGlobal ? NULL : ra[-1].AsObjectNoAddRef(), false); code += 2; break;
case VM_CHKINS: InstanceOf(TJS_GET_VM_REG(ra, code[2]), TJS_GET_VM_REG(ra, code[1])); code += 3; break;
case VM_CALL: case VM_NEW: code += CallFunction(ra, code, args, numargs); break;
case VM_CALLD: code += CallFunctionDirect(ra, code, args, numargs); break;
case VM_CALLI: code += CallFunctionIndirect(ra, code, args, numargs); break;
case VM_GPD: GetPropertyDirect(ra, code, 0); code += 4; break;
case VM_GPDS: GetPropertyDirect(ra, code, TJS_IGNOREPROP); code += 4; break;
case VM_SPD: SetPropertyDirect(ra, code, 0); code += 4; break;
case VM_SPDE: SetPropertyDirect(ra, code, TJS_MEMBERENSURE); code += 4; break;
case VM_SPDEH: SetPropertyDirect(ra, code, TJS_MEMBERENSURE|TJS_HIDDENMEMBER); code += 4; break;
case VM_SPDS: SetPropertyDirect(ra, code, TJS_MEMBERENSURE|TJS_IGNOREPROP); code += 4; break;
case VM_GPI: GetPropertyIndirect(ra, code, 0); code += 4; break;
case VM_GPIS: GetPropertyIndirect(ra, code, TJS_IGNOREPROP); code += 4; break;
case VM_SPI: SetPropertyIndirect(ra, code, 0); code += 4; break;
case VM_SPIE: SetPropertyIndirect(ra, code, TJS_MEMBERENSURE); code += 4; break;
case VM_SPIS: SetPropertyIndirect(ra, code, TJS_MEMBERENSURE|TJS_IGNOREPROP); code += 4; break;
case VM_GETP: GetProperty(ra, code); code += 3; break;
case VM_SETP: SetProperty(ra, code); code += 3; break;
case VM_DELD: DeleteMemberDirect(ra, code); code += 4; break;
case VM_DELI: DeleteMemberIndirect(ra, code); code += 4; break;
case VM_SRV: if(result) result->CopyRef(TJS_GET_VM_REG(ra, code[1])); code += 2; break;
case VM_RET: return code+1-CodeArea;
case VM_ENTRY: code = CodeArea + ExecuteCodeInTryBlock(ra, code-CodeArea + 3, args, numargs, result, TJS_FROM_VM_CODE_ADDR(code[1])+code-CodeArea, TJS_FROM_VM_REG_ADDR(code[2])); break;
case VM_EXTRY: return code+1-CodeArea; // same as ret
case VM_THROW: ThrowScriptException(TJS_GET_VM_REG(ra, code[1]), Block, CodePosToSrcPos(code-CodeArea)); code += 2; // actually here not proceed... break;
case VM_CHGTHIS: TJS_GET_VM_REG(ra, code[1]).ChangeClosureObjThis( TJS_GET_VM_REG(ra, code[2]).AsObjectNoAddRef()); code += 3; break;
case VM_GLOBAL: TJS_GET_VM_REG(ra, code[1]) = Block->GetTJS()->GetGlobalNoAddRef(); code += 2; break;
case VM_ADDCI: AddClassInstanceInfo(ra, code); code+=3; break;
case VM_REGMEMBER: RegisterObjectMember(ra[-1].AsObjectNoAddRef()); code ++; break;
case VM_DEBUGGER: TJSNativeDebuggerBreak(); code ++; break;
default: ThrowInvalidVMCode(); } } } catch(eTJSSilent &e) { throw e; } catch(eTJSScriptException &e) { e.AddTrace(this, codesave-CodeArea); throw e; } catch(eTJSScriptError &e) { e.AddTrace(this, codesave-CodeArea); throw e; } catch(eTJS &e) { DisplayExceptionGeneratedCode(codesave - CodeArea, ra_org); TJS_eTJSScriptError(e.GetMessage(), this, codesave-CodeArea); } catch(exception &e) { DisplayExceptionGeneratedCode(codesave - CodeArea, ra_org); TJS_eTJSScriptError(e.what(), this, codesave-CodeArea); } catch(const wchar_t *text) { DisplayExceptionGeneratedCode(codesave - CodeArea, ra_org); TJS_eTJSScriptError(text, this, codesave-CodeArea); } catch(const char *text) { DisplayExceptionGeneratedCode(codesave - CodeArea, ra_org); TJS_eTJSScriptError(text, this, codesave-CodeArea); }#ifdef TJS_SUPPORT_VCL catch(const EAccessViolation &e) { DisplayExceptionGeneratedCode(codesave - CodeArea, ra_org); TJS_eTJSScriptError(e.Message.c_str(), this, codesave-CodeArea); } catch(const Exception &e) { DisplayExceptionGeneratedCode(codesave - CodeArea, ra_org); TJS_eTJSScriptError(e.Message.c_str(), this, codesave-CodeArea); }#endif
return codesave-CodeArea;}
果然还是相当典型且直观的解释器。这种不断读入VM码(在每个case之后决定code的增量),并通过单一的switch语句来完成dispatch的方法,是基本解释器实现里最直观,但通常也是最慢的方式。
对于一个比较小的指令集(例如RISC),threaded code通常是更好的解决方案,因为在每个指令例程的最后添加跳转操作能减少现代CPU的跳转预测失误。其中又有indirected与directed这两大类的threadeding。[url=http://www.complang.tuwien.ac.at/anton/home.html]Anton Ertl[/url]有篇[url=http://www.complang.tuwien.ac.at/forth/threaded-code.html]关于threaded code不错的文章[/url]。在[url=http://www.amazon.com/Virtual-Machines-Versatile-Platforms-Architecture/dp/1558609105]Virtual Machines: Versatile Platforms for Systems and Processes[/url]一书中有更详细的解释。
TJS2 VM现有的实现有不少让人很想吐槽的地方,例如说基于引用计数的GC,还有那难以阅读的代码……那么大量的宏用起来真够难受的。
其实W.Dee氏之所以宁可直接开始实现吉里吉里3的Risse VM而不在现有的吉里吉里2的codebase上修改,恐怕也是因为这codebase太乱了吧。新的Risse VM已经有了不少实质性的改进,例如用Boehm GC来代替原本不太好的引用计数GC;将中间表示(IR)改进为SSA形式,等等。不过就这么那TJS2 VM扔掉也怪可惜的。想慢慢把TJS2 VM中可改进的地方挖出来,看看是否适合给予改进。如果能赶在吉里吉里3的Risse VM完成前对TJS2 VM做些改进,那还算有价值。
不过吉里吉里系列内的VM有个很紧的要求,那就是整个运行时的外表看起来要像一个解释器,即:内部实现是先将文本形式的脚本源代码编译为中间表现,然后再由VM执行(此处的VM又是一个真正的解释器)。这对编译的部分要求比较高,使一些耗时间的优化不太好进行。要是W.Dee氏肯接受真正完整的编译,再交由VM执行,会轻松很多。
(试想一下,写一个程序把Java Compiler与JVM包装起来,像解释脚本一样执行Java源文件。如果你用的是Sun的JDK,恭喜你,编译HelloWorld可能也要半分钟。而且JDK 1.6.0系列还经常诡异的出现NoClassDefFoundError,让我只好对1.6.0系列敬而远之)
附注:吉里吉里2的源代码基于GPL许可证发布
吉里吉里2中TJS2 VM的dispatch loop相关推荐
- 吉里吉里1/吉里吉里2中KAG脚本的解释执行(1)
从我开始关注吉里吉里2这个引擎开始,就一直看到关于"KAG的执行效率比TJS2低很多"的说法.但是到底慢多少呢?没见到过具体测评. 有机会的话我看看设计一个测评方法好了.关键是看看 ...
- 吉里吉里2 2.30版正式发布了
这应该是不到两个小时之前才发生的事吧--嗯应该还算是新闻. 吉里吉里2 2.30下载点:[url]http://kikyou.info/tvp/[/url] 从2.28到2.30版的变更点: [quo ...
- 吉里吉里2相关的一些引用资料
[url=http://www.aiplus.idv.tw/phpBB2/viewtopic.php?f=28&t=5038][後知後覺]吉里吉里與KAG引擎與Fate/StayNight[/ ...
- 吉里吉里2 2.28 rev3发布
2008/01/22 [quote][list][*]レイヤの重ね合わせ方によってはまれに EAccessViolation 例外が発生していたのを修正 [*]きわめてまれなケース(intervalが ...
- 在FlashBuilder里的ActionScript工程中使用Flash CS5中的类 fl.controls库
在FlashBuilder里的ActionScript工程中使用Flash CS5中的类 1.用Flash CS5打开User Interface.fla文件 我的路径:C:\Program File ...
- 360 浏览器设置里安全设置里清除上网痕迹中没有“管理保存过的账号和密码”这个选项
360浏览器设置里安全设置里清除上网痕迹中没有"管理保存过的账号和密码"这个选项 点击360浏览器右上角"管理"->"管理"里的&qu ...
- 计算机配置表中的内存是指什么,电脑里的性能选项中,处理器计划和内存使用所写的是什么意思...
电脑里的性能选项中,处理器计划和内存使用所写的是什么意思以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! 电脑里的性能选项 ...
- 重电计算机学院李明建,【i小T有话说 | 第8期】风里雨里,我在上电计算机学院等你!...
原标题:[i小T有话说 | 第8期]风里雨里,我在上电计算机学院等你! 临港的风为炎炎夏日带来凉意 在这舒服的时节 我们上电计算机又迎来了新的一批小可爱 2019年9月1日上午,各位领导.老师和学长学 ...
- 两台xenserver 同一个vlan中的vm 不能ping通?
我遇到一个问题 我有两台Xenserver, 放到一个pool里面, 创建了一个vlan10的网络 同一台xenserver中的vm之间可以ping通,vm都在vlan10里面. 两台xenserve ...
最新文章
- pandas read_csv ‘utf-8‘ codec can‘t decode bytes in position 1198-1199: invalid continuation byte解决
- 5 Handler与子线程
- 蔡崇信将担任阿里巴巴集团第一组董事
- 单身狗应该怎么过七夕节?
- 微信小游戏flappy bird填坑
- 离散数学及其应用 第一章:集合论
- 乔布斯简介及其十大经典语录
- 23西南大学电子信息907专硕考研经验贴
- 计算机大赛横幅标语有趣的,有创意的横幅标语大全
- 【Wing Loss】《Wing Loss for Robust Facial Landmark Localisation with Convolutional Neural Networks》
- EAX、ECX、EDX、EBX寄存器的作用
- opencv实战——图像矫正算法深入探讨
- requests.exceptions.ChunkedEncodingError: (‘Connection broken: IncompleteRead(0 bytes read)‘, Incomp
- 未被定义的 “智能座舱”,如何将产业化进行到底
- 2022-2028年中国植物蛋白饮品行业市场全景调研及战略咨询研究报告
- POWER PIVOT的使用介绍1
- netty(五) 【 transferTo 零拷贝实例】
- 【Vue】vuex-五个核心
- 由于找不到服务器或出现DNS错误
- 二维码如何区分微信支付还是支付宝