{"id":237,"date":"2018-07-20T09:06:30","date_gmt":"2018-07-20T09:06:30","guid":{"rendered":"http:\/\/csp10.epgpbooks.inflibnet.ac.in\/?post_type=chapter&#038;p=237"},"modified":"2018-07-20T09:07:13","modified_gmt":"2018-07-20T09:07:13","slug":"switch-case-statements-and-run-time-storage-manage-ment","status":"publish","type":"chapter","link":"https:\/\/ebooks.inflibnet.ac.in\/csp10\/chapter\/switch-case-statements-and-run-time-storage-manage-ment\/","title":{"rendered":"Switch-case statements and Run-time storage manage ment"},"content":{"raw":"<div>\r\n<p style=\"text-align: justify\">In this module we will discuss the pending constructs in generating three-address code namely switch-case statements. We will also discuss the procedure involved in generating three-address code for expressions involving mixed operands. Run-time memory management is essential in knowing how the function calls are handled by the compiler and in this module we will discuss that as well. Finally, we will conclude this module with the various parameter passing techniques<\/p>\r\n&nbsp;\r\n\r\n<strong>27.1Mixed Operands<\/strong>\r\n\r\n&nbsp;\r\n<p style=\"text-align: justify\">Boolean expressions can have arithmetic sub-expressions. A Boolean can be considered as arithmetic in languages where true is \u201c1\u2019 and false is \u201c0\u201d. We could incorporate short-circuit information to handle this as well. Consider the following grammar:<\/p>\r\n&nbsp;\r\n\r\n\u2022\u00a0\u00a0\u00a0\u00a0\u00a0 E \u00e0 E+E | E and E | E relop E | id\r\n\r\n&nbsp;\r\n<p style=\"text-align: justify\">In this scenario, the variable E could refer to an arithmetic expression or could be a boolean. If one of the \u2018E\u2019 in E \u00e0 E+E is arithmetic and other is Boolean, how can the compiler allow computation of addition of the two expressions is the cause of concern. The semantic rules should handle this situation and generate appropriate three-address code. Another example is the expression \u201cE+E\u201d could produce arithmetic result and could be \u201cand\u201d with another boolean expression\u2019s result.<\/p>\r\n&nbsp;\r\n<p style=\"text-align: justify\">The following is the set of semantic rules which are added along with the other semantic rules which are part of the arithmetic expression grammar for the production E \u00e0E1 + E2. As the LHS variable is to store an arithmetic result, we assign the type of this variable E as arithmetic. We use two types of values for the variable E. If the value of the expression is \u20180\u2019 or \u20181\u2019 it is said to be of type \u201cbool\u201d meaning Boolean, else it is said to be of type \u201carith\u201d meaning arithmetic.<\/p>\r\n&nbsp;\r\n\r\n&nbsp;\r\n\r\nE.Type := arith;\r\n\r\nif E1.type = arith and E2.type = arith\r\n\r\nE.place :=\u00a0 newtemp\r\n\r\nE.code := E1.code || E2.code ||\r\n\r\ngen(E.place \u2018:=\u2018 E1.place \u2018+\u2019 E2.place)\r\n\r\nelse if E1.type = arith and E2.type = bool\r\n\r\nE.place := newtemp\r\n\r\nE2.true := newlabel\r\n\r\nE2.false := newlabel\r\n\r\nE.code := E1.code || E2.code ||\r\n\r\ngen(E2.true \u2018:\u2019 E.place \u2018:=\u2018 E1.place +1)\r\n\r\ngen(\u2018goto\u2019 nextstat +1)\r\n\r\n<\/div>\r\n<div>\r\n\r\ngen(E2.false \u2018:\u2019 E.place \u2018:=\u2018 E1.place)\r\n\r\nelse if E2.type = arith and E1.type = bool\r\n\r\nE.place := newtemp\r\n\r\nE1.true := newlabel\r\n\r\nE1.false := newlabel\r\n\r\nE.code := E1.code || E2.code ||\r\n\r\ngen(E1.true \u2018:\u2019 E.place \u2018:=\u2018 E2.place +1)\r\n\r\ngen(\u2018goto\u2019 nextstat +1)\r\n\r\ngen(E1.false \u2018:\u2019 E.place \u2018:=\u2018 E2.place)\r\n\r\n&nbsp;\r\n\r\nelse \u2026\r\n\r\n&nbsp;\r\n\r\n&nbsp;\r\n<p style=\"text-align: justify\">The logic behind the semantic rules is that if the expressions are arithmetic, we proceed in a similar manner as the expression grammar. If the expressions are mixed, then we increment or decrement depending on one of the expressions being true or false. In the first set, we verify if both the expressions are of type arithmetic and simply proceed like arithmetic expression grammar. In the second and third cases, we will check which expression is Boolean and assign two labels as true and false for this Boolean expression. At the corresponding label we compute the LHS variable\u2019s value as the RHS\u2019s arithmetic variable\u2019s value +1 or arithmetic variable alone depending on whether the Boolean RHS variable is true or false respectively. The \u201cgoto\u201d in between the two \u201cgen\u201d ensures that we don\u2019t compute both and skip the computation of \u201cfalse\u201d label.<\/p>\r\n&nbsp;\r\n\r\n<strong>27.2 Switch-case statements<\/strong>\r\n\r\n&nbsp;\r\n<p style=\"text-align: justify\">A switch-case statement is a shorter representation of nested if-else statements. The switch case statement\u2019s general construct is given below. There is an expression which is evaluated and the various case statements are the different values that the expression would get. After every case statement, there is a break which will come out of the switch \u2013 case construct. The presence of a default statement is optional.<\/p>\r\n&nbsp;\r\n\r\nSwitch expression\r\n\r\nbegin\r\n\r\ncase value: statement\r\n\r\ncase value: statement\r\n\r\n\u2026\r\n\r\ndefault: statement\r\n\r\nend\r\n\r\n&nbsp;\r\n\r\nThe semantic rules necessary to generate three-address code for the switch-case statement involves the following steps:\r\n\r\n&nbsp;\r\n\r\n\u2022\u00a0\u00a0\u00a0\u00a0\u00a0 Evaluate the expression\r\n\r\n\u2022\u00a0\u00a0\u00a0\u00a0\u00a0 Find which value in the list of cases is the same as the value of the expression\r\n\r\n<\/div>\r\n<div>\r\n<p style=\"text-align: justify\">\u2022\u00a0\u00a0\u00a0\u00a0\u00a0 Execute the statement associated with the value found and come out of the switch-case construct The following is the translation scheme of the switch case which by itself represent the three-address code.<\/p>\r\n\u2022\u00a0\u00a0\u00a0\u00a0\u00a0 Code to evaluate E into t\r\n\r\n\u2022\u00a0\u00a0\u00a0\u00a0\u00a0 goto test\r\n\r\n\u2022\u00a0\u00a0\u00a0\u00a0\u00a0 L1:code for S1\u00a0 goto next\r\n\r\n\u2022\u00a0\u00a0\u00a0\u00a0\u00a0 L2:code for S2\r\n\r\n&nbsp;\r\n\r\ngoto next\r\n\r\n\u2026\r\n\r\n&nbsp;\r\n\r\n\u2022\u00a0\u00a0\u00a0\u00a0\u00a0 Ln:code for Sn goto next\r\n\r\n\u2022\u00a0\u00a0\u00a0\u00a0\u00a0 test:\u00a0\u00a0\u00a0 if t = V1 goto L1\r\n\r\n&nbsp;\r\n\r\nif t = V2 goto L2\r\n\r\n\u2026\r\n\r\n&nbsp;\r\n\r\nif t = Vn-1 goto Ln goto Ln\r\n\r\n\u2022\u00a0\u00a0\u00a0\u00a0\u00a0 next:\r\n\r\n&nbsp;\r\n\r\nIn the above sequence, the first step is to compute the expression E and store it in a temporary variable\u2018t\u2019. We generate a \u2018goto\u2019 to compare the value of the variable with known cases of the switch case. The various cases of the switch-case is available in variable V1, V2, .. and the default case does not have a value V. The block of \u201ctest\u201d compares and generates a goto to the appropriate body of the case statements. If the value does not match with any of the Vi, then this case corresponds to the default case and that is handled by the goto Ln statement. After every case body S1, S2,..Sn, there is a \u201cgoto next\u201d to come out of the switch-case construct.\r\n\r\n&nbsp;\r\n\r\n<strong>27.3 Run-time Memory management<\/strong>\r\n\r\n&nbsp;\r\n\r\nTo compile and execute the program, we need to use memory to store:\r\n\r\n&nbsp;\r\n\r\n\u2022\u00a0\u00a0\u00a0\u00a0\u00a0 Code\r\n\r\n\u2022\u00a0\u00a0\u00a0\u00a0\u00a0 Static data (global variables)\r\n<p style=\"text-align: justify\">\u2022\u00a0\u00a0\u00a0\u00a0\u00a0 Dynamic data objects - data that are used when executing a certain procedure as well as dynamically allocated objects where user requests memory using malloc, new, etc., depending on the programming language.<\/p>\r\n&nbsp;\r\n<p style=\"text-align: justify\">Figure 27.1 shows the memory layout where the code, static data are stored. Stack data occupies the top portion while heap memory occup ies from bottom to top. Static allocation is typically known during compile time and it is typically occupied by static variables and<\/p>\r\n\r\n<\/div>\r\n&nbsp;\r\n<p style=\"text-align: justify\">global variables of a function. Stack allocation is the result of memory required during function calls and Heap allocation results during dynamic memory allocation.<\/p>\r\n<img class=\"size-full wp-image-238 aligncenter\" src=\"http:\/\/csp10.epgpbooks.inflibnet.ac.in\/wp-content\/uploads\/sites\/50\/2018\/07\/Untitled-139.png\" alt=\"\" width=\"648\" height=\"802\" \/>\r\n\r\n<img class=\"size-full wp-image-239 aligncenter\" src=\"http:\/\/csp10.epgpbooks.inflibnet.ac.in\/wp-content\/uploads\/sites\/50\/2018\/07\/Untitled-140.png\" alt=\"\" width=\"624\" height=\"89\" \/>\r\n<div>\r\n\r\n&nbsp;\r\n<p style=\"text-align: justify\">Static allocation lays out storage for all data objects at compile time. The advantage of this approach is memory management becomes simple. However, the constraints of this approach of memory allocation is ,<\/p>\r\n&nbsp;\r\n\r\n\u2022\u00a0\u00a0\u00a0\u00a0\u00a0 size of object must be known and alignment requirements must be known at compile time.\r\n\r\n\u2022\u00a0\u00a0\u00a0\u00a0\u00a0 Recursion is not supported\r\n\r\n\u2022\u00a0\u00a0\u00a0\u00a0\u00a0 Dynamic data structure is not supported\r\n\r\n&nbsp;\r\n\r\nA balance of the pros and cons of this approach need to be managed and we need some static allocation as part of the program.\r\n\r\n&nbsp;\r\n\r\n<strong>27.3.3 Stack allocation<\/strong>\r\n\r\n&nbsp;\r\n<p style=\"text-align: justify\">Stack allocation manages the run time storage as a stack. Stack allocation is managed by creating a new activation record for every function call. When a function call happens, t he activation record is pushed on when a function is entered. The same activation record is popped off as the function exits. The constraints of this approach are<\/p>\r\n&nbsp;\r\n\r\n\u00b7\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Values of locals cannot be retained when activation ends. So local variables need to be properly returned to the caller.\r\n\r\n\u00b7\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 A called activation cannot outlive a caller.\r\n\r\n&nbsp;\r\n<p style=\"text-align: justify\">The constraints of the stack memory are unavoidable as we modularize our programs and hence there will be function calls. We need to take care of handling the constraints to achieve correct result.<\/p>\r\n&nbsp;\r\n<p style=\"text-align: justify\">The stack memory management is done by the compiler. The procedure is implemented by defining a function calling sequence and a matching return sequence. A calling sequence allocates an activation record and enters information into its fields (push the activation record). On the opposite of the calling sequence is the return sequence. Return sequence restores the state of the machine so that the calling procedure can continue execution.\u00a0<span style=\"font-size: 1em;text-align: initial\">A Calling Sequence typically does the following sequence of actions after creating an activation record.<\/span><\/p>\r\n\r\n<\/div>\r\n<div>\r\n\r\n&nbsp;\r\n\r\n\u2022\u00a0\u00a0\u00a0\u00a0\u00a0 The caller evaluates actual parameters and push these actual parameters on the stack\r\n\r\n\u2022\u00a0\u00a0\u00a0\u00a0\u00a0 The caller saves return address(program counter) and the old value of stack pointer (sp) into the stack\r\n\r\n\u2022\u00a0\u00a0\u00a0\u00a0\u00a0 The caller increments the sp\r\n\r\n\u2022\u00a0\u00a0\u00a0\u00a0\u00a0 The callee saves registers and other status information\r\n<p style=\"text-align: justify\">\u2022\u00a0\u00a0\u00a0\u00a0\u00a0 The callee initializes local variables and begins execution. After executing a function the called function returns control to the caller and this is defined using the return sequence and the following are the typical sequence of actions:<\/p>\r\n\u2022\u00a0\u00a0\u00a0\u00a0\u00a0 The callee places a return value next to the activation record of the caller.\r\n\r\n\u2022\u00a0\u00a0\u00a0\u00a0\u00a0 The callee restores other registers and sp and return (jump to pc).\r\n\r\n\u2022\u00a0\u00a0\u00a0\u00a0\u00a0 The caller copies the return value to its activation record.\r\n\r\n&nbsp;\r\n\r\n<strong>27.3.4 Heap allocation<\/strong>\r\n\r\n&nbsp;\r\n<p style=\"text-align: justify\">Memory is allocated and freed dynamically as needed at runtime from a data area called as heap. The memory architecture of figure 27.1 indicates that this memory grows from bottom to top. Heap allocation is essential as we cannot predetermine all memory requirements and have then as static variable. The requirements for heap memory are that the procedures to be activated in the LIFO manner and the compiler should support true dynamic memory management.<\/p>\r\n&nbsp;\r\n\r\n<strong>27.3.5 Access to non-local variables<\/strong>\r\n\r\n&nbsp;\r\n<p style=\"text-align: justify\">Nonlocal variables in programming languages like C (without nested procedures) would still have nested scopes (blocks). The problem is whether to consider them as static, stack or heap variable? The simple proposed solution is as follows:<\/p>\r\n&nbsp;\r\n\r\n\u2022\u00a0\u00a0\u00a0\u00a0\u00a0 All data declared outside procedures are static.\r\n\r\n\u2022\u00a0\u00a0\u00a0\u00a0\u00a0 Other names must be in the activation record at the top of the stack, can be accessed from sp.\r\n\r\n&nbsp;\r\n\r\n\u2013\u00a0 Treat a block as a parameter- less procedure\r\n\r\n\u2013\u00a0 Allocates space for all blocks in a procedure.\r\n\r\n<\/div>\r\n<div>\r\n<p style=\"text-align: justify\">\u2022\u00a0\u00a0\u00a0\u00a0\u00a0 If p is nested immediately within q in the source text, then the access link in an activation record for p points to the access link in the record for the most recent activation of q.<\/p>\r\n<p style=\"text-align: justify\">\u2022\u00a0\u00a0\u00a0\u00a0\u00a0 A procedure p at nesting depth n_p accesses a nonlocal \u2018a\u2019 at nesting depth n_a: (1) following n_p \u2013 n_a links and (2) using the relative offset in the activation record.<\/p>\r\n&nbsp;\r\n\r\n<strong>27.4<\/strong>\u00a0\u00a0\u00a0 <strong>Parameter Passing<\/strong>\r\n\r\n&nbsp;\r\n<p style=\"text-align: justify\">Parameter passing is the method to associate actual parameters with formal parameters. The parameter passing method will affect the code generated. The four major types of parameter passing techniques are Call by value, Call by reference, Call by copy-restore, and Call by name. Let us discuss each one of them in detail in the following sub-sections<\/p>\r\n&nbsp;\r\n\r\n<strong>27.4.1 Call by value<\/strong>\r\n\r\n&nbsp;\r\n<p style=\"text-align: justify\">In this method of parameter passing, the actual parameters are evaluated and their r-values are passed to the called procedure. A formal parameter is treated like a local name, so the storage for the formals is in the activation record of the called procedure. The caller evaluates the actual parameters and places their r-values in the storage for the formals. Consider the following example that has a function swap( ) and the main ( ) function in the C programming language. The function swap, tries to exchange the values in the variables \u2018a\u2019 and \u2018b\u2019.<\/p>\r\n&nbsp;\r\n\r\nSwap(int a, int b)\r\n\r\n&nbsp;\r\n\r\n{int temp;\r\n\r\n&nbsp;\r\n\r\ntemp = a; a = b; b = temp;\r\n\r\n&nbsp;\r\n\r\n}\r\n\r\n&nbsp;\r\n\r\nVoid main()\r\n\r\n&nbsp;\r\n\r\n{int a = 1, b = 2;\r\n\r\n&nbsp;\r\n\r\nSwap(a, b); printf(\u201c%d \\t %d\u201d, a, b);\r\n\r\n&nbsp;\r\n\r\n}\r\n\r\n&nbsp;\r\n<p style=\"text-align: justify\">When this function is called from main() the actual arguments \u201c1\u201d and \u201c2\u201d are passed as values in the activation record. The function swap() considers these values and swaps them in the activation record. Once, the swap function returns, the activation record is removed and thus the swapping of the variables does not take place. This is the drawback of call by value. This is restored in the Call by reference method of parameter passing.<\/p>\r\n\r\n<\/div>\r\n&nbsp;\r\n<div>\r\n\r\n<strong>27.4.2 Call by Reference<\/strong>\r\n\r\n&nbsp;\r\n<p style=\"text-align: justify\">This method of parameter passing is also referred to as call-by address or call-by- location. The caller passes to the called procedure a pointer to the storage address of each actual parameter. Actual parameter must have an address and hence only variables make sense while expressions do not make much sense. Thus the location of the temporary variable that holds the result of the expression will be passed as parameter.<\/p>\r\n&nbsp;\r\n<p style=\"text-align: justify\">Consider the same example as discussed in Call by value. But, here we pass reference, that is the address of the parameters and hence in the activation record, address of the variables gets passed. As the swap function tries to change the values of the variable in the addresses that has been passed, call by reference ensures swap function is computed correctly. Call by reference is implemented by C++, Java etc.,<\/p>\r\n&nbsp;\r\n\r\nSwap(int * a, int * b)\r\n\r\n&nbsp;\r\n\r\n{int temp;\r\n\r\n&nbsp;\r\n\r\ntemp = *a; *a = *b; *b = temp;\r\n\r\n&nbsp;\r\n\r\n}\r\n\r\n&nbsp;\r\n\r\nVoid main()\r\n\r\n&nbsp;\r\n\r\n{int a = 1, b = 2;\r\n\r\n&nbsp;\r\n\r\nSwap(&amp;a, &amp;b); printf(\u201c%d \\t %d\u201d, a, b);\r\n\r\n&nbsp;\r\n\r\n}\r\n\r\n&nbsp;\r\n\r\n<strong>27.4.3 Call by Copy-Restore<\/strong>\r\n\r\n&nbsp;\r\n<p style=\"text-align: justify\">This technique is a hybrid between call-by- value and call-by-reference. The actual parameters are evaluated and its r-values are passed to the called procedure as in call-by- value. When the control returns, the r-value of the formal parameters are copied back into the l- value of the actuals.<\/p>\r\n&nbsp;\r\n<p style=\"text-align: justify\">Considering the same example of swap function, if the function is called as \u201cswap(i,a[i])\u201d it works correctly using copy-restore. Location of a[i] is computed and preserved by the calling program before initiating the call. This parameter passing technique is used by Fortran.<\/p>\r\n&nbsp;\r\n\r\n<strong>27.4.4 Call by name<\/strong>\r\n\r\n&nbsp;\r\n<p style=\"text-align: justify\">This technique of parameter passing is defined by the copy-rule of Algol. Every procedure is considered as if it were a macro. The actual parameters are literally substituted with the formal as\u00a0\u00a0<span style=\"text-align: initial;font-size: 1em\">a macro-expansion. Local names of called procedures are kept distinct and it may be renamed. Actual parameters are surrounded by parentheses to preserve integrity\u00a0The following are permitted and yields correct results in <\/span>call<span style=\"text-align: initial;font-size: 1em\"> by name technique:<\/span><\/p>\r\n\r\n<\/div>\r\n<ul>\r\n \t<li>x : = f(A) + f(B)<\/li>\r\n \t<li>Here A , B are expressions<\/li>\r\n \t<li style=\"text-align: justify\">Substitution of expressions A and B in the formal parameter leads to call by name parameter passing technique being implemented.<\/li>\r\n<\/ul>\r\n&nbsp;\r\n<p style=\"text-align: justify\"><strong>Summary: <\/strong>In this module, control flow statements involving switch \u2013 case are studied. We also discussed run-time storage management in the context of Static, Stack, and Heap memory allocation. We also discussed the various parameter passing techniques.<\/p>\r\n&nbsp;","rendered":"<div>\n<p style=\"text-align: justify\">In this module we will discuss the pending constructs in generating three-address code namely switch-case statements. We will also discuss the procedure involved in generating three-address code for expressions involving mixed operands. Run-time memory management is essential in knowing how the function calls are handled by the compiler and in this module we will discuss that as well. Finally, we will conclude this module with the various parameter passing techniques<\/p>\n<p>&nbsp;<\/p>\n<p><strong>27.1Mixed Operands<\/strong><\/p>\n<p>&nbsp;<\/p>\n<p style=\"text-align: justify\">Boolean expressions can have arithmetic sub-expressions. A Boolean can be considered as arithmetic in languages where true is \u201c1\u2019 and false is \u201c0\u201d. We could incorporate short-circuit information to handle this as well. Consider the following grammar:<\/p>\n<p>&nbsp;<\/p>\n<p>\u2022\u00a0\u00a0\u00a0\u00a0\u00a0 E \u00e0 E+E | E and E | E relop E | id<\/p>\n<p>&nbsp;<\/p>\n<p style=\"text-align: justify\">In this scenario, the variable E could refer to an arithmetic expression or could be a boolean. If one of the \u2018E\u2019 in E \u00e0 E+E is arithmetic and other is Boolean, how can the compiler allow computation of addition of the two expressions is the cause of concern. The semantic rules should handle this situation and generate appropriate three-address code. Another example is the expression \u201cE+E\u201d could produce arithmetic result and could be \u201cand\u201d with another boolean expression\u2019s result.<\/p>\n<p>&nbsp;<\/p>\n<p style=\"text-align: justify\">The following is the set of semantic rules which are added along with the other semantic rules which are part of the arithmetic expression grammar for the production E \u00e0E1 + E2. As the LHS variable is to store an arithmetic result, we assign the type of this variable E as arithmetic. We use two types of values for the variable E. If the value of the expression is \u20180\u2019 or \u20181\u2019 it is said to be of type \u201cbool\u201d meaning Boolean, else it is said to be of type \u201carith\u201d meaning arithmetic.<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>E.Type := arith;<\/p>\n<p>if E1.type = arith and E2.type = arith<\/p>\n<p>E.place :=\u00a0 newtemp<\/p>\n<p>E.code := E1.code || E2.code ||<\/p>\n<p>gen(E.place \u2018:=\u2018 E1.place \u2018+\u2019 E2.place)<\/p>\n<p>else if E1.type = arith and E2.type = bool<\/p>\n<p>E.place := newtemp<\/p>\n<p>E2.true := newlabel<\/p>\n<p>E2.false := newlabel<\/p>\n<p>E.code := E1.code || E2.code ||<\/p>\n<p>gen(E2.true \u2018:\u2019 E.place \u2018:=\u2018 E1.place +1)<\/p>\n<p>gen(\u2018goto\u2019 nextstat +1)<\/p>\n<\/div>\n<div>\n<p>gen(E2.false \u2018:\u2019 E.place \u2018:=\u2018 E1.place)<\/p>\n<p>else if E2.type = arith and E1.type = bool<\/p>\n<p>E.place := newtemp<\/p>\n<p>E1.true := newlabel<\/p>\n<p>E1.false := newlabel<\/p>\n<p>E.code := E1.code || E2.code ||<\/p>\n<p>gen(E1.true \u2018:\u2019 E.place \u2018:=\u2018 E2.place +1)<\/p>\n<p>gen(\u2018goto\u2019 nextstat +1)<\/p>\n<p>gen(E1.false \u2018:\u2019 E.place \u2018:=\u2018 E2.place)<\/p>\n<p>&nbsp;<\/p>\n<p>else \u2026<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p style=\"text-align: justify\">The logic behind the semantic rules is that if the expressions are arithmetic, we proceed in a similar manner as the expression grammar. If the expressions are mixed, then we increment or decrement depending on one of the expressions being true or false. In the first set, we verify if both the expressions are of type arithmetic and simply proceed like arithmetic expression grammar. In the second and third cases, we will check which expression is Boolean and assign two labels as true and false for this Boolean expression. At the corresponding label we compute the LHS variable\u2019s value as the RHS\u2019s arithmetic variable\u2019s value +1 or arithmetic variable alone depending on whether the Boolean RHS variable is true or false respectively. The \u201cgoto\u201d in between the two \u201cgen\u201d ensures that we don\u2019t compute both and skip the computation of \u201cfalse\u201d label.<\/p>\n<p>&nbsp;<\/p>\n<p><strong>27.2 Switch-case statements<\/strong><\/p>\n<p>&nbsp;<\/p>\n<p style=\"text-align: justify\">A switch-case statement is a shorter representation of nested if-else statements. The switch case statement\u2019s general construct is given below. There is an expression which is evaluated and the various case statements are the different values that the expression would get. After every case statement, there is a break which will come out of the switch \u2013 case construct. The presence of a default statement is optional.<\/p>\n<p>&nbsp;<\/p>\n<p>Switch expression<\/p>\n<p>begin<\/p>\n<p>case value: statement<\/p>\n<p>case value: statement<\/p>\n<p>\u2026<\/p>\n<p>default: statement<\/p>\n<p>end<\/p>\n<p>&nbsp;<\/p>\n<p>The semantic rules necessary to generate three-address code for the switch-case statement involves the following steps:<\/p>\n<p>&nbsp;<\/p>\n<p>\u2022\u00a0\u00a0\u00a0\u00a0\u00a0 Evaluate the expression<\/p>\n<p>\u2022\u00a0\u00a0\u00a0\u00a0\u00a0 Find which value in the list of cases is the same as the value of the expression<\/p>\n<\/div>\n<div>\n<p style=\"text-align: justify\">\u2022\u00a0\u00a0\u00a0\u00a0\u00a0 Execute the statement associated with the value found and come out of the switch-case construct The following is the translation scheme of the switch case which by itself represent the three-address code.<\/p>\n<p>\u2022\u00a0\u00a0\u00a0\u00a0\u00a0 Code to evaluate E into t<\/p>\n<p>\u2022\u00a0\u00a0\u00a0\u00a0\u00a0 goto test<\/p>\n<p>\u2022\u00a0\u00a0\u00a0\u00a0\u00a0 L1:code for S1\u00a0 goto next<\/p>\n<p>\u2022\u00a0\u00a0\u00a0\u00a0\u00a0 L2:code for S2<\/p>\n<p>&nbsp;<\/p>\n<p>goto next<\/p>\n<p>\u2026<\/p>\n<p>&nbsp;<\/p>\n<p>\u2022\u00a0\u00a0\u00a0\u00a0\u00a0 Ln:code for Sn goto next<\/p>\n<p>\u2022\u00a0\u00a0\u00a0\u00a0\u00a0 test:\u00a0\u00a0\u00a0 if t = V1 goto L1<\/p>\n<p>&nbsp;<\/p>\n<p>if t = V2 goto L2<\/p>\n<p>\u2026<\/p>\n<p>&nbsp;<\/p>\n<p>if t = Vn-1 goto Ln goto Ln<\/p>\n<p>\u2022\u00a0\u00a0\u00a0\u00a0\u00a0 next:<\/p>\n<p>&nbsp;<\/p>\n<p>In the above sequence, the first step is to compute the expression E and store it in a temporary variable\u2018t\u2019. We generate a \u2018goto\u2019 to compare the value of the variable with known cases of the switch case. The various cases of the switch-case is available in variable V1, V2, .. and the default case does not have a value V. The block of \u201ctest\u201d compares and generates a goto to the appropriate body of the case statements. If the value does not match with any of the Vi, then this case corresponds to the default case and that is handled by the goto Ln statement. After every case body S1, S2,..Sn, there is a \u201cgoto next\u201d to come out of the switch-case construct.<\/p>\n<p>&nbsp;<\/p>\n<p><strong>27.3 Run-time Memory management<\/strong><\/p>\n<p>&nbsp;<\/p>\n<p>To compile and execute the program, we need to use memory to store:<\/p>\n<p>&nbsp;<\/p>\n<p>\u2022\u00a0\u00a0\u00a0\u00a0\u00a0 Code<\/p>\n<p>\u2022\u00a0\u00a0\u00a0\u00a0\u00a0 Static data (global variables)<\/p>\n<p style=\"text-align: justify\">\u2022\u00a0\u00a0\u00a0\u00a0\u00a0 Dynamic data objects &#8211; data that are used when executing a certain procedure as well as dynamically allocated objects where user requests memory using malloc, new, etc., depending on the programming language.<\/p>\n<p>&nbsp;<\/p>\n<p style=\"text-align: justify\">Figure 27.1 shows the memory layout where the code, static data are stored. Stack data occupies the top portion while heap memory occup ies from bottom to top. Static allocation is typically known during compile time and it is typically occupied by static variables and<\/p>\n<\/div>\n<p>&nbsp;<\/p>\n<p style=\"text-align: justify\">global variables of a function. Stack allocation is the result of memory required during function calls and Heap allocation results during dynamic memory allocation.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-238 aligncenter\" src=\"http:\/\/csp10.epgpbooks.inflibnet.ac.in\/wp-content\/uploads\/sites\/50\/2018\/07\/Untitled-139.png\" alt=\"\" width=\"648\" height=\"802\" srcset=\"https:\/\/ebooks.inflibnet.ac.in\/csp10\/wp-content\/uploads\/sites\/50\/2018\/07\/Untitled-139.png 648w, https:\/\/ebooks.inflibnet.ac.in\/csp10\/wp-content\/uploads\/sites\/50\/2018\/07\/Untitled-139-242x300.png 242w, https:\/\/ebooks.inflibnet.ac.in\/csp10\/wp-content\/uploads\/sites\/50\/2018\/07\/Untitled-139-65x80.png 65w, https:\/\/ebooks.inflibnet.ac.in\/csp10\/wp-content\/uploads\/sites\/50\/2018\/07\/Untitled-139-225x278.png 225w, https:\/\/ebooks.inflibnet.ac.in\/csp10\/wp-content\/uploads\/sites\/50\/2018\/07\/Untitled-139-350x433.png 350w\" sizes=\"auto, (max-width: 648px) 100vw, 648px\" \/><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-239 aligncenter\" src=\"http:\/\/csp10.epgpbooks.inflibnet.ac.in\/wp-content\/uploads\/sites\/50\/2018\/07\/Untitled-140.png\" alt=\"\" width=\"624\" height=\"89\" srcset=\"https:\/\/ebooks.inflibnet.ac.in\/csp10\/wp-content\/uploads\/sites\/50\/2018\/07\/Untitled-140.png 624w, https:\/\/ebooks.inflibnet.ac.in\/csp10\/wp-content\/uploads\/sites\/50\/2018\/07\/Untitled-140-300x43.png 300w, https:\/\/ebooks.inflibnet.ac.in\/csp10\/wp-content\/uploads\/sites\/50\/2018\/07\/Untitled-140-65x9.png 65w, https:\/\/ebooks.inflibnet.ac.in\/csp10\/wp-content\/uploads\/sites\/50\/2018\/07\/Untitled-140-225x32.png 225w, https:\/\/ebooks.inflibnet.ac.in\/csp10\/wp-content\/uploads\/sites\/50\/2018\/07\/Untitled-140-350x50.png 350w\" sizes=\"auto, (max-width: 624px) 100vw, 624px\" \/><\/p>\n<div>\n<p>&nbsp;<\/p>\n<p style=\"text-align: justify\">Static allocation lays out storage for all data objects at compile time. The advantage of this approach is memory management becomes simple. However, the constraints of this approach of memory allocation is ,<\/p>\n<p>&nbsp;<\/p>\n<p>\u2022\u00a0\u00a0\u00a0\u00a0\u00a0 size of object must be known and alignment requirements must be known at compile time.<\/p>\n<p>\u2022\u00a0\u00a0\u00a0\u00a0\u00a0 Recursion is not supported<\/p>\n<p>\u2022\u00a0\u00a0\u00a0\u00a0\u00a0 Dynamic data structure is not supported<\/p>\n<p>&nbsp;<\/p>\n<p>A balance of the pros and cons of this approach need to be managed and we need some static allocation as part of the program.<\/p>\n<p>&nbsp;<\/p>\n<p><strong>27.3.3 Stack allocation<\/strong><\/p>\n<p>&nbsp;<\/p>\n<p style=\"text-align: justify\">Stack allocation manages the run time storage as a stack. Stack allocation is managed by creating a new activation record for every function call. When a function call happens, t he activation record is pushed on when a function is entered. The same activation record is popped off as the function exits. The constraints of this approach are<\/p>\n<p>&nbsp;<\/p>\n<p>\u00b7\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Values of locals cannot be retained when activation ends. So local variables need to be properly returned to the caller.<\/p>\n<p>\u00b7\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 A called activation cannot outlive a caller.<\/p>\n<p>&nbsp;<\/p>\n<p style=\"text-align: justify\">The constraints of the stack memory are unavoidable as we modularize our programs and hence there will be function calls. We need to take care of handling the constraints to achieve correct result.<\/p>\n<p>&nbsp;<\/p>\n<p style=\"text-align: justify\">The stack memory management is done by the compiler. The procedure is implemented by defining a function calling sequence and a matching return sequence. A calling sequence allocates an activation record and enters information into its fields (push the activation record). On the opposite of the calling sequence is the return sequence. Return sequence restores the state of the machine so that the calling procedure can continue execution.\u00a0<span style=\"font-size: 1em;text-align: initial\">A Calling Sequence typically does the following sequence of actions after creating an activation record.<\/span><\/p>\n<\/div>\n<div>\n<p>&nbsp;<\/p>\n<p>\u2022\u00a0\u00a0\u00a0\u00a0\u00a0 The caller evaluates actual parameters and push these actual parameters on the stack<\/p>\n<p>\u2022\u00a0\u00a0\u00a0\u00a0\u00a0 The caller saves return address(program counter) and the old value of stack pointer (sp) into the stack<\/p>\n<p>\u2022\u00a0\u00a0\u00a0\u00a0\u00a0 The caller increments the sp<\/p>\n<p>\u2022\u00a0\u00a0\u00a0\u00a0\u00a0 The callee saves registers and other status information<\/p>\n<p style=\"text-align: justify\">\u2022\u00a0\u00a0\u00a0\u00a0\u00a0 The callee initializes local variables and begins execution. After executing a function the called function returns control to the caller and this is defined using the return sequence and the following are the typical sequence of actions:<\/p>\n<p>\u2022\u00a0\u00a0\u00a0\u00a0\u00a0 The callee places a return value next to the activation record of the caller.<\/p>\n<p>\u2022\u00a0\u00a0\u00a0\u00a0\u00a0 The callee restores other registers and sp and return (jump to pc).<\/p>\n<p>\u2022\u00a0\u00a0\u00a0\u00a0\u00a0 The caller copies the return value to its activation record.<\/p>\n<p>&nbsp;<\/p>\n<p><strong>27.3.4 Heap allocation<\/strong><\/p>\n<p>&nbsp;<\/p>\n<p style=\"text-align: justify\">Memory is allocated and freed dynamically as needed at runtime from a data area called as heap. The memory architecture of figure 27.1 indicates that this memory grows from bottom to top. Heap allocation is essential as we cannot predetermine all memory requirements and have then as static variable. The requirements for heap memory are that the procedures to be activated in the LIFO manner and the compiler should support true dynamic memory management.<\/p>\n<p>&nbsp;<\/p>\n<p><strong>27.3.5 Access to non-local variables<\/strong><\/p>\n<p>&nbsp;<\/p>\n<p style=\"text-align: justify\">Nonlocal variables in programming languages like C (without nested procedures) would still have nested scopes (blocks). The problem is whether to consider them as static, stack or heap variable? The simple proposed solution is as follows:<\/p>\n<p>&nbsp;<\/p>\n<p>\u2022\u00a0\u00a0\u00a0\u00a0\u00a0 All data declared outside procedures are static.<\/p>\n<p>\u2022\u00a0\u00a0\u00a0\u00a0\u00a0 Other names must be in the activation record at the top of the stack, can be accessed from sp.<\/p>\n<p>&nbsp;<\/p>\n<p>\u2013\u00a0 Treat a block as a parameter- less procedure<\/p>\n<p>\u2013\u00a0 Allocates space for all blocks in a procedure.<\/p>\n<\/div>\n<div>\n<p style=\"text-align: justify\">\u2022\u00a0\u00a0\u00a0\u00a0\u00a0 If p is nested immediately within q in the source text, then the access link in an activation record for p points to the access link in the record for the most recent activation of q.<\/p>\n<p style=\"text-align: justify\">\u2022\u00a0\u00a0\u00a0\u00a0\u00a0 A procedure p at nesting depth n_p accesses a nonlocal \u2018a\u2019 at nesting depth n_a: (1) following n_p \u2013 n_a links and (2) using the relative offset in the activation record.<\/p>\n<p>&nbsp;<\/p>\n<p><strong>27.4<\/strong>\u00a0\u00a0\u00a0 <strong>Parameter Passing<\/strong><\/p>\n<p>&nbsp;<\/p>\n<p style=\"text-align: justify\">Parameter passing is the method to associate actual parameters with formal parameters. The parameter passing method will affect the code generated. The four major types of parameter passing techniques are Call by value, Call by reference, Call by copy-restore, and Call by name. Let us discuss each one of them in detail in the following sub-sections<\/p>\n<p>&nbsp;<\/p>\n<p><strong>27.4.1 Call by value<\/strong><\/p>\n<p>&nbsp;<\/p>\n<p style=\"text-align: justify\">In this method of parameter passing, the actual parameters are evaluated and their r-values are passed to the called procedure. A formal parameter is treated like a local name, so the storage for the formals is in the activation record of the called procedure. The caller evaluates the actual parameters and places their r-values in the storage for the formals. Consider the following example that has a function swap( ) and the main ( ) function in the C programming language. The function swap, tries to exchange the values in the variables \u2018a\u2019 and \u2018b\u2019.<\/p>\n<p>&nbsp;<\/p>\n<p>Swap(int a, int b)<\/p>\n<p>&nbsp;<\/p>\n<p>{int temp;<\/p>\n<p>&nbsp;<\/p>\n<p>temp = a; a = b; b = temp;<\/p>\n<p>&nbsp;<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>Void main()<\/p>\n<p>&nbsp;<\/p>\n<p>{int a = 1, b = 2;<\/p>\n<p>&nbsp;<\/p>\n<p>Swap(a, b); printf(\u201c%d \\t %d\u201d, a, b);<\/p>\n<p>&nbsp;<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p style=\"text-align: justify\">When this function is called from main() the actual arguments \u201c1\u201d and \u201c2\u201d are passed as values in the activation record. The function swap() considers these values and swaps them in the activation record. Once, the swap function returns, the activation record is removed and thus the swapping of the variables does not take place. This is the drawback of call by value. This is restored in the Call by reference method of parameter passing.<\/p>\n<\/div>\n<p>&nbsp;<\/p>\n<div>\n<p><strong>27.4.2 Call by Reference<\/strong><\/p>\n<p>&nbsp;<\/p>\n<p style=\"text-align: justify\">This method of parameter passing is also referred to as call-by address or call-by- location. The caller passes to the called procedure a pointer to the storage address of each actual parameter. Actual parameter must have an address and hence only variables make sense while expressions do not make much sense. Thus the location of the temporary variable that holds the result of the expression will be passed as parameter.<\/p>\n<p>&nbsp;<\/p>\n<p style=\"text-align: justify\">Consider the same example as discussed in Call by value. But, here we pass reference, that is the address of the parameters and hence in the activation record, address of the variables gets passed. As the swap function tries to change the values of the variable in the addresses that has been passed, call by reference ensures swap function is computed correctly. Call by reference is implemented by C++, Java etc.,<\/p>\n<p>&nbsp;<\/p>\n<p>Swap(int * a, int * b)<\/p>\n<p>&nbsp;<\/p>\n<p>{int temp;<\/p>\n<p>&nbsp;<\/p>\n<p>temp = *a; *a = *b; *b = temp;<\/p>\n<p>&nbsp;<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>Void main()<\/p>\n<p>&nbsp;<\/p>\n<p>{int a = 1, b = 2;<\/p>\n<p>&nbsp;<\/p>\n<p>Swap(&amp;a, &amp;b); printf(\u201c%d \\t %d\u201d, a, b);<\/p>\n<p>&nbsp;<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p><strong>27.4.3 Call by Copy-Restore<\/strong><\/p>\n<p>&nbsp;<\/p>\n<p style=\"text-align: justify\">This technique is a hybrid between call-by- value and call-by-reference. The actual parameters are evaluated and its r-values are passed to the called procedure as in call-by- value. When the control returns, the r-value of the formal parameters are copied back into the l- value of the actuals.<\/p>\n<p>&nbsp;<\/p>\n<p style=\"text-align: justify\">Considering the same example of swap function, if the function is called as \u201cswap(i,a[i])\u201d it works correctly using copy-restore. Location of a[i] is computed and preserved by the calling program before initiating the call. This parameter passing technique is used by Fortran.<\/p>\n<p>&nbsp;<\/p>\n<p><strong>27.4.4 Call by name<\/strong><\/p>\n<p>&nbsp;<\/p>\n<p style=\"text-align: justify\">This technique of parameter passing is defined by the copy-rule of Algol. Every procedure is considered as if it were a macro. The actual parameters are literally substituted with the formal as\u00a0\u00a0<span style=\"text-align: initial;font-size: 1em\">a macro-expansion. Local names of called procedures are kept distinct and it may be renamed. Actual parameters are surrounded by parentheses to preserve integrity\u00a0The following are permitted and yields correct results in <\/span>call<span style=\"text-align: initial;font-size: 1em\"> by name technique:<\/span><\/p>\n<\/div>\n<ul>\n<li>x : = f(A) + f(B)<\/li>\n<li>Here A , B are expressions<\/li>\n<li style=\"text-align: justify\">Substitution of expressions A and B in the formal parameter leads to call by name parameter passing technique being implemented.<\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<p style=\"text-align: justify\"><strong>Summary: <\/strong>In this module, control flow statements involving switch \u2013 case are studied. We also discussed run-time storage management in the context of Static, Stack, and Heap memory allocation. We also discussed the various parameter passing techniques.<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"author":4,"menu_order":27,"template":"","meta":{"pb_show_title":"on","pb_short_title":"","pb_subtitle":"","pb_authors":["dr-rajeswari-sridhar"],"pb_section_license":""},"chapter-type":[],"contributor":[59],"license":[],"class_list":["post-237","chapter","type-chapter","status-publish","hentry","contributor-dr-rajeswari-sridhar"],"part":3,"_links":{"self":[{"href":"https:\/\/ebooks.inflibnet.ac.in\/csp10\/wp-json\/pressbooks\/v2\/chapters\/237","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ebooks.inflibnet.ac.in\/csp10\/wp-json\/pressbooks\/v2\/chapters"}],"about":[{"href":"https:\/\/ebooks.inflibnet.ac.in\/csp10\/wp-json\/wp\/v2\/types\/chapter"}],"author":[{"embeddable":true,"href":"https:\/\/ebooks.inflibnet.ac.in\/csp10\/wp-json\/wp\/v2\/users\/4"}],"version-history":[{"count":1,"href":"https:\/\/ebooks.inflibnet.ac.in\/csp10\/wp-json\/pressbooks\/v2\/chapters\/237\/revisions"}],"predecessor-version":[{"id":240,"href":"https:\/\/ebooks.inflibnet.ac.in\/csp10\/wp-json\/pressbooks\/v2\/chapters\/237\/revisions\/240"}],"part":[{"href":"https:\/\/ebooks.inflibnet.ac.in\/csp10\/wp-json\/pressbooks\/v2\/parts\/3"}],"metadata":[{"href":"https:\/\/ebooks.inflibnet.ac.in\/csp10\/wp-json\/pressbooks\/v2\/chapters\/237\/metadata\/"}],"wp:attachment":[{"href":"https:\/\/ebooks.inflibnet.ac.in\/csp10\/wp-json\/wp\/v2\/media?parent=237"}],"wp:term":[{"taxonomy":"chapter-type","embeddable":true,"href":"https:\/\/ebooks.inflibnet.ac.in\/csp10\/wp-json\/pressbooks\/v2\/chapter-type?post=237"},{"taxonomy":"contributor","embeddable":true,"href":"https:\/\/ebooks.inflibnet.ac.in\/csp10\/wp-json\/wp\/v2\/contributor?post=237"},{"taxonomy":"license","embeddable":true,"href":"https:\/\/ebooks.inflibnet.ac.in\/csp10\/wp-json\/wp\/v2\/license?post=237"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}