【CS 143 Compiler】Assignment 4.1:阅读源码 & 理解任务

汇编码布局

Spim 手册中的图示如下,编译器输出的 目标文件(Object File)布局为:

目标文件(Object File)布局

代码存放在 .text 段,而代码的总入口是 _start_start 会做一些准备工作(包括初始化垃圾处理程序, 初始化 Main 函数) 最后调用 Main 对象实例 中的 main 方法 开始执行 COOL 程序.

_start 作为入口符号是链接器的默认设置(也可以自定义)。参考:A Whirlwind Tutorial on Creating Really Teensy ELF Executables for Linux (muppetlabs.com)

_start 也是大多数编程语言的 runtime 的入口函数。_start 作为引导程序,包括一些准备工作,然后引导进入主程序。参考: gcc - What is the use of _start() in C? - Stack Overflow

runtime 不一定是和主程序并行的进程或线程,也包括为主程序代码作准备工作和收尾的辅助代码。

目标代码如何运行

下面就是引导程序 _start。根据 COOL 手册中提示, runtime 代码包含在 [cool root]/lib/trap.handler 中。_start 作为开始入口,必然也在其中。

	.globl __start      
__start:
	li	$v0 9           # 指定系统调用代码 9: sbrk
	move	$a0 $zero   # $a0 用来指定 sbrk 的入参, 这里值为 0, 表示分配内存大小
	syscall				# sbrk: 程序启动时, 向 OS 申请一块内存, $v0 保存了这块内存的地址
	move	$a0 $sp			# initialize the garbage collector
	li	$a1 MemMgr_REG_MASK
	move	$a2 $v0
	jal	_MemMgr_Init		# sets $gp and $s7 (limit) 初始化了垃圾处理程序

	la    	$t9 _exception_handler	# Exception: Set uncaught Exception Address

	la	$a0 Main_protObj	# create the Main object
	jal	Object.copy		    # Call copy
	addiu	$sp $sp -4
	sw	$a0 4($sp)		    # save the Main object on the stack
	move	$s0 $a0			# set $s0 to point to self
	jal	Main_init		    # initialize the Main object 执行 Main.init()
	jal	Main.main		    # Invoke main method 执行 main()

要点:

  • .globl _start.globl 用来声明 符号 _start 对此文件外的文件可见.
  • syscall 指令抛出一个 陷入 (trap) 来调用底层的系统调用. 调用哪个系统调用 (syscall编码) 由 $v0 中的内容指定.
    • 这一块内存 是在 程序进程的内存空间中的 Heap 中分配的. (C 语言中 malloc 就是基于这个系统调用实现的.)
    • 申请了 大小为 0 的堆空间, 实际上是得到了 Heap 的首地址. 相应的 $sp 是栈顶, 实际上也是 堆 的结束地址.
    • 程序载入时 OS 会将所有寄存器清零,只有 $sp 会赋值为栈顶第一个空余位置。

“代码生成”(cgen)生成什么

通过文件流,输出一个完整的汇编代码。这段代码中同样包含 .data .text 这些段,它们会被链接器和 runtime 代码 链接在一起。

cgen 程序总入口为:

// cgen.cc
void program_class::cgen(ostream &os) 
{
  // spim wants comments to start with '#'
  os << "# start of generated code\n";

  initialize_constants();
  CgenClassTable *codegen_classtable = new CgenClassTable(classes,os);

  os << "\n# end of generated code\n";
}

初始只执行了 CgenClassTable 的构造函数:

// cgen.cc
CgenClassTable::CgenClassTable(Classes classes, ostream& s) : nds(NULL) , str(s)
{
   stringclasstag = 0 /* Change to your String class tag here */;
   intclasstag =    0 /* Change to your Int class tag here */;
   boolclasstag =   0 /* Change to your Bool class tag here */;

   enterscope();
   if (cgen_debug) cout << "Building CgenClassTable" << endl;
   install_basic_classes();
   install_classes(classes);
   build_inheritance_tree();

   code();
   exitscope();
}

enterscope()exitscope() 之间,前三步用依赖图的形式把所有的 Class_ 组织起来。

重点在于 code() (如下)。code_xxx() 执行的顺序,也是汇编代码从上到下的顺序。前三个(以及第一个注释区)都是生成 .data 段 的代码;而 后面的部分是生成 .text 段的代码。

// cgen.cc
void CgenClassTable::code()
{
  if (cgen_debug) cout << "coding global data" << endl;
  code_global_data();

  if (cgen_debug) cout << "choosing gc" << endl;
  code_select_gc();

  if (cgen_debug) cout << "coding constants" << endl;
  code_constants();

//                 Add your code to emit
//                   - prototype objects
//                   - class_nameTab
//                   - dispatch tables

  if (cgen_debug) cout << "coding global text" << endl;
  code_global_text();

//                 Add your code to emit
//                   - object initializer
//                   - the class methods
//                   - etc...

}

code_constants() 中注释为写入编译时需要使用的常量,包括编译过程中通过 stringtable::add_string 创建的 Int、Str、Bool 值的 Symbol。因为 Bool 只有两个值,所以 true 也存进来了。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值