[an error occurred while processing this directive]
mov BYTE PTR [eax], 200 add BYTE PTR [eax], 16
Answer: mov BYTE PTR [eax], 2162. Find a shorter instruction sequence with the same behavior as:
xor ebx, ebx sub ecx, ecx neg ecx and edx, 0 mov eax, ebx add eax, ebx shl eax, cl
Answer: Xor-ing any value with itself produces 0, as does subtracting any value from itself. This means the neg ecx instruction has no effect on the value of ecx. The mov eax,ebx instruction moves the value of ebx (we know it is 0) into eax. Since ebx is zero, add eax, ebx has no affect on the register values. Similarly, we know cl is 0 (since it is part of ecx which is also 0. So, we can remove the unnecessary instructions. The result of the code is setting eax, ebx, ecx, and edx to 0. There are lots of ways to do this. Here's one:3. Find a shorter instruction sequence with the same behavior as:xor eax, eax xor ebx, ebx xor ecx, ecx xor edx, edxNote that we should be very careful what same behavior means here. This answer interprets is as leaving the general-purpose registers in the same state. If we included the flag registers also, then the new code is not exactly equivalent since many of the removed instructions actually update flag registers.
label1: inc eax cmp eax, ebx jl label2 jmp label1 label2: cmp ebx, eax jle label1 imul eax, [var]
Answer: From label1 we have two cases to consider:4. An alternative calling convention would use registers to pass some parameters. Suppose we used the EBX, ECX, and EDX registers to pass the first three parameters instead of the stack.
So, if the initial value of eax is less than ebx - 1 we do:
- If the value of eax is initially below the value of ebx - 1. After the inc, eax is still less than ebx, so it will jump to label2. At label 2, it compares ebx with eax. If ebx < eax, it jumps back to label1. Note that this case could only happen if eax = ebx - 1 when label1 is reached. Otherwise, it proceeds to the imul instruction.
- If the initial value of eax is at least the value of ebx - 1, then after the inc we know eax ≥ ebx so the jl is not taken. The jmp label1 returns to label1. The value only changes by increasing eax. But, because we have a fixed integer representation, eventually it will wraparound. Once it does, we will be in case 1.inc eax ; jump to label 2 imul eax, [var]If the initial value of eax = ebx - 1 we do:inc eax ; jump to label 2 ; jump to label 1 ...If the initial value of eax ≥ ebx - 1 we do:inc eax ... inc eax ; repeates through label1 until eax wraps around imul eax, [var]A short equivalent sequence is:label1: inc eax cmp eax, ebx jle label2 xor eax, eax label2: imul eax, [var]Note the xor eax, eax instruction zeros eax. This is the value it will have after wrapping around.
As in the previous questions, we are using same general-purpose register values are our standard for equivalence. The execution time will be very different!
a. Describe other changes that would need to be made to the calling convention.
Using these registers to pass parameters means they are not available for other things. Before the call, the caller needs to save any needed values in those registers on the stack so they can be restored after the call. Note that if it is typically necessary to save all three registers, then this counteracts the savings we hoped to get by passing parameters in the registers. But, in cases like tail recursive calls (no caller state is needed after the call), then there would be no need to save and restore the old register values.b. Discuss the advantages and disadvantages of such a change. Would it improve the running time of typical programs?
The convention would need to state whether or not the callee could modify those registers (e.g., are they callee-saved), or if the caller cannot rely on them holding the same values after the call returns. Probably, it should state that the caller cannot rely of these values after the call. Otherwise, they would need to be saved on the stack (again, losing the performance benefits we hoped for by passing them in registers).
The caller code would need to be generated to not alter the values in EBX, ECX, and EDX until the parameter values are no longer needed.
Using three registers to pass parameters would not make sense for most procedures, but it does make sense for procedues that don't need many registers for their own computation. The problem is x86 has so few registers, that even if we save the initial cost of putting those 3 parameters on the stack, in the end we will need more stack operations to save and restore those registers. It does make sense to use one register to pass the first parameter in many cases.(No comments for Questions 5-10, although we may discuss some of these in future classes.)
The __fastcall calling convention in MSVS uses a similar convention. It passed the first two (32-bit or smaller) arguments in the ECX and EDX registers. We saw an example of this in Class 22 with the call that checks the stack cookie. Different compilers have different register calling conventions.
CS216: Program and Data Representation
University of Virginia
Using these Materials