Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
A For-Next bug in 1.2.8-s682
#1
This works:

Code:
For i = AttrAddress to AttrAddress + NumberOfCells - 1
  Poke i, AttrColor
Next i

This does not:

Code:
For AttrAddress = AttrAddress to AttrAddress + NumberOfCells - 1
  Poke AttrAddress, AttrColor
Next AttrAddress

This would work in other dialects.

Thanks,

Darkstar
Reply
#2
Darkstar Wrote:This works:

Code:
For i = AttrAddress to AttrAddress + NumberOfCells - 1
  Poke i, AttrColor
Next i

This does not:

Code:
For AttrAddress = AttrAddress to AttrAddress + NumberOfCells - 1
  Poke AttrAddress, AttrColor
Next AttrAddress

This would work in other dialects.

Thanks,

Darkstar
This is related to the ZX Basic to have "Dynamic" evaluation on each pass. In your loop:
Code:
For i = i to i + N - 1
  Poke i, AttrColor
Next i
the expression i + N - 1 is evaluated *on each pass* (like in C). On most BASIC dialects (including Sinclair BASIC), that expression is only evaluated the 1st time (on entering the loop). On each pass, i + N - 1 is recalculated, and since it's always increasing, the loop will eventually overflow (or run forever).
At the moment, the only way to fix this is:
Code:
LET z = i + N - 1
For i = i to z
  Poke i, AttrColor
Next i
Can you check this?

Which produces the expected behavior. I'm thinking in fixing this in an more efficient manner, so the user can choose dynamic or static FOR loops with a #pragma directive, and let FOR to be static by default for compatibility's sake. What do you think?
Reply
#3
Boriel,

I think that's probably the right choice, since most basic dialects use it. You can always do a dynamic expression with a do...until (x) style loop, where it feels more natural to have an expression evaluated each loop, I think.

Will static evaluation speed up for loops?
Reply
#4
britlion Wrote:Boriel,

I think that's probably the right choice, since most basic dialects use it. You can always do a dynamic expression with a do...until (x) style loop, where it feels more natural to have an expression evaluated each loop, I think.

Will static evaluation speed up for loops?
Certainly, if they're complex expressions. Static ones are just like using a temporary variable to store them. The problem is... how to compile them? :roll: Using the stack is a problem if the user jumps out the loop using GOTO. Using a static place is also problematic (and a waste of memory). The most straightforward solution is to use a hidden variable and implement it as shown above. This would imply FOR-NEXT variables will take twice the space of a normal variable, anyway (Sinclair BASIC also makes a distinction between normal variables and FOR-NEXT ones, not only in their length name, but also in their internal implementation).
Reply
#5
boriel Wrote:At the moment, the only way to fix this is:
Code:
LET z = i + N - 1
For i = i to z
  Poke i, AttrColor
Next i
Can you check this?

Which produces the expected behavior. I'm thinking in fixing this in an more efficient manner, so the user can choose dynamic or static FOR loops with a #pragma directive, and let FOR to be static by default for compatibility's sake. What do you think?

This would work, no need to check it and it would speed up the current
code. I did think that this was it, that is how the loops were evaluated at
the beginning. This did produce some "undefined" behaviour that did not
make any sense. Is a directive needed? Looks to me that the C style is the
poor mans way of doing For-Next loops. What are the benefits? There are
already some cons as noted above both in terms of workarounds and speed.
If you are thinking about attracting C coders then I think the sacrifice in
consistency, even with a pragma directive, is not worth it.

A man is nothing if not consistent.

Thanks for your(s) reply(s),

Darkstar.
Reply
#6
I also agree with both Britlion and you. I'm going to implement this behaviour by default. If the programmer wants the dynamic behaviour s/he will be able to enable it using something like:
Code:
#pragma for dynamic
or
Code:
#pragma for static
which will be enabled by default (needs not to be used unless you want to switch from/to dynamic for). What do you think?
Reply
#7
boriel Wrote:I also agree with both Britlion and you. I'm going to implement this behaviour by default. If the programmer wants the dynamic behaviour s/he will be able to enable it using something like:
Code:
#pragma for dynamic
or
Code:
#pragma for static
which will be enabled by default (needs not to be used unless you want to switch from/to dynamic for). What do you think?

Well, static should be a default behaviour yes for consistency but is there
any benfits to be derived from dynamic that outweigs the costs? If the
answer is no then why go through the work of implementing a directive and
it would only add to the confusion amoung coders that expect a language
to behave in a certain way in my oppinion.

