.. (לתיקייה המכילה) | ||
Some more tips for getting started | |
0. Remove all the scope printouts from hw4. You don't need them anymore. You do, however, need the scopes for later so keep those. 1. Leave variables alone for a start. The recommended order is literals, arithmetic expressions, boolean expressions, control structures, and only *then* variables (that is, using the stack) and function calls (that is, advanced use of the stack). 2. Like we saw in BP problems, work from the simplest, lower-most sub-trees to the top. First do just numeric literals. Once you can do those, do addition of two of them. Etc. 3. Make yourself a little "mips program template" from one of the hello world programs - one you can paste code into and run in the debugger. Then paste in the code generated from your assembly generation. This will help you check small chunks of code before you can generate a full program. |
After errors (division by zero and the bonus) do we stop compilation or continue? | |
Stop immediately. You cannot continue running after a division by zero, and once you've written the bonus error to stdout your assembly code no longer parses, so there's no use in continuing. |
How do I work with spim and qtspim? | |
spim is a linux executable which runs a mips program, and QtSpim is a debugger which you can install for windows, linux or mac. Here are a few pointers for using them. 1. spim.zip needs to be unzipped in your t2 account, and you will then have access to spim (the simulator you will be tested with). 2. qtspim likes files with a *.s extension. 3. qtspim can set breakpoints in your code. 4. You can always debug with printouts using the syscalls (see the impl of print and printi in the pdf). 5. Both of these only run a "full" assembly program. What does this mean for you? That initially, when all you can handle is, say, basic arithmetic, you will need to wrap that code in a program - an entry point and an exit point. You can take the "wrapper" from either spim's hello world or qtspim's hello world. Until such time as you generate this code yourself, you can take your code and paste it into an existing main, and then run. |
When should we use registers, when should we use the stack, and when should we use the data section? | |
Registers are for temporary results. There's not that many of them, so they're to be used for computing an Exp, or to pass arguments to syscalls and the return value back from functions. The stack is for variables and parameters - things that are persistent between statements. The data section is for constants, a chunk of memory with a named label - and specifically in your case, for string constants. |
Do we need to initialize $sp and $fp? | |
No. $sp is initialized to the top of the stack, which is just where you want it. (see chart in the pdf), and you move $fp around freely whenever you need to. |
How do I use makelist and merge? | |
makelist and merge are static methods meant to help you manage the lists of statements to be backpatched, like you saw in T10. You should notice a few things about them: 1. What they actually store is a list of indices in the buffer. This is useful if you ever want to debug the buffer. 2. These indices should come from the return value of emit() - it returns the location for backpatching purposes of the command you just wrote. 3. That means this is perfectly legit code: CodeBuffer::makelist(CodeBuffer::instance().emit("j "); 4. Merge takes two such lists and returns a new list with the contents of both |
The provided implementation of printi prints numbers as if they were signed. Is this ok? | |
Yes. |
Is the division by zero error a compilation error or a runtime error? | |
We wish to stop *every* division by zero, so we will do this at runtime - the program that is running will print an error and exit immediately. Additionally, notice that while you can implement a trap to handle this, there is an easier way. |
When is it ok to store a boolean value in a register? | |
In short? When you have no choice: when you're reading/writing to the stack, and when you're communicating between functions. In long: 1. When storing to a variable (and, likewise, to a parameter, which is just a variable in another scope) you need to save the final result of the boolean Exp to a register in order to call sw. 2. When reading from a variable (and, likewise, a parameter) you need to load the value from the stack to *somewhere*. 3. When returning from a function that returns bool, if your implementation uses $v0, you need to put the value in $v0. Notice that these are all *final* values of boolean expressions, so they will only take up *one* register. Whenever the value from a variable is participating in a compound boolean expression, you must load it into a register, then immediately use the value and free the register. |
I would like to do <a thing> using an assembly command I found in the mips manual. Can I? Should I? | |
Can you? Yes. However, note that the list of commands at the beginning of the exercise is a match for all the IR commands you learned in class, plus lw and sw that write and read from the stack, and that you don't need more in order to solve the homework. If you're looking outside this list and, at most "syscall", you're probably making your life difficult in some way. |
I would like to do <a thing> which would create more optimized assembly code in case of <a scenario>. Can I do that? | |
NO. STOP. STEP AWAY FROM THE OPTIMIZATION. DO NOT DO THIS. YOU'RE JUST MAKING THINGS HARD FOR YOURSELF. Produce naive code. Naive code is enough. |