本 Wiki 开启了 HTTPS。但由于同 IP 的 Blog 也开启了 HTTPS,因此本站必须要支持 SNI 的浏览器才能浏览。为了兼容一部分浏览器,本站保留了 HTTP 作为兼容。如果您的浏览器支持 SNI,请尽量通过 HTTPS 访问本站,谢谢!
这里会显示出您选择的修订版和当前版本之间的差别。
两侧同时换到之前的修订记录前一修订版后一修订版 | 前一修订版 | ||
cs:comp_n_arch:courses:fnti_ii:week_1 [2025/05/22 22:38] – codinghare | cs:comp_n_arch:courses:fnti_ii:week_1 [2025/05/24 02:17] (当前版本) – [Memory segments] codinghare | ||
---|---|---|---|
行 4: | 行 4: | ||
VM(// | VM(// | ||
{{ : | {{ : | ||
+ | ====Complication Overview==== | ||
+ | >We can only see a short distance ahead, but we can see plenty there that needs to be done. --//Alan Turing// | ||
+ | ===Two tiers compilation=== | ||
+ | 为什么会有这种架构: | ||
+ | * 计算器的构成不同,导致机器语言不同 | ||
+ | * 直接面向机器语言的编译器是 machine language depended,编译器实现不同。 | ||
+ | <WRAP center round tip 100%> | ||
+ | 就应用上来说,这种设计下的高级语言都有**跨平台**的优势。 | ||
+ | </ | ||
+ | |||
+ | ==Tier I: virtual machine== | ||
+ | //VM// 的理念就是将所有的高级语言翻译为一种抽象化的代码:// | ||
+ | ==Tier II: virtual machine implementation== | ||
+ | 本层级负责具体的,基于计算机类型的实现。具体的工作是将虚拟机中的 // | ||
+ | ===Jack compilation=== | ||
+ | * Jack compiler: Jack 语言 -> VM code | ||
+ | * VM emulator:VMcode to PC & Hack computer | ||
+ | ====VM Abstraction: | ||
+ | VM code 有两点需要关注: | ||
+ | * VM code 需要足够的接近高级语言层,这样编译器的实现会相对简单 | ||
+ | * VM code 也需要足够接近底层,这样 VM implementaion 也会相对简单 | ||
+ | ===Stack machine abstraction=== | ||
+ | //Stack machine abstraction// | ||
+ | ==Stack 的基础操作== | ||
+ | Stack 由一块 RAM 和一个 Stack 组成。 | ||
+ | Stack 拥有两种基础操作: | ||
+ | * '' | ||
+ | * '' | ||
+ | {{ : | ||
+ | ==Stack 的运算== | ||
+ | 基于上述两种基础操作,Stack 中定义了一系列算术运算。这些算术运算的基础流程为: | ||
+ | * 将所有需要 argument 从 stack 中 pop 出来 | ||
+ | * 计算结果 | ||
+ | * 将结果 push 回 stack | ||
+ | 比如下面的命令 '' | ||
+ | * 先将两个元素都 pop 出来 | ||
+ | * 进行加法运算 | ||
+ | * 在 push 到原有的 stack 里 | ||
+ | {{ : | ||
+ | \\ | ||
+ | 常见的命令: | ||
+ | * 算术:'' | ||
+ | * 逻辑:'' | ||
+ | <WRAP center round info 100%> | ||
+ | * 常见的命令分几个部分:算术 / 逻辑运算,内存相关运算,Branching & 函数相关运算 | ||
+ | * 这一系列的命令序列是基于高级语言的内容,通过编译器转化而来 | ||
+ | * 运算的算子按 top-most 的顺序来计算:也就是左算子是栈顶值,而右算子是栈顶的下一个值 | ||
+ | </ | ||
+ | |||
+ | <WRAP center round box 100%> | ||
+ | 可以看到一个有趣的现象,Stack 的结构非常利于将复合运算分解为子运算的过程,用序列化的指令表达出来。 | ||
+ | </ | ||
+ | ====VM Abstraction: | ||
+ | **// | ||
+ | 我们在编程中通常会根不同作用域的变量打交道。比如下面的 jack 语言: | ||
+ | <code clike> | ||
+ | static inst s1, s2; | ||
+ | function int bar(int x, int y) | ||
+ | { | ||
+ | var int a, b, c; | ||
+ | ... | ||
+ | let c = s1 + y; | ||
+ | ... | ||
+ | } | ||
+ | </ | ||
+ | 如果翻译为 VM code,这几个变量的作用域实际上完全不同。有些是需要在函数运行完毕后销毁的局部变量,而有些则是需要保存下来全局变量: | ||
+ | <code armasm> | ||
+ | push s1 // global | ||
+ | push y // argument | ||
+ | add | ||
+ | pop c // local | ||
+ | </ | ||
+ | ===Memory segments=== | ||
+ | 本课的 VM 设计中提出了 memory segment 这一概念来解决该问题。其原理是将不同类型的变量存储到对应的 memory segment 中。比如上面的例子:\\ \\ | ||
+ | {{ : | ||
+ | \\ | ||
+ | 实际上通过 memory segment 对所有的变量进行了一次分组。因此对应的命令实际上可以改写为: | ||
+ | <code asm> | ||
+ | push static 0 // s1 | ||
+ | push argument 1 // y | ||
+ | add | ||
+ | pop local 2 // c | ||
+ | </ | ||
+ | 实际上,VM 编译器无法识别变量名,在翻译的过程中,所有的变量名都会被解释为 '' | ||
+ | * '' | ||
+ | * '' | ||
+ | 比如下面的例子: | ||
+ | <code armasm> | ||
+ | push local 1 // 将 local[1] 的内容推到栈顶 | ||
+ | pop local 1 // 将栈顶的内容移除,并存储到 local[1] | ||
+ | </ | ||
+ | 本课的 VM 提供了八种类型的 memory segments: | ||
+ | <code armasm> | ||
+ | local // 局部变量 | ||
+ | argument // 参数变量 | ||
+ | this // 类对象 | ||
+ | that // | ||
+ | constant // 常量 | ||
+ | static // | ||
+ | </ | ||
+ | <WRAP center round important 100%> | ||
+ | * 注意 '' | ||
+ | * 双目运算的算子出栈后**不会**写回对应的 segment | ||
+ | </ | ||
+ | ====VM Implementation: |