 All right, yeah. So this is what we're being creative about, right? Yes. So when we submit it, it says syntax error, and that has not been given to me. Syntax error is here. So these are the, basically, if you don't get the syntax errors correctly, then you haven't implemented the parser correctly. So those test cases are a 3 out of 3 passing means you've passed all these test cases. If you don't pass the syntax error test cases, then your parser is not parsing correctly. So if we get, let's say, 2 s, it's going to be 100%. Yes. It's got to be 100%, because that's the basis of everything, right? If your parser doesn't work, then how do you type that? So you still get 15. Yeah. It says 5%? 5.0, yeah. It says 5 points extra credit for implementing the parsing for case, caseless, and switch statement. However, there's the parsing, but then there's also the type checking for those things. Correct. So is there also extra credit for type checking in case? These are all the sets. So you have to implement it fully, right? At the parser, you have to type check. So if I only do the parsing, do I get two of the five points that says 2 out of 2? I guess. So I wouldn't be 2 of the, yeah, it would be. OK. Well, the other one's at 6. Actually, it says extra credit. Did you pass all the syntax cases for 5? But I didn't pass all the extra credit cases. Then yes. Then I guess, yeah, you get two points for doing the parsing. OK, I see where you're coming from. But yeah, but you didn't do the type checking one, so this is, I guess it's implementing parsing and doing all of these, right? OK. Yeah. In the same text, basically we are passing your message body. So let's say you might create the list of the parser and put in some message body, which is not the same. Are you calling the syntax error function? Are you calling the syntax error function? Yeah. Then that's fine. Yes. So wait. Before you answer this, let me make sure this is recording correctly. Actually, I don't know how to check that. A claim has been made that if you don't pass all of the 15 for type error 3, you get none of the 60 points. Oh, it's just like before. You get percentage based off of the total, right? So all of it towards 60 points. If you pass whatever, 50% of the test cases, then you'll get 30 points. Any other, yeah, probably four questions? Yeah. Yes, you can modify whatever you want. Yes, you got free reign to do whatever you want. As long as it's still working, right? Yeah. And then I have one more question. First, can you update me? Yes. Do I consider one line or four lines? Well, what did you just say? So what's the purpose of printing out the line number? To align your error. Yeah, to find the error, right? So if you're a human and you run your code and it says, hey, there's this type error on line 4, how are you as a human going to know which line to look at? You look at line 4, right? You have four new lines in the program. You go down, that's the line, right? So it doesn't matter statements or anything like that, right? It's not saying you have a problem in this statement here. So that's why you should just use the line number that's already there in the program. So you shouldn't have to do anything extra for that. It's just, and the other answer of why are we also printing out the line number, well, it's also because you have to actually implement it, right? I know you just say there's a type error somewhere, and you'd be right some percentage of the time. So here you have to say exactly what line it's on. So it would be more precise. Other questions? We can spend a little time talking about project 4, except for me or no. So the expression contains pieces of information, the left operand and the right operand. I never see in your parser that you set, I've printed it out to figure out how it stores things. To the left is the first number. If you have like D equals 7 plus 6 plus 5 plus 4, to the left will be the 7. Then you go again, then it will be the 6, then you go again, then it will be the 5. But I never see where you set the right operand, people. So the right operand is going to equal the number instead of an expression at some point, right? It's going to equal a primary. So I have an expression at some point. Is that in the type? So like, you know how the expression node has a type? What gets set in the expression node when it's parsing? So it's kind of asking what is the code like, right? Yeah, it's like if I write a recursive method to traverse that, and then I get to type that's a primary, that's when I stop and start building it back up. I guess it's whatever the parser does, right? So you have to look at how it's parsing. You have to look at that case. So when does it terminate, right? When does that parser, the expression parsing stop? And remember, so here there's actually a lot more involved. This is just to get the precedence right here. So we have expressions, and then terms, and then factors. Is it never a parsed term or a factor? Right, they're rolled up and considered expressions basically, right? But there are functions for parsed factor and parsed term, right? There's a function called factor and a function called term. So it would almost be like maybe where this factor is, where it's going to real number, num real number ID, probably something in there that's a flag that tells you what that thing returns, that it's one of those. Anybody else want to ask a question? We can keep doing this, yeah. My last question is, if you have an assigned statement. Are you sure it's your last question? Yes. Why is it that it's not? I mean, you just don't want to make sleuthing statements. My last to this last question, lots of questions. So the assigned statement, it has the ID and then it has the expression. Let's say the ID is not declared up top. So it's a general type. It's a non-constrained type, implicit type. Implicit, there we go. If it's an implicit type. Actually, let's claim that it's an int. And then what this returns, the right side of the expression returns an implicit type. That means I have to go back in and go all the way down and find everything and set them all to int. By an a plus b plus c plus b plus e plus f plus h plus i. And they're all implicit. And then they all equal the assigned. Is that what you have to do when you're doing an Indie-Milner type checking? Yes. If they're all implicit, then they're all going to be the same type. Yeah, exactly. The question is, do you have to traverse down into the expressions again? That's up to you. I mean, are you keeping track of? Because if I use a again somewhere else in the program, it must be an int. Right. So where's the type of a to type? In that expression chain that's going crazy. Oh, no. It's got to be a type for every variable in the program, right? When you get to a. It's not if it's int. It just shows up. Yeah, it's a new type. You have a new variable with a new type. But I don't. So it'd be just the same thing as explicitly declaring it with a type that is not a built-in, right? With a new name. So you should be able to create in whatever you're keeping track of the variables and their types. You should be able to create, hey, I saw this variable a, and it has this type. And its type is generic. Great, it's a new type. It's a new type, it's not generic. It's a new type. Yes. And then once I get to the bottom, I start coming up. Once I start coming up, then all of a sudden I've lost that I was there once, and then I need to go back again. So it's all about the constraints, right? So we have the constraints of our type system here, right? So you're saying that, where is it? The assignment? Basically, so assignments cannot be made between variables of different types. So it's another way of saying this. The types must match, right? The left-hand type has to match the right-hand type. Exactly. But the right-hand type could be an expression that's like in length. Great. So what's the type of that expression? If they're all new types, and this is an incident, then what constraint do you have now? What constraint do you have? That that's an int. Perfect. All these are ints. There you go. And so if you see any of those implicit, that same variable, and it has that same implicit type, then you know it's got to be an int. And if it ever occurred where it's not an int, Yes, for how I implement that. That is the question. That's what you have to do, yeah. So you should be keeping track of the variables and the types of the variables in your program, right? Sure. And you can also keep track of, maybe let's say, declared types and current types. If you could reduce every type, maybe, down to basic types that you can. Or just like you did when we did Hindi Nolner, right? You are Hindi Nolner. You go through and you update and say, hey, all of type C, or whatever, new type A, they're ints. And now later, when you see an A, you look up in your table and you see, oh, A is a type A, which isn't int. Perfect. Do you have any more questions? Anybody else have questions? Yeah. Would you say that then it's easier to do some of the type checking as you're building with parse tree instead of just doing it all at the end of the parse tree? Does somebody else want to answer? Somebody did, yeah. It's always easier to test smaller pieces of code and larger pieces of code because you can't isolate where your arrays are. Yeah. So you got to think about the parsing functions, right? They're starting at the top. They're parsing the whole thing. I mean, you can do it this way, right? And this is how if you're doing it, let's say, as a real, as part of a real compiler, I would separate the parsing from the type checking, right? I have parse and then I have type checking, type checking the entire structure. But for something like this, it's going to be a lot easier to do the type checking while you're doing parsing. But you have to be very careful in that you need to type check when you have enough information to type check. Does that make sense, right? So for instance, if you're looking for the parsing function for all these, does it make sense to type check an ID list? No, it's nice to know, why not? Right, so an ID list is just ID, comma, ID, comma, ID, comma. With no context, there's no types here, no nothing, right? So it doesn't really make sense to type check there. But what about maybe a variable declaration? So why does it make sense to type check here? Yeah, because up here, we've parsed. OK, we know there's, so we're declaring variables, explicitly declaring variables. And what are the names of those variables? That's dangerous. What are the names of the variables? Where do we find the names of the variables that we've just declared? In the ID list. In the ID list, right? Yeah, and by the time that we know we have a valid variable declaration, right, when our variable declaration is valid, then we know we have a list of IDs and then we have a type name, right? So I can go through this ID list and I can get all of the variable names. I can declare those variables. And what type do all these variables have? Type name. Type name. Yeah, whatever type name is. And it's just in my data structure I can just grab it. I would say for the stuff that's given to you, yes. But remember, we're not giving you a print condition or a print switch or a print while, right? So you have to write those. So if you write those correctly and parse it correctly, then you're fine. Is that against? Like is it possible that you messed up the printing but the parsing is actually correct? Yeah, it's totally possible, right? But it should be fairly simple when you're printing it out to kind of eyeball it and know, yes, that's correct or yes, that's not correct. Yeah. Just for debugging purposes. Just for debugging purposes, yeah. You should not print the tree out at all when you submit. Anything else, yeah? Just like you're asking, if you wanted to do it so that you had your parsing and your type checking separate, how would you keep track of, like, line numbers? So if you're trying to do it separate, do your type checking separate, how would you keep track of line numbers? Yeah, so you could build them into the parse tree, right? So each of those nodes in the parse tree is like an expression, you could have one line number it's on. And then after the fact, you can say, oh, there's an error here with this expression, I think I can say exactly what line number it is. Yeah, so you're free to add, you can add more data to the parse tree structures if you want, you know. Just up to you to set those and make sure that they're valid. All right, anything else? Before we get on to new material, yeah? I built your parser completely and you ran all the test cases on all the test cases that are given to us. I'm still getting two out of three on the syntax error. It's kind of hard to figure out where you're not parsing correctly if you're passing all the parsing cases. So it sounds like the parsing cases are checking syntax errors, right? What I'm saying is I have all my test cases and I'm printing out my parse tree after all nine test cases and it's passing for every single one. So I don't, but I'm still on like one submission. I don't know what to check from here on out. So the problem is that you're passing two out of the three syntax error checks, right? Exactly. So do any of the test cases would give you for a syntax error? No. So you got to think, okay, what we gave you is correct, hopefully, in parsing the rest of the grammar. So those syntax check correctly, so it's probably something in the code that you wrote that's not checking the syntax errors correctly or not calling the syntax error function like it should. So you can create that, right? You have the grammar right here. If it's while statement, create a grammar with a while statement that doesn't follow this, right? Yeah. So does that mean you're testing for like syntax errors within the test cases? There are test cases called syntax errors. So yes. No, I mean, like you're given an invalid program, yes. Yeah, if you've given an invalid program, it's got to put out a part of the syntax error, right? Just like we've been doing on like the homeworks in the midterms, right? When you wrote a parser, you had to say when there's a syntax error, right? Just like here, because you can't type check a program that's not syntactically valid, because it's not a valid program. It doesn't matter if it's a type. It doesn't even make sense to ask the question. So it's not a valid program. Yeah. Is it safe to say we don't have explicit declarations inside the expression? Say that again? So we have implicit declarations inside of the expression. So like if you have the assignment state in D, where D is an implicit variable, then on the right-hand side, you have an expression, and you put another implicit variable, is that allowed? Is it disallowed? So what does it say about implicit variables, you see? So what does it say here? Yeah. Right? So where's the body of the program? Right? But the program, we have our declarations in the body, the declarations are type declarations, the variable declarations, the body is everything. So yeah, but it doesn't matter if there's a statement, condition, whatever. You see an ID that you've never seen before that doesn't exist in the declared variables. Or it's not when you've seen already implicitly, it means it's a new implicit variable that should have a new implicit type, or a new unique type. Yeah. Yeah. So types are not a variable. Types are what? Types are not a variable, it's just variables. What happens if types appear in the body as variables? Error, right? There's one of these errors. Four. Four, yeah. One of them is exactly, yeah. Variable re-declare is a type name. Or variable. I don't think it will parse the type of the body. It won't parse if you put a built-in type that you could try to put a type name. If you could use it as a variable, then that would be an error. Yeah, we'll get one more. We should, I don't know how to answer that. You can make sure you're parsing correctly. So if you're not doing it correctly then you're accepting invalid programs or you're accepting valid programs. Like you're accepting things that shouldn't be accepted or you're not accepting things that should be accepted. Is something about the entry-syncing error? All those test cases are checking if it matches, if the output matches, right? So either you're specifying a type-free error on the wrong line, like if you want an issue, you're throwing a syntax error when there shouldn't be a syntax error, that would be another issue. Or you're saying one of the other type errors should be a type-free error and those other errors don't exist. So yeah, I can't, I don't know, I'm not. They're just like looking at a program and then expect it output. If it doesn't match, it doesn't match. All right, one more question? Extension? Extension? Extension? Highly likely not. Maybe you want to gamble on it, though? Yeah. Okay. Okay, well go ahead and call my bluff and see what happens on Wednesday. We're going to have a very sad Thursday. Anything else? It's more about the content rather than creating or something like that. Yeah, oh, yeah. Is that what we're talking about? It's a pointer, or no? I don't know. Okay, sorry. So if we have an expression like A plus B, where both of them are like undeclared, just implicit types. Yep. Okay, we don't know what types there are. There's things with B. Does it follow the, does it, let's see, does it follow all the constraints? And like, if under that, we say like A is equal to one, and we would have to declare A as an integer again. We're going to have to clear it, you're inferring, right? Well, we inferred that as an integer. And then what does that say about the type of B? So then A has to do with that. Yes, exactly. So then if B is later on used as a Boolean or something, right, then that would be a type error. Yeah, perfect. So yeah, so just like in the Hindley Milner, right? So you see an implicit variable, you give it a new type. Now, the only thing you know about that is it has to pass the constraints. And so in a, in these operators, the only constraints that we have are they must be the same type. We don't know what those types are, but they must be the same types. So A and B have to have the same type. And then later on, if we see an assignment, we see, oh, they have to be assignments of the same type. So that means they have to both be integers. Yeah? So you said, I think I opposed that there was an error with one of the test cases. Correct. So if we had submitted prior, are you changing that? They all got re-graded, so you should look, and if it didn't fix that one, then it wasn't a problem. But you'd already submitted successfully prior to that. It's fine, it should work, it should work. Like we just barely updated the test case to get rid of this weird scenario. So if we submitted successfully, would it work? Yeah, yeah, yeah. Go check, make sure, but yeah, if there's any problems, email most of them. I thought I saw another hand in this direction. All right, cool. Let's get back to the run sign environment. All right, so on Wednesday we looked at the, here we're looking at the calling convention and specifically how arguments are passed from one function to another using the stack at the actual implementation level. So we saw a function main that calls a function callee with values 10 and 40, takes that integer, gets back and then returns it. Callee just takes A and B, adds them together and then adds one to the result. So we actually looked at the assembly code if we won't go into right now because we're gonna get into it in a second. But we saw that main has a bunch of code where essentially it's setting up and passing all the parameters onto the stack and calling callee. And then we talked about the prologue and the epilogue and then we looked at callee and we saw that it had code like that. So just a slight recap, it's returning the value in Eax. So looking at this bottom of the top, it's adding one to what's in Eax. Here it's adding what's in Epax to Eax into there and here it moves each of the arguments A and B into Eax and Epax. So let's look at an example of what this code looks like, exactly the stack looks like as this assembly code executes. So here we have the function, the callee function, the function that's going to get called and here we have the main function. And so before we didn't worry about where this code actually was, but now because it's important to the semantics of call instructions and jump instructions, I gave each of these memory addresses, so these I ripped from, I believe these were from the binary itself. So this is where GCC actually put them in memory. So for whatever reason, callee is like right above main or below depending on how you look at it. Okay, so then we have our handy-dandy stack. Memory, the higher memory addresses are where on the stack? At the top, low addresses at the bottom, and we have our stack layout. So then we're going to look at only the registers that matter for this. So right now, we only really care about EIX, EDX, the stack pointer, the base pointer, and EIP, what's EIP? Something, what was it? Instruction pointer. Instruction pointer, what's in the instruction pointer? Yeah, so what's in EIP is the address of the next instruction to be executed. So we'll review from your architecture class. It's like, so what happens when instruction is executed? Was that something we want to, yeah. Well, it goes to the next instruction, but so the processor fetches what's in the current instruction pointer, decodes that as an instruction, then the CPU basically does some kind of execution depending on what that instruction is, right? Depending on if it's add to registers and put it in a third, it sets all that up, and then it increments the instruction pointer after that, so it actually executes to point to the next instruction. And of course, we have pipelining and all this kind of crazy stuff. All this stuff goes out of the window. And the thing I think I didn't mention, you guys learned about or heard about microcode? Some of you? So your actual CPU, like the CPU that's in all of your machines, if it's an x86 machine, it's not actually implementing and executing these instructions directly. It actually breaks them down into what's called microcode, which is specific to the processor, but that's what it actually executes on. So yeah, so hardware and architecture and all that stuff is way crazier than what you learned about in like MIPS classes. But from our point of view, from this view of looking at x86 instructions, all of that complexity is abstracted from us. So we don't have to worry about that. We can just think about it at this instruction level. Yeah? What's the difference between microcode and microcode? Because I thought the next level of that from the center was microcode. No, so bytecode, well, so bytecode is usually referred to in terms of like a virtual machine, like Java or C sharp. So like the bytecode is essentially the assembly language version of Java. Like a Java program gets compiled through a bunch of bytes, which are bytecode, but they're not x86 instructions. They don't execute directly on your hardware. There's a Java virtual machine that takes those instructions and executes it and essentially translates it into the underlying assembly language. Anything else? Okay, cool. All right, so let's just assume that our stack pointer before we get to main is somewhere here at the top of the stack. We'll give it a slightly more realistic-ish memory address than what we've been looking at before. So then, so if I tell you this at the top of the stack, what does this mean about the register? What do we know about the registers? ESP points to? ESP points to what? When you say points to, what does that mean? Yeah, so that value fd2d4 is stored in ESP. And so really, this pointer of the stack is here comes from the fact that this value is in here, right? So it's not just like I arbitrarily decided, it's like whatever the stack pointer was. So I don't know, in case you're wondering, I did take like a break point at main and this was where the stack pointer was, but there's a bunch of Fs before it, but that just like doesn't really add anything, right? Okay, so if we're about to execute the first instruction here, so before we execute the instruction, the value in the instruction pointer is gonna be the first instruction of main, right? So this is the instruction that's gonna be executed next. Okay, so where am I? Okay, got the instruction pointer. Do we know anything about what's in the base pointer register before main executes? Is there a real value in there? There's the garbage in there. Garbage. Garbage, why garbage? Because we haven't moved the stack at all. So what's the calling convention that we've been looking at? The C, Cdecal calling convention, right? The call e does what? It sets the base pointer. Not only sets it, but before that, before it can set it, it saves it, right? Yeah, it has to save the base pointer because what's in this base pointer? What was it? The top of, yeah, whoever called us frame. So this is the base pointer of whoever called us, right? So what do we know about that value? So is it above or below this FD2D4? Above, right? It's gotta be above us, because somebody else called us, so their frame pointer's gotta be above us. So we know it's just something. Let's just say it's C0. That is above, yes, okay, perfect. Okay, so yeah, so there is something in there, it is something important because it belongs to whoever called us. Okay, so we are going to, okay, so we're gonna push EVP, oh, excuse me. So the, I went too far. Okay, so we're gonna push EVP. So what's the first thing we do when we push something? What do we what? Yeah? We're gonna put it onto the stack. How do we do that? It depends on the semantics, right? So we're going to, we first move, we make room on the stack for our new value. So which way do we move the stack pointer? Down, yeah. So we first decrement the stack pointer by four, which then moves our old imaginary arrow down four bytes. Then we put that value. So we take whatever's in EVP and put it on the stack, which is FDC two zero. Okay, and then we move the instruction pointer down to the next instruction, we just executed it. So now we're pointing at this next instruction. So now we've completely finished executing our last instruction. Okay, now we're gonna move the stack pointer into EVP. So what's this doing? Semantically? Setting the new base pointer. Yeah, setting the new base pointer. Who's new base pointer is it? For us, yeah, exactly. For this function, right, it's setting our base pointer. So we say that wherever the stack currently is, that's gonna be our new base pointer. So we're gonna copy FD two D zero into the base pointer. Yeah. Wait, why is it? Because what changed the stack pointer? So what did this push instruction do? Right, which moved, did what to our stack pointer? Oh, moved it down. Moved it down, exactly, yeah. Didn't you, Richard, when you said it moved down? Yeah. Perfect, yeah, you just gotta put it all together. Yeah, you're moving it down, right? So it means now it's permanently moved down. So now that's the current bottom-most location of our stack. And then, so when we put ESP into EVP to set our base pointer, we just copy that value over. So now they're both pointing at the same location, the stack pointer and the base pointer. Okay, then we subtract hex 18, which I think we said before, what is that, it's 24. So it's moving the stack 24 down. So when we do that, let's see, say it's 2B8, I don't know, we can verify, which then is gonna move this pointer down how many of these squares? Six. Six, yeah. So it's gonna divide by four, right? So each of these is like an offset of four. So it's gonna move it down to V8. Oh, I did this math right, who knows? Okay, so then we move on to the next instruction. Now we're gonna move 28 into ESP plus four. So Y, hex 28, 40, and what is 40 important for? The variable, so yeah, it probably makes, so we were looking at the line of code as A is able to call E, parentheses 10 and 40, where the actual parameters we're sending to call E is 10 and 40, right? So 40 is the second most parameter, so we know it goes higher up on the stack than the left most parameter, and this is what we know from the calling convention. So we're gonna move the constant value 28 onto the stack pointer plus four. So the stack pointer plus four is FD2BC, then we copy the value 28, put it on the stack there, one of the next instruction, now we're gonna move hex 10, what's hex 10 in decimal? Or hex A in decimal? Yeah, hex 10 in decimal 16, that should give you the wrong answer. Okay, yeah, so we're gonna move 10 onto the stack pointer, and we know where the stack pointer is, FD2V8. So that's gonna move that on there. So now what are the essence of these two instructions done? Yeah, so they're providing the parameters. So in essence, these push those values onto the stack, right? They essentially did a push OX28 and a push OXA, but for whatever reason, the compiler just decided to kind of make the space there and just put them directly onto the stack. Okay, so what are the semantics of the call instruction? What does the call instruction do? Yeah? I'm sorry, I have a question. Sure. For the total of the stack, is that a typo or am I just not understanding? Is this supposed to be FD2C0? This is the value that's on the stack at location FD2D0. And where did you get that number from? That number was what happened to be in EVP when main got called. So this is the same base pointer. So this first instruction here was EVP. So this is just kind of like a, to be honest, I'm not entirely sure where it comes from, but main is not the first function that gets called because if your program does dynamic linking and needs any libraries, other things need to happen. So there's actually other functions and other code that gets called before your main method gets called. So this is essentially what happens. If somebody calls main, and it means it had a base pointer, and so main is just a function, right? We can have other functions. We can have colleague call back into main, right? There's no reason we can't do that. So yeah. It's not supposed to be D0, it's supposed to be C0. It's C0 is pretty arbitrary. It just means that it needs to be something above us on the stack. Because somebody, there was the base pointer was pointed, you guys can't see when I go to the front. The base pointer was pointing somewhere up at the top of the stack above FD2D4. And so the first thing we do as main, because that's the calling convention, the callee has to save the base pointer before it uses it, right? So it's pushing it onto the stack and then it's copying the stack pointer to the base pointer. Yeah. Is it C0 lower than C0? So it was like, no, no, the C, it is, yeah, it's a good point. Let's add another F here or something, I don't know. It's like way too difficult to fix these types once these things are done. Yeah, it's a good point. Yeah, it should be like E as probably what it's going for. But yeah, it's a good thing because this isn't actually a computer, right? It's a PowerPoint slide. Okay, so what are the semantics of the call instruction? Yeah? It does a jump there and it puts that memory location into the execution. Yeah, so the parameter to this call instruction is the location that it wants to jump to, right? So think semantically. We need some way to call and jump to another function. There's two, in assembly, there's kind of two types. There's a jump, a direct jump. So it says, hey, always go here. Call is also a direct jump, right? Because we always start executing at this 8048394, which is the start of call E, right? We're gonna always go there. But the side effect is we're gonna push onto the stack the next instruction that would be executed after this call instruction, right? So think of it, how many places do we have to store the instruction, the currently executing instruction, or the next, it should be the next executing instruction? One, we have one register, right? So if we go jump to some other code into this call E, what's the instruction pointer gonna be here? The instruction pointer's gonna be 8048394, right? This has gotta start executing in there. It's gonna execute down. But how does it know to go back? Right, store it on the stack because we don't have an instruction pointer for that. Exactly. So that's what the call instruction does. It's like a two and one. So it starts executing there and it pushes the next instruction onto the stack. So it's going to, I think, yeah, so when it pushes, right, it's gonna actually decrement the stack pointer by four and it's gonna push the next instruction and the next instruction here is 80483BF and then it's gonna set the EIP of the instruction pointer to 8048394, which means it's gonna be pointing here. So this is the next instruction that's gonna be executed. Any questions on that? Right, so this is just, it actually is, it's just another example of the base pointer, right? Somebody's gotta save it because we need a new one when we're executing this new function. So you just have to set up who saves it. In this case, the caller pushes the return value onto the stack, which also makes sense because it's the one that knows where execution should go to when that function returns. Questions? Okay, so then what's the first thing we need to do in the new function? What was that? Yeah, we need to save the function that just called this base pointer, right? Because we're about to overwrite it because we want our own base pointer. Exactly, so which part, what is, are these two lines in the, so these two lines are identical, right, in main and call E, so what are these, what do we call these? Prolog, yeah, okay. I always get them mixed up, see. Guess I need to read more books. Yeah, so the prolog here is everything that deals with the calling convention that when it gets called has to do and the epilog is everything that has to happen to exit that function. Okay, so we're gonna push EVP onto the stack which says what to the base pointer? Decorates it by four, which moves it where? Down, yeah, we're gonna move it down to put the base pointer onto the stack. So it's gonna be moved down, the base pointer is gonna be pointing to B zero and then we're gonna put the current base pointer which is FD2D0 onto the stack and then we go to the next instruction. And here's where we set up our base pointer and say, okay, we're getting called, we need to set up our own frame, our own base pointer. So we'll move the stack pointer into the base pointer. So now both the stack pointer and the base pointer, are pointing here at two B zero. And so the only way we have to get back that base pointer is here on the stack. And so let me kind of see how we have the frame before main's base pointer is saved on the stack and main's base pointer is saved on the stack. So each piece of these frames is on the stack and so all that information is stored on the stack. So that way when we return to that function, we can make sure that everything is right before we called it. Okay, we're gonna move EVP plus C into EAX. So what's EVP plus C? This is like the easiest of the hex addition you could do. What's zero plus C? C, so EVP is now B zero. So what's that BC? 40, right, value 20. So how does it know C? How does it know to go up C? Because I told it to in love instruction. Did I write any of this code? No, I don't care. As a C programmer, I don't care about pointer or I do care about pointers. I don't care about registers and stacks and all that stuff, right? I don't care about that. It's the first parameter. The 40 is, I think, the second parameter. But yeah, so, but why, so, yeah. These things are four bytes. Things are four bytes. Yeah, so I always lose those four bytes. But why C? Because that's four bytes from A. Why A? Because that's four bytes from the back to the sides. So okay, let's get, let's talk about one thing first. We talked about local variables. Where are they offset from EVP? Negative or positive? Down the stack, negative, right? It makes sense, all right? When we move the stack pointer down, we're essentially allocating space on the stack from the base pointer down here. So our base pointer used to be up here. We haven't gotten there yet. We can sneak ahead and see that minus four EVP here, which would be minus four here, that's our value A, our local variable A. So that's local on the stack is negative. So why, so then, what are these? Why a positive? Yeah, almost, yeah. So we need to, so kind of where you define EVP is pretty arbitrary. You just have to make sure you're consistent, right? So really, this frame starts here in some sense, right? Because everything from here to the bottom of the stack is important to call E, right? There are the parameters, and then there's the return value, which isn't really important for the computation, and then there's the base pointer. So the important thing is that the compiler knows, okay, I know I wanna access, let's say, the first parameter, or in this case, the second parameter of the function. So from my current base pointer that I know I just set up here, what's four above me? Or what's, I guess, what's at the base pointer? What was it? The previous base pointer. Yeah, the previous base pointer. And then what's above that? Not yet. What's this? Is this a parameter? No, it's the instruction to return to, right? So the saved EIP. And then what's above that? Parameters. The first parameter, exactly. And then, so eight up the stack is the first parameter and then eight plus the size of the first parameter is gonna be the second parameter. And eight plus the size of the first parameter plus the size of the second parameter is the third parameter, and so on. And so the compiler knows, hey, I can compile this function and since we're speaking the same calling convention, I know exactly how to calculate that offset because I know exactly what the stack's gonna look like. I may not know the exact values, right? I'll never know what the saved EIP is on the stack, but I know those first four bytes above the, above EDP are gonna be the saved base pointer and the four bytes above that are gonna be the saved instruction pointer and then above that are my parameters. And so you can calculate all that, right? It's not gonna change and so that's what the caller, what main did was it push, it created this stack by putting 28 and A on the stack in this order so that the callee knew how to find it. Yeah. So if a compiler learns how many parameters are being input from function during compile time. Yes, it looks at it, right? So just like you're looking at your parsing structure, just like with Hindley-Millner type checking, right? With our tree, we can see, hey, this is a call, this is a function call and what's the name of the function? Okay, foo is on the left and I know exactly how many number of parameters they are and then I can type check that to make sure there does exist a function foo with that many parameters and then once I know that, that that function does exist and I can create this code here to call that. Any other questions? Okay, then we're gonna move, okay, so we're gonna move 0xc, so we're gonna move 28 into ex. Okay, so yeah, this is where I have the, so basically here is everything on the stack that belongs to main, right? This is main's frame on the stack. And yes, the main's base pointer points to b0, so really you kind of gotta think about, well, what things is it responsible for? Similarly, like calle, right? Pretty much everything here belongs to calle, so these are the parameters, the local variables, if there are any and all that stuff. So any questions on that? And so you can think about it conceptually while they're being called, right? Is it's literally a stack with all these frames on top of it so you get to the currently executed function. So is it possible to access from calle main's parent's base pointer? Add a certain amount to the stack or the base pointer that would go up with the stack? Right, so we know our calle's base pointer, right? So if we can get to our parameters, right, we can get to the saved base pointer. Right, it's actually at evp. So we could get that value, we'd go look up that value. Where does that point to? Yeah, the previous base pointer up here at d0, right? And then we can actually grab main's parent's base pointer. You can actually walk up the stack, so anyways. So yeah, it'll come up later, probably in a week, but. But yeah, so all these things are saved. So because you know they're all at fixed offsets of an evp, as soon as you get to a base pointer, you know how to find the saved base pointer and then you could go to that base pointer and we could get to that saved base pointer, all that fucked up. Okay, so we're gonna move ox28 into eax, which we've just done, and now we're gonna increment the instruction pointer. Now we're gonna move evp plus eight into edx, evp plus eight is, how's the value? Was it? Was it? OXA. OXA, XA, yeah. So we're gonna move 10 into edx, and now we have this fancy load effective address which is essentially gonna add edx to ex and store the result in ex. So that's gonna add hex28 plus hexA. Anybody know what that is on the top of their head? Anybody know what it is in decimal? 50, yeah, so whatever 50 is in hex, which 32, is that right? Sure. Yeah. Good. Perfect, all right. So now we move the instruction pointer onto the next one. Now we're adding one, why this add one? Part of the C code. Close it? It's part of the C code. Part of the C code, yeah, exactly. So we're still in the code of our function, right? Our function was return A plus B plus one. So here we've got A, we've got B, or actually we've got B and then we've got A and then we're adding one, we added them together, and now we're adding one to that. And we stored it in EAX. Why did we store it in EAX? Right, so we're returning this A plus B plus one and for our calling convention that A is, EAX is the register where we pass back values. Exactly, okay. So then we pop EEDD. So what's this gonna do, semantically? Yeah, so it's gonna put the base pointer back to main's base pointer. So why do we have to do that? Why does a main do that? And so the real answer is, well that's what the calling convention says. So we're the ones who saved the base pointer. It means that we better put it back before main returns, otherwise main's gonna think it's base pointer somewhere else and everything's gonna break. So yeah, we're gonna pop EVP, right? So we're going to take the value on the stack pointer, put it in EVP and then what are we gonna do to the stack? Move it which direction? Yeah, we're popping, right? So we're taking that value, putting it into a register and moving the stack up. So we are going to take D zero, put it into EVP which we've done. Then we're gonna increment the stack pointer by four. So now the stack pointer now points at B four. All right, then we move on to the next instruction. Now here, RET. So what is the RET semantics? Return, yes. Very good. So semantically for the program, for the assembly, right? So I'll tell you this, it's essentially the dual of call. Is that it yet? We need to restore the instruction player. Right, because we're done executing, right? We need to go back to whoever called us. Do we know where to go? Yeah, we know because it's on the stack, right? But we don't know just off hand because we could have been called from anywhere, right? We could even have called ourselves so we may be jumping back to the start of this function. So the semantics here are, let's see, oh, I think it's pretty easy actually. Pop the value currently on the pop into EIP. I think it's equivalent to that. So take the stack pointer, take the value of the stack pointer, put it into the instruction pointer and move the stack up. So by putting it into the instruction pointer, we'll now start executing at whatever the value was that was in here. Which is where? What's 804.83 BF? Yeah, the next instruction after call, right? It's exactly what we want. We want to call this call E function and then we want to do something and then we want to come back right after when we called in. So that's how this works. So Rhett is going to take that value, put it into EIP, then it's going to increment the stack pointer, move the stack pointer up and now the next instruction we're going to execute is going to be here right after this call instruction. Questions on how that works? Okay. So then we're going to move Eax into EVP minus four. So what's in Eax? The return value. The return value. Well, what is it? We have to know this. Well, it's here, right? It's 51, right? So we're going to move Eax into EVP minus four. Where is EVP minus four on here? One down to the base pointer. So here, yeah. So we should move 33 into there. So that's good. Oh, and so that would be at FD2CC in case you were wanting to follow along. Okay. Then we're going to move minus four EVP into Eax. So what's minus four EVP? Yeah. Add just where we just stored into 33. We're going to move that into Eax. Why are we doing that? What did main return? A. Yeah. So returns the value A, which is the return from that function call, right? So where does main put the X return value? Eax, right? Yeah. It's the calling convention. Doesn't matter if it's the main function. Doesn't matter. Any function has to follow those standard. So we're going to take that value, put it into Eax, which I just can probably see does nothing, changes nothing. Okay, and then we're going to call a leave instruction where the leave instruction is this compound instruction that sets the stack pointer to the current base pointer. So basically it gets rid of anything we've done in the stack. So set the stack pointer to the current base pointer and then pops into EVP. So take whatever's currently on the stack, take that value, put it in EVP and move the stack up. So essentially this is getting rid of these two, push EVP and move ESP to EVP, right? So essentially we're first moving the base pointer into the stack pointer. So that gets rid of this one, right? So we're first, we're going to set the stack pointer to be D zero, which is going to move the stack all the way up to the base pointer. And then we're going to pop that value from the base pointer into, sorry, we're going to pop the value that's currently on the stack into EVP, which is doing what? Resetting the base pointer for who? Whoever called name, exactly. We don't know what it is, but we're going to put it back there because we're good at functions. And so it'll be at C zero and then we increment the stack up for right because we just popped. So now the stack's going to be up here. So is the stack where we left it? Yeah, I sure hope so, right? When we started and made the stack was at D four and so right before we leave, it's got to be there. So what's this ret going to do? Not the base pointer. So it's basically a pop EIP. So take whatever's on the stack, put it in the instruction pointer and start executing there and move the stack up. So remember, we did that here to get back from our function call e, right? So right, somebody called name. So we need to return to whoever called us. So that's how we're going to do this return is we don't see it here because to main's perspective, it doesn't matter what's there. We're just going to jump to it and start executing. So any questions on this example of parameter passing and function calls and frames and yeah. So as you go from one instruction to the next, how does EIP know the address of the next instruction? So how does EIP know the address of the next instruction? So on MIPS, how do you know the address of the next instruction? Right, so they're all the same size. So it's always like four bytes or something like that depending on your MIPS system. So here X86, as you can maybe tell by the offsets here, there are variable size instructions, right? So some instructions, like you can see, you'll get the differences between these, right? This means that the push instruction is only one byte, whereas the move ESP into EVP is two bytes and so on and so forth. There can be really long instructions. The whole point is that it knows. So all of this stuff is built into the processor. So it knows when it's decoding an instruction. So it has this whole step where it first decodes an instruction. So basically starting with a bunch of bytes, how many of these are the next instruction? And then once you figure out what the instruction is, then you know exactly what the next value is gonna be. So yeah, it's one of the stages in the pipeline is instruction, like instruction fetch, instruction decode, then a whole bunch of other stuff. Yeah? As soon as you return from college, we don't care about the parameters. Correct. So, but we didn't pop those off the stack, right? Is there any reason why we just let them sit there, what if we wanted to always think two bytes, you know? Yeah, that's actually a very good question. I don't think it's something to do with GCC doing some sort of optimization. Like, I didn't go in and say no optimizations, but so it's doing some, and that's why I think at first, so the other thing is, right, it's reserving 24 bytes on the stack, and then it's using that space that it reserved to pass these parameters in. So it's not reserving space for its local variables and then pushing the rightmost parameter and then pushing the leftmost parameter and then calling the function. So why it's doing that, I honestly don't know. I think it's probably an optimization where it looked at it and was like, oh, I don't ever need these bytes again, so I can just move the stack down, copy them in right there. Yeah, I'm not 100% sure, but it's interesting. So the other thing is, you look at it and think, well, compiler should be smart, right? And then you look at these two instructions, right? Which, so why, so I guess this is a good time to start it out, so why would the compiler maybe want to leave those in here? Yeah, so what if, okay, so what if I call the, so yeah, okay, so I guess it does depend on kind of what you're saying is what the compiler could look at, right? I mean, I guess you can say as a human, you could probably just optimize, remove both of these values. Essentially optimize out that local variable A and just say return, because you know nobody uses that value A, right? But is that always true, right? Is the program, what if it's in a multi-threaded environment where there's somebody has a reference to this value A and is counting on it to be set? I guess it's highly unlikely to happen in our example, but it could happen, right? You could have even, I mean this is a bad example, but you could have memory mapped IO in some cases, so you actually really want that memory address to be written to, and if it doesn't happen then your program breaks and your semantics are all out of whack. Yeah, so I think it does it to be safe, but I think it'd be interesting to look at it as you increase the optimization levels, whether it would get rid of it or not. Any other questions? Yeah. So once all this is done executing everything that's below the base pointer, it's literally just garbage at that point, would it need to do anything? Exactly, yeah, so that's the beauty of it, right? So as soon as the program terminates the OS just gets rid of this memory, right? It doesn't need it at all. Because, so this is why you have to, you really want to, C doesn't force you to, but you really should set your, you want to declare or assign to your variables before you use them, right? Because you can't ever assume, right? It just, C is a very low level language, it just translates it directly to assembly instructions here. So for instance, if you were accessing a local variable before assigning to it, you're gonna get whatever garbage happened to be in that memory, which is dependent on the stack layout. So you don't have to worry about, I think you have to worry about another program getting your stack or something. But yeah, you pretty much, you just leave it, it doesn't matter today. Cool, any other questions? Okay, so we kind of looked at this. So how did the previous, so let's talk about some semantics for a little bit. So how did the previous example pass parameters to that function call E? Yeah. Yeah, it pushes the values on the stack. So what does that mean semantic wise? When you see like a function calling in C, what does that mean semantically that you're thinking about when calling a function? So if you pass a variable A, a local variable A to a function as the actual parameter, in the function, that formal parameter, what does it get? So yeah, it gets the value and the location associated with A, but it gets that value, right? So can it change A? No, right? Is that the only way you could do it? What about C plus plus passing by reference, right? So yeah, there's multiple approaches to parameter passing and specifically the semantics of parameter passing. One is what we call pass by value. So that's what you're used to. So that's, you take the value in the location associated with the actual parameter and you put that value in the location associated with the formal parameter. The other one is pass by reference like in C plus plus. So here you're, well, we'll get to it in a second, but you're binding the actual parameter to the same location, the formal parameter to the same location as the actual parameter. And finally, pass by name, which is really crazy and it's kind of just something to show you as to how far you can push a crazy language semantics. Okay, so for pass by value, so this is very, yeah. Is it a pass by address? I thought there was a pass by reference and pass by address, pass by value. Pass by address, I'd probably say is another name for pass by reference. Because, yeah, maybe it all depends on. Passing a pointer, the other you're passing, like you could just use the variable name. But when you're passing in a pointer, you're copying the value, right? What's in the value of a pointer? Address, an address. An address, right? So when you're using pass by value semantics, when you pass in a pointer to a function, you're copying that value inside the pointer, which is an address, into the location. Pass by value? Yes. Yes, the effects can be the same, right? It can be similar as if you did pass by reference. But the semantics are that you're actually copying that value. Okay, and so here, the values of the actual parameters at function vocation are calculated, right? Because it may not be a, you know, you don't have to pass a variable as an actual parameter. It can have an expression, right? Arbitrary expression. And it's copied to the function. So we've seen in precise detail about how this is done for C. And that, so in that example, right? Copies of those values were placed onto the stack. Okay, so let's look at an example that has some garbage answers here. Okay, so we have an integer x. We have a main function. This main function has a local variable y. We call the function test y. And then we print out the value of f, the value of y. Sorry, and then we return y. So in our function test, takes in as its formal parameter x. So when we refer to x in this function, which x are we referring to? The one right after test, right? So I guess one thing, are we talking about static or dynamic scoping? But here, that answer doesn't really matter. But here we're talking strictly static scoping. So this x refers to the parameter x, right, that formal parameter x. And so here we're gonna add five to x and we're gonna print out the value of x. Okay, so when we run this, so is this going to type check? Okay, what's the value when we run it? If I somehow block out some of those floating numbers. So why is nine even printed out? Because there's a print declaration in test. Because there's a print declaration in test. And so what's this print f printing out? Parameter x with the test. The parameter x, the value of x, right? And so we pass in four to test. We add five to that, four plus five is nine. And then, so we print out nine. And then when test returns, how come y wasn't changed? Right, it's passed by value. So we took the value that was in y and we passed it as x. So here, going back to our box circle diagrams, right? So here in main we have the variable y, which is bound to some location. And what's the value inside this location? Four, exactly. Then when test is called, right, we have its formal parameter is named x. So it has a name x, it's bound to some location. And so what's the value that gets, so what happens when we invoke test? Yeah, we copy the value that's in the location associated with the actual parameter, which is y. We're gonna take that value, place it in the location, the value of the location associated with the formal parameter x. So here we're gonna put it in here. And then when we execute this x is equal to x plus five, we're gonna increment this x here, right? Because this is the local x. We're gonna say that that's nine. And then what happens when, so we're gonna print out nine, then what happens when test returns? x gets destroyed, or what's another word for destroyed? Deallocated, yeah, exactly. Okay, so it goes away, and now we have y, so we print out y, y has the value of four. All right, and again, so on Wednesday we'll start off with a pass by reference. Thank you.