So, this is a loop which just adds two vectors puts the result in one of the vectors right and this is the MIPS translation. So, 0 dollar a 0 is the base of x and base of y is 400 dollar a 0 ok. So, these two instructions loads the values of x i and y i into two respective registers. This is the loop iterator incremented by one and this instruction adds these two puts the result in these two. So, this one could not be put here because of the load delay slot all right we have to be delayed by one cycle. Then this one stores the value back to x i same as this address and this one compares dollar a 1 against 100. If it is already there then it sets dollar v 0 to 1 this one checks if v 0 is already 1 if not goes back and executes it and this is the branch delay slot which is always executed which increments the address by 4 because these are integer arrays all right to take you to the next element of the array. So, now the question is that with our model of any question on this translation is it clear. So, with our model of execution we are fetching at a certain rate all right and the execution model is that whenever an instruction is ready to execute I will execute it and I also leave the constraint that in a particular cycle you can only execute one instruction. You can execute unlimited number of instructions in a cycle. So, the question is what is the minimum number of cycles required to complete let us say one iteration. So, what will you do in the first cycle which instructions you can execute you can send these two for execution because they are independent you can also send this one for execution you can also send this one, but there is a small problem if you send this one this one violates what kind of hazard is this between this and this these two instructions right after read right. So, we violate a right after read dependence if we actually. So, essentially by executing these I essentially what I am doing is I am reordering this instruction ahead of this one I am saying that this this this and this. So, this instruction will be put here essentially all right and that would violate this particular dependence all right. How can I fix it any simple solution I still want to execute this one in the first cycle there is a slight problem because this one is my sort of it is connected to the loop iterator. So, how do you really translate then ok. So, I you are saying that I change the target to something else what do I do in the next iteration I cannot use a 0 anymore right. So, my translation breaks there is a very simple solution what can I do can I change this to minus 4 this offset it will have the same effect right I increment it first and then I put it put a minus 4 here instead of 0 right. So, that will allow me to execute these four instructions in the first cycle is it clear what about the second cycle what can I execute. So, if you want me to show you the pipeline. So, this is the first load second load add i u and add i u ok. So, if I have a 5 stage pipe. So, what will what I will do in the second cycle. So, my second cycle fetch will be here what are the instructions that are eligible that will be ready I know that that will be ready what about this one will it be ready there will be the delay slot of the loads they cannot issue this one of course, not because this one this one depends on this one this one no this one also depends on this one this one no this one depends on this one. So, I have to put a stall cycle there is no other option actually ok. So, after one cycle stall what can I do. So, now I can probably send this one why because this one will pick up the value from the bypass right ok. So, this is add u what else can I send this one no because this one actually stores the value that I am computing in this cycle and everything else. Oh wait no no no ok sorry let me back track a little bit no why do not I send this instruction in the in the in this cycle this one is just comparing dollar a 1 against 100 and dollar a 1 is being computed here. So, as such if I fetch it here execute it here it will actually pick up from the bypass what is the problem there what is it called right after. So, there are two hazards that are being violated here one is right after read another one is right after write right same problem. So, dollar v 0 is written here and read here and I am actually if I if I boost it this instruction before it I would be violating both the both the dependencies right exactly yes this one also has a source dollar v 0. But this is somewhat unfortunate that is very important that is that is the important observation that you should make because as such these two instructions have nothing to do with this one absolutely nothing this is a completely independent operation it is just comparing dollar a 1 against 100 it does not use any results from these two instructions. So, how what is what could be a solution sorry use some other register except v 0 right. So, use some other register except anything used here so that you would not violate any dependence right. So, these are often called name dependencies these are not true dependencies they are actually false dependencies they arise only because compiler happen to choose this register for allocating the result of this operation all right. So, we will come up with systematic solutions to get rid of name dependencies the point is that name dependencies are false dependencies and they should never hamper your ILP all right. So, let us for now assume that this is actually some other register all right. So, I should be allowed to issue it here right without any problem. So, this is SLTI all right I do not think I can issue anything else in this cycle. So, next cycle can right and can send the branch also. So, add you and this one will pick up the values from the bypass b and z this one will pick up the value from the bypass. So, what are you left with actually I could all right we are left with only this one right everything else is done I believe 1 2 3 4 5 6 7 8 yes. So, only left with the store which will now go into the branch delay slot all right is that clear. So, that is pretty much the best we can do even if I give you the freedom of executing unlimited number of instructions every cycle. So, question is how can we go beyond this can we or is it the theoretical limit what do you think given these particular loop which executes 100 times we have looked at just one iteration can we improve this any further. So, any question on this anybody has any question on this particular schedule ok yeah somebody was saying something and why why can you do that exactly right very good. So, loop iterations are independent in this case two different iterations compute on different data. So, we should be able to execute instructions from different iterations in parallel the only question is how to really systematically do that because the biggest problem is the branches. So, although here you can clearly see that this branch will be taken 99 times or 100 times and the last one will be not taken the question is how does the hardware figure out at run time that this is going to be the case the hardware hits this particular branch and has to figure out should I go down or should I actually go back and execute this one sorry right exactly. So, but the predictor will actually help you, but it is not the it is not really an oracle it will not give you the correct answer all the time. So, the point is that branches introduce a problem that one has to recognize it reduces the stretch of independent instructions what it introduces is control dependence there is no data dependence as such, but all it is doing is it is putting bunch of instructions that that become dependent on a certain branch solution once somebody has already proposed that have branch predictors, but once in a while they will make mistakes. So, you have to do corrective measures the second solution is unroll the loop. So, what if I change the loop to something like this you go from 0 to 99 at a step of 2 and within an iteration you actually do two things. So, instead of having 100 branch instructions you now have 50 branch instructions right. So, that definitely helps is there a downside of doing this. So, does everybody see that it will now help because now I will have four load instructions which are completely independent I have a lot of freedom in selecting my instructions is there a downside of this. So, I will let you see this one. So, imagine that this is just doubled everything will have now every instructions will get replicated every instruction will get replicated size of size of code yes that is one problem your size of code will double if you have if you. So, by the way. So, this is called a loop with an unroll factor of 2 unroll twice if you move in a step of 4 and have 4 such things the unroll factor will be 4 and so on and so forth. You can have an arbitrary unroll factor if the unroll factor does not divide your loop trip count you will have to have some code at the end of the loop to fix up that you know to execute the residual iterations yes. So, as she has suggested this will definitely inflate your code size. So, that is a big problem. So, when you actually duplicate these instructions to be able to use them to be able to execute them in parallel they must have different targets they cannot use the same target clearly. So, that puts more pressure on your register. So, often you will see that when you start unrolling more your false dependencies will start showing up more and more because the compiler is actually running out of registers. So, it is trying to reuse same register over and over. So, that is one problem and in fact beyond a certain unroll factor the compiler will have no register to use. So, essentially what will do is it will take register values and store them to memory and use that register for doing some computation and when that value is needed it will load it back from memory to some register. So, that increases your code size even further and what is worse it will actually increase your number of memory operations. You will have now extra memory operations which are not because of the code it is because of shortage in registers. But in general if you can choose your unroll factor wisely it gives you it exposes more ILP especially in this type of loops where iterations are independent. So, essentially so this is just one technique. So, loop unrolling that is also called a static technique which is compiler driven because of the compiler will actually do this transformation. However, of course not all loops are like this they are not so well behaved where iterations are independent. So, for those you require more sophisticated solutions and as I told you last time if I get time I will get into this otherwise not. So, I will just name a few things. So, this is this stands for very long instruction word computers. So, this is again a compiler technique where compiler analyzes your code and prepares long instruction packets. These are essentially combination of multiple instructions. So, in a VLIW machine one instruction would be for example a combination of four instructions that will go in parallel. So, that is how the compiler will prepare instruction packets and that is where the name comes from these are very long instruction word computers. Epic is very similar it stands for explicitly parallel instruction computers. So, here you do the pretty much the same thing the compiler prepares this explicit parallel instruction packets. The only thing only difference between this and this is that in VLIW the packet length is usually constant that is at the design time you decide that one instruction packet will have four instructions one long instruction packet will have four instructions. Which essentially means that if in a particular slot compiler cannot find four parallel instructions it will put no ops in the empty slots to make sure that every slot is four instructions long. Like for example here four long VLIW would be happy with this packet, but will have three no ops in this packet will have only SLTI and three no ops. Similarly, in this packet it will have two instructions and two no ops and so on and so forth. Epic allows you to terminate instruction packets early. So, epic will have a variable size instruction packet. Software pipelining and predication these are again two techniques to expose more ILP. Predication is very much related to branches this is not prediction this is slightly different this is predication essentially the idea is that you convert control dependence to data dependence. The idea is that you evaluate this expression put it into a predicate register and you tag all these instructions here with predicate register value true and tag all these with predicate register value false. Now essentially what I was saying is that there is no control dependence. All these instructions depend on this predicate register. Similarly, all these instructions depend on this predicate register. These instructions will finally survive if the predicate register value turns out to be true and these instructions will survive if the predicate register value turns out to be false. We are converting our control dependence to a data dependence. So, it will essentially look like exactly same predicate registers to be bypassed to your, you know, required instructions and so on and so forth. The only thing is that the extra thing that will require is you will execute all these instructions, some of them will get cancelled later when the predicate register value becomes available. So, again as I told you, if I have time I will get into this, but this really belongs to a compiler course. So, this is not really priority here. So, what are the dynamic techniques? So, that is these are essentially hardware techniques. So, as I have already hinted, one important thing is registered renaming, where you try to remove name dependencies that we have already seen. Branch prediction, we have talked about it at length. So, I will not get into this anymore. Out of order issue, we will talk about that. So, I have already shown you some flavor of this here. What is happening is that I am reordering instructions. Multiple issue, that is also shown there. I am issuing multiple instructions every cycle and some more advanced techniques for exposing more ILP. So, this is going to be our agent essentially. This is what we will focus on. And you might ask, well, will I ever get an effect of loop unrolling in this particular loop? The answer is yes. Can somebody see how is that possible with dynamic techniques? That the amount of ILP that is exposed here will also get exposed in dynamic techniques. How is that? But the code that the machine will see is exactly this. What will happen exactly? So, I keep on fetching. When I hit this branch, I ask my predictor. So, in this case, the predictor will be fairly accurate. You will say that, go back. So, you continue fetching. So, now essentially what will happen is that at any point in time, your processor, the instruction pool is going to have a large number of iterations already fetched. So, what matters now is that the hardware has to pick up the independent instructions. So, automatically it will pick up these two loads from one iteration and some other two loads from the other iteration and execute them in parallel. So, it is going to have the same effect, but there is one condition here. That is, the predictor has to be accurate. The predictor tells you something wrong. Your instruction pool will get populated with useless instructions. After that, whatever you do, it does not matter. You are doing essentially wrong things. So, eventually your branch will resolve and you will get to know that your predictor said something wrong. You have to cancel everything, flush everything from an instruction pool and start over again. So, you are going to get the same effect pretty much depending on how good your predictor is. The compiler removes that particular condition by analyzing the code and telling you that you know this code can be unrolled and you can get good ILP from that. So, to summarize what limits ILP, just whatever we have discussed here, one is data dependence or true dependence. That was the reason why we could not execute more than one in this particular cycle, because whatever we had here were dependent on something that were executing. So, this is often the primary concern, because these are the dependencies that you have to obey, because these are needed for correctness. You cannot really violate any of these dependencies. So, this is essentially this concerns flow of data, dependence between a producer instruction and a consumer instruction and it may introduce read after write hazard and stalls. So, I say that it may, because the reason is that dependence is a property of the program. Stall is caused by pipeline organization. How many cycle you will stall will depend on, for example, how deep my pipe is. Certain dependencies may not cause any stall, simply because the distance between them is large enough, so that I can actually issue the consumer without any stall at all. So, I mean you can see that here. So, I could separate this particular instruction from this one, so that there was no stall. So, that just like that. So, is this point clear to everybody? The dependencies may not always cause a stall. That is very important to understand actually. And flow can happen through register or memory. So, in this particular example, we are only seeing flow through registers. For example, the value flows from this instruction to this instruction through dollar v 0. Value flows from this instruction to this instruction through dollar v 1 and so on and so forth. But you can have data flow through memory also. How is that? Can somebody give an example? Exactly. So, you store something to memory and later read that value from memory through a load instruction. That establishes a memory dependence, a dependence through memory. So, the question is how to discover memory dependence? So, let me show you why this is not as easy as register dependence. So, how do you discover register dependence? Exactly. So, you just compare the registers, the source of one instruction and target of another instruction. If they match, we know that there is a dependence. Here, this is not so easy. So, let us take a look at two instructions that are in a dependence. So, let us say, as he has mentioned, we have a store which let us say, stores register R 2 to some address 100 R 3.  Turns out that these two are actually same address. It can happen. The problem is by looking at these two instructions, there is no way to know that actually they are in a dependence. Is that clear to everybody? It is just that the values of R 3 and R 6 are such that these two addresses turn out to be same. And there is nothing that stops the compiler from generating this code. In fact, compiler does generate this code many, many times. Given the availability of registers, when it is generating this particular load instruction, it may not use R 3 again. Because R 3 may have been allocated to some other variable by then. So, the question is, how do you really discover them? That two instructions are in dependence because I may make a mistake. I may think that, well, I am probably, it is probably safe to send these two instructions together or even worse, I can execute these instructions before this story then, which may end up this load getting a wrong value. So, how do I discover this? What is the simplest solution? Reorder, what is that? Reorder. What is that? No, I am not, no, no, let us not get there. You may be knowing what the reorder buffer is, but I am not really asking for a solution. What I am asking is, give me a naive way of figuring out that these are dependent. What you are saying is that, well, I can actually go ahead and execute the load before the store, but eventually I will be able to figure out. That is what you are saying. Well, so, we will get to that solution. What I am saying? How does normally, how do you really, even your case, suppose let us take that case. You say that the load, I have executed this load already. When the store executes, I figure out that there is a problem. How do I figure this out? The final value. So, the point is that, before these instructions can go and access memory, the address will have to be computed. So, when that is done, I am ready to figure out whether it is in a memory dependence or not. The point is that I have to wait till then, whereas in case of register dependence, even the decode time, I know which are the instructions that are dependent on each other, but here I have to wait until the address gets computed. Then only I will get to know whether this instruction depends on this one. So, that makes things a lot complicated, because which means my instruction pool, which is having instructions, which are already decoded. So, my job was just to go through this pool and pick up independent instructions for execution. It seems that that is not sufficient anymore, because of this memory dependence, because from the decoded instructions, I cannot figure it out anymore. I have to actually partially execute these instructions to compute the addresses and then only I will get to know. So, that poses a lot of problem that clearly hinders your ILP, because essentially it says that if you do not have any other machinery, you have to be conservative. Whenever you find a load, you say that, well, I cannot really execute it until all stores before it have been executed. So, that is correct. That will guarantee correctness, but that will make sure that you will not get as much ILP as you would have got if you could actually execute this load earlier. So, we will come up with more sophisticated solution, which can actually get rid of this problem, but is this problem clear to everybody, this particular one, memory dependence problem. Any question on this? Is it clear? So, how do you really go around data or true dependence? Well, you try to schedule as many independent instructions as possible and that is what we have been trying to do there and we will see dynamic techniques to do that. The second type of limiter is name dependence. These are also called false dependence, as we said, because there is no data flow between involved pair of instructions. So, there are two types of false dependence. One is called anti dependence, which may cause right after read hazard. So, this is just opposite of flow. That is why it is called anti dependence. So, we can look at these two actually. So, these two instructions are in an anti dependence. So, this one is reading from dollar v 0, it is writing to dollar v 0. There is no as such data dependence between them. It is a totally false dependence, happened only because compiler ended up choosing dollar v 0 as the target of this instruction. And these are anti dependence, because there is a read before a write. Just an opposite of the flow dependence. Does anybody see any other anti dependence in this code? There are more pair of instructions in anti dependence. Now, order 4th and 6th, 4th and 6th. Anything else? First and last? So, dollar a 0 written here, read here. So, the other type of false dependence is called output dependence, which may cause right after write hazard. So, these are essentially dependences when you are trying to write to the same register. For example, these two instructions are in output dependence. And this again happens unfortunately, because the compiler chose v 0 as the target of this instruction. So, dynamic techniques are there to get rid of these as he has suggested. Just rename them, use some other register. So, there has to be systematic way of renaming. So, that because you can see that if I change this one, this register to something else, my iteration flow will break immediately, because next iteration will have a problem then. So, you have to be very systematic in renaming these registers. So, we will talk about algorithms to do that. So, solution for both is renaming. And again, you have to answer this question that is you may have false dependence in involving memory operations also. How is that possible? Can I have an anti dependence involving two memory instructions? I can have right. I can switch these two pairs and there will be an anti dependence. Or I can introduce another store and if it turns out that these two address the same, these two instructions are in an output dependence. So, you have to also do memory renaming to get rid of these dependent instructions. So, we will answer all these questions. Any question? And the third thing that limits ILP is control dependence, which we have already discussed in great detail. Simply because data flow alone is not sufficient for program correctness. Control flow must also be preserved, because that decides along which path you execute. And the solution to that is branch prediction, which we have discussed. And what if you go wrong? We will discuss that. We have not discussed that in much detail. We will have to have systematic ways of recovering from mis predictions. Because now, what may happen is that in this case, although in this particular case it may not show up, but when you have two iterations clubbed together, what may happen is that a large number of instructions might have been issued on the wrong path, when the branch finally results. And this will especially happen if you have a very long pipe. So, there has to be a systematic way of removing these wrong instructions from the pipeline. And fixing certain other things, which are related, for example, you will now start renaming registers and all. That will have to be also fixed on the wrong path, which whatever you have renamed. So, we will see all these problems very soon. So, this one is not easy. That is the whole point. It is not as easy as we have seen in a single instruction fetch executed linear pipe. It is much more involved. And it gets complicated because of various types of renaming that we will do. So, any question? So, essentially our goal would be to. So, ultimately, the ultimate limiter is essentially this one. Assuming that you have an oracle to decide your control dependence. This is false. You have unlimited number of registers to get rid of all name dependence. So, then the ultimate limiter is going to be this one only, which will actually limit your IMP. Everything else will go away. And that is what the goal of a processor is. That you know, a designer would like to live only with this and get rid of everything else. So, the goal is to design a good branch predictor. And the goal is to design good mechanisms to do renaming. So, we will focus on these two things. Branch prediction is already talked about. So, I would not go to get into that. Questions? Sure. So, that is the limiter. And the source is flow dependence. No, that is what I am saying. So, if you remove all these. Suppose, you have an oracle predictor here, which is 100 percent accurate. And you have enough registers to have no false dependence. Then what are you left with? Flow dependence. And all stalls in your pipeline will come from only this. Nothing else. So, that is what you are saying probably. So, that is what I am also saying. That this is the final limiter, which you have to live with. Because this actually guarantees correctness of your program. So, but we can even remove here, try with stall. How? We have a pipeline. By making the pipeline shallower, you are saying. Yes. Yes, of course. Sure, you can do that. Yes, yes. But there are other trade-offs involved there. As you make the pipe shallower, you may lose frequency. So, that will have to be looked into more carefully. But what I am saying is that ultimately, this is what will limit your IMP. So, any question on this? So, of course, we will also talk about techniques. So, this is often called the data flow limit of a processor. So, we will talk about techniques, which actually try to go beyond that limit. Can anybody guess what it might be? So, I tell you that I have an oracle branch predictor, 100 percent accurate. I have enough registers, so that I have no false dependence. So, what else can I do to go beyond the data flow limit? So, data flow limit is essentially this. The reason why I could not issue this instruction in this cycle, this cycle, that was because of a data flow limit, because the value would not be available on time, which is why I have to delay this instruction by one cycle. Suppose I issue this instruction here, I fetch it here. So, by the time it reaches here, the value is not yet ready. What can I do? Come on, you should be able to answer this question now. What makes sense? Other than stalling, of course. Predict the value, exactly. Why cannot I predict the value that it is going to produce from memory? So, we will talk about that a little bit though, because it is a much harder problem than branch prediction. Yes, yes, exactly, yes. So, accuracy is going to be low, but turns out that there are many programs which load constant values. So, they are predictable. Some values are predictable, some values are not. And turns out that one of the most loaded constant is 0. So, anyway, so that comes from program analysis. We will talk about things that how does this such a predictor look like. And what you can do when you make a mis-prediction? You have to have a fixing mechanism there also, just like a branch predictor. So, that will allow you to actually go beyond this limit, this data flow limit. You can start predicting values correctly and you can even execute instruction dependence before the producer is done. So, that is possible. So, just to kick start the process with a simple example. So, here there is a code snippet which shows that data hazards may introduce unnecessary stalls. So, let us try to understand that first. So, here I am talking about our traditional pipeline which can fetch one instruction every cycle and it will take the instruction to the pipeline. So, whatever we have seen so far, not like this that I can issue multiple things together. So, what is it doing here? So, the first instruction is a division, a double position division operation which operates on F 2 and F 4 and produces value in F 0. Second one uses F 0, produces value in F 10 and this operation is using F 8 and F 14 and produces value in F 12 which is independent of both of these. So, now, whatever we have seen, what will happen? This instruction will have to wait until the value of, until the division operation completes and produces F 0. And since we have a constraint that we have to go in order one after another, this instruction will have to wait. It cannot be fetched actually or even if it is fetched, it has to wait somewhere in the pipeline simply because this instruction cannot execute at this point. So, this in order issue an execution and precise exception as you have talked about actually disallows this instruction to overtake this one because if it overtakes this one, there are many dangers. One thing is that this instruction may later raise an exception, some type of arithmetic exception by when this instruction is already completed. Then you have to find out, we have already talked about solutions how to fix those things. So, I want out of order execution and still maintain precise exception. So, what this means is that, so this is a very generic pipeline that you will find today. The front end of the pipeline, there is a fetch, decode and possibly issue are in order. So, they will fetch instructions in order, decode them in order and issue them in order. By issuing I mean, they will put the instructions in some queue and then this particular middle part of the pipeline is going to be out of order completely. So, they will pick up instructions from this issue queues which are ready as and when they become ready, they will be sent to register file to read the values, read the register values, pick up the values from bypass, execute, look up memory and they will put the result somewhere. They will remember the result somewhere, let us for now assume that they will remember the result in the issue queues itself. The issue queues slot is maintained for this instruction and the value will come back here. It will not yet write to the register file. So, finally what will happen is that this issue queue will be drained in a FIFO manner one after another, the values will be transferred to the register file and that is when they execute this particular phase. So, what I am doing is that I am now decoupling the pipeline of an instruction into three phases. This is one phase which is completely in order. This phase is totally out of order. We do not maintain any order at all here and this phase happens sometime much later when the instructions turn comes to complete. So, this makes sure that you have no problem with precise exception at least. You can catch exceptions in the order they go and you can cancel instructions which are after the accepting instruction, but you still have to deal with these two types of hazards. But does everybody see that or have I already solved them in some way. So, I said that every instruction the model is very simple. I fetch an instruction, I decode the instruction and I allocate an issue queue entry for that instruction and this entry is maintained until the instruction is completely done. By done I mean has written the register value back to the register file. And from this issue queue I pick ready instructions as and when it becomes ready that may not be in the P4 order. They look up the register file, they execute, they look up memory and the value is stored back to the instructions issue queue slot. And finally when an instruction is done and is at the head of the issue queue, it will move the value from the issue queue slot to the register file entry and the issue queue entry will be freed at that point. So, I tell you that I have not only solved the precise exception problem, I have actually solved these two also. Why is that? I am somewhere doing a renaming actually. What is the name space of these instructions now? The name space of the target of these instructions. In the decode phase the name space was the register name space of course, whatever the register IDs are. Where do I store the value after the instruction has done executing issue queue entry? So, that becomes a new name space actually. So, two instructions with the same target register will now get two different issue queue slots and they will store the values actually in those two slots. So, these two slots now serve as two different names for the same register. How do I guarantee true dependence in this particular model? But the dependent instructions should get the correct values also. How do I make sure that happens? Who goes in order? Execution is not in order. Yeah, but let us go back to this example. So, here this instruction. So, let us give them slots, issue queue slots. Suppose these are the slot IDs. Actually the slot IDs will be in this order. I am sorry, yes. Because the issue queue is assigned in order. So, let us see what are the slot IDs? So, 0, 1, 2, 3, 4, 6, 7. So, these are my issue queue slots taken by these instructions. So, these load instruction will complete and put the value in slot 0. This instruction will complete and put the value in slot 2. The question is this instruction. So, the slot 5 instruction must get the value from slot 2. How do I guarantee that this happens? So, that means when instruction 5 executes or issues, just to look up the entire queue, why should it be at the end of the queue? I am really looking for dollar A1, the latest dollar A1. That is what I am looking for actually. Which may not be at the end? So, I have to search through until I get the latest dollar A1. So, that is how I maintain true dependence in this particular model and the other two dependence are already gone because I have renamed them already. So, for example, here you can look at, we had a problem with these two instructions. We had a anti dependence here. So, slot 4 here will pick up the value from slot 3 and this instruction here has already renamed dollar V0 to slot 5. So, that has decoupled dollar V0 from this dollar V0. This dollar V0 really corresponds to slot 5 now. It is not dollar V0 anymore. Similarly, if you look at this one here, this is now really slot 7. It has nothing to do with this dollar V0. So, finally, of course, your queue will be drained in order. So, this instruction will first go and write to dollar V0. This one will write to dollar V1. This one will write to dollar A1 and so on and so forth. And when writing back, in fact, even you can even nullify some of the writes. For example, you can say that well by the time if dollar V0 is already consumed and overwritten, I may not even write it back. It is enough to write back this one because this is a final dollar V0 that survives. But anyway, so those are some optimizations which are not really important. But is the model clear to everybody? How I resolve name dependence? And this one will also resolve memory dependence, same way. Because two memory operations here will get two different issue slots. Even if they have two different addresses, it does not really matter. Finally, they will write to the memory in order, but they can execute out of order without any problem. So, we will continue from here next time. We will try to concretize this model more.


