 Okay, thanks everyone for coming, braving the elements outside, fighting through 60-degree weather and light rain to be here. I really appreciate it. It's good to see so many smiling faces. Any other questions before we start? What was the point made about Java and if there was a question about what Java does? Yes, we talked about it on Monday. The question about Java was what are the parameter passing semantics of Java? Is it pass-by value or pass-by reference? So we have determined it's a weird pass-by value, but they have the assignment semantics or sharing semantics. So in that case, the objects you get copied there plus you can access fields of an object that you pass in. So it's not like pass-by value where the entire object is being copied. You basically have access to that object, but you can't change what something called you is pointing to or is bound to. Any else? All right, it should be as of now, maybe, if I still don't really know how to use blackboard, but if it is working, so it should be okay. So it's definitely homework four is posted. It may have a wrong title there, but the content is the same. So do the 16th. I kind of leave it up to you all. We could make it do the 18th, but the 18th is the midterm. So I feel like forcing you to get it done before the midterm is actually better for you. So we'll leave it at the 16th unless somebody can convince the rest of the class plurality that they really want to do it on the 18th. So it may seem like it, but it's for you. Okay, so first question is about structural equivalents of types, a bunch of types, which types are structurally equivalent? You may have to refer to the type system lectures for that. Then a bit of seed code or code that looks like seed and questions on what's the output if parameters are passed by value, passed by reference, and passed by me. Like always, show your work if you want partial credit in case your answer is wrong. And note that it's really easy to grade this kind of stuff, right? So if you're supposed to output four things and you put two things, that's like 50% right off the bat, right? So don't do that. Okay, and then the last question is another bit of seed code that has two functions, main and foo, and they call each other. And what you're supposed to do is draw the program stack at this execution point when location one is first hit. So you don't have to do the crazy super D, I think I wrote it in here. You don't have to do precise seed echo calling convention semantics, but I want you to label on the stack from top to bottom what's that frame, what's the name of the frame, what functions correspond to, what are the parameters of that frame, if any, what are the values of those parameters at that point in time, and then what are the local variables, if any, and values for all the frames on the stack at this point. So questions on this? This should be up. Yeah. Will the solutions be posted the same the day after it's due? Yeah, same. Yeah, so it'll be before the midterm basically. At no point in having it be the day of midterm if you don't have the solutions to it. I mean, it's up to you. You get more time, I guess, if you want to. But yeah, that's why I was saying it makes more sense. So it's a week and a half, but it's good midterm practice. Any other questions on homework? Do you notice any, I don't know, irregularities, questions, comments? Does anybody need them? Okay. All right, project five. Okay. First thing, project five is due on the second. So the last day of class at midnight. So this is how many weeks? Okay. Four or five? It's a lot of weeks, right? So it will not be extending its deadline. Five weeks or four weeks, whatever it is. I don't even know, but check that on me. I don't even know. Anyways, point is it's a long time, so you should definitely, when should we start on this? The first of December? No, absolutely not. It's going to be lots of tears. Okay. So yeah, so you have all this time to do this. So here's the basic idea. You're going to be writing the front end of a compiler. So what is the front end of the compiler? No, we haven't talked about it. So it's kind of written up there, but the idea is you can separate a compiler into the front end and the back end, where the front end reads in the tokens, parses the input, and creates some data structure that the back end code will then traverse that data structure to either execute the code or to output binary code or whatever. And so this is the design that pretty much all modern compilers take, so it's kind of cool. So if you ever in the future want to create your own programming language, instead of doing the whole thing, you can create a new front end for something like LLVM. Part your language into the data structures that the back end of LLVM understands, and then you're good to go. So we're providing you with the back end. So there are data structures defined at this intermediate representation, and there are precise semantics for that intermediate representation. So your job is to write that front end that parses this grammar, this language, and creates this intermediate representation data structures. We're just ready to go in case we reduce the time. All right. So, okay, the grammar's here. Obviously you'll have to read through this. It's simpler than the project three grammar, or project four grammar, so that's nice. So you can read through it to see exactly what I mean. All the tokens are defined here. We also have done all the lexing for you, so all you need to do is call getToken and ungetToken to parse the tokens, and just like you've been using this entire semester, it's almost as if these projects build on each other conceptually, but not right away. Okay, let's look at a program. Okay, so really simple, there's no types in this language, or you can think of that there's one type, every variable is an integer. That's it, and you don't need to worry about type checking, so no type checking. You can assume that all programs are valid types and valid syntax. So you don't need to do any error checking. You should do some error checking because you're good programmers, but you don't have to in terms of grade. That probably means none of you will. But four main statements in our program, assigned statements. So up top we have just an ID list of variables that we're going to use in our program. Here we have one variable A. Then we have the body with a bunch of statements, so we have where we're setting an assigned statement that A is value 10. Then we have print statements, so this means that we want to print out the value that A has at this moment of execution. Then the next statement is going to set A to be the value 20, so then what's the second line going to output? 20, yes, very, very simple. Okay, let's see, there's if statements, if statements have conditions that are Boolean, relational operations, also while statements. So while statement semantics is very similar to what you're thinking about with while statements. So evaluate the condition, if that condition is true, execute the body, and then evaluate the condition again. If the condition is false, then you go to the next statement after that while statement. Switch, anybody not know how to switch? Is this done? So just like in C, let me go down to my... I don't have a ton of examples in here because we're, I'll get to it in a second. You have a lot of samples. Okay, so for instance, we have this program here. How many variables are we defining here? One, what's the name of it? Okay, then we set A is value of 10. Now we have a switch statement. So a switch has various cases. So whatever the parameter is, A is tested against and says, if A is 0, then this case is executed. And then after that, it goes to after the switch statement. And if A is 10, then we're going to increment A by 40 and set A here. So what's this going to output? 50. So do you have to worry about outputting anything? No. You have to worry about, well, I don't know. Yeah, so you don't have to worry about outputting anything because your goal is to create the intermediate representation. So this is a graph and that the compiler backend knows how to traverse and execute the program. So just like our language has semantics about what all the operators mean, our intermediate representation has semantics. So essentially, your job is stitching those two together and saying, okay, I know I need to parse in this program. How can I represent this program in the intermediate representation? There's not a lot of questions yet. I'm getting worried, yeah. How is that different than all the different data structures, all the structs that were defined for us in Project 4, you know, when we were creating the parser? So what were the structures that were defined for you and were created in Project 4? Types expressed them. Yeah, but what did they represent? Different parts of the program, yeah. Input. Input, the input program. What's the name for that structure that we talked about before? Parsh tree. Parsh tree, yeah, it's the parse tree, right? So that's the parse tree in the program. Here, but you didn't ever execute that parse tree. All you did was do type checking on that parse tree, right, to see if the types, to see if that parse tree was semantically valid. Here, you don't have to, you're not, you have to parse the input grammar, but your output isn't a parse tree and your output isn't, yes, it's semantically valid. Your output is this intermediate representation, which is similar to a parse tree, but not exactly, because it's essentially a different language. So this intermediate representation has its own semantics that are similar but different from the language itself. Just like, so you write C code, right? Can you use a while statement in C code? Does x86 have a while statement? No, it doesn't, right? The compiler translates that while statement into something that's semantically identical using branches and loop, using a jump in a branch and a condition. So just like that, we have this intermediate representation that as long as you can translate that structure into like, for instance, our intermediate representation doesn't have a while statement, but it has ifs and it has go-tos. So you can simulate the semantics of a while statement with an if statement to do it properly, which you will. So these will be provided to you. I'll release it later today. I'll send out an email, but it should be very quickly after this class. Yeah, this is all just the semantics of our input language and the semantics of the intermediate representation. Important note that I mentioned here multiple times. You're not allowed to change the back end at all. The compiler.c that we give you is the compiler.h that we give you. Those you can't touch. So when you submit, you're actually not even going to submit that. You're going to submit your own code. So all compiler.c does is call this function parse generate intermediate representation. And that's what you have to implement with this exact signature. Should you code all of your project 5 in this one function? Probably. I would hope probably not. It may be very difficult. I mean, could you theoretically? You're going to have a, you should start now if that's your goal. Start now. And you can use c or c++. It doesn't matter. There's instructions in compiler.h about how to include compiler.h depending on the language. So follow those instructions. The great thing is the back end isn't a black box. So the back end is in compiler.c. So you can look at it and see how does it work. What does it do when it gets an error and how does that work. So you should be reading the .h file type of data structure. If you should read how the back end uses those data structures and you should definitely read this document. It contains hints and helpful things I think. Yeah. So we're giving compiler.c. We're also giving lexer.c. They're in the compiler. They're lazy program. They're all in the compiler. So you can't modify those to change the language or the tokens or anything like that. And I guess data structure. That's a good question. It depends. Yeah. I mean, that's part of what you have to do is you have to read what you're supposed to do, understand that, and then say okay, this is what my output should be. How do I actually create that by parsing these input tokens? So yeah. So exactly. If there's a list of variables that you may need to do later, maybe you want to create a list of variables. Other questions? Or do I want to create it? Yeah. Comparison to the last project, how long do you think this one might take on average? I have absolutely no idea. I think to the last one, I think it all depends. All these projects exercise different skills. So project three was all about can you build something from scratch, basically. Project four was can you integrate with our code and read our code, and be able to fill out and do all the parsing in there. And then this project, it's kind of a combination of both, but it's a pretty well defined interface. So this really exercises all the things we've been talking about in class. You have to build the parser from scratch to be able to parse in all the input, make sure it's all semantically valid versus the integer, all that kind of stuff. So you need to use the grammar to do that, whereas in the last project we gave that to you. But you don't have to execute the code or anything. You just have to build the data structures that you need here. I have no idea. Maybe slightly more difficult, but more time. So that's better. Very better. Okay, let's see. Some important things here. All right. Okay, so everyone wants to know about grading and vibrations. Okay, so once again, automated test cases should not come as a shock to anyone. So there's going to be multiple test cases in each of these four categories. Assignment statements, if statements, while statements, and switch statements. So which of the statements that I already talked about is not on this list? What was it? Where is that at? Print. Yeah, print statement, right? So why? It's handled by the back end. It's handled by the back end, but you still have to support it and parse it, right? Because otherwise we're never going to get your output. So it doesn't matter what you do, we're not going to be able to see the execution of your program. So you basically have to do print statements. So then assignment statements are worth 20. They're fairly simple. If statements are worth 25, and switch statements worth 30. So unlike on previous projects, you will get all the test cases for these three cases. Yes, yay. But no cases on switch statements, so that's on you to do on your own. Look closely, there's one. This shows that you know how the language works, you know how to create test cases, and hopefully you've learned from if and while, because they are fairly tricky, so you should start early and start thinking about it. Because you have all the test cases, there's going to be things that don't work and you're going to be wondering why, and then you'll re-read the project description, and hopefully it'll make sense, and then it should be good. So yeah, that's what the breakdown is. Just to be super clear, if, while, and switch all depend on assignment statements, so get your assignment statements done first, right? It's really hard to test control flow logic. If you have nothing to change the state or to output anything, so, yeah. So then are you suggesting that we set up print and assignment, go through, get it fully running with test cases, and then go back and work on different files? Absolutely. Yeah? You've got to look at the language, but no, there's no break. It's all very do this, this whole body, and then you think there's an implicit break at the end, but yeah, if there's any doubt, like, there's a whole section on the switch statement semantics, so read that if there's any doubt in your mind, email so we can clarify it, because I don't want the semantics of the language to be confusing. I want not to be confusing, but I want you to work at developing this and creating it. More questions? Yeah. So going back to the intermediate portion, so what makes that data structure different from just the part street of the group? Ah. What makes that data structure different from a part street? It's not a tree, it's a graph, so that's the first thing. So you think it's a list of statements, and some of those statements, like a go-to statement, have a pointer to the next statement to go execute, so you think you could have a loop like that. That's how you do like an infinite loop. And that's pretty well, it's a valid program, so you just keep going forever. So that's the essential reason is the way the data structure works. So if statements are going to have a true branch and a false branch, and then those are going to execute, but when they're done executing, they should go to whatever the next statement after that if statement is. So you can have this complicated, if you can have something that loops, right, with while loops, you're going to want to have loops in there, but that have if branches that go the other way, that's going to stop. More questions? I expect questions on Monday, at least. All right, I think that's... Oh, there will probably be an extra credit extension to this about extending the language to support another feature. I don't know exactly what it is yet, but I'll announce that by Monday, let's say. Just in case. But it will probably not be easy. So, few more. Okay, anything else before we start? Yeah? We'll do anything, is there classes on Monday? No, no class next Wednesday. Cool. Don't show up, or you can, I won't show up. I mean, I didn't do it, but it's a school day. I think I'll probably be in office hours, so I don't know if you want to come and hang out, whatever. Knock on my door. No, no, no. Do you want more lectures? Okay, no. All right, no. That was already planned in. That's why I think I said at the very beginning, we have a lot less days, because we're on Monday, Wednesday, so we miss, like, Veteran Day cost us a day, and then there was another day where we had the Monday off that cost us another day. And we start on Thursday, so that also messes things up. So, anyways. Yeah, we're missing that all the time. I know you guys hate it. Okay, anything else? So next week, no class on Wednesday, the week after that, midterm on that Wednesday. We do midterm review that Monday, and that's kind of where we're going to report. All right, last time we talked about buffer overflows. Any questions on this? There were some people who came to our office and asked questions. Anybody else have questions? So does everybody understand why this saving of the EIT onto the stack is a bad idea? Or causes this problem? Maybe it's the best idea, but it has this drawback. So think about it. Let's see if I can... Okay, we were here, right? And we saw that the EIT, the saved instruction pointer, was overwritten on the stack with a little string copy. Right? So this is kind of like... If you think about everybody kind of remember the story of Hansel and Gretel? Kind of. So they would go out in the woods and they'd leave breadcrumbs to find their way back. But then I think at some point they found that the birds or whatever were eating their breadcrumbs so they could no longer find their way back. So this is exactly what's happening. Because this value on the stack, right, that is the breadcrumm of where we need to go after this function executes. So when we erase that, now we have no idea where to go when the program crashes. And if an attacker is very clever and controls this value instead of just overwriting with random jump, more controlled jump, then they can force your program to start executing somewhere else. So they're like the witch finding the breadcrumbs and then changing the trail to go back to her house. So they go, oh yeah, it's going home, awesome. Keep following breadcrumbs and then end up at the witch's house. Any questions on that? All this stuff. So now we're going to switch to the functions. We were looking at function semantics, pass by value, the parameter passing semantics. So in C, what do these functions have? From declaration to the end of the file? Yeah. What was another name for that? Block level. Can you declare a function in a block? You can see? No. Yeah. Global scope. Yeah, exactly. From where it's declared to the end of the file. So can we have a function in a local scope in C? Can you define a function in a block? Have you ever tried? No? You can't, so good thing you didn't. So yeah, but so is that a crazy language feature that we would never want? Yes. No. We're talking about crazy language features, right? Yeah. In languages like JavaScript you may want to define a local function that's only valid in this scope, right? And you can even pass functions around them. You could say, hey, whenever anybody clicks this button, execute this function right here. And then whenever anybody clicks that button, that code gets executed. So you want a function that's only valid basically right there. Because it doesn't make sense for it to be a global function because the rest of the pages, the rest of the JavaScript code so this is a case where you really want a local function. So can we do that? So functions we saw are only valid in the global scope. So with C, in C. So specifically talking about C here, we'll be talking about it. So let's look at an example of what this would look like. So let's envision a C language function. Is it crazy to do? Could you just do that? Is it like C++? Don't think there are local functions exactly what we're talking about but I know they have newer features in like C++ 11 that I don't know about 100%. So it's possible. But maybe you can look at what we're talking about here and then see if it does that. Okay. So we're going to have our standard includes a function this time. We're going to think about a function foo. So we define what's X here? A variable of the scope of this variable? Local. Yeah, it's local to function foo. But then we want to define a new function bar whose scope is local to foo. Right. So we're just defining a function. We know how to define a function, right? Just like this. And then maybe we want to define a function of the scope of baths. Can we do that? Sure. In this language, yes. If we have one local function, why can't that function have a local function? Then in baths, we want to increment X. So we want to say X is equal to X plus 1. So can we do this? Y. X is locally scoped in foo and it's inside the scope of baths. So we should be able to reference that variable. And then we can check if X is less than 10 or less. Why? Because baths can access the scope of bar. So can you do this in normal function? No. You can have a function call itself. You can have a recursive function. So when a function is declared, that function is valid in that scope. So you can call it. So here function bar is valid in this scope here. So it can call itself and its local functions can call itself. So we should be able to. Then at the end of bar, well, the first thing we do in bar is call function baths. And then function foo. So if we call foo, do bar and baths automatically get called? No. When do they get called? When they're called? Yeah. We have to actually invoke them. It's just like defining a function in the global scope. So you define a function. That doesn't mean that that function gets executed right there. I want to invoke that function. It gets called. So the first thing we do is set x is equal to 10. What x is this? The local x to foo. Then we call function bar. Then we print out the value of x. So what does this function output? 10, 20, 132, 600 and 10. So we're going to compile this with a special version. You guys have me confused or doubting myself. 10? The output 10? Yeah. So bar is going to get called. The first thing bar does is call baths. It's going to increment it. So x is going to be 1 now. It's going to check as x less than 10. Yes. So call bar again which calls baths. Which increments x. x is now 2. Call bar again. Bar calls baths. Call bar again. x is now 4. Call it again 5. Call it again 6. Call it again 7. Call it again 8. Call it again 9. And finally it checks 9 is less than 10. So it calls bar. Which calls baths. Which increments x to 10. And then is 10 less than 10? Nope. So then we finally return from this inner baths. And then all the functions return. And we return from this first call to bar. And then we print out 10. So why, I mean, I don't know. Is this nice that we can define these local functions? Could I write the same crazy code with global functions? Yeah. I could just lift x up to be a global variable. And it would be just as confusing as to what actually is happening. But let's start from a software engineering perspective. Why might we want local functions? Security. What about security? I mean, it's basically like outside function. If you were to call that function, the larger function couldn't change and modify the inner function. So I'd probably call that encapsulation, right? So you're trying to... Or what? Information hiding? You're trying to... Yeah, you may want in a language like C where all of these functions are global functions, right? I may not want other people to call certain functions. So maybe I want them specific to this scope. Or even in a language like JavaScript where you have no way of scoping everything's in the global scope. So you have to actually define local functions if you want people to not call your functions and not to pollute the global namespace. Any other reasons? Yeah. So you're kind of, in essence, simulating the global variables but scoping it to only this function, right? So yeah, you could have two functions that refer to the same variable in this local scope. Other people can't mess with it, can't change it, but maybe they're doing something. Yeah, yeah. Probably also maybe increased variability if you like smaller parts. And you know that all those parts apply directly to that in one function. Yeah, exactly. So you could, if you... So why does it help readability in this case? Well, exactly. Your way. So why would it help readability? Yeah, but couldn't you just get rid of that and copy that code from that function every time you invoked it? You get names of functions, exactly, right? So every, you have a function that has a name. What does the name describe about a function? What it does. It should be what it does, right? That's good software engineering practice. I don't comment on it. All right, so yeah. So you want descriptive names that describe what a function does to help with and help to that abstraction because you can see the function call and kind of assume you know what it does. Obviously if you're using names like Foo, Bar and Baz, you're already gone off the deep end. If you can encapsulate that chunk of code and give it a name, well then now you've documented what that code should do. Anything else? Yeah. Yeah, it's interesting. I hadn't thought about it like that. Yeah, I think it depends exactly on the language. I think in JavaScript it makes more sense because it's an interpreter, right? So it's interpreting and creating the functions on the fly. Whereas in language like C, you still have to create the code for Bar and Baz, so I don't know that you get a ton in the way of efficiency there. Yeah. Something like a singleton function in what sense? Exactly, yeah. What if we wanted, kind of gets into the purpose of modules that would be a poor man's namespace where if you wanted to create a function called size that only made sense inside this function, you could do that without worrying about that conflicting with somebody else's name of a function size, right? Anything else? I like this. So you just sold yourselves on why this is awesome, right? No? Why not? You don't like it? What's bad about it? To make things more confusing in what sense? You can say that about functions though, right? Functions make things super confusing. I got to like see a function call, and I have to go to that code to understand what it does. Well, I mean couldn't it kind of be a bad design practice if you need to have a different size function or something? Like if you had a global size function, but this data structure would be a different size function and function if you didn't insist it? Yeah. It could be a, but I mean is that really a language feature problem? Yeah. I guess it kind of depends on your perspective of should you include something in the language because people can abuse it and do it poorly? Like pointers at sea, or should you get rid of that like in Java? I don't know. It depends on the feature, right? Yeah. You didn't like it? Why don't you like it? Well, is any of this necessary? Why aren't we programming an assembly? Because it's easier. Because it's easier, right? Because we have nice abstractions, right? These languages do, we have wild statements because they are nice abstractions. Ah, well, I could give you C code that doesn't have local functions that's just as unreadable, right? Yeah, this is kind of like, I don't think we'll get there, maybe, but here I'm defining the function then getting it a name. But it could be cases where I want to define a function, maybe it's very short, but not give it a name. Right? Like I want to say, hey, when this thing happens, right, it doesn't make sense to give it a name because it's only ever used in that context. So in that case it's local and it's anonymous, which is nice. Yeah. Increase the what? Cyclomatic complexity. Increases the cyclomatic complexity, maybe, but I don't know that I would argue that that's a bad thing or a good thing, right? That's just a measurement, whether that actually is like more complicated to understand. I think it actually depends on your perspective. So isn't there a lot of language features that you like hate the first time you see them? Could you see like, man, this is stupid. Why would I ever want to do this? And then you start using them and you're like, oh, our inheritance is actually kind of nice in certain situations, or I don't know. I'd really love pointers is what you'll say at the end of this class, right? Or I appreciate them. Maybe. So it lets you have the concept of global variables with scope. Especially if you want to see some of those functions. Reason about, right? Exactly. Because you can kind of see that they're all in this function. Of course, did we say that there's anything that precludes these inner functions from referencing a global variable? Correct. There's global variables you don't have to scope for the whole program. Yeah, absolutely. So yeah, so you're referencing these variables that are only valid in that function. So when I look at them, I know the only functions that can ever change x are going to be defined here in food. So that's why if I want to know what happens to x, I can look at all the functions here. Whereas if I have a global variable, I literally have to look at every single function every time that variable is used. And I have to worry about scoping rules to see if somebody else shadowed that variable x, because maybe it's actually referring to a different variable x. So then you get other kinds of craziness that you have to worry about. You just don't have to worry about that. Yes, you do. But maybe you've argued I'd argue you've less, I mean, it's clearly less code, right? The function food versus the entire program probably would be. Okay, so now that we know we've talked about this and we've said yes, it's a great idea we should at least experiment and try to do this and include this in our languages. So Ken, so we talked about the C-Decoration C-Decal calling convention. Can it support local functions? No. Why? I mean, I'm a compiler, right? I can compile that function somewhere. I can give it like a quote. I can call it foo underscore bar or something. Give it a new name in those global scope. So what do I mean by the C-Decal calling convention? The stack. So what gets passed, what gets pushed on the stack when you're trying to call a function? It doesn't have to be in order. It doesn't have to be who does what. But what are things that are stored and saved on the stack when invoking a function? The instruction pointer. So the next instruction to be executed after that function is called? What was it? All the parameters of the function. What was that? The base pointer. Yeah, so the person who called you the base pointer, right? Because we want to get back to that. Anything else? Important or interesting? The return address. The return address? Yeah, I think that was number one. So cannot support local functions? Sure. It doesn't restrict them, but it can implement them. Okay, so let's think about this. So let's think about no local functions. When I see this reference to X how does the compiler know where that X lives? So what is X? It's a variable. Who's a local to? Foo. So how does how do we access this variable X? It's on the stack. What do we use? An offset of somebody just said it. The base pointer. Yeah, so we're going to use the base pointer and we're going to say X is an offset of Foo's base pointer. Right? That's how we do local variables. We just say, okay, the base pointer is here. We know an offset is X. Great. In Foo, we know that's how we reference it. But once Foo calls bar and then so we have Foo's function frame on the stack Foo calls bar we put bar function frame on the stack and now bar calls baz, we put baz's function frame on the stack. Baz tries to access X. How does it know where to look? Where does that X live that it wants to access? Foo's. Yeah, the one up in Foo's frame, right? So let's see. Baz has stored Foo's frame pointer bar and bar stored Foo's frame pointer Foo's. So could bar look up that frame pointer and then know the offset X on Foo's frame? It could. It could though. Right. So the problem is you need to get to Foo's base pointer. So once you can get to Foo's base pointer then you know the offset. So the compiler, we already said it's base pointer minus four. Bam. That's X of Foo. The question is when you're in Baz how do you get to Foo? What was that? Keep track of it somewhere. Yeah. Maybe a different way to do it. So passing the locals with applied function parameters. Yeah, that's actually a very interesting approach. So basically so we could analyze when we're parsing bar and Baz. When we see X we know where X is declared, right? We know that X is a local variable from Foo. So we can actually change the signatures of bar and Baz to take in a pointer to some integer X. And then when we invoke them we can make sure we pass in that address and then we change all these references to star X's. That is actually one very interesting way to do it. But we're not going to do it that way. Yeah. Yeah. So let's walk through the code and see what it does and look at the function frames in just terms of stacks, right? Of just frames. So we won't go into all the details. But okay, first thing we do is set X equal to zero, right? But we're in Foo. So think of this as the stack in each of these boxes instead of in memory addresses now or is the entire frame of Foo. So you can say the base pointer points at the top there, whatever, it doesn't really matter. So we know that the next stack pointer is here and just like before it's going to grow down. So then after we set X to be 10 then we're going to call bar, right? So now we've pushed a new stack frame for bar onto the stack. And bar is going to call baz which is going to put a stack frame on the stack. And now baz is going to execute, right? And baz needs to access X. So which of these boxes does baz really want to access? Foo, right? Yeah. It needs to access Foo. It knows if I can get to Foo's base pointer I know exactly the offset to look at. The question is with the seed declaration calling convention, with all the information that baz has available, right? It has the return address and has the caller's return address and the caller's base pointer. So could baz look up and say hey I have the base pointer to bar. I can go up look at that. And bar has the base pointer of Foo. So now I can look at that. And now I'm at Foo. So now I can look at the offset to get X. So could it do it? Yeah, it could, right? In this situation, right? So what did we do? We had to look it up three times. How did it know three times it had to look up? There were three functions in from where? From Foo. So then what happens if we keep executing, right? So let's say, okay, then we keep going. We pass this check. We're going to execute the function bar. So we push bar on the stack again. And now bar calls baz, right? So we go back, put baz on the stack. Now we want to access the X variable, right? So now where is the X that we want to reference? Is in Foo. Is it three above us? If we looked three up, we'd go one, two, well one, two, three and we'd access some offset in bar getting completely the wrong variable, right? So the question is here we want to access five up, but how do we know? So how does the function know? How does baz know, right? Because the code of baz has to be able to be executed no matter what. So can we implement local functions with what we have in the C-deckle calling convention? Maybe if we use that other scheme, but we're not going to use that. So, no. No, we can't. There's no way to write code in baz that says, hey keep looking at the stack until you see Foo. Well, how do you know when you see Foo? These functions don't have names, right? Remember, these are all just chunks of memory that we are giving names because we're tracing through the execution. But we want baz to be independent. So what do we do? Is it game over? Huh? Keep track of how nested we are. Keep track of how nested we are? Or we could save like biz and parrot. Yeah, so save the memory address where Foo is. Yeah, but you already know it. Yeah, so that's what, so what is, so, okay, let's think about it this way. So we have, we're saving our caller's base pointer, right? Yeah. The offset of Foo. Yes. Bar is also an offset of Foo. So if baz calls the base pointer and baz is defined within bar, you're good. Look for X. If you don't find it, just call up again. But how do I know I'm in bar? How do I know that as baz, bar called me? Because I could be called from anywhere, right? Not if you're global. Another place in Foo could have called me, right? So I could have had X is equal to zero and the first thing I could have had was baz, right? And then I need to be able to say to look up just one, to look up the Foo. Wait, no, that's wrong. Just a quick minute. No, you can't do that. Because baz is only scoped in bar, so you can't do that. Okay. So let's go back to that thought. So if you call the base pointer or if you call the function that called you, you can check to see if you're one of the offsets of that function. If so, look for the local variable in that function. If it's not there, call again, see if Foo is defined as a local variable in that function. If so, look for it again. And then go back. If Foo is defined as a local variable in that function. So if Foo was a local function to some other function, I think Foo is getting at us looking back at the stack for X. So as soon as you find an X, then you know... But how do you know X? That's... So one thing you could do, you could keep track of a symbol table, right? You're essentially doing dynamic scoping, which may mess up your scoping rules. Right? Because you want the X that's in... Well, that's in your lexical scope, not necessarily the dynamic scope. Alright, let's think about this. So... We have function... We have instruction pointers... Sorry, we have the base pointer of the person who called us. What does that depend on? So you can think about we have links all the way up here. Right? So at each of these frames, as a link to the person who called that. So what determined this order? What is defined within what? Is that what control these functions being laid out? Because baz is defined in bar, but here another function frame of baz is higher up on the sac from it. So what determined this order? What was it? Yeah, the order in which they're called, which is the execution of the program, right? So really you can think of this as the pointer you have is the person who called you to call you to call you, but when we want to result this X, do we care about who called us? So you doing this by hand, when you say, okay, I need X. So whose scope do you look in first for X? So baz is X there? So then what do you look at? You look at bar, and then it's in bar, and then whose scope do you look at? Food. And how do you know that? With your geniuses? Right. But the question is, from us here in this version of baz, how do we get to food reliably every single time? Do we use static scoping? We know that. We're going to talk about that. Essentially you think about what you're doing, right? So you're in bar, just like the compiler does, and it says, okay, I see X. Now let's look up in bar, in bar scope, no X. Let's look up in food scope, hey, there's an X here. So what? So that's defined here in the function, right? So it's defined essentially lexically or statically, right? The static scoping rules define this. But what we have here is we have saved instruction pointers based on blood. Is it static? It's based on the dynamic code execution, right? So what would we really like a pointer to from Baz? Well, food, yes, eventually. Yeah. Sure, what are you going to point to? Statically? Maybe. I mean, this depends, yeah. Make a new statically allocated X, that's cheating. That's cheating. But then you can't do it statically, right? Because any of these functions could call food again, which would create a new X in that new food. So yeah, let's go back, what were you saying? About, yeah, a third frame pointer. So what if we had a third frame pointer that was instead of control base, control flow base dynamically set like here? What if we had it set statically in essence? So that would be a link to not who called us, but to who our parent's base pointer is statically. So if I had that with Baz, I can look that up once to say, okay, where's Barr's base pointer? And if Barr has that, who's Barr's parent? So if Barr would then have foo's base pointer stored. And so it can say, oh, perfect. Now I know how to get to foo. Because I know X is too away for me. I know I can look up two of those we'll call them access links or access pointers. I can look up two of those and then take that offset from there. I know that's going to be foo because I know it's statically. So don't have any animation here. But, so yeah, as a group we just invented access links. Yay! Don't try to publish a pattern on it. Sorry, we've been done. But the point is that we can use extra information. We can change the calling convention. We can add additional data there to resolve these questions to extend our language and to change our language to add cool new semantics. Or we can make a more clever compiler and compile these things away so that the programmer never knows about them. But we're using cool pointers to do that. So, the way to kind of think about this is now instead of, like on the right hand side, how we just have the same base pointer so the previous frames, right? Based on the calling. Well here, each of these frames is now going to have a second pointer that points to their parent's base pointers, the static parent's base pointer. So for here, for Baz, I'd say, bar, right? Because statically, Baz's parent is would it point to that bar or would it point to the very first bar? Which one should it point to? The very first bar. This one? What if bar defines a local variable? Which, if bar defined a variable foo, sorry, not foo, that's going to be super confusing. If bar defined a local variable a which one would it want? This one or this one? The one that just called it y. So it's all about, this is why we mean local functions. This function is local to this invocation of that function. Right? So if, yeah, we had Baz calling foo again and then we had another foo on the stack which would set x equal to zero and then that called bar which called Baz. When that accesses x, the closest in the call stack to it, right? You can do it while you want the static parent. So basically every time you invoke the function you pass who its static parent is because you know, like when you called that, when Baz calls, when bar calls Baz here it knows that it did static parent so it can pass its face pointer. Just like when foo invokes bar here, it can pass its face pointer. And so you have all the information you need when you're compiling this to specify who's pointer, like do I look at do I pass to you my my base pointer, do I pass my parent's base pointer depends on where in the scope you are. And this would ensure that so when bar, this bar called Baz it says, hey your access link is my base pointer because that's all it cares about, right? Bar doesn't know that there's another bar on the stack. It doesn't care at all, right? It could be called once if we called the stack, it doesn't matter. It could be stored on the stack. Yeah, you just specify in the calling convention that the caller is supposed to push the access link on the stack. Exactly. So Baz Baz's access link points to bar, so yeah, where's bar's access link point to? Boo! Yeah, exactly. And then what about this Baz? Where does this Baz's access link point to? Which bar? The one below it? This one? Why the one above it? Yeah, that's the one who called it, right? So bam, that's going to point up there. And then what about this bar? Where's this bar's access link point to? The foo. Yeah, so that way and what if later on, right? Because we trace through this, this is going to happen ten times. So you're going to have a bunch of these foo, bar, Baz, bar, Baz, bar, Baz, bar, Baz, okay? So all of the saved base pointers that we looked at on the right-hand side, where do they all point? The one right above it, right? Yeah, because you're saving your caller's base pointer. So it's just bam, links all the way up. And then the access links in this time, each Baz is going to point to a bar and then each bar is going to point to the foo. So at this time, when this Baz needs to access variable X, what does it do? What's the algorithm for accessing variable X in Baz? Yeah. Follow the access link once, follow it again and then take the offset of whatever that is and that's variable X, bam. And so here we've given enough information so that at runtime the program, each function can compute that. So does that algorithm work depending on what Baz this is? Yeah, exactly. It's completely independent of whatever which Baz we are on this list. We know if we follow the access link twice, we're going to go to our parent. Questions on this? So it's really like this. You have a bunch of bars to Baz's and every bar has a link to foo and so that way everybody can always access any variable that they need in their parents scope. Yeah. So if bar had a variable X, so bar would, I mean, bar would need to point to foo. It would not. You need to, but I think you do it anyways because you don't know if bar defined any functions, those functions may refer to a different variable. Yeah, I mean the compiler could perform some optimizations and say bar does not access, nobody accesses anything outside so they don't need access links but I think you still include them just because you'd rather do the stupid slow thing that works definitely than do an optimization and use access link to go to access. You aren't actually using any of these base pointers but they're still all saved. I mean you're using them to restore them, right, but it's not like you're doing anything with them. Yeah. How do restricted variables in Java for example, they're kind of scoped a little bit. How would they implement something similar to this? So I believe so how are, like in what sense are variables scoped in Java? It's been a long time. I was thinking more restricted levels so when they can only be accessed within a single package. So yeah, that's actually, so it's one of those things that's the difference between the language semantics and whatever your back end semantics are. So just like here, actually I'm x86 code that's running with full access to all of this memory. So there's nothing to stop Baz from reading the stack pointer and looking at the stack and modifying values on the stack so we can do that but that's not a valid C program. A C program's not going to do that. So just like what Java will do I can't, I don't remember the semantics of the JVN but essentially it basically just will semantic check not compile a program that violates those constraints. So if you have a private variable in one of your classes if somebody tries to access it that's a compilation error. That's a semantic error. Not that it couldn't do that. It actually has all the information but the point is you as the programmer specify this variable's private that means don't ever generate any Java program. A Java program that accesses a private variable is not a valid Java program. Any other questions? Yeah. So how did Baz know to jump up two links as opposed to just one? So how does Baz know how to jump up two links to access X rather than just one? What was it? It doesn't define X. Yeah, so statically it knows just like we knew so you can basically calculate how many scopes do I have to travel up to access that variable? So what is it in this case? Two. You've got to go into bar scope and then into foo scope and then you have X. That's something that's just defined statically. Yeah, it's statically here. We can define that so this is why we're using the static scoping rules here. You can all look and tell just like the compiler can. So it does static scoping just like what we learned about, right? By looking through its local scope and then the containing scope and then the containing scope until it finds it. And so depending on how many scopes that is if it's global then it knows how it can just access it with a global location otherwise it knows exactly how many access links to travel. More questions? If you had a function 100,000 times, one of those functions in memory is going to have a pointer stored. So you couldn't get over time and just get actually... Can't that happen with functions anyways? So yeah, it's one of those things that's like you're adding more data to each function so each function is going to take an extra four bytes for a function call. I don't know. On a modern CPU is that really going to affect things that much? I don't know. I mean it all depends on how much how many cycles you're counting and why it counts I guess. I would probably not do this in a car or in a embedded system like that, right? The point is you can't. It's not any crazy different, right? You just have to think about what information do I need in each function to be able to perform this operation semantically. Alright, final chance. Any other questions? Alright, well that's it. I'll see you all in a minute.