trealla-prolog / trealla

A compact, efficient Prolog interpreter written in plain-old C.
MIT License
268 stars 13 forks source link

copy_term/3 leak + wasm clpz wackiness #575

Closed guregu closed 1 month ago

guregu commented 2 months ago

I noticed clpz usage in wasm trealla can sometimes OOM very easily, looks kinda like it gets stuck in a loop (tracing it vs. native 64-bit version shows that it runs for orders of magnitude more instructions before dying). While tracking that down I found a leak in copy_term/3 which may be related.

Here's a way to reproduce the leak:

% uhoh.pl
:- use_module(library(clpz)).

test :-
    foo(A),
    copy_term(A, A, Atts),
    writeln(Atts).

foo(A) :-
    X in 1 .. 3,
    Y #= 2,
    A #= X + Y.
$ valgrind --leak-check=full ./tpl -f uhoh -g 'test,halt'
==9872== Memcheck, a memory error detector
==9872== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al.
==9872== Using Valgrind-3.19.0 and LibVEX; rerun with -h for copyright info
==9872== Command: ./tpl -f uhoh -g test,halt
==9872== 
[[clpz:(_0 in 3..5),clpz:(_1989+2#=_0),clpz:(_2+2#=_0)]]
==9872== 
==9872== HEAP SUMMARY:
==9872==     in use at exit: 1,440 bytes in 2 blocks
==9872==   total heap usage: 487,102 allocs, 487,100 frees, 2,448,288,130 bytes allocated
==9872== 
==9872== 1,440 bytes in 2 blocks are definitely lost in loss record 1 of 1
==9872==    at 0x48850E8: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-arm64-linux.so)
==9872==    by 0x232AA7: copy_vars (heap.c:401)
==9872==    by 0x232FFF: deep_copy_to_tmp_with_replacement (heap.c:478)
==9872==    by 0x23322B: deep_copy_to_heap (heap.c:518)
==9872==    by 0x18FE6B: do_duplicate_term (bif_predicates.c:1974)
==9872==    by 0x18FF5B: bif_duplicate_term_2 (bif_predicates.c:1999)
==9872==    by 0x28284B: start (query.c:1632)
==9872==    by 0x283313: execute (query.c:1844)
==9872==    by 0x259E9B: run (parser.c:4034)
==9872==    by 0x27692B: pl_eval (prolog.c:144)
==9872==    by 0x1187DF: main (tpl.c:304)
==9872== 
==9872== LEAK SUMMARY:
==9872==    definitely lost: 1,440 bytes in 2 blocks
==9872==    indirectly lost: 0 bytes in 0 blocks
==9872==      possibly lost: 0 bytes in 0 blocks
==9872==    still reachable: 0 bytes in 0 blocks
==9872==         suppressed: 0 bytes in 0 blocks
==9872== 
==9872== For lists of detected and suppressed errors, rerun with: -s
==9872== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
guregu commented 2 months ago

Thanks for the fix, looks better now. I don't see any leaks now.

Here's another thing I found, maybe this is the issue I've been seeing with 32-bit wasm. I've been trying to reduce as much as possible and this is what I've gotten it down to so far.

% weird.pl

:- use_module(library(clpz)).

my_cool_predicate(
    panel(FooWidth),
    panel(BarWidth),
    panel(BazWidth, Count)
) :-
    Columns = 3,
    BarWidth = 2600,
    constraint_a(FooWidth),
    constraint_b(BazWidth, Count),
    SubSection #= BazWidth * (Columns - 1) + BarWidth * Columns,
    10000 #= FooWidth * 2 + SubSection.

constraint_a(X) :-
    X #=< 1300,
    X #>= 230.

constraint_b(X, Count) :-
    X0 #=< 1300,
    X0 #>= 700,
    X0 mod 100 #= 0,
    Count in 1 .. sup,
    X #= X0 * Count.

Some invalid writes/reads can be triggered by this query:

?- my_cool_predicate(panel(A),panel(C),panel(D, E)).

Valgrind logs:

$ valgrind --leak-check=full ./tpl weird.pl -g 'my_cool_predicate(panel(A),panel(C),panel(D, E))'
==24237== Memcheck, a memory error detector
==24237== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al.
==24237== Using Valgrind-3.19.0 and LibVEX; rerun with -h for copyright info
==24237== Command: ../tpl-linux2 weird.pl -g my_cool_predicate(panel(A),panel(C),panel(D,\ E))
==24237== 
==24237== Invalid write of size 8
==24237==    at 0x232AB0: copy_vars (heap.c:401)
==24237==    by 0x232DD7: deep_copy_to_tmp_with_replacement (heap.c:465)
==24237==    by 0x232ED7: deep_copy_to_heap (heap.c:492)
==24237==    by 0x18FE6B: do_duplicate_term (bif_predicates.c:1974)
==24237==    by 0x18FF5B: bif_duplicate_term_2 (bif_predicates.c:1999)
==24237==    by 0x283CE7: start (query.c:1626)
==24237==    by 0x28BD13: dump_vars (toplevel.c:491)
==24237==    by 0x28448B: start (query.c:1729)
==24237==    by 0x28478B: execute (query.c:1836)
==24237==    by 0x25A2A3: run (parser.c:4043)
==24237==    by 0x277D87: pl_eval (prolog.c:144)
==24237==    by 0x1187DF: main (tpl.c:304)
==24237==  Address 0x7a52ec8 is 56 bytes inside a block of size 2,400 free'd
==24237==    at 0x488A1E4: realloc (in /usr/libexec/valgrind/vgpreload_memcheck-arm64-linux.so)
==24237==    by 0x230E63: alloc_grow (heap.c:39)
==24237==    by 0x23111F: alloc_on_tmp (heap.c:105)
==24237==    by 0x23165B: deep_clone2_to_tmp (heap.c:206)
==24237==    by 0x2322AB: deep_clone2_to_tmp (heap.c:284)
==24237==    by 0x2322AB: deep_clone2_to_tmp (heap.c:284)
==24237==    by 0x2319FF: deep_clone2_to_tmp (heap.c:231)
==24237==    by 0x2322AB: deep_clone2_to_tmp (heap.c:284)
==24237==    by 0x2322AB: deep_clone2_to_tmp (heap.c:284)
==24237==    by 0x2322AB: deep_clone2_to_tmp (heap.c:284)
==24237==    by 0x2322AB: deep_clone2_to_tmp (heap.c:284)
==24237==    by 0x2319FF: deep_clone2_to_tmp (heap.c:231)
==24237==  Block was alloc'd at
==24237==    at 0x48850E8: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-arm64-linux.so)
==24237==    by 0x230F67: init_tmp_heap (heap.c:70)
==24237==    by 0x23240F: clone_to_heap (heap.c:305)
==24237==    by 0x281ABB: find_key (query.c:1132)
==24237==    by 0x282E0B: match_head (query.c:1451)
==24237==    by 0x2840F3: start (query.c:1678)
==24237==    by 0x28478B: execute (query.c:1836)
==24237==    by 0x25A2A3: run (parser.c:4043)
==24237==    by 0x277D87: pl_eval (prolog.c:144)
==24237==    by 0x1187DF: main (tpl.c:304)
==24237== 
==24237== Invalid read of size 8
==24237==    at 0x232AB8: copy_vars (heap.c:402)
==24237==    by 0x232DD7: deep_copy_to_tmp_with_replacement (heap.c:465)
==24237==    by 0x232ED7: deep_copy_to_heap (heap.c:492)
==24237==    by 0x18FE6B: do_duplicate_term (bif_predicates.c:1974)
==24237==    by 0x18FF5B: bif_duplicate_term_2 (bif_predicates.c:1999)
==24237==    by 0x283CE7: start (query.c:1626)
==24237==    by 0x28BD13: dump_vars (toplevel.c:491)
==24237==    by 0x28448B: start (query.c:1729)
==24237==    by 0x28478B: execute (query.c:1836)
==24237==    by 0x25A2A3: run (parser.c:4043)
==24237==    by 0x277D87: pl_eval (prolog.c:144)
==24237==    by 0x1187DF: main (tpl.c:304)
==24237==  Address 0x7a52ec8 is 56 bytes inside a block of size 2,400 free'd
==24237==    at 0x488A1E4: realloc (in /usr/libexec/valgrind/vgpreload_memcheck-arm64-linux.so)
==24237==    by 0x230E63: alloc_grow (heap.c:39)
==24237==    by 0x23111F: alloc_on_tmp (heap.c:105)
==24237==    by 0x23165B: deep_clone2_to_tmp (heap.c:206)
==24237==    by 0x2322AB: deep_clone2_to_tmp (heap.c:284)
==24237==    by 0x2322AB: deep_clone2_to_tmp (heap.c:284)
==24237==    by 0x2319FF: deep_clone2_to_tmp (heap.c:231)
==24237==    by 0x2322AB: deep_clone2_to_tmp (heap.c:284)
==24237==    by 0x2322AB: deep_clone2_to_tmp (heap.c:284)
==24237==    by 0x2322AB: deep_clone2_to_tmp (heap.c:284)
==24237==    by 0x2322AB: deep_clone2_to_tmp (heap.c:284)
==24237==    by 0x2319FF: deep_clone2_to_tmp (heap.c:231)
==24237==  Block was alloc'd at
==24237==    at 0x48850E8: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-arm64-linux.so)
==24237==    by 0x230F67: init_tmp_heap (heap.c:70)
==24237==    by 0x23240F: clone_to_heap (heap.c:305)
==24237==    by 0x281ABB: find_key (query.c:1132)
==24237==    by 0x282E0B: match_head (query.c:1451)
==24237==    by 0x2840F3: start (query.c:1678)
==24237==    by 0x28478B: execute (query.c:1836)
==24237==    by 0x25A2A3: run (parser.c:4043)
==24237==    by 0x277D87: pl_eval (prolog.c:144)
==24237==    by 0x1187DF: main (tpl.c:304)
==24237== 
==24237== Invalid read of size 4
==24237==    at 0x232EF4: deep_copy_to_heap (heap.c:494)
==24237==    by 0x18FE6B: do_duplicate_term (bif_predicates.c:1974)
==24237==    by 0x18FF5B: bif_duplicate_term_2 (bif_predicates.c:1999)
==24237==    by 0x283CE7: start (query.c:1626)
==24237==    by 0x28BD13: dump_vars (toplevel.c:491)
==24237==    by 0x28448B: start (query.c:1729)
==24237==    by 0x28478B: execute (query.c:1836)
==24237==    by 0x25A2A3: run (parser.c:4043)
==24237==    by 0x277D87: pl_eval (prolog.c:144)
==24237==    by 0x1187DF: main (tpl.c:304)
==24237==  Address 0x7a52e94 is 4 bytes inside a block of size 2,400 free'd
==24237==    at 0x488A1E4: realloc (in /usr/libexec/valgrind/vgpreload_memcheck-arm64-linux.so)
==24237==    by 0x230E63: alloc_grow (heap.c:39)
==24237==    by 0x23111F: alloc_on_tmp (heap.c:105)
==24237==    by 0x23165B: deep_clone2_to_tmp (heap.c:206)
==24237==    by 0x2322AB: deep_clone2_to_tmp (heap.c:284)
==24237==    by 0x2322AB: deep_clone2_to_tmp (heap.c:284)
==24237==    by 0x2319FF: deep_clone2_to_tmp (heap.c:231)
==24237==    by 0x2322AB: deep_clone2_to_tmp (heap.c:284)
==24237==    by 0x2322AB: deep_clone2_to_tmp (heap.c:284)
==24237==    by 0x2322AB: deep_clone2_to_tmp (heap.c:284)
==24237==    by 0x2322AB: deep_clone2_to_tmp (heap.c:284)
==24237==    by 0x2319FF: deep_clone2_to_tmp (heap.c:231)
==24237==  Block was alloc'd at
==24237==    at 0x48850E8: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-arm64-linux.so)
==24237==    by 0x230F67: init_tmp_heap (heap.c:70)
==24237==    by 0x23240F: clone_to_heap (heap.c:305)
==24237==    by 0x281ABB: find_key (query.c:1132)
==24237==    by 0x282E0B: match_head (query.c:1451)
==24237==    by 0x2840F3: start (query.c:1678)
==24237==    by 0x28478B: execute (query.c:1836)
==24237==    by 0x25A2A3: run (parser.c:4043)
==24237==    by 0x277D87: pl_eval (prolog.c:144)
==24237==    by 0x1187DF: main (tpl.c:304)
==24237== 
==24237== Invalid read of size 4
==24237==    at 0x232F20: deep_copy_to_heap (heap.c:496)
==24237==    by 0x18FE6B: do_duplicate_term (bif_predicates.c:1974)
==24237==    by 0x18FF5B: bif_duplicate_term_2 (bif_predicates.c:1999)
==24237==    by 0x283CE7: start (query.c:1626)
==24237==    by 0x28BD13: dump_vars (toplevel.c:491)
==24237==    by 0x28448B: start (query.c:1729)
==24237==    by 0x28478B: execute (query.c:1836)
==24237==    by 0x25A2A3: run (parser.c:4043)
==24237==    by 0x277D87: pl_eval (prolog.c:144)
==24237==    by 0x1187DF: main (tpl.c:304)
==24237==  Address 0x7a52e94 is 4 bytes inside a block of size 2,400 free'd
==24237==    at 0x488A1E4: realloc (in /usr/libexec/valgrind/vgpreload_memcheck-arm64-linux.so)
==24237==    by 0x230E63: alloc_grow (heap.c:39)
==24237==    by 0x23111F: alloc_on_tmp (heap.c:105)
==24237==    by 0x23165B: deep_clone2_to_tmp (heap.c:206)
==24237==    by 0x2322AB: deep_clone2_to_tmp (heap.c:284)
==24237==    by 0x2322AB: deep_clone2_to_tmp (heap.c:284)
==24237==    by 0x2319FF: deep_clone2_to_tmp (heap.c:231)
==24237==    by 0x2322AB: deep_clone2_to_tmp (heap.c:284)
==24237==    by 0x2322AB: deep_clone2_to_tmp (heap.c:284)
==24237==    by 0x2322AB: deep_clone2_to_tmp (heap.c:284)
==24237==    by 0x2322AB: deep_clone2_to_tmp (heap.c:284)
==24237==    by 0x2319FF: deep_clone2_to_tmp (heap.c:231)
==24237==  Block was alloc'd at
==24237==    at 0x48850E8: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-arm64-linux.so)
==24237==    by 0x230F67: init_tmp_heap (heap.c:70)
==24237==    by 0x23240F: clone_to_heap (heap.c:305)
==24237==    by 0x281ABB: find_key (query.c:1132)
==24237==    by 0x282E0B: match_head (query.c:1451)
==24237==    by 0x2840F3: start (query.c:1678)
==24237==    by 0x28478B: execute (query.c:1836)
==24237==    by 0x25A2A3: run (parser.c:4043)
==24237==    by 0x277D87: pl_eval (prolog.c:144)
==24237==    by 0x1187DF: main (tpl.c:304)
==24237== 
==24237== Invalid read of size 8
==24237==    at 0x23099C: dup_cells (internal.h:999)
==24237==    by 0x232F33: deep_copy_to_heap (heap.c:496)
==24237==    by 0x18FE6B: do_duplicate_term (bif_predicates.c:1974)
==24237==    by 0x18FF5B: bif_duplicate_term_2 (bif_predicates.c:1999)
==24237==    by 0x283CE7: start (query.c:1626)
==24237==    by 0x28BD13: dump_vars (toplevel.c:491)
==24237==    by 0x28448B: start (query.c:1729)
==24237==    by 0x28478B: execute (query.c:1836)
==24237==    by 0x25A2A3: run (parser.c:4043)
==24237==    by 0x277D87: pl_eval (prolog.c:144)
==24237==    by 0x1187DF: main (tpl.c:304)
==24237==  Address 0x7a52e90 is 0 bytes inside a block of size 2,400 free'd
==24237==    at 0x488A1E4: realloc (in /usr/libexec/valgrind/vgpreload_memcheck-arm64-linux.so)
==24237==    by 0x230E63: alloc_grow (heap.c:39)
==24237==    by 0x23111F: alloc_on_tmp (heap.c:105)
==24237==    by 0x23165B: deep_clone2_to_tmp (heap.c:206)
==24237==    by 0x2322AB: deep_clone2_to_tmp (heap.c:284)
==24237==    by 0x2322AB: deep_clone2_to_tmp (heap.c:284)
==24237==    by 0x2319FF: deep_clone2_to_tmp (heap.c:231)
==24237==    by 0x2322AB: deep_clone2_to_tmp (heap.c:284)
==24237==    by 0x2322AB: deep_clone2_to_tmp (heap.c:284)
==24237==    by 0x2322AB: deep_clone2_to_tmp (heap.c:284)
==24237==    by 0x2322AB: deep_clone2_to_tmp (heap.c:284)
==24237==    by 0x2319FF: deep_clone2_to_tmp (heap.c:231)
==24237==  Block was alloc'd at
==24237==    at 0x48850E8: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-arm64-linux.so)
==24237==    by 0x230F67: init_tmp_heap (heap.c:70)
==24237==    by 0x23240F: clone_to_heap (heap.c:305)
==24237==    by 0x281ABB: find_key (query.c:1132)
==24237==    by 0x282E0B: match_head (query.c:1451)
==24237==    by 0x2840F3: start (query.c:1678)
==24237==    by 0x28478B: execute (query.c:1836)
==24237==    by 0x25A2A3: run (parser.c:4043)
==24237==    by 0x277D87: pl_eval (prolog.c:144)
==24237==    by 0x1187DF: main (tpl.c:304)
==24237== 
==24237== Invalid read of size 8
==24237==    at 0x2309A4: dup_cells (internal.h:999)
==24237==    by 0x232F33: deep_copy_to_heap (heap.c:496)
==24237==    by 0x18FE6B: do_duplicate_term (bif_predicates.c:1974)
==24237==    by 0x18FF5B: bif_duplicate_term_2 (bif_predicates.c:1999)
==24237==    by 0x283CE7: start (query.c:1626)
==24237==    by 0x28BD13: dump_vars (toplevel.c:491)
==24237==    by 0x28448B: start (query.c:1729)
==24237==    by 0x28478B: execute (query.c:1836)
==24237==    by 0x25A2A3: run (parser.c:4043)
==24237==    by 0x277D87: pl_eval (prolog.c:144)
==24237==    by 0x1187DF: main (tpl.c:304)
==24237==  Address 0x7a52ea0 is 16 bytes inside a block of size 2,400 free'd
==24237==    at 0x488A1E4: realloc (in /usr/libexec/valgrind/vgpreload_memcheck-arm64-linux.so)
==24237==    by 0x230E63: alloc_grow (heap.c:39)
==24237==    by 0x23111F: alloc_on_tmp (heap.c:105)
==24237==    by 0x23165B: deep_clone2_to_tmp (heap.c:206)
==24237==    by 0x2322AB: deep_clone2_to_tmp (heap.c:284)
==24237==    by 0x2322AB: deep_clone2_to_tmp (heap.c:284)
==24237==    by 0x2319FF: deep_clone2_to_tmp (heap.c:231)
==24237==    by 0x2322AB: deep_clone2_to_tmp (heap.c:284)
==24237==    by 0x2322AB: deep_clone2_to_tmp (heap.c:284)
==24237==    by 0x2322AB: deep_clone2_to_tmp (heap.c:284)
==24237==    by 0x2322AB: deep_clone2_to_tmp (heap.c:284)
==24237==    by 0x2319FF: deep_clone2_to_tmp (heap.c:231)
==24237==  Block was alloc'd at
==24237==    at 0x48850E8: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-arm64-linux.so)
==24237==    by 0x230F67: init_tmp_heap (heap.c:70)
==24237==    by 0x23240F: clone_to_heap (heap.c:305)
==24237==    by 0x281ABB: find_key (query.c:1132)
==24237==    by 0x282E0B: match_head (query.c:1451)
==24237==    by 0x2840F3: start (query.c:1678)
==24237==    by 0x28478B: execute (query.c:1836)
==24237==    by 0x25A2A3: run (parser.c:4043)
==24237==    by 0x277D87: pl_eval (prolog.c:144)
==24237==    by 0x1187DF: main (tpl.c:304)
==24237== 
==24237== Invalid read of size 2
==24237==    at 0x2309B0: dup_cells (internal.h:1000)
==24237==    by 0x232F33: deep_copy_to_heap (heap.c:496)
==24237==    by 0x18FE6B: do_duplicate_term (bif_predicates.c:1974)
==24237==    by 0x18FF5B: bif_duplicate_term_2 (bif_predicates.c:1999)
==24237==    by 0x283CE7: start (query.c:1626)
==24237==    by 0x28BD13: dump_vars (toplevel.c:491)
==24237==    by 0x28448B: start (query.c:1729)
==24237==    by 0x28478B: execute (query.c:1836)
==24237==    by 0x25A2A3: run (parser.c:4043)
==24237==    by 0x277D87: pl_eval (prolog.c:144)
==24237==    by 0x1187DF: main (tpl.c:304)
==24237==  Address 0x7a52e92 is 2 bytes inside a block of size 2,400 free'd
==24237==    at 0x488A1E4: realloc (in /usr/libexec/valgrind/vgpreload_memcheck-arm64-linux.so)
==24237==    by 0x230E63: alloc_grow (heap.c:39)
==24237==    by 0x23111F: alloc_on_tmp (heap.c:105)
==24237==    by 0x23165B: deep_clone2_to_tmp (heap.c:206)
==24237==    by 0x2322AB: deep_clone2_to_tmp (heap.c:284)
==24237==    by 0x2322AB: deep_clone2_to_tmp (heap.c:284)
==24237==    by 0x2319FF: deep_clone2_to_tmp (heap.c:231)
==24237==    by 0x2322AB: deep_clone2_to_tmp (heap.c:284)
==24237==    by 0x2322AB: deep_clone2_to_tmp (heap.c:284)
==24237==    by 0x2322AB: deep_clone2_to_tmp (heap.c:284)
==24237==    by 0x2322AB: deep_clone2_to_tmp (heap.c:284)
==24237==    by 0x2319FF: deep_clone2_to_tmp (heap.c:231)
==24237==  Block was alloc'd at
==24237==    at 0x48850E8: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-arm64-linux.so)
==24237==    by 0x230F67: init_tmp_heap (heap.c:70)
==24237==    by 0x23240F: clone_to_heap (heap.c:305)
==24237==    by 0x281ABB: find_key (query.c:1132)
==24237==    by 0x282E0B: match_head (query.c:1451)
==24237==    by 0x2840F3: start (query.c:1678)
==24237==    by 0x28478B: execute (query.c:1836)
==24237==    by 0x25A2A3: run (parser.c:4043)
==24237==    by 0x277D87: pl_eval (prolog.c:144)
==24237==    by 0x1187DF: main (tpl.c:304)
==24237== 
   C = 2600, E = 1, clpz:(D*2#=_A), clpz:(_B*2#=_C), clpz:(_C+7800#=_D), clpz:(_E+7800#=_F), clpz:(2*A+_G#=10000), clpz:(2*_H+_F#=10000), clpz:(D mod 100#=0), clpz:(A in 300..400), clpz:(D in 700..800), clpz:(_F in 9200..9400), clpz:(_C in 1400..1600).
?- halt.
==24237== 
==24237== HEAP SUMMARY:
==24237==     in use at exit: 232,023 bytes in 234 blocks
==24237==   total heap usage: 490,680 allocs, 490,446 frees, 2,457,607,920 bytes allocated
==24237== 
==24237== LEAK SUMMARY:
==24237==    definitely lost: 0 bytes in 0 blocks
==24237==    indirectly lost: 0 bytes in 0 blocks
==24237==      possibly lost: 0 bytes in 0 blocks
==24237==    still reachable: 232,023 bytes in 234 blocks
==24237==         suppressed: 0 bytes in 0 blocks
==24237== Reachable blocks (those to which a pointer was found) are not shown.
==24237== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==24237== 
==24237== For lists of detected and suppressed errors, rerun with: -s
==24237== ERROR SUMMARY: 16 errors from 7 contexts (suppressed: 0 from 0)

(This log is from running a 64-bit Linux VM)

guregu commented 2 months ago

This probably doesn't help, but I did a trace of 64-bit native ARM (left, good example) vs. 32-bit wasm (right, bad example) for the first simpler program I posted. The highlighted lines are when they appear to diverge. Left side finished after 2687 lines of trace, right side OOM'd after 2599554. Interestingly the right side never calls cyclic_term at all. (I don't think it's necessarily related to cyclic_term, but it's odd that they diverged).

Screenshot 2024-08-01 at 21 35 46
guregu commented 2 months ago

Thank you, looks better now. The cause of my clpz troubles seems to be something else. I will continue to poke at it.

guregu commented 2 months ago

Hmm, I can kind of understand the symptoms I'm seeing now. Basically the 32-bit (wasm) version gets stuck in a loop of adding propagator(scalar_product_eq([1,-1] ...) to the queue, which makes me wonder if maybe the enable/disable queue stuff is acting funny. However, the 64-bit version doesn't call this at all, it uses the pplus propagator and ends quickly. I wonder if non-wasm 32-bit versions also exhibit this behavior.

?- use_module(library(clpz)).
   true.
?- A #= B + 2, B #= 1.
   throw(error(resource_error(memory),put_atts/2)).
infradig commented 2 months ago

I did 32-bit build of Trealla on Ubuntu:

~/trealla (devel) $ make OPT=-m32 NOFFI=1 NOSSL=1 ISOCLINE=1 ~/trealla (devel) $ file tpl tpl: ELF 32-bit LSB pie executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, BuildID[sha1]=35d1963133a777e99a9a2bf5f08b65360e1b625a, for GNU/Linux 3.2.0, not stripped ~/trealla (devel) $ ./tpl ?- use_module(library(clpz)). true. ?- A #= B + 2, B #= 1. A = 3, B = 1. ?- ~/trealla (devel) $

On Fri, Aug 2, 2024 at 1:30 PM guregu @.***> wrote:

Hmm, I can kind of understand the symptoms I'm seeing now. Basically the 32-bit (wasm) version gets stuck in a loop of adding propagator(scalar_product_eq([1,-1] ...) to the queue, which makes me wonder if maybe the enable/disable queue stuff is acting funny. However, the 64-bit version doesn't call this at all, it uses the pplus propagator and ends quickly. I wonder if non-wasm 32-bit versions also exhibit this behavior.

?- use_module(library(clpz)). true. ?- A #= B + 2, B #= 1. throw(error(resource_error(memory),put_atts/2)).

— Reply to this email directly, view it on GitHub https://github.com/trealla-prolog/trealla/issues/575#issuecomment-2264446820, or unsubscribe https://github.com/notifications/unsubscribe-auth/AFNKSET74MU66WXAC7QVUM3ZPL4MJAVCNFSM6AAAAABLZPCBRGVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDENRUGQ2DMOBSGA . You are receiving this because you commented.Message ID: @.***>

guregu commented 2 months ago

Nice, that is good news. Likely some wasm weirdness afoot. I'll see if I can isolate it further

guregu commented 2 months ago

I've made some discoveries!

First one is that I'm dumb and was comparing a debug and non-debug build. Second is that the issue can be reduced to A #= B which is much easier to see the issue.

The problem seems to be that the clauses in clpz_equal_/2 are in different orders. This leads the wasm build to execute the wrong instructions and get stuck. We can verify this easily with clpz:listing(clpz_equal_/2), yay.

Now the question is why they are jumbled up in the wasm build. One of the few differences is the qsort implementation, but not sure if relevant. I think we're close to the answer though.

Good (native)

?- use_module(library(clpz)).
   true.
?- clpz:listing(clpz_equal_/2).
clpz_equal_(A,B) :-
   cyclic_term(A)->domain_error(clpz_expression,A);cyclic_term(B)->domain_error(clpz_expression,B);false.
clpz_equal_(A,B) :-
   (((((nonvar(A),(A= ?C;A= #C)->must_be_fd_integer(C),D=C;v_or_i(A),D=A),(nonvar(B),(B= ?E;B= #E)->must_be_fd_integer(E),F=E;v_or_i(B),F=B)),true),!) , constrain_to_integer(D)),D=F.
clpz_equal_(A,B) :-
   (((((((((((nonvar(A),(A= ?C;A= #C)->must_be_fd_integer(C),D=C;v_or_i(A),D=A),nonvar(B)),B=E+F),(nonvar(E),(E= ?G;E= #G)->must_be_fd_integer(G),H=G;v_or_i(E),H=E)),(nonvar(F),(F= ?I;F= #I)->must_be_fd_integer(I),J=I;v_or_i(F),J=F)),true),!) , make_propagator(pplus(H,J,D,K),L)),new_queue(M)),phrase(init_propagator_([H,J,D],L),[M],[N])),variables_same_queue([H,J,D])),trigger_once_(L,N).
clpz_equal_(A,B) :-
   (((((((((((nonvar(B),(B= ?C;B= #C)->must_be_fd_integer(C),D=C;v_or_i(B),D=B),nonvar(A)),A=E+F),(nonvar(E),(E= ?G;E= #G)->must_be_fd_integer(G),H=G;v_or_i(E),H=E)),(nonvar(F),(F= ?I;F= #I)->must_be_fd_integer(I),J=I;v_or_i(F),J=F)),true),!) , make_propagator(pplus(H,J,D,K),L)),new_queue(M)),phrase(init_propagator_([H,J,D],L),[M],[N])),variables_same_queue([H,J,D])),trigger_once_(L,N).
clpz_equal_(A,B) :-
   (((((((((((nonvar(A),(A= ?C;A= #C)->must_be_fd_integer(C),D=C;v_or_i(A),D=A),nonvar(B)),B=E-F),(nonvar(E),(E= ?G;E= #G)->must_be_fd_integer(G),H=G;v_or_i(E),H=E)),(nonvar(F),(F= ?I;F= #I)->must_be_fd_integer(I),J=I;v_or_i(F),J=F)),true),!) , make_propagator(pplus(D,J,H,K),L)),new_queue(M)),phrase(init_propagator_([D,J,H],L),[M],[N])),variables_same_queue([D,J,H])),trigger_once_(L,N).
clpz_equal_(A,B) :-
   (((((((((((nonvar(B),(B= ?C;B= #C)->must_be_fd_integer(C),D=C;v_or_i(B),D=B),nonvar(A)),A=E-F),(nonvar(E),(E= ?G;E= #G)->must_be_fd_integer(G),H=G;v_or_i(E),H=E)),(nonvar(F),(F= ?I;F= #I)->must_be_fd_integer(I),J=I;v_or_i(F),J=F)),true),!) , make_propagator(pplus(D,J,H,K),L)),new_queue(M)),phrase(init_propagator_([D,J,H],L),[M],[N])),variables_same_queue([D,J,H])),trigger_once_(L,N).
clpz_equal_(A,B) :-
   (((((((((((nonvar(A),(A= ?C;A= #C)->must_be_fd_integer(C),D=C;v_or_i(A),D=A),nonvar(B)),B=E*F),(nonvar(E),(E= ?G;E= #G)->must_be_fd_integer(G),H=G;v_or_i(E),H=E)),(nonvar(F),(F= ?I;F= #I)->must_be_fd_integer(I),J=I;v_or_i(F),J=F)),true),!) , make_propagator(ptimes(H,J,D,K),L)),new_queue(M)),phrase(init_propagator_([H,J,D],L),[M],[N])),variables_same_queue([H,J,D])),trigger_once_(L,N).
clpz_equal_(A,B) :-
   (((((((((((nonvar(B),(B= ?C;B= #C)->must_be_fd_integer(C),D=C;v_or_i(B),D=B),nonvar(A)),A=E*F),(nonvar(E),(E= ?G;E= #G)->must_be_fd_integer(G),H=G;v_or_i(E),H=E)),(nonvar(F),(F= ?I;F= #I)->must_be_fd_integer(I),J=I;v_or_i(F),J=F)),true),!) , make_propagator(ptimes(H,J,D,K),L)),new_queue(M)),phrase(init_propagator_([H,J,D],L),[M],[N])),variables_same_queue([H,J,D])),trigger_once_(L,N).
clpz_equal_(A,B) :-
   ((((((((((nonvar(A),(A= ?C;A= #C)->must_be_fd_integer(C),D=C;v_or_i(A),D=A),nonvar(B)),B= -E),(nonvar(E),(E= ?F;E= #F)->must_be_fd_integer(F),G=F;v_or_i(E),G=E)),true),!) , make_propagator(pplus(D,G,0,H),I)),new_queue(J)),phrase(init_propagator_([D,G],I),[J],[K])),variables_same_queue([D,G])),trigger_once_(I,K).
clpz_equal_(A,B) :-
   ((((((((((nonvar(B),(B= ?C;B= #C)->must_be_fd_integer(C),D=C;v_or_i(B),D=B),nonvar(A)),A= -E),(nonvar(E),(E= ?F;E= #F)->must_be_fd_integer(F),G=F;v_or_i(E),G=E)),true),!) , make_propagator(pplus(D,G,0,H),I)),new_queue(J)),phrase(init_propagator_([D,G],I),[J],[K])),variables_same_queue([D,G])),trigger_once_(I,K).
clpz_equal_(A,B) :-
   (((C=A,D=B),left_right_linsum_const(C,D,E,F,G)),!) , scalar_product_(#=,E,F,G).
clpz_equal_(A,B) :-
   ((((((((((((((nonvar(A),(A= ?C;A= #C)->must_be_fd_integer(C),D=C;v_or_i(A),D=A),nonvar(B)),B=E+F),nonvar(E)),E=abs(G)),(nonvar(G),(G= ?H;G= #H)->must_be_fd_integer(H),I=H;v_or_i(G),I=G)),J=F),D==I),!) , parse_clpz(J,K)),make_propagator(x_eq_abs_plus_v(D,K),L)),new_queue(M)),phrase(init_propagator_([D,K],L),[M],[N])),variables_same_queue([D,K])),trigger_once_(L,N).
clpz_equal_(A,B) :-
   ((((((((((((((nonvar(B),(B= ?C;B= #C)->must_be_fd_integer(C),D=C;v_or_i(B),D=B),nonvar(A)),A=E+F),nonvar(E)),E=abs(G)),(nonvar(G),(G= ?H;G= #H)->must_be_fd_integer(H),I=H;v_or_i(G),I=G)),J=F),D==I),!) , parse_clpz(J,K)),make_propagator(x_eq_abs_plus_v(D,K),L)),new_queue(M)),phrase(init_propagator_([D,K],L),[M],[N])),variables_same_queue([D,K])),trigger_once_(L,N).
clpz_equal_(A,B) :-
   ((((((((((((((nonvar(A),(A= ?C;A= #C)->must_be_fd_integer(C),D=C;v_or_i(A),D=A),nonvar(B)),B=E-F),nonvar(E)),E=abs(G)),(nonvar(G),(G= ?H;G= #H)->must_be_fd_integer(H),I=H;v_or_i(G),I=G)),J=F),D==I),!) , parse_clpz(-J,K)),make_propagator(x_eq_abs_plus_v(D,K),L)),new_queue(M)),phrase(init_propagator_([D,K],L),[M],[N])),variables_same_queue([D,K])),trigger_once_(L,N).
clpz_equal_(A,B) :-
   ((((((((((((((nonvar(B),(B= ?C;B= #C)->must_be_fd_integer(C),D=C;v_or_i(B),D=B),nonvar(A)),A=E-F),nonvar(E)),E=abs(G)),(nonvar(G),(G= ?H;G= #H)->must_be_fd_integer(H),I=H;v_or_i(G),I=G)),J=F),D==I),!) , parse_clpz(-J,K)),make_propagator(x_eq_abs_plus_v(D,K),L)),new_queue(M)),phrase(init_propagator_([D,K],L),[M],[N])),variables_same_queue([D,K])),trigger_once_(L,N).
clpz_equal_(A,B) :-
   ((((nonvar(A),(A= ?C;A= #C)->must_be_fd_integer(C),D=C;v_or_i(A),D=A),E=B),true),!) , parse_clpz(E,D).
clpz_equal_(A,B) :-
   ((((nonvar(B),(B= ?C;B= #C)->must_be_fd_integer(C),D=C;v_or_i(B),D=B),E=A),true),!) , parse_clpz(E,D).
clpz_equal_(A,B) :-
   ((((C=A,D=B),true),!) , parse_clpz(C,E)),parse_clpz(D,E).

Bad (wasm)

?- use_module(library(clpz)).
   true.
?- clpz:listing(clpz_equal_/2).
clpz_equal_(A,B) :-
   (((C=A,D=B),left_right_linsum_const(C,D,E,F,G)),!) , scalar_product_(#=,E,F,G).
clpz_equal_(A,B) :-
   ((((((((((((((nonvar(A),(A= ?C;A= #C)->must_be_fd_integer(C),D=C;v_or_i(A),D=A),nonvar(B)),B=E+F),nonvar(E)),E=abs(G)),(nonvar(G),(G= ?H;G= #H)->must_be_fd_integer(H),I=H;v_or_i(G),I=G)),J=F),D==I),!) , parse_clpz(J,K)),make_propagator(x_eq_abs_plus_v(D,K),L)),new_queue(M)),phrase(init_propagator_([D,K],L),[M],[N])),variables_same_queue([D,K])),trigger_once_(L,N).
clpz_equal_(A,B) :-
   ((((((((((((((nonvar(B),(B= ?C;B= #C)->must_be_fd_integer(C),D=C;v_or_i(B),D=B),nonvar(A)),A=E+F),nonvar(E)),E=abs(G)),(nonvar(G),(G= ?H;G= #H)->must_be_fd_integer(H),I=H;v_or_i(G),I=G)),J=F),D==I),!) , parse_clpz(J,K)),make_propagator(x_eq_abs_plus_v(D,K),L)),new_queue(M)),phrase(init_propagator_([D,K],L),[M],[N])),variables_same_queue([D,K])),trigger_once_(L,N).
clpz_equal_(A,B) :-
   ((((((((((((((nonvar(A),(A= ?C;A= #C)->must_be_fd_integer(C),D=C;v_or_i(A),D=A),nonvar(B)),B=E-F),nonvar(E)),E=abs(G)),(nonvar(G),(G= ?H;G= #H)->must_be_fd_integer(H),I=H;v_or_i(G),I=G)),J=F),D==I),!) , parse_clpz(-J,K)),make_propagator(x_eq_abs_plus_v(D,K),L)),new_queue(M)),phrase(init_propagator_([D,K],L),[M],[N])),variables_same_queue([D,K])),trigger_once_(L,N).
clpz_equal_(A,B) :-
   ((((((((((((((nonvar(B),(B= ?C;B= #C)->must_be_fd_integer(C),D=C;v_or_i(B),D=B),nonvar(A)),A=E-F),nonvar(E)),E=abs(G)),(nonvar(G),(G= ?H;G= #H)->must_be_fd_integer(H),I=H;v_or_i(G),I=G)),J=F),D==I),!) , parse_clpz(-J,K)),make_propagator(x_eq_abs_plus_v(D,K),L)),new_queue(M)),phrase(init_propagator_([D,K],L),[M],[N])),variables_same_queue([D,K])),trigger_once_(L,N).
clpz_equal_(A,B) :-
   ((((nonvar(A),(A= ?C;A= #C)->must_be_fd_integer(C),D=C;v_or_i(A),D=A),E=B),true),!) , parse_clpz(E,D).
clpz_equal_(A,B) :-
   cyclic_term(A)->domain_error(clpz_expression,A);cyclic_term(B)->domain_error(clpz_expression,B);false.
clpz_equal_(A,B) :-
   ((((C=A,D=B),true),!) , parse_clpz(C,E)),parse_clpz(D,E).
clpz_equal_(A,B) :-
   ((((nonvar(B),(B= ?C;B= #C)->must_be_fd_integer(C),D=C;v_or_i(B),D=B),E=A),true),!) , parse_clpz(E,D).
clpz_equal_(A,B) :-
   ((((((((((nonvar(B),(B= ?C;B= #C)->must_be_fd_integer(C),D=C;v_or_i(B),D=B),nonvar(A)),A= -E),(nonvar(E),(E= ?F;E= #F)->must_be_fd_integer(F),G=F;v_or_i(E),G=E)),true),!) , make_propagator(pplus(D,G,0,H),I)),new_queue(J)),phrase(init_propagator_([D,G],I),[J],[K])),variables_same_queue([D,G])),trigger_once_(I,K).
clpz_equal_(A,B) :-
   (((((((((((nonvar(A),(A= ?C;A= #C)->must_be_fd_integer(C),D=C;v_or_i(A),D=A),nonvar(B)),B=E+F),(nonvar(E),(E= ?G;E= #G)->must_be_fd_integer(G),H=G;v_or_i(E),H=E)),(nonvar(F),(F= ?I;F= #I)->must_be_fd_integer(I),J=I;v_or_i(F),J=F)),true),!) , make_propagator(pplus(H,J,D,K),L)),new_queue(M)),phrase(init_propagator_([H,J,D],L),[M],[N])),variables_same_queue([H,J,D])),trigger_once_(L,N).
clpz_equal_(A,B) :-
   (((((((((((nonvar(B),(B= ?C;B= #C)->must_be_fd_integer(C),D=C;v_or_i(B),D=B),nonvar(A)),A=E+F),(nonvar(E),(E= ?G;E= #G)->must_be_fd_integer(G),H=G;v_or_i(E),H=E)),(nonvar(F),(F= ?I;F= #I)->must_be_fd_integer(I),J=I;v_or_i(F),J=F)),true),!) , make_propagator(pplus(H,J,D,K),L)),new_queue(M)),phrase(init_propagator_([H,J,D],L),[M],[N])),variables_same_queue([H,J,D])),trigger_once_(L,N).
clpz_equal_(A,B) :-
   (((((((((((nonvar(A),(A= ?C;A= #C)->must_be_fd_integer(C),D=C;v_or_i(A),D=A),nonvar(B)),B=E-F),(nonvar(E),(E= ?G;E= #G)->must_be_fd_integer(G),H=G;v_or_i(E),H=E)),(nonvar(F),(F= ?I;F= #I)->must_be_fd_integer(I),J=I;v_or_i(F),J=F)),true),!) , make_propagator(pplus(D,J,H,K),L)),new_queue(M)),phrase(init_propagator_([D,J,H],L),[M],[N])),variables_same_queue([D,J,H])),trigger_once_(L,N).
clpz_equal_(A,B) :-
   (((((((((((nonvar(B),(B= ?C;B= #C)->must_be_fd_integer(C),D=C;v_or_i(B),D=B),nonvar(A)),A=E-F),(nonvar(E),(E= ?G;E= #G)->must_be_fd_integer(G),H=G;v_or_i(E),H=E)),(nonvar(F),(F= ?I;F= #I)->must_be_fd_integer(I),J=I;v_or_i(F),J=F)),true),!) , make_propagator(pplus(D,J,H,K),L)),new_queue(M)),phrase(init_propagator_([D,J,H],L),[M],[N])),variables_same_queue([D,J,H])),trigger_once_(L,N).
clpz_equal_(A,B) :-
   (((((((((((nonvar(A),(A= ?C;A= #C)->must_be_fd_integer(C),D=C;v_or_i(A),D=A),nonvar(B)),B=E*F),(nonvar(E),(E= ?G;E= #G)->must_be_fd_integer(G),H=G;v_or_i(E),H=E)),(nonvar(F),(F= ?I;F= #I)->must_be_fd_integer(I),J=I;v_or_i(F),J=F)),true),!) , make_propagator(ptimes(H,J,D,K),L)),new_queue(M)),phrase(init_propagator_([H,J,D],L),[M],[N])),variables_same_queue([H,J,D])),trigger_once_(L,N).
clpz_equal_(A,B) :-
   (((((((((((nonvar(B),(B= ?C;B= #C)->must_be_fd_integer(C),D=C;v_or_i(B),D=B),nonvar(A)),A=E*F),(nonvar(E),(E= ?G;E= #G)->must_be_fd_integer(G),H=G;v_or_i(E),H=E)),(nonvar(F),(F= ?I;F= #I)->must_be_fd_integer(I),J=I;v_or_i(F),J=F)),true),!) , make_propagator(ptimes(H,J,D,K),L)),new_queue(M)),phrase(init_propagator_([H,J,D],L),[M],[N])),variables_same_queue([H,J,D])),trigger_once_(L,N).
clpz_equal_(A,B) :-
   ((((((((((nonvar(A),(A= ?C;A= #C)->must_be_fd_integer(C),D=C;v_or_i(A),D=A),nonvar(B)),B= -E),(nonvar(E),(E= ?F;E= #F)->must_be_fd_integer(F),G=F;v_or_i(E),G=E)),true),!) , make_propagator(pplus(D,G,0,H),I)),new_queue(J)),phrase(init_propagator_([D,G],I),[J],[K])),variables_same_queue([D,G])),trigger_once_(I,K).
clpz_equal_(A,B) :-
   (((((nonvar(A),(A= ?C;A= #C)->must_be_fd_integer(C),D=C;v_or_i(A),D=A),(nonvar(B),(B= ?E;B= #E)->must_be_fd_integer(E),F=E;v_or_i(B),F=B)),true),!) , constrain_to_integer(D)),D=F.
   true.
[0:user:8:f0:fp1:cp1:sp3:hp9:tp1] EXIT trace
[0:user:9:f0:fp1:cp1:sp3:hp9:tp1] CALL _1#=_2
[0:clpz:10:f1:fp2:cp1:sp5:hp9:tp1] EXIT _1#=_2
[0:clpz:11:f1:fp2:cp1:sp5:hp9:tp1] CALL clpz_equal(_1,_2)
[0:clpz:12:f2:fp3:cp1:sp7:hp9:tp1] EXIT clpz_equal(_1,_2)
[0:clpz:13:f2:fp3:cp1:sp7:hp9:tp1] CALL clpz_equal_(_1,_2),reinforce(_1)
[0:clpz:14:f2:fp3:cp1:sp7:hp9:tp1] CALL clpz_equal_(_1,_2)
[0:clpz:15:f3:fp4:cp2:sp197:hp9:tp1] EXIT clpz_equal_(_1,_2)
[0:clpz:16:f3:fp4:cp2:sp197:hp9:tp1] CALL (((_9=_1,_10=_2),left_right_linsum_const(_9,_10,_11,_12,_13)),!) , scalar_product_(#=,_11,_12,_13)
[0:clpz:17:f3:fp4:cp2:sp197:hp9:tp1] CALL ((_9=_1,_10=_2),left_right_linsum_const(_9,_10,_11,_12,_13)),!
[0:clpz:18:f3:fp4:cp2:sp197:hp9:tp1] CALL (_9=_1,_10=_2),left_right_linsum_const(_9,_10,_11,_12,_13)
[0:clpz:19:f3:fp4:cp2:sp197:hp9:tp1] CALL _9=_1,_10=_2
[0:clpz:20:f3:fp4:cp2:sp197:hp9:tp1] CALL _9=_1
[0:clpz:21:f3:fp4:cp2:sp197:hp9:tp2] EXIT _1=_1
[0:clpz:22:f3:fp4:cp2:sp197:hp9:tp2] CALL _10=_2
[0:clpz:23:f3:fp4:cp2:sp197:hp9:tp3] EXIT _2=_2
[0:clpz:24:f3:fp4:cp2:sp197:hp9:tp3] CALL left_right_linsum_const(_1,_2,_11,_12,_13)
[0:clpz:25:f4:fp5:cp2:sp213:hp9:tp3] EXIT left_right_linsum_const(_1,_2,_11,_12,_13)
[0:clpz:26:f4:fp5:cp2:sp213:hp9:tp3] CALL phrase(linsum(_1,0,_198),_199,_200),phrase(linsum(_2,0,_202),_203),maplist(linterm_negate,_203,_200),samsort(_199,_204),_204=[vn(_
205,_206)|_207],vns_coeffs_variables(_207,_206,_205,_208,_209),filter_linsum(_208,_209,_11,_12),_13 is _202-_198
[0:clpz:27:f4:fp5:cp2:sp213:hp9:tp3] CALL phrase(linsum(_1,0,_198),_199,_200)
[0:dcgs:28:f5:fp6:cp2:sp219:hp37:tp3] EXIT phrase(clpz:linsum(_1,0,_198),_199,_200)
[0:dcgs:29:f5:fp6:cp2:sp219:hp37:tp3] CALL strip_module(clpz:linsum(_1,0,_198),_214,_215),(var(clpz:linsum(_1,0,_198))->instantiation_error(phrase/3);nonvar(_215),dcg_const
r(_215),dcg_body(_215,_199,_200,_218)->call(_214:_218);call(_214:_215,_199,_200))
[0:dcgs:30:f5:fp6:cp2:sp219:hp37:tp3] CALL strip_module(clpz:linsum(_1,0,_198),_214,_215)
[0:dcgs:31:f5:fp6:cp2:sp219:hp37:tp5] EXIT strip_module(clpz:linsum(_1,0,_198),clpz,linsum(_1,0,_198))
[0:dcgs:32:f5:fp6:cp2:sp219:hp37:tp5] CALL var(clpz:linsum(_1,0,_198))->instantiation_error(phrase/3);nonvar(linsum(_1,0,_198)),dcg_constr(linsum(_1,0,_198)),dcg_body(linsu
m(_1,0,_198),_199,_200,_218)->call(clpz:_218);call(clpz:linsum(_1,0,_198),_199,_200)
[0:dcgs:33:f5:fp6:cp3:sp219:hp48:tp5] EXIT var(clpz:linsum(_1,0,_198))->instantiation_error(phrase/3);nonvar(linsum(_1,0,_198)),dcg_constr(linsum(_1,0,_198)),dcg_body(linsu
m(_1,0,_198),_199,_200,_218)->call(clpz:_218);call(clpz:linsum(_1,0,_198),_199,_200)
[0:dcgs:34:f5:fp6:cp3:sp219:hp48:tp5] CALL var(clpz:linsum(_1,0,_198))
( loops forever )
infradig commented 2 months ago

Interesting!

On Fri, 2 Aug 2024, 19:30 guregu, @.***> wrote:

I've made some discoveries!

First one is that I'm dumb and was comparing a debug and non-debug build. Second is that the issue can be reduced to A #= B which is much easier to see the issue.

The problem seems to be that the clauses in clpzequal/2 are in different orders. This leads the wasm build to execute the wrong instructions and get stuck. We can verify this easily with clpz:listing(clpzequal/2), yay.

Now the question is why they are jumbled up in the wasm build. One of the few differences is the qsort implementation, but not sure if relevant. I think we're close to the answer though. Good (native)

?- use_module(library(clpz)). true. ?- clpz:listing(clpzequal/2). clpzequal(A,B) :- cyclic_term(A)->domain_error(clpz_expression,A);cyclic_term(B)->domain_error(clpz_expression,B);false. clpzequal(A,B) :- (((((nonvar(A),(A= ?C;A= #C)->must_be_fd_integer(C),D=C;v_or_i(A),D=A),(nonvar(B),(B= ?E;B= #E)->must_be_fd_integer(E),F=E;v_or_i(B),F=B)),true),!) , constrain_to_integer(D)),D=F. clpzequal(A,B) :- (((((((((((nonvar(A),(A= ?C;A= #C)->must_be_fd_integer(C),D=C;v_or_i(A),D=A),nonvar(B)),B=E+F),(nonvar(E),(E= ?G;E= #G)->must_be_fd_integer(G),H=G;v_or_i(E),H=E)),(nonvar(F),(F= ?I;F= #I)->must_be_fd_integer(I),J=I;v_or_i(F),J=F)),true),!) , make_propagator(pplus(H,J,D,K),L)),new_queue(M)),phrase(initpropagator([H,J,D],L),[M],[N])),variables_same_queue([H,J,D])),triggeronce(L,N). clpzequal(A,B) :- (((((((((((nonvar(B),(B= ?C;B= #C)->must_be_fd_integer(C),D=C;v_or_i(B),D=B),nonvar(A)),A=E+F),(nonvar(E),(E= ?G;E= #G)->must_be_fd_integer(G),H=G;v_or_i(E),H=E)),(nonvar(F),(F= ?I;F= #I)->must_be_fd_integer(I),J=I;v_or_i(F),J=F)),true),!) , make_propagator(pplus(H,J,D,K),L)),new_queue(M)),phrase(initpropagator([H,J,D],L),[M],[N])),variables_same_queue([H,J,D])),triggeronce(L,N). clpzequal(A,B) :- (((((((((((nonvar(A),(A= ?C;A= #C)->must_be_fd_integer(C),D=C;v_or_i(A),D=A),nonvar(B)),B=E-F),(nonvar(E),(E= ?G;E= #G)->must_be_fd_integer(G),H=G;v_or_i(E),H=E)),(nonvar(F),(F= ?I;F= #I)->must_be_fd_integer(I),J=I;v_or_i(F),J=F)),true),!) , make_propagator(pplus(D,J,H,K),L)),new_queue(M)),phrase(initpropagator([D,J,H],L),[M],[N])),variables_same_queue([D,J,H])),triggeronce(L,N). clpzequal(A,B) :- (((((((((((nonvar(B),(B= ?C;B= #C)->must_be_fd_integer(C),D=C;v_or_i(B),D=B),nonvar(A)),A=E-F),(nonvar(E),(E= ?G;E= #G)->must_be_fd_integer(G),H=G;v_or_i(E),H=E)),(nonvar(F),(F= ?I;F= #I)->must_be_fd_integer(I),J=I;v_or_i(F),J=F)),true),!) , make_propagator(pplus(D,J,H,K),L)),new_queue(M)),phrase(initpropagator([D,J,H],L),[M],[N])),variables_same_queue([D,J,H])),triggeronce(L,N). clpzequal(A,B) :- (((((((((((nonvar(A),(A= ?C;A= #C)->must_be_fd_integer(C),D=C;v_or_i(A),D=A),nonvar(B)),B=EF),(nonvar(E),(E= ?G;E= #G)->must_be_fd_integer(G),H=G;v_or_i(E),H=E)),(nonvar(F),(F= ?I;F= #I)->must_be_fd_integer(I),J=I;v_or_i(F),J=F)),true),!) , make_propagator(ptimes(H,J,D,K),L)),new_queue(M)),phrase(initpropagator([H,J,D],L),[M],[N])),variables_same_queue([H,J,D])),triggeronce(L,N). clpzequal(A,B) :- (((((((((((nonvar(B),(B= ?C;B= #C)->must_be_fd_integer(C),D=C;v_or_i(B),D=B),nonvar(A)),A=EF),(nonvar(E),(E= ?G;E= #G)->must_be_fd_integer(G),H=G;v_or_i(E),H=E)),(nonvar(F),(F= ?I;F= #I)->must_be_fd_integer(I),J=I;v_or_i(F),J=F)),true),!) , make_propagator(ptimes(H,J,D,K),L)),new_queue(M)),phrase(initpropagator([H,J,D],L),[M],[N])),variables_same_queue([H,J,D])),triggeronce(L,N). clpzequal(A,B) :- ((((((((((nonvar(A),(A= ?C;A= #C)->must_be_fd_integer(C),D=C;v_or_i(A),D=A),nonvar(B)),B= -E),(nonvar(E),(E= ?F;E= #F)->must_be_fd_integer(F),G=F;v_or_i(E),G=E)),true),!) , make_propagator(pplus(D,G,0,H),I)),new_queue(J)),phrase(initpropagator([D,G],I),[J],[K])),variables_same_queue([D,G])),triggeronce(I,K). clpzequal(A,B) :- ((((((((((nonvar(B),(B= ?C;B= #C)->must_be_fd_integer(C),D=C;v_or_i(B),D=B),nonvar(A)),A= -E),(nonvar(E),(E= ?F;E= #F)->must_be_fd_integer(F),G=F;v_or_i(E),G=E)),true),!) , make_propagator(pplus(D,G,0,H),I)),new_queue(J)),phrase(initpropagator([D,G],I),[J],[K])),variables_same_queue([D,G])),triggeronce(I,K). clpzequal(A,B) :- (((C=A,D=B),left_right_linsum_const(C,D,E,F,G)),!) , scalarproduct(#=,E,F,G). clpzequal(A,B) :- ((((((((((((((nonvar(A),(A= ?C;A= #C)->must_be_fd_integer(C),D=C;v_or_i(A),D=A),nonvar(B)),B=E+F),nonvar(E)),E=abs(G)),(nonvar(G),(G= ?H;G= #H)->must_be_fd_integer(H),I=H;v_or_i(G),I=G)),J=F),D==I),!) , parse_clpz(J,K)),make_propagator(x_eq_abs_plus_v(D,K),L)),new_queue(M)),phrase(initpropagator([D,K],L),[M],[N])),variables_same_queue([D,K])),triggeronce(L,N). clpzequal(A,B) :- ((((((((((((((nonvar(B),(B= ?C;B= #C)->must_be_fd_integer(C),D=C;v_or_i(B),D=B),nonvar(A)),A=E+F),nonvar(E)),E=abs(G)),(nonvar(G),(G= ?H;G= #H)->must_be_fd_integer(H),I=H;v_or_i(G),I=G)),J=F),D==I),!) , parse_clpz(J,K)),make_propagator(x_eq_abs_plus_v(D,K),L)),new_queue(M)),phrase(initpropagator([D,K],L),[M],[N])),variables_same_queue([D,K])),triggeronce(L,N). clpzequal(A,B) :- ((((((((((((((nonvar(A),(A= ?C;A= #C)->must_be_fd_integer(C),D=C;v_or_i(A),D=A),nonvar(B)),B=E-F),nonvar(E)),E=abs(G)),(nonvar(G),(G= ?H;G= #H)->must_be_fd_integer(H),I=H;v_or_i(G),I=G)),J=F),D==I),!) , parse_clpz(-J,K)),make_propagator(x_eq_abs_plus_v(D,K),L)),new_queue(M)),phrase(initpropagator([D,K],L),[M],[N])),variables_same_queue([D,K])),triggeronce(L,N). clpzequal(A,B) :- ((((((((((((((nonvar(B),(B= ?C;B= #C)->must_be_fd_integer(C),D=C;v_or_i(B),D=B),nonvar(A)),A=E-F),nonvar(E)),E=abs(G)),(nonvar(G),(G= ?H;G= #H)->must_be_fd_integer(H),I=H;v_or_i(G),I=G)),J=F),D==I),!) , parse_clpz(-J,K)),make_propagator(x_eq_abs_plus_v(D,K),L)),new_queue(M)),phrase(initpropagator([D,K],L),[M],[N])),variables_same_queue([D,K])),triggeronce(L,N). clpzequal(A,B) :- ((((nonvar(A),(A= ?C;A= #C)->must_be_fd_integer(C),D=C;v_or_i(A),D=A),E=B),true),!) , parse_clpz(E,D). clpzequal(A,B) :- ((((nonvar(B),(B= ?C;B= #C)->must_be_fd_integer(C),D=C;v_or_i(B),D=B),E=A),true),!) , parse_clpz(E,D). clpzequal(A,B) :- ((((C=A,D=B),true),!) , parse_clpz(C,E)),parse_clpz(D,E).

Bad (wasm)

?- use_module(library(clpz)). true. ?- clpz:listing(clpzequal/2). clpzequal(A,B) :- (((C=A,D=B),left_right_linsum_const(C,D,E,F,G)),!) , scalarproduct(#=,E,F,G). clpzequal(A,B) :- ((((((((((((((nonvar(A),(A= ?C;A= #C)->must_be_fd_integer(C),D=C;v_or_i(A),D=A),nonvar(B)),B=E+F),nonvar(E)),E=abs(G)),(nonvar(G),(G= ?H;G= #H)->must_be_fd_integer(H),I=H;v_or_i(G),I=G)),J=F),D==I),!) , parse_clpz(J,K)),make_propagator(x_eq_abs_plus_v(D,K),L)),new_queue(M)),phrase(initpropagator([D,K],L),[M],[N])),variables_same_queue([D,K])),triggeronce(L,N). clpzequal(A,B) :- ((((((((((((((nonvar(B),(B= ?C;B= #C)->must_be_fd_integer(C),D=C;v_or_i(B),D=B),nonvar(A)),A=E+F),nonvar(E)),E=abs(G)),(nonvar(G),(G= ?H;G= #H)->must_be_fd_integer(H),I=H;v_or_i(G),I=G)),J=F),D==I),!) , parse_clpz(J,K)),make_propagator(x_eq_abs_plus_v(D,K),L)),new_queue(M)),phrase(initpropagator([D,K],L),[M],[N])),variables_same_queue([D,K])),triggeronce(L,N). clpzequal(A,B) :- ((((((((((((((nonvar(A),(A= ?C;A= #C)->must_be_fd_integer(C),D=C;v_or_i(A),D=A),nonvar(B)),B=E-F),nonvar(E)),E=abs(G)),(nonvar(G),(G= ?H;G= #H)->must_be_fd_integer(H),I=H;v_or_i(G),I=G)),J=F),D==I),!) , parse_clpz(-J,K)),make_propagator(x_eq_abs_plus_v(D,K),L)),new_queue(M)),phrase(initpropagator([D,K],L),[M],[N])),variables_same_queue([D,K])),triggeronce(L,N). clpzequal(A,B) :- ((((((((((((((nonvar(B),(B= ?C;B= #C)->must_be_fd_integer(C),D=C;v_or_i(B),D=B),nonvar(A)),A=E-F),nonvar(E)),E=abs(G)),(nonvar(G),(G= ?H;G= #H)->must_be_fd_integer(H),I=H;v_or_i(G),I=G)),J=F),D==I),!) , parse_clpz(-J,K)),make_propagator(x_eq_abs_plus_v(D,K),L)),new_queue(M)),phrase(initpropagator([D,K],L),[M],[N])),variables_same_queue([D,K])),triggeronce(L,N). clpzequal(A,B) :- ((((nonvar(A),(A= ?C;A= #C)->must_be_fd_integer(C),D=C;v_or_i(A),D=A),E=B),true),!) , parse_clpz(E,D). clpzequal(A,B) :- cyclic_term(A)->domain_error(clpz_expression,A);cyclic_term(B)->domain_error(clpz_expression,B);false. clpzequal(A,B) :- ((((C=A,D=B),true),!) , parse_clpz(C,E)),parse_clpz(D,E). clpzequal(A,B) :- ((((nonvar(B),(B= ?C;B= #C)->must_be_fd_integer(C),D=C;v_or_i(B),D=B),E=A),true),!) , parse_clpz(E,D). clpzequal(A,B) :- ((((((((((nonvar(B),(B= ?C;B= #C)->must_be_fd_integer(C),D=C;v_or_i(B),D=B),nonvar(A)),A= -E),(nonvar(E),(E= ?F;E= #F)->must_be_fd_integer(F),G=F;v_or_i(E),G=E)),true),!) , make_propagator(pplus(D,G,0,H),I)),new_queue(J)),phrase(initpropagator([D,G],I),[J],[K])),variables_same_queue([D,G])),triggeronce(I,K). clpzequal(A,B) :- (((((((((((nonvar(A),(A= ?C;A= #C)->must_be_fd_integer(C),D=C;v_or_i(A),D=A),nonvar(B)),B=E+F),(nonvar(E),(E= ?G;E= #G)->must_be_fd_integer(G),H=G;v_or_i(E),H=E)),(nonvar(F),(F= ?I;F= #I)->must_be_fd_integer(I),J=I;v_or_i(F),J=F)),true),!) , make_propagator(pplus(H,J,D,K),L)),new_queue(M)),phrase(initpropagator([H,J,D],L),[M],[N])),variables_same_queue([H,J,D])),triggeronce(L,N). clpzequal(A,B) :- (((((((((((nonvar(B),(B= ?C;B= #C)->must_be_fd_integer(C),D=C;v_or_i(B),D=B),nonvar(A)),A=E+F),(nonvar(E),(E= ?G;E= #G)->must_be_fd_integer(G),H=G;v_or_i(E),H=E)),(nonvar(F),(F= ?I;F= #I)->must_be_fd_integer(I),J=I;v_or_i(F),J=F)),true),!) , make_propagator(pplus(H,J,D,K),L)),new_queue(M)),phrase(initpropagator([H,J,D],L),[M],[N])),variables_same_queue([H,J,D])),triggeronce(L,N). clpzequal(A,B) :- (((((((((((nonvar(A),(A= ?C;A= #C)->must_be_fd_integer(C),D=C;v_or_i(A),D=A),nonvar(B)),B=E-F),(nonvar(E),(E= ?G;E= #G)->must_be_fd_integer(G),H=G;v_or_i(E),H=E)),(nonvar(F),(F= ?I;F= #I)->must_be_fd_integer(I),J=I;v_or_i(F),J=F)),true),!) , make_propagator(pplus(D,J,H,K),L)),new_queue(M)),phrase(initpropagator([D,J,H],L),[M],[N])),variables_same_queue([D,J,H])),triggeronce(L,N). clpzequal(A,B) :- (((((((((((nonvar(B),(B= ?C;B= #C)->must_be_fd_integer(C),D=C;v_or_i(B),D=B),nonvar(A)),A=E-F),(nonvar(E),(E= ?G;E= #G)->must_be_fd_integer(G),H=G;v_or_i(E),H=E)),(nonvar(F),(F= ?I;F= #I)->must_be_fd_integer(I),J=I;v_or_i(F),J=F)),true),!) , make_propagator(pplus(D,J,H,K),L)),new_queue(M)),phrase(initpropagator([D,J,H],L),[M],[N])),variables_same_queue([D,J,H])),triggeronce(L,N). clpzequal(A,B) :- (((((((((((nonvar(A),(A= ?C;A= #C)->must_be_fd_integer(C),D=C;v_or_i(A),D=A),nonvar(B)),B=EF),(nonvar(E),(E= ?G;E= #G)->must_be_fd_integer(G),H=G;v_or_i(E),H=E)),(nonvar(F),(F= ?I;F= #I)->must_be_fd_integer(I),J=I;v_or_i(F),J=F)),true),!) , make_propagator(ptimes(H,J,D,K),L)),new_queue(M)),phrase(initpropagator([H,J,D],L),[M],[N])),variables_same_queue([H,J,D])),triggeronce(L,N). clpzequal(A,B) :- (((((((((((nonvar(B),(B= ?C;B= #C)->must_be_fd_integer(C),D=C;v_or_i(B),D=B),nonvar(A)),A=EF),(nonvar(E),(E= ?G;E= #G)->must_be_fd_integer(G),H=G;v_or_i(E),H=E)),(nonvar(F),(F= ?I;F= #I)->must_be_fd_integer(I),J=I;v_or_i(F),J=F)),true),!) , make_propagator(ptimes(H,J,D,K),L)),new_queue(M)),phrase(initpropagator([H,J,D],L),[M],[N])),variables_same_queue([H,J,D])),triggeronce(L,N). clpzequal(A,B) :- ((((((((((nonvar(A),(A= ?C;A= #C)->must_be_fd_integer(C),D=C;v_or_i(A),D=A),nonvar(B)),B= -E),(nonvar(E),(E= ?F;E= #F)->must_be_fd_integer(F),G=F;v_or_i(E),G=E)),true),!) , make_propagator(pplus(D,G,0,H),I)),new_queue(J)),phrase(initpropagator([D,G],I),[J],[K])),variables_same_queue([D,G])),triggeronce(I,K). clpzequal(A,B) :- (((((nonvar(A),(A= ?C;A= #C)->must_be_fd_integer(C),D=C;v_or_i(A),D=A),(nonvar(B),(B= ?E;B= #E)->must_be_fd_integer(E),F=E;v_or_i(B),F=B)),true),!) , constrain_to_integer(D)),D=F. true.

[0:user:8:f0:fp1:cp1:sp3:hp9:tp1] EXIT trace [0:user:9:f0:fp1:cp1:sp3:hp9:tp1] CALL _1#=_2 [0:clpz:10:f1:fp2:cp1:sp5:hp9:tp1] EXIT _1#=_2 [0:clpz:11:f1:fp2:cp1:sp5:hp9:tp1] CALL clpz_equal(_1,_2) [0:clpz:12:f2:fp3:cp1:sp7:hp9:tp1] EXIT clpz_equal(_1,_2) [0:clpz:13:f2:fp3:cp1:sp7:hp9:tp1] CALL clpzequal(_1,_2),reinforce(_1) [0:clpz:14:f2:fp3:cp1:sp7:hp9:tp1] CALL clpzequal(_1,_2) [0:clpz:15:f3:fp4:cp2:sp197:hp9:tp1] EXIT clpzequal(_1,_2) [0:clpz:16:f3:fp4:cp2:sp197:hp9:tp1] CALL (((_9=_1,_10=_2),left_right_linsum_const(_9,_10,_11,_12,_13)),!) , scalarproduct(#=,_11,_12,_13) [0:clpz:17:f3:fp4:cp2:sp197:hp9:tp1] CALL ((_9=_1,_10=_2),left_right_linsum_const(_9,_10,_11,_12,_13)),! [0:clpz:18:f3:fp4:cp2:sp197:hp9:tp1] CALL (_9=_1,_10=_2),left_right_linsum_const(_9,_10,_11,_12,_13) [0:clpz:19:f3:fp4:cp2:sp197:hp9:tp1] CALL _9=_1,_10=_2 [0:clpz:20:f3:fp4:cp2:sp197:hp9:tp1] CALL _9=_1 [0:clpz:21:f3:fp4:cp2:sp197:hp9:tp2] EXIT _1=_1 [0:clpz:22:f3:fp4:cp2:sp197:hp9:tp2] CALL _10=_2 [0:clpz:23:f3:fp4:cp2:sp197:hp9:tp3] EXIT _2=_2 [0:clpz:24:f3:fp4:cp2:sp197:hp9:tp3] CALL left_right_linsum_const(_1,_2,_11,_12,_13) [0:clpz:25:f4:fp5:cp2:sp213:hp9:tp3] EXIT left_right_linsum_const(_1,_2,_11,_12,_13) [0:clpz:26:f4:fp5:cp2:sp213:hp9:tp3] CALL phrase(linsum(_1,0,_198),_199,_200),phrase(linsum(_2,0,_202),_203),maplist(linterm_negate,_203,_200),samsort(_199,_204),204=[vn( 205,_206)|_207],vns_coeffs_variables(_207,_206,_205,_208,_209),filter_linsum(_208,_209,_11,_12),_13 is _202-_198 [0:clpz:27:f4:fp5:cp2:sp213:hp9:tp3] CALL phrase(linsum(_1,0,_198),_199,_200) [0:dcgs:28:f5:fp6:cp2:sp219:hp37:tp3] EXIT phrase(clpz:linsum(_1,0,_198),_199,_200) [0:dcgs:29:f5:fp6:cp2:sp219:hp37:tp3] CALL strip_module(clpz:linsum(_1,0,_198),_214,_215),(var(clpz:linsum(_1,0,_198))->instantiation_error(phrase/3);nonvar(_215),dcg_const r(_215),dcg_body(_215,_199,_200,_218)->call(_214:_218);call(_214:_215,_199,_200)) [0:dcgs:30:f5:fp6:cp2:sp219:hp37:tp3] CALL strip_module(clpz:linsum(_1,0,_198),_214,_215) [0:dcgs:31:f5:fp6:cp2:sp219:hp37:tp5] EXIT strip_module(clpz:linsum(_1,0,_198),clpz,linsum(_1,0,_198)) [0:dcgs:32:f5:fp6:cp2:sp219:hp37:tp5] CALL var(clpz:linsum(_1,0,_198))->instantiation_error(phrase/3);nonvar(linsum(_1,0,_198)),dcg_constr(linsum(_1,0,_198)),dcg_body(linsu m(_1,0,_198),_199,_200,_218)->call(clpz:_218);call(clpz:linsum(_1,0,_198),_199,_200) [0:dcgs:33:f5:fp6:cp3:sp219:hp48:tp5] EXIT var(clpz:linsum(_1,0,_198))->instantiation_error(phrase/3);nonvar(linsum(_1,0,_198)),dcg_constr(linsum(_1,0,_198)),dcg_body(linsu m(_1,0,_198),_199,_200,_218)->call(clpz:_218);call(clpz:linsum(_1,0,_198),_199,_200) [0:dcgs:34:f5:fp6:cp3:sp219:hp48:tp5] CALL var(clpz:linsum(_1,0,_198)) ( loops forever )

— Reply to this email directly, view it on GitHub https://github.com/trealla-prolog/trealla/issues/575#issuecomment-2264964265, or unsubscribe https://github.com/notifications/unsubscribe-auth/AFNKSET6ZMBZNHCQXNYW5N3ZPNGUJAVCNFSM6AAAAABLZPCBRGVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDENRUHE3DIMRWGU . You are receiving this because you commented.Message ID: @.***>

guregu commented 2 months ago

Aha, I think it is qsort_r after all. I figured out how to induce the problem in the normal native build. All you need to do is disable the native qsort_r impls and use the fallback one (sort_r_simple).

guregu commented 2 months ago

Good news: WASI's libc includes a qsort_r implementation that works :-) I will submit a small PR that fixes it

guregu commented 2 months ago

Thanks for the help! Glad to have this one figured out

infradig commented 2 months ago

Thanks for the help! Glad to have this one figured out

Thank you. There were some good fixes in there.

infradig commented 1 month ago

This seems to have introduced a use-after-free error as evidenced here...

valgrind tpl -g halt -f tests/tests/test096.pl

I'll look into it.