Log In  

(Edit: Moved to "bugs", please see @jasondelaat's reply below for a minimal example and analysis.)

Three remarks before my actual question:

  • I am using GOTO here without neccessity (and I'm not using _update or _draw); but this isn't a question about style.

  • I get a syntax error ("GOTO jumps into the scope of local '(FOR compound)'"); even though I don't use any FOR loop and I am not jumping over variables being declared.

  • I am having difficulties reproducing this with truly minimal code. Removing a trivial WHILE loop well outside the GOTO and its label removes the syntax error.

I'm writing a first game. Here's what I got:

Version 0 (see below): This version runs fine. Use the arrow keys to draw; there's a status line that moves out of you way if you come too close.

Version 1 (see below): Now, uncommenting the seemingly unrelated WHILE-loop (while 0==btn() do flip() end), and the cartridge doesn't even run ("GOTO final at line 20 (tab 1) jumps into the scope of local '(FOR compound)'"): See version 1 below.

In an attempt to understand this error I stripped the code down: Removing the calls to PRINTSTATUS and UNPRINTSTATUS does nothing. Neither does removing all contents of the PRINTSTATUS and UNPRINTSTATUS functions, but now I can remove a global assignment (sty=118) which is the first line of the program.

Version 2 (see below): And without this first line, the cartridge runs fine, again!

Neither the WHILE-loop waiting for a button press (and not using any variables), nor the first line of the program should change anything when it comes to the GOTO; so what is happening??


Attached cartridges:

✅ Version 0 is fine:

Cart #natibobozi-0 | 2023-03-19 | Code ▽ | Embed ▽ | No License
1

🛑 Version 1 has a syntax error:

Cart #natibobozi-1 | 2023-03-19 | Code ▽ | Embed ▽ | No License
1

✅ Version 2 is fine:

Cart #natibobozi-2 | 2023-03-19 | Code ▽ | Embed ▽ | No License
1

P#127311 2023-03-19 02:02 ( Edited 2023-03-20 01:28)

1

I wrote a whole giant post speculating about what's going on here but I think I figured it out so I'll spare you the giant post and just give you what I think is the important bit.

For starters, here's a minimal example which reproduces the problem:

  rectfill(0, 0, 127, 127, 0)
  rectfill(0, 0, 127, 127, 0)
  rectfill(0, 0, 127, 127, 0)
  rectfill(0, 0, 127, 127, 0)
  rectfill(0, 0, 127, 127, 0)
  rectfill(0, 0, 127, 127, 0)
  rectfill(0, 0, 127, 127, 0)
  rectfill(0, 0, 127, 127, 0)
  rectfill(0, 0, 127, 127, 0)
  rectfill(0, 0, 127, 127, 0)
  rectfill(0, 0, 127, 127, 0)
  rectfill(0, 0, 127, 127, 0)

  while true do
     x = 0
     goto final
     x += 1

     ::final::
     flip()
  end

One less rectfill and there's no error. Make x local and there's no error. Comment out flip, no error.

What I think is going on is a problem with loop unrolling.

Loop unrolling is like the interpreter copy/pasting the loop body into memory one after the other so instead of looping, it just runs a whole bunch of instructions one after the other. You can only unroll so much at a time but this usually works fine, even for infinite loops. As instructions are executed and space is freed up you just unroll more iterations of the loop.

So what's going wrong?

Well, what happens if we put a whole bunch of instructions—a bunch of rectfills for instance—before the loop? Those instructions will take up a bunch of space in memory and we'll only have room to unroll, say, two iterations of the loop and start a third. But if we don't have enough space to unroll the whole iteration, if we run out of space before the labelled section, then we've got a problem. A goto is just a jump instruction in the bytecode and jump instructions can't jump to labels they have to jump to addresses in memory. Those addresses would be resolved during unrolling not during execution. So if we can't unroll the full loop, we can't search ahead for the appropriate label/address, we have to look back. If each unrolled iteration of the loop is treated as it's own scope then when we eventually execute that instruction and jump backwards, it's an illegal jump into an unknown scope.

If I'm right, the reason removing seemingly unrelated code fixes the problem is because it's shortening the code up front enough that you end up only ever unrolling complete iterations of the loop, avoiding backwards jumps.

Honestly though, this is still just speculation. If you can, maybe change the category of this post to 'bugs' and maybe it'll catch @zep's eye and we'll get a bit more insight. Or maybe not. I suspect goto is somewhat discouraged so I'd expect a bug which affects it to be pretty low priority. But you never know.

P#127327 2023-03-19 16:13

And the behavior remains the same if any or all of your rectfills is included in the loop.

Fascinating, thank you for the analysis!
[16x15]

P#127345 2023-03-20 01:46

[Please log in to post a comment]

Follow Lexaloffle:          
Generated 2024-04-21 14:13:54 | 0.034s | Q:24