<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>StephenChan&#039;s Tech Space &#187; Compiler</title>
	<atom:link href="http://blog.endlesscode.com/category/compiler/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.endlesscode.com</link>
	<description>Stay Hungry. Stay Foolish.</description>
	<lastBuildDate>Tue, 25 Oct 2011 01:15:07 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>基于栈的运行时环境</title>
		<link>http://blog.endlesscode.com/2009/12/12/runtime-env-based-on-stack/</link>
		<comments>http://blog.endlesscode.com/2009/12/12/runtime-env-based-on-stack/#comments</comments>
		<pubDate>Sat, 12 Dec 2009 20:00:22 +0000</pubDate>
		<dc:creator>Stephen</dc:creator>
				<category><![CDATA[Compiler]]></category>

		<guid isPermaLink="false">http://blog.endlesscode.com/?p=7</guid>
		<description><![CDATA[在《Compiler Construction Principles and Practice》一书中讲了两种语言运行时环境：完全静态运行时环境和基于栈的运行时环境。 完全静态运行时环境是最简单的运行时环境类别，其所有的数据都是静态的，且在程序执行期间内存中的数据都是固定的，这种运行时环境没有指针和动态内存分配，并且也不支持函数递归调用，这类语言的一个标准例子就是FORTRAN77。在完全静态运行时环境中，所有的变量，不论全局和局部的变量，都是静态分配的，我们可以通过固定的地址来直接访问到这些变量。 基于栈的运行时环境是我们常见的语言运行时环境，如C，C++等语言就是基于栈的运行时环境。允许递归调用并且在递归调用过程中要重新分配局部变量，不能静态分配活动记录(Activation Record)。一般在内存中，数据的组织如下： 代码区域是程序的执行指令，是不允许修改的，接下来就是全局和静态数据区，然后就是栈和堆。栈是向下生长的(向地址空间减少的方向)，而堆是向上生长的(向地址空间增加的方向)。在内存分配中的一个重要单元为过程活动记录(procedure activation record)，它包含了过程或函数被调用时内存分配的局部数据，一个活动记录一般包含下面几个部分： 其实活动记录就是函数调用时被压入栈的数据总称，首先被压入栈的是函数或过程的参数，接着是返回地址，然后在函数执行过程中分配的局部数据等等。涉及函数调用还有一个指向当前活动记录的指针，称为框架指针(frame pointer)或fp，通常fp保存在寄存器中，而每个活动记录中，在bookkeeping information区域(薄记信息)，都会有一个值(称为Control Link)指向上一个活动记录，即调用当前函数的函数活动记录，使整个执行流程中的活动记录呈现链状形式。 在活动记录中的参数和局部数据可以通过相对于fp的偏移值来获取。 对于左边的C程序，右边显示其在第2次对g调用时的运行时栈状态： 以上是没有允许定义局部函数的运行时栈的状态，如果在语言编译时允许定义局部的函数，前面的组织方式就没有办法做到了，因为没有提供非局部(nonlocal)和非全局的(nonglobal)的引用，支持这类运行时环境中的语言有Python、Pascal等支持在函数或者过程中再定义函数和过程的语言。解决这个问题的方法可以在bookkeeping information的区域添加添加一个叫访问链(Assess Link)的值，用于指向定义环境(Control Link是指向调用环境的)。在下面左边的Pascal程序中，右边为添加了访问链后的栈运行时状态： 可以看到，在q调用过程中，对变量n的搜索，是直接从定义 q的过程p中搜索，而没有从r的参数中获取。如果在过程中函数多层嵌套定义的过程，需要越过多个作用域去寻找变量，重复地取出访问链来实现。对于这样的访问链工作，编译程序必须能够在局部访问之前判定出要链接多少个嵌套层。这就要求编译程序预先为每个声明计算出嵌套层属性，当进入一个过程或者函数时，嵌套层+1，离开时则-1。 在某些语言中，特别是函数语言如Haskell之类的，不仅允许有局部过程或函数定义,而且还可以将过程和函数作为参数进行传递。在这样的语言中，当调用一个作为参数传递的过程时，编译程序就不可能像上面所说的访问链进行操作了。当将过程作为参数传递时，必须预先计算出被传递过程的访问链并与过程代码的指针一同传递，这时候，被传递的过程就拥有了代码指针和访问链，即指令指针和环境指针，可以让传递进行的过程访问其定义环境中的数据，而指令指针和环境指针就通称为闭包，以&#60;ip, ep&#62;表示，ip表示过程的指令指针，ep表示过程的环境指针。下面左边的表示了一个含有过程参数的例子，右边为被传递过程参数执行时的运行时栈状态： 可以看到过程r被当作过程参数传递给p的时候，在p中被调用时，访问链指向的为定义r的过程p。 以上内容是对《Compiler Construction Principles and Practice》书中讲到基于栈的运行时环境的总结。]]></description>
			<content:encoded><![CDATA[<p>在《Compiler Construction Principles and Practice》一书中讲了两种语言运行时环境：完全静态运行时环境和基于栈的运行时环境。</p>
<p>完全静态运行时环境是最简单的运行时环境类别，其所有的数据都是静态的，且在程序执行期间内存中的数据都是固定的，这种运行时环境没有指针和动态内存分配，并且也不支持函数递归调用，这类语言的一个标准例子就是FORTRAN77。在完全静态运行时环境中，所有的变量，不论全局和局部的变量，都是静态分配的，我们可以通过固定的地址来直接访问到这些变量。</p>
<p>基于栈的运行时环境是我们常见的语言运行时环境，如C，C++等语言就是基于栈的运行时环境。允许递归调用并且在递归调用过程中要重新分配局部变量，不能静态分配活动记录(Activation Record)。一般在内存中，数据的组织如下：</p>
<p style="text-align: center;"><img class="size-full wp-image-11 aligncenter" title="在内存中数据组织图" src="http://blog.endlesscode.com/wp-content/uploads/2009/12/存储器分类.jpg" alt="在内存中数据组织图" width="188" height="190" /></p>
<p style="text-align: left;">代码区域是程序的执行指令，是不允许修改的，接下来就是全局和静态数据区，然后就是栈和堆。栈是向下生长的(向地址空间减少的方向)，而堆是向上生长的(向地址空间增加的方向)。在内存分配中的一个重要单元为过程活动记录(procedure activation record)，它包含了过程或函数被调用时内存分配的局部数据，一个活动记录一般包含下面几个部分：</p>
<p style="text-align: center;"><img class="alignnone size-full wp-image-13" title="活动记录" src="http://blog.endlesscode.com/wp-content/uploads/2009/12/活动记录.jpg" alt="活动记录" width="259" height="205" /></p>
<p style="text-align: left;">其实活动记录就是函数调用时被压入栈的数据总称，首先被压入栈的是函数或过程的参数，接着是返回地址，然后在函数执行过程中分配的局部数据等等。涉及函数调用还有一个指向当前活动记录的指针，称为框架指针(frame pointer)或fp，通常fp保存在寄存器中，而每个活动记录中，在bookkeeping information区域(薄记信息)，都会有一个值(称为Control Link)指向上一个活动记录，即调用当前函数的函数活动记录，使整个执行流程中的活动记录呈现链状形式。</p>
<p><span id="more-7"></span></p>
<p style="text-align: left;"><img class="size-full wp-image-14 aligncenter" title="控制链" src="http://blog.endlesscode.com/wp-content/uploads/2009/12/控制链.jpg" alt="控制链" width="394" height="205" />在活动记录中的参数和局部数据可以通过相对于fp的偏移值来获取。</p>
<p style="text-align: left;">对于左边的C程序，右边显示其在第2次对g调用时的运行时栈状态：</p>
<p><img class="size-full wp-image-15  alignleft" title="cexample" src="http://blog.endlesscode.com/wp-content/uploads/2009/12/cexample.jpg" alt="cexample" width="197" height="369" /></p>
<p style="text-align: center;"><img class="size-full wp-image-16 aligncenter" title="cruntime" src="http://blog.endlesscode.com/wp-content/uploads/2009/12/cruntime.jpg" alt="cruntime" width="367" height="396" /></p>
<p style="text-align: left;">以上是没有允许定义局部函数的运行时栈的状态，如果在语言编译时允许定义局部的函数，前面的组织方式就没有办法做到了，因为没有提供非局部(nonlocal)和非全局的(nonglobal)的引用，支持这类运行时环境中的语言有Python、Pascal等支持在函数或者过程中再定义函数和过程的语言。解决这个问题的方法可以在bookkeeping information的区域添加添加一个叫访问链(Assess Link)的值，用于指向定义环境(Control Link是指向调用环境的)。在下面左边的Pascal程序中，右边为添加了访问链后的栈运行时状态：</p>
<p style="text-align: left;"><img class="alignnone size-full wp-image-17" title="pascal" src="http://blog.endlesscode.com/wp-content/uploads/2009/12/pascal.jpg" alt="pascal" width="291" height="507" /><img class="alignnone size-full wp-image-18" title="pascalruntime" src="http://blog.endlesscode.com/wp-content/uploads/2009/12/pascalruntime.jpg" alt="pascalruntime" width="352" height="339" /></p>
<p style="text-align: left;">可以看到，在q调用过程中，对变量n的搜索，是直接从定义 q的过程p中搜索，而没有从r的参数中获取。如果在过程中函数多层嵌套定义的过程，需要越过多个作用域去寻找变量，重复地取出访问链来实现。对于这样的访问链工作，编译程序必须能够在局部访问之前判定出要链接多少个嵌套层。这就要求编译程序预先为每个声明计算出嵌套层属性，当进入一个过程或者函数时，嵌套层+1，离开时则-1。</p>
<p style="text-align: left;">在某些语言中，特别是函数语言如Haskell之类的，不仅允许有局部过程或函数定义,而且还可以将过程和函数作为参数进行传递。在这样的语言中，当调用一个作为参数传递的过程时，编译程序就不可能像上面所说的访问链进行操作了。当将过程作为参数传递时，必须预先计算出被传递过程的访问链并与过程代码的指针一同传递，这时候，被传递的过程就拥有了代码指针和访问链，即指令指针和环境指针，可以让传递进行的过程访问其定义环境中的数据，而指令指针和环境指针就通称为闭包，以&lt;ip, ep&gt;表示，ip表示过程的指令指针，ep表示过程的环境指针。下面左边的表示了一个含有过程参数的例子，右边为被传递过程参数执行时的运行时栈状态：</p>
<p style="text-align: left;"><img class="alignnone size-full wp-image-19" title="pascalfunc" src="http://blog.endlesscode.com/wp-content/uploads/2009/12/pascalfunc.jpg" alt="pascalfunc" width="278" height="476" /><img class="alignnone size-full wp-image-21" title="pascalfuncruntime" src="http://blog.endlesscode.com/wp-content/uploads/2009/12/pascalfuncruntime1.jpg" alt="pascalfuncruntime" width="362" height="408" /></p>
<p style="text-align: left;">可以看到过程r被当作过程参数传递给p的时候，在p中被调用时，访问链指向的为定义r的过程p。</p>
<p style="text-align: left;">以上内容是对《Compiler Construction Principles and Practice》书中讲到基于栈的运行时环境的总结。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.endlesscode.com/2009/12/12/runtime-env-based-on-stack/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

