I discussed the Pointer [To] data type in an article published at the GFA=BASIC 32 site: http://gfabasic32.googlepages.com/thepointertodatatype. It is actually quite accurate ;-). However, I needed to re-investigate the Pointer data type, because I got some unexpected 'Unexpected Exceptions'. I had to extend the investigations by studying disassemblies and the Gfa_Var object. I also had to reset my knowledge entirely and start from scratch all over again.
In the article I published I focussed on comparing a Pointer To to a normal data type. I tried to proof that a Pointer data type isn't so different from a normal variable. Although, I am correct in this article I missed an important aspect of pointers. I simply didn't realize that GFA-BASIC 32 handles a Pointer data type differently, and I didn't realize it because I didn't inspect the resulting code generated by the compiler.
Actually, the first lines of the first article signal the problem: "Pointer is a data type to declare variables as pointers.", and then the syntax is given as:
Dim p As [Register] Pointer [To] type
p: pointer variable
type: any data type (Int, String, Double, user-defined-type)
This declaration is complete but not accurate enough, because a pointer to an Ocx object is allowed too. In fact, you may declare a pointer to anything that GFA-BASIC 32 considers a data type. And this might proof to be very important, because GFA-BASIC 32 considers something to be a data type when it is an element of the internal TypeInfo hash table containing data types. How these data types are added to this hash table, GFA doesn't care! For instance, when you load a $Library, the data types from the lg32 are appended to this hash table, and the same is true when GFA-BASIC 32 starts and initializes all primary data types (Int, Bool, String, Hash, Variant, etc, etc.), they are simply added to the hash table. (Since I'm trying to implement the Class data type, this information is essential. GFA-BASIC doesn't care about a type as long as it is in the 'TypeInfo' hash table. We'll see where this will go.)
Ok, back to the problem. In the declaration it is explicitly stated that a pointer can be located in a register, see the optional Register keyword. This actually indicates a different treatment of a pointer variable! A variable of type Double (8-bytes) cannot be located in a register, because a register is only 32-bits (4-bytes) wide. It is beyond this post how GFA stores (local) variables relative to the ebx register and reserves edi and esi for Register variables. Maybe in a later post; it isn't relevant to the topic at hand. What is important, is that GFA indeed handles a pointer type different, it handles a Pointer variable indeed as 32-bits pointer, otherwise you couldn't store a pointer (to any type) in a 32-bits register, would you? Wow. How did I miss this?
Well, I'm only human too, you know. All I did was investigate the information GFA-BASIC 32 returned from a (pointer) variable using GFA functions like TypeName, VarPtr, and ArrPtr. I only wanted to know how the returned information differed form a normal variable compared to a pointer variable. As I pointed out in the article, the only difference seems to be the lack of a memory location for the data for a variable. Of course, this is true, but GFA-BASIC also generates different code to access the data of a pointer variable. Since I didn't inspect the disassembly (DisAsm Ocx object) I didn't see it. As it turned out, GFA-BASIC uses the C pointer-style to access a memory location through a pointer; the GFA-BASIC pointer type is compatible with the C-pointer. Since I will try to keep this post not too technical, I will limit the assembly code a minimum. Important to relaize is that a pointer can be kept in a 32-bit register, which doesn't apply for variables wider than 32-bits. Unfortunately, the compiler won't accept the statement:
Local p1 As Register Pointer To Double ' double *p1;The syntax control accepts it, but the compiler doesn't put p1 in a register. Schade (unfortunately). On the other hand the speed increase would be minimal, it is the difference of using a register directly:
mov dpt [esi], value ; set first long pointed to in esi
and of first copying the pointer to eax from the its (stack) position:
mov eax, dpt 116[ebx] ; move pointer content
mov dpt [eax], value ; set first long to value
GFA also optimizes te code by keeping the pointer in eax when the pointer is accessed multiple times in a row (see for yourself).
Now we are where I want it to be. The compiler generates assembler code to access the data of a variable declared with Pointer [To] through a pointer, like C. For instance the second long eax points to is accessed using
mov dpt 4[eax], value ; set second long
And this is completely different from the code used to access a non-pointer variable.
As a conclusion I will summarize how to treat a pointer in GFA compared to C:
' Declare and initialize a double
Dim dbl As Double = 3.1 ' double dbl = 3.1;
' Declare an unitialized pointer to double
Dim p As Pointer To Double ' double *p;
' Initialize the pointer
Pointer(p) = V:dbl ' p = &dbl;
' Assign a value to dbl through p:
p = 7.2 ' *p = 7.2
In the next post I discuss the ByRef and Pointer commands.
Post a Comment