A single if statement is used for execution of a statement or a block of statements depending on whether the specified condition is true. In a general form, this statement looks like this:
if (condition) statements
In C++, it can be written as follows :
if (condition) { <statements> }
A more complex variant of the statement, if else , makes it possible to selectively execute one of two actions depending on the condition. Here is its syntax in C++:
if (condition) { <statements 1> { else { <statements 2> }
There are no such constructions in assembly language, but they can be implemented easily with certain sequences of commands.
For example, we will consider an algorithm that checks two operands for equality and jumps to one or the other branch depending on the result.
In Visual C++ .NET, the expression will look like this:
if (operand1 = operand2) { ... } else { ... }
Assembly language allows you to implement the if else construction quite simply:
cmp operand1, operand2 je YES <commands 1> jmp EXIT YES: <commands 2> EXIT: ...
Another variant is also possible:
cmp operand1, operand2 jne NO <commands 2> EXIT: ... NO: <commands 1> jmp EXIT
Develop a code fragment that compares two integers, X and Y . Depending on the result of comparison, the X variable takes the value of Y if X is greater than Y , and remains unchanged if X is less or equal to Y .
In C++, this code fragment looks like this:
if (X > Y) X = Y;
Here is its implementation in the assembler:
mov EAX, DWORD PTR Y cmp EAX, DWORD PTR X jge EXIT mov DWORD PTR X, EAX EXIT:
Since 32-bit operands are used, all variables and registers are declared appropriately. The cmp comparison command cannot be executed if both its operands are located in the memory, so one of them ( Y in this case) is put to the EAX register. The result is stored in the X variable.
The following fragment of code computes the sum of two integers, X and Y , if both are within the range from 1 to 100.
In Visual C++, this fragment of code looks like this:
if ((X <= 100 && X >= 1) && (Y <= 100 && Y >= 1)) X = X + Y
The code in the assembler is shown in Listing 4.1.
. . . cmp DWORD PTR X, 1 jge check_x100 jmp EXIT check_x100: cmp DWORD PTR X, 100 jle check_y1 jmp EXIT check_y1: cmp DWORD PTR Y, 1 jge check_y100 jmp EXIT check_y100: cmp DWORD PTR Y, 100 jg EXIT mov EAX, DWORD PTR Y add DWORD PTR X, EAX EXIT: . . .
As you can see from the algorithm, in order to find the sum of X and Y , you need to check at least four conditions:
X is greater or equal to 1
Y is greater or equal to 1
X is less or equal to 100
Y is less or equal to 100
Only if these four conditions are true simultaneously can you assign the X variable the value of the X + Y expression. To implement such a task, you should break the condition
(X <= 100 && X >= 1) && (Y <= 100 && Y >= 1)
into the following four simpler constructions:
X <= 100, X >= 1, Y <= 100, Y >= 1
The task is simpler now. Each of the four conditions can be checked easily with the cmp command. For example, checking the X < = 100 condition and the subsequent jump is done as follows:
cmp DWORD PTR X, 100 jle check_y1
The other checks can be done with similar combinations of commands.
It is important to note that an assembly analog of a high-level language construction is not necessarily obvious, and this is illustrated in the following example.
Develop some code to compute the absolute value of the X integer. A possible variant of implementing such a task involves the if else construction.
In Visual C++, it will look like this:
if (X >= 0) AbsX = X else AbsX = X
where AbsX is a variable that stores the absolute value of X . Here is an implementation of this construction in the assembler:
cmp DWORD PTR X, 0 jl NOT_X jmp EXIT NOT_X: neg DWORD PTR X EXIT: mov EAX, DWORD PTR X mov DWORD PTR AbsX, EAX
An assignment statement is executed in both the if and else branch. These two assignments can be combined and put at the end of the fragment of code:
mov EAX, DWORD PTR X mov DWORD PTR AbsX, EAX
The else branch is implemented in the assembler with the command:
NOT_X: neg DWORD PTR X
The result is stored in the Absx variable.
Now, we will solve a simple problem. Find the maximum of two integers and assign it to a third variable. An appropriate C++ .NET console application would consist of a few lines of code, and its code is shown in Listing 4.2.
// IF_ELSE_SETCC.cpp : Defines the entry point // for the console application #include "stdafx.h" int _tmain(int argc, _TCHAR* argv[]) { int i1, i2, ires; while (true) { printf("\n"); printf("Enter first number (i1): "); scanf("%d", &i1); printf("Enter second number (i2): "); scanf("%d", &i2); if (i1 >= i2) ires = i1; else ires = i2; printf("Maximum = %d\n", ires); } return 0; }
As you can see from the listing, a comparison is made with the following statements:
if (i1 >= i2) ires = i1; else ires = i2;
Now, we will try to optimize the code fragment that contains the if else statement. It will be convenient to use the Visual C++ .NET inline assembler for this purpose. An assembly language analog of a conditional statement will look like this:
_asm { mov EAX, i1 mov EBX, i2 cmp EAX, EBX jge set_ires xchg EAX, EBX set_ires: mov ires, EAX }
This fragment of code is simple and does not require additional explanation. You can create even more effective code if you get rid of branches and jumps in the program or at least minimize their number. Processors such as Pentium II and higher include a number of commands that allow you to effectively implement branches in a program. Among these commands are setcc (set conditionally) and the cmov and fcmov commands. By combining these, you can achieve significant results, while improving the performance of your applications.
An assembly analog of the if else statement that uses the setge and cmovl commands looks like this:
_asm { xor EBX, EBX mov EAX, i1 mov EDX, i2 cmp EAX, EDX setge BL mov ires, EAX cmp BL, 1 cmovl EAX, EDX mov ires, EAX }
First, the EBX register is zeroed because it will be used as a greater than or less than indicator. The first number ( i1 ) is put to the EAX register, and the second number ( i2 ) to the EDX register. If the contents of the EAX register are greater than or equal to those of the EDX register, the setge BL command writes a one to the low order part of EBX ; otherwise , zero will remain in EBX . If BL=0 , the contents of EDX are put to the EAX register. Before the last command, the EAX register contains the maximum, which is stored in the ires variable. As you can see from this fragment of code, there are no branches and jumps. Before you use the cmov command, you should make sure that your processor supports it. The check can be done with the cpuid command.
The complete code of the console application is shown in Listing 4.3.
// IF_ELSE_SETCC.cpp : Defines the entry point // for the console application #include "stdafx.h" int _tmain(int argc, _TCHAR* argv[]) { int i1, i2, ires; while (true) { printf("\n"); printf("Enter first number (i1): "); scanf("%d", &i1); printf("Enter second number (i2): "); scanf("%d", &i2); _asm { xor EBX, EBX mov EAX, i1 mov EDX, i2 cmp EAX, EDX setge BL mov ires, EAX cmp BL, 1 cmovl EAX, EDX mov ires, EAX } printf("Maximum=%d\n", ires); } return 0; }
The window of the application is shown in Fig. 4.1.