Hello, everyone. We're really glad to be here. So our talk today is about how small can you go. And this is in the context of customizing interpreted languages for WebAssembly. And so if we talk about the REST report that we have in the state of Wasm as of 2022, we see that we have a lot of users that are interested or are already using serverless or containerized environments. And for example, they are also using WebAssembly as a plug-in environment. But also more than that, if we look at the evolution from 2021 to 2022, we see that serverless and containerization, everything is a plug-in environment, especially everything is growing a lot. And so we ask or we wonder, is there anything we can do to help this community? Is there anything we can do to help WebAssembly and also the people who actually want to try WebAssembly and see the benefits of WebAssembly? And the answer with interpreted languages is that they have a huge existing user base. And so we want to take advantage of that. We want them to see that WebAssembly has multiple benefits. We have seen today multiple times in terms of security, in terms of portability, of composability. And so we want them to come to WebAssembly. And at the same time, we want WebAssembly to have more users. And so we want them to come with their ecosystem and reach WebAssembly completely. And the question there is, how do we bring them to WebAssembly? Do we just lift and ship their applications? Will just that work? Like when we add virtual machines, we move to containers kind of a lift and ship kind of move. Is this something that we can actually pull? Does it really make sense? And so the target for interpreted language, this is a little bit different. And so in this case, because we are focusing on size, we are trying to make our interpretation smaller. And the reason that we are trying to do this is because when you move to the edge or closer to the edge, I don't have a really good definition of fits. But what I can understand, or my guts tell me, is that it's a monetary business environment. It's usually smaller in terms of what it can compute. And so we want things to be smaller. We want things to speed up faster. And so the first thing that we want to do in terms of size is to reduce external dependencies on the interpreters. And so for that, the Wasm Labs team, we created the Wasm Language Runtems Project that other companies are using today or are contributing to it. And so we have created or we have published Ruby, Python, and PHP. So you can just use those interpreters, run your programs in those languages, and just use them. But as part of this work, and because we want these people that are using interpreted languages to come to WebAssembly without having to do a lot of other work, we want things to just work out of the box. So we had to support a lot of different libraries. This is a work in progress, and the job is not done. Of course, it's not finished. But we needed, for example, to board libraries as the ones that you see here, and more will come, of course. By the way, if you want to contribute, you have the door open, so come and contribute to this project. And so these libraries is not only to support these languages. In this project, we also want other people to use these libraries as third-party libraries. So if you need SettleDeep, you know where to go. You get SettleDeep, and then you use SettleDeep from there. So that's in terms of the interpreter. We just made the interpreter smaller, and so this is what we can do with the interpreter itself. But there is another moving piece that is the standard library. And so, yeah, one second. So there is a standard library that we can also reduce. And so for example, with Python, you are able to reduce the standard library in two ways mainly. One is, I can seep the standard library, and then the interpreter is going to unzip it on the fly, and then it's going to read that interpreter. It's going to read that standard library. Or you can extract and use features from it. And so what we saw from the standard library of Python, this is just from the standard library itself. You see that the unit test, for example, takes a lot of space. This is something we didn't really experiment with on the WebAsync language runtimes yet. But for example, if you remove this, you are stripping almost two megs out of it. Let's try the way. And so this is something that we wanted to check as well. So if we go to the interpreter again, we were talking about having this slim version that you can just run closer to the user. So it's something less powerful that can run it. And so for this, we have this limb, and the battery is included in versions. This limb is around 5 megs for PHP CGI. Batteries included versions are around 13 megs. But what happens if I need something in the middle? Because I want things to just run out of the box. I don't need a lot of things. I just need what I need. So for example, in this case, I just built with this parade, with WebAsync language runtimes, a version that just built XML and SQLite that is around 10 megs. And so you are able to make your own. So we have all this pipe work, let's say, that you can just use. And you can configure and build your own interpreter. And you can even optimize it as you want. So basically, you have the door open to do whatever you need in terms of optimizing your interpreter on the standard library. And so the last part of this is, OK, I need to embed everything into a single piece of software. And then you need to bundle three things. Mainly, that's the interpreter. That's the standard library. If it applies, for example, in PHP, there is no standard library that goes next to the interpreter. Everything is bundled into the interpreter. And then your application, that is what is actually going to serve the user. And so for this, you have different options. Oscar is going to look more into that, and he's going to give you more hints on how to do that. But you have manual pre-opens, where you have the source code next to your module. You just open that, and then it runs. You would also use Docker plus Wasm, where you just build the container, and then you run that right away. Or you could use Wasi VFS. This is a period that hasn't been mentioned before. You need to have Wasi builds for it. But in this case, on Web as in language runtimes, we are using Wasi for all our builds. And it's a virtual file system where you can just put that together with your statically linked with your program. And so you can refer to files on this virtual file system and load them. So one thing that we did for this talk was, OK, let's try to compress with Wasi VFS. So this is a feature that is not supported on Wasi VFS as of today. And so it's like, what if we are able to compress? So when you do Wasi VFS, there is two things. One is the guest part, which is the static library that you link together with the program. And then the Wasi VFS pack CLI that you call from your host, actually CC64, if you want, for example. And then it will just go and read all the things that you want to mount inside the Web as in the module. And it gets bundled together into the Web as in the binary itself. So let's look for a quick demo on this one. This is not yet open. There is no request open for this. But we are going to open that soon. And so, for example, we have a very simple main C program that is just going to FOPEN TMP test file. And then it's going to read a recharacter and it's going to print a recharacter that it finds. The cool thing about this is that with the compress option, as you can see, there is nothing that this program knows about XSET. This implementation that we created uses XSET. So we have 12 support lib LZMA. In order for this to work, this is going to be transparent to you. You just open and you just read. And the thing is that when you do WASP VFS minus C with compress, it will do on the CLI side compress everything and pre-open everything compressed with XSET. And then the guest part of it will automatically decompress when it reads or when you read from the module. It can be optimized a lot because right now it doesn't even implement a cursor. So every time you kind of read, it decompresses the whole thing. So it's very slow right now. But the idea is that it will optimize it. So this is a test file that I just created for this example. And now I have another file that is bigger. It's ASCII test. So it has a very high compressed ratio. And so we have the test file here and a big ASCII text file. And then what I'm going to do here is to package a regular WASP VFS version of our module without compress. So the first command that you can see here is just going to build our program, the main C file. As you can see here, it's also going to link the WASP VFS, the guest site, along with it. And then it's going to output that to main WASP TMP. And then we have the target, WASP 32 WASP. So this was suspected. And now I can do the very same thing but with compress option. And in this case, you can see I can skip the part where I built the main C file. And now I can just do the minus C and then it just finished. And now we can compare both files, one without compress and one with compress. And you can see it's left and half the size because it had a really high compressed ratio. So it's pretty easy for that to work that way. And now we can just brand the compressed version out of it. OK, so this is just an example of a very simple text file. But for example, if you create a WASP program that contains everything embedded with WASP VFS, and for example, you embed WordPress in it, you will see that there are really, really high compressed ratios. Like we are talking about 29 megs instead of 71, for example, right? So it's like very big compressed ratios that we can just pull without having to worry about lots of things. OK, and with that, Oscar has been talking more about the details of other options. All right, so that's one side of the coin, right? Where it's like, OK, how small can we get the interpreter itself, like the actual web assembly module that we need to run? But the other side of that is, what if we can actually take the source code that the user wants to run and provide that at runtime? If we can do that, essentially, especially in this component model kind of world, we can essentially link in that code at runtime and only move that bit over, and then that's going to be quite a bit smaller. So that's the huge thing is just having the interpreter ready to go. That also is going to help out a lot with speed and performance, because a lot of the computation time is actually, hey, we have to compile that huge interpreter and get it up and running. Now, the easiest way to do this is just via WASI. And there's sort of two main approaches you can do that. One is via pre-opens.  And so it's actually pretty simple to do this. And in a language like Python that just loves that disk, this solution actually works really, really well. Because essentially, hey, yeah, just method directory, it works kind of just the same way you run Python on your computer. There can be some downsides with this approach, where it can be a little bit brittle. Because in this approach, it's not the situation of, hey, I just compiled a Rust module, for example, where the only thing I need to care about when I run that thing is that Rust module. I don't need to care about additional files. But when going to a system like this, all of a sudden, now when I want to run that user's program, I've got to make sure I have that WASM file ready to go, and I've got to make sure that I've got their source code somewhere that I can actually pre-load it or pre-open it so that way the system can read it. So depending on how you architect your system, that could be a little bit brittle. Now, ways around this, and this is, for example, at Suborbital, how we do this today is we just bundle all the code in with the interpreter itself, have one WASM module, and it's not a problem. But there are other ways around this as well. The other major way is via standard in. So it's like, hey, I don't want to deal with the files at all. I actually just have some source code that I want to run. And this sort of thing works really well with a language like JavaScript.  So that actually works really well here because, hey, I can just stream that in over standard in. The interpreter can read it and run it, and hey, I didn't have to deal with any files, and I'm a happy camper. So one downside of this approach is what if you want your user's code to be able to use standard in and standard out, that could potentially be a problem because you're taking it away to be able to do this exact thing. But depending on how you architect your system, you can restore it. There's solutions here like, hey, actually, let's just use a different file descriptor. There are some easy ways around that. Now, where things get really interesting is doing it via module linking. So this goes back to, hey, actually, I just want to deal with web assembly modules. I don't want to deal with source code files. I don't want to stream source code over standard in. I really just want a web assembly module. So our system at Suborbital really likes web assembly modules. It doesn't really like other types of files, and I don't want to have to get the team to re-architect everything to make it work by bringing source files along. So by packaging up user source into its own web assembly module and linking that in at runtime solves a million issues here. And in some cases, we can even pre-compile byte code. So instead of saying, hey, actually, this is just a web assembly module that wraps some text. Potentially for whatever interpreter you're using, it has some sort of byte code format that it uses. You can pre-compile that to whatever byte code format. And then when you link it in at runtime, it's even faster because it's already completely ready to go. The interpreter doesn't have to do that extra work to get it working. So the cool thing about that is you get exceptionally small binaries. Especially if you're not even dealing with generating byte code, the size of your wasm module is only just a hair bigger than the size of whatever your source code was. And so then we go from having to deal with this 26 megabyte Python web assembly module to this little baby 300 bytes web assembly module. And that's a lot easier to move around in the system. So I want to show you guys some code on how if you are using an interpreter, this is how you sort of make it work. A lot of this code is being pulled directly from the web assembly language runtimes repo. And so in here, the major way you make this work is you have a couple of exports for your interpreter that the web assembly module itself is going to expose. So the main thing you need is, how can I allocate memory so I can feed in that source code? And we do that via this allocate string function. Then the next is, how do I execute that user source code? We do that via exec string. And then, of course, every interpreter needs to do some initialization. So we provide a way to do that as well. So these functions are dead simple to implement. And it's really easy for anyone to do. Don't feel that, hey, this is too complicated for me to get this working. You have to write this code one time. In the sense, each of these are just a single line of code. So for initialize, it's OK. Just tell the interpreter, hey, I'll just do whatever you got to do to get ready. That's pretty easy. Allocating a string, it's just a matter of, in this case, we want to have a no-terminated string. I was lazy. I just used Calyx for this and added one to it, which, in production, you can write code that's slightly better, but this totally works for here. And then in terms of actually doing the execution, that's it. You just say, hey, whatever method. And I'll show you that as well. Whatever method by which your interpreter does that, just tell it, hey, I just execute the string. In this case, it's literally a matter for Python anyway, of saying, hey, Python, compile this string. And it's just like, all right, I did it. Here you go. And then the next is, hey, now I want you to execute that code. And that's it. And this is all of the code that is necessary, really, to get this working. You don't need anything more complicated than that. So how do you actually get the code that the user wrote into a WebAssembly module? I promise you that it's so dang easy, you could write that WebAssembly module by hand. And so what I did was I wrote that WebAssembly module by hand, because it's really that easy. So it's a matter of on line six. And this is the program that we want to run.  That's pretty easy. Line seven is just, what's the size of the data? It's 21 bytes. Line eight does the actual loading into memory. But that's just a couple of WebAssembly instructions. And then the big piece here is just the start function. So what the start function does is it'll say, hey, let me allocate a string for this data, sets it into a local. Then after that, we load that data in. And that's calling the function that we define on line eight, which just literally writes that data into memory. And then on line 19, we just say, all right, interpreter, execute that. That's how dead simple it is to put something like this together. So in fact, in terms of the quote unquote tool that you need to actually take source code and put it in a WebAssembly module, actually just wrote a bash script, because you don't need anything more complicated than that. You can, of course, do it properly, break out some rust as we all like to do. But sometimes it's literally a matter of write a bash script that writes this file out and then just use something like what to wasm to just switch it into WebAssembly and then you can get this stuff running. So with that, I swear I know how to run my own demo, I swear. So with that, I'm going to show you how easy it is to actually get this up and running. Sweet. So the Python code that we're going to want to bundle into a module is right here. It's super simple. It's literally a hello world file. Nothing special there. But we need to actually build that module. Like I said, I literally made a bash script. All it does is it takes the code from the file, puts it into that file, writes it to disk, and then runs what to wasm on it. It's super simple. And so that's it. And then the next question is, of course, how do you actually run that? This is possible literally with wasm time. You don't need to do anything special. So the way this works here is wasm time has this flag called dash dash preload. And you just tell it, hey, here is the interpreter. It is here. It is ready. And you give it a name in this case. They call it Python Provider. And here's that web assembly module. So just preload this and then execute my web assembly module that I gave you. And if we do that, it runs. And it's actually pretty quick. It says we get our hello wasm day running in Python. And it was linked from two separate web assembly modules. Now, the sweet part about that is in my system, I can have that Python Provider web assembly module already running.  And then I have this 300 megabyte web assembly file that I'm shipping. And boy, can I get that over the network quick. And that makes me pretty happy than shipping this 26 megabyte web assembly file. So overall, it's an actual real way to make this happen. And in fact, if you use Java and you use the linking flag for that, it does the exact same thing. And so this is an easy general approach you can use for your interpret language is running in web assembly. So with that, how can we make the experience with interpreters better overall? Well, we have a lot of WASI proposals coming up for things like, oh, databases and HTTP. One of the things that exists as a Python standard library is a full-fledged HTTP client. And imagine there was a way that we didn't have to ship that because I'm sure that takes up probably like one, two, maybe even three megabytes in our interpreter, right? Well, the beauty of that is we can maybe get that working via WASI and via the component model. Because with the new proposals coming up on WASI, we can start replacing a lot of the functionality in our interpreters with native support that exists in WASI. And that's going to be able to get us our interpreter binary sizes way, way down, because we're essentially just removing so much handwritten functionality that just doesn't need to be there when we can re-provide it. And that's going to be the big piece that gets it all working and gets those interpreters even smaller. And that's about it, folks. Thank you so much. Thank you for a great presentation, Oscar and Rafael. We have time for a few questions? Yes. I don't think you need the introduction, but. Hey, Alan Bailey. So I'm curious how much of this do you think can land in the upstream languages like for Python and PHP? Yep. Thank you for the question. That's a great one. So for example, in the case of what we're doing, we are maintaining the PHP patch, because it's kind of a big patch. And what we are doing is contributing to the upstream. And so we are seeing when the community is going to give that like a green light, and it's going to get into there. But we don't know much about when is that going to happen. But we're trying to upstream everything we do in that case. One of the things I'll add to that is the work that Rafael did on Wasi BFS with Compress. He told me he was going to get a PR up for that pretty soon. And so that would be available. Because that's obviously a tool that a lot of us use in just having the ability to just hit a compile flag and say, hey, man, just compress this for me. And it just works with no effort required. It's amazing. So I believe that's going to be up pretty soon, and with tests too, maybe. And that should be upstream pretty soon. Any other questions? OK, thank you very much. Please give a round of applause to Rafael Oscar. Thank you very much. Thank you.


