⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 chapter 8 procedures and functions.htm

📁 英文版编译器设计:里面详细介绍啦C编译器的设计
💻 HTM
📖 第 1 页 / 共 3 页
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<!-- saved from url=(0062)http://topaz.cs.byu.edu/text/html/Textbook/Chapter8/index.html -->
<HTML><HEAD><TITLE>Chapter 8: Procedures and Functions</TITLE>
<META http-equiv=Content-Type content="text/html; charset=iso-8859-1">
<META content="MSHTML 6.00.2800.1458" name=GENERATOR></HEAD>
<BODY><!----------------------------------------------------------------------------->
<H2>8.1 Procedures and Functions</H2><!----------------------------------------------------------------------------->Up 
to this point, we have made a strong effort to base the material in this text on 
the grammar rules. Each point has been explained by showing where it was in the 
compiler in relation to some grammar rule. This chapter is a little different, 
since the points of reference are fairly sparce. In this chapter we will explain 
how procedures work in SAL. Generating code for a procedure is not really too 
complicated. The hardest part is in getting the arguments (if there are any) off 
of the EES. For the most part, the real work is shown in figure {RULE60}. 
<MENU><IMG src="Chapter 8 Procedures and Functions.files/RULE60.gif">
  <P><FONT face=arial size=-1><B>Figure {RULE60}.</B> The code generation that 
  needs to be done in rule ProcFuncBlock. The bulk of the work that needs to be 
  done is in storing parameters and in allocating arrays. The latter part, i.e., 
  the call to <TT>PutCodeText()</TT> writes the procedure to disk.</FONT> 
</P></MENU>Basically, a procedure/function is laid out in memory as is shown in 
figure {PROCMEM}. 
<MENU><IMG src="Chapter 8 Procedures and Functions.files/PROCMEM.gif">
  <P><FONT face=arial size=-1><B>Figure {PROCMEM}.</B> Layout of a procedure or 
  function in memory.</FONT> </P></MENU>A procedure or function consists of five 
