当前位置: 首页 > article >正文

23 Jumping Back and Forth

1 JVM用了什么机制来实现的if?这章来回答。

2 Lox是个结构化编程的语言。但是clox bytecode 不是

即使字节码是无结构的,我们也要确保编译器只生成与 Lox 本身保持相同结构和嵌套的简洁代码。

3 cpu也这样。他底下就是在jump goto。在底层,goto是唯一的 control flow.

为啥底层的bytecode 不能是结构化编程呢?

4 if 这么搞

expression计算弄完之后,会将值放在栈顶。用这个来判断是否执行if 的body

OP_JUMP_IF_FALSE 这个指令有个operand。来决定如果是false,ip要偏移多少

当写入这个指令时,没有编译完body。所以不知道要偏移多少

backpatching 方法来搞顶偏移量

我们先给OP_JUMP_IF_FALSE 这个指令的operand 一个placeholder。

但编译完body之后。我们可以把这个placeholder的值换一下。

7 用这个函数来实现

因为稍后会有两条不同的指令使用这个辅助指令。我们使用两个字节作为跳转偏移操作数。16 位偏移量可让我们跳转多达 65,535 字节的代码.

compile 完if的body。用这个函数来把真的数填上

这个-2 就是为了上面那两个0xff

8 接下来这样就是实现了

如果时false,就跳过if 的body

9 这里可以用c的if 来实现

Let’s assume we have a function falsey() that takes a Lox Value and returns 1 if it’s falsey or 0 otherwise.

10 else 分支

如果if时true。那么运行完if 的true body(也就是then brach)后还要跳过else

11 then branch后,还要跳转一下

如果有else。在else分支 编译后 要patch一下 这个jump的位置

这个jump指令无条件

12 清理 if 的判断值 (留在了栈上)/每个statement对栈应该无影响。

这里显示的加了pop 操作。这里时为了logical operator可以复用OP_JUMP_IF_FALSE 指令。

最终的逻辑是这样的。不管写不写else都有个隐式的else

然后这样debug code

13 logical operator(and 、or)和+ - 不一样。因为判断是会短路。像是if的变种

and 操作

运行到and_,左侧已经compile完了。这个值如果是false,就不用往右边看了。

如果是true,就不用看这个值了,所以pop以下,就看右边的值就行了。

这时能看出来why

OP_JUMP_IF_FALSE 会把值留在stack的顶部。当左侧是false,这个值就是整个expression的结果。

expression都要有返回值的。这里其实可以创建一个会pop出来的值的指令。

14 or操作

如果左边的值为,那么我们就跳过右边的操作数。因此,我们需要在值为真时跳转。本来可以加个新的指令,但是这里没加。

15

如果左侧operand是false,则小跳以下。跳过的是无条件跳越到右侧operand。

如果左侧operand是true,则执行下一条无条件跳到右侧operand。(这不是最好的实现。)

这里为啥左侧是true的时候,要pop?为了和右边计算。

当这个值为true的时候,这个值留在栈顶。就当返回值了。

这里的pop的意思是不要这个值了。因为0 或上任何数都是那个数

16 while

先计算while()里的expression。然后看这个值是否是false。

如果false。就跳过下面的statement啥的。然后pop出这个expression的值

如果true。先pop出这个expression的值。然后运行statement。

17 但这次有个loop

loopstart用来记录expression的位置。loop时每次都要计算一下这个表达式。

18 然后有这个emitJump() and patchJump()的结合

+2 时OP_LOOP的两个offset,也要加上。

OP_LOOPOP_JUMP 在语义上并无区别,估计是不想改这个函数

还有一个是+ 一个是-

19 

20 for loop的特点

这次还会用 已经有的东西来搞

如果只是for(;;)

21 初始化clause

我们利用var关键字的存在来区分变量声明和表达式。对于表达式,我们调用expressionStatement( ) 而不是 expression() 。它会查找分号(我们在这里也需要分号),并发出OP_POP指令来丢弃值。我们不希望初始化程序在堆栈上留下任何东西。

如果for()里定义了变量那么这里就有了scope

22 condition clause

因为这个condition可有可无。所以如果初始化后下个字符不是;那么就是有这个内容。for(int i =0;i<10;i++)

如果条件是false,就跳出来。把判断值也pop。

然后在后面把这个跳出的offset给补上。

23 increment clause 有难度。因为他是先编译但在for 的body运行后才运行。

首先,我们发出一个无条件跳转,跳过 increment clause’s code,跳到循环的主体。

接下来,我们编译increment clause本身。这通常是一个赋值(执行它只是为了它的副作用)。也要发出一个 pop 来丢弃它的值。

最后的部分

首先,我们发出一条循环指令。这是一个主循环,它将我们带回for循环的顶部--如果有条件表达式的话,就在条件表达式之前。该循环紧接在递增之后,因为递增在每次循环迭代结束时执行。

上面的statement后  还有个emitloop(loopStart);

这里这个emitloop会代码jump到increment clause code。而不是loop的top。

如果没有increment clause code,则会跳到loop的top。

24 现在clox 图灵完备了。只多了3个指令。牛逼。

25 why goto不好?


http://www.kler.cn/a/404083.html

相关文章:

  • Verilog HDL可综合与不可综合语句
  • fastify 连接 mysql
  • Java list
  • 动态规划子数组系列一>等差数列划分
  • 【Linux】【Shell】Shell 基础与变量
  • Mybatis-Plus 多租户插件属性自动赋值
  • debian 如何进入root
  • JS推荐实践
  • AI社媒引流工具:解锁智能化营销的新未来
  • Java语言编程,通过阿里云mongo数据库监控实现数据库的连接池优化
  • 排序【数据结构】【算法】
  • EasyExcel并行导出多个excel文件并压缩下载
  • 登上Nature封面!强化学习+卡尔曼滤波上大分
  • 原生安卓和ios开发的app和uniapp开发的app都有什么特点
  • Docker是一个容器化平台注意事项
  • flutter项目苹果编译运行打包上线
  • Matlab 答题卡方案
  • Unity 使用 Excel 进行配置管理(读Excel配置表、Excel转保存Txt 文本、读取保存的 Txt 文本配置内容)
  • 时序论文22|ICML24港科大:面向多变量不规则的时间序列预测方法
  • 设计模式学习[8]---原型模式
  • Elasticsearch面试内容整理-常见问题和解决方案
  • 微积分复习笔记 Calculus Volume 1 - 6.4 Arc Length of a Curve and Surface Area
  • nacos开启鉴权与配置加密
  • Python | 结合动态加载importlib模块来理解inspect模块的使用
  • Vue项目部署至服务器后报404错误的原因分析及解决方案
  • Dubbo HTTP接入架构