• Welcome to Jose's Read Only Forum 2023.
 

Theo, Show Us The Beef!

Started by Donald Darden, May 21, 2007, 11:43:53 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Donald Darden

Theo, I found your posts on checking compiler optimization to be both interesting and instructive.  But you did not really tell people about the tools that you used or how you achieved your results.  Perhaps you might like to provide an example and either the tools in an archive or a link to where people can get them.

I noted that when using Assembler in PowerBasic, that you can possibly run into
a conflict situation.  If you permit PowerBasic to use registers for some variables, using the #REGISTER ALL, #REGISTER DEFAULT, or REGISTER directives and statement.  That is, if PowerBasic assigns a variable to a register for faster processing, and you attempt to access that variable from memory using some assembler instructions, you will bypass and defeat the efforts of the compiler to perform optimization there.  Further, the compiler will overwrite your changes when it attempts to return the assigned register contents to the same memory location.

Unfortunately, when you use REGISTER, you can designate which variable to assign to an available register, but you are not able to designate which register
to use.  The compiler is keeping track of the registers as they are allocated to
different variables, and only it knows which register may represent any given
variable.  You should be able to work out which register is involved in a given set of circumstances (by tracking which register reflects the same contents as the variable has), but this is at best an uncertain process.

So the problem is, if you want to improve performance by writing some assembler
code, you may have to use #REGISTER NONE so as to avoid possible conflicts
with the compiler's allocation scheme for variables, and that reduces the amount
of optimization that the compiler will attempt.

Another issue is accessing passed parameters for Subs and Functions that are
placed on the stack.  How do you get to them?  Well, there are several methods, but first you have to know how those parameters are being passed.
There are generally three ways:  BYREF, BYVAL and BYCOPY.  BYREF is a
reference to the variable, it is not the variable directly.  The reference is
actually a pointer, which is 32-bits long.  It points to the variable in memory.
For integers and floating point numbers, it is pointing to the value.  For UDTs, it
is pointing to the first byte of that user defined type.  But for strings, it equates
to as string reference, which consists of another pointer to the actual place
where the string's first byte is (this pointer is also 32 bits), followed by a 32-bit
length designator for that sequence of bytes (string),

So the BYREF parameter for a string is effectively the same as using VARPTR()
on that string, and the dereference of that pointer, using the first 32 bits, is
the same as STRPTR(), and then using the second 32-bits from that pointer,
we get the same as LEN().

Often, you see people defining LOCAL variables inside a Sub or Function, then
assigning a passed parameter to a given local variable before trying to use it
with assembler code.  No harm in that, and in fact it can be beneficial at times.
For instance, you can pass a dynamic variable length string, then assign it to
a local ASCIIZ string with a fixed length, and PowerBasic will automatically
convert from one format to the other.  This is handy when calling APIs that
do not support the first string format, or when you want your own code to
loop through a string until it encounters a zero byte ($NUL character).

But if you know how to access the parameters already on the stack from your
assembler code, you gain an edge in speed and cut the amount of code and
memory required.  BYVAL actually puts a numeric value on the stack rather than
the pointer reference, so you would not use this for passing a string.  BYCOPY
is what the compiler will use when you pass a string constant or string construct, by creating a copy of the string in memory, then passing a pointer
reference to the copy.  I've read that you also get BYCOPY if you pass a
numeric expression rather than a number variable, but I would take it that this
means that you requested it be passed BYREF.  In other words, PowerBasic
reverts to BYCOPY when you request (or default to) BYREF, and there is no
specific variable to point back to.  So the compiler creates a temorary one and
points to it instead.




Bernard Ertl

QuoteUnfortunately, when you use REGISTER, you can designate which variable to assign to an available register, but you are not able to designate which register to use.

For integer class variables, the first register variable defined is *always* the ESI register and the second variable is the EDI variable.

Theo Gottwald

#2
@Donald, to get the beef, ask Jose, I am strict vegetarian :-).
You may even ask him for "jabugo jam" I think he may show you that!

