[an error occurred while processing this directive]

Problem Set 7 Comments

1. Find a shorter instruction sequence with the same behavior as:
   mov BYTE PTR [eax], 200
   add BYTE PTR [eax], 16
Answer: mov BYTE PTR [eax], 216
2. 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:
   xor eax, eax
   xor ebx, ebx
   xor ecx, ecx
   xor edx, edx
Note 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.
3. Find a shorter instruction sequence with the same behavior as:
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:
  1. 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.
  2. If the initial value of eax is at least the value of ebx - 1, then after the inc we know eaxebx 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.
So, if the initial value of eax is less than ebx - 1 we do:
   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 eaxebx - 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!

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.

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.

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.

b. Discuss the advantages and disadvantages of such a change. Would it improve the running time of typical programs?
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.

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.

(No comments for Questions 5-10, although we may discuss some of these in future classes.)
CS216: Program and Data Representation
University of Virginia
David Evans
evans@cs.virginia.edu
Using these Materials