Faster execution of subroutines.
Sub | Proc[edure] | Function[Var] name () [As Type] Naked
It is important to understand that the GFA-BASIC 32 Naked modifier isn't the same as the naked keyword in MSVC. In GFA-BASIC 32 Naked instructs the compiler to generate a minimum of prologue and epilogue code, in MSVC naked doesn't generate prologue and epilogue at all. In fact, the GFA-BASIC 32 Naked attribute generates the same prologue and epilogue code MSVC does for a normal function. Naked instructs the compiler to generate code much like C. In GFA-BASIC 32 Naked results in the fastest possible code (assembler excluded).
Using Naked comes with a severe penalty, though. All safety nets are removed and an exception definitely crashes the program. Try/Catch cannot be applied to Naked subroutines, as well as debugging. Naked implicitly implies $StepOff for the entire subroutine. Local variables that require additional memory of the heap are to be released explicitly. For a local string, variant, and array, the descriptors are placed on the stack and will be removed, but the allocated memory isn't. A string must be released by setting it to "" and an array must be erased (Erase). A Variant must be assigned a safe value (Int, Float, whatever as long as it doesn't require additional memory). Any objects that are referenced must be set to Nothing explicitly.
From the above it is clear that a normal subroutine performs quite some housekeeping. The normal prologue code of a GFA-BASIC 32 routine sets up a table for all local variables and releases their contents at the end of the routine (EndSub, Exit Proc, Return, etc). It also includes code to step through the code line by line and keeps record of the current executing line so that in case of an error the line can be marked in the editor. Finally, it includes code to create an error trap using Try/Catch or On Error. Everything that makes BASIC programming easy is left out when Naked is applied. Naked is for advanced programmers only, although some subroutines might be naked without much background knowledge. See example.
Local t1#, t2#, n As Int32, a$ = "A", res?
t1# = Timer : For n = 1 To 100000 : res? = IsAlpha(Asc(a$)) : Next n : t1# = Timer - t1#
t2# = Timer : For n = 1 To 100000 : res? = IsAlpha_nn(Asc(a$)) : Next n : t2# = Timer - t2#
Print "Time Test for IsAlpha:"
Print "Naked version: "; Format(t1#, "0.######"); " secs"
Print "Normal version: "; Format(t2#, "0.######"); " secs"
Print "Performance Increase: "; Format((t2# / t1#) - 1, "###%")
t1# = Timer : For n = 1 To 100000 : res? = IsAlnum_(Asc(a$)) : Next n : t1# = Timer - t1#
t2# = Timer : For n = 1 To 100000 : res? = IsAlnum_nn(Asc(a$)) : Next n : t2# = Timer - t2#
Print "Time Test for IsAlnum:"
Print "Naked version: "; Format(t1#, "0.######"); " secs"
Print "Normal version: "; Format(t2#, "0.######"); " secs"
Print "Performance Increase: "; Format((t2# / t1#) - 1, "###%")
t1# = Timer : For n = 1 To 100000 : res? = IsUpper(Asc(a$)) : Next n : t1# = Timer - t1#
t2# = Timer : For n = 1 To 100000 : res? = IsUpper_nn(Asc(a$)) : Next n : t2# = Timer - t2#
Print "Time Test for IsUpper:"
Print "Naked version: "; Format(t1#, "0.######"); " secs"
Print "Normal version: "; Format(t2#, "0.######"); " secs"
Print "Performance Increase: "; Format((t2# / t1#) - 1, "###%")
Function IsAlpha(a As Int) As Bool Naked
// Alphabetic (A - Z or a - z)
IsAlpha := (a >= 65 && a <= 90) || ( a >= 97 && a <= 122)
EndFunction
Function IsAlpha_nn(a As Int) As Bool
// Alphabetic (A - Z or a - z)
IsAlpha_nn := (a >= 65 && a <= 90) || ( a >= 97 && a <= 122)
EndFunction
Function IsAlnum_(a As Int) As Bool Naked
// Alphanumeric (A - Z, a - z, or 0 - 9)
IsAlnum_ := (a = 95) || (a >= 48 && a <= 57) _
|| (a >= 65 && a <= 90) || ( a >= 97 && a <= 122)
EndFunction
Function IsAlnum_nn(a As Int) As Bool
// Alphanumeric (A - Z, a - z, or 0 - 9)
IsAlnum_nn := (a = 95) || (a >= 48 && a <= 57) _
|| (a >= 65 && a <= 90) || ( a >= 97 && a <= 122)
EndFunction
Function IsUpper(a As Int) As Bool Naked
IsUpper := (a = Asc(Upper(Chr(a))))
EndFunction
Function IsUpper_nn(a As Int) As Bool
IsUpper_nn := (a = Asc(Upper(Chr(a))))
EndFunction
A normal GFA-BASIC 32 subroutine does not guarantee anything about the contents of processor registers when exiting and returning to the caller. Just before returning GFA-BASIC 32 calls a library function that clears the local variables and resets the stack. In the process register variables are used and any value assigned to the register is deleted. This is why a Procedure used as a call back function that returns a value through the eax register must be Naked; the library call to release the local variables is not made. Therefore, you will see procedures like these:
Proc WndProc(hWnd As Handle, msg As Int, wParam As Int, lParam As Int) Naked
Local RetVal
//... Code ...
Asm mov eax, [RetVal]
EndProc
However, when a Function is used as a call back subroutine, you can simply use the Return statement to return a value to the caller. Values returned from a Function are always passed in the eax register. Now the subroutine doesn't need to be Naked and Try/Catch error trapping can be implemented.
Naked must be used when porting _fastcall functions. Without Naked GFA-BASIC 32 puts prologue and epilogue code in the function that obscures the registers used for parameter passing and returning.
The next sample shows the amount of stack memory for a recursive function. Note that the string is allocated in the caller. A special string optimizing feature of the compiler allows this construction.
Print // OpenW 1
Print abc("test", 9)
Do
Sleep
Loop Until Me Is Nothing
Function abc(a$, c%) As Int Naked
Local r%
Static p% = V:r
Print V:r - p
If c% > 0
abc = abc(a$, c% - 1) + 1
EndIf
EndFunc
Sub, Procedure, Function, $StepOff
{Created by Sjouke Hamstra; Last updated: 24/06/2017 by James Gaite}