Visual Basic

Using the Compiler to Optimize Your Code

The effect of the optimization options (on the Compile tab of the Project Properties dialog box and in the Advanced Optimizations dialog box) on how C2.EXE and LINK.EXE are driven is summarized in Table 7-4 (for building a standard EXE).

Obviously, -G6 means favor the Pentium Pro.

Notice that most of the switches have no effect on how C2 or LINK are started (although the EXE size changes so that we know the option is making itself known!). Since most switches have no effect, we must assume they are being acted on within VB6.EXE itself (as it seems to contain the compiler's first pass). Or perhaps the mystery files shown earlier (VB603389GL, VB603389SY, VB603389EX, VB603389IN, and VB603389DB) have some way of influencing the code generator, thus sidestepping our efforts to understand how the process is being controlled.

Table 7-4 The Compiler Effect

Optimization Option C2.EXE Effect LINK.EXE Effect
Optimize For Small Code None None
Optimize For Fast Code None None
Favor Pentium Pro /G6 (from G5) None
Create Symbolic Debug Info /Zi /DEBUG
/DEBUGTYPE:CV
Assume No Aliasing None None
Remove Array Bounds Checks None None
Remove Integer Overflow Checks None None
Remove Floating Point Error Checks None None
Allow Unrounded Floating Point Operations None None
Remove Safe Pentium(tm)FDIV Checks /QIfdiv Removed None

Advanced Optimizations

Microsoft generally encourages you to play around with what they call the safe compiler options. Naturally, these are options that aren't situated beneath the Advanced Optimizations button. For those options Microsoft usually provides a disclaimer: "These might crash your program." Let's see what these Advanced Optimizations are about and why this warning is given. (See Table 7-5)

Table 7-5 Advanced Optimizations Options

Option Description
Allow Unrounded Floating Point Operations Allows the compiler to compare floating-point expressions without first rounding to the correct precision. Floating-point calculations are normally rounded off to the correct degree of precision (Single or Double) before comparisons are made. Selecting this option allows the compiler to do floating-point comparisons before rounding, when it can do so more efficiently. This improves the speed of some floating-point operations; however, this may result in calculations being maintained to a higher precision than expected, and two floating-point values not comparing equal when they might be expected to.
Assume No Aliasing Tells the compiler that your program does not use aliasing (that your program does not refer to the same memory location by more than one name, which occurs when using ByRef arguments that refer to the same variable in two ways). Checking this option allows the compiler to apply optimization such as storing variables in registers and performing loop optimizations.
Remove Array Bounds Checks Disables Visual Basic array bounds checking. By default, Visual Basic makes a check on every access to an array to determine if the index is within the range of the array. If the index is outside the bounds of the array, an error is returned. Selecting this option will turn off this error checking, which can speed up array manipulation significantly. However, if your program accesses an array with an index that is out of bounds, invalid memory locations might be accessed without warning. This can cause unexpected behavior or program crashes.
Remove Floating Point Error Checks Disables Visual Basic floating-point error checking and turns off error checking for valid floating-point operations and numeric values assigned to floating-point variables. By default in Visual Basic, a check is made on every calculation to a variable with floating-point data types (Single and Double) to be sure that the resulting value is within the range of that data type. If the value is of the wrong magnitude, an error will occur.

Error checking is also performed to determine if division by zero or other invalid operations are attempted. Selecting this option turns off this error checking, which can speed up floating-point calculations. If data type capacities are overflowed, however, no error will be returned and incorrect results might occur.

Remove Integer Overflow Checks Disables Visual Basic integer overflow checking. By default in Visual Basic, a check is made on every calculation to a variable with an integer data type (Byte, Integer, Long, and Currency) to be sure that the resulting value is within range of that data type. If the value is of the wrong magnitude, an error will occur. Selecting this option will turn off this error checking, which can speed up integer calculations. If data type capacities are overflowed, however, no error will be returned and incorrect results might occur.
Remove Safe Pentium FDIV Checks Disables checking for safe Pentium floating-point division and turns off the generation of special code for Pentium processors with the FDIV bug. The native code compiler automatically adds extra code for floating-point operations to make these operations safe when run on Pentium processors that have the FDIV bug. Selecting this option produces code that is smaller and faster, but which might in rare cases produce slightly incorrect results on Pentium processors with the FDIV bug.
By using the Visual C++ debugger (or any compatible debugger) with Visual Basic code that has been compiled to contain symbolic debugging information, it's possible to see more of what each option does to your code. By way of explanation, here are a few annotated examples (obviously you won't expect to see commented code like this from a debugger!):

