 Welcome to part 4 of the lecture on machine code generation. Today, we will continue with our discussion on machine code generation, specially tree rewriting code generation from direct acyclic graphs and P-Pole optimization. So, first of all let us see the difficulties, deficiencies and limitations of the dynamic programming based code generation. The major problem is many instructions such as the multiply instruction and you know many other floating point instructions etcetera in modern day processors require even odd register pairs for their operation for example, R 0, R 1, R 2, R 3 etcetera. They really cannot do with pairs such as R 1, R 2 or R 3, R 4 and so on. So, in such cases they may require non contiguous you know evaluation orders to become optimal code generators. So, this cannot be handled by dynamic programming as we have already studied it, it requires ad hoc changes and this is one of the major limitations of the approach. To overcome this approach there has been you know at trial with tree rewriting, this is very successful, it caters to very complex instruction sets and very general machine models, it can produce locally optimal code for basic blocks and then non contiguous evaluation orders can also be handled without sacrificing any optimality. It is of course, you know a possible to retarget the code generator to different kinds of machines and automatic generation of code generators from specifications is also possible. So, let us take an example, this is an example of you know the tree intermediate code. So, instead of using quadruples tree rewriting systems require tree intermediate code. So, this tree rewriting system that I am going to show you by example, will be a simple one, a complete tree rewriting system will require many more instructions and so on. So, in this example we will take a very simple tree intermediate code for example, a i equal to b plus 1, a is a local variable, it is an array, i is also a local variable and b is a global variable. The difference between the local and global variable is that, the global variable will be present in the static area of memory whereas, the local variable will be accessed using the stack pointer. So, that is the difference. So, let me explain what these operators mean and then we will go on to the tree rewriting example. The first operator at the root is the assignment operator and the assignment has a left hand side and a right hand side. The right hand side is a very simple plus expression and the left hand side is an elaborate array expression corresponding to a of i. So, the right hand side first because it is simpler, it has a memory operand b and a constant 1. So, b plus 1 is synthesized here and on the left hand side we have the base address of the array which is a constant here and then the register is the stack pointer and then the contents of the stack pointer and the constant a are added. So, that is the plus operator and so this tells you where exactly the array is placed inside the activation record. So, that is what this gives us and here we have the constant i and then again the register stack pointer. So, adding these two will tell us where the variable i is positioned in the array activation record. So, taking the contents of that location will give you the contents of the location i. So, adding that to the base address within the activation record for the array a will give us the position of the element a i. So, in the operator tells us that we should really consider the address of that because it is on the left hand side of the assignment operator. So, this is the meaning of this entire tree intermediate code. So, now let me also show you some tree rewriting rules that we are going to use in our example. The first tree rewriting rule says reg i with an arrow which is pointed towards the left and then constant a. So, remember in a context tree grammar production this arrow is actually pointing the other way whereas, in a tree rewriting specification the arrow points from right to left. So, the way to you know read this would be constant a would be rewritten or replaced by reg i. So, that is going to be the semantics of this rewriting rule. So, the way the tree pattern matching or tree rewriting system works would be it determines where this constant a is present in the entire tree and then it tries to once it matches it, it replaces with a node called register with an appropriate register number allocated to it. So, when this happens at an appropriate time a small code piece of code will also be emitted and that code in this case will be load hash a comma reg i. So, a hash a is the you know immediate mode of addressing and reg i would be the appropriate register which is used in this instruction. So, similarly there are other instructions with plus reg i comma reg j and then end plus const c comma reg i etcetera. So, we will understand each of these as we go on. So, match number 1 in this intermediate code. So, the match happens at this place this is the constant a node. So, as I already indicated to you the pattern which matches at this node is the reg i arrow const a pattern. So, const a would be rewritten by reg and at this point there is a register location which happens and register 0 is allocated to the you know hold the constant a at this point. So, this node const a would be rewritten by reg with the appropriate you know 0 now register number 0 allocated to it and code generation does not happen immediately at this time it can happen later also, but let us assume that code generation can happen in a hand in hand with this matching itself. I will tell you why it should not be done immediately, but at this point it is ok to assume that it will happen immediately. The code which is emitted would be load hash a comma r 0 indicating that the constant a would be loaded into the register number 0. So, this is the code which is generated so far and now. So, we have the register number you know const a being replaced by reg 0 now that along with the other two nodes in the tree we will constitute the next pattern which is to be matched. So, the pattern that is going to match at this point is the reg i left arrow plus reg i comma reg j indicating that root is plus the left sub tree is reg 0 and the right sub tree is reg s p. So, here reg i is identified with reg 0 and reg j is identified with reg s p and plus remains as it is. So, when this tree pattern is reduced to reg 0 which is the left hand side we emit the code add s p comma r 0. So, the code which is emitted so far says load hash a comma r 0 add s p comma r 0. So, this entire sub tree will now be deleted from the intermediate code and it will be replaced by reg 0. So, this is the node which we replaced so far and now we have this entire big tree which is found as a pattern in the intermediate code. So, in fact there are two possibilities of match at this point one is to use a smaller tree that is this part the second option is to use a bigger tree which is this entire thing. So, the smaller one would be so, the in plus con c comma reg j that is this part and the bigger one would be plus reg i in plus con c comma reg j which is this entire big one. What is the difference between these two matches? The difference is if we actually match the bigger pattern that we have found here it leads us to emitting fewer number of instructions. Whereas, if we match this smaller pattern first and then go on to produce code it is possible that we emit more number of instructions and the code generation scheme may not produce optimal code. So, in this case let us assume usually it is better to assume better to choose the bigger pattern. So, let us assume this entire tree gets matched and that would be replaced by reg 0 as far as our pattern tree pattern matching rule says. So, again reg i corresponds to reg 0 and the con c corresponds to con c i here and then reg j corresponds to reg s p. So, if this is matched so then we have reg 0 here that was matched before and now the new node that matches is mem b which is going to be replaced by reg 1 the pattern which matches is reg i left arrow mem a. So, the a corresponds to b here and then this 1 corresponds to i here and the code that is generated is load b comma r 1. So, the indication is this memory is now going to be replaced by a register showing that memory has been moved into the memory location has been moved into a contents of the memory location have been moved into a given register at this point. So, this becomes reg 1 so far this remains as it is. So, we have emitted 4 instructions so far and now match number 5 this was the previous match that we found and now we match this entire thing. So, here it is very convenient to emit an increment instruction it is very easy to see that this is a plus this is reg and this is a const 1. So, whenever there is a constant of 1 it is easy to emit a increment instruction. So, the pattern which matches at this point is reg i comma const 1 and associated code would be increment instruction. So, we emit the increment r 1 instruction and reduce this entire tree pattern to the reg 1. So, this is the code that we have emitted so far. Now, what remains is this and it matches the pattern assignment in reg i reg j and the code generated would be load r 1 comma star r 0 and this entire thing reduces to memory. So, now we stop at this point because there is no more rewriting that can be done. So, the instruction which is generated here says take the contents of r 1 and put it into the location point it 2 by reg 0. So, that is what star r 0 really means. So, this in on the left hand side of an assignment always says take the address and not the contents. So, the code that we have generated so far corresponds to this. So, this is the general method of performing tree rewriting and code generation. So, now how does a code generator generator really work? So, it is based on tree pattern matching and dynamic programming. So, where does dynamic programming come in here? So, that is easy. The reason is in this particular example here, we saw that there were two alternatives for the pattern that matched here. So, in general what we do in a tree rewriting system is to associate a cost for each one of the pattern matches. Then once we associate a pattern match with every node of the tree, it is said to cover the tree. The minimum cost cover of the tree is computed and from that we deduce the patterns which match at this at the various nodes and produce the optimal cost code. So, code emission will not happen hand in hand with tree pattern matching. Tree pattern matching happens first. The various nodes are actually decorated with the matches that the matcher has produced. The cost of these matches is computed and finally, the cost of the match at the root gets computed. So, this would have happened in this case also. It so happens that the second alternative uses better code. So, that code was used in our example. So, that is what this code generator generator in general does. It uses dynamic programming to compute the minimum cost cover. So, we accept tree patterns associated cost and the semantic actions associated with the tree patterns for register location and code generation. So, taking all this as input, it produces tree matches, tree pattern matchers that produce a cover of minimal cost. So, dynamic programming becomes inbuilt into this tree pattern matching process and we actually make two passes. The pattern matcher rather the code generator which is produced makes two passes. The first pass is a bottom up pass and it finds a set of patterns that cover the entire tree that I explained just a few minutes ago. Once we find this, we find the minimum cost cover as well and then in the second pass, we execute the semantic actions associated with the minimum cost cover and produce and emit the code that is necessary. So, this is how the code generators which are produced by the code generator generator systems work. So, there are many not commercial but very successful open source tools available in the public domain. One is called twig, the other is called burg and the second one, third one is called iBurg. So, let me tell you briefly about each one of these. iBurg uses dynamic programming at compile time. So, in other words when it is building the code generator, it does not use any dynamic programming at all. It does not analyze the tree rewriting rules to produce the best tree pattern matching and so on and so forth, but it uses dynamic programming at code generation time. Therefore, the advantage is the cost can be very arbitrary, it can be any computation, but it is a bit slow compared to the other schemes. The matcher of course is hard coded. Twig uses a table driven tree pattern matcher and it uses a string pattern matching based tree pattern matcher. It has very high overheads, it uses o n square time and n being the number of nodes in the subject tree. It uses dynamic programming again at compile time like iBurg does and of course, the cost can be arbitrary. Burg is the best of the tools that is available again, but the problem is Burg uses bottom up tree rewriting system and the theory is extremely complicated. Therefore, it is very hard to produce a code generator generator system using this particular theory, but it produces optimal code. It really moves the dynamic programming technique into the code generator time rather than the code generation time and the costs must be constants. So, the implication of this cost being constants is that you cannot have arbitrary computations, but you can only have constants for each one of the rules that we have mentioned. Now, let us move on to the code generation from directed acyclic graphs. So far, we have actually looked at trees and studied mechanisms to produce code generation from trees, but in practice a basic block gives rise to directed acyclic graphs. So, code generation from directed acyclic graphs is known to be an NP complete problem. It cannot be done in polynomial time, it requires you know exponential amount of time to produce optimal code. So, directed acyclic graphs are divided into trees and then processed, otherwise there is no algorithm which processes directed acyclic graphs and produces optimal code. I am going to give you examples of how this happens. So, we may actually replicate shared trees or we may store the result of the tree into you know memory and then use it in all the places where the tree is used. So, there are two possibilities. If we share the replicated trees in such a case, the code size increases you know dramatically, whereas if we store it into memory and then use it, then it may result in suboptimal code. So, let me give you an example to drive home the point. So, now the this is a DAG, why is this a directed acyclic graph? Well the nodes are shared here. So, this node and this node both of them are shared. So, we cannot conduct any tree pattern matching on this directed acyclic graph. So, what we really try to do is take the shared tree, this is a shared tree, it is shared between the nodes 2 and 3. So, we actually replicate the shared tree for both these nodes. So, the right subtree of 2 is shared and the left subtree of 3 is shared. So, we replicate and make it into two separate copies, but in the meanwhile a small subtree 8 and 11 is again shared between 4 and 5. So, we really have to make a copy of this small tree for 4 as well. So, we do that here. So, it is easy to see that such a code replication increases the size of the tree too much. So, in fact, this is not even though the code that is produced will be very fast, this is not a viable scheme, it really increases the size of the code too much. The second scheme would be and I forgot to mention that on this tree we really apply tree pattern matching and code generation as we have studied so far and that would produce code that is optimal to this tree, but since we have replicated the optimality is lost with respect to the directed acyclic graph. So, what happens if we chop the tree at this point? For example, let us chop the tree at this point and let us chop the tree at this point as well. So, we have produced 3 sub trees. So, the first one it has 5 which is not shared which is assumed to be a memory location and then the second subtree is 5, 8 and 9 and then the third subtree is 8, 10 and 11. So, the indication is we definitely have to evaluate 8, 10, 11 first because 8 is contained as the left subtree of 5 here and then it is also contained as the right subtree of 4. So, it is not possible to evaluate this pattern and this pattern before we evaluate this tree. So, this must be evaluated first. Now, the question is having evaluated 8, 10, 11 should we go ahead with the evaluation of parts of this tree or should we evaluate this tree and then go to this. So, as you can see there are several choices here. So, in general if the tree is very large and we have many such smaller trees produced out of this pruning operation, there will be a large number of choices which need to be checked before we decide that one of them gives us optimal code. This is the difficulty. So, the code generation strategy again becomes you know an expensive one unless we use a heuristic. So, say this is the order in which we produce the code. So, whether we use duplication or we use sharing the code generation scheme is still really is more difficult than in the case of ordinary trees. So, let us move on to a minor optimization which is called as a peephole optimization. Peephole optimization by definition looks at a small window of instructions. So, this is a very simple, but very effective local optimization and it is usually carried out on machine code, but there is nothing to prevent us from doing it on intermediate code. The reason we do not do it on intermediate code is that it is not that effective compared to what is done on machine code. Reason is simple, when we generate machine code there are many more instructions for each one of the intermediate code instructions. So, there is much more scope to produce you know more optimized code when we use actually do peephole optimization. Whereas, if we do it on intermediate code then the opportunities for code optimization would be fewer. So, now the details of peephole optimization it examines a sliding window of code. So, this is what is known as a peephole and then it tries to find patterns within that peephole, replaces the patterns which are found by more efficient code that it can actually determine and now the entire sequence of instructions in the window becomes possibly a much more compact and efficient sequence of instructions compared to the previous one. Improvement opportunities for additional improvements will accrue as we go on doing it. For once we improve the code then you know it may give you opportunities for more improvements. So, we may have to repeat the peephole optimization until no more improvements are possible. So, peephole optimizations are many in number. For example, eliminating redundant instructions and then eliminating unreachable code, eliminating jumps over jumps, algebraic simplifications, strength reduction and use of machine idioms. These are this is a small list there are many other minor possibilities. So, let us understand each of these by looking at an example. So, how do we eliminate redundant load and store instructions in a peephole? So, let us assume that here is a basic block it contains many instructions among these instructions there is a load x comma r 0 followed by you know many other instructions which do not modify r 0 r x and then at the end of this sequence there is a store r 0 comma x. So, now if you assume that there are no modifications to x and r 0 in this space, it is very clear that the second instruction store r 0 comma x is redundant. So, let us see why there is load x comma r 0. So, x and r 0 now contain the same value. So, if x is not modified here and r 0 is not modified here loading r 0 into x again will not change the value of x at all. So, again r 0 and has not been modified. So, there is no new value in r 0 to be put into x the same is true for the other three patterns as well. We have a load x comma r 0 and then there are no instructions which change x either x or r 0 then there is a load x comma r 0 again very clearly this is redundant. So, third one store r 0 comma x and followed by instructions which do not change either x or r 0 then we have a load x comma r 0. Again you know since r 0 and x do not change the second one does not put a new value into r 0 and the load instruction is redundant. The fourth pattern is store r 0 comma x followed by a sequence of instructions which do not change either r 0 or x and finally, another instruction store r 0 comma x again this is redundant because the store has already been executed previously. So, in any of these four patterns the pattern matcher would detect that there is a load followed by instructions which do not change the value of either x or r 0 followed by an r 0 store r 0 comma x. So, what is the role of peephole here? The possibility is the peephole is the entire basic block which may have thousands of instructions which is a bit too large usually a peephole consists of just about 10 to 20 instructions. So, it would examine the instructions in that peephole exhaustively for each one of the patterns that we have actually identified. So, in this case these are the four patterns for redundant load and store elimination if any of these patterns are found then it does exactly what is denoted here delete the store instruction delete the load instruction etcetera. So, this is an example of how peephole optimization improves the by removing redundant load and store instructions. How does one eliminate unreachable code? The code that becomes unreachable may be because of many reasons. See if there is a you know an unlabeled instruction which follows an unconditional jump instruction right then it is very clear that we will never get to the instruction following the unconditional jump because there is no label to that instruction. So, such an instruction can be easily removed, but in the first place how did such a situation arise? I will give you an example it may be produced because we had debugging code introduced into the program during development stage it was never removed in the final version of the software one possibility. Second possibility there were lots of updates which were made to the program. So, as the program changed there were many pieces of code which became kind of bad they had unreachable code and so on, but it was never ironed out and never improved by the programmer. So, the final code again contained such unreachable code. So, let me give you an example of how to eliminate such unreachable code. So, this is the you know piece of code which existed before it had a debugging instruction if print equal to 1 go to L 1 otherwise go to L 2 and then L 1 had some debugging information print information which printed out a lot of information useful for debugging and L 2 of course, was normal code. So, whenever the programmer wanted to debug he would set the variable print to 1 and then it would go and print the debugging information and once the debugging was over the programmer did not do anything. So, the code remained as it is, but the print was set to 0 and then left at that point. The first thing that a compiler does in all these cases is you know instead of having this go to L 1 and then go to L 2 which is a jump over the jump. So, this jumps over this jump it can change the condition as if print not equal to 1 go to L 2 it did that automatically. So, now you know there is an improvement already you know so instead of a check here and then a go to it can directly go to L 2 in case print is not required. Since print was initialized to 0 by the programmer debugging phase is over so now this is production version. The instruction now the constant propagation took over print was actually replace sorry by 0. So, this became 0 not equal to 1 which was in turn found to be false by the compiler. So, sorry 0 not equal to 1 is found to be true by the compiler. So, this entire thing becomes a go to L 2 this part is not even needed it is evaluated permanently by the compiler itself. So, now you have a go to instruction here followed by print instructions which would not have any labels and then the normal piece of code. So, it is very clear that the print instructions which are here can never be reached by any part of the code again. So, this has become unreachable code the compiler now automatically removes such print instructions or any other instructions which exist between go to L 2 and L 2 because they are unreachable code. So, this is an example of how unreachable code can be removed in people optimization. There are a couple of flow of control optimizations which are very similar in spirit to the unreachable code removal. For example, here is go to L 1 and then L 1 says go to L 2. So, we jump here and then again jump to L 2. So, this can be obviously optimized as go to L 2 and the other one retained as go to L 2. So, we do not have to jump twice in order to go to L 2. Now, suppose you know we observe this code we never jump to L 1 and there are no other jumps to L 1 anywhere in the rest of the code let us say. So, now we do not have to really keep this statement provided it can be you know it can be preceded it is preceded by an unconditional jump. So, otherwise it is possible that we there is a jump to L 1 from some other place. So, we cannot remove this piece of code without being assured that just before this there is a there are no jumps to L 1 and fall through from this part of the code to go to L 2 does not happen. See one is requirement is there are no jumps to L 1 from any other place. So, that makes sure that we do not come into L 1 from any other place, but what happens if this piece of code gets executed and then we fall through and execute go to L 2. In order that this option is also taken care of there must be a you know an unconditional go to just before L 1. So, if that happens and this also happens we can remove the statement L 1 labeled L 1. So, this is the way in which we actually remove some of the unnecessary go to statements in the code. Then the next option next possibility is if A less than B then go to L 1 and L 1 has another go to L 2. So, in such a case again we can replace it as if A less than B go to L 2 and keep the other one as go to L 2. So, in such a situation again we eliminate you know double jumps and replace it by a single jump instruction. So, the other difficulty is there is a go to L 1 instruction and L 1 itself as if A less than B then go to L 2. In such a case only one jump to L 1 you know in this case go to L 1 has always is always executed. So, every execution goes to go to L 1 and then jumps to L 1 whereas, only one jump to L 1 L 1 is preceded by an unconditional go to. So, if that is the pattern that we detect then you know it is possible that we some we can sometimes skip the statement go to L 3. So, if A less is this pattern this code is now going to be replaced by this code. So, if A less than B go to L 2 and then go to L 3. So, now in this case the code possibly skips go to L 3 sometimes not necessarily always whereas, here we had always executed go to L 1. So, the number of go to statements which is executed by the program reduces by this transformation. The next people optimization is called as reduction in strength and use of machine idioms. This is more like you know may sound like advice which is given to embedded system programmers as well. X square is obviously cheaper to implement as x star x and usually we never call a routine exponentiation routine to actually compute x square. But in case it has been done and we find that there is a call to an exponentiation routine inserted by a naive programmer this can be replaced by you know x star x. So, this is a very simple optimization which actually gives us much more improvement in execution speed. Similarly, x into 2 cube is obviously cheaper to be implemented as x left shift 3 that is x left shifted 3 times. So, you know every left shift operation actually is equivalent to multiplication by 2. So, if we do it 3 times then it is multiplication 3 times by 2. So, that is 2 cube. Similarly, every right shift operation is a division by 2. So, x by 2 square can be implement as 2 right shift operations. Similar advice again floating point division is a by a constant you know can be actually the constant 1 by a constant can be evaluated by the compiler and that division can now be replaced as a multiplication by a constant. So, this can be done by a P4 optimizer. Then if the machine has auto increment and auto decrement addressing modes they can be used wherever possible, but how does one use it? You know it is possible to subsume increment and decrement operations respectively into these addressing modes. So, whenever we have these addressing modes available we can and there is an increment operation which can be combined with it. It can be subsumed into this addressing mode and appropriate instructions can be emitted by the P4 optimizer. There are more complicated patterns which can be detected such as multiply and add. This is a much more complicated pattern and not many P4 optimizers actually detect and complete it. So, from now we move on to the you know this is the code generation that we have been discussing so far. So, now let us see how to generate code for object oriented programming languages. So far we discussed code generation for C type of programming languages. Now, let us see how to generate code for object oriented languages such as Java and C plus plus. So, now let us first overview the requirements of the language and then you know the rules that are used for mapping the names to methods etcetera etcetera. The variable names and their visibility the rules for these also have to be studied then we come to the code generation methods. So, the some of the simple optimizations which can be performed for such languages are also going to be reviewed here and some parts of this lecture are you know based on an excellent chapter in the book engineering a compiler by Cooper and Torxon. So, let us review the language requirements to make a language as object oriented. First of all the language which is said to be object oriented must have you know a declaration for objects and it must have a declaration for classes. So, C plus plus and Java both satisfy this requirement. The second important requirement is that there must be inheritance we should be able to declare sub classes and have available super classes. This is obviously satisfied. So, inheritance is a property by which we can say here is a class A. Now, I declare another class B which has inherited all the properties from the class A. So, that is how inheritance works we will see examples of this very soon. So, inheritance requires that a sub class have all the instance variables specified by its super class. So, in other words whenever we instantiate a you know a class and we produce a an object then you know we obviously, are going to have variables of that particular class instantiated in the object that is possible, but here we are talking about a different variety all together. We have a class then we produce a sub class using inheritance. So, the sub class will have you know all the variables instance variables specified by the super class itself. So, this is something different from what the object specified. So, whatever the super class has will all be available to the sub class. This is kind of necessary the reason is the super class will have a lot of methods which work with its instance variables. Now, when we declare a sub class we may have methods of the super class working on the objects of type sub class itself. So, when we want to this to happen unless the rule is satisfied and there would be difficulties in execution. Then if a is b super class then some are all of a's you know methods and instance variables may be available re-refined in b. So, let us look at an example to understand all these concepts in a better way. The color code is very simple. So, this brown indicates objects this greenish blue indicates classes and red indicates methods. So, there are three classes here this is the highest level super class that is called as three. There is a super class called two which is a sub class of this class three. So, this is class two which is a sub class of class three, but two itself is a super class as far as class one is concerned. So, the class hierarchy is three is at the highest level two is the next level and one is at the lowest level. The implication of this is whatever methods and variables that three declares will all be available to in two and whatever is declared in three and two will all be available in one as well. So, it does not mean the these two classes cannot define something on their own they can add extra you know for example, let us go through the list to understand this better. There is a static variable n which is available in all of them. There is a very you know a method called phi which is actually defined in three, but it is redefined in two and one. So, each of them have their own phi see this is phi of two and is phi of one. So, whatever is available here will not be used by one and two they will be redefined. Then there is a method called FUM which is defined by class three. FUM is actually shared by both class two, class one and class three. So, this is the difference. So, phi is private to not private sorry phi is not shared between one two and three. It is available to three it is redefined in two and it is redefined in one as well. Whereas, the method FUM is available to both three two and one and they are also shared. So, this is they are also sharing the method FUM that is very important. Four is not available in the class three at all it is a new method which is defined in class two it was not available in three and now this is shared between class one and class two. So, and finally, this method phi FIE phi is available within class one alone it is not defined in two it is not defined in three it is available only in you know class one. So, the whatever is available in the super class may be redefined in the sub class it can share the available one with the super class or and it can also introduce new methods of its own in the sub class itself. Apart from this it is also possible to introduce extra variables. For example, if we let us see see for example, is a variable which is an instance of class number class two. So, it has two you know variables within it x and y two fields whereas, the variables a and b are instances of the class one. So, these are two objects which are instances of class one. So, x and y of course are available in both a and b simply because they were available to all instances of class two which is the super class of class one, but z is extra it is available only to instances of class one and it is not available to any instance of class two. So, these are some of the general difficulties and specialties which are introduced in the language because of inheritance. So, now let us look at a few more details. Method invocations are not always static calls what is a static call? We know the address of the method which is to be called that is when it is static. If it is not static then there are special things that need to be done in order to call that particular method. So, for example, a dot phi is a method call it invokes one dot phi. So, let us look at it we have the you know object a here right. So, it is actually an instance of class one instance of class one and we are calling a dot phi. So, that is the method of class one itself. So, that is what we want here one dot phi whereas, if we call the function method a dot four it invokes two dot four. So, again going back to this. So, if we say a dot four the method four is not defined by class one, but it is defined by its super class class two. So, it is not available within class one, but it has to go and get it from class two. So, that is the difference. So, even though class this object a belongs to class one the method call actually now invoked you know supposed to be a which is invoked it does not belong to class one, but it is invoked from class two. Even worse suppose we call a dot firm. So, a dot firm even though a is an object of class one it is firm is not defined either in class one or in class two it is actually defined in class three which is two classes away it is a super class of both one and two. So, after having such one it goes to class two it is not found there either it will have to check class three then get hold of the code for firm and then execute it. So, this is the this is what I mean by saying it is not always a static call it is not even known whether the code corresponding to the method exists within that particular class from which we are making a call or it belongs to the super call. So, that is not even known then how does this method look up really work in practice. So, method look up behaves as if it is such making a search for each procedure call. So, these are called as virtual calls because they are not static calls they must be given a name. So, they are really called virtual calls. So, the first search is always within the first the parent class. So, the method the object actually makes a call to a method the method is searched in the parent you know method table if it is not found there the method tables of the super class of the parent are searched and the search goes upwards to the various super classes available in the hierarchy. How do you make this search very efficient that is the question one possibility is we copy the entire method table from each of these super classes into the parent class itself each one of the classes will contain a complete method table. So, that the implementation becomes very efficient the second possibility is instead of including the entire method table we could introduce actually a pointer to the method table and that is called as a virtual table pointer. The next thing is to study the mapping of names to methods. So, if the class hierarchy and the class structure can be determined completely at you know compile time then we can actually build the entire method table at compile time and put it into each class that is very easy right. So, we know all the classes we know all the methods in every one of the classes. So, we build the class hierarchy then we determine the various method you know build all the method tables place the entire method table into each class. So, that is fairly straightforward, but there are situations in languages situations which has java where the class does not exist at compile time. The class may be produced dynamically during execution or it can be possibly loaded dynamically via internet when the program execution after the program execution begins. So, we suddenly call let us say a method which of a class which does not exist right now then the method will be downloaded via internet rather it is not the method the class will be downloaded via the internet and then you know we create that class in the data structures the data structures of that class will be created. We create an object of that particular class and we may have to execute the method corresponding to that particular class later on. So, such dynamic execution you know rather dynamic loading of classes introduces difficulties into our mapping names to methods and etcetera. A full look up in the class hierarchy can be performed at run time to take care of this. So, we build the class hierarchy at run time and then we perform the look up. Of course, we can use complete method tables as before, but we need to update the method tables whenever there is a change in the class hierarchy or a new class is added and so on and so forth. We will stop our discussion at this time and continue in the next class. Thank you.