 And what we started looking towards the end of previous class was that how do we represent the intermediate representations and what are the various options which are available to us. And we were looking at certain functionalities and certain properties of the intermediate representation. And the first functionality we looked at was that there is an abstraction at the source level and there is an abstraction at the target level. And we want to make sure that these two abstractions match in the process of conversion. And the fourth generation is going to be that you have a front end which is going to now translate. So when I say front end what we have to realize is we are talking about the implementation of a logical case. So implementation is that I am going to plug the type checking along with the fourth generation and it is going to be done in a syntax that is a translation manner so that we finally get intermediate code and then we will convert that into machine code. And the back end is then going to generate the machine code from the intermediate language and benefits obviously are that we will be able to re-target the whole code and the machine independent code optimization. We will talk about both code optimization. We will look at IR and we will see how to do code optimization on IR and we will also see how to write machine specifications so that we can re-target the final machine code. And this is going to be the logical model that we have a front end, we have an intermediate code generation and a machine code generator and what we have done so far is front end and then we are going to talk about the intermediate code generation. So this is going to be the overall structure of what we discuss here. So as we started discussing that how to be writes in IR and turned out that IR does not seem to have a scientific basis. I mean it has a scientific basis but it has more of an immediate problem when you say that I want to design something and what is a good design. And good design can only give you guidelines but does not have an elusive way to say that my input is this and I will get a good IR. So each compiler is going to use a set of intermediate representations and there are several intermediate representations we started looking at. So we have a high level intermediate representation or IR in talk which we will find in the picture and we can also talk about medium level intermediate representation where loop information and the error information goes away and you will reach a representation which is closer to machine but not really will be the machine, concrete machines in time. So we will see examples of each of these and then there is going to be a low level representation which is very similar to machine or very close to machine where you actually talk in terms of main pointers, you talk in terms of registers and you talk in terms of offsets and so on. So again going back to in production just to give you so that at least what we saw at that point of time that is the why that there was a move very early in the history of compiler like that you can have some kind of at least people thought that there should be some kind of intermediate representation but there is nothing like a standard that was only a thought process and people designed them languages where you could take a set of languages and translate that into a set of machines but it was not possible to really have some kind of IR which will be able to then represent all programming languages and will be able to generate code for all possible machines. That was not possible if that did not succeed. Now let's start looking at therefore what are the properties we are looking in the design of NIR, what are the properties we are looking at in the map. So first thing that happens is that why do I need NIR? I want to continuously transform IR so there is going to be some kind of intermediate code generation which is going to give you some representation and then I have an optimizer somewhere and this optimizer will start doing consumations and possibly will give me back the similar IR or the same IR and then the machine code generator is going to generate code from this and what we want this or what so there are several categories of users. One very clear is the users of the compiler and the second category is the people who write compilers like us. Now what is it that an end user is loose? End user is not concerned with the IR design. They are not bothered about what goes inside the compiler. For them compiler is just a black box. So they are only looking at two things. I should be able to do a fast compilation and I should be able to do correct compilation. Beyond that it doesn't matter what kind of representation you have. So we want only so as far as a user of compiler is concerned you are only looking at something which will be correct and fast. Beyond that it doesn't matter. But as a compiler writer what I am looking at? Are these the only things I will be interested in as a compiler writer? But I want to do something more than this. So what are the additional things I am looking at? A in my IR as a compiler writer. So do I want something which will be flexible, which will help me in representing as many languages as possible, which will help me in representing as many machines as possible? Will it also help me in doing transformations? It will be easy to transform. So the kind of things we look at as compiler writer are we want optimizations to be simple to be written and easy to understand and easy to understand. So what may happen is that suppose, so at this point of time let's not worry about what an optimization is. But what I am looking at is some kind of transformer. So if I have this transform which is looking at some IR and is giving me again IR, and this loop can go on several times. So what I want here is that continuously I will keep on changing my intermediate representation and therefore want something which will be available to these kind of transformations. If I have a representation which is very difficult to transform then that is not going to be very useful for a compiler. So that is one thing we want and obviously if I have this intermediate representation which is very difficult to understand then this phase becomes very complex. So I want to make sure that this phase does not become too complex and therefore the representation I use is also simple and straightforward. But that has an implication. It should not happen that it becomes so simple that I am not able to represent my languages and machines in this. So you have to specify the balance. So IR should be simple. It should not be too complex. It should be lightweighted but it should also allow me to do optimizations and all the transformations from IR. So you have to strike a balance there. And what we are going to do is we are going to look at several IRs and we will analyze properties of these IRs and see that what each IR is going to capture. So again continuing with the discussion on the issues which are involved into the design. Obviously IR is going to be impacted by the source language and target language. So if you have a class of languages which you may be able to design and intermediate representation another class of languages may have a very different type. So both source and target machine are going to impact it. Then another thing that is going to happen is that the cost of porting. That means if I take an IR and I say that I want to have a retarget in port language. So at some point of time when I plug in my machine port generator here this is my input and this comes out. Then I want to really do this transformation and what I am looking at is one is the porting cost. Another is that reuse of this design. I mean imagine a situation where I have an IR and I say that instead of let's say Intel XAV6 I want to port it onto MIPS and says no this IR is not suitable you have to redesign the whole IR. That means redesign of the whole IR means that my transformer will change and my front end will change, front end which is generating all this information. That means to begin with this was not something which was good because it impacted so many things. So I want to make sure that all these issues are addressed in IR design and also that this is appropriate for these transformations that remains a very important issue for us. So here is a small example. This example says it may not be usable in the current machines but historically what it is exposing is that the kind of IR design which we are going to do and how people were able to address these issues. So at some point of time there was an IR called U-Code and I will give you examples of many IRs. U-Code was based on PRIS. PRIS was an architecture which was used by HP and then it was also based on MIPS which was used by SGI at some point of time. These are two very high end servers which are available and this particular IR was suitable for stack machines. What is a stack machine? Have you read about something about stack machines? How many of you have know something about stack machines? What is that? Remember we had a reading assignment at some point of time? In February was the reading assignment and that clearly shows that we have not opened the book. Stack machine is described in the introduction chapter of Avalam safety unit. How many of you have read the first chapter of Avalam safety unit? So I can declare one question right away for the answer. The performance in Witsom is not too exciting and answers are going to have at least one question on stack machine. It is surprising that nobody has opened the book so far. Nobody has read the first chapter of Avalam safety unit? Start it on the second chapter. Start it on the second chapter. Nobody did not start on the index. What happened was that there was some kind of stack machine which was used and basically stack machines where you do not have a stack but you have a stack and all instructions are going to take their options from the stack. They are going to other than pushing it to the disk they are going to take all the options from the stack and then operate on the stack and put the stack machine. In fact stack machine was the first machine which was used for porting compilers on multiple platforms. So when the first Pascal compiler was designed it was not generating any target architecture but they had a code coming out for a stack machine and then all you needed to do was to port a Pascal compiler or a new machine just write the interpreter for the stack machine. Then it could work on many places. And writing the interpreter for stack machine is not difficult. So this is an intermediate presentation which is not very suitable for load store kind of architecture but is very suitable for stack machines. So both compilers they translate, they take U-port and before they can really operate it on their machine maybe the transformation to something else. So HP decided that they want to translate it into a very low level representation and MIPS decided that they will take this port and will translate it into some kind of MIR and then translate it back into U-port for port generation. Now which is a better approach? The first one or the second one? Second one. And why the first one is not a good approach? Very few machines will be. Very few machines will be. So we can write compilers for very few machines by this approach but this approach seems to be not more flexible. Everyone agrees with that? Observation is correct, conclusion is not. So observation is correct that using the first approach I can write compilers for very few machines. But suppose I want to write compilers only for few machines then is first approach better? Because if HP decided that our architecture line for next 10 to 15 years is going to be same, then why should I have MIR and keep on translating I will straight away go to something which is very close to my machine and I know that for next 10 years I don't have to worry about it. So by looking at this you cannot really arrive at a conclusion and you have to look at the life cycle of your compiler and see that how many times you are going to reuse parts of this compiler, how frequently your machines are chained and so on. So issue in a new IR design is going to be one question we have to address is how much of it is machine dependent? Like you saw that in case of HP this was completely machine dependent in case of MIPS it was not. So if you say that my architecture line is going to be common then maybe it's a good idea to have a machine dependent IR it really doesn't matter. Then I also want to see that how expressive this is that means how many languages I can cover how many different languages I can cover with my IR but suppose I say that my group is only going to write compiler's for let's say inner or my group is only going to write compiler's for C. Then I don't have to worry about a very general IR because in IR we can very effectively express that particular language. So I don't have to have a general purpose solution when I say that my solution should get us only to this particular subset of languages. So I'm looking for expressiveness there. I'm also making sure that this is appropriate for code optimization or for transformation. This is another requirement and it is also obviously appropriate for code generation so you can see that it should be appropriate I should be able to easily generate this. I should be able to easily transform it and I should be able to easily convert this into a machine code. That is always something useful and therefore we use more than one IR like in case of PRS we were saying that I'll start with U-Code and we'll translate it to machine code and this is what they would do like their frontend so in their frontend they had this U-Code which was then converted into some low-level intermediate representation and the optimization was straight away pushed into this. So this is the kind of architecture which was used. So continuing on this discussion more than one IR for more than one optimization is possible. Now why do I need more than one IR for different optimizations? Why one IR may not be sufficient for an optimization? Sleeping will not mean sleeping in the afternoon. What comes with that? 9 o'clock is too early. That is Thursday and Friday. So no response. Today is afternoon. When do we start thinking? So why do I need more than one IR for different optimizations? So suppose I say I am only doing what let's say time. Only one kind of optimizations. Do I still need more than one IR? I am not talking about data spectrum. I am not talking about even implementation. Implementation will come much later. We are just talking about structure of IR. So let me give you an example. Let's skip this. So here is an example. So I have this instruction and I have this declaration and I have an error here. And now so I have an HIR. HIR is of the form which says T1 is a signed AI J plus 2. And I have an MIR which says that J plus 2 is signed T1 and then I multiply that by 20 and then I add T1 and so I multiply I by 20 and I multiply T3 by 4 add some base address to this and write this kind of code. Now what are the things I can do here which I cannot do here? Are there some transformations or some analysis which is possible only on the first one and not on the second one? So again let me write a small piece of code. So suppose I write this piece of code which says I is going from 1 to N I plus plus and then I have AI being a signed something and I have now AJ being read here. Suppose this is a kind of code. And the question I want to ask is for some value of I or let me write let's say some function of I and another function of I doesn't matter 2 I plus 3 and 3 I plus 1 something. Now I want to ask a question which says that for what value of I in some iteration value of F I will be same as value of G I. This is a question I want to answer. This may be a relevant question for some optimizations. Now suppose I use this intermediate representation. Will I be able to answer that question or will I be easily able to answer that question? Or which representation will be better this or this to answer this question for what value of I in some iteration F I will be equal to G I. First one. First one right. So suppose I am doing optimizations where I need to do lot of analysis on the indices then I don't want to lose this information. Now what is this doing? What is this code doing statement? It is finding out the memory location where A I J plus 2 is stored and what it does is it is taking J plus 2 and it is assuming that dimension of I is 20 and it is now counting number of rows and taking basically the offset from the base and this 4 is assuming that 4 bytes are being taken by each element. So this gives me total offset from the base of A and that gives me the address and then I dereference that and take this value of A I J plus 2. Now suppose I want to do optimizations here which are at the level of statement and saying that I want to do a register allocation then obviously this will be much better as compared to this. So depending upon the optimizations I want to do and the kind of questions I want to ask my representation is going to be different. So even when I am doing optimizations you may find that I am able to do optimizations on one piece of I R and I may be able to do completely different optimization on another I R and there is yet another I R which I am calling now low level and if you see here what it has done is it now says that J is some value and J must be stored somewhere and suppose this is stored at an offset of minus 4 from the frame pointer. So this is saying that I want value of J from this location and then I is stored at frame pointer minus 8 so I take this value multiply that by 20 and so on. So this is very close to the machine. Now if I am doing only code optimization now you can see that this has actually exposed all the addresses to me. So if I want to do now optimizations at machine level then this is going to be very useful I R as compared to both this and this. Because all the addresses are available to me in this case you can see that they are not really available to me. All I am looking at are certain mnemonics. So important point to remember is that there is nothing like some best I R and I may need to have multiple I R for kind of optimizations I am trying to do. So this represents a subscript by list of subscript. So the H I R there is something missing here. So H I R represents subscripts which is suitable for the kind of analysis which I just talked about which is called dependence analysis and low level I R is something which may be making all the addresses very explicit and some kind of optimizations are very useful there. So when we want to do constant folding, stem production, looping, very important motion and so on. Then this is the kind of I R which is going to be more useful. So we will talk about these code optimizations towards the end of the year. So these are Sorry? What is the disadvantage? No disadvantage. Why do you say disadvantage? So the context of the question is not clear. I can have five let's say five intermediate I R. Yeah sure. But I will have just 4 writing number will be greater but my optimizations will be better. That is what is the normal. We just don't have one I R. We have multiple I R's. But I don't want to have arbitrary number of I R's. So the way I look at the problem is that first I decide what is it that I want to do and then I decide what is the suitable I R for the kind of functionality I want to achieve. So I don't want to fix a number of I R is saying that I will have only one I R or I will have five I R's. So depending upon the kind of things I am trying to do I may have many I R's. So I don't want to pack that to number one, two, three or five I R's. Right. So this point already has been brought out. So let's look at now various high level representations. So let's take this kind of code and this is something which you have frequently encountered. So what I have is now a function F which is returning type integer. It takes two arguments A and B. Both are of type integer and then it has a local variable of type integer. So there is an expression that involves this argument and local variable is being assigned this value and that is just printing B and C. Now what are the various I R's we have seen abstract syntax tree is definitely one kind of representation and this is going to keep enough information so that I can reconstruct really the source. So if I look at abstract syntax tree, if I unpass the abstract syntax tree I can get something which is very close to the source. So many times when you are trying to write DSM-less then this kind of I R may be something very good. Now when you write DSM-less so if you are doing for example source and source transformation you say that I want to convert my C programs into faster and I want to convert C++ programs into so on and so on. That time you don't want to go all the way to something which is very low level I R and then try to reconstruct something because it may not be possible to reconstruct. So I try to keep my I R at much higher level which is close to abstract syntax tree and all the information I need to put whatever is required for this conversion in a single table. So it may look something like this I will say that I have a function and the identifier is F here then I have a list of parameter then I have a body and parameter list may be given again by a recursive definition which says that first argument is A and again a parameter list another argument is B end of it and then I have body then within body I have declaration and statement and so on. So you can see that in this I R I will be able to reconstruct almost something which is close to the source. If I take it to something like M I R or L I R I may not be able to come back to what the source was. So this is something which is very useful representation I will have to change all these colors for some reason today this collector is behaving so even right side is getting cut and these colors are not coming out properly I will change it. So in this case what happens is that all this information about the identifier is going to be in the single table and wherever you see this identifier basically all this is saying is that I just want to have here a link to the symbol table and get all the information about the identifier from the single table. Medium level I R is going to then have features of source language so again what you remember what we saw in the medium I R when I was trying to translate this particular expression that I really went for not actual addresses of I N G but some symbolic name for I N G and I also recognize the fact that this is of type maybe in the float and I know that there was additional thing I need and what was that the two dimension array which is being mapped on to a single dimension now what is the order in which I am going to store so suppose this is a I have it has certain rows and columns then it knows that how do I map this two dimension into a single dimension so what is the map what was the mapping I was using here no no order mapping or no major map mapping so I was going in this order so this is something I recognized that my language support this I also recognize the fact that each cell was taking 4 bytes now depending upon the language it is possible that instead of row major I may have column major mapping so for example if you have a language like 4 pen it has a column column major again it also had information like saying that floating point was taking 4 bytes but on some other machine floating point could have been taken 8 bytes so my I R had this information but did not have really the machine information but it had something about implementation which HIR will never have so if I go back to this it has no information whatsoever about the machine or what the compiler was this is something about abstract syntax so you can see that multiple I R are having multiple information and they are exposing different information at different places so normally what we have at MIR is what we know as 3 address code now this is something which is good for optimization so large number of optimization algorithms have been written for this I R and we will again see that and this is also good for code generation for a large number of party vectors so typically what is a 3 address code and this is something we will be working with as far as our intermediate code generation is concerned 3 address code says that every statement will have at most 3 addresses that means I cannot use more than 3 arguments and right hand side of the statement will have only one operator at most one of it not more than that and low level I R is going to correspond so we will look at examples of this and low level I R is going to correspond to more than one target architecture and multi level I R sometimes I want to mix all these so there is nothing sacrosanct about saying what my I R is whether it should be H I R or M I R when there is no reason to say that I will keep loop in arrays at H I R level but all other statements will get translated into M I R that is also possible so I may have a hybrid of all these so important thing and important take away from this is first you try to define what your functionality is and then decide what the I R should be that I will have an I R and then I will say that let me now so that it suits this particular I R these two things cannot happen nice position so abstract syntax tree is one I R and this is condensed form of parse tree and useful for representing language construct and this also gives you a very natural order of hierarchy so if you remember that function tree I showed you that is very clearly telling you in which order arguments are appearing and in which order your statements are appearing so it has a nice structure and internal nodes correspond to operators and the children nodes are going to be offerings and so on and leaf nodes are going to obviously to the office so there is one kind of I R and DAG is again a more compressed form of abstract syntax tree in that we have been seen how to give the DAGs so here is an abstract syntax tree for this and here is a DAG for the same expression okay so what you have to remember here is that I have an abstract syntax tree here where the root is at the assign which is this operator and the left hand side is the left hand side of this and right hand side is the expression corresponding to this which says it is an addition of two terms and each term is B multiplication of minus and similarly this part and when it comes to this part what we have is that we know that since this part of the tree has been replicated here rather than replicating it I have one tree and then I am just having the edges going from here so this is one kind of intermediate representation and another common representation is what we know as post tricks representation what happens in post tricks representation if I just go post order traversal of the syntax tree that is going to give me a post fix okay now why post fix may be useful what are the situations in which a post fix kind of IR may be useful stack based machine so if you have stack based evaluation then having a post fix makes not of sense I mean there is no point in having a tree because once you convert it into post fix all you are saying is that all your operators will be on the stack and whenever you get an operator your operator will be on the top of stack top them out do the operation and push the result back so then post fix is going to be very useful so you will have just the list of nodes of the tree and nodes are going to appear immediately after it is chosen so if I now say that I want to convert an expression e into post fix how will I do that okay if it is just available then what is the post fix notation the variable exactly but suppose I say that I have e which is of the form e1 of e2 what will be post fix of this what will be post fix of e1 e2 e1 e2 is this correct yes no recursive definitions that e1 also has to be converted into its own post fix how do you know that e1 is not to be converted into post fix so if I write something like this will you get a correct translation no so then it has to be e1 prime and e2 prime because these are small things you have to remember I mean just you never know what e1 and e2 are these are just expressions so even those expressions need to be converted into post fix so if e is an expression of this form it says e1 or e2 then post fix of this is going to be e1 prime and e2 prime which are post fix of e1 and e2 and this is e1 prime if e1 is an expression of form which is bracketed expression then what will be post fix of this if this is of this form if I say what will be post fix of this why do I use brackets brackets are just to make sure that I am applied right this is different than right associated if you have a post fix you still need to have brackets no right so I can throw your brackets so what will be post fix of this bracketed expression e just post fix of it right so that is going to give me just okay so continuing on post fix notation you do not require any parenthesis because post fix notation itself is going to capture all the associated with the presidents okay and post fix notation therefore for this kind of expression is going to be where I will say that even assignment is an operation okay and then you can see that this becomes very suitable for stat machine evaluation so what I am going to do here is that I am going to push a I am going to push b then I am going to push c and then I will say now I have a unary negation and therefore pop this value and negate the value and then push the value on the stat and then I will say start start will now say take b and minus c and do a multiplication and so there is a okay so by the time I finish this the value will be stored here so very very suitable for stat type okay so 3 address code is and this is going to be something which we are going to use for all the examples we have because there is something which is close to most of the machine architecture okay and 3 address code is where all my instructions are going to be of the form x is sign y of z which is the most general case everything else will be a subset of this and what that means is that I will have at most 3 addresses on the right hand side I will have at most at most I can have fewer okay so x, y and z are some names so these need not be variable these are either variables which are written in the program or these could be internally interpreted by the compiler okay I can have temporary names there and they could also be constants okay and obstants for any operator in this case this is a binary operator but suppose I have a unary operator then what kind of expression I will be writing I will just say x is a sign minus y if I have no operator then I will just say if I just have a copy instruction then it could be just x in the sign y okay so I can have 3 address code of this form where only one operator on the right hand side and if I now say that I want to generate 3 address code for an expression which is x plus y star z what kind of 3 address code I will have for this so if I want to say x what is the 3 address code I am going to write for this t is equal to t is equal to a sign y star z and r is equal to x plus okay so normally what we will do is that we will use only t and I will subscript it with different numbers to say these are all temporary which have been generated internally by the compiler these are not user variables and therefore I may use something which is little more informative but really what you have pointed out is correct that is really the 3 address code that is say t1 is y star z and t2 is x plus t1 and so on so I will write this kind of code okay and t1 and t2 are compiler generated temporary names now immediately at this point of time what should bring a value is that when I say that I am now trying to convert an expression like this into a 3 address code okay you can really see that what I am doing here okay at machine level so this is not really machine code but at machine level what is it that I am doing what kind of code I will end up generating for t1 being assigned by star z at machine level so I will say load y load z multiply the 2 and store value in register r1 okay and then I will say load x and add r1 and whatever location I loaded x and store that into r2 okay now also what I could have done if you see from the point of view of optimization if I find that none of them are in registers then I will say load y into r1 load z into r2 multiply the 2 and store result into r1 okay and then r1 already has t1 then I will say load x into r2 add the 2 and store result into r1 so with 2 register I will be able to do all the computation okay so if I can remember all this information and this this will get exposed when we really do machine code generation from the 3 address code that how I am going to manage my registers okay so 3 address code you can see is something which is very close to most of the machine instructions that kind of load store architectures which have registers okay so this also takes all the complex expressions like so I will not call it very easy okay this straightforward expression but if you can see that even if you have a complex arithmetic expression okay then I am going to almost take this and convert that into a sequence of 3 address codes okay so if you recall I took this and converted this into a sequence of 3 address codes so what did I do I took the first index I took the second one added 2 to that to the row row width multiply that to the first one and found out 2 then multiplied the whole thing by 4 with the base address of A added it that really gave me the full computation of A I gave plus 2 right so this way I can expose all the address information and the use of name immediately allows it to be really easily re-arranged so for example if I had so take an xx syntax tree for this xx syntax tree for this will look something like this right now suppose I want to re-arrange this okay I want to re-order this okay here is one computation here is another computation now suppose there was a situation where I say that I want to switch order of computation here so take a situation like this where I say my xx syntax tree is and now I say that normally I am going to go from left to right and therefore I will say that this is going to be evaluated first then I will evaluate this okay now suppose I did not have any side effects in evaluation of A, B, C and these are straightforward way I am just loading this information does it matter in which order I compute this whether I compute this or I compute this does it really matter no okay but now suppose I want to capture this information here and say that now instead of this computation do this computation what will I have to do on the tree I have to do a transformation and that transformation on trees can be very complex depending on the kind of operator you have as opposed to that imagine that I have a 3 address code now how will 3 address code look so 3 address code may look something like this p1 is assigned A plus B and p2 is assigned C plus B and then I may say p3 is assigned p1 plus p2 okay and now I want to do the same transformation what will I do I will just re-order that just take it here okay and I am done okay so this structural transformation is going to be lot easier on 3 address code as compared to aspects of trees okay so use of names and intermediate values this allows 3 address code to be easily rearrange basically if you see t1 and t2 here t1 and t2 really correspond to nodes of these sub trees so rather than using this kind of tree if I start using names explicitly then this rearrangement becomes easy code this is point clear because all these are going to be required at the time of code generation and code optimization always so 3 address code is nothing but if I just linearize this and say that whenever I am linearizing for every node or every sub tree I introduce a temporary name then I am done so in fact another way to write 3 address code may be that I just linearize it and instead of using temporary names I start using the memory addresses so I can say that I am going to now put code into this form which will say that I will have plus ab and this instruction is going to be some address a1 okay and then I can also write something like saying that I have plus c and d and this is address a2 and then I will say that address a3 is nothing but plus a1 and a2 so this is another notation which is commonly used and therefore what may happen is that these are nothing but really pointless here so instead of using temporary names I can also use addresses so various kind of representations are going to be used here so 3 address code is nothing but some kind of linearized representation of abstract syntax tree where for each sub node an explicit name or an explicit address rather than just keeping some kind of tree structure okay so continuing on this 3 address instructions I may have a set of assignments so assignments may look something like x being assigned y or z or x may be assigned y or x may be assigned y so this really captures all possible combinations of binary operators, unary operators and right and I can also have jumps which are either unconditional jump which says jump to L okay so what is address here so I have an address in go to L like we said that I can have an address here which is x and y in z x and y in x and y what is the address here in go to L address of the address of the instruction what does that mean address of the instruction so is label an address go to L L is a label is label an address yes no if you have written assembly language program you know when you say jump to a label what it means is that change your PC value form whatever is the current PC value do the new PC value we have the instruction with label L is stored right so L is an address here and this says if x rel of y and this is some relational operation so x and y are R2 operators like jump to L to the conditional jump so this says that if this is true then jump to this okay and if it is false then what do I do can have a statement which says if x of x rel of y go to L1 else go to L2 can I have that kind of instruction instruction so that will give me 4 addresses which are not permitted in 3 addresses okay so I will use a fall through mechanism therefore that if this fails I will just fall through and execute the next system okay and similarly I may have for indexing so I may say x is signed y i and x i may be signed y okay can I write x being assigned y i plus okay so what are the 3 addresses here in this x y and i so x and y are the base addresses of or x is address of x y is the base address and y is the offset so 3 addresses I have okay and what is the operator on the right-hand side do I have an operator here so operator here is I am just saying base address plus offset that is the operator okay so similarly in this case I have 3 addresses but operator is on the left-hand side in this case okay similarly I can have for argument passing I may have saying that I can have a parameter and then I can have a list of parameters and then I say now jump to procedure 3 and parameters and this is return control 2 from wherever I take okay or return control 2 label y okay or I can have pointers I can say that I can dereference pointer I can take a variable take its pointers and so on okay so you can see that with this kind of intermediate representation I can generate code for intermediate code for a large class of languages looks fairly small and straightforward but you can see that this itself can be converted into something which is closer to machine code for a large class of languages what we need to do now is we need to start from program and program is going to have several parts now program is going to have declarations I need to process those declarations and put information in the symbol table and it is going to have loops and it is going to have conditionals and it is going to have straight line code I need to convert all that into 3 address code and that is going to have functions and procedures all my functions and procedures into a sequence of instructions like this plus management of my stack and P okay so what we are going to do in the next classes we will see that how do I start taking parts of the code or parts of my program and start converting that into either symbol table or a code of run time system okay so that we will start in the next class let us take a break here today