Integer Overflow

  Dim n As Integer
  n = 100 * 200 * 300

Disassembly (without Integer Overflow check)

  ' Do the multiplication - ax = 300
  mov         ax,offset    Form::Proc+4Ch
  ' Signed integer multiplication. 300 * 20000
  ' The 20000 is stored in Form::Proc+51h and was
  ' created by the compiler from the constant exp.
  ' 100 * 200 and held as 'immediate data'
  imul        ax,ax,offset Form::Proc+51h
  n = Result
  mov         word ptr [n],ax

Disassembly (with Integer Overflow check)

  ' Do the multiplication - ax = 100
  mov         ax,offset    Form::Proc+4Ch
  imul        ax,ax,offset Form::Proc+51h
  ' Jump to error handler if the overflow flag set
  jo          ___vbaErrorOverflow
  Else, n = Result
  mov         word ptr [n],ax

Array Bounds

    Dim n1        As Integer
    Dim n(100)    As Integer
    n1 = n(101)

Disassembly (without Array Bounds check)

  ' Sizeof(Integer) = 2, put in eax
  push        2
  pop         eax
  ' Integer multiplication.  2 * 101 (&H65) = result in eax.
  imul        eax,eax,65h
  ' Get array base address in to ecx.
  mov         ecx,dword ptr [ebp-20h]
  n(101) (base plus offset) is in ax
  mov         ax,word ptr [ecx+eax]
  n1 = n(101)
  mov         word ptr [n1],ax

Disassembly (with Array Bounds check)

  ' Address pointed to by v1 = 101, the offset we want
  mov         dword ptr [unnamed_var1],65h
  ' Compare value thus assigned with the known size of array + 1
  cmp         dword ptr [unnamed_var1],65h
  ' Jump above or equal to 'Call ___vbaGenerateBoundsError'
  jae         Form1::Proc+6Dh
  ' Zero the flags and a memory location.
  and         dword ptr [ebp-48h],0
  ' Jump to 'mov eax,dword ptr [unnamed_var1]'
  jmp         Form1::Proc+75h
  ' Raise the VB error here
  call        ___vbaGenerateBoundsError
  ' Store element number we want to access
  mov         dword ptr [ebp-48h],eax
  ' Get the element we wanted to access into eax
  mov         eax,dword ptr [unnamed_var1]
  ' Get array base address in to ecx.
  mov         ecx,dword ptr [ebp-20h]
  ' n(101) is in ax (* 2 because sizeof(Integer) = 2
  mov         ax,word ptr [ecx+eax*2]
  ' n1 = n(101)
  mov         word ptr [n1],ax
  Floating Point Error
  Dim s As Single
  s = s * s

Disassembly (without Floating Point Error check)

  ' Pushes the specified operand onto the FP stack
  fld         dword ptr [s]
  ' Multiplies the source by the destination and returns
  ' the product in the destination
  fmul        dword ptr [s]
  ' Stores the value in the floating point store (ST?)
  ' to the specified memory location
  fstp        dword ptr [s]

Disassembly (with Floating Point Error check)

  fld         dword ptr [s]
  fmul        dword ptr [s]
  fstp        dword ptr [s]
  ' Store the floating point flags in AX (no wait)
  fnstsw      ax
  ' Test for floating point error flag set
  test        al,0Dh
  ' Jump if zero flag not set
  jne         ___vbaFPException

You should now have more of a feel for why these options are left partially obscured like they are, and for the warning given by Microsoft. Without a native code debugger it's really hard to see just how your code's being affected. Even with a debugger like the one that comes with Visual Studio it's not a straightforward task to read through the assembly-language dumps and state that your code is cool and that the optimizations you've chosen are safe!