scryer-prolog
scryer-prolog copied to clipboard
between/3 leaking
ulrich@p0:/opt/gupu/scryer-prolog$ ulimit -v
50000
ulrich@p0:/opt/gupu/scryer-prolog$ target/release/scryer-prolog -f
?- use_module(library(between)).
true.
?- between(1,100000,I),I<0.
false.
?- between(1,150000,I),I<0.
Segmentation fault (core dumped)
Failure or missing Prolog garbage collection? Relying solely on Rust smart pointers? Sloppy built-in implementation?
And why a Segmentation fault error? Also there is something wrong with its speed: https://github.com/mthom/scryer-prolog/issues/1291
Rust uses guard pages on its stack, is it a stack overflow? Also this may behave differently on rebis-dev branch.
@infogulch : There should not be any overflow in this example at all. But in general, such overflows should be handled with the exception mechanism in Prolog #16
Looking at it, it seems the disjunction is not compiled properly. In any case, here is a version without disjunction replacing the existing between_/3.
between_(Lower, Upper, X) :-
Lower < Upper,
!,
between_or(Lower, Upper, X).
between_(Lower, Lower, Lower).
between_or(Lower, _, Lower).
between_or(Lower, Upper, X) :-
Lower0 is Lower + 1,
between_(Lower0, Upper, X).
So I looked at the WAM code:
?- [user].
between_or(Lower, Upper, X) :-
Lower0 is Lower + 1,
between_(Lower0, Upper, X).
?- wam_instructions(between_or/3,Ls),maplist(portray_clause,Ls).
allocate(3).
get_variable(y(2),2).
get_variable(y(1),3).
get_variable(x(3),1).
put_variable(y(3),1).
set_local_value(x(3)).
add(x(3),1,1).
call(is,2).
put_unsafe_value(3,1).
put_unsafe_value(2,2).
put_unsafe_value(1,3).
deallocate.
execute(between_,3).
At best there is one put_unsafe_value for the Lower0. But here are 3! Also, that instruction in itself should not cause any memory overflows since it should first look at the value that might be unsafe. Clearly, all three values are safe. The first two because they are nonvar, the last one might be put unsafe the first time, but then it is on the heap and from then on it is safe (even if that should not happen at all, since X comes from the head).
... and it is also independent of the (is)/2 compilation.
?- [user].
between_or(Lower, Upper, X) :-
succ(Lower, Lower0),
between_(Lower0, Upper, X).
?- wam_instructions(between_or/3,Ls),maplist(portray_clause,Ls).
allocate(3).
get_variable(y(3),2).
get_variable(y(1),3).
put_variable(y(2),2).
call(succ,2).
put_unsafe_value(2,1).
put_unsafe_value(3,2).
put_unsafe_value(1,3).
deallocate.
execute(between_,3).
Ls = [allocate(3),get_variable(y(3),2),get_variable(y(1),3),put_variable(y(2),2),call(succ,2),put_unsafe_value(2,1),put_unsafe_value(3,2),put_unsafe_value(1,3),deallocate,execute(between_,3)].
Here, the two put_unsafe_value related to argument 2 and 3 are superfluous.
It seems that there is also another somewhat related error:
$ ulimit -v 250000; target/release/scryer-prolog -f
?- use_module(library(between)).
true.
?- between(1,65000,I), I < 0.
false. % thus, it works with 65000
?- between(1,60000,I), I < 0.
false. % and of course also with less
?- repeat, between(1,60000,I), I < 0.
Segmentation fault (core dumped)
How can this happen? This uses definitely less space, than the first query.
Current reproduction:
ulrich@TU-Wien:/opt/gupu/scryer-prolog$ ulimit -v 70000
ulrich@TU-Wien:/opt/gupu/scryer-prolog$ target/release/scryer-prolog -f
?- use_module(library(between)).
true.
?- between(1,2000000,I),I<0.
memory allocation of 2097152 bytes failed
Aborted (core dumped)
I think the latest improvements to compilation successfully address this issue?