Thread: [Flashforth-devel] jumping without a return
Brought to you by:
oh2aun
From: Christopher H. <chr...@li...> - 2021-04-22 01:07:09
|
Hi, I was wondering if in FlashForth there was some trick to execute a word without throwing anything on the return stack? Kind of like you would with a tail call recursion, but in any arbitrary place? It looks like there are some jumping instructions in ff5 reference but I don't understand how to use them. I'm thinking in avr context if that makes a difference. Christopher Howard |
From: Mikael N. <mik...@fl...> - 2021-04-22 11:36:39
|
I am not sure what you are after, but the Forth way is to use EXECUTE. If you give an address to EXECUTE on the parameter stack, the code execution will continue at that address. 12 ok<$,ram> 12 2+ ok<$,ram> 14 ' 2+ ok<$,ram> 14 15b8 execute ok<$,ram> 16 Any other solution must revert to ATmega assembler to compile a jmp or ijmp. In order to not break the Forth there must by a return address on the stack. That gets you back to after the address where EXECUTE was executed. What do you want to achieve ? On 2021-04-22 04:06, Christopher Howard wrote: > Hi, I was wondering if in FlashForth there was some trick to execute a word without throwing anything on the return stack? Kind of like you would with a tail call recursion, but in any arbitrary place? It looks like there are some jumping instructions in ff5 reference but I don't understand how to use them. I'm thinking in avr context if that makes a difference. > > Christopher Howard |
From: Christopher H. <chr...@li...> - 2021-04-22 13:51:49
|
The goal is to make a call to the function /without/ growing the return stack. An example would be tail call optimization in a language like scheme. In that specific example, you would be calling the same function recursively, but the optimization would prevent you from growing the return stack. It would also be helpful for things like mutual recursion. E.g., in gforth: : foo 124 emit s" bar" evaluate ; : bar 43 emit s" foo" evaluate ; This does not work however, because the return stack quickly overflows. However, if I had another version of execute, which worked exactly like execute normally does, but does not place a return value on the stack, then it would work. : foo 124 emit s" ' bar myexecute" evaluate ; : bar 43 emit s" ' foo myexecute" evaluate ; I have not seen that forth has a built-in word for this, but thought maybe you knew how it could be done with other ff5-specific words. -- Christopher Howard blog: https://librehacker.com social: https://gnusocial.club/librehacker On Thu, 2021-04-22 at 14:36 +0300, Mikael Nordman wrote: > I am not sure what you are after, but the Forth way is to use > EXECUTE. If you give an address to EXECUTE > on the parameter stack, the code execution will continue at that > address. > > 12 ok<$,ram> 12 > 2+ ok<$,ram> 14 > ' 2+ ok<$,ram> 14 15b8 > execute ok<$,ram> 16 > > Any other solution must revert to ATmega assembler to compile a jmp > or ijmp. > > In order to not break the Forth there must by a return address on the > stack. That gets you back to after the address where EXECUTE was > executed. > > What do you want to achieve ? > > On 2021-04-22 04:06, Christopher Howard wrote: > > > Hi, I was wondering if in FlashForth there was some trick to > > execute a word without throwing anything on the return stack? Kind > > of like you would with a tail call recursion, but in any arbitrary > > place? It looks like there are some jumping instructions in ff5 > > reference but I don't understand how to use them. I'm thinking in > > avr context if that makes a difference. > > > > Christopher Howard > > _______________________________________________ > Flashforth-devel mailing list > Fla...@li... > https://lists.sourceforge.net/lists/listinfo/flashforth-devel |
From: Mikael N. <mik...@fl...> - 2021-04-22 16:20:35
|
You can use value or defer for mutual recursion. flash 0 value foo ok<$,flash> flash 0 value bar ok<$,flash> :noname ." foo " bar execute ; is foo ok<$,ram> :noname ." bar " foo execute ; is bar ok<$,ram> bar execute<enter> bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo Goes on forever. foo dis<enter> 3ab2 940e 035f call (s" 3ab6 6604 3ab8 6f6f 3aba 0320 3abc 940e 0351 call type 3ac0 dff4 rcall bar 3ac2 940c 01a0 jmp execute bar dis<enter> 3ac6 940e 035f call (s" 3aca 6204 3acc 7261 3ace 0320 3ad0 940e 0351 call type 3ad4 dfe3 rcall foo 3ad6 940c 01a0 jmp execute This also works with defer instead of value. flash defer foo ok<$,flash> flash defer bar ok<$,flash> :noname ." foo " bar ; is foo ok<$,ram> :noname ." bar " foo ; is bar ok<$,ram> bar<enter> bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo Goes on forever. Calling foo or bar in the middle of the word would overflow the return stack. Like in Scheme you can tail jump yourself using FlashForth. : foo ( n -- ) dup . 1+ dup 100 = if drop exit then foo ; It is equivalent to using begin and again. : foo ( n -- ) begin dup . 1+ dup 100 = if drop exit then again ; The stuff with evaluate will not work in FlashForth without blowing the return stack. Evaluate and interpret would make calls using the return stack. Does the mutual recursion example really work in Scheme ? BR Mikael On 2021-04-22 16:51, Christopher Howard wrote: > The goal is to make a call to the function /without/ growing the return > stack. An example would be tail call optimization in a language like > scheme. In that specific example, you would be calling the same > function recursively, but the optimization would prevent you from > growing the return stack. It would also be helpful for things like > mutual recursion. E.g., in gforth: > > : foo 124 emit s" bar" evaluate ; > : bar 43 emit s" foo" evaluate ; |
From: Christopher H. <chr...@li...> - 2021-04-22 16:36:06
|
It works fine so long as the procedure call is at the end of calling procedure. Scheme standard guarantees tail call optimization. In other cases you might get a return stack overflow. When you define the first of the two procedures, you get a warning that the other procedure might not be defined. Or at least this is how it works in Guile scheme: scheme@(guile-user)> (define (foo) (display "|") (sleep 1) (bar)) ;;; <stdin>:13:38: warning: possibly unbound variable `bar' scheme@(guile-user)> (define (bar) (display "-") (sleep 1) (foo)) scheme@(guile-user)> (foo) |-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|... -----Original Message----- From: Mikael Nordman <mik...@fl...> To: Christopher Howard <chr...@li...> Cc: fla...@li... Subject: Re: [Flashforth-devel] jumping without a return Date: Thu, 22 Apr 2021 19:20:07 +0300 Does the mutual recursion example really work in Scheme ? BR Mikael |
From: Mikael N. <mik...@fl...> - 2021-04-22 18:28:16
|
What I meant to ask was if the the cross function tail jump works when using eval("foo") at the tail ? On 2021-04-22 19:35, Christopher Howard wrote: > It works fine so long as the procedure call is at the end of calling procedure. Scheme standard guarantees tail call optimization. In other cases you might get a return stack overflow. > > When you define the first of the two procedures, you get a warning that the other procedure might not be defined. Or at least this is how it works in Guile scheme: > > scheme@(guile-user)> (define (foo) (display "|") (sleep 1) (bar)) > ;;; <stdin>:13:38: warning: possibly unbound variable `bar' > scheme@(guile-user)> (define (bar) (display "-") (sleep 1) (foo)) |
From: Christopher H. <chr...@li...> - 2021-04-22 18:57:51
|
So far the following has not crashed: scheme@(guile-user)> (define (foo) (display "|") (sleep 1) (eval-string "(bar)"))scheme@(guile-user)> (define (bar) (display "-") (sleep 1) (eval-string "(foo)"))scheme@(guile-user)> (foo)|-|-|-|-|-|-|-|-|-|-|- |-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|- |-|-|-|-|-|-|-|-|-|-|-|-|-|... -----Original Message-----From: Mikael Nordman < mik...@fl...>To: Christopher Howard < chr...@li...>Cc: fla...@li...Subject: Re: [Flashforth-devel] jumping without a returnDate: Thu, 22 Apr 2021 21:28:04 +0300 What I meant to ask was if the the cross function tail jump works when using eval("foo") at the tail ? On 2021-04-22 19:35, Christopher Howard wrote: > It works fine so long as the procedure call is at the end of calling > procedure. Scheme standard guarantees tail call optimization. In > other cases you might get a return stack overflow. > > When you define the first of the two procedures, you get a warning > that the other procedure might not be defined. Or at least this is > how it works in Guile scheme: > > scheme@(guile-user)> (define (foo) (display "|") (sleep 1) (bar)) > ;;; <stdin>:13:38: warning: possibly unbound variable `bar' > scheme@(guile-user)> (define (bar) (display "-") (sleep 1) (foo)) > |