none albert schrieb am Mittwoch, 10. Mai 2023 um 13:12:47 UTC+2:
In article <d66b1778-4063-4777...@googlegroups.com>,
minforth <minf...@arcor.de> wrote:
Forth standard section 13.3.3.1.b) stipulates thatIf they were located on the data stack, this would impeded the
"The (locals) storage resource shall not be the data stack."
Where did this restriction come from, and with what justification?
use of the regular data stack.
Why impeded? Stack frames are the only locals store in most other languages.
In a naive implementation, Forth words that leave output items on the stack could shift them 'en bloc' downwards and overwrite the discarded frame location.
This seems easier than moving input items piece-wise away from the stack
to their local identifiers or a separate locals stack.
My question was triggered by whether it makes sense to keep complete
stack frames in CPU registers
given that register-register movements are
fast and cheap.
Perhaps one could get rid of the locals stack as well.
I took https://www.openbookproject.net/py4fun/forth/forth.py as my starting point and have developed it from there.In a naive implementation, Forth words that leave output items on the stackMy Forth implementation is naive, but I'm using it to build web sites and DMX control lighting for stage shows. I use a deque rather a data stack and that way can get a couple of 'locals' at the bottom of the deque. My Forth is built on top of Python.
could shift them 'en bloc' downwards and overwrite the discarded frame location.
This seems easier than moving input items piece-wise away from the stack to their local identifiers or a separate locals stack.
none albert schrieb am Mittwoch, 10. Mai 2023 um 13:12:47 UTC+2:
If they were located on the data stack, this would impeded the
use of the regular data stack.
Why impeded? Stack frames are the only locals store in most other languages.
minforth <minf...@arcor.de> writes:
none albert schrieb am Mittwoch, 10. Mai 2023 um 13:12:47 UTC+2:
If they were located on the data stack, this would impeded the
use of the regular data stack.
Why impeded? Stack frames are the only locals store in most other languages. Most other languages don't have a programmer-visible data stack. Thestack many implementations use is more like the return stack than the
data stack.
As for "impeded", consider:
: map-array ( ... addr u xt -- ... )
{: xt :}
cells bounds ?do
i @ xt execute
1 cells +loop ;
Defined =C3=A0 la
: map-array1 {: addr u xt -- :} ...=20
would cause no such problems.=20
minforth <minf...@arcor.de> writes:
Defined =C3=A0 la
: map-array1 {: addr u xt -- :} ...=20
would cause no such problems.=20
It still would have the same problems:
: map-array1 {: addr u xt -- :}
addr u cells bounds ?do
i @ xt execute
1 cells +loop ;
create v 3 , -2 , 4 ,
v 3 ' . map-array1
0 v 3 ' + map-array1 .
|Where on the data stack would MAP-ARRAY keep the local xt, such that
|the first invocation does not produce a stack underflow, while the
|second can still access the accumulated sum (initially 0)?
Of course with MAP-ARRAY1 you don't have the "..." to warn you that
there may be accesses further down, but they still happen in the
second invocation of MAP-ARRAY1.
minforth <minforth@arcor.de> writes:
none albert schrieb am Mittwoch, 10. Mai 2023 um 13:12:47 UTC+2:
If they were located on the data stack, this would impeded the
use of the regular data stack.
Why impeded? Stack frames are the only locals store in most other languages.
Most other languages don't have a programmer-visible data stack. The
stack many implementations use is more like the return stack than the
data stack.
As for "impeded", consider:
: map-array ( ... addr u xt -- ... )
{: xt :}
cells bounds ?do
i @ xt execute
1 cells +loop ;
create v 3 , -2 , 4 ,
v 3 ' . map-array
0 v 3 ' + map-array .
Where on the data stack would MAP-ARRAY keep the local xt, such that
the first invocation does not produce a stack underflow, while the
second can still access the accumulated sum (initially 0)?
Anton Ertl schrieb am Donnerstag, 11. Mai 2023 um 17:39:10 UTC+2:
minforth <minf...@arcor.de> writes:
Defined =C3=A0 la
: map-array1 {: addr u xt -- :} ...=20
would cause no such problems.=20
It still would have the same problems:
: map-array1 {: addr u xt -- :}
addr u cells bounds ?do
i @ xt execute
1 cells +loop ;
create v 3 , -2 , 4 ,
v 3 ' . map-array1
0 v 3 ' + map-array1 .
|Where on the data stack would MAP-ARRAY keep the local xt, such that
|the first invocation does not produce a stack underflow, while the
|second can still access the accumulated sum (initially 0)?
Of course with MAP-ARRAY1 you don't have the "..." to warn you that
there may be accesses further down, but they still happen in the
second invocation of MAP-ARRAY1.
Maybe splitting hairs here, but . is a unary operator and + a binary operator.
But the stack notation or locals notation does not reflect it.
IOW mingling stack input parameters and locals is problematic as you have shown.
After all Forth is a wonderful language to shoot your own foot when handled carelessly. ;-)
On 12/05/2023 7:12 am, minforth wrote:
Anton Ertl schrieb am Donnerstag, 11. Mai 2023 um 17:39:10 UTC+2:
minforth <minf...@arcor.de> writes:
Defined =C3=A0 la
: map-array1 {: addr u xt -- :} ...=20
would cause no such problems.=20
It still would have the same problems:
: map-array1 {: addr u xt -- :}
addr u cells bounds ?do
i @ xt execute
1 cells +loop ;
create v 3 , -2 , 4 ,
v 3 ' . map-array1
0 v 3 ' + map-array1 .
|Where on the data stack would MAP-ARRAY keep the local xt, such that
|the first invocation does not produce a stack underflow, while the
|second can still access the accumulated sum (initially 0)?
Of course with MAP-ARRAY1 you don't have the "..." to warn you that
there may be accesses further down, but they still happen in the
second invocation of MAP-ARRAY1.
Maybe splitting hairs here, but . is a unary operator and + a binary operator.
But the stack notation or locals notation does not reflect it.
IOW mingling stack input parameters and locals is problematic as you have shown.Passing all inputs solves the general problem. Moore advises don't do that. In one of his essays, Fox remarked at the lack of 'layers' in Moore's code. Presumably he was referencing unnecessary generalizations.
After all Forth is a wonderful language to shoot your own foot when handled carelessly. ;-)
Anton Ertl schrieb am Donnerstag, 11. Mai 2023 um 17:39:10 UTC+2:
minforth <minf...@arcor.de> writes:
Defined =C3=A0 la
: map-array1 {: addr u xt -- :} ...=20
would cause no such problems.=20
It still would have the same problems:
: map-array1 {: addr u xt -- :}
addr u cells bounds ?do
i @ xt execute
1 cells +loop ;
create v 3 , -2 , 4 ,
v 3 ' . map-array1
0 v 3 ' + map-array1 .
|Where on the data stack would MAP-ARRAY keep the local xt, such that
|the first invocation does not produce a stack underflow, while the
|second can still access the accumulated sum (initially 0)?
Of course with MAP-ARRAY1 you don't have the "..." to warn you that
there may be accesses further down, but they still happen in the
second invocation of MAP-ARRAY1.
Maybe splitting hairs here, but . is a unary operator and + a binary operator. >But the stack notation or locals notation does not reflect it.
FD Vol 8 No 3 Stack Number By Name - Rosenfeld M.It resembles the way the LOCALS library on 4tH works. Of course, since it has its own
While the article lacks details (missing page or two?) it's strongly suggestiveThat's exactly what happens in 4tH - when exiting the locals stack has to be adjusted
the stack is adjusted on exit.
Forth standard section 13.3.3.1.b) stipulates that
"The (locals) storage resource shall not be the data stack."
Where did this restriction come from, and with what justification?
I vaguely remember old Forths with "named stack elements".
So "it works" as well...
Given modern CPUs with sufficient registers to hold lots of
stack elements there, that a.m. old restriction does not seem to hold
any more.
In view of RISCV you have easily 8 registers to spare.
You could easily use these registers for locals.
For re-entrant code you have to store the previous values on
the return stack.
All other solutions seem obsolete.
On Thursday, May 11, 2023 at 2:38:59 AM UTC+2, dxforth wrote:
FD Vol 8 No 3 Stack Number By Name - Rosenfeld M.It resembles the way the LOCALS library on 4tH works. Of course, since it has its own
stack, the issues with the data stack top are avoided. https://sourceforge.net/p/forth-4th/code/HEAD/tree/trunk/4th.src/lib/locals.4th
While the article lacks details (missing page or two?) it's strongly suggestiveThat's exactly what happens in 4tH - when exiting the locals stack has to be adjusted
the stack is adjusted on exit.
MANUALLY. It isn't part of core 4tH, so EXIT has no idea what is going on. Of course,
one could add a few (preprocessor) macros to correct that, but then use of the
preprocessor becomes compulsory.
Both the return stack as well as the data stack CANNOT be accessed directly in 4tH
(for obvious reasons), so the "Rosenfeld approach" could NEVER work on 4tH. It's
also one of the reasons that locals on the return stack would become very messy
in thew 4tH environment.
However, R@, R'@ and R"@ (as aliases for I, I' and J) allow the TORS, 2ORS and 3ORS
to be used as a kind of "read-only" local variables. I've used this on several occasions,
most notoriously in the "midpoint circle algorithm": https://sourceforge.net/p/forth-4th/code/HEAD/tree/trunk/4th.src/lib/gcircle.4th
I learned this trick from the FIG editor author.
dxforth schrieb am Freitag, 12. Mai 2023 um 03:34:56 UTC+2:
On 12/05/2023 7:12 am, minforth wrote:
Anton Ertl schrieb am Donnerstag, 11. Mai 2023 um 17:39:10 UTC+2:Passing all inputs solves the general problem. Moore advises don't do that. >> In one of his essays, Fox remarked at the lack of 'layers' in Moore's code. >> Presumably he was referencing unnecessary generalizations.
minforth <minf...@arcor.de> writes:
Defined =C3=A0 la
: map-array1 {: addr u xt -- :} ...=20
would cause no such problems.=20
It still would have the same problems:
: map-array1 {: addr u xt -- :}
addr u cells bounds ?do
i @ xt execute
1 cells +loop ;
create v 3 , -2 , 4 ,
v 3 ' . map-array1
0 v 3 ' + map-array1 .
|Where on the data stack would MAP-ARRAY keep the local xt, such that
|the first invocation does not produce a stack underflow, while the
|second can still access the accumulated sum (initially 0)?
Of course with MAP-ARRAY1 you don't have the "..." to warn you that
there may be accesses further down, but they still happen in the
second invocation of MAP-ARRAY1.
Maybe splitting hairs here, but . is a unary operator and + a binary operator.
But the stack notation or locals notation does not reflect it.
IOW mingling stack input parameters and locals is problematic as you have shown.
After all Forth is a wonderful language to shoot your own foot when handled >>> carelessly. ;-)
Moores and Foxes won't help you.
On Friday, May 12, 2023 at 1:02:48 PM UTC+2, none albert wrote:
[..]
In view of RISCV you have easily 8 registers to spare.Well... If you have a word using a local, and you call just any
You could easily use these registers for locals.
For re-entrant code you have to store the previous values on
the return stack.
All other solutions seem obsolete.
other word, how would you transparently make sure that after
that call your register has not been changed? The "other" word
might use locals too, and call many words that do the same.
That is not recursion, that is normal use.
The only way I can see that work is with a real Forth compiler.
Marcel Hendrix schrieb am Freitag, 12. Mai 2023 um 20:50:49 UTC+2:
On Friday, May 12, 2023 at 1:02:48 PM UTC+2, none albert wrote:
[..]
In view of RISCV you have easily 8 registers to spare.Well... If you have a word using a local, and you call just any
You could easily use these registers for locals.
For re-entrant code you have to store the previous values on
the return stack.
All other solutions seem obsolete.
other word, how would you transparently make sure that after
that call your register has not been changed? The "other" word
might use locals too, and call many words that do the same.
That is not recursion, that is normal use.
The only way I can see that work is with a real Forth compiler.
From MSVC:
"The __fastcall convention uses registers for the first four arguments,
and the stack frame to pass more arguments."
AFAIU called words that use only up to four arguments run pretty fast.
Of course if they call other functions they have to save those registers.
This scheme should also be feasible with a Forth compiler. Forth primitives >wouldn't even call other words.
In article <dc8f44f6-293f-40e9...@googlegroups.com>,
minforth <minf...@arcor.de> wrote:
Marcel Hendrix schrieb am Freitag, 12. Mai 2023 um 20:50:49 UTC+2:
On Friday, May 12, 2023 at 1:02:48 PM UTC+2, none albert wrote:
[..]
In view of RISCV you have easily 8 registers to spare.Well... If you have a word using a local, and you call just any
You could easily use these registers for locals.
For re-entrant code you have to store the previous values on
the return stack.
All other solutions seem obsolete.
other word, how would you transparently make sure that after
that call your register has not been changed? The "other" word
might use locals too, and call many words that do the same.
That is not recursion, that is normal use.
The only way I can see that work is with a real Forth compiler.
From MSVC:
"The __fastcall convention uses registers for the first four arguments, >and the stack frame to pass more arguments."
AFAIU called words that use only up to four arguments run pretty fast.
Of course if they call other functions they have to save those registers.
This scheme should also be feasible with a Forth compiler. Forth primitives >wouldn't even call other words.You can have an el-cheapo solution. Only allow locals within words,
that doesn't call words that call locals itself.
One step further, if you want to call a word with locals, save you
locals on the return stack.
It is so easy, I'm even tempted to do that.
Sysop: | Keyop |
---|---|
Location: | Huddersfield, West Yorkshire, UK |
Users: | 475 |
Nodes: | 16 (2 / 14) |
Uptime: | 18:16:40 |
Calls: | 9,487 |
Calls today: | 6 |
Files: | 13,617 |
Messages: | 6,121,091 |