dokutan / bf2lua

Brainfuck to Lua transpiler
MIT License
0 stars 0 forks source link

Large BF program fails to compile. #1

Closed rdebath closed 1 year ago

rdebath commented 1 year ago

Looks like a lua limitation.

$ lua bf2.lua -i LostKng.b
lua: /tmp/lua_dgxDKI:187929: control structure too long near 'end'
stack traceback:
        [C]: in function 'dofile'
        bf2.lua:251: in function 'main'
        bf2.lua:256: in main chunk
        [C]: in ?

LostKng.b is sometimes available on http://jonripley.com/ eg: https://web.archive.org/web/20060102171205/http://jonripley.com/i-fiction/games/LostKingdomBF.html and https://web.archive.org/web/20060102171205/http://jonripley.com/i-fiction/games/LostKingdomBF.zip

dokutan commented 1 year ago

Which version of Lua are you using? I have tested all Lua implementations i have installed with the following results:

Therefore a limitation of Lua seems like the cause. Unless you have an idea how to fix this for older versions, i would suggest using lua5.4.

Edit: I tested this using some changes i forgot to push until now, therefore the line number of the error is different.

rdebath commented 1 year ago

You're right I was using luajit, it's a bit quicker. In the unlikely event you are interested in a workaround it seems that functions are not limited in their size so you could replace the contents of large loops with a function call.

dokutan commented 1 year ago

I have added the -f / --functions option in 4de4526b5bceefa136cc447268f78b5e85bcc717, which causes functions to be created for all loops. LostKng.b seems to work on Lua < 5.4 and luajit using this option.

rdebath commented 1 year ago

Interesting that it mostly works that way round, I guess it means that Lua has some sort of optimisation that lets it drop out of the function rather than trying to jump too far to the end of the loop.

But, it doesn't work every time, nor is it consistent between different versions. It seems it works always if you put the function in the loop rather than putting the loop in a function. That way you directly avoid having an overlong while loop.

while (data[ptr] or 0) ~= 0 do
function loop1()
        ptr = ptr + 1
end
loop1()
end
luajit: /tmp/lua_HppjwK:101923: control structure too long near 'end'
stack traceback:
        [C]: in function 'dofile'
        bf2.lua:258: in function 'main'
        bf2.lua:263: in main chunk
        [C]: at 0x56642110
dokutan commented 1 year ago

Thanks, that makes a bit more sense than my original attempt. I implemented your suggestion in e8e9b681db2e4796b06cf5ce2288012cb2bb9985.

Interestingly there doesn't seem to be a meaningful limit for the length of a function, LostKng.b works even with a single function for the body of the main loop.

rdebath commented 1 year ago

Yes, it seems to be that there's a 64k limit on the distance between the start and end of a while loop because that's the limit on a virtual jump instruction. There's no such limit on the instructions used for the function call/return so the limit is immense (I think I've tried 75Mb BF programs, but it might have not worked because there's a 64 million instruction limit there somewhere too).

The reason the LostKng.b works with just one loop "fixed" is the outer loop will be the only one that is even slightly large. It's translated from a BASIC with a compiler and is constructed as a single while loop wrapping a long list of if (StmtNo == 2) { code-for-stmt-2; StmtNo = 3; } pieces. So those pieces are all relatively small.

dokutan commented 1 year ago

there's a 64k limit on the distance between the start and end of a while loop because that's the limit on a virtual jump instruction

I suspected something like this, but couldn't find any information on the details/reason. Thanks for confirming this.

I documented the -f option in the readme and merged a significantly better optimizer into the main branch (run with -O 2), which could be another strategy to prevent this issue on some programs. I would consider this issue to be resolved, given both of these options.