But no jokes, I thought about that.
I'll show you all the tools and how to do it, soon!

I could do it, see here:
http://www.jose.it-berater.org/smfforum/index.php?topic=786.0

Theo Gottwald

Quote from: Donald Darden on May 21, 2007, 11:43:53 PM
I noted that when using Assembler in PowerBasic, that you can possibly run into a conflict situation. 

Let me say that I have never had this problem in real life until now. Thats why I can not say that this is a real problem for me.
I remember only 1 theoretical issue, in which i know I should have used #REGISTER NON ever.
Because its just like Bernard says: ESI and EDI are used. They do not conflict with regular operations, there seem to be one exception:

Quoteyou may need to use #REGISTER NONE in a function that makes uses of byref variants because register variables can be used and will give an error.
I got this out of an mail from Jose, I did never check if this problem exists - but I also never had errors because of this.

QuoteIf you permit PowerBasic to use registers for some variables, using the #REGISTER ALL, #REGISTER DEFAULT, or REGISTER directives and statement. 
Looking claser at this statement, we must say that these statements allow PB to use the registers for internal usage.
I can recommend them, while I think they will rather help to shorten your code a bit, then make it really a lot faster in most practical cases.
You know, ASM-Mnemonics that access Registers are a bit shorter then those accessing a Menmory Location with an Increment.

QuoteThat is, if PowerBasic assigns a variable to a register for faster processing, and you attempt to access that variable from memory using some assembler instructions, you will bypass and defeat the efforts of the compiler to perform optimization there. 
You do exactly that, by using #REGISTER NONE.

QuoteUnfortunately, when you use REGISTER, you can designate which variable to assign to an available register, but you are not able to designate which register to use.
Good luck, you can now use the DisASM from my post, to see that Bernards statements below are true. In fact you can think of the two Registers ESI an EDI as normally unused by Powerbasic. They just get used when you use Register variables.

QuoteSo the problem is, if you want to improve performance by writing some assembler code, you may have to use #REGISTER NONE so as to avoid possible conflicts with the compiler's allocation scheme for variables, and that reduces the amount of optimization that the compiler will attempt.

As you know now, you can use #REGISTER and still use ASM, and I do that a lot, had never problems until now. Of course, if I am goind to use ESI and EDI, then I may need to do a #REGISTERS NONE as you suggest.


QuoteOften, you see people defining LOCAL variables inside a Sub or Function, then assigning a passed parameter to a given local variable before trying to use it with assembler code.

You can normally just use the variable name in PB with the ASM command. PB is really god in replacing it with the adress in memory where the variable is.
The replacement may be necessary, if you want to change the variable just local, and it was given BYREF, not BYVAL.

Donald Darden

Theo, it is no accident that you have had few problems with the possible conflicts
between the Inline Assembler and PowerBasic per se.  As I said, if you use the REGISTRY option under PowerBASIC, then messing with the ESI, and possibly the
EDI registers might cause problems.  You acknowledged that happened to you in one case, and your solution was to use #REGISTER NONE, the same solution that I proposed.

The other conflict case would be if you were to change EBP or ESP, then try to
exit a procedure before restoring them.  I've also seen several references to not
changing either the DS or ES segment registers, but I'm having trouble sourcing
where those restraints were imposed.  Some of my understanding dates back to
PB/DOS, and it may have been in that context.  So if you want to try, go ahead.
Just try to give PowerBasic back what it had prior to your changes. and you should have few problems.

Most programs I've seen written to work with PB/CC or PB/Win have relied on the
EBX register for pointer references, and that works quite well.  The EBX register
has added support for indirect addressing, making it a good fit.  If that has been
one of your preferences, you would not experience any problems with PowerBasic's use of BASIC code statements.  PowerBasic's Help file does warn you to "preserve" (save and restore are a method of preserving) the registers EBX, ESI, and EDI by your Sub or Function.


Theo Gottwald

#5
QuoteJust try to give PowerBasic back what it had prior to your changes. and you should have few problems.

We can take that as a conclusion in the discussion.
Its the final word in preserving registers.

:-)