parts: 
<MENU><B>The <TT>ENTR</TT> instruction:</B> This should always be the first 
  instruction in every procedure block. The VM actually checks to see if this 
  instruction is present before executing a procedure or a function. The 
  <TT>ENTR</TT> instruction also takes a 16-bit immediate parameter which tells 
  the VM how many bytes of space on the procedure stack need to be allocated for 
  local variables.
  <P><B>Storing Parameters:</B> If parameters are passed by value, they need to 
  be stored in local variables. If they are passed by reference, then pointers 
  to the memory locations where their variables are stored need to be stored in 
  local memory.
  <P><B>Allocate Local Records/Arrays:</B> Local records and arrays are stored 
  just like global records and arrays: by a 32-bit pointer that references the 
  actual data. This needs to be allocated by the compiler.
  <P><B>Procedure Body:</B> This is the body of the procedure. This is where all 
  the statements of the procedure goes.
  <P><B>The <TT>RTN</TT> Instruction:</B> This is the lase instruction. It needs 
  to be emitted (for safety's sake) even if the compiler was compiling a 
  function, which normally ends with a <TT>return</TT> statement. </P></MENU>Let 
us examine each of these areas in greater detail, one by one.
<P>
<P>&nbsp;
<P>
<H3>8.1.1 The <TT>ENTR</TT> Instruction</H3><!----------------------------------------------------------------------------->The 
purpose of the <TT>ENTR</TT> instruction is to allocate space on the stack for 
local variables. Figure {ENTR} is a description of how this works. We can see 
that all it really does is move the S register over so we have space to store 
some variables. 
<MENU><IMG src="Chapter 8 Procedures and Functions.files/ENTR.gif">
  <P><FONT face=arial size=-1><B>Figure {ENTR}.</B> The <TT>ENTR</TT> 
  instruction.</FONT> </P></MENU>The amount of bytes allocated by this instruction 
is the total size of a procedure/function's variables: that is, size of 
parameters plus size of local variables. At this time, arrays and records are 
given a size of just their pointer, which is four bytes. For instance, the 
following function would start with an <TT>ENTR</TT> instruction that specified 
a size of 13 bytes. <PRE>      func DoIt(x: int; var z: MyArray)
        var
          p: MyRecord;
          q: bool;

      begin
        . . .
      end func.
</PRE>All arrays and records are considered to be pointers, and have four bytes. 
All parameters that are passed by reference are considered to be pointers, and 
are also four bytes. All other parameters are considered to be their normal 
size, e.g., <TT>bool</TT>s are 1, <TT>char</TT>s are 1, <TT>int</TT>s are 4, 
etc.
<P>
<P>&nbsp;
<P>
<H3>8.1.2 Storing Parameters</H3>Procedures and functions in SAL always make 
local copies of each of their parameters. For each dummy argument, a variable is 
created, named, and is given an offset on the local stack. This is the reverse 
of the process used when calling a procedure or function, which is handled in 
rule ProcFuncCall. This task is illustrated in figure {STOPAR}. 
<MENU><IMG src="Chapter 8 Procedures and Functions.files/STOPAR.gif">
  <P><FONT face=arial size=-1><B>Figure {STOPAR}.</B> Storing a 
  procedure/function's parameters into local variables.</FONT> </P></MENU>This 
process (numbered (2) in figures {RULE60} and {PROCMEM}) is done by the 
following algorithm: 
<P><PRE><B>01</B>      //*** Get a pointer to the last parameter
<B>02</B>      ident = funcblock-&gt;getLastParam();
<B>03</B>
<B>04</B>      //*** Loop thru all parameters in reverse
<B>05</B>      while ident &lt;&gt; null do
<B>06</B>        if ident-&gt;getByVal() then                     //*** if pass by value copy it in
<B>07</B>          select ident-&gt;getType() from
<B>08</B>            case arraytyp:                              // array, record
<B>09</B>            case recordtyp: 
<B>10</B>              Emit (LID, ident-&gt;getExtra()-&gt;getSize());    
<B>11</B>              Emit (PCOP, ident-&gt;getOffset());
<B>12</B>            else:                                       // bool, char, int, card, or real
<B>13</B>              select ident-&gt;getSize() from
<B>14</B>                case 1:
<B>15</B>                  Emit (SLB, ident-&gt;getOffset());   
<B>16</B>                case 2:
<B>17</B>                  Emit (SLW, ident-&gt;getOffset());   
<B>18</B>                case 4:
<B>19</B>                  Emit (SLD, ident-&gt;getOffset());   
<B>20</B>                case 8:
<B>21</B>                  Emit (SLQ, ident-&gt;getOffset());   
<B>22</B>              end select;
<B>23</B>          end select;
<B>24</B>        else 
<B>25</B>          Emit (SLD, ident-&gt;getOffset());             //*** Arg is passed by reference; store pointer
<B>26</B>        end if;
<B>27</B>
<B>28</B>        ident = funcblock-&gt;getPrevIdent();
<B>29</B>      loop;
<B>30</B>
<B>31</B>
</PRE>
<MENU><FONT face=arial size=-1><B>Listing {LCLSTO}.</B> The algorithm for 
  storing local copies of all parameters.</FONT> </MENU>If the argument is an 
array passed by value then we need to make a local copy. If the argument is an 
intrinsic type, then we just store it at its offset. Otherwise, if the argument 
is passed by reference then it is always a pointer, and we store that. The next 
three sections take a closer look at how this is done. 
<P>&nbsp;
<P>
<H4>8.1.2.1 Arrays and Records Passed by Value <I>(Lines 8-11)</I></H4>When we 
pass an array or a record by value, we need to make our own local copy. We are 
given a pointer on the EES to the actual array. What we need to do is allocate 
space for the local copy, and set the local pointer to point to the local copy. 
This can be accomplished with a single instruction, called <TT>PCOP</TT>. 
<TT>PCOP</TT> takes a 32-bit immediate parameter that indicates the local offset 
of the pointer to the data, and 32-bit parameter from the EES that indicates the 
size of the data. Figure {PCOP} explains all this. 
<MENU><IMG src="Chapter 8 Procedures and Functions.files/PCOP.gif">
  <P><FONT face=arial size=-1><B>Figure {PCOP}.</B> The <TT>PCOP</TT> 
  instruction. This instruction not only copies the array or record, it also 
  initializes the local pointer.</FONT> </P></MENU>There is a difference between a 
<TT>PCOP</TT> instruction and a memory-copy, like <TT>memcpy()</TT>. A 
<TT>memcpy()</TT> copies memory from a source pointer to a destination pointer. 
A <TT>PCOP</TT> copies data from the EES to a pointer. The difference is that 
the source pointer is on the EES, and the destination pointer is the S register. 
When <TT>PCOP</TT> is through, it will store the value of the S register in the 
pointer to the structure, and then move S to the byte immediately following the 
structure's data.
<P>
<P>&nbsp;
<P>
<H4>8.1.2.2 Intrinsic Types Passed by Value <I>(Lines 13-22)</I></H4>These 
instructions explicitly store each scalar parameter one by one, from the EES 
into local memory. We use the <TT>SLx</TT> instructions to accomplish this task. 
Each time we give the offset to the local variable. See the code for details.
<P>
<P>&nbsp;
<P>
<H4>8.1.2.3 Parameters Passed by Reference <I>(Lines 25)</I></H4>When parameters 
are passed by reference, all we store is a pointer. This is represented by 
figure {BYREF}. See listing {LCLSTO} for details. 
<MENU><IMG src="Chapter 8 Procedures and Functions.files/BYREF.gif">
  <P><FONT face=arial size=-1><B>Figure {BYREF}.</B> Anything passed by 
  reference is stored locally as a pointer to the data.</FONT> </P></MENU>
<P>The last thing that the algorithm does is on line 28. The algorithm has a 
loop that iterates continually in reverse until there are no more parameters.
<P>
<P>&nbsp;
<P>
<H3>8.1.3 Allocating Local Arrays and Records</H3>This step is numbered (3) in 
figures {RULE60} and {PROCMEM}. Since the area for storage of local arrays and 
records was not allocated with the <TT>ENTR</TT> instruction, we need to have 
the compiler allocate the space explicitly. This is done using the 
<TT>ALLOC</TT> instruction. 
<MENU><IMG src="Chapter 8 Procedures and Functions.files/ALLOC.gif">
  <P><FONT face=arial size=-1><B>Figure {ALLOC}.</B> The <TT>ALLOC</TT> 
  instruction before and after execution. This instruction loads the value of S 
  onto the EES, and then increments S by the size indicated in its immediate 
  parameter.</FONT> </P></MENU>Once the space for the array or record has been 
allocated, the address of the block will be placed on the EES. This address must 
be stored in the pointer to the array or record, using a <TT>SLD</TT>. The 
offset to the <TT>SLD</TT> instruction should be the offset to the pointer to 
the array/record. Once the array/record block has been allocated on the stack 
and the address of the block stored at the pointer's location, the array should 
look like figure {LOCAR}. 
<MENU><IMG src="Chapter 8 Procedures and Functions.files/LOCAR.gif">
  <P><FONT face=arial size=-1><B>Figure {LOCAR}.</B> A local array/record after 
  it has been properly allocated and the pointer has been properly set.</FONT> 
  </P></MENU>The general algorithm goes as follows. We get the function's first 
identifier and loop forward through the identifiers. For each identifier that is 
an array or a record, we emit an <TT>ALLOC</TT> and the size of the 
array/record's data. Then we emit a <TT>SLW</TT> at the offset to the pointer to 
the array/record. <PRE>      ident = funcblock-&gt;getFirstVar();

      while ident &lt;&gt; null do
        if (ident-&gt;getObj = varobj) and ident-&gt;getType()-&gt;IsComplexType() then
          Emit (ALLOC, ident-&gt;getExtra()-&gt;size());
          Emit (SLD, ident-&gt;getOffset());
        end if;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -