At the end of this lesson, we will discuss some other IPC-Enhancing Compiler Techniques. That we will not discuss in detail, but you can find other techniques like this is more advanced compiler classes. The first technique is Software Pipelining. It’s a technique that helps schedule loops, like these in a way that doesn’t greatly increase the code size. But it allows us to get the effect of unrolling many times. Again, I’m not going to discuss this technique completely but the idea of it, is to treat the loop as a pipeline. Where this is the first stage, this is the second, this is third stage and so on. And then schedule the code so that we do the last stage of a third iteration while doing the second stage of the next iteration and the first stage of the next, next iteration. Why do this this way? Well, because here, we have dependencies, whereas here we don’t. This store here is storing a value to which we have been adding in the previous iteration of the loop. And this ADD is using the value that we have loaded in the previous iteration of the loop, and so on. So it kind of forms a pipeline out of the loop. And then it allows us to do parts of different iterations concurrently with each other. And another technique that is very powerful is trace scheduling. You can think of trace scheduling as if conversion on steroids. Conceptually we take a code that has branches because of if then elses and other things. We find what the common path through this code is, and the blocks that are on the common path are. Then put together like this, without the branches in between. Now we can freely schedule instructions in between this, and we also put checks if the common path is not being executed for example here. What if this path should have been executed, if that happens we branch out of this scheduled code. And at the point when we are branching out, because we have intermixed these instructions, we first have to fix it. Basically undo the effects of instructions that we shouldn’t have done. So the compiler, it needs to create some coded compensates for that. Then we can execute this block that should have executed. And then, we execute the next block that is on the common path. But we cannot execute it here, because we have interchanged these instructions. And then, we can jump to the continuation of this. So simply, we form a likely trace. We execute that with an excellent schedule. But any departure from the trace requires us to not only execute instructions in a slightly less efficient way. because we couldn’t reschedule as well. But also, we need to execute some compensatory code. Basically, fix things that we messed up by reordering instructions. Again, this is not the full explanation of trace scheduling. I just wanted to give you some idea about how it works. But if you’re interested in techniques, like software pipelining and trace scheduling. Then I strongly encourage you to take an advanced compiler class, where these techniques will be discussed in detail.