28 November 2015

A Function creates a silent local variable

The documentation on Naked shows an optimized example of a windows-procedure callback. Interesting is the fact, that the example shows GB-code using a Procedure rather than a Function. The code shows how to pass a return value to the caller of WndProc.

Proc WndProc(hWnd As Handle, msg As Int, _
  wParam As Int, lParam As Int) Naked
  Local RetVal
  '... Code ...
  Asm mov eax, [RetVal]

The example is misleading in the assumption that you need to use a Procedure rather than a Function!

The example merely shows how to use a local variable RetVal to return a value to the caller of the subroutine, which in this particular case is the Windows OS. The assembler instruction mov eax, [RetVal] copies the value to return from the local variable in the eax-register. Returning a 32-bit value through eax, and 64-bit values through eax and edx, is conform the Windows StdCall convention. The caller of the subroutine knows from this convention that any return value (32-bits) is placed in eax. The caller may examine the eax register and can respond accordingly.

A Function is easier and safer
Most of us won’t use a Procedure when creating a callback. Instead, we will use a Function so that the need for an explicit Asm command disappears. With Function the GB-compiler generates the code to return a value. Because the compiler takes into account the Function’s data type, it generates code to put values into the correct registers. Maybe, in case of returning a simple Long value, putting a value into the eax register isn’t very hard, but when it comes to other types, like floating-point values or even Currency values, everything becomes more complicated.
For these reasons you will always turn to the use of Functions, you do not want to fiddle around with assembler instructions just to return some value.

Procedure and Function are not so different
On a pure theoretical level something interesting can be deduced. When a Function and a Procedure-returning-a-value are compared they actually are quite the same. For a Function the compiler silently inserts a local variable of the function’s data type and name. To return a value the Return value (or Exit Func) statement copies the value into the local variable - before it copies the value to the proper register(s). The function behaves the same as a procedure with an explicit local variable.
Consequently, a Function and a Procedure-returning-a-value are equally efficient, also when Naked is used. Both need a local variable to temporarily save a return value, which is later copied to the appropriate register(s). On the other hand the Procedure type doesn’t need a local variable to store a return value. Everything can be handled by registers using assembler. When using Naked the Sub or Procedure type can be used to create highly optimized code using assembler.

Function and Naked
When performance is a concern (hardly these days so it seems), you should realize that the compiler always silently inserts a local variable when it compiles a Function statement, even when the function doesn’t declare local variables it self. The compiler generates VB-compatible code for a Function and VB uses a local variable to return a value from a function. This leads to a small decrease in performance, because he compiler has to set up prologue and epilogue code for the Function. Naked has does not omit prologue and epilogue code generation, because of the use of a silent, hidden silent local variable to be stored in the stack.

When a Sub or Procedure doesn’t use local variables and parameters Naked optimizes by omitting prologue and epilogue code. For optimization reasons it might be useful to use a Naked Procedure and return a value using assembler. A Procedure does not add an additional local variable and lends itself better for using assembler or for creating optimized code.

No comments:

Post a Comment