 Okay, so Project Five will be released today. I'm still putting the finishing touches on everything, but kind of, wow, I love the project. I think it's like not doing that. Not doing that. No. At least you're on it. Yeah, it's actually for an interesting reason because we actually use this project to compare you with all the other classes to make sure that we're, as part of our ABET certification, so how you do on this Project Five impacts our certification, so. This is one of the other projects you're doing. Huh? This is Project Four. No, but we've done it in the past. That's what I'm saying. We can't compare historically if we change projects. Sorry. This is the same project. Anyways, okay, so the important thing, so we'll talk about the project. I'll post it later today. You're basically writing a small compiler. You're writing the front end to the compiler. You have to generate this intermediate representation. Probably the important thing that everybody's actually interested in. Wait, before you go back. Yeah, what's up? You can keep thinking about it. Can I? Come back. Come back. Got it. So there is the opportunity for extra credit on this project, so you can, but it's big extra credit, so you can either, you have the option to do a bonus project, so you can either resubmit Project Three, and you'll get, it'll replace your Project Three score up to with a 70% reduction. So if you got a zero on your Project Three, if you do it 100%, that score will change from a zero to a 70. Same thing for Project Four. You can do, and it's an either, or you can choose one of these. You can't do all of them. So you can do Project Four, if you put the bomb, Project Four, you can redo that for 70%, or you can do a new bonus project that'll be described in there that will replace the lowest grade to be a Project Three or Project Four. Yeah. Did you mean 30% reduction? Yes, that's why I meant, sorry. You'll get 70% of the points, yes. So can that bonus project be used to boost your grade well in the process? Yeah. Yeah. Yeah, sure. I agree with that. Your opinion, do you think Project Five is more difficult than Project Four? I guess that's how things are set up. No, I think it's on a similar vein, maybe less. I think it's one of these things that really depends on the person. I've heard completely different things from different people where some people say Project Three is hardest, some people say Project Four is hardest. I don't think anybody said Project Five is the hardest. It is tricky to get it working 100%. It's very detail-oriented. You're gonna be working with graphs, and so you're gonna be working with recursive data structures, and so there's a lot of challenge that comes with that. But it's a good challenge because when you solve it and you fix it, it makes you feel strong and powerful. Is that any of the projects that you've done that I suppose are in detail on your website? Yes, it will be. Once I post Project Five, all the details will be on there. And then the other important thing on the submission site, when you submit Project Five, there's, so when you resubmit Project Three or Project Four, don't do it on Project Three or Project Four. There'll be an option of bonus project for Project Five, yeah. So this bonus project would not be beneficial to someone who's gotten over 70% on all of it? The bonus project will replace your bonus grade, so with no deduction. So it's a brand new project, right? So it's like, if you're gonna do one of the old ones, you can't get a problem. It's not really fair to everyone else who did it on time, right? We'll do 100%, yeah. So when will it be posted today? Today? So for the old one, if we did Project Three or Project Four, do we, will there be a separate like Project Three bonus test server or Project Four bonus test server? Yes. I think either way, the test cases are the same. But to get credit, you have to submit it on the bonus account. Anything else? Oh, it seems like no question. It's quite some time, let's go over, I'm just kidding, what's up? So will there be like the five points normal bonus on the project that you're giving out, Project Five? I don't remember, I should look at it. Maybe, maybe not, though. So can we schedule? I don't know, I don't think so. Yeah. Is there some sort of hopefully permanent solution to the submission server peaking? I think it is. I mean, if it was working last night, then it's worked. It's going to work. That's a solution that's been programmatically applied to the manual intervention. Yes. Okay, awesome, thank you. More memory, I always fix this, thanks. Okay, okay, so why are we talking about the stack? Why do we care about the stack? Because it's cool. Because it's cool, it is cool. But, and useful. But why, useful for what? Temporary memory. Also for programming, temporary memory, right? So this is our functions, basically our scratch memory, right? So the stack, so what is the stack? So the stack is a data type, right? So if you apply to this US foods job and they go, hey, what does the stack do, right? What does that mean? What are they, what operations does the stack have? Push, pop, push, and pop. Is that the, what are the pushes the pops do? Push the data on the pop, pop pulls data off the top. Okay, so when you push things on, so I push like three things on, and I call a pop, which one comes off? Last in, first in, first out. Yeah, last in, first out, right? So you push things on the stack and we pop things off the stack. So, the stack is used in all these processors. Whenever we look at this, it's gonna start at high memory and grow down. And so functions can use this stack for scratch values. They can push things onto the stack, pop things off the stack. I think we talked about this, so x86, the register ESP holds the value of the top of the stack. And when we push things on the stack, the stack is gonna move down, and when we pop things off, the stack is gonna move up. So I'm gonna have to go a little bit quickly over this because I want you to get an idea of how things are working. We don't have to go into, well, we're gonna go into all the details. So to look at this, so we have stack at all fs, right? So high to low is the way we're gonna draw our stacks. I'm just gonna draw it as essentially words in memory. So 32 bits is gonna be my kind of chunk size. There's something up at the top and then let's say the stack is here. Let's say it's at memory address 10,000 and there's stuff below it. So, if the stack grows down, right? When we push things on the stack, it's gonna grow down. What does that mean about everything below the stack here? It could get overwritten. It could get overwritten, exactly. So what does it mean about this stuff above this pointer? It's safe. It's safe, yeah. Until it's popped off. Until it's popped off, yes. But this memory is essentially allocated or valid memory. Everything below it is essentially garbage. So wait, because of the way the stack works in this where you start at the top and then you push everything down. Yes. Does that mean every time that you push something in, every single piece of data needs to move down? No. It's gonna go down like this. We'll see, for example. So if I have some code that says push EAX and then pop EVX. So what's gonna be, so if you just think about a stack, right, what is the result of these two operators? Stores EAX and the current address and then moves the pointer down one and then takes the thing out of the address and... Right, so what's the semantic effect because those two instructions? It's a copy. Copying what? EVX. Yeah, copying what's then EAX into EVX, exactly. Using the stacks. We're gonna push something on the stack and pop it off into some other register. Exactly. Okay, so here we have our registers. So this is gonna be kind of simulating the CPU and the stacks. We have EAX here. Let's say it has the value 10. We have EVX, which has whatever, zero in it. And then we have the stack pointer. So the stack pointer's at 10,000 hex. So I kind of drew this arrow, right? Kind of arbitrarily at first. But the only reason we know the arrow points there that this is where the stack is is because there's this value in the stack pointer register, right? If there's any other value in here, the stack suddenly changes to something else. Okay, so when we execute this, we're gonna push EAX. So what this is going to do is decrement the stack pointer. So move the stack pointer down. Essentially say, okay, the stack is now, the start of the stack is now here. And then copy the value in EAX onto the stack. So the stack's going to be decremented to FFFC, right? This is gonna move down four bytes. And then we're gonna put the value EAX onto the stack. So this is why it grows down. So now when we pop, we're gonna build back up. This is why we don't have to move anything else. So now when we do this pop, we're gonna take the value that's at the stack pointer, which is 10. We're gonna copy that into that register EVX. And then we're gonna increment the stack pointer by four, essentially moving the stack up. So we're gonna move here. We're gonna increment. So we move A into EVX. We increment the stack pointer by four, which then move the stack up here. What happens to this memory that's here? Nothing, yeah, just gets left here. In essence, it's been deallocated, right? We've moved up the stack pointer. Now everything below here is now garbage. Everybody understand how the stack works? So the idea is, so why we talk about the stack is every function, right? We need, for every function, we need to store local variables. And those local variables, right, are not specific to that function. They're specific to that invocation of that function, right? So every invocation of a function has to have its own copy of the local variables. And the same thing with parameters to those functions, right? So every function has to have the parameters to that function and the local variables, and they're all gonna be stored onto the stack. So the idea is, right? So we want to use, okay, let's think about it like this. So we want to use space on the stack to store the values, right? To store the local parameters to say that this is local parameter A and this is local parameter B. But how do we actually access these local variables, right? We have to compile some x86 code that can tell anytime there's an access to variable A, how do I access that memory in my current executing stack frame location, right? So how do we do this for global variables? Its position is always constant. Yes, the position is always constant for global variables. So the compiler just decides on locations and then every time that variable is used, it uses that location, right? Can we do a similar thing here? Yeah, we need some track of where the function is in the stack. So could we use maybe an offset from the stack pointer? Yes, we could. But the problem is that a function, right? When a function executes, it can change the stack. You can push things on the stack and pop things off. So our offsets would have to change depending on where we are in the function, which makes this a huge pain. So x86 and a lot of other languages introduce the concept of a frame pointer that's constant for every function onto the stack that points to basically this function frame. So this function frame is all the memory of the function that is important, so parameters and local variables and offsets from this function pointer. So every variable usage is gonna be an offset from this function pointer. And so as long as the function correctly sets up the pointer the right way every time and it doesn't change throughout the function's execution, now we have a way to specify the offsets. So on x86, the frame pointer is called the base pointer, so you can think of it as the base of the function, and it's stored in the variable ebx, so for extended base pointer. So let's look at how this idea works out. So then does each function have its own specific function frame with its own? Yes, every, so there's two things to think about, right? So it's every invocation of a function. So on the stack, your call stack of how you get to that function, there's a function frame for every function in that call stack. Does a function frame exist for a function that's not called? I don't know, if you look at the code there is a theoretical frame, right? When that function gets called it knows exactly the offsets there, but it doesn't actually exist until that function is called. And yeah, question? Oh, no, it was a question. So actually the other super cool thing is that when you're debugging with like GDB, right? It actually uses these function frames to figure out what are all the local variables and everything. So it has extra meta information there about what are variables, what offsets are they from the base pointers, what is my currently executing function so that it can do the debugging. And that's why you can do on GDB, you can say stack trace, give me the stack trace of all the functions that are called. You can actually move up and down, which GDB just changes your base pointer to be to those functions. It's actually really cool. Okay, so let's look at an example. So to change our previous global example, right? Here we're just gonna move those variables from global variables into the local scope of main. So we have an int A and int B, a float C to int 10 B to 100 C to 10.45. And then we're gonna do A is equal to A plus B and now we're gonna return zero. Right? So the idea is now the compiler when it's compiling this function main, right? It looks at C's all the local variables, right? And can it easily do this in C? What part about C makes this easy? The way the scoping works. The way scoping works? What about variable declarations? Or can you declare variables in straight C in blocks, where in the block? Traditionally it's at the top. It's not traditionally, it has to be. It's according to the C specification. Yes, C plus plus allows you to declare it anywhere. And maybe the newer C things allow you to declare it. Otherwise, but this is why when you look at kind of classic C code, all the variables are declared at the top. And it actually helps the compiler out because the compiler, just like in your program when you have a variable declaration section, you can go through and see all the variables that are declared. Then when you execute the body of the function, you don't need to worry about variables being declared in the middle there. So this helps the compiler. The compiler can actually look here and see, okay, I need three local variables. So before, the compiler would look at these declarations and it would say, okay, give a constant address to every one of these variables because they're in the global scope, right? But for here, what's the compiler gonna say for each of these variables? Yeah, create on the stack, but how's it gonna uniquely identify A versus B versus C on the stack? Offset, yes. So it's the offset from the base pointer. So it's kind of gonna use the base pointer as a reference and it's gonna say, okay, every time this function executes, A is gonna be at the base pointer plus A. And B is gonna be at the base pointer plus B and C is gonna be at the base pointer plus C, right? So like before, where it just put absolute values here and said A is at A, B is at B, here it's using offsets from the base pointer. And so the code looks incredibly, will look incredibly similar to what we had before, right? Where we set the memory region of EVP, the base pointer plus four, which is A, or base pointer EVP plus A to 10. It's gonna set EVP plus B to 100. It's gonna set EVP plus C to 10.45 and then it's gonna set memory of EVP plus A is equal to memory of EVP plus A plus the memory of EVP plus B and then it would return zero. Okay, some tricky things. On x86, local variables from the base pointer are negative offsets. So I actually look at exactly how this is in the stack, but they're negative offsets and parameters to functions are positive offsets. So, for instance, A is at EVP minus C and B is at EVP minus A and C is at EVP minus four. This is just how the compiler decided to order these variables, right? There's nothing in the C specification that says they have to be ordered exactly like this. They could be ordered, the compiler could randomize it, they got to do whatever they want. As long as every usage, right? Every time we use A here, we do EVP minus XC, which is 12, right? As long as that code is consistent, then it doesn't matter where, what offset it lives at, right? So if we look at the code, the code does some interesting things. So it first, so when this function gets called, the stack pointer's gonna be pointing to the bottom of the stack. And then the code, the every function in C is gonna start with this and it's gonna say, okay, set the base pointer to the current stack pointer. So that's how it decides where the stack pointer, where the base pointer is for every function. So wherever the stack is, when you get called, the base pointer points to there. And it's not gonna change throughout the execution of this program because the stack could change, we may call other thing. And then we're gonna subtract here 16 bytes from the stack pointer. And this essentially allocates that memory for our local variables, right? So all of these variables are offsets negatively of EVP. Yeah. What happens when you have situations where you, so you need to keep track of that base pointer? How does it? Yes, we'll see that. Okay. Yes? Where are we allocating 16 bytes for three? Yes, compilers, that's why. They like to have offsets of four or 16 bytes for whatever reason. That's strange, but okay. Yes. I don't know, I think it's, I think somebody did look into it on some architectures. It can be the case that if you access a word or something on not specifically a line byte, it throws errors or I have no idea is the answer. There's lots of reasons. The main thing goes to the compilers. It goes back to assemblies. You remember like 230 when you had to align words in assembly, that's what it goes back to. Yeah, but the problem is on x86, it doesn't happen. So the compiler doesn't have to do this. Right? And these are all words, right? These are all four bytes. So it could just move it down 12 instead of 16. Right? So it actually does kind of an extra four bytes of padding. But it's actually aligning it on like 16 byte boundaries for some reason. I don't know. Because it can. Because it can, yeah. Okay. Then it's gonna move 10 or hex A into EVP minus C. Right? So that's the syntax here. If you take EVP minus C and dereference it. So this is copying 10 there. Right? So this exactly is this operation here. Put 10 into the memory of EVP minus C. And then we're gonna move 100 into EVP minus eight and we know that EVP minus eight is B from our mapping up there. And then it's gonna move, now we have our weird, where it's gonna move the float value into EAX and then move EAX into EVP minus four. Right? Which is consistent with our mapping we have there. Then it's going to, oh that's right, this is the addition, right? So it's gonna, oh, so this is actually the weirdness. For whatever reason, so in the other one we saw it used load effective address to do the addition. Here it's gonna move EVP minus eight, which is B into EAX. It's gonna add EAX to EVP minus C, which is C, like EVP minus C, no, which is A. Right? So it's gonna add B to C, B to A and put it in A. So here it uses the add part when before it was not using it. This is why it's called evil. It's not evil, it's cool. It's evil. You just have to kind of give these unique names in your head. Like these minus C. Yeah, it's called AB and C. Exactly. Yeah, and you know, actually the cool thing is if you didn't know that table, right? You can look at this code and you can see this line here moves and the source code moves 10 into A. So that means A must be at EVP minus C, right? So if you don't have that table, right? You can infer that from the source code. So how does this get into function frames? So let's look at how the frame looks with this code here. So we have our stack, right? Our stack is currently pointing at 10,000 hex. We have, who knows what else above us, but remember we can't touch that memory because that memory is good memory, right? It's safe memory, we can't touch that. So the stack pointer's gonna be pointing to here at 10,000 hex. And we have registers, EAX, ESP, EVP. So at the start, the stack pointer points at 10,000. Is it always gonna be 10,000 when we make this code? No, it could be anything, right? That's the beauty here. So now this kind of gets to the point. This is gonna move the stack pointer into the base pointer, so it's gonna make the base pointer point to where the stack is. So in essence, we actually just killed whoever called us. We overwrote whoever called us as base pointer, right? So we'll actually see that that could save on the stack above us. Then we're gonna subtract 10 from ESP to move the stack down. So now I have two arrows here. One is the base pointer at the top with 10,000. The other one lower than that is the current stack pointer. And so just, so now we're moving 10 into EVP minus C. So I have all of the four byte offsets here. So EVP minus C is gonna be what? One, two, three here. So it's gonna move 10 hex A here onto the stack. It's gonna move 64 here onto the stack. And then it's gonna move the floating point value for 10.45 into EAX, right? And then we're gonna move that into EVP minus four, which is C, right? So we can look at it, we can see, okay, this is the layout of all of the variables in our program. So this is main's function frame, right? And it only has, what we saw, it only has local variables. So these are the only things we care about for that function frame. Questions? So now we're gonna move EVP minus eight, so EVP minus four, 64 into EAX, right? And now we're gonna add EAX to EVP minus C to A, and we're gonna store that back into EVP minus C. So that's gonna be 6E here, and then we're gonna be done. So now we can see we can map all of the values, all of the variables to their actual values, and then we know exactly where they are located in the memory of the function frame of the program. Questions? So we're gonna stop here for now and we come back, we're gonna see how we can do, we can use this to do function calls and how function frames relate to function calls.