In today's video, we are going to talk about some interesting facts and myths about the Oriental Short Hair Cat. A beautiful cat, the Oriental is also highly intelligent and requires a parent who will return this breed's trademark dedication. The Oriental is a long, elegant cat. The body, neck, legs and tail are long. She is medium-sized but nicely muscled. The Oriental is a cat of extremes. The head is a long triangle. The tall ears are set to be a continuation of this triangle. The nose is long and straight. The legs are long and slender. The tail is long and tapers to a point. The eyes are almond-shaped and are usually a bright green. White orientals may have blue, green, or odd eyes. The pointed Oriental has blue eyes. The Oriental Short Hair coat is short, glossy and lies close to the body. The long hair variant has a medium-length coat rather than a full, flowing coat. For those who may not be familiar with this awesome cat breed, the Oriental Short Hair is a favorite among many cat lovers. Here are some interesting facts about the Oriental Short Hair Cat. 1. Oriental Short Hair Cat's Personality. The Oriental Cat is not only beautiful but also is highly intelligent. She can be trained to walk on a lead. This does not mean that she can be trained to do everything you might wish. Like most other highly intelligent breeds, the Oriental has her own desires. The Oriental is an affectionate cat and requires her parent to be as dedicated to her as she is to her parent. The parent must be affectionate to the Oriental and must spend some time spent playing with her. 2. They are Hypoallergenic. While no cat is ever going to be 100% allergy-free to humans, the Oriental Short Hair is reported to trigger almost little to no allergies for those who are said to be allergic to cats. An Oriental Short Hair can become jealous or even territorial with their human companion. 3. They are the smartest of all cat breeds. It may be hard to decide which cat breed is head of the class due to stipulations in each breed, but many report that the Oriental Short Hair is among the smartest of all cat breeds. These intelligent cats enjoy interaction, stimulation, play time, and are naturally inquisitive by nature even more than your typical cat. 4. They have more patterns than any other cat breed. These rainbow cats come in every pattern and color you could possibly imagine, at almost 300 combos to be exact. Some have stripes, others are solid, but all true Oriental share the same angular face, large expressive eyes, tall pharaoh-like ears, and limber lengthy bodies. Despite the unique colors, there is no mistaking an Oriental Short Hair for any other breed given their distinctive appearance. The Oriental Short Hair is a result of man-made crossbreeding between the Siamese, Abyssinian, and the Russian Blue. 5. Oriental Short Hairs are entertainers. This breed is very vocal, loves to be the center of attention, and will always keep you guessing with what they are going to do next. The Oriental is a great cat choice for an active family that will give their cat lots of love and attention as this is a true requirement of the breed. Orientals do not do well in environments where they are left alone often, as they crave attention and can even become depressed much like the Siamese which they share bloodlines with. 6. They are super talkative. For anyone that owns an Oriental Short Hair, they will agree that these cats make no bones about conversing and grabbing your undivided attention. If you are wanting a cat that yearns to be near you, keeps you guessing, and is devoted to being your loyal companion, look no further than the Oriental Short Hair. 7. Oriental Short Hair trainability and training tips. Play and training are two ways to make sure your Oriental Short Hair is happy and healthy. And when a cat is happy and healthy, they're not tearing up your house. Luckily, Oriental Short Hairs are easy to train. They love to make their owners happy, and they'll find training sessions exciting and fun. The best way to go about training an Oriental Short Hair is to draw upon their strengths and natural instincts. They love to climb, they love affection, and they love to chat with you. These three elements can make your training sessions rich and rewarding for both you and your cat.  You might set up a house-wide agility course that ends with lots of treats and cuddles. There are also fun ways to train your Oriental Short Hair to use vocalizations to get rewards. The possibilities are endless. The main thing to keep in mind is that training should be regular and lifelong. Keeping your kitty engaged and well-exercised is key to their happiness. A. Living with Oriental Short Hair Cat. Living with an Oriental Short Hair can be both entertaining and a bit of a challenge. If you were to put an Oriental Short Hair in a room with nothing more than a chair and a lamp, you would come back to find that they had magically procured a variety of things to play with. Maybe they had ripped up a patch of carpet. Maybe they unscrewed a bolt from the lamp. Parents can tell when the Oriental gains weight. Oriental cats show a pot belly even after indulging in one day of overeating. The nutrition must be carefully controlled for this breed. The long, slim legs are not made to hold a fat body. Orientals are great jumpers and love heights. For this reason, purchase and cat trees should be provided. Oriental cats love to play and appreciate toys around the house for their pleasure. Provide them with plenty of cat toys, and remember to schedule playtime every day. Your Oriental Short Hair will also love the opportunity to look outside. They also like to watch television. One crucial part of living with an Oriental Short Hair is that they need ample social time. This is not a great cat for families that work a lot and can't spend time with their cat. Instead, they love a full house and will even get along with dogs and other cats. As elegant as the Oriental looks, she can be quite a lap cat. She is extremely affectionate and will sleep next to her parent in bed. 9. Oriental Short Hares Grooming Grooming an Oriental Short Hair is easy. They've got a short, shiny coat that just needs a brush with soft bristles every few days. This breed is pretty close to hypoallergenic, and their coat is soft and easy to manage. While the coat needs little care, the Oriental Short Hair associates brushing with affection and will enjoy being groomed. If you're looking for your Oriental Short Hair, chances are, they're digging around in your sock drawer, your purse, or even the fridge. These wily little kitties are great at opening doors, cabinets and drawers, and they'll stop at nothing when they're bored. This cat might have started out being turned away from the Siamese Breed Standard Party, but they've found their true home in the cat-loving community. If you enjoyed this video, kindly press the like button. Also don't forget to subscribe with notifications on, so that you don't miss out on videos like this. Thank you for watching.