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

📄 chapter 6 assignment and expressions.htm

📁 英文版编译器设计:里面详细介绍啦C编译器的设计
💻 HTM
📖 第 1 页 / 共 5 页
字号:
  <LI><B>In local memory.</B> These variables are found on the local stack at 
  some offset from the L register. Local variables always belong to the current 
  function, and die when the function exits. We can determine whether or not a 
  variable is local if table.curFuncLevel() returns the same value as 
  <TT>Ident::getFuncLev()</TT>. 
  <P></P>
  <LI><B>In a previous scope.</B> If none of the other conditions apply then the 
  variable is assumed to reside in a parent function's scope. A nested procedure 
  can see all the variables of the parent procedure. If a procedure called 
  <TT>foobar()</TT> has two nested procedures called <TT>foo()</TT> and 
  <TT>bar()</TT>, and <TT>foo()</TT> calls <TT>bar()</TT>, <TT>bar()</TT> still 
  needs to be able to see the local variables of <TT>foobar()</TT>. However, by 
  the time <TT>bar()</TT> is called, <TT>foobar()</TT>'s local variables are 
  lost deep in the stack. The variables are accesed through a "static link" 
  (explained in chapter 8). Basically, when <TT>foo()</TT> calls <TT>bar()</TT> 
  it leaves a pointer in the call frame to the parent procedure (i.e., 
  <TT>foobar()</TT>). 
  <P>Variables are retrieved from previous scopes using the <TT>GB</TT> (which 
  stands for Get Base) instruction. This instruction takes a single byte 
  parameter that tells the VM how many jumps back it needs to go. We use the 
  <TT>Ident::getFuncLevel()</TT> method to get the value of this parameter. The 
  <TT>GB</TT> instruction basically returns the value of L for the parent 
  function where the variable lives. This address is placed on the EES, and we 
  can calculate an offset based on this to get to the variable. 
  <P></P></LI></OL>Variables that are passed by reference (or simply, references) 
are by far much simpler, and are stored only one of two ways. They are either 
local or within a previous scope. By definition, a reference can only be a 
function parameter, and as such, a reference must be either local or within a 
parent procedure's scope. In all cases, a reference is a single pointer--in 
other words, there is no such thing as a reference to a reference to a 
reference... If a reference is passed into a procedure that also takes its 
parameter as a reference, then we give the reference that we already have. We 
would not give a reference to the reference. 
<P>
<H4>6.4.2.2 Operations on a Variable</H4>In all, we need to be concerned with 
four different operations on a variable: 
<OL>
  <LI>make an rvalue from a variable, 
  <LI>make an rvalue from a reference, 
  <LI>make an lvalue from a variable, 
  <LI>make an lvalue from a reference. </LI></OL>Variables are assumed to include 
procedure/function parameters passed by value, and all references are 
procedure/function parameters that are passed by reference. We can call 
<TT>Ident::getByVal()</TT> to see whether or not our identifier is a variable 
passed by reference. Let's go over these four cases one by one. 
<P>
<MENU><B>Making an rvalue from a variable.</B> Essentially, we want to get a 
  value from a variable and store it on the EES. This can be something as simple 
  as: <PRE>      var
        x: int;

      begin
        write x;
              ^
              |______ Get the value of x and put it on the EES
</PRE>Our variable will be at some specific offset from one of the four areas 
  that were previously mentioned. We can use <TT>Ident::getOffset()</TT> method 
  to get the offset. We will also need to know the size of the variable. This 
  can be found by calling <TT>Ident::getSize()</TT>. To simplify the amount of 
  conditions that we will encounter, we can use the <TT>EmitLoad()</TT> function 
  found in <TT>SAL-CodeGen.CPP</TT>. This function covers all four cases of 
  where the variable might be stored and covers any possible size that the 
  variable might be. In all this amounts to sixteen different possiblities. This 
  is the code that we use to generate an rvalue of (i.e., retrieve the value 
  from) a variable: 
  <P><PRE>      if ident-&gt;getByVal() = true then
        size:=   ident-&gt;getSize();
        offset:= ident-&gt;getOffset();
        level:=  ident-&gt;getFuncLevel();

        if ident-&gt;getMod() &lt;&gt; table.ModNum then       // Variable is external
          EmitLoad( _LE_, size, offset, ident-&gt;mod() );
        else if level = 0 then                              // Variable is global
          EmitLoad( _LG_, size, offset );
        else if table.curFuncLevel() = level then           // Variable is local
          EmitLoad( _LL_, size, offset );
        else                                                // Variable is in parent proc/func
          Emit( xGB, level );
          EmitLoad( _LS_, size, offset );
        end if;
      end if;
</PRE>
  <P>We begin by taking the size of the variable and getting its offset. If the 
  variable is external, we use the <TT>_LE_</TT> constant, and pass in the size 
  and offset, and also pass in the module number. The next two cases are 
  virtually the same, with exception to the <TT>_LG_</TT> and <TT>_LL_</TT> 
  values passed in as the first parameter. In the last case, we emit a 
  <TT>GB</TT> instruction, which leaves an address on the stack to a parent 
  function's local variable area. The variable that we want is at an offset from 
  this address. The <TT>LSx</TT> instruction will take an offset as a parameter, 
  and retrieve the value stored at the computed address. 
  <P><B>Making an rvalue from a reference.</B> This is the case when a variable 
  has been passed into a function by reference: <PRE>      proc foo(var x: int);

      begin
        write x;
              ^
              |______ Get the value of x and put it on the EES