Darkstar.
Reply
#8
Implementing dynamic is much easier (it's already done) and saves memory comparing to static. Static might be faster sometimes (eg. when using expressions), and take twice the memory for each iterator variable. I think leaving it alone should be ok (since almost no one will be using it). FOR is currently "dynamic" and has not caused much trouble when porting original Sinclair BASIC programs.

In fact FOR loops are expensive in much ways (code, speed, etc). While and DO...LOOP are much faster and simpler.
Reply
#9
boriel Wrote:Implementing dynamic is much easier (it's already done) and saves memory comparing to static. Static might be faster sometimes (eg. when using expressions), and take twice the memory for each iterator variable. I think leaving it alone should be ok (since almost no one will be using it). FOR is currently "dynamic" and has not caused much trouble when porting original Sinclair BASIC programs.

In fact FOR loops are expensive in much ways (code, speed, etc). While and DO...LOOP are much faster and simpler.

If you use expressions then you already have to use an extra variable (double
the amount) if speed is the concern and it always is with computers not to
mention the ZX at 3.5 MHz. It is the classical speed vs size when it comes
to opptimization and you can lay the burden on the programmer or the
compiler. This is a compiler's job and it makes for a cleaner code for
a programers perspective in the end instead of having an extra variable
hanging around that you have to DIM in future versions of this compiler.

Dynamic is not in lieu with the BASIC tradition or desing philosophy and
For-Next loops are a fact of life.

This could lead to pontential trouble in porting, sometimes people have
to revise and rewrite instead of preserving the work they have done. Compiler
writing is about the whole and not the individual in my humble oppinion.

Darkstar.
Reply
#10
Darkstar Wrote:
boriel Wrote:Implementing dynamic is much easier (it's already done) and saves memory comparing to static. Static might be faster sometimes (eg. when using expressions), and take twice the memory for each iterator variable. I think leaving it alone should be ok (since almost no one will be using it). FOR is currently "dynamic" and has not caused much trouble when porting original Sinclair BASIC programs.

In fact FOR loops are expensive in much ways (code, speed, etc). While and DO...LOOP are much faster and simpler.
If you use expressions then you already have to use an extra variable (double the amount)
Not exactly: the code that computes the FOR upper limit expression is the same (regardless its static or dynamic calculated on each iteration). The difference is that once it's used it's discarded (dynamic) or must be stored to be reused later (static). Other than that, there's no difference. I was to implement that anyway (there's another thread in this forum asking for that), but leaving the possibility to use a dynamic for if one wants. I meant: can't see why we must enforce dynamic removal once static is the default behaviour.
Reply
#11
boriel Wrote:
Darkstar Wrote:
boriel Wrote:Implementing dynamic is much easier (it's already done) and saves memory comparing to static. Static might be faster sometimes (eg. when using expressions), and take twice the memory for each iterator variable. I think leaving it alone should be ok (since almost no one will be using it). FOR is currently "dynamic" and has not caused much trouble when porting original Sinclair BASIC programs.

In fact FOR loops are expensive in much ways (code, speed, etc). While and DO...LOOP are much faster and simpler.
If you use expressions then you already have to use an extra variable (double the amount)
Not exactly: the code that computes the FOR upper limit expression is the same (regardless its static or dynamic calculated on each iteration). The difference is that once it's used it's discarded (dynamic) or must be stored to be reused later (static). Other than that, there's no difference. I was to implement that anyway (there's another thread in this forum asking for that), but leaving the possibility to use a dynamic for if one wants. I meant: can't see why we must enforce dynamic removal once static is the default behaviour.

Yes, it throws it away and if you want to retain it then you have to set
up an extra variable in your code and by logic I assume that if you use
the upper limit as an iterator it overwrites the upper limit instead of
the case with static where it is kept as a single background variable.

No need to enforce a removal but I do not see much sense in keeping a
dynamic structure unless you want to shave off a one extra internal variable
and the code to manage it meaning certain limitations. If you want to be
really smart then the compiler could decide which loops would benefit
from a dynamic structure vs a static one, and instead of having two sets
of For-Next code in the compiler just do a branch based on a flag but that
could eat up the savings made so...

If you were going to implement it anyway then that's good, as a default.

Thanks,

Darkstar.
Reply
#12
Darkstar Wrote:No need to enforce a removal but I do not see much sense in keeping a
dynamic structure unless you want to shave off a one extra internal variable
and the code to manage it meaning certain limitations. If you want to be
really smart then the compiler could decide which loops would benefit
from a dynamic structure vs a static one, and instead of having two sets
of For-Next code in the compiler just do a branch based on a flag but that
could eat up the savings made so...
Hey, that's a much nicer idea instead:!: But could be difficult. At the moment, only constant expressions are detected as invariant code.
There's still much work to do in this area. Please, be patient.
Reply
#13
boriel Wrote:Not exactly: the code that computes the FOR upper limit expression is the same (regardless its static or dynamic calculated on each iteration). The difference is that once it's used it's discarded (dynamic) or must be stored to be reused later (static). Other than that, there's no difference. I was to implement that anyway (there's another thread in this forum asking for that), but leaving the possibility to use a dynamic for if one wants. I meant: can't see why we must enforce dynamic removal once static is the default behaviour.

Found it:
<!-- l --><a class="postlink-local" href="http://www.boriel.com/forum/help-support/topic434.html#p1124">help-support/topic434.html#p1124</a><!-- l -->
boriel Wrote:Notice also that FOR upper limit is evaluated ON EACH iteration (like C). So if len(a$) changes, the loop will shorten. So better use a temporary var l to store initial LEN(a$).

Arrays subscripts starts from 0 to N-1 (like in C). But this behavior can be changed using --sinclair or --array-base switch.

NOTE: I'm planning (for compatibility) an --string-base (so LEN and string-slicing will work exactly as in Sinclair BASIC, by specifying --string-base=1). Also with FOR, stating --constant-for etc...

Then how about evaluating the expression once and have internal variables
for Lower, Higher and Step limits that do not chance as I assume that that
is the traditional way in BASIC, or only the upper limit as the loop is already running. It could make for a speed gain.
Reply
#14
You know it occurs to me that it might spot cases where it could keep it on register. Simple loops that don't do much, and live below 256 for B or 65536 for BC..

Remember that code I showed you that made djnz-like speed from a 16 bit counter?

Perhaps implementing a push-pop to bc if it calls something that would need that register. But quite a lot of for-next loops are to a static number, or in the next best case from something like 1 to uInteger. Both of which could be stored in registers, assuming that doesn't get too complicated.

And of course, it would be blinding fast Smile
Reply


Forum Jump:


Users browsing this thread: 3 Guest(s)