 So let's begin with the last phase of the compilation which is machine code generation. And what we're going to look at in next remaining classes, whatever remains there, how do we convert now into a different equation of how it's the next of the machine. Now, the only issue which is left now is that we have already done all the code generation. We know that here this code is going to be very close to whatever machine we think of. We'll pick up a symbolic machine which will be close to almost all kind of risk-insist architectures. And we are just going to convert that into popular syntax except one thing, and it is the register optimization. So what we also simultaneously do is we'll see that with the activity variables. We want to minimize all the number of categories we have in that all the categories, as far as possible, anti-capital registers. And if in case it does not fit into register, then we also have to see how to spill the register and then load it back. What we are not going to do is look at register optimization problem, because that is something which goes really beyond this, and which is an independent activity. And there are a lot of graph based algorithm which are available. Graph does we can get from the register optimization. So we'll look at functional correctness, and we're not going too much about as far as register problems are about optimization itself. So this is really what you see here as the specifications that we have this front end. We have seen an intermediate port generator. We have all the information at the symbol table. And now we want to take information from the intermediate port generator, which is the theater score. We want to take information from the symbol table, and we want to give the complete syntax of the machine. And what are the functional requirements? Obviously the output port is correct. That is the biggest functional requirement we have. Also the port must be of high quality. And then the port generator itself should run efficiently. It should not happen that port generator takes so much time. Then whatever gains we have in terms of an efficient port, those are lost. So these are heightable functional specifications of what we want to do. And what are the inputs and outputs we have? So input is an intermediate representation with symbol table. And the assumption we are going to make here is that input has already been validated by the front end. Because if the input is not correct with respect to the language specifications, then even the intermediate port generator would not work. So process would have been terminated right there. But at this point of time, it's unlikely that we'll find any kind of error scale. But there are other issues which we'll talk about and we'll address those issues. Now what about the target programs? Target program could either be an absolute machine language. Now this is very fast for small programs. And you have already seen that how to be actually generate machine port. So we have seen that patching. We have seen to generate the numbers there which are PC values. And we can now generate absolute machine port. We can also generate relocatable machine port, which is normally what we do. Because the advantage with relocatable port is that I can have multiple functions and that I can link them. In case of absolute machine language port, so absolute machine language port, even the linker has been absorbed in the compilation phase. That is not something which is desirable in the little world for very small programs. And then we may have an assembly output which obviously will require assembly linker and loader support. So normally most compilers either we generate this or we generate this. But we'll not go for this unless one is really, really the retoy compiler. And I don't know how many of you have used programs like Turbo-C compiler. Have you ever installed Turbo-C compiler on your machine? Some point of time? How many? Any kind of turbo compiler. Most people do it at the level of school, not even in the graduating school. So not in IP. You must have done it in some time, class 10, 11. And if you recall, by installation, it asks for two, three options. It asks whether you want a tiny version, you want a small version, you want a mid version, or a large version. And if you select the option tiny version, what it installs is a compiler which actually generates absolute machine port. It will not permit you to write multiple functions and multiple files and it will allow you to do it. Just remove all that. The compiler becomes very fast. But then you can only handle small programs. So we are only dealing in this space and we will not worry about the absolute machine port. So what are the issues we face? Now these are the issues I am flagging so that when we actually start looking at the algorithm, you will realize that why these issues are important. So first issue that comes is the uniform. Now since we have done a similar language program, if I pick up a typical, say, assist machine, then I am not dealing with this for the timing, this is just maybe too trivial for this. But assume that you have a assist machine and you have off-codes. So typically, how do I equip this machine? I have off-codes and I have addressing modes. Now can I take all addressing modes and leave them with all off-codes? Have you seen whether there is kind of problem in doing that? How many of you have the experience of extensive assembly time program? Extensive means at least you have written, say, 100 to 200 lines of code. That is all the small for assembly. But about 100 to 200 lines of assembly code. Anyone? No one. Is that interesting? Even in CS240, you will do that? Or you have power to do that? Power to do that. Graduating in forget the beta also. Come on. CS240 was only last year. So what happens normally in off-codes is if you see addressing modes, most machines have a lot of off-codes. But there is off-codes to not work with all addressing modes. So for example, if I say that I want to do addition. Addition will say that it will work only with registers or it will work with indirect registers, for example. Or if I say store, store will work with index offset. May not work with registers. So normally, you will find that if you look at machine specifications, there, they will say that certain off-codes are not going to work with certain addressing modes. That immediately brings an issue of unique. Or if you say that if I pick up an operation, what are the access modes? Addressing mode is nothing but an access pattern. Addressing mode is the one which is telling you that how can I store the variable and how can I pick it up? This immediately says that you have to be careful while choosing off-codes. Because all addressing modes may not be applicable on this. All access patterns may not be applicable on a particular address mode. So this is one issue. You have to be aware of that when I'm picking up an addressing mode, it means an access pattern. Then I need to pick up the right kind of off-codes for that. And vice versa. Similarly, when we talk of completeness, and this question has not been answered, as yet even in the research community, how do I prove that the intermediate representation I have generated can be completely mapped out to the machine? Will I get into a situation where I cannot find a machine off-codes? I cannot find an addressing mode to map that intermediate representation to the machine? So this issue came up very early in the game, and machines did not have, for example, multiplication and division. So on architecture, you don't have an off-codes for doing multiplication and division, and suddenly you found that if you had to do multiplication and multiplication and division, you had to do completeness. Somebody was able to write a macro and saying that a multiplication can be done by a sequence of additions. Can I get into a situation where I say that something just cannot be computed? Is there a proof for that? My experience, we know that we're not getting into a problem, but at least from the search point of view, completeness still remains an issue that there's no guarantee that compilers will always be able to generate the functional code. Instruction speed is another issue that comes up that I can write multiple programs for doing the same computation. That means I can generate multiple codes at the target machine and which will do the same computation. Now which one is better? Sometimes you see that some programs are going to be faster, some programs are going to consume less number of memory cycles, and there are more issues like if you, again, go into the research direction, it will even talk about that which is a speech program. That's a program which will consume less energy. So can I generate instructions which are power sensitive and which will consume total less energy? That is, again, an optimization, but we'll only look at optimization with respect to resources and speech. We'll not go into the power mode, at least not in the solution. Then register allocation becomes an issue for us that instructions with register offerings are going to be faster. So the variables we are going to store in registers, normally the variables which have long life times and which have very frequent taxes. Now long life time may be determined by saying what is the start time and what is the end time, but I may be only accessing it once in the beginning and once in the end. That is not really the long life time. Long life time should be coupled with, yes, it's a variable, not just the time. So all long life time encounters which are frequent variables are going to be in the registers, and then they are also going to be every location than on some machines. I'll also have to worry about clearing of even and not registers, typically to take it to machines and fit. Almost every machine has an internal processor. You'll find that if I take a long word, then you have to pair it in certain way. I cannot say that take register three and four, and load this word, I may have to take certain payout for registers. So even when I have more registers available, I may not be able to load certain variables into the registers. Evaluation order of instruction again remains an issue that if I have a large expression being evaluated, what is the evaluation order? On it says that I must compute from left to right. Once you have converted that into some kind of abstract syntax tree, and for optimization reasons, you may find that it may not be beneficial to compute from left to right. It may be beneficial to say that if we first compute the other side, store the result somewhere, and that may compute fewer resources. So again we'll see an example of that. So you'll see that as we keep on moving, all these issues will be at some point of time raised and addressed. So let's look at four generation or what I call as instruction selection, because we have already been four generation at this point of time. What we are looking at is concrete syntax of the machine. So what may happen is that if I take something as trivial as saying that two assignments, A is being assigned B plus C, and B is being assigned A plus E, then I can generate very trivial code for this. All I need to do is that for the first three address instruction, I generate three machine instructions, and I'm using a generating syntax. This is not taking any of the machines. You will recognize this as soon as you read it. So this says move B to a register, add C to the register, and then move register to take. I'm not using again addressing words of A, B, and C. Assuming that is address removal permissible, then I just use them, and then I say that when I come to the second three at the store, I'm saying move A to register, add E to register, and again move register to B. So somewhere you will find that this move would be equivalent to load, and this would be stored. So again symbolic names may be different from different machines, but this indicates that in which direction I'm moving my data. Now you can see that I can do some very simple optimizations, and I can do an optimization saying, oh, I remember that A is already in the register, so I don't have to have a load again. And I can immediately eliminate these instructions and reduce instructions from number of instructions which are here, six to five. Is that a big gain? Six to five instruction reduction? Yes. Yes, so if you count with some percentages, they look like it's like a 16% gain on the speed. But actually what may happen is that if this is part of a loop and that loop operates a large number of times, then this cumulative gain can be very high. So one is not really talking about only programs which will have six instructions because six instructions absolute time will be so small that it really doesn't matter, but one is talking about overall program optimization. And if I have an instruction like this which says that I want to increment A by one, then I may have an instruction says move A to R zero at immediate one to R zero and then store R zero to A, but many machines will also provide me a single instruction to say increment A. Now what may happen on some machines is that when I'm trying to increment A that A if it is in memory may not be increment instruction, may not be allowed, only if A is in register then increment is allowed. So normally what will happen is that if I have a loop going on and this is really the loop counter, then assumption is that at first time I must have loaded this. So if it is just this and I say that instead of add I use increment that doesn't give me much gain, but suppose this was already in register and every time I say I need to increment my loop counter by one, I mean this is typical form loop one is going to write, then instead of using add I can use increment and this increment is going to be much faster, okay. Why? Because not only this is a single instruction, it has fewer fetches, at least I mean it does not have to worry about the number of sites we say. And this, how do I know the cost? How do I know the cost of each instruction who provides me that? You can look at an architecture manual and that will provide you the how many micro, four micro cycles each instruction is going to take, okay. In fact they provide a lot more information like how much power each of these instructions is going to consume, okay. So if you really want to optimize you can look at all those parameters and then you can find out that which is the fastest possible code. So you can find that we'll get into situations where if I do very trivial code generation I may have generating sequence like this but then I can optimize. Now what should I do? Should I generate this code and optimize? Or at this point of time I do the analysis. So most of the compilers will take an approach that first generate trivial code using a trivial algorithm which is functionally correct and then optimize it using a technique called equal optimization, okay. So here it will say that people optimization will again look at people optimization but will people optimization will say that I have sequence of slow and load and therefore you can eliminate second load and the first load is one which will slow the value but value always remains in the register and then just reuse that, okay. So this way I can do straightforward code generation and efficiency is not an issue but efficiency can be adjusted by an optimization. So you can see that if you now recall overall compiler structure I gave you there was an optimization phase between the front end and the back end and then there was a post-code generation optimization, okay. So at every step, so I talked about optimization which was at the level of algebra where we said that in three address code if you have additions with zero, addition, multiplication with one and so on you can eliminate that and then post-code generation we had an optimization which was people optimization and those are the ones that we are going to address. So let's assume certain target machine, okay. Now what we are assuming for target machine is if you want to really pick up complicated examples that we have five addressable machine and four bytes are forming a word and it has a sequence of registers R0 to Rn minus one, depending on the machine you may have eight registers, 16 registers, 32 registers, one pair registers and so on, okay. And two address instructions are in the form that I have op-code, source and destination. So I don't have three address instructions but two address instructions and machines which will provide you op-con, op-to, destination, okay. So that machines like this where you actually have to give three addresses but we are assuming that I have two address machine source and destination so it also is this is op-con and op-to and one of the op-prenses also is a target of the instruction, okay or target of the computation of the instruction. Then we have standard op-codes like move, add, subtract all the arithmetic and logical operations, okay. And what are the possible addressing modes I may have? I may have absolute addressing mode where I say that take this content of memory location or I may say take the value in the register or I may say that I can use indexing where I say that my register has an address and I take that address, add some constant to this and then fetch from memory location the contents of that particular memory location or I may have an indirect register where I may say that my register contains an address and then I'm trying to find the contents of the memory location whose address is stored in the register or I may have indirect index where I may say that I apply one mode level of indirection or I may have little where I say that add this value immediately or use this value immediately like I said add immediate one, okay. Now just to give you non-uniformity things you will find that if I have add instruction you will find their machines which will say that if this operand the first operand is up to can be stored in three bits, okay. Then this opcode is different but it needs to be stored in four bytes, okay. Then this is going to be different, okay. So there may be instruction like if you look at Motorola instructions that it will say add immediate, okay. Now add immediate means that this whole instruction so what they did was something very tricky, okay. This said that normally your opcode is going to be stored in four bytes, okay. But since add immediate we know that operand can only be fitted in three bits that the reduced size of opcode and the part of the operand was encoded in that opcode byte, okay. That means in a single fetch you'll be able to get both the opcode and one of the operands, okay. But it cannot, so now and why this instruction is provided you can see that most of the time what is going to happen is that if you are incrementing your loop counter you increment it by a small number, right. I mean you don't say increment it every time by 100 you may say increment by one or two and normally these kind of instructions therefore will work much faster, okay. So architects normally will provide you different kind of instructions which will do the same thing but on different data sizes and different address inputs, yes, okay. So there's a kind of machine we'll assume, okay. So you can see that this is a generic machine and you can actually take any architecture or any opcode, any instruction set and you can sort of find a mapping from one to one map, okay. So this is my machine model. Now let's look at starting of the code generation and the first thing I want to do now is assuming that I have generated free address code I want to break this free address code into what you know as basic log, okay. What is a basic log? A basic log is a sequence of free address code which can have a single entry and single exit. That means I can jump only to the first instruction and once I start executing the first instruction all instructions in that basic log must get executed and I can exit out of this basic log either by naturally falling through the control or by having an explicit jump statement, okay. There's no way that I can have a basic log and I can say that jump into middle or exit from the middle. These kind of situations are not permitted. So there's a reason for doing that, okay. The reason is that this gives me a structure where I say that I can do certain optimizations on this without worrying about how control is going because I know that how control is going control is always going essentially, okay. And only the last instruction can be a jump statement and within log obviously then I'll not be able to jump, okay. I'll have to go outside the block to jump back, okay. So sequence of statements in which close control enters at the beginning and leaves at the end. So what is only permitted is a situation like this, okay. No matter what's in the middle, no exit's out of the middle, okay. And how do I find out now what are the instructions, three address instructions which form the basic log? How will I do that? I'll give you some remains very simple. Just from the definition what I can do is I can start scanning my three address code and mark certain instructions as leaders. And what are the instructions which are the leaders? First instruction obviously is going to be beginning of a basic log. Then I say that anything which is target of a jump has to be beginning of a basic log. Any instruction which follows a jump instruction also is a basic log, beginning of a basic log. So all these instructions I, in my three address code I mark as leaders. And then I say that start scanning instructions from a leader up to the instruction prior to the next leader. That gives me a basic log, right. So basically what I'm doing is I'm taking my three address code and within this three address code I say this definitely is a leader instruction. No doubt about that. That is the beginning of a basic log. Then I say that target of a jump, the target of a jump instruction has to be beginning of a basic log, okay. Aren't do anything about it. And what is the third thing? Instruction next to a jump. I instruction next to a jump because as soon as I say jump that must be the last instruction. So next instruction was always the beginning of another basic log, okay. So once I mark all these leaders, okay, then what do I say? And these are the leaders I mark during this logic. Then I say every state starting from the leader up to the next instruction is a leader and they give me all the basic log. So first thing I do is I take this three address code and break it into what we know as basic log. And once I have constructed a set of basic logs, then I say let me add now control flow to this, okay. So what do I do now? Okay, so just finish this slide. So first statement is a leader, any target of a go-to is a leader, and any statement that follows a go-to statement is also a leader. And each leader and its basic log consists of leader and all the statements are put the next leader, right. Now how do I add control flow? So now I say that I'm going to now actually convert this into a graph where I'll say nodes are going to be each of the basic logs. And how will control flow go? Control flow will say that if there is an explicit jump from a log V1, so suppose I say these are high basic logs, V1, V2, V3, and V4. If I say that there is an explicit jump from V1 to V2, then there is going to be a range from V1 to V2 in this graph. And if there is a natural flow of control from V1 to V2, then there is going to be a range from V1. Okay, I will hurt his neck. Why don't you wake him up and ask him to go through this one? You'll hurt your neck the way that you're filtering. You can go back to your room and sleep there peacefully. You don't need to come here, right. Any other edges I can add here? So basically what you're saying is that in the control flow graph, in the flow graph, I'm taking all the basic logs as nodes. Nodes are the basic logs and if there's a directed edge from V1 to V2, then V2 can follow V1 in some execution order. Or if there is a jump from the last mentioned statement of V1 to the first statement of V2, these are the only edges I'm going to add. Do you need an example for this? So if I just say that, look at a situation like this. So what are the kinds of graphs I can get? I can get something like this. What may happen here is that the last statement may not be a jump statement. Still I may have an edge from V1 to V2 because there is a jump from the last statement of basic log 2, the beginning of this procedure. That means this instruction becomes a leader and this follows it in the natural execution order. So if I have a jump or a conditional test here, I can construct my control flow graph. And all further code generation will be done on control flow graph. And why I'm doing the control flow graph? The idea is that as far as the basic log is concerned, I'll be able to generate straight line code for that. And as far as the last instruction, if it is, if it happens to be a jump instruction, which is either a conditional or unconditional jump, then I'll be able to generate a different kind of instruction for that. Because jump instructions, free address jump instructions are going to be mapped on to machine instruction in a slightly different map. So this, taking a free address code and converting this into a basic log and then a control flow graph makes sense. Is it to understand? Any problems? Any questions there? So then let's move on. And what we want to do now is we want to now do the register optimization. Register optimization to the extent, saying that if some value is already there register, then I want to use it. I want to use the same location. And for that I compute what we know as next use information. And next use information is going to be contributed for each of the basic blocks. And why I'm contributing for each of the basic blocks? Because it does not have control flow. Control flow is strictly sequential. I can scan all the instructions. And I know that I don't come across a jump instruction. So what we do first thing is we actually use some rotation and say that if this is the kind of instruction I have, then I say that this is IF instruction. It is defining variable X and it is using two variables, Y and Z. This is the normal clicker value. So basically we are saying that I want to now optimize registers and temporary allocations. And I want to remove variables from registers because they are not going to be used. So suppose I take some temporary value. So X, Y, Z could either be two defined variables or they could be temporary variables that can be generated by the compiler. And when does compiler generate temporary variables? So whenever I'm generating the address code, you will find that compiler generates all the T1, T2, and so on. Those are temporary variables. These are not the user defined variables. So compiler is going to generate that. And hopefully I'm going to put all these temporary variables into registers as far as possible if I have sufficient registers. Or I optimize that. I minimize the number of temporary and then use fewer registers. Where did we see that earlier? Somewhere did we have an example where we were minimizing the number of temporary and doing some optimization. There was that. Type checking syntax translation. When I was looking at saying that I can keep all these attributes somewhere and rather than keeping attributes for the lifetime of the program, I said I want to keep attributes only for the lifetime of this attribute only. And then if you recall one example we had in the type checking where I was trying to define declaration of an array, I reduced all those attribute equations into three locations. And I said I can use only R1, R2, and R3. And then we realized that some of them were copies of each other and I could reduce the whole thing to just two registers. So we have already done some kind of resource optimization. Now what we want to do is, we want to systematically look at each basic law, look at how many temporaries it uses and minimize number of temporary. So that I can reduce number of registers I use. I don't have to do register screening because if I use too many temporaries then I will not have sufficient number of registers. So what do we do? We say that this statement which says x is signed y of z is defining x and is using y of z. And we say that any variable from register is going to be removed if it is not going to be used in. So what I do is systematically, suppose I have this instruction x being designed y of z and suppose x is in a register. And beyond this point x has no use. So if I look at a sequence of instruction and I find x is not being used here, I can immediately free all the resources which are found with that particular x. Now when I say free, what that means is that the same resource can be now used for another temporary. That is what free means. So scan each basic law of records and you will see why we are doing this. And assume that all the temporaries are negated. So register allocation and register optimization are only going to do across the basic law. That means when I enter basic law, no variables are going to be registered. And when I exit basic law, no variables are going to be registered. Now there is another technique which you will learn in some other course not in this. Where you say that I want to do optimization across the basic law. But optimization across the basic law requires a general technique called data flow analysis because I do not know which paths may be followed. So to reach this particular basic law I may follow this path or sometimes I may follow this path and I have to optimize it across all the paths. And idea of the basic law is that you have only one path so I do not have to worry about data flow analysis at all. That is what makes it simple. So what we are going to do is we are going to assume that all the temporaries because we are doing optimization only within the basic law. That all the temporaries are made on exit and all user variables are live on exit. User variables have a live time which is the whole program but temporaries have a live time which is limited by the boundaries of the basic law. There is an assumption I am taking. And if we do data flow analysis we can extend this and say that these temporaries are going to be live even across the basic blocks and they can be live anywhere but since we are not doing data flow analysis we will not worry about this. So are boundary conditions clear? What we are trying to do here? No. Say something now. So what we do here is that for each basic law now I will make a small symbol table. They are very simple symbol table. All it says is that I will keep status of each variable available in this symbol table. And this will say that whether a variable is live or dead. Live or dead means whether it has a future use or it does not have a future use. Any variable which has a future use is live any variable which does not have a future use is dead. So what we do is that suppose I am scanning this instruction then what will I do? I will say that 2 i's I am going to attach information in this symbol table about variables x, y and z. I will only worry about temperature I will not worry about user variables. And then I will say that beyond this point I will say x is not live and it has no next use in symbol table and I am going to say y and z to be live and z to be live and has a next use in i. Now first conceptually understand what is happening. And then we can go through the cellular then I will pick up an example and then take me to an example and explain this. But conceptually I am able to see what I am doing. So suppose I say that I am scanning this and let's say I am at this point. See I can only get points which are either next to instruction So suppose I am at this point. Now when I am at this point what does this point mean? At this point it is saying that x and y and z are going to be used here. So this is what I am saying that y and z are live at this point and they have a use in i. They are going to be used here. And this is saying that x is not live and it has no next use in the symbol table. That whatever x is being created here whatever x I had prior to this is no longer going to be true. From this point onwards this is the x which is going to be live. All prior axis are going to be now dead. This is what I am saying. So if I take any instruction and I say that let me find out status of each of the variables at this point. Just before this instruction passes. This instruction so basically at this point my information is that whatever definitions of x I had prior to this is valid because beyond this point this is the only definition of x which will be used in a basic law. And at this point I am saying that y and z are still live and they are being used here. And obviously if I come to this point then what will I say? What can I say about y and z at this point? Whether they have a future use or not? So at this point after this instruction has been executed what can I say about status of x, y and z? So at this point is x live. We are scanning that way. Yes. We have already scanned that way. So exactly the point I am trying to break out is why I am scanning backwards. So if you just scan forward at this point you cannot say anything about x, y and z unless you see the instructions which follow it. And scanning backwards I will be able to see that information and that is the reason I actually scan my code backwards and not forward. So what is the use of a variable and then I find the most recent definition of that variable. So what I will do is I will start there for scanning in this direction and at some point of time I say that x is being used then I immediately set x to live and say this is the definition I am going to use in an instruction and then I will try to find out the most recent definition of x and say this is the way definition of x which must be used here. And that is the reason I do not scan forward. Because in forward scan I cannot really determine anything about x, y and z till I scan everything till the end. So that is unnecessary long questions. So it is logic clear why I am scanning my code backwards and why I am setting this kind of status. So what we can do is we take an example I take you through this algorithm and actually show you how do we optimize. So let me write a piece of code and let me copy this on the board and then I go back to the previous point and what is the example of. So that both the algorithm and the example remain in front of me. This says that we just copy here this says t1 is is star k. So this is clear this code t2 is is star v t v is t2 t4 t1 plus t5 is star v and t6 is t4 plus t5 and x is sine of t6 and what I have asked this is instruction 1 this is this is what my instruction is now what I need to do is I need to create a simple table and how many variables I have for which I need to have empty different table. I am only worried about the temporary variables I will not do any optimization of the user variables. So a, b are user variables and p1 a, b and x are user variables and p1, p6 are the temporary compiler variables. So what I say here is that I have 6 entries here because only 2 p1 p2 p3 p4 p5 p6 and then I say that let me scan variable status at these points these are the points in my program. Now first thing I have said is that at this point all the variables are dead nothing is left because I am not looking at life of p1, p6 beyond this point so assumption I gave was that all the variables are dead. So what I do is I set in my simple table that all the variables are dead and now look at what I am doing here. So what I am doing here is that when I scan this instruction I say attach to i information in simple table about x, y and z. So now I am saying that I am attaching to 7 information about p6 and information about p6 what is the information about p6 in my simple table this p6 is dead. So here I attach information saying so let me write it here but I think this is the case. So p6 is dead there is only information about x as I said x is a user variable so I don't even worry about it. And once I have done this then I say that set x to not lie and load x2 in the simple table. Now what is x? x is the left hand side which is a user variable which is not a category variable so I don't change anything in the simple table and this says set y and z to be live and it says x2 is in i in the simple table. So on the right hand side I have p6 so what I do is that I set p6 to live and I say use in clear what I have done so what I have done is something very simple I started with my simple table saying that all the variables are dead which was the status here then I just followed these three steps I said to 7 I am going to attach information about p6 and I said p6 is dead that was in the simple table and this order is important and then I said set x to not lie so x is not a user variable I don't do anything about it I am not even in my simple table and then I say that set y is that to be live and say it has an exclusive simple table in i so I have set this to live and I say this is now as a user itself so I am actually at this program okay now I go to this program okay so I am standing backwards I have done this I have done this how about about this okay I am at this program so first step clear because the first step is not clear then we have to get lost in the remaining class clear the first step is clear to everyone okay now I say again I just follow this okay then I say that I am going to attach to 6 information about p4, p5 and p6 okay so what is the information about p4, p5 and p6 this is p4 and p5 are dead and p6 is live and using not sufficient so let me write it on this okay so this is p4 and p5 are dead okay and p6 as in 7 this is the information I have in simple table but I will just talk with this information and I will attach to it okay and then I say set x to not live so what is my x here p6 okay so again I say that I remove this and I say that this is set okay and then I say that set y and z to be live and x to be in simple table so now I say that p4 and p5 are live at this program point and they have used in 6 so p4 and so I say they have used in 6 everyone is comfortable up to this point okay then I go back okay and I am at this program point now so I have done this program point and when I come to this program point what do I do same thing okay to attach to 5 information about the variables in the simple table p is a user variable so I cannot worry about it but p5 is now a variable which is definitely variable so I say p5 is use in 6 this is the information I attach to 5 okay and then at this point okay then immediately I go and start changing information and what do I say that set x to not live so I say p5 is not live p5 becomes dead here okay and there is nothing that I can go about right inside because it says y and z and this happens to be a user variable so I just pick out this okay then I have done this so I now come to this combination okay and what is this combination now this is saying that attach status of p1, p3 and p4 to this statement so p1 and p3 are dead and what are the status of p4 p4 says it has a use in 6 so I attach that information I say it has a use in 6 okay and how do I change my symbol table at this time I say that look at the second part I say p4 is not live okay so p4 is again going to be set dead and then p1 and p3 are live and have a use in 4 okay so p1 has now a use in 4 and p3 has a use in 4 this is the information I put in my symbol table okay so I am now done with this one okay now I come to this statement and I am again trying to attach status of each variable so p2 is the only variable compiler generated variable I don't worry about the envy and this says p2 is dead this is the purpose of I have skipped one so I have to now come to this p3 design to start p2 okay so now I do this attach information it says p2 is dead and p3 is what is the status of p3? p3 is using the information okay and then I come to this program point and what do I change? okay look at this so I say p3 is not live so I am getting dead and then I say p2 is live and has a use in 3 okay so p3 has now a use in 3 okay then I come to I am done with this program point now I attach information about p2 to the statement and I say what is p2 now so p2 has a use in 3 okay and then what do I do? after this come to the second step and say p2 is dead and A and B are user variables so I don't worry about it okay and then I come to this statement and I attach information about p1 to this so I say p1 has a use in 4 and then I come to this program I am done with this I come to this program point and now what do I do? I say p1 is dead and A is user variables so nothing is here okay so now what is my symbol table? you can see that in my symbol table all the variables are dead so I started with an assumption that at this program point all my categories are dead and when I enter again my categories are going to be dead but what it has captured is something very interesting it has captured lifetime information this is saying that p1 has a use in p4 that is the last use that means lifetime of p1 is only up to this this is where it is defined and this is where it is used beyond this point p1 has no use okay and this says that p2 is being defined here and has a use here beyond this point p2 has a no use okay similarly this says that p3 is defined here and has a use in 4 so the last use of p3 is here okay that is defined in the lifetime okay and then this says p4 is being defined here and is being used in is being defined here and is being used in 6 okay so that is where the lifetime of p4 is and here I say p5 is being defined here and is being used in 6 so lifetime of p5 is here and this says p6 is being defined here and is being used in 7 so that is where the lifetime of p7 is Now, if I say I want to bind registers or certain resources with this, for non-overlapping lifetimes, I can bind the same resources with this. So, how is this look now, that if this report I had, and if I attach now all this information, so we have already gone through this. So, let me skip this part and straight away take you to the final situation. Final situation is something like this, that even has a lifetime which starts at 1, which ends at 4. And then, T2 has a lifetime which starts here and ends here. And T3 has a lifetime which starts here and ends here. And T3 has a lifetime which starts here and ends here, which is 4 to 6. And T5's lifetime is between 5 and 6. And T6's lifetime is between 6 and 7. How many resources do I need? Do I need 6 resources now for this? So, you can see that this is non-overlapping. This is non-overlapping. I can actually do with only 2 resources. So, if I now to register a location, I know that actually I just need 2 registers. I don't really need 6 registers to do this computation. And what is it that I have actually have computed this? The computation is saying x is assigned a square plus 2 a v plus b square, x plus b whole square. Look at the computation. I have computed a square. Then I computed a to b and then I multiply that by 2. So, that is 2 a v and this is b square. So, a square plus 2 a v plus b square. That is what I was computing. And now if you see, I mean logically you can see that as soon as I compute the first one. I can store a square in a register and then I can do computation of saying that compute now 2 star a star v. Keep that in register at the 2. And I am left with one more register where I compute b square. And then whatever was the first one that will match. So, basically now I can rewrite my whole code by saying that let me reduce number of countries. So, at this point of time I am not even binding and I will say p1 is a star a. So, p1, p4 and p6 can be in the same name. So, and p2 is a star v and p2 is 2 star p2 because beyond this point p2 has no use. So, I can reuse p2 here. So, p1 and p2 are non overlapping and therefore I can use the same variable. And then I can say beyond this point p1 has no use. So, p1 can be reassigned as p1 being assigned p1 plus p2. And then I can say p2 is assigned b square. And then I can say p1 is assigned p1 plus p2. So, this gives me a square plus 2 a v plus b square and x is assigned p1. So, basically p1, p4 and p6 have to be mapped out to p1, p2, p3 and p5 have to be mapped out to p2. So, instead of 6 resources I can do with only 2 resources. And what I have done I have just computed the lifetime information of each of the variables. So, basically next to the information is saying how do I compute my lifetime information and then minimize this information. But actually we will find registers and we will see that when we do code generation, we will use this next use information and then using this next use information I will generate code where I will be using the same register again and again. Because I will say that this register has no use and therefore can be used for this computation. So, is it clear what we went through? What we went through was by doing next use information over a basic law. I have computed lifetime of each of the variables and once I have lifetime of each of the variables all I need to do is find out which are the non-overlapping lifetimes and use the same resource for that non-overlapping lifetimes. Clear? Questions? How do I find the lifetime? How do I find the lifetime? So, this is saying that p1 has a use in 4. This is giving me the last use. So, p1 is starting from here and is being used here. This is the lifetime. That beyond this point, it starts immediately after 1 and ends with just beginning of the 4. So, this line is giving you the lifetime of p1. Why does this seem to be very, very, very useful? Yes? Use twice. Use twice. So, that is why I am scanning backwards and I know what is the last use. So, suppose I had a use of p1 here. Then this I would have attached saying that p1 is used in instruction 6 also. So, that is the information that would have come. I would never have made it that. So, that is why I am scanning backwards and looking at the last times and finding out what was the definition. Just coming to say a simple activity. Just insert one instruction here which uses p1 which is the same value. You will find that when I am scanning by simple table I will say p1 and p1 will never be set to that. p1 will be set to that only at this point. So, my lifetime of p1 will be, I will say p1 has a use in say instruction 6.5. If I insert something between 6 and 5, p1 has a use in 6.5 and therefore that information will be carried out on the way. So, let us wait here today and in the next class we will continue to use this information for portability. So, our next class is now on Q stick. We do not have a class over the weekend. So, that was our next class.