</PRE>This feat involves dereferencing a pointer. Since a reference can only 
  be found on the local stack, we don't have to worry about global variables or 
  external variables that are references. Only two cases apply. Instead of 
  storing a value, the variable stores a 32-bit pointer. We load the pointer 
  from wherever it is in memory onto the EES, and then tell the VM to load a 
  value at that address. It is a two-step process. 
  <P><PRE>      if ident-&gt;getByVal() = false then
        size:=   ident-&gt;size();
        offset:= ident-&gt;offset();
        level:=  ident-&gt;getFuncLevel();

        //*** Step 1: load the address ************************
        else if table.curFuncLevel() = level then           // Reference is local
          Emit( xLLD, offset );
        else                                                // Reference is in parent proc/func
          Emit( xGB, level );
          Emit( xLSD, offset );
        end if;

        //*** Step 2: load the value **************************
        EmitLoad( _LS_, size, 0 );
      end if;
</PRE>In step one, the first condition loads a dword at the offset from L. The 
  second condition computes a parent's base address, and then loads a dword at 
  an offset from that. In step two, the address that we now have on the EES is 
  used to fetch the actual value. 
  <P><B>Making an lvalue from a variable.</B> This is a simpler operation. All 
  we need is to load the address to the variable onto the EES. We will use one 
  of the four instructions, <TT>LGA</TT>, <TT>LLA</TT>, <TT>LEA</TT>, or 
  <TT>LSA</TT> for computing addresses. All we need to know is the area where 
  the variable is stored, and the offset from the starting point of the area. 
  Again, the location is going to be either the global data area, the local 
  stack, an external module, or in a parent scope. 
  <P><PRE>      if ident-&gt;getByVal() = true then
        offset:= ident-&gt;getOffset()
        level:=  ident-&gt;getFuncLevel();

        if ident-&gt;getMod() &lt;&gt; table.ModNum then       // Variable is external
          Emit( LEA, ident-&gt;getMod(), offset );
        else if level = 0 then                              // Variable is global
          Emit( xLGA, offset );
        else if table.curFuncLevel() = level then           // Variable is local
          Emit( xLLA, offset );
        else                                                // Variable is in parent proc/func
          Emit( xGB, level );
          Emit( xLSA, offset );
        end if;
      end if;
</PRE><B>Making an lvalue from a reference.</B> This is the simplest case, 
  since a reference and an lvalue are both a pointer. As always, references are 
  stored on the local stack or in a parent procedure's local stack frame. We use 
  the <TT>LLD</TT> or <TT>LSD</TT> instructions (Load Local Dword and Load Stack 
  Dword) to load the variables address from where it is stored, and we are done. 
<PRE>      if ident-&gt;getByVal() = false then
        offset:= ident-&gt;offset();
        level:=  ident-&gt;getFuncLevel();

        else if table.curFuncLevel() = level then           // Reference is local
          Emit( xLLD, offset );
        else                                                // Reference is in parent proc/func
          Emit( xGB, level );
          Emit( xLSD, offset );
        end if;
      end if;
</PRE>Notice the similarity between this and the code to load the value of a 
  reference. The only thing not done in this case is the call to emit an 
  additional <TT>LSx</TT> 
  <P></P></MENU>
<H4>6.4.2.3 A Short Example</H4>Let's bring this all together with a simple 
example. Here is a program: <PRE>      program VarAccess;

        var
          g: int;

        proc DoIt(var x: int);
          var
            i: int;
        begin
          //*** lvalues needed
          read g;     // Get an lvalue of a global
          read i;     // Get an lvalue of a local
          read x;     // Get an lvalue of a reference

          //*** rvalues needed
          write g;    // Get an rvalue of a global
          write i;    // Get an rvalue of a local
          write x;    // Get an rvalue of a reference
        end proc;

      begin
        DoIt(g);
      end program.
</PRE>This example is fairly simple, and does not test parent procedures or 
external variables. The real meat of this example is in procedure 
<TT>DoIt()</TT>. Three variables are visible from inside its scope, <TT>g</TT>, 
<TT>i</TT>, and a reference <TT>x</TT>. If we were to take a snapshot of memory 
at any time during <TT>DoIt()</TT>'s execution and diagram it out, it would look 
something like figure {VARMEM}. 
<P>
<MENU><IMG src="Chapter 6 Assignment and Expressions.files/VARMEM.gif"> 
  <P><FONT face=arial size=-1><B>Figure {VARMEM}.</B> A snapshot of memory as it 
  looks from procedure <TT>DoIt()</TT>. The variable <TT>g</TT> is at some 
  offset from register G, and variables <TT>i</TT> and <TT>x</TT> are at some 
  offset from register L.</FONT> </P></MENU>We can see how variable <TT>g</TT> is 
at some offset from register G, and variables <TT>i</TT> and <TT>x</TT> are at 
some offset from register L. Let's look at the first three lines of 
<TT>DoIt()</TT>. Generating an lvalue means that we need the address. We can add 
the offset of variable <TT>g</TT> to the G register to get the address of 
<TT>g</TT>. The <TT>LGA</TT> instruction is useful here. <PRE>      LGA offset(g)
</PRE>This instruction takes an immediate parameter that is added to the G 
register. The result is left on the EES. This gives us the address of 
<TT>g</TT>. We do a very similar thing with variable <TT>i</TT>. However, since 
it is local we use the <TT>LLA</TT> instruction, instead. <PRE>      LLA offset(i)
</PRE>The <TT>LLA</TT> instruction takes an immediate parameter that is added to 
the L register. Th

⌨️ 快捷键说明

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