 This time we're going to be looking at branch target buffers. In the previous videos we saw a few different methods of doing branch prediction, figuring out whether or not we want to take a branch. These are nice because they give us a good chance of knowing whether or not we're going to take a branch before we've actually calculated it. But in order to fetch the next instruction we need to have the target address available to us in cycle one. That's the stage that we update the program counter, so we need to have the data available then. Unfortunately our hardware is set up in such a way that the instructions are decoded and branch target addresses are all calculated in the second stage. We're going to need to move that back to doing it in the first stage. And the way we're going to do that is by using a branch target buffer. So a branch target buffer is really just a table that's indexed by the current program counter, but which contains branch target addresses. Specifically it contains branch target addresses that we want to take. There's an alternative though, we could instead include the target instruction. This is actually kind of cool because in some cases, for example if you have a jump instruction, you can actually complete your instruction in zero cycles. So our program counter comes in, we see that this is an unconditional branch or a jump instruction. We just pull out that target instruction and go run that immediately. We don't send the jump instruction off to the hardware to be processed. We send the instruction that the jump instruction is pointing at as the instruction to be processed. Branch target buffer is actually pretty simple. We'll walk through the stages of our architecture and see what happens to the branch target buffer in each of these depending on what's happening with our instructions. So the first thing that we always do is we just fetch our instruction out. We're going to pass the program counter to the branch target buffer so that we can determine whether or not this is a branch and if we should take it. If we find a matching entry in the branch target buffer, then we know this is a branch instruction and we believe it's going to be taken. So we take the address out of the branch target buffer and we use that to query the instruction memory. If it turns out that we do actually take this branch, then we're just going to continue as normal. We did the right thing. This was the correct path to take. But if it turns out we weren't supposed to take that branch, then we need to flush the mispredicted instructions. We're going to delete that entry from the branch target buffer. Then we'll go back and restart from the correct instruction, the one that we should have taken. On the other hand, if we don't have an entry in the branch target buffer, then our process is a little different. In this case, we believe we don't have a branch that we're going to take, so we just fetch the next instruction the way we normally would. As long as it doesn't turn out that this was actually a branch that we were supposed to have taken, then we just continue like normal. This could be a branch instruction that we don't take, but it's more likely that it's just some random instruction that doesn't have anything to do with branching at all. On the other hand, if we discover that this was in fact a taken branch, then we want to add the instruction address and the branch target address to that branch target buffer. Then we will flush out all the mispredicted instructions and restart at the correct instruction. This way, the next time we get to this instruction, we'll have the program counter and the target address in the branch target buffer, and we can pull them out and use them. Branch target buffers only contain the target address for branches that are predicted to be taken. In this case, we're really just doing that based on the performance of the branch last time. These are very different from branch prediction buffers where we're trying to determine whether or not a branch will be taken before we process it. In this case, we're just writing down everything we know about a branch. Our branch prediction buffers can keep track of all of the branches in our code. The way they do this is the branch prediction buffer will map multiple instruction addresses to one branch predictor. In general, this is fine because we don't have a whole lot of branches in our code, so there isn't a whole lot of chance that we're going to have some conflicts there. It turns out even if you do have some conflicts, they're often not going to be major, and the branch predictor will still work pretty well, even if there is some conflict between two branches trying to use the same predictor. With a branch target buffer on the other hand, you don't want to do this. Imagine if you have a buffer where, say, eight different addresses correspond to one line in our branch target buffer. Then, any time you got to any one of those eight instructions, you would think it's a branch and you're going to take it automatically, regardless of whether it's even a branch at all. So our branch target buffers can only keep track of a limited number of branch instructions. In a more complicated algorithm, we could choose when we want to take a given target address out of our branch target buffer and replace it with a new one. We could, say, keep around the most recently used ones, for example. But either way, we're going to have a limited number of entries in our branch target buffer. So our branch target buffers are not going to be mutually exclusive with branch prediction buffers. We'll be able to have a branch prediction system working alongside a branch target buffer. And in general, we want to use both of those systems to maximize our performance.