 Welcome to part 2 of the lecture on intermediate code generation. So, in this part of the lecture we will continue with intermediate code generation techniques for various types of constructs in programming languages. So, to do a bit of recap last time we looked at this code generation technique for if then else statement. So, let us just go through it again. So, the production for the if then else statement can be of the form if x s going to if x s 1 semicolon n else m s 2 and then for the if then statement it is of the form s going to if x s 1 semicolon if x expands to if expression. The reason for doing this I already mentioned during semantic analysis phase and also during the last lecture we need to generate you know the the test for this expression. So, since this is an S S attributed grammar and is used with L R parsing we want to perform an action at the end of this particular handle just before the reduction by this handle happens that is the reason for breaking up this particular these productions. Then we also have a couple of markers n and m here the markers are required to generate appropriate go to statements you know at these places. For example, after the then part of the if then else statement that is s 1 is executed we are actually required to jump out of the if then else to the end of this whatever it this statement is. To do that when the marker n is used and when the reduction by n to epsilon happens a go to statement with a an unknown label target is generated. So, this particular go to statement is put on n dot next which is a list. So, at this point n dot next will contain the quadruple this you know this go to quadruple with its target unfilled m is required to remember the beginning of the code for S 2 this is required because we want to jump at if the expression is false we want to jump and execute S 2 we do not want to execute S 1 at all. So, m remembers the quadruple number of the beginning of S 2. So, that is m dot quad. So, at this point the attribute m dot quad contains the quadruple number of the beginning of the code for S 2. So, when we reduce by if e effects going to if e we generate this comparison statement if e dot result S n equal to 0 go to and we do not know the target at this point. So, we leave it as blank and this particular quadruple with its blank target is put on effects dot false list here. So, during this production the reduction by this production the effects dot false list which comes out of as a production as an attribute of effects is patched to m dot quad thereby if the result is actually false in e dot result dot S n equal to 0 is false we jump to the else part and not the then part. If the result is true then we just fall through and execute S 1 that is the idea and what is S dot next? S dot next contains all the possible jumps out of S 1. So, if there is any jump within S 1 then we need to go to the end of S 2 here after S 2. Similarly, this n dot quad must also the n dot next must also go to this point and then any other jumps out of S 2 should also go into this point. I already gave you an example of how this can happen during execution in the last lecture. So, we merge all these three lists and put it on S dot next. So, this is the way we generate code for if then else. So, let us move on and see how we can generate code for rest of the constructs. Here is a compound statement. So, there is a begin that is the parenthesis in our left flower bracket then the list of statements L and N that is the right flower bracket. So, here we just have to transfer all the unfilled jumps from L dot next to S and the A is an assignment therefore, it does not generate any jumps. So, S dot next is a N L list. For the return E that is return statement we generate the quadruple return E dot result and S dot next will be N L because the control goes out of this to the calling procedure itself. L going to L 1 semi colon M S is the production which produces a list of statements. So, obviously we need the marker M to remember the beginning of S. So, all the jumps out of L 1 are patched to M dot quad which is nothing but the beginning of S and whatever jumps remain within S or rather out of S will be put on L dot next. And the terminating production L to X simply copies S dot next into L dot next. When the procedure body ends you know there will still be some more you know jumps which are left out. So, we perform back patch S dot next comma next quad and followed by generation of the statement function N. Function N typically is returning from the procedure. So, all the left out quadruples their targets will all be patched to func N. There by there will be no unfilled targets at the end of the procedure itself. So, now let us look at a translation trace of the if then else statement. However, exactly the various back patch and gen statements are executed during the process of L R parsing and reduction. Our example is the example that we have already studied before if E 1 then there is a nested if then else if E 2 A 1 L C A 2 followed by the else part of the first if L C A 3 and here it terminates followed by A 4 which is a different statement. So, this is this entire thing is one statement and A 4 is another statement and within this statement is another if then else statement. So, we have seen this already. So, let us I will show you only a partial derivation of this. So, S derives if X S 1 semicolon N 1 else M 1 S 2. So, this is the high level if then else. So, if X derives if E 1 this S 1 is nothing but you know this entire if E 2 A 1 L C A 2 then S 2 is nothing but A 3. So, if we expand S 1 we get if X 2 S 2 1 N 2 else M 2 S 2 2 where S 2 1 is nothing but A 1 and S 2 2 is nothing but A 2. So, this is the partial derivation of our you know statement here. So, let us see how the code generation happens. First of all consider the outer if then else. So, if E 1 would reduce to if X 1 before that the code generation for E 1 would have happened then you know on reduction by if X 1 going to if E 1 we generate the code if E 1 dot result S R equal to 0 go to dash. So, at this point the just after this we have generated code for the jump by testing the expression E 1 and this quadruple address is remembered in if X 1 dot false list. So, so far we are here then the inner if then else code generation for E 2 will now happen. So, that is just before reduction to this particular handle rather if this particular non-terminal and another if then you know if E 2 dot result less than equal to 0 go to dash is generated during the reduction by if X 2 going to if E 2. So, that is what and that is remembered on if X 2 dot false list. So, we have so far finished this much. So, the code generated so far is just this code for E 1 and then a jump code for E 2 and another jump, but we still do not know the jump targets. Now, we are here so, code generation for S 2 1 happens and then we have N 2. So, N 2 by epsilon is the reduction that happens at this point and we generate a go to at this point and we remember it on N 2 dot next. Then we have M 2 dot quad which is remembered. So, that is the label say L 1 on reduction by M 2 to epsilon and then we generate the code for S 2 2 and now this entire violet part is reduced to a statement. So, on reduction by S 1 going to if X 2 S 2 1 N 2 else M 2 S 2 2 we backpatch if X 2 dot false list to L 1 that is the M dot quad. So, this entire thing you know if the expression is false we know where to go. So, we actually patch it to M 2 dot quad. So, that is this L 1. So, N 2 dot next is not yet patched because we are supposed to jump out of this. So, that is if this is this if S 2 1 is executed N 2 has generated a go to which will take you to really speaking A 4. So, we still do not know that there is A 4. So, we cannot do anything about it. It is just put on S 1 dot next which is the left hand side non-terminal here. So, so far we have generated code for S 2 2 and did some backpatching. So, because of that backpatching this target has been filled the other two are still not filled. Now, we have to take care of N 1. So, we generate a go to on reduction by N 1 to epsilon and remember it in N 1 dot next. Now, we come to M 1 and let us say that level that quadruple level is L 2. So, there is no level in practice, but it is just a number, but let us give it a level for understanding. So, this is the level which is remembered by the on reduction by M 1 to epsilon and that is remembered as M 1 dot quad. Now, we generate code for S 2 and finally, this entire thing is reduced to S. So, on reduction by S going to if X S 1 N 1 L's M 1 S 2, we can patch the false part of if X 1. So, if this if X 1 is this expression is false, then we actually have to jump to S 2. So, that is remembered by M 1 dot quad. So, that we can do at this point and N 1 dot next is still unfilled it is merged with S 1 dot next and both are put on S dot next. So, at this point S dot next contains jumps out of many points. For example, it contains jumps due to N 2 and then it will contain jumps due to N 1 and we still have not seen A 4. So, we really cannot do anything right now. So far, we have generated up to this code for S 2. So, what that means is the code for A 4. So, to do that let us look at the production which handles a list of statements. So, L going to L 1 semicolon M 3 S 4. So, at this point this L 1 is our statement actually. So, that is S 3 right this statement. So, S 3 is semicolon M 3 S 4. So, S 3 is our complete nested if then else and S 4 is A 4. So, M 3 is actually M 3 dot quad remembers the beginning of S 4. So, that is M L 3 and then the code generation for S 4 happens. Now, we reduce using this entire production L going to L 1 M 3 S 4 and we back patch all the jumps out of L 1. So, that is whatever comes out now this go to and this go to both are actually patch to A 4 which is nothing but S 4. So, now L naught lengths is empty and we because A 4 does not generate any jumps and the process of code generation is over. So, here is the final code that is generated. So, this is the trace of actions that happen during the code generation process including back patch and gen. So, let us move on let us find out how to generate code for while loops. So, we need to break up the while statement into the production for the while statement into do parts for the same reason as the if then else. So, we have while x going to while E while M E. So, M is a marker again which denotes which remembers the beginning of this expression. Why do we need this at the end of the loop we actually have to jump to the beginning of the code for E. So, that E is evaluated again and then that it is tested. So, at this point while x p dot false list is a make list next quad next quad is nothing but this particular quadruple. So, the test for E. So, the target is unfilled this is the exit for the while loop. So, we have no idea where we are we have to go this will be known only when there is a list of statements. So, while x dot begin is nothing but M dot quad. So, we remember that also as an attribute of while x here s going to while x do s 1. So, we generate we have actually finished this entire code generation or expression and s 1. So, we are here we need to go back to the beginning of the while loop. So, then go to while x dot begin will take care of that. So, remember here while x dot begin is nothing but the beginning of the expression code. Then all the jumps out of s 1 have to again take the control to the beginning of the while loop. So, back patch s 1 dot next to while x dot begin. Finally, what are the jumps out of the statement s dot next equal to while x dot false list that is the only jump out of the entire while loop. So, let us now move on try to understand code generation for function declaration and function call. So, assume that there is no nesting of functions that is the norm in C as well. So, let us take a result a function called foo its type is result it has a parameter list it has variable declarations and it has a statement list. So, this is the body of the function foo. So, the code generated for the function foo would be funk begin foo. So, this intermediate code is supposed to create the activation record reserve the space for local variables and temporaries. So, then the code for the statement list is here. Remember there is no code that is necessary for parameter list and variables it is just the space that is needed for them and the space you know offsets etcetera have already been computed. Then the function end. So, this religious the activation record and returns. So, fairly simple you know as far as the function declaration is concerned. Let us look at a function call. So, here is an assignment x equal to and then a function call bar of p 1 comma p 2 comma p 3 three parameters. So, obviously before the call is made we need to evaluate the three parameters. So, code for evaluation of p 1 p 2 and p 3 the results are in the three temporaries t 1, t 2 and t 3. Then the result of the call let us say is supposed to be returned in a temporary call t 4. Finally, we need to actually you know assign that to x. So, x equal to t 4 after the call returns we will take care of this, but there is a point that needs to be stressed here. The temporary t 4 that we are going to use to return the result is now in the callers space that is the function in which this x equal to bar p 1 p 2 p 3 is enclosed. It is not a local variable of the function bar, but it is a variable in the local space of the enclosing function for this assignment statement. So, now the parameter code is param t 1, param t 2, param t 3. These three will actually push the parameters onto the stack or the activation record. Then there is a rough param t 4 why did we really want to pass the address of t 4 and not t 4 itself. We would like to access you know this particular address of t 4 which is in this callers space within the function bar. So, if we had simply said param t 4 then you know we would have actually written the result within bar to a local variable, but here because it is a rough param the address of t 4 is passed and whenever we assign the result within bar we would use indirect addressing so that the actual you know temporary t 4 gets the value. So, including this result part t 4 there are now 4 parameters 3 explicit and 1 implicit. So, we call bar with 4 parameters so this procedure call or function call is supposed to create access links push return address and jump to the code for bar. We are going to see the details of all this in one of the future lectures on runtime environments. After the call has materialized t 4 will contain the return result. So, x equal to t 4 takes care of assigning the result to the variable x. So, now that is the code template. So, let us understand how to generate that type of code. So, again we are assuming that there is no nesting of functions. So, the function call is a very simple production function call going to id and followed by parameter list. So, let us embed 2 actions in between and expand them here. So, action 1 is the code for action 1 is search func id dot name comma found comma f n p t r. So, we are going to check and get the pointer to the function in the symbol table. So, that is returned in f n p t r. So, call name p t r equal to f n p t r. So, at this point we know that this is the function pointer. This is required to process the parameter list and emit the code for parameters. So, here in action 2 the code for parameters has already been emitted. So, remember call name p t r is a global variable because we have no inherited variables are been inherited attributes available. So, now we need to generate a temporary for the result. What is the type of that temporary? We do not know that. So, we use the function pointer into the symbol table function name pointer into the symbol table execute a function get result type which is a compiler function which goes into the symbol table and gets the type of that function and then generate a temporary of that particular type. So, let that be result var. So, we generate ref param result var we already know why this is necessary and the machine code at the time of you know running will actually place the a in return a in the variable result var. How is this possible? This is possible because the position of the result in the parameter list is always known to the code generator and the run time system. So, the code generator knows that this will be the last in the parameter list it knows the number of parameters and it also knows the position of the result. So, it is easy for the machine code generator to generate an instruction to place this return result into the variable result var using indirect addressing remember result var has been passed as a ref param. Now, the call itself call name pointer is the pointer to the function in the symbol table and how many parameters? Param list dot p n o is the number of parameters plus 1 corresponds to the result which is an implicit parameter. So, this is the call for the function. Now, how do we compute the number of parameters? That is fairly easy. Param list going to p list you know just transfers the p list dot p n o to param list dot p n o. We need this kind of a unique production because we want to make sure that we permit 0 number of parameters as well. So, param list going to epsilon make sure that 0 number of parameters is permitted. So, param list dot p n o is 0 and p list generates one or more parameters p list going to e or p list going to p list comma e. If it is p list going to e this is the first parameter. So, we set the number to 1 and the first gen first param e result is generated. For the rest of it as and when the expressions are passed we generate p list 1 rather we generate gen param e dot result and the number of parameters is counted and incremented. So, p list 2 dot p n o plus 1 is assigned to p list 1 dot p n o. So, this is how p list accumulates the number of parameters and passes it on to param list and that is used in this production to generate the appropriate call with the number of parameters. So, what about declaration? Fairly easy. Function declaration is function head followed by wire declaration body. So, at this point we generate the gen funk end. So, because we have passed this entire right hand side and what about body dot next? So, as I already showed you body dot next corresponds to all the jumps out of the loops etcetera within this body and the last part of the body and that is passed to the function end. So, that there are no more hanging gotoes. Function head goes to result id followed by declaration p list. So, now we search for id dot name find it in the symbol table and it is pointer is name p t r and active funk p t r would be. So, this is the function name. So, that would be name p t r. We are now looking at the body of the function. So, the pointer is active funk p t r. Now, generate funk begin active funk p t r. So, the code generation for function declarations is quite straight forward. Let us now understand how to generate code for various types of assignment statements. So, before that we must understand the representation for multi dimensional arrays because we will use multi dimensional arrays in assignments and other expressions. So, if there is a three dimensional array we can look at it in three dimensions as slices. So, these are the various slices and each slice consists of squares. So, each square is an element. So, this is the 2 d array and this is the third dimension of the array. So, this is the i part, this is the j part and this is the k part. So, when we represent a three dimensional array in memory. Obviously, memories are single dimensional. So, we must map the 3 d array to a 1 d representation. How is that done? For the 3 d array that we have. So, these are the slices. So, i equal to 0, i equal to 1, i equal to 2, etcetera, etcetera. So, this is i equal to 0 and this entire thing is i equal to 1. Similarly, we will have i equal to 2, etcetera. In one of the slices, so say this is the second slice. So, we have i equal to 1, we have j equal to 0, j equal to 1, j equal to 2, 3, etcetera. So, this is j equal to 0 and this is j equal to 1. So, let us say there are only 2 possible indices here. So, within this slice and the part corresponding to j equal to 1. So, i equal to 1 and j equal to 1. So, this is the third element 0, 1, 2, 3. So, k equal to 3. So, whenever we say a of 1, 1, 3, we must map it to this element in the entire single dimensional array representation. So, that offset is computed as i star n 2, n 2 is the size of the second dimension, number of elements in the second dimension. So, this is the slice number. Then n 2 corresponds to this, you know, 2 d array size. Each one of the elements of the slice contains as many elements in this as in this 2 d array. So, that is why i begins with a 0 here, we are considering c like representation, c like programming language. So, i starts with a 0, that is why it is i star n 2. So, if we have i equal to 0, then this becomes 0. Obviously, with i equal to 1, we need to skip this i equal to 0. So, that is why when i equal to 1, we need to skip n 2 elements. So, we have skipped here and come to this point, skip this entire slice and come up to this point for i equal to 1. Then we add j to it. So, let us say we are looking at this element. So, we have, we now want i equal to 1, j equal to 1, k equal to 3. So, we add j to it. So, that is plus 1 and then multiply the whole thing by n 3, that is because we need to skip so many elements, you know, within the j. So, j is 0 here, j equal to 1 here. So, we need to skip this entire j equal to 0 and come to this part of the slice rather the 2D array. So, that is done by star n 3. So, star n 3 says, you know, this is j. So, this is the row number. So, how many elements are there in each row? That is n 3. Now, plus k means the number of elements that we have skipped here. So, 0, 1, 2 and then k equal to 3. So, we come to this point by adding k. All this has to be multiplied by the number of bytes that each element of the array, so that is this element takes. So, that is why we multiply it by the element size. So, that will bring us to the beginning of this element and then we can do what we want with it. So, let us see the code generation template for this. Consider a declaration A 10, 20, 35 and then another simple variable B. A is an array, B is a simple variable. For the simple assignment B equal to x 1, where x 1 is an arithmetic expression, the template is code for evaluation of x 1 result in t 1 and then the quad B equal to t 1 fairly simple. But, suppose the assignment statement is complicated A of x 2, x 3, x 4. So, this is the element of A to which we want to assign x 5. So, I have mentioned the offset part again here, so that we can understand how the code is computed or as what is generated. So, x 2 is evaluated first result in t 2, x 3 is evaluated next result in t 3. So, now we do t 2 star 20 which is nothing but the n 2 part second dimension 20. Then we add the j that is x 3, now we generate code for x 4, then you know this t 5 is multiplied by n 3 that is 35. We add the k part that is t 6 here x 3 or rather x 4. Now, the each element is an int here, so element size is int size, so t 8 into int size is t 9. Now, t 10 we get address A, we generate code for x 5 in t 11 and now finally, we can say t 10, t 9 is t 11. So, t 10 is the base address of the array, t 9 is the offset that we have computed byte level and t 11 is the expression on the right hand side. So, this is the type of code that gets generated for arrays. So, let us see the S attributed grammar for assignments and expressions and understand how the code gets generated during L R parsing. This is a set E G again, so we have the assignment statement S going to L equal to E. Now, L is the left hand side, it could be an array element, it could be a simple variable. So, if it is a an array element, then we need the base address and the offset and if it is a simple variable, we just need the address of that variable. So, L requires two attributes, one is L dot place pointing to the name of the variable or the temporary in the symbol table. So, this could be the array base address or array name or the simple variable name and L dot offset is the offset in the case of an array. So, if it is a simple variable then it is null. So, we check whether the variable is simple or array. So, if L dot offset equal to null then it is a simple variable. So, we simply generate L dot place equal to E dot result. So, this is the assignment which is generated. So, I already showed you that here, so this is simple variable. So, L will have only the address of B and nothing else. So, if it is an array variable then we need to generate L dot place brackets L dot offset equal to E dot result. So, this is the indexed instruction that needs to be generated. So, this is what we generated here you know this particular instruction. Then parent size expression is there is only a transfer of the attribute and E going to L. So, this E is on the right side. So, remember that this can also be either a simple variable or an array expression again we need to distinguish between these two. So, if L dot offset is null then E dot result is L dot place. So, simple straight forward if it is not null then the right hand side is an array expression. So, the point is a single quadruple cannot have array expression on the left hand side and array expression on the right hand side high level language statements can. So, I can write A of B equal to C of D in the high level language statement, but in the quadruple level there can be an array axis or indexed instruction only on the left side or on the right side one at a time. So, we know that it is an array and we know that this is the right hand side. So, we generate a temporary E dot result equal to new temp L dot type. So, that is the type of L and generate E dot result equal to L dot place bracket L dot offset. So, an indexed instruction is generated and it is a you know and that will be in E dot result. So, the value will be in E dot result. So, this is how we take care of assignments from an array and here we take care of assignments into an array. What remains is computation of the mapping from 3D array to R 4D array to single dimensional array, we will see that very soon. If E is a number we just generate E dot result equal to num dot value. So, E dot result is a temporary of the type num dot type addition. So, E going to E 1 plus E 2, not so trivial because here the types of E 1 and E 2 will drive the code generation process. So, if E 1 and E 2 are of the same type and then there is no problem you know we do not need any type conversion. If E 1 and E 2 are of different types then we actually have to do a type conversion. So, for example, result type is compatible type E 1 dot type comma E 2 dot type. So, if E 1 dot type is real and E 2 dot type is also real absolutely no problem then result type will be real. Similarly, if both are integers this will be integer, if one is integer and other one is real then result type will always be real. So, this is how compatible type checks all this we have seen this during semantic analysis. So, we require this to generate the temporary for E dot result, new temp result type. Now, generating code for type conversion is done here. So, if E 1 dot type is result type then there is no type conversion necessary opera and one equal to E dot result. So, we just transfer the address and if E 1 dot type is integer and result dot type is real. So, that means we must generate code for type conversion. So, we generate a temporary opera and one equal to new temp real and generate instruction opera and one equal to convert float E 1 dot result. So, remember E 1 dot result is integer type. So, we are converting it into real type and assigning it into the temporary opera and one. So, this is the instruction that is generated for the type conversion exactly the same thing happens for E 2 as well. So, if there is no type conversion opera and 2 retains the address of E 2 dot result, if there is a need for type conversion then we generate a temporary and you know generate the instruction opera and 2 equal to convert float E 2 dot result. So, now at the end of this exercise opera and one holds the address of the first opera and opera and two holds the address of the second opera and with or without type conversion. Now, we are ready to generate the plus operation E dot result equal to opera and one plus opera and two. So, this is the instruction for E 1 plus E 2. If there was no problem and all types were just integer then all this checking was not necessary type conversion was not necessary. We would have generated a simple instruction E dot result equal to E 1 dot result plus E 2 dot result. So, that is not possible anymore because of many types which are permitted in expressions. Exactly the same thing happens for the logical operation or E dot result we generate a new temporary and we generate E dot result equal to E 1 dot result or E 2 dot result. I am skipping some of the details like type conversion here because even here character or integer both are permitted in C. So, some simple type conversion may be necessary even for this and it is on the same lines as the E 1 plus E 2. So, I will skip that and leave it for exercises E going to even less than E 2. So, there is a relational operation here. So, even less than E 2 will generate either a 1 or a 0 depending on the conclusion. So, if even is definitely less than E 2 then the result is true. So, we generate a 1 if even is not less than E 2 then we generate a 0 and that value is put in E dot result. So, E dot result gets an integer temporary the address is stored in E dot result. First we initialize it to 1. So, generate instruction E dot result equal to 1. Then we generate the comparison instruction if even dot result less than E 2 dot result go to next squared plus 2. So, that is the result is true. So, this is next squared. So, if E 1 dot result less than E 2 dot result is actually next squared. So, this is the number next squared in the quadruple array this is next squared plus 1 and after this that is after the expression E is next squared plus 2. So, that is where we need to jump and continue from that point onwards. So, if this is true then we do not reset the result to 0 we keep it as 1 and then go further. If the result is false then we make the result 0 and then continue with the operation. So, that is how it would be. So, if the left hand side L is just a name then search for the name. So, the name could be either a variable or a parameter. So, we search the symbol table and get its pointer. So, L dot place becomes V n and L dot offset becomes null because this is a simple variable. Now, we come to the important part of expressions array expressions. So, again we have broken the productions into parts as in the case of semantic analysis. So, E is going to I D bracket E. So, this is the first expression search for the expression find its pointer. Now, the dimension number is 1 because this is the first expression the E list dot array pointer is V n. So, that is the pointer to the name of the array in the symbol table. So, that is the entry and E list dot result will be E dot result. So, that is the first subscript of the array expression the rest of the subscripts are generated by this production and finally, the expression is closed using this production. So, let us look at this first the E list has generated all the expressions and so I D bracket E is also E list. So, first expression is generated then the second third etcetera are all generated here finally, the bracket closes the subscript expression list. So, once all the subscripts are generated L dot place is still E list dot array pointer. So, that is the base address part of L then temp equal to new temp int. So, we generate a new temporary and offset will be actually nothing but that new temporary. We are going to generate instructions to collect the offset into the temporary. Size of the element is obtained through the symbol table pointer. So, E list dot array pointer point to E list E list size. So, this will give us the element size from the symbol table. Now, we generate the instruction temp equal to E list dot result. So, that is the function of all the parameters subscripts here star E list size. So, we have done the nested evaluation finally, what remains is to multiply by the size of the element like int or float. So, that is done here after E list dot result accumulates the entire expression value. So, now at the end of this we L dot place and L dot offset will contain the appropriate pointers into the symbol table and the code for evaluation of the entire subscript list has been generated. So, here is the subscript list generated. So, this is the first part of the subscript list and this is the next last one. So, if there are three subscripts first one is generated here then you know E list 1 comma E. So, this is the second one and then the third one and so on and so forth using this we can generate as many as we really want. So, if we do this three or two times we generate E list 1 comma E and E list 1 comma E again. So, and finally, E list going to I D expression generates the first one. So, this is the first one and then the second one and third one can be generated using these productions again and again. Number of dimensions E list dot dime is this plus 1. So, this is the next dimension. So, that we did array pointer is just transferred you know from here. So, we found it we got that here right. So, we are going to transfer it to E list dot array pointer. Number of elements we look at the symbol table using the array pointer and then the number of dimensions or number of elements in this dimension for this subscript will be E list 1 dot dime plus 1 right. So, this is the dimension number of this. So, we search the symbol table and get the number of elements corresponding to that particular dimension that is this E. Now, generate temporaries to do partial evaluation temp 1 and temp 2 both are in type because subscripts are always integer expressions. Temp 1 multiplies. So, E list 1 dot result star num alum. So, the number of elements in this dimension is multiplied with E list 1 dot result. So, that is I star into plus j whole thing star n 3. So, that is the you know nested evaluation that we are doing here. Then we add temp 2 equal to generate temp 2 equal to temp 1 plus E dot result. So, this is this does I star into then plus j that is what we are doing. So, if we repeat this using the same production again and again we can generate the instructions for the entire subscript list. So, now we have completed code generation for expressions assuming that there is nothing called short circuit evaluation. So, let us understand what exactly is short circuit evaluation for Boolean expressions. So, expression 1 and expression 2 suppose we want to evaluate this the straight forward method would be evaluate expression 1 completely then evaluate expression 2 completely and generate an and instruction for the 2. So, that is what we are doing here right. So, instead of this or we can have and here. So, you would generate even dot result and E 2 dot result, but there is another way of generating the result. So, its value is if expression 1 is false then the entire expression is false that is true because if this is and operation. So, if this is false then the entire expression 1 is false. So, if the expression 1 is true then you know we must evaluate expression 2. Otherwise we would not know how exactly what exactly the value would be. So, the value would be x 2 itself whether it is true or false whatever that is. So, this implies that x 2 need not be evaluated if expression 1 is false. So, in other words we are short circuiting expression 2 that is why this is called short circuit evaluation. The same thing holds for r as well in, but it is exactly the converse. In the case of and if this was false the expression would have been false in the case of r if this is true x 1 is true the entire expression is true. So, if x 1 is true we can short circuit evaluate x 2 because it is not necessary to evaluate it anymore, but if expression 1 is false then we must evaluate expression 2 as well. So, the value is if x 1 then true else x 2. So, that is the way short circuit evaluation is done expression 2 is not evaluated if expression 1 is true. So, Boolean expressions are commonly used conditional loop statements and if we use short circuit evaluation it is possible to realize this short circuit evaluation of expressions using control flow constructs I will show you how that can be done. So, in other words in such cases there are no explicit r and and operators in the intermediate code as we saw earlier, but there are going to be only extra jump statements. This is much faster because complete expression is not evaluated at all and if un-evaluated expressions have side effects. For example, if expression 2 has a function which alters a global variable or prints out something into a file etcetera then it will never be done if expression 1 is true because we are going to short circuit expression 2. So, in such cases the program may have non-deterministic behavior because the once first time possibly expression is true and this entire thing is skipped, but next time possibly expression 1 is false. So, expression 2 will also be evaluated and at that time the printing will happen. So, the person who is running the program will see that printing happens sometime and printing does not happen some other time. So, this is non-deterministic behavior. Let me show you a template or rather an example of how it can be done and then we will see how to generate code. So, the expression is if a plus b less than c plus d or this entire thing then a 1 else a 2 followed by the treatment a 3 irrespective of what happens here. So, we evaluate a plus b and c plus d if t 1 is less than t 2. So, we are evaluating this now go to l 1. So, if this is true then the entire thing can be skipped. So, and where is l 1 l 1 is code for a 1. So, this is short circuit we have short circuit at this entire expression. So, that is taken care of by go to l 1 otherwise we go to l 2 l 2 now evaluates the second expression. Second expression has and here. So, if e equal to f go to l 3. So, that then we must evaluate this expression if e equal to f is false then you know we do not have to evaluate this entire expression at all. So, that means we go to l 4 which is code for a 2. So, that is the else part of it. So, inside this we again check g greater than h minus k go to l 5 or go to l 6. So, that is the code for a 2 finally code for a 3 will be executed in all the cases. So, this is how short circuit expression really happens in the case of if the nulls. We will stop here and we will continue with code generation scheme for such expressions in the next part of the lecture. Thank you.