From ecc350ca5c2eb50be8309c5f14f8bf0d87db9a02 Mon Sep 17 00:00:00 2001 From: Moon Flower Date: Mon, 12 Oct 2026 08:46:23 +0330 Subject: a --- .gitattributes | 21 + .gitignore | 11 + Makefile | 113 + README.md | 548 ++ TODO.md | 66 + docs/BORROW.md | 89 + docs/LEARN.md | 718 ++ docs/design/adt.md | 19 + docs/design/handoff.md | 20 + docs/design/memory.md | 106 + docs/design/storage-analysis.md | 33 + docs/design/struct-by-value.md | 9 + docs/design/tiering.md | 7 + docs/history/01-golf/notes.md | 10 + docs/history/02-hardware/notes.md | 46 + docs/history/04-darwin-compiler/notes.md | 9 + docs/history/05-hybrid/notes.md | 9 + docs/history/06-choosing/notes.md | 10 + docs/history/07-zygote/code/README.md | 168 + docs/history/07-zygote/code/aslib.s | 67 + docs/history/07-zygote/code/core.c | 276 + docs/history/07-zygote/code/examples/asmain.s | 9 + docs/history/07-zygote/code/examples/echo.s | 1 + docs/history/07-zygote/code/examples/fact.s | 3 + docs/history/07-zygote/code/examples/hello.s | 2 + docs/history/07-zygote/code/examples/sm.s | 3 + docs/history/07-zygote/code/gen.s | 9 + docs/history/07-zygote/code/genome.h | 39 + docs/history/07-zygote/code/io_linux.c | 6 + docs/history/07-zygote/code/mkgenome.c | 35 + docs/history/07-zygote/code/repl.s | 11 + docs/history/07-zygote/code/test.sh | 18 + docs/history/07-zygote/notes.md | 10 + docs/history/08-packaging/notes.md | 17 + docs/history/10-zc/code/README.md | 300 + docs/history/10-zc/code/core.c | 332 + docs/history/10-zc/code/examples/echo.zc | 2 + docs/history/10-zc/code/examples/fact.zc | 4 + docs/history/10-zc/code/examples/hello.zc | 1 + docs/history/10-zc/code/examples/lists.zc | 35 + docs/history/10-zc/code/examples/repl.zc | 27 + docs/history/10-zc/code/examples/selfpatch.zc | 17 + docs/history/10-zc/code/genome.h | 2349 ++++++ docs/history/10-zc/code/io_linux.c | 6 + docs/history/10-zc/code/mkgenome.c | 16 + docs/history/10-zc/code/src/compiler.zc | 1591 ++++ docs/history/10-zc/code/src/rt.zc | 168 + docs/history/10-zc/code/test.sh | 48 + docs/history/10-zc/code/tests/basics.zc | 22 + docs/history/10-zc/code/tests/errors/back_to_m.zc | 2 + .../10-zc/code/tests/errors/borrow_escape.zc | 3 + .../10-zc/code/tests/errors/branch_mismatch.zc | 3 + docs/history/10-zc/code/tests/errors/child_dies.zc | 3 + .../10-zc/code/tests/errors/child_recast.zc | 2 + docs/history/10-zc/code/tests/errors/copy_owned.zc | 2 + .../10-zc/code/tests/errors/d_into_block.zc | 2 + docs/history/10-zc/code/tests/errors/d_kept.zc | 2 + docs/history/10-zc/code/tests/errors/discard_u.zc | 3 + docs/history/10-zc/code/tests/errors/give_m.zc | 3 + .../10-zc/code/tests/errors/global_store.zc | 3 + .../10-zc/code/tests/errors/leak_overwrite.zc | 2 + docs/history/10-zc/code/tests/errors/lend_d.zc | 4 + docs/history/10-zc/code/tests/errors/loop_move.zc | 3 + .../10-zc/code/tests/errors/plain_return.zc | 3 + .../10-zc/code/tests/errors/proto_needed.zc | 3 + docs/history/10-zc/code/tests/errors/return_m.zc | 3 + .../10-zc/code/tests/errors/tag_mismatch.zc | 2 + docs/history/10-zc/code/tests/errors/u_dropped.zc | 2 + .../10-zc/code/tests/errors/u_skip_level.zc | 2 + .../10-zc/code/tests/errors/unchecked_touch.zc | 2 + .../10-zc/code/tests/errors/untagged_param.zc | 3 + docs/history/10-zc/code/tests/errors/unused.zc | 2 + .../10-zc/code/tests/errors/use_after_give.zc | 3 + docs/history/10-zc/code/tests/gc.zc | 20 + docs/history/10-zc/code/tests/meta.zc | 46 + docs/history/10-zc/code/tests/misc.zc | 30 + docs/history/10-zc/code/tests/runerr.sh | 9 + docs/history/10-zc/code/tests/umd.zc | 52 + docs/history/10-zc/code/tools/build.zc | 13 + docs/history/10-zc/code/zc0.c | 439 ++ docs/history/10-zc/notes.md | 37 + docs/history/11-types-enhancements/code/README.md | 389 + docs/history/11-types-enhancements/code/core.c | 1157 +++ .../11-types-enhancements/code/examples/echo.zc | 2 + .../11-types-enhancements/code/examples/fact.zc | 4 + .../11-types-enhancements/code/examples/hello.zc | 1 + .../11-types-enhancements/code/examples/lists.zc | 35 + .../11-types-enhancements/code/examples/repl.zc | 27 + .../code/examples/selfpatch.zc | 17 + .../11-types-enhancements/code/examples/typed.zc | 47 + .../11-types-enhancements/code/examples/wiring.zc | 19 + docs/history/11-types-enhancements/code/genome.h | 3596 +++++++++ docs/history/11-types-enhancements/code/io_linux.c | 6 + docs/history/11-types-enhancements/code/mkgenome.c | 16 + .../11-types-enhancements/code/src/compiler.zc | 2253 ++++++ docs/history/11-types-enhancements/code/src/rt.zc | 183 + docs/history/11-types-enhancements/code/test.sh | 64 + .../11-types-enhancements/code/tests/basics.zc | 22 + .../11-types-enhancements/code/tests/enhance.zc | 29 + .../code/tests/errors/back_to_m.zc | 2 + .../code/tests/errors/borrow_escape.zc | 3 + .../code/tests/errors/branch_mismatch.zc | 3 + .../code/tests/errors/child_dies.zc | 3 + .../code/tests/errors/child_recast.zc | 2 + .../code/tests/errors/copy_owned.zc | 2 + .../code/tests/errors/d_into_block.zc | 2 + .../code/tests/errors/d_kept.zc | 2 + .../code/tests/errors/discard_u.zc | 3 + .../code/tests/errors/give_m.zc | 3 + .../code/tests/errors/global_store.zc | 3 + .../code/tests/errors/leak_overwrite.zc | 2 + .../code/tests/errors/lend_d.zc | 4 + .../code/tests/errors/loop_move.zc | 3 + .../code/tests/errors/plain_return.zc | 3 + .../code/tests/errors/proto_needed.zc | 3 + .../code/tests/errors/return_m.zc | 3 + .../code/tests/errors/tag_mismatch.zc | 2 + .../code/tests/errors/u_dropped.zc | 2 + .../code/tests/errors/u_skip_level.zc | 2 + .../code/tests/errors/unchecked_touch.zc | 2 + .../code/tests/errors/unused.zc | 2 + .../code/tests/errors/use_after_give.zc | 3 + .../history/11-types-enhancements/code/tests/gc.zc | 20 + .../11-types-enhancements/code/tests/meta.zc | 46 + .../11-types-enhancements/code/tests/misc.zc | 30 + .../11-types-enhancements/code/tests/runerr.sh | 10 + .../code/tests/typeerrors/arg.zc | 5 + .../code/tests/typeerrors/arith.zc | 3 + .../code/tests/typeerrors/assign.zc | 3 + .../code/tests/typeerrors/call.zc | 3 + .../code/tests/typeerrors/deref.zc | 3 + .../code/tests/typeerrors/field.zc | 3 + .../code/tests/typeerrors/generic.zc | 4 + .../code/tests/typeerrors/notstruct.zc | 2 + .../code/tests/typeerrors/ret.zc | 4 + .../code/tests/typeerrors/voidret.zc | 4 + .../11-types-enhancements/code/tests/typeoff.zc | 2 + .../11-types-enhancements/code/tests/typeok.zc | 22 + .../11-types-enhancements/code/tests/types.zc | 57 + .../11-types-enhancements/code/tests/umd.zc | 52 + .../11-types-enhancements/code/tools/build.zc | 13 + docs/history/11-types-enhancements/code/zc0.c | 460 ++ docs/history/11-types-enhancements/notes.md | 73 + docs/history/13-gap-fixes/code/README.md | 422 + docs/history/13-gap-fixes/code/core.c | 1183 +++ docs/history/13-gap-fixes/code/examples/echo.zc | 2 + docs/history/13-gap-fixes/code/examples/fact.zc | 4 + docs/history/13-gap-fixes/code/examples/hello.zc | 1 + docs/history/13-gap-fixes/code/examples/lists.zc | 35 + docs/history/13-gap-fixes/code/examples/repl.zc | 27 + .../13-gap-fixes/code/examples/selfpatch.zc | 17 + docs/history/13-gap-fixes/code/examples/typed.zc | 47 + docs/history/13-gap-fixes/code/examples/wiring.zc | 19 + docs/history/13-gap-fixes/code/genome.h | 4443 +++++++++++ docs/history/13-gap-fixes/code/io_linux.c | 6 + docs/history/13-gap-fixes/code/mkgenome.c | 16 + docs/history/13-gap-fixes/code/src/compiler.zc | 2638 +++++++ docs/history/13-gap-fixes/code/src/rt.zc | 240 + docs/history/13-gap-fixes/code/test.sh | 71 + docs/history/13-gap-fixes/code/tests/basics.zc | 22 + docs/history/13-gap-fixes/code/tests/borrowmove.zc | 2 + docs/history/13-gap-fixes/code/tests/buddy.zc | 15 + docs/history/13-gap-fixes/code/tests/enhance.zc | 29 + .../13-gap-fixes/code/tests/errors/back_to_m.zc | 2 + .../code/tests/errors/borrow_escape.zc | 3 + .../code/tests/errors/branch_mismatch.zc | 3 + .../13-gap-fixes/code/tests/errors/child_dies.zc | 3 + .../13-gap-fixes/code/tests/errors/child_recast.zc | 2 + .../13-gap-fixes/code/tests/errors/copy_owned.zc | 2 + .../13-gap-fixes/code/tests/errors/d_into_block.zc | 2 + .../13-gap-fixes/code/tests/errors/d_kept.zc | 2 + .../13-gap-fixes/code/tests/errors/discard_u.zc | 3 + .../13-gap-fixes/code/tests/errors/give_m.zc | 3 + .../13-gap-fixes/code/tests/errors/global_store.zc | 3 + .../code/tests/errors/leak_overwrite.zc | 2 + .../13-gap-fixes/code/tests/errors/lend_d.zc | 4 + .../13-gap-fixes/code/tests/errors/loop_move.zc | 3 + .../13-gap-fixes/code/tests/errors/plain_return.zc | 3 + .../13-gap-fixes/code/tests/errors/return_m.zc | 3 + .../13-gap-fixes/code/tests/errors/tag_mismatch.zc | 2 + .../13-gap-fixes/code/tests/errors/u_dropped.zc | 2 + .../13-gap-fixes/code/tests/errors/u_skip_level.zc | 2 + .../code/tests/errors/unchecked_touch.zc | 2 + .../13-gap-fixes/code/tests/errors/unused.zc | 2 + .../code/tests/errors/use_after_give.zc | 3 + docs/history/13-gap-fixes/code/tests/gc.zc | 20 + docs/history/13-gap-fixes/code/tests/joins.zc | 32 + docs/history/13-gap-fixes/code/tests/manyerrors.zc | 6 + docs/history/13-gap-fixes/code/tests/meta.zc | 46 + docs/history/13-gap-fixes/code/tests/misc.zc | 30 + .../13-gap-fixes/code/tests/overflow_data.zc | 1 + .../13-gap-fixes/code/tests/overflow_frame.zc | 2 + .../13-gap-fixes/code/tests/overflow_return.zc | 2 + docs/history/13-gap-fixes/code/tests/runerr.sh | 10 + .../13-gap-fixes/code/tests/typeerrors/arg.zc | 5 + .../13-gap-fixes/code/tests/typeerrors/arith.zc | 3 + .../13-gap-fixes/code/tests/typeerrors/assign.zc | 3 + .../13-gap-fixes/code/tests/typeerrors/call.zc | 3 + .../13-gap-fixes/code/tests/typeerrors/deref.zc | 3 + .../13-gap-fixes/code/tests/typeerrors/field.zc | 3 + .../13-gap-fixes/code/tests/typeerrors/generic.zc | 4 + .../code/tests/typeerrors/notstruct.zc | 2 + .../13-gap-fixes/code/tests/typeerrors/ret.zc | 4 + .../13-gap-fixes/code/tests/typeerrors/voidret.zc | 4 + docs/history/13-gap-fixes/code/tests/typeoff.zc | 2 + docs/history/13-gap-fixes/code/tests/typeok.zc | 22 + docs/history/13-gap-fixes/code/tests/types.zc | 57 + docs/history/13-gap-fixes/code/tests/umd.zc | 52 + docs/history/13-gap-fixes/code/tools/build.zc | 13 + docs/history/13-gap-fixes/code/zc0.c | 460 ++ docs/history/13-gap-fixes/notes.md | 48 + docs/history/15-o3/code/README.md | 511 ++ docs/history/15-o3/code/bench.sh | 26 + docs/history/15-o3/code/core.c | 2019 +++++ docs/history/15-o3/code/examples/bf.zc | 50 + docs/history/15-o3/code/examples/dump.zc | 32 + docs/history/15-o3/code/examples/echo.zc | 2 + docs/history/15-o3/code/examples/fact.zc | 4 + docs/history/15-o3/code/examples/hello.zc | 1 + docs/history/15-o3/code/examples/lang.zc | 53 + docs/history/15-o3/code/examples/life.zc | 38 + docs/history/15-o3/code/examples/lists.zc | 34 + docs/history/15-o3/code/examples/mandel.zc | 36 + docs/history/15-o3/code/examples/primes.zc | 38 + docs/history/15-o3/code/examples/quine.zc | 1 + docs/history/15-o3/code/examples/repl.zc | 27 + docs/history/15-o3/code/examples/selfpatch.zc | 17 + docs/history/15-o3/code/examples/staged.zc | 35 + docs/history/15-o3/code/examples/tree.zc | 28 + docs/history/15-o3/code/examples/typed.zc | 44 + docs/history/15-o3/code/examples/wiring.zc | 19 + docs/history/15-o3/code/genome.h | 4893 ++++++++++++ docs/history/15-o3/code/io_linux.c | 6 + docs/history/15-o3/code/mkgenome.c | 16 + docs/history/15-o3/code/src/compiler.zc | 2832 +++++++ docs/history/15-o3/code/src/rt.zc | 290 + docs/history/15-o3/code/test.sh | 88 + docs/history/15-o3/code/tests/arrays.zc | 24 + docs/history/15-o3/code/tests/basics.zc | 22 + docs/history/15-o3/code/tests/bench.zc | 30 + docs/history/15-o3/code/tests/borrowmove.zc | 2 + docs/history/15-o3/code/tests/buddy.zc | 15 + docs/history/15-o3/code/tests/enhance.zc | 29 + docs/history/15-o3/code/tests/errors/back_to_m.zc | 2 + .../15-o3/code/tests/errors/borrow_escape.zc | 3 + .../15-o3/code/tests/errors/branch_mismatch.zc | 3 + docs/history/15-o3/code/tests/errors/child_dies.zc | 3 + .../15-o3/code/tests/errors/child_recast.zc | 2 + docs/history/15-o3/code/tests/errors/copy_owned.zc | 2 + .../15-o3/code/tests/errors/d_into_block.zc | 2 + docs/history/15-o3/code/tests/errors/d_kept.zc | 2 + docs/history/15-o3/code/tests/errors/discard_u.zc | 3 + docs/history/15-o3/code/tests/errors/give_m.zc | 3 + .../15-o3/code/tests/errors/global_store.zc | 3 + .../15-o3/code/tests/errors/leak_overwrite.zc | 2 + docs/history/15-o3/code/tests/errors/lend_d.zc | 4 + docs/history/15-o3/code/tests/errors/loop_move.zc | 3 + .../15-o3/code/tests/errors/plain_return.zc | 3 + docs/history/15-o3/code/tests/errors/return_m.zc | 3 + .../15-o3/code/tests/errors/tag_mismatch.zc | 2 + docs/history/15-o3/code/tests/errors/u_dropped.zc | 2 + .../15-o3/code/tests/errors/u_skip_level.zc | 2 + .../15-o3/code/tests/errors/unchecked_touch.zc | 2 + docs/history/15-o3/code/tests/errors/unused.zc | 2 + .../15-o3/code/tests/errors/use_after_give.zc | 3 + docs/history/15-o3/code/tests/gc.zc | 20 + docs/history/15-o3/code/tests/joins.zc | 32 + .../15-o3/code/tests/langerrors/const_index.zc | 2 + .../15-o3/code/tests/langerrors/fixed_nolen.zc | 2 + .../15-o3/code/tests/langerrors/fixed_push.zc | 2 + .../15-o3/code/tests/langerrors/fixed_to_dyn.zc | 4 + .../history/15-o3/code/tests/langerrors/o3_addr.zc | 2 + docs/history/15-o3/code/tests/langerrors/o3_asm.zc | 2 + docs/history/15-o3/code/tests/langerrors/o3_let.zc | 2 + .../15-o3/code/tests/langerrors/o3_param.zc | 3 + docs/history/15-o3/code/tests/manyerrors.zc | 6 + docs/history/15-o3/code/tests/meta.zc | 46 + docs/history/15-o3/code/tests/misc.zc | 30 + docs/history/15-o3/code/tests/o3.zc | 83 + docs/history/15-o3/code/tests/o3_bounds.zc | 2 + docs/history/15-o3/code/tests/overflow_data.zc | 1 + docs/history/15-o3/code/tests/overflow_frame.zc | 2 + docs/history/15-o3/code/tests/overflow_return.zc | 2 + docs/history/15-o3/code/tests/runerr.sh | 10 + docs/history/15-o3/code/tests/typeerrors/arg.zc | 5 + docs/history/15-o3/code/tests/typeerrors/arith.zc | 3 + docs/history/15-o3/code/tests/typeerrors/assign.zc | 3 + docs/history/15-o3/code/tests/typeerrors/call.zc | 3 + docs/history/15-o3/code/tests/typeerrors/deref.zc | 3 + docs/history/15-o3/code/tests/typeerrors/field.zc | 3 + .../history/15-o3/code/tests/typeerrors/generic.zc | 4 + .../15-o3/code/tests/typeerrors/notstruct.zc | 2 + docs/history/15-o3/code/tests/typeerrors/ret.zc | 4 + .../history/15-o3/code/tests/typeerrors/voidret.zc | 4 + docs/history/15-o3/code/tests/typeoff.zc | 2 + docs/history/15-o3/code/tests/typeok.zc | 21 + docs/history/15-o3/code/tests/types.zc | 56 + docs/history/15-o3/code/tests/umd.zc | 51 + docs/history/15-o3/code/tools/build.zc | 13 + docs/history/15-o3/code/zc0.c | 461 ++ docs/history/15-o3/notes.md | 49 + docs/history/17-o3-boundaries/code/README.md | 528 ++ docs/history/17-o3-boundaries/code/bench.sh | 36 + docs/history/17-o3-boundaries/code/core.c | 2030 +++++ docs/history/17-o3-boundaries/code/examples/bf.zc | 50 + .../history/17-o3-boundaries/code/examples/dump.zc | 32 + .../history/17-o3-boundaries/code/examples/echo.zc | 2 + .../history/17-o3-boundaries/code/examples/fact.zc | 4 + .../17-o3-boundaries/code/examples/hello.zc | 1 + .../history/17-o3-boundaries/code/examples/lang.zc | 53 + .../history/17-o3-boundaries/code/examples/life.zc | 38 + .../17-o3-boundaries/code/examples/lists.zc | 34 + .../17-o3-boundaries/code/examples/mandel.zc | 36 + .../17-o3-boundaries/code/examples/primes.zc | 38 + .../17-o3-boundaries/code/examples/quine.zc | 1 + .../history/17-o3-boundaries/code/examples/repl.zc | 27 + .../17-o3-boundaries/code/examples/selfpatch.zc | 17 + .../17-o3-boundaries/code/examples/staged.zc | 35 + .../history/17-o3-boundaries/code/examples/tree.zc | 28 + .../17-o3-boundaries/code/examples/typed.zc | 44 + .../17-o3-boundaries/code/examples/wiring.zc | 19 + docs/history/17-o3-boundaries/code/genome.h | 5262 +++++++++++++ docs/history/17-o3-boundaries/code/io_linux.c | 6 + docs/history/17-o3-boundaries/code/mkgenome.c | 16 + docs/history/17-o3-boundaries/code/src/compiler.zc | 2980 ++++++++ docs/history/17-o3-boundaries/code/src/rt.zc | 291 + docs/history/17-o3-boundaries/code/test.sh | 90 + docs/history/17-o3-boundaries/code/tests/arrays.zc | 24 + docs/history/17-o3-boundaries/code/tests/basics.zc | 22 + docs/history/17-o3-boundaries/code/tests/bench.zc | 30 + .../17-o3-boundaries/code/tests/borrowmove.zc | 2 + docs/history/17-o3-boundaries/code/tests/buddy.zc | 15 + .../history/17-o3-boundaries/code/tests/enhance.zc | 29 + .../code/tests/errors/back_to_m.zc | 2 + .../code/tests/errors/borrow_escape.zc | 3 + .../code/tests/errors/branch_mismatch.zc | 3 + .../code/tests/errors/child_dies.zc | 3 + .../code/tests/errors/child_recast.zc | 2 + .../code/tests/errors/copy_owned.zc | 2 + .../code/tests/errors/d_into_block.zc | 2 + .../17-o3-boundaries/code/tests/errors/d_kept.zc | 2 + .../code/tests/errors/discard_u.zc | 3 + .../17-o3-boundaries/code/tests/errors/give_m.zc | 3 + .../code/tests/errors/global_store.zc | 3 + .../code/tests/errors/leak_overwrite.zc | 2 + .../17-o3-boundaries/code/tests/errors/lend_d.zc | 4 + .../code/tests/errors/loop_move.zc | 3 + .../code/tests/errors/plain_return.zc | 3 + .../17-o3-boundaries/code/tests/errors/return_m.zc | 3 + .../code/tests/errors/tag_mismatch.zc | 2 + .../code/tests/errors/u_dropped.zc | 2 + .../code/tests/errors/u_skip_level.zc | 2 + .../code/tests/errors/unchecked_touch.zc | 2 + .../17-o3-boundaries/code/tests/errors/unused.zc | 2 + .../code/tests/errors/use_after_give.zc | 3 + docs/history/17-o3-boundaries/code/tests/gc.zc | 20 + docs/history/17-o3-boundaries/code/tests/joins.zc | 32 + .../code/tests/langerrors/const_index.zc | 2 + .../code/tests/langerrors/fixed_nolen.zc | 2 + .../code/tests/langerrors/fixed_push.zc | 2 + .../code/tests/langerrors/fixed_to_dyn.zc | 4 + .../code/tests/langerrors/gen_undeclared.zc | 3 + .../code/tests/langerrors/o3_addr.zc | 2 + .../code/tests/langerrors/o3_asm.zc | 2 + .../code/tests/langerrors/o3_let.zc | 2 + .../code/tests/langerrors/o3_param.zc | 3 + .../17-o3-boundaries/code/tests/manyerrors.zc | 6 + docs/history/17-o3-boundaries/code/tests/meta.zc | 46 + docs/history/17-o3-boundaries/code/tests/misc.zc | 30 + docs/history/17-o3-boundaries/code/tests/o3.zc | 83 + .../17-o3-boundaries/code/tests/o3_bounds.zc | 2 + docs/history/17-o3-boundaries/code/tests/o3fat.zc | 22 + .../17-o3-boundaries/code/tests/o3scopes.zc | 19 + .../17-o3-boundaries/code/tests/overflow_data.zc | 1 + .../17-o3-boundaries/code/tests/overflow_frame.zc | 2 + .../17-o3-boundaries/code/tests/overflow_return.zc | 2 + docs/history/17-o3-boundaries/code/tests/runerr.sh | 10 + .../17-o3-boundaries/code/tests/typeerrors/arg.zc | 5 + .../code/tests/typeerrors/arith.zc | 3 + .../code/tests/typeerrors/assign.zc | 3 + .../17-o3-boundaries/code/tests/typeerrors/call.zc | 3 + .../code/tests/typeerrors/deref.zc | 3 + .../code/tests/typeerrors/field.zc | 3 + .../code/tests/typeerrors/generic.zc | 4 + .../code/tests/typeerrors/notstruct.zc | 2 + .../17-o3-boundaries/code/tests/typeerrors/ret.zc | 4 + .../code/tests/typeerrors/voidret.zc | 4 + .../history/17-o3-boundaries/code/tests/typeoff.zc | 2 + docs/history/17-o3-boundaries/code/tests/typeok.zc | 21 + docs/history/17-o3-boundaries/code/tests/types.zc | 56 + docs/history/17-o3-boundaries/code/tests/umd.zc | 51 + docs/history/17-o3-boundaries/code/tools/build.zc | 13 + docs/history/17-o3-boundaries/code/zc0.c | 461 ++ docs/history/17-o3-boundaries/notes.md | 48 + docs/history/18-aliases/notes.md | 13 + docs/history/19-tools/notes.md | 25 + docs/history/20-words/notes.md | 22 + docs/history/21-micro/notes.md | 23 + docs/history/22-size/notes.md | 82 + docs/history/23-baremetal/notes.md | 85 + docs/history/24-backends/notes.md | 49 + docs/history/25-genmap/notes.md | 28 + docs/history/26-bareapi/notes.md | 25 + docs/history/27-float/notes.md | 19 + docs/history/28-bytes/notes.md | 21 + docs/history/29-strings/notes.md | 21 + docs/history/30-sharing/notes.md | 17 + docs/history/31-fpu/notes.md | 23 + docs/history/32-ftype/notes.md | 17 + docs/history/33-fo3/notes.md | 24 + docs/history/34-ftext/notes.md | 11 + docs/history/35-bench/notes.md | 82 + docs/history/36-alloc/notes.md | 47 + docs/history/37-call/notes.md | 43 + docs/history/38-bounds/notes.md | 43 + docs/history/39-blocks/notes.md | 43 + docs/history/40-fatten/notes.md | 41 + docs/history/41-obytes/notes.md | 39 + docs/history/42-roots/notes.md | 41 + docs/history/43-stdlib/notes.md | 47 + docs/history/44-check-mode/notes.md | 33 + docs/history/45-fast-alloc/notes.md | 13 + docs/history/46-c-abi/notes.md | 17 + docs/history/47-limits/notes.md | 15 + docs/history/48-landing/notes.md | 7 + docs/history/49-generics/notes.md | 5 + docs/history/50-adt/notes.md | 9 + docs/history/51-llvm-inline-stdlib/notes.md | 19 + docs/history/52-closures/notes.md | 14 + docs/history/53-typed-source/notes.md | 16 + docs/history/README.md | 50 + docs/history/baremetal-measurements.md | 55 + docs/history/maintenance-notes.md | 30 + docs/history/transcript.md | 257 + editors/vscode/.vscodeignore | 1 + editors/vscode/README.md | 30 + editors/vscode/client/diagcache.js | 137 + editors/vscode/client/extension.js | 537 ++ editors/vscode/client/hover.js | 21 + editors/vscode/client/vmproc.js | 81 + editors/vscode/client/wasmproc.js | 96 + editors/vscode/language-configuration.json | 30 + editors/vscode/package.json | 127 + editors/vscode/syntaxes/zc.tmLanguage.json | 175 + examples/bf.zc | 48 + examples/cli.zc | 12 + examples/cli2.zc | 29 + examples/dump.zc | 30 + examples/echo.zc | 2 + examples/fact.zc | 4 + examples/hello.zc | 1 + examples/lang.zc | 49 + examples/learn/01-hello.out | 1 + examples/learn/01-hello.zc | 4 + examples/learn/02-loops.out | 1 + examples/learn/02-loops.zc | 6 + examples/learn/03-do-continue.out | 1 + examples/learn/03-do-continue.zc | 6 + examples/learn/04-enums.out | 1 + examples/learn/04-enums.zc | 5 + examples/learn/05-small-ints.out | 3 + examples/learn/05-small-ints.zc | 9 + examples/learn/06-floats.out | 1 + examples/learn/06-floats.zc | 6 + examples/learn/07-bytes.out | 1 + examples/learn/07-bytes.zc | 7 + examples/learn/08-byte-strings.out | 2 + examples/learn/08-byte-strings.zc | 8 + examples/learn/09-shared.out | 1 + examples/learn/09-shared.zc | 8 + examples/learn/10-owned.out | 1 + examples/learn/10-owned.zc | 6 + examples/learn/11-tags.out | 1 + examples/learn/11-tags.zc | 6 + examples/learn/12-aliases.out | 1 + examples/learn/12-aliases.zc | 8 + examples/learn/13-memdebug.out | 2 + examples/learn/13-memdebug.zc | 10 + examples/learn/14-include.out | 1 + examples/learn/14-include.zc | 7 + examples/learn/15-generics.out | 1 + examples/learn/15-generics.zc | 9 + examples/learn/16-tagged-enums.out | 2 + examples/learn/16-tagged-enums.zc | 18 + examples/learn/17-switch.out | 1 + examples/learn/17-switch.zc | 6 + examples/learn/18-o3.out | 2 + examples/learn/18-o3.zc | 10 + examples/learn/19-growable.out | 3 + examples/learn/19-growable.zc | 13 + examples/learn/20-text-num.out | 2 + examples/learn/20-text-num.zc | 6 + examples/learn/21-host.out | 1 + examples/learn/21-host.zc | 5 + examples/learn/22-raw-x86.out | 1 + examples/learn/22-raw-x86.zc | 5 + examples/learn/23-c-abi.out | 2 + examples/learn/23-c-abi.zc | 12 + examples/learn/24-backtick.out | 1 + examples/learn/24-backtick.zc | 5 + examples/learn/25-closures.out | 2 + examples/learn/25-closures.zc | 8 + examples/learn/26-closure-state.out | 3 + examples/learn/26-closure-state.zc | 13 + examples/life.zc | 35 + examples/lists.zc | 32 + examples/mandel.zc | 32 + examples/opt_test.zc | 54 + examples/primes.zc | 35 + examples/quine.zc | 1 + examples/repl.zc | 25 + examples/selfpatch.zc | 16 + examples/staged.zc | 33 + examples/strings.zc | 8 + examples/struct_closure_opt.zc | 47 + examples/tree.zc | 26 + examples/typed.zc | 51 + examples/wiring.zc | 18 + src/baremetal/link_aarch64.ld | 37 + src/baremetal/link_riscv.ld | 39 + src/baremetal/port.c | 339 + src/baremetal/run.sh | 104 + src/baremetal/start_aarch64.S | 38 + src/baremetal/start_riscv.S | 20 + src/compiler/compiler.zc | 8027 ++++++++++++++++++++ src/core/bops.h | 87 + src/core/core.c | 5889 ++++++++++++++ src/core/fops.h | 1048 +++ src/core/io_linux.c | 69 + src/core/io_win.c | 25 + src/core/memmap.h | 53 + src/core/stack.h | 112 + src/js/extract_genome.js | 45 + src/js/extract_lsp.js | 21 + src/js/mapvalues.js | 22 + src/js/tinyvm.ts | 946 +++ src/lib/std.zc | 455 ++ src/lsp/server.zc | 1929 +++++ src/micro/micro.c | 643 ++ src/micro/micro_port.h | 89 + src/micro/port_linux.c | 284 + src/micro/port_stub.c | 59 + src/micro/port_wasm_stream.c | 121 + src/micro/port_win.c | 268 + src/runtime/rt.zc | 1326 ++++ src/runtime/run.zc | 27 + src/tools/automap.sh | 58 + src/tools/build.zc | 14 + src/tools/genmap.c | 858 +++ src/tools/mkgenome.c | 31 + src/tools/mklsp.sh | 14 + src/tools/mklspwasm.sh | 20 + src/tools/mkvsix.sh | 35 + src/tools/zc0.c | 880 +++ src/tools/zcc | 54 + src/web/build.sh | 12 + src/web/index.html | 60 + src/web/port_wasm.c | 86 + src/web/zc.js | 28 + tests/bench/bench.sh | 36 + tests/bench/bench_bytes.sh | 33 + tests/bench/bench_float.sh | 54 + tests/bench/bench_share.sh | 32 + tests/bench/bench_strings.sh | 32 + tests/bench/run.py | 304 + tests/bench/run.sh | 5 + tests/bench/selfmod.zc | 19 + tests/bench/sieve.c | 58 + tests/bench/sieve.zc | 51 + tests/bench/trees.c | 85 + tests/bench/trees.zc | 92 + tests/data/hi.txt | 1 + tests/data/sbv.c | 18 + tests/drivers/code_address_growth_test.py | 23 + tests/drivers/code_emission_growth_test.py | 40 + tests/drivers/compiler_growth_test.py | 82 + tests/drivers/core_lsp_test.js | 43 + tests/drivers/diagcache_test.js | 140 + tests/drivers/diagnostic_revision_test.js | 47 + tests/drivers/enhancement_backends_test.py | 45 + tests/drivers/ext_cache_test.js | 154 + tests/drivers/float_backends_test.py | 47 + tests/drivers/frame_growth_test.py | 32 + tests/drivers/genome_blob_test.py | 32 + tests/drivers/hover_features_test.js | 85 + tests/drivers/hover_test.js | 23 + tests/drivers/image_growth_test.py | 25 + tests/drivers/image_run.js | 14 + tests/drivers/inference_growth_test.py | 34 + tests/drivers/layout_config_test.py | 26 + tests/drivers/learn.py | 64 + tests/drivers/local_index_test.py | 23 + tests/drivers/lsp_growth_test.js | 53 + tests/drivers/lsp_live.js | 23 + tests/drivers/lsp_sandbox.js | 22 + tests/drivers/lsp_stream.js | 11 + tests/drivers/lsp_test.js | 246 + tests/drivers/lsp_vm.js | 112 + tests/drivers/lsp_wasm.js | 112 + tests/drivers/memory_limits_test.py | 23 + tests/drivers/parser_growth_test.py | 48 + tests/drivers/protocol_growth_test.js | 34 + tests/drivers/seed_growth_test.py | 25 + tests/drivers/stack_growth_test.py | 58 + tests/drivers/storage_analysis_test.js | 71 + tests/drivers/vscode_hover_test.js | 48 + tests/drivers/wasm_test.js | 28 + tests/drivers/word_literals_test.py | 19 + tests/errors/alias_field.zc | 9 + tests/errors/alias_index.zc | 9 + tests/errors/alias_plus.zc | 2 + tests/errors/alias_scope.zc | 6 + tests/errors/back_to_m.zc | 2 + tests/errors/borrow_escape.zc | 3 + tests/errors/branch_mismatch.zc | 3 + tests/errors/child_dies.zc | 3 + tests/errors/child_recast.zc | 2 + tests/errors/copy_owned.zc | 2 + tests/errors/d_into_block.zc | 2 + tests/errors/d_kept.zc | 2 + tests/errors/did_you_mean.zc | 3 + tests/errors/discard_u.zc | 3 + tests/errors/found_token.zc | 2 + tests/errors/give_m.zc | 3 + tests/errors/global_store.zc | 3 + tests/errors/leak_overwrite.zc | 2 + tests/errors/lend_d.zc | 4 + tests/errors/loop_move.zc | 3 + tests/errors/plain_return.zc | 3 + tests/errors/return_m.zc | 3 + tests/errors/tag_mismatch.zc | 2 + tests/errors/temporary_output.zc | 9 + tests/errors/u_dropped.zc | 2 + tests/errors/u_skip_level.zc | 2 + tests/errors/unchecked_touch.zc | 2 + tests/errors/unused.zc | 2 + tests/errors/use_after_give.zc | 3 + tests/langerrors/cfunc_name.zc | 2 + tests/langerrors/const_index.zc | 2 + tests/langerrors/extern_bigret.zc | 4 + tests/langerrors/extern_struct.zc | 3 + tests/langerrors/extern_type.zc | 3 + tests/langerrors/fixed_nolen.zc | 2 + tests/langerrors/fixed_push.zc | 2 + tests/langerrors/fixed_to_dyn.zc | 4 + tests/langerrors/gen_undeclared.zc | 3 + tests/langerrors/o3_addr.zc | 2 + tests/langerrors/o3_asm.zc | 2 + tests/langerrors/o3_let.zc | 2 + tests/langerrors/o3_param.zc | 3 + tests/langerrors/parseword_noread.zc | 5 + tests/langerrors/pk_addr.zc | 6 + tests/langerrors/tagenum_args.zc | 3 + tests/langerrors/tagenum_bind.zc | 10 + tests/langerrors/tagenum_value.zc | 3 + tests/programs/alias.zc | 21 + tests/programs/allocfast.zc | 45 + tests/programs/annotate.expected | 33 + tests/programs/annotate.zc | 33 + tests/programs/arena.zc | 16 + tests/programs/args.zc | 16 + tests/programs/arrays.zc | 23 + tests/programs/backtick.zc | 9 + tests/programs/bareexit.zc | 7 + tests/programs/bareio.zc | 28 + tests/programs/basics.zc | 21 + tests/programs/bench.zc | 29 + tests/programs/bench_bytes.zc | 19 + tests/programs/bench_share.zc | 30 + tests/programs/bench_strings.zc | 27 + tests/programs/bitops.expected | 2 + tests/programs/bitops.zc | 26 + tests/programs/borrowmove.zc | 2 + tests/programs/bounds.zc | 28 + tests/programs/bounds_hi.zc | 8 + tests/programs/bounds_lo.zc | 8 + tests/programs/bstr_io.zc | 22 + tests/programs/buddy.zc | 16 + tests/programs/bytes.zc | 44 + tests/programs/cabi.expected | 5 + tests/programs/cabi.zc | 46 + tests/programs/cabi_o3.expected | 2 + tests/programs/cabi_o3.zc | 30 + tests/programs/cabi_sized.expected | 1 + tests/programs/cabi_sized.zc | 28 + tests/programs/capture.zc | 31 + tests/programs/closure.zc | 52 + tests/programs/csyn.zc | 18 + tests/programs/decl_undefined.zc | 2 + tests/programs/discard_m.zc | 3 + tests/programs/doublefree.zc | 9 + tests/programs/enhance.zc | 28 + tests/programs/enumscope.zc | 9 + tests/programs/enumtype.zc | 12 + tests/programs/f32.expected | 1 + tests/programs/f32.zc | 37 + tests/programs/f32o3.zc | 14 + tests/programs/fbench.zc | 37 + tests/programs/float.zc | 32 + tests/programs/fo3.zc | 50 + tests/programs/ftext.zc | 37 + tests/programs/ftruth.zc | 22 + tests/programs/ftype.zc | 47 + tests/programs/gc.zc | 18 + tests/programs/generics.expected | 2 + tests/programs/generics.zc | 54 + tests/programs/genmeth.expected | 1 + tests/programs/genmeth.zc | 36 + tests/programs/growheap.zc | 32 + tests/programs/growone.zc | 9 + tests/programs/infer_return.zc | 3 + tests/programs/interior.zc | 17 + tests/programs/joins.zc | 31 + tests/programs/manyerrors.zc | 6 + tests/programs/memleak.zc | 11 + tests/programs/memory_bounds.zc | 13 + tests/programs/meta.zc | 43 + tests/programs/methodown.zc | 15 + tests/programs/misc.zc | 27 + tests/programs/names.zc | 9 + tests/programs/o3.zc | 75 + tests/programs/o3_bounds.zc | 2 + tests/programs/o3bytes.zc | 52 + tests/programs/o3call.zc | 29 + tests/programs/o3copy.zc | 12 + tests/programs/o3deep.zc | 3 + tests/programs/o3deep_over.zc | 3 + tests/programs/o3direct.expected | 2 + tests/programs/o3direct.zc | 33 + tests/programs/o3fat.zc | 20 + tests/programs/o3licm.zc | 46 + tests/programs/o3push.expected | 1 + tests/programs/o3push.zc | 38 + tests/programs/o3scopes.zc | 17 + tests/programs/o3zero.zc | 8 + tests/programs/overflow_data.zc | 1 + tests/programs/overflow_frame.zc | 2 + tests/programs/overflow_return.zc | 2 + tests/programs/packedfields.expected | 2 + tests/programs/packedfields.zc | 42 + tests/programs/quarantine.zc | 25 + tests/programs/rawpid.zc | 6 + tests/programs/rawsys.zc | 5 + tests/programs/recycle.zc | 23 + tests/programs/roots.zc | 17 + tests/programs/scrub.zc | 9 + tests/programs/share.zc | 34 + tests/programs/share_dbg.zc | 10 + tests/programs/sized.expected | 5 + tests/programs/sized.zc | 80 + tests/programs/sizedvec.expected | 2 + tests/programs/sizedvec.zc | 39 + tests/programs/static.zc | 2 + tests/programs/stdbuf.zc | 18 + tests/programs/stdmore.zc | 32 + tests/programs/strings.zc | 37 + tests/programs/structret.zc | 24 + tests/programs/structval.zc | 26 + tests/programs/sys.zc | 11 + tests/programs/tagenum.zc | 26 + tests/programs/tagenum_two.zc | 20 + tests/programs/tagenum_warn.zc | 17 + tests/programs/typedmethods.zc | 16 + tests/programs/typeoff.zc | 2 + tests/programs/typeok.zc | 21 + tests/programs/types.zc | 55 + tests/programs/uaf.zc | 8 + tests/programs/uaf_in.zc | 9 + tests/programs/umd.zc | 50 + tests/programs/undefined_call.zc | 1 + tests/programs/use_std.zc | 9 + tests/programs/wide.zc | 9 + tests/programs/zc0plain.zc | 23 + tests/programs/zc0typed.zc | 23 + tests/runerr.sh | 10 + tests/test.sh | 577 ++ tests/test_win.ps1 | 258 + tests/typeerrors/arg.zc | 5 + tests/typeerrors/arith.zc | 3 + tests/typeerrors/assign.zc | 3 + tests/typeerrors/call.zc | 3 + tests/typeerrors/deref.zc | 3 + tests/typeerrors/enum_int.zc | 4 + tests/typeerrors/enum_other.zc | 5 + tests/typeerrors/field.zc | 3 + tests/typeerrors/generic.zc | 4 + tests/typeerrors/notstruct.zc | 2 + tests/typeerrors/ret.zc | 4 + tests/typeerrors/voidret.zc | 4 + tinyvm.bat | 635 ++ tinyvm.sh | 106 + 791 files changed, 86944 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 README.md create mode 100644 TODO.md create mode 100644 docs/BORROW.md create mode 100644 docs/LEARN.md create mode 100644 docs/design/adt.md create mode 100644 docs/design/handoff.md create mode 100644 docs/design/memory.md create mode 100644 docs/design/storage-analysis.md create mode 100644 docs/design/struct-by-value.md create mode 100644 docs/design/tiering.md create mode 100644 docs/history/01-golf/notes.md create mode 100644 docs/history/02-hardware/notes.md create mode 100644 docs/history/04-darwin-compiler/notes.md create mode 100644 docs/history/05-hybrid/notes.md create mode 100644 docs/history/06-choosing/notes.md create mode 100644 docs/history/07-zygote/code/README.md create mode 100644 docs/history/07-zygote/code/aslib.s create mode 100644 docs/history/07-zygote/code/core.c create mode 100644 docs/history/07-zygote/code/examples/asmain.s create mode 100644 docs/history/07-zygote/code/examples/echo.s create mode 100644 docs/history/07-zygote/code/examples/fact.s create mode 100644 docs/history/07-zygote/code/examples/hello.s create mode 100644 docs/history/07-zygote/code/examples/sm.s create mode 100644 docs/history/07-zygote/code/gen.s create mode 100644 docs/history/07-zygote/code/genome.h create mode 100644 docs/history/07-zygote/code/io_linux.c create mode 100644 docs/history/07-zygote/code/mkgenome.c create mode 100644 docs/history/07-zygote/code/repl.s create mode 100755 docs/history/07-zygote/code/test.sh create mode 100644 docs/history/07-zygote/notes.md create mode 100644 docs/history/08-packaging/notes.md create mode 100644 docs/history/10-zc/code/README.md create mode 100644 docs/history/10-zc/code/core.c create mode 100644 docs/history/10-zc/code/examples/echo.zc create mode 100644 docs/history/10-zc/code/examples/fact.zc create mode 100644 docs/history/10-zc/code/examples/hello.zc create mode 100644 docs/history/10-zc/code/examples/lists.zc create mode 100644 docs/history/10-zc/code/examples/repl.zc create mode 100644 docs/history/10-zc/code/examples/selfpatch.zc create mode 100644 docs/history/10-zc/code/genome.h create mode 100644 docs/history/10-zc/code/io_linux.c create mode 100644 docs/history/10-zc/code/mkgenome.c create mode 100644 docs/history/10-zc/code/src/compiler.zc create mode 100644 docs/history/10-zc/code/src/rt.zc create mode 100755 docs/history/10-zc/code/test.sh create mode 100644 docs/history/10-zc/code/tests/basics.zc create mode 100644 docs/history/10-zc/code/tests/errors/back_to_m.zc create mode 100644 docs/history/10-zc/code/tests/errors/borrow_escape.zc create mode 100644 docs/history/10-zc/code/tests/errors/branch_mismatch.zc create mode 100644 docs/history/10-zc/code/tests/errors/child_dies.zc create mode 100644 docs/history/10-zc/code/tests/errors/child_recast.zc create mode 100644 docs/history/10-zc/code/tests/errors/copy_owned.zc create mode 100644 docs/history/10-zc/code/tests/errors/d_into_block.zc create mode 100644 docs/history/10-zc/code/tests/errors/d_kept.zc create mode 100644 docs/history/10-zc/code/tests/errors/discard_u.zc create mode 100644 docs/history/10-zc/code/tests/errors/give_m.zc create mode 100644 docs/history/10-zc/code/tests/errors/global_store.zc create mode 100644 docs/history/10-zc/code/tests/errors/leak_overwrite.zc create mode 100644 docs/history/10-zc/code/tests/errors/lend_d.zc create mode 100644 docs/history/10-zc/code/tests/errors/loop_move.zc create mode 100644 docs/history/10-zc/code/tests/errors/plain_return.zc create mode 100644 docs/history/10-zc/code/tests/errors/proto_needed.zc create mode 100644 docs/history/10-zc/code/tests/errors/return_m.zc create mode 100644 docs/history/10-zc/code/tests/errors/tag_mismatch.zc create mode 100644 docs/history/10-zc/code/tests/errors/u_dropped.zc create mode 100644 docs/history/10-zc/code/tests/errors/u_skip_level.zc create mode 100644 docs/history/10-zc/code/tests/errors/unchecked_touch.zc create mode 100644 docs/history/10-zc/code/tests/errors/untagged_param.zc create mode 100644 docs/history/10-zc/code/tests/errors/unused.zc create mode 100644 docs/history/10-zc/code/tests/errors/use_after_give.zc create mode 100644 docs/history/10-zc/code/tests/gc.zc create mode 100644 docs/history/10-zc/code/tests/meta.zc create mode 100644 docs/history/10-zc/code/tests/misc.zc create mode 100755 docs/history/10-zc/code/tests/runerr.sh create mode 100644 docs/history/10-zc/code/tests/umd.zc create mode 100644 docs/history/10-zc/code/tools/build.zc create mode 100644 docs/history/10-zc/code/zc0.c create mode 100644 docs/history/10-zc/notes.md create mode 100644 docs/history/11-types-enhancements/code/README.md create mode 100644 docs/history/11-types-enhancements/code/core.c create mode 100644 docs/history/11-types-enhancements/code/examples/echo.zc create mode 100644 docs/history/11-types-enhancements/code/examples/fact.zc create mode 100644 docs/history/11-types-enhancements/code/examples/hello.zc create mode 100644 docs/history/11-types-enhancements/code/examples/lists.zc create mode 100644 docs/history/11-types-enhancements/code/examples/repl.zc create mode 100644 docs/history/11-types-enhancements/code/examples/selfpatch.zc create mode 100644 docs/history/11-types-enhancements/code/examples/typed.zc create mode 100644 docs/history/11-types-enhancements/code/examples/wiring.zc create mode 100644 docs/history/11-types-enhancements/code/genome.h create mode 100644 docs/history/11-types-enhancements/code/io_linux.c create mode 100644 docs/history/11-types-enhancements/code/mkgenome.c create mode 100644 docs/history/11-types-enhancements/code/src/compiler.zc create mode 100644 docs/history/11-types-enhancements/code/src/rt.zc create mode 100755 docs/history/11-types-enhancements/code/test.sh create mode 100644 docs/history/11-types-enhancements/code/tests/basics.zc create mode 100644 docs/history/11-types-enhancements/code/tests/enhance.zc create mode 100644 docs/history/11-types-enhancements/code/tests/errors/back_to_m.zc create mode 100644 docs/history/11-types-enhancements/code/tests/errors/borrow_escape.zc create mode 100644 docs/history/11-types-enhancements/code/tests/errors/branch_mismatch.zc create mode 100644 docs/history/11-types-enhancements/code/tests/errors/child_dies.zc create mode 100644 docs/history/11-types-enhancements/code/tests/errors/child_recast.zc create mode 100644 docs/history/11-types-enhancements/code/tests/errors/copy_owned.zc create mode 100644 docs/history/11-types-enhancements/code/tests/errors/d_into_block.zc create mode 100644 docs/history/11-types-enhancements/code/tests/errors/d_kept.zc create mode 100644 docs/history/11-types-enhancements/code/tests/errors/discard_u.zc create mode 100644 docs/history/11-types-enhancements/code/tests/errors/give_m.zc create mode 100644 docs/history/11-types-enhancements/code/tests/errors/global_store.zc create mode 100644 docs/history/11-types-enhancements/code/tests/errors/leak_overwrite.zc create mode 100644 docs/history/11-types-enhancements/code/tests/errors/lend_d.zc create mode 100644 docs/history/11-types-enhancements/code/tests/errors/loop_move.zc create mode 100644 docs/history/11-types-enhancements/code/tests/errors/plain_return.zc create mode 100644 docs/history/11-types-enhancements/code/tests/errors/proto_needed.zc create mode 100644 docs/history/11-types-enhancements/code/tests/errors/return_m.zc create mode 100644 docs/history/11-types-enhancements/code/tests/errors/tag_mismatch.zc create mode 100644 docs/history/11-types-enhancements/code/tests/errors/u_dropped.zc create mode 100644 docs/history/11-types-enhancements/code/tests/errors/u_skip_level.zc create mode 100644 docs/history/11-types-enhancements/code/tests/errors/unchecked_touch.zc create mode 100644 docs/history/11-types-enhancements/code/tests/errors/unused.zc create mode 100644 docs/history/11-types-enhancements/code/tests/errors/use_after_give.zc create mode 100644 docs/history/11-types-enhancements/code/tests/gc.zc create mode 100644 docs/history/11-types-enhancements/code/tests/meta.zc create mode 100644 docs/history/11-types-enhancements/code/tests/misc.zc create mode 100755 docs/history/11-types-enhancements/code/tests/runerr.sh create mode 100644 docs/history/11-types-enhancements/code/tests/typeerrors/arg.zc create mode 100644 docs/history/11-types-enhancements/code/tests/typeerrors/arith.zc create mode 100644 docs/history/11-types-enhancements/code/tests/typeerrors/assign.zc create mode 100644 docs/history/11-types-enhancements/code/tests/typeerrors/call.zc create mode 100644 docs/history/11-types-enhancements/code/tests/typeerrors/deref.zc create mode 100644 docs/history/11-types-enhancements/code/tests/typeerrors/field.zc create mode 100644 docs/history/11-types-enhancements/code/tests/typeerrors/generic.zc create mode 100644 docs/history/11-types-enhancements/code/tests/typeerrors/notstruct.zc create mode 100644 docs/history/11-types-enhancements/code/tests/typeerrors/ret.zc create mode 100644 docs/history/11-types-enhancements/code/tests/typeerrors/voidret.zc create mode 100644 docs/history/11-types-enhancements/code/tests/typeoff.zc create mode 100644 docs/history/11-types-enhancements/code/tests/typeok.zc create mode 100644 docs/history/11-types-enhancements/code/tests/types.zc create mode 100644 docs/history/11-types-enhancements/code/tests/umd.zc create mode 100644 docs/history/11-types-enhancements/code/tools/build.zc create mode 100644 docs/history/11-types-enhancements/code/zc0.c create mode 100644 docs/history/11-types-enhancements/notes.md create mode 100644 docs/history/13-gap-fixes/code/README.md create mode 100644 docs/history/13-gap-fixes/code/core.c create mode 100644 docs/history/13-gap-fixes/code/examples/echo.zc create mode 100644 docs/history/13-gap-fixes/code/examples/fact.zc create mode 100644 docs/history/13-gap-fixes/code/examples/hello.zc create mode 100644 docs/history/13-gap-fixes/code/examples/lists.zc create mode 100644 docs/history/13-gap-fixes/code/examples/repl.zc create mode 100644 docs/history/13-gap-fixes/code/examples/selfpatch.zc create mode 100644 docs/history/13-gap-fixes/code/examples/typed.zc create mode 100644 docs/history/13-gap-fixes/code/examples/wiring.zc create mode 100644 docs/history/13-gap-fixes/code/genome.h create mode 100644 docs/history/13-gap-fixes/code/io_linux.c create mode 100644 docs/history/13-gap-fixes/code/mkgenome.c create mode 100644 docs/history/13-gap-fixes/code/src/compiler.zc create mode 100644 docs/history/13-gap-fixes/code/src/rt.zc create mode 100755 docs/history/13-gap-fixes/code/test.sh create mode 100644 docs/history/13-gap-fixes/code/tests/basics.zc create mode 100644 docs/history/13-gap-fixes/code/tests/borrowmove.zc create mode 100644 docs/history/13-gap-fixes/code/tests/buddy.zc create mode 100644 docs/history/13-gap-fixes/code/tests/enhance.zc create mode 100644 docs/history/13-gap-fixes/code/tests/errors/back_to_m.zc create mode 100644 docs/history/13-gap-fixes/code/tests/errors/borrow_escape.zc create mode 100644 docs/history/13-gap-fixes/code/tests/errors/branch_mismatch.zc create mode 100644 docs/history/13-gap-fixes/code/tests/errors/child_dies.zc create mode 100644 docs/history/13-gap-fixes/code/tests/errors/child_recast.zc create mode 100644 docs/history/13-gap-fixes/code/tests/errors/copy_owned.zc create mode 100644 docs/history/13-gap-fixes/code/tests/errors/d_into_block.zc create mode 100644 docs/history/13-gap-fixes/code/tests/errors/d_kept.zc create mode 100644 docs/history/13-gap-fixes/code/tests/errors/discard_u.zc create mode 100644 docs/history/13-gap-fixes/code/tests/errors/give_m.zc create mode 100644 docs/history/13-gap-fixes/code/tests/errors/global_store.zc create mode 100644 docs/history/13-gap-fixes/code/tests/errors/leak_overwrite.zc create mode 100644 docs/history/13-gap-fixes/code/tests/errors/lend_d.zc create mode 100644 docs/history/13-gap-fixes/code/tests/errors/loop_move.zc create mode 100644 docs/history/13-gap-fixes/code/tests/errors/plain_return.zc create mode 100644 docs/history/13-gap-fixes/code/tests/errors/return_m.zc create mode 100644 docs/history/13-gap-fixes/code/tests/errors/tag_mismatch.zc create mode 100644 docs/history/13-gap-fixes/code/tests/errors/u_dropped.zc create mode 100644 docs/history/13-gap-fixes/code/tests/errors/u_skip_level.zc create mode 100644 docs/history/13-gap-fixes/code/tests/errors/unchecked_touch.zc create mode 100644 docs/history/13-gap-fixes/code/tests/errors/unused.zc create mode 100644 docs/history/13-gap-fixes/code/tests/errors/use_after_give.zc create mode 100644 docs/history/13-gap-fixes/code/tests/gc.zc create mode 100644 docs/history/13-gap-fixes/code/tests/joins.zc create mode 100644 docs/history/13-gap-fixes/code/tests/manyerrors.zc create mode 100644 docs/history/13-gap-fixes/code/tests/meta.zc create mode 100644 docs/history/13-gap-fixes/code/tests/misc.zc create mode 100644 docs/history/13-gap-fixes/code/tests/overflow_data.zc create mode 100644 docs/history/13-gap-fixes/code/tests/overflow_frame.zc create mode 100644 docs/history/13-gap-fixes/code/tests/overflow_return.zc create mode 100755 docs/history/13-gap-fixes/code/tests/runerr.sh create mode 100644 docs/history/13-gap-fixes/code/tests/typeerrors/arg.zc create mode 100644 docs/history/13-gap-fixes/code/tests/typeerrors/arith.zc create mode 100644 docs/history/13-gap-fixes/code/tests/typeerrors/assign.zc create mode 100644 docs/history/13-gap-fixes/code/tests/typeerrors/call.zc create mode 100644 docs/history/13-gap-fixes/code/tests/typeerrors/deref.zc create mode 100644 docs/history/13-gap-fixes/code/tests/typeerrors/field.zc create mode 100644 docs/history/13-gap-fixes/code/tests/typeerrors/generic.zc create mode 100644 docs/history/13-gap-fixes/code/tests/typeerrors/notstruct.zc create mode 100644 docs/history/13-gap-fixes/code/tests/typeerrors/ret.zc create mode 100644 docs/history/13-gap-fixes/code/tests/typeerrors/voidret.zc create mode 100644 docs/history/13-gap-fixes/code/tests/typeoff.zc create mode 100644 docs/history/13-gap-fixes/code/tests/typeok.zc create mode 100644 docs/history/13-gap-fixes/code/tests/types.zc create mode 100644 docs/history/13-gap-fixes/code/tests/umd.zc create mode 100644 docs/history/13-gap-fixes/code/tools/build.zc create mode 100644 docs/history/13-gap-fixes/code/zc0.c create mode 100644 docs/history/13-gap-fixes/notes.md create mode 100644 docs/history/15-o3/code/README.md create mode 100755 docs/history/15-o3/code/bench.sh create mode 100644 docs/history/15-o3/code/core.c create mode 100644 docs/history/15-o3/code/examples/bf.zc create mode 100644 docs/history/15-o3/code/examples/dump.zc create mode 100644 docs/history/15-o3/code/examples/echo.zc create mode 100644 docs/history/15-o3/code/examples/fact.zc create mode 100644 docs/history/15-o3/code/examples/hello.zc create mode 100644 docs/history/15-o3/code/examples/lang.zc create mode 100644 docs/history/15-o3/code/examples/life.zc create mode 100644 docs/history/15-o3/code/examples/lists.zc create mode 100644 docs/history/15-o3/code/examples/mandel.zc create mode 100644 docs/history/15-o3/code/examples/primes.zc create mode 100644 docs/history/15-o3/code/examples/quine.zc create mode 100644 docs/history/15-o3/code/examples/repl.zc create mode 100644 docs/history/15-o3/code/examples/selfpatch.zc create mode 100644 docs/history/15-o3/code/examples/staged.zc create mode 100644 docs/history/15-o3/code/examples/tree.zc create mode 100644 docs/history/15-o3/code/examples/typed.zc create mode 100644 docs/history/15-o3/code/examples/wiring.zc create mode 100644 docs/history/15-o3/code/genome.h create mode 100644 docs/history/15-o3/code/io_linux.c create mode 100644 docs/history/15-o3/code/mkgenome.c create mode 100644 docs/history/15-o3/code/src/compiler.zc create mode 100644 docs/history/15-o3/code/src/rt.zc create mode 100755 docs/history/15-o3/code/test.sh create mode 100644 docs/history/15-o3/code/tests/arrays.zc create mode 100644 docs/history/15-o3/code/tests/basics.zc create mode 100644 docs/history/15-o3/code/tests/bench.zc create mode 100644 docs/history/15-o3/code/tests/borrowmove.zc create mode 100644 docs/history/15-o3/code/tests/buddy.zc create mode 100644 docs/history/15-o3/code/tests/enhance.zc create mode 100644 docs/history/15-o3/code/tests/errors/back_to_m.zc create mode 100644 docs/history/15-o3/code/tests/errors/borrow_escape.zc create mode 100644 docs/history/15-o3/code/tests/errors/branch_mismatch.zc create mode 100644 docs/history/15-o3/code/tests/errors/child_dies.zc create mode 100644 docs/history/15-o3/code/tests/errors/child_recast.zc create mode 100644 docs/history/15-o3/code/tests/errors/copy_owned.zc create mode 100644 docs/history/15-o3/code/tests/errors/d_into_block.zc create mode 100644 docs/history/15-o3/code/tests/errors/d_kept.zc create mode 100644 docs/history/15-o3/code/tests/errors/discard_u.zc create mode 100644 docs/history/15-o3/code/tests/errors/give_m.zc create mode 100644 docs/history/15-o3/code/tests/errors/global_store.zc create mode 100644 docs/history/15-o3/code/tests/errors/leak_overwrite.zc create mode 100644 docs/history/15-o3/code/tests/errors/lend_d.zc create mode 100644 docs/history/15-o3/code/tests/errors/loop_move.zc create mode 100644 docs/history/15-o3/code/tests/errors/plain_return.zc create mode 100644 docs/history/15-o3/code/tests/errors/return_m.zc create mode 100644 docs/history/15-o3/code/tests/errors/tag_mismatch.zc create mode 100644 docs/history/15-o3/code/tests/errors/u_dropped.zc create mode 100644 docs/history/15-o3/code/tests/errors/u_skip_level.zc create mode 100644 docs/history/15-o3/code/tests/errors/unchecked_touch.zc create mode 100644 docs/history/15-o3/code/tests/errors/unused.zc create mode 100644 docs/history/15-o3/code/tests/errors/use_after_give.zc create mode 100644 docs/history/15-o3/code/tests/gc.zc create mode 100644 docs/history/15-o3/code/tests/joins.zc create mode 100644 docs/history/15-o3/code/tests/langerrors/const_index.zc create mode 100644 docs/history/15-o3/code/tests/langerrors/fixed_nolen.zc create mode 100644 docs/history/15-o3/code/tests/langerrors/fixed_push.zc create mode 100644 docs/history/15-o3/code/tests/langerrors/fixed_to_dyn.zc create mode 100644 docs/history/15-o3/code/tests/langerrors/o3_addr.zc create mode 100644 docs/history/15-o3/code/tests/langerrors/o3_asm.zc create mode 100644 docs/history/15-o3/code/tests/langerrors/o3_let.zc create mode 100644 docs/history/15-o3/code/tests/langerrors/o3_param.zc create mode 100644 docs/history/15-o3/code/tests/manyerrors.zc create mode 100644 docs/history/15-o3/code/tests/meta.zc create mode 100644 docs/history/15-o3/code/tests/misc.zc create mode 100644 docs/history/15-o3/code/tests/o3.zc create mode 100644 docs/history/15-o3/code/tests/o3_bounds.zc create mode 100644 docs/history/15-o3/code/tests/overflow_data.zc create mode 100644 docs/history/15-o3/code/tests/overflow_frame.zc create mode 100644 docs/history/15-o3/code/tests/overflow_return.zc create mode 100755 docs/history/15-o3/code/tests/runerr.sh create mode 100644 docs/history/15-o3/code/tests/typeerrors/arg.zc create mode 100644 docs/history/15-o3/code/tests/typeerrors/arith.zc create mode 100644 docs/history/15-o3/code/tests/typeerrors/assign.zc create mode 100644 docs/history/15-o3/code/tests/typeerrors/call.zc create mode 100644 docs/history/15-o3/code/tests/typeerrors/deref.zc create mode 100644 docs/history/15-o3/code/tests/typeerrors/field.zc create mode 100644 docs/history/15-o3/code/tests/typeerrors/generic.zc create mode 100644 docs/history/15-o3/code/tests/typeerrors/notstruct.zc create mode 100644 docs/history/15-o3/code/tests/typeerrors/ret.zc create mode 100644 docs/history/15-o3/code/tests/typeerrors/voidret.zc create mode 100644 docs/history/15-o3/code/tests/typeoff.zc create mode 100644 docs/history/15-o3/code/tests/typeok.zc create mode 100644 docs/history/15-o3/code/tests/types.zc create mode 100644 docs/history/15-o3/code/tests/umd.zc create mode 100644 docs/history/15-o3/code/tools/build.zc create mode 100644 docs/history/15-o3/code/zc0.c create mode 100644 docs/history/15-o3/notes.md create mode 100644 docs/history/17-o3-boundaries/code/README.md create mode 100755 docs/history/17-o3-boundaries/code/bench.sh create mode 100644 docs/history/17-o3-boundaries/code/core.c create mode 100644 docs/history/17-o3-boundaries/code/examples/bf.zc create mode 100644 docs/history/17-o3-boundaries/code/examples/dump.zc create mode 100644 docs/history/17-o3-boundaries/code/examples/echo.zc create mode 100644 docs/history/17-o3-boundaries/code/examples/fact.zc create mode 100644 docs/history/17-o3-boundaries/code/examples/hello.zc create mode 100644 docs/history/17-o3-boundaries/code/examples/lang.zc create mode 100644 docs/history/17-o3-boundaries/code/examples/life.zc create mode 100644 docs/history/17-o3-boundaries/code/examples/lists.zc create mode 100644 docs/history/17-o3-boundaries/code/examples/mandel.zc create mode 100644 docs/history/17-o3-boundaries/code/examples/primes.zc create mode 100644 docs/history/17-o3-boundaries/code/examples/quine.zc create mode 100644 docs/history/17-o3-boundaries/code/examples/repl.zc create mode 100644 docs/history/17-o3-boundaries/code/examples/selfpatch.zc create mode 100644 docs/history/17-o3-boundaries/code/examples/staged.zc create mode 100644 docs/history/17-o3-boundaries/code/examples/tree.zc create mode 100644 docs/history/17-o3-boundaries/code/examples/typed.zc create mode 100644 docs/history/17-o3-boundaries/code/examples/wiring.zc create mode 100644 docs/history/17-o3-boundaries/code/genome.h create mode 100644 docs/history/17-o3-boundaries/code/io_linux.c create mode 100644 docs/history/17-o3-boundaries/code/mkgenome.c create mode 100644 docs/history/17-o3-boundaries/code/src/compiler.zc create mode 100644 docs/history/17-o3-boundaries/code/src/rt.zc create mode 100755 docs/history/17-o3-boundaries/code/test.sh create mode 100644 docs/history/17-o3-boundaries/code/tests/arrays.zc create mode 100644 docs/history/17-o3-boundaries/code/tests/basics.zc create mode 100644 docs/history/17-o3-boundaries/code/tests/bench.zc create mode 100644 docs/history/17-o3-boundaries/code/tests/borrowmove.zc create mode 100644 docs/history/17-o3-boundaries/code/tests/buddy.zc create mode 100644 docs/history/17-o3-boundaries/code/tests/enhance.zc create mode 100644 docs/history/17-o3-boundaries/code/tests/errors/back_to_m.zc create mode 100644 docs/history/17-o3-boundaries/code/tests/errors/borrow_escape.zc create mode 100644 docs/history/17-o3-boundaries/code/tests/errors/branch_mismatch.zc create mode 100644 docs/history/17-o3-boundaries/code/tests/errors/child_dies.zc create mode 100644 docs/history/17-o3-boundaries/code/tests/errors/child_recast.zc create mode 100644 docs/history/17-o3-boundaries/code/tests/errors/copy_owned.zc create mode 100644 docs/history/17-o3-boundaries/code/tests/errors/d_into_block.zc create mode 100644 docs/history/17-o3-boundaries/code/tests/errors/d_kept.zc create mode 100644 docs/history/17-o3-boundaries/code/tests/errors/discard_u.zc create mode 100644 docs/history/17-o3-boundaries/code/tests/errors/give_m.zc create mode 100644 docs/history/17-o3-boundaries/code/tests/errors/global_store.zc create mode 100644 docs/history/17-o3-boundaries/code/tests/errors/leak_overwrite.zc create mode 100644 docs/history/17-o3-boundaries/code/tests/errors/lend_d.zc create mode 100644 docs/history/17-o3-boundaries/code/tests/errors/loop_move.zc create mode 100644 docs/history/17-o3-boundaries/code/tests/errors/plain_return.zc create mode 100644 docs/history/17-o3-boundaries/code/tests/errors/return_m.zc create mode 100644 docs/history/17-o3-boundaries/code/tests/errors/tag_mismatch.zc create mode 100644 docs/history/17-o3-boundaries/code/tests/errors/u_dropped.zc create mode 100644 docs/history/17-o3-boundaries/code/tests/errors/u_skip_level.zc create mode 100644 docs/history/17-o3-boundaries/code/tests/errors/unchecked_touch.zc create mode 100644 docs/history/17-o3-boundaries/code/tests/errors/unused.zc create mode 100644 docs/history/17-o3-boundaries/code/tests/errors/use_after_give.zc create mode 100644 docs/history/17-o3-boundaries/code/tests/gc.zc create mode 100644 docs/history/17-o3-boundaries/code/tests/joins.zc create mode 100644 docs/history/17-o3-boundaries/code/tests/langerrors/const_index.zc create mode 100644 docs/history/17-o3-boundaries/code/tests/langerrors/fixed_nolen.zc create mode 100644 docs/history/17-o3-boundaries/code/tests/langerrors/fixed_push.zc create mode 100644 docs/history/17-o3-boundaries/code/tests/langerrors/fixed_to_dyn.zc create mode 100644 docs/history/17-o3-boundaries/code/tests/langerrors/gen_undeclared.zc create mode 100644 docs/history/17-o3-boundaries/code/tests/langerrors/o3_addr.zc create mode 100644 docs/history/17-o3-boundaries/code/tests/langerrors/o3_asm.zc create mode 100644 docs/history/17-o3-boundaries/code/tests/langerrors/o3_let.zc create mode 100644 docs/history/17-o3-boundaries/code/tests/langerrors/o3_param.zc create mode 100644 docs/history/17-o3-boundaries/code/tests/manyerrors.zc create mode 100644 docs/history/17-o3-boundaries/code/tests/meta.zc create mode 100644 docs/history/17-o3-boundaries/code/tests/misc.zc create mode 100644 docs/history/17-o3-boundaries/code/tests/o3.zc create mode 100644 docs/history/17-o3-boundaries/code/tests/o3_bounds.zc create mode 100644 docs/history/17-o3-boundaries/code/tests/o3fat.zc create mode 100644 docs/history/17-o3-boundaries/code/tests/o3scopes.zc create mode 100644 docs/history/17-o3-boundaries/code/tests/overflow_data.zc create mode 100644 docs/history/17-o3-boundaries/code/tests/overflow_frame.zc create mode 100644 docs/history/17-o3-boundaries/code/tests/overflow_return.zc create mode 100755 docs/history/17-o3-boundaries/code/tests/runerr.sh create mode 100644 docs/history/17-o3-boundaries/code/tests/typeerrors/arg.zc create mode 100644 docs/history/17-o3-boundaries/code/tests/typeerrors/arith.zc create mode 100644 docs/history/17-o3-boundaries/code/tests/typeerrors/assign.zc create mode 100644 docs/history/17-o3-boundaries/code/tests/typeerrors/call.zc create mode 100644 docs/history/17-o3-boundaries/code/tests/typeerrors/deref.zc create mode 100644 docs/history/17-o3-boundaries/code/tests/typeerrors/field.zc create mode 100644 docs/history/17-o3-boundaries/code/tests/typeerrors/generic.zc create mode 100644 docs/history/17-o3-boundaries/code/tests/typeerrors/notstruct.zc create mode 100644 docs/history/17-o3-boundaries/code/tests/typeerrors/ret.zc create mode 100644 docs/history/17-o3-boundaries/code/tests/typeerrors/voidret.zc create mode 100644 docs/history/17-o3-boundaries/code/tests/typeoff.zc create mode 100644 docs/history/17-o3-boundaries/code/tests/typeok.zc create mode 100644 docs/history/17-o3-boundaries/code/tests/types.zc create mode 100644 docs/history/17-o3-boundaries/code/tests/umd.zc create mode 100644 docs/history/17-o3-boundaries/code/tools/build.zc create mode 100644 docs/history/17-o3-boundaries/code/zc0.c create mode 100644 docs/history/17-o3-boundaries/notes.md create mode 100644 docs/history/18-aliases/notes.md create mode 100644 docs/history/19-tools/notes.md create mode 100644 docs/history/20-words/notes.md create mode 100644 docs/history/21-micro/notes.md create mode 100644 docs/history/22-size/notes.md create mode 100644 docs/history/23-baremetal/notes.md create mode 100644 docs/history/24-backends/notes.md create mode 100644 docs/history/25-genmap/notes.md create mode 100644 docs/history/26-bareapi/notes.md create mode 100644 docs/history/27-float/notes.md create mode 100644 docs/history/28-bytes/notes.md create mode 100644 docs/history/29-strings/notes.md create mode 100644 docs/history/30-sharing/notes.md create mode 100644 docs/history/31-fpu/notes.md create mode 100644 docs/history/32-ftype/notes.md create mode 100644 docs/history/33-fo3/notes.md create mode 100644 docs/history/34-ftext/notes.md create mode 100644 docs/history/35-bench/notes.md create mode 100644 docs/history/36-alloc/notes.md create mode 100644 docs/history/37-call/notes.md create mode 100644 docs/history/38-bounds/notes.md create mode 100644 docs/history/39-blocks/notes.md create mode 100644 docs/history/40-fatten/notes.md create mode 100644 docs/history/41-obytes/notes.md create mode 100644 docs/history/42-roots/notes.md create mode 100644 docs/history/43-stdlib/notes.md create mode 100644 docs/history/44-check-mode/notes.md create mode 100644 docs/history/45-fast-alloc/notes.md create mode 100644 docs/history/46-c-abi/notes.md create mode 100644 docs/history/47-limits/notes.md create mode 100644 docs/history/48-landing/notes.md create mode 100644 docs/history/49-generics/notes.md create mode 100644 docs/history/50-adt/notes.md create mode 100644 docs/history/51-llvm-inline-stdlib/notes.md create mode 100644 docs/history/52-closures/notes.md create mode 100644 docs/history/53-typed-source/notes.md create mode 100644 docs/history/README.md create mode 100644 docs/history/baremetal-measurements.md create mode 100644 docs/history/maintenance-notes.md create mode 100644 docs/history/transcript.md create mode 100644 editors/vscode/.vscodeignore create mode 100644 editors/vscode/README.md create mode 100644 editors/vscode/client/diagcache.js create mode 100644 editors/vscode/client/extension.js create mode 100644 editors/vscode/client/hover.js create mode 100644 editors/vscode/client/vmproc.js create mode 100644 editors/vscode/client/wasmproc.js create mode 100644 editors/vscode/language-configuration.json create mode 100644 editors/vscode/package.json create mode 100644 editors/vscode/syntaxes/zc.tmLanguage.json create mode 100644 examples/bf.zc create mode 100644 examples/cli.zc create mode 100644 examples/cli2.zc create mode 100644 examples/dump.zc create mode 100644 examples/echo.zc create mode 100644 examples/fact.zc create mode 100644 examples/hello.zc create mode 100644 examples/lang.zc create mode 100644 examples/learn/01-hello.out create mode 100644 examples/learn/01-hello.zc create mode 100644 examples/learn/02-loops.out create mode 100644 examples/learn/02-loops.zc create mode 100644 examples/learn/03-do-continue.out create mode 100644 examples/learn/03-do-continue.zc create mode 100644 examples/learn/04-enums.out create mode 100644 examples/learn/04-enums.zc create mode 100644 examples/learn/05-small-ints.out create mode 100644 examples/learn/05-small-ints.zc create mode 100644 examples/learn/06-floats.out create mode 100644 examples/learn/06-floats.zc create mode 100644 examples/learn/07-bytes.out create mode 100644 examples/learn/07-bytes.zc create mode 100644 examples/learn/08-byte-strings.out create mode 100644 examples/learn/08-byte-strings.zc create mode 100644 examples/learn/09-shared.out create mode 100644 examples/learn/09-shared.zc create mode 100644 examples/learn/10-owned.out create mode 100644 examples/learn/10-owned.zc create mode 100644 examples/learn/11-tags.out create mode 100644 examples/learn/11-tags.zc create mode 100644 examples/learn/12-aliases.out create mode 100644 examples/learn/12-aliases.zc create mode 100644 examples/learn/13-memdebug.out create mode 100644 examples/learn/13-memdebug.zc create mode 100644 examples/learn/14-include.out create mode 100644 examples/learn/14-include.zc create mode 100644 examples/learn/15-generics.out create mode 100644 examples/learn/15-generics.zc create mode 100644 examples/learn/16-tagged-enums.out create mode 100644 examples/learn/16-tagged-enums.zc create mode 100644 examples/learn/17-switch.out create mode 100644 examples/learn/17-switch.zc create mode 100644 examples/learn/18-o3.out create mode 100644 examples/learn/18-o3.zc create mode 100644 examples/learn/19-growable.out create mode 100644 examples/learn/19-growable.zc create mode 100644 examples/learn/20-text-num.out create mode 100644 examples/learn/20-text-num.zc create mode 100644 examples/learn/21-host.out create mode 100644 examples/learn/21-host.zc create mode 100644 examples/learn/22-raw-x86.out create mode 100644 examples/learn/22-raw-x86.zc create mode 100644 examples/learn/23-c-abi.out create mode 100644 examples/learn/23-c-abi.zc create mode 100644 examples/learn/24-backtick.out create mode 100644 examples/learn/24-backtick.zc create mode 100644 examples/learn/25-closures.out create mode 100644 examples/learn/25-closures.zc create mode 100644 examples/learn/26-closure-state.out create mode 100644 examples/learn/26-closure-state.zc create mode 100644 examples/life.zc create mode 100644 examples/lists.zc create mode 100644 examples/mandel.zc create mode 100644 examples/opt_test.zc create mode 100644 examples/primes.zc create mode 100644 examples/quine.zc create mode 100644 examples/repl.zc create mode 100644 examples/selfpatch.zc create mode 100644 examples/staged.zc create mode 100644 examples/strings.zc create mode 100644 examples/struct_closure_opt.zc create mode 100644 examples/tree.zc create mode 100644 examples/typed.zc create mode 100644 examples/wiring.zc create mode 100644 src/baremetal/link_aarch64.ld create mode 100644 src/baremetal/link_riscv.ld create mode 100644 src/baremetal/port.c create mode 100755 src/baremetal/run.sh create mode 100644 src/baremetal/start_aarch64.S create mode 100644 src/baremetal/start_riscv.S create mode 100644 src/compiler/compiler.zc create mode 100644 src/core/bops.h create mode 100644 src/core/core.c create mode 100644 src/core/fops.h create mode 100644 src/core/io_linux.c create mode 100644 src/core/io_win.c create mode 100644 src/core/memmap.h create mode 100644 src/core/stack.h create mode 100644 src/js/extract_genome.js create mode 100644 src/js/extract_lsp.js create mode 100644 src/js/mapvalues.js create mode 100644 src/js/tinyvm.ts create mode 100644 src/lib/std.zc create mode 100644 src/lsp/server.zc create mode 100644 src/micro/micro.c create mode 100644 src/micro/micro_port.h create mode 100644 src/micro/port_linux.c create mode 100644 src/micro/port_stub.c create mode 100644 src/micro/port_wasm_stream.c create mode 100644 src/micro/port_win.c create mode 100644 src/runtime/rt.zc create mode 100644 src/runtime/run.zc create mode 100755 src/tools/automap.sh create mode 100644 src/tools/build.zc create mode 100644 src/tools/genmap.c create mode 100644 src/tools/mkgenome.c create mode 100755 src/tools/mklsp.sh create mode 100755 src/tools/mklspwasm.sh create mode 100755 src/tools/mkvsix.sh create mode 100644 src/tools/zc0.c create mode 100755 src/tools/zcc create mode 100755 src/web/build.sh create mode 100644 src/web/index.html create mode 100644 src/web/port_wasm.c create mode 100644 src/web/zc.js create mode 100644 tests/bench/bench.sh create mode 100644 tests/bench/bench_bytes.sh create mode 100644 tests/bench/bench_float.sh create mode 100644 tests/bench/bench_share.sh create mode 100644 tests/bench/bench_strings.sh create mode 100755 tests/bench/run.py create mode 100755 tests/bench/run.sh create mode 100644 tests/bench/selfmod.zc create mode 100644 tests/bench/sieve.c create mode 100644 tests/bench/sieve.zc create mode 100644 tests/bench/trees.c create mode 100644 tests/bench/trees.zc create mode 100644 tests/data/hi.txt create mode 100644 tests/data/sbv.c create mode 100644 tests/drivers/code_address_growth_test.py create mode 100644 tests/drivers/code_emission_growth_test.py create mode 100644 tests/drivers/compiler_growth_test.py create mode 100644 tests/drivers/core_lsp_test.js create mode 100644 tests/drivers/diagcache_test.js create mode 100644 tests/drivers/diagnostic_revision_test.js create mode 100644 tests/drivers/enhancement_backends_test.py create mode 100644 tests/drivers/ext_cache_test.js create mode 100644 tests/drivers/float_backends_test.py create mode 100644 tests/drivers/frame_growth_test.py create mode 100644 tests/drivers/genome_blob_test.py create mode 100644 tests/drivers/hover_features_test.js create mode 100644 tests/drivers/hover_test.js create mode 100644 tests/drivers/image_growth_test.py create mode 100644 tests/drivers/image_run.js create mode 100644 tests/drivers/inference_growth_test.py create mode 100644 tests/drivers/layout_config_test.py create mode 100644 tests/drivers/learn.py create mode 100644 tests/drivers/local_index_test.py create mode 100644 tests/drivers/lsp_growth_test.js create mode 100644 tests/drivers/lsp_live.js create mode 100644 tests/drivers/lsp_sandbox.js create mode 100644 tests/drivers/lsp_stream.js create mode 100644 tests/drivers/lsp_test.js create mode 100644 tests/drivers/lsp_vm.js create mode 100644 tests/drivers/lsp_wasm.js create mode 100644 tests/drivers/memory_limits_test.py create mode 100644 tests/drivers/parser_growth_test.py create mode 100644 tests/drivers/protocol_growth_test.js create mode 100644 tests/drivers/seed_growth_test.py create mode 100644 tests/drivers/stack_growth_test.py create mode 100644 tests/drivers/storage_analysis_test.js create mode 100644 tests/drivers/vscode_hover_test.js create mode 100644 tests/drivers/wasm_test.js create mode 100644 tests/drivers/word_literals_test.py create mode 100644 tests/errors/alias_field.zc create mode 100644 tests/errors/alias_index.zc create mode 100644 tests/errors/alias_plus.zc create mode 100644 tests/errors/alias_scope.zc create mode 100644 tests/errors/back_to_m.zc create mode 100644 tests/errors/borrow_escape.zc create mode 100644 tests/errors/branch_mismatch.zc create mode 100644 tests/errors/child_dies.zc create mode 100644 tests/errors/child_recast.zc create mode 100644 tests/errors/copy_owned.zc create mode 100644 tests/errors/d_into_block.zc create mode 100644 tests/errors/d_kept.zc create mode 100644 tests/errors/did_you_mean.zc create mode 100644 tests/errors/discard_u.zc create mode 100644 tests/errors/found_token.zc create mode 100644 tests/errors/give_m.zc create mode 100644 tests/errors/global_store.zc create mode 100644 tests/errors/leak_overwrite.zc create mode 100644 tests/errors/lend_d.zc create mode 100644 tests/errors/loop_move.zc create mode 100644 tests/errors/plain_return.zc create mode 100644 tests/errors/return_m.zc create mode 100644 tests/errors/tag_mismatch.zc create mode 100644 tests/errors/temporary_output.zc create mode 100644 tests/errors/u_dropped.zc create mode 100644 tests/errors/u_skip_level.zc create mode 100644 tests/errors/unchecked_touch.zc create mode 100644 tests/errors/unused.zc create mode 100644 tests/errors/use_after_give.zc create mode 100644 tests/langerrors/cfunc_name.zc create mode 100644 tests/langerrors/const_index.zc create mode 100644 tests/langerrors/extern_bigret.zc create mode 100644 tests/langerrors/extern_struct.zc create mode 100644 tests/langerrors/extern_type.zc create mode 100644 tests/langerrors/fixed_nolen.zc create mode 100644 tests/langerrors/fixed_push.zc create mode 100644 tests/langerrors/fixed_to_dyn.zc create mode 100644 tests/langerrors/gen_undeclared.zc create mode 100644 tests/langerrors/o3_addr.zc create mode 100644 tests/langerrors/o3_asm.zc create mode 100644 tests/langerrors/o3_let.zc create mode 100644 tests/langerrors/o3_param.zc create mode 100644 tests/langerrors/parseword_noread.zc create mode 100644 tests/langerrors/pk_addr.zc create mode 100644 tests/langerrors/tagenum_args.zc create mode 100644 tests/langerrors/tagenum_bind.zc create mode 100644 tests/langerrors/tagenum_value.zc create mode 100644 tests/programs/alias.zc create mode 100644 tests/programs/allocfast.zc create mode 100644 tests/programs/annotate.expected create mode 100644 tests/programs/annotate.zc create mode 100644 tests/programs/arena.zc create mode 100644 tests/programs/args.zc create mode 100644 tests/programs/arrays.zc create mode 100644 tests/programs/backtick.zc create mode 100644 tests/programs/bareexit.zc create mode 100644 tests/programs/bareio.zc create mode 100644 tests/programs/basics.zc create mode 100644 tests/programs/bench.zc create mode 100644 tests/programs/bench_bytes.zc create mode 100644 tests/programs/bench_share.zc create mode 100644 tests/programs/bench_strings.zc create mode 100644 tests/programs/bitops.expected create mode 100644 tests/programs/bitops.zc create mode 100644 tests/programs/borrowmove.zc create mode 100644 tests/programs/bounds.zc create mode 100644 tests/programs/bounds_hi.zc create mode 100644 tests/programs/bounds_lo.zc create mode 100644 tests/programs/bstr_io.zc create mode 100644 tests/programs/buddy.zc create mode 100644 tests/programs/bytes.zc create mode 100644 tests/programs/cabi.expected create mode 100644 tests/programs/cabi.zc create mode 100644 tests/programs/cabi_o3.expected create mode 100644 tests/programs/cabi_o3.zc create mode 100644 tests/programs/cabi_sized.expected create mode 100644 tests/programs/cabi_sized.zc create mode 100644 tests/programs/capture.zc create mode 100644 tests/programs/closure.zc create mode 100644 tests/programs/csyn.zc create mode 100644 tests/programs/decl_undefined.zc create mode 100644 tests/programs/discard_m.zc create mode 100644 tests/programs/doublefree.zc create mode 100644 tests/programs/enhance.zc create mode 100644 tests/programs/enumscope.zc create mode 100644 tests/programs/enumtype.zc create mode 100644 tests/programs/f32.expected create mode 100644 tests/programs/f32.zc create mode 100644 tests/programs/f32o3.zc create mode 100644 tests/programs/fbench.zc create mode 100644 tests/programs/float.zc create mode 100644 tests/programs/fo3.zc create mode 100644 tests/programs/ftext.zc create mode 100644 tests/programs/ftruth.zc create mode 100644 tests/programs/ftype.zc create mode 100644 tests/programs/gc.zc create mode 100644 tests/programs/generics.expected create mode 100644 tests/programs/generics.zc create mode 100644 tests/programs/genmeth.expected create mode 100644 tests/programs/genmeth.zc create mode 100644 tests/programs/growheap.zc create mode 100644 tests/programs/growone.zc create mode 100644 tests/programs/infer_return.zc create mode 100644 tests/programs/interior.zc create mode 100644 tests/programs/joins.zc create mode 100644 tests/programs/manyerrors.zc create mode 100644 tests/programs/memleak.zc create mode 100644 tests/programs/memory_bounds.zc create mode 100644 tests/programs/meta.zc create mode 100644 tests/programs/methodown.zc create mode 100644 tests/programs/misc.zc create mode 100644 tests/programs/names.zc create mode 100644 tests/programs/o3.zc create mode 100644 tests/programs/o3_bounds.zc create mode 100644 tests/programs/o3bytes.zc create mode 100644 tests/programs/o3call.zc create mode 100644 tests/programs/o3copy.zc create mode 100644 tests/programs/o3deep.zc create mode 100644 tests/programs/o3deep_over.zc create mode 100644 tests/programs/o3direct.expected create mode 100644 tests/programs/o3direct.zc create mode 100644 tests/programs/o3fat.zc create mode 100644 tests/programs/o3licm.zc create mode 100644 tests/programs/o3push.expected create mode 100644 tests/programs/o3push.zc create mode 100644 tests/programs/o3scopes.zc create mode 100644 tests/programs/o3zero.zc create mode 100644 tests/programs/overflow_data.zc create mode 100644 tests/programs/overflow_frame.zc create mode 100644 tests/programs/overflow_return.zc create mode 100644 tests/programs/packedfields.expected create mode 100644 tests/programs/packedfields.zc create mode 100644 tests/programs/quarantine.zc create mode 100644 tests/programs/rawpid.zc create mode 100644 tests/programs/rawsys.zc create mode 100644 tests/programs/recycle.zc create mode 100644 tests/programs/roots.zc create mode 100644 tests/programs/scrub.zc create mode 100644 tests/programs/share.zc create mode 100644 tests/programs/share_dbg.zc create mode 100644 tests/programs/sized.expected create mode 100644 tests/programs/sized.zc create mode 100644 tests/programs/sizedvec.expected create mode 100644 tests/programs/sizedvec.zc create mode 100644 tests/programs/static.zc create mode 100644 tests/programs/stdbuf.zc create mode 100644 tests/programs/stdmore.zc create mode 100644 tests/programs/strings.zc create mode 100644 tests/programs/structret.zc create mode 100644 tests/programs/structval.zc create mode 100644 tests/programs/sys.zc create mode 100644 tests/programs/tagenum.zc create mode 100644 tests/programs/tagenum_two.zc create mode 100644 tests/programs/tagenum_warn.zc create mode 100644 tests/programs/typedmethods.zc create mode 100644 tests/programs/typeoff.zc create mode 100644 tests/programs/typeok.zc create mode 100644 tests/programs/types.zc create mode 100644 tests/programs/uaf.zc create mode 100644 tests/programs/uaf_in.zc create mode 100644 tests/programs/umd.zc create mode 100644 tests/programs/undefined_call.zc create mode 100644 tests/programs/use_std.zc create mode 100644 tests/programs/wide.zc create mode 100644 tests/programs/zc0plain.zc create mode 100644 tests/programs/zc0typed.zc create mode 100755 tests/runerr.sh create mode 100755 tests/test.sh create mode 100644 tests/test_win.ps1 create mode 100644 tests/typeerrors/arg.zc create mode 100644 tests/typeerrors/arith.zc create mode 100644 tests/typeerrors/assign.zc create mode 100644 tests/typeerrors/call.zc create mode 100644 tests/typeerrors/deref.zc create mode 100644 tests/typeerrors/enum_int.zc create mode 100644 tests/typeerrors/enum_other.zc create mode 100644 tests/typeerrors/field.zc create mode 100644 tests/typeerrors/generic.zc create mode 100644 tests/typeerrors/notstruct.zc create mode 100644 tests/typeerrors/ret.zc create mode 100644 tests/typeerrors/voidret.zc create mode 100755 tinyvm.bat create mode 100755 tinyvm.sh diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..2ea4dfb --- /dev/null +++ b/.gitattributes @@ -0,0 +1,21 @@ +# shell, python and every source file stay lf so they run on linux, macos and wsl +* text=auto +*.sh text eol=lf +*.py text eol=lf +*.c text eol=lf +*.h text eol=lf +*.S text eol=lf +*.ld text eol=lf +*.zc text eol=lf +*.js text eol=lf +*.ts text eol=lf +*.json text eol=lf +*.md text eol=lf +# cmd.exe needs crlf for labels and goto to be reliable +*.bat text eol=crlf +*.cmd text eol=crlf +*.ps1 text eol=crlf +# built binaries +*.exe binary +*.html text eol=lf +*.wasm binary diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6c1dc32 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +# build output +bin/ +*.o +*.obj +*.exe +!editors/vscode/bin/*.exe + +# built programs +/bin/ +/build/ +/src/web/zc.wasm diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..6c42e62 --- /dev/null +++ b/Makefile @@ -0,0 +1,113 @@ +# tinyvm and zc. `make` builds the tools, the interpreter, the microcore and the +# language server into bin/. nothing built is kept in the tree. +# +# make everything below that is a program +# make test the whole suite (tests/test.sh) +# make check the minimal suite, for iterating (tests/test.sh -q) +# make test JOBS=4 the same, four sections at a time +# make genome rebuild src/core/genome.h from src/ and check the fixpoint +# make lsp bin/zclsp, the language server +# make web src/web/zc.wasm (needs clang with the wasm32 target) +# make js src/js/genome.json, for the javascript vm +# make run PROG=examples/hello.zc +# compile one program into a run-only image on a memory +# map sized to it (src/tools/automap.sh) -> build/run/zymr +# make clean remove bin/ and build/ and the web build +# +# CC picks the compiler (cc by default; clang works too). + +CC ?= cc +CFLAGS ?= -O2 -w -fwrapv +ifeq ($(OS),Windows_NT) + EXE := .exe + IO := src/core/io_win.c + PORT := src/micro/port_win.c +else + EXE := + IO := src/core/io_linux.c + PORT := src/micro/port_linux.c +endif + +BIN := bin +B := build + +TOOLS := $(BIN)/zc0$(EXE) $(BIN)/mkgenome$(EXE) $(BIN)/genmap$(EXE) +PROGS := $(BIN)/zy$(EXE) $(BIN)/zym$(EXE) $(BIN)/zclsp$(EXE) + +all: $(TOOLS) $(PROGS) + +$(BIN) $(B): + mkdir -p $@ + +$(BIN)/zc0$(EXE): src/tools/zc0.c src/core/memmap.h src/core/fops.h src/core/bops.h | $(BIN) + $(CC) -O1 -w -fwrapv src/tools/zc0.c -Isrc/core -o $@ +$(BIN)/mkgenome$(EXE): src/tools/mkgenome.c | $(BIN) + $(CC) -O1 -w -fwrapv src/tools/mkgenome.c -Isrc/core -o $@ +$(BIN)/genmap$(EXE): src/tools/genmap.c | $(BIN) + $(CC) $(CFLAGS) src/tools/genmap.c -Isrc/core -o $@ + +# the interpreter: the vm core with the compiler genome inside +$(BIN)/zy$(EXE): src/core/core.c $(IO) src/core/genome.h src/core/memmap.h src/core/fops.h src/core/bops.h src/core/stack.h | $(BIN) + $(CC) $(CFLAGS) -DJTYPE=int src/core/core.c $(IO) -Isrc/core -o $@ +# the portable microcore +$(BIN)/zym$(EXE): src/micro/micro.c src/micro/micro_port.h $(PORT) src/core/genome.h src/core/memmap.h src/core/fops.h src/core/bops.h src/core/stack.h | $(BIN) + $(CC) $(CFLAGS) src/micro/micro.c $(PORT) -Isrc/core -Isrc/micro -o $@ + +# the language server is the compiler in check mode plus src/lsp/server.zc, on a map +# sized to it +lsp: $(BIN)/zclsp$(EXE) +$(BIN)/zclsp$(EXE): src/core/core.c src/core/io_linux.c src/core/fops.h src/core/bops.h src/core/stack.h src/core/memmap.h src/runtime/rt.zc src/compiler/compiler.zc src/lsp/server.zc src/tools/zc0.c src/tools/genmap.c src/tools/automap.sh src/tools/mklsp.sh | $(BIN) + CC="$(CC)" bash src/tools/mklsp.sh $(B)/lsp + cp $(B)/lsp/zclsp$(EXE) $@ + +# bootstrap: zc0 compiles the compiler to g0, g0 compiles it to g1, g1 to g2. +# g1 and g2 must be identical (the fixpoint), and g1 becomes src/core/genome.h. +genome: $(BIN)/zc0$(EXE) $(BIN)/mkgenome$(EXE) | $(B) + mkdir -p $(B)/s0 $(B)/s1 + cp src/core/core.c $(IO) src/core/memmap.h src/core/fops.h src/core/bops.h src/core/stack.h $(B)/s0/ + cp src/core/core.c $(IO) src/core/memmap.h src/core/fops.h src/core/bops.h src/core/stack.h $(B)/s1/ + cat src/runtime/rt.zc src/compiler/compiler.zc | $(BIN)/zc0$(EXE) > $(B)/s0/genome.h + $(CC) $(CFLAGS) -DJTYPE=int $(B)/s0/core.c $(B)/s0/$(notdir $(IO)) -o $(B)/zy0$(EXE) + (cat src/tools/build.zc; printf '`'; cat src/runtime/rt.zc src/compiler/compiler.zc) | $(B)/zy0$(EXE) > $(B)/g1.bin + $(BIN)/mkgenome$(EXE) < $(B)/g1.bin > $(B)/s1/genome.h + $(CC) $(CFLAGS) -DJTYPE=int $(B)/s1/core.c $(B)/s1/$(notdir $(IO)) -o $(B)/zy1$(EXE) + (cat src/tools/build.zc; printf '`'; cat src/runtime/rt.zc src/compiler/compiler.zc) | $(B)/zy1$(EXE) > $(B)/g2.bin + cmp $(B)/g1.bin $(B)/g2.bin + cp $(B)/s1/genome.h src/core/genome.h + @echo "fixpoint: $$(( $$(wc -c < $(B)/g1.bin) / 8 )) words, src/core/genome.h updated" + +js: src/js/genome.json src/js/layout.ts src/js/layout.js src/js/tinyvm.js +src/js/genome.json: src/core/genome.h src/core/memmap.h src/js/extract_genome.js src/js/mapvalues.js + node src/js/extract_genome.js +src/js/layout.ts: src/js/genome.json + @test -f $@ || node src/js/extract_genome.js +src/js/layout.js: src/js/layout.ts + @test -f $@ || node src/js/extract_genome.js +src/js/tinyvm.js: src/js/tinyvm.ts src/js/layout.ts + tsc src/js/tinyvm.ts --target es2020 --lib es2020,dom --module commonjs --outDir src/js + +web: src/web/zc.wasm +src/web/zc.wasm: src/micro/micro.c src/micro/micro_port.h src/web/port_wasm.c src/web/build.sh src/core/genome.h src/core/memmap.h src/core/fops.h src/core/bops.h src/core/stack.h + bash src/web/build.sh $@ + +# one program as a run-only image: the runtime, the program, and a map exactly +# big enough for them +run: | $(B) + @test -n "$(PROG)" || { echo "make run PROG=examples/hello.zc"; exit 2; } + bash src/tools/automap.sh run $(B)/run src/runtime/run.zc $(PROG) + cp src/micro/micro.c src/micro/micro_port.h $(PORT) src/core/fops.h src/core/bops.h src/core/stack.h $(B)/run/ + $(CC) $(CFLAGS) $(B)/run/micro.c $(B)/run/$(notdir $(PORT)) -I$(B)/run -o $(B)/run/zymr$(EXE) + @echo "built $(B)/run/zymr$(EXE)" + +# JOBS sets how many sections run at once: make test JOBS=4 +test: + bash tests/test.sh -j $(or $(JOBS),1) + +# the minimal suite: bootstrap, fixpoint, interpreter and lsp checks +check: + bash tests/test.sh -q -j $(or $(JOBS),2) + +clean: + rm -rf $(BIN) $(B) src/web/zc.wasm + +.PHONY: all lsp genome js web run test check clean diff --git a/README.md b/README.md new file mode 100644 index 0000000..711247b --- /dev/null +++ b/README.md @@ -0,0 +1,548 @@ +# tinyvm zygote + +a tiny stack cpu that started as a c89 code golf entry and ended up as a self-hosting runtime. a small native core runs vm code: jit-compiled to arm64, compiled through llvm on other hosts if you build with it, or interpreted. the genome it boots is a compiler for zc, a c-like language that keeps the forth habits. the compiler lives in vm memory next to your program, and your program can call into it, patch its tables, add syntax to it and compile new code while it runs. heap objects carry an ownership tag (u, m or d) that a static analyzer inside the compiler checks, with deterministic frees at scope ends and a mark-sweep collector underneath. types are a compiler primitive, with structs, generics, reflection and an optional typechecker. a program can also wire up to 512 custom opcodes into the cpu (virtual enhancements), which the jits inline as native code. and code marked `o3` skips the stack machine: the core compiles it as a whole region, with its variables in registers. + +this folder has the latest code. `docs/LEARN.md` is a tour of the language. `docs/history/` is the scrapbook: notes for every stage, plus frozen code from the stages that still have it. + +## files + +`src/core/core.c` is the native core. it contains the arm64 jit, the llvm backend (behind `-DZYLLVM`), the portable interpreter, the enhancement unit (recipe parser, vm-code lifter, optimizer and lowering), and the o3 region compiler. `src/runtime/rt.zc` provides i/o, native division enhancements, the heap, and the collector; `src/compiler/compiler.zc` is the compiler. both are written in zc and begin with an overview. together they make up the genome. `src/tools/zc0.c` is stage 0 of the bootstrap, a c compiler for the subset of zc used by `src/compiler/compiler.zc`. `src/tools/build.zc` has the resident compiler compile its own source into a fresh image, and `src/tools/mkgenome.c` turns that image into the generated, checked-in `src/core/genome.h`. `examples/` contains small programs (hello, factorial, echo, self-patching code, an owning list, a repl, a typed program, and an enhancement demo) and larger proofs of concept: + +- `mandel.zc`: the mandelbrot set in 16.16 fixed point, with the fixed-point multiply wired in as a custom opcode. +- `bf.zc`: a brainfuck compiler. it turns brainfuck into zc source, has the resident compiler compile it, and runs it (native code on the jit). it takes a program after a backtick, or runs hello world. +- `dump.zc`: prints any heap struct, nested ones included, with no printing code, by walking its type through reflection. +- `cli2.zc`: reads the command line and the environment, then captures its own output into a string. +- `tree.zc`: a binary search tree whose nodes own their children. there's no `free` anywhere, and the live object count drops back to 0 when the root's block ends. +- `lang.zc`: grows the language while it compiles: a `|>` pipe operator (one table entry, since on a stack machine it's just `call`), `unless`, an `assert` that knows its source line, and `times n { }`. +- `staged.zc`: writes a polynomial as source with its coefficients baked in, compiles it while running, and finds it by name. +- `quine.zc`: prints its own source. +- `life.zc`: conway's life on a typed 2d board, with a glider wrapping around a torus. +- `primes.zc`: an o3 sieve over a fixed array, the primes gathered into a growable one, then twin primes and the widest gap. + +`tests/` contains feature tests, `tests/errors/` contains programs the lifetime analyzer must reject, `tests/typeerrors/` contains programs the typechecker must reject, and `tests/langerrors/` covers o3 and array misuse. each error test begins with its expected message; `tests/runerr.sh` runs a test folder. `tests/test.sh` bootstraps the compiler and runs the suite on the interpreter, microcore, and available jit and llvm backends, then repeats a subset on smaller maps. `tests/bench/bench.sh` compares o3 with plain code on each backend. `src/lib/std.zc` is a library included by programs, not part of the genome. `docs/LEARN.md` is a language tour, and `docs/history/` keeps earlier versions. `src/micro/micro.c` is a portable interpreter for the same genome; `src/micro/micro_port.h` defines its host interface, with `src/micro/port_linux.c` as the hosted port and `src/micro/port_stub.c` as a bare-metal starting point. `src/baremetal/` boots the microcore without an operating system under `qemu-system-riscv64` and `qemu-system-aarch64`. `src/core/memmap.h` defines the default map and initial reservations. `src/tools/genmap.c` writes smaller maps and matching source files; `src/runtime/run.zc` is the runtime-only boot stub. + +## build and run + +run these commands from the repository root. `make` writes executables to `bin/` and temporary builds to `build/`: + +```sh +make +bin/zy < examples/hello.zc +make test JOBS=4 +make genome +make web +make js +make run PROG=examples/hello.zc +``` + +`make genome` bootstraps the compiler, compares two self-compiled images, and updates `src/core/genome.h` only after they agree. rebuild the native, javascript, and web runtimes after changing the genome. `make check` runs the smaller suite while iterating. + +`make js` regenerates the JavaScript bundle and needs Node.js and the TypeScript compiler (`tsc`). running the committed `src/js/tinyvm.js` directly needs Node.js only. + +### interactive runners + +`./tinyvm.sh` opens the linux menu; `tinyvm.bat` opens the windows menu. each runner builds, runs, checks source, starts the repl, and runs tests. direct commands use the same entry point: + +```sh +./tinyvm.sh build +./tinyvm.sh run examples/hello.zc +./tinyvm.sh run-micro examples/hello.zc +./tinyvm.sh lsp examples/opt_test.zc +./tinyvm.sh js examples/primes.zc +./tinyvm.sh test +``` + +```cmd +tinyvm.bat build +tinyvm.bat run examples\hello.zc +tinyvm.bat lsp examples\opt_test.zc +tinyvm.bat test +``` + +windows needs clang or gcc and powershell for the tests. the batch file is the single windows entry point; its menu also offers individual build targets and cleanup. + +### native backends + +on arm64, `src/core/core.c` uses its jit by default. other hosts use the interpreter. `-DZYINTERP` forces interpretation; `-DZYLLVM` enables the llvm backend when its headers and libraries are available. direct builds from the root are: + +```sh +mkdir -p bin +cc -O2 -fwrapv -DJTYPE=int src/core/core.c src/core/io_linux.c -Isrc/core -o bin/zy +cc -O2 -fwrapv src/micro/micro.c src/micro/port_linux.c -Isrc/core -Isrc/micro -o bin/zym +cc -O2 -fwrapv -DJTYPE=int -DZYLLVM $(llvm-config --cflags) src/core/core.c src/core/io_linux.c -Isrc/core -o bin/zy-llvm $(llvm-config --ldflags --libs) +``` + +`J` must match the host port: the supplied ports use `int`. a source unit ends at eof or a backtick outside a comment or literal; bytes after that backtick remain available to the program. + +`ZYTRACE=1` logs translations and installations to stderr. with tracing, `ZYO3DUMP=1` also prints region micro-ops. on llvm builds, `ZYHOT=n` sets the compilation threshold, `ZYPASSES` selects the pass pipeline, and `ZYNOLLVM=1` disables llvm compilation. + +### language server + +`src/lsp/server.zc` is a language server written in zc. it doesn't have a parser of its own: it is the real compiler (`src/compiler/compiler.zc`) running in check mode, with a json-rpc server on top. so the errors are the compiler's errors, with the same words, and a program that grows the language with parse words (`immediate fn`, `add_binop`, `quote`) is checked and highlighted with the syntax it added, because those words run during the check exactly as they do in a build. + +hover shows what a name is, from the text of its declaration. a top-level function, global or struct shows its declaration line, and a method or static function of a struct shows its indented line the same way. the comment lines directly above a declaration are its doc, whether or not it has a signature, so a declaration with no doc still shows its signature. a parameter shows its declaration, and a `let` local shows the type the compiler gave it (recorded during the check, by byte offset). an untyped local, parameter or function return shows the type the compiler infers for it. to get that, the server checks the document once more with the untyped names left open and reads back the type each one ends up with. the result is kept until the document changes, so the first hover after an edit takes longer than the rest. a built-in function shows the signature stored in the genome, and a keyword such as `micro` or `unchecked` shows a short line on its form and what it does, from one table in `src/lsp/server.zc`. calls that come through an `include` are not followed yet, so a helper like `Text.itoa` from `src/lib/std.zc` shows nothing from the server. + +check mode is the compiler with errors that write records instead of printing. it doesn't halt: it keeps going after an error, up to 20 of them, so a document with several mistakes shows all of them. each record carries its line and the token's place in the source, and the server turns that into a utf-16 range. check mode also notices things that aren't errors, and reports them as hints (see below). + +- **diagnostics** on open, change and save, with an id echoed on every reply, `MethodNotFound` for unknown requests and `-32002` before `initialize`. +- **semantic tokens** (`textDocument/semanticTokens/full`) in 12 types: keyword, type, function, variable, string, number, operator, comment, parameter, property, macro, enumMember. what a name is comes from the compiler's own symbol tables and the names it saw declared, not from guessing. the check records each name it resolves and where, and a name resolves the way the compiler resolves it, so locals and parameters are looked up ahead of globals, and built-ins come last. a local named `cap` is a variable wherever it is in scope, and the built-in `cap` only applies to uses outside that scope. +- **cli mode**: source on stdin prints one line per finding, `line:col: error: ...` or `line:col: hint: ...`, or `no errors`. + +the server runs on its own memory map (`genmap --preset lsp`), which has a bigger genome slot and a smaller frame stack than the default, and it keeps its documents in a store of its own, 4 chars to a word. + +```cmd +tinyvm.bat build lsp # bin\zclsp.exe +src/tools/mklsp.sh build/lsp # elsewhere: build/lsp/zclsp +tinyvm.bat lsp examples\opt_test.zc +``` + +`tests/drivers/lsp_test.js` drives the server over the protocol (unicode columns, a 50 kb document, state that must not leak between documents, parse words) and checks that every valid program in `examples` and `tests` comes back clean and every bad one in `tests/errors`, `typeerrors` and `langerrors` is rejected with the message in its first line. + +### vscode extension + +the `editors/vscode` directory contains the complete VS Code extension for ZC: + +- **syntax grammar**: full TextMate grammar (`syntaxes/zc.tmLanguage.json`) highlighting keywords, types, lifetime tags/recasts (`.u`, `.m`, `.d`, `u*`, `m*`, `d*`), literals, and operators. +- **language configuration**: bracket matching, indentation rules, auto-closing pairs, and comments (`//`, `/* */`). +- **language client**: a small node client (`client/extension.js`) with no packages. it speaks the protocol properly (byte lengths, `textDocument` wrappers, `didClose`), restarts the server if it exits and sends the open documents again. + +build a package with `bash src/tools/mkvsix.sh`, then use "extensions: install from vsix" in vscode. the bundled wasm server needs no native executable. `zc.lsp.serverPath` optionally selects a native server. see [the extension guide](editors/vscode/README.md) for settings and logs. + +### javascript & typescript runtime + +a complete, self-contained 64-bit TinyVM core implemented in JavaScript and TypeScript is provided in `src/js/`: + +- `src/js/tinyvm.ts` and `src/js/tinyvm.js`: Implements the 64-bit word machine on `BigInt64Array` with byte addressing (`Uint8Array` / `DataView`), dual stacks, all 22 base opcodes, byte/float opcodes (612–619), system calls, and custom virtual enhancement execution (`xrun`). +- `src/js/genome.json`: the compiler genome loaded into VM memory. `node src/js/extract_genome.js` rebuilds it from `src/core/genome.h`, and `tests/test.sh` checks it is current. + +run any `.zc` program in Node.js: +```cmd +node src/js/tinyvm.js examples/hello.zc +node src/js/tinyvm.js examples/fact.zc +node src/js/tinyvm.js examples/mandel.zc +# or via the Windows runner: +tinyvm.bat js examples/primes.zc +``` + +### web (webassembly) + +`src/web/` runs the compiler in a browser. `src/web/build.sh` compiles the microcore and genome to `src/web/zc.wasm` with clang's wasm32 target, without emscripten or libc. `src/web/port_wasm.c` provides the port and the 64-by-64-to-128-bit multiply used by the core. `src/web/zc.js` loads the module and exposes `machine.run(source)`, which returns the output produced by `zy < file.zc` from a fresh instance. `src/web/index.html` provides an editor, examples list, and output pane. serve the repository with `python3 -m http.server` and open `http://localhost:8000/src/web/index.html`. + +``` +bash src/web/build.sh +node tests/drivers/wasm_test.js +``` + +## zc, the language + +it reads like c. underneath every value is a 64 bit word and memory is word addressed, so `p + 1` is the next word whatever `p` points at, and strings are one char per word with a 0 at the end. types are optional: untyped code is typed `word`, which goes with anything. + +``` +const N = 10; +[N]int table; // a global array +int fib(int n) { if (n < 2) return n; return fib(n - 1) + fib(n - 2); } +fn main() { + for (int i = 0; i < N; i++) table[i] = fib(i); + let sq = { let t = table[5]; t * t }; // a block is an expression too, its last line is its value + let x = asm { 2 3 + }; // raw vm code, leaves one value + put_n(sq + x); put_c(10); + return 0; +} +``` + +declarations are c-style, type first: `int a = 1, b;`, `node* p = new(node);`, `[4]char buf;`, `point origin;`. `let` declares without a type: it infers pointers and function types from the initializer, uses a pointer alias for an in-place array or struct, and keeps numbers as plain words, and `let a[10];` is an untyped array. statements are declarations, `if`/`else`, `while`, `do`/`while`, `for`, `switch`, `break`, `continue`, `return`, `{ }` blocks, `asm { }` and expressions. `do` body `while (` cond `);` runs the body once before the test, and `continue` jumps to the test. `enum color { RED, GREEN = 5, BLUE };` makes global constants, and `color` is a type of its own in typed code (`typecheck on;`): a member takes the enum, a plain int does not become a `color`, and a `color` still becomes an `int`. untyped code treats the members as plain ints. each member is also reached as `color.BLUE`, in an expression or a `case` label. a member can carry fields too: `enum shape { circle { int r; }, rect { int w; int h; }, dot };` makes the constructors `shape.circle(3)` and `shape.rect(4, 5)`, which return an owned `shape*`, and `shape.dot()`. the tag is the word at offset 0 of the object. a `switch` over a `shape*` matches a member with `case circle(r):`, which binds its fields in that arm only. a switch on a tagged enum that leaves out a member and has no `default` gets a warning on its line (not an error). `(T)e` is a cast with the same meaning as `e as T`: the bits stay, the type changes. it has to be a simple type the parser can see in a few tokens, `int` or `*node`, not a nested array. `include "path";` reads a file and compiles it at that point, signatures first, so a library can live in its own file. `u`, `m` and `d` are ordinary names. a tag is `.u`, `.m` or `.d`, and `u*x` with no space before the star is still a recast. `switch (e) { case 1, 2: ... default: ... }` takes constant labels, and its cases don't fall through: each ends where the next begins, and `break` leaves early. operators are c's with c's precedence, including `c ? a : b`, plus `p.x` and `p->x` (the same thing: `.` goes through a pointer on its own), `e as T` to change a type without changing the bits, and the casts below. comparisons and `&& ||` give 0 or 1. `/`, `%` and `<<` compile to native enhancement opcodes. character literals, `0x` hex and the usual escapes are there. + +functions are declared like c, return type first: `int f(int x)`, `node*.u make()`, `.u grab()` (a tag on its own), `void log(char* s)`. `fn f(a, b)` is the untyped form, returning a word. a generic function lists its type parameters after its name, `T first(*list l)`, the same place a call gives explicit ones, `first(l)`. the return type can use them before they're declared: while it's read, an unknown name is taken as a type parameter, and the list after the name has to confirm it. function types are written the same way, `int(int, *char)`, and `fn(int)` for one that returns a word. the earlier `fn f(...) -> T` spelling is gone. + +arrays come in three kinds. `[n]T` has its size in the type and lives in place. `[]T` is a growable array: a small handle object that owns its items, made with `new([]T)` or `new([]T, n)`, indexed with a bounds check, and changed with `push(a, x)`, `pop(a)` and `resize(a, n)`. `len(a)` works on both (for `[n]T` it's the constant) and `cap(a)` gives the room before the next grow. `fixed []T` is a `[]T` that can't change size: `new(fixed []T, n)` needs the length, and `push`, `pop` and `resize` on it are compile errors. a `fixed []T` can't be passed where a `[]T` is expected, the other way round is fine, and a constant index outside a `[n]T` is caught at compile time. the items belong to the array, so freeing it frees them, and objects stored into a `[]*T` are its children like any container's. + +a unit is read whole and scanned for function signatures before it compiles, so calls can come before definitions, tags included, and prototypes are optional. `main` runs after the whole program has compiled. every function returns one word, 0 if it falls off the end. + +builtins from the runtime: `put_c`, `get_c`, `eof`, `put_s`, `put_n`, `halt`, `len_w`, `eq_w`, `copy_w`, `fill_w`, `new`, `new_t`, `new_raw`, `new_oz`, `free`, `recycle`, `arena_new`, `arena_alloc`, `arena_reset`, `gc`, `isobj`, `osize`, `typeof_obj`, `live_objects`, `install`, `zdiv`, `zmod`, `zshl`, `mem_set`, `memreport`, `checkptr`, `syscall`, `readfile`. `src/lib/std.zc` groups its helpers in structs of static functions, called as `Group.name(args)`, with typed signatures: `Text` (`atoi`, `itoa`, `cat`, `starts`), `Host` (`env`, `now`, `writefile`), `Bytes` (the same helpers for packed byte strings), `Arena` (`alloc`, `reset`), `Buf` (`length`, `capacity`, `get`, `set`) and `capture` (`start`, and `end` on its buffer). the helpers are typed like any function: a method declares its return type and parameters the same way (`int atoi(char* s)`, `static word start()`). the constructors `arena(n)` and `bnew(n)` stay plain functions, because an owned result has to carry its `.u` tag, and a method with a `.u` return is not supported yet. the globals `argc`, `argv`, `envc` and `envp` are set by `libinit`. + +the command line, the way c hands it to `main`: `argc` is an `int` and `argv` an `[]word` (a zc string is one character per word, so there is no `string` type), and the same for `envc` and `envp`, whose entries are `NAME=value`. `argv[0]` is the program path, and `` when the source came from stdin. `libinit()` fills all four and the runtime calls it before `main`, if the program has one, so `main` keeps its `fn main()` shape and every example is unchanged: + +``` +include "src/lib/std.zc"; +fn main() { + for (int i = 0; i < argc; i++) { put_n(i); put_c(32); put_s(argv[i]); put_c(10); } + return 0; +} +``` + +`capture.start()` redirects everything the program writes into a string, and `buf.end()` stops it and hands the string back. output already redirects for the language server (`outmode`); this is the same thing turned into syntax, and `end()` is a method on a `capture` struct, which is how a plain word gets a method: + +``` +let buf = capture.start(); +put_s("hello world!"); // nothing on the terminal +buf.end(); +put_s(buf); // hello world! +``` + +## memory model: u, m and d + +`new(n)` or `new(T)` gives back a fresh object. bind it to a local and the local owns it, tagged **m**. tags live on local variables and parameters, the analyzer follows them through the code, and the compiler inserts `free` calls where owned objects die. a tag is written after the type, `*node.u p`, or alone, `.d x`. `u`, `m` and `d` are ordinary names; the tag is the dot. + +**m** means the object belongs to this block and has to be used in it. you can lend it to callees as often as you like, and when the block ends it's freed. an m object that's never used anywhere in its block is an error. + +**d** means the object is going down. the first time it's handed to a lower scope its life here is over: passing it to a `.d` parameter, or touching it inside an inner block, which then owns it as m and frees it when that block ends. using it afterwards is an error, and so is ending the block with a d object that never left. + +**u** means the object is going up. it has to leave this block for the one above, where it arrives as m. it leaves through `return`, as the value of a block expression, or by assignment to a variable of the directly enclosing block. the function's signature doesn't have to say `.u`: when its body returns a u object, the return is owned and the function is inferred to give one back. a call to a function whose body hasn't been read yet can't know that, so such a function has to be declared `.u` (like `*node.u make()` or just `.u make()`) before the call. letting it fall out of scope is an error, and so is throwing away a u result by just calling it. to discard one on purpose, recast the call with m: `m*make();` lets the unnamed object go. + +recasting is `u*x`, `d*x`, `m*x`, with no space before the star. a space is ordinary multiplication by a variable of that name. it only goes away from m: m can become u or d (and u and d can swap), but nothing goes back to m in the same scope. a declaration can carry a tag too, `.u r;`, and then whatever lands in it gets that tag. `m*` also works on an unnamed u result, a call, which is how such a result is thrown away; `u*` and `d*` need a name. + +parameters: an untagged parameter is m from the caller's side, a lend, and the callee sees a plain word. `.m` makes it a checked borrow: the callee can read and write it but can't free it, return it, recast it or store it anywhere. `.d` hands the object over, and it arrives as m. + +``` +int*.u box(int v) { int* b = new(int); *b = v; return u*b; } +fn add(list* l, int*.d item) { l.items[l.len] = d*item; l.len++; return 0; } +``` + +containers own what you store in them. `p[i] = c` or `p.f = c` needs `c` to carry the same tag as `p` (or be a fresh object), and from then on `c` follows `p`: if `p` is recast, so is `c`, if `p` is given away `c` goes with it, and when `p` is freed its children are freed too. a child can't be recast or given away on its own. storing into an object you only borrowed gives the child away: it's dead here afterwards, and the caller's object owns it. the runtime records the owner in the child's header, so freeing a container frees exactly the objects it owns. + +globals, plain pointers and arrays aren't tracked. storing an owned object there needs `d*`, and the mark-sweep collector takes it over. a fresh object can go straight in. + +a pointer computed from a tracked variable is tracked too. `p + k`, `p.field` and `p[i]` (when that field or element is a pointer, or when `p + k` is untyped pointer arithmetic on an owned object) stores a borrow of `p` in the local it lands in, the same child link a real child uses. using that local after `p` dies is an error. the pointer itself is not an owner: it can be copied, incremented and passed around, and it is not freed on its own. + +### memory debug + +off unless a program asks. `memdebug on;` at the top level turns on header checks and allocation sites for the rest of the file, and the runtime enables them before `main`. the same line inside a function does it from there on. `memdebug scrub;` also fills a freed payload with 57005 (`0xDEAD`). `memdebug off;` stops it. `mem_set(n)` is the same switch as a call: bit 1 checks and sites, bit 2 poison, bit 4 interior pinning. + +with checks on, every load and store through a heap address looks for the object header, walking back at most 64 words so `p + k` and a field still find it. a dead header stops the program: + +``` +zy: use after free from line 12 +zy: double free from line 12 +zy: bad heap pointer +``` + +the line is the `new` that made the block. a double free prints and returns; it does not smash the block. `memreport()` prints one line per recorded object that is still live, `leak: line 12 3 words`, and returns how many. the word count is the block's payload, which is a power-of-two chunk minus the 5 word header, not the size you asked for. the table holds 64 sites. freed blocks stay in it until the storage is reused, which is how a double free still knows the line. nothing here changes programs that never say `memdebug`. + +bit 4 makes the collector treat a word that falls inside a quarantined payload as a pointer, up to 64 words back to the header. an exact pointer already did this. turn it on when you want `p + k` to keep the block. it can also pin a block that a stale integer happens to land in, so it stays off unless you set the bit. + +where paths join (after an `if`, a loop, a `switch` or `?:`) every variable has to be in the same state on every path that reaches the join, with one exception. a variable that became d, or was given away, on some path is dead afterwards, and the compiler frees it on the paths where it's still alive, on their way in. a loop's exit state is the join of its `break`s and its normal exit, so giving something away and breaking is fine. what a loop can't do is give something away on a path that comes back around to the top, since the next pass would use it dead. a u object doesn't get the dead treatment: it still has to leave. + +### the analyzer + +it runs inside the compiler on every function, including code compiled at runtime by `eval` and friends, and reports each violation with a line number and the variable's name. the compiler keeps going after errors, up to 20: a syntax error skips to the next `;` or `}` (or the next top-level declaration), and analyzer and type errors just get reported, since the parse is still in step. a unit with errors doesn't run. `tests/errors/` has one small program per rule. to turn it off: `unchecked fn f() { }` for one function, `unchecked { }` for a block, and a top-level `unchecked;` for the rest of the unit (a parse word can also set `check_on = 0`). unchecked code doesn't track anything, so what it allocates belongs to the collector, and checked variables can't be touched from inside an unchecked block. + +## types + +types are a compiler primitive. the compiler keeps them as records in a type area at 0x80000 (65536 words), hash-consed so the same type is always the same pointer. they stay in memory after compiling, so programs and parse words can look at them. + +``` +struct point { int x, y; }; +struct list { int len; *T items; }; +type num = int; +T first(list* l) { return l.items[0]; } +list*.u mk(int cap) { list* l = new(list); l.items = new(T, cap); return u*l; } +... +list* ps = mk(8); // explicit type arguments, or inferred from the arguments +``` + +the kinds are `word`, `int`, `char`, `bool`, `void`, pointers `T*`, arrays `[n]T`, `[]T` and `fixed []T`, function types `int(int, char*)`, structs, generic structs and generic functions. structs and arrays live in place (in a frame, a global or inside another struct), and their value is their address, so they're passed around by pointer. `sizeof` counts words. a generic function is compiled once for each set of type arguments it is called with, so `T` can be a number, a pointer or a struct of any size, `sizeof(T)` and `new(T)` mean the real type, and a struct can take several parameters (`struct record { K key; V val; };`). indexing scales by the element's size, while plain pointer arithmetic stays in words, like b. `new(T)` and `new(T, n)` stamp the type into the object's header. + +### pointer types + +a pointer type places its star after the type, like c: `int*`, `point*`, `vec*`, `vec*`, `point**`. a tag follows the stars (`vec*.u`, `int*.d x`). there is no prefix form: `*int` as a type is an error, and a star in front of an expression is only ever a dereference. a pointer to an array or to a function type is spelled with a `type` alias (`type row = [3]int;` then `row*`), because `[3]int*` reads as an array of pointers. `tprint`, and so error messages and reflection, print the same form. + +### methods and closures + +a struct can hold functions. they get a hidden first parameter called `this`, and `p.name(args)` calls them with `p` in that place: + +``` +struct point { + let x, y; + fn dist(other) { return (this.x - other.x) * (this.x - other.x) + (this.y - other.y) * (this.y - other.y); } +} +... +p.dist(q); +``` + +underneath, a method is an ordinary function called `point.dist` whose first parameter is `point* this`, compiled once the struct is complete, so a method can call the ones declared after it. a field with the same name wins over a method. a method of a generic struct is a generic function over the struct's parameters, compiled for each set of arguments it is called with (the receiver says which), so `this.items[i]` and `sizeof(K)` inside it are exact. + +`fn(a, b) { ... }` in an expression is a closure. the value is a heap block: word 0 is the body's entry, and the captured variables start at word 3. the vm only runs code in the code area, so the block is never executed. a call through a value checks whether the callee is at or above `HEAP_LO`. a plain function is called as it is. a closure is called with the block plus 3 as its environment and jumps to the entry in word 0. the capture is by value, taken when the closure is made. a variable from further out is captured through the closure around it. a closure's own copy is its state, so a counter inside it keeps its value between calls. the block is an owned object: bind it to a name before passing it to a parameter that isn't `.d`, and return it from a function whose result is `.u`. `tests/programs/closure.zc` and `examples/learn/25-closures.zc` cover these. + +### hints + +check mode (the language server) reports things that are legal but probably not what you meant. they don't change what the compiler emits, and a normal build doesn't compute them. + +- a variable that is never read +- a `let` with a constant initializer that is never assigned again: make it a `const` +- multiplying by a power of two: write a shift +- a number of 100 or more with no name (powers of two, masks and character literals are left alone) +- `new` inside a loop: it allocates on every pass +- a `new(n)` of 64 words or less that never leaves its function: a stack buffer, `let buf[n]`, avoids the heap +- an owned object last used four or more lines above the end of its block + +`examples/opt_test.zc` illustrates these cases and still runs. + +### reflection + +`typeof(e)` and `typeof(T)` give a type record and `sizeof(...)` its size. both work on any expression, which is compiled dry and never runs. at runtime: `tkind(t)` (one of the `TY_` constants), `tsize`, `tname`, `tlen`, `tnfields`, `tfname(t, i)`, `tftype`, `tfoff`, `tret`, `tnparams`, `tparam`, `tparamtag` (the u/m/d tag of a parameter), `tretu`, `tnargs` and `targ` for generic instances, and `tprint(t)`. `sigof(f)` gives the type of a function by address (untyped ones get a signature built from their arity and tags), `sigofs("name")` by name, and `typeof_obj(p)` reads the type stamped on a heap object (0 when the header has no type yet: `new(n)` or `new_raw`). + +### the typechecker + +off by default. `typecheck on;` turns it on for what follows and `typecheck off;` turns it back off (a parse word can flip `tc_on`). it checks initializers, assignments, arguments (unifying generic parameters), return values, arithmetic, comparisons, dereferencing, indexing and calls. `word` matches anything, numbers mix with each other, a literal 0 is a null pointer, and arrays decay to pointers. it stops at the first error, printing what it expected and what it got. `tests/typeerrors/` has one per rule. some things are checked whatever the setting, because the compiler needs them to generate code: field names, and field access on something that isn't a struct. + +### sized integers and f32 + +`i8 u8 i16 u16 i32 u32` (and `byte`, which is `u8`) are integer types that keep their size. a variable, parameter, field or return value of one is still a word, but it is cut to size every time it is stored: unsigned values are masked, signed ones are masked and then sign extended, so `u8 a = 250; a = a + 10;` leaves 4, and `i8 s = 100; s = s + 100;` leaves -56. arrays and pointers of them are packed, which is where the memory goes: a `[100]u8` takes 13 words and `new(u16, n)` takes n/4 words, rounded up. indexing a packed array or pointer reads and writes the bytes with the byte ops (612 to 617), so the same code runs on every engine and o3 lifts it like any other load. `f32` is single precision: a variable holds the value already rounded to a float (arithmetic is done in double, which gives the correctly rounded float result for `+ - * /`), and an array or pointer of f32 is packed two to a word. `f64` is a second name for `float`. the rounding is the `f2s` and `s2f` micro-ops, one instruction on arm64. + +``` +u8 gbuf[100]; +struct pix { u8 r, g, b, a; i16 dx; }; +f32* p = new(f32, 1000); // 500 words +p[3] = 0.1; // stored as the nearest float +``` + +two limits. `sizeof` counts words, so `sizeof(gbuf)` is 13 and a struct of four u8s has `sizeof` 1: the fields of a non-generic struct are packed into bytes, and a field read or write goes through the same byte ops as an array element. growable `[]u8`, `[]i16`, `[]f32` and the rest work too (the handle's element size is stored as minus the byte size, and `push`, `pop`, `resize`, indexing and `for` use byte accesses for them), in o3 as well. the address of one element of a packed array cannot be taken, because it is a byte inside a word, and that is on purpose: pointer arithmetic moves in words, so such an address would not mean the same thing as `&a[i]` does elsewhere. take the array's own address and index it. `f32` is a type for `extern` and `cfunc` signatures: it goes to c as a 32-bit float and comes back rounded. a zc struct whose fields are all one word (`int`, `float`, pointers) can be passed by value to an `extern`: `extern long f(struct point p);`. it goes to c as its words, classed per eightbyte by the sysv x86-64 rules (a float word in an sse register, any other in an integer register, the whole struct on the stack when it does not fit, and a struct over 16 bytes always on the stack). arm64 (aapcs64) follows the same idea: a homogeneous float struct of up to four words goes in the v registers, a struct of two words or less in the x registers, and a bigger one is passed by reference to a copy. this is checked on x86-64 and on arm64 under qemu-user, on the interpreter, in o3 code, and on the llvm backend and jit (`tests/programs/structval.zc`). a struct can also be returned: `extern struct point mk(long x);` gives a new `point*` owned by the caller (the stub makes it with `new` and the native call fills it in). the return is at most 7 words, and its floats are classed like the arguments'. returns are checked on x86-64 and on arm64 under qemu-user, on the interpreter, and on llvm (`tests/programs/structret.zc`). apple arm64 is not checked. + +## virtual enhancements + +the cpu has room for 512 custom opcodes (22 to 533), and opcode 21 (`%` in asm) installs one at runtime. it's the closest a soft cpu gets to rewiring itself. an enhancement takes up to 8 inputs off the data stack and leaves up to 8 outputs, and it's written one of two ways: + +``` +enhance clamp(x, lo, hi) micro "x = max x lo; x = min x hi"; +enhance 2 divmod(a, b) micro "r2 = sdiv a b; r1 = srem a b; r0 = r2"; // the number is how many outputs +enhance cube(x) vm { 0 $ 0 $ * * }; +enhance noisy(x) vm { 0 $ . } else soft_noisy; // can't be lifted (it does i/o), so calls use the fallback +``` + +a micro recipe works on registers `r0`..`r15`. inputs arrive in those registers, also under their parameter names, and outputs leave from `r0` upward. the micro-ops are `li mov add sub mul sdiv udiv srem urem and or xor shl shr sar neg not lt ltu eq ne min max minu maxu mulhs mulhu clz abs sel ld st`, followed by the bit operations `ctz popcnt bswap rotl rotr` and the single-precision conversions `f2s s2f` (rounding a double's bits to 32-bit float and back exactly). float operations and byte loads and stores are available to enhancements on the full cores. `src/runtime/rt.zc` defines callable enhancements for `clz ctz popcnt bswap rotl rotr mulhi umulhi f2s s2f`. they compile to one instruction on arm64 (popcnt uses the vector unit), an llvm intrinsic on the llvm backend, or plain c in the interpreter. the optimizer folds them when their inputs are constant. a `vm` body is straight-line vm code lifted by symbolic execution. both recipe and vm enhancements become ssa; the core optimizer folds constants, simplifies algebra, removes common subexpressions and dead code, and lowers constant division to multiply-high sequences (`ZYTRACE=1` prints the result). the arm64 jit allocates registers and inlines each use; llvm inlines the ir; the interpreter runs the optimized list. if an operation does not fit in registers, the jit calls the interpreter for that operation. when literals feed the last inputs of an enhancement (`x / 7` is `x 7 idiv`), the arm64 jit specializes that use: it substitutes the literals, optimizes again, and turns `x / 7` into multiply-high and shifts instead of division. + +an enhancement is called like a function when it has one output, or by name from asm. `enhance` installs it at compile time, so the wiring is available before `main` runs and parse words can use it immediately. an image installs its enhancements from its symbol table when it boots. that's how `/`, `%`, and `<<` work: `src/runtime/rt.zc` declares `idiv`, `imod`, and `ishl` as one-instruction recipes. `install(desc)` is the raw interface; its descriptor format is documented in `src/core/core.c`. stores performed by an enhancement do not invalidate compiled code, so use enhancements for data operations. + +## o3 + +`o3` marks code for the optimizing tier. it goes on a function, a block or a single variable: + +``` +o3 int sum(fixed []int a) { int s = 0; for (int i = 0; i < len(a); i++) s += a[i]; return s; } +fn main() { + o3 int hits = 0; // one o3 variable + o3 { for (int i = 0; i < 1000; i++) if (i % 7 == 3) hits++; } + ... +} +``` + +what it gives up, as asked: variables declared in o3 code must be typed (`let` and untyped parameters are errors), they have no address (`&x` and naming one from `asm` are errors), an untyped `new(n)` in o3 code is `new_oz` (no type stamp, and the words asked for are zero), and an o3 function skips the frame stack check in its prologue. typed `new(T)` and `new(T, n)` stamp the header and zero the payload, in o3 the same as outside it. what it gets: the compiler hands each o3 function body and each o3 block to the core as a region (install kind 2: the range of vm words plus its typed scalar variables' frame slots, each with a register), and the core compiles the region right away. the cpu has 7 registers for variables. a variable takes the one most recently freed by a scope that closed, or the next unused one, so loop counters in sibling loops share a register, and a variable declared when all 7 are busy stays in memory. a register shared by several slots gets written back to all of them when the region exits, and they never hold different live values, since their scopes don't overlap. + +inside the core, a region is cut into basic blocks at jump targets, branch fallthroughs and call return points. an empty jump is then folded into its target, a block with one predecessor is pasted onto that predecessor, and a straight loop is rotated so one iteration is a single block that branches to itself. a call, a return, a value left on the stack, or a second branch stays where it is, and an entry is never pasted over. each block is lifted to ssa the way enhancements are: the vm stack becomes values, and only what's still on it at the end of the block is written to memory. the frame pointer and the promoted slots become variables that live in registers across the whole region, so `i++` on an o3 int is one add. enhancements are spliced into the block, so `/`, `%` and `<<` become one instruction or a magic-number sequence. a call to `ld8`, `st8`, `ld16`, `st16`, `ld32` or `st32` from o3 code emits opcodes 612..617, and the region lifts those instead of calling out. every block then goes through the same optimizer (folding, algebra, cse, dce, division by constants). in o3 code `a[i]` on a `[]T` is compiled inline with its bounds check (`outside(i, len)`, one enhancement), and `len(a)` is a load. a `fixed []T` in an o3 variable does better: its items pointer and length can't change, so the compiler gives it two hidden o3 variables holding them. they're loaded when the region starts and whenever the array variable is assigned, so indexing is a compare against a register and an indexed load, with no loads through the handle. + +the backends: on arm64 the region becomes one piece of native code. variables sit in w0..w7, block values in w8..w14 and w26..w28, constants are folded into instructions where they fit, loads from a fixed address are one `ldr`, and blocks branch to each other directly with no dispatch and no stack checks. on the llvm build the region becomes one function, with the variables in allocas that mem2reg turns into ssa across blocks, and it goes through llvm's O3 pipeline. on the plain interpreter, a small region interpreter runs the optimized blocks with the variables in locals. + +calls, returns, jumps out of the region, i/o, `install`, a store to the frame pointer and anything else a block can't lift all leave the region: the variables are written back to the frame first, so outside code sees ordinary memory. a call's return point is an entry, which loads them again. stores inside a region still check the coverage map, so writing to compiled code works, and a store into the region's own code drops it (it lifts again at its next entry). to keep variable addresses from waiting on the stack across a branch, assignment to an o3 variable in o3 code evaluates the value first and the address after. the lifter refuses a block that would leave such an address on the stack, rather than miscompile it. single o3 variables outside o3 code are reached through `lfa(k)`, an enhancement for `fp + k`, which the arm64 jit specializes into a load and an add. + +`bench.sh` runs four kernels from `tests/programs/bench.zc` with and without `o3`. milliseconds per run, with start-up and compile time removed by timing 1 run against enough runs to take more than a second (arm64 is under qemu on an x86 host, so only its ratios mean much, and the machine was noisy enough that the small numbers move by 20% between runs): + +``` +backend kernel plain ms o3 ms speedup +interp collatz 324.90 240.61 1.4x +interp sieve 47.08 21.71 2.2x +interp digits 160.89 100.82 1.6x +interp fib 3.48 4.09 0.8x +llvm collatz 47.03 3.34 14.1x +llvm sieve 10.77 0.28 38.9x +llvm digits 18.93 1.45 13.1x +llvm fib 0.87 1.01 0.9x +arm64 (qemu) collatz 323.89 13.04 24.8x +arm64 (qemu) sieve 56.84 1.74 32.6x +arm64 (qemu) digits 147.06 4.17 35.3x +arm64 (qemu) fib 5.19 5.71 0.9x +``` + +fib gains nothing and can lose a little, since every call leaves the region through the dispatcher and comes back in through an entry stub, which costs about what the body saves. `tests/bench/run.sh` times a word sieve and a binary tree against the same programs in C; the machine, the medians, and the gaps are in `docs/history/35-bench`. + +### leaving o3: fattening + +going into o3 is free: o3 code reads normal objects the same way and ignores their metadata. typed `new(T)` stamps the type into the header and zeroes the payload, so a node is born with the type it will keep. a child store in o3 code writes `p[-2] = owner` in the region when memdebug is off (a shared child keeps the owner it has, and a null store does not land) and does not call `own`. it also skips `fatten` when that value was just allocated, or returned from an o3 function whose type has a pointer. `new(n)` with no type inside o3 calls `new_oz`: the type word stays -(n + 1), and the words asked for are zero. `new_raw` still leaves those words uninitialized and zeroes only the slack past them. + +`fatten` runs where o3 data reaches code that isn't o3, using the static type known there: storing a pointer into a normal local or a global, passing one to a function that isn't o3 (`free` and `recycle` excepted), the call site where ordinary code calls an o3 function whose result has pointers, `push` into a growable array, and, at the end of an o3 block, whatever the o3 variables declared outside it hold. a call or a return from one o3 function to another does not fatten, does not call `own`, and does not run the callee's prologue: the caller enters the callee's region directly. `fatten(p, t)` (in the compiler, so it can read type records) stamps the type, as `[0]T` when more than one element was asked for, zeroes the slack, and then follows every typed pointer, field, array element and growable array element inside. an object whose header already holds a type stops the walk, and a depth limit of 32 stops cycles through growable arrays. untyped words carry no hint, so pointers kept in `word` variables stay raw until typed code sees them. + +## the llvm backend + +a tiered jit for hosts the arm64 backend doesn't cover. the interpreter counts how often each block start gets reached, and a hot block is translated to llvm ir using the same block rules as the arm64 jit: it ends at call, jump, ret or halt, keeps going past a jz, and literal targets are fused. the ir goes through the O2 pipeline and gets compiled by orc's lljit. stores into compiled words drop the blocks covering them, like on arm64. it pays off for long-running code: a loop-and-recursion benchmark runs about 3.3x faster than the interpreter. short bursty work like the self-compile is slower, since compile time dominates there. `ZYHOT=1` compiles every block, which is a good stress test, and the test suite runs that way too. + +## the heap + +**constant-size allocations.** when a `new` has a size the compiler knows (`new(T)` of a type, or `new(6)`), it works out the size class itself and calls `new_k(n, t, k)`, which pops a block straight off the class's free list and skips the class search. a typed `new` of a struct made only of numbers (no pointer fields, no `word`, no generics) is also freed through `recycle_leaf(p, t, k)` when its owner goes out of scope: it checks the type word and the class still match what the compiler assumed (the variable may have been reassigned), and if so pushes the block back on the head of its list with no walk over children. a loop that makes such an object each pass and frees it at the end of the block therefore gets the same block back every time: the pair costs a dozen steps, where the general path searched classes, split blocks and walked the object. memdebug, an empty list, or a block inside the cut-off type area use the general path, so the checks and the messages stay what they were. in the interpreter this cut a 200000-pass loop of `point* p = new(point)` from about 1.9 s to 0.8 s. + +the heap is a buddy allocator over [HEAP_LO, top), where top starts at HEAP_HI and moves up. when no block is big enough, `new_raw` asks `heap_grow` to append a piece (a few times the failed request, at least 0x40000 words, capped by MW). the new range enters the same free lists, so it behaves like part of the heap from startup. growth stops when `heap_reset` cuts the allocator off below the type area; the language server also runs with this cut in place. with a host that answers `syscall(9, 0)`, the heap can grow until the host denies more memory. `tests/programs/growheap.zc` allocates well past the initial heap, frees it, then takes a block larger than any part of the original heap. when available, host operations 18, 19, and 20 fill, copy, and scan memory in one call, avoiding long zc loops. blocks are aligned powers of two, and a freed block merges with its free buddy so space can be reused at any size. an earlier bug let a child merge into a container while the container was still being freed; growable arrays exposed it because their handles and item blocks are often buddies. allocation finds the smallest free block that fits and splits it. each size class has a doubly linked free list, so removing a buddy during a merge takes constant time. checked code frees deterministically. `free` quarantines a block: it marks the header free but leaves the bytes intact, so a stale alias reads a dead block and a second free through it does nothing. the next collection returns a quarantined block to the free lists only when no word points to it. `free(d*x)` and compiler-generated cleanup for an owned local call `recycle` instead, returning the block to its size-class list after freeing its children. `arena_new(n)` creates an owned block for bump allocation; `arena_alloc` returns interior words and `arena_reset` rewinds the bump. the mark recognizes exact pointers by their check word. with memdebug bit 4, it also accepts interior pointers up to 64 words into the payload. `memdebug scrub` fills the payload with 57005 after releasing its children. the mark-sweep collector is conservative and non-moving; it handles globals, unchecked code, and values passed to it. `gc()` returns the number of objects freed, and collection also runs when an allocation does not fit. the sweep frees through the same merging path. a shared object (header flag bit 8) is not quarantined: `free` leaves it in place, and collection reclaims it when unmarked. the low three flag bits are the live, mark, and pin state; the share bit stays set across a mark. + +## c abi + +zc can call native c functions and hand zc functions to native code. it is built on the syscall gate (ops 14 to 17), so it needs a host with `dlsym`: the interpreter, the llvm backend and the arm64 jit on linux and macos (the jit has to be a dynamic binary, since a static one has no `dlsym`). the microcore, the javascript vm, the web build and the windows scripts answer -1, and a native call there gives -1 and nothing else. + +``` +extern double pow(double x, double y); +extern long c_strlen(str s) = "len_w"; // zc name = "c name" +extern void qsort(word *base, long n, long size, void *cmp); + +int cmp(int *a, int *b) { return *a - *b; } +o3 int cmp3(int *a, int *b) { return *a - *b; } + +fn main() { + let v[4]; + v[0] = 3; v[1] = 1; v[2] = 2; v[3] = 0; + qsort(v, 4, 8, cfunc(cmp3)); // native code calls back into o3 + put_n(c_strlen(b"hello")); +} +``` + +`extern` reads a c prototype and defines an ordinary zc function with that name, so a call is a normal call. types are spelled the c way. the integer types (`long`, `size_t`, `int64_t` and the other 64-bit names) pass a whole word; `int`, `short`, `char` and their unsigned forms pass and come back as that width, sign or zero extended; `double` and `float` are a zc float (a `float` goes to the c side as a 32-bit float); any `T *` is a byte address, the same number `addr_b` gives, except `word *`, which is a word address (a zc object or array, what `fixed []int` or `new` hands you), and `str`, which is a zc byte string (`b"..."`) passed as the address of its bytes. a result that points inside vm memory comes back as a byte address or word address by the same rule, and any other pointer (a `malloc` result, a `FILE *`) is an opaque number you can hand back to c. up to 15 arguments in all. the first six integers and eight floats go in registers and the rest on the c stack, in order, so a function like `snprintf` with many arguments works; more than 15 is a compile error. from o3 code the call is a call instruction in the generated code: the arm64 jit emits it, and the llvm backend emits a direct call to the symbol's address, so llvm can see through it, and `sqrt`, `fabs`, `floor`, `ceil`, `trunc`, `round`, `sin`, `cos`, `exp`, `exp2`, `log`, `log2`, `log10`, `pow`, `copysign` and `fma` on doubles become llvm's own intrinsics and fold or vectorize like any other arithmetic. the darwin host adapts the known fixed prefixes of `printf`, `fprintf`, `sprintf`, and `snprintf`; other darwin variadic exports still need an explicit ABI adapter. a variadic function is declared once per shape you call it with (`extern int snprintf2(word *buf, long cap, str fmt, long a, double b) = "snprintf";`), and the call sets up the registers a variadic call needs. a missing symbol prints `zy: no native symbol name` and the call gives -1. `cload("libfoo.so")` (a byte string) opens a library so its symbols resolve, `cfind(name)` asks whether one does, and the math library is opened on demand. + +`cfunc(f)` gives a native function pointer that runs the zc function `f`. the c signature comes from `f`'s declared types: `float` is a double, a pointer (`int *`, `[]T`) is a word address (so `int cmp(int *a, int *b)` reads the elements c points at), and everything else is a long. an `o3` function is entered at its compiled region, so a comparator or a worker in o3 runs as native code both ways. the pointer is only good while a native call from zc is running (it runs vm code on top of the stacks as they were at that call), so it belongs in an argument of the call that uses it or in a callback registration that is done again each time. there are 16 distinct callbacks per result class (long, double, float); the same function and signature reuse their slot. a callback that makes native calls of its own works, to any depth the stacks allow. + +how it works: the compiler writes the symbol's name, a signature word (4 bits per kind: the result first, then each argument) and room for the arguments as data in the code stream, and the stub it compiles stores its arguments there and does `syscall(14, block)`. the host looks the name up with `dlsym` (and remembers the answer in the block), passes the integer arguments in the first six integer registers and the floats in the first eight fp registers by calling through a prototype of six longs and eight doubles, and converts the result. `cfunc` asks gate op 16 for a trampoline: one of 16 prebuilt c functions per result class, which pushes its arguments on the vm data stack, pushes a return address that stops the nested run, runs the vm function with the same `vm_until` an o3 call uses, and pops the result. the stack pointers it needs are published by the gate store (a global in the interpreter, an alloca store in llvm blocks, and the same two words the jit saves on exit). a call from o3 code to an extern leaves the region like any call to ordinary code, and the stub's store to the gate goes through the host, so a native call costs a gate round trip. lowering it to a direct call inside a region (so llvm sees it) is not done. + +## syscalls + +the host is reached by storing an op number at word `0x84002`. the three words after it are the arguments, and the store leaves the result in place. `syscall(op, a, b, c)` does that. ops 1 to 5 still pass word strings, one character per word. ops 10 to 13 pass the byte address of a packed string's payload (`addr_b(s + 1)`), which is NUL-terminated. + +``` +SYS_OPEN 1 open(path, 0 read, or 1 create/truncate for write) -> fd or -1 +SYS_CLOSE 2 close(fd) +SYS_READ 3 read(fd, buf, n) -> bytes stored, one per word +SYS_WRITE 4 write(fd, buf, n) -> bytes written +SYS_GETENV 5 getenv(name, buf, cap) -> length, or -1 if unset +SYS_TIME 6 unix seconds +SYS_BOPEN 10 open a packed path +SYS_BREAD 11 read into packed bytes +SYS_BWRITE 12 write packed bytes +SYS_BGETENV 13 getenv into packed bytes +SYS_RAW 40 any linux syscall: the block at `rawblk` holds the number and six arguments; the result is the kernel's, or -errno. `Host.raw(nr, a, b, c, d, e, f)` in src/lib/std.zc builds the block. linux only, -1 elsewhere +SYS_MEMINFO 9 memory facts: 0 total words, 1 heap start, 2 heap end, 3 total bytes +SYS_FILL 18 fill n words at an address with a value +SYS_COPY 19 copy n words (overlap safe) +SYS_SCAN 20 does any word of a block look like a heap pointer (used by release) +SYS_ARGC 21 how many entries argv has +SYS_ARG 22 argv(index, buf, cap) -> length, or -1 past the end +SYS_ENVC 23 how many entries envp has +SYS_ENV 24 envp(index, buf, cap) -> length, or -1 past the end +``` + +ops 21 to 24 are the command line and the environment. the string ops take an index and copy one entry across one character per word, like `SYS_GETENV`: a zero buffer asks for the length alone, so the caller sizes the string before it fetches it. `argv[0]` is the program path, and `` when the source was read from stdin, so `argc` is 1 even with nothing on the command line. the javascript and wasm vms are given `args` and `env` when they are constructed, and a bare-metal port has neither, so it answers `` on its own. + +`readfile(path)` returns a fresh string, or 0 if the file can't be opened. `include` uses it. ops 14 to 17 are the c abi. a host that doesn't know an op answers -1, and the zc side then does the work itself with a loop, so the microcore and the javascript vm run the same programs. these are not available from o3 code, whose stores don't go through the gate. + +## code as data + +the compiler is in memory with your program and every name in it is callable. + +`asm { }` emits raw vm words: numbers push, `-5` pushes a negative, `#n` emits a raw word, one-char tokens from the mnemonic table are opcodes, `:name` defines a label local to the block, enhancement names emit their opcode, and other names push the address of a variable or function. + +`quote { ... }` is a token list as a fresh object: `[n, kind, value, line, kind, value, line, ...]`, so errors inside a macro expansion point at the line the tokens came from, where names, strings and operators are interned pointers (`intern("while")` gets one). `tokens(s)` makes one from a string, and `qdup(q)` copies one. + +`eval(q)` compiles a token list as a function body and runs it. `compile(q)` returns its address instead, `load(q)` compiles top-level declarations, and `evals(s)` runs `eval` on a string. + +parse words: `immediate fn name() { }` runs at compile time whenever `name` shows up as a statement, inside an expression or at the top level. it starts with the current token on its own name and works the compiler directly: `next()`, `peek()` and `peekat(k)` (up to 4 tokens ahead), `tk`/`tv` (current token kind and value), `expect`, `accept`, `expr()`, `stmt()`, `substmt()`, `ptype()`, `emit`, `lit`, `op`, `hole`/`setlit` for jumps, and `here`. `grab()` reads a `{ }` group into a token list, and `feed(q)` pushes a list back into the input, which is all a macro needs. parse words can call `eval` partway through a compile: the compiler saves its state and emits the new function in place with a jump around it. the built-in keywords are entries in the same table, so a parse word called `while` replaces `while`. + +tables: `add_binop("**", 11, "ipow c")` adds or replaces a binary operator with a precedence and its code, written like asm (names resolve when it's used). `add_unop` does the same for prefix operators, and the lexer learns new spellings from both. `mnem` is the asm mnemonic table, `q@!$_~cjzr,?.+-*&|^><%`, and can be rewritten. + +## the bootstrap + +`src/tools/zc0.c` compiles the subset of zc used by `src/compiler/compiler.zc` (without the analyzer, types, quote, parse words, or block expressions, but with `enhance` declarations) into the first genome. that genome compiles `src/compiler/compiler.zc` again in the VM, where the full compiler, analyzer, and native division code meet their own source. the compiler passes its lifetime checks and compiles itself into identical words; the test suite checks this fixpoint on the jit and llvm backends when available. an image build (`build_image(org, dst)`) compiles with only the image's names, keywords, and built-in types visible, writes code for the requested address, and appends a symbol table so the booted genome knows its names and enhancements. + +## the instruction set + +every word is 64 bits. an odd word is a literal that pushes `w>>1` (63 bits, sign extended). an even word is an opcode, `w/2`. there's a data stack and a return stack, both stored inside memory. a literal cannot hold the minimum word (`-2^63`); `put_n` and `itoa` special-case it. shift counts wrap at 64. `mulhs` and `mulhu` are the high half of a 128-bit product, and `clz` of 0 is 64. + +byte addresses name the same memory as word addresses. word `w` is the eight bytes `w << 3` through `(w << 3) + 7`, and byte 0 of a word is its low 8 bits, so the layout does not depend on the host's endianness. `addr_b(w)` and `addr_w(b)` convert. a byte address is an ordinary integer: the odd/even tag is only for code words, and `u`/`m`/`d` stay on the word pointer of a heap object. opcodes 612..617 load and store 8, 16 and 32 bits, little-endian, and a 16- or 32-bit access may cross into the next word. an access that does not lie entirely in memory loads 0 and stores nothing. `ld8`, `st8`, `ld16`, `st16`, `ld32` and `st32` in the runtime are those opcodes. a byte store of a word some translation covers is dropped the same way a word store is. it does not fire the syscall gate; that is still a word store at `SYSGATE`. `bops.h` is the shared implementation. + +a byte string is that layout as a value. `b"..."` (the `b` against the quote) packs the characters into the code stream: word 0 is the length, the bytes start at the next word, eight per word, and a 0 byte follows the last character. `"..."` stays one character per word. `len` on a byte string is `len_s`. `s[i]` loads one byte and is not a place to store. `s[i:j]` copies a slice into a fresh object, end exclusive, and clamps the bounds. `+` concatenates two byte strings. the six comparisons require a byte string on both sides and order unsigned bytes. `s_to_n` parses a signed integer and `n_to_s` builds a fresh string; both special-case the minimum word. `put_s` of one byte string writes its bytes. a result of `+`, a slice, `n_to_s` or `new_s` is a shared heap object. the compiler records that a local holds a byte string in bit 6 of its tag. `cons(a, b)` and `triple(a, b, c)` are shared cells on the same heap; `car`, `cdr` and `tget` read them, and `share(p)` publishes an existing object. bit 8 of the header flags is the share bit. it is orthogonal to live, marked and releasing. `free` of a shared object does nothing, `own` and `release` do not follow one, and the sweep collects it when no word points at it, cycles included. a unique object still quarantines on `free`. putting a heap object into a cell publishes it, so the old owner's later `free` does not clear a cell that still points at it. + +a float is an IEEE-754 binary64 value in one whole word, not an odd literal: 63 bits is not enough, and bit 0 of a double is data. opcode 600 (`flit`, the word 1200) is followed by those raw bits, and the decoder consumes that word itself. 601..604 are add, sub, mul, div. 605..607 are `==`, `<` and `<=` (the other relations are those plus a swap or a xor). 608 and 609 convert int to float and float to int, truncating toward zero. 610 prints six digits after the dot and 611 parses a word-string into bits. 618 prints with a mode: 0 is the shortest spelling that parses back to the same bits, 1 is fixed (digits after the dot) and 2 is scientific (significant digits), and the precision is capped at 17. they sit above the enhancement range, so slots 22..533 do not move. where the processor has an FPU the arithmetic is that FPU: the arm64 jit emits `fadd`, `fmul`, `fcmp`, `scvtf` and `fcvtzs`; llvm lowers the same opcodes to native `double` operations; the interpreter and the microcore call `fops.h`, which is a volatile host `double` so gcc emits SSE2, AArch64 FP or RV64 `fadd.d`. both virt boards turn the FPU on before `main` (RISC-V `mstatus.FS`, AArch64 `CPACR_EL1.FPEN`) and compile with a hard-float ABI. a port with no FPU builds with `-DZY_SOFTFLOAT`: the same `fops.h` entry points are then an integer IEEE implementation, round ties to even, and the float line is unchanged. `fops.h` is also the shared parser and the printer, and both are integer arithmetic, so the text is the same on every platform and does not call libc. opcode 610 is an optional minus, the integer digits, a dot, and six fractional digits, with a tie in the next digit rounded away from zero, for the whole finite range. opcode 618 is the shortest round-trip spelling (a plain decimal when the exponent is between -4 and 15, otherwise scientific), or a fixed or scientific form. the parser keeps the digits it is given, rounds ties to even, and accepts an exponent, `inf` and `nan`. `nan`, `inf` and `-inf` are the non-finite texts, and `-0` is distinct from `0`. `float` is a type. the compiler tracks it through locals (bit 5 of the tag), globals, fields, parameters, call results and returns, and promotes an integer operand of `+ - * /` and the comparisons, so a use does not need its own annotation. `++` is still an integer add. `o3` lifts 600..609: two constant operands fold by the IEEE operation itself, float values stay in FP registers, and `x - x` is left alone so a NaN stays a NaN. `0.0` and `-0.0` are both false in `if`, `while`, `for` and `&&` `||`: a float is compared with `+0` before the integer test. `1.field` is a field access. a dot after digits is a fraction only when a digit or an exponent follows it, and an integer address uses a field name that belongs to one struct. + +``` +0 q halt 5 ~ swap 10 , in (Q) 15 * mul 20 < less +1 @ load 6 c call 11 ? empty (J) 16 & and 21 % install +2 ! store 7 j jump 12 . out (G) 17 | or 22..533 enhancements +600 flit 601 fadd 602 fsub 603 fmul 604 fdiv +605 feq 606 flt 607 fle 608 i2f 609 f2i +610 fout 611 fparse 618 ftext +612 ld8 613 st8 614 ld16 615 st16 616 ld32 617 st32 +3 $ pick 8 z jz 13 + add 18 ^ xor +4 _ drop 9 r ret 14 - sub 19 > shr +``` + +stack effects: `!` is `(v addr --)`, `$` is `(n -- copy of the nth item)` so `0 $` is dup, `z` is `(cond addr --)` and jumps when cond is 0, `c` and `j` take their target from the stack, and `%` is `(descriptor -- status)`. there's still no div or mod in the base set, because a divider can't finish in one cycle in hardware. an enhancement can add one. + +## memory map and abi + +see [memory configuration](docs/design/memory.md) for build controls and the remaining runtime growth work. + +the machine has two sizes. PW is the end of the fixed part: the code area, the compiler's tables, the genome and the stacks. every table indexed by a code address is PW words long, and the program counter never goes past it. MW is the total number of words the host gives the machine (1 GiB, 0x8000000 words, on the default build), and the heap starts at PW and grows into the rest of it while the program runs. the host's array behind MW is static and untouched until the heap reaches it, so a program that never allocates much never pays for the memory. a build can pick another total with `-DMW=...`; `src/core/memmap.h` refuses to compile if it is smaller than the initial heap. + +``` +0x000000..0x07FFFF code: the program, with globals between functions (CODE_END) +0x080000..0x08FFFF type records (65536 words) +0x090000 frame pointer word (ZFP); 0x090002 is the syscall gate +0x090100..0x113FFF compiler data: interned names, symbol table (16384), locals, fixups, tables +0x114100..0x163FFF genome: 0x114100 entry, then the symbol table address and the image end (327680 words reserved) +0x164100..0x1A3FFF frame stack, grows down from 0x1A4100 (262144 words) +0x1A4100..0x1B40FF return stack, grows up +0x1B4100..0x1BC0FF mark stack +0x1BC100..0x1C4FF0 data stack, grows down +0x1C5000..0x224FFF the heap to start with (0x60000 words), then it grows up to MW +``` + +the numbers are for the default build. its regions are sized so a program does not run into them (the tables and stacks are large, and words nobody writes cost nothing, because the host's array behind MW is not touched until it is used), where the small and run presets are tight and sized from measurements; `src/tools/genmap` writes the same layout for a smaller machine (see "smaller machines") and `--preset fixed` gives the older arrangement with a fixed heap and no growth, which the language server uses. the host answers `syscall(9, k)` with facts about the map (0 total words, 1 heap start, 2 heap end for now, 3 bytes), so zc code never assumes a size. + +calls push the arguments left to right and the callee's prologue pops them into its frame. locals live in the frame at `*0x90000 + slot`. every function leaves exactly one word on the data stack. heap objects have a 5 word header: size, flags with the size class in the high bits, a check word, owner, type. + +## how the core works + +registers: x19 holds the memory base, x20 the data stack pointer, x21 the return stack pointer, x22 the translation table and x23 the state block (memory, s, r, table, coverage map, Q, G, &J, store hook, install hook, enhancement fallback). o3 regions also use x26..x28, which the entry stub saves. + +compilation is lazy. the first time control reaches an address, that block is translated from live memory. a block ends at call, jump, ret or halt. a `z` doesn't end it: the not-taken side keeps compiling, which makes superblocks. a literal followed by `c`, `j` or `z` is fused into a constant target. every exit goes through a dispatch stub that checks both stack pointers against their limits (kept in x24 and x25), bounds-checks the target, loads `tab[t]`, and branches straight to it when it's already compiled. otherwise it returns to c, which compiles the block and re-enters. enhancements are inlined: inputs load from the data stack into x0..x15, the optimized micro-ops run in registers with x16 and x17 as scratch, and the outputs go back. an install calls into c and then returns to the loop, which flushes every translation, since code built before the install may have inlined the old version. a flush drops o3 regions too, and they lift again on their next entry. when the loop reaches an o3 entry point, it compiles the region (or reuses it) instead of translating a block. + +self-modification is handled with a counted coverage map: one byte per word, the number of live translations that cover it, sticky at 255. a store checks that byte inline: an uncovered target, the usual case, gets a plain store. a covered target calls the hook, which looks the word up in a page index (64-word pages, each with a list of the blocks that touch it) and drops only the translations containing that word, decrementing the coverage of just their words. the jit code then returns to c at the next instruction, so even patches to the very next word take effect. writing back the same value is a no-op. installing an enhancement does not flush: every translation records which enhancement slots it inlined, and an install drops only those (and the o3 regions that depend on the slot). if the 16 mb code buffer or the block index fills, everything is flushed. on llvm each block has its own resource tracker, so dropped code is freed once no compiled code is running. + +the stacks are checked. the data and return stacks are checked at every block exit on all three backends, so the cost is a couple of compares per block rather than per push. a block pushes at most about 1024 words, which the data stack limit leaves room for. overflowing either one stops the cpu with `zy: data stack overflow` or `zy: return stack overflow`. the frame stack is checked in every function prologue, which resets the frame pointer and stops with `zy: frame stack overflow`. + +platform: the darwin path uses `MAP_JIT`, `pthread_jit_write_protect_np` and `sys_icache_invalidate`; linux uses rwx mmap and `__builtin___clear_cache`. this session verified the macOS arm64 native path for dispatch, frames, stacks, and code segments, along with the FFI learning example and the LLVM 23 backend. the shell runner was exercised with bash on macOS. no linux-host runtime tests were run in this session, and the Windows batch runner was not run on a Windows host. under the hardened runtime, the darwin path also needs the allow-jit entitlement. every host shares the same semantics: shift counts wrap at 64, stores outside memory are ignored, loads outside memory read 0, and jumping outside memory or to an unwired opcode halts. cells are 8 bytes on the host; the word addresses in the map above did not move. + +## the microcore + +`src/micro/micro.c` is a portable interpreter for the same genome, small enough to move to new targets, including bare metal. it complements `src/core/core.c` with the memory map, two stacks, base opcodes, call and return, and genome loading at `GORG`. it has no jit, llvm backend, o3 lifter, or enhancement optimizer. it uses `stdint.h` and the shared headers `src/micro/micro_port.h`, `src/core/fops.h`, `src/core/bops.h`, and `src/core/stack.h`, plus the generated genome header; `micro_port.h` includes `memmap.h`. its five host functions are declared in `src/micro/micro_port.h`. bss must be zero before `micro_run`. on a cpu without a 64-bit divider, the compiler may link its own division helpers. the microcore's initial memory comes from the generated map, and hosted ports can configure the initial size and growth limit. + +the host interface is `src/micro/micro_port.h`. `src/micro/port_linux.c` provides stdin, stdout, syscalls, and stderr diagnostics, matching `src/core/io_linux.c` plus the syscall gate in `src/core/core.c`. syscalls 1 to 6 handle files, environment variables, and unix time; 7 exits, 8 reads a counter, and 9 reports or configures memory. `src/micro/port_stub.c` is the freestanding template: connect `uart_putc` to a transmit register, leave `micro_getc` at end of input, and have `micro_syscall` return -1 for every operation. the runtime then sees failed file opens and no clock. `src/baremetal/port.c` implements the same five functions for the virt boards, where the operations are available. link the stub instead of `port_linux.c` and call `micro_run` from reset. + +`/`, `%`, and `<<` are not base opcodes. `src/runtime/rt.zc` installs them, along with `lfa` and `outside`, through opcode 21 when the genome boots; programs can install more with `enhance`. the microcore supports install because examples such as `wiring.zc`, `mandel.zc`, and o3 programs call `lfa` and `outside` from ordinary bytecode. recipes and straight-line `vm` bodies are lowered to micro-ops and executed, without constant folding or multiply-high rewrites, so `x / 7` remains a divide. a `vm` body that performs i/o or branches fails at install time with the same error as the full core, and any `else` fallback remains available. install kind 2, an o3 region, returns success but is ignored; the bytecode still runs, so these programs keep their output without the o3 fast path. lowering is limited to 128 ops and 256 values per enhancement, and 4096 ops in the pool. enhancement stores do not pass through the syscall gate, matching `src/core/core.c`. + +what it leaves out, on purpose: native code, the optimizer, o3's register interpreter, coverage maps, and threads. it is the speed of the plain interpreter, and a bit slower where the full core turned a constant divide into a multiply. it is still not a C ABI, and a character is still one word. details are in `docs/history/21-micro`. + +## smaller machines + +`src/tools/genmap.c` computes the layout for each build. the `small` preset carries the compiler; `run` carries the runtime and a precompiled program. `src/tools/automap.sh` sizes the genome reservation to the actual image and rebuilds the bootstrap compiler for that map. + +```sh +bash src/tools/automap.sh small build/small +cp src/micro/micro.c src/micro/micro_port.h src/micro/port_linux.c src/core/fops.h src/core/bops.h src/core/stack.h build/small/ +cc -O2 -fwrapv build/small/micro.c build/small/port_linux.c -Ibuild/small -o build/small/zym +build/small/zym < examples/hello.zc +make run PROG=examples/hello.zc +build/run/zymr +bin/genmap --preset default --check +``` + +use `genmap` options to adjust heap, table, and stack sizes at build time. compile the core against the generated headers and the genome for that same layout. `--preset fixed` keeps the historical layout; `--preset default` places the heap above the fixed regions so it can grow. see [memory configuration](docs/design/memory.md) for controls and current limits. + +## bare metal + +`src/baremetal/run.sh` builds the microcore without an os or libc and boots it on a qemu `virt` board. the port provides a uart, clock, ram files, and exit handling. startup enables the fpu, clears bss, and calls `micro_run`. + +```sh +src/baremetal/run.sh ready riscv +src/baremetal/run.sh riscv run examples/hello.zc +src/baremetal/run.sh riscv io examples/echo.zc +src/baremetal/run.sh riscv compile examples/fact.zc +src/baremetal/run.sh aarch64 run tests/programs/gc.zc +``` + +`run` embeds the compiled program. `io` also supplies stdin through the uart. `compile` embeds the resident compiler and sends it source followed by a backtick. the scripts check for the required cross compiler and qemu before running; unavailable targets are skipped by the test suite. board ram remains a physical limit. + +## maintenance + +the compiler also reports [definite bounds and lifetime findings](docs/design/storage-analysis.md), with known sizes and lifetimes in editor hovers. the maintenance checklist and remaining memory work are tracked in [TODO.md](TODO.md). the [development transcript](docs/history/transcript.md) and [earlier test results](docs/history/maintenance-notes.md) are historical records. diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..b72d3f2 --- /dev/null +++ b/TODO.md @@ -0,0 +1,66 @@ +- [x] read the compiler, runtime, vm cores, language server, and build tools. +- [x] verify formatted markdown and code in vs code hovers, including bundled server builds. +- [x] add mandatory documentation for every built-in type. +- [x] use `/* ... */` comments as documentation, including comments for individual variables and arguments on the same line. +- [x] run focused tests between changes and avoid repeating the full suite. +- [x] check every core source file through the lsp document protocol with its expected source dependencies; reproduce and fix editor diagnostics, including those in `src/compiler/compiler.zc`. +- [x] rename current i/o and string helpers consistently, including `puts` → `put_s`, `putn` → `put_n`, and `scat` → `cat_s`; update compiler references, examples, tests, and documentation. +- [x] review passing a temporary owned return to an output function; preserve the restriction if it is needed for lifetime safety, otherwise fix it with regression coverage. +- [x] draft `docs/BORROW.md` with a concrete plan for a less intrusive borrow checker and lifetime analyzer. +- [x] grow collector mark work without losing pending objects or counting compiler storage as user objects on the default map. +- [x] verify more than 64 generic declarations, more than 128 instantiations, more than 32 captures, and more than 8 nested closures on the default map. +- [x] move source files into appropriate subdirectories of `src/`, tests into subdirectories of `tests/`, and documentation and designs into `docs/`. +- [x] consolidate windows build, run, and test entry points into one interactive `tinyvm.bat`. +- [x] add a matching interactive `tinyvm.sh` for linux, with direct command mode. +- [x] remove stale planning references from current documentation and code. +- [x] consolidate useful memory-limit information, then remove `CEILINGS.md` if redundant. +- [x] reject stale lsp diagnostics after closing and reopening a document. +- [x] make raw native stack picks obey the same address bounds as the portable backends. +- [x] show array, pointer, struct, enum, alias, and function types in hover previews. +- [x] show inferred types for untyped variables, function returns, arguments, and other inferred declarations. +- [x] verify inference for 600 functions and 5000 locals without silently dropping later edits. +- [x] grow stable frame segments and preserve older local addresses through collection on native, microcore, and javascript. +- [x] verify stable frame growth with llvm 23 and fix the macos libc helper-name conflict. +- [x] inventory fixed addresses and capacities across native, microcore, javascript, and lsp backends in docs/design/memory.md. +- [x] grow execution data and return stacks on native, microcore, and javascript, with explicit runtime reservations and collection after relocation. +- [x] verify operand and return stack growth on llvm and check invalid requests and denied growth without corrupting the vm. +- [x] repeat growth checks on a small generated map. +- [x] verify growing frames on small generated maps. +- [x] preserve all generic parameter names and inference state across nested compilation; infer later function arguments. +- [x] grow assembly and remaining parser scratch storage. +- [x] preserve long lsp request identifiers, grow json and header scratch buffers, and reset tagged-enum records between document checks. +- [x] grow image-building output independently of the building host’s genome slot while preserving logical image addresses. +- [x] grow native code-address metadata and preserve warm calls and invalidation after bulk rewrites. +- [x] restore missing javascript float opcodes and binary32 conversions, then compare focused arithmetic and formatting tests across backends. +- [x] grow program emission and image-building capacity without relocating live code or globals. +- [x] correct the lexer’s float-buffer load and read long float spellings without clipping their exponents. +- [x] point documented image boot headers at the symbol table after packed documentation. +- [x] polish the learning guide and verify its examples against their documented output. +- [x] fix reproducible bugs and apply useful optimizations with regression coverage. +- [x] support build-time adjustment through a consistent generated layout. +- [x] finish growing all compiler tables and execution stacks during execution, preserving live references; heap growth alone does not satisfy this requirement. +- [x] test changed limits, memory growth, invalid configurations, and agreement between image and host layout. +- [x] document available controls and any remaining fixed constraints precisely. +- [x] update builds, runners, editor bundles, tests, and links for the new paths, keeping the repository root focused on entry points. +- [x] preserve useful commands, propagate failures, and handle paths containing spaces. +- [x] verify runner behavior on available hosts and document untested platform behavior. +- [x] polish the learning guide and reference documentation; keep examples executable. +- [x] replace obsolete build commands and distinguish current behavior from historical notes. +- [x] make comments consistent and keep section overviews separate from unrelated declarations. +- [x] keep documentation and comment prose lowercase, matching the older style; preserve case-sensitive code and identifiers. +- [x] preserve full 64-bit integer literals, including values outside the single-word literal format. +- [x] grow stage 0 source input and image output beyond their initial capacities. +- [x] execute javascript vm and micro enhancement recipes with the same slot and output rules as the native hosts. +- [x] add basic bounds and lifetime analysis that warns about provable out-of-bounds access and use after free. +- [x] test definite failures and safe cases so uncertain behavior does not produce a definite warning. +- [x] show known size and lifetime information when hovering over an array or pointer. +- [x] fix the macos first-call crash in native mutual o3 recursion and verify it with a focused regression. +- [x] preserve nonfatal bounds and lifetime warnings across native, microcore, javascript, and both extension hosts. +- [x] verify block documentation, inferred pointer types, and lifetime metadata in the real editor. +- [x] replace quadratic hint deduplication with a growing set and verify large javascript documents. +- [x] make compact compiler maps grow past startup pressure and update obsolete fixed-limit test expectations. +- [x] fix darwin stdio variadic argument placement exposed by native integration tests. +- [x] index local names and preserve shadow chains to avoid rescanning all earlier locals. +- [ ] keep cast bounds conservative and infer aggregate aliases as pointers to their stored addresses. +- [ ] allow run-only heaps to grow and separate freestanding physical backing from logical vm limits. +- [ ] complete final integration checks and refresh generated editor artifacts after the final fixes. diff --git a/docs/BORROW.md b/docs/BORROW.md new file mode 100644 index 0000000..78b28e9 --- /dev/null +++ b/docs/BORROW.md @@ -0,0 +1,89 @@ +# a quieter ownership and lifetime checker + +## objective + +accept ordinary, safe code with little annotation. infer ownership transfers and borrow duration when the compiler can prove them, and explain the cases it cannot prove. measure this on a corpus of small programs rather than claiming the checker is generally smarter than rust's. + +rust already infers non-lexical lifetimes from control flow; reproducing that idea would not establish an advantage. its checker also tracks initialization and moves. zc can choose different ergonomics for its single-threaded ownership model and collector, while documenting where that model provides fewer guarantees. see the [rust compiler guide](https://rustc-dev-guide.rust-lang.org/borrow-check.html). + +## current behavior + +the analyzer runs alongside parsing and code emission. a local records an owner, scope, and state: held here, moving up, moving down, borrowed, or dead. snapshots reconcile branches and loops; derived pointer aliases retain their owner. explicit `.m`, `.d`, and `.u` tags describe borrowing and transfers. + +passing a fresh owned result directly to a borrowing parameter is deliberately rejected by `argcheck`. only a consuming `.d` parameter can currently receive it without a named local. naming the result gives the caller a place to insert cleanup. accepting the same call without generating that cleanup would change lifetime behavior and can leak the temporary until collection. + +```zc +char*.u message() { char* s = new(2); s[0] = 'x'; s[1] = 0; return u*s; } +fn main() { + let text = message(); + put_s(text); + return 0; +} +``` + +plain borrowing parameters do not provide a complete interprocedural escape contract. unchecked code, raw host calls, integer-to-pointer conversions, and unknown native functions remain boundaries of static reasoning. + +## proposed rules + +### temporary borrows + +allow `put_s(message())` when the callee is proved to borrow without retaining, freeing, or transferring the argument. evaluate arguments in their existing order, hold each owned temporary in a compiler-generated slot, call the function, then free the temporary after its last dependent use. keep a returned alias alive through the enclosing expression when necessary. + +do not substitute `.d`: the output function does not consume its argument. nested calls, short-circuit expressions, early returns, and error recovery need their own cleanup paths. a temporary cannot be destroyed while another argument or the result still refers to it. unknown or escaping callees keep the explicit binding requirement. + +rust also has temporary scopes and lifetime extension rules; the proposed benefit here is a smaller set of user-facing rules for zc, not the invention of temporary lifetimes. see the [rust reference](https://doc.rust-lang.org/reference/destructors.html#temporary-scopes). + +### function effects + +infer a summary for each parameter: read, write, retain, consume, or return an alias. distinguish a returned owned result from a returned borrow and record which argument the borrow depends on. compute summaries to a fixed point for recursive functions; cache them by function body and relevant callee summaries. + +start with the runtime and ordinary functions whose bodies are available. annotations remain useful for externs, separate compilation, and deliberately unchecked code. a missing summary means unknown effects, not a promise that the call is safe. + +### borrows end at their last use + +derive borrow duration from control flow. a temporary alias that is no longer used should not prevent a later move of its owner. infer ordinary moves into consuming calls and returns where there is one unambiguous owner; retain explicit tags for ambiguous transfers and low-level code. + +use one fact graph for ownership, initialization, aliases, and known bounds. keep the existing parser and parse words: record analysis events from the real compiler, rather than adding an independent source parser that can disagree with it. + +### fields, elements, and mutation + +track accesses by owner and projection: a struct field, a constant array index, or an element range. allow disjoint accesses when their projections are proved separate. invalidate those facts after an overlapping write, resize, unknown call, or unchecked operation. + +ordinary aliases may coexist in zc; mutation is safe only while the owner remains alive and the access stays valid. permitting aliases does not provide rust's concurrency guarantees. do not infer disjointness from uncertain indices, and do not treat a quarantined block's intact bytes as a valid lifetime. + +### useful diagnostics + +report the operation that makes a later use invalid, the later use, and the relevant owner or bound. show known size, ownership, borrow source, and lifetime end in hover text. use "unknown" when a fact is unavailable. + +preserve hard errors for existing definite lifetime violations. add warnings for newly detected definite out-of-bounds access or use after free in code the old analyzer did not track. keep heuristic suggestions separate from definite findings, and avoid claiming an uncertain path is definitely invalid. + +## implementation order + +1. finish the existing maintenance and memory work in `TODO.md`; analysis tables must grow without silently dropping facts. +2. add compiler events with source ranges and a small control-flow representation. reproduce the current analyzer's accepted and rejected corpus before changing its rules. +3. infer non-escaping summaries for the runtime and add temporary cleanup for proved borrowing calls. +4. implement last-use borrow endings and unambiguous ownership transfer inference. +5. add projection facts, bounds facts, and explanatory editor previews. perform the final warning phase only after the preceding maintenance is complete. +6. compare the old and new checker on the same programs. expose unexplained behavior changes as regressions, not as an improvement count. + +## acceptance cases + +| case | required behavior | +|---|---| +| an owned temporary passed to a proved non-escaping output function | accepted; one cleanup after its last use | +| a temporary passed to a callee that stores it globally | rejected unless ownership is transferred | +| a returned alias of a temporary used later in the same expression | accepted only with a proved lifetime extension | +| two arguments depending on the same owner | evaluate without freeing or moving that owner too early | +| an alias whose last use precedes an ownership transfer | accepted when no surviving alias escapes | +| independent struct fields or constant array elements | permit proved disjoint accesses | +| resize while an element alias remains live | reject or invalidate the alias according to an explicit rule | +| a use after a definite free or move | retain the error, with both source locations | +| an index definitely outside a known length | show a definite bounds finding | +| a dynamic index with no useful bound | retain runtime checks; no definite warning | +| unknown native or unchecked effects | require a contract or explicit escape hatch | + +verify cleanup counts, allocation counts, alias lifetimes, and output on the interpreter, microcore, and available native backends. tests must include safe neighbors of every rejected example. track annotations removed, safe programs accepted, compile time, and diagnostic precision as separate outcomes. + +## scope + +this is a design plan, not an implemented guarantee. lifetime inference must not change evaluation order or silently choose a different ownership contract. the initial work targets zc's present single-threaded execution model; shared mutable concurrency needs a separate model. diff --git a/docs/LEARN.md b/docs/LEARN.md new file mode 100644 index 0000000..676358a --- /dev/null +++ b/docs/LEARN.md @@ -0,0 +1,718 @@ +# learning zc + +zc is a small c-like language for a stack machine with 64-bit words. this guide starts with ordinary programs, then covers memory, types, native calls, and metaprogramming. every zc code block is a complete file in [examples/learn/](../examples/learn/). the output below it comes from the matching `.out` file. + +## setup + +build from the repository root, then run a source file on stdin: + +```sh +make +bin/zy < examples/learn/01-hello.zc +``` + +`bin/zy` is the native core: it uses the arm64 jit where available and otherwise interprets. `bin/zym` is the portable microcore, `bin/zc0` is the bootstrap compiler, and `bin/zclsp` checks source for the editor. the interactive runners are `./tinyvm.sh` on linux and `tinyvm.bat` on windows. + +run from the root throughout this guide. paths in `include` and `readfile` are resolved from the process's working directory. `tests/drivers/learn.py` checks the examples and verifies that the code blocks here match their source files: + +```sh +python3 tests/drivers/learn.py bin/zy +``` + +native c calls need the full native core. the raw syscall example needs linux on x86-64. the other examples use ordinary language features; availability of host services still depends on the port. + +## contents + +- [programs and control flow](#programs-and-control-flow) +- [numbers and strings](#numbers-and-strings) +- [ownership and the heap](#ownership-and-the-heap) +- [libraries and the host](#libraries-and-the-host) +- [types and containers](#types-and-containers) +- [optimized regions](#optimized-regions) +- [closures](#closures) +- [further reading](#further-reading) + +## programs and control flow + +### the first program + +a unit is read to eof or a backtick outside a comment or literal. after compiling the unit, the runtime calls `main`. an ordinary function returns one word; falling off its end returns 0. the return value of `main` is not the process exit status. `halt()` stops the machine. + +`put_s(s)` prints a string, `put_n(n)` prints an integer, and `put_c(c)` prints a character code. `put_c(10)` prints a newline. + +[example source](../examples/learn/01-hello.zc): + +```zc +fn main() { + put_s("hello, world!\n"); + return 0; +} +``` + +output: + +```text +hello, world! +``` + +### variables and while + +`let` infers a variable's type from its initializer. an integer literal stays a plain word. `int i = 0;` writes the type explicitly. the lifetime analyzer runs by default; the optional typechecker is enabled separately with `typecheck on;`. + +`while` tests before entering the body. `i++` increments the variable, and `break` leaves the loop. + +[example source](../examples/learn/02-loops.zc): + +```zc +fn main() { + let i = 0, n = 0; + while (i < 4) { n = n + i; i++; } + put_n(n); put_c(10); + return 0; +} +``` + +output: + +```text +6 +``` + +### do and continue + +`do` runs the body once before testing the condition. in a `do` loop, `continue` jumps to the condition; in a `for` loop, it jumps to the increment step. this example skips adding 2. + +[example source](../examples/learn/03-do-continue.zc): + +```zc +fn main() { + int n = 0, i = 0; + do { i++; if (i == 2) continue; n = n + i; } while (i < 5); + put_n(n); put_c(10); + return 0; +} +``` + +output: + +```text +13 +``` + +### switch + +`switch` selects the matching `case`; `default` handles the rest. cases do not fall through, so `break` is optional at the end of an arm. + +[example source](../examples/learn/17-switch.zc): + +```zc +fn main() { + int k = 2; + switch (k) { case 1: put_s("one"); break; case 2: put_s("two"); break; default: put_s("other"); } + put_c(10); + return 0; +} +``` + +output: + +```text +two +``` + +### plain enums + +members without an explicit value increase by one, starting at 0. `GREEN = 5` makes `BLUE` equal 6. members can also be written as `color.BLUE` and used as case labels. + +with the typechecker enabled, `color` is its own type: members have that type, an enum value can become an `int`, and an arbitrary integer does not become a `color`. + +[example source](../examples/learn/04-enums.zc): + +```zc +enum color { RED, GREEN = 5, BLUE }; +fn main() { + put_n(RED); put_c(32); put_n(GREEN); put_c(32); put_n(BLUE); put_c(10); + return 0; +} +``` + +output: + +```text +0 5 6 +``` + +## numbers and strings + +### sized integers + +`u8`, `i8`, `u16`, `i16`, `u32`, and `i32` narrow on assignment. adding 10 to an unsigned byte holding 250 leaves 4. arrays and pointers of these types use packed storage. + +addresses normally count words. `sizeof` also counts words, so `[100]u8` occupies 13 words. an `int*` points to word-sized integers; `p + 1` advances one word. a sized pointer such as `u8*` indexes packed elements. + +[example source](../examples/learn/05-small-ints.zc): + +```zc +fn main() { + u8 a = 250; a = a + 10; + put_n(a); put_c(10); + put_n(sizeof([100]u8)); put_c(10); + u8* px = new(u8, 4096); + px[10] = 300; + put_n(px[10]); put_c(10); + return 0; +} +``` + +output: + +```text +4 +13 +44 +``` + +### floating point + +a literal such as `1.5` is an ieee binary64 value in one word. `float` and `f64` name that type; `f32` rounds on store. integer operands are promoted when mixed with floats in arithmetic. + +`put_n` prints integers. the helper `put_f` below calls the vm's float printer with encoded instruction `#1220`. division by zero gives `inf`; `0.0 / 0.0` gives `nan`. both `0.0` and `-0.0` are false in conditions. + +[example source](../examples/learn/06-floats.zc): + +```zc +fn put_f(x) { asm { x @ #1220 } return 0; } +fn main() { + let a = 1.5; + put_f(a + 2.5); put_c(10); + return 0; +} +``` + +output: + +```text +4.000000 +``` + +### byte addresses + +`addr_b(p)` converts a word address to a byte address; `addr_w(b)` converts back. `ld8`, `ld16`, and `ld32` load little-endian values; `st8`, `st16`, and `st32` store them. byte addresses name the same memory as word addresses. + +this example stores `0x11` in the low byte and `0x22` in the next byte. the resulting word is `0x2211`, or 8721. + +[example source](../examples/learn/07-bytes.zc): + +```zc +fn main() { + let w = 0; + st8(addr_b(&w), 0x11); + st8(addr_b(&w) + 1, 0x22); + put_n(ld8(addr_b(&w))); put_c(32); put_n(w); put_c(10); + return 0; +} +``` + +output: + +```text +17 8721 +``` + +### packed byte strings + +`"text"` is a word string: one character per word, followed by a zero word. `b"text"` is a packed byte string: a length word, then eight characters per word and a terminating zero byte. write the `b` directly against the quote. + +for byte strings, `len`, indexing, and `put_s` use the packed representation. indexing reads a byte and cannot be assigned through. `+` concatenates strings, `s[i:j]` makes a slice with an exclusive end, and comparisons order unsigned bytes. `s_to_n` parses an integer; `n_to_s` creates a byte string containing its decimal form. + +[example source](../examples/learn/08-byte-strings.zc): + +```zc +fn main() { + let name = b"zc"; + let hi = b"hello, " + name; + put_s(hi); put_c(10); + put_n(len(hi)); put_c(32); + put_n(s_to_n(b"40") + 2); put_c(10); + return 0; +} +``` + +output: + +```text +hello, zc +9 42 +``` + +## ownership and the heap + +### shared cells + +`cons(a, b)` creates an immutable pair, read with `car` and `cdr`. `triple` and `tget` do the same for three values. several pointers may share a cell. `share(p)` publishes an existing object, and `is_shared(p)` reports whether it is shared. + +`free` leaves shared cells intact. `gc()` reclaims them once no root points to them, including cycles. `scrub()` clears unused stack words that would otherwise look like roots. here, `left` and `right` share the same tail. + +[example source](../examples/learn/09-shared.zc): + +```zc +fn main() { + let tail = cons(1, 2); + let left = cons(tail, 3); + let right = cons(tail, 4); + put_n(car(car(left))); put_c(32); + put_n(car(left) == car(right)); put_c(10); + return 0; +} +``` + +output: + +```text +1 1 +``` + +### owned objects + +binding a fresh `new` result to a local makes that local its owner. the analyzer inserts a free when the scope ends and reports an owned object that is never used. you normally do not write the free yourself. + +`int*` is a pointer to an integer. `*p` accesses that integer. + +[example source](../examples/learn/10-owned.zc): + +```zc +fn main() { + int* p = new(int); + *p = 7; + put_n(*p); put_c(10); + return 0; +} +``` + +output: + +```text +7 +``` + +### moving ownership + +tags describe where an owned object goes: + +- `.m` keeps it in the current block. +- `.d` hands it down to a callee or container; the previous owner cannot use it afterward. +- `.u` hands it up through a return, block value, or assignment to an enclosing scope. + +`m*p`, `d*p`, and `u*p` recast the tag. a plain parameter lends the object without transferring ownership; `.m` on a parameter adds a checked borrow. `free(d*p)` hands the object to `free`. an owned result can be declared with `.u` or inferred from its return. + +[example source](../examples/learn/11-tags.zc): + +```zc +int*.u box(int v) { int* b = new(int); *b = v; return u*b; } +fn main() { + int* p = box(4); + put_n(*p); put_c(10); + return 0; +} +``` + +output: + +```text +4 +``` + +### aliases + +a pointer derived from an owner is an alias. pointer arithmetic, pointer fields, and pointer elements retain the relationship to that owner. an alias can be copied or passed to a call, but it does not free the object at scope end. using it after its owner is handed away is an error. + +in this example, `a` owns `b` after `a.next = b`, and `c` borrows the child. indexing an untyped word array is not tracked as a pointer operation. + +[example source](../examples/learn/12-aliases.zc): + +```zc +struct node { int v; node* next; }; +fn main() { + node* a = new(node), b = new(node); + a.v = 3; b.v = 4; a.next = b; + node* c = a.next; + put_n(c.v); put_c(10); + return 0; +} +``` + +output: + +```text +4 +``` + +### runtime memory checks + +`unchecked fn` and `unchecked { }` disable lifetime analysis for their scope. allocations there rely on the collector. checked variables cannot be accessed from an unchecked block. + +`memdebug on;` adds runtime checks for heap loads and stores, use after free, and double free. `memreport()` lists live allocations made while debugging was on. the allocation line below is the line containing `new`; the reported size is the buddy block's payload capacity. + +`memdebug scrub;` also fills freed payloads with `0xDEAD`; `memdebug off;` disables debugging. ordinary `free` quarantines an owned block until collection can safely reuse it. stale contents surviving quarantine are not permission to use a dead object. + +[example source](../examples/learn/13-memdebug.zc): + +```zc +memdebug on; +int* g; +fn main() { + int* p = new(int); + *p = 1; + g = d*p; + put_n(memreport()); + put_c(10); + return 0; +} +``` + +output: + +```text +leak: line 4 3 words +1 +``` + +## libraries and the host + +### including a library + +`include "src/lib/std.zc";` reads and compiles that file at the include. place it before code that uses the library. the path is relative to the process's working directory. + +the library groups related helpers in structs and calls them as `Group.name(args)`. for example, `Text.itoa` writes a decimal integer into a caller-provided word-string buffer. + +[example source](../examples/learn/14-include.zc): + +```zc +include "src/lib/std.zc"; +fn main() { + let s = readfile("tests/data/hi.txt"); + put_n(len_w(s)); put_c(10); + free(d*s); + return 0; +} +``` + +output: + +```text +6 +``` + +### text and number helpers + +`Text` provides word-string parsing, formatting, copying, search, and case conversion. `Num` provides helpers such as `gcd`, `pow`, `isqrt`, and `clamp`. `Bytes` provides packed-string helpers. `Host`, `File`, `Arena`, and `Buf` cover host services, files, allocation arenas, and byte buffers. + +check the signatures in `src/lib/std.zc` before passing a buffer: many text functions write into storage supplied by the caller. + +[example source](../examples/learn/20-text-num.zc): + +```zc +include "src/lib/std.zc"; +fn main() { + put_n(Num.gcd(12, 18)); put_c(32); put_n(Num.pow(2, 10)); put_c(32); put_n(Num.isqrt(50)); put_c(10); + put_n(Text.atoi("123") + 1); put_c(10); + return 0; +} +``` + +output: + +```text +6 1024 7 +124 +``` + +### host services + +`syscall(op, a, b, c)` passes a request through the vm's syscall gate. the runtime defines names such as `SYS_OPEN`, `SYS_READ`, `SYS_WRITE`, `SYS_GETENV`, and `SYS_TIME`. unsupported operations return -1. `Host.now()` wraps the clock request. + +`getenvs(name)` returns a fresh word string or 0. the library also supplies `argc`, `argv`, `envc`, and `envp`; a program read on stdin has `` as its first argument. gate calls must be made from ordinary code, outside an `o3` region. + +[example source](../examples/learn/21-host.zc): + +```zc +include "src/lib/std.zc"; +fn main() { + put_n(Host.now() > 0); put_c(10); + return 0; +} +``` + +output: + +```text +1 +``` + +### raw linux syscalls + +`Host.raw` uses the kernel's own syscall number and argument convention. it returns the result or a negated errno. the syscall numbers differ by architecture: this example uses x86-64 linux `getpid`, number 39. + +this path bypasses the vm's syscall checks. pointer arguments must use the host addresses required by that syscall. the example does not take pointer arguments and is skipped on other hosts. + +[example source](../examples/learn/22-raw-x86.zc): + +```zc +include "src/lib/std.zc"; +fn main() { + put_n(Host.raw(39, 0, 0, 0, 0, 0, 0) > 0); put_c(10); + return 0; +} +``` + +output: + +```text +1 +``` + +### input after the source + +a backtick outside a comment or literal ends the source unit. bytes after it are the program's input, read with `get_c`. `eof()` reports the end. this complete file includes both the program and the input `abc`; the program reads only the first two characters. + +[example source](../examples/learn/24-backtick.zc): + +```zc +fn main() { + put_c(get_c()); put_c(get_c()); put_c(10); + return 0; +} +`abc +``` + +output: + +```text +ab +``` + +### calling c + +`extern` declares a native function with its c types. `cfunc(f)` produces a native function pointer so c can call back into zc. use `word*` for a pointer to vm words, `char*` for the byte-string convention of a native c signature, and `double` for a zc float. + +this example calls `sqrt`, then calls `qsort` with a zc comparison callback. native calls require the full native core; the microcore and javascript runtime do not implement them. struct arguments have additional abi restrictions described in the reference. + +[example source](../examples/learn/23-c-abi.zc): + +```zc +fn put_f(x) { asm { x @ #1220 } return 0; } +extern double sqrt(double); +extern void qsort(word *base, long n, long size, void *cmp); +int cmp(int* a, int* b) { return *a - *b; } +fn main() { + let a[4]; + a[0] = 5; a[1] = 3; a[2] = 9; a[3] = 1; + put_f(sqrt(2.0)); put_c(10); + qsort(a, 4, 8, cfunc(cmp)); + put_n(a[0]); put_c(32); put_n(a[1]); put_c(32); put_n(a[2]); put_c(32); put_n(a[3]); put_c(10); + return 0; +} +``` + +output: + +```text +1.414214 +1 3 5 9 +``` + +## types and containers + +### arrays and methods + +`[n]T` is an array stored in place. `[]T` is a growable handle that owns its items, created with `new([]T)` or `new([]T, n)`. `push`, `pop`, `resize`, `len`, and `cap` operate on it. `fixed []T` has a length that cannot change. + +struct methods receive a hidden `this` parameter: `p.get()` passes `p`. static functions belong to the struct and are called as `cnt.make()`. freeing a container also frees its owned children. + +[example source](../examples/learn/19-growable.zc): + +```zc +struct cnt { + int n; + static int make() { return 3; } + int get() { return this.n; } +}; +fn main() { + []int xs = new([]int); + push(xs, 4); push(xs, 5); + put_n(len(xs)); put_c(10); + put_n(pop(xs)); put_c(10); + put_n(cnt.make()); put_c(10); + return 0; +} +``` + +output: + +```text +2 +5 +3 +``` + +### generic functions and structs + +type parameters follow the name: `record` and `keyof`. the compiler creates a distinct function instance for each set of concrete types. you can supply type arguments explicitly or infer them from the call. + +`record` stores its `point` field in place. inside an instantiated function, `sizeof` uses the concrete type. a generic body is checked when it is instantiated, so an unused template can contain a mistake that has not been reported yet. + +[example source](../examples/learn/15-generics.zc): + +```zc +struct record { K key; V val; }; +struct point { int x; int y; }; +K keyof(record* r) { return r.key; } +fn main() { + record* r = new(record); + r.key = 9; + put_n(keyof(r)); put_c(10); + return 0; +} +``` + +output: + +```text +9 +``` + +### tagged enums and matching + +an enum member can carry fields written as a block. a constructor such as `shape.circle(3)` returns an owned `shape*`. word 0 holds the member tag; the remaining words hold its payload. + +`case circle(r):` matches the tag and binds `r` only in that arm. a switch that misses a member without a `default` produces a warning. release or return the value according to the ordinary ownership rules. + +[example source](../examples/learn/16-tagged-enums.zc): + +```zc +enum shape { circle { int r; }, rect { int w; int h; }, dot }; +int area(shape* s) { + switch (s) { + case circle(r): return r * r * 3; + case rect(w, h): return w * h; + case dot: return 0; + } + return -1; +} +fn main() { + shape* s = shape.circle(3); + put_n(area(s)); put_c(10); + free(d*s); + shape* t = shape.rect(2, 5); + put_n(area(t)); put_c(10); + free(d*t); + return 0; +} +``` + +output: + +```text +27 +10 +``` + +## optimized regions + +### requesting native optimization + +`o3` can mark a function, block, or variable. the compiler emits a region for the full core to optimize and compile into native code. the microcore and javascript runtime execute the ordinary bytecode fallback. + +inside a region, locals and parameters must be typed. promoted variables have no address, so `&x` is not allowed for one. gate calls and closures are restricted. an untyped `new(n)` is zeroed without a type stamp. the fixed array below keeps its length, while the optimized block counts 143 matching integers. + +[example source](../examples/learn/18-o3.zc): + +```zc +o3 int sum(fixed []int a) { int s = 0; for (int i = 0; i < len(a); i++) s += a[i]; return s; } +fn main() { + fixed []int a = new(fixed []int, 5); + for (int i = 0; i < 5; i++) a[i] = i * i; + put_n(sum(a)); put_c(10); + o3 int hits = 0; + o3 { for (int i = 0; i < 1000; i++) if (i % 7 == 3) hits++; } + put_n(hits); put_c(10); + return 0; +} +``` + +output: + +```text +30 +143 +``` + +## closures + +### capturing a value + +`fn(x) { ... }` in an expression creates an owned closure. it copies the variables it uses from the surrounding function when it is created. calls use ordinary function syntax. + +bind a closure to a local before lending it to a plain parameter. to return an owned closure, use an owned return such as `return u*f;`; the result tag can be inferred or declared as `.u`. + +[example source](../examples/learn/25-closures.zc): + +```zc +fn apply(f, x) { return f(x); } +fn main() { + let k = 10; + let add = fn(x) { return x + k; }; + put_n(add(1)); put_c(10); + put_n(apply(add, 5)); put_c(10); + return 0; +} +``` + +output: + +```text +11 +15 +``` + +### nested captures and mutable state + +a nested closure can capture a variable from more than one enclosing function. each enclosing closure carries the values needed by the next one. + +captures are by value, but a closure can change its own copy. `tick` keeps its counter between calls; the `count` in `main` stays 0. changing closure state does not write back into the function that made it. + +[example source](../examples/learn/26-closure-state.zc): + +```zc +fn main() { + let base = 2; + let outer = fn(x) { + let inner = fn(y) { return y + base + x; }; + return inner(100); + }; + put_n(outer(7)); put_c(10); + let count = 0; + let tick = fn(d) { count = count + d; return count; }; + put_n(tick(1)); put_c(32); put_n(tick(4)); put_c(10); + put_n(count); put_c(10); + return 0; +} +``` + +output: + +```text +109 +1 5 +0 +``` + +## the optional typechecker + +`typecheck on;` enables type checking for the declarations that follow; `typecheck off;` disables it. errors report the expected and actual types. the lifetime analyzer runs independently of this switch. a literal 0 can be a null pointer, `word` matches any type, and arrays can decay to pointers. + +## further reading + +[README.md](../README.md) is the full reference. `tests/` contains runnable feature checks, `tests/errors/` contains rejected lifetime examples, and `tests/typeerrors/` contains rejected type examples. `docs/design/` explains implementation decisions; `docs/history/` records earlier versions. + +use `make check` while editing and `make test` for the full available suite. enable `ZYTRACE=1` to inspect native translation, or `ZYHOT=1` on an llvm build to compile each block as early as possible. diff --git a/docs/design/adt.md b/docs/design/adt.md new file mode 100644 index 0000000..2011337 --- /dev/null +++ b/docs/design/adt.md @@ -0,0 +1,19 @@ +# tagged enums + +tagged enums let one pointer refer to a value with one of several payload shapes. a member with fields is declared using a block of field declarations; a member without fields has no block: + +```zc +enum shape { circle { int r; }, rect { int w; int h; }, dot }; +int area(shape* s) { + switch (s) { + case circle(r): return r * r * 3; + case rect(w, h): return w * h; + case dot: return 0; + } + return -1; +} +``` + +each constructor allocates an owned enum object and returns a pointer. word zero stores the tag; payload fields follow it. the object is sized for the largest member, and unused payload words are zeroed. a `case member(names)` arm tests the tag and binds that member's fields as locals scoped to the arm. a member omitted from a switch without `default` produces a warning, in typed and untyped code. + +see [the tagged enum example](../../examples/learn/16-tagged-enums.zc). the syntax and behavior described here are implemented by the parser, typechecker, code generator and language server. diff --git a/docs/design/handoff.md b/docs/design/handoff.md new file mode 100644 index 0000000..0b08048 --- /dev/null +++ b/docs/design/handoff.md @@ -0,0 +1,20 @@ +# implementation notes + +this note summarizes implemented language and tooling features that were previously tracked across design documents. feature details live in the project readme and examples. + +## language and library + +- struct methods and static functions support typed parameters and return types. the existing untyped form remains available. +- `src/lib/std.zc` groups helpers in structs of static functions, called with `Group.name(args)`. `Text`, `Bytes`, `Host`, `Arena` and `Buf` provide string, byte, host, arena and buffer helpers. linux `syscall` is host-only and bypasses vm checks. +- tagged enums support payload members, constructors that return owned enum pointers, and `case member(fields)` bindings in ordinary `switch` statements. omitted members produce a warning when there is no `default`. +- struct values can cross native calls on x86-64 sysv and linux arm64 aapcs64 for the interpreter, o3, llvm and jit paths described in the readme. apple arm64 remains unchecked. + +## language server + +the language server runs the compiler in check mode, so diagnostics and semantic highlighting use the same parser and symbol tables as compilation. hover supports declarations, parameters, locals, built-ins and documented keywords. it does not follow declarations reached through `include` yet. + +## optimization + +o3 remains the explicit way to request native regions; automatic tiering was dropped. llvm inlining and global copy propagation are implemented. see [tiering](tiering.md) for the rationale and [struct-by-value](struct-by-value.md) for native aggregate limits. + +the runtime memory map is generated from a preset. fixed code and compiler tables have finite capacity; the heap grows within the configured machine memory. see the project readme's memory map section for the current build controls. diff --git a/docs/design/memory.md b/docs/design/memory.md new file mode 100644 index 0000000..bcbdf35 --- /dev/null +++ b/docs/design/memory.md @@ -0,0 +1,106 @@ +# memory configuration + +## one image and one layout + +the compiler, runtime, and host must agree on their addresses. `genmap` writes a matching `memmap.h`, runtime source, and compiler source; `src/tools/automap.sh` builds an image against those files. rebuild the host against the generated headers. changing a header alone does not relocate an existing genome. compact compiler and run-only maps also put their heap above the fixed regions, so compiler storage can grow after startup. + +```sh +bin/genmap --preset default --check +bash src/tools/automap.sh small build/small +bin/genmap --preset default --code 65536 --symbols 256 --locals 64 --fwd 512 --pool 8192 --hash 1024 -o build/custom +``` + +run `bin/genmap --help` for the table, heap, genome, and stack options. sizes are in vm words unless an option says otherwise; one word is eight bytes. `--snap-cap` selects the initial snapshot reservation and `--quote-cap` selects an initial token count, with three words per token. both regions grow after startup. + +## layout inventory + +the generated addresses reserve initial storage. native code-address tables start at `PW` and grow together when code needs new addresses; the microcore and javascript execute within their mapped memory. every host must use the same layout as its image. + +| region | build control | runtime behavior | +| --- | --- | --- | +| initial emitted program, through `CODE_END` | `--code` | links stable heap segments when full | +| type arena below `ZFP` | `--types` | stable type chunks and a growing index | +| compiler names and columns | `--hash`, `--pool`, `--symbols`, `--locals`, `--fwd` | grow independently | +| compiler scratch regions | `--snap`, `--quote`, `--src`, `--seq`, `--ctx`, `--loop` | grow independently | +| enum members, tagged-enum index, struct methods, generated source | `--enum-members`, `--tagged-enums`, `--methods`, `--generated-text` | grow independently | +| operator columns | `--binary`, `--unary`, `--assignments`, `--keywords` | grow independently | +| assembly labels and patches at `R_ASM` | `--asm-labels`, `--asm-refs` | columns grow | +| exit storage at `R_EXIT` | `--exit` | grows | +| genome reservation, from `GORG` to `FBOT` | `--genome` | existing image addresses stay fixed | +| frame region, from `FBOT` to `FTOP` | `--frame` | adds stable heap segments | +| return region, from `RS0` to `MARK0` | `--return` | copies live return addresses to larger storage | +| collector work at `MARK0` | `--mark` | grows during collection | +| data region, from `DSLO` to `DSHI` | `--data` | copies live operands to larger storage | +| initial heap | `--heap`, `--heap-lo` on compact maps | grows when the host supports it | +| total host memory | `--total`, host startup options | growth limit can change during execution | + +`--words` selects the fixed-map size; unused words extend the initial data region. `--total` selects the host reservation for a growing map. inside `genmap`, the fixed-map size is called `MW` and the host reservation `TW`; generated headers name them `PW` and `MW`. `HEAP_HI` is the initial heap end, not its eventual limit. `SYSGATE` stays at `ZFP + 2`, and the linked image supplies `GORG`. + +parameter tags, native calling conventions, packed token positions, and opcode formats also have limits. these describe the language or machine interface; they are separate from the storage capacities above. their parser buffers grow independently; for example, an enum index can grow while one tagged enum keeps its 63-member exhaustiveness mask. function parameter tags and generic arity retain their current encoding. packed token positions retain a 24-bit line, 16-bit token length, and 21-bit byte offset; this is an encoding constraint, not a growing buffer. + +## compiler tables during execution + +the generated layout supplies initial storage. the following tables grow when they fill: + +| storage | growth behavior | explicit reservation | +| --- | --- | --- | +| symbols and their types | copy columns; symbol indices stay valid | `reserve_symbols(n)` | +| locals and analyzer facts | copy every local column | `reserve_locals(n)` | +| forward references | copy patch columns; links retain indices | `reserve_fixes(n)` | +| intern hash | rehash pointers; interned spellings stay at their old addresses | `reserve_names(n)` | +| string pool | add larger chunks; old handles and symbol-chain heads stay valid | `poolchunk` sets the next chunk's minimum size | +| type records and type index | add stable chunks and grow the index | automatic | +| loop-range facts, joined paths, queued o3 regions | copy records before appending; build controls are `--facts`, `--paths`, `--regions` | automatic | +| analyzer snapshots and exits | copy storage; saved offsets stay valid | `reserve_R_SNAP(n)`, `reserve_R_EXIT(n)` | +| source, loop, and compiler-context stacks | copy complete frames | `reserve_R_SRC(n)`, `reserve_R_LOOP(n)`, `reserve_R_CTX(n)` | +| quote tokens and operator sequences | copy storage before appending | `reserve_R_QBUF(n)`, `reserve_R_SEQ(n)` | +| emitted code | link stable heap segments; code addresses remain valid | `reserve_code(n)`, `code_chunk` | +| operator columns | copy columns before adding an operator | `reserve_binary(n)`, `reserve_unary(n)`, `reserve_assignment(n)`, `reserve_operators(n)` | +| execution frames | add segments without moving older local addresses | `reserve_frames(n)` | + +reservation functions take minimum capacities and round upward. they can be called by running programs or immediate functions. scratch reservations count words; source frames use 32 words, loop frames use eight, and compiler contexts use 34. local and symbol capacities count entries. the intern hash stays a power of two and grows before it is half full. + +new chunks and columns are collector roots. garbage collection can reclaim replaced heap columns, while chunk chains retain interned text and type records for the current compiler state. fixed map storage remains reserved; growing a table does not reuse another map region. + +## language server + +document slots, uri text, hover text, declaration types, parsed type spans, resolved names, comment ranges, and diagnostics grow independently. hint deduplication uses a growing set of source positions. local name lookup uses growing name heads and shadow links; leaving a scope restores its previous bindings. a large source file must not lose its later diagnostics because earlier hints filled a buffer. the editor bundle must be rebuilt when the server changes; packaging always rebuilds its image. + +inference function records retain stable addresses while their index grows. inference edits, replacement text, and annotation verification buffers also grow; later declarations are not dropped at the original function-count or edit-count limits. protocol method names, request identifiers, json keys, nesting paths, and header lines also grow; request identifiers are echoed in full. + +## freestanding backing + +the stub and board ports reserve `MICRO_PORT_WORDS` cells at build time; it defaults to the initial image and heap size. the vm's active size and growth limit still change at runtime, within that physical backing. set the macro when linking a board with more reserved ram. a rejected physical allocation preserves the current memory. hosted maps keep their heap above the fixed regions; the historical `fixed` preset retains its bounded layout for compatibility. + +## host memory and image storage + +the default native map places a growing heap above the fixed regions. hosted runners accept `TINYVM_MEMORY_WORDS` for initial memory and `TINYVM_MAX_MEMORY_WORDS` for its growth limit. javascript also accepts `memoryWords` and `maxMemoryWords` when constructing a vm. invalid configurations are rejected before execution. + +```sh +TINYVM_MAX_MEMORY_WORDS=16777216 bin/zy < examples/hello.zc +TINYVM_MEMORY_WORDS=4194304 bin/zym < examples/hello.zc +``` + +the native core reserves a stable backing allocation up to its startup maximum; increasing the available size keeps addresses and native code pointers valid. the microcore and javascript vm replace backing allocations when they grow, preserving existing contents. native bounds use the reservation; portable bounds use the current allocation. memory syscall `9` with argument `4` requests growth, and argument `5` changes the growth limit during execution. a limit cannot fall below the current size. the native limit cannot exceed its startup reservation. + +data and return stacks grow automatically before their guard space is exhausted. `reserve_data(n)` and `reserve_returns(n)` request minimum capacities during execution; failure returns `-1` and preserves the old stack. live operands and return addresses are copied before old heap storage is returned to its free list. locals remain in frame storage, so pointers into locals keep their addresses. raw indices returned by `dsp()` describe the current operand stack and must be read again after a reservation. + +the host takes stack blocks directly from the runtime's buddy free lists. it does not call vm code or collect while servicing an overflow. when it needs another heap extent, the host's configured memory limit still applies. newly allocated stack blocks are excluded from user-object counts and are protected if collection is already running. operand-stack roots use the current stack's live range. + +frame segments grow on the heap, preserve pointers into older frames, and return empty segments to the allocator. collection scans only the live ranges of each segment. `frame_chunk` controls the minimum size of the next segment; `reserve_frames(n)` raises it. the growth helper uses a guard region to allocate without recursively growing its own frame. program emission links new segments through `reserve_code(n)`, with `code_chunk` controlling their minimum size. earlier functions and globals retain their addresses. strings, quotes, global arrays, and enhancement recipes reserve contiguous storage. + +optimizer regions that cross a segment boundary run as ordinary bytecode. image output grows independently through `build_image_grow(org, initial)`; its length is returned, and `image_words` and `image_size` expose its output. `build_image(org, dst)` retains the explicit-destination interface. a finished artifact still needs a host layout whose genome reservation fits it; `src/tools/automap.sh` sizes that reservation before linking. + +assembly columns, enum members, tagged-enum indices, struct methods, and generated source grow. native program-address metadata grows through memory syscall `9`, argument `9`; a denied request preserves the previous tables. annotation records and edit buffers also grow, within the host’s configured memory limit. generated initial addresses remain part of the image format; language and machine-interface limits do not change when storage grows. + +mark work grows through `reserve_mark(n)`, including during a collection. generic templates and instances grow through `reserve_generics(n)` and `reserve_instances(n)`. closure frames and capture rows grow through `reserve_closures(n)` and `reserve_captures(n)`. lexer text, documentation text, and stored signatures also grow. documentation objects keep stable addresses, and compiler storage is excluded from `live_objects()`. + +## verification + +`tests/drivers/compiler_growth_test.py` checks locals, distinct array types, long names, generic records, captures, and collector work, then reserves larger tables and scratch stacks and runs collection. `tests/drivers/frame_growth_test.py` checks deep recursion, oversized frames, and collection with a pointer into an older segment. `tests/drivers/inference_growth_test.py` checks 600 functions and 5000 local declarations. `tests/drivers/core_lsp_test.js` opens every maintained core zc source through the editor protocol and checks diagnostics and highlighting. `tests/drivers/memory_limits_test.py` checks startup validation and runtime growth on the native, microcore, and javascript hosts. a bootstrap fixpoint alone does not verify runtime growth. collection is conservative: code literals can resemble heap addresses, particularly on compact maps, so reclamation counts can differ between layouts. checks must verify collection progress and retained roots without assuming identical counts. + +## bootstrap storage + +stage 0 reads source and grows its temporary image buffer on the host. `MAXW`, when supplied at build time, selects an initial image reservation; it is not a final size limit. the resident compiler builds images with `build_image_grow(org, initial)` and returns their current buffer through `image_words`. image output is temporary storage, so it can move while the logical addresses recorded in the image stay unchanged. a matching generated layout must still fit the finished artifact before it is loaded. + +vm literals carry 63 signed bits in a single odd word. the compiler emits a short sequence for other 64-bit values; float payloads use their own full word. `setlit` patches one encoded word and requires a value that fits that single-word format. diff --git a/docs/design/storage-analysis.md b/docs/design/storage-analysis.md new file mode 100644 index 0000000..59fbcde --- /dev/null +++ b/docs/design/storage-analysis.md @@ -0,0 +1,33 @@ +# bounds and lifetime findings + +## what the compiler knows + +the compiler records constant element counts for `new(n)`, `new(T, n)`, fixed arrays, and newly created array handles. a direct local alias shares the allocation's facts. a definite `free` or `recycle` ends that allocation's lifetime; closing a scope ends its stack storage and automatically released owned objects. + +an index outside a known length produces a warning. dereferencing, indexing, or accessing a field after a recorded lifetime end also produces a warning. these findings apply to unchecked code too. existing ownership errors and fixed-array index errors keep their current behavior. warnings do not prevent compilation. + +```zc +unchecked fn example() { + let p = new(int, 3); + let q = p; + q[3] = 1; // warns: index 3 is outside length 3 + free(p); + return q[0]; // warns: this allocation was freed +} +``` + +## what stays unknown + +this is basic analysis of local facts, not a proof of the whole program. branches, loops, short-circuit operators, raw assembly, and nested compilation discard facts instead of merging uncertain paths. unknown callees invalidate facts about allocations passed to them. array mutations invalidate recorded lengths. pointer arithmetic, explicit casts, pointer-valued fields, stored aliases, and function return contracts are not inferred by this pass. + +an unknown fact does not produce a definite warning. runtime array checks still run. the pass may miss an error across one of these boundaries; silence is not a safety guarantee. the broader analysis in [BORROW.md](../BORROW.md) remains a design plan. + +## editor previews + +array and pointer hovers show the known element count and lifetime at that reference. a live stack allocation names its scope exit as its lifetime end. a live heap allocation ends when freed or released by its owner. a reference after a recorded end names that line. unavailable sizes and lifetimes say `unknown`. + +facts are copied into each reference's editor record while the compiler parses it. a later `free` therefore does not make an earlier hover claim the object was already dead. block comments and inferred type text remain part of the same markdown preview. + +## focused checks + +`node tests/drivers/storage_analysis_test.js bin/zclsp` checks known bounds, packed elements, direct aliases, free and scope lifetimes, hover metadata, and safe or uncertain neighboring cases. diff --git a/docs/design/struct-by-value.md b/docs/design/struct-by-value.md new file mode 100644 index 0000000..bbc8f26 --- /dev/null +++ b/docs/design/struct-by-value.md @@ -0,0 +1,9 @@ +# structs in native calls + +zc structs whose fields are one word each (`int`, `float` or pointers) can be passed by value to `extern` functions, and native functions can return structs. the interpreter, o3, llvm and jit paths support sysv x86-64 and linux arm64 aapcs64. apple arm64 is not verified. + +arguments are classified according to the host ABI. on sysv x86-64, structs up to 16 bytes use integer or sse registers when the whole value fits; otherwise they go on the stack. larger structs go on the stack. on aapcs64, homogeneous float structs of up to four words use v registers, integer structs of up to two words use x registers, and other arguments follow the platform ABI. + +returns are limited to seven words. the call stub allocates an owned zc object and passes its address through the call block; the native ABI determines how the result is filled. the llvm descriptor path does not handle grouped struct signatures, so these calls use the generic native-call path. + +see `tests/programs/structval.zc` and `tests/programs/structret.zc` for executable examples. platform-specific behavior beyond x86-64 sysv and linux arm64 aapcs64 remains unverified. diff --git a/docs/design/tiering.md b/docs/design/tiering.md new file mode 100644 index 0000000..21a74c9 --- /dev/null +++ b/docs/design/tiering.md @@ -0,0 +1,7 @@ +# automatic tiering + +automatic tiering was dropped. the `o3` keyword remains the explicit way to request an optimized native region. + +tiering would require runtime information the compiler currently records only for `o3` functions, including frame layout, address-taken slots, arrays and argument placement. `o3` also uses a different calling convention for dynamic arrays and enforces compile-time restrictions such as closure and gate-store limits. promoting an ordinary function would therefore need either a uniform calling convention, duplicate compiled versions with call-site switching, or equivalent runtime checks. + +keeping `o3` explicit avoids those changes. the existing optimization work—including direct `o3` calls, llvm inlining, global constant and copy propagation, loop-invariant code motion and cross-block common-subexpression elimination—remains available. diff --git a/docs/history/01-golf/notes.md b/docs/history/01-golf/notes.md new file mode 100644 index 0000000..f8c8b5d --- /dev/null +++ b/docs/history/01-golf/notes.md @@ -0,0 +1,10 @@ +# the golfed vm + +no code from this stage survives except where a code/ folder sits next to this file. these are the notes as they were written in the transcript. + +## message 1 + +> codegolf a tiny cpu vm in C89. the goal is to achieve the lowest character count possible. include an assembler as well (scored separately) and a hello world program (not scored, but elegance and simplicity is a bonus) +> it must be capable of re-entrant code and a realistically workable instruction set. + +**summary:** a two-stack forth-style machine. the vm was 384 bytes: one ternary chain inside `while(o=m[p++])`, the top two stack items cached in `a` and `b`, the program loaded with `read(open(...))` so it needs no headers, and both stacks inside the 65536-word memory. it had 22 opcodes including div and mod, a two-word literal, and jump/call targets popped from the stack. the assembler was 348 bytes and two-pass, using `strcspn` instead of `strchr` so the implicit int return stays safe on 64-bit. hello world was a pointer loop over a string with a zero terminator. re-entrancy was shown with a recursive factorial plus a recursive decimal printer. diff --git a/docs/history/02-hardware/notes.md b/docs/history/02-hardware/notes.md new file mode 100644 index 0000000..e5ad911 --- /dev/null +++ b/docs/history/02-hardware/notes.md @@ -0,0 +1,46 @@ +# closer to hardware + +no code from this stage survives except where a code/ folder sits next to this file. these are the notes as they were written in the transcript. + +## message 2 + +> your cpu should also be realistically close to hardware (translating it to logical circuitry hardware should be fairly easy) and sport an acceptably fast execution (if hypothetically ported to hardware, not in software) +> +> assume i/o is handled by two external functions, `char Q()` (getchar) and `void G(char)` (putchar) and a state variable J (boolean, shows if input is empty). +> +> don't be afraid to use macros. + +**summary:** no reply was sent to this message at the time. it was answered together with message 3. + +## message 3 + +> pro tip: if you define a macro as `!o--`, you can save 2 characters per if condition +> oh and btw, newlines are free + +**summary:** used the tip as `#define X !--o?`, with pre-decrement because opcode 0 (halt) is already handled by the loop, and with the `?` folded into the macro and free newlines used as separators. the hardware-oriented redesign: bit 0 of each word is a literal flag, so literals take one cycle like the j1 cpu. div and mod were dropped because they can't finish in one cycle and were replaced by an arithmetic shift right, giving 8 alu ops that decode from 3 bits. every instruction does a fixed amount of work with at most one data memory access, and call and jump share a datapath (`r-=o`). i/o now goes through `Q`, `G` and a `?` op that reads `J`. the result was the vm at 339 bytes and the assembler at 346. the final golfed code: + +```c +#define X !--o? +int m[1<<16],p,s=65e3,r=6e4,o,a,b;main(c,v)char**v;{read(open(v[1],0),m,s);while(o=m[p++])a=m[s],b=m[s+1],o&1?m[--s]=o>>1:(o/=2,X +m[s]=m[a]:X +m[a]=b,s+=2:X +m[s]=m[s-~a]:X +s++:X +m[s]=b,m[s+1]=a:(o-=2)<1?m[r]=p,r-=o,p=a,s++:X +p=b?p:a,s+=2:X +p=m[--r]:X +m[--s]=Q():X +m[--s]=J:X +G(a),s++:(m[++s]=X +b+a:X +b-a:X +b*a:X +b&a:X +b|a:X +b^a:X +b>>a:b now let's move in stages. write a compiler that takes this assembler's code and converts it into darwin arm + +**summary:** an ungolfed c89 compiler from vm assembly to darwin arm64 assembly. every word got its own label, the image was emitted as an addressable data array, x19/x20/x21 held the memory base and the two stack pointers, and computed jumps went through a table of offsets. a literal followed by a jump became a direct branch. `Q` and `G` went through `sxtb`, since char is signed on apple arm64, and `J` was read with `ldrb`. it was verified by assembling with llvm-mc for darwin, then rewriting to linux with sed and running under qemu, where it matched the vm byte for byte. this version couldn't handle self-modifying code. diff --git a/docs/history/05-hybrid/notes.md b/docs/history/05-hybrid/notes.md new file mode 100644 index 0000000..e111cec --- /dev/null +++ b/docs/history/05-hybrid/notes.md @@ -0,0 +1,9 @@ +# the assembler in assembly, and the hybrid compiler + +no code from this stage survives except where a code/ folder sits next to this file. these are the notes as they were written in the transcript. + +## message 5 + +> can you write the assembler in assembly and pair the code with the assembly and the assembler so that it can re-wire itself at runtime? or maybe include an interp for self-modification, otherwise fns are exec'd native + +**summary:** both. `aslib.s` is the assembler written in vm assembly as a library (`a.src`, `a.dst`, `a.org`, `a.lab`, then `a.run c`), and with `asmain.s` it's a 568-word standalone assembler. it's byte-identical to the c assembler and reaches a fixpoint when assembling itself. this is also where the `:;comment` trick came from. the compiler became a hybrid. the image is split into basic blocks, each block counts how many of its words differ from the original image, and stores into code go through a slow path that updates the counters. clean blocks run native, while dirty blocks and anything past the image run in an interpreter compiled into the same binary, which hands control back to native code whenever it lands in a clean block. `cc -t` printed `[` and `]` at each switch. the demo rewired a function at runtime to point at freshly assembled code and then restored it. diff --git a/docs/history/06-choosing/notes.md b/docs/history/06-choosing/notes.md new file mode 100644 index 0000000..1b9be6b --- /dev/null +++ b/docs/history/06-choosing/notes.md @@ -0,0 +1,10 @@ +# picking a favorite + +no code from this stage survives except where a code/ folder sits next to this file. these are the notes as they were written in the transcript. + +## message 6 + +> which one do you like more? and which is more performant? +> keep that one, then port to using llvm ir so that it can be platform independent + +**summary:** no reply was sent at the time. the llvm port was dropped when message 7 changed direction. the question was answered at the start of the reply to message 7: the hybrid was the favorite because it kept the vm's semantics exact. the plain compiler was slightly faster on code that never rewrites itself, but it silently ran stale code once anything did. the zygote beats both. diff --git a/docs/history/07-zygote/code/README.md b/docs/history/07-zygote/code/README.md new file mode 100644 index 0000000..924c48b --- /dev/null +++ b/docs/history/07-zygote/code/README.md @@ -0,0 +1,168 @@ +# tinyvm zygote: notes and transcript + +a tiny stack cpu that started as a c89 code golf entry and ended up as a self-hosting runtime. a small native core jit-compiles vm code to arm64, and the assembler lives in vm memory next to the program, where the program can modify it. + +this folder only has the latest code. earlier stages are described in the transcript below, but their code isn't included. + +## files + +`core.c` is the native core. it holds the arm64 jit, the stubs and the invalidation logic, and is the only native code in the system. `gen.s` is the genome header and boot loader, in vm assembly. `aslib.s` is the assembler, written in vm assembly. `mkgenome.c` is a build tool that assembles `gen.s` + `aslib.s` at address 48000 into `genome.h`, which is the generated result and is checked in. `repl.s` is a demo that mutates the resident assembler and repeatedly assembles and runs input chunks. `io_linux.c` is the `Q`/`G`/`J` test harness. `test.sh` builds for arm64 linux and runs everything under qemu. `examples/` has hello, a recursive factorial (prints in binary), echo, a self-patching test, and `asmain.s`, the standalone wrapper that turns `aslib.s` into a stdin-to-stdout assembler. + +## build + +``` +cc -o mkgenome mkgenome.c && cat gen.s aslib.s | ./mkgenome >genome.h # only after changing gen.s or aslib.s +cc -O2 core.c io.c -o zy # io.c provides char Q(void), void G(char), and J +./zy >1` (31 bits, sign extended). an even word is an opcode, `w/2`. there's a data stack and a return stack, both stored inside memory. the data stack grows down from 65000 and the return stack grows up from 60000. + +``` +0 q halt 5 ~ swap 10 , in (Q) 15 * mul 20 < less +1 @ load 6 c call 11 ? empty (J) 16 & and +2 ! store 7 j jump 12 . out (G) 17 | or +3 $ pick 8 z jz 13 + add 18 ^ xor +4 _ drop 9 r ret 14 - sub 19 > shr +``` + +stack effects: `!` is `(v addr --)`, `$` is `(n -- copy of the nth item)` so `0 $` is dup, `z` is `(cond addr --)` and jumps when cond is 0, and `c` and `j` take their target from the stack. there's no div or mod, because a divider can't finish in one cycle in hardware. division is done in software. + +assembler syntax uses whitespace-separated tokens. `:name` defines a label, a single op character is that opcode, `"text` emits raw characters, `#x` emits a raw number or label address, and anything else becomes a literal push of a number or label. when a label is defined twice, the last definition wins. a label nobody references emits nothing, so `:;like_this` works as a comment. + +## memory map and abi + +``` +0.. the program (assembled at origin 0 by the boot loader) +20000..44999 boot source buffer, one char per word (free after boot) +45000..47999 boot label table (free after boot) +48000 genome: 48000 boot, 48002 a.run (call with 48002 c), + 48004 &a.src 48005 &a.dst 48006 &a.org 48007 &a.lab 48008 &a.n 48009 &a.ops +60000 up return stack +65000 down data stack +``` + +to use the resident assembler, store a source address into `48004 @`, the destination into `48005 @`, the run-at origin into `48006 @` and a label scratch area into `48007 @`, then do `48002 c`. the word count ends up in `48008 @`. `48009 @` points to the mnemonic table `q@!$_~cjzr,?.+-*&|^><`, which you can rewrite. boot limits: the program has to fit in 20000 words and the source in 25000 chars. + +## how the core works + +registers: x19 holds the memory base, x20 the data stack pointer, x21 the return stack pointer, x22 the translation table and x23 the state block (memory, s, r, table, coverage map, Q, G, &J, store hook). + +compilation is lazy. the first time control reaches an address, that block is translated from live memory. a block ends at call, jump, ret or halt. a `z` doesn't end it: the not-taken side keeps compiling, which makes superblocks. a literal followed by `c`, `j` or `z` is fused into a constant target. every exit goes through a 5-instruction dispatch stub that bounds-checks the target, loads `tab[t]` and branches to it directly when it's already compiled. otherwise it returns to c, which compiles the block and re-enters. + +self-modification is handled with a byte map of which words some live translation covers. a store checks that byte inline: an uncovered target, the usual case, gets a plain store. a covered target calls the hook, which drops every translation containing that word and makes the jit code return to c at the next instruction, so even patches to the very next word take effect. writing back the same value is a no-op. if the 16 mb code buffer fills, everything is flushed. + +platform: arm64 only. the darwin path uses `MAP_JIT`, `pthread_jit_write_protect_np` and `sys_icache_invalidate`. linux uses rwx mmap and `__builtin___clear_cache`. only the linux arm64 path has actually been run, under qemu. the darwin path follows apple's documented recipe but hasn't been tested. under the hardened runtime it also needs the allow-jit entitlement. + +differences from the golfed reference vm, which leaves all of these undefined: shift counts wrap at 32, stores outside memory are ignored, and jumping outside memory halts. + +## test results (linux arm64 via qemu) + +everything in `test.sh` passes: hello, factorial, echo after the backtick, self-patching that matches the golfed vm byte for byte, the self-hosted assembler running on the zygote and producing the same bytes as the c assembler, and the repl that prints `hi` and then `yo` from two chunks assembled into the same address. the trace shows the first chunk's translation being dropped. earlier in the session, `asm.s` running on the zygote reproduced the c assembler's output for its own source too. a 20 million iteration loop took 0.54 s on the zygote against 2.18 s for the golfed vm interpreter under the same qemu, and that 0.54 includes assembling the source at startup. + +## open ends + +- the llvm ir port was asked for and then dropped in favor of the zygote before any of it was written. +- the assembler library uses globals, so it isn't re-entrant. code that it assembles at runtime shouldn't call it while an outer `a.run` is still running. +- translations are only reclaimed by a full flush, and the hook rescans the block list on every store to a covered word. that's fine unless code rewrites itself in a hot loop. +- none of the later stages are golfed. the golf-sized code is the reference vm and assembler from stage 2. + +--- + +## transcript + +each of my messages is quoted exactly, followed by a summary of the reply. + +### 1 + +> codegolf a tiny cpu vm in C89. the goal is to achieve the lowest character count possible. include an assembler as well (scored separately) and a hello world program (not scored, but elegance and simplicity is a bonus) +> it must be capable of re-entrant code and a realistically workable instruction set. + +**summary:** a two-stack forth-style machine. the vm was 384 bytes: one ternary chain inside `while(o=m[p++])`, the top two stack items cached in `a` and `b`, the program loaded with `read(open(...))` so it needs no headers, and both stacks inside the 65536-word memory. it had 22 opcodes including div and mod, a two-word literal, and jump/call targets popped from the stack. the assembler was 348 bytes and two-pass, using `strcspn` instead of `strchr` so the implicit int return stays safe on 64-bit. hello world was a pointer loop over a string with a zero terminator. re-entrancy was shown with a recursive factorial plus a recursive decimal printer. + +### 2 + +> your cpu should also be realistically close to hardware (translating it to logical circuitry hardware should be fairly easy) and sport an acceptably fast execution (if hypothetically ported to hardware, not in software) +> +> assume i/o is handled by two external functions, `char Q()` (getchar) and `void G(char)` (putchar) and a state variable J (boolean, shows if input is empty). +> +> don't be afraid to use macros. + +**summary:** no reply was sent to this message at the time. it was answered together with message 3. + +### 3 + +> pro tip: if you define a macro as `!o--`, you can save 2 characters per if condition +> oh and btw, newlines are free + +**summary:** used the tip as `#define X !--o?`, with pre-decrement because opcode 0 (halt) is already handled by the loop, and with the `?` folded into the macro and free newlines used as separators. the hardware-oriented redesign: bit 0 of each word is a literal flag, so literals take one cycle like the j1 cpu. div and mod were dropped because they can't finish in one cycle and were replaced by an arithmetic shift right, giving 8 alu ops that decode from 3 bits. every instruction does a fixed amount of work with at most one data memory access, and call and jump share a datapath (`r-=o`). i/o now goes through `Q`, `G` and a `?` op that reads `J`. the result was the vm at 339 bytes and the assembler at 346. the final golfed code: + +```c +#define X !--o? +int m[1<<16],p,s=65e3,r=6e4,o,a,b;main(c,v)char**v;{read(open(v[1],0),m,s);while(o=m[p++])a=m[s],b=m[s+1],o&1?m[--s]=o>>1:(o/=2,X +m[s]=m[a]:X +m[a]=b,s+=2:X +m[s]=m[s-~a]:X +s++:X +m[s]=b,m[s+1]=a:(o-=2)<1?m[r]=p,r-=o,p=a,s++:X +p=b?p:a,s+=2:X +p=m[--r]:X +m[--s]=Q():X +m[--s]=J:X +G(a),s++:(m[++s]=X +b+a:X +b-a:X +b*a:X +b&a:X +b|a:X +b^a:X +b>>a:b now let's move in stages. write a compiler that takes this assembler's code and converts it into darwin arm + +**summary:** an ungolfed c89 compiler from vm assembly to darwin arm64 assembly. every word got its own label, the image was emitted as an addressable data array, x19/x20/x21 held the memory base and the two stack pointers, and computed jumps went through a table of offsets. a literal followed by a jump became a direct branch. `Q` and `G` went through `sxtb`, since char is signed on apple arm64, and `J` was read with `ldrb`. it was verified by assembling with llvm-mc for darwin, then rewriting to linux with sed and running under qemu, where it matched the vm byte for byte. this version couldn't handle self-modifying code. + +### 5 + +> can you write the assembler in assembly and pair the code with the assembly and the assembler so that it can re-wire itself at runtime? or maybe include an interp for self-modification, otherwise fns are exec'd native + +**summary:** both. `aslib.s` is the assembler written in vm assembly as a library (`a.src`, `a.dst`, `a.org`, `a.lab`, then `a.run c`), and with `asmain.s` it's a 568-word standalone assembler. it's byte-identical to the c assembler and reaches a fixpoint when assembling itself. this is also where the `:;comment` trick came from. the compiler became a hybrid. the image is split into basic blocks, each block counts how many of its words differ from the original image, and stores into code go through a slow path that updates the counters. clean blocks run native, while dirty blocks and anything past the image run in an interpreter compiled into the same binary, which hands control back to native code whenever it lands in a clean block. `cc -t` printed `[` and `]` at each switch. the demo rewired a function at runtime to point at freshly assembled code and then restored it. + +### 6 + +> which one do you like more? and which is more performant? +> keep that one, then port to using llvm ir so that it can be platform independent + +**summary:** no reply was sent at the time. the llvm port was dropped when message 7 changed direction. the question was answered at the start of the reply to message 7: the hybrid was the favorite because it kept the vm's semantics exact. the plain compiler was slightly faster on code that never rewrites itself, but it silently ran stale code once anything did. the zygote beats both. + +### 7 + +> can you make it a zygote approach? +> a minimal core loads first, reads and compiles the program (either wholly or partially, for speed) and replaces it with itself. so it's like a jvm but self-contained and on the same level as itself, where the assembler can itself be modified in-memory to assemble different code. for this we need to ditch llvm + +**summary:** the current design, described above. the native core only does jit and glue. it loads the genome (boot loader + assembler, both vm code) at 48000. the boot loader reads source until eof or a backtick, assembles it at 0 and jumps there. compilation is lazy per block, straight from live memory, with superblocks through `z`, fused constant targets, and dispatch chained through a table. invalidation uses the coverage byte map plus a store hook, so self-modified code gets retranslated instead of interpreted. the resident assembler sits at fixed addresses, and `repl.s` rewrites its mnemonic table so `p` means out, then assembles and runs input chunks in a loop, overwriting the same address each time. verified under qemu on linux arm64, including the self-hosted assembler running on the zygote. about 4x faster than the golfed interpreter. + +### 8 + +> damn dawg you made an entire baremetal os? +> +> let's move to cowork. pack everything up alongside a transcript of every message i've sent and your own notes on what you've implemented in an md file, with the code, so i can move it. + +**summary:** not quite an os. it's a userland runtime. it still runs on darwin or linux, uses their mmap for executable memory, and gets i/o from the host through `Q`, `G` and `J`. there's no scheduler, no drivers and no hardware access, so a closer description is a tiny self-hosting jit, in the spirit of a jvm. the first packing attempt bundled every stage's code with a full test script. + +### 9 + +> nono we don't need all the code, just the recent ones. my msgs and your summaries are more important and all should be there + +**summary:** this package. only the zygote stack plus its examples and tests are included, and earlier stages appear only as summaries. the golfed reference vm and assembler are inlined under message 3, because they define the instruction set. diff --git a/docs/history/07-zygote/code/aslib.s b/docs/history/07-zygote/code/aslib.s new file mode 100644 index 0000000..fc36df8 --- /dev/null +++ b/docs/history/07-zygote/code/aslib.s @@ -0,0 +1,67 @@ +:;assembler_library +:;in:_a.src_source_text_one_char_per_word_zero_terminated +:;in:_a.dst_output_address_a.org_address_the_code_will_run_at_a.lab_label_scratch +:;out:_a.n_words_written +:a.run 0 a.pass ! 0 a.nl ! +:a.p0 a.src @ a.q ! 0 a.n ! +:a.lp a.skip c a.q @ @ 0 $ a.c ! a.pd z + a.c @ 34 - a.str z + a.c @ 58 - a.lbl z + a.q @ 1 + @ a.end c a.ch z + a.c @ a.op c 0 $ -1 ~ < a.ch1 z + 2 * a.emit c a.q @ 1 + a.q ! a.lp j +:a.ch1 _ +:a.ch a.c @ 35 - a.raw z + a.q @ a.val c 2 * 1 + a.emit c a.tend c a.lp j +:a.raw a.q @ 1 + a.val c a.emit c a.tend c a.lp j +:a.str a.q @ 1 + a.q ! +:a.s1 a.q @ @ 0 $ a.end c a.s2 z _ a.lp j +:a.s2 a.emit c a.q @ 1 + a.q ! a.s1 j +:a.lbl a.pass @ a.l1 z a.tend c a.lp j +:a.l1 a.q @ 1 + a.lab @ a.nl @ 2 * + ! + a.org @ a.n @ + a.lab @ a.nl @ 2 * + 1 + ! + a.nl @ 1 + a.nl ! a.tend c a.lp j +:a.pd a.pass @ a.p1 z r +:a.p1 1 a.pass ! a.p0 j + +:;(c_--_f)_true_for_whitespace_and_the_terminator +:a.end 0 $ 33 < ~ -1 ~ < & r +:;skip_whitespace_at_a.q +:a.skip a.q @ @ 0 $ a.sk1 z a.end c a.sk2 z a.q @ 1 + a.q ! a.skip j +:a.sk1 _ +:a.sk2 r +:;move_a.q_to_the_end_of_the_token +:a.tend a.q @ @ a.end c a.te1 z r +:a.te1 a.q @ 1 + a.q ! a.tend j +:;(w_--) +:a.emit a.dst @ a.n @ + ! a.n @ 1 + a.n ! r +:;(x_y_--_f)_compare_two_tokens +:a.eq a.y ! a.x ! +:a.e1 a.x @ @ a.end c a.y @ @ a.end c 1 $ 1 $ & a.e2 z _ _ 1 r +:a.e2 | a.e3 z 0 r +:a.e3 a.x @ @ a.y @ @ - a.e4 z 0 r +:a.e4 a.x @ 1 + a.x ! a.y @ 1 + a.y ! a.e1 j +:;(c_--_index_or_-1) +:a.op a.c ! 0 a.j ! +:a.o1 a.ops a.j @ + @ 0 $ a.o2 z a.c @ - a.o3 z a.j @ 1 + a.j ! a.o1 j +:a.o2 _ -1 r +:a.o3 a.j @ r +:;(q_--_v)_last_matching_label_else_decimal +:a.val a.t ! a.nl @ a.j ! +:a.v1 a.j @ a.vn z a.j @ 1 - a.j ! + a.lab @ a.j @ 2 * + @ a.t @ a.eq c a.v1 z + a.lab @ a.j @ 2 * + 1 + @ r +:a.vn a.t @ a.num j +:;(q_--_v)_like_atoi +:a.num a.x ! 0 a.neg ! 0 a.v ! + a.x @ @ 45 - a.n1 z a.x @ @ 43 - a.n2 z a.n3 j +:a.n1 1 a.neg ! +:a.n2 a.x @ 1 + a.x ! +:a.n3 a.x @ @ 48 - 0 $ 10 < 1 $ -1 ~ < & a.n4 z + a.v @ 10 * + a.v ! a.x @ 1 + a.x ! a.n3 j +:a.n4 _ a.v @ a.neg @ a.n5 z 0 ~ - +:a.n5 r + +:a.src #0 :a.dst #0 :a.org #0 :a.lab #0 :a.n #0 :a.pass #0 :a.nl #0 :a.q #0 +:a.c #0 :a.j #0 :a.t #0 :a.v #0 :a.neg #0 :a.x #0 :a.y #0 +:a.ops "q@!$_~cjzr,?.+-*&|^>< #0 diff --git a/docs/history/07-zygote/code/core.c b/docs/history/07-zygote/code/core.c new file mode 100644 index 0000000..41930f4 --- /dev/null +++ b/docs/history/07-zygote/code/core.c @@ -0,0 +1,276 @@ +/* zygote core: puts the genome (boot loader + assembler, both vm code) into + memory and runs it. every block of vm code is translated to arm64 the first + time it is reached, straight from live memory. stores that land on translated + words throw those translations away, so code that rewrites itself, including + the resident assembler, just gets retranslated on the next visit. + arm64 only (darwin or linux). ZYTRACE=1 logs translations on stderr. */ +#include +#include +#include +#include +#ifdef __APPLE__ +#include +#include +#endif +#include "genome.h" + +#ifndef JTYPE +#define JTYPE char +#endif +char Q(void); +void G(char); +extern JTYPE J; + +#define MW 65536 +#define CODEW (4 << 20) /* 16 MB of code, in instruction words */ +#define MAXB 65536 +#define BLKW 1024 /* longest block, in vm words */ + +static int m[MW]; +static unsigned char cov[MW]; /* 1 if a live translation covers the word */ +static void *tab[MW]; /* vm address -> translation */ +static struct { int a, b; } bl[MAXB]; /* live translations cover [a,b); a < 0 is dead */ +static int nbl, trace; +static unsigned *code, *cp, *x_enter, *x_exit, *x_disp, *x_first; +static long S[9]; /* m, s, r, tab, cov, Q, G, &J, store hook; read by the stubs */ + +enum { W0 = 0, W1 = 1, X9 = 9, X10, X11, M = 19, SP = 20, RP = 21, TAB = 22, ST = 23 }; + +static void E(unsigned x) { *cp++ = x; } +static void li(int d, int v) +{ + unsigned u = (unsigned)v; + E(0x52800000 | (u & 0xffff) << 5 | d); + if (u >> 16) E(0x72a00000 | (u >> 16) << 5 | d); +} +static void ldr_post(int t, int n, int i) { E(0xb8400400 | (i & 0x1ff) << 12 | n << 5 | t); } +static void ldr_pre(int t, int n, int i) { E(0xb8400c00 | (i & 0x1ff) << 12 | n << 5 | t); } +static void str_pre(int t, int n, int i) { E(0xb8000c00 | (i & 0x1ff) << 12 | n << 5 | t); } +static void str_post(int t, int n, int i) { E(0xb8000400 | (i & 0x1ff) << 12 | n << 5 | t); } +static void ldr_u(int t, int n, int o) { E(0xb9400000 | (o / 4) << 10 | n << 5 | t); } +static void str_u(int t, int n, int o) { E(0xb9000000 | (o / 4) << 10 | n << 5 | t); } +static void ldrx_u(int t, int n, int o) { E(0xf9400000 | (o / 8) << 10 | n << 5 | t); } +static void ldr_rr(int t, int n, int r, int ext) { E(0xb8600800 | r << 16 | ext << 13 | 1 << 12 | n << 5 | t); } +static void str_rr(int t, int n, int r, int ext) { E(0xb8200800 | r << 16 | ext << 13 | 1 << 12 | n << 5 | t); } +static void ldp_post(int t, int u, int n, int i) { E(0x28c00000 | ((i / 4) & 0x7f) << 15 | u << 10 | n << 5 | t); } +static void op3(unsigned op, int d, int n, int r) { E(op | r << 16 | n << 5 | d); } +static void addx_ext(int d, int n, int r, int ext) { E(0x8b200000 | r << 16 | ext << 13 | 2 << 10 | n << 5 | d); } +static void movw(int d, int r) { E(0x2a0003e0 | r << 16 | d); } +static void blr(int n) { E(0xd63f0000 | n << 5); } +static void b_to(unsigned *t) { long o = t - cp; E(0x14000000 | (o & 0x3ffffff)); } +static void cb_fix(unsigned *at, unsigned *to) { *at |= ((to - at) & 0x7ffff) << 5; } +#define SXTW 6 +#define UXTW 2 + +static void build_stubs(void) +{ + cp = code; + x_enter = cp; /* int enter(long *S, void *code) */ + E(0xa9bc7bfd); /* stp x29, x30, [sp, #-64]! */ + E(0x910003fd); /* mov x29, sp */ + E(0xa90153f3); /* stp x19, x20, [sp, #16] */ + E(0xa9025bf5); /* stp x21, x22, [sp, #32] */ + E(0xf9001bf7); /* str x23, [sp, #48] */ + E(0xaa0003f7); /* mov x23, x0 */ + ldrx_u(M, ST, 0); + ldr_u(X9, ST, 8); addx_ext(SP, M, X9, UXTW); + ldr_u(X9, ST, 16); addx_ext(RP, M, X9, UXTW); + ldrx_u(TAB, ST, 24); + E(0xd61f0020); /* br x1 */ + x_exit = cp; /* w0 = next vm address */ + op3(0xcb000000, X9, SP, M); E(0xd342fd29); str_u(X9, ST, 8); /* s = (x20 - x19) >> 2 */ + op3(0xcb000000, X9, RP, M); E(0xd342fd29); str_u(X9, ST, 16); + E(0xa94153f3); /* ldp x19, x20, [sp, #16] */ + E(0xa9425bf5); /* ldp x21, x22, [sp, #32] */ + E(0xf9401bf7); /* ldr x23, [sp, #48] */ + E(0xa8c47bfd); /* ldp x29, x30, [sp], #64 */ + E(0xd65f03c0); /* ret */ + x_disp = cp; /* w0 = target: chain through tab[] or fall back to c */ + E(0x7140401f); /* cmp w0, #65536 */ + E(0x54000002 | ((x_exit - cp) & 0x7ffff) << 5); /* b.hs exit */ + E(0xf8605ac9); /* ldr x9, [x22, w0, uxtw #3] */ + E(0xb4000009 | ((x_exit - cp) & 0x7ffff) << 5); /* cbz x9, exit */ + E(0xd61f0120); /* br x9 */ + x_first = cp; +} + +static void flush(void) +{ + memset(tab, 0, sizeof tab); + memset(cov, 0, sizeof cov); + nbl = 0; + cp = x_first; + if (trace) fprintf(stderr, "jit: flushed\n"); +} + +static void jitwrite(int on) +{ +#ifdef __APPLE__ + pthread_jit_write_protect_np(!on); +#else + (void)on; +#endif +} + +static void syncicache(unsigned *a, unsigned *b) +{ +#ifdef __APPLE__ + sys_icache_invalidate(a, (char *)b - (char *)a); +#else + __builtin___clear_cache((char *)a, (char *)b); +#endif +} + +/* store hook for words that are covered by a translation */ +static int hook(int a, int v) +{ + int j, x, hit = 0; + if (m[a] == v) return 0; + m[a] = v; + for (j = 0; j < nbl; j++) + if (bl[j].a >= 0 && bl[j].a <= a && a < bl[j].b) { + tab[bl[j].a] = 0; + for (x = bl[j].a; x < bl[j].b; x++) cov[x] = 0; + if (trace) fprintf(stderr, "jit: dropped %d..%d\n", bl[j].a, bl[j].b - 1); + bl[j].a = -1; + hit = 1; + } + for (j = 0; j < nbl; j++) + if (bl[j].a >= 0) for (x = bl[j].a; x < bl[j].b; x++) cov[x] = 1; + return hit; +} + +static void op(int i, int k) /* everything except control flow */ +{ + unsigned *a, *b, *c, *z; + switch (k) { + case 1: ldr_u(X9, SP, 0); ldr_rr(X9, M, X9, SXTW); str_u(X9, SP, 0); return; + case 2: /* store: inline unless the target word is translated */ + ldp_post(X9, X10, SP, 8); + E(0x7140413f); /* cmp w9, #65536 */ + a = cp; E(0x54000002); /* b.hs done */ + ldrx_u(X11, ST, 32); + E(0x3869496b); /* ldrb w11, [x11, w9, uxtw] */ + b = cp; E(0x3500000b); /* cbnz w11, slow */ + str_rr(X10, M, X9, UXTW); + c = cp; E(0x14000000); /* b done */ + cb_fix(b, cp); /* slow: hook(a, v) */ + movw(W0, X9); movw(W1, X10); ldrx_u(X11, ST, 64); blr(X11); + z = cp; E(0x34000000); /* cbz w0, done */ + li(W0, i + 1); b_to(x_exit); /* a translation died: resume at i+1 in c */ + cb_fix(a, cp); cb_fix(z, cp); + *c |= (cp - c) & 0x3ffffff; + return; + case 3: ldr_u(X9, SP, 0); E(0x9100128a); ldr_rr(X9, X10, X9, SXTW); str_u(X9, SP, 0); return; /* add x10, x20, #4 */ + case 4: E(0x91001294); return; /* add x20, x20, #4 */ + case 5: E(0x29402a89); E(0x2900268a); return; /* ldp w9, w10, [x20]; stp w10, w9, [x20] */ + case 10: ldrx_u(X11, ST, 40); blr(X11); E(0x13001c09); str_pre(X9, SP, -4); return; /* sxtb w9, w0 */ + case 11: + ldrx_u(X9, ST, 56); + if (sizeof J == 1) E(0x39400129); else ldr_u(X9, X9, 0); /* ldrb w9, [x9] */ + str_pre(X9, SP, -4); + return; + case 12: ldr_post(W0, SP, 4); E(0x13001c00); ldrx_u(X11, ST, 48); blr(X11); return; /* sxtb w0, w0 */ + } + ldr_post(X9, SP, 4); + ldr_u(X10, SP, 0); + switch (k) { + case 13: op3(0x0b000000, X10, X10, X9); break; + case 14: op3(0x4b000000, X10, X10, X9); break; + case 15: op3(0x1b007c00, X10, X10, X9); break; + case 16: op3(0x0a000000, X10, X10, X9); break; + case 17: op3(0x2a000000, X10, X10, X9); break; + case 18: op3(0x4a000000, X10, X10, X9); break; + case 19: op3(0x1ac02800, X10, X10, X9); break; + default: E(0x6b09015f); E(0x1a9fa7ea); /* cmp w10, w9; cset w10, lt */ + } + str_u(X10, SP, 0); +} + +static void *translate(int p) +{ + unsigned *start; + int i = p, n = 0, w, k, t; + if (cp - code > CODEW - 65536 || nbl >= MAXB) flush(); + start = cp; + for (;;) { + if (i >= MW) { E(0x12800000); b_to(x_exit); break; } /* movn w0, #0: halt */ + if (n++ >= BLKW) { li(W0, i); b_to(x_disp); break; } + w = m[i]; + cov[i] = 1; + if (w & 1 && i + 1 < MW && !(m[i + 1] & 1) && (k = m[i + 1] / 2) >= 6 && k <= 8) { + t = (w - 1) / 2; /* literal + call/jmp/jz with a known target */ + cov[i + 1] = 1; + if (k == 8) { + unsigned *a; + ldr_post(X10, SP, 4); + a = cp; E(0x3500000a); /* cbnz w10, fallthrough */ + li(W0, t); b_to(x_disp); + cb_fix(a, cp); + i += 2; + continue; + } + li(X10, i + 2); + if (k == 6) str_post(X10, RP, 4); else str_u(X10, RP, 0); + li(W0, t); b_to(x_disp); + i += 2; + break; + } + if (w & 1) { li(X9, (w - 1) / 2); str_pre(X9, SP, -4); i++; continue; } + k = w / 2; + if (k == 0) { E(0x12800000); b_to(x_exit); i++; break; } + if (k < 0 || k == 6 || k == 7) { + ldr_post(W0, SP, 4); + li(X10, i + 1); + if (k == 6) str_post(X10, RP, 4); else str_u(X10, RP, 0); + if (k < 0) { li(X11, 7 - k); addx_ext(RP, RP, X11, SXTW); } + b_to(x_disp); + i++; + break; + } + if (k == 8) { /* jz: taken leaves, not taken keeps going */ + ldp_post(X9, X10, SP, 8); + E(0x3500006a); /* cbnz w10, +3 */ + movw(W0, X9); + b_to(x_disp); + i++; + continue; + } + if (k == 9) { ldr_pre(W0, RP, -4); b_to(x_disp); i++; break; } + op(i, k); + i++; + } + bl[nbl].a = p; + bl[nbl++].b = i; + tab[p] = start; + syncicache(start, cp); + if (trace) fprintf(stderr, "jit: %d..%d -> %ld bytes\n", p, i - 1, (long)((char *)cp - (char *)start)); + return start; +} + +int main(void) +{ + int p = GORG; + union { unsigned *u; int (*f)(long *, void *); } enter; + trace = getenv("ZYTRACE") != 0; +#ifdef __APPLE__ + code = mmap(0, CODEW * 4, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANON | MAP_JIT, -1, 0); +#else + code = mmap(0, CODEW * 4, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); +#endif + if (code == MAP_FAILED) { perror("mmap"); return 1; } + jitwrite(1); + build_stubs(); + jitwrite(0); + syncicache(code, cp); + memcpy(m + GORG, genome, sizeof genome); + S[0] = (long)m; S[1] = 65000; S[2] = 60000; S[3] = (long)tab; S[4] = (long)cov; + S[5] = (long)Q; S[6] = (long)G; S[7] = (long)&J; S[8] = (long)hook; + enter.u = x_enter; + while ((unsigned)p < MW) { + void *c = tab[p]; + if (!c) { jitwrite(1); c = translate(p); jitwrite(0); } + p = enter.f(S, c); + } + return 0; +} diff --git a/docs/history/07-zygote/code/examples/asmain.s b/docs/history/07-zygote/code/examples/asmain.s new file mode 100644 index 0000000..96d9658 --- /dev/null +++ b/docs/history/07-zygote/code/examples/asmain.s @@ -0,0 +1,9 @@ +:;standalone:_source_on_Q,_binary_on_G +4000 a.q ! +:m.rd ? m.ch z 0 a.q @ ! m.go j +:m.ch , a.q @ ! a.q @ 1 + a.q ! m.rd j +:m.go 4000 a.src ! 40000 a.dst ! 0 a.org ! 50000 a.lab ! a.run c 0 a.j ! +:m.out a.j @ a.n @ < m.end z + 40000 a.j @ + @ 0 $ . 0 $ 8 > . 0 $ 16 > . 24 > . + a.j @ 1 + a.j ! m.out j +:m.end q diff --git a/docs/history/07-zygote/code/examples/echo.s b/docs/history/07-zygote/code/examples/echo.s new file mode 100644 index 0000000..002935d --- /dev/null +++ b/docs/history/07-zygote/code/examples/echo.s @@ -0,0 +1 @@ +:l ? x z q :x , . l j diff --git a/docs/history/07-zygote/code/examples/fact.s b/docs/history/07-zygote/code/examples/fact.s new file mode 100644 index 0000000..21f6e32 --- /dev/null +++ b/docs/history/07-zygote/code/examples/fact.s @@ -0,0 +1,3 @@ +10 fa c pb c 10 . q +:fa 0 $ 2 < fb z _ 1 r :fb 0 $ 1 - fa c * r +:pb 0 $ 1 > 0 $ p1 z pb c pd j :p1 _ :pd 1 & 48 + . r diff --git a/docs/history/07-zygote/code/examples/hello.s b/docs/history/07-zygote/code/examples/hello.s new file mode 100644 index 0000000..e5baf1a --- /dev/null +++ b/docs/history/07-zygote/code/examples/hello.s @@ -0,0 +1,2 @@ +s :l 0 $ @ 0 $ e z . 1 + l j :e q +:s "hello, #32 "world! #10 #0 diff --git a/docs/history/07-zygote/code/examples/sm.s b/docs/history/07-zygote/code/examples/sm.s new file mode 100644 index 0000000..954886e --- /dev/null +++ b/docs/history/07-zygote/code/examples/sm.s @@ -0,0 +1,3 @@ +133 x ! :x 65 . 10 . +fn c 133 fn ! fn c 131 fn ! fn c 10 . q +:fn 65 . r diff --git a/docs/history/07-zygote/code/gen.s b/docs/history/07-zygote/code/gen.s new file mode 100644 index 0000000..8ec6aa2 --- /dev/null +++ b/docs/history/07-zygote/code/gen.s @@ -0,0 +1,9 @@ +:;genome_header,_fixed_addresses_starting_at_48000 +:;48000_boot__48002_a.run__48004.._&a.src_&a.dst_&a.org_&a.lab_&a.n_&a.ops +z.boot j a.run j #a.src #a.dst #a.org #a.lab #a.n #a.ops +:;boot:_read_source_until_eof_or_backtick,_assemble_it_at_0,_become_it +:z.boot 20000 a.q ! +:z.rd ? z.ch z z.go j +:z.ch , 0 $ 96 - z.end z a.q @ ! a.q @ 1 + a.q ! z.rd j +:z.end _ +:z.go 0 a.q @ ! 20000 a.src ! 0 a.dst ! 0 a.org ! 45000 a.lab ! a.run c 0 j diff --git a/docs/history/07-zygote/code/genome.h b/docs/history/07-zygote/code/genome.h new file mode 100644 index 0000000..abf6072 --- /dev/null +++ b/docs/history/07-zygote/code/genome.h @@ -0,0 +1,39 @@ +#define GORG 48000 +#define GLEN 547 +static const int genome[] = { +96021,14,96115,14,48510,48511,48512,48513,48514,48525,40001,97035,4,22,96037,16, +96075,14,20,1,6,193,28,96073,16,97035,2,4,97035,2,3,26, +97035,4,96027,14,8,1,97035,2,4,40001,97021,4,1,97023,4,1, +97025,4,90001,97027,4,96115,12,1,14,1,97031,4,1,97033,4,97021, +2,97035,4,1,97029,4,96503,12,97035,2,2,1,6,97037,4,96463, +16,97037,2,69,28,96317,16,97037,2,117,28,96373,16,97035,2,3, +26,2,96483,12,96253,16,97037,2,96713,12,1,6,-1,10,40,96251, +16,5,30,96577,12,97035,2,3,26,97035,4,96141,14,8,97037,2, +71,28,96293,16,97035,2,96779,12,5,30,3,26,96577,12,96545,12, +96141,14,97035,2,3,26,96779,12,96577,12,96545,12,96141,14,97035,2, +3,26,97035,4,97035,2,2,1,6,96483,12,96353,16,8,96141,14, +96577,12,97035,2,3,26,97035,4,96329,14,97031,2,96389,16,96545,12, +96141,14,97035,2,3,26,97027,2,97033,2,5,30,26,4,97025,2, +97029,2,26,97027,2,97033,2,5,30,26,3,26,4,97033,2,3, +26,97033,4,96545,12,96141,14,97031,2,96473,16,18,3,97031,4,96127, +14,1,6,67,40,10,-1,10,40,32,18,97035,2,2,1,6, +96541,16,96483,12,96543,16,97035,2,3,26,97035,4,96503,14,8,18, +97035,2,2,96483,12,96561,16,18,97035,2,3,26,97035,4,96545,14, +97023,2,97029,2,26,4,97029,2,3,26,97029,4,18,97049,4,97047, +4,97047,2,2,96483,12,97049,2,2,96483,12,3,6,3,6,32, +96653,16,8,8,3,18,34,96663,16,1,18,97047,2,2,97049,2, +2,28,96685,16,1,18,97047,2,3,26,97047,4,97049,2,3,26, +97049,4,96611,14,97037,4,1,97039,4,97051,97039,2,26,2,1,6, +96767,16,97037,2,28,96773,16,97039,2,3,26,97039,4,96723,14,8, +-1,18,97039,2,18,97041,4,97033,2,97039,4,97039,2,96861,16,97039, +2,3,28,97039,4,97027,2,97039,2,5,30,26,2,97041,2,96603, +12,96791,16,97027,2,97039,2,5,30,26,3,26,2,18,97041,2, +96869,14,97047,4,1,97045,4,1,97043,4,97047,2,2,91,28,96917, +16,97047,2,2,87,28,96923,16,96935,14,3,97045,4,97047,2,3, +26,97047,4,97047,2,2,97,28,1,6,21,40,3,6,-1,10, +40,32,96999,16,97043,2,21,30,26,97043,4,97047,2,3,26,97047, +4,96935,14,8,97043,2,97045,2,97019,16,1,10,28,18,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,113,64,33, +36,95,126,99,106,122,114,44,63,46,43,45,42,38,124,94, +62,60,0 +}; diff --git a/docs/history/07-zygote/code/io_linux.c b/docs/history/07-zygote/code/io_linux.c new file mode 100644 index 0000000..7ea34e0 --- /dev/null +++ b/docs/history/07-zygote/code/io_linux.c @@ -0,0 +1,6 @@ +#include +int J; +static int pk(void){int c=getchar();ungetc(c,stdin);return c==EOF;} +char Q(void){int c=getchar();J=pk();return c;} +void G(char c){putchar(c);} +__attribute__((constructor)) static void init(void){J=pk();} diff --git a/docs/history/07-zygote/code/mkgenome.c b/docs/history/07-zygote/code/mkgenome.c new file mode 100644 index 0000000..7d9f0bc --- /dev/null +++ b/docs/history/07-zygote/code/mkgenome.c @@ -0,0 +1,35 @@ +/* assembles the genome at a fixed origin and writes it as a c header */ +#include +#include +#include +#define ORG 48000 +static char tok[30000][99]; +static int lab[30000], nt, img[17536], n; +static char *ops = "q@!$_~cjzr,?.+-*&|^><"; +static int val(char *s) +{ + int j; char *e; long v; + for (j = nt; j--;) if (tok[j][0] == ':' && !strcmp(tok[j] + 1, s)) return lab[j]; + v = strtol(s, &e, 0); + if (!*s || *e) { fprintf(stderr, "unknown label %s\n", s); exit(1); } + return (int)v; +} +static void put(int w) { if (n >= 17536) exit(1); img[n++] = w; } +int main(void) +{ + int pass, i; char *q, *c; + while (nt < 30000 && scanf("%98s", tok[nt]) == 1) nt++; + for (pass = 0; pass < 2; pass++) + for (n = i = 0; i < nt; i++) { + q = tok[i]; + if (*q == '"') while (*++q) put(*q); + else if (*q == ':') lab[i] = ORG + n; + else if (!q[1] && (c = strchr(ops, *q))) put((c - ops) * 2); + else if (*q == '#') put(pass ? val(q + 1) : 0); + else put(pass ? val(q) * 2 + 1 : 1); + } + printf("#define GORG %d\n#define GLEN %d\nstatic const int genome[] = {", ORG, n); + for (i = 0; i < n; i++) printf("%s%d", !i ? "\n" : i % 16 ? "," : ",\n", img[i]); + printf("\n};\n"); + return 0; +} diff --git a/docs/history/07-zygote/code/repl.s b/docs/history/07-zygote/code/repl.s new file mode 100644 index 0000000..35ab7d4 --- /dev/null +++ b/docs/history/07-zygote/code/repl.s @@ -0,0 +1,11 @@ +:;teach_the_resident_assembler_that_p_means_out +112 48009 @ 12 + ! +:;each_backtick-separated_chunk_of_input_is_assembled_at_40000_and_called +:nx ? rd0 z q +:rd0 30000 ptr ! +:rd ? ch z go j +:ch , 0 $ 96 - eos z ptr @ ! ptr @ 1 + ptr ! rd j +:eos _ +:go 0 ptr @ ! 30000 48004 @ ! 40000 48005 @ ! 40000 48006 @ ! 41000 48007 @ ! 48002 c + 40000 c nx j +:ptr #0 diff --git a/docs/history/07-zygote/code/test.sh b/docs/history/07-zygote/code/test.sh new file mode 100755 index 0000000..add0379 --- /dev/null +++ b/docs/history/07-zygote/code/test.sh @@ -0,0 +1,18 @@ +#!/bin/bash +# builds the zygote for arm64 linux and runs it under qemu. +# needs gcc, aarch64-linux-gnu-gcc and qemu-aarch64. on an arm64 mac or linux box, +# build natively instead and drop the qemu prefix. +cd "$(dirname "$0")"; T=$(mktemp -d); Q="timeout 60 qemu-aarch64" +chk(){ if [ "$1" = "$2" ]; then echo "ok $3"; else echo "FAIL $3: got [$1]"; fi; } +gcc -w mkgenome.c -o $T/mkg && cat gen.s aslib.s | $T/mkg >$T/g.h +cmp -s $T/g.h genome.h && echo "ok genome.h is up to date" || echo "FAIL genome.h is stale, regenerate it" +aarch64-linux-gnu-gcc -O2 -w -DJTYPE=int -static core.c io_linux.c -o $T/zy || exit 1 +chk "$($Q $T/zy $T/asm.s +(cat $T/asm.s; printf '`'; cat examples/hello.s) | $Q $T/zy >$T/a +chk "$(od -An -td4 $T/a | tr -s ' \n' ' ' | sed 's/^ //;s/ $//')" "29 1 6 2 1 6 27 16 24 3 26 3 14 0 104 101 108 108 111 44 32 119 111 114 108 100 33 10 0" "self-hosted assembler, running on the zygote, assembles hello" +chk "$( (cat repl.s; printf '`104 p 105 p 10 p r`121 p 111 p 10 p r') | $Q $T/zy | tr '\n' '/')" "hi/yo/" "repl with mutated assembler" +rm -rf $T diff --git a/docs/history/07-zygote/notes.md b/docs/history/07-zygote/notes.md new file mode 100644 index 0000000..a4f1089 --- /dev/null +++ b/docs/history/07-zygote/notes.md @@ -0,0 +1,10 @@ +# the zygote + +the code/ folder next to this file is the zygote exactly as it was packed in stage 8 and 9: the asm genome (gen.s, aslib.s), the arm64-only core, the repl and the .s examples. these are the notes as they were written in the transcript. + +## message 7 + +> can you make it a zygote approach? +> a minimal core loads first, reads and compiles the program (either wholly or partially, for speed) and replaces it with itself. so it's like a jvm but self-contained and on the same level as itself, where the assembler can itself be modified in-memory to assemble different code. for this we need to ditch llvm + +**summary:** the current design, described above. the native core only does jit and glue. it loads the genome (boot loader + assembler, both vm code) at 48000. the boot loader reads source until eof or a backtick, assembles it at 0 and jumps there. compilation is lazy per block, straight from live memory, with superblocks through `z`, fused constant targets, and dispatch chained through a table. invalidation uses the coverage byte map plus a store hook, so self-modified code gets retranslated instead of interpreted. the resident assembler sits at fixed addresses, and `repl.s` rewrites its mnemonic table so `p` means out, then assembles and runs input chunks in a loop, overwriting the same address each time. verified under qemu on linux arm64, including the self-hosted assembler running on the zygote. about 4x faster than the golfed interpreter. diff --git a/docs/history/08-packaging/notes.md b/docs/history/08-packaging/notes.md new file mode 100644 index 0000000..646dd89 --- /dev/null +++ b/docs/history/08-packaging/notes.md @@ -0,0 +1,17 @@ +# moving to cowork + +no code from this stage survives except where a code/ folder sits next to this file. these are the notes as they were written in the transcript. + +## message 8 + +> damn dawg you made an entire baremetal os? +> +> let's move to cowork. pack everything up alongside a transcript of every message i've sent and your own notes on what you've implemented in an md file, with the code, so i can move it. + +**summary:** not quite an os. it's a userland runtime. it still runs on darwin or linux, uses their mmap for executable memory, and gets i/o from the host through `Q`, `G` and `J`. there's no scheduler, no drivers and no hardware access, so a closer description is a tiny self-hosting jit, in the spirit of a jvm. the first packing attempt bundled every stage's code with a full test script. + +## message 9 + +> nono we don't need all the code, just the recent ones. my msgs and your summaries are more important and all should be there + +**summary:** this package. only the zygote stack plus its examples and tests are included, and earlier stages appear only as summaries. the golfed reference vm and assembler are inlined under message 3, because they define the instruction set. diff --git a/docs/history/10-zc/code/README.md b/docs/history/10-zc/code/README.md new file mode 100644 index 0000000..c9576c3 --- /dev/null +++ b/docs/history/10-zc/code/README.md @@ -0,0 +1,300 @@ +# tinyvm zygote: notes and transcript + +a tiny stack cpu that started as a c89 code golf entry and ended up as a self-hosting runtime. a small native core runs vm code (jit-compiled to arm64, or interpreted anywhere else), and the genome it boots is a compiler for zc, a c-like language that keeps the forth habits: the compiler lives in vm memory next to your program, your program can call into it, patch its tables, add syntax to it and compile new code while it runs. heap objects carry an ownership tag (u, m or d) that a static analyzer inside the compiler checks, with deterministic frees at scope ends and a mark-sweep collector underneath for everything else. + +this folder only has the latest code. earlier stages are described in the transcript below, but their code isn't included, except the asm-only genome from stage 7, which sits in `legacy/`. + +## files + +`core.c` is the native core and the only native code at runtime: the arm64 jit, its stubs and invalidation logic, and a portable interpreter used on every other host. `src/rt.zc` is the runtime (i/o, software division, the heap and the collector) and `src/compiler.zc` is the compiler, both written in zc. together they are the genome. `zc0.c` is stage 0 of the bootstrap, a compiler in c for the subset of zc that `compiler.zc` is written in. `tools/build.zc` is a small zc program that makes the resident compiler compile its own source into a fresh image, and `mkgenome.c` turns that image into `genome.h`, which is generated and checked in. `examples/` has hello, factorial, echo, self-patching code, a list that owns its elements, and a repl. `tests/` has feature tests, plus `tests/errors/` with programs the analyzer has to reject (each starts with the error it expects) and `tests/runerr.sh` to run them. `test.sh` does the whole bootstrap and runs everything on both the interpreter and the jit. `legacy/` has the old asm genome. + +## build + +``` +cc -O2 core.c io_linux.c -o zy # io_linux.c provides Q, G and J +./zy genome.h # stage 0, from c +cc -O2 core.c io_linux.c -o zy +(cat tools/build.zc; printf '`'; cat src/rt.zc src/compiler.zc) | ./zy | ./mkgenome >g.h +mv g.h genome.h && cc -O2 core.c io_linux.c -o zy # now built by itself +``` + +run the last two lines once more and `genome.h` should come out byte for byte the same. `test.sh` checks that. + +## zc, the language + +it reads like c and behaves like b: there are no types, every value is a 32 bit word, and a heap object is an array of words. memory is word addressed, so `p + 1` is the next word and strings are one char per word with a 0 at the end. + +``` +const N = 10; +var table[N], count; // globals: scalars and arrays, constant initializers or "strings" +fn fib(n) { if (n < 2) return n; return fib(n - 1) + fib(n - 2); } +fn main() { + for (var i = 0; i < N; i++) table[i] = fib(i); + var sq = { var t = table[5]; t * t }; // a block is an expression too, its last line is its value + var x = asm { 2 3 + }; // raw vm code, leaves one value + putn(sq + x); putc(10); + return 0; +} +``` + +statements are `var`, `if`/`else`, `while`, `for`, `break`, `continue`, `return`, `{ }` blocks, `asm { }` and expressions. operators are c's, with c's precedence: `* / % + - << >> < > <= >= == != & ^ | && ||`, unary `- ! ~ * &`, `++`/`--` both ways, `=` and the compound assignments, indexing `a[i]` (which is `*(a + i)`), and calls. a call through a value works (`f(x)` when `f` holds an address). comparisons and `&& ||` give 0 or 1. `/`, `%` and `<<` call into the runtime, since the cpu has no divider and only a right shift. character literals, `0x` hex and the usual escapes are there. there's no `?:`, no `switch` and no struct syntax. + +a function is called by name once it's declared, so mutual recursion works without prototypes, except that a function with tagged parameters has to be declared before a checked call to it (`fn f(d p);` is a prototype). `main` runs after the whole program compiled. every function returns one word, 0 if it falls off the end. + +builtins come from the runtime: `putc`, `getc`, `eof`, `puts`, `putn`, `halt`, `strlen`, `streq`, `wcopy`, `wfill`, `new`, `free`, `gc`, `isobj`, `osize`, `live_objects`. + +## memory model: u, m and d + +`new(n)` gives back a fresh object of n words. bind it to a local and the local owns it, tagged **m**. tags live on local variables and parameters, the analyzer follows them through the code, and the compiler inserts `free` calls where owned objects die. + +**m** means the object belongs to this block and has to be used in it. you can lend it to callees as often as you like (to parameters declared `m`), and when the block ends it's freed. an m object that's never used anywhere in its block is an error. + +**d** means the object is going down. `x as d` recasts it, and the first time it's handed to a lower scope its life here is over: passing it to a parameter declared `d`, or touching it inside an inner block, which then owns it as m and frees it when that block ends. using it afterwards is an error, and so is ending the block with a d object that never left. + +**u** means the object is going up. `x as u` recasts it, and from then on it has to leave this block for the one above, where it arrives as m. it leaves through `return` (the function has to be declared `-> u`), as the value of a block expression, or by assignment to a variable of the enclosing block. letting it fall out of scope, or throwing away a u result, is an error. + +recasting only goes away from m: m can become u or d (and u and d can swap), but nothing goes back to m in the same scope. a `d` parameter arrives as m. an `m` parameter is a borrow: the callee can read and write it, but it can't free it, return it, recast it or store it anywhere. + +``` +fn box(v) -> u { var b = new(1); b[0] = v; return b as u; } +fn push(m list, d item) { list[list[0] + 1] = item as d; list[0]++; return 0; } +fn main() { + var l = new(9); + var b = box(5); // u from box becomes m here + push(l, b as d); // l is lent, b is given away + return 0; // l is freed here, and b with it, since l owns it +} +``` + +containers own what you store in them. `p[i] = c` needs `c` to carry the same tag as `p` (or be a fresh object), and from then on `c` follows `p`: if `p` is recast, so is `c`, if `p` is given away, `c` goes with it, and when `p` is freed its children are freed too. a child can't be recast or given away on its own. storing into an object you only borrowed is giving the child away, so it needs `as d`. the runtime records the owner in the child's header, so freeing a container frees exactly the objects it owns. + +globals, plain pointers and arrays aren't tracked. storing an owned object there needs `as d`, and the mark-sweep collector takes it over. a fresh object can go straight in. + +branches have to agree: at the end of an `if` every variable has to be in the same state on both paths (a path that returns or breaks doesn't count), and a loop body has to leave every outer variable the way it found it, so a d object can't be given away inside a loop that might run twice. + +### the analyzer + +it runs inside the compiler on every function, including code compiled at runtime by `eval` and friends, and stops at the first violation with a line number and the variable's name. `tests/errors/` has one small program for each rule. three ways to turn it off: `unchecked fn f() { }` for one function, `unchecked { }` for a block, and a top-level `unchecked;` for the rest of the unit (a parse word can also set `check_on = 0`). unchecked code doesn't track anything, so whatever it allocates belongs to the collector, and checked variables can't be touched from inside an unchecked block. what it doesn't see: a value that isn't a bare variable name (`p + 0`, `p[0]` read back out) is a plain word, so pointer arithmetic and reading pointers out of containers make untracked aliases. that's the escape hatch, and also the hole. + +## the heap and the collector + +the heap is 0x40000..0xA0000. every block is a power of two words with a 5 word header (size, class, flags, a check word, owner) and comes from a free list per size class or a bump pointer, so `new` and `free` are a few instructions in the common case. checked code frees deterministically and never needs the collector. the collector is mark-sweep, non-moving and conservative: roots are the program area, the genome's globals, the compiler's input stack, the live frame stack and the data stack region, and a word counts as a pointer only if its object header checks out. it runs when you call `gc()` (which returns how many objects it freed) or when `new` runs out of room. objects never move, so raw addresses stay valid, which keeps the forth habits working. + +## code as data + +the compiler is in memory with your program and every name in it is callable. the pieces: + +`asm { }` emits raw vm words: numbers push, `-5` pushes a negative, `#n` emits a raw word, one-char tokens from the mnemonic table are opcodes, `:name` defines a label local to the block, and other names push the address of a variable or function. + +`quote { ... }` is a token list as a fresh object: `[n, kind, value, kind, value, ...]`, where names, strings and operators are interned pointers (`intern("while")` gets one). `tokens(s)` makes one from a string, `qdup(q)` copies one. + +`eval(q)` compiles a token list as a function body and runs it, `compile(q)` returns its address instead, `load(q)` compiles top-level declarations, and `evals(s)` does `eval` on a string. + +parse words: `immediate fn name() { }` runs at compile time whenever `name` shows up as a statement, inside an expression or at the top level. it starts with the current token on its own name and works the compiler directly: `next()`, `tk`/`tv` (current token kind and value), `expect`, `accept`, `expr()`, `stmt()`, `substmt()`, `emit`, `lit`, `op`, `hole`/`setlit` for jumps, `here`. `grab()` reads a `{ }` group into a token list and `feed(q)` pushes a list back into the input, which is all a macro needs. parse words can call `eval` mid-compile; the compiler saves its state and emits the new function in place with a jump around it. the built-in keywords are entries in the same table, so a parse word called `while` replaces `while`. + +tables: `add_binop("**", 11, "ipow c")` adds or replaces a binary operator with a precedence and its code (written like asm, names resolve when it's used), `add_unop` does the same for prefix operators, and the lexer learns new spellings from them. `mnem` is the asm mnemonic table, `q@!$_~cjzr,?.+-*&|^><`, and can be rewritten like before. + +``` +fn ipow(a, b) { var r = 1; while (b > 0) { r *= a; b--; } return r; } +immediate fn pow_op() { next(); add_binop("**", 11, "ipow c"); return 0; } +pow_op // runs now, at compile time +immediate fn repeat() { // repeat 3 { body } + var n, body, i = 0; + next(); n = tv; next(); + body = grab(); + while (i < n) { feed(qdup(body)); i++; } + free(body as d); + return 0; +} +fn main() { repeat 2 { putn(2 ** 10); putc(10); } return 0; } +``` + +## the bootstrap + +`zc0.c` compiles the subset `compiler.zc` uses (no analyzer, quote, parse words or block expressions) into a first genome. that genome compiles `compiler.zc` again, running on the vm, which is where the real compiler and the analyzer first see their own source, and `compiler.zc` passes its own lifetime checks. the result compiles itself into identical words, and that's the `genome.h` that ships. an image build (`build_image(org, dst)`) compiles with only the image's own names visible, writes code for one address into another, and appends a symbol table so the booted genome knows its own names. + +## the instruction set + +unchanged since stage 3. every word is 32 bits. an odd word is a literal that pushes `w>>1` (31 bits, sign extended). an even word is an opcode, `w/2`. there's a data stack and a return stack, both stored inside memory. + +``` +0 q halt 5 ~ swap 10 , in (Q) 15 * mul 20 < less +1 @ load 6 c call 11 ? empty (J) 16 & and +2 ! store 7 j jump 12 . out (G) 17 | or +3 $ pick 8 z jz 13 + add 18 ^ xor +4 _ drop 9 r ret 14 - sub 19 > shr +``` + +stack effects: `!` is `(v addr --)`, `$` is `(n -- copy of the nth item)` so `0 $` is dup, `z` is `(cond addr --)` and jumps when cond is 0, and `c` and `j` take their target from the stack. there's no div or mod, because a divider can't finish in one cycle in hardware. division is done in software. + +## memory map and abi + +memory grew from 64k words to 1m (1048576) words, since a self-hosted compiler and a heap don't fit in 64k. nothing in the instruction set changed. + +``` +0x00000.. the program: code, with globals between functions +0x40000..0x9FFFF heap +0xA0000 frame pointer word +0xA0100..0xBDFFF compiler data: interned names, symbol table, locals, tables +0xC0000.. genome: 0xC0000 entry, 0xC0005 symbol table address, 0xC0006 image end +0xE0000..0xEFFFF frame stack, grows down from 0xF0000 +0xF0000.. return stack, grows up +0xF8000.. mark stack +0xFC000..0xFFFF0 data stack, grows down +``` + +calls push the arguments left to right and the callee's prologue pops them into its frame. locals live in the frame at `*0xA0000 + slot`. every function leaves exactly one word on the data stack. + +## how the core works + +registers: x19 holds the memory base, x20 the data stack pointer, x21 the return stack pointer, x22 the translation table and x23 the state block (memory, s, r, table, coverage map, Q, G, &J, store hook). + +compilation is lazy. the first time control reaches an address, that block is translated from live memory. a block ends at call, jump, ret or halt. a `z` doesn't end it: the not-taken side keeps compiling, which makes superblocks. a literal followed by `c`, `j` or `z` is fused into a constant target. every exit goes through a 5-instruction dispatch stub that bounds-checks the target, loads `tab[t]` and branches to it directly when it's already compiled. otherwise it returns to c, which compiles the block and re-enters. + +self-modification is handled with a byte map of which words some live translation covers. a store checks that byte inline: an uncovered target, the usual case, gets a plain store. a covered target calls the hook, which drops every translation containing that word and makes the jit code return to c at the next instruction, so even patches to the very next word take effect. writing back the same value is a no-op. if the 16 mb code buffer fills, everything is flushed. + +platform: arm64 for the jit. the darwin path uses `MAP_JIT`, `pthread_jit_write_protect_np` and `sys_icache_invalidate`. linux uses rwx mmap and `__builtin___clear_cache`. only the linux arm64 path has actually been run, under qemu. the darwin path follows apple's documented recipe but hasn't been tested. under the hardened runtime it also needs the allow-jit entitlement. every other host gets the interpreter, which has the same semantics: shift counts wrap at 32, stores outside memory are ignored, loads outside memory read 0, and jumping outside memory halts. + +## test results + +`test.sh` passes all 27 checks on x86-64 linux (interpreter) and on linux arm64 under qemu (jit): the fixpoint, an up-to-date `genome.h`, every example and feature test on both paths with identical output, all 24 programs in `tests/errors/` rejected with the expected message on both, and the jit's self-compile matching too. the genome is 37505 words. the compiler compiles itself in about 0.15 s on the interpreter and 0.45 s on the jit under qemu. + +## open ends + +- branch and loop states have to match exactly. a loop that gives something away and then always breaks is rejected, even though it's fine. +- untracked aliases (pointer arithmetic, pointers read out of containers) aren't checked, and can dangle once the owner is freed. +- the collector is conservative, so a stale word on the stack can keep an object alive a little longer. it scans the whole data stack region because the vm can't read its own stack pointer. +- `?:`, `switch` and structs are missing. a parse word could add any of them. +- free lists never coalesce, so a heap full of one size class can't be reused for another until those objects die. +- translations are only reclaimed by a full flush, and the hook rescans the block list on every store to a covered word. + +--- + +## transcript + +each of my messages is quoted exactly, followed by a summary of the reply. + +### 1 + +> codegolf a tiny cpu vm in C89. the goal is to achieve the lowest character count possible. include an assembler as well (scored separately) and a hello world program (not scored, but elegance and simplicity is a bonus) +> it must be capable of re-entrant code and a realistically workable instruction set. + +**summary:** a two-stack forth-style machine. the vm was 384 bytes: one ternary chain inside `while(o=m[p++])`, the top two stack items cached in `a` and `b`, the program loaded with `read(open(...))` so it needs no headers, and both stacks inside the 65536-word memory. it had 22 opcodes including div and mod, a two-word literal, and jump/call targets popped from the stack. the assembler was 348 bytes and two-pass, using `strcspn` instead of `strchr` so the implicit int return stays safe on 64-bit. hello world was a pointer loop over a string with a zero terminator. re-entrancy was shown with a recursive factorial plus a recursive decimal printer. + +### 2 + +> your cpu should also be realistically close to hardware (translating it to logical circuitry hardware should be fairly easy) and sport an acceptably fast execution (if hypothetically ported to hardware, not in software) +> +> assume i/o is handled by two external functions, `char Q()` (getchar) and `void G(char)` (putchar) and a state variable J (boolean, shows if input is empty). +> +> don't be afraid to use macros. + +**summary:** no reply was sent to this message at the time. it was answered together with message 3. + +### 3 + +> pro tip: if you define a macro as `!o--`, you can save 2 characters per if condition +> oh and btw, newlines are free + +**summary:** used the tip as `#define X !--o?`, with pre-decrement because opcode 0 (halt) is already handled by the loop, and with the `?` folded into the macro and free newlines used as separators. the hardware-oriented redesign: bit 0 of each word is a literal flag, so literals take one cycle like the j1 cpu. div and mod were dropped because they can't finish in one cycle and were replaced by an arithmetic shift right, giving 8 alu ops that decode from 3 bits. every instruction does a fixed amount of work with at most one data memory access, and call and jump share a datapath (`r-=o`). i/o now goes through `Q`, `G` and a `?` op that reads `J`. the result was the vm at 339 bytes and the assembler at 346. the final golfed code: + +```c +#define X !--o? +int m[1<<16],p,s=65e3,r=6e4,o,a,b;main(c,v)char**v;{read(open(v[1],0),m,s);while(o=m[p++])a=m[s],b=m[s+1],o&1?m[--s]=o>>1:(o/=2,X +m[s]=m[a]:X +m[a]=b,s+=2:X +m[s]=m[s-~a]:X +s++:X +m[s]=b,m[s+1]=a:(o-=2)<1?m[r]=p,r-=o,p=a,s++:X +p=b?p:a,s+=2:X +p=m[--r]:X +m[--s]=Q():X +m[--s]=J:X +G(a),s++:(m[++s]=X +b+a:X +b-a:X +b*a:X +b&a:X +b|a:X +b^a:X +b>>a:b now let's move in stages. write a compiler that takes this assembler's code and converts it into darwin arm + +**summary:** an ungolfed c89 compiler from vm assembly to darwin arm64 assembly. every word got its own label, the image was emitted as an addressable data array, x19/x20/x21 held the memory base and the two stack pointers, and computed jumps went through a table of offsets. a literal followed by a jump became a direct branch. `Q` and `G` went through `sxtb`, since char is signed on apple arm64, and `J` was read with `ldrb`. it was verified by assembling with llvm-mc for darwin, then rewriting to linux with sed and running under qemu, where it matched the vm byte for byte. this version couldn't handle self-modifying code. + +### 5 + +> can you write the assembler in assembly and pair the code with the assembly and the assembler so that it can re-wire itself at runtime? or maybe include an interp for self-modification, otherwise fns are exec'd native + +**summary:** both. `aslib.s` is the assembler written in vm assembly as a library (`a.src`, `a.dst`, `a.org`, `a.lab`, then `a.run c`), and with `asmain.s` it's a 568-word standalone assembler. it's byte-identical to the c assembler and reaches a fixpoint when assembling itself. this is also where the `:;comment` trick came from. the compiler became a hybrid. the image is split into basic blocks, each block counts how many of its words differ from the original image, and stores into code go through a slow path that updates the counters. clean blocks run native, while dirty blocks and anything past the image run in an interpreter compiled into the same binary, which hands control back to native code whenever it lands in a clean block. `cc -t` printed `[` and `]` at each switch. the demo rewired a function at runtime to point at freshly assembled code and then restored it. + +### 6 + +> which one do you like more? and which is more performant? +> keep that one, then port to using llvm ir so that it can be platform independent + +**summary:** no reply was sent at the time. the llvm port was dropped when message 7 changed direction. the question was answered at the start of the reply to message 7: the hybrid was the favorite because it kept the vm's semantics exact. the plain compiler was slightly faster on code that never rewrites itself, but it silently ran stale code once anything did. the zygote beats both. + +### 7 + +> can you make it a zygote approach? +> a minimal core loads first, reads and compiles the program (either wholly or partially, for speed) and replaces it with itself. so it's like a jvm but self-contained and on the same level as itself, where the assembler can itself be modified in-memory to assemble different code. for this we need to ditch llvm + +**summary:** the current design, described above. the native core only does jit and glue. it loads the genome (boot loader + assembler, both vm code) at 48000. the boot loader reads source until eof or a backtick, assembles it at 0 and jumps there. compilation is lazy per block, straight from live memory, with superblocks through `z`, fused constant targets, and dispatch chained through a table. invalidation uses the coverage byte map plus a store hook, so self-modified code gets retranslated instead of interpreted. the resident assembler sits at fixed addresses, and `repl.s` rewrites its mnemonic table so `p` means out, then assembles and runs input chunks in a loop, overwriting the same address each time. verified under qemu on linux arm64, including the self-hosted assembler running on the zygote. about 4x faster than the golfed interpreter. + +### 8 + +> damn dawg you made an entire baremetal os? +> +> let's move to cowork. pack everything up alongside a transcript of every message i've sent and your own notes on what you've implemented in an md file, with the code, so i can move it. + +**summary:** not quite an os. it's a userland runtime. it still runs on darwin or linux, uses their mmap for executable memory, and gets i/o from the host through `Q`, `G` and `J`. there's no scheduler, no drivers and no hardware access, so a closer description is a tiny self-hosting jit, in the spirit of a jvm. the first packing attempt bundled every stage's code with a full test script. + +### 9 + +> nono we don't need all the code, just the recent ones. my msgs and your summaries are more important and all should be there + +**summary:** this package. only the zygote stack plus its examples and tests are included, and earlier stages appear only as summaries. the golfed reference vm and assembler are inlined under message 3, because they define the instruction set. + +### 10 + +> we're gonna make this thing cooler. make the syntax C-like whilst still keeping the forth capabilities like super-homiconicity and the likes, add explicit and automatic gc (use a fast approach) and memory management that looks like this: +> every heap object has a classifier u m d, up middle down. up means the object will die at the scope above (becomes m when returned to the parent scope, and it must be returned and cannot be discarded). m means it must be used here and can be passed down (but once it's recasted as u or d it can't be casted back to m in this scope). d means the variable is passed down, and its lifetime ends after it's given for the first time to a scope lower than the current one. +> ask me questions to clarify this approach, then add it to the code and also add a simple static analyzer which checks these lifetimes + +my answers to the clarifying questions, exactly: + +> scope: every { } block +> +> lending an m object to a callee: borrow, no escape (Recommended) +> +> storing a heap object inside another object or a global: container gets ownership. putting a value inside a container requires its umd to match the parent, and from then on, the umd for that is also inherited from the parent (as in if a parent m is casted to d, so is the child heap object) +> +> gc: free lists + mark-sweep (Recommended) +> +> where the compiler lives: resident in vm code. bootstrap it step by step, by writing a c-like in core.c and then rewriting the compiler inside the c-like. also bring back llvm if you think it'll make your job easier +> +> homoiconic features: parse words, quoted code blocks, inline stack code, patchable compiler tables +> +> how an inner block hands a u object up: expression and assign, both +> +> when the analyzer runs: inside the compiler but can be turned off if the code requests it + +**summary:** the genome is now zc, a typeless c-like language, described above. stage 0 is `zc0.c` (kept as its own file next to core.c rather than inside it, so the runtime still has one native file), and the resident compiler is `src/compiler.zc` plus `src/rt.zc`, which reached a byte-identical fixpoint compiling itself. llvm wasn't brought back, since the target is the vm itself. memory went to 1m words, and core.c gained an interpreter for non-arm64 hosts. calls i made without asking: tags go on signatures (`fn f(m a, d b) -> u`) and recasts are `x as u` / `x as d`; a d object touched inside an inner block moves into that block; storing into a borrowed object, a global or untracked memory needs `as d`; handing a u up by assignment only reaches the directly enclosing block; unchecked code gives its objects to the collector. the old asm genome moved to `legacy/`. diff --git a/docs/history/10-zc/code/core.c b/docs/history/10-zc/code/core.c new file mode 100644 index 0000000..ed148c6 --- /dev/null +++ b/docs/history/10-zc/code/core.c @@ -0,0 +1,332 @@ +/* zygote core: puts the genome (the zc compiler and its runtime, both vm code) + into memory and runs it. on arm64, every block of vm code is translated to + arm64 the first time it is reached, straight from live memory. stores that + land on translated words throw those translations away, so code that + rewrites itself, including the resident compiler, just gets retranslated + on the next visit. on other hosts (or with -DZYINTERP) a plain interpreter + with the same semantics runs instead. ZYTRACE=1 logs translations on stderr. */ +#include +#include +#include +#if defined(__aarch64__) && !defined(ZYINTERP) +#define ZYJIT 1 +#include +#ifdef __APPLE__ +#include +#include +#endif +#endif +#include "genome.h" + +#ifndef JTYPE +#define JTYPE char +#endif +char Q(void); +void G(char); +extern JTYPE J; + +#define MW (1 << 20) /* memory, in words */ +#define DS0 (MW - 16) /* data stack grows down from here */ +#define RS0 0xF0000 /* return stack grows up from here */ +#define CODEW (4 << 20) /* 16 MB of code, in instruction words */ +#define MAXB 65536 +#define BLKW 1024 /* longest block, in vm words */ + +static int m[MW]; +static int trace; +#ifdef ZYJIT +static unsigned char cov[MW]; /* 1 if a live translation covers the word */ +static void *tab[MW]; /* vm address -> translation */ +static struct { int a, b; } bl[MAXB]; /* live translations cover [a,b); a < 0 is dead */ +static int nbl; +static unsigned *code, *cp, *x_enter, *x_exit, *x_disp, *x_first; +static long S[9]; /* m, s, r, tab, cov, Q, G, &J, store hook; read by the stubs */ + +enum { W0 = 0, W1 = 1, X9 = 9, X10, X11, M = 19, SP = 20, RP = 21, TAB = 22, ST = 23 }; + +static void E(unsigned x) { *cp++ = x; } +static void li(int d, int v) +{ + unsigned u = (unsigned)v; + E(0x52800000 | (u & 0xffff) << 5 | d); + if (u >> 16) E(0x72a00000 | (u >> 16) << 5 | d); +} +static void ldr_post(int t, int n, int i) { E(0xb8400400 | (i & 0x1ff) << 12 | n << 5 | t); } +static void ldr_pre(int t, int n, int i) { E(0xb8400c00 | (i & 0x1ff) << 12 | n << 5 | t); } +static void str_pre(int t, int n, int i) { E(0xb8000c00 | (i & 0x1ff) << 12 | n << 5 | t); } +static void str_post(int t, int n, int i) { E(0xb8000400 | (i & 0x1ff) << 12 | n << 5 | t); } +static void ldr_u(int t, int n, int o) { E(0xb9400000 | (o / 4) << 10 | n << 5 | t); } +static void str_u(int t, int n, int o) { E(0xb9000000 | (o / 4) << 10 | n << 5 | t); } +static void ldrx_u(int t, int n, int o) { E(0xf9400000 | (o / 8) << 10 | n << 5 | t); } +static void ldr_rr(int t, int n, int r, int ext) { E(0xb8600800 | r << 16 | ext << 13 | 1 << 12 | n << 5 | t); } +static void str_rr(int t, int n, int r, int ext) { E(0xb8200800 | r << 16 | ext << 13 | 1 << 12 | n << 5 | t); } +static void ldp_post(int t, int u, int n, int i) { E(0x28c00000 | ((i / 4) & 0x7f) << 15 | u << 10 | n << 5 | t); } +static void op3(unsigned op, int d, int n, int r) { E(op | r << 16 | n << 5 | d); } +static void addx_ext(int d, int n, int r, int ext) { E(0x8b200000 | r << 16 | ext << 13 | 2 << 10 | n << 5 | d); } +static void movw(int d, int r) { E(0x2a0003e0 | r << 16 | d); } +static void blr(int n) { E(0xd63f0000 | n << 5); } +static void b_to(unsigned *t) { long o = t - cp; E(0x14000000 | (o & 0x3ffffff)); } +static void cb_fix(unsigned *at, unsigned *to) { *at |= ((to - at) & 0x7ffff) << 5; } +#define SXTW 6 +#define UXTW 2 + +static void build_stubs(void) +{ + cp = code; + x_enter = cp; /* int enter(long *S, void *code) */ + E(0xa9bc7bfd); /* stp x29, x30, [sp, #-64]! */ + E(0x910003fd); /* mov x29, sp */ + E(0xa90153f3); /* stp x19, x20, [sp, #16] */ + E(0xa9025bf5); /* stp x21, x22, [sp, #32] */ + E(0xf9001bf7); /* str x23, [sp, #48] */ + E(0xaa0003f7); /* mov x23, x0 */ + ldrx_u(M, ST, 0); + ldr_u(X9, ST, 8); addx_ext(SP, M, X9, UXTW); + ldr_u(X9, ST, 16); addx_ext(RP, M, X9, UXTW); + ldrx_u(TAB, ST, 24); + E(0xd61f0020); /* br x1 */ + x_exit = cp; /* w0 = next vm address */ + op3(0xcb000000, X9, SP, M); E(0xd342fd29); str_u(X9, ST, 8); /* s = (x20 - x19) >> 2 */ + op3(0xcb000000, X9, RP, M); E(0xd342fd29); str_u(X9, ST, 16); + E(0xa94153f3); /* ldp x19, x20, [sp, #16] */ + E(0xa9425bf5); /* ldp x21, x22, [sp, #32] */ + E(0xf9401bf7); /* ldr x23, [sp, #48] */ + E(0xa8c47bfd); /* ldp x29, x30, [sp], #64 */ + E(0xd65f03c0); /* ret */ + x_disp = cp; /* w0 = target: chain through tab[] or fall back to c */ + E(0x7144001f); /* cmp w0, #MW */ + E(0x54000002 | ((x_exit - cp) & 0x7ffff) << 5); /* b.hs exit */ + E(0xf8605ac9); /* ldr x9, [x22, w0, uxtw #3] */ + E(0xb4000009 | ((x_exit - cp) & 0x7ffff) << 5); /* cbz x9, exit */ + E(0xd61f0120); /* br x9 */ + x_first = cp; +} + +static void flush(void) +{ + memset(tab, 0, sizeof tab); + memset(cov, 0, sizeof cov); + nbl = 0; + cp = x_first; + if (trace) fprintf(stderr, "jit: flushed\n"); +} + +static void jitwrite(int on) +{ +#ifdef __APPLE__ + pthread_jit_write_protect_np(!on); +#else + (void)on; +#endif +} + +static void syncicache(unsigned *a, unsigned *b) +{ +#ifdef __APPLE__ + sys_icache_invalidate(a, (char *)b - (char *)a); +#else + __builtin___clear_cache((char *)a, (char *)b); +#endif +} + +/* store hook for words that are covered by a translation */ +static int hook(int a, int v) +{ + int j, x, hit = 0; + if (m[a] == v) return 0; + m[a] = v; + for (j = 0; j < nbl; j++) + if (bl[j].a >= 0 && bl[j].a <= a && a < bl[j].b) { + tab[bl[j].a] = 0; + for (x = bl[j].a; x < bl[j].b; x++) cov[x] = 0; + if (trace) fprintf(stderr, "jit: dropped %d..%d\n", bl[j].a, bl[j].b - 1); + bl[j].a = -1; + hit = 1; + } + for (j = 0; j < nbl; j++) + if (bl[j].a >= 0) for (x = bl[j].a; x < bl[j].b; x++) cov[x] = 1; + return hit; +} + +static void op(int i, int k) /* everything except control flow */ +{ + unsigned *a, *b, *c, *z; + switch (k) { + case 1: ldr_u(X9, SP, 0); ldr_rr(X9, M, X9, SXTW); str_u(X9, SP, 0); return; + case 2: /* store: inline unless the target word is translated */ + ldp_post(X9, X10, SP, 8); + E(0x7144013f); /* cmp w9, #MW */ + a = cp; E(0x54000002); /* b.hs done */ + ldrx_u(X11, ST, 32); + E(0x3869496b); /* ldrb w11, [x11, w9, uxtw] */ + b = cp; E(0x3500000b); /* cbnz w11, slow */ + str_rr(X10, M, X9, UXTW); + c = cp; E(0x14000000); /* b done */ + cb_fix(b, cp); /* slow: hook(a, v) */ + movw(W0, X9); movw(W1, X10); ldrx_u(X11, ST, 64); blr(X11); + z = cp; E(0x34000000); /* cbz w0, done */ + li(W0, i + 1); b_to(x_exit); /* a translation died: resume at i+1 in c */ + cb_fix(a, cp); cb_fix(z, cp); + *c |= (cp - c) & 0x3ffffff; + return; + case 3: ldr_u(X9, SP, 0); E(0x9100128a); ldr_rr(X9, X10, X9, SXTW); str_u(X9, SP, 0); return; /* add x10, x20, #4 */ + case 4: E(0x91001294); return; /* add x20, x20, #4 */ + case 5: E(0x29402a89); E(0x2900268a); return; /* ldp w9, w10, [x20]; stp w10, w9, [x20] */ + case 10: ldrx_u(X11, ST, 40); blr(X11); E(0x13001c09); str_pre(X9, SP, -4); return; /* sxtb w9, w0 */ + case 11: + ldrx_u(X9, ST, 56); + if (sizeof J == 1) E(0x39400129); else ldr_u(X9, X9, 0); /* ldrb w9, [x9] */ + str_pre(X9, SP, -4); + return; + case 12: ldr_post(W0, SP, 4); E(0x13001c00); ldrx_u(X11, ST, 48); blr(X11); return; /* sxtb w0, w0 */ + } + ldr_post(X9, SP, 4); + ldr_u(X10, SP, 0); + switch (k) { + case 13: op3(0x0b000000, X10, X10, X9); break; + case 14: op3(0x4b000000, X10, X10, X9); break; + case 15: op3(0x1b007c00, X10, X10, X9); break; + case 16: op3(0x0a000000, X10, X10, X9); break; + case 17: op3(0x2a000000, X10, X10, X9); break; + case 18: op3(0x4a000000, X10, X10, X9); break; + case 19: op3(0x1ac02800, X10, X10, X9); break; + default: E(0x6b09015f); E(0x1a9fa7ea); /* cmp w10, w9; cset w10, lt */ + } + str_u(X10, SP, 0); +} + +static void *translate(int p) +{ + unsigned *start; + int i = p, n = 0, w, k, t; + if (cp - code > CODEW - 65536 || nbl >= MAXB) flush(); + start = cp; + for (;;) { + if (i >= MW) { E(0x12800000); b_to(x_exit); break; } /* movn w0, #0: halt */ + if (n++ >= BLKW) { li(W0, i); b_to(x_disp); break; } + w = m[i]; + cov[i] = 1; + if (w & 1 && i + 1 < MW && !(m[i + 1] & 1) && (k = m[i + 1] / 2) >= 6 && k <= 8) { + t = (w - 1) / 2; /* literal + call/jmp/jz with a known target */ + cov[i + 1] = 1; + if (k == 8) { + unsigned *a; + ldr_post(X10, SP, 4); + a = cp; E(0x3500000a); /* cbnz w10, fallthrough */ + li(W0, t); b_to(x_disp); + cb_fix(a, cp); + i += 2; + continue; + } + li(X10, i + 2); + if (k == 6) str_post(X10, RP, 4); else str_u(X10, RP, 0); + li(W0, t); b_to(x_disp); + i += 2; + break; + } + if (w & 1) { li(X9, (w - 1) / 2); str_pre(X9, SP, -4); i++; continue; } + k = w / 2; + if (k == 0) { E(0x12800000); b_to(x_exit); i++; break; } + if (k < 0 || k == 6 || k == 7) { + ldr_post(W0, SP, 4); + li(X10, i + 1); + if (k == 6) str_post(X10, RP, 4); else str_u(X10, RP, 0); + if (k < 0) { li(X11, 7 - k); addx_ext(RP, RP, X11, SXTW); } + b_to(x_disp); + i++; + break; + } + if (k == 8) { /* jz: taken leaves, not taken keeps going */ + ldp_post(X9, X10, SP, 8); + E(0x3500006a); /* cbnz w10, +3 */ + movw(W0, X9); + b_to(x_disp); + i++; + continue; + } + if (k == 9) { ldr_pre(W0, RP, -4); b_to(x_disp); i++; break; } + op(i, k); + i++; + } + bl[nbl].a = p; + bl[nbl++].b = i; + tab[p] = start; + syncicache(start, cp); + if (trace) fprintf(stderr, "jit: %d..%d -> %ld bytes\n", p, i - 1, (long)((char *)cp - (char *)start)); + return start; +} + +#else +/* portable path for hosts without the arm64 jit: a plain interpreter with + the same semantics (shift counts wrap at 32, stores outside memory are + ignored, loads outside memory read 0, leaving memory halts). */ +#define LD(x) ((unsigned)(x) < MW ? m[x] : 0) +static void run(int p) +{ + int s = DS0, r = RS0, w, a, b; + unsigned long long steps = 0; + while ((unsigned)p < MW) { + w = m[p++]; + steps++; + if (w & 1) { m[--s] = w >> 1; continue; } + a = m[s]; b = m[s + 1]; + switch (w / 2) { + case 1: m[s] = LD(a); break; + case 2: if ((unsigned)a < MW) m[a] = b; s += 2; break; + case 3: m[s] = LD(s + 1 + a); break; + case 4: s++; break; + case 5: m[s] = b; m[s + 1] = a; break; + case 6: m[r++] = p; p = a; s++; break; + case 7: p = a; s++; break; + case 8: if (!b) p = a; s += 2; break; + case 9: p = m[--r]; break; + case 10: m[--s] = (signed char)Q(); break; + case 11: m[--s] = J; break; + case 12: G((char)a); s++; break; + case 13: m[++s] = b + a; break; + case 14: m[++s] = (int)((unsigned)b - (unsigned)a); break; + case 15: m[++s] = (int)((unsigned)b * (unsigned)a); break; + case 16: m[++s] = b & a; break; + case 17: m[++s] = b | a; break; + case 18: m[++s] = b ^ a; break; + case 19: m[++s] = b >> (a & 31); break; + case 20: m[++s] = b < a; break; + default: p = MW; break; /* halt, and the reserved negative opcodes */ + } + } + if (trace) fprintf(stderr, "interp: %llu steps\n", steps); +} +#endif + +int main(void) +{ + trace = getenv("ZYTRACE") != 0; + memcpy(m + GORG, genome, sizeof genome); +#ifdef ZYJIT + { + int p = GORG; + union { unsigned *u; int (*f)(long *, void *); } enter; +#ifdef __APPLE__ + code = mmap(0, CODEW * 4, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANON | MAP_JIT, -1, 0); +#else + code = mmap(0, CODEW * 4, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); +#endif + if (code == MAP_FAILED) { perror("mmap"); return 1; } + jitwrite(1); + build_stubs(); + jitwrite(0); + syncicache(code, cp); + S[0] = (long)m; S[1] = DS0; S[2] = RS0; S[3] = (long)tab; S[4] = (long)cov; + S[5] = (long)Q; S[6] = (long)G; S[7] = (long)&J; S[8] = (long)hook; + enter.u = x_enter; + while ((unsigned)p < MW) { + void *c = tab[p]; + if (!c) { jitwrite(1); c = translate(p); jitwrite(0); } + p = enter.f(S, c); + } + } +#else + run(GORG); +#endif + return 0; +} diff --git a/docs/history/10-zc/code/examples/echo.zc b/docs/history/10-zc/code/examples/echo.zc new file mode 100644 index 0000000..01f28b0 --- /dev/null +++ b/docs/history/10-zc/code/examples/echo.zc @@ -0,0 +1,2 @@ +// everything after the backtick is left unread, for the program itself +fn main() { while (!eof()) putc(getc()); return 0; } diff --git a/docs/history/10-zc/code/examples/fact.zc b/docs/history/10-zc/code/examples/fact.zc new file mode 100644 index 0000000..2a218b2 --- /dev/null +++ b/docs/history/10-zc/code/examples/fact.zc @@ -0,0 +1,4 @@ +fn fact(n) { if (n < 2) return 1; return n * fact(n - 1); } +// prints in binary, like the old asm example +fn bin(n) { if (n > 1) bin(n >> 1); putc('0' + (n & 1)); return 0; } +fn main() { bin(fact(10)); putc(10); putn(fact(10)); putc(10); return 0; } diff --git a/docs/history/10-zc/code/examples/hello.zc b/docs/history/10-zc/code/examples/hello.zc new file mode 100644 index 0000000..f36a810 --- /dev/null +++ b/docs/history/10-zc/code/examples/hello.zc @@ -0,0 +1 @@ +fn main() { puts("hello, world!\n"); return 0; } diff --git a/docs/history/10-zc/code/examples/lists.zc b/docs/history/10-zc/code/examples/lists.zc new file mode 100644 index 0000000..7392752 --- /dev/null +++ b/docs/history/10-zc/code/examples/lists.zc @@ -0,0 +1,35 @@ +// ownership in practice: a list owns its boxes. nothing here calls free; +// everything is released at the end of the block that owns it. +fn box(v) -> u { var b = new(1); b[0] = v; return b as u; } +fn mklist(cap) -> u { var l = new(cap + 1); l[0] = 0; return l as u; } +// item arrives as m (it was given down), and goes on as d into the borrowed list +fn push(m l, d item) { l[l[0] + 1] = item as d; l[0] = l[0] + 1; return 0; } +fn show(m l) +{ + var i = 1; + putc('['); + while (i <= l[0]) { if (i > 1) putc(' '); putn(l[i][0]); i++; } + putc(']'); + return 0; +} +fn squares(n) -> u +{ + var l = mklist(n), i = 0; + while (i < n) { + var b = box(i * i); + push(l, b as d); + i++; + } + return l as u; +} +fn main() +{ + var before = live_objects(); + { + var l = squares(6); + show(l); putc(10); + puts("live: "); putn(live_objects() - before); putc(10); + } + puts("after the block: "); putn(live_objects() - before); putc(10); + return 0; +} diff --git a/docs/history/10-zc/code/examples/repl.zc b/docs/history/10-zc/code/examples/repl.zc new file mode 100644 index 0000000..49a8c7b --- /dev/null +++ b/docs/history/10-zc/code/examples/repl.zc @@ -0,0 +1,27 @@ +// a repl on the resident compiler: each backtick-separated chunk of input is +// compiled as a function body and run. definitions (fn ...) can be loaded too: +// a chunk that starts with "fn" goes through load instead of eval. +var buf[4000]; +fn readchunk() +{ + var n = 0, c; + while (!eof()) { + c = getc(); + if (c == 96) break; + if (n < 3999) { buf[n] = c; n++; } + } + buf[n] = 0; + return n; +} +fn main() +{ + var q, i; + while (!eof()) { + readchunk(); + q = tokens(buf); + if (q[0] && q[1] == T_ID && q[2] == intern("fn")) load(q); + else eval(q); + free(q as d); + } + return 0; +} diff --git a/docs/history/10-zc/code/examples/selfpatch.zc b/docs/history/10-zc/code/examples/selfpatch.zc new file mode 100644 index 0000000..35a4843 --- /dev/null +++ b/docs/history/10-zc/code/examples/selfpatch.zc @@ -0,0 +1,17 @@ +// code is data: find the literal 'A' inside f's compiled words and rewrite it. +// on the jit the store lands on translated code, which gets dropped and +// retranslated on the next call. +fn f() { putc('A'); return 0; } +fn litw(v) { return v + v + 1; } // how the vm encodes a literal push +fn main() +{ + var p = f; + f(); + while (*p != litw('A')) p++; + *p = litw('B'); + f(); + *p = litw('C'); + f(); + putc(10); + return 0; +} diff --git a/docs/history/10-zc/code/genome.h b/docs/history/10-zc/code/genome.h new file mode 100644 index 0000000..0075426 --- /dev/null +++ b/docs/history/10-zc/code/genome.h @@ -0,0 +1,2349 @@ +#define GORG 786432 +static const int genome[] = { +1966081,1310721,4,1640539,14,820495,823937,1310721,2,3,28,1310721,4,1310721,2,4, +1310721,2,2,24,1,1572913,14,1,1310721,2,3,26,1310721,4,18,1310721, +2,1,28,1310721,4,20,1572947,14,1,1310721,2,1,26,1310721,4,18, +1310721,2,1,28,1310721,4,22,1572981,14,1,1310721,2,1,26,1310721,4, +18,1310721,2,1,28,1310721,4,0,1,1573017,14,1,1310721,2,1,26, +1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,4,1,1310721,2,3, +26,4,1310721,2,2,1310721,2,3,26,2,26,2,1573149,16,1310721,2, +2,1310721,2,3,26,2,26,2,1572879,12,8,1310721,2,3,26,1, +6,2,1,6,3,26,5,6,4,10,8,8,1573061,14,1,1573157, +14,1,1310721,2,5,26,1310721,4,18,1310721,2,29,28,1310721,4,1310721, +2,4,1,1310721,2,27,26,4,1310721,2,2,1,40,1573249,16,91, +1572879,12,8,1310721,2,1,1310721,2,2,28,10,3,6,10,4,8, +1310721,2,2,1,40,1573303,16,1573289,14,50,49,52,55,52,56,51, +54,52,56,0,1573267,1573031,12,8,1,1573521,14,3,1573445,16,1310721,2, +3,26,1310721,2,27,26,1,6,2,1,6,3,26,5,6,4, +10,8,26,97,1310721,2,2,21,1575169,12,26,10,3,6,10,4, +8,1310721,2,1310721,2,2,21,1574815,12,10,3,6,10,4,8,1310721, +2,2,1,6,1,10,28,34,63,38,3,26,1573441,16,1573445,14, +1573303,14,1310721,2,27,26,2,1573513,16,1310721,2,3,26,1310721,2,27, +26,1,6,2,-1,26,1,6,5,6,4,10,8,26,2,1572879, +12,8,1573445,14,1,1573521,14,1,1310721,2,29,26,1310721,4,18,1310721, +2,5,28,1310721,4,1310721,2,4,1,1310721,2,3,26,4,1310721,2, +2,1310721,2,3,26,2,26,2,1573627,16,1310721,2,3,26,1,6, +2,1,6,3,26,5,6,4,10,8,8,1573565,14,1310721,2,3, +26,2,1573643,14,1,1310721,2,5,26,1310721,4,18,1310721,2,7,28, +1310721,4,1310721,2,3,26,4,1310721,2,4,1,1310721,2,5,26,4, +1310721,2,2,1310721,2,5,26,2,26,2,1573813,16,1310721,2,2,1310721, +2,5,26,2,26,2,1310721,2,3,26,2,1310721,2,5,26,2, +26,2,28,1,6,1,10,28,34,63,38,3,26,1,6,1, +10,28,34,63,38,1,10,28,1573815,14,1,1573857,16,1310721,2,5, +26,1,6,2,1,6,3,26,5,6,4,10,8,8,1573697,14, +1310721,2,2,1310721,2,5,26,2,26,2,1310721,2,3,26,2,1310721, +2,5,26,2,26,2,28,1,6,1,10,28,34,63,38,3, +26,1573929,14,1,1310721,2,7,26,1310721,4,18,1310721,2,9,28,1310721, +4,1310721,2,5,26,4,1310721,2,3,26,4,1310721,2,4,1,1310721, +2,7,26,4,1310721,2,7,26,2,1310721,2,5,26,2,40,1574111, +16,1310721,2,2,1310721,2,7,26,2,26,1310721,2,3,26,2,1310721, +2,7,26,2,26,2,10,3,6,10,4,8,1310721,2,7,26, +1,6,2,1,6,3,26,5,6,4,10,8,8,1573993,14,1, +1574119,14,1,1310721,2,9,26,1310721,4,18,1310721,2,9,28,1310721,4, +1310721,2,5,26,4,1310721,2,3,26,4,1310721,2,4,1,1310721,2, +7,26,4,1310721,2,7,26,2,1310721,2,5,26,2,40,1574287,16, +1310721,2,2,1310721,2,7,26,2,26,1310721,2,3,26,2,10,3, +6,10,4,8,1310721,2,7,26,1,6,2,1,6,3,26,5, +6,4,10,8,8,1574183,14,1,1574295,14,1,1310721,2,9,26,1310721, +4,18,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,1310721,2,11,28,1310721,4,1310721,2,3,26,4,1310721,2, +4,1,1310721,2,5,26,4,1,1310721,2,7,26,4,61,1310721,2, +9,26,4,1310721,2,2,1310721,2,3,26,2,40,1574487,16,1574373,1310721, +2,2,10,3,6,10,4,8,1,1574801,14,1310721,2,9,26,2, +1,40,3,36,1574761,16,1310721,2,7,26,1310721,2,7,26,2,1310721, +2,7,26,2,26,1310721,2,2,1310721,2,9,26,2,38,3,32, +26,10,3,6,10,4,8,1310721,2,5,26,1310721,2,5,26,2, +1310721,2,5,26,2,26,10,3,6,10,4,8,1310721,2,7,26, +2,1310721,2,3,26,2,40,3,36,1574723,16,1310721,2,7,26,1310721, +2,7,26,2,1310721,2,3,26,2,28,10,3,6,10,4,8, +1310721,2,5,26,1,6,2,1,6,3,26,5,6,4,10,8, +8,1310721,2,9,26,1,6,2,1,6,-1,26,5,6,4,10, +8,8,1574487,14,1574373,1310721,2,7,26,2,10,3,6,10,4,8, +1310721,2,5,26,2,1574801,14,1,1310721,2,11,26,1310721,4,18,1310721, +2,9,28,1310721,4,1310721,2,3,26,4,1310721,2,4,1,1310721,2, +5,26,4,1310721,2,3,26,2,1,28,1,6,1,10,28,34, +63,38,3,26,1574919,16,1574373,1310721,2,2,10,3,6,10,4,8, +1,1575155,14,1310721,2,2,1,40,1574981,16,1310721,2,1,1310721,2,2, +28,10,3,6,10,4,8,1310721,2,5,26,3,10,3,6,10, +4,8,1310721,2,3,26,2,1,40,1575067,16,1310721,2,3,26,1, +1310721,2,3,26,2,28,10,3,6,10,4,8,1310721,2,5,26, +1310721,2,5,26,2,3,36,10,3,6,10,4,8,1310721,2,7, +26,1310721,2,2,1310721,2,3,26,2,1574375,12,10,3,6,10,4, +8,1310721,2,5,26,2,1575139,16,1,1310721,2,7,26,2,28,1575155, +14,1310721,2,7,26,2,1575155,14,1,1310721,2,9,26,1310721,4,18, +1310721,2,7,28,1310721,4,1310721,2,3,26,4,1310721,2,4,1310721,2, +2,1,40,1310721,2,5,26,4,1310721,2,2,1310721,2,3,26,2, +1574815,12,8,1310721,2,5,26,2,1575265,16,1,1574373,2,28,1575275,14, +1574373,2,1575275,14,1,1310721,2,7,26,1310721,4,18,1310721,2,5,28, +1310721,4,1310721,2,3,26,4,1310721,2,4,1310721,2,2,1574309,1310721,2, +3,26,2,63,32,26,2,30,1575351,14,1,1310721,2,5,26,1310721, +4,18,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,1,1310721,2,3,28,1310721,4,3,1310721,2,4, +1574309,1,26,3,10,3,6,10,4,8,1310721,2,2,65,40,1575581, +16,1574309,1310721,2,2,26,1574309,1310721,2,2,3,28,26,2,1574309,1310721, +2,2,3,28,26,2,26,10,3,6,10,4,8,1310721,2,1, +6,2,1,6,3,26,5,6,4,10,8,8,1575477,14,1575365,524289, +10,3,6,10,4,8,1575367,1,65,1574133,12,8,1,1575617,14,1, +1310721,2,3,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,4, +1310721,2,2,524289,11,26,40,3,36,1575709,16,1310721,2,2,1575365,2, +40,1,6,1,10,28,34,63,38,1,10,28,1575711,14,1,1575791, +16,1310721,2,2,5,1,10,28,26,2,1310721,2,2,1459366445,36,28, +1,6,1,10,28,34,63,38,3,26,1,6,1,10,28,34, +63,38,1,10,28,1575793,14,1,1575845,16,1310721,2,2,7,1,10, +28,26,2,3,32,1,6,1,10,28,34,63,38,1,10,28, +1575847,14,1,1575853,14,1,1310721,2,3,26,1310721,4,18,1310721,2,1, +28,1310721,4,1575931,14,122,99,58,32,111,117,116,32,111,102,32, +104,101,97,112,32,109,101,109,111,114,121,10,0,1575883,1573031,12, +8,1572995,12,8,1,1575953,14,1,1310721,2,1,26,1310721,4,18,1310721, +2,13,28,1310721,4,1310721,2,4,1310721,2,2,11,26,1310721,2,3, +26,4,7,1310721,2,5,26,4,17,1310721,2,7,26,4,1,1310721, +2,9,26,4,1310721,2,2,1,40,1576077,16,1310721,2,3,26,11, +10,3,6,10,4,8,1310721,2,7,26,2,1310721,2,3,26,2, +40,1576183,16,1310721,2,7,26,1310721,2,7,26,2,1310721,2,7,26, +2,26,10,3,6,10,4,8,1310721,2,5,26,1,6,2,1, +6,3,26,5,6,4,10,8,8,1576077,14,1575367,1310721,2,5,26, +2,26,2,1576285,16,1310721,2,9,26,1575367,1310721,2,5,26,2,26, +2,10,3,6,10,4,8,1575367,1310721,2,5,26,2,26,1310721,2, +9,26,2,11,26,2,10,3,6,10,4,8,1576583,14,1575365,2, +1310721,2,7,26,2,26,1310721,10,40,3,36,1576373,16,1310721,2,9, +26,1575365,2,10,3,6,10,4,8,1575365,1575365,2,1310721,2,7,26, +2,26,10,3,6,10,4,8,1576583,14,1575435,2,1576387,16,1578127,12, +8,1575367,1310721,2,5,26,2,26,2,1576489,16,1310721,2,9,26,1575367, +1310721,2,5,26,2,26,2,10,3,6,10,4,8,1575367,1310721,2, +5,26,2,26,1310721,2,9,26,2,11,26,2,10,3,6,10, +4,8,1576583,14,1575365,2,1310721,2,7,26,2,26,1310721,10,40,3, +36,1576577,16,1310721,2,9,26,1575365,2,10,3,6,10,4,8,1575365, +1575365,2,1310721,2,7,26,2,26,10,3,6,10,4,8,1576583,14, +1575867,12,8,1310721,2,11,26,1310721,2,9,26,2,11,26,10,3, +6,10,4,8,1310721,2,9,26,2,1,26,1310721,2,7,26,2, +10,3,6,10,4,8,1310721,2,9,26,2,3,26,1310721,2,5, +26,2,10,3,6,10,4,8,1310721,2,9,26,2,5,26,3, +10,3,6,10,4,8,1310721,2,9,26,2,7,26,1310721,2,11, +26,2,1459366445,36,10,3,6,10,4,8,1310721,2,9,26,2,9, +26,1,10,3,6,10,4,8,1310721,2,11,26,2,1,1310721,2, +7,26,2,11,28,1574133,12,8,1310721,2,11,26,2,1576833,14,1, +1310721,2,13,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,4, +1310721,2,2,1575631,12,1,6,1,10,28,34,63,38,3,26,1576905, +16,1,1576933,14,1310721,2,2,11,1,10,28,26,2,11,28,1576933, +14,1,1310721,2,3,26,1310721,4,18,1310721,2,11,28,1310721,4,1310721, +2,4,1310721,2,2,11,28,1310721,2,3,26,4,1310721,2,3,26, +2,1,26,2,11,28,1310721,2,5,26,4,1,1310721,2,7,26, +4,1310721,2,3,26,2,5,26,1,10,3,6,10,4,8,1310721, +2,7,26,2,1310721,2,5,26,2,40,1577275,16,1310721,2,9,26, +1310721,2,2,1310721,2,7,26,2,26,2,10,3,6,10,4,8, +1310721,2,9,26,2,1575631,12,1577215,16,1310721,2,9,26,2,3,1, +10,28,26,2,1310721,2,2,28,1,6,1,10,28,34,63,38, +3,26,1,6,1,10,28,34,63,38,1,10,28,1577217,14,1, +1577237,16,1310721,2,9,26,2,1576947,12,8,1310721,2,7,26,1,6, +2,1,6,3,26,5,6,4,10,8,8,1577055,14,1310721,2,3, +26,2,9,26,1,10,3,6,10,4,8,1310721,2,3,26,2, +11,26,1575367,1310721,2,3,26,2,3,26,2,26,2,10,3,6, +10,4,8,1575367,1310721,2,3,26,2,3,26,2,26,1310721,2,3, +26,2,10,3,6,10,4,8,1,1577401,14,1,1310721,2,11,26, +1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,4,1310721,2,2,1575631, +12,1577459,16,1310721,2,2,1576947,12,8,1,1577467,14,1,1310721,2,3, +26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,3,26,4,1310721, +2,4,1310721,2,2,1575631,12,1577561,16,1310721,2,2,3,1,10,28, +26,1310721,2,3,26,2,10,3,6,10,4,8,1310721,2,2,1577573, +14,1,1310721,2,5,26,1310721,4,18,0,0,0,0,1310721,2,3, +28,1310721,4,1310721,2,4,1310721,2,2,1575631,12,1,6,1,10,28, +34,63,38,3,26,1577653,16,3,1577697,14,1310721,2,2,7,1,10, +28,26,2,5,32,1,6,1,10,28,34,63,38,1,10,28, +1577707,16,1,1577851,14,1310721,2,2,7,1,10,28,26,1310721,2,2, +7,1,10,28,26,2,5,34,10,3,6,10,4,8,1577587,2, +32769,40,1577827,16,2031617,1577587,2,26,1310721,2,2,10,3,6,10,4, +8,1577587,1,6,2,1,6,3,26,5,6,4,10,8,8,1577843, +14,1577589,3,10,3,6,10,4,8,1,1577851,14,1,1310721,2,3, +26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,3,26,4,1310721, +2,4,1310721,2,2,1310721,2,3,26,2,40,1577963,16,1310721,2,2, +2,1577595,12,8,1310721,2,1,6,2,1,6,3,26,5,6,4, +10,8,8,1577893,14,1,1577971,14,1,1310721,2,5,26,1310721,4,18, +1310721,2,3,28,1310721,4,1577587,2,1578105,16,1577587,1,6,2,1,6, +-1,26,5,6,4,10,8,8,1310721,2,2031617,1577587,2,26,2,10, +3,6,10,4,8,1310721,2,2,1310721,2,2,1310721,2,2,11,1, +10,28,26,2,26,11,28,1577865,12,8,1577997,14,1,1578113,14,1, +1310721,2,3,26,1310721,4,18,1310721,2,7,28,1310721,4,1,1310721,2, +5,26,4,1577587,1,10,3,6,10,4,8,1577589,1,10,3,6, +10,4,8,1637825,12,8,1577985,12,8,1577589,2,1578379,16,1577589,1,10, +3,6,10,4,8,1310721,2,524289,10,3,6,10,4,8,1310721,2, +2,1575365,2,40,1578375,16,1310721,2,2,5,26,2,7,28,1,6, +1,10,28,34,63,38,3,26,1578329,16,1310721,2,2,11,26,1310721, +2,2,1310721,2,2,1,26,2,26,1577865,12,8,1310721,2,1310721,2, +2,1310721,2,2,1,26,2,26,10,3,6,10,4,8,1577985,12, +8,1578237,14,1578195,14,1310721,2,524289,10,3,6,10,4,8,1310721,2, +2,1575365,2,40,1578747,16,1310721,2,3,26,1310721,2,2,1,26,2, +10,3,6,10,4,8,1310721,2,2,5,26,2,3,28,1,6, +1,10,28,34,63,38,3,26,1578645,16,1310721,2,2,5,26,1, +10,3,6,10,4,8,1310721,2,2,9,26,1,10,3,6,10, +4,8,1310721,2,2,11,26,1575367,1310721,2,2,3,26,2,26,2, +10,3,6,10,4,8,1575367,1310721,2,2,3,26,2,26,1310721,2, +2,10,3,6,10,4,8,1310721,2,5,26,1,6,2,1,6, +3,26,5,6,4,10,8,8,1578709,14,1310721,2,2,5,26,2, +7,28,1,6,1,10,28,34,63,38,3,26,1578709,16,1310721,2, +2,5,26,3,10,3,6,10,4,8,1310721,2,1310721,2,2,1310721, +2,3,26,2,26,10,3,6,10,4,8,1578397,14,1575431,1,6, +2,1,6,3,26,5,6,4,10,8,8,1575433,1575433,2,1310721,2, +5,26,2,26,10,3,6,10,4,8,1310721,2,5,26,2,1578821, +14,1,1310721,2,7,26,1310721,4,18,1310721,2,5,28,1310721,4,524289, +1310721,2,4,1,1310721,2,3,26,4,1310721,2,2,1575365,2,40,1578977, +16,1310721,2,2,5,26,2,3,32,1578937,16,1310721,2,3,26,1, +6,2,1,6,3,26,5,6,4,10,8,8,1310721,2,1310721,2, +2,1310721,2,2,1,26,2,26,10,3,6,10,4,8,1578867,14, +1310721,2,3,26,2,1578993,14,1,1310721,2,5,26,1310721,4,18,0, +0,0,0,0,0,0,0,0,0,0,789515,113,64,33,36,95, +126,99,106,122,114,44,63,46,43,45,42,38,124,94,62,60, +0,1,0,0,0,0,1,0,0,0,0,-1,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,1310721,2,3,28,1310721,4,1310721,2,4,1581455,14,122,99,58, +32,108,105,110,101,32,0,1581435,1573031,12,8,1579083,2,1573171,12,8, +1581483,14,58,32,0,1581477,1573031,12,8,1310721,2,2,1573031,12,8,21, +1572879,12,8,1572995,12,8,1,1581525,14,1,1310721,2,3,26,1310721,4, +18,1310721,2,5,28,1310721,4,1310721,2,3,26,4,1310721,2,4,1581591, +14,122,99,58,32,108,105,110,101,32,0,1581571,1573031,12,8,1579083, +2,1573171,12,8,1581619,14,58,32,0,1581613,1573031,12,8,1310721,2,2, +1573031,12,8,1581649,14,58,32,0,1581643,1573031,12,8,1310721,2,3,26, +2,1573031,12,8,21,1572879,12,8,1572995,12,8,1,1581695,14,1,1310721, +2,5,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,4,1581313, +2,1581765,16,1581317,2,1,6,1,10,28,34,63,38,1,10,28, +1581767,14,1,1581783,16,1310721,2,2,1581413,12,8,1,1581791,14,1,1310721, +2,3,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,3,26, +4,1310721,2,4,1581313,2,1581871,16,1581317,2,1,6,1,10,28,34, +63,38,1,10,28,1581873,14,1,1581905,16,1466881,1310721,2,2,26,2, +1310721,2,3,26,2,1581539,12,8,1,1581913,14,1,1310721,2,5,26, +1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,4,1,1310721,2,3, +26,4,1310721,2,2,2,1582047,16,1310721,2,3,26,1310721,2,3,26, +2,63,30,1310721,2,2,2,26,10,3,6,10,4,8,1310721,2, +1,6,2,1,6,3,26,5,6,4,10,8,8,1581957,14,1310721, +2,3,26,2,16385,3,28,32,1582071,14,1,1310721,2,5,26,1310721, +4,18,1310721,2,9,28,1310721,4,1310721,2,4,1310721,2,2,1581927,12, +1310721,2,3,26,4,3,1582287,16,1310721,2,5,26,1311233,1310721,2,3, +26,2,26,2,10,3,6,10,4,8,1310721,2,5,26,2,1, +6,1,10,28,34,63,38,3,26,1582203,16,1582287,14,1310721,2,5, +26,2,1310721,2,2,1573657,12,1582241,16,1310721,2,5,26,2,1582567,14, +1310721,2,3,26,1310721,2,3,26,2,3,26,16385,3,28,32,10, +3,6,10,4,8,1582123,14,1310721,2,7,26,1310721,2,2,1573535,12, +10,3,6,10,4,8,1579007,2,1310721,2,7,26,2,26,5,26, +1409025,40,3,36,1582395,16,1582387,14,115,116,114,105,110,103,32,112, +111,111,108,32,102,117,108,108,0,1582353,1581413,12,8,1579007,2,1, +26,1,10,3,6,10,4,8,1310721,2,5,26,1579007,2,3,26, +10,3,6,10,4,8,1310721,2,5,26,2,1310721,2,2,1310721,2, +7,26,2,3,26,1573943,12,8,1579007,1579007,2,1310721,2,7,26,2, +26,5,26,10,3,6,10,4,8,1311233,1310721,2,3,26,2,26, +1310721,2,5,26,2,10,3,6,10,4,8,1310721,2,5,26,2, +1582567,14,1,1310721,2,9,26,1310721,4,18,1310721,2,9,28,1310721,4, +1310721,2,5,26,4,1310721,2,3,26,4,1310721,2,4,1579009,2,1310721, +2,7,26,4,1579009,2,8193,40,3,36,1582697,16,1582689,14,115,121, +109,98,111,108,32,116,97,98,108,101,32,102,117,108,108,0, +1582653,1581413,12,8,1579009,1,6,2,1,6,3,26,5,6,4,10, +8,8,1409537,1310721,2,7,26,2,26,1310721,2,2,10,3,6,10, +4,8,1417729,1310721,2,7,26,2,26,1310721,2,3,26,2,10,3, +6,10,4,8,1425921,1310721,2,7,26,2,26,1310721,2,5,26,2, +10,3,6,10,4,8,1434113,1310721,2,7,26,2,26,1,10,3, +6,10,4,8,1442305,1310721,2,7,26,2,26,3,10,3,6,10, +4,8,1450497,1310721,2,7,26,2,26,1,10,3,6,10,4,8, +1458689,1310721,2,7,26,2,26,1310721,2,2,3,1,10,28,26,2, +10,3,6,10,4,8,1310721,2,2,3,1,10,28,26,1310721,2, +7,26,2,3,26,10,3,6,10,4,8,1310721,2,7,26,2, +1583015,14,1,1310721,2,9,26,1310721,4,18,1310721,2,5,28,1310721,4, +1310721,2,4,1310721,2,2,3,1,10,28,26,2,3,28,1310721,2, +3,26,4,1310721,2,3,26,2,1,40,3,36,1583255,16,1310721,2, +3,26,2,1579013,2,40,3,36,1583131,16,3,1583193,14,1417729,1310721,2, +3,26,2,26,2,11,28,1,6,1,10,28,34,63,38,3, +26,1,6,1,10,28,34,63,38,1,10,28,1583211,16,1310721,2, +3,26,2,1583269,14,1310721,2,3,26,1458689,1310721,2,3,26,2,26, +2,3,28,10,3,6,10,4,8,1583079,14,3,1,10,28,1583269, +14,1,1310721,2,5,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721, +2,4,1579009,2,1310721,2,2,10,40,1583393,16,1579009,1,6,2,1, +6,-1,26,5,6,4,10,8,8,1409537,1579009,2,26,2,3,1, +10,28,26,1458689,1579009,2,26,2,10,3,6,10,4,8,1583301,14, +1,1583401,14,1,1310721,2,3,26,1310721,4,18,1310721,2,3,28,1310721, +4,1310721,2,4,1310721,2,2,1582085,12,1583029,12,1583453,14,1,1310721,2, +3,26,1310721,4,18,1310721,2,7,28,1310721,4,1310721,2,3,26,4, +1310721,2,4,1579017,2,1310721,2,5,26,4,1310721,2,5,26,2,1583557, +16,1579017,1491457,1310721,2,5,26,2,26,2,10,3,6,10,4,8, +1583699,14,1579015,1,6,2,1,6,3,26,5,6,4,10,8,8, +1310721,2,5,26,1579015,2,10,3,6,10,4,8,1310721,2,5,26, +2,16385,40,3,36,1583699,16,1583691,14,116,111,111,32,109,97,110, +121,32,102,111,114,119,97,114,100,32,114,101,102,101,114,101, +110,99,101,115,0,1583635,1581413,12,8,1475073,1310721,2,5,26,2,26, +1310721,2,2,10,3,6,10,4,8,1491457,1310721,2,5,26,2,26, +1310721,2,3,26,2,10,3,6,10,4,8,1310721,2,5,26,2, +1583783,14,1,1310721,2,7,26,1310721,4,18,1310721,2,7,28,1310721,4, +1310721,2,3,26,4,1310721,2,4,1310721,2,2,1583971,16,1475073,1310721,2, +2,26,2,1310721,2,3,26,2,1584275,12,8,1310721,2,5,26,1491457, +1310721,2,2,26,2,10,3,6,10,4,8,1491457,1310721,2,2,26, +1579017,2,10,3,6,10,4,8,1579017,1310721,2,2,10,3,6,10, +4,8,1310721,2,1310721,2,5,26,2,10,3,6,10,4,8,1583825, +14,1,1583979,14,1,1310721,2,7,26,1310721,4,18,1310721,2,3,28, +1310721,4,1310721,2,4,1581297,2,1581301,2,40,3,36,1584073,16,1584065,14, +112,114,111,103,114,97,109,32,116,111,111,32,98,105,103,0, +1584033,1581413,12,8,1581297,2,1581299,2,26,1310721,2,2,10,3,6,10, +4,8,1581297,1,6,2,1,6,3,26,5,6,4,10,8,8, +1,1584137,14,1,1310721,2,3,26,1310721,4,18,1310721,2,3,28,1310721, +4,1310721,2,4,1310721,2,2,1310721,2,2,26,3,26,1583993,12,8, +1,1584201,14,1,1310721,2,3,26,1310721,4,18,1310721,2,3,28,1310721, +4,1310721,2,4,1310721,2,2,1310721,2,2,26,1583993,12,8,1,1584261, +14,1,1310721,2,3,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721, +2,3,26,4,1310721,2,4,1310721,2,2,1581299,2,26,1310721,2,3, +26,2,1310721,2,3,26,2,26,3,26,10,3,6,10,4,8, +1,1584361,14,1,1310721,2,5,26,1310721,4,18,1310721,2,3,28,1310721, +4,1310721,2,4,1310721,2,2,1584151,12,8,15,1584215,12,8,1,1584421, +14,1,1310721,2,3,26,1310721,4,18,1310721,2,3,28,1310721,4,1581297, +2,1310721,2,4,2097153,3,28,1584151,12,8,1310721,2,2,1584481,14,1, +1310721,2,3,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,4, +1310721,2,2,3,1,1582581,12,1310721,2,3,26,4,1442305,1310721,2,3, +26,2,26,1,10,3,6,10,4,8,1310721,2,3,26,2,1584581, +14,1,1310721,2,5,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721, +2,4,1442305,1310721,2,2,26,2,3,32,1584657,16,1425921,1310721,2,2, +26,2,1584151,12,8,1,1584719,14,1450497,1310721,2,2,26,1581297,2,1450497, +1310721,2,2,26,2,1583467,12,10,3,6,10,4,8,2097153,3,28, +1584151,12,8,1,1584719,14,1,1310721,2,3,26,1310721,4,18,1310721,2, +5,28,1310721,4,1310721,2,4,1310721,2,2,1583029,12,1310721,2,3,26, +4,1310721,2,3,26,2,1,40,1584819,16,1310721,2,3,26,1310721,2, +2,1584495,12,10,3,6,10,4,8,1310721,2,3,26,2,1584595,12, +8,13,1584215,12,8,1,1584851,14,1,1310721,2,5,26,1310721,4,18, +1310721,2,3,28,1310721,4,1310721,2,4,1310721,1584151,12,8,3,1584215,12, +8,1310721,2,2,1584929,16,1310721,2,2,1584151,12,8,27,1584215,12,8, +1,1584937,14,1,1310721,2,3,26,1310721,4,18,1310721,2,7,28,1310721, +4,1310721,2,4,1584435,12,1310721,2,3,26,4,15,1584215,12,8,1310721, +2,5,26,1581297,2,10,3,6,10,4,8,1310721,2,2,2,1585075, +16,1310721,2,2,2,1583993,12,8,1310721,2,1,6,2,1,6,3, +26,5,6,4,10,8,8,1585015,14,1,1583993,12,8,1310721,2,3, +26,2,1581297,2,1584275,12,8,1310721,2,5,26,2,1584151,12,8,1, +1585127,14,1,1310721,2,7,26,1310721,4,18,1310721,2,1,28,1310721,4, +1579089,2,1,28,1,6,1,10,28,34,63,38,3,26,1585355,16, +1579093,2,1585199,16,3,1585225,14,1572961,12,1,6,1,10,28,34,63, +38,1,10,28,1585257,16,1579087,3,1,10,28,10,3,6,10,4, +8,1,1585505,14,1579087,1572927,12,10,3,6,10,4,8,1579087,2,193, +28,1,6,1,10,28,34,63,38,3,26,1585351,16,1579093,3,10, +3,6,10,4,8,1579087,3,1,10,28,10,3,6,10,4,8, +1,1585505,14,1585437,14,1579087,1579091,2,2,10,3,6,10,4,8,1579087, +2,1585415,16,1579091,1,6,2,1,6,3,26,5,6,4,10,8, +8,1585437,14,1579087,3,1,10,28,10,3,6,10,4,8,1579087,2, +21,28,1,6,1,10,28,34,63,38,3,26,1585497,16,1579085,1, +6,2,1,6,3,26,5,6,4,10,8,8,1,1585505,14,1, +1310721,2,1,26,1310721,4,18,1310721,2,11,28,1310721,4,1310721,2,7, +26,4,1310721,2,5,26,4,1310721,2,3,26,4,1310721,2,4,1579095, +1,6,2,1,6,3,26,5,6,4,10,8,8,1579095,2,65, +40,3,36,1585675,16,1585667,14,115,111,117,114,99,101,115,32,110, +101,115,116,101,100,32,116,111,111,32,100,101,101,112,108,121, +0,1585615,1581413,12,8,1310721,2,9,26,1540609,1579095,2,25,30,26,10, +3,6,10,4,8,1310721,2,9,26,2,1,26,1310721,2,2,10, +3,6,10,4,8,1310721,2,9,26,2,3,26,1310721,2,3,26, +2,10,3,6,10,4,8,1310721,2,9,26,2,5,26,1,10, +3,6,10,4,8,1310721,2,9,26,2,7,26,1,10,3,6, +10,4,8,1310721,2,9,26,2,9,26,1310721,2,5,26,2,10, +3,6,10,4,8,1310721,2,9,26,2,23,26,1310721,2,7,26, +2,10,3,6,10,4,8,1310721,2,9,26,2,11,26,1579077,2, +10,3,6,10,4,8,1310721,2,9,26,2,13,26,1579079,2,10, +3,6,10,4,8,1310721,2,9,26,2,15,26,1579081,2,10,3, +6,10,4,8,1310721,2,9,26,2,17,26,1579087,2,10,3,6, +10,4,8,1310721,2,9,26,2,19,26,1579089,2,10,3,6,10, +4,8,1310721,2,9,26,2,21,26,1579091,2,10,3,6,10,4, +8,1310721,2,2,3,28,1,6,1,10,28,34,63,38,3,26, +1586159,16,1310721,2,9,26,2,7,26,1310721,2,3,26,2,1,26, +2,10,3,6,10,4,8,1310721,2,2,1,28,1,6,1,10, +28,34,63,38,3,26,1586231,16,1579089,1,10,3,6,10,4,8, +1579093,1,10,3,6,10,4,8,1585141,12,8,1310721,2,2,5,28, +1,6,1,10,28,34,63,38,3,26,1586311,16,1579089,5,10,3, +6,10,4,8,1579091,1310721,2,3,26,2,10,3,6,10,4,8, +1585141,12,8,1,1586319,14,1,1310721,2,11,26,1310721,4,18,1310721,2, +3,28,1310721,4,1540609,1579095,2,25,30,26,1310721,2,4,1579077,1310721,2, +2,11,26,2,10,3,6,10,4,8,1579079,1310721,2,2,13,26, +2,10,3,6,10,4,8,1579081,1310721,2,2,15,26,2,10,3, +6,10,4,8,1579087,1310721,2,2,17,26,2,10,3,6,10,4, +8,1579089,1310721,2,2,19,26,2,10,3,6,10,4,8,1579091,1310721, +2,2,21,26,2,10,3,6,10,4,8,1579095,1,6,2,1, +6,-1,26,5,6,4,10,8,8,1310721,2,2,23,26,2,1586581, +16,1310721,2,2,3,26,2,1577415,12,8,1,1586589,14,1,1310721,2, +3,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,4,1586665,14, +43,45,42,47,37,61,60,62,33,38,124,94,126,63,58,46, +64,35,36,0,1586625,1310721,2,3,26,4,1310721,2,3,26,2,2, +1586781,16,1310721,2,3,26,2,2,1310721,2,2,28,1,6,1,10, +28,34,63,38,3,26,1586743,16,3,1586789,14,1310721,2,3,26,1, +6,2,1,6,3,26,5,6,4,10,8,8,1586677,14,1,1586789, +14,1,1310721,2,5,26,1310721,4,18,1310721,2,11,28,1310721,4,1310721, +2,3,26,4,1310721,2,4,1,1310721,2,5,26,4,1310721,2,5, +26,2,1579025,2,40,1587245,16,1310721,2,7,26,1547777,1310721,2,5,26, +2,26,2,10,3,6,10,4,8,1310721,2,9,26,1,10,3, +6,10,4,8,1310721,2,9,26,2,1310721,2,2,40,1587031,16,1310721, +2,7,26,2,1310721,2,9,26,2,26,2,1579097,1310721,2,9,26, +2,26,2,28,1,6,1,10,28,34,63,38,3,26,1,6, +1,10,28,34,63,38,1,10,28,1587033,14,1,1587075,16,1310721,2, +9,26,1,6,2,1,6,3,26,5,6,4,10,8,8,1586921, +14,1310721,2,9,26,2,1310721,2,2,28,1,6,1,10,28,34, +63,38,3,26,1587195,16,1310721,2,7,26,2,1310721,2,2,26,2, +1310721,2,3,26,2,28,1,6,1,10,28,34,63,38,3,26, +1,6,1,10,28,34,63,38,1,10,28,1587197,14,1,1587207,16, +3,1587253,14,1310721,2,5,26,1,6,2,1,6,3,26,5,6, +4,10,8,8,1586843,14,1,1587253,14,1,1310721,2,11,26,1310721,4, +18,1310721,2,3,28,1310721,4,1310721,2,4,1310721,2,2,97,40,3, +36,1587345,16,1310721,2,2,115,10,40,3,36,1,6,1,10,28, +34,63,38,1,10,28,1587347,14,1,1587353,14,1,1310721,2,3,26, +1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,4,1310721,2,2,195, +40,3,36,1587445,16,1310721,2,2,245,10,40,3,36,1,6,1, +10,28,34,63,38,1,10,28,1587447,14,1,1587457,16,3,1587541,14, +1310721,2,2,131,40,3,36,1587517,16,1310721,2,2,181,10,40,3, +36,1,6,1,10,28,34,63,38,1,10,28,1587519,14,1,1, +6,1,10,28,34,63,38,1,10,28,1587551,16,3,1587603,14,1310721, +2,2,191,28,1,6,1,10,28,34,63,38,3,26,1,6, +1,10,28,34,63,38,1,10,28,1587609,14,1,1310721,2,3,26, +1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,4,1310721,2,2,1587267, +12,1587669,16,1310721,2,2,97,28,1587851,14,1310721,2,2,195,40,3, +36,1587729,16,1310721,2,2,205,10,40,3,36,1,6,1,10,28, +34,63,38,1,10,28,1587731,14,1,1587753,16,1310721,2,2,195,28, +21,26,1587851,14,1310721,2,2,131,40,3,36,1587813,16,1310721,2,2, +141,10,40,3,36,1,6,1,10,28,34,63,38,1,10,28, +1587815,14,1,1587837,16,1310721,2,2,131,28,21,26,1587851,14,3,1, +10,28,1587851,14,1,1310721,2,3,26,1310721,4,18,1310721,2,3,28, +1310721,4,1579087,2,1310721,2,4,1585141,12,8,1310721,2,2,185,28,1, +6,1,10,28,34,63,38,1,10,28,1587939,16,1310721,2,2,1588177, +14,1310721,2,1579087,2,10,3,6,10,4,8,1585141,12,8,1310721,2, +2,221,28,1,6,1,10,28,34,63,38,3,26,1588005,16,21, +1588177,14,1310721,2,2,233,28,1,6,1,10,28,34,63,38,3, +26,1588045,16,19,1588177,14,1310721,2,2,229,28,1,6,1,10,28, +34,63,38,3,26,1588085,16,27,1588177,14,1310721,2,2,97,28,1, +6,1,10,28,34,63,38,3,26,1588125,16,1,1588177,14,1310721,2, +2,203,28,1,6,1,10,28,34,63,38,3,26,1588165,16,55, +1588177,14,1310721,2,2,1588177,14,1,1310721,2,3,26,1310721,4,18,1310721, +2,3,28,1310721,4,1310721,2,4,1579087,2,1,40,3,36,1588259,16, +1579087,2,1586603,12,1,6,1,10,28,34,63,38,1,10,28,1588261, +14,1,1588305,16,1310721,2,2,1579087,2,1586803,12,1,6,1,10,28, +34,63,38,1,10,28,1588307,14,1,1588377,16,1579097,1310721,2,2,26, +1579087,2,10,3,6,10,4,8,1310721,2,1,6,2,1,6,3, +26,5,6,4,10,8,8,1585141,12,8,1588209,14,1579097,1310721,2,2, +26,1,10,3,6,10,4,8,1579077,9,10,3,6,10,4,8, +1579079,1579097,1582085,12,10,3,6,10,4,8,1,1588445,14,1,1310721,2, +3,26,1310721,4,18,1310721,2,7,28,1310721,4,1,1310721,2,4,1579081, +3,10,3,6,10,4,8,3,1589147,16,1579087,2,65,28,1,6, +1,10,28,34,63,38,3,26,1588539,16,3,1588589,14,1579087,2,19, +28,1,6,1,10,28,34,63,38,3,26,1,6,1,10,28, +34,63,38,1,10,28,1588599,16,3,1588649,14,1579087,2,21,28,1, +6,1,10,28,34,63,38,3,26,1,6,1,10,28,34,63, +38,1,10,28,1588659,16,3,1588709,14,1579087,2,27,28,1,6,1, +10,28,34,63,38,3,26,1,6,1,10,28,34,63,38,1, +10,28,1588739,16,1585141,12,8,1579081,1,10,3,6,10,4,8,1588501, +14,1579087,2,95,28,1,6,1,10,28,34,63,38,1,10,28, +1588777,16,1589147,14,1585141,12,8,1579087,2,95,28,1,6,1,10,28, +34,63,38,3,26,1588923,16,1579087,2,1,40,3,36,1588887,16,1579087, +2,21,28,1,6,1,10,28,34,63,38,1,10,28,1,6, +1,10,28,34,63,38,1,10,28,1588889,14,1,1588903,16,1585141,12, +8,1588815,14,1579081,1,10,3,6,10,4,8,1588495,14,1579087,2,85, +28,1,6,1,10,28,34,63,38,3,26,1589091,16,1585141,12,8, +1579087,2,1,40,3,36,1589065,16,1579087,2,85,28,1,6,1,10, +28,34,63,38,3,26,1589055,16,1585141,12,8,1579087,2,95,28,1, +6,1,10,28,34,63,38,3,26,1589051,16,1589065,14,1589061,14,1585141, +12,8,1588961,14,1585141,12,8,1579081,1,10,3,6,10,4,8,1588495, +14,1579083,1579085,2,10,3,6,10,4,8,1579097,1,26,95,10,3, +6,10,4,8,3,1588191,12,8,1,1590431,14,1588495,14,1579083,1579085,2, +10,3,6,10,4,8,1579087,2,1,40,1589215,16,1579077,1,10,3, +6,10,4,8,1579079,1,10,3,6,10,4,8,1,1590431,14,1579087, +2,1587267,12,1589637,16,1310721,2,3,26,1,10,3,6,10,4,8, +1579087,2,97,28,1,6,1,10,28,34,63,38,3,26,1589525,16, +1585141,12,8,1579087,2,241,28,1,6,1,10,28,34,63,38,3, +26,1589325,16,3,1589375,14,1579087,2,177,28,1,6,1,10,28,34, +63,38,3,26,1,6,1,10,28,34,63,38,1,10,28,1589525, +16,1585141,12,8,1310721,2,5,26,1579087,2,1587623,12,10,3,6,10, +4,1,40,3,36,1589479,16,1310721,2,3,26,1310721,2,3,26,2, +33,30,1310721,2,5,26,2,26,10,3,6,10,4,8,1585141,12, +8,1589385,14,1579077,3,10,3,6,10,4,8,1579079,1310721,2,3,26, +2,10,3,6,10,4,8,1,1590431,14,1579087,2,1587267,12,1589591,16, +1310721,2,3,26,1310721,2,3,26,2,21,30,1579087,2,26,97,28, +10,3,6,10,4,8,1585141,12,8,1589525,14,1579077,3,10,3,6, +10,4,8,1579079,1310721,2,3,26,2,10,3,6,10,4,8,1, +1590431,14,1579087,2,1587367,12,1589847,16,1579087,2,1587367,12,1589667,16,3,1589697, +14,1579087,2,1587267,12,1,6,1,10,28,34,63,38,1,10,28, +1589781,16,1310721,2,2,2001,40,1589771,16,1579097,1310721,2,2,26,1579087,2, +10,3,6,10,4,8,1310721,2,1,6,2,1,6,3,26,5, +6,4,10,8,8,1585141,12,8,1589649,14,1579097,1310721,2,2,26,1, +10,3,6,10,4,8,1579077,5,10,3,6,10,4,8,1579079,1579097, +1582085,12,10,3,6,10,4,8,1,1590431,14,1579087,2,79,28,1, +6,1,10,28,34,63,38,3,26,1590021,16,1585141,12,8,1579079,1587865, +12,10,3,6,10,4,8,1579087,2,79,28,1,6,1,10,28, +34,63,38,1,10,28,1589993,16,1589985,14,98,97,100,32,99,104, +97,114,97,99,116,101,114,32,108,105,116,101,114,97,108,0, +1589941,1581413,12,8,1585141,12,8,1579077,3,10,3,6,10,4,8,1, +1590431,14,1579087,2,69,28,1,6,1,10,28,34,63,38,3,26, +1590313,16,1585141,12,8,1579087,2,1,40,3,36,1590131,16,1579087,2,69, +28,1,6,1,10,28,34,63,38,1,10,28,1,6,1,10, +28,34,63,38,1,10,28,1590133,14,1,1590241,16,1310721,2,3,26, +1587865,12,10,3,6,10,4,8,1310721,2,2,2001,40,1590237,16,1579097, +1310721,2,2,26,1310721,2,3,26,2,10,3,6,10,4,8,1310721, +2,1,6,2,1,6,3,26,5,6,4,10,8,8,1590059,14, +1585141,12,8,1579097,1310721,2,2,26,1,10,3,6,10,4,8,1579077, +7,10,3,6,10,4,8,1579079,1579097,1582085,12,10,3,6,10,4, +8,1,1590431,14,1579097,1,26,1579087,2,10,3,6,10,4,8,1585141, +12,8,1579097,1,26,2,1586603,12,1590367,16,3,1588191,12,1590431,14,1579097, +3,26,1,10,3,6,10,4,8,1579077,9,10,3,6,10,4, +8,1579079,1579097,1582085,12,10,3,6,10,4,8,1,1590431,14,1,1310721, +2,7,26,1310721,4,18,1310721,2,7,28,1310721,4,1310721,2,1540609,1579095, +2,25,30,26,10,3,6,10,4,8,1310721,2,2,1,26,2, +3,28,1,6,1,10,28,34,63,38,1,10,28,1590539,16,1588459, +12,8,1,1590885,14,1310721,2,5,26,1310721,2,2,5,26,2,10, +3,6,10,4,8,1310721,2,5,26,2,1310721,2,2,7,26,2, +40,1590797,16,1310721,2,3,26,1310721,2,2,3,26,2,10,3,6, +10,4,8,1579077,1310721,2,3,26,2,3,1310721,2,5,26,2,26, +1310721,2,5,26,2,26,26,2,10,3,6,10,4,8,1579079,1310721, +2,3,26,2,5,1310721,2,5,26,2,26,1310721,2,5,26,2, +26,26,2,10,3,6,10,4,8,1579081,1,10,3,6,10,4, +8,1310721,2,2,5,26,1310721,2,5,26,2,3,26,10,3,6, +10,4,8,1,1590885,14,1310721,2,2,9,26,2,1,6,1,10, +28,34,63,38,3,26,1590871,16,1579077,1,10,3,6,10,4,8, +1579079,1,10,3,6,10,4,8,1,1590885,14,1586333,12,8,1,1590885, +14,1,1310721,2,7,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721, +2,4,1579077,2,9,28,1,6,1,10,28,34,63,38,3,26, +1591007,16,1579079,2,1310721,2,2,28,1,6,1,10,28,34,63,38, +3,26,1,6,1,10,28,34,63,38,1,10,28,1591009,14,1, +1591015,14,1,1310721,2,3,26,1310721,4,18,1310721,2,3,28,1310721,4, +1310721,2,4,1579077,2,5,28,1,6,1,10,28,34,63,38,3, +26,1591137,16,1579079,2,1310721,2,2,28,1,6,1,10,28,34,63, +38,3,26,1,6,1,10,28,34,63,38,1,10,28,1591139,14, +1,1591145,14,1,1310721,2,3,26,1310721,4,18,1310721,2,3,28,1310721, +4,1310721,2,4,1579077,2,9,28,1,6,1,10,28,34,63,38, +3,26,1591215,16,3,1591265,14,1579077,2,5,28,1,6,1,10,28, +34,63,38,3,26,1,6,1,10,28,34,63,38,1,10,28, +1591327,16,1579079,2,1310721,2,2,28,1,6,1,10,28,34,63,38, +3,26,1,6,1,10,28,34,63,38,1,10,28,1591329,14,1, +1591345,16,1590445,12,8,3,1591353,14,1,1591353,14,1,1310721,2,3,26, +1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,4,1310721,2,2,1591159, +12,1,6,1,10,28,34,63,38,3,26,1591523,16,1591443,14,122, +99,58,32,108,105,110,101,32,0,1591423,1573031,12,8,1579083,2,1573171, +12,8,1591489,14,58,32,101,120,112,101,99,116,101,100,32,0, +1591465,1573031,12,8,1310721,2,2,1573031,12,8,21,1572879,12,8,1572995,12, +8,1,1591531,14,1,1310721,2,3,26,1310721,4,18,1310721,2,3,28, +1310721,4,1579079,2,1310721,2,4,1579077,2,5,28,1,6,1,10,28, +34,63,38,1,10,28,1591645,16,1591637,14,101,120,112,101,99,116, +101,100,32,97,32,110,97,109,101,0,1591605,1581413,12,8,1590445,12, +8,1310721,2,2,1591663,14,1,1310721,2,3,26,1310721,4,18,1310721,2, +15,28,1310721,4,1310721,2,4,1579027,2,1310721,2,3,26,4,3,1592763, +16,1310721,2,2,2,65,28,1,6,1,10,28,34,63,38,3, +26,1591785,16,1310721,2,1,6,2,1,6,3,26,5,6,4,10, +8,8,1591715,14,1310721,2,2,2,1,6,1,10,28,34,63,38, +3,26,1591821,16,1592763,14,1310721,2,5,26,1,10,3,6,10,4, +8,1310721,2,2,1310721,2,5,26,2,26,2,1591939,16,1310721,2,2, +1310721,2,5,26,2,26,2,65,28,1,6,1,10,28,34,63, +38,1,10,28,1,6,1,10,28,34,63,38,1,10,28,1591941, +14,1,1592029,16,1579097,1310721,2,5,26,2,26,1310721,2,2,1310721,2, +5,26,2,26,2,10,3,6,10,4,8,1310721,2,5,26,1, +6,2,1,6,3,26,5,6,4,10,8,8,1591843,14,1579097,1310721, +2,5,26,2,26,1,10,3,6,10,4,8,1310721,2,1310721,2, +2,1310721,2,5,26,2,26,10,3,6,10,4,8,1310721,2,7, +26,1579097,1,26,2,91,28,1,6,1,10,28,34,63,38,3, +26,1592173,16,1579097,3,26,2,1587267,12,1,6,1,10,28,34,63, +38,1,10,28,1592175,14,1,10,3,6,10,4,8,1579097,1310721,2, +7,26,2,26,2,1587267,12,1592503,16,1310721,2,9,26,1,10,3, +6,10,4,8,1310721,2,11,26,1310721,2,7,26,2,10,3,6, +10,4,8,1579097,1310721,2,11,26,2,26,2,1592377,16,1310721,2,9, +26,1310721,2,9,26,2,21,30,1579097,1310721,2,11,26,2,26,2, +26,97,28,10,3,6,10,4,8,1310721,2,11,26,1,6,2, +1,6,3,26,5,6,4,10,8,8,1592263,14,1310721,2,7,26, +2,1592425,16,1310721,2,9,26,1,1310721,2,9,26,2,28,10,3, +6,10,4,8,1542657,1579027,2,26,1310721,2,9,26,2,1310721,2,9, +26,2,26,3,26,10,3,6,10,4,8,1579027,1,6,2,1, +6,3,26,5,6,4,10,8,8,1592759,14,1310721,2,5,26,2, +3,28,1,6,1,10,28,34,63,38,3,26,1592605,16,1310721,2, +13,26,1579097,1,26,2,1592793,12,10,3,6,10,4,1,40,3, +36,1,6,1,10,28,34,63,38,1,10,28,1592607,14,1,1592685, +16,1542657,1579027,2,26,1310721,2,13,26,2,1310721,2,13,26,2,26, +10,3,6,10,4,8,1579027,1,6,2,1,6,3,26,5,6, +4,10,8,8,1592759,14,1542657,1579027,2,26,-3,10,3,6,10,4, +8,1542657,1579027,2,3,26,26,1579097,1582085,12,10,3,6,10,4,8, +1579027,1579027,2,5,26,10,3,6,10,4,8,1591709,14,1310721,2,3, +26,2,1592779,14,1,1310721,2,15,26,1310721,4,18,1310721,2,5,28, +1310721,4,1310721,2,4,1,1310721,2,3,26,4,1579029,2,1310721,2,3, +26,2,26,2,1592947,16,1579029,2,1310721,2,3,26,2,26,2,1310721, +2,2,28,1,6,1,10,28,34,63,38,3,26,1592909,16,1310721, +2,3,26,2,1592961,14,1310721,2,3,26,1,6,2,1,6,3, +26,5,6,4,10,8,8,1592823,14,3,1,10,28,1592961,14,1, +1310721,2,5,26,1310721,4,18,1310721,2,9,28,1310721,4,1310721,2,3, +26,4,1310721,2,4,1310721,2,2,1310721,2,3,26,2,26,1310721,2, +5,26,4,1310721,2,2,1310721,2,5,26,2,40,1593359,16,1542657,1310721, +2,2,26,2,-3,28,1,6,1,10,28,34,63,38,3,26, +1593307,16,1310721,2,7,26,1542657,1310721,2,2,3,26,26,2,1583029,12, +10,3,6,10,4,8,1310721,2,7,26,2,1,40,1593191,16,1310721, +2,7,26,1542657,1310721,2,2,3,26,26,2,1584495,12,10,3,6, +10,4,8,1417729,1310721,2,7,26,2,26,2,3,28,1,6,1, +10,28,34,63,38,3,26,1593255,16,1310721,2,7,26,2,1584595,12, +8,1593277,14,1425921,1310721,2,7,26,2,26,2,1584151,12,8,1310721,2, +1310721,2,2,5,26,10,3,6,10,4,8,1593355,14,1542657,1310721,2, +2,26,2,1583993,12,8,1310721,2,1,6,2,1,6,3,26,5, +6,4,10,8,8,1593031,14,1,1593367,14,1,1310721,2,9,26,1310721, +4,18,1310721,2,5,28,1310721,4,1310721,2,4,1,1310721,2,3,26, +4,1310721,2,3,26,2,1579025,2,40,1593523,16,1547777,1310721,2,3,26, +2,26,2,1310721,2,2,28,1,6,1,10,28,34,63,38,3, +26,1593485,16,1,1593585,14,1310721,2,3,26,1,6,2,1,6,3, +26,5,6,4,10,8,8,1593411,14,1547777,1579025,2,26,1310721,2,2, +10,3,6,10,4,8,1579025,1,6,2,1,6,3,26,5,6, +4,10,8,8,1,1593585,14,1,1310721,2,5,26,1310721,4,18,1310721, +2,5,28,1310721,4,1310721,2,4,1579019,2,1310721,2,3,26,4,1310721, +2,3,26,2,1593745,16,1310721,2,3,26,1,6,2,1,6,-1, +26,5,6,4,10,8,8,1546753,1310721,2,3,26,2,26,2,1310721, +2,2,28,1,6,1,10,28,34,63,38,3,26,1593741,16,1310721, +2,3,26,2,1593759,14,1593631,14,3,1,10,28,1593759,14,1,1310721, +2,5,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,4,1579021, +2,1310721,2,3,26,4,1310721,2,3,26,2,1593919,16,1310721,2,3, +26,1,6,2,1,6,-1,26,5,6,4,10,8,8,1547265,1310721, +2,3,26,2,26,2,1310721,2,2,28,1,6,1,10,28,34, +63,38,3,26,1593915,16,1310721,2,3,26,2,1593933,14,1593805,14,3, +1,10,28,1593933,14,1,1310721,2,5,26,1310721,4,18,1310721,2,5, +28,1310721,4,1310721,2,4,1579023,2,1310721,2,3,26,4,1310721,2,3, +26,2,1594093,16,1310721,2,3,26,1,6,2,1,6,-1,26,5, +6,4,10,8,8,1547457,1310721,2,3,26,2,26,2,1310721,2,2, +28,1,6,1,10,28,34,63,38,3,26,1594089,16,1310721,2,3, +26,2,1594107,14,1593979,14,3,1,10,28,1594107,14,1,1310721,2,5, +26,1310721,4,18,1310721,2,15,28,1310721,4,1310721,2,5,26,4,1310721, +2,3,26,4,1310721,2,4,1310721,2,2,1582085,12,1310721,2,7,26, +4,1310721,2,7,26,2,1593599,12,1310721,2,9,26,4,1310721,2,9, +26,2,1,40,1594273,16,1310721,2,9,26,1579019,2,10,3,6,10, +4,8,1579019,1,6,2,1,6,3,26,5,6,4,10,8,8, +1546753,1310721,2,9,26,2,26,1310721,2,7,26,2,10,3,6,10, +4,8,1546881,1310721,2,9,26,2,26,1310721,2,3,26,2,10,3, +6,10,4,8,1310721,2,3,26,2,1,40,1594497,16,1546881,1310721,2, +9,26,2,26,1,1310721,2,3,26,2,28,10,3,6,10,4, +8,1547137,1310721,2,9,26,2,26,1310721,2,3,26,2,10,3,6, +10,4,8,1547009,1310721,2,9,26,2,26,1,10,3,6,10,4, +8,1310721,2,7,26,2,1593381,12,8,1310721,2,9,26,2,1595125,14, +1547009,1310721,2,9,26,2,26,1310721,2,5,26,2,1591677,12,10,3, +6,10,4,8,1547137,1310721,2,9,26,2,26,1579027,2,1547009,1310721,2, +9,26,2,26,2,28,10,3,6,10,4,8,1310721,2,7,26, +2,1593381,12,8,1310721,2,13,26,1310721,2,2,1573535,12,10,3,6, +10,4,8,1579097,2001,26,1310721,2,2,1310721,2,13,26,2,1573943,12, +8,1579097,2001,1310721,2,13,26,2,26,26,123,10,3,6,10,4, +8,1579097,2003,1310721,2,13,26,2,26,26,1,10,3,6,10,4, +8,1310721,2,11,26,1579097,2001,26,1582085,12,10,3,6,10,4,8, +1310721,2,3,26,2,17,40,3,36,1594781,16,3,1594837,14,1310721,2, +3,26,2,11,28,1,6,1,10,28,34,63,38,3,26,1, +6,1,10,28,34,63,38,1,10,28,1594847,16,3,1594903,14,1310721, +2,3,26,2,9,28,1,6,1,10,28,34,63,38,3,26, +1,6,1,10,28,34,63,38,1,10,28,1594913,16,3,1594969,14, +1310721,2,3,26,2,7,28,1,6,1,10,28,34,63,38,3, +26,1,6,1,10,28,34,63,38,1,10,28,1595109,16,1310721,2, +11,26,2,1593947,12,1,40,1595053,16,1547457,1579023,2,26,1310721,2,11, +26,2,10,3,6,10,4,8,1579023,1,6,2,1,6,3,26, +5,6,4,10,8,8,1547585,1310721,2,11,26,2,1593947,12,26,1310721, +2,9,26,2,10,3,6,10,4,8,1310721,2,11,26,2,1593381, +12,8,1310721,2,9,26,2,1595125,14,1,1310721,2,15,26,1310721,4, +18,1310721,2,9,28,1310721,4,1310721,2,3,26,4,1310721,2,4,1310721, +2,2,1582085,12,1310721,2,5,26,4,1310721,2,5,26,2,1593773,12, +1310721,2,7,26,4,1310721,2,7,26,2,1,40,1595281,16,1310721,2, +7,26,1579021,2,10,3,6,10,4,8,1579021,1,6,2,1,6, +3,26,5,6,4,10,8,8,1547265,1310721,2,7,26,2,26,1310721, +2,5,26,2,10,3,6,10,4,8,1547329,1310721,2,7,26,2, +26,1310721,2,3,26,2,1591677,12,10,3,6,10,4,8,1547393,1310721, +2,7,26,2,26,1579027,2,1547329,1310721,2,7,26,2,26,2,28, +10,3,6,10,4,8,1310721,2,5,26,2,1593381,12,8,1310721,2, +7,26,2,1595437,14,1,1310721,2,9,26,1310721,4,18,1310721,2,9, +28,1310721,4,1310721,2,5,26,4,1310721,2,3,26,4,1310721,2,4, +1310721,2,2,1582085,12,11,1310721,2,3,26,2,1582581,12,1310721,2,7, +26,4,1442305,1310721,2,7,26,2,26,3,1310721,2,5,26,2,34, +10,3,6,10,4,8,1310721,2,7,26,2,1595581,14,1,1310721,2, +9,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,4,1581305,2, +1310721,2,3,26,4,1310721,2,3,26,2,1581307,2,10,40,1595749,16, +1310721,2,3,26,1,6,2,1,6,-1,26,5,6,4,10,8, +8,1466881,1310721,2,3,26,2,26,2,1310721,2,2,28,1,6,1, +10,28,34,63,38,3,26,1595745,16,1310721,2,3,26,2,1595763,14, +1595627,14,3,1,10,28,1595763,14,1,1310721,2,5,26,1310721,4,18, +1310721,2,5,28,1310721,4,1310721,2,4,1581305,2,1310721,2,3,26,4, +1581305,2,1025,40,3,36,1595869,16,1595861,14,116,111,111,32,109,97, +110,121,32,108,111,99,97,108,115,0,1595829,1581413,12,8,1581305,1, +6,2,1,6,3,26,5,6,4,10,8,8,1466881,1310721,2,3, +26,2,26,1310721,2,2,10,3,6,10,4,8,1467905,1310721,2,3, +26,2,26,1581311,2,10,3,6,10,4,8,1468929,1310721,2,3,26, +2,26,1581309,2,10,3,6,10,4,8,1469953,1310721,2,3,26,2, +26,1,10,3,6,10,4,8,1470977,1310721,2,3,26,2,26,1, +10,3,6,10,4,8,1472001,1310721,2,3,26,2,26,1,10,3, +6,10,4,8,1473025,1310721,2,3,26,2,26,1,10,3,6,10, +4,8,1474049,1310721,2,3,26,2,26,1,10,3,6,10,4,8, +1310721,2,3,26,2,1596145,14,1,1310721,2,5,26,1310721,4,18,1310721, +2,3,28,1310721,4,1310721,2,4,1469953,1310721,2,2,26,2,11,28, +1,6,1,10,28,34,63,38,3,26,1596249,16,1310721,2,1470977,1310721, +2,2,26,2,10,3,6,10,4,8,1596177,14,1310721,2,2,1596261, +14,1,1310721,2,3,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721, +2,4,1469953,1310721,2,2,26,2,1,28,1,6,1,10,28,34, +63,38,1,10,28,1596337,14,1,1310721,2,3,26,1310721,4,18,1310721, +2,5,28,1310721,4,1310721,2,4,1472001,1310721,2,2,26,3,10,3, +6,10,4,8,1581313,2,1,6,1,10,28,34,63,38,3,26, +1596427,16,1,1596803,14,1310721,2,3,26,1310721,2,2,1596159,12,10,3, +6,10,4,8,1472001,1310721,2,3,26,2,26,3,10,3,6,10, +4,8,1469953,1310721,2,3,26,2,26,2,9,28,1,6,1,10, +28,34,63,38,3,26,1596611,16,1310721,2,2,1596597,14,117,115,101, +100,32,97,102,116,101,114,32,105,116,32,119,97,115,32,103, +105,118,101,110,32,97,119,97,121,0,1596539,1581805,12,8,1,1596803, +14,1469953,1310721,2,3,26,2,26,2,7,28,1,6,1,10,28, +34,63,38,3,26,1596703,16,1468929,1310721,2,3,26,2,26,2,1581309, +2,40,1,6,1,10,28,34,63,38,1,10,28,1596705,14,1, +1596795,16,1468929,1310721,2,3,26,2,26,1581309,2,10,3,6,10,4, +8,1469953,1310721,2,3,26,2,26,3,10,3,6,10,4,8,1474049, +1310721,2,3,26,2,26,3,10,3,6,10,4,8,1,1596803,14, +1,1310721,2,5,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2, +4,1310721,2,2,3,28,1,6,1,10,28,34,63,38,3,26, +1597019,16,1597011,14,97,32,110,101,119,32,111,98,106,101,99,116, +32,99,97,110,39,116,32,98,101,32,117,115,101,100,32,97, +115,32,97,32,112,108,97,105,110,32,118,97,108,117,101,59, +32,98,105,110,100,32,105,116,32,116,111,32,97,32,110,97, +109,101,32,102,105,114,115,116,0,1596873,1581709,12,8,1310721,2,2, +5,40,3,36,1597053,16,1310721,2,2,5,28,1596351,12,8,1,1597061, +14,1,1310721,2,3,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721, +2,4,1467905,1310721,2,2,26,2,1584865,12,8,3,1584215,12,8,1581407, +2,1584733,12,8,9,1584215,12,8,1,1597145,14,1,1310721,2,3,26, +1310721,4,18,1310721,2,9,28,1310721,4,1310721,2,3,26,4,1310721,2, +4,1581305,2,1310721,2,5,26,4,1581313,2,1,6,1,10,28,34, +63,38,3,26,1597235,16,3,1597281,14,1581317,2,1,6,1,10,28, +34,63,38,3,26,1,6,1,10,28,34,63,38,1,10,28, +1597291,16,1,1597851,14,1310721,2,5,26,2,1581307,2,10,40,1597843,16, +1310721,2,5,26,1,6,2,1,6,-1,26,5,6,4,10,8, +8,1468929,1310721,2,5,26,2,26,2,1310721,2,2,40,1597379,16,1597291, +14,1310721,2,7,26,1469953,1310721,2,5,26,2,26,2,10,3,6, +10,4,8,1310721,2,7,26,2,3,28,1,6,1,10,28,34, +63,38,3,26,1597473,16,1310721,2,5,26,2,1597075,12,8,1597839,14, +1310721,2,7,26,2,5,28,1,6,1,10,28,34,63,38,3, +26,1597719,16,1310721,2,5,26,2,1597707,14,117,32,111,98,106,101, +99,116,32,109,117,115,116,32,108,101,97,118,101,32,116,104, +105,115,32,98,108,111,99,107,32,40,98,108,111,99,107,32, +118,97,108,117,101,44,32,97,115,115,105,103,110,109,101,110, +116,32,116,111,32,116,104,101,32,101,110,99,108,111,115,105, +110,103,32,98,108,111,99,107,44,32,111,114,32,114,101,116, +117,114,110,41,0,1597525,1581805,12,8,1597839,14,1310721,2,7,26,2, +7,28,1,6,1,10,28,34,63,38,3,26,1597839,16,1310721,2, +5,26,2,1597831,14,100,32,111,98,106,101,99,116,32,119,97, +115,32,110,101,118,101,114,32,103,105,118,101,110,32,97,119, +97,121,0,1597771,1581805,12,8,1597291,14,1,1597851,14,1,1310721,2,9, +26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,4,1310721,2,2, +1581305,2,40,1598119,16,1581313,2,1597945,16,1474049,1310721,2,2,26,2,1, +6,1,10,28,34,63,38,1,10,28,1597947,14,1,1598009,16,1472001, +1310721,2,2,26,2,1,6,1,10,28,34,63,38,3,26,1, +6,1,10,28,34,63,38,1,10,28,1598011,14,1,1598085,16,1466881, +1310721,2,2,26,2,1598077,14,109,32,111,98,106,101,99,116,32, +105,115,32,110,101,118,101,114,32,117,115,101,100,0,1598031,1581539, +12,8,1310721,2,1,6,2,1,6,3,26,5,6,4,10,8, +8,1597883,14,1,1598127,14,1,1310721,2,3,26,1310721,4,18,1310721,2, +5,28,1310721,4,1310721,2,4,1581307,2,1310721,2,3,26,4,1310721,2, +2,1597865,12,8,1581309,2,3,1597159,12,8,1310721,2,3,26,2,1310721, +2,2,40,1598349,16,1468929,1310721,2,3,26,2,26,2,1581309,2,40, +3,36,1598311,16,1469953,1310721,2,3,26,2,26,9,10,3,6,10, +4,8,1468929,1310721,2,3,26,2,26,1581309,2,3,28,10,3,6, +10,4,8,1310721,2,3,26,1,6,2,1,6,3,26,5,6, +4,10,8,8,1598197,14,1581305,1310721,2,2,10,3,6,10,4,8, +1,1598377,14,1,1310721,2,5,26,1310721,4,18,1310721,2,7,28,1310721, +4,1581333,2,1310721,2,4,1581307,2,1310721,2,3,26,4,1310721,2,5, +26,1507841,1310721,2,2,26,10,3,6,10,4,8,1310721,2,5,26, +2,1,26,1581307,2,10,3,6,10,4,8,1310721,2,5,26,2, +3,26,1581305,2,10,3,6,10,4,8,1310721,2,5,26,1310721,2, +5,26,2,5,26,10,3,6,10,4,8,1310721,2,3,26,2, +1581305,2,40,1598769,16,1310721,2,5,26,2,1,26,1469953,1310721,2,3, +26,2,26,2,10,3,6,10,4,8,1310721,2,5,26,2,3, +26,1470977,1310721,2,3,26,2,26,2,10,3,6,10,4,8,1310721, +2,5,26,2,5,26,1468929,1310721,2,3,26,2,26,2,10,3, +6,10,4,8,1310721,2,5,26,1310721,2,5,26,2,7,26,10, +3,6,10,4,8,1310721,2,3,26,1,6,2,1,6,3,26, +5,6,4,10,8,8,1598551,14,1581333,1310721,2,5,26,2,1507841,28, +10,3,6,10,4,8,1581333,2,16001,40,3,36,1598893,16,1598885,14, +110,101,115,116,105,110,103,32,116,111,111,32,100,101,101,112, +32,102,111,114,32,116,104,101,32,97,110,97,108,121,122,101, +114,0,1598817,1581413,12,8,1310721,2,2,1598905,14,1,1310721,2,7,26, +1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,4,1581333,1310721,2,2, +10,3,6,10,4,8,1,1598965,14,1,1310721,2,3,26,1310721,4, +18,1310721,2,9,28,1310721,4,1310721,2,4,1507841,1310721,2,2,26,1310721, +2,3,26,4,1310721,2,3,26,2,1,26,2,1310721,2,5,26, +4,1310721,2,3,26,2,3,26,2,1310721,2,7,26,4,1310721,2, +7,26,2,1581305,2,10,40,1599115,16,1310721,2,7,26,1581305,2,10, +3,6,10,4,8,1310721,2,3,26,1310721,2,3,26,2,5,26, +10,3,6,10,4,8,1310721,2,5,26,2,1310721,2,7,26,2, +40,1599373,16,1469953,1310721,2,5,26,2,26,1310721,2,3,26,2,1, +26,2,10,3,6,10,4,8,1470977,1310721,2,5,26,2,26,1310721, +2,3,26,2,3,26,2,10,3,6,10,4,8,1468929,1310721,2, +5,26,2,26,1310721,2,3,26,2,5,26,2,10,3,6,10, +4,8,1310721,2,3,26,1310721,2,3,26,2,7,26,10,3,6, +10,4,8,1310721,2,5,26,1,6,2,1,6,3,26,5,6, +4,10,8,8,1599149,14,1,1599381,14,1,1310721,2,9,26,1310721,4, +18,1310721,2,3,28,1310721,4,1310721,2,4,1310721,2,2,1,28,1, +6,1,10,28,34,63,38,3,26,1599453,16,3,1599505,14,1310721,2, +2,9,28,1,6,1,10,28,34,63,38,3,26,1,6,1, +10,28,34,63,38,1,10,28,1599511,14,1,1310721,2,3,26,1310721, +4,18,1310721,2,11,28,1310721,4,1310721,2,4,1507841,1310721,2,2,26, +1310721,2,3,26,4,1310721,2,3,26,2,1,26,2,1310721,2,5, +26,4,1310721,2,3,26,2,3,26,2,1310721,2,7,26,4,1310721, +2,7,26,2,1581305,2,10,40,1599661,16,1310721,2,7,26,1581305,2, +10,3,6,10,4,8,1310721,2,3,26,1310721,2,3,26,2,5, +26,10,3,6,10,4,8,1310721,2,5,26,2,1310721,2,7,26, +2,40,1600285,16,1310721,2,9,26,1469953,1310721,2,5,26,2,26,2, +10,3,6,10,4,8,1310721,2,9,26,2,1599395,12,1599821,16,1310721, +2,3,26,2,1,26,2,1599395,12,1,6,1,10,28,34,63, +38,1,10,28,1599823,14,1,1599903,16,1310721,2,3,26,2,1,26, +2,9,28,1,6,1,10,28,34,63,38,3,26,1599899,16,1469953, +1310721,2,5,26,2,26,9,10,3,6,10,4,8,1600213,14,1310721, +2,9,26,2,1310721,2,3,26,2,1,26,2,28,1,6,1, +10,28,34,63,38,1,10,28,1599963,16,3,1600041,14,1468929,1310721,2, +5,26,2,26,2,1310721,2,3,26,2,5,26,2,28,1,6, +1,10,28,34,63,38,1,10,28,1,6,1,10,28,34,63, +38,1,10,28,1600051,16,3,1600195,14,1310721,2,9,26,2,11,28, +1,6,1,10,28,34,63,38,3,26,1600171,16,1470977,1310721,2,5, +26,2,26,2,1310721,2,3,26,2,3,26,2,28,1,6,1, +10,28,34,63,38,1,10,28,1,6,1,10,28,34,63,38, +1,10,28,1600173,14,1,1,6,1,10,28,34,63,38,1,10, +28,1600213,16,1310721,2,5,26,2,1600299,14,1310721,2,3,26,1310721,2, +3,26,2,7,26,10,3,6,10,4,8,1310721,2,5,26,1, +6,2,1,6,3,26,5,6,4,10,8,8,1599695,14,3,1, +10,28,1600299,14,1,1310721,2,11,26,1310721,4,18,1310721,2,3,28, +1310721,4,1310721,2,4,1310721,2,2,1,28,1,6,1,10,28,34, +63,38,3,26,1600387,16,1600381,14,101,109,112,116,121,0,1600369,1600713, +14,1310721,2,2,3,28,1,6,1,10,28,34,63,38,3,26, +1600435,16,1600429,14,109,0,1600425,1600713,14,1310721,2,2,5,28,1,6, +1,10,28,34,63,38,3,26,1600483,16,1600477,14,117,0,1600473,1600713, +14,1310721,2,2,7,28,1,6,1,10,28,34,63,38,3,26, +1600531,16,1600525,14,100,0,1600521,1600713,14,1310721,2,2,9,28,1,6, +1,10,28,34,63,38,3,26,1600597,16,1600591,14,103,105,118,101, +110,32,97,119,97,121,0,1600569,1600713,14,1310721,2,2,11,28,1, +6,1,10,28,34,63,38,3,26,1600683,16,1600677,14,111,119,110, +101,100,32,98,121,32,97,32,99,111,110,116,97,105,110,101, +114,0,1600635,1600713,14,1600705,14,98,111,114,114,111,119,101,100,0, +1600687,1600713,14,1,1310721,2,3,26,1310721,4,18,1310721,2,7,28,1310721, +4,1310721,2,5,26,4,1310721,2,3,26,4,1310721,2,4,1581313,2, +1,6,1,10,28,34,63,38,3,26,1600799,16,1,1601075,14,1600823, +14,122,99,58,32,108,105,110,101,32,0,1600803,1573031,12,8,1579083, +2,1573171,12,8,1600851,14,58,32,0,1600845,1573031,12,8,1466881,1310721,2, +2,26,2,1573031,12,8,1600887,14,58,32,0,1600881,1573031,12,8,1310721, +2,5,26,2,1573031,12,8,1600959,14,32,40,111,110,101,32,112, +97,116,104,32,108,101,97,118,101,115,32,105,116,32,0,1600915, +1573031,12,8,1310721,2,3,26,2,1600313,12,1573031,12,8,1601013,14,44, +32,97,110,111,116,104,101,114,32,0,1600991,1573031,12,8,1469953,1310721, +2,2,26,2,1600313,12,1573031,12,8,1601053,14,41,10,0,1601047,1573031, +12,8,1572995,12,8,1,1601075,14,1,1310721,2,7,26,1310721,4,18, +1310721,2,9,28,1310721,4,1310721,2,3,26,4,1310721,2,4,1310721,2, +3,26,2,1,6,1,10,28,34,63,38,3,26,1601157,16,1, +1601475,14,1581317,2,1,6,1,10,28,34,63,38,3,26,1601219,16, +1310721,2,2,1598979,12,8,1581317,3,10,3,6,10,4,8,1,1601475, +14,1310721,2,5,26,1310721,2,2,1599525,12,10,3,6,10,4,8, +1310721,2,5,26,2,1,40,3,36,1601467,16,1310721,2,7,26,1507841, +1310721,2,2,26,5,26,7,1310721,2,5,26,2,1507841,1310721,2,2, +26,2,28,30,26,10,3,6,10,4,8,1310721,2,5,26,2, +1310721,2,7,26,2,1,26,2,1601459,14,101,110,100,115,32,116, +104,101,32,98,114,97,110,99,104,101,115,32,111,102,32,97, +110,32,105,102,32,105,110,32,100,105,102,102,101,114,101,110, +116,32,115,116,97,116,101,115,0,1601365,1600727,12,8,1,1601475,14, +1,1310721,2,9,26,1310721,4,18,1310721,2,7,28,1310721,4,1310721,2, +4,1581313,2,1,6,1,10,28,34,63,38,3,26,1601541,16,3, +1601587,14,1581317,2,1,6,1,10,28,34,63,38,3,26,1,6, +1,10,28,34,63,38,1,10,28,1601597,16,1,1601899,14,1310721,2, +3,26,1310721,2,2,1599525,12,10,3,6,10,4,8,1310721,2,3, +26,2,1,40,3,36,1601891,16,1310721,2,5,26,1507841,1310721,2,2, +26,5,26,7,1310721,2,3,26,2,1507841,1310721,2,2,26,2,28, +30,26,10,3,6,10,4,8,1310721,2,3,26,2,1310721,2,5, +26,2,1,26,2,1601883,14,99,104,97,110,103,101,115,32,115, +116,97,116,101,32,105,110,115,105,100,101,32,97,32,108,111, +111,112,59,32,101,97,99,104,32,112,97,115,115,32,104,97, +115,32,116,111,32,108,101,97,118,101,32,105,116,32,97,115, +32,105,116,32,102,111,117,110,100,32,105,116,0,1601743,1600727,12, +8,1,1601899,14,1,1310721,2,7,26,1310721,4,18,1310721,2,9,28, +1310721,4,1310721,2,3,26,4,1310721,2,4,1581313,2,1,6,1,10, +28,34,63,38,3,26,1601979,16,1310721,2,2,1602949,14,1310721,2,2, +5,40,1602095,16,1602077,14,111,110,108,121,32,97,32,110,97,109, +101,100,32,111,119,110,101,100,32,111,98,106,101,99,116,32, +99,97,110,32,98,101,32,114,101,99,97,115,116,0,1601997,1581709, +12,8,1310721,2,2,1602949,14,1310721,2,5,26,1310721,2,2,5,28, +10,3,6,10,4,8,1310721,2,7,26,1469953,1310721,2,5,26,2, +26,2,10,3,6,10,4,8,1472001,1310721,2,5,26,2,26,3, +10,3,6,10,4,8,1310721,2,7,26,2,11,28,1,6,1, +10,28,34,63,38,3,26,1602359,16,1310721,2,5,26,2,1602347,14, +98,101,108,111,110,103,115,32,116,111,32,97,32,99,111,110, +116,97,105,110,101,114,59,32,114,101,99,97,115,116,32,116, +104,101,32,99,111,110,116,97,105,110,101,114,32,105,110,115, +116,101,97,100,0,1602241,1581805,12,8,1602937,14,1310721,2,7,26,2, +13,28,1,6,1,10,28,34,63,38,3,26,1602487,16,1310721,2, +5,26,2,1602475,14,105,115,32,98,111,114,114,111,119,101,100, +32,97,110,100,32,99,97,110,39,116,32,98,101,32,114,101, +99,97,115,116,0,1602411,1581805,12,8,1602937,14,1310721,2,7,26,2, +1,28,1,6,1,10,28,34,63,38,3,26,1602595,16,1310721,2, +5,26,2,1602583,14,105,115,110,39,116,32,97,110,32,111,119, +110,101,100,32,111,98,106,101,99,116,0,1602539,1581805,12,8,1602937, +14,1310721,2,7,26,2,9,28,1,6,1,10,28,34,63,38, +3,26,1602717,16,1310721,2,5,26,2,1602705,14,117,115,101,100,32, +97,102,116,101,114,32,105,116,32,119,97,115,32,103,105,118, +101,110,32,97,119,97,121,0,1602647,1581805,12,8,1602937,14,1310721,2, +3,26,2,3,28,1,6,1,10,28,34,63,38,3,26,1602817, +16,1310721,2,7,26,2,3,28,1,6,1,10,28,34,63,38, +1,10,28,1,6,1,10,28,34,63,38,1,10,28,1602819,14, +1,1602901,16,1310721,2,5,26,2,1602889,14,99,97,110,39,116,32, +98,101,32,114,101,99,97,115,116,32,98,97,99,107,32,116, +111,32,109,0,1602837,1581805,12,8,1602937,14,1469953,1310721,2,5,26,2, +26,1310721,2,3,26,2,10,3,6,10,4,8,1310721,2,2,1602949, +14,1,1310721,2,9,26,1310721,4,18,1310721,2,9,28,1310721,4,1310721, +2,3,26,4,1310721,2,4,1581313,2,1,6,1,10,28,34,63, +38,3,26,1603025,16,1,1604681,14,1310721,2,2,3,28,1,6,1, +10,28,34,63,38,3,26,1603269,16,1310721,2,3,26,2,5,28, +1,6,1,10,28,34,63,38,1,10,28,1603263,16,1603255,14,97, +32,110,101,119,32,111,98,106,101,99,116,32,99,97,110,32, +111,110,108,121,32,103,111,32,115,116,114,97,105,103,104,116, +32,116,111,32,97,32,100,32,112,97,114,97,109,101,116,101, +114,59,32,98,105,110,100,32,105,116,32,116,111,32,97,32, +110,97,109,101,32,102,105,114,115,116,0,1603103,1581709,12,8,1, +1604681,14,1310721,2,2,5,40,1603289,16,3,1603345,14,1310721,2,2,5, +28,1596275,12,1,6,1,10,28,34,63,38,3,26,1,6,1, +10,28,34,63,38,1,10,28,1603355,16,1,1604681,14,1310721,2,5, +26,1310721,2,2,5,28,10,3,6,10,4,8,1310721,2,7,26, +1469953,1310721,2,5,26,2,26,2,10,3,6,10,4,8,1469953,1310721, +2,5,26,2,1596159,12,26,2,9,28,1,6,1,10,28,34, +63,38,3,26,1603555,16,1310721,2,5,26,2,1603541,14,117,115,101, +100,32,97,102,116,101,114,32,105,116,32,119,97,115,32,103, +105,118,101,110,32,97,119,97,121,0,1603483,1581805,12,8,1,1604681, +14,1310721,2,3,26,2,1,28,1,6,1,10,28,34,63,38, +3,26,1603751,16,1310721,2,5,26,2,1603737,14,105,115,32,97,110, +32,111,119,110,101,100,32,111,98,106,101,99,116,59,32,105, +116,32,99,97,110,32,111,110,108,121,32,98,101,32,112,97, +115,115,101,100,32,116,111,32,97,110,32,109,32,111,114,32, +100,32,112,97,114,97,109,101,116,101,114,0,1603607,1581805,12,8, +1,1604681,14,1310721,2,3,26,2,3,28,1,6,1,10,28,34, +63,38,3,26,1603993,16,1310721,2,7,26,2,7,28,1,6,1, +10,28,34,63,38,3,26,1603971,16,1310721,2,5,26,2,1603957,14, +105,115,32,100,59,32,112,97,115,115,32,105,116,32,116,111, +32,97,32,100,32,112,97,114,97,109,101,116,101,114,32,40, +108,101,110,100,105,110,103,32,105,116,32,119,111,117,108,100, +32,108,111,115,101,32,105,116,41,0,1603841,1581805,12,8,1,1604681, +14,1310721,2,5,26,2,1596351,12,8,1,1604681,14,1310721,2,7,26, +2,7,28,1,6,1,10,28,34,63,38,3,26,1604093,16,1469953, +1310721,2,5,26,2,26,9,10,3,6,10,4,8,1472001,1310721,2, +5,26,2,26,3,10,3,6,10,4,8,1,1604681,14,1310721,2, +7,26,2,3,28,1,6,1,10,28,34,63,38,3,26,1604227, +16,1310721,2,5,26,2,1604215,14,105,115,32,109,59,32,119,114, +105,116,101,32,39,97,115,32,100,39,32,116,111,32,103,105, +118,101,32,105,116,32,97,119,97,121,0,1604145,1581805,12,8,1604673, +14,1310721,2,7,26,2,5,28,1,6,1,10,28,34,63,38, +3,26,1604409,16,1310721,2,5,26,2,1604397,14,105,115,32,117,59, +32,119,114,105,116,101,32,39,97,115,32,100,39,32,116,111, +32,103,105,118,101,32,105,116,32,97,119,97,121,32,105,110, +115,116,101,97,100,32,111,102,32,114,101,116,117,114,110,105, +110,103,32,105,116,0,1604279,1581805,12,8,1604673,14,1310721,2,7,26, +2,11,28,1,6,1,10,28,34,63,38,3,26,1604579,16,1310721, +2,5,26,2,1604567,14,98,101,108,111,110,103,115,32,116,111, +32,97,32,99,111,110,116,97,105,110,101,114,32,97,110,100, +32,99,97,110,39,116,32,98,101,32,103,105,118,101,110,32, +97,119,97,121,32,97,108,111,110,101,0,1604461,1581805,12,8,1604673, +14,1310721,2,5,26,2,1604665,14,105,115,32,98,111,114,114,111, +119,101,100,32,97,110,100,32,99,97,110,39,116,32,98,101, +32,103,105,118,101,110,32,97,119,97,121,0,1604593,1581805,12,8, +1,1604681,14,1,1310721,2,9,26,1310721,4,18,1310721,2,3,28,1310721, +4,1310721,2,4,1467905,1310721,2,2,26,2,1584865,12,8,3,1584215,12, +8,1581409,2,1584733,12,8,1,1604757,14,1,1310721,2,3,26,1310721,4, +18,1310721,2,15,28,1310721,4,1310721,2,5,26,4,1310721,2,3,26, +4,1310721,2,4,1581313,2,1,6,1,10,28,34,63,38,3,26, +1604843,16,1,1608095,14,1310721,2,9,26,3,1,10,28,10,3,6, +10,4,8,1310721,2,5,26,2,5,40,3,36,1604937,16,1310721,2, +5,26,2,5,28,1596275,12,1,6,1,10,28,34,63,38,1, +10,28,1604939,14,1,1604977,16,1310721,2,9,26,1310721,2,5,26,2, +5,28,10,3,6,10,4,8,1310721,2,9,26,2,1,40,3, +36,1605069,16,1469953,1310721,2,9,26,2,1596159,12,26,2,9,28,1, +6,1,10,28,34,63,38,3,26,1,6,1,10,28,34,63, +38,1,10,28,1605071,14,1,1605161,16,1310721,2,9,26,2,1605147,14, +117,115,101,100,32,97,102,116,101,114,32,105,116,32,119,97, +115,32,103,105,118,101,110,32,97,119,97,121,0,1605089,1581805,12, +8,1,1608095,14,1310721,2,2,3,28,1,6,1,10,28,34,63, +38,3,26,1606493,16,1310721,2,7,26,1469953,1310721,2,3,26,2,26, +2,10,3,6,10,4,8,1310721,2,9,26,2,1,40,1605311,16, +1310721,2,5,26,2,3,28,1,6,1,10,28,34,63,38,1, +10,28,1,6,1,10,28,34,63,38,1,10,28,1605313,14,1, +1605517,16,1310721,2,7,26,2,1599395,12,1,6,1,10,28,34,63, +38,3,26,1605483,16,1310721,2,3,26,2,1605471,14,115,116,105,108, +108,32,111,119,110,115,32,97,110,32,111,98,106,101,99,116, +59,32,111,118,101,114,119,114,105,116,105,110,103,32,105,116, +32,119,111,117,108,100,32,108,101,97,107,32,105,116,0,1605369, +1581805,12,8,1605511,14,1469953,1310721,2,3,26,2,26,1,10,3,6, +10,4,8,1,1608095,14,1310721,2,7,26,2,1599395,12,1,6,1, +10,28,34,63,38,3,26,1605685,16,1310721,2,3,26,2,1605671,14, +115,116,105,108,108,32,111,119,110,115,32,97,110,32,111,98, +106,101,99,116,59,32,111,118,101,114,119,114,105,116,105,110, +103,32,105,116,32,119,111,117,108,100,32,108,101,97,107,32, +105,116,0,1605569,1581805,12,8,1,1608095,14,1310721,2,5,26,2,3, +28,1,6,1,10,28,34,63,38,3,26,1605813,16,1469953,1310721,2, +3,26,2,26,3,10,3,6,10,4,8,1472001,1310721,2,3,26, +2,26,1,10,3,6,10,4,8,1474049,1310721,2,3,26,2,26, +3,10,3,6,10,4,8,1,1608095,14,1310721,2,13,26,1469953,1310721, +2,9,26,2,26,2,10,3,6,10,4,8,1310721,2,13,26, +2,5,28,1,6,1,10,28,34,63,38,3,26,1605971,16,1468929, +1310721,2,3,26,2,26,2,1468929,1310721,2,9,26,2,26,2,3, +28,28,1,6,1,10,28,34,63,38,3,26,1,6,1,10, +28,34,63,38,1,10,28,1605973,14,1,1606123,16,1469953,1310721,2,3, +26,2,26,3,10,3,6,10,4,8,1472001,1310721,2,3,26,2, +26,1,10,3,6,10,4,8,1474049,1310721,2,3,26,2,26,3, +10,3,6,10,4,8,1469953,1310721,2,9,26,2,26,9,10,3, +6,10,4,8,1472001,1310721,2,9,26,2,26,3,10,3,6,10, +4,8,1,1608095,14,1310721,2,13,26,2,5,28,1,6,1,10, +28,34,63,38,3,26,1606343,16,1310721,2,9,26,2,1606331,14,105, +115,32,117,59,32,105,116,32,99,97,110,32,111,110,108,121, +32,98,101,32,104,97,110,100,101,100,32,116,111,32,97,32, +118,97,114,105,97,98,108,101,32,111,102,32,116,104,101,32, +101,110,99,108,111,115,105,110,103,32,98,108,111,99,107,44, +32,111,114,32,114,101,116,117,114,110,101,100,0,1606175,1581805,12, +8,1606487,14,1310721,2,9,26,2,1606479,14,105,115,32,97,110,32, +111,119,110,101,100,32,111,98,106,101,99,116,32,97,110,100, +32,99,97,110,39,116,32,98,101,32,99,111,112,105,101,100, +32,105,110,116,111,32,97,110,111,116,104,101,114,32,118,97, +114,105,97,98,108,101,0,1606357,1581805,12,8,1,1608095,14,1310721,2, +2,7,28,1,6,1,10,28,34,63,38,3,26,1607763,16,1310721, +2,11,26,1310721,2,3,26,2,1596159,12,10,3,6,10,4,8, +1310721,2,7,26,1469953,1310721,2,11,26,2,26,2,10,3,6,10, +4,8,1310721,2,3,26,2,1596351,12,8,1310721,2,5,26,2,3, +28,1,6,1,10,28,34,63,38,3,26,1606673,16,1310721,2,3, +26,2,1604695,12,8,1,1608095,14,1310721,2,9,26,2,1,40,1606697, +16,1,1608095,14,1310721,2,13,26,1469953,1310721,2,9,26,2,26,2, +10,3,6,10,4,8,1310721,2,13,26,2,11,28,1,6,1, +10,28,34,63,38,3,26,1606859,16,1310721,2,9,26,2,1606847,14, +97,108,114,101,97,100,121,32,98,101,108,111,110,103,115,32, +116,111,32,97,32,99,111,110,116,97,105,110,101,114,0,1606785, +1581805,12,8,1607757,14,1310721,2,13,26,2,13,28,1,6,1,10, +28,34,63,38,3,26,1606987,16,1310721,2,9,26,2,1606975,14,105, +115,32,98,111,114,114,111,119,101,100,32,97,110,100,32,99, +97,110,39,116,32,98,101,32,115,116,111,114,101,100,0,1606911, +1581805,12,8,1607757,14,1310721,2,7,26,2,13,28,1,6,1,10, +28,34,63,38,3,26,1607259,16,1310721,2,13,26,2,7,28,1, +6,1,10,28,34,63,38,1,10,28,1607183,16,1310721,2,9,26, +2,1607171,14,109,117,115,116,32,98,101,32,100,32,116,111,32, +98,101,32,115,116,111,114,101,100,32,105,110,116,111,32,97, +32,98,111,114,114,111,119,101,100,32,111,98,106,101,99,116, +0,1607079,1581805,12,8,1607255,14,1469953,1310721,2,9,26,2,26,9,10, +3,6,10,4,8,1472001,1310721,2,9,26,2,26,3,10,3,6, +10,4,8,1310721,2,3,26,2,1604695,12,8,1607757,14,1310721,2,13, +26,2,1310721,2,7,26,2,28,1,6,1,10,28,34,63,38, +1,10,28,1607465,16,1310721,2,9,26,2,1607453,14,105,116,115,32, +116,97,103,32,100,111,101,115,110,39,116,32,109,97,116,99, +104,32,105,116,115,32,99,111,110,116,97,105,110,101,114,39, +115,32,40,98,111,116,104,32,104,97,118,101,32,116,111,32, +98,101,32,109,44,32,117,32,111,114,32,100,41,0,1607321,1581805, +12,8,1607757,14,1468929,1310721,2,9,26,2,26,2,1468929,1310721,2,11, +26,2,26,2,40,1607649,16,1310721,2,9,26,2,1607637,14,108,105, +118,101,115,32,105,110,32,97,110,32,111,117,116,101,114,32, +98,108,111,99,107,32,116,104,97,110,32,116,104,101,32,99, +111,110,116,97,105,110,101,114,32,105,116,39,115,32,115,116, +111,114,101,100,32,105,110,116,111,0,1607517,1581805,12,8,1607757,14, +1469953,1310721,2,9,26,2,26,11,10,3,6,10,4,8,1470977,1310721, +2,9,26,2,26,1310721,2,3,26,2,10,3,6,10,4,8, +1472001,1310721,2,9,26,2,26,3,10,3,6,10,4,8,1310721,2, +3,26,2,1604695,12,8,1,1608095,14,1310721,2,9,26,2,1,40, +3,36,1608087,16,1469953,1310721,2,9,26,2,26,2,7,28,1,6, +1,10,28,34,63,38,3,26,1607889,16,1469953,1310721,2,9,26,2, +26,9,10,3,6,10,4,8,1472001,1310721,2,9,26,2,26,3, +10,3,6,10,4,8,1608087,14,1310721,2,9,26,2,1608079,14,109, +117,115,116,32,98,101,32,100,32,116,111,32,98,101,32,115, +116,111,114,101,100,32,105,110,32,97,32,103,108,111,98,97, +108,32,111,114,32,105,110,32,117,110,116,114,97,99,107,101, +100,32,109,101,109,111,114,121,32,40,116,104,101,32,99,111, +108,108,101,99,116,111,114,32,116,97,107,101,115,32,105,116, +32,111,118,101,114,41,0,1607903,1581805,12,8,1,1608095,14,1,1310721, +2,15,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,4,1581313, +2,1,6,1,10,28,34,63,38,3,26,1608161,16,1,1609057,14, +1310721,2,3,26,3,1,10,28,10,3,6,10,4,8,1310721,2, +2,5,40,3,36,1608247,16,1310721,2,2,5,28,1596275,12,1,6, +1,10,28,34,63,38,1,10,28,1608249,14,1,1608283,16,1310721,2, +3,26,1310721,2,2,5,28,10,3,6,10,4,8,1310721,2,3, +26,2,1,40,3,36,1608375,16,1469953,1310721,2,3,26,2,1596159,12, +26,2,9,28,1,6,1,10,28,34,63,38,3,26,1,6, +1,10,28,34,63,38,1,10,28,1608377,14,1,1608467,16,1310721,2, +3,26,2,1608453,14,117,115,101,100,32,97,102,116,101,114,32, +105,116,32,119,97,115,32,103,105,118,101,110,32,97,119,97, +121,0,1608395,1581805,12,8,1,1609057,14,1581315,2,1608729,16,1310721,2,3, +26,2,1,40,3,36,1608723,16,1469953,1310721,2,3,26,2,26,2, +5,28,1,6,1,10,28,34,63,38,3,26,1608601,16,1469953,1310721, +2,3,26,2,26,9,10,3,6,10,4,8,1472001,1310721,2,3, +26,2,26,3,10,3,6,10,4,8,1608723,14,1310721,2,3,26, +2,1608715,14,104,97,115,32,116,111,32,98,101,32,117,32,116, +111,32,98,101,32,114,101,116,117,114,110,101,100,59,32,119, +114,105,116,101,32,39,114,101,116,117,114,110,32,120,32,97, +115,32,117,39,0,1608615,1581805,12,8,1,1609057,14,1310721,2,2,3, +28,1,6,1,10,28,34,63,38,3,26,1608885,16,1608873,14,114, +101,116,117,114,110,115,32,97,32,110,101,119,32,111,98,106, +101,99,116,59,32,100,101,99,108,97,114,101,32,116,104,101, +32,102,117,110,99,116,105,111,110,32,119,105,116,104,32,45, +62,32,117,0,1608767,1581709,12,8,1609049,14,1310721,2,3,26,2,1, +40,3,36,1609049,16,1310721,2,3,26,2,1609041,14,105,115,32,111, +119,110,101,100,32,97,110,100,32,99,97,110,39,116,32,98, +101,32,114,101,116,117,114,110,101,100,32,102,114,111,109,32, +97,32,102,117,110,99,116,105,111,110,32,119,105,116,104,111, +117,116,32,45,62,32,117,0,1608921,1581805,12,8,1,1609057,14,1, +1310721,2,5,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,4, +1581313,2,1,6,1,10,28,34,63,38,3,26,1609123,16,1,1609751, +14,1310721,2,2,3,28,1,6,1,10,28,34,63,38,3,26, +1609163,16,3,1609751,14,1310721,2,2,5,40,1609183,16,3,1609239,14,1310721, +2,2,5,28,1596275,12,1,6,1,10,28,34,63,38,3,26, +1,6,1,10,28,34,63,38,1,10,28,1609249,16,1,1609751,14, +1310721,2,3,26,1310721,2,2,5,28,10,3,6,10,4,8,1469953, +1310721,2,3,26,2,26,2,5,28,1,6,1,10,28,34,63, +38,3,26,1609391,16,1468929,1310721,2,3,26,2,26,2,1581309,2,28, +1,6,1,10,28,34,63,38,3,26,1,6,1,10,28,34, +63,38,1,10,28,1609393,14,1,1609459,16,1469953,1310721,2,3,26,2, +26,9,10,3,6,10,4,8,1472001,1310721,2,3,26,2,26,3, +10,3,6,10,4,8,3,1609751,14,1469953,1310721,2,3,26,2,26, +2,5,28,1,6,1,10,28,34,63,38,3,26,1609623,16,1310721, +2,3,26,2,1609611,14,111,110,108,121,32,97,32,117,32,111, +98,106,101,99,116,32,111,102,32,116,104,105,115,32,98,108, +111,99,107,32,99,97,110,32,98,101,32,105,116,115,32,118, +97,108,117,101,0,1609517,1581805,12,8,1609743,14,1310721,2,3,26,2, +1609735,14,104,97,115,32,116,111,32,98,101,32,117,32,116,111, +32,98,101,32,104,97,110,100,101,100,32,117,112,32,97,115, +32,116,104,101,32,98,108,111,99,107,39,115,32,118,97,108, +117,101,0,1609637,1581805,12,8,1,1609751,14,1,1310721,2,5,26,1310721, +4,18,1310721,2,3,28,1310721,4,1310721,2,4,1310721,2,2,3,28, +1,6,1,10,28,34,63,38,3,26,1609933,16,1609921,14,97,32, +110,101,119,32,111,98,106,101,99,116,32,40,111,114,32,97, +32,117,32,114,101,115,117,108,116,41,32,99,97,110,39,116, +32,98,101,32,116,104,114,111,119,110,32,97,119,97,121,0, +1609821,1581709,12,8,1609967,14,1310721,2,2,5,40,3,36,1609967,16,1310721, +2,2,5,28,1596351,12,8,1,1609975,14,1,1310721,2,3,26,1310721, +4,18,1310721,2,1,28,1310721,4,1581335,2,1610033,16,3,1584215,12,8, +1581335,1,10,3,6,10,4,8,1,1610041,14,1,1310721,2,1,26, +1310721,4,18,1310721,2,11,28,1310721,4,1310721,2,3,26,4,1310721,2, +4,1425921,1310721,2,2,26,2,1310721,2,5,26,4,1581341,2,1310721,2, +7,26,4,1581341,1310721,2,3,26,2,10,3,6,10,4,8,1581343, +1,10,3,6,10,4,8,1310721,2,5,26,2,1,6,12,10, +8,8,1581341,1310721,2,7,26,2,10,3,6,10,4,8,1310721,2, +9,26,1581343,2,10,3,6,10,4,8,1581335,1,10,3,6,10, +4,8,1581337,1,10,3,6,10,4,8,1310721,2,9,26,2,1610277, +14,1,1310721,2,11,26,1310721,4,18,1310721,2,1,28,1310721,4,1581341, +2,1610403,16,1610395,14,116,104,105,115,32,99,97,110,39,116,32, +98,101,32,117,115,101,100,32,105,110,115,105,100,101,32,97, +110,32,101,120,112,114,101,115,115,105,111,110,0,1610315,1581413,12, +8,1,1610411,14,1,1310721,2,1,26,1310721,4,18,1310721,2,11,28, +1310721,4,1310721,2,4,1,1310721,2,3,26,4,1,1310721,2,5,26, +4,1,1310721,2,7,26,4,1442305,1310721,2,2,26,2,5,32,1610575, +16,1310721,2,5,26,1434113,1310721,2,2,26,2,13,38,10,3,6, +10,4,8,1310721,2,7,26,1434113,1310721,2,2,26,2,3,32,10, +3,6,10,4,8,1610613,14,1442305,1310721,2,2,26,1442305,1310721,2,2, +26,2,9,34,10,3,6,10,4,8,1581345,2,1591367,12,8,1581347, +2,1590899,12,1,6,1,10,28,34,63,38,3,26,1610819,16,3, +1610819,16,1310721,2,9,26,1616659,12,10,3,6,10,4,8,1609989,12, +8,1310721,2,9,26,2,1310721,2,5,26,2,1310721,2,3,26,2, +1310721,2,3,26,2,26,38,7,32,1602963,12,8,1310721,2,3,26, +1,6,2,1,6,3,26,5,6,4,10,8,8,1581357,2,1591159, +12,1,6,1,10,28,34,63,38,3,26,1610815,16,1610819,14,1610655, +14,1581347,2,1591367,12,8,1442305,1310721,2,2,26,2,5,32,1610929,16, +1310721,2,3,26,2,1434113,1310721,2,2,26,2,3,38,63,32,28, +1,6,1,10,28,34,63,38,1,10,28,1,6,1,10,28, +34,63,38,1,10,28,1610931,14,1,1611011,16,1409537,1310721,2,2,26, +2,1611003,14,119,114,111,110,103,32,110,117,109,98,101,114,32, +111,102,32,97,114,103,117,109,101,110,116,115,0,1610951,1581539,12, +8,1310721,2,2,1584595,12,8,13,1584215,12,8,1581335,1,10,3,6, +10,4,8,1581337,1,10,3,6,10,4,8,1310721,2,7,26,2, +1611083,16,3,1611091,14,1,1611091,14,1,1310721,2,11,26,1310721,4,18, +1310721,2,9,28,1310721,4,1581335,1,10,3,6,10,4,8,1581337,1, +10,3,6,10,4,8,1579077,2,3,28,1,6,1,10,28,34, +63,38,3,26,1611203,16,1579079,2,1584151,12,8,1590445,12,8,1,1612793, +14,1579077,2,7,28,1,6,1,10,28,34,63,38,3,26,1611257, +16,1579079,2,1584951,12,8,1590445,12,8,1,1612793,14,1579077,2,9,28, +1,6,1,10,28,34,63,38,3,26,1611477,16,1579079,2,1581345,2, +28,1,6,1,10,28,34,63,38,3,26,1611377,16,1590445,12,8, +1310721,2,7,26,1616659,12,10,3,6,10,4,8,1581347,2,1591367,12, +8,1310721,2,7,26,2,1612793,14,1579079,2,1581349,2,28,1,6,1, +10,28,34,63,38,3,26,1611419,16,1618013,12,1612793,14,1611469,14,101, +120,112,101,99,116,101,100,32,97,110,32,101,120,112,114,101, +115,115,105,111,110,0,1611423,1581413,12,8,1579077,2,5,28,1,6, +1,10,28,34,63,38,1,10,28,1611569,16,1611561,14,101,120,112, +101,99,116,101,100,32,97,110,32,101,120,112,114,101,115,115, +105,111,110,0,1611515,1581413,12,8,1310721,2,1579079,2,10,3,6,10, +4,8,1310721,2,3,26,1310721,2,2,1595595,12,10,3,6,10,4, +8,1310721,2,3,26,2,1,40,3,36,1612011,16,1590445,12,8,1581313, +2,1,6,1,10,28,34,63,38,3,26,1611715,16,1310721,2,3, +26,2,1596275,12,1,6,1,10,28,34,63,38,1,10,28,1611717, +14,1,1611865,16,1310721,2,2,1611857,14,105,115,32,116,114,97,99, +107,101,100,32,98,121,32,116,104,101,32,97,110,97,108,121, +122,101,114,32,97,110,100,32,99,97,110,39,116,32,98,101, +32,117,115,101,100,32,105,110,32,117,110,99,104,101,99,107, +101,100,32,99,111,100,101,0,1611731,1581539,12,8,1467905,1310721,2,3, +26,2,26,2,1584865,12,8,1473025,1310721,2,3,26,2,26,2,1611913, +16,1,1612793,14,1581335,3,10,3,6,10,4,8,1581337,3,10,3, +6,10,4,8,1581339,1310721,2,3,26,2,10,3,6,10,4,8, +1310721,2,3,26,2,1596275,12,1612005,16,5,1310721,2,3,26,2,26, +1612793,14,1,1612793,14,1310721,2,5,26,1310721,2,2,1583029,12,10,3, +6,10,4,8,1310721,2,5,26,2,1,40,3,36,1612129,16,1417729, +1310721,2,5,26,2,26,2,11,28,1,6,1,10,28,34,63, +38,3,26,1,6,1,10,28,34,63,38,1,10,28,1612131,14, +1,1612287,16,1442305,1310721,2,5,26,2,26,2,33,32,1,6,1, +10,28,34,63,38,3,26,1612267,16,1310721,2,2,1612259,14,99,97, +110,39,116,32,98,101,32,117,115,101,100,32,105,110,115,105, +100,101,32,97,110,32,101,120,112,114,101,115,115,105,111,110, +0,1612189,1581539,12,8,1310721,2,5,26,2,3,1610055,12,1612793,14,1590445, +12,8,1310721,2,5,26,2,1,40,3,36,1612381,16,1417729,1310721,2, +5,26,2,26,2,9,28,1,6,1,10,28,34,63,38,3, +26,1,6,1,10,28,34,63,38,1,10,28,1612383,14,1,1612415, +16,1425921,1310721,2,5,26,2,26,2,1584151,12,8,1,1612793,14,1310721, +2,5,26,2,1,40,3,36,1612503,16,1417729,1310721,2,5,26,2, +26,2,5,28,1,6,1,10,28,34,63,38,3,26,1,6, +1,10,28,34,63,38,1,10,28,1612505,14,1,1612569,16,1425921,1310721, +2,5,26,2,26,2,1584151,12,8,1581335,3,10,3,6,10,4, +8,1581337,5,10,3,6,10,4,8,1,1612793,14,1310721,2,5,26, +2,1,40,3,36,1612657,16,1417729,1310721,2,5,26,2,26,2,7, +28,1,6,1,10,28,34,63,38,3,26,1,6,1,10,28, +34,63,38,1,10,28,1612659,14,1,1612691,16,1425921,1310721,2,5,26, +2,26,2,1584151,12,8,1,1612793,14,1310721,2,5,26,2,1,40, +1612739,16,1310721,2,5,26,1310721,2,2,1584495,12,10,3,6,10,4, +8,1581345,2,1590899,12,1612769,16,1310721,2,5,26,2,1610425,12,1612793,14, +1310721,2,5,26,2,1584595,12,8,1,1612793,14,1,1310721,2,9,26, +1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,3,26,4,1310721,2, +4,1581335,2,1,6,1,10,28,34,63,38,3,26,1612951,16,1612943, +14,43,43,32,97,110,100,32,45,45,32,110,101,101,100,32, +115,111,109,101,116,104,105,110,103,32,116,111,32,97,115,115, +105,103,110,32,116,111,0,1612867,1581413,12,8,1581337,2,3,28,1, +6,1,10,28,34,63,38,3,26,1613013,16,1581313,2,1,6,1, +10,28,34,63,38,1,10,28,1613015,14,1,1613053,16,1581339,2,1596275, +12,1,6,1,10,28,34,63,38,1,10,28,1613055,14,1,1613155, +16,1581339,2,1613147,14,111,119,110,115,32,97,110,32,111,98,106, +101,99,116,32,97,110,100,32,99,97,110,39,116,32,98,101, +32,105,110,99,114,101,109,101,110,116,101,100,0,1613067,1581805,12, +8,1581335,1,10,3,6,10,4,8,1581337,1,10,3,6,10,4, +8,1,1584151,12,8,7,1584215,12,8,3,1584215,12,8,1310721,2,2, +1613289,16,1,1584151,12,8,7,1584215,12,8,1310721,2,3,26,2,1584151, +12,8,27,1584215,12,8,5,1584151,12,8,7,1584215,12,8,5,1584215, +12,8,1613353,14,1310721,2,3,26,2,1584151,12,8,27,1584215,12,8, +1,1584151,12,8,7,1584215,12,8,5,1584151,12,8,7,1584215,12,8, +5,1584215,12,8,11,1584215,12,8,9,1584215,12,8,1,1613377,14,1, +1310721,2,5,26,1310721,4,18,1310721,2,9,28,1310721,4,1611105,12,1310721, +2,4,3,1614945,16,1579077,2,5,28,1,6,1,10,28,34,63, +38,3,26,1613507,16,1579079,2,1581391,2,28,1,6,1,10,28,34, +63,38,3,26,1,6,1,10,28,34,63,38,1,10,28,1613509, +14,1,1613935,16,1590445,12,8,1579077,2,5,28,1,6,1,10,28, +34,63,38,1,10,28,1613621,16,1613613,14,101,120,112,101,99,116, +101,100,32,117,44,32,100,32,111,114,32,109,32,97,102,116, +101,114,32,97,115,0,1613557,1581413,12,8,1310721,2,7,26,3,10, +3,6,10,4,8,1579079,2,1581401,2,28,1,6,1,10,28,34, +63,38,3,26,1613703,16,1310721,2,7,26,5,10,3,6,10,4, +8,1613867,14,1579079,2,1581399,2,28,1,6,1,10,28,34,63,38, +3,26,1613763,16,1310721,2,7,26,7,10,3,6,10,4,8,1613867, +14,1579079,2,1581397,2,28,1,6,1,10,28,34,63,38,1,10, +28,1613867,16,1613859,14,101,120,112,101,99,116,101,100,32,117,44, +32,100,32,111,114,32,109,32,97,102,116,101,114,32,97,115, +0,1613803,1581413,12,8,1590445,12,8,1609989,12,8,1310721,2,1310721,2,2, +1310721,2,7,26,2,1601913,12,10,3,6,10,4,8,1581337,1,10, +3,6,10,4,8,1613413,14,1579077,2,9,28,1,6,1,10,28, +34,63,38,1,10,28,1613979,16,1310721,2,2,1614957,14,1579079,2,1581353, +2,28,1,6,1,10,28,34,63,38,3,26,1614445,16,1590445,12, +8,1609989,12,8,1310721,2,3,26,3,1,10,28,10,3,6,10, +4,8,1310721,2,2,3,28,1,6,1,10,28,34,63,38,3, +26,1614205,16,1614193,14,105,110,100,101,120,32,97,32,110,101,119, +32,111,98,106,101,99,116,32,111,110,108,121,32,97,102,116, +101,114,32,98,105,110,100,105,110,103,32,105,116,32,116,111, +32,97,32,110,97,109,101,0,1614091,1581709,12,8,1614269,14,1310721,2, +2,5,40,3,36,1614269,16,1310721,2,2,5,28,1596351,12,8,1310721, +2,3,26,1310721,2,2,5,28,10,3,6,10,4,8,1310721,2, +1616659,12,10,3,6,10,4,8,1609989,12,8,1310721,2,2,1596817,12, +8,1581355,2,1591367,12,8,27,1584215,12,8,1581335,3,10,3,6,10, +4,8,1310721,2,3,26,2,1,40,3,36,1614407,16,1581337,7,10, +3,6,10,4,8,1581339,1310721,2,3,26,2,10,3,6,10,4, +8,1614423,14,1581337,9,10,3,6,10,4,8,1310721,2,1,10,3, +6,10,4,8,1613413,14,1579079,2,1581345,2,28,1,6,1,10,28, +34,63,38,3,26,1614789,16,1609989,12,8,1310721,2,2,1596817,12,8, +1590445,12,8,1310721,2,5,26,1,10,3,6,10,4,8,1581347,2, +1590899,12,1,6,1,10,28,34,63,38,3,26,1614677,16,3,1614677, +16,1310721,2,1616659,12,10,3,6,10,4,8,1609989,12,8,1310721,2, +2,1,1602963,12,8,1310721,2,5,26,1,6,2,1,6,3,26, +5,6,4,10,8,8,1581357,2,1591159,12,1,6,1,10,28,34, +63,38,3,26,1614673,16,1614677,14,1614557,14,1581347,2,1591367,12,8,1310721, +2,5,26,2,1584151,12,8,7,1584215,12,8,13,1584215,12,8,11, +1584215,12,8,9,1584215,12,8,1581335,1,10,3,6,10,4,8,1581337, +1,10,3,6,10,4,8,1310721,2,1,10,3,6,10,4,8, +1613413,14,1579079,2,1581365,2,28,1,6,1,10,28,34,63,38,3, +26,1614857,16,1590445,12,8,1310721,2,3,3,1612807,12,10,3,6,10, +4,8,1613413,14,1579079,2,1581367,2,28,1,6,1,10,28,34,63, +38,3,26,1614931,16,1590445,12,8,1310721,2,3,3,1,10,28,1612807, +12,10,3,6,10,4,8,1613413,14,1310721,2,2,1614957,14,1613413,14, +1310721,2,2,1614957,14,1,1310721,2,9,26,1310721,4,18,1310721,2,5, +28,1310721,4,1579077,2,9,28,1,6,1,10,28,34,63,38,3, +26,1615655,16,1579079,2,1581369,2,28,1,6,1,10,28,34,63,38, +3,26,1615131,16,1590445,12,8,1310721,2,1614971,12,10,3,6,10,4, +8,1609989,12,8,1310721,2,2,1596817,12,8,1581335,3,10,3,6,10, +4,8,1581337,9,10,3,6,10,4,8,1,1615665,14,1579079,2,1581371, +2,28,1,6,1,10,28,34,63,38,3,26,1615363,16,1590445,12, +8,1310721,2,1614971,12,10,3,6,10,4,8,1581335,2,1,6,1, +10,28,34,63,38,3,26,1615291,16,1615283,14,38,32,110,101,101, +100,115,32,115,111,109,101,116,104,105,110,103,32,97,100,100, +114,101,115,115,97,98,108,101,0,1615223,1581413,12,8,1310721,2,2, +5,40,3,36,1615325,16,1310721,2,2,5,28,1596351,12,8,1581335,1, +10,3,6,10,4,8,1581337,1,10,3,6,10,4,8,1,1615665, +14,1579079,2,1581365,2,28,1,6,1,10,28,34,63,38,3,26, +1615421,16,1590445,12,8,1614971,12,8,1,3,1612807,12,1615665,14,1579079,2, +1581367,2,28,1,6,1,10,28,34,63,38,3,26,1615485,16,1590445, +12,8,1614971,12,8,1,3,1,10,28,1612807,12,1615665,14,1310721,2, +3,26,1579079,2,1593773,12,10,3,6,10,4,8,1310721,2,3,26, +2,1,40,3,36,1615655,16,1590445,12,8,1310721,2,1614971,12,10,3, +6,10,4,8,1609989,12,8,1310721,2,2,1596817,12,8,1547329,1310721,2, +3,26,2,26,2,1547393,1310721,2,3,26,2,26,2,1592975,12,8, +1581335,1,10,3,6,10,4,8,1581337,1,10,3,6,10,4,8, +1,1615665,14,1613391,12,1615665,14,1,1310721,2,5,26,1310721,4,18,1310721, +2,13,28,1310721,4,1310721,2,4,1614971,12,1310721,2,3,26,4,1579077, +2,9,28,1,6,1,10,28,34,63,38,3,26,1616555,16,1310721, +2,5,26,1579079,2,1593599,12,10,3,6,10,4,8,1310721,2,5, +26,2,1,40,1615803,16,1310721,2,3,26,2,1616571,14,1310721,2,7, +26,1546881,1310721,2,5,26,2,26,2,10,3,6,10,4,8,1310721, +2,7,26,2,1310721,2,2,40,1615875,16,1310721,2,3,26,2,1616571, +14,1590445,12,8,1609989,12,8,1310721,2,3,26,2,1596817,12,8,1547137, +1310721,2,5,26,2,26,2,3,1,10,28,28,1,6,1,10, +28,34,63,38,3,26,1616151,16,1310721,2,9,26,1584435,12,10,3, +6,10,4,8,17,1584215,12,8,1310721,2,3,26,1310721,2,7,26, +2,3,26,1615679,12,10,3,6,10,4,8,1609989,12,8,1310721,2, +3,26,2,1596817,12,8,1547329,3,26,2,1547393,3,26,2,1592975,12, +8,1310721,2,11,26,1584435,12,10,3,6,10,4,8,15,1584215,12, +8,1310721,2,9,26,2,1581297,2,1584275,12,8,1,1584151,12,8,1310721, +2,11,26,2,1581297,2,1584275,12,8,1616497,14,1547137,1310721,2,5,26, +2,26,2,5,1,10,28,28,1,6,1,10,28,34,63,38, +3,26,1616399,16,1310721,2,9,26,1584435,12,10,3,6,10,4,8, +17,1584215,12,8,3,1584151,12,8,1310721,2,11,26,1584435,12,10,3, +6,10,4,8,15,1584215,12,8,1310721,2,9,26,2,1581297,2,1584275, +12,8,1310721,2,3,26,1310721,2,7,26,2,3,26,1615679,12,10, +3,6,10,4,8,1609989,12,8,1310721,2,3,26,2,1596817,12,8, +1547329,3,26,2,1547393,3,26,2,1592975,12,8,1310721,2,11,26,2, +1581297,2,1584275,12,8,1616497,14,1310721,2,3,26,1310721,2,7,26,2, +3,26,1615679,12,10,3,6,10,4,8,1609989,12,8,1310721,2,3, +26,2,1596817,12,8,1547009,1310721,2,5,26,2,26,2,1547137,1310721,2, +5,26,2,26,2,1592975,12,8,1581335,1,10,3,6,10,4,8, +1581337,1,10,3,6,10,4,8,1310721,2,3,26,1,10,3,6, +10,4,8,1615711,14,1310721,2,3,26,2,1616571,14,1,1310721,2,13, +26,1310721,4,18,1310721,2,1,28,1310721,4,11,1584215,12,8,3,1584151, +12,8,7,1584215,12,8,11,1584215,12,8,5,1584215,12,8,1,1616645, +14,1,1310721,2,1,26,1310721,4,18,1310721,2,11,28,1310721,4,3, +1615679,12,1310721,2,4,1579077,2,9,28,1,6,1,10,28,34,63, +38,1,10,28,1616727,16,1310721,2,2,1617577,14,1579079,2,1581361,2,28, +1,6,1,10,28,34,63,38,3,26,1617013,16,1581335,2,1,6, +1,10,28,34,63,38,3,26,1616853,16,1616845,14,116,104,105,115, +32,99,97,110,39,116,32,98,101,32,97,115,115,105,103,110, +101,100,32,116,111,0,1616793,1581413,12,8,1310721,2,3,26,1581337,2, +10,3,6,10,4,8,1310721,2,5,26,1581339,2,10,3,6,10, +4,8,1590445,12,8,1310721,2,9,26,1616659,12,10,3,6,10,4, +8,1609989,12,8,1616585,12,8,1581335,1,10,3,6,10,4,8,1581337, +1,10,3,6,10,4,8,1310721,2,3,26,2,1310721,2,5,26, +2,1310721,2,9,26,2,1604771,12,1617577,14,1310721,2,7,26,1579079,2, +1593947,12,10,3,6,10,4,8,1310721,2,7,26,2,1,40,3, +36,1617565,16,1581335,2,1,6,1,10,28,34,63,38,3,26,1617155, +16,1617147,14,116,104,105,115,32,99,97,110,39,116,32,98,101, +32,97,115,115,105,103,110,101,100,32,116,111,0,1617095,1581413,12, +8,1581337,2,3,28,1,6,1,10,28,34,63,38,3,26,1617217, +16,1581313,2,1,6,1,10,28,34,63,38,1,10,28,1617219,14, +1,1617257,16,1581339,2,1596275,12,1,6,1,10,28,34,63,38,1, +10,28,1617259,14,1,1617379,16,1581339,2,1617371,14,111,119,110,115,32, +97,110,32,111,98,106,101,99,116,59,32,99,111,109,112,111, +117,110,100,32,97,115,115,105,103,110,109,101,110,116,32,119, +111,117,108,100,32,108,111,115,101,32,105,116,0,1617271,1581805,12, +8,1590445,12,8,1,1584151,12,8,7,1584215,12,8,3,1584215,12,8, +1581335,1,10,3,6,10,4,8,1310721,2,9,26,1616659,12,10,3, +6,10,4,8,1609989,12,8,1310721,2,9,26,2,1596817,12,8,1547009, +1547585,1310721,2,7,26,2,26,2,26,2,1547137,1547585,1310721,2,7,26, +2,26,2,26,2,1592975,12,8,1616585,12,8,1581335,1,10,3,6, +10,4,8,1581337,1,10,3,6,10,4,8,1,1617577,14,1310721,2, +2,1617577,14,1,1310721,2,11,26,1310721,4,18,1310721,2,3,28,1310721, +4,1579077,2,9,28,1,6,1,10,28,34,63,38,3,26,1617731, +16,1579079,2,1581359,2,28,1,6,1,10,28,34,63,38,3,26, +1617675,16,3,1617727,14,1579079,2,1581349,2,28,1,6,1,10,28,34, +63,38,3,26,1,6,1,10,28,34,63,38,1,10,28,1617999, +14,1579077,2,5,28,1,6,1,10,28,34,63,38,1,10,28, +1617771,16,3,1617809,14,1579079,2,1595595,12,1,40,3,36,1,6,1, +10,28,34,63,38,1,10,28,1617819,16,1,1617999,14,1310721,2,1579079, +2,1583029,12,10,3,6,10,4,8,1310721,2,2,1,40,3,36, +1617923,16,1417729,1310721,2,2,26,2,11,28,1,6,1,10,28,34, +63,38,3,26,1,6,1,10,28,34,63,38,1,10,28,1617925, +14,1,1617991,16,1442305,1310721,2,2,26,2,33,32,1,6,1,10, +28,34,63,38,3,26,1,6,1,10,28,34,63,38,1,10, +28,1617993,14,1,1617999,14,1,1310721,2,3,26,1310721,4,18,1310721,2, +7,28,1310721,4,1581305,2,1310721,2,4,1,1310721,2,3,26,4,1, +1310721,2,5,26,4,1590445,12,8,1581309,1,6,2,1,6,3,26, +5,6,4,10,8,8,1581319,1,6,2,1,6,3,26,5,6, +4,10,8,8,1581351,2,1590899,12,1,6,1,10,28,34,63,38, +3,26,1618469,16,1579077,2,1,28,1,6,1,10,28,34,63,38, +3,26,1618217,16,1618209,14,109,105,115,115,105,110,103,32,125,0, +1618189,1581413,12,8,1617591,12,1618235,16,1620153,12,8,1618121,14,1310721,2,3, +26,1616659,12,10,3,6,10,4,8,1609989,12,8,1581359,2,1591159,12, +1618327,16,9,1584215,12,8,1310721,2,3,26,2,1609765,12,8,1310721,2, +3,26,1,10,3,6,10,4,8,1618121,14,1581351,2,1590899,12,1, +6,1,10,28,34,63,38,3,26,1618443,16,1618435,14,101,120,112, +101,99,116,101,100,32,59,32,111,114,32,125,32,97,102,116, +101,114,32,97,110,32,101,120,112,114,101,115,115,105,111,110, +0,1618363,1581413,12,8,1310721,2,5,26,3,10,3,6,10,4,8, +1618121,14,1590445,12,8,1310721,2,5,26,2,1618527,16,1310721,2,3,26, +1310721,2,3,26,2,1609071,12,10,3,6,10,4,8,1618535,14,1, +1584151,12,8,1310721,2,2,1598141,12,8,1581309,1,6,2,1,6,-1, +26,5,6,4,10,8,8,1581319,1,6,2,1,6,-1,26,5, +6,4,10,8,8,1581335,1,10,3,6,10,4,8,1581337,1,10, +3,6,10,4,8,1310721,2,3,26,2,1618651,14,1,1310721,2,7, +26,1310721,4,18,1310721,2,5,28,1310721,4,1579077,2,3,28,1,6, +1,10,28,34,63,38,3,26,1618745,16,1310721,2,1579079,2,10,3, +6,10,4,8,1590445,12,8,1310721,2,2,1619445,14,1581377,2,1591159,12, +1618769,16,1,1618665,12,28,1619445,14,1579077,2,9,28,1,6,1,10, +28,34,63,38,3,26,1618861,16,1579079,2,1547265,5,26,2,28,1, +6,1,10,28,34,63,38,3,26,1,6,1,10,28,34,63, +38,1,10,28,1618863,14,1,1618885,16,1590445,12,8,1618665,12,-1,36, +1619445,14,1581345,2,1591159,12,1618937,16,1310721,2,1619459,12,10,3,6,10, +4,8,1581347,2,1591367,12,8,1310721,2,2,1619445,14,1579077,2,5,28, +1,6,1,10,28,34,63,38,3,26,1619385,16,1310721,2,3,26, +1579079,2,1583029,12,10,3,6,10,4,8,1310721,2,3,26,2,1, +40,3,36,1619353,16,1417729,1310721,2,3,26,2,26,2,9,28,1, +6,1,10,28,34,63,38,3,26,1619069,16,3,1619131,14,1417729,1310721, +2,3,26,2,26,2,7,28,1,6,1,10,28,34,63,38, +3,26,1,6,1,10,28,34,63,38,1,10,28,1619141,16,3, +1619255,14,1417729,1310721,2,3,26,2,26,2,3,28,1,6,1,10, +28,34,63,38,3,26,1619231,16,1442305,1310721,2,3,26,2,26,2, +3,32,1,6,1,10,28,34,63,38,1,10,28,1619233,14,1, +1,6,1,10,28,34,63,38,1,10,28,1619265,16,3,1619327,14, +1417729,1310721,2,3,26,2,26,2,5,28,1,6,1,10,28,34, +63,38,3,26,1,6,1,10,28,34,63,38,1,10,28,1, +6,1,10,28,34,63,38,1,10,28,1619355,14,1,1619385,16,1590445, +12,8,1425921,1310721,2,3,26,2,26,2,1619445,14,1619429,14,101,120, +112,101,99,116,101,100,32,97,32,99,111,110,115,116,97,110, +116,0,1619389,1581413,12,8,1,1619445,14,1,1310721,2,5,26,1310721,4, +18,1310721,2,5,28,1310721,4,1618665,12,1310721,2,4,1579077,2,9,28, +1,6,1,10,28,34,63,38,3,26,1620127,16,1310721,2,3,26, +1579079,2,10,3,6,10,4,8,1310721,2,3,26,2,1546753,7,26, +2,28,1,6,1,10,28,34,63,38,3,26,1619619,16,1590445,12, +8,1310721,2,1310721,2,2,1618665,12,26,10,3,6,10,4,8,1620123, +14,1310721,2,3,26,2,1546753,9,26,2,28,1,6,1,10,28, +34,63,38,3,26,1619701,16,1590445,12,8,1310721,2,1310721,2,2,1618665, +12,28,10,3,6,10,4,8,1620123,14,1310721,2,3,26,2,1546753, +1,26,2,28,1,6,1,10,28,34,63,38,3,26,1619783,16, +1590445,12,8,1310721,2,1310721,2,2,1618665,12,30,10,3,6,10,4, +8,1620123,14,1310721,2,3,26,2,1546753,31,26,2,28,1,6,1, +10,28,34,63,38,3,26,1619865,16,1590445,12,8,1310721,2,1310721,2, +2,1618665,12,34,10,3,6,10,4,8,1620123,14,1310721,2,3,26, +2,1546753,27,26,2,28,1,6,1,10,28,34,63,38,3,26, +1619947,16,1590445,12,8,1310721,2,1310721,2,2,1618665,12,32,10,3,6, +10,4,8,1620123,14,1310721,2,3,26,2,1546753,11,26,2,28,1, +6,1,10,28,34,63,38,3,26,1620031,16,1590445,12,8,1310721,2, +1310721,2,2,1618665,12,1575289,12,10,3,6,10,4,8,1620123,14,1310721, +2,3,26,2,1546753,13,26,2,28,1,6,1,10,28,34,63, +38,3,26,1620113,16,1590445,12,8,1310721,2,1310721,2,2,1618665,12,38, +10,3,6,10,4,8,1620123,14,1310721,2,2,1620139,14,1619481,14,1310721, +2,2,1620139,14,1,1310721,2,5,26,1310721,4,18,1310721,2,5,28, +1310721,4,1579077,2,5,28,1,6,1,10,28,34,63,38,3,26, +1620235,16,1579079,2,1595595,12,1,40,1,6,1,10,28,34,63,38, +1,10,28,1620237,14,1,1620419,16,1310721,2,1579079,2,1583029,12,10,3, +6,10,4,8,1310721,2,2,1,40,3,36,1620345,16,1417729,1310721,2, +2,26,2,11,28,1,6,1,10,28,34,63,38,3,26,1, +6,1,10,28,34,63,38,1,10,28,1620347,14,1,1620393,16,1442305, +1310721,2,2,26,2,17,32,1,6,1,10,28,34,63,38,1, +10,28,1620395,14,1,1620419,16,1310721,2,2,1,1610055,12,8,1,1620615, +14,1579077,2,9,28,1,6,1,10,28,34,63,38,3,26,1620543, +16,1579079,2,1581359,2,28,1,6,1,10,28,34,63,38,3,26, +1620497,16,1590445,12,8,1,1620615,14,1579079,2,1581349,2,28,1,6,1, +10,28,34,63,38,3,26,1620543,16,1620629,12,8,1,1620615,14,1310721, +2,3,26,1616659,12,10,3,6,10,4,8,1609989,12,8,9,1584215, +12,8,1581359,2,1591367,12,8,1310721,2,3,26,2,1609765,12,8,1, +1620615,14,1,1310721,2,5,26,1310721,4,18,1310721,2,3,28,1310721,4, +1581305,2,1310721,2,4,1581349,2,1591367,12,8,1581309,1,6,2,1,6, +3,26,5,6,4,10,8,8,1581351,2,1590899,12,1,6,1,10, +28,34,63,38,3,26,1620795,16,1579077,2,1,28,1,6,1,10, +28,34,63,38,3,26,1620785,16,1620777,14,109,105,115,115,105,110, +103,32,125,0,1620757,1581413,12,8,1620153,12,8,1620689,14,1590445,12,8, +1310721,2,2,1598141,12,8,1581309,1,6,2,1,6,-1,26,5,6, +4,10,8,8,1,1620849,14,1,1310721,2,3,26,1310721,4,18,1310721, +2,3,28,1310721,4,1581305,2,1310721,2,4,1581349,2,1590899,12,1620905,16, +1620629,12,1620987,14,1581309,1,6,2,1,6,3,26,5,6,4,10, +8,8,1620153,12,8,1310721,2,2,1598141,12,8,1581309,1,6,2,1, +6,-1,26,5,6,4,10,8,8,1,1620987,14,1,1310721,2,3, +26,1310721,4,18,1310721,2,9,28,1310721,4,1610291,12,8,1590445,12,8, +3,1621357,16,1310721,2,1591545,12,10,3,6,10,4,8,1310721,2,3, +26,1310721,2,2,1595777,12,10,3,6,10,4,8,1581353,2,1591159,12, +1621189,16,1310721,2,7,26,1619459,12,10,3,6,10,4,8,1581355,2, +1591367,12,8,1473025,1310721,2,3,26,2,26,3,10,3,6,10,4, +8,1581311,1581311,2,1310721,2,7,26,2,26,10,3,6,10,4,8, +1621217,14,1581311,1,6,2,1,6,3,26,5,6,4,10,8,8, +1581361,2,1591159,12,1621317,16,1310721,2,5,26,1616659,12,10,3,6,10, +4,8,1609989,12,8,1467905,1310721,2,3,26,2,26,2,1584865,12,8, +5,1584215,12,8,3,1310721,2,3,26,2,1310721,2,5,26,2,1604771, +12,8,1581357,2,1591159,12,1,6,1,10,28,34,63,38,3,26, +1621353,16,1621357,14,1621025,14,1581359,2,1591367,12,8,1,1621375,14,1,1310721, +2,9,26,1310721,4,18,1310721,2,15,28,1310721,4,1610291,12,8,1590445, +12,8,1581345,2,1591367,12,8,1310721,2,5,26,1616659,12,10,3,6, +10,4,8,1609989,12,8,1310721,2,5,26,2,1596817,12,8,1581347,2, +1591367,12,8,1310721,2,1584435,12,10,3,6,10,4,8,17,1584215,12, +8,1310721,2,11,26,1581317,2,10,3,6,10,4,8,1310721,2,7, +26,1598391,12,10,3,6,10,4,8,1620863,12,8,1310721,2,13,26, +1581317,2,10,3,6,10,4,8,1310721,2,9,26,1598391,12,10,3, +6,10,4,8,1581389,2,1591029,12,1621745,16,1590445,12,8,1310721,2,3, +26,1584435,12,10,3,6,10,4,8,15,1584215,12,8,1310721,2,2, +1581297,2,1584275,12,8,1310721,2,7,26,2,1598979,12,8,1581317,1310721,2, +11,26,2,10,3,6,10,4,8,1620863,12,8,1310721,2,3,26, +2,1581297,2,1584275,12,8,1621801,14,1310721,2,2,1581297,2,1584275,12,8, +1310721,2,7,26,2,1598979,12,8,1581317,1310721,2,11,26,2,10,3, +6,10,4,8,1310721,2,9,26,2,1310721,2,13,26,2,1601089,12, +8,1310721,2,7,26,2,1598919,12,8,1,1621851,14,1,1310721,2,15, +26,1310721,4,18,1310721,2,7,28,1310721,4,1310721,2,3,26,4,1310721, +2,4,1552897,1581327,2,13,30,26,1310721,2,5,26,4,1581327,2,121, +40,3,36,1621991,16,1621983,14,108,111,111,112,115,32,110,101,115, +116,101,100,32,116,111,111,32,100,101,101,112,108,121,0,1621935, +1581413,12,8,1310721,2,5,26,2,1,26,1310721,2,2,10,3,6, +10,4,8,1310721,2,5,26,2,3,26,1,10,3,6,10,4, +8,1310721,2,5,26,2,5,26,1581309,2,10,3,6,10,4,8, +1310721,2,5,26,2,7,26,1310721,2,3,26,2,10,3,6,10, +4,8,1310721,2,5,26,2,9,26,1581319,2,10,3,6,10,4, +8,1581327,1,6,2,1,6,3,26,5,6,4,10,8,8,1, +1622183,14,1,1310721,2,7,26,1310721,4,18,1310721,2,1,28,1310721,4, +1581327,1,6,2,1,6,-1,26,5,6,4,10,8,8,1552897,1581327, +2,13,30,3,26,26,2,1581297,2,1583797,12,8,1,1622273,14,1, +1310721,2,1,26,1310721,4,18,1310721,2,9,28,1310721,4,1581297,2,1310721, +2,4,1610291,12,8,1590445,12,8,1310721,2,5,26,1598391,12,10,3, +6,10,4,8,1581345,2,1591367,12,8,1310721,2,7,26,1616659,12,10, +3,6,10,4,8,1609989,12,8,1310721,2,7,26,2,1596817,12,8, +1581347,2,1591367,12,8,1310721,2,3,26,1584435,12,10,3,6,10,4, +8,17,1584215,12,8,1310721,2,2,1310721,2,5,26,2,1621865,12,8, +1620863,12,8,1310721,2,5,26,2,1601489,12,8,1310721,2,2,1584375,12, +8,1310721,2,3,26,2,1581297,2,1584275,12,8,1622197,12,8,1310721,2, +5,26,2,1598979,12,8,1581317,3,10,3,6,10,4,8,1310721,2, +5,26,2,1598919,12,8,1,1622581,14,1,1310721,2,9,26,1310721,4, +18,1310721,2,15,28,1310721,4,1581305,2,1310721,2,4,1610291,12,8,1590445, +12,8,1581345,2,1591367,12,8,1581309,1,6,2,1,6,3,26,5, +6,4,10,8,8,1581385,2,1591029,12,1622689,16,1621001,12,8,1622785,14, +1581359,2,1590899,12,1,6,1,10,28,34,63,38,3,26,1622775,16, +1310721,2,13,26,1616659,12,10,3,6,10,4,8,1609989,12,8,9, +1584215,12,8,1310721,2,13,26,2,1609765,12,8,1581359,2,1591367,12,8, +1310721,2,3,26,1598391,12,10,3,6,10,4,8,1310721,2,5,26, +1581297,2,10,3,6,10,4,8,1581359,2,1590899,12,1,6,1,10, +28,34,63,38,3,26,1622915,16,1310721,2,13,26,1616659,12,10,3, +6,10,4,8,1609989,12,8,1310721,2,13,26,2,1596817,12,8,1622923, +14,3,1584151,12,8,1581359,2,1591367,12,8,1310721,2,7,26,1584435,12, +10,3,6,10,4,8,17,1584215,12,8,1310721,2,9,26,1584435,12, +10,3,6,10,4,8,15,1584215,12,8,1310721,2,11,26,1581297,2, +10,3,6,10,4,8,1581347,2,1590899,12,1,6,1,10,28,34, +63,38,3,26,1623107,16,1310721,2,13,26,1616659,12,10,3,6,10, +4,8,1609989,12,8,9,1584215,12,8,1310721,2,13,26,2,1609765,12, +8,1581347,2,1591367,12,8,1310721,2,5,26,2,1584375,12,8,1310721,2, +9,26,2,1581297,2,1584275,12,8,1310721,2,11,26,2,1310721,2,3, +26,2,1621865,12,8,1620863,12,8,1310721,2,3,26,2,1601489,12,8, +1310721,2,11,26,2,1584375,12,8,1310721,2,7,26,2,1581297,2,1584275, +12,8,1622197,12,8,1310721,2,3,26,2,1598979,12,8,1581317,3,10, +3,6,10,4,8,1310721,2,3,26,2,1598919,12,8,1310721,2,2, +1598141,12,8,1581309,1,6,2,1,6,-1,26,5,6,4,10,8, +8,1,1623339,14,1,1310721,2,15,26,1310721,4,18,1310721,2,3,28, +1310721,4,1310721,2,4,1581327,2,1581329,2,10,40,3,36,1623469,16,1623461, +14,98,114,101,97,107,32,111,114,32,99,111,110,116,105,110, +117,101,32,111,117,116,115,105,100,101,32,97,32,108,111,111, +112,0,1623395,1581413,12,8,1310721,2,2,9,26,2,1581319,2,28,1, +6,1,10,28,34,63,38,1,10,28,1623625,16,1623617,14,98,114, +101,97,107,32,97,110,100,32,99,111,110,116,105,110,117,101, +32,99,97,110,39,116,32,108,101,97,118,101,32,97,32,98, +108,111,99,107,32,101,120,112,114,101,115,115,105,111,110,0, +1623517,1581413,12,8,1310721,2,2,7,26,2,1601489,12,8,1310721,2,2, +5,26,2,3,26,1,1597159,12,8,1,1623675,14,1,1310721,2,3, +26,1310721,4,18,1310721,2,3,28,1310721,4,1552897,1581327,2,3,28,13, +30,26,1310721,2,4,1610291,12,8,1590445,12,8,1581359,2,1591367,12,8, +1310721,2,2,1623353,12,8,1310721,2,2,3,26,1581297,2,1310721,2,2, +3,26,2,1583467,12,10,3,6,10,4,8,2097153,3,28,1584151,12, +8,15,1584215,12,8,1581317,1,10,3,6,10,4,8,1,1623843,14, +1,1310721,2,3,26,1310721,4,18,1310721,2,3,28,1310721,4,1552897,1581327, +2,3,28,13,30,26,1310721,2,4,1610291,12,8,1590445,12,8,1581359, +2,1591367,12,8,1310721,2,2,1623353,12,8,1310721,2,2,1,26,2, +1584375,12,8,1581317,1,10,3,6,10,4,8,1,1623967,14,1,1310721, +2,3,26,1310721,4,18,1310721,2,3,28,1310721,4,1610291,12,8,1590445, +12,8,1581325,2,1,6,1,10,28,34,63,38,3,26,1624097,16, +1624089,14,114,101,116,117,114,110,32,111,117,116,115,105,100,101, +32,97,32,102,117,110,99,116,105,111,110,0,1624037,1581413,12,8, +1581319,2,1624193,16,1624185,14,114,101,116,117,114,110,32,99,97,110, +39,116,32,108,101,97,118,101,32,97,32,98,108,111,99,107, +32,101,120,112,114,101,115,115,105,111,110,0,1624109,1581413,12,8, +1581359,2,1590899,12,1624235,16,1,1584151,12,8,1310721,2,1,10,3,6, +10,4,8,1624261,14,1310721,2,1616659,12,10,3,6,10,4,8,1609989, +12,8,1581359,2,1591367,12,8,1310721,2,2,1608109,12,8,3,1,1597159, +12,8,1581321,1581297,2,1581321,2,1583467,12,10,3,6,10,4,8,2097153, +3,28,1584151,12,8,15,1584215,12,8,1581317,1,10,3,6,10,4, +8,1,1624363,14,1,1310721,2,3,26,1310721,4,18,1310721,2,3,28, +1310721,4,1581313,2,1310721,2,4,1610291,12,8,1590445,12,8,1581313,1,10, +3,6,10,4,8,1620863,12,8,1581313,1310721,2,2,10,3,6,10, +4,8,1,1624461,14,1,1310721,2,3,26,1310721,4,18,1310721,2,21, +28,1310721,4,1,1310721,2,4,1,1310721,2,3,26,4,1554945,1310721,2, +13,26,4,1554945,129,26,1310721,2,15,26,4,1554945,257,26,1310721,2, +17,26,4,1554945,769,26,1310721,2,19,26,4,1581349,2,1591367,12,8, +1581351,2,1590899,12,1,6,1,10,28,34,63,38,3,26,1626417,16, +1579077,2,1,28,1,6,1,10,28,34,63,38,3,26,1624699,16, +1624691,14,117,110,116,101,114,109,105,110,97,116,101,100,32,97, +115,109,32,98,108,111,99,107,0,1624645,1581413,12,8,1579077,2,3, +28,1,6,1,10,28,34,63,38,3,26,1624751,16,1579079,2,1584151, +12,8,1590445,12,8,1624577,14,1579077,2,7,28,1,6,1,10,28, +34,63,38,3,26,1624803,16,1579079,2,1584951,12,8,1590445,12,8,1624577, +14,1581375,2,1590899,12,1624921,16,1590445,12,8,1579077,2,3,28,1,6, +1,10,28,34,63,38,1,10,28,1624901,16,1624893,14,35,32,110, +101,101,100,115,32,97,32,110,117,109,98,101,114,0,1624859,1581413, +12,8,1579079,2,1583993,12,8,1590445,12,8,1624577,14,1581377,2,1590899,12, +1625043,16,1590445,12,8,1579077,2,3,28,1,6,1,10,28,34,63, +38,3,26,1625001,16,1579081,2,1,6,1,10,28,34,63,38,1, +10,28,1625003,14,1,1625031,16,1,1579079,2,28,1584151,12,8,1590445,12, +8,1625039,14,29,1584215,12,8,1624577,14,1581373,2,1590899,12,1625233,16,1590445, +12,8,1310721,2,2,129,40,3,36,1625131,16,1625123,14,116,111,111, +32,109,97,110,121,32,97,115,109,32,108,97,98,101,108,115, +0,1625083,1581413,12,8,1310721,2,13,26,2,1310721,2,2,26,1591545,12, +10,3,6,10,4,8,1310721,2,15,26,2,1310721,2,2,26,1581297, +2,10,3,6,10,4,8,1310721,2,1,6,2,1,6,3,26, +5,6,4,10,8,8,1624577,14,1579079,2,1,26,2,1625303,16,1579079, +2,3,26,2,1,6,1,10,28,34,63,38,3,26,1,6, +1,10,28,34,63,38,1,10,28,1625305,14,1,1625375,16,1310721,2, +9,26,1579079,2,1,26,2,1592793,12,10,3,6,10,4,1,40, +3,36,1,6,1,10,28,34,63,38,1,10,28,1625377,14,1, +1625407,16,1310721,2,9,26,2,1584215,12,8,1590445,12,8,1624577,14,1579077, +2,5,28,1,6,1,10,28,34,63,38,1,10,28,1625487,16, +1625479,14,98,97,100,32,116,111,107,101,110,32,105,110,32,97, +115,109,0,1625445,1581413,12,8,1310721,2,5,26,1579079,2,1595595,12,10, +3,6,10,4,8,1310721,2,5,26,2,1,40,3,36,1625597,16, +1472001,1310721,2,5,26,2,26,3,10,3,6,10,4,8,1467905,1310721, +2,5,26,2,26,2,1584865,12,8,1590445,12,8,1624577,14,1310721,2, +7,26,3,1,10,28,10,3,6,10,4,8,1310721,2,5,26, +1,10,3,6,10,4,8,1310721,2,5,26,2,1310721,2,2,40, +1625791,16,1310721,2,13,26,2,1310721,2,5,26,2,26,2,1579079,2, +28,1,6,1,10,28,34,63,38,3,26,1625753,16,1310721,2,7, +26,1310721,2,5,26,2,10,3,6,10,4,8,1310721,2,5,26, +1,6,2,1,6,3,26,5,6,4,10,8,8,1625647,14,1310721, +2,7,26,2,1,40,3,36,1625853,16,1310721,2,15,26,2,1310721, +2,7,26,2,26,2,1584151,12,8,1590445,12,8,1624577,14,1310721,2, +11,26,1579079,2,1583029,12,10,3,6,10,4,8,1310721,2,11,26, +2,1,40,3,36,1625971,16,1417729,1310721,2,11,26,2,26,2,3, +28,1,6,1,10,28,34,63,38,1,10,28,1,6,1,10, +28,34,63,38,1,10,28,1625973,14,1,1626045,16,1417729,1310721,2,11, +26,2,26,2,11,28,1,6,1,10,28,34,63,38,1,10, +28,1,6,1,10,28,34,63,38,1,10,28,1626047,14,1,1626083, +16,1425921,1310721,2,11,26,2,26,2,1584151,12,8,1590445,12,8,1624577, +14,1310721,2,11,26,2,1,40,3,36,1626171,16,1417729,1310721,2,11, +26,2,26,2,3,28,1,6,1,10,28,34,63,38,3,26, +1,6,1,10,28,34,63,38,1,10,28,1626173,14,1,1626203,16, +1310721,2,11,26,2,1584595,12,8,1590445,12,8,1624577,14,1310721,2,3, +26,2,501,40,3,36,1626297,16,1626289,14,116,111,111,32,109,97, +110,121,32,97,115,109,32,108,97,98,101,108,32,114,101,102, +101,114,101,110,99,101,115,0,1626229,1581413,12,8,1310721,2,17,26, +2,1310721,2,3,26,2,26,1579079,2,10,3,6,10,4,8,1310721, +2,19,26,2,1310721,2,3,26,2,26,1584435,12,10,3,6,10, +4,8,1310721,2,3,26,1,6,2,1,6,3,26,5,6,4, +10,8,8,1590445,12,8,1624577,14,1590445,12,8,1310721,2,5,26,1, +10,3,6,10,4,8,1310721,2,5,26,2,1310721,2,3,26,2, +40,1626871,16,1310721,2,7,26,3,1,10,28,10,3,6,10,4, +8,1310721,2,9,26,1,10,3,6,10,4,8,1310721,2,9,26, +2,1310721,2,2,40,1626685,16,1310721,2,13,26,2,1310721,2,9,26, +2,26,2,1310721,2,17,26,2,1310721,2,5,26,2,26,2,28, +1,6,1,10,28,34,63,38,3,26,1626647,16,1310721,2,7,26, +1310721,2,9,26,2,10,3,6,10,4,8,1310721,2,9,26,1, +6,2,1,6,3,26,5,6,4,10,8,8,1626521,14,1310721,2, +7,26,2,1,40,1626779,16,1310721,2,17,26,2,1310721,2,5,26, +2,26,2,1626771,14,117,110,107,110,111,119,110,32,110,97,109, +101,32,105,110,32,97,115,109,0,1626731,1581539,12,8,1310721,2,19, +26,2,1310721,2,5,26,2,26,2,1310721,2,15,26,2,1310721,2, +7,26,2,26,2,1584275,12,8,1310721,2,5,26,1,6,2,1, +6,3,26,5,6,4,10,8,8,1626445,14,1,1626879,14,1,1310721, +2,21,26,1310721,4,18,1310721,2,1,28,1310721,4,1590445,12,8,1624475, +12,8,1581343,1,10,3,6,10,4,8,1,1626941,14,1,1310721,2, +1,26,1310721,4,18,1310721,2,7,28,1310721,4,1,1310721,2,4,1, +1310721,2,3,26,4,1581349,2,1590899,12,1,6,1,10,28,34,63, +38,3,26,1627053,16,1627045,14,101,120,112,101,99,116,101,100,32, +123,0,1627023,1581413,12,8,1590445,12,8,3,1627449,16,1579077,2,1,28, +1,6,1,10,28,34,63,38,3,26,1627147,16,1627139,14,117,110, +116,101,114,109,105,110,97,116,101,100,32,113,117,111,116,101, +0,1627101,1581413,12,8,1581349,2,1590899,12,1627193,16,1310721,2,3,26,1, +6,2,1,6,3,26,5,6,4,10,8,8,1581351,2,1590899,12, +1627277,16,1310721,2,3,26,2,1,6,1,10,28,34,63,38,3, +26,1627243,16,1627449,14,1310721,2,3,26,1,6,2,1,6,-1,26, +5,6,4,10,8,8,1310721,2,2,8001,40,3,36,1627337,16,1627329, +14,113,117,111,116,101,32,116,111,111,32,108,111,110,103,0, +1627299,1581413,12,8,1524225,1310721,2,2,1310721,2,2,26,26,1579077,2,10, +3,6,10,4,8,1524225,1310721,2,2,1310721,2,2,26,3,26,26, +1579079,2,10,3,6,10,4,8,1310721,2,1,6,2,1,6,3, +26,5,6,4,10,8,8,1590445,12,8,1627059,14,1590445,12,8,1310721, +2,5,26,1310721,2,2,1310721,2,2,26,3,26,1575967,12,10,3, +6,10,4,8,1310721,2,5,26,2,1,26,1310721,2,2,10,3, +6,10,4,8,1310721,2,5,26,2,3,26,1524225,1310721,2,2,1310721, +2,2,26,1573943,12,8,1310721,2,5,26,2,1627581,14,1,1310721,2, +7,26,1310721,4,18,1310721,2,7,28,1310721,4,1310721,2,4,1310721,2, +2,1,26,2,5,30,3,26,1310721,2,3,26,4,1310721,2,3, +26,2,1575967,12,1310721,2,5,26,4,1310721,2,5,26,2,1310721,2, +2,1310721,2,3,26,2,1573943,12,8,1310721,2,5,26,2,1627715,14, +1,1310721,2,7,26,1310721,4,18,1310721,2,7,28,1310721,4,1310721,2, +4,1310721,2,2,1,26,2,5,30,3,26,1310721,2,3,26,4, +1310721,2,3,26,2,1575967,12,1310721,2,5,26,4,1310721,2,5,26, +2,1310721,2,2,1310721,2,3,26,2,1573943,12,8,1310721,2,5,26, +2,1627849,14,1,1310721,2,7,26,1310721,4,18,1310721,2,7,28,1310721, +4,1310721,2,3,26,4,1310721,2,4,1,1310721,2,5,26,4,1310721, +2,5,26,2,1310721,2,3,26,2,40,1627993,16,1310721,2,2,1310721, +2,5,26,2,26,2,1583993,12,8,1310721,2,5,26,1,6,2, +1,6,3,26,5,6,4,10,8,8,1627903,14,1,1628001,14,1, +1310721,2,7,26,1310721,4,18,1310721,2,9,28,1310721,4,1590445,12,8, +1310721,2,1626955,12,10,3,6,10,4,8,1310721,2,3,26,1584435,12, +10,3,6,10,4,8,15,1584215,12,8,1310721,2,5,26,1581297,2, +10,3,6,10,4,8,1310721,2,7,26,1310721,2,2,1,26,2, +5,30,3,26,10,3,6,10,4,8,1310721,2,2,1310721,2,7, +26,2,1627863,12,8,1310721,2,2,1577415,12,8,1310721,2,3,26,2, +1581297,2,1584275,12,8,1310721,2,5,26,2,1584151,12,8,1581411,2,1584733, +12,8,1581343,3,10,3,6,10,4,8,1,1628253,14,1,1310721,2, +9,26,1310721,4,18,1310721,2,7,28,1310721,4,1310721,2,4,1,1310721, +2,3,26,4,5,1310721,2,2,1,1,1585519,12,8,1590445,12,8, +1579077,2,1,28,1,6,1,10,28,34,63,38,1,10,28,1628553, +16,1310721,2,3,26,2,8001,40,3,36,1628421,16,1628413,14,116,111, +111,32,109,97,110,121,32,116,111,107,101,110,115,0,1628381,1581413, +12,8,1524225,1310721,2,3,26,2,1310721,2,3,26,2,26,26,1579077, +2,10,3,6,10,4,8,1524225,1310721,2,3,26,2,1310721,2,3, +26,2,26,3,26,26,1579079,2,10,3,6,10,4,8,1310721,2, +3,26,1,6,2,1,6,3,26,5,6,4,10,8,8,1590445, +12,8,1628321,14,1586333,12,8,1310721,2,5,26,1310721,2,3,26,2, +1310721,2,3,26,2,26,3,26,1575967,12,10,3,6,10,4,8, +1310721,2,5,26,2,1,26,1310721,2,3,26,2,10,3,6,10, +4,8,1310721,2,5,26,2,3,26,1524225,1310721,2,3,26,2,1310721, +2,3,26,2,26,1573943,12,8,1310721,2,5,26,2,1628705,14,1, +1310721,2,7,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,4, +3,1310721,2,2,3,3,1585519,12,8,1590445,12,8,1,1628769,14,1, +1310721,2,3,26,1310721,4,18,1310721,2,3,28,1310721,4,1548801,1581331,2, +49,30,26,1310721,2,4,1581331,2,61,40,3,36,1628895,16,1628887,14, +99,111,109,112,105,108,101,115,32,110,101,115,116,101,100,32, +116,111,111,32,100,101,101,112,108,121,0,1628833,1581413,12,8,1581331, +1,6,2,1,6,3,26,5,6,4,10,8,8,1310721,2,2, +1,26,1581305,2,10,3,6,10,4,8,1310721,2,2,3,26,1581307, +2,10,3,6,10,4,8,1310721,2,2,5,26,1581309,2,10,3, +6,10,4,8,1310721,2,2,7,26,1581311,2,10,3,6,10,4, +8,1310721,2,2,9,26,1581313,2,10,3,6,10,4,8,1310721,2, +2,11,26,1581315,2,10,3,6,10,4,8,1310721,2,2,13,26, +1581317,2,10,3,6,10,4,8,1310721,2,2,15,26,1581319,2,10, +3,6,10,4,8,1310721,2,2,17,26,1581321,2,10,3,6,10, +4,8,1310721,2,2,19,26,1581323,2,10,3,6,10,4,8,1310721, +2,2,21,26,1581325,2,10,3,6,10,4,8,1310721,2,2,23, +26,1581327,2,10,3,6,10,4,8,1310721,2,2,25,26,1581329,2, +10,3,6,10,4,8,1310721,2,2,27,26,1581335,2,10,3,6, +10,4,8,1310721,2,2,29,26,1581337,2,10,3,6,10,4,8, +1310721,2,2,31,26,1581339,2,10,3,6,10,4,8,1310721,2,2, +33,26,1581341,2,10,3,6,10,4,8,1310721,2,2,35,26,1581343, +2,10,3,6,10,4,8,1310721,2,2,37,26,1581333,2,10,3, +6,10,4,8,1,1629425,14,1,1310721,2,3,26,1310721,4,18,1310721, +2,3,28,1310721,4,1581331,1,6,2,1,6,-1,26,5,6,4, +10,8,8,1310721,2,1548801,1581331,2,49,30,26,10,3,6,10,4, +8,1581305,1310721,2,2,1,26,2,10,3,6,10,4,8,1581307,1310721, +2,2,3,26,2,10,3,6,10,4,8,1581309,1310721,2,2,5, +26,2,10,3,6,10,4,8,1581311,1310721,2,2,7,26,2,10, +3,6,10,4,8,1581313,1310721,2,2,9,26,2,10,3,6,10, +4,8,1581315,1310721,2,2,11,26,2,10,3,6,10,4,8,1581317, +1310721,2,2,13,26,2,10,3,6,10,4,8,1581319,1310721,2,2, +15,26,2,10,3,6,10,4,8,1581321,1310721,2,2,17,26,2, +10,3,6,10,4,8,1581323,1310721,2,2,19,26,2,10,3,6, +10,4,8,1581325,1310721,2,2,21,26,2,10,3,6,10,4,8, +1581327,1310721,2,2,23,26,2,10,3,6,10,4,8,1581329,1310721,2, +2,25,26,2,10,3,6,10,4,8,1581335,1310721,2,2,27,26, +2,10,3,6,10,4,8,1581337,1310721,2,2,29,26,2,10,3, +6,10,4,8,1581339,1310721,2,2,31,26,2,10,3,6,10,4, +8,1581341,1310721,2,2,33,26,2,10,3,6,10,4,8,1581343,1310721, +2,2,35,26,2,10,3,6,10,4,8,1581333,1310721,2,2,37, +26,2,10,3,6,10,4,8,1,1630009,14,1,1310721,2,3,26, +1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,3,26,4,1310721,2, +4,1581307,1581305,2,10,3,6,10,4,8,1581309,3,10,3,6,10, +4,8,1581311,1,10,3,6,10,4,8,1581313,1310721,2,2,1630143,16, +1579075,2,1,6,1,10,28,34,63,38,1,10,28,1630145,14,1, +10,3,6,10,4,8,1581315,1310721,2,3,26,2,10,3,6,10, +4,8,1581317,3,10,3,6,10,4,8,1581319,1,10,3,6,10, +4,8,1581321,1,10,3,6,10,4,8,1581325,3,10,3,6,10, +4,8,1581329,1581327,2,10,3,6,10,4,8,1581335,1,10,3,6, +10,4,8,1581337,1,10,3,6,10,4,8,1,1630303,14,1,1310721, +2,5,26,1310721,4,18,1310721,2,1,28,1310721,4,1310721,1584151,12,8, +3,1584215,12,8,1581323,1584435,12,10,3,6,10,4,8,29,1584215,12, +8,1310721,1584151,12,8,5,1584215,12,8,1,1630395,14,1,1310721,2,1, +26,1310721,4,18,1310721,2,1,28,1310721,4,1,1584151,12,8,1581321,2, +1581297,2,1583797,12,8,1310721,1584151,12,8,3,1584215,12,8,1581311,2,1584151, +12,8,27,1584215,12,8,1310721,1584151,12,8,5,1584215,12,8,19,1584215, +12,8,1581323,2,1581311,2,1584275,12,8,1581325,1,10,3,6,10,4, +8,1581305,1581307,2,10,3,6,10,4,8,1,1630557,14,1,1310721,2, +1,26,1310721,4,18,1310721,2,1,28,1310721,4,1581349,2,1591367,12,8, +1581351,2,1590899,12,1,6,1,10,28,34,63,38,3,26,1630749,16, +1579077,2,1,28,1,6,1,10,28,34,63,38,3,26,1630739,16, +1630731,14,109,105,115,115,105,110,103,32,125,32,97,116,32,116, +104,101,32,101,110,100,32,111,102,32,97,32,102,117,110,99, +116,105,111,110,0,1630661,1581413,12,8,1620153,12,8,1630593,14,1590445,12, +8,1581307,2,1597865,12,8,1581313,2,1630803,16,1581317,2,1,6,1,10, +28,34,63,38,1,10,28,1630805,14,1,1630921,16,3,3,1597159,12, +8,1581315,2,1630921,16,1630913,14,97,32,45,62,32,117,32,102,117, +110,99,116,105,111,110,32,104,97,115,32,116,111,32,101,110, +100,32,119,105,116,104,32,97,32,114,101,116,117,114,110,0, +1630831,1581709,12,8,1,1630929,14,1,1310721,2,1,26,1310721,4,18,1310721, +2,115,28,1310721,4,1310721,2,4,1,1310721,2,7,26,4,1,1310721, +2,9,26,4,1,1310721,2,13,26,4,1590445,12,8,1310721,2,3, +26,1591545,12,10,3,6,10,4,8,1310721,2,5,26,1310721,2,3, +26,2,1583029,12,10,3,6,10,4,8,1310721,2,5,26,2,1, +40,1631085,16,3,1631123,14,1310721,2,5,26,2,1579011,2,40,1,6, +1,10,28,34,63,38,1,10,28,1631133,16,3,1631197,14,1417729,1310721, +2,5,26,2,26,2,3,28,1,6,1,10,28,34,63,38, +1,10,28,1,6,1,10,28,34,63,38,1,10,28,1631207,16, +3,1631249,14,1442305,1310721,2,5,26,2,26,2,3,32,1,6,1, +10,28,34,63,38,1,10,28,1631319,16,1310721,2,5,26,1310721,2, +3,26,2,3,1,1582581,12,10,3,6,10,4,8,1442305,1310721,2, +5,26,2,26,1,10,3,6,10,4,8,1581345,2,1591367,12,8, +1581347,2,1590899,12,1,6,1,10,28,34,63,38,3,26,1632237,16, +3,1632237,16,1310721,2,11,26,1,10,3,6,10,4,8,1579077,2, +5,28,1,6,1,10,28,34,63,38,3,26,1631539,16,1579079,2, +1581397,2,28,1,6,1,10,28,34,63,38,3,26,1631461,16,3, +1631513,14,1579079,2,1581399,2,28,1,6,1,10,28,34,63,38,3, +26,1,6,1,10,28,34,63,38,1,10,28,1,6,1,10, +28,34,63,38,1,10,28,1631541,14,1,1631763,16,1310721,2,11,26, +3,10,3,6,10,4,8,1579079,2,1581399,2,28,1,6,1,10, +28,34,63,38,3,26,1631623,16,1310721,2,11,26,5,10,3,6, +10,4,8,1310721,2,19,26,1310721,2,7,26,2,26,1579079,2,10, +3,6,10,4,8,1590445,12,8,1579077,2,5,28,1,6,1,10, +28,34,63,38,3,26,1631737,16,1310721,2,19,26,1310721,2,7,26, +2,26,1591545,12,10,3,6,10,4,8,1631759,14,1310721,2,11,26, +1,10,3,6,10,4,8,1631799,14,1310721,2,19,26,1310721,2,7, +26,2,26,1591545,12,10,3,6,10,4,8,1310721,2,7,26,2, +25,40,3,36,1631857,16,1310721,2,11,26,2,1,6,1,10,28, +34,63,38,1,10,28,1631859,14,1,1631961,16,1631953,14,111,110,108, +121,32,116,104,101,32,102,105,114,115,116,32,49,50,32,112, +97,114,97,109,101,116,101,114,115,32,99,97,110,32,98,101, +32,116,97,103,103,101,100,0,1631867,1581413,12,8,1310721,2,67,26, +1310721,2,7,26,2,26,1310721,2,11,26,2,10,3,6,10,4, +8,1310721,2,7,26,2,25,40,1632089,16,1310721,2,9,26,1310721,2, +9,26,2,1310721,2,11,26,2,1310721,2,7,26,2,1310721,2,7, +26,2,26,1575289,12,26,10,3,6,10,4,8,1310721,2,7,26, +1,6,2,1,6,3,26,5,6,4,10,8,8,1310721,2,7, +26,2,49,40,3,36,1632197,16,1632189,14,116,111,111,32,109,97, +110,121,32,112,97,114,97,109,101,116,101,114,115,0,1632149,1581413, +12,8,1581357,2,1591159,12,1,6,1,10,28,34,63,38,3,26, +1632233,16,1632237,14,1631361,14,1581347,2,1591367,12,8,1581363,2,1591159,12,1632387, +16,1581401,2,1591029,12,1,6,1,10,28,34,63,38,3,26,1632359, +16,1632351,14,116,104,101,32,111,110,108,121,32,114,101,116,117, +114,110,32,116,97,103,32,105,115,32,45,62,32,117,0,1632295, +1581413,12,8,1590445,12,8,1310721,2,13,26,3,10,3,6,10,4, +8,1310721,2,15,26,1310721,2,13,26,2,1310721,2,7,26,2,26, +1310721,2,7,26,2,26,1310721,2,9,26,2,129,30,26,10,3, +6,10,4,8,1442305,1310721,2,5,26,2,26,2,5,32,1632637,16, +1434113,1310721,2,5,26,2,26,2,1310721,2,15,26,2,28,1,6, +1,10,28,34,63,38,1,10,28,1632633,16,1310721,2,3,26,2, +1632625,14,100,111,101,115,110,39,116,32,109,97,116,99,104,32, +105,116,115,32,101,97,114,108,105,101,114,32,100,101,99,108, +97,114,97,116,105,111,110,0,1632549,1581539,12,8,1632925,14,1442305,1310721, +2,5,26,2,26,2,9,32,1632739,16,1310721,2,9,26,2,1632681, +16,3,1632713,14,1310721,2,13,26,2,1,6,1,10,28,34,63, +38,1,10,28,1,6,1,10,28,34,63,38,1,10,28,1632741, +14,1,1632925,16,1310721,2,3,26,2,1632917,14,119,97,115,32,99, +97,108,108,101,100,32,98,101,102,111,114,101,32,105,116,115, +32,116,97,103,115,32,119,101,114,101,32,100,101,99,108,97, +114,101,100,59,32,112,117,116,32,97,32,112,114,111,116,111, +116,121,112,101,32,97,98,111,118,101,32,116,104,101,32,102, +105,114,115,116,32,99,97,108,108,0,1632759,1581539,12,8,1434113,1310721, +2,5,26,2,26,1310721,2,15,26,2,10,3,6,10,4,8, +1442305,1310721,2,5,26,2,26,1442305,1310721,2,5,26,2,26,2,5, +34,10,3,6,10,4,8,1581359,2,1591159,12,1633033,16,1310721,2,5, +26,2,1633821,14,1442305,1310721,2,5,26,2,26,2,3,32,1633113,16, +1310721,2,3,26,2,1633105,14,105,115,32,100,101,102,105,110,101, +100,32,116,119,105,99,101,0,1633071,1581539,12,8,1425921,1310721,2,5, +26,2,26,1581297,2,10,3,6,10,4,8,1442305,1310721,2,5,26, +2,26,1442305,1310721,2,5,26,2,26,2,3,34,10,3,6,10, +4,8,1450497,1310721,2,5,26,2,26,2,1581297,2,1583797,12,8,1450497, +1310721,2,5,26,2,26,1,10,3,6,10,4,8,1310721,2,2, +1310721,2,13,26,2,1630023,12,8,1630317,12,8,1310721,2,17,26,1, +10,3,6,10,4,8,1310721,2,17,26,2,1310721,2,7,26,2, +40,1633687,16,1310721,2,11,26,1310721,2,19,26,1310721,2,17,26,2, +26,2,1595777,12,10,3,6,10,4,8,1581311,1,6,2,1,6, +3,26,5,6,4,10,8,8,1581313,2,1633473,16,1310721,2,67,26, +1310721,2,17,26,2,26,2,3,28,1,6,1,10,28,34,63, +38,3,26,1,6,1,10,28,34,63,38,1,10,28,1633475,14, +1,1633507,16,1469953,1310721,2,11,26,2,26,13,10,3,6,10,4, +8,1581313,2,1633587,16,1310721,2,67,26,1310721,2,17,26,2,26,2, +5,28,1,6,1,10,28,34,63,38,3,26,1,6,1,10, +28,34,63,38,1,10,28,1633589,14,1,1633649,16,1469953,1310721,2,11, +26,2,26,3,10,3,6,10,4,8,1474049,1310721,2,11,26,2, +26,3,10,3,6,10,4,8,1310721,2,17,26,1,6,2,1, +6,3,26,5,6,4,10,8,8,1633293,14,1310721,2,17,26,1310721, +2,7,26,2,10,3,6,10,4,8,1310721,2,17,26,2,1633793, +16,1310721,2,17,26,1,6,2,1,6,-1,26,5,6,4,10, +8,8,1310721,2,17,26,2,1584865,12,8,5,1584215,12,8,1633717,14, +1630571,12,8,1630409,12,8,1310721,2,5,26,2,1633821,14,1,1310721,2, +115,26,1310721,4,18,1310721,2,7,28,1310721,4,1590445,12,8,3,1634325, +16,1310721,2,1591545,12,10,3,6,10,4,8,1581353,2,1591159,12,1634007, +16,1310721,2,5,26,1619459,12,10,3,6,10,4,8,1581355,2,1591367, +12,8,1310721,2,2,7,1581297,2,1582581,12,8,1310721,2,5,26,2, +1634003,16,1,1583993,12,8,1310721,2,5,26,1,6,2,1,6,-1, +26,5,6,4,10,8,8,1633943,14,1634285,14,1310721,2,3,26,1581297, +2,10,3,6,10,4,8,1310721,2,2,5,1310721,2,3,26,2, +1582581,12,8,1,1583993,12,8,1581361,2,1591159,12,1634285,16,1579077,2,7, +28,1,6,1,10,28,34,63,38,3,26,1634253,16,1310721,2,3, +26,2,1581299,2,26,1581297,2,10,3,6,10,4,8,1310721,2,5, +26,1579079,2,10,3,6,10,4,8,1310721,2,5,26,2,2,1634235, +16,1310721,2,5,26,2,2,1583993,12,8,1310721,2,5,26,1,6, +2,1,6,3,26,5,6,4,10,8,8,1634163,14,1,1583993,12, +8,1590445,12,8,1634285,14,1310721,2,3,26,2,1581299,2,26,1619459,12, +10,3,6,10,4,8,1581357,2,1591159,12,1,6,1,10,28,34, +63,38,3,26,1634321,16,1634325,14,1633853,14,1581359,2,1591367,12,8,1, +1634343,14,1,1310721,2,7,26,1310721,4,18,1310721,2,3,28,1310721,4, +1590445,12,8,3,1634469,16,1310721,2,1591545,12,10,3,6,10,4,8, +1581361,2,1591367,12,8,1310721,2,2,9,1619459,12,1582581,12,8,1581357,2, +1591159,12,1,6,1,10,28,34,63,38,3,26,1634465,16,1634469,14, +1634375,14,1581359,2,1591367,12,8,1,1634487,14,1,1310721,2,3,26,1310721, +4,18,1310721,2,9,28,1310721,4,3,1310721,2,4,1,1310721,2,3, +26,4,1581393,2,1591029,12,1634603,16,1590445,12,8,1581359,2,1591159,12,1634585, +16,1579075,1,10,3,6,10,4,8,1,1635379,14,1310721,2,1,10, +3,6,10,4,8,1581395,2,1591029,12,1634643,16,1590445,12,8,1310721,2, +3,26,3,10,3,6,10,4,8,1581383,2,1591029,12,1634929,16,1310721, +2,5,26,1310721,2,2,1630943,12,10,3,6,10,4,8,1310721,2, +3,26,2,1634923,16,1581299,2,1634825,16,1634817,14,112,97,114,115,101, +32,119,111,114,100,115,32,99,97,110,39,116,32,98,101,32, +100,101,102,105,110,101,100,32,119,104,105,108,101,32,98,117, +105,108,100,105,110,103,32,97,110,32,105,109,97,103,101,0, +1634711,1581413,12,8,1310721,2,7,26,1409537,1310721,2,5,26,2,26,2, +11,1425921,1310721,2,5,26,2,26,2,1582581,12,10,3,6,10,4, +8,1442305,1310721,2,7,26,2,26,3,17,34,33,34,65,34,10, +3,6,10,4,8,1,1635379,14,1310721,2,2,1,6,1,10,28, +34,63,38,3,26,1634965,16,3,1634997,14,1310721,2,3,26,2,1, +6,1,10,28,34,63,38,1,10,28,1635037,16,1635029,14,101,120, +112,101,99,116,101,100,32,102,110,0,1635005,1581413,12,8,1581385,2, +1591029,12,1635057,16,1633835,12,1635379,14,1581387,2,1591029,12,1635077,16,1634357,12, +1635379,14,1579077,2,5,28,1,6,1,10,28,34,63,38,3,26, +1635307,16,1310721,2,5,26,1579079,2,1583029,12,10,3,6,10,4,8, +1310721,2,5,26,2,1,40,3,36,1635225,16,1417729,1310721,2,5,26, +2,26,2,11,28,1,6,1,10,28,34,63,38,3,26,1, +6,1,10,28,34,63,38,1,10,28,1635227,14,1,1635277,16,1442305, +1310721,2,5,26,2,26,2,65,32,1,6,1,10,28,34,63, +38,1,10,28,1635279,14,1,1635307,16,1310721,2,5,26,2,1,1610055, +12,8,1,1635379,14,1635363,14,101,120,112,101,99,116,101,100,32, +102,110,44,32,118,97,114,32,111,114,32,99,111,110,115,116, +0,1635311,1581413,12,8,1,1635379,14,1,1310721,2,9,26,1310721,4,18, +1310721,2,1,28,1310721,4,1579077,2,1,28,1,6,1,10,28,34, +63,38,1,10,28,1635449,16,1634501,12,8,1635405,14,1,1635457,14,1, +1310721,2,1,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,4, +1310721,2,2,1310721,2,3,26,4,1310721,2,3,26,2,1579009,2,40, +1635807,16,1417729,1310721,2,3,26,2,26,2,3,28,1,6,1,10, +28,34,63,38,3,26,1635635,16,1442305,1310721,2,3,26,2,26,2, +3,32,1,6,1,10,28,34,63,38,3,26,1,6,1,10, +28,34,63,38,1,10,28,1635637,14,1,1635683,16,1450497,1310721,2,3, +26,2,26,2,1,6,1,10,28,34,63,38,1,10,28,1635685, +14,1,1635769,16,1409537,1310721,2,3,26,2,26,2,1635761,14,105,115, +32,117,115,101,100,32,98,117,116,32,110,101,118,101,114,32, +100,101,102,105,110,101,100,0,1635709,1581539,12,8,1310721,2,3,26, +1,6,2,1,6,3,26,5,6,4,10,8,8,1635505,14,1, +1635815,14,1,1310721,2,5,26,1310721,4,18,1310721,2,11,28,1310721,4, +1310721,2,3,26,4,1310721,2,4,3,1,10,28,1310721,2,5,26, +4,1,1310721,2,7,26,4,1579009,2,1310721,2,9,26,4,1628783,12, +8,1581325,2,1635947,16,1310721,2,5,26,1584435,12,10,3,6,10,4, +8,15,1584215,12,8,3,1310721,2,2,1,1,1585519,12,8,1590445,12, +8,1310721,2,3,26,2,1635995,16,1635393,12,8,1636149,14,1310721,2,7, +26,1581297,2,10,3,6,10,4,8,3,1,1630023,12,8,1630317,12, +8,1579077,2,1,28,1,6,1,10,28,34,63,38,1,10,28, +1636079,16,1620153,12,8,1636035,14,1581307,2,1597865,12,8,1581313,2,1636127,16, +1581317,2,1,6,1,10,28,34,63,38,1,10,28,1636129,14,1, +1636143,16,3,3,1597159,12,8,1630409,12,8,1586333,12,8,1310721,2,9, +26,2,1635471,12,8,1310721,2,5,26,2,1,40,3,36,1636213,16, +1310721,2,5,26,2,1581297,2,1584275,12,8,1581297,2,1581303,2,10,40, +1636247,16,1581303,1581297,2,10,3,6,10,4,8,1629439,12,8,1310721,2, +7,26,2,1636269,14,1,1310721,2,11,26,1310721,4,18,1310721,2,3, +28,1310721,4,1310721,2,4,1310721,2,2,1,1635829,12,1636319,14,1,1310721, +2,3,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,4,1310721, +2,2,3,1635829,12,8,1,1636373,14,1,1310721,2,3,26,1310721,4, +18,1310721,2,5,28,1310721,4,1310721,2,4,1310721,2,2,1,1635829,12, +1310721,2,3,26,4,1310721,2,3,26,2,1,6,12,10,8,1636453, +14,1,1310721,2,5,26,1310721,4,18,1310721,2,7,28,1310721,4,1310721, +2,4,1310721,2,2,1628267,12,1310721,2,3,26,4,1310721,2,3,26, +2,1636387,12,1310721,2,5,26,4,1310721,2,3,26,2,1577415,12,8, +1310721,2,5,26,2,1636561,14,1,1310721,2,7,26,1310721,4,18,1310721, +2,7,28,1310721,4,1310721,2,4,1310721,2,2,1310721,2,3,26,4, +1310721,2,3,26,2,1579009,2,40,1636935,16,1417729,1310721,2,3,26,2, +26,2,3,40,3,36,1636709,16,1417729,1310721,2,3,26,2,26,2, +9,10,40,3,36,1,6,1,10,28,34,63,38,1,10,28, +1636711,14,1,1636897,16,1417729,1310721,2,3,26,2,26,2,1583993,12,8, +1425921,1310721,2,3,26,2,26,2,1583993,12,8,1434113,1310721,2,3,26, +2,26,2,1583993,12,8,1310721,2,5,26,1409537,1310721,2,3,26,2, +26,2,10,3,6,10,4,8,1310721,2,5,26,2,2,1636889,16, +1310721,2,5,26,2,2,1583993,12,8,1310721,2,5,26,1,6,2, +1,6,3,26,5,6,4,10,8,8,1636817,14,1,1583993,12,8, +1310721,2,3,26,1,6,2,1,6,3,26,5,6,4,10,8, +8,1636609,14,1,1583993,12,8,1,1636951,14,1,1310721,2,7,26,1310721, +4,18,1310721,2,7,28,1310721,4,1310721,2,4,1310721,2,2,2,1637207, +16,1310721,2,5,26,1310721,2,2,7,26,1582085,12,10,3,6,10, +4,8,1310721,2,3,26,1310721,2,5,26,2,1310721,2,2,1,26, +2,1310721,2,2,3,26,2,1582581,12,10,3,6,10,4,8,1434113, +1310721,2,3,26,2,26,1310721,2,2,5,26,2,10,3,6,10, +4,8,1442305,1310721,2,3,26,2,26,3,5,34,10,3,6,10, +4,8,1310721,2,1310721,2,2,7,26,1310721,2,2,7,26,1573535,12, +26,3,26,10,3,6,10,4,8,1636983,14,1,1637215,14,1,1310721, +2,7,26,1310721,4,18,1310721,2,19,28,1310721,4,1310721,2,3,26, +4,1310721,2,4,1581297,2,1310721,2,5,26,4,1581299,2,1310721,2,7, +26,4,1581301,2,1310721,2,9,26,4,1579011,2,1310721,2,11,26,4, +1579013,2,1310721,2,13,26,4,1579009,2,1310721,2,15,26,4,1579011,1579009, +2,10,3,6,10,4,8,1579013,1579009,2,10,3,6,10,4,8, +1581297,1310721,2,2,10,3,6,10,4,8,1581299,1310721,2,3,26,2, +1310721,2,2,28,10,3,6,10,4,8,1581301,1310721,2,2,393217,26, +10,3,6,10,4,8,1966081,1584151,12,8,1310721,1584151,12,8,5,1584215, +12,8,1581403,2,1584495,12,1584595,12,8,15,1584215,12,8,1,1583993,12, +8,1,1583993,12,8,1,1,1,1,1585519,12,8,1590445,12,8,1635393, +12,8,1579011,2,1635471,12,8,1586333,12,8,1310721,2,3,26,2,11, +26,1581297,2,10,3,6,10,4,8,1579011,2,1636575,12,8,1310721,2, +3,26,2,13,26,1581297,2,10,3,6,10,4,8,1310721,2,17, +26,1581297,2,1310721,2,2,28,10,3,6,10,4,8,1310721,2,15, +26,2,1583283,12,8,1579011,1310721,2,11,26,2,10,3,6,10,4, +8,1579013,1310721,2,13,26,2,10,3,6,10,4,8,1581297,1310721,2, +5,26,2,10,3,6,10,4,8,1581299,1310721,2,7,26,2,10, +3,6,10,4,8,1581301,1310721,2,9,26,2,10,3,6,10,4, +8,1310721,2,17,26,2,1637811,14,1,1310721,2,19,26,1310721,4,18, +1310721,2,1,28,1310721,4,1,1581303,2,1577865,12,8,1581299,2,1,6, +1,10,28,34,63,38,3,26,1637915,16,1581297,2,1581303,2,10,40, +1,6,1,10,28,34,63,38,1,10,28,1637917,14,1,1637935,16, +1581303,2,1581297,2,1577865,12,8,1572865,1572865,13,26,2,1577865,12,8,1540609, +1540609,801,26,1577865,12,8,1310721,2,1966081,1577865,12,8,2064385,2097121,1577865,12, +8,1,1637995,14,1,1310721,2,1,26,1310721,4,18,1310721,2,1,28, +1310721,4,1579007,1327617,10,3,6,10,4,8,1311233,1,16385,1574133,12,8, +1579009,1,10,3,6,10,4,8,1579015,1,10,3,6,10,4,8, +1579017,1,10,3,6,10,4,8,1579019,1,10,3,6,10,4,8, +1579021,1,10,3,6,10,4,8,1579023,1,10,3,6,10,4,8, +1579025,1,10,3,6,10,4,8,1579027,1,10,3,6,10,4,8, +1581327,1,10,3,6,10,4,8,1581331,1,10,3,6,10,4,8, +1581333,1,10,3,6,10,4,8,1581305,1,10,3,6,10,4,8, +1581307,1,10,3,6,10,4,8,1581345,1638267,14,40,0,1638263,1582085,12, +10,3,6,10,4,8,1581347,1638295,14,41,0,1638291,1582085,12,10,3, +6,10,4,8,1581349,1638323,14,123,0,1638319,1582085,12,10,3,6,10, +4,8,1581351,1638351,14,125,0,1638347,1582085,12,10,3,6,10,4,8, +1581353,1638379,14,91,0,1638375,1582085,12,10,3,6,10,4,8,1581355,1638407, +14,93,0,1638403,1582085,12,10,3,6,10,4,8,1581357,1638435,14,44, +0,1638431,1582085,12,10,3,6,10,4,8,1581359,1638463,14,59,0,1638459, +1582085,12,10,3,6,10,4,8,1581361,1638491,14,61,0,1638487,1582085,12, +10,3,6,10,4,8,1581363,1638521,14,45,62,0,1638515,1582085,12,10, +3,6,10,4,8,1581365,1638551,14,43,43,0,1638545,1582085,12,10,3, +6,10,4,8,1581367,1638581,14,45,45,0,1638575,1582085,12,10,3,6, +10,4,8,1581369,1638609,14,42,0,1638605,1582085,12,10,3,6,10,4, +8,1581371,1638637,14,38,0,1638633,1582085,12,10,3,6,10,4,8,1581373, +1638665,14,58,0,1638661,1582085,12,10,3,6,10,4,8,1581375,1638693,14, +35,0,1638689,1582085,12,10,3,6,10,4,8,1581377,1638721,14,45,0, +1638717,1582085,12,10,3,6,10,4,8,1581379,1638751,14,38,38,0,1638745, +1582085,12,10,3,6,10,4,8,1581381,1638781,14,124,124,0,1638775,1582085, +12,10,3,6,10,4,8,1581383,1638811,14,102,110,0,1638805,1582085,12, +10,3,6,10,4,8,1581385,1638843,14,118,97,114,0,1638835,1582085,12, +10,3,6,10,4,8,1581387,1638879,14,99,111,110,115,116,0,1638867, +1582085,12,10,3,6,10,4,8,1581389,1638913,14,101,108,115,101,0, +1638903,1582085,12,10,3,6,10,4,8,1581391,1638943,14,97,115,0,1638937, +1582085,12,10,3,6,10,4,8,1581393,1638987,14,117,110,99,104,101, +99,107,101,100,0,1638967,1582085,12,10,3,6,10,4,8,1581395,1639031, +14,105,109,109,101,100,105,97,116,101,0,1639011,1582085,12,10,3, +6,10,4,8,1581397,1639059,14,109,0,1639055,1582085,12,10,3,6,10, +4,8,1581399,1639087,14,100,0,1639083,1582085,12,10,3,6,10,4,8, +1581401,1639115,14,117,0,1639111,1582085,12,10,3,6,10,4,8,1581403,1639151, +14,115,116,97,114,116,0,1639139,1582085,12,10,3,6,10,4,8, +1581405,1639185,14,109,97,105,110,0,1639175,1582085,12,10,3,6,10,4, +8,1581407,1639219,14,102,114,101,101,0,1639209,1582085,12,10,3,6,10, +4,8,1581409,1639251,14,111,119,110,0,1639243,1582085,12,10,3,6,10, +4,8,1581411,1639285,14,113,110,101,119,0,1639275,1582085,12,10,3,6, +10,4,8,1,1639311,14,1,1310721,2,1,26,1310721,4,18,1310721,2, +1,28,1310721,4,1639345,14,33,0,1639341,1639393,14,48,32,36,32,48, +32,126,32,45,32,124,32,51,49,32,62,32,49,32,43,0, +1639351,1595139,12,8,1639411,14,33,33,0,1639405,1639463,14,48,32,36,32, +48,32,126,32,45,32,124,32,51,49,32,62,32,48,32,126, +32,45,0,1639417,1595139,12,8,1639479,14,126,0,1639475,1639495,14,45,49, +32,94,0,1639485,1595139,12,8,1639511,14,45,0,1639507,1639529,14,48,32, +126,32,45,0,1639517,1595139,12,8,1639545,14,42,0,1639541,21,1639557,14, +42,0,1639553,1594121,12,8,1639573,14,47,0,1639569,21,1639595,14,122,100, +105,118,32,99,0,1639581,1594121,12,8,1639611,14,37,0,1639607,21,1639633, +14,122,109,111,100,32,99,0,1639619,1594121,12,8,1639649,14,43,0, +1639645,19,1639661,14,43,0,1639657,1594121,12,8,1639677,14,45,0,1639673,19, +1639689,14,45,0,1639685,1594121,12,8,1639707,14,60,60,0,1639701,17,1639729, +14,122,115,104,108,32,99,0,1639715,1594121,12,8,1639747,14,62,62, +0,1639741,17,1639759,14,62,0,1639755,1594121,12,8,1639775,14,60,0,1639771, +15,1639787,14,60,0,1639783,1594121,12,8,1639803,14,62,0,1639799,15,1639819, +14,126,32,60,0,1639811,1594121,12,8,1639837,14,60,61,0,1639831,15, +1639861,14,126,32,60,32,49,32,94,0,1639845,1594121,12,8,1639879,14, +62,61,0,1639873,15,1639899,14,60,32,49,32,94,0,1639887,1594121,12, +8,1639917,14,61,61,0,1639911,13,1639971,14,45,32,48,32,36,32, +48,32,126,32,45,32,124,32,51,49,32,62,32,49,32,43, +0,1639925,1594121,12,8,1639989,14,33,61,0,1639983,13,1640047,14,45,32, +48,32,36,32,48,32,126,32,45,32,124,32,51,49,32,62, +32,48,32,126,32,45,0,1639997,1594121,12,8,1640063,14,38,0,1640059, +11,1640075,14,38,0,1640071,1594121,12,8,1640091,14,94,0,1640087,9,1640103, +14,94,0,1640099,1594121,12,8,1640119,14,124,0,1640115,7,1640131,14,124, +0,1640127,1594121,12,8,1640149,14,38,38,0,1640143,3,1,10,28,1, +1594121,12,8,1640177,14,124,124,0,1640171,5,1,10,28,1,1594121,12, +8,1581361,2,1593381,12,8,1581363,2,1593381,12,8,1581365,2,1593381,12,8, +1581367,2,1593381,12,8,1640247,14,118,97,114,0,1640239,1621001,17,1595451,12, +8,1640269,14,105,102,0,1640263,1621389,17,1595451,12,8,1640297,14,119,104, +105,108,101,0,1640285,1622287,17,1595451,12,8,1640321,14,102,111,114,0, +1640313,1622595,17,1595451,12,8,1640349,14,98,114,101,97,107,0,1640337,1623689, +17,1595451,12,8,1640383,14,99,111,110,116,105,110,117,101,0,1640365, +1623857,17,1595451,12,8,1640413,14,114,101,116,117,114,110,0,1640399,1623981, +17,1595451,12,8,1640449,14,117,110,99,104,101,99,107,101,100,0, +1640429,1624377,17,1595451,12,8,1640473,14,97,115,109,0,1640465,1626893,17,33, +34,1595451,12,8,1640505,14,113,117,111,116,101,0,1640493,1628015,33,1595451, +12,8,1,1640525,14,1,1310721,2,1,26,1310721,4,18,1310721,2,5, +28,1310721,4,1575437,12,8,1638009,12,8,1572865,11,26,2,1636965,12,8, +1639325,12,8,1581297,1,10,3,6,10,4,8,1581299,1,10,3,6, +10,4,8,1581301,524289,10,3,6,10,4,8,1581303,1,10,3,6, +10,4,8,1579011,1579009,2,10,3,6,10,4,8,1579013,1,10,3, +6,10,4,8,1,1,1,1,1585519,12,8,1590445,12,8,1635393,12, +8,1579011,2,1635471,12,8,1581297,2,1581303,2,10,40,1640751,16,1581303,1581297, +2,10,3,6,10,4,8,1310721,2,1581405,2,1583029,12,10,3,6, +10,4,8,1310721,2,2,1,40,3,36,1640855,16,1417729,1310721,2,2, +26,2,3,28,1,6,1,10,28,34,63,38,3,26,1,6, +1,10,28,34,63,38,1,10,28,1640857,14,1,1640903,16,1442305,1310721, +2,2,26,2,3,32,1,6,1,10,28,34,63,38,1,10, +28,1640905,14,1,1640963,16,1310721,2,3,26,1425921,1310721,2,2,26,2, +10,3,6,10,4,8,1310721,2,3,26,2,1,6,12,10,8, +8,1572995,12,8,1,1640977,14,1,1310721,2,5,26,1310721,4,18,1, +820269,0,115,116,97,114,116,0,4,1048576,0,77,87,0,4,262144, +0,72,69,65,80,95,76,79,0,4,655360,0,72,69,65,80, +95,72,73,0,4,655360,0,90,70,80,0,4,786432,0,71,79, +82,71,0,4,917504,0,70,66,79,84,0,4,983040,0,70,84, +79,80,0,4,983040,0,82,83,48,0,4,1015808,0,77,65,82, +75,48,0,4,16384,0,77,65,82,75,78,0,4,1032192,0,68, +83,76,79,0,4,1048560,0,68,83,72,73,0,4,729683222,0,77, +65,71,73,67,0,1,786439,2,112,117,116,99,0,1,786463,0, +103,101,116,99,0,1,786480,0,101,111,102,0,1,786497,0,104, +97,108,116,0,1,786515,66,112,117,116,115,0,1,786585,2,112, +117,116,110,0,1,787584,4,122,109,111,100,0,1,787407,4,122, +100,105,118,0,1,786767,66,115,116,114,108,101,110,0,1,786828, +324,115,116,114,101,113,0,1,786971,326,119,99,111,112,121,0, +1,787066,70,119,102,105,108,108,0,3,787154,0,112,111,119,50, +0,2,787186,0,122,114,101,109,0,1,787187,4,122,117,100,105, +118,0,1,787644,4,122,115,104,108,0,2,787682,0,104,98,117, +109,112,0,3,787683,0,102,114,101,101,108,0,2,787715,0,103, +99,95,114,117,110,115,0,2,787716,0,103,99,95,102,114,101, +101,100,0,2,787717,0,103,99,95,97,117,116,111,0,1,787718, +0,104,101,97,112,95,105,110,105,116,0,1,787815,66,105,115, +111,98,106,0,1,787933,0,111,111,109,0,1,787983,3,110,101, +119,0,1,789063,0,103,99,0,1,788423,66,111,115,105,122,101, +0,1,788473,2,114,101,108,101,97,115,101,0,1,788707,130,102, +114,101,101,0,1,788740,4,111,119,110,0,2,788793,0,109,115, +95,116,111,112,0,2,788794,0,109,115,95,111,118,101,114,0, +2,788795,0,103,99,95,108,111,0,2,788796,0,103,99,95,104, +105,0,1,788797,2,109,97,114,107,0,1,788932,4,109,97,114, +107,114,97,110,103,101,0,1,788992,0,100,114,97,105,110,0, +1,818912,0,103,99,95,114,111,111,116,115,0,1,789417,0,108, +105,118,101,95,111,98,106,101,99,116,115,0,4,8192,0,73, +72,78,0,4,655616,0,82,95,73,72,0,4,663808,0,82,95, +80,79,79,76,0,4,704512,0,82,95,80,79,79,76,69,78, +68,0,4,4096,0,78,83,0,4,704768,0,82,95,83,89,77, +0,4,704768,0,115,110,97,109,101,0,4,708864,0,115,107,105, +110,100,0,4,712960,0,115,118,97,108,0,4,717056,0,115,115, +105,103,0,4,721152,0,115,102,108,97,103,0,4,725248,0,115, +102,105,120,0,4,729344,0,115,110,101,120,116,0,4,512,0, +78,76,0,4,733440,0,108,110,97,109,101,0,4,733952,0,108, +115,108,111,116,0,4,734464,0,108,104,111,109,101,0,4,734976, +0,108,115,116,97,116,101,0,4,735488,0,108,111,119,110,101, +114,0,4,736000,0,108,117,115,101,100,0,4,736512,0,108,97, +114,114,0,4,737024,0,108,111,119,110,0,4,8192,0,78,70, +0,4,737536,0,102,115,105,116,101,0,4,745728,0,102,110,101, +120,116,0,4,753920,0,82,95,83,78,65,80,0,4,762112,0, +82,95,81,66,85,70,0,4,770304,0,82,95,83,82,67,0, +4,771328,0,82,95,83,69,81,0,4,773376,0,98,110,97,109, +101,0,4,773440,0,98,112,114,101,99,0,4,773504,0,98,115, +101,113,0,4,773568,0,98,108,101,110,0,4,773632,0,117,110, +97,109,101,0,4,773664,0,117,115,101,113,0,4,773696,0,117, +108,101,110,0,4,773728,0,97,110,97,109,101,0,4,773792,0, +97,98,105,110,0,4,773888,0,107,111,112,115,0,4,774400,0, +82,95,67,84,88,0,4,776448,0,82,95,76,79,79,80,0, +4,777472,0,82,95,65,83,77,0,4,0,0,84,95,69,79, +70,0,4,1,0,84,95,78,85,77,0,4,2,0,84,95, +73,68,0,4,3,0,84,95,83,84,82,0,4,4,0,84, +95,79,80,0,4,1,0,75,95,70,78,0,4,2,0,75, +95,71,86,65,82,0,4,3,0,75,95,71,65,82,82,0, +4,4,0,75,95,67,79,78,83,84,0,4,5,0,75,95, +75,87,0,4,1,0,70,95,68,69,70,0,4,2,0,70, +95,83,73,71,0,4,4,0,70,95,85,78,84,89,80,69, +68,0,4,8,0,70,95,83,84,77,84,0,4,16,0,70, +95,69,88,80,82,0,4,32,0,70,95,84,79,80,0,4, +0,0,83,95,80,76,65,73,78,0,4,1,0,83,95,77, +0,4,2,0,83,95,85,0,4,3,0,83,95,68,0,4, +4,0,83,95,68,69,65,68,0,4,5,0,83,95,67,72, +73,76,68,0,4,6,0,83,95,66,79,82,82,79,87,0, +4,0,0,67,95,80,76,65,73,78,0,4,1,0,67,95, +70,82,69,83,72,0,4,2,0,67,95,86,65,82,0,4, +0,0,76,86,95,78,79,78,69,0,4,1,0,76,86,95, +76,79,67,65,76,0,4,2,0,76,86,95,71,76,79,66, +65,76,0,4,3,0,76,86,95,69,76,69,77,0,4,4, +0,76,86,95,79,84,72,69,82,0,4,0,0,79,95,81, +0,4,1,0,79,95,76,68,0,4,2,0,79,95,83,84, +0,4,3,0,79,95,80,73,67,75,0,4,4,0,79,95, +68,82,79,80,0,4,5,0,79,95,83,87,65,80,0,4, +6,0,79,95,67,65,76,76,0,4,7,0,79,95,74,77, +80,0,4,8,0,79,95,74,90,0,4,9,0,79,95,82, +69,84,0,4,13,0,79,95,65,68,68,0,4,14,0,79, +95,83,85,66,0,4,-2,0,83,69,81,82,69,70,0,2, +789503,0,112,111,111,108,116,111,112,0,2,789504,0,110,115,121, +109,0,2,789505,0,117,98,97,115,101,0,2,789506,0,118,105, +115,108,111,0,2,789507,0,110,102,105,120,117,0,2,789508,0, +102,105,120,102,114,101,101,0,2,789509,0,110,98,105,110,0, +2,789510,0,110,117,110,0,2,789511,0,110,97,115,103,0,2, +789512,0,110,107,111,112,0,2,789513,0,115,101,113,116,111,112, +0,2,789514,0,109,110,101,109,0,2,789537,0,99,104,101,99, +107,95,111,110,0,2,789538,0,116,107,0,2,789539,0,116,118, +0,2,789540,0,116,103,108,117,101,0,2,789541,0,116,108,105, +110,101,0,2,789542,0,108,105,110,101,0,2,789543,0,99,104, +0,2,789544,0,99,104,107,105,110,100,0,2,789545,0,99,104, +112,116,114,0,2,789546,0,113,100,111,110,101,0,2,789547,0, +105,115,112,0,3,789548,0,116,98,117,102,0,2,790648,0,104, +101,114,101,0,2,790649,0,104,100,101,108,116,97,0,2,790650, +0,104,108,105,109,105,116,0,2,790651,0,112,116,111,112,0, +2,790652,0,110,108,111,99,0,2,790653,0,108,98,97,115,101, +0,2,790654,0,102,100,101,112,116,104,0,2,790655,0,102,115, +108,111,116,115,0,2,790656,0,102,99,104,107,0,2,790657,0, +102,114,101,116,0,2,790658,0,108,105,118,101,0,2,790659,0, +98,120,100,101,112,116,104,0,2,790660,0,101,112,105,99,104, +97,105,110,0,2,790661,0,102,102,114,97,109,101,0,2,790662, +0,105,110,102,110,0,2,790663,0,108,115,112,0,2,790664,0, +108,111,111,112,98,97,115,101,0,2,790665,0,99,116,120,115, +112,0,2,790666,0,115,110,97,112,116,111,112,0,2,790667,0, +108,118,0,2,790668,0,108,118,107,0,2,790669,0,108,118,105, +0,2,790670,0,107,119,95,101,120,112,114,0,2,790671,0,120, +99,108,115,0,2,790672,0,115,95,108,112,0,2,790673,0,115, +95,114,112,0,2,790674,0,115,95,108,98,0,2,790675,0,115, +95,114,98,0,2,790676,0,115,95,108,107,0,2,790677,0,115, +95,114,107,0,2,790678,0,115,95,99,111,109,109,97,0,2, +790679,0,115,95,115,101,109,105,0,2,790680,0,115,95,101,113, +0,2,790681,0,115,95,97,114,114,111,119,0,2,790682,0,115, +95,105,110,99,0,2,790683,0,115,95,100,101,99,0,2,790684, +0,115,95,115,116,97,114,0,2,790685,0,115,95,97,109,112, +0,2,790686,0,115,95,99,111,108,111,110,0,2,790687,0,115, +95,104,97,115,104,0,2,790688,0,115,95,109,105,110,117,115, +0,2,790689,0,115,95,97,110,100,97,110,100,0,2,790690,0, +115,95,111,114,111,114,0,2,790691,0,115,95,102,110,0,2, +790692,0,115,95,118,97,114,0,2,790693,0,115,95,99,111,110, +115,116,0,2,790694,0,115,95,101,108,115,101,0,2,790695,0, +115,95,97,115,0,2,790696,0,115,95,117,110,99,104,101,99, +107,101,100,0,2,790697,0,115,95,105,109,109,101,100,105,97, +116,101,0,2,790698,0,115,95,109,0,2,790699,0,115,95,100, +0,2,790700,0,115,95,117,0,2,790701,0,115,95,115,116,97, +114,116,0,2,790702,0,115,95,109,97,105,110,0,2,790703,0, +115,95,102,114,101,101,0,2,790704,0,115,95,111,119,110,0, +2,790705,0,115,95,113,110,101,119,0,1,790706,2,100,105,101, +0,1,790769,4,100,105,101,110,0,1,790854,2,99,101,114,114, +0,1,790902,4,99,101,114,114,110,0,1,790963,2,104,97,115, +104,115,0,1,791042,2,105,110,116,101,114,110,0,1,791290,6, +97,100,100,115,121,109,0,1,791514,2,108,111,111,107,117,112, +0,1,791641,2,112,111,112,115,121,109,115,0,1,791707,2,108, +111,111,107,117,112,115,0,1,791733,4,102,105,120,110,101,119, +0,1,791898,4,102,105,120,97,108,108,0,1,792137,4,115,101, +116,108,105,116,0,1,791996,2,101,109,105,116,0,1,792075,2, +108,105,116,0,1,792107,2,111,112,0,1,792187,2,106,109,112, +0,1,792217,0,104,111,108,101,0,1,792247,2,102,119,100,102, +110,0,1,792297,2,102,110,114,101,102,0,1,792366,2,99,97, +108,108,110,97,109,101,0,1,792432,2,108,97,100,100,114,0, +1,792475,2,115,116,114,108,105,116,0,1,792570,0,110,101,120, +116,99,104,0,1,792759,8,115,114,99,95,112,117,115,104,0, +1,793166,0,115,114,99,95,112,111,112,0,1,793301,2,105,115, +111,112,99,104,0,1,793401,4,111,112,112,114,101,102,105,120, +0,1,793633,2,105,115,100,105,103,0,1,793683,2,105,115,97, +108,0,1,793811,2,104,101,120,118,0,1,793932,0,101,115,99, +99,104,0,1,794095,2,111,112,108,101,120,0,1,794229,0,108, +101,120,0,1,795222,0,110,101,120,116,0,1,795449,2,105,115, +95,111,112,0,1,795514,2,105,115,95,105,100,0,1,795579,2, +97,99,99,101,112,116,0,1,795683,2,101,120,112,101,99,116, +0,1,795772,0,110,97,109,101,0,1,795838,2,115,101,113,97, +115,109,0,1,796396,2,109,110,101,109,105,100,120,0,1,796487, +4,101,109,105,116,115,101,113,0,1,796690,2,97,100,100,107, +111,112,0,1,796799,2,98,105,110,102,105,110,100,0,1,796886, +2,117,110,102,105,110,100,0,1,796973,2,97,115,103,102,105, +110,100,0,1,797060,6,97,100,100,95,98,105,110,111,112,0, +1,797569,4,97,100,100,95,117,110,111,112,0,1,797725,6,97, +100,100,95,107,119,0,1,797797,2,102,105,110,100,108,111,99, +0,1,797888,2,110,101,119,108,111,99,0,1,798079,2,108,114, +111,111,116,0,1,798137,2,116,114,97,99,107,101,100,0,1, +798175,2,116,111,117,99,104,0,1,798408,2,112,108,97,105,110, +117,115,101,0,1,798537,2,102,114,101,101,118,97,114,0,1, +798579,4,101,120,105,116,95,118,97,114,115,0,1,798932,2,117, +110,117,115,101,100,95,99,104,101,99,107,0,1,799070,2,98, +108,111,99,107,95,99,108,111,115,101,0,1,799195,0,115,110, +97,112,95,112,117,115,104,0,1,799459,2,115,110,97,112,95, +112,111,112,0,1,799489,2,115,110,97,112,95,114,101,115,116, +111,114,101,0,1,799697,2,101,109,112,116,121,0,1,799762,2, +115,110,97,112,95,99,109,112,0,1,800156,2,115,116,97,116, +101,110,97,109,101,0,1,800363,6,109,101,114,103,101,95,101, +114,114,0,1,800544,4,109,101,114,103,101,0,1,800744,2,108, +111,111,112,99,104,101,99,107,0,1,800956,4,114,101,99,97, +115,116,0,1,801481,4,97,114,103,99,104,101,99,107,0,1, +802347,2,101,109,105,116,111,119,110,0,1,802385,6,97,115,115, +105,103,110,99,104,101,99,107,0,1,804054,2,114,101,116,99, +104,101,99,107,0,1,804535,2,104,97,110,100,117,112,0,1, +804882,2,100,105,115,99,97,114,100,0,1,804994,0,114,118,97, +108,0,1,805027,4,107,119,99,97,108,108,0,1,805145,0,115, +116,109,116,111,110,108,121,0,1,805212,2,99,97,108,108,0, +1,808329,0,101,120,112,114,0,1,805552,0,112,114,105,109,97, +114,121,0,1,809006,0,98,108,111,99,107,101,120,112,114,0, +1,806403,4,105,110,99,100,101,99,0,1,806695,0,112,111,115, +116,102,105,120,0,1,807485,0,117,110,97,114,121,0,1,807839, +2,98,105,110,0,1,808292,0,115,116,111,114,101,107,101,101, +112,0,1,808795,0,115,116,109,116,115,116,97,114,116,0,1, +810076,0,115,116,109,116,0,1,809332,0,99,112,114,105,109,0, +1,809729,0,99,101,120,112,114,0,1,810314,0,98,108,111,99, +107,0,1,810431,0,115,117,98,115,116,109,116,0,1,810500,0, +107,119,95,118,97,114,0,1,810694,0,107,119,95,105,102,0, +1,810932,4,108,111,111,112,95,112,117,115,104,0,1,811098,0, +108,111,111,112,95,112,111,112,0,1,811143,0,107,119,95,119, +104,105,108,101,0,1,811297,0,107,119,95,102,111,114,0,1, +811676,2,108,111,111,112,101,120,105,116,0,1,811844,0,107,119, +95,98,114,101,97,107,0,1,811928,0,107,119,95,99,111,110, +116,105,110,117,101,0,1,811990,0,107,119,95,114,101,116,117, +114,110,0,1,812188,0,107,119,95,117,110,99,104,101,99,107, +101,100,0,1,812237,0,97,115,109,98,108,111,99,107,0,1, +813446,0,107,119,95,97,115,109,0,1,813477,1,103,114,97,98, +0,1,813797,3,113,110,101,119,0,1,813864,67,113,100,117,112, +0,1,813931,68,119,99,111,112,121,116,111,0,1,814007,0,107, +119,95,113,117,111,116,101,0,1,814133,3,116,111,107,101,110, +115,0,1,814359,130,102,101,101,100,0,1,814391,0,99,116,120, +95,112,117,115,104,0,1,814719,0,99,116,120,95,112,111,112, +0,1,815011,4,102,110,95,98,101,103,105,110,0,1,815158,0, +112,114,111,108,111,103,117,101,0,1,815204,0,101,112,105,108, +111,103,117,101,0,1,815285,0,98,111,100,121,0,1,815471,2, +102,110,100,101,102,0,1,816917,0,103,118,97,114,0,1,817178, +0,103,99,111,110,115,116,0,1,817250,0,116,111,112,105,116, +101,109,0,1,817696,0,117,110,105,116,95,116,111,112,0,1, +817735,2,117,110,105,116,95,101,110,100,0,1,817914,68,99,111, +109,112,105,108,101,95,108,105,115,116,0,1,818141,66,99,111, +109,112,105,108,101,0,1,818166,66,108,111,97,100,0,1,818193, +66,101,118,97,108,0,1,818233,2,101,118,97,108,115,0,1, +818287,2,98,108,111,98,95,101,109,105,116,0,1,818482,2,98, +108,111,98,95,108,111,97,100,0,1,818614,4,98,117,105,108, +100,95,105,109,97,103,101,0,1,819004,0,122,99,95,105,110, +105,116,0,1,819662,0,122,99,95,116,97,98,108,101,115,0, +0 +}; +#define GLEN 37505 diff --git a/docs/history/10-zc/code/io_linux.c b/docs/history/10-zc/code/io_linux.c new file mode 100644 index 0000000..7ea34e0 --- /dev/null +++ b/docs/history/10-zc/code/io_linux.c @@ -0,0 +1,6 @@ +#include +int J; +static int pk(void){int c=getchar();ungetc(c,stdin);return c==EOF;} +char Q(void){int c=getchar();J=pk();return c;} +void G(char c){putchar(c);} +__attribute__((constructor)) static void init(void){J=pk();} diff --git a/docs/history/10-zc/code/mkgenome.c b/docs/history/10-zc/code/mkgenome.c new file mode 100644 index 0000000..8fb2cc5 --- /dev/null +++ b/docs/history/10-zc/code/mkgenome.c @@ -0,0 +1,16 @@ +/* turns a genome image (little-endian 32-bit words on stdin, as written by + tools/build.zc) into genome.h */ +#include +#define GORG 0xC0000 +int main(void) +{ + unsigned char b[4]; int n = 0; + printf("#define GORG %d\nstatic const int genome[] = {", GORG); + while (fread(b, 1, 4, stdin) == 4) { + int w = (int)(b[0] | b[1] << 8 | b[2] << 16 | (unsigned)b[3] << 24); + printf("%s%d", !n ? "\n" : n % 16 ? "," : ",\n", w); + n++; + } + printf("\n};\n#define GLEN %d\n", n); + return n ? 0 : 1; +} diff --git a/docs/history/10-zc/code/src/compiler.zc b/docs/history/10-zc/code/src/compiler.zc new file mode 100644 index 0000000..29116fa --- /dev/null +++ b/docs/history/10-zc/code/src/compiler.zc @@ -0,0 +1,1591 @@ +// ============================================================================ +// compiler.zc: the resident zc compiler. it lives in the genome next to the +// runtime, compiles source straight to vm words in memory, and is visible to +// the programs it compiles: every function and global here can be called or +// patched by name, and parse words (immediate fns) run inside it. +// ============================================================================ + +// ---- compiler data region (fixed addresses, 0xA0100 .. 0xBE100) ---- +const IHN = 8192; +const R_IH = 0xA0100; // intern hash table +const R_POOL = 0xA2100; // interned strings: [symbol head] chars 0 +const R_POOLEND = 0xAC000; +const NS = 4096; // symbol table, one array per field +const R_SYM = 0xAC100; +const sname = 0xAC100, skind = 0xAD100, sval = 0xAE100, ssig = 0xAF100; +const sflag = 0xB0100, sfix = 0xB1100, snext = 0xB2100; +const NL = 512; // locals, one array per field +const lname = 0xB3100, lslot = 0xB3300, lhome = 0xB3500, lstate = 0xB3700; +const lowner = 0xB3900, lused = 0xB3B00, larr = 0xB3D00, lown = 0xB3F00; // lown: has owned an object +const NF = 8192; // patch nodes: a site and a link +const fsite = 0xB4100, fnext = 0xB6100; +const R_SNAP = 0xB8100; // analyzer snapshots +const R_QBUF = 0xBA100; // token buffer for quote and grab +const R_SRC = 0xBC100; // input source stack, 12 words per frame +const R_SEQ = 0xBC500; // operator code sequences +const bname = 0xBCD00, bprec = 0xBCD40, bseq = 0xBCD80, blen = 0xBCDC0; // binary operators +const uname = 0xBCE00, useq = 0xBCE20, ulen = 0xBCE40; // unary operators +const aname = 0xBCE60, abin = 0xBCEA0; // compound assignment +const kops = 0xBCF00; // every operator spelling the lexer knows +const R_CTX = 0xBD100; // saved compiler contexts for nested compiles +const R_LOOP = 0xBD900; // loop stack, 6 words per loop +const R_ASM = 0xBDD00; // asm block labels + +// ---- token kinds, symbol kinds and flags ---- +const T_EOF = 0, T_NUM = 1, T_ID = 2, T_STR = 3, T_OP = 4; +const K_FN = 1, K_GVAR = 2, K_GARR = 3, K_CONST = 4, K_KW = 5; +const F_DEF = 1, F_SIG = 2, F_UNTYPED = 4, F_STMT = 8, F_EXPR = 16, F_TOP = 32; +// variable states for the lifetime analyzer +const S_PLAIN = 0, S_M = 1, S_U = 2, S_D = 3, S_DEAD = 4, S_CHILD = 5, S_BORROW = 6; +// expression classes: plain word, a new object, or a bare variable (C_VAR + index) +const C_PLAIN = 0, C_FRESH = 1, C_VAR = 2; +// lvalue kinds +const LV_NONE = 0, LV_LOCAL = 1, LV_GLOBAL = 2, LV_ELEM = 3, LV_OTHER = 4; +// opcodes +const O_Q = 0, O_LD = 1, O_ST = 2, O_PICK = 3, O_DROP = 4, O_SWAP = 5, O_CALL = 6, O_JMP = 7; +const O_JZ = 8, O_RET = 9, O_ADD = 13, O_SUB = 14; +const SEQREF = -2; // in an operator sequence: the next word names a symbol + +// ---- state ---- +var pooltop, nsym, ubase, vislo, nfixu, fixfree; +var nbin, nun, nasg, nkop, seqtop; +var mnem = "q@!$_~cjzr,?.+-*&|^><"; +var check_on = 1; // set to 0 to switch the analyzer off for what follows +var tk, tv, tglue, tline, line = 1, ch, chkind, chptr, qdone, isp = -1; +var tbuf[1100]; +var here, hdelta, hlimit, ptop; +var nloc, lbase, fdepth, fslots, fchk, fret, live, bxdepth, epichain, fframe, infn; +var lsp, loopbase, ctxsp, snaptop; +var lv, lvk, lvi, kw_expr, xcls; +// interned spellings the parser compares against +var s_lp, s_rp, s_lb, s_rb, s_lk, s_rk, s_comma, s_semi, s_eq, s_arrow, s_inc, s_dec; +var s_star, s_amp, s_colon, s_hash, s_minus, s_andand, s_oror; +var s_fn, s_var, s_const, s_else, s_as, s_unchecked, s_immediate, s_m, s_d, s_u; +var s_start, s_main, s_free, s_own, s_qnew; + +// ---- errors ---- +fn die(msg) { puts("zc: line "); putn(tline); puts(": "); puts(msg); putc(10); halt(); return 0; } +fn dien(name, msg) { puts("zc: line "); putn(tline); puts(": "); puts(name); puts(": "); puts(msg); putc(10); halt(); return 0; } +// analyzer errors only count in checked, reachable code +fn cerr(msg) { if (fchk && live) die(msg); return 0; } +fn cerrn(i, msg) { if (fchk && live) dien(lname[i], msg); return 0; } + +// ---- interning ---- +fn hashs(s) { var h = 0; while (*s) { h = h * 31 + *s; s++; } return h & (IHN - 1); } +fn intern(s) +{ + var h = hashs(s), p, n; + while (1) { + p = R_IH[h]; + if (!p) break; + if (streq(p, s)) return p; + h = (h + 1) & (IHN - 1); + } + n = strlen(s); + if (pooltop + n + 2 >= R_POOLEND) die("string pool full"); + pooltop[0] = 0; + p = pooltop + 1; + wcopy(p, s, n + 1); + pooltop = pooltop + n + 2; + R_IH[h] = p; + return p; +} + +// ---- symbols ---- +// each interned name keeps the index (+1) of its newest symbol in p[-1], and +// each symbol links to the one it shadows, so lookup is one step. +fn addsym(name, kind, val) +{ + var i = nsym; + if (nsym >= NS) die("symbol table full"); + nsym++; + sname[i] = name; skind[i] = kind; sval[i] = val; ssig[i] = 0; sflag[i] = F_DEF; sfix[i] = 0; + snext[i] = name[-1]; + name[-1] = i + 1; + return i; +} +// while an image builds, names below vislo are hidden, except keywords +fn lookup(name) +{ + var i = name[-1] - 1; + while (i >= 0) { + if (i >= vislo || skind[i] == K_KW) return i; + i = snext[i] - 1; + } + return -1; +} +fn popsyms(to) { while (nsym > to) { nsym--; sname[nsym][-1] = snext[nsym]; } return 0; } +fn lookups(s) { return lookup(intern(s)); } + +// patch nodes +fn fixnew(site, link) +{ + var i = fixfree; + if (i) fixfree = fnext[i]; + else { nfixu++; i = nfixu; if (i >= NF) die("too many forward references"); } + fsite[i] = site; fnext[i] = link; + return i; +} +// patch every lit in a chain to point at v, and recycle the nodes +fn fixall(head, v) +{ + var n; + while (head) { setlit(fsite[head], v); n = fnext[head]; fnext[head] = fixfree; fixfree = head; head = n; } + return 0; +} + +// ---- code emission ---- +fn emit(w) { if (here >= hlimit) die("program too big"); *(here + hdelta) = w; here++; return 0; } +fn lit(v) { emit(v + v + 1); return 0; } +fn op(k) { emit(k + k); return 0; } +fn setlit(at, v) { *(at + hdelta) = v + v + 1; return 0; } +fn jmp(t) { lit(t); op(O_JMP); return 0; } +// a lit that will be patched later; returns its address +fn hole() { var a = here; lit(MW - 1); return a; } +fn fwdfn(name) { var s = addsym(name, K_FN, 0); sflag[s] = 0; return s; } +fn fnref(s) +{ + if (sflag[s] & F_DEF) { lit(sval[s]); return 0; } + sfix[s] = fixnew(here, sfix[s]); + lit(MW - 1); // a jump to MW-1 halts if it ever runs unpatched + return 0; +} +fn callname(name) { var s = lookup(name); if (s < 0) s = fwdfn(name); fnref(s); op(O_CALL); return 0; } +fn laddr(k) { lit(ZFP); op(O_LD); if (k) { lit(k); op(O_ADD); } return 0; } +fn strlit(s) +{ + var j = hole(), a; + op(O_JMP); + a = here; + while (*s) { emit(*s); s++; } + emit(0); + setlit(j, here); + lit(a); + return 0; +} + +// ---- input sources ---- +// frame: 0 kind (0 stdin, 1 token list, 2 string) 1 object 2 index 3 end +// 4 transparent 5..7 saved token 8..10 saved char state 11 owned +fn nextch() +{ + if (chkind == 0) { + if (qdone || eof()) { ch = -1; return 0; } + ch = getc(); + if (ch == 96) { qdone = 1; ch = -1; return 0; } // a backtick ends the source + } else { + ch = *chptr; + if (ch) chptr++; else ch = -1; + } + if (ch == 10) line++; + return 0; +} +fn src_push(kind, obj, trans, owned) +{ + var f; + isp++; + if (isp >= 32) die("sources nested too deeply"); + f = R_SRC + isp * 12; + f[0] = kind; f[1] = obj; f[2] = 0; f[3] = 0; f[4] = trans; f[11] = owned; + f[5] = tk; f[6] = tv; f[7] = tglue; f[8] = ch; f[9] = chkind; f[10] = chptr; + if (kind == 1) f[3] = obj[0]; + if (kind == 0) { chkind = 0; qdone = 0; nextch(); } + if (kind == 2) { chkind = 2; chptr = obj; nextch(); } + return 0; +} +fn src_pop() +{ + var f = R_SRC + isp * 12; + tk = f[5]; tv = f[6]; tglue = f[7]; ch = f[8]; chkind = f[9]; chptr = f[10]; + isp--; + if (f[11]) free(f[1]); + return 0; +} +fn isopch(c) +{ + var s = "+-*/%=<>!&|^~?:.@#$"; + while (*s) { if (*s == c) return 1; s++; } + return 0; +} +// is buf[0..n) followed by c the start of some operator the lexer knows? +fn opprefix(n, c) +{ + var i = 0, k, j; + while (i < nkop) { + k = kops[i]; + j = 0; + while (j < n && k[j] == tbuf[j]) j++; + if (j == n && k[n] == c) return 1; + i++; + } + return 0; +} +fn isdig(c) { return c >= '0' && c <= '9'; } +fn isal(c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'; } +fn hexv(c) +{ + if (isdig(c)) return c - '0'; + if (c >= 'a' && c <= 'f') return c - 'a' + 10; + if (c >= 'A' && c <= 'F') return c - 'A' + 10; + return -1; +} +fn escch() +{ + var c = ch; + nextch(); + if (c != 92) return c; + c = ch; + nextch(); + if (c == 'n') return 10; + if (c == 't') return 9; + if (c == 'r') return 13; + if (c == '0') return 0; + if (c == 'e') return 27; + return c; +} +fn oplex(n) +{ + while (ch >= 0 && isopch(ch) && opprefix(n, ch)) { tbuf[n] = ch; n++; nextch(); } + tbuf[n] = 0; + tk = T_OP; tv = intern(tbuf); + return 0; +} +fn lex() +{ + var n = 0, v, d; + tglue = 1; + while (1) { + while (ch == 32 || ch == 9 || ch == 10 || ch == 13) { nextch(); tglue = 0; } + if (ch != '/') break; + nextch(); + if (ch == '/') { while (ch >= 0 && ch != 10) nextch(); tglue = 0; continue; } + if (ch == '*') { + nextch(); + while (ch >= 0) { if (ch == '*') { nextch(); if (ch == '/') break; } else nextch(); } + nextch(); tglue = 0; continue; + } + tline = line; tbuf[0] = '/'; oplex(1); return 0; + } + tline = line; + if (ch < 0) { tk = T_EOF; tv = 0; return 0; } + if (isdig(ch)) { + v = 0; + if (ch == '0') { + nextch(); + if (ch == 'x' || ch == 'X') { + nextch(); + while ((d = hexv(ch)) >= 0) { v = v * 16 + d; nextch(); } + tk = T_NUM; tv = v; return 0; + } + } + while (isdig(ch)) { v = v * 10 + ch - '0'; nextch(); } + tk = T_NUM; tv = v; return 0; + } + if (isal(ch)) { + while (isal(ch) || isdig(ch)) { if (n < 1000) { tbuf[n] = ch; n++; } nextch(); } + tbuf[n] = 0; + tk = T_ID; tv = intern(tbuf); return 0; + } + if (ch == 39) { nextch(); tv = escch(); if (ch != 39) die("bad character literal"); nextch(); tk = T_NUM; return 0; } + if (ch == 34) { + nextch(); + while (ch >= 0 && ch != 34) { v = escch(); if (n < 1000) { tbuf[n] = v; n++; } } + nextch(); + tbuf[n] = 0; + tk = T_STR; tv = intern(tbuf); return 0; + } + tbuf[0] = ch; + nextch(); + if (isopch(tbuf[0])) return oplex(1); + tbuf[1] = 0; + tk = T_OP; tv = intern(tbuf); + return 0; +} +fn next() +{ + var f, q, i; + f = R_SRC + isp * 12; + if (f[0] != 1) { lex(); return 0; } + i = f[2]; + if (i < f[3]) { q = f[1]; tk = q[1 + i + i]; tv = q[2 + i + i]; tglue = 0; f[2] = i + 1; return 0; } + if (!f[4]) { tk = T_EOF; tv = 0; return 0; } + src_pop(); // a fed list ran out: continue with the token that followed it + return 0; +} +fn is_op(s) { return tk == T_OP && tv == s; } +fn is_id(s) { return tk == T_ID && tv == s; } +fn accept(s) { if ((tk == T_OP || tk == T_ID) && tv == s) { next(); return 1; } return 0; } +fn expect(s) { if (!accept(s)) { puts("zc: line "); putn(tline); puts(": expected "); puts(s); putc(10); halt(); } return 0; } +fn name() +{ + var n = tv; + if (tk != T_ID) die("expected a name"); + next(); + return n; +} + +// ---- operator tables ---- +// a sequence is written like asm: one-char mnemonics, numbers, and names +// (resolved when the operator is used, so image builds stay self-contained) +fn seqasm(s) +{ + var start = seqtop, n, neg, v, k, c; + while (1) { + while (*s == 32) s++; + if (!*s) break; + n = 0; + while (s[n] && s[n] != 32) { tbuf[n] = s[n]; n++; } + tbuf[n] = 0; + s = s + n; + neg = tbuf[0] == '-' && isdig(tbuf[1]); + if (isdig(tbuf[neg])) { + v = 0; k = neg; + while (tbuf[k]) { v = v * 10 + tbuf[k] - '0'; k++; } + if (neg) v = 0 - v; + R_SEQ[seqtop] = v + v + 1; seqtop++; + } else if (n == 1 && (c = mnemidx(tbuf[0])) >= 0) { + R_SEQ[seqtop] = c + c; seqtop++; + } else { + R_SEQ[seqtop] = SEQREF; R_SEQ[seqtop + 1] = intern(tbuf); seqtop = seqtop + 2; + } + } + return start; +} +fn mnemidx(c) { var i = 0; while (mnem[i]) { if (mnem[i] == c) return i; i++; } return -1; } +fn emitseq(a, n) +{ + var e = a + n, s; + while (a < e) { + if (R_SEQ[a] == SEQREF) { + s = lookup(R_SEQ[a + 1]); + if (s < 0) s = fwdfn(R_SEQ[a + 1]); + if (skind[s] == K_FN) fnref(s); else lit(sval[s]); + a = a + 2; + } else { emit(R_SEQ[a]); a++; } + } + return 0; +} +fn addkop(p) { var i = 0; while (i < nkop) { if (kops[i] == p) return 0; i++; } kops[nkop] = p; nkop++; return 0; } +fn binfind(p) { var i = nbin; while (i) { i--; if (bname[i] == p) return i; } return -1; } +fn unfind(p) { var i = nun; while (i) { i--; if (uname[i] == p) return i; } return -1; } +fn asgfind(p) { var i = nasg; while (i) { i--; if (aname[i] == p) return i; } return -1; } +// add or replace a binary operator. code is a sequence string, or 0 for the +// built-in && (prec 2) and || (prec 1) behaviour, chosen by -1 / -2 in prec. +fn add_binop(nm, prec, code) +{ + var p = intern(nm), i = binfind(p), a, n; + if (i < 0) { i = nbin; nbin++; } + bname[i] = p; bprec[i] = prec; + if (prec < 0) { bprec[i] = 0 - prec; blen[i] = prec; bseq[i] = 0; addkop(p); return i; } + bseq[i] = seqasm(code); blen[i] = seqtop - bseq[i]; + addkop(p); + // and its compound assignment, x op= y + n = strlen(nm); + wcopy(tbuf + 1000, nm, n); + tbuf[1000 + n] = '='; tbuf[1001 + n] = 0; + a = intern(tbuf + 1000); + if (prec >= 8 || prec == 5 || prec == 4 || prec == 3) { + if (asgfind(a) < 0) { aname[nasg] = a; nasg++; } + abin[asgfind(a)] = i; + addkop(a); + } + return i; +} +fn add_unop(nm, code) +{ + var p = intern(nm), i = unfind(p); + if (i < 0) { i = nun; nun++; } + uname[i] = p; useq[i] = seqasm(code); ulen[i] = seqtop - useq[i]; + addkop(p); + return i; +} +fn add_kw(nm, handler, flags) { var s = addsym(intern(nm), K_KW, handler); sflag[s] = F_DEF | flags; return s; } + +// ---- locals and the analyzer's view of them ---- +fn findloc(n) { var i = nloc; while (i > lbase) { i--; if (lname[i] == n) return i; } return -1; } +fn newloc(n) +{ + var i = nloc; + if (nloc >= NL) die("too many locals"); + nloc++; + lname[i] = n; lslot[i] = fslots; lhome[i] = fdepth; lstate[i] = S_PLAIN; + lowner[i] = 0; lused[i] = 0; larr[i] = 0; lown[i] = 0; + return i; +} +fn lroot(i) { while (lstate[i] == S_CHILD) i = lowner[i]; return i; } +fn tracked(i) { return lstate[i] != S_PLAIN; } +// a use that doesn't give the object away +fn touch(i) +{ + var r; + lused[i] = 1; + if (!fchk) return 0; + r = lroot(i); + lused[r] = 1; + if (lstate[r] == S_DEAD) { cerrn(i, "used after it was given away"); return 0; } + // a d object that shows up in a deeper block is given to that block + if (lstate[r] == S_D && lhome[r] < fdepth) { lhome[r] = fdepth; lstate[r] = S_M; lown[r] = 1; } + return 0; +} +fn plainuse(c) +{ + if (c == C_FRESH) cerr("a new object can't be used as a plain value; bind it to a name first"); + if (c >= C_VAR) touch(c - C_VAR); + return C_PLAIN; +} +fn freevar(i) { laddr(lslot[i]); op(O_LD); callname(s_free); op(O_DROP); return 0; } +// leaving every block deeper than or equal to dep: free what is owned, and +// complain about what should have left already. final means the block really +// ends here rather than an early exit. +fn exit_vars(dep, final) +{ + var i = nloc, st; + if (!fchk || !live) return 0; + while (i > lbase) { + i--; + if (lhome[i] < dep) continue; + st = lstate[i]; + if (st == S_M) freevar(i); else if (st == S_U) cerrn(i, "u object must leave this block (block value, assignment to the enclosing block, or return)"); + else if (st == S_D) cerrn(i, "d object was never given away"); + } + return 0; +} +// an m object has to be used somewhere in the block that owns it, on any path +fn unused_check(from) +{ + while (from < nloc) { if (fchk && lown[from] && !lused[from]) dien(lname[from], "m object is never used"); from++; } + return 0; +} +fn block_close(save) +{ + var i = lbase; + unused_check(save); + exit_vars(fdepth, 1); + while (i < save) { if (lhome[i] >= fdepth) { lstate[i] = S_DEAD; lhome[i] = fdepth - 1; } i++; } + nloc = save; + return 0; +} + +// snapshots: [lbase] [nloc] then state, owner, home per local +fn snap_push() +{ + var b = snaptop, i = lbase, p; + p = R_SNAP + b; + p[0] = lbase; p[1] = nloc; + p = p + 2; + while (i < nloc) { p[0] = lstate[i]; p[1] = lowner[i]; p[2] = lhome[i]; p = p + 3; i++; } + snaptop = p - R_SNAP; + if (snaptop >= 8000) die("nesting too deep for the analyzer"); + return b; +} +fn snap_pop(b) { snaptop = b; return 0; } +fn snap_restore(b) +{ + var p = R_SNAP + b, i = p[0], e = p[1]; + if (e > nloc) e = nloc; + p = p + 2; + while (i < e) { lstate[i] = p[0]; lowner[i] = p[1]; lhome[i] = p[2]; p = p + 3; i++; } + return 0; +} +fn empty(s) { return s == S_PLAIN || s == S_DEAD; } +// compare the current states with a snapshot: -1 if they agree, else the +// first local that differs. plain and dead agree and merge to dead. +fn snap_cmp(b) +{ + var p = R_SNAP + b, i = p[0], e = p[1], s; + if (e > nloc) e = nloc; + p = p + 2; + while (i < e) { + s = lstate[i]; + if (empty(s) && empty(p[0])) { if (p[0] == S_DEAD) lstate[i] = S_DEAD; } + else if (s != p[0] || lhome[i] != p[2] || (s == S_CHILD && lowner[i] != p[1])) return i; + p = p + 3; i++; + } + return -1; +} +fn statename(s) +{ + if (s == S_PLAIN) return "empty"; + if (s == S_M) return "m"; + if (s == S_U) return "u"; + if (s == S_D) return "d"; + if (s == S_DEAD) return "given away"; + if (s == S_CHILD) return "owned by a container"; + return "borrowed"; +} +fn merge_err(i, sa, what) +{ + if (!fchk) return 0; + puts("zc: line "); putn(tline); puts(": "); puts(lname[i]); puts(": "); puts(what); + puts(" (one path leaves it "); puts(statename(sa)); puts(", another "); puts(statename(lstate[i])); puts(")\n"); + halt(); + return 0; +} +// after an if: the current state is the else path, sa is the then path +fn merge(sa, livea) +{ + var i, p; + if (!livea) return 0; + if (!live) { snap_restore(sa); live = 1; return 0; } + i = snap_cmp(sa); + if (i >= 0) { p = R_SNAP + sa + 2 + 3 * (i - R_SNAP[sa]); merge_err(i, p[0], "ends the branches of an if in different states"); } + return 0; +} +fn loopcheck(s0) +{ + var i, p; + if (!fchk || !live) return 0; + i = snap_cmp(s0); + if (i >= 0) { p = R_SNAP + s0 + 2 + 3 * (i - R_SNAP[s0]); merge_err(i, p[0], "changes state inside a loop; each pass has to leave it as it found it"); } + return 0; +} + +// ---- analyzer rules for the places values flow ---- +// recast: x as u / x as d / x as m +fn recast(c, t) +{ + var i, st; + if (!fchk) return c; + if (c < C_VAR) { cerr("only a named owned object can be recast"); return c; } + i = c - C_VAR; + st = lstate[i]; + lused[i] = 1; + if (st == S_CHILD) cerrn(i, "belongs to a container; recast the container instead"); + else if (st == S_BORROW) cerrn(i, "is borrowed and can't be recast"); + else if (st == S_PLAIN) cerrn(i, "isn't an owned object"); + else if (st == S_DEAD) cerrn(i, "used after it was given away"); + else if (t == S_M && st != S_M) cerrn(i, "can't be recast back to m"); + else lstate[i] = t; + return c; +} +// an argument for a parameter tagged t (0 plain, 1 m, 2 d) +fn argcheck(c, t) +{ + var i, st; + if (!fchk) return 0; + if (c == C_FRESH) { if (t != 2) cerr("a new object can only go straight to a d parameter; bind it to a name first"); return 0; } + if (c < C_VAR || !tracked(c - C_VAR)) return 0; + i = c - C_VAR; + st = lstate[i]; + if (lstate[lroot(i)] == S_DEAD) { cerrn(i, "used after it was given away"); return 0; } + if (t == 0) { cerrn(i, "is an owned object; it can only be passed to an m or d parameter"); return 0; } + if (t == 1) { + if (st == S_D) { cerrn(i, "is d; pass it to a d parameter (lending it would lose it)"); return 0; } + touch(i); + return 0; + } + if (st == S_D) { lstate[i] = S_DEAD; lused[i] = 1; return 0; } + if (st == S_M) cerrn(i, "is m; write 'as d' to give it away"); + else if (st == S_U) cerrn(i, "is u; write 'as d' to give it away instead of returning it"); + else if (st == S_CHILD) cerrn(i, "belongs to a container and can't be given away alone"); + else cerrn(i, "is borrowed and can't be given away"); + return 0; +} +fn emitown(p) { laddr(lslot[p]); op(O_LD); callname(s_own); return 0; } +// a store of class rc into an lvalue of kind k (local or container index i) +fn assigncheck(k, i, rc) +{ + var st, j, r, js; + if (!fchk) return C_PLAIN; + j = -1; + if (rc >= C_VAR && tracked(rc - C_VAR)) j = rc - C_VAR; + if (j >= 0 && lstate[lroot(j)] == S_DEAD) { cerrn(j, "used after it was given away"); return C_PLAIN; } + if (k == LV_LOCAL) { + st = lstate[i]; + if (j < 0 && rc != C_FRESH) { + if (!empty(st)) cerrn(i, "still owns an object; overwriting it would leak it"); + else lstate[i] = S_PLAIN; + return C_PLAIN; + } + if (!empty(st)) { cerrn(i, "still owns an object; overwriting it would leak it"); return C_PLAIN; } + if (rc == C_FRESH) { lstate[i] = S_M; lused[i] = 0; lown[i] = 1; return C_PLAIN; } + js = lstate[j]; + if (js == S_U && lhome[i] == lhome[j] - 1) { // handing a u object up one block + lstate[i] = S_M; lused[i] = 0; lown[i] = 1; lstate[j] = S_DEAD; lused[j] = 1; + return C_PLAIN; + } + if (js == S_U) cerrn(j, "is u; it can only be handed to a variable of the enclosing block, or returned"); + else cerrn(j, "is an owned object and can't be copied into another variable"); + return C_PLAIN; + } + if (k == LV_ELEM) { + r = lroot(i); + st = lstate[r]; + touch(i); + if (rc == C_FRESH) { emitown(i); return C_PLAIN; } + if (j < 0) return C_PLAIN; + js = lstate[j]; + if (js == S_CHILD) cerrn(j, "already belongs to a container"); + else if (js == S_BORROW) cerrn(j, "is borrowed and can't be stored"); + else if (st == S_BORROW) { + if (js != S_D) cerrn(j, "must be d to be stored into a borrowed object"); + else { lstate[j] = S_DEAD; lused[j] = 1; emitown(i); } + } else if (js != st) cerrn(j, "its tag doesn't match its container's (both have to be m, u or d)"); + else if (lhome[j] < lhome[r]) cerrn(j, "lives in an outer block than the container it's stored into"); + else { lstate[j] = S_CHILD; lowner[j] = i; lused[j] = 1; emitown(i); } + return C_PLAIN; + } + // a global, or memory the analyzer doesn't track: the collector takes it + if (j >= 0) { + if (lstate[j] == S_D) { lstate[j] = S_DEAD; lused[j] = 1; } + else cerrn(j, "must be d to be stored in a global or in untracked memory (the collector takes it over)"); + } + return C_PLAIN; +} +fn retcheck(c) +{ + var i; + if (!fchk) return 0; + i = -1; + if (c >= C_VAR && tracked(c - C_VAR)) i = c - C_VAR; + if (i >= 0 && lstate[lroot(i)] == S_DEAD) { cerrn(i, "used after it was given away"); return 0; } + if (fret) { + if (i >= 0) { + if (lstate[i] == S_U) { lstate[i] = S_DEAD; lused[i] = 1; } + else cerrn(i, "has to be u to be returned; write 'return x as u'"); + } + return 0; + } + if (c == C_FRESH) cerr("returns a new object; declare the function with -> u"); + else if (i >= 0) cerrn(i, "is owned and can't be returned from a function without -> u"); + return 0; +} +// the last expression of a block expression +fn handup(c) +{ + var i; + if (!fchk) return C_PLAIN; + if (c == C_FRESH) return C_FRESH; + if (c < C_VAR || !tracked(c - C_VAR)) return C_PLAIN; + i = c - C_VAR; + if (lstate[i] == S_U && lhome[i] == fdepth) { lstate[i] = S_DEAD; lused[i] = 1; return C_FRESH; } + if (lstate[i] == S_U) cerrn(i, "only a u object of this block can be its value"); + else cerrn(i, "has to be u to be handed up as the block's value"); + return C_PLAIN; +} +fn discard(c) +{ + if (c == C_FRESH) cerr("a new object (or a u result) can't be thrown away"); + else if (c >= C_VAR) touch(c - C_VAR); + return 0; +} + +// ---- expressions ---- +fn rval() { if (lv) { op(O_LD); lv = 0; } return 0; } +fn kwcall(s, e) +{ + var f = sval[s], old = kw_expr, c; + kw_expr = e; xcls = C_PLAIN; + f(); + kw_expr = old; + c = xcls; + lv = 0; lvk = LV_NONE; + return c; +} +fn stmtonly() { if (kw_expr) die("this can't be used inside an expression"); return 0; } +fn call(s) +{ + var np = 0, tags = 0, ret = 0, c; + if (sflag[s] & F_SIG) { tags = ssig[s] >> 6; ret = ssig[s] & 1; } + else sflag[s] = sflag[s] | F_UNTYPED; + expect(s_lp); + if (!is_op(s_rp)) { + while (1) { + c = expr(); rval(); + argcheck(c, (tags >> (np + np)) & 3); + np++; + if (!accept(s_comma)) break; + } + } + expect(s_rp); + if ((sflag[s] & F_SIG) && np != ((ssig[s] >> 1) & 31)) dien(sname[s], "wrong number of arguments"); + fnref(s); op(O_CALL); + lv = 0; lvk = LV_NONE; + if (ret) return C_FRESH; + return C_PLAIN; +} +fn primary() +{ + var n, i, s, c; + lv = 0; lvk = LV_NONE; + if (tk == T_NUM) { lit(tv); next(); return C_PLAIN; } + if (tk == T_STR) { strlit(tv); next(); return C_PLAIN; } + if (tk == T_OP) { + if (tv == s_lp) { next(); c = expr(); expect(s_rp); return c; } + if (tv == s_lb) return blockexpr(); + die("expected an expression"); + } + if (tk != T_ID) die("expected an expression"); + n = tv; + i = findloc(n); + if (i >= 0) { + next(); + if (!fchk && tracked(i)) dien(n, "is tracked by the analyzer and can't be used in unchecked code"); + laddr(lslot[i]); + if (larr[i]) return C_PLAIN; + lv = 1; lvk = LV_LOCAL; lvi = i; + if (tracked(i)) return C_VAR + i; + return C_PLAIN; + } + s = lookup(n); + if (s >= 0 && skind[s] == K_KW) { + if (!(sflag[s] & F_EXPR)) dien(n, "can't be used inside an expression"); + return kwcall(s, 1); + } + next(); + if (s >= 0 && skind[s] == K_CONST) { lit(sval[s]); return C_PLAIN; } + if (s >= 0 && skind[s] == K_GVAR) { lit(sval[s]); lv = 1; lvk = LV_GLOBAL; return C_PLAIN; } + if (s >= 0 && skind[s] == K_GARR) { lit(sval[s]); return C_PLAIN; } + if (s < 0) s = fwdfn(n); + if (is_op(s_lp)) return call(s); + fnref(s); + return C_PLAIN; +} +// ++ and -- on the address at the top of the stack +fn incdec(post, d) +{ + if (!lv) die("++ and -- need something to assign to"); + if (lvk == LV_LOCAL && fchk && tracked(lvi)) cerrn(lvi, "owns an object and can't be incremented"); + lv = 0; lvk = LV_NONE; + lit(0); op(O_PICK); op(O_LD); + if (post) { lit(0); op(O_PICK); lit(d); op(O_ADD); lit(2); op(O_PICK); op(O_ST); } + else { lit(d); op(O_ADD); lit(0); op(O_PICK); lit(2); op(O_PICK); op(O_ST); } + op(O_SWAP); op(O_DROP); + return C_PLAIN; +} +fn postfix() +{ + var c = primary(), b, n, t; + while (1) { + if (tk == T_ID && tv == s_as) { + next(); + if (tk != T_ID) die("expected u, d or m after as"); + t = S_M; + if (tv == s_u) t = S_U; else if (tv == s_d) t = S_D; else if (tv != s_m) die("expected u, d or m after as"); + next(); + rval(); + c = recast(c, t); + lvk = LV_NONE; + continue; + } + if (tk != T_OP) return c; + if (tv == s_lk) { + next(); + rval(); + b = -1; + if (c == C_FRESH) cerr("index a new object only after binding it to a name"); + else if (c >= C_VAR) { touch(c - C_VAR); b = c - C_VAR; } + c = expr(); rval(); plainuse(c); + expect(s_rk); + op(O_ADD); + lv = 1; + if (b >= 0) { lvk = LV_ELEM; lvi = b; } else lvk = LV_OTHER; + c = C_PLAIN; + continue; + } + if (tv == s_lp) { // call through a value + rval(); plainuse(c); + next(); + n = 0; + if (!is_op(s_rp)) { while (1) { c = expr(); rval(); argcheck(c, 0); n++; if (!accept(s_comma)) break; } } + expect(s_rp); + lit(n); op(O_PICK); op(O_CALL); op(O_SWAP); op(O_DROP); + lv = 0; lvk = LV_NONE; c = C_PLAIN; + continue; + } + if (tv == s_inc) { next(); c = incdec(1, 1); continue; } + if (tv == s_dec) { next(); c = incdec(1, -1); continue; } + return c; + } + return c; +} +fn unary() +{ + var c, u; + if (tk == T_OP) { + if (tv == s_star) { next(); c = unary(); rval(); plainuse(c); lv = 1; lvk = LV_OTHER; return C_PLAIN; } + if (tv == s_amp) { + next(); c = unary(); + if (!lv) die("& needs something addressable"); + if (c >= C_VAR) touch(c - C_VAR); + lv = 0; lvk = LV_NONE; + return C_PLAIN; + } + if (tv == s_inc) { next(); unary(); return incdec(0, 1); } + if (tv == s_dec) { next(); unary(); return incdec(0, -1); } + u = unfind(tv); + if (u >= 0) { + next(); c = unary(); rval(); plainuse(c); + emitseq(useq[u], ulen[u]); + lv = 0; lvk = LV_NONE; + return C_PLAIN; + } + } + return postfix(); +} +fn bin(minp) +{ + var c = unary(), b, p, a, j; + while (tk == T_OP) { + b = binfind(tv); + if (b < 0) return c; + p = bprec[b]; + if (p < minp) return c; + next(); rval(); plainuse(c); + if (blen[b] == -1) { // && : 0 if the left side is 0, else the right side as 0 or 1 + a = hole(); op(O_JZ); + c = bin(p + 1); rval(); plainuse(c); + emitseq(useq[1], ulen[1]); // !! normalizes + j = hole(); op(O_JMP); + setlit(a, here); lit(0); setlit(j, here); + } else if (blen[b] == -2) { // || : 1 if the left side isn't 0, else the right side as 0 or 1 + a = hole(); op(O_JZ); + lit(1); j = hole(); op(O_JMP); + setlit(a, here); + c = bin(p + 1); rval(); plainuse(c); + emitseq(useq[1], ulen[1]); + setlit(j, here); + } else { + c = bin(p + 1); rval(); plainuse(c); + emitseq(bseq[b], blen[b]); + } + lv = 0; lvk = LV_NONE; c = C_PLAIN; + } + return c; +} +// [addr v] -> [v], storing v at addr +fn storekeep() { op(O_SWAP); lit(1); op(O_PICK); op(O_SWAP); op(O_ST); return 0; } +fn expr() +{ + var c = bin(1), k, i, a, rc; + if (tk != T_OP) return c; + if (tv == s_eq) { + if (!lv) die("this can't be assigned to"); + k = lvk; i = lvi; + next(); + rc = expr(); rval(); + storekeep(); + lv = 0; lvk = LV_NONE; + return assigncheck(k, i, rc); + } + a = asgfind(tv); + if (a >= 0) { + if (!lv) die("this can't be assigned to"); + if (lvk == LV_LOCAL && fchk && tracked(lvi)) cerrn(lvi, "owns an object; compound assignment would lose it"); + next(); + lit(0); op(O_PICK); op(O_LD); + lv = 0; + rc = expr(); rval(); plainuse(rc); + emitseq(bseq[abin[a]], blen[abin[a]]); + storekeep(); + lv = 0; lvk = LV_NONE; + return C_PLAIN; + } + return c; +} +// does the current token start a statement rather than an expression? +fn stmtstart() +{ + var s; + if (tk == T_OP) return tv == s_semi || tv == s_lb; + if (tk != T_ID || findloc(tv) >= 0) return 0; + s = lookup(tv); + return s >= 0 && skind[s] == K_KW && !(sflag[s] & F_EXPR); +} +// { stmts; tail } as an expression: its value is the tail, or 0 +fn blockexpr() +{ + var save = nloc, c = C_PLAIN, tail = 0; + next(); + fdepth++; bxdepth++; + while (!is_op(s_rb)) { + if (tk == T_EOF) die("missing }"); + if (stmtstart()) { stmt(); continue; } + c = expr(); rval(); + if (accept(s_semi)) { op(O_DROP); discard(c); c = C_PLAIN; continue; } + if (!is_op(s_rb)) die("expected ; or } after an expression"); + tail = 1; + } + next(); + if (tail) c = handup(c); else lit(0); + block_close(save); + fdepth--; bxdepth--; + lv = 0; lvk = LV_NONE; + return c; +} + +// ---- constant expressions ---- +fn cprim() +{ + var v, s; + if (tk == T_NUM) { v = tv; next(); return v; } + if (accept(s_minus)) return 0 - cprim(); + if (tk == T_OP && tv == uname[2]) { next(); return ~cprim(); } + if (accept(s_lp)) { v = cexpr(); expect(s_rp); return v; } + if (tk == T_ID) { + s = lookup(tv); + if (s >= 0 && (skind[s] == K_CONST || skind[s] == K_GARR || (skind[s] == K_FN && (sflag[s] & F_DEF)) || skind[s] == K_GVAR)) { next(); return sval[s]; } + } + die("expected a constant"); + return 0; +} +fn cexpr() +{ + var v = cprim(), o; + while (tk == T_OP) { + o = tv; + if (o == bname[3]) { next(); v = v + cprim(); } + else if (o == bname[4]) { next(); v = v - cprim(); } + else if (o == bname[0]) { next(); v = v * cprim(); } + else if (o == bname[15]) { next(); v = v | cprim(); } + else if (o == bname[13]) { next(); v = v & cprim(); } + else if (o == bname[5]) { next(); v = v << cprim(); } + else if (o == bname[6]) { next(); v = v >> cprim(); } + else return v; + } + return v; +} + +// ---- statements ---- +fn stmt() +{ + var s, c; + if (tk == T_ID && findloc(tv) < 0) { + s = lookup(tv); + if (s >= 0 && skind[s] == K_KW && (sflag[s] & F_STMT)) { kwcall(s, 0); return 0; } + } + if (tk == T_OP) { + if (tv == s_semi) { next(); return 0; } + if (tv == s_lb) { block(); return 0; } + } + c = expr(); rval(); op(O_DROP); + expect(s_semi); + discard(c); + return 0; +} +fn block() +{ + var save = nloc; + expect(s_lb); + fdepth++; + while (!is_op(s_rb)) { if (tk == T_EOF) die("missing }"); stmt(); } + next(); + block_close(save); + fdepth--; + return 0; +} +// the body of if/while/for: its own block, even without braces +fn substmt() +{ + var save = nloc; + if (is_op(s_lb)) return block(); + fdepth++; + stmt(); + block_close(save); + fdepth--; + return 0; +} +fn kw_var() +{ + var n, i, c, k; + stmtonly(); + next(); + while (1) { + n = name(); + i = newloc(n); + if (accept(s_lk)) { k = cexpr(); expect(s_rk); larr[i] = 1; fslots = fslots + k; } + else fslots++; + if (accept(s_eq)) { c = expr(); rval(); laddr(lslot[i]); op(O_ST); assigncheck(LV_LOCAL, i, c); } + if (!accept(s_comma)) break; + } + expect(s_semi); + return 0; +} +fn kw_if() +{ + var a, b, c, s0, sa, live0, livea; + stmtonly(); + next(); + expect(s_lp); c = expr(); rval(); plainuse(c); expect(s_rp); + a = hole(); op(O_JZ); + live0 = live; + s0 = snap_push(); + substmt(); + livea = live; sa = snap_push(); + if (is_id(s_else)) { + next(); + b = hole(); op(O_JMP); + setlit(a, here); + snap_restore(s0); live = live0; + substmt(); + setlit(b, here); + } else { setlit(a, here); snap_restore(s0); live = live0; } + merge(sa, livea); + snap_pop(s0); + return 0; +} +// loop frame: 0 continue target 1 break chain 2 depth 3 snapshot 4 block-expression depth +fn loop_push(cont, s0) +{ + var f = R_LOOP + lsp * 6; + if (lsp >= 60) die("loops nested too deeply"); + f[0] = cont; f[1] = 0; f[2] = fdepth; f[3] = s0; f[4] = bxdepth; + lsp++; + return 0; +} +fn loop_pop() { lsp--; fixall(R_LOOP[lsp * 6 + 1], here); return 0; } +fn kw_while() +{ + var c0 = here, a, s0, c; + stmtonly(); + next(); + s0 = snap_push(); + expect(s_lp); c = expr(); rval(); plainuse(c); expect(s_rp); + a = hole(); op(O_JZ); + loop_push(c0, s0); + substmt(); + loopcheck(s0); + jmp(c0); + setlit(a, here); + loop_pop(); + snap_restore(s0); live = 1; + snap_pop(s0); + return 0; +} +fn kw_for() +{ + var save = nloc, s0, c0, a, b, d, c; + stmtonly(); + next(); + expect(s_lp); + fdepth++; + if (is_id(s_var)) kw_var(); + else { if (!is_op(s_semi)) { c = expr(); rval(); op(O_DROP); discard(c); } expect(s_semi); } + s0 = snap_push(); + c0 = here; + if (!is_op(s_semi)) { c = expr(); rval(); plainuse(c); } else lit(1); + expect(s_semi); + a = hole(); op(O_JZ); + b = hole(); op(O_JMP); + d = here; + if (!is_op(s_rp)) { c = expr(); rval(); op(O_DROP); discard(c); } + expect(s_rp); + jmp(c0); + setlit(b, here); + loop_push(d, s0); + substmt(); + loopcheck(s0); + jmp(d); + setlit(a, here); + loop_pop(); + snap_restore(s0); live = 1; + snap_pop(s0); + block_close(save); + fdepth--; + return 0; +} +fn loopexit(f) +{ + if (lsp <= loopbase) die("break or continue outside a loop"); + if (f[4] != bxdepth) die("break and continue can't leave a block expression"); + loopcheck(f[3]); + exit_vars(f[2] + 1, 0); + return 0; +} +fn kw_break() +{ + var f = R_LOOP + (lsp - 1) * 6; + stmtonly(); + next(); expect(s_semi); + loopexit(f); + f[1] = fixnew(here, f[1]); + lit(MW - 1); op(O_JMP); + live = 0; + return 0; +} +fn kw_continue() +{ + var f = R_LOOP + (lsp - 1) * 6; + stmtonly(); + next(); expect(s_semi); + loopexit(f); + jmp(f[0]); + live = 0; + return 0; +} +fn kw_return() +{ + var c; + stmtonly(); + next(); + if (!infn) die("return outside a function"); + if (bxdepth) die("return can't leave a block expression"); + if (is_op(s_semi)) { lit(0); c = C_PLAIN; } else { c = expr(); rval(); } + expect(s_semi); + retcheck(c); + exit_vars(1, 0); + epichain = fixnew(here, epichain); + lit(MW - 1); op(O_JMP); + live = 0; + return 0; +} +fn kw_unchecked() +{ + var old = fchk; + stmtonly(); + next(); + fchk = 0; + substmt(); + fchk = old; + return 0; +} + +// ---- asm blocks: raw vm words, one per token ---- +// numbers push, -n pushes a negative number, #n emits a raw word, one-char +// tokens from the mnemonic table are opcodes, names push the address of a +// variable or function, :name defines a label for this block. +fn asmblock() +{ + var nl = 0, nr = 0, i, k, c, s, ln = R_ASM, la = R_ASM + 64, rn = R_ASM + 128, ra = R_ASM + 384; + expect(s_lb); + while (!is_op(s_rb)) { + if (tk == T_EOF) die("unterminated asm block"); + if (tk == T_NUM) { lit(tv); next(); continue; } + if (tk == T_STR) { strlit(tv); next(); continue; } + if (is_op(s_hash)) { next(); if (tk != T_NUM) die("# needs a number"); emit(tv); next(); continue; } + if (is_op(s_minus)) { next(); if (tk == T_NUM && tglue) { lit(0 - tv); next(); } else op(O_SUB); continue; } + if (is_op(s_colon)) { next(); if (nl >= 64) die("too many asm labels"); ln[nl] = name(); la[nl] = here; nl++; continue; } + if (tv[0] && !tv[1] && (c = mnemidx(tv[0])) >= 0) { op(c); next(); continue; } + if (tk != T_ID) die("bad token in asm"); + i = findloc(tv); + if (i >= 0) { lused[i] = 1; laddr(lslot[i]); next(); continue; } + k = -1; i = 0; + while (i < nl) { if (ln[i] == tv) k = i; i++; } + if (k >= 0) { lit(la[k]); next(); continue; } + s = lookup(tv); + if (s >= 0 && skind[s] != K_FN && skind[s] != K_KW) { lit(sval[s]); next(); continue; } + if (s >= 0 && skind[s] == K_FN) { fnref(s); next(); continue; } + if (nr >= 250) die("too many asm label references"); + rn[nr] = tv; ra[nr] = hole(); nr++; next(); // a label further down this block + } + next(); + i = 0; + while (i < nr) { + k = -1; c = 0; + while (c < nl) { if (ln[c] == rn[i]) k = c; c++; } + if (k < 0) dien(rn[i], "unknown name in asm"); + setlit(ra[i], la[k]); + i++; + } + return 0; +} +fn kw_asm() { next(); asmblock(); xcls = C_PLAIN; return 0; } + +// ---- quote: code as data ---- +// a token list is an object: [n] then kind, value for each token. names, +// strings and operators are interned pointers. +fn grab() -> u +{ + var n = 0, depth = 0, q; + if (!is_op(s_lb)) die("expected {"); + next(); + while (1) { + if (tk == T_EOF) die("unterminated quote"); + if (is_op(s_lb)) depth++; + if (is_op(s_rb)) { if (!depth) break; depth--; } + if (n >= 4000) die("quote too long"); + R_QBUF[n + n] = tk; R_QBUF[n + n + 1] = tv; n++; + next(); + } + next(); + q = new(n + n + 1); + q[0] = n; + wcopy(q + 1, R_QBUF, n + n); + return q as u; +} +unchecked fn qnew(src) -> u { var n = src[0] * 2 + 1, q = new(n); wcopy(q, src, n); return q; } +fn qdup(m q) -> u { var n = q[0] * 2 + 1, r = new(n); wcopy(r, q, n); return r as u; } +fn wcopyto(m q, n) { var i = 0; while (i < n) { emit(q[i]); i++; } return 0; } +fn kw_quote() +{ + var q, j, a, n; + next(); + q = grab(); + j = hole(); op(O_JMP); + a = here; + n = q[0] * 2 + 1; + wcopyto(q, n); + free(q as d); + setlit(j, here); + lit(a); + callname(s_qnew); + xcls = C_FRESH; + return 0; +} +// tokens of a string, as a token list +fn tokens(s) -> u +{ + var n = 0, q; + src_push(2, s, 0, 0); + next(); + while (tk != T_EOF) { + if (n >= 4000) die("too many tokens"); + R_QBUF[n + n] = tk; R_QBUF[n + n + 1] = tv; n++; + next(); + } + src_pop(); + q = new(n + n + 1); + q[0] = n; + wcopy(q + 1, R_QBUF, n + n); + return q as u; +} +// feed a token list into the input: the parser reads it next, then carries +// on where it was. the list is freed once it has been read. +unchecked fn feed(d q) { src_push(1, q, 1, 1); next(); return 0; } + +// ---- nested compiles ---- +// compile() and load() can run while another function is being compiled (from +// a parse word). the function context is saved, the new code is emitted in +// place with a jump around it, and then everything is put back. +fn ctx_push() +{ + var f = R_CTX + ctxsp * 24; + if (ctxsp >= 30) die("compiles nested too deeply"); + ctxsp++; + f[0] = nloc; f[1] = lbase; f[2] = fdepth; f[3] = fslots; f[4] = fchk; f[5] = fret; f[6] = live; + f[7] = bxdepth; f[8] = epichain; f[9] = fframe; f[10] = infn; f[11] = lsp; f[12] = loopbase; + f[13] = lv; f[14] = lvk; f[15] = lvi; f[16] = kw_expr; f[17] = xcls; f[18] = snaptop; + return 0; +} +fn ctx_pop() +{ + var f; + ctxsp--; + f = R_CTX + ctxsp * 24; + nloc = f[0]; lbase = f[1]; fdepth = f[2]; fslots = f[3]; fchk = f[4]; fret = f[5]; live = f[6]; + bxdepth = f[7]; epichain = f[8]; fframe = f[9]; infn = f[10]; lsp = f[11]; loopbase = f[12]; + lv = f[13]; lvk = f[14]; lvi = f[15]; kw_expr = f[16]; xcls = f[17]; snaptop = f[18]; + return 0; +} + +// ---- functions and the top level ---- +fn fn_begin(chk, ret) +{ + lbase = nloc; fdepth = 1; fslots = 0; fchk = chk && check_on; fret = ret; + live = 1; bxdepth = 0; epichain = 0; infn = 1; loopbase = lsp; lv = 0; lvk = LV_NONE; + return 0; +} +fn prologue() { lit(ZFP); op(O_LD); fframe = hole(); op(O_SUB); lit(ZFP); op(O_ST); return 0; } +fn epilogue() +{ + lit(0); + fixall(epichain, here); + lit(ZFP); op(O_LD); lit(fslots); op(O_ADD); lit(ZFP); op(O_ST); op(O_RET); + setlit(fframe, fslots); + infn = 0; + nloc = lbase; + return 0; +} +fn body() +{ + expect(s_lb); + while (!is_op(s_rb)) { if (tk == T_EOF) die("missing } at the end of a function"); stmt(); } + next(); + unused_check(lbase); + if (fchk && live) { exit_vars(1, 1); if (fret) cerr("a -> u function has to end with a return"); } + return 0; +} +fn fndef(chk) +{ + var n, s, np = 0, tags = 0, t, ret = 0, sig, i, pn[24], pt[24]; + next(); + n = name(); + s = lookup(n); + if (s < 0 || s < ubase || skind[s] != K_FN || (sflag[s] & F_DEF)) { s = addsym(n, K_FN, 0); sflag[s] = 0; } + expect(s_lp); + if (!is_op(s_rp)) { + while (1) { + t = 0; + if (tk == T_ID && (tv == s_m || tv == s_d)) { + t = 1; if (tv == s_d) t = 2; + pn[np] = tv; + next(); + if (tk == T_ID) pn[np] = name(); else t = 0; // a parameter called m or d + } else pn[np] = name(); + if (np >= 12 && t) die("only the first 12 parameters can be tagged"); + pt[np] = t; + if (np < 12) tags = tags + (t << (np + np)); + np++; + if (np >= 24) die("too many parameters"); + if (!accept(s_comma)) break; + } + } + expect(s_rp); + if (accept(s_arrow)) { if (!is_id(s_u)) die("the only return tag is -> u"); next(); ret = 1; } + sig = ret + np + np + tags * 64; + if (sflag[s] & F_SIG) { if (ssig[s] != sig) dien(n, "doesn't match its earlier declaration"); } + else if ((sflag[s] & F_UNTYPED) && (tags || ret)) dien(n, "was called before its tags were declared; put a prototype above the first call"); + ssig[s] = sig; + sflag[s] = sflag[s] | F_SIG; + if (accept(s_semi)) return s; + if (sflag[s] & F_DEF) dien(n, "is defined twice"); + sval[s] = here; + sflag[s] = sflag[s] | F_DEF; + fixall(sfix[s], here); sfix[s] = 0; + fn_begin(chk, ret); + prologue(); + i = 0; + while (i < np) { + t = newloc(pn[i]); fslots++; + if (fchk && pt[i] == 1) lstate[t] = S_BORROW; + if (fchk && pt[i] == 2) { lstate[t] = S_M; lown[t] = 1; } + i++; + } + i = np; + while (i) { i--; laddr(i); op(O_ST); } + body(); + epilogue(); + return s; +} +fn gvar() +{ + var n, a, k; + next(); + while (1) { + n = name(); + if (accept(s_lk)) { + k = cexpr(); expect(s_rk); + addsym(n, K_GARR, here); + while (k) { emit(0); k--; } + } else { + a = here; + addsym(n, K_GVAR, a); + emit(0); + if (accept(s_eq)) { + if (tk == T_STR) { *(a + hdelta) = here; k = tv; while (*k) { emit(*k); k++; } emit(0); next(); } + else *(a + hdelta) = cexpr(); + } + } + if (!accept(s_comma)) break; + } + expect(s_semi); + return 0; +} +fn gconst() +{ + var n; + next(); + while (1) { n = name(); expect(s_eq); addsym(n, K_CONST, cexpr()); if (!accept(s_comma)) break; } + expect(s_semi); + return 0; +} +fn topitem() +{ + var chk = 1, imm = 0, s, k; + if (is_id(s_unchecked)) { + next(); + if (accept(s_semi)) { check_on = 0; return 0; } // unchecked; switches the analyzer off from here on + chk = 0; + } + if (is_id(s_immediate)) { next(); imm = 1; } + if (is_id(s_fn)) { + s = fndef(chk); + if (imm) { + if (hdelta) die("parse words can't be defined while building an image"); + k = addsym(sname[s], K_KW, sval[s]); + sflag[k] = F_DEF | F_STMT | F_EXPR | F_TOP; + } + return 0; + } + if (!chk || imm) die("expected fn"); + if (is_id(s_var)) return gvar(); + if (is_id(s_const)) return gconst(); + if (tk == T_ID) { + s = lookup(tv); + if (s >= 0 && skind[s] == K_KW && (sflag[s] & F_TOP)) { kwcall(s, 0); return 0; } + } + die("expected fn, var or const"); + return 0; +} +fn unit_top() { while (tk != T_EOF) topitem(); return 0; } +fn unit_end(from) +{ + var i = from; + while (i < nsym) { + if (skind[i] == K_FN && !(sflag[i] & F_DEF) && sfix[i]) dien(sname[i], "is used but never defined"); + i++; + } + return 0; +} + +// compile a token list as the body of a new function, or (top) as top-level +// declarations. returns the entry address of the function. +unchecked fn compile_list(m q, top) +{ + var hop = -1, entry = 0, from = nsym; + ctx_push(); + if (infn) { hop = hole(); op(O_JMP); } + src_push(1, q, 0, 0); + next(); + if (top) unit_top(); + else { + entry = here; + fn_begin(1, 0); + prologue(); + while (tk != T_EOF) stmt(); + unused_check(lbase); + if (fchk && live) exit_vars(1, 1); + epilogue(); + } + src_pop(); + unit_end(from); + if (hop >= 0) setlit(hop, here); + if (here > ptop) ptop = here; + ctx_pop(); + return entry; +} +fn compile(m q) { return compile_list(q, 0); } +fn load(m q) { compile_list(q, 1); return 0; } +fn eval(m q) { var f = compile_list(q, 0); return f(); } +fn evals(s) { var q = tokens(s), r = eval(q); free(q as d); return r; } + +// ---- symbol blob ---- +// an image ends with its own symbol table so the genome knows its names: +// kind, value, signature, name chars, 0 ... then a 0 kind. +fn blob_emit(from) +{ + var i = from, p; + while (i < nsym) { + if (skind[i] >= K_FN && skind[i] <= K_CONST) { + emit(skind[i]); emit(sval[i]); emit(ssig[i]); + p = sname[i]; + while (*p) { emit(*p); p++; } + emit(0); + } + i++; + } + emit(0); + return 0; +} +fn blob_load(p) +{ + var s, n; + while (*p) { + n = intern(p + 3); + s = addsym(n, p[0], p[1]); + ssig[s] = p[2]; + sflag[s] = F_DEF | F_SIG; + p = p + 3 + strlen(p + 3) + 1; + } + return 0; +} +// compile the rest of stdin as a genome image that will run at org, writing +// it to dst. only the source's own names are visible while it compiles. +// returns the image length in words. +fn build_image(org, dst) +{ + var sh = here, sd = hdelta, sl = hlimit, sub = ubase, svl = vislo, sns = nsym, n; + ubase = nsym; vislo = nsym; + here = org; hdelta = dst - org; hlimit = org + 0x30000; + lit(FTOP); lit(ZFP); op(O_ST); fnref(fwdfn(s_start)); op(O_JMP); emit(0); emit(0); + src_push(0, 0, 0, 0); + next(); + unit_top(); + unit_end(ubase); + src_pop(); + *(dst + 5) = here; + blob_emit(ubase); + *(dst + 6) = here; + n = here - org; + popsyms(sns); + ubase = sub; vislo = svl; here = sh; hdelta = sd; hlimit = sl; + return n; +} + +// ---- setup ---- +fn gc_roots() +{ + markrange(0, ptop); + if (!hdelta && here > ptop) markrange(ptop, here); // globals of a unit still compiling + markrange(GORG, GORG[6]); + markrange(R_SRC, R_SRC + 400); + markrange(*ZFP, FTOP); + markrange(DSLO, DSHI); + return 0; +} +fn zc_init() +{ + pooltop = R_POOL; + wfill(R_IH, 0, IHN); + nsym = 0; nfixu = 0; fixfree = 0; nbin = 0; nun = 0; nasg = 0; nkop = 0; seqtop = 0; + lsp = 0; ctxsp = 0; snaptop = 0; nloc = 0; lbase = 0; + s_lp = intern("("); s_rp = intern(")"); s_lb = intern("{"); s_rb = intern("}"); + s_lk = intern("["); s_rk = intern("]"); s_comma = intern(","); s_semi = intern(";"); + s_eq = intern("="); s_arrow = intern("->"); s_inc = intern("++"); s_dec = intern("--"); + s_star = intern("*"); s_amp = intern("&"); s_colon = intern(":"); s_hash = intern("#"); + s_minus = intern("-"); s_andand = intern("&&"); s_oror = intern("||"); + s_fn = intern("fn"); s_var = intern("var"); s_const = intern("const"); s_else = intern("else"); + s_as = intern("as"); s_unchecked = intern("unchecked"); s_immediate = intern("immediate"); + s_m = intern("m"); s_d = intern("d"); s_u = intern("u"); + s_start = intern("start"); s_main = intern("main"); s_free = intern("free"); + s_own = intern("own"); s_qnew = intern("qnew"); + return 0; +} +// operators and keywords. these are plain tables in memory: a program can +// add operators, change precedences or replace keywords with its own. +fn zc_tables() +{ + add_unop("!", "0 $ 0 ~ - | 31 > 1 +"); // index 0 + add_unop("!!", "0 $ 0 ~ - | 31 > 0 ~ -"); // index 1: normalize to 0 or 1 + add_unop("~", "-1 ^"); // index 2 + add_unop("-", "0 ~ -"); + add_binop("*", 10, "*"); // the first few indexes are used by cexpr + add_binop("/", 10, "zdiv c"); + add_binop("%", 10, "zmod c"); + add_binop("+", 9, "+"); // 3 + add_binop("-", 9, "-"); // 4 + add_binop("<<", 8, "zshl c"); // 5 + add_binop(">>", 8, ">"); // 6 + add_binop("<", 7, "<"); + add_binop(">", 7, "~ <"); + add_binop("<=", 7, "~ < 1 ^"); + add_binop(">=", 7, "< 1 ^"); + add_binop("==", 6, "- 0 $ 0 ~ - | 31 > 1 +"); + add_binop("!=", 6, "- 0 $ 0 ~ - | 31 > 0 ~ -"); + add_binop("&", 5, "&"); // 13 + add_binop("^", 4, "^"); + add_binop("|", 3, "|"); // 15 + add_binop("&&", -1, 0); + add_binop("||", -2, 0); + addkop(s_eq); addkop(s_arrow); addkop(s_inc); addkop(s_dec); + add_kw("var", kw_var, F_STMT); + add_kw("if", kw_if, F_STMT); + add_kw("while", kw_while, F_STMT); + add_kw("for", kw_for, F_STMT); + add_kw("break", kw_break, F_STMT); + add_kw("continue", kw_continue, F_STMT); + add_kw("return", kw_return, F_STMT); + add_kw("unchecked", kw_unchecked, F_STMT); + add_kw("asm", kw_asm, F_STMT | F_EXPR); + add_kw("quote", kw_quote, F_EXPR); + return 0; +} +fn start() +{ + var s, f; + heap_init(); + zc_init(); + blob_load(GORG[5]); + zc_tables(); + here = 0; hdelta = 0; hlimit = HEAP_LO; ptop = 0; + ubase = nsym; vislo = 0; + src_push(0, 0, 0, 0); + next(); + unit_top(); + unit_end(ubase); + if (here > ptop) ptop = here; + s = lookup(s_main); + if (s >= 0 && skind[s] == K_FN && (sflag[s] & F_DEF)) { f = sval[s]; f(); } + halt(); + return 0; +} diff --git a/docs/history/10-zc/code/src/rt.zc b/docs/history/10-zc/code/src/rt.zc new file mode 100644 index 0000000..139924b --- /dev/null +++ b/docs/history/10-zc/code/src/rt.zc @@ -0,0 +1,168 @@ +// ============================================================================ +// rt.zc: the runtime half of the genome. memory map, i/o, arithmetic helpers, +// the heap (size-class free lists) and the mark-sweep collector. +// ============================================================================ + +// ---- memory map (words) ---- +const MW = 0x100000; // 1m words of memory +const HEAP_LO = 0x40000; // heap: 0x40000 .. 0xA0000 +const HEAP_HI = 0xA0000; +const ZFP = 0xA0000; // frame pointer word, used by every prologue +const GORG = 0xC0000; // the genome (this code) lives here +const FBOT = 0xE0000; // frame stack: FBOT .. FTOP, grows down +const FTOP = 0xF0000; +const RS0 = 0xF0000; // return stack grows up from here +const MARK0 = 0xF8000; // mark stack for the collector +const MARKN = 0x4000; +const DSLO = 0xFC000; // data stack: DSLO .. MW-16, grows down +const DSHI = 0xFFFF0; +const MAGIC = 0x2B7E1516; + +// ---- i/o ---- +fn putc(ch) { asm { ch @ . } return 0; } +fn getc() { return asm { , }; } +fn eof() { return asm { ? }; } +fn halt() { asm { q } return 0; } +fn puts(m s) { var i = 0; while (s[i]) { putc(s[i]); i++; } return 0; } +fn putn(n) +{ + var b[12], i = 0; + if (n < 0) { putc('-'); n = 0 - n; } + if (n < 0) { puts("2147483648"); return 0; } + while (1) { b[i++] = '0' + n % 10; n = n / 10; if (!n) break; } + while (i) putc(b[--i]); + return 0; +} +fn strlen(m s) { var n = 0; while (s[n]) n++; return n; } +fn streq(m a, m b) { var i = 0; while (a[i] && a[i] == b[i]) i++; return a[i] == b[i]; } +fn wcopy(m dst, m src, n) { var i = 0; while (i < n) { dst[i] = src[i]; i++; } return 0; } +fn wfill(m dst, v, n) { var i = 0; while (i < n) { dst[i] = v; i++; } return 0; } + +// ---- arithmetic the hardware doesn't have ---- +var pow2[32]; +var zrem; +fn zudiv(n, d) +{ + var q = 0, r = 0, i = 30; + if (n < d) { zrem = n; return 0; } + while (i >= 0) { + r = r + r + ((n >> i) & 1); + q = q + q; + if (r >= d) { r = r - d; q++; } + i--; + } + zrem = r; + return q; +} +fn zdiv(a, b) +{ + var s = 0, q; + if (b == 0) { zrem = a; return 0; } + if (a < 0) { a = 0 - a; s = 1; } + if (b < 0) { b = 0 - b; s = s ^ 1; } + q = zudiv(a, b); + if (s) return 0 - q; + return q; +} +fn zmod(a, b) { var neg = a < 0; zdiv(a, b); if (neg) return 0 - zrem; return zrem; } +fn zshl(a, b) { return a * pow2[b & 31]; } + +// ---- heap ---- +// every block is a power of two words with a 5 word header before the +// pointer the program sees: +// p[-5] block size p[-4] size class p[-3] flags (1 live, 2 marked) +// p[-2] p ^ MAGIC p[-1] owner (the container that owns it, or 0) +// freed blocks keep size and class so the sweep can walk the heap linearly, +// and go on a free list per class (the link is in p[0]). +var hbump, freel[32], gc_runs, gc_freed, gc_auto = 1; + +fn heap_init() +{ + var i = 1; + pow2[0] = 1; + while (i < 32) { pow2[i] = pow2[i - 1] + pow2[i - 1]; i++; } + hbump = HEAP_LO; + wfill(freel, 0, 32); + return 0; +} +fn isobj(m x) { return x >= HEAP_LO + 5 && x < hbump && x[-2] == (x ^ MAGIC) && (x[-3] & 1); } + +fn oom() { puts("zc: out of heap memory\n"); halt(); return 0; } +unchecked fn new(n) -> u +{ + var need = n + 5, k = 3, sz = 8, b = 0, p; + if (n < 0) need = 5; + while (sz < need) { sz = sz + sz; k++; } + if (freel[k]) { b = freel[k]; freel[k] = b[5]; } + else if (hbump + sz <= HEAP_HI) { b = hbump; hbump = hbump + sz; } + else { + if (gc_auto) gc(); + if (freel[k]) { b = freel[k]; freel[k] = b[5]; } + else if (hbump + sz <= HEAP_HI) { b = hbump; hbump = hbump + sz; } + else oom(); + } + p = b + 5; + b[0] = sz; b[1] = k; b[2] = 1; b[3] = p ^ MAGIC; b[4] = 0; + wfill(p, 0, sz - 5); + return p; +} +fn osize(m p) { if (!isobj(p)) return 0; return p[-5] - 5; } + +// frees p and, recursively, every object p owns +fn release(p) +{ + var b = p - 5, n = b[0] - 5, i = 0, w; + b[2] = 0; + while (i < n) { w = p[i]; if (isobj(w) && w[-1] == p) release(w); i++; } + b[4] = 0; + b[5] = freel[b[1]]; + freel[b[1]] = b; + return 0; +} +unchecked fn free(d p) { if (isobj(p)) release(p); return 0; } +fn own(c, p) { if (isobj(c)) c[-1] = p; return c; } + +// ---- mark-sweep ---- +// conservative and non-moving. roots: the program area (globals live +// between functions), the genome's globals, the compiler's input stack, +// the live part of the frame stack and the whole data stack region. +var ms_top, ms_over, gc_lo, gc_hi; +fn mark(w) +{ + if (!isobj(w) || (w[-3] & 2)) return 0; + w[-3] = w[-3] | 2; + if (ms_top < MARKN) { MARK0[ms_top] = w; ms_top++; } else ms_over = 1; + return 0; +} +fn markrange(a, b) { while (a < b) { mark(*a); a++; } return 0; } +fn drain() +{ + var p; + while (ms_top) { ms_top--; p = MARK0[ms_top]; markrange(p, p + p[-5] - 5); } + return 0; +} +fn gc_roots(); +unchecked fn gc() +{ + var b, sz, n = 0; + ms_top = 0; ms_over = 0; + gc_roots(); + drain(); + while (ms_over) { // the mark stack overflowed: rescan marked objects + ms_over = 0; + b = HEAP_LO; + while (b < hbump) { if (b[2] == 3) markrange(b + 5, b + b[0]); b = b + b[0]; drain(); } + } + b = HEAP_LO; + while (b < hbump) { + sz = b[0]; + if (b[2] == 1) { b[2] = 0; b[4] = 0; b[5] = freel[b[1]]; freel[b[1]] = b; n++; } + else if (b[2] == 3) b[2] = 1; + b = b + sz; + } + gc_runs++; + gc_freed = gc_freed + n; + return n; +} +// number of live objects, for tests +fn live_objects() { var b = HEAP_LO, n = 0; while (b < hbump) { if (b[2] & 1) n++; b = b + b[0]; } return n; } diff --git a/docs/history/10-zc/code/test.sh b/docs/history/10-zc/code/test.sh new file mode 100755 index 0000000..09b92c3 --- /dev/null +++ b/docs/history/10-zc/code/test.sh @@ -0,0 +1,48 @@ +#!/bin/bash +# full check: bootstraps the compiler from zc0, verifies the fixpoint, then runs +# the examples and tests on the portable interpreter (host) and on the arm64 +# jit under qemu. needs gcc; the jit half needs aarch64-linux-gnu-gcc and +# qemu-aarch64 (it's skipped if they're missing). +cd "$(dirname "$0")"; T=$(mktemp -d); trap 'rm -rf $T' EXIT +ok=0; bad=0 +chk(){ if [ "$1" = "$2" ]; then echo "ok $3"; ok=$((ok+1)); else echo "FAIL $3: got [$1]"; bad=$((bad+1)); fi; } +src(){ cat src/rt.zc src/compiler.zc; } +selfc(){ (cat tools/build.zc; printf '`'; src) | $1 > $2; } + +# bootstrap: zc0 (c) -> g0, g0 compiles compiler.zc -> g1, g1 compiles it -> g2 +gcc -O1 -w zc0.c -o $T/zc0 && gcc -O1 -w mkgenome.c -o $T/mkg || exit 1 +mkdir $T/s0 $T/s1 +cp core.c io_linux.c $T/s0/; cp core.c io_linux.c $T/s1/ +src | $T/zc0 > $T/s0/genome.h || exit 1 +gcc -O2 -w -DJTYPE=int $T/s0/core.c $T/s0/io_linux.c -o $T/zy0 +selfc $T/zy0 $T/g1.bin; $T/mkg < $T/g1.bin > $T/s1/genome.h +gcc -O2 -w -DJTYPE=int $T/s1/core.c $T/s1/io_linux.c -o $T/zy1 +selfc $T/zy1 $T/g2.bin +cmp -s $T/g1.bin $T/g2.bin && chk 1 1 "fixpoint: the compiler compiles itself to the same $(($(stat -c %s $T/g1.bin) / 4)) words" || chk 0 1 "fixpoint" +cmp -s $T/s1/genome.h genome.h && chk 1 1 "genome.h is up to date" || chk 0 1 "genome.h is stale (cp the bootstrap output over it)" + +run(){ # $1 = vm command, $2 = label + local vm="$1" l="$2" + chk "$($vm < examples/hello.zc)" "hello, world!" "$l hello" + chk "$($vm < examples/fact.zc | tr '\n' '/')" "1101110101111100000000/3628800/" "$l factorial" + chk "$( (cat examples/echo.zc; printf '`abc\n') | $vm)" "abc" "$l echo after the backtick" + chk "$($vm < examples/selfpatch.zc)" "ABC" "$l self-patching code" + chk "$($vm < examples/lists.zc | tr '\n' '/')" "[0 1 4 9 16 25]/live: 7/after the block: 0/" "$l ownership frees everything" + chk "$( (cat examples/repl.zc; printf '`fn sq(x) { return x * x; }`putn(sq(7)); putc(10);`puts("hi\\n");') | $vm | tr '\n' '/')" "49/hi/" "$l repl on the resident compiler" + chk "$($vm < tests/basics.zc | tr '\n' '/')" "285 610 42 512/hey 65 -3 -2 1024 -8/12456/757/9 100-101/5/ok/" "$l language basics" + chk "$($vm < tests/umd.zc)" "100 6 0 15 99 5 5" "$l u m d lifetimes" + chk "$($vm < tests/gc.zc)" "1000 1 900 1 199900" "$l mark-sweep collector" + chk "$($vm < tests/meta.zc | tr '\n' '/')" "1024 19/unless ok/rrr/15 42/7/42/7/144/" "$l parse words, quote, eval, load, operator tables" + chk "$($vm < tests/misc.zc | tr '\n' '/')" "45/54321/ok/" "$l compile-time eval, patched mnemonics, unchecked" + local r; r=$(tests/runerr.sh "$vm" | tail -1) + chk "$r" "analyzer rejects: $(ls tests/errors/*.zc | wc -l) passed, 0 failed" "$l analyzer rejects every bad program" +} +gcc -O2 -w -DJTYPE=int core.c io_linux.c -o $T/zy +run $T/zy "interp" +if command -v aarch64-linux-gnu-gcc >/dev/null && command -v qemu-aarch64 >/dev/null; then + aarch64-linux-gnu-gcc -O2 -w -DJTYPE=int -static core.c io_linux.c -o $T/zyj || exit 1 + printf '#!/bin/sh\nexec qemu-aarch64 %s "$@"\n' $T/zyj > $T/jit; chmod +x $T/jit + run $T/jit "jit" + selfc $T/jit $T/gj.bin; cmp -s $T/g1.bin $T/gj.bin && chk 1 1 "jit self-compile matches" || chk 0 1 "jit self-compile matches" +else echo "skip jit (no aarch64 toolchain or qemu)"; fi +echo "$ok passed, $bad failed"; [ $bad = 0 ] diff --git a/docs/history/10-zc/code/tests/basics.zc b/docs/history/10-zc/code/tests/basics.zc new file mode 100644 index 0000000..9dfb44e --- /dev/null +++ b/docs/history/10-zc/code/tests/basics.zc @@ -0,0 +1,22 @@ +var table[10]; +var greeting = "hey"; +const N = 10, BIG = 0x100 * 2; +fn fib(n) { if (n < 2) return n; return fib(n - 1) + fib(n - 2); } +fn twice(f, x) { return f(f(x)); } +fn inc(x) { return x + 1; } +fn main() +{ + var i, s = 0, a[5]; + for (i = 0; i < N; i++) table[i] = i * i; + for (var j = 0; j < N; j++) s += table[j]; + putn(s); putc(' '); putn(fib(15)); putc(' '); putn(twice(inc, 40)); putc(' '); putn(BIG); putc(10); + puts(greeting); putc(' '); putn('A'); putc(' '); putn(-17 / 5); putc(' '); putn(-17 % 5); putc(' '); putn(1 << 10); putc(' '); putn(-64 >> 3); putc(10); + i = 0; while (1) { i++; if (i == 3) continue; if (i > 6) break; putn(i); } putc(10); + a[0] = 5; a[1] = a[0]++; a[2] = ++a[0]; putn(a[0]); putn(a[1]); putn(a[2]); putc(10); + var b = { var t = 3; t * t }; + putn(b); putc(' '); putn(3 > 2 && 2 > 1); putn(0 || 0); putn(!5); putn(~0); putn(5 != 5); putn(4 >= 4); putc(10); + var x = asm { 2 3 + }; + putn(x); putc(10); + asm { 'o' . 'k' . 10 . } + return 0; +} diff --git a/docs/history/10-zc/code/tests/errors/back_to_m.zc b/docs/history/10-zc/code/tests/errors/back_to_m.zc new file mode 100644 index 0000000..ee25096 --- /dev/null +++ b/docs/history/10-zc/code/tests/errors/back_to_m.zc @@ -0,0 +1,2 @@ +// expect: x: can't be recast back to m +fn main() { var x = new(3); x as d; x as m; return 0; } diff --git a/docs/history/10-zc/code/tests/errors/borrow_escape.zc b/docs/history/10-zc/code/tests/errors/borrow_escape.zc new file mode 100644 index 0000000..725bc61 --- /dev/null +++ b/docs/history/10-zc/code/tests/errors/borrow_escape.zc @@ -0,0 +1,3 @@ +// expect: p: is owned and can't be returned +fn f(m p) { return p; } +fn main() { var x = new(1); f(x); return 0; } diff --git a/docs/history/10-zc/code/tests/errors/branch_mismatch.zc b/docs/history/10-zc/code/tests/errors/branch_mismatch.zc new file mode 100644 index 0000000..62cb18b --- /dev/null +++ b/docs/history/10-zc/code/tests/errors/branch_mismatch.zc @@ -0,0 +1,3 @@ +// expect: x: ends the branches of an if in different states +fn take(d p) { return p[0]; } +fn main() { var x = new(1); x as d; if (x[0]) take(x); return 0; } diff --git a/docs/history/10-zc/code/tests/errors/child_dies.zc b/docs/history/10-zc/code/tests/errors/child_dies.zc new file mode 100644 index 0000000..16e1935 --- /dev/null +++ b/docs/history/10-zc/code/tests/errors/child_dies.zc @@ -0,0 +1,3 @@ +// expect: c: used after it was given away +fn take(d p) { return p[0]; } +fn main() { var p = new(2), c = new(1); p[0] = c; take(p as d); return c[0]; } diff --git a/docs/history/10-zc/code/tests/errors/child_recast.zc b/docs/history/10-zc/code/tests/errors/child_recast.zc new file mode 100644 index 0000000..f4be23e --- /dev/null +++ b/docs/history/10-zc/code/tests/errors/child_recast.zc @@ -0,0 +1,2 @@ +// expect: c: belongs to a container; recast the container instead +fn main() { var p = new(2), c = new(1); p[0] = c; c as d; return 0; } diff --git a/docs/history/10-zc/code/tests/errors/copy_owned.zc b/docs/history/10-zc/code/tests/errors/copy_owned.zc new file mode 100644 index 0000000..42d2ab0 --- /dev/null +++ b/docs/history/10-zc/code/tests/errors/copy_owned.zc @@ -0,0 +1,2 @@ +// expect: x: is an owned object and can't be copied +fn main() { var x = new(1); var y = x; return y[0]; } diff --git a/docs/history/10-zc/code/tests/errors/d_into_block.zc b/docs/history/10-zc/code/tests/errors/d_into_block.zc new file mode 100644 index 0000000..02189ab --- /dev/null +++ b/docs/history/10-zc/code/tests/errors/d_into_block.zc @@ -0,0 +1,2 @@ +// expect: x: used after it was given away +fn main() { var x = new(1); x as d; { x[0] = 5; } return x[0]; } diff --git a/docs/history/10-zc/code/tests/errors/d_kept.zc b/docs/history/10-zc/code/tests/errors/d_kept.zc new file mode 100644 index 0000000..28eaccf --- /dev/null +++ b/docs/history/10-zc/code/tests/errors/d_kept.zc @@ -0,0 +1,2 @@ +// expect: x: d object was never given away +fn main() { var x = new(3); x as d; x[0] = 1; return 0; } diff --git a/docs/history/10-zc/code/tests/errors/discard_u.zc b/docs/history/10-zc/code/tests/errors/discard_u.zc new file mode 100644 index 0000000..18bc810 --- /dev/null +++ b/docs/history/10-zc/code/tests/errors/discard_u.zc @@ -0,0 +1,3 @@ +// expect: can't be thrown away +fn mk() -> u { var p = new(1); return p as u; } +fn main() { mk(); return 0; } diff --git a/docs/history/10-zc/code/tests/errors/give_m.zc b/docs/history/10-zc/code/tests/errors/give_m.zc new file mode 100644 index 0000000..537fa0c --- /dev/null +++ b/docs/history/10-zc/code/tests/errors/give_m.zc @@ -0,0 +1,3 @@ +// expect: x: is m; write 'as d' +fn take(d p) { return p[0]; } +fn main() { var x = new(3); take(x); return 0; } diff --git a/docs/history/10-zc/code/tests/errors/global_store.zc b/docs/history/10-zc/code/tests/errors/global_store.zc new file mode 100644 index 0000000..13882b9 --- /dev/null +++ b/docs/history/10-zc/code/tests/errors/global_store.zc @@ -0,0 +1,3 @@ +// expect: x: must be d to be stored in a global +var g; +fn main() { var x = new(1); g = x; return 0; } diff --git a/docs/history/10-zc/code/tests/errors/leak_overwrite.zc b/docs/history/10-zc/code/tests/errors/leak_overwrite.zc new file mode 100644 index 0000000..42773fb --- /dev/null +++ b/docs/history/10-zc/code/tests/errors/leak_overwrite.zc @@ -0,0 +1,2 @@ +// expect: x: still owns an object +fn main() { var x = new(1); x = new(2); return x[0]; } diff --git a/docs/history/10-zc/code/tests/errors/lend_d.zc b/docs/history/10-zc/code/tests/errors/lend_d.zc new file mode 100644 index 0000000..e1e37d5 --- /dev/null +++ b/docs/history/10-zc/code/tests/errors/lend_d.zc @@ -0,0 +1,4 @@ +// expect: x: is d; pass it to a d parameter +fn look(m p) { return p[0]; } +fn take(d p) { return p[0]; } +fn main() { var x = new(3); x as d; look(x); take(x); return 0; } diff --git a/docs/history/10-zc/code/tests/errors/loop_move.zc b/docs/history/10-zc/code/tests/errors/loop_move.zc new file mode 100644 index 0000000..b1a6019 --- /dev/null +++ b/docs/history/10-zc/code/tests/errors/loop_move.zc @@ -0,0 +1,3 @@ +// expect: x: changes state inside a loop +fn take(d p) { return p[0]; } +fn main() { var x = new(1); var i = 0; x as d; while (i < 3) { take(x); i++; } return 0; } diff --git a/docs/history/10-zc/code/tests/errors/plain_return.zc b/docs/history/10-zc/code/tests/errors/plain_return.zc new file mode 100644 index 0000000..61f546f --- /dev/null +++ b/docs/history/10-zc/code/tests/errors/plain_return.zc @@ -0,0 +1,3 @@ +// expect: returns a new object; declare the function with -> u +fn mk() { return new(1); } +fn main() { return mk(); } diff --git a/docs/history/10-zc/code/tests/errors/proto_needed.zc b/docs/history/10-zc/code/tests/errors/proto_needed.zc new file mode 100644 index 0000000..2613b9d --- /dev/null +++ b/docs/history/10-zc/code/tests/errors/proto_needed.zc @@ -0,0 +1,3 @@ +// expect: f: was called before its tags were declared +fn main() { var x = new(1); f(1); return x[0]; } +fn f(d p) { return 0; } diff --git a/docs/history/10-zc/code/tests/errors/return_m.zc b/docs/history/10-zc/code/tests/errors/return_m.zc new file mode 100644 index 0000000..c361760 --- /dev/null +++ b/docs/history/10-zc/code/tests/errors/return_m.zc @@ -0,0 +1,3 @@ +// expect: x: has to be u to be returned +fn mk() -> u { var x = new(1); return x; } +fn main() { var y = mk(); return y[0]; } diff --git a/docs/history/10-zc/code/tests/errors/tag_mismatch.zc b/docs/history/10-zc/code/tests/errors/tag_mismatch.zc new file mode 100644 index 0000000..f9631d6 --- /dev/null +++ b/docs/history/10-zc/code/tests/errors/tag_mismatch.zc @@ -0,0 +1,2 @@ +// expect: c: its tag doesn't match its container's +fn main() { var p = new(2), c = new(1); p as u; p[0] = c; return 0; } diff --git a/docs/history/10-zc/code/tests/errors/u_dropped.zc b/docs/history/10-zc/code/tests/errors/u_dropped.zc new file mode 100644 index 0000000..76b7765 --- /dev/null +++ b/docs/history/10-zc/code/tests/errors/u_dropped.zc @@ -0,0 +1,2 @@ +// expect: x: u object must leave this block +fn main() { var x = new(3); x as u; return 0; } diff --git a/docs/history/10-zc/code/tests/errors/u_skip_level.zc b/docs/history/10-zc/code/tests/errors/u_skip_level.zc new file mode 100644 index 0000000..b8a024f --- /dev/null +++ b/docs/history/10-zc/code/tests/errors/u_skip_level.zc @@ -0,0 +1,2 @@ +// expect: t: is u; it can only be handed to a variable of the enclosing block +fn main() { var r; { { var t = new(1); t as u; r = t; } } return r[0]; } diff --git a/docs/history/10-zc/code/tests/errors/unchecked_touch.zc b/docs/history/10-zc/code/tests/errors/unchecked_touch.zc new file mode 100644 index 0000000..1f57466 --- /dev/null +++ b/docs/history/10-zc/code/tests/errors/unchecked_touch.zc @@ -0,0 +1,2 @@ +// expect: x: is tracked by the analyzer and can't be used in unchecked code +fn main() { var x = new(1); unchecked { x[0] = 1; } return 0; } diff --git a/docs/history/10-zc/code/tests/errors/untagged_param.zc b/docs/history/10-zc/code/tests/errors/untagged_param.zc new file mode 100644 index 0000000..5a044c6 --- /dev/null +++ b/docs/history/10-zc/code/tests/errors/untagged_param.zc @@ -0,0 +1,3 @@ +// expect: x: is an owned object; it can only be passed to an m or d parameter +fn f(p) { return 0; } +fn main() { var x = new(1); f(x); return 0; } diff --git a/docs/history/10-zc/code/tests/errors/unused.zc b/docs/history/10-zc/code/tests/errors/unused.zc new file mode 100644 index 0000000..cbc6129 --- /dev/null +++ b/docs/history/10-zc/code/tests/errors/unused.zc @@ -0,0 +1,2 @@ +// expect: x: m object is never used +fn main() { var x = new(3); return 0; } diff --git a/docs/history/10-zc/code/tests/errors/use_after_give.zc b/docs/history/10-zc/code/tests/errors/use_after_give.zc new file mode 100644 index 0000000..5225462 --- /dev/null +++ b/docs/history/10-zc/code/tests/errors/use_after_give.zc @@ -0,0 +1,3 @@ +// expect: x: used after it was given away +fn take(d p) { return p[0]; } +fn main() { var x = new(3); take(x as d); return x[0]; } diff --git a/docs/history/10-zc/code/tests/gc.zc b/docs/history/10-zc/code/tests/gc.zc new file mode 100644 index 0000000..e5ff7f7 --- /dev/null +++ b/docs/history/10-zc/code/tests/gc.zc @@ -0,0 +1,20 @@ +var keep; +unchecked fn churn(n) +{ + var i = 0, p; + while (i < n) { p = new(10); p[0] = i; if (i % 100 == 0) keep = p; i++; } + return 0; +} +fn main() +{ + var base = live_objects(), f; + churn(1000); + putn(live_objects() - base); putc(' '); + f = gc(); + putn(f >= 990); putc(' '); // conservative: a stale word or two may keep one alive + putn(keep[0]); putc(' '); // the one a global still points at survives + churn(200000); // 3.2m words through a 393k word heap: needs the automatic collector + putn(gc_runs > 1); putc(' '); + putn(keep[0]); putc(10); + return 0; +} diff --git a/docs/history/10-zc/code/tests/meta.zc b/docs/history/10-zc/code/tests/meta.zc new file mode 100644 index 0000000..9c81616 --- /dev/null +++ b/docs/history/10-zc/code/tests/meta.zc @@ -0,0 +1,46 @@ +fn ipow(a, b) { var r = 1; while (b > 0) { r *= a; b--; } return r; } +// a parse word that adds an operator to the compiler's tables +immediate fn pow_op() { next(); add_binop("**", 11, "ipow c"); return 0; } +pow_op + +// unless (cond) stmt: a parse word that emits code itself +immediate fn unless() +{ + var a; + next(); + expect(intern("(")); rval(); plainuse(expr()); rval(); expect(intern(")")); + emitseq(useq[0], ulen[0]); // not + a = hole(); op(O_JZ); // skip the body when the condition was true + substmt(); + setlit(a, here); + return 0; +} +// repeat N { body }: a macro, built from token lists +immediate fn repeat() +{ + var n, body, i = 0; + next(); n = tv; next(); + body = grab(); + while (i < n) { feed(qdup(body)); i++; } + free(body as d); + return 0; +} +fn main() +{ + putn(2 ** 10); putc(' '); putn(3 + 2 ** 3 * 2); putc(10); + unless (1 > 2) puts("unless ok\n"); + unless (2 > 1) puts("wrong\n"); + repeat 3 { putc('r'); } + putc(10); + var q = quote { putn(40 + 2); putc(10); return 7; }; + putn(q[0]); putc(' '); + putn(eval(q)); putc(10); + var f = compile(q); + putn(f()); putc(10); + free(q as d); + var d = quote { fn sq(x) { return x * x; } }; + load(d); + free(d as d); + putn(evals("return sq(12);")); putc(10); + return 0; +} diff --git a/docs/history/10-zc/code/tests/misc.zc b/docs/history/10-zc/code/tests/misc.zc new file mode 100644 index 0000000..4a8ceef --- /dev/null +++ b/docs/history/10-zc/code/tests/misc.zc @@ -0,0 +1,30 @@ +// ct { ... } runs a block while compiling and leaves its result as a constant +immediate fn ct() +{ + var q, v; + next(); + q = grab(); + v = eval(q); + free(q as d); + lit(v); + return 0; +} +// teach the asm syntax that p means out, like the old repl demo +immediate fn teach() { next(); mnem[12] = 'p'; return 0; } +teach + +fn countdown() +{ + asm { 5 :top 0 $ '0' + p 1 - 0 $ done z top j :done _ 10 p } + return 0; +} +fn main() +{ + putn(ct { var i = 0, s = 0; while (i < 10) { s += i; i++; } return s; }); putc(10); + countdown(); + puts(lazy()); putc(10); + return 0; +} +// the analyzer is off from here on +unchecked; +fn lazy() { var p = new(3); p[0] = 'o'; p[1] = 'k'; return p; } diff --git a/docs/history/10-zc/code/tests/runerr.sh b/docs/history/10-zc/code/tests/runerr.sh new file mode 100755 index 0000000..c1d42c5 --- /dev/null +++ b/docs/history/10-zc/code/tests/runerr.sh @@ -0,0 +1,9 @@ +#!/bin/bash +# runs every tests/errors/*.zc and checks the compiler rejects it with the expected message +vm=${1:-zy}; pass=0; fail=0 +for f in "$(dirname "$0")"/errors/*.zc; do + want=$(head -1 "$f" | sed 's#^// expect: ##') + got=$($vm < "$f" 2>&1 | head -1) + if [[ "$got" == *"$want"* ]]; then pass=$((pass+1)); else fail=$((fail+1)); echo "FAIL $(basename $f): got [$got]"; fi +done +echo "analyzer rejects: $pass passed, $fail failed" diff --git a/docs/history/10-zc/code/tests/umd.zc b/docs/history/10-zc/code/tests/umd.zc new file mode 100644 index 0000000..7d1bffc --- /dev/null +++ b/docs/history/10-zc/code/tests/umd.zc @@ -0,0 +1,52 @@ +// lists as [len, items...], items owned by the list +fn mklist(n) -> u +{ + var l = new(n + 1); + l[0] = 0; + return l as u; +} +fn push(m l, d item) { l[l[0] + 1] = item as d; l[0] = l[0] + 1; return 0; } +fn sum(m l) { var s = 0, i = 1; while (i <= l[0]) { s += l[i][0]; i++; } return s; } +fn box(v) -> u { var b = new(1); b[0] = v; return b as u; } +fn consume(d b) { return b[0]; } + +fn main() +{ + var base = live_objects(); + { + var l = mklist(8); // u from mklist arrives here as m + var i = 0; + while (i < 5) { + var b = box(i * 10); // m in the loop body + b as d; // give it down + push(l, b); + i++; + } + putn(sum(l)); putc(' '); + putn(live_objects() - base); putc(' '); // list + 5 boxes + } + putn(live_objects() - base); putc(' '); // freed at the block's end, children too + + // u handed up one block by assignment, and as a block value + var outer; + { + var t = box(7); + t as u; + outer = t; + } + var v = { var w = box(8); w as u }; + putn(outer[0] + v[0]); putc(' '); + + // d given away to a callee + var g = box(99); + putn(consume(g as d)); putc(' '); + + // containers: child takes its parent's tag + var p = new(2), c = new(1); + c[0] = 5; + p[0] = c; + p[1] = new(3); + putn(p[0][0]); putc(' '); + putn(live_objects() - base); putc(10); + return 0; +} diff --git a/docs/history/10-zc/code/tools/build.zc b/docs/history/10-zc/code/tools/build.zc new file mode 100644 index 0000000..afa1626 --- /dev/null +++ b/docs/history/10-zc/code/tools/build.zc @@ -0,0 +1,13 @@ +// compiles the rest of stdin (after the backtick) as a genome image at GORG +// and writes it to stdout as little-endian 32-bit words. +const OUT = 0x10000; +fn main() +{ + var n = build_image(GORG, OUT), i = 0, w; + while (i < n) { + w = OUT[i]; + putc(w); putc(w >> 8); putc(w >> 16); putc(w >> 24); + i++; + } + return 0; +} diff --git a/docs/history/10-zc/code/zc0.c b/docs/history/10-zc/code/zc0.c new file mode 100644 index 0000000..23df463 --- /dev/null +++ b/docs/history/10-zc/code/zc0.c @@ -0,0 +1,439 @@ +/* zc0: stage 0 of the bootstrap. a small c compiler for the subset of zc that + compiler.zc is written in. it reads zc source on stdin and writes a genome + header (vm words placed at GORG) on stdout, in the same image layout the + resident compiler's image mode produces. no lifetime analysis, no quote, no + parse words, no block expressions: those only exist in compiler.zc. tags + (m, d, -> u) and casts (as u, as d) are parsed and recorded in signatures + but not checked. */ +#include +#include +#include +#include + +#define GORG 0xC0000 +#define ZFP 0xA0000 /* frame pointer word */ +#define FTOP 0xF0000 /* frame stack grows down from here */ +#define MAXW 0x40000 + +enum { T_EOF, T_NUM, T_ID, T_STR, T_OP }; +enum { K_FN = 1, K_GVAR, K_GARR, K_CONST }; +enum { O_Q, O_LD, O_ST, O_PICK, O_DROP, O_SWAP, O_CALL, O_JMP, O_JZ, O_RET, + O_IN, O_EMPTY, O_OUT, O_ADD, O_SUB, O_MUL, O_AND, O_OR, O_XOR, O_SHR, O_LT }; +static const char *mn = "q@!$_~cjzr,?.+-*&|^><"; + +static char *src; static int pos, line = 1; +static int tk, tv, glue; static char ts[4096]; /* current token */ + +static int img[MAXW], here = GORG; +static void emit(int w) { if (here - GORG >= MAXW) { fprintf(stderr, "image too big\n"); exit(1); } img[here++ - GORG] = w; } +static void lit(int v) { emit((int)((unsigned)v * 2 + 1)); } +static void op(int k) { emit(k * 2); } +static void setlit(int at, int v) { img[at - GORG] = (int)((unsigned)v * 2 + 1); } + +static void die(const char *m) { fprintf(stderr, "zc0: line %d: %s (at '%s')\n", line, m, ts); exit(1); } + +/* ---- lexer ---- */ +static const char *ops3[] = { "<<=", ">>=", 0 }; +static const char *ops2[] = { "==", "!=", "<=", ">=", "<<", ">>", "&&", "||", "++", "--", "+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "->", 0 }; +static int esc(void) +{ + int c = src[pos++]; + if (c != '\\') return c; + c = src[pos++]; + switch (c) { case 'n': return 10; case 't': return 9; case 'r': return 13; case '0': return 0; case 'e': return 27; } + return c; +} +static void next(void) +{ + int ws = 0, i; + for (;;) { + while (src[pos] && isspace((unsigned char)src[pos])) { if (src[pos] == '\n') line++; pos++; ws = 1; } + if (src[pos] == '/' && src[pos + 1] == '/') { while (src[pos] && src[pos] != '\n') pos++; ws = 1; continue; } + if (src[pos] == '/' && src[pos + 1] == '*') { pos += 2; while (src[pos] && !(src[pos] == '*' && src[pos + 1] == '/')) { if (src[pos] == '\n') line++; pos++; } if (src[pos]) pos += 2; ws = 1; continue; } + break; + } + glue = !ws; + if (!src[pos] || src[pos] == '`') { tk = T_EOF; strcpy(ts, ""); return; } + if (isdigit((unsigned char)src[pos])) { + char *e; tv = (int)strtoul(src + pos, &e, 0); memcpy(ts, src + pos, e - src - pos); ts[e - src - pos] = 0; pos = e - src; tk = T_NUM; return; + } + if (isalpha((unsigned char)src[pos]) || src[pos] == '_') { + i = 0; while (isalnum((unsigned char)src[pos]) || src[pos] == '_') ts[i++] = src[pos++]; + ts[i] = 0; tk = T_ID; return; + } + if (src[pos] == '\'') { pos++; tv = esc(); if (src[pos++] != '\'') die("bad char literal"); tk = T_NUM; strcpy(ts, "'c'"); return; } + if (src[pos] == '"') { pos++; i = 0; while (src[pos] && src[pos] != '"') ts[i++] = (char)esc(); ts[i] = 0; pos++; tk = T_STR; return; } + tk = T_OP; + for (i = 0; ops3[i]; i++) if (!strncmp(src + pos, ops3[i], 3)) { strcpy(ts, ops3[i]); pos += 3; return; } + for (i = 0; ops2[i]; i++) if (!strncmp(src + pos, ops2[i], 2)) { strcpy(ts, ops2[i]); pos += 2; return; } + ts[0] = src[pos++]; ts[1] = 0; +} +static int is(const char *s) { return (tk == T_OP || tk == T_ID) && !strcmp(ts, s); } +static int accept(const char *s) { if (is(s)) { next(); return 1; } return 0; } +static void expect(const char *s) { if (!accept(s)) { char b[64]; sprintf(b, "expected '%s'", s); die(b); } } +static char *ident(void) { static char b[8][256]; static int k; char *r = b[k++ & 7]; if (tk != T_ID) die("expected a name"); strcpy(r, ts); next(); return r; } + +/* ---- symbols ---- */ +typedef struct { char name[64]; int kind, val, sig, defd; } Sym; +static Sym sy[4096]; static int nsy; +typedef struct { int at, sym; } Fix; +static Fix fx[65536]; static int nfx; +static Sym *find(const char *n) { int i; for (i = nsy; i--;) if (!strcmp(sy[i].name, n)) return &sy[i]; return 0; } +static Sym *add(const char *n, int kind, int val) +{ + Sym *s = find(n); + if (s && s->kind == K_FN && kind == K_FN && !s->defd) { s->val = val; return s; } /* prototype or forward use */ + s = &sy[nsy++]; strncpy(s->name, n, 63); s->kind = kind; s->val = val; s->defd = 1; s->sig = 0; return s; +} +static void fnref(const char *n) /* push a function address, fixing it up later if needed */ +{ + Sym *s = find(n); + if (!s) { s = add(n, K_FN, 0); s->defd = 0; } + if (s->kind != K_FN) die("not a function"); + if (s->defd) { lit(s->val); return; } + fx[nfx].at = here; fx[nfx++].sym = (int)(s - sy); lit(0); +} + +/* ---- locals ---- */ +typedef struct { char name[64]; int slot, arr; } Loc; +static Loc lo[1024]; static int nlo, nslot, infn; +static Loc *findl(const char *n) { int i; for (i = nlo; i--;) if (!strcmp(lo[i].name, n)) return &lo[i]; return 0; } +static void laddr(int k) { lit(ZFP); op(O_LD); if (k) { lit(k); op(O_ADD); } } + +/* ---- operator tables ---- */ +static void seq_isz(void) { lit(0); op(O_PICK); lit(0); op(O_SWAP); op(O_SUB); op(O_OR); lit(31); op(O_SHR); lit(1); op(O_ADD); } +static void seq_nz(void) { lit(0); op(O_PICK); lit(0); op(O_SWAP); op(O_SUB); op(O_OR); lit(31); op(O_SHR); lit(0); op(O_SWAP); op(O_SUB); } +static int binprec(void) +{ + static const char *n[] = { "*", "/", "%", "+", "-", "<<", ">>", "<", ">", "<=", ">=", "==", "!=", "&", "^", "|", "&&", "||", 0 }; + static int p[] = { 10, 10, 10, 9, 9, 8, 8, 7, 7, 7, 7, 6, 6, 5, 4, 3, 2, 1 }; + int i; + if (tk != T_OP) return 0; + for (i = 0; n[i]; i++) if (!strcmp(ts, n[i])) return p[i]; + return 0; +} +static void binop(const char *o) +{ + if (!strcmp(o, "*")) op(O_MUL); + else if (!strcmp(o, "/")) { fnref("zdiv"); op(O_CALL); } + else if (!strcmp(o, "%")) { fnref("zmod"); op(O_CALL); } + else if (!strcmp(o, "+")) op(O_ADD); + else if (!strcmp(o, "-")) op(O_SUB); + else if (!strcmp(o, "<<")) { fnref("zshl"); op(O_CALL); } + else if (!strcmp(o, ">>")) op(O_SHR); + else if (!strcmp(o, "<")) op(O_LT); + else if (!strcmp(o, ">")) { op(O_SWAP); op(O_LT); } + else if (!strcmp(o, "<=")) { op(O_SWAP); op(O_LT); lit(1); op(O_XOR); } + else if (!strcmp(o, ">=")) { op(O_LT); lit(1); op(O_XOR); } + else if (!strcmp(o, "==")) { op(O_SUB); seq_isz(); } + else if (!strcmp(o, "!=")) { op(O_SUB); seq_nz(); } + else if (!strcmp(o, "&")) op(O_AND); + else if (!strcmp(o, "^")) op(O_XOR); + else if (!strcmp(o, "|")) op(O_OR); + else die("bad operator"); +} + +/* ---- constant expressions (globals, array sizes, const) ---- */ +static int cexpr(void); +static int cprim(void) +{ + int v; Sym *s; + if (tk == T_NUM) { v = tv; next(); return v; } + if (accept("-")) return -cprim(); + if (accept("~")) return ~cprim(); + if (accept("(")) { v = cexpr(); expect(")"); return v; } + if (tk == T_ID && (s = find(ts)) && (s->kind == K_CONST || (s->kind != K_GVAR && s->defd))) { next(); return s->val; } + die("constant expected"); return 0; +} +static int cexpr(void) +{ + int v = cprim(); + for (;;) { + if (accept("+")) v += cprim(); else if (accept("-")) v -= cprim(); + else if (accept("*")) v *= cprim(); else if (accept("|")) v |= cprim(); + else if (accept("&")) v &= cprim(); else if (accept("<<")) v <<= cprim(); + else if (accept(">>")) v >>= cprim(); else return v; + } +} + +/* ---- expressions ---- */ +static int lv; /* the stack top is an address not yet loaded */ +static void rval(void) { if (lv) { op(O_LD); lv = 0; } } +static void expr(void); +static void strlit(void) /* inline string: jump over it, push its address */ +{ + int j = here, a, i, n = (int)strlen(ts); + lit(0); op(O_JMP); a = here; + for (i = 0; i < n; i++) emit((unsigned char)ts[i]); + emit(0); setlit(j, here); lit(a); next(); +} +static int args(void) { int n = 0; expect("("); if (!accept(")")) { do { expr(); rval(); n++; } while (accept(",")); expect(")"); } return n; } +static void asmblock(void); +static void primary(void) +{ + char *n; Loc *l; Sym *s; + lv = 0; + if (tk == T_NUM) { lit(tv); next(); return; } + if (tk == T_STR) { strlit(); return; } + if (accept("(")) { expr(); expect(")"); return; } + if (is("asm")) { next(); asmblock(); return; } + n = ident(); + if ((l = findl(n))) { laddr(l->slot); lv = !l->arr; return; } + s = find(n); + if (s && s->kind == K_CONST) { lit(s->val); return; } + if (s && s->kind == K_GVAR) { lit(s->val); lv = 1; return; } + if (s && s->kind == K_GARR) { lit(s->val); return; } + if (is("(")) { args(); fnref(n); op(O_CALL); return; } + fnref(n); +} +static void incdec(int post, int d) +{ + if (!lv) die("++/-- needs an lvalue"); + lv = 0; + if (post) { lit(0); op(O_PICK); op(O_LD); lit(0); op(O_PICK); lit(d); op(O_ADD); lit(2); op(O_PICK); op(O_ST); op(O_SWAP); op(O_DROP); } + else { lit(0); op(O_PICK); op(O_LD); lit(d); op(O_ADD); lit(0); op(O_PICK); lit(2); op(O_PICK); op(O_ST); op(O_SWAP); op(O_DROP); } +} +static void postfix(void) +{ + int n; + primary(); + for (;;) { + if (accept("[")) { rval(); expr(); rval(); expect("]"); op(O_ADD); lv = 1; } + else if (is("(")) { rval(); n = args(); lit(n); op(O_PICK); op(O_CALL); op(O_SWAP); op(O_DROP); } + else if (accept("++")) incdec(1, 1); + else if (accept("--")) incdec(1, -1); + else if (accept("as")) ident(); + else return; + } +} +static void unary(void) +{ + if (accept("-")) { unary(); rval(); lit(0); op(O_SWAP); op(O_SUB); } + else if (accept("!")) { unary(); rval(); seq_isz(); } + else if (accept("~")) { unary(); rval(); lit(-1); op(O_XOR); } + else if (accept("*")) { unary(); rval(); lv = 1; } + else if (accept("&")) { unary(); if (!lv) die("& needs an lvalue"); lv = 0; } + else if (accept("++")) { unary(); incdec(0, 1); } + else if (accept("--")) { unary(); incdec(0, -1); } + else postfix(); +} +static void bin(int minp) +{ + int p, a, b; char o[8]; + unary(); + while ((p = binprec()) && p >= minp) { + strcpy(o, ts); next(); rval(); + if (!strcmp(o, "&&")) { + a = here; lit(0); op(O_JZ); bin(p + 1); rval(); seq_nz(); + b = here; lit(0); op(O_JMP); setlit(a, here); lit(0); setlit(b, here); + } else if (!strcmp(o, "||")) { + a = here; lit(0); op(O_JZ); lit(1); b = here; lit(0); op(O_JMP); + setlit(a, here); bin(p + 1); rval(); seq_nz(); setlit(b, here); + } else { bin(p + 1); rval(); binop(o); } + lv = 0; + } +} +static void expr(void) +{ + char o[8]; + bin(1); + if (is("=")) { + if (!lv) die("assignment needs an lvalue"); + next(); expr(); rval(); + op(O_SWAP); lit(1); op(O_PICK); op(O_SWAP); op(O_ST); lv = 0; + } else if (tk == T_OP && strlen(ts) >= 2 && ts[strlen(ts) - 1] == '=' && strcmp(ts, "==") && strcmp(ts, "!=") && strcmp(ts, "<=") && strcmp(ts, ">=")) { + if (!lv) die("assignment needs an lvalue"); + strcpy(o, ts); o[strlen(o) - 1] = 0; next(); + lit(0); op(O_PICK); op(O_LD); lv = 0; expr(); rval(); binop(o); + op(O_SWAP); lit(1); op(O_PICK); op(O_SWAP); op(O_ST); lv = 0; + } +} + +/* ---- asm blocks: raw vm words ---- */ +static void asmblock(void) +{ + char ln[128][64], rn[256][64]; int la[128], nl = 0, ra[256], nr = 0, i, k; const char *c; + Loc *l; Sym *s; + expect("{"); + while (!accept("}")) { + if (tk == T_EOF) die("unterminated asm"); + if (tk == T_NUM) { lit(tv); next(); continue; } + if (tk == T_STR) { strlit(); continue; } + if (accept("#")) { if (tk != T_NUM) die("# needs a number"); emit(tv); next(); continue; } + if (is("-")) { next(); if (tk == T_NUM && glue) { lit(-tv); next(); } else op(O_SUB); continue; } + if (accept(":")) { strcpy(ln[nl], ident()); la[nl++] = here; continue; } + if (strlen(ts) == 1 && (c = strchr(mn, ts[0]))) { op((int)(c - mn)); next(); continue; } + if (tk != T_ID) die("bad asm token"); + if ((l = findl(ts))) { laddr(l->slot); next(); continue; } + if ((s = find(ts)) && s->kind != K_FN) { lit(s->val); next(); continue; } + if (s) { fnref(ts); next(); continue; } + strcpy(rn[nr], ts); ra[nr++] = here; lit(0); next(); /* a label of this block */ + } + for (i = 0; i < nr; i++) { + for (k = -1; ++k < nl && strcmp(ln[k], rn[i]);) ; + if (k == nl) die("unknown asm label"); + setlit(ra[i], la[k]); + } +} + +/* ---- statements ---- */ +static int brk[256][64], nbrk[256], cont[256], nloop; +static int epi[1024], nepi; +static void stmt(void); +static void block(void) +{ + int save = nlo; + expect("{"); + while (!accept("}")) { if (tk == T_EOF) die("missing }"); stmt(); } + nlo = save; +} +static void jmp_to(int t) { lit(t); op(O_JMP); } +static void loopbody(int contaddr) +{ + nbrk[nloop] = 0; cont[nloop++] = contaddr; + stmt(); + nloop--; +} +static void patchbreaks(void) { int i; for (i = 0; i < nbrk[nloop]; i++) setlit(brk[nloop][i], here); } +static void stmt(void) +{ + int a, b, c, d; + if (accept(";")) return; + if (is("{")) { block(); return; } + if (accept("var")) { + do { + Loc *l = &lo[nlo++]; strcpy(l->name, ident()); l->arr = 0; + if (accept("[")) { int n = cexpr(); expect("]"); nslot += n; l->slot = nslot - n; l->arr = 1; } + else l->slot = nslot++; + if (accept("=")) { expr(); rval(); laddr(l->slot); op(O_ST); } + } while (accept(",")); + expect(";"); return; + } + if (accept("unchecked")) { stmt(); return; } + if (accept("if")) { + expect("("); expr(); rval(); expect(")"); + a = here; lit(0); op(O_JZ); stmt(); + if (accept("else")) { b = here; lit(0); op(O_JMP); setlit(a, here); stmt(); setlit(b, here); } + else setlit(a, here); + return; + } + if (accept("while")) { + c = here; expect("("); expr(); rval(); expect(")"); + a = here; lit(0); op(O_JZ); + loopbody(c); jmp_to(c); setlit(a, here); patchbreaks(); return; + } + if (accept("for")) { + int save = nlo; + expect("("); + if (!is(";")) { if (is("var")) { stmt(); goto cond; } expr(); rval(); op(O_DROP); } + expect(";"); + cond: c = here; + if (!is(";")) { expr(); rval(); } else lit(1); + expect(";"); + a = here; lit(0); op(O_JZ); b = here; lit(0); op(O_JMP); + d = here; + if (!is(")")) { expr(); rval(); op(O_DROP); } + expect(")"); jmp_to(c); + setlit(b, here); + loopbody(d); jmp_to(d); setlit(a, here); patchbreaks(); + nlo = save; return; + } + if (accept("break")) { if (!nloop) die("break outside a loop"); brk[nloop - 1][nbrk[nloop - 1]++] = here; lit(0); op(O_JMP); expect(";"); return; } + if (accept("continue")) { if (!nloop) die("continue outside a loop"); jmp_to(cont[nloop - 1]); expect(";"); return; } + if (accept("return")) { + if (!is(";")) { expr(); rval(); } else lit(0); + expect(";"); epi[nepi++] = here; lit(0); op(O_JMP); return; + } + if (is("asm")) { next(); asmblock(); return; } + expr(); rval(); op(O_DROP); expect(";"); +} + +/* ---- top level ---- */ +static void fndef(void) +{ + char *n = ident(); Sym *s; int np = 0, tags = 0, t, ret = 0, frame, i; + char pn[16][64]; + s = find(n); + if (!s || s->kind != K_FN) { s = add(n, K_FN, 0); s->defd = 0; } + expect("("); + if (!accept(")")) { + do { + t = 0; + if (tk == T_ID && (!strcmp(ts, "m") || !strcmp(ts, "d"))) { + char k = ts[0]; next(); + if (tk == T_ID) t = k == 'm' ? 1 : 2; + else { pn[np][0] = k; pn[np][1] = 0; np++; continue; } /* a parameter called m or d */ + } + if (np < 12) tags += t << (2 * np); + strcpy(pn[np++], ident()); + } while (accept(",")); + expect(")"); + } + if (accept("->")) { if (strcmp(ident(), "u")) die("only -> u is a return tag"); ret = 1; } + s->sig = ret + 2 * np + 64 * tags; + if (accept(";")) return; /* prototype */ + if (s->defd) die("function defined twice"); + s->val = here; s->defd = 1; + nlo = 0; nslot = 0; nepi = 0; infn = 1; + for (i = 0; i < np; i++) { strcpy(lo[nlo].name, pn[i]); lo[nlo].arr = 0; lo[nlo++].slot = nslot++; } + /* prologue: fp -= frame, then pop the arguments into their slots */ + lit(ZFP); op(O_LD); frame = here; lit(0); op(O_SUB); lit(ZFP); op(O_ST); + for (i = np; i--;) { laddr(i); op(O_ST); } + block(); + lit(0); + for (i = 0; i < nepi; i++) setlit(epi[i], here); + lit(ZFP); op(O_LD); lit(nslot); op(O_ADD); lit(ZFP); op(O_ST); op(O_RET); + setlit(frame, nslot); + infn = 0; +} +static void top(void) +{ + while (tk != T_EOF) { + if (accept("unchecked")) continue; + if (accept("fn")) { fndef(); continue; } + if (accept("const")) { do { char *n = ident(); expect("="); add(n, K_CONST, cexpr()); } while (accept(",")); expect(";"); continue; } + if (accept("var")) { + do { + char *n = ident(); + if (accept("[")) { int k = cexpr(), a = here; expect("]"); while (k--) emit(0); add(n, K_GARR, a); } + else { + int a = here; add(n, K_GVAR, a); emit(0); + if (accept("=")) { + if (tk == T_STR) { int i, l = (int)strlen(ts); img[a - GORG] = here; for (i = 0; i < l; i++) emit((unsigned char)ts[i]); emit(0); next(); } + else img[a - GORG] = cexpr(); + } + } + } while (accept(",")); + expect(";"); continue; + } + die("expected fn, var or const"); + } +} + +int main(void) +{ + static char buf[1 << 22]; int n = (int)fread(buf, 1, sizeof buf - 1, stdin), i, j; Sym *st; + buf[n] = 0; src = buf; next(); + /* image header: set the frame pointer, jump to start, then the symbol blob pointer and image end */ + lit(FTOP); lit(ZFP); op(O_ST); fnref("start"); op(O_JMP); emit(0); emit(0); + top(); + for (i = 0; i < nfx; i++) { + Sym *s = &sy[fx[i].sym]; + if (!s->defd) { fprintf(stderr, "zc0: undefined function %s\n", s->name); return 1; } + setlit(fx[i].at, s->val); + } + if (!(st = find("start")) || !st->defd) { fprintf(stderr, "zc0: no start\n"); return 1; } + /* symbol blob: kind, value, sig, name chars, 0 ... then a 0 kind */ + img[5] = here; + for (i = 0; i < nsy; i++) { + emit(sy[i].kind); emit(sy[i].val); emit(sy[i].sig); + for (j = 0; sy[i].name[j]; j++) emit((unsigned char)sy[i].name[j]); + emit(0); + } + emit(0); + img[6] = here; + printf("#define GORG %d\n#define GLEN %d\nstatic const int genome[] = {", GORG, here - GORG); + for (i = 0; i < here - GORG; i++) printf("%s%d", !i ? "\n" : i % 16 ? "," : ",\n", img[i]); + printf("\n};\n"); + return 0; +} diff --git a/docs/history/10-zc/notes.md b/docs/history/10-zc/notes.md new file mode 100644 index 0000000..96baec2 --- /dev/null +++ b/docs/history/10-zc/notes.md @@ -0,0 +1,37 @@ +# zc: the c-like genome, u m d and the collector + +code/ next to this file is the tree as shipped at the end of this stage (tinyvm-zc.zip), with its own README. + +## message 10 + +> we're gonna make this thing cooler. make the syntax C-like whilst still keeping the forth capabilities like super-homiconicity and the likes, add explicit and automatic gc (use a fast approach) and memory management that looks like this: +> every heap object has a classifier u m d, up middle down. up means the object will die at the scope above (becomes m when returned to the parent scope, and it must be returned and cannot be discarded). m means it must be used here and can be passed down (but once it's recasted as u or d it can't be casted back to m in this scope). d means the variable is passed down, and its lifetime ends after it's given for the first time to a scope lower than the current one. +> ask me questions to clarify this approach, then add it to the code and also add a simple static analyzer which checks these lifetimes + +my answers to the clarifying questions, exactly: + +> scope: every { } block +> +> lending an m object to a callee: borrow, no escape (Recommended) +> +> storing a heap object inside another object or a global: container gets ownership. putting a value inside a container requires its umd to match the parent, and from then on, the umd for that is also inherited from the parent (as in if a parent m is casted to d, so is the child heap object) +> +> gc: free lists + mark-sweep (Recommended) +> +> where the compiler lives: resident in vm code. bootstrap it step by step, by writing a c-like in core.c and then rewriting the compiler inside the c-like. also bring back llvm if you think it'll make your job easier +> +> homoiconic features: parse words, quoted code blocks, inline stack code, patchable compiler tables +> +> how an inner block hands a u object up: expression and assign, both +> +> when the analyzer runs: inside the compiler but can be turned off if the code requests it + +**summary:** the genome is now zc, a typeless c-like language, described above. stage 0 is `zc0.c` (kept as its own file next to core.c rather than inside it, so the runtime still has one native file), and the resident compiler is `src/compiler.zc` plus `src/rt.zc`, which reached a byte-identical fixpoint compiling itself. llvm wasn't brought back, since the target is the vm itself. memory went to 1m words, and core.c gained an interpreter for non-arm64 hosts. calls i made without asking: tags go on signatures (`fn f(m a, d b) -> u`) and recasts are `x as u` / `x as d`; a d object touched inside an inner block moves into that block; storing into a borrowed object, a global or untracked memory needs `as d`; handing a u up by assignment only reaches the directly enclosing block; unchecked code gives its objects to the collector. the old asm genome moved to `legacy/`. + +## working notes + +before asking anything i restated the model so it could be corrected early: m is owned here, lent freely and freed at the scope end; d is a move to the first lower scope it touches; u is a return obligation that arrives as m above; recasts only go away from m. the answers settled scopes (every block), lending (borrow, no escape), containment (tags must match and children inherit), the collector (free lists plus mark-sweep), where the compiler lives (resident, bootstrapped from c), the homoiconic features (all four), how blocks hand u up (value and assignment) and the analyzer (on, with an off switch). + +calls made without asking: memory grew to 1m words; core.c gained a plain interpreter for non-arm64 hosts; the language is typeless like b; tags went on signatures (`m p`, `d p`, `-> u`) with `x as u` casts; storing into globals or untracked memory needs d and hands the object to the collector; unchecked code is collector-managed. + +the bootstrap went zc0 (c) -> g0 -> g1 -> g2 and g1 == g2 on the first self-compile. things the analyzer caught in its own runtime on the way: puts walking a borrowed pointer with s++, wcopy returning a borrowed pointer, and the whole compiler source getting cut short by a backtick inside an error message (the backtick ends the source). the first round of rejection tests found one real gap, an m object unused before an early return, fixed with a path-independent "owned but never used" check, and one wrong test, where touching a d object in an else block moved it into that block, exactly as the rules say. diff --git a/docs/history/11-types-enhancements/code/README.md b/docs/history/11-types-enhancements/code/README.md new file mode 100644 index 0000000..db3e378 --- /dev/null +++ b/docs/history/11-types-enhancements/code/README.md @@ -0,0 +1,389 @@ +# tinyvm zygote: notes and transcript + +a tiny stack cpu that started as a c89 code golf entry and ended up as a self-hosting runtime. a small native core runs vm code: jit-compiled to arm64, compiled through llvm on other hosts if you build with it, or interpreted. the genome it boots is a compiler for zc, a c-like language that keeps the forth habits. the compiler lives in vm memory next to your program, and your program can call into it, patch its tables, add syntax to it and compile new code while it runs. heap objects carry an ownership tag (u, m or d) that a static analyzer inside the compiler checks, with deterministic frees at scope ends and a mark-sweep collector underneath. types are a compiler primitive, with structs, generics, reflection and an optional typechecker. a program can also wire up to 512 custom opcodes into the cpu (virtual enhancements), which the jits inline as native code. + +this folder has the latest code. `history/` is the scrapbook: notes for every stage, plus frozen code from the stages that still have it. + +## files + +`core.c` is the native core and the only native code at runtime: the arm64 jit, the llvm backend (behind `-DZYLLVM`), the portable interpreter, and the enhancement unit (recipe parser, vm-code lifter, optimizer and lowering). `src/rt.zc` is the runtime (i/o, the native division enhancements, the heap and the collector) and `src/compiler.zc` is the compiler, both written in zc. together they are the genome. `zc0.c` is stage 0 of the bootstrap, a compiler in c for the subset of zc that `compiler.zc` is written in. `tools/build.zc` makes the resident compiler compile its own source into a fresh image, and `mkgenome.c` turns that image into `genome.h`, which is generated and checked in. `examples/` has hello, factorial, echo, self-patching code, an owning list, a repl, a typed program and an enhancement demo. `tests/` has feature tests, `tests/errors/` (programs the lifetime analyzer has to reject) and `tests/typeerrors/` (programs the typechecker has to reject). each of those starts with the message it expects, and `tests/runerr.sh` runs a folder of them. `test.sh` does the whole bootstrap and runs everything on all three backends. `history/` is the scrapbook. + +## build + +``` +cc -O2 core.c io_linux.c -o zy # io_linux.c provides Q, G and J +./zy `). `ZYNOLLVM=1` turns the llvm tier off. + +regenerating the genome after changing `src/`: + +``` +cc zc0.c -o zc0 && cc mkgenome.c -o mkgenome +cat src/rt.zc src/compiler.zc | ./zc0 >genome.h # stage 0, from c +cc -O2 core.c io_linux.c -o zy +(cat tools/build.zc; printf '`'; cat src/rt.zc src/compiler.zc) | ./zy | ./mkgenome >g.h +mv g.h genome.h && cc -O2 core.c io_linux.c -o zy # now built by itself +``` + +run the last two lines once more and `genome.h` should come out byte for byte the same. `test.sh` checks that, on every backend. + +## zc, the language + +it reads like c. underneath every value is a 32 bit word and memory is word addressed, so `p + 1` is the next word whatever `p` points at, and strings are one char per word with a 0 at the end. types are optional: untyped code is typed `word`, which goes with anything. + +``` +const N = 10; +[N]int table; // a global array +fn fib(int n) -> int { if (n < 2) return n; return fib(n - 1) + fib(n - 2); } +fn main() { + for (int i = 0; i < N; i++) table[i] = fib(i); + let sq = { let t = table[5]; t * t }; // a block is an expression too, its last line is its value + let x = asm { 2 3 + }; // raw vm code, leaves one value + putn(sq + x); putc(10); + return 0; +} +``` + +declarations are c-style, type first: `int a = 1, b;`, `*node p = new(node);`, `[4]char buf;`, `point origin;`. `let` declares without a type: it infers pointer, struct and function types from the initializer and keeps numbers as plain words, and `let a[10];` is an untyped array. statements are declarations, `if`/`else`, `while`, `for`, `break`, `continue`, `return`, `{ }` blocks, `asm { }` and expressions. operators are c's with c's precedence, plus `p.x` and `p->x` (the same thing: `.` goes through a pointer on its own), `e as T` to change a type without changing the bits, and the casts below. comparisons and `&& ||` give 0 or 1. `/`, `%` and `<<` compile to native enhancement opcodes. character literals, `0x` hex and the usual escapes are there. there's no `?:` and no `switch`. + +functions are called by name once declared, so mutual recursion works without prototypes, except that a function with tags has to be declared (a prototype is enough) before a checked call to it. `main` runs after the whole program has compiled. every function returns one word, 0 if it falls off the end. + +builtins from the runtime: `putc`, `getc`, `eof`, `puts`, `putn`, `halt`, `strlen`, `streq`, `wcopy`, `wfill`, `new`, `new_t`, `free`, `gc`, `isobj`, `osize`, `typeof_obj`, `live_objects`, `install`, `zdiv`, `zmod`, `zshl`. + +## memory model: u, m and d + +`new(n)` or `new(T)` gives back a fresh object. bind it to a local and the local owns it, tagged **m**. tags live on local variables and parameters, the analyzer follows them through the code, and the compiler inserts `free` calls where owned objects die. a tag is written after the type, `*node.u p`, or alone, `.d x`. `u`, `m` and `d` are reserved words. + +**m** means the object belongs to this block and has to be used in it. you can lend it to callees as often as you like, and when the block ends it's freed. an m object that's never used anywhere in its block is an error. + +**d** means the object is going down. the first time it's handed to a lower scope its life here is over: passing it to a `.d` parameter, or touching it inside an inner block, which then owns it as m and frees it when that block ends. using it afterwards is an error, and so is ending the block with a d object that never left. + +**u** means the object is going up. it has to leave this block for the one above, where it arrives as m. it leaves through `return` (the function's return type has to carry `.u`, like `-> *node.u` or just `-> .u`), as the value of a block expression, or by assignment to a variable of the directly enclosing block. letting it fall out of scope, or throwing away a u result, is an error. + +recasting is multiplication by the tag: `u*x`, `d*x`, `m*x`. it only goes away from m: m can become u or d (and u and d can swap), but nothing goes back to m in the same scope. a declaration can carry a tag too, `.u r;`, and then whatever lands in it gets that tag. + +parameters: an untagged parameter is m from the caller's side, a lend, and the callee sees a plain word. `.m` makes it a checked borrow: the callee can read and write it but can't free it, return it, recast it or store it anywhere. `.d` hands the object over, and it arrives as m. + +``` +fn box(int v) -> *int.u { *int b = new(int); *b = v; return u*b; } +fn push(*list l, *int.d item) { l.items[l.len] = d*item; l.len++; return 0; } +``` + +containers own what you store in them. `p[i] = c` or `p.f = c` needs `c` to carry the same tag as `p` (or be a fresh object), and from then on `c` follows `p`: if `p` is recast, so is `c`, if `p` is given away `c` goes with it, and when `p` is freed its children are freed too. a child can't be recast or given away on its own. storing into an object you only borrowed counts as giving the child away, so it needs `d*`. the runtime records the owner in the child's header, so freeing a container frees exactly the objects it owns. + +globals, plain pointers and arrays aren't tracked. storing an owned object there needs `d*`, and the mark-sweep collector takes it over. a fresh object can go straight in. + +branches have to agree: at the end of an `if` every variable has to be in the same state on both paths (a path that returns or breaks doesn't count), and a loop body has to leave every outer variable the way it found it. + +### the analyzer + +it runs inside the compiler on every function, including code compiled at runtime by `eval` and friends, and stops at the first violation with a line number and the variable's name. `tests/errors/` has one small program per rule. to turn it off: `unchecked fn f() { }` for one function, `unchecked { }` for a block, and a top-level `unchecked;` for the rest of the unit (a parse word can also set `check_on = 0`). unchecked code doesn't track anything, so what it allocates belongs to the collector, and checked variables can't be touched from inside an unchecked block. + +## types + +types are a compiler primitive. the compiler keeps them as records in a type area at 0x9C000, hash-consed so the same type is always the same pointer. they stay in memory after compiling, so programs and parse words can look at them. + +``` +struct point { int x, y; }; +struct list { int len; *T items; }; +type num = int; +fn first(*list l) -> T { return l.items[0]; } +fn mk(int cap) -> *list.u { *list l = new(list); l.items = new(T, cap); return u*l; } +... +*list<*point> ps = mk<*point>(8); // explicit type arguments, or inferred from the arguments +``` + +the kinds are `word`, `int`, `char`, `bool`, `void`, pointers `*T`, fixed arrays `[n]T`, function types `fn(int, *char) -> int`, structs, generic structs and generic functions. structs and arrays live in place (in a frame, a global or inside another struct), and their value is their address, so they're passed around by pointer. `sizeof` counts words. type arguments have to be one word (a number, a pointer or another parameter), so a generic function is compiled once and works for every instance. indexing scales by the element's size, while plain pointer arithmetic stays in words, like b. `new(T)` and `new(T, n)` stamp the type into the object's header. + +### reflection + +`typeof(e)` and `typeof(T)` give a type record and `sizeof(...)` its size. both work on any expression, which is compiled dry and never runs. at runtime: `tkind(t)` (one of the `TY_` constants), `tsize`, `tname`, `tlen`, `tnfields`, `tfname(t, i)`, `tftype`, `tfoff`, `tret`, `tnparams`, `tparam`, `tparamtag` (the u/m/d tag of a parameter), `tretu`, `tnargs` and `targ` for generic instances, and `tprint(t)`. `sigof(f)` gives the type of a function by address (untyped ones get a signature built from their arity and tags), `sigofs("name")` by name, and `typeof_obj(p)` reads the type stamped on a heap object. + +### the typechecker + +off by default. `typecheck on;` turns it on for what follows and `typecheck off;` turns it back off (a parse word can flip `tc_on`). it checks initializers, assignments, arguments (unifying generic parameters), return values, arithmetic, comparisons, dereferencing, indexing and calls. `word` matches anything, numbers mix with each other, a literal 0 is a null pointer, and arrays decay to pointers. it stops at the first error, printing what it expected and what it got. `tests/typeerrors/` has one per rule. some things are checked whatever the setting, because the compiler needs them to generate code: field names, and field access on something that isn't a struct. + +## virtual enhancements + +the cpu has room for 512 custom opcodes (22 to 533), and opcode 21 (`%` in asm) installs one at runtime. it's the closest a soft cpu gets to rewiring itself. an enhancement takes up to 8 inputs off the data stack and leaves up to 8 outputs, and it's written one of two ways: + +``` +enhance clamp(x, lo, hi) micro "x = max x lo; x = min x hi"; +enhance divmod(a, b) -> 2 micro "r2 = sdiv a b; r1 = srem a b; r0 = r2"; +enhance cube(x) vm { 0 $ 0 $ * * }; +enhance noisy(x) vm { 0 $ . } else soft_noisy; // can't be lifted (it does i/o), so calls use the fallback +``` + +a micro recipe works on registers `r0`..`r15`, which the inputs arrive in (also under the parameter names), and the outputs leave from `r0` up. the micro-ops are `li mov add sub mul sdiv udiv srem urem and or xor shl shr sar neg not lt ltu eq ne min max minu maxu mulhs mulhu clz abs sel ld st`. a `vm` body is straight-line vm code that gets lifted by running it symbolically. both turn into ssa, and the core's optimizer folds constants, simplifies algebra, removes common subexpressions and dead code, and turns division by a constant into a multiply-high sequence (`ZYTRACE=1` prints the result). the arm64 jit then inlines the code at every use with registers allocated, the llvm backend inlines it as ir, and the interpreter runs the optimized list. if something doesn't fit in registers, the jit calls the interpreter for that op. + +an enhancement is called like a function when it has one output, or by name from asm. `enhance` installs at compile time, so the wiring is there before `main` runs, and a parse word can use it right away. an image installs its own enhancements when it boots, from its symbol table. that's how `/`, `%` and `<<` work: `rt.zc` declares `idiv`, `imod` and `ishl` as one-instruction recipes. `install(desc)` is the raw interface, and its descriptor format is documented in `core.c`. stores done by an enhancement don't invalidate compiled code, so use them for data. + +this replaces my first plan for enhancements, which compiled c or llvm ir sources at runtime. part of that reply got stopped by a safety check, and once it turned out llvm was meant for portability, it moved into the backend below instead. + +## the llvm backend + +a tiered jit for hosts the arm64 backend doesn't cover. the interpreter counts how often each block start gets reached, and a hot block is translated to llvm ir using the same block rules as the arm64 jit: it ends at call, jump, ret or halt, keeps going past a jz, and literal targets are fused. the ir goes through the O2 pipeline and gets compiled by orc's lljit. stores into compiled words drop the blocks covering them, like on arm64. it pays off for long-running code: a loop-and-recursion benchmark runs about 3.3x faster than the interpreter. short bursty work like the self-compile is slower, since compile time dominates there. `ZYHOT=1` compiles every block, which is a good stress test, and the test suite runs that way too. + +## code as data + +the compiler is in memory with your program and every name in it is callable. + +`asm { }` emits raw vm words: numbers push, `-5` pushes a negative, `#n` emits a raw word, one-char tokens from the mnemonic table are opcodes, `:name` defines a label local to the block, enhancement names emit their opcode, and other names push the address of a variable or function. + +`quote { ... }` is a token list as a fresh object: `[n, kind, value, kind, value, ...]`, where names, strings and operators are interned pointers (`intern("while")` gets one). `tokens(s)` makes one from a string, and `qdup(q)` copies one. + +`eval(q)` compiles a token list as a function body and runs it. `compile(q)` returns its address instead, `load(q)` compiles top-level declarations, and `evals(s)` runs `eval` on a string. + +parse words: `immediate fn name() { }` runs at compile time whenever `name` shows up as a statement, inside an expression or at the top level. it starts with the current token on its own name and works the compiler directly: `next()`, `peek()`, `tk`/`tv` (current token kind and value), `expect`, `accept`, `expr()`, `stmt()`, `substmt()`, `ptype()`, `emit`, `lit`, `op`, `hole`/`setlit` for jumps, and `here`. `grab()` reads a `{ }` group into a token list, and `feed(q)` pushes a list back into the input, which is all a macro needs. parse words can call `eval` partway through a compile: the compiler saves its state and emits the new function in place with a jump around it. the built-in keywords are entries in the same table, so a parse word called `while` replaces `while`. + +tables: `add_binop("**", 11, "ipow c")` adds or replaces a binary operator with a precedence and its code, written like asm (names resolve when it's used). `add_unop` does the same for prefix operators, and the lexer learns new spellings from both. `mnem` is the asm mnemonic table, `q@!$_~cjzr,?.+-*&|^><%`, and can be rewritten. + +## the bootstrap + +`zc0.c` compiles the subset `compiler.zc` uses (no analyzer, types, quote, parse words or block expressions, but it does lay out `enhance` declarations) into a first genome. that genome compiles `compiler.zc` again on the vm. that's where the real compiler, the analyzer and native division first meet their own source, and `compiler.zc` passes its own lifetime checks. the result compiles itself into identical words, on all three backends, and that's the `genome.h` that ships (57463 words). an image build (`build_image(org, dst)`) compiles with only the image's own names visible (plus keywords and builtin types), writes code meant for one address into another, and appends a symbol table so the booted genome knows its own names and enhancements. + +## the instruction set + +every word is 32 bits. an odd word is a literal that pushes `w>>1` (31 bits, sign extended). an even word is an opcode, `w/2`. there's a data stack and a return stack, both stored inside memory. + +``` +0 q halt 5 ~ swap 10 , in (Q) 15 * mul 20 < less +1 @ load 6 c call 11 ? empty (J) 16 & and 21 % install +2 ! store 7 j jump 12 . out (G) 17 | or 22..533 enhancements +3 $ pick 8 z jz 13 + add 18 ^ xor +4 _ drop 9 r ret 14 - sub 19 > shr +``` + +stack effects: `!` is `(v addr --)`, `$` is `(n -- copy of the nth item)` so `0 $` is dup, `z` is `(cond addr --)` and jumps when cond is 0, `c` and `j` take their target from the stack, and `%` is `(descriptor -- status)`. there's still no div or mod in the base set, because a divider can't finish in one cycle in hardware. an enhancement can add one. + +## memory map and abi + +memory is 1m (1048576) words. it could grow until the host runs out (literals already reach 1g words), but that would mean reserving the jit tables with mmap instead of static arrays, and 1m is plenty for now. + +``` +0x00000.. the program: code, with globals between functions +0x40000..0x9BFFF heap +0x9C000..0x9FFFF type records +0xA0000 frame pointer word +0xA0100..0xBFFFF compiler data: interned names, symbol table, locals, tables +0xC0000.. genome: 0xC0000 entry, 0xC0005 symbol table address, 0xC0006 image end +0xE0000..0xEFFFF frame stack, grows down from 0xF0000 +0xF0000.. return stack, grows up +0xF8000.. mark stack +0xFC000..0xFFFF0 data stack, grows down +``` + +calls push the arguments left to right and the callee's prologue pops them into its frame. locals live in the frame at `*0xA0000 + slot`. every function leaves exactly one word on the data stack. heap objects have a 6 word header: size, size class, flags, a check word, owner, type. + +## how the core works + +registers: x19 holds the memory base, x20 the data stack pointer, x21 the return stack pointer, x22 the translation table and x23 the state block (memory, s, r, table, coverage map, Q, G, &J, store hook, install hook, enhancement fallback). + +compilation is lazy. the first time control reaches an address, that block is translated from live memory. a block ends at call, jump, ret or halt. a `z` doesn't end it: the not-taken side keeps compiling, which makes superblocks. a literal followed by `c`, `j` or `z` is fused into a constant target. every exit goes through a 5-instruction dispatch stub that bounds-checks the target, loads `tab[t]`, and branches straight to it when it's already compiled. otherwise it returns to c, which compiles the block and re-enters. enhancements are inlined: inputs load from the data stack into w0..w15, the optimized micro-ops run in registers with x16 and x17 as scratch, and the outputs go back. an install calls into c and then returns to the loop, which flushes every translation, since code built before the install may have inlined the old version. + +self-modification is handled with a byte map of which words some live translation covers. a store checks that byte inline: an uncovered target, the usual case, gets a plain store. a covered target calls the hook, which drops every translation containing that word and makes the jit code return to c at the next instruction, so even patches to the very next word take effect. writing back the same value is a no-op. if the 16 mb code buffer fills, everything is flushed. + +platform: the darwin path uses `MAP_JIT`, `pthread_jit_write_protect_np` and `sys_icache_invalidate`. linux uses rwx mmap and `__builtin___clear_cache`. only linux arm64 has actually been run, under qemu. the darwin path follows apple's documented recipe but hasn't been tested, and under the hardened runtime it also needs the allow-jit entitlement. every host shares the same semantics: shift counts wrap at 32, stores outside memory are ignored, loads outside memory read 0, and jumping outside memory or to an unwired opcode halts. + +## test results + +`test.sh` passes all 62 checks on x86-64 linux: the fixpoint, an up-to-date `genome.h`, and then every example and feature test on the interpreter, on the arm64 jit under qemu, and on the llvm backend, all with identical output. it also checks that all 23 programs in `tests/errors/` and all 10 in `tests/typeerrors/` get rejected with the expected message on each backend, that llvm agrees when it compiles every block, and that the self-compile matches byte for byte on all three. separately, 80000 random divisions through enhancements matched the software routine, covering divide by zero, INT_MIN/-1, negatives and the magic-number path for constants. + +## open ends + +the earlier gaps, in more detail: + +**branch and loop states must match exactly.** after a loop, the analyzer assumes every variable is back in the state it had when the loop started, and checks every `break` and `continue` against that. so a loop that gives something away and then always breaks is rejected, even though it's fine. the fix is to make the state after the loop the join of the break paths and the condition-false exit, and to check only the back-edges against the loop's start. + +**untracked aliases.** the analyzer only follows bare variable names. `let q = p + 0;` or `let c = list[1];` is a plain word pointing at an owned object, and nothing tracks it. when the owner is freed, the alias dangles. plain words are allowed into `.d` parameters, so you can even free through one. `free` checks the header, so freeing a dead block twice does nothing, but if that block has already been reused you free someone else's object. untagged parameters make this easier to hit now, since they accept lends while the callee sees a plain word. `examples/typed.zc` shows the safe way: `push` takes its item as `.d`. + +**the conservative collector.** the vm can't read its own stack pointer, so the collector scans the whole data stack region, stale values included. any word that happens to equal an object's address keeps that object alive. it never frees anything early, it just keeps garbage around longer. + +**free lists never merge.** each size class has its own list. freeing ten thousand small blocks doesn't help a big request, so the heap can run out while it's mostly free. the classes are powers of two, so a buddy allocator would be a small change. + +**coarse invalidation.** translations are only reclaimed by a full flush, and every store to a covered word rescans the block list. installing an enhancement flushes everything. the llvm backend never frees compiled code at all. + +**no stack overflow checks.** the frame stack is 64k words, the return stack 32k and the data stack 16k, and nothing checks them. deep recursion runs into the neighboring regions without a word. + +**generics are erased.** a generic function is compiled once, so the type stamped by `new(T)` inside it says `T`, and type arguments have to be one word. + +**the rest.** compile errors stop at the first one. a token list fed in by a macro reports the line where the macro was used. a declaration starting with `**` reads as an expression. `?:` and `switch` are still missing. the llvm backend compiles one module per block, so it's slow to warm up, and a background compile thread would fix that. stores done by enhancements don't invalidate compiled code. + +--- + +## transcript + +each of my messages is quoted exactly, followed by a summary of the reply. + +### 1 + +> codegolf a tiny cpu vm in C89. the goal is to achieve the lowest character count possible. include an assembler as well (scored separately) and a hello world program (not scored, but elegance and simplicity is a bonus) +> it must be capable of re-entrant code and a realistically workable instruction set. + +**summary:** a two-stack forth-style machine. the vm was 384 bytes: one ternary chain inside `while(o=m[p++])`, the top two stack items cached in `a` and `b`, the program loaded with `read(open(...))` so it needs no headers, and both stacks inside the 65536-word memory. it had 22 opcodes including div and mod, a two-word literal, and jump/call targets popped from the stack. the assembler was 348 bytes and two-pass, using `strcspn` instead of `strchr` so the implicit int return stays safe on 64-bit. hello world was a pointer loop over a string with a zero terminator. re-entrancy was shown with a recursive factorial plus a recursive decimal printer. + +### 2 + +> your cpu should also be realistically close to hardware (translating it to logical circuitry hardware should be fairly easy) and sport an acceptably fast execution (if hypothetically ported to hardware, not in software) +> +> assume i/o is handled by two external functions, `char Q()` (getchar) and `void G(char)` (putchar) and a state variable J (boolean, shows if input is empty). +> +> don't be afraid to use macros. + +**summary:** no reply was sent to this message at the time. it was answered together with message 3. + +### 3 + +> pro tip: if you define a macro as `!o--`, you can save 2 characters per if condition +> oh and btw, newlines are free + +**summary:** used the tip as `#define X !--o?`, with pre-decrement because opcode 0 (halt) is already handled by the loop, and with the `?` folded into the macro and free newlines used as separators. the hardware-oriented redesign: bit 0 of each word is a literal flag, so literals take one cycle like the j1 cpu. div and mod were dropped because they can't finish in one cycle and were replaced by an arithmetic shift right, giving 8 alu ops that decode from 3 bits. every instruction does a fixed amount of work with at most one data memory access, and call and jump share a datapath (`r-=o`). i/o now goes through `Q`, `G` and a `?` op that reads `J`. the result was the vm at 339 bytes and the assembler at 346. the final golfed code: + +```c +#define X !--o? +int m[1<<16],p,s=65e3,r=6e4,o,a,b;main(c,v)char**v;{read(open(v[1],0),m,s);while(o=m[p++])a=m[s],b=m[s+1],o&1?m[--s]=o>>1:(o/=2,X +m[s]=m[a]:X +m[a]=b,s+=2:X +m[s]=m[s-~a]:X +s++:X +m[s]=b,m[s+1]=a:(o-=2)<1?m[r]=p,r-=o,p=a,s++:X +p=b?p:a,s+=2:X +p=m[--r]:X +m[--s]=Q():X +m[--s]=J:X +G(a),s++:(m[++s]=X +b+a:X +b-a:X +b*a:X +b&a:X +b|a:X +b^a:X +b>>a:b now let's move in stages. write a compiler that takes this assembler's code and converts it into darwin arm + +**summary:** an ungolfed c89 compiler from vm assembly to darwin arm64 assembly. every word got its own label, the image was emitted as an addressable data array, x19/x20/x21 held the memory base and the two stack pointers, and computed jumps went through a table of offsets. a literal followed by a jump became a direct branch. `Q` and `G` went through `sxtb`, since char is signed on apple arm64, and `J` was read with `ldrb`. it was verified by assembling with llvm-mc for darwin, then rewriting to linux with sed and running under qemu, where it matched the vm byte for byte. this version couldn't handle self-modifying code. + +### 5 + +> can you write the assembler in assembly and pair the code with the assembly and the assembler so that it can re-wire itself at runtime? or maybe include an interp for self-modification, otherwise fns are exec'd native + +**summary:** both. `aslib.s` is the assembler written in vm assembly as a library (`a.src`, `a.dst`, `a.org`, `a.lab`, then `a.run c`), and with `asmain.s` it's a 568-word standalone assembler. it's byte-identical to the c assembler and reaches a fixpoint when assembling itself. this is also where the `:;comment` trick came from. the compiler became a hybrid. the image is split into basic blocks, each block counts how many of its words differ from the original image, and stores into code go through a slow path that updates the counters. clean blocks run native, while dirty blocks and anything past the image run in an interpreter compiled into the same binary, which hands control back to native code whenever it lands in a clean block. `cc -t` printed `[` and `]` at each switch. the demo rewired a function at runtime to point at freshly assembled code and then restored it. + +### 6 + +> which one do you like more? and which is more performant? +> keep that one, then port to using llvm ir so that it can be platform independent + +**summary:** no reply was sent at the time. the llvm port was dropped when message 7 changed direction. the question was answered at the start of the reply to message 7: the hybrid was the favorite because it kept the vm's semantics exact. the plain compiler was slightly faster on code that never rewrites itself, but it silently ran stale code once anything did. the zygote beats both. + +### 7 + +> can you make it a zygote approach? +> a minimal core loads first, reads and compiles the program (either wholly or partially, for speed) and replaces it with itself. so it's like a jvm but self-contained and on the same level as itself, where the assembler can itself be modified in-memory to assemble different code. for this we need to ditch llvm + +**summary:** the current design, described above. the native core only does jit and glue. it loads the genome (boot loader + assembler, both vm code) at 48000. the boot loader reads source until eof or a backtick, assembles it at 0 and jumps there. compilation is lazy per block, straight from live memory, with superblocks through `z`, fused constant targets, and dispatch chained through a table. invalidation uses the coverage byte map plus a store hook, so self-modified code gets retranslated instead of interpreted. the resident assembler sits at fixed addresses, and `repl.s` rewrites its mnemonic table so `p` means out, then assembles and runs input chunks in a loop, overwriting the same address each time. verified under qemu on linux arm64, including the self-hosted assembler running on the zygote. about 4x faster than the golfed interpreter. + +### 8 + +> damn dawg you made an entire baremetal os? +> +> let's move to cowork. pack everything up alongside a transcript of every message i've sent and your own notes on what you've implemented in an md file, with the code, so i can move it. + +**summary:** not quite an os. it's a userland runtime. it still runs on darwin or linux, uses their mmap for executable memory, and gets i/o from the host through `Q`, `G` and `J`. there's no scheduler, no drivers and no hardware access, so a closer description is a tiny self-hosting jit, in the spirit of a jvm. the first packing attempt bundled every stage's code with a full test script. + +### 9 + +> nono we don't need all the code, just the recent ones. my msgs and your summaries are more important and all should be there + +**summary:** this package. only the zygote stack plus its examples and tests are included, and earlier stages appear only as summaries. the golfed reference vm and assembler are inlined under message 3, because they define the instruction set. + +### 10 + +> we're gonna make this thing cooler. make the syntax C-like whilst still keeping the forth capabilities like super-homiconicity and the likes, add explicit and automatic gc (use a fast approach) and memory management that looks like this: +> every heap object has a classifier u m d, up middle down. up means the object will die at the scope above (becomes m when returned to the parent scope, and it must be returned and cannot be discarded). m means it must be used here and can be passed down (but once it's recasted as u or d it can't be casted back to m in this scope). d means the variable is passed down, and its lifetime ends after it's given for the first time to a scope lower than the current one. +> ask me questions to clarify this approach, then add it to the code and also add a simple static analyzer which checks these lifetimes + +my answers to the clarifying questions, exactly: + +> scope: every { } block +> +> lending an m object to a callee: borrow, no escape (Recommended) +> +> storing a heap object inside another object or a global: container gets ownership. putting a value inside a container requires its umd to match the parent, and from then on, the umd for that is also inherited from the parent (as in if a parent m is casted to d, so is the child heap object) +> +> gc: free lists + mark-sweep (Recommended) +> +> where the compiler lives: resident in vm code. bootstrap it step by step, by writing a c-like in core.c and then rewriting the compiler inside the c-like. also bring back llvm if you think it'll make your job easier +> +> homoiconic features: parse words, quoted code blocks, inline stack code, patchable compiler tables +> +> how an inner block hands a u object up: expression and assign, both +> +> when the analyzer runs: inside the compiler but can be turned off if the code requests it + +**summary:** the genome is now zc, a typeless c-like language, described above. stage 0 is `zc0.c` (kept as its own file next to core.c rather than inside it, so the runtime still has one native file), and the resident compiler is `src/compiler.zc` plus `src/rt.zc`, which reached a byte-identical fixpoint compiling itself. llvm wasn't brought back, since the target is the vm itself. memory went to 1m words, and core.c gained an interpreter for non-arm64 hosts. calls i made without asking: tags go on signatures (`fn f(m a, d b) -> u`) and recasts are `x as u` / `x as d`; a d object touched inside an inner block moves into that block; storing into a borrowed object, a global or untracked memory needs `as d`; handing a u up by assignment only reaches the directly enclosing block; unchecked code gives its objects to the collector. the old asm genome moved to `legacy/`. + +### 11 + +> progress report? +> add types please :) +> mostly as a compiler primitive though, plus type-inspection via reflection +> and a small optional typechecker (default off) +> oh and keep the stuff you write as a history scrapbook +> if it helps, add a path to the hardware which is called "virtual enhancements", and allows up to 512 custom operations to be compiled down via jit into hardware-level speeds and interoped directly into a fast path in the vm to become essentially custom wiring. useful for division and other stuff as a dynamic runtime path + +my answers to the clarifying questions, exactly: + +> types: words + structs + generics +> +> reflection: runtime type of heap objects, function signatures, typeof at compile time +> +> how an enhancement is defined: either actual C or llvm ir code or vm code or micro-ops passed through an optimizer. since for example, micro-ops and vm code are not sufficient for optimizing things like division +> +> scrapbook: notes + code snapshots + +**summary:** the progress report said the previous round was finished and shipped. i then laid out a plan where the core would compile c or llvm ir enhancement sources at runtime with the host toolchain and load the result. part of that reply was stopped by a safety check before it finished, so i stopped and checked in instead of building it. + +### 12 + +> also make the umd syntax like `type.u name`, and casting is multiplication `u*name` +> ideally i'd prefer memory to be unlimited words growing until system oom, but 1m is fine +> core.c plain interp is also nice +> zc0.c own file is fine +> i mentioned llvm for portability to non-arm targets +> yes, d object semantic that was intended +> u object hands up one block and becomes an m, that's fine. the code can make it a u or a d or whatever +> explain your gaps more +> +> wait why safety check? what's wrong with that + +my answers to the clarifying questions, exactly: + +> declarations: c-style, var -> let +> +> tags on untyped values and the names u, m, d: names reserved, bare .u allowed, untype is m by default +> +> llvm: llvm jit backend (Recommended) + +**summary:** on the safety check: nothing was wrong with the request. my best guess was that it tripped on the plan for a program inside the vm handing source text to the system compiler and loading the result into the running process, since that pattern also shows up in malware. with llvm meant for portability, that path wasn't needed. the gaps got the longer explanations that are now under open ends above. then the round itself: + +**syntax.** declarations are c-style (`int x`, `*node.m p`), `let` replaces `var` for untyped and inferred declarations, tags are `.u .m .d` after the type or on their own, recasts are `u*x` / `d*x` / `m*x`, `u m d` are reserved, and an untagged parameter counts as an m lend. + +**types.** a compiler primitive: hash-consed records with structs, generic structs and functions, fixed arrays, function types, `typeof`/`sizeof`, `as` casts and a reflection api, with object headers carrying their type. the typechecker is off by default and turned on with `typecheck on;`. + +**virtual enhancements.** opcode 21 installs, 22 to 533 are the slots. micro-op recipes and lifted vm code go through an optimizer in the core (folding, cse, dce, multiply-high division by constants) and get inlined by both jits. the language's own `/ % <<` now run on them. + +**backends.** a tiered llvm backend (`-DZYLLVM`) for non-arm hosts. + +**bootstrap and history.** it still reaches its fixpoint on all three backends. `history/` holds notes for every stage and code from stages 7, 10 and this one (in 11-types-enhancements). diff --git a/docs/history/11-types-enhancements/code/core.c b/docs/history/11-types-enhancements/code/core.c new file mode 100644 index 0000000..49cc6cc --- /dev/null +++ b/docs/history/11-types-enhancements/code/core.c @@ -0,0 +1,1157 @@ +/* zygote core: puts the genome (the zc compiler and its runtime, both vm code) + into memory and runs it. on arm64, every block of vm code is translated to + arm64 the first time it is reached, straight from live memory. stores that + land on translated words throw those translations away, so code that + rewrites itself, including the resident compiler, just gets retranslated + on the next visit. on other hosts (or with -DZYINTERP) a plain interpreter + with the same semantics runs instead. ZYTRACE=1 logs translations on stderr. */ +#include +#include +#include +#if defined(__aarch64__) && !defined(ZYINTERP) +#define ZYJIT 1 +#include +#ifdef __APPLE__ +#include +#include +#endif +#endif +#include "genome.h" + +#ifndef JTYPE +#define JTYPE char +#endif +char Q(void); +void G(char); +extern JTYPE J; + +#define MW (1 << 20) /* memory, in words */ +#define DS0 (MW - 16) /* data stack grows down from here */ +#define RS0 0xF0000 /* return stack grows up from here */ +#define CODEW (4 << 20) /* 16 MB of code, in instruction words */ +#define MAXB 65536 +#define BLKW 1024 /* longest block, in vm words */ + +static int m[MW]; +static int trace; + +/* ---- virtual enhancements ---- + up to 512 custom opcodes (22 .. 533), wired in at runtime by the install + opcode (21). an enhancement is a short program over native micro-ops, + written either as a recipe (text) or as straight-line vm code that gets + lifted. both become ssa, go through the optimizer below, and then run in + the interpreter or get inlined by the jit as native code. */ +#define NENH 512 +#define XOP0 22 +enum { U_LI, U_MOV, U_ADD, U_SUB, U_MUL, U_SDIV, U_UDIV, U_SREM, U_UREM, U_AND, U_OR, U_XOR, + U_SHL, U_SHR, U_SAR, U_NEG, U_NOT, U_LT, U_LTU, U_EQ, U_NE, U_MIN, U_MAX, U_MINU, U_MAXU, + U_MULHS, U_MULHU, U_CLZ, U_ABS, U_SEL, U_LD, U_ST, U_N }; +static const char *unames[U_N] = { "li", "mov", "add", "sub", "mul", "sdiv", "udiv", "srem", "urem", + "and", "or", "xor", "shl", "shr", "sar", "neg", "not", "lt", "ltu", "eq", "ne", "min", "max", + "minu", "maxu", "mulhs", "mulhu", "clz", "abs", "sel", "ld", "st" }; +static const signed char uarity[U_N] = { 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 1, 3, 1, 2 }; +typedef struct { int op, d, a, b, c, imm; } Uop; /* ssa: d is a fresh value, a b c are values */ +typedef struct { + int live, kind, nin, nout, n, nv, out[8]; + Uop *u; + signed char *reg; /* physical register per value, for the jit; 0 if it didn't fit */ +} Enh; +static Enh enh[NENH]; +static int need_flush; + +#define MAXUOP 4096 +static Uop ub[MAXUOP]; static int un, unv, uerr; /* the program being built */ +static int unew(int op, int a, int b, int c, int imm) +{ + Uop *u; + if (un >= MAXUOP) { uerr = -5; return 0; } + u = &ub[un++]; u->op = op; u->d = unv++; u->a = a; u->b = b; u->c = c; u->imm = imm; + return u->d; +} +static int mread(int a) { return (unsigned)a < MW ? m[a] : 0; } + +/* recipe text: one statement per line or per ';' + r0 = sdiv r0 r1 dst = op operands, operands are rN, parameter names or numbers + r2 = r0 a copy, or a number + st r1 r0 memory[r1] = r0 + inputs arrive in r0.. (and under the parameter names), outputs leave from r0.. */ +static int parse_recipe(int src, int names, int nin, int nout, int *out) +{ + int cur[16], i, p = src, n, k, op, args[3], na, dst, isst; + char tok[8][32]; int nt; + for (i = 0; i < 16; i++) cur[i] = i < nin ? i : -1; + for (;;) { + /* read one statement into tokens */ + nt = 0; + for (;;) { + int c = mread(p); + while (c == ' ' || c == '\t' || c == '\r') c = mread(++p); + if (!c || c == '\n' || c == ';') break; + if (c == '=') { if (nt < 8) strcpy(tok[nt++], "="); p++; continue; } + n = 0; + while (c && c != ' ' && c != '\t' && c != '\n' && c != ';' && c != '=' && c != '\r') { if (n < 31) tok[nt < 8 ? nt : 7][n++] = (char)c; c = mread(++p); } + if (nt < 8) tok[nt++][n] = 0; + } + if (nt) { + isst = !strcmp(tok[0], "st"); + if (!isst && (nt < 3 || strcmp(tok[1], "="))) return -2; + /* destination register */ + dst = -1; + if (!isst) { + if (tok[0][0] == 'r' && tok[0][1] >= '0' && tok[0][1] <= '9') dst = atoi(tok[0] + 1); + else for (i = 0; i < nin && names; i++) { + int s = mread(names + i), j = 0; + while (mread(s + j) && mread(s + j) == (unsigned char)tok[0][j]) j++; + if (!mread(s + j) && !tok[0][j]) dst = i; + } + if (dst < 0 || dst > 15) return -2; + } + k = isst ? 1 : 2; /* first token after "=" (or after st) */ + op = -1; + if (!isst && nt > 3) { for (i = 0; i < U_N; i++) if (!strcmp(tok[k], unames[i])) op = i; if (op < 0) return -2; k++; } + if (isst) op = U_ST; + na = 0; + for (; k < nt; k++) { + char *t = tok[k], *e; int v = -1; + if (na >= 3) return -2; + if (t[0] == 'r' && t[1] >= '0' && t[1] <= '9') { int r = atoi(t + 1); if (r > 15) return -2; if (cur[r] < 0) cur[r] = unew(U_LI, 0, 0, 0, 0); v = cur[r]; } + else if ((t[0] >= '0' && t[0] <= '9') || t[0] == '-') { long x = strtol(t, &e, 0); if (*e) return -2; v = unew(U_LI, 0, 0, 0, (int)x); } + else { + for (i = 0; i < nin && names; i++) { + int s = mread(names + i), j = 0; + while (mread(s + j) && mread(s + j) == (unsigned char)t[j]) j++; + if (!mread(s + j) && !t[j]) v = cur[i] < 0 ? (cur[i] = unew(U_LI, 0, 0, 0, 0)) : cur[i]; + } + if (v < 0) return -2; + } + args[na++] = v; + } + if (op < 0) { /* x = y */ + if (na != 1) return -2; + cur[dst] = args[0]; + } else { + if (na != (op == U_LI ? 0 : uarity[op])) { + if (!(op == U_LI && na == 1)) return -2; + } + if (op == U_LI) cur[dst] = args[0]; + else if (op == U_ST) unew(U_ST, args[0], args[1], 0, 0); + else cur[dst] = unew(op, args[0], na > 1 ? args[1] : 0, na > 2 ? args[2] : 0, 0); + } + if (uerr) return uerr; + } + if (!mread(p)) break; + p++; + } + for (i = 0; i < nout; i++) { if (cur[i] < 0) cur[i] = unew(U_LI, 0, 0, 0, 0); out[i] = cur[i]; } + return uerr; +} + +/* straight-line vm code from addr up to its ret, run symbolically */ +static int lift_vm(int a, int len, int nin, int nout, int *out) +{ + int st[256], sp = 0, i, w, k, x, y; + for (i = 0; i < nin; i++) st[sp++] = i; + for (i = 0; i < len; i++) { + w = mread(a + i); + if (w & 1) { if (sp >= 256) return -4; st[sp++] = unew(U_LI, 0, 0, 0, w >> 1); continue; } + k = w / 2; + if (k == 9) break; + switch (k) { + case 1: if (sp < 1) return -4; st[sp - 1] = unew(U_LD, st[sp - 1], 0, 0, 0); break; + case 2: if (sp < 2) return -4; unew(U_ST, st[sp - 1], st[sp - 2], 0, 0); sp -= 2; break; + case 3: /* pick needs a constant index */ + if (sp < 1) return -4; + x = st[sp - 1]; + if (x < nin) return -4; + { int j; for (j = 0; j < un && ub[j].d != x; j++); if (j == un || ub[j].op != U_LI) return -4; y = ub[j].imm; } + if (y < 0 || y >= sp - 1) return -4; + st[sp - 1] = st[sp - 2 - y]; + break; + case 4: if (sp < 1) return -4; sp--; break; + case 5: if (sp < 2) return -4; x = st[sp - 1]; st[sp - 1] = st[sp - 2]; st[sp - 2] = x; break; + case 13: case 14: case 15: case 16: case 17: case 18: case 19: case 20: { + static const int map[] = { U_ADD, U_SUB, U_MUL, U_AND, U_OR, U_XOR, U_SAR, U_LT }; + if (sp < 2) return -4; + x = st[sp - 1]; y = st[sp - 2]; sp -= 2; + st[sp++] = unew(map[k - 13], y, x, 0, 0); + break; } + default: return -4; /* control flow, i/o and other enhancements don't lift */ + } + if (uerr) return uerr; + } + if (sp != nout) return -4; + for (i = 0; i < nout; i++) out[i] = st[i]; + return uerr; +} + +/* ---- the optimizer ---- + one forward pass of copy propagation, constant folding, algebraic + simplification, division by constants (the multiply-high trick) and + common subexpression elimination, then dead code removal. */ +static int ev(int op, int a, int b, int c) +{ + unsigned ua = (unsigned)a, ub_ = (unsigned)b; + switch (op) { + case U_ADD: return (int)(ua + ub_); + case U_SUB: return (int)(ua - ub_); + case U_MUL: return (int)(ua * ub_); + case U_SDIV: return b == 0 ? 0 : (a == (int)0x80000000 && b == -1) ? a : a / b; + case U_UDIV: return b == 0 ? 0 : (int)(ua / ub_); + case U_SREM: return b == 0 ? a : (a == (int)0x80000000 && b == -1) ? 0 : a % b; + case U_UREM: return b == 0 ? a : (int)(ua % ub_); + case U_AND: return a & b; + case U_OR: return a | b; + case U_XOR: return a ^ b; + case U_SHL: return (int)(ua << (b & 31)); + case U_SHR: return (int)(ua >> (b & 31)); + case U_SAR: return a >> (b & 31); + case U_NEG: return (int)(0u - ua); + case U_NOT: return ~a; + case U_LT: return a < b; + case U_LTU: return ua < ub_; + case U_EQ: return a == b; + case U_NE: return a != b; + case U_MIN: return a < b ? a : b; + case U_MAX: return a > b ? a : b; + case U_MINU: return ua < ub_ ? a : b; + case U_MAXU: return ua > ub_ ? a : b; + case U_MULHS: return (int)(((long long)a * (long long)b) >> 32); + case U_MULHU: return (int)(((unsigned long long)ua * ub_) >> 32); + case U_CLZ: { int n = 0; if (!ua) return 32; while (!(ua & 0x80000000u)) { ua <<= 1; n++; } return n; } + case U_ABS: return a < 0 ? (int)(0u - ua) : a; + case U_SEL: return a ? b : c; + } + return 0; +} +static void smagic(int d, int *mul, int *sh) /* hacker's delight, signed */ +{ + const unsigned two31 = 0x80000000u; + unsigned ad = d < 0 ? 0u - (unsigned)d : (unsigned)d, t = two31 + ((unsigned)d >> 31); + unsigned anc = t - 1 - t % ad, q1 = two31 / anc, r1 = two31 - q1 * anc, q2 = two31 / ad, r2 = two31 - q2 * ad, delta; + int p = 31; + do { + p++; + q1 *= 2; r1 *= 2; if (r1 >= anc) { q1++; r1 -= anc; } + q2 *= 2; r2 *= 2; if (r2 >= ad) { q2++; r2 -= ad; } + delta = ad - r2; + } while (q1 < delta || (q1 == delta && r1 == 0)); + *mul = (int)(q2 + 1); if (d < 0) *mul = -*mul; + *sh = p - 32; +} +static void umagic(unsigned d, unsigned *mul, int *add, int *sh) /* hacker's delight, unsigned */ +{ + unsigned nc = 0xFFFFFFFFu - (0u - d) % d, q1, r1, q2, r2, delta; + int p = 31; + *add = 0; + q1 = 0x80000000u / nc; r1 = 0x80000000u - q1 * nc; + q2 = 0x7FFFFFFFu / d; r2 = 0x7FFFFFFFu - q2 * d; + do { + p++; + if (r1 >= nc - r1) { q1 = 2 * q1 + 1; r1 = 2 * r1 - nc; } else { q1 = 2 * q1; r1 = 2 * r1; } + if (r2 + 1 >= d - r2) { if (q2 >= 0x7FFFFFFFu) *add = 1; q2 = 2 * q2 + 1; r2 = 2 * r2 + 1 - d; } + else { if (q2 >= 0x80000000u) *add = 1; q2 = 2 * q2; r2 = 2 * r2 + 1; } + delta = d - 1 - r2; + } while (p < 64 && (q1 < delta || (q1 == delta && r1 == 0))); + *mul = q2 + 1; *sh = p - 32; +} + +static Uop ob[MAXUOP]; static int on, onv; +static int *omap, *isk, *kv; /* old value -> new value; constness of new values */ +static int pure(int op) { return op != U_LD && op != U_ST; } +static int ispow2(unsigned x) { return x && !(x & (x - 1)); } +static int lg2(unsigned x) { int n = 0; while (x > 1) { x >>= 1; n++; } return n; } +static int oe(int op, int a, int b, int c, int imm); +static int K(int v) { return oe(U_LI, 0, 0, 0, v); } +static int raw(int op, int a, int b, int c, int imm) +{ + Uop *u; int i; + if (pure(op)) /* cse */ + for (i = 0; i < on; i++) { + u = &ob[i]; + if (u->op == op && u->a == a && u->b == b && u->c == c && u->imm == imm) return u->d; + } + if (on >= MAXUOP) { uerr = -5; return 0; } + u = &ob[on++]; u->op = op; u->d = onv++; u->a = a; u->b = b; u->c = c; u->imm = imm; + isk[u->d] = op == U_LI; kv[u->d] = imm; + return u->d; +} +static int sdivk(int a, int d) /* a / d for a constant d, no divide instruction */ +{ + int mul, sh, q, t; + if (d == 1) return a; + if (d == -1) return oe(U_NEG, a, 0, 0, 0); + if (d == (int)0x80000000) return oe(U_EQ, a, K(d), 0, 0); + if (ispow2((unsigned)(d < 0 ? -d : d))) { + int k = lg2((unsigned)(d < 0 ? -d : d)); + t = oe(U_SAR, a, K(31), 0, 0); + t = oe(U_SHR, t, K(32 - k), 0, 0); + q = oe(U_SAR, oe(U_ADD, a, t, 0, 0), K(k), 0, 0); + return d < 0 ? oe(U_NEG, q, 0, 0, 0) : q; + } + smagic(d, &mul, &sh); + q = oe(U_MULHS, a, K(mul), 0, 0); + if (d > 0 && mul < 0) q = oe(U_ADD, q, a, 0, 0); + if (d < 0 && mul > 0) q = oe(U_SUB, q, a, 0, 0); + if (sh) q = oe(U_SAR, q, K(sh), 0, 0); + return oe(U_ADD, q, oe(U_SHR, q, K(31), 0, 0), 0, 0); +} +static int udivk(int a, unsigned d) +{ + unsigned mul; int add, sh, t; + if (d == 1) return a; + if (ispow2(d)) return oe(U_SHR, a, K(lg2(d)), 0, 0); + if (d > 0x80000000u) return oe(U_XOR, oe(U_LTU, a, K((int)d), 0, 0), K(1), 0, 0); /* a >= d */ + umagic(d, &mul, &add, &sh); + t = oe(U_MULHU, a, K((int)mul), 0, 0); + if (!add) return sh ? oe(U_SHR, t, K(sh), 0, 0) : t; + t = oe(U_ADD, oe(U_SHR, oe(U_SUB, a, t, 0, 0), K(1), 0, 0), t, 0, 0); + return sh > 1 ? oe(U_SHR, t, K(sh - 1), 0, 0) : t; +} +static int oe(int op, int a, int b, int c, int imm) +{ + int ka, kb, A, B; + if (op == U_LI) return raw(U_LI, 0, 0, 0, imm); + if (op == U_MOV) return a; + if (op == U_LD || op == U_ST) return raw(op, a, b, 0, 0); + ka = isk[a]; kb = uarity[op] > 1 && isk[b]; + A = kv[a]; B = kv[b]; + /* everything constant: fold */ + if (ka && (uarity[op] < 2 || kb) && (uarity[op] < 3 || isk[c])) return K(ev(op, A, B, kv[c])); + /* constants go right for commutative ops */ + if (ka && !kb && (op == U_ADD || op == U_MUL || op == U_AND || op == U_OR || op == U_XOR || op == U_EQ || op == U_NE || + op == U_MIN || op == U_MAX || op == U_MINU || op == U_MAXU || op == U_MULHS || op == U_MULHU)) { + int t = a; a = b; b = t; kb = 1; B = A; ka = 0; + } + if (op == U_SEL && ka) return A ? b : c; + if (op == U_SEL && b == c) return b; + if (kb) switch (op) { + case U_ADD: case U_SUB: case U_OR: case U_XOR: case U_SHL: case U_SHR: case U_SAR: + if (!B || (op >= U_SHL && op <= U_SAR && !(B & 31))) return a; + break; + case U_MUL: + if (B == 0) return K(0); + if (B == 1) return a; + if (B == -1) return oe(U_NEG, a, 0, 0, 0); + if (ispow2((unsigned)B)) return oe(U_SHL, a, K(lg2((unsigned)B)), 0, 0); + break; + case U_AND: if (B == 0) return K(0); if (B == -1) return a; break; + case U_SDIV: if (B == 0) return K(0); return sdivk(a, B); + case U_SREM: if (B == 0) return a; if (B == 1 || B == -1) return K(0); + return oe(U_SUB, a, oe(U_MUL, sdivk(a, B), b, 0, 0), 0, 0); + case U_UDIV: if (B == 0) return K(0); return udivk(a, (unsigned)B); + case U_UREM: if (B == 0) return a; + if (ispow2((unsigned)B)) return oe(U_AND, a, K(B - 1), 0, 0); + return oe(U_SUB, a, oe(U_MUL, udivk(a, (unsigned)B), b, 0, 0), 0, 0); + } + if (a == b && uarity[op] == 2) { + if (op == U_SUB || op == U_XOR || op == U_LT || op == U_LTU || op == U_NE) return K(0); + if (op == U_AND || op == U_OR || op == U_MIN || op == U_MAX || op == U_MINU || op == U_MAXU) return a; + if (op == U_EQ) return K(1); + } + return raw(op, a, b, c, 0); +} +/* runs the optimizer over ub[0..un) with nin inputs; leaves the result in e */ +static int optimize(Enh *e, int nin, int *out, int nout) +{ + int i, *live, n = 0; + static int omap_[MAXUOP + 16], isk_[MAXUOP * 4], kv_[MAXUOP * 4]; + omap = omap_; isk = isk_; kv = kv_; + on = 0; onv = nin; + for (i = 0; i < nin; i++) { omap[i] = i; isk[i] = 0; } + for (i = 0; i < un && !uerr; i++) { + Uop *u = &ub[i]; + int a = uarity[u->op] > 0 || u->op == U_ST || u->op == U_LD ? omap[u->a] : 0; + int b = uarity[u->op] > 1 || u->op == U_ST ? omap[u->b] : 0; + int c = uarity[u->op] > 2 ? omap[u->c] : 0; + if (u->op == U_ST) oe(U_ST, a, b, 0, 0); + else omap[u->d] = oe(u->op, a, b, c, u->imm); + } + if (uerr) return uerr; + /* dead code: outputs and stores are live */ + live = calloc(onv + 1, sizeof *live); + for (i = 0; i < nout; i++) { e->out[i] = omap[out[i]]; live[e->out[i]] = 1; } + for (i = on; i--;) { + Uop *u = &ob[i]; + if (u->op != U_ST && !live[u->d]) { u->op = -1; continue; } + if (uarity[u->op] > 0 || u->op == U_ST || u->op == U_LD) live[u->a] = 1; + if (uarity[u->op] > 1 || u->op == U_ST) live[u->b] = 1; + if (uarity[u->op] > 2) live[u->c] = 1; + } + free(live); + e->u = malloc((on + 1) * sizeof(Uop)); + for (i = 0; i < on; i++) if (ob[i].op >= 0) e->u[n++] = ob[i]; + e->n = n; e->nv = onv; e->nin = nin; e->nout = nout; + return 0; +} +/* run an installed enhancement on a downward-growing stack; returns the new top */ +static int *xexec(Enh *e, int *sp) +{ + int vb[256], *v = e->nv <= 256 ? vb : malloc(e->nv * sizeof(int)), i, j; + for (j = 0; j < e->nin; j++) v[j] = sp[e->nin - 1 - j]; + for (i = 0; i < e->n; i++) { + Uop *u = &e->u[i]; + switch (u->op) { + case U_LI: v[u->d] = u->imm; break; + case U_LD: v[u->d] = mread(v[u->a]); break; + case U_ST: if ((unsigned)v[u->a] < MW) m[v[u->a]] = v[u->b]; break; + default: v[u->d] = ev(u->op, v[u->a], v[u->b], v[u->c]); + } + } + sp += e->nin - e->nout; + for (j = 0; j < e->nout; j++) sp[e->nout - 1 - j] = v[e->out[j]]; + if (v != vb) free(v); + return sp; +} +static void xdump(int slot, Enh *e) +{ + int i; + fprintf(stderr, "enh %d: %d in, %d out, %d ops\n", slot, e->nin, e->nout, e->n); + for (i = 0; i < e->n; i++) { + Uop *u = &e->u[i]; + if (u->op == U_ST) fprintf(stderr, " st v%d v%d\n", u->a, u->b); + else if (u->op == U_LI) fprintf(stderr, " v%d = %d\n", u->d, u->imm); + else { fprintf(stderr, " v%d = %s v%d", u->d, unames[u->op], u->a); if (uarity[u->op] > 1) fprintf(stderr, " v%d", u->b); if (uarity[u->op] > 2) fprintf(stderr, " v%d", u->c); fprintf(stderr, "\n"); } + } + for (i = 0; i < e->nout; i++) fprintf(stderr, " out%d = v%d\n", i, e->out[i]); +} +/* registers for the jit: values live in w0..w15, straight-line linear scan. + if they don't fit, reg stays 0 and the jit calls the interpreter instead. */ +static void regalloc(Enh *e) +{ + int *last = malloc(e->nv * sizeof(int)), i, j, k, fr = 0xffff; + signed char *r = malloc(e->nv); + for (i = 0; i < e->nv; i++) { last[i] = -2; r[i] = -1; } + for (i = 0; i < e->n; i++) { + Uop *u = &e->u[i]; + if (uarity[u->op] > 0 || u->op == U_ST || u->op == U_LD) last[u->a] = i; + if (uarity[u->op] > 1 || u->op == U_ST) last[u->b] = i; + if (uarity[u->op] > 2) last[u->c] = i; + } + for (i = 0; i < e->nout; i++) last[e->out[i]] = e->n; +#define TAKE(v) do { for (k = 0; k < 16 && !(fr >> k & 1); k++); if (k == 16) goto fail; fr &= ~(1 << k); r[v] = (signed char)k; } while (0) + for (j = 0; j < e->nin; j++) if (last[j] >= -1 && last[j] != -2) TAKE(j); + for (i = 0; i < e->n; i++) { + Uop *u = &e->u[i]; int ops[3], no = 0; + if (uarity[u->op] > 0 || u->op == U_ST || u->op == U_LD) ops[no++] = u->a; + if (uarity[u->op] > 1 || u->op == U_ST) ops[no++] = u->b; + if (uarity[u->op] > 2) ops[no++] = u->c; + for (j = 0; j < no; j++) if (last[ops[j]] == i && r[ops[j]] >= 0 && (fr >> r[ops[j]] & 1) == 0) fr |= 1 << r[ops[j]]; + if (u->op != U_ST) TAKE(u->d); + if (u->op != U_ST && last[u->d] == -2) fr |= 1 << r[u->d]; /* never used */ + } +#undef TAKE + free(last); e->reg = r; return; +fail: + free(last); free(r); e->reg = 0; +} +/* the install opcode. descriptor: slot, kind (0 recipe, 1 vm code), inputs, + outputs, payload address, payload length, parameter names (or 0). + returns 0, or -1 bad descriptor, -2 recipe syntax, -4 code doesn't lift, + -5 too big. */ +static int install(int d) +{ + int slot = mread(d), kind = mread(d + 1), nin = mread(d + 2), nout = mread(d + 3); + int pay = mread(d + 4), len = mread(d + 5), names = mread(d + 6), out[8], r; + Enh ne; + if ((unsigned)slot >= NENH || (unsigned)nin > 8 || (unsigned)nout > 8 || (kind != 0 && kind != 1)) return -1; + un = 0; unv = nin; uerr = 0; + r = kind == 0 ? parse_recipe(pay, names, nin, nout, out) : lift_vm(pay, len, nin, nout, out); + if (r) { if (trace) fprintf(stderr, "enh %d: install failed (%d)\n", slot, r); return r; } + memset(&ne, 0, sizeof ne); + if ((r = optimize(&ne, nin, out, nout))) return r; + ne.kind = kind; ne.live = 1; + regalloc(&ne); + if (enh[slot].live) { free(enh[slot].u); free(enh[slot].reg); } + enh[slot] = ne; + need_flush = 1; + if (trace) xdump(slot, &enh[slot]); + return 0; +} +#ifdef ZYJIT +static unsigned char cov[MW]; /* 1 if a live translation covers the word */ +static void *tab[MW]; /* vm address -> translation */ +static struct { int a, b; } bl[MAXB]; /* live translations cover [a,b); a < 0 is dead */ +static int nbl; +static unsigned *code, *cp, *x_enter, *x_exit, *x_disp, *x_first; +static long S[11]; /* m, s, r, tab, cov, Q, G, &J, store hook, install, enhancement fallback */ + +enum { W0 = 0, W1 = 1, X9 = 9, X10, X11, M = 19, SP = 20, RP = 21, TAB = 22, ST = 23 }; + +static void E(unsigned x) { *cp++ = x; } +static void li(int d, int v) +{ + unsigned u = (unsigned)v; + E(0x52800000 | (u & 0xffff) << 5 | d); + if (u >> 16) E(0x72a00000 | (u >> 16) << 5 | d); +} +static void ldr_post(int t, int n, int i) { E(0xb8400400 | (i & 0x1ff) << 12 | n << 5 | t); } +static void ldr_pre(int t, int n, int i) { E(0xb8400c00 | (i & 0x1ff) << 12 | n << 5 | t); } +static void str_pre(int t, int n, int i) { E(0xb8000c00 | (i & 0x1ff) << 12 | n << 5 | t); } +static void str_post(int t, int n, int i) { E(0xb8000400 | (i & 0x1ff) << 12 | n << 5 | t); } +static void ldr_u(int t, int n, int o) { E(0xb9400000 | (o / 4) << 10 | n << 5 | t); } +static void str_u(int t, int n, int o) { E(0xb9000000 | (o / 4) << 10 | n << 5 | t); } +static void ldrx_u(int t, int n, int o) { E(0xf9400000 | (o / 8) << 10 | n << 5 | t); } +static void ldr_rr(int t, int n, int r, int ext) { E(0xb8600800 | r << 16 | ext << 13 | 1 << 12 | n << 5 | t); } +static void str_rr(int t, int n, int r, int ext) { E(0xb8200800 | r << 16 | ext << 13 | 1 << 12 | n << 5 | t); } +static void ldp_post(int t, int u, int n, int i) { E(0x28c00000 | ((i / 4) & 0x7f) << 15 | u << 10 | n << 5 | t); } +static void op3(unsigned op, int d, int n, int r) { E(op | r << 16 | n << 5 | d); } +static void addx_ext(int d, int n, int r, int ext) { E(0x8b200000 | r << 16 | ext << 13 | 2 << 10 | n << 5 | d); } +static void movw(int d, int r) { E(0x2a0003e0 | r << 16 | d); } +static void blr(int n) { E(0xd63f0000 | n << 5); } +static void b_to(unsigned *t) { long o = t - cp; E(0x14000000 | (o & 0x3ffffff)); } +static void cb_fix(unsigned *at, unsigned *to) { *at |= ((to - at) & 0x7ffff) << 5; } +#define SXTW 6 +#define UXTW 2 + +static void build_stubs(void) +{ + cp = code; + x_enter = cp; /* int enter(long *S, void *code) */ + E(0xa9bc7bfd); /* stp x29, x30, [sp, #-64]! */ + E(0x910003fd); /* mov x29, sp */ + E(0xa90153f3); /* stp x19, x20, [sp, #16] */ + E(0xa9025bf5); /* stp x21, x22, [sp, #32] */ + E(0xf9001bf7); /* str x23, [sp, #48] */ + E(0xaa0003f7); /* mov x23, x0 */ + ldrx_u(M, ST, 0); + ldr_u(X9, ST, 8); addx_ext(SP, M, X9, UXTW); + ldr_u(X9, ST, 16); addx_ext(RP, M, X9, UXTW); + ldrx_u(TAB, ST, 24); + E(0xd61f0020); /* br x1 */ + x_exit = cp; /* w0 = next vm address */ + op3(0xcb000000, X9, SP, M); E(0xd342fd29); str_u(X9, ST, 8); /* s = (x20 - x19) >> 2 */ + op3(0xcb000000, X9, RP, M); E(0xd342fd29); str_u(X9, ST, 16); + E(0xa94153f3); /* ldp x19, x20, [sp, #16] */ + E(0xa9425bf5); /* ldp x21, x22, [sp, #32] */ + E(0xf9401bf7); /* ldr x23, [sp, #48] */ + E(0xa8c47bfd); /* ldp x29, x30, [sp], #64 */ + E(0xd65f03c0); /* ret */ + x_disp = cp; /* w0 = target: chain through tab[] or fall back to c */ + E(0x7144001f); /* cmp w0, #MW */ + E(0x54000002 | ((x_exit - cp) & 0x7ffff) << 5); /* b.hs exit */ + E(0xf8605ac9); /* ldr x9, [x22, w0, uxtw #3] */ + E(0xb4000009 | ((x_exit - cp) & 0x7ffff) << 5); /* cbz x9, exit */ + E(0xd61f0120); /* br x9 */ + x_first = cp; +} + +static void flush(void) +{ + memset(tab, 0, sizeof tab); + memset(cov, 0, sizeof cov); + nbl = 0; + cp = x_first; + if (trace) fprintf(stderr, "jit: flushed\n"); +} + +static void jitwrite(int on) +{ +#ifdef __APPLE__ + pthread_jit_write_protect_np(!on); +#else + (void)on; +#endif +} + +static void syncicache(unsigned *a, unsigned *b) +{ +#ifdef __APPLE__ + sys_icache_invalidate(a, (char *)b - (char *)a); +#else + __builtin___clear_cache((char *)a, (char *)b); +#endif +} + +/* store hook for words that are covered by a translation */ +static int hook(int a, int v) +{ + int j, x, hit = 0; + if (m[a] == v) return 0; + m[a] = v; + for (j = 0; j < nbl; j++) + if (bl[j].a >= 0 && bl[j].a <= a && a < bl[j].b) { + tab[bl[j].a] = 0; + for (x = bl[j].a; x < bl[j].b; x++) cov[x] = 0; + if (trace) fprintf(stderr, "jit: dropped %d..%d\n", bl[j].a, bl[j].b - 1); + bl[j].a = -1; + hit = 1; + } + for (j = 0; j < nbl; j++) + if (bl[j].a >= 0) for (x = bl[j].a; x < bl[j].b; x++) cov[x] = 1; + return hit; +} + +static void op(int i, int k) /* everything except control flow */ +{ + unsigned *a, *b, *c, *z; + switch (k) { + case 1: ldr_u(X9, SP, 0); ldr_rr(X9, M, X9, SXTW); str_u(X9, SP, 0); return; + case 2: /* store: inline unless the target word is translated */ + ldp_post(X9, X10, SP, 8); + E(0x7144013f); /* cmp w9, #MW */ + a = cp; E(0x54000002); /* b.hs done */ + ldrx_u(X11, ST, 32); + E(0x3869496b); /* ldrb w11, [x11, w9, uxtw] */ + b = cp; E(0x3500000b); /* cbnz w11, slow */ + str_rr(X10, M, X9, UXTW); + c = cp; E(0x14000000); /* b done */ + cb_fix(b, cp); /* slow: hook(a, v) */ + movw(W0, X9); movw(W1, X10); ldrx_u(X11, ST, 64); blr(X11); + z = cp; E(0x34000000); /* cbz w0, done */ + li(W0, i + 1); b_to(x_exit); /* a translation died: resume at i+1 in c */ + cb_fix(a, cp); cb_fix(z, cp); + *c |= (cp - c) & 0x3ffffff; + return; + case 3: ldr_u(X9, SP, 0); E(0x9100128a); ldr_rr(X9, X10, X9, SXTW); str_u(X9, SP, 0); return; /* add x10, x20, #4 */ + case 4: E(0x91001294); return; /* add x20, x20, #4 */ + case 5: E(0x29402a89); E(0x2900268a); return; /* ldp w9, w10, [x20]; stp w10, w9, [x20] */ + case 10: ldrx_u(X11, ST, 40); blr(X11); E(0x13001c09); str_pre(X9, SP, -4); return; /* sxtb w9, w0 */ + case 11: + ldrx_u(X9, ST, 56); + if (sizeof J == 1) E(0x39400129); else ldr_u(X9, X9, 0); /* ldrb w9, [x9] */ + str_pre(X9, SP, -4); + return; + case 12: ldr_post(W0, SP, 4); E(0x13001c00); ldrx_u(X11, ST, 48); blr(X11); return; /* sxtb w0, w0 */ + } + ldr_post(X9, SP, 4); + ldr_u(X10, SP, 0); + switch (k) { + case 13: op3(0x0b000000, X10, X10, X9); break; + case 14: op3(0x4b000000, X10, X10, X9); break; + case 15: op3(0x1b007c00, X10, X10, X9); break; + case 16: op3(0x0a000000, X10, X10, X9); break; + case 17: op3(0x2a000000, X10, X10, X9); break; + case 18: op3(0x4a000000, X10, X10, X9); break; + case 19: op3(0x1ac02800, X10, X10, X9); break; + default: E(0x6b09015f); E(0x1a9fa7ea); /* cmp w10, w9; cset w10, lt */ + } + str_u(X10, SP, 0); +} + +/* an enhancement, inlined: inputs from the data stack into w0..w15, the + optimized micro-ops, outputs back. x16 and x17 are scratch. */ +static void emit_enh(int s) +{ + Enh *e = &enh[s]; + signed char *r = e->reg; + int i, j, delta; + if (!r) { /* didn't fit in registers: x0 = data stack, w1 = slot, call the interpreter */ + E(0xaa1403e0); li(W1, s); ldrx_u(X11, ST, 80); blr(X11); E(0xaa0003f4); + return; + } + for (j = 0; j < e->nin; j++) if (r[j] >= 0) ldr_u(r[j], SP, 4 * (e->nin - 1 - j)); + for (i = 0; i < e->n; i++) { + Uop *u = &e->u[i]; + int d = u->op == U_ST ? 0 : r[u->d], a = r[u->a], b = r[u->b], c = r[u->c]; + switch (u->op) { + case U_LI: li(d, u->imm); break; + case U_ADD: op3(0x0b000000, d, a, b); break; + case U_SUB: op3(0x4b000000, d, a, b); break; + case U_MUL: op3(0x1b007c00, d, a, b); break; + case U_SDIV: op3(0x1ac00c00, d, a, b); break; + case U_UDIV: op3(0x1ac00800, d, a, b); break; + case U_SREM: op3(0x1ac00c00, 16, a, b); E(0x1b008000 | b << 16 | a << 10 | 16 << 5 | d); break; /* msub */ + case U_UREM: op3(0x1ac00800, 16, a, b); E(0x1b008000 | b << 16 | a << 10 | 16 << 5 | d); break; + case U_AND: op3(0x0a000000, d, a, b); break; + case U_OR: op3(0x2a000000, d, a, b); break; + case U_XOR: op3(0x4a000000, d, a, b); break; + case U_SHL: op3(0x1ac02000, d, a, b); break; + case U_SHR: op3(0x1ac02400, d, a, b); break; + case U_SAR: op3(0x1ac02800, d, a, b); break; + case U_NEG: E(0x4b0003e0 | a << 16 | d); break; + case U_NOT: E(0x2a2003e0 | a << 16 | d); break; + case U_LT: E(0x6b00001f | b << 16 | a << 5); E(0x1a9fa7e0 | d); break; /* cmp; cset lt */ + case U_LTU: E(0x6b00001f | b << 16 | a << 5); E(0x1a9f27e0 | d); break; /* cset lo */ + case U_EQ: E(0x6b00001f | b << 16 | a << 5); E(0x1a9f17e0 | d); break; + case U_NE: E(0x6b00001f | b << 16 | a << 5); E(0x1a9f07e0 | d); break; + case U_MIN: E(0x6b00001f | b << 16 | a << 5); E(0x1a800000 | b << 16 | 11 << 12 | a << 5 | d); break; + case U_MAX: E(0x6b00001f | b << 16 | a << 5); E(0x1a800000 | b << 16 | 12 << 12 | a << 5 | d); break; + case U_MINU: E(0x6b00001f | b << 16 | a << 5); E(0x1a800000 | b << 16 | 3 << 12 | a << 5 | d); break; + case U_MAXU: E(0x6b00001f | b << 16 | a << 5); E(0x1a800000 | b << 16 | 8 << 12 | a << 5 | d); break; + case U_MULHS: E(0x9b207c00 | b << 16 | a << 5 | 16); E(0xd360fc00 | 16 << 5 | d); break; /* smull; lsr #32 */ + case U_MULHU: E(0x9ba07c00 | b << 16 | a << 5 | 16); E(0xd360fc00 | 16 << 5 | d); break; + case U_CLZ: E(0x5ac01000 | a << 5 | d); break; + case U_ABS: E(0x7100001f | a << 5); E(0x5a800400 | a << 16 | 10 << 12 | a << 5 | d); break; /* cmp #0; cneg */ + case U_SEL: E(0x7100001f | a << 5); E(0x1a800000 | c << 16 | 1 << 12 | b << 5 | d); break; /* csel ne */ + case U_LD: /* out of range reads 0, like the interpreter */ + E(0x52800011); E(0x7144001f | a << 5); E(0x54000042); ldr_rr(17, M, a, UXTW); movw(d, 17); + break; + case U_ST: E(0x7144001f | a << 5); E(0x54000042); str_rr(b, M, a, UXTW); break; + } + } + delta = 4 * (e->nin - e->nout); + if (delta > 0) E(0x91000294 | delta << 10); + if (delta < 0) E(0xd1000294 | (-delta) << 10); + for (j = 0; j < e->nout; j++) str_u(r[e->out[j]], SP, 4 * (e->nout - 1 - j)); +} +static int inst_hook(int d) { return install(d); } +static long xrun(long sp, int slot) { return (long)xexec(&enh[slot], (int *)sp); } + +static void *translate(int p) +{ + unsigned *start; + int i = p, n = 0, w, k, t; + if (cp - code > CODEW - 65536 || nbl >= MAXB) flush(); + start = cp; + for (;;) { + if (i >= MW) { E(0x12800000); b_to(x_exit); break; } /* movn w0, #0: halt */ + if (n++ >= BLKW) { li(W0, i); b_to(x_disp); break; } + w = m[i]; + cov[i] = 1; + if (w & 1 && i + 1 < MW && !(m[i + 1] & 1) && (k = m[i + 1] / 2) >= 6 && k <= 8) { + t = (w - 1) / 2; /* literal + call/jmp/jz with a known target */ + cov[i + 1] = 1; + if (k == 8) { + unsigned *a; + ldr_post(X10, SP, 4); + a = cp; E(0x3500000a); /* cbnz w10, fallthrough */ + li(W0, t); b_to(x_disp); + cb_fix(a, cp); + i += 2; + continue; + } + li(X10, i + 2); + if (k == 6) str_post(X10, RP, 4); else str_u(X10, RP, 0); + li(W0, t); b_to(x_disp); + i += 2; + break; + } + if (w & 1) { li(X9, (w - 1) / 2); str_pre(X9, SP, -4); i++; continue; } + k = w / 2; + if (k == 0) { E(0x12800000); b_to(x_exit); i++; break; } + if (k < 0 || k == 6 || k == 7) { + ldr_post(W0, SP, 4); + li(X10, i + 1); + if (k == 6) str_post(X10, RP, 4); else str_u(X10, RP, 0); + if (k < 0) { li(X11, 7 - k); addx_ext(RP, RP, X11, SXTW); } + b_to(x_disp); + i++; + break; + } + if (k == 8) { /* jz: taken leaves, not taken keeps going */ + ldp_post(X9, X10, SP, 8); + E(0x3500006a); /* cbnz w10, +3 */ + movw(W0, X9); + b_to(x_disp); + i++; + continue; + } + if (k == 9) { ldr_pre(W0, RP, -4); b_to(x_disp); i++; break; } + if (k == 21) { /* install: call into c, then return to it so it can flush */ + ldr_u(W0, SP, 0); ldrx_u(X11, ST, 72); blr(X11); str_u(W0, SP, 0); + li(W0, i + 1); b_to(x_exit); i++; break; + } + if (k >= XOP0 && k < XOP0 + NENH && enh[k - XOP0].live) { emit_enh(k - XOP0); i++; continue; } + if (k > 20) { E(0x12800000); b_to(x_exit); i++; break; } /* not wired: halt */ + op(i, k); + i++; + } + bl[nbl].a = p; + bl[nbl++].b = i; + tab[p] = start; + syncicache(start, cp); + if (trace) fprintf(stderr, "jit: %d..%d -> %ld bytes\n", p, i - 1, (long)((char *)cp - (char *)start)); + return start; +} + +#else +/* portable path for hosts without the arm64 jit: a plain interpreter with + the same semantics (shift counts wrap at 32, stores outside memory are + ignored, loads outside memory read 0, leaving memory halts). */ +#ifdef ZYLLVM +/* ---- llvm backend ---- + a tiered jit for hosts the arm64 backend doesn't cover. the interpreter + counts how often each block start is reached; a hot block is translated to + llvm ir (same block rules as the arm64 jit: it ends at call, jump, ret or + halt, and keeps going past a jz), optimized at O2 and compiled with orc. + enhancements are inlined as ir. stores into compiled words drop the blocks + that cover them, so self-modifying code still works. */ +#include +#include +#include +#include +#include +#include +static int HOT = 5000; /* runs before a block gets compiled: ZYHOT */ +static const char *lpasses = "default"; /* ZYPASSES */ +#define LMAXB 65536 +typedef int (*Blk)(int *mem, int *regs); +static Blk lt[MW]; /* address -> compiled block */ +static unsigned short hot[MW]; +static unsigned char lcov[MW]; /* 1 if a compiled block covers the word */ +static struct { int a, b; } lbl[LMAXB]; +static int nlbl, llvm_ok, nblocks; +static LLVMOrcLLJITRef jit; +static LLVMOrcThreadSafeContextRef tsc; +static LLVMContextRef ctx; +static LLVMTypeRef i8t, i32t, i64t, ptrt, i1t; +static LLVMBuilderRef bd; +static LLVMValueRef fn_, vm_, vregs, vs, vr; /* function, memory, regs, s and r slots */ +static LLVMModuleRef mod_; + +static void ldrop_all(void) +{ + memset(lt, 0, sizeof lt); memset(lcov, 0, sizeof lcov); memset(hot, 0, sizeof hot); nlbl = 0; + if (trace) fprintf(stderr, "llvm: dropped every block\n"); +} +/* a store to a word some block covers: write it, drop those blocks */ +static int lhook(int a, int v) +{ + int j, x, hit = 0; + if (m[a] == v) return 0; + m[a] = v; + for (j = 0; j < nlbl; j++) + if (lbl[j].a >= 0 && lbl[j].a <= a && a < lbl[j].b) { + lt[lbl[j].a] = 0; hot[lbl[j].a] = 0; + if (trace) fprintf(stderr, "llvm: dropped %d..%d\n", lbl[j].a, lbl[j].b - 1); + lbl[j].a = -1; hit = 1; + } + if (hit) { + memset(lcov, 0, sizeof lcov); + for (j = 0; j < nlbl; j++) if (lbl[j].a >= 0) for (x = lbl[j].a; x < lbl[j].b; x++) lcov[x] = 1; + } + return hit; +} +static int linstall(int d) { return install(d); } + +static LLVMValueRef K32(int v) { return LLVMConstInt(i32t, (unsigned)v, 0); } +static LLVMValueRef cptr(void *p) { return LLVMConstIntToPtr(LLVMConstInt(i64t, (unsigned long long)(size_t)p, 0), ptrt); } +static LLVMValueRef mslot(LLVMValueRef idx) { return LLVMBuildGEP2(bd, i32t, vm_, &idx, 1, ""); } +static LLVMValueRef ld(LLVMValueRef idx) { return LLVMBuildLoad2(bd, i32t, mslot(idx), ""); } +static void st(LLVMValueRef idx, LLVMValueRef v) { LLVMBuildStore(bd, v, mslot(idx)); } +static LLVMValueRef gets(void) { return LLVMBuildLoad2(bd, i32t, vs, ""); } +static void sets(LLVMValueRef v) { LLVMBuildStore(bd, v, vs); } +static LLVMValueRef add(LLVMValueRef a, int k) { return LLVMBuildAdd(bd, a, K32(k), ""); } +/* a load that reads 0 outside memory */ +static LLVMValueRef safeld(LLVMValueRef a) +{ + LLVMValueRef in = LLVMBuildICmp(bd, LLVMIntULT, a, K32(MW), ""); + LLVMValueRef v = ld(LLVMBuildSelect(bd, in, a, K32(0), "")); + return LLVMBuildSelect(bd, in, v, K32(0), ""); +} +static void push(LLVMValueRef v) { LLVMValueRef s = add(gets(), -1); sets(s); st(s, v); } +/* leave the block: write s and r back, return the next vm address */ +static void leave(LLVMValueRef next) +{ + LLVMValueRef r0 = K32(0), r1 = K32(1); + LLVMBuildStore(bd, gets(), LLVMBuildGEP2(bd, i32t, vregs, &r0, 1, "")); + LLVMBuildStore(bd, LLVMBuildLoad2(bd, i32t, vr, ""), LLVMBuildGEP2(bd, i32t, vregs, &r1, 1, "")); + LLVMBuildRet(bd, next); +} +static LLVMBasicBlockRef newbb(void) { return LLVMAppendBasicBlockInContext(ctx, fn_, ""); } +static LLVMValueRef callp(void *f, LLVMTypeRef ret, LLVMTypeRef *at, int n, LLVMValueRef *args) +{ + LLVMTypeRef ft = LLVMFunctionType(ret, at, (unsigned)n, 0); + return LLVMBuildCall2(bd, ft, cptr(f), args, (unsigned)n, ""); +} +/* an enhancement as ir: the same semantics as ev() */ +static void ir_enh(Enh *e) +{ + LLVMValueRef *v = calloc(e->nv + 1, sizeof *v), s = gets(), z = K32(0), q, d, t, c1, c2; + int i, j; + LLVMTypeRef i64 = i64t; + for (j = 0; j < e->nin; j++) v[j] = ld(add(s, e->nin - 1 - j)); + for (i = 0; i < e->n; i++) { + Uop *u = &e->u[i]; + LLVMValueRef a = u->op == U_LI ? 0 : v[u->a], b = uarity[u->op] > 1 || u->op == U_ST ? v[u->b] : 0, c = uarity[u->op] > 2 ? v[u->c] : 0, r = 0; + switch (u->op) { + case U_LI: r = K32(u->imm); break; + case U_ADD: r = LLVMBuildAdd(bd, a, b, ""); break; + case U_SUB: r = LLVMBuildSub(bd, a, b, ""); break; + case U_MUL: r = LLVMBuildMul(bd, a, b, ""); break; + case U_SDIV: case U_SREM: /* /0 and INT_MIN/-1 are defined, like the cpu */ + c1 = LLVMBuildICmp(bd, LLVMIntEQ, b, z, ""); + c2 = LLVMBuildAnd(bd, LLVMBuildICmp(bd, LLVMIntEQ, a, K32((int)0x80000000), ""), LLVMBuildICmp(bd, LLVMIntEQ, b, K32(-1), ""), ""); + d = LLVMBuildSelect(bd, LLVMBuildOr(bd, c1, c2, ""), K32(1), b, ""); + if (u->op == U_SDIV) { q = LLVMBuildSDiv(bd, a, d, ""); r = LLVMBuildSelect(bd, c1, z, q, ""); } + else { q = LLVMBuildSRem(bd, a, d, ""); r = LLVMBuildSelect(bd, c1, a, LLVMBuildSelect(bd, c2, z, q, ""), ""); } + break; + case U_UDIV: case U_UREM: + c1 = LLVMBuildICmp(bd, LLVMIntEQ, b, z, ""); + d = LLVMBuildSelect(bd, c1, K32(1), b, ""); + if (u->op == U_UDIV) r = LLVMBuildSelect(bd, c1, z, LLVMBuildUDiv(bd, a, d, ""), ""); + else r = LLVMBuildSelect(bd, c1, a, LLVMBuildURem(bd, a, d, ""), ""); + break; + case U_AND: r = LLVMBuildAnd(bd, a, b, ""); break; + case U_OR: r = LLVMBuildOr(bd, a, b, ""); break; + case U_XOR: r = LLVMBuildXor(bd, a, b, ""); break; + case U_SHL: r = LLVMBuildShl(bd, a, LLVMBuildAnd(bd, b, K32(31), ""), ""); break; + case U_SHR: r = LLVMBuildLShr(bd, a, LLVMBuildAnd(bd, b, K32(31), ""), ""); break; + case U_SAR: r = LLVMBuildAShr(bd, a, LLVMBuildAnd(bd, b, K32(31), ""), ""); break; + case U_NEG: r = LLVMBuildSub(bd, z, a, ""); break; + case U_NOT: r = LLVMBuildNot(bd, a, ""); break; + case U_LT: r = LLVMBuildZExt(bd, LLVMBuildICmp(bd, LLVMIntSLT, a, b, ""), i32t, ""); break; + case U_LTU: r = LLVMBuildZExt(bd, LLVMBuildICmp(bd, LLVMIntULT, a, b, ""), i32t, ""); break; + case U_EQ: r = LLVMBuildZExt(bd, LLVMBuildICmp(bd, LLVMIntEQ, a, b, ""), i32t, ""); break; + case U_NE: r = LLVMBuildZExt(bd, LLVMBuildICmp(bd, LLVMIntNE, a, b, ""), i32t, ""); break; + case U_MIN: r = LLVMBuildSelect(bd, LLVMBuildICmp(bd, LLVMIntSLT, a, b, ""), a, b, ""); break; + case U_MAX: r = LLVMBuildSelect(bd, LLVMBuildICmp(bd, LLVMIntSGT, a, b, ""), a, b, ""); break; + case U_MINU: r = LLVMBuildSelect(bd, LLVMBuildICmp(bd, LLVMIntULT, a, b, ""), a, b, ""); break; + case U_MAXU: r = LLVMBuildSelect(bd, LLVMBuildICmp(bd, LLVMIntUGT, a, b, ""), a, b, ""); break; + case U_MULHS: case U_MULHU: { + LLVMValueRef x = u->op == U_MULHS ? LLVMBuildSExt(bd, a, i64, "") : LLVMBuildZExt(bd, a, i64, ""); + LLVMValueRef y = u->op == U_MULHS ? LLVMBuildSExt(bd, b, i64, "") : LLVMBuildZExt(bd, b, i64, ""); + r = LLVMBuildTrunc(bd, LLVMBuildLShr(bd, LLVMBuildMul(bd, x, y, ""), LLVMConstInt(i64, 32, 0), ""), i32t, ""); + break; } + case U_CLZ: { + unsigned id = LLVMLookupIntrinsicID("llvm.ctlz", 9); + LLVMValueRef f = LLVMGetIntrinsicDeclaration(mod_, id, &i32t, 1), args[2]; + args[0] = a; args[1] = LLVMConstInt(i1t, 0, 0); + r = LLVMBuildCall2(bd, LLVMIntrinsicGetType(ctx, id, &i32t, 1), f, args, 2, ""); + break; } + case U_ABS: t = LLVMBuildICmp(bd, LLVMIntSLT, a, z, ""); r = LLVMBuildSelect(bd, t, LLVMBuildSub(bd, z, a, ""), a, ""); break; + case U_SEL: r = LLVMBuildSelect(bd, LLVMBuildICmp(bd, LLVMIntNE, a, z, ""), b, c, ""); break; + case U_LD: r = safeld(a); break; + case U_ST: { + LLVMBasicBlockRef yes = newbb(), done = newbb(); + LLVMBuildCondBr(bd, LLVMBuildICmp(bd, LLVMIntULT, a, K32(MW), ""), yes, done); + LLVMPositionBuilderAtEnd(bd, yes); st(a, b); LLVMBuildBr(bd, done); + LLVMPositionBuilderAtEnd(bd, done); + break; } + } + if (u->op != U_ST) v[u->d] = r; + } + s = add(s, e->nin - e->nout); sets(s); + for (j = 0; j < e->nout; j++) st(add(s, e->nout - 1 - j), v[e->out[j]]); + free(v); +} +static void llvm_init(void) +{ + LLVMErrorRef err; + if (getenv("ZYHOT")) HOT = atoi(getenv("ZYHOT")); + if (HOT < 1) HOT = 1; + if (HOT > 65000) HOT = 65000; + if (getenv("ZYPASSES")) lpasses = getenv("ZYPASSES"); + LLVMInitializeNativeTarget(); LLVMInitializeNativeAsmPrinter(); + if ((err = LLVMOrcCreateLLJIT(&jit, 0))) { char *msg = LLVMGetErrorMessage(err); fprintf(stderr, "llvm: %s\n", msg); LLVMDisposeErrorMessage(msg); return; } + tsc = LLVMOrcCreateNewThreadSafeContext(); + ctx = LLVMOrcThreadSafeContextGetContext(tsc); + i1t = LLVMInt1TypeInContext(ctx); i8t = LLVMInt8TypeInContext(ctx); i32t = LLVMInt32TypeInContext(ctx); + i64t = LLVMInt64TypeInContext(ctx); ptrt = LLVMPointerTypeInContext(ctx, 0); + bd = LLVMCreateBuilderInContext(ctx); + llvm_ok = 1; +} +/* compile the block at p; 0 if it couldn't be */ +static Blk lcompile(int p) +{ + char name[32]; LLVMTypeRef pt[2], ft; LLVMValueRef s, a, b, v, r, args[2]; LLVMTypeRef at[2]; + LLVMOrcExecutorAddress addr; LLVMErrorRef err; LLVMPassBuilderOptionsRef po; + int i = p, n = 0, w, k, t, done = 0; + if (!llvm_ok || nlbl >= LMAXB) return 0; + sprintf(name, "b%d_%d", p, nblocks++); + mod_ = LLVMModuleCreateWithNameInContext(name, ctx); + pt[0] = ptrt; pt[1] = ptrt; + ft = LLVMFunctionType(i32t, pt, 2, 0); + fn_ = LLVMAddFunction(mod_, name, ft); + vm_ = LLVMGetParam(fn_, 0); vregs = LLVMGetParam(fn_, 1); + LLVMPositionBuilderAtEnd(bd, newbb()); + vs = LLVMBuildAlloca(bd, i32t, "s"); vr = LLVMBuildAlloca(bd, i32t, "r"); + { LLVMValueRef r0 = K32(0), r1 = K32(1); + sets(LLVMBuildLoad2(bd, i32t, LLVMBuildGEP2(bd, i32t, vregs, &r0, 1, ""), "")); + LLVMBuildStore(bd, LLVMBuildLoad2(bd, i32t, LLVMBuildGEP2(bd, i32t, vregs, &r1, 1, ""), ""), vr); } + while (!done) { + if (i >= MW) { leave(K32(MW)); break; } + if (n++ >= 1024) { leave(K32(i)); break; } + w = m[i]; + lcov[i] = 1; + if (w & 1) { + /* a literal followed by call, jump or jz: a known target */ + if (i + 1 < MW && !(m[i + 1] & 1) && (k = m[i + 1] / 2) >= 6 && k <= 8) { + t = w >> 1; lcov[i + 1] = 1; + if (k == 8) { + LLVMBasicBlockRef tk = newbb(), go = newbb(); + s = gets(); a = ld(s); sets(add(s, 1)); + LLVMBuildCondBr(bd, LLVMBuildICmp(bd, LLVMIntEQ, a, K32(0), ""), tk, go); + LLVMPositionBuilderAtEnd(bd, tk); leave(K32(t)); + LLVMPositionBuilderAtEnd(bd, go); + i += 2; continue; + } + if (k == 6) { r = LLVMBuildLoad2(bd, i32t, vr, ""); st(r, K32(i + 2)); LLVMBuildStore(bd, add(r, 1), vr); } + leave(K32(t)); i += 2; break; + } + push(K32(w >> 1)); i++; continue; + } + k = w / 2; + s = gets(); + switch (k) { + case 0: leave(K32(MW)); done = 1; break; + case 1: st(s, safeld(ld(s))); break; + case 2: { + LLVMBasicBlockRef in = newbb(), cv = newbb(), plain = newbb(), out = newbb(), quit = newbb(); + a = ld(s); b = ld(add(s, 1)); sets(add(s, 2)); + LLVMBuildCondBr(bd, LLVMBuildICmp(bd, LLVMIntULT, a, K32(MW), ""), in, out); + LLVMPositionBuilderAtEnd(bd, in); + v = LLVMBuildLoad2(bd, i8t, LLVMBuildGEP2(bd, i8t, cptr(lcov), &a, 1, ""), ""); + LLVMBuildCondBr(bd, LLVMBuildICmp(bd, LLVMIntNE, v, LLVMConstInt(i8t, 0, 0), ""), cv, plain); + LLVMPositionBuilderAtEnd(bd, plain); st(a, b); LLVMBuildBr(bd, out); + LLVMPositionBuilderAtEnd(bd, cv); + at[0] = i32t; at[1] = i32t; args[0] = a; args[1] = b; + v = callp((void *)lhook, i32t, at, 2, args); + LLVMBuildCondBr(bd, LLVMBuildICmp(bd, LLVMIntNE, v, K32(0), ""), quit, out); + LLVMPositionBuilderAtEnd(bd, quit); leave(K32(i + 1)); /* a block died: back to the loop */ + LLVMPositionBuilderAtEnd(bd, out); + break; } + case 3: a = ld(s); st(s, safeld(LLVMBuildAdd(bd, add(s, 1), a, ""))); break; + case 4: sets(add(s, 1)); break; + case 5: a = ld(s); b = ld(add(s, 1)); st(s, b); st(add(s, 1), a); break; + case 6: case 7: + a = ld(s); sets(add(s, 1)); + if (k == 6) { r = LLVMBuildLoad2(bd, i32t, vr, ""); st(r, K32(i + 1)); LLVMBuildStore(bd, add(r, 1), vr); } + leave(a); done = 1; break; + case 8: { + LLVMBasicBlockRef tk = newbb(), go = newbb(); + a = ld(s); b = ld(add(s, 1)); sets(add(s, 2)); + LLVMBuildCondBr(bd, LLVMBuildICmp(bd, LLVMIntEQ, b, K32(0), ""), tk, go); + LLVMPositionBuilderAtEnd(bd, tk); leave(a); + LLVMPositionBuilderAtEnd(bd, go); + break; } + case 9: + r = add(LLVMBuildLoad2(bd, i32t, vr, ""), -1); LLVMBuildStore(bd, r, vr); + leave(ld(r)); done = 1; break; + case 10: v = callp((void *)Q, i8t, 0, 0, 0); push(LLVMBuildSExt(bd, v, i32t, "")); break; + case 11: + if (sizeof J == 1) push(LLVMBuildZExt(bd, LLVMBuildLoad2(bd, i8t, cptr(&J), ""), i32t, "")); + else push(LLVMBuildLoad2(bd, i32t, cptr(&J), "")); + break; + case 12: at[0] = i8t; args[0] = LLVMBuildTrunc(bd, ld(s), i8t, ""); callp((void *)G, LLVMVoidTypeInContext(ctx), at, 1, args); sets(add(s, 1)); break; + case 13: case 14: case 15: case 16: case 17: case 18: case 19: case 20: + a = ld(s); b = ld(add(s, 1)); s = add(s, 1); sets(s); + switch (k) { + case 13: v = LLVMBuildAdd(bd, b, a, ""); break; + case 14: v = LLVMBuildSub(bd, b, a, ""); break; + case 15: v = LLVMBuildMul(bd, b, a, ""); break; + case 16: v = LLVMBuildAnd(bd, b, a, ""); break; + case 17: v = LLVMBuildOr(bd, b, a, ""); break; + case 18: v = LLVMBuildXor(bd, b, a, ""); break; + case 19: v = LLVMBuildAShr(bd, b, LLVMBuildAnd(bd, a, K32(31), ""), ""); break; + default: v = LLVMBuildZExt(bd, LLVMBuildICmp(bd, LLVMIntSLT, b, a, ""), i32t, ""); + } + st(s, v); break; + case 21: /* install: then back to the loop, which drops every block */ + at[0] = i32t; args[0] = ld(s); + st(s, callp((void *)linstall, i32t, at, 1, args)); + leave(K32(i + 1)); done = 1; break; + default: + if (k >= XOP0 && k < XOP0 + NENH && enh[k - XOP0].live) { ir_enh(&enh[k - XOP0]); break; } + leave(K32(MW)); done = 1; /* not wired: halt */ + } + i++; + } + lbl[nlbl].a = p; lbl[nlbl++].b = i; + po = LLVMCreatePassBuilderOptions(); + if ((err = LLVMRunPasses(mod_, lpasses, 0, po))) { LLVMConsumeError(err); } + LLVMDisposePassBuilderOptions(po); + if ((err = LLVMOrcLLJITAddLLVMIRModule(jit, LLVMOrcLLJITGetMainJITDylib(jit), LLVMOrcCreateNewThreadSafeModule(mod_, tsc)))) { + char *msg = LLVMGetErrorMessage(err); fprintf(stderr, "llvm: %s\n", msg); LLVMDisposeErrorMessage(msg); return 0; + } + if ((err = LLVMOrcLLJITLookup(jit, &addr, name))) { char *msg = LLVMGetErrorMessage(err); fprintf(stderr, "llvm: %s\n", msg); LLVMDisposeErrorMessage(msg); return 0; } + if (trace) fprintf(stderr, "llvm: %d..%d compiled\n", p, i - 1); + return (Blk)(size_t)addr; +} +#endif +#define LD(x) ((unsigned)(x) < MW ? m[x] : 0) +static void run(int p) +{ + int s = DS0, r = RS0, w, a, b, xfer = 1; + unsigned long long steps = 0; +#ifdef ZYLLVM + int regs[2]; + if (!getenv("ZYNOLLVM")) llvm_init(); +#endif + while ((unsigned)p < MW) { +#ifdef ZYLLVM + if (xfer && llvm_ok) { /* at a block start: run it compiled, or count it */ + xfer = 0; + if (!lt[p] && hot[p] < 65535 && ++hot[p] == HOT && !(lt[p] = lcompile(p))) hot[p] = 65535; + if (lt[p]) { + regs[0] = s; regs[1] = r; + p = lt[p](m, regs); + s = regs[0]; r = regs[1]; + if (need_flush) { need_flush = 0; ldrop_all(); } + xfer = 1; + continue; + } + } +#endif + w = m[p++]; + steps++; + if (w & 1) { m[--s] = w >> 1; continue; } + a = m[s]; b = m[s + 1]; + switch (w / 2) { + case 1: m[s] = LD(a); break; + case 2: + if ((unsigned)a < MW) { +#ifdef ZYLLVM + if (lcov[a]) lhook(a, b); else +#endif + m[a] = b; + } + s += 2; break; + case 3: m[s] = LD(s + 1 + a); break; + case 4: s++; break; + case 5: m[s] = b; m[s + 1] = a; break; + case 6: m[r++] = p; p = a; s++; xfer = 1; break; + case 7: p = a; s++; xfer = 1; break; + case 8: if (!b) { p = a; xfer = 1; } s += 2; break; + case 9: p = m[--r]; xfer = 1; break; + case 10: m[--s] = (signed char)Q(); break; + case 11: m[--s] = J; break; + case 12: G((char)a); s++; break; + case 13: m[++s] = b + a; break; + case 14: m[++s] = (int)((unsigned)b - (unsigned)a); break; + case 15: m[++s] = (int)((unsigned)b * (unsigned)a); break; + case 16: m[++s] = b & a; break; + case 17: m[++s] = b | a; break; + case 18: m[++s] = b ^ a; break; + case 19: m[++s] = b >> (a & 31); break; + case 20: m[++s] = b < a; break; + case 21: + m[s] = install(a); +#ifdef ZYLLVM + if (need_flush) { need_flush = 0; ldrop_all(); } +#endif + break; + default: + if (w / 2 >= XOP0 && w / 2 < XOP0 + NENH && enh[w / 2 - XOP0].live) s = (int)(xexec(&enh[w / 2 - XOP0], m + s) - m); + else p = MW; /* halt: unwired opcodes and the reserved negative ones */ + } + } + if (trace) fprintf(stderr, "interp: %llu steps\n", steps); +} +#endif + +int main(void) +{ + trace = getenv("ZYTRACE") != 0; + memcpy(m + GORG, genome, sizeof genome); +#ifdef ZYJIT + { + int p = GORG; + union { unsigned *u; int (*f)(long *, void *); } enter; +#ifdef __APPLE__ + code = mmap(0, CODEW * 4, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANON | MAP_JIT, -1, 0); +#else + code = mmap(0, CODEW * 4, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); +#endif + if (code == MAP_FAILED) { perror("mmap"); return 1; } + jitwrite(1); + build_stubs(); + jitwrite(0); + syncicache(code, cp); + S[0] = (long)m; S[1] = DS0; S[2] = RS0; S[3] = (long)tab; S[4] = (long)cov; + S[5] = (long)Q; S[6] = (long)G; S[7] = (long)&J; S[8] = (long)hook; + S[9] = (long)inst_hook; S[10] = (long)xrun; + enter.u = x_enter; + while ((unsigned)p < MW) { + void *c = tab[p]; + if (!c) { jitwrite(1); c = translate(p); jitwrite(0); } + p = enter.f(S, c); + if (need_flush) { need_flush = 0; jitwrite(1); flush(); jitwrite(0); } + } + } +#else + run(GORG); +#endif + return 0; +} diff --git a/docs/history/11-types-enhancements/code/examples/echo.zc b/docs/history/11-types-enhancements/code/examples/echo.zc new file mode 100644 index 0000000..01f28b0 --- /dev/null +++ b/docs/history/11-types-enhancements/code/examples/echo.zc @@ -0,0 +1,2 @@ +// everything after the backtick is left unread, for the program itself +fn main() { while (!eof()) putc(getc()); return 0; } diff --git a/docs/history/11-types-enhancements/code/examples/fact.zc b/docs/history/11-types-enhancements/code/examples/fact.zc new file mode 100644 index 0000000..2a218b2 --- /dev/null +++ b/docs/history/11-types-enhancements/code/examples/fact.zc @@ -0,0 +1,4 @@ +fn fact(n) { if (n < 2) return 1; return n * fact(n - 1); } +// prints in binary, like the old asm example +fn bin(n) { if (n > 1) bin(n >> 1); putc('0' + (n & 1)); return 0; } +fn main() { bin(fact(10)); putc(10); putn(fact(10)); putc(10); return 0; } diff --git a/docs/history/11-types-enhancements/code/examples/hello.zc b/docs/history/11-types-enhancements/code/examples/hello.zc new file mode 100644 index 0000000..f36a810 --- /dev/null +++ b/docs/history/11-types-enhancements/code/examples/hello.zc @@ -0,0 +1 @@ +fn main() { puts("hello, world!\n"); return 0; } diff --git a/docs/history/11-types-enhancements/code/examples/lists.zc b/docs/history/11-types-enhancements/code/examples/lists.zc new file mode 100644 index 0000000..6ac276e --- /dev/null +++ b/docs/history/11-types-enhancements/code/examples/lists.zc @@ -0,0 +1,35 @@ +// ownership in practice: a list owns its boxes. nothing here calls free; +// everything is released at the end of the block that owns it. +fn box(v) -> .u { let b = new(1); b[0] = v; return u*b; } +fn mklist(cap) -> .u { let l = new(cap + 1); l[0] = 0; return u*l; } +// item m*arrives (it was given down), and goes d*on into the borrowed list +fn push(.m l, .d item) { l[l[0] + 1] = d*item; l[0] = l[0] + 1; return 0; } +fn show(.m l) +{ + let i = 1; + putc('['); + while (i <= l[0]) { if (i > 1) putc(' '); putn(l[i][0]); i++; } + putc(']'); + return 0; +} +fn squares(n) -> .u +{ + let l = mklist(n), i = 0; + while (i < n) { + let b = box(i * i); + push(l, d*b); + i++; + } + return u*l; +} +fn main() +{ + let before = live_objects(); + { + let l = squares(6); + show(l); putc(10); + puts("live: "); putn(live_objects() - before); putc(10); + } + puts("after the block: "); putn(live_objects() - before); putc(10); + return 0; +} diff --git a/docs/history/11-types-enhancements/code/examples/repl.zc b/docs/history/11-types-enhancements/code/examples/repl.zc new file mode 100644 index 0000000..b7b81b8 --- /dev/null +++ b/docs/history/11-types-enhancements/code/examples/repl.zc @@ -0,0 +1,27 @@ +// a repl on the resident compiler: each backtick-separated chunk of input is +// compiled as a function body and run. definitions (fn ...) can be loaded too: +// a chunk that starts with "fn" goes through load instead of eval. +let buf[4000]; +fn readchunk() +{ + let n = 0, c; + while (!eof()) { + c = getc(); + if (c == 96) break; + if (n < 3999) { buf[n] = c; n++; } + } + buf[n] = 0; + return n; +} +fn main() +{ + let q, i; + while (!eof()) { + readchunk(); + q = tokens(buf); + if (q[0] && q[1] == T_ID && q[2] == intern("fn")) load(q); + else eval(q); + free(d*q); + } + return 0; +} diff --git a/docs/history/11-types-enhancements/code/examples/selfpatch.zc b/docs/history/11-types-enhancements/code/examples/selfpatch.zc new file mode 100644 index 0000000..074ccec --- /dev/null +++ b/docs/history/11-types-enhancements/code/examples/selfpatch.zc @@ -0,0 +1,17 @@ +// code is data: find the literal 'A' inside f's compiled words and rewrite it. +// on the jit the store lands on translated code, which gets dropped and +// retranslated on the next call. +fn f() { putc('A'); return 0; } +fn litw(v) { return v + v + 1; } // how the vm encodes a literal push +fn main() +{ + let p = f; + f(); + while (*p != litw('A')) p++; + *p = litw('B'); + f(); + *p = litw('C'); + f(); + putc(10); + return 0; +} diff --git a/docs/history/11-types-enhancements/code/examples/typed.zc b/docs/history/11-types-enhancements/code/examples/typed.zc new file mode 100644 index 0000000..d91fe6a --- /dev/null +++ b/docs/history/11-types-enhancements/code/examples/typed.zc @@ -0,0 +1,47 @@ +// types: structs, a generic container, and looking at it all through reflection +typecheck on; +struct point { int x, y; }; +struct vec { int len, cap; *T items; }; + +fn mkvec(int cap) -> *vec.u +{ + *vec v = new(vec); + v.cap = cap; + v.items = new(T, cap); + return u*v; +} +// x is given away (.d): the vector's memory holds it from now on, and the +// collector looks after it +fn push(*vec v, T.d x) -> bool +{ + if (v.len == v.cap) { free(d*x); return 0; } + v.items[v.len] = d*x; + v.len++; + return 1; +} +fn show(word t) +{ + let i = 0; + tprint(t); puts(" is "); putn(tsize(t)); puts(" words: "); + while (i < tnfields(t)) { if (i) puts(", "); puts(tfname(t, i)); puts(" "); tprint(tftype(t, i)); i++; } + putc(10); + return 0; +} +fn main() -> int +{ + *vec<*point> v = mkvec<*point>(4); + int i = 0; + while (i < 3) { + *point p = new(point); + p.x = i; p.y = i * i; + push(v, d*p); + i++; + } + puts("last: "); putn(v.items[2].y); putc(10); + show(typeof(point)); + show(typeof(*v)); + puts("push is "); tprint(sigof(push)); putc(10); + // generic code is compiled once, so what mkvec stamped is vec + puts("the heap says v is a "); tprint(typeof_obj(v)); puts(", and v.items[0] is a "); tprint(typeof_obj(v.items[0])); putc(10); + return 0; +} diff --git a/docs/history/11-types-enhancements/code/examples/wiring.zc b/docs/history/11-types-enhancements/code/examples/wiring.zc new file mode 100644 index 0000000..ed30935 --- /dev/null +++ b/docs/history/11-types-enhancements/code/examples/wiring.zc @@ -0,0 +1,19 @@ +// virtual enhancements: custom opcodes wired into the cpu while compiling. +// the recipes run through the core's optimizer; the jit inlines the result. +enhance popcount(x) micro + "r1 = shr x 1; r1 = and r1 0x55555555; x = sub x r1" + "; r1 = shr x 2; r1 = and r1 0x33333333; x = and x 0x33333333; x = add x r1" + "; r1 = shr x 4; x = add x r1; x = and x 0x0f0f0f0f; x = mul x 0x01010101; x = shr x 24"; +enhance div10(x) micro "x = udiv x 10"; // becomes a multiply-high, no divide +enhance hypot2(a, b) vm { 0 $ * ~ 0 $ * + }; // lifted from vm code +enhance minmax(a, b) -> 2 micro "r2 = min a b; r1 = max a b; r0 = r2"; +let lo, hi; +fn main() +{ + putn(popcount(0xff00ff)); putc(' '); + putn(div10(12345)); putc(' '); + putn(hypot2(3, 4)); putc(' '); + asm { 9 4 minmax hi ! lo ! } + putn(lo); putc('/'); putn(hi); putc(10); + return 0; +} diff --git a/docs/history/11-types-enhancements/code/genome.h b/docs/history/11-types-enhancements/code/genome.h new file mode 100644 index 0000000..26cf3dd --- /dev/null +++ b/docs/history/11-types-enhancements/code/genome.h @@ -0,0 +1,3596 @@ +#define GORG 786432 +static const int genome[] = { +1966081,1310721,4,1677827,14,839142,843895,1310721,2,3,28,1310721,4,1310721,2,4, +1310721,2,2,42,1572911,14,1,1310721,2,3,26,1310721,4,18,0,0, +2,1,786475,0,786469,786471,786473,97,0,98,0,97,32,61,32,115, +100,105,118,32,97,32,98,0,1,0,2,1,786501,0,786495,786497, +786499,97,0,98,0,97,32,61,32,115,114,101,109,32,97,32, +98,0,2,0,2,1,786527,0,786521,786523,786525,97,0,98,0,97, +32,61,32,115,104,108,32,97,32,98,0,1310721,2,3,28,1310721, +4,1310721,2,4,1310721,2,2,24,1,1573113,14,1,1310721,2,3,26, +1310721,4,18,1310721,2,1,28,1310721,4,20,1573147,14,1,1310721,2,1, +26,1310721,4,18,1310721,2,1,28,1310721,4,22,1573181,14,1,1310721,2, +1,26,1310721,4,18,1310721,2,1,28,1310721,4,0,1,1573217,14,1, +1310721,2,1,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,4, +1,1310721,2,3,26,4,1310721,2,2,1310721,2,3,26,2,26,2, +1573349,16,1310721,2,2,1310721,2,3,26,2,26,2,1573079,12,8,1310721, +2,3,26,1,6,2,1,6,3,26,5,6,4,10,8,8, +1573261,14,1,1573357,14,1,1310721,2,5,26,1310721,4,18,1310721,2,29, +28,1310721,4,1310721,2,4,1,1310721,2,27,26,4,1310721,2,2,1, +40,1573449,16,91,1573079,12,8,1310721,2,1,1310721,2,2,28,10,3, +6,10,4,8,1310721,2,2,1,40,1573503,16,1573489,14,50,49,52, +55,52,56,51,54,52,56,0,1573467,1573231,12,8,1,1573717,14,3, +1573641,16,1310721,2,3,26,1310721,2,27,26,1,6,2,1,6,3, +26,5,6,4,10,8,26,97,1310721,2,2,21,46,26,10,3, +6,10,4,8,1310721,2,1310721,2,2,21,44,10,3,6,10,4, +8,1310721,2,2,1,6,1,10,28,34,63,38,3,26,1573637,16, +1573641,14,1573503,14,1310721,2,27,26,2,1573709,16,1310721,2,3,26,1310721, +2,27,26,1,6,2,-1,26,1,6,5,6,4,10,8,26, +2,1573079,12,8,1573641,14,1,1573717,14,1,1310721,2,29,26,1310721,4, +18,1310721,2,5,28,1310721,4,1310721,2,4,1,1310721,2,3,26,4, +1310721,2,2,1310721,2,3,26,2,26,2,1573823,16,1310721,2,3,26, +1,6,2,1,6,3,26,5,6,4,10,8,8,1573761,14,1310721, +2,3,26,2,1573839,14,1,1310721,2,5,26,1310721,4,18,1310721,2, +7,28,1310721,4,1310721,2,3,26,4,1310721,2,4,1,1310721,2,5, +26,4,1310721,2,2,1310721,2,5,26,2,26,2,1574009,16,1310721,2, +2,1310721,2,5,26,2,26,2,1310721,2,3,26,2,1310721,2,5, +26,2,26,2,28,1,6,1,10,28,34,63,38,3,26,1, +6,1,10,28,34,63,38,1,10,28,1574011,14,1,1574053,16,1310721, +2,5,26,1,6,2,1,6,3,26,5,6,4,10,8,8, +1573893,14,1310721,2,2,1310721,2,5,26,2,26,2,1310721,2,3,26, +2,1310721,2,5,26,2,26,2,28,1,6,1,10,28,34,63, +38,3,26,1574125,14,1,1310721,2,7,26,1310721,4,18,1310721,2,9, +28,1310721,4,1310721,2,5,26,4,1310721,2,3,26,4,1310721,2,4, +1,1310721,2,7,26,4,1310721,2,7,26,2,1310721,2,5,26,2, +40,1574307,16,1310721,2,2,1310721,2,7,26,2,26,1310721,2,3,26, +2,1310721,2,7,26,2,26,2,10,3,6,10,4,8,1310721,2, +7,26,1,6,2,1,6,3,26,5,6,4,10,8,8,1574189, +14,1,1574315,14,1,1310721,2,9,26,1310721,4,18,1310721,2,9,28, +1310721,4,1310721,2,5,26,4,1310721,2,3,26,4,1310721,2,4,1, +1310721,2,7,26,4,1310721,2,7,26,2,1310721,2,5,26,2,40, +1574483,16,1310721,2,2,1310721,2,7,26,2,26,1310721,2,3,26,2, +10,3,6,10,4,8,1310721,2,7,26,1,6,2,1,6,3, +26,5,6,4,10,8,8,1574379,14,1,1574491,14,1,1310721,2,9, +26,1310721,4,18,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,1310721,2,11,28,1310721,4,1310721,2,3,26,4, +1310721,2,4,1,1310721,2,5,26,4,1,1310721,2,7,26,4,61, +1310721,2,9,26,4,1310721,2,2,1310721,2,3,26,2,40,1574683,16, +1574569,1310721,2,2,10,3,6,10,4,8,1,1574997,14,1310721,2,9, +26,2,1,40,3,36,1574957,16,1310721,2,7,26,1310721,2,7,26, +2,1310721,2,7,26,2,26,1310721,2,2,1310721,2,9,26,2,38, +3,32,26,10,3,6,10,4,8,1310721,2,5,26,1310721,2,5, +26,2,1310721,2,5,26,2,26,10,3,6,10,4,8,1310721,2, +7,26,2,1310721,2,3,26,2,40,3,36,1574919,16,1310721,2,7, +26,1310721,2,7,26,2,1310721,2,3,26,2,28,10,3,6,10, +4,8,1310721,2,5,26,1,6,2,1,6,3,26,5,6,4, +10,8,8,1310721,2,9,26,1,6,2,1,6,-1,26,5,6, +4,10,8,8,1574683,14,1574569,1310721,2,7,26,2,10,3,6,10, +4,8,1310721,2,5,26,2,1574997,14,1,1310721,2,11,26,1310721,4, +18,1310721,2,9,28,1310721,4,1310721,2,3,26,4,1310721,2,4,1, +1310721,2,5,26,4,1310721,2,3,26,2,1,28,1,6,1,10, +28,34,63,38,3,26,1575115,16,1574569,1310721,2,2,10,3,6,10, +4,8,1,1575351,14,1310721,2,2,1,40,1575177,16,1310721,2,1,1310721, +2,2,28,10,3,6,10,4,8,1310721,2,5,26,3,10,3, +6,10,4,8,1310721,2,3,26,2,1,40,1575263,16,1310721,2,3, +26,1,1310721,2,3,26,2,28,10,3,6,10,4,8,1310721,2, +5,26,1310721,2,5,26,2,3,36,10,3,6,10,4,8,1310721, +2,7,26,1310721,2,2,1310721,2,3,26,2,1574571,12,10,3,6, +10,4,8,1310721,2,5,26,2,1575335,16,1,1310721,2,7,26,2, +28,1575351,14,1310721,2,7,26,2,1575351,14,1,1310721,2,9,26,1310721, +4,18,1310721,2,7,28,1310721,4,1310721,2,3,26,4,1310721,2,4, +1310721,2,2,1,40,1310721,2,5,26,4,1310721,2,3,26,2,1, +28,1,6,1,10,28,34,63,38,3,26,1575461,16,1310721,2,2, +1575519,14,1310721,2,2,1310721,2,3,26,2,1575011,12,8,1310721,2,5, +26,2,1575509,16,1,1574569,2,28,1575519,14,1574569,2,1575519,14,1,1310721, +2,7,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,3,26, +4,1310721,2,4,1310721,2,2,1574505,1310721,2,3,26,2,63,32,26, +2,30,1575595,14,1,1310721,2,5,26,1310721,4,18,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, +1310721,2,3,28,1310721,4,3,1310721,2,4,1574505,1,26,3,10,3, +6,10,4,8,1310721,2,2,65,40,1575825,16,1574505,1310721,2,2,26, +1574505,1310721,2,2,3,28,26,2,1574505,1310721,2,2,3,28,26,2, +26,10,3,6,10,4,8,1310721,2,1,6,2,1,6,3,26, +5,6,4,10,8,8,1575721,14,1575609,524289,10,3,6,10,4,8, +1575611,1,65,1574329,12,8,1,1575861,14,1,1310721,2,3,26,1310721,4, +18,1310721,2,3,28,1310721,4,1310721,2,4,1310721,2,2,524289,13,26, +40,3,36,1575953,16,1310721,2,2,1575609,2,40,1,6,1,10,28, +34,63,38,1,10,28,1575955,14,1,1576035,16,1310721,2,2,7,1, +10,28,26,2,1310721,2,2,1459366445,36,28,1,6,1,10,28,34, +63,38,3,26,1,6,1,10,28,34,63,38,1,10,28,1576037, +14,1,1576089,16,1310721,2,2,9,1,10,28,26,2,3,32,1, +6,1,10,28,34,63,38,1,10,28,1576091,14,1,1576097,14,1, +1310721,2,3,26,1310721,4,18,1310721,2,1,28,1310721,4,1576175,14,122, +99,58,32,111,117,116,32,111,102,32,104,101,97,112,32,109, +101,109,111,114,121,10,0,1576127,1573231,12,8,1573195,12,8,1,1576197, +14,1,1310721,2,1,26,1310721,4,18,1310721,2,15,28,1310721,4,1310721, +2,3,26,4,1310721,2,4,1310721,2,2,13,26,1310721,2,5,26, +4,7,1310721,2,7,26,4,17,1310721,2,9,26,4,1,1310721,2, +11,26,4,1310721,2,2,1,40,1576331,16,1310721,2,5,26,13,10, +3,6,10,4,8,1310721,2,9,26,2,1310721,2,5,26,2,40, +1576437,16,1310721,2,9,26,1310721,2,9,26,2,1310721,2,9,26,2, +26,10,3,6,10,4,8,1310721,2,7,26,1,6,2,1,6, +3,26,5,6,4,10,8,8,1576331,14,1575611,1310721,2,7,26,2, +26,2,1576539,16,1310721,2,11,26,1575611,1310721,2,7,26,2,26,2, +10,3,6,10,4,8,1575611,1310721,2,7,26,2,26,1310721,2,11, +26,2,13,26,2,10,3,6,10,4,8,1576837,14,1575609,2,1310721, +2,9,26,2,26,1310721,10,40,3,36,1576627,16,1310721,2,11,26, +1575609,2,10,3,6,10,4,8,1575609,1575609,2,1310721,2,9,26,2, +26,10,3,6,10,4,8,1576837,14,1575679,2,1576641,16,1578563,12,8, +1575611,1310721,2,7,26,2,26,2,1576743,16,1310721,2,11,26,1575611,1310721, +2,7,26,2,26,2,10,3,6,10,4,8,1575611,1310721,2,7, +26,2,26,1310721,2,11,26,2,13,26,2,10,3,6,10,4, +8,1576837,14,1575609,2,1310721,2,9,26,2,26,1310721,10,40,3,36, +1576831,16,1310721,2,11,26,1575609,2,10,3,6,10,4,8,1575609,1575609, +2,1310721,2,9,26,2,26,10,3,6,10,4,8,1576837,14,1576111, +12,8,1310721,2,13,26,1310721,2,11,26,2,13,26,10,3,6, +10,4,8,1310721,2,11,26,2,1,26,1310721,2,9,26,2,10, +3,6,10,4,8,1310721,2,11,26,2,3,26,1310721,2,7,26, +2,10,3,6,10,4,8,1310721,2,11,26,2,5,26,3,10, +3,6,10,4,8,1310721,2,11,26,2,7,26,1310721,2,13,26, +2,1459366445,36,10,3,6,10,4,8,1310721,2,11,26,2,9,26, +1,10,3,6,10,4,8,1310721,2,11,26,2,11,26,1310721,2, +3,26,2,10,3,6,10,4,8,1310721,2,13,26,2,1,1310721, +2,9,26,2,13,28,1574329,12,8,1310721,2,13,26,2,1577123,14, +1,1310721,2,15,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2, +4,1310721,2,2,1,1576211,12,1577173,14,1,1310721,2,3,26,1310721,4, +18,1310721,2,3,28,1310721,4,1310721,2,4,1310721,2,2,1575875,12,1, +6,1,10,28,34,63,38,3,26,1577245,16,1,1577273,14,1310721,2, +2,13,1,10,28,26,2,13,28,1577273,14,1,1310721,2,3,26, +1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,4,1310721,2,2,1575875, +12,1,6,1,10,28,34,63,38,3,26,1577345,16,1,1577369,14, +1310721,2,2,3,1,10,28,26,2,1577369,14,1,1310721,2,3,26, +1310721,4,18,1310721,2,11,28,1310721,4,1310721,2,4,1310721,2,2,13, +28,1310721,2,3,26,4,1310721,2,3,26,2,1,26,2,13,28, +1310721,2,5,26,4,1,1310721,2,7,26,4,1310721,2,3,26,2, +5,26,1,10,3,6,10,4,8,1310721,2,7,26,2,1310721,2, +5,26,2,40,1577711,16,1310721,2,9,26,1310721,2,2,1310721,2,7, +26,2,26,2,10,3,6,10,4,8,1310721,2,9,26,2,1575875, +12,1577651,16,1310721,2,9,26,2,5,1,10,28,26,2,1310721,2, +2,28,1,6,1,10,28,34,63,38,3,26,1,6,1,10, +28,34,63,38,1,10,28,1577653,14,1,1577673,16,1310721,2,9,26, +2,1577383,12,8,1310721,2,7,26,1,6,2,1,6,3,26,5, +6,4,10,8,8,1577491,14,1310721,2,3,26,2,9,26,1,10, +3,6,10,4,8,1310721,2,3,26,2,13,26,1575611,1310721,2,3, +26,2,3,26,2,26,2,10,3,6,10,4,8,1575611,1310721,2, +3,26,2,3,26,2,26,1310721,2,3,26,2,10,3,6,10, +4,8,1,1577837,14,1,1310721,2,11,26,1310721,4,18,1310721,2,3, +28,1310721,4,1310721,2,4,1310721,2,2,1575875,12,1577895,16,1310721,2,2, +1577383,12,8,1,1577903,14,1,1310721,2,3,26,1310721,4,18,1310721,2, +5,28,1310721,4,1310721,2,3,26,4,1310721,2,4,1310721,2,2,1575875, +12,1577997,16,1310721,2,2,5,1,10,28,26,1310721,2,3,26,2, +10,3,6,10,4,8,1310721,2,2,1578009,14,1,1310721,2,5,26, +1310721,4,18,0,0,0,0,1310721,2,3,28,1310721,4,1310721,2,4, +1310721,2,2,1575875,12,1,6,1,10,28,34,63,38,3,26,1578089, +16,3,1578133,14,1310721,2,2,9,1,10,28,26,2,5,32,1, +6,1,10,28,34,63,38,1,10,28,1578143,16,1,1578287,14,1310721, +2,2,9,1,10,28,26,1310721,2,2,9,1,10,28,26,2, +5,34,10,3,6,10,4,8,1578023,2,32769,40,1578263,16,2031617,1578023, +2,26,1310721,2,2,10,3,6,10,4,8,1578023,1,6,2,1, +6,3,26,5,6,4,10,8,8,1578279,14,1578025,3,10,3,6, +10,4,8,1,1578287,14,1,1310721,2,3,26,1310721,4,18,1310721,2, +5,28,1310721,4,1310721,2,3,26,4,1310721,2,4,1310721,2,2,1310721, +2,3,26,2,40,1578399,16,1310721,2,2,2,1578031,12,8,1310721,2, +1,6,2,1,6,3,26,5,6,4,10,8,8,1578329,14,1, +1578407,14,1,1310721,2,5,26,1310721,4,18,1310721,2,3,28,1310721,4, +1578023,2,1578541,16,1578023,1,6,2,1,6,-1,26,5,6,4,10, +8,8,1310721,2,2031617,1578023,2,26,2,10,3,6,10,4,8,1310721, +2,2,1310721,2,2,1310721,2,2,13,1,10,28,26,2,26,13, +28,1578301,12,8,1578433,14,1,1578549,14,1,1310721,2,3,26,1310721,4, +18,1310721,2,7,28,1310721,4,1,1310721,2,5,26,4,1578023,1,10, +3,6,10,4,8,1578025,1,10,3,6,10,4,8,1674931,12,8, +1578421,12,8,1578025,2,1578815,16,1578025,1,10,3,6,10,4,8,1310721, +2,524289,10,3,6,10,4,8,1310721,2,2,1575609,2,40,1578811,16, +1310721,2,2,5,26,2,7,28,1,6,1,10,28,34,63,38, +3,26,1578765,16,1310721,2,2,13,26,1310721,2,2,1310721,2,2,1, +26,2,26,1578301,12,8,1310721,2,1310721,2,2,1310721,2,2,1,26, +2,26,10,3,6,10,4,8,1578421,12,8,1578673,14,1578631,14,1310721, +2,524289,10,3,6,10,4,8,1310721,2,2,1575609,2,40,1579183,16, +1310721,2,3,26,1310721,2,2,1,26,2,10,3,6,10,4,8, +1310721,2,2,5,26,2,3,28,1,6,1,10,28,34,63,38, +3,26,1579081,16,1310721,2,2,5,26,1,10,3,6,10,4,8, +1310721,2,2,9,26,1,10,3,6,10,4,8,1310721,2,2,13, +26,1575611,1310721,2,2,3,26,2,26,2,10,3,6,10,4,8, +1575611,1310721,2,2,3,26,2,26,1310721,2,2,10,3,6,10,4, +8,1310721,2,5,26,1,6,2,1,6,3,26,5,6,4,10, +8,8,1579145,14,1310721,2,2,5,26,2,7,28,1,6,1,10, +28,34,63,38,3,26,1579145,16,1310721,2,2,5,26,3,10,3, +6,10,4,8,1310721,2,1310721,2,2,1310721,2,3,26,2,26,10, +3,6,10,4,8,1578833,14,1575675,1,6,2,1,6,3,26,5, +6,4,10,8,8,1575677,1575677,2,1310721,2,5,26,2,26,10,3, +6,10,4,8,1310721,2,5,26,2,1579257,14,1,1310721,2,7,26, +1310721,4,18,1310721,2,5,28,1310721,4,524289,1310721,2,4,1,1310721,2, +3,26,4,1310721,2,2,1575609,2,40,1579413,16,1310721,2,2,5,26, +2,3,32,1579373,16,1310721,2,3,26,1,6,2,1,6,3,26, +5,6,4,10,8,8,1310721,2,1310721,2,2,1310721,2,2,1,26, +2,26,10,3,6,10,4,8,1579303,14,1310721,2,3,26,2,1579429, +14,1,1310721,2,5,26,1310721,4,18,0,0,0,0,0,0,0, +0,0,0,0,0,789734,113,64,33,36,95,126,99,106,122,114, +44,63,46,43,45,42,38,124,94,62,60,37,0,1,0,0, +0,0,1,0,0,0,0,-1,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,1310721,2,3,28,1310721,4,1310721,2,4,1582033,14,122,99, +58,32,108,105,110,101,32,0,1582013,1573231,12,8,1579523,2,1573371,12, +8,1582061,14,58,32,0,1582055,1573231,12,8,1310721,2,2,1573231,12,8, +21,1573079,12,8,1573195,12,8,1,1582103,14,1,1310721,2,3,26,1310721, +4,18,1310721,2,5,28,1310721,4,1310721,2,3,26,4,1310721,2,4, +1582169,14,122,99,58,32,108,105,110,101,32,0,1582149,1573231,12,8, +1579523,2,1573371,12,8,1582197,14,58,32,0,1582191,1573231,12,8,1310721,2, +2,1573231,12,8,1582227,14,58,32,0,1582221,1573231,12,8,1310721,2,3, +26,2,1573231,12,8,21,1573079,12,8,1573195,12,8,1,1582273,14,1, +1310721,2,5,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,4, +1581763,2,1582343,16,1581767,2,1,6,1,10,28,34,63,38,1,10, +28,1582345,14,1,1582361,16,1310721,2,2,1581991,12,8,1,1582369,14,1, +1310721,2,3,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,3, +26,4,1310721,2,4,1581763,2,1582449,16,1581767,2,1,6,1,10,28, +34,63,38,1,10,28,1582451,14,1,1582483,16,1466881,1310721,2,2,26, +2,1310721,2,3,26,2,1582117,12,8,1,1582491,14,1,1310721,2,5, +26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,4,1,1310721,2, +3,26,4,1310721,2,2,2,1582625,16,1310721,2,3,26,1310721,2,3, +26,2,63,30,1310721,2,2,2,26,10,3,6,10,4,8,1310721, +2,1,6,2,1,6,3,26,5,6,4,10,8,8,1582535,14, +1310721,2,3,26,2,16385,3,28,32,1582649,14,1,1310721,2,5,26, +1310721,4,18,1310721,2,9,28,1310721,4,1310721,2,4,1310721,2,2,1582505, +12,1310721,2,3,26,4,3,1582865,16,1310721,2,5,26,1311233,1310721,2, +3,26,2,26,2,10,3,6,10,4,8,1310721,2,5,26,2, +1,6,1,10,28,34,63,38,3,26,1582781,16,1582865,14,1310721,2, +5,26,2,1310721,2,2,1573853,12,1582819,16,1310721,2,5,26,2,1583145, +14,1310721,2,3,26,1310721,2,3,26,2,3,26,16385,3,28,32, +10,3,6,10,4,8,1582701,14,1310721,2,7,26,1310721,2,2,1573731, +12,10,3,6,10,4,8,1579445,2,1310721,2,7,26,2,26,5, +26,1409025,40,3,36,1582973,16,1582965,14,115,116,114,105,110,103,32, +112,111,111,108,32,102,117,108,108,0,1582931,1581991,12,8,1579445,2, +1,26,1,10,3,6,10,4,8,1310721,2,5,26,1579445,2,3, +26,10,3,6,10,4,8,1310721,2,5,26,2,1310721,2,2,1310721, +2,7,26,2,3,26,1574139,12,8,1579445,1579445,2,1310721,2,7,26, +2,26,5,26,10,3,6,10,4,8,1311233,1310721,2,3,26,2, +26,1310721,2,5,26,2,10,3,6,10,4,8,1310721,2,5,26, +2,1583145,14,1,1310721,2,9,26,1310721,4,18,1310721,2,9,28,1310721, +4,1310721,2,5,26,4,1310721,2,3,26,4,1310721,2,4,1579447,2, +1310721,2,7,26,4,1579447,2,8193,40,3,36,1583275,16,1583267,14,115, +121,109,98,111,108,32,116,97,98,108,101,32,102,117,108,108, +0,1583231,1581991,12,8,1579447,1,6,2,1,6,3,26,5,6,4, +10,8,8,1409537,1310721,2,7,26,2,26,1310721,2,2,10,3,6, +10,4,8,1417729,1310721,2,7,26,2,26,1310721,2,3,26,2,10, +3,6,10,4,8,1425921,1310721,2,7,26,2,26,1310721,2,5,26, +2,10,3,6,10,4,8,1434113,1310721,2,7,26,2,26,1,10, +3,6,10,4,8,1442305,1310721,2,7,26,2,26,3,10,3,6, +10,4,8,1450497,1310721,2,7,26,2,26,1,10,3,6,10,4, +8,1559041,1310721,2,7,26,2,26,1,10,3,6,10,4,8,1458689, +1310721,2,7,26,2,26,1310721,2,2,3,1,10,28,26,2,10, +3,6,10,4,8,1310721,2,2,3,1,10,28,26,1310721,2,7, +26,2,3,26,10,3,6,10,4,8,1310721,2,7,26,2,1583621, +14,1,1310721,2,9,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721, +2,4,1310721,2,2,3,1,10,28,26,2,3,28,1310721,2,3, +26,4,1310721,2,3,26,2,1,40,3,36,1583933,16,1310721,2,3, +26,2,1579451,2,40,3,36,1583737,16,3,1583799,14,1417729,1310721,2,3, +26,2,26,2,11,28,1,6,1,10,28,34,63,38,3,26, +1,6,1,10,28,34,63,38,1,10,28,1583809,16,3,1583871,14, +1417729,1310721,2,3,26,2,26,2,15,28,1,6,1,10,28,34, +63,38,3,26,1,6,1,10,28,34,63,38,1,10,28,1583889, +16,1310721,2,3,26,2,1583947,14,1310721,2,3,26,1458689,1310721,2,3, +26,2,26,2,3,28,10,3,6,10,4,8,1583685,14,3,1, +10,28,1583947,14,1,1310721,2,5,26,1310721,4,18,1310721,2,3,28, +1310721,4,1310721,2,4,1579447,2,1310721,2,2,10,40,1584071,16,1579447,1, +6,2,1,6,-1,26,5,6,4,10,8,8,1409537,1579447,2,26, +2,3,1,10,28,26,1458689,1579447,2,26,2,10,3,6,10,4, +8,1583979,14,1,1584079,14,1,1310721,2,3,26,1310721,4,18,1310721,2, +3,28,1310721,4,1310721,2,4,1310721,2,2,1582663,12,1583635,12,1584131,14, +1,1310721,2,3,26,1310721,4,18,1310721,2,7,28,1310721,4,1310721,2, +3,26,4,1310721,2,4,1579455,2,1310721,2,5,26,4,1310721,2,5, +26,2,1584235,16,1579455,1491457,1310721,2,5,26,2,26,2,10,3,6, +10,4,8,1584377,14,1579453,1,6,2,1,6,3,26,5,6,4, +10,8,8,1310721,2,5,26,1579453,2,10,3,6,10,4,8,1310721, +2,5,26,2,16385,40,3,36,1584377,16,1584369,14,116,111,111,32, +109,97,110,121,32,102,111,114,119,97,114,100,32,114,101,102, +101,114,101,110,99,101,115,0,1584313,1581991,12,8,1475073,1310721,2,5, +26,2,26,1310721,2,2,10,3,6,10,4,8,1491457,1310721,2,5, +26,2,26,1310721,2,3,26,2,10,3,6,10,4,8,1310721,2, +5,26,2,1584461,14,1,1310721,2,7,26,1310721,4,18,1310721,2,7, +28,1310721,4,1310721,2,3,26,4,1310721,2,4,1310721,2,2,1584649,16, +1475073,1310721,2,2,26,2,1310721,2,3,26,2,1584967,12,8,1310721,2, +5,26,1491457,1310721,2,2,26,2,10,3,6,10,4,8,1491457,1310721, +2,2,26,1579455,2,10,3,6,10,4,8,1579455,1310721,2,2,10, +3,6,10,4,8,1310721,2,1310721,2,5,26,2,10,3,6,10, +4,8,1584503,14,1,1584657,14,1,1310721,2,7,26,1310721,4,18,1310721, +2,3,28,1310721,4,1310721,2,4,1581893,2,1584703,16,1,1584829,14,1581747, +2,1581751,2,40,3,36,1584765,16,1584757,14,112,114,111,103,114,97, +109,32,116,111,111,32,98,105,103,0,1584725,1581991,12,8,1581747,2, +1581749,2,26,1310721,2,2,10,3,6,10,4,8,1581747,1,6,2, +1,6,3,26,5,6,4,10,8,8,1,1584829,14,1,1310721,2, +3,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,4,1310721,2, +2,1310721,2,2,26,3,26,1584671,12,8,1,1584893,14,1,1310721,2, +3,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,4,1310721,2, +2,1310721,2,2,26,1584671,12,8,1,1584953,14,1,1310721,2,3,26, +1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,3,26,4,1310721,2, +4,1581893,2,1585009,16,1,1585067,14,1310721,2,2,1581749,2,26,1310721,2, +3,26,2,1310721,2,3,26,2,26,3,26,10,3,6,10,4, +8,1,1585067,14,1,1310721,2,5,26,1310721,4,18,1310721,2,3,28, +1310721,4,1310721,2,4,1310721,2,2,1584843,12,8,15,1584907,12,8,1, +1585127,14,1,1310721,2,3,26,1310721,4,18,1310721,2,3,28,1310721,4, +1581747,2,1310721,2,4,2097153,3,28,1584843,12,8,1310721,2,2,1585187,14, +1,1310721,2,3,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2, +4,1310721,2,2,3,1,1583159,12,1310721,2,3,26,4,1442305,1310721,2, +3,26,2,26,1,10,3,6,10,4,8,1310721,2,3,26,2, +1585287,14,1,1310721,2,5,26,1310721,4,18,1310721,2,3,28,1310721,4, +1310721,2,4,1442305,1310721,2,2,26,2,3,32,1585363,16,1425921,1310721,2, +2,26,2,1584843,12,8,1,1585439,14,1581893,2,1585377,16,1,1585439,14, +1450497,1310721,2,2,26,1581747,2,1450497,1310721,2,2,26,2,1584145,12,10, +3,6,10,4,8,2097153,3,28,1584843,12,8,1,1585439,14,1,1310721, +2,3,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,4,1310721, +2,2,1583635,12,1310721,2,3,26,4,1310721,2,3,26,2,1,40, +1585539,16,1310721,2,3,26,1310721,2,2,1585201,12,10,3,6,10,4, +8,1310721,2,3,26,2,1585301,12,8,13,1584907,12,8,1,1585571,14, +1,1310721,2,5,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2, +4,1310721,1584843,12,8,3,1584907,12,8,1310721,2,2,1585649,16,1310721,2, +2,1584843,12,8,27,1584907,12,8,1,1585657,14,1,1310721,2,3,26, +1310721,4,18,1310721,2,7,28,1310721,4,1310721,2,4,1585141,12,1310721,2, +3,26,4,15,1584907,12,8,1310721,2,5,26,1581747,2,10,3,6, +10,4,8,1310721,2,2,2,1585795,16,1310721,2,2,2,1584671,12,8, +1310721,2,1,6,2,1,6,3,26,5,6,4,10,8,8,1585735, +14,1,1584671,12,8,1310721,2,3,26,2,1581747,2,1584967,12,8,1310721, +2,5,26,2,1584843,12,8,1,1585847,14,1,1310721,2,7,26,1310721, +4,18,1310721,2,1,28,1310721,4,1579529,2,1,28,1,6,1,10, +28,34,63,38,3,26,1586075,16,1579533,2,1585919,16,3,1585945,14,1573161, +12,1,6,1,10,28,34,63,38,1,10,28,1585977,16,1579527,3, +1,10,28,10,3,6,10,4,8,1,1586225,14,1579527,1573127,12,10, +3,6,10,4,8,1579527,2,193,28,1,6,1,10,28,34,63, +38,3,26,1586071,16,1579533,3,10,3,6,10,4,8,1579527,3,1, +10,28,10,3,6,10,4,8,1,1586225,14,1586157,14,1579527,1579531,2, +2,10,3,6,10,4,8,1579527,2,1586135,16,1579531,1,6,2,1, +6,3,26,5,6,4,10,8,8,1586157,14,1579527,3,1,10,28, +10,3,6,10,4,8,1579527,2,21,28,1,6,1,10,28,34, +63,38,3,26,1586217,16,1579525,1,6,2,1,6,3,26,5,6, +4,10,8,8,1,1586225,14,1,1310721,2,1,26,1310721,4,18,1310721, +2,11,28,1310721,4,1310721,2,7,26,4,1310721,2,5,26,4,1310721, +2,3,26,4,1310721,2,4,1579535,1,6,2,1,6,3,26,5, +6,4,10,8,8,1579535,2,65,40,3,36,1586395,16,1586387,14,115, +111,117,114,99,101,115,32,110,101,115,116,101,100,32,116,111, +111,32,100,101,101,112,108,121,0,1586335,1581991,12,8,1310721,2,9, +26,1540609,1579535,2,33,30,26,10,3,6,10,4,8,1310721,2,9, +26,2,1,26,1310721,2,2,10,3,6,10,4,8,1310721,2,9, +26,2,3,26,1310721,2,3,26,2,10,3,6,10,4,8,1310721, +2,9,26,2,5,26,1,10,3,6,10,4,8,1310721,2,9, +26,2,7,26,1,10,3,6,10,4,8,1310721,2,9,26,2, +9,26,1310721,2,5,26,2,10,3,6,10,4,8,1310721,2,9, +26,2,23,26,1310721,2,7,26,2,10,3,6,10,4,8,1310721, +2,9,26,2,11,26,1579517,2,10,3,6,10,4,8,1310721,2, +9,26,2,13,26,1579519,2,10,3,6,10,4,8,1310721,2,9, +26,2,15,26,1579521,2,10,3,6,10,4,8,1310721,2,9,26, +2,17,26,1579527,2,10,3,6,10,4,8,1310721,2,9,26,2, +19,26,1579529,2,10,3,6,10,4,8,1310721,2,9,26,2,21, +26,1579531,2,10,3,6,10,4,8,1310721,2,9,26,2,25,26, +1581737,2,10,3,6,10,4,8,1310721,2,9,26,2,27,26,1581739, +2,10,3,6,10,4,8,1310721,2,9,26,2,29,26,1581741,2, +10,3,6,10,4,8,1310721,2,9,26,2,31,26,1581743,2,10, +3,6,10,4,8,1581737,1,10,3,6,10,4,8,1310721,2,2, +3,28,1,6,1,10,28,34,63,38,3,26,1587015,16,1310721,2, +9,26,2,7,26,1310721,2,3,26,2,1,26,2,10,3,6, +10,4,8,1310721,2,2,1,28,1,6,1,10,28,34,63,38, +3,26,1587087,16,1579529,1,10,3,6,10,4,8,1579533,1,10,3, +6,10,4,8,1585861,12,8,1310721,2,2,5,28,1,6,1,10, +28,34,63,38,3,26,1587167,16,1579529,5,10,3,6,10,4,8, +1579531,1310721,2,3,26,2,10,3,6,10,4,8,1585861,12,8,1, +1587175,14,1,1310721,2,11,26,1310721,4,18,1310721,2,3,28,1310721,4, +1540609,1579535,2,33,30,26,1310721,2,4,1579517,1310721,2,2,11,26,2, +10,3,6,10,4,8,1579519,1310721,2,2,13,26,2,10,3,6, +10,4,8,1579521,1310721,2,2,15,26,2,10,3,6,10,4,8, +1579527,1310721,2,2,17,26,2,10,3,6,10,4,8,1579529,1310721,2, +2,19,26,2,10,3,6,10,4,8,1579531,1310721,2,2,21,26, +2,10,3,6,10,4,8,1581737,1310721,2,2,25,26,2,10,3, +6,10,4,8,1581739,1310721,2,2,27,26,2,10,3,6,10,4, +8,1581741,1310721,2,2,29,26,2,10,3,6,10,4,8,1581743,1310721, +2,2,31,26,2,10,3,6,10,4,8,1581745,1579523,2,10,3, +6,10,4,8,1579535,1,6,2,1,6,-1,26,5,6,4,10, +8,8,1310721,2,2,23,26,2,1587559,16,1310721,2,2,3,26,2, +1577851,12,8,1,1587567,14,1,1310721,2,3,26,1310721,4,18,1310721,2, +5,28,1310721,4,1310721,2,4,1587643,14,43,45,42,47,37,61,60, +62,33,38,124,94,126,63,58,46,64,35,36,0,1587603,1310721,2, +3,26,4,1310721,2,3,26,2,2,1587759,16,1310721,2,3,26,2, +2,1310721,2,2,28,1,6,1,10,28,34,63,38,3,26,1587721, +16,3,1587767,14,1310721,2,3,26,1,6,2,1,6,3,26,5, +6,4,10,8,8,1587655,14,1,1587767,14,1,1310721,2,5,26,1310721, +4,18,1310721,2,11,28,1310721,4,1310721,2,3,26,4,1310721,2,4, +1,1310721,2,5,26,4,1310721,2,5,26,2,1579463,2,40,1588223,16, +1310721,2,7,26,1547777,1310721,2,5,26,2,26,2,10,3,6,10, +4,8,1310721,2,9,26,1,10,3,6,10,4,8,1310721,2,9, +26,2,1310721,2,2,40,1588009,16,1310721,2,7,26,2,1310721,2,9, +26,2,26,2,1579537,1310721,2,9,26,2,26,2,28,1,6,1, +10,28,34,63,38,3,26,1,6,1,10,28,34,63,38,1, +10,28,1588011,14,1,1588053,16,1310721,2,9,26,1,6,2,1,6, +3,26,5,6,4,10,8,8,1587899,14,1310721,2,9,26,2,1310721, +2,2,28,1,6,1,10,28,34,63,38,3,26,1588173,16,1310721, +2,7,26,2,1310721,2,2,26,2,1310721,2,3,26,2,28,1, +6,1,10,28,34,63,38,3,26,1,6,1,10,28,34,63, +38,1,10,28,1588175,14,1,1588185,16,3,1588231,14,1310721,2,5,26, +1,6,2,1,6,3,26,5,6,4,10,8,8,1587821,14,1, +1588231,14,1,1310721,2,11,26,1310721,4,18,1310721,2,3,28,1310721,4, +1310721,2,4,1310721,2,2,97,40,3,36,1588323,16,1310721,2,2,115, +10,40,3,36,1,6,1,10,28,34,63,38,1,10,28,1588325, +14,1,1588331,14,1,1310721,2,3,26,1310721,4,18,1310721,2,3,28, +1310721,4,1310721,2,4,1310721,2,2,195,40,3,36,1588423,16,1310721,2, +2,245,10,40,3,36,1,6,1,10,28,34,63,38,1,10, +28,1588425,14,1,1588435,16,3,1588519,14,1310721,2,2,131,40,3,36, +1588495,16,1310721,2,2,181,10,40,3,36,1,6,1,10,28,34, +63,38,1,10,28,1588497,14,1,1,6,1,10,28,34,63,38, +1,10,28,1588529,16,3,1588581,14,1310721,2,2,191,28,1,6,1, +10,28,34,63,38,3,26,1,6,1,10,28,34,63,38,1, +10,28,1588587,14,1,1310721,2,3,26,1310721,4,18,1310721,2,3,28, +1310721,4,1310721,2,4,1310721,2,2,1588245,12,1588647,16,1310721,2,2,97, +28,1588829,14,1310721,2,2,195,40,3,36,1588707,16,1310721,2,2,205, +10,40,3,36,1,6,1,10,28,34,63,38,1,10,28,1588709, +14,1,1588731,16,1310721,2,2,195,28,21,26,1588829,14,1310721,2,2, +131,40,3,36,1588791,16,1310721,2,2,141,10,40,3,36,1,6, +1,10,28,34,63,38,1,10,28,1588793,14,1,1588815,16,1310721,2, +2,131,28,21,26,1588829,14,3,1,10,28,1588829,14,1,1310721,2, +3,26,1310721,4,18,1310721,2,3,28,1310721,4,1579527,2,1310721,2,4, +1585861,12,8,1310721,2,2,185,28,1,6,1,10,28,34,63,38, +1,10,28,1588917,16,1310721,2,2,1589155,14,1310721,2,1579527,2,10,3, +6,10,4,8,1585861,12,8,1310721,2,2,221,28,1,6,1,10, +28,34,63,38,3,26,1588983,16,21,1589155,14,1310721,2,2,233,28, +1,6,1,10,28,34,63,38,3,26,1589023,16,19,1589155,14,1310721, +2,2,229,28,1,6,1,10,28,34,63,38,3,26,1589063,16, +27,1589155,14,1310721,2,2,97,28,1,6,1,10,28,34,63,38, +3,26,1589103,16,1,1589155,14,1310721,2,2,203,28,1,6,1,10, +28,34,63,38,3,26,1589143,16,55,1589155,14,1310721,2,2,1589155,14, +1,1310721,2,3,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2, +4,1579527,2,1,40,3,36,1589237,16,1579527,2,1587581,12,1,6,1, +10,28,34,63,38,1,10,28,1589239,14,1,1589283,16,1310721,2,2, +1579527,2,1587781,12,1,6,1,10,28,34,63,38,1,10,28,1589285, +14,1,1589355,16,1579537,1310721,2,2,26,1579527,2,10,3,6,10,4, +8,1310721,2,1,6,2,1,6,3,26,5,6,4,10,8,8, +1585861,12,8,1589187,14,1579537,1310721,2,2,26,1,10,3,6,10,4, +8,1579517,9,10,3,6,10,4,8,1579519,1579537,1582663,12,10,3,6, +10,4,8,1,1589423,14,1,1310721,2,3,26,1310721,4,18,1310721,2, +7,28,1310721,4,1,1310721,2,4,1579521,3,10,3,6,10,4,8, +3,1590125,16,1579527,2,65,28,1,6,1,10,28,34,63,38,3, +26,1589517,16,3,1589567,14,1579527,2,19,28,1,6,1,10,28,34, +63,38,3,26,1,6,1,10,28,34,63,38,1,10,28,1589577, +16,3,1589627,14,1579527,2,21,28,1,6,1,10,28,34,63,38, +3,26,1,6,1,10,28,34,63,38,1,10,28,1589637,16,3, +1589687,14,1579527,2,27,28,1,6,1,10,28,34,63,38,3,26, +1,6,1,10,28,34,63,38,1,10,28,1589717,16,1585861,12,8, +1579521,1,10,3,6,10,4,8,1589479,14,1579527,2,95,28,1,6, +1,10,28,34,63,38,1,10,28,1589755,16,1590125,14,1585861,12,8, +1579527,2,95,28,1,6,1,10,28,34,63,38,3,26,1589901,16, +1579527,2,1,40,3,36,1589865,16,1579527,2,21,28,1,6,1,10, +28,34,63,38,1,10,28,1,6,1,10,28,34,63,38,1, +10,28,1589867,14,1,1589881,16,1585861,12,8,1589793,14,1579521,1,10,3, +6,10,4,8,1589473,14,1579527,2,85,28,1,6,1,10,28,34, +63,38,3,26,1590069,16,1585861,12,8,1579527,2,1,40,3,36,1590043, +16,1579527,2,85,28,1,6,1,10,28,34,63,38,3,26,1590033, +16,1585861,12,8,1579527,2,95,28,1,6,1,10,28,34,63,38, +3,26,1590029,16,1590043,14,1590039,14,1585861,12,8,1589939,14,1585861,12,8, +1579521,1,10,3,6,10,4,8,1589473,14,1579523,1579525,2,10,3,6, +10,4,8,1579537,1,26,95,10,3,6,10,4,8,3,1589169,12, +8,1,1591409,14,1589473,14,1579523,1579525,2,10,3,6,10,4,8,1579527, +2,1,40,1590193,16,1579517,1,10,3,6,10,4,8,1579519,1,10, +3,6,10,4,8,1,1591409,14,1579527,2,1588245,12,1590615,16,1310721,2, +3,26,1,10,3,6,10,4,8,1579527,2,97,28,1,6,1, +10,28,34,63,38,3,26,1590503,16,1585861,12,8,1579527,2,241,28, +1,6,1,10,28,34,63,38,3,26,1590303,16,3,1590353,14,1579527, +2,177,28,1,6,1,10,28,34,63,38,3,26,1,6,1, +10,28,34,63,38,1,10,28,1590503,16,1585861,12,8,1310721,2,5, +26,1579527,2,1588601,12,10,3,6,10,4,1,40,3,36,1590457,16, +1310721,2,3,26,1310721,2,3,26,2,33,30,1310721,2,5,26,2, +26,10,3,6,10,4,8,1585861,12,8,1590363,14,1579517,3,10,3, +6,10,4,8,1579519,1310721,2,3,26,2,10,3,6,10,4,8, +1,1591409,14,1579527,2,1588245,12,1590569,16,1310721,2,3,26,1310721,2,3, +26,2,21,30,1579527,2,26,97,28,10,3,6,10,4,8,1585861, +12,8,1590503,14,1579517,3,10,3,6,10,4,8,1579519,1310721,2,3, +26,2,10,3,6,10,4,8,1,1591409,14,1579527,2,1588345,12,1590825, +16,1579527,2,1588345,12,1590645,16,3,1590675,14,1579527,2,1588245,12,1,6, +1,10,28,34,63,38,1,10,28,1590759,16,1310721,2,2,2001,40, +1590749,16,1579537,1310721,2,2,26,1579527,2,10,3,6,10,4,8,1310721, +2,1,6,2,1,6,3,26,5,6,4,10,8,8,1585861,12, +8,1590627,14,1579537,1310721,2,2,26,1,10,3,6,10,4,8,1579517, +5,10,3,6,10,4,8,1579519,1579537,1582663,12,10,3,6,10,4, +8,1,1591409,14,1579527,2,79,28,1,6,1,10,28,34,63,38, +3,26,1590999,16,1585861,12,8,1579519,1588843,12,10,3,6,10,4,8, +1579527,2,79,28,1,6,1,10,28,34,63,38,1,10,28,1590971, +16,1590963,14,98,97,100,32,99,104,97,114,97,99,116,101,114, +32,108,105,116,101,114,97,108,0,1590919,1581991,12,8,1585861,12,8, +1579517,3,10,3,6,10,4,8,1,1591409,14,1579527,2,69,28,1, +6,1,10,28,34,63,38,3,26,1591291,16,1585861,12,8,1579527,2, +1,40,3,36,1591109,16,1579527,2,69,28,1,6,1,10,28,34, +63,38,1,10,28,1,6,1,10,28,34,63,38,1,10,28, +1591111,14,1,1591219,16,1310721,2,3,26,1588843,12,10,3,6,10,4, +8,1310721,2,2,2001,40,1591215,16,1579537,1310721,2,2,26,1310721,2,3, +26,2,10,3,6,10,4,8,1310721,2,1,6,2,1,6,3, +26,5,6,4,10,8,8,1591037,14,1585861,12,8,1579537,1310721,2,2, +26,1,10,3,6,10,4,8,1579517,7,10,3,6,10,4,8, +1579519,1579537,1582663,12,10,3,6,10,4,8,1,1591409,14,1579537,1,26, +1579527,2,10,3,6,10,4,8,1585861,12,8,1579537,1,26,2,1587581, +12,1591345,16,3,1589169,12,1591409,14,1579537,3,26,1,10,3,6,10, +4,8,1579517,9,10,3,6,10,4,8,1579519,1579537,1582663,12,10,3, +6,10,4,8,1,1591409,14,1,1310721,2,7,26,1310721,4,18,1310721, +2,1,28,1310721,4,1581737,2,1591537,16,1579517,1581739,2,10,3,6,10, +4,8,1579519,1581741,2,10,3,6,10,4,8,1579521,1581743,2,10,3, +6,10,4,8,1579523,1581745,2,10,3,6,10,4,8,1581737,1,10, +3,6,10,4,8,1,1591547,14,1591887,12,1591547,14,1,1310721,2,1, +26,1310721,4,18,1310721,2,9,28,1310721,4,1581737,2,1591587,16,1,1591873, +14,1310721,2,1579517,2,10,3,6,10,4,8,1310721,2,3,26,1579519, +2,10,3,6,10,4,8,1310721,2,5,26,1579521,2,10,3,6, +10,4,8,1310721,2,7,26,1579523,2,10,3,6,10,4,8,1591887, +12,8,1581739,1579517,2,10,3,6,10,4,8,1581741,1579519,2,10,3, +6,10,4,8,1581743,1579521,2,10,3,6,10,4,8,1581745,1579523,2, +10,3,6,10,4,8,1581737,3,10,3,6,10,4,8,1579517,1310721, +2,2,10,3,6,10,4,8,1579519,1310721,2,3,26,2,10,3, +6,10,4,8,1579521,1310721,2,5,26,2,10,3,6,10,4,8, +1579523,1310721,2,7,26,2,10,3,6,10,4,8,1,1591873,14,1, +1310721,2,9,26,1310721,4,18,1310721,2,7,28,1310721,4,1310721,2,1540609, +1579535,2,33,30,26,10,3,6,10,4,8,1310721,2,2,1,26, +2,3,28,1,6,1,10,28,34,63,38,1,10,28,1591981,16, +1589437,12,8,1,1592327,14,1310721,2,5,26,1310721,2,2,5,26,2, +10,3,6,10,4,8,1310721,2,5,26,2,1310721,2,2,7,26, +2,40,1592239,16,1310721,2,3,26,1310721,2,2,3,26,2,10,3, +6,10,4,8,1579517,1310721,2,3,26,2,3,1310721,2,5,26,2, +26,1310721,2,5,26,2,26,26,2,10,3,6,10,4,8,1579519, +1310721,2,3,26,2,5,1310721,2,5,26,2,26,1310721,2,5,26, +2,26,26,2,10,3,6,10,4,8,1579521,1,10,3,6,10, +4,8,1310721,2,2,5,26,1310721,2,5,26,2,3,26,10,3, +6,10,4,8,1,1592327,14,1310721,2,2,9,26,2,1,6,1, +10,28,34,63,38,3,26,1592313,16,1579517,1,10,3,6,10,4, +8,1579519,1,10,3,6,10,4,8,1,1592327,14,1587189,12,8,1, +1592327,14,1,1310721,2,7,26,1310721,4,18,1310721,2,3,28,1310721,4, +1310721,2,4,1579517,2,9,28,1,6,1,10,28,34,63,38,3, +26,1592449,16,1579519,2,1310721,2,2,28,1,6,1,10,28,34,63, +38,3,26,1,6,1,10,28,34,63,38,1,10,28,1592451,14, +1,1592457,14,1,1310721,2,3,26,1310721,4,18,1310721,2,3,28,1310721, +4,1310721,2,4,1579517,2,5,28,1,6,1,10,28,34,63,38, +3,26,1592579,16,1579519,2,1310721,2,2,28,1,6,1,10,28,34, +63,38,3,26,1,6,1,10,28,34,63,38,1,10,28,1592581, +14,1,1592587,14,1,1310721,2,3,26,1310721,4,18,1310721,2,3,28, +1310721,4,1310721,2,4,1579517,2,9,28,1,6,1,10,28,34,63, +38,3,26,1592657,16,3,1592707,14,1579517,2,5,28,1,6,1,10, +28,34,63,38,3,26,1,6,1,10,28,34,63,38,1,10, +28,1592769,16,1579519,2,1310721,2,2,28,1,6,1,10,28,34,63, +38,3,26,1,6,1,10,28,34,63,38,1,10,28,1592771,14, +1,1592787,16,1591423,12,8,3,1592795,14,1,1592795,14,1,1310721,2,3, +26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,4,1310721,2,2, +1592601,12,1,6,1,10,28,34,63,38,3,26,1592965,16,1592885,14, +122,99,58,32,108,105,110,101,32,0,1592865,1573231,12,8,1579523,2, +1573371,12,8,1592931,14,58,32,101,120,112,101,99,116,101,100,32, +0,1592907,1573231,12,8,1310721,2,2,1573231,12,8,21,1573079,12,8,1573195, +12,8,1,1592973,14,1,1310721,2,3,26,1310721,4,18,1310721,2,3, +28,1310721,4,1579519,2,1310721,2,4,1579517,2,5,28,1,6,1,10, +28,34,63,38,1,10,28,1593087,16,1593079,14,101,120,112,101,99, +116,101,100,32,97,32,110,97,109,101,0,1593047,1581991,12,8,1591423, +12,8,1310721,2,2,1593105,14,1,1310721,2,3,26,1310721,4,18,1310721, +2,15,28,1310721,4,1310721,2,4,1579465,2,1310721,2,3,26,4,3, +1594205,16,1310721,2,2,2,65,28,1,6,1,10,28,34,63,38, +3,26,1593227,16,1310721,2,1,6,2,1,6,3,26,5,6,4, +10,8,8,1593157,14,1310721,2,2,2,1,6,1,10,28,34,63, +38,3,26,1593263,16,1594205,14,1310721,2,5,26,1,10,3,6,10, +4,8,1310721,2,2,1310721,2,5,26,2,26,2,1593381,16,1310721,2, +2,1310721,2,5,26,2,26,2,65,28,1,6,1,10,28,34, +63,38,1,10,28,1,6,1,10,28,34,63,38,1,10,28, +1593383,14,1,1593471,16,1579537,1310721,2,5,26,2,26,1310721,2,2,1310721, +2,5,26,2,26,2,10,3,6,10,4,8,1310721,2,5,26, +1,6,2,1,6,3,26,5,6,4,10,8,8,1593285,14,1579537, +1310721,2,5,26,2,26,1,10,3,6,10,4,8,1310721,2,1310721, +2,2,1310721,2,5,26,2,26,10,3,6,10,4,8,1310721,2, +7,26,1579537,1,26,2,91,28,1,6,1,10,28,34,63,38, +3,26,1593615,16,1579537,3,26,2,1588245,12,1,6,1,10,28,34, +63,38,1,10,28,1593617,14,1,10,3,6,10,4,8,1579537,1310721, +2,7,26,2,26,2,1588245,12,1593945,16,1310721,2,9,26,1,10, +3,6,10,4,8,1310721,2,11,26,1310721,2,7,26,2,10,3, +6,10,4,8,1579537,1310721,2,11,26,2,26,2,1593819,16,1310721,2, +9,26,1310721,2,9,26,2,21,30,1579537,1310721,2,11,26,2,26, +2,26,97,28,10,3,6,10,4,8,1310721,2,11,26,1,6, +2,1,6,3,26,5,6,4,10,8,8,1593705,14,1310721,2,7, +26,2,1593867,16,1310721,2,9,26,1,1310721,2,9,26,2,28,10, +3,6,10,4,8,1542657,1579465,2,26,1310721,2,9,26,2,1310721,2, +9,26,2,26,3,26,10,3,6,10,4,8,1579465,1,6,2, +1,6,3,26,5,6,4,10,8,8,1594201,14,1310721,2,5,26, +2,3,28,1,6,1,10,28,34,63,38,3,26,1594047,16,1310721, +2,13,26,1579537,1,26,2,1594235,12,10,3,6,10,4,1,40, +3,36,1,6,1,10,28,34,63,38,1,10,28,1594049,14,1, +1594127,16,1542657,1579465,2,26,1310721,2,13,26,2,1310721,2,13,26,2, +26,10,3,6,10,4,8,1579465,1,6,2,1,6,3,26,5, +6,4,10,8,8,1594201,14,1542657,1579465,2,26,-3,10,3,6,10, +4,8,1542657,1579465,2,3,26,26,1579537,1582663,12,10,3,6,10,4, +8,1579465,1579465,2,5,26,10,3,6,10,4,8,1593151,14,1310721,2, +3,26,2,1594221,14,1,1310721,2,15,26,1310721,4,18,1310721,2,5, +28,1310721,4,1310721,2,4,1,1310721,2,3,26,4,1579467,2,1310721,2, +3,26,2,26,2,1594389,16,1579467,2,1310721,2,3,26,2,26,2, +1310721,2,2,28,1,6,1,10,28,34,63,38,3,26,1594351,16, +1310721,2,3,26,2,1594403,14,1310721,2,3,26,1,6,2,1,6, +3,26,5,6,4,10,8,8,1594265,14,3,1,10,28,1594403,14, +1,1310721,2,5,26,1310721,4,18,1310721,2,9,28,1310721,4,1310721,2, +3,26,4,1310721,2,4,1310721,2,2,1310721,2,3,26,2,26,1310721, +2,5,26,4,1310721,2,2,1310721,2,5,26,2,40,1594875,16,1542657, +1310721,2,2,26,2,-3,28,1,6,1,10,28,34,63,38,3, +26,1594823,16,1310721,2,7,26,1542657,1310721,2,2,3,26,26,2,1583635, +12,10,3,6,10,4,8,1310721,2,7,26,2,1,40,1594633,16, +1310721,2,7,26,1542657,1310721,2,2,3,26,26,2,1585201,12,10,3, +6,10,4,8,1417729,1310721,2,7,26,2,26,2,3,28,1,6, +1,10,28,34,63,38,3,26,1594697,16,1310721,2,7,26,2,1585301, +12,8,1594793,14,1417729,1310721,2,7,26,2,26,2,13,28,1,6, +1,10,28,34,63,38,3,26,1594771,16,45,1425921,1310721,2,7,26, +2,26,2,26,1584907,12,8,1594793,14,1425921,1310721,2,7,26,2,26, +2,1584843,12,8,1310721,2,1310721,2,2,5,26,10,3,6,10,4, +8,1594871,14,1542657,1310721,2,2,26,2,1584671,12,8,1310721,2,1,6, +2,1,6,3,26,5,6,4,10,8,8,1594473,14,1,1594883,14, +1,1310721,2,9,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2, +4,1,1310721,2,3,26,4,1310721,2,3,26,2,1579463,2,40,1595039, +16,1547777,1310721,2,3,26,2,26,2,1310721,2,2,28,1,6,1, +10,28,34,63,38,3,26,1595001,16,1,1595101,14,1310721,2,3,26, +1,6,2,1,6,3,26,5,6,4,10,8,8,1594927,14,1547777, +1579463,2,26,1310721,2,2,10,3,6,10,4,8,1579463,1,6,2, +1,6,3,26,5,6,4,10,8,8,1,1595101,14,1,1310721,2, +5,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,4,1579457,2, +1310721,2,3,26,4,1310721,2,3,26,2,1595261,16,1310721,2,3,26, +1,6,2,1,6,-1,26,5,6,4,10,8,8,1546753,1310721,2, +3,26,2,26,2,1310721,2,2,28,1,6,1,10,28,34,63, +38,3,26,1595257,16,1310721,2,3,26,2,1595275,14,1595147,14,3,1, +10,28,1595275,14,1,1310721,2,5,26,1310721,4,18,1310721,2,5,28, +1310721,4,1310721,2,4,1579459,2,1310721,2,3,26,4,1310721,2,3,26, +2,1595435,16,1310721,2,3,26,1,6,2,1,6,-1,26,5,6, +4,10,8,8,1547265,1310721,2,3,26,2,26,2,1310721,2,2,28, +1,6,1,10,28,34,63,38,3,26,1595431,16,1310721,2,3,26, +2,1595449,14,1595321,14,3,1,10,28,1595449,14,1,1310721,2,5,26, +1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,4,1579461,2,1310721,2, +3,26,4,1310721,2,3,26,2,1595609,16,1310721,2,3,26,1,6, +2,1,6,-1,26,5,6,4,10,8,8,1547457,1310721,2,3,26, +2,26,2,1310721,2,2,28,1,6,1,10,28,34,63,38,3, +26,1595605,16,1310721,2,3,26,2,1595623,14,1595495,14,3,1,10,28, +1595623,14,1,1310721,2,5,26,1310721,4,18,1310721,2,15,28,1310721,4, +1310721,2,5,26,4,1310721,2,3,26,4,1310721,2,4,1310721,2,2, +1582663,12,1310721,2,7,26,4,1310721,2,7,26,2,1595115,12,1310721,2, +9,26,4,1310721,2,9,26,2,1,40,1595789,16,1310721,2,9,26, +1579457,2,10,3,6,10,4,8,1579457,1,6,2,1,6,3,26, +5,6,4,10,8,8,1546753,1310721,2,9,26,2,26,1310721,2,7, +26,2,10,3,6,10,4,8,1546881,1310721,2,9,26,2,26,1310721, +2,3,26,2,10,3,6,10,4,8,1310721,2,3,26,2,1, +40,1596013,16,1546881,1310721,2,9,26,2,26,1,1310721,2,3,26,2, +28,10,3,6,10,4,8,1547137,1310721,2,9,26,2,26,1310721,2, +3,26,2,10,3,6,10,4,8,1547009,1310721,2,9,26,2,26, +1,10,3,6,10,4,8,1310721,2,7,26,2,1594897,12,8,1310721, +2,9,26,2,1596641,14,1547009,1310721,2,9,26,2,26,1310721,2,5, +26,2,1593119,12,10,3,6,10,4,8,1547137,1310721,2,9,26,2, +26,1579465,2,1547009,1310721,2,9,26,2,26,2,28,10,3,6,10, +4,8,1310721,2,7,26,2,1594897,12,8,1310721,2,13,26,1310721,2, +2,1573731,12,10,3,6,10,4,8,1579537,2001,26,1310721,2,2,1310721, +2,13,26,2,1574139,12,8,1579537,2001,1310721,2,13,26,2,26,26, +123,10,3,6,10,4,8,1579537,2003,1310721,2,13,26,2,26,26, +1,10,3,6,10,4,8,1310721,2,11,26,1579537,2001,26,1582663,12, +10,3,6,10,4,8,1310721,2,3,26,2,17,40,3,36,1596297, +16,3,1596353,14,1310721,2,3,26,2,11,28,1,6,1,10,28, +34,63,38,3,26,1,6,1,10,28,34,63,38,1,10,28, +1596363,16,3,1596419,14,1310721,2,3,26,2,9,28,1,6,1,10, +28,34,63,38,3,26,1,6,1,10,28,34,63,38,1,10, +28,1596429,16,3,1596485,14,1310721,2,3,26,2,7,28,1,6,1, +10,28,34,63,38,3,26,1,6,1,10,28,34,63,38,1, +10,28,1596625,16,1310721,2,11,26,2,1595463,12,1,40,1596569,16,1547457, +1579461,2,26,1310721,2,11,26,2,10,3,6,10,4,8,1579461,1, +6,2,1,6,3,26,5,6,4,10,8,8,1547585,1310721,2,11, +26,2,1595463,12,26,1310721,2,9,26,2,10,3,6,10,4,8, +1310721,2,11,26,2,1594897,12,8,1310721,2,9,26,2,1596641,14,1, +1310721,2,15,26,1310721,4,18,1310721,2,9,28,1310721,4,1310721,2,3, +26,4,1310721,2,4,1310721,2,2,1582663,12,1310721,2,5,26,4,1310721, +2,5,26,2,1595289,12,1310721,2,7,26,4,1310721,2,7,26,2, +1,40,1596797,16,1310721,2,7,26,1579459,2,10,3,6,10,4,8, +1579459,1,6,2,1,6,3,26,5,6,4,10,8,8,1547265,1310721, +2,7,26,2,26,1310721,2,5,26,2,10,3,6,10,4,8, +1547329,1310721,2,7,26,2,26,1310721,2,3,26,2,1593119,12,10,3, +6,10,4,8,1547393,1310721,2,7,26,2,26,1579465,2,1547329,1310721,2, +7,26,2,26,2,28,10,3,6,10,4,8,1310721,2,5,26, +2,1594897,12,8,1310721,2,7,26,2,1596953,14,1,1310721,2,9,26, +1310721,4,18,1310721,2,9,28,1310721,4,1310721,2,5,26,4,1310721,2, +3,26,4,1310721,2,4,1310721,2,2,1582663,12,11,1310721,2,3,26, +2,1583159,12,1310721,2,7,26,4,1442305,1310721,2,7,26,2,26,3, +1310721,2,5,26,2,34,10,3,6,10,4,8,1310721,2,7,26, +2,1597097,14,1,1310721,2,9,26,1310721,4,18,1310721,2,5,28,1310721, +4,1310721,2,4,1581755,2,1310721,2,3,26,4,1310721,2,3,26,2, +1581757,2,10,40,1597265,16,1310721,2,3,26,1,6,2,1,6,-1, +26,5,6,4,10,8,8,1466881,1310721,2,3,26,2,26,2,1310721, +2,2,28,1,6,1,10,28,34,63,38,3,26,1597261,16,1310721, +2,3,26,2,1597279,14,1597143,14,3,1,10,28,1597279,14,1,1310721, +2,5,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,4,1581755, +2,1310721,2,3,26,4,1581755,2,1025,40,3,36,1597385,16,1597377,14, +116,111,111,32,109,97,110,121,32,108,111,99,97,108,115,0, +1597345,1581991,12,8,1581755,1,6,2,1,6,3,26,5,6,4,10, +8,8,1466881,1310721,2,3,26,2,26,1310721,2,2,10,3,6,10, +4,8,1467905,1310721,2,3,26,2,26,1581761,2,10,3,6,10,4, +8,1468929,1310721,2,3,26,2,26,1581759,2,10,3,6,10,4,8, +1469953,1310721,2,3,26,2,26,1,10,3,6,10,4,8,1470977,1310721, +2,3,26,2,26,1,10,3,6,10,4,8,1472001,1310721,2,3, +26,2,26,1,10,3,6,10,4,8,1473025,1310721,2,3,26,2, +26,1,10,3,6,10,4,8,1474049,1310721,2,3,26,2,26,1, +10,3,6,10,4,8,1556993,1310721,2,3,26,2,26,1581877,2,10, +3,6,10,4,8,1558017,1310721,2,3,26,2,26,1,10,3,6, +10,4,8,1310721,2,3,26,2,1597719,14,1,1310721,2,5,26,1310721, +4,18,1310721,2,3,28,1310721,4,1310721,2,4,1469953,1310721,2,2,26, +2,11,28,1,6,1,10,28,34,63,38,3,26,1597823,16,1310721, +2,1470977,1310721,2,2,26,2,10,3,6,10,4,8,1597751,14,1310721, +2,2,1597835,14,1,1310721,2,3,26,1310721,4,18,1310721,2,3,28, +1310721,4,1310721,2,4,1469953,1310721,2,2,26,2,1,28,1,6,1, +10,28,34,63,38,1,10,28,1597911,14,1,1310721,2,3,26,1310721, +4,18,1310721,2,5,28,1310721,4,1310721,2,4,1472001,1310721,2,2,26, +3,10,3,6,10,4,8,1581763,2,1,6,1,10,28,34,63, +38,3,26,1598001,16,1,1598377,14,1310721,2,3,26,1310721,2,2,1597733, +12,10,3,6,10,4,8,1472001,1310721,2,3,26,2,26,3,10, +3,6,10,4,8,1469953,1310721,2,3,26,2,26,2,9,28,1, +6,1,10,28,34,63,38,3,26,1598185,16,1310721,2,2,1598171,14, +117,115,101,100,32,97,102,116,101,114,32,105,116,32,119,97, +115,32,103,105,118,101,110,32,97,119,97,121,0,1598113,1582383,12, +8,1,1598377,14,1469953,1310721,2,3,26,2,26,2,7,28,1,6, +1,10,28,34,63,38,3,26,1598277,16,1468929,1310721,2,3,26,2, +26,2,1581759,2,40,1,6,1,10,28,34,63,38,1,10,28, +1598279,14,1,1598369,16,1468929,1310721,2,3,26,2,26,1581759,2,10,3, +6,10,4,8,1469953,1310721,2,3,26,2,26,3,10,3,6,10, +4,8,1474049,1310721,2,3,26,2,26,3,10,3,6,10,4,8, +1,1598377,14,1,1310721,2,5,26,1310721,4,18,1310721,2,3,28,1310721, +4,1310721,2,4,1310721,2,2,3,28,1,6,1,10,28,34,63, +38,3,26,1598593,16,1598585,14,97,32,110,101,119,32,111,98,106, +101,99,116,32,99,97,110,39,116,32,98,101,32,117,115,101, +100,32,97,115,32,97,32,112,108,97,105,110,32,118,97,108, +117,101,59,32,98,105,110,100,32,105,116,32,116,111,32,97, +32,110,97,109,101,32,102,105,114,115,116,0,1598447,1582287,12,8, +1310721,2,2,5,40,3,36,1598627,16,1310721,2,2,5,28,1597925,12, +8,1,1598635,14,1,1310721,2,3,26,1310721,4,18,1310721,2,3,28, +1310721,4,1310721,2,4,1467905,1310721,2,2,26,2,1585585,12,8,3,1584907, +12,8,1581861,2,1585453,12,8,9,1584907,12,8,1,1598719,14,1,1310721, +2,3,26,1310721,4,18,1310721,2,9,28,1310721,4,1310721,2,3,26, +4,1310721,2,4,1581755,2,1310721,2,5,26,4,1581763,2,1,6,1, +10,28,34,63,38,3,26,1598809,16,3,1598855,14,1581767,2,1,6, +1,10,28,34,63,38,3,26,1,6,1,10,28,34,63,38, +1,10,28,1598865,16,1,1599425,14,1310721,2,5,26,2,1581757,2,10, +40,1599417,16,1310721,2,5,26,1,6,2,1,6,-1,26,5,6, +4,10,8,8,1468929,1310721,2,5,26,2,26,2,1310721,2,2,40, +1598953,16,1598865,14,1310721,2,7,26,1469953,1310721,2,5,26,2,26,2, +10,3,6,10,4,8,1310721,2,7,26,2,3,28,1,6,1, +10,28,34,63,38,3,26,1599047,16,1310721,2,5,26,2,1598649,12, +8,1599413,14,1310721,2,7,26,2,5,28,1,6,1,10,28,34, +63,38,3,26,1599293,16,1310721,2,5,26,2,1599281,14,117,32,111, +98,106,101,99,116,32,109,117,115,116,32,108,101,97,118,101, +32,116,104,105,115,32,98,108,111,99,107,32,40,98,108,111, +99,107,32,118,97,108,117,101,44,32,97,115,115,105,103,110, +109,101,110,116,32,116,111,32,116,104,101,32,101,110,99,108, +111,115,105,110,103,32,98,108,111,99,107,44,32,111,114,32, +114,101,116,117,114,110,41,0,1599099,1582383,12,8,1599413,14,1310721,2, +7,26,2,7,28,1,6,1,10,28,34,63,38,3,26,1599413, +16,1310721,2,5,26,2,1599405,14,100,32,111,98,106,101,99,116, +32,119,97,115,32,110,101,118,101,114,32,103,105,118,101,110, +32,97,119,97,121,0,1599345,1582383,12,8,1598865,14,1,1599425,14,1, +1310721,2,9,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,4, +1310721,2,2,1581755,2,40,1599693,16,1581763,2,1599519,16,1474049,1310721,2,2, +26,2,1,6,1,10,28,34,63,38,1,10,28,1599521,14,1, +1599583,16,1472001,1310721,2,2,26,2,1,6,1,10,28,34,63,38, +3,26,1,6,1,10,28,34,63,38,1,10,28,1599585,14,1, +1599659,16,1466881,1310721,2,2,26,2,1599651,14,109,32,111,98,106,101, +99,116,32,105,115,32,110,101,118,101,114,32,117,115,101,100, +0,1599605,1582117,12,8,1310721,2,1,6,2,1,6,3,26,5,6, +4,10,8,8,1599457,14,1,1599701,14,1,1310721,2,3,26,1310721,4, +18,1310721,2,5,28,1310721,4,1310721,2,4,1581757,2,1310721,2,3,26, +4,1310721,2,2,1599439,12,8,1581759,2,3,1598733,12,8,1310721,2,3, +26,2,1310721,2,2,40,1599923,16,1468929,1310721,2,3,26,2,26,2, +1581759,2,40,3,36,1599885,16,1469953,1310721,2,3,26,2,26,9,10, +3,6,10,4,8,1468929,1310721,2,3,26,2,26,1581759,2,3,28, +10,3,6,10,4,8,1310721,2,3,26,1,6,2,1,6,3, +26,5,6,4,10,8,8,1599771,14,1581755,1310721,2,2,10,3,6, +10,4,8,1,1599951,14,1,1310721,2,5,26,1310721,4,18,1310721,2, +7,28,1310721,4,1581783,2,1310721,2,4,1581757,2,1310721,2,3,26,4, +1310721,2,5,26,1507841,1310721,2,2,26,10,3,6,10,4,8,1310721, +2,5,26,2,1,26,1581757,2,10,3,6,10,4,8,1310721,2, +5,26,2,3,26,1581755,2,10,3,6,10,4,8,1310721,2,5, +26,1310721,2,5,26,2,5,26,10,3,6,10,4,8,1310721,2, +3,26,2,1581755,2,40,1600343,16,1310721,2,5,26,2,1,26,1469953, +1310721,2,3,26,2,26,2,10,3,6,10,4,8,1310721,2,5, +26,2,3,26,1470977,1310721,2,3,26,2,26,2,10,3,6,10, +4,8,1310721,2,5,26,2,5,26,1468929,1310721,2,3,26,2,26, +2,10,3,6,10,4,8,1310721,2,5,26,1310721,2,5,26,2, +7,26,10,3,6,10,4,8,1310721,2,3,26,1,6,2,1, +6,3,26,5,6,4,10,8,8,1600125,14,1581783,1310721,2,5,26, +2,1507841,28,10,3,6,10,4,8,1581783,2,16001,40,3,36,1600467, +16,1600459,14,110,101,115,116,105,110,103,32,116,111,111,32,100, +101,101,112,32,102,111,114,32,116,104,101,32,97,110,97,108, +121,122,101,114,0,1600391,1581991,12,8,1310721,2,2,1600479,14,1,1310721, +2,7,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,4,1581783, +1310721,2,2,10,3,6,10,4,8,1,1600539,14,1,1310721,2,3, +26,1310721,4,18,1310721,2,9,28,1310721,4,1310721,2,4,1507841,1310721,2, +2,26,1310721,2,3,26,4,1310721,2,3,26,2,1,26,2,1310721, +2,5,26,4,1310721,2,3,26,2,3,26,2,1310721,2,7,26, +4,1310721,2,7,26,2,1581755,2,10,40,1600689,16,1310721,2,7,26, +1581755,2,10,3,6,10,4,8,1310721,2,3,26,1310721,2,3,26, +2,5,26,10,3,6,10,4,8,1310721,2,5,26,2,1310721,2, +7,26,2,40,1600947,16,1469953,1310721,2,5,26,2,26,1310721,2,3, +26,2,1,26,2,10,3,6,10,4,8,1470977,1310721,2,5,26, +2,26,1310721,2,3,26,2,3,26,2,10,3,6,10,4,8, +1468929,1310721,2,5,26,2,26,1310721,2,3,26,2,5,26,2,10, +3,6,10,4,8,1310721,2,3,26,1310721,2,3,26,2,7,26, +10,3,6,10,4,8,1310721,2,5,26,1,6,2,1,6,3, +26,5,6,4,10,8,8,1600723,14,1,1600955,14,1,1310721,2,9, +26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,4,1310721,2,2, +1,28,1,6,1,10,28,34,63,38,3,26,1601027,16,3,1601079, +14,1310721,2,2,9,28,1,6,1,10,28,34,63,38,3,26, +1,6,1,10,28,34,63,38,1,10,28,1601085,14,1,1310721,2, +3,26,1310721,4,18,1310721,2,11,28,1310721,4,1310721,2,4,1507841,1310721, +2,2,26,1310721,2,3,26,4,1310721,2,3,26,2,1,26,2, +1310721,2,5,26,4,1310721,2,3,26,2,3,26,2,1310721,2,7, +26,4,1310721,2,7,26,2,1581755,2,10,40,1601235,16,1310721,2,7, +26,1581755,2,10,3,6,10,4,8,1310721,2,3,26,1310721,2,3, +26,2,5,26,10,3,6,10,4,8,1310721,2,5,26,2,1310721, +2,7,26,2,40,1601859,16,1310721,2,9,26,1469953,1310721,2,5,26, +2,26,2,10,3,6,10,4,8,1310721,2,9,26,2,1600969,12, +1601395,16,1310721,2,3,26,2,1,26,2,1600969,12,1,6,1,10, +28,34,63,38,1,10,28,1601397,14,1,1601477,16,1310721,2,3,26, +2,1,26,2,9,28,1,6,1,10,28,34,63,38,3,26, +1601473,16,1469953,1310721,2,5,26,2,26,9,10,3,6,10,4,8, +1601787,14,1310721,2,9,26,2,1310721,2,3,26,2,1,26,2,28, +1,6,1,10,28,34,63,38,1,10,28,1601537,16,3,1601615,14, +1468929,1310721,2,5,26,2,26,2,1310721,2,3,26,2,5,26,2, +28,1,6,1,10,28,34,63,38,1,10,28,1,6,1,10, +28,34,63,38,1,10,28,1601625,16,3,1601769,14,1310721,2,9,26, +2,11,28,1,6,1,10,28,34,63,38,3,26,1601745,16,1470977, +1310721,2,5,26,2,26,2,1310721,2,3,26,2,3,26,2,28, +1,6,1,10,28,34,63,38,1,10,28,1,6,1,10,28, +34,63,38,1,10,28,1601747,14,1,1,6,1,10,28,34,63, +38,1,10,28,1601787,16,1310721,2,5,26,2,1601873,14,1310721,2,3, +26,1310721,2,3,26,2,7,26,10,3,6,10,4,8,1310721,2, +5,26,1,6,2,1,6,3,26,5,6,4,10,8,8,1601269, +14,3,1,10,28,1601873,14,1,1310721,2,11,26,1310721,4,18,1310721, +2,3,28,1310721,4,1310721,2,4,1310721,2,2,1,28,1,6,1, +10,28,34,63,38,3,26,1601961,16,1601955,14,101,109,112,116,121, +0,1601943,1602287,14,1310721,2,2,3,28,1,6,1,10,28,34,63, +38,3,26,1602009,16,1602003,14,109,0,1601999,1602287,14,1310721,2,2,5, +28,1,6,1,10,28,34,63,38,3,26,1602057,16,1602051,14,117, +0,1602047,1602287,14,1310721,2,2,7,28,1,6,1,10,28,34,63, +38,3,26,1602105,16,1602099,14,100,0,1602095,1602287,14,1310721,2,2,9, +28,1,6,1,10,28,34,63,38,3,26,1602171,16,1602165,14,103, +105,118,101,110,32,97,119,97,121,0,1602143,1602287,14,1310721,2,2, +11,28,1,6,1,10,28,34,63,38,3,26,1602257,16,1602251,14, +111,119,110,101,100,32,98,121,32,97,32,99,111,110,116,97, +105,110,101,114,0,1602209,1602287,14,1602279,14,98,111,114,114,111,119, +101,100,0,1602261,1602287,14,1,1310721,2,3,26,1310721,4,18,1310721,2, +7,28,1310721,4,1310721,2,5,26,4,1310721,2,3,26,4,1310721,2, +4,1581763,2,1,6,1,10,28,34,63,38,3,26,1602373,16,1, +1602649,14,1602397,14,122,99,58,32,108,105,110,101,32,0,1602377,1573231, +12,8,1579523,2,1573371,12,8,1602425,14,58,32,0,1602419,1573231,12,8, +1466881,1310721,2,2,26,2,1573231,12,8,1602461,14,58,32,0,1602455,1573231, +12,8,1310721,2,5,26,2,1573231,12,8,1602533,14,32,40,111,110, +101,32,112,97,116,104,32,108,101,97,118,101,115,32,105,116, +32,0,1602489,1573231,12,8,1310721,2,3,26,2,1601887,12,1573231,12,8, +1602587,14,44,32,97,110,111,116,104,101,114,32,0,1602565,1573231,12, +8,1469953,1310721,2,2,26,2,1601887,12,1573231,12,8,1602627,14,41,10, +0,1602621,1573231,12,8,1573195,12,8,1,1602649,14,1,1310721,2,7,26, +1310721,4,18,1310721,2,9,28,1310721,4,1310721,2,3,26,4,1310721,2, +4,1310721,2,3,26,2,1,6,1,10,28,34,63,38,3,26, +1602731,16,1,1603049,14,1581767,2,1,6,1,10,28,34,63,38,3, +26,1602793,16,1310721,2,2,1600553,12,8,1581767,3,10,3,6,10,4, +8,1,1603049,14,1310721,2,5,26,1310721,2,2,1601099,12,10,3,6, +10,4,8,1310721,2,5,26,2,1,40,3,36,1603041,16,1310721,2, +7,26,1507841,1310721,2,2,26,5,26,7,1310721,2,5,26,2,1507841, +1310721,2,2,26,2,28,30,26,10,3,6,10,4,8,1310721,2, +5,26,2,1310721,2,7,26,2,1,26,2,1603033,14,101,110,100, +115,32,116,104,101,32,98,114,97,110,99,104,101,115,32,111, +102,32,97,110,32,105,102,32,105,110,32,100,105,102,102,101, +114,101,110,116,32,115,116,97,116,101,115,0,1602939,1602301,12,8, +1,1603049,14,1,1310721,2,9,26,1310721,4,18,1310721,2,7,28,1310721, +4,1310721,2,4,1581763,2,1,6,1,10,28,34,63,38,3,26, +1603115,16,3,1603161,14,1581767,2,1,6,1,10,28,34,63,38,3, +26,1,6,1,10,28,34,63,38,1,10,28,1603171,16,1,1603473, +14,1310721,2,3,26,1310721,2,2,1601099,12,10,3,6,10,4,8, +1310721,2,3,26,2,1,40,3,36,1603465,16,1310721,2,5,26,1507841, +1310721,2,2,26,5,26,7,1310721,2,3,26,2,1507841,1310721,2,2, +26,2,28,30,26,10,3,6,10,4,8,1310721,2,3,26,2, +1310721,2,5,26,2,1,26,2,1603457,14,99,104,97,110,103,101, +115,32,115,116,97,116,101,32,105,110,115,105,100,101,32,97, +32,108,111,111,112,59,32,101,97,99,104,32,112,97,115,115, +32,104,97,115,32,116,111,32,108,101,97,118,101,32,105,116, +32,97,115,32,105,116,32,102,111,117,110,100,32,105,116,0, +1603317,1602301,12,8,1,1603473,14,1,1310721,2,7,26,1310721,4,18,1310721, +2,9,28,1310721,4,1310721,2,3,26,4,1310721,2,4,1581763,2,1, +6,1,10,28,34,63,38,3,26,1603553,16,1310721,2,2,1604523,14, +1310721,2,2,5,40,1603669,16,1603651,14,111,110,108,121,32,97,32, +110,97,109,101,100,32,111,119,110,101,100,32,111,98,106,101, +99,116,32,99,97,110,32,98,101,32,114,101,99,97,115,116, +0,1603571,1582287,12,8,1310721,2,2,1604523,14,1310721,2,5,26,1310721,2, +2,5,28,10,3,6,10,4,8,1310721,2,7,26,1469953,1310721,2, +5,26,2,26,2,10,3,6,10,4,8,1472001,1310721,2,5,26, +2,26,3,10,3,6,10,4,8,1310721,2,7,26,2,11,28, +1,6,1,10,28,34,63,38,3,26,1603933,16,1310721,2,5,26, +2,1603921,14,98,101,108,111,110,103,115,32,116,111,32,97,32, +99,111,110,116,97,105,110,101,114,59,32,114,101,99,97,115, +116,32,116,104,101,32,99,111,110,116,97,105,110,101,114,32, +105,110,115,116,101,97,100,0,1603815,1582383,12,8,1604511,14,1310721,2, +7,26,2,13,28,1,6,1,10,28,34,63,38,3,26,1604061, +16,1310721,2,5,26,2,1604049,14,105,115,32,98,111,114,114,111, +119,101,100,32,97,110,100,32,99,97,110,39,116,32,98,101, +32,114,101,99,97,115,116,0,1603985,1582383,12,8,1604511,14,1310721,2, +7,26,2,1,28,1,6,1,10,28,34,63,38,3,26,1604169, +16,1310721,2,5,26,2,1604157,14,105,115,110,39,116,32,97,110, +32,111,119,110,101,100,32,111,98,106,101,99,116,0,1604113,1582383, +12,8,1604511,14,1310721,2,7,26,2,9,28,1,6,1,10,28, +34,63,38,3,26,1604291,16,1310721,2,5,26,2,1604279,14,117,115, +101,100,32,97,102,116,101,114,32,105,116,32,119,97,115,32, +103,105,118,101,110,32,97,119,97,121,0,1604221,1582383,12,8,1604511, +14,1310721,2,3,26,2,3,28,1,6,1,10,28,34,63,38, +3,26,1604391,16,1310721,2,7,26,2,3,28,1,6,1,10,28, +34,63,38,1,10,28,1,6,1,10,28,34,63,38,1,10, +28,1604393,14,1,1604475,16,1310721,2,5,26,2,1604463,14,99,97,110, +39,116,32,98,101,32,114,101,99,97,115,116,32,98,97,99, +107,32,116,111,32,109,0,1604411,1582383,12,8,1604511,14,1469953,1310721,2, +5,26,2,26,1310721,2,3,26,2,10,3,6,10,4,8,1310721, +2,2,1604523,14,1,1310721,2,9,26,1310721,4,18,1310721,2,9,28, +1310721,4,1310721,2,3,26,4,1310721,2,4,1581763,2,1,6,1,10, +28,34,63,38,3,26,1604599,16,1,1606033,14,1310721,2,2,3,28, +1,6,1,10,28,34,63,38,3,26,1604843,16,1310721,2,3,26, +2,5,28,1,6,1,10,28,34,63,38,1,10,28,1604837,16, +1604829,14,97,32,110,101,119,32,111,98,106,101,99,116,32,99, +97,110,32,111,110,108,121,32,103,111,32,115,116,114,97,105, +103,104,116,32,116,111,32,97,32,100,32,112,97,114,97,109, +101,116,101,114,59,32,98,105,110,100,32,105,116,32,116,111, +32,97,32,110,97,109,101,32,102,105,114,115,116,0,1604677,1582287, +12,8,1,1606033,14,1310721,2,2,5,40,1604863,16,3,1604919,14,1310721, +2,2,5,28,1597849,12,1,6,1,10,28,34,63,38,3,26, +1,6,1,10,28,34,63,38,1,10,28,1604929,16,1,1606033,14, +1310721,2,5,26,1310721,2,2,5,28,10,3,6,10,4,8,1310721, +2,7,26,1469953,1310721,2,5,26,2,26,2,10,3,6,10,4, +8,1469953,1310721,2,5,26,2,1597733,12,26,2,9,28,1,6,1, +10,28,34,63,38,3,26,1605129,16,1310721,2,5,26,2,1605115,14, +117,115,101,100,32,97,102,116,101,114,32,105,116,32,119,97, +115,32,103,105,118,101,110,32,97,119,97,121,0,1605057,1582383,12, +8,1,1606033,14,1310721,2,3,26,2,3,10,40,3,36,1605357,16, +1310721,2,7,26,2,7,28,1,6,1,10,28,34,63,38,3, +26,1605335,16,1310721,2,5,26,2,1605321,14,105,115,32,100,59,32, +112,97,115,115,32,105,116,32,116,111,32,97,32,100,32,112, +97,114,97,109,101,116,101,114,32,40,108,101,110,100,105,110, +103,32,105,116,32,119,111,117,108,100,32,108,111,115,101,32, +105,116,41,0,1605205,1582383,12,8,1,1606033,14,1310721,2,5,26,2, +1597925,12,8,1,1606033,14,1310721,2,7,26,2,7,28,1,6,1, +10,28,34,63,38,3,26,1605457,16,1469953,1310721,2,5,26,2,26, +9,10,3,6,10,4,8,1472001,1310721,2,5,26,2,26,3,10, +3,6,10,4,8,1,1606033,14,1310721,2,7,26,2,3,28,1, +6,1,10,28,34,63,38,3,26,1605585,16,1310721,2,5,26,2, +1605573,14,105,115,32,109,59,32,119,114,105,116,101,32,100,42, +120,32,116,111,32,103,105,118,101,32,105,116,32,97,119,97, +121,0,1605509,1582383,12,8,1606025,14,1310721,2,7,26,2,5,28,1, +6,1,10,28,34,63,38,3,26,1605761,16,1310721,2,5,26,2, +1605749,14,105,115,32,117,59,32,119,114,105,116,101,32,100,42, +120,32,116,111,32,103,105,118,101,32,105,116,32,97,119,97, +121,32,105,110,115,116,101,97,100,32,111,102,32,114,101,116, +117,114,110,105,110,103,32,105,116,0,1605637,1582383,12,8,1606025,14, +1310721,2,7,26,2,11,28,1,6,1,10,28,34,63,38,3, +26,1605931,16,1310721,2,5,26,2,1605919,14,98,101,108,111,110,103, +115,32,116,111,32,97,32,99,111,110,116,97,105,110,101,114, +32,97,110,100,32,99,97,110,39,116,32,98,101,32,103,105, +118,101,110,32,97,119,97,121,32,97,108,111,110,101,0,1605813, +1582383,12,8,1606025,14,1310721,2,5,26,2,1606017,14,105,115,32,98, +111,114,114,111,119,101,100,32,97,110,100,32,99,97,110,39, +116,32,98,101,32,103,105,118,101,110,32,97,119,97,121,0, +1605945,1582383,12,8,1,1606033,14,1,1310721,2,9,26,1310721,4,18,1310721, +2,3,28,1310721,4,1310721,2,4,1467905,1310721,2,2,26,2,1585585,12, +8,3,1584907,12,8,1581863,2,1585453,12,8,1,1606109,14,1,1310721,2, +3,26,1310721,4,18,1310721,2,15,28,1310721,4,1310721,2,5,26,4, +1310721,2,3,26,4,1310721,2,4,1581763,2,1,6,1,10,28,34, +63,38,3,26,1606195,16,1,1609571,14,1310721,2,9,26,3,1,10, +28,10,3,6,10,4,8,1310721,2,5,26,2,5,40,3,36, +1606289,16,1310721,2,5,26,2,5,28,1597849,12,1,6,1,10,28, +34,63,38,1,10,28,1606291,14,1,1606329,16,1310721,2,9,26,1310721, +2,5,26,2,5,28,10,3,6,10,4,8,1310721,2,9,26, +2,1,40,3,36,1606421,16,1469953,1310721,2,9,26,2,1597733,12,26, +2,9,28,1,6,1,10,28,34,63,38,3,26,1,6,1, +10,28,34,63,38,1,10,28,1606423,14,1,1606513,16,1310721,2,9, +26,2,1606499,14,117,115,101,100,32,97,102,116,101,114,32,105, +116,32,119,97,115,32,103,105,118,101,110,32,97,119,97,121, +0,1606441,1582383,12,8,1,1609571,14,1310721,2,2,3,28,1,6,1, +10,28,34,63,38,3,26,1607969,16,1310721,2,7,26,1469953,1310721,2, +3,26,2,26,2,10,3,6,10,4,8,1310721,2,9,26,2, +1,40,1606663,16,1310721,2,5,26,2,3,28,1,6,1,10,28, +34,63,38,1,10,28,1,6,1,10,28,34,63,38,1,10, +28,1606665,14,1,1606869,16,1310721,2,7,26,2,1600969,12,1,6,1, +10,28,34,63,38,3,26,1606835,16,1310721,2,3,26,2,1606823,14, +115,116,105,108,108,32,111,119,110,115,32,97,110,32,111,98, +106,101,99,116,59,32,111,118,101,114,119,114,105,116,105,110, +103,32,105,116,32,119,111,117,108,100,32,108,101,97,107,32, +105,116,0,1606721,1582383,12,8,1606863,14,1469953,1310721,2,3,26,2,26, +1,10,3,6,10,4,8,1,1609571,14,1310721,2,7,26,2,1600969, +12,1,6,1,10,28,34,63,38,3,26,1607037,16,1310721,2,3, +26,2,1607023,14,115,116,105,108,108,32,111,119,110,115,32,97, +110,32,111,98,106,101,99,116,59,32,111,118,101,114,119,114, +105,116,105,110,103,32,105,116,32,119,111,117,108,100,32,108, +101,97,107,32,105,116,0,1606921,1582383,12,8,1,1609571,14,1310721,2, +5,26,2,3,28,1,6,1,10,28,34,63,38,3,26,1607227, +16,1469953,1310721,2,3,26,2,26,3,10,3,6,10,4,8,1472001, +1310721,2,3,26,2,26,1,10,3,6,10,4,8,1474049,1310721,2, +3,26,2,26,3,10,3,6,10,4,8,1558017,1310721,2,3,26, +2,26,2,1607221,16,1469953,1310721,2,3,26,2,26,1558017,1310721,2,3, +26,2,26,2,10,3,6,10,4,8,1,1609571,14,1310721,2,13, +26,1469953,1310721,2,9,26,2,26,2,10,3,6,10,4,8,1310721, +2,13,26,2,5,28,1,6,1,10,28,34,63,38,3,26, +1607385,16,1468929,1310721,2,3,26,2,26,2,1468929,1310721,2,9,26,2, +26,2,3,28,28,1,6,1,10,28,34,63,38,3,26,1, +6,1,10,28,34,63,38,1,10,28,1607387,14,1,1607599,16,1469953, +1310721,2,3,26,2,26,3,10,3,6,10,4,8,1472001,1310721,2, +3,26,2,26,1,10,3,6,10,4,8,1474049,1310721,2,3,26, +2,26,3,10,3,6,10,4,8,1469953,1310721,2,9,26,2,26, +9,10,3,6,10,4,8,1472001,1310721,2,9,26,2,26,3,10, +3,6,10,4,8,1558017,1310721,2,3,26,2,26,2,1607593,16,1469953, +1310721,2,3,26,2,26,1558017,1310721,2,3,26,2,26,2,10,3, +6,10,4,8,1,1609571,14,1310721,2,13,26,2,5,28,1,6, +1,10,28,34,63,38,3,26,1607819,16,1310721,2,9,26,2,1607807, +14,105,115,32,117,59,32,105,116,32,99,97,110,32,111,110, +108,121,32,98,101,32,104,97,110,100,101,100,32,116,111,32, +97,32,118,97,114,105,97,98,108,101,32,111,102,32,116,104, +101,32,101,110,99,108,111,115,105,110,103,32,98,108,111,99, +107,44,32,111,114,32,114,101,116,117,114,110,101,100,0,1607651, +1582383,12,8,1607963,14,1310721,2,9,26,2,1607955,14,105,115,32,97, +110,32,111,119,110,101,100,32,111,98,106,101,99,116,32,97, +110,100,32,99,97,110,39,116,32,98,101,32,99,111,112,105, +101,100,32,105,110,116,111,32,97,110,111,116,104,101,114,32, +118,97,114,105,97,98,108,101,0,1607833,1582383,12,8,1,1609571,14, +1310721,2,2,7,28,1,6,1,10,28,34,63,38,3,26,1609239, +16,1310721,2,11,26,1310721,2,3,26,2,1597733,12,10,3,6,10, +4,8,1310721,2,7,26,1469953,1310721,2,11,26,2,26,2,10,3, +6,10,4,8,1310721,2,3,26,2,1597925,12,8,1310721,2,5,26, +2,3,28,1,6,1,10,28,34,63,38,3,26,1608149,16,1310721, +2,3,26,2,1606047,12,8,1,1609571,14,1310721,2,9,26,2,1, +40,1608173,16,1,1609571,14,1310721,2,13,26,1469953,1310721,2,9,26,2, +26,2,10,3,6,10,4,8,1310721,2,13,26,2,11,28,1, +6,1,10,28,34,63,38,3,26,1608335,16,1310721,2,9,26,2, +1608323,14,97,108,114,101,97,100,121,32,98,101,108,111,110,103, +115,32,116,111,32,97,32,99,111,110,116,97,105,110,101,114, +0,1608261,1582383,12,8,1609233,14,1310721,2,13,26,2,13,28,1,6, +1,10,28,34,63,38,3,26,1608463,16,1310721,2,9,26,2,1608451, +14,105,115,32,98,111,114,114,111,119,101,100,32,97,110,100, +32,99,97,110,39,116,32,98,101,32,115,116,111,114,101,100, +0,1608387,1582383,12,8,1609233,14,1310721,2,7,26,2,13,28,1,6, +1,10,28,34,63,38,3,26,1608735,16,1310721,2,13,26,2,7, +28,1,6,1,10,28,34,63,38,1,10,28,1608659,16,1310721,2, +9,26,2,1608647,14,109,117,115,116,32,98,101,32,100,32,116, +111,32,98,101,32,115,116,111,114,101,100,32,105,110,116,111, +32,97,32,98,111,114,114,111,119,101,100,32,111,98,106,101, +99,116,0,1608555,1582383,12,8,1608731,14,1469953,1310721,2,9,26,2,26, +9,10,3,6,10,4,8,1472001,1310721,2,9,26,2,26,3,10, +3,6,10,4,8,1310721,2,3,26,2,1606047,12,8,1609233,14,1310721, +2,13,26,2,1310721,2,7,26,2,28,1,6,1,10,28,34, +63,38,1,10,28,1608941,16,1310721,2,9,26,2,1608929,14,105,116, +115,32,116,97,103,32,100,111,101,115,110,39,116,32,109,97, +116,99,104,32,105,116,115,32,99,111,110,116,97,105,110,101, +114,39,115,32,40,98,111,116,104,32,104,97,118,101,32,116, +111,32,98,101,32,109,44,32,117,32,111,114,32,100,41,0, +1608797,1582383,12,8,1609233,14,1468929,1310721,2,9,26,2,26,2,1468929,1310721, +2,11,26,2,26,2,40,1609125,16,1310721,2,9,26,2,1609113,14, +108,105,118,101,115,32,105,110,32,97,110,32,111,117,116,101, +114,32,98,108,111,99,107,32,116,104,97,110,32,116,104,101, +32,99,111,110,116,97,105,110,101,114,32,105,116,39,115,32, +115,116,111,114,101,100,32,105,110,116,111,0,1608993,1582383,12,8, +1609233,14,1469953,1310721,2,9,26,2,26,11,10,3,6,10,4,8, +1470977,1310721,2,9,26,2,26,1310721,2,3,26,2,10,3,6,10, +4,8,1472001,1310721,2,9,26,2,26,3,10,3,6,10,4,8, +1310721,2,3,26,2,1606047,12,8,1,1609571,14,1310721,2,9,26,2, +1,40,3,36,1609563,16,1469953,1310721,2,9,26,2,26,2,7,28, +1,6,1,10,28,34,63,38,3,26,1609365,16,1469953,1310721,2,9, +26,2,26,9,10,3,6,10,4,8,1472001,1310721,2,9,26,2, +26,3,10,3,6,10,4,8,1609563,14,1310721,2,9,26,2,1609555, +14,109,117,115,116,32,98,101,32,100,32,116,111,32,98,101, +32,115,116,111,114,101,100,32,105,110,32,97,32,103,108,111, +98,97,108,32,111,114,32,105,110,32,117,110,116,114,97,99, +107,101,100,32,109,101,109,111,114,121,32,40,116,104,101,32, +99,111,108,108,101,99,116,111,114,32,116,97,107,101,115,32, +105,116,32,111,118,101,114,41,0,1609379,1582383,12,8,1,1609571,14, +1,1310721,2,15,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2, +4,1581763,2,1,6,1,10,28,34,63,38,3,26,1609637,16,1, +1610531,14,1310721,2,3,26,3,1,10,28,10,3,6,10,4,8, +1310721,2,2,5,40,3,36,1609723,16,1310721,2,2,5,28,1597849,12, +1,6,1,10,28,34,63,38,1,10,28,1609725,14,1,1609759,16, +1310721,2,3,26,1310721,2,2,5,28,10,3,6,10,4,8,1310721, +2,3,26,2,1,40,3,36,1609851,16,1469953,1310721,2,3,26,2, +1597733,12,26,2,9,28,1,6,1,10,28,34,63,38,3,26, +1,6,1,10,28,34,63,38,1,10,28,1609853,14,1,1609943,16, +1310721,2,3,26,2,1609929,14,117,115,101,100,32,97,102,116,101, +114,32,105,116,32,119,97,115,32,103,105,118,101,110,32,97, +119,97,121,0,1609871,1582383,12,8,1,1610531,14,1581765,2,1610199,16,1310721, +2,3,26,2,1,40,3,36,1610193,16,1469953,1310721,2,3,26,2, +26,2,5,28,1,6,1,10,28,34,63,38,3,26,1610077,16, +1469953,1310721,2,3,26,2,26,9,10,3,6,10,4,8,1472001,1310721, +2,3,26,2,26,3,10,3,6,10,4,8,1610193,14,1310721,2, +3,26,2,1610185,14,104,97,115,32,116,111,32,98,101,32,117, +32,116,111,32,98,101,32,114,101,116,117,114,110,101,100,59, +32,119,114,105,116,101,32,39,114,101,116,117,114,110,32,117, +42,120,39,0,1610091,1582383,12,8,1,1610531,14,1310721,2,2,3,28, +1,6,1,10,28,34,63,38,3,26,1610357,16,1610345,14,114,101, +116,117,114,110,115,32,97,32,110,101,119,32,111,98,106,101, +99,116,59,32,100,101,99,108,97,114,101,32,116,104,101,32, +102,117,110,99,116,105,111,110,32,119,105,116,104,32,45,62, +32,46,117,0,1610237,1582287,12,8,1610523,14,1310721,2,3,26,2,1, +40,3,36,1610523,16,1310721,2,3,26,2,1610515,14,105,115,32,111, +119,110,101,100,32,97,110,100,32,99,97,110,39,116,32,98, +101,32,114,101,116,117,114,110,101,100,32,102,114,111,109,32, +97,32,102,117,110,99,116,105,111,110,32,119,105,116,104,111, +117,116,32,45,62,32,46,117,0,1610393,1582383,12,8,1,1610531,14, +1,1310721,2,5,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2, +4,1581763,2,1,6,1,10,28,34,63,38,3,26,1610597,16,1, +1611225,14,1310721,2,2,3,28,1,6,1,10,28,34,63,38,3, +26,1610637,16,3,1611225,14,1310721,2,2,5,40,1610657,16,3,1610713,14, +1310721,2,2,5,28,1597849,12,1,6,1,10,28,34,63,38,3, +26,1,6,1,10,28,34,63,38,1,10,28,1610723,16,1,1611225, +14,1310721,2,3,26,1310721,2,2,5,28,10,3,6,10,4,8, +1469953,1310721,2,3,26,2,26,2,5,28,1,6,1,10,28,34, +63,38,3,26,1610865,16,1468929,1310721,2,3,26,2,26,2,1581759,2, +28,1,6,1,10,28,34,63,38,3,26,1,6,1,10,28, +34,63,38,1,10,28,1610867,14,1,1610933,16,1469953,1310721,2,3,26, +2,26,9,10,3,6,10,4,8,1472001,1310721,2,3,26,2,26, +3,10,3,6,10,4,8,3,1611225,14,1469953,1310721,2,3,26,2, +26,2,5,28,1,6,1,10,28,34,63,38,3,26,1611097,16, +1310721,2,3,26,2,1611085,14,111,110,108,121,32,97,32,117,32, +111,98,106,101,99,116,32,111,102,32,116,104,105,115,32,98, +108,111,99,107,32,99,97,110,32,98,101,32,105,116,115,32, +118,97,108,117,101,0,1610991,1582383,12,8,1611217,14,1310721,2,3,26, +2,1611209,14,104,97,115,32,116,111,32,98,101,32,117,32,116, +111,32,98,101,32,104,97,110,100,101,100,32,117,112,32,97, +115,32,116,104,101,32,98,108,111,99,107,39,115,32,118,97, +108,117,101,0,1611111,1582383,12,8,1,1611225,14,1,1310721,2,5,26, +1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,4,1310721,2,2,3, +28,1,6,1,10,28,34,63,38,3,26,1611407,16,1611395,14,97, +32,110,101,119,32,111,98,106,101,99,116,32,40,111,114,32, +97,32,117,32,114,101,115,117,108,116,41,32,99,97,110,39, +116,32,98,101,32,116,104,114,111,119,110,32,97,119,97,121, +0,1611295,1582287,12,8,1611441,14,1310721,2,2,5,40,3,36,1611441,16, +1310721,2,2,5,28,1597925,12,8,1,1611449,14,1,1310721,2,3,26, +1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,4,1581873,2,1310721,2, +3,26,4,1581873,2,1310721,2,2,26,1310721,40,3,36,1611575,16,1611567, +14,116,104,101,32,116,121,112,101,32,97,114,101,97,32,105, +115,32,102,117,108,108,0,1611523,1581991,12,8,1581873,1581873,2,1310721,2, +2,26,10,3,6,10,4,8,1310721,2,3,26,2,1,1310721,2, +2,1574329,12,8,1310721,2,3,26,2,1611641,14,1,1310721,2,5,26, +1310721,4,18,1310721,2,11,28,1310721,4,1310721,2,7,26,4,1310721,2, +5,26,4,1310721,2,3,26,4,1310721,2,4,15,1310721,2,7,26, +2,26,1611463,12,1310721,2,9,26,4,1310721,2,9,26,2,1,26, +1310721,2,2,10,3,6,10,4,8,1310721,2,9,26,2,3,26, +1310721,2,3,26,2,10,3,6,10,4,8,1310721,2,9,26,2, +5,26,1310721,2,5,26,2,10,3,6,10,4,8,1581875,2,2049, +40,3,36,1611893,16,1611885,14,116,111,111,32,109,97,110,121,32, +116,121,112,101,115,0,1611855,1581991,12,8,1277953,1581875,2,26,1310721,2, +9,26,2,10,3,6,10,4,8,1581875,1,6,2,1,6,3, +26,5,6,4,10,8,8,1310721,2,9,26,2,1611967,14,1,1310721, +2,11,26,1310721,4,18,1310721,2,7,28,1310721,4,1310721,2,4,1, +1310721,2,3,26,4,1310721,2,3,26,2,1581875,2,40,1612239,16,1310721, +2,5,26,1277953,1310721,2,3,26,2,26,2,10,3,6,10,4, +8,1310721,2,5,26,2,1,26,2,11,28,1,6,1,10,28, +34,63,38,3,26,1612181,16,1310721,2,5,26,2,9,26,2,1310721, +2,2,28,1,6,1,10,28,34,63,38,3,26,1,6,1, +10,28,34,63,38,1,10,28,1612183,14,1,1612201,16,1310721,2,5, +26,2,1612319,14,1310721,2,3,26,1,6,2,1,6,3,26,5, +6,4,10,8,8,1612011,14,1310721,2,5,26,11,3,1,1,1611655, +12,10,3,6,10,4,8,1310721,2,5,26,2,9,26,1310721,2, +2,10,3,6,10,4,8,1310721,2,5,26,2,1612319,14,1,1310721, +2,7,26,1310721,4,18,1310721,2,9,28,1310721,4,1310721,2,3,26, +4,1310721,2,4,1,1310721,2,5,26,4,1310721,2,5,26,2,1581875, +2,40,1612681,16,1310721,2,7,26,1277953,1310721,2,5,26,2,26,2, +10,3,6,10,4,8,1310721,2,7,26,2,1,26,2,13,28, +1,6,1,10,28,34,63,38,3,26,1612543,16,1310721,2,7,26, +2,9,26,2,1310721,2,2,28,1,6,1,10,28,34,63,38, +3,26,1,6,1,10,28,34,63,38,1,10,28,1612545,14,1, +1612623,16,1310721,2,7,26,2,7,26,2,1310721,2,3,26,2,28, +1,6,1,10,28,34,63,38,3,26,1,6,1,10,28,34, +63,38,1,10,28,1612625,14,1,1612643,16,1310721,2,7,26,2,1612817, +14,1310721,2,5,26,1,6,2,1,6,3,26,5,6,4,10, +8,8,1612373,14,1310721,2,7,26,13,1310721,2,3,26,2,1310721,2, +2,1615957,12,30,1,1,1611655,12,10,3,6,10,4,8,1310721,2, +7,26,2,9,26,1310721,2,2,10,3,6,10,4,8,1310721,2, +7,26,2,7,26,1310721,2,3,26,2,10,3,6,10,4,8, +1310721,2,7,26,2,1612817,14,1,1310721,2,9,26,1310721,4,18,1310721, +2,15,28,1310721,4,1310721,2,7,26,4,1310721,2,5,26,4,1310721, +2,3,26,4,1310721,2,4,1,1310721,2,9,26,4,1310721,2,9, +26,2,1581875,2,40,1613517,16,1310721,2,13,26,1277953,1310721,2,9,26, +2,26,2,10,3,6,10,4,8,1310721,2,13,26,2,1,26, +2,15,28,1,6,1,10,28,34,63,38,3,26,1613061,16,1310721, +2,13,26,2,9,26,2,1310721,2,2,28,1,6,1,10,28, +34,63,38,3,26,1,6,1,10,28,34,63,38,1,10,28, +1613063,14,1,1613141,16,1310721,2,13,26,2,7,26,2,1310721,2,3, +26,2,28,1,6,1,10,28,34,63,38,3,26,1,6,1, +10,28,34,63,38,1,10,28,1613143,14,1,1613221,16,1310721,2,13, +26,2,11,26,2,1310721,2,7,26,2,28,1,6,1,10,28, +34,63,38,3,26,1,6,1,10,28,34,63,38,1,10,28, +1613223,14,1,1613479,16,1310721,2,11,26,1,10,3,6,10,4,8, +1310721,2,11,26,2,1310721,2,3,26,2,40,1613375,16,1310721,2,13, +26,2,15,1310721,2,11,26,2,26,26,2,1310721,2,5,26,2, +1310721,2,11,26,2,26,2,28,1,6,1,10,28,34,63,38, +3,26,1,6,1,10,28,34,63,38,1,10,28,1613377,14,1, +1613419,16,1310721,2,11,26,1,6,2,1,6,3,26,5,6,4, +10,8,8,1613249,14,1310721,2,11,26,2,1310721,2,3,26,2,28, +1,6,1,10,28,34,63,38,3,26,1613479,16,1310721,2,13,26, +2,1613717,14,1310721,2,9,26,1,6,2,1,6,3,26,5,6, +4,10,8,8,1612891,14,1310721,2,13,26,15,3,1,1310721,2,3, +26,2,1611655,12,10,3,6,10,4,8,1310721,2,13,26,2,9, +26,1310721,2,2,10,3,6,10,4,8,1310721,2,13,26,2,7, +26,1310721,2,3,26,2,10,3,6,10,4,8,1310721,2,13,26, +2,11,26,1310721,2,7,26,2,10,3,6,10,4,8,1310721,2, +13,26,2,15,26,1310721,2,5,26,2,1310721,2,3,26,2,1574139, +12,8,1310721,2,13,26,2,1613717,14,1,1310721,2,15,26,1310721,4, +18,1310721,2,9,28,1310721,4,1310721,2,3,26,4,1310721,2,4,1, +1310721,2,5,26,4,1310721,2,5,26,2,1581875,2,40,1613999,16,1310721, +2,7,26,1277953,1310721,2,5,26,2,26,2,10,3,6,10,4, +8,1310721,2,7,26,2,1,26,2,19,28,1,6,1,10,28, +34,63,38,3,26,1613941,16,1310721,2,7,26,2,7,26,2,1310721, +2,2,28,1,6,1,10,28,34,63,38,3,26,1,6,1, +10,28,34,63,38,1,10,28,1613943,14,1,1613961,16,1310721,2,7, +26,2,1614087,14,1310721,2,5,26,1,6,2,1,6,3,26,5, +6,4,10,8,8,1613771,14,1310721,2,7,26,19,3,1310721,2,3, +26,2,1,1611655,12,10,3,6,10,4,8,1310721,2,7,26,2, +7,26,1310721,2,2,10,3,6,10,4,8,1310721,2,7,26,2, +1614087,14,1,1310721,2,9,26,1310721,4,18,1310721,2,15,28,1310721,4, +1310721,2,5,26,4,1310721,2,3,26,4,1310721,2,4,1,1310721,2, +7,26,4,1310721,2,7,26,2,1581875,2,40,1614653,16,1310721,2,11, +26,1277953,1310721,2,7,26,2,26,2,10,3,6,10,4,8,1310721, +2,11,26,2,1,26,2,17,28,1,6,1,10,28,34,63, +38,3,26,1614321,16,1310721,2,11,26,2,11,26,2,1310721,2,2, +28,1,6,1,10,28,34,63,38,3,26,1,6,1,10,28, +34,63,38,1,10,28,1614323,14,1,1614615,16,1310721,2,13,26,1310721, +2,11,26,2,13,26,2,10,3,6,10,4,8,1310721,2,9, +26,1,10,3,6,10,4,8,1310721,2,9,26,2,1310721,2,5, +26,2,40,1614511,16,1310721,2,13,26,2,3,1310721,2,9,26,2, +26,26,2,1310721,2,3,26,2,1310721,2,9,26,2,26,2,28, +1,6,1,10,28,34,63,38,3,26,1,6,1,10,28,34, +63,38,1,10,28,1614513,14,1,1614555,16,1310721,2,9,26,1,6, +2,1,6,3,26,5,6,4,10,8,8,1614385,14,1310721,2,9, +26,2,1310721,2,5,26,2,28,1,6,1,10,28,34,63,38, +3,26,1614615,16,1310721,2,11,26,2,1614927,14,1310721,2,7,26,1, +6,2,1,6,3,26,5,6,4,10,8,8,1614151,14,1310721,2, +13,26,1310721,2,5,26,2,3,26,1611463,12,10,3,6,10,4, +8,1310721,2,13,26,2,1,26,1310721,2,5,26,2,10,3,6, +10,4,8,1310721,2,13,26,2,3,26,1310721,2,3,26,2,1310721, +2,5,26,2,1574139,12,8,1310721,2,11,26,17,1,1310721,2,2, +5,26,2,1,1611655,12,10,3,6,10,4,8,1310721,2,11,26, +2,7,26,3,1,10,28,10,3,6,10,4,8,1310721,2,11, +26,2,11,26,1310721,2,2,10,3,6,10,4,8,1310721,2,11, +26,2,13,26,1310721,2,13,26,2,10,3,6,10,4,8,1310721, +2,11,26,2,1614927,14,1,1310721,2,15,26,1310721,4,18,1310721,2, +33,28,1310721,4,1310721,2,4,1,1310721,2,13,26,4,1310721,2,2, +1,26,2,17,28,1,6,1,10,28,34,63,38,1,10,28, +1615019,16,3,1615061,14,1310721,2,2,7,26,2,1,40,3,36,1, +6,1,10,28,34,63,38,1,10,28,1615075,16,1310721,2,2,1615943, +14,1310721,2,2,7,26,2,5,1,10,28,28,1,6,1,10, +28,34,63,38,3,26,1615211,16,1615203,14,97,32,115,116,114,117, +99,116,32,99,97,110,39,116,32,99,111,110,116,97,105,110, +32,105,116,115,101,108,102,32,98,121,32,118,97,108,117,101, +0,1615125,1581991,12,8,1310721,2,2,7,26,5,1,10,28,10,3, +6,10,4,8,1310721,2,3,26,1310721,2,2,11,26,2,10,3, +6,10,4,8,1310721,2,7,26,1310721,2,3,26,2,11,26,2, +10,3,6,10,4,8,1310721,2,5,26,1310721,2,3,26,2,9, +26,2,10,3,6,10,4,8,1310721,2,17,26,1,17,1574329,12, +8,1310721,2,17,26,1310721,2,2,13,26,2,3,26,1310721,2,2, +13,26,2,1,26,2,1574139,12,8,1310721,2,11,26,1310721,2,7, +26,2,7,30,3,26,1611463,12,10,3,6,10,4,8,1310721,2, +9,26,1,10,3,6,10,4,8,1310721,2,9,26,2,1310721,2, +7,26,2,40,1615835,16,1310721,2,15,26,1310721,2,5,26,2,1310721, +2,9,26,2,1310721,2,9,26,2,26,3,26,26,2,1310721,2, +17,26,1616107,12,10,3,6,10,4,8,1310721,2,11,26,2,1310721, +2,9,26,2,7,30,26,1310721,2,5,26,2,1310721,2,9,26, +2,1310721,2,9,26,2,26,26,2,10,3,6,10,4,8,1310721, +2,11,26,2,1310721,2,9,26,2,7,30,3,26,26,1310721,2, +15,26,2,10,3,6,10,4,8,1310721,2,11,26,2,1310721,2, +9,26,2,7,30,5,26,26,1310721,2,13,26,2,10,3,6, +10,4,8,1310721,2,13,26,1310721,2,13,26,2,1310721,2,15,26, +2,1615957,12,26,10,3,6,10,4,8,1310721,2,9,26,1,6, +2,1,6,3,26,5,6,4,10,8,8,1615475,14,1310721,2,2, +9,26,1310721,2,11,26,2,10,3,6,10,4,8,1310721,2,2, +3,26,1310721,2,13,26,2,10,3,6,10,4,8,1310721,2,2, +7,26,1310721,2,7,26,2,10,3,6,10,4,8,1310721,2,2, +1615943,14,1,1310721,2,33,26,1310721,4,18,1310721,2,3,28,1310721,4, +1310721,2,4,1310721,2,2,1,26,2,17,28,1,6,1,10,28, +34,63,38,3,26,1616057,16,1310721,2,2,7,26,2,1,40,1, +6,1,10,28,34,63,38,1,10,28,1616059,14,1,1616075,16,1310721, +2,2,1614941,12,8,1310721,2,2,3,26,2,1616093,14,1,1310721,2, +3,26,1310721,4,18,1310721,2,61,28,1310721,4,1310721,2,3,26,4, +1310721,2,4,1310721,2,2,1,26,2,1310721,2,5,26,4,1310721,2, +5,26,2,19,28,1,6,1,10,28,34,63,38,3,26,1616263, +16,1310721,2,3,26,2,1310721,2,2,7,26,2,26,2,1616255,16, +1310721,2,3,26,2,1310721,2,2,7,26,2,26,2,1617069,14,1581877, +2,1617069,14,1310721,2,5,26,2,11,28,1,6,1,10,28,34, +63,38,3,26,1616335,16,1310721,2,2,9,26,2,1310721,2,3,26, +2,1616107,12,1611981,12,1617069,14,1310721,2,5,26,2,13,28,1,6, +1,10,28,34,63,38,3,26,1616419,16,1310721,2,2,9,26,2, +1310721,2,3,26,2,1616107,12,1310721,2,2,7,26,2,1612333,12,1617069, +14,1310721,2,5,26,2,15,28,1,6,1,10,28,34,63,38, +3,26,1616709,16,1310721,2,9,26,1310721,2,2,7,26,2,10,3, +6,10,4,8,1310721,2,7,26,1,10,3,6,10,4,8,1310721, +2,7,26,2,1310721,2,9,26,2,40,1616645,16,1310721,2,11,26, +1310721,2,7,26,2,26,1310721,2,2,15,1310721,2,7,26,2,26, +26,2,1310721,2,3,26,2,1616107,12,10,3,6,10,4,8,1310721, +2,7,26,1,6,2,1,6,3,26,5,6,4,10,8,8, +1616511,14,1310721,2,2,9,26,2,1310721,2,3,26,2,1616107,12,1310721, +2,9,26,2,1310721,2,11,26,1310721,2,2,11,26,2,1612831,12, +1617069,14,1310721,2,5,26,2,17,28,1,6,1,10,28,34,63, +38,3,26,1616785,16,1310721,2,2,11,26,2,1,6,1,10,28, +34,63,38,1,10,28,1616787,14,1,1617057,16,1310721,2,59,26,1310721, +2,2,13,26,2,10,3,6,10,4,8,1310721,2,9,26,1310721, +2,59,26,2,1,26,2,10,3,6,10,4,8,1310721,2,7, +26,1,10,3,6,10,4,8,1310721,2,7,26,2,1310721,2,9, +26,2,40,1617019,16,1310721,2,11,26,1310721,2,7,26,2,26,1310721, +2,59,26,2,3,1310721,2,7,26,2,26,26,2,1310721,2,3, +26,2,1616107,12,10,3,6,10,4,8,1310721,2,7,26,1,6, +2,1,6,3,26,5,6,4,10,8,8,1616881,14,1310721,2,2, +11,26,2,1310721,2,11,26,1310721,2,9,26,2,1614101,12,1617069,14, +1310721,2,2,1617069,14,1,1310721,2,61,26,1310721,4,18,1310721,2,3, +28,1310721,4,1310721,2,4,1310721,2,2,1,26,2,3,28,1,6, +1,10,28,34,63,38,3,26,1617147,16,3,1617205,14,1310721,2,2, +1,26,2,5,28,1,6,1,10,28,34,63,38,3,26,1, +6,1,10,28,34,63,38,1,10,28,1617215,16,3,1617273,14,1310721, +2,2,1,26,2,7,28,1,6,1,10,28,34,63,38,3, +26,1,6,1,10,28,34,63,38,1,10,28,1617279,14,1,1310721, +2,3,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,4,1310721, +2,2,1,26,2,13,28,1,6,1,10,28,34,63,38,3, +26,1617357,16,3,1617415,14,1310721,2,2,1,26,2,17,28,1,6, +1,10,28,34,63,38,3,26,1,6,1,10,28,34,63,38, +1,10,28,1617421,14,1,1310721,2,3,26,1310721,4,18,1310721,2,3, +28,1310721,4,1310721,2,4,1310721,2,2,1,26,2,11,28,1,6, +1,10,28,34,63,38,3,26,1617499,16,3,1617557,14,1310721,2,2, +1,26,2,13,28,1,6,1,10,28,34,63,38,3,26,1, +6,1,10,28,34,63,38,1,10,28,1617563,14,1,1310721,2,3, +26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,4,1310721,2,2, +1,26,2,13,28,1,6,1,10,28,34,63,38,3,26,1617655, +16,1310721,2,2,9,26,2,1611981,12,1617667,14,1310721,2,2,1617667,14, +1,1310721,2,3,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2, +4,1310721,2,2,1,26,2,11,28,1,6,1,10,28,34,63, +38,3,26,1617745,16,3,1617803,14,1310721,2,2,1,26,2,13,28, +1,6,1,10,28,34,63,38,3,26,1,6,1,10,28,34, +63,38,1,10,28,1617823,16,1310721,2,2,9,26,2,1617833,14,1581877, +2,1617833,14,1,1310721,2,3,26,1310721,4,18,1310721,2,5,28,1310721, +4,1310721,2,3,26,4,1310721,2,4,1310721,2,2,1310721,2,3,26, +2,28,1,6,1,10,28,34,63,38,3,26,1617923,16,3,1617977, +14,1310721,2,2,1581877,2,28,1,6,1,10,28,34,63,38,3, +26,1,6,1,10,28,34,63,38,1,10,28,1617987,16,3,1618045, +14,1310721,2,3,26,2,1581877,2,28,1,6,1,10,28,34,63, +38,3,26,1,6,1,10,28,34,63,38,1,10,28,1618055,16, +3,1618781,14,1310721,2,2,1617083,12,1618109,16,1310721,2,3,26,2,1617083, +12,1,6,1,10,28,34,63,38,1,10,28,1618111,14,1,1618121, +16,3,1618781,14,1310721,2,3,26,1310721,2,3,26,2,1617577,12,10, +3,6,10,4,8,1310721,2,2,1,26,2,11,28,1,6,1, +10,28,34,63,38,3,26,1618261,16,1310721,2,3,26,2,1,26, +2,11,28,1,6,1,10,28,34,63,38,3,26,1,6,1, +10,28,34,63,38,1,10,28,1618263,14,1,1618587,16,1310721,2,2, +9,26,2,1581877,2,28,1,6,1,10,28,34,63,38,3,26, +1618315,16,3,1618379,14,1310721,2,3,26,2,9,26,2,1581877,2,28, +1,6,1,10,28,34,63,38,3,26,1,6,1,10,28,34, +63,38,1,10,28,1618389,16,3,1618449,14,1310721,2,2,9,26,2, +1581885,2,28,1,6,1,10,28,34,63,38,3,26,1,6,1, +10,28,34,63,38,1,10,28,1618459,16,3,1618523,14,1310721,2,3, +26,2,9,26,2,1581885,2,28,1,6,1,10,28,34,63,38, +3,26,1,6,1,10,28,34,63,38,1,10,28,1618533,16,3, +1618781,14,1310721,2,2,9,26,2,1310721,2,3,26,2,9,26,2, +28,1,6,1,10,28,34,63,38,3,26,1618781,14,1310721,2,2, +1,26,2,11,28,1,6,1,10,28,34,63,38,3,26,1618633, +16,3,1618691,14,1310721,2,2,1,26,2,15,28,1,6,1,10, +28,34,63,38,3,26,1,6,1,10,28,34,63,38,1,10, +28,1618725,16,1581891,2,1,6,1,10,28,34,63,38,1,10,28, +1618727,14,1,1618737,16,3,1618781,14,1310721,2,2,1310721,2,3,26,2, +28,1,6,1,10,28,34,63,38,3,26,1618781,14,1,1310721,2, +5,26,1310721,4,18,1310721,2,15,28,1310721,4,1310721,2,5,26,4, +1310721,2,3,26,4,1310721,2,4,1310721,2,2,1,26,2,19,28, +1,6,1,10,28,34,63,38,3,26,1619045,16,1310721,2,7,26, +1310721,2,2,7,26,2,10,3,6,10,4,8,1310721,2,5,26, +2,1310721,2,7,26,2,26,2,1,6,1,10,28,34,63,38, +3,26,1619003,16,1310721,2,5,26,2,1310721,2,7,26,2,26,1310721, +2,3,26,2,10,3,6,10,4,8,3,1619849,14,1310721,2,5, +26,2,1310721,2,7,26,2,26,2,1310721,2,3,26,2,1617847,12, +1619849,14,1310721,2,3,26,1310721,2,3,26,2,1617577,12,10,3,6, +10,4,8,1310721,2,2,1,26,2,11,28,1,6,1,10,28, +34,63,38,3,26,1619185,16,1310721,2,3,26,2,1,26,2,11, +28,1,6,1,10,28,34,63,38,3,26,1,6,1,10,28, +34,63,38,1,10,28,1619187,14,1,1619289,16,1310721,2,3,26,2, +9,26,2,1581877,2,28,1,6,1,10,28,34,63,38,3,26, +1619243,16,3,1619849,14,1310721,2,2,9,26,2,1310721,2,3,26,2, +9,26,2,1310721,2,5,26,2,1618795,12,1619849,14,1310721,2,2,1, +26,2,17,28,1,6,1,10,28,34,63,38,3,26,1619367,16, +1310721,2,2,11,26,2,1,6,1,10,28,34,63,38,1,10, +28,1619369,14,1,1619439,16,1310721,2,3,26,2,1,26,2,17,28, +1,6,1,10,28,34,63,38,3,26,1,6,1,10,28,34, +63,38,1,10,28,1619441,14,1,1619521,16,1310721,2,3,26,2,11, +26,2,1310721,2,2,11,26,2,28,1,6,1,10,28,34,63, +38,3,26,1,6,1,10,28,34,63,38,1,10,28,1619523,14, +1,1619823,16,1310721,2,11,26,1310721,2,2,13,26,2,10,3,6, +10,4,8,1310721,2,13,26,1310721,2,3,26,2,13,26,2,10, +3,6,10,4,8,1310721,2,9,26,1310721,2,11,26,2,1,26, +2,10,3,6,10,4,8,1310721,2,7,26,1,10,3,6,10, +4,8,1310721,2,7,26,2,1310721,2,9,26,2,40,1619817,16,1310721, +2,11,26,2,3,1310721,2,7,26,2,26,26,2,1310721,2,13, +26,2,3,1310721,2,7,26,2,26,26,2,1310721,2,5,26,2, +1618795,12,1,6,1,10,28,34,63,38,3,26,1619779,16,1,1619849, +14,1310721,2,7,26,1,6,2,1,6,3,26,5,6,4,10, +8,8,1619653,14,3,1619849,14,1310721,2,2,1310721,2,3,26,2,1617847, +12,1619849,14,1,1310721,2,15,26,1310721,4,18,1310721,2,9,28,1310721, +4,1310721,2,4,1310721,2,2,1,26,2,1310721,2,3,26,4,1310721, +2,3,26,2,11,28,1,6,1,10,28,34,63,38,3,26, +1619969,16,85,1573079,12,8,1310721,2,2,9,26,2,1619863,12,1621071,14, +1310721,2,3,26,2,13,28,1,6,1,10,28,34,63,38,3, +26,1620061,16,183,1573079,12,8,1310721,2,2,7,26,2,1573371,12,8, +187,1573079,12,8,1310721,2,2,9,26,2,1619863,12,1621071,14,1310721,2, +3,26,2,15,28,1,6,1,10,28,34,63,38,3,26,1620721, +16,1620111,14,102,110,40,0,1620103,1573231,12,8,1310721,2,5,26,1, +10,3,6,10,4,8,1310721,2,5,26,2,1310721,2,2,7,26, +2,40,1620493,16,1310721,2,5,26,2,1620201,16,1620193,14,44,32,0, +1620187,1573231,12,8,1310721,2,2,15,1310721,2,5,26,2,26,26,2, +1619863,12,8,1310721,2,5,26,2,25,40,1620319,16,1310721,2,2,1310721, +2,5,26,2,1632761,12,3,28,1,6,1,10,28,34,63,38, +3,26,1,6,1,10,28,34,63,38,1,10,28,1620321,14,1, +1620343,16,1620335,14,46,109,0,1620329,1573231,12,8,1310721,2,5,26,2, +25,40,1620431,16,1310721,2,2,1310721,2,5,26,2,1632761,12,5,28, +1,6,1,10,28,34,63,38,3,26,1,6,1,10,28,34, +63,38,1,10,28,1620433,14,1,1620455,16,1620447,14,46,100,0,1620441, +1573231,12,8,1310721,2,5,26,1,6,2,1,6,3,26,5,6, +4,10,8,8,1620141,14,83,1573079,12,8,1310721,2,2,9,26,2, +1581877,2,28,1,6,1,10,28,34,63,38,1,10,28,1620551,16, +3,1620589,14,1310721,2,2,11,26,2,3,32,1,6,1,10,28, +34,63,38,1,10,28,1620715,16,1620607,14,32,45,62,32,0,1620597, +1573231,12,8,1310721,2,2,9,26,2,1581877,2,28,1,6,1,10, +28,34,63,38,1,10,28,1620677,16,1310721,2,2,9,26,2,1619863, +12,8,1310721,2,2,11,26,2,3,32,1620715,16,1620707,14,46,117, +0,1620701,1573231,12,8,1,1621071,14,1310721,2,2,5,26,2,1620759,16, +1310721,2,2,5,26,2,1573231,12,8,1620775,14,1620767,14,63,0,1620763, +1573231,12,8,1310721,2,3,26,2,17,28,1,6,1,10,28,34, +63,38,3,26,1620851,16,1310721,2,2,11,26,2,1,6,1,10, +28,34,63,38,1,10,28,1620853,14,1,1621063,16,1310721,2,7,26, +1310721,2,2,13,26,2,10,3,6,10,4,8,121,1573079,12,8, +1310721,2,5,26,1,10,3,6,10,4,8,1310721,2,5,26,2, +1310721,2,7,26,2,1,26,2,40,1621055,16,1310721,2,5,26,2, +1620983,16,1620975,14,44,32,0,1620969,1573231,12,8,1310721,2,7,26,2, +3,1310721,2,5,26,2,26,26,2,1619863,12,8,1310721,2,5,26, +1,6,2,1,6,3,26,5,6,4,10,8,8,1620919,14,125, +1573079,12,8,1,1621071,14,1,1310721,2,9,26,1310721,4,18,1310721,2, +7,28,1310721,4,1310721,2,5,26,4,1310721,2,3,26,4,1310721,2, +4,1581887,2,1,6,1,10,28,34,63,38,3,26,1621157,16,3, +1621203,14,1581767,2,1,6,1,10,28,34,63,38,3,26,1,6, +1,10,28,34,63,38,1,10,28,1621213,16,3,1621239,14,1581893,2, +1,6,1,10,28,34,63,38,1,10,28,1621249,16,1,1621461,14, +1621273,14,122,99,58,32,108,105,110,101,32,0,1621253,1573231,12,8, +1579523,2,1573371,12,8,1621325,14,58,32,116,121,112,101,32,101,114, +114,111,114,58,32,0,1621295,1573231,12,8,1310721,2,2,1573231,12,8, +1621373,14,58,32,101,120,112,101,99,116,101,100,32,0,1621349,1573231, +12,8,1310721,2,3,26,2,1619863,12,8,1621415,14,44,32,103,111, +116,32,0,1621401,1573231,12,8,1310721,2,5,26,2,1619863,12,8,21, +1573079,12,8,1573195,12,8,1,1621461,14,1,1310721,2,7,26,1310721,4, +18,1310721,2,7,28,1310721,4,1310721,2,5,26,4,1310721,2,3,26, +4,1310721,2,4,1310721,2,2,1621529,16,3,1621575,14,1581887,2,1,6, +1,10,28,34,63,38,3,26,1,6,1,10,28,34,63,38, +1,10,28,1621585,16,3,1621631,14,1581767,2,1,6,1,10,28,34, +63,38,3,26,1,6,1,10,28,34,63,38,1,10,28,1621641, +16,3,1621667,14,1581893,2,1,6,1,10,28,34,63,38,1,10, +28,1621677,16,1,1621841,14,1621701,14,122,99,58,32,108,105,110,101, +32,0,1621681,1573231,12,8,1579523,2,1573371,12,8,1621753,14,58,32,116, +121,112,101,32,101,114,114,111,114,58,32,0,1621723,1573231,12,8, +1310721,2,3,26,2,1573231,12,8,1621795,14,44,32,103,111,116,32, +0,1621781,1573231,12,8,1310721,2,5,26,2,1619863,12,8,21,1573079,12, +8,1573195,12,8,1,1621841,14,1,1310721,2,7,26,1310721,4,18,1310721, +2,5,28,1310721,4,1310721,2,4,1581911,2,1310721,2,3,26,4,1310721, +2,3,26,2,1622001,16,1310721,2,3,26,1,6,2,1,6,-1, +26,5,6,4,10,8,8,1581895,1310721,2,3,26,2,26,2,1310721, +2,2,28,1,6,1,10,28,34,63,38,3,26,1621997,16,1310721, +2,3,26,2,1622015,14,1621887,14,3,1,10,28,1622015,14,1,1310721, +2,5,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,4,1310721, +2,2,1621855,12,1,40,3,36,1622075,16,3,1622229,14,1310721,2,2, +1597111,12,1,40,3,36,1622103,16,1,1622229,14,1310721,2,3,26,1310721, +2,2,1583635,12,10,3,6,10,4,8,1310721,2,3,26,2,1, +40,3,36,1622221,16,1417729,1310721,2,3,26,2,26,2,15,28,1, +6,1,10,28,34,63,38,3,26,1,6,1,10,28,34,63, +38,1,10,28,1622223,14,1,1622229,14,1,1310721,2,5,26,1310721,4, +18,1310721,2,1,28,1310721,4,1581921,2,1592341,12,1622291,16,1579519,1581919,2, +10,3,6,10,4,8,1,1622309,14,1581919,2,1592809,12,8,1,1622309, +14,1,1310721,2,1,26,1310721,4,18,1310721,2,79,28,1310721,4,1, +1310721,2,7,26,4,1,1310721,2,75,26,4,1581821,2,1592601,12,1622383, +16,1622323,12,1611981,12,1624165,14,1581805,2,1592601,12,1622547,16,1310721,2,1649967, +12,10,3,6,10,4,8,1581807,2,1592809,12,8,1310721,2,5,26, +1622323,12,10,3,6,10,4,8,1310721,2,2,1,40,1622523,16,1622515, +14,97,32,110,101,103,97,116,105,118,101,32,97,114,114,97, +121,32,108,101,110,103,116,104,0,1622467,1581991,12,8,1310721,2,5, +26,2,1310721,2,2,1612333,12,1624165,14,1581837,2,1592471,12,1623117,16,1591423, +12,8,1581797,2,1592809,12,8,1581799,2,1592341,12,1,6,1,10,28, +34,63,38,3,26,1622797,16,3,1622797,16,1310721,2,7,26,2,49, +40,3,36,1622687,16,1622679,14,116,111,111,32,109,97,110,121,32, +112,97,114,97,109,101,116,101,114,115,0,1622639,1581991,12,8,1310721, +2,9,26,1310721,2,7,26,2,26,1622323,12,10,3,6,10,4, +8,1310721,2,7,26,1,6,2,1,6,3,26,5,6,4,10, +8,8,1581809,2,1592601,12,1,6,1,10,28,34,63,38,3,26, +1622793,16,1622797,14,1622607,14,1581799,2,1592809,12,8,1310721,2,57,26,1581877, +2,10,3,6,10,4,8,1310721,2,77,26,1,10,3,6,10, +4,8,1581815,2,1592601,12,1623047,16,1581835,2,1592341,12,1,6,1,10, +28,34,63,38,3,26,1622921,16,1310721,2,57,26,1622323,12,10,3, +6,10,4,8,1581835,2,1592601,12,1623047,16,1581855,2,1592471,12,1,6, +1,10,28,34,63,38,3,26,1623019,16,1623011,14,116,104,101,32, +114,101,116,117,114,110,32,116,97,103,32,105,115,32,46,117, +0,1622969,1581991,12,8,1591423,12,8,1310721,2,77,26,3,10,3,6, +10,4,8,1310721,2,57,26,2,1310721,2,7,26,2,1310721,2,9, +26,1310721,2,77,26,2,1310721,2,7,26,2,26,1310721,2,7,26, +2,26,1612831,12,1624165,14,1579517,2,5,28,1,6,1,10,28,34, +63,38,1,10,28,1623195,16,1623187,14,101,120,112,101,99,116,101, +100,32,97,32,116,121,112,101,0,1623155,1581991,12,8,1310721,2,1579519, +2,10,3,6,10,4,8,1310721,2,77,26,1310721,2,2,1621855,12, +10,3,6,10,4,8,1310721,2,77,26,2,1,40,3,36,1623297, +16,1591423,12,8,1310721,2,77,26,2,1310721,2,2,1613731,12,1624165,14, +1310721,2,3,26,1310721,2,2,1583635,12,10,3,6,10,4,8,1310721, +2,3,26,2,1,40,1623351,16,3,1623415,14,1417729,1310721,2,3,26, +2,26,2,15,28,1,6,1,10,28,34,63,38,1,10,28, +1,6,1,10,28,34,63,38,1,10,28,1623463,16,1310721,2,2, +1623455,14,105,115,110,39,116,32,97,32,116,121,112,101,0,1623429, +1582117,12,8,1591423,12,8,1310721,2,5,26,1425921,1310721,2,3,26,2, +26,2,10,3,6,10,4,8,1310721,2,5,26,2,1,26,2, +21,28,1,6,1,10,28,34,63,38,1,10,28,1623565,16,1310721, +2,5,26,2,1624165,14,1581917,2,1592809,12,8,3,1623987,16,1310721,2, +75,26,2,17,40,3,36,1623663,16,1623655,14,116,111,111,32,109, +97,110,121,32,116,121,112,101,32,97,114,103,117,109,101,110, +116,115,0,1623607,1581991,12,8,1310721,2,59,26,1310721,2,75,26,2, +26,1622323,12,10,3,6,10,4,8,1310721,2,59,26,1310721,2,75, +26,2,26,2,1615957,12,3,28,1,6,1,10,28,34,63,38, +1,10,28,1623913,16,1623905,14,116,121,112,101,32,97,114,103,117, +109,101,110,116,115,32,104,97,118,101,32,116,111,32,98,101, +32,111,110,101,32,119,111,114,100,32,40,97,32,112,111,105, +110,116,101,114,44,32,97,32,110,117,109,98,101,114,44,32, +111,114,32,97,32,112,97,114,97,109,101,116,101,114,41,0, +1623759,1581991,12,8,1310721,2,75,26,1,6,2,1,6,3,26,5, +6,4,10,8,8,1581809,2,1592601,12,1,6,1,10,28,34,63, +38,3,26,1623983,16,1623987,14,1623575,14,1622243,12,8,1310721,2,75,26, +2,1310721,2,5,26,2,7,26,2,28,1,6,1,10,28,34, +63,38,1,10,28,1624127,16,1310721,2,2,1624119,14,119,114,111,110, +103,32,110,117,109,98,101,114,32,111,102,32,116,121,112,101, +32,97,114,103,117,109,101,110,116,115,0,1624057,1582117,12,8,1310721, +2,5,26,2,1310721,2,59,26,1310721,2,75,26,2,1614101,12,1624165, +14,1,1310721,2,79,26,1310721,4,18,1310721,2,1,28,1310721,4,1579517, +2,9,28,1,6,1,10,28,34,63,38,3,26,1624627,16,1579519, +2,1581805,2,28,1,6,1,10,28,34,63,38,3,26,1624263,16, +3,1624315,14,1579519,2,1581835,2,28,1,6,1,10,28,34,63,38, +3,26,1,6,1,10,28,34,63,38,1,10,28,1624325,16,3, +1624743,14,1579519,2,1581821,2,28,1,6,1,10,28,34,63,38,3, +26,1624621,16,1591561,12,8,1581739,2,5,28,1,6,1,10,28,34, +63,38,3,26,1624493,16,1581741,2,1622029,12,1624415,16,3,1624467,14,1581741, +2,1581837,2,28,1,6,1,10,28,34,63,38,3,26,1,6, +1,10,28,34,63,38,1,10,28,1,6,1,10,28,34,63, +38,1,10,28,1624495,14,1,1624505,16,3,1624617,14,1581739,2,9,28, +1,6,1,10,28,34,63,38,3,26,1624593,16,1581741,2,1581805,2, +28,1,6,1,10,28,34,63,38,3,26,1,6,1,10,28, +34,63,38,1,10,28,1624595,14,1,1,6,1,10,28,34,63, +38,1,10,28,1624743,14,1,1624743,14,1579517,2,5,28,1,6,1, +10,28,34,63,38,1,10,28,1624667,16,1,1624743,14,1579519,2,1581837, +2,28,1,6,1,10,28,34,63,38,3,26,1624707,16,3,1624737, +14,1579519,2,1622029,12,1,6,1,10,28,34,63,38,1,10,28, +1624743,14,1,1310721,2,1,26,1310721,4,18,1310721,2,1,28,1310721,4, +1581913,1,10,3,6,10,4,8,1581915,1,10,3,6,10,4,8, +1581835,2,1592341,12,1,6,1,10,28,34,63,38,3,26,1624851,16, +1581913,1622323,12,10,3,6,10,4,8,1581835,2,1592601,12,1625087,16,1579519, +2,1581851,2,28,1,6,1,10,28,34,63,38,3,26,1624917,16, +1581915,3,10,3,6,10,4,8,1625081,14,1579519,2,1581855,2,28,1, +6,1,10,28,34,63,38,3,26,1624971,16,1581915,5,10,3,6, +10,4,8,1625081,14,1579519,2,1581853,2,28,1,6,1,10,28,34, +63,38,3,26,1625025,16,1581915,7,10,3,6,10,4,8,1625081,14, +1625073,14,97,32,116,97,103,32,105,115,32,46,117,44,32,46, +109,32,111,114,32,46,100,0,1625029,1581991,12,8,1591423,12,8,1, +1625095,14,1,1310721,2,1,26,1310721,4,18,1310721,2,21,28,1310721,4, +1,1310721,2,7,26,4,1524225,1310721,2,9,26,4,1,1310721,2,15, +26,4,1,1310721,2,17,26,4,1591423,12,8,1310721,2,1592987,12,10, +3,6,10,4,8,1581917,2,1592341,12,1625589,16,1591423,12,8,1581911,1, +10,3,6,10,4,8,3,1625405,16,1581911,2,17,40,3,36,1625313, +16,1625305,14,116,111,111,32,109,97,110,121,32,116,121,112,101, +32,112,97,114,97,109,101,116,101,114,115,0,1625255,1581991,12,8, +1581895,1581911,2,26,1592987,12,10,3,6,10,4,8,1581911,1,6,2, +1,6,3,26,5,6,4,10,8,8,1581809,2,1592601,12,1,6, +1,10,28,34,63,38,3,26,1625401,16,1625405,14,1625229,14,1622243,12, +8,1310721,2,17,26,3,10,3,6,10,4,8,1310721,2,3,26, +21,1,1310721,2,2,1,1611655,12,10,3,6,10,4,8,1310721,2, +3,26,2,7,26,1581911,2,10,3,6,10,4,8,1310721,2,13, +26,1581911,2,1611463,12,10,3,6,10,4,8,1310721,2,13,26,2, +1581895,1581911,2,1574139,12,8,1310721,2,3,26,2,13,26,1310721,2,13, +26,2,10,3,6,10,4,8,1625659,14,1310721,2,3,26,17,1, +1310721,2,2,1,1611655,12,10,3,6,10,4,8,1310721,2,3,26, +2,7,26,5,1,10,28,10,3,6,10,4,8,1310721,2,5, +26,1310721,2,2,15,1310721,2,3,26,2,1583159,12,10,3,6,10, +4,8,1581801,2,1592809,12,8,1581803,2,1592341,12,1,6,1,10,28, +34,63,38,3,26,1626037,16,1310721,2,19,26,1622323,12,10,3,6, +10,4,8,3,1626023,16,1310721,2,7,26,2,2001,40,3,36,1625839, +16,1625831,14,116,111,111,32,109,97,110,121,32,102,105,101,108, +100,115,0,1625799,1581991,12,8,1310721,2,9,26,2,1310721,2,7,26, +2,1310721,2,7,26,2,26,26,1592987,12,10,3,6,10,4,8, +1310721,2,9,26,2,1310721,2,7,26,2,1310721,2,7,26,2,26, +3,26,26,1310721,2,19,26,2,10,3,6,10,4,8,1310721,2, +7,26,1,6,2,1,6,3,26,5,6,4,10,8,8,1581809, +2,1592601,12,1,6,1,10,28,34,63,38,3,26,1626019,16,1626023, +14,1625767,14,1581811,2,1592809,12,8,1625711,14,1591423,12,8,1310721,2,17, +26,2,1626243,16,1310721,2,13,26,1310721,2,7,26,2,1310721,2,7, +26,2,26,1611463,12,10,3,6,10,4,8,1310721,2,13,26,2, +1310721,2,9,26,2,1310721,2,7,26,2,1310721,2,7,26,2,26, +1574139,12,8,1310721,2,3,26,2,9,26,1310721,2,13,26,2,10, +3,6,10,4,8,1310721,2,3,26,2,11,26,1310721,2,7,26, +2,10,3,6,10,4,8,1581911,1,10,3,6,10,4,8,1626929, +14,1310721,2,13,26,1310721,2,7,26,2,7,30,3,26,1611463,12, +10,3,6,10,4,8,1310721,2,11,26,1,10,3,6,10,4, +8,1310721,2,11,26,2,1310721,2,7,26,2,40,1626821,16,1310721,2, +19,26,1310721,2,9,26,2,1310721,2,11,26,2,1310721,2,11,26, +2,26,3,26,26,2,10,3,6,10,4,8,1310721,2,19,26, +2,1310721,2,3,26,2,28,1,6,1,10,28,34,63,38,3, +26,1626559,16,1626551,14,97,32,115,116,114,117,99,116,32,99,97, +110,39,116,32,99,111,110,116,97,105,110,32,105,116,115,101, +108,102,32,98,121,32,118,97,108,117,101,59,32,117,115,101, +32,97,32,112,111,105,110,116,101,114,0,1626443,1581991,12,8,1310721, +2,13,26,2,1310721,2,11,26,2,7,30,26,1310721,2,9,26, +2,1310721,2,11,26,2,1310721,2,11,26,2,26,26,2,10,3, +6,10,4,8,1310721,2,13,26,2,1310721,2,11,26,2,7,30, +3,26,26,1310721,2,19,26,2,10,3,6,10,4,8,1310721,2, +13,26,2,1310721,2,11,26,2,7,30,5,26,26,1310721,2,15, +26,2,10,3,6,10,4,8,1310721,2,15,26,1310721,2,15,26, +2,1310721,2,19,26,2,1615957,12,26,10,3,6,10,4,8,1310721, +2,11,26,1,6,2,1,6,3,26,5,6,4,10,8,8, +1626307,14,1310721,2,3,26,2,9,26,1310721,2,13,26,2,10,3, +6,10,4,8,1310721,2,3,26,2,3,26,1310721,2,15,26,2, +10,3,6,10,4,8,1310721,2,3,26,2,7,26,1310721,2,7, +26,2,10,3,6,10,4,8,1581811,2,1592601,12,8,1310721,2,5, +26,2,1626955,14,1,1310721,2,21,26,1310721,4,18,1310721,2,3,28, +1310721,4,1591423,12,8,1310721,2,1592987,12,10,3,6,10,4,8,1581813, +2,1592809,12,8,1310721,2,2,15,1622323,12,1583159,12,8,1581811,2,1592809, +12,8,1,1627053,14,1,1310721,2,3,26,1310721,4,18,1310721,2,1, +28,1310721,4,1591423,12,8,1581939,2,1592471,12,1627117,16,1581887,3,10,3, +6,10,4,8,1627221,14,1581941,2,1592471,12,1627149,16,1581887,1,10,3, +6,10,4,8,1627221,14,1627213,14,116,121,112,101,99,104,101,99, +107,32,111,110,32,111,114,32,116,121,112,101,99,104,101,99, +107,32,111,102,102,0,1627153,1581991,12,8,1591423,12,8,1581811,2,1592809, +12,8,1,1627245,14,1,1310721,2,1,26,1310721,4,18,1310721,2,7, +28,1310721,4,1310721,2,3,26,4,1310721,2,4,1,1310721,2,5,26, +4,1310721,2,2,1614941,12,8,1310721,2,5,26,2,1310721,2,2,7, +26,2,40,1627457,16,1310721,2,2,9,26,2,1310721,2,5,26,2, +7,30,26,2,1310721,2,3,26,2,28,1,6,1,10,28,34, +63,38,3,26,1627419,16,1310721,2,5,26,2,1627471,14,1310721,2,5, +26,1,6,2,1,6,3,26,5,6,4,10,8,8,1627311,14, +3,1,10,28,1627471,14,1,1310721,2,7,26,1310721,4,18,1310721,2, +7,28,1310721,4,1581797,2,1592809,12,8,1624179,12,1627569,16,1581835,2,1592341, +12,1,6,1,10,28,34,63,38,3,26,1,6,1,10,28, +34,63,38,1,10,28,1627571,14,1,1627599,16,1310721,2,1622323,12,10, +3,6,10,4,8,1627761,14,1310721,2,3,26,1599965,12,10,3,6, +10,4,8,1581893,1,6,2,1,6,3,26,5,6,4,10,8, +8,1310721,2,5,26,1646495,12,10,3,6,10,4,8,1633929,12,8, +1310721,2,1581889,2,10,3,6,10,4,8,1581893,1,6,2,1,6, +-1,26,5,6,4,10,8,8,1310721,2,3,26,2,1600553,12,8, +1310721,2,3,26,2,1600493,12,8,1581799,2,1592809,12,8,1310721,2,2, +1627783,14,1,1310721,2,7,26,1310721,4,18,1310721,2,3,28,1310721,4, +1591423,12,8,1310721,2,1627485,12,10,3,6,10,4,8,1310721,2,2, +1615957,12,1584843,12,8,1581889,1581879,2,10,3,6,10,4,8,1,1627877, +14,1,1310721,2,3,26,1310721,4,18,1310721,2,3,28,1310721,4,1591423, +12,8,1310721,2,1627485,12,10,3,6,10,4,8,1310721,2,2,1584843, +12,8,1581889,1581877,2,10,3,6,10,4,8,1,1627967,14,1,1310721, +2,3,26,1310721,4,18,1310721,2,1,28,1310721,4,1581873,1277953,2049,26, +10,3,6,10,4,8,1581875,1,10,3,6,10,4,8,1581877,1, +3,1628049,14,119,111,114,100,0,1628039,1582663,12,1,1611655,12,10,3, +6,10,4,8,1581879,3,3,1628091,14,105,110,116,0,1628083,1582663,12, +1,1611655,12,10,3,6,10,4,8,1581881,5,3,1628135,14,99,104, +97,114,0,1628125,1582663,12,1,1611655,12,10,3,6,10,4,8,1581883, +7,3,1628179,14,98,111,111,108,0,1628169,1582663,12,1,1611655,12,10, +3,6,10,4,8,1581885,9,1,1628223,14,118,111,105,100,0,1628213, +1582663,12,1,1611655,12,10,3,6,10,4,8,1581877,2,5,26,2, +15,1581877,2,1583159,12,8,1581879,2,5,26,2,15,1581879,2,1583159,12, +8,1581881,2,5,26,2,15,1581881,2,1583159,12,8,1581883,2,5,26, +2,15,1581883,2,1583159,12,8,1581885,2,5,26,2,15,1581885,2,1583159, +12,8,1581917,1628367,14,60,0,1628363,1582663,12,10,3,6,10,4,8, +1581919,1628395,14,62,0,1628391,1582663,12,10,3,6,10,4,8,1581921,1628425, +14,62,62,0,1628419,1582663,12,10,3,6,10,4,8,1581923,1628455,14, +60,61,0,1628449,1582663,12,10,3,6,10,4,8,1581925,1628485,14,62, +61,0,1628479,1582663,12,10,3,6,10,4,8,1581927,1628515,14,61,61, +0,1628509,1582663,12,10,3,6,10,4,8,1581929,1628545,14,33,61,0, +1628539,1582663,12,10,3,6,10,4,8,1581931,1628573,14,43,0,1628569,1582663, +12,10,3,6,10,4,8,1581933,1628611,14,115,116,114,117,99,116, +0,1628597,1582663,12,10,3,6,10,4,8,1581935,1628645,14,116,121,112, +101,0,1628635,1582663,12,10,3,6,10,4,8,1581937,1628689,14,116,121, +112,101,99,104,101,99,107,0,1628669,1582663,12,10,3,6,10,4, +8,1581939,1628719,14,111,110,0,1628713,1582663,12,10,3,6,10,4,8, +1581941,1628751,14,111,102,102,0,1628743,1582663,12,10,3,6,10,4,8, +1581943,1628783,14,110,101,119,0,1628775,1582663,12,10,3,6,10,4,8, +1581945,1628819,14,110,101,119,95,116,0,1628807,1582663,12,10,3,6,10, +4,8,1581947,1,26,1628851,14,42,0,1628847,1582663,12,10,3,6,10, +4,8,1581947,3,26,1628883,14,47,0,1628879,1582663,12,10,3,6,10, +4,8,1581947,5,26,1628915,14,37,0,1628911,1582663,12,10,3,6,10, +4,8,1581947,7,26,1628947,14,38,0,1628943,1582663,12,10,3,6,10, +4,8,1581947,9,26,1628979,14,124,0,1628975,1582663,12,10,3,6,10, +4,8,1581947,11,26,1629011,14,94,0,1629007,1582663,12,10,3,6,10, +4,8,1581947,13,26,1629045,14,60,60,0,1629039,1582663,12,10,3,6, +10,4,8,1581947,15,26,1581921,2,10,3,6,10,4,8,1581971,17, +10,3,6,10,4,8,1,1629109,14,1,1310721,2,1,26,1310721,4, +18,1310721,2,5,28,1310721,4,1310721,2,4,1,1310721,2,3,26,4, +1310721,2,3,26,2,1581971,2,40,1629265,16,1581947,1310721,2,3,26,2, +26,2,1310721,2,2,28,1,6,1,10,28,34,63,38,3,26, +1629227,16,3,1629273,14,1310721,2,3,26,1,6,2,1,6,3,26, +5,6,4,10,8,8,1629153,14,1,1629273,14,1,1310721,2,5,26, +1310721,4,18,1310721,2,9,28,1310721,4,1310721,2,5,26,4,1310721,2, +3,26,4,1310721,2,4,1546753,1310721,2,2,26,2,1310721,2,7,26, +4,1310721,2,3,26,1310721,2,3,26,2,1617577,12,10,3,6,10, +4,8,1310721,2,5,26,1310721,2,5,26,2,1617577,12,10,3,6, +10,4,8,1547137,1310721,2,2,26,2,1,40,1629611,16,1310721,2,3, +26,2,1617293,12,1,6,1,10,28,34,63,38,3,26,1629533,16, +1310721,2,5,26,2,1617293,12,1,6,1,10,28,34,63,38,3, +26,1,6,1,10,28,34,63,38,1,10,28,1629535,14,1,1629585, +14,38,38,32,97,110,100,32,124,124,32,110,101,101,100,32, +115,99,97,108,97,114,115,0,1629539,1310721,2,3,26,2,1621475,12, +8,1581883,2,1632037,14,1310721,2,7,26,2,1581927,2,28,1,6,1, +10,28,34,63,38,3,26,1629657,16,3,1629715,14,1310721,2,7,26, +2,1581929,2,28,1,6,1,10,28,34,63,38,3,26,1,6, +1,10,28,34,63,38,1,10,28,1629905,16,1310721,2,3,26,2, +1310721,2,5,26,2,1617847,12,1,6,1,10,28,34,63,38,3, +26,1629837,16,1310721,2,5,26,2,1310721,2,3,26,2,1617847,12,1, +6,1,10,28,34,63,38,3,26,1,6,1,10,28,34,63, +38,1,10,28,1629839,14,1,1629897,16,1629869,14,99,111,109,112,97, +114,105,115,111,110,0,1629847,1310721,2,3,26,2,1310721,2,5,26, +2,1621085,12,8,1581883,2,1632037,14,1310721,2,7,26,2,1581917,2,28, +1,6,1,10,28,34,63,38,3,26,1629951,16,3,1630009,14,1310721, +2,7,26,2,1581919,2,28,1,6,1,10,28,34,63,38,3, +26,1,6,1,10,28,34,63,38,1,10,28,1630019,16,3,1630077, +14,1310721,2,7,26,2,1581923,2,28,1,6,1,10,28,34,63, +38,3,26,1,6,1,10,28,34,63,38,1,10,28,1630087,16, +3,1630145,14,1310721,2,7,26,2,1581925,2,28,1,6,1,10,28, +34,63,38,3,26,1,6,1,10,28,34,63,38,1,10,28, +1630575,16,1310721,2,3,26,2,1581877,2,28,1,6,1,10,28,34, +63,38,3,26,1630195,16,3,1630253,14,1310721,2,5,26,2,1581877,2, +28,1,6,1,10,28,34,63,38,3,26,1,6,1,10,28, +34,63,38,1,10,28,1630263,16,3,1630345,14,1310721,2,3,26,2, +1617083,12,1630321,16,1310721,2,5,26,2,1617083,12,1,6,1,10,28, +34,63,38,1,10,28,1630323,14,1,1,6,1,10,28,34,63, +38,1,10,28,1630355,16,3,1630489,14,1310721,2,3,26,2,1,26, +2,11,28,1,6,1,10,28,34,63,38,3,26,1630465,16,1310721, +2,5,26,2,1,26,2,11,28,1,6,1,10,28,34,63, +38,3,26,1,6,1,10,28,34,63,38,1,10,28,1630467,14, +1,1,6,1,10,28,34,63,38,1,10,28,1,6,1,10, +28,34,63,38,3,26,1630567,16,1630539,14,99,111,109,112,97,114, +105,115,111,110,0,1630517,1310721,2,3,26,2,1310721,2,5,26,2, +1621085,12,8,1581883,2,1632037,14,1310721,2,7,26,2,1581931,2,28,1, +6,1,10,28,34,63,38,3,26,1631123,16,1310721,2,3,26,2, +1,26,2,11,28,1,6,1,10,28,34,63,38,3,26,1630767, +16,1310721,2,5,26,2,1617083,12,1630683,16,3,1630741,14,1310721,2,5, +26,2,1581877,2,28,1,6,1,10,28,34,63,38,3,26,1, +6,1,10,28,34,63,38,1,10,28,1,6,1,10,28,34, +63,38,1,10,28,1630769,14,1,1630787,16,1310721,2,3,26,2,1632037, +14,1310721,2,5,26,2,1,26,2,11,28,1,6,1,10,28, +34,63,38,3,26,1630871,16,1310721,2,3,26,2,1617083,12,1,6, +1,10,28,34,63,38,1,10,28,1630873,14,1,1630891,16,1310721,2, +5,26,2,1632037,14,1310721,2,3,26,2,1581877,2,28,1,6,1, +10,28,34,63,38,3,26,1630937,16,3,1630995,14,1310721,2,5,26, +2,1581877,2,28,1,6,1,10,28,34,63,38,3,26,1,6, +1,10,28,34,63,38,1,10,28,1631007,16,1581877,2,1632037,14,1310721, +2,3,26,2,1617083,12,1631065,16,1310721,2,5,26,2,1617083,12,1, +6,1,10,28,34,63,38,1,10,28,1631067,14,1,1631079,16,1581879, +2,1632037,14,1631087,14,43,0,1631083,1310721,2,3,26,2,1310721,2,5, +26,2,1621085,12,8,1581877,2,1632037,14,1310721,2,7,26,2,1581829,2, +28,1,6,1,10,28,34,63,38,3,26,1631691,16,1310721,2,3, +26,2,1,26,2,11,28,1,6,1,10,28,34,63,38,3, +26,1631273,16,1310721,2,5,26,2,1,26,2,11,28,1,6,1, +10,28,34,63,38,3,26,1,6,1,10,28,34,63,38,1, +10,28,1631275,14,1,1631287,16,1581879,2,1632037,14,1310721,2,3,26,2, +1,26,2,11,28,1,6,1,10,28,34,63,38,3,26,1631439, +16,1310721,2,5,26,2,1617083,12,1631355,16,3,1631413,14,1310721,2,5, +26,2,1581877,2,28,1,6,1,10,28,34,63,38,3,26,1, +6,1,10,28,34,63,38,1,10,28,1,6,1,10,28,34, +63,38,1,10,28,1631441,14,1,1631459,16,1310721,2,3,26,2,1632037, +14,1310721,2,3,26,2,1581877,2,28,1,6,1,10,28,34,63, +38,3,26,1631505,16,3,1631563,14,1310721,2,5,26,2,1581877,2,28, +1,6,1,10,28,34,63,38,3,26,1,6,1,10,28,34, +63,38,1,10,28,1631575,16,1581877,2,1632037,14,1310721,2,3,26,2, +1617083,12,1631633,16,1310721,2,5,26,2,1617083,12,1,6,1,10,28, +34,63,38,1,10,28,1631635,14,1,1631647,16,1581879,2,1632037,14,1631655, +14,45,0,1631651,1310721,2,3,26,2,1310721,2,5,26,2,1621085,12, +8,1581877,2,1632037,14,1310721,2,7,26,2,1629123,12,1632027,16,1310721,2, +3,26,2,1581877,2,28,1,6,1,10,28,34,63,38,3,26, +1631755,16,3,1631813,14,1310721,2,5,26,2,1581877,2,28,1,6,1, +10,28,34,63,38,3,26,1,6,1,10,28,34,63,38,1, +10,28,1631825,16,1581877,2,1632037,14,1310721,2,3,26,2,1617083,12,1631883, +16,1310721,2,5,26,2,1617083,12,1,6,1,10,28,34,63,38, +1,10,28,1631885,14,1,1631897,16,1581879,2,1632037,14,1310721,2,3,26, +2,1617083,12,1631945,16,1310721,2,3,26,1310721,2,5,26,2,10,3, +6,10,4,8,1,1632001,14,97,114,105,116,104,109,101,116,105, +99,32,110,101,101,100,115,32,110,117,109,98,101,114,115,0, +1631951,1310721,2,3,26,2,1621475,12,8,1581877,2,1632037,14,1581877,2,1632037, +14,1,1310721,2,9,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721, +2,4,1310721,2,2,1,26,2,1632087,14,1,1310721,2,3,26,1310721, +4,18,1310721,2,3,28,1310721,4,1310721,2,4,1310721,2,2,1615957,12, +1632135,14,1,1310721,2,3,26,1310721,4,18,1310721,2,3,28,1310721,4, +1310721,2,4,1310721,2,2,5,26,2,1632185,14,1,1310721,2,3,26, +1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,4,1310721,2,2,7, +26,2,1632235,14,1,1310721,2,3,26,1310721,4,18,1310721,2,3,28, +1310721,4,1310721,2,4,1310721,2,2,1614941,12,8,1310721,2,2,7,26, +2,1632297,14,1,1310721,2,3,26,1310721,4,18,1310721,2,5,28,1310721, +4,1310721,2,3,26,4,1310721,2,4,1310721,2,2,1614941,12,8,1310721, +2,2,9,26,2,1310721,2,3,26,2,7,30,26,2,1632387,14, +1,1310721,2,5,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2, +3,26,4,1310721,2,4,1310721,2,2,1614941,12,8,1310721,2,2,9, +26,2,1310721,2,3,26,2,7,30,3,26,26,2,1632481,14,1, +1310721,2,5,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,3, +26,4,1310721,2,4,1310721,2,2,1614941,12,8,1310721,2,2,9,26, +2,1310721,2,3,26,2,7,30,5,26,26,2,1632575,14,1,1310721, +2,5,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,4,1310721, +2,2,9,26,2,1632625,14,1,1310721,2,3,26,1310721,4,18,1310721, +2,3,28,1310721,4,1310721,2,4,1310721,2,2,7,26,2,1632675,14, +1,1310721,2,3,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2, +3,26,4,1310721,2,4,1310721,2,2,15,1310721,2,3,26,2,26, +26,2,1632747,14,1,1310721,2,5,26,1310721,4,18,1310721,2,5,28, +1310721,4,1310721,2,3,26,4,1310721,2,4,1310721,2,2,11,26,2, +13,38,1310721,2,3,26,2,1310721,2,3,26,2,26,38,7,32, +1632839,14,1,1310721,2,5,26,1310721,4,18,1310721,2,3,28,1310721,4, +1310721,2,4,1310721,2,2,11,26,2,3,32,1632893,14,1,1310721,2, +3,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,4,1310721,2, +2,1,26,2,17,28,1,6,1,10,28,34,63,38,1,10, +28,1632973,16,3,1633027,14,1310721,2,2,11,26,2,1,6,1,10, +28,34,63,38,3,26,1,6,1,10,28,34,63,38,1,10, +28,1633037,16,1,1633061,14,1310721,2,2,13,26,2,1,26,2,1633061, +14,1,1310721,2,3,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721, +2,3,26,4,1310721,2,4,1310721,2,2,13,26,2,3,1310721,2, +3,26,2,26,26,2,1633139,14,1,1310721,2,5,26,1310721,4,18, +1310721,2,59,28,1310721,4,1310721,2,4,1579447,2,1310721,2,3,26,4, +3,1,10,28,1310721,2,5,26,4,1310721,2,3,26,2,1633461,16, +1310721,2,3,26,1,6,2,1,6,-1,26,5,6,4,10,8, +8,1417729,1310721,2,3,26,2,26,2,3,28,1,6,1,10,28, +34,63,38,3,26,1633365,16,1425921,1310721,2,3,26,2,26,2,1310721, +2,2,28,1,6,1,10,28,34,63,38,3,26,1,6,1, +10,28,34,63,38,1,10,28,1633367,14,1,1633417,16,1442305,1310721,2, +3,26,2,26,2,3,32,1,6,1,10,28,34,63,38,1, +10,28,1633419,14,1,1633457,16,1310721,2,5,26,1310721,2,3,26,2, +10,3,6,10,4,8,1633461,14,1633203,14,1310721,2,5,26,2,1, +40,1633485,16,1,1633739,14,1559041,1310721,2,5,26,2,26,2,1633525,16, +1559041,1310721,2,5,26,2,26,2,1633739,14,1310721,2,55,26,1434113,1310721, +2,5,26,2,26,2,3,38,63,32,10,3,6,10,4,8, +1310721,2,57,26,1,10,3,6,10,4,8,1310721,2,57,26,2, +1310721,2,55,26,2,40,1633691,16,1310721,2,7,26,1310721,2,57,26, +2,26,1581877,2,10,3,6,10,4,8,1310721,2,57,26,1,6, +2,1,6,3,26,5,6,4,10,8,8,1633591,14,1581877,2,1310721, +2,55,26,2,1310721,2,7,26,1434113,1310721,2,5,26,2,26,2, +1612831,12,1633739,14,1,1310721,2,59,26,1310721,4,18,1310721,2,5,28, +1310721,4,1310721,2,4,1310721,2,2,1584093,12,1310721,2,3,26,4,1310721, +2,3,26,2,1,40,1633815,16,3,1633879,14,1417729,1310721,2,3,26, +2,26,2,3,28,1,6,1,10,28,34,63,38,1,10,28, +1,6,1,10,28,34,63,38,1,10,28,1633889,16,1,1633915,14, +1425921,1310721,2,3,26,2,26,2,1633153,12,1633915,14,1,1310721,2,5, +26,1310721,4,18,1310721,2,1,28,1310721,4,1581785,2,1634005,16,1581889,2, +1617293,12,1,6,1,10,28,34,63,38,3,26,1633989,16,3,1584907, +12,8,1581785,1,10,3,6,10,4,8,1,1634013,14,1,1310721,2, +1,26,1310721,4,18,1310721,2,11,28,1310721,4,1310721,2,3,26,4, +1310721,2,4,1425921,1310721,2,2,26,2,1310721,2,5,26,4,1581791,2, +1310721,2,7,26,4,1581791,1310721,2,3,26,2,10,3,6,10,4, +8,1581793,1,10,3,6,10,4,8,1581889,1581877,2,10,3,6,10, +4,8,1581891,1,10,3,6,10,4,8,1310721,2,5,26,2,1, +6,12,10,8,8,1581791,1310721,2,7,26,2,10,3,6,10,4, +8,1310721,2,9,26,1581793,2,10,3,6,10,4,8,1581785,1,10, +3,6,10,4,8,1581787,1,10,3,6,10,4,8,1310721,2,9, +26,2,1634283,14,1,1310721,2,11,26,1310721,4,18,1310721,2,1,28, +1310721,4,1581791,2,1634409,16,1634401,14,116,104,105,115,32,99,97,110, +39,116,32,98,101,32,117,115,101,100,32,105,110,115,105,100, +101,32,97,110,32,101,120,112,114,101,115,115,105,111,110,0, +1634321,1581991,12,8,1,1634417,14,1,1310721,2,1,26,1310721,4,18,1310721, +2,13,28,1310721,4,1310721,2,5,26,4,1310721,2,3,26,4,1310721, +2,4,1,1310721,2,7,26,4,1581797,2,1592809,12,8,1581799,2,1592341, +12,1,6,1,10,28,34,63,38,3,26,1634915,16,3,1634915,16, +1310721,2,9,26,1646495,12,10,3,6,10,4,8,1633929,12,8,1310721, +2,9,26,2,1310721,2,2,1310721,2,7,26,2,1310721,2,7,26, +2,26,38,7,32,1604537,12,8,1310721,2,3,26,2,1634677,16,1310721, +2,7,26,2,1310721,2,3,26,2,7,26,2,40,1,6,1, +10,28,34,63,38,1,10,28,1634679,14,1,1634841,16,1310721,2,11, +26,1310721,2,3,26,2,15,1310721,2,7,26,2,26,26,2,10, +3,6,10,4,8,1310721,2,11,26,2,1581889,2,1310721,2,5,26, +2,1618795,12,1,6,1,10,28,34,63,38,3,26,1634841,16,1634805, +14,97,114,103,117,109,101,110,116,0,1634787,1310721,2,11,26,2, +1310721,2,5,26,2,1616107,12,1581889,2,1621085,12,8,1310721,2,7,26, +1,6,2,1,6,3,26,5,6,4,10,8,8,1581809,2,1592601, +12,1,6,1,10,28,34,63,38,3,26,1634911,16,1634915,14,1634523, +14,1581799,2,1592809,12,8,1310721,2,7,26,2,1634941,14,1,1310721,2, +13,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,1622323,12,10, +3,6,10,4,8,1581809,2,1592601,12,1635131,16,1310721,2,3,26,1646495, +12,10,3,6,10,4,8,1633929,12,8,1310721,2,3,26,2,1598391, +12,8,1310721,2,2,1615957,12,3,28,1,6,1,10,28,34,63, +38,1,10,28,1635109,16,1310721,2,2,1615957,12,1584843,12,8,31,1584907, +12,8,1310721,2,2,1,1612333,12,1584843,12,8,1635159,14,1310721,2,2, +1615957,12,1584843,12,8,1310721,2,2,1584843,12,8,1581799,2,1592809,12,8, +1581945,2,1585453,12,8,1581785,1,10,3,6,10,4,8,1581787,1,10, +3,6,10,4,8,1581889,1310721,2,2,1611981,12,10,3,6,10,4, +8,1581891,1,10,3,6,10,4,8,3,1635259,14,1,1310721,2,5, +26,1310721,4,18,1310721,2,27,28,1310721,4,1310721,2,4,1,1310721,2, +5,26,4,1,1310721,2,7,26,4,1559041,1310721,2,2,26,2,1310721, +2,9,26,4,1409537,1310721,2,2,26,2,1581943,2,28,1,6,1, +10,28,34,63,38,3,26,1635441,16,1417729,1310721,2,2,26,2,3, +28,1,6,1,10,28,34,63,38,3,26,1,6,1,10,28, +34,63,38,1,10,28,1635443,14,1,1635771,16,1591561,12,8,1581739,2, +9,28,1,6,1,10,28,34,63,38,3,26,1635603,16,1581741,2, +1581821,2,28,1,6,1,10,28,34,63,38,3,26,1635525,16,3, +1635577,14,1581741,2,1581805,2,28,1,6,1,10,28,34,63,38,3, +26,1,6,1,10,28,34,63,38,1,10,28,1,6,1,10, +28,34,63,38,1,10,28,1635605,14,1,1635623,16,1591423,12,8,1634955, +12,1636371,14,1581739,2,5,28,1,6,1,10,28,34,63,38,3, +26,1635751,16,1581741,2,1622029,12,1635673,16,3,1635725,14,1581741,2,1581837,2, +28,1,6,1,10,28,34,63,38,3,26,1,6,1,10,28, +34,63,38,1,10,28,1,6,1,10,28,34,63,38,1,10, +28,1635753,14,1,1635771,16,1591423,12,8,1634955,12,1636371,14,1442305,1310721,2, +2,26,2,5,32,1635867,16,1310721,2,5,26,1434113,1310721,2,2,26, +2,13,38,10,3,6,10,4,8,1310721,2,7,26,1434113,1310721,2, +2,26,2,3,32,10,3,6,10,4,8,1635905,14,1442305,1310721,2, +2,26,1442305,1310721,2,2,26,2,9,34,10,3,6,10,4,8, +1310721,2,11,26,1,17,1574329,12,8,1581989,2,1635967,16,1310721,2,11, +26,1581973,1581989,2,1574139,12,8,1581989,1,10,3,6,10,4,8,1310721, +2,3,26,1310721,2,5,26,2,1310721,2,9,26,2,1310721,2,11, +26,1634431,12,10,3,6,10,4,8,1442305,1310721,2,2,26,2,5, +32,1636119,16,1310721,2,3,26,2,1434113,1310721,2,2,26,2,3,38, +63,32,28,1,6,1,10,28,34,63,38,1,10,28,1,6, +1,10,28,34,63,38,1,10,28,1636121,14,1,1636201,16,1409537,1310721, +2,2,26,2,1636193,14,119,114,111,110,103,32,110,117,109,98, +101,114,32,111,102,32,97,114,103,117,109,101,110,116,115,0, +1636141,1582117,12,8,1310721,2,2,1585301,12,8,13,1584907,12,8,1581785,1, +10,3,6,10,4,8,1581787,1,10,3,6,10,4,8,1581891,1, +10,3,6,10,4,8,1581889,1581877,2,10,3,6,10,4,8,1310721, +2,9,26,2,1636343,16,1581889,1310721,2,9,26,2,9,26,2,1310721, +2,11,26,1616107,12,10,3,6,10,4,8,1310721,2,7,26,2, +1636363,16,3,1636371,14,1,1636371,14,1,1310721,2,27,26,1310721,4,18, +1310721,2,11,28,1310721,4,1310721,2,4,1434113,1310721,2,2,26,2,31, +32,1310721,2,3,26,4,1434113,1310721,2,2,26,2,9,38,31,32, +1310721,2,5,26,4,1,1310721,2,7,26,4,1581797,2,1592809,12,8, +1581799,2,1592341,12,1,6,1,10,28,34,63,38,3,26,1636639,16, +3,1636639,16,1310721,2,9,26,1646495,12,10,3,6,10,4,8,1633929, +12,8,1310721,2,9,26,2,1598391,12,8,1310721,2,7,26,1,6, +2,1,6,3,26,5,6,4,10,8,8,1581809,2,1592601,12,1, +6,1,10,28,34,63,38,3,26,1636635,16,1636639,14,1636513,14,1581799, +2,1592809,12,8,1310721,2,7,26,2,1310721,2,3,26,2,28,1, +6,1,10,28,34,63,38,1,10,28,1636767,16,1409537,1310721,2,2, +26,2,1636759,14,119,114,111,110,103,32,110,117,109,98,101,114, +32,111,102,32,105,110,112,117,116,115,0,1636713,1582117,12,8,1581785, +1,10,3,6,10,4,8,1581787,1,10,3,6,10,4,8,1581889, +1581877,2,10,3,6,10,4,8,1581891,1,10,3,6,10,4,8, +1442305,1310721,2,2,26,2,129,32,1636889,16,1450497,1310721,2,2,26,2, +3,28,1585301,12,8,13,1584907,12,8,1,1637087,14,45,1425921,1310721,2, +2,26,2,26,1584907,12,8,1310721,2,5,26,2,1,28,1,6, +1,10,28,34,63,38,3,26,1636957,16,1,1584843,12,8,1310721,2, +5,26,2,3,10,40,1637079,16,1409537,1310721,2,2,26,2,1637071,14, +108,101,97,118,101,115,32,115,101,118,101,114,97,108,32,118, +97,108,117,101,115,59,32,117,115,101,32,105,116,32,102,114, +111,109,32,97,115,109,0,1636993,1582117,12,8,1,1637087,14,1,1310721, +2,11,26,1310721,4,18,1310721,2,9,28,1310721,4,1581785,1,10,3, +6,10,4,8,1581787,1,10,3,6,10,4,8,1581891,1,10,3, +6,10,4,8,1579517,2,3,28,1,6,1,10,28,34,63,38, +3,26,1637275,16,1579519,2,1584843,12,8,1581891,1579519,2,1,28,1,6, +1,10,28,34,63,38,3,26,10,3,6,10,4,8,1581889,1581879, +2,10,3,6,10,4,8,1591423,12,8,1,1639809,14,1579517,2,7, +28,1,6,1,10,28,34,63,38,3,26,1637351,16,1579519,2,1585671, +12,8,1591423,12,8,1581889,1581881,2,1611981,12,10,3,6,10,4,8, +1,1639809,14,1579517,2,9,28,1,6,1,10,28,34,63,38,3, +26,1637571,16,1579519,2,1581797,2,28,1,6,1,10,28,34,63,38, +3,26,1637471,16,1591423,12,8,1310721,2,7,26,1646495,12,10,3,6, +10,4,8,1581799,2,1592809,12,8,1310721,2,7,26,2,1639809,14,1579519, +2,1581801,2,28,1,6,1,10,28,34,63,38,3,26,1637513,16, +1648443,12,1639809,14,1637563,14,101,120,112,101,99,116,101,100,32,97, +110,32,101,120,112,114,101,115,115,105,111,110,0,1637517,1581991,12, +8,1579517,2,5,28,1,6,1,10,28,34,63,38,1,10,28, +1637663,16,1637655,14,101,120,112,101,99,116,101,100,32,97,110,32, +101,120,112,114,101,115,115,105,111,110,0,1637609,1581991,12,8,1310721, +2,1579519,2,10,3,6,10,4,8,1310721,2,3,26,1310721,2,2, +1597111,12,10,3,6,10,4,8,1310721,2,3,26,2,1,40,3, +36,1638135,16,1591423,12,8,1581763,2,1,6,1,10,28,34,63,38, +3,26,1637809,16,1310721,2,3,26,2,1597849,12,1,6,1,10,28, +34,63,38,1,10,28,1637811,14,1,1637959,16,1310721,2,2,1637951,14, +105,115,32,116,114,97,99,107,101,100,32,98,121,32,116,104, +101,32,97,110,97,108,121,122,101,114,32,97,110,100,32,99, +97,110,39,116,32,98,101,32,117,115,101,100,32,105,110,32, +117,110,99,104,101,99,107,101,100,32,99,111,100,101,0,1637825, +1582117,12,8,1467905,1310721,2,3,26,2,26,2,1585585,12,8,1581889,1556993, +1310721,2,3,26,2,26,2,10,3,6,10,4,8,1473025,1310721,2, +3,26,2,26,2,1638037,16,1,1639809,14,1581785,3,10,3,6,10, +4,8,1581787,3,10,3,6,10,4,8,1581789,1310721,2,3,26,2, +10,3,6,10,4,8,1310721,2,3,26,2,1597849,12,1638129,16,5, +1310721,2,3,26,2,26,1639809,14,1,1639809,14,1310721,2,5,26,1310721, +2,2,1583635,12,10,3,6,10,4,8,1310721,2,5,26,2,1, +40,3,36,1638253,16,1417729,1310721,2,5,26,2,26,2,11,28,1, +6,1,10,28,34,63,38,3,26,1,6,1,10,28,34,63, +38,1,10,28,1638255,14,1,1638411,16,1442305,1310721,2,5,26,2,26, +2,33,32,1,6,1,10,28,34,63,38,3,26,1638391,16,1310721, +2,2,1638383,14,99,97,110,39,116,32,98,101,32,117,115,101, +100,32,105,110,115,105,100,101,32,97,110,32,101,120,112,114, +101,115,115,105,111,110,0,1638313,1582117,12,8,1310721,2,5,26,2, +3,1634027,12,1639809,14,1310721,2,5,26,2,1,40,3,36,1638499,16, +1417729,1310721,2,5,26,2,26,2,15,28,1,6,1,10,28,34, +63,38,3,26,1,6,1,10,28,34,63,38,1,10,28,1638501, +14,1,1638569,16,1310721,2,2,1638561,14,105,115,32,97,32,116,121, +112,101,44,32,110,111,116,32,97,32,118,97,108,117,101,0, +1638515,1582117,12,8,1591423,12,8,1581889,1581877,2,10,3,6,10,4,8, +1310721,2,5,26,2,1,40,3,36,1638681,16,1417729,1310721,2,5,26, +2,26,2,9,28,1,6,1,10,28,34,63,38,3,26,1, +6,1,10,28,34,63,38,1,10,28,1638683,14,1,1638787,16,1425921, +1310721,2,5,26,2,26,2,1584843,12,8,1581889,1581879,2,10,3,6, +10,4,8,1581891,1425921,1310721,2,5,26,2,26,2,1,28,1,6, +1,10,28,34,63,38,3,26,10,3,6,10,4,8,1,1639809, +14,1310721,2,5,26,2,1,40,3,36,1638875,16,1417729,1310721,2,5, +26,2,26,2,5,28,1,6,1,10,28,34,63,38,3,26, +1,6,1,10,28,34,63,38,1,10,28,1638877,14,1,1638991,16, +1425921,1310721,2,5,26,2,26,2,1584843,12,8,1559041,1310721,2,5,26, +2,26,2,1638953,16,1581889,1559041,1310721,2,5,26,2,26,2,10,3, +6,10,4,8,1581785,3,10,3,6,10,4,8,1581787,5,10,3, +6,10,4,8,1,1639809,14,1310721,2,5,26,2,1,40,3,36, +1639079,16,1417729,1310721,2,5,26,2,26,2,7,28,1,6,1,10, +28,34,63,38,3,26,1,6,1,10,28,34,63,38,1,10, +28,1639081,14,1,1639163,16,1425921,1310721,2,5,26,2,26,2,1584843,12, +8,1559041,1310721,2,5,26,2,26,2,1639157,16,1581889,1559041,1310721,2,5, +26,2,26,2,10,3,6,10,4,8,1,1639809,14,1310721,2,5, +26,2,1,40,3,36,1639251,16,1417729,1310721,2,5,26,2,26,2, +13,28,1,6,1,10,28,34,63,38,3,26,1,6,1,10, +28,34,63,38,1,10,28,1639253,14,1,1639275,16,1310721,2,5,26, +2,1636385,12,1639809,14,1310721,2,5,26,2,1,40,1639323,16,1310721,2, +5,26,1310721,2,2,1585201,12,10,3,6,10,4,8,1581917,2,1592341, +12,1639381,16,1442305,1310721,2,5,26,2,26,2,257,32,1,6,1, +10,28,34,63,38,1,10,28,1639383,14,1,1639705,16,1591423,12,8, +1581989,1,10,3,6,10,4,8,3,1639583,16,1581989,2,17,40,3, +36,1639491,16,1639483,14,116,111,111,32,109,97,110,121,32,116,121, +112,101,32,97,114,103,117,109,101,110,116,115,0,1639435,1581991,12, +8,1581973,1581989,2,26,1622323,12,10,3,6,10,4,8,1581989,1,6, +2,1,6,3,26,5,6,4,10,8,8,1581809,2,1592601,12,1, +6,1,10,28,34,63,38,3,26,1639579,16,1639583,14,1639409,14,1622243, +12,8,1581797,2,1592341,12,1,6,1,10,28,34,63,38,3,26, +1639705,16,1639697,14,101,120,112,101,99,116,101,100,32,40,32,97, +102,116,101,114,32,116,104,101,32,116,121,112,101,32,97,114, +103,117,109,101,110,116,115,0,1639625,1581991,12,8,1581797,2,1592341,12, +1639735,16,1310721,2,5,26,2,1635273,12,1639809,14,1310721,2,5,26,2, +1585301,12,8,1559041,1310721,2,5,26,2,26,2,1639801,16,1581889,1559041,1310721, +2,5,26,2,26,2,10,3,6,10,4,8,1,1639809,14,1, +1310721,2,9,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,3, +26,4,1310721,2,4,1581785,2,1,6,1,10,28,34,63,38,3, +26,1639967,16,1639959,14,43,43,32,97,110,100,32,45,45,32,110, +101,101,100,32,115,111,109,101,116,104,105,110,103,32,116,111, +32,97,115,115,105,103,110,32,116,111,0,1639883,1581991,12,8,1581787, +2,3,28,1,6,1,10,28,34,63,38,3,26,1640029,16,1581763, +2,1,6,1,10,28,34,63,38,1,10,28,1640031,14,1,1640069, +16,1581789,2,1597849,12,1,6,1,10,28,34,63,38,1,10,28, +1640071,14,1,1640171,16,1581789,2,1640163,14,111,119,110,115,32,97,110, +32,111,98,106,101,99,116,32,97,110,100,32,99,97,110,39, +116,32,98,101,32,105,110,99,114,101,109,101,110,116,101,100, +0,1640083,1582383,12,8,1581889,2,1617083,12,1640189,16,3,1640245,14,1581889,2, +1,26,2,11,28,1,6,1,10,28,34,63,38,3,26,1, +6,1,10,28,34,63,38,1,10,28,1640255,16,3,1640307,14,1581889, +2,1581877,2,28,1,6,1,10,28,34,63,38,3,26,1,6, +1,10,28,34,63,38,1,10,28,1640385,14,43,43,32,97,110, +100,32,45,45,32,110,101,101,100,32,97,32,110,117,109,98, +101,114,32,111,114,32,97,32,112,111,105,110,116,101,114,0, +1640311,1581889,2,1621475,12,8,1581785,1,10,3,6,10,4,8,1581787,1, +10,3,6,10,4,8,1581891,1,10,3,6,10,4,8,1,1584843, +12,8,7,1584907,12,8,3,1584907,12,8,1310721,2,2,1640547,16,1, +1584843,12,8,7,1584907,12,8,1310721,2,3,26,2,1584843,12,8,27, +1584907,12,8,5,1584843,12,8,7,1584907,12,8,5,1584907,12,8,1640611, +14,1310721,2,3,26,2,1584843,12,8,27,1584907,12,8,1,1584843,12, +8,7,1584907,12,8,5,1584843,12,8,7,1584907,12,8,5,1584907,12, +8,11,1584907,12,8,9,1584907,12,8,1,1640635,14,1,1310721,2,5, +26,1310721,4,18,1310721,2,11,28,1310721,4,1310721,2,4,3,1,10, +28,1310721,2,9,26,4,1591423,12,8,1310721,2,3,26,1592987,12,10, +3,6,10,4,8,1633929,12,8,1310721,2,2,3,28,1,6,1, +10,28,34,63,38,3,26,1640891,16,1640879,14,117,115,101,32,97, +32,102,105,101,108,100,32,111,102,32,97,32,110,101,119,32, +111,98,106,101,99,116,32,111,110,108,121,32,97,102,116,101, +114,32,98,105,110,100,105,110,103,32,105,116,32,116,111,32, +97,32,110,97,109,101,0,1640759,1582287,12,8,1640955,14,1310721,2,2, +5,40,3,36,1640955,16,1310721,2,2,5,28,1597925,12,8,1310721,2, +9,26,1310721,2,2,5,28,10,3,6,10,4,8,1310721,2,5, +26,1581889,2,10,3,6,10,4,8,1310721,2,5,26,2,1,26, +2,11,28,1,6,1,10,28,34,63,38,3,26,1641059,16,1310721, +2,5,26,1310721,2,5,26,2,9,26,2,10,3,6,10,4, +8,1310721,2,5,26,2,1,26,2,17,28,1,6,1,10,28, +34,63,38,1,10,28,1641319,16,1641129,14,122,99,58,32,108,105, +110,101,32,0,1641109,1573231,12,8,1579523,2,1573371,12,8,1641159,14,58, +32,46,0,1641151,1573231,12,8,1310721,2,3,26,2,1573231,12,8,1641211, +14,58,32,116,104,97,116,39,115,32,97,32,0,1641187,1573231,12, +8,1581889,2,1619863,12,8,1641305,14,44,32,110,111,116,32,97,32, +115,116,114,117,99,116,32,111,114,32,97,32,112,111,105,110, +116,101,114,32,116,111,32,111,110,101,10,0,1641233,1573231,12,8, +1573195,12,8,1310721,2,7,26,1310721,2,5,26,2,1310721,2,3,26, +2,1627259,12,10,3,6,10,4,8,1310721,2,7,26,2,1,40, +1641529,16,1641405,14,122,99,58,32,108,105,110,101,32,0,1641385,1573231, +12,8,1579523,2,1573371,12,8,1641433,14,58,32,0,1641427,1573231,12,8, +1310721,2,5,26,2,1619863,12,8,1641491,14,32,104,97,115,32,110, +111,32,102,105,101,108,100,32,0,1641461,1573231,12,8,1310721,2,3, +26,2,1573231,12,8,21,1573079,12,8,1573195,12,8,1310721,2,5,26, +2,9,26,2,1310721,2,7,26,2,7,30,5,26,26,2,1641623, +16,1310721,2,5,26,2,9,26,2,1310721,2,7,26,2,7,30, +5,26,26,2,1584843,12,8,27,1584907,12,8,1581889,1310721,2,5,26, +2,9,26,2,1310721,2,7,26,2,7,30,3,26,26,2,10, +3,6,10,4,8,1581785,3,10,3,6,10,4,8,1581891,1,10, +3,6,10,4,8,1310721,2,9,26,2,1,40,3,36,1641773,16, +1581787,7,10,3,6,10,4,8,1581789,1310721,2,9,26,2,10,3, +6,10,4,8,1641789,14,1581787,9,10,3,6,10,4,8,1,1641797, +14,1,1310721,2,11,26,1310721,4,18,1310721,2,29,28,1310721,4,1637101, +12,1310721,2,4,3,1643751,16,1579517,2,5,28,1,6,1,10,28, +34,63,38,3,26,1641927,16,1579519,2,1581845,2,28,1,6,1,10, +28,34,63,38,3,26,1,6,1,10,28,34,63,38,1,10, +28,1641929,14,1,1642013,16,1591423,12,8,1310721,2,7,26,1622323,12,10, +3,6,10,4,8,1633929,12,8,1581889,1310721,2,7,26,2,10,3, +6,10,4,8,1581787,1,10,3,6,10,4,8,1641833,14,1579517,2, +9,28,1,6,1,10,28,34,63,38,1,10,28,1642057,16,1310721, +2,2,1643763,14,1579519,2,1581805,2,28,1,6,1,10,28,34,63, +38,3,26,1642971,16,1591423,12,8,1633929,12,8,1310721,2,3,26,3, +1,10,28,10,3,6,10,4,8,1310721,2,2,3,28,1,6, +1,10,28,34,63,38,3,26,1642283,16,1642271,14,105,110,100,101, +120,32,97,32,110,101,119,32,111,98,106,101,99,116,32,111, +110,108,121,32,97,102,116,101,114,32,98,105,110,100,105,110, +103,32,105,116,32,116,111,32,97,32,110,97,109,101,0,1642169, +1582287,12,8,1642347,14,1310721,2,2,5,40,3,36,1642347,16,1310721,2, +2,5,28,1597925,12,8,1310721,2,3,26,1310721,2,2,5,28,10, +3,6,10,4,8,1581889,2,1617435,12,1642365,16,3,1642417,14,1581889,2, +1581877,2,28,1,6,1,10,28,34,63,38,3,26,1,6,1, +10,28,34,63,38,1,10,28,1642501,14,111,110,108,121,32,112, +111,105,110,116,101,114,115,32,97,110,100,32,97,114,114,97, +121,115,32,99,97,110,32,98,101,32,105,110,100,101,120,101, +100,0,1642421,1581889,2,1621475,12,8,1310721,2,9,26,1581889,2,1617681,12, +10,3,6,10,4,8,1310721,2,1646495,12,10,3,6,10,4,8, +1633929,12,8,1310721,2,2,1598391,12,8,1581889,2,1617083,12,1642597,16,3, +1642649,14,1581889,2,1581877,2,28,1,6,1,10,28,34,63,38,3, +26,1,6,1,10,28,34,63,38,1,10,28,1642709,14,97,110, +32,105,110,100,101,120,32,104,97,115,32,116,111,32,98,101, +32,97,32,110,117,109,98,101,114,0,1642653,1581889,2,1621475,12,8, +1581807,2,1592809,12,8,1310721,2,9,26,2,1615957,12,3,28,1,6, +1,10,28,34,63,38,1,10,28,1642803,16,1310721,2,9,26,2, +1615957,12,1584843,12,8,31,1584907,12,8,27,1584907,12,8,1581785,3,10, +3,6,10,4,8,1581889,1310721,2,9,26,2,10,3,6,10,4, +8,1581891,1,10,3,6,10,4,8,1310721,2,3,26,2,1,40, +3,36,1642933,16,1581787,7,10,3,6,10,4,8,1581789,1310721,2,3, +26,2,10,3,6,10,4,8,1642949,14,1581787,9,10,3,6,10, +4,8,1310721,2,1,10,3,6,10,4,8,1641833,14,1579519,2,1581835, +2,28,1,6,1,10,28,34,63,38,3,26,1643011,16,3,1643063, +14,1579519,2,1581815,2,28,1,6,1,10,28,34,63,38,3,26, +1,6,1,10,28,34,63,38,1,10,28,1643097,16,1310721,2,1310721, +2,2,1640649,12,10,3,6,10,4,8,1641833,14,1579519,2,1581797,2, +28,1,6,1,10,28,34,63,38,3,26,1643595,16,1633929,12,8, +1310721,2,2,1598391,12,8,1310721,2,11,26,1,10,3,6,10,4, +8,1581889,2,1,26,2,15,28,1,6,1,10,28,34,63,38, +3,26,1643237,16,1310721,2,11,26,1581889,2,10,3,6,10,4,8, +1643341,14,1581889,2,1581877,2,28,1,6,1,10,28,34,63,38,3, +26,1643329,14,111,110,108,121,32,102,117,110,99,116,105,111,110, +115,32,99,97,110,32,98,101,32,99,97,108,108,101,100,0, +1643271,1581889,2,1621475,12,8,1310721,2,13,26,1,17,1574329,12,8,1310721, +2,5,26,1,1310721,2,11,26,2,1310721,2,13,26,1634431,12,10, +3,6,10,4,8,1310721,2,5,26,2,1584843,12,8,7,1584907,12, +8,13,1584907,12,8,11,1584907,12,8,9,1584907,12,8,1581785,1,10, +3,6,10,4,8,1581787,1,10,3,6,10,4,8,1310721,2,1, +10,3,6,10,4,8,1581891,1,10,3,6,10,4,8,1581889,1581877, +2,10,3,6,10,4,8,1310721,2,11,26,2,1643591,16,1581889,1310721, +2,11,26,2,9,26,2,1310721,2,13,26,1616107,12,10,3,6, +10,4,8,1641833,14,1579519,2,1581817,2,28,1,6,1,10,28,34, +63,38,3,26,1643663,16,1591423,12,8,1310721,2,3,3,1639823,12,10, +3,6,10,4,8,1641833,14,1579519,2,1581819,2,28,1,6,1,10, +28,34,63,38,3,26,1643737,16,1591423,12,8,1310721,2,3,3,1, +10,28,1639823,12,10,3,6,10,4,8,1641833,14,1310721,2,2,1643763, +14,1641833,14,1310721,2,2,1643763,14,1,1310721,2,29,26,1310721,4,18, +1310721,2,7,28,1310721,4,1579517,2,5,28,1,6,1,10,28,34, +63,38,3,26,1644001,16,1579519,2,1581855,2,28,1,6,1,10,28, +34,63,38,3,26,1643861,16,3,1643913,14,1579519,2,1581851,2,28,1, +6,1,10,28,34,63,38,3,26,1,6,1,10,28,34,63, +38,1,10,28,1643923,16,3,1643975,14,1579519,2,1581853,2,28,1,6, +1,10,28,34,63,38,3,26,1,6,1,10,28,34,63,38, +1,10,28,1,6,1,10,28,34,63,38,1,10,28,1644003,14, +1,1644223,16,1310721,2,5,26,3,10,3,6,10,4,8,1579519,2, +1581855,2,28,1,6,1,10,28,34,63,38,3,26,1644085,16,1310721, +2,5,26,5,10,3,6,10,4,8,1579519,2,1581853,2,28,1, +6,1,10,28,34,63,38,3,26,1644141,16,1310721,2,5,26,7, +10,3,6,10,4,8,1591423,12,8,1581821,2,1592809,12,8,1310721,2, +1643777,12,10,3,6,10,4,8,1633929,12,8,1581787,1,10,3,6, +10,4,8,1310721,2,2,1310721,2,5,26,2,1603487,12,1645419,14,1579517, +2,9,28,1,6,1,10,28,34,63,38,3,26,1645409,16,1579519, +2,1581821,2,28,1,6,1,10,28,34,63,38,3,26,1644589,16, +1591423,12,8,1310721,2,1643777,12,10,3,6,10,4,8,1633929,12,8, +1310721,2,2,1598391,12,8,1581889,2,1,26,2,11,28,1,6,1, +10,28,34,63,38,3,26,1644377,16,3,1644429,14,1581889,2,1581877,2, +28,1,6,1,10,28,34,63,38,3,26,1,6,1,10,28, +34,63,38,1,10,28,1644501,14,111,110,108,121,32,112,111,105, +110,116,101,114,115,32,99,97,110,32,98,101,32,100,101,114, +101,102,101,114,101,110,99,101,100,0,1644433,1581889,2,1621475,12,8, +1581889,1581889,2,1617681,12,10,3,6,10,4,8,1581785,3,10,3,6, +10,4,8,1581787,9,10,3,6,10,4,8,1581891,1,10,3,6, +10,4,8,1,1645419,14,1579519,2,1581823,2,28,1,6,1,10,28, +34,63,38,3,26,1644919,16,1591423,12,8,1310721,2,1643777,12,10,3, +6,10,4,8,1581785,2,1,6,1,10,28,34,63,38,3,26, +1644731,16,1581889,2,1617293,12,1,6,1,10,28,34,63,38,3,26, +1,6,1,10,28,34,63,38,1,10,28,1644733,14,1,1644809,16, +1644801,14,38,32,110,101,101,100,115,32,115,111,109,101,116,104, +105,110,103,32,97,100,100,114,101,115,115,97,98,108,101,0, +1644741,1581991,12,8,1310721,2,2,5,40,3,36,1644843,16,1310721,2,2, +5,28,1597925,12,8,1581785,1,10,3,6,10,4,8,1581787,1,10, +3,6,10,4,8,1581891,1,10,3,6,10,4,8,1581889,1581889,2, +1611981,12,10,3,6,10,4,8,1,1645419,14,1579519,2,1581817,2,28, +1,6,1,10,28,34,63,38,3,26,1644977,16,1591423,12,8,1643777, +12,8,1,3,1639823,12,1645419,14,1579519,2,1581819,2,28,1,6,1, +10,28,34,63,38,3,26,1645041,16,1591423,12,8,1643777,12,8,1, +3,1,10,28,1639823,12,1645419,14,1310721,2,3,26,1579519,2,1595289,12, +10,3,6,10,4,8,1310721,2,3,26,2,1,40,3,36,1645409, +16,1591423,12,8,1310721,2,1643777,12,10,3,6,10,4,8,1633929,12, +8,1310721,2,2,1598391,12,8,1547329,1310721,2,3,26,2,26,2,1547393, +1310721,2,3,26,2,26,2,1594417,12,8,1310721,2,3,26,2,3, +10,40,3,36,1645219,16,1581889,1581883,2,10,3,6,10,4,8,1645355, +14,1581889,2,1581877,2,28,1,6,1,10,28,34,63,38,1,10, +28,1645355,16,1581889,2,1617083,12,1645325,14,116,104,105,115,32,111,112, +101,114,97,116,111,114,32,110,101,101,100,115,32,97,32,110, +117,109,98,101,114,0,1645267,1581889,2,1621475,12,8,1581889,1581879,2,10, +3,6,10,4,8,1581785,1,10,3,6,10,4,8,1581787,1,10, +3,6,10,4,8,1581891,1,10,3,6,10,4,8,1,1645419,14, +1641811,12,1645419,14,1,1310721,2,7,26,1310721,4,18,1310721,2,15,28, +1310721,4,1310721,2,4,1643777,12,1310721,2,3,26,4,1579517,2,9,28, +1,6,1,10,28,34,63,38,3,26,1646391,16,1310721,2,5,26, +1579519,2,1595115,12,10,3,6,10,4,8,1310721,2,5,26,2,1, +40,1645557,16,1310721,2,3,26,2,1646407,14,1310721,2,7,26,1546881,1310721, +2,5,26,2,26,2,10,3,6,10,4,8,1310721,2,7,26, +2,1310721,2,2,40,1645629,16,1310721,2,3,26,2,1646407,14,1591423,12, +8,1633929,12,8,1310721,2,3,26,2,1598391,12,8,1310721,2,13,26, +1581889,2,10,3,6,10,4,8,1547137,1310721,2,5,26,2,26,2, +3,1,10,28,28,1,6,1,10,28,34,63,38,3,26,1645929, +16,1310721,2,9,26,1585141,12,10,3,6,10,4,8,17,1584907,12, +8,1310721,2,3,26,1310721,2,7,26,2,3,26,1645433,12,10,3, +6,10,4,8,1633929,12,8,1310721,2,3,26,2,1598391,12,8,1547329, +3,26,2,1547393,3,26,2,1594417,12,8,1310721,2,11,26,1585141,12, +10,3,6,10,4,8,15,1584907,12,8,1310721,2,9,26,2,1581747, +2,1584967,12,8,1,1584843,12,8,1310721,2,11,26,2,1581747,2,1584967, +12,8,1646275,14,1547137,1310721,2,5,26,2,26,2,5,1,10,28, +28,1,6,1,10,28,34,63,38,3,26,1646177,16,1310721,2,9, +26,1585141,12,10,3,6,10,4,8,17,1584907,12,8,3,1584843,12, +8,1310721,2,11,26,1585141,12,10,3,6,10,4,8,15,1584907,12, +8,1310721,2,9,26,2,1581747,2,1584967,12,8,1310721,2,3,26,1310721, +2,7,26,2,3,26,1645433,12,10,3,6,10,4,8,1633929,12, +8,1310721,2,3,26,2,1598391,12,8,1547329,3,26,2,1547393,3,26, +2,1594417,12,8,1310721,2,11,26,2,1581747,2,1584967,12,8,1646275,14, +1310721,2,3,26,1310721,2,7,26,2,3,26,1645433,12,10,3,6, +10,4,8,1633929,12,8,1310721,2,3,26,2,1598391,12,8,1547009,1310721, +2,5,26,2,26,2,1547137,1310721,2,5,26,2,26,2,1594417,12, +8,1581889,1310721,2,5,26,2,1310721,2,13,26,2,1581889,2,1629287,12, +10,3,6,10,4,8,1581785,1,10,3,6,10,4,8,1581787,1, +10,3,6,10,4,8,1310721,2,3,26,1,10,3,6,10,4, +8,1581891,1,10,3,6,10,4,8,1645465,14,1310721,2,3,26,2, +1646407,14,1,1310721,2,15,26,1310721,4,18,1310721,2,1,28,1310721,4, +11,1584907,12,8,3,1584843,12,8,7,1584907,12,8,11,1584907,12,8, +5,1584907,12,8,1,1646481,14,1,1310721,2,1,26,1310721,4,18,1310721, +2,13,28,1310721,4,3,1645433,12,1310721,2,4,1579517,2,9,28,1, +6,1,10,28,34,63,38,1,10,28,1646563,16,1310721,2,2,1647957, +14,1579519,2,1581813,2,28,1,6,1,10,28,34,63,38,3,26, +1647155,16,1581785,2,1,6,1,10,28,34,63,38,3,26,1646689,16, +1646681,14,116,104,105,115,32,99,97,110,39,116,32,98,101,32, +97,115,115,105,103,110,101,100,32,116,111,0,1646629,1581991,12,8, +1310721,2,3,26,1581787,2,10,3,6,10,4,8,1310721,2,5,26, +1581789,2,10,3,6,10,4,8,1310721,2,11,26,1581889,2,10,3, +6,10,4,8,1310721,2,11,26,2,1617293,12,1646913,16,1646905,14,97, +32,119,104,111,108,101,32,115,116,114,117,99,116,32,111,114, +32,97,114,114,97,121,32,99,97,110,39,116,32,98,101,32, +97,115,115,105,103,110,101,100,59,32,97,115,115,105,103,110, +32,105,116,115,32,102,105,101,108,100,115,0,1646783,1581991,12,8, +1591423,12,8,1310721,2,9,26,1646495,12,10,3,6,10,4,8,1633929, +12,8,1310721,2,11,26,2,1581889,2,1617847,12,1,6,1,10,28, +34,63,38,3,26,1647039,16,1647017,14,97,115,115,105,103,110,109, +101,110,116,0,1646995,1310721,2,11,26,2,1581889,2,1621085,12,8,1646421, +12,8,1581785,1,10,3,6,10,4,8,1581787,1,10,3,6,10, +4,8,1581889,1310721,2,11,26,2,10,3,6,10,4,8,1581891,1, +10,3,6,10,4,8,1310721,2,3,26,2,1310721,2,5,26,2, +1310721,2,9,26,2,1606123,12,1647957,14,1310721,2,7,26,1579519,2,1595463, +12,10,3,6,10,4,8,1310721,2,7,26,2,1,40,3,36, +1647945,16,1581785,2,1,6,1,10,28,34,63,38,3,26,1647297,16, +1647289,14,116,104,105,115,32,99,97,110,39,116,32,98,101,32, +97,115,115,105,103,110,101,100,32,116,111,0,1647237,1581991,12,8, +1581787,2,3,28,1,6,1,10,28,34,63,38,3,26,1647359,16, +1581763,2,1,6,1,10,28,34,63,38,1,10,28,1647361,14,1, +1647399,16,1581789,2,1597849,12,1,6,1,10,28,34,63,38,1,10, +28,1647401,14,1,1647521,16,1581789,2,1647513,14,111,119,110,115,32,97, +110,32,111,98,106,101,99,116,59,32,99,111,109,112,111,117, +110,100,32,97,115,115,105,103,110,109,101,110,116,32,119,111, +117,108,100,32,108,111,115,101,32,105,116,0,1647413,1582383,12,8, +1310721,2,11,26,1581889,2,10,3,6,10,4,8,1591423,12,8,1, +1584843,12,8,7,1584907,12,8,3,1584907,12,8,1581785,1,10,3,6, +10,4,8,1310721,2,9,26,1646495,12,10,3,6,10,4,8,1633929, +12,8,1310721,2,9,26,2,1598391,12,8,1547009,1547585,1310721,2,7,26, +2,26,2,26,2,1547137,1547585,1310721,2,7,26,2,26,2,26,2, +1594417,12,8,1310721,2,3,26,1547585,1310721,2,7,26,2,26,2,1310721, +2,11,26,2,1581889,2,1629287,12,10,3,6,10,4,8,1310721,2, +11,26,2,1310721,2,3,26,2,1617847,12,1,6,1,10,28,34, +63,38,3,26,1647861,16,1647833,14,99,111,109,112,111,117,110,100, +32,97,115,115,105,103,110,109,101,110,116,0,1647793,1310721,2,11, +26,2,1310721,2,3,26,2,1621085,12,8,1646421,12,8,1581785,1,10, +3,6,10,4,8,1581787,1,10,3,6,10,4,8,1581889,1310721,2, +11,26,2,10,3,6,10,4,8,1581891,1,10,3,6,10,4, +8,1,1647957,14,1310721,2,2,1647957,14,1,1310721,2,13,26,1310721,4, +18,1310721,2,3,28,1310721,4,1579517,2,9,28,1,6,1,10,28, +34,63,38,3,26,1648147,16,1579519,2,1581811,2,28,1,6,1,10, +28,34,63,38,3,26,1648055,16,3,1648107,14,1579519,2,1581801,2,28, +1,6,1,10,28,34,63,38,3,26,1,6,1,10,28,34, +63,38,1,10,28,1648117,16,3,1648143,14,1624179,12,1,6,1,10, +28,34,63,38,1,10,28,1648429,14,1579517,2,5,28,1,6,1, +10,28,34,63,38,1,10,28,1648187,16,3,1648225,14,1579519,2,1597111, +12,1,40,3,36,1,6,1,10,28,34,63,38,1,10,28, +1648235,16,1,1648429,14,1624179,12,1648249,16,3,1648429,14,1310721,2,1579519,2, +1583635,12,10,3,6,10,4,8,1310721,2,2,1,40,3,36,1648353, +16,1417729,1310721,2,2,26,2,11,28,1,6,1,10,28,34,63, +38,3,26,1,6,1,10,28,34,63,38,1,10,28,1648355,14, +1,1648421,16,1442305,1310721,2,2,26,2,33,32,1,6,1,10,28, +34,63,38,3,26,1,6,1,10,28,34,63,38,1,10,28, +1648423,14,1,1648429,14,1,1310721,2,3,26,1310721,4,18,1310721,2,9, +28,1310721,4,1581755,2,1310721,2,4,1,1310721,2,3,26,4,1,1310721, +2,5,26,4,1581879,2,1310721,2,7,26,4,1591423,12,8,1581759,1, +6,2,1,6,3,26,5,6,4,10,8,8,1581769,1,6,2, +1,6,3,26,5,6,4,10,8,8,1581803,2,1592341,12,1,6, +1,10,28,34,63,38,3,26,1648937,16,1579517,2,1,28,1,6, +1,10,28,34,63,38,3,26,1648661,16,1648653,14,109,105,115,115, +105,110,103,32,125,0,1648633,1581991,12,8,1647971,12,1648679,16,1650659,12, +8,1648565,14,1310721,2,3,26,1646495,12,10,3,6,10,4,8,1633929, +12,8,1581811,2,1592601,12,1648771,16,9,1584907,12,8,1310721,2,3,26, +2,1611239,12,8,1310721,2,3,26,1,10,3,6,10,4,8,1648565, +14,1581803,2,1592341,12,1,6,1,10,28,34,63,38,3,26,1648887, +16,1648879,14,101,120,112,101,99,116,101,100,32,59,32,111,114, +32,125,32,97,102,116,101,114,32,97,110,32,101,120,112,114, +101,115,115,105,111,110,0,1648807,1581991,12,8,1310721,2,5,26,3, +10,3,6,10,4,8,1310721,2,7,26,1581889,2,10,3,6,10, +4,8,1648565,14,1591423,12,8,1310721,2,5,26,2,1648995,16,1310721,2, +3,26,1310721,2,3,26,2,1610545,12,10,3,6,10,4,8,1649003, +14,1,1584843,12,8,1310721,2,2,1599715,12,8,1581759,1,6,2,1, +6,-1,26,5,6,4,10,8,8,1581769,1,6,2,1,6,-1, +26,5,6,4,10,8,8,1581785,1,10,3,6,10,4,8,1581787, +1,10,3,6,10,4,8,1581889,1310721,2,7,26,2,10,3,6, +10,4,8,1581891,1,10,3,6,10,4,8,1310721,2,3,26,2, +1649159,14,1,1310721,2,9,26,1310721,4,18,1310721,2,5,28,1310721,4, +1579517,2,3,28,1,6,1,10,28,34,63,38,3,26,1649253,16, +1310721,2,1579519,2,10,3,6,10,4,8,1591423,12,8,1310721,2,2, +1649953,14,1581829,2,1592601,12,1649277,16,1,1649173,12,28,1649953,14,1579517,2, +9,28,1,6,1,10,28,34,63,38,3,26,1649369,16,1579519,2, +1547265,5,26,2,28,1,6,1,10,28,34,63,38,3,26,1, +6,1,10,28,34,63,38,1,10,28,1649371,14,1,1649393,16,1591423, +12,8,1649173,12,-1,36,1649953,14,1581797,2,1592601,12,1649445,16,1310721,2, +1649967,12,10,3,6,10,4,8,1581799,2,1592809,12,8,1310721,2,2, +1649953,14,1579517,2,5,28,1,6,1,10,28,34,63,38,3,26, +1649893,16,1310721,2,3,26,1579519,2,1583635,12,10,3,6,10,4,8, +1310721,2,3,26,2,1,40,3,36,1649861,16,1417729,1310721,2,3,26, +2,26,2,9,28,1,6,1,10,28,34,63,38,3,26,1649577, +16,3,1649639,14,1417729,1310721,2,3,26,2,26,2,7,28,1,6, +1,10,28,34,63,38,3,26,1,6,1,10,28,34,63,38, +1,10,28,1649649,16,3,1649763,14,1417729,1310721,2,3,26,2,26,2, +3,28,1,6,1,10,28,34,63,38,3,26,1649739,16,1442305,1310721, +2,3,26,2,26,2,3,32,1,6,1,10,28,34,63,38, +1,10,28,1649741,14,1,1,6,1,10,28,34,63,38,1,10, +28,1649773,16,3,1649835,14,1417729,1310721,2,3,26,2,26,2,5,28, +1,6,1,10,28,34,63,38,3,26,1,6,1,10,28,34, +63,38,1,10,28,1,6,1,10,28,34,63,38,1,10,28, +1649863,14,1,1649893,16,1591423,12,8,1425921,1310721,2,3,26,2,26,2, +1649953,14,1649937,14,101,120,112,101,99,116,101,100,32,97,32,99, +111,110,115,116,97,110,116,0,1649897,1581991,12,8,1,1649953,14,1, +1310721,2,5,26,1310721,4,18,1310721,2,5,28,1310721,4,1649173,12,1310721, +2,4,1579517,2,9,28,1,6,1,10,28,34,63,38,3,26, +1650633,16,1310721,2,3,26,1579519,2,10,3,6,10,4,8,1310721,2, +3,26,2,1546753,7,26,2,28,1,6,1,10,28,34,63,38, +3,26,1650127,16,1591423,12,8,1310721,2,1310721,2,2,1649173,12,26,10, +3,6,10,4,8,1650629,14,1310721,2,3,26,2,1546753,9,26,2, +28,1,6,1,10,28,34,63,38,3,26,1650209,16,1591423,12,8, +1310721,2,1310721,2,2,1649173,12,28,10,3,6,10,4,8,1650629,14, +1310721,2,3,26,2,1546753,1,26,2,28,1,6,1,10,28,34, +63,38,3,26,1650291,16,1591423,12,8,1310721,2,1310721,2,2,1649173,12, +30,10,3,6,10,4,8,1650629,14,1310721,2,3,26,2,1546753,31, +26,2,28,1,6,1,10,28,34,63,38,3,26,1650373,16,1591423, +12,8,1310721,2,1310721,2,2,1649173,12,34,10,3,6,10,4,8, +1650629,14,1310721,2,3,26,2,1546753,27,26,2,28,1,6,1,10, +28,34,63,38,3,26,1650455,16,1591423,12,8,1310721,2,1310721,2,2, +1649173,12,32,10,3,6,10,4,8,1650629,14,1310721,2,3,26,2, +1546753,11,26,2,28,1,6,1,10,28,34,63,38,3,26,1650537, +16,1591423,12,8,1310721,2,1310721,2,2,1649173,12,48,10,3,6,10, +4,8,1650629,14,1310721,2,3,26,2,1546753,13,26,2,28,1,6, +1,10,28,34,63,38,3,26,1650619,16,1591423,12,8,1310721,2,1310721, +2,2,1649173,12,38,10,3,6,10,4,8,1650629,14,1310721,2,2, +1650645,14,1649989,14,1310721,2,2,1650645,14,1,1310721,2,5,26,1310721,4, +18,1310721,2,5,28,1310721,4,1579517,2,5,28,1,6,1,10,28, +34,63,38,3,26,1650741,16,1579519,2,1597111,12,1,40,1,6,1, +10,28,34,63,38,1,10,28,1650743,14,1,1650925,16,1310721,2,1579519, +2,1583635,12,10,3,6,10,4,8,1310721,2,2,1,40,3,36, +1650851,16,1417729,1310721,2,2,26,2,11,28,1,6,1,10,28,34, +63,38,3,26,1,6,1,10,28,34,63,38,1,10,28,1650853, +14,1,1650899,16,1442305,1310721,2,2,26,2,17,32,1,6,1,10, +28,34,63,38,1,10,28,1650901,14,1,1650925,16,1310721,2,2,1, +1634027,12,8,1,1651143,14,1624179,12,1650947,16,3,1651529,12,8,1,1651143, +14,1579517,2,9,28,1,6,1,10,28,34,63,38,3,26,1651071, +16,1579519,2,1581811,2,28,1,6,1,10,28,34,63,38,3,26, +1651025,16,1591423,12,8,1,1651143,14,1579519,2,1581801,2,28,1,6,1, +10,28,34,63,38,3,26,1651071,16,1651157,12,8,1,1651143,14,1310721, +2,3,26,1646495,12,10,3,6,10,4,8,1633929,12,8,9,1584907, +12,8,1581811,2,1592809,12,8,1310721,2,3,26,2,1611239,12,8,1, +1651143,14,1,1310721,2,5,26,1310721,4,18,1310721,2,3,28,1310721,4, +1581755,2,1310721,2,4,1581801,2,1592809,12,8,1581759,1,6,2,1,6, +3,26,5,6,4,10,8,8,1581803,2,1592341,12,1,6,1,10, +28,34,63,38,3,26,1651323,16,1579517,2,1,28,1,6,1,10, +28,34,63,38,3,26,1651313,16,1651305,14,109,105,115,115,105,110, +103,32,125,0,1651285,1581991,12,8,1650659,12,8,1651217,14,1591423,12,8, +1310721,2,2,1599715,12,8,1581759,1,6,2,1,6,-1,26,5,6, +4,10,8,8,1,1651377,14,1,1310721,2,3,26,1310721,4,18,1310721, +2,3,28,1310721,4,1581755,2,1310721,2,4,1581801,2,1592341,12,1651433,16, +1651157,12,1651515,14,1581759,1,6,2,1,6,3,26,5,6,4,10, +8,8,1650659,12,8,1310721,2,2,1599715,12,8,1581759,1,6,2,1, +6,-1,26,5,6,4,10,8,8,1,1651515,14,1,1310721,2,3, +26,1310721,4,18,1310721,2,15,28,1310721,4,1310721,2,4,1,1310721,2, +11,26,4,1,1310721,2,13,26,4,1310721,2,2,1651737,16,1624757,12, +8,1310721,2,11,26,1581913,2,10,3,6,10,4,8,1310721,2,13, +26,1581915,2,10,3,6,10,4,8,1310721,2,11,26,2,1581885,2, +28,1,6,1,10,28,34,63,38,3,26,1651737,16,1651729,14,97, +32,118,97,114,105,97,98,108,101,32,99,97,110,39,116,32, +98,101,32,118,111,105,100,0,1651679,1581991,12,8,3,1652745,16,1310721, +2,3,26,1592987,12,10,3,6,10,4,8,1310721,2,5,26,1310721, +2,3,26,2,1597293,12,10,3,6,10,4,8,1558017,1310721,2,5, +26,2,26,1310721,2,13,26,2,10,3,6,10,4,8,1581805,2, +1592601,12,1652063,16,1310721,2,9,26,1649967,12,10,3,6,10,4,8, +1581807,2,1592809,12,8,1310721,2,11,26,2,1,6,1,10,28,34, +63,38,3,26,1651941,16,1310721,2,11,26,1581877,2,10,3,6,10, +4,8,1473025,1310721,2,5,26,2,26,3,10,3,6,10,4,8, +1556993,1310721,2,5,26,2,26,1310721,2,11,26,2,1310721,2,9,26, +2,1612333,12,10,3,6,10,4,8,1581761,1581761,2,1556993,1310721,2,5, +26,2,26,2,1615957,12,26,10,3,6,10,4,8,1652303,14,1310721, +2,11,26,2,1652117,16,1310721,2,11,26,2,1617293,12,1,6,1, +10,28,34,63,38,1,10,28,1652119,14,1,1652225,16,1473025,1310721,2, +5,26,2,26,3,10,3,6,10,4,8,1556993,1310721,2,5,26, +2,26,1310721,2,11,26,2,10,3,6,10,4,8,1581761,1581761,2, +1310721,2,11,26,2,1615957,12,26,10,3,6,10,4,8,1652303,14, +1581761,1,6,2,1,6,3,26,5,6,4,10,8,8,1310721,2, +11,26,2,1652303,16,1556993,1310721,2,5,26,2,26,1310721,2,11,26, +2,10,3,6,10,4,8,1581813,2,1592601,12,1652705,16,1473025,1310721,2, +5,26,2,26,2,1652445,16,1652437,14,97,110,32,97,114,114,97, +121,32,111,114,32,97,32,115,116,114,117,99,116,32,99,97, +110,39,116,32,98,101,32,105,110,105,116,105,97,108,105,122, +101,100,32,119,105,116,104,32,61,0,1652339,1581991,12,8,1310721,2, +7,26,1646495,12,10,3,6,10,4,8,1633929,12,8,1467905,1310721,2, +5,26,2,26,2,1585585,12,8,5,1584907,12,8,1310721,2,11,26, +2,1652615,16,1310721,2,11,26,2,1581889,2,1617847,12,1,6,1,10, +28,34,63,38,3,26,1652611,16,1652589,14,105,110,105,116,105,97, +108,105,122,101,114,0,1652565,1310721,2,11,26,2,1581889,2,1621085,12, +8,1652677,14,1581889,2,1617083,12,1,6,1,10,28,34,63,38,3, +26,1652677,16,1556993,1310721,2,5,26,2,26,1581889,2,10,3,6,10, +4,8,3,1310721,2,5,26,2,1310721,2,7,26,2,1606123,12,8, +1581809,2,1592601,12,1,6,1,10,28,34,63,38,3,26,1652741,16, +1652745,14,1651737,14,1581811,2,1592809,12,8,1,1652763,14,1,1310721,2,15, +26,1310721,4,18,1310721,2,1,28,1310721,4,1634297,12,8,1591423,12,8, +1,1651529,12,1652813,14,1,1310721,2,1,26,1310721,4,18,1310721,2,15, +28,1310721,4,1634297,12,8,1591423,12,8,1581797,2,1592809,12,8,1310721,2, +5,26,1646495,12,10,3,6,10,4,8,1633929,12,8,1310721,2,5, +26,2,1598391,12,8,1581799,2,1592809,12,8,1310721,2,1585141,12,10,3, +6,10,4,8,17,1584907,12,8,1310721,2,11,26,1581767,2,10,3, +6,10,4,8,1310721,2,7,26,1599965,12,10,3,6,10,4,8, +1651391,12,8,1310721,2,13,26,1581767,2,10,3,6,10,4,8,1310721, +2,9,26,1599965,12,10,3,6,10,4,8,1581843,2,1592471,12,1653183, +16,1591423,12,8,1310721,2,3,26,1585141,12,10,3,6,10,4,8, +15,1584907,12,8,1310721,2,2,1581747,2,1584967,12,8,1310721,2,7,26, +2,1600553,12,8,1581767,1310721,2,11,26,2,10,3,6,10,4,8, +1651391,12,8,1310721,2,3,26,2,1581747,2,1584967,12,8,1653239,14,1310721, +2,2,1581747,2,1584967,12,8,1310721,2,7,26,2,1600553,12,8,1581767, +1310721,2,11,26,2,10,3,6,10,4,8,1310721,2,9,26,2, +1310721,2,13,26,2,1602663,12,8,1310721,2,7,26,2,1600493,12,8, +1,1653289,14,1,1310721,2,15,26,1310721,4,18,1310721,2,7,28,1310721, +4,1310721,2,3,26,4,1310721,2,4,1552897,1581777,2,13,30,26,1310721, +2,5,26,4,1581777,2,121,40,3,36,1653429,16,1653421,14,108,111, +111,112,115,32,110,101,115,116,101,100,32,116,111,111,32,100, +101,101,112,108,121,0,1653373,1581991,12,8,1310721,2,5,26,2,1, +26,1310721,2,2,10,3,6,10,4,8,1310721,2,5,26,2,3, +26,1,10,3,6,10,4,8,1310721,2,5,26,2,5,26,1581759, +2,10,3,6,10,4,8,1310721,2,5,26,2,7,26,1310721,2, +3,26,2,10,3,6,10,4,8,1310721,2,5,26,2,9,26, +1581769,2,10,3,6,10,4,8,1581777,1,6,2,1,6,3,26, +5,6,4,10,8,8,1,1653621,14,1,1310721,2,7,26,1310721,4, +18,1310721,2,1,28,1310721,4,1581777,1,6,2,1,6,-1,26,5, +6,4,10,8,8,1552897,1581777,2,13,30,3,26,26,2,1581747,2, +1584475,12,8,1,1653711,14,1,1310721,2,1,26,1310721,4,18,1310721,2, +9,28,1310721,4,1581747,2,1310721,2,4,1634297,12,8,1591423,12,8,1310721, +2,5,26,1599965,12,10,3,6,10,4,8,1581797,2,1592809,12,8, +1310721,2,7,26,1646495,12,10,3,6,10,4,8,1633929,12,8,1310721, +2,7,26,2,1598391,12,8,1581799,2,1592809,12,8,1310721,2,3,26, +1585141,12,10,3,6,10,4,8,17,1584907,12,8,1310721,2,2,1310721, +2,5,26,2,1653303,12,8,1651391,12,8,1310721,2,5,26,2,1603063, +12,8,1310721,2,2,1585081,12,8,1310721,2,3,26,2,1581747,2,1584967, +12,8,1653635,12,8,1310721,2,5,26,2,1600553,12,8,1581767,3,10, +3,6,10,4,8,1310721,2,5,26,2,1600493,12,8,1,1654019,14, +1,1310721,2,9,26,1310721,4,18,1310721,2,15,28,1310721,4,1581755,2, +1310721,2,4,1634297,12,8,1591423,12,8,1581797,2,1592809,12,8,1581759,1, +6,2,1,6,3,26,5,6,4,10,8,8,1581839,2,1592471,12, +1654127,16,1652777,12,8,1654243,14,1624179,12,1654147,16,3,1651529,12,8,1654243, +14,1581811,2,1592341,12,1,6,1,10,28,34,63,38,3,26,1654233, +16,1310721,2,13,26,1646495,12,10,3,6,10,4,8,1633929,12,8, +9,1584907,12,8,1310721,2,13,26,2,1611239,12,8,1581811,2,1592809,12, +8,1310721,2,3,26,1599965,12,10,3,6,10,4,8,1310721,2,5, +26,1581747,2,10,3,6,10,4,8,1581811,2,1592341,12,1,6,1, +10,28,34,63,38,3,26,1654373,16,1310721,2,13,26,1646495,12,10, +3,6,10,4,8,1633929,12,8,1310721,2,13,26,2,1598391,12,8, +1654381,14,3,1584843,12,8,1581811,2,1592809,12,8,1310721,2,7,26,1585141, +12,10,3,6,10,4,8,17,1584907,12,8,1310721,2,9,26,1585141, +12,10,3,6,10,4,8,15,1584907,12,8,1310721,2,11,26,1581747, +2,10,3,6,10,4,8,1581799,2,1592341,12,1,6,1,10,28, +34,63,38,3,26,1654565,16,1310721,2,13,26,1646495,12,10,3,6, +10,4,8,1633929,12,8,9,1584907,12,8,1310721,2,13,26,2,1611239, +12,8,1581799,2,1592809,12,8,1310721,2,5,26,2,1585081,12,8,1310721, +2,9,26,2,1581747,2,1584967,12,8,1310721,2,11,26,2,1310721,2, +3,26,2,1653303,12,8,1651391,12,8,1310721,2,3,26,2,1603063,12, +8,1310721,2,11,26,2,1585081,12,8,1310721,2,7,26,2,1581747,2, +1584967,12,8,1653635,12,8,1310721,2,3,26,2,1600553,12,8,1581767,3, +10,3,6,10,4,8,1310721,2,3,26,2,1600493,12,8,1310721,2, +2,1599715,12,8,1581759,1,6,2,1,6,-1,26,5,6,4,10, +8,8,1,1654797,14,1,1310721,2,15,26,1310721,4,18,1310721,2,3, +28,1310721,4,1310721,2,4,1581777,2,1581779,2,10,40,3,36,1654927,16, +1654919,14,98,114,101,97,107,32,111,114,32,99,111,110,116,105, +110,117,101,32,111,117,116,115,105,100,101,32,97,32,108,111, +111,112,0,1654853,1581991,12,8,1310721,2,2,9,26,2,1581769,2,28, +1,6,1,10,28,34,63,38,1,10,28,1655083,16,1655075,14,98, +114,101,97,107,32,97,110,100,32,99,111,110,116,105,110,117, +101,32,99,97,110,39,116,32,108,101,97,118,101,32,97,32, +98,108,111,99,107,32,101,120,112,114,101,115,115,105,111,110, +0,1654975,1581991,12,8,1310721,2,2,7,26,2,1603063,12,8,1310721,2, +2,5,26,2,3,26,1,1598733,12,8,1,1655133,14,1,1310721,2, +3,26,1310721,4,18,1310721,2,3,28,1310721,4,1552897,1581777,2,3,28, +13,30,26,1310721,2,4,1634297,12,8,1591423,12,8,1581811,2,1592809,12, +8,1310721,2,2,1654811,12,8,1310721,2,2,3,26,1581747,2,1310721,2, +2,3,26,2,1584145,12,10,3,6,10,4,8,2097153,3,28,1584843, +12,8,15,1584907,12,8,1581767,1,10,3,6,10,4,8,1,1655301, +14,1,1310721,2,3,26,1310721,4,18,1310721,2,3,28,1310721,4,1552897, +1581777,2,3,28,13,30,26,1310721,2,4,1634297,12,8,1591423,12,8, +1581811,2,1592809,12,8,1310721,2,2,1654811,12,8,1310721,2,2,1,26, +2,1585081,12,8,1581767,1,10,3,6,10,4,8,1,1655425,14,1, +1310721,2,3,26,1310721,4,18,1310721,2,3,28,1310721,4,1634297,12,8, +1591423,12,8,1581775,2,1,6,1,10,28,34,63,38,3,26,1655555, +16,1655547,14,114,101,116,117,114,110,32,111,117,116,115,105,100, +101,32,97,32,102,117,110,99,116,105,111,110,0,1655495,1581991,12, +8,1581769,2,1655651,16,1655643,14,114,101,116,117,114,110,32,99,97, +110,39,116,32,108,101,97,118,101,32,97,32,98,108,111,99, +107,32,101,120,112,114,101,115,115,105,111,110,0,1655567,1581991,12, +8,1581811,2,1592341,12,1655693,16,1,1584843,12,8,1310721,2,1,10,3, +6,10,4,8,1655921,14,1310721,2,1646495,12,10,3,6,10,4,8, +1633929,12,8,1581795,2,1581885,2,28,1,6,1,10,28,34,63,38, +3,26,1655803,16,1655783,14,114,101,116,117,114,110,32,118,97,108, +117,101,0,1655757,1581885,2,1581889,2,1621085,12,8,1655921,14,1581795,2,1655869, +16,1581795,2,1581889,2,1617847,12,1,6,1,10,28,34,63,38,3, +26,1,6,1,10,28,34,63,38,1,10,28,1655871,14,1,1655921, +16,1655905,14,114,101,116,117,114,110,32,118,97,108,117,101,0, +1655879,1581795,2,1581889,2,1621085,12,8,1581811,2,1592809,12,8,1310721,2,2, +1609585,12,8,3,1,1598733,12,8,1581771,1581747,2,1581771,2,1584145,12,10, +3,6,10,4,8,2097153,3,28,1584843,12,8,15,1584907,12,8,1581767, +1,10,3,6,10,4,8,1,1656023,14,1,1310721,2,3,26,1310721, +4,18,1310721,2,3,28,1310721,4,1581763,2,1310721,2,4,1634297,12,8, +1591423,12,8,1581763,1,10,3,6,10,4,8,1651391,12,8,1581763,1310721, +2,2,10,3,6,10,4,8,1,1656121,14,1,1310721,2,3,26, +1310721,4,18,1310721,2,21,28,1310721,4,1,1310721,2,4,1,1310721,2, +3,26,4,1554945,1310721,2,13,26,4,1554945,129,26,1310721,2,15,26, +4,1554945,257,26,1310721,2,17,26,4,1554945,769,26,1310721,2,19,26, +4,1581801,2,1592809,12,8,1581803,2,1592341,12,1,6,1,10,28,34, +63,38,3,26,1658207,16,1579517,2,1,28,1,6,1,10,28,34, +63,38,3,26,1656359,16,1656351,14,117,110,116,101,114,109,105,110, +97,116,101,100,32,97,115,109,32,98,108,111,99,107,0,1656305, +1581991,12,8,1579517,2,3,28,1,6,1,10,28,34,63,38,3, +26,1656411,16,1579519,2,1584843,12,8,1591423,12,8,1656237,14,1579517,2,7, +28,1,6,1,10,28,34,63,38,3,26,1656463,16,1579519,2,1585671, +12,8,1591423,12,8,1656237,14,1581827,2,1592341,12,1656581,16,1591423,12,8, +1579517,2,3,28,1,6,1,10,28,34,63,38,1,10,28,1656561, +16,1656553,14,35,32,110,101,101,100,115,32,97,32,110,117,109, +98,101,114,0,1656519,1581991,12,8,1579519,2,1584671,12,8,1591423,12,8, +1656237,14,1581829,2,1592341,12,1656703,16,1591423,12,8,1579517,2,3,28,1, +6,1,10,28,34,63,38,3,26,1656661,16,1579521,2,1,6,1, +10,28,34,63,38,1,10,28,1656663,14,1,1656691,16,1,1579519,2, +28,1584843,12,8,1591423,12,8,1656699,14,29,1584907,12,8,1656237,14,1581825, +2,1592341,12,1656893,16,1591423,12,8,1310721,2,2,129,40,3,36,1656791, +16,1656783,14,116,111,111,32,109,97,110,121,32,97,115,109,32, +108,97,98,101,108,115,0,1656743,1581991,12,8,1310721,2,13,26,2, +1310721,2,2,26,1592987,12,10,3,6,10,4,8,1310721,2,15,26, +2,1310721,2,2,26,1581747,2,10,3,6,10,4,8,1310721,2,1, +6,2,1,6,3,26,5,6,4,10,8,8,1656237,14,1579519,2, +1,26,2,1656963,16,1579519,2,3,26,2,1,6,1,10,28,34, +63,38,3,26,1,6,1,10,28,34,63,38,1,10,28,1656965, +14,1,1657035,16,1310721,2,9,26,1579519,2,1,26,2,1594235,12,10, +3,6,10,4,1,40,3,36,1,6,1,10,28,34,63,38, +1,10,28,1657037,14,1,1657067,16,1310721,2,9,26,2,1584907,12,8, +1591423,12,8,1656237,14,1579517,2,5,28,1,6,1,10,28,34,63, +38,1,10,28,1657147,16,1657139,14,98,97,100,32,116,111,107,101, +110,32,105,110,32,97,115,109,0,1657105,1581991,12,8,1310721,2,5, +26,1579519,2,1597111,12,10,3,6,10,4,8,1310721,2,5,26,2, +1,40,3,36,1657257,16,1472001,1310721,2,5,26,2,26,3,10,3, +6,10,4,8,1467905,1310721,2,5,26,2,26,2,1585585,12,8,1591423, +12,8,1656237,14,1310721,2,7,26,3,1,10,28,10,3,6,10, +4,8,1310721,2,5,26,1,10,3,6,10,4,8,1310721,2,5, +26,2,1310721,2,2,40,1657451,16,1310721,2,13,26,2,1310721,2,5, +26,2,26,2,1579519,2,28,1,6,1,10,28,34,63,38,3, +26,1657413,16,1310721,2,7,26,1310721,2,5,26,2,10,3,6,10, +4,8,1310721,2,5,26,1,6,2,1,6,3,26,5,6,4, +10,8,8,1657307,14,1310721,2,7,26,2,1,40,3,36,1657513,16, +1310721,2,15,26,2,1310721,2,7,26,2,26,2,1584843,12,8,1591423, +12,8,1656237,14,1310721,2,11,26,1579519,2,1583635,12,10,3,6,10, +4,8,1310721,2,11,26,2,1,40,3,36,1657629,16,1417729,1310721,2, +11,26,2,26,2,13,28,1,6,1,10,28,34,63,38,3, +26,1,6,1,10,28,34,63,38,1,10,28,1657631,14,1,1657671, +16,45,1425921,1310721,2,11,26,2,26,2,26,1584907,12,8,1591423,12, +8,1656237,14,1310721,2,11,26,2,1,40,3,36,1657761,16,1417729,1310721, +2,11,26,2,26,2,3,28,1,6,1,10,28,34,63,38, +1,10,28,1,6,1,10,28,34,63,38,1,10,28,1657763,14, +1,1657835,16,1417729,1310721,2,11,26,2,26,2,11,28,1,6,1, +10,28,34,63,38,1,10,28,1,6,1,10,28,34,63,38, +1,10,28,1657837,14,1,1657873,16,1425921,1310721,2,11,26,2,26,2, +1584843,12,8,1591423,12,8,1656237,14,1310721,2,11,26,2,1,40,3, +36,1657961,16,1417729,1310721,2,11,26,2,26,2,3,28,1,6,1, +10,28,34,63,38,3,26,1,6,1,10,28,34,63,38,1, +10,28,1657963,14,1,1657993,16,1310721,2,11,26,2,1585301,12,8,1591423, +12,8,1656237,14,1310721,2,3,26,2,501,40,3,36,1658087,16,1658079, +14,116,111,111,32,109,97,110,121,32,97,115,109,32,108,97, +98,101,108,32,114,101,102,101,114,101,110,99,101,115,0,1658019, +1581991,12,8,1310721,2,17,26,2,1310721,2,3,26,2,26,1579519,2, +10,3,6,10,4,8,1310721,2,19,26,2,1310721,2,3,26,2, +26,1585141,12,10,3,6,10,4,8,1310721,2,3,26,1,6,2, +1,6,3,26,5,6,4,10,8,8,1591423,12,8,1656237,14,1591423, +12,8,1310721,2,5,26,1,10,3,6,10,4,8,1310721,2,5, +26,2,1310721,2,3,26,2,40,1658661,16,1310721,2,7,26,3,1, +10,28,10,3,6,10,4,8,1310721,2,9,26,1,10,3,6, +10,4,8,1310721,2,9,26,2,1310721,2,2,40,1658475,16,1310721,2, +13,26,2,1310721,2,9,26,2,26,2,1310721,2,17,26,2,1310721, +2,5,26,2,26,2,28,1,6,1,10,28,34,63,38,3, +26,1658437,16,1310721,2,7,26,1310721,2,9,26,2,10,3,6,10, +4,8,1310721,2,9,26,1,6,2,1,6,3,26,5,6,4, +10,8,8,1658311,14,1310721,2,7,26,2,1,40,1658569,16,1310721,2, +17,26,2,1310721,2,5,26,2,26,2,1658561,14,117,110,107,110, +111,119,110,32,110,97,109,101,32,105,110,32,97,115,109,0, +1658521,1582117,12,8,1310721,2,19,26,2,1310721,2,5,26,2,26,2, +1310721,2,15,26,2,1310721,2,7,26,2,26,2,1584967,12,8,1310721, +2,5,26,1,6,2,1,6,3,26,5,6,4,10,8,8, +1658235,14,1,1658669,14,1,1310721,2,21,26,1310721,4,18,1310721,2,1, +28,1310721,4,1591423,12,8,1656135,12,8,1581793,1,10,3,6,10,4, +8,1,1658731,14,1,1310721,2,1,26,1310721,4,18,1310721,2,7,28, +1310721,4,1,1310721,2,4,1,1310721,2,3,26,4,1581801,2,1592341,12, +1,6,1,10,28,34,63,38,3,26,1658843,16,1658835,14,101,120, +112,101,99,116,101,100,32,123,0,1658813,1581991,12,8,1591423,12,8, +3,1659239,16,1579517,2,1,28,1,6,1,10,28,34,63,38,3, +26,1658937,16,1658929,14,117,110,116,101,114,109,105,110,97,116,101, +100,32,113,117,111,116,101,0,1658891,1581991,12,8,1581801,2,1592341,12, +1658983,16,1310721,2,3,26,1,6,2,1,6,3,26,5,6,4, +10,8,8,1581803,2,1592341,12,1659067,16,1310721,2,3,26,2,1,6, +1,10,28,34,63,38,3,26,1659033,16,1659239,14,1310721,2,3,26, +1,6,2,1,6,-1,26,5,6,4,10,8,8,1310721,2,2, +8001,40,3,36,1659127,16,1659119,14,113,117,111,116,101,32,116,111, +111,32,108,111,110,103,0,1659089,1581991,12,8,1524225,1310721,2,2,1310721, +2,2,26,26,1579517,2,10,3,6,10,4,8,1524225,1310721,2,2, +1310721,2,2,26,3,26,26,1579519,2,10,3,6,10,4,8,1310721, +2,1,6,2,1,6,3,26,5,6,4,10,8,8,1591423,12, +8,1658849,14,1591423,12,8,1310721,2,5,26,1310721,2,2,1310721,2,2, +26,3,26,1577137,12,10,3,6,10,4,8,1310721,2,5,26,2, +1,26,1310721,2,2,10,3,6,10,4,8,1310721,2,5,26,2, +3,26,1524225,1310721,2,2,1310721,2,2,26,1574139,12,8,1310721,2,5, +26,2,1659371,14,1,1310721,2,7,26,1310721,4,18,1310721,2,7,28, +1310721,4,1310721,2,4,1310721,2,2,1,26,2,5,30,3,26,1310721, +2,3,26,4,1310721,2,3,26,2,1577137,12,1310721,2,5,26,4, +1310721,2,5,26,2,1310721,2,2,1310721,2,3,26,2,1574139,12,8, +1310721,2,5,26,2,1659505,14,1,1310721,2,7,26,1310721,4,18,1310721, +2,7,28,1310721,4,1310721,2,4,1310721,2,2,1,26,2,5,30, +3,26,1310721,2,3,26,4,1310721,2,3,26,2,1577137,12,1310721,2, +5,26,4,1310721,2,5,26,2,1310721,2,2,1310721,2,3,26,2, +1574139,12,8,1310721,2,5,26,2,1659639,14,1,1310721,2,7,26,1310721, +4,18,1310721,2,7,28,1310721,4,1310721,2,3,26,4,1310721,2,4, +1,1310721,2,5,26,4,1310721,2,5,26,2,1310721,2,3,26,2, +40,1659783,16,1310721,2,2,1310721,2,5,26,2,26,2,1584671,12,8, +1310721,2,5,26,1,6,2,1,6,3,26,5,6,4,10,8, +8,1659693,14,1,1659791,14,1,1310721,2,7,26,1310721,4,18,1310721,2, +9,28,1310721,4,1591423,12,8,1310721,2,1658745,12,10,3,6,10,4, +8,1310721,2,3,26,1585141,12,10,3,6,10,4,8,15,1584907,12, +8,1310721,2,5,26,1581747,2,10,3,6,10,4,8,1310721,2,7, +26,1310721,2,2,1,26,2,5,30,3,26,10,3,6,10,4, +8,1310721,2,2,1310721,2,7,26,2,1659653,12,8,1310721,2,2,1577851, +12,8,1310721,2,3,26,2,1581747,2,1584967,12,8,1310721,2,5,26, +2,1584843,12,8,1581865,2,1585453,12,8,1581793,3,10,3,6,10,4, +8,1,1660043,14,1,1310721,2,9,26,1310721,4,18,1310721,2,7,28, +1310721,4,1310721,2,4,1,1310721,2,3,26,4,5,1310721,2,2,1, +1,1586239,12,8,1591423,12,8,1579517,2,1,28,1,6,1,10,28, +34,63,38,1,10,28,1660343,16,1310721,2,3,26,2,8001,40,3, +36,1660211,16,1660203,14,116,111,111,32,109,97,110,121,32,116,111, +107,101,110,115,0,1660171,1581991,12,8,1524225,1310721,2,3,26,2,1310721, +2,3,26,2,26,26,1579517,2,10,3,6,10,4,8,1524225,1310721, +2,3,26,2,1310721,2,3,26,2,26,3,26,26,1579519,2,10, +3,6,10,4,8,1310721,2,3,26,1,6,2,1,6,3,26, +5,6,4,10,8,8,1591423,12,8,1660111,14,1587189,12,8,1310721,2, +5,26,1310721,2,3,26,2,1310721,2,3,26,2,26,3,26,1577137, +12,10,3,6,10,4,8,1310721,2,5,26,2,1,26,1310721,2, +3,26,2,10,3,6,10,4,8,1310721,2,5,26,2,3,26, +1524225,1310721,2,3,26,2,1310721,2,3,26,2,26,1574139,12,8,1310721, +2,5,26,2,1660495,14,1,1310721,2,7,26,1310721,4,18,1310721,2, +3,28,1310721,4,1310721,2,4,3,1310721,2,2,3,3,1586239,12,8, +1591423,12,8,1,1660559,14,1,1310721,2,3,26,1310721,4,18,1310721,2, +3,28,1310721,4,1548801,1581781,2,49,30,26,1310721,2,4,1581781,2,61, +40,3,36,1660685,16,1660677,14,99,111,109,112,105,108,101,115,32, +110,101,115,116,101,100,32,116,111,111,32,100,101,101,112,108, +121,0,1660623,1581991,12,8,1581781,1,6,2,1,6,3,26,5,6, +4,10,8,8,1310721,2,2,1,26,1581755,2,10,3,6,10,4, +8,1310721,2,2,3,26,1581757,2,10,3,6,10,4,8,1310721,2, +2,5,26,1581759,2,10,3,6,10,4,8,1310721,2,2,7,26, +1581761,2,10,3,6,10,4,8,1310721,2,2,9,26,1581763,2,10, +3,6,10,4,8,1310721,2,2,11,26,1581765,2,10,3,6,10, +4,8,1310721,2,2,13,26,1581767,2,10,3,6,10,4,8,1310721, +2,2,15,26,1581769,2,10,3,6,10,4,8,1310721,2,2,17, +26,1581771,2,10,3,6,10,4,8,1310721,2,2,19,26,1581773,2, +10,3,6,10,4,8,1310721,2,2,21,26,1581775,2,10,3,6, +10,4,8,1310721,2,2,23,26,1581777,2,10,3,6,10,4,8, +1310721,2,2,25,26,1581779,2,10,3,6,10,4,8,1310721,2,2, +27,26,1581785,2,10,3,6,10,4,8,1310721,2,2,29,26,1581787, +2,10,3,6,10,4,8,1310721,2,2,31,26,1581789,2,10,3, +6,10,4,8,1310721,2,2,33,26,1581791,2,10,3,6,10,4, +8,1310721,2,2,35,26,1581793,2,10,3,6,10,4,8,1310721,2, +2,37,26,1581783,2,10,3,6,10,4,8,1310721,2,2,39,26, +1581795,2,10,3,6,10,4,8,1310721,2,2,41,26,1581911,2,10, +3,6,10,4,8,1310721,2,2,43,26,1581889,2,10,3,6,10, +4,8,1310721,2,2,45,26,1581895,5,1574139,12,8,1,1661313,14,1, +1310721,2,3,26,1310721,4,18,1310721,2,3,28,1310721,4,1581781,1,6, +2,1,6,-1,26,5,6,4,10,8,8,1310721,2,1548801,1581781,2, +49,30,26,10,3,6,10,4,8,1581755,1310721,2,2,1,26,2, +10,3,6,10,4,8,1581757,1310721,2,2,3,26,2,10,3,6, +10,4,8,1581759,1310721,2,2,5,26,2,10,3,6,10,4,8, +1581761,1310721,2,2,7,26,2,10,3,6,10,4,8,1581763,1310721,2, +2,9,26,2,10,3,6,10,4,8,1581765,1310721,2,2,11,26, +2,10,3,6,10,4,8,1581767,1310721,2,2,13,26,2,10,3, +6,10,4,8,1581769,1310721,2,2,15,26,2,10,3,6,10,4, +8,1581771,1310721,2,2,17,26,2,10,3,6,10,4,8,1581773,1310721, +2,2,19,26,2,10,3,6,10,4,8,1581775,1310721,2,2,21, +26,2,10,3,6,10,4,8,1581777,1310721,2,2,23,26,2,10, +3,6,10,4,8,1581779,1310721,2,2,25,26,2,10,3,6,10, +4,8,1581785,1310721,2,2,27,26,2,10,3,6,10,4,8,1581787, +1310721,2,2,29,26,2,10,3,6,10,4,8,1581789,1310721,2,2, +31,26,2,10,3,6,10,4,8,1581791,1310721,2,2,33,26,2, +10,3,6,10,4,8,1581793,1310721,2,2,35,26,2,10,3,6, +10,4,8,1581783,1310721,2,2,37,26,2,10,3,6,10,4,8, +1581795,1310721,2,2,39,26,2,10,3,6,10,4,8,1581911,1310721,2, +2,41,26,2,10,3,6,10,4,8,1581889,1310721,2,2,43,26, +2,10,3,6,10,4,8,1581895,1310721,2,2,45,26,5,1574139,12, +8,1,1661995,14,1,1310721,2,3,26,1310721,4,18,1310721,2,5,28, +1310721,4,1310721,2,3,26,4,1310721,2,4,1581757,1581755,2,10,3,6, +10,4,8,1581759,3,10,3,6,10,4,8,1581761,1,10,3,6, +10,4,8,1581763,1310721,2,2,1662129,16,1579515,2,1,6,1,10,28, +34,63,38,1,10,28,1662131,14,1,10,3,6,10,4,8,1581765, +1310721,2,3,26,2,10,3,6,10,4,8,1581795,1,10,3,6, +10,4,8,1581767,3,10,3,6,10,4,8,1581769,1,10,3,6, +10,4,8,1581771,1,10,3,6,10,4,8,1581775,3,10,3,6, +10,4,8,1581779,1581777,2,10,3,6,10,4,8,1581785,1,10,3, +6,10,4,8,1581787,1,10,3,6,10,4,8,1,1662305,14,1, +1310721,2,5,26,1310721,4,18,1310721,2,1,28,1310721,4,1310721,1584843,12, +8,3,1584907,12,8,1581773,1585141,12,10,3,6,10,4,8,29,1584907, +12,8,1310721,1584843,12,8,5,1584907,12,8,1,1662397,14,1,1310721,2, +1,26,1310721,4,18,1310721,2,1,28,1310721,4,1,1584843,12,8,1581771, +2,1581747,2,1584475,12,8,1310721,1584843,12,8,3,1584907,12,8,1581761,2, +1584843,12,8,27,1584907,12,8,1310721,1584843,12,8,5,1584907,12,8,19, +1584907,12,8,1581773,2,1581761,2,1584967,12,8,1581775,1,10,3,6,10, +4,8,1581755,1581757,2,10,3,6,10,4,8,1,1662559,14,1,1310721, +2,1,26,1310721,4,18,1310721,2,1,28,1310721,4,1581801,2,1592809,12, +8,1581803,2,1592341,12,1,6,1,10,28,34,63,38,3,26,1662751, +16,1579517,2,1,28,1,6,1,10,28,34,63,38,3,26,1662741, +16,1662733,14,109,105,115,115,105,110,103,32,125,32,97,116,32, +116,104,101,32,101,110,100,32,111,102,32,97,32,102,117,110, +99,116,105,111,110,0,1662663,1581991,12,8,1650659,12,8,1662595,14,1591423, +12,8,1581757,2,1599439,12,8,1581763,2,1662805,16,1581767,2,1,6,1, +10,28,34,63,38,1,10,28,1662807,14,1,1662925,16,3,3,1598733, +12,8,1581765,2,1662925,16,1662917,14,97,32,45,62,32,46,117,32, +102,117,110,99,116,105,111,110,32,104,97,115,32,116,111,32, +101,110,100,32,119,105,116,104,32,97,32,114,101,116,117,114, +110,0,1662833,1582287,12,8,1,1662933,14,1,1310721,2,1,26,1310721,4, +18,1310721,2,169,28,1310721,4,1310721,2,4,1,1310721,2,7,26,4, +1,1310721,2,9,26,4,1,1310721,2,13,26,4,1,1310721,2,163, +26,4,1,1310721,2,165,26,4,1591423,12,8,1310721,2,3,26,1592987, +12,10,3,6,10,4,8,1581911,1,10,3,6,10,4,8,1581917, +2,1592341,12,1663293,16,1591423,12,8,3,1663265,16,1581911,2,17,40,3, +36,1663173,16,1663165,14,116,111,111,32,109,97,110,121,32,116,121, +112,101,32,112,97,114,97,109,101,116,101,114,115,0,1663115,1581991, +12,8,1581895,1581911,2,26,1592987,12,10,3,6,10,4,8,1581911,1, +6,2,1,6,3,26,5,6,4,10,8,8,1581809,2,1592601,12, +1,6,1,10,28,34,63,38,3,26,1663261,16,1663265,14,1663089,14, +1622243,12,8,1310721,2,165,26,3,10,3,6,10,4,8,1310721,2, +5,26,1310721,2,3,26,2,1583635,12,10,3,6,10,4,8,1310721, +2,5,26,2,1,40,1663351,16,3,1663389,14,1310721,2,5,26,2, +1579449,2,40,1,6,1,10,28,34,63,38,1,10,28,1663399,16, +3,1663463,14,1417729,1310721,2,5,26,2,26,2,3,28,1,6,1, +10,28,34,63,38,1,10,28,1,6,1,10,28,34,63,38, +1,10,28,1663473,16,3,1663515,14,1442305,1310721,2,5,26,2,26,2, +3,32,1,6,1,10,28,34,63,38,1,10,28,1663585,16,1310721, +2,5,26,1310721,2,3,26,2,3,1,1583159,12,10,3,6,10, +4,8,1442305,1310721,2,5,26,2,26,1,10,3,6,10,4,8, +1581797,2,1592809,12,8,1581799,2,1592341,12,1,6,1,10,28,34,63, +38,3,26,1664929,16,3,1664929,16,1310721,2,11,26,1,10,3,6, +10,4,8,1310721,2,115,26,1310721,2,7,26,2,26,1581877,2,10, +3,6,10,4,8,1581835,2,1592341,12,1,6,1,10,28,34,63, +38,3,26,1664063,16,1579517,2,9,28,1,6,1,10,28,34,63, +38,3,26,1663873,16,1579519,2,1581821,2,28,1,6,1,10,28,34, +63,38,3,26,1663795,16,3,1663847,14,1579519,2,1581805,2,28,1,6, +1,10,28,34,63,38,3,26,1,6,1,10,28,34,63,38, +1,10,28,1,6,1,10,28,34,63,38,1,10,28,1663875,14, +1,1663885,16,3,1664037,14,1579517,2,5,28,1,6,1,10,28,34, +63,38,3,26,1664013,16,1579519,2,1581837,2,28,1,6,1,10,28, +34,63,38,3,26,1663957,16,3,1663987,14,1579519,2,1622029,12,1,6, +1,10,28,34,63,38,1,10,28,1,6,1,10,28,34,63, +38,1,10,28,1664015,14,1,1,6,1,10,28,34,63,38,1, +10,28,1,6,1,10,28,34,63,38,1,10,28,1664065,14,1, +1664251,16,1310721,2,115,26,1310721,2,7,26,2,26,1622323,12,10,3, +6,10,4,8,1310721,2,165,26,3,10,3,6,10,4,8,1310721, +2,115,26,1310721,2,7,26,2,26,2,1617293,12,1664251,16,1664243,14, +115,116,114,117,99,116,115,32,97,110,100,32,97,114,114,97, +121,115,32,97,114,101,32,112,97,115,115,101,100,32,98,121, +32,112,111,105,110,116,101,114,0,1664161,1581991,12,8,1581835,2,1592601, +12,1664457,16,1579519,2,1581851,2,28,1,6,1,10,28,34,63,38, +3,26,1664323,16,1310721,2,11,26,3,10,3,6,10,4,8,1664451, +14,1579519,2,1581853,2,28,1,6,1,10,28,34,63,38,3,26, +1664383,16,1310721,2,11,26,5,10,3,6,10,4,8,1664451,14,1664443, +14,97,32,112,97,114,97,109,101,116,101,114,32,116,97,103, +32,105,115,32,46,109,32,111,114,32,46,100,0,1664387,1581991,12, +8,1591423,12,8,1310721,2,19,26,1310721,2,7,26,2,26,1592987,12, +10,3,6,10,4,8,1310721,2,7,26,2,25,40,3,36,1664551, +16,1310721,2,11,26,2,1,6,1,10,28,34,63,38,1,10, +28,1664553,14,1,1664655,16,1664647,14,111,110,108,121,32,116,104,101, +32,102,105,114,115,116,32,49,50,32,112,97,114,97,109,101, +116,101,114,115,32,99,97,110,32,98,101,32,116,97,103,103, +101,100,0,1664561,1581991,12,8,1310721,2,67,26,1310721,2,7,26,2, +26,1310721,2,11,26,2,10,3,6,10,4,8,1310721,2,7,26, +2,25,40,1664781,16,1310721,2,9,26,1310721,2,9,26,2,1310721,2, +11,26,2,1310721,2,7,26,2,1310721,2,7,26,2,26,48,26, +10,3,6,10,4,8,1310721,2,7,26,1,6,2,1,6,3, +26,5,6,4,10,8,8,1310721,2,7,26,2,49,40,3,36, +1664889,16,1664881,14,116,111,111,32,109,97,110,121,32,112,97,114, +97,109,101,116,101,114,115,0,1664841,1581991,12,8,1581809,2,1592601,12, +1,6,1,10,28,34,63,38,3,26,1664925,16,1664929,14,1663627,14, +1581799,2,1592809,12,8,1581815,2,1592601,12,1665155,16,1581835,2,1592341,12,1, +6,1,10,28,34,63,38,3,26,1665029,16,1310721,2,163,26,1622323, +12,10,3,6,10,4,8,1310721,2,165,26,3,10,3,6,10, +4,8,1581835,2,1592601,12,1665155,16,1581855,2,1592471,12,1,6,1,10, +28,34,63,38,3,26,1665127,16,1665119,14,116,104,101,32,114,101, +116,117,114,110,32,116,97,103,32,105,115,32,46,117,0,1665077, +1581991,12,8,1591423,12,8,1310721,2,13,26,3,10,3,6,10,4, +8,1310721,2,15,26,1310721,2,13,26,2,1310721,2,7,26,2,26, +1310721,2,7,26,2,26,1310721,2,9,26,2,129,30,26,10,3, +6,10,4,8,1442305,1310721,2,5,26,2,26,2,5,32,1665405,16, +1434113,1310721,2,5,26,2,26,2,1310721,2,15,26,2,28,1,6, +1,10,28,34,63,38,1,10,28,1665401,16,1310721,2,3,26,2, +1665393,14,100,111,101,115,110,39,116,32,109,97,116,99,104,32, +105,116,115,32,101,97,114,108,105,101,114,32,100,101,99,108, +97,114,97,116,105,111,110,0,1665317,1582117,12,8,1665693,14,1442305,1310721, +2,5,26,2,26,2,9,32,1665507,16,1310721,2,9,26,2,1665449, +16,3,1665481,14,1310721,2,13,26,2,1,6,1,10,28,34,63, +38,1,10,28,1,6,1,10,28,34,63,38,1,10,28,1665509, +14,1,1665693,16,1310721,2,3,26,2,1665685,14,119,97,115,32,99, +97,108,108,101,100,32,98,101,102,111,114,101,32,105,116,115, +32,116,97,103,115,32,119,101,114,101,32,100,101,99,108,97, +114,101,100,59,32,112,117,116,32,97,32,112,114,111,116,111, +116,121,112,101,32,97,98,111,118,101,32,116,104,101,32,102, +105,114,115,116,32,99,97,108,108,0,1665527,1582117,12,8,1434113,1310721, +2,5,26,2,26,1310721,2,15,26,2,10,3,6,10,4,8, +1442305,1310721,2,5,26,2,26,1442305,1310721,2,5,26,2,26,2,5, +34,10,3,6,10,4,8,1581911,2,1665829,16,1442305,1310721,2,5,26, +2,26,1442305,1310721,2,5,26,2,26,2,257,34,10,3,6,10, +4,8,1310721,2,165,26,2,1666199,16,1310721,2,163,26,2,1,6, +1,10,28,34,63,38,3,26,1665901,16,1310721,2,163,26,1581877,2, +10,3,6,10,4,8,1310721,2,167,26,1310721,2,163,26,2,1310721, +2,7,26,2,1310721,2,115,26,1310721,2,15,26,2,1612831,12,10, +3,6,10,4,8,1559041,1310721,2,5,26,2,26,2,1666059,16,1559041, +1310721,2,5,26,2,26,2,1310721,2,167,26,2,28,1,6,1, +10,28,34,63,38,1,10,28,1,6,1,10,28,34,63,38, +1,10,28,1666061,14,1,1666163,16,1310721,2,3,26,2,1666155,14,100, +111,101,115,110,39,116,32,109,97,116,99,104,32,105,116,115, +32,101,97,114,108,105,101,114,32,100,101,99,108,97,114,97, +116,105,111,110,0,1666079,1582117,12,8,1559041,1310721,2,5,26,2,26, +1310721,2,167,26,2,10,3,6,10,4,8,1581811,2,1592601,12,1666241, +16,1581911,1,10,3,6,10,4,8,1310721,2,5,26,2,1667131,14, +1442305,1310721,2,5,26,2,26,2,3,32,1666321,16,1310721,2,3,26, +2,1666313,14,105,115,32,100,101,102,105,110,101,100,32,116,119, +105,99,101,0,1666279,1582117,12,8,1425921,1310721,2,5,26,2,26,1581747, +2,10,3,6,10,4,8,1442305,1310721,2,5,26,2,26,1442305,1310721, +2,5,26,2,26,2,3,34,10,3,6,10,4,8,1450497,1310721, +2,5,26,2,26,2,1581747,2,1584475,12,8,1450497,1310721,2,5,26, +2,26,1,10,3,6,10,4,8,1310721,2,2,1310721,2,13,26, +2,1662009,12,8,1310721,2,165,26,2,1666511,16,1581795,1310721,2,163,26, +2,10,3,6,10,4,8,1662319,12,8,1310721,2,17,26,1,10, +3,6,10,4,8,1310721,2,17,26,2,1310721,2,7,26,2,40, +1666981,16,1310721,2,11,26,1310721,2,19,26,1310721,2,17,26,2,26, +2,1597293,12,10,3,6,10,4,8,1581761,1,6,2,1,6,3, +26,5,6,4,10,8,8,1556993,1310721,2,11,26,2,26,1310721,2, +115,26,1310721,2,17,26,2,26,2,10,3,6,10,4,8,1581763, +2,1666767,16,1310721,2,67,26,1310721,2,17,26,2,26,2,3,28, +1,6,1,10,28,34,63,38,3,26,1,6,1,10,28,34, +63,38,1,10,28,1666769,14,1,1666801,16,1469953,1310721,2,11,26,2, +26,13,10,3,6,10,4,8,1581763,2,1666881,16,1310721,2,67,26, +1310721,2,17,26,2,26,2,5,28,1,6,1,10,28,34,63, +38,3,26,1,6,1,10,28,34,63,38,1,10,28,1666883,14, +1,1666943,16,1469953,1310721,2,11,26,2,26,3,10,3,6,10,4, +8,1474049,1310721,2,11,26,2,26,3,10,3,6,10,4,8,1310721, +2,17,26,1,6,2,1,6,3,26,5,6,4,10,8,8, +1666539,14,1310721,2,17,26,1310721,2,7,26,2,10,3,6,10,4, +8,1310721,2,17,26,2,1667087,16,1310721,2,17,26,1,6,2,1, +6,-1,26,5,6,4,10,8,8,1310721,2,17,26,2,1585585,12, +8,5,1584907,12,8,1667011,14,1662573,12,8,1662411,12,8,1581911,1,10, +3,6,10,4,8,1310721,2,5,26,2,1667131,14,1,1310721,2,169, +26,1310721,4,18,1310721,2,13,28,1310721,4,1310721,2,4,1,1310721,2, +9,26,4,1310721,2,2,1667433,16,1624757,12,8,1310721,2,9,26,1581913, +2,10,3,6,10,4,8,1581915,2,1667327,16,1667319,14,116,97,103, +115,32,103,111,32,111,110,32,108,111,99,97,108,115,32,97, +110,100,32,112,97,114,97,109,101,116,101,114,115,44,32,110, +111,116,32,103,108,111,98,97,108,115,0,1667227,1581991,12,8,1310721, +2,9,26,2,1581885,2,28,1,6,1,10,28,34,63,38,3, +26,1667429,16,1667421,14,97,32,118,97,114,105,97,98,108,101,32, +99,97,110,39,116,32,98,101,32,118,111,105,100,0,1667371,1581991, +12,8,1667439,14,1591423,12,8,3,1668391,16,1310721,2,3,26,1592987,12, +10,3,6,10,4,8,1581805,2,1592601,12,1667767,16,1310721,2,7,26, +1649967,12,10,3,6,10,4,8,1581807,2,1592809,12,8,1310721,2,11, +26,1310721,2,3,26,2,7,1581747,2,1583159,12,10,3,6,10,4, +8,1310721,2,9,26,2,1,6,1,10,28,34,63,38,3,26, +1667613,16,1310721,2,9,26,1581877,2,10,3,6,10,4,8,1559041,1310721, +2,11,26,2,26,1310721,2,9,26,2,1310721,2,7,26,2,1612333, +12,10,3,6,10,4,8,1310721,2,7,26,1559041,1310721,2,11,26, +2,26,2,1615957,12,10,3,6,10,4,8,1310721,2,7,26,2, +1667763,16,1,1584671,12,8,1310721,2,7,26,1,6,2,1,6,-1, +26,5,6,4,10,8,8,1667703,14,1668351,14,1310721,2,9,26,2, +1667821,16,1310721,2,9,26,2,1617293,12,1,6,1,10,28,34,63, +38,1,10,28,1667823,14,1,1668001,16,1310721,2,11,26,1310721,2,3, +26,2,7,1581747,2,1583159,12,10,3,6,10,4,8,1559041,1310721,2, +11,26,2,26,1310721,2,9,26,2,10,3,6,10,4,8,1310721, +2,7,26,1310721,2,9,26,2,1615957,12,10,3,6,10,4,8, +1310721,2,7,26,2,1667997,16,1,1584671,12,8,1310721,2,7,26,1, +6,2,1,6,-1,26,5,6,4,10,8,8,1667937,14,1668351,14, +1310721,2,5,26,1581747,2,10,3,6,10,4,8,1310721,2,11,26, +1310721,2,3,26,2,5,1310721,2,5,26,2,1583159,12,10,3,6, +10,4,8,1310721,2,9,26,2,1668121,16,1559041,1310721,2,11,26,2, +26,1310721,2,9,26,2,10,3,6,10,4,8,1,1584671,12,8, +1581813,2,1592601,12,1668351,16,1579517,2,7,28,1,6,1,10,28,34, +63,38,3,26,1668319,16,1310721,2,5,26,2,1581749,2,26,1581747,2, +10,3,6,10,4,8,1310721,2,7,26,1579519,2,10,3,6,10, +4,8,1310721,2,7,26,2,2,1668301,16,1310721,2,7,26,2,2, +1584671,12,8,1310721,2,7,26,1,6,2,1,6,3,26,5,6, +4,10,8,8,1668229,14,1,1584671,12,8,1591423,12,8,1668351,14,1310721, +2,5,26,2,1581749,2,26,1649967,12,10,3,6,10,4,8,1581809, +2,1592601,12,1,6,1,10,28,34,63,38,3,26,1668387,16,1668391, +14,1667439,14,1581811,2,1592809,12,8,1,1668409,14,1,1310721,2,13,26, +1310721,4,18,1310721,2,3,28,1310721,4,1591423,12,8,3,1668535,16,1310721, +2,1592987,12,10,3,6,10,4,8,1581813,2,1592809,12,8,1310721,2, +2,9,1649967,12,1583159,12,8,1581809,2,1592601,12,1,6,1,10,28, +34,63,38,3,26,1668531,16,1668535,14,1668441,14,1581811,2,1592809,12,8, +1,1668553,14,1,1310721,2,3,26,1310721,4,18,1310721,2,43,28,1310721, +4,1,1310721,2,3,26,4,3,1310721,2,5,26,4,1,1310721,2, +13,26,4,1,1310721,2,15,26,4,1591423,12,8,1310721,2,1592987,12, +10,3,6,10,4,8,1581797,2,1592809,12,8,1581799,2,1592341,12,1, +6,1,10,28,34,63,38,3,26,1668937,16,3,1668937,16,1310721,2, +3,26,2,17,28,1,6,1,10,28,34,63,38,3,26,1668827, +16,1668819,14,97,110,32,101,110,104,97,110,99,101,109,101,110, +116,32,116,97,107,101,115,32,97,116,32,109,111,115,116,32, +56,32,105,110,112,117,116,115,0,1668743,1581991,12,8,1310721,2,27, +26,1310721,2,3,26,2,26,1592987,12,10,3,6,10,4,8,1310721, +2,3,26,1,6,2,1,6,3,26,5,6,4,10,8,8, +1581809,2,1592601,12,1,6,1,10,28,34,63,38,3,26,1668933,16, +1668937,14,1668695,14,1581799,2,1592809,12,8,1581815,2,1592601,12,1669179,16,1579517, +2,3,28,1,6,1,10,28,34,63,38,1,10,28,1668999,16, +3,1669029,14,1579519,2,1,40,1,6,1,10,28,34,63,38,1, +10,28,1669039,16,3,1669071,14,1579519,2,17,10,40,1,6,1,10, +28,34,63,38,1,10,28,1669149,16,1669141,14,101,120,112,101,99, +116,101,100,32,116,104,101,32,110,117,109,98,101,114,32,111, +102,32,111,117,116,112,117,116,115,0,1669079,1581991,12,8,1310721,2, +5,26,1579519,2,10,3,6,10,4,8,1591423,12,8,1310721,2,7, +26,1581747,2,10,3,6,10,4,8,1310721,2,17,26,1,10,3, +6,10,4,8,1310721,2,17,26,2,15,40,1669289,16,1,1584671,12, +8,1310721,2,17,26,1,6,2,1,6,3,26,5,6,4,10, +8,8,1669225,14,1310721,2,9,26,1581747,2,10,3,6,10,4,8, +1310721,2,17,26,1,10,3,6,10,4,8,1310721,2,17,26,2, +1310721,2,3,26,2,40,1669407,16,1,1584671,12,8,1310721,2,17,26, +1,6,2,1,6,3,26,5,6,4,10,8,8,1669335,14,1310721, +2,17,26,1,10,3,6,10,4,8,1310721,2,17,26,2,1310721, +2,3,26,2,40,1669659,16,1310721,2,9,26,2,1310721,2,17,26, +2,26,1581749,2,26,1581747,2,10,3,6,10,4,8,1310721,2,19, +26,1310721,2,27,26,1310721,2,17,26,2,26,2,10,3,6,10, +4,8,1310721,2,19,26,2,2,1669613,16,1310721,2,19,26,2,2, +1584671,12,8,1310721,2,19,26,1,6,2,1,6,3,26,5,6, +4,10,8,8,1669541,14,1,1584671,12,8,1310721,2,17,26,1,6, +2,1,6,3,26,5,6,4,10,8,8,1669429,14,1581869,2,1592601, +12,1669955,16,1310721,2,11,26,1581747,2,10,3,6,10,4,8,1579517, +2,7,28,1,6,1,10,28,34,63,38,1,10,28,1669805,16, +1669797,14,101,120,112,101,99,116,101,100,32,116,104,101,32,114, +101,99,105,112,101,32,97,115,32,97,32,115,116,114,105,110, +103,0,1669733,1581991,12,8,1579517,2,7,28,1,6,1,10,28,34, +63,38,3,26,1669943,16,1310721,2,19,26,1579519,2,10,3,6,10, +4,8,1310721,2,19,26,2,2,1669933,16,1310721,2,19,26,2,2, +1584671,12,8,1310721,2,19,26,1,6,2,1,6,3,26,5,6, +4,10,8,8,1669861,14,1591423,12,8,1669805,14,1,1584671,12,8,1670121, +14,1581871,2,1592601,12,1670067,16,1310721,2,11,26,1581747,2,10,3,6, +10,4,8,1656135,12,8,19,1584907,12,8,1310721,2,13,26,1581747,2, +1310721,2,11,26,2,28,10,3,6,10,4,8,1310721,2,15,26, +3,10,3,6,10,4,8,1670121,14,1670113,14,101,120,112,101,99, +116,101,100,32,109,105,99,114,111,32,111,114,32,118,109,0, +1670071,1581991,12,8,1310721,2,23,26,1579443,2,10,3,6,10,4,8, +1310721,2,23,26,2,1025,40,3,36,1670251,16,1670243,14,97,108,108, +32,53,49,50,32,101,110,104,97,110,99,101,109,101,110,116, +32,115,108,111,116,115,32,97,114,101,32,116,97,107,101,110, +0,1670171,1581991,12,8,1579443,1,6,2,1,6,3,26,5,6,4, +10,8,8,1310721,2,19,26,1310721,2,7,26,2,1581749,2,26,10, +3,6,10,4,8,1310721,2,19,26,2,1,26,1310721,2,23,26, +2,10,3,6,10,4,8,1310721,2,19,26,2,3,26,1310721,2, +15,26,2,10,3,6,10,4,8,1310721,2,19,26,2,5,26, +1310721,2,3,26,2,10,3,6,10,4,8,1310721,2,19,26,2, +7,26,1310721,2,5,26,2,10,3,6,10,4,8,1310721,2,19, +26,2,9,26,1310721,2,11,26,2,10,3,6,10,4,8,1310721, +2,19,26,2,11,26,1310721,2,13,26,2,10,3,6,10,4, +8,1310721,2,19,26,2,13,26,1310721,2,9,26,2,10,3,6, +10,4,8,1310721,2,21,26,1310721,2,2,13,1310721,2,23,26,2, +1583159,12,10,3,6,10,4,8,1434113,1310721,2,21,26,2,26,1310721, +2,3,26,2,1310721,2,5,26,2,33,30,26,1310721,2,7,26, +2,513,30,26,10,3,6,10,4,8,1581843,2,1592601,12,1670809,16, +1310721,2,25,26,1579519,2,1583635,12,10,3,6,10,4,8,1310721,2, +25,26,2,1,40,1670763,16,1310721,2,25,26,1579519,2,1585201,12,10, +3,6,10,4,8,1591423,12,8,1450497,1310721,2,21,26,2,26,1310721, +2,25,26,2,3,26,10,3,6,10,4,8,1581811,2,1592809,12, +8,1581749,2,1,6,1,10,28,34,63,38,3,26,1671179,16,1310721, +2,25,26,1310721,2,7,26,2,1572879,12,10,3,6,10,4,8, +1310721,2,25,26,2,1,40,1671179,16,1450497,1310721,2,21,26,2,26, +2,1,6,1,10,28,34,63,38,3,26,1671133,16,1670963,14,122, +99,58,32,108,105,110,101,32,0,1670943,1573231,12,8,1579523,2,1573371, +12,8,1670991,14,58,32,0,1670985,1573231,12,8,1310721,2,2,1573231,12, +8,1671085,14,58,32,116,104,101,32,101,110,104,97,110,99,101, +109,101,110,116,32,100,105,100,110,39,116,32,105,110,115,116, +97,108,108,32,40,0,1671015,1573231,12,8,1310721,2,25,26,2,1573371, +12,8,1671119,14,41,10,0,1671113,1573231,12,8,1573195,12,8,1442305,1310721, +2,21,26,2,26,1442305,1310721,2,21,26,2,26,2,129,34,10, +3,6,10,4,8,1,1671187,14,1,1310721,2,43,26,1310721,4,18, +1310721,2,9,28,1310721,4,3,1310721,2,4,1,1310721,2,3,26,4, +1581847,2,1592471,12,1671303,16,1591423,12,8,1581811,2,1592601,12,1671285,16,1579515, +1,10,3,6,10,4,8,1,1672241,14,1310721,2,1,10,3,6, +10,4,8,1581849,2,1592471,12,1671343,16,1591423,12,8,1310721,2,3,26, +3,10,3,6,10,4,8,1581837,2,1592471,12,1671629,16,1310721,2,5, +26,1310721,2,2,1662947,12,10,3,6,10,4,8,1310721,2,3,26, +2,1671623,16,1581749,2,1671525,16,1671517,14,112,97,114,115,101,32,119, +111,114,100,115,32,99,97,110,39,116,32,98,101,32,100,101, +102,105,110,101,100,32,119,104,105,108,101,32,98,117,105,108, +100,105,110,103,32,97,110,32,105,109,97,103,101,0,1671411,1581991, +12,8,1310721,2,7,26,1409537,1310721,2,5,26,2,26,2,11,1425921, +1310721,2,5,26,2,26,2,1583159,12,10,3,6,10,4,8,1442305, +1310721,2,7,26,2,26,3,17,34,33,34,65,34,10,3,6, +10,4,8,1,1672241,14,1310721,2,2,1,6,1,10,28,34,63, +38,3,26,1671665,16,3,1671697,14,1310721,2,3,26,2,1,6,1, +10,28,34,63,38,1,10,28,1671737,16,1671729,14,101,120,112,101, +99,116,101,100,32,102,110,0,1671705,1581991,12,8,1581839,2,1592471,12, +1671759,16,1,1667145,12,1672241,14,1581933,2,1592471,12,1671783,16,1625109,12,8, +1,1672241,14,1581935,2,1592471,12,1671803,16,1626969,12,1672241,14,1581937,2,1592471, +12,1671823,16,1627067,12,1672241,14,1581867,2,1592471,12,1671843,16,1668567,12,1672241, +14,1581841,2,1592471,12,1671863,16,1668423,12,1672241,14,1579517,2,5,28,1, +6,1,10,28,34,63,38,3,26,1672093,16,1310721,2,5,26,1579519, +2,1583635,12,10,3,6,10,4,8,1310721,2,5,26,2,1,40, +3,36,1672011,16,1417729,1310721,2,5,26,2,26,2,11,28,1,6, +1,10,28,34,63,38,3,26,1,6,1,10,28,34,63,38, +1,10,28,1672013,14,1,1672063,16,1442305,1310721,2,5,26,2,26,2, +65,32,1,6,1,10,28,34,63,38,1,10,28,1672065,14,1, +1672093,16,1310721,2,5,26,2,1,1634027,12,8,1,1672241,14,1624179,12, +1672111,16,3,1667145,12,1672241,14,1672225,14,101,120,112,101,99,116,101, +100,32,102,110,44,32,108,101,116,44,32,99,111,110,115,116, +44,32,115,116,114,117,99,116,44,32,116,121,112,101,32,111, +114,32,97,32,100,101,99,108,97,114,97,116,105,111,110,0, +1672115,1581991,12,8,1,1672241,14,1,1310721,2,9,26,1310721,4,18,1310721, +2,1,28,1310721,4,1579517,2,1,28,1,6,1,10,28,34,63, +38,1,10,28,1672311,16,1671201,12,8,1672267,14,1,1672319,14,1,1310721, +2,1,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,4,1310721, +2,2,1310721,2,3,26,4,1310721,2,3,26,2,1579447,2,40,1672669, +16,1417729,1310721,2,3,26,2,26,2,3,28,1,6,1,10,28, +34,63,38,3,26,1672497,16,1442305,1310721,2,3,26,2,26,2,3, +32,1,6,1,10,28,34,63,38,3,26,1,6,1,10,28, +34,63,38,1,10,28,1672499,14,1,1672545,16,1450497,1310721,2,3,26, +2,26,2,1,6,1,10,28,34,63,38,1,10,28,1672547,14, +1,1672631,16,1409537,1310721,2,3,26,2,26,2,1672623,14,105,115,32, +117,115,101,100,32,98,117,116,32,110,101,118,101,114,32,100, +101,102,105,110,101,100,0,1672571,1582117,12,8,1310721,2,3,26,1, +6,2,1,6,3,26,5,6,4,10,8,8,1672367,14,1,1672677, +14,1,1310721,2,5,26,1310721,4,18,1310721,2,11,28,1310721,4,1310721, +2,3,26,4,1310721,2,4,3,1,10,28,1310721,2,5,26,4, +1,1310721,2,7,26,4,1579447,2,1310721,2,9,26,4,1660573,12,8, +1581775,2,1672809,16,1310721,2,5,26,1585141,12,10,3,6,10,4,8, +15,1584907,12,8,3,1310721,2,2,1,1,1586239,12,8,1591423,12,8, +1310721,2,3,26,2,1672857,16,1672255,12,8,1673011,14,1310721,2,7,26, +1581747,2,10,3,6,10,4,8,3,1,1662009,12,8,1662319,12,8, +1579517,2,1,28,1,6,1,10,28,34,63,38,1,10,28,1672941, +16,1650659,12,8,1672897,14,1581757,2,1599439,12,8,1581763,2,1672989,16,1581767, +2,1,6,1,10,28,34,63,38,1,10,28,1672991,14,1,1673005, +16,3,3,1598733,12,8,1662411,12,8,1587189,12,8,1310721,2,9,26, +2,1672333,12,8,1310721,2,5,26,2,1,40,3,36,1673075,16,1310721, +2,5,26,2,1581747,2,1584967,12,8,1581747,2,1581753,2,10,40,1673109, +16,1581753,1581747,2,10,3,6,10,4,8,1661327,12,8,1310721,2,7, +26,2,1673131,14,1,1310721,2,11,26,1310721,4,18,1310721,2,3,28, +1310721,4,1310721,2,4,1310721,2,2,1,1672691,12,1673181,14,1,1310721,2, +3,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,4,1310721,2, +2,3,1672691,12,8,1,1673235,14,1,1310721,2,3,26,1310721,4,18, +1310721,2,5,28,1310721,4,1310721,2,4,1310721,2,2,1,1672691,12,1310721, +2,3,26,4,1310721,2,3,26,2,1,6,12,10,8,1673315,14, +1,1310721,2,5,26,1310721,4,18,1310721,2,7,28,1310721,4,1310721,2, +4,1310721,2,2,1660057,12,1310721,2,3,26,4,1310721,2,3,26,2, +1673249,12,1310721,2,5,26,4,1310721,2,3,26,2,1577851,12,8,1310721, +2,5,26,2,1673423,14,1,1310721,2,7,26,1310721,4,18,1310721,2, +7,28,1310721,4,1310721,2,4,1310721,2,2,1310721,2,3,26,4,1310721, +2,3,26,2,1579447,2,40,1673869,16,1417729,1310721,2,3,26,2,26, +2,3,40,3,36,1673571,16,1417729,1310721,2,3,26,2,26,2,9, +10,40,3,36,1,6,1,10,28,34,63,38,1,10,28,1673573, +14,1,1673583,16,3,1673645,14,1417729,1310721,2,3,26,2,26,2,13, +28,1,6,1,10,28,34,63,38,3,26,1,6,1,10,28, +34,63,38,1,10,28,1673831,16,1417729,1310721,2,3,26,2,26,2, +1584671,12,8,1425921,1310721,2,3,26,2,26,2,1584671,12,8,1434113,1310721, +2,3,26,2,26,2,1584671,12,8,1310721,2,5,26,1409537,1310721,2, +3,26,2,26,2,10,3,6,10,4,8,1310721,2,5,26,2, +2,1673823,16,1310721,2,5,26,2,2,1584671,12,8,1310721,2,5,26, +1,6,2,1,6,3,26,5,6,4,10,8,8,1673751,14,1, +1584671,12,8,1310721,2,3,26,1,6,2,1,6,3,26,5,6, +4,10,8,8,1673471,14,1,1584671,12,8,1,1673885,14,1,1310721,2, +7,26,1310721,4,18,1310721,2,7,28,1310721,4,1310721,2,4,1310721,2, +2,2,1674259,16,1310721,2,5,26,1310721,2,2,7,26,1582663,12,10, +3,6,10,4,8,1310721,2,3,26,1310721,2,5,26,2,1310721,2, +2,1,26,2,1310721,2,2,3,26,2,1583159,12,10,3,6,10, +4,8,1434113,1310721,2,3,26,2,26,1310721,2,2,5,26,2,10, +3,6,10,4,8,1442305,1310721,2,3,26,2,26,3,5,34,10, +3,6,10,4,8,1310721,2,2,1,26,2,13,28,1,6,1, +10,28,34,63,38,3,26,1674209,16,1310721,2,2,3,26,2,1579443, +2,40,3,36,1674187,16,1579443,1310721,2,2,3,26,2,3,26,10, +3,6,10,4,8,1310721,2,2,5,26,2,17,38,1572879,12,8, +1310721,2,1310721,2,2,7,26,1310721,2,2,7,26,1573731,12,26,3, +26,10,3,6,10,4,8,1673917,14,1,1674267,14,1,1310721,2,7, +26,1310721,4,18,1310721,2,21,28,1310721,4,1310721,2,3,26,4,1310721, +2,4,1581747,2,1310721,2,5,26,4,1581749,2,1310721,2,7,26,4, +1581751,2,1310721,2,9,26,4,1579449,2,1310721,2,11,26,4,1579451,2, +1310721,2,13,26,4,1579447,2,1310721,2,15,26,4,1579443,2,1310721,2, +17,26,4,1579449,1579447,2,10,3,6,10,4,8,1579451,1579447,2,10, +3,6,10,4,8,1579443,1,10,3,6,10,4,8,1581747,1310721,2, +2,10,3,6,10,4,8,1581749,1310721,2,3,26,2,1310721,2,2, +28,10,3,6,10,4,8,1581751,1310721,2,2,393217,26,10,3,6, +10,4,8,1966081,1584843,12,8,1310721,1584843,12,8,5,1584907,12,8,1581857, +2,1585201,12,1585301,12,8,15,1584907,12,8,1,1584671,12,8,1,1584671, +12,8,1,1,1,1,1586239,12,8,1591423,12,8,1672255,12,8,1579449, +2,1672333,12,8,1587189,12,8,1310721,2,3,26,2,11,26,1581747,2, +10,3,6,10,4,8,1579449,2,1673437,12,8,1310721,2,3,26,2, +13,26,1581747,2,10,3,6,10,4,8,1310721,2,19,26,1581747,2, +1310721,2,2,28,10,3,6,10,4,8,1310721,2,15,26,2,1583961, +12,8,1579449,1310721,2,11,26,2,10,3,6,10,4,8,1579451,1310721, +2,13,26,2,10,3,6,10,4,8,1581747,1310721,2,5,26,2, +10,3,6,10,4,8,1581749,1310721,2,7,26,2,10,3,6,10, +4,8,1581751,1310721,2,9,26,2,10,3,6,10,4,8,1579443,1310721, +2,17,26,2,10,3,6,10,4,8,1310721,2,19,26,2,1674917, +14,1,1310721,2,21,26,1310721,4,18,1310721,2,1,28,1310721,4,1, +1581753,2,1578301,12,8,1581749,2,1,6,1,10,28,34,63,38,3, +26,1675021,16,1581747,2,1581753,2,10,40,1,6,1,10,28,34,63, +38,1,10,28,1675023,14,1,1675041,16,1581753,2,1581747,2,1578301,12,8, +1572865,1572865,13,26,2,1578301,12,8,1540609,1540609,1025,26,1578301,12,8,1310721, +2,1966081,1578301,12,8,2064385,2097121,1578301,12,8,1,1675101,14,1,1310721,2, +1,26,1310721,4,18,1310721,2,1,28,1310721,4,1579445,1327617,10,3,6, +10,4,8,1311233,1,16385,1574329,12,8,1579447,1,10,3,6,10,4, +8,1579453,1,10,3,6,10,4,8,1579455,1,10,3,6,10,4, +8,1579457,1,10,3,6,10,4,8,1579459,1,10,3,6,10,4, +8,1579461,1,10,3,6,10,4,8,1579463,1,10,3,6,10,4, +8,1579465,1,10,3,6,10,4,8,1581777,1,10,3,6,10,4, +8,1581781,1,10,3,6,10,4,8,1581783,1,10,3,6,10,4, +8,1581755,1,10,3,6,10,4,8,1581757,1,10,3,6,10,4, +8,1581797,1675373,14,40,0,1675369,1582663,12,10,3,6,10,4,8,1581799, +1675401,14,41,0,1675397,1582663,12,10,3,6,10,4,8,1581801,1675429,14, +123,0,1675425,1582663,12,10,3,6,10,4,8,1581803,1675457,14,125,0, +1675453,1582663,12,10,3,6,10,4,8,1581805,1675485,14,91,0,1675481,1582663, +12,10,3,6,10,4,8,1581807,1675513,14,93,0,1675509,1582663,12,10, +3,6,10,4,8,1581809,1675541,14,44,0,1675537,1582663,12,10,3,6, +10,4,8,1581811,1675569,14,59,0,1675565,1582663,12,10,3,6,10,4, +8,1581813,1675597,14,61,0,1675593,1582663,12,10,3,6,10,4,8,1581815, +1675627,14,45,62,0,1675621,1582663,12,10,3,6,10,4,8,1581817,1675657, +14,43,43,0,1675651,1582663,12,10,3,6,10,4,8,1581819,1675687,14, +45,45,0,1675681,1582663,12,10,3,6,10,4,8,1581821,1675715,14,42, +0,1675711,1582663,12,10,3,6,10,4,8,1581823,1675743,14,38,0,1675739, +1582663,12,10,3,6,10,4,8,1581825,1675771,14,58,0,1675767,1582663,12, +10,3,6,10,4,8,1581827,1675799,14,35,0,1675795,1582663,12,10,3, +6,10,4,8,1581829,1675827,14,45,0,1675823,1582663,12,10,3,6,10, +4,8,1581835,1675855,14,46,0,1675851,1582663,12,10,3,6,10,4,8, +1581831,1675885,14,38,38,0,1675879,1582663,12,10,3,6,10,4,8,1581833, +1675915,14,124,124,0,1675909,1582663,12,10,3,6,10,4,8,1581837,1675945, +14,102,110,0,1675939,1582663,12,10,3,6,10,4,8,1581839,1675977,14, +108,101,116,0,1675969,1582663,12,10,3,6,10,4,8,1581841,1676013,14, +99,111,110,115,116,0,1676001,1582663,12,10,3,6,10,4,8,1581843, +1676047,14,101,108,115,101,0,1676037,1582663,12,10,3,6,10,4,8, +1581845,1676077,14,97,115,0,1676071,1582663,12,10,3,6,10,4,8,1581847, +1676121,14,117,110,99,104,101,99,107,101,100,0,1676101,1582663,12,10, +3,6,10,4,8,1581849,1676165,14,105,109,109,101,100,105,97,116, +101,0,1676145,1582663,12,10,3,6,10,4,8,1581851,1676193,14,109,0, +1676189,1582663,12,10,3,6,10,4,8,1581853,1676221,14,100,0,1676217,1582663, +12,10,3,6,10,4,8,1581855,1676249,14,117,0,1676245,1582663,12,10, +3,6,10,4,8,1581857,1676285,14,115,116,97,114,116,0,1676273,1582663, +12,10,3,6,10,4,8,1581859,1676319,14,109,97,105,110,0,1676309, +1582663,12,10,3,6,10,4,8,1581861,1676353,14,102,114,101,101,0, +1676343,1582663,12,10,3,6,10,4,8,1581863,1676385,14,111,119,110,0, +1676377,1582663,12,10,3,6,10,4,8,1581865,1676419,14,113,110,101,119, +0,1676409,1582663,12,10,3,6,10,4,8,1581867,1676459,14,101,110,104, +97,110,99,101,0,1676443,1582663,12,10,3,6,10,4,8,1581869,1676495, +14,109,105,99,114,111,0,1676483,1582663,12,10,3,6,10,4,8, +1581871,1676525,14,118,109,0,1676519,1582663,12,10,3,6,10,4,8,1, +1676551,14,1,1310721,2,1,26,1310721,4,18,1310721,2,1,28,1310721,4, +1676585,14,33,0,1676581,1676633,14,48,32,36,32,48,32,126,32,45, +32,124,32,51,49,32,62,32,49,32,43,0,1676591,1596655,12,8, +1676651,14,33,33,0,1676645,1676703,14,48,32,36,32,48,32,126,32, +45,32,124,32,51,49,32,62,32,48,32,126,32,45,0,1676657, +1596655,12,8,1676719,14,126,0,1676715,1676735,14,45,49,32,94,0,1676725, +1596655,12,8,1676751,14,45,0,1676747,1676769,14,48,32,126,32,45,0, +1676757,1596655,12,8,1676785,14,42,0,1676781,21,1676797,14,42,0,1676793,1595637, +12,8,1676813,14,47,0,1676809,21,1676831,14,105,100,105,118,0,1676821, +1595637,12,8,1676847,14,37,0,1676843,21,1676865,14,105,109,111,100,0, +1676855,1595637,12,8,1676881,14,43,0,1676877,19,1676893,14,43,0,1676889,1595637, +12,8,1676909,14,45,0,1676905,19,1676921,14,45,0,1676917,1595637,12,8, +1676939,14,60,60,0,1676933,17,1676957,14,105,115,104,108,0,1676947,1595637, +12,8,1676975,14,62,62,0,1676969,17,1676987,14,62,0,1676983,1595637,12, +8,1677003,14,60,0,1676999,15,1677015,14,60,0,1677011,1595637,12,8,1677031, +14,62,0,1677027,15,1677047,14,126,32,60,0,1677039,1595637,12,8,1677065, +14,60,61,0,1677059,15,1677089,14,126,32,60,32,49,32,94,0, +1677073,1595637,12,8,1677107,14,62,61,0,1677101,15,1677127,14,60,32,49, +32,94,0,1677115,1595637,12,8,1677145,14,61,61,0,1677139,13,1677199,14, +45,32,48,32,36,32,48,32,126,32,45,32,124,32,51,49, +32,62,32,49,32,43,0,1677153,1595637,12,8,1677217,14,33,61,0, +1677211,13,1677275,14,45,32,48,32,36,32,48,32,126,32,45,32, +124,32,51,49,32,62,32,48,32,126,32,45,0,1677225,1595637,12, +8,1677291,14,38,0,1677287,11,1677303,14,38,0,1677299,1595637,12,8,1677319, +14,94,0,1677315,9,1677331,14,94,0,1677327,1595637,12,8,1677347,14,124, +0,1677343,7,1677359,14,124,0,1677355,1595637,12,8,1677377,14,38,38,0, +1677371,3,1,10,28,1,1595637,12,8,1677405,14,124,124,0,1677399,5, +1,10,28,1,1595637,12,8,1581813,2,1594897,12,8,1581815,2,1594897,12, +8,1581817,2,1594897,12,8,1581819,2,1594897,12,8,1677475,14,108,101,116, +0,1677467,1652777,17,1596967,12,8,1677497,14,105,102,0,1677491,1652827,17,1596967, +12,8,1677525,14,119,104,105,108,101,0,1677513,1653725,17,1596967,12,8, +1677549,14,102,111,114,0,1677541,1654033,17,1596967,12,8,1677577,14,98,114, +101,97,107,0,1677565,1655147,17,1596967,12,8,1677611,14,99,111,110,116, +105,110,117,101,0,1677593,1655315,17,1596967,12,8,1677641,14,114,101,116, +117,114,110,0,1677627,1655439,17,1596967,12,8,1677677,14,117,110,99,104, +101,99,107,101,100,0,1677657,1656037,17,1596967,12,8,1677701,14,97,115, +109,0,1677693,1658683,17,33,34,1596967,12,8,1677733,14,113,117,111,116, +101,0,1677721,1659805,33,1596967,12,8,1677763,14,115,105,122,101,111,102, +0,1677749,1627797,33,1596967,12,8,1677793,14,116,121,112,101,111,102,0, +1677779,1627891,33,1596967,12,8,1,1677813,14,1,1310721,2,1,26,1310721,4, +18,1310721,2,5,28,1310721,4,1575681,12,8,1675115,12,8,1572865,11,26, +2,1673899,12,8,1676565,12,8,1627981,12,8,1581747,1,10,3,6,10, +4,8,1581749,1,10,3,6,10,4,8,1581751,524289,10,3,6,10, +4,8,1581753,1,10,3,6,10,4,8,1579449,1579447,2,10,3,6, +10,4,8,1579451,1,10,3,6,10,4,8,1,1,1,1,1586239, +12,8,1591423,12,8,1672255,12,8,1579449,2,1672333,12,8,1581747,2,1581753, +2,10,40,1678045,16,1581753,1581747,2,10,3,6,10,4,8,1310721,2, +1581859,2,1583635,12,10,3,6,10,4,8,1310721,2,2,1,40,3, +36,1678149,16,1417729,1310721,2,2,26,2,3,28,1,6,1,10,28, +34,63,38,3,26,1,6,1,10,28,34,63,38,1,10,28, +1678151,14,1,1678197,16,1442305,1310721,2,2,26,2,3,32,1,6,1, +10,28,34,63,38,1,10,28,1678199,14,1,1678257,16,1310721,2,3, +26,1425921,1310721,2,2,26,2,10,3,6,10,4,8,1310721,2,3, +26,2,1,6,12,10,8,8,1573195,12,8,1,1678271,14,1,1310721, +2,5,26,1310721,4,18,1,838913,0,115,116,97,114,116,0,4, +1048576,0,77,87,0,4,262144,0,72,69,65,80,95,76,79,0, +4,655360,0,72,69,65,80,95,72,73,0,4,655360,0,90,70, +80,0,4,786432,0,71,79,82,71,0,4,917504,0,70,66,79, +84,0,4,983040,0,70,84,79,80,0,4,983040,0,82,83,48, +0,4,1015808,0,77,65,82,75,48,0,4,16384,0,77,65,82, +75,78,0,4,1032192,0,68,83,76,79,0,4,1048560,0,68,83, +72,73,0,4,729683222,0,77,65,71,73,67,0,1,786439,2,105, +110,115,116,97,108,108,0,6,0,201334290,105,100,105,118,0,6, +1,201340946,105,109,111,100,0,6,2,201347602,105,115,104,108,0,1, +786539,2,112,117,116,99,0,1,786563,0,103,101,116,99,0,1, +786580,0,101,111,102,0,1,786597,0,104,97,108,116,0,1,786615, +2,112,117,116,115,0,1,786685,2,112,117,116,110,0,1,786865, +2,115,116,114,108,101,110,0,1,786926,4,115,116,114,101,113, +0,1,787069,6,119,99,111,112,121,0,1,787164,6,119,102,105, +108,108,0,3,787252,0,112,111,119,50,0,2,787284,0,122,114, +101,109,0,1,787285,4,122,117,100,105,118,0,1,787505,4,122, +100,105,118,0,1,787682,4,122,109,111,100,0,1,787766,4,122, +115,104,108,0,4,6,0,72,68,82,0,2,787804,0,104,98, +117,109,112,0,3,787805,0,102,114,101,101,108,0,2,787837,0, +103,99,95,114,117,110,115,0,2,787838,0,103,99,95,102,114, +101,101,100,0,2,787839,0,103,99,95,97,117,116,111,0,1, +787840,0,104,101,97,112,95,105,110,105,116,0,1,787937,2,105, +115,111,98,106,0,1,788055,0,111,111,109,0,1,788105,5,110, +101,119,95,116,0,1,789281,0,103,99,0,1,788568,3,110,101, +119,0,1,788593,2,111,115,105,122,101,0,1,788643,2,116,121, +112,101,111,102,95,111,98,106,0,1,788691,2,114,101,108,101, +97,115,101,0,1,788925,130,102,114,101,101,0,1,788958,4,111, +119,110,0,2,789011,0,109,115,95,116,111,112,0,2,789012,0, +109,115,95,111,118,101,114,0,2,789013,0,103,99,95,108,111, +0,2,789014,0,103,99,95,104,105,0,1,789015,2,109,97,114, +107,0,1,789150,4,109,97,114,107,114,97,110,103,101,0,1, +789210,0,100,114,97,105,110,0,1,837465,0,103,99,95,114,111, +111,116,115,0,1,789635,0,108,105,118,101,95,111,98,106,101, +99,116,115,0,4,8192,0,73,72,78,0,4,655616,0,82,95, +73,72,0,4,663808,0,82,95,80,79,79,76,0,4,704512,0, +82,95,80,79,79,76,69,78,68,0,4,4096,0,78,83,0, +4,704768,0,82,95,83,89,77,0,4,704768,0,115,110,97,109, +101,0,4,708864,0,115,107,105,110,100,0,4,712960,0,115,118, +97,108,0,4,717056,0,115,115,105,103,0,4,721152,0,115,102, +108,97,103,0,4,725248,0,115,102,105,120,0,4,729344,0,115, +110,101,120,116,0,4,512,0,78,76,0,4,733440,0,108,110, +97,109,101,0,4,733952,0,108,115,108,111,116,0,4,734464,0, +108,104,111,109,101,0,4,734976,0,108,115,116,97,116,101,0, +4,735488,0,108,111,119,110,101,114,0,4,736000,0,108,117,115, +101,100,0,4,736512,0,108,97,114,114,0,4,737024,0,108,111, +119,110,0,4,8192,0,78,70,0,4,737536,0,102,115,105,116, +101,0,4,745728,0,102,110,101,120,116,0,4,753920,0,82,95, +83,78,65,80,0,4,762112,0,82,95,81,66,85,70,0,4, +770304,0,82,95,83,82,67,0,4,771328,0,82,95,83,69,81, +0,4,773376,0,98,110,97,109,101,0,4,773440,0,98,112,114, +101,99,0,4,773504,0,98,115,101,113,0,4,773568,0,98,108, +101,110,0,4,773632,0,117,110,97,109,101,0,4,773664,0,117, +115,101,113,0,4,773696,0,117,108,101,110,0,4,773728,0,97, +110,97,109,101,0,4,773792,0,97,98,105,110,0,4,773888,0, +107,111,112,115,0,4,774400,0,82,95,67,84,88,0,4,776448, +0,82,95,76,79,79,80,0,4,777472,0,82,95,65,83,77, +0,4,0,0,84,95,69,79,70,0,4,1,0,84,95,78, +85,77,0,4,2,0,84,95,73,68,0,4,3,0,84,95, +83,84,82,0,4,4,0,84,95,79,80,0,4,1,0,75, +95,70,78,0,4,2,0,75,95,71,86,65,82,0,4,3, +0,75,95,71,65,82,82,0,4,4,0,75,95,67,79,78, +83,84,0,4,5,0,75,95,75,87,0,4,6,0,75,95, +69,78,72,0,4,7,0,75,95,84,89,80,69,0,4,1, +0,70,95,68,69,70,0,4,2,0,70,95,83,73,71,0, +4,4,0,70,95,85,78,84,89,80,69,68,0,4,8,0, +70,95,83,84,77,84,0,4,16,0,70,95,69,88,80,82, +0,4,32,0,70,95,84,79,80,0,4,64,0,70,95,70, +65,76,76,66,65,67,75,0,4,128,0,70,95,71,69,78, +0,4,0,0,83,95,80,76,65,73,78,0,4,1,0,83, +95,77,0,4,2,0,83,95,85,0,4,3,0,83,95,68, +0,4,4,0,83,95,68,69,65,68,0,4,5,0,83,95, +67,72,73,76,68,0,4,6,0,83,95,66,79,82,82,79, +87,0,4,0,0,67,95,80,76,65,73,78,0,4,1,0, +67,95,70,82,69,83,72,0,4,2,0,67,95,86,65,82, +0,4,0,0,76,86,95,78,79,78,69,0,4,1,0,76, +86,95,76,79,67,65,76,0,4,2,0,76,86,95,71,76, +79,66,65,76,0,4,3,0,76,86,95,69,76,69,77,0, +4,4,0,76,86,95,79,84,72,69,82,0,4,0,0,79, +95,81,0,4,1,0,79,95,76,68,0,4,2,0,79,95, +83,84,0,4,3,0,79,95,80,73,67,75,0,4,4,0, +79,95,68,82,79,80,0,4,5,0,79,95,83,87,65,80, +0,4,6,0,79,95,67,65,76,76,0,4,7,0,79,95, +74,77,80,0,4,8,0,79,95,74,90,0,4,9,0,79, +95,82,69,84,0,4,13,0,79,95,65,68,68,0,4,14, +0,79,95,83,85,66,0,4,22,0,88,79,80,48,0,4, +-2,0,83,69,81,82,69,70,0,2,789721,0,110,101,110,104, +0,2,789722,0,112,111,111,108,116,111,112,0,2,789723,0,110, +115,121,109,0,2,789724,0,117,98,97,115,101,0,2,789725,0, +118,105,115,108,111,0,2,789726,0,110,102,105,120,117,0,2, +789727,0,102,105,120,102,114,101,101,0,2,789728,0,110,98,105, +110,0,2,789729,0,110,117,110,0,2,789730,0,110,97,115,103, +0,2,789731,0,110,107,111,112,0,2,789732,0,115,101,113,116, +111,112,0,2,789733,0,109,110,101,109,0,2,789757,0,99,104, +101,99,107,95,111,110,0,2,789758,0,116,107,0,2,789759,0, +116,118,0,2,789760,0,116,103,108,117,101,0,2,789761,0,116, +108,105,110,101,0,2,789762,0,108,105,110,101,0,2,789763,0, +99,104,0,2,789764,0,99,104,107,105,110,100,0,2,789765,0, +99,104,112,116,114,0,2,789766,0,113,100,111,110,101,0,2, +789767,0,105,115,112,0,3,789768,0,116,98,117,102,0,2,790868, +0,112,107,95,104,97,115,0,2,790869,0,112,107,95,116,107, +0,2,790870,0,112,107,95,116,118,0,2,790871,0,112,107,95, +103,108,117,101,0,2,790872,0,112,107,95,108,105,110,101,0, +2,790873,0,104,101,114,101,0,2,790874,0,104,100,101,108,116, +97,0,2,790875,0,104,108,105,109,105,116,0,2,790876,0,112, +116,111,112,0,2,790877,0,110,108,111,99,0,2,790878,0,108, +98,97,115,101,0,2,790879,0,102,100,101,112,116,104,0,2, +790880,0,102,115,108,111,116,115,0,2,790881,0,102,99,104,107, +0,2,790882,0,102,114,101,116,0,2,790883,0,108,105,118,101, +0,2,790884,0,98,120,100,101,112,116,104,0,2,790885,0,101, +112,105,99,104,97,105,110,0,2,790886,0,102,102,114,97,109, +101,0,2,790887,0,105,110,102,110,0,2,790888,0,108,115,112, +0,2,790889,0,108,111,111,112,98,97,115,101,0,2,790890,0, +99,116,120,115,112,0,2,790891,0,115,110,97,112,116,111,112, +0,2,790892,0,108,118,0,2,790893,0,108,118,107,0,2,790894, +0,108,118,105,0,2,790895,0,107,119,95,101,120,112,114,0, +2,790896,0,120,99,108,115,0,2,790897,0,102,116,121,0,2, +790898,0,115,95,108,112,0,2,790899,0,115,95,114,112,0,2, +790900,0,115,95,108,98,0,2,790901,0,115,95,114,98,0,2, +790902,0,115,95,108,107,0,2,790903,0,115,95,114,107,0,2, +790904,0,115,95,99,111,109,109,97,0,2,790905,0,115,95,115, +101,109,105,0,2,790906,0,115,95,101,113,0,2,790907,0,115, +95,97,114,114,111,119,0,2,790908,0,115,95,105,110,99,0, +2,790909,0,115,95,100,101,99,0,2,790910,0,115,95,115,116, +97,114,0,2,790911,0,115,95,97,109,112,0,2,790912,0,115, +95,99,111,108,111,110,0,2,790913,0,115,95,104,97,115,104, +0,2,790914,0,115,95,109,105,110,117,115,0,2,790915,0,115, +95,97,110,100,97,110,100,0,2,790916,0,115,95,111,114,111, +114,0,2,790917,0,115,95,100,111,116,0,2,790918,0,115,95, +102,110,0,2,790919,0,115,95,118,97,114,0,2,790920,0,115, +95,99,111,110,115,116,0,2,790921,0,115,95,101,108,115,101, +0,2,790922,0,115,95,97,115,0,2,790923,0,115,95,117,110, +99,104,101,99,107,101,100,0,2,790924,0,115,95,105,109,109, +101,100,105,97,116,101,0,2,790925,0,115,95,109,0,2,790926, +0,115,95,100,0,2,790927,0,115,95,117,0,2,790928,0,115, +95,115,116,97,114,116,0,2,790929,0,115,95,109,97,105,110, +0,2,790930,0,115,95,102,114,101,101,0,2,790931,0,115,95, +111,119,110,0,2,790932,0,115,95,113,110,101,119,0,2,790933, +0,115,95,101,110,104,97,110,99,101,0,2,790934,0,115,95, +109,105,99,114,111,0,2,790935,0,115,95,118,109,0,4,0, +0,84,89,95,87,79,82,68,0,4,1,0,84,89,95,73, +78,84,0,4,2,0,84,89,95,67,72,65,82,0,4,3, +0,84,89,95,66,79,79,76,0,4,4,0,84,89,95,86, +79,73,68,0,4,5,0,84,89,95,80,84,82,0,4,6, +0,84,89,95,65,82,82,0,4,7,0,84,89,95,70,78, +0,4,8,0,84,89,95,83,84,82,85,67,84,0,4,9, +0,84,89,95,86,65,82,0,4,10,0,84,89,95,71,69, +78,0,4,638976,0,82,95,84,89,0,4,655360,0,82,95,84, +89,69,78,68,0,4,1024,0,78,84,76,0,4,778496,0,108, +116,121,112,101,0,4,779008,0,108,116,97,103,0,4,779520,0, +115,116,121,112,101,0,2,790936,0,116,121,116,111,112,0,2, +790937,0,110,116,108,0,2,790938,0,116,119,111,114,100,0,2, +790939,0,116,105,110,116,0,2,790940,0,116,99,104,97,114,0, +2,790941,0,116,98,111,111,108,0,2,790942,0,116,118,111,105, +100,0,2,790943,0,116,99,95,111,110,0,2,790944,0,120,116, +121,0,2,790945,0,120,122,101,114,111,0,2,790946,0,100,114, +121,0,3,790947,0,116,112,110,0,2,790955,0,110,116,112,0, +2,790956,0,100,116,121,0,2,790957,0,100,116,97,103,0,2, +790958,0,115,95,108,116,0,2,790959,0,115,95,103,116,0,2, +790960,0,115,95,115,104,114,0,2,790961,0,115,95,108,101,0, +2,790962,0,115,95,103,101,0,2,790963,0,115,95,101,113,101, +113,0,2,790964,0,115,95,110,101,0,2,790965,0,115,95,112, +108,117,115,0,2,790966,0,115,95,115,116,114,117,99,116,0, +2,790967,0,115,95,116,121,112,101,0,2,790968,0,115,95,116, +121,112,101,99,104,101,99,107,0,2,790969,0,115,95,111,110, +0,2,790970,0,115,95,111,102,102,0,2,790971,0,115,95,110, +101,119,0,2,790972,0,115,95,110,101,119,95,116,0,3,790973, +0,110,117,109,111,112,115,0,2,790985,0,110,110,117,109,111, +112,115,0,3,790986,0,103,98,105,110,100,0,2,790994,0,110, +103,98,105,110,100,0,1,790995,2,100,105,101,0,1,791058,4, +100,105,101,110,0,1,791143,2,99,101,114,114,0,1,791191,4, +99,101,114,114,110,0,1,791252,2,104,97,115,104,115,0,1, +791331,2,105,110,116,101,114,110,0,1,791579,6,97,100,100,115, +121,109,0,1,791817,2,108,111,111,107,117,112,0,1,791980,2, +112,111,112,115,121,109,115,0,1,792046,2,108,111,111,107,117, +112,115,0,1,792072,4,102,105,120,110,101,119,0,1,792237,4, +102,105,120,97,108,108,0,1,792483,4,115,101,116,108,105,116, +0,1,792335,2,101,109,105,116,0,1,792421,2,108,105,116,0, +1,792453,2,111,112,0,1,792540,2,106,109,112,0,1,792570,0, +104,111,108,101,0,1,792600,2,102,119,100,102,110,0,1,792650, +2,102,110,114,101,102,0,1,792726,2,99,97,108,108,110,97, +109,101,0,1,792792,2,108,97,100,100,114,0,1,792835,2,115, +116,114,108,105,116,0,1,792930,0,110,101,120,116,99,104,0, +1,793119,8,115,114,99,95,112,117,115,104,0,1,793594,0,115, +114,99,95,112,111,112,0,1,793790,2,105,115,111,112,99,104, +0,1,793890,4,111,112,112,114,101,102,105,120,0,1,794122,2, +105,115,100,105,103,0,1,794172,2,105,115,97,108,0,1,794300, +2,104,101,120,118,0,1,794421,0,101,115,99,99,104,0,1, +794584,2,111,112,108,101,120,0,1,794718,0,108,101,120,0,1, +795711,0,110,101,120,116,0,1,795943,0,110,101,120,116,48,0, +1,795780,0,112,101,101,107,0,1,796170,2,105,115,95,111,112, +0,1,796235,2,105,115,95,105,100,0,1,796300,2,97,99,99, +101,112,116,0,1,796404,2,101,120,112,101,99,116,0,1,796493, +0,110,97,109,101,0,1,796559,2,115,101,113,97,115,109,0, +1,797117,2,109,110,101,109,105,100,120,0,1,797208,4,101,109, +105,116,115,101,113,0,1,797448,2,97,100,100,107,111,112,0, +1,797557,2,98,105,110,102,105,110,100,0,1,797644,2,117,110, +102,105,110,100,0,1,797731,2,97,115,103,102,105,110,100,0, +1,797818,6,97,100,100,95,98,105,110,111,112,0,1,798327,4, +97,100,100,95,117,110,111,112,0,1,798483,6,97,100,100,95, +107,119,0,1,798555,2,102,105,110,100,108,111,99,0,1,798646, +2,110,101,119,108,111,99,0,1,798866,2,108,114,111,111,116, +0,1,798924,2,116,114,97,99,107,101,100,0,1,798962,2,116, +111,117,99,104,0,1,799195,2,112,108,97,105,110,117,115,101, +0,1,799324,2,102,114,101,101,118,97,114,0,1,799366,4,101, +120,105,116,95,118,97,114,115,0,1,799719,2,117,110,117,115, +101,100,95,99,104,101,99,107,0,1,799857,2,98,108,111,99, +107,95,99,108,111,115,101,0,1,799982,0,115,110,97,112,95, +112,117,115,104,0,1,800246,2,115,110,97,112,95,112,111,112, +0,1,800276,2,115,110,97,112,95,114,101,115,116,111,114,101, +0,1,800484,2,101,109,112,116,121,0,1,800549,2,115,110,97, +112,95,99,109,112,0,1,800943,2,115,116,97,116,101,110,97, +109,101,0,1,801150,6,109,101,114,103,101,95,101,114,114,0, +1,801331,4,109,101,114,103,101,0,1,801531,2,108,111,111,112, +99,104,101,99,107,0,1,801743,4,114,101,99,97,115,116,0, +1,802268,4,97,114,103,99,104,101,99,107,0,1,803023,2,101, +109,105,116,111,119,110,0,1,803061,6,97,115,115,105,103,110, +99,104,101,99,107,0,1,804792,2,114,101,116,99,104,101,99, +107,0,1,805272,2,104,97,110,100,117,112,0,1,805619,2,100, +105,115,99,97,114,100,0,1,805731,2,116,97,108,108,111,99, +0,1,805827,8,116,110,101,119,0,1,805990,2,116,112,116,114, +0,1,806166,4,116,97,114,114,0,1,807978,2,116,115,122,0, +1,806415,8,116,102,110,0,1,806865,4,116,118,97,114,0,1, +807050,6,116,105,110,115,116,0,1,807470,2,116,114,101,115,111, +108,118,101,0,1,808053,4,115,117,98,115,116,0,1,808541,2, +105,115,110,117,109,0,1,808646,2,105,115,97,103,103,0,1, +808717,2,105,115,112,116,114,0,1,808788,2,100,101,99,97,121, +0,1,808840,2,116,101,108,101,109,0,1,808923,4,99,111,109, +112,97,116,0,1,809397,6,117,110,105,102,121,0,1,809931,2, +116,112,114,105,110,116,0,1,816380,4,116,112,97,114,97,109, +116,97,103,0,1,810542,6,116,99,101,114,114,0,1,810737,6, +116,99,110,101,101,100,0,1,810927,2,116,112,102,105,110,100, +0,1,811014,2,105,115,116,121,112,101,110,97,109,101,0,1, +811121,0,99,108,111,115,101,97,110,103,108,101,0,1,811161,0, +112,116,121,112,101,0,1,824983,0,99,101,120,112,114,0,1, +812089,0,116,115,116,97,114,116,0,1,812378,0,100,116,121,112, +101,0,1,812554,0,107,119,95,115,116,114,117,99,116,0,1, +813484,0,107,119,95,116,121,112,101,0,1,813533,0,107,119,95, +116,121,112,101,99,104,101,99,107,0,1,813629,4,116,102,105, +110,100,102,105,101,108,100,0,1,813742,0,116,121,112,101,97, +114,103,0,1,823247,0,101,120,112,114,0,1,816964,0,114,118, +97,108,0,1,813898,0,107,119,95,115,105,122,101,111,102,0, +1,813945,0,107,119,95,116,121,112,101,111,102,0,1,813990,0, +116,121,112,101,115,95,105,110,105,116,0,1,814561,2,105,115, +110,117,109,111,112,0,1,814643,6,98,105,110,116,121,112,101, +0,1,816025,2,116,107,105,110,100,0,1,816050,2,116,115,105, +122,101,0,1,816074,2,116,110,97,109,101,0,1,816099,2,116, +108,101,110,0,1,816124,2,116,110,102,105,101,108,100,115,0, +1,816155,4,116,102,110,97,109,101,0,1,816200,4,116,102,116, +121,112,101,0,1,816247,4,116,102,111,102,102,0,1,816294,2, +116,114,101,116,0,1,816319,2,116,110,112,97,114,97,109,115, +0,1,816344,4,116,112,97,114,97,109,0,1,816426,2,116,114, +101,116,117,0,1,816453,2,116,110,97,114,103,115,0,1,816537, +4,116,97,114,103,0,1,816576,2,115,105,103,111,102,0,1, +816876,2,115,105,103,111,102,115,0,1,817013,4,107,119,99,97, +108,108,0,1,817148,0,115,116,109,116,111,110,108,121,0,1, +817215,6,97,114,103,115,0,1,817477,0,110,101,119,116,121,112, +101,100,0,1,817636,2,99,97,108,108,0,1,818192,2,101,110, +104,99,97,108,108,0,1,818550,0,112,114,105,109,97,114,121, +0,1,824221,0,98,108,111,99,107,101,120,112,114,0,1,819911, +4,105,110,99,100,101,99,0,1,820324,2,102,105,101,108,100, +0,1,820905,0,112,111,115,116,102,105,120,0,1,821888,0,117, +110,97,114,121,0,1,822716,2,98,105,110,0,1,823210,0,115, +116,111,114,101,107,101,101,112,0,1,823985,0,115,116,109,116, +115,116,97,114,116,0,1,825329,0,115,116,109,116,0,1,824586, +0,99,112,114,105,109,0,1,825764,2,108,111,99,97,108,100, +101,99,108,0,1,825578,0,98,108,111,99,107,0,1,825695,0, +115,117,98,115,116,109,116,0,1,826388,0,107,119,95,118,97, +114,0,1,826413,0,107,119,95,105,102,0,1,826651,4,108,111, +111,112,95,112,117,115,104,0,1,826817,0,108,111,111,112,95, +112,111,112,0,1,826862,0,107,119,95,119,104,105,108,101,0, +1,827016,0,107,119,95,102,111,114,0,1,827405,2,108,111,111, +112,101,120,105,116,0,1,827573,0,107,119,95,98,114,101,97, +107,0,1,827657,0,107,119,95,99,111,110,116,105,110,117,101, +0,1,827719,0,107,119,95,114,101,116,117,114,110,0,1,828018, +0,107,119,95,117,110,99,104,101,99,107,101,100,0,1,828067, +0,97,115,109,98,108,111,99,107,0,1,829341,0,107,119,95, +97,115,109,0,1,829372,1,103,114,97,98,0,1,829692,3,113, +110,101,119,0,1,829759,67,113,100,117,112,0,1,829826,68,119, +99,111,112,121,116,111,0,1,829902,0,107,119,95,113,117,111, +116,101,0,1,830028,3,116,111,107,101,110,115,0,1,830254,130, +102,101,101,100,0,1,830286,0,99,116,120,95,112,117,115,104, +0,1,830663,0,99,116,120,95,112,111,112,0,1,831004,4,102, +110,95,98,101,103,105,110,0,1,831159,0,112,114,111,108,111, +103,117,101,0,1,831205,0,101,112,105,108,111,103,117,101,0, +1,831286,0,98,111,100,121,0,1,831473,2,102,110,100,101,102, +0,1,833572,2,103,100,101,99,108,0,1,834211,0,103,99,111, +110,115,116,0,1,834283,0,101,110,104,100,101,102,0,1,835600, +0,116,111,112,105,116,101,109,0,1,836127,0,117,110,105,116, +95,116,111,112,0,1,836166,2,117,110,105,116,95,101,110,100, +0,1,836345,4,99,111,109,112,105,108,101,95,108,105,115,116, +0,1,836572,2,99,111,109,112,105,108,101,0,1,836597,2,108, +111,97,100,0,1,836624,2,101,118,97,108,0,1,836664,2,101, +118,97,108,115,0,1,836718,2,98,108,111,98,95,101,109,105, +116,0,1,836949,2,98,108,111,98,95,108,111,97,100,0,1, +837140,4,98,117,105,108,100,95,105,109,97,103,101,0,1,837557, +0,122,99,95,105,110,105,116,0,1,838282,0,122,99,95,116, +97,98,108,101,115,0,0 +}; +#define GLEN 57463 diff --git a/docs/history/11-types-enhancements/code/io_linux.c b/docs/history/11-types-enhancements/code/io_linux.c new file mode 100644 index 0000000..7ea34e0 --- /dev/null +++ b/docs/history/11-types-enhancements/code/io_linux.c @@ -0,0 +1,6 @@ +#include +int J; +static int pk(void){int c=getchar();ungetc(c,stdin);return c==EOF;} +char Q(void){int c=getchar();J=pk();return c;} +void G(char c){putchar(c);} +__attribute__((constructor)) static void init(void){J=pk();} diff --git a/docs/history/11-types-enhancements/code/mkgenome.c b/docs/history/11-types-enhancements/code/mkgenome.c new file mode 100644 index 0000000..8fb2cc5 --- /dev/null +++ b/docs/history/11-types-enhancements/code/mkgenome.c @@ -0,0 +1,16 @@ +/* turns a genome image (little-endian 32-bit words on stdin, as written by + tools/build.zc) into genome.h */ +#include +#define GORG 0xC0000 +int main(void) +{ + unsigned char b[4]; int n = 0; + printf("#define GORG %d\nstatic const int genome[] = {", GORG); + while (fread(b, 1, 4, stdin) == 4) { + int w = (int)(b[0] | b[1] << 8 | b[2] << 16 | (unsigned)b[3] << 24); + printf("%s%d", !n ? "\n" : n % 16 ? "," : ",\n", w); + n++; + } + printf("\n};\n#define GLEN %d\n", n); + return n ? 0 : 1; +} diff --git a/docs/history/11-types-enhancements/code/src/compiler.zc b/docs/history/11-types-enhancements/code/src/compiler.zc new file mode 100644 index 0000000..7e86b07 --- /dev/null +++ b/docs/history/11-types-enhancements/code/src/compiler.zc @@ -0,0 +1,2253 @@ +// ============================================================================ +// compiler.zc: the resident zc compiler. it lives in the genome next to the +// runtime, compiles source straight to vm words in memory, and is visible to +// the programs it compiles: every function and global here can be called or +// patched by name, and parse words (immediate fns) run inside it. +// ============================================================================ + +// ---- compiler data region (fixed addresses, 0xA0100 .. 0xBE100) ---- +const IHN = 8192; +const R_IH = 0xA0100; // intern hash table +const R_POOL = 0xA2100; // interned strings: [symbol head] chars 0 +const R_POOLEND = 0xAC000; +const NS = 4096; // symbol table, one array per field +const R_SYM = 0xAC100; +const sname = 0xAC100, skind = 0xAD100, sval = 0xAE100, ssig = 0xAF100; +const sflag = 0xB0100, sfix = 0xB1100, snext = 0xB2100; +const NL = 512; // locals, one array per field +const lname = 0xB3100, lslot = 0xB3300, lhome = 0xB3500, lstate = 0xB3700; +const lowner = 0xB3900, lused = 0xB3B00, larr = 0xB3D00, lown = 0xB3F00; // lown: has owned an object +const NF = 8192; // patch nodes: a site and a link +const fsite = 0xB4100, fnext = 0xB6100; +const R_SNAP = 0xB8100; // analyzer snapshots +const R_QBUF = 0xBA100; // token buffer for quote and grab +const R_SRC = 0xBC100; // input source stack, 12 words per frame +const R_SEQ = 0xBC500; // operator code sequences +const bname = 0xBCD00, bprec = 0xBCD40, bseq = 0xBCD80, blen = 0xBCDC0; // binary operators +const uname = 0xBCE00, useq = 0xBCE20, ulen = 0xBCE40; // unary operators +const aname = 0xBCE60, abin = 0xBCEA0; // compound assignment +const kops = 0xBCF00; // every operator spelling the lexer knows +const R_CTX = 0xBD100; // saved compiler contexts for nested compiles +const R_LOOP = 0xBD900; // loop stack, 6 words per loop +const R_ASM = 0xBDD00; // asm block labels + +// ---- token kinds, symbol kinds and flags ---- +const T_EOF = 0, T_NUM = 1, T_ID = 2, T_STR = 3, T_OP = 4; +const K_FN = 1, K_GVAR = 2, K_GARR = 3, K_CONST = 4, K_KW = 5, K_ENH = 6, K_TYPE = 7; +const F_DEF = 1, F_SIG = 2, F_UNTYPED = 4, F_STMT = 8, F_EXPR = 16, F_TOP = 32, F_FALLBACK = 64, F_GEN = 128; +// variable states for the lifetime analyzer +const S_PLAIN = 0, S_M = 1, S_U = 2, S_D = 3, S_DEAD = 4, S_CHILD = 5, S_BORROW = 6; +// expression classes: plain word, a new object, or a bare variable (C_VAR + index) +const C_PLAIN = 0, C_FRESH = 1, C_VAR = 2; +// lvalue kinds +const LV_NONE = 0, LV_LOCAL = 1, LV_GLOBAL = 2, LV_ELEM = 3, LV_OTHER = 4; +// opcodes +const O_Q = 0, O_LD = 1, O_ST = 2, O_PICK = 3, O_DROP = 4, O_SWAP = 5, O_CALL = 6, O_JMP = 7; +const O_JZ = 8, O_RET = 9, O_ADD = 13, O_SUB = 14; +const XOP0 = 22; // first enhancement opcode +const SEQREF = -2; // in an operator sequence: the next word names a symbol + +// ---- state ---- +let nenh, pooltop, nsym, ubase, vislo, nfixu, fixfree; +let nbin, nun, nasg, nkop, seqtop; +let mnem = "q@!$_~cjzr,?.+-*&|^><%"; +let check_on = 1; // set to 0 to switch the analyzer off for what follows +let tk, tv, tglue, tline, line = 1, ch, chkind, chptr, qdone, isp = -1; +let tbuf[1100]; +let pk_has, pk_tk, pk_tv, pk_glue, pk_line; // a peeked token +let here, hdelta, hlimit, ptop; +let nloc, lbase, fdepth, fslots, fchk, fret, live, bxdepth, epichain, fframe, infn; +let lsp, loopbase, ctxsp, snaptop; +let lv, lvk, lvi, kw_expr, xcls, fty; +// interned spellings the parser compares against +let s_lp, s_rp, s_lb, s_rb, s_lk, s_rk, s_comma, s_semi, s_eq, s_arrow, s_inc, s_dec; +let s_star, s_amp, s_colon, s_hash, s_minus, s_andand, s_oror; +let s_dot, s_fn, s_var, s_const, s_else, s_as, s_unchecked, s_immediate, s_m, s_d, s_u; +let s_start, s_main, s_free, s_own, s_qnew, s_enhance, s_micro, s_vm; + +// ---- types: kinds, tables and parser state (see the types section) ---- +const TY_WORD = 0, TY_INT = 1, TY_CHAR = 2, TY_BOOL = 3, TY_VOID = 4, TY_PTR = 5, TY_ARR = 6; +const TY_FN = 7, TY_STRUCT = 8, TY_VAR = 9, TY_GEN = 10; +const R_TY = 0x9C000, R_TYEND = 0xA0000, NTL = 1024; // the first NTL words list every record +const ltype = 0xBE100, ltag = 0xBE300, stype = 0xBE500; // type per local, declared tag per local, type per symbol +let tytop, ntl, tword, tint, tchar, tbool, tvoid; +let tc_on; // the optional typechecker, off unless a program says typecheck on; +let xty, xzero, dry; // type of the last expression, was it a literal 0, and dry runs (typeof) +let tpn[8], ntp; // type parameters in scope +let dty, dtag; // result of dtype() +let s_lt, s_gt, s_shr, s_le, s_ge, s_eqeq, s_ne, s_plus, s_struct, s_type, s_typecheck, s_on, s_off, s_new, s_new_t; +let numops[12], nnumops; +let gbind[8], ngbind; // explicit type arguments for the next call: f(...) + +// ---- errors ---- +fn die(msg) { puts("zc: line "); putn(tline); puts(": "); puts(msg); putc(10); halt(); return 0; } +fn dien(name, msg) { puts("zc: line "); putn(tline); puts(": "); puts(name); puts(": "); puts(msg); putc(10); halt(); return 0; } +// analyzer errors only count in checked, reachable code +fn cerr(msg) { if (fchk && live) die(msg); return 0; } +fn cerrn(i, msg) { if (fchk && live) dien(lname[i], msg); return 0; } + +// ---- interning ---- +fn hashs(s) { let h = 0; while (*s) { h = h * 31 + *s; s++; } return h & (IHN - 1); } +fn intern(s) +{ + let h = hashs(s), p, n; + while (1) { + p = R_IH[h]; + if (!p) break; + if (streq(p, s)) return p; + h = (h + 1) & (IHN - 1); + } + n = strlen(s); + if (pooltop + n + 2 >= R_POOLEND) die("string pool full"); + pooltop[0] = 0; + p = pooltop + 1; + wcopy(p, s, n + 1); + pooltop = pooltop + n + 2; + R_IH[h] = p; + return p; +} + +// ---- symbols ---- +// each interned name keeps the index (+1) of its newest symbol in p[-1], and +// each symbol links to the one it shadows, so lookup is one step. +fn addsym(name, kind, val) +{ + let i = nsym; + if (nsym >= NS) die("symbol table full"); + nsym++; + sname[i] = name; skind[i] = kind; sval[i] = val; ssig[i] = 0; sflag[i] = F_DEF; sfix[i] = 0; stype[i] = 0; + snext[i] = name[-1]; + name[-1] = i + 1; + return i; +} +// while an image builds, names below vislo are hidden, except keywords +fn lookup(name) +{ + let i = name[-1] - 1; + while (i >= 0) { + if (i >= vislo || skind[i] == K_KW || skind[i] == K_TYPE) return i; + i = snext[i] - 1; + } + return -1; +} +fn popsyms(to) { while (nsym > to) { nsym--; sname[nsym][-1] = snext[nsym]; } return 0; } +fn lookups(s) { return lookup(intern(s)); } + +// patch nodes +fn fixnew(site, link) +{ + let i = fixfree; + if (i) fixfree = fnext[i]; + else { nfixu++; i = nfixu; if (i >= NF) die("too many forward references"); } + fsite[i] = site; fnext[i] = link; + return i; +} +// patch every lit in a chain to point at v, and recycle the nodes +fn fixall(head, v) +{ + let n; + while (head) { setlit(fsite[head], v); n = fnext[head]; fnext[head] = fixfree; fixfree = head; head = n; } + return 0; +} + +// ---- code emission ---- +fn emit(w) { if (dry) return 0; if (here >= hlimit) die("program too big"); *(here + hdelta) = w; here++; return 0; } +fn lit(v) { emit(v + v + 1); return 0; } +fn op(k) { emit(k + k); return 0; } +fn setlit(at, v) { if (dry) return 0; *(at + hdelta) = v + v + 1; return 0; } +fn jmp(t) { lit(t); op(O_JMP); return 0; } +// a lit that will be patched later; returns its address +fn hole() { let a = here; lit(MW - 1); return a; } +fn fwdfn(name) { let s = addsym(name, K_FN, 0); sflag[s] = 0; return s; } +fn fnref(s) +{ + if (sflag[s] & F_DEF) { lit(sval[s]); return 0; } + if (dry) return 0; + sfix[s] = fixnew(here, sfix[s]); + lit(MW - 1); // a jump to MW-1 halts if it ever runs unpatched + return 0; +} +fn callname(name) { let s = lookup(name); if (s < 0) s = fwdfn(name); fnref(s); op(O_CALL); return 0; } +fn laddr(k) { lit(ZFP); op(O_LD); if (k) { lit(k); op(O_ADD); } return 0; } +fn strlit(s) +{ + let j = hole(), a; + op(O_JMP); + a = here; + while (*s) { emit(*s); s++; } + emit(0); + setlit(j, here); + lit(a); + return 0; +} + +// ---- input sources ---- +// frame: 0 kind (0 stdin, 1 token list, 2 string) 1 object 2 index 3 end +// 4 transparent 5..7 saved token 8..10 saved char state 11 owned 12..15 saved lookahead +fn nextch() +{ + if (chkind == 0) { + if (qdone || eof()) { ch = -1; return 0; } + ch = getc(); + if (ch == 96) { qdone = 1; ch = -1; return 0; } // a backtick ends the source + } else { + ch = *chptr; + if (ch) chptr++; else ch = -1; + } + if (ch == 10) line++; + return 0; +} +fn src_push(kind, obj, trans, owned) +{ + let f; + isp++; + if (isp >= 32) die("sources nested too deeply"); + f = R_SRC + isp * 16; + f[0] = kind; f[1] = obj; f[2] = 0; f[3] = 0; f[4] = trans; f[11] = owned; + f[5] = tk; f[6] = tv; f[7] = tglue; f[8] = ch; f[9] = chkind; f[10] = chptr; + f[12] = pk_has; f[13] = pk_tk; f[14] = pk_tv; f[15] = pk_glue; pk_has = 0; + if (kind == 1) f[3] = obj[0]; + if (kind == 0) { chkind = 0; qdone = 0; nextch(); } + if (kind == 2) { chkind = 2; chptr = obj; nextch(); } + return 0; +} +fn src_pop() +{ + let f = R_SRC + isp * 16; + tk = f[5]; tv = f[6]; tglue = f[7]; ch = f[8]; chkind = f[9]; chptr = f[10]; + pk_has = f[12]; pk_tk = f[13]; pk_tv = f[14]; pk_glue = f[15]; pk_line = tline; + isp--; + if (f[11]) free(f[1]); + return 0; +} +fn isopch(c) +{ + let s = "+-*/%=<>!&|^~?:.@#$"; + while (*s) { if (*s == c) return 1; s++; } + return 0; +} +// is buf[0..n) followed by c the start of some operator the lexer knows? +fn opprefix(n, c) +{ + let i = 0, k, j; + while (i < nkop) { + k = kops[i]; + j = 0; + while (j < n && k[j] == tbuf[j]) j++; + if (j == n && k[n] == c) return 1; + i++; + } + return 0; +} +fn isdig(c) { return c >= '0' && c <= '9'; } +fn isal(c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'; } +fn hexv(c) +{ + if (isdig(c)) return c - '0'; + if (c >= 'a' && c <= 'f') return c - 'a' + 10; + if (c >= 'A' && c <= 'F') return c - 'A' + 10; + return -1; +} +fn escch() +{ + let c = ch; + nextch(); + if (c != 92) return c; + c = ch; + nextch(); + if (c == 'n') return 10; + if (c == 't') return 9; + if (c == 'r') return 13; + if (c == '0') return 0; + if (c == 'e') return 27; + return c; +} +fn oplex(n) +{ + while (ch >= 0 && isopch(ch) && opprefix(n, ch)) { tbuf[n] = ch; n++; nextch(); } + tbuf[n] = 0; + tk = T_OP; tv = intern(tbuf); + return 0; +} +fn lex() +{ + let n = 0, v, dg; + tglue = 1; + while (1) { + while (ch == 32 || ch == 9 || ch == 10 || ch == 13) { nextch(); tglue = 0; } + if (ch != '/') break; + nextch(); + if (ch == '/') { while (ch >= 0 && ch != 10) nextch(); tglue = 0; continue; } + if (ch == '*') { + nextch(); + while (ch >= 0) { if (ch == '*') { nextch(); if (ch == '/') break; } else nextch(); } + nextch(); tglue = 0; continue; + } + tline = line; tbuf[0] = '/'; oplex(1); return 0; + } + tline = line; + if (ch < 0) { tk = T_EOF; tv = 0; return 0; } + if (isdig(ch)) { + v = 0; + if (ch == '0') { + nextch(); + if (ch == 'x' || ch == 'X') { + nextch(); + while ((dg = hexv(ch)) >= 0) { v = v * 16 + dg; nextch(); } + tk = T_NUM; tv = v; return 0; + } + } + while (isdig(ch)) { v = v * 10 + ch - '0'; nextch(); } + tk = T_NUM; tv = v; return 0; + } + if (isal(ch)) { + while (isal(ch) || isdig(ch)) { if (n < 1000) { tbuf[n] = ch; n++; } nextch(); } + tbuf[n] = 0; + tk = T_ID; tv = intern(tbuf); return 0; + } + if (ch == 39) { nextch(); tv = escch(); if (ch != 39) die("bad character literal"); nextch(); tk = T_NUM; return 0; } + if (ch == 34) { + nextch(); + while (ch >= 0 && ch != 34) { v = escch(); if (n < 1000) { tbuf[n] = v; n++; } } + nextch(); + tbuf[n] = 0; + tk = T_STR; tv = intern(tbuf); return 0; + } + tbuf[0] = ch; + nextch(); + if (isopch(tbuf[0])) return oplex(1); + tbuf[1] = 0; + tk = T_OP; tv = intern(tbuf); + return 0; +} +// one token of lookahead: peek() fills pk_*, next() takes it first +fn next() +{ + if (pk_has) { tk = pk_tk; tv = pk_tv; tglue = pk_glue; tline = pk_line; pk_has = 0; return 0; } + return next0(); +} +fn peek() +{ + let a, b, c, e; + if (pk_has) return 0; + a = tk; b = tv; c = tglue; e = tline; + next0(); + pk_tk = tk; pk_tv = tv; pk_glue = tglue; pk_line = tline; pk_has = 1; + tk = a; tv = b; tglue = c; tline = e; + return 0; +} +fn next0() +{ + let f, q, i; + f = R_SRC + isp * 16; + if (f[0] != 1) { lex(); return 0; } + i = f[2]; + if (i < f[3]) { q = f[1]; tk = q[1 + i + i]; tv = q[2 + i + i]; tglue = 0; f[2] = i + 1; return 0; } + if (!f[4]) { tk = T_EOF; tv = 0; return 0; } + src_pop(); // a fed list ran out: continue with the token that followed it + return 0; +} +fn is_op(s) { return tk == T_OP && tv == s; } +fn is_id(s) { return tk == T_ID && tv == s; } +fn accept(s) { if ((tk == T_OP || tk == T_ID) && tv == s) { next(); return 1; } return 0; } +fn expect(s) { if (!accept(s)) { puts("zc: line "); putn(tline); puts(": expected "); puts(s); putc(10); halt(); } return 0; } +fn name() +{ + let n = tv; + if (tk != T_ID) die("expected a name"); + next(); + return n; +} + +// ---- operator tables ---- +// a sequence is written like asm: one-char mnemonics, numbers, and names +// (resolved when the operator is used, so image builds stay self-contained) +fn seqasm(s) +{ + let start = seqtop, n, neg, v, k, c; + while (1) { + while (*s == 32) s++; + if (!*s) break; + n = 0; + while (s[n] && s[n] != 32) { tbuf[n] = s[n]; n++; } + tbuf[n] = 0; + s = s + n; + neg = tbuf[0] == '-' && isdig(tbuf[1]); + if (isdig(tbuf[neg])) { + v = 0; k = neg; + while (tbuf[k]) { v = v * 10 + tbuf[k] - '0'; k++; } + if (neg) v = 0 - v; + R_SEQ[seqtop] = v + v + 1; seqtop++; + } else if (n == 1 && (c = mnemidx(tbuf[0])) >= 0) { + R_SEQ[seqtop] = c + c; seqtop++; + } else { + R_SEQ[seqtop] = SEQREF; R_SEQ[seqtop + 1] = intern(tbuf); seqtop = seqtop + 2; + } + } + return start; +} +fn mnemidx(c) { let i = 0; while (mnem[i]) { if (mnem[i] == c) return i; i++; } return -1; } +fn emitseq(a, n) +{ + let e = a + n, s; + while (a < e) { + if (R_SEQ[a] == SEQREF) { + s = lookup(R_SEQ[a + 1]); + if (s < 0) s = fwdfn(R_SEQ[a + 1]); + if (skind[s] == K_FN) fnref(s); else if (skind[s] == K_ENH) op(XOP0 + sval[s]); else lit(sval[s]); + a = a + 2; + } else { emit(R_SEQ[a]); a++; } + } + return 0; +} +fn addkop(p) { let i = 0; while (i < nkop) { if (kops[i] == p) return 0; i++; } kops[nkop] = p; nkop++; return 0; } +fn binfind(p) { let i = nbin; while (i) { i--; if (bname[i] == p) return i; } return -1; } +fn unfind(p) { let i = nun; while (i) { i--; if (uname[i] == p) return i; } return -1; } +fn asgfind(p) { let i = nasg; while (i) { i--; if (aname[i] == p) return i; } return -1; } +// add or replace a binary operator. code is a sequence string, or 0 for the +// built-in && (prec 2) and || (prec 1) behaviour, chosen by -1 / -2 in prec. +fn add_binop(nm, prec, code) +{ + let p = intern(nm), i = binfind(p), a, n; + if (i < 0) { i = nbin; nbin++; } + bname[i] = p; bprec[i] = prec; + if (prec < 0) { bprec[i] = 0 - prec; blen[i] = prec; bseq[i] = 0; addkop(p); return i; } + bseq[i] = seqasm(code); blen[i] = seqtop - bseq[i]; + addkop(p); + // and its compound assignment, x op= y + n = strlen(nm); + wcopy(tbuf + 1000, nm, n); + tbuf[1000 + n] = '='; tbuf[1001 + n] = 0; + a = intern(tbuf + 1000); + if (prec >= 8 || prec == 5 || prec == 4 || prec == 3) { + if (asgfind(a) < 0) { aname[nasg] = a; nasg++; } + abin[asgfind(a)] = i; + addkop(a); + } + return i; +} +fn add_unop(nm, code) +{ + let p = intern(nm), i = unfind(p); + if (i < 0) { i = nun; nun++; } + uname[i] = p; useq[i] = seqasm(code); ulen[i] = seqtop - useq[i]; + addkop(p); + return i; +} +fn add_kw(nm, handler, flags) { let s = addsym(intern(nm), K_KW, handler); sflag[s] = F_DEF | flags; return s; } + +// ---- locals and the analyzer's view of them ---- +fn findloc(n) { let i = nloc; while (i > lbase) { i--; if (lname[i] == n) return i; } return -1; } +fn newloc(n) +{ + let i = nloc; + if (nloc >= NL) die("too many locals"); + nloc++; + lname[i] = n; lslot[i] = fslots; lhome[i] = fdepth; lstate[i] = S_PLAIN; + lowner[i] = 0; lused[i] = 0; larr[i] = 0; lown[i] = 0; ltype[i] = tword; ltag[i] = 0; + return i; +} +fn lroot(i) { while (lstate[i] == S_CHILD) i = lowner[i]; return i; } +fn tracked(i) { return lstate[i] != S_PLAIN; } +// a use that doesn't give the object away +fn touch(i) +{ + let r; + lused[i] = 1; + if (!fchk) return 0; + r = lroot(i); + lused[r] = 1; + if (lstate[r] == S_DEAD) { cerrn(i, "used after it was given away"); return 0; } + // a d object that shows up in a deeper block is given to that block + if (lstate[r] == S_D && lhome[r] < fdepth) { lhome[r] = fdepth; lstate[r] = S_M; lown[r] = 1; } + return 0; +} +fn plainuse(c) +{ + if (c == C_FRESH) cerr("a new object can't be used as a plain value; bind it to a name first"); + if (c >= C_VAR) touch(c - C_VAR); + return C_PLAIN; +} +fn freevar(i) { laddr(lslot[i]); op(O_LD); callname(s_free); op(O_DROP); return 0; } +// leaving every block deeper than or equal to dep: free what is owned, and +// complain about what should have left already. final means the block really +// ends here rather than an early exit. +fn exit_vars(dep, final) +{ + let i = nloc, st; + if (!fchk || !live) return 0; + while (i > lbase) { + i--; + if (lhome[i] < dep) continue; + st = lstate[i]; + if (st == S_M) freevar(i); else if (st == S_U) cerrn(i, "u object must leave this block (block value, assignment to the enclosing block, or return)"); + else if (st == S_D) cerrn(i, "d object was never given away"); + } + return 0; +} +// an m object has to be used somewhere in the block that owns it, on any path +fn unused_check(from) +{ + while (from < nloc) { if (fchk && lown[from] && !lused[from]) dien(lname[from], "m object is never used"); from++; } + return 0; +} +fn block_close(save) +{ + let i = lbase; + unused_check(save); + exit_vars(fdepth, 1); + while (i < save) { if (lhome[i] >= fdepth) { lstate[i] = S_DEAD; lhome[i] = fdepth - 1; } i++; } + nloc = save; + return 0; +} + +// snapshots: [lbase] [nloc] then state, owner, home per local +fn snap_push() +{ + let b = snaptop, i = lbase, p; + p = R_SNAP + b; + p[0] = lbase; p[1] = nloc; + p = p + 2; + while (i < nloc) { p[0] = lstate[i]; p[1] = lowner[i]; p[2] = lhome[i]; p = p + 3; i++; } + snaptop = p - R_SNAP; + if (snaptop >= 8000) die("nesting too deep for the analyzer"); + return b; +} +fn snap_pop(b) { snaptop = b; return 0; } +fn snap_restore(b) +{ + let p = R_SNAP + b, i = p[0], e = p[1]; + if (e > nloc) e = nloc; + p = p + 2; + while (i < e) { lstate[i] = p[0]; lowner[i] = p[1]; lhome[i] = p[2]; p = p + 3; i++; } + return 0; +} +fn empty(s) { return s == S_PLAIN || s == S_DEAD; } +// compare the current states with a snapshot: -1 if they agree, else the +// first local that differs. plain and dead agree and merge to dead. +fn snap_cmp(b) +{ + let p = R_SNAP + b, i = p[0], e = p[1], s; + if (e > nloc) e = nloc; + p = p + 2; + while (i < e) { + s = lstate[i]; + if (empty(s) && empty(p[0])) { if (p[0] == S_DEAD) lstate[i] = S_DEAD; } + else if (s != p[0] || lhome[i] != p[2] || (s == S_CHILD && lowner[i] != p[1])) return i; + p = p + 3; i++; + } + return -1; +} +fn statename(s) +{ + if (s == S_PLAIN) return "empty"; + if (s == S_M) return "m"; + if (s == S_U) return "u"; + if (s == S_D) return "d"; + if (s == S_DEAD) return "given away"; + if (s == S_CHILD) return "owned by a container"; + return "borrowed"; +} +fn merge_err(i, sa, what) +{ + if (!fchk) return 0; + puts("zc: line "); putn(tline); puts(": "); puts(lname[i]); puts(": "); puts(what); + puts(" (one path leaves it "); puts(statename(sa)); puts(", another "); puts(statename(lstate[i])); puts(")\n"); + halt(); + return 0; +} +// after an if: the current state is the else path, sa is the then path +fn merge(sa, livea) +{ + let i, p; + if (!livea) return 0; + if (!live) { snap_restore(sa); live = 1; return 0; } + i = snap_cmp(sa); + if (i >= 0) { p = R_SNAP + sa + 2 + 3 * (i - R_SNAP[sa]); merge_err(i, p[0], "ends the branches of an if in different states"); } + return 0; +} +fn loopcheck(s0) +{ + let i, p; + if (!fchk || !live) return 0; + i = snap_cmp(s0); + if (i >= 0) { p = R_SNAP + s0 + 2 + 3 * (i - R_SNAP[s0]); merge_err(i, p[0], "changes state inside a loop; each pass has to leave it as it found it"); } + return 0; +} + +// ---- analyzer rules for the places values flow ---- +// recast: x as u / x as d / x as m +fn recast(c, t) +{ + let i, st; + if (!fchk) return c; + if (c < C_VAR) { cerr("only a named owned object can be recast"); return c; } + i = c - C_VAR; + st = lstate[i]; + lused[i] = 1; + if (st == S_CHILD) cerrn(i, "belongs to a container; recast the container instead"); + else if (st == S_BORROW) cerrn(i, "is borrowed and can't be recast"); + else if (st == S_PLAIN) cerrn(i, "isn't an owned object"); + else if (st == S_DEAD) cerrn(i, "used after it was given away"); + else if (t == S_M && st != S_M) cerrn(i, "can't be recast back to m"); + else lstate[i] = t; + return c; +} +// an argument for a parameter tagged t (0 plain, 1 m, 2 d) +fn argcheck(c, t) +{ + let i, st; + if (!fchk) return 0; + if (c == C_FRESH) { if (t != 2) cerr("a new object can only go straight to a d parameter; bind it to a name first"); return 0; } + if (c < C_VAR || !tracked(c - C_VAR)) return 0; + i = c - C_VAR; + st = lstate[i]; + if (lstate[lroot(i)] == S_DEAD) { cerrn(i, "used after it was given away"); return 0; } + if (t <= 1) { // untagged parameters are m: a lend + if (st == S_D) { cerrn(i, "is d; pass it to a d parameter (lending it would lose it)"); return 0; } + touch(i); + return 0; + } + if (st == S_D) { lstate[i] = S_DEAD; lused[i] = 1; return 0; } + if (st == S_M) cerrn(i, "is m; write d*x to give it away"); + else if (st == S_U) cerrn(i, "is u; write d*x to give it away instead of returning it"); + else if (st == S_CHILD) cerrn(i, "belongs to a container and can't be given away alone"); + else cerrn(i, "is borrowed and can't be given away"); + return 0; +} +fn emitown(p) { laddr(lslot[p]); op(O_LD); callname(s_own); return 0; } +// a store of class rc into an lvalue of kind k (local or container index i) +fn assigncheck(k, i, rc) +{ + let st, j, r, js; + if (!fchk) return C_PLAIN; + j = -1; + if (rc >= C_VAR && tracked(rc - C_VAR)) j = rc - C_VAR; + if (j >= 0 && lstate[lroot(j)] == S_DEAD) { cerrn(j, "used after it was given away"); return C_PLAIN; } + if (k == LV_LOCAL) { + st = lstate[i]; + if (j < 0 && rc != C_FRESH) { + if (!empty(st)) cerrn(i, "still owns an object; overwriting it would leak it"); + else lstate[i] = S_PLAIN; + return C_PLAIN; + } + if (!empty(st)) { cerrn(i, "still owns an object; overwriting it would leak it"); return C_PLAIN; } + if (rc == C_FRESH) { lstate[i] = S_M; lused[i] = 0; lown[i] = 1; if (ltag[i]) lstate[i] = ltag[i]; return C_PLAIN; } + js = lstate[j]; + if (js == S_U && lhome[i] == lhome[j] - 1) { // handing a u object up one block + lstate[i] = S_M; lused[i] = 0; lown[i] = 1; lstate[j] = S_DEAD; lused[j] = 1; if (ltag[i]) lstate[i] = ltag[i]; + return C_PLAIN; + } + if (js == S_U) cerrn(j, "is u; it can only be handed to a variable of the enclosing block, or returned"); + else cerrn(j, "is an owned object and can't be copied into another variable"); + return C_PLAIN; + } + if (k == LV_ELEM) { + r = lroot(i); + st = lstate[r]; + touch(i); + if (rc == C_FRESH) { emitown(i); return C_PLAIN; } + if (j < 0) return C_PLAIN; + js = lstate[j]; + if (js == S_CHILD) cerrn(j, "already belongs to a container"); + else if (js == S_BORROW) cerrn(j, "is borrowed and can't be stored"); + else if (st == S_BORROW) { + if (js != S_D) cerrn(j, "must be d to be stored into a borrowed object"); + else { lstate[j] = S_DEAD; lused[j] = 1; emitown(i); } + } else if (js != st) cerrn(j, "its tag doesn't match its container's (both have to be m, u or d)"); + else if (lhome[j] < lhome[r]) cerrn(j, "lives in an outer block than the container it's stored into"); + else { lstate[j] = S_CHILD; lowner[j] = i; lused[j] = 1; emitown(i); } + return C_PLAIN; + } + // a global, or memory the analyzer doesn't track: the collector takes it + if (j >= 0) { + if (lstate[j] == S_D) { lstate[j] = S_DEAD; lused[j] = 1; } + else cerrn(j, "must be d to be stored in a global or in untracked memory (the collector takes it over)"); + } + return C_PLAIN; +} +fn retcheck(c) +{ + let i; + if (!fchk) return 0; + i = -1; + if (c >= C_VAR && tracked(c - C_VAR)) i = c - C_VAR; + if (i >= 0 && lstate[lroot(i)] == S_DEAD) { cerrn(i, "used after it was given away"); return 0; } + if (fret) { + if (i >= 0) { + if (lstate[i] == S_U) { lstate[i] = S_DEAD; lused[i] = 1; } + else cerrn(i, "has to be u to be returned; write 'return u*x'"); + } + return 0; + } + if (c == C_FRESH) cerr("returns a new object; declare the function with -> .u"); + else if (i >= 0) cerrn(i, "is owned and can't be returned from a function without -> .u"); + return 0; +} +// the last expression of a block expression +fn handup(c) +{ + let i; + if (!fchk) return C_PLAIN; + if (c == C_FRESH) return C_FRESH; + if (c < C_VAR || !tracked(c - C_VAR)) return C_PLAIN; + i = c - C_VAR; + if (lstate[i] == S_U && lhome[i] == fdepth) { lstate[i] = S_DEAD; lused[i] = 1; return C_FRESH; } + if (lstate[i] == S_U) cerrn(i, "only a u object of this block can be its value"); + else cerrn(i, "has to be u to be handed up as the block's value"); + return C_PLAIN; +} +fn discard(c) +{ + if (c == C_FRESH) cerr("a new object (or a u result) can't be thrown away"); + else if (c >= C_VAR) touch(c - C_VAR); + return 0; +} + +// ---- types ---- +// types are a compiler primitive: records in the type area, hash-consed so +// the same type is always the same pointer and comparing types is comparing +// words. untyped code is typed word, which goes with anything. the records +// stay in memory after compiling, so programs can inspect them. +// t[0] kind t[1] size in words t[2] name t[3] n t[4] a t[5] b t[6] c t[7..] extra +// ptr: a = element arr: a = element, n = length +// fn: a = return type, n = parameter count, b = signature (u m d tags), extra = parameter types +// struct: n = field count (-1 until laid out), a = fields (name, type, offset), +// b = generic template, c = type arguments [n, t1, t2 ...] +// var: a type parameter, n = its index +// gen: a generic struct: n = parameter count, a = fields (name, type), b = field count, c = parameter names + + +fn talloc(n) { let p = tytop; if (tytop + n >= R_TYEND) die("the type area is full"); tytop = tytop + n; wfill(p, 0, n); return p; } +fn tnew(kind, size, nm, extra) +{ + let t = talloc(7 + extra); + t[0] = kind; t[1] = size; t[2] = nm; + if (ntl >= NTL) die("too many types"); + R_TY[ntl] = t; ntl++; + return t; +} +fn tptr(e) +{ + let i = 0, t; + while (i < ntl) { t = R_TY[i]; if (t[0] == TY_PTR && t[4] == e) return t; i++; } + t = tnew(TY_PTR, 1, 0, 0); t[4] = e; + return t; +} +fn tarr(e, n) +{ + let i = 0, t; + while (i < ntl) { t = R_TY[i]; if (t[0] == TY_ARR && t[4] == e && t[3] == n) return t; i++; } + t = tnew(TY_ARR, n * tsz(e), 0, 0); t[4] = e; t[3] = n; + return t; +} +fn tfn(ret, np, ps, sig) +{ + let i = 0, j, t; + while (i < ntl) { + t = R_TY[i]; + if (t[0] == TY_FN && t[4] == ret && t[3] == np && t[5] == sig) { + j = 0; while (j < np && t[7 + j] == ps[j]) j++; + if (j == np) return t; + } + i++; + } + t = tnew(TY_FN, 1, 0, np); t[4] = ret; t[3] = np; t[5] = sig; + wcopy(t + 7, ps, np); + return t; +} +fn tvar(i, nm) +{ + let k = 0, t; + while (k < ntl) { t = R_TY[k]; if (t[0] == TY_VAR && t[3] == i) return t; k++; } + t = tnew(TY_VAR, 1, nm, 0); t[3] = i; + return t; +} +fn tinst(g, args, n) +{ + let i = 0, j, t, v; + while (i < ntl) { + t = R_TY[i]; + if (t[0] == TY_STRUCT && t[5] == g) { + v = t[6]; j = 0; while (j < n && v[1 + j] == args[j]) j++; + if (j == n) return t; + } + i++; + } + v = talloc(n + 1); v[0] = n; wcopy(v + 1, args, n); + t = tnew(TY_STRUCT, 0, g[2], 0); t[3] = -1; t[5] = g; t[6] = v; + return t; +} +// lay out a generic instance on first use +fn tresolve(t) +{ + let g, f, n, i, fs, off = 0, ft, bs[8]; + if (t[0] != TY_STRUCT || t[3] >= 0) return t; + if (t[3] == -2) die("a struct can't contain itself by value"); + t[3] = -2; + g = t[5]; n = g[5]; f = g[4]; + wfill(bs, 0, 8); wcopy(bs, t[6] + 1, t[6][0]); + fs = talloc(n * 3 + 1); + i = 0; + while (i < n) { + ft = subst(f[i + i + 1], bs); + fs[i * 3] = f[i + i]; fs[i * 3 + 1] = ft; fs[i * 3 + 2] = off; + off = off + tsz(ft); + i++; + } + t[4] = fs; t[1] = off; t[3] = n; + return t; +} +fn tsz(t) { if (t[0] == TY_STRUCT && t[3] < 0) tresolve(t); return t[1]; } +// replace type parameters with what they were bound to (unbound ones become word) +fn subst(t, bs) +{ + let k = t[0], i, n, v[24], vec; + if (k == TY_VAR) { if (bs[t[3]]) return bs[t[3]]; return tword; } + if (k == TY_PTR) return tptr(subst(t[4], bs)); + if (k == TY_ARR) return tarr(subst(t[4], bs), t[3]); + if (k == TY_FN) { n = t[3]; i = 0; while (i < n) { v[i] = subst(t[7 + i], bs); i++; } return tfn(subst(t[4], bs), n, v, t[5]); } + if (k == TY_STRUCT && t[5]) { vec = t[6]; n = vec[0]; i = 0; while (i < n) { v[i] = subst(vec[1 + i], bs); i++; } return tinst(t[5], v, n); } + return t; +} +fn isnum(t) { return t[0] == TY_INT || t[0] == TY_CHAR || t[0] == TY_BOOL; } +fn isagg(t) { return t[0] == TY_ARR || t[0] == TY_STRUCT; } +fn isptr(t) { return t[0] == TY_PTR || t[0] == TY_ARR; } +fn decay(t) { if (t[0] == TY_ARR) return tptr(t[4]); return t; } +fn telem(t) { if (t[0] == TY_PTR || t[0] == TY_ARR) return t[4]; return tword; } +// can a value of type src go where dst is expected? +fn compat(dst, src) +{ + if (dst == src || dst == tword || src == tword) return 1; + if (isnum(dst) && isnum(src)) return 1; + src = decay(src); + if (dst[0] == TY_PTR && src[0] == TY_PTR) { + if (dst[4] == tword || src[4] == tword || dst[4] == tvoid || src[4] == tvoid) return 1; + return dst[4] == src[4]; + } + if ((dst[0] == TY_PTR || dst[0] == TY_FN) && xzero) return 1; // a literal 0 is a null pointer + return dst == src; +} +fn unify(p, a, bs) +{ + let i, n, pv, av; + if (p[0] == TY_VAR) { i = p[3]; if (!bs[i]) { bs[i] = a; return 1; } return compat(bs[i], a); } + a = decay(a); + if (p[0] == TY_PTR && a[0] == TY_PTR) { if (a[4] == tword) return 1; return unify(p[4], a[4], bs); } + if (p[0] == TY_STRUCT && p[5] && a[0] == TY_STRUCT && a[5] == p[5]) { + pv = p[6]; av = a[6]; n = pv[0]; i = 0; + while (i < n) { if (!unify(pv[1 + i], av[1 + i], bs)) return 0; i++; } + return 1; + } + return compat(p, a); +} +fn tprint(t) +{ + let k = t[0], i, v; + if (k == TY_PTR) { putc('*'); return tprint(t[4]); } + if (k == TY_ARR) { putc('['); putn(t[3]); putc(']'); return tprint(t[4]); } + if (k == TY_FN) { + puts("fn("); + i = 0; + while (i < t[3]) { + if (i) puts(", "); + tprint(t[7 + i]); + if (i < 12 && tparamtag(t, i) == 1) puts(".m"); + if (i < 12 && tparamtag(t, i) == 2) puts(".d"); + i++; + } + putc(')'); + if (t[4] != tword || (t[5] & 1)) { puts(" -> "); if (t[4] != tword) tprint(t[4]); if (t[5] & 1) puts(".u"); } + return 0; + } + if (t[2]) puts(t[2]); else puts("?"); + if (k == TY_STRUCT && t[5]) { + v = t[6]; putc('<'); + i = 0; while (i < v[0]) { if (i) puts(", "); tprint(v[1 + i]); i++; } + putc('>'); + } + return 0; +} +fn tcerr(what, want, got) +{ + if (!tc_on || !live || dry) return 0; + puts("zc: line "); putn(tline); puts(": type error: "); puts(what); + puts(": expected "); tprint(want); puts(", got "); tprint(got); putc(10); + halt(); + return 0; +} +fn tcneed(ok, what, got) +{ + if (ok || !tc_on || !live || dry) return 0; + puts("zc: line "); putn(tline); puts(": type error: "); puts(what); puts(", got "); tprint(got); putc(10); + halt(); + return 0; +} +fn tpfind(n) { let i = ntp; while (i) { i--; if (tpn[i] == n) return i; } return -1; } +fn istypename(n) +{ + let s; + if (tpfind(n) >= 0) return 1; + if (findloc(n) >= 0) return 0; + s = lookup(n); + return s >= 0 && skind[s] == K_TYPE; +} +// the closing > of a type argument list; splits >> in two +fn closeangle() +{ + if (is_op(s_shr)) { tv = s_gt; return 0; } + expect(s_gt); + return 0; +} +fn ptype() +{ + let n, s, t, np = 0, ps[24], ret, args[8], na = 0, i; + if (accept(s_star)) return tptr(ptype()); + if (accept(s_lk)) { n = cexpr(); expect(s_rk); t = ptype(); if (n < 0) die("a negative array length"); return tarr(t, n); } + if (is_id(s_fn)) { + next(); expect(s_lp); + if (!is_op(s_rp)) { while (1) { if (np >= 24) die("too many parameters"); ps[np] = ptype(); np++; if (!accept(s_comma)) break; } } + expect(s_rp); + ret = tword; + i = 0; + if (accept(s_arrow)) { if (!is_op(s_dot)) ret = ptype(); if (accept(s_dot)) { if (!is_id(s_u)) die("the return tag is .u"); next(); i = 1; } } + return tfn(ret, np, ps, i + np + np); + } + if (tk != T_ID) die("expected a type"); + n = tv; + i = tpfind(n); + if (i >= 0) { next(); return tvar(i, n); } + s = lookup(n); + if (s < 0 || skind[s] != K_TYPE) dien(n, "isn't a type"); + next(); + t = sval[s]; + if (t[0] != TY_GEN) return t; + expect(s_lt); + while (1) { + if (na >= 8) die("too many type arguments"); + args[na] = ptype(); + if (tsz(args[na]) != 1) die("type arguments have to be one word (a pointer, a number, or a parameter)"); + na++; + if (!accept(s_comma)) break; + } + closeangle(); + if (na != t[3]) dien(n, "wrong number of type arguments"); + return tinst(t, args, na); +} +// does a declaration start here? (a type, or a bare .tag) +fn tstart() +{ + if (tk == T_OP) { + if (tv == s_lk || tv == s_dot) return 1; + if (tv == s_star) { peek(); return (pk_tk == T_ID && (istypename(pk_tv) || pk_tv == s_fn)) || (pk_tk == T_OP && pk_tv == s_lk); } + return 0; + } + if (tk != T_ID) return 0; + return tv == s_fn || istypename(tv); +} +// [type] [.tag] +fn dtype() +{ + dty = 0; dtag = 0; + if (!is_op(s_dot)) dty = ptype(); + if (accept(s_dot)) { + if (tv == s_m) dtag = S_M; else if (tv == s_u) dtag = S_U; else if (tv == s_d) dtag = S_D; else die("a tag is .u, .m or .d"); + next(); + } + return 0; +} +// struct name { type field, ... } or struct name { ... } +fn kw_struct() +{ + let n, t, s, nf = 0, buf = R_QBUF, i, fs, off = 0, gen = 0, ft; + next(); + n = name(); + if (is_op(s_lt)) { // generic + next(); ntp = 0; + while (1) { if (ntp >= 8) die("too many type parameters"); tpn[ntp] = name(); ntp++; if (!accept(s_comma)) break; } + closeangle(); + gen = 1; + t = tnew(TY_GEN, 0, n, 0); t[3] = ntp; + fs = talloc(ntp); wcopy(fs, tpn, ntp); t[6] = fs; + } else { t = tnew(TY_STRUCT, 0, n, 0); t[3] = -2; } // -2 while its fields are read + s = addsym(n, K_TYPE, t); + expect(s_lb); + while (!is_op(s_rb)) { + ft = ptype(); + while (1) { + if (nf >= 1000) die("too many fields"); + buf[nf + nf] = name(); buf[nf + nf + 1] = ft; nf++; + if (!accept(s_comma)) break; + } + expect(s_semi); + } + next(); + if (gen) { + fs = talloc(nf + nf); wcopy(fs, buf, nf + nf); + t[4] = fs; t[5] = nf; + ntp = 0; + } else { + fs = talloc(nf * 3 + 1); + i = 0; + while (i < nf) { + ft = buf[i + i + 1]; + if (ft == t) die("a struct can't contain itself by value; use a pointer"); + fs[i * 3] = buf[i + i]; fs[i * 3 + 1] = ft; fs[i * 3 + 2] = off; + off = off + tsz(ft); + i++; + } + t[4] = fs; t[1] = off; t[3] = nf; + } + accept(s_semi); + return s; +} +// type name = T; +fn kw_type() { let n; next(); n = name(); expect(s_eq); addsym(n, K_TYPE, ptype()); expect(s_semi); return 0; } +// typecheck on; / typecheck off; +fn kw_typecheck() +{ + next(); + if (is_id(s_on)) tc_on = 1; else if (is_id(s_off)) tc_on = 0; else die("typecheck on or typecheck off"); + next(); expect(s_semi); + return 0; +} +// the field of a struct type, or -1 +fn tfindfield(t, n) { let i = 0; tresolve(t); while (i < t[3]) { if (t[4][i * 3] == n) return i; i++; } return -1; } +// sizeof(T or expr), typeof(T or expr): the expression form is compiled dry, so it never runs +fn typearg() +{ + let t, sb, c; + expect(s_lp); + if (tstart() && !is_op(s_dot)) t = ptype(); + else { + sb = snap_push(); dry++; + c = expr(); rval(); + t = xty; + dry--; snap_restore(sb); snap_pop(sb); + } + expect(s_rp); + return t; +} +fn kw_sizeof() { let t; next(); t = typearg(); lit(tsz(t)); xty = tint; return 0; } +fn kw_typeof() { let t; next(); t = typearg(); lit(t); xty = tword; return 0; } +fn types_init() +{ + tytop = R_TY + NTL; ntl = 0; + tword = tnew(TY_WORD, 1, intern("word"), 0); + tint = tnew(TY_INT, 1, intern("int"), 0); + tchar = tnew(TY_CHAR, 1, intern("char"), 0); + tbool = tnew(TY_BOOL, 1, intern("bool"), 0); + tvoid = tnew(TY_VOID, 0, intern("void"), 0); + addsym(tword[2], K_TYPE, tword); addsym(tint[2], K_TYPE, tint); addsym(tchar[2], K_TYPE, tchar); + addsym(tbool[2], K_TYPE, tbool); addsym(tvoid[2], K_TYPE, tvoid); + s_lt = intern("<"); s_gt = intern(">"); s_shr = intern(">>"); s_le = intern("<="); s_ge = intern(">="); + s_eqeq = intern("=="); s_ne = intern("!="); s_plus = intern("+"); + s_struct = intern("struct"); s_type = intern("type"); s_typecheck = intern("typecheck"); + s_on = intern("on"); s_off = intern("off"); s_new = intern("new"); s_new_t = intern("new_t"); + numops[0] = intern("*"); numops[1] = intern("/"); numops[2] = intern("%"); numops[3] = intern("&"); + numops[4] = intern("|"); numops[5] = intern("^"); numops[6] = intern("<<"); numops[7] = s_shr; nnumops = 8; + return 0; +} +fn isnumop(o) { let i = 0; while (i < nnumops) { if (numops[i] == o) return 1; i++; } return 0; } +// the type of a binary operation, checking it when the typechecker is on +fn bintype(b, lt, rt) +{ + let o = bname[b]; + lt = decay(lt); rt = decay(rt); + if (blen[b] < 0) { tcneed(!isagg(lt) && !isagg(rt), "&& and || need scalars", lt); return tbool; } + if (o == s_eqeq || o == s_ne) { if (!compat(lt, rt) && !compat(rt, lt)) tcerr("comparison", lt, rt); return tbool; } + if (o == s_lt || o == s_gt || o == s_le || o == s_ge) { + if (!(lt == tword || rt == tword || (isnum(lt) && isnum(rt)) || (lt[0] == TY_PTR && rt[0] == TY_PTR))) tcerr("comparison", lt, rt); + return tbool; + } + if (o == s_plus) { + if (lt[0] == TY_PTR && (isnum(rt) || rt == tword)) return lt; + if (rt[0] == TY_PTR && isnum(lt)) return rt; + if (lt == tword || rt == tword) return tword; + if (isnum(lt) && isnum(rt)) return tint; + tcerr("+", lt, rt); return tword; + } + if (o == s_minus) { + if (lt[0] == TY_PTR && rt[0] == TY_PTR) return tint; + if (lt[0] == TY_PTR && (isnum(rt) || rt == tword)) return lt; + if (lt == tword || rt == tword) return tword; + if (isnum(lt) && isnum(rt)) return tint; + tcerr("-", lt, rt); return tword; + } + if (isnumop(o)) { + if (lt == tword || rt == tword) return tword; + if (isnum(lt) && isnum(rt)) return tint; + if (isnum(lt)) lt = rt; + tcneed(0, "arithmetic needs numbers", lt); return tword; + } + return tword; // operators a program added +} + +// ---- reflection ---- +// for programs (and parse words) looking at types at runtime +fn tkind(t) { return t[0]; } +fn tsize(t) { return tsz(t); } +fn tname(t) { return t[2]; } +fn tlen(t) { return t[3]; } +fn tnfields(t) { tresolve(t); return t[3]; } +fn tfname(t, i) { tresolve(t); return t[4][i * 3]; } +fn tftype(t, i) { tresolve(t); return t[4][i * 3 + 1]; } +fn tfoff(t, i) { tresolve(t); return t[4][i * 3 + 2]; } +fn tret(t) { return t[4]; } +fn tnparams(t) { return t[3]; } +fn tparam(t, i) { return t[7 + i]; } +fn tparamtag(t, i) { return ((t[5] >> 6) >> (i + i)) & 3; } // 0 untagged (m), 1 .m, 2 .d +fn tretu(t) { return t[5] & 1; } +fn tnargs(t) { if (t[0] != TY_STRUCT || !t[5]) return 0; return t[6][0]; } +fn targ(t, i) { return t[6][1 + i]; } +// the type of a function, by address. untyped functions get one made from their signature +fn sigof(f) +{ + let i = nsym, s = -1, ws[24], n, k; + while (i) { i--; if (skind[i] == K_FN && sval[i] == f && (sflag[i] & F_DEF)) { s = i; break; } } + if (s < 0) return 0; + if (stype[s]) return stype[s]; + n = (ssig[s] >> 1) & 31; k = 0; + while (k < n) { ws[k] = tword; k++; } + return tfn(tword, n, ws, ssig[s]); +} +fn sigofs(nm) { let s = lookups(nm); if (s < 0 || skind[s] != K_FN) return 0; return sigof(sval[s]); } + +// ---- expressions ---- +// every expression leaves its value on the stack, returns its analyzer class, +// and sets xty to its type. lv means the stack holds an address that hasn't +// been loaded yet; structs and arrays are never loaded, their address is +// their value. +fn rval() { if (lv) { if (!isagg(xty)) op(O_LD); lv = 0; } return 0; } +fn kwcall(s, e) +{ + let f = sval[s], old = kw_expr, c; + kw_expr = e; xcls = C_PLAIN; xty = tword; xzero = 0; + f(); + kw_expr = old; + c = xcls; + lv = 0; lvk = LV_NONE; + return c; +} +fn stmtonly() { if (kw_expr) die("this can't be used inside an expression"); return 0; } +// the arguments of a call. ft is the callee's type (or 0), bs the generic bindings +fn args(tags, ft, bs) +{ + let np = 0, c, pt; + expect(s_lp); + if (!is_op(s_rp)) { + while (1) { + c = expr(); rval(); + argcheck(c, (tags >> (np + np)) & 3); + if (ft && np < ft[3]) { pt = ft[7 + np]; if (!unify(pt, xty, bs)) tcerr("argument", subst(pt, bs), xty); } + np++; + if (!accept(s_comma)) break; + } + } + expect(s_rp); + return np; +} +// new(T) and new(T, count): a typed object, its type stamped into the header +fn newtyped() +{ + let t, c; + t = ptype(); + if (accept(s_comma)) { + c = expr(); rval(); plainuse(c); + if (tsz(t) != 1) { lit(tsz(t)); op(15); } + lit(tarr(t, 0)); + } else { lit(tsz(t)); lit(t); } + expect(s_rp); + callname(s_new_t); + lv = 0; lvk = LV_NONE; xty = tptr(t); xzero = 0; + return C_FRESH; +} +fn call(s) +{ + let np, tags = 0, ret = 0, ft = stype[s], bs[8]; + if (sname[s] == s_new && skind[s] == K_FN) { + peek(); + if (pk_tk == T_OP && (pk_tv == s_star || pk_tv == s_lk)) { next(); return newtyped(); } + if (pk_tk == T_ID && (istypename(pk_tv) || pk_tv == s_fn)) { next(); return newtyped(); } + } + if (sflag[s] & F_SIG) { tags = ssig[s] >> 6; ret = ssig[s] & 1; } + else sflag[s] = sflag[s] | F_UNTYPED; + wfill(bs, 0, 8); + if (ngbind) { wcopy(bs, gbind, ngbind); ngbind = 0; } + np = args(tags, ft, bs); + if ((sflag[s] & F_SIG) && np != ((ssig[s] >> 1) & 31)) dien(sname[s], "wrong number of arguments"); + fnref(s); op(O_CALL); + lv = 0; lvk = LV_NONE; xzero = 0; + xty = tword; + if (ft) xty = subst(ft[4], bs); + if (ret) return C_FRESH; + return C_PLAIN; +} +// an enhancement used like a function: the arguments, then its one opcode +fn enhcall(s) +{ + let nin = ssig[s] & 15, nout = (ssig[s] >> 4) & 15, n = 0, c; + expect(s_lp); + if (!is_op(s_rp)) { while (1) { c = expr(); rval(); plainuse(c); n++; if (!accept(s_comma)) break; } } + expect(s_rp); + if (n != nin) dien(sname[s], "wrong number of inputs"); + lv = 0; lvk = LV_NONE; xty = tword; xzero = 0; + if (sflag[s] & F_FALLBACK) { fnref(sfix[s] - 1); op(O_CALL); return C_PLAIN; } + op(XOP0 + sval[s]); + if (nout == 0) lit(0); + if (nout > 1) dien(sname[s], "leaves several values; use it from asm"); + return C_PLAIN; +} +fn primary() +{ + let n, i, s, c; + lv = 0; lvk = LV_NONE; xzero = 0; + if (tk == T_NUM) { lit(tv); xzero = tv == 0; xty = tint; next(); return C_PLAIN; } + if (tk == T_STR) { strlit(tv); next(); xty = tptr(tchar); return C_PLAIN; } + if (tk == T_OP) { + if (tv == s_lp) { next(); c = expr(); expect(s_rp); return c; } + if (tv == s_lb) return blockexpr(); + die("expected an expression"); + } + if (tk != T_ID) die("expected an expression"); + n = tv; + i = findloc(n); + if (i >= 0) { + next(); + if (!fchk && tracked(i)) dien(n, "is tracked by the analyzer and can't be used in unchecked code"); + laddr(lslot[i]); + xty = ltype[i]; + if (larr[i]) return C_PLAIN; + lv = 1; lvk = LV_LOCAL; lvi = i; + if (tracked(i)) return C_VAR + i; + return C_PLAIN; + } + s = lookup(n); + if (s >= 0 && skind[s] == K_KW) { + if (!(sflag[s] & F_EXPR)) dien(n, "can't be used inside an expression"); + return kwcall(s, 1); + } + if (s >= 0 && skind[s] == K_TYPE) dien(n, "is a type, not a value"); + next(); + xty = tword; + if (s >= 0 && skind[s] == K_CONST) { lit(sval[s]); xty = tint; xzero = sval[s] == 0; return C_PLAIN; } + if (s >= 0 && skind[s] == K_GVAR) { lit(sval[s]); if (stype[s]) xty = stype[s]; lv = 1; lvk = LV_GLOBAL; return C_PLAIN; } + if (s >= 0 && skind[s] == K_GARR) { lit(sval[s]); if (stype[s]) xty = stype[s]; return C_PLAIN; } + if (s >= 0 && skind[s] == K_ENH) return enhcall(s); + if (s < 0) s = fwdfn(n); + if (is_op(s_lt) && (sflag[s] & F_GEN)) { // f(...) + next(); ngbind = 0; + while (1) { if (ngbind >= 8) die("too many type arguments"); gbind[ngbind] = ptype(); ngbind++; if (!accept(s_comma)) break; } + closeangle(); + if (!is_op(s_lp)) die("expected ( after the type arguments"); + } + if (is_op(s_lp)) return call(s); + fnref(s); + if (stype[s]) xty = stype[s]; + return C_PLAIN; +} +// ++ and -- on the address at the top of the stack +fn incdec(post, dlt) +{ + if (!lv) die("++ and -- need something to assign to"); + if (lvk == LV_LOCAL && fchk && tracked(lvi)) cerrn(lvi, "owns an object and can't be incremented"); + tcneed(isnum(xty) || xty[0] == TY_PTR || xty == tword, "++ and -- need a number or a pointer", xty); + lv = 0; lvk = LV_NONE; xzero = 0; + lit(0); op(O_PICK); op(O_LD); + if (post) { lit(0); op(O_PICK); lit(dlt); op(O_ADD); lit(2); op(O_PICK); op(O_ST); } + else { lit(dlt); op(O_ADD); lit(0); op(O_PICK); lit(2); op(O_PICK); op(O_ST); } + op(O_SWAP); op(O_DROP); + return C_PLAIN; +} +// p.x and p->x: a field of a struct, or of the struct a pointer points at +fn field(c) +{ + let n, st, i, b = -1; + next(); + n = name(); + rval(); + if (c == C_FRESH) cerr("use a field of a new object only after binding it to a name"); + else if (c >= C_VAR) { touch(c - C_VAR); b = c - C_VAR; } + st = xty; + if (st[0] == TY_PTR) st = st[4]; + if (st[0] != TY_STRUCT) { puts("zc: line "); putn(tline); puts(": ."); puts(n); puts(": that's a "); tprint(xty); puts(", not a struct or a pointer to one\n"); halt(); } + i = tfindfield(st, n); + if (i < 0) { puts("zc: line "); putn(tline); puts(": "); tprint(st); puts(" has no field "); puts(n); putc(10); halt(); } + if (st[4][i * 3 + 2]) { lit(st[4][i * 3 + 2]); op(O_ADD); } + xty = st[4][i * 3 + 1]; + lv = 1; xzero = 0; + if (b >= 0) { lvk = LV_ELEM; lvi = b; } else lvk = LV_OTHER; + return C_PLAIN; +} +fn postfix() +{ + let c = primary(), b, n, t, et, ft, bs[8]; + while (1) { + if (tk == T_ID && tv == s_as) { // e as T: same bits, another type + next(); t = ptype(); rval(); xty = t; lvk = LV_NONE; + continue; + } + if (tk != T_OP) return c; + if (tv == s_lk) { + next(); + rval(); + b = -1; + if (c == C_FRESH) cerr("index a new object only after binding it to a name"); + else if (c >= C_VAR) { touch(c - C_VAR); b = c - C_VAR; } + tcneed(isptr(xty) || xty == tword, "only pointers and arrays can be indexed", xty); + et = telem(xty); + c = expr(); rval(); plainuse(c); + tcneed(isnum(xty) || xty == tword, "an index has to be a number", xty); + expect(s_rk); + if (tsz(et) != 1) { lit(tsz(et)); op(15); } + op(O_ADD); + lv = 1; xty = et; xzero = 0; + if (b >= 0) { lvk = LV_ELEM; lvi = b; } else lvk = LV_OTHER; + c = C_PLAIN; + continue; + } + if (tv == s_dot || tv == s_arrow) { c = field(c); continue; } + if (tv == s_lp) { // call through a value + rval(); plainuse(c); + ft = 0; + if (xty[0] == TY_FN) ft = xty; + else tcneed(xty == tword, "only functions can be called", xty); + wfill(bs, 0, 8); + n = args(0, ft, bs); + lit(n); op(O_PICK); op(O_CALL); op(O_SWAP); op(O_DROP); + lv = 0; lvk = LV_NONE; c = C_PLAIN; xzero = 0; + xty = tword; + if (ft) xty = subst(ft[4], bs); + continue; + } + if (tv == s_inc) { next(); c = incdec(1, 1); continue; } + if (tv == s_dec) { next(); c = incdec(1, -1); continue; } + return c; + } + return c; +} +fn unary() +{ + let c, un, t; + if (tk == T_ID && (tv == s_u || tv == s_m || tv == s_d)) { // u*x, d*x, m*x: recast + t = S_M; if (tv == s_u) t = S_U; if (tv == s_d) t = S_D; + next(); expect(s_star); + c = unary(); rval(); + lvk = LV_NONE; + return recast(c, t); + } + if (tk == T_OP) { + if (tv == s_star) { + next(); c = unary(); rval(); plainuse(c); + tcneed(xty[0] == TY_PTR || xty == tword, "only pointers can be dereferenced", xty); + xty = telem(xty); + lv = 1; lvk = LV_OTHER; xzero = 0; + return C_PLAIN; + } + if (tv == s_amp) { + next(); c = unary(); + if (!lv && !isagg(xty)) die("& needs something addressable"); + if (c >= C_VAR) touch(c - C_VAR); + lv = 0; lvk = LV_NONE; xzero = 0; + xty = tptr(xty); + return C_PLAIN; + } + if (tv == s_inc) { next(); unary(); return incdec(0, 1); } + if (tv == s_dec) { next(); unary(); return incdec(0, -1); } + un = unfind(tv); + if (un >= 0) { + next(); c = unary(); rval(); plainuse(c); + emitseq(useq[un], ulen[un]); + if (un <= 1) xty = tbool; + else if (xty != tword) { tcneed(isnum(xty), "this operator needs a number", xty); xty = tint; } + lv = 0; lvk = LV_NONE; xzero = 0; + return C_PLAIN; + } + } + return postfix(); +} +fn bin(minp) +{ + let c = unary(), b, p, a, j, lt; + while (tk == T_OP) { + b = binfind(tv); + if (b < 0) return c; + p = bprec[b]; + if (p < minp) return c; + next(); rval(); plainuse(c); + lt = xty; + if (blen[b] == -1) { // && : 0 if the left side is 0, else the right side as 0 or 1 + a = hole(); op(O_JZ); + c = bin(p + 1); rval(); plainuse(c); + emitseq(useq[1], ulen[1]); // !! normalizes + j = hole(); op(O_JMP); + setlit(a, here); lit(0); setlit(j, here); + } else if (blen[b] == -2) { // || : 1 if the left side isn't 0, else the right side as 0 or 1 + a = hole(); op(O_JZ); + lit(1); j = hole(); op(O_JMP); + setlit(a, here); + c = bin(p + 1); rval(); plainuse(c); + emitseq(useq[1], ulen[1]); + setlit(j, here); + } else { + c = bin(p + 1); rval(); plainuse(c); + emitseq(bseq[b], blen[b]); + } + xty = bintype(b, lt, xty); + lv = 0; lvk = LV_NONE; c = C_PLAIN; xzero = 0; + } + return c; +} +// [addr v] -> [v], storing v at addr +fn storekeep() { op(O_SWAP); lit(1); op(O_PICK); op(O_SWAP); op(O_ST); return 0; } +fn expr() +{ + let c = bin(1), k, i, a, rc, lt; + if (tk != T_OP) return c; + if (tv == s_eq) { + if (!lv) die("this can't be assigned to"); + k = lvk; i = lvi; lt = xty; + if (isagg(lt)) die("a whole struct or array can't be assigned; assign its fields"); + next(); + rc = expr(); rval(); + if (!compat(lt, xty)) tcerr("assignment", lt, xty); + storekeep(); + lv = 0; lvk = LV_NONE; xty = lt; xzero = 0; + return assigncheck(k, i, rc); + } + a = asgfind(tv); + if (a >= 0) { + if (!lv) die("this can't be assigned to"); + if (lvk == LV_LOCAL && fchk && tracked(lvi)) cerrn(lvi, "owns an object; compound assignment would lose it"); + lt = xty; + next(); + lit(0); op(O_PICK); op(O_LD); + lv = 0; + rc = expr(); rval(); plainuse(rc); + emitseq(bseq[abin[a]], blen[abin[a]]); + k = bintype(abin[a], lt, xty); + if (!compat(lt, k)) tcerr("compound assignment", lt, k); + storekeep(); + lv = 0; lvk = LV_NONE; xty = lt; xzero = 0; + return C_PLAIN; + } + return c; +} +// does the current token start a statement rather than an expression? +fn stmtstart() +{ + let s; + if (tk == T_OP) return tv == s_semi || tv == s_lb || tstart(); + if (tk != T_ID || findloc(tv) >= 0) return 0; + if (tstart()) return 1; + s = lookup(tv); + return s >= 0 && skind[s] == K_KW && !(sflag[s] & F_EXPR); +} +// { stmts; tail } as an expression: its value is the tail, or 0 +fn blockexpr() +{ + let save = nloc, c = C_PLAIN, tail = 0, t = tint; + next(); + fdepth++; bxdepth++; + while (!is_op(s_rb)) { + if (tk == T_EOF) die("missing }"); + if (stmtstart()) { stmt(); continue; } + c = expr(); rval(); + if (accept(s_semi)) { op(O_DROP); discard(c); c = C_PLAIN; continue; } + if (!is_op(s_rb)) die("expected ; or } after an expression"); + tail = 1; t = xty; + } + next(); + if (tail) c = handup(c); else lit(0); + block_close(save); + fdepth--; bxdepth--; + lv = 0; lvk = LV_NONE; xty = t; xzero = 0; + return c; +} + +// ---- constant expressions ---- +fn cprim() +{ + let v, s; + if (tk == T_NUM) { v = tv; next(); return v; } + if (accept(s_minus)) return 0 - cprim(); + if (tk == T_OP && tv == uname[2]) { next(); return ~cprim(); } + if (accept(s_lp)) { v = cexpr(); expect(s_rp); return v; } + if (tk == T_ID) { + s = lookup(tv); + if (s >= 0 && (skind[s] == K_CONST || skind[s] == K_GARR || (skind[s] == K_FN && (sflag[s] & F_DEF)) || skind[s] == K_GVAR)) { next(); return sval[s]; } + } + die("expected a constant"); + return 0; +} +fn cexpr() +{ + let v = cprim(), o; + while (tk == T_OP) { + o = tv; + if (o == bname[3]) { next(); v = v + cprim(); } + else if (o == bname[4]) { next(); v = v - cprim(); } + else if (o == bname[0]) { next(); v = v * cprim(); } + else if (o == bname[15]) { next(); v = v | cprim(); } + else if (o == bname[13]) { next(); v = v & cprim(); } + else if (o == bname[5]) { next(); v = v << cprim(); } + else if (o == bname[6]) { next(); v = v >> cprim(); } + else return v; + } + return v; +} + +// ---- statements ---- +fn stmt() +{ + let s, c; + if (tk == T_ID && findloc(tv) < 0) { + s = lookup(tv); + if (s >= 0 && skind[s] == K_KW && (sflag[s] & F_STMT)) { kwcall(s, 0); return 0; } + } + if (tstart()) { localdecl(1); return 0; } + if (tk == T_OP) { + if (tv == s_semi) { next(); return 0; } + if (tv == s_lb) { block(); return 0; } + } + c = expr(); rval(); op(O_DROP); + expect(s_semi); + discard(c); + return 0; +} +fn block() +{ + let save = nloc; + expect(s_lb); + fdepth++; + while (!is_op(s_rb)) { if (tk == T_EOF) die("missing }"); stmt(); } + next(); + block_close(save); + fdepth--; + return 0; +} +// the body of if/while/for: its own block, even without braces +fn substmt() +{ + let save = nloc; + if (is_op(s_lb)) return block(); + fdepth++; + stmt(); + block_close(save); + fdepth--; + return 0; +} +// let a = e, b[10]; or, typed: int a = 1, b; *node.m p = new(node); .u r; +// let infers pointer, struct and function types from the initializer and +// keeps numbers as plain words. +fn localdecl(typed) +{ + let n, i, c, k, t = 0, tg = 0; + if (typed) { dtype(); t = dty; tg = dtag; if (t == tvoid) die("a variable can't be void"); } + while (1) { + n = name(); + i = newloc(n); + ltag[i] = tg; + if (accept(s_lk)) { + k = cexpr(); expect(s_rk); + if (!t) t = tword; + larr[i] = 1; ltype[i] = tarr(t, k); fslots = fslots + tsz(ltype[i]); + } else if (t && isagg(t)) { larr[i] = 1; ltype[i] = t; fslots = fslots + tsz(t); } + else { fslots++; if (t) ltype[i] = t; } + if (accept(s_eq)) { + if (larr[i]) die("an array or a struct can't be initialized with ="); + c = expr(); rval(); laddr(lslot[i]); op(O_ST); + if (t) { if (!compat(t, xty)) tcerr("initializer", t, xty); } + else if (!isnum(xty)) ltype[i] = xty; + assigncheck(LV_LOCAL, i, c); + } + if (!accept(s_comma)) break; + } + expect(s_semi); + return 0; +} +fn kw_var() { stmtonly(); next(); return localdecl(0); } +fn kw_if() +{ + let a, b, c, s0, sa, live0, livea; + stmtonly(); + next(); + expect(s_lp); c = expr(); rval(); plainuse(c); expect(s_rp); + a = hole(); op(O_JZ); + live0 = live; + s0 = snap_push(); + substmt(); + livea = live; sa = snap_push(); + if (is_id(s_else)) { + next(); + b = hole(); op(O_JMP); + setlit(a, here); + snap_restore(s0); live = live0; + substmt(); + setlit(b, here); + } else { setlit(a, here); snap_restore(s0); live = live0; } + merge(sa, livea); + snap_pop(s0); + return 0; +} +// loop frame: 0 continue target 1 break chain 2 depth 3 snapshot 4 block-expression depth +fn loop_push(cont, s0) +{ + let f = R_LOOP + lsp * 6; + if (lsp >= 60) die("loops nested too deeply"); + f[0] = cont; f[1] = 0; f[2] = fdepth; f[3] = s0; f[4] = bxdepth; + lsp++; + return 0; +} +fn loop_pop() { lsp--; fixall(R_LOOP[lsp * 6 + 1], here); return 0; } +fn kw_while() +{ + let c0 = here, a, s0, c; + stmtonly(); + next(); + s0 = snap_push(); + expect(s_lp); c = expr(); rval(); plainuse(c); expect(s_rp); + a = hole(); op(O_JZ); + loop_push(c0, s0); + substmt(); + loopcheck(s0); + jmp(c0); + setlit(a, here); + loop_pop(); + snap_restore(s0); live = 1; + snap_pop(s0); + return 0; +} +fn kw_for() +{ + let save = nloc, s0, c0, a, b, st, c; + stmtonly(); + next(); + expect(s_lp); + fdepth++; + if (is_id(s_var)) kw_var(); + else if (tstart()) localdecl(1); + else { if (!is_op(s_semi)) { c = expr(); rval(); op(O_DROP); discard(c); } expect(s_semi); } + s0 = snap_push(); + c0 = here; + if (!is_op(s_semi)) { c = expr(); rval(); plainuse(c); } else lit(1); + expect(s_semi); + a = hole(); op(O_JZ); + b = hole(); op(O_JMP); + st = here; + if (!is_op(s_rp)) { c = expr(); rval(); op(O_DROP); discard(c); } + expect(s_rp); + jmp(c0); + setlit(b, here); + loop_push(st, s0); + substmt(); + loopcheck(s0); + jmp(st); + setlit(a, here); + loop_pop(); + snap_restore(s0); live = 1; + snap_pop(s0); + block_close(save); + fdepth--; + return 0; +} +fn loopexit(f) +{ + if (lsp <= loopbase) die("break or continue outside a loop"); + if (f[4] != bxdepth) die("break and continue can't leave a block expression"); + loopcheck(f[3]); + exit_vars(f[2] + 1, 0); + return 0; +} +fn kw_break() +{ + let f = R_LOOP + (lsp - 1) * 6; + stmtonly(); + next(); expect(s_semi); + loopexit(f); + f[1] = fixnew(here, f[1]); + lit(MW - 1); op(O_JMP); + live = 0; + return 0; +} +fn kw_continue() +{ + let f = R_LOOP + (lsp - 1) * 6; + stmtonly(); + next(); expect(s_semi); + loopexit(f); + jmp(f[0]); + live = 0; + return 0; +} +fn kw_return() +{ + let c; + stmtonly(); + next(); + if (!infn) die("return outside a function"); + if (bxdepth) die("return can't leave a block expression"); + if (is_op(s_semi)) { lit(0); c = C_PLAIN; } + else { + c = expr(); rval(); + if (fty == tvoid) tcerr("return value", tvoid, xty); + else if (fty && !compat(fty, xty)) tcerr("return value", fty, xty); + } + expect(s_semi); + retcheck(c); + exit_vars(1, 0); + epichain = fixnew(here, epichain); + lit(MW - 1); op(O_JMP); + live = 0; + return 0; +} +fn kw_unchecked() +{ + let old = fchk; + stmtonly(); + next(); + fchk = 0; + substmt(); + fchk = old; + return 0; +} + +// ---- asm blocks: raw vm words, one per token ---- +// numbers push, -n pushes a negative number, #n emits a raw word, one-char +// tokens from the mnemonic table are opcodes, names push the address of a +// variable or function, :name defines a label for this block. +fn asmblock() +{ + let nl = 0, nr = 0, i, k, c, s, ln = R_ASM, la = R_ASM + 64, rn = R_ASM + 128, ra = R_ASM + 384; + expect(s_lb); + while (!is_op(s_rb)) { + if (tk == T_EOF) die("unterminated asm block"); + if (tk == T_NUM) { lit(tv); next(); continue; } + if (tk == T_STR) { strlit(tv); next(); continue; } + if (is_op(s_hash)) { next(); if (tk != T_NUM) die("# needs a number"); emit(tv); next(); continue; } + if (is_op(s_minus)) { next(); if (tk == T_NUM && tglue) { lit(0 - tv); next(); } else op(O_SUB); continue; } + if (is_op(s_colon)) { next(); if (nl >= 64) die("too many asm labels"); ln[nl] = name(); la[nl] = here; nl++; continue; } + if (tv[0] && !tv[1] && (c = mnemidx(tv[0])) >= 0) { op(c); next(); continue; } + if (tk != T_ID) die("bad token in asm"); + i = findloc(tv); + if (i >= 0) { lused[i] = 1; laddr(lslot[i]); next(); continue; } + k = -1; i = 0; + while (i < nl) { if (ln[i] == tv) k = i; i++; } + if (k >= 0) { lit(la[k]); next(); continue; } + s = lookup(tv); + if (s >= 0 && skind[s] == K_ENH) { op(XOP0 + sval[s]); next(); continue; } + if (s >= 0 && skind[s] != K_FN && skind[s] != K_KW) { lit(sval[s]); next(); continue; } + if (s >= 0 && skind[s] == K_FN) { fnref(s); next(); continue; } + if (nr >= 250) die("too many asm label references"); + rn[nr] = tv; ra[nr] = hole(); nr++; next(); // a label further down this block + } + next(); + i = 0; + while (i < nr) { + k = -1; c = 0; + while (c < nl) { if (ln[c] == rn[i]) k = c; c++; } + if (k < 0) dien(rn[i], "unknown name in asm"); + setlit(ra[i], la[k]); + i++; + } + return 0; +} +fn kw_asm() { next(); asmblock(); xcls = C_PLAIN; return 0; } + +// ---- quote: code as data ---- +// a token list is an object: [n] then kind, value for each token. names, +// strings and operators are interned pointers. +fn grab() -> .u +{ + let n = 0, depth = 0, q; + if (!is_op(s_lb)) die("expected {"); + next(); + while (1) { + if (tk == T_EOF) die("unterminated quote"); + if (is_op(s_lb)) depth++; + if (is_op(s_rb)) { if (!depth) break; depth--; } + if (n >= 4000) die("quote too long"); + R_QBUF[n + n] = tk; R_QBUF[n + n + 1] = tv; n++; + next(); + } + next(); + q = new(n + n + 1); + q[0] = n; + wcopy(q + 1, R_QBUF, n + n); + return u*q; +} +unchecked fn qnew(src) -> .u { let n = src[0] * 2 + 1, q = new(n); wcopy(q, src, n); return q; } +fn qdup(.m q) -> .u { let n = q[0] * 2 + 1, r = new(n); wcopy(r, q, n); return u*r; } +fn wcopyto(.m q, n) { let i = 0; while (i < n) { emit(q[i]); i++; } return 0; } +fn kw_quote() +{ + let q, j, a, n; + next(); + q = grab(); + j = hole(); op(O_JMP); + a = here; + n = q[0] * 2 + 1; + wcopyto(q, n); + free(d*q); + setlit(j, here); + lit(a); + callname(s_qnew); + xcls = C_FRESH; + return 0; +} +// tokens of a string, as a token list +fn tokens(s) -> .u +{ + let n = 0, q; + src_push(2, s, 0, 0); + next(); + while (tk != T_EOF) { + if (n >= 4000) die("too many tokens"); + R_QBUF[n + n] = tk; R_QBUF[n + n + 1] = tv; n++; + next(); + } + src_pop(); + q = new(n + n + 1); + q[0] = n; + wcopy(q + 1, R_QBUF, n + n); + return u*q; +} +// feed a token list into the input: the parser reads it next, then carries +// on where it was. the list is freed once it has been read. +unchecked fn feed(.d q) { src_push(1, q, 1, 1); next(); return 0; } + +// ---- nested compiles ---- +// compile() and load() can run while another function is being compiled (from +// a parse word). the function context is saved, the new code is emitted in +// place with a jump around it, and then everything is put back. +fn ctx_push() +{ + let f = R_CTX + ctxsp * 24; + if (ctxsp >= 30) die("compiles nested too deeply"); + ctxsp++; + f[0] = nloc; f[1] = lbase; f[2] = fdepth; f[3] = fslots; f[4] = fchk; f[5] = fret; f[6] = live; + f[7] = bxdepth; f[8] = epichain; f[9] = fframe; f[10] = infn; f[11] = lsp; f[12] = loopbase; + f[13] = lv; f[14] = lvk; f[15] = lvi; f[16] = kw_expr; f[17] = xcls; f[18] = snaptop; + f[19] = fty; f[20] = ntp; f[21] = xty; wcopy(f + 22, tpn, 2); + return 0; +} +fn ctx_pop() +{ + let f; + ctxsp--; + f = R_CTX + ctxsp * 24; + nloc = f[0]; lbase = f[1]; fdepth = f[2]; fslots = f[3]; fchk = f[4]; fret = f[5]; live = f[6]; + bxdepth = f[7]; epichain = f[8]; fframe = f[9]; infn = f[10]; lsp = f[11]; loopbase = f[12]; + lv = f[13]; lvk = f[14]; lvi = f[15]; kw_expr = f[16]; xcls = f[17]; snaptop = f[18]; + fty = f[19]; ntp = f[20]; xty = f[21]; wcopy(tpn, f + 22, 2); + return 0; +} + +// ---- functions and the top level ---- +fn fn_begin(chk, ret) +{ + lbase = nloc; fdepth = 1; fslots = 0; fchk = chk && check_on; fret = ret; fty = 0; + live = 1; bxdepth = 0; epichain = 0; infn = 1; loopbase = lsp; lv = 0; lvk = LV_NONE; + return 0; +} +fn prologue() { lit(ZFP); op(O_LD); fframe = hole(); op(O_SUB); lit(ZFP); op(O_ST); return 0; } +fn epilogue() +{ + lit(0); + fixall(epichain, here); + lit(ZFP); op(O_LD); lit(fslots); op(O_ADD); lit(ZFP); op(O_ST); op(O_RET); + setlit(fframe, fslots); + infn = 0; + nloc = lbase; + return 0; +} +fn body() +{ + expect(s_lb); + while (!is_op(s_rb)) { if (tk == T_EOF) die("missing } at the end of a function"); stmt(); } + next(); + unused_check(lbase); + if (fchk && live) { exit_vars(1, 1); if (fret) cerr("a -> .u function has to end with a return"); } + return 0; +} +// fn name(int a, *node.m n, .d x, b) -> *list.u { ... } +fn fndef(chk) +{ + let n, s, np = 0, tags = 0, t, ret = 0, sig, i, pn[24], pt[24], pty[24], rty = 0, typed = 0, ft; + next(); + n = name(); + ntp = 0; + if (is_op(s_lt)) { // type parameters + next(); + while (1) { if (ntp >= 8) die("too many type parameters"); tpn[ntp] = name(); ntp++; if (!accept(s_comma)) break; } + closeangle(); + typed = 1; + } + s = lookup(n); + if (s < 0 || s < ubase || skind[s] != K_FN || (sflag[s] & F_DEF)) { s = addsym(n, K_FN, 0); sflag[s] = 0; } + expect(s_lp); + if (!is_op(s_rp)) { + while (1) { + t = 0; + pty[np] = tword; + if (!is_op(s_dot) && ((tk == T_OP && (tv == s_star || tv == s_lk)) || (tk == T_ID && (tv == s_fn || istypename(tv))))) { + pty[np] = ptype(); typed = 1; + if (isagg(pty[np])) die("structs and arrays are passed by pointer"); + } + if (accept(s_dot)) { // .m lends, .d takes over + if (tv == s_m) t = 1; else if (tv == s_d) t = 2; else die("a parameter tag is .m or .d"); + next(); + } + pn[np] = name(); + if (np >= 12 && t) die("only the first 12 parameters can be tagged"); + pt[np] = t; + if (np < 12) tags = tags + (t << (np + np)); + np++; + if (np >= 24) die("too many parameters"); + if (!accept(s_comma)) break; + } + } + expect(s_rp); + if (accept(s_arrow)) { + if (!is_op(s_dot)) { rty = ptype(); typed = 1; } + if (accept(s_dot)) { if (!is_id(s_u)) die("the return tag is .u"); next(); ret = 1; } + } + sig = ret + np + np + tags * 64; + if (sflag[s] & F_SIG) { if (ssig[s] != sig) dien(n, "doesn't match its earlier declaration"); } + else if ((sflag[s] & F_UNTYPED) && (tags || ret)) dien(n, "was called before its tags were declared; put a prototype above the first call"); + ssig[s] = sig; + sflag[s] = sflag[s] | F_SIG; + if (ntp) sflag[s] = sflag[s] | F_GEN; + if (typed) { + if (!rty) rty = tword; + ft = tfn(rty, np, pty, sig); + if (stype[s] && stype[s] != ft) dien(n, "doesn't match its earlier declaration"); + stype[s] = ft; + } + if (accept(s_semi)) { ntp = 0; return s; } + if (sflag[s] & F_DEF) dien(n, "is defined twice"); + sval[s] = here; + sflag[s] = sflag[s] | F_DEF; + fixall(sfix[s], here); sfix[s] = 0; + fn_begin(chk, ret); + if (typed) fty = rty; + prologue(); + i = 0; + while (i < np) { + t = newloc(pn[i]); fslots++; + ltype[t] = pty[i]; + if (fchk && pt[i] == 1) lstate[t] = S_BORROW; + if (fchk && pt[i] == 2) { lstate[t] = S_M; lown[t] = 1; } + i++; + } + i = np; + while (i) { i--; laddr(i); op(O_ST); } + body(); + epilogue(); + ntp = 0; + return s; +} +// let g = 1, buf[10]; int g = 3; [10]int buf; point origin; +fn gdecl(typed) +{ + let n, a, k, t = 0, s; + if (typed) { dtype(); t = dty; if (dtag) die("tags go on locals and parameters, not globals"); if (t == tvoid) die("a variable can't be void"); } + else next(); + while (1) { + n = name(); + if (accept(s_lk)) { + k = cexpr(); expect(s_rk); + s = addsym(n, K_GARR, here); + if (!t) t = tword; + stype[s] = tarr(t, k); + k = tsz(stype[s]); + while (k) { emit(0); k--; } + } else if (t && isagg(t)) { + s = addsym(n, K_GARR, here); stype[s] = t; + k = tsz(t); + while (k) { emit(0); k--; } + } else { + a = here; + s = addsym(n, K_GVAR, a); + if (t) stype[s] = t; + emit(0); + if (accept(s_eq)) { + if (tk == T_STR) { *(a + hdelta) = here; k = tv; while (*k) { emit(*k); k++; } emit(0); next(); } + else *(a + hdelta) = cexpr(); + } + } + if (!accept(s_comma)) break; + } + expect(s_semi); + return 0; +} +fn gconst() +{ + let n; + next(); + while (1) { n = name(); expect(s_eq); addsym(n, K_CONST, cexpr()); if (!accept(s_comma)) break; } + expect(s_semi); + return 0; +} +// enhance name(a, b) [-> n] micro "recipe" [else fn]; +// enhance name(a, b) [-> n] vm { asm } [else fn]; +// lays out the descriptor the install opcode reads, and wires it in right +// away (an image wires its own in when it boots). +fn enhdef() +{ + let n, np = 0, nout = 1, dsc, na, pay, len = 0, kind = 0, i, p, s, slot, r, pn[8]; + next(); + n = name(); + expect(s_lp); + if (!is_op(s_rp)) { while (1) { if (np == 8) die("an enhancement takes at most 8 inputs"); pn[np] = name(); np++; if (!accept(s_comma)) break; } } + expect(s_rp); + if (accept(s_arrow)) { if (tk != T_NUM || tv < 0 || tv > 8) die("expected the number of outputs"); nout = tv; next(); } + dsc = here; i = 0; while (i < 7) { emit(0); i++; } + na = here; i = 0; while (i < np) { emit(0); i++; } + i = 0; + while (i < np) { *(na + i + hdelta) = here; p = pn[i]; while (*p) { emit(*p); p++; } emit(0); i++; } + if (accept(s_micro)) { + pay = here; + if (tk != T_STR) die("expected the recipe as a string"); + while (tk == T_STR) { p = tv; while (*p) { emit(*p); p++; } next(); } + emit(0); + } else if (accept(s_vm)) { pay = here; asmblock(); op(O_RET); len = here - pay; kind = 1; } + else die("expected micro or vm"); + slot = nenh; + if (slot >= 512) die("all 512 enhancement slots are taken"); + nenh++; + p = dsc + hdelta; + p[0] = slot; p[1] = kind; p[2] = np; p[3] = nout; p[4] = pay; p[5] = len; p[6] = na; + s = addsym(n, K_ENH, slot); + ssig[s] = np + nout * 16 + dsc * 256; + if (accept(s_else)) { r = lookup(tv); if (r < 0) r = fwdfn(tv); next(); sfix[s] = r + 1; } + expect(s_semi); + if (!hdelta) { + r = install(dsc); + if (r < 0) { + if (!sfix[s]) { puts("zc: line "); putn(tline); puts(": "); puts(n); puts(": the enhancement didn't install ("); putn(r); puts(")\n"); halt(); } + sflag[s] = sflag[s] | F_FALLBACK; + } + } + return 0; +} +fn topitem() +{ + let chk = 1, imm = 0, s, k; + if (is_id(s_unchecked)) { + next(); + if (accept(s_semi)) { check_on = 0; return 0; } // unchecked; switches the analyzer off from here on + chk = 0; + } + if (is_id(s_immediate)) { next(); imm = 1; } + if (is_id(s_fn)) { + s = fndef(chk); + if (imm) { + if (hdelta) die("parse words can't be defined while building an image"); + k = addsym(sname[s], K_KW, sval[s]); + sflag[k] = F_DEF | F_STMT | F_EXPR | F_TOP; + } + return 0; + } + if (!chk || imm) die("expected fn"); + if (is_id(s_var)) return gdecl(0); + if (is_id(s_struct)) { kw_struct(); return 0; } + if (is_id(s_type)) return kw_type(); + if (is_id(s_typecheck)) return kw_typecheck(); + if (is_id(s_enhance)) return enhdef(); + if (is_id(s_const)) return gconst(); + if (tk == T_ID) { + s = lookup(tv); + if (s >= 0 && skind[s] == K_KW && (sflag[s] & F_TOP)) { kwcall(s, 0); return 0; } + } + if (tstart()) return gdecl(1); + die("expected fn, let, const, struct, type or a declaration"); + return 0; +} +fn unit_top() { while (tk != T_EOF) topitem(); return 0; } +fn unit_end(from) +{ + let i = from; + while (i < nsym) { + if (skind[i] == K_FN && !(sflag[i] & F_DEF) && sfix[i]) dien(sname[i], "is used but never defined"); + i++; + } + return 0; +} + +// compile a token list as the body of a new function, or (top) as top-level +// declarations. returns the entry address of the function. +unchecked fn compile_list(q, top) +{ + let hop = -1, entry = 0, from = nsym; + ctx_push(); + if (infn) { hop = hole(); op(O_JMP); } + src_push(1, q, 0, 0); + next(); + if (top) unit_top(); + else { + entry = here; + fn_begin(1, 0); + prologue(); + while (tk != T_EOF) stmt(); + unused_check(lbase); + if (fchk && live) exit_vars(1, 1); + epilogue(); + } + src_pop(); + unit_end(from); + if (hop >= 0) setlit(hop, here); + if (here > ptop) ptop = here; + ctx_pop(); + return entry; +} +fn compile(q) { return compile_list(q, 0); } +fn load(q) { compile_list(q, 1); return 0; } +fn eval(q) { let f = compile_list(q, 0); return f(); } +fn evals(s) { let q = tokens(s), r = eval(q); free(d*q); return r; } + +// ---- symbol blob ---- +// an image ends with its own symbol table so the genome knows its names: +// kind, value, signature, name chars, 0 ... then a 0 kind. +fn blob_emit(from) +{ + let i = from, p; + while (i < nsym) { + if ((skind[i] >= K_FN && skind[i] <= K_CONST) || skind[i] == K_ENH) { + emit(skind[i]); emit(sval[i]); emit(ssig[i]); + p = sname[i]; + while (*p) { emit(*p); p++; } + emit(0); + } + i++; + } + emit(0); + return 0; +} +fn blob_load(p) +{ + let s, n; + while (*p) { + n = intern(p + 3); + s = addsym(n, p[0], p[1]); + ssig[s] = p[2]; + sflag[s] = F_DEF | F_SIG; + if (p[0] == K_ENH) { if (p[1] >= nenh) nenh = p[1] + 1; install(p[2] >> 8); } + p = p + 3 + strlen(p + 3) + 1; + } + return 0; +} +// compile the rest of stdin as a genome image that will run at org, writing +// it to dst. only the source's own names are visible while it compiles. +// returns the image length in words. +fn build_image(org, dst) +{ + let sh = here, sd = hdelta, sl = hlimit, sub = ubase, svl = vislo, sns = nsym, sen = nenh, n; + ubase = nsym; vislo = nsym; nenh = 0; + here = org; hdelta = dst - org; hlimit = org + 0x30000; + lit(FTOP); lit(ZFP); op(O_ST); fnref(fwdfn(s_start)); op(O_JMP); emit(0); emit(0); + src_push(0, 0, 0, 0); + next(); + unit_top(); + unit_end(ubase); + src_pop(); + *(dst + 5) = here; + blob_emit(ubase); + *(dst + 6) = here; + n = here - org; + popsyms(sns); + ubase = sub; vislo = svl; here = sh; hdelta = sd; hlimit = sl; nenh = sen; + return n; +} + +// ---- setup ---- +fn gc_roots() +{ + markrange(0, ptop); + if (!hdelta && here > ptop) markrange(ptop, here); // globals of a unit still compiling + markrange(GORG, GORG[6]); + markrange(R_SRC, R_SRC + 512); + markrange(*ZFP, FTOP); + markrange(DSLO, DSHI); + return 0; +} +fn zc_init() +{ + pooltop = R_POOL; + wfill(R_IH, 0, IHN); + nsym = 0; nfixu = 0; fixfree = 0; nbin = 0; nun = 0; nasg = 0; nkop = 0; seqtop = 0; + lsp = 0; ctxsp = 0; snaptop = 0; nloc = 0; lbase = 0; + s_lp = intern("("); s_rp = intern(")"); s_lb = intern("{"); s_rb = intern("}"); + s_lk = intern("["); s_rk = intern("]"); s_comma = intern(","); s_semi = intern(";"); + s_eq = intern("="); s_arrow = intern("->"); s_inc = intern("++"); s_dec = intern("--"); + s_star = intern("*"); s_amp = intern("&"); s_colon = intern(":"); s_hash = intern("#"); + s_minus = intern("-"); s_dot = intern("."); s_andand = intern("&&"); s_oror = intern("||"); + s_fn = intern("fn"); s_var = intern("let"); s_const = intern("const"); s_else = intern("else"); + s_as = intern("as"); s_unchecked = intern("unchecked"); s_immediate = intern("immediate"); + s_m = intern("m"); s_d = intern("d"); s_u = intern("u"); + s_start = intern("start"); s_main = intern("main"); s_free = intern("free"); + s_own = intern("own"); s_qnew = intern("qnew"); + s_enhance = intern("enhance"); s_micro = intern("micro"); s_vm = intern("vm"); + return 0; +} +// operators and keywords. these are plain tables in memory: a program can +// add operators, change precedences or replace keywords with its own. +fn zc_tables() +{ + add_unop("!", "0 $ 0 ~ - | 31 > 1 +"); // index 0 + add_unop("!!", "0 $ 0 ~ - | 31 > 0 ~ -"); // index 1: normalize to 0 or 1 + add_unop("~", "-1 ^"); // index 2 + add_unop("-", "0 ~ -"); + add_binop("*", 10, "*"); // the first few indexes are used by cexpr + add_binop("/", 10, "idiv"); // native divide, wired in by rt.zc + add_binop("%", 10, "imod"); + add_binop("+", 9, "+"); // 3 + add_binop("-", 9, "-"); // 4 + add_binop("<<", 8, "ishl"); // 5 + add_binop(">>", 8, ">"); // 6 + add_binop("<", 7, "<"); + add_binop(">", 7, "~ <"); + add_binop("<=", 7, "~ < 1 ^"); + add_binop(">=", 7, "< 1 ^"); + add_binop("==", 6, "- 0 $ 0 ~ - | 31 > 1 +"); + add_binop("!=", 6, "- 0 $ 0 ~ - | 31 > 0 ~ -"); + add_binop("&", 5, "&"); // 13 + add_binop("^", 4, "^"); + add_binop("|", 3, "|"); // 15 + add_binop("&&", -1, 0); + add_binop("||", -2, 0); + addkop(s_eq); addkop(s_arrow); addkop(s_inc); addkop(s_dec); + add_kw("let", kw_var, F_STMT); + add_kw("if", kw_if, F_STMT); + add_kw("while", kw_while, F_STMT); + add_kw("for", kw_for, F_STMT); + add_kw("break", kw_break, F_STMT); + add_kw("continue", kw_continue, F_STMT); + add_kw("return", kw_return, F_STMT); + add_kw("unchecked", kw_unchecked, F_STMT); + add_kw("asm", kw_asm, F_STMT | F_EXPR); + add_kw("quote", kw_quote, F_EXPR); + add_kw("sizeof", kw_sizeof, F_EXPR); + add_kw("typeof", kw_typeof, F_EXPR); + return 0; +} +fn start() +{ + let s, f; + heap_init(); + zc_init(); + blob_load(GORG[5]); + zc_tables(); + types_init(); + here = 0; hdelta = 0; hlimit = HEAP_LO; ptop = 0; + ubase = nsym; vislo = 0; + src_push(0, 0, 0, 0); + next(); + unit_top(); + unit_end(ubase); + if (here > ptop) ptop = here; + s = lookup(s_main); + if (s >= 0 && skind[s] == K_FN && (sflag[s] & F_DEF)) { f = sval[s]; f(); } + halt(); + return 0; +} diff --git a/docs/history/11-types-enhancements/code/src/rt.zc b/docs/history/11-types-enhancements/code/src/rt.zc new file mode 100644 index 0000000..750a2fe --- /dev/null +++ b/docs/history/11-types-enhancements/code/src/rt.zc @@ -0,0 +1,183 @@ +// ============================================================================ +// rt.zc: the runtime half of the genome. memory map, i/o, arithmetic helpers, +// the heap (size-class free lists) and the mark-sweep collector. +// ============================================================================ + +// ---- memory map (words) ---- +const MW = 0x100000; // 1m words of memory +const HEAP_LO = 0x40000; // heap: 0x40000 .. 0xA0000 +const HEAP_HI = 0xA0000; +const ZFP = 0xA0000; // frame pointer word, used by every prologue +const GORG = 0xC0000; // the genome (this code) lives here +const FBOT = 0xE0000; // frame stack: FBOT .. FTOP, grows down +const FTOP = 0xF0000; +const RS0 = 0xF0000; // return stack grows up from here +const MARK0 = 0xF8000; // mark stack for the collector +const MARKN = 0x4000; +const DSLO = 0xFC000; // data stack: DSLO .. MW-16, grows down +const DSHI = 0xFFFF0; +const MAGIC = 0x2B7E1516; + +// ---- virtual enhancements ---- +// install(desc) wires a custom opcode into the cpu (the % opcode). these three +// give the language native division and left shift (zdiv, zmod and zshl +// below are the same thing done in software). they come first, since the +// compiler resolves / % and << to them by name. +fn install(desc) { return asm { desc @ % }; } +enhance idiv(a, b) micro "a = sdiv a b"; +enhance imod(a, b) micro "a = srem a b"; +enhance ishl(a, b) micro "a = shl a b"; + +// ---- i/o ---- +fn putc(ch) { asm { ch @ . } return 0; } +fn getc() { return asm { , }; } +fn eof() { return asm { ? }; } +fn halt() { asm { q } return 0; } +fn puts(s) { let i = 0; while (s[i]) { putc(s[i]); i++; } return 0; } +fn putn(n) +{ + let b[12], i = 0; + if (n < 0) { putc('-'); n = 0 - n; } + if (n < 0) { puts("2147483648"); return 0; } + while (1) { b[i++] = '0' + n % 10; n = n / 10; if (!n) break; } + while (i) putc(b[--i]); + return 0; +} +fn strlen(s) { let n = 0; while (s[n]) n++; return n; } +fn streq(a, b) { let i = 0; while (a[i] && a[i] == b[i]) i++; return a[i] == b[i]; } +fn wcopy(dst, src, n) { let i = 0; while (i < n) { dst[i] = src[i]; i++; } return 0; } +fn wfill(dst, v, n) { let i = 0; while (i < n) { dst[i] = v; i++; } return 0; } + +// ---- arithmetic the hardware doesn't have ---- +let pow2[32]; +let zrem; +fn zudiv(n, dv) +{ + let q = 0, r = 0, i = 30; + if (n < dv) { zrem = n; return 0; } + while (i >= 0) { + r = r + r + ((n >> i) & 1); + q = q + q; + if (r >= dv) { r = r - dv; q++; } + i--; + } + zrem = r; + return q; +} +fn zdiv(a, b) +{ + let s = 0, q; + if (b == 0) { zrem = a; return 0; } + if (a < 0) { a = 0 - a; s = 1; } + if (b < 0) { b = 0 - b; s = s ^ 1; } + q = zudiv(a, b); + if (s) return 0 - q; + return q; +} +fn zmod(a, b) { let neg = a < 0; if (b == 0) return a; zdiv(a, b); if (neg) return 0 - zrem; return zrem; } +fn zshl(a, b) { return a * pow2[b & 31]; } + +// ---- heap ---- +// every block is a power of two words with a 6 word header before the +// pointer the program sees: +// p[-6] block size p[-5] size class p[-4] flags (1 live, 2 marked) +// p[-3] p ^ MAGIC p[-2] owner (the container that owns it, or 0) +// p[-1] its type (a type record from new(T), or 0 from new(n)) +// freed blocks keep size and class so the sweep can walk the heap linearly, +// and go on a free list per class (the link is in p[0]). +const HDR = 6; +let hbump, freel[32], gc_runs, gc_freed, gc_auto = 1; + +fn heap_init() +{ + let i = 1; + pow2[0] = 1; + while (i < 32) { pow2[i] = pow2[i - 1] + pow2[i - 1]; i++; } + hbump = HEAP_LO; + wfill(freel, 0, 32); + return 0; +} +fn isobj(x) { return x >= HEAP_LO + HDR && x < hbump && x[-3] == (x ^ MAGIC) && (x[-4] & 1); } + +fn oom() { puts("zc: out of heap memory\n"); halt(); return 0; } +// n words typed t +unchecked fn new_t(n, t) -> .u +{ + let need = n + HDR, k = 3, sz = 8, b = 0, p; + if (n < 0) need = HDR; + while (sz < need) { sz = sz + sz; k++; } + if (freel[k]) { b = freel[k]; freel[k] = b[HDR]; } + else if (hbump + sz <= HEAP_HI) { b = hbump; hbump = hbump + sz; } + else { + if (gc_auto) gc(); + if (freel[k]) { b = freel[k]; freel[k] = b[HDR]; } + else if (hbump + sz <= HEAP_HI) { b = hbump; hbump = hbump + sz; } + else oom(); + } + p = b + HDR; + b[0] = sz; b[1] = k; b[2] = 1; b[3] = p ^ MAGIC; b[4] = 0; b[5] = t; + wfill(p, 0, sz - HDR); + return p; +} +unchecked fn new(n) -> .u { return new_t(n, 0); } +fn osize(p) { if (!isobj(p)) return 0; return p[-6] - HDR; } +fn typeof_obj(p) { if (!isobj(p)) return 0; return p[-1]; } + +// frees p and, recursively, every object p owns +fn release(p) +{ + let b = p - HDR, n = b[0] - HDR, i = 0, w; + b[2] = 0; + while (i < n) { w = p[i]; if (isobj(w) && w[-2] == p) release(w); i++; } + b[4] = 0; + b[HDR] = freel[b[1]]; + freel[b[1]] = b; + return 0; +} +unchecked fn free(.d p) { if (isobj(p)) release(p); return 0; } +fn own(c, p) { if (isobj(c)) c[-2] = p; return c; } + +// ---- mark-sweep ---- +// conservative and non-moving. roots: the program area (globals live +// between functions), the genome's globals, the compiler's input stack, +// the live part of the frame stack and the whole data stack region. +let ms_top, ms_over, gc_lo, gc_hi; +fn mark(w) +{ + if (!isobj(w) || (w[-4] & 2)) return 0; + w[-4] = w[-4] | 2; + if (ms_top < MARKN) { MARK0[ms_top] = w; ms_top++; } else ms_over = 1; + return 0; +} +fn markrange(a, b) { while (a < b) { mark(*a); a++; } return 0; } +fn drain() +{ + let p; + while (ms_top) { ms_top--; p = MARK0[ms_top]; markrange(p, p + p[-6] - HDR); } + return 0; +} +fn gc_roots(); +unchecked fn gc() +{ + let b, sz, n = 0; + ms_top = 0; ms_over = 0; + gc_roots(); + drain(); + while (ms_over) { // the mark stack overflowed: rescan marked objects + ms_over = 0; + b = HEAP_LO; + while (b < hbump) { if (b[2] == 3) markrange(b + HDR, b + b[0]); b = b + b[0]; drain(); } + } + b = HEAP_LO; + while (b < hbump) { + sz = b[0]; + if (b[2] == 1) { b[2] = 0; b[4] = 0; b[HDR] = freel[b[1]]; freel[b[1]] = b; n++; } + else if (b[2] == 3) b[2] = 1; + b = b + sz; + } + gc_runs++; + gc_freed = gc_freed + n; + return n; +} +// number of live objects, for tests +fn live_objects() { let b = HEAP_LO, n = 0; while (b < hbump) { if (b[2] & 1) n++; b = b + b[0]; } return n; } diff --git a/docs/history/11-types-enhancements/code/test.sh b/docs/history/11-types-enhancements/code/test.sh new file mode 100755 index 0000000..02fac14 --- /dev/null +++ b/docs/history/11-types-enhancements/code/test.sh @@ -0,0 +1,64 @@ +#!/bin/bash +# full check: bootstraps the compiler from zc0, verifies the fixpoint, then runs +# the examples and tests on the portable interpreter (host), the arm64 jit under +# qemu, and the llvm backend. needs gcc; the jit half needs aarch64-linux-gnu-gcc +# and qemu-aarch64, the llvm half needs llvm-config and libLLVM (each is skipped +# if missing). +cd "$(dirname "$0")"; T=$(mktemp -d); trap 'rm -rf $T' EXIT +ok=0; bad=0 +chk(){ if [ "$1" = "$2" ]; then echo "ok $3"; ok=$((ok+1)); else echo "FAIL $3: got [$1]"; bad=$((bad+1)); fi; } +src(){ cat src/rt.zc src/compiler.zc; } +selfc(){ (cat tools/build.zc; printf '`'; src) | $1 > $2; } + +# bootstrap: zc0 (c) -> g0, g0 compiles compiler.zc -> g1, g1 compiles it -> g2 +gcc -O1 -w zc0.c -o $T/zc0 && gcc -O1 -w mkgenome.c -o $T/mkg || exit 1 +mkdir $T/s0 $T/s1 +cp core.c io_linux.c $T/s0/; cp core.c io_linux.c $T/s1/ +src | $T/zc0 > $T/s0/genome.h || exit 1 +gcc -O2 -w -DJTYPE=int $T/s0/core.c $T/s0/io_linux.c -o $T/zy0 +selfc $T/zy0 $T/g1.bin; $T/mkg < $T/g1.bin > $T/s1/genome.h +gcc -O2 -w -DJTYPE=int $T/s1/core.c $T/s1/io_linux.c -o $T/zy1 +selfc $T/zy1 $T/g2.bin +cmp -s $T/g1.bin $T/g2.bin && chk 1 1 "fixpoint: the compiler compiles itself to the same $(($(stat -c %s $T/g1.bin) / 4)) words" || chk 0 1 "fixpoint" +cmp -s $T/s1/genome.h genome.h && chk 1 1 "genome.h is up to date" || chk 0 1 "genome.h is stale (cp the bootstrap output over it)" + +run(){ # $1 = vm command, $2 = label + local vm="$1" l="$2" + chk "$($vm < examples/hello.zc)" "hello, world!" "$l hello" + chk "$($vm < examples/fact.zc | tr '\n' '/')" "1101110101111100000000/3628800/" "$l factorial" + chk "$( (cat examples/echo.zc; printf '`abc\n') | $vm)" "abc" "$l echo after the backtick" + chk "$($vm < examples/selfpatch.zc)" "ABC" "$l self-patching code" + chk "$($vm < examples/lists.zc | tr '\n' '/')" "[0 1 4 9 16 25]/live: 7/after the block: 0/" "$l ownership frees everything" + chk "$( (cat examples/repl.zc; printf '`fn sq(x) { return x * x; }`putn(sq(7)); putc(10);`puts("hi\\n");') | $vm | tr '\n' '/')" "49/hi/" "$l repl on the resident compiler" + chk "$($vm < examples/typed.zc | tr '\n' '/')" "last: 4/point is 2 words: x int, y int/vec<*point> is 3 words: len int, cap int, items **point/push is fn(*vec, T.d) -> bool/the heap says v is a vec, and v.items[0] is a point/" "$l typed example" + chk "$($vm < examples/wiring.zc)" "16 1234 25 4/9" "$l enhancement example" + chk "$($vm < tests/basics.zc | tr '\n' '/')" "285 610 42 512/hey 65 -3 -2 1024 -8/12456/757/9 100-101/5/ok/" "$l language basics" + chk "$($vm < tests/umd.zc)" "100 6 0 15 99 5 5" "$l u m d lifetimes" + chk "$($vm < tests/gc.zc)" "1000 1 900 1 199900" "$l mark-sweep collector" + chk "$($vm < tests/meta.zc | tr '\n' '/')" "1024 19/unless ok/rrr/15 42/7/42/7/144/" "$l parse words, quote, eval, load, operator tables" + chk "$($vm < tests/misc.zc | tr '\n' '/')" "45/54321/ok/" "$l compile-time eval, patched mnemonics, unchecked" + chk "$($vm < tests/types.zc | tr '\n' '/')" "12 5 2 15 56 42 7 14 3/rect *rect *list fn(int) -> *list.u fn(*list) -> T fn(word.d)/a:point@0 b:point@2 tag:int@4 /point 1 1/" "$l types, generics, reflection" + chk "$($vm < tests/typeok.zc)" "14" "$l a program the typechecker accepts" + chk "$($vm < tests/typeoff.zc)" "6" "$l the typechecker is off by default" + chk "$($vm < tests/enhance.zc)" "100 0 343 3,2 5 -2 0" "$l virtual enhancements" + local r; r=$(tests/runerr.sh "$vm" tests/errors | tail -1) + chk "$r" "rejected: $(ls tests/errors/*.zc | wc -l) passed, 0 failed" "$l analyzer rejects every bad program" + r=$(tests/runerr.sh "$vm" tests/typeerrors | tail -1) + chk "$r" "rejected: $(ls tests/typeerrors/*.zc | wc -l) passed, 0 failed" "$l typechecker rejects every bad program" +} +gcc -O2 -w -DJTYPE=int core.c io_linux.c -o $T/zy +run $T/zy "interp" +if command -v aarch64-linux-gnu-gcc >/dev/null && command -v qemu-aarch64 >/dev/null; then + aarch64-linux-gnu-gcc -O2 -w -DJTYPE=int -static core.c io_linux.c -o $T/zyj || exit 1 + printf '#!/bin/sh\nexec qemu-aarch64 %s "$@"\n' $T/zyj > $T/jit; chmod +x $T/jit + run $T/jit "jit" + selfc $T/jit $T/gj.bin; cmp -s $T/g1.bin $T/gj.bin && chk 1 1 "jit self-compile matches" || chk 0 1 "jit self-compile matches" +else echo "skip jit (no aarch64 toolchain or qemu)"; fi +if command -v llvm-config >/dev/null; then + gcc -O2 -w -DJTYPE=int -DZYLLVM $(llvm-config --cflags) core.c io_linux.c -o $T/zyl $(llvm-config --ldflags --libs) || exit 1 + run $T/zyl "llvm" + printf '#!/bin/sh\nZYHOT=1 exec %s "$@"\n' $T/zyl > $T/llall; chmod +x $T/llall + chk "$($T/llall < tests/types.zc | md5sum)" "$($T/zy < tests/types.zc | md5sum)" "llvm compiling every block agrees" + selfc $T/zyl $T/gl.bin; cmp -s $T/g1.bin $T/gl.bin && chk 1 1 "llvm self-compile matches" || chk 0 1 "llvm self-compile matches" +else echo "skip llvm (no llvm-config)"; fi +echo "$ok passed, $bad failed"; [ $bad = 0 ] diff --git a/docs/history/11-types-enhancements/code/tests/basics.zc b/docs/history/11-types-enhancements/code/tests/basics.zc new file mode 100644 index 0000000..c1f1faf --- /dev/null +++ b/docs/history/11-types-enhancements/code/tests/basics.zc @@ -0,0 +1,22 @@ +let table[10]; +let greeting = "hey"; +const N = 10, BIG = 0x100 * 2; +fn fib(n) { if (n < 2) return n; return fib(n - 1) + fib(n - 2); } +fn twice(f, x) { return f(f(x)); } +fn inc(x) { return x + 1; } +fn main() +{ + let i, s = 0, a[5]; + for (i = 0; i < N; i++) table[i] = i * i; + for (let j = 0; j < N; j++) s += table[j]; + putn(s); putc(' '); putn(fib(15)); putc(' '); putn(twice(inc, 40)); putc(' '); putn(BIG); putc(10); + puts(greeting); putc(' '); putn('A'); putc(' '); putn(-17 / 5); putc(' '); putn(-17 % 5); putc(' '); putn(1 << 10); putc(' '); putn(-64 >> 3); putc(10); + i = 0; while (1) { i++; if (i == 3) continue; if (i > 6) break; putn(i); } putc(10); + a[0] = 5; a[1] = a[0]++; a[2] = ++a[0]; putn(a[0]); putn(a[1]); putn(a[2]); putc(10); + let b = { let t = 3; t * t }; + putn(b); putc(' '); putn(3 > 2 && 2 > 1); putn(0 || 0); putn(!5); putn(~0); putn(5 != 5); putn(4 >= 4); putc(10); + let x = asm { 2 3 + }; + putn(x); putc(10); + asm { 'o' . 'k' . 10 . } + return 0; +} diff --git a/docs/history/11-types-enhancements/code/tests/enhance.zc b/docs/history/11-types-enhancements/code/tests/enhance.zc new file mode 100644 index 0000000..6acc742 --- /dev/null +++ b/docs/history/11-types-enhancements/code/tests/enhance.zc @@ -0,0 +1,29 @@ +// custom opcodes: recipes, lifted vm code, several outputs, and a fallback +enhance clampadd(a, b) micro "a = add a b; a = min a 100; a = max a 0"; +enhance cube(x) vm { 0 $ 0 $ * * }; +enhance divmod(a, b) -> 2 micro "r2 = sdiv a b" "\n r1 = srem a b\n r0 = r2"; +enhance div7(a) micro "a = sdiv a 7"; +enhance urem10(a) micro "a = urem a 10"; +enhance hi(a, b) micro "a = mulhu a b"; +enhance noisy(x) vm { 0 $ . } else soft_noisy; // i/o can't be lifted, so calls use the fallback +fn soft_noisy(x) { return x + 1; } +let qq, rr, seed = 7; +fn rnd() { seed = seed * 1103515245 + 12345; return seed ^ (seed >> 11); } +fn main() +{ + let i = 0, a, bad = 0; + putn(clampadd(70, 50)); putc(' '); putn(clampadd(-50, 2)); putc(' '); putn(cube(7)); putc(' '); + asm { 17 5 divmod rr ! qq ! } + putn(qq); putc(','); putn(rr); putc(' '); + putn(noisy(4)); putc(' '); + putn(hi(-1, -1)); putc(' '); + while (i < 5000) { + a = rnd(); + if (div7(a) != zdiv(a, 7)) bad++; + if (urem10(a) < 0 || urem10(a) > 9) bad++; + if (a / (i + 1) != zdiv(a, i + 1)) bad++; + i++; + } + putn(bad); putc(10); + return 0; +} diff --git a/docs/history/11-types-enhancements/code/tests/errors/back_to_m.zc b/docs/history/11-types-enhancements/code/tests/errors/back_to_m.zc new file mode 100644 index 0000000..b8c160f --- /dev/null +++ b/docs/history/11-types-enhancements/code/tests/errors/back_to_m.zc @@ -0,0 +1,2 @@ +// expect: x: can't be recast back to m +fn main() { let x = new(3); d*x; m*x; return 0; } diff --git a/docs/history/11-types-enhancements/code/tests/errors/borrow_escape.zc b/docs/history/11-types-enhancements/code/tests/errors/borrow_escape.zc new file mode 100644 index 0000000..ce24480 --- /dev/null +++ b/docs/history/11-types-enhancements/code/tests/errors/borrow_escape.zc @@ -0,0 +1,3 @@ +// expect: p: is owned and can't be returned +fn f(.m p) { return p; } +fn main() { let x = new(1); f(x); return 0; } diff --git a/docs/history/11-types-enhancements/code/tests/errors/branch_mismatch.zc b/docs/history/11-types-enhancements/code/tests/errors/branch_mismatch.zc new file mode 100644 index 0000000..a8498f8 --- /dev/null +++ b/docs/history/11-types-enhancements/code/tests/errors/branch_mismatch.zc @@ -0,0 +1,3 @@ +// expect: x: ends the branches of an if in different states +fn take(.d p) { return p[0]; } +fn main() { let x = new(1); d*x; if (x[0]) take(x); return 0; } diff --git a/docs/history/11-types-enhancements/code/tests/errors/child_dies.zc b/docs/history/11-types-enhancements/code/tests/errors/child_dies.zc new file mode 100644 index 0000000..67caca9 --- /dev/null +++ b/docs/history/11-types-enhancements/code/tests/errors/child_dies.zc @@ -0,0 +1,3 @@ +// expect: c: used after it was given away +fn take(.d p) { return p[0]; } +fn main() { let p = new(2), c = new(1); p[0] = c; take(d*p); return c[0]; } diff --git a/docs/history/11-types-enhancements/code/tests/errors/child_recast.zc b/docs/history/11-types-enhancements/code/tests/errors/child_recast.zc new file mode 100644 index 0000000..c9ccd62 --- /dev/null +++ b/docs/history/11-types-enhancements/code/tests/errors/child_recast.zc @@ -0,0 +1,2 @@ +// expect: c: belongs to a container; recast the container instead +fn main() { let p = new(2), c = new(1); p[0] = c; d*c; return 0; } diff --git a/docs/history/11-types-enhancements/code/tests/errors/copy_owned.zc b/docs/history/11-types-enhancements/code/tests/errors/copy_owned.zc new file mode 100644 index 0000000..862c7ef --- /dev/null +++ b/docs/history/11-types-enhancements/code/tests/errors/copy_owned.zc @@ -0,0 +1,2 @@ +// expect: x: is an owned object and can't be copied +fn main() { let x = new(1); let y = x; return y[0]; } diff --git a/docs/history/11-types-enhancements/code/tests/errors/d_into_block.zc b/docs/history/11-types-enhancements/code/tests/errors/d_into_block.zc new file mode 100644 index 0000000..2d7f526 --- /dev/null +++ b/docs/history/11-types-enhancements/code/tests/errors/d_into_block.zc @@ -0,0 +1,2 @@ +// expect: x: used after it was given away +fn main() { let x = new(1); d*x; { x[0] = 5; } return x[0]; } diff --git a/docs/history/11-types-enhancements/code/tests/errors/d_kept.zc b/docs/history/11-types-enhancements/code/tests/errors/d_kept.zc new file mode 100644 index 0000000..dcbfb86 --- /dev/null +++ b/docs/history/11-types-enhancements/code/tests/errors/d_kept.zc @@ -0,0 +1,2 @@ +// expect: x: d object was never given away +fn main() { let x = new(3); d*x; x[0] = 1; return 0; } diff --git a/docs/history/11-types-enhancements/code/tests/errors/discard_u.zc b/docs/history/11-types-enhancements/code/tests/errors/discard_u.zc new file mode 100644 index 0000000..dd76e94 --- /dev/null +++ b/docs/history/11-types-enhancements/code/tests/errors/discard_u.zc @@ -0,0 +1,3 @@ +// expect: can't be thrown away +fn mk() -> .u { let p = new(1); return u*p; } +fn main() { mk(); return 0; } diff --git a/docs/history/11-types-enhancements/code/tests/errors/give_m.zc b/docs/history/11-types-enhancements/code/tests/errors/give_m.zc new file mode 100644 index 0000000..cd67896 --- /dev/null +++ b/docs/history/11-types-enhancements/code/tests/errors/give_m.zc @@ -0,0 +1,3 @@ +// expect: x: is m; write d*x +fn take(.d p) { return p[0]; } +fn main() { let x = new(3); take(x); return 0; } diff --git a/docs/history/11-types-enhancements/code/tests/errors/global_store.zc b/docs/history/11-types-enhancements/code/tests/errors/global_store.zc new file mode 100644 index 0000000..dc0e546 --- /dev/null +++ b/docs/history/11-types-enhancements/code/tests/errors/global_store.zc @@ -0,0 +1,3 @@ +// expect: x: must be d to be stored in a global +let g; +fn main() { let x = new(1); g = x; return 0; } diff --git a/docs/history/11-types-enhancements/code/tests/errors/leak_overwrite.zc b/docs/history/11-types-enhancements/code/tests/errors/leak_overwrite.zc new file mode 100644 index 0000000..d19b5fd --- /dev/null +++ b/docs/history/11-types-enhancements/code/tests/errors/leak_overwrite.zc @@ -0,0 +1,2 @@ +// expect: x: still owns an object +fn main() { let x = new(1); x = new(2); return x[0]; } diff --git a/docs/history/11-types-enhancements/code/tests/errors/lend_d.zc b/docs/history/11-types-enhancements/code/tests/errors/lend_d.zc new file mode 100644 index 0000000..55ac7e0 --- /dev/null +++ b/docs/history/11-types-enhancements/code/tests/errors/lend_d.zc @@ -0,0 +1,4 @@ +// expect: x: is d; pass it to a d parameter +fn look(.m p) { return p[0]; } +fn take(.d p) { return p[0]; } +fn main() { let x = new(3); d*x; look(x); take(x); return 0; } diff --git a/docs/history/11-types-enhancements/code/tests/errors/loop_move.zc b/docs/history/11-types-enhancements/code/tests/errors/loop_move.zc new file mode 100644 index 0000000..6d2b8b2 --- /dev/null +++ b/docs/history/11-types-enhancements/code/tests/errors/loop_move.zc @@ -0,0 +1,3 @@ +// expect: x: changes state inside a loop +fn take(.d p) { return p[0]; } +fn main() { let x = new(1); let i = 0; d*x; while (i < 3) { take(x); i++; } return 0; } diff --git a/docs/history/11-types-enhancements/code/tests/errors/plain_return.zc b/docs/history/11-types-enhancements/code/tests/errors/plain_return.zc new file mode 100644 index 0000000..4540714 --- /dev/null +++ b/docs/history/11-types-enhancements/code/tests/errors/plain_return.zc @@ -0,0 +1,3 @@ +// expect: returns a new object; declare the function with -> .u +fn mk() { return new(1); } +fn main() { return mk(); } diff --git a/docs/history/11-types-enhancements/code/tests/errors/proto_needed.zc b/docs/history/11-types-enhancements/code/tests/errors/proto_needed.zc new file mode 100644 index 0000000..c4ab28a --- /dev/null +++ b/docs/history/11-types-enhancements/code/tests/errors/proto_needed.zc @@ -0,0 +1,3 @@ +// expect: f: was called before its tags were declared +fn main() { let x = new(1); f(1); return x[0]; } +fn f(.d p) { return 0; } diff --git a/docs/history/11-types-enhancements/code/tests/errors/return_m.zc b/docs/history/11-types-enhancements/code/tests/errors/return_m.zc new file mode 100644 index 0000000..1acc4be --- /dev/null +++ b/docs/history/11-types-enhancements/code/tests/errors/return_m.zc @@ -0,0 +1,3 @@ +// expect: x: has to be u to be returned +fn mk() -> .u { let x = new(1); return x; } +fn main() { let y = mk(); return y[0]; } diff --git a/docs/history/11-types-enhancements/code/tests/errors/tag_mismatch.zc b/docs/history/11-types-enhancements/code/tests/errors/tag_mismatch.zc new file mode 100644 index 0000000..079f249 --- /dev/null +++ b/docs/history/11-types-enhancements/code/tests/errors/tag_mismatch.zc @@ -0,0 +1,2 @@ +// expect: c: its tag doesn't match its container's +fn main() { let p = new(2), c = new(1); u*p; p[0] = c; return 0; } diff --git a/docs/history/11-types-enhancements/code/tests/errors/u_dropped.zc b/docs/history/11-types-enhancements/code/tests/errors/u_dropped.zc new file mode 100644 index 0000000..e1ca68a --- /dev/null +++ b/docs/history/11-types-enhancements/code/tests/errors/u_dropped.zc @@ -0,0 +1,2 @@ +// expect: x: u object must leave this block +fn main() { let x = new(3); u*x; return 0; } diff --git a/docs/history/11-types-enhancements/code/tests/errors/u_skip_level.zc b/docs/history/11-types-enhancements/code/tests/errors/u_skip_level.zc new file mode 100644 index 0000000..6caeeb3 --- /dev/null +++ b/docs/history/11-types-enhancements/code/tests/errors/u_skip_level.zc @@ -0,0 +1,2 @@ +// expect: t: is u; it can only be handed to a variable of the enclosing block +fn main() { let r; { { let t = new(1); u*t; r = t; } } return r[0]; } diff --git a/docs/history/11-types-enhancements/code/tests/errors/unchecked_touch.zc b/docs/history/11-types-enhancements/code/tests/errors/unchecked_touch.zc new file mode 100644 index 0000000..141639d --- /dev/null +++ b/docs/history/11-types-enhancements/code/tests/errors/unchecked_touch.zc @@ -0,0 +1,2 @@ +// expect: x: is tracked by the analyzer and can't be used in unchecked code +fn main() { let x = new(1); unchecked { x[0] = 1; } return 0; } diff --git a/docs/history/11-types-enhancements/code/tests/errors/unused.zc b/docs/history/11-types-enhancements/code/tests/errors/unused.zc new file mode 100644 index 0000000..64aff1f --- /dev/null +++ b/docs/history/11-types-enhancements/code/tests/errors/unused.zc @@ -0,0 +1,2 @@ +// expect: x: m object is never used +fn main() { let x = new(3); return 0; } diff --git a/docs/history/11-types-enhancements/code/tests/errors/use_after_give.zc b/docs/history/11-types-enhancements/code/tests/errors/use_after_give.zc new file mode 100644 index 0000000..8a10922 --- /dev/null +++ b/docs/history/11-types-enhancements/code/tests/errors/use_after_give.zc @@ -0,0 +1,3 @@ +// expect: x: used after it was given away +fn take(.d p) { return p[0]; } +fn main() { let x = new(3); take(d*x); return x[0]; } diff --git a/docs/history/11-types-enhancements/code/tests/gc.zc b/docs/history/11-types-enhancements/code/tests/gc.zc new file mode 100644 index 0000000..41f14a0 --- /dev/null +++ b/docs/history/11-types-enhancements/code/tests/gc.zc @@ -0,0 +1,20 @@ +let keep; +unchecked fn churn(n) +{ + let i = 0, p; + while (i < n) { p = new(10); p[0] = i; if (i % 100 == 0) keep = p; i++; } + return 0; +} +fn main() +{ + let base = live_objects(), f; + churn(1000); + putn(live_objects() - base); putc(' '); + f = gc(); + putn(f >= 990); putc(' '); // conservative: a stale word or two may keep one alive + putn(keep[0]); putc(' '); // the one a global still points at survives + churn(200000); // 3.2m words through a 393k word heap: needs the automatic collector + putn(gc_runs > 1); putc(' '); + putn(keep[0]); putc(10); + return 0; +} diff --git a/docs/history/11-types-enhancements/code/tests/meta.zc b/docs/history/11-types-enhancements/code/tests/meta.zc new file mode 100644 index 0000000..bc89454 --- /dev/null +++ b/docs/history/11-types-enhancements/code/tests/meta.zc @@ -0,0 +1,46 @@ +fn ipow(a, b) { let r = 1; while (b > 0) { r *= a; b--; } return r; } +// a parse word that adds an operator to the compiler's tables +immediate fn pow_op() { next(); add_binop("**", 11, "ipow c"); return 0; } +pow_op + +// unless (cond) stmt: a parse word that emits code itself +immediate fn unless() +{ + let a; + next(); + expect(intern("(")); rval(); plainuse(expr()); rval(); expect(intern(")")); + emitseq(useq[0], ulen[0]); // not + a = hole(); op(O_JZ); // skip the body when the condition was true + substmt(); + setlit(a, here); + return 0; +} +// repeat N { body }: a macro, built from token lists +immediate fn repeat() +{ + let n, body, i = 0; + next(); n = tv; next(); + body = grab(); + while (i < n) { feed(qdup(body)); i++; } + free(d*body); + return 0; +} +fn main() +{ + putn(2 ** 10); putc(' '); putn(3 + 2 ** 3 * 2); putc(10); + unless (1 > 2) puts("unless ok\n"); + unless (2 > 1) puts("wrong\n"); + repeat 3 { putc('r'); } + putc(10); + let q = quote { putn(40 + 2); putc(10); return 7; }; + putn(q[0]); putc(' '); + putn(eval(q)); putc(10); + let f = compile(q); + putn(f()); putc(10); + free(d*q); + let defs = quote { fn sq(x) { return x * x; } }; + load(defs); + free(d*defs); + putn(evals("return sq(12);")); putc(10); + return 0; +} diff --git a/docs/history/11-types-enhancements/code/tests/misc.zc b/docs/history/11-types-enhancements/code/tests/misc.zc new file mode 100644 index 0000000..25fc53b --- /dev/null +++ b/docs/history/11-types-enhancements/code/tests/misc.zc @@ -0,0 +1,30 @@ +// ct { ... } runs a block while compiling and leaves its result as a constant +immediate fn ct() +{ + let q, v; + next(); + q = grab(); + v = eval(q); + free(d*q); + lit(v); + return 0; +} +// teach the asm syntax that p means out, like the old repl demo +immediate fn teach() { next(); mnem[12] = 'p'; return 0; } +teach + +fn countdown() +{ + asm { 5 :top 0 $ '0' + p 1 - 0 $ done z top j :done _ 10 p } + return 0; +} +fn main() +{ + putn(ct { let i = 0, s = 0; while (i < 10) { s += i; i++; } return s; }); putc(10); + countdown(); + puts(lazy()); putc(10); + return 0; +} +// the analyzer is off from here on +unchecked; +fn lazy() { let p = new(3); p[0] = 'o'; p[1] = 'k'; return p; } diff --git a/docs/history/11-types-enhancements/code/tests/runerr.sh b/docs/history/11-types-enhancements/code/tests/runerr.sh new file mode 100755 index 0000000..23ea054 --- /dev/null +++ b/docs/history/11-types-enhancements/code/tests/runerr.sh @@ -0,0 +1,10 @@ +#!/bin/bash +# runs every .zc in a folder (tests/errors by default) and checks the compiler +# rejects it with the message on its first line: // expect: ... +vm=${1:-zy}; dir=${2:-$(dirname "$0")/errors}; pass=0; fail=0 +for f in "$dir"/*.zc; do + want=$(head -1 "$f" | sed 's#^// expect: ##') + got=$($vm < "$f" 2>&1 | head -1) + if [[ "$got" == *"$want"* ]]; then pass=$((pass+1)); else fail=$((fail+1)); echo "FAIL $(basename $f): got [$got]"; fi +done +echo "rejected: $pass passed, $fail failed" diff --git a/docs/history/11-types-enhancements/code/tests/typeerrors/arg.zc b/docs/history/11-types-enhancements/code/tests/typeerrors/arg.zc new file mode 100644 index 0000000..b762c19 --- /dev/null +++ b/docs/history/11-types-enhancements/code/tests/typeerrors/arg.zc @@ -0,0 +1,5 @@ +// expect: type error: argument: expected *point, got *int +typecheck on; +struct point { int x, y; }; +fn f(*point p) -> int { return p.x; } +fn main() { *int q = new(int); f(q); free(d*q); return 0; } diff --git a/docs/history/11-types-enhancements/code/tests/typeerrors/arith.zc b/docs/history/11-types-enhancements/code/tests/typeerrors/arith.zc new file mode 100644 index 0000000..76e3111 --- /dev/null +++ b/docs/history/11-types-enhancements/code/tests/typeerrors/arith.zc @@ -0,0 +1,3 @@ +// expect: type error: arithmetic needs numbers, got *int +typecheck on; +fn main() { *int p = 0; int x = p * 2; return x; } diff --git a/docs/history/11-types-enhancements/code/tests/typeerrors/assign.zc b/docs/history/11-types-enhancements/code/tests/typeerrors/assign.zc new file mode 100644 index 0000000..8b8a888 --- /dev/null +++ b/docs/history/11-types-enhancements/code/tests/typeerrors/assign.zc @@ -0,0 +1,3 @@ +// expect: type error: initializer: expected *char, got int +typecheck on; +fn main() { *char s = 5; return 0; } diff --git a/docs/history/11-types-enhancements/code/tests/typeerrors/call.zc b/docs/history/11-types-enhancements/code/tests/typeerrors/call.zc new file mode 100644 index 0000000..7b03dbf --- /dev/null +++ b/docs/history/11-types-enhancements/code/tests/typeerrors/call.zc @@ -0,0 +1,3 @@ +// expect: type error: only functions can be called, got int +typecheck on; +fn main() { int k = 3; return k(1); } diff --git a/docs/history/11-types-enhancements/code/tests/typeerrors/deref.zc b/docs/history/11-types-enhancements/code/tests/typeerrors/deref.zc new file mode 100644 index 0000000..ed030f5 --- /dev/null +++ b/docs/history/11-types-enhancements/code/tests/typeerrors/deref.zc @@ -0,0 +1,3 @@ +// expect: type error: only pointers can be dereferenced, got int +typecheck on; +fn main() { int x = 3; return *x; } diff --git a/docs/history/11-types-enhancements/code/tests/typeerrors/field.zc b/docs/history/11-types-enhancements/code/tests/typeerrors/field.zc new file mode 100644 index 0000000..ba03697 --- /dev/null +++ b/docs/history/11-types-enhancements/code/tests/typeerrors/field.zc @@ -0,0 +1,3 @@ +// expect: point has no field z +struct point { int x, y; }; +fn main() { point p; return p.z; } diff --git a/docs/history/11-types-enhancements/code/tests/typeerrors/generic.zc b/docs/history/11-types-enhancements/code/tests/typeerrors/generic.zc new file mode 100644 index 0000000..4a502ce --- /dev/null +++ b/docs/history/11-types-enhancements/code/tests/typeerrors/generic.zc @@ -0,0 +1,4 @@ +// expect: type error: argument: expected int, got *char +typecheck on; +fn same(T a, T b) -> T { return a; } +fn main() { return same(1, "x"); } diff --git a/docs/history/11-types-enhancements/code/tests/typeerrors/notstruct.zc b/docs/history/11-types-enhancements/code/tests/typeerrors/notstruct.zc new file mode 100644 index 0000000..0dff6bd --- /dev/null +++ b/docs/history/11-types-enhancements/code/tests/typeerrors/notstruct.zc @@ -0,0 +1,2 @@ +// expect: .x: that's a int, not a struct or a pointer to one +fn main() { int n = 1; return n.x; } diff --git a/docs/history/11-types-enhancements/code/tests/typeerrors/ret.zc b/docs/history/11-types-enhancements/code/tests/typeerrors/ret.zc new file mode 100644 index 0000000..cedd534 --- /dev/null +++ b/docs/history/11-types-enhancements/code/tests/typeerrors/ret.zc @@ -0,0 +1,4 @@ +// expect: type error: return value: expected bool, got *char +typecheck on; +fn f() -> bool { return "no"; } +fn main() { return f(); } diff --git a/docs/history/11-types-enhancements/code/tests/typeerrors/voidret.zc b/docs/history/11-types-enhancements/code/tests/typeerrors/voidret.zc new file mode 100644 index 0000000..a897290 --- /dev/null +++ b/docs/history/11-types-enhancements/code/tests/typeerrors/voidret.zc @@ -0,0 +1,4 @@ +// expect: type error: return value: expected void, got int +typecheck on; +fn f() -> void { return 3; } +fn main() { f(); return 0; } diff --git a/docs/history/11-types-enhancements/code/tests/typeoff.zc b/docs/history/11-types-enhancements/code/tests/typeoff.zc new file mode 100644 index 0000000..e44e450 --- /dev/null +++ b/docs/history/11-types-enhancements/code/tests/typeoff.zc @@ -0,0 +1,2 @@ +// the typechecker is off unless asked for: this mismatch compiles and runs +fn main() { *char s = 5; putn(s as int + 1); putc(10); return 0; } diff --git a/docs/history/11-types-enhancements/code/tests/typeok.zc b/docs/history/11-types-enhancements/code/tests/typeok.zc new file mode 100644 index 0000000..3cdee0c --- /dev/null +++ b/docs/history/11-types-enhancements/code/tests/typeok.zc @@ -0,0 +1,22 @@ +// passes the typechecker: words mix with everything, literal 0 is null, +// arrays decay to pointers, generic calls bind their parameters +typecheck on; +struct pair { A a; B b; }; +fn mk(A x, B y) -> *pair.u { *pair p = new(pair); p.a = x; p.b = y; return u*p; } +fn left(*pair p) -> A { return p.a; } +fn strlen2(*char s) -> int { int n = 0; while (s[n]) n++; return n; } +fn main() -> int +{ + *pair p = mk(3, "four"); + int a = left(p); + *char s = p.b; + [8]char buf; + *char q = buf; + *int nothing = 0; + let w = 5; + int mixed = w + 1; + q[0] = 'h'; q[1] = 0; + putn(a + strlen2(s) + strlen2(buf) + mixed); putc(10); + free(d*p); + return 0; +} diff --git a/docs/history/11-types-enhancements/code/tests/types.zc b/docs/history/11-types-enhancements/code/tests/types.zc new file mode 100644 index 0000000..ccbbd26 --- /dev/null +++ b/docs/history/11-types-enhancements/code/tests/types.zc @@ -0,0 +1,57 @@ +struct point { int x, y; }; +struct rect { point a, b; int tag; }; +struct list { int len; *T items; }; +struct node { int v; *node next; }; +type num = int; + +int counter = 5; +[4]int squares; +point origin; + +fn area(*rect r) -> int { return (r.b.x - r.a.x) * (r.b.y - r.a.y); } +fn mklist(int cap) -> *list.u +{ + *list l = new(list); + l.len = 0; + l.items = new(T, cap) as *T; + return u*l; +} +fn first(*list l) -> T { return l.items[0]; } +fn sum(*node n) -> int { int s = 0; while (n) { s += n.v; n = n.next; } return s; } + +fn main() +{ + rect r; + r.a.x = 1; r.a.y = 2; r.b.x = 4; r.b.y = 6; + putn(area(&r)); putc(' '); + putn(sizeof(rect)); putc(' '); putn(sizeof(point)); putc(' '); putn(sizeof([3]rect)); putc(' '); + let p = new(point); // let infers *point + p.x = 7; p->y = 8; + putn(p.x * p.y); putc(' '); + free(d*p); + *list l = mklist(4); + l.items[0] = 42; l.len = 1; + putn(first(l)); putc(' '); + free(d*l); + *node a = new(node), b = new(node); + a.v = 3; b.v = 4; a.next = b; + putn(sum(a)); putc(' '); + free(d*a); // b went with it: a.next = b made a its owner + [3]point pts; + pts[2].y = 9; + putn(pts[2].y + counter); putc(' '); + num k = 3; + putn(k); putc(10); + + // reflection + tprint(typeof(r)); putc(' '); tprint(typeof(&r)); putc(' '); tprint(typeof(l)); putc(' '); + tprint(sigof(mklist)); putc(' '); tprint(sigof(first)); putc(' '); tprint(sigofs("free")); putc(10); + let t = typeof(r), i = 0; + while (i < tnfields(t)) { puts(tfname(t, i)); putc(':'); tprint(tftype(t, i)); putc('@'); putn(tfoff(t, i)); putc(' '); i++; } + putc(10); + *point q = new(point); + tprint(typeof_obj(q)); putc(' '); putn(typeof_obj(q) == typeof(point)); putc(' '); putn(tkind(typeof_obj(q)) == TY_STRUCT); + free(d*q); + putc(10); + return 0; +} diff --git a/docs/history/11-types-enhancements/code/tests/umd.zc b/docs/history/11-types-enhancements/code/tests/umd.zc new file mode 100644 index 0000000..ee463bf --- /dev/null +++ b/docs/history/11-types-enhancements/code/tests/umd.zc @@ -0,0 +1,52 @@ +// lists as [len, items...], items owned by the list +fn mklist(n) -> .u +{ + let l = new(n + 1); + l[0] = 0; + return u*l; +} +fn push(.m l, .d item) { l[l[0] + 1] = d*item; l[0] = l[0] + 1; return 0; } +fn sum(.m l) { let s = 0, i = 1; while (i <= l[0]) { s += l[i][0]; i++; } return s; } +fn box(v) -> .u { let b = new(1); b[0] = v; return u*b; } +fn consume(.d b) { return b[0]; } + +fn main() +{ + let base = live_objects(); + { + let l = mklist(8); // u from mklist arrives m*here + let i = 0; + while (i < 5) { + let b = box(i * 10); // m in the loop body + d*b; // give it down + push(l, b); + i++; + } + putn(sum(l)); putc(' '); + putn(live_objects() - base); putc(' '); // list + 5 boxes + } + putn(live_objects() - base); putc(' '); // freed at the block's end, children too + + // u handed up one block by assignment, and as a block value + let outer; + { + let t = box(7); + u*t; + outer = t; + } + let v = { let w = box(8); u*w }; + putn(outer[0] + v[0]); putc(' '); + + // d given away to a callee + let g = box(99); + putn(consume(d*g)); putc(' '); + + // containers: child takes its parent's tag + let p = new(2), c = new(1); + c[0] = 5; + p[0] = c; + p[1] = new(3); + putn(p[0][0]); putc(' '); + putn(live_objects() - base); putc(10); + return 0; +} diff --git a/docs/history/11-types-enhancements/code/tools/build.zc b/docs/history/11-types-enhancements/code/tools/build.zc new file mode 100644 index 0000000..a38e97f --- /dev/null +++ b/docs/history/11-types-enhancements/code/tools/build.zc @@ -0,0 +1,13 @@ +// compiles the rest of stdin (after the backtick) as a genome image at GORG +// and writes it to stdout as little-endian 32-bit words. +const OUT = 0x10000; +fn main() +{ + let n = build_image(GORG, OUT), i = 0, w; + while (i < n) { + w = OUT[i]; + putc(w); putc(w >> 8); putc(w >> 16); putc(w >> 24); + i++; + } + return 0; +} diff --git a/docs/history/11-types-enhancements/code/zc0.c b/docs/history/11-types-enhancements/code/zc0.c new file mode 100644 index 0000000..4df587e --- /dev/null +++ b/docs/history/11-types-enhancements/code/zc0.c @@ -0,0 +1,460 @@ +/* zc0: stage 0 of the bootstrap. a small c compiler for the subset of zc that + compiler.zc is written in. it reads zc source on stdin and writes a genome + header (vm words placed at GORG) on stdout, in the same image layout the + resident compiler's image mode produces. no lifetime analysis, no quote, no + parse words, no block expressions: those only exist in compiler.zc. tags + (m, d, -> u) and casts (as u, as d) are parsed and recorded in signatures + but not checked. */ +#include +#include +#include +#include + +#define GORG 0xC0000 +#define ZFP 0xA0000 /* frame pointer word */ +#define FTOP 0xF0000 /* frame stack grows down from here */ +#define MAXW 0x40000 + +enum { T_EOF, T_NUM, T_ID, T_STR, T_OP }; +enum { K_FN = 1, K_GVAR, K_GARR, K_CONST, K_KW, K_ENH }; +enum { O_Q, O_LD, O_ST, O_PICK, O_DROP, O_SWAP, O_CALL, O_JMP, O_JZ, O_RET, + O_IN, O_EMPTY, O_OUT, O_ADD, O_SUB, O_MUL, O_AND, O_OR, O_XOR, O_SHR, O_LT }; +static const char *mn = "q@!$_~cjzr,?.+-*&|^><%"; + +static char *src; static int pos, line = 1; +static int tk, tv, glue; static char ts[4096]; /* current token */ + +static int img[MAXW], here = GORG; +static void emit(int w) { if (here - GORG >= MAXW) { fprintf(stderr, "image too big\n"); exit(1); } img[here++ - GORG] = w; } +static void lit(int v) { emit((int)((unsigned)v * 2 + 1)); } +static void op(int k) { emit(k * 2); } +static void setlit(int at, int v) { img[at - GORG] = (int)((unsigned)v * 2 + 1); } + +static void die(const char *m) { fprintf(stderr, "zc0: line %d: %s (at '%s')\n", line, m, ts); exit(1); } + +/* ---- lexer ---- */ +static const char *ops3[] = { "<<=", ">>=", 0 }; +static const char *ops2[] = { "==", "!=", "<=", ">=", "<<", ">>", "&&", "||", "++", "--", "+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "->", 0 }; +static int esc(void) +{ + int c = src[pos++]; + if (c != '\\') return c; + c = src[pos++]; + switch (c) { case 'n': return 10; case 't': return 9; case 'r': return 13; case '0': return 0; case 'e': return 27; } + return c; +} +static void next(void) +{ + int ws = 0, i; + for (;;) { + while (src[pos] && isspace((unsigned char)src[pos])) { if (src[pos] == '\n') line++; pos++; ws = 1; } + if (src[pos] == '/' && src[pos + 1] == '/') { while (src[pos] && src[pos] != '\n') pos++; ws = 1; continue; } + if (src[pos] == '/' && src[pos + 1] == '*') { pos += 2; while (src[pos] && !(src[pos] == '*' && src[pos + 1] == '/')) { if (src[pos] == '\n') line++; pos++; } if (src[pos]) pos += 2; ws = 1; continue; } + break; + } + glue = !ws; + if (!src[pos] || src[pos] == '`') { tk = T_EOF; strcpy(ts, ""); return; } + if (isdigit((unsigned char)src[pos])) { + char *e; tv = (int)strtoul(src + pos, &e, 0); memcpy(ts, src + pos, e - src - pos); ts[e - src - pos] = 0; pos = e - src; tk = T_NUM; return; + } + if (isalpha((unsigned char)src[pos]) || src[pos] == '_') { + i = 0; while (isalnum((unsigned char)src[pos]) || src[pos] == '_') ts[i++] = src[pos++]; + ts[i] = 0; tk = T_ID; return; + } + if (src[pos] == '\'') { pos++; tv = esc(); if (src[pos++] != '\'') die("bad char literal"); tk = T_NUM; strcpy(ts, "'c'"); return; } + if (src[pos] == '"') { pos++; i = 0; while (src[pos] && src[pos] != '"') ts[i++] = (char)esc(); ts[i] = 0; pos++; tk = T_STR; return; } + tk = T_OP; + for (i = 0; ops3[i]; i++) if (!strncmp(src + pos, ops3[i], 3)) { strcpy(ts, ops3[i]); pos += 3; return; } + for (i = 0; ops2[i]; i++) if (!strncmp(src + pos, ops2[i], 2)) { strcpy(ts, ops2[i]); pos += 2; return; } + ts[0] = src[pos++]; ts[1] = 0; +} +static int is(const char *s) { return (tk == T_OP || tk == T_ID) && !strcmp(ts, s); } +static int accept(const char *s) { if (is(s)) { next(); return 1; } return 0; } +static void expect(const char *s) { if (!accept(s)) { char b[64]; sprintf(b, "expected '%s'", s); die(b); } } +static char *ident(void) { static char b[8][256]; static int k; char *r = b[k++ & 7]; if (tk != T_ID) die("expected a name"); strcpy(r, ts); next(); return r; } + +/* ---- symbols ---- */ +typedef struct { char name[64]; int kind, val, sig, defd; } Sym; +static Sym sy[4096]; static int nsy; +typedef struct { int at, sym; } Fix; +static Fix fx[65536]; static int nfx; +static Sym *find(const char *n) { int i; for (i = nsy; i--;) if (!strcmp(sy[i].name, n)) return &sy[i]; return 0; } +static Sym *add(const char *n, int kind, int val) +{ + Sym *s = find(n); + if (s && s->kind == K_FN && kind == K_FN && !s->defd) { s->val = val; return s; } /* prototype or forward use */ + s = &sy[nsy++]; strncpy(s->name, n, 63); s->kind = kind; s->val = val; s->defd = 1; s->sig = 0; return s; +} +static void fnref(const char *n) /* push a function address, fixing it up later if needed */ +{ + Sym *s = find(n); + if (!s) { s = add(n, K_FN, 0); s->defd = 0; } + if (s->kind != K_FN) die("not a function"); + if (s->defd) { lit(s->val); return; } + fx[nfx].at = here; fx[nfx++].sym = (int)(s - sy); lit(0); +} + +/* ---- locals ---- */ +typedef struct { char name[64]; int slot, arr; } Loc; +static Loc lo[1024]; static int nlo, nslot, infn; +static Loc *findl(const char *n) { int i; for (i = nlo; i--;) if (!strcmp(lo[i].name, n)) return &lo[i]; return 0; } +static void laddr(int k) { lit(ZFP); op(O_LD); if (k) { lit(k); op(O_ADD); } } + +/* ---- operator tables ---- */ +static void seq_isz(void) { lit(0); op(O_PICK); lit(0); op(O_SWAP); op(O_SUB); op(O_OR); lit(31); op(O_SHR); lit(1); op(O_ADD); } +static void seq_nz(void) { lit(0); op(O_PICK); lit(0); op(O_SWAP); op(O_SUB); op(O_OR); lit(31); op(O_SHR); lit(0); op(O_SWAP); op(O_SUB); } +static int binprec(void) +{ + static const char *n[] = { "*", "/", "%", "+", "-", "<<", ">>", "<", ">", "<=", ">=", "==", "!=", "&", "^", "|", "&&", "||", 0 }; + static int p[] = { 10, 10, 10, 9, 9, 8, 8, 7, 7, 7, 7, 6, 6, 5, 4, 3, 2, 1 }; + int i; + if (tk != T_OP) return 0; + for (i = 0; n[i]; i++) if (!strcmp(ts, n[i])) return p[i]; + return 0; +} +static void binop(const char *o) +{ + if (!strcmp(o, "*")) op(O_MUL); + else if (!strcmp(o, "/")) { fnref("zdiv"); op(O_CALL); } + else if (!strcmp(o, "%")) { fnref("zmod"); op(O_CALL); } + else if (!strcmp(o, "+")) op(O_ADD); + else if (!strcmp(o, "-")) op(O_SUB); + else if (!strcmp(o, "<<")) { fnref("zshl"); op(O_CALL); } + else if (!strcmp(o, ">>")) op(O_SHR); + else if (!strcmp(o, "<")) op(O_LT); + else if (!strcmp(o, ">")) { op(O_SWAP); op(O_LT); } + else if (!strcmp(o, "<=")) { op(O_SWAP); op(O_LT); lit(1); op(O_XOR); } + else if (!strcmp(o, ">=")) { op(O_LT); lit(1); op(O_XOR); } + else if (!strcmp(o, "==")) { op(O_SUB); seq_isz(); } + else if (!strcmp(o, "!=")) { op(O_SUB); seq_nz(); } + else if (!strcmp(o, "&")) op(O_AND); + else if (!strcmp(o, "^")) op(O_XOR); + else if (!strcmp(o, "|")) op(O_OR); + else die("bad operator"); +} + +/* ---- constant expressions (globals, array sizes, const) ---- */ +static int cexpr(void); +static int cprim(void) +{ + int v; Sym *s; + if (tk == T_NUM) { v = tv; next(); return v; } + if (accept("-")) return -cprim(); + if (accept("~")) return ~cprim(); + if (accept("(")) { v = cexpr(); expect(")"); return v; } + if (tk == T_ID && (s = find(ts)) && (s->kind == K_CONST || (s->kind != K_GVAR && s->defd))) { next(); return s->val; } + die("constant expected"); return 0; +} +static int cexpr(void) +{ + int v = cprim(); + for (;;) { + if (accept("+")) v += cprim(); else if (accept("-")) v -= cprim(); + else if (accept("*")) v *= cprim(); else if (accept("|")) v |= cprim(); + else if (accept("&")) v &= cprim(); else if (accept("<<")) v <<= cprim(); + else if (accept(">>")) v >>= cprim(); else return v; + } +} + +/* ---- expressions ---- */ +static int lv; /* the stack top is an address not yet loaded */ +static void rval(void) { if (lv) { op(O_LD); lv = 0; } } +static void expr(void); +static void strlit(void) /* inline string: jump over it, push its address */ +{ + int j = here, a, i, n = (int)strlen(ts); + lit(0); op(O_JMP); a = here; + for (i = 0; i < n; i++) emit((unsigned char)ts[i]); + emit(0); setlit(j, here); lit(a); next(); +} +static int args(void) { int n = 0; expect("("); if (!accept(")")) { do { expr(); rval(); n++; } while (accept(",")); expect(")"); } return n; } +static void asmblock(void); +static void primary(void) +{ + char *n; Loc *l; Sym *s; + lv = 0; + if (tk == T_NUM) { lit(tv); next(); return; } + if (tk == T_STR) { strlit(); return; } + if (accept("(")) { expr(); expect(")"); return; } + if (is("asm")) { next(); asmblock(); return; } + n = ident(); + if ((l = findl(n))) { laddr(l->slot); lv = !l->arr; return; } + s = find(n); + if (s && s->kind == K_CONST) { lit(s->val); return; } + if (s && s->kind == K_GVAR) { lit(s->val); lv = 1; return; } + if (s && s->kind == K_GARR) { lit(s->val); return; } + if (is("(")) { args(); fnref(n); op(O_CALL); return; } + fnref(n); +} +static void incdec(int post, int d) +{ + if (!lv) die("++/-- needs an lvalue"); + lv = 0; + if (post) { lit(0); op(O_PICK); op(O_LD); lit(0); op(O_PICK); lit(d); op(O_ADD); lit(2); op(O_PICK); op(O_ST); op(O_SWAP); op(O_DROP); } + else { lit(0); op(O_PICK); op(O_LD); lit(d); op(O_ADD); lit(0); op(O_PICK); lit(2); op(O_PICK); op(O_ST); op(O_SWAP); op(O_DROP); } +} +static void postfix(void) +{ + int n; + primary(); + for (;;) { + if (accept("[")) { rval(); expr(); rval(); expect("]"); op(O_ADD); lv = 1; } + else if (is("(")) { rval(); n = args(); lit(n); op(O_PICK); op(O_CALL); op(O_SWAP); op(O_DROP); } + else if (accept("++")) incdec(1, 1); + else if (accept("--")) incdec(1, -1); + else if (accept("as")) ident(); + else return; + } +} +static void unary(void) +{ + if (accept("-")) { unary(); rval(); lit(0); op(O_SWAP); op(O_SUB); } + else if (accept("!")) { unary(); rval(); seq_isz(); } + else if (accept("~")) { unary(); rval(); lit(-1); op(O_XOR); } + else if (accept("*")) { unary(); rval(); lv = 1; } + else if (accept("&")) { unary(); if (!lv) die("& needs an lvalue"); lv = 0; } + else if (accept("++")) { unary(); incdec(0, 1); } + else if (accept("--")) { unary(); incdec(0, -1); } + else if (tk == T_ID && (!strcmp(ts, "u") || !strcmp(ts, "m") || !strcmp(ts, "d"))) { next(); expect("*"); unary(); } /* u*x: a recast, no code */ + else postfix(); +} +static void bin(int minp) +{ + int p, a, b; char o[8]; + unary(); + while ((p = binprec()) && p >= minp) { + strcpy(o, ts); next(); rval(); + if (!strcmp(o, "&&")) { + a = here; lit(0); op(O_JZ); bin(p + 1); rval(); seq_nz(); + b = here; lit(0); op(O_JMP); setlit(a, here); lit(0); setlit(b, here); + } else if (!strcmp(o, "||")) { + a = here; lit(0); op(O_JZ); lit(1); b = here; lit(0); op(O_JMP); + setlit(a, here); bin(p + 1); rval(); seq_nz(); setlit(b, here); + } else { bin(p + 1); rval(); binop(o); } + lv = 0; + } +} +static void expr(void) +{ + char o[8]; + bin(1); + if (is("=")) { + if (!lv) die("assignment needs an lvalue"); + next(); expr(); rval(); + op(O_SWAP); lit(1); op(O_PICK); op(O_SWAP); op(O_ST); lv = 0; + } else if (tk == T_OP && strlen(ts) >= 2 && ts[strlen(ts) - 1] == '=' && strcmp(ts, "==") && strcmp(ts, "!=") && strcmp(ts, "<=") && strcmp(ts, ">=")) { + if (!lv) die("assignment needs an lvalue"); + strcpy(o, ts); o[strlen(o) - 1] = 0; next(); + lit(0); op(O_PICK); op(O_LD); lv = 0; expr(); rval(); binop(o); + op(O_SWAP); lit(1); op(O_PICK); op(O_SWAP); op(O_ST); lv = 0; + } +} + +/* ---- asm blocks: raw vm words ---- */ +static void asmblock(void) +{ + char ln[128][64], rn[256][64]; int la[128], nl = 0, ra[256], nr = 0, i, k; const char *c; + Loc *l; Sym *s; + expect("{"); + while (!accept("}")) { + if (tk == T_EOF) die("unterminated asm"); + if (tk == T_NUM) { lit(tv); next(); continue; } + if (tk == T_STR) { strlit(); continue; } + if (accept("#")) { if (tk != T_NUM) die("# needs a number"); emit(tv); next(); continue; } + if (is("-")) { next(); if (tk == T_NUM && glue) { lit(-tv); next(); } else op(O_SUB); continue; } + if (accept(":")) { strcpy(ln[nl], ident()); la[nl++] = here; continue; } + if (strlen(ts) == 1 && (c = strchr(mn, ts[0]))) { op((int)(c - mn)); next(); continue; } + if (tk != T_ID) die("bad asm token"); + if ((l = findl(ts))) { laddr(l->slot); next(); continue; } + if ((s = find(ts)) && s->kind != K_FN) { lit(s->val); next(); continue; } + if (s) { fnref(ts); next(); continue; } + strcpy(rn[nr], ts); ra[nr++] = here; lit(0); next(); /* a label of this block */ + } + for (i = 0; i < nr; i++) { + for (k = -1; ++k < nl && strcmp(ln[k], rn[i]);) ; + if (k == nl) die("unknown asm label"); + setlit(ra[i], la[k]); + } +} + +/* ---- statements ---- */ +static int brk[256][64], nbrk[256], cont[256], nloop; +static int epi[1024], nepi; +static void stmt(void); +static void block(void) +{ + int save = nlo; + expect("{"); + while (!accept("}")) { if (tk == T_EOF) die("missing }"); stmt(); } + nlo = save; +} +static void jmp_to(int t) { lit(t); op(O_JMP); } +static void loopbody(int contaddr) +{ + nbrk[nloop] = 0; cont[nloop++] = contaddr; + stmt(); + nloop--; +} +static void patchbreaks(void) { int i; for (i = 0; i < nbrk[nloop]; i++) setlit(brk[nloop][i], here); } +static void stmt(void) +{ + int a, b, c, d; + if (accept(";")) return; + if (is("{")) { block(); return; } + if (accept("let") || accept("var")) { + do { + Loc *l = &lo[nlo++]; strcpy(l->name, ident()); l->arr = 0; + if (accept("[")) { int n = cexpr(); expect("]"); nslot += n; l->slot = nslot - n; l->arr = 1; } + else l->slot = nslot++; + if (accept("=")) { expr(); rval(); laddr(l->slot); op(O_ST); } + } while (accept(",")); + expect(";"); return; + } + if (accept("unchecked")) { stmt(); return; } + if (accept("if")) { + expect("("); expr(); rval(); expect(")"); + a = here; lit(0); op(O_JZ); stmt(); + if (accept("else")) { b = here; lit(0); op(O_JMP); setlit(a, here); stmt(); setlit(b, here); } + else setlit(a, here); + return; + } + if (accept("while")) { + c = here; expect("("); expr(); rval(); expect(")"); + a = here; lit(0); op(O_JZ); + loopbody(c); jmp_to(c); setlit(a, here); patchbreaks(); return; + } + if (accept("for")) { + int save = nlo; + expect("("); + if (!is(";")) { if (is("let") || is("var")) { stmt(); goto cond; } expr(); rval(); op(O_DROP); } + expect(";"); + cond: c = here; + if (!is(";")) { expr(); rval(); } else lit(1); + expect(";"); + a = here; lit(0); op(O_JZ); b = here; lit(0); op(O_JMP); + d = here; + if (!is(")")) { expr(); rval(); op(O_DROP); } + expect(")"); jmp_to(c); + setlit(b, here); + loopbody(d); jmp_to(d); setlit(a, here); patchbreaks(); + nlo = save; return; + } + if (accept("break")) { if (!nloop) die("break outside a loop"); brk[nloop - 1][nbrk[nloop - 1]++] = here; lit(0); op(O_JMP); expect(";"); return; } + if (accept("continue")) { if (!nloop) die("continue outside a loop"); jmp_to(cont[nloop - 1]); expect(";"); return; } + if (accept("return")) { + if (!is(";")) { expr(); rval(); } else lit(0); + expect(";"); epi[nepi++] = here; lit(0); op(O_JMP); return; + } + if (is("asm")) { next(); asmblock(); return; } + expr(); rval(); op(O_DROP); expect(";"); +} + +/* ---- top level ---- */ +static void fndef(void) +{ + char *n = ident(); Sym *s; int np = 0, tags = 0, t, ret = 0, frame, i; + char pn[16][64]; + s = find(n); + if (!s || s->kind != K_FN) { s = add(n, K_FN, 0); s->defd = 0; } + expect("("); + if (!accept(")")) { + do { + t = 0; + if (accept(".")) { /* .m name, .d name */ + if (is("m")) t = 1; else if (is("d")) t = 2; else die("a parameter tag is .m or .d"); + next(); + } + if (np < 12) tags += t << (2 * np); + strcpy(pn[np++], ident()); + } while (accept(",")); + expect(")"); + } + if (accept("->")) { expect("."); if (strcmp(ident(), "u")) die("the return tag is -> .u"); ret = 1; } + s->sig = ret + 2 * np + 64 * tags; + if (accept(";")) return; /* prototype */ + if (s->defd) die("function defined twice"); + s->val = here; s->defd = 1; + nlo = 0; nslot = 0; nepi = 0; infn = 1; + for (i = 0; i < np; i++) { strcpy(lo[nlo].name, pn[i]); lo[nlo].arr = 0; lo[nlo++].slot = nslot++; } + /* prologue: fp -= frame, then pop the arguments into their slots */ + lit(ZFP); op(O_LD); frame = here; lit(0); op(O_SUB); lit(ZFP); op(O_ST); + for (i = np; i--;) { laddr(i); op(O_ST); } + block(); + lit(0); + for (i = 0; i < nepi; i++) setlit(epi[i], here); + lit(ZFP); op(O_LD); lit(nslot); op(O_ADD); lit(ZFP); op(O_ST); op(O_RET); + setlit(frame, nslot); + infn = 0; +} +/* enhance name(params) [-> n] micro "recipe"; or ... vm { asm }; + emits the install descriptor; the booted genome installs it */ +static int nenh; +static void enhdef(void) +{ + char *n = ident(), pn[8][64]; int np = 0, nout = 1, d, na, pay, len = 0, i, j; Sym *s; + expect("("); + if (!accept(")")) { do { if (np == 8) die("at most 8 inputs"); strcpy(pn[np++], ident()); } while (accept(",")); expect(")"); } + if (accept("->")) { nout = tv; next(); } + d = here; for (i = 0; i < 7; i++) emit(0); + na = here; for (i = 0; i < np; i++) emit(0); + for (i = 0; i < np; i++) { img[na + i - GORG] = here; for (j = 0; pn[i][j]; j++) emit((unsigned char)pn[i][j]); emit(0); } + if (accept("micro")) { pay = here; while (tk == T_STR) { for (j = 0; ts[j]; j++) emit((unsigned char)ts[j]); next(); } emit(0); } + else if (accept("vm")) { pay = here; asmblock(); op(O_RET); len = here - pay; img[d + 1 - GORG] = 1; } + else die("expected micro or vm"); + img[d - GORG] = nenh; img[d + 2 - GORG] = np; img[d + 3 - GORG] = nout; + img[d + 4 - GORG] = pay; img[d + 5 - GORG] = len; img[d + 6 - GORG] = na; + s = add(n, K_ENH, nenh++); s->sig = np + nout * 16 + d * 256; + expect(";"); +} +static void top(void) +{ + while (tk != T_EOF) { + if (accept("unchecked")) continue; + if (accept("fn")) { fndef(); continue; } + if (accept("const")) { do { char *n = ident(); expect("="); add(n, K_CONST, cexpr()); } while (accept(",")); expect(";"); continue; } + if (accept("enhance")) { enhdef(); continue; } + if (accept("let") || accept("var")) { + do { + char *n = ident(); + if (accept("[")) { int k = cexpr(), a = here; expect("]"); while (k--) emit(0); add(n, K_GARR, a); } + else { + int a = here; add(n, K_GVAR, a); emit(0); + if (accept("=")) { + if (tk == T_STR) { int i, l = (int)strlen(ts); img[a - GORG] = here; for (i = 0; i < l; i++) emit((unsigned char)ts[i]); emit(0); next(); } + else img[a - GORG] = cexpr(); + } + } + } while (accept(",")); + expect(";"); continue; + } + die("expected fn, var or const"); + } +} + +int main(void) +{ + static char buf[1 << 22]; int n = (int)fread(buf, 1, sizeof buf - 1, stdin), i, j; Sym *st; + buf[n] = 0; src = buf; next(); + /* image header: set the frame pointer, jump to start, then the symbol blob pointer and image end */ + lit(FTOP); lit(ZFP); op(O_ST); fnref("start"); op(O_JMP); emit(0); emit(0); + top(); + for (i = 0; i < nfx; i++) { + Sym *s = &sy[fx[i].sym]; + if (!s->defd) { fprintf(stderr, "zc0: undefined function %s\n", s->name); return 1; } + setlit(fx[i].at, s->val); + } + if (!(st = find("start")) || !st->defd) { fprintf(stderr, "zc0: no start\n"); return 1; } + /* symbol blob: kind, value, sig, name chars, 0 ... then a 0 kind */ + img[5] = here; + for (i = 0; i < nsy; i++) { + emit(sy[i].kind); emit(sy[i].val); emit(sy[i].sig); + for (j = 0; sy[i].name[j]; j++) emit((unsigned char)sy[i].name[j]); + emit(0); + } + emit(0); + img[6] = here; + printf("#define GORG %d\n#define GLEN %d\nstatic const int genome[] = {", GORG, here - GORG); + for (i = 0; i < here - GORG; i++) printf("%s%d", !i ? "\n" : i % 16 ? "," : ",\n", img[i]); + printf("\n};\n"); + return 0; +} diff --git a/docs/history/11-types-enhancements/notes.md b/docs/history/11-types-enhancements/notes.md new file mode 100644 index 0000000..ae79685 --- /dev/null +++ b/docs/history/11-types-enhancements/notes.md @@ -0,0 +1,73 @@ +# types, virtual enhancements, the llvm backend + +code/ next to this file is the tree at the end of this stage. + +## message 11 + +> progress report? +> add types please :) +> mostly as a compiler primitive though, plus type-inspection via reflection +> and a small optional typechecker (default off) +> oh and keep the stuff you write as a history scrapbook +> if it helps, add a path to the hardware which is called "virtual enhancements", and allows up to 512 custom operations to be compiled down via jit into hardware-level speeds and interoped directly into a fast path in the vm to become essentially custom wiring. useful for division and other stuff as a dynamic runtime path + +my answers to the clarifying questions, exactly: + +> types: words + structs + generics +> +> reflection: runtime type of heap objects, function signatures, typeof at compile time +> +> how an enhancement is defined: either actual C or llvm ir code or vm code or micro-ops passed through an optimizer. since for example, micro-ops and vm code are not sufficient for optimizing things like division +> +> scrapbook: notes + code snapshots + +**summary:** the progress report said the previous round was finished and shipped. i then laid out a plan where the core would compile c or llvm ir enhancement sources at runtime with the host toolchain and load the result. part of that reply was stopped by a safety check before it finished, so i stopped and checked in instead of building it. + +## message 12 + +> also make the umd syntax like `type.u name`, and casting is multiplication `u*name` +> ideally i'd prefer memory to be unlimited words growing until system oom, but 1m is fine +> core.c plain interp is also nice +> zc0.c own file is fine +> i mentioned llvm for portability to non-arm targets +> yes, d object semantic that was intended +> u object hands up one block and becomes an m, that's fine. the code can make it a u or a d or whatever +> explain your gaps more +> +> wait why safety check? what's wrong with that + +my answers to the clarifying questions, exactly: + +> declarations: c-style, var -> let +> +> tags on untyped values and the names u, m, d: names reserved, bare .u allowed, untype is m by default +> +> llvm: llvm jit backend (Recommended) + +**summary:** on the safety check: nothing was wrong with the request. my best guess was that it tripped on the plan for a program inside the vm handing source text to the system compiler and loading the result into the running process, since that pattern also shows up in malware. with llvm meant for portability, that path wasn't needed. the gaps got the longer explanations that are now under open ends above. then the round itself: + +**syntax.** declarations are c-style (`int x`, `*node.m p`), `let` replaces `var` for untyped and inferred declarations, tags are `.u .m .d` after the type or on their own, recasts are `u*x` / `d*x` / `m*x`, `u m d` are reserved, and an untagged parameter counts as an m lend. + +**types.** a compiler primitive: hash-consed records with structs, generic structs and functions, fixed arrays, function types, `typeof`/`sizeof`, `as` casts and a reflection api, with object headers carrying their type. the typechecker is off by default and turned on with `typecheck on;`. + +**virtual enhancements.** opcode 21 installs, 22 to 533 are the slots. micro-op recipes and lifted vm code go through an optimizer in the core (folding, cse, dce, multiply-high division by constants) and get inlined by both jits. the language's own `/ % <<` now run on them. + +**backends.** a tiered llvm backend (`-DZYLLVM`) for non-arm hosts. + +**bootstrap and history.** it still reaches its fixpoint on all three backends. `history/` holds notes for every stage and code from stages 7, 10 and 12. + +## working notes + +the first plan for enhancements had four source kinds: micro-op recipes, vm code, c and llvm ir. the last two would have been compiled at runtime by the host toolchain and loaded into the process. part of the reply describing that got stopped by a safety check, so i checked in instead of building it. the user's answer clarified that llvm was about portability, so llvm became a backend (vm blocks -> ir -> orc) and enhancements stayed with recipes and lifted vm code, both of which go through an optimizer in the core. + +order of work: the enhancement unit in core.c first, tested from zc with raw opcode words before the compiler knew about them (80000 random divisions against the software routine, including /0, INT_MIN/-1 and constant divisors through the magic-number path; the trace for x/7 came out as the textbook mulhs, add, sar, shr, add). then the syntax change, enhance declarations and native / % <<, then types, then the llvm backend, then tests, examples and docs. + +bugs along the way, kept here because they were instructive: +- the rebuild script compiled stage 0 against a stale copy of core.c that didn't know opcode 21, so the genome halted silently at boot. +- uses of % in rt.zc came before the `enhance imod` line, so they became forward function references that never resolved. the enhance lines moved to the top of the runtime. +- u, m and d becoming reserved meant renaming variables called d in the compiler and the runtime. +- stage 0 needs globals and constants declared before use, so the type tables had to move to the top of the compiler. +- i used `?:` in the compiler, which zc doesn't have. +- my own typed example lent heap points to a vector and let them be freed at the end of each loop pass, leaving dangling pointers. that's the untracked-alias gap from the open ends, and the example now hands them over with .d. + +numbers: the genome is 57463 words. the llvm tier runs a loop-and-recursion benchmark in 0.11 s against 0.40 s interpreted (threshold 5000), but takes about 1 s for the self-compile against 0.24 s, because compile time dominates short bursty work. O1 and O2 cost about the same here. everything also passes with ZYHOT=1, which compiles every block. diff --git a/docs/history/13-gap-fixes/code/README.md b/docs/history/13-gap-fixes/code/README.md new file mode 100644 index 0000000..ce56f28 --- /dev/null +++ b/docs/history/13-gap-fixes/code/README.md @@ -0,0 +1,422 @@ +# tinyvm zygote: notes and transcript + +a tiny stack cpu that started as a c89 code golf entry and ended up as a self-hosting runtime. a small native core runs vm code: jit-compiled to arm64, compiled through llvm on other hosts if you build with it, or interpreted. the genome it boots is a compiler for zc, a c-like language that keeps the forth habits. the compiler lives in vm memory next to your program, and your program can call into it, patch its tables, add syntax to it and compile new code while it runs. heap objects carry an ownership tag (u, m or d) that a static analyzer inside the compiler checks, with deterministic frees at scope ends and a mark-sweep collector underneath. types are a compiler primitive, with structs, generics, reflection and an optional typechecker. a program can also wire up to 512 custom opcodes into the cpu (virtual enhancements), which the jits inline as native code. + +this folder has the latest code. `history/` is the scrapbook: notes for every stage, plus frozen code from the stages that still have it. + +## files + +`core.c` is the native core and the only native code at runtime: the arm64 jit, the llvm backend (behind `-DZYLLVM`), the portable interpreter, and the enhancement unit (recipe parser, vm-code lifter, optimizer and lowering). `src/rt.zc` is the runtime (i/o, the native division enhancements, the heap and the collector) and `src/compiler.zc` is the compiler, both written in zc. together they are the genome. `zc0.c` is stage 0 of the bootstrap, a compiler in c for the subset of zc that `compiler.zc` is written in. `tools/build.zc` makes the resident compiler compile its own source into a fresh image, and `mkgenome.c` turns that image into `genome.h`, which is generated and checked in. `examples/` has hello, factorial, echo, self-patching code, an owning list, a repl, a typed program and an enhancement demo. `tests/` has feature tests, `tests/errors/` (programs the lifetime analyzer has to reject) and `tests/typeerrors/` (programs the typechecker has to reject). each of those starts with the message it expects, and `tests/runerr.sh` runs a folder of them. `test.sh` does the whole bootstrap and runs everything on all three backends. `history/` is the scrapbook. + +## build + +``` +cc -O2 core.c io_linux.c -o zy # io_linux.c provides Q, G and J +./zy `). `ZYNOLLVM=1` turns the llvm tier off. + +regenerating the genome after changing `src/`: + +``` +cc zc0.c -o zc0 && cc mkgenome.c -o mkgenome +cat src/rt.zc src/compiler.zc | ./zc0 >genome.h # stage 0, from c +cc -O2 core.c io_linux.c -o zy +(cat tools/build.zc; printf '`'; cat src/rt.zc src/compiler.zc) | ./zy | ./mkgenome >g.h +mv g.h genome.h && cc -O2 core.c io_linux.c -o zy # now built by itself +``` + +run the last two lines once more and `genome.h` should come out byte for byte the same. `test.sh` checks that, on every backend. + +## zc, the language + +it reads like c. underneath every value is a 32 bit word and memory is word addressed, so `p + 1` is the next word whatever `p` points at, and strings are one char per word with a 0 at the end. types are optional: untyped code is typed `word`, which goes with anything. + +``` +const N = 10; +[N]int table; // a global array +fn fib(int n) -> int { if (n < 2) return n; return fib(n - 1) + fib(n - 2); } +fn main() { + for (int i = 0; i < N; i++) table[i] = fib(i); + let sq = { let t = table[5]; t * t }; // a block is an expression too, its last line is its value + let x = asm { 2 3 + }; // raw vm code, leaves one value + putn(sq + x); putc(10); + return 0; +} +``` + +declarations are c-style, type first: `int a = 1, b;`, `*node p = new(node);`, `[4]char buf;`, `point origin;`. `let` declares without a type: it infers pointer, struct and function types from the initializer and keeps numbers as plain words, and `let a[10];` is an untyped array. statements are declarations, `if`/`else`, `while`, `for`, `switch`, `break`, `continue`, `return`, `{ }` blocks, `asm { }` and expressions. `switch (e) { case 1, 2: ... default: ... }` takes constant labels, and its cases don't fall through: each ends where the next begins, and `break` leaves early. operators are c's with c's precedence, including `c ? a : b`, plus `p.x` and `p->x` (the same thing: `.` goes through a pointer on its own), `e as T` to change a type without changing the bits, and the casts below. comparisons and `&& ||` give 0 or 1. `/`, `%` and `<<` compile to native enhancement opcodes. character literals, `0x` hex and the usual escapes are there. + +a unit is read whole and scanned for function signatures before it compiles, so calls can come before definitions, tags included, and prototypes are optional. `main` runs after the whole program has compiled. every function returns one word, 0 if it falls off the end. + +builtins from the runtime: `putc`, `getc`, `eof`, `puts`, `putn`, `halt`, `strlen`, `streq`, `wcopy`, `wfill`, `new`, `new_t`, `free`, `gc`, `isobj`, `osize`, `typeof_obj`, `live_objects`, `install`, `zdiv`, `zmod`, `zshl`. + +## memory model: u, m and d + +`new(n)` or `new(T)` gives back a fresh object. bind it to a local and the local owns it, tagged **m**. tags live on local variables and parameters, the analyzer follows them through the code, and the compiler inserts `free` calls where owned objects die. a tag is written after the type, `*node.u p`, or alone, `.d x`. `u`, `m` and `d` are reserved words. + +**m** means the object belongs to this block and has to be used in it. you can lend it to callees as often as you like, and when the block ends it's freed. an m object that's never used anywhere in its block is an error. + +**d** means the object is going down. the first time it's handed to a lower scope its life here is over: passing it to a `.d` parameter, or touching it inside an inner block, which then owns it as m and frees it when that block ends. using it afterwards is an error, and so is ending the block with a d object that never left. + +**u** means the object is going up. it has to leave this block for the one above, where it arrives as m. it leaves through `return` (the function's return type has to carry `.u`, like `-> *node.u` or just `-> .u`), as the value of a block expression, or by assignment to a variable of the directly enclosing block. letting it fall out of scope, or throwing away a u result, is an error. + +recasting is multiplication by the tag: `u*x`, `d*x`, `m*x`. it only goes away from m: m can become u or d (and u and d can swap), but nothing goes back to m in the same scope. a declaration can carry a tag too, `.u r;`, and then whatever lands in it gets that tag. + +parameters: an untagged parameter is m from the caller's side, a lend, and the callee sees a plain word. `.m` makes it a checked borrow: the callee can read and write it but can't free it, return it, recast it or store it anywhere. `.d` hands the object over, and it arrives as m. + +``` +fn box(int v) -> *int.u { *int b = new(int); *b = v; return u*b; } +fn push(*list l, *int.d item) { l.items[l.len] = d*item; l.len++; return 0; } +``` + +containers own what you store in them. `p[i] = c` or `p.f = c` needs `c` to carry the same tag as `p` (or be a fresh object), and from then on `c` follows `p`: if `p` is recast, so is `c`, if `p` is given away `c` goes with it, and when `p` is freed its children are freed too. a child can't be recast or given away on its own. storing into an object you only borrowed gives the child away: it's dead here afterwards, and the caller's object owns it. the runtime records the owner in the child's header, so freeing a container frees exactly the objects it owns. + +globals, plain pointers and arrays aren't tracked. storing an owned object there needs `d*`, and the mark-sweep collector takes it over. a fresh object can go straight in. + +where paths join (after an `if`, a loop, a `switch` or `?:`) every variable has to be in the same state on every path that reaches the join, with one exception. a variable that became d, or was given away, on some path is dead afterwards, and the compiler frees it on the paths where it's still alive, on their way in. a loop's exit state is the join of its `break`s and its normal exit, so giving something away and breaking is fine. what a loop can't do is give something away on a path that comes back around to the top, since the next pass would use it dead. a u object doesn't get the dead treatment: it still has to leave. + +### the analyzer + +it runs inside the compiler on every function, including code compiled at runtime by `eval` and friends, and reports each violation with a line number and the variable's name. the compiler keeps going after errors, up to 20: a syntax error skips to the next `;` or `}` (or the next top-level declaration), and analyzer and type errors just get reported, since the parse is still in step. a unit with errors doesn't run. `tests/errors/` has one small program per rule. to turn it off: `unchecked fn f() { }` for one function, `unchecked { }` for a block, and a top-level `unchecked;` for the rest of the unit (a parse word can also set `check_on = 0`). unchecked code doesn't track anything, so what it allocates belongs to the collector, and checked variables can't be touched from inside an unchecked block. + +## types + +types are a compiler primitive. the compiler keeps them as records in a type area at 0x9C000, hash-consed so the same type is always the same pointer. they stay in memory after compiling, so programs and parse words can look at them. + +``` +struct point { int x, y; }; +struct list { int len; *T items; }; +type num = int; +fn first(*list l) -> T { return l.items[0]; } +fn mk(int cap) -> *list.u { *list l = new(list); l.items = new(T, cap); return u*l; } +... +*list<*point> ps = mk<*point>(8); // explicit type arguments, or inferred from the arguments +``` + +the kinds are `word`, `int`, `char`, `bool`, `void`, pointers `*T`, fixed arrays `[n]T`, function types `fn(int, *char) -> int`, structs, generic structs and generic functions. structs and arrays live in place (in a frame, a global or inside another struct), and their value is their address, so they're passed around by pointer. `sizeof` counts words. type arguments have to be one word (a number, a pointer or another parameter), so a generic function is compiled once and works for every instance. indexing scales by the element's size, while plain pointer arithmetic stays in words, like b. `new(T)` and `new(T, n)` stamp the type into the object's header. + +### reflection + +`typeof(e)` and `typeof(T)` give a type record and `sizeof(...)` its size. both work on any expression, which is compiled dry and never runs. at runtime: `tkind(t)` (one of the `TY_` constants), `tsize`, `tname`, `tlen`, `tnfields`, `tfname(t, i)`, `tftype`, `tfoff`, `tret`, `tnparams`, `tparam`, `tparamtag` (the u/m/d tag of a parameter), `tretu`, `tnargs` and `targ` for generic instances, and `tprint(t)`. `sigof(f)` gives the type of a function by address (untyped ones get a signature built from their arity and tags), `sigofs("name")` by name, and `typeof_obj(p)` reads the type stamped on a heap object. + +### the typechecker + +off by default. `typecheck on;` turns it on for what follows and `typecheck off;` turns it back off (a parse word can flip `tc_on`). it checks initializers, assignments, arguments (unifying generic parameters), return values, arithmetic, comparisons, dereferencing, indexing and calls. `word` matches anything, numbers mix with each other, a literal 0 is a null pointer, and arrays decay to pointers. it stops at the first error, printing what it expected and what it got. `tests/typeerrors/` has one per rule. some things are checked whatever the setting, because the compiler needs them to generate code: field names, and field access on something that isn't a struct. + +## virtual enhancements + +the cpu has room for 512 custom opcodes (22 to 533), and opcode 21 (`%` in asm) installs one at runtime. it's the closest a soft cpu gets to rewiring itself. an enhancement takes up to 8 inputs off the data stack and leaves up to 8 outputs, and it's written one of two ways: + +``` +enhance clamp(x, lo, hi) micro "x = max x lo; x = min x hi"; +enhance divmod(a, b) -> 2 micro "r2 = sdiv a b; r1 = srem a b; r0 = r2"; +enhance cube(x) vm { 0 $ 0 $ * * }; +enhance noisy(x) vm { 0 $ . } else soft_noisy; // can't be lifted (it does i/o), so calls use the fallback +``` + +a micro recipe works on registers `r0`..`r15`, which the inputs arrive in (also under the parameter names), and the outputs leave from `r0` up. the micro-ops are `li mov add sub mul sdiv udiv srem urem and or xor shl shr sar neg not lt ltu eq ne min max minu maxu mulhs mulhu clz abs sel ld st`. a `vm` body is straight-line vm code that gets lifted by running it symbolically. both turn into ssa, and the core's optimizer folds constants, simplifies algebra, removes common subexpressions and dead code, and turns division by a constant into a multiply-high sequence (`ZYTRACE=1` prints the result). the arm64 jit then inlines the code at every use with registers allocated, the llvm backend inlines it as ir, and the interpreter runs the optimized list. if something doesn't fit in registers, the jit calls the interpreter for that op. + +an enhancement is called like a function when it has one output, or by name from asm. `enhance` installs at compile time, so the wiring is there before `main` runs, and a parse word can use it right away. an image installs its own enhancements when it boots, from its symbol table. that's how `/`, `%` and `<<` work: `rt.zc` declares `idiv`, `imod` and `ishl` as one-instruction recipes. `install(desc)` is the raw interface, and its descriptor format is documented in `core.c`. stores done by an enhancement don't invalidate compiled code, so use them for data. + +this replaces my first plan for enhancements, which compiled c or llvm ir sources at runtime. part of that reply got stopped by a safety check, and once it turned out llvm was meant for portability, it moved into the backend below instead. + +## the llvm backend + +a tiered jit for hosts the arm64 backend doesn't cover. the interpreter counts how often each block start gets reached, and a hot block is translated to llvm ir using the same block rules as the arm64 jit: it ends at call, jump, ret or halt, keeps going past a jz, and literal targets are fused. the ir goes through the O2 pipeline and gets compiled by orc's lljit. stores into compiled words drop the blocks covering them, like on arm64. it pays off for long-running code: a loop-and-recursion benchmark runs about 3.3x faster than the interpreter. short bursty work like the self-compile is slower, since compile time dominates there. `ZYHOT=1` compiles every block, which is a good stress test, and the test suite runs that way too. + +## the heap + +0x40000 to 0x9BFFF, managed by a buddy allocator. the heap is split into aligned power-of-two chunks, every block is a power of two words, and a freed block merges with its buddy whenever that one is free too, so freed space can be reused at any size. allocation finds the smallest free block that fits and splits it down. free blocks sit on a doubly linked list per class, so taking a buddy off its list during a merge is constant time. checked code frees deterministically. under that runs a mark-sweep collector, conservative and non-moving, which handles globals, unchecked code and anything given to it. it runs on `gc()`, which returns how many objects it freed, or when an allocation doesn't fit. the sweep frees through the same merging path. + +## code as data + +the compiler is in memory with your program and every name in it is callable. + +`asm { }` emits raw vm words: numbers push, `-5` pushes a negative, `#n` emits a raw word, one-char tokens from the mnemonic table are opcodes, `:name` defines a label local to the block, enhancement names emit their opcode, and other names push the address of a variable or function. + +`quote { ... }` is a token list as a fresh object: `[n, kind, value, line, kind, value, line, ...]`, so errors inside a macro expansion point at the line the tokens came from, where names, strings and operators are interned pointers (`intern("while")` gets one). `tokens(s)` makes one from a string, and `qdup(q)` copies one. + +`eval(q)` compiles a token list as a function body and runs it. `compile(q)` returns its address instead, `load(q)` compiles top-level declarations, and `evals(s)` runs `eval` on a string. + +parse words: `immediate fn name() { }` runs at compile time whenever `name` shows up as a statement, inside an expression or at the top level. it starts with the current token on its own name and works the compiler directly: `next()`, `peek()` and `peekat(k)` (up to 4 tokens ahead), `tk`/`tv` (current token kind and value), `expect`, `accept`, `expr()`, `stmt()`, `substmt()`, `ptype()`, `emit`, `lit`, `op`, `hole`/`setlit` for jumps, and `here`. `grab()` reads a `{ }` group into a token list, and `feed(q)` pushes a list back into the input, which is all a macro needs. parse words can call `eval` partway through a compile: the compiler saves its state and emits the new function in place with a jump around it. the built-in keywords are entries in the same table, so a parse word called `while` replaces `while`. + +tables: `add_binop("**", 11, "ipow c")` adds or replaces a binary operator with a precedence and its code, written like asm (names resolve when it's used). `add_unop` does the same for prefix operators, and the lexer learns new spellings from both. `mnem` is the asm mnemonic table, `q@!$_~cjzr,?.+-*&|^><%`, and can be rewritten. + +## the bootstrap + +`zc0.c` compiles the subset `compiler.zc` uses (no analyzer, types, quote, parse words or block expressions, but it does lay out `enhance` declarations) into a first genome. that genome compiles `compiler.zc` again on the vm. that's where the real compiler, the analyzer and native division first meet their own source, and `compiler.zc` passes its own lifetime checks. the result compiles itself into identical words, on all three backends, and that's the `genome.h` that ships (71014 words). an image build (`build_image(org, dst)`) compiles with only the image's own names visible (plus keywords and builtin types), writes code meant for one address into another, and appends a symbol table so the booted genome knows its own names and enhancements. + +## the instruction set + +every word is 32 bits. an odd word is a literal that pushes `w>>1` (31 bits, sign extended). an even word is an opcode, `w/2`. there's a data stack and a return stack, both stored inside memory. + +``` +0 q halt 5 ~ swap 10 , in (Q) 15 * mul 20 < less +1 @ load 6 c call 11 ? empty (J) 16 & and 21 % install +2 ! store 7 j jump 12 . out (G) 17 | or 22..533 enhancements +3 $ pick 8 z jz 13 + add 18 ^ xor +4 _ drop 9 r ret 14 - sub 19 > shr +``` + +stack effects: `!` is `(v addr --)`, `$` is `(n -- copy of the nth item)` so `0 $` is dup, `z` is `(cond addr --)` and jumps when cond is 0, `c` and `j` take their target from the stack, and `%` is `(descriptor -- status)`. there's still no div or mod in the base set, because a divider can't finish in one cycle in hardware. an enhancement can add one. + +## memory map and abi + +memory is 1m (1048576) words. it could grow until the host runs out (literals already reach 1g words), but that would mean reserving the jit tables with mmap instead of static arrays, and 1m is plenty for now. + +``` +0x00000.. the program: code, with globals between functions +0x40000..0x9BFFF heap +0x9C000..0x9FFFF type records +0xA0000 frame pointer word +0xA0100..0xBFFFF compiler data: interned names, symbol table, locals, tables +0xC0000.. genome: 0xC0000 entry, 0xC0005 symbol table address, 0xC0006 image end +0xE0000..0xEFFFF frame stack, grows down from 0xF0000 +0xF0000.. return stack, grows up +0xF8000.. mark stack +0xFC000..0xFFFF0 data stack, grows down +``` + +calls push the arguments left to right and the callee's prologue pops them into its frame. locals live in the frame at `*0xA0000 + slot`. every function leaves exactly one word on the data stack. heap objects have a 6 word header: size, size class, flags, a check word, owner, type. + +## how the core works + +registers: x19 holds the memory base, x20 the data stack pointer, x21 the return stack pointer, x22 the translation table and x23 the state block (memory, s, r, table, coverage map, Q, G, &J, store hook, install hook, enhancement fallback). + +compilation is lazy. the first time control reaches an address, that block is translated from live memory. a block ends at call, jump, ret or halt. a `z` doesn't end it: the not-taken side keeps compiling, which makes superblocks. a literal followed by `c`, `j` or `z` is fused into a constant target. every exit goes through a dispatch stub that checks both stack pointers against their limits (kept in x24 and x25), bounds-checks the target, loads `tab[t]`, and branches straight to it when it's already compiled. otherwise it returns to c, which compiles the block and re-enters. enhancements are inlined: inputs load from the data stack into w0..w15, the optimized micro-ops run in registers with x16 and x17 as scratch, and the outputs go back. an install calls into c and then returns to the loop, which flushes every translation, since code built before the install may have inlined the old version. + +self-modification is handled with a byte map of which words some live translation covers. a store checks that byte inline: an uncovered target, the usual case, gets a plain store. a covered target calls the hook, which drops every translation containing that word and makes the jit code return to c at the next instruction, so even patches to the very next word take effect. writing back the same value is a no-op. if the 16 mb code buffer fills, everything is flushed. + +the stacks are checked. the data and return stacks are checked at every block exit on all three backends, so the cost is a couple of compares per block rather than per push. a block pushes at most about 1024 words, which the data stack limit leaves room for. overflowing either one stops the cpu with `zy: data stack overflow` or `zy: return stack overflow`. the frame stack is checked in every function prologue, which resets the frame pointer and stops with `zy: frame stack overflow`. + +platform: the darwin path uses `MAP_JIT`, `pthread_jit_write_protect_np` and `sys_icache_invalidate`. linux uses rwx mmap and `__builtin___clear_cache`. only linux arm64 has actually been run, under qemu. the darwin path follows apple's documented recipe but hasn't been tested, and under the hardened runtime it also needs the allow-jit entitlement. every host shares the same semantics: shift counts wrap at 32, stores outside memory are ignored, loads outside memory read 0, and jumping outside memory or to an unwired opcode halts. + +## test results + +`test.sh` passes all 83 checks on x86-64 linux: the fixpoint, an up-to-date `genome.h`, and then every example and feature test on the interpreter, on the arm64 jit under qemu, and on the llvm backend, all with identical output. it also covers joins and fixup frees (with a leak count), `switch` and `?:`, the buddy allocator, several errors in one run, and all three stack overflows. it checks that all 22 programs in `tests/errors/` and all 10 in `tests/typeerrors/` get rejected with the expected message on each backend, that llvm agrees when it compiles every block, and that the self-compile matches byte for byte on all three. separately, 80000 random divisions through enhancements matched the software routine, covering divide by zero, INT_MIN/-1, negatives and the magic-number path for constants. + +## open ends + +fixed in stage 13 (details in `history/13-gap-fixes`): branch and loop joins, free lists that never merged (now a buddy allocator), missing stack checks, stopping at the first error, macro line numbers, required prototypes, `d*` for storing into borrowed objects, `**` declarations, and `?:` and `switch`. still open: + +**untracked aliases.** the analyzer only follows bare variable names, so `let q = p + 0;` or `let c = list[1];` is a plain word nobody tracks, and it dangles once the owner is freed. two cheap fixes, and they combine well. at runtime: don't reuse a freed block right away. `free` would put it on a quarantine list, and the collector's next sweep only releases quarantined blocks that no word points at. the conservative mark already finds such words, so that's maybe 30 lines in rt.zc. a stale alias then reads a dead but intact block, and a second free through it is a no-op, because the header's flags say free. at compile time: now that there are types, a pointer-typed value computed from a tracked variable (`p + k`, `p.field`, `p[i]` when the element is a pointer) can carry a "borrowed from p" mark into the local it's stored in. using that local after `p` dies becomes an error. that's the same machinery children already use. + +**the conservative collector.** it scans the whole data stack region, because the vm can't read its own stack pointer, and stale words keep garbage alive a little longer. the fix is one base opcode that pushes the data stack depth, which is trivial in hardware since the register already exists. then the collector scans only the live part. zeroing a frame in the prologue would do the same for the frame stack, at a few words per call. + +**coarse invalidation.** a store to a covered word rescans every block, and an install flushes everything. three fixes, in order of payoff. first, index blocks by page (say 64 words): a covered store only looks at the blocks touching its page, and dropping a block only rebuilds the coverage bytes for that block. second, record which enhancement slots each translation inlines, so an install only drops those. third, on llvm, give each block its own resource tracker, so dropped code actually gets freed. + +**tiering on arm64.** answered when you asked: a run counter per block costs about 4 instructions per block entry, and a hot block could be relifted to ssa, optimized with the enhancement optimizer, and given registers instead of the memory stack. that's likely 2 to 4x on tight loops, and it would also beat llvm's warm-up on x86. not built yet. + +**generics are erased.** a generic function is compiled once, so the type stamped by `new(T)` inside it says `T`, and type arguments have to be one word. + +**the rest.** the prescan only knows tags, not types, so a call to a typed function that's defined further down gets `word` as its result type until the definition shows up. the llvm backend compiles one module per block, so it warms up slowly. stores done by enhancements don't invalidate compiled code. a parse word that changes the syntax can confuse the prescan. it only records signatures, though, and a real definition always overrides what the prescan guessed. + +--- + +## transcript + +each of my messages is quoted exactly, followed by a summary of the reply. + +### 1 + +> codegolf a tiny cpu vm in C89. the goal is to achieve the lowest character count possible. include an assembler as well (scored separately) and a hello world program (not scored, but elegance and simplicity is a bonus) +> it must be capable of re-entrant code and a realistically workable instruction set. + +**summary:** a two-stack forth-style machine. the vm was 384 bytes: one ternary chain inside `while(o=m[p++])`, the top two stack items cached in `a` and `b`, the program loaded with `read(open(...))` so it needs no headers, and both stacks inside the 65536-word memory. it had 22 opcodes including div and mod, a two-word literal, and jump/call targets popped from the stack. the assembler was 348 bytes and two-pass, using `strcspn` instead of `strchr` so the implicit int return stays safe on 64-bit. hello world was a pointer loop over a string with a zero terminator. re-entrancy was shown with a recursive factorial plus a recursive decimal printer. + +### 2 + +> your cpu should also be realistically close to hardware (translating it to logical circuitry hardware should be fairly easy) and sport an acceptably fast execution (if hypothetically ported to hardware, not in software) +> +> assume i/o is handled by two external functions, `char Q()` (getchar) and `void G(char)` (putchar) and a state variable J (boolean, shows if input is empty). +> +> don't be afraid to use macros. + +**summary:** no reply was sent to this message at the time. it was answered together with message 3. + +### 3 + +> pro tip: if you define a macro as `!o--`, you can save 2 characters per if condition +> oh and btw, newlines are free + +**summary:** used the tip as `#define X !--o?`, with pre-decrement because opcode 0 (halt) is already handled by the loop, and with the `?` folded into the macro and free newlines used as separators. the hardware-oriented redesign: bit 0 of each word is a literal flag, so literals take one cycle like the j1 cpu. div and mod were dropped because they can't finish in one cycle and were replaced by an arithmetic shift right, giving 8 alu ops that decode from 3 bits. every instruction does a fixed amount of work with at most one data memory access, and call and jump share a datapath (`r-=o`). i/o now goes through `Q`, `G` and a `?` op that reads `J`. the result was the vm at 339 bytes and the assembler at 346. the final golfed code: + +```c +#define X !--o? +int m[1<<16],p,s=65e3,r=6e4,o,a,b;main(c,v)char**v;{read(open(v[1],0),m,s);while(o=m[p++])a=m[s],b=m[s+1],o&1?m[--s]=o>>1:(o/=2,X +m[s]=m[a]:X +m[a]=b,s+=2:X +m[s]=m[s-~a]:X +s++:X +m[s]=b,m[s+1]=a:(o-=2)<1?m[r]=p,r-=o,p=a,s++:X +p=b?p:a,s+=2:X +p=m[--r]:X +m[--s]=Q():X +m[--s]=J:X +G(a),s++:(m[++s]=X +b+a:X +b-a:X +b*a:X +b&a:X +b|a:X +b^a:X +b>>a:b now let's move in stages. write a compiler that takes this assembler's code and converts it into darwin arm + +**summary:** an ungolfed c89 compiler from vm assembly to darwin arm64 assembly. every word got its own label, the image was emitted as an addressable data array, x19/x20/x21 held the memory base and the two stack pointers, and computed jumps went through a table of offsets. a literal followed by a jump became a direct branch. `Q` and `G` went through `sxtb`, since char is signed on apple arm64, and `J` was read with `ldrb`. it was verified by assembling with llvm-mc for darwin, then rewriting to linux with sed and running under qemu, where it matched the vm byte for byte. this version couldn't handle self-modifying code. + +### 5 + +> can you write the assembler in assembly and pair the code with the assembly and the assembler so that it can re-wire itself at runtime? or maybe include an interp for self-modification, otherwise fns are exec'd native + +**summary:** both. `aslib.s` is the assembler written in vm assembly as a library (`a.src`, `a.dst`, `a.org`, `a.lab`, then `a.run c`), and with `asmain.s` it's a 568-word standalone assembler. it's byte-identical to the c assembler and reaches a fixpoint when assembling itself. this is also where the `:;comment` trick came from. the compiler became a hybrid. the image is split into basic blocks, each block counts how many of its words differ from the original image, and stores into code go through a slow path that updates the counters. clean blocks run native, while dirty blocks and anything past the image run in an interpreter compiled into the same binary, which hands control back to native code whenever it lands in a clean block. `cc -t` printed `[` and `]` at each switch. the demo rewired a function at runtime to point at freshly assembled code and then restored it. + +### 6 + +> which one do you like more? and which is more performant? +> keep that one, then port to using llvm ir so that it can be platform independent + +**summary:** no reply was sent at the time. the llvm port was dropped when message 7 changed direction. the question was answered at the start of the reply to message 7: the hybrid was the favorite because it kept the vm's semantics exact. the plain compiler was slightly faster on code that never rewrites itself, but it silently ran stale code once anything did. the zygote beats both. + +### 7 + +> can you make it a zygote approach? +> a minimal core loads first, reads and compiles the program (either wholly or partially, for speed) and replaces it with itself. so it's like a jvm but self-contained and on the same level as itself, where the assembler can itself be modified in-memory to assemble different code. for this we need to ditch llvm + +**summary:** the current design, described above. the native core only does jit and glue. it loads the genome (boot loader + assembler, both vm code) at 48000. the boot loader reads source until eof or a backtick, assembles it at 0 and jumps there. compilation is lazy per block, straight from live memory, with superblocks through `z`, fused constant targets, and dispatch chained through a table. invalidation uses the coverage byte map plus a store hook, so self-modified code gets retranslated instead of interpreted. the resident assembler sits at fixed addresses, and `repl.s` rewrites its mnemonic table so `p` means out, then assembles and runs input chunks in a loop, overwriting the same address each time. verified under qemu on linux arm64, including the self-hosted assembler running on the zygote. about 4x faster than the golfed interpreter. + +### 8 + +> damn dawg you made an entire baremetal os? +> +> let's move to cowork. pack everything up alongside a transcript of every message i've sent and your own notes on what you've implemented in an md file, with the code, so i can move it. + +**summary:** not quite an os. it's a userland runtime. it still runs on darwin or linux, uses their mmap for executable memory, and gets i/o from the host through `Q`, `G` and `J`. there's no scheduler, no drivers and no hardware access, so a closer description is a tiny self-hosting jit, in the spirit of a jvm. the first packing attempt bundled every stage's code with a full test script. + +### 9 + +> nono we don't need all the code, just the recent ones. my msgs and your summaries are more important and all should be there + +**summary:** this package. only the zygote stack plus its examples and tests are included, and earlier stages appear only as summaries. the golfed reference vm and assembler are inlined under message 3, because they define the instruction set. + +### 10 + +> we're gonna make this thing cooler. make the syntax C-like whilst still keeping the forth capabilities like super-homiconicity and the likes, add explicit and automatic gc (use a fast approach) and memory management that looks like this: +> every heap object has a classifier u m d, up middle down. up means the object will die at the scope above (becomes m when returned to the parent scope, and it must be returned and cannot be discarded). m means it must be used here and can be passed down (but once it's recasted as u or d it can't be casted back to m in this scope). d means the variable is passed down, and its lifetime ends after it's given for the first time to a scope lower than the current one. +> ask me questions to clarify this approach, then add it to the code and also add a simple static analyzer which checks these lifetimes + +my answers to the clarifying questions, exactly: + +> scope: every { } block +> +> lending an m object to a callee: borrow, no escape (Recommended) +> +> storing a heap object inside another object or a global: container gets ownership. putting a value inside a container requires its umd to match the parent, and from then on, the umd for that is also inherited from the parent (as in if a parent m is casted to d, so is the child heap object) +> +> gc: free lists + mark-sweep (Recommended) +> +> where the compiler lives: resident in vm code. bootstrap it step by step, by writing a c-like in core.c and then rewriting the compiler inside the c-like. also bring back llvm if you think it'll make your job easier +> +> homoiconic features: parse words, quoted code blocks, inline stack code, patchable compiler tables +> +> how an inner block hands a u object up: expression and assign, both +> +> when the analyzer runs: inside the compiler but can be turned off if the code requests it + +**summary:** the genome is now zc, a typeless c-like language, described above. stage 0 is `zc0.c` (kept as its own file next to core.c rather than inside it, so the runtime still has one native file), and the resident compiler is `src/compiler.zc` plus `src/rt.zc`, which reached a byte-identical fixpoint compiling itself. llvm wasn't brought back, since the target is the vm itself. memory went to 1m words, and core.c gained an interpreter for non-arm64 hosts. calls i made without asking: tags go on signatures (`fn f(m a, d b) -> u`) and recasts are `x as u` / `x as d`; a d object touched inside an inner block moves into that block; storing into a borrowed object, a global or untracked memory needs `as d`; handing a u up by assignment only reaches the directly enclosing block; unchecked code gives its objects to the collector. the old asm genome moved to `legacy/`. + +### 11 + +> progress report? +> add types please :) +> mostly as a compiler primitive though, plus type-inspection via reflection +> and a small optional typechecker (default off) +> oh and keep the stuff you write as a history scrapbook +> if it helps, add a path to the hardware which is called "virtual enhancements", and allows up to 512 custom operations to be compiled down via jit into hardware-level speeds and interoped directly into a fast path in the vm to become essentially custom wiring. useful for division and other stuff as a dynamic runtime path + +my answers to the clarifying questions, exactly: + +> types: words + structs + generics +> +> reflection: runtime type of heap objects, function signatures, typeof at compile time +> +> how an enhancement is defined: either actual C or llvm ir code or vm code or micro-ops passed through an optimizer. since for example, micro-ops and vm code are not sufficient for optimizing things like division +> +> scrapbook: notes + code snapshots + +**summary:** the progress report said the previous round was finished and shipped. i then laid out a plan where the core would compile c or llvm ir enhancement sources at runtime with the host toolchain and load the result. part of that reply was stopped by a safety check before it finished, so i stopped and checked in instead of building it. + +### 12 + +> also make the umd syntax like `type.u name`, and casting is multiplication `u*name` +> ideally i'd prefer memory to be unlimited words growing until system oom, but 1m is fine +> core.c plain interp is also nice +> zc0.c own file is fine +> i mentioned llvm for portability to non-arm targets +> yes, d object semantic that was intended +> u object hands up one block and becomes an m, that's fine. the code can make it a u or a d or whatever +> explain your gaps more +> +> wait why safety check? what's wrong with that + +my answers to the clarifying questions, exactly: + +> declarations: c-style, var -> let +> +> tags on untyped values and the names u, m, d: names reserved, bare .u allowed, untype is m by default +> +> llvm: llvm jit backend (Recommended) + +**summary:** on the safety check: nothing was wrong with the request. my best guess was that it tripped on the plan for a program inside the vm handing source text to the system compiler and loading the result into the running process, since that pattern also shows up in malware. with llvm meant for portability, that path wasn't needed. the gaps got the longer explanations that are now under open ends above. then the round itself: + +**syntax.** declarations are c-style (`int x`, `*node.m p`), `let` replaces `var` for untyped and inferred declarations, tags are `.u .m .d` after the type or on their own, recasts are `u*x` / `d*x` / `m*x`, `u m d` are reserved, and an untagged parameter counts as an m lend. + +**types.** a compiler primitive: hash-consed records with structs, generic structs and functions, fixed arrays, function types, `typeof`/`sizeof`, `as` casts and a reflection api, with object headers carrying their type. the typechecker is off by default and turned on with `typecheck on;`. + +**virtual enhancements.** opcode 21 installs, 22 to 533 are the slots. micro-op recipes and lifted vm code go through an optimizer in the core (folding, cse, dce, multiply-high division by constants) and get inlined by both jits. the language's own `/ % <<` now run on them. + +**backends.** a tiered llvm backend (`-DZYLLVM`) for non-arm hosts. + +**bootstrap and history.** it still reaches its fixpoint on all three backends. `history/` holds notes for every stage and code from stages 7, 10 and this one (in 11-types-enhancements). + +### 13 + +> gaps +> 1. when there's a var state change to d, mark that var as dead by default for any other scope that can access it afterwards. u is fine since it just returns +> 2. any suggestions for a cheap fix? +> 3. it's fine, rare occurrence (probably?), suggest a fix +> 4. fix that +> 5. suggest a fix +> 6. fix +> 7. fix + +**summary:** the numbers followed the gap list from my previous chat message. here's what each one got. + +**1. joins.** a variable that became d, or was given away, on any path is dead after the join, and fixup code frees it on the paths that still own it. a loop's exit state is the join of its breaks and its normal exit. giving something away on a path that loops back is still an error. u and borrows don't get the dead treatment. + +**2, 3 and 5.** suggestions only, now under open ends: a quarantine on reuse plus "borrowed from" marks on pointer-typed values for aliases, a stack-depth opcode for the collector, and page-indexed invalidation plus per-slot install tracking for the jit. + +**4. heap.** a buddy allocator. + +**6. stack checks.** all three stacks are checked: data and return at every block exit on all backends, and frames in every prologue. + +**7. the small stuff.** error recovery with up to 20 reported errors, line numbers carried in token lists, a signature prescan over the whole unit so prototypes are optional, storing into a borrowed object moving the object automatically, `?:`, and go-style `switch`. on the side, a 4-token lookahead fixed `**` declarations. + +### 14 + +> how costly is it if we add a counter to each block to see how many times it runs, then run the optimizer on it if it's a hot path? + +> continue + +**summary:** answered without building anything. about 4 instructions per block entry while a block is being counted, and nothing once its optimized translation replaces it. the optimizing pass would reuse the enhancement lifter, optimizer and register allocator. lifting whole blocks would need jz side exits and barriers at calls and i/o. compiling a block that way takes microseconds, against milliseconds for llvm, and keeping values in registers instead of the memory stack likely gives 2 to 4x on tight loops. it's listed under open ends. then the gap fixes continued. diff --git a/docs/history/13-gap-fixes/code/core.c b/docs/history/13-gap-fixes/code/core.c new file mode 100644 index 0000000..ce353c8 --- /dev/null +++ b/docs/history/13-gap-fixes/code/core.c @@ -0,0 +1,1183 @@ +/* zygote core: puts the genome (the zc compiler and its runtime, both vm code) + into memory and runs it. on arm64, every block of vm code is translated to + arm64 the first time it is reached, straight from live memory. stores that + land on translated words throw those translations away, so code that + rewrites itself, including the resident compiler, just gets retranslated + on the next visit. on other hosts (or with -DZYINTERP) a plain interpreter + with the same semantics runs instead. ZYTRACE=1 logs translations on stderr. */ +#include +#include +#include +#if defined(__aarch64__) && !defined(ZYINTERP) +#define ZYJIT 1 +#include +#ifdef __APPLE__ +#include +#include +#endif +#endif +#include "genome.h" + +#ifndef JTYPE +#define JTYPE char +#endif +char Q(void); +void G(char); +extern JTYPE J; + +#define MW (1 << 20) /* memory, in words */ +#define DS0 (MW - 16) /* data stack grows down from here */ +#define RS0 0xF0000 /* return stack grows up from here */ +#define DSLIM (0xFC000 + 1100) /* data stack overflows below this (a block pushes at most ~1024) */ +#define RSLIM (0xF8000 - 8) /* return stack overflows above this */ +/* why the cpu stopped, when it wasn't a halt */ +static int stopped(int p) +{ + if (p == MW + 1) { fprintf(stderr, "zy: data stack overflow\n"); return 2; } + if (p == MW + 2) { fprintf(stderr, "zy: return stack overflow\n"); return 2; } + return 0; +} +#define CODEW (4 << 20) /* 16 MB of code, in instruction words */ +#define MAXB 65536 +#define BLKW 1024 /* longest block, in vm words */ + +static int m[MW]; +static int trace; + +/* ---- virtual enhancements ---- + up to 512 custom opcodes (22 .. 533), wired in at runtime by the install + opcode (21). an enhancement is a short program over native micro-ops, + written either as a recipe (text) or as straight-line vm code that gets + lifted. both become ssa, go through the optimizer below, and then run in + the interpreter or get inlined by the jit as native code. */ +#define NENH 512 +#define XOP0 22 +enum { U_LI, U_MOV, U_ADD, U_SUB, U_MUL, U_SDIV, U_UDIV, U_SREM, U_UREM, U_AND, U_OR, U_XOR, + U_SHL, U_SHR, U_SAR, U_NEG, U_NOT, U_LT, U_LTU, U_EQ, U_NE, U_MIN, U_MAX, U_MINU, U_MAXU, + U_MULHS, U_MULHU, U_CLZ, U_ABS, U_SEL, U_LD, U_ST, U_N }; +static const char *unames[U_N] = { "li", "mov", "add", "sub", "mul", "sdiv", "udiv", "srem", "urem", + "and", "or", "xor", "shl", "shr", "sar", "neg", "not", "lt", "ltu", "eq", "ne", "min", "max", + "minu", "maxu", "mulhs", "mulhu", "clz", "abs", "sel", "ld", "st" }; +static const signed char uarity[U_N] = { 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 1, 3, 1, 2 }; +typedef struct { int op, d, a, b, c, imm; } Uop; /* ssa: d is a fresh value, a b c are values */ +typedef struct { + int live, kind, nin, nout, n, nv, out[8]; + Uop *u; + signed char *reg; /* physical register per value, for the jit; 0 if it didn't fit */ +} Enh; +static Enh enh[NENH]; +static int need_flush; + +#define MAXUOP 4096 +static Uop ub[MAXUOP]; static int un, unv, uerr; /* the program being built */ +static int unew(int op, int a, int b, int c, int imm) +{ + Uop *u; + if (un >= MAXUOP) { uerr = -5; return 0; } + u = &ub[un++]; u->op = op; u->d = unv++; u->a = a; u->b = b; u->c = c; u->imm = imm; + return u->d; +} +static int mread(int a) { return (unsigned)a < MW ? m[a] : 0; } + +/* recipe text: one statement per line or per ';' + r0 = sdiv r0 r1 dst = op operands, operands are rN, parameter names or numbers + r2 = r0 a copy, or a number + st r1 r0 memory[r1] = r0 + inputs arrive in r0.. (and under the parameter names), outputs leave from r0.. */ +static int parse_recipe(int src, int names, int nin, int nout, int *out) +{ + int cur[16], i, p = src, n, k, op, args[3], na, dst, isst; + char tok[8][32]; int nt; + for (i = 0; i < 16; i++) cur[i] = i < nin ? i : -1; + for (;;) { + /* read one statement into tokens */ + nt = 0; + for (;;) { + int c = mread(p); + while (c == ' ' || c == '\t' || c == '\r') c = mread(++p); + if (!c || c == '\n' || c == ';') break; + if (c == '=') { if (nt < 8) strcpy(tok[nt++], "="); p++; continue; } + n = 0; + while (c && c != ' ' && c != '\t' && c != '\n' && c != ';' && c != '=' && c != '\r') { if (n < 31) tok[nt < 8 ? nt : 7][n++] = (char)c; c = mread(++p); } + if (nt < 8) tok[nt++][n] = 0; + } + if (nt) { + isst = !strcmp(tok[0], "st"); + if (!isst && (nt < 3 || strcmp(tok[1], "="))) return -2; + /* destination register */ + dst = -1; + if (!isst) { + if (tok[0][0] == 'r' && tok[0][1] >= '0' && tok[0][1] <= '9') dst = atoi(tok[0] + 1); + else for (i = 0; i < nin && names; i++) { + int s = mread(names + i), j = 0; + while (mread(s + j) && mread(s + j) == (unsigned char)tok[0][j]) j++; + if (!mread(s + j) && !tok[0][j]) dst = i; + } + if (dst < 0 || dst > 15) return -2; + } + k = isst ? 1 : 2; /* first token after "=" (or after st) */ + op = -1; + if (!isst && nt > 3) { for (i = 0; i < U_N; i++) if (!strcmp(tok[k], unames[i])) op = i; if (op < 0) return -2; k++; } + if (isst) op = U_ST; + na = 0; + for (; k < nt; k++) { + char *t = tok[k], *e; int v = -1; + if (na >= 3) return -2; + if (t[0] == 'r' && t[1] >= '0' && t[1] <= '9') { int r = atoi(t + 1); if (r > 15) return -2; if (cur[r] < 0) cur[r] = unew(U_LI, 0, 0, 0, 0); v = cur[r]; } + else if ((t[0] >= '0' && t[0] <= '9') || t[0] == '-') { long x = strtol(t, &e, 0); if (*e) return -2; v = unew(U_LI, 0, 0, 0, (int)x); } + else { + for (i = 0; i < nin && names; i++) { + int s = mread(names + i), j = 0; + while (mread(s + j) && mread(s + j) == (unsigned char)t[j]) j++; + if (!mread(s + j) && !t[j]) v = cur[i] < 0 ? (cur[i] = unew(U_LI, 0, 0, 0, 0)) : cur[i]; + } + if (v < 0) return -2; + } + args[na++] = v; + } + if (op < 0) { /* x = y */ + if (na != 1) return -2; + cur[dst] = args[0]; + } else { + if (na != (op == U_LI ? 0 : uarity[op])) { + if (!(op == U_LI && na == 1)) return -2; + } + if (op == U_LI) cur[dst] = args[0]; + else if (op == U_ST) unew(U_ST, args[0], args[1], 0, 0); + else cur[dst] = unew(op, args[0], na > 1 ? args[1] : 0, na > 2 ? args[2] : 0, 0); + } + if (uerr) return uerr; + } + if (!mread(p)) break; + p++; + } + for (i = 0; i < nout; i++) { if (cur[i] < 0) cur[i] = unew(U_LI, 0, 0, 0, 0); out[i] = cur[i]; } + return uerr; +} + +/* straight-line vm code from addr up to its ret, run symbolically */ +static int lift_vm(int a, int len, int nin, int nout, int *out) +{ + int st[256], sp = 0, i, w, k, x, y; + for (i = 0; i < nin; i++) st[sp++] = i; + for (i = 0; i < len; i++) { + w = mread(a + i); + if (w & 1) { if (sp >= 256) return -4; st[sp++] = unew(U_LI, 0, 0, 0, w >> 1); continue; } + k = w / 2; + if (k == 9) break; + switch (k) { + case 1: if (sp < 1) return -4; st[sp - 1] = unew(U_LD, st[sp - 1], 0, 0, 0); break; + case 2: if (sp < 2) return -4; unew(U_ST, st[sp - 1], st[sp - 2], 0, 0); sp -= 2; break; + case 3: /* pick needs a constant index */ + if (sp < 1) return -4; + x = st[sp - 1]; + if (x < nin) return -4; + { int j; for (j = 0; j < un && ub[j].d != x; j++); if (j == un || ub[j].op != U_LI) return -4; y = ub[j].imm; } + if (y < 0 || y >= sp - 1) return -4; + st[sp - 1] = st[sp - 2 - y]; + break; + case 4: if (sp < 1) return -4; sp--; break; + case 5: if (sp < 2) return -4; x = st[sp - 1]; st[sp - 1] = st[sp - 2]; st[sp - 2] = x; break; + case 13: case 14: case 15: case 16: case 17: case 18: case 19: case 20: { + static const int map[] = { U_ADD, U_SUB, U_MUL, U_AND, U_OR, U_XOR, U_SAR, U_LT }; + if (sp < 2) return -4; + x = st[sp - 1]; y = st[sp - 2]; sp -= 2; + st[sp++] = unew(map[k - 13], y, x, 0, 0); + break; } + default: return -4; /* control flow, i/o and other enhancements don't lift */ + } + if (uerr) return uerr; + } + if (sp != nout) return -4; + for (i = 0; i < nout; i++) out[i] = st[i]; + return uerr; +} + +/* ---- the optimizer ---- + one forward pass of copy propagation, constant folding, algebraic + simplification, division by constants (the multiply-high trick) and + common subexpression elimination, then dead code removal. */ +static int ev(int op, int a, int b, int c) +{ + unsigned ua = (unsigned)a, ub_ = (unsigned)b; + switch (op) { + case U_ADD: return (int)(ua + ub_); + case U_SUB: return (int)(ua - ub_); + case U_MUL: return (int)(ua * ub_); + case U_SDIV: return b == 0 ? 0 : (a == (int)0x80000000 && b == -1) ? a : a / b; + case U_UDIV: return b == 0 ? 0 : (int)(ua / ub_); + case U_SREM: return b == 0 ? a : (a == (int)0x80000000 && b == -1) ? 0 : a % b; + case U_UREM: return b == 0 ? a : (int)(ua % ub_); + case U_AND: return a & b; + case U_OR: return a | b; + case U_XOR: return a ^ b; + case U_SHL: return (int)(ua << (b & 31)); + case U_SHR: return (int)(ua >> (b & 31)); + case U_SAR: return a >> (b & 31); + case U_NEG: return (int)(0u - ua); + case U_NOT: return ~a; + case U_LT: return a < b; + case U_LTU: return ua < ub_; + case U_EQ: return a == b; + case U_NE: return a != b; + case U_MIN: return a < b ? a : b; + case U_MAX: return a > b ? a : b; + case U_MINU: return ua < ub_ ? a : b; + case U_MAXU: return ua > ub_ ? a : b; + case U_MULHS: return (int)(((long long)a * (long long)b) >> 32); + case U_MULHU: return (int)(((unsigned long long)ua * ub_) >> 32); + case U_CLZ: { int n = 0; if (!ua) return 32; while (!(ua & 0x80000000u)) { ua <<= 1; n++; } return n; } + case U_ABS: return a < 0 ? (int)(0u - ua) : a; + case U_SEL: return a ? b : c; + } + return 0; +} +static void smagic(int d, int *mul, int *sh) /* hacker's delight, signed */ +{ + const unsigned two31 = 0x80000000u; + unsigned ad = d < 0 ? 0u - (unsigned)d : (unsigned)d, t = two31 + ((unsigned)d >> 31); + unsigned anc = t - 1 - t % ad, q1 = two31 / anc, r1 = two31 - q1 * anc, q2 = two31 / ad, r2 = two31 - q2 * ad, delta; + int p = 31; + do { + p++; + q1 *= 2; r1 *= 2; if (r1 >= anc) { q1++; r1 -= anc; } + q2 *= 2; r2 *= 2; if (r2 >= ad) { q2++; r2 -= ad; } + delta = ad - r2; + } while (q1 < delta || (q1 == delta && r1 == 0)); + *mul = (int)(q2 + 1); if (d < 0) *mul = -*mul; + *sh = p - 32; +} +static void umagic(unsigned d, unsigned *mul, int *add, int *sh) /* hacker's delight, unsigned */ +{ + unsigned nc = 0xFFFFFFFFu - (0u - d) % d, q1, r1, q2, r2, delta; + int p = 31; + *add = 0; + q1 = 0x80000000u / nc; r1 = 0x80000000u - q1 * nc; + q2 = 0x7FFFFFFFu / d; r2 = 0x7FFFFFFFu - q2 * d; + do { + p++; + if (r1 >= nc - r1) { q1 = 2 * q1 + 1; r1 = 2 * r1 - nc; } else { q1 = 2 * q1; r1 = 2 * r1; } + if (r2 + 1 >= d - r2) { if (q2 >= 0x7FFFFFFFu) *add = 1; q2 = 2 * q2 + 1; r2 = 2 * r2 + 1 - d; } + else { if (q2 >= 0x80000000u) *add = 1; q2 = 2 * q2; r2 = 2 * r2 + 1; } + delta = d - 1 - r2; + } while (p < 64 && (q1 < delta || (q1 == delta && r1 == 0))); + *mul = q2 + 1; *sh = p - 32; +} + +static Uop ob[MAXUOP]; static int on, onv; +static int *omap, *isk, *kv; /* old value -> new value; constness of new values */ +static int pure(int op) { return op != U_LD && op != U_ST; } +static int ispow2(unsigned x) { return x && !(x & (x - 1)); } +static int lg2(unsigned x) { int n = 0; while (x > 1) { x >>= 1; n++; } return n; } +static int oe(int op, int a, int b, int c, int imm); +static int K(int v) { return oe(U_LI, 0, 0, 0, v); } +static int raw(int op, int a, int b, int c, int imm) +{ + Uop *u; int i; + if (pure(op)) /* cse */ + for (i = 0; i < on; i++) { + u = &ob[i]; + if (u->op == op && u->a == a && u->b == b && u->c == c && u->imm == imm) return u->d; + } + if (on >= MAXUOP) { uerr = -5; return 0; } + u = &ob[on++]; u->op = op; u->d = onv++; u->a = a; u->b = b; u->c = c; u->imm = imm; + isk[u->d] = op == U_LI; kv[u->d] = imm; + return u->d; +} +static int sdivk(int a, int d) /* a / d for a constant d, no divide instruction */ +{ + int mul, sh, q, t; + if (d == 1) return a; + if (d == -1) return oe(U_NEG, a, 0, 0, 0); + if (d == (int)0x80000000) return oe(U_EQ, a, K(d), 0, 0); + if (ispow2((unsigned)(d < 0 ? -d : d))) { + int k = lg2((unsigned)(d < 0 ? -d : d)); + t = oe(U_SAR, a, K(31), 0, 0); + t = oe(U_SHR, t, K(32 - k), 0, 0); + q = oe(U_SAR, oe(U_ADD, a, t, 0, 0), K(k), 0, 0); + return d < 0 ? oe(U_NEG, q, 0, 0, 0) : q; + } + smagic(d, &mul, &sh); + q = oe(U_MULHS, a, K(mul), 0, 0); + if (d > 0 && mul < 0) q = oe(U_ADD, q, a, 0, 0); + if (d < 0 && mul > 0) q = oe(U_SUB, q, a, 0, 0); + if (sh) q = oe(U_SAR, q, K(sh), 0, 0); + return oe(U_ADD, q, oe(U_SHR, q, K(31), 0, 0), 0, 0); +} +static int udivk(int a, unsigned d) +{ + unsigned mul; int add, sh, t; + if (d == 1) return a; + if (ispow2(d)) return oe(U_SHR, a, K(lg2(d)), 0, 0); + if (d > 0x80000000u) return oe(U_XOR, oe(U_LTU, a, K((int)d), 0, 0), K(1), 0, 0); /* a >= d */ + umagic(d, &mul, &add, &sh); + t = oe(U_MULHU, a, K((int)mul), 0, 0); + if (!add) return sh ? oe(U_SHR, t, K(sh), 0, 0) : t; + t = oe(U_ADD, oe(U_SHR, oe(U_SUB, a, t, 0, 0), K(1), 0, 0), t, 0, 0); + return sh > 1 ? oe(U_SHR, t, K(sh - 1), 0, 0) : t; +} +static int oe(int op, int a, int b, int c, int imm) +{ + int ka, kb, A, B; + if (op == U_LI) return raw(U_LI, 0, 0, 0, imm); + if (op == U_MOV) return a; + if (op == U_LD || op == U_ST) return raw(op, a, b, 0, 0); + ka = isk[a]; kb = uarity[op] > 1 && isk[b]; + A = kv[a]; B = kv[b]; + /* everything constant: fold */ + if (ka && (uarity[op] < 2 || kb) && (uarity[op] < 3 || isk[c])) return K(ev(op, A, B, kv[c])); + /* constants go right for commutative ops */ + if (ka && !kb && (op == U_ADD || op == U_MUL || op == U_AND || op == U_OR || op == U_XOR || op == U_EQ || op == U_NE || + op == U_MIN || op == U_MAX || op == U_MINU || op == U_MAXU || op == U_MULHS || op == U_MULHU)) { + int t = a; a = b; b = t; kb = 1; B = A; ka = 0; + } + if (op == U_SEL && ka) return A ? b : c; + if (op == U_SEL && b == c) return b; + if (kb) switch (op) { + case U_ADD: case U_SUB: case U_OR: case U_XOR: case U_SHL: case U_SHR: case U_SAR: + if (!B || (op >= U_SHL && op <= U_SAR && !(B & 31))) return a; + break; + case U_MUL: + if (B == 0) return K(0); + if (B == 1) return a; + if (B == -1) return oe(U_NEG, a, 0, 0, 0); + if (ispow2((unsigned)B)) return oe(U_SHL, a, K(lg2((unsigned)B)), 0, 0); + break; + case U_AND: if (B == 0) return K(0); if (B == -1) return a; break; + case U_SDIV: if (B == 0) return K(0); return sdivk(a, B); + case U_SREM: if (B == 0) return a; if (B == 1 || B == -1) return K(0); + return oe(U_SUB, a, oe(U_MUL, sdivk(a, B), b, 0, 0), 0, 0); + case U_UDIV: if (B == 0) return K(0); return udivk(a, (unsigned)B); + case U_UREM: if (B == 0) return a; + if (ispow2((unsigned)B)) return oe(U_AND, a, K(B - 1), 0, 0); + return oe(U_SUB, a, oe(U_MUL, udivk(a, (unsigned)B), b, 0, 0), 0, 0); + } + if (a == b && uarity[op] == 2) { + if (op == U_SUB || op == U_XOR || op == U_LT || op == U_LTU || op == U_NE) return K(0); + if (op == U_AND || op == U_OR || op == U_MIN || op == U_MAX || op == U_MINU || op == U_MAXU) return a; + if (op == U_EQ) return K(1); + } + return raw(op, a, b, c, 0); +} +/* runs the optimizer over ub[0..un) with nin inputs; leaves the result in e */ +static int optimize(Enh *e, int nin, int *out, int nout) +{ + int i, *live, n = 0; + static int omap_[MAXUOP + 16], isk_[MAXUOP * 4], kv_[MAXUOP * 4]; + omap = omap_; isk = isk_; kv = kv_; + on = 0; onv = nin; + for (i = 0; i < nin; i++) { omap[i] = i; isk[i] = 0; } + for (i = 0; i < un && !uerr; i++) { + Uop *u = &ub[i]; + int a = uarity[u->op] > 0 || u->op == U_ST || u->op == U_LD ? omap[u->a] : 0; + int b = uarity[u->op] > 1 || u->op == U_ST ? omap[u->b] : 0; + int c = uarity[u->op] > 2 ? omap[u->c] : 0; + if (u->op == U_ST) oe(U_ST, a, b, 0, 0); + else omap[u->d] = oe(u->op, a, b, c, u->imm); + } + if (uerr) return uerr; + /* dead code: outputs and stores are live */ + live = calloc(onv + 1, sizeof *live); + for (i = 0; i < nout; i++) { e->out[i] = omap[out[i]]; live[e->out[i]] = 1; } + for (i = on; i--;) { + Uop *u = &ob[i]; + if (u->op != U_ST && !live[u->d]) { u->op = -1; continue; } + if (uarity[u->op] > 0 || u->op == U_ST || u->op == U_LD) live[u->a] = 1; + if (uarity[u->op] > 1 || u->op == U_ST) live[u->b] = 1; + if (uarity[u->op] > 2) live[u->c] = 1; + } + free(live); + e->u = malloc((on + 1) * sizeof(Uop)); + for (i = 0; i < on; i++) if (ob[i].op >= 0) e->u[n++] = ob[i]; + e->n = n; e->nv = onv; e->nin = nin; e->nout = nout; + return 0; +} +/* run an installed enhancement on a downward-growing stack; returns the new top */ +static int *xexec(Enh *e, int *sp) +{ + int vb[256], *v = e->nv <= 256 ? vb : malloc(e->nv * sizeof(int)), i, j; + for (j = 0; j < e->nin; j++) v[j] = sp[e->nin - 1 - j]; + for (i = 0; i < e->n; i++) { + Uop *u = &e->u[i]; + switch (u->op) { + case U_LI: v[u->d] = u->imm; break; + case U_LD: v[u->d] = mread(v[u->a]); break; + case U_ST: if ((unsigned)v[u->a] < MW) m[v[u->a]] = v[u->b]; break; + default: v[u->d] = ev(u->op, v[u->a], v[u->b], v[u->c]); + } + } + sp += e->nin - e->nout; + for (j = 0; j < e->nout; j++) sp[e->nout - 1 - j] = v[e->out[j]]; + if (v != vb) free(v); + return sp; +} +static void xdump(int slot, Enh *e) +{ + int i; + fprintf(stderr, "enh %d: %d in, %d out, %d ops\n", slot, e->nin, e->nout, e->n); + for (i = 0; i < e->n; i++) { + Uop *u = &e->u[i]; + if (u->op == U_ST) fprintf(stderr, " st v%d v%d\n", u->a, u->b); + else if (u->op == U_LI) fprintf(stderr, " v%d = %d\n", u->d, u->imm); + else { fprintf(stderr, " v%d = %s v%d", u->d, unames[u->op], u->a); if (uarity[u->op] > 1) fprintf(stderr, " v%d", u->b); if (uarity[u->op] > 2) fprintf(stderr, " v%d", u->c); fprintf(stderr, "\n"); } + } + for (i = 0; i < e->nout; i++) fprintf(stderr, " out%d = v%d\n", i, e->out[i]); +} +/* registers for the jit: values live in w0..w15, straight-line linear scan. + if they don't fit, reg stays 0 and the jit calls the interpreter instead. */ +static void regalloc(Enh *e) +{ + int *last = malloc(e->nv * sizeof(int)), i, j, k, fr = 0xffff; + signed char *r = malloc(e->nv); + for (i = 0; i < e->nv; i++) { last[i] = -2; r[i] = -1; } + for (i = 0; i < e->n; i++) { + Uop *u = &e->u[i]; + if (uarity[u->op] > 0 || u->op == U_ST || u->op == U_LD) last[u->a] = i; + if (uarity[u->op] > 1 || u->op == U_ST) last[u->b] = i; + if (uarity[u->op] > 2) last[u->c] = i; + } + for (i = 0; i < e->nout; i++) last[e->out[i]] = e->n; +#define TAKE(v) do { for (k = 0; k < 16 && !(fr >> k & 1); k++); if (k == 16) goto fail; fr &= ~(1 << k); r[v] = (signed char)k; } while (0) + for (j = 0; j < e->nin; j++) if (last[j] >= -1 && last[j] != -2) TAKE(j); + for (i = 0; i < e->n; i++) { + Uop *u = &e->u[i]; int ops[3], no = 0; + if (uarity[u->op] > 0 || u->op == U_ST || u->op == U_LD) ops[no++] = u->a; + if (uarity[u->op] > 1 || u->op == U_ST) ops[no++] = u->b; + if (uarity[u->op] > 2) ops[no++] = u->c; + for (j = 0; j < no; j++) if (last[ops[j]] == i && r[ops[j]] >= 0 && (fr >> r[ops[j]] & 1) == 0) fr |= 1 << r[ops[j]]; + if (u->op != U_ST) TAKE(u->d); + if (u->op != U_ST && last[u->d] == -2) fr |= 1 << r[u->d]; /* never used */ + } +#undef TAKE + free(last); e->reg = r; return; +fail: + free(last); free(r); e->reg = 0; +} +/* the install opcode. descriptor: slot, kind (0 recipe, 1 vm code), inputs, + outputs, payload address, payload length, parameter names (or 0). + returns 0, or -1 bad descriptor, -2 recipe syntax, -4 code doesn't lift, + -5 too big. */ +static int install(int d) +{ + int slot = mread(d), kind = mread(d + 1), nin = mread(d + 2), nout = mread(d + 3); + int pay = mread(d + 4), len = mread(d + 5), names = mread(d + 6), out[8], r; + Enh ne; + if ((unsigned)slot >= NENH || (unsigned)nin > 8 || (unsigned)nout > 8 || (kind != 0 && kind != 1)) return -1; + un = 0; unv = nin; uerr = 0; + r = kind == 0 ? parse_recipe(pay, names, nin, nout, out) : lift_vm(pay, len, nin, nout, out); + if (r) { if (trace) fprintf(stderr, "enh %d: install failed (%d)\n", slot, r); return r; } + memset(&ne, 0, sizeof ne); + if ((r = optimize(&ne, nin, out, nout))) return r; + ne.kind = kind; ne.live = 1; + regalloc(&ne); + if (enh[slot].live) { free(enh[slot].u); free(enh[slot].reg); } + enh[slot] = ne; + need_flush = 1; + if (trace) xdump(slot, &enh[slot]); + return 0; +} +#ifdef ZYJIT +static unsigned char cov[MW]; /* 1 if a live translation covers the word */ +static void *tab[MW]; /* vm address -> translation */ +static struct { int a, b; } bl[MAXB]; /* live translations cover [a,b); a < 0 is dead */ +static int nbl; +static unsigned *code, *cp, *x_enter, *x_exit, *x_disp, *x_first; +static long S[13]; /* m, s, r, tab, cov, Q, G, &J, store hook, install, enhancement fallback, stack limits */ + +enum { W0 = 0, W1 = 1, X9 = 9, X10, X11, M = 19, SP = 20, RP = 21, TAB = 22, ST = 23 }; + +static void E(unsigned x) { *cp++ = x; } +static void li(int d, int v) +{ + unsigned u = (unsigned)v; + E(0x52800000 | (u & 0xffff) << 5 | d); + if (u >> 16) E(0x72a00000 | (u >> 16) << 5 | d); +} +static void ldr_post(int t, int n, int i) { E(0xb8400400 | (i & 0x1ff) << 12 | n << 5 | t); } +static void ldr_pre(int t, int n, int i) { E(0xb8400c00 | (i & 0x1ff) << 12 | n << 5 | t); } +static void str_pre(int t, int n, int i) { E(0xb8000c00 | (i & 0x1ff) << 12 | n << 5 | t); } +static void str_post(int t, int n, int i) { E(0xb8000400 | (i & 0x1ff) << 12 | n << 5 | t); } +static void ldr_u(int t, int n, int o) { E(0xb9400000 | (o / 4) << 10 | n << 5 | t); } +static void str_u(int t, int n, int o) { E(0xb9000000 | (o / 4) << 10 | n << 5 | t); } +static void ldrx_u(int t, int n, int o) { E(0xf9400000 | (o / 8) << 10 | n << 5 | t); } +static void ldr_rr(int t, int n, int r, int ext) { E(0xb8600800 | r << 16 | ext << 13 | 1 << 12 | n << 5 | t); } +static void str_rr(int t, int n, int r, int ext) { E(0xb8200800 | r << 16 | ext << 13 | 1 << 12 | n << 5 | t); } +static void ldp_post(int t, int u, int n, int i) { E(0x28c00000 | ((i / 4) & 0x7f) << 15 | u << 10 | n << 5 | t); } +static void op3(unsigned op, int d, int n, int r) { E(op | r << 16 | n << 5 | d); } +static void addx_ext(int d, int n, int r, int ext) { E(0x8b200000 | r << 16 | ext << 13 | 2 << 10 | n << 5 | d); } +static void movw(int d, int r) { E(0x2a0003e0 | r << 16 | d); } +static void blr(int n) { E(0xd63f0000 | n << 5); } +static void b_to(unsigned *t) { long o = t - cp; E(0x14000000 | (o & 0x3ffffff)); } +static void cb_fix(unsigned *at, unsigned *to) { *at |= ((to - at) & 0x7ffff) << 5; } +#define SXTW 6 +#define UXTW 2 + +static void build_stubs(void) +{ + cp = code; + x_enter = cp; /* int enter(long *S, void *code) */ + E(0xa9bb7bfd); /* stp x29, x30, [sp, #-80]! */ + E(0x910003fd); /* mov x29, sp */ + E(0xa90153f3); /* stp x19, x20, [sp, #16] */ + E(0xa9025bf5); /* stp x21, x22, [sp, #32] */ + E(0xf9001bf7); /* str x23, [sp, #48] */ + E(0xa90467f8); /* stp x24, x25, [sp, #64] */ + E(0xaa0003f7); /* mov x23, x0 */ + ldrx_u(M, ST, 0); + ldrx_u(24, ST, 88); ldrx_u(25, ST, 96); /* x24, x25: the stack limits, as addresses */ + ldr_u(X9, ST, 8); addx_ext(SP, M, X9, UXTW); + ldr_u(X9, ST, 16); addx_ext(RP, M, X9, UXTW); + ldrx_u(TAB, ST, 24); + E(0xd61f0020); /* br x1 */ + x_exit = cp; /* w0 = next vm address */ + op3(0xcb000000, X9, SP, M); E(0xd342fd29); str_u(X9, ST, 8); /* s = (x20 - x19) >> 2 */ + op3(0xcb000000, X9, RP, M); E(0xd342fd29); str_u(X9, ST, 16); + E(0xa94153f3); /* ldp x19, x20, [sp, #16] */ + E(0xa9425bf5); /* ldp x21, x22, [sp, #32] */ + E(0xf9401bf7); /* ldr x23, [sp, #48] */ + E(0xa94467f8); /* ldp x24, x25, [sp, #64] */ + E(0xa8c57bfd); /* ldp x29, x30, [sp], #80 */ + E(0xd65f03c0); /* ret */ + { + unsigned *ovd = cp, *ovr; + li(W0, MW + 1); b_to(x_exit); /* data stack overflow */ + ovr = cp; + li(W0, MW + 2); b_to(x_exit); /* return stack overflow */ + x_disp = cp; /* w0 = target: check the stacks, chain through tab[] or fall back to c */ + E(0xeb18029f); /* cmp x20, x24 */ + E(0x54000003 | ((ovd - cp) & 0x7ffff) << 5); /* b.lo data overflow */ + E(0xeb1902bf); /* cmp x21, x25 */ + E(0x54000002 | ((ovr - cp) & 0x7ffff) << 5); /* b.hs return overflow */ + } + E(0x7144001f); /* cmp w0, #MW */ + E(0x54000002 | ((x_exit - cp) & 0x7ffff) << 5); /* b.hs exit */ + E(0xf8605ac9); /* ldr x9, [x22, w0, uxtw #3] */ + E(0xb4000009 | ((x_exit - cp) & 0x7ffff) << 5); /* cbz x9, exit */ + E(0xd61f0120); /* br x9 */ + x_first = cp; +} + +static void flush(void) +{ + memset(tab, 0, sizeof tab); + memset(cov, 0, sizeof cov); + nbl = 0; + cp = x_first; + if (trace) fprintf(stderr, "jit: flushed\n"); +} + +static void jitwrite(int on) +{ +#ifdef __APPLE__ + pthread_jit_write_protect_np(!on); +#else + (void)on; +#endif +} + +static void syncicache(unsigned *a, unsigned *b) +{ +#ifdef __APPLE__ + sys_icache_invalidate(a, (char *)b - (char *)a); +#else + __builtin___clear_cache((char *)a, (char *)b); +#endif +} + +/* store hook for words that are covered by a translation */ +static int hook(int a, int v) +{ + int j, x, hit = 0; + if (m[a] == v) return 0; + m[a] = v; + for (j = 0; j < nbl; j++) + if (bl[j].a >= 0 && bl[j].a <= a && a < bl[j].b) { + tab[bl[j].a] = 0; + for (x = bl[j].a; x < bl[j].b; x++) cov[x] = 0; + if (trace) fprintf(stderr, "jit: dropped %d..%d\n", bl[j].a, bl[j].b - 1); + bl[j].a = -1; + hit = 1; + } + for (j = 0; j < nbl; j++) + if (bl[j].a >= 0) for (x = bl[j].a; x < bl[j].b; x++) cov[x] = 1; + return hit; +} + +static void op(int i, int k) /* everything except control flow */ +{ + unsigned *a, *b, *c, *z; + switch (k) { + case 1: ldr_u(X9, SP, 0); ldr_rr(X9, M, X9, SXTW); str_u(X9, SP, 0); return; + case 2: /* store: inline unless the target word is translated */ + ldp_post(X9, X10, SP, 8); + E(0x7144013f); /* cmp w9, #MW */ + a = cp; E(0x54000002); /* b.hs done */ + ldrx_u(X11, ST, 32); + E(0x3869496b); /* ldrb w11, [x11, w9, uxtw] */ + b = cp; E(0x3500000b); /* cbnz w11, slow */ + str_rr(X10, M, X9, UXTW); + c = cp; E(0x14000000); /* b done */ + cb_fix(b, cp); /* slow: hook(a, v) */ + movw(W0, X9); movw(W1, X10); ldrx_u(X11, ST, 64); blr(X11); + z = cp; E(0x34000000); /* cbz w0, done */ + li(W0, i + 1); b_to(x_exit); /* a translation died: resume at i+1 in c */ + cb_fix(a, cp); cb_fix(z, cp); + *c |= (cp - c) & 0x3ffffff; + return; + case 3: ldr_u(X9, SP, 0); E(0x9100128a); ldr_rr(X9, X10, X9, SXTW); str_u(X9, SP, 0); return; /* add x10, x20, #4 */ + case 4: E(0x91001294); return; /* add x20, x20, #4 */ + case 5: E(0x29402a89); E(0x2900268a); return; /* ldp w9, w10, [x20]; stp w10, w9, [x20] */ + case 10: ldrx_u(X11, ST, 40); blr(X11); E(0x13001c09); str_pre(X9, SP, -4); return; /* sxtb w9, w0 */ + case 11: + ldrx_u(X9, ST, 56); + if (sizeof J == 1) E(0x39400129); else ldr_u(X9, X9, 0); /* ldrb w9, [x9] */ + str_pre(X9, SP, -4); + return; + case 12: ldr_post(W0, SP, 4); E(0x13001c00); ldrx_u(X11, ST, 48); blr(X11); return; /* sxtb w0, w0 */ + } + ldr_post(X9, SP, 4); + ldr_u(X10, SP, 0); + switch (k) { + case 13: op3(0x0b000000, X10, X10, X9); break; + case 14: op3(0x4b000000, X10, X10, X9); break; + case 15: op3(0x1b007c00, X10, X10, X9); break; + case 16: op3(0x0a000000, X10, X10, X9); break; + case 17: op3(0x2a000000, X10, X10, X9); break; + case 18: op3(0x4a000000, X10, X10, X9); break; + case 19: op3(0x1ac02800, X10, X10, X9); break; + default: E(0x6b09015f); E(0x1a9fa7ea); /* cmp w10, w9; cset w10, lt */ + } + str_u(X10, SP, 0); +} + +/* an enhancement, inlined: inputs from the data stack into w0..w15, the + optimized micro-ops, outputs back. x16 and x17 are scratch. */ +static void emit_enh(int s) +{ + Enh *e = &enh[s]; + signed char *r = e->reg; + int i, j, delta; + if (!r) { /* didn't fit in registers: x0 = data stack, w1 = slot, call the interpreter */ + E(0xaa1403e0); li(W1, s); ldrx_u(X11, ST, 80); blr(X11); E(0xaa0003f4); + return; + } + for (j = 0; j < e->nin; j++) if (r[j] >= 0) ldr_u(r[j], SP, 4 * (e->nin - 1 - j)); + for (i = 0; i < e->n; i++) { + Uop *u = &e->u[i]; + int d = u->op == U_ST ? 0 : r[u->d], a = r[u->a], b = r[u->b], c = r[u->c]; + switch (u->op) { + case U_LI: li(d, u->imm); break; + case U_ADD: op3(0x0b000000, d, a, b); break; + case U_SUB: op3(0x4b000000, d, a, b); break; + case U_MUL: op3(0x1b007c00, d, a, b); break; + case U_SDIV: op3(0x1ac00c00, d, a, b); break; + case U_UDIV: op3(0x1ac00800, d, a, b); break; + case U_SREM: op3(0x1ac00c00, 16, a, b); E(0x1b008000 | b << 16 | a << 10 | 16 << 5 | d); break; /* msub */ + case U_UREM: op3(0x1ac00800, 16, a, b); E(0x1b008000 | b << 16 | a << 10 | 16 << 5 | d); break; + case U_AND: op3(0x0a000000, d, a, b); break; + case U_OR: op3(0x2a000000, d, a, b); break; + case U_XOR: op3(0x4a000000, d, a, b); break; + case U_SHL: op3(0x1ac02000, d, a, b); break; + case U_SHR: op3(0x1ac02400, d, a, b); break; + case U_SAR: op3(0x1ac02800, d, a, b); break; + case U_NEG: E(0x4b0003e0 | a << 16 | d); break; + case U_NOT: E(0x2a2003e0 | a << 16 | d); break; + case U_LT: E(0x6b00001f | b << 16 | a << 5); E(0x1a9fa7e0 | d); break; /* cmp; cset lt */ + case U_LTU: E(0x6b00001f | b << 16 | a << 5); E(0x1a9f27e0 | d); break; /* cset lo */ + case U_EQ: E(0x6b00001f | b << 16 | a << 5); E(0x1a9f17e0 | d); break; + case U_NE: E(0x6b00001f | b << 16 | a << 5); E(0x1a9f07e0 | d); break; + case U_MIN: E(0x6b00001f | b << 16 | a << 5); E(0x1a800000 | b << 16 | 11 << 12 | a << 5 | d); break; + case U_MAX: E(0x6b00001f | b << 16 | a << 5); E(0x1a800000 | b << 16 | 12 << 12 | a << 5 | d); break; + case U_MINU: E(0x6b00001f | b << 16 | a << 5); E(0x1a800000 | b << 16 | 3 << 12 | a << 5 | d); break; + case U_MAXU: E(0x6b00001f | b << 16 | a << 5); E(0x1a800000 | b << 16 | 8 << 12 | a << 5 | d); break; + case U_MULHS: E(0x9b207c00 | b << 16 | a << 5 | 16); E(0xd360fc00 | 16 << 5 | d); break; /* smull; lsr #32 */ + case U_MULHU: E(0x9ba07c00 | b << 16 | a << 5 | 16); E(0xd360fc00 | 16 << 5 | d); break; + case U_CLZ: E(0x5ac01000 | a << 5 | d); break; + case U_ABS: E(0x7100001f | a << 5); E(0x5a800400 | a << 16 | 10 << 12 | a << 5 | d); break; /* cmp #0; cneg */ + case U_SEL: E(0x7100001f | a << 5); E(0x1a800000 | c << 16 | 1 << 12 | b << 5 | d); break; /* csel ne */ + case U_LD: /* out of range reads 0, like the interpreter */ + E(0x52800011); E(0x7144001f | a << 5); E(0x54000042); ldr_rr(17, M, a, UXTW); movw(d, 17); + break; + case U_ST: E(0x7144001f | a << 5); E(0x54000042); str_rr(b, M, a, UXTW); break; + } + } + delta = 4 * (e->nin - e->nout); + if (delta > 0) E(0x91000294 | delta << 10); + if (delta < 0) E(0xd1000294 | (-delta) << 10); + for (j = 0; j < e->nout; j++) str_u(r[e->out[j]], SP, 4 * (e->nout - 1 - j)); +} +static int inst_hook(int d) { return install(d); } +static long xrun(long sp, int slot) { return (long)xexec(&enh[slot], (int *)sp); } + +static void *translate(int p) +{ + unsigned *start; + int i = p, n = 0, w, k, t; + if (cp - code > CODEW - 65536 || nbl >= MAXB) flush(); + start = cp; + for (;;) { + if (i >= MW) { E(0x12800000); b_to(x_exit); break; } /* movn w0, #0: halt */ + if (n++ >= BLKW) { li(W0, i); b_to(x_disp); break; } + w = m[i]; + cov[i] = 1; + if (w & 1 && i + 1 < MW && !(m[i + 1] & 1) && (k = m[i + 1] / 2) >= 6 && k <= 8) { + t = (w - 1) / 2; /* literal + call/jmp/jz with a known target */ + cov[i + 1] = 1; + if (k == 8) { + unsigned *a; + ldr_post(X10, SP, 4); + a = cp; E(0x3500000a); /* cbnz w10, fallthrough */ + li(W0, t); b_to(x_disp); + cb_fix(a, cp); + i += 2; + continue; + } + li(X10, i + 2); + if (k == 6) str_post(X10, RP, 4); else str_u(X10, RP, 0); + li(W0, t); b_to(x_disp); + i += 2; + break; + } + if (w & 1) { li(X9, (w - 1) / 2); str_pre(X9, SP, -4); i++; continue; } + k = w / 2; + if (k == 0) { E(0x12800000); b_to(x_exit); i++; break; } + if (k < 0 || k == 6 || k == 7) { + ldr_post(W0, SP, 4); + li(X10, i + 1); + if (k == 6) str_post(X10, RP, 4); else str_u(X10, RP, 0); + if (k < 0) { li(X11, 7 - k); addx_ext(RP, RP, X11, SXTW); } + b_to(x_disp); + i++; + break; + } + if (k == 8) { /* jz: taken leaves, not taken keeps going */ + ldp_post(X9, X10, SP, 8); + E(0x3500006a); /* cbnz w10, +3 */ + movw(W0, X9); + b_to(x_disp); + i++; + continue; + } + if (k == 9) { ldr_pre(W0, RP, -4); b_to(x_disp); i++; break; } + if (k == 21) { /* install: call into c, then return to it so it can flush */ + ldr_u(W0, SP, 0); ldrx_u(X11, ST, 72); blr(X11); str_u(W0, SP, 0); + li(W0, i + 1); b_to(x_exit); i++; break; + } + if (k >= XOP0 && k < XOP0 + NENH && enh[k - XOP0].live) { emit_enh(k - XOP0); i++; continue; } + if (k > 20) { E(0x12800000); b_to(x_exit); i++; break; } /* not wired: halt */ + op(i, k); + i++; + } + bl[nbl].a = p; + bl[nbl++].b = i; + tab[p] = start; + syncicache(start, cp); + if (trace) fprintf(stderr, "jit: %d..%d -> %ld bytes\n", p, i - 1, (long)((char *)cp - (char *)start)); + return start; +} + +#else +/* portable path for hosts without the arm64 jit: a plain interpreter with + the same semantics (shift counts wrap at 32, stores outside memory are + ignored, loads outside memory read 0, leaving memory halts). */ +#ifdef ZYLLVM +/* ---- llvm backend ---- + a tiered jit for hosts the arm64 backend doesn't cover. the interpreter + counts how often each block start is reached; a hot block is translated to + llvm ir (same block rules as the arm64 jit: it ends at call, jump, ret or + halt, and keeps going past a jz), optimized at O2 and compiled with orc. + enhancements are inlined as ir. stores into compiled words drop the blocks + that cover them, so self-modifying code still works. */ +#include +#include +#include +#include +#include +#include +static int HOT = 5000; /* runs before a block gets compiled: ZYHOT */ +static const char *lpasses = "default"; /* ZYPASSES */ +#define LMAXB 65536 +typedef int (*Blk)(int *mem, int *regs); +static Blk lt[MW]; /* address -> compiled block */ +static unsigned short hot[MW]; +static unsigned char lcov[MW]; /* 1 if a compiled block covers the word */ +static struct { int a, b; } lbl[LMAXB]; +static int nlbl, llvm_ok, nblocks; +static LLVMOrcLLJITRef jit; +static LLVMOrcThreadSafeContextRef tsc; +static LLVMContextRef ctx; +static LLVMTypeRef i8t, i32t, i64t, ptrt, i1t; +static LLVMBuilderRef bd; +static LLVMValueRef fn_, vm_, vregs, vs, vr; /* function, memory, regs, s and r slots */ +static LLVMModuleRef mod_; + +static void ldrop_all(void) +{ + memset(lt, 0, sizeof lt); memset(lcov, 0, sizeof lcov); memset(hot, 0, sizeof hot); nlbl = 0; + if (trace) fprintf(stderr, "llvm: dropped every block\n"); +} +/* a store to a word some block covers: write it, drop those blocks */ +static int lhook(int a, int v) +{ + int j, x, hit = 0; + if (m[a] == v) return 0; + m[a] = v; + for (j = 0; j < nlbl; j++) + if (lbl[j].a >= 0 && lbl[j].a <= a && a < lbl[j].b) { + lt[lbl[j].a] = 0; hot[lbl[j].a] = 0; + if (trace) fprintf(stderr, "llvm: dropped %d..%d\n", lbl[j].a, lbl[j].b - 1); + lbl[j].a = -1; hit = 1; + } + if (hit) { + memset(lcov, 0, sizeof lcov); + for (j = 0; j < nlbl; j++) if (lbl[j].a >= 0) for (x = lbl[j].a; x < lbl[j].b; x++) lcov[x] = 1; + } + return hit; +} +static int linstall(int d) { return install(d); } + +static LLVMValueRef K32(int v) { return LLVMConstInt(i32t, (unsigned)v, 0); } +static LLVMValueRef cptr(void *p) { return LLVMConstIntToPtr(LLVMConstInt(i64t, (unsigned long long)(size_t)p, 0), ptrt); } +static LLVMValueRef mslot(LLVMValueRef idx) { return LLVMBuildGEP2(bd, i32t, vm_, &idx, 1, ""); } +static LLVMValueRef ld(LLVMValueRef idx) { return LLVMBuildLoad2(bd, i32t, mslot(idx), ""); } +static void st(LLVMValueRef idx, LLVMValueRef v) { LLVMBuildStore(bd, v, mslot(idx)); } +static LLVMValueRef gets(void) { return LLVMBuildLoad2(bd, i32t, vs, ""); } +static void sets(LLVMValueRef v) { LLVMBuildStore(bd, v, vs); } +static LLVMValueRef add(LLVMValueRef a, int k) { return LLVMBuildAdd(bd, a, K32(k), ""); } +/* a load that reads 0 outside memory */ +static LLVMValueRef safeld(LLVMValueRef a) +{ + LLVMValueRef in = LLVMBuildICmp(bd, LLVMIntULT, a, K32(MW), ""); + LLVMValueRef v = ld(LLVMBuildSelect(bd, in, a, K32(0), "")); + return LLVMBuildSelect(bd, in, v, K32(0), ""); +} +static void push(LLVMValueRef v) { LLVMValueRef s = add(gets(), -1); sets(s); st(s, v); } +/* leave the block: write s and r back, return the next vm address */ +static void leave(LLVMValueRef next) +{ + LLVMValueRef r0 = K32(0), r1 = K32(1); + LLVMBuildStore(bd, gets(), LLVMBuildGEP2(bd, i32t, vregs, &r0, 1, "")); + LLVMBuildStore(bd, LLVMBuildLoad2(bd, i32t, vr, ""), LLVMBuildGEP2(bd, i32t, vregs, &r1, 1, "")); + LLVMBuildRet(bd, next); +} +static LLVMBasicBlockRef newbb(void) { return LLVMAppendBasicBlockInContext(ctx, fn_, ""); } +static LLVMValueRef callp(void *f, LLVMTypeRef ret, LLVMTypeRef *at, int n, LLVMValueRef *args) +{ + LLVMTypeRef ft = LLVMFunctionType(ret, at, (unsigned)n, 0); + return LLVMBuildCall2(bd, ft, cptr(f), args, (unsigned)n, ""); +} +/* an enhancement as ir: the same semantics as ev() */ +static void ir_enh(Enh *e) +{ + LLVMValueRef *v = calloc(e->nv + 1, sizeof *v), s = gets(), z = K32(0), q, d, t, c1, c2; + int i, j; + LLVMTypeRef i64 = i64t; + for (j = 0; j < e->nin; j++) v[j] = ld(add(s, e->nin - 1 - j)); + for (i = 0; i < e->n; i++) { + Uop *u = &e->u[i]; + LLVMValueRef a = u->op == U_LI ? 0 : v[u->a], b = uarity[u->op] > 1 || u->op == U_ST ? v[u->b] : 0, c = uarity[u->op] > 2 ? v[u->c] : 0, r = 0; + switch (u->op) { + case U_LI: r = K32(u->imm); break; + case U_ADD: r = LLVMBuildAdd(bd, a, b, ""); break; + case U_SUB: r = LLVMBuildSub(bd, a, b, ""); break; + case U_MUL: r = LLVMBuildMul(bd, a, b, ""); break; + case U_SDIV: case U_SREM: /* /0 and INT_MIN/-1 are defined, like the cpu */ + c1 = LLVMBuildICmp(bd, LLVMIntEQ, b, z, ""); + c2 = LLVMBuildAnd(bd, LLVMBuildICmp(bd, LLVMIntEQ, a, K32((int)0x80000000), ""), LLVMBuildICmp(bd, LLVMIntEQ, b, K32(-1), ""), ""); + d = LLVMBuildSelect(bd, LLVMBuildOr(bd, c1, c2, ""), K32(1), b, ""); + if (u->op == U_SDIV) { q = LLVMBuildSDiv(bd, a, d, ""); r = LLVMBuildSelect(bd, c1, z, q, ""); } + else { q = LLVMBuildSRem(bd, a, d, ""); r = LLVMBuildSelect(bd, c1, a, LLVMBuildSelect(bd, c2, z, q, ""), ""); } + break; + case U_UDIV: case U_UREM: + c1 = LLVMBuildICmp(bd, LLVMIntEQ, b, z, ""); + d = LLVMBuildSelect(bd, c1, K32(1), b, ""); + if (u->op == U_UDIV) r = LLVMBuildSelect(bd, c1, z, LLVMBuildUDiv(bd, a, d, ""), ""); + else r = LLVMBuildSelect(bd, c1, a, LLVMBuildURem(bd, a, d, ""), ""); + break; + case U_AND: r = LLVMBuildAnd(bd, a, b, ""); break; + case U_OR: r = LLVMBuildOr(bd, a, b, ""); break; + case U_XOR: r = LLVMBuildXor(bd, a, b, ""); break; + case U_SHL: r = LLVMBuildShl(bd, a, LLVMBuildAnd(bd, b, K32(31), ""), ""); break; + case U_SHR: r = LLVMBuildLShr(bd, a, LLVMBuildAnd(bd, b, K32(31), ""), ""); break; + case U_SAR: r = LLVMBuildAShr(bd, a, LLVMBuildAnd(bd, b, K32(31), ""), ""); break; + case U_NEG: r = LLVMBuildSub(bd, z, a, ""); break; + case U_NOT: r = LLVMBuildNot(bd, a, ""); break; + case U_LT: r = LLVMBuildZExt(bd, LLVMBuildICmp(bd, LLVMIntSLT, a, b, ""), i32t, ""); break; + case U_LTU: r = LLVMBuildZExt(bd, LLVMBuildICmp(bd, LLVMIntULT, a, b, ""), i32t, ""); break; + case U_EQ: r = LLVMBuildZExt(bd, LLVMBuildICmp(bd, LLVMIntEQ, a, b, ""), i32t, ""); break; + case U_NE: r = LLVMBuildZExt(bd, LLVMBuildICmp(bd, LLVMIntNE, a, b, ""), i32t, ""); break; + case U_MIN: r = LLVMBuildSelect(bd, LLVMBuildICmp(bd, LLVMIntSLT, a, b, ""), a, b, ""); break; + case U_MAX: r = LLVMBuildSelect(bd, LLVMBuildICmp(bd, LLVMIntSGT, a, b, ""), a, b, ""); break; + case U_MINU: r = LLVMBuildSelect(bd, LLVMBuildICmp(bd, LLVMIntULT, a, b, ""), a, b, ""); break; + case U_MAXU: r = LLVMBuildSelect(bd, LLVMBuildICmp(bd, LLVMIntUGT, a, b, ""), a, b, ""); break; + case U_MULHS: case U_MULHU: { + LLVMValueRef x = u->op == U_MULHS ? LLVMBuildSExt(bd, a, i64, "") : LLVMBuildZExt(bd, a, i64, ""); + LLVMValueRef y = u->op == U_MULHS ? LLVMBuildSExt(bd, b, i64, "") : LLVMBuildZExt(bd, b, i64, ""); + r = LLVMBuildTrunc(bd, LLVMBuildLShr(bd, LLVMBuildMul(bd, x, y, ""), LLVMConstInt(i64, 32, 0), ""), i32t, ""); + break; } + case U_CLZ: { + unsigned id = LLVMLookupIntrinsicID("llvm.ctlz", 9); + LLVMValueRef f = LLVMGetIntrinsicDeclaration(mod_, id, &i32t, 1), args[2]; + args[0] = a; args[1] = LLVMConstInt(i1t, 0, 0); + r = LLVMBuildCall2(bd, LLVMIntrinsicGetType(ctx, id, &i32t, 1), f, args, 2, ""); + break; } + case U_ABS: t = LLVMBuildICmp(bd, LLVMIntSLT, a, z, ""); r = LLVMBuildSelect(bd, t, LLVMBuildSub(bd, z, a, ""), a, ""); break; + case U_SEL: r = LLVMBuildSelect(bd, LLVMBuildICmp(bd, LLVMIntNE, a, z, ""), b, c, ""); break; + case U_LD: r = safeld(a); break; + case U_ST: { + LLVMBasicBlockRef yes = newbb(), done = newbb(); + LLVMBuildCondBr(bd, LLVMBuildICmp(bd, LLVMIntULT, a, K32(MW), ""), yes, done); + LLVMPositionBuilderAtEnd(bd, yes); st(a, b); LLVMBuildBr(bd, done); + LLVMPositionBuilderAtEnd(bd, done); + break; } + } + if (u->op != U_ST) v[u->d] = r; + } + s = add(s, e->nin - e->nout); sets(s); + for (j = 0; j < e->nout; j++) st(add(s, e->nout - 1 - j), v[e->out[j]]); + free(v); +} +static void llvm_init(void) +{ + LLVMErrorRef err; + if (getenv("ZYHOT")) HOT = atoi(getenv("ZYHOT")); + if (HOT < 1) HOT = 1; + if (HOT > 65000) HOT = 65000; + if (getenv("ZYPASSES")) lpasses = getenv("ZYPASSES"); + LLVMInitializeNativeTarget(); LLVMInitializeNativeAsmPrinter(); + if ((err = LLVMOrcCreateLLJIT(&jit, 0))) { char *msg = LLVMGetErrorMessage(err); fprintf(stderr, "llvm: %s\n", msg); LLVMDisposeErrorMessage(msg); return; } + tsc = LLVMOrcCreateNewThreadSafeContext(); + ctx = LLVMOrcThreadSafeContextGetContext(tsc); + i1t = LLVMInt1TypeInContext(ctx); i8t = LLVMInt8TypeInContext(ctx); i32t = LLVMInt32TypeInContext(ctx); + i64t = LLVMInt64TypeInContext(ctx); ptrt = LLVMPointerTypeInContext(ctx, 0); + bd = LLVMCreateBuilderInContext(ctx); + llvm_ok = 1; +} +/* compile the block at p; 0 if it couldn't be */ +static Blk lcompile(int p) +{ + char name[32]; LLVMTypeRef pt[2], ft; LLVMValueRef s, a, b, v, r, args[2]; LLVMTypeRef at[2]; + LLVMOrcExecutorAddress addr; LLVMErrorRef err; LLVMPassBuilderOptionsRef po; + int i = p, n = 0, w, k, t, done = 0; + if (!llvm_ok || nlbl >= LMAXB) return 0; + sprintf(name, "b%d_%d", p, nblocks++); + mod_ = LLVMModuleCreateWithNameInContext(name, ctx); + pt[0] = ptrt; pt[1] = ptrt; + ft = LLVMFunctionType(i32t, pt, 2, 0); + fn_ = LLVMAddFunction(mod_, name, ft); + vm_ = LLVMGetParam(fn_, 0); vregs = LLVMGetParam(fn_, 1); + LLVMPositionBuilderAtEnd(bd, newbb()); + vs = LLVMBuildAlloca(bd, i32t, "s"); vr = LLVMBuildAlloca(bd, i32t, "r"); + { LLVMValueRef r0 = K32(0), r1 = K32(1); + sets(LLVMBuildLoad2(bd, i32t, LLVMBuildGEP2(bd, i32t, vregs, &r0, 1, ""), "")); + LLVMBuildStore(bd, LLVMBuildLoad2(bd, i32t, LLVMBuildGEP2(bd, i32t, vregs, &r1, 1, ""), ""), vr); } + while (!done) { + if (i >= MW) { leave(K32(MW)); break; } + if (n++ >= 1024) { leave(K32(i)); break; } + w = m[i]; + lcov[i] = 1; + if (w & 1) { + /* a literal followed by call, jump or jz: a known target */ + if (i + 1 < MW && !(m[i + 1] & 1) && (k = m[i + 1] / 2) >= 6 && k <= 8) { + t = w >> 1; lcov[i + 1] = 1; + if (k == 8) { + LLVMBasicBlockRef tk = newbb(), go = newbb(); + s = gets(); a = ld(s); sets(add(s, 1)); + LLVMBuildCondBr(bd, LLVMBuildICmp(bd, LLVMIntEQ, a, K32(0), ""), tk, go); + LLVMPositionBuilderAtEnd(bd, tk); leave(K32(t)); + LLVMPositionBuilderAtEnd(bd, go); + i += 2; continue; + } + if (k == 6) { r = LLVMBuildLoad2(bd, i32t, vr, ""); st(r, K32(i + 2)); LLVMBuildStore(bd, add(r, 1), vr); } + leave(K32(t)); i += 2; break; + } + push(K32(w >> 1)); i++; continue; + } + k = w / 2; + s = gets(); + switch (k) { + case 0: leave(K32(MW)); done = 1; break; + case 1: st(s, safeld(ld(s))); break; + case 2: { + LLVMBasicBlockRef in = newbb(), cv = newbb(), plain = newbb(), out = newbb(), quit = newbb(); + a = ld(s); b = ld(add(s, 1)); sets(add(s, 2)); + LLVMBuildCondBr(bd, LLVMBuildICmp(bd, LLVMIntULT, a, K32(MW), ""), in, out); + LLVMPositionBuilderAtEnd(bd, in); + v = LLVMBuildLoad2(bd, i8t, LLVMBuildGEP2(bd, i8t, cptr(lcov), &a, 1, ""), ""); + LLVMBuildCondBr(bd, LLVMBuildICmp(bd, LLVMIntNE, v, LLVMConstInt(i8t, 0, 0), ""), cv, plain); + LLVMPositionBuilderAtEnd(bd, plain); st(a, b); LLVMBuildBr(bd, out); + LLVMPositionBuilderAtEnd(bd, cv); + at[0] = i32t; at[1] = i32t; args[0] = a; args[1] = b; + v = callp((void *)lhook, i32t, at, 2, args); + LLVMBuildCondBr(bd, LLVMBuildICmp(bd, LLVMIntNE, v, K32(0), ""), quit, out); + LLVMPositionBuilderAtEnd(bd, quit); leave(K32(i + 1)); /* a block died: back to the loop */ + LLVMPositionBuilderAtEnd(bd, out); + break; } + case 3: a = ld(s); st(s, safeld(LLVMBuildAdd(bd, add(s, 1), a, ""))); break; + case 4: sets(add(s, 1)); break; + case 5: a = ld(s); b = ld(add(s, 1)); st(s, b); st(add(s, 1), a); break; + case 6: case 7: + a = ld(s); sets(add(s, 1)); + if (k == 6) { r = LLVMBuildLoad2(bd, i32t, vr, ""); st(r, K32(i + 1)); LLVMBuildStore(bd, add(r, 1), vr); } + leave(a); done = 1; break; + case 8: { + LLVMBasicBlockRef tk = newbb(), go = newbb(); + a = ld(s); b = ld(add(s, 1)); sets(add(s, 2)); + LLVMBuildCondBr(bd, LLVMBuildICmp(bd, LLVMIntEQ, b, K32(0), ""), tk, go); + LLVMPositionBuilderAtEnd(bd, tk); leave(a); + LLVMPositionBuilderAtEnd(bd, go); + break; } + case 9: + r = add(LLVMBuildLoad2(bd, i32t, vr, ""), -1); LLVMBuildStore(bd, r, vr); + leave(ld(r)); done = 1; break; + case 10: v = callp((void *)Q, i8t, 0, 0, 0); push(LLVMBuildSExt(bd, v, i32t, "")); break; + case 11: + if (sizeof J == 1) push(LLVMBuildZExt(bd, LLVMBuildLoad2(bd, i8t, cptr(&J), ""), i32t, "")); + else push(LLVMBuildLoad2(bd, i32t, cptr(&J), "")); + break; + case 12: at[0] = i8t; args[0] = LLVMBuildTrunc(bd, ld(s), i8t, ""); callp((void *)G, LLVMVoidTypeInContext(ctx), at, 1, args); sets(add(s, 1)); break; + case 13: case 14: case 15: case 16: case 17: case 18: case 19: case 20: + a = ld(s); b = ld(add(s, 1)); s = add(s, 1); sets(s); + switch (k) { + case 13: v = LLVMBuildAdd(bd, b, a, ""); break; + case 14: v = LLVMBuildSub(bd, b, a, ""); break; + case 15: v = LLVMBuildMul(bd, b, a, ""); break; + case 16: v = LLVMBuildAnd(bd, b, a, ""); break; + case 17: v = LLVMBuildOr(bd, b, a, ""); break; + case 18: v = LLVMBuildXor(bd, b, a, ""); break; + case 19: v = LLVMBuildAShr(bd, b, LLVMBuildAnd(bd, a, K32(31), ""), ""); break; + default: v = LLVMBuildZExt(bd, LLVMBuildICmp(bd, LLVMIntSLT, b, a, ""), i32t, ""); + } + st(s, v); break; + case 21: /* install: then back to the loop, which drops every block */ + at[0] = i32t; args[0] = ld(s); + st(s, callp((void *)linstall, i32t, at, 1, args)); + leave(K32(i + 1)); done = 1; break; + default: + if (k >= XOP0 && k < XOP0 + NENH && enh[k - XOP0].live) { ir_enh(&enh[k - XOP0]); break; } + leave(K32(MW)); done = 1; /* not wired: halt */ + } + i++; + } + lbl[nlbl].a = p; lbl[nlbl++].b = i; + po = LLVMCreatePassBuilderOptions(); + if ((err = LLVMRunPasses(mod_, lpasses, 0, po))) { LLVMConsumeError(err); } + LLVMDisposePassBuilderOptions(po); + if ((err = LLVMOrcLLJITAddLLVMIRModule(jit, LLVMOrcLLJITGetMainJITDylib(jit), LLVMOrcCreateNewThreadSafeModule(mod_, tsc)))) { + char *msg = LLVMGetErrorMessage(err); fprintf(stderr, "llvm: %s\n", msg); LLVMDisposeErrorMessage(msg); return 0; + } + if ((err = LLVMOrcLLJITLookup(jit, &addr, name))) { char *msg = LLVMGetErrorMessage(err); fprintf(stderr, "llvm: %s\n", msg); LLVMDisposeErrorMessage(msg); return 0; } + if (trace) fprintf(stderr, "llvm: %d..%d compiled\n", p, i - 1); + return (Blk)(size_t)addr; +} +#endif +#define LD(x) ((unsigned)(x) < MW ? m[x] : 0) +static int run(int p) +{ + int s = DS0, r = RS0, w, a, b, xfer = 1; + unsigned long long steps = 0; +#ifdef ZYLLVM + int regs[2]; + if (!getenv("ZYNOLLVM")) llvm_init(); +#endif + while ((unsigned)p < MW) { + if (xfer && (s < DSLIM || r > RSLIM)) { p = s < DSLIM ? MW + 1 : MW + 2; break; } +#ifdef ZYLLVM + if (xfer && llvm_ok) { /* at a block start: run it compiled, or count it */ + xfer = 0; + if (!lt[p] && hot[p] < 65535 && ++hot[p] == HOT && !(lt[p] = lcompile(p))) hot[p] = 65535; + if (lt[p]) { + regs[0] = s; regs[1] = r; + p = lt[p](m, regs); + s = regs[0]; r = regs[1]; + if (need_flush) { need_flush = 0; ldrop_all(); } + xfer = 1; + continue; + } + } +#endif + w = m[p++]; + steps++; + if (w & 1) { m[--s] = w >> 1; continue; } + a = m[s]; b = m[s + 1]; + switch (w / 2) { + case 1: m[s] = LD(a); break; + case 2: + if ((unsigned)a < MW) { +#ifdef ZYLLVM + if (lcov[a]) lhook(a, b); else +#endif + m[a] = b; + } + s += 2; break; + case 3: m[s] = LD(s + 1 + a); break; + case 4: s++; break; + case 5: m[s] = b; m[s + 1] = a; break; + case 6: m[r++] = p; p = a; s++; xfer = 1; break; + case 7: p = a; s++; xfer = 1; break; + case 8: if (!b) { p = a; xfer = 1; } s += 2; break; + case 9: p = m[--r]; xfer = 1; break; + case 10: m[--s] = (signed char)Q(); break; + case 11: m[--s] = J; break; + case 12: G((char)a); s++; break; + case 13: m[++s] = b + a; break; + case 14: m[++s] = (int)((unsigned)b - (unsigned)a); break; + case 15: m[++s] = (int)((unsigned)b * (unsigned)a); break; + case 16: m[++s] = b & a; break; + case 17: m[++s] = b | a; break; + case 18: m[++s] = b ^ a; break; + case 19: m[++s] = b >> (a & 31); break; + case 20: m[++s] = b < a; break; + case 21: + m[s] = install(a); +#ifdef ZYLLVM + if (need_flush) { need_flush = 0; ldrop_all(); } +#endif + break; + default: + if (w / 2 >= XOP0 && w / 2 < XOP0 + NENH && enh[w / 2 - XOP0].live) s = (int)(xexec(&enh[w / 2 - XOP0], m + s) - m); + else p = MW; /* halt: unwired opcodes and the reserved negative ones */ + } + } + if (trace) fprintf(stderr, "interp: %llu steps\n", steps); + return stopped(p); +} +#endif + +int main(void) +{ + trace = getenv("ZYTRACE") != 0; + memcpy(m + GORG, genome, sizeof genome); +#ifdef ZYJIT + { + int p = GORG; + union { unsigned *u; int (*f)(long *, void *); } enter; +#ifdef __APPLE__ + code = mmap(0, CODEW * 4, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANON | MAP_JIT, -1, 0); +#else + code = mmap(0, CODEW * 4, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); +#endif + if (code == MAP_FAILED) { perror("mmap"); return 1; } + jitwrite(1); + build_stubs(); + jitwrite(0); + syncicache(code, cp); + S[0] = (long)m; S[1] = DS0; S[2] = RS0; S[3] = (long)tab; S[4] = (long)cov; + S[5] = (long)Q; S[6] = (long)G; S[7] = (long)&J; S[8] = (long)hook; + S[9] = (long)inst_hook; S[10] = (long)xrun; + S[11] = (long)(m + DSLIM); S[12] = (long)(m + RSLIM); + enter.u = x_enter; + while ((unsigned)p < MW) { + void *c = tab[p]; + if (!c) { jitwrite(1); c = translate(p); jitwrite(0); } + p = enter.f(S, c); + if (need_flush) { need_flush = 0; jitwrite(1); flush(); jitwrite(0); } + } + return stopped(p); + } +#else + return run(GORG); +#endif + return 0; +} diff --git a/docs/history/13-gap-fixes/code/examples/echo.zc b/docs/history/13-gap-fixes/code/examples/echo.zc new file mode 100644 index 0000000..01f28b0 --- /dev/null +++ b/docs/history/13-gap-fixes/code/examples/echo.zc @@ -0,0 +1,2 @@ +// everything after the backtick is left unread, for the program itself +fn main() { while (!eof()) putc(getc()); return 0; } diff --git a/docs/history/13-gap-fixes/code/examples/fact.zc b/docs/history/13-gap-fixes/code/examples/fact.zc new file mode 100644 index 0000000..2a218b2 --- /dev/null +++ b/docs/history/13-gap-fixes/code/examples/fact.zc @@ -0,0 +1,4 @@ +fn fact(n) { if (n < 2) return 1; return n * fact(n - 1); } +// prints in binary, like the old asm example +fn bin(n) { if (n > 1) bin(n >> 1); putc('0' + (n & 1)); return 0; } +fn main() { bin(fact(10)); putc(10); putn(fact(10)); putc(10); return 0; } diff --git a/docs/history/13-gap-fixes/code/examples/hello.zc b/docs/history/13-gap-fixes/code/examples/hello.zc new file mode 100644 index 0000000..f36a810 --- /dev/null +++ b/docs/history/13-gap-fixes/code/examples/hello.zc @@ -0,0 +1 @@ +fn main() { puts("hello, world!\n"); return 0; } diff --git a/docs/history/13-gap-fixes/code/examples/lists.zc b/docs/history/13-gap-fixes/code/examples/lists.zc new file mode 100644 index 0000000..6ac276e --- /dev/null +++ b/docs/history/13-gap-fixes/code/examples/lists.zc @@ -0,0 +1,35 @@ +// ownership in practice: a list owns its boxes. nothing here calls free; +// everything is released at the end of the block that owns it. +fn box(v) -> .u { let b = new(1); b[0] = v; return u*b; } +fn mklist(cap) -> .u { let l = new(cap + 1); l[0] = 0; return u*l; } +// item m*arrives (it was given down), and goes d*on into the borrowed list +fn push(.m l, .d item) { l[l[0] + 1] = d*item; l[0] = l[0] + 1; return 0; } +fn show(.m l) +{ + let i = 1; + putc('['); + while (i <= l[0]) { if (i > 1) putc(' '); putn(l[i][0]); i++; } + putc(']'); + return 0; +} +fn squares(n) -> .u +{ + let l = mklist(n), i = 0; + while (i < n) { + let b = box(i * i); + push(l, d*b); + i++; + } + return u*l; +} +fn main() +{ + let before = live_objects(); + { + let l = squares(6); + show(l); putc(10); + puts("live: "); putn(live_objects() - before); putc(10); + } + puts("after the block: "); putn(live_objects() - before); putc(10); + return 0; +} diff --git a/docs/history/13-gap-fixes/code/examples/repl.zc b/docs/history/13-gap-fixes/code/examples/repl.zc new file mode 100644 index 0000000..b7b81b8 --- /dev/null +++ b/docs/history/13-gap-fixes/code/examples/repl.zc @@ -0,0 +1,27 @@ +// a repl on the resident compiler: each backtick-separated chunk of input is +// compiled as a function body and run. definitions (fn ...) can be loaded too: +// a chunk that starts with "fn" goes through load instead of eval. +let buf[4000]; +fn readchunk() +{ + let n = 0, c; + while (!eof()) { + c = getc(); + if (c == 96) break; + if (n < 3999) { buf[n] = c; n++; } + } + buf[n] = 0; + return n; +} +fn main() +{ + let q, i; + while (!eof()) { + readchunk(); + q = tokens(buf); + if (q[0] && q[1] == T_ID && q[2] == intern("fn")) load(q); + else eval(q); + free(d*q); + } + return 0; +} diff --git a/docs/history/13-gap-fixes/code/examples/selfpatch.zc b/docs/history/13-gap-fixes/code/examples/selfpatch.zc new file mode 100644 index 0000000..074ccec --- /dev/null +++ b/docs/history/13-gap-fixes/code/examples/selfpatch.zc @@ -0,0 +1,17 @@ +// code is data: find the literal 'A' inside f's compiled words and rewrite it. +// on the jit the store lands on translated code, which gets dropped and +// retranslated on the next call. +fn f() { putc('A'); return 0; } +fn litw(v) { return v + v + 1; } // how the vm encodes a literal push +fn main() +{ + let p = f; + f(); + while (*p != litw('A')) p++; + *p = litw('B'); + f(); + *p = litw('C'); + f(); + putc(10); + return 0; +} diff --git a/docs/history/13-gap-fixes/code/examples/typed.zc b/docs/history/13-gap-fixes/code/examples/typed.zc new file mode 100644 index 0000000..d91fe6a --- /dev/null +++ b/docs/history/13-gap-fixes/code/examples/typed.zc @@ -0,0 +1,47 @@ +// types: structs, a generic container, and looking at it all through reflection +typecheck on; +struct point { int x, y; }; +struct vec { int len, cap; *T items; }; + +fn mkvec(int cap) -> *vec.u +{ + *vec v = new(vec); + v.cap = cap; + v.items = new(T, cap); + return u*v; +} +// x is given away (.d): the vector's memory holds it from now on, and the +// collector looks after it +fn push(*vec v, T.d x) -> bool +{ + if (v.len == v.cap) { free(d*x); return 0; } + v.items[v.len] = d*x; + v.len++; + return 1; +} +fn show(word t) +{ + let i = 0; + tprint(t); puts(" is "); putn(tsize(t)); puts(" words: "); + while (i < tnfields(t)) { if (i) puts(", "); puts(tfname(t, i)); puts(" "); tprint(tftype(t, i)); i++; } + putc(10); + return 0; +} +fn main() -> int +{ + *vec<*point> v = mkvec<*point>(4); + int i = 0; + while (i < 3) { + *point p = new(point); + p.x = i; p.y = i * i; + push(v, d*p); + i++; + } + puts("last: "); putn(v.items[2].y); putc(10); + show(typeof(point)); + show(typeof(*v)); + puts("push is "); tprint(sigof(push)); putc(10); + // generic code is compiled once, so what mkvec stamped is vec + puts("the heap says v is a "); tprint(typeof_obj(v)); puts(", and v.items[0] is a "); tprint(typeof_obj(v.items[0])); putc(10); + return 0; +} diff --git a/docs/history/13-gap-fixes/code/examples/wiring.zc b/docs/history/13-gap-fixes/code/examples/wiring.zc new file mode 100644 index 0000000..ed30935 --- /dev/null +++ b/docs/history/13-gap-fixes/code/examples/wiring.zc @@ -0,0 +1,19 @@ +// virtual enhancements: custom opcodes wired into the cpu while compiling. +// the recipes run through the core's optimizer; the jit inlines the result. +enhance popcount(x) micro + "r1 = shr x 1; r1 = and r1 0x55555555; x = sub x r1" + "; r1 = shr x 2; r1 = and r1 0x33333333; x = and x 0x33333333; x = add x r1" + "; r1 = shr x 4; x = add x r1; x = and x 0x0f0f0f0f; x = mul x 0x01010101; x = shr x 24"; +enhance div10(x) micro "x = udiv x 10"; // becomes a multiply-high, no divide +enhance hypot2(a, b) vm { 0 $ * ~ 0 $ * + }; // lifted from vm code +enhance minmax(a, b) -> 2 micro "r2 = min a b; r1 = max a b; r0 = r2"; +let lo, hi; +fn main() +{ + putn(popcount(0xff00ff)); putc(' '); + putn(div10(12345)); putc(' '); + putn(hypot2(3, 4)); putc(' '); + asm { 9 4 minmax hi ! lo ! } + putn(lo); putc('/'); putn(hi); putc(10); + return 0; +} diff --git a/docs/history/13-gap-fixes/code/genome.h b/docs/history/13-gap-fixes/code/genome.h new file mode 100644 index 0000000..fecb1c9 --- /dev/null +++ b/docs/history/13-gap-fixes/code/genome.h @@ -0,0 +1,4443 @@ +#define GORG 786432 +static const int genome[] = { +1966081,1310721,4,1703963,14,852206,857446,1310721,2,3,28,1310721,4,1310721,2,1835009, +40,1572913,16,1966081,1310721,4,1579069,12,1310721,2,4,1310721,2,2,42,1572933, +14,1,1310721,2,3,26,1310721,4,18,0,0,2,1,786486,0,786480, +786482,786484,97,0,98,0,97,32,61,32,115,100,105,118,32,97, +32,98,0,1,0,2,1,786512,0,786506,786508,786510,97,0,98,0, +97,32,61,32,115,114,101,109,32,97,32,98,0,2,0,2, +1,786538,0,786532,786534,786536,97,0,98,0,97,32,61,32,115,104, +108,32,97,32,98,0,1310721,2,3,28,1310721,4,1310721,2,1835009,40, +1573135,16,1966081,1310721,4,1579069,12,1310721,2,4,1310721,2,2,24,1,1573157, +14,1,1310721,2,3,26,1310721,4,18,1310721,2,1,28,1310721,4,1310721, +2,1835009,40,1573205,16,1966081,1310721,4,1579069,12,20,1573213,14,1,1310721,2, +1,26,1310721,4,18,1310721,2,1,28,1310721,4,1310721,2,1835009,40,1573261, +16,1966081,1310721,4,1579069,12,22,1573269,14,1,1310721,2,1,26,1310721,4, +18,1310721,2,1,28,1310721,4,1310721,2,1835009,40,1573317,16,1966081,1310721,4, +1579069,12,0,1,1573327,14,1,1310721,2,1,26,1310721,4,18,1310721,2, +5,28,1310721,4,1310721,2,1835009,40,1573375,16,1966081,1310721,4,1579069,12,1310721, +2,4,1,1310721,2,3,26,4,1310721,2,2,1310721,2,3,26,2, +26,2,1573481,16,1310721,2,2,1310721,2,3,26,2,26,2,1573101,12, +8,1310721,2,3,26,1,6,2,1,6,3,26,5,6,4,10, +8,8,1573393,14,1,1573489,14,1,1310721,2,5,26,1310721,4,18,1310721, +2,29,28,1310721,4,1310721,2,1835009,40,1573537,16,1966081,1310721,4,1579069,12, +1310721,2,4,1,1310721,2,27,26,4,1310721,2,2,1,40,1573603,16, +91,1573101,12,8,1310721,2,1,1310721,2,2,28,10,3,6,10,4, +8,1310721,2,2,1,40,1573657,16,1573643,14,50,49,52,55,52,56, +51,54,52,56,0,1573621,1573341,12,8,1,1573871,14,3,1573795,16,1310721, +2,3,26,1310721,2,27,26,1,6,2,1,6,3,26,5,6, +4,10,8,26,97,1310721,2,2,21,46,26,10,3,6,10,4, +8,1310721,2,1310721,2,2,21,44,10,3,6,10,4,8,1310721,2, +2,1,6,1,10,28,34,63,38,3,26,1573791,16,1573795,14,1573657, +14,1310721,2,27,26,2,1573863,16,1310721,2,3,26,1310721,2,27,26, +1,6,2,-1,26,1,6,5,6,4,10,8,26,2,1573101,12, +8,1573795,14,1,1573871,14,1,1310721,2,29,26,1310721,4,18,1310721,2, +5,28,1310721,4,1310721,2,1835009,40,1573919,16,1966081,1310721,4,1579069,12,1310721, +2,4,1,1310721,2,3,26,4,1310721,2,2,1310721,2,3,26,2, +26,2,1573999,16,1310721,2,3,26,1,6,2,1,6,3,26,5, +6,4,10,8,8,1573937,14,1310721,2,3,26,2,1574015,14,1,1310721, +2,5,26,1310721,4,18,1310721,2,7,28,1310721,4,1310721,2,1835009,40, +1574063,16,1966081,1310721,4,1579069,12,1310721,2,3,26,4,1310721,2,4,1, +1310721,2,5,26,4,1310721,2,2,1310721,2,5,26,2,26,2,1574207, +16,1310721,2,2,1310721,2,5,26,2,26,2,1310721,2,3,26,2, +1310721,2,5,26,2,26,2,28,1,6,1,10,28,34,63,38, +3,26,1,6,1,10,28,34,63,38,1,10,28,1574209,14,1, +1574251,16,1310721,2,5,26,1,6,2,1,6,3,26,5,6,4, +10,8,8,1574091,14,1310721,2,2,1310721,2,5,26,2,26,2,1310721, +2,3,26,2,1310721,2,5,26,2,26,2,28,1,6,1,10, +28,34,63,38,3,26,1574323,14,1,1310721,2,7,26,1310721,4,18, +1310721,2,9,28,1310721,4,1310721,2,1835009,40,1574371,16,1966081,1310721,4,1579069, +12,1310721,2,5,26,4,1310721,2,3,26,4,1310721,2,4,1,1310721, +2,7,26,4,1310721,2,7,26,2,1310721,2,5,26,2,40,1574527, +16,1310721,2,2,1310721,2,7,26,2,26,1310721,2,3,26,2,1310721, +2,7,26,2,26,2,10,3,6,10,4,8,1310721,2,7,26, +1,6,2,1,6,3,26,5,6,4,10,8,8,1574409,14,1, +1574535,14,1,1310721,2,9,26,1310721,4,18,1310721,2,9,28,1310721,4, +1310721,2,1835009,40,1574583,16,1966081,1310721,4,1579069,12,1310721,2,5,26,4, +1310721,2,3,26,4,1310721,2,4,1,1310721,2,7,26,4,1310721,2, +7,26,2,1310721,2,5,26,2,40,1574725,16,1310721,2,2,1310721,2, +7,26,2,26,1310721,2,3,26,2,10,3,6,10,4,8,1310721, +2,7,26,1,6,2,1,6,3,26,5,6,4,10,8,8, +1574621,14,1,1574733,14,1,1310721,2,9,26,1310721,4,18,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,1310721,2, +11,28,1310721,4,1310721,2,1835009,40,1574847,16,1966081,1310721,4,1579069,12,1310721, +2,3,26,4,1310721,2,4,1,1310721,2,5,26,4,1,1310721,2, +7,26,4,61,1310721,2,9,26,4,1310721,2,2,1310721,2,3,26, +2,40,1574947,16,1574811,1310721,2,2,10,3,6,10,4,8,1,1575261, +14,1310721,2,9,26,2,1,40,3,36,1575221,16,1310721,2,7,26, +1310721,2,7,26,2,1310721,2,7,26,2,26,1310721,2,2,1310721,2, +9,26,2,38,3,32,26,10,3,6,10,4,8,1310721,2,5, +26,1310721,2,5,26,2,1310721,2,5,26,2,26,10,3,6,10, +4,8,1310721,2,7,26,2,1310721,2,3,26,2,40,3,36,1575183, +16,1310721,2,7,26,1310721,2,7,26,2,1310721,2,3,26,2,28, +10,3,6,10,4,8,1310721,2,5,26,1,6,2,1,6,3, +26,5,6,4,10,8,8,1310721,2,9,26,1,6,2,1,6, +-1,26,5,6,4,10,8,8,1574947,14,1574811,1310721,2,7,26,2, +10,3,6,10,4,8,1310721,2,5,26,2,1575261,14,1,1310721,2, +11,26,1310721,4,18,1310721,2,9,28,1310721,4,1310721,2,1835009,40,1575309, +16,1966081,1310721,4,1579069,12,1310721,2,3,26,4,1310721,2,4,1,1310721, +2,5,26,4,1310721,2,3,26,2,1,28,1,6,1,10,28, +34,63,38,3,26,1575401,16,1574811,1310721,2,2,10,3,6,10,4, +8,1,1575637,14,1310721,2,2,1,40,1575463,16,1310721,2,1,1310721,2, +2,28,10,3,6,10,4,8,1310721,2,5,26,3,10,3,6, +10,4,8,1310721,2,3,26,2,1,40,1575549,16,1310721,2,3,26, +1,1310721,2,3,26,2,28,10,3,6,10,4,8,1310721,2,5, +26,1310721,2,5,26,2,3,36,10,3,6,10,4,8,1310721,2, +7,26,1310721,2,2,1310721,2,3,26,2,1574813,12,10,3,6,10, +4,8,1310721,2,5,26,2,1575621,16,1,1310721,2,7,26,2,28, +1575637,14,1310721,2,7,26,2,1575637,14,1,1310721,2,9,26,1310721,4, +18,1310721,2,7,28,1310721,4,1310721,2,1835009,40,1575685,16,1966081,1310721,4, +1579069,12,1310721,2,3,26,4,1310721,2,4,1310721,2,2,1,40,1310721, +2,5,26,4,1310721,2,3,26,2,1,28,1,6,1,10,28, +34,63,38,3,26,1575769,16,1310721,2,2,1575827,14,1310721,2,2,1310721, +2,3,26,2,1575275,12,8,1310721,2,5,26,2,1575817,16,1,1574811, +2,28,1575827,14,1574811,2,1575827,14,1,1310721,2,7,26,1310721,4,18, +1310721,2,5,28,1310721,4,1310721,2,1835009,40,1575875,16,1966081,1310721,4,1579069, +12,1310721,2,3,26,4,1310721,2,4,1310721,2,2,1574747,1310721,2,3, +26,2,63,32,26,2,30,1575925,14,1,1310721,2,5,26,1310721,4, +18,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,1,1310721,2,9,28,1310721,4,1310721,2,1835009,40,1576045, +16,1966081,1310721,4,1579069,12,1310721,2,4,1310721,524289,28,1310721,2,3,26, +4,1,1310721,2,5,26,4,1310721,2,3,26,2,1576341,16,1310721,2, +7,26,3,10,3,6,10,4,8,1310721,2,7,26,2,1310721,2, +7,26,2,26,1310721,2,3,26,2,10,40,3,36,1576205,16,1310721, +2,7,26,1310721,2,7,26,2,1310721,2,7,26,2,26,10,3, +6,10,4,8,1576115,14,1310721,2,2,1310721,2,5,26,2,1310721,2, +7,26,2,26,40,1576253,16,1310721,2,7,26,2,1576349,14,1310721,2, +5,26,1310721,2,5,26,2,1310721,2,7,26,2,26,10,3,6, +10,4,8,1310721,2,3,26,1310721,2,3,26,2,1310721,2,7,26, +2,28,10,3,6,10,4,8,1576079,14,1,1576349,14,1,1310721,2, +9,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,1835009,40,1576397, +16,1966081,1310721,4,1579069,12,1310721,2,3,26,4,1310721,2,4,1310721,2, +2,5,26,1,10,3,6,10,4,8,1310721,2,2,13,26,1575941, +1310721,2,3,26,2,26,2,10,3,6,10,4,8,1310721,2,2, +15,26,1,10,3,6,10,4,8,1575941,1310721,2,3,26,2,26, +2,1576557,16,1575941,1310721,2,3,26,2,26,2,15,26,1310721,2,2, +10,3,6,10,4,8,1575941,1310721,2,3,26,2,26,1310721,2,2, +10,3,6,10,4,8,1,1576597,14,1,1310721,2,5,26,1310721,4, +18,1310721,2,5,28,1310721,4,1310721,2,1835009,40,1576645,16,1966081,1310721,4, +1579069,12,1310721,2,3,26,4,1310721,2,4,1310721,2,2,15,26,2, +1576721,16,1310721,2,2,15,26,2,13,26,1310721,2,2,13,26,2, +10,3,6,10,4,8,1576759,14,1575941,1310721,2,3,26,2,26,1310721, +2,2,13,26,2,10,3,6,10,4,8,1310721,2,2,13,26, +2,1576815,16,1310721,2,2,13,26,2,15,26,1310721,2,2,15,26, +2,10,3,6,10,4,8,1,1576823,14,1,1310721,2,5,26,1310721, +4,18,1310721,2,5,28,1310721,4,1310721,2,1835009,40,1576871,16,1966081,1310721, +4,1579069,12,1310721,2,3,26,4,1310721,2,4,1310721,2,2,1,26, +1574747,1310721,2,3,26,2,26,2,10,3,6,10,4,8,1310721,2, +2,3,26,1310721,2,3,26,2,10,3,6,10,4,8,1310721,2, +2,5,26,1,10,3,6,10,4,8,1310721,2,2,7,26,1, +10,3,6,10,4,8,1310721,2,2,9,26,1,10,3,6,10, +4,8,1310721,2,2,11,26,1,10,3,6,10,4,8,1310721,2, +2,1577065,14,1,1310721,2,5,26,1310721,4,18,1310721,2,9,28,1310721, +4,1310721,2,1835009,40,1577113,16,1966081,1310721,4,1579069,12,3,1310721,2,4, +524289,1310721,2,3,26,4,1574747,1,26,3,10,3,6,10,4,8, +1310721,2,2,65,40,1577257,16,1574747,1310721,2,2,26,1574747,1310721,2,2, +3,28,26,2,1574747,1310721,2,2,3,28,26,2,26,10,3,6, +10,4,8,1310721,2,1,6,2,1,6,3,26,5,6,4,10, +8,8,1577153,14,1575939,1310721,10,3,6,10,4,8,1575941,1,65,1574549, +12,8,1310721,2,3,26,2,1310721,40,1577519,16,1310721,2,5,26,1310721, +2,3,26,2,524289,28,1576011,12,10,3,6,10,4,8,1310721,2, +7,26,1,10,3,6,10,4,8,1574747,1310721,2,7,26,2,26, +2,1310721,2,5,26,2,40,1577433,16,1310721,2,7,26,1,6,2, +1,6,3,26,5,6,4,10,8,8,1577363,14,1310721,2,3,26, +2,1310721,2,7,26,2,1576837,12,1310721,2,7,26,2,1576363,12,8, +1310721,2,3,26,1310721,2,3,26,2,1310721,2,5,26,2,26,10, +3,6,10,4,8,1577285,14,1,1577527,14,1,1310721,2,9,26,1310721, +4,18,1310721,2,9,28,1310721,4,1310721,2,1835009,40,1577575,16,1966081,1310721, +4,1579069,12,1310721,2,4,1310721,2,2,1310721,2,3,26,4,1310721,2, +3,26,2,65,40,1577677,16,1575941,1310721,2,3,26,2,26,2,1, +6,1,10,28,34,63,38,3,26,1,6,1,10,28,34,63, +38,1,10,28,1577679,14,1,1577721,16,1310721,2,3,26,1,6,2, +1,6,3,26,5,6,4,10,8,8,1577597,14,1310721,2,3,26, +2,65,40,3,36,1577749,16,1,1577999,14,1310721,2,5,26,1575941,1310721, +2,3,26,2,26,2,10,3,6,10,4,8,1310721,2,5,26, +2,1310721,2,3,26,2,1576611,12,8,1310721,2,3,26,2,1310721,2, +2,10,40,1577961,16,1310721,2,3,26,1,6,2,1,6,-1,26, +5,6,4,10,8,8,1310721,2,7,26,1310721,2,5,26,2,1574747, +1310721,2,3,26,2,26,2,26,10,3,6,10,4,8,1310721,2, +7,26,2,1310721,2,3,26,2,1576837,12,1310721,2,3,26,2,1576363, +12,8,1577811,14,1310721,2,5,26,2,1310721,2,2,1576837,12,8,1310721, +2,5,26,2,1577999,14,1,1310721,2,9,26,1310721,4,18,0,1310721, +2,11,28,1310721,4,1310721,2,1835009,40,1578049,16,1966081,1310721,4,1579069,12, +1310721,2,4,1310721,2,2,3,26,2,1310721,2,3,26,4,1574747,1310721, +2,3,26,2,26,2,1310721,2,5,26,4,3,1578721,16,1310721,2, +7,26,1310721,2,2,524289,28,10,3,6,10,4,8,1310721,2,5, +26,2,1310721,2,7,26,2,1576011,12,40,3,36,1578177,16,1578721,14, +1310721,2,9,26,524289,1310721,2,7,26,2,1310721,2,5,26,2,36, +26,10,3,6,10,4,8,1310721,2,9,26,2,5,26,2,1, +28,1,6,1,10,28,34,63,38,1,10,28,1578275,16,3,1578347, +14,1310721,2,9,26,2,3,26,2,1310721,2,3,26,2,28,1, +6,1,10,28,34,63,38,1,10,28,1,6,1,10,28,34, +63,38,1,10,28,1578357,16,3,1578429,14,1310721,2,9,26,2,1, +26,2,1310721,2,5,26,2,28,1,6,1,10,28,34,63,38, +1,10,28,1,6,1,10,28,34,63,38,1,10,28,1578437,16, +1578721,14,1310721,2,9,26,2,1310721,2,3,26,2,1576611,12,8,1310721, +2,9,26,2,1310721,2,2,40,1578563,16,1310721,2,2,1,26,1, +10,3,6,10,4,8,1310721,2,2,7,26,1,10,3,6,10, +4,8,1310721,2,1310721,2,9,26,2,10,3,6,10,4,8,1578619, +14,1310721,2,9,26,2,1,26,1,10,3,6,10,4,8,1310721, +2,9,26,2,7,26,1,10,3,6,10,4,8,1310721,2,3, +26,1,6,2,1,6,3,26,5,6,4,10,8,8,1310721,2, +5,26,1310721,2,5,26,2,1310721,2,5,26,2,26,10,3,6, +10,4,8,1310721,2,2,1310721,2,3,26,2,1576837,12,8,1578103,14, +1310721,2,2,1310721,2,3,26,2,1576837,12,1310721,2,3,26,2,1576363, +12,8,1578013,1310721,2,2,1310721,2,5,26,2,26,10,3,6,10, +4,8,1,1578797,14,1,1310721,2,11,26,1310721,4,18,1310721,2,3, +28,1310721,4,1310721,2,1835009,40,1578845,16,1966081,1310721,4,1579069,12,1310721,2, +4,1310721,2,2,524289,13,26,40,3,36,1578911,16,1310721,2,2,1575939, +2,40,1,6,1,10,28,34,63,38,1,10,28,1578913,14,1, +1578993,16,1310721,2,2,7,1,10,28,26,2,1310721,2,2,1459366445,36, +28,1,6,1,10,28,34,63,38,3,26,1,6,1,10,28, +34,63,38,1,10,28,1578995,14,1,1579047,16,1310721,2,2,9,1, +10,28,26,2,3,32,1,6,1,10,28,34,63,38,1,10, +28,1579049,14,1,1579055,14,1,1310721,2,3,26,1310721,4,18,1310721,2, +1,28,1310721,4,1310721,2,1835009,40,1579103,16,1966081,1310721,4,1579069,12,1579159, +14,122,121,58,32,102,114,97,109,101,32,115,116,97,99,107, +32,111,118,101,114,102,108,111,119,10,0,1579107,1573341,12,8,1573283, +12,8,1,1579181,14,1,1310721,2,1,26,1310721,4,18,1310721,2,1, +28,1310721,4,1310721,2,1835009,40,1579229,16,1966081,1310721,4,1579069,12,1579281,14, +122,99,58,32,111,117,116,32,111,102,32,104,101,97,112,32, +109,101,109,111,114,121,10,0,1579233,1573341,12,8,1573283,12,8,1, +1579303,14,1,1310721,2,1,26,1310721,4,18,1310721,2,13,28,1310721,4, +1310721,2,1835009,40,1579351,16,1966081,1310721,4,1579069,12,1310721,2,3,26,4, +1310721,2,4,1310721,2,2,13,26,1310721,2,5,26,4,7,1310721,2, +7,26,4,1310721,2,2,1,40,1579435,16,1310721,2,5,26,13,10, +3,6,10,4,8,1574747,1310721,2,7,26,2,26,2,1310721,2,5, +26,2,40,1579505,16,1310721,2,7,26,1,6,2,1,6,3,26, +5,6,4,10,8,8,1579435,14,1310721,2,9,26,1310721,2,7,26, +2,1577541,12,10,3,6,10,4,8,1310721,2,9,26,2,1,6, +1,10,28,34,63,38,3,26,1579603,16,1576009,2,1,6,1,10, +28,34,63,38,1,10,28,1579605,14,1,1579649,16,1581445,12,8,1310721, +2,9,26,1310721,2,7,26,2,1577541,12,10,3,6,10,4,8, +1310721,2,9,26,2,1,6,1,10,28,34,63,38,3,26,1579689, +16,1579195,12,8,1310721,2,11,26,1310721,2,9,26,2,13,26,10, +3,6,10,4,8,1310721,2,9,26,2,5,26,3,10,3,6, +10,4,8,1310721,2,9,26,2,7,26,1310721,2,11,26,2,1459366445, +36,10,3,6,10,4,8,1310721,2,9,26,2,9,26,1,10, +3,6,10,4,8,1310721,2,9,26,2,11,26,1310721,2,3,26, +2,10,3,6,10,4,8,1310721,2,11,26,2,1,1574747,1310721,2, +7,26,2,26,2,13,28,1574549,12,8,1310721,2,11,26,2,1579909, +14,1,1310721,2,13,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721, +2,1835009,40,1579957,16,1966081,1310721,4,1579069,12,1310721,2,4,1310721,2,2, +1,1579317,12,1579981,14,1,1310721,2,3,26,1310721,4,18,1310721,2,3, +28,1310721,4,1310721,2,1835009,40,1580029,16,1966081,1310721,4,1579069,12,1310721,2, +4,1310721,2,2,1578811,12,1,6,1,10,28,34,63,38,3,26, +1580075,16,1,1580103,14,1310721,2,2,13,1,10,28,26,2,13,28, +1580103,14,1,1310721,2,3,26,1310721,4,18,1310721,2,3,28,1310721,4, +1310721,2,1835009,40,1580151,16,1966081,1310721,4,1579069,12,1310721,2,4,1310721,2, +2,1578811,12,1,6,1,10,28,34,63,38,3,26,1580197,16,1, +1580221,14,1310721,2,2,3,1,10,28,26,2,1580221,14,1,1310721,2, +3,26,1310721,4,18,1310721,2,11,28,1310721,4,1310721,2,1835009,40,1580269, +16,1966081,1310721,4,1579069,12,1310721,2,4,1310721,2,2,13,28,1310721,2, +3,26,4,1310721,2,3,26,2,1,26,2,13,28,1310721,2,5, +26,4,1,1310721,2,7,26,4,1310721,2,3,26,2,5,26,1, +10,3,6,10,4,8,1310721,2,7,26,2,1310721,2,5,26,2, +40,1580585,16,1310721,2,9,26,1310721,2,2,1310721,2,7,26,2,26, +2,10,3,6,10,4,8,1310721,2,9,26,2,1578811,12,1580525,16, +1310721,2,9,26,2,5,1,10,28,26,2,1310721,2,2,28,1, +6,1,10,28,34,63,38,3,26,1,6,1,10,28,34,63, +38,1,10,28,1580527,14,1,1580547,16,1310721,2,9,26,2,1580235,12, +8,1310721,2,7,26,1,6,2,1,6,3,26,5,6,4,10, +8,8,1580365,14,1310721,2,3,26,2,1578015,12,8,1,1580609,14,1, +1310721,2,11,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009, +40,1580657,16,1966081,1310721,4,1579069,12,1310721,2,4,1310721,2,2,1578811,12, +1580689,16,1310721,2,2,1580235,12,8,1,1580697,14,1,1310721,2,3,26, +1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,1835009,40,1580745,16,1966081, +1310721,4,1579069,12,1310721,2,3,26,4,1310721,2,4,1310721,2,2,1578811, +12,1580813,16,1310721,2,2,5,1,10,28,26,1310721,2,3,26,2, +10,3,6,10,4,8,1310721,2,2,1580825,14,1,1310721,2,5,26, +1310721,4,18,0,0,0,0,1310721,2,3,28,1310721,4,1310721,2,1835009, +40,1580881,16,1966081,1310721,4,1579069,12,1310721,2,4,1310721,2,2,1578811,12, +1,6,1,10,28,34,63,38,3,26,1580927,16,3,1580971,14,1310721, +2,2,9,1,10,28,26,2,5,32,1,6,1,10,28,34, +63,38,1,10,28,1580981,16,1,1581125,14,1310721,2,2,9,1,10, +28,26,1310721,2,2,9,1,10,28,26,2,5,34,10,3,6, +10,4,8,1580839,2,32769,40,1581101,16,2031617,1580839,2,26,1310721,2,2, +10,3,6,10,4,8,1580839,1,6,2,1,6,3,26,5,6, +4,10,8,8,1581117,14,1580841,3,10,3,6,10,4,8,1,1581125, +14,1,1310721,2,3,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721, +2,1835009,40,1581173,16,1966081,1310721,4,1579069,12,1310721,2,3,26,4,1310721, +2,4,1310721,2,2,1310721,2,3,26,2,40,1581259,16,1310721,2,2, +2,1580847,12,8,1310721,2,1,6,2,1,6,3,26,5,6,4, +10,8,8,1581189,14,1,1581267,14,1,1310721,2,5,26,1310721,4,18, +1310721,2,3,28,1310721,4,1310721,2,1835009,40,1581315,16,1966081,1310721,4,1579069, +12,1580839,2,1581423,16,1580839,1,6,2,1,6,-1,26,5,6,4, +10,8,8,1310721,2,2031617,1580839,2,26,2,10,3,6,10,4,8, +1310721,2,2,1310721,2,2,1310721,2,2,13,1,10,28,26,2,26, +13,28,1581139,12,8,1581315,14,1,1581431,14,1,1310721,2,3,26,1310721, +4,18,1310721,2,7,28,1310721,4,1310721,2,1835009,40,1581479,16,1966081,1310721, +4,1579069,12,1,1310721,2,5,26,4,1580839,1,10,3,6,10,4, +8,1580841,1,10,3,6,10,4,8,1700815,12,8,1581281,12,8,1580841, +2,1581717,16,1580841,1,10,3,6,10,4,8,1310721,2,524289,10,3, +6,10,4,8,1310721,2,2,1310721,40,1581713,16,1310721,2,2,5,26, +2,7,28,1,6,1,10,28,34,63,38,3,26,1581667,16,1310721, +2,2,13,26,1310721,2,2,1310721,2,2,1,26,2,26,1581139,12, +8,1310721,2,1310721,2,2,1310721,2,2,1,26,2,26,10,3,6, +10,4,8,1581281,12,8,1581577,14,1581535,14,1310721,2,524289,10,3,6, +10,4,8,1310721,2,2,1310721,40,1581993,16,1310721,2,3,26,1310721,2, +2,1,26,2,10,3,6,10,4,8,1310721,2,2,5,26,2, +3,28,1,6,1,10,28,34,63,38,3,26,1581891,16,1310721,2, +2,1578015,12,8,1310721,2,5,26,1,6,2,1,6,3,26,5, +6,4,10,8,8,1310721,2,1578013,2,10,3,6,10,4,8,1581989, +14,1310721,2,2,5,26,2,7,28,1,6,1,10,28,34,63, +38,3,26,1581955,16,1310721,2,2,5,26,3,10,3,6,10,4, +8,1310721,2,1310721,2,2,1310721,2,3,26,2,26,10,3,6,10, +4,8,1581735,14,1576005,1,6,2,1,6,3,26,5,6,4,10, +8,8,1576007,1576007,2,1310721,2,5,26,2,26,10,3,6,10,4, +8,1310721,2,5,26,2,1582067,14,1,1310721,2,7,26,1310721,4,18, +1310721,2,5,28,1310721,4,1310721,2,1835009,40,1582115,16,1966081,1310721,4,1579069, +12,524289,1310721,2,4,1,1310721,2,3,26,4,1310721,2,2,1310721,40, +1582243,16,1310721,2,2,5,26,2,3,32,1582203,16,1310721,2,3,26, +1,6,2,1,6,3,26,5,6,4,10,8,8,1310721,2,1310721, +2,2,1310721,2,2,1,26,2,26,10,3,6,10,4,8,1582135, +14,1310721,2,3,26,2,1582259,14,1,1310721,2,5,26,1310721,4,18, +1310721,2,3,28,1310721,4,1310721,2,1835009,40,1582307,16,1966081,1310721,4,1579069, +12,63,1310721,2,4,1310721,2,2,1,10,40,1582389,16,1575941,1310721,2, +2,26,2,1,6,1,10,28,34,63,38,3,26,1,6,1, +10,28,34,63,38,1,10,28,1582391,14,1,1582429,16,1310721,2,1, +6,2,1,6,-1,26,5,6,4,10,8,8,1582315,14,1575941,1310721, +2,2,26,2,1,6,1,10,28,34,63,38,3,26,1582471,16, +1,1582489,14,1574747,1310721,2,2,26,2,1582489,14,1,1310721,2,3,26, +1310721,4,18,0,0,0,0,0,0,0,0,0,0,0,0,791264, +113,64,33,36,95,126,99,106,122,114,44,63,46,43,45,42, +38,124,94,62,60,37,0,1,0,0,0,0,1,0,0,0, +0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,1310721,2,1,28,1310721,4,1310721,2,1835009,40,1585127,16,1966081,1310721, +4,1579069,12,1585151,14,122,99,58,32,108,105,110,101,32,0,1585131, +1573341,12,8,1582583,2,1573503,12,8,1585179,14,58,32,0,1585173,1573341,12, +8,1585089,1,6,2,1,6,3,26,5,6,4,10,8,8,1, +1585223,14,1,1310721,2,1,26,1310721,4,18,1310721,2,1,28,1310721,4, +1310721,2,1835009,40,1585271,16,1966081,1310721,4,1579069,12,21,1573101,12,8,1585089, +2,41,40,3,36,1585375,16,1585361,14,122,99,58,32,116,111,111, +32,109,97,110,121,32,101,114,114,111,114,115,44,32,115,116, +111,112,112,105,110,103,10,0,1585299,1573341,12,8,1573283,12,8,1, +1585383,14,1,1310721,2,1,26,1310721,4,18,1310721,2,1,28,1310721,4, +1310721,2,1835009,40,1585431,16,1966081,1310721,4,1579069,12,1585445,14,122,99,58, +32,0,1585435,1573341,12,8,1585089,2,1573503,12,8,1585089,2,3,28,1, +6,1,10,28,34,63,38,3,26,1585527,16,1585515,14,32,101,114, +114,111,114,10,0,1585499,1573341,12,8,1585557,14,1585549,14,32,101,114, +114,111,114,115,10,0,1585531,1573341,12,8,1573283,12,8,1,1585571,14, +1,1310721,2,1,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2, +1835009,40,1585619,16,1966081,1310721,4,1579069,12,1310721,2,4,1585093,12,8,1310721, +2,2,1573341,12,8,21,1573101,12,8,1573283,12,8,1,1585665,14,1, +1310721,2,3,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009, +40,1585713,16,1966081,1310721,4,1579069,12,1310721,2,4,1585091,2,1,6,1, +10,28,34,63,38,3,26,1585797,16,1584991,2,1,6,1,10,28, +34,63,38,3,26,1,6,1,10,28,34,63,38,1,10,28, +1585799,14,1,1585827,16,1585093,12,8,1310721,2,2,1573341,12,8,1585237,12, +8,1,1585835,14,1,1310721,2,3,26,1310721,4,18,1310721,2,5,28, +1310721,4,1310721,2,1835009,40,1585883,16,1966081,1310721,4,1579069,12,1310721,2,3, +26,4,1310721,2,4,1585091,2,1,6,1,10,28,34,63,38,3, +26,1585977,16,1584991,2,1,6,1,10,28,34,63,38,3,26,1, +6,1,10,28,34,63,38,1,10,28,1585979,14,1,1586041,16,1585093, +12,8,1310721,2,2,1573341,12,8,1586011,14,58,32,0,1586005,1573341,12, +8,1310721,2,3,26,2,1573341,12,8,1585237,12,8,1,1586049,14,1, +1310721,2,5,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009, +40,1586097,16,1966081,1310721,4,1579069,12,1310721,2,4,1310721,2,2,1585679,12, +8,1585091,3,10,3,6,10,4,8,1582577,2,1,28,1,6,1, +10,28,34,63,38,3,26,1586169,16,1585397,12,8,1597071,12,8,1, +1586183,14,1,1310721,2,3,26,1310721,4,18,1310721,2,5,28,1310721,4, +1310721,2,1835009,40,1586231,16,1966081,1310721,4,1579069,12,1310721,2,3,26,4, +1310721,2,4,1310721,2,2,1310721,2,3,26,2,1585849,12,8,1585091,3, +10,3,6,10,4,8,1582577,2,1,28,1,6,1,10,28,34, +63,38,3,26,1586323,16,1585397,12,8,1597071,12,8,1,1586337,14,1, +1310721,2,5,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009, +40,1586385,16,1966081,1310721,4,1579069,12,1310721,2,4,1584853,2,1586429,16,1584857, +2,1,6,1,10,28,34,63,38,1,10,28,1586431,14,1,1586447, +16,1310721,2,2,1585679,12,8,1,1586455,14,1,1310721,2,3,26,1310721, +4,18,1310721,2,5,28,1310721,4,1310721,2,1835009,40,1586503,16,1966081,1310721, +4,1579069,12,1310721,2,3,26,4,1310721,2,4,1584853,2,1586557,16,1584857, +2,1,6,1,10,28,34,63,38,1,10,28,1586559,14,1,1586591, +16,1466881,1310721,2,2,26,2,1310721,2,3,26,2,1585849,12,8,1, +1586599,14,1,1310721,2,5,26,1310721,4,18,1310721,2,5,28,1310721,4, +1310721,2,1835009,40,1586647,16,1966081,1310721,4,1579069,12,1310721,2,4,1,1310721, +2,3,26,4,1310721,2,2,2,1586755,16,1310721,2,3,26,1310721,2, +3,26,2,63,30,1310721,2,2,2,26,10,3,6,10,4,8, +1310721,2,1,6,2,1,6,3,26,5,6,4,10,8,8,1586665, +14,1310721,2,3,26,2,16385,3,28,32,1586779,14,1,1310721,2,5, +26,1310721,4,18,1310721,2,9,28,1310721,4,1310721,2,1835009,40,1586827,16, +1966081,1310721,4,1579069,12,1310721,2,4,1310721,2,2,1586613,12,1310721,2,3, +26,4,3,1587017,16,1310721,2,5,26,1311233,1310721,2,3,26,2,26, +2,10,3,6,10,4,8,1310721,2,5,26,2,1,6,1,10, +28,34,63,38,3,26,1586933,16,1587017,14,1310721,2,5,26,2,1310721, +2,2,1574029,12,1586971,16,1310721,2,5,26,2,1587297,14,1310721,2,3, +26,1310721,2,3,26,2,3,26,16385,3,28,32,10,3,6,10, +4,8,1586853,14,1310721,2,7,26,1310721,2,2,1573885,12,10,3,6, +10,4,8,1582505,2,1310721,2,7,26,2,26,5,26,1409025,40,3, +36,1587125,16,1587117,14,115,116,114,105,110,103,32,112,111,111,108, +32,102,117,108,108,0,1587083,1585585,12,8,1582505,2,1,26,1,10, +3,6,10,4,8,1310721,2,5,26,1582505,2,3,26,10,3,6, +10,4,8,1310721,2,5,26,2,1310721,2,2,1310721,2,7,26,2, +3,26,1574337,12,8,1582505,1582505,2,1310721,2,7,26,2,26,5,26, +10,3,6,10,4,8,1311233,1310721,2,3,26,2,26,1310721,2,5, +26,2,10,3,6,10,4,8,1310721,2,5,26,2,1587297,14,1, +1310721,2,9,26,1310721,4,18,1310721,2,9,28,1310721,4,1310721,2,1835009, +40,1587345,16,1966081,1310721,4,1579069,12,1310721,2,5,26,4,1310721,2,3, +26,4,1310721,2,4,1582507,2,1310721,2,7,26,4,1582507,2,8193,40, +3,36,1587449,16,1587441,14,115,121,109,98,111,108,32,116,97,98, +108,101,32,102,117,108,108,0,1587405,1585585,12,8,1582507,1,6,2, +1,6,3,26,5,6,4,10,8,8,1409537,1310721,2,7,26,2, +26,1310721,2,2,10,3,6,10,4,8,1417729,1310721,2,7,26,2, +26,1310721,2,3,26,2,10,3,6,10,4,8,1425921,1310721,2,7, +26,2,26,1310721,2,5,26,2,10,3,6,10,4,8,1434113,1310721, +2,7,26,2,26,1,10,3,6,10,4,8,1442305,1310721,2,7, +26,2,26,3,10,3,6,10,4,8,1450497,1310721,2,7,26,2, +26,1,10,3,6,10,4,8,1559041,1310721,2,7,26,2,26,1, +10,3,6,10,4,8,1458689,1310721,2,7,26,2,26,1310721,2,2, +3,1,10,28,26,2,10,3,6,10,4,8,1310721,2,2,3, +1,10,28,26,1310721,2,7,26,2,3,26,10,3,6,10,4, +8,1310721,2,7,26,2,1587795,14,1,1310721,2,9,26,1310721,4,18, +1310721,2,5,28,1310721,4,1310721,2,1835009,40,1587843,16,1966081,1310721,4,1579069, +12,1310721,2,4,1310721,2,2,3,1,10,28,26,2,3,28,1310721, +2,3,26,4,1310721,2,3,26,2,1,40,3,36,1588129,16,1310721, +2,3,26,2,1582511,2,40,3,36,1587933,16,3,1587995,14,1417729,1310721, +2,3,26,2,26,2,11,28,1,6,1,10,28,34,63,38, +3,26,1,6,1,10,28,34,63,38,1,10,28,1588005,16,3, +1588067,14,1417729,1310721,2,3,26,2,26,2,15,28,1,6,1,10, +28,34,63,38,3,26,1,6,1,10,28,34,63,38,1,10, +28,1588085,16,1310721,2,3,26,2,1588143,14,1310721,2,3,26,1458689,1310721, +2,3,26,2,26,2,3,28,10,3,6,10,4,8,1587881,14, +3,1,10,28,1588143,14,1,1310721,2,5,26,1310721,4,18,1310721,2, +3,28,1310721,4,1310721,2,1835009,40,1588191,16,1966081,1310721,4,1579069,12,1310721, +2,4,1582507,2,1310721,2,2,10,40,1588289,16,1582507,1,6,2,1, +6,-1,26,5,6,4,10,8,8,1409537,1582507,2,26,2,3,1, +10,28,26,1458689,1582507,2,26,2,10,3,6,10,4,8,1588197,14, +1,1588297,14,1,1310721,2,3,26,1310721,4,18,1310721,2,3,28,1310721, +4,1310721,2,1835009,40,1588345,16,1966081,1310721,4,1579069,12,1310721,2,4,1310721, +2,2,1586793,12,1587809,12,1588371,14,1,1310721,2,3,26,1310721,4,18, +1310721,2,7,28,1310721,4,1310721,2,1835009,40,1588419,16,1966081,1310721,4,1579069, +12,1310721,2,3,26,4,1310721,2,4,1582515,2,1310721,2,5,26,4, +1310721,2,5,26,2,1588497,16,1582515,1491457,1310721,2,5,26,2,26,2, +10,3,6,10,4,8,1588639,14,1582513,1,6,2,1,6,3,26, +5,6,4,10,8,8,1310721,2,5,26,1582513,2,10,3,6,10, +4,8,1310721,2,5,26,2,16385,40,3,36,1588639,16,1588631,14,116, +111,111,32,109,97,110,121,32,102,111,114,119,97,114,100,32, +114,101,102,101,114,101,110,99,101,115,0,1588575,1585585,12,8,1475073, +1310721,2,5,26,2,26,1310721,2,2,10,3,6,10,4,8,1491457, +1310721,2,5,26,2,26,1310721,2,3,26,2,10,3,6,10,4, +8,1310721,2,5,26,2,1588723,14,1,1310721,2,7,26,1310721,4,18, +1310721,2,7,28,1310721,4,1310721,2,1835009,40,1588771,16,1966081,1310721,4,1579069, +12,1310721,2,3,26,4,1310721,2,4,1310721,2,2,1588933,16,1475073,1310721, +2,2,26,2,1310721,2,3,26,2,1589317,12,8,1310721,2,5,26, +1491457,1310721,2,2,26,2,10,3,6,10,4,8,1491457,1310721,2,2, +26,1582515,2,10,3,6,10,4,8,1582515,1310721,2,2,10,3,6, +10,4,8,1310721,2,1310721,2,5,26,2,10,3,6,10,4,8, +1588787,14,1,1588941,14,1,1310721,2,7,26,1310721,4,18,1310721,2,3, +28,1310721,4,1310721,2,1835009,40,1588989,16,1966081,1310721,4,1579069,12,1310721,2, +4,1584991,2,1589009,16,1,1589135,14,1584837,2,1584841,2,40,3,36,1589071, +16,1589063,14,112,114,111,103,114,97,109,32,116,111,111,32,98, +105,103,0,1589031,1585585,12,8,1584837,2,1584839,2,26,1310721,2,2,10, +3,6,10,4,8,1584837,1,6,2,1,6,3,26,5,6,4, +10,8,8,1,1589135,14,1,1310721,2,3,26,1310721,4,18,1310721,2, +3,28,1310721,4,1310721,2,1835009,40,1589183,16,1966081,1310721,4,1579069,12,1310721, +2,4,1310721,2,2,1310721,2,2,26,3,26,1588955,12,8,1,1589221, +14,1,1310721,2,3,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721, +2,1835009,40,1589269,16,1966081,1310721,4,1579069,12,1310721,2,4,1310721,2,2, +1310721,2,2,26,1588955,12,8,1,1589303,14,1,1310721,2,3,26,1310721, +4,18,1310721,2,5,28,1310721,4,1310721,2,1835009,40,1589351,16,1966081,1310721, +4,1579069,12,1310721,2,3,26,4,1310721,2,4,1584991,2,1589381,16,1, +1589439,14,1310721,2,2,1584839,2,26,1310721,2,3,26,2,1310721,2,3, +26,2,26,3,26,10,3,6,10,4,8,1,1589439,14,1,1310721, +2,5,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40, +1589487,16,1966081,1310721,4,1579069,12,1310721,2,4,1310721,2,2,1589149,12,8, +15,1589235,12,8,1,1589521,14,1,1310721,2,3,26,1310721,4,18,1310721, +2,3,28,1310721,4,1310721,2,1835009,40,1589569,16,1966081,1310721,4,1579069,12, +1584837,2,1310721,2,4,2097153,3,28,1589149,12,8,1310721,2,2,1589603,14, +1,1310721,2,3,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2, +1835009,40,1589651,16,1966081,1310721,4,1579069,12,1310721,2,4,1310721,2,2,3, +1582583,2,1587311,12,1310721,2,3,26,4,1442305,1310721,2,3,26,2,26, +1,10,3,6,10,4,8,1310721,2,3,26,2,1589727,14,1,1310721, +2,5,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40, +1589775,16,1966081,1310721,4,1579069,12,1310721,2,4,1442305,1310721,2,2,26,2, +3,32,1589825,16,1425921,1310721,2,2,26,2,1589149,12,8,1,1589901,14, +1584991,2,1589839,16,1,1589901,14,1450497,1310721,2,2,26,1584837,2,1450497,1310721, +2,2,26,2,1588385,12,10,3,6,10,4,8,2097153,3,28,1589149, +12,8,1,1589901,14,1,1310721,2,3,26,1310721,4,18,1310721,2,5, +28,1310721,4,1310721,2,1835009,40,1589949,16,1966081,1310721,4,1579069,12,1310721,2, +4,1310721,2,2,1587809,12,1310721,2,3,26,4,1310721,2,3,26,2, +1,40,1590023,16,1310721,2,3,26,1310721,2,2,1589617,12,10,3,6, +10,4,8,1310721,2,3,26,2,1589741,12,8,13,1589235,12,8,1, +1590055,14,1,1310721,2,5,26,1310721,4,18,1310721,2,3,28,1310721,4, +1310721,2,1835009,40,1590103,16,1966081,1310721,4,1579069,12,1310721,2,4,1310721,1589149, +12,8,3,1589235,12,8,1310721,2,2,1590155,16,1310721,2,2,1589149,12, +8,27,1589235,12,8,1,1590163,14,1,1310721,2,3,26,1310721,4,18, +1310721,2,7,28,1310721,4,1310721,2,1835009,40,1590211,16,1966081,1310721,4,1579069, +12,1310721,2,4,1589535,12,1310721,2,3,26,4,15,1589235,12,8,1310721, +2,5,26,1584837,2,10,3,6,10,4,8,1310721,2,2,2,1590323, +16,1310721,2,2,2,1588955,12,8,1310721,2,1,6,2,1,6,3, +26,5,6,4,10,8,8,1590263,14,1,1588955,12,8,1310721,2,3, +26,2,1584837,2,1589317,12,8,1310721,2,5,26,2,1589149,12,8,1, +1590375,14,1,1310721,2,7,26,1310721,4,18,1310721,2,1,28,1310721,4, +1310721,2,1835009,40,1590423,16,1966081,1310721,4,1579069,12,1582589,2,1,28,1, +6,1,10,28,34,63,38,3,26,1590629,16,1582595,2,1590469,16,3, +1590495,14,1573227,12,1,6,1,10,28,34,63,38,1,10,28,1590527, +16,1582587,3,1,10,28,10,3,6,10,4,8,1,1590929,14,1582587, +1573171,12,511,32,10,3,6,10,4,8,1582587,2,193,28,1,6, +1,10,28,34,63,38,3,26,1590625,16,1582595,3,10,3,6,10, +4,8,1582587,3,1,10,28,10,3,6,10,4,8,1,1590929,14, +1590861,14,1582589,2,7,28,1,6,1,10,28,34,63,38,3,26, +1590775,16,1582587,1582593,2,1582591,2,5,38,26,2,1582591,2,7,32,17, +30,38,511,32,10,3,6,10,4,8,1582587,2,1590749,16,1582591,1, +6,2,1,6,3,26,5,6,4,10,8,8,1590771,14,1582587,3, +1,10,28,10,3,6,10,4,8,1590861,14,1582587,1582591,2,2,511, +32,10,3,6,10,4,8,1582587,2,1590839,16,1582591,1,6,2,1, +6,3,26,5,6,4,10,8,8,1590861,14,1582587,3,1,10,28, +10,3,6,10,4,8,1582587,2,21,28,1,6,1,10,28,34, +63,38,3,26,1590921,16,1582585,1,6,2,1,6,3,26,5,6, +4,10,8,8,1,1590929,14,1,1310721,2,1,26,1310721,4,18,1310721, +2,13,28,1310721,4,1310721,2,1835009,40,1590977,16,1966081,1310721,4,1579069,12, +1310721,2,7,26,4,1310721,2,5,26,4,1310721,2,3,26,4,1310721, +2,4,1582597,1,6,2,1,6,3,26,5,6,4,10,8,8, +1582597,2,65,40,3,36,1591121,16,1591113,14,115,111,117,114,99,101, +115,32,110,101,115,116,101,100,32,116,111,111,32,100,101,101, +112,108,121,0,1591061,1585585,12,8,1310721,2,9,26,1540609,1582597,2,65, +30,26,10,3,6,10,4,8,1310721,2,9,26,2,1,26,1310721, +2,2,10,3,6,10,4,8,1310721,2,9,26,2,3,26,1310721, +2,3,26,2,10,3,6,10,4,8,1310721,2,9,26,2,5, +26,1,10,3,6,10,4,8,1310721,2,9,26,2,7,26,1, +10,3,6,10,4,8,1310721,2,9,26,2,9,26,1310721,2,5, +26,2,10,3,6,10,4,8,1310721,2,9,26,2,23,26,1310721, +2,7,26,2,10,3,6,10,4,8,1310721,2,9,26,2,11, +26,1582577,2,10,3,6,10,4,8,1310721,2,9,26,2,13,26, +1582579,2,10,3,6,10,4,8,1310721,2,9,26,2,15,26,1582581, +2,10,3,6,10,4,8,1310721,2,9,26,2,17,26,1582587,2, +10,3,6,10,4,8,1310721,2,9,26,2,19,26,1582589,2,10, +3,6,10,4,8,1310721,2,9,26,2,21,26,1582591,2,10,3, +6,10,4,8,1310721,2,9,26,2,25,26,1582585,2,10,3,6, +10,4,8,1310721,2,9,26,2,27,26,1582583,2,10,3,6,10, +4,8,1310721,2,9,26,2,29,26,1584803,2,10,3,6,10,4, +8,1310721,2,9,26,2,63,26,1582593,2,10,3,6,10,4,8, +1310721,2,11,26,1,10,3,6,10,4,8,1310721,2,11,26,2, +1584803,2,40,1591961,16,1310721,2,9,26,2,31,1310721,2,11,26,2, +9,30,26,26,1584805,1310721,2,11,26,2,26,2,10,3,6,10, +4,8,1310721,2,9,26,2,33,1310721,2,11,26,2,9,30,26, +26,1584813,1310721,2,11,26,2,26,2,10,3,6,10,4,8,1310721, +2,9,26,2,35,1310721,2,11,26,2,9,30,26,26,1584821,1310721, +2,11,26,2,26,2,10,3,6,10,4,8,1310721,2,9,26, +2,37,1310721,2,11,26,2,9,30,26,26,1584829,1310721,2,11,26, +2,26,2,10,3,6,10,4,8,1310721,2,11,26,1,6,2, +1,6,3,26,5,6,4,10,8,8,1591671,14,1584803,1,10,3, +6,10,4,8,1310721,2,2,3,28,1,6,1,10,28,34,63, +38,3,26,1592053,16,1310721,2,9,26,2,7,26,1310721,2,3,26, +2,1,26,2,10,3,6,10,4,8,1310721,2,2,1,28,1, +6,1,10,28,34,63,38,3,26,1592141,16,1582589,1,10,3,6, +10,4,8,1582595,1,10,3,6,10,4,8,1582585,3,10,3,6, +10,4,8,1590389,12,8,1310721,2,2,5,28,1,6,1,10,28, +34,63,38,3,26,1592237,16,1582589,5,10,3,6,10,4,8,1582591, +1310721,2,3,26,2,10,3,6,10,4,8,1582585,3,10,3,6, +10,4,8,1590389,12,8,1310721,2,2,7,28,1,6,1,10,28, +34,63,38,3,26,1592349,16,1582589,7,10,3,6,10,4,8,1582593, +1310721,2,3,26,2,10,3,6,10,4,8,1582591,1,10,3,6, +10,4,8,1582585,3,10,3,6,10,4,8,1590389,12,8,1,1592357, +14,1,1310721,2,13,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721, +2,1835009,40,1592405,16,1966081,1310721,4,1579069,12,1540609,1582597,2,65,30,26, +1310721,2,4,1,1310721,2,3,26,4,1582577,1310721,2,2,11,26,2, +10,3,6,10,4,8,1582579,1310721,2,2,13,26,2,10,3,6, +10,4,8,1582581,1310721,2,2,15,26,2,10,3,6,10,4,8, +1582587,1310721,2,2,17,26,2,10,3,6,10,4,8,1582589,1310721,2, +2,19,26,2,10,3,6,10,4,8,1582591,1310721,2,2,21,26, +2,10,3,6,10,4,8,1582585,1310721,2,2,25,26,2,10,3, +6,10,4,8,1582583,1310721,2,2,27,26,2,10,3,6,10,4, +8,1584803,1310721,2,2,29,26,2,10,3,6,10,4,8,1582593,1310721, +2,2,63,26,2,10,3,6,10,4,8,1310721,2,3,26,2, +1584803,2,40,1592969,16,1584805,1310721,2,3,26,2,26,1310721,2,2,31, +1310721,2,3,26,2,9,30,26,26,2,10,3,6,10,4,8, +1584813,1310721,2,3,26,2,26,1310721,2,2,33,1310721,2,3,26,2, +9,30,26,26,2,10,3,6,10,4,8,1584821,1310721,2,3,26, +2,26,1310721,2,2,35,1310721,2,3,26,2,9,30,26,26,2, +10,3,6,10,4,8,1584829,1310721,2,3,26,2,26,1310721,2,2, +37,1310721,2,3,26,2,9,30,26,26,2,10,3,6,10,4, +8,1310721,2,3,26,1,6,2,1,6,3,26,5,6,4,10, +8,8,1592695,14,1582597,1,6,2,1,6,-1,26,5,6,4,10, +8,8,1310721,2,2,23,26,2,1593031,16,1310721,2,2,3,26,2, +1580623,12,8,1,1593039,14,1,1310721,2,5,26,1310721,4,18,1310721,2, +5,28,1310721,4,1310721,2,1835009,40,1593087,16,1966081,1310721,4,1579069,12,1310721, +2,4,1593137,14,43,45,42,47,37,61,60,62,33,38,124,94, +126,63,58,46,64,35,36,0,1593097,1310721,2,3,26,4,1310721,2, +3,26,2,2,1593253,16,1310721,2,3,26,2,2,1310721,2,2,28, +1,6,1,10,28,34,63,38,3,26,1593215,16,3,1593261,14,1310721, +2,3,26,1,6,2,1,6,3,26,5,6,4,10,8,8, +1593149,14,1,1593261,14,1,1310721,2,5,26,1310721,4,18,1310721,2,11, +28,1310721,4,1310721,2,1835009,40,1593309,16,1966081,1310721,4,1579069,12,1310721,2, +3,26,4,1310721,2,4,1,1310721,2,5,26,4,1310721,2,5,26, +2,1582523,2,40,1593739,16,1310721,2,7,26,1547777,1310721,2,5,26,2, +26,2,10,3,6,10,4,8,1310721,2,9,26,1,10,3,6, +10,4,8,1310721,2,9,26,2,1310721,2,2,40,1593525,16,1310721,2, +7,26,2,1310721,2,9,26,2,26,2,1582599,1310721,2,9,26,2, +26,2,28,1,6,1,10,28,34,63,38,3,26,1,6,1, +10,28,34,63,38,1,10,28,1593527,14,1,1593569,16,1310721,2,9, +26,1,6,2,1,6,3,26,5,6,4,10,8,8,1593415,14, +1310721,2,9,26,2,1310721,2,2,28,1,6,1,10,28,34,63, +38,3,26,1593689,16,1310721,2,7,26,2,1310721,2,2,26,2,1310721, +2,3,26,2,28,1,6,1,10,28,34,63,38,3,26,1, +6,1,10,28,34,63,38,1,10,28,1593691,14,1,1593701,16,3, +1593747,14,1310721,2,5,26,1,6,2,1,6,3,26,5,6,4, +10,8,8,1593337,14,1,1593747,14,1,1310721,2,11,26,1310721,4,18, +1310721,2,3,28,1310721,4,1310721,2,1835009,40,1593795,16,1966081,1310721,4,1579069, +12,1310721,2,4,1310721,2,2,97,40,3,36,1593861,16,1310721,2,2, +115,10,40,3,36,1,6,1,10,28,34,63,38,1,10,28, +1593863,14,1,1593869,14,1,1310721,2,3,26,1310721,4,18,1310721,2,3, +28,1310721,4,1310721,2,1835009,40,1593917,16,1966081,1310721,4,1579069,12,1310721,2, +4,1310721,2,2,195,40,3,36,1593983,16,1310721,2,2,245,10,40, +3,36,1,6,1,10,28,34,63,38,1,10,28,1593985,14,1, +1593995,16,3,1594079,14,1310721,2,2,131,40,3,36,1594055,16,1310721,2, +2,181,10,40,3,36,1,6,1,10,28,34,63,38,1,10, +28,1594057,14,1,1,6,1,10,28,34,63,38,1,10,28,1594089, +16,3,1594141,14,1310721,2,2,191,28,1,6,1,10,28,34,63, +38,3,26,1,6,1,10,28,34,63,38,1,10,28,1594147,14, +1,1310721,2,3,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2, +1835009,40,1594195,16,1966081,1310721,4,1579069,12,1310721,2,4,1310721,2,2,1593761, +12,1594229,16,1310721,2,2,97,28,1594411,14,1310721,2,2,195,40,3, +36,1594289,16,1310721,2,2,205,10,40,3,36,1,6,1,10,28, +34,63,38,1,10,28,1594291,14,1,1594313,16,1310721,2,2,195,28, +21,26,1594411,14,1310721,2,2,131,40,3,36,1594373,16,1310721,2,2, +141,10,40,3,36,1,6,1,10,28,34,63,38,1,10,28, +1594375,14,1,1594397,16,1310721,2,2,131,28,21,26,1594411,14,3,1, +10,28,1594411,14,1,1310721,2,3,26,1310721,4,18,1310721,2,3,28, +1310721,4,1310721,2,1835009,40,1594459,16,1966081,1310721,4,1579069,12,1582587,2,1310721, +2,4,1590389,12,8,1310721,2,2,185,28,1,6,1,10,28,34, +63,38,1,10,28,1594521,16,1310721,2,2,1594759,14,1310721,2,1582587,2, +10,3,6,10,4,8,1590389,12,8,1310721,2,2,221,28,1,6, +1,10,28,34,63,38,3,26,1594587,16,21,1594759,14,1310721,2,2, +233,28,1,6,1,10,28,34,63,38,3,26,1594627,16,19,1594759, +14,1310721,2,2,229,28,1,6,1,10,28,34,63,38,3,26, +1594667,16,27,1594759,14,1310721,2,2,97,28,1,6,1,10,28,34, +63,38,3,26,1594707,16,1,1594759,14,1310721,2,2,203,28,1,6, +1,10,28,34,63,38,3,26,1594747,16,55,1594759,14,1310721,2,2, +1594759,14,1,1310721,2,3,26,1310721,4,18,1310721,2,3,28,1310721,4, +1310721,2,1835009,40,1594807,16,1966081,1310721,4,1579069,12,1310721,2,4,1582587,2, +1,40,3,36,1594863,16,1582587,2,1593053,12,1,6,1,10,28,34, +63,38,1,10,28,1594865,14,1,1594909,16,1310721,2,2,1582587,2,1593275, +12,1,6,1,10,28,34,63,38,1,10,28,1594911,14,1,1594981, +16,1582599,1310721,2,2,26,1582587,2,10,3,6,10,4,8,1310721,2, +1,6,2,1,6,3,26,5,6,4,10,8,8,1590389,12,8, +1594813,14,1582599,1310721,2,2,26,1,10,3,6,10,4,8,1582577,9, +10,3,6,10,4,8,1582579,1582599,1586793,12,10,3,6,10,4,8, +1,1595049,14,1,1310721,2,3,26,1310721,4,18,1310721,2,7,28,1310721, +4,1310721,2,1835009,40,1595097,16,1966081,1310721,4,1579069,12,1,1310721,2,4, +1582581,3,10,3,6,10,4,8,3,1595773,16,1582587,2,65,28,1, +6,1,10,28,34,63,38,3,26,1595165,16,3,1595215,14,1582587,2, +19,28,1,6,1,10,28,34,63,38,3,26,1,6,1,10, +28,34,63,38,1,10,28,1595225,16,3,1595275,14,1582587,2,21,28, +1,6,1,10,28,34,63,38,3,26,1,6,1,10,28,34, +63,38,1,10,28,1595285,16,3,1595335,14,1582587,2,27,28,1,6, +1,10,28,34,63,38,3,26,1,6,1,10,28,34,63,38, +1,10,28,1595365,16,1590389,12,8,1582581,1,10,3,6,10,4,8, +1595127,14,1582587,2,95,28,1,6,1,10,28,34,63,38,1,10, +28,1595403,16,1595773,14,1590389,12,8,1582587,2,95,28,1,6,1,10, +28,34,63,38,3,26,1595549,16,1582587,2,1,40,3,36,1595513,16, +1582587,2,21,28,1,6,1,10,28,34,63,38,1,10,28,1, +6,1,10,28,34,63,38,1,10,28,1595515,14,1,1595529,16,1590389, +12,8,1595441,14,1582581,1,10,3,6,10,4,8,1595121,14,1582587,2, +85,28,1,6,1,10,28,34,63,38,3,26,1595717,16,1590389,12, +8,1582587,2,1,40,3,36,1595691,16,1582587,2,85,28,1,6,1, +10,28,34,63,38,3,26,1595681,16,1590389,12,8,1582587,2,95,28, +1,6,1,10,28,34,63,38,3,26,1595677,16,1595691,14,1595687,14, +1590389,12,8,1595587,14,1590389,12,8,1582581,1,10,3,6,10,4,8, +1595121,14,1582583,1582585,2,10,3,6,10,4,8,1582599,1,26,95,10, +3,6,10,4,8,3,1594773,12,8,1,1597057,14,1595121,14,1582583,1582585, +2,10,3,6,10,4,8,1582587,2,1,40,1595841,16,1582577,1,10, +3,6,10,4,8,1582579,1,10,3,6,10,4,8,1,1597057,14, +1582587,2,1593761,12,1596263,16,1310721,2,3,26,1,10,3,6,10,4, +8,1582587,2,97,28,1,6,1,10,28,34,63,38,3,26,1596151, +16,1590389,12,8,1582587,2,241,28,1,6,1,10,28,34,63,38, +3,26,1595951,16,3,1596001,14,1582587,2,177,28,1,6,1,10,28, +34,63,38,3,26,1,6,1,10,28,34,63,38,1,10,28, +1596151,16,1590389,12,8,1310721,2,5,26,1582587,2,1594161,12,10,3,6, +10,4,1,40,3,36,1596105,16,1310721,2,3,26,1310721,2,3,26, +2,33,30,1310721,2,5,26,2,26,10,3,6,10,4,8,1590389, +12,8,1596011,14,1582577,3,10,3,6,10,4,8,1582579,1310721,2,3, +26,2,10,3,6,10,4,8,1,1597057,14,1582587,2,1593761,12,1596217, +16,1310721,2,3,26,1310721,2,3,26,2,21,30,1582587,2,26,97, +28,10,3,6,10,4,8,1590389,12,8,1596151,14,1582577,3,10,3, +6,10,4,8,1582579,1310721,2,3,26,2,10,3,6,10,4,8, +1,1597057,14,1582587,2,1593883,12,1596473,16,1582587,2,1593883,12,1596293,16,3, +1596323,14,1582587,2,1593761,12,1,6,1,10,28,34,63,38,1,10, +28,1596407,16,1310721,2,2,2001,40,1596397,16,1582599,1310721,2,2,26,1582587, +2,10,3,6,10,4,8,1310721,2,1,6,2,1,6,3,26, +5,6,4,10,8,8,1590389,12,8,1596275,14,1582599,1310721,2,2,26, +1,10,3,6,10,4,8,1582577,5,10,3,6,10,4,8,1582579, +1582599,1586793,12,10,3,6,10,4,8,1,1597057,14,1582587,2,79,28, +1,6,1,10,28,34,63,38,3,26,1596647,16,1590389,12,8,1582579, +1594425,12,10,3,6,10,4,8,1582587,2,79,28,1,6,1,10, +28,34,63,38,1,10,28,1596619,16,1596611,14,98,97,100,32,99, +104,97,114,97,99,116,101,114,32,108,105,116,101,114,97,108, +0,1596567,1586063,12,8,1590389,12,8,1582577,3,10,3,6,10,4,8, +1,1597057,14,1582587,2,69,28,1,6,1,10,28,34,63,38,3, +26,1596939,16,1590389,12,8,1582587,2,1,40,3,36,1596757,16,1582587,2, +69,28,1,6,1,10,28,34,63,38,1,10,28,1,6,1, +10,28,34,63,38,1,10,28,1596759,14,1,1596867,16,1310721,2,3, +26,1594425,12,10,3,6,10,4,8,1310721,2,2,2001,40,1596863,16, +1582599,1310721,2,2,26,1310721,2,3,26,2,10,3,6,10,4,8, +1310721,2,1,6,2,1,6,3,26,5,6,4,10,8,8,1596685, +14,1590389,12,8,1582599,1310721,2,2,26,1,10,3,6,10,4,8, +1582577,7,10,3,6,10,4,8,1582579,1582599,1586793,12,10,3,6,10, +4,8,1,1597057,14,1582599,1,26,1582587,2,10,3,6,10,4,8, +1590389,12,8,1582599,1,26,2,1593053,12,1596993,16,3,1594773,12,1597057,14, +1582599,3,26,1,10,3,6,10,4,8,1582577,9,10,3,6,10, +4,8,1582579,1582599,1586793,12,10,3,6,10,4,8,1,1597057,14,1, +1310721,2,7,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009, +40,1597105,16,1966081,1310721,4,1579069,12,3,1310721,2,4,1584803,2,1597445,16, +1582577,1584805,1,26,2,10,3,6,10,4,8,1582579,1584813,1,26,2, +10,3,6,10,4,8,1582581,1584821,1,26,2,10,3,6,10,4, +8,1582583,1584829,1,26,2,10,3,6,10,4,8,1310721,2,2,1584803, +2,40,1597411,16,1584805,1310721,2,2,3,28,26,1584805,1310721,2,2,26, +2,10,3,6,10,4,8,1584813,1310721,2,2,3,28,26,1584813,1310721, +2,2,26,2,10,3,6,10,4,8,1584821,1310721,2,2,3,28, +26,1584821,1310721,2,2,26,2,10,3,6,10,4,8,1584829,1310721,2, +2,3,28,26,1584829,1310721,2,2,26,2,10,3,6,10,4,8, +1310721,2,1,6,2,1,6,3,26,5,6,4,10,8,8,1597209, +14,1584803,1,6,2,1,6,-1,26,5,6,4,10,8,8,1, +1597455,14,1597991,12,1597455,14,1,1310721,2,3,26,1310721,4,18,1310721,2, +11,28,1310721,4,1310721,2,1835009,40,1597503,16,1966081,1310721,4,1579069,12,1310721, +2,4,1584803,2,1310721,2,2,10,40,3,36,1597857,16,1310721,2,3, +26,1582577,2,10,3,6,10,4,8,1310721,2,5,26,1582579,2,10, +3,6,10,4,8,1310721,2,7,26,1582581,2,10,3,6,10,4, +8,1310721,2,9,26,1582583,2,10,3,6,10,4,8,1597991,12,8, +1584805,1584803,2,26,1582577,2,10,3,6,10,4,8,1584813,1584803,2,26, +1582579,2,10,3,6,10,4,8,1584821,1584803,2,26,1582581,2,10,3, +6,10,4,8,1584829,1584803,2,26,1582583,2,10,3,6,10,4,8, +1584803,1,6,2,1,6,3,26,5,6,4,10,8,8,1582577,1310721, +2,3,26,2,10,3,6,10,4,8,1582579,1310721,2,5,26,2, +10,3,6,10,4,8,1582581,1310721,2,7,26,2,10,3,6,10, +4,8,1582583,1310721,2,9,26,2,10,3,6,10,4,8,1597509,14, +1584799,1584805,1310721,2,2,26,2,10,3,6,10,4,8,1584801,1584813,1310721, +2,2,26,2,10,3,6,10,4,8,1,1597917,14,1,1310721,2, +11,26,1310721,4,18,1310721,2,1,28,1310721,4,1310721,2,1835009,40,1597965, +16,1966081,1310721,4,1579069,12,1,1597469,12,1597977,14,1,1310721,2,1,26, +1310721,4,18,1310721,2,7,28,1310721,4,1310721,2,1835009,40,1598025,16,1966081, +1310721,4,1579069,12,1310721,2,1540609,1582597,2,65,30,26,10,3,6,10, +4,8,1310721,2,2,1,26,2,3,28,1,6,1,10,28,34, +63,38,1,10,28,1598107,16,1595063,12,8,1,1598483,14,1310721,2,5, +26,1310721,2,2,5,26,2,10,3,6,10,4,8,1310721,2,5, +26,2,1310721,2,2,7,26,2,40,1598395,16,1310721,2,3,26,1310721, +2,2,3,26,2,10,3,6,10,4,8,1582577,1310721,2,3,26, +2,3,1310721,2,5,26,2,7,30,26,26,2,10,3,6,10, +4,8,1582579,1310721,2,3,26,2,5,1310721,2,5,26,2,7,30, +26,26,2,10,3,6,10,4,8,1582583,1310721,2,3,26,2,7, +1310721,2,5,26,2,7,30,26,26,2,10,3,6,10,4,8, +1582581,1,10,3,6,10,4,8,1310721,2,2,5,26,1310721,2,5, +26,2,3,26,10,3,6,10,4,8,1,1598483,14,1310721,2,2, +9,26,2,1,6,1,10,28,34,63,38,3,26,1598469,16,1582577, +1,10,3,6,10,4,8,1582579,1,10,3,6,10,4,8,1, +1598483,14,1592371,12,8,1,1598483,14,1,1310721,2,7,26,1310721,4,18, +1310721,2,3,28,1310721,4,1310721,2,1835009,40,1598531,16,1966081,1310721,4,1579069, +12,1310721,2,4,1582577,2,9,28,1,6,1,10,28,34,63,38, +3,26,1598627,16,1582579,2,1310721,2,2,28,1,6,1,10,28,34, +63,38,3,26,1,6,1,10,28,34,63,38,1,10,28,1598629, +14,1,1598635,14,1,1310721,2,3,26,1310721,4,18,1310721,2,3,28, +1310721,4,1310721,2,1835009,40,1598683,16,1966081,1310721,4,1579069,12,1310721,2,4, +1582577,2,5,28,1,6,1,10,28,34,63,38,3,26,1598779,16, +1582579,2,1310721,2,2,28,1,6,1,10,28,34,63,38,3,26, +1,6,1,10,28,34,63,38,1,10,28,1598781,14,1,1598787,14, +1,1310721,2,3,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2, +1835009,40,1598835,16,1966081,1310721,4,1579069,12,1310721,2,4,1582577,2,9,28, +1,6,1,10,28,34,63,38,3,26,1598879,16,3,1598929,14,1582577, +2,5,28,1,6,1,10,28,34,63,38,3,26,1,6,1, +10,28,34,63,38,1,10,28,1598991,16,1582579,2,1310721,2,2,28, +1,6,1,10,28,34,63,38,3,26,1,6,1,10,28,34, +63,38,1,10,28,1598993,14,1,1599009,16,1597071,12,8,3,1599017,14, +1,1599017,14,1,1310721,2,3,26,1310721,4,18,1310721,2,3,28,1310721, +4,1310721,2,1835009,40,1599065,16,1966081,1310721,4,1579069,12,1310721,2,4,1310721, +2,2,1598801,12,1,6,1,10,28,34,63,38,3,26,1599299,16, +1585091,2,1,6,1,10,28,34,63,38,3,26,1599183,16,1584991,2, +1,6,1,10,28,34,63,38,3,26,1,6,1,10,28,34, +63,38,1,10,28,1599185,14,1,1599245,16,1585093,12,8,1599219,14,101, +120,112,101,99,116,101,100,32,0,1599199,1573341,12,8,1310721,2,2, +1573341,12,8,1585237,12,8,1585091,3,10,3,6,10,4,8,1582577,2, +1,28,1,6,1,10,28,34,63,38,3,26,1599299,16,1585397,12, +8,1,1599307,14,1,1310721,2,3,26,1310721,4,18,1310721,2,3,28, +1310721,4,1310721,2,1835009,40,1599355,16,1966081,1310721,4,1579069,12,1582579,2,1310721, +2,4,1582577,2,5,28,1,6,1,10,28,34,63,38,1,10, +28,1599443,16,1599435,14,101,120,112,101,99,116,101,100,32,97,32, +110,97,109,101,0,1599403,1586063,12,8,1597071,12,8,1310721,2,2,1599461, +14,1,1310721,2,3,26,1310721,4,18,1310721,2,15,28,1310721,4,1310721, +2,1835009,40,1599509,16,1966081,1310721,4,1579069,12,1310721,2,4,1582525,2,1310721, +2,3,26,4,3,1600583,16,1310721,2,2,2,65,28,1,6,1, +10,28,34,63,38,3,26,1599605,16,1310721,2,1,6,2,1,6, +3,26,5,6,4,10,8,8,1599535,14,1310721,2,2,2,1,6, +1,10,28,34,63,38,3,26,1599641,16,1600583,14,1310721,2,5,26, +1,10,3,6,10,4,8,1310721,2,2,1310721,2,5,26,2,26, +2,1599759,16,1310721,2,2,1310721,2,5,26,2,26,2,65,28,1, +6,1,10,28,34,63,38,1,10,28,1,6,1,10,28,34, +63,38,1,10,28,1599761,14,1,1599849,16,1582599,1310721,2,5,26,2, +26,1310721,2,2,1310721,2,5,26,2,26,2,10,3,6,10,4, +8,1310721,2,5,26,1,6,2,1,6,3,26,5,6,4,10, +8,8,1599663,14,1582599,1310721,2,5,26,2,26,1,10,3,6,10, +4,8,1310721,2,1310721,2,2,1310721,2,5,26,2,26,10,3,6, +10,4,8,1310721,2,7,26,1582599,1,26,2,91,28,1,6,1, +10,28,34,63,38,3,26,1599993,16,1582599,3,26,2,1593761,12,1, +6,1,10,28,34,63,38,1,10,28,1599995,14,1,10,3,6, +10,4,8,1582599,1310721,2,7,26,2,26,2,1593761,12,1600323,16,1310721, +2,9,26,1,10,3,6,10,4,8,1310721,2,11,26,1310721,2, +7,26,2,10,3,6,10,4,8,1582599,1310721,2,11,26,2,26, +2,1600197,16,1310721,2,9,26,1310721,2,9,26,2,21,30,1582599,1310721, +2,11,26,2,26,2,26,97,28,10,3,6,10,4,8,1310721, +2,11,26,1,6,2,1,6,3,26,5,6,4,10,8,8, +1600083,14,1310721,2,7,26,2,1600245,16,1310721,2,9,26,1,1310721,2, +9,26,2,28,10,3,6,10,4,8,1542657,1582525,2,26,1310721,2, +9,26,2,1310721,2,9,26,2,26,3,26,10,3,6,10,4, +8,1582525,1,6,2,1,6,3,26,5,6,4,10,8,8,1600579, +14,1310721,2,5,26,2,3,28,1,6,1,10,28,34,63,38, +3,26,1600425,16,1310721,2,13,26,1582599,1,26,2,1600613,12,10,3, +6,10,4,1,40,3,36,1,6,1,10,28,34,63,38,1, +10,28,1600427,14,1,1600505,16,1542657,1582525,2,26,1310721,2,13,26,2, +1310721,2,13,26,2,26,10,3,6,10,4,8,1582525,1,6,2, +1,6,3,26,5,6,4,10,8,8,1600579,14,1542657,1582525,2,26, +-3,10,3,6,10,4,8,1542657,1582525,2,3,26,26,1582599,1586793,12, +10,3,6,10,4,8,1582525,1582525,2,5,26,10,3,6,10,4, +8,1599529,14,1310721,2,3,26,2,1600599,14,1,1310721,2,15,26,1310721, +4,18,1310721,2,5,28,1310721,4,1310721,2,1835009,40,1600647,16,1966081,1310721, +4,1579069,12,1310721,2,4,1,1310721,2,3,26,4,1582527,2,1310721,2, +3,26,2,26,2,1600789,16,1582527,2,1310721,2,3,26,2,26,2, +1310721,2,2,28,1,6,1,10,28,34,63,38,3,26,1600751,16, +1310721,2,3,26,2,1600803,14,1310721,2,3,26,1,6,2,1,6, +3,26,5,6,4,10,8,8,1600665,14,3,1,10,28,1600803,14, +1,1310721,2,5,26,1310721,4,18,1310721,2,9,28,1310721,4,1310721,2, +1835009,40,1600851,16,1966081,1310721,4,1579069,12,1310721,2,3,26,4,1310721,2, +4,1310721,2,2,1310721,2,3,26,2,26,1310721,2,5,26,4,1310721, +2,2,1310721,2,5,26,2,40,1601297,16,1542657,1310721,2,2,26,2, +-3,28,1,6,1,10,28,34,63,38,3,26,1601245,16,1310721,2, +7,26,1542657,1310721,2,2,3,26,26,2,1587809,12,10,3,6,10, +4,8,1310721,2,7,26,2,1,40,1601055,16,1310721,2,7,26,1542657, +1310721,2,2,3,26,26,2,1589617,12,10,3,6,10,4,8,1417729, +1310721,2,7,26,2,26,2,3,28,1,6,1,10,28,34,63, +38,3,26,1601119,16,1310721,2,7,26,2,1589741,12,8,1601215,14,1417729, +1310721,2,7,26,2,26,2,13,28,1,6,1,10,28,34,63, +38,3,26,1601193,16,45,1425921,1310721,2,7,26,2,26,2,26,1589235, +12,8,1601215,14,1425921,1310721,2,7,26,2,26,2,1589149,12,8,1310721, +2,1310721,2,2,5,26,10,3,6,10,4,8,1601293,14,1542657,1310721, +2,2,26,2,1588955,12,8,1310721,2,1,6,2,1,6,3,26, +5,6,4,10,8,8,1600895,14,1,1601305,14,1,1310721,2,9,26, +1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,1835009,40,1601353,16,1966081, +1310721,4,1579069,12,1310721,2,4,1,1310721,2,3,26,4,1310721,2,3, +26,2,1582523,2,40,1601483,16,1547777,1310721,2,3,26,2,26,2,1310721, +2,2,28,1,6,1,10,28,34,63,38,3,26,1601445,16,1, +1601545,14,1310721,2,3,26,1,6,2,1,6,3,26,5,6,4, +10,8,8,1601371,14,1547777,1582523,2,26,1310721,2,2,10,3,6,10, +4,8,1582523,1,6,2,1,6,3,26,5,6,4,10,8,8, +1,1601545,14,1,1310721,2,5,26,1310721,4,18,1310721,2,5,28,1310721, +4,1310721,2,1835009,40,1601593,16,1966081,1310721,4,1579069,12,1310721,2,4,1582517, +2,1310721,2,3,26,4,1310721,2,3,26,2,1601727,16,1310721,2,3, +26,1,6,2,1,6,-1,26,5,6,4,10,8,8,1546753,1310721, +2,3,26,2,26,2,1310721,2,2,28,1,6,1,10,28,34, +63,38,3,26,1601723,16,1310721,2,3,26,2,1601741,14,1601613,14,3, +1,10,28,1601741,14,1,1310721,2,5,26,1310721,4,18,1310721,2,5, +28,1310721,4,1310721,2,1835009,40,1601789,16,1966081,1310721,4,1579069,12,1310721,2, +4,1582519,2,1310721,2,3,26,4,1310721,2,3,26,2,1601923,16,1310721, +2,3,26,1,6,2,1,6,-1,26,5,6,4,10,8,8, +1547265,1310721,2,3,26,2,26,2,1310721,2,2,28,1,6,1,10, +28,34,63,38,3,26,1601919,16,1310721,2,3,26,2,1601937,14,1601809, +14,3,1,10,28,1601937,14,1,1310721,2,5,26,1310721,4,18,1310721, +2,5,28,1310721,4,1310721,2,1835009,40,1601985,16,1966081,1310721,4,1579069,12, +1310721,2,4,1582521,2,1310721,2,3,26,4,1310721,2,3,26,2,1602119, +16,1310721,2,3,26,1,6,2,1,6,-1,26,5,6,4,10, +8,8,1547457,1310721,2,3,26,2,26,2,1310721,2,2,28,1,6, +1,10,28,34,63,38,3,26,1602115,16,1310721,2,3,26,2,1602133, +14,1602005,14,3,1,10,28,1602133,14,1,1310721,2,5,26,1310721,4, +18,1310721,2,15,28,1310721,4,1310721,2,1835009,40,1602181,16,1966081,1310721,4, +1579069,12,1310721,2,5,26,4,1310721,2,3,26,4,1310721,2,4,1310721, +2,2,1586793,12,1310721,2,7,26,4,1310721,2,7,26,2,1601559,12, +1310721,2,9,26,4,1310721,2,9,26,2,1,40,1602321,16,1310721,2, +9,26,1582517,2,10,3,6,10,4,8,1582517,1,6,2,1,6, +3,26,5,6,4,10,8,8,1546753,1310721,2,9,26,2,26,1310721, +2,7,26,2,10,3,6,10,4,8,1546881,1310721,2,9,26,2, +26,1310721,2,3,26,2,10,3,6,10,4,8,1310721,2,3,26, +2,1,40,1602545,16,1546881,1310721,2,9,26,2,26,1,1310721,2,3, +26,2,28,10,3,6,10,4,8,1547137,1310721,2,9,26,2,26, +1310721,2,3,26,2,10,3,6,10,4,8,1547009,1310721,2,9,26, +2,26,1,10,3,6,10,4,8,1310721,2,7,26,2,1601319,12, +8,1310721,2,9,26,2,1603173,14,1547009,1310721,2,9,26,2,26,1310721, +2,5,26,2,1599475,12,10,3,6,10,4,8,1547137,1310721,2,9, +26,2,26,1582525,2,1547009,1310721,2,9,26,2,26,2,28,10,3, +6,10,4,8,1310721,2,7,26,2,1601319,12,8,1310721,2,13,26, +1310721,2,2,1573885,12,10,3,6,10,4,8,1582599,2001,26,1310721,2, +2,1310721,2,13,26,2,1574337,12,8,1582599,2001,1310721,2,13,26,2, +26,26,123,10,3,6,10,4,8,1582599,2003,1310721,2,13,26,2, +26,26,1,10,3,6,10,4,8,1310721,2,11,26,1582599,2001,26, +1586793,12,10,3,6,10,4,8,1310721,2,3,26,2,17,40,3, +36,1602829,16,3,1602885,14,1310721,2,3,26,2,11,28,1,6,1, +10,28,34,63,38,3,26,1,6,1,10,28,34,63,38,1, +10,28,1602895,16,3,1602951,14,1310721,2,3,26,2,9,28,1,6, +1,10,28,34,63,38,3,26,1,6,1,10,28,34,63,38, +1,10,28,1602961,16,3,1603017,14,1310721,2,3,26,2,7,28,1, +6,1,10,28,34,63,38,3,26,1,6,1,10,28,34,63, +38,1,10,28,1603157,16,1310721,2,11,26,2,1601951,12,1,40,1603101, +16,1547457,1582521,2,26,1310721,2,11,26,2,10,3,6,10,4,8, +1582521,1,6,2,1,6,3,26,5,6,4,10,8,8,1547585,1310721, +2,11,26,2,1601951,12,26,1310721,2,9,26,2,10,3,6,10, +4,8,1310721,2,11,26,2,1601319,12,8,1310721,2,9,26,2,1603173, +14,1,1310721,2,15,26,1310721,4,18,1310721,2,9,28,1310721,4,1310721, +2,1835009,40,1603221,16,1966081,1310721,4,1579069,12,1310721,2,3,26,4,1310721, +2,4,1310721,2,2,1586793,12,1310721,2,5,26,4,1310721,2,5,26, +2,1601755,12,1310721,2,7,26,4,1310721,2,7,26,2,1,40,1603351, +16,1310721,2,7,26,1582519,2,10,3,6,10,4,8,1582519,1,6, +2,1,6,3,26,5,6,4,10,8,8,1547265,1310721,2,7,26, +2,26,1310721,2,5,26,2,10,3,6,10,4,8,1547329,1310721,2, +7,26,2,26,1310721,2,3,26,2,1599475,12,10,3,6,10,4, +8,1547393,1310721,2,7,26,2,26,1582525,2,1547329,1310721,2,7,26,2, +26,2,28,10,3,6,10,4,8,1310721,2,5,26,2,1601319,12, +8,1310721,2,7,26,2,1603507,14,1,1310721,2,9,26,1310721,4,18, +1310721,2,9,28,1310721,4,1310721,2,1835009,40,1603555,16,1966081,1310721,4,1579069, +12,1310721,2,5,26,4,1310721,2,3,26,4,1310721,2,4,1310721,2, +2,1586793,12,11,1310721,2,3,26,2,1587311,12,1310721,2,7,26,4, +1442305,1310721,2,7,26,2,26,3,1310721,2,5,26,2,34,10,3, +6,10,4,8,1310721,2,7,26,2,1603673,14,1,1310721,2,9,26, +1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,1835009,40,1603721,16,1966081, +1310721,4,1579069,12,1310721,2,4,1584845,2,1310721,2,3,26,4,1310721,2, +3,26,2,1584847,2,10,40,1603863,16,1310721,2,3,26,1,6,2, +1,6,-1,26,5,6,4,10,8,8,1466881,1310721,2,3,26,2, +26,2,1310721,2,2,28,1,6,1,10,28,34,63,38,3,26, +1603859,16,1310721,2,3,26,2,1603877,14,1603741,14,3,1,10,28,1603877, +14,1,1310721,2,5,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721, +2,1835009,40,1603925,16,1966081,1310721,4,1579069,12,1310721,2,4,1584845,2,1310721, +2,3,26,4,1584845,2,1025,40,3,36,1604005,16,1603997,14,116,111, +111,32,109,97,110,121,32,108,111,99,97,108,115,0,1603965,1585585, +12,8,1584845,1,6,2,1,6,3,26,5,6,4,10,8,8, +1466881,1310721,2,3,26,2,26,1310721,2,2,10,3,6,10,4,8, +1467905,1310721,2,3,26,2,26,1584851,2,10,3,6,10,4,8,1468929, +1310721,2,3,26,2,26,1584849,2,10,3,6,10,4,8,1469953,1310721, +2,3,26,2,26,1,10,3,6,10,4,8,1470977,1310721,2,3, +26,2,26,1,10,3,6,10,4,8,1472001,1310721,2,3,26,2, +26,1,10,3,6,10,4,8,1473025,1310721,2,3,26,2,26,1, +10,3,6,10,4,8,1474049,1310721,2,3,26,2,26,1,10,3, +6,10,4,8,1556993,1310721,2,3,26,2,26,1584975,2,10,3,6, +10,4,8,1558017,1310721,2,3,26,2,26,1,10,3,6,10,4, +8,1310721,2,3,26,2,1604339,14,1,1310721,2,5,26,1310721,4,18, +1310721,2,3,28,1310721,4,1310721,2,1835009,40,1604387,16,1966081,1310721,4,1579069, +12,1310721,2,4,1469953,1310721,2,2,26,2,11,28,1,6,1,10, +28,34,63,38,3,26,1604465,16,1310721,2,1470977,1310721,2,2,26,2, +10,3,6,10,4,8,1604393,14,1310721,2,2,1604477,14,1,1310721,2, +3,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1604525, +16,1966081,1310721,4,1579069,12,1310721,2,4,1469953,1310721,2,2,26,2,1, +28,1,6,1,10,28,34,63,38,1,10,28,1604575,14,1,1310721, +2,3,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,1835009,40, +1604623,16,1966081,1310721,4,1579069,12,1310721,2,4,1472001,1310721,2,2,26,3, +10,3,6,10,4,8,1584853,2,1,6,1,10,28,34,63,38, +3,26,1604687,16,1,1605063,14,1310721,2,3,26,1310721,2,2,1604353,12, +10,3,6,10,4,8,1472001,1310721,2,3,26,2,26,3,10,3, +6,10,4,8,1469953,1310721,2,3,26,2,26,2,9,28,1,6, +1,10,28,34,63,38,3,26,1604871,16,1310721,2,2,1604857,14,117, +115,101,100,32,97,102,116,101,114,32,105,116,32,119,97,115, +32,103,105,118,101,110,32,97,119,97,121,0,1604799,1586469,12,8, +1,1605063,14,1469953,1310721,2,3,26,2,26,2,7,28,1,6,1, +10,28,34,63,38,3,26,1604963,16,1468929,1310721,2,3,26,2,26, +2,1584849,2,40,1,6,1,10,28,34,63,38,1,10,28,1604965, +14,1,1605055,16,1468929,1310721,2,3,26,2,26,1584849,2,10,3,6, +10,4,8,1469953,1310721,2,3,26,2,26,3,10,3,6,10,4, +8,1474049,1310721,2,3,26,2,26,3,10,3,6,10,4,8,1, +1605063,14,1,1310721,2,5,26,1310721,4,18,1310721,2,3,28,1310721,4, +1310721,2,1835009,40,1605111,16,1966081,1310721,4,1579069,12,1310721,2,4,1310721,2, +2,3,28,1,6,1,10,28,34,63,38,3,26,1605301,16,1605293, +14,97,32,110,101,119,32,111,98,106,101,99,116,32,99,97, +110,39,116,32,98,101,32,117,115,101,100,32,97,115,32,97, +32,112,108,97,105,110,32,118,97,108,117,101,59,32,98,105, +110,100,32,105,116,32,116,111,32,97,32,110,97,109,101,32, +102,105,114,115,116,0,1605155,1586351,12,8,1310721,2,2,5,40,3, +36,1605335,16,1310721,2,2,5,28,1604589,12,8,1,1605343,14,1,1310721, +2,3,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40, +1605391,16,1966081,1310721,4,1579069,12,1310721,2,4,1467905,1310721,2,2,26,2, +1590069,12,8,3,1589235,12,8,1584959,2,1589915,12,8,9,1589235,12,8, +1,1605449,14,1,1310721,2,3,26,1310721,4,18,1310721,2,9,28,1310721, +4,1310721,2,1835009,40,1605497,16,1966081,1310721,4,1579069,12,1310721,2,3,26, +4,1310721,2,4,1584845,2,1310721,2,5,26,4,1584853,2,1,6,1, +10,28,34,63,38,3,26,1605561,16,3,1605607,14,1584857,2,1,6, +1,10,28,34,63,38,3,26,1,6,1,10,28,34,63,38, +1,10,28,1605617,16,1,1606177,14,1310721,2,5,26,2,1584847,2,10, +40,1606169,16,1310721,2,5,26,1,6,2,1,6,-1,26,5,6, +4,10,8,8,1468929,1310721,2,5,26,2,26,2,1310721,2,2,40, +1605705,16,1605617,14,1310721,2,7,26,1469953,1310721,2,5,26,2,26,2, +10,3,6,10,4,8,1310721,2,7,26,2,3,28,1,6,1, +10,28,34,63,38,3,26,1605799,16,1310721,2,5,26,2,1605357,12, +8,1606165,14,1310721,2,7,26,2,5,28,1,6,1,10,28,34, +63,38,3,26,1606045,16,1310721,2,5,26,2,1606033,14,117,32,111, +98,106,101,99,116,32,109,117,115,116,32,108,101,97,118,101, +32,116,104,105,115,32,98,108,111,99,107,32,40,98,108,111, +99,107,32,118,97,108,117,101,44,32,97,115,115,105,103,110, +109,101,110,116,32,116,111,32,116,104,101,32,101,110,99,108, +111,115,105,110,103,32,98,108,111,99,107,44,32,111,114,32, +114,101,116,117,114,110,41,0,1605851,1586469,12,8,1606165,14,1310721,2, +7,26,2,7,28,1,6,1,10,28,34,63,38,3,26,1606165, +16,1310721,2,5,26,2,1606157,14,100,32,111,98,106,101,99,116, +32,119,97,115,32,110,101,118,101,114,32,103,105,118,101,110, +32,97,119,97,121,0,1606097,1586469,12,8,1605617,14,1,1606177,14,1, +1310721,2,9,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009, +40,1606225,16,1966081,1310721,4,1579069,12,1310721,2,4,1310721,2,2,1584845,2, +40,1606467,16,1584853,2,1606293,16,1474049,1310721,2,2,26,2,1,6,1, +10,28,34,63,38,1,10,28,1606295,14,1,1606357,16,1472001,1310721,2, +2,26,2,1,6,1,10,28,34,63,38,3,26,1,6,1, +10,28,34,63,38,1,10,28,1606359,14,1,1606433,16,1466881,1310721,2, +2,26,2,1606425,14,109,32,111,98,106,101,99,116,32,105,115, +32,110,101,118,101,114,32,117,115,101,100,0,1606379,1585849,12,8, +1310721,2,1,6,2,1,6,3,26,5,6,4,10,8,8,1606231, +14,1,1606475,14,1,1310721,2,3,26,1310721,4,18,1310721,2,5,28, +1310721,4,1310721,2,1835009,40,1606523,16,1966081,1310721,4,1579069,12,1310721,2,4, +1584847,2,1310721,2,3,26,4,1310721,2,2,1606191,12,8,1584849,2,3, +1605463,12,8,1310721,2,3,26,2,1310721,2,2,40,1606719,16,1468929,1310721, +2,3,26,2,26,2,1584849,2,40,3,36,1606681,16,1469953,1310721,2, +3,26,2,26,9,10,3,6,10,4,8,1468929,1310721,2,3,26, +2,26,1584849,2,3,28,10,3,6,10,4,8,1310721,2,3,26, +1,6,2,1,6,3,26,5,6,4,10,8,8,1606567,14,1584845, +1310721,2,2,10,3,6,10,4,8,1,1606747,14,1,1310721,2,5, +26,1310721,4,18,1310721,2,7,28,1310721,4,1310721,2,1835009,40,1606795,16, +1966081,1310721,4,1579069,12,1584873,2,1310721,2,4,1584847,2,1310721,2,3,26, +4,1310721,2,5,26,1507841,1310721,2,2,26,10,3,6,10,4,8, +1310721,2,5,26,2,1,26,1584847,2,10,3,6,10,4,8,1310721, +2,5,26,2,3,26,1584845,2,10,3,6,10,4,8,1310721,2, +5,26,1310721,2,5,26,2,5,26,10,3,6,10,4,8,1310721, +2,3,26,2,1584845,2,40,1607161,16,1310721,2,5,26,2,1,26, +1469953,1310721,2,3,26,2,26,2,10,3,6,10,4,8,1310721,2, +5,26,2,3,26,1470977,1310721,2,3,26,2,26,2,10,3,6, +10,4,8,1310721,2,5,26,2,5,26,1468929,1310721,2,3,26,2, +26,2,10,3,6,10,4,8,1310721,2,5,26,1310721,2,5,26, +2,7,26,10,3,6,10,4,8,1310721,2,3,26,1,6,2, +1,6,3,26,5,6,4,10,8,8,1606943,14,1584873,1310721,2,5, +26,2,1507841,28,10,3,6,10,4,8,1584873,2,16001,40,3,36, +1607285,16,1607277,14,110,101,115,116,105,110,103,32,116,111,111,32, +100,101,101,112,32,102,111,114,32,116,104,101,32,97,110,97, +108,121,122,101,114,0,1607209,1585585,12,8,1310721,2,2,1607297,14,1, +1310721,2,7,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009, +40,1607345,16,1966081,1310721,4,1579069,12,1310721,2,4,1584873,1310721,2,2,10, +3,6,10,4,8,1,1607379,14,1,1310721,2,3,26,1310721,4,18, +1310721,2,9,28,1310721,4,1310721,2,1835009,40,1607427,16,1966081,1310721,4,1579069, +12,1310721,2,4,1507841,1310721,2,2,26,1310721,2,3,26,4,1310721,2, +3,26,2,1,26,2,1310721,2,5,26,4,1310721,2,3,26,2, +3,26,2,1310721,2,7,26,4,1310721,2,7,26,2,1584845,2,10, +40,1607551,16,1310721,2,7,26,1584845,2,10,3,6,10,4,8,1310721, +2,3,26,1310721,2,3,26,2,5,26,10,3,6,10,4,8, +1310721,2,5,26,2,1310721,2,7,26,2,40,1607809,16,1469953,1310721,2, +5,26,2,26,1310721,2,3,26,2,1,26,2,10,3,6,10, +4,8,1470977,1310721,2,5,26,2,26,1310721,2,3,26,2,3,26, +2,10,3,6,10,4,8,1468929,1310721,2,5,26,2,26,1310721,2, +3,26,2,5,26,2,10,3,6,10,4,8,1310721,2,3,26, +1310721,2,3,26,2,7,26,10,3,6,10,4,8,1310721,2,5, +26,1,6,2,1,6,3,26,5,6,4,10,8,8,1607585,14, +1,1607817,14,1,1310721,2,9,26,1310721,4,18,1310721,2,3,28,1310721, +4,1310721,2,1835009,40,1607865,16,1966081,1310721,4,1579069,12,1310721,2,4,1310721, +2,2,1,28,1,6,1,10,28,34,63,38,3,26,1607911,16, +3,1607963,14,1310721,2,2,9,28,1,6,1,10,28,34,63,38, +3,26,1,6,1,10,28,34,63,38,1,10,28,1607969,14,1, +1310721,2,3,26,1310721,4,18,1310721,2,11,28,1310721,4,1310721,2,1835009, +40,1608017,16,1966081,1310721,4,1579069,12,1310721,2,4,1507841,1310721,2,2,26, +1310721,2,3,26,4,1310721,2,3,26,2,1,26,2,1310721,2,5, +26,4,1310721,2,3,26,2,3,26,2,1310721,2,7,26,4,1310721, +2,7,26,2,1584845,2,10,40,1608141,16,1310721,2,7,26,1584845,2, +10,3,6,10,4,8,1310721,2,3,26,1310721,2,3,26,2,5, +26,10,3,6,10,4,8,1310721,2,5,26,2,1310721,2,7,26, +2,40,1608765,16,1310721,2,9,26,1469953,1310721,2,5,26,2,26,2, +10,3,6,10,4,8,1310721,2,9,26,2,1607831,12,1608301,16,1310721, +2,3,26,2,1,26,2,1607831,12,1,6,1,10,28,34,63, +38,1,10,28,1608303,14,1,1608383,16,1310721,2,3,26,2,1,26, +2,9,28,1,6,1,10,28,34,63,38,3,26,1608379,16,1469953, +1310721,2,5,26,2,26,9,10,3,6,10,4,8,1608693,14,1310721, +2,9,26,2,1310721,2,3,26,2,1,26,2,28,1,6,1, +10,28,34,63,38,1,10,28,1608443,16,3,1608521,14,1468929,1310721,2, +5,26,2,26,2,1310721,2,3,26,2,5,26,2,28,1,6, +1,10,28,34,63,38,1,10,28,1,6,1,10,28,34,63, +38,1,10,28,1608531,16,3,1608675,14,1310721,2,9,26,2,11,28, +1,6,1,10,28,34,63,38,3,26,1608651,16,1470977,1310721,2,5, +26,2,26,2,1310721,2,3,26,2,3,26,2,28,1,6,1, +10,28,34,63,38,1,10,28,1,6,1,10,28,34,63,38, +1,10,28,1608653,14,1,1,6,1,10,28,34,63,38,1,10, +28,1608693,16,1310721,2,5,26,2,1608779,14,1310721,2,3,26,1310721,2, +3,26,2,7,26,10,3,6,10,4,8,1310721,2,5,26,1, +6,2,1,6,3,26,5,6,4,10,8,8,1608175,14,3,1, +10,28,1608779,14,1,1310721,2,11,26,1310721,4,18,1310721,2,3,28, +1310721,4,1310721,2,1835009,40,1608827,16,1966081,1310721,4,1579069,12,1310721,2,4, +1310721,2,2,1,28,1,6,1,10,28,34,63,38,3,26,1608889, +16,1608883,14,101,109,112,116,121,0,1608871,1609215,14,1310721,2,2,3, +28,1,6,1,10,28,34,63,38,3,26,1608937,16,1608931,14,109, +0,1608927,1609215,14,1310721,2,2,5,28,1,6,1,10,28,34,63, +38,3,26,1608985,16,1608979,14,117,0,1608975,1609215,14,1310721,2,2,7, +28,1,6,1,10,28,34,63,38,3,26,1609033,16,1609027,14,100, +0,1609023,1609215,14,1310721,2,2,9,28,1,6,1,10,28,34,63, +38,3,26,1609099,16,1609093,14,103,105,118,101,110,32,97,119,97, +121,0,1609071,1609215,14,1310721,2,2,11,28,1,6,1,10,28,34, +63,38,3,26,1609185,16,1609179,14,111,119,110,101,100,32,98,121, +32,97,32,99,111,110,116,97,105,110,101,114,0,1609137,1609215,14, +1609207,14,98,111,114,114,111,119,101,100,0,1609189,1609215,14,1,1310721, +2,3,26,1310721,4,18,1310721,2,7,28,1310721,4,1310721,2,1835009,40, +1609263,16,1966081,1310721,4,1579069,12,1310721,2,5,26,4,1310721,2,3,26, +4,1310721,2,4,1584853,2,1,6,1,10,28,34,63,38,3,26, +1609323,16,3,1609349,14,1585091,2,1,6,1,10,28,34,63,38,1, +10,28,1609359,16,3,1609385,14,1584991,2,1,6,1,10,28,34,63, +38,1,10,28,1609395,16,1,1609615,14,1585093,12,8,1466881,1310721,2,2, +26,2,1573341,12,8,1609429,14,58,32,0,1609423,1573341,12,8,1310721,2, +5,26,2,1573341,12,8,1609501,14,32,40,111,110,101,32,112,97, +116,104,32,108,101,97,118,101,115,32,105,116,32,0,1609457,1573341, +12,8,1310721,2,3,26,2,1608793,12,1573341,12,8,1609555,14,44,32, +97,110,111,116,104,101,114,32,0,1609533,1573341,12,8,1469953,1310721,2, +2,26,2,1608793,12,1573341,12,8,1609593,14,41,0,1609589,1573341,12,8, +1585237,12,8,1,1609615,14,1,1310721,2,7,26,1310721,4,18,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1310721,2,5,28,1310721,4,1310721,2,1835009,40,1609795,16,1966081,1310721,4,1579069, +12,1310721,2,3,26,4,1310721,2,4,1310721,2,3,26,2,1310721,2, +2,3,26,2,40,3,36,1609849,16,1,1609897,14,1310721,2,2,5, +7,1310721,2,3,26,2,1310721,2,2,1,26,2,28,30,26,26, +2,1609897,14,1,1310721,2,5,26,1310721,4,18,1310721,2,5,28,1310721, +4,1310721,2,1835009,40,1609945,16,1966081,1310721,4,1579069,12,1310721,2,3,26, +4,1310721,2,4,1310721,2,2,7,7,1310721,2,3,26,2,1310721,2, +2,1,26,2,28,30,26,26,2,1610009,14,1,1310721,2,5,26, +1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,1835009,40,1610057,16,1966081, +1310721,4,1579069,12,1310721,2,3,26,4,1310721,2,4,1310721,2,2,9, +7,1310721,2,3,26,2,1310721,2,2,1,26,2,28,30,26,26, +2,1610121,14,1,1310721,2,5,26,1310721,4,18,1310721,2,9,28,1310721, +4,1310721,2,1835009,40,1610169,16,1966081,1310721,4,1579069,12,1310721,2,4,1567233, +1609629,2,26,1310721,2,3,26,4,1584847,2,1310721,2,5,26,4,1609629, +2,7,26,7,1584845,2,1584847,2,28,30,26,1572353,1567233,28,40,3, +36,1610325,16,1610317,14,116,111,111,32,109,97,110,121,32,98,114, +101,97,107,115,32,102,111,114,32,116,104,101,32,97,110,97, +108,121,122,101,114,0,1610251,1585585,12,8,1310721,2,3,26,2,1, +26,1310721,2,2,10,3,6,10,4,8,1310721,2,3,26,2,3, +26,1584847,2,10,3,6,10,4,8,1310721,2,3,26,2,5,26, +1584845,2,10,3,6,10,4,8,1310721,2,7,26,1310721,2,3,26, +2,7,26,10,3,6,10,4,8,1310721,2,5,26,2,1584845,2, +40,1610669,16,1310721,2,7,26,2,1,26,1469953,1310721,2,5,26,2, +26,2,10,3,6,10,4,8,1310721,2,7,26,2,3,26,1470977, +1310721,2,5,26,2,26,2,10,3,6,10,4,8,1310721,2,7, +26,2,5,26,1468929,1310721,2,5,26,2,26,2,10,3,6,10, +4,8,1310721,2,7,26,1310721,2,7,26,2,7,26,10,3,6, +10,4,8,1310721,2,5,26,1,6,2,1,6,3,26,5,6, +4,10,8,8,1610451,14,1609629,1310721,2,7,26,2,1567233,28,10,3, +6,10,4,8,1310721,2,3,26,2,1610713,14,1,1310721,2,9,26, +1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1610761,16,1966081, +1310721,4,1579069,12,1310721,2,4,1609759,2,129,40,3,36,1610859,16,1610851, +14,116,111,111,32,109,97,110,121,32,112,97,116,104,115,32, +102,111,114,32,116,104,101,32,97,110,97,108,121,122,101,114, +0,1610787,1585585,12,8,1609631,1609759,2,26,1310721,2,2,10,3,6,10, +4,8,1609759,1,6,2,1,6,3,26,5,6,4,10,8,8, +1,1610921,14,1,1310721,2,3,26,1310721,4,18,1310721,2,11,28,1310721, +4,1310721,2,1835009,40,1610969,16,1966081,1310721,4,1579069,12,1310721,2,5,26, +4,1310721,2,3,26,4,1310721,2,4,1310721,2,2,1310721,2,5,26, +2,1609761,12,1310721,2,7,26,4,1310721,2,3,26,2,1310721,2,5, +26,2,1609761,12,1310721,2,9,26,4,1310721,2,7,26,2,1607831,12, +1611117,16,1310721,2,9,26,2,1607831,12,1,6,1,10,28,34,63, +38,1,10,28,1611119,14,1,1611129,16,3,1611441,14,1310721,2,7,26, +2,1310721,2,9,26,2,28,1,6,1,10,28,34,63,38,3, +26,1611267,16,1310721,2,2,1310721,2,5,26,2,1610023,12,1310721,2,3, +26,2,1310721,2,5,26,2,1610023,12,28,1,6,1,10,28,34, +63,38,3,26,1,6,1,10,28,34,63,38,1,10,28,1611269, +14,1,1611433,16,1310721,2,7,26,2,11,28,1,6,1,10,28, +34,63,38,1,10,28,1611319,16,3,1611407,14,1310721,2,2,1310721,2, +5,26,2,1609911,12,1310721,2,3,26,2,1310721,2,5,26,2,1609911, +12,28,1,6,1,10,28,34,63,38,3,26,1,6,1,10, +28,34,63,38,1,10,28,1,6,1,10,28,34,63,38,1, +10,28,1611435,14,1,1611441,14,1,1310721,2,11,26,1310721,4,18,1310721, +2,21,28,1310721,4,1310721,2,1835009,40,1611489,16,1966081,1310721,4,1579069,12, +1310721,2,3,26,4,1310721,2,4,1584847,2,1310721,2,5,26,4,1609759, +2,1,6,1,10,28,34,63,38,3,26,1611553,16,1,1613025,14, +1310721,2,5,26,2,1310721,2,2,40,1613017,16,1310721,2,9,26,1609631, +1,26,2,10,3,6,10,4,8,1310721,2,13,26,1310721,2,9, +26,2,1310721,2,5,26,2,1609761,12,10,3,6,10,4,8,1310721, +2,15,26,1,10,3,6,10,4,8,1310721,2,17,26,3,10, +3,6,10,4,8,1310721,2,19,26,3,1,10,28,10,3,6, +10,4,8,1310721,2,7,26,1,10,3,6,10,4,8,1310721,2, +7,26,2,1609759,2,40,1612195,16,1310721,2,11,26,1609631,1310721,2,7, +26,2,26,2,1310721,2,5,26,2,1609761,12,10,3,6,10,4, +8,1310721,2,11,26,2,9,28,1,6,1,10,28,34,63,38, +3,26,1611855,16,3,1611911,14,1310721,2,11,26,2,7,28,1,6, +1,10,28,34,63,38,3,26,1,6,1,10,28,34,63,38, +1,10,28,1611937,16,1310721,2,15,26,3,10,3,6,10,4,8, +1310721,2,11,26,2,5,28,1,6,1,10,28,34,63,38,3, +26,1611981,16,3,1612037,14,1310721,2,11,26,2,13,28,1,6,1, +10,28,34,63,38,3,26,1,6,1,10,28,34,63,38,1, +10,28,1612063,16,1310721,2,17,26,1,10,3,6,10,4,8,1310721, +2,9,26,2,1609631,1310721,2,7,26,2,26,2,1310721,2,5,26, +2,1610935,12,1,6,1,10,28,34,63,38,3,26,1612157,16,1310721, +2,19,26,1310721,2,7,26,2,10,3,6,10,4,8,1310721,2, +7,26,1,6,2,1,6,3,26,5,6,4,10,8,8,1611741, +14,1469953,1310721,2,5,26,2,26,1310721,2,13,26,2,10,3,6, +10,4,8,1470977,1310721,2,5,26,2,26,1310721,2,9,26,2,1310721, +2,5,26,2,1609911,12,10,3,6,10,4,8,1468929,1310721,2,5, +26,2,26,1310721,2,9,26,2,1310721,2,5,26,2,1610023,12,10, +3,6,10,4,8,1310721,2,19,26,2,1,40,1612537,16,1310721,2, +13,26,2,1607831,12,1612533,16,1310721,2,7,26,1,10,3,6,10, +4,8,1310721,2,7,26,2,1609759,2,40,1612533,16,1609631,1310721,2,7, +26,2,26,2,1310721,2,5,26,2,1609761,12,9,28,1,6,1, +10,28,34,63,38,3,26,1612495,16,1469953,1310721,2,5,26,2,26, +9,10,3,6,10,4,8,1310721,2,7,26,1,6,2,1,6, +3,26,5,6,4,10,8,8,1612389,14,1612979,14,1310721,2,15,26, +2,1612587,16,1310721,2,17,26,2,1,6,1,10,28,34,63,38, +1,10,28,1612589,14,1,1612625,16,1469953,1310721,2,5,26,2,26,9, +10,3,6,10,4,8,1612979,14,1584853,2,1612683,16,1585091,2,1,6, +1,10,28,34,63,38,3,26,1,6,1,10,28,34,63,38, +1,10,28,1612685,14,1,1612739,16,1584991,2,1,6,1,10,28,34, +63,38,3,26,1,6,1,10,28,34,63,38,1,10,28,1612741, +14,1,1612979,16,1585093,12,8,1466881,1310721,2,5,26,2,26,2,1573341, +12,8,1612783,14,58,32,0,1612777,1573341,12,8,1310721,2,3,26,2, +1573341,12,8,1612855,14,32,40,111,110,101,32,112,97,116,104,32, +108,101,97,118,101,115,32,105,116,32,0,1612811,1573341,12,8,1310721, +2,13,26,2,1608793,12,1573341,12,8,1612909,14,44,32,97,110,111, +116,104,101,114,32,0,1612887,1573341,12,8,1609631,1310721,2,19,26,2, +26,2,1310721,2,5,26,2,1609761,12,1608793,12,1573341,12,8,1612965,14, +41,0,1612961,1573341,12,8,1585237,12,8,1310721,2,5,26,1,6,2, +1,6,3,26,5,6,4,10,8,8,1611553,14,1,1613025,14,1, +1310721,2,21,26,1310721,4,18,1310721,2,13,28,1310721,4,1310721,2,1835009, +40,1613073,16,1966081,1310721,4,1579069,12,1310721,2,5,26,4,1310721,2,3, +26,4,1310721,2,4,1584847,2,1310721,2,7,26,4,1,1310721,2,9, +26,4,1584853,2,1,6,1,10,28,34,63,38,3,26,1613159,16, +1,1613519,14,1310721,2,7,26,2,1310721,2,3,26,2,40,1613503,16, +1310721,2,11,26,1310721,2,2,1310721,2,7,26,2,1609761,12,10,3, +6,10,4,8,1469953,1310721,2,7,26,2,26,2,9,28,1,6, +1,10,28,34,63,38,3,26,1613395,16,1310721,2,11,26,2,3, +28,1,6,1,10,28,34,63,38,3,26,1613313,16,3,1613369,14, +1310721,2,11,26,2,7,28,1,6,1,10,28,34,63,38,3, +26,1,6,1,10,28,34,63,38,1,10,28,1,6,1,10, +28,34,63,38,1,10,28,1613397,14,1,1613465,16,1310721,2,9,26, +1,6,2,1,6,3,26,5,6,4,10,8,8,1310721,2,5, +26,2,1613465,16,1310721,2,7,26,2,1605357,12,8,1310721,2,7,26, +1,6,2,1,6,3,26,5,6,4,10,8,8,1613159,14,1310721, +2,9,26,2,1613519,14,1,1310721,2,13,26,1310721,4,18,1310721,2, +11,28,1310721,4,1310721,2,1835009,40,1613567,16,1966081,1310721,4,1579069,12,1310721, +2,4,1507841,1310721,2,2,26,1310721,2,3,26,4,1310721,2,3,26, +2,1,26,2,1310721,2,5,26,4,1310721,2,3,26,2,3,26, +2,1310721,2,7,26,4,1584853,2,1,6,1,10,28,34,63,38, +3,26,1613679,16,3,1613725,14,1584857,2,1,6,1,10,28,34,63, +38,3,26,1,6,1,10,28,34,63,38,1,10,28,1613735,16, +1,1615101,14,1310721,2,7,26,2,1584845,2,10,40,1613781,16,1310721,2, +7,26,1584845,2,10,3,6,10,4,8,1310721,2,5,26,2,1310721, +2,7,26,2,40,1615093,16,1310721,2,9,26,1469953,1310721,2,5,26, +2,26,2,10,3,6,10,4,8,1310721,2,9,26,2,1607831,12, +1613915,16,1310721,2,3,26,2,1310721,2,5,26,2,1609761,12,1607831,12, +1,6,1,10,28,34,63,38,1,10,28,1613917,14,1,1614005,16, +1310721,2,3,26,2,1310721,2,5,26,2,1609761,12,9,28,1,6, +1,10,28,34,63,38,3,26,1614001,16,1469953,1310721,2,5,26,2, +26,9,10,3,6,10,4,8,1615055,14,1310721,2,9,26,2,1310721, +2,3,26,2,1310721,2,5,26,2,1609761,12,28,1,6,1,10, +28,34,63,38,1,10,28,1614073,16,3,1614159,14,1468929,1310721,2,5, +26,2,26,2,1310721,2,3,26,2,1310721,2,5,26,2,1610023,12, +28,1,6,1,10,28,34,63,38,1,10,28,1,6,1,10, +28,34,63,38,1,10,28,1614169,16,3,1614321,14,1310721,2,9,26, +2,11,28,1,6,1,10,28,34,63,38,3,26,1614297,16,1470977, +1310721,2,5,26,2,26,2,1310721,2,3,26,2,1310721,2,5,26, +2,1609911,12,28,1,6,1,10,28,34,63,38,1,10,28,1, +6,1,10,28,34,63,38,1,10,28,1614299,14,1,1,6,1, +10,28,34,63,38,1,10,28,1615055,16,1310721,2,9,26,2,9, +28,1,6,1,10,28,34,63,38,3,26,1614369,16,3,1614425,14, +1310721,2,9,26,2,7,28,1,6,1,10,28,34,63,38,3, +26,1,6,1,10,28,34,63,38,1,10,28,1614659,16,1310721,2, +5,26,2,1614647,14,105,115,32,103,105,118,101,110,32,97,119, +97,121,32,105,110,115,105,100,101,32,97,32,108,111,111,112, +32,116,104,97,116,32,99,111,109,101,115,32,98,97,99,107, +32,97,114,111,117,110,100,32,116,111,32,105,116,59,32,103, +105,118,101,32,105,116,32,97,119,97,121,32,111,110,32,97, +32,112,97,116,104,32,116,104,97,116,32,108,101,97,118,101, +115,32,116,104,101,32,108,111,111,112,0,1614443,1586469,12,8,1615055, +14,1585091,2,1,6,1,10,28,34,63,38,3,26,1614737,16,1584991, +2,1,6,1,10,28,34,63,38,3,26,1,6,1,10,28, +34,63,38,1,10,28,1614739,14,1,1615055,16,1585093,12,8,1466881,1310721, +2,5,26,2,26,2,1573341,12,8,1614949,14,58,32,99,104,97, +110,103,101,115,32,115,116,97,116,101,32,105,110,115,105,100, +101,32,97,32,108,111,111,112,59,32,101,97,99,104,32,112, +97,115,115,32,104,97,115,32,116,111,32,108,101,97,118,101, +32,105,116,32,97,115,32,105,116,32,102,111,117,110,100,32, +105,116,32,40,105,116,32,119,101,110,116,32,102,114,111,109, +32,0,1614775,1573341,12,8,1310721,2,3,26,2,1310721,2,5,26,2, +1609761,12,1608793,12,1573341,12,8,1615005,14,32,116,111,32,0,1614995,1573341, +12,8,1310721,2,9,26,2,1608793,12,1573341,12,8,1615041,14,41,0, +1615037,1573341,12,8,1585237,12,8,1310721,2,5,26,1,6,2,1,6, +3,26,5,6,4,10,8,8,1613781,14,1,1615101,14,1,1310721,2, +11,26,1310721,4,18,1310721,2,19,28,1310721,4,1310721,2,1835009,40,1615149, +16,1966081,1310721,4,1579069,12,1310721,2,11,26,4,1310721,2,9,26,4, +1310721,2,7,26,4,1310721,2,5,26,4,1310721,2,3,26,4,1310721, +2,4,1310721,2,13,26,1606761,12,10,3,6,10,4,8,1609759,1, +10,3,6,10,4,8,1310721,2,3,26,2,1615275,16,1507841,1310721,2, +2,26,1610727,12,8,1310721,2,7,26,2,1615309,16,1507841,1310721,2,13, +26,2,26,1610727,12,8,1310721,2,9,26,2,1310721,2,11,26,2, +1611455,12,8,1310721,2,7,26,2,1615381,16,1507841,1310721,2,13,26,2, +26,1310721,2,9,26,2,3,1613039,12,8,1310721,2,15,26,1,10, +3,6,10,4,8,1310721,2,3,26,2,1615463,16,1310721,2,15,26, +1507841,1310721,2,2,26,1310721,2,9,26,2,1,1613039,12,10,3,6, +10,4,8,1310721,2,15,26,2,1615581,16,1310721,2,17,26,1589535,12, +10,3,6,10,4,8,15,1589235,12,8,1310721,2,5,26,2,1584837, +2,1589317,12,8,1507841,1310721,2,2,26,1310721,2,9,26,2,3,1613039, +12,8,1310721,2,17,26,2,1584837,2,1589317,12,8,1615601,14,1310721,2, +5,26,2,1584837,2,1589317,12,8,1584857,1310721,2,3,26,2,1615623,16, +3,1615655,14,1310721,2,7,26,2,1,6,1,10,28,34,63,38, +1,10,28,10,3,6,10,4,8,1,1615675,14,1,1310721,2,19, +26,1310721,4,18,1310721,2,9,28,1310721,4,1310721,2,1835009,40,1615723,16, +1966081,1310721,4,1579069,12,1310721,2,3,26,4,1310721,2,4,1584853,2,1, +6,1,10,28,34,63,38,3,26,1615777,16,1310721,2,2,1616747,14, +1310721,2,2,5,40,1615893,16,1615875,14,111,110,108,121,32,97,32, +110,97,109,101,100,32,111,119,110,101,100,32,111,98,106,101, +99,116,32,99,97,110,32,98,101,32,114,101,99,97,115,116, +0,1615795,1586351,12,8,1310721,2,2,1616747,14,1310721,2,5,26,1310721,2, +2,5,28,10,3,6,10,4,8,1310721,2,7,26,1469953,1310721,2, +5,26,2,26,2,10,3,6,10,4,8,1472001,1310721,2,5,26, +2,26,3,10,3,6,10,4,8,1310721,2,7,26,2,11,28, +1,6,1,10,28,34,63,38,3,26,1616157,16,1310721,2,5,26, +2,1616145,14,98,101,108,111,110,103,115,32,116,111,32,97,32, +99,111,110,116,97,105,110,101,114,59,32,114,101,99,97,115, +116,32,116,104,101,32,99,111,110,116,97,105,110,101,114,32, +105,110,115,116,101,97,100,0,1616039,1586469,12,8,1616735,14,1310721,2, +7,26,2,13,28,1,6,1,10,28,34,63,38,3,26,1616285, +16,1310721,2,5,26,2,1616273,14,105,115,32,98,111,114,114,111, +119,101,100,32,97,110,100,32,99,97,110,39,116,32,98,101, +32,114,101,99,97,115,116,0,1616209,1586469,12,8,1616735,14,1310721,2, +7,26,2,1,28,1,6,1,10,28,34,63,38,3,26,1616393, +16,1310721,2,5,26,2,1616381,14,105,115,110,39,116,32,97,110, +32,111,119,110,101,100,32,111,98,106,101,99,116,0,1616337,1586469, +12,8,1616735,14,1310721,2,7,26,2,9,28,1,6,1,10,28, +34,63,38,3,26,1616515,16,1310721,2,5,26,2,1616503,14,117,115, +101,100,32,97,102,116,101,114,32,105,116,32,119,97,115,32, +103,105,118,101,110,32,97,119,97,121,0,1616445,1586469,12,8,1616735, +14,1310721,2,3,26,2,3,28,1,6,1,10,28,34,63,38, +3,26,1616615,16,1310721,2,7,26,2,3,28,1,6,1,10,28, +34,63,38,1,10,28,1,6,1,10,28,34,63,38,1,10, +28,1616617,14,1,1616699,16,1310721,2,5,26,2,1616687,14,99,97,110, +39,116,32,98,101,32,114,101,99,97,115,116,32,98,97,99, +107,32,116,111,32,109,0,1616635,1586469,12,8,1616735,14,1469953,1310721,2, +5,26,2,26,1310721,2,3,26,2,10,3,6,10,4,8,1310721, +2,2,1616747,14,1,1310721,2,9,26,1310721,4,18,1310721,2,9,28, +1310721,4,1310721,2,1835009,40,1616795,16,1966081,1310721,4,1579069,12,1310721,2,3, +26,4,1310721,2,4,1584853,2,1,6,1,10,28,34,63,38,3, +26,1616845,16,1,1618279,14,1310721,2,2,3,28,1,6,1,10,28, +34,63,38,3,26,1617089,16,1310721,2,3,26,2,5,28,1,6, +1,10,28,34,63,38,1,10,28,1617083,16,1617075,14,97,32,110, +101,119,32,111,98,106,101,99,116,32,99,97,110,32,111,110, +108,121,32,103,111,32,115,116,114,97,105,103,104,116,32,116, +111,32,97,32,100,32,112,97,114,97,109,101,116,101,114,59, +32,98,105,110,100,32,105,116,32,116,111,32,97,32,110,97, +109,101,32,102,105,114,115,116,0,1616923,1586351,12,8,1,1618279,14, +1310721,2,2,5,40,1617109,16,3,1617165,14,1310721,2,2,5,28,1604491, +12,1,6,1,10,28,34,63,38,3,26,1,6,1,10,28, +34,63,38,1,10,28,1617175,16,1,1618279,14,1310721,2,5,26,1310721, +2,2,5,28,10,3,6,10,4,8,1310721,2,7,26,1469953,1310721, +2,5,26,2,26,2,10,3,6,10,4,8,1469953,1310721,2,5, +26,2,1604353,12,26,2,9,28,1,6,1,10,28,34,63,38, +3,26,1617375,16,1310721,2,5,26,2,1617361,14,117,115,101,100,32, +97,102,116,101,114,32,105,116,32,119,97,115,32,103,105,118, +101,110,32,97,119,97,121,0,1617303,1586469,12,8,1,1618279,14,1310721, +2,3,26,2,3,10,40,3,36,1617603,16,1310721,2,7,26,2, +7,28,1,6,1,10,28,34,63,38,3,26,1617581,16,1310721,2, +5,26,2,1617567,14,105,115,32,100,59,32,112,97,115,115,32, +105,116,32,116,111,32,97,32,100,32,112,97,114,97,109,101, +116,101,114,32,40,108,101,110,100,105,110,103,32,105,116,32, +119,111,117,108,100,32,108,111,115,101,32,105,116,41,0,1617451, +1586469,12,8,1,1618279,14,1310721,2,5,26,2,1604589,12,8,1,1618279, +14,1310721,2,7,26,2,7,28,1,6,1,10,28,34,63,38, +3,26,1617703,16,1469953,1310721,2,5,26,2,26,9,10,3,6,10, +4,8,1472001,1310721,2,5,26,2,26,3,10,3,6,10,4,8, +1,1618279,14,1310721,2,7,26,2,3,28,1,6,1,10,28,34, +63,38,3,26,1617831,16,1310721,2,5,26,2,1617819,14,105,115,32, +109,59,32,119,114,105,116,101,32,100,42,120,32,116,111,32, +103,105,118,101,32,105,116,32,97,119,97,121,0,1617755,1586469,12, +8,1618271,14,1310721,2,7,26,2,5,28,1,6,1,10,28,34, +63,38,3,26,1618007,16,1310721,2,5,26,2,1617995,14,105,115,32, +117,59,32,119,114,105,116,101,32,100,42,120,32,116,111,32, +103,105,118,101,32,105,116,32,97,119,97,121,32,105,110,115, +116,101,97,100,32,111,102,32,114,101,116,117,114,110,105,110, +103,32,105,116,0,1617883,1586469,12,8,1618271,14,1310721,2,7,26,2, +11,28,1,6,1,10,28,34,63,38,3,26,1618177,16,1310721,2, +5,26,2,1618165,14,98,101,108,111,110,103,115,32,116,111,32, +97,32,99,111,110,116,97,105,110,101,114,32,97,110,100,32, +99,97,110,39,116,32,98,101,32,103,105,118,101,110,32,97, +119,97,121,32,97,108,111,110,101,0,1618059,1586469,12,8,1618271,14, +1310721,2,5,26,2,1618263,14,105,115,32,98,111,114,114,111,119, +101,100,32,97,110,100,32,99,97,110,39,116,32,98,101,32, +103,105,118,101,110,32,97,119,97,121,0,1618191,1586469,12,8,1, +1618279,14,1,1310721,2,9,26,1310721,4,18,1310721,2,3,28,1310721,4, +1310721,2,1835009,40,1618327,16,1966081,1310721,4,1579069,12,1310721,2,4,1467905,1310721, +2,2,26,2,1590069,12,8,3,1589235,12,8,1584961,2,1589915,12,8, +1,1618377,14,1,1310721,2,3,26,1310721,4,18,1310721,2,15,28,1310721, +4,1310721,2,1835009,40,1618425,16,1966081,1310721,4,1579069,12,1310721,2,5,26, +4,1310721,2,3,26,4,1310721,2,4,1584853,2,1,6,1,10,28, +34,63,38,3,26,1618485,16,1,1621915,14,1310721,2,9,26,3,1, +10,28,10,3,6,10,4,8,1310721,2,5,26,2,5,40,3, +36,1618579,16,1310721,2,5,26,2,5,28,1604491,12,1,6,1,10, +28,34,63,38,1,10,28,1618581,14,1,1618619,16,1310721,2,9,26, +1310721,2,5,26,2,5,28,10,3,6,10,4,8,1310721,2,9, +26,2,1,40,3,36,1618711,16,1469953,1310721,2,9,26,2,1604353,12, +26,2,9,28,1,6,1,10,28,34,63,38,3,26,1,6, +1,10,28,34,63,38,1,10,28,1618713,14,1,1618803,16,1310721,2, +9,26,2,1618789,14,117,115,101,100,32,97,102,116,101,114,32, +105,116,32,119,97,115,32,103,105,118,101,110,32,97,119,97, +121,0,1618731,1586469,12,8,1,1621915,14,1310721,2,2,3,28,1,6, +1,10,28,34,63,38,3,26,1620259,16,1310721,2,7,26,1469953,1310721, +2,3,26,2,26,2,10,3,6,10,4,8,1310721,2,9,26, +2,1,40,1618953,16,1310721,2,5,26,2,3,28,1,6,1,10, +28,34,63,38,1,10,28,1,6,1,10,28,34,63,38,1, +10,28,1618955,14,1,1619159,16,1310721,2,7,26,2,1607831,12,1,6, +1,10,28,34,63,38,3,26,1619125,16,1310721,2,3,26,2,1619113, +14,115,116,105,108,108,32,111,119,110,115,32,97,110,32,111, +98,106,101,99,116,59,32,111,118,101,114,119,114,105,116,105, +110,103,32,105,116,32,119,111,117,108,100,32,108,101,97,107, +32,105,116,0,1619011,1586469,12,8,1619153,14,1469953,1310721,2,3,26,2, +26,1,10,3,6,10,4,8,1,1621915,14,1310721,2,7,26,2, +1607831,12,1,6,1,10,28,34,63,38,3,26,1619327,16,1310721,2, +3,26,2,1619313,14,115,116,105,108,108,32,111,119,110,115,32, +97,110,32,111,98,106,101,99,116,59,32,111,118,101,114,119, +114,105,116,105,110,103,32,105,116,32,119,111,117,108,100,32, +108,101,97,107,32,105,116,0,1619211,1586469,12,8,1,1621915,14,1310721, +2,5,26,2,3,28,1,6,1,10,28,34,63,38,3,26, +1619517,16,1469953,1310721,2,3,26,2,26,3,10,3,6,10,4,8, +1472001,1310721,2,3,26,2,26,1,10,3,6,10,4,8,1474049,1310721, +2,3,26,2,26,3,10,3,6,10,4,8,1558017,1310721,2,3, +26,2,26,2,1619511,16,1469953,1310721,2,3,26,2,26,1558017,1310721,2, +3,26,2,26,2,10,3,6,10,4,8,1,1621915,14,1310721,2, +13,26,1469953,1310721,2,9,26,2,26,2,10,3,6,10,4,8, +1310721,2,13,26,2,5,28,1,6,1,10,28,34,63,38,3, +26,1619675,16,1468929,1310721,2,3,26,2,26,2,1468929,1310721,2,9,26, +2,26,2,3,28,28,1,6,1,10,28,34,63,38,3,26, +1,6,1,10,28,34,63,38,1,10,28,1619677,14,1,1619889,16, +1469953,1310721,2,3,26,2,26,3,10,3,6,10,4,8,1472001,1310721, +2,3,26,2,26,1,10,3,6,10,4,8,1474049,1310721,2,3, +26,2,26,3,10,3,6,10,4,8,1469953,1310721,2,9,26,2, +26,9,10,3,6,10,4,8,1472001,1310721,2,9,26,2,26,3, +10,3,6,10,4,8,1558017,1310721,2,3,26,2,26,2,1619883,16, +1469953,1310721,2,3,26,2,26,1558017,1310721,2,3,26,2,26,2,10, +3,6,10,4,8,1,1621915,14,1310721,2,13,26,2,5,28,1, +6,1,10,28,34,63,38,3,26,1620109,16,1310721,2,9,26,2, +1620097,14,105,115,32,117,59,32,105,116,32,99,97,110,32,111, +110,108,121,32,98,101,32,104,97,110,100,101,100,32,116,111, +32,97,32,118,97,114,105,97,98,108,101,32,111,102,32,116, +104,101,32,101,110,99,108,111,115,105,110,103,32,98,108,111, +99,107,44,32,111,114,32,114,101,116,117,114,110,101,100,0, +1619941,1586469,12,8,1620253,14,1310721,2,9,26,2,1620245,14,105,115,32, +97,110,32,111,119,110,101,100,32,111,98,106,101,99,116,32, +97,110,100,32,99,97,110,39,116,32,98,101,32,99,111,112, +105,101,100,32,105,110,116,111,32,97,110,111,116,104,101,114, +32,118,97,114,105,97,98,108,101,0,1620123,1586469,12,8,1,1621915, +14,1310721,2,2,7,28,1,6,1,10,28,34,63,38,3,26, +1621583,16,1310721,2,11,26,1310721,2,3,26,2,1604353,12,10,3,6, +10,4,8,1310721,2,7,26,1469953,1310721,2,11,26,2,26,2,10, +3,6,10,4,8,1310721,2,3,26,2,1604589,12,8,1310721,2,5, +26,2,3,28,1,6,1,10,28,34,63,38,3,26,1620439,16, +1310721,2,3,26,2,1618293,12,8,1,1621915,14,1310721,2,9,26,2, +1,40,1620463,16,1,1621915,14,1310721,2,13,26,1469953,1310721,2,9,26, +2,26,2,10,3,6,10,4,8,1310721,2,13,26,2,11,28, +1,6,1,10,28,34,63,38,3,26,1620625,16,1310721,2,9,26, +2,1620613,14,97,108,114,101,97,100,121,32,98,101,108,111,110, +103,115,32,116,111,32,97,32,99,111,110,116,97,105,110,101, +114,0,1620551,1586469,12,8,1621577,14,1310721,2,13,26,2,13,28,1, +6,1,10,28,34,63,38,3,26,1620753,16,1310721,2,9,26,2, +1620741,14,105,115,32,98,111,114,114,111,119,101,100,32,97,110, +100,32,99,97,110,39,116,32,98,101,32,115,116,111,114,101, +100,0,1620677,1586469,12,8,1621577,14,1310721,2,7,26,2,13,28,1, +6,1,10,28,34,63,38,3,26,1621079,16,1310721,2,13,26,2, +5,28,1,6,1,10,28,34,63,38,3,26,1621003,16,1310721,2, +9,26,2,1620991,14,105,115,32,117,59,32,105,116,32,104,97, +115,32,116,111,32,108,101,97,118,101,32,116,104,114,111,117, +103,104,32,116,104,101,32,98,108,111,99,107,32,97,98,111, +118,101,44,32,110,111,116,32,105,110,116,111,32,97,32,98, +111,114,114,111,119,101,100,32,111,98,106,101,99,116,0,1620843, +1586469,12,8,1621075,14,1469953,1310721,2,9,26,2,26,9,10,3,6, +10,4,8,1472001,1310721,2,9,26,2,26,3,10,3,6,10,4, +8,1310721,2,3,26,2,1618293,12,8,1621577,14,1310721,2,13,26,2, +1310721,2,7,26,2,28,1,6,1,10,28,34,63,38,1,10, +28,1621285,16,1310721,2,9,26,2,1621273,14,105,116,115,32,116,97, +103,32,100,111,101,115,110,39,116,32,109,97,116,99,104,32, +105,116,115,32,99,111,110,116,97,105,110,101,114,39,115,32, +40,98,111,116,104,32,104,97,118,101,32,116,111,32,98,101, +32,109,44,32,117,32,111,114,32,100,41,0,1621141,1586469,12,8, +1621577,14,1468929,1310721,2,9,26,2,26,2,1468929,1310721,2,11,26,2, +26,2,40,1621469,16,1310721,2,9,26,2,1621457,14,108,105,118,101, +115,32,105,110,32,97,110,32,111,117,116,101,114,32,98,108, +111,99,107,32,116,104,97,110,32,116,104,101,32,99,111,110, +116,97,105,110,101,114,32,105,116,39,115,32,115,116,111,114, +101,100,32,105,110,116,111,0,1621337,1586469,12,8,1621577,14,1469953,1310721, +2,9,26,2,26,11,10,3,6,10,4,8,1470977,1310721,2,9, +26,2,26,1310721,2,3,26,2,10,3,6,10,4,8,1472001,1310721, +2,9,26,2,26,3,10,3,6,10,4,8,1310721,2,3,26, +2,1618293,12,8,1,1621915,14,1310721,2,9,26,2,1,40,3,36, +1621907,16,1469953,1310721,2,9,26,2,26,2,7,28,1,6,1,10, +28,34,63,38,3,26,1621709,16,1469953,1310721,2,9,26,2,26,9, +10,3,6,10,4,8,1472001,1310721,2,9,26,2,26,3,10,3, +6,10,4,8,1621907,14,1310721,2,9,26,2,1621899,14,109,117,115, +116,32,98,101,32,100,32,116,111,32,98,101,32,115,116,111, +114,101,100,32,105,110,32,97,32,103,108,111,98,97,108,32, +111,114,32,105,110,32,117,110,116,114,97,99,107,101,100,32, +109,101,109,111,114,121,32,40,116,104,101,32,99,111,108,108, +101,99,116,111,114,32,116,97,107,101,115,32,105,116,32,111, +118,101,114,41,0,1621723,1586469,12,8,1,1621915,14,1,1310721,2,15, +26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,1835009,40,1621963,16, +1966081,1310721,4,1579069,12,1310721,2,4,1584853,2,1,6,1,10,28,34, +63,38,3,26,1622003,16,1,1622897,14,1310721,2,3,26,3,1,10, +28,10,3,6,10,4,8,1310721,2,2,5,40,3,36,1622089,16, +1310721,2,2,5,28,1604491,12,1,6,1,10,28,34,63,38,1, +10,28,1622091,14,1,1622125,16,1310721,2,3,26,1310721,2,2,5,28, +10,3,6,10,4,8,1310721,2,3,26,2,1,40,3,36,1622217, +16,1469953,1310721,2,3,26,2,1604353,12,26,2,9,28,1,6,1, +10,28,34,63,38,3,26,1,6,1,10,28,34,63,38,1, +10,28,1622219,14,1,1622309,16,1310721,2,3,26,2,1622295,14,117,115, +101,100,32,97,102,116,101,114,32,105,116,32,119,97,115,32, +103,105,118,101,110,32,97,119,97,121,0,1622237,1586469,12,8,1, +1622897,14,1584855,2,1622565,16,1310721,2,3,26,2,1,40,3,36,1622559, +16,1469953,1310721,2,3,26,2,26,2,5,28,1,6,1,10,28, +34,63,38,3,26,1622443,16,1469953,1310721,2,3,26,2,26,9,10, +3,6,10,4,8,1472001,1310721,2,3,26,2,26,3,10,3,6, +10,4,8,1622559,14,1310721,2,3,26,2,1622551,14,104,97,115,32, +116,111,32,98,101,32,117,32,116,111,32,98,101,32,114,101, +116,117,114,110,101,100,59,32,119,114,105,116,101,32,39,114, +101,116,117,114,110,32,117,42,120,39,0,1622457,1586469,12,8,1, +1622897,14,1310721,2,2,3,28,1,6,1,10,28,34,63,38,3, +26,1622723,16,1622711,14,114,101,116,117,114,110,115,32,97,32,110, +101,119,32,111,98,106,101,99,116,59,32,100,101,99,108,97, +114,101,32,116,104,101,32,102,117,110,99,116,105,111,110,32, +119,105,116,104,32,45,62,32,46,117,0,1622603,1586351,12,8,1622889, +14,1310721,2,3,26,2,1,40,3,36,1622889,16,1310721,2,3,26, +2,1622881,14,105,115,32,111,119,110,101,100,32,97,110,100,32, +99,97,110,39,116,32,98,101,32,114,101,116,117,114,110,101, +100,32,102,114,111,109,32,97,32,102,117,110,99,116,105,111, +110,32,119,105,116,104,111,117,116,32,45,62,32,46,117,0, +1622759,1586469,12,8,1,1622897,14,1,1310721,2,5,26,1310721,4,18,1310721, +2,5,28,1310721,4,1310721,2,1835009,40,1622945,16,1966081,1310721,4,1579069,12, +1310721,2,4,1584853,2,1,6,1,10,28,34,63,38,3,26,1622985, +16,1,1623613,14,1310721,2,2,3,28,1,6,1,10,28,34,63, +38,3,26,1623025,16,3,1623613,14,1310721,2,2,5,40,1623045,16,3, +1623101,14,1310721,2,2,5,28,1604491,12,1,6,1,10,28,34,63, +38,3,26,1,6,1,10,28,34,63,38,1,10,28,1623111,16, +1,1623613,14,1310721,2,3,26,1310721,2,2,5,28,10,3,6,10, +4,8,1469953,1310721,2,3,26,2,26,2,5,28,1,6,1,10, +28,34,63,38,3,26,1623253,16,1468929,1310721,2,3,26,2,26,2, +1584849,2,28,1,6,1,10,28,34,63,38,3,26,1,6,1, +10,28,34,63,38,1,10,28,1623255,14,1,1623321,16,1469953,1310721,2, +3,26,2,26,9,10,3,6,10,4,8,1472001,1310721,2,3,26, +2,26,3,10,3,6,10,4,8,3,1623613,14,1469953,1310721,2,3, +26,2,26,2,5,28,1,6,1,10,28,34,63,38,3,26, +1623485,16,1310721,2,3,26,2,1623473,14,111,110,108,121,32,97,32, +117,32,111,98,106,101,99,116,32,111,102,32,116,104,105,115, +32,98,108,111,99,107,32,99,97,110,32,98,101,32,105,116, +115,32,118,97,108,117,101,0,1623379,1586469,12,8,1623605,14,1310721,2, +3,26,2,1623597,14,104,97,115,32,116,111,32,98,101,32,117, +32,116,111,32,98,101,32,104,97,110,100,101,100,32,117,112, +32,97,115,32,116,104,101,32,98,108,111,99,107,39,115,32, +118,97,108,117,101,0,1623499,1586469,12,8,1,1623613,14,1,1310721,2, +5,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1623661, +16,1966081,1310721,4,1579069,12,1310721,2,4,1310721,2,2,3,28,1,6, +1,10,28,34,63,38,3,26,1623817,16,1623805,14,97,32,110,101, +119,32,111,98,106,101,99,116,32,40,111,114,32,97,32,117, +32,114,101,115,117,108,116,41,32,99,97,110,39,116,32,98, +101,32,116,104,114,111,119,110,32,97,119,97,121,0,1623705,1586351, +12,8,1623851,14,1310721,2,2,5,40,3,36,1623851,16,1310721,2,2, +5,28,1604589,12,8,1,1623859,14,1,1310721,2,3,26,1310721,4,18, +1310721,2,5,28,1310721,4,1310721,2,1835009,40,1623907,16,1966081,1310721,4,1579069, +12,1310721,2,4,1584971,2,1310721,2,3,26,4,1584971,2,1310721,2,2, +26,1310721,40,3,36,1624007,16,1623999,14,116,104,101,32,116,121,112, +101,32,97,114,101,97,32,105,115,32,102,117,108,108,0,1623955, +1585585,12,8,1584971,1584971,2,1310721,2,2,26,10,3,6,10,4,8, +1310721,2,3,26,2,1,1310721,2,2,1574549,12,8,1310721,2,3,26, +2,1624073,14,1,1310721,2,5,26,1310721,4,18,1310721,2,11,28,1310721, +4,1310721,2,1835009,40,1624121,16,1966081,1310721,4,1579069,12,1310721,2,7,26, +4,1310721,2,5,26,4,1310721,2,3,26,4,1310721,2,4,15,1310721, +2,7,26,2,26,1623873,12,1310721,2,9,26,4,1310721,2,9,26, +2,1,26,1310721,2,2,10,3,6,10,4,8,1310721,2,9,26, +2,3,26,1310721,2,3,26,2,10,3,6,10,4,8,1310721,2, +9,26,2,5,26,1310721,2,5,26,2,10,3,6,10,4,8, +1584973,2,2049,40,3,36,1624347,16,1624339,14,116,111,111,32,109,97, +110,121,32,116,121,112,101,115,0,1624309,1585585,12,8,1277953,1584973,2, +26,1310721,2,9,26,2,10,3,6,10,4,8,1584973,1,6,2, +1,6,3,26,5,6,4,10,8,8,1310721,2,9,26,2,1624421, +14,1,1310721,2,11,26,1310721,4,18,1310721,2,7,28,1310721,4,1310721, +2,1835009,40,1624469,16,1966081,1310721,4,1579069,12,1310721,2,4,1,1310721,2, +3,26,4,1310721,2,3,26,2,1584973,2,40,1624715,16,1310721,2,5, +26,1277953,1310721,2,3,26,2,26,2,10,3,6,10,4,8,1310721, +2,5,26,2,1,26,2,11,28,1,6,1,10,28,34,63, +38,3,26,1624657,16,1310721,2,5,26,2,9,26,2,1310721,2,2, +28,1,6,1,10,28,34,63,38,3,26,1,6,1,10,28, +34,63,38,1,10,28,1624659,14,1,1624677,16,1310721,2,5,26,2, +1624795,14,1310721,2,3,26,1,6,2,1,6,3,26,5,6,4, +10,8,8,1624487,14,1310721,2,5,26,11,3,1,1,1624087,12,10, +3,6,10,4,8,1310721,2,5,26,2,9,26,1310721,2,2,10, +3,6,10,4,8,1310721,2,5,26,2,1624795,14,1,1310721,2,7, +26,1310721,4,18,1310721,2,9,28,1310721,4,1310721,2,1835009,40,1624843,16, +1966081,1310721,4,1579069,12,1310721,2,3,26,4,1310721,2,4,1,1310721,2, +5,26,4,1310721,2,5,26,2,1584973,2,40,1625179,16,1310721,2,7, +26,1277953,1310721,2,5,26,2,26,2,10,3,6,10,4,8,1310721, +2,7,26,2,1,26,2,13,28,1,6,1,10,28,34,63, +38,3,26,1625041,16,1310721,2,7,26,2,9,26,2,1310721,2,2, +28,1,6,1,10,28,34,63,38,3,26,1,6,1,10,28, +34,63,38,1,10,28,1625043,14,1,1625121,16,1310721,2,7,26,2, +7,26,2,1310721,2,3,26,2,28,1,6,1,10,28,34,63, +38,3,26,1,6,1,10,28,34,63,38,1,10,28,1625123,14, +1,1625141,16,1310721,2,7,26,2,1625315,14,1310721,2,5,26,1,6, +2,1,6,3,26,5,6,4,10,8,8,1624871,14,1310721,2,7, +26,13,1310721,2,3,26,2,1310721,2,2,1628577,12,30,1,1,1624087, +12,10,3,6,10,4,8,1310721,2,7,26,2,9,26,1310721,2, +2,10,3,6,10,4,8,1310721,2,7,26,2,7,26,1310721,2, +3,26,2,10,3,6,10,4,8,1310721,2,7,26,2,1625315,14, +1,1310721,2,9,26,1310721,4,18,1310721,2,15,28,1310721,4,1310721,2, +1835009,40,1625363,16,1966081,1310721,4,1579069,12,1310721,2,7,26,4,1310721,2, +5,26,4,1310721,2,3,26,4,1310721,2,4,1,1310721,2,9,26, +4,1310721,2,9,26,2,1584973,2,40,1626037,16,1310721,2,13,26,1277953, +1310721,2,9,26,2,26,2,10,3,6,10,4,8,1310721,2,13, +26,2,1,26,2,15,28,1,6,1,10,28,34,63,38,3, +26,1625581,16,1310721,2,13,26,2,9,26,2,1310721,2,2,28,1, +6,1,10,28,34,63,38,3,26,1,6,1,10,28,34,63, +38,1,10,28,1625583,14,1,1625661,16,1310721,2,13,26,2,7,26, +2,1310721,2,3,26,2,28,1,6,1,10,28,34,63,38,3, +26,1,6,1,10,28,34,63,38,1,10,28,1625663,14,1,1625741, +16,1310721,2,13,26,2,11,26,2,1310721,2,7,26,2,28,1, +6,1,10,28,34,63,38,3,26,1,6,1,10,28,34,63, +38,1,10,28,1625743,14,1,1625999,16,1310721,2,11,26,1,10,3, +6,10,4,8,1310721,2,11,26,2,1310721,2,3,26,2,40,1625895, +16,1310721,2,13,26,2,15,1310721,2,11,26,2,26,26,2,1310721, +2,5,26,2,1310721,2,11,26,2,26,2,28,1,6,1,10, +28,34,63,38,3,26,1,6,1,10,28,34,63,38,1,10, +28,1625897,14,1,1625939,16,1310721,2,11,26,1,6,2,1,6,3, +26,5,6,4,10,8,8,1625769,14,1310721,2,11,26,2,1310721,2, +3,26,2,28,1,6,1,10,28,34,63,38,3,26,1625999,16, +1310721,2,13,26,2,1626237,14,1310721,2,9,26,1,6,2,1,6, +3,26,5,6,4,10,8,8,1625411,14,1310721,2,13,26,15,3, +1,1310721,2,3,26,2,1624087,12,10,3,6,10,4,8,1310721,2, +13,26,2,9,26,1310721,2,2,10,3,6,10,4,8,1310721,2, +13,26,2,7,26,1310721,2,3,26,2,10,3,6,10,4,8, +1310721,2,13,26,2,11,26,1310721,2,7,26,2,10,3,6,10, +4,8,1310721,2,13,26,2,15,26,1310721,2,5,26,2,1310721,2, +3,26,2,1574337,12,8,1310721,2,13,26,2,1626237,14,1,1310721,2, +15,26,1310721,4,18,1310721,2,9,28,1310721,4,1310721,2,1835009,40,1626285, +16,1966081,1310721,4,1579069,12,1310721,2,3,26,4,1310721,2,4,1,1310721, +2,5,26,4,1310721,2,5,26,2,1584973,2,40,1626541,16,1310721,2, +7,26,1277953,1310721,2,5,26,2,26,2,10,3,6,10,4,8, +1310721,2,7,26,2,1,26,2,19,28,1,6,1,10,28,34, +63,38,3,26,1626483,16,1310721,2,7,26,2,7,26,2,1310721,2, +2,28,1,6,1,10,28,34,63,38,3,26,1,6,1,10, +28,34,63,38,1,10,28,1626485,14,1,1626503,16,1310721,2,7,26, +2,1626629,14,1310721,2,5,26,1,6,2,1,6,3,26,5,6, +4,10,8,8,1626313,14,1310721,2,7,26,19,3,1310721,2,3,26, +2,1,1624087,12,10,3,6,10,4,8,1310721,2,7,26,2,7, +26,1310721,2,2,10,3,6,10,4,8,1310721,2,7,26,2,1626629, +14,1,1310721,2,9,26,1310721,4,18,1310721,2,15,28,1310721,4,1310721, +2,1835009,40,1626677,16,1966081,1310721,4,1579069,12,1310721,2,5,26,4,1310721, +2,3,26,4,1310721,2,4,1,1310721,2,7,26,4,1310721,2,7, +26,2,1584973,2,40,1627217,16,1310721,2,11,26,1277953,1310721,2,7,26, +2,26,2,10,3,6,10,4,8,1310721,2,11,26,2,1,26, +2,17,28,1,6,1,10,28,34,63,38,3,26,1626885,16,1310721, +2,11,26,2,11,26,2,1310721,2,2,28,1,6,1,10,28, +34,63,38,3,26,1,6,1,10,28,34,63,38,1,10,28, +1626887,14,1,1627179,16,1310721,2,13,26,1310721,2,11,26,2,13,26, +2,10,3,6,10,4,8,1310721,2,9,26,1,10,3,6,10, +4,8,1310721,2,9,26,2,1310721,2,5,26,2,40,1627075,16,1310721, +2,13,26,2,3,1310721,2,9,26,2,26,26,2,1310721,2,3, +26,2,1310721,2,9,26,2,26,2,28,1,6,1,10,28,34, +63,38,3,26,1,6,1,10,28,34,63,38,1,10,28,1627077, +14,1,1627119,16,1310721,2,9,26,1,6,2,1,6,3,26,5, +6,4,10,8,8,1626949,14,1310721,2,9,26,2,1310721,2,5,26, +2,28,1,6,1,10,28,34,63,38,3,26,1627179,16,1310721,2, +11,26,2,1627491,14,1310721,2,7,26,1,6,2,1,6,3,26, +5,6,4,10,8,8,1626715,14,1310721,2,13,26,1310721,2,5,26, +2,3,26,1623873,12,10,3,6,10,4,8,1310721,2,13,26,2, +1,26,1310721,2,5,26,2,10,3,6,10,4,8,1310721,2,13, +26,2,3,26,1310721,2,3,26,2,1310721,2,5,26,2,1574337,12, +8,1310721,2,11,26,17,1,1310721,2,2,5,26,2,1,1624087,12, +10,3,6,10,4,8,1310721,2,11,26,2,7,26,3,1,10, +28,10,3,6,10,4,8,1310721,2,11,26,2,11,26,1310721,2, +2,10,3,6,10,4,8,1310721,2,11,26,2,13,26,1310721,2, +13,26,2,10,3,6,10,4,8,1310721,2,11,26,2,1627491,14, +1,1310721,2,15,26,1310721,4,18,1310721,2,33,28,1310721,4,1310721,2, +1835009,40,1627539,16,1966081,1310721,4,1579069,12,1310721,2,4,1,1310721,2,13, +26,4,1310721,2,2,1,26,2,17,28,1,6,1,10,28,34, +63,38,1,10,28,1627605,16,3,1627647,14,1310721,2,2,7,26,2, +1,40,3,36,1,6,1,10,28,34,63,38,1,10,28,1627661, +16,1310721,2,2,1628563,14,1310721,2,2,7,26,2,5,1,10,28, +28,1,6,1,10,28,34,63,38,3,26,1627831,16,1627789,14,97, +32,115,116,114,117,99,116,32,99,97,110,39,116,32,99,111, +110,116,97,105,110,32,105,116,115,101,108,102,32,98,121,32, +118,97,108,117,101,0,1627711,1585679,12,8,1310721,2,2,7,26,1, +10,3,6,10,4,8,1310721,2,2,1628563,14,1310721,2,2,7,26, +5,1,10,28,10,3,6,10,4,8,1310721,2,3,26,1310721,2, +2,11,26,2,10,3,6,10,4,8,1310721,2,7,26,1310721,2, +3,26,2,11,26,2,10,3,6,10,4,8,1310721,2,5,26, +1310721,2,3,26,2,9,26,2,10,3,6,10,4,8,1310721,2, +17,26,1,17,1574549,12,8,1310721,2,17,26,1310721,2,2,13,26, +2,3,26,1310721,2,2,13,26,2,1,26,2,1574337,12,8,1310721, +2,11,26,1310721,2,7,26,2,7,30,3,26,1623873,12,10,3, +6,10,4,8,1310721,2,9,26,1,10,3,6,10,4,8,1310721, +2,9,26,2,1310721,2,7,26,2,40,1628455,16,1310721,2,15,26, +1310721,2,5,26,2,1310721,2,9,26,2,1310721,2,9,26,2,26, +3,26,26,2,1310721,2,17,26,1628749,12,10,3,6,10,4,8, +1310721,2,11,26,2,1310721,2,9,26,2,7,30,26,1310721,2,5, +26,2,1310721,2,9,26,2,1310721,2,9,26,2,26,26,2,10, +3,6,10,4,8,1310721,2,11,26,2,1310721,2,9,26,2,7, +30,3,26,26,1310721,2,15,26,2,10,3,6,10,4,8,1310721, +2,11,26,2,1310721,2,9,26,2,7,30,5,26,26,1310721,2, +13,26,2,10,3,6,10,4,8,1310721,2,13,26,1310721,2,13, +26,2,1310721,2,15,26,2,1628577,12,26,10,3,6,10,4,8, +1310721,2,9,26,1,6,2,1,6,3,26,5,6,4,10,8, +8,1628095,14,1310721,2,2,9,26,1310721,2,11,26,2,10,3,6, +10,4,8,1310721,2,2,3,26,1310721,2,13,26,2,10,3,6, +10,4,8,1310721,2,2,7,26,1310721,2,7,26,2,10,3,6, +10,4,8,1310721,2,2,1628563,14,1,1310721,2,33,26,1310721,4,18, +1310721,2,3,28,1310721,4,1310721,2,1835009,40,1628611,16,1966081,1310721,4,1579069, +12,1310721,2,4,1310721,2,2,1,26,2,17,28,1,6,1,10, +28,34,63,38,3,26,1628699,16,1310721,2,2,7,26,2,1,40, +1,6,1,10,28,34,63,38,1,10,28,1628701,14,1,1628717,16, +1310721,2,2,1627505,12,8,1310721,2,2,3,26,2,1628735,14,1,1310721, +2,3,26,1310721,4,18,1310721,2,61,28,1310721,4,1310721,2,1835009,40, +1628783,16,1966081,1310721,4,1579069,12,1310721,2,3,26,4,1310721,2,4,1310721, +2,2,1,26,2,1310721,2,5,26,4,1310721,2,5,26,2,19, +28,1,6,1,10,28,34,63,38,3,26,1628927,16,1310721,2,3, +26,2,1310721,2,2,7,26,2,26,2,1628919,16,1310721,2,3,26, +2,1310721,2,2,7,26,2,26,2,1629733,14,1584975,2,1629733,14,1310721, +2,5,26,2,11,28,1,6,1,10,28,34,63,38,3,26, +1628999,16,1310721,2,2,9,26,2,1310721,2,3,26,2,1628749,12,1624435, +12,1629733,14,1310721,2,5,26,2,13,28,1,6,1,10,28,34, +63,38,3,26,1629083,16,1310721,2,2,9,26,2,1310721,2,3,26, +2,1628749,12,1310721,2,2,7,26,2,1624809,12,1629733,14,1310721,2,5, +26,2,15,28,1,6,1,10,28,34,63,38,3,26,1629373,16, +1310721,2,9,26,1310721,2,2,7,26,2,10,3,6,10,4,8, +1310721,2,7,26,1,10,3,6,10,4,8,1310721,2,7,26,2, +1310721,2,9,26,2,40,1629309,16,1310721,2,11,26,1310721,2,7,26, +2,26,1310721,2,2,15,1310721,2,7,26,2,26,26,2,1310721,2, +3,26,2,1628749,12,10,3,6,10,4,8,1310721,2,7,26,1, +6,2,1,6,3,26,5,6,4,10,8,8,1629175,14,1310721,2, +2,9,26,2,1310721,2,3,26,2,1628749,12,1310721,2,9,26,2, +1310721,2,11,26,1310721,2,2,11,26,2,1625329,12,1629733,14,1310721,2, +5,26,2,17,28,1,6,1,10,28,34,63,38,3,26,1629449, +16,1310721,2,2,11,26,2,1,6,1,10,28,34,63,38,1, +10,28,1629451,14,1,1629721,16,1310721,2,59,26,1310721,2,2,13,26, +2,10,3,6,10,4,8,1310721,2,9,26,1310721,2,59,26,2, +1,26,2,10,3,6,10,4,8,1310721,2,7,26,1,10,3, +6,10,4,8,1310721,2,7,26,2,1310721,2,9,26,2,40,1629683, +16,1310721,2,11,26,1310721,2,7,26,2,26,1310721,2,59,26,2, +3,1310721,2,7,26,2,26,26,2,1310721,2,3,26,2,1628749,12, +10,3,6,10,4,8,1310721,2,7,26,1,6,2,1,6,3, +26,5,6,4,10,8,8,1629545,14,1310721,2,2,11,26,2,1310721, +2,11,26,1310721,2,9,26,2,1626643,12,1629733,14,1310721,2,2,1629733, +14,1,1310721,2,61,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721, +2,1835009,40,1629781,16,1966081,1310721,4,1579069,12,1310721,2,4,1310721,2,2, +1,26,2,3,28,1,6,1,10,28,34,63,38,3,26,1629833, +16,3,1629891,14,1310721,2,2,1,26,2,5,28,1,6,1,10, +28,34,63,38,3,26,1,6,1,10,28,34,63,38,1,10, +28,1629901,16,3,1629959,14,1310721,2,2,1,26,2,7,28,1,6, +1,10,28,34,63,38,3,26,1,6,1,10,28,34,63,38, +1,10,28,1629965,14,1,1310721,2,3,26,1310721,4,18,1310721,2,3, +28,1310721,4,1310721,2,1835009,40,1630013,16,1966081,1310721,4,1579069,12,1310721,2, +4,1310721,2,2,1,26,2,13,28,1,6,1,10,28,34,63, +38,3,26,1630065,16,3,1630123,14,1310721,2,2,1,26,2,17,28, +1,6,1,10,28,34,63,38,3,26,1,6,1,10,28,34, +63,38,1,10,28,1630129,14,1,1310721,2,3,26,1310721,4,18,1310721, +2,3,28,1310721,4,1310721,2,1835009,40,1630177,16,1966081,1310721,4,1579069,12, +1310721,2,4,1310721,2,2,1,26,2,11,28,1,6,1,10,28, +34,63,38,3,26,1630229,16,3,1630287,14,1310721,2,2,1,26,2, +13,28,1,6,1,10,28,34,63,38,3,26,1,6,1,10, +28,34,63,38,1,10,28,1630293,14,1,1310721,2,3,26,1310721,4, +18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1630341,16,1966081,1310721,4, +1579069,12,1310721,2,4,1310721,2,2,1,26,2,13,28,1,6,1, +10,28,34,63,38,3,26,1630407,16,1310721,2,2,9,26,2,1624435, +12,1630419,14,1310721,2,2,1630419,14,1,1310721,2,3,26,1310721,4,18, +1310721,2,3,28,1310721,4,1310721,2,1835009,40,1630467,16,1966081,1310721,4,1579069, +12,1310721,2,4,1310721,2,2,1,26,2,11,28,1,6,1,10, +28,34,63,38,3,26,1630519,16,3,1630577,14,1310721,2,2,1,26, +2,13,28,1,6,1,10,28,34,63,38,3,26,1,6,1, +10,28,34,63,38,1,10,28,1630597,16,1310721,2,2,9,26,2, +1630607,14,1584975,2,1630607,14,1,1310721,2,3,26,1310721,4,18,1310721,2, +5,28,1310721,4,1310721,2,1835009,40,1630655,16,1966081,1310721,4,1579069,12,1310721, +2,3,26,4,1310721,2,4,1310721,2,2,1310721,2,3,26,2,28, +1,6,1,10,28,34,63,38,3,26,1630719,16,3,1630773,14,1310721, +2,2,1584975,2,28,1,6,1,10,28,34,63,38,3,26,1, +6,1,10,28,34,63,38,1,10,28,1630783,16,3,1630841,14,1310721, +2,3,26,2,1584975,2,28,1,6,1,10,28,34,63,38,3, +26,1,6,1,10,28,34,63,38,1,10,28,1630851,16,3,1631577, +14,1310721,2,2,1629747,12,1630905,16,1310721,2,3,26,2,1629747,12,1, +6,1,10,28,34,63,38,1,10,28,1630907,14,1,1630917,16,3, +1631577,14,1310721,2,3,26,1310721,2,3,26,2,1630307,12,10,3,6, +10,4,8,1310721,2,2,1,26,2,11,28,1,6,1,10,28, +34,63,38,3,26,1631057,16,1310721,2,3,26,2,1,26,2,11, +28,1,6,1,10,28,34,63,38,3,26,1,6,1,10,28, +34,63,38,1,10,28,1631059,14,1,1631383,16,1310721,2,2,9,26, +2,1584975,2,28,1,6,1,10,28,34,63,38,3,26,1631111,16, +3,1631175,14,1310721,2,3,26,2,9,26,2,1584975,2,28,1,6, +1,10,28,34,63,38,3,26,1,6,1,10,28,34,63,38, +1,10,28,1631185,16,3,1631245,14,1310721,2,2,9,26,2,1584983,2, +28,1,6,1,10,28,34,63,38,3,26,1,6,1,10,28, +34,63,38,1,10,28,1631255,16,3,1631319,14,1310721,2,3,26,2, +9,26,2,1584983,2,28,1,6,1,10,28,34,63,38,3,26, +1,6,1,10,28,34,63,38,1,10,28,1631329,16,3,1631577,14, +1310721,2,2,9,26,2,1310721,2,3,26,2,9,26,2,28,1, +6,1,10,28,34,63,38,3,26,1631577,14,1310721,2,2,1,26, +2,11,28,1,6,1,10,28,34,63,38,3,26,1631429,16,3, +1631487,14,1310721,2,2,1,26,2,15,28,1,6,1,10,28,34, +63,38,3,26,1,6,1,10,28,34,63,38,1,10,28,1631521, +16,1584989,2,1,6,1,10,28,34,63,38,1,10,28,1631523,14, +1,1631533,16,3,1631577,14,1310721,2,2,1310721,2,3,26,2,28,1, +6,1,10,28,34,63,38,3,26,1631577,14,1,1310721,2,5,26, +1310721,4,18,1310721,2,15,28,1310721,4,1310721,2,1835009,40,1631625,16,1966081, +1310721,4,1579069,12,1310721,2,5,26,4,1310721,2,3,26,4,1310721,2, +4,1310721,2,2,1,26,2,19,28,1,6,1,10,28,34,63, +38,3,26,1631863,16,1310721,2,7,26,1310721,2,2,7,26,2,10, +3,6,10,4,8,1310721,2,5,26,2,1310721,2,7,26,2,26, +2,1,6,1,10,28,34,63,38,3,26,1631821,16,1310721,2,5, +26,2,1310721,2,7,26,2,26,1310721,2,3,26,2,10,3,6, +10,4,8,3,1632667,14,1310721,2,5,26,2,1310721,2,7,26,2, +26,2,1310721,2,3,26,2,1630621,12,1632667,14,1310721,2,3,26,1310721, +2,3,26,2,1630307,12,10,3,6,10,4,8,1310721,2,2,1, +26,2,11,28,1,6,1,10,28,34,63,38,3,26,1632003,16, +1310721,2,3,26,2,1,26,2,11,28,1,6,1,10,28,34, +63,38,3,26,1,6,1,10,28,34,63,38,1,10,28,1632005, +14,1,1632107,16,1310721,2,3,26,2,9,26,2,1584975,2,28,1, +6,1,10,28,34,63,38,3,26,1632061,16,3,1632667,14,1310721,2, +2,9,26,2,1310721,2,3,26,2,9,26,2,1310721,2,5,26, +2,1631591,12,1632667,14,1310721,2,2,1,26,2,17,28,1,6,1, +10,28,34,63,38,3,26,1632185,16,1310721,2,2,11,26,2,1, +6,1,10,28,34,63,38,1,10,28,1632187,14,1,1632257,16,1310721, +2,3,26,2,1,26,2,17,28,1,6,1,10,28,34,63, +38,3,26,1,6,1,10,28,34,63,38,1,10,28,1632259,14, +1,1632339,16,1310721,2,3,26,2,11,26,2,1310721,2,2,11,26, +2,28,1,6,1,10,28,34,63,38,3,26,1,6,1,10, +28,34,63,38,1,10,28,1632341,14,1,1632641,16,1310721,2,11,26, +1310721,2,2,13,26,2,10,3,6,10,4,8,1310721,2,13,26, +1310721,2,3,26,2,13,26,2,10,3,6,10,4,8,1310721,2, +9,26,1310721,2,11,26,2,1,26,2,10,3,6,10,4,8, +1310721,2,7,26,1,10,3,6,10,4,8,1310721,2,7,26,2, +1310721,2,9,26,2,40,1632635,16,1310721,2,11,26,2,3,1310721,2, +7,26,2,26,26,2,1310721,2,13,26,2,3,1310721,2,7,26, +2,26,26,2,1310721,2,5,26,2,1631591,12,1,6,1,10,28, +34,63,38,3,26,1632597,16,1,1632667,14,1310721,2,7,26,1,6, +2,1,6,3,26,5,6,4,10,8,8,1632471,14,3,1632667,14, +1310721,2,2,1310721,2,3,26,2,1630621,12,1632667,14,1,1310721,2,15, +26,1310721,4,18,1310721,2,9,28,1310721,4,1310721,2,1835009,40,1632715,16, +1966081,1310721,4,1579069,12,1310721,2,4,1310721,2,2,1,26,2,1310721,2, +3,26,4,1310721,2,3,26,2,11,28,1,6,1,10,28,34, +63,38,3,26,1632809,16,85,1573101,12,8,1310721,2,2,9,26,2, +1632681,12,1633911,14,1310721,2,3,26,2,13,28,1,6,1,10,28, +34,63,38,3,26,1632901,16,183,1573101,12,8,1310721,2,2,7,26, +2,1573503,12,8,187,1573101,12,8,1310721,2,2,9,26,2,1632681,12, +1633911,14,1310721,2,3,26,2,15,28,1,6,1,10,28,34,63, +38,3,26,1633561,16,1632951,14,102,110,40,0,1632943,1573341,12,8,1310721, +2,5,26,1,10,3,6,10,4,8,1310721,2,5,26,2,1310721, +2,2,7,26,2,40,1633333,16,1310721,2,5,26,2,1633041,16,1633033, +14,44,32,0,1633027,1573341,12,8,1310721,2,2,15,1310721,2,5,26, +2,26,26,2,1632681,12,8,1310721,2,5,26,2,25,40,1633159,16, +1310721,2,2,1310721,2,5,26,2,1646457,12,3,28,1,6,1,10, +28,34,63,38,3,26,1,6,1,10,28,34,63,38,1,10, +28,1633161,14,1,1633183,16,1633175,14,46,109,0,1633169,1573341,12,8,1310721, +2,5,26,2,25,40,1633271,16,1310721,2,2,1310721,2,5,26,2, +1646457,12,5,28,1,6,1,10,28,34,63,38,3,26,1,6, +1,10,28,34,63,38,1,10,28,1633273,14,1,1633295,16,1633287,14, +46,100,0,1633281,1573341,12,8,1310721,2,5,26,1,6,2,1,6, +3,26,5,6,4,10,8,8,1632981,14,83,1573101,12,8,1310721,2, +2,9,26,2,1584975,2,28,1,6,1,10,28,34,63,38,1, +10,28,1633391,16,3,1633429,14,1310721,2,2,11,26,2,3,32,1, +6,1,10,28,34,63,38,1,10,28,1633555,16,1633447,14,32,45, +62,32,0,1633437,1573341,12,8,1310721,2,2,9,26,2,1584975,2,28, +1,6,1,10,28,34,63,38,1,10,28,1633517,16,1310721,2,2, +9,26,2,1632681,12,8,1310721,2,2,11,26,2,3,32,1633555,16, +1633547,14,46,117,0,1633541,1573341,12,8,1,1633911,14,1310721,2,2,5, +26,2,1633599,16,1310721,2,2,5,26,2,1573341,12,8,1633615,14,1633607, +14,63,0,1633603,1573341,12,8,1310721,2,3,26,2,17,28,1,6, +1,10,28,34,63,38,3,26,1633691,16,1310721,2,2,11,26,2, +1,6,1,10,28,34,63,38,1,10,28,1633693,14,1,1633903,16, +1310721,2,7,26,1310721,2,2,13,26,2,10,3,6,10,4,8, +121,1573101,12,8,1310721,2,5,26,1,10,3,6,10,4,8,1310721, +2,5,26,2,1310721,2,7,26,2,1,26,2,40,1633895,16,1310721, +2,5,26,2,1633823,16,1633815,14,44,32,0,1633809,1573341,12,8,1310721, +2,7,26,2,3,1310721,2,5,26,2,26,26,2,1632681,12,8, +1310721,2,5,26,1,6,2,1,6,3,26,5,6,4,10,8, +8,1633759,14,125,1573101,12,8,1,1633911,14,1,1310721,2,9,26,1310721, +4,18,1310721,2,7,28,1310721,4,1310721,2,1835009,40,1633959,16,1966081,1310721, +4,1579069,12,1310721,2,5,26,4,1310721,2,3,26,4,1310721,2,4, +1584985,2,1,6,1,10,28,34,63,38,3,26,1634019,16,3,1634065, +14,1584857,2,1,6,1,10,28,34,63,38,3,26,1,6,1, +10,28,34,63,38,1,10,28,1634075,16,3,1634101,14,1584991,2,1, +6,1,10,28,34,63,38,1,10,28,1634111,16,3,1634137,14,1585091, +2,1,6,1,10,28,34,63,38,1,10,28,1634147,16,1,1634311, +14,1585093,12,8,1634183,14,116,121,112,101,32,101,114,114,111,114, +58,32,0,1634157,1573341,12,8,1310721,2,2,1573341,12,8,1634231,14,58, +32,101,120,112,101,99,116,101,100,32,0,1634207,1573341,12,8,1310721, +2,3,26,2,1632681,12,8,1634273,14,44,32,103,111,116,32,0, +1634259,1573341,12,8,1310721,2,5,26,2,1632681,12,8,1585237,12,8,1, +1634311,14,1,1310721,2,7,26,1310721,4,18,1310721,2,7,28,1310721,4, +1310721,2,1835009,40,1634359,16,1966081,1310721,4,1579069,12,1310721,2,5,26,4, +1310721,2,3,26,4,1310721,2,4,1310721,2,2,1634401,16,3,1634447,14, +1584985,2,1,6,1,10,28,34,63,38,3,26,1,6,1,10, +28,34,63,38,1,10,28,1634457,16,3,1634503,14,1584857,2,1,6, +1,10,28,34,63,38,3,26,1,6,1,10,28,34,63,38, +1,10,28,1634513,16,3,1634539,14,1584991,2,1,6,1,10,28,34, +63,38,1,10,28,1634549,16,3,1634575,14,1585091,2,1,6,1,10, +28,34,63,38,1,10,28,1634585,16,1,1634701,14,1585093,12,8,1634621, +14,116,121,112,101,32,101,114,114,111,114,58,32,0,1634595,1573341, +12,8,1310721,2,3,26,2,1573341,12,8,1634663,14,44,32,103,111, +116,32,0,1634649,1573341,12,8,1310721,2,5,26,2,1632681,12,8,1585237, +12,8,1,1634701,14,1,1310721,2,7,26,1310721,4,18,1310721,2,5, +28,1310721,4,1310721,2,1835009,40,1634749,16,1966081,1310721,4,1579069,12,1310721,2, +4,1585009,2,1310721,2,3,26,4,1310721,2,3,26,2,1634883,16,1310721, +2,3,26,1,6,2,1,6,-1,26,5,6,4,10,8,8, +1584993,1310721,2,3,26,2,26,2,1310721,2,2,28,1,6,1,10, +28,34,63,38,3,26,1634879,16,1310721,2,3,26,2,1634897,14,1634769, +14,3,1,10,28,1634897,14,1,1310721,2,5,26,1310721,4,18,1310721, +2,5,28,1310721,4,1310721,2,1835009,40,1634945,16,1966081,1310721,4,1579069,12, +1310721,2,4,1310721,2,2,1634715,12,1,40,3,36,1634979,16,3,1635133, +14,1310721,2,2,1603687,12,1,40,3,36,1635007,16,1,1635133,14,1310721, +2,3,26,1310721,2,2,1587809,12,10,3,6,10,4,8,1310721,2, +3,26,2,1,40,3,36,1635125,16,1417729,1310721,2,3,26,2,26, +2,15,28,1,6,1,10,28,34,63,38,3,26,1,6,1, +10,28,34,63,38,1,10,28,1635127,14,1,1635133,14,1,1310721,2, +5,26,1310721,4,18,1310721,2,1,28,1310721,4,1310721,2,1835009,40,1635181, +16,1966081,1310721,4,1579069,12,1585019,2,1598497,12,1635217,16,1582579,1585017,2,10, +3,6,10,4,8,1,1635235,14,1585017,2,1599031,12,8,1,1635235,14, +1,1310721,2,1,26,1310721,4,18,1310721,2,79,28,1310721,4,1310721,2, +1835009,40,1635283,16,1966081,1310721,4,1579069,12,1,1310721,2,7,26,4,1, +1310721,2,75,26,4,1584911,2,1598801,12,1635331,16,1635249,12,1624435,12,1637129, +14,1584895,2,1598801,12,1635495,16,1310721,2,1665803,12,10,3,6,10,4, +8,1584897,2,1599031,12,8,1310721,2,5,26,1635249,12,10,3,6,10, +4,8,1310721,2,2,1,40,1635471,16,1635463,14,97,32,110,101,103, +97,116,105,118,101,32,97,114,114,97,121,32,108,101,110,103, +116,104,0,1635415,1585679,12,8,1310721,2,5,26,2,1310721,2,2,1624809, +12,1637129,14,1584927,2,1598649,12,1636065,16,1597071,12,8,1584887,2,1599031,12, +8,1584889,2,1598497,12,1,6,1,10,28,34,63,38,3,26,1635745, +16,3,1635745,16,1310721,2,7,26,2,49,40,3,36,1635635,16,1635627, +14,116,111,111,32,109,97,110,121,32,112,97,114,97,109,101, +116,101,114,115,0,1635587,1586063,12,8,1310721,2,9,26,1310721,2,7, +26,2,26,1635249,12,10,3,6,10,4,8,1310721,2,7,26,1, +6,2,1,6,3,26,5,6,4,10,8,8,1584899,2,1598801,12, +1,6,1,10,28,34,63,38,3,26,1635741,16,1635745,14,1635555,14, +1584889,2,1599031,12,8,1310721,2,57,26,1584975,2,10,3,6,10,4, +8,1310721,2,77,26,1,10,3,6,10,4,8,1584905,2,1598801,12, +1635995,16,1584925,2,1598497,12,1,6,1,10,28,34,63,38,3,26, +1635869,16,1310721,2,57,26,1635249,12,10,3,6,10,4,8,1584925,2, +1598801,12,1635995,16,1584945,2,1598649,12,1,6,1,10,28,34,63,38, +3,26,1635967,16,1635959,14,116,104,101,32,114,101,116,117,114,110, +32,116,97,103,32,105,115,32,46,117,0,1635917,1586063,12,8,1597071, +12,8,1310721,2,77,26,3,10,3,6,10,4,8,1310721,2,57, +26,2,1310721,2,7,26,2,1310721,2,9,26,1310721,2,77,26,2, +1310721,2,7,26,2,26,1310721,2,7,26,2,26,1625329,12,1637129,14, +1582577,2,5,28,1,6,1,10,28,34,63,38,1,10,28,1636151, +16,1636135,14,101,120,112,101,99,116,101,100,32,97,32,116,121, +112,101,0,1636103,1586063,12,8,1584975,2,1637129,14,1310721,2,1582579,2,10, +3,6,10,4,8,1310721,2,77,26,1310721,2,2,1634715,12,10,3, +6,10,4,8,1310721,2,77,26,2,1,40,3,36,1636253,16,1597071, +12,8,1310721,2,77,26,2,1310721,2,2,1626251,12,1637129,14,1310721,2, +3,26,1310721,2,2,1587809,12,10,3,6,10,4,8,1310721,2,3, +26,2,1,40,1636307,16,3,1636371,14,1417729,1310721,2,3,26,2,26, +2,15,28,1,6,1,10,28,34,63,38,1,10,28,1,6, +1,10,28,34,63,38,1,10,28,1636427,16,1310721,2,2,1636411,14, +105,115,110,39,116,32,97,32,116,121,112,101,0,1636385,1586197,12, +8,1584975,2,1637129,14,1597071,12,8,1310721,2,5,26,1425921,1310721,2,3, +26,2,26,2,10,3,6,10,4,8,1310721,2,5,26,2,1, +26,2,21,28,1,6,1,10,28,34,63,38,1,10,28,1636529, +16,1310721,2,5,26,2,1637129,14,1585015,2,1599031,12,8,3,1636951,16, +1310721,2,75,26,2,17,40,3,36,1636627,16,1636619,14,116,111,111, +32,109,97,110,121,32,116,121,112,101,32,97,114,103,117,109, +101,110,116,115,0,1636571,1586063,12,8,1310721,2,59,26,1310721,2,75, +26,2,26,1635249,12,10,3,6,10,4,8,1310721,2,59,26,1310721, +2,75,26,2,26,2,1628577,12,3,28,1,6,1,10,28,34, +63,38,1,10,28,1636877,16,1636869,14,116,121,112,101,32,97,114, +103,117,109,101,110,116,115,32,104,97,118,101,32,116,111,32, +98,101,32,111,110,101,32,119,111,114,100,32,40,97,32,112, +111,105,110,116,101,114,44,32,97,32,110,117,109,98,101,114, +44,32,111,114,32,97,32,112,97,114,97,109,101,116,101,114, +41,0,1636723,1585679,12,8,1310721,2,75,26,1,6,2,1,6,3, +26,5,6,4,10,8,8,1584899,2,1598801,12,1,6,1,10,28, +34,63,38,3,26,1636947,16,1636951,14,1636539,14,1635147,12,8,1310721,2, +75,26,2,1310721,2,5,26,2,7,26,2,28,1,6,1,10, +28,34,63,38,1,10,28,1637091,16,1310721,2,2,1637083,14,119,114, +111,110,103,32,110,117,109,98,101,114,32,111,102,32,116,121, +112,101,32,97,114,103,117,109,101,110,116,115,0,1637021,1585849,12, +8,1310721,2,5,26,2,1310721,2,59,26,1310721,2,75,26,2,1626643, +12,1637129,14,1,1310721,2,79,26,1310721,4,18,1310721,2,3,28,1310721, +4,1310721,2,1835009,40,1637177,16,1966081,1310721,4,1579069,12,1582577,2,9,28, +1,6,1,10,28,34,63,38,3,26,1637815,16,1582579,2,1584895,2, +28,1,6,1,10,28,34,63,38,3,26,1637249,16,3,1637301,14, +1582579,2,1584925,2,28,1,6,1,10,28,34,63,38,3,26,1, +6,1,10,28,34,63,38,1,10,28,1637311,16,3,1637931,14,1582579, +2,1584911,2,28,1,6,1,10,28,34,63,38,3,26,1637809,16, +1310721,2,1,10,3,6,10,4,8,1310721,2,2,7,40,1637541,16, +1310721,2,2,1597469,12,8,1584799,2,9,28,1,6,1,10,28,34, +63,38,3,26,1637477,16,1584801,2,1584911,2,28,1,6,1,10,28, +34,63,38,3,26,1,6,1,10,28,34,63,38,1,10,28, +1637479,14,1,1,6,1,10,28,34,63,38,3,26,1637507,16,1637541, +14,1310721,2,1,6,2,1,6,3,26,5,6,4,10,8,8, +1637363,14,1310721,2,2,1597469,12,8,1584799,2,5,28,1,6,1,10, +28,34,63,38,3,26,1637681,16,1584801,2,1634911,12,1637603,16,3,1637655, +14,1584801,2,1584927,2,28,1,6,1,10,28,34,63,38,3,26, +1,6,1,10,28,34,63,38,1,10,28,1,6,1,10,28, +34,63,38,1,10,28,1637683,14,1,1637693,16,3,1637805,14,1584799,2, +9,28,1,6,1,10,28,34,63,38,3,26,1637781,16,1584801,2, +1584895,2,28,1,6,1,10,28,34,63,38,3,26,1,6,1, +10,28,34,63,38,1,10,28,1637783,14,1,1,6,1,10,28, +34,63,38,1,10,28,1637931,14,1,1637931,14,1582577,2,5,28,1, +6,1,10,28,34,63,38,1,10,28,1637855,16,1,1637931,14,1582579, +2,1584927,2,28,1,6,1,10,28,34,63,38,3,26,1637895,16, +3,1637925,14,1582579,2,1634911,12,1,6,1,10,28,34,63,38,1, +10,28,1637931,14,1,1310721,2,3,26,1310721,4,18,1310721,2,1,28, +1310721,4,1310721,2,1835009,40,1637979,16,1966081,1310721,4,1579069,12,1585011,1,10, +3,6,10,4,8,1585013,1,10,3,6,10,4,8,1584925,2,1598497, +12,1,6,1,10,28,34,63,38,3,26,1638061,16,1585011,1635249,12, +10,3,6,10,4,8,1584925,2,1598801,12,1638297,16,1582579,2,1584941,2, +28,1,6,1,10,28,34,63,38,3,26,1638127,16,1585013,3,10, +3,6,10,4,8,1638291,14,1582579,2,1584945,2,28,1,6,1,10, +28,34,63,38,3,26,1638181,16,1585013,5,10,3,6,10,4,8, +1638291,14,1582579,2,1584943,2,28,1,6,1,10,28,34,63,38,3, +26,1638235,16,1585013,7,10,3,6,10,4,8,1638291,14,1638283,14,97, +32,116,97,103,32,105,115,32,46,117,44,32,46,109,32,111, +114,32,46,100,0,1638239,1586063,12,8,1597071,12,8,1,1638305,14,1, +1310721,2,1,26,1310721,4,18,1310721,2,21,28,1310721,4,1310721,2,1835009, +40,1638353,16,1966081,1310721,4,1579069,12,1,1310721,2,7,26,4,1524225,1310721, +2,9,26,4,1,1310721,2,15,26,4,1,1310721,2,17,26,4, +1597071,12,8,1310721,2,1599321,12,10,3,6,10,4,8,1585015,2,1598497, +12,1638821,16,1597071,12,8,1585009,1,10,3,6,10,4,8,3,1638637, +16,1585009,2,17,40,3,36,1638545,16,1638537,14,116,111,111,32,109, +97,110,121,32,116,121,112,101,32,112,97,114,97,109,101,116, +101,114,115,0,1638487,1586063,12,8,1584993,1585009,2,26,1599321,12,10,3, +6,10,4,8,1585009,1,6,2,1,6,3,26,5,6,4,10, +8,8,1584899,2,1598801,12,1,6,1,10,28,34,63,38,3,26, +1638633,16,1638637,14,1638461,14,1635147,12,8,1310721,2,17,26,3,10,3, +6,10,4,8,1310721,2,3,26,21,1,1310721,2,2,1,1624087,12, +10,3,6,10,4,8,1310721,2,3,26,2,7,26,1585009,2,10, +3,6,10,4,8,1310721,2,13,26,1585009,2,1623873,12,10,3,6, +10,4,8,1310721,2,13,26,2,1584993,1585009,2,1574337,12,8,1310721,2, +3,26,2,13,26,1310721,2,13,26,2,10,3,6,10,4,8, +1638891,14,1310721,2,3,26,17,1,1310721,2,2,1,1624087,12,10,3, +6,10,4,8,1310721,2,3,26,2,7,26,5,1,10,28,10, +3,6,10,4,8,1310721,2,5,26,1310721,2,2,15,1310721,2,3, +26,2,1587311,12,10,3,6,10,4,8,1584891,2,1599031,12,8,1584893, +2,1598497,12,1,6,1,10,28,34,63,38,3,26,1639269,16,1310721, +2,19,26,1635249,12,10,3,6,10,4,8,3,1639255,16,1310721,2, +7,26,2,2001,40,3,36,1639071,16,1639063,14,116,111,111,32,109, +97,110,121,32,102,105,101,108,100,115,0,1639031,1585585,12,8,1310721, +2,9,26,2,1310721,2,7,26,2,1310721,2,7,26,2,26,26, +1599321,12,10,3,6,10,4,8,1310721,2,9,26,2,1310721,2,7, +26,2,1310721,2,7,26,2,26,3,26,26,1310721,2,19,26,2, +10,3,6,10,4,8,1310721,2,7,26,1,6,2,1,6,3, +26,5,6,4,10,8,8,1584899,2,1598801,12,1,6,1,10,28, +34,63,38,3,26,1639251,16,1639255,14,1638999,14,1584901,2,1599031,12,8, +1638943,14,1597071,12,8,1310721,2,17,26,2,1639475,16,1310721,2,13,26, +1310721,2,7,26,2,1310721,2,7,26,2,26,1623873,12,10,3,6, +10,4,8,1310721,2,13,26,2,1310721,2,9,26,2,1310721,2,7, +26,2,1310721,2,7,26,2,26,1574337,12,8,1310721,2,3,26,2, +9,26,1310721,2,13,26,2,10,3,6,10,4,8,1310721,2,3, +26,2,11,26,1310721,2,7,26,2,10,3,6,10,4,8,1585009, +1,10,3,6,10,4,8,1640185,14,1310721,2,13,26,1310721,2,7, +26,2,7,30,3,26,1623873,12,10,3,6,10,4,8,1310721,2, +11,26,1,10,3,6,10,4,8,1310721,2,11,26,2,1310721,2, +7,26,2,40,1640077,16,1310721,2,19,26,1310721,2,9,26,2,1310721, +2,11,26,2,1310721,2,11,26,2,26,3,26,26,2,10,3, +6,10,4,8,1310721,2,19,26,2,1310721,2,3,26,2,28,1, +6,1,10,28,34,63,38,3,26,1639815,16,1639783,14,97,32,115, +116,114,117,99,116,32,99,97,110,39,116,32,99,111,110,116, +97,105,110,32,105,116,115,101,108,102,32,98,121,32,118,97, +108,117,101,59,32,117,115,101,32,97,32,112,111,105,110,116, +101,114,0,1639675,1585679,12,8,1310721,2,19,26,1584975,2,10,3,6, +10,4,8,1310721,2,13,26,2,1310721,2,11,26,2,7,30,26, +1310721,2,9,26,2,1310721,2,11,26,2,1310721,2,11,26,2,26, +26,2,10,3,6,10,4,8,1310721,2,13,26,2,1310721,2,11, +26,2,7,30,3,26,26,1310721,2,19,26,2,10,3,6,10, +4,8,1310721,2,13,26,2,1310721,2,11,26,2,7,30,5,26, +26,1310721,2,15,26,2,10,3,6,10,4,8,1310721,2,15,26, +1310721,2,15,26,2,1310721,2,19,26,2,1628577,12,26,10,3,6, +10,4,8,1310721,2,11,26,1,6,2,1,6,3,26,5,6, +4,10,8,8,1639539,14,1310721,2,3,26,2,9,26,1310721,2,13, +26,2,10,3,6,10,4,8,1310721,2,3,26,2,3,26,1310721, +2,15,26,2,10,3,6,10,4,8,1310721,2,3,26,2,7, +26,1310721,2,7,26,2,10,3,6,10,4,8,1584901,2,1598801,12, +8,1310721,2,5,26,2,1640211,14,1,1310721,2,21,26,1310721,4,18, +1310721,2,3,28,1310721,4,1310721,2,1835009,40,1640259,16,1966081,1310721,4,1579069, +12,1597071,12,8,1310721,2,1599321,12,10,3,6,10,4,8,1584903,2, +1599031,12,8,1310721,2,2,15,1635249,12,1587311,12,8,1584901,2,1599031,12, +8,1,1640331,14,1,1310721,2,3,26,1310721,4,18,1310721,2,1,28, +1310721,4,1310721,2,1835009,40,1640379,16,1966081,1310721,4,1579069,12,1597071,12,8, +1585037,2,1598649,12,1640417,16,1584985,3,10,3,6,10,4,8,1640521,14, +1585039,2,1598649,12,1640449,16,1584985,1,10,3,6,10,4,8,1640521,14, +1640513,14,116,121,112,101,99,104,101,99,107,32,111,110,32,111, +114,32,116,121,112,101,99,104,101,99,107,32,111,102,102,0, +1640453,1586063,12,8,1597071,12,8,1584901,2,1599031,12,8,1,1640545,14,1, +1310721,2,1,26,1310721,4,18,1310721,2,7,28,1310721,4,1310721,2,1835009, +40,1640593,16,1966081,1310721,4,1579069,12,1310721,2,3,26,4,1310721,2,4, +1,1310721,2,5,26,4,1310721,2,2,1627505,12,8,1310721,2,5,26, +2,1310721,2,2,7,26,2,40,1640779,16,1310721,2,2,9,26,2, +1310721,2,5,26,2,7,30,26,2,1310721,2,3,26,2,28,1, +6,1,10,28,34,63,38,3,26,1640741,16,1310721,2,5,26,2, +1640793,14,1310721,2,5,26,1,6,2,1,6,3,26,5,6,4, +10,8,8,1640633,14,3,1,10,28,1640793,14,1,1310721,2,7,26, +1310721,4,18,1310721,2,7,28,1310721,4,1310721,2,1835009,40,1640841,16,1966081, +1310721,4,1579069,12,1584887,2,1599031,12,8,1637143,12,1640913,16,1584925,2,1598497, +12,1,6,1,10,28,34,63,38,3,26,1,6,1,10,28, +34,63,38,1,10,28,1640915,14,1,1640943,16,1310721,2,1635249,12,10, +3,6,10,4,8,1641105,14,1310721,2,3,26,1606761,12,10,3,6, +10,4,8,1584991,1,6,2,1,6,3,26,5,6,4,10,8, +8,1310721,2,5,26,1662195,12,10,3,6,10,4,8,1647757,12,8, +1310721,2,1584987,2,10,3,6,10,4,8,1584991,1,6,2,1,6, +-1,26,5,6,4,10,8,8,1310721,2,3,26,2,1607393,12,8, +1310721,2,3,26,2,1607311,12,8,1584889,2,1599031,12,8,1310721,2,2, +1641127,14,1,1310721,2,7,26,1310721,4,18,1310721,2,3,28,1310721,4, +1310721,2,1835009,40,1641175,16,1966081,1310721,4,1579069,12,1597071,12,8,1310721,2, +1640807,12,10,3,6,10,4,8,1310721,2,2,1628577,12,1589149,12,8, +1584987,1584977,2,10,3,6,10,4,8,1,1641243,14,1,1310721,2,3, +26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1641291,16, +1966081,1310721,4,1579069,12,1597071,12,8,1310721,2,1640807,12,10,3,6,10, +4,8,1310721,2,2,1589149,12,8,1584987,1584975,2,10,3,6,10,4, +8,1,1641355,14,1,1310721,2,3,26,1310721,4,18,1310721,2,1,28, +1310721,4,1310721,2,1835009,40,1641403,16,1966081,1310721,4,1579069,12,1584971,1277953,2049, +26,10,3,6,10,4,8,1584973,1,10,3,6,10,4,8,1584975, +1,3,1641459,14,119,111,114,100,0,1641449,1586793,12,1,1624087,12,10, +3,6,10,4,8,1584977,3,3,1641501,14,105,110,116,0,1641493,1586793, +12,1,1624087,12,10,3,6,10,4,8,1584979,5,3,1641545,14,99, +104,97,114,0,1641535,1586793,12,1,1624087,12,10,3,6,10,4,8, +1584981,7,3,1641589,14,98,111,111,108,0,1641579,1586793,12,1,1624087,12, +10,3,6,10,4,8,1584983,9,1,1641633,14,118,111,105,100,0, +1641623,1586793,12,1,1624087,12,10,3,6,10,4,8,1584975,2,5,26, +2,15,1584975,2,1587311,12,8,1584977,2,5,26,2,15,1584977,2,1587311, +12,8,1584979,2,5,26,2,15,1584979,2,1587311,12,8,1584981,2,5, +26,2,15,1584981,2,1587311,12,8,1584983,2,5,26,2,15,1584983,2, +1587311,12,8,1585015,1641777,14,60,0,1641773,1586793,12,10,3,6,10,4, +8,1585017,1641805,14,62,0,1641801,1586793,12,10,3,6,10,4,8,1585019, +1641835,14,62,62,0,1641829,1586793,12,10,3,6,10,4,8,1585021,1641865, +14,60,61,0,1641859,1586793,12,10,3,6,10,4,8,1585023,1641895,14, +62,61,0,1641889,1586793,12,10,3,6,10,4,8,1585025,1641925,14,61, +61,0,1641919,1586793,12,10,3,6,10,4,8,1585027,1641955,14,33,61, +0,1641949,1586793,12,10,3,6,10,4,8,1585029,1641983,14,43,0,1641979, +1586793,12,10,3,6,10,4,8,1585031,1642021,14,115,116,114,117,99, +116,0,1642007,1586793,12,10,3,6,10,4,8,1585033,1642055,14,116,121, +112,101,0,1642045,1586793,12,10,3,6,10,4,8,1585035,1642099,14,116, +121,112,101,99,104,101,99,107,0,1642079,1586793,12,10,3,6,10, +4,8,1585037,1642129,14,111,110,0,1642123,1586793,12,10,3,6,10,4, +8,1585039,1642161,14,111,102,102,0,1642153,1586793,12,10,3,6,10,4, +8,1585041,1642193,14,110,101,119,0,1642185,1586793,12,10,3,6,10,4, +8,1585043,1642229,14,110,101,119,95,116,0,1642217,1586793,12,10,3,6, +10,4,8,1585045,1,26,1642261,14,42,0,1642257,1586793,12,10,3,6, +10,4,8,1585045,3,26,1642293,14,47,0,1642289,1586793,12,10,3,6, +10,4,8,1585045,5,26,1642325,14,37,0,1642321,1586793,12,10,3,6, +10,4,8,1585045,7,26,1642357,14,38,0,1642353,1586793,12,10,3,6, +10,4,8,1585045,9,26,1642389,14,124,0,1642385,1586793,12,10,3,6, +10,4,8,1585045,11,26,1642421,14,94,0,1642417,1586793,12,10,3,6, +10,4,8,1585045,13,26,1642455,14,60,60,0,1642449,1586793,12,10,3, +6,10,4,8,1585045,15,26,1585019,2,10,3,6,10,4,8,1585069, +17,10,3,6,10,4,8,1,1642519,14,1,1310721,2,1,26,1310721, +4,18,1310721,2,5,28,1310721,4,1310721,2,1835009,40,1642567,16,1966081,1310721, +4,1579069,12,1310721,2,4,1,1310721,2,3,26,4,1310721,2,3,26, +2,1585069,2,40,1642697,16,1585045,1310721,2,3,26,2,26,2,1310721,2, +2,28,1,6,1,10,28,34,63,38,3,26,1642659,16,3,1642705, +14,1310721,2,3,26,1,6,2,1,6,3,26,5,6,4,10, +8,8,1642585,14,1,1642705,14,1,1310721,2,5,26,1310721,4,18,1310721, +2,9,28,1310721,4,1310721,2,1835009,40,1642753,16,1966081,1310721,4,1579069,12, +1310721,2,5,26,4,1310721,2,3,26,4,1310721,2,4,1546753,1310721,2, +2,26,2,1310721,2,7,26,4,1310721,2,3,26,1310721,2,3,26, +2,1630307,12,10,3,6,10,4,8,1310721,2,5,26,1310721,2,5, +26,2,1630307,12,10,3,6,10,4,8,1547137,1310721,2,2,26,2, +1,40,1643065,16,1310721,2,3,26,2,1629979,12,1,6,1,10,28, +34,63,38,3,26,1642987,16,1310721,2,5,26,2,1629979,12,1,6, +1,10,28,34,63,38,3,26,1,6,1,10,28,34,63,38, +1,10,28,1642989,14,1,1643039,14,38,38,32,97,110,100,32,124, +124,32,110,101,101,100,32,115,99,97,108,97,114,115,0,1642993, +1310721,2,3,26,2,1634325,12,8,1584981,2,1645491,14,1310721,2,7,26, +2,1585025,2,28,1,6,1,10,28,34,63,38,3,26,1643111,16, +3,1643169,14,1310721,2,7,26,2,1585027,2,28,1,6,1,10,28, +34,63,38,3,26,1,6,1,10,28,34,63,38,1,10,28, +1643359,16,1310721,2,3,26,2,1310721,2,5,26,2,1630621,12,1,6, +1,10,28,34,63,38,3,26,1643291,16,1310721,2,5,26,2,1310721, +2,3,26,2,1630621,12,1,6,1,10,28,34,63,38,3,26, +1,6,1,10,28,34,63,38,1,10,28,1643293,14,1,1643351,16, +1643323,14,99,111,109,112,97,114,105,115,111,110,0,1643301,1310721,2, +3,26,2,1310721,2,5,26,2,1633925,12,8,1584981,2,1645491,14,1310721, +2,7,26,2,1585015,2,28,1,6,1,10,28,34,63,38,3, +26,1643405,16,3,1643463,14,1310721,2,7,26,2,1585017,2,28,1,6, +1,10,28,34,63,38,3,26,1,6,1,10,28,34,63,38, +1,10,28,1643473,16,3,1643531,14,1310721,2,7,26,2,1585021,2,28, +1,6,1,10,28,34,63,38,3,26,1,6,1,10,28,34, +63,38,1,10,28,1643541,16,3,1643599,14,1310721,2,7,26,2,1585023, +2,28,1,6,1,10,28,34,63,38,3,26,1,6,1,10, +28,34,63,38,1,10,28,1644029,16,1310721,2,3,26,2,1584975,2, +28,1,6,1,10,28,34,63,38,3,26,1643649,16,3,1643707,14, +1310721,2,5,26,2,1584975,2,28,1,6,1,10,28,34,63,38, +3,26,1,6,1,10,28,34,63,38,1,10,28,1643717,16,3, +1643799,14,1310721,2,3,26,2,1629747,12,1643775,16,1310721,2,5,26,2, +1629747,12,1,6,1,10,28,34,63,38,1,10,28,1643777,14,1, +1,6,1,10,28,34,63,38,1,10,28,1643809,16,3,1643943,14, +1310721,2,3,26,2,1,26,2,11,28,1,6,1,10,28,34, +63,38,3,26,1643919,16,1310721,2,5,26,2,1,26,2,11,28, +1,6,1,10,28,34,63,38,3,26,1,6,1,10,28,34, +63,38,1,10,28,1643921,14,1,1,6,1,10,28,34,63,38, +1,10,28,1,6,1,10,28,34,63,38,3,26,1644021,16,1643993, +14,99,111,109,112,97,114,105,115,111,110,0,1643971,1310721,2,3, +26,2,1310721,2,5,26,2,1633925,12,8,1584981,2,1645491,14,1310721,2, +7,26,2,1585029,2,28,1,6,1,10,28,34,63,38,3,26, +1644577,16,1310721,2,3,26,2,1,26,2,11,28,1,6,1,10, +28,34,63,38,3,26,1644221,16,1310721,2,5,26,2,1629747,12,1644137, +16,3,1644195,14,1310721,2,5,26,2,1584975,2,28,1,6,1,10, +28,34,63,38,3,26,1,6,1,10,28,34,63,38,1,10, +28,1,6,1,10,28,34,63,38,1,10,28,1644223,14,1,1644241, +16,1310721,2,3,26,2,1645491,14,1310721,2,5,26,2,1,26,2, +11,28,1,6,1,10,28,34,63,38,3,26,1644325,16,1310721,2, +3,26,2,1629747,12,1,6,1,10,28,34,63,38,1,10,28, +1644327,14,1,1644345,16,1310721,2,5,26,2,1645491,14,1310721,2,3,26, +2,1584975,2,28,1,6,1,10,28,34,63,38,3,26,1644391,16, +3,1644449,14,1310721,2,5,26,2,1584975,2,28,1,6,1,10,28, +34,63,38,3,26,1,6,1,10,28,34,63,38,1,10,28, +1644461,16,1584975,2,1645491,14,1310721,2,3,26,2,1629747,12,1644519,16,1310721, +2,5,26,2,1629747,12,1,6,1,10,28,34,63,38,1,10, +28,1644521,14,1,1644533,16,1584977,2,1645491,14,1644541,14,43,0,1644537,1310721, +2,3,26,2,1310721,2,5,26,2,1633925,12,8,1584975,2,1645491,14, +1310721,2,7,26,2,1584919,2,28,1,6,1,10,28,34,63,38, +3,26,1645145,16,1310721,2,3,26,2,1,26,2,11,28,1,6, +1,10,28,34,63,38,3,26,1644727,16,1310721,2,5,26,2,1, +26,2,11,28,1,6,1,10,28,34,63,38,3,26,1,6, +1,10,28,34,63,38,1,10,28,1644729,14,1,1644741,16,1584977,2, +1645491,14,1310721,2,3,26,2,1,26,2,11,28,1,6,1,10, +28,34,63,38,3,26,1644893,16,1310721,2,5,26,2,1629747,12,1644809, +16,3,1644867,14,1310721,2,5,26,2,1584975,2,28,1,6,1,10, +28,34,63,38,3,26,1,6,1,10,28,34,63,38,1,10, +28,1,6,1,10,28,34,63,38,1,10,28,1644895,14,1,1644913, +16,1310721,2,3,26,2,1645491,14,1310721,2,3,26,2,1584975,2,28, +1,6,1,10,28,34,63,38,3,26,1644959,16,3,1645017,14,1310721, +2,5,26,2,1584975,2,28,1,6,1,10,28,34,63,38,3, +26,1,6,1,10,28,34,63,38,1,10,28,1645029,16,1584975,2, +1645491,14,1310721,2,3,26,2,1629747,12,1645087,16,1310721,2,5,26,2, +1629747,12,1,6,1,10,28,34,63,38,1,10,28,1645089,14,1, +1645101,16,1584977,2,1645491,14,1645109,14,45,0,1645105,1310721,2,3,26,2, +1310721,2,5,26,2,1633925,12,8,1584975,2,1645491,14,1310721,2,7,26, +2,1642533,12,1645481,16,1310721,2,3,26,2,1584975,2,28,1,6,1, +10,28,34,63,38,3,26,1645209,16,3,1645267,14,1310721,2,5,26, +2,1584975,2,28,1,6,1,10,28,34,63,38,3,26,1,6, +1,10,28,34,63,38,1,10,28,1645279,16,1584975,2,1645491,14,1310721, +2,3,26,2,1629747,12,1645337,16,1310721,2,5,26,2,1629747,12,1, +6,1,10,28,34,63,38,1,10,28,1645339,14,1,1645351,16,1584977, +2,1645491,14,1310721,2,3,26,2,1629747,12,1645399,16,1310721,2,3,26, +1310721,2,5,26,2,10,3,6,10,4,8,1,1645455,14,97,114, +105,116,104,109,101,116,105,99,32,110,101,101,100,115,32,110, +117,109,98,101,114,115,0,1645405,1310721,2,3,26,2,1634325,12,8, +1584975,2,1645491,14,1584975,2,1645491,14,1,1310721,2,9,26,1310721,4,18, +1310721,2,3,28,1310721,4,1310721,2,1835009,40,1645539,16,1966081,1310721,4,1579069, +12,1310721,2,4,1310721,2,2,1,26,2,1645563,14,1,1310721,2,3, +26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1645611,16, +1966081,1310721,4,1579069,12,1310721,2,4,1310721,2,2,1628577,12,1645633,14,1, +1310721,2,3,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009, +40,1645681,16,1966081,1310721,4,1579069,12,1310721,2,4,1310721,2,2,5,26, +2,1645705,14,1,1310721,2,3,26,1310721,4,18,1310721,2,3,28,1310721, +4,1310721,2,1835009,40,1645753,16,1966081,1310721,4,1579069,12,1310721,2,4,1310721, +2,2,7,26,2,1645777,14,1,1310721,2,3,26,1310721,4,18,1310721, +2,3,28,1310721,4,1310721,2,1835009,40,1645825,16,1966081,1310721,4,1579069,12, +1310721,2,4,1310721,2,2,1627505,12,8,1310721,2,2,7,26,2,1645861, +14,1,1310721,2,3,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721, +2,1835009,40,1645909,16,1966081,1310721,4,1579069,12,1310721,2,3,26,4,1310721, +2,4,1310721,2,2,1627505,12,8,1310721,2,2,9,26,2,1310721,2, +3,26,2,7,30,26,2,1645973,14,1,1310721,2,5,26,1310721,4, +18,1310721,2,5,28,1310721,4,1310721,2,1835009,40,1646021,16,1966081,1310721,4, +1579069,12,1310721,2,3,26,4,1310721,2,4,1310721,2,2,1627505,12,8, +1310721,2,2,9,26,2,1310721,2,3,26,2,7,30,3,26,26, +2,1646089,14,1,1310721,2,5,26,1310721,4,18,1310721,2,5,28,1310721, +4,1310721,2,1835009,40,1646137,16,1966081,1310721,4,1579069,12,1310721,2,3,26, +4,1310721,2,4,1310721,2,2,1627505,12,8,1310721,2,2,9,26,2, +1310721,2,3,26,2,7,30,5,26,26,2,1646205,14,1,1310721,2, +5,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1646253, +16,1966081,1310721,4,1579069,12,1310721,2,4,1310721,2,2,9,26,2,1646277, +14,1,1310721,2,3,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721, +2,1835009,40,1646325,16,1966081,1310721,4,1579069,12,1310721,2,4,1310721,2,2, +7,26,2,1646349,14,1,1310721,2,3,26,1310721,4,18,1310721,2,5, +28,1310721,4,1310721,2,1835009,40,1646397,16,1966081,1310721,4,1579069,12,1310721,2, +3,26,4,1310721,2,4,1310721,2,2,15,1310721,2,3,26,2,26, +26,2,1646443,14,1,1310721,2,5,26,1310721,4,18,1310721,2,5,28, +1310721,4,1310721,2,1835009,40,1646491,16,1966081,1310721,4,1579069,12,1310721,2,3, +26,4,1310721,2,4,1310721,2,2,11,26,2,13,38,1310721,2,3, +26,2,1310721,2,3,26,2,26,38,7,32,1646557,14,1,1310721,2, +5,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1646605, +16,1966081,1310721,4,1579069,12,1310721,2,4,1310721,2,2,11,26,2,3, +32,1646633,14,1,1310721,2,3,26,1310721,4,18,1310721,2,3,28,1310721, +4,1310721,2,1835009,40,1646681,16,1966081,1310721,4,1579069,12,1310721,2,4,1310721, +2,2,1,26,2,17,28,1,6,1,10,28,34,63,38,1, +10,28,1646735,16,3,1646789,14,1310721,2,2,11,26,2,1,6,1, +10,28,34,63,38,3,26,1,6,1,10,28,34,63,38,1, +10,28,1646799,16,1,1646823,14,1310721,2,2,13,26,2,1,26,2, +1646823,14,1,1310721,2,3,26,1310721,4,18,1310721,2,5,28,1310721,4, +1310721,2,1835009,40,1646871,16,1966081,1310721,4,1579069,12,1310721,2,3,26,4, +1310721,2,4,1310721,2,2,13,26,2,3,1310721,2,3,26,2,26, +26,2,1646923,14,1,1310721,2,5,26,1310721,4,18,1310721,2,59,28, +1310721,4,1310721,2,1835009,40,1646971,16,1966081,1310721,4,1579069,12,1310721,2,4, +1582507,2,1310721,2,3,26,4,3,1,10,28,1310721,2,5,26,4, +1310721,2,3,26,2,1647267,16,1310721,2,3,26,1,6,2,1,6, +-1,26,5,6,4,10,8,8,1417729,1310721,2,3,26,2,26,2, +3,28,1,6,1,10,28,34,63,38,3,26,1647171,16,1425921,1310721, +2,3,26,2,26,2,1310721,2,2,28,1,6,1,10,28,34, +63,38,3,26,1,6,1,10,28,34,63,38,1,10,28,1647173, +14,1,1647223,16,1442305,1310721,2,3,26,2,26,2,3,32,1,6, +1,10,28,34,63,38,1,10,28,1647225,14,1,1647263,16,1310721,2, +5,26,1310721,2,3,26,2,10,3,6,10,4,8,1647267,14,1647009, +14,1310721,2,5,26,2,1,40,1647291,16,1,1647545,14,1559041,1310721,2, +5,26,2,26,2,1647331,16,1559041,1310721,2,5,26,2,26,2,1647545, +14,1310721,2,55,26,1434113,1310721,2,5,26,2,26,2,3,38,63, +32,10,3,6,10,4,8,1310721,2,57,26,1,10,3,6,10, +4,8,1310721,2,57,26,2,1310721,2,55,26,2,40,1647497,16,1310721, +2,7,26,1310721,2,57,26,2,26,1584975,2,10,3,6,10,4, +8,1310721,2,57,26,1,6,2,1,6,3,26,5,6,4,10, +8,8,1647397,14,1584975,2,1310721,2,55,26,2,1310721,2,7,26,1434113, +1310721,2,5,26,2,26,2,1625329,12,1647545,14,1,1310721,2,59,26, +1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,1835009,40,1647593,16,1966081, +1310721,4,1579069,12,1310721,2,4,1310721,2,2,1588311,12,1310721,2,3,26, +4,1310721,2,3,26,2,1,40,1647643,16,3,1647707,14,1417729,1310721,2, +3,26,2,26,2,3,28,1,6,1,10,28,34,63,38,1, +10,28,1,6,1,10,28,34,63,38,1,10,28,1647717,16,1, +1647743,14,1425921,1310721,2,3,26,2,26,2,1646937,12,1647743,14,1,1310721, +2,5,26,1310721,4,18,1310721,2,1,28,1310721,4,1310721,2,1835009,40, +1647791,16,1966081,1310721,4,1579069,12,1584875,2,1647855,16,1584987,2,1629979,12,1, +6,1,10,28,34,63,38,3,26,1647839,16,3,1589235,12,8,1584875, +1,10,3,6,10,4,8,1,1647863,14,1,1310721,2,1,26,1310721, +4,18,1310721,2,11,28,1310721,4,1310721,2,1835009,40,1647911,16,1966081,1310721, +4,1579069,12,1310721,2,3,26,4,1310721,2,4,1425921,1310721,2,2,26, +2,1310721,2,5,26,4,1584881,2,1310721,2,7,26,4,1584881,1310721,2, +3,26,2,10,3,6,10,4,8,1584883,1,10,3,6,10,4, +8,1584987,1584975,2,10,3,6,10,4,8,1584989,1,10,3,6,10, +4,8,1310721,2,5,26,2,1,6,12,10,8,8,1584881,1310721,2, +7,26,2,10,3,6,10,4,8,1310721,2,9,26,1584883,2,10, +3,6,10,4,8,1584875,1,10,3,6,10,4,8,1584877,1,10, +3,6,10,4,8,1310721,2,9,26,2,1648155,14,1,1310721,2,11, +26,1310721,4,18,1310721,2,1,28,1310721,4,1310721,2,1835009,40,1648203,16, +1966081,1310721,4,1579069,12,1584881,2,1648303,16,1648295,14,116,104,105,115,32, +99,97,110,39,116,32,98,101,32,117,115,101,100,32,105,110, +115,105,100,101,32,97,110,32,101,120,112,114,101,115,115,105, +111,110,0,1648215,1585679,12,8,1,1648311,14,1,1310721,2,1,26,1310721, +4,18,1310721,2,13,28,1310721,4,1310721,2,1835009,40,1648359,16,1966081,1310721, +4,1579069,12,1310721,2,5,26,4,1310721,2,3,26,4,1310721,2,4, +1,1310721,2,7,26,4,1584887,2,1599031,12,8,1584889,2,1598497,12,1, +6,1,10,28,34,63,38,3,26,1648831,16,3,1648831,16,1310721,2, +9,26,1662195,12,10,3,6,10,4,8,1647757,12,8,1310721,2,9, +26,2,1310721,2,2,1310721,2,7,26,2,1310721,2,7,26,2,26, +38,7,32,1616761,12,8,1310721,2,3,26,2,1648593,16,1310721,2,7, +26,2,1310721,2,3,26,2,7,26,2,40,1,6,1,10,28, +34,63,38,1,10,28,1648595,14,1,1648757,16,1310721,2,11,26,1310721, +2,3,26,2,15,1310721,2,7,26,2,26,26,2,10,3,6, +10,4,8,1310721,2,11,26,2,1584987,2,1310721,2,5,26,2,1631591, +12,1,6,1,10,28,34,63,38,3,26,1648757,16,1648721,14,97, +114,103,117,109,101,110,116,0,1648703,1310721,2,11,26,2,1310721,2, +5,26,2,1628749,12,1584987,2,1633925,12,8,1310721,2,7,26,1,6, +2,1,6,3,26,5,6,4,10,8,8,1584899,2,1598801,12,1, +6,1,10,28,34,63,38,3,26,1648827,16,1648831,14,1648439,14,1584889, +2,1599031,12,8,1310721,2,7,26,2,1648857,14,1,1310721,2,13,26, +1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,1835009,40,1648905,16,1966081, +1310721,4,1579069,12,1310721,2,1635249,12,10,3,6,10,4,8,1584899,2, +1598801,12,1649069,16,1310721,2,3,26,1662195,12,10,3,6,10,4,8, +1647757,12,8,1310721,2,3,26,2,1605077,12,8,1310721,2,2,1628577,12, +3,28,1,6,1,10,28,34,63,38,1,10,28,1649047,16,1310721, +2,2,1628577,12,1589149,12,8,31,1589235,12,8,1310721,2,2,1,1624809, +12,1589149,12,8,1649097,14,1310721,2,2,1628577,12,1589149,12,8,1310721,2, +2,1589149,12,8,1584889,2,1599031,12,8,1585043,2,1589915,12,8,1584875,1, +10,3,6,10,4,8,1584877,1,10,3,6,10,4,8,1584987,1310721, +2,2,1624435,12,10,3,6,10,4,8,1584989,1,10,3,6,10, +4,8,3,1649197,14,1,1310721,2,5,26,1310721,4,18,1310721,2,27, +28,1310721,4,1310721,2,1835009,40,1649245,16,1966081,1310721,4,1579069,12,1310721,2, +4,1,1310721,2,5,26,4,1,1310721,2,7,26,4,1559041,1310721,2, +2,26,2,1310721,2,9,26,4,1409537,1310721,2,2,26,2,1585041,2, +28,1,6,1,10,28,34,63,38,3,26,1649401,16,1417729,1310721,2, +2,26,2,3,28,1,6,1,10,28,34,63,38,3,26,1, +6,1,10,28,34,63,38,1,10,28,1649403,14,1,1649731,16,1597931, +12,8,1584799,2,9,28,1,6,1,10,28,34,63,38,3,26, +1649563,16,1584801,2,1584911,2,28,1,6,1,10,28,34,63,38,3, +26,1649485,16,3,1649537,14,1584801,2,1584895,2,28,1,6,1,10,28, +34,63,38,3,26,1,6,1,10,28,34,63,38,1,10,28, +1,6,1,10,28,34,63,38,1,10,28,1649565,14,1,1649583,16, +1597071,12,8,1648871,12,1650331,14,1584799,2,5,28,1,6,1,10,28, +34,63,38,3,26,1649711,16,1584801,2,1634911,12,1649633,16,3,1649685,14, +1584801,2,1584927,2,28,1,6,1,10,28,34,63,38,3,26,1, +6,1,10,28,34,63,38,1,10,28,1,6,1,10,28,34, +63,38,1,10,28,1649713,14,1,1649731,16,1597071,12,8,1648871,12,1650331, +14,1442305,1310721,2,2,26,2,5,32,1649827,16,1310721,2,5,26,1434113, +1310721,2,2,26,2,13,38,10,3,6,10,4,8,1310721,2,7, +26,1434113,1310721,2,2,26,2,3,32,10,3,6,10,4,8,1649865, +14,1442305,1310721,2,2,26,1442305,1310721,2,2,26,2,9,34,10,3, +6,10,4,8,1310721,2,11,26,1,17,1574549,12,8,1585087,2,1649927, +16,1310721,2,11,26,1585071,1585087,2,1574337,12,8,1585087,1,10,3,6, +10,4,8,1310721,2,3,26,1310721,2,5,26,2,1310721,2,9,26, +2,1310721,2,11,26,1648325,12,10,3,6,10,4,8,1442305,1310721,2, +2,26,2,5,32,1650079,16,1310721,2,3,26,2,1434113,1310721,2,2, +26,2,3,38,63,32,28,1,6,1,10,28,34,63,38,1, +10,28,1,6,1,10,28,34,63,38,1,10,28,1650081,14,1, +1650161,16,1409537,1310721,2,2,26,2,1650153,14,119,114,111,110,103,32, +110,117,109,98,101,114,32,111,102,32,97,114,103,117,109,101, +110,116,115,0,1650101,1585849,12,8,1310721,2,2,1589741,12,8,13,1589235, +12,8,1584875,1,10,3,6,10,4,8,1584877,1,10,3,6,10, +4,8,1584989,1,10,3,6,10,4,8,1584987,1584975,2,10,3,6, +10,4,8,1310721,2,9,26,2,1650303,16,1584987,1310721,2,9,26,2, +9,26,2,1310721,2,11,26,1628749,12,10,3,6,10,4,8,1310721, +2,7,26,2,1650323,16,3,1650331,14,1,1650331,14,1,1310721,2,27, +26,1310721,4,18,1310721,2,11,28,1310721,4,1310721,2,1835009,40,1650379,16, +1966081,1310721,4,1579069,12,1310721,2,4,1434113,1310721,2,2,26,2,31,32, +1310721,2,3,26,4,1434113,1310721,2,2,26,2,9,38,31,32,1310721, +2,5,26,4,1,1310721,2,7,26,4,1584887,2,1599031,12,8,1584889, +2,1598497,12,1,6,1,10,28,34,63,38,3,26,1650621,16,3, +1650621,16,1310721,2,9,26,1662195,12,10,3,6,10,4,8,1647757,12, +8,1310721,2,9,26,2,1605077,12,8,1310721,2,7,26,1,6,2, +1,6,3,26,5,6,4,10,8,8,1584899,2,1598801,12,1,6, +1,10,28,34,63,38,3,26,1650617,16,1650621,14,1650495,14,1584889,2, +1599031,12,8,1310721,2,7,26,2,1310721,2,3,26,2,28,1,6, +1,10,28,34,63,38,1,10,28,1650749,16,1409537,1310721,2,2,26, +2,1650741,14,119,114,111,110,103,32,110,117,109,98,101,114,32, +111,102,32,105,110,112,117,116,115,0,1650695,1585849,12,8,1584875,1, +10,3,6,10,4,8,1584877,1,10,3,6,10,4,8,1584987,1584975, +2,10,3,6,10,4,8,1584989,1,10,3,6,10,4,8,1442305, +1310721,2,2,26,2,129,32,1650871,16,1450497,1310721,2,2,26,2,3, +28,1589741,12,8,13,1589235,12,8,1,1651069,14,45,1425921,1310721,2,2, +26,2,26,1589235,12,8,1310721,2,5,26,2,1,28,1,6,1, +10,28,34,63,38,3,26,1650939,16,1,1589149,12,8,1310721,2,5, +26,2,3,10,40,1651061,16,1409537,1310721,2,2,26,2,1651053,14,108, +101,97,118,101,115,32,115,101,118,101,114,97,108,32,118,97, +108,117,101,115,59,32,117,115,101,32,105,116,32,102,114,111, +109,32,97,115,109,0,1650975,1585849,12,8,1,1651069,14,1,1310721,2, +11,26,1310721,4,18,1310721,2,9,28,1310721,4,1310721,2,1835009,40,1651117, +16,1966081,1310721,4,1579069,12,1584875,1,10,3,6,10,4,8,1584877,1, +10,3,6,10,4,8,1584989,1,10,3,6,10,4,8,1582577,2, +3,28,1,6,1,10,28,34,63,38,3,26,1651279,16,1582579,2, +1589149,12,8,1584989,1582579,2,1,28,1,6,1,10,28,34,63,38, +3,26,10,3,6,10,4,8,1584987,1584977,2,10,3,6,10,4, +8,1597071,12,8,1,1653909,14,1582577,2,7,28,1,6,1,10,28, +34,63,38,3,26,1651355,16,1582579,2,1590177,12,8,1597071,12,8,1584987, +1584979,2,1624435,12,10,3,6,10,4,8,1,1653909,14,1582577,2,9, +28,1,6,1,10,28,34,63,38,3,26,1651607,16,1582579,2,1584887, +2,28,1,6,1,10,28,34,63,38,3,26,1651475,16,1597071,12, +8,1310721,2,7,26,1662195,12,10,3,6,10,4,8,1584889,2,1599031, +12,8,1310721,2,7,26,2,1653909,14,1582579,2,1584891,2,28,1,6, +1,10,28,34,63,38,3,26,1651517,16,1664235,12,1653909,14,1651567,14, +101,120,112,101,99,116,101,100,32,97,110,32,101,120,112,114, +101,115,115,105,111,110,0,1651521,1586063,12,8,1,1589149,12,8,1584987, +1584975,2,10,3,6,10,4,8,1,1653909,14,1582577,2,5,28,1, +6,1,10,28,34,63,38,1,10,28,1651731,16,1651691,14,101,120, +112,101,99,116,101,100,32,97,110,32,101,120,112,114,101,115, +115,105,111,110,0,1651645,1586063,12,8,1,1589149,12,8,1584987,1584975,2, +10,3,6,10,4,8,1,1653909,14,1310721,2,1582579,2,10,3,6, +10,4,8,1310721,2,3,26,1310721,2,2,1603687,12,10,3,6,10, +4,8,1310721,2,3,26,2,1,40,3,36,1652203,16,1597071,12,8, +1584853,2,1,6,1,10,28,34,63,38,3,26,1651877,16,1310721,2, +3,26,2,1604491,12,1,6,1,10,28,34,63,38,1,10,28, +1651879,14,1,1652027,16,1310721,2,2,1652019,14,105,115,32,116,114,97, +99,107,101,100,32,98,121,32,116,104,101,32,97,110,97,108, +121,122,101,114,32,97,110,100,32,99,97,110,39,116,32,98, +101,32,117,115,101,100,32,105,110,32,117,110,99,104,101,99, +107,101,100,32,99,111,100,101,0,1651893,1585849,12,8,1467905,1310721,2, +3,26,2,26,2,1590069,12,8,1584987,1556993,1310721,2,3,26,2,26, +2,10,3,6,10,4,8,1473025,1310721,2,3,26,2,26,2,1652105, +16,1,1653909,14,1584875,3,10,3,6,10,4,8,1584877,3,10,3, +6,10,4,8,1584879,1310721,2,3,26,2,10,3,6,10,4,8, +1310721,2,3,26,2,1604491,12,1652197,16,5,1310721,2,3,26,2,26, +1653909,14,1,1653909,14,1310721,2,5,26,1310721,2,2,1587809,12,10,3, +6,10,4,8,1310721,2,5,26,2,1,40,3,36,1652321,16,1417729, +1310721,2,5,26,2,26,2,11,28,1,6,1,10,28,34,63, +38,3,26,1,6,1,10,28,34,63,38,1,10,28,1652323,14, +1,1652479,16,1442305,1310721,2,5,26,2,26,2,33,32,1,6,1, +10,28,34,63,38,3,26,1652459,16,1310721,2,2,1652451,14,99,97, +110,39,116,32,98,101,32,117,115,101,100,32,105,110,115,105, +100,101,32,97,110,32,101,120,112,114,101,115,115,105,111,110, +0,1652381,1586197,12,8,1310721,2,5,26,2,3,1647877,12,1653909,14,1310721, +2,5,26,2,1,40,3,36,1652567,16,1417729,1310721,2,5,26,2, +26,2,15,28,1,6,1,10,28,34,63,38,3,26,1,6, +1,10,28,34,63,38,1,10,28,1652569,14,1,1652669,16,1310721,2, +2,1652629,14,105,115,32,97,32,116,121,112,101,44,32,110,111, +116,32,97,32,118,97,108,117,101,0,1652583,1586197,12,8,1,1589149, +12,8,1584987,1584975,2,10,3,6,10,4,8,1,1653909,14,1597071,12, +8,1584987,1584975,2,10,3,6,10,4,8,1310721,2,5,26,2,1, +40,3,36,1652781,16,1417729,1310721,2,5,26,2,26,2,9,28,1, +6,1,10,28,34,63,38,3,26,1,6,1,10,28,34,63, +38,1,10,28,1652783,14,1,1652887,16,1425921,1310721,2,5,26,2,26, +2,1589149,12,8,1584987,1584977,2,10,3,6,10,4,8,1584989,1425921,1310721, +2,5,26,2,26,2,1,28,1,6,1,10,28,34,63,38, +3,26,10,3,6,10,4,8,1,1653909,14,1310721,2,5,26,2, +1,40,3,36,1652975,16,1417729,1310721,2,5,26,2,26,2,5,28, +1,6,1,10,28,34,63,38,3,26,1,6,1,10,28,34, +63,38,1,10,28,1652977,14,1,1653091,16,1425921,1310721,2,5,26,2, +26,2,1589149,12,8,1559041,1310721,2,5,26,2,26,2,1653053,16,1584987, +1559041,1310721,2,5,26,2,26,2,10,3,6,10,4,8,1584875,3, +10,3,6,10,4,8,1584877,5,10,3,6,10,4,8,1,1653909, +14,1310721,2,5,26,2,1,40,3,36,1653179,16,1417729,1310721,2,5, +26,2,26,2,7,28,1,6,1,10,28,34,63,38,3,26, +1,6,1,10,28,34,63,38,1,10,28,1653181,14,1,1653263,16, +1425921,1310721,2,5,26,2,26,2,1589149,12,8,1559041,1310721,2,5,26, +2,26,2,1653257,16,1584987,1559041,1310721,2,5,26,2,26,2,10,3, +6,10,4,8,1,1653909,14,1310721,2,5,26,2,1,40,3,36, +1653351,16,1417729,1310721,2,5,26,2,26,2,13,28,1,6,1,10, +28,34,63,38,3,26,1,6,1,10,28,34,63,38,1,10, +28,1653353,14,1,1653375,16,1310721,2,5,26,2,1650345,12,1653909,14,1310721, +2,5,26,2,1,40,1653423,16,1310721,2,5,26,1310721,2,2,1589617, +12,10,3,6,10,4,8,1585015,2,1598497,12,1653481,16,1442305,1310721,2, +5,26,2,26,2,257,32,1,6,1,10,28,34,63,38,1, +10,28,1653483,14,1,1653805,16,1597071,12,8,1585087,1,10,3,6,10, +4,8,3,1653683,16,1585087,2,17,40,3,36,1653591,16,1653583,14,116, +111,111,32,109,97,110,121,32,116,121,112,101,32,97,114,103, +117,109,101,110,116,115,0,1653535,1586063,12,8,1585071,1585087,2,26,1635249, +12,10,3,6,10,4,8,1585087,1,6,2,1,6,3,26,5, +6,4,10,8,8,1584899,2,1598801,12,1,6,1,10,28,34,63, +38,3,26,1653679,16,1653683,14,1653509,14,1635147,12,8,1584887,2,1598497,12, +1,6,1,10,28,34,63,38,3,26,1653805,16,1653797,14,101,120, +112,101,99,116,101,100,32,40,32,97,102,116,101,114,32,116, +104,101,32,116,121,112,101,32,97,114,103,117,109,101,110,116, +115,0,1653725,1586063,12,8,1584887,2,1598497,12,1653835,16,1310721,2,5,26, +2,1649211,12,1653909,14,1310721,2,5,26,2,1589741,12,8,1559041,1310721,2, +5,26,2,26,2,1653901,16,1584987,1559041,1310721,2,5,26,2,26,2, +10,3,6,10,4,8,1,1653909,14,1,1310721,2,9,26,1310721,4, +18,1310721,2,5,28,1310721,4,1310721,2,1835009,40,1653957,16,1966081,1310721,4, +1579069,12,1310721,2,3,26,4,1310721,2,4,1584875,2,1,6,1,10, +28,34,63,38,3,26,1654089,16,1654081,14,43,43,32,97,110,100, +32,45,45,32,110,101,101,100,32,115,111,109,101,116,104,105, +110,103,32,116,111,32,97,115,115,105,103,110,32,116,111,0, +1654005,1586063,12,8,1584877,2,3,28,1,6,1,10,28,34,63,38, +3,26,1654151,16,1584853,2,1,6,1,10,28,34,63,38,1,10, +28,1654153,14,1,1654191,16,1584879,2,1604491,12,1,6,1,10,28,34, +63,38,1,10,28,1654193,14,1,1654293,16,1584879,2,1654285,14,111,119, +110,115,32,97,110,32,111,98,106,101,99,116,32,97,110,100, +32,99,97,110,39,116,32,98,101,32,105,110,99,114,101,109, +101,110,116,101,100,0,1654205,1586469,12,8,1584987,2,1629747,12,1654311,16, +3,1654367,14,1584987,2,1,26,2,11,28,1,6,1,10,28,34, +63,38,3,26,1,6,1,10,28,34,63,38,1,10,28,1654377, +16,3,1654429,14,1584987,2,1584975,2,28,1,6,1,10,28,34,63, +38,3,26,1,6,1,10,28,34,63,38,1,10,28,1654507,14, +43,43,32,97,110,100,32,45,45,32,110,101,101,100,32,97, +32,110,117,109,98,101,114,32,111,114,32,97,32,112,111,105, +110,116,101,114,0,1654433,1584987,2,1634325,12,8,1584875,1,10,3,6, +10,4,8,1584877,1,10,3,6,10,4,8,1584989,1,10,3,6, +10,4,8,1,1589149,12,8,7,1589235,12,8,3,1589235,12,8,1310721, +2,2,1654669,16,1,1589149,12,8,7,1589235,12,8,1310721,2,3,26, +2,1589149,12,8,27,1589235,12,8,5,1589149,12,8,7,1589235,12,8, +5,1589235,12,8,1654733,14,1310721,2,3,26,2,1589149,12,8,27,1589235, +12,8,1,1589149,12,8,7,1589235,12,8,5,1589149,12,8,7,1589235, +12,8,5,1589235,12,8,11,1589235,12,8,9,1589235,12,8,1,1654757, +14,1,1310721,2,5,26,1310721,4,18,1310721,2,11,28,1310721,4,1310721, +2,1835009,40,1654805,16,1966081,1310721,4,1579069,12,1310721,2,4,3,1,10, +28,1310721,2,9,26,4,1597071,12,8,1310721,2,3,26,1599321,12,10, +3,6,10,4,8,1647757,12,8,1310721,2,2,3,28,1,6,1, +10,28,34,63,38,3,26,1655035,16,1655023,14,117,115,101,32,97, +32,102,105,101,108,100,32,111,102,32,97,32,110,101,119,32, +111,98,106,101,99,116,32,111,110,108,121,32,97,102,116,101, +114,32,98,105,110,100,105,110,103,32,105,116,32,116,111,32, +97,32,110,97,109,101,0,1654903,1586351,12,8,1655099,14,1310721,2,2, +5,40,3,36,1655099,16,1310721,2,2,5,28,1604589,12,8,1310721,2, +9,26,1310721,2,2,5,28,10,3,6,10,4,8,1310721,2,5, +26,1584987,2,10,3,6,10,4,8,1310721,2,5,26,2,1,26, +2,11,28,1,6,1,10,28,34,63,38,3,26,1655203,16,1310721, +2,5,26,1310721,2,5,26,2,9,26,2,10,3,6,10,4, +8,1310721,2,5,26,2,1,26,2,17,28,1,6,1,10,28, +34,63,38,1,10,28,1655553,16,1585091,2,1,6,1,10,28,34, +63,38,3,26,1655327,16,1584991,2,1,6,1,10,28,34,63,38, +3,26,1,6,1,10,28,34,63,38,1,10,28,1655329,14,1, +1655497,16,1585093,12,8,93,1573101,12,8,1310721,2,3,26,2,1573341,12, +8,1655391,14,58,32,116,104,97,116,39,115,32,97,32,0,1655367, +1573341,12,8,1584987,2,1632681,12,8,1655483,14,44,32,110,111,116,32, +97,32,115,116,114,117,99,116,32,111,114,32,97,32,112,111, +105,110,116,101,114,32,116,111,32,111,110,101,0,1655413,1573341,12, +8,1585237,12,8,1584987,1584975,2,10,3,6,10,4,8,1584875,3,10, +3,6,10,4,8,1584877,9,10,3,6,10,4,8,1,1656109,14, +1310721,2,7,26,1310721,2,5,26,2,1310721,2,3,26,2,1640559,12, +10,3,6,10,4,8,1310721,2,7,26,2,1,40,1655841,16,1585091, +2,1,6,1,10,28,34,63,38,3,26,1655693,16,1584991,2,1, +6,1,10,28,34,63,38,3,26,1,6,1,10,28,34,63, +38,1,10,28,1655695,14,1,1655785,16,1585093,12,8,1310721,2,5,26, +2,1632681,12,8,1655755,14,32,104,97,115,32,110,111,32,102,105, +101,108,100,32,0,1655725,1573341,12,8,1310721,2,3,26,2,1573341,12, +8,1585237,12,8,1584987,1584975,2,10,3,6,10,4,8,1584875,3,10, +3,6,10,4,8,1584877,9,10,3,6,10,4,8,1,1656109,14, +1310721,2,5,26,2,9,26,2,1310721,2,7,26,2,7,30,5, +26,26,2,1655935,16,1310721,2,5,26,2,9,26,2,1310721,2,7, +26,2,7,30,5,26,26,2,1589149,12,8,27,1589235,12,8,1584987, +1310721,2,5,26,2,9,26,2,1310721,2,7,26,2,7,30,3, +26,26,2,10,3,6,10,4,8,1584875,3,10,3,6,10,4, +8,1584989,1,10,3,6,10,4,8,1310721,2,9,26,2,1,40, +3,36,1656085,16,1584877,7,10,3,6,10,4,8,1584879,1310721,2,9, +26,2,10,3,6,10,4,8,1656101,14,1584877,9,10,3,6,10, +4,8,1,1656109,14,1,1310721,2,11,26,1310721,4,18,1310721,2,29, +28,1310721,4,1310721,2,1835009,40,1656157,16,1966081,1310721,4,1579069,12,1651083,12, +1310721,2,4,3,1658085,16,1582577,2,5,28,1,6,1,10,28,34, +63,38,3,26,1656261,16,1582579,2,1584935,2,28,1,6,1,10,28, +34,63,38,3,26,1,6,1,10,28,34,63,38,1,10,28, +1656263,14,1,1656347,16,1597071,12,8,1310721,2,7,26,1635249,12,10,3, +6,10,4,8,1647757,12,8,1584987,1310721,2,7,26,2,10,3,6, +10,4,8,1584877,1,10,3,6,10,4,8,1656167,14,1582577,2,9, +28,1,6,1,10,28,34,63,38,1,10,28,1656391,16,1310721,2, +2,1658097,14,1582579,2,1584895,2,28,1,6,1,10,28,34,63,38, +3,26,1657305,16,1597071,12,8,1647757,12,8,1310721,2,3,26,3,1, +10,28,10,3,6,10,4,8,1310721,2,2,3,28,1,6,1, +10,28,34,63,38,3,26,1656617,16,1656605,14,105,110,100,101,120, +32,97,32,110,101,119,32,111,98,106,101,99,116,32,111,110, +108,121,32,97,102,116,101,114,32,98,105,110,100,105,110,103, +32,105,116,32,116,111,32,97,32,110,97,109,101,0,1656503,1586351, +12,8,1656681,14,1310721,2,2,5,40,3,36,1656681,16,1310721,2,2, +5,28,1604589,12,8,1310721,2,3,26,1310721,2,2,5,28,10,3, +6,10,4,8,1584987,2,1630143,12,1656699,16,3,1656751,14,1584987,2,1584975, +2,28,1,6,1,10,28,34,63,38,3,26,1,6,1,10, +28,34,63,38,1,10,28,1656835,14,111,110,108,121,32,112,111, +105,110,116,101,114,115,32,97,110,100,32,97,114,114,97,121, +115,32,99,97,110,32,98,101,32,105,110,100,101,120,101,100, +0,1656755,1584987,2,1634325,12,8,1310721,2,9,26,1584987,2,1630433,12,10, +3,6,10,4,8,1310721,2,1662195,12,10,3,6,10,4,8,1647757, +12,8,1310721,2,2,1605077,12,8,1584987,2,1629747,12,1656931,16,3,1656983, +14,1584987,2,1584975,2,28,1,6,1,10,28,34,63,38,3,26, +1,6,1,10,28,34,63,38,1,10,28,1657043,14,97,110,32, +105,110,100,101,120,32,104,97,115,32,116,111,32,98,101,32, +97,32,110,117,109,98,101,114,0,1656987,1584987,2,1634325,12,8,1584897, +2,1599031,12,8,1310721,2,9,26,2,1628577,12,3,28,1,6,1, +10,28,34,63,38,1,10,28,1657137,16,1310721,2,9,26,2,1628577, +12,1589149,12,8,31,1589235,12,8,27,1589235,12,8,1584875,3,10,3, +6,10,4,8,1584987,1310721,2,9,26,2,10,3,6,10,4,8, +1584989,1,10,3,6,10,4,8,1310721,2,3,26,2,1,40,3, +36,1657267,16,1584877,7,10,3,6,10,4,8,1584879,1310721,2,3,26, +2,10,3,6,10,4,8,1657283,14,1584877,9,10,3,6,10,4, +8,1310721,2,1,10,3,6,10,4,8,1656167,14,1582579,2,1584925,2, +28,1,6,1,10,28,34,63,38,3,26,1657345,16,3,1657397,14, +1582579,2,1584905,2,28,1,6,1,10,28,34,63,38,3,26,1, +6,1,10,28,34,63,38,1,10,28,1657431,16,1310721,2,1310721,2, +2,1654771,12,10,3,6,10,4,8,1656167,14,1582579,2,1584887,2,28, +1,6,1,10,28,34,63,38,3,26,1657929,16,1647757,12,8,1310721, +2,2,1605077,12,8,1310721,2,11,26,1,10,3,6,10,4,8, +1584987,2,1,26,2,15,28,1,6,1,10,28,34,63,38,3, +26,1657571,16,1310721,2,11,26,1584987,2,10,3,6,10,4,8,1657675, +14,1584987,2,1584975,2,28,1,6,1,10,28,34,63,38,3,26, +1657663,14,111,110,108,121,32,102,117,110,99,116,105,111,110,115, +32,99,97,110,32,98,101,32,99,97,108,108,101,100,0,1657605, +1584987,2,1634325,12,8,1310721,2,13,26,1,17,1574549,12,8,1310721,2, +5,26,1,1310721,2,11,26,2,1310721,2,13,26,1648325,12,10,3, +6,10,4,8,1310721,2,5,26,2,1589149,12,8,7,1589235,12,8, +13,1589235,12,8,11,1589235,12,8,9,1589235,12,8,1584875,1,10,3, +6,10,4,8,1584877,1,10,3,6,10,4,8,1310721,2,1,10, +3,6,10,4,8,1584989,1,10,3,6,10,4,8,1584987,1584975,2, +10,3,6,10,4,8,1310721,2,11,26,2,1657925,16,1584987,1310721,2, +11,26,2,9,26,2,1310721,2,13,26,1628749,12,10,3,6,10, +4,8,1656167,14,1582579,2,1584907,2,28,1,6,1,10,28,34,63, +38,3,26,1657997,16,1597071,12,8,1310721,2,3,3,1653923,12,10,3, +6,10,4,8,1656167,14,1582579,2,1584909,2,28,1,6,1,10,28, +34,63,38,3,26,1658071,16,1597071,12,8,1310721,2,3,3,1,10, +28,1653923,12,10,3,6,10,4,8,1656167,14,1310721,2,2,1658097,14, +1656167,14,1310721,2,2,1658097,14,1,1310721,2,29,26,1310721,4,18,1310721, +2,7,28,1310721,4,1310721,2,1835009,40,1658145,16,1966081,1310721,4,1579069,12, +1582577,2,5,28,1,6,1,10,28,34,63,38,3,26,1658357,16, +1582579,2,1584945,2,28,1,6,1,10,28,34,63,38,3,26,1658217, +16,3,1658269,14,1582579,2,1584941,2,28,1,6,1,10,28,34,63, +38,3,26,1,6,1,10,28,34,63,38,1,10,28,1658279,16, +3,1658331,14,1582579,2,1584943,2,28,1,6,1,10,28,34,63,38, +3,26,1,6,1,10,28,34,63,38,1,10,28,1,6,1, +10,28,34,63,38,1,10,28,1658359,14,1,1658579,16,1310721,2,5, +26,3,10,3,6,10,4,8,1582579,2,1584945,2,28,1,6,1, +10,28,34,63,38,3,26,1658441,16,1310721,2,5,26,5,10,3, +6,10,4,8,1582579,2,1584943,2,28,1,6,1,10,28,34,63, +38,3,26,1658497,16,1310721,2,5,26,7,10,3,6,10,4,8, +1597071,12,8,1584911,2,1599031,12,8,1310721,2,1658111,12,10,3,6,10, +4,8,1647757,12,8,1584877,1,10,3,6,10,4,8,1310721,2,2, +1310721,2,5,26,2,1615689,12,1659775,14,1582577,2,9,28,1,6,1, +10,28,34,63,38,3,26,1659765,16,1582579,2,1584911,2,28,1,6, +1,10,28,34,63,38,3,26,1658945,16,1597071,12,8,1310721,2,1658111, +12,10,3,6,10,4,8,1647757,12,8,1310721,2,2,1605077,12,8, +1584987,2,1,26,2,11,28,1,6,1,10,28,34,63,38,3, +26,1658733,16,3,1658785,14,1584987,2,1584975,2,28,1,6,1,10,28, +34,63,38,3,26,1,6,1,10,28,34,63,38,1,10,28, +1658857,14,111,110,108,121,32,112,111,105,110,116,101,114,115,32, +99,97,110,32,98,101,32,100,101,114,101,102,101,114,101,110, +99,101,100,0,1658789,1584987,2,1634325,12,8,1584987,1584987,2,1630433,12,10, +3,6,10,4,8,1584875,3,10,3,6,10,4,8,1584877,9,10, +3,6,10,4,8,1584989,1,10,3,6,10,4,8,1,1659775,14, +1582579,2,1584913,2,28,1,6,1,10,28,34,63,38,3,26,1659275, +16,1597071,12,8,1310721,2,1658111,12,10,3,6,10,4,8,1584875,2, +1,6,1,10,28,34,63,38,3,26,1659087,16,1584987,2,1629979,12, +1,6,1,10,28,34,63,38,3,26,1,6,1,10,28,34, +63,38,1,10,28,1659089,14,1,1659165,16,1659157,14,38,32,110,101, +101,100,115,32,115,111,109,101,116,104,105,110,103,32,97,100, +100,114,101,115,115,97,98,108,101,0,1659097,1586063,12,8,1310721,2, +2,5,40,3,36,1659199,16,1310721,2,2,5,28,1604589,12,8,1584875, +1,10,3,6,10,4,8,1584877,1,10,3,6,10,4,8,1584989, +1,10,3,6,10,4,8,1584987,1584987,2,1624435,12,10,3,6,10, +4,8,1,1659775,14,1582579,2,1584907,2,28,1,6,1,10,28,34, +63,38,3,26,1659333,16,1597071,12,8,1658111,12,8,1,3,1653923,12, +1659775,14,1582579,2,1584909,2,28,1,6,1,10,28,34,63,38,3, +26,1659397,16,1597071,12,8,1658111,12,8,1,3,1,10,28,1653923,12, +1659775,14,1310721,2,3,26,1582579,2,1601755,12,10,3,6,10,4,8, +1310721,2,3,26,2,1,40,3,36,1659765,16,1597071,12,8,1310721,2, +1658111,12,10,3,6,10,4,8,1647757,12,8,1310721,2,2,1605077,12, +8,1547329,1310721,2,3,26,2,26,2,1547393,1310721,2,3,26,2,26, +2,1600817,12,8,1310721,2,3,26,2,3,10,40,3,36,1659575,16, +1584987,1584981,2,10,3,6,10,4,8,1659711,14,1584987,2,1584975,2,28, +1,6,1,10,28,34,63,38,1,10,28,1659711,16,1584987,2,1629747, +12,1659681,14,116,104,105,115,32,111,112,101,114,97,116,111,114, +32,110,101,101,100,115,32,97,32,110,117,109,98,101,114,0, +1659623,1584987,2,1634325,12,8,1584987,1584977,2,10,3,6,10,4,8,1584875, +1,10,3,6,10,4,8,1584877,1,10,3,6,10,4,8,1584989, +1,10,3,6,10,4,8,1,1659775,14,1656123,12,1659775,14,1,1310721, +2,7,26,1310721,4,18,1310721,2,15,28,1310721,4,1310721,2,1835009,40, +1659823,16,1966081,1310721,4,1579069,12,1310721,2,4,1658111,12,1310721,2,3,26, +4,1582577,2,9,28,1,6,1,10,28,34,63,38,3,26,1660769, +16,1310721,2,5,26,1582579,2,1601559,12,10,3,6,10,4,8,1310721, +2,5,26,2,1,40,1659935,16,1310721,2,3,26,2,1660785,14,1310721, +2,7,26,1546881,1310721,2,5,26,2,26,2,10,3,6,10,4, +8,1310721,2,7,26,2,1310721,2,2,40,1660007,16,1310721,2,3,26, +2,1660785,14,1597071,12,8,1647757,12,8,1310721,2,3,26,2,1605077,12, +8,1310721,2,13,26,1584987,2,10,3,6,10,4,8,1547137,1310721,2, +5,26,2,26,2,3,1,10,28,28,1,6,1,10,28,34, +63,38,3,26,1660307,16,1310721,2,9,26,1589535,12,10,3,6,10, +4,8,17,1589235,12,8,1310721,2,3,26,1310721,2,7,26,2,3, +26,1659789,12,10,3,6,10,4,8,1647757,12,8,1310721,2,3,26, +2,1605077,12,8,1547329,3,26,2,1547393,3,26,2,1600817,12,8,1310721, +2,11,26,1589535,12,10,3,6,10,4,8,15,1589235,12,8,1310721, +2,9,26,2,1584837,2,1589317,12,8,1,1589149,12,8,1310721,2,11, +26,2,1584837,2,1589317,12,8,1660653,14,1547137,1310721,2,5,26,2,26, +2,5,1,10,28,28,1,6,1,10,28,34,63,38,3,26, +1660555,16,1310721,2,9,26,1589535,12,10,3,6,10,4,8,17,1589235, +12,8,3,1589149,12,8,1310721,2,11,26,1589535,12,10,3,6,10, +4,8,15,1589235,12,8,1310721,2,9,26,2,1584837,2,1589317,12,8, +1310721,2,3,26,1310721,2,7,26,2,3,26,1659789,12,10,3,6, +10,4,8,1647757,12,8,1310721,2,3,26,2,1605077,12,8,1547329,3, +26,2,1547393,3,26,2,1600817,12,8,1310721,2,11,26,2,1584837,2, +1589317,12,8,1660653,14,1310721,2,3,26,1310721,2,7,26,2,3,26, +1659789,12,10,3,6,10,4,8,1647757,12,8,1310721,2,3,26,2, +1605077,12,8,1547009,1310721,2,5,26,2,26,2,1547137,1310721,2,5,26, +2,26,2,1600817,12,8,1584987,1310721,2,5,26,2,1310721,2,13,26, +2,1584987,2,1642719,12,10,3,6,10,4,8,1584875,1,10,3,6, +10,4,8,1584877,1,10,3,6,10,4,8,1310721,2,3,26,1, +10,3,6,10,4,8,1584989,1,10,3,6,10,4,8,1659843,14, +1310721,2,3,26,2,1660785,14,1,1310721,2,15,26,1310721,4,18,1310721, +2,1,28,1310721,4,1310721,2,1835009,40,1660833,16,1966081,1310721,4,1579069,12, +11,1589235,12,8,3,1589149,12,8,7,1589235,12,8,11,1589235,12,8, +5,1589235,12,8,1,1660881,14,1,1310721,2,1,26,1310721,4,18,1310721, +2,23,28,1310721,4,1310721,2,1835009,40,1660929,16,1966081,1310721,4,1579069,12, +1310721,2,4,1584857,2,1310721,2,13,26,4,1584845,2,1310721,2,15,26, +4,1647757,12,8,1310721,2,2,1605077,12,8,1597071,12,8,1310721,2,3, +26,1589535,12,10,3,6,10,4,8,17,1589235,12,8,1310721,2,7, +26,1606761,12,10,3,6,10,4,8,1310721,2,17,26,1662195,12,10, +3,6,10,4,8,1647757,12,8,1310721,2,21,26,1584987,2,10,3, +6,10,4,8,1310721,2,17,26,2,3,28,1,6,1,10,28, +34,63,38,1,10,28,1661153,16,1310721,2,17,26,2,1605077,12,8, +1310721,2,11,26,1584857,2,10,3,6,10,4,8,1310721,2,9,26, +1606761,12,10,3,6,10,4,8,1310721,2,5,26,1589535,12,10,3, +6,10,4,8,15,1589235,12,8,1584915,2,1599031,12,8,1310721,2,3, +26,2,1584837,2,1589317,12,8,1310721,2,7,26,2,1607393,12,8,1584857, +1310721,2,13,26,2,10,3,6,10,4,8,1310721,2,19,26,1662195, +12,10,3,6,10,4,8,1647757,12,8,1310721,2,19,26,2,3, +28,1,6,1,10,28,34,63,38,1,10,28,1661389,16,1310721,2, +19,26,2,1605077,12,8,1310721,2,9,26,2,1310721,2,11,26,2, +1310721,2,5,26,2,1584857,2,1310721,2,15,26,2,1661527,14,101,110, +100,115,32,116,104,101,32,116,119,111,32,115,105,100,101,115, +32,111,102,32,63,58,32,105,110,32,100,105,102,102,101,114, +101,110,116,32,115,116,97,116,101,115,0,1661437,1615115,12,8,1310721, +2,7,26,2,1607311,12,8,1310721,2,21,26,2,1584987,2,1630621,12, +1,6,1,10,28,34,63,38,3,26,1661657,16,1584987,2,1310721,2, +21,26,2,1630621,12,1,6,1,10,28,34,63,38,3,26,1, +6,1,10,28,34,63,38,1,10,28,1661659,14,1,1661729,16,1661707, +14,116,104,101,32,116,119,111,32,115,105,100,101,115,32,111, +102,32,63,58,0,1661667,1310721,2,21,26,2,1584987,2,1633925,12,8, +1310721,2,21,26,2,1584975,2,28,1,6,1,10,28,34,63,38, +1,10,28,1661795,16,1584987,1310721,2,21,26,2,10,3,6,10,4, +8,1584875,1,10,3,6,10,4,8,1584877,1,10,3,6,10,4, +8,1584989,1,10,3,6,10,4,8,1310721,2,17,26,2,3,28, +1,6,1,10,28,34,63,38,3,26,1661941,16,1310721,2,19,26, +2,3,28,1,6,1,10,28,34,63,38,3,26,1,6,1, +10,28,34,63,38,1,10,28,1661943,14,1,1661953,16,3,1662181,14, +1310721,2,17,26,2,3,28,1,6,1,10,28,34,63,38,3, +26,1661997,16,3,1662053,14,1310721,2,19,26,2,3,28,1,6,1, +10,28,34,63,38,3,26,1,6,1,10,28,34,63,38,1, +10,28,1662173,16,1662165,14,98,111,116,104,32,115,105,100,101,115, +32,111,102,32,63,58,32,104,97,118,101,32,116,111,32,98, +101,32,110,101,119,32,111,98,106,101,99,116,115,44,32,111, +114,32,110,101,105,116,104,101,114,0,1662061,1586351,12,8,1,1662181, +14,1,1310721,2,23,26,1310721,4,18,1310721,2,13,28,1310721,4,1310721, +2,1835009,40,1662229,16,1966081,1310721,4,1579069,12,3,1659789,12,1310721,2,4, +1582577,2,9,28,1,6,1,10,28,34,63,38,1,10,28,1662285, +16,1310721,2,2,1663727,14,1582579,2,1584947,2,28,1,6,1,10,28, +34,63,38,3,26,1662333,16,1310721,2,2,1660895,12,1663727,14,1582579,2, +1584903,2,28,1,6,1,10,28,34,63,38,3,26,1662925,16,1584875, +2,1,6,1,10,28,34,63,38,3,26,1662459,16,1662451,14,116, +104,105,115,32,99,97,110,39,116,32,98,101,32,97,115,115, +105,103,110,101,100,32,116,111,0,1662399,1586063,12,8,1310721,2,3, +26,1584877,2,10,3,6,10,4,8,1310721,2,5,26,1584879,2,10, +3,6,10,4,8,1310721,2,11,26,1584987,2,10,3,6,10,4, +8,1310721,2,11,26,2,1629979,12,1662683,16,1662675,14,97,32,119,104, +111,108,101,32,115,116,114,117,99,116,32,111,114,32,97,114, +114,97,121,32,99,97,110,39,116,32,98,101,32,97,115,115, +105,103,110,101,100,59,32,97,115,115,105,103,110,32,105,116, +115,32,102,105,101,108,100,115,0,1662553,1585679,12,8,1597071,12,8, +1310721,2,9,26,1662195,12,10,3,6,10,4,8,1647757,12,8,1310721, +2,11,26,2,1584987,2,1630621,12,1,6,1,10,28,34,63,38, +3,26,1662809,16,1662787,14,97,115,115,105,103,110,109,101,110,116, +0,1662765,1310721,2,11,26,2,1584987,2,1633925,12,8,1660799,12,8,1584875, +1,10,3,6,10,4,8,1584877,1,10,3,6,10,4,8,1584987, +1310721,2,11,26,2,10,3,6,10,4,8,1584989,1,10,3,6, +10,4,8,1310721,2,3,26,2,1310721,2,5,26,2,1310721,2,9, +26,2,1618391,12,1663727,14,1310721,2,7,26,1582579,2,1601951,12,10,3, +6,10,4,8,1310721,2,7,26,2,1,40,3,36,1663715,16,1584875, +2,1,6,1,10,28,34,63,38,3,26,1663067,16,1663059,14,116, +104,105,115,32,99,97,110,39,116,32,98,101,32,97,115,115, +105,103,110,101,100,32,116,111,0,1663007,1586063,12,8,1584877,2,3, +28,1,6,1,10,28,34,63,38,3,26,1663129,16,1584853,2,1, +6,1,10,28,34,63,38,1,10,28,1663131,14,1,1663169,16,1584879, +2,1604491,12,1,6,1,10,28,34,63,38,1,10,28,1663171,14, +1,1663291,16,1584879,2,1663283,14,111,119,110,115,32,97,110,32,111, +98,106,101,99,116,59,32,99,111,109,112,111,117,110,100,32, +97,115,115,105,103,110,109,101,110,116,32,119,111,117,108,100, +32,108,111,115,101,32,105,116,0,1663183,1586469,12,8,1310721,2,11, +26,1584987,2,10,3,6,10,4,8,1597071,12,8,1,1589149,12,8, +7,1589235,12,8,3,1589235,12,8,1584875,1,10,3,6,10,4,8, +1310721,2,9,26,1662195,12,10,3,6,10,4,8,1647757,12,8,1310721, +2,9,26,2,1605077,12,8,1547009,1547585,1310721,2,7,26,2,26,2, +26,2,1547137,1547585,1310721,2,7,26,2,26,2,26,2,1600817,12,8, +1310721,2,3,26,1547585,1310721,2,7,26,2,26,2,1310721,2,11,26, +2,1584987,2,1642719,12,10,3,6,10,4,8,1310721,2,11,26,2, +1310721,2,3,26,2,1630621,12,1,6,1,10,28,34,63,38,3, +26,1663631,16,1663603,14,99,111,109,112,111,117,110,100,32,97,115, +115,105,103,110,109,101,110,116,0,1663563,1310721,2,11,26,2,1310721, +2,3,26,2,1633925,12,8,1660799,12,8,1584875,1,10,3,6,10, +4,8,1584877,1,10,3,6,10,4,8,1584987,1310721,2,11,26,2, +10,3,6,10,4,8,1584989,1,10,3,6,10,4,8,1,1663727, +14,1310721,2,2,1663727,14,1,1310721,2,13,26,1310721,4,18,1310721,2, +3,28,1310721,4,1310721,2,1835009,40,1663775,16,1966081,1310721,4,1579069,12,1582577, +2,9,28,1,6,1,10,28,34,63,38,3,26,1663939,16,1582579, +2,1584901,2,28,1,6,1,10,28,34,63,38,3,26,1663847,16, +3,1663899,14,1582579,2,1584891,2,28,1,6,1,10,28,34,63,38, +3,26,1,6,1,10,28,34,63,38,1,10,28,1663909,16,3, +1663935,14,1637143,12,1,6,1,10,28,34,63,38,1,10,28,1664221, +14,1582577,2,5,28,1,6,1,10,28,34,63,38,1,10,28, +1663979,16,3,1664017,14,1582579,2,1603687,12,1,40,3,36,1,6,1, +10,28,34,63,38,1,10,28,1664027,16,1,1664221,14,1637143,12,1664041, +16,3,1664221,14,1310721,2,1582579,2,1587809,12,10,3,6,10,4,8, +1310721,2,2,1,40,3,36,1664145,16,1417729,1310721,2,2,26,2,11, +28,1,6,1,10,28,34,63,38,3,26,1,6,1,10,28, +34,63,38,1,10,28,1664147,14,1,1664213,16,1442305,1310721,2,2,26, +2,33,32,1,6,1,10,28,34,63,38,3,26,1,6,1, +10,28,34,63,38,1,10,28,1664215,14,1,1664221,14,1,1310721,2, +3,26,1310721,4,18,1310721,2,9,28,1310721,4,1310721,2,1835009,40,1664269, +16,1966081,1310721,4,1579069,12,1584845,2,1310721,2,4,1,1310721,2,3,26, +4,1,1310721,2,5,26,4,1584977,2,1310721,2,7,26,4,1597071,12, +8,1584849,1,6,2,1,6,3,26,5,6,4,10,8,8,1584859, +1,6,2,1,6,3,26,5,6,4,10,8,8,1584893,2,1598497, +12,1,6,1,10,28,34,63,38,3,26,1664751,16,1582577,2,1, +28,1,6,1,10,28,34,63,38,3,26,1664475,16,1664467,14,109, +105,115,115,105,110,103,32,125,0,1664447,1586063,12,8,1663741,12,1664493, +16,1666771,12,8,1664379,14,1310721,2,3,26,1662195,12,10,3,6,10, +4,8,1647757,12,8,1584901,2,1598801,12,1664585,16,9,1589235,12,8,1310721, +2,3,26,2,1623627,12,8,1310721,2,3,26,1,10,3,6,10, +4,8,1664379,14,1584893,2,1598497,12,1,6,1,10,28,34,63,38, +3,26,1664701,16,1664693,14,101,120,112,101,99,116,101,100,32,59, +32,111,114,32,125,32,97,102,116,101,114,32,97,110,32,101, +120,112,114,101,115,115,105,111,110,0,1664621,1586063,12,8,1310721,2, +5,26,3,10,3,6,10,4,8,1310721,2,7,26,1584987,2,10, +3,6,10,4,8,1664379,14,1597071,12,8,1310721,2,5,26,2,1664809, +16,1310721,2,3,26,1310721,2,3,26,2,1622911,12,10,3,6,10, +4,8,1664817,14,1,1589149,12,8,1310721,2,2,1606489,12,8,1584849,1, +6,2,1,6,-1,26,5,6,4,10,8,8,1584859,1,6,2, +1,6,-1,26,5,6,4,10,8,8,1584875,1,10,3,6,10, +4,8,1584877,1,10,3,6,10,4,8,1584987,1310721,2,7,26,2, +10,3,6,10,4,8,1584989,1,10,3,6,10,4,8,1310721,2, +3,26,2,1664973,14,1,1310721,2,9,26,1310721,4,18,1310721,2,5, +28,1310721,4,1310721,2,1835009,40,1665021,16,1966081,1310721,4,1579069,12,1582577,2, +3,28,1,6,1,10,28,34,63,38,3,26,1665089,16,1310721,2, +1582579,2,10,3,6,10,4,8,1597071,12,8,1310721,2,2,1665789,14, +1584919,2,1598801,12,1665113,16,1,1664987,12,28,1665789,14,1582577,2,9,28, +1,6,1,10,28,34,63,38,3,26,1665205,16,1582579,2,1547265,5, +26,2,28,1,6,1,10,28,34,63,38,3,26,1,6,1, +10,28,34,63,38,1,10,28,1665207,14,1,1665229,16,1597071,12,8, +1664987,12,-1,36,1665789,14,1584887,2,1598801,12,1665281,16,1310721,2,1665803,12, +10,3,6,10,4,8,1584889,2,1599031,12,8,1310721,2,2,1665789,14, +1582577,2,5,28,1,6,1,10,28,34,63,38,3,26,1665729,16, +1310721,2,3,26,1582579,2,1587809,12,10,3,6,10,4,8,1310721,2, +3,26,2,1,40,3,36,1665697,16,1417729,1310721,2,3,26,2,26, +2,9,28,1,6,1,10,28,34,63,38,3,26,1665413,16,3, +1665475,14,1417729,1310721,2,3,26,2,26,2,7,28,1,6,1,10, +28,34,63,38,3,26,1,6,1,10,28,34,63,38,1,10, +28,1665485,16,3,1665599,14,1417729,1310721,2,3,26,2,26,2,3,28, +1,6,1,10,28,34,63,38,3,26,1665575,16,1442305,1310721,2,3, +26,2,26,2,3,32,1,6,1,10,28,34,63,38,1,10, +28,1665577,14,1,1,6,1,10,28,34,63,38,1,10,28,1665609, +16,3,1665671,14,1417729,1310721,2,3,26,2,26,2,5,28,1,6, +1,10,28,34,63,38,3,26,1,6,1,10,28,34,63,38, +1,10,28,1,6,1,10,28,34,63,38,1,10,28,1665699,14, +1,1665729,16,1597071,12,8,1425921,1310721,2,3,26,2,26,2,1665789,14, +1665773,14,101,120,112,101,99,116,101,100,32,97,32,99,111,110, +115,116,97,110,116,0,1665733,1586063,12,8,1,1665789,14,1,1310721,2, +5,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,1835009,40,1665837, +16,1966081,1310721,4,1579069,12,1664987,12,1310721,2,4,1582577,2,9,28,1, +6,1,10,28,34,63,38,3,26,1666491,16,1310721,2,3,26,1582579, +2,10,3,6,10,4,8,1310721,2,3,26,2,1546753,7,26,2, +28,1,6,1,10,28,34,63,38,3,26,1665985,16,1597071,12,8, +1310721,2,1310721,2,2,1664987,12,26,10,3,6,10,4,8,1666487,14, +1310721,2,3,26,2,1546753,9,26,2,28,1,6,1,10,28,34, +63,38,3,26,1666067,16,1597071,12,8,1310721,2,1310721,2,2,1664987,12, +28,10,3,6,10,4,8,1666487,14,1310721,2,3,26,2,1546753,1, +26,2,28,1,6,1,10,28,34,63,38,3,26,1666149,16,1597071, +12,8,1310721,2,1310721,2,2,1664987,12,30,10,3,6,10,4,8, +1666487,14,1310721,2,3,26,2,1546753,31,26,2,28,1,6,1,10, +28,34,63,38,3,26,1666231,16,1597071,12,8,1310721,2,1310721,2,2, +1664987,12,34,10,3,6,10,4,8,1666487,14,1310721,2,3,26,2, +1546753,27,26,2,28,1,6,1,10,28,34,63,38,3,26,1666313, +16,1597071,12,8,1310721,2,1310721,2,2,1664987,12,32,10,3,6,10, +4,8,1666487,14,1310721,2,3,26,2,1546753,11,26,2,28,1,6, +1,10,28,34,63,38,3,26,1666395,16,1597071,12,8,1310721,2,1310721, +2,2,1664987,12,48,10,3,6,10,4,8,1666487,14,1310721,2,3, +26,2,1546753,13,26,2,28,1,6,1,10,28,34,63,38,3, +26,1666477,16,1597071,12,8,1310721,2,1310721,2,2,1664987,12,38,10,3, +6,10,4,8,1666487,14,1310721,2,2,1666503,14,1665847,14,1310721,2,2, +1666503,14,1,1310721,2,5,26,1310721,4,18,1310721,2,1,28,1310721,4, +1310721,2,1835009,40,1666551,16,1966081,1310721,4,1579069,12,1582577,2,1,28,1, +6,1,10,28,34,63,38,1,10,28,1666639,16,1584901,2,1598497,12, +1,6,1,10,28,34,63,38,3,26,1,6,1,10,28,34, +63,38,1,10,28,1666641,14,1,1666699,16,1584893,2,1598497,12,1,6, +1,10,28,34,63,38,3,26,1,6,1,10,28,34,63,38, +1,10,28,1666701,14,1,1666715,16,1597071,12,8,1666551,14,1584901,2,1598497, +12,1666733,16,1597071,12,8,1585091,1,10,3,6,10,4,8,1,1666757, +14,1,1310721,2,1,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721, +2,1835009,40,1666805,16,1966081,1310721,4,1579069,12,1585091,2,1666821,16,1666517,12, +1667293,14,1582577,2,5,28,1,6,1,10,28,34,63,38,3,26, +1666891,16,1582579,2,1603687,12,1,40,1,6,1,10,28,34,63,38, +1,10,28,1666893,14,1,1667075,16,1310721,2,1582579,2,1587809,12,10,3, +6,10,4,8,1310721,2,2,1,40,3,36,1667001,16,1417729,1310721,2, +2,26,2,11,28,1,6,1,10,28,34,63,38,3,26,1, +6,1,10,28,34,63,38,1,10,28,1667003,14,1,1667049,16,1442305, +1310721,2,2,26,2,17,32,1,6,1,10,28,34,63,38,1, +10,28,1667051,14,1,1667075,16,1310721,2,2,1,1647877,12,8,1,1667293, +14,1637143,12,1667097,16,3,1667723,12,8,1,1667293,14,1582577,2,9,28, +1,6,1,10,28,34,63,38,3,26,1667221,16,1582579,2,1584901,2, +28,1,6,1,10,28,34,63,38,3,26,1667175,16,1597071,12,8, +1,1667293,14,1582579,2,1584891,2,28,1,6,1,10,28,34,63,38, +3,26,1667221,16,1667307,12,8,1,1667293,14,1310721,2,3,26,1662195,12, +10,3,6,10,4,8,1647757,12,8,9,1589235,12,8,1584901,2,1599031, +12,8,1310721,2,3,26,2,1623627,12,8,1,1667293,14,1,1310721,2, +5,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1667341, +16,1966081,1310721,4,1579069,12,1584845,2,1310721,2,4,1584891,2,1599031,12,8, +1584849,1,6,2,1,6,3,26,5,6,4,10,8,8,1584893,2, +1598497,12,1,6,1,10,28,34,63,38,3,26,1667495,16,1582577,2, +1,28,1,6,1,10,28,34,63,38,3,26,1667485,16,1667477,14, +109,105,115,115,105,110,103,32,125,0,1667457,1586063,12,8,1666771,12, +8,1667389,14,1310721,2,2,1606489,12,8,1597071,12,8,1584849,1,6,2, +1,6,-1,26,5,6,4,10,8,8,1,1667549,14,1,1310721,2, +3,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1667597, +16,1966081,1310721,4,1579069,12,1584845,2,1310721,2,4,1584891,2,1598497,12,1667627, +16,1667307,12,1667709,14,1584849,1,6,2,1,6,3,26,5,6,4, +10,8,8,1666771,12,8,1310721,2,2,1606489,12,8,1584849,1,6,2, +1,6,-1,26,5,6,4,10,8,8,1,1667709,14,1,1310721,2, +3,26,1310721,4,18,1310721,2,15,28,1310721,4,1310721,2,1835009,40,1667757, +16,1966081,1310721,4,1579069,12,1310721,2,4,1,1310721,2,11,26,4,1, +1310721,2,13,26,4,1310721,2,2,1667953,16,1637945,12,8,1310721,2,11, +26,1585011,2,10,3,6,10,4,8,1310721,2,13,26,1585013,2,10, +3,6,10,4,8,1310721,2,11,26,2,1584983,2,28,1,6,1, +10,28,34,63,38,3,26,1667953,16,1667945,14,97,32,118,97,114, +105,97,98,108,101,32,99,97,110,39,116,32,98,101,32,118, +111,105,100,0,1667895,1585679,12,8,3,1668961,16,1310721,2,3,26,1599321, +12,10,3,6,10,4,8,1310721,2,5,26,1310721,2,3,26,2, +1603891,12,10,3,6,10,4,8,1558017,1310721,2,5,26,2,26,1310721, +2,13,26,2,10,3,6,10,4,8,1584895,2,1598801,12,1668279,16, +1310721,2,9,26,1665803,12,10,3,6,10,4,8,1584897,2,1599031,12, +8,1310721,2,11,26,2,1,6,1,10,28,34,63,38,3,26, +1668157,16,1310721,2,11,26,1584975,2,10,3,6,10,4,8,1473025,1310721, +2,5,26,2,26,3,10,3,6,10,4,8,1556993,1310721,2,5, +26,2,26,1310721,2,11,26,2,1310721,2,9,26,2,1624809,12,10, +3,6,10,4,8,1584851,1584851,2,1556993,1310721,2,5,26,2,26,2, +1628577,12,26,10,3,6,10,4,8,1668519,14,1310721,2,11,26,2, +1668333,16,1310721,2,11,26,2,1629979,12,1,6,1,10,28,34,63, +38,1,10,28,1668335,14,1,1668441,16,1473025,1310721,2,5,26,2,26, +3,10,3,6,10,4,8,1556993,1310721,2,5,26,2,26,1310721,2, +11,26,2,10,3,6,10,4,8,1584851,1584851,2,1310721,2,11,26, +2,1628577,12,26,10,3,6,10,4,8,1668519,14,1584851,1,6,2, +1,6,3,26,5,6,4,10,8,8,1310721,2,11,26,2,1668519, +16,1556993,1310721,2,5,26,2,26,1310721,2,11,26,2,10,3,6, +10,4,8,1584903,2,1598801,12,1668921,16,1473025,1310721,2,5,26,2,26, +2,1668661,16,1668653,14,97,110,32,97,114,114,97,121,32,111,114, +32,97,32,115,116,114,117,99,116,32,99,97,110,39,116,32, +98,101,32,105,110,105,116,105,97,108,105,122,101,100,32,119, +105,116,104,32,61,0,1668555,1585679,12,8,1310721,2,7,26,1662195,12, +10,3,6,10,4,8,1647757,12,8,1467905,1310721,2,5,26,2,26, +2,1590069,12,8,5,1589235,12,8,1310721,2,11,26,2,1668831,16,1310721, +2,11,26,2,1584987,2,1630621,12,1,6,1,10,28,34,63,38, +3,26,1668827,16,1668805,14,105,110,105,116,105,97,108,105,122,101, +114,0,1668781,1310721,2,11,26,2,1584987,2,1633925,12,8,1668893,14,1584987, +2,1629747,12,1,6,1,10,28,34,63,38,3,26,1668893,16,1556993, +1310721,2,5,26,2,26,1584987,2,10,3,6,10,4,8,3,1310721, +2,5,26,2,1310721,2,7,26,2,1618391,12,8,1584899,2,1598801,12, +1,6,1,10,28,34,63,38,3,26,1668957,16,1668961,14,1667953,14, +1584901,2,1599031,12,8,1,1668979,14,1,1310721,2,15,26,1310721,4,18, +1310721,2,1,28,1310721,4,1310721,2,1835009,40,1669027,16,1966081,1310721,4,1579069, +12,1648169,12,8,1597071,12,8,1,1667723,12,1669051,14,1,1310721,2,1, +26,1310721,4,18,1310721,2,21,28,1310721,4,1310721,2,1835009,40,1669099,16, +1966081,1310721,4,1579069,12,1584845,2,1310721,2,15,26,4,1648169,12,8,1597071, +12,8,1584887,2,1599031,12,8,1310721,2,5,26,1662195,12,10,3,6, +10,4,8,1647757,12,8,1310721,2,5,26,2,1605077,12,8,1584889,2, +1599031,12,8,1310721,2,1589535,12,10,3,6,10,4,8,17,1589235,12, +8,1310721,2,11,26,1584857,2,10,3,6,10,4,8,1310721,2,7, +26,1606761,12,10,3,6,10,4,8,1667563,12,8,1310721,2,13,26, +1584857,2,10,3,6,10,4,8,1310721,2,9,26,1606761,12,10,3, +6,10,4,8,1584933,2,1598649,12,1669587,16,1597071,12,8,1310721,2,3, +26,1589535,12,10,3,6,10,4,8,15,1589235,12,8,1310721,2,2, +1584837,2,1589317,12,8,1310721,2,7,26,2,1607393,12,8,1584857,1310721,2, +11,26,2,10,3,6,10,4,8,1667563,12,8,1310721,2,9,26, +2,1310721,2,13,26,2,1310721,2,3,26,2,1584857,2,1310721,2,15, +26,2,1669575,14,101,110,100,115,32,116,104,101,32,98,114,97, +110,99,104,101,115,32,111,102,32,97,110,32,105,102,32,105, +110,32,100,105,102,102,101,114,101,110,116,32,115,116,97,116, +101,115,0,1669481,1615115,12,8,1670119,14,1609759,1,10,3,6,10,4, +8,1310721,2,13,26,2,1669637,16,1507841,1310721,2,9,26,2,26,1610727, +12,8,1310721,2,11,26,2,1669671,16,1507841,1310721,2,7,26,2,26, +1610727,12,8,1310721,2,15,26,2,1669779,14,101,110,100,115,32,116, +104,101,32,98,114,97,110,99,104,101,115,32,111,102,32,97, +110,32,105,102,32,105,110,32,100,105,102,102,101,114,101,110, +116,32,115,116,97,116,101,115,0,1669685,1611455,12,8,1310721,2,13, +26,2,1669833,16,1507841,1310721,2,9,26,2,26,1310721,2,15,26,2, +3,1613039,12,8,1310721,2,17,26,1,10,3,6,10,4,8,1310721, +2,11,26,2,1669919,16,1310721,2,17,26,1507841,1310721,2,7,26,2, +26,1310721,2,15,26,2,1,1613039,12,10,3,6,10,4,8,1310721, +2,17,26,2,1670037,16,1310721,2,19,26,1589535,12,10,3,6,10, +4,8,15,1589235,12,8,1310721,2,2,1584837,2,1589317,12,8,1507841,1310721, +2,7,26,2,26,1310721,2,15,26,2,3,1613039,12,8,1310721,2, +19,26,2,1584837,2,1589317,12,8,1670053,14,1310721,2,2,1584837,2,1589317, +12,8,1584857,1310721,2,13,26,2,1670075,16,3,1670107,14,1310721,2,11, +26,2,1,6,1,10,28,34,63,38,1,10,28,10,3,6, +10,4,8,1310721,2,7,26,2,1607311,12,8,1,1670143,14,1,1310721, +2,21,26,1310721,4,18,1310721,2,7,28,1310721,4,1310721,2,1835009,40, +1670191,16,1966081,1310721,4,1579069,12,1310721,2,3,26,4,1310721,2,4,1552897, +1584867,2,17,30,26,1310721,2,5,26,4,1584867,2,121,40,3,36, +1670305,16,1670297,14,108,111,111,112,115,32,110,101,115,116,101,100, +32,116,111,111,32,100,101,101,112,108,121,0,1670249,1585585,12,8, +1310721,2,5,26,2,1,26,1310721,2,2,10,3,6,10,4,8, +1310721,2,5,26,2,3,26,1,10,3,6,10,4,8,1310721,2, +5,26,2,5,26,1584849,2,10,3,6,10,4,8,1310721,2,5, +26,2,7,26,1310721,2,3,26,2,10,3,6,10,4,8,1310721, +2,5,26,2,9,26,1584859,2,10,3,6,10,4,8,1310721,2, +5,26,2,11,26,1584845,2,10,3,6,10,4,8,1310721,2,5, +26,2,13,26,1609629,2,10,3,6,10,4,8,1584867,1,6,2, +1,6,3,26,5,6,4,10,8,8,1,1670557,14,1,1310721,2, +7,26,1310721,4,18,1310721,2,15,28,1310721,4,1310721,2,1835009,40,1670605, +16,1966081,1310721,4,1579069,12,1310721,2,3,26,4,1310721,2,4,1,1310721, +2,13,26,4,1584867,1,6,2,1,6,-1,26,5,6,4,10, +8,8,1310721,2,5,26,1552897,1584867,2,17,30,26,10,3,6,10, +4,8,1310721,2,11,26,1310721,2,5,26,2,11,26,2,10,3, +6,10,4,8,1609759,1,10,3,6,10,4,8,1310721,2,3,26, +2,1,40,3,36,1670787,16,1507841,1310721,2,3,26,2,26,1610727,12, +8,1310721,2,7,26,1567233,1310721,2,5,26,2,13,26,2,26,10, +3,6,10,4,8,1310721,2,9,26,1567233,1609629,2,26,10,3,6, +10,4,8,1310721,2,7,26,2,1310721,2,9,26,2,40,1670979,16, +1310721,2,7,26,2,3,26,1610727,12,8,1310721,2,7,26,1310721,2, +7,26,2,7,26,7,1310721,2,7,26,2,5,26,2,1310721,2, +7,26,2,3,26,2,28,30,26,10,3,6,10,4,8,1670855, +14,1310721,2,11,26,2,1671103,14,108,101,97,118,101,115,32,116, +104,101,32,108,111,111,112,32,105,110,32,100,105,102,102,101, +114,101,110,116,32,115,116,97,116,101,115,32,111,110,32,100, +105,102,102,101,114,101,110,116,32,112,97,116,104,115,0,1670993, +1611455,12,8,1584857,1609759,2,1,10,40,10,3,6,10,4,8,1310721, +2,7,26,1567233,1310721,2,5,26,2,13,26,2,26,10,3,6, +10,4,8,1310721,2,7,26,2,1310721,2,9,26,2,40,1671471,16, +1310721,2,7,26,2,3,26,1310721,2,11,26,2,1,1613039,12,1671343, +16,1310721,2,7,26,2,1,26,2,1584837,2,1589317,12,8,1310721,2, +7,26,2,3,26,1310721,2,11,26,2,3,1613039,12,8,1310721,2, +13,26,1589535,12,1310721,2,13,26,2,1588385,12,10,3,6,10,4, +8,15,1589235,12,8,1671393,14,1310721,2,13,26,1310721,2,7,26,2, +1,26,2,1310721,2,13,26,2,1588385,12,10,3,6,10,4,8, +1310721,2,7,26,1310721,2,7,26,2,7,26,7,1310721,2,7,26, +2,5,26,2,1310721,2,7,26,2,3,26,2,28,30,26,10, +3,6,10,4,8,1671175,14,1310721,2,2,1,40,3,36,1671505,16, +1310721,2,2,1584837,2,1589317,12,8,1310721,2,3,26,2,1,40,3, +36,1671559,16,1507841,1310721,2,3,26,2,26,1310721,2,11,26,2,3, +1613039,12,8,1310721,2,13,26,2,1584837,2,1588737,12,8,1609629,1310721,2, +5,26,2,13,26,2,10,3,6,10,4,8,1,1671617,14,1, +1310721,2,15,26,1310721,4,18,1310721,2,13,28,1310721,4,1310721,2,1835009, +40,1671665,16,1966081,1310721,4,1579069,12,1584837,2,1310721,2,4,3,1,10, +28,1310721,2,7,26,4,1,1310721,2,11,26,4,1648169,12,8,1597071, +12,8,1310721,2,5,26,1606761,12,10,3,6,10,4,8,1584887,2, +1599031,12,8,1582577,2,3,28,1,6,1,10,28,34,63,38,3, +26,1671813,16,1582579,2,1,6,1,10,28,34,63,38,1,10,28, +1671815,14,1,1671941,16,1597931,12,8,1584799,2,9,28,1,6,1,10, +28,34,63,38,3,26,1671913,16,1584801,2,1584889,2,28,1,6,1, +10,28,34,63,38,3,26,1,6,1,10,28,34,63,38,1, +10,28,1671915,14,1,1671941,16,1310721,2,11,26,3,10,3,6,10, +4,8,1310721,2,9,26,1662195,12,10,3,6,10,4,8,1647757,12, +8,1310721,2,9,26,2,1605077,12,8,1584889,2,1599031,12,8,1310721,2, +3,26,1589535,12,10,3,6,10,4,8,17,1589235,12,8,1310721,2, +11,26,2,1,6,1,10,28,34,63,38,3,26,1672093,16,1584857, +2,1,6,1,10,28,34,63,38,1,10,28,1672095,14,1,1672123, +16,1310721,2,7,26,1606761,12,10,3,6,10,4,8,1310721,2,2, +1310721,2,5,26,2,1670157,12,8,1667563,12,8,1310721,2,5,26,2, +1613533,12,8,1310721,2,2,1589453,12,8,1310721,2,3,26,2,1310721,2, +7,26,2,1670571,12,8,1310721,2,5,26,2,1607311,12,8,1,1672229, +14,1,1310721,2,13,26,1310721,4,18,1310721,2,19,28,1310721,4,1310721, +2,1835009,40,1672277,16,1966081,1310721,4,1579069,12,1584845,2,1310721,2,4,3, +1,10,28,1310721,2,5,26,4,1,1310721,2,17,26,4,1648169,12, +8,1597071,12,8,1584887,2,1599031,12,8,1584849,1,6,2,1,6,3, +26,5,6,4,10,8,8,1584929,2,1598649,12,1672389,16,1668993,12,8, +1672505,14,1637143,12,1672409,16,3,1667723,12,8,1672505,14,1584901,2,1598497,12, +1,6,1,10,28,34,63,38,3,26,1672495,16,1310721,2,15,26, +1662195,12,10,3,6,10,4,8,1647757,12,8,9,1589235,12,8,1310721, +2,15,26,2,1623627,12,8,1584901,2,1599031,12,8,1310721,2,3,26, +1606761,12,10,3,6,10,4,8,1310721,2,7,26,1584837,2,10,3, +6,10,4,8,1584901,2,1598497,12,1,6,1,10,28,34,63,38, +3,26,1672825,16,1582577,2,3,28,1,6,1,10,28,34,63,38, +3,26,1672647,16,1582579,2,1,6,1,10,28,34,63,38,1,10, +28,1672649,14,1,1672775,16,1597931,12,8,1584799,2,9,28,1,6,1, +10,28,34,63,38,3,26,1672747,16,1584801,2,1584901,2,28,1,6, +1,10,28,34,63,38,3,26,1,6,1,10,28,34,63,38, +1,10,28,1672749,14,1,1672775,16,1310721,2,17,26,3,10,3,6, +10,4,8,1310721,2,15,26,1662195,12,10,3,6,10,4,8,1647757, +12,8,1310721,2,15,26,2,1605077,12,8,1672855,14,3,1589149,12,8, +1310721,2,17,26,3,10,3,6,10,4,8,1584901,2,1599031,12,8, +1310721,2,9,26,1589535,12,10,3,6,10,4,8,17,1589235,12,8, +1310721,2,17,26,2,1,6,1,10,28,34,63,38,3,26,1672961, +16,1584857,2,1,6,1,10,28,34,63,38,1,10,28,1672963,14, +1,1672991,16,1310721,2,5,26,1606761,12,10,3,6,10,4,8,1310721, +2,11,26,1589535,12,10,3,6,10,4,8,15,1589235,12,8,1310721, +2,13,26,1584837,2,10,3,6,10,4,8,1584889,2,1598497,12,1, +6,1,10,28,34,63,38,3,26,1673133,16,1310721,2,15,26,1662195, +12,10,3,6,10,4,8,1647757,12,8,9,1589235,12,8,1310721,2, +15,26,2,1623627,12,8,1584889,2,1599031,12,8,1310721,2,7,26,2, +1589453,12,8,1310721,2,11,26,2,1584837,2,1589317,12,8,1310721,2,13, +26,2,1310721,2,3,26,2,1670157,12,8,1667563,12,8,1310721,2,3, +26,2,1613533,12,8,1310721,2,13,26,2,1589453,12,8,1310721,2,9, +26,2,1310721,2,5,26,2,1670571,12,8,1310721,2,3,26,2,1607311, +12,8,1310721,2,2,1606489,12,8,1584849,1,6,2,1,6,-1,26, +5,6,4,10,8,8,1,1673333,14,1,1310721,2,19,26,1310721,4, +18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1673381,16,1966081,1310721,4, +1579069,12,1648169,12,8,1597071,12,8,1584901,2,1599031,12,8,1584867,2,1584869, +2,10,40,3,36,1673507,16,1673493,14,98,114,101,97,107,32,111, +117,116,115,105,100,101,32,97,32,108,111,111,112,32,111,114, +32,97,32,115,119,105,116,99,104,0,1673427,1585679,12,8,1,1673759, +14,1310721,2,1552897,1584867,2,3,28,17,30,26,10,3,6,10,4, +8,1310721,2,2,9,26,2,1584859,2,28,1,6,1,10,28,34, +63,38,1,10,28,1673675,16,1673661,14,98,114,101,97,107,32,99, +97,110,39,116,32,108,101,97,118,101,32,97,32,98,108,111, +99,107,32,101,120,112,114,101,115,115,105,111,110,0,1673587,1585679, +12,8,1,1673759,14,1310721,2,2,5,26,2,3,26,1,1605463,12, +8,1584857,2,1673721,16,1589535,12,1610135,12,8,1673727,14,1589535,12,8,15, +1589235,12,8,1584857,1,10,3,6,10,4,8,1,1673759,14,1,1310721, +2,3,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,1835009,40, +1673807,16,1966081,1310721,4,1579069,12,1584867,2,3,28,1310721,2,4,1648169,12, +8,1597071,12,8,1584901,2,1599031,12,8,1310721,2,2,1584869,2,40,3, +36,1673935,16,1552897,1310721,2,2,17,30,26,2,3,1,10,28,28, +1,6,1,10,28,34,63,38,3,26,1,6,1,10,28,34, +63,38,1,10,28,1673937,14,1,1673975,16,1310721,2,1,6,2,1, +6,-1,26,5,6,4,10,8,8,1673843,14,1310721,2,2,1584869,2, +40,1674057,16,1674043,14,99,111,110,116,105,110,117,101,32,111,117, +116,115,105,100,101,32,97,32,108,111,111,112,0,1673995,1585679,12, +8,1,1674333,14,1310721,2,3,26,1552897,1310721,2,2,17,30,26,10, +3,6,10,4,8,1310721,2,3,26,2,9,26,2,1584859,2,28, +1,6,1,10,28,34,63,38,1,10,28,1674237,16,1674223,14,99, +111,110,116,105,110,117,101,32,99,97,110,39,116,32,108,101, +97,118,101,32,97,32,98,108,111,99,107,32,101,120,112,114, +101,115,115,105,111,110,0,1674143,1585679,12,8,1,1674333,14,1310721,2, +3,26,2,7,26,2,1613533,12,8,1310721,2,3,26,2,5,26, +2,3,26,1,1605463,12,8,1310721,2,3,26,2,1,26,2,1589453, +12,8,1584857,1,10,3,6,10,4,8,1,1674333,14,1,1310721,2, +5,26,1310721,4,18,1310721,2,7,28,1310721,4,1310721,2,1835009,40,1674381, +16,1966081,1310721,4,1579069,12,1310721,2,3,26,4,1310721,2,4,1584845,2, +1310721,2,5,26,4,1310721,2,2,1607393,12,8,1584857,1310721,2,3,26, +2,10,3,6,10,4,8,1584849,1,6,2,1,6,3,26,5, +6,4,10,8,8,1584893,2,1598497,12,1,6,1,10,28,34,63, +38,3,26,1674561,16,1584949,2,1598649,12,1,6,1,10,28,34,63, +38,3,26,1,6,1,10,28,34,63,38,1,10,28,1674563,14, +1,1674621,16,1584951,2,1598649,12,1,6,1,10,28,34,63,38,3, +26,1,6,1,10,28,34,63,38,1,10,28,1674623,14,1,1674705, +16,1582577,2,1,28,1,6,1,10,28,34,63,38,3,26,1674695, +16,1674683,14,109,105,115,115,105,110,103,32,125,0,1674663,1586063,12, +8,1674705,14,1666771,12,8,1674475,14,1310721,2,5,26,2,1606489,12,8, +1584849,1,6,2,1,6,-1,26,5,6,4,10,8,8,1584857,2, +1674775,16,1589535,12,1610135,12,8,15,1589235,12,8,1584857,1,10,3,6, +10,4,8,1,1674799,14,1,1310721,2,7,26,1310721,4,18,1310721,2, +23,28,1310721,4,1310721,2,1835009,40,1674847,16,1966081,1310721,4,1579069,12,1584845, +2,1310721,2,4,3,1,10,28,1310721,2,11,26,4,3,1,10, +28,1310721,2,21,26,4,1648169,12,8,1597071,12,8,1584887,2,1599031,12, +8,1310721,2,5,26,1662195,12,10,3,6,10,4,8,1647757,12,8, +1310721,2,5,26,2,1605077,12,8,1584889,2,1599031,12,8,1584849,1,6, +2,1,6,3,26,5,6,4,10,8,8,1310721,2,3,26,1, +1603891,12,10,3,6,10,4,8,1584851,1,6,2,1,6,3,26, +5,6,4,10,8,8,1467905,1310721,2,3,26,2,26,2,1590069,12, +8,5,1589235,12,8,1310721,2,9,26,1584857,2,10,3,6,10,4, +8,1310721,2,7,26,1606761,12,10,3,6,10,4,8,3,1,10, +28,1310721,2,7,26,2,1670157,12,8,1584891,2,1599031,12,8,1584893,2, +1598497,12,1,6,1,10,28,34,63,38,3,26,1675871,16,1582577,2, +1,28,1,6,1,10,28,34,63,38,3,26,1675265,16,1675253,14, +109,105,115,115,105,110,103,32,125,0,1675233,1586063,12,8,1675871,14, +1584949,2,1598649,12,1675587,16,1597071,12,8,1310721,2,17,26,1,10,3, +6,10,4,8,3,1675475,16,1310721,2,19,26,1665803,12,10,3,6, +10,4,8,1467905,1310721,2,3,26,2,26,2,1590069,12,8,3,1589235, +12,8,1310721,2,19,26,2,1589149,12,8,29,1589235,12,8,1310721,2, +17,26,1589535,12,1310721,2,17,26,2,1588385,12,10,3,6,10,4, +8,17,1589235,12,8,1584899,2,1598801,12,1,6,1,10,28,34,63, +38,3,26,1675471,16,1675475,14,1675305,14,1584915,2,1599031,12,8,1310721,2, +15,26,1589535,12,10,3,6,10,4,8,15,1589235,12,8,1310721,2, +17,26,2,1584837,2,1588737,12,8,1310721,2,7,26,2,1310721,2,9, +26,2,1674347,12,8,1310721,2,15,26,2,1584837,2,1589317,12,8,1675867, +14,1584951,2,1598649,12,1675805,16,1597071,12,8,1584915,2,1599031,12,8,1310721, +2,11,26,2,1,40,3,36,1675699,16,1675691,14,97,32,115,119, +105,116,99,104,32,104,97,115,32,111,110,101,32,100,101,102, +97,117,108,116,0,1675641,1585679,12,8,1310721,2,13,26,1589535,12,10, +3,6,10,4,8,15,1589235,12,8,1310721,2,11,26,1584837,2,10, +3,6,10,4,8,1310721,2,7,26,2,1310721,2,9,26,2,1674347, +12,8,1310721,2,13,26,2,1584837,2,1589317,12,8,1675867,14,1675859,14, +101,120,112,101,99,116,101,100,32,99,97,115,101,32,111,114, +32,100,101,102,97,117,108,116,0,1675809,1586063,12,8,1675165,14,1597071, +12,8,1310721,2,7,26,2,1607393,12,8,1584857,1310721,2,9,26,2, +10,3,6,10,4,8,1310721,2,11,26,2,1,40,3,36,1675981, +16,1310721,2,11,26,2,1589453,12,8,3,1,10,28,3,1,10, +28,1670571,12,8,1676039,14,1310721,2,21,26,1589535,12,10,3,6,10, +4,8,15,1589235,12,8,1310721,2,21,26,2,1310721,2,7,26,2, +1670571,12,8,1310721,2,7,26,2,1607311,12,8,1310721,2,2,1606489,12, +8,1584849,1,6,2,1,6,-1,26,5,6,4,10,8,8,1, +1676103,14,1,1310721,2,23,26,1310721,4,18,1310721,2,3,28,1310721,4, +1310721,2,1835009,40,1676151,16,1966081,1310721,4,1579069,12,1648169,12,8,1597071,12, +8,1584865,2,1,6,1,10,28,34,63,38,3,26,1676255,16,1676247, +14,114,101,116,117,114,110,32,111,117,116,115,105,100,101,32, +97,32,102,117,110,99,116,105,111,110,0,1676195,1585679,12,8,1584859, +2,1676351,16,1676343,14,114,101,116,117,114,110,32,99,97,110,39, +116,32,108,101,97,118,101,32,97,32,98,108,111,99,107,32, +101,120,112,114,101,115,115,105,111,110,0,1676267,1585679,12,8,1584901, +2,1598497,12,1676393,16,1,1589149,12,8,1310721,2,1,10,3,6,10, +4,8,1676621,14,1310721,2,1662195,12,10,3,6,10,4,8,1647757,12, +8,1584885,2,1584983,2,28,1,6,1,10,28,34,63,38,3,26, +1676503,16,1676483,14,114,101,116,117,114,110,32,118,97,108,117,101, +0,1676457,1584983,2,1584987,2,1633925,12,8,1676621,14,1584885,2,1676569,16,1584885, +2,1584987,2,1630621,12,1,6,1,10,28,34,63,38,3,26,1, +6,1,10,28,34,63,38,1,10,28,1676571,14,1,1676621,16,1676605, +14,114,101,116,117,114,110,32,118,97,108,117,101,0,1676579,1584885, +2,1584987,2,1633925,12,8,1584901,2,1599031,12,8,1310721,2,2,1621929,12, +8,3,1,1605463,12,8,1584861,1584837,2,1584861,2,1588385,12,10,3,6, +10,4,8,2097153,3,28,1589149,12,8,15,1589235,12,8,1584857,1,10, +3,6,10,4,8,1,1676723,14,1,1310721,2,3,26,1310721,4,18, +1310721,2,3,28,1310721,4,1310721,2,1835009,40,1676771,16,1966081,1310721,4,1579069, +12,1584853,2,1310721,2,4,1648169,12,8,1597071,12,8,1584853,1,10,3, +6,10,4,8,1667563,12,8,1584853,1310721,2,2,10,3,6,10,4, +8,1,1676843,14,1,1310721,2,3,26,1310721,4,18,1310721,2,21,28, +1310721,4,1310721,2,1835009,40,1676891,16,1966081,1310721,4,1579069,12,1,1310721,2, +4,1,1310721,2,3,26,4,1554945,1310721,2,13,26,4,1554945,129,26, +1310721,2,15,26,4,1554945,257,26,1310721,2,17,26,4,1554945,769,26, +1310721,2,19,26,4,1584891,2,1599031,12,8,1584893,2,1598497,12,1,6, +1,10,28,34,63,38,3,26,1678951,16,1582577,2,1,28,1,6, +1,10,28,34,63,38,3,26,1677103,16,1677095,14,117,110,116,101, +114,109,105,110,97,116,101,100,32,97,115,109,32,98,108,111, +99,107,0,1677049,1586063,12,8,1582577,2,3,28,1,6,1,10,28, +34,63,38,3,26,1677155,16,1582579,2,1589149,12,8,1597071,12,8,1676981, +14,1582577,2,7,28,1,6,1,10,28,34,63,38,3,26,1677207, +16,1582579,2,1590177,12,8,1597071,12,8,1676981,14,1584917,2,1598497,12,1677325, +16,1597071,12,8,1582577,2,3,28,1,6,1,10,28,34,63,38, +1,10,28,1677305,16,1677297,14,35,32,110,101,101,100,115,32,97, +32,110,117,109,98,101,114,0,1677263,1586063,12,8,1582579,2,1588955,12, +8,1597071,12,8,1676981,14,1584919,2,1598497,12,1677447,16,1597071,12,8,1582577, +2,3,28,1,6,1,10,28,34,63,38,3,26,1677405,16,1582581, +2,1,6,1,10,28,34,63,38,1,10,28,1677407,14,1,1677435, +16,1,1582579,2,28,1589149,12,8,1597071,12,8,1677443,14,29,1589235,12, +8,1676981,14,1584915,2,1598497,12,1677637,16,1597071,12,8,1310721,2,2,129, +40,3,36,1677535,16,1677527,14,116,111,111,32,109,97,110,121,32, +97,115,109,32,108,97,98,101,108,115,0,1677487,1585585,12,8,1310721, +2,13,26,2,1310721,2,2,26,1599321,12,10,3,6,10,4,8, +1310721,2,15,26,2,1310721,2,2,26,1584837,2,10,3,6,10,4, +8,1310721,2,1,6,2,1,6,3,26,5,6,4,10,8,8, +1676981,14,1582579,2,1,26,2,1677707,16,1582579,2,3,26,2,1,6, +1,10,28,34,63,38,3,26,1,6,1,10,28,34,63,38, +1,10,28,1677709,14,1,1677779,16,1310721,2,9,26,1582579,2,1,26, +2,1600613,12,10,3,6,10,4,1,40,3,36,1,6,1,10, +28,34,63,38,1,10,28,1677781,14,1,1677811,16,1310721,2,9,26, +2,1589235,12,8,1597071,12,8,1676981,14,1582577,2,5,28,1,6,1, +10,28,34,63,38,1,10,28,1677891,16,1677883,14,98,97,100,32, +116,111,107,101,110,32,105,110,32,97,115,109,0,1677849,1586063,12, +8,1310721,2,5,26,1582579,2,1603687,12,10,3,6,10,4,8,1310721, +2,5,26,2,1,40,3,36,1678001,16,1472001,1310721,2,5,26,2, +26,3,10,3,6,10,4,8,1467905,1310721,2,5,26,2,26,2, +1590069,12,8,1597071,12,8,1676981,14,1310721,2,7,26,3,1,10,28, +10,3,6,10,4,8,1310721,2,5,26,1,10,3,6,10,4, +8,1310721,2,5,26,2,1310721,2,2,40,1678195,16,1310721,2,13,26, +2,1310721,2,5,26,2,26,2,1582579,2,28,1,6,1,10,28, +34,63,38,3,26,1678157,16,1310721,2,7,26,1310721,2,5,26,2, +10,3,6,10,4,8,1310721,2,5,26,1,6,2,1,6,3, +26,5,6,4,10,8,8,1678051,14,1310721,2,7,26,2,1,40, +3,36,1678257,16,1310721,2,15,26,2,1310721,2,7,26,2,26,2, +1589149,12,8,1597071,12,8,1676981,14,1310721,2,11,26,1582579,2,1587809,12, +10,3,6,10,4,8,1310721,2,11,26,2,1,40,3,36,1678373, +16,1417729,1310721,2,11,26,2,26,2,13,28,1,6,1,10,28, +34,63,38,3,26,1,6,1,10,28,34,63,38,1,10,28, +1678375,14,1,1678415,16,45,1425921,1310721,2,11,26,2,26,2,26,1589235, +12,8,1597071,12,8,1676981,14,1310721,2,11,26,2,1,40,3,36, +1678505,16,1417729,1310721,2,11,26,2,26,2,3,28,1,6,1,10, +28,34,63,38,1,10,28,1,6,1,10,28,34,63,38,1, +10,28,1678507,14,1,1678579,16,1417729,1310721,2,11,26,2,26,2,11, +28,1,6,1,10,28,34,63,38,1,10,28,1,6,1,10, +28,34,63,38,1,10,28,1678581,14,1,1678617,16,1425921,1310721,2,11, +26,2,26,2,1589149,12,8,1597071,12,8,1676981,14,1310721,2,11,26, +2,1,40,3,36,1678705,16,1417729,1310721,2,11,26,2,26,2,3, +28,1,6,1,10,28,34,63,38,3,26,1,6,1,10,28, +34,63,38,1,10,28,1678707,14,1,1678737,16,1310721,2,11,26,2, +1589741,12,8,1597071,12,8,1676981,14,1310721,2,3,26,2,501,40,3, +36,1678831,16,1678823,14,116,111,111,32,109,97,110,121,32,97,115, +109,32,108,97,98,101,108,32,114,101,102,101,114,101,110,99, +101,115,0,1678763,1585585,12,8,1310721,2,17,26,2,1310721,2,3,26, +2,26,1582579,2,10,3,6,10,4,8,1310721,2,19,26,2,1310721, +2,3,26,2,26,1589535,12,10,3,6,10,4,8,1310721,2,3, +26,1,6,2,1,6,3,26,5,6,4,10,8,8,1597071,12, +8,1676981,14,1597071,12,8,1310721,2,5,26,1,10,3,6,10,4, +8,1310721,2,5,26,2,1310721,2,3,26,2,40,1679405,16,1310721,2, +7,26,3,1,10,28,10,3,6,10,4,8,1310721,2,9,26, +1,10,3,6,10,4,8,1310721,2,9,26,2,1310721,2,2,40, +1679219,16,1310721,2,13,26,2,1310721,2,9,26,2,26,2,1310721,2, +17,26,2,1310721,2,5,26,2,26,2,28,1,6,1,10,28, +34,63,38,3,26,1679181,16,1310721,2,7,26,1310721,2,9,26,2, +10,3,6,10,4,8,1310721,2,9,26,1,6,2,1,6,3, +26,5,6,4,10,8,8,1679055,14,1310721,2,7,26,2,1,40, +1679313,16,1310721,2,17,26,2,1310721,2,5,26,2,26,2,1679305,14, +117,110,107,110,111,119,110,32,110,97,109,101,32,105,110,32, +97,115,109,0,1679265,1585849,12,8,1310721,2,19,26,2,1310721,2,5, +26,2,26,2,1310721,2,15,26,2,1310721,2,7,26,2,26,2, +1589317,12,8,1310721,2,5,26,1,6,2,1,6,3,26,5,6, +4,10,8,8,1678979,14,1,1679413,14,1,1310721,2,21,26,1310721,4, +18,1310721,2,1,28,1310721,4,1310721,2,1835009,40,1679461,16,1966081,1310721,4, +1579069,12,1597071,12,8,1676857,12,8,1584883,1,10,3,6,10,4,8, +1,1679497,14,1,1310721,2,1,26,1310721,4,18,1310721,2,7,28,1310721, +4,1310721,2,1835009,40,1679545,16,1966081,1310721,4,1579069,12,1,1310721,2,4, +1,1310721,2,3,26,4,1584891,2,1598497,12,1,6,1,10,28,34, +63,38,3,26,1679631,16,1679623,14,101,120,112,101,99,116,101,100, +32,123,0,1679601,1586063,12,8,1597071,12,8,3,1680053,16,1582577,2,1, +28,1,6,1,10,28,34,63,38,3,26,1679725,16,1679717,14,117, +110,116,101,114,109,105,110,97,116,101,100,32,113,117,111,116, +101,0,1679679,1586063,12,8,1584891,2,1598497,12,1679771,16,1310721,2,3,26, +1,6,2,1,6,3,26,5,6,4,10,8,8,1584893,2,1598497, +12,1679855,16,1310721,2,3,26,2,1,6,1,10,28,34,63,38, +3,26,1679821,16,1680053,14,1310721,2,3,26,1,6,2,1,6,-1, +26,5,6,4,10,8,8,1310721,2,2,5401,40,3,36,1679915,16, +1679907,14,113,117,111,116,101,32,116,111,111,32,108,111,110,103, +0,1679877,1585585,12,8,1524225,1310721,2,2,7,30,26,1582577,2,10,3, +6,10,4,8,1524225,1310721,2,2,7,30,3,26,26,1582579,2,10, +3,6,10,4,8,1524225,1310721,2,2,7,30,5,26,26,1582583,2, +10,3,6,10,4,8,1310721,2,1,6,2,1,6,3,26,5, +6,4,10,8,8,1597071,12,8,1679637,14,1597071,12,8,1310721,2,5, +26,1310721,2,2,7,30,3,26,1579923,12,10,3,6,10,4,8, +1310721,2,5,26,2,1,26,1310721,2,2,10,3,6,10,4,8, +1310721,2,5,26,2,3,26,1524225,1310721,2,2,7,30,1574337,12,8, +1310721,2,5,26,2,1680177,14,1,1310721,2,7,26,1310721,4,18,1310721, +2,7,28,1310721,4,1310721,2,1835009,40,1680225,16,1966081,1310721,4,1579069,12, +1310721,2,4,1310721,2,2,1,26,2,7,30,3,26,1310721,2,3, +26,4,1310721,2,3,26,2,1579923,12,1310721,2,5,26,4,1310721,2, +5,26,2,1310721,2,2,1310721,2,3,26,2,1574337,12,8,1310721,2, +5,26,2,1680333,14,1,1310721,2,7,26,1310721,4,18,1310721,2,7, +28,1310721,4,1310721,2,1835009,40,1680381,16,1966081,1310721,4,1579069,12,1310721,2, +4,1310721,2,2,1,26,2,7,30,3,26,1310721,2,3,26,4, +1310721,2,3,26,2,1579923,12,1310721,2,5,26,4,1310721,2,5,26, +2,1310721,2,2,1310721,2,3,26,2,1574337,12,8,1310721,2,5,26, +2,1680489,14,1,1310721,2,7,26,1310721,4,18,1310721,2,7,28,1310721, +4,1310721,2,1835009,40,1680537,16,1966081,1310721,4,1579069,12,1310721,2,3,26, +4,1310721,2,4,1,1310721,2,5,26,4,1310721,2,5,26,2,1310721, +2,3,26,2,40,1680655,16,1310721,2,2,1310721,2,5,26,2,26, +2,1588955,12,8,1310721,2,5,26,1,6,2,1,6,3,26,5, +6,4,10,8,8,1680565,14,1,1680663,14,1,1310721,2,7,26,1310721, +4,18,1310721,2,9,28,1310721,4,1310721,2,1835009,40,1680711,16,1966081,1310721, +4,1579069,12,1597071,12,8,1310721,2,1679511,12,10,3,6,10,4,8, +1310721,2,3,26,1589535,12,10,3,6,10,4,8,15,1589235,12,8, +1310721,2,5,26,1584837,2,10,3,6,10,4,8,1310721,2,7,26, +1310721,2,2,1,26,2,7,30,3,26,10,3,6,10,4,8, +1310721,2,2,1310721,2,7,26,2,1680503,12,8,1310721,2,2,1580623,12, +8,1310721,2,3,26,2,1584837,2,1589317,12,8,1310721,2,5,26,2, +1589149,12,8,1584963,2,1589915,12,8,1584883,3,10,3,6,10,4,8, +1,1680937,14,1,1310721,2,9,26,1310721,4,18,1310721,2,7,28,1310721, +4,1310721,2,1835009,40,1680985,16,1966081,1310721,4,1579069,12,1310721,2,4,1, +1310721,2,3,26,4,5,1310721,2,2,1,1,1590943,12,8,1597071,12, +8,1582577,2,1,28,1,6,1,10,28,34,63,38,1,10,28, +1681281,16,1310721,2,3,26,2,5401,40,3,36,1681127,16,1681119,14,116, +111,111,32,109,97,110,121,32,116,111,107,101,110,115,0,1681087, +1585585,12,8,1524225,1310721,2,3,26,2,7,30,26,1582577,2,10,3, +6,10,4,8,1524225,1310721,2,3,26,2,7,30,3,26,26,1582579, +2,10,3,6,10,4,8,1524225,1310721,2,3,26,2,7,30,5, +26,26,1582583,2,10,3,6,10,4,8,1310721,2,3,26,1,6, +2,1,6,3,26,5,6,4,10,8,8,1597071,12,8,1681027,14, +1592371,12,8,1310721,2,5,26,1310721,2,3,26,2,7,30,3,26, +1579923,12,10,3,6,10,4,8,1310721,2,5,26,2,1,26,1310721, +2,3,26,2,10,3,6,10,4,8,1310721,2,5,26,2,3, +26,1524225,1310721,2,3,26,2,7,30,1574337,12,8,1310721,2,5,26, +2,1681417,14,1,1310721,2,7,26,1310721,4,18,1310721,2,3,28,1310721, +4,1310721,2,1835009,40,1681465,16,1966081,1310721,4,1579069,12,1310721,2,4,3, +1310721,2,2,3,3,1590943,12,8,1597071,12,8,1,1681503,14,1,1310721, +2,3,26,1310721,4,18,1310721,2,13,28,1310721,4,1310721,2,1835009,40, +1681551,16,1966081,1310721,4,1579069,12,8181,1310721,2,4,8181,1579923,12,1310721,2, +3,26,4,1,1310721,2,5,26,4,1573227,12,1,6,1,10,28, +34,63,38,3,26,1682109,16,1310721,2,7,26,1573171,12,511,32,10, +3,6,10,4,8,1310721,2,7,26,2,193,28,1,6,1,10, +28,34,63,38,3,26,1681685,16,1682109,14,1310721,2,5,26,2,5, +38,5,26,1310721,2,2,40,3,36,1681873,16,1310721,2,9,26,1310721, +2,2,1310721,2,2,26,13,26,1579923,12,10,3,6,10,4,8, +1310721,2,9,26,2,1310721,2,3,26,2,1310721,2,2,1574337,12,8, +1310721,2,3,26,2,1580623,12,8,1310721,2,3,26,1310721,2,9,26, +2,10,3,6,10,4,8,1310721,2,1310721,2,2,1310721,2,2,26, +13,26,10,3,6,10,4,8,1310721,2,11,26,1310721,2,5,26, +2,5,38,10,3,6,10,4,8,1310721,2,5,26,2,7,32, +1,6,1,10,28,34,63,38,3,26,1681981,16,1310721,2,3,26, +2,1310721,2,11,26,2,26,1,10,3,6,10,4,8,1310721,2, +3,26,2,1310721,2,11,26,2,26,1310721,2,3,26,2,1310721,2, +11,26,2,26,2,1310721,2,7,26,2,1310721,2,5,26,2,7, +32,17,30,48,34,10,3,6,10,4,8,1310721,2,5,26,1, +6,2,1,6,3,26,5,6,4,10,8,8,1681587,14,1310721,2, +3,26,2,1310721,2,5,26,2,5,38,26,1310721,2,3,26,2, +1310721,2,5,26,2,5,38,26,2,1574747,1310721,2,5,26,2,7, +32,17,30,26,2,3,28,32,10,3,6,10,4,8,1310721,2, +5,26,2,7,32,1,28,1,6,1,10,28,34,63,38,3, +26,1682287,16,1310721,2,3,26,2,1310721,2,5,26,2,5,38,26, +1,10,3,6,10,4,8,1310721,2,3,26,2,1310721,2,5,26, +2,5,38,3,26,26,1,10,3,6,10,4,8,1310721,2,3, +26,2,1682347,14,1,1310721,2,13,26,1310721,4,18,1310721,2,3,28, +1310721,4,1310721,2,1835009,40,1682395,16,1966081,1310721,4,1579069,12,1,1310721,2, +4,1582577,2,1,28,1,6,1,10,28,34,63,38,1,10,28, +1682603,16,1585015,2,1598497,12,1682483,16,1310721,2,1,6,2,1,6,3, +26,5,6,4,10,8,8,1682567,14,1585017,2,1598497,12,1682529,16,1310721, +2,1,6,2,1,6,-1,26,5,6,4,10,8,8,1682567,14, +1585019,2,1598497,12,1682567,16,1310721,2,1310721,2,2,5,28,10,3,6, +10,4,8,1597071,12,8,1310721,2,2,1,10,40,3,36,1682599,16, +1,1682611,14,1682403,14,1,1682611,14,1,1310721,2,3,26,1310721,4,18, +1310721,2,15,28,1310721,4,1310721,2,1835009,40,1682659,16,1966081,1310721,4,1579069, +12,1582579,2,1310721,2,4,1,1310721,2,3,26,4,1,1310721,2,5, +26,4,1,1310721,2,7,26,4,1,1310721,2,9,26,4,1582577,2, +5,28,1,6,1,10,28,34,63,38,1,10,28,1682757,16,1, +1684237,14,1597071,12,8,1585015,2,1598497,12,1682781,16,1682361,12,8,1584887,2, +1598497,12,1,6,1,10,28,34,63,38,3,26,1682819,16,1,1684237, +14,1597071,12,8,1584889,2,1598497,12,1,6,1,10,28,34,63,38, +3,26,1683569,16,1310721,2,3,26,3,10,3,6,10,4,8,1310721, +2,11,26,3,10,3,6,10,4,8,1582577,2,1,28,1,6, +1,10,28,34,63,38,1,10,28,1683475,16,1584887,2,1598497,12,1682985, +16,1310721,2,11,26,1,6,2,1,6,3,26,5,6,4,10, +8,8,1683465,14,1584889,2,1598497,12,1683073,16,1310721,2,11,26,1,6, +2,1,6,-1,26,5,6,4,10,8,8,1310721,2,11,26,2, +1,6,1,10,28,34,63,38,3,26,1683069,16,1683475,14,1683465,14, +1310721,2,11,26,2,3,28,1,6,1,10,28,34,63,38,3, +26,1683145,16,1584899,2,1598497,12,1,6,1,10,28,34,63,38,1, +10,28,1683147,14,1,1683305,16,1310721,2,3,26,2,25,10,40,3, +36,1683245,16,1310721,2,5,26,1310721,2,5,26,2,1310721,2,7,26, +2,1310721,2,3,26,2,1310721,2,3,26,2,26,5,28,48,26, +10,3,6,10,4,8,1310721,2,3,26,1,6,2,1,6,3, +26,5,6,4,10,8,8,1310721,2,7,26,1,10,3,6,10, +4,8,1683465,14,1310721,2,11,26,2,3,28,1,6,1,10,28, +34,63,38,3,26,1683377,16,1584925,2,1598497,12,1,6,1,10,28, +34,63,38,1,10,28,1683379,14,1,1683465,16,1597071,12,8,1584941,2, +1598649,12,1683427,16,1310721,2,7,26,3,10,3,6,10,4,8,1683461, +14,1584943,2,1598649,12,1683461,16,1310721,2,7,26,5,10,3,6,10, +4,8,1682901,14,1597071,12,8,1682901,14,1310721,2,3,26,2,25,10, +40,3,36,1683569,16,1310721,2,5,26,1310721,2,5,26,2,1310721,2, +7,26,2,1310721,2,3,26,2,1310721,2,3,26,2,26,5,28, +48,26,10,3,6,10,4,8,1597071,12,8,1584905,2,1598801,12,1683807, +16,1582577,2,1,28,1,6,1,10,28,34,63,38,1,10,28, +1683675,16,1584891,2,1598497,12,1,6,1,10,28,34,63,38,3,26, +1,6,1,10,28,34,63,38,1,10,28,1683677,14,1,1683735,16, +1584901,2,1598497,12,1,6,1,10,28,34,63,38,3,26,1,6, +1,10,28,34,63,38,1,10,28,1683737,14,1,1683807,16,1584925,2, +1598497,12,1683797,16,1597071,12,8,1584945,2,1598649,12,1683793,16,1310721,2,9, +26,3,10,3,6,10,4,8,1683587,14,1597071,12,8,1683587,14,1310721, +2,13,26,1310721,2,2,1587809,12,10,3,6,10,4,8,1310721,2, +13,26,2,1,40,1683861,16,3,1683899,14,1310721,2,13,26,2,1582509, +2,40,1,6,1,10,28,34,63,38,1,10,28,1683909,16,3, +1683973,14,1417729,1310721,2,13,26,2,26,2,3,28,1,6,1,10, +28,34,63,38,1,10,28,1,6,1,10,28,34,63,38,1, +10,28,1683983,16,3,1684025,14,1442305,1310721,2,13,26,2,26,2,3, +32,1,6,1,10,28,34,63,38,1,10,28,1684059,16,1310721,2, +13,26,1310721,2,2,1589617,12,10,3,6,10,4,8,1442305,1310721,2, +13,26,2,26,2,5,32,1,6,1,10,28,34,63,38,3, +26,1684229,16,1434113,1310721,2,13,26,2,26,1310721,2,9,26,2,1310721, +2,3,26,2,26,1310721,2,3,26,2,26,1310721,2,5,26,2, +129,30,26,10,3,6,10,4,8,1442305,1310721,2,13,26,2,26, +1442305,1310721,2,13,26,2,26,2,5,34,513,34,10,3,6,10, +4,8,1,1684237,14,1,1310721,2,15,26,1310721,4,18,1310721,2,11, +28,1310721,4,1310721,2,1835009,40,1684285,16,1966081,1310721,4,1579069,12,1310721,2, +3,26,4,1310721,2,4,1,1310721,2,5,26,4,1585091,2,1310721,2, +7,26,4,1585089,2,1310721,2,9,26,4,1584991,1,6,2,1,6, +3,26,5,6,4,10,8,8,1310721,2,2,1310721,2,3,26,2, +1,1,1590943,12,8,1597071,12,8,1582577,2,1,28,1,6,1,10, +28,34,63,38,1,10,28,1684625,16,1584891,2,1598497,12,1684485,16,1310721, +2,5,26,1,6,2,1,6,3,26,5,6,4,10,8,8, +1684615,14,1584893,2,1598497,12,1684535,16,1310721,2,5,26,1,6,2,1, +6,-1,26,5,6,4,10,8,8,1684615,14,1310721,2,5,26,2, +1,10,40,3,36,1684593,16,1584927,2,1598649,12,1,6,1,10,28, +34,63,38,1,10,28,1684595,14,1,1684615,16,1597071,12,8,1682625,12, +8,1684401,14,1597071,12,8,1684401,14,1592371,12,8,1584991,1,6,2,1, +6,-1,26,5,6,4,10,8,8,1585091,1310721,2,7,26,2,10, +3,6,10,4,8,1585089,1310721,2,9,26,2,10,3,6,10,4, +8,1,1684715,14,1,1310721,2,11,26,1310721,4,18,1310721,2,3,28, +1310721,4,1310721,2,1835009,40,1684763,16,1966081,1310721,4,1579069,12,1681517,12,1310721, +2,4,7,1310721,2,2,1684251,12,8,7,1310721,2,2,1,1,1590943, +12,8,1597071,12,8,1697867,12,8,1582509,2,1697967,12,8,1592371,12,8, +1310721,2,2,1580623,12,8,1,1684853,14,1,1310721,2,3,26,1310721,4, +18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1684901,16,1966081,1310721,4, +1579069,12,1548801,1584871,2,49,30,26,1310721,2,4,1584871,2,61,40,3, +36,1685001,16,1684993,14,99,111,109,112,105,108,101,115,32,110,101, +115,116,101,100,32,116,111,111,32,100,101,101,112,108,121,0, +1684939,1585585,12,8,1584871,1,6,2,1,6,3,26,5,6,4,10, +8,8,1310721,2,2,1,26,1584845,2,10,3,6,10,4,8,1310721, +2,2,3,26,1584847,2,10,3,6,10,4,8,1310721,2,2,5, +26,1584849,2,10,3,6,10,4,8,1310721,2,2,7,26,1584851,2, +10,3,6,10,4,8,1310721,2,2,9,26,1584853,2,10,3,6, +10,4,8,1310721,2,2,11,26,1584855,2,10,3,6,10,4,8, +1310721,2,2,13,26,1584857,2,10,3,6,10,4,8,1310721,2,2, +15,26,1584859,2,10,3,6,10,4,8,1310721,2,2,17,26,1584861, +2,10,3,6,10,4,8,1310721,2,2,19,26,1584863,2,10,3, +6,10,4,8,1310721,2,2,21,26,1584865,2,10,3,6,10,4, +8,1310721,2,2,23,26,1584867,2,10,3,6,10,4,8,1310721,2, +2,25,26,1584869,2,10,3,6,10,4,8,1310721,2,2,27,26, +1584875,2,10,3,6,10,4,8,1310721,2,2,29,26,1584877,2,10, +3,6,10,4,8,1310721,2,2,31,26,1584879,2,10,3,6,10, +4,8,1310721,2,2,33,26,1584881,2,10,3,6,10,4,8,1310721, +2,2,35,26,1584883,2,10,3,6,10,4,8,1310721,2,2,37, +26,1584873,2,10,3,6,10,4,8,1310721,2,2,39,26,1584885,2, +10,3,6,10,4,8,1310721,2,2,41,26,1585009,2,10,3,6, +10,4,8,1310721,2,2,43,26,1584987,2,10,3,6,10,4,8, +1310721,2,2,45,26,1584993,5,1574337,12,8,1,1685629,14,1,1310721,2, +3,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1685677, +16,1966081,1310721,4,1579069,12,1584871,1,6,2,1,6,-1,26,5,6, +4,10,8,8,1310721,2,1548801,1584871,2,49,30,26,10,3,6,10, +4,8,1584845,1310721,2,2,1,26,2,10,3,6,10,4,8,1584847, +1310721,2,2,3,26,2,10,3,6,10,4,8,1584849,1310721,2,2, +5,26,2,10,3,6,10,4,8,1584851,1310721,2,2,7,26,2, +10,3,6,10,4,8,1584853,1310721,2,2,9,26,2,10,3,6, +10,4,8,1584855,1310721,2,2,11,26,2,10,3,6,10,4,8, +1584857,1310721,2,2,13,26,2,10,3,6,10,4,8,1584859,1310721,2, +2,15,26,2,10,3,6,10,4,8,1584861,1310721,2,2,17,26, +2,10,3,6,10,4,8,1584863,1310721,2,2,19,26,2,10,3, +6,10,4,8,1584865,1310721,2,2,21,26,2,10,3,6,10,4, +8,1584867,1310721,2,2,23,26,2,10,3,6,10,4,8,1584869,1310721, +2,2,25,26,2,10,3,6,10,4,8,1584875,1310721,2,2,27, +26,2,10,3,6,10,4,8,1584877,1310721,2,2,29,26,2,10, +3,6,10,4,8,1584879,1310721,2,2,31,26,2,10,3,6,10, +4,8,1584881,1310721,2,2,33,26,2,10,3,6,10,4,8,1584883, +1310721,2,2,35,26,2,10,3,6,10,4,8,1584873,1310721,2,2, +37,26,2,10,3,6,10,4,8,1584885,1310721,2,2,39,26,2, +10,3,6,10,4,8,1585009,1310721,2,2,41,26,2,10,3,6, +10,4,8,1584987,1310721,2,2,43,26,2,10,3,6,10,4,8, +1584993,1310721,2,2,45,26,5,1574337,12,8,1,1686333,14,1,1310721,2, +3,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,1835009,40,1686381, +16,1966081,1310721,4,1579069,12,1310721,2,3,26,4,1310721,2,4,1584847,1584845, +2,10,3,6,10,4,8,1584849,3,10,3,6,10,4,8,1584851, +1,10,3,6,10,4,8,1584853,1310721,2,2,1686489,16,1582575,2,1, +6,1,10,28,34,63,38,1,10,28,1686491,14,1,10,3,6, +10,4,8,1584855,1310721,2,3,26,2,10,3,6,10,4,8,1584885, +1,10,3,6,10,4,8,1584857,3,10,3,6,10,4,8,1584859, +1,10,3,6,10,4,8,1584861,1,10,3,6,10,4,8,1584865, +3,10,3,6,10,4,8,1584869,1584867,2,10,3,6,10,4,8, +1584875,1,10,3,6,10,4,8,1584877,1,10,3,6,10,4,8, +1,1686665,14,1,1310721,2,5,26,1310721,4,18,1310721,2,3,28,1310721, +4,1310721,2,1835009,40,1686713,16,1966081,1310721,4,1579069,12,1310721,1589149,12,8, +3,1589235,12,8,1584863,1589535,12,10,3,6,10,4,8,29,1589235,12, +8,1310721,1589149,12,8,5,1589235,12,8,1310721,1589149,12,8,3,1589235,12, +8,1835009,1589149,12,8,41,1589235,12,8,1310721,2,1589535,12,10,3,6, +10,4,8,17,1589235,12,8,1966081,1589149,12,8,1310721,1589149,12,8,5, +1589235,12,8,1584953,2,1589915,12,8,1310721,2,2,1584837,2,1589317,12,8, +1,1686889,14,1,1310721,2,3,26,1310721,4,18,1310721,2,1,28,1310721, +4,1310721,2,1835009,40,1686937,16,1966081,1310721,4,1579069,12,1,1589149,12,8, +1584861,2,1584837,2,1588737,12,8,1310721,1589149,12,8,3,1589235,12,8,1584851, +2,1589149,12,8,27,1589235,12,8,1310721,1589149,12,8,5,1589235,12,8, +19,1589235,12,8,1584863,2,1584851,2,1589317,12,8,1584865,1,10,3,6, +10,4,8,1584845,1584847,2,10,3,6,10,4,8,1,1687073,14,1, +1310721,2,1,26,1310721,4,18,1310721,2,1,28,1310721,4,1310721,2,1835009, +40,1687121,16,1966081,1310721,4,1579069,12,1584891,2,1599031,12,8,1584893,2,1598497, +12,1,6,1,10,28,34,63,38,3,26,1687287,16,1582577,2,1, +28,1,6,1,10,28,34,63,38,3,26,1687277,16,1687269,14,109, +105,115,115,105,110,103,32,125,32,97,116,32,116,104,101,32, +101,110,100,32,111,102,32,97,32,102,117,110,99,116,105,111, +110,0,1687199,1586063,12,8,1666771,12,8,1687131,14,1584847,2,1606191,12,8, +1584853,2,1687335,16,1584857,2,1,6,1,10,28,34,63,38,1,10, +28,1687337,14,1,1687455,16,3,3,1605463,12,8,1584855,2,1687455,16,1687447, +14,97,32,45,62,32,46,117,32,102,117,110,99,116,105,111, +110,32,104,97,115,32,116,111,32,101,110,100,32,119,105,116, +104,32,97,32,114,101,116,117,114,110,0,1687363,1586351,12,8,1597071, +12,8,1,1687469,14,1,1310721,2,1,26,1310721,4,18,1310721,2,169, +28,1310721,4,1310721,2,1835009,40,1687517,16,1966081,1310721,4,1579069,12,1310721,2, +4,1,1310721,2,7,26,4,1,1310721,2,9,26,4,1,1310721,2, +13,26,4,1,1310721,2,163,26,4,1,1310721,2,165,26,4,1597071, +12,8,1310721,2,3,26,1599321,12,10,3,6,10,4,8,1585009,1, +10,3,6,10,4,8,1585015,2,1598497,12,1687851,16,1597071,12,8,3, +1687823,16,1585009,2,17,40,3,36,1687731,16,1687723,14,116,111,111,32, +109,97,110,121,32,116,121,112,101,32,112,97,114,97,109,101, +116,101,114,115,0,1687673,1586063,12,8,1584993,1585009,2,26,1599321,12,10, +3,6,10,4,8,1585009,1,6,2,1,6,3,26,5,6,4, +10,8,8,1584899,2,1598801,12,1,6,1,10,28,34,63,38,3, +26,1687819,16,1687823,14,1687647,14,1635147,12,8,1310721,2,165,26,3,10, +3,6,10,4,8,1310721,2,5,26,1310721,2,3,26,2,1587809,12, +10,3,6,10,4,8,1310721,2,5,26,2,1,40,1687909,16,3, +1687947,14,1310721,2,5,26,2,1582509,2,40,1,6,1,10,28,34, +63,38,1,10,28,1687957,16,3,1688021,14,1417729,1310721,2,5,26,2, +26,2,3,28,1,6,1,10,28,34,63,38,1,10,28,1, +6,1,10,28,34,63,38,1,10,28,1688031,16,3,1688073,14,1442305, +1310721,2,5,26,2,26,2,3,32,1,6,1,10,28,34,63, +38,1,10,28,1688143,16,1310721,2,5,26,1310721,2,3,26,2,3, +1,1587311,12,10,3,6,10,4,8,1442305,1310721,2,5,26,2,26, +1,10,3,6,10,4,8,1584887,2,1599031,12,8,1584889,2,1598497,12, +1,6,1,10,28,34,63,38,3,26,1689487,16,3,1689487,16,1310721, +2,11,26,1,10,3,6,10,4,8,1310721,2,115,26,1310721,2, +7,26,2,26,1584975,2,10,3,6,10,4,8,1584925,2,1598497,12, +1,6,1,10,28,34,63,38,3,26,1688621,16,1582577,2,9,28, +1,6,1,10,28,34,63,38,3,26,1688431,16,1582579,2,1584911,2, +28,1,6,1,10,28,34,63,38,3,26,1688353,16,3,1688405,14, +1582579,2,1584895,2,28,1,6,1,10,28,34,63,38,3,26,1, +6,1,10,28,34,63,38,1,10,28,1,6,1,10,28,34, +63,38,1,10,28,1688433,14,1,1688443,16,3,1688595,14,1582577,2,5, +28,1,6,1,10,28,34,63,38,3,26,1688571,16,1582579,2,1584927, +2,28,1,6,1,10,28,34,63,38,3,26,1688515,16,3,1688545, +14,1582579,2,1634911,12,1,6,1,10,28,34,63,38,1,10,28, +1,6,1,10,28,34,63,38,1,10,28,1688573,14,1,1,6, +1,10,28,34,63,38,1,10,28,1,6,1,10,28,34,63, +38,1,10,28,1688623,14,1,1688809,16,1310721,2,115,26,1310721,2,7, +26,2,26,1635249,12,10,3,6,10,4,8,1310721,2,165,26,3, +10,3,6,10,4,8,1310721,2,115,26,1310721,2,7,26,2,26, +2,1629979,12,1688809,16,1688801,14,115,116,114,117,99,116,115,32,97, +110,100,32,97,114,114,97,121,115,32,97,114,101,32,112,97, +115,115,101,100,32,98,121,32,112,111,105,110,116,101,114,0, +1688719,1585679,12,8,1584925,2,1598801,12,1689015,16,1582579,2,1584941,2,28,1, +6,1,10,28,34,63,38,3,26,1688881,16,1310721,2,11,26,3, +10,3,6,10,4,8,1689009,14,1582579,2,1584943,2,28,1,6,1, +10,28,34,63,38,3,26,1688941,16,1310721,2,11,26,5,10,3, +6,10,4,8,1689009,14,1689001,14,97,32,112,97,114,97,109,101, +116,101,114,32,116,97,103,32,105,115,32,46,109,32,111,114, +32,46,100,0,1688945,1586063,12,8,1597071,12,8,1310721,2,19,26,1310721, +2,7,26,2,26,1599321,12,10,3,6,10,4,8,1310721,2,7, +26,2,25,40,3,36,1689109,16,1310721,2,11,26,2,1,6,1, +10,28,34,63,38,1,10,28,1689111,14,1,1689213,16,1689205,14,111, +110,108,121,32,116,104,101,32,102,105,114,115,116,32,49,50, +32,112,97,114,97,109,101,116,101,114,115,32,99,97,110,32, +98,101,32,116,97,103,103,101,100,0,1689119,1586063,12,8,1310721,2, +67,26,1310721,2,7,26,2,26,1310721,2,11,26,2,10,3,6, +10,4,8,1310721,2,7,26,2,25,40,1689339,16,1310721,2,9,26, +1310721,2,9,26,2,1310721,2,11,26,2,1310721,2,7,26,2,1310721, +2,7,26,2,26,48,26,10,3,6,10,4,8,1310721,2,7, +26,1,6,2,1,6,3,26,5,6,4,10,8,8,1310721,2, +7,26,2,49,40,3,36,1689447,16,1689439,14,116,111,111,32,109, +97,110,121,32,112,97,114,97,109,101,116,101,114,115,0,1689399, +1586063,12,8,1584899,2,1598801,12,1,6,1,10,28,34,63,38,3, +26,1689483,16,1689487,14,1688185,14,1584889,2,1599031,12,8,1584905,2,1598801,12, +1689713,16,1584925,2,1598497,12,1,6,1,10,28,34,63,38,3,26, +1689587,16,1310721,2,163,26,1635249,12,10,3,6,10,4,8,1310721,2, +165,26,3,10,3,6,10,4,8,1584925,2,1598801,12,1689713,16,1584945, +2,1598649,12,1,6,1,10,28,34,63,38,3,26,1689685,16,1689677, +14,116,104,101,32,114,101,116,117,114,110,32,116,97,103,32, +105,115,32,46,117,0,1689635,1586063,12,8,1597071,12,8,1310721,2,13, +26,3,10,3,6,10,4,8,1310721,2,15,26,1310721,2,13,26, +2,1310721,2,7,26,2,26,1310721,2,7,26,2,26,1310721,2,9, +26,2,129,30,26,10,3,6,10,4,8,1442305,1310721,2,5,26, +2,26,2,5,32,1689873,16,1442305,1310721,2,5,26,2,26,2,513, +32,1,6,1,10,28,34,63,38,3,26,1,6,1,10,28, +34,63,38,1,10,28,1689875,14,1,1690035,16,1434113,1310721,2,5,26, +2,26,2,1310721,2,15,26,2,28,1,6,1,10,28,34,63, +38,1,10,28,1690031,16,1310721,2,3,26,2,1690023,14,100,111,101, +115,110,39,116,32,109,97,116,99,104,32,105,116,115,32,101, +97,114,108,105,101,114,32,100,101,99,108,97,114,97,116,105, +111,110,0,1689947,1585849,12,8,1690323,14,1442305,1310721,2,5,26,2,26, +2,9,32,1690137,16,1310721,2,9,26,2,1690079,16,3,1690111,14,1310721, +2,13,26,2,1,6,1,10,28,34,63,38,1,10,28,1, +6,1,10,28,34,63,38,1,10,28,1690139,14,1,1690323,16,1310721, +2,3,26,2,1690315,14,119,97,115,32,99,97,108,108,101,100, +32,98,101,102,111,114,101,32,105,116,115,32,116,97,103,115, +32,119,101,114,101,32,100,101,99,108,97,114,101,100,59,32, +112,117,116,32,97,32,112,114,111,116,111,116,121,112,101,32, +97,98,111,118,101,32,116,104,101,32,102,105,114,115,116,32, +99,97,108,108,0,1690157,1585849,12,8,1434113,1310721,2,5,26,2,26, +1310721,2,15,26,2,10,3,6,10,4,8,1442305,1310721,2,5,26, +2,26,1442305,1310721,2,5,26,2,26,2,5,34,1442305,1310721,2,5, +26,2,26,2,513,32,28,10,3,6,10,4,8,1585009,2,1690481, +16,1442305,1310721,2,5,26,2,26,1442305,1310721,2,5,26,2,26,2, +257,34,10,3,6,10,4,8,1310721,2,165,26,2,1690851,16,1310721, +2,163,26,2,1,6,1,10,28,34,63,38,3,26,1690553,16, +1310721,2,163,26,1584975,2,10,3,6,10,4,8,1310721,2,167,26, +1310721,2,163,26,2,1310721,2,7,26,2,1310721,2,115,26,1310721,2, +15,26,2,1625329,12,10,3,6,10,4,8,1559041,1310721,2,5,26, +2,26,2,1690711,16,1559041,1310721,2,5,26,2,26,2,1310721,2,167, +26,2,28,1,6,1,10,28,34,63,38,1,10,28,1,6, +1,10,28,34,63,38,1,10,28,1690713,14,1,1690815,16,1310721,2, +3,26,2,1690807,14,100,111,101,115,110,39,116,32,109,97,116, +99,104,32,105,116,115,32,101,97,114,108,105,101,114,32,100, +101,99,108,97,114,97,116,105,111,110,0,1690731,1585849,12,8,1559041, +1310721,2,5,26,2,26,1310721,2,167,26,2,10,3,6,10,4, +8,1584901,2,1598801,12,1690893,16,1585009,1,10,3,6,10,4,8,1310721, +2,5,26,2,1691783,14,1442305,1310721,2,5,26,2,26,2,3,32, +1690973,16,1310721,2,3,26,2,1690965,14,105,115,32,100,101,102,105, +110,101,100,32,116,119,105,99,101,0,1690931,1585849,12,8,1425921,1310721, +2,5,26,2,26,1584837,2,10,3,6,10,4,8,1442305,1310721,2, +5,26,2,26,1442305,1310721,2,5,26,2,26,2,3,34,10,3, +6,10,4,8,1450497,1310721,2,5,26,2,26,2,1584837,2,1588737,12, +8,1450497,1310721,2,5,26,2,26,1,10,3,6,10,4,8,1310721, +2,2,1310721,2,13,26,2,1686347,12,8,1310721,2,165,26,2,1691163, +16,1584885,1310721,2,163,26,2,10,3,6,10,4,8,1686679,12,8, +1310721,2,17,26,1,10,3,6,10,4,8,1310721,2,17,26,2, +1310721,2,7,26,2,40,1691633,16,1310721,2,11,26,1310721,2,19,26, +1310721,2,17,26,2,26,2,1603891,12,10,3,6,10,4,8,1584851, +1,6,2,1,6,3,26,5,6,4,10,8,8,1556993,1310721,2, +11,26,2,26,1310721,2,115,26,1310721,2,17,26,2,26,2,10, +3,6,10,4,8,1584853,2,1691419,16,1310721,2,67,26,1310721,2,17, +26,2,26,2,3,28,1,6,1,10,28,34,63,38,3,26, +1,6,1,10,28,34,63,38,1,10,28,1691421,14,1,1691453,16, +1469953,1310721,2,11,26,2,26,13,10,3,6,10,4,8,1584853,2, +1691533,16,1310721,2,67,26,1310721,2,17,26,2,26,2,5,28,1, +6,1,10,28,34,63,38,3,26,1,6,1,10,28,34,63, +38,1,10,28,1691535,14,1,1691595,16,1469953,1310721,2,11,26,2,26, +3,10,3,6,10,4,8,1474049,1310721,2,11,26,2,26,3,10, +3,6,10,4,8,1310721,2,17,26,1,6,2,1,6,3,26, +5,6,4,10,8,8,1691191,14,1310721,2,17,26,1310721,2,7,26, +2,10,3,6,10,4,8,1310721,2,17,26,2,1691739,16,1310721,2, +17,26,1,6,2,1,6,-1,26,5,6,4,10,8,8,1310721, +2,17,26,2,1590069,12,8,5,1589235,12,8,1691663,14,1687087,12,8, +1686903,12,8,1585009,1,10,3,6,10,4,8,1310721,2,5,26,2, +1691783,14,1,1310721,2,169,26,1310721,4,18,1310721,2,13,28,1310721,4, +1310721,2,1835009,40,1691831,16,1966081,1310721,4,1579069,12,1310721,2,4,1,1310721, +2,9,26,4,1310721,2,2,1692107,16,1637945,12,8,1310721,2,9,26, +1585011,2,10,3,6,10,4,8,1585013,2,1692001,16,1691993,14,116,97, +103,115,32,103,111,32,111,110,32,108,111,99,97,108,115,32, +97,110,100,32,112,97,114,97,109,101,116,101,114,115,44,32, +110,111,116,32,103,108,111,98,97,108,115,0,1691901,1585679,12,8, +1310721,2,9,26,2,1584983,2,28,1,6,1,10,28,34,63,38, +3,26,1692103,16,1692095,14,97,32,118,97,114,105,97,98,108,101, +32,99,97,110,39,116,32,98,101,32,118,111,105,100,0,1692045, +1585679,12,8,1692113,14,1597071,12,8,3,1693065,16,1310721,2,3,26,1599321, +12,10,3,6,10,4,8,1584895,2,1598801,12,1692441,16,1310721,2,7, +26,1665803,12,10,3,6,10,4,8,1584897,2,1599031,12,8,1310721,2, +11,26,1310721,2,3,26,2,7,1584837,2,1587311,12,10,3,6,10, +4,8,1310721,2,9,26,2,1,6,1,10,28,34,63,38,3, +26,1692287,16,1310721,2,9,26,1584975,2,10,3,6,10,4,8,1559041, +1310721,2,11,26,2,26,1310721,2,9,26,2,1310721,2,7,26,2, +1624809,12,10,3,6,10,4,8,1310721,2,7,26,1559041,1310721,2,11, +26,2,26,2,1628577,12,10,3,6,10,4,8,1310721,2,7,26, +2,1692437,16,1,1588955,12,8,1310721,2,7,26,1,6,2,1,6, +-1,26,5,6,4,10,8,8,1692377,14,1693025,14,1310721,2,9,26, +2,1692495,16,1310721,2,9,26,2,1629979,12,1,6,1,10,28,34, +63,38,1,10,28,1692497,14,1,1692675,16,1310721,2,11,26,1310721,2, +3,26,2,7,1584837,2,1587311,12,10,3,6,10,4,8,1559041,1310721, +2,11,26,2,26,1310721,2,9,26,2,10,3,6,10,4,8, +1310721,2,7,26,1310721,2,9,26,2,1628577,12,10,3,6,10,4, +8,1310721,2,7,26,2,1692671,16,1,1588955,12,8,1310721,2,7,26, +1,6,2,1,6,-1,26,5,6,4,10,8,8,1692611,14,1693025, +14,1310721,2,5,26,1584837,2,10,3,6,10,4,8,1310721,2,11, +26,1310721,2,3,26,2,5,1310721,2,5,26,2,1587311,12,10,3, +6,10,4,8,1310721,2,9,26,2,1692795,16,1559041,1310721,2,11,26, +2,26,1310721,2,9,26,2,10,3,6,10,4,8,1,1588955,12, +8,1584903,2,1598801,12,1693025,16,1582577,2,7,28,1,6,1,10,28, +34,63,38,3,26,1692993,16,1310721,2,5,26,2,1584839,2,26,1584837, +2,10,3,6,10,4,8,1310721,2,7,26,1582579,2,10,3,6, +10,4,8,1310721,2,7,26,2,2,1692975,16,1310721,2,7,26,2, +2,1588955,12,8,1310721,2,7,26,1,6,2,1,6,3,26,5, +6,4,10,8,8,1692903,14,1,1588955,12,8,1597071,12,8,1693025,14, +1310721,2,5,26,2,1584839,2,26,1665803,12,10,3,6,10,4,8, +1584899,2,1598801,12,1,6,1,10,28,34,63,38,3,26,1693061,16, +1693065,14,1692113,14,1584901,2,1599031,12,8,1,1693083,14,1,1310721,2,13, +26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1693131,16, +1966081,1310721,4,1579069,12,1597071,12,8,3,1693231,16,1310721,2,1599321,12,10, +3,6,10,4,8,1584903,2,1599031,12,8,1310721,2,2,9,1665803,12, +1587311,12,8,1584899,2,1598801,12,1,6,1,10,28,34,63,38,3, +26,1693227,16,1693231,14,1693137,14,1584901,2,1599031,12,8,1,1693249,14,1, +1310721,2,3,26,1310721,4,18,1310721,2,43,28,1310721,4,1310721,2,1835009, +40,1693297,16,1966081,1310721,4,1579069,12,1,1310721,2,3,26,4,3,1310721, +2,5,26,4,1,1310721,2,13,26,4,1,1310721,2,15,26,4, +1597071,12,8,1310721,2,1599321,12,10,3,6,10,4,8,1584887,2,1599031, +12,8,1584889,2,1598497,12,1,6,1,10,28,34,63,38,3,26, +1693655,16,3,1693655,16,1310721,2,3,26,2,17,28,1,6,1,10, +28,34,63,38,3,26,1693545,16,1693537,14,97,110,32,101,110,104, +97,110,99,101,109,101,110,116,32,116,97,107,101,115,32,97, +116,32,109,111,115,116,32,56,32,105,110,112,117,116,115,0, +1693461,1586063,12,8,1310721,2,27,26,1310721,2,3,26,2,26,1599321,12, +10,3,6,10,4,8,1310721,2,3,26,1,6,2,1,6,3, +26,5,6,4,10,8,8,1584899,2,1598801,12,1,6,1,10,28, +34,63,38,3,26,1693651,16,1693655,14,1693413,14,1584889,2,1599031,12,8, +1584905,2,1598801,12,1693897,16,1582577,2,3,28,1,6,1,10,28,34, +63,38,1,10,28,1693717,16,3,1693747,14,1582579,2,1,40,1,6, +1,10,28,34,63,38,1,10,28,1693757,16,3,1693789,14,1582579,2, +17,10,40,1,6,1,10,28,34,63,38,1,10,28,1693867,16, +1693859,14,101,120,112,101,99,116,101,100,32,116,104,101,32,110, +117,109,98,101,114,32,111,102,32,111,117,116,112,117,116,115, +0,1693797,1586063,12,8,1310721,2,5,26,1582579,2,10,3,6,10,4, +8,1597071,12,8,1310721,2,7,26,1584837,2,10,3,6,10,4,8, +1310721,2,17,26,1,10,3,6,10,4,8,1310721,2,17,26,2, +15,40,1694007,16,1,1588955,12,8,1310721,2,17,26,1,6,2,1, +6,3,26,5,6,4,10,8,8,1693943,14,1310721,2,9,26,1584837, +2,10,3,6,10,4,8,1310721,2,17,26,1,10,3,6,10, +4,8,1310721,2,17,26,2,1310721,2,3,26,2,40,1694125,16,1, +1588955,12,8,1310721,2,17,26,1,6,2,1,6,3,26,5,6, +4,10,8,8,1694053,14,1310721,2,17,26,1,10,3,6,10,4, +8,1310721,2,17,26,2,1310721,2,3,26,2,40,1694377,16,1310721,2, +9,26,2,1310721,2,17,26,2,26,1584839,2,26,1584837,2,10,3, +6,10,4,8,1310721,2,19,26,1310721,2,27,26,1310721,2,17,26, +2,26,2,10,3,6,10,4,8,1310721,2,19,26,2,2,1694331, +16,1310721,2,19,26,2,2,1588955,12,8,1310721,2,19,26,1,6, +2,1,6,3,26,5,6,4,10,8,8,1694259,14,1,1588955,12, +8,1310721,2,17,26,1,6,2,1,6,3,26,5,6,4,10, +8,8,1694147,14,1584967,2,1598801,12,1694673,16,1310721,2,11,26,1584837,2, +10,3,6,10,4,8,1582577,2,7,28,1,6,1,10,28,34, +63,38,1,10,28,1694523,16,1694515,14,101,120,112,101,99,116,101, +100,32,116,104,101,32,114,101,99,105,112,101,32,97,115,32, +97,32,115,116,114,105,110,103,0,1694451,1586063,12,8,1582577,2,7, +28,1,6,1,10,28,34,63,38,3,26,1694661,16,1310721,2,19, +26,1582579,2,10,3,6,10,4,8,1310721,2,19,26,2,2,1694651, +16,1310721,2,19,26,2,2,1588955,12,8,1310721,2,19,26,1,6, +2,1,6,3,26,5,6,4,10,8,8,1694579,14,1597071,12,8, +1694523,14,1,1588955,12,8,1694839,14,1584969,2,1598801,12,1694785,16,1310721,2, +11,26,1584837,2,10,3,6,10,4,8,1676857,12,8,19,1589235,12, +8,1310721,2,13,26,1584837,2,1310721,2,11,26,2,28,10,3,6, +10,4,8,1310721,2,15,26,3,10,3,6,10,4,8,1694839,14, +1694831,14,101,120,112,101,99,116,101,100,32,109,105,99,114,111, +32,111,114,32,118,109,0,1694789,1586063,12,8,1310721,2,23,26,1582503, +2,10,3,6,10,4,8,1310721,2,23,26,2,1025,40,3,36, +1694969,16,1694961,14,97,108,108,32,53,49,50,32,101,110,104,97, +110,99,101,109,101,110,116,32,115,108,111,116,115,32,97,114, +101,32,116,97,107,101,110,0,1694889,1585585,12,8,1582503,1,6,2, +1,6,3,26,5,6,4,10,8,8,1310721,2,19,26,1310721,2, +7,26,2,1584839,2,26,10,3,6,10,4,8,1310721,2,19,26, +2,1,26,1310721,2,23,26,2,10,3,6,10,4,8,1310721,2, +19,26,2,3,26,1310721,2,15,26,2,10,3,6,10,4,8, +1310721,2,19,26,2,5,26,1310721,2,3,26,2,10,3,6,10, +4,8,1310721,2,19,26,2,7,26,1310721,2,5,26,2,10,3, +6,10,4,8,1310721,2,19,26,2,9,26,1310721,2,11,26,2, +10,3,6,10,4,8,1310721,2,19,26,2,11,26,1310721,2,13, +26,2,10,3,6,10,4,8,1310721,2,19,26,2,13,26,1310721, +2,9,26,2,10,3,6,10,4,8,1310721,2,21,26,1310721,2, +2,13,1310721,2,23,26,2,1587311,12,10,3,6,10,4,8,1434113, +1310721,2,21,26,2,26,1310721,2,3,26,2,1310721,2,5,26,2, +33,30,26,1310721,2,7,26,2,513,30,26,10,3,6,10,4, +8,1584933,2,1598801,12,1695527,16,1310721,2,25,26,1582579,2,1587809,12,10, +3,6,10,4,8,1310721,2,25,26,2,1,40,1695481,16,1310721,2, +25,26,1582579,2,1589617,12,10,3,6,10,4,8,1597071,12,8,1450497, +1310721,2,21,26,2,26,1310721,2,25,26,2,3,26,10,3,6, +10,4,8,1584901,2,1599031,12,8,1584839,2,1,6,1,10,28,34, +63,38,3,26,1695847,16,1310721,2,25,26,1310721,2,7,26,2,1572879, +12,10,3,6,10,4,8,1310721,2,25,26,2,1,40,1695847,16, +1450497,1310721,2,21,26,2,26,2,1,6,1,10,28,34,63,38, +3,26,1695801,16,1310721,2,2,1695793,14,116,104,101,32,101,110,104, +97,110,99,101,109,101,110,116,32,100,105,100,110,39,116,32, +105,110,115,116,97,108,108,32,40,115,101,101,32,105,110,115, +116,97,108,108,39,115,32,99,111,100,101,115,32,105,110,32, +99,111,114,101,46,99,41,0,1695667,1585849,12,8,1442305,1310721,2,21, +26,2,26,1442305,1310721,2,21,26,2,26,2,129,34,10,3,6, +10,4,8,1,1695855,14,1,1310721,2,43,26,1310721,4,18,1310721,2, +3,28,1310721,4,1310721,2,1835009,40,1695903,16,1966081,1310721,4,1579069,12,1, +1310721,2,4,1582577,2,1,28,1,6,1,10,28,34,63,38,1, +10,28,1696731,16,1584891,2,1598497,12,1695991,16,1310721,2,1,6,2,1, +6,3,26,5,6,4,10,8,8,1696721,14,1584893,2,1598497,12,1696069, +16,1310721,2,1,6,2,1,6,-1,26,5,6,4,10,8,8, +1310721,2,2,1,40,1696065,16,1310721,2,1,10,3,6,10,4,8, +1696721,14,1310721,2,2,1,6,1,10,28,34,63,38,3,26,1696153, +16,1582577,2,5,28,1,6,1,10,28,34,63,38,3,26,1, +6,1,10,28,34,63,38,1,10,28,1696155,14,1,1696711,16,1582579, +2,1584927,2,28,1,6,1,10,28,34,63,38,3,26,1696199,16, +3,1696251,14,1582579,2,1584929,2,28,1,6,1,10,28,34,63,38, +3,26,1,6,1,10,28,34,63,38,1,10,28,1696261,16,3, +1696313,14,1582579,2,1584931,2,28,1,6,1,10,28,34,63,38,3, +26,1,6,1,10,28,34,63,38,1,10,28,1696323,16,3,1696375, +14,1582579,2,1585031,2,28,1,6,1,10,28,34,63,38,3,26, +1,6,1,10,28,34,63,38,1,10,28,1696385,16,3,1696437,14, +1582579,2,1585033,2,28,1,6,1,10,28,34,63,38,3,26,1, +6,1,10,28,34,63,38,1,10,28,1696447,16,3,1696499,14,1582579, +2,1584965,2,28,1,6,1,10,28,34,63,38,3,26,1,6, +1,10,28,34,63,38,1,10,28,1696509,16,3,1696561,14,1582579,2, +1585035,2,28,1,6,1,10,28,34,63,38,3,26,1,6,1, +10,28,34,63,38,1,10,28,1696571,16,3,1696623,14,1582579,2,1584937, +2,28,1,6,1,10,28,34,63,38,3,26,1,6,1,10, +28,34,63,38,1,10,28,1696633,16,3,1696685,14,1582579,2,1584939,2, +28,1,6,1,10,28,34,63,38,3,26,1,6,1,10,28, +34,63,38,1,10,28,1,6,1,10,28,34,63,38,1,10, +28,1696713,14,1,1696721,16,1696731,14,1597071,12,8,1695911,14,1585091,1,10, +3,6,10,4,8,1,1696755,14,1,1310721,2,3,26,1310721,4,18, +1310721,2,9,28,1310721,4,1310721,2,1835009,40,1696803,16,1966081,1310721,4,1579069, +12,3,1310721,2,4,1,1310721,2,3,26,4,1585091,2,1696839,16,1695869, +12,1697853,14,1584937,2,1598649,12,1696909,16,1597071,12,8,1584901,2,1598801,12, +1696891,16,1582575,1,10,3,6,10,4,8,1,1697853,14,1310721,2,1, +10,3,6,10,4,8,1584939,2,1598649,12,1696949,16,1597071,12,8,1310721, +2,3,26,3,10,3,6,10,4,8,1584927,2,1598649,12,1697235,16, +1310721,2,5,26,1310721,2,2,1687483,12,10,3,6,10,4,8,1310721, +2,3,26,2,1697229,16,1584839,2,1697131,16,1697123,14,112,97,114,115, +101,32,119,111,114,100,115,32,99,97,110,39,116,32,98,101, +32,100,101,102,105,110,101,100,32,119,104,105,108,101,32,98, +117,105,108,100,105,110,103,32,97,110,32,105,109,97,103,101, +0,1697017,1585679,12,8,1310721,2,7,26,1409537,1310721,2,5,26,2,26, +2,11,1425921,1310721,2,5,26,2,26,2,1587311,12,10,3,6,10, +4,8,1442305,1310721,2,7,26,2,26,3,17,34,33,34,65,34, +10,3,6,10,4,8,1,1697853,14,1310721,2,2,1,6,1,10, +28,34,63,38,3,26,1697271,16,3,1697303,14,1310721,2,3,26,2, +1,6,1,10,28,34,63,38,1,10,28,1697349,16,1697335,14,101, +120,112,101,99,116,101,100,32,102,110,0,1697311,1586063,12,8,1, +1697853,14,1584929,2,1598649,12,1697371,16,1,1691797,12,1697853,14,1585031,2,1598649, +12,1697395,16,1638319,12,8,1,1697853,14,1585033,2,1598649,12,1697415,16,1640225, +12,1697853,14,1585035,2,1598649,12,1697435,16,1640345,12,1697853,14,1584965,2,1598649, +12,1697455,16,1693263,12,1697853,14,1584931,2,1598649,12,1697475,16,1693097,12,1697853, +14,1582577,2,5,28,1,6,1,10,28,34,63,38,3,26,1697705, +16,1310721,2,5,26,1582579,2,1587809,12,10,3,6,10,4,8,1310721, +2,5,26,2,1,40,3,36,1697623,16,1417729,1310721,2,5,26,2, +26,2,11,28,1,6,1,10,28,34,63,38,3,26,1,6, +1,10,28,34,63,38,1,10,28,1697625,14,1,1697675,16,1442305,1310721, +2,5,26,2,26,2,65,32,1,6,1,10,28,34,63,38, +1,10,28,1697677,14,1,1697705,16,1310721,2,5,26,2,1,1647877,12, +8,1,1697853,14,1637143,12,1697723,16,3,1691797,12,1697853,14,1697837,14,101, +120,112,101,99,116,101,100,32,102,110,44,32,108,101,116,44, +32,99,111,110,115,116,44,32,115,116,114,117,99,116,44,32, +116,121,112,101,32,111,114,32,97,32,100,101,99,108,97,114, +97,116,105,111,110,0,1697727,1586063,12,8,1,1697853,14,1,1310721,2, +9,26,1310721,4,18,1310721,2,1,28,1310721,4,1310721,2,1835009,40,1697901, +16,1966081,1310721,4,1579069,12,1582577,2,1,28,1,6,1,10,28,34, +63,38,1,10,28,1697945,16,1696769,12,8,1697901,14,1,1697953,14,1, +1310721,2,1,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,1835009, +40,1698001,16,1966081,1310721,4,1579069,12,1310721,2,4,1310721,2,2,1310721,2, +3,26,4,1585091,1,10,3,6,10,4,8,1310721,2,3,26,2, +1582507,2,40,1698371,16,1417729,1310721,2,3,26,2,26,2,3,28,1, +6,1,10,28,34,63,38,3,26,1698169,16,1442305,1310721,2,3,26, +2,26,2,3,32,1,6,1,10,28,34,63,38,3,26,1, +6,1,10,28,34,63,38,1,10,28,1698171,14,1,1698217,16,1450497, +1310721,2,3,26,2,26,2,1,6,1,10,28,34,63,38,1, +10,28,1698219,14,1,1698333,16,1582583,1425921,1310721,2,3,26,2,26,2, +10,3,6,10,4,8,1409537,1310721,2,3,26,2,26,2,1698325,14, +105,115,32,117,115,101,100,32,98,117,116,32,110,101,118,101, +114,32,100,101,102,105,110,101,100,0,1698273,1585849,12,8,1310721,2, +3,26,1,6,2,1,6,3,26,5,6,4,10,8,8,1698039, +14,1585089,2,1698385,16,1585397,12,8,1,1698393,14,1,1310721,2,5,26, +1310721,4,18,1310721,2,11,28,1310721,4,1310721,2,1835009,40,1698441,16,1966081, +1310721,4,1579069,12,1310721,2,3,26,4,1310721,2,4,3,1,10,28, +1310721,2,5,26,4,1,1310721,2,7,26,4,1582507,2,1310721,2,9, +26,4,1684867,12,8,1584865,2,1698547,16,1310721,2,5,26,1589535,12,10, +3,6,10,4,8,15,1589235,12,8,1310721,2,3,26,2,1698575,16, +3,1310721,2,2,1684251,12,8,3,1310721,2,2,1,1,1590943,12,8, +1597071,12,8,1310721,2,3,26,2,1698623,16,1697867,12,8,1698777,14,1310721, +2,7,26,1584837,2,10,3,6,10,4,8,3,1,1686347,12,8, +1686679,12,8,1582577,2,1,28,1,6,1,10,28,34,63,38,1, +10,28,1698707,16,1666771,12,8,1698663,14,1584847,2,1606191,12,8,1584853,2, +1698755,16,1584857,2,1,6,1,10,28,34,63,38,1,10,28,1698757, +14,1,1698771,16,3,3,1605463,12,8,1686903,12,8,1592371,12,8,1310721, +2,9,26,2,1697967,12,8,1310721,2,5,26,2,1,40,3,36, +1698841,16,1310721,2,5,26,2,1584837,2,1589317,12,8,1584837,2,1584843,2, +10,40,1698875,16,1584843,1584837,2,10,3,6,10,4,8,1685643,12,8, +1310721,2,7,26,2,1698897,14,1,1310721,2,11,26,1310721,4,18,1310721, +2,3,28,1310721,4,1310721,2,1835009,40,1698945,16,1966081,1310721,4,1579069,12, +1310721,2,4,1310721,2,2,1,1698407,12,1698969,14,1,1310721,2,3,26, +1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1699017,16,1966081, +1310721,4,1579069,12,1310721,2,4,1310721,2,2,3,1698407,12,8,1,1699045, +14,1,1310721,2,3,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721, +2,1835009,40,1699093,16,1966081,1310721,4,1579069,12,1310721,2,4,1310721,2,2, +1,1698407,12,1310721,2,3,26,4,1310721,2,3,26,2,1,6,12, +10,8,1699147,14,1,1310721,2,5,26,1310721,4,18,1310721,2,7,28, +1310721,4,1310721,2,1835009,40,1699195,16,1966081,1310721,4,1579069,12,1310721,2,4, +1310721,2,2,1680951,12,1310721,2,3,26,4,1310721,2,3,26,2,1699059, +12,1310721,2,5,26,4,1310721,2,3,26,2,1580623,12,8,1310721,2, +5,26,2,1699277,14,1,1310721,2,7,26,1310721,4,18,1310721,2,7, +28,1310721,4,1310721,2,1835009,40,1699325,16,1966081,1310721,4,1579069,12,1310721,2, +4,1310721,2,2,1310721,2,3,26,4,1310721,2,3,26,2,1582507,2, +40,1699745,16,1417729,1310721,2,3,26,2,26,2,3,40,3,36,1699447, +16,1417729,1310721,2,3,26,2,26,2,9,10,40,3,36,1,6, +1,10,28,34,63,38,1,10,28,1699449,14,1,1699459,16,3,1699521, +14,1417729,1310721,2,3,26,2,26,2,13,28,1,6,1,10,28, +34,63,38,3,26,1,6,1,10,28,34,63,38,1,10,28, +1699707,16,1417729,1310721,2,3,26,2,26,2,1588955,12,8,1425921,1310721,2, +3,26,2,26,2,1588955,12,8,1434113,1310721,2,3,26,2,26,2, +1588955,12,8,1310721,2,5,26,1409537,1310721,2,3,26,2,26,2,10, +3,6,10,4,8,1310721,2,5,26,2,2,1699699,16,1310721,2,5, +26,2,2,1588955,12,8,1310721,2,5,26,1,6,2,1,6,3, +26,5,6,4,10,8,8,1699627,14,1,1588955,12,8,1310721,2,3, +26,1,6,2,1,6,3,26,5,6,4,10,8,8,1699347,14, +1,1588955,12,8,1,1699761,14,1,1310721,2,7,26,1310721,4,18,1310721, +2,7,28,1310721,4,1310721,2,1835009,40,1699809,16,1966081,1310721,4,1579069,12, +1310721,2,4,1310721,2,2,2,1700157,16,1310721,2,5,26,1310721,2,2, +7,26,1586793,12,10,3,6,10,4,8,1310721,2,3,26,1310721,2, +5,26,2,1310721,2,2,1,26,2,1310721,2,2,3,26,2,1587311, +12,10,3,6,10,4,8,1434113,1310721,2,3,26,2,26,1310721,2, +2,5,26,2,10,3,6,10,4,8,1442305,1310721,2,3,26,2, +26,3,5,34,10,3,6,10,4,8,1310721,2,2,1,26,2, +13,28,1,6,1,10,28,34,63,38,3,26,1700107,16,1310721,2, +2,3,26,2,1582503,2,40,3,36,1700085,16,1582503,1310721,2,2,3, +26,2,3,26,10,3,6,10,4,8,1310721,2,2,5,26,2, +17,38,1572879,12,8,1310721,2,1310721,2,2,7,26,1310721,2,2,7, +26,1573885,12,26,3,26,10,3,6,10,4,8,1699815,14,1,1700165, +14,1,1310721,2,7,26,1310721,4,18,1310721,2,21,28,1310721,4,1310721, +2,1835009,40,1700213,16,1966081,1310721,4,1579069,12,1310721,2,3,26,4,1310721, +2,4,1584837,2,1310721,2,5,26,4,1584839,2,1310721,2,7,26,4, +1584841,2,1310721,2,9,26,4,1582509,2,1310721,2,11,26,4,1582511,2, +1310721,2,13,26,4,1582507,2,1310721,2,15,26,4,1582503,2,1310721,2, +17,26,4,1582509,1582507,2,10,3,6,10,4,8,1582511,1582507,2,10, +3,6,10,4,8,1582503,1,10,3,6,10,4,8,1584837,1310721,2, +2,10,3,6,10,4,8,1584839,1310721,2,3,26,2,1310721,2,2, +28,10,3,6,10,4,8,1584841,1310721,2,2,393217,26,10,3,6, +10,4,8,1966081,1589149,12,8,1310721,1589149,12,8,5,1589235,12,8,1584955, +2,1589617,12,1589741,12,8,15,1589235,12,8,1,1588955,12,8,1,1588955, +12,8,1684729,12,8,1310721,2,3,26,2,11,26,1584837,2,10,3, +6,10,4,8,1582509,2,1699291,12,8,1310721,2,3,26,2,13,26, +1584837,2,10,3,6,10,4,8,1310721,2,19,26,1584837,2,1310721,2, +2,28,10,3,6,10,4,8,1310721,2,15,26,2,1588157,12,8, +1582509,1310721,2,11,26,2,10,3,6,10,4,8,1582511,1310721,2,13, +26,2,10,3,6,10,4,8,1584837,1310721,2,5,26,2,10,3, +6,10,4,8,1584839,1310721,2,7,26,2,10,3,6,10,4,8, +1584841,1310721,2,9,26,2,10,3,6,10,4,8,1582503,1310721,2,17, +26,2,10,3,6,10,4,8,1310721,2,19,26,2,1700801,14,1, +1310721,2,21,26,1310721,4,18,1310721,2,1,28,1310721,4,1310721,2,1835009, +40,1700849,16,1966081,1310721,4,1579069,12,1,1584843,2,1581139,12,8,1584839,2, +1,6,1,10,28,34,63,38,3,26,1700927,16,1584837,2,1584843,2, +10,40,1,6,1,10,28,34,63,38,1,10,28,1700929,14,1, +1700947,16,1584843,2,1584837,2,1581139,12,8,1572865,1572865,13,26,2,1581139,12, +8,1540609,1540609,2049,26,1581139,12,8,1310721,2,1966081,1581139,12,8,2064385,2097121, +1581139,12,8,1,1701007,14,1,1310721,2,1,26,1310721,4,18,1310721,2, +1,28,1310721,4,1310721,2,1835009,40,1701055,16,1966081,1310721,4,1579069,12,1582505, +1327617,10,3,6,10,4,8,1311233,1,16385,1574549,12,8,1582507,1,10, +3,6,10,4,8,1582513,1,10,3,6,10,4,8,1582515,1,10, +3,6,10,4,8,1582517,1,10,3,6,10,4,8,1582519,1,10, +3,6,10,4,8,1582521,1,10,3,6,10,4,8,1582523,1,10, +3,6,10,4,8,1582525,1,10,3,6,10,4,8,1584867,1,10, +3,6,10,4,8,1584871,1,10,3,6,10,4,8,1584873,1,10, +3,6,10,4,8,1584845,1,10,3,6,10,4,8,1584847,1,10, +3,6,10,4,8,1584887,1701301,14,40,0,1701297,1586793,12,10,3,6, +10,4,8,1584889,1701329,14,41,0,1701325,1586793,12,10,3,6,10,4, +8,1584891,1701357,14,123,0,1701353,1586793,12,10,3,6,10,4,8,1584893, +1701385,14,125,0,1701381,1586793,12,10,3,6,10,4,8,1584895,1701413,14, +91,0,1701409,1586793,12,10,3,6,10,4,8,1584897,1701441,14,93,0, +1701437,1586793,12,10,3,6,10,4,8,1584899,1701469,14,44,0,1701465,1586793, +12,10,3,6,10,4,8,1584901,1701497,14,59,0,1701493,1586793,12,10, +3,6,10,4,8,1584903,1701525,14,61,0,1701521,1586793,12,10,3,6, +10,4,8,1584905,1701555,14,45,62,0,1701549,1586793,12,10,3,6,10, +4,8,1584907,1701585,14,43,43,0,1701579,1586793,12,10,3,6,10,4, +8,1584909,1701615,14,45,45,0,1701609,1586793,12,10,3,6,10,4,8, +1584911,1701643,14,42,0,1701639,1586793,12,10,3,6,10,4,8,1584913,1701671, +14,38,0,1701667,1586793,12,10,3,6,10,4,8,1584915,1701699,14,58, +0,1701695,1586793,12,10,3,6,10,4,8,1584917,1701727,14,35,0,1701723, +1586793,12,10,3,6,10,4,8,1584919,1701755,14,45,0,1701751,1586793,12, +10,3,6,10,4,8,1584925,1701783,14,46,0,1701779,1586793,12,10,3, +6,10,4,8,1584947,1701811,14,63,0,1701807,1586793,12,10,3,6,10, +4,8,1584949,1701845,14,99,97,115,101,0,1701835,1586793,12,10,3,6, +10,4,8,1584951,1701885,14,100,101,102,97,117,108,116,0,1701869,1586793, +12,10,3,6,10,4,8,1584921,1701915,14,38,38,0,1701909,1586793,12, +10,3,6,10,4,8,1584923,1701945,14,124,124,0,1701939,1586793,12,10, +3,6,10,4,8,1584927,1701975,14,102,110,0,1701969,1586793,12,10,3, +6,10,4,8,1584929,1702007,14,108,101,116,0,1701999,1586793,12,10,3, +6,10,4,8,1584931,1702043,14,99,111,110,115,116,0,1702031,1586793,12, +10,3,6,10,4,8,1584933,1702077,14,101,108,115,101,0,1702067,1586793, +12,10,3,6,10,4,8,1584935,1702107,14,97,115,0,1702101,1586793,12, +10,3,6,10,4,8,1584937,1702151,14,117,110,99,104,101,99,107, +101,100,0,1702131,1586793,12,10,3,6,10,4,8,1584939,1702195,14,105, +109,109,101,100,105,97,116,101,0,1702175,1586793,12,10,3,6,10, +4,8,1584941,1702223,14,109,0,1702219,1586793,12,10,3,6,10,4,8, +1584943,1702251,14,100,0,1702247,1586793,12,10,3,6,10,4,8,1584945,1702279, +14,117,0,1702275,1586793,12,10,3,6,10,4,8,1584955,1702315,14,115, +116,97,114,116,0,1702303,1586793,12,10,3,6,10,4,8,1584957,1702349, +14,109,97,105,110,0,1702339,1586793,12,10,3,6,10,4,8,1584959, +1702383,14,102,114,101,101,0,1702373,1586793,12,10,3,6,10,4,8, +1584961,1702415,14,111,119,110,0,1702407,1586793,12,10,3,6,10,4,8, +1584963,1702449,14,113,110,101,119,0,1702439,1586793,12,10,3,6,10,4, +8,1584953,1702503,14,102,114,97,109,101,95,111,118,101,114,102,108, +111,119,0,1702473,1586793,12,10,3,6,10,4,8,1584965,1702543,14,101, +110,104,97,110,99,101,0,1702527,1586793,12,10,3,6,10,4,8, +1584967,1702579,14,109,105,99,114,111,0,1702567,1586793,12,10,3,6,10, +4,8,1584969,1702609,14,118,109,0,1702603,1586793,12,10,3,6,10,4, +8,1,1702635,14,1,1310721,2,1,26,1310721,4,18,1310721,2,1,28, +1310721,4,1310721,2,1835009,40,1702683,16,1966081,1310721,4,1579069,12,1702691,14,33, +0,1702687,1702739,14,48,32,36,32,48,32,126,32,45,32,124,32, +51,49,32,62,32,49,32,43,0,1702697,1603187,12,8,1702757,14,33, +33,0,1702751,1702809,14,48,32,36,32,48,32,126,32,45,32,124, +32,51,49,32,62,32,48,32,126,32,45,0,1702763,1603187,12,8, +1702825,14,126,0,1702821,1702841,14,45,49,32,94,0,1702831,1603187,12,8, +1702857,14,45,0,1702853,1702875,14,48,32,126,32,45,0,1702863,1603187,12, +8,1702891,14,42,0,1702887,21,1702903,14,42,0,1702899,1602147,12,8,1702919, +14,47,0,1702915,21,1702937,14,105,100,105,118,0,1702927,1602147,12,8, +1702953,14,37,0,1702949,21,1702971,14,105,109,111,100,0,1702961,1602147,12, +8,1702987,14,43,0,1702983,19,1702999,14,43,0,1702995,1602147,12,8,1703015, +14,45,0,1703011,19,1703027,14,45,0,1703023,1602147,12,8,1703045,14,60, +60,0,1703039,17,1703063,14,105,115,104,108,0,1703053,1602147,12,8,1703081, +14,62,62,0,1703075,17,1703093,14,62,0,1703089,1602147,12,8,1703109,14, +60,0,1703105,15,1703121,14,60,0,1703117,1602147,12,8,1703137,14,62,0, +1703133,15,1703153,14,126,32,60,0,1703145,1602147,12,8,1703171,14,60,61, +0,1703165,15,1703195,14,126,32,60,32,49,32,94,0,1703179,1602147,12, +8,1703213,14,62,61,0,1703207,15,1703233,14,60,32,49,32,94,0, +1703221,1602147,12,8,1703251,14,61,61,0,1703245,13,1703305,14,45,32,48, +32,36,32,48,32,126,32,45,32,124,32,51,49,32,62,32, +49,32,43,0,1703259,1602147,12,8,1703323,14,33,61,0,1703317,13,1703381, +14,45,32,48,32,36,32,48,32,126,32,45,32,124,32,51, +49,32,62,32,48,32,126,32,45,0,1703331,1602147,12,8,1703397,14, +38,0,1703393,11,1703409,14,38,0,1703405,1602147,12,8,1703425,14,94,0, +1703421,9,1703437,14,94,0,1703433,1602147,12,8,1703453,14,124,0,1703449,7, +1703465,14,124,0,1703461,1602147,12,8,1703483,14,38,38,0,1703477,3,1, +10,28,1,1602147,12,8,1703511,14,124,124,0,1703505,5,1,10,28, +1,1602147,12,8,1584903,2,1601319,12,8,1584905,2,1601319,12,8,1584907,2, +1601319,12,8,1584909,2,1601319,12,8,1703581,14,108,101,116,0,1703573,1668993, +17,1603521,12,8,1703603,14,105,102,0,1703597,1669065,17,1603521,12,8,1703631, +14,119,104,105,108,101,0,1703619,1671631,17,1603521,12,8,1703655,14,102, +111,114,0,1703647,1672243,17,1603521,12,8,1703685,14,115,119,105,116,99, +104,0,1703671,1674813,17,1603521,12,8,1703713,14,98,114,101,97,107,0, +1703701,1673347,17,1603521,12,8,1703747,14,99,111,110,116,105,110,117,101, +0,1703729,1673773,17,1603521,12,8,1703777,14,114,101,116,117,114,110,0, +1703763,1676117,17,1603521,12,8,1703813,14,117,110,99,104,101,99,107,101, +100,0,1703793,1676737,17,1603521,12,8,1703837,14,97,115,109,0,1703829,1679427, +17,33,34,1603521,12,8,1703869,14,113,117,111,116,101,0,1703857,1680677, +33,1603521,12,8,1703899,14,115,105,122,101,111,102,0,1703885,1641141,33, +1603521,12,8,1703929,14,116,121,112,101,111,102,0,1703915,1641257,33,1603521, +12,8,1,1703949,14,1,1310721,2,1,26,1310721,4,18,1310721,2,5, +28,1310721,4,1310721,2,1835009,40,1703997,16,1966081,1310721,4,1579069,12,1577079,12, +8,1701021,12,8,1572865,11,26,2,1699775,12,8,1702649,12,8,1641369,12, +8,1584837,1,10,3,6,10,4,8,1584839,1,10,3,6,10,4, +8,1584841,524289,10,3,6,10,4,8,1584843,1,10,3,6,10,4, +8,1582509,1582507,2,10,3,6,10,4,8,1582511,1,10,3,6,10, +4,8,1684729,12,8,1584837,2,1584843,2,10,40,1704173,16,1584843,1584837,2, +10,3,6,10,4,8,1310721,2,1584957,2,1587809,12,10,3,6,10, +4,8,1310721,2,2,1,40,3,36,1704277,16,1417729,1310721,2,2,26, +2,3,28,1,6,1,10,28,34,63,38,3,26,1,6,1, +10,28,34,63,38,1,10,28,1704279,14,1,1704325,16,1442305,1310721,2, +2,26,2,3,32,1,6,1,10,28,34,63,38,1,10,28, +1704327,14,1,1704385,16,1310721,2,3,26,1425921,1310721,2,2,26,2,10, +3,6,10,4,8,1310721,2,3,26,2,1,6,12,10,8,8, +1573283,12,8,1,1704399,14,1,1310721,2,5,26,1310721,4,18,1,851981, +0,115,116,97,114,116,0,1,786439,2,105,110,115,116,97,108, +108,0,1,786550,2,112,117,116,99,0,1,786585,0,103,101,116, +99,0,1,786613,0,101,111,102,0,1,786641,0,104,97,108,116, +0,1,786670,2,112,117,116,115,0,1,786751,2,112,117,116,110, +0,1,786942,2,115,116,114,108,101,110,0,1,787014,4,115,116, +114,101,113,0,1,787168,6,119,99,111,112,121,0,1,787274,6, +119,102,105,108,108,0,1,787406,4,122,117,100,105,118,0,1, +787637,4,122,100,105,118,0,1,787825,4,122,109,111,100,0,1, +787920,4,122,115,104,108,0,1,788005,2,116,111,112,115,105,122, +101,0,1,788181,4,102,112,117,115,104,0,1,788305,4,102,117, +110,108,105,110,107,0,1,788418,4,109,107,98,108,111,99,107, +0,1,788539,0,104,101,97,112,95,105,110,105,116,0,1,788770, +2,98,97,108,108,111,99,0,1,789007,2,98,102,114,101,101, +0,1,789405,2,105,115,111,98,106,0,1,789534,0,102,114,97, +109,101,95,111,118,101,114,102,108,111,119,0,1,789597,0,111, +111,109,0,1,789658,5,110,101,119,95,116,0,1,789961,3,110, +101,119,0,1,789997,2,111,115,105,122,101,0,1,790058,2,116, +121,112,101,111,102,95,111,98,106,0,1,790117,2,114,101,108, +101,97,115,101,0,1,790311,130,102,114,101,101,0,1,790355,4, +111,119,110,0,1,790423,2,109,97,114,107,0,1,790569,4,109, +97,114,107,114,97,110,103,101,0,1,790640,0,100,114,97,105, +110,0,1,850407,0,103,99,95,114,111,111,116,115,0,1,790722, +0,103,99,0,1,791040,0,108,105,118,101,95,111,98,106,101, +99,116,115,0,1,791136,0,108,97,114,103,101,115,116,95,102, +114,101,101,0,1,792546,0,101,114,114,97,116,0,1,792618,0, +101,114,114,101,110,100,0,1,792698,0,103,105,118,101,117,112, +0,1,792792,2,102,97,116,97,108,0,1,792839,2,115,101,114, +114,0,1,792924,4,115,101,114,114,110,0,1,793031,2,100,105, +101,0,1,793098,4,100,105,101,110,0,1,793175,2,99,101,114, +114,0,1,793234,4,99,101,114,114,110,0,1,793306,2,104,97, +115,104,115,0,1,793396,2,105,110,116,101,114,110,0,1,793655, +6,97,100,100,115,121,109,0,1,793904,2,108,111,111,107,117, +112,0,1,794078,2,112,111,112,115,121,109,115,0,1,794155,2, +108,111,111,107,117,112,115,0,1,794192,4,102,105,120,110,101, +119,0,1,794368,4,102,105,120,97,108,108,0,1,794477,2,101, +109,105,116,0,1,794574,2,108,105,116,0,1,794617,2,111,112, +0,1,794658,4,115,101,116,108,105,116,0,1,794726,2,106,109, +112,0,1,794767,0,104,111,108,101,0,1,794808,2,102,119,100, +102,110,0,1,794870,2,102,110,114,101,102,0,1,794957,2,99, +97,108,108,110,97,109,101,0,1,795034,2,108,97,100,100,114, +0,1,795088,2,115,116,114,108,105,116,0,1,795194,0,110,101, +120,116,99,104,0,1,795471,8,115,114,99,95,112,117,115,104, +0,1,796185,0,115,114,99,95,112,111,112,0,1,796526,2,105, +115,111,112,99,104,0,1,796637,4,111,112,112,114,101,102,105, +120,0,1,796880,2,105,115,100,105,103,0,1,796941,2,105,115, +97,108,0,1,797080,2,104,101,120,118,0,1,797212,0,101,115, +99,99,104,0,1,797386,2,111,112,108,101,120,0,1,797531,0, +108,101,120,0,1,798535,0,110,101,120,116,0,1,798734,2,112, +101,101,107,97,116,0,1,798965,0,112,101,101,107,0,1,798995, +0,110,101,120,116,48,0,1,799248,2,105,115,95,111,112,0, +1,799324,2,105,115,95,105,100,0,1,799400,2,97,99,99,101, +112,116,0,1,799515,2,101,120,112,101,99,116,0,1,799660,0, +110,97,109,101,0,1,799737,2,115,101,113,97,115,109,0,1, +800306,2,109,110,101,109,105,100,120,0,1,800408,4,101,109,105, +116,115,101,113,0,1,800659,2,97,100,100,107,111,112,0,1, +800779,2,98,105,110,102,105,110,100,0,1,800877,2,117,110,102, +105,110,100,0,1,800975,2,97,115,103,102,105,110,100,0,1, +801073,6,97,100,100,95,98,105,110,111,112,0,1,801593,4,97, +100,100,95,117,110,111,112,0,1,801760,6,97,100,100,95,107, +119,0,1,801843,2,102,105,110,100,108,111,99,0,1,801945,2, +110,101,119,108,111,99,0,1,802176,2,108,114,111,111,116,0, +1,802245,2,116,114,97,99,107,101,100,0,1,802294,2,116,111, +117,99,104,0,1,802538,2,112,108,97,105,110,117,115,101,0, +1,802678,2,102,114,101,101,118,97,114,0,1,802731,4,101,120, +105,116,95,118,97,114,115,0,1,803095,2,117,110,117,115,101, +100,95,99,104,101,99,107,0,1,803244,2,98,108,111,99,107, +95,99,108,111,115,101,0,1,803380,0,115,110,97,112,95,112, +117,115,104,0,1,803655,2,115,110,97,112,95,112,111,112,0, +1,803696,2,115,110,97,112,95,114,101,115,116,111,114,101,0, +1,803915,2,101,109,112,116,121,0,1,803991,2,115,110,97,112, +95,99,109,112,0,1,804396,2,115,116,97,116,101,110,97,109, +101,0,1,804614,6,109,101,114,103,101,95,101,114,114,0,1, +804880,4,106,115,116,0,1,804955,4,106,111,119,0,1,805011,4, +106,104,109,0,1,805067,2,101,120,105,116,95,114,101,99,0, +1,805363,2,106,97,100,100,0,1,805467,6,106,115,97,109,101, +0,1,805727,4,106,111,105,110,0,1,806519,6,102,105,120,117, +112,115,0,1,806766,2,98,97,99,107,101,100,103,101,0,1, +807557,12,106,111,105,110,50,0,1,807844,4,114,101,99,97,115, +116,0,1,808380,4,97,114,103,99,104,101,99,107,0,1,809146, +2,101,109,105,116,111,119,110,0,1,809195,6,97,115,115,105, +103,110,99,104,101,99,107,0,1,810964,2,114,101,116,99,104, +101,99,107,0,1,811455,2,104,97,110,100,117,112,0,1,811813, +2,100,105,115,99,97,114,100,0,1,811936,2,116,97,108,108, +111,99,0,1,812043,8,116,110,101,119,0,1,812217,2,116,112, +116,114,0,1,812404,4,116,97,114,114,0,1,812664,8,116,102, +110,0,1,813125,4,116,118,97,114,0,1,813321,6,116,105,110, +115,116,0,1,813752,2,116,114,101,115,111,108,118,101,0,1, +814288,2,116,115,122,0,1,814374,4,115,117,98,115,116,0,1, +814873,2,105,115,110,117,109,0,1,814989,2,105,115,97,103,103, +0,1,815071,2,105,115,112,116,114,0,1,815153,2,100,101,99, +97,121,0,1,815216,2,116,101,108,101,109,0,1,815310,4,99, +111,109,112,97,116,0,1,815795,6,117,110,105,102,121,0,1, +816340,2,116,112,114,105,110,116,0,1,816962,6,116,99,101,114, +114,0,1,817162,6,116,99,110,101,101,100,0,1,817357,2,116, +112,102,105,110,100,0,1,817455,2,105,115,116,121,112,101,110, +97,109,101,0,1,817573,0,99,108,111,115,101,97,110,103,108, +101,0,1,817624,0,112,116,121,112,101,0,1,818571,0,116,115, +116,97,114,116,0,1,818972,0,100,116,121,112,101,0,1,819159, +0,107,119,95,115,116,114,117,99,116,0,1,820112,0,107,119, +95,116,121,112,101,0,1,820172,0,107,119,95,116,121,112,101, +99,104,101,99,107,0,1,820279,4,116,102,105,110,100,102,105, +101,108,100,0,1,820403,0,116,121,112,101,97,114,103,0,1, +820570,0,107,119,95,115,105,122,101,111,102,0,1,820628,0,107, +119,95,116,121,112,101,111,102,0,1,820684,0,116,121,112,101, +115,95,105,110,105,116,0,1,821266,2,105,115,110,117,109,111, +112,0,1,821359,6,98,105,110,116,121,112,101,0,1,822752,2, +116,107,105,110,100,0,1,822788,2,116,115,105,122,101,0,1, +822823,2,116,110,97,109,101,0,1,822859,2,116,108,101,110,0, +1,822895,2,116,110,102,105,101,108,100,115,0,1,822937,4,116, +102,110,97,109,101,0,1,822993,4,116,102,116,121,112,101,0, +1,823051,4,116,102,111,102,102,0,1,823109,2,116,114,101,116, +0,1,823145,2,116,110,112,97,114,97,109,115,0,1,823181,4, +116,112,97,114,97,109,0,1,823228,4,116,112,97,114,97,109, +116,97,103,0,1,823285,2,116,114,101,116,117,0,1,823323,2, +116,110,97,114,103,115,0,1,823418,4,116,97,114,103,0,1, +823468,2,115,105,103,111,102,0,1,823779,2,115,105,103,111,102, +115,0,1,823878,0,114,118,97,108,0,1,823938,4,107,119,99, +97,108,108,0,1,824084,0,115,116,109,116,111,110,108,121,0, +1,824162,6,97,114,103,115,0,1,824435,0,110,101,119,116,121, +112,101,100,0,1,824605,2,99,97,108,108,0,1,825172,2,101, +110,104,99,97,108,108,0,1,825541,0,112,114,105,109,97,114, +121,0,1,826961,4,105,110,99,100,101,99,0,1,827385,2,102, +105,101,108,100,0,1,828061,0,112,111,115,116,102,105,120,0, +1,829055,0,117,110,97,114,121,0,1,829894,2,98,105,110,0, +1,830399,0,115,116,111,114,101,107,101,101,112,0,1,830447,2, +116,101,114,110,97,114,121,0,1,831097,0,101,120,112,114,0, +1,831870,0,115,116,109,116,115,116,97,114,116,0,1,832117,0, +98,108,111,99,107,101,120,112,114,0,1,832493,0,99,112,114, +105,109,0,1,832901,0,99,101,120,112,114,0,1,833258,0,114, +101,115,121,110,99,0,1,833385,0,115,116,109,116,0,1,833653, +0,98,108,111,99,107,0,1,833781,0,115,117,98,115,116,109, +116,0,1,833861,2,108,111,99,97,108,100,101,99,108,0,1, +834496,0,107,119,95,118,97,114,0,1,834532,0,107,119,95,105, +102,0,1,835078,4,108,111,111,112,95,112,117,115,104,0,1, +835285,4,108,111,111,112,95,101,110,100,0,1,835815,0,107,119, +95,119,104,105,108,101,0,1,836121,0,107,119,95,102,111,114, +0,1,836673,0,107,119,95,98,114,101,97,107,0,1,836886,0, +107,119,95,99,111,110,116,105,110,117,101,0,1,837173,4,99, +97,115,101,98,111,100,121,0,1,837406,0,107,119,95,115,119, +105,116,99,104,0,1,838058,0,107,119,95,114,101,116,117,114, +110,0,1,838368,0,107,119,95,117,110,99,104,101,99,107,101, +100,0,1,838428,0,97,115,109,98,108,111,99,107,0,1,839713, +0,107,119,95,97,115,109,0,1,839755,1,103,114,97,98,0, +1,840095,3,113,110,101,119,0,1,840173,67,113,100,117,112,0, +1,840251,68,119,99,111,112,121,116,111,0,1,840338,0,107,119, +95,113,117,111,116,101,0,1,840475,3,116,111,107,101,110,115, +0,1,840715,130,102,101,101,100,0,1,840758,0,114,101,97,100, +115,114,99,0,1,841180,0,112,115,107,105,112,97,110,103,108, +101,0,1,841312,0,112,114,101,115,99,97,110,95,102,110,0, +1,842125,4,112,114,101,115,99,97,110,0,1,842364,0,117,110, +105,116,95,115,116,100,105,110,0,1,842433,0,99,116,120,95, +112,117,115,104,0,1,842821,0,99,116,120,95,112,111,112,0, +1,843173,4,102,110,95,98,101,103,105,110,0,1,843339,0,112, +114,111,108,111,103,117,101,0,1,843451,0,101,112,105,108,111, +103,117,101,0,1,843543,0,98,111,100,121,0,1,843741,2,102, +110,100,101,102,0,1,845898,2,103,100,101,99,108,0,1,846548, +0,103,99,111,110,115,116,0,1,846631,0,101,110,104,100,101, +102,0,1,847934,0,116,111,112,115,121,110,99,0,1,848384,0, +116,111,112,105,116,101,109,0,1,848933,0,117,110,105,116,95, +116,111,112,0,1,848983,2,117,110,105,116,95,101,110,100,0, +1,849203,4,99,111,109,112,105,108,101,95,108,105,115,116,0, +1,849455,2,99,111,109,112,105,108,101,0,1,849491,2,108,111, +97,100,0,1,849529,2,101,118,97,108,0,1,849580,2,101,118, +97,108,115,0,1,849645,2,98,108,111,98,95,101,109,105,116, +0,1,849887,2,98,108,111,98,95,108,111,97,100,0,1,850089, +4,98,117,105,108,100,95,105,109,97,103,101,0,1,850510,0, +122,99,95,105,110,105,116,0,1,851324,0,122,99,95,116,97, +98,108,101,115,0,4,1048576,0,77,87,0,4,262144,0,72,69, +65,80,95,76,79,0,4,655360,0,72,69,65,80,95,72,73, +0,4,655360,0,90,70,80,0,4,786432,0,71,79,82,71,0, +4,917504,0,70,66,79,84,0,4,983040,0,70,84,79,80,0, +4,983040,0,82,83,48,0,4,1015808,0,77,65,82,75,48,0, +4,16384,0,77,65,82,75,78,0,4,1032192,0,68,83,76,79, +0,4,1048560,0,68,83,72,73,0,4,729683222,0,77,65,71,73, +67,0,6,0,201337106,105,100,105,118,0,6,1,201343762,105,109,111, +100,0,6,2,201350418,105,115,104,108,0,3,787373,0,112,111,119, +50,0,2,787405,0,122,114,101,109,0,4,6,0,72,68,82, +0,2,787969,0,104,98,117,109,112,0,3,787970,0,102,114,101, +101,108,0,2,788002,0,103,99,95,114,117,110,115,0,2,788003, +0,103,99,95,102,114,101,101,100,0,2,788004,0,103,99,95, +97,117,116,111,0,2,789006,0,102,101,110,100,0,2,790419,0, +109,115,95,116,111,112,0,2,790420,0,109,115,95,111,118,101, +114,0,2,790421,0,103,99,95,108,111,0,2,790422,0,103,99, +95,104,105,0,4,8192,0,73,72,78,0,4,655616,0,82,95, +73,72,0,4,663808,0,82,95,80,79,79,76,0,4,704512,0, +82,95,80,79,79,76,69,78,68,0,4,4096,0,78,83,0, +4,704768,0,82,95,83,89,77,0,4,704768,0,115,110,97,109, +101,0,4,708864,0,115,107,105,110,100,0,4,712960,0,115,118, +97,108,0,4,717056,0,115,115,105,103,0,4,721152,0,115,102, +108,97,103,0,4,725248,0,115,102,105,120,0,4,729344,0,115, +110,101,120,116,0,4,512,0,78,76,0,4,733440,0,108,110, +97,109,101,0,4,733952,0,108,115,108,111,116,0,4,734464,0, +108,104,111,109,101,0,4,734976,0,108,115,116,97,116,101,0, +4,735488,0,108,111,119,110,101,114,0,4,736000,0,108,117,115, +101,100,0,4,736512,0,108,97,114,114,0,4,737024,0,108,111, +119,110,0,4,8192,0,78,70,0,4,737536,0,102,115,105,116, +101,0,4,745728,0,102,110,101,120,116,0,4,753920,0,82,95, +83,78,65,80,0,4,762112,0,82,95,81,66,85,70,0,4, +770304,0,82,95,83,82,67,0,4,771328,0,82,95,83,69,81, +0,4,773376,0,98,110,97,109,101,0,4,773440,0,98,112,114, +101,99,0,4,773504,0,98,115,101,113,0,4,773568,0,98,108, +101,110,0,4,773632,0,117,110,97,109,101,0,4,773664,0,117, +115,101,113,0,4,773696,0,117,108,101,110,0,4,773728,0,97, +110,97,109,101,0,4,773792,0,97,98,105,110,0,4,773888,0, +107,111,112,115,0,4,774400,0,82,95,67,84,88,0,4,776448, +0,82,95,76,79,79,80,0,4,777472,0,82,95,65,83,77, +0,4,0,0,84,95,69,79,70,0,4,1,0,84,95,78, +85,77,0,4,2,0,84,95,73,68,0,4,3,0,84,95, +83,84,82,0,4,4,0,84,95,79,80,0,4,1,0,75, +95,70,78,0,4,2,0,75,95,71,86,65,82,0,4,3, +0,75,95,71,65,82,82,0,4,4,0,75,95,67,79,78, +83,84,0,4,5,0,75,95,75,87,0,4,6,0,75,95, +69,78,72,0,4,7,0,75,95,84,89,80,69,0,4,1, +0,70,95,68,69,70,0,4,2,0,70,95,83,73,71,0, +4,4,0,70,95,85,78,84,89,80,69,68,0,4,8,0, +70,95,83,84,77,84,0,4,16,0,70,95,69,88,80,82, +0,4,32,0,70,95,84,79,80,0,4,64,0,70,95,70, +65,76,76,66,65,67,75,0,4,128,0,70,95,71,69,78, +0,4,256,0,70,95,80,82,69,0,4,0,0,83,95,80, +76,65,73,78,0,4,1,0,83,95,77,0,4,2,0,83, +95,85,0,4,3,0,83,95,68,0,4,4,0,83,95,68, +69,65,68,0,4,5,0,83,95,67,72,73,76,68,0,4, +6,0,83,95,66,79,82,82,79,87,0,4,0,0,67,95, +80,76,65,73,78,0,4,1,0,67,95,70,82,69,83,72, +0,4,2,0,67,95,86,65,82,0,4,0,0,76,86,95, +78,79,78,69,0,4,1,0,76,86,95,76,79,67,65,76, +0,4,2,0,76,86,95,71,76,79,66,65,76,0,4,3, +0,76,86,95,69,76,69,77,0,4,4,0,76,86,95,79, +84,72,69,82,0,4,0,0,79,95,81,0,4,1,0,79, +95,76,68,0,4,2,0,79,95,83,84,0,4,3,0,79, +95,80,73,67,75,0,4,4,0,79,95,68,82,79,80,0, +4,5,0,79,95,83,87,65,80,0,4,6,0,79,95,67, +65,76,76,0,4,7,0,79,95,74,77,80,0,4,8,0, +79,95,74,90,0,4,9,0,79,95,82,69,84,0,4,13, +0,79,95,65,68,68,0,4,14,0,79,95,83,85,66,0, +4,20,0,79,95,76,84,0,4,22,0,88,79,80,48,0, +4,-2,0,83,69,81,82,69,70,0,2,791251,0,110,101,110, +104,0,2,791252,0,112,111,111,108,116,111,112,0,2,791253,0, +110,115,121,109,0,2,791254,0,117,98,97,115,101,0,2,791255, +0,118,105,115,108,111,0,2,791256,0,110,102,105,120,117,0, +2,791257,0,102,105,120,102,114,101,101,0,2,791258,0,110,98, +105,110,0,2,791259,0,110,117,110,0,2,791260,0,110,97,115, +103,0,2,791261,0,110,107,111,112,0,2,791262,0,115,101,113, +116,111,112,0,2,791263,0,109,110,101,109,0,2,791287,0,99, +104,101,99,107,95,111,110,0,2,791288,0,116,107,0,2,791289, +0,116,118,0,2,791290,0,116,103,108,117,101,0,2,791291,0, +116,108,105,110,101,0,2,791292,0,108,105,110,101,0,2,791293, +0,99,104,0,2,791294,0,99,104,107,105,110,100,0,2,791295, +0,99,104,112,116,114,0,2,791296,0,99,104,98,97,115,101, +0,2,791297,0,113,100,111,110,101,0,2,791298,0,105,115,112, +0,3,791299,0,116,98,117,102,0,2,792399,0,112,107,95,116, +107,0,2,792400,0,112,107,95,116,118,0,2,792401,0,112,113, +95,110,0,3,792402,0,112,113,95,116,107,0,3,792406,0,112, +113,95,116,118,0,3,792410,0,112,113,95,103,108,0,3,792414, +0,112,113,95,108,110,0,2,792418,0,104,101,114,101,0,2, +792419,0,104,100,101,108,116,97,0,2,792420,0,104,108,105,109, +105,116,0,2,792421,0,112,116,111,112,0,2,792422,0,110,108, +111,99,0,2,792423,0,108,98,97,115,101,0,2,792424,0,102, +100,101,112,116,104,0,2,792425,0,102,115,108,111,116,115,0, +2,792426,0,102,99,104,107,0,2,792427,0,102,114,101,116,0, +2,792428,0,108,105,118,101,0,2,792429,0,98,120,100,101,112, +116,104,0,2,792430,0,101,112,105,99,104,97,105,110,0,2, +792431,0,102,102,114,97,109,101,0,2,792432,0,105,110,102,110, +0,2,792433,0,108,115,112,0,2,792434,0,108,111,111,112,98, +97,115,101,0,2,792435,0,99,116,120,115,112,0,2,792436,0, +115,110,97,112,116,111,112,0,2,792437,0,108,118,0,2,792438, +0,108,118,107,0,2,792439,0,108,118,105,0,2,792440,0,107, +119,95,101,120,112,114,0,2,792441,0,120,99,108,115,0,2, +792442,0,102,116,121,0,2,792443,0,115,95,108,112,0,2,792444, +0,115,95,114,112,0,2,792445,0,115,95,108,98,0,2,792446, +0,115,95,114,98,0,2,792447,0,115,95,108,107,0,2,792448, +0,115,95,114,107,0,2,792449,0,115,95,99,111,109,109,97, +0,2,792450,0,115,95,115,101,109,105,0,2,792451,0,115,95, +101,113,0,2,792452,0,115,95,97,114,114,111,119,0,2,792453, +0,115,95,105,110,99,0,2,792454,0,115,95,100,101,99,0, +2,792455,0,115,95,115,116,97,114,0,2,792456,0,115,95,97, +109,112,0,2,792457,0,115,95,99,111,108,111,110,0,2,792458, +0,115,95,104,97,115,104,0,2,792459,0,115,95,109,105,110, +117,115,0,2,792460,0,115,95,97,110,100,97,110,100,0,2, +792461,0,115,95,111,114,111,114,0,2,792462,0,115,95,100,111, +116,0,2,792463,0,115,95,102,110,0,2,792464,0,115,95,118, +97,114,0,2,792465,0,115,95,99,111,110,115,116,0,2,792466, +0,115,95,101,108,115,101,0,2,792467,0,115,95,97,115,0, +2,792468,0,115,95,117,110,99,104,101,99,107,101,100,0,2, +792469,0,115,95,105,109,109,101,100,105,97,116,101,0,2,792470, +0,115,95,109,0,2,792471,0,115,95,100,0,2,792472,0,115, +95,117,0,2,792473,0,115,95,113,0,2,792474,0,115,95,99, +97,115,101,0,2,792475,0,115,95,100,101,102,97,117,108,116, +0,2,792476,0,115,95,102,111,118,102,0,2,792477,0,115,95, +115,116,97,114,116,0,2,792478,0,115,95,109,97,105,110,0, +2,792479,0,115,95,102,114,101,101,0,2,792480,0,115,95,111, +119,110,0,2,792481,0,115,95,113,110,101,119,0,2,792482,0, +115,95,101,110,104,97,110,99,101,0,2,792483,0,115,95,109, +105,99,114,111,0,2,792484,0,115,95,118,109,0,4,0,0, +84,89,95,87,79,82,68,0,4,1,0,84,89,95,73,78, +84,0,4,2,0,84,89,95,67,72,65,82,0,4,3,0, +84,89,95,66,79,79,76,0,4,4,0,84,89,95,86,79, +73,68,0,4,5,0,84,89,95,80,84,82,0,4,6,0, +84,89,95,65,82,82,0,4,7,0,84,89,95,70,78,0, +4,8,0,84,89,95,83,84,82,85,67,84,0,4,9,0, +84,89,95,86,65,82,0,4,10,0,84,89,95,71,69,78, +0,4,638976,0,82,95,84,89,0,4,655360,0,82,95,84,89, +69,78,68,0,4,1024,0,78,84,76,0,4,778496,0,108,116, +121,112,101,0,4,779008,0,108,116,97,103,0,4,779520,0,115, +116,121,112,101,0,2,792485,0,116,121,116,111,112,0,2,792486, +0,110,116,108,0,2,792487,0,116,119,111,114,100,0,2,792488, +0,116,105,110,116,0,2,792489,0,116,99,104,97,114,0,2, +792490,0,116,98,111,111,108,0,2,792491,0,116,118,111,105,100, +0,2,792492,0,116,99,95,111,110,0,2,792493,0,120,116,121, +0,2,792494,0,120,122,101,114,111,0,2,792495,0,100,114,121, +0,3,792496,0,116,112,110,0,2,792504,0,110,116,112,0,2, +792505,0,100,116,121,0,2,792506,0,100,116,97,103,0,2,792507, +0,115,95,108,116,0,2,792508,0,115,95,103,116,0,2,792509, +0,115,95,115,104,114,0,2,792510,0,115,95,108,101,0,2, +792511,0,115,95,103,101,0,2,792512,0,115,95,101,113,101,113, +0,2,792513,0,115,95,110,101,0,2,792514,0,115,95,112,108, +117,115,0,2,792515,0,115,95,115,116,114,117,99,116,0,2, +792516,0,115,95,116,121,112,101,0,2,792517,0,115,95,116,121, +112,101,99,104,101,99,107,0,2,792518,0,115,95,111,110,0, +2,792519,0,115,95,111,102,102,0,2,792520,0,115,95,110,101, +119,0,2,792521,0,115,95,110,101,119,95,116,0,3,792522,0, +110,117,109,111,112,115,0,2,792534,0,110,110,117,109,111,112, +115,0,3,792535,0,103,98,105,110,100,0,2,792543,0,110,103, +98,105,110,100,0,2,792544,0,110,101,114,114,0,2,792545,0, +112,97,110,105,99,0,4,783616,0,82,95,69,88,73,84,0, +4,786176,0,82,95,69,88,73,84,69,78,68,0,2,804814,0, +101,120,116,111,112,0,3,804815,0,106,112,115,0,2,804879,0, +110,106,112,115,0,0 +}; +#define GLEN 71014 diff --git a/docs/history/13-gap-fixes/code/io_linux.c b/docs/history/13-gap-fixes/code/io_linux.c new file mode 100644 index 0000000..7ea34e0 --- /dev/null +++ b/docs/history/13-gap-fixes/code/io_linux.c @@ -0,0 +1,6 @@ +#include +int J; +static int pk(void){int c=getchar();ungetc(c,stdin);return c==EOF;} +char Q(void){int c=getchar();J=pk();return c;} +void G(char c){putchar(c);} +__attribute__((constructor)) static void init(void){J=pk();} diff --git a/docs/history/13-gap-fixes/code/mkgenome.c b/docs/history/13-gap-fixes/code/mkgenome.c new file mode 100644 index 0000000..8fb2cc5 --- /dev/null +++ b/docs/history/13-gap-fixes/code/mkgenome.c @@ -0,0 +1,16 @@ +/* turns a genome image (little-endian 32-bit words on stdin, as written by + tools/build.zc) into genome.h */ +#include +#define GORG 0xC0000 +int main(void) +{ + unsigned char b[4]; int n = 0; + printf("#define GORG %d\nstatic const int genome[] = {", GORG); + while (fread(b, 1, 4, stdin) == 4) { + int w = (int)(b[0] | b[1] << 8 | b[2] << 16 | (unsigned)b[3] << 24); + printf("%s%d", !n ? "\n" : n % 16 ? "," : ",\n", w); + n++; + } + printf("\n};\n#define GLEN %d\n", n); + return n ? 0 : 1; +} diff --git a/docs/history/13-gap-fixes/code/src/compiler.zc b/docs/history/13-gap-fixes/code/src/compiler.zc new file mode 100644 index 0000000..c6fa3cc --- /dev/null +++ b/docs/history/13-gap-fixes/code/src/compiler.zc @@ -0,0 +1,2638 @@ +// ============================================================================ +// compiler.zc: the resident zc compiler. it lives in the genome next to the +// runtime, compiles source straight to vm words in memory, and is visible to +// the programs it compiles: every function and global here can be called or +// patched by name, and parse words (immediate fns) run inside it. +// ============================================================================ + +// ---- compiler data region (fixed addresses, 0xA0100 .. 0xBE100) ---- +const IHN = 8192; +const R_IH = 0xA0100; // intern hash table +const R_POOL = 0xA2100; // interned strings: [symbol head] chars 0 +const R_POOLEND = 0xAC000; +const NS = 4096; // symbol table, one array per field +const R_SYM = 0xAC100; +const sname = 0xAC100, skind = 0xAD100, sval = 0xAE100, ssig = 0xAF100; +const sflag = 0xB0100, sfix = 0xB1100, snext = 0xB2100; +const NL = 512; // locals, one array per field +const lname = 0xB3100, lslot = 0xB3300, lhome = 0xB3500, lstate = 0xB3700; +const lowner = 0xB3900, lused = 0xB3B00, larr = 0xB3D00, lown = 0xB3F00; // lown: has owned an object +const NF = 8192; // patch nodes: a site and a link +const fsite = 0xB4100, fnext = 0xB6100; +const R_SNAP = 0xB8100; // analyzer snapshots +const R_QBUF = 0xBA100; // token buffer for quote and grab +const R_SRC = 0xBC100; // input source stack, 32 words per frame +const R_SEQ = 0xBC500; // operator code sequences +const bname = 0xBCD00, bprec = 0xBCD40, bseq = 0xBCD80, blen = 0xBCDC0; // binary operators +const uname = 0xBCE00, useq = 0xBCE20, ulen = 0xBCE40; // unary operators +const aname = 0xBCE60, abin = 0xBCEA0; // compound assignment +const kops = 0xBCF00; // every operator spelling the lexer knows +const R_CTX = 0xBD100; // saved compiler contexts for nested compiles +const R_LOOP = 0xBD900; // loop stack, 8 words per loop or switch +const R_ASM = 0xBDD00; // asm block labels + +// ---- token kinds, symbol kinds and flags ---- +const T_EOF = 0, T_NUM = 1, T_ID = 2, T_STR = 3, T_OP = 4; +const K_FN = 1, K_GVAR = 2, K_GARR = 3, K_CONST = 4, K_KW = 5, K_ENH = 6, K_TYPE = 7; +const F_DEF = 1, F_SIG = 2, F_UNTYPED = 4, F_STMT = 8, F_EXPR = 16, F_TOP = 32, F_FALLBACK = 64, F_GEN = 128, F_PRE = 256; +// variable states for the lifetime analyzer +const S_PLAIN = 0, S_M = 1, S_U = 2, S_D = 3, S_DEAD = 4, S_CHILD = 5, S_BORROW = 6; +// expression classes: plain word, a new object, or a bare variable (C_VAR + index) +const C_PLAIN = 0, C_FRESH = 1, C_VAR = 2; +// lvalue kinds +const LV_NONE = 0, LV_LOCAL = 1, LV_GLOBAL = 2, LV_ELEM = 3, LV_OTHER = 4; +// opcodes +const O_Q = 0, O_LD = 1, O_ST = 2, O_PICK = 3, O_DROP = 4, O_SWAP = 5, O_CALL = 6, O_JMP = 7; +const O_JZ = 8, O_RET = 9, O_ADD = 13, O_SUB = 14, O_LT = 20; +const XOP0 = 22; // first enhancement opcode +const SEQREF = -2; // in an operator sequence: the next word names a symbol + +// ---- state ---- +let nenh, pooltop, nsym, ubase, vislo, nfixu, fixfree; +let nbin, nun, nasg, nkop, seqtop; +let mnem = "q@!$_~cjzr,?.+-*&|^><%"; +let check_on = 1; // set to 0 to switch the analyzer off for what follows +let tk, tv, tglue, tline, line = 1, ch, chkind, chptr, chbase, qdone, isp = -1; +let tbuf[1100]; +let pk_tk, pk_tv, pq_n, pq_tk[4], pq_tv[4], pq_gl[4], pq_ln[4]; // up to 4 tokens of lookahead +let here, hdelta, hlimit, ptop; +let nloc, lbase, fdepth, fslots, fchk, fret, live, bxdepth, epichain, fframe, infn; +let lsp, loopbase, ctxsp, snaptop; +let lv, lvk, lvi, kw_expr, xcls, fty; +// interned spellings the parser compares against +let s_lp, s_rp, s_lb, s_rb, s_lk, s_rk, s_comma, s_semi, s_eq, s_arrow, s_inc, s_dec; +let s_star, s_amp, s_colon, s_hash, s_minus, s_andand, s_oror; +let s_dot, s_fn, s_var, s_const, s_else, s_as, s_unchecked, s_immediate, s_m, s_d, s_u; +let s_q, s_case, s_default, s_fovf, s_start, s_main, s_free, s_own, s_qnew, s_enhance, s_micro, s_vm; + +// ---- types: kinds, tables and parser state (see the types section) ---- +const TY_WORD = 0, TY_INT = 1, TY_CHAR = 2, TY_BOOL = 3, TY_VOID = 4, TY_PTR = 5, TY_ARR = 6; +const TY_FN = 7, TY_STRUCT = 8, TY_VAR = 9, TY_GEN = 10; +const R_TY = 0x9C000, R_TYEND = 0xA0000, NTL = 1024; // the first NTL words list every record +const ltype = 0xBE100, ltag = 0xBE300, stype = 0xBE500; // type per local, declared tag per local, type per symbol +let tytop, ntl, tword, tint, tchar, tbool, tvoid; +let tc_on; // the optional typechecker, off unless a program says typecheck on; +let xty, xzero, dry; // type of the last expression, was it a literal 0, and dry runs (typeof) +let tpn[8], ntp; // type parameters in scope +let dty, dtag; // result of dtype() +let s_lt, s_gt, s_shr, s_le, s_ge, s_eqeq, s_ne, s_plus, s_struct, s_type, s_typecheck, s_on, s_off, s_new, s_new_t; +let numops[12], nnumops; +let gbind[8], ngbind; // explicit type arguments for the next call: f(...) + +// ---- errors ---- +// the compiler reports up to 20 errors before it stops. a syntax error +// (die) skips the token it happened on and sets panic, which skips ahead to +// the next ; or } (or the next top-level item) and keeps quiet until then. +// other errors (serr) just report: the parse is still in step. +let nerr, panic; +fn errat() { puts("zc: line "); putn(tline); puts(": "); nerr++; return 0; } +fn errend() { putc(10); if (nerr >= 20) { puts("zc: too many errors, stopping\n"); halt(); } return 0; } +fn giveup() { puts("zc: "); putn(nerr); if (nerr == 1) puts(" error\n"); else puts(" errors\n"); halt(); return 0; } +fn fatal(msg) { errat(); puts(msg); putc(10); halt(); return 0; } +fn serr(msg) { if (!panic && !dry) { errat(); puts(msg); errend(); } return 0; } +fn serrn(name, msg) { if (!panic && !dry) { errat(); puts(name); puts(": "); puts(msg); errend(); } return 0; } +fn die(msg) { serr(msg); panic = 1; if (tk == T_EOF) giveup(); next(); return 0; } +fn dien(name, msg) { serrn(name, msg); panic = 1; if (tk == T_EOF) giveup(); next(); return 0; } +// analyzer errors only count in checked, reachable code +fn cerr(msg) { if (fchk && live) serr(msg); return 0; } +fn cerrn(i, msg) { if (fchk && live) serrn(lname[i], msg); return 0; } + +// ---- interning ---- +fn hashs(s) { let h = 0; while (*s) { h = h * 31 + *s; s++; } return h & (IHN - 1); } +fn intern(s) +{ + let h = hashs(s), p, n; + while (1) { + p = R_IH[h]; + if (!p) break; + if (streq(p, s)) return p; + h = (h + 1) & (IHN - 1); + } + n = strlen(s); + if (pooltop + n + 2 >= R_POOLEND) fatal("string pool full"); + pooltop[0] = 0; + p = pooltop + 1; + wcopy(p, s, n + 1); + pooltop = pooltop + n + 2; + R_IH[h] = p; + return p; +} + +// ---- symbols ---- +// each interned name keeps the index (+1) of its newest symbol in p[-1], and +// each symbol links to the one it shadows, so lookup is one step. +fn addsym(name, kind, val) +{ + let i = nsym; + if (nsym >= NS) fatal("symbol table full"); + nsym++; + sname[i] = name; skind[i] = kind; sval[i] = val; ssig[i] = 0; sflag[i] = F_DEF; sfix[i] = 0; stype[i] = 0; + snext[i] = name[-1]; + name[-1] = i + 1; + return i; +} +// while an image builds, names below vislo are hidden, except keywords +fn lookup(name) +{ + let i = name[-1] - 1; + while (i >= 0) { + if (i >= vislo || skind[i] == K_KW || skind[i] == K_TYPE) return i; + i = snext[i] - 1; + } + return -1; +} +fn popsyms(to) { while (nsym > to) { nsym--; sname[nsym][-1] = snext[nsym]; } return 0; } +fn lookups(s) { return lookup(intern(s)); } + +// patch nodes +fn fixnew(site, link) +{ + let i = fixfree; + if (i) fixfree = fnext[i]; + else { nfixu++; i = nfixu; if (i >= NF) fatal("too many forward references"); } + fsite[i] = site; fnext[i] = link; + return i; +} +// patch every lit in a chain to point at v, and recycle the nodes +fn fixall(head, v) +{ + let n; + while (head) { setlit(fsite[head], v); n = fnext[head]; fnext[head] = fixfree; fixfree = head; head = n; } + return 0; +} + +// ---- code emission ---- +fn emit(w) { if (dry) return 0; if (here >= hlimit) fatal("program too big"); *(here + hdelta) = w; here++; return 0; } +fn lit(v) { emit(v + v + 1); return 0; } +fn op(k) { emit(k + k); return 0; } +fn setlit(at, v) { if (dry) return 0; *(at + hdelta) = v + v + 1; return 0; } +fn jmp(t) { lit(t); op(O_JMP); return 0; } +// a lit that will be patched later; returns its address +fn hole() { let a = here; lit(MW - 1); return a; } +fn fwdfn(name) { let s = addsym(name, K_FN, tline); sflag[s] = 0; return s; } // the line of the first use, until it's defined +fn fnref(s) +{ + if (sflag[s] & F_DEF) { lit(sval[s]); return 0; } + if (dry) return 0; + sfix[s] = fixnew(here, sfix[s]); + lit(MW - 1); // a jump to MW-1 halts if it ever runs unpatched + return 0; +} +fn callname(name) { let s = lookup(name); if (s < 0) s = fwdfn(name); fnref(s); op(O_CALL); return 0; } +fn laddr(k) { lit(ZFP); op(O_LD); if (k) { lit(k); op(O_ADD); } return 0; } +fn strlit(s) +{ + let j = hole(), a; + op(O_JMP); + a = here; + while (*s) { emit(*s); s++; } + emit(0); + setlit(j, here); + lit(a); + return 0; +} + +// ---- input sources ---- +// frame: 0 kind (0 stdin, 1 token list, 2 string) 1 object 2 index 3 end +// 4 transparent 5..7 saved token 8..10 saved char state 11 owned 12 saved line +// 13 saved token line 14 saved lookahead count 15..30 saved lookahead 31 saved char buffer +fn nextch() +{ + if (chkind == 0) { + if (qdone || eof()) { ch = -1; return 0; } + ch = getc() & 255; + if (ch == 96) { qdone = 1; ch = -1; return 0; } // a backtick ends the source + } else if (chkind == 3) { + ch = (chbase[chptr >> 2] >> ((chptr & 3) * 8)) & 255; + if (ch) chptr++; else ch = -1; + } else { + ch = *chptr & 255; + if (ch) chptr++; else ch = -1; + } + if (ch == 10) line++; + return 0; +} +fn src_push(kind, obj, trans, owned) +{ + let f, i; + isp++; + if (isp >= 32) fatal("sources nested too deeply"); + f = R_SRC + isp * 32; + f[0] = kind; f[1] = obj; f[2] = 0; f[3] = 0; f[4] = trans; f[11] = owned; + f[5] = tk; f[6] = tv; f[7] = tglue; f[8] = ch; f[9] = chkind; f[10] = chptr; + f[12] = line; f[13] = tline; f[14] = pq_n; f[31] = chbase; + i = 0; while (i < pq_n) { f[15 + i * 4] = pq_tk[i]; f[16 + i * 4] = pq_tv[i]; f[17 + i * 4] = pq_gl[i]; f[18 + i * 4] = pq_ln[i]; i++; } + pq_n = 0; + if (kind == 1) f[3] = obj[0]; + if (kind == 0) { chkind = 0; qdone = 0; line = 1; nextch(); } + if (kind == 2) { chkind = 2; chptr = obj; line = 1; nextch(); } + if (kind == 3) { chkind = 3; chbase = obj; chptr = 0; line = 1; nextch(); } + return 0; +} +fn src_pop() +{ + let f = R_SRC + isp * 32, i = 0; + tk = f[5]; tv = f[6]; tglue = f[7]; ch = f[8]; chkind = f[9]; chptr = f[10]; + line = f[12]; tline = f[13]; pq_n = f[14]; chbase = f[31]; + while (i < pq_n) { pq_tk[i] = f[15 + i * 4]; pq_tv[i] = f[16 + i * 4]; pq_gl[i] = f[17 + i * 4]; pq_ln[i] = f[18 + i * 4]; i++; } + isp--; + if (f[11]) free(f[1]); + return 0; +} +fn isopch(c) +{ + let s = "+-*/%=<>!&|^~?:.@#$"; + while (*s) { if (*s == c) return 1; s++; } + return 0; +} +// is buf[0..n) followed by c the start of some operator the lexer knows? +fn opprefix(n, c) +{ + let i = 0, k, j; + while (i < nkop) { + k = kops[i]; + j = 0; + while (j < n && k[j] == tbuf[j]) j++; + if (j == n && k[n] == c) return 1; + i++; + } + return 0; +} +fn isdig(c) { return c >= '0' && c <= '9'; } +fn isal(c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'; } +fn hexv(c) +{ + if (isdig(c)) return c - '0'; + if (c >= 'a' && c <= 'f') return c - 'a' + 10; + if (c >= 'A' && c <= 'F') return c - 'A' + 10; + return -1; +} +fn escch() +{ + let c = ch; + nextch(); + if (c != 92) return c; + c = ch; + nextch(); + if (c == 'n') return 10; + if (c == 't') return 9; + if (c == 'r') return 13; + if (c == '0') return 0; + if (c == 'e') return 27; + return c; +} +fn oplex(n) +{ + while (ch >= 0 && isopch(ch) && opprefix(n, ch)) { tbuf[n] = ch; n++; nextch(); } + tbuf[n] = 0; + tk = T_OP; tv = intern(tbuf); + return 0; +} +fn lex() +{ + let n = 0, v, dg; + tglue = 1; + while (1) { + while (ch == 32 || ch == 9 || ch == 10 || ch == 13) { nextch(); tglue = 0; } + if (ch != '/') break; + nextch(); + if (ch == '/') { while (ch >= 0 && ch != 10) nextch(); tglue = 0; continue; } + if (ch == '*') { + nextch(); + while (ch >= 0) { if (ch == '*') { nextch(); if (ch == '/') break; } else nextch(); } + nextch(); tglue = 0; continue; + } + tline = line; tbuf[0] = '/'; oplex(1); return 0; + } + tline = line; + if (ch < 0) { tk = T_EOF; tv = 0; return 0; } + if (isdig(ch)) { + v = 0; + if (ch == '0') { + nextch(); + if (ch == 'x' || ch == 'X') { + nextch(); + while ((dg = hexv(ch)) >= 0) { v = v * 16 + dg; nextch(); } + tk = T_NUM; tv = v; return 0; + } + } + while (isdig(ch)) { v = v * 10 + ch - '0'; nextch(); } + tk = T_NUM; tv = v; return 0; + } + if (isal(ch)) { + while (isal(ch) || isdig(ch)) { if (n < 1000) { tbuf[n] = ch; n++; } nextch(); } + tbuf[n] = 0; + tk = T_ID; tv = intern(tbuf); return 0; + } + if (ch == 39) { nextch(); tv = escch(); if (ch != 39) die("bad character literal"); nextch(); tk = T_NUM; return 0; } + if (ch == 34) { + nextch(); + while (ch >= 0 && ch != 34) { v = escch(); if (n < 1000) { tbuf[n] = v; n++; } } + nextch(); + tbuf[n] = 0; + tk = T_STR; tv = intern(tbuf); return 0; + } + tbuf[0] = ch; + nextch(); + if (isopch(tbuf[0])) return oplex(1); + tbuf[1] = 0; + tk = T_OP; tv = intern(tbuf); + return 0; +} +// one token of lookahead: peek() fills pk_*, next() takes it first +fn next() +{ + let i = 1; + if (pq_n) { + tk = pq_tk[0]; tv = pq_tv[0]; tglue = pq_gl[0]; tline = pq_ln[0]; + while (i < pq_n) { pq_tk[i - 1] = pq_tk[i]; pq_tv[i - 1] = pq_tv[i]; pq_gl[i - 1] = pq_gl[i]; pq_ln[i - 1] = pq_ln[i]; i++; } + pq_n--; + return 0; + } + return next0(); +} +// the k-th token after the current one (k from 0 to 3), into pk_tk and pk_tv +fn peekat(k) +{ + let a, b, c, e; + while (pq_n <= k) { + a = tk; b = tv; c = tglue; e = tline; + next0(); + pq_tk[pq_n] = tk; pq_tv[pq_n] = tv; pq_gl[pq_n] = tglue; pq_ln[pq_n] = tline; pq_n++; + tk = a; tv = b; tglue = c; tline = e; + } + pk_tk = pq_tk[k]; pk_tv = pq_tv[k]; + return 0; +} +fn peek() { return peekat(0); } +fn next0() +{ + let f, q, i; + f = R_SRC + isp * 32; + if (f[0] != 1) { lex(); return 0; } + i = f[2]; + if (i < f[3]) { q = f[1]; tk = q[1 + i * 3]; tv = q[2 + i * 3]; tline = q[3 + i * 3]; tglue = 0; f[2] = i + 1; return 0; } + if (!f[4]) { tk = T_EOF; tv = 0; return 0; } + src_pop(); // a fed list ran out: continue with the token that followed it + return 0; +} +fn is_op(s) { return tk == T_OP && tv == s; } +fn is_id(s) { return tk == T_ID && tv == s; } +fn accept(s) { if ((tk == T_OP || tk == T_ID) && tv == s) { next(); return 1; } return 0; } +fn expect(s) { if (!accept(s)) { if (!panic && !dry) { errat(); puts("expected "); puts(s); errend(); } panic = 1; if (tk == T_EOF) giveup(); } return 0; } +fn name() +{ + let n = tv; + if (tk != T_ID) die("expected a name"); + next(); + return n; +} + +// ---- operator tables ---- +// a sequence is written like asm: one-char mnemonics, numbers, and names +// (resolved when the operator is used, so image builds stay self-contained) +fn seqasm(s) +{ + let start = seqtop, n, neg, v, k, c; + while (1) { + while (*s == 32) s++; + if (!*s) break; + n = 0; + while (s[n] && s[n] != 32) { tbuf[n] = s[n]; n++; } + tbuf[n] = 0; + s = s + n; + neg = tbuf[0] == '-' && isdig(tbuf[1]); + if (isdig(tbuf[neg])) { + v = 0; k = neg; + while (tbuf[k]) { v = v * 10 + tbuf[k] - '0'; k++; } + if (neg) v = 0 - v; + R_SEQ[seqtop] = v + v + 1; seqtop++; + } else if (n == 1 && (c = mnemidx(tbuf[0])) >= 0) { + R_SEQ[seqtop] = c + c; seqtop++; + } else { + R_SEQ[seqtop] = SEQREF; R_SEQ[seqtop + 1] = intern(tbuf); seqtop = seqtop + 2; + } + } + return start; +} +fn mnemidx(c) { let i = 0; while (mnem[i]) { if (mnem[i] == c) return i; i++; } return -1; } +fn emitseq(a, n) +{ + let e = a + n, s; + while (a < e) { + if (R_SEQ[a] == SEQREF) { + s = lookup(R_SEQ[a + 1]); + if (s < 0) s = fwdfn(R_SEQ[a + 1]); + if (skind[s] == K_FN) fnref(s); else if (skind[s] == K_ENH) op(XOP0 + sval[s]); else lit(sval[s]); + a = a + 2; + } else { emit(R_SEQ[a]); a++; } + } + return 0; +} +fn addkop(p) { let i = 0; while (i < nkop) { if (kops[i] == p) return 0; i++; } kops[nkop] = p; nkop++; return 0; } +fn binfind(p) { let i = nbin; while (i) { i--; if (bname[i] == p) return i; } return -1; } +fn unfind(p) { let i = nun; while (i) { i--; if (uname[i] == p) return i; } return -1; } +fn asgfind(p) { let i = nasg; while (i) { i--; if (aname[i] == p) return i; } return -1; } +// add or replace a binary operator. code is a sequence string, or 0 for the +// built-in && (prec 2) and || (prec 1) behaviour, chosen by -1 / -2 in prec. +fn add_binop(nm, prec, code) +{ + let p = intern(nm), i = binfind(p), a, n; + if (i < 0) { i = nbin; nbin++; } + bname[i] = p; bprec[i] = prec; + if (prec < 0) { bprec[i] = 0 - prec; blen[i] = prec; bseq[i] = 0; addkop(p); return i; } + bseq[i] = seqasm(code); blen[i] = seqtop - bseq[i]; + addkop(p); + // and its compound assignment, x op= y + n = strlen(nm); + wcopy(tbuf + 1000, nm, n); + tbuf[1000 + n] = '='; tbuf[1001 + n] = 0; + a = intern(tbuf + 1000); + if (prec >= 8 || prec == 5 || prec == 4 || prec == 3) { + if (asgfind(a) < 0) { aname[nasg] = a; nasg++; } + abin[asgfind(a)] = i; + addkop(a); + } + return i; +} +fn add_unop(nm, code) +{ + let p = intern(nm), i = unfind(p); + if (i < 0) { i = nun; nun++; } + uname[i] = p; useq[i] = seqasm(code); ulen[i] = seqtop - useq[i]; + addkop(p); + return i; +} +fn add_kw(nm, handler, flags) { let s = addsym(intern(nm), K_KW, handler); sflag[s] = F_DEF | flags; return s; } + +// ---- locals and the analyzer's view of them ---- +fn findloc(n) { let i = nloc; while (i > lbase) { i--; if (lname[i] == n) return i; } return -1; } +fn newloc(n) +{ + let i = nloc; + if (nloc >= NL) fatal("too many locals"); + nloc++; + lname[i] = n; lslot[i] = fslots; lhome[i] = fdepth; lstate[i] = S_PLAIN; + lowner[i] = 0; lused[i] = 0; larr[i] = 0; lown[i] = 0; ltype[i] = tword; ltag[i] = 0; + return i; +} +fn lroot(i) { while (lstate[i] == S_CHILD) i = lowner[i]; return i; } +fn tracked(i) { return lstate[i] != S_PLAIN; } +// a use that doesn't give the object away +fn touch(i) +{ + let r; + lused[i] = 1; + if (!fchk) return 0; + r = lroot(i); + lused[r] = 1; + if (lstate[r] == S_DEAD) { cerrn(i, "used after it was given away"); return 0; } + // a d object that shows up in a deeper block is given to that block + if (lstate[r] == S_D && lhome[r] < fdepth) { lhome[r] = fdepth; lstate[r] = S_M; lown[r] = 1; } + return 0; +} +fn plainuse(c) +{ + if (c == C_FRESH) cerr("a new object can't be used as a plain value; bind it to a name first"); + if (c >= C_VAR) touch(c - C_VAR); + return C_PLAIN; +} +fn freevar(i) { laddr(lslot[i]); op(O_LD); callname(s_free); op(O_DROP); return 0; } +// leaving every block deeper than or equal to dep: free what is owned, and +// complain about what should have left already. final means the block really +// ends here rather than an early exit. +fn exit_vars(dep, final) +{ + let i = nloc, st; + if (!fchk || !live) return 0; + while (i > lbase) { + i--; + if (lhome[i] < dep) continue; + st = lstate[i]; + if (st == S_M) freevar(i); else if (st == S_U) cerrn(i, "u object must leave this block (block value, assignment to the enclosing block, or return)"); + else if (st == S_D) cerrn(i, "d object was never given away"); + } + return 0; +} +// an m object has to be used somewhere in the block that owns it, on any path +fn unused_check(from) +{ + while (from < nloc) { if (fchk && lown[from] && !lused[from]) serrn(lname[from], "m object is never used"); from++; } + return 0; +} +fn block_close(save) +{ + let i = lbase; + unused_check(save); + exit_vars(fdepth, 1); + while (i < save) { if (lhome[i] >= fdepth) { lstate[i] = S_DEAD; lhome[i] = fdepth - 1; } i++; } + nloc = save; + return 0; +} + +// snapshots: [lbase] [nloc] then state, owner, home per local +fn snap_push() +{ + let b = snaptop, i = lbase, p; + p = R_SNAP + b; + p[0] = lbase; p[1] = nloc; + p = p + 2; + while (i < nloc) { p[0] = lstate[i]; p[1] = lowner[i]; p[2] = lhome[i]; p = p + 3; i++; } + snaptop = p - R_SNAP; + if (snaptop >= 8000) fatal("nesting too deep for the analyzer"); + return b; +} +fn snap_pop(b) { snaptop = b; return 0; } +fn snap_restore(b) +{ + let p = R_SNAP + b, i = p[0], e = p[1]; + if (e > nloc) e = nloc; + p = p + 2; + while (i < e) { lstate[i] = p[0]; lowner[i] = p[1]; lhome[i] = p[2]; p = p + 3; i++; } + return 0; +} +fn empty(s) { return s == S_PLAIN || s == S_DEAD; } +// compare the current states with a snapshot: -1 if they agree, else the +// first local that differs. plain and dead agree and merge to dead. +fn snap_cmp(b) +{ + let p = R_SNAP + b, i = p[0], e = p[1], s; + if (e > nloc) e = nloc; + p = p + 2; + while (i < e) { + s = lstate[i]; + if (empty(s) && empty(p[0])) { if (p[0] == S_DEAD) lstate[i] = S_DEAD; } + else if (s != p[0] || lhome[i] != p[2] || (s == S_CHILD && lowner[i] != p[1])) return i; + p = p + 3; i++; + } + return -1; +} +fn statename(s) +{ + if (s == S_PLAIN) return "empty"; + if (s == S_M) return "m"; + if (s == S_U) return "u"; + if (s == S_D) return "d"; + if (s == S_DEAD) return "given away"; + if (s == S_CHILD) return "owned by a container"; + return "borrowed"; +} +fn merge_err(i, sa, what) +{ + if (!fchk || panic || dry) return 0; + errat(); puts(lname[i]); puts(": "); puts(what); + puts(" (one path leaves it "); puts(statename(sa)); puts(", another "); puts(statename(lstate[i])); puts(")"); + errend(); + return 0; +} +// after an if: the current state is the else path, sa is the then path + +// ---- joins ---- +// where control flow comes back together (after an if, a loop, a switch or +// ?:) every variable has to agree across the paths that reach the join. one +// exception: a variable that became d, or was given away, on some path is dead +// after the join, and the paths where it's still alive free it on the way in +// (fixups). u objects and borrows don't get this treatment: a u object still +// has to leave, and a borrow never changes. +const R_EXIT = 0xBF500, R_EXITEND = 0xBFF00; // exit records: the states at each break +let extop, jps[64], njps; // the paths being joined: [lbase, nloc, triples] +fn jst(p, i) { if (i >= p[1]) return S_PLAIN; return p[2 + 3 * (i - p[0])]; } +fn jow(p, i) { return p[3 + 3 * (i - p[0])]; } +fn jhm(p, i) { return p[4 + 3 * (i - p[0])]; } +// the states at a break, with the jump to patch: [site] then a snapshot +fn exit_rec(site) +{ + let p = R_EXIT + extop, i = lbase, q; + if (extop + 3 + 3 * (nloc - lbase) >= R_EXITEND - R_EXIT) fatal("too many breaks for the analyzer"); + p[0] = site; p[1] = lbase; p[2] = nloc; + q = p + 3; + while (i < nloc) { q[0] = lstate[i]; q[1] = lowner[i]; q[2] = lhome[i]; q = q + 3; i++; } + extop = q - R_EXIT; + return p; +} +fn jadd(p) { if (njps >= 64) fatal("too many paths for the analyzer"); jps[njps] = p; njps++; return 0; } +fn jsame(p, q, i) +{ + let s = jst(p, i), t = jst(q, i); + if (empty(s) && empty(t)) return 1; + return s == t && jhm(p, i) == jhm(q, i) && (s != S_CHILD || jow(p, i) == jow(q, i)); +} +// join the paths in jps over the locals [lbase, n0) into the current states +fn join(n0, what) +{ + let i = lbase, j, p, s, s0, dead, okdead, bad; + if (!njps) return 0; + while (i < n0) { + p = jps[0]; s0 = jst(p, i); + dead = 0; okdead = 1; bad = -1; + j = 0; + while (j < njps) { + s = jst(jps[j], i); + if (s == S_DEAD || s == S_D) dead = 1; + if (s == S_U || s == S_BORROW) okdead = 0; + if (!jsame(p, jps[j], i)) bad = j; + j++; + } + lstate[i] = s0; lowner[i] = jow(p, i); lhome[i] = jhm(p, i); + if (bad < 0) { + if (empty(s0)) { j = 0; while (j < njps) { if (jst(jps[j], i) == S_DEAD) lstate[i] = S_DEAD; j++; } } + } else if (dead && okdead) lstate[i] = S_DEAD; + else if (fchk && !panic && !dry) { + errat(); puts(lname[i]); puts(": "); puts(what); + puts(" (one path leaves it "); puts(statename(s0)); puts(", another "); puts(statename(jst(jps[bad], i))); puts(")"); + errend(); + } + i++; + } + return 0; +} +// on path p: count (and with doit, emit) the frees for what the join made dead but p still owns +fn fixups(p, n0, doit) +{ + let i = lbase, n = 0, s; + if (!fchk) return 0; + while (i < n0) { + s = jst(p, i); + if (lstate[i] == S_DEAD && (s == S_M || s == S_D)) { n++; if (doit) freevar(i); } + i++; + } + return n; +} +// a jump back to the top of a loop: whatever the loop owns has to be as it was +fn backedge(s0) +{ + let p = R_SNAP + s0, i = p[0], e = p[1], s; + if (!fchk || !live) return 0; + if (e > nloc) e = nloc; + while (i < e) { + s = lstate[i]; + if (empty(s) && empty(jst(p, i))) { if (jst(p, i) == S_DEAD) lstate[i] = S_DEAD; } + else if (s != jst(p, i) || lhome[i] != jhm(p, i) || (s == S_CHILD && lowner[i] != jow(p, i))) { + if (s == S_DEAD || s == S_D) cerrn(i, "is given away inside a loop that comes back around to it; give it away on a path that leaves the loop"); + else if (!panic && !dry) { + errat(); puts(lname[i]); puts(": changes state inside a loop; each pass has to leave it as it found it (it went from "); + puts(statename(jst(p, i))); puts(" to "); puts(statename(s)); puts(")"); errend(); + } + } + i++; + } + return 0; +} +// joins two paths that end at the same point, like the branches of if/else: +// path a jumped (through the hole at ja) and path b falls through to here +fn join2(sa, livea, ja, livebv, n0, what) +{ + let sb, nt, cj; + sb = snap_push(); + njps = 0; if (livea) jadd(R_SNAP + sa); if (livebv) jadd(R_SNAP + sb); + join(n0, what); + if (livebv) fixups(R_SNAP + sb, n0, 1); + nt = 0; if (livea) nt = fixups(R_SNAP + sa, n0, 0); + if (nt) { cj = hole(); op(O_JMP); setlit(ja, here); fixups(R_SNAP + sa, n0, 1); setlit(cj, here); } + else setlit(ja, here); + live = livea || livebv; + return 0; +} + +// ---- analyzer rules for the places values flow ---- +// recast: x as u / x as d / x as m +fn recast(c, t) +{ + let i, st; + if (!fchk) return c; + if (c < C_VAR) { cerr("only a named owned object can be recast"); return c; } + i = c - C_VAR; + st = lstate[i]; + lused[i] = 1; + if (st == S_CHILD) cerrn(i, "belongs to a container; recast the container instead"); + else if (st == S_BORROW) cerrn(i, "is borrowed and can't be recast"); + else if (st == S_PLAIN) cerrn(i, "isn't an owned object"); + else if (st == S_DEAD) cerrn(i, "used after it was given away"); + else if (t == S_M && st != S_M) cerrn(i, "can't be recast back to m"); + else lstate[i] = t; + return c; +} +// an argument for a parameter tagged t (0 plain, 1 m, 2 d) +fn argcheck(c, t) +{ + let i, st; + if (!fchk) return 0; + if (c == C_FRESH) { if (t != 2) cerr("a new object can only go straight to a d parameter; bind it to a name first"); return 0; } + if (c < C_VAR || !tracked(c - C_VAR)) return 0; + i = c - C_VAR; + st = lstate[i]; + if (lstate[lroot(i)] == S_DEAD) { cerrn(i, "used after it was given away"); return 0; } + if (t <= 1) { // untagged parameters are m: a lend + if (st == S_D) { cerrn(i, "is d; pass it to a d parameter (lending it would lose it)"); return 0; } + touch(i); + return 0; + } + if (st == S_D) { lstate[i] = S_DEAD; lused[i] = 1; return 0; } + if (st == S_M) cerrn(i, "is m; write d*x to give it away"); + else if (st == S_U) cerrn(i, "is u; write d*x to give it away instead of returning it"); + else if (st == S_CHILD) cerrn(i, "belongs to a container and can't be given away alone"); + else cerrn(i, "is borrowed and can't be given away"); + return 0; +} +fn emitown(p) { laddr(lslot[p]); op(O_LD); callname(s_own); return 0; } +// a store of class rc into an lvalue of kind k (local or container index i) +fn assigncheck(k, i, rc) +{ + let st, j, r, js; + if (!fchk) return C_PLAIN; + j = -1; + if (rc >= C_VAR && tracked(rc - C_VAR)) j = rc - C_VAR; + if (j >= 0 && lstate[lroot(j)] == S_DEAD) { cerrn(j, "used after it was given away"); return C_PLAIN; } + if (k == LV_LOCAL) { + st = lstate[i]; + if (j < 0 && rc != C_FRESH) { + if (!empty(st)) cerrn(i, "still owns an object; overwriting it would leak it"); + else lstate[i] = S_PLAIN; + return C_PLAIN; + } + if (!empty(st)) { cerrn(i, "still owns an object; overwriting it would leak it"); return C_PLAIN; } + if (rc == C_FRESH) { lstate[i] = S_M; lused[i] = 0; lown[i] = 1; if (ltag[i]) lstate[i] = ltag[i]; return C_PLAIN; } + js = lstate[j]; + if (js == S_U && lhome[i] == lhome[j] - 1) { // handing a u object up one block + lstate[i] = S_M; lused[i] = 0; lown[i] = 1; lstate[j] = S_DEAD; lused[j] = 1; if (ltag[i]) lstate[i] = ltag[i]; + return C_PLAIN; + } + if (js == S_U) cerrn(j, "is u; it can only be handed to a variable of the enclosing block, or returned"); + else cerrn(j, "is an owned object and can't be copied into another variable"); + return C_PLAIN; + } + if (k == LV_ELEM) { + r = lroot(i); + st = lstate[r]; + touch(i); + if (rc == C_FRESH) { emitown(i); return C_PLAIN; } + if (j < 0) return C_PLAIN; + js = lstate[j]; + if (js == S_CHILD) cerrn(j, "already belongs to a container"); + else if (js == S_BORROW) cerrn(j, "is borrowed and can't be stored"); + else if (st == S_BORROW) { + if (js == S_U) cerrn(j, "is u; it has to leave through the block above, not into a borrowed object"); + else { lstate[j] = S_DEAD; lused[j] = 1; emitown(i); } // storing it gives it away + } else if (js != st) cerrn(j, "its tag doesn't match its container's (both have to be m, u or d)"); + else if (lhome[j] < lhome[r]) cerrn(j, "lives in an outer block than the container it's stored into"); + else { lstate[j] = S_CHILD; lowner[j] = i; lused[j] = 1; emitown(i); } + return C_PLAIN; + } + // a global, or memory the analyzer doesn't track: the collector takes it + if (j >= 0) { + if (lstate[j] == S_D) { lstate[j] = S_DEAD; lused[j] = 1; } + else cerrn(j, "must be d to be stored in a global or in untracked memory (the collector takes it over)"); + } + return C_PLAIN; +} +fn retcheck(c) +{ + let i; + if (!fchk) return 0; + i = -1; + if (c >= C_VAR && tracked(c - C_VAR)) i = c - C_VAR; + if (i >= 0 && lstate[lroot(i)] == S_DEAD) { cerrn(i, "used after it was given away"); return 0; } + if (fret) { + if (i >= 0) { + if (lstate[i] == S_U) { lstate[i] = S_DEAD; lused[i] = 1; } + else cerrn(i, "has to be u to be returned; write 'return u*x'"); + } + return 0; + } + if (c == C_FRESH) cerr("returns a new object; declare the function with -> .u"); + else if (i >= 0) cerrn(i, "is owned and can't be returned from a function without -> .u"); + return 0; +} +// the last expression of a block expression +fn handup(c) +{ + let i; + if (!fchk) return C_PLAIN; + if (c == C_FRESH) return C_FRESH; + if (c < C_VAR || !tracked(c - C_VAR)) return C_PLAIN; + i = c - C_VAR; + if (lstate[i] == S_U && lhome[i] == fdepth) { lstate[i] = S_DEAD; lused[i] = 1; return C_FRESH; } + if (lstate[i] == S_U) cerrn(i, "only a u object of this block can be its value"); + else cerrn(i, "has to be u to be handed up as the block's value"); + return C_PLAIN; +} +fn discard(c) +{ + if (c == C_FRESH) cerr("a new object (or a u result) can't be thrown away"); + else if (c >= C_VAR) touch(c - C_VAR); + return 0; +} + +// ---- types ---- +// types are a compiler primitive: records in the type area, hash-consed so +// the same type is always the same pointer and comparing types is comparing +// words. untyped code is typed word, which goes with anything. the records +// stay in memory after compiling, so programs can inspect them. +// t[0] kind t[1] size in words t[2] name t[3] n t[4] a t[5] b t[6] c t[7..] extra +// ptr: a = element arr: a = element, n = length +// fn: a = return type, n = parameter count, b = signature (u m d tags), extra = parameter types +// struct: n = field count (-1 until laid out), a = fields (name, type, offset), +// b = generic template, c = type arguments [n, t1, t2 ...] +// var: a type parameter, n = its index +// gen: a generic struct: n = parameter count, a = fields (name, type), b = field count, c = parameter names + + +fn talloc(n) { let p = tytop; if (tytop + n >= R_TYEND) fatal("the type area is full"); tytop = tytop + n; wfill(p, 0, n); return p; } +fn tnew(kind, size, nm, extra) +{ + let t = talloc(7 + extra); + t[0] = kind; t[1] = size; t[2] = nm; + if (ntl >= NTL) fatal("too many types"); + R_TY[ntl] = t; ntl++; + return t; +} +fn tptr(e) +{ + let i = 0, t; + while (i < ntl) { t = R_TY[i]; if (t[0] == TY_PTR && t[4] == e) return t; i++; } + t = tnew(TY_PTR, 1, 0, 0); t[4] = e; + return t; +} +fn tarr(e, n) +{ + let i = 0, t; + while (i < ntl) { t = R_TY[i]; if (t[0] == TY_ARR && t[4] == e && t[3] == n) return t; i++; } + t = tnew(TY_ARR, n * tsz(e), 0, 0); t[4] = e; t[3] = n; + return t; +} +fn tfn(ret, np, ps, sig) +{ + let i = 0, j, t; + while (i < ntl) { + t = R_TY[i]; + if (t[0] == TY_FN && t[4] == ret && t[3] == np && t[5] == sig) { + j = 0; while (j < np && t[7 + j] == ps[j]) j++; + if (j == np) return t; + } + i++; + } + t = tnew(TY_FN, 1, 0, np); t[4] = ret; t[3] = np; t[5] = sig; + wcopy(t + 7, ps, np); + return t; +} +fn tvar(i, nm) +{ + let k = 0, t; + while (k < ntl) { t = R_TY[k]; if (t[0] == TY_VAR && t[3] == i) return t; k++; } + t = tnew(TY_VAR, 1, nm, 0); t[3] = i; + return t; +} +fn tinst(g, args, n) +{ + let i = 0, j, t, v; + while (i < ntl) { + t = R_TY[i]; + if (t[0] == TY_STRUCT && t[5] == g) { + v = t[6]; j = 0; while (j < n && v[1 + j] == args[j]) j++; + if (j == n) return t; + } + i++; + } + v = talloc(n + 1); v[0] = n; wcopy(v + 1, args, n); + t = tnew(TY_STRUCT, 0, g[2], 0); t[3] = -1; t[5] = g; t[6] = v; + return t; +} +// lay out a generic instance on first use +fn tresolve(t) +{ + let g, f, n, i, fs, off = 0, ft, bs[8]; + if (t[0] != TY_STRUCT || t[3] >= 0) return t; + if (t[3] == -2) { serr("a struct can't contain itself by value"); t[3] = 0; return t; } + t[3] = -2; + g = t[5]; n = g[5]; f = g[4]; + wfill(bs, 0, 8); wcopy(bs, t[6] + 1, t[6][0]); + fs = talloc(n * 3 + 1); + i = 0; + while (i < n) { + ft = subst(f[i + i + 1], bs); + fs[i * 3] = f[i + i]; fs[i * 3 + 1] = ft; fs[i * 3 + 2] = off; + off = off + tsz(ft); + i++; + } + t[4] = fs; t[1] = off; t[3] = n; + return t; +} +fn tsz(t) { if (t[0] == TY_STRUCT && t[3] < 0) tresolve(t); return t[1]; } +// replace type parameters with what they were bound to (unbound ones become word) +fn subst(t, bs) +{ + let k = t[0], i, n, v[24], vec; + if (k == TY_VAR) { if (bs[t[3]]) return bs[t[3]]; return tword; } + if (k == TY_PTR) return tptr(subst(t[4], bs)); + if (k == TY_ARR) return tarr(subst(t[4], bs), t[3]); + if (k == TY_FN) { n = t[3]; i = 0; while (i < n) { v[i] = subst(t[7 + i], bs); i++; } return tfn(subst(t[4], bs), n, v, t[5]); } + if (k == TY_STRUCT && t[5]) { vec = t[6]; n = vec[0]; i = 0; while (i < n) { v[i] = subst(vec[1 + i], bs); i++; } return tinst(t[5], v, n); } + return t; +} +fn isnum(t) { return t[0] == TY_INT || t[0] == TY_CHAR || t[0] == TY_BOOL; } +fn isagg(t) { return t[0] == TY_ARR || t[0] == TY_STRUCT; } +fn isptr(t) { return t[0] == TY_PTR || t[0] == TY_ARR; } +fn decay(t) { if (t[0] == TY_ARR) return tptr(t[4]); return t; } +fn telem(t) { if (t[0] == TY_PTR || t[0] == TY_ARR) return t[4]; return tword; } +// can a value of type src go where dst is expected? +fn compat(dst, src) +{ + if (dst == src || dst == tword || src == tword) return 1; + if (isnum(dst) && isnum(src)) return 1; + src = decay(src); + if (dst[0] == TY_PTR && src[0] == TY_PTR) { + if (dst[4] == tword || src[4] == tword || dst[4] == tvoid || src[4] == tvoid) return 1; + return dst[4] == src[4]; + } + if ((dst[0] == TY_PTR || dst[0] == TY_FN) && xzero) return 1; // a literal 0 is a null pointer + return dst == src; +} +fn unify(p, a, bs) +{ + let i, n, pv, av; + if (p[0] == TY_VAR) { i = p[3]; if (!bs[i]) { bs[i] = a; return 1; } return compat(bs[i], a); } + a = decay(a); + if (p[0] == TY_PTR && a[0] == TY_PTR) { if (a[4] == tword) return 1; return unify(p[4], a[4], bs); } + if (p[0] == TY_STRUCT && p[5] && a[0] == TY_STRUCT && a[5] == p[5]) { + pv = p[6]; av = a[6]; n = pv[0]; i = 0; + while (i < n) { if (!unify(pv[1 + i], av[1 + i], bs)) return 0; i++; } + return 1; + } + return compat(p, a); +} +fn tprint(t) +{ + let k = t[0], i, v; + if (k == TY_PTR) { putc('*'); return tprint(t[4]); } + if (k == TY_ARR) { putc('['); putn(t[3]); putc(']'); return tprint(t[4]); } + if (k == TY_FN) { + puts("fn("); + i = 0; + while (i < t[3]) { + if (i) puts(", "); + tprint(t[7 + i]); + if (i < 12 && tparamtag(t, i) == 1) puts(".m"); + if (i < 12 && tparamtag(t, i) == 2) puts(".d"); + i++; + } + putc(')'); + if (t[4] != tword || (t[5] & 1)) { puts(" -> "); if (t[4] != tword) tprint(t[4]); if (t[5] & 1) puts(".u"); } + return 0; + } + if (t[2]) puts(t[2]); else puts("?"); + if (k == TY_STRUCT && t[5]) { + v = t[6]; putc('<'); + i = 0; while (i < v[0]) { if (i) puts(", "); tprint(v[1 + i]); i++; } + putc('>'); + } + return 0; +} +fn tcerr(what, want, got) +{ + if (!tc_on || !live || dry || panic) return 0; + errat(); puts("type error: "); puts(what); + puts(": expected "); tprint(want); puts(", got "); tprint(got); + errend(); + return 0; +} +fn tcneed(ok, what, got) +{ + if (ok || !tc_on || !live || dry || panic) return 0; + errat(); puts("type error: "); puts(what); puts(", got "); tprint(got); + errend(); + return 0; +} +fn tpfind(n) { let i = ntp; while (i) { i--; if (tpn[i] == n) return i; } return -1; } +fn istypename(n) +{ + let s; + if (tpfind(n) >= 0) return 1; + if (findloc(n) >= 0) return 0; + s = lookup(n); + return s >= 0 && skind[s] == K_TYPE; +} +// the closing > of a type argument list; splits >> in two +fn closeangle() +{ + if (is_op(s_shr)) { tv = s_gt; return 0; } + expect(s_gt); + return 0; +} +fn ptype() +{ + let n, s, t, np = 0, ps[24], ret, args[8], na = 0, i; + if (accept(s_star)) return tptr(ptype()); + if (accept(s_lk)) { n = cexpr(); expect(s_rk); t = ptype(); if (n < 0) serr("a negative array length"); return tarr(t, n); } + if (is_id(s_fn)) { + next(); expect(s_lp); + if (!is_op(s_rp)) { while (1) { if (np >= 24) die("too many parameters"); ps[np] = ptype(); np++; if (!accept(s_comma)) break; } } + expect(s_rp); + ret = tword; + i = 0; + if (accept(s_arrow)) { if (!is_op(s_dot)) ret = ptype(); if (accept(s_dot)) { if (!is_id(s_u)) die("the return tag is .u"); next(); i = 1; } } + return tfn(ret, np, ps, i + np + np); + } + if (tk != T_ID) { die("expected a type"); return tword; } + n = tv; + i = tpfind(n); + if (i >= 0) { next(); return tvar(i, n); } + s = lookup(n); + if (s < 0 || skind[s] != K_TYPE) { dien(n, "isn't a type"); return tword; } + next(); + t = sval[s]; + if (t[0] != TY_GEN) return t; + expect(s_lt); + while (1) { + if (na >= 8) die("too many type arguments"); + args[na] = ptype(); + if (tsz(args[na]) != 1) serr("type arguments have to be one word (a pointer, a number, or a parameter)"); + na++; + if (!accept(s_comma)) break; + } + closeangle(); + if (na != t[3]) serrn(n, "wrong number of type arguments"); + return tinst(t, args, na); +} +// does a declaration start here? (a type, or a bare .tag) +fn tstart() +{ + let k; + if (tk == T_OP) { + if (tv == s_lk || tv == s_dot) return 1; + if (tv == s_star) { // *T name, **T name ...: look past the stars + k = 0; + while (k < 3) { peekat(k); if (!(pk_tk == T_OP && pk_tv == s_star)) break; k++; } + peekat(k); + return (pk_tk == T_ID && (istypename(pk_tv) || pk_tv == s_fn)) || (pk_tk == T_OP && pk_tv == s_lk); + } + return 0; + } + if (tk != T_ID) return 0; + return tv == s_fn || istypename(tv); +} +// [type] [.tag] +fn dtype() +{ + dty = 0; dtag = 0; + if (!is_op(s_dot)) dty = ptype(); + if (accept(s_dot)) { + if (tv == s_m) dtag = S_M; else if (tv == s_u) dtag = S_U; else if (tv == s_d) dtag = S_D; else die("a tag is .u, .m or .d"); + next(); + } + return 0; +} +// struct name { type field, ... } or struct name { ... } +fn kw_struct() +{ + let n, t, s, nf = 0, buf = R_QBUF, i, fs, off = 0, gen = 0, ft; + next(); + n = name(); + if (is_op(s_lt)) { // generic + next(); ntp = 0; + while (1) { if (ntp >= 8) die("too many type parameters"); tpn[ntp] = name(); ntp++; if (!accept(s_comma)) break; } + closeangle(); + gen = 1; + t = tnew(TY_GEN, 0, n, 0); t[3] = ntp; + fs = talloc(ntp); wcopy(fs, tpn, ntp); t[6] = fs; + } else { t = tnew(TY_STRUCT, 0, n, 0); t[3] = -2; } // -2 while its fields are read + s = addsym(n, K_TYPE, t); + expect(s_lb); + while (!is_op(s_rb)) { + ft = ptype(); + while (1) { + if (nf >= 1000) fatal("too many fields"); + buf[nf + nf] = name(); buf[nf + nf + 1] = ft; nf++; + if (!accept(s_comma)) break; + } + expect(s_semi); + } + next(); + if (gen) { + fs = talloc(nf + nf); wcopy(fs, buf, nf + nf); + t[4] = fs; t[5] = nf; + ntp = 0; + } else { + fs = talloc(nf * 3 + 1); + i = 0; + while (i < nf) { + ft = buf[i + i + 1]; + if (ft == t) { serr("a struct can't contain itself by value; use a pointer"); ft = tword; } + fs[i * 3] = buf[i + i]; fs[i * 3 + 1] = ft; fs[i * 3 + 2] = off; + off = off + tsz(ft); + i++; + } + t[4] = fs; t[1] = off; t[3] = nf; + } + accept(s_semi); + return s; +} +// type name = T; +fn kw_type() { let n; next(); n = name(); expect(s_eq); addsym(n, K_TYPE, ptype()); expect(s_semi); return 0; } +// typecheck on; / typecheck off; +fn kw_typecheck() +{ + next(); + if (is_id(s_on)) tc_on = 1; else if (is_id(s_off)) tc_on = 0; else die("typecheck on or typecheck off"); + next(); expect(s_semi); + return 0; +} +// the field of a struct type, or -1 +fn tfindfield(t, n) { let i = 0; tresolve(t); while (i < t[3]) { if (t[4][i * 3] == n) return i; i++; } return -1; } +// sizeof(T or expr), typeof(T or expr): the expression form is compiled dry, so it never runs +fn typearg() +{ + let t, sb, c; + expect(s_lp); + if (tstart() && !is_op(s_dot)) t = ptype(); + else { + sb = snap_push(); dry++; + c = expr(); rval(); + t = xty; + dry--; snap_restore(sb); snap_pop(sb); + } + expect(s_rp); + return t; +} +fn kw_sizeof() { let t; next(); t = typearg(); lit(tsz(t)); xty = tint; return 0; } +fn kw_typeof() { let t; next(); t = typearg(); lit(t); xty = tword; return 0; } +fn types_init() +{ + tytop = R_TY + NTL; ntl = 0; + tword = tnew(TY_WORD, 1, intern("word"), 0); + tint = tnew(TY_INT, 1, intern("int"), 0); + tchar = tnew(TY_CHAR, 1, intern("char"), 0); + tbool = tnew(TY_BOOL, 1, intern("bool"), 0); + tvoid = tnew(TY_VOID, 0, intern("void"), 0); + addsym(tword[2], K_TYPE, tword); addsym(tint[2], K_TYPE, tint); addsym(tchar[2], K_TYPE, tchar); + addsym(tbool[2], K_TYPE, tbool); addsym(tvoid[2], K_TYPE, tvoid); + s_lt = intern("<"); s_gt = intern(">"); s_shr = intern(">>"); s_le = intern("<="); s_ge = intern(">="); + s_eqeq = intern("=="); s_ne = intern("!="); s_plus = intern("+"); + s_struct = intern("struct"); s_type = intern("type"); s_typecheck = intern("typecheck"); + s_on = intern("on"); s_off = intern("off"); s_new = intern("new"); s_new_t = intern("new_t"); + numops[0] = intern("*"); numops[1] = intern("/"); numops[2] = intern("%"); numops[3] = intern("&"); + numops[4] = intern("|"); numops[5] = intern("^"); numops[6] = intern("<<"); numops[7] = s_shr; nnumops = 8; + return 0; +} +fn isnumop(o) { let i = 0; while (i < nnumops) { if (numops[i] == o) return 1; i++; } return 0; } +// the type of a binary operation, checking it when the typechecker is on +fn bintype(b, lt, rt) +{ + let o = bname[b]; + lt = decay(lt); rt = decay(rt); + if (blen[b] < 0) { tcneed(!isagg(lt) && !isagg(rt), "&& and || need scalars", lt); return tbool; } + if (o == s_eqeq || o == s_ne) { if (!compat(lt, rt) && !compat(rt, lt)) tcerr("comparison", lt, rt); return tbool; } + if (o == s_lt || o == s_gt || o == s_le || o == s_ge) { + if (!(lt == tword || rt == tword || (isnum(lt) && isnum(rt)) || (lt[0] == TY_PTR && rt[0] == TY_PTR))) tcerr("comparison", lt, rt); + return tbool; + } + if (o == s_plus) { + if (lt[0] == TY_PTR && (isnum(rt) || rt == tword)) return lt; + if (rt[0] == TY_PTR && isnum(lt)) return rt; + if (lt == tword || rt == tword) return tword; + if (isnum(lt) && isnum(rt)) return tint; + tcerr("+", lt, rt); return tword; + } + if (o == s_minus) { + if (lt[0] == TY_PTR && rt[0] == TY_PTR) return tint; + if (lt[0] == TY_PTR && (isnum(rt) || rt == tword)) return lt; + if (lt == tword || rt == tword) return tword; + if (isnum(lt) && isnum(rt)) return tint; + tcerr("-", lt, rt); return tword; + } + if (isnumop(o)) { + if (lt == tword || rt == tword) return tword; + if (isnum(lt) && isnum(rt)) return tint; + if (isnum(lt)) lt = rt; + tcneed(0, "arithmetic needs numbers", lt); return tword; + } + return tword; // operators a program added +} + +// ---- reflection ---- +// for programs (and parse words) looking at types at runtime +fn tkind(t) { return t[0]; } +fn tsize(t) { return tsz(t); } +fn tname(t) { return t[2]; } +fn tlen(t) { return t[3]; } +fn tnfields(t) { tresolve(t); return t[3]; } +fn tfname(t, i) { tresolve(t); return t[4][i * 3]; } +fn tftype(t, i) { tresolve(t); return t[4][i * 3 + 1]; } +fn tfoff(t, i) { tresolve(t); return t[4][i * 3 + 2]; } +fn tret(t) { return t[4]; } +fn tnparams(t) { return t[3]; } +fn tparam(t, i) { return t[7 + i]; } +fn tparamtag(t, i) { return ((t[5] >> 6) >> (i + i)) & 3; } // 0 untagged (m), 1 .m, 2 .d +fn tretu(t) { return t[5] & 1; } +fn tnargs(t) { if (t[0] != TY_STRUCT || !t[5]) return 0; return t[6][0]; } +fn targ(t, i) { return t[6][1 + i]; } +// the type of a function, by address. untyped functions get one made from their signature +fn sigof(f) +{ + let i = nsym, s = -1, ws[24], n, k; + while (i) { i--; if (skind[i] == K_FN && sval[i] == f && (sflag[i] & F_DEF)) { s = i; break; } } + if (s < 0) return 0; + if (stype[s]) return stype[s]; + n = (ssig[s] >> 1) & 31; k = 0; + while (k < n) { ws[k] = tword; k++; } + return tfn(tword, n, ws, ssig[s]); +} +fn sigofs(nm) { let s = lookups(nm); if (s < 0 || skind[s] != K_FN) return 0; return sigof(sval[s]); } + +// ---- expressions ---- +// every expression leaves its value on the stack, returns its analyzer class, +// and sets xty to its type. lv means the stack holds an address that hasn't +// been loaded yet; structs and arrays are never loaded, their address is +// their value. +fn rval() { if (lv) { if (!isagg(xty)) op(O_LD); lv = 0; } return 0; } +fn kwcall(s, e) +{ + let f = sval[s], old = kw_expr, c; + kw_expr = e; xcls = C_PLAIN; xty = tword; xzero = 0; + f(); + kw_expr = old; + c = xcls; + lv = 0; lvk = LV_NONE; + return c; +} +fn stmtonly() { if (kw_expr) serr("this can't be used inside an expression"); return 0; } +// the arguments of a call. ft is the callee's type (or 0), bs the generic bindings +fn args(tags, ft, bs) +{ + let np = 0, c, pt; + expect(s_lp); + if (!is_op(s_rp)) { + while (1) { + c = expr(); rval(); + argcheck(c, (tags >> (np + np)) & 3); + if (ft && np < ft[3]) { pt = ft[7 + np]; if (!unify(pt, xty, bs)) tcerr("argument", subst(pt, bs), xty); } + np++; + if (!accept(s_comma)) break; + } + } + expect(s_rp); + return np; +} +// new(T) and new(T, count): a typed object, its type stamped into the header +fn newtyped() +{ + let t, c; + t = ptype(); + if (accept(s_comma)) { + c = expr(); rval(); plainuse(c); + if (tsz(t) != 1) { lit(tsz(t)); op(15); } + lit(tarr(t, 0)); + } else { lit(tsz(t)); lit(t); } + expect(s_rp); + callname(s_new_t); + lv = 0; lvk = LV_NONE; xty = tptr(t); xzero = 0; + return C_FRESH; +} +fn call(s) +{ + let np, tags = 0, ret = 0, ft = stype[s], bs[8]; + if (sname[s] == s_new && skind[s] == K_FN) { + peek(); + if (pk_tk == T_OP && (pk_tv == s_star || pk_tv == s_lk)) { next(); return newtyped(); } + if (pk_tk == T_ID && (istypename(pk_tv) || pk_tv == s_fn)) { next(); return newtyped(); } + } + if (sflag[s] & F_SIG) { tags = ssig[s] >> 6; ret = ssig[s] & 1; } + else sflag[s] = sflag[s] | F_UNTYPED; + wfill(bs, 0, 8); + if (ngbind) { wcopy(bs, gbind, ngbind); ngbind = 0; } + np = args(tags, ft, bs); + if ((sflag[s] & F_SIG) && np != ((ssig[s] >> 1) & 31)) serrn(sname[s], "wrong number of arguments"); + fnref(s); op(O_CALL); + lv = 0; lvk = LV_NONE; xzero = 0; + xty = tword; + if (ft) xty = subst(ft[4], bs); + if (ret) return C_FRESH; + return C_PLAIN; +} +// an enhancement used like a function: the arguments, then its one opcode +fn enhcall(s) +{ + let nin = ssig[s] & 15, nout = (ssig[s] >> 4) & 15, n = 0, c; + expect(s_lp); + if (!is_op(s_rp)) { while (1) { c = expr(); rval(); plainuse(c); n++; if (!accept(s_comma)) break; } } + expect(s_rp); + if (n != nin) serrn(sname[s], "wrong number of inputs"); + lv = 0; lvk = LV_NONE; xty = tword; xzero = 0; + if (sflag[s] & F_FALLBACK) { fnref(sfix[s] - 1); op(O_CALL); return C_PLAIN; } + op(XOP0 + sval[s]); + if (nout == 0) lit(0); + if (nout > 1) serrn(sname[s], "leaves several values; use it from asm"); + return C_PLAIN; +} +fn primary() +{ + let n, i, s, c; + lv = 0; lvk = LV_NONE; xzero = 0; + if (tk == T_NUM) { lit(tv); xzero = tv == 0; xty = tint; next(); return C_PLAIN; } + if (tk == T_STR) { strlit(tv); next(); xty = tptr(tchar); return C_PLAIN; } + if (tk == T_OP) { + if (tv == s_lp) { next(); c = expr(); expect(s_rp); return c; } + if (tv == s_lb) return blockexpr(); + die("expected an expression"); lit(0); xty = tword; return C_PLAIN; + } + if (tk != T_ID) { die("expected an expression"); lit(0); xty = tword; return C_PLAIN; } + n = tv; + i = findloc(n); + if (i >= 0) { + next(); + if (!fchk && tracked(i)) serrn(n, "is tracked by the analyzer and can't be used in unchecked code"); + laddr(lslot[i]); + xty = ltype[i]; + if (larr[i]) return C_PLAIN; + lv = 1; lvk = LV_LOCAL; lvi = i; + if (tracked(i)) return C_VAR + i; + return C_PLAIN; + } + s = lookup(n); + if (s >= 0 && skind[s] == K_KW) { + if (!(sflag[s] & F_EXPR)) dien(n, "can't be used inside an expression"); + return kwcall(s, 1); + } + if (s >= 0 && skind[s] == K_TYPE) { dien(n, "is a type, not a value"); lit(0); xty = tword; return C_PLAIN; } + next(); + xty = tword; + if (s >= 0 && skind[s] == K_CONST) { lit(sval[s]); xty = tint; xzero = sval[s] == 0; return C_PLAIN; } + if (s >= 0 && skind[s] == K_GVAR) { lit(sval[s]); if (stype[s]) xty = stype[s]; lv = 1; lvk = LV_GLOBAL; return C_PLAIN; } + if (s >= 0 && skind[s] == K_GARR) { lit(sval[s]); if (stype[s]) xty = stype[s]; return C_PLAIN; } + if (s >= 0 && skind[s] == K_ENH) return enhcall(s); + if (s < 0) s = fwdfn(n); + if (is_op(s_lt) && (sflag[s] & F_GEN)) { // f(...) + next(); ngbind = 0; + while (1) { if (ngbind >= 8) die("too many type arguments"); gbind[ngbind] = ptype(); ngbind++; if (!accept(s_comma)) break; } + closeangle(); + if (!is_op(s_lp)) die("expected ( after the type arguments"); + } + if (is_op(s_lp)) return call(s); + fnref(s); + if (stype[s]) xty = stype[s]; + return C_PLAIN; +} +// ++ and -- on the address at the top of the stack +fn incdec(post, dlt) +{ + if (!lv) die("++ and -- need something to assign to"); + if (lvk == LV_LOCAL && fchk && tracked(lvi)) cerrn(lvi, "owns an object and can't be incremented"); + tcneed(isnum(xty) || xty[0] == TY_PTR || xty == tword, "++ and -- need a number or a pointer", xty); + lv = 0; lvk = LV_NONE; xzero = 0; + lit(0); op(O_PICK); op(O_LD); + if (post) { lit(0); op(O_PICK); lit(dlt); op(O_ADD); lit(2); op(O_PICK); op(O_ST); } + else { lit(dlt); op(O_ADD); lit(0); op(O_PICK); lit(2); op(O_PICK); op(O_ST); } + op(O_SWAP); op(O_DROP); + return C_PLAIN; +} +// p.x and p->x: a field of a struct, or of the struct a pointer points at +fn field(c) +{ + let n, st, i, b = -1; + next(); + n = name(); + rval(); + if (c == C_FRESH) cerr("use a field of a new object only after binding it to a name"); + else if (c >= C_VAR) { touch(c - C_VAR); b = c - C_VAR; } + st = xty; + if (st[0] == TY_PTR) st = st[4]; + if (st[0] != TY_STRUCT) { + if (!panic && !dry) { errat(); putc('.'); puts(n); puts(": that's a "); tprint(xty); puts(", not a struct or a pointer to one"); errend(); } + xty = tword; lv = 1; lvk = LV_OTHER; return C_PLAIN; + } + i = tfindfield(st, n); + if (i < 0) { + if (!panic && !dry) { errat(); tprint(st); puts(" has no field "); puts(n); errend(); } + xty = tword; lv = 1; lvk = LV_OTHER; return C_PLAIN; + } + if (st[4][i * 3 + 2]) { lit(st[4][i * 3 + 2]); op(O_ADD); } + xty = st[4][i * 3 + 1]; + lv = 1; xzero = 0; + if (b >= 0) { lvk = LV_ELEM; lvi = b; } else lvk = LV_OTHER; + return C_PLAIN; +} +fn postfix() +{ + let c = primary(), b, n, t, et, ft, bs[8]; + while (1) { + if (tk == T_ID && tv == s_as) { // e as T: same bits, another type + next(); t = ptype(); rval(); xty = t; lvk = LV_NONE; + continue; + } + if (tk != T_OP) return c; + if (tv == s_lk) { + next(); + rval(); + b = -1; + if (c == C_FRESH) cerr("index a new object only after binding it to a name"); + else if (c >= C_VAR) { touch(c - C_VAR); b = c - C_VAR; } + tcneed(isptr(xty) || xty == tword, "only pointers and arrays can be indexed", xty); + et = telem(xty); + c = expr(); rval(); plainuse(c); + tcneed(isnum(xty) || xty == tword, "an index has to be a number", xty); + expect(s_rk); + if (tsz(et) != 1) { lit(tsz(et)); op(15); } + op(O_ADD); + lv = 1; xty = et; xzero = 0; + if (b >= 0) { lvk = LV_ELEM; lvi = b; } else lvk = LV_OTHER; + c = C_PLAIN; + continue; + } + if (tv == s_dot || tv == s_arrow) { c = field(c); continue; } + if (tv == s_lp) { // call through a value + rval(); plainuse(c); + ft = 0; + if (xty[0] == TY_FN) ft = xty; + else tcneed(xty == tword, "only functions can be called", xty); + wfill(bs, 0, 8); + n = args(0, ft, bs); + lit(n); op(O_PICK); op(O_CALL); op(O_SWAP); op(O_DROP); + lv = 0; lvk = LV_NONE; c = C_PLAIN; xzero = 0; + xty = tword; + if (ft) xty = subst(ft[4], bs); + continue; + } + if (tv == s_inc) { next(); c = incdec(1, 1); continue; } + if (tv == s_dec) { next(); c = incdec(1, -1); continue; } + return c; + } + return c; +} +fn unary() +{ + let c, un, t; + if (tk == T_ID && (tv == s_u || tv == s_m || tv == s_d)) { // u*x, d*x, m*x: recast + t = S_M; if (tv == s_u) t = S_U; if (tv == s_d) t = S_D; + next(); expect(s_star); + c = unary(); rval(); + lvk = LV_NONE; + return recast(c, t); + } + if (tk == T_OP) { + if (tv == s_star) { + next(); c = unary(); rval(); plainuse(c); + tcneed(xty[0] == TY_PTR || xty == tword, "only pointers can be dereferenced", xty); + xty = telem(xty); + lv = 1; lvk = LV_OTHER; xzero = 0; + return C_PLAIN; + } + if (tv == s_amp) { + next(); c = unary(); + if (!lv && !isagg(xty)) die("& needs something addressable"); + if (c >= C_VAR) touch(c - C_VAR); + lv = 0; lvk = LV_NONE; xzero = 0; + xty = tptr(xty); + return C_PLAIN; + } + if (tv == s_inc) { next(); unary(); return incdec(0, 1); } + if (tv == s_dec) { next(); unary(); return incdec(0, -1); } + un = unfind(tv); + if (un >= 0) { + next(); c = unary(); rval(); plainuse(c); + emitseq(useq[un], ulen[un]); + if (un <= 1) xty = tbool; + else if (xty != tword) { tcneed(isnum(xty), "this operator needs a number", xty); xty = tint; } + lv = 0; lvk = LV_NONE; xzero = 0; + return C_PLAIN; + } + } + return postfix(); +} +fn bin(minp) +{ + let c = unary(), b, p, a, j, lt; + while (tk == T_OP) { + b = binfind(tv); + if (b < 0) return c; + p = bprec[b]; + if (p < minp) return c; + next(); rval(); plainuse(c); + lt = xty; + if (blen[b] == -1) { // && : 0 if the left side is 0, else the right side as 0 or 1 + a = hole(); op(O_JZ); + c = bin(p + 1); rval(); plainuse(c); + emitseq(useq[1], ulen[1]); // !! normalizes + j = hole(); op(O_JMP); + setlit(a, here); lit(0); setlit(j, here); + } else if (blen[b] == -2) { // || : 1 if the left side isn't 0, else the right side as 0 or 1 + a = hole(); op(O_JZ); + lit(1); j = hole(); op(O_JMP); + setlit(a, here); + c = bin(p + 1); rval(); plainuse(c); + emitseq(useq[1], ulen[1]); + setlit(j, here); + } else { + c = bin(p + 1); rval(); plainuse(c); + emitseq(bseq[b], blen[b]); + } + xty = bintype(b, lt, xty); + lv = 0; lvk = LV_NONE; c = C_PLAIN; xzero = 0; + } + return c; +} +// [addr v] -> [v], storing v at addr +fn storekeep() { op(O_SWAP); lit(1); op(O_PICK); op(O_SWAP); op(O_ST); return 0; } +// c ? a : b +fn ternary(c) +{ + let a, b, s0, sa, livea, live0 = live, n0 = nloc, ca, cb, ta; + rval(); plainuse(c); + next(); + a = hole(); op(O_JZ); + s0 = snap_push(); + ca = expr(); rval(); ta = xty; + if (ca != C_FRESH) plainuse(ca); + livea = live; sa = snap_push(); + b = hole(); op(O_JMP); + expect(s_colon); + setlit(a, here); + snap_restore(s0); live = live0; + cb = expr(); rval(); + if (cb != C_FRESH) plainuse(cb); + join2(sa, livea, b, live, n0, "ends the two sides of ?: in different states"); + snap_pop(s0); + if (!compat(ta, xty) && !compat(xty, ta)) tcerr("the two sides of ?:", ta, xty); + if (ta != tword) xty = ta; + lv = 0; lvk = LV_NONE; xzero = 0; + if (ca == C_FRESH && cb == C_FRESH) return C_FRESH; + if (ca == C_FRESH || cb == C_FRESH) cerr("both sides of ?: have to be new objects, or neither"); + return C_PLAIN; +} +fn expr() +{ + let c = bin(1), k, i, a, rc, lt; + if (tk != T_OP) return c; + if (tv == s_q) return ternary(c); + if (tv == s_eq) { + if (!lv) die("this can't be assigned to"); + k = lvk; i = lvi; lt = xty; + if (isagg(lt)) serr("a whole struct or array can't be assigned; assign its fields"); + next(); + rc = expr(); rval(); + if (!compat(lt, xty)) tcerr("assignment", lt, xty); + storekeep(); + lv = 0; lvk = LV_NONE; xty = lt; xzero = 0; + return assigncheck(k, i, rc); + } + a = asgfind(tv); + if (a >= 0) { + if (!lv) die("this can't be assigned to"); + if (lvk == LV_LOCAL && fchk && tracked(lvi)) cerrn(lvi, "owns an object; compound assignment would lose it"); + lt = xty; + next(); + lit(0); op(O_PICK); op(O_LD); + lv = 0; + rc = expr(); rval(); plainuse(rc); + emitseq(bseq[abin[a]], blen[abin[a]]); + k = bintype(abin[a], lt, xty); + if (!compat(lt, k)) tcerr("compound assignment", lt, k); + storekeep(); + lv = 0; lvk = LV_NONE; xty = lt; xzero = 0; + return C_PLAIN; + } + return c; +} +// does the current token start a statement rather than an expression? +fn stmtstart() +{ + let s; + if (tk == T_OP) return tv == s_semi || tv == s_lb || tstart(); + if (tk != T_ID || findloc(tv) >= 0) return 0; + if (tstart()) return 1; + s = lookup(tv); + return s >= 0 && skind[s] == K_KW && !(sflag[s] & F_EXPR); +} +// { stmts; tail } as an expression: its value is the tail, or 0 +fn blockexpr() +{ + let save = nloc, c = C_PLAIN, tail = 0, t = tint; + next(); + fdepth++; bxdepth++; + while (!is_op(s_rb)) { + if (tk == T_EOF) die("missing }"); + if (stmtstart()) { stmt(); continue; } + c = expr(); rval(); + if (accept(s_semi)) { op(O_DROP); discard(c); c = C_PLAIN; continue; } + if (!is_op(s_rb)) die("expected ; or } after an expression"); + tail = 1; t = xty; + } + next(); + if (tail) c = handup(c); else lit(0); + block_close(save); + fdepth--; bxdepth--; + lv = 0; lvk = LV_NONE; xty = t; xzero = 0; + return c; +} + +// ---- constant expressions ---- +fn cprim() +{ + let v, s; + if (tk == T_NUM) { v = tv; next(); return v; } + if (accept(s_minus)) return 0 - cprim(); + if (tk == T_OP && tv == uname[2]) { next(); return ~cprim(); } + if (accept(s_lp)) { v = cexpr(); expect(s_rp); return v; } + if (tk == T_ID) { + s = lookup(tv); + if (s >= 0 && (skind[s] == K_CONST || skind[s] == K_GARR || (skind[s] == K_FN && (sflag[s] & F_DEF)) || skind[s] == K_GVAR)) { next(); return sval[s]; } + } + die("expected a constant"); + return 0; +} +fn cexpr() +{ + let v = cprim(), o; + while (tk == T_OP) { + o = tv; + if (o == bname[3]) { next(); v = v + cprim(); } + else if (o == bname[4]) { next(); v = v - cprim(); } + else if (o == bname[0]) { next(); v = v * cprim(); } + else if (o == bname[15]) { next(); v = v | cprim(); } + else if (o == bname[13]) { next(); v = v & cprim(); } + else if (o == bname[5]) { next(); v = v << cprim(); } + else if (o == bname[6]) { next(); v = v >> cprim(); } + else return v; + } + return v; +} + +// ---- statements ---- +// after a syntax error: skip to the end of the statement +fn resync() +{ + while (tk != T_EOF && !is_op(s_semi) && !is_op(s_rb)) next(); + if (is_op(s_semi)) next(); + panic = 0; + return 0; +} +fn stmt() +{ + let s, c; + if (panic) return resync(); + if (tk == T_ID && findloc(tv) < 0) { + s = lookup(tv); + if (s >= 0 && skind[s] == K_KW && (sflag[s] & F_STMT)) { kwcall(s, 0); return 0; } + } + if (tstart()) { localdecl(1); return 0; } + if (tk == T_OP) { + if (tv == s_semi) { next(); return 0; } + if (tv == s_lb) { block(); return 0; } + } + c = expr(); rval(); op(O_DROP); + expect(s_semi); + discard(c); + return 0; +} +fn block() +{ + let save = nloc; + expect(s_lb); + fdepth++; + while (!is_op(s_rb)) { if (tk == T_EOF) die("missing }"); stmt(); } + block_close(save); // before the }, so its line is the one reported + next(); + fdepth--; + return 0; +} +// the body of if/while/for: its own block, even without braces +fn substmt() +{ + let save = nloc; + if (is_op(s_lb)) return block(); + fdepth++; + stmt(); + block_close(save); + fdepth--; + return 0; +} +// let a = e, b[10]; or, typed: int a = 1, b; *node.m p = new(node); .u r; +// let infers pointer, struct and function types from the initializer and +// keeps numbers as plain words. +fn localdecl(typed) +{ + let n, i, c, k, t = 0, tg = 0; + if (typed) { dtype(); t = dty; tg = dtag; if (t == tvoid) serr("a variable can't be void"); } + while (1) { + n = name(); + i = newloc(n); + ltag[i] = tg; + if (accept(s_lk)) { + k = cexpr(); expect(s_rk); + if (!t) t = tword; + larr[i] = 1; ltype[i] = tarr(t, k); fslots = fslots + tsz(ltype[i]); + } else if (t && isagg(t)) { larr[i] = 1; ltype[i] = t; fslots = fslots + tsz(t); } + else { fslots++; if (t) ltype[i] = t; } + if (accept(s_eq)) { + if (larr[i]) serr("an array or a struct can't be initialized with ="); + c = expr(); rval(); laddr(lslot[i]); op(O_ST); + if (t) { if (!compat(t, xty)) tcerr("initializer", t, xty); } + else if (!isnum(xty)) ltype[i] = xty; + assigncheck(LV_LOCAL, i, c); + } + if (!accept(s_comma)) break; + } + expect(s_semi); + return 0; +} +fn kw_var() { stmtonly(); next(); return localdecl(0); } +fn kw_if() +{ + let a, b, c, s0, sa, live0, livea, n0 = nloc, nt, cj; + stmtonly(); + next(); + expect(s_lp); c = expr(); rval(); plainuse(c); expect(s_rp); + a = hole(); op(O_JZ); + live0 = live; + s0 = snap_push(); + substmt(); + livea = live; sa = snap_push(); + if (is_id(s_else)) { + next(); + b = hole(); op(O_JMP); + setlit(a, here); + snap_restore(s0); live = live0; + substmt(); + join2(sa, livea, b, live, n0, "ends the branches of an if in different states"); + } else { + // the path that skips the then branch still has the states from before it + njps = 0; if (livea) jadd(R_SNAP + sa); if (live0) jadd(R_SNAP + s0); + join(n0, "ends the branches of an if in different states"); + if (livea) fixups(R_SNAP + sa, n0, 1); + nt = 0; if (live0) nt = fixups(R_SNAP + s0, n0, 0); + if (nt) { cj = hole(); op(O_JMP); setlit(a, here); fixups(R_SNAP + s0, n0, 1); setlit(cj, here); } + else setlit(a, here); + live = livea || live0; + } + snap_pop(s0); + return 0; +} +// loop frame: 0 continue target 1 break chain 2 depth 3 snapshot 4 block-expression depth +// loop frame: 0 continue target (-1 for a switch) 2 depth 3 snapshot at the top +// 4 block-expression depth 5 locals at the start 6 its first exit record +fn loop_push(cont, s0) +{ + let f = R_LOOP + lsp * 8; + if (lsp >= 60) fatal("loops nested too deeply"); + f[0] = cont; f[1] = 0; f[2] = fdepth; f[3] = s0; f[4] = bxdepth; f[5] = nloc; f[6] = extop; + lsp++; + return 0; +} +// the end of a loop or switch: join its exits (the fall-out path sc through +// the jump at a, if there is one, and every break) and lay out each path's +// fixups on its way to the end +fn loop_end(a, sc) +{ + let f, p, e, n0, outs = 0; + lsp--; + f = R_LOOP + lsp * 8; + n0 = f[5]; + njps = 0; + if (sc >= 0) jadd(R_SNAP + sc); + p = R_EXIT + f[6]; e = R_EXIT + extop; + while (p < e) { jadd(p + 1); p = p + 3 + 3 * (p[2] - p[1]); } + join(n0, "leaves the loop in different states on different paths"); + live = njps > 0; + p = R_EXIT + f[6]; + while (p < e) { + if (fixups(p + 1, n0, 0)) { setlit(p[0], here); fixups(p + 1, n0, 1); outs = fixnew(hole(), outs); op(O_JMP); } + else outs = fixnew(p[0], outs); + p = p + 3 + 3 * (p[2] - p[1]); + } + if (a >= 0) setlit(a, here); + if (sc >= 0) fixups(R_SNAP + sc, n0, 1); + fixall(outs, here); + extop = f[6]; + return 0; +} +fn kw_while() +{ + let c0 = here, a, s0, sc = -1, c, inf = 0; + stmtonly(); + next(); + s0 = snap_push(); + expect(s_lp); + if (tk == T_NUM && tv) { peek(); if (pk_tk == T_OP && pk_tv == s_rp) inf = 1; } // while (1): no way out but break + c = expr(); rval(); plainuse(c); expect(s_rp); + a = hole(); op(O_JZ); + if (!inf && live) sc = snap_push(); + loop_push(c0, s0); + substmt(); + backedge(s0); + jmp(c0); + loop_end(a, sc); + snap_pop(s0); + return 0; +} +fn kw_for() +{ + let save = nloc, s0, sc = -1, c0, a, b, st, c, inf = 0; + stmtonly(); + next(); + expect(s_lp); + fdepth++; + if (is_id(s_var)) kw_var(); + else if (tstart()) localdecl(1); + else { if (!is_op(s_semi)) { c = expr(); rval(); op(O_DROP); discard(c); } expect(s_semi); } + s0 = snap_push(); + c0 = here; + if (!is_op(s_semi)) { + if (tk == T_NUM && tv) { peek(); if (pk_tk == T_OP && pk_tv == s_semi) inf = 1; } + c = expr(); rval(); plainuse(c); + } else { lit(1); inf = 1; } + expect(s_semi); + a = hole(); op(O_JZ); + if (!inf && live) sc = snap_push(); + b = hole(); op(O_JMP); + st = here; + if (!is_op(s_rp)) { c = expr(); rval(); op(O_DROP); discard(c); } + expect(s_rp); + jmp(c0); + setlit(b, here); + loop_push(st, s0); + substmt(); + backedge(s0); + jmp(st); + loop_end(a, sc); + snap_pop(s0); + block_close(save); + fdepth--; + return 0; +} +fn kw_break() +{ + let f; + stmtonly(); + next(); expect(s_semi); + if (lsp <= loopbase) { serr("break outside a loop or a switch"); return 0; } + f = R_LOOP + (lsp - 1) * 8; + if (f[4] != bxdepth) { serr("break can't leave a block expression"); return 0; } + exit_vars(f[2] + 1, 0); + if (live) exit_rec(hole()); else hole(); + op(O_JMP); + live = 0; + return 0; +} +fn kw_continue() +{ + let j = lsp - 1, f; + stmtonly(); + next(); expect(s_semi); + while (j >= loopbase && R_LOOP[j * 8] == -1) j--; // past any switch + if (j < loopbase) { serr("continue outside a loop"); return 0; } + f = R_LOOP + j * 8; + if (f[4] != bxdepth) { serr("continue can't leave a block expression"); return 0; } + backedge(f[3]); + exit_vars(f[2] + 1, 0); + jmp(f[0]); + live = 0; + return 0; +} +// switch (e) { case 1, 2: ... case 'x': ... default: ... } +// cases don't fall through: each one ends where the next begins, and break +// leaves the switch early. case labels are constants. +fn casebody(s0, live0) +{ + let save = nloc; + snap_restore(s0); live = live0; + fdepth++; + while (!is_op(s_rb) && !is_id(s_case) && !is_id(s_default)) { if (tk == T_EOF) { die("missing }"); break; } stmt(); } + block_close(save); + fdepth--; + if (live) { exit_rec(hole()); op(O_JMP); } // the end of a case leaves the switch + live = 0; + return 0; +} +fn kw_switch() +{ + let save = nloc, tmp, c, s0, live0, defent = -1, skip, nxt, bj, v, a = -1; + stmtonly(); + next(); + expect(s_lp); c = expr(); rval(); plainuse(c); expect(s_rp); + fdepth++; + tmp = newloc(0); fslots++; // a hidden local holds the value + laddr(lslot[tmp]); op(O_ST); + live0 = live; + s0 = snap_push(); + loop_push(-1, s0); + expect(s_lb); + while (!is_op(s_rb)) { + if (tk == T_EOF) { die("missing }"); break; } + if (is_id(s_case)) { + next(); + bj = 0; + while (1) { + v = cexpr(); + laddr(lslot[tmp]); op(O_LD); lit(v); op(O_SUB); + bj = fixnew(hole(), bj); op(O_JZ); + if (!accept(s_comma)) break; + } + expect(s_colon); + nxt = hole(); op(O_JMP); + fixall(bj, here); + casebody(s0, live0); + setlit(nxt, here); + } else if (is_id(s_default)) { + next(); expect(s_colon); + if (defent >= 0) serr("a switch has one default"); + skip = hole(); op(O_JMP); // only reached when nothing matched + defent = here; + casebody(s0, live0); + setlit(skip, here); + } else die("expected case or default"); + } + next(); + snap_restore(s0); live = live0; + if (defent >= 0) { jmp(defent); loop_end(-1, -1); } + else { a = hole(); op(O_JMP); loop_end(a, s0); } // nothing matched: out with the states from the top + snap_pop(s0); + block_close(save); + fdepth--; + return 0; +} +fn kw_return() +{ + let c; + stmtonly(); + next(); + if (!infn) serr("return outside a function"); + if (bxdepth) serr("return can't leave a block expression"); + if (is_op(s_semi)) { lit(0); c = C_PLAIN; } + else { + c = expr(); rval(); + if (fty == tvoid) tcerr("return value", tvoid, xty); + else if (fty && !compat(fty, xty)) tcerr("return value", fty, xty); + } + expect(s_semi); + retcheck(c); + exit_vars(1, 0); + epichain = fixnew(here, epichain); + lit(MW - 1); op(O_JMP); + live = 0; + return 0; +} +fn kw_unchecked() +{ + let old = fchk; + stmtonly(); + next(); + fchk = 0; + substmt(); + fchk = old; + return 0; +} + +// ---- asm blocks: raw vm words, one per token ---- +// numbers push, -n pushes a negative number, #n emits a raw word, one-char +// tokens from the mnemonic table are opcodes, names push the address of a +// variable or function, :name defines a label for this block. +fn asmblock() +{ + let nl = 0, nr = 0, i, k, c, s, ln = R_ASM, la = R_ASM + 64, rn = R_ASM + 128, ra = R_ASM + 384; + expect(s_lb); + while (!is_op(s_rb)) { + if (tk == T_EOF) die("unterminated asm block"); + if (tk == T_NUM) { lit(tv); next(); continue; } + if (tk == T_STR) { strlit(tv); next(); continue; } + if (is_op(s_hash)) { next(); if (tk != T_NUM) die("# needs a number"); emit(tv); next(); continue; } + if (is_op(s_minus)) { next(); if (tk == T_NUM && tglue) { lit(0 - tv); next(); } else op(O_SUB); continue; } + if (is_op(s_colon)) { next(); if (nl >= 64) fatal("too many asm labels"); ln[nl] = name(); la[nl] = here; nl++; continue; } + if (tv[0] && !tv[1] && (c = mnemidx(tv[0])) >= 0) { op(c); next(); continue; } + if (tk != T_ID) die("bad token in asm"); + i = findloc(tv); + if (i >= 0) { lused[i] = 1; laddr(lslot[i]); next(); continue; } + k = -1; i = 0; + while (i < nl) { if (ln[i] == tv) k = i; i++; } + if (k >= 0) { lit(la[k]); next(); continue; } + s = lookup(tv); + if (s >= 0 && skind[s] == K_ENH) { op(XOP0 + sval[s]); next(); continue; } + if (s >= 0 && skind[s] != K_FN && skind[s] != K_KW) { lit(sval[s]); next(); continue; } + if (s >= 0 && skind[s] == K_FN) { fnref(s); next(); continue; } + if (nr >= 250) fatal("too many asm label references"); + rn[nr] = tv; ra[nr] = hole(); nr++; next(); // a label further down this block + } + next(); + i = 0; + while (i < nr) { + k = -1; c = 0; + while (c < nl) { if (ln[c] == rn[i]) k = c; c++; } + if (k < 0) serrn(rn[i], "unknown name in asm"); + setlit(ra[i], la[k]); + i++; + } + return 0; +} +fn kw_asm() { next(); asmblock(); xcls = C_PLAIN; return 0; } + +// ---- quote: code as data ---- +// a token list is an object: [n] then kind, value, line for each token. names, +// strings and operators are interned pointers. +fn grab() -> .u +{ + let n = 0, depth = 0, q; + if (!is_op(s_lb)) die("expected {"); + next(); + while (1) { + if (tk == T_EOF) die("unterminated quote"); + if (is_op(s_lb)) depth++; + if (is_op(s_rb)) { if (!depth) break; depth--; } + if (n >= 2700) fatal("quote too long"); + R_QBUF[n * 3] = tk; R_QBUF[n * 3 + 1] = tv; R_QBUF[n * 3 + 2] = tline; n++; + next(); + } + next(); + q = new(n * 3 + 1); + q[0] = n; + wcopy(q + 1, R_QBUF, n * 3); + return u*q; +} +unchecked fn qnew(src) -> .u { let n = src[0] * 3 + 1, q = new(n); wcopy(q, src, n); return q; } +fn qdup(.m q) -> .u { let n = q[0] * 3 + 1, r = new(n); wcopy(r, q, n); return u*r; } +fn wcopyto(.m q, n) { let i = 0; while (i < n) { emit(q[i]); i++; } return 0; } +fn kw_quote() +{ + let q, j, a, n; + next(); + q = grab(); + j = hole(); op(O_JMP); + a = here; + n = q[0] * 3 + 1; + wcopyto(q, n); + free(d*q); + setlit(j, here); + lit(a); + callname(s_qnew); + xcls = C_FRESH; + return 0; +} +// tokens of a string, as a token list +fn tokens(s) -> .u +{ + let n = 0, q; + src_push(2, s, 0, 0); + next(); + while (tk != T_EOF) { + if (n >= 2700) fatal("too many tokens"); + R_QBUF[n * 3] = tk; R_QBUF[n * 3 + 1] = tv; R_QBUF[n * 3 + 2] = tline; n++; + next(); + } + src_pop(); + q = new(n * 3 + 1); + q[0] = n; + wcopy(q + 1, R_QBUF, n * 3); + return u*q; +} +// feed a token list into the input: the parser reads it next, then carries +// on where it was. the list is freed once it has been read. +unchecked fn feed(.d q) { src_push(1, q, 1, 1); next(); return 0; } + +// ---- reading a unit and scanning it for signatures ---- +// a unit from stdin is read whole first (4 chars to a word), then scanned +// once for fn signatures, so a function's tags are known before its first +// call even when it's defined further down. +unchecked fn readsrc() +{ + let cap = 4090, b = new(4090), n = 0, c, nb, w; + while (!eof()) { + c = getc() & 255; + if (c == 96) break; + if ((n >> 2) + 2 >= cap) { nb = new(cap + cap + 6); wcopy(nb, b, cap); free(b); b = nb; cap = cap + cap + 6; } + w = n >> 2; + if (!(n & 3)) b[w] = 0; + b[w] = b[w] | (c << ((n & 3) * 8)); + n++; + } + b[n >> 2] = b[n >> 2] & (pow2[(n & 3) * 8] - 1); // the 0 at the end + if ((n & 3) == 0) b[n >> 2] = 0; + b[(n >> 2) + 1] = 0; + return b; +} +// skip a <...> group (type parameters) +fn pskipangle() +{ + let dp = 0; + while (tk != T_EOF) { + if (is_op(s_lt)) dp++; + else if (is_op(s_gt)) dp--; + else if (is_op(s_shr)) dp = dp - 2; + next(); + if (dp <= 0) return 0; + } + return 0; +} +// at the name after fn: read the signature's tags and record them +fn prescan_fn() +{ + let n = tv, np = 0, tags = 0, t = 0, ret = 0, dp, s; + if (tk != T_ID) return 0; + next(); + if (is_op(s_lt)) pskipangle(); + if (!is_op(s_lp)) return 0; + next(); + if (!is_op(s_rp)) { + np = 1; dp = 1; + while (tk != T_EOF) { + if (is_op(s_lp)) dp++; + else if (is_op(s_rp)) { dp--; if (!dp) break; } + else if (dp == 1 && is_op(s_comma)) { if (np <= 12) tags = tags + (t << (np + np - 2)); np++; t = 0; } + else if (dp == 1 && is_op(s_dot)) { next(); if (is_id(s_m)) t = 1; else if (is_id(s_d)) t = 2; continue; } + next(); + } + if (np <= 12) tags = tags + (t << (np + np - 2)); + } + next(); + if (accept(s_arrow)) { + while (tk != T_EOF && !is_op(s_lb) && !is_op(s_semi)) { + if (is_op(s_dot)) { next(); if (is_id(s_u)) ret = 1; continue; } + next(); + } + } + s = lookup(n); + if (s < 0 || s < ubase || skind[s] != K_FN || (sflag[s] & F_DEF)) s = fwdfn(n); + if (!(sflag[s] & F_SIG)) { ssig[s] = ret + np + np + tags * 64; sflag[s] = sflag[s] | F_SIG | F_PRE; } + return 0; +} +fn prescan(kind, obj) +{ + let depth = 0, sp = panic, sn = nerr; + dry++; + src_push(kind, obj, 0, 0); + next(); + while (tk != T_EOF) { + if (is_op(s_lb)) depth++; + else if (is_op(s_rb)) depth--; + else if (depth <= 0 && is_id(s_fn)) { next(); prescan_fn(); continue; } + next(); + } + src_pop(); + dry--; panic = sp; nerr = sn; + return 0; +} +// compile a whole unit from stdin +fn unit_stdin() +{ + let src = readsrc(); + prescan(3, src); + src_push(3, src, 0, 0); + next(); + unit_top(); + unit_end(ubase); + src_pop(); + free(src); + return 0; +} + +// ---- nested compiles ---- +// compile() and load() can run while another function is being compiled (from +// a parse word). the function context is saved, the new code is emitted in +// place with a jump around it, and then everything is put back. +fn ctx_push() +{ + let f = R_CTX + ctxsp * 24; + if (ctxsp >= 30) fatal("compiles nested too deeply"); + ctxsp++; + f[0] = nloc; f[1] = lbase; f[2] = fdepth; f[3] = fslots; f[4] = fchk; f[5] = fret; f[6] = live; + f[7] = bxdepth; f[8] = epichain; f[9] = fframe; f[10] = infn; f[11] = lsp; f[12] = loopbase; + f[13] = lv; f[14] = lvk; f[15] = lvi; f[16] = kw_expr; f[17] = xcls; f[18] = snaptop; + f[19] = fty; f[20] = ntp; f[21] = xty; wcopy(f + 22, tpn, 2); + return 0; +} +fn ctx_pop() +{ + let f; + ctxsp--; + f = R_CTX + ctxsp * 24; + nloc = f[0]; lbase = f[1]; fdepth = f[2]; fslots = f[3]; fchk = f[4]; fret = f[5]; live = f[6]; + bxdepth = f[7]; epichain = f[8]; fframe = f[9]; infn = f[10]; lsp = f[11]; loopbase = f[12]; + lv = f[13]; lvk = f[14]; lvi = f[15]; kw_expr = f[16]; xcls = f[17]; snaptop = f[18]; + fty = f[19]; ntp = f[20]; xty = f[21]; wcopy(tpn, f + 22, 2); + return 0; +} + +// ---- functions and the top level ---- +fn fn_begin(chk, ret) +{ + lbase = nloc; fdepth = 1; fslots = 0; fchk = chk && check_on; fret = ret; fty = 0; + live = 1; bxdepth = 0; epichain = 0; infn = 1; loopbase = lsp; lv = 0; lvk = LV_NONE; + return 0; +} +// fp -= frame size, then check the frame stack: past FBOT, reset fp and stop +fn prologue() +{ + let k; + lit(ZFP); op(O_LD); fframe = hole(); op(O_SUB); lit(ZFP); op(O_ST); + lit(ZFP); op(O_LD); lit(FBOT); op(O_LT); k = hole(); op(O_JZ); + lit(FTOP); lit(ZFP); op(O_ST); callname(s_fovf); + setlit(k, here); + return 0; +} +fn epilogue() +{ + lit(0); + fixall(epichain, here); + lit(ZFP); op(O_LD); lit(fslots); op(O_ADD); lit(ZFP); op(O_ST); op(O_RET); + setlit(fframe, fslots); + infn = 0; + nloc = lbase; + return 0; +} +fn body() +{ + expect(s_lb); + while (!is_op(s_rb)) { if (tk == T_EOF) die("missing } at the end of a function"); stmt(); } + unused_check(lbase); + if (fchk && live) { exit_vars(1, 1); if (fret) cerr("a -> .u function has to end with a return"); } + next(); + return 0; +} +// fn name(int a, *node.m n, .d x, b) -> *list.u { ... } +fn fndef(chk) +{ + let n, s, np = 0, tags = 0, t, ret = 0, sig, i, pn[24], pt[24], pty[24], rty = 0, typed = 0, ft; + next(); + n = name(); + ntp = 0; + if (is_op(s_lt)) { // type parameters + next(); + while (1) { if (ntp >= 8) die("too many type parameters"); tpn[ntp] = name(); ntp++; if (!accept(s_comma)) break; } + closeangle(); + typed = 1; + } + s = lookup(n); + if (s < 0 || s < ubase || skind[s] != K_FN || (sflag[s] & F_DEF)) { s = addsym(n, K_FN, 0); sflag[s] = 0; } + expect(s_lp); + if (!is_op(s_rp)) { + while (1) { + t = 0; + pty[np] = tword; + if (!is_op(s_dot) && ((tk == T_OP && (tv == s_star || tv == s_lk)) || (tk == T_ID && (tv == s_fn || istypename(tv))))) { + pty[np] = ptype(); typed = 1; + if (isagg(pty[np])) serr("structs and arrays are passed by pointer"); + } + if (accept(s_dot)) { // .m lends, .d takes over + if (tv == s_m) t = 1; else if (tv == s_d) t = 2; else die("a parameter tag is .m or .d"); + next(); + } + pn[np] = name(); + if (np >= 12 && t) die("only the first 12 parameters can be tagged"); + pt[np] = t; + if (np < 12) tags = tags + (t << (np + np)); + np++; + if (np >= 24) die("too many parameters"); + if (!accept(s_comma)) break; + } + } + expect(s_rp); + if (accept(s_arrow)) { + if (!is_op(s_dot)) { rty = ptype(); typed = 1; } + if (accept(s_dot)) { if (!is_id(s_u)) die("the return tag is .u"); next(); ret = 1; } + } + sig = ret + np + np + tags * 64; + if ((sflag[s] & F_SIG) && !(sflag[s] & F_PRE)) { if (ssig[s] != sig) serrn(n, "doesn't match its earlier declaration"); } + else if ((sflag[s] & F_UNTYPED) && (tags || ret)) serrn(n, "was called before its tags were declared; put a prototype above the first call"); + ssig[s] = sig; + sflag[s] = (sflag[s] | F_SIG) - (sflag[s] & F_PRE); + if (ntp) sflag[s] = sflag[s] | F_GEN; + if (typed) { + if (!rty) rty = tword; + ft = tfn(rty, np, pty, sig); + if (stype[s] && stype[s] != ft) serrn(n, "doesn't match its earlier declaration"); + stype[s] = ft; + } + if (accept(s_semi)) { ntp = 0; return s; } + if (sflag[s] & F_DEF) serrn(n, "is defined twice"); + sval[s] = here; + sflag[s] = sflag[s] | F_DEF; + fixall(sfix[s], here); sfix[s] = 0; + fn_begin(chk, ret); + if (typed) fty = rty; + prologue(); + i = 0; + while (i < np) { + t = newloc(pn[i]); fslots++; + ltype[t] = pty[i]; + if (fchk && pt[i] == 1) lstate[t] = S_BORROW; + if (fchk && pt[i] == 2) { lstate[t] = S_M; lown[t] = 1; } + i++; + } + i = np; + while (i) { i--; laddr(i); op(O_ST); } + body(); + epilogue(); + ntp = 0; + return s; +} +// let g = 1, buf[10]; int g = 3; [10]int buf; point origin; +fn gdecl(typed) +{ + let n, a, k, t = 0, s; + if (typed) { dtype(); t = dty; if (dtag) serr("tags go on locals and parameters, not globals"); if (t == tvoid) serr("a variable can't be void"); } + else next(); + while (1) { + n = name(); + if (accept(s_lk)) { + k = cexpr(); expect(s_rk); + s = addsym(n, K_GARR, here); + if (!t) t = tword; + stype[s] = tarr(t, k); + k = tsz(stype[s]); + while (k) { emit(0); k--; } + } else if (t && isagg(t)) { + s = addsym(n, K_GARR, here); stype[s] = t; + k = tsz(t); + while (k) { emit(0); k--; } + } else { + a = here; + s = addsym(n, K_GVAR, a); + if (t) stype[s] = t; + emit(0); + if (accept(s_eq)) { + if (tk == T_STR) { *(a + hdelta) = here; k = tv; while (*k) { emit(*k); k++; } emit(0); next(); } + else *(a + hdelta) = cexpr(); + } + } + if (!accept(s_comma)) break; + } + expect(s_semi); + return 0; +} +fn gconst() +{ + let n; + next(); + while (1) { n = name(); expect(s_eq); addsym(n, K_CONST, cexpr()); if (!accept(s_comma)) break; } + expect(s_semi); + return 0; +} +// enhance name(a, b) [-> n] micro "recipe" [else fn]; +// enhance name(a, b) [-> n] vm { asm } [else fn]; +// lays out the descriptor the install opcode reads, and wires it in right +// away (an image wires its own in when it boots). +fn enhdef() +{ + let n, np = 0, nout = 1, dsc, na, pay, len = 0, kind = 0, i, p, s, slot, r, pn[8]; + next(); + n = name(); + expect(s_lp); + if (!is_op(s_rp)) { while (1) { if (np == 8) die("an enhancement takes at most 8 inputs"); pn[np] = name(); np++; if (!accept(s_comma)) break; } } + expect(s_rp); + if (accept(s_arrow)) { if (tk != T_NUM || tv < 0 || tv > 8) die("expected the number of outputs"); nout = tv; next(); } + dsc = here; i = 0; while (i < 7) { emit(0); i++; } + na = here; i = 0; while (i < np) { emit(0); i++; } + i = 0; + while (i < np) { *(na + i + hdelta) = here; p = pn[i]; while (*p) { emit(*p); p++; } emit(0); i++; } + if (accept(s_micro)) { + pay = here; + if (tk != T_STR) die("expected the recipe as a string"); + while (tk == T_STR) { p = tv; while (*p) { emit(*p); p++; } next(); } + emit(0); + } else if (accept(s_vm)) { pay = here; asmblock(); op(O_RET); len = here - pay; kind = 1; } + else die("expected micro or vm"); + slot = nenh; + if (slot >= 512) fatal("all 512 enhancement slots are taken"); + nenh++; + p = dsc + hdelta; + p[0] = slot; p[1] = kind; p[2] = np; p[3] = nout; p[4] = pay; p[5] = len; p[6] = na; + s = addsym(n, K_ENH, slot); + ssig[s] = np + nout * 16 + dsc * 256; + if (accept(s_else)) { r = lookup(tv); if (r < 0) r = fwdfn(tv); next(); sfix[s] = r + 1; } + expect(s_semi); + if (!hdelta) { + r = install(dsc); + if (r < 0) { + if (!sfix[s]) serrn(n, "the enhancement didn't install (see install's codes in core.c)"); + sflag[s] = sflag[s] | F_FALLBACK; + } + } + return 0; +} +// after a syntax error at the top level: skip to something that starts a declaration +fn topsync() +{ + let depth = 0; + while (tk != T_EOF) { + if (is_op(s_lb)) depth++; + else if (is_op(s_rb)) { depth--; if (depth < 0) depth = 0; } + else if (!depth && tk == T_ID && (tv == s_fn || tv == s_var || tv == s_const || tv == s_struct || tv == s_type + || tv == s_enhance || tv == s_typecheck || tv == s_unchecked || tv == s_immediate)) break; + next(); + } + panic = 0; + return 0; +} +fn topitem() +{ + let chk = 1, imm = 0, s, k; + if (panic) return topsync(); + if (is_id(s_unchecked)) { + next(); + if (accept(s_semi)) { check_on = 0; return 0; } // unchecked; switches the analyzer off from here on + chk = 0; + } + if (is_id(s_immediate)) { next(); imm = 1; } + if (is_id(s_fn)) { + s = fndef(chk); + if (imm) { + if (hdelta) serr("parse words can't be defined while building an image"); + k = addsym(sname[s], K_KW, sval[s]); + sflag[k] = F_DEF | F_STMT | F_EXPR | F_TOP; + } + return 0; + } + if (!chk || imm) { die("expected fn"); return 0; } + if (is_id(s_var)) return gdecl(0); + if (is_id(s_struct)) { kw_struct(); return 0; } + if (is_id(s_type)) return kw_type(); + if (is_id(s_typecheck)) return kw_typecheck(); + if (is_id(s_enhance)) return enhdef(); + if (is_id(s_const)) return gconst(); + if (tk == T_ID) { + s = lookup(tv); + if (s >= 0 && skind[s] == K_KW && (sflag[s] & F_TOP)) { kwcall(s, 0); return 0; } + } + if (tstart()) return gdecl(1); + die("expected fn, let, const, struct, type or a declaration"); + return 0; +} +fn unit_top() { while (tk != T_EOF) topitem(); return 0; } +fn unit_end(from) +{ + let i = from; + panic = 0; + while (i < nsym) { + if (skind[i] == K_FN && !(sflag[i] & F_DEF) && sfix[i]) { tline = sval[i]; serrn(sname[i], "is used but never defined"); } + i++; + } + if (nerr) giveup(); + return 0; +} + +// compile a token list as the body of a new function, or (top) as top-level +// declarations. returns the entry address of the function. +unchecked fn compile_list(q, top) +{ + let hop = -1, entry = 0, from = nsym; + ctx_push(); + if (infn) { hop = hole(); op(O_JMP); } + if (top) prescan(1, q); + src_push(1, q, 0, 0); + next(); + if (top) unit_top(); + else { + entry = here; + fn_begin(1, 0); + prologue(); + while (tk != T_EOF) stmt(); + unused_check(lbase); + if (fchk && live) exit_vars(1, 1); + epilogue(); + } + src_pop(); + unit_end(from); + if (hop >= 0) setlit(hop, here); + if (here > ptop) ptop = here; + ctx_pop(); + return entry; +} +fn compile(q) { return compile_list(q, 0); } +fn load(q) { compile_list(q, 1); return 0; } +fn eval(q) { let f = compile_list(q, 0); return f(); } +fn evals(s) { let q = tokens(s), r = eval(q); free(d*q); return r; } + +// ---- symbol blob ---- +// an image ends with its own symbol table so the genome knows its names: +// kind, value, signature, name chars, 0 ... then a 0 kind. +fn blob_emit(from) +{ + let i = from, p; + while (i < nsym) { + if ((skind[i] >= K_FN && skind[i] <= K_CONST) || skind[i] == K_ENH) { + emit(skind[i]); emit(sval[i]); emit(ssig[i]); + p = sname[i]; + while (*p) { emit(*p); p++; } + emit(0); + } + i++; + } + emit(0); + return 0; +} +fn blob_load(p) +{ + let s, n; + while (*p) { + n = intern(p + 3); + s = addsym(n, p[0], p[1]); + ssig[s] = p[2]; + sflag[s] = F_DEF | F_SIG; + if (p[0] == K_ENH) { if (p[1] >= nenh) nenh = p[1] + 1; install(p[2] >> 8); } + p = p + 3 + strlen(p + 3) + 1; + } + return 0; +} +// compile the rest of stdin as a genome image that will run at org, writing +// it to dst. only the source's own names are visible while it compiles. +// returns the image length in words. +fn build_image(org, dst) +{ + let sh = here, sd = hdelta, sl = hlimit, sub = ubase, svl = vislo, sns = nsym, sen = nenh, n; + ubase = nsym; vislo = nsym; nenh = 0; + here = org; hdelta = dst - org; hlimit = org + 0x30000; + lit(FTOP); lit(ZFP); op(O_ST); fnref(fwdfn(s_start)); op(O_JMP); emit(0); emit(0); + unit_stdin(); + *(dst + 5) = here; + blob_emit(ubase); + *(dst + 6) = here; + n = here - org; + popsyms(sns); + ubase = sub; vislo = svl; here = sh; hdelta = sd; hlimit = sl; nenh = sen; + return n; +} + +// ---- setup ---- +fn gc_roots() +{ + markrange(0, ptop); + if (!hdelta && here > ptop) markrange(ptop, here); // globals of a unit still compiling + markrange(GORG, GORG[6]); + markrange(R_SRC, R_SRC + 1024); + markrange(*ZFP, FTOP); + markrange(DSLO, DSHI); + return 0; +} +fn zc_init() +{ + pooltop = R_POOL; + wfill(R_IH, 0, IHN); + nsym = 0; nfixu = 0; fixfree = 0; nbin = 0; nun = 0; nasg = 0; nkop = 0; seqtop = 0; + lsp = 0; ctxsp = 0; snaptop = 0; nloc = 0; lbase = 0; + s_lp = intern("("); s_rp = intern(")"); s_lb = intern("{"); s_rb = intern("}"); + s_lk = intern("["); s_rk = intern("]"); s_comma = intern(","); s_semi = intern(";"); + s_eq = intern("="); s_arrow = intern("->"); s_inc = intern("++"); s_dec = intern("--"); + s_star = intern("*"); s_amp = intern("&"); s_colon = intern(":"); s_hash = intern("#"); + s_minus = intern("-"); s_dot = intern("."); s_q = intern("?"); s_case = intern("case"); s_default = intern("default"); s_andand = intern("&&"); s_oror = intern("||"); + s_fn = intern("fn"); s_var = intern("let"); s_const = intern("const"); s_else = intern("else"); + s_as = intern("as"); s_unchecked = intern("unchecked"); s_immediate = intern("immediate"); + s_m = intern("m"); s_d = intern("d"); s_u = intern("u"); + s_start = intern("start"); s_main = intern("main"); s_free = intern("free"); + s_own = intern("own"); s_qnew = intern("qnew"); s_fovf = intern("frame_overflow"); + s_enhance = intern("enhance"); s_micro = intern("micro"); s_vm = intern("vm"); + return 0; +} +// operators and keywords. these are plain tables in memory: a program can +// add operators, change precedences or replace keywords with its own. +fn zc_tables() +{ + add_unop("!", "0 $ 0 ~ - | 31 > 1 +"); // index 0 + add_unop("!!", "0 $ 0 ~ - | 31 > 0 ~ -"); // index 1: normalize to 0 or 1 + add_unop("~", "-1 ^"); // index 2 + add_unop("-", "0 ~ -"); + add_binop("*", 10, "*"); // the first few indexes are used by cexpr + add_binop("/", 10, "idiv"); // native divide, wired in by rt.zc + add_binop("%", 10, "imod"); + add_binop("+", 9, "+"); // 3 + add_binop("-", 9, "-"); // 4 + add_binop("<<", 8, "ishl"); // 5 + add_binop(">>", 8, ">"); // 6 + add_binop("<", 7, "<"); + add_binop(">", 7, "~ <"); + add_binop("<=", 7, "~ < 1 ^"); + add_binop(">=", 7, "< 1 ^"); + add_binop("==", 6, "- 0 $ 0 ~ - | 31 > 1 +"); + add_binop("!=", 6, "- 0 $ 0 ~ - | 31 > 0 ~ -"); + add_binop("&", 5, "&"); // 13 + add_binop("^", 4, "^"); + add_binop("|", 3, "|"); // 15 + add_binop("&&", -1, 0); + add_binop("||", -2, 0); + addkop(s_eq); addkop(s_arrow); addkop(s_inc); addkop(s_dec); + add_kw("let", kw_var, F_STMT); + add_kw("if", kw_if, F_STMT); + add_kw("while", kw_while, F_STMT); + add_kw("for", kw_for, F_STMT); + add_kw("switch", kw_switch, F_STMT); + add_kw("break", kw_break, F_STMT); + add_kw("continue", kw_continue, F_STMT); + add_kw("return", kw_return, F_STMT); + add_kw("unchecked", kw_unchecked, F_STMT); + add_kw("asm", kw_asm, F_STMT | F_EXPR); + add_kw("quote", kw_quote, F_EXPR); + add_kw("sizeof", kw_sizeof, F_EXPR); + add_kw("typeof", kw_typeof, F_EXPR); + return 0; +} +fn start() +{ + let s, f; + heap_init(); + zc_init(); + blob_load(GORG[5]); + zc_tables(); + types_init(); + here = 0; hdelta = 0; hlimit = HEAP_LO; ptop = 0; + ubase = nsym; vislo = 0; + unit_stdin(); + if (here > ptop) ptop = here; + s = lookup(s_main); + if (s >= 0 && skind[s] == K_FN && (sflag[s] & F_DEF)) { f = sval[s]; f(); } + halt(); + return 0; +} diff --git a/docs/history/13-gap-fixes/code/src/rt.zc b/docs/history/13-gap-fixes/code/src/rt.zc new file mode 100644 index 0000000..55b8976 --- /dev/null +++ b/docs/history/13-gap-fixes/code/src/rt.zc @@ -0,0 +1,240 @@ +// ============================================================================ +// rt.zc: the runtime half of the genome. memory map, i/o, arithmetic helpers, +// the heap (size-class free lists) and the mark-sweep collector. +// ============================================================================ + +// ---- memory map (words) ---- +const MW = 0x100000; // 1m words of memory +const HEAP_LO = 0x40000; // heap: 0x40000 .. 0xA0000 +const HEAP_HI = 0xA0000; +const ZFP = 0xA0000; // frame pointer word, used by every prologue +const GORG = 0xC0000; // the genome (this code) lives here +const FBOT = 0xE0000; // frame stack: FBOT .. FTOP, grows down +const FTOP = 0xF0000; +const RS0 = 0xF0000; // return stack grows up from here +const MARK0 = 0xF8000; // mark stack for the collector +const MARKN = 0x4000; +const DSLO = 0xFC000; // data stack: DSLO .. MW-16, grows down +const DSHI = 0xFFFF0; +const MAGIC = 0x2B7E1516; + +// ---- virtual enhancements ---- +// install(desc) wires a custom opcode into the cpu (the % opcode). these three +// give the language native division and left shift (zdiv, zmod and zshl +// below are the same thing done in software). they come first, since the +// compiler resolves / % and << to them by name. +fn install(desc) { return asm { desc @ % }; } +enhance idiv(a, b) micro "a = sdiv a b"; +enhance imod(a, b) micro "a = srem a b"; +enhance ishl(a, b) micro "a = shl a b"; + +// ---- i/o ---- +fn putc(ch) { asm { ch @ . } return 0; } +fn getc() { return asm { , }; } +fn eof() { return asm { ? }; } +fn halt() { asm { q } return 0; } +fn puts(s) { let i = 0; while (s[i]) { putc(s[i]); i++; } return 0; } +fn putn(n) +{ + let b[12], i = 0; + if (n < 0) { putc('-'); n = 0 - n; } + if (n < 0) { puts("2147483648"); return 0; } + while (1) { b[i++] = '0' + n % 10; n = n / 10; if (!n) break; } + while (i) putc(b[--i]); + return 0; +} +fn strlen(s) { let n = 0; while (s[n]) n++; return n; } +fn streq(a, b) { let i = 0; while (a[i] && a[i] == b[i]) i++; return a[i] == b[i]; } +fn wcopy(dst, src, n) { let i = 0; while (i < n) { dst[i] = src[i]; i++; } return 0; } +fn wfill(dst, v, n) { let i = 0; while (i < n) { dst[i] = v; i++; } return 0; } + +// ---- arithmetic the hardware doesn't have ---- +let pow2[32]; +let zrem; +fn zudiv(n, dv) +{ + let q = 0, r = 0, i = 30; + if (n < dv) { zrem = n; return 0; } + while (i >= 0) { + r = r + r + ((n >> i) & 1); + q = q + q; + if (r >= dv) { r = r - dv; q++; } + i--; + } + zrem = r; + return q; +} +fn zdiv(a, b) +{ + let s = 0, q; + if (b == 0) { zrem = a; return 0; } + if (a < 0) { a = 0 - a; s = 1; } + if (b < 0) { b = 0 - b; s = s ^ 1; } + q = zudiv(a, b); + if (s) return 0 - q; + return q; +} +fn zmod(a, b) { let neg = a < 0; if (b == 0) return a; zdiv(a, b); if (neg) return 0 - zrem; return zrem; } +fn zshl(a, b) { return a * pow2[b & 31]; } + +// ---- heap ---- +// a buddy allocator. the heap is cut into aligned power-of-two chunks, every +// block is a power of two words, and a freed block merges with its buddy +// (the other half of the block they were split from) whenever that one is +// free too, so freed space can be reused at any size. every block has a 6 +// word header before the pointer the program sees: +// p[-6] block size p[-5] size class p[-4] flags (0 free, 1 live, 3 marked) +// p[-3] p ^ MAGIC p[-2] owner (the container that owns it, or 0) +// p[-1] its type (a type record from new(T), or 0 from new(n)) +// free blocks sit on a doubly linked list per class, linked through p[0], p[1]. +const HDR = 6; +let hbump, freel[32], gc_runs, gc_freed, gc_auto = 1; + +// the size of the top-level chunk holding heap offset off +fn topsize(off) +{ + let h = HEAP_HI - HEAP_LO, base = 0, sz; + while (h) { + sz = 1; + while (sz + sz <= h) sz = sz + sz; + if (off < base + sz) return sz; + base = base + sz; h = h - sz; + } + return 0; +} +fn fpush(b, k) { b[2] = 0; b[6] = freel[k]; b[7] = 0; if (freel[k]) freel[k][7] = b; freel[k] = b; return 0; } +fn funlink(b, k) +{ + if (b[7]) b[7][6] = b[6]; else freel[k] = b[6]; + if (b[6]) b[6][7] = b[7]; + return 0; +} +fn mkblock(b, k) { b[0] = pow2[k]; b[1] = k; b[2] = 0; b[3] = 0; b[4] = 0; b[5] = 0; return b; } +fn heap_init() +{ + let i = 1, b = HEAP_LO, sz, k; + pow2[0] = 1; + while (i < 32) { pow2[i] = pow2[i - 1] + pow2[i - 1]; i++; } + hbump = HEAP_HI; + wfill(freel, 0, 32); + while (b < HEAP_HI) { + sz = topsize(b - HEAP_LO); k = 0; + while (pow2[k] < sz) k++; + fpush(mkblock(b, k), k); + b = b + sz; + } + return 0; +} +// a free block of class k, split down from a bigger one if needed; 0 if none +fn balloc(k) +{ + let j = k, b, h; + while (j < 32 && !freel[j]) j++; + if (j >= 32) return 0; + b = freel[j]; + funlink(b, j); + while (j > k) { j--; h = b + pow2[j]; fpush(mkblock(h, j), j); } + mkblock(b, k); + return b; +} +let fend; // end of the block bfree last made, for the sweep +// put block b back, merging it with its buddy as long as it can +fn bfree(b) +{ + let k = b[1], sz = pow2[k], off, bud; + while (1) { + off = b - HEAP_LO; + if (sz >= topsize(off)) break; + bud = HEAP_LO + (off ^ sz); + if (bud[2] != 0 || bud[1] != k || bud[0] != sz) break; + funlink(bud, k); + if (bud < b) { b[0] = 0; b[3] = 0; b = bud; } // the upper header is inside the merged block now + else { bud[0] = 0; bud[3] = 0; } + k++; sz = sz + sz; + mkblock(b, k); + } + fpush(mkblock(b, k), k); + fend = b + sz; + return 0; +} +fn isobj(x) { return x >= HEAP_LO + HDR && x < hbump && x[-3] == (x ^ MAGIC) && (x[-4] & 1); } + +// every prologue checks the frame stack; on overflow it resets the frame +// pointer (so this can run at all) and stops +fn frame_overflow() { puts("zy: frame stack overflow\n"); halt(); return 0; } +fn oom() { puts("zc: out of heap memory\n"); halt(); return 0; } +// n words typed t +unchecked fn new_t(n, t) -> .u +{ + let need = n + HDR, k = 3, b, p; + if (n < 0) need = HDR; + while (pow2[k] < need) k++; + b = balloc(k); + if (!b && gc_auto) { gc(); b = balloc(k); } + if (!b) oom(); + p = b + HDR; + b[2] = 1; b[3] = p ^ MAGIC; b[4] = 0; b[5] = t; + wfill(p, 0, pow2[k] - HDR); + return p; +} +unchecked fn new(n) -> .u { return new_t(n, 0); } +fn osize(p) { if (!isobj(p)) return 0; return p[-6] - HDR; } +fn typeof_obj(p) { if (!isobj(p)) return 0; return p[-1]; } + +// frees p and, recursively, every object p owns +fn release(p) +{ + let b = p - HDR, n = b[0] - HDR, i = 0, w; + b[2] = 0; + while (i < n) { w = p[i]; if (isobj(w) && w[-2] == p) release(w); i++; } + bfree(b); + return 0; +} +unchecked fn free(.d p) { if (isobj(p)) release(p); return 0; } +fn own(c, p) { if (isobj(c)) c[-2] = p; return c; } + +// ---- mark-sweep ---- +// conservative and non-moving. roots: the program area (globals live +// between functions), the genome's globals, the compiler's input stack, +// the live part of the frame stack and the whole data stack region. +let ms_top, ms_over, gc_lo, gc_hi; +fn mark(w) +{ + if (!isobj(w) || (w[-4] & 2)) return 0; + w[-4] = w[-4] | 2; + if (ms_top < MARKN) { MARK0[ms_top] = w; ms_top++; } else ms_over = 1; + return 0; +} +fn markrange(a, b) { while (a < b) { mark(*a); a++; } return 0; } +fn drain() +{ + let p; + while (ms_top) { ms_top--; p = MARK0[ms_top]; markrange(p, p + p[-6] - HDR); } + return 0; +} +fn gc_roots(); +unchecked fn gc() +{ + let b, sz, n = 0; + ms_top = 0; ms_over = 0; + gc_roots(); + drain(); + while (ms_over) { // the mark stack overflowed: rescan marked objects + ms_over = 0; + b = HEAP_LO; + while (b < HEAP_HI) { if (b[2] == 3) markrange(b + HDR, b + b[0]); b = b + b[0]; drain(); } + } + b = HEAP_LO; + while (b < HEAP_HI) { + sz = b[0]; + if (b[2] == 1) { bfree(b); n++; b = fend; } // merging may swallow what follows + else { if (b[2] == 3) b[2] = 1; b = b + sz; } + } + gc_runs++; + gc_freed = gc_freed + n; + return n; +} +// number of live objects, for tests +fn live_objects() { let b = HEAP_LO, n = 0; while (b < HEAP_HI) { if (b[2] & 1) n++; b = b + b[0]; } return n; } +// the biggest block that's free right now, in words +fn largest_free() { let k = 31; while (k > 0 && !freel[k]) k--; if (!freel[k]) return 0; return pow2[k]; } diff --git a/docs/history/13-gap-fixes/code/test.sh b/docs/history/13-gap-fixes/code/test.sh new file mode 100755 index 0000000..e28182d --- /dev/null +++ b/docs/history/13-gap-fixes/code/test.sh @@ -0,0 +1,71 @@ +#!/bin/bash +# full check: bootstraps the compiler from zc0, verifies the fixpoint, then runs +# the examples and tests on the portable interpreter (host), the arm64 jit under +# qemu, and the llvm backend. needs gcc; the jit half needs aarch64-linux-gnu-gcc +# and qemu-aarch64, the llvm half needs llvm-config and libLLVM (each is skipped +# if missing). +cd "$(dirname "$0")"; T=$(mktemp -d); trap 'rm -rf $T' EXIT +ok=0; bad=0 +chk(){ if [ "$1" = "$2" ]; then echo "ok $3"; ok=$((ok+1)); else echo "FAIL $3: got [$1]"; bad=$((bad+1)); fi; } +src(){ cat src/rt.zc src/compiler.zc; } +selfc(){ (cat tools/build.zc; printf '`'; src) | $1 > $2; } + +# bootstrap: zc0 (c) -> g0, g0 compiles compiler.zc -> g1, g1 compiles it -> g2 +gcc -O1 -w zc0.c -o $T/zc0 && gcc -O1 -w mkgenome.c -o $T/mkg || exit 1 +mkdir $T/s0 $T/s1 +cp core.c io_linux.c $T/s0/; cp core.c io_linux.c $T/s1/ +src | $T/zc0 > $T/s0/genome.h || exit 1 +gcc -O2 -w -DJTYPE=int $T/s0/core.c $T/s0/io_linux.c -o $T/zy0 +selfc $T/zy0 $T/g1.bin; $T/mkg < $T/g1.bin > $T/s1/genome.h +gcc -O2 -w -DJTYPE=int $T/s1/core.c $T/s1/io_linux.c -o $T/zy1 +selfc $T/zy1 $T/g2.bin +cmp -s $T/g1.bin $T/g2.bin && chk 1 1 "fixpoint: the compiler compiles itself to the same $(($(stat -c %s $T/g1.bin) / 4)) words" || chk 0 1 "fixpoint" +cmp -s $T/s1/genome.h genome.h && chk 1 1 "genome.h is up to date" || chk 0 1 "genome.h is stale (cp the bootstrap output over it)" + +run(){ # $1 = vm command, $2 = label + local vm="$1" l="$2" + chk "$($vm < examples/hello.zc)" "hello, world!" "$l hello" + chk "$($vm < examples/fact.zc | tr '\n' '/')" "1101110101111100000000/3628800/" "$l factorial" + chk "$( (cat examples/echo.zc; printf '`abc\n') | $vm)" "abc" "$l echo after the backtick" + chk "$($vm < examples/selfpatch.zc)" "ABC" "$l self-patching code" + chk "$($vm < examples/lists.zc | tr '\n' '/')" "[0 1 4 9 16 25]/live: 7/after the block: 0/" "$l ownership frees everything" + chk "$( (cat examples/repl.zc; printf '`fn sq(x) { return x * x; }`putn(sq(7)); putc(10);`puts("hi\\n");') | $vm | tr '\n' '/')" "49/hi/" "$l repl on the resident compiler" + chk "$($vm < examples/typed.zc | tr '\n' '/')" "last: 4/point is 2 words: x int, y int/vec<*point> is 3 words: len int, cap int, items **point/push is fn(*vec, T.d) -> bool/the heap says v is a vec, and v.items[0] is a point/" "$l typed example" + chk "$($vm < examples/wiring.zc)" "16 1234 25 4/9" "$l enhancement example" + chk "$($vm < tests/basics.zc | tr '\n' '/')" "285 610 42 512/hey 65 -3 -2 1024 -8/12456/757/9 100-101/5/ok/" "$l language basics" + chk "$($vm < tests/umd.zc)" "100 6 0 15 99 5 5" "$l u m d lifetimes" + chk "$($vm < tests/gc.zc)" "1000 1 900 1 199900" "$l mark-sweep collector" + chk "$($vm < tests/meta.zc | tr '\n' '/')" "1024 19/unless ok/rrr/15 42/7/42/7/144/" "$l parse words, quote, eval, load, operator tables" + chk "$($vm < tests/misc.zc | tr '\n' '/')" "45/54321/ok/" "$l compile-time eval, patched mnemonics, unchecked" + chk "$($vm < tests/types.zc | tr '\n' '/')" "12 5 2 15 56 42 7 14 3/rect *rect *list fn(int) -> *list.u fn(*list) -> T fn(word.d)/a:point@0 b:point@2 tag:int@4 /point 1 1/" "$l types, generics, reflection" + chk "$($vm < tests/typeok.zc)" "14" "$l a program the typechecker accepts" + chk "$($vm < tests/typeoff.zc)" "6" "$l the typechecker is off by default" + chk "$($vm < tests/enhance.zc)" "100 0 343 3,2 5 -2 0" "$l virtual enhancements" + chk "$($vm < tests/joins.zc)" "0 0 5 80 8 3 9 0" "$l joins free what one path gave away; switch, ?:, no prototypes" + chk "$($vm < tests/buddy.zc)" "131072 262144 1 0" "$l buddy allocator merges freed blocks" + chk "$($vm < tests/borrowmove.zc)" "6" "$l storing into a borrowed object moves it" + chk "$($vm < tests/manyerrors.zc | tr '\n' '/')" "zc: line 3: expected an expression/zc: line 4: x: m object is never used/zc: line 5: type error: initializer: expected *char, got int/zc: line 6: expected ;/zc: 4 errors/" "$l several errors in one run" + chk "$($vm < tests/overflow_return.zc 2>&1)" "zy: return stack overflow" "$l return stack overflow is caught" + chk "$($vm < tests/overflow_data.zc 2>&1)" "zy: data stack overflow" "$l data stack overflow is caught" + chk "$($vm < tests/overflow_frame.zc 2>&1)" "zy: frame stack overflow" "$l frame stack overflow is caught" + local r; r=$(tests/runerr.sh "$vm" tests/errors | tail -1) + chk "$r" "rejected: $(ls tests/errors/*.zc | wc -l) passed, 0 failed" "$l analyzer rejects every bad program" + r=$(tests/runerr.sh "$vm" tests/typeerrors | tail -1) + chk "$r" "rejected: $(ls tests/typeerrors/*.zc | wc -l) passed, 0 failed" "$l typechecker rejects every bad program" +} +gcc -O2 -w -DJTYPE=int core.c io_linux.c -o $T/zy +run $T/zy "interp" +if command -v aarch64-linux-gnu-gcc >/dev/null && command -v qemu-aarch64 >/dev/null; then + aarch64-linux-gnu-gcc -O2 -w -DJTYPE=int -static core.c io_linux.c -o $T/zyj || exit 1 + printf '#!/bin/sh\nexec qemu-aarch64 %s "$@"\n' $T/zyj > $T/jit; chmod +x $T/jit + run $T/jit "jit" + selfc $T/jit $T/gj.bin; cmp -s $T/g1.bin $T/gj.bin && chk 1 1 "jit self-compile matches" || chk 0 1 "jit self-compile matches" +else echo "skip jit (no aarch64 toolchain or qemu)"; fi +if command -v llvm-config >/dev/null; then + gcc -O2 -w -DJTYPE=int -DZYLLVM $(llvm-config --cflags) core.c io_linux.c -o $T/zyl $(llvm-config --ldflags --libs) || exit 1 + run $T/zyl "llvm" + printf '#!/bin/sh\nZYHOT=1 exec %s "$@"\n' $T/zyl > $T/llall; chmod +x $T/llall + chk "$($T/llall < tests/types.zc | md5sum)" "$($T/zy < tests/types.zc | md5sum)" "llvm compiling every block agrees" + selfc $T/zyl $T/gl.bin; cmp -s $T/g1.bin $T/gl.bin && chk 1 1 "llvm self-compile matches" || chk 0 1 "llvm self-compile matches" +else echo "skip llvm (no llvm-config)"; fi +echo "$ok passed, $bad failed"; [ $bad = 0 ] diff --git a/docs/history/13-gap-fixes/code/tests/basics.zc b/docs/history/13-gap-fixes/code/tests/basics.zc new file mode 100644 index 0000000..c1f1faf --- /dev/null +++ b/docs/history/13-gap-fixes/code/tests/basics.zc @@ -0,0 +1,22 @@ +let table[10]; +let greeting = "hey"; +const N = 10, BIG = 0x100 * 2; +fn fib(n) { if (n < 2) return n; return fib(n - 1) + fib(n - 2); } +fn twice(f, x) { return f(f(x)); } +fn inc(x) { return x + 1; } +fn main() +{ + let i, s = 0, a[5]; + for (i = 0; i < N; i++) table[i] = i * i; + for (let j = 0; j < N; j++) s += table[j]; + putn(s); putc(' '); putn(fib(15)); putc(' '); putn(twice(inc, 40)); putc(' '); putn(BIG); putc(10); + puts(greeting); putc(' '); putn('A'); putc(' '); putn(-17 / 5); putc(' '); putn(-17 % 5); putc(' '); putn(1 << 10); putc(' '); putn(-64 >> 3); putc(10); + i = 0; while (1) { i++; if (i == 3) continue; if (i > 6) break; putn(i); } putc(10); + a[0] = 5; a[1] = a[0]++; a[2] = ++a[0]; putn(a[0]); putn(a[1]); putn(a[2]); putc(10); + let b = { let t = 3; t * t }; + putn(b); putc(' '); putn(3 > 2 && 2 > 1); putn(0 || 0); putn(!5); putn(~0); putn(5 != 5); putn(4 >= 4); putc(10); + let x = asm { 2 3 + }; + putn(x); putc(10); + asm { 'o' . 'k' . 10 . } + return 0; +} diff --git a/docs/history/13-gap-fixes/code/tests/borrowmove.zc b/docs/history/13-gap-fixes/code/tests/borrowmove.zc new file mode 100644 index 0000000..43f27b2 --- /dev/null +++ b/docs/history/13-gap-fixes/code/tests/borrowmove.zc @@ -0,0 +1,2 @@ +fn put(.m box, int v) { *int c = new(int); *c = v; box[0] = c; return 0; } +fn main() { let b = new(1); put(b, 6); putn(*(b[0] as *int)); putc(10); return 0; } diff --git a/docs/history/13-gap-fixes/code/tests/buddy.zc b/docs/history/13-gap-fixes/code/tests/buddy.zc new file mode 100644 index 0000000..2000598 --- /dev/null +++ b/docs/history/13-gap-fixes/code/tests/buddy.zc @@ -0,0 +1,15 @@ +// the buddy allocator: 20000 small blocks, freed, merge back into one big one +let keep[20000]; +fn main() +{ + let i = 0, p; + while (i < 20000) { keep[i] = new(2); i++; } // 20000 8-word blocks + putn(largest_free()); putc(' '); + i = 0; while (i < 20000) { free(keep[i]); i++; } + putn(largest_free()); putc(' '); + p = new(200000); // only possible if the small blocks merged back + putn(osize(p) >= 200000); putc(' '); + free(d*p); + putn(live_objects()); putc(10); + return 0; +} diff --git a/docs/history/13-gap-fixes/code/tests/enhance.zc b/docs/history/13-gap-fixes/code/tests/enhance.zc new file mode 100644 index 0000000..6acc742 --- /dev/null +++ b/docs/history/13-gap-fixes/code/tests/enhance.zc @@ -0,0 +1,29 @@ +// custom opcodes: recipes, lifted vm code, several outputs, and a fallback +enhance clampadd(a, b) micro "a = add a b; a = min a 100; a = max a 0"; +enhance cube(x) vm { 0 $ 0 $ * * }; +enhance divmod(a, b) -> 2 micro "r2 = sdiv a b" "\n r1 = srem a b\n r0 = r2"; +enhance div7(a) micro "a = sdiv a 7"; +enhance urem10(a) micro "a = urem a 10"; +enhance hi(a, b) micro "a = mulhu a b"; +enhance noisy(x) vm { 0 $ . } else soft_noisy; // i/o can't be lifted, so calls use the fallback +fn soft_noisy(x) { return x + 1; } +let qq, rr, seed = 7; +fn rnd() { seed = seed * 1103515245 + 12345; return seed ^ (seed >> 11); } +fn main() +{ + let i = 0, a, bad = 0; + putn(clampadd(70, 50)); putc(' '); putn(clampadd(-50, 2)); putc(' '); putn(cube(7)); putc(' '); + asm { 17 5 divmod rr ! qq ! } + putn(qq); putc(','); putn(rr); putc(' '); + putn(noisy(4)); putc(' '); + putn(hi(-1, -1)); putc(' '); + while (i < 5000) { + a = rnd(); + if (div7(a) != zdiv(a, 7)) bad++; + if (urem10(a) < 0 || urem10(a) > 9) bad++; + if (a / (i + 1) != zdiv(a, i + 1)) bad++; + i++; + } + putn(bad); putc(10); + return 0; +} diff --git a/docs/history/13-gap-fixes/code/tests/errors/back_to_m.zc b/docs/history/13-gap-fixes/code/tests/errors/back_to_m.zc new file mode 100644 index 0000000..b8c160f --- /dev/null +++ b/docs/history/13-gap-fixes/code/tests/errors/back_to_m.zc @@ -0,0 +1,2 @@ +// expect: x: can't be recast back to m +fn main() { let x = new(3); d*x; m*x; return 0; } diff --git a/docs/history/13-gap-fixes/code/tests/errors/borrow_escape.zc b/docs/history/13-gap-fixes/code/tests/errors/borrow_escape.zc new file mode 100644 index 0000000..ce24480 --- /dev/null +++ b/docs/history/13-gap-fixes/code/tests/errors/borrow_escape.zc @@ -0,0 +1,3 @@ +// expect: p: is owned and can't be returned +fn f(.m p) { return p; } +fn main() { let x = new(1); f(x); return 0; } diff --git a/docs/history/13-gap-fixes/code/tests/errors/branch_mismatch.zc b/docs/history/13-gap-fixes/code/tests/errors/branch_mismatch.zc new file mode 100644 index 0000000..bd19968 --- /dev/null +++ b/docs/history/13-gap-fixes/code/tests/errors/branch_mismatch.zc @@ -0,0 +1,3 @@ +// expect: x: ends the branches of an if in different states +fn take(.d p) { return p[0]; } +fn main() { let x = new(1); if (x[0]) take(d*x); else u*x; return 0; } diff --git a/docs/history/13-gap-fixes/code/tests/errors/child_dies.zc b/docs/history/13-gap-fixes/code/tests/errors/child_dies.zc new file mode 100644 index 0000000..67caca9 --- /dev/null +++ b/docs/history/13-gap-fixes/code/tests/errors/child_dies.zc @@ -0,0 +1,3 @@ +// expect: c: used after it was given away +fn take(.d p) { return p[0]; } +fn main() { let p = new(2), c = new(1); p[0] = c; take(d*p); return c[0]; } diff --git a/docs/history/13-gap-fixes/code/tests/errors/child_recast.zc b/docs/history/13-gap-fixes/code/tests/errors/child_recast.zc new file mode 100644 index 0000000..c9ccd62 --- /dev/null +++ b/docs/history/13-gap-fixes/code/tests/errors/child_recast.zc @@ -0,0 +1,2 @@ +// expect: c: belongs to a container; recast the container instead +fn main() { let p = new(2), c = new(1); p[0] = c; d*c; return 0; } diff --git a/docs/history/13-gap-fixes/code/tests/errors/copy_owned.zc b/docs/history/13-gap-fixes/code/tests/errors/copy_owned.zc new file mode 100644 index 0000000..862c7ef --- /dev/null +++ b/docs/history/13-gap-fixes/code/tests/errors/copy_owned.zc @@ -0,0 +1,2 @@ +// expect: x: is an owned object and can't be copied +fn main() { let x = new(1); let y = x; return y[0]; } diff --git a/docs/history/13-gap-fixes/code/tests/errors/d_into_block.zc b/docs/history/13-gap-fixes/code/tests/errors/d_into_block.zc new file mode 100644 index 0000000..2d7f526 --- /dev/null +++ b/docs/history/13-gap-fixes/code/tests/errors/d_into_block.zc @@ -0,0 +1,2 @@ +// expect: x: used after it was given away +fn main() { let x = new(1); d*x; { x[0] = 5; } return x[0]; } diff --git a/docs/history/13-gap-fixes/code/tests/errors/d_kept.zc b/docs/history/13-gap-fixes/code/tests/errors/d_kept.zc new file mode 100644 index 0000000..dcbfb86 --- /dev/null +++ b/docs/history/13-gap-fixes/code/tests/errors/d_kept.zc @@ -0,0 +1,2 @@ +// expect: x: d object was never given away +fn main() { let x = new(3); d*x; x[0] = 1; return 0; } diff --git a/docs/history/13-gap-fixes/code/tests/errors/discard_u.zc b/docs/history/13-gap-fixes/code/tests/errors/discard_u.zc new file mode 100644 index 0000000..dd76e94 --- /dev/null +++ b/docs/history/13-gap-fixes/code/tests/errors/discard_u.zc @@ -0,0 +1,3 @@ +// expect: can't be thrown away +fn mk() -> .u { let p = new(1); return u*p; } +fn main() { mk(); return 0; } diff --git a/docs/history/13-gap-fixes/code/tests/errors/give_m.zc b/docs/history/13-gap-fixes/code/tests/errors/give_m.zc new file mode 100644 index 0000000..cd67896 --- /dev/null +++ b/docs/history/13-gap-fixes/code/tests/errors/give_m.zc @@ -0,0 +1,3 @@ +// expect: x: is m; write d*x +fn take(.d p) { return p[0]; } +fn main() { let x = new(3); take(x); return 0; } diff --git a/docs/history/13-gap-fixes/code/tests/errors/global_store.zc b/docs/history/13-gap-fixes/code/tests/errors/global_store.zc new file mode 100644 index 0000000..dc0e546 --- /dev/null +++ b/docs/history/13-gap-fixes/code/tests/errors/global_store.zc @@ -0,0 +1,3 @@ +// expect: x: must be d to be stored in a global +let g; +fn main() { let x = new(1); g = x; return 0; } diff --git a/docs/history/13-gap-fixes/code/tests/errors/leak_overwrite.zc b/docs/history/13-gap-fixes/code/tests/errors/leak_overwrite.zc new file mode 100644 index 0000000..d19b5fd --- /dev/null +++ b/docs/history/13-gap-fixes/code/tests/errors/leak_overwrite.zc @@ -0,0 +1,2 @@ +// expect: x: still owns an object +fn main() { let x = new(1); x = new(2); return x[0]; } diff --git a/docs/history/13-gap-fixes/code/tests/errors/lend_d.zc b/docs/history/13-gap-fixes/code/tests/errors/lend_d.zc new file mode 100644 index 0000000..55ac7e0 --- /dev/null +++ b/docs/history/13-gap-fixes/code/tests/errors/lend_d.zc @@ -0,0 +1,4 @@ +// expect: x: is d; pass it to a d parameter +fn look(.m p) { return p[0]; } +fn take(.d p) { return p[0]; } +fn main() { let x = new(3); d*x; look(x); take(x); return 0; } diff --git a/docs/history/13-gap-fixes/code/tests/errors/loop_move.zc b/docs/history/13-gap-fixes/code/tests/errors/loop_move.zc new file mode 100644 index 0000000..df3c94a --- /dev/null +++ b/docs/history/13-gap-fixes/code/tests/errors/loop_move.zc @@ -0,0 +1,3 @@ +// expect: x: is given away inside a loop that comes back around to it +fn take(.d p) { return p[0]; } +fn main() { let x = new(1); let i = 0; d*x; while (i < 3) { take(x); i++; } return 0; } diff --git a/docs/history/13-gap-fixes/code/tests/errors/plain_return.zc b/docs/history/13-gap-fixes/code/tests/errors/plain_return.zc new file mode 100644 index 0000000..4540714 --- /dev/null +++ b/docs/history/13-gap-fixes/code/tests/errors/plain_return.zc @@ -0,0 +1,3 @@ +// expect: returns a new object; declare the function with -> .u +fn mk() { return new(1); } +fn main() { return mk(); } diff --git a/docs/history/13-gap-fixes/code/tests/errors/return_m.zc b/docs/history/13-gap-fixes/code/tests/errors/return_m.zc new file mode 100644 index 0000000..1acc4be --- /dev/null +++ b/docs/history/13-gap-fixes/code/tests/errors/return_m.zc @@ -0,0 +1,3 @@ +// expect: x: has to be u to be returned +fn mk() -> .u { let x = new(1); return x; } +fn main() { let y = mk(); return y[0]; } diff --git a/docs/history/13-gap-fixes/code/tests/errors/tag_mismatch.zc b/docs/history/13-gap-fixes/code/tests/errors/tag_mismatch.zc new file mode 100644 index 0000000..079f249 --- /dev/null +++ b/docs/history/13-gap-fixes/code/tests/errors/tag_mismatch.zc @@ -0,0 +1,2 @@ +// expect: c: its tag doesn't match its container's +fn main() { let p = new(2), c = new(1); u*p; p[0] = c; return 0; } diff --git a/docs/history/13-gap-fixes/code/tests/errors/u_dropped.zc b/docs/history/13-gap-fixes/code/tests/errors/u_dropped.zc new file mode 100644 index 0000000..e1ca68a --- /dev/null +++ b/docs/history/13-gap-fixes/code/tests/errors/u_dropped.zc @@ -0,0 +1,2 @@ +// expect: x: u object must leave this block +fn main() { let x = new(3); u*x; return 0; } diff --git a/docs/history/13-gap-fixes/code/tests/errors/u_skip_level.zc b/docs/history/13-gap-fixes/code/tests/errors/u_skip_level.zc new file mode 100644 index 0000000..6caeeb3 --- /dev/null +++ b/docs/history/13-gap-fixes/code/tests/errors/u_skip_level.zc @@ -0,0 +1,2 @@ +// expect: t: is u; it can only be handed to a variable of the enclosing block +fn main() { let r; { { let t = new(1); u*t; r = t; } } return r[0]; } diff --git a/docs/history/13-gap-fixes/code/tests/errors/unchecked_touch.zc b/docs/history/13-gap-fixes/code/tests/errors/unchecked_touch.zc new file mode 100644 index 0000000..141639d --- /dev/null +++ b/docs/history/13-gap-fixes/code/tests/errors/unchecked_touch.zc @@ -0,0 +1,2 @@ +// expect: x: is tracked by the analyzer and can't be used in unchecked code +fn main() { let x = new(1); unchecked { x[0] = 1; } return 0; } diff --git a/docs/history/13-gap-fixes/code/tests/errors/unused.zc b/docs/history/13-gap-fixes/code/tests/errors/unused.zc new file mode 100644 index 0000000..64aff1f --- /dev/null +++ b/docs/history/13-gap-fixes/code/tests/errors/unused.zc @@ -0,0 +1,2 @@ +// expect: x: m object is never used +fn main() { let x = new(3); return 0; } diff --git a/docs/history/13-gap-fixes/code/tests/errors/use_after_give.zc b/docs/history/13-gap-fixes/code/tests/errors/use_after_give.zc new file mode 100644 index 0000000..8a10922 --- /dev/null +++ b/docs/history/13-gap-fixes/code/tests/errors/use_after_give.zc @@ -0,0 +1,3 @@ +// expect: x: used after it was given away +fn take(.d p) { return p[0]; } +fn main() { let x = new(3); take(d*x); return x[0]; } diff --git a/docs/history/13-gap-fixes/code/tests/gc.zc b/docs/history/13-gap-fixes/code/tests/gc.zc new file mode 100644 index 0000000..41f14a0 --- /dev/null +++ b/docs/history/13-gap-fixes/code/tests/gc.zc @@ -0,0 +1,20 @@ +let keep; +unchecked fn churn(n) +{ + let i = 0, p; + while (i < n) { p = new(10); p[0] = i; if (i % 100 == 0) keep = p; i++; } + return 0; +} +fn main() +{ + let base = live_objects(), f; + churn(1000); + putn(live_objects() - base); putc(' '); + f = gc(); + putn(f >= 990); putc(' '); // conservative: a stale word or two may keep one alive + putn(keep[0]); putc(' '); // the one a global still points at survives + churn(200000); // 3.2m words through a 393k word heap: needs the automatic collector + putn(gc_runs > 1); putc(' '); + putn(keep[0]); putc(10); + return 0; +} diff --git a/docs/history/13-gap-fixes/code/tests/joins.zc b/docs/history/13-gap-fixes/code/tests/joins.zc new file mode 100644 index 0000000..a272e08 --- /dev/null +++ b/docs/history/13-gap-fixes/code/tests/joins.zc @@ -0,0 +1,32 @@ +// joins: something given away on one path is dead afterwards, and the paths +// that still own it free it on the way in. nothing here leaks. +fn take(.d p) { return p[0]; } +fn later(.d p); // not needed any more, but still allowed +fn mk(int v) -> *int.u { *int p = new(int); *p = v; return u*p; } +fn count(int n) { let s = 0; switch (n % 4) { case 0: s = 10; case 1, 2: s = 20; break; default: s = 30; } return s; } +fn main() +{ + let base = live_objects(), i = 0, hits = 0, k; + while (i < 50) { + let x = mk(i); + if (i % 3 == 0) take(d*x); // else: freed here by the fixup + i++; + } + putn(live_objects() - base); putc(' '); + // give away and break: fine now; the normal exit frees it + i = 0; + let y = mk(7); + while (i < 10) { if (i == 4) { take(d*y); break; } i++; } + putn(live_objects() - base); putc(' '); + let z = mk(5); + while (1) { if (z[0] == 5) { hits = take(d*z); break; } } + putn(hits); putc(' '); + putn(count(0) + count(1) + count(2) + count(3)); putc(' '); + k = i > 3 ? i * 2 : 0 - 1; + putn(k); putc(' '); + putn(forward(new(int))); putc(' '); // tagged and defined below: the prescan knows .d + **int pp; *int q = new(int); *q = 9; pp = &q; putn(**pp); free(d*q); putc(' '); + putn(live_objects() - base); putc(10); + return 0; +} +fn forward(*int.d p) { return 3 + *p; } diff --git a/docs/history/13-gap-fixes/code/tests/manyerrors.zc b/docs/history/13-gap-fixes/code/tests/manyerrors.zc new file mode 100644 index 0000000..7c9febf --- /dev/null +++ b/docs/history/13-gap-fixes/code/tests/manyerrors.zc @@ -0,0 +1,6 @@ +// several errors in one run: a syntax error, an analyzer error, a type error +typecheck on; +fn f() { let a = ; return 0; } +fn g() { let x = new(3); return 0; } +fn h() -> int { *char s = 5; return 1; } +fn main() { return 0 } diff --git a/docs/history/13-gap-fixes/code/tests/meta.zc b/docs/history/13-gap-fixes/code/tests/meta.zc new file mode 100644 index 0000000..bc89454 --- /dev/null +++ b/docs/history/13-gap-fixes/code/tests/meta.zc @@ -0,0 +1,46 @@ +fn ipow(a, b) { let r = 1; while (b > 0) { r *= a; b--; } return r; } +// a parse word that adds an operator to the compiler's tables +immediate fn pow_op() { next(); add_binop("**", 11, "ipow c"); return 0; } +pow_op + +// unless (cond) stmt: a parse word that emits code itself +immediate fn unless() +{ + let a; + next(); + expect(intern("(")); rval(); plainuse(expr()); rval(); expect(intern(")")); + emitseq(useq[0], ulen[0]); // not + a = hole(); op(O_JZ); // skip the body when the condition was true + substmt(); + setlit(a, here); + return 0; +} +// repeat N { body }: a macro, built from token lists +immediate fn repeat() +{ + let n, body, i = 0; + next(); n = tv; next(); + body = grab(); + while (i < n) { feed(qdup(body)); i++; } + free(d*body); + return 0; +} +fn main() +{ + putn(2 ** 10); putc(' '); putn(3 + 2 ** 3 * 2); putc(10); + unless (1 > 2) puts("unless ok\n"); + unless (2 > 1) puts("wrong\n"); + repeat 3 { putc('r'); } + putc(10); + let q = quote { putn(40 + 2); putc(10); return 7; }; + putn(q[0]); putc(' '); + putn(eval(q)); putc(10); + let f = compile(q); + putn(f()); putc(10); + free(d*q); + let defs = quote { fn sq(x) { return x * x; } }; + load(defs); + free(d*defs); + putn(evals("return sq(12);")); putc(10); + return 0; +} diff --git a/docs/history/13-gap-fixes/code/tests/misc.zc b/docs/history/13-gap-fixes/code/tests/misc.zc new file mode 100644 index 0000000..25fc53b --- /dev/null +++ b/docs/history/13-gap-fixes/code/tests/misc.zc @@ -0,0 +1,30 @@ +// ct { ... } runs a block while compiling and leaves its result as a constant +immediate fn ct() +{ + let q, v; + next(); + q = grab(); + v = eval(q); + free(d*q); + lit(v); + return 0; +} +// teach the asm syntax that p means out, like the old repl demo +immediate fn teach() { next(); mnem[12] = 'p'; return 0; } +teach + +fn countdown() +{ + asm { 5 :top 0 $ '0' + p 1 - 0 $ done z top j :done _ 10 p } + return 0; +} +fn main() +{ + putn(ct { let i = 0, s = 0; while (i < 10) { s += i; i++; } return s; }); putc(10); + countdown(); + puts(lazy()); putc(10); + return 0; +} +// the analyzer is off from here on +unchecked; +fn lazy() { let p = new(3); p[0] = 'o'; p[1] = 'k'; return p; } diff --git a/docs/history/13-gap-fixes/code/tests/overflow_data.zc b/docs/history/13-gap-fixes/code/tests/overflow_data.zc new file mode 100644 index 0000000..3042f0a --- /dev/null +++ b/docs/history/13-gap-fixes/code/tests/overflow_data.zc @@ -0,0 +1 @@ +fn main() { while (1) asm { 1 } return 0; } diff --git a/docs/history/13-gap-fixes/code/tests/overflow_frame.zc b/docs/history/13-gap-fixes/code/tests/overflow_frame.zc new file mode 100644 index 0000000..3897bf4 --- /dev/null +++ b/docs/history/13-gap-fixes/code/tests/overflow_frame.zc @@ -0,0 +1,2 @@ +fn big(n) { let a[500]; a[0] = n; return big(n + 1) + a[0]; } +fn main() { return big(0); } diff --git a/docs/history/13-gap-fixes/code/tests/overflow_return.zc b/docs/history/13-gap-fixes/code/tests/overflow_return.zc new file mode 100644 index 0000000..9d5c622 --- /dev/null +++ b/docs/history/13-gap-fixes/code/tests/overflow_return.zc @@ -0,0 +1,2 @@ +fn deep(n) { return deep(n + 1) + 1; } +fn main() { return deep(0); } diff --git a/docs/history/13-gap-fixes/code/tests/runerr.sh b/docs/history/13-gap-fixes/code/tests/runerr.sh new file mode 100755 index 0000000..23ea054 --- /dev/null +++ b/docs/history/13-gap-fixes/code/tests/runerr.sh @@ -0,0 +1,10 @@ +#!/bin/bash +# runs every .zc in a folder (tests/errors by default) and checks the compiler +# rejects it with the message on its first line: // expect: ... +vm=${1:-zy}; dir=${2:-$(dirname "$0")/errors}; pass=0; fail=0 +for f in "$dir"/*.zc; do + want=$(head -1 "$f" | sed 's#^// expect: ##') + got=$($vm < "$f" 2>&1 | head -1) + if [[ "$got" == *"$want"* ]]; then pass=$((pass+1)); else fail=$((fail+1)); echo "FAIL $(basename $f): got [$got]"; fi +done +echo "rejected: $pass passed, $fail failed" diff --git a/docs/history/13-gap-fixes/code/tests/typeerrors/arg.zc b/docs/history/13-gap-fixes/code/tests/typeerrors/arg.zc new file mode 100644 index 0000000..b762c19 --- /dev/null +++ b/docs/history/13-gap-fixes/code/tests/typeerrors/arg.zc @@ -0,0 +1,5 @@ +// expect: type error: argument: expected *point, got *int +typecheck on; +struct point { int x, y; }; +fn f(*point p) -> int { return p.x; } +fn main() { *int q = new(int); f(q); free(d*q); return 0; } diff --git a/docs/history/13-gap-fixes/code/tests/typeerrors/arith.zc b/docs/history/13-gap-fixes/code/tests/typeerrors/arith.zc new file mode 100644 index 0000000..76e3111 --- /dev/null +++ b/docs/history/13-gap-fixes/code/tests/typeerrors/arith.zc @@ -0,0 +1,3 @@ +// expect: type error: arithmetic needs numbers, got *int +typecheck on; +fn main() { *int p = 0; int x = p * 2; return x; } diff --git a/docs/history/13-gap-fixes/code/tests/typeerrors/assign.zc b/docs/history/13-gap-fixes/code/tests/typeerrors/assign.zc new file mode 100644 index 0000000..8b8a888 --- /dev/null +++ b/docs/history/13-gap-fixes/code/tests/typeerrors/assign.zc @@ -0,0 +1,3 @@ +// expect: type error: initializer: expected *char, got int +typecheck on; +fn main() { *char s = 5; return 0; } diff --git a/docs/history/13-gap-fixes/code/tests/typeerrors/call.zc b/docs/history/13-gap-fixes/code/tests/typeerrors/call.zc new file mode 100644 index 0000000..7b03dbf --- /dev/null +++ b/docs/history/13-gap-fixes/code/tests/typeerrors/call.zc @@ -0,0 +1,3 @@ +// expect: type error: only functions can be called, got int +typecheck on; +fn main() { int k = 3; return k(1); } diff --git a/docs/history/13-gap-fixes/code/tests/typeerrors/deref.zc b/docs/history/13-gap-fixes/code/tests/typeerrors/deref.zc new file mode 100644 index 0000000..ed030f5 --- /dev/null +++ b/docs/history/13-gap-fixes/code/tests/typeerrors/deref.zc @@ -0,0 +1,3 @@ +// expect: type error: only pointers can be dereferenced, got int +typecheck on; +fn main() { int x = 3; return *x; } diff --git a/docs/history/13-gap-fixes/code/tests/typeerrors/field.zc b/docs/history/13-gap-fixes/code/tests/typeerrors/field.zc new file mode 100644 index 0000000..ba03697 --- /dev/null +++ b/docs/history/13-gap-fixes/code/tests/typeerrors/field.zc @@ -0,0 +1,3 @@ +// expect: point has no field z +struct point { int x, y; }; +fn main() { point p; return p.z; } diff --git a/docs/history/13-gap-fixes/code/tests/typeerrors/generic.zc b/docs/history/13-gap-fixes/code/tests/typeerrors/generic.zc new file mode 100644 index 0000000..4a502ce --- /dev/null +++ b/docs/history/13-gap-fixes/code/tests/typeerrors/generic.zc @@ -0,0 +1,4 @@ +// expect: type error: argument: expected int, got *char +typecheck on; +fn same(T a, T b) -> T { return a; } +fn main() { return same(1, "x"); } diff --git a/docs/history/13-gap-fixes/code/tests/typeerrors/notstruct.zc b/docs/history/13-gap-fixes/code/tests/typeerrors/notstruct.zc new file mode 100644 index 0000000..0dff6bd --- /dev/null +++ b/docs/history/13-gap-fixes/code/tests/typeerrors/notstruct.zc @@ -0,0 +1,2 @@ +// expect: .x: that's a int, not a struct or a pointer to one +fn main() { int n = 1; return n.x; } diff --git a/docs/history/13-gap-fixes/code/tests/typeerrors/ret.zc b/docs/history/13-gap-fixes/code/tests/typeerrors/ret.zc new file mode 100644 index 0000000..cedd534 --- /dev/null +++ b/docs/history/13-gap-fixes/code/tests/typeerrors/ret.zc @@ -0,0 +1,4 @@ +// expect: type error: return value: expected bool, got *char +typecheck on; +fn f() -> bool { return "no"; } +fn main() { return f(); } diff --git a/docs/history/13-gap-fixes/code/tests/typeerrors/voidret.zc b/docs/history/13-gap-fixes/code/tests/typeerrors/voidret.zc new file mode 100644 index 0000000..a897290 --- /dev/null +++ b/docs/history/13-gap-fixes/code/tests/typeerrors/voidret.zc @@ -0,0 +1,4 @@ +// expect: type error: return value: expected void, got int +typecheck on; +fn f() -> void { return 3; } +fn main() { f(); return 0; } diff --git a/docs/history/13-gap-fixes/code/tests/typeoff.zc b/docs/history/13-gap-fixes/code/tests/typeoff.zc new file mode 100644 index 0000000..e44e450 --- /dev/null +++ b/docs/history/13-gap-fixes/code/tests/typeoff.zc @@ -0,0 +1,2 @@ +// the typechecker is off unless asked for: this mismatch compiles and runs +fn main() { *char s = 5; putn(s as int + 1); putc(10); return 0; } diff --git a/docs/history/13-gap-fixes/code/tests/typeok.zc b/docs/history/13-gap-fixes/code/tests/typeok.zc new file mode 100644 index 0000000..3cdee0c --- /dev/null +++ b/docs/history/13-gap-fixes/code/tests/typeok.zc @@ -0,0 +1,22 @@ +// passes the typechecker: words mix with everything, literal 0 is null, +// arrays decay to pointers, generic calls bind their parameters +typecheck on; +struct pair { A a; B b; }; +fn mk(A x, B y) -> *pair.u { *pair p = new(pair); p.a = x; p.b = y; return u*p; } +fn left(*pair p) -> A { return p.a; } +fn strlen2(*char s) -> int { int n = 0; while (s[n]) n++; return n; } +fn main() -> int +{ + *pair p = mk(3, "four"); + int a = left(p); + *char s = p.b; + [8]char buf; + *char q = buf; + *int nothing = 0; + let w = 5; + int mixed = w + 1; + q[0] = 'h'; q[1] = 0; + putn(a + strlen2(s) + strlen2(buf) + mixed); putc(10); + free(d*p); + return 0; +} diff --git a/docs/history/13-gap-fixes/code/tests/types.zc b/docs/history/13-gap-fixes/code/tests/types.zc new file mode 100644 index 0000000..ccbbd26 --- /dev/null +++ b/docs/history/13-gap-fixes/code/tests/types.zc @@ -0,0 +1,57 @@ +struct point { int x, y; }; +struct rect { point a, b; int tag; }; +struct list { int len; *T items; }; +struct node { int v; *node next; }; +type num = int; + +int counter = 5; +[4]int squares; +point origin; + +fn area(*rect r) -> int { return (r.b.x - r.a.x) * (r.b.y - r.a.y); } +fn mklist(int cap) -> *list.u +{ + *list l = new(list); + l.len = 0; + l.items = new(T, cap) as *T; + return u*l; +} +fn first(*list l) -> T { return l.items[0]; } +fn sum(*node n) -> int { int s = 0; while (n) { s += n.v; n = n.next; } return s; } + +fn main() +{ + rect r; + r.a.x = 1; r.a.y = 2; r.b.x = 4; r.b.y = 6; + putn(area(&r)); putc(' '); + putn(sizeof(rect)); putc(' '); putn(sizeof(point)); putc(' '); putn(sizeof([3]rect)); putc(' '); + let p = new(point); // let infers *point + p.x = 7; p->y = 8; + putn(p.x * p.y); putc(' '); + free(d*p); + *list l = mklist(4); + l.items[0] = 42; l.len = 1; + putn(first(l)); putc(' '); + free(d*l); + *node a = new(node), b = new(node); + a.v = 3; b.v = 4; a.next = b; + putn(sum(a)); putc(' '); + free(d*a); // b went with it: a.next = b made a its owner + [3]point pts; + pts[2].y = 9; + putn(pts[2].y + counter); putc(' '); + num k = 3; + putn(k); putc(10); + + // reflection + tprint(typeof(r)); putc(' '); tprint(typeof(&r)); putc(' '); tprint(typeof(l)); putc(' '); + tprint(sigof(mklist)); putc(' '); tprint(sigof(first)); putc(' '); tprint(sigofs("free")); putc(10); + let t = typeof(r), i = 0; + while (i < tnfields(t)) { puts(tfname(t, i)); putc(':'); tprint(tftype(t, i)); putc('@'); putn(tfoff(t, i)); putc(' '); i++; } + putc(10); + *point q = new(point); + tprint(typeof_obj(q)); putc(' '); putn(typeof_obj(q) == typeof(point)); putc(' '); putn(tkind(typeof_obj(q)) == TY_STRUCT); + free(d*q); + putc(10); + return 0; +} diff --git a/docs/history/13-gap-fixes/code/tests/umd.zc b/docs/history/13-gap-fixes/code/tests/umd.zc new file mode 100644 index 0000000..ee463bf --- /dev/null +++ b/docs/history/13-gap-fixes/code/tests/umd.zc @@ -0,0 +1,52 @@ +// lists as [len, items...], items owned by the list +fn mklist(n) -> .u +{ + let l = new(n + 1); + l[0] = 0; + return u*l; +} +fn push(.m l, .d item) { l[l[0] + 1] = d*item; l[0] = l[0] + 1; return 0; } +fn sum(.m l) { let s = 0, i = 1; while (i <= l[0]) { s += l[i][0]; i++; } return s; } +fn box(v) -> .u { let b = new(1); b[0] = v; return u*b; } +fn consume(.d b) { return b[0]; } + +fn main() +{ + let base = live_objects(); + { + let l = mklist(8); // u from mklist arrives m*here + let i = 0; + while (i < 5) { + let b = box(i * 10); // m in the loop body + d*b; // give it down + push(l, b); + i++; + } + putn(sum(l)); putc(' '); + putn(live_objects() - base); putc(' '); // list + 5 boxes + } + putn(live_objects() - base); putc(' '); // freed at the block's end, children too + + // u handed up one block by assignment, and as a block value + let outer; + { + let t = box(7); + u*t; + outer = t; + } + let v = { let w = box(8); u*w }; + putn(outer[0] + v[0]); putc(' '); + + // d given away to a callee + let g = box(99); + putn(consume(d*g)); putc(' '); + + // containers: child takes its parent's tag + let p = new(2), c = new(1); + c[0] = 5; + p[0] = c; + p[1] = new(3); + putn(p[0][0]); putc(' '); + putn(live_objects() - base); putc(10); + return 0; +} diff --git a/docs/history/13-gap-fixes/code/tools/build.zc b/docs/history/13-gap-fixes/code/tools/build.zc new file mode 100644 index 0000000..a38e97f --- /dev/null +++ b/docs/history/13-gap-fixes/code/tools/build.zc @@ -0,0 +1,13 @@ +// compiles the rest of stdin (after the backtick) as a genome image at GORG +// and writes it to stdout as little-endian 32-bit words. +const OUT = 0x10000; +fn main() +{ + let n = build_image(GORG, OUT), i = 0, w; + while (i < n) { + w = OUT[i]; + putc(w); putc(w >> 8); putc(w >> 16); putc(w >> 24); + i++; + } + return 0; +} diff --git a/docs/history/13-gap-fixes/code/zc0.c b/docs/history/13-gap-fixes/code/zc0.c new file mode 100644 index 0000000..4df587e --- /dev/null +++ b/docs/history/13-gap-fixes/code/zc0.c @@ -0,0 +1,460 @@ +/* zc0: stage 0 of the bootstrap. a small c compiler for the subset of zc that + compiler.zc is written in. it reads zc source on stdin and writes a genome + header (vm words placed at GORG) on stdout, in the same image layout the + resident compiler's image mode produces. no lifetime analysis, no quote, no + parse words, no block expressions: those only exist in compiler.zc. tags + (m, d, -> u) and casts (as u, as d) are parsed and recorded in signatures + but not checked. */ +#include +#include +#include +#include + +#define GORG 0xC0000 +#define ZFP 0xA0000 /* frame pointer word */ +#define FTOP 0xF0000 /* frame stack grows down from here */ +#define MAXW 0x40000 + +enum { T_EOF, T_NUM, T_ID, T_STR, T_OP }; +enum { K_FN = 1, K_GVAR, K_GARR, K_CONST, K_KW, K_ENH }; +enum { O_Q, O_LD, O_ST, O_PICK, O_DROP, O_SWAP, O_CALL, O_JMP, O_JZ, O_RET, + O_IN, O_EMPTY, O_OUT, O_ADD, O_SUB, O_MUL, O_AND, O_OR, O_XOR, O_SHR, O_LT }; +static const char *mn = "q@!$_~cjzr,?.+-*&|^><%"; + +static char *src; static int pos, line = 1; +static int tk, tv, glue; static char ts[4096]; /* current token */ + +static int img[MAXW], here = GORG; +static void emit(int w) { if (here - GORG >= MAXW) { fprintf(stderr, "image too big\n"); exit(1); } img[here++ - GORG] = w; } +static void lit(int v) { emit((int)((unsigned)v * 2 + 1)); } +static void op(int k) { emit(k * 2); } +static void setlit(int at, int v) { img[at - GORG] = (int)((unsigned)v * 2 + 1); } + +static void die(const char *m) { fprintf(stderr, "zc0: line %d: %s (at '%s')\n", line, m, ts); exit(1); } + +/* ---- lexer ---- */ +static const char *ops3[] = { "<<=", ">>=", 0 }; +static const char *ops2[] = { "==", "!=", "<=", ">=", "<<", ">>", "&&", "||", "++", "--", "+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "->", 0 }; +static int esc(void) +{ + int c = src[pos++]; + if (c != '\\') return c; + c = src[pos++]; + switch (c) { case 'n': return 10; case 't': return 9; case 'r': return 13; case '0': return 0; case 'e': return 27; } + return c; +} +static void next(void) +{ + int ws = 0, i; + for (;;) { + while (src[pos] && isspace((unsigned char)src[pos])) { if (src[pos] == '\n') line++; pos++; ws = 1; } + if (src[pos] == '/' && src[pos + 1] == '/') { while (src[pos] && src[pos] != '\n') pos++; ws = 1; continue; } + if (src[pos] == '/' && src[pos + 1] == '*') { pos += 2; while (src[pos] && !(src[pos] == '*' && src[pos + 1] == '/')) { if (src[pos] == '\n') line++; pos++; } if (src[pos]) pos += 2; ws = 1; continue; } + break; + } + glue = !ws; + if (!src[pos] || src[pos] == '`') { tk = T_EOF; strcpy(ts, ""); return; } + if (isdigit((unsigned char)src[pos])) { + char *e; tv = (int)strtoul(src + pos, &e, 0); memcpy(ts, src + pos, e - src - pos); ts[e - src - pos] = 0; pos = e - src; tk = T_NUM; return; + } + if (isalpha((unsigned char)src[pos]) || src[pos] == '_') { + i = 0; while (isalnum((unsigned char)src[pos]) || src[pos] == '_') ts[i++] = src[pos++]; + ts[i] = 0; tk = T_ID; return; + } + if (src[pos] == '\'') { pos++; tv = esc(); if (src[pos++] != '\'') die("bad char literal"); tk = T_NUM; strcpy(ts, "'c'"); return; } + if (src[pos] == '"') { pos++; i = 0; while (src[pos] && src[pos] != '"') ts[i++] = (char)esc(); ts[i] = 0; pos++; tk = T_STR; return; } + tk = T_OP; + for (i = 0; ops3[i]; i++) if (!strncmp(src + pos, ops3[i], 3)) { strcpy(ts, ops3[i]); pos += 3; return; } + for (i = 0; ops2[i]; i++) if (!strncmp(src + pos, ops2[i], 2)) { strcpy(ts, ops2[i]); pos += 2; return; } + ts[0] = src[pos++]; ts[1] = 0; +} +static int is(const char *s) { return (tk == T_OP || tk == T_ID) && !strcmp(ts, s); } +static int accept(const char *s) { if (is(s)) { next(); return 1; } return 0; } +static void expect(const char *s) { if (!accept(s)) { char b[64]; sprintf(b, "expected '%s'", s); die(b); } } +static char *ident(void) { static char b[8][256]; static int k; char *r = b[k++ & 7]; if (tk != T_ID) die("expected a name"); strcpy(r, ts); next(); return r; } + +/* ---- symbols ---- */ +typedef struct { char name[64]; int kind, val, sig, defd; } Sym; +static Sym sy[4096]; static int nsy; +typedef struct { int at, sym; } Fix; +static Fix fx[65536]; static int nfx; +static Sym *find(const char *n) { int i; for (i = nsy; i--;) if (!strcmp(sy[i].name, n)) return &sy[i]; return 0; } +static Sym *add(const char *n, int kind, int val) +{ + Sym *s = find(n); + if (s && s->kind == K_FN && kind == K_FN && !s->defd) { s->val = val; return s; } /* prototype or forward use */ + s = &sy[nsy++]; strncpy(s->name, n, 63); s->kind = kind; s->val = val; s->defd = 1; s->sig = 0; return s; +} +static void fnref(const char *n) /* push a function address, fixing it up later if needed */ +{ + Sym *s = find(n); + if (!s) { s = add(n, K_FN, 0); s->defd = 0; } + if (s->kind != K_FN) die("not a function"); + if (s->defd) { lit(s->val); return; } + fx[nfx].at = here; fx[nfx++].sym = (int)(s - sy); lit(0); +} + +/* ---- locals ---- */ +typedef struct { char name[64]; int slot, arr; } Loc; +static Loc lo[1024]; static int nlo, nslot, infn; +static Loc *findl(const char *n) { int i; for (i = nlo; i--;) if (!strcmp(lo[i].name, n)) return &lo[i]; return 0; } +static void laddr(int k) { lit(ZFP); op(O_LD); if (k) { lit(k); op(O_ADD); } } + +/* ---- operator tables ---- */ +static void seq_isz(void) { lit(0); op(O_PICK); lit(0); op(O_SWAP); op(O_SUB); op(O_OR); lit(31); op(O_SHR); lit(1); op(O_ADD); } +static void seq_nz(void) { lit(0); op(O_PICK); lit(0); op(O_SWAP); op(O_SUB); op(O_OR); lit(31); op(O_SHR); lit(0); op(O_SWAP); op(O_SUB); } +static int binprec(void) +{ + static const char *n[] = { "*", "/", "%", "+", "-", "<<", ">>", "<", ">", "<=", ">=", "==", "!=", "&", "^", "|", "&&", "||", 0 }; + static int p[] = { 10, 10, 10, 9, 9, 8, 8, 7, 7, 7, 7, 6, 6, 5, 4, 3, 2, 1 }; + int i; + if (tk != T_OP) return 0; + for (i = 0; n[i]; i++) if (!strcmp(ts, n[i])) return p[i]; + return 0; +} +static void binop(const char *o) +{ + if (!strcmp(o, "*")) op(O_MUL); + else if (!strcmp(o, "/")) { fnref("zdiv"); op(O_CALL); } + else if (!strcmp(o, "%")) { fnref("zmod"); op(O_CALL); } + else if (!strcmp(o, "+")) op(O_ADD); + else if (!strcmp(o, "-")) op(O_SUB); + else if (!strcmp(o, "<<")) { fnref("zshl"); op(O_CALL); } + else if (!strcmp(o, ">>")) op(O_SHR); + else if (!strcmp(o, "<")) op(O_LT); + else if (!strcmp(o, ">")) { op(O_SWAP); op(O_LT); } + else if (!strcmp(o, "<=")) { op(O_SWAP); op(O_LT); lit(1); op(O_XOR); } + else if (!strcmp(o, ">=")) { op(O_LT); lit(1); op(O_XOR); } + else if (!strcmp(o, "==")) { op(O_SUB); seq_isz(); } + else if (!strcmp(o, "!=")) { op(O_SUB); seq_nz(); } + else if (!strcmp(o, "&")) op(O_AND); + else if (!strcmp(o, "^")) op(O_XOR); + else if (!strcmp(o, "|")) op(O_OR); + else die("bad operator"); +} + +/* ---- constant expressions (globals, array sizes, const) ---- */ +static int cexpr(void); +static int cprim(void) +{ + int v; Sym *s; + if (tk == T_NUM) { v = tv; next(); return v; } + if (accept("-")) return -cprim(); + if (accept("~")) return ~cprim(); + if (accept("(")) { v = cexpr(); expect(")"); return v; } + if (tk == T_ID && (s = find(ts)) && (s->kind == K_CONST || (s->kind != K_GVAR && s->defd))) { next(); return s->val; } + die("constant expected"); return 0; +} +static int cexpr(void) +{ + int v = cprim(); + for (;;) { + if (accept("+")) v += cprim(); else if (accept("-")) v -= cprim(); + else if (accept("*")) v *= cprim(); else if (accept("|")) v |= cprim(); + else if (accept("&")) v &= cprim(); else if (accept("<<")) v <<= cprim(); + else if (accept(">>")) v >>= cprim(); else return v; + } +} + +/* ---- expressions ---- */ +static int lv; /* the stack top is an address not yet loaded */ +static void rval(void) { if (lv) { op(O_LD); lv = 0; } } +static void expr(void); +static void strlit(void) /* inline string: jump over it, push its address */ +{ + int j = here, a, i, n = (int)strlen(ts); + lit(0); op(O_JMP); a = here; + for (i = 0; i < n; i++) emit((unsigned char)ts[i]); + emit(0); setlit(j, here); lit(a); next(); +} +static int args(void) { int n = 0; expect("("); if (!accept(")")) { do { expr(); rval(); n++; } while (accept(",")); expect(")"); } return n; } +static void asmblock(void); +static void primary(void) +{ + char *n; Loc *l; Sym *s; + lv = 0; + if (tk == T_NUM) { lit(tv); next(); return; } + if (tk == T_STR) { strlit(); return; } + if (accept("(")) { expr(); expect(")"); return; } + if (is("asm")) { next(); asmblock(); return; } + n = ident(); + if ((l = findl(n))) { laddr(l->slot); lv = !l->arr; return; } + s = find(n); + if (s && s->kind == K_CONST) { lit(s->val); return; } + if (s && s->kind == K_GVAR) { lit(s->val); lv = 1; return; } + if (s && s->kind == K_GARR) { lit(s->val); return; } + if (is("(")) { args(); fnref(n); op(O_CALL); return; } + fnref(n); +} +static void incdec(int post, int d) +{ + if (!lv) die("++/-- needs an lvalue"); + lv = 0; + if (post) { lit(0); op(O_PICK); op(O_LD); lit(0); op(O_PICK); lit(d); op(O_ADD); lit(2); op(O_PICK); op(O_ST); op(O_SWAP); op(O_DROP); } + else { lit(0); op(O_PICK); op(O_LD); lit(d); op(O_ADD); lit(0); op(O_PICK); lit(2); op(O_PICK); op(O_ST); op(O_SWAP); op(O_DROP); } +} +static void postfix(void) +{ + int n; + primary(); + for (;;) { + if (accept("[")) { rval(); expr(); rval(); expect("]"); op(O_ADD); lv = 1; } + else if (is("(")) { rval(); n = args(); lit(n); op(O_PICK); op(O_CALL); op(O_SWAP); op(O_DROP); } + else if (accept("++")) incdec(1, 1); + else if (accept("--")) incdec(1, -1); + else if (accept("as")) ident(); + else return; + } +} +static void unary(void) +{ + if (accept("-")) { unary(); rval(); lit(0); op(O_SWAP); op(O_SUB); } + else if (accept("!")) { unary(); rval(); seq_isz(); } + else if (accept("~")) { unary(); rval(); lit(-1); op(O_XOR); } + else if (accept("*")) { unary(); rval(); lv = 1; } + else if (accept("&")) { unary(); if (!lv) die("& needs an lvalue"); lv = 0; } + else if (accept("++")) { unary(); incdec(0, 1); } + else if (accept("--")) { unary(); incdec(0, -1); } + else if (tk == T_ID && (!strcmp(ts, "u") || !strcmp(ts, "m") || !strcmp(ts, "d"))) { next(); expect("*"); unary(); } /* u*x: a recast, no code */ + else postfix(); +} +static void bin(int minp) +{ + int p, a, b; char o[8]; + unary(); + while ((p = binprec()) && p >= minp) { + strcpy(o, ts); next(); rval(); + if (!strcmp(o, "&&")) { + a = here; lit(0); op(O_JZ); bin(p + 1); rval(); seq_nz(); + b = here; lit(0); op(O_JMP); setlit(a, here); lit(0); setlit(b, here); + } else if (!strcmp(o, "||")) { + a = here; lit(0); op(O_JZ); lit(1); b = here; lit(0); op(O_JMP); + setlit(a, here); bin(p + 1); rval(); seq_nz(); setlit(b, here); + } else { bin(p + 1); rval(); binop(o); } + lv = 0; + } +} +static void expr(void) +{ + char o[8]; + bin(1); + if (is("=")) { + if (!lv) die("assignment needs an lvalue"); + next(); expr(); rval(); + op(O_SWAP); lit(1); op(O_PICK); op(O_SWAP); op(O_ST); lv = 0; + } else if (tk == T_OP && strlen(ts) >= 2 && ts[strlen(ts) - 1] == '=' && strcmp(ts, "==") && strcmp(ts, "!=") && strcmp(ts, "<=") && strcmp(ts, ">=")) { + if (!lv) die("assignment needs an lvalue"); + strcpy(o, ts); o[strlen(o) - 1] = 0; next(); + lit(0); op(O_PICK); op(O_LD); lv = 0; expr(); rval(); binop(o); + op(O_SWAP); lit(1); op(O_PICK); op(O_SWAP); op(O_ST); lv = 0; + } +} + +/* ---- asm blocks: raw vm words ---- */ +static void asmblock(void) +{ + char ln[128][64], rn[256][64]; int la[128], nl = 0, ra[256], nr = 0, i, k; const char *c; + Loc *l; Sym *s; + expect("{"); + while (!accept("}")) { + if (tk == T_EOF) die("unterminated asm"); + if (tk == T_NUM) { lit(tv); next(); continue; } + if (tk == T_STR) { strlit(); continue; } + if (accept("#")) { if (tk != T_NUM) die("# needs a number"); emit(tv); next(); continue; } + if (is("-")) { next(); if (tk == T_NUM && glue) { lit(-tv); next(); } else op(O_SUB); continue; } + if (accept(":")) { strcpy(ln[nl], ident()); la[nl++] = here; continue; } + if (strlen(ts) == 1 && (c = strchr(mn, ts[0]))) { op((int)(c - mn)); next(); continue; } + if (tk != T_ID) die("bad asm token"); + if ((l = findl(ts))) { laddr(l->slot); next(); continue; } + if ((s = find(ts)) && s->kind != K_FN) { lit(s->val); next(); continue; } + if (s) { fnref(ts); next(); continue; } + strcpy(rn[nr], ts); ra[nr++] = here; lit(0); next(); /* a label of this block */ + } + for (i = 0; i < nr; i++) { + for (k = -1; ++k < nl && strcmp(ln[k], rn[i]);) ; + if (k == nl) die("unknown asm label"); + setlit(ra[i], la[k]); + } +} + +/* ---- statements ---- */ +static int brk[256][64], nbrk[256], cont[256], nloop; +static int epi[1024], nepi; +static void stmt(void); +static void block(void) +{ + int save = nlo; + expect("{"); + while (!accept("}")) { if (tk == T_EOF) die("missing }"); stmt(); } + nlo = save; +} +static void jmp_to(int t) { lit(t); op(O_JMP); } +static void loopbody(int contaddr) +{ + nbrk[nloop] = 0; cont[nloop++] = contaddr; + stmt(); + nloop--; +} +static void patchbreaks(void) { int i; for (i = 0; i < nbrk[nloop]; i++) setlit(brk[nloop][i], here); } +static void stmt(void) +{ + int a, b, c, d; + if (accept(";")) return; + if (is("{")) { block(); return; } + if (accept("let") || accept("var")) { + do { + Loc *l = &lo[nlo++]; strcpy(l->name, ident()); l->arr = 0; + if (accept("[")) { int n = cexpr(); expect("]"); nslot += n; l->slot = nslot - n; l->arr = 1; } + else l->slot = nslot++; + if (accept("=")) { expr(); rval(); laddr(l->slot); op(O_ST); } + } while (accept(",")); + expect(";"); return; + } + if (accept("unchecked")) { stmt(); return; } + if (accept("if")) { + expect("("); expr(); rval(); expect(")"); + a = here; lit(0); op(O_JZ); stmt(); + if (accept("else")) { b = here; lit(0); op(O_JMP); setlit(a, here); stmt(); setlit(b, here); } + else setlit(a, here); + return; + } + if (accept("while")) { + c = here; expect("("); expr(); rval(); expect(")"); + a = here; lit(0); op(O_JZ); + loopbody(c); jmp_to(c); setlit(a, here); patchbreaks(); return; + } + if (accept("for")) { + int save = nlo; + expect("("); + if (!is(";")) { if (is("let") || is("var")) { stmt(); goto cond; } expr(); rval(); op(O_DROP); } + expect(";"); + cond: c = here; + if (!is(";")) { expr(); rval(); } else lit(1); + expect(";"); + a = here; lit(0); op(O_JZ); b = here; lit(0); op(O_JMP); + d = here; + if (!is(")")) { expr(); rval(); op(O_DROP); } + expect(")"); jmp_to(c); + setlit(b, here); + loopbody(d); jmp_to(d); setlit(a, here); patchbreaks(); + nlo = save; return; + } + if (accept("break")) { if (!nloop) die("break outside a loop"); brk[nloop - 1][nbrk[nloop - 1]++] = here; lit(0); op(O_JMP); expect(";"); return; } + if (accept("continue")) { if (!nloop) die("continue outside a loop"); jmp_to(cont[nloop - 1]); expect(";"); return; } + if (accept("return")) { + if (!is(";")) { expr(); rval(); } else lit(0); + expect(";"); epi[nepi++] = here; lit(0); op(O_JMP); return; + } + if (is("asm")) { next(); asmblock(); return; } + expr(); rval(); op(O_DROP); expect(";"); +} + +/* ---- top level ---- */ +static void fndef(void) +{ + char *n = ident(); Sym *s; int np = 0, tags = 0, t, ret = 0, frame, i; + char pn[16][64]; + s = find(n); + if (!s || s->kind != K_FN) { s = add(n, K_FN, 0); s->defd = 0; } + expect("("); + if (!accept(")")) { + do { + t = 0; + if (accept(".")) { /* .m name, .d name */ + if (is("m")) t = 1; else if (is("d")) t = 2; else die("a parameter tag is .m or .d"); + next(); + } + if (np < 12) tags += t << (2 * np); + strcpy(pn[np++], ident()); + } while (accept(",")); + expect(")"); + } + if (accept("->")) { expect("."); if (strcmp(ident(), "u")) die("the return tag is -> .u"); ret = 1; } + s->sig = ret + 2 * np + 64 * tags; + if (accept(";")) return; /* prototype */ + if (s->defd) die("function defined twice"); + s->val = here; s->defd = 1; + nlo = 0; nslot = 0; nepi = 0; infn = 1; + for (i = 0; i < np; i++) { strcpy(lo[nlo].name, pn[i]); lo[nlo].arr = 0; lo[nlo++].slot = nslot++; } + /* prologue: fp -= frame, then pop the arguments into their slots */ + lit(ZFP); op(O_LD); frame = here; lit(0); op(O_SUB); lit(ZFP); op(O_ST); + for (i = np; i--;) { laddr(i); op(O_ST); } + block(); + lit(0); + for (i = 0; i < nepi; i++) setlit(epi[i], here); + lit(ZFP); op(O_LD); lit(nslot); op(O_ADD); lit(ZFP); op(O_ST); op(O_RET); + setlit(frame, nslot); + infn = 0; +} +/* enhance name(params) [-> n] micro "recipe"; or ... vm { asm }; + emits the install descriptor; the booted genome installs it */ +static int nenh; +static void enhdef(void) +{ + char *n = ident(), pn[8][64]; int np = 0, nout = 1, d, na, pay, len = 0, i, j; Sym *s; + expect("("); + if (!accept(")")) { do { if (np == 8) die("at most 8 inputs"); strcpy(pn[np++], ident()); } while (accept(",")); expect(")"); } + if (accept("->")) { nout = tv; next(); } + d = here; for (i = 0; i < 7; i++) emit(0); + na = here; for (i = 0; i < np; i++) emit(0); + for (i = 0; i < np; i++) { img[na + i - GORG] = here; for (j = 0; pn[i][j]; j++) emit((unsigned char)pn[i][j]); emit(0); } + if (accept("micro")) { pay = here; while (tk == T_STR) { for (j = 0; ts[j]; j++) emit((unsigned char)ts[j]); next(); } emit(0); } + else if (accept("vm")) { pay = here; asmblock(); op(O_RET); len = here - pay; img[d + 1 - GORG] = 1; } + else die("expected micro or vm"); + img[d - GORG] = nenh; img[d + 2 - GORG] = np; img[d + 3 - GORG] = nout; + img[d + 4 - GORG] = pay; img[d + 5 - GORG] = len; img[d + 6 - GORG] = na; + s = add(n, K_ENH, nenh++); s->sig = np + nout * 16 + d * 256; + expect(";"); +} +static void top(void) +{ + while (tk != T_EOF) { + if (accept("unchecked")) continue; + if (accept("fn")) { fndef(); continue; } + if (accept("const")) { do { char *n = ident(); expect("="); add(n, K_CONST, cexpr()); } while (accept(",")); expect(";"); continue; } + if (accept("enhance")) { enhdef(); continue; } + if (accept("let") || accept("var")) { + do { + char *n = ident(); + if (accept("[")) { int k = cexpr(), a = here; expect("]"); while (k--) emit(0); add(n, K_GARR, a); } + else { + int a = here; add(n, K_GVAR, a); emit(0); + if (accept("=")) { + if (tk == T_STR) { int i, l = (int)strlen(ts); img[a - GORG] = here; for (i = 0; i < l; i++) emit((unsigned char)ts[i]); emit(0); next(); } + else img[a - GORG] = cexpr(); + } + } + } while (accept(",")); + expect(";"); continue; + } + die("expected fn, var or const"); + } +} + +int main(void) +{ + static char buf[1 << 22]; int n = (int)fread(buf, 1, sizeof buf - 1, stdin), i, j; Sym *st; + buf[n] = 0; src = buf; next(); + /* image header: set the frame pointer, jump to start, then the symbol blob pointer and image end */ + lit(FTOP); lit(ZFP); op(O_ST); fnref("start"); op(O_JMP); emit(0); emit(0); + top(); + for (i = 0; i < nfx; i++) { + Sym *s = &sy[fx[i].sym]; + if (!s->defd) { fprintf(stderr, "zc0: undefined function %s\n", s->name); return 1; } + setlit(fx[i].at, s->val); + } + if (!(st = find("start")) || !st->defd) { fprintf(stderr, "zc0: no start\n"); return 1; } + /* symbol blob: kind, value, sig, name chars, 0 ... then a 0 kind */ + img[5] = here; + for (i = 0; i < nsy; i++) { + emit(sy[i].kind); emit(sy[i].val); emit(sy[i].sig); + for (j = 0; sy[i].name[j]; j++) emit((unsigned char)sy[i].name[j]); + emit(0); + } + emit(0); + img[6] = here; + printf("#define GORG %d\n#define GLEN %d\nstatic const int genome[] = {", GORG, here - GORG); + for (i = 0; i < here - GORG; i++) printf("%s%d", !i ? "\n" : i % 16 ? "," : ",\n", img[i]); + printf("\n};\n"); + return 0; +} diff --git a/docs/history/13-gap-fixes/notes.md b/docs/history/13-gap-fixes/notes.md new file mode 100644 index 0000000..f7e5ab5 --- /dev/null +++ b/docs/history/13-gap-fixes/notes.md @@ -0,0 +1,48 @@ +# gap fixes + +code/ next to this file is the tree at the end of this stage. + +## message 13 + +> gaps +> 1. when there's a var state change to d, mark that var as dead by default for any other scope that can access it afterwards. u is fine since it just returns +> 2. any suggestions for a cheap fix? +> 3. it's fine, rare occurrence (probably?), suggest a fix +> 4. fix that +> 5. suggest a fix +> 6. fix +> 7. fix + +**summary:** the numbers followed the gap list from my previous chat message. here's what each one got. + +**1. joins.** a variable that became d, or was given away, on any path is dead after the join, and fixup code frees it on the paths that still own it. a loop's exit state is the join of its breaks and its normal exit. giving something away on a path that loops back is still an error. u and borrows don't get the dead treatment. + +**2, 3 and 5.** suggestions only, now under open ends: a quarantine on reuse plus "borrowed from" marks on pointer-typed values for aliases, a stack-depth opcode for the collector, and page-indexed invalidation plus per-slot install tracking for the jit. + +**4. heap.** a buddy allocator. + +**6. stack checks.** all three stacks are checked: data and return at every block exit on all backends, and frames in every prologue. + +**7. the small stuff.** error recovery with up to 20 reported errors, line numbers carried in token lists, a signature prescan over the whole unit so prototypes are optional, storing into a borrowed object moving the object automatically, `?:`, and go-style `switch`. on the side, a 4-token lookahead fixed `**` declarations. + +## message 14 + +> how costly is it if we add a counter to each block to see how many times it runs, then run the optimizer on it if it's a hot path? + +> continue + +**summary:** answered without building anything. about 4 instructions per block entry while a block is being counted, and nothing once its optimized translation replaces it. the optimizing pass would reuse the enhancement lifter, optimizer and register allocator. lifting whole blocks would need jz side exits and barriers at calls and i/o. compiling a block that way takes microseconds, against milliseconds for llvm, and keeping values in registers instead of the memory stack likely gives 2 to 4x on tight loops. it's listed under open ends. then the gap fixes continued. + +## working notes + +the join rule from message 1 needed a place to put frees, because the merged state is only known after the last path has compiled. for if/else, the then branch already jumps over the else, so that jump now lands on a small fixup block after the else when it has frees to do, and on the join otherwise. if without else does its then-fixups inline, since the skip path's state (the state before the branch) is known in advance. loops record the states at every break in an exit record, and after the loop each break gets its own fixup stub, or a direct jump when it has nothing to free. the normal exit's fixups come last and fall into the end. switch is built on the same loop frames, with continue skipping past them to the real loop. + +error recovery without exceptions: the vm can't unwind its own stacks, so a syntax error returns normally and sets panic. the error consumes the token it happened on, which guarantees every loop makes progress, and statement and top-level parsing skip ahead until panic clears. the risky part was code after an error that used a bad lookup result (sval[-1] and the like), which on the jit would read outside memory. those spots return safe defaults now. + +the prescan reads the whole unit first, packed 4 chars to a word (a char per word would have needed 400k words of heap for the compiler's own source), then lexes it once with the analyzer and errors muted. signatures it finds are marked as prescanned, so a real definition quietly overrides a wrong guess. + +bugs on the way: deleting a one-line function with a "find the next closing brace" helper also deleted the function after it. the variable d bit again in new code, since u, m and d are reserved. and a rename helper turned die into sdie when it was supposed to produce serr. + +the buddy heap: freed blocks merge with their buddy while it's free, the same size, and inside the same top-level chunk. the sweep walks the heap by block sizes, and a merge can swallow the block that comes next, so the sweep continues from the end of the merged block. a merge also clears the absorbed header, so no stale header can look like a free block later. + +numbers: the genome is 71014 words. the per-prologue frame check costs about 12 words per function, which accounts for most of the growth. diff --git a/docs/history/15-o3/code/README.md b/docs/history/15-o3/code/README.md new file mode 100644 index 0000000..e4859f2 --- /dev/null +++ b/docs/history/15-o3/code/README.md @@ -0,0 +1,511 @@ +# tinyvm zygote: notes and transcript + +a tiny stack cpu that started as a c89 code golf entry and ended up as a self-hosting runtime. a small native core runs vm code: jit-compiled to arm64, compiled through llvm on other hosts if you build with it, or interpreted. the genome it boots is a compiler for zc, a c-like language that keeps the forth habits. the compiler lives in vm memory next to your program, and your program can call into it, patch its tables, add syntax to it and compile new code while it runs. heap objects carry an ownership tag (u, m or d) that a static analyzer inside the compiler checks, with deterministic frees at scope ends and a mark-sweep collector underneath. types are a compiler primitive, with structs, generics, reflection and an optional typechecker. a program can also wire up to 512 custom opcodes into the cpu (virtual enhancements), which the jits inline as native code. and code marked `o3` skips the stack machine: the core compiles it as a whole region, with its variables in registers. + +this folder has the latest code. `history/` is the scrapbook: notes for every stage, plus frozen code from the stages that still have it. + +## files + +`core.c` is the native core and the only native code at runtime: the arm64 jit, the llvm backend (behind `-DZYLLVM`), the portable interpreter, the enhancement unit (recipe parser, vm-code lifter, optimizer and lowering) and the o3 region compiler. `src/rt.zc` is the runtime (i/o, the native division enhancements, the heap and the collector) and `src/compiler.zc` is the compiler, both written in zc. together they are the genome. `zc0.c` is stage 0 of the bootstrap, a compiler in c for the subset of zc that `compiler.zc` is written in. `tools/build.zc` makes the resident compiler compile its own source into a fresh image, and `mkgenome.c` turns that image into `genome.h`, which is generated and checked in. `examples/` has the small ones (hello, factorial, echo, self-patching code, an owning list, a repl, a typed program, an enhancement demo) and some bigger proofs of concept: + +- `mandel.zc`: the mandelbrot set in 16.16 fixed point, with the fixed-point multiply wired in as a custom opcode. +- `bf.zc`: a brainfuck compiler. it turns brainfuck into zc source, has the resident compiler compile it, and runs it (native code on the jit). it takes a program after a backtick, or runs hello world. +- `dump.zc`: prints any heap struct, nested ones included, with no printing code, by walking its type through reflection. +- `tree.zc`: a binary search tree whose nodes own their children. there's no `free` anywhere, and the live object count drops back to 0 when the root's block ends. +- `lang.zc`: grows the language while it compiles: a `|>` pipe operator (one table entry, since on a stack machine it's just `call`), `unless`, an `assert` that knows its source line, and `times n { }`. +- `staged.zc`: writes a polynomial as source with its coefficients baked in, compiles it while running, and finds it by name. +- `quine.zc`: prints its own source. +- `life.zc`: conway's life on a typed 2d board, with a glider wrapping around a torus. +- `primes.zc`: an o3 sieve over a fixed array, the primes gathered into a growable one, then twin primes and the widest gap. + +`tests/` has feature tests, `tests/errors/` (programs the lifetime analyzer has to reject), `tests/typeerrors/` (programs the typechecker has to reject) and `tests/langerrors/` (o3 and array misuse). each of those starts with the message it expects, and `tests/runerr.sh` runs a folder of them. `test.sh` does the whole bootstrap and runs everything on all three backends. `bench.sh` times o3 against plain code on each backend. `history/` is the scrapbook. + +## build + +``` +cc -O2 core.c io_linux.c -o zy # io_linux.c provides Q, G and J +./zy `). `ZYNOLLVM=1` turns the llvm tier off. with `ZYTRACE=1`, `ZYO3DUMP=1` also prints every o3 block's micro-ops. + +regenerating the genome after changing `src/`: + +``` +cc zc0.c -o zc0 && cc mkgenome.c -o mkgenome +cat src/rt.zc src/compiler.zc | ./zc0 >genome.h # stage 0, from c +cc -O2 core.c io_linux.c -o zy +(cat tools/build.zc; printf '`'; cat src/rt.zc src/compiler.zc) | ./zy | ./mkgenome >g.h +mv g.h genome.h && cc -O2 core.c io_linux.c -o zy # now built by itself +``` + +run the last two lines once more and `genome.h` should come out byte for byte the same. `test.sh` checks that, on every backend. + +## zc, the language + +it reads like c. underneath every value is a 32 bit word and memory is word addressed, so `p + 1` is the next word whatever `p` points at, and strings are one char per word with a 0 at the end. types are optional: untyped code is typed `word`, which goes with anything. + +``` +const N = 10; +[N]int table; // a global array +int fib(int n) { if (n < 2) return n; return fib(n - 1) + fib(n - 2); } +fn main() { + for (int i = 0; i < N; i++) table[i] = fib(i); + let sq = { let t = table[5]; t * t }; // a block is an expression too, its last line is its value + let x = asm { 2 3 + }; // raw vm code, leaves one value + putn(sq + x); putc(10); + return 0; +} +``` + +declarations are c-style, type first: `int a = 1, b;`, `*node p = new(node);`, `[4]char buf;`, `point origin;`. `let` declares without a type: it infers pointer, struct and function types from the initializer and keeps numbers as plain words, and `let a[10];` is an untyped array. statements are declarations, `if`/`else`, `while`, `for`, `switch`, `break`, `continue`, `return`, `{ }` blocks, `asm { }` and expressions. `switch (e) { case 1, 2: ... default: ... }` takes constant labels, and its cases don't fall through: each ends where the next begins, and `break` leaves early. operators are c's with c's precedence, including `c ? a : b`, plus `p.x` and `p->x` (the same thing: `.` goes through a pointer on its own), `e as T` to change a type without changing the bits, and the casts below. comparisons and `&& ||` give 0 or 1. `/`, `%` and `<<` compile to native enhancement opcodes. character literals, `0x` hex and the usual escapes are there. + +functions are declared like c, return type first: `int f(int x)`, `*node.u make()`, `.u grab()` (a tag on its own), `void log(*char s)`. `fn f(a, b)` is the untyped form, returning a word. a generic function puts its type parameters in front, ` T first(*list l)`. function types are written the same way, `int(int, *char)`, and `fn(int)` for one that returns a word. the earlier `fn f(...) -> T` spelling is gone. + +arrays come in three kinds. `[n]T` has its size in the type and lives in place. `[]T` is a growable array: a small handle object that owns its items, made with `new([]T)` or `new([]T, n)`, indexed with a bounds check, and changed with `push(a, x)`, `pop(a)` and `resize(a, n)`. `len(a)` works on both (for `[n]T` it's the constant) and `cap(a)` gives the room before the next grow. `fixed []T` is a `[]T` that can't change size: `new(fixed []T, n)` needs the length, and `push`, `pop` and `resize` on it are compile errors. a `fixed []T` can't be passed where a `[]T` is expected, the other way round is fine, and a constant index outside a `[n]T` is caught at compile time. the items belong to the array, so freeing it frees them, and objects stored into a `[]*T` are its children like any container's. + +a unit is read whole and scanned for function signatures before it compiles, so calls can come before definitions, tags included, and prototypes are optional. `main` runs after the whole program has compiled. every function returns one word, 0 if it falls off the end. + +builtins from the runtime: `putc`, `getc`, `eof`, `puts`, `putn`, `halt`, `strlen`, `streq`, `wcopy`, `wfill`, `new`, `new_t`, `new_raw`, `free`, `gc`, `isobj`, `osize`, `typeof_obj`, `live_objects`, `install`, `zdiv`, `zmod`, `zshl`. + +## memory model: u, m and d + +`new(n)` or `new(T)` gives back a fresh object. bind it to a local and the local owns it, tagged **m**. tags live on local variables and parameters, the analyzer follows them through the code, and the compiler inserts `free` calls where owned objects die. a tag is written after the type, `*node.u p`, or alone, `.d x`. `u`, `m` and `d` are reserved words. + +**m** means the object belongs to this block and has to be used in it. you can lend it to callees as often as you like, and when the block ends it's freed. an m object that's never used anywhere in its block is an error. + +**d** means the object is going down. the first time it's handed to a lower scope its life here is over: passing it to a `.d` parameter, or touching it inside an inner block, which then owns it as m and frees it when that block ends. using it afterwards is an error, and so is ending the block with a d object that never left. + +**u** means the object is going up. it has to leave this block for the one above, where it arrives as m. it leaves through `return` (the function's return type has to carry `.u`, like `*node.u make()` or just `.u make()`), as the value of a block expression, or by assignment to a variable of the directly enclosing block. letting it fall out of scope, or throwing away a u result, is an error. + +recasting is multiplication by the tag: `u*x`, `d*x`, `m*x`. it only goes away from m: m can become u or d (and u and d can swap), but nothing goes back to m in the same scope. a declaration can carry a tag too, `.u r;`, and then whatever lands in it gets that tag. + +parameters: an untagged parameter is m from the caller's side, a lend, and the callee sees a plain word. `.m` makes it a checked borrow: the callee can read and write it but can't free it, return it, recast it or store it anywhere. `.d` hands the object over, and it arrives as m. + +``` +*int.u box(int v) { *int b = new(int); *b = v; return u*b; } +fn add(*list l, *int.d item) { l.items[l.len] = d*item; l.len++; return 0; } +``` + +containers own what you store in them. `p[i] = c` or `p.f = c` needs `c` to carry the same tag as `p` (or be a fresh object), and from then on `c` follows `p`: if `p` is recast, so is `c`, if `p` is given away `c` goes with it, and when `p` is freed its children are freed too. a child can't be recast or given away on its own. storing into an object you only borrowed gives the child away: it's dead here afterwards, and the caller's object owns it. the runtime records the owner in the child's header, so freeing a container frees exactly the objects it owns. + +globals, plain pointers and arrays aren't tracked. storing an owned object there needs `d*`, and the mark-sweep collector takes it over. a fresh object can go straight in. + +where paths join (after an `if`, a loop, a `switch` or `?:`) every variable has to be in the same state on every path that reaches the join, with one exception. a variable that became d, or was given away, on some path is dead afterwards, and the compiler frees it on the paths where it's still alive, on their way in. a loop's exit state is the join of its `break`s and its normal exit, so giving something away and breaking is fine. what a loop can't do is give something away on a path that comes back around to the top, since the next pass would use it dead. a u object doesn't get the dead treatment: it still has to leave. + +### the analyzer + +it runs inside the compiler on every function, including code compiled at runtime by `eval` and friends, and reports each violation with a line number and the variable's name. the compiler keeps going after errors, up to 20: a syntax error skips to the next `;` or `}` (or the next top-level declaration), and analyzer and type errors just get reported, since the parse is still in step. a unit with errors doesn't run. `tests/errors/` has one small program per rule. to turn it off: `unchecked fn f() { }` for one function, `unchecked { }` for a block, and a top-level `unchecked;` for the rest of the unit (a parse word can also set `check_on = 0`). unchecked code doesn't track anything, so what it allocates belongs to the collector, and checked variables can't be touched from inside an unchecked block. + +## types + +types are a compiler primitive. the compiler keeps them as records in a type area at 0x9C000, hash-consed so the same type is always the same pointer. they stay in memory after compiling, so programs and parse words can look at them. + +``` +struct point { int x, y; }; +struct list { int len; *T items; }; +type num = int; + T first(*list l) { return l.items[0]; } + *list.u mk(int cap) { *list l = new(list); l.items = new(T, cap); return u*l; } +... +*list<*point> ps = mk<*point>(8); // explicit type arguments, or inferred from the arguments +``` + +the kinds are `word`, `int`, `char`, `bool`, `void`, pointers `*T`, arrays `[n]T`, `[]T` and `fixed []T`, function types `int(int, *char)`, structs, generic structs and generic functions. structs and arrays live in place (in a frame, a global or inside another struct), and their value is their address, so they're passed around by pointer. `sizeof` counts words. type arguments have to be one word (a number, a pointer or another parameter), so a generic function is compiled once and works for every instance. indexing scales by the element's size, while plain pointer arithmetic stays in words, like b. `new(T)` and `new(T, n)` stamp the type into the object's header. + +### reflection + +`typeof(e)` and `typeof(T)` give a type record and `sizeof(...)` its size. both work on any expression, which is compiled dry and never runs. at runtime: `tkind(t)` (one of the `TY_` constants), `tsize`, `tname`, `tlen`, `tnfields`, `tfname(t, i)`, `tftype`, `tfoff`, `tret`, `tnparams`, `tparam`, `tparamtag` (the u/m/d tag of a parameter), `tretu`, `tnargs` and `targ` for generic instances, and `tprint(t)`. `sigof(f)` gives the type of a function by address (untyped ones get a signature built from their arity and tags), `sigofs("name")` by name, and `typeof_obj(p)` reads the type stamped on a heap object. + +### the typechecker + +off by default. `typecheck on;` turns it on for what follows and `typecheck off;` turns it back off (a parse word can flip `tc_on`). it checks initializers, assignments, arguments (unifying generic parameters), return values, arithmetic, comparisons, dereferencing, indexing and calls. `word` matches anything, numbers mix with each other, a literal 0 is a null pointer, and arrays decay to pointers. it stops at the first error, printing what it expected and what it got. `tests/typeerrors/` has one per rule. some things are checked whatever the setting, because the compiler needs them to generate code: field names, and field access on something that isn't a struct. + +## virtual enhancements + +the cpu has room for 512 custom opcodes (22 to 533), and opcode 21 (`%` in asm) installs one at runtime. it's the closest a soft cpu gets to rewiring itself. an enhancement takes up to 8 inputs off the data stack and leaves up to 8 outputs, and it's written one of two ways: + +``` +enhance clamp(x, lo, hi) micro "x = max x lo; x = min x hi"; +enhance 2 divmod(a, b) micro "r2 = sdiv a b; r1 = srem a b; r0 = r2"; // the number is how many outputs +enhance cube(x) vm { 0 $ 0 $ * * }; +enhance noisy(x) vm { 0 $ . } else soft_noisy; // can't be lifted (it does i/o), so calls use the fallback +``` + +a micro recipe works on registers `r0`..`r15`, which the inputs arrive in (also under the parameter names), and the outputs leave from `r0` up. the micro-ops are `li mov add sub mul sdiv udiv srem urem and or xor shl shr sar neg not lt ltu eq ne min max minu maxu mulhs mulhu clz abs sel ld st`. a `vm` body is straight-line vm code that gets lifted by running it symbolically. both turn into ssa, and the core's optimizer folds constants, simplifies algebra, removes common subexpressions and dead code, and turns division by a constant into a multiply-high sequence (`ZYTRACE=1` prints the result). the arm64 jit then inlines the code at every use with registers allocated, the llvm backend inlines it as ir, and the interpreter runs the optimized list. if something doesn't fit in registers, the jit calls the interpreter for that op. when literals feed an enhancement's last inputs (`x / 7` is `x 7 idiv`), the arm64 jit specializes that use on them: the literals become constants, the optimizer runs again, and `x / 7` turns into a multiply-high and two shifts instead of a divide. + +an enhancement is called like a function when it has one output, or by name from asm. `enhance` installs at compile time, so the wiring is there before `main` runs, and a parse word can use it right away. an image installs its own enhancements when it boots, from its symbol table. that's how `/`, `%` and `<<` work: `rt.zc` declares `idiv`, `imod` and `ishl` as one-instruction recipes. `install(desc)` is the raw interface, and its descriptor format is documented in `core.c`. stores done by an enhancement don't invalidate compiled code, so use them for data. + +this replaces my first plan for enhancements, which compiled c or llvm ir sources at runtime. part of that reply got stopped by a safety check, and once it turned out llvm was meant for portability, it moved into the backend below instead. + +## o3 + +`o3` marks code for the optimizing tier. it goes on a function, a block or a single variable: + +``` +o3 int sum(fixed []int a) { int s = 0; for (int i = 0; i < len(a); i++) s += a[i]; return s; } +fn main() { + o3 int hits = 0; // one o3 variable + o3 { for (int i = 0; i < 1000; i++) if (i % 7 == 3) hits++; } + ... +} +``` + +what it gives up, as asked: variables declared in o3 code must be typed (`let` and untyped parameters are errors), they have no address (`&x` and naming one from `asm` are errors), `new` in o3 code returns raw memory with no type stamp and no zero fill (`new_raw`), and an o3 function skips the frame stack check in its prologue. what it gets: the compiler hands each o3 function body and each o3 block to the core as a region (install kind 2: the range of vm words plus the frame slots of up to 7 typed variables, picked in declaration order), and the core compiles the region right away. + +inside the core, a region is cut into basic blocks at jump targets, branch fallthroughs and call return points. each block is lifted to ssa the way enhancements are: the vm stack becomes values, and only what's still on it at the end of the block is written to memory. the frame pointer and the promoted slots become variables that live in registers across the whole region, so `i++` on an o3 int is one add. enhancements are spliced into the block, so `/`, `%` and `<<` become one instruction or a magic-number sequence. every block then goes through the same optimizer (folding, algebra, cse, dce, division by constants). in o3 code `a[i]` on a `[]T` is compiled inline with its bounds check (`outside(i, len)`, one enhancement), and `len(a)` is a load. + +the backends: on arm64 the region becomes one piece of native code. variables sit in w0..w7, block values in w8..w14 and w26..w28, constants are folded into instructions where they fit, loads from a fixed address are one `ldr`, and blocks branch to each other directly with no dispatch and no stack checks. on the llvm build the region becomes one function, with the variables in allocas that mem2reg turns into ssa across blocks, and it goes through llvm's O3 pipeline. on the plain interpreter, a small region interpreter runs the optimized blocks with the variables in locals. + +calls, returns, jumps out of the region, i/o, `install`, a store to the frame pointer and anything else a block can't lift all leave the region: the variables are written back to the frame first, so outside code sees ordinary memory. a call's return point is an entry, which loads them again. stores inside a region still check the coverage map, so writing to compiled code works, and a store into the region's own code drops it (it lifts again at its next entry). to keep variable addresses from waiting on the stack across a branch, assignment to an o3 variable in o3 code evaluates the value first and the address after. the lifter refuses a block that would leave such an address on the stack, rather than miscompile it. single o3 variables outside o3 code are reached through `lfa(k)`, an enhancement for `fp + k`, which the arm64 jit specializes into a load and an add. + +`bench.sh` runs four kernels from `tests/bench.zc` with and without `o3`. milliseconds per run, start-up and compile time removed (arm64 is under qemu on an x86 host, so only its ratios mean much): + +``` +backend kernel plain ms o3 ms speedup +interp collatz 309.63 209.87 1.5x +interp sieve 45.58 15.95 2.8x +interp digits 191.32 92.29 2.1x +interp fib 6.70 5.50 1.2x +llvm collatz 27.69 3.48 7.9x +llvm sieve 17.32 0.17 101.9x +llvm digits 24.72 1.60 15.4x +llvm fib 1.82 0.84 2.2x +arm64 (qemu) collatz 327.33 16.78 19.5x +arm64 (qemu) sieve 90.23 3.85 23.4x +arm64 (qemu) digits 147.67 3.46 42.7x +arm64 (qemu) fib 22.62 5.73 3.9x +``` + +fib is the weak one, since every call leaves the region and comes back through the dispatcher. llvm's sieve number is llvm's loop optimizations doing their work on a region with no stack traffic left in it. + +## the llvm backend + +a tiered jit for hosts the arm64 backend doesn't cover. the interpreter counts how often each block start gets reached, and a hot block is translated to llvm ir using the same block rules as the arm64 jit: it ends at call, jump, ret or halt, keeps going past a jz, and literal targets are fused. the ir goes through the O2 pipeline and gets compiled by orc's lljit. stores into compiled words drop the blocks covering them, like on arm64. it pays off for long-running code: a loop-and-recursion benchmark runs about 3.3x faster than the interpreter. short bursty work like the self-compile is slower, since compile time dominates there. `ZYHOT=1` compiles every block, which is a good stress test, and the test suite runs that way too. + +## the heap + +0x40000 to 0x9BFFF, managed by a buddy allocator. the heap is split into aligned power-of-two chunks, every block is a power of two words, and a freed block merges with its buddy whenever that one is free too, so freed space can be reused at any size. (stage 15 fixed a bug here: `release` marked a container free before freeing its children, so a child whose buddy was the container merged into it while the container was still being freed, and corrupted a free list. growable arrays hit it every time, since a handle and its items are often buddies.) allocation finds the smallest free block that fits and splits it down. free blocks sit on a doubly linked list per class, so taking a buddy off its list during a merge is constant time. checked code frees deterministically. under that runs a mark-sweep collector, conservative and non-moving, which handles globals, unchecked code and anything given to it. it runs on `gc()`, which returns how many objects it freed, or when an allocation doesn't fit. the sweep frees through the same merging path. + +## code as data + +the compiler is in memory with your program and every name in it is callable. + +`asm { }` emits raw vm words: numbers push, `-5` pushes a negative, `#n` emits a raw word, one-char tokens from the mnemonic table are opcodes, `:name` defines a label local to the block, enhancement names emit their opcode, and other names push the address of a variable or function. + +`quote { ... }` is a token list as a fresh object: `[n, kind, value, line, kind, value, line, ...]`, so errors inside a macro expansion point at the line the tokens came from, where names, strings and operators are interned pointers (`intern("while")` gets one). `tokens(s)` makes one from a string, and `qdup(q)` copies one. + +`eval(q)` compiles a token list as a function body and runs it. `compile(q)` returns its address instead, `load(q)` compiles top-level declarations, and `evals(s)` runs `eval` on a string. + +parse words: `immediate fn name() { }` runs at compile time whenever `name` shows up as a statement, inside an expression or at the top level. it starts with the current token on its own name and works the compiler directly: `next()`, `peek()` and `peekat(k)` (up to 4 tokens ahead), `tk`/`tv` (current token kind and value), `expect`, `accept`, `expr()`, `stmt()`, `substmt()`, `ptype()`, `emit`, `lit`, `op`, `hole`/`setlit` for jumps, and `here`. `grab()` reads a `{ }` group into a token list, and `feed(q)` pushes a list back into the input, which is all a macro needs. parse words can call `eval` partway through a compile: the compiler saves its state and emits the new function in place with a jump around it. the built-in keywords are entries in the same table, so a parse word called `while` replaces `while`. + +tables: `add_binop("**", 11, "ipow c")` adds or replaces a binary operator with a precedence and its code, written like asm (names resolve when it's used). `add_unop` does the same for prefix operators, and the lexer learns new spellings from both. `mnem` is the asm mnemonic table, `q@!$_~cjzr,?.+-*&|^><%`, and can be rewritten. + +## the bootstrap + +`zc0.c` compiles the subset `compiler.zc` uses (no analyzer, types, quote, parse words or block expressions, but it does lay out `enhance` declarations) into a first genome. that genome compiles `compiler.zc` again on the vm. that's where the real compiler, the analyzer and native division first meet their own source, and `compiler.zc` passes its own lifetime checks. the result compiles itself into identical words, on all three backends, and that's the `genome.h` that ships (78217 words). an image build (`build_image(org, dst)`) compiles with only the image's own names visible (plus keywords and builtin types), writes code meant for one address into another, and appends a symbol table so the booted genome knows its own names and enhancements. + +## the instruction set + +every word is 32 bits. an odd word is a literal that pushes `w>>1` (31 bits, sign extended). an even word is an opcode, `w/2`. there's a data stack and a return stack, both stored inside memory. + +``` +0 q halt 5 ~ swap 10 , in (Q) 15 * mul 20 < less +1 @ load 6 c call 11 ? empty (J) 16 & and 21 % install +2 ! store 7 j jump 12 . out (G) 17 | or 22..533 enhancements +3 $ pick 8 z jz 13 + add 18 ^ xor +4 _ drop 9 r ret 14 - sub 19 > shr +``` + +stack effects: `!` is `(v addr --)`, `$` is `(n -- copy of the nth item)` so `0 $` is dup, `z` is `(cond addr --)` and jumps when cond is 0, `c` and `j` take their target from the stack, and `%` is `(descriptor -- status)`. there's still no div or mod in the base set, because a divider can't finish in one cycle in hardware. an enhancement can add one. + +## memory map and abi + +memory is 1m (1048576) words. it could grow until the host runs out (literals already reach 1g words), but that would mean reserving the jit tables with mmap instead of static arrays, and 1m is plenty for now. + +``` +0x00000.. the program: code, with globals between functions +0x40000..0x9BFFF heap +0x9C000..0x9FFFF type records +0xA0000 frame pointer word +0xA0100..0xBFFFF compiler data: interned names, symbol table, locals, tables +0xC0000.. genome: 0xC0000 entry, 0xC0005 symbol table address, 0xC0006 image end +0xE0000..0xEFFFF frame stack, grows down from 0xF0000 +0xF0000.. return stack, grows up +0xF8000.. mark stack +0xFC000..0xFFFF0 data stack, grows down +``` + +calls push the arguments left to right and the callee's prologue pops them into its frame. locals live in the frame at `*0xA0000 + slot`. every function leaves exactly one word on the data stack. heap objects have a 6 word header: size, size class, flags, a check word, owner, type. + +## how the core works + +registers: x19 holds the memory base, x20 the data stack pointer, x21 the return stack pointer, x22 the translation table and x23 the state block (memory, s, r, table, coverage map, Q, G, &J, store hook, install hook, enhancement fallback). o3 regions also use x26..x28, which the entry stub saves. + +compilation is lazy. the first time control reaches an address, that block is translated from live memory. a block ends at call, jump, ret or halt. a `z` doesn't end it: the not-taken side keeps compiling, which makes superblocks. a literal followed by `c`, `j` or `z` is fused into a constant target. every exit goes through a dispatch stub that checks both stack pointers against their limits (kept in x24 and x25), bounds-checks the target, loads `tab[t]`, and branches straight to it when it's already compiled. otherwise it returns to c, which compiles the block and re-enters. enhancements are inlined: inputs load from the data stack into w0..w15, the optimized micro-ops run in registers with x16 and x17 as scratch, and the outputs go back. an install calls into c and then returns to the loop, which flushes every translation, since code built before the install may have inlined the old version. a flush drops o3 regions too, and they lift again on their next entry. when the loop reaches an o3 entry point, it compiles the region (or reuses it) instead of translating a block. + +self-modification is handled with a byte map of which words some live translation covers. a store checks that byte inline: an uncovered target, the usual case, gets a plain store. a covered target calls the hook, which drops every translation containing that word and makes the jit code return to c at the next instruction, so even patches to the very next word take effect. writing back the same value is a no-op. if the 16 mb code buffer fills, everything is flushed. + +the stacks are checked. the data and return stacks are checked at every block exit on all three backends, so the cost is a couple of compares per block rather than per push. a block pushes at most about 1024 words, which the data stack limit leaves room for. overflowing either one stops the cpu with `zy: data stack overflow` or `zy: return stack overflow`. the frame stack is checked in every function prologue, which resets the frame pointer and stops with `zy: frame stack overflow`. + +platform: the darwin path uses `MAP_JIT`, `pthread_jit_write_protect_np` and `sys_icache_invalidate`. linux uses rwx mmap and `__builtin___clear_cache`. only linux arm64 has actually been run, under qemu. the darwin path follows apple's documented recipe but hasn't been tested, and under the hardened runtime it also needs the allow-jit entitlement. every host shares the same semantics: shift counts wrap at 32, stores outside memory are ignored, loads outside memory read 0, and jumping outside memory or to an unwired opcode halts. + +## test results + +`test.sh` passes all 125 checks on x86-64 linux: the fixpoint, an up-to-date `genome.h`, and then every example and feature test on the interpreter, on the arm64 jit under qemu, and on the llvm backend, all with identical output. it also covers joins and fixup frees (with a leak count), `switch` and `?:`, the buddy allocator, several errors in one run, all three stack overflows, growable and fixed arrays, and `tests/o3.zc` (loops, recursion, calls inside loops, `switch`, `&& ||`, structs through pointers, arrays, division by constants, and a function with more variables than registers), whose output matches the same file with `o3` taken out. it checks that every o3 region in that test lifts, and gets compiled on the jit and on llvm, that the o3 bounds check fires, and that all 22 programs in `tests/errors/`, all 10 in `tests/typeerrors/` and all 8 in `tests/langerrors/` get rejected with the expected message on each backend, that llvm agrees when it compiles every block, and that the self-compile matches byte for byte on all three. separately, 80000 random divisions through enhancements matched the software routine, covering divide by zero, INT_MIN/-1, negatives and the magic-number path for constants. + +## open ends + +fixed in stage 13 (details in `history/13-gap-fixes`): branch and loop joins, free lists that never merged (now a buddy allocator), missing stack checks, stopping at the first error, macro line numbers, required prototypes, `d*` for storing into borrowed objects, `**` declarations, and `?:` and `switch`. still open: + +**untracked aliases.** the analyzer only follows bare variable names, so `let q = p + 0;` or `let c = list[1];` is a plain word nobody tracks, and it dangles once the owner is freed. two cheap fixes, and they combine well. at runtime: don't reuse a freed block right away. `free` would put it on a quarantine list, and the collector's next sweep only releases quarantined blocks that no word points at. the conservative mark already finds such words, so that's maybe 30 lines in rt.zc. a stale alias then reads a dead but intact block, and a second free through it is a no-op, because the header's flags say free. at compile time: now that there are types, a pointer-typed value computed from a tracked variable (`p + k`, `p.field`, `p[i]` when the element is a pointer) can carry a "borrowed from p" mark into the local it's stored in. using that local after `p` dies becomes an error. that's the same machinery children already use. + +**the conservative collector.** it scans the whole data stack region, because the vm can't read its own stack pointer, and stale words keep garbage alive a little longer. the fix is one base opcode that pushes the data stack depth, which is trivial in hardware since the register already exists. then the collector scans only the live part. zeroing a frame in the prologue would do the same for the frame stack, at a few words per call. + +**coarse invalidation.** a store to a covered word rescans every block, and an install flushes everything. three fixes, in order of payoff. first, index blocks by page (say 64 words): a covered store only looks at the blocks touching its page, and dropping a block only rebuilds the coverage bytes for that block. second, record which enhancement slots each translation inlines, so an install only drops those. third, on llvm, give each block its own resource tracker, so dropped code actually gets freed. + +**automatic tiering.** o3 is the explicit version of what you asked about in message 14. doing it without the keyword means a run counter per block (about 4 instructions per block entry) and, for a hot loop, finding its region and its variables without the compiler's help. the hard part is the variables: o3 can keep them in registers because the compiler promises nothing takes their address, and code without that promise would need the lifter to prove it. + +**o3's own gaps.** calls always leave the region through the dispatcher, so call-heavy code gains least (fib above). a direct call from one compiled region into another, with the variables spilled only for the slots the callee can't see, would fix most of that. only 7 variables get registers, picked by declaration order rather than by use. a fixed array's length and items pointer can't change, so o3 code could load them once per region instead of at every index, and doesn't yet. the core's own optimizer works one block at a time (no loop-invariant motion or cross-block cse); llvm does that on its build. regions aren't built into images, so `rt.zc` and the compiler can't use o3. and o3 code that rewrites its own region sees the change at the next entry, not right away. + +**generics are erased.** a generic function is compiled once, so the type stamped by `new(T)` inside it says `T`, and type arguments have to be one word. + +**the rest.** the prescan only knows tags, not types, so a call to a typed function that's defined further down gets `word` as its result type until the definition shows up. the llvm backend compiles one module per block, so it warms up slowly. stores done by enhancements don't invalidate compiled code. a parse word that changes the syntax can confuse the prescan. it only records signatures, though, and a real definition always overrides what the prescan guessed. + +--- + +## transcript + +each of my messages is quoted exactly, followed by a summary of the reply. + +### 1 + +> codegolf a tiny cpu vm in C89. the goal is to achieve the lowest character count possible. include an assembler as well (scored separately) and a hello world program (not scored, but elegance and simplicity is a bonus) +> it must be capable of re-entrant code and a realistically workable instruction set. + +**summary:** a two-stack forth-style machine. the vm was 384 bytes: one ternary chain inside `while(o=m[p++])`, the top two stack items cached in `a` and `b`, the program loaded with `read(open(...))` so it needs no headers, and both stacks inside the 65536-word memory. it had 22 opcodes including div and mod, a two-word literal, and jump/call targets popped from the stack. the assembler was 348 bytes and two-pass, using `strcspn` instead of `strchr` so the implicit int return stays safe on 64-bit. hello world was a pointer loop over a string with a zero terminator. re-entrancy was shown with a recursive factorial plus a recursive decimal printer. + +### 2 + +> your cpu should also be realistically close to hardware (translating it to logical circuitry hardware should be fairly easy) and sport an acceptably fast execution (if hypothetically ported to hardware, not in software) +> +> assume i/o is handled by two external functions, `char Q()` (getchar) and `void G(char)` (putchar) and a state variable J (boolean, shows if input is empty). +> +> don't be afraid to use macros. + +**summary:** no reply was sent to this message at the time. it was answered together with message 3. + +### 3 + +> pro tip: if you define a macro as `!o--`, you can save 2 characters per if condition +> oh and btw, newlines are free + +**summary:** used the tip as `#define X !--o?`, with pre-decrement because opcode 0 (halt) is already handled by the loop, and with the `?` folded into the macro and free newlines used as separators. the hardware-oriented redesign: bit 0 of each word is a literal flag, so literals take one cycle like the j1 cpu. div and mod were dropped because they can't finish in one cycle and were replaced by an arithmetic shift right, giving 8 alu ops that decode from 3 bits. every instruction does a fixed amount of work with at most one data memory access, and call and jump share a datapath (`r-=o`). i/o now goes through `Q`, `G` and a `?` op that reads `J`. the result was the vm at 339 bytes and the assembler at 346. the final golfed code: + +```c +#define X !--o? +int m[1<<16],p,s=65e3,r=6e4,o,a,b;main(c,v)char**v;{read(open(v[1],0),m,s);while(o=m[p++])a=m[s],b=m[s+1],o&1?m[--s]=o>>1:(o/=2,X +m[s]=m[a]:X +m[a]=b,s+=2:X +m[s]=m[s-~a]:X +s++:X +m[s]=b,m[s+1]=a:(o-=2)<1?m[r]=p,r-=o,p=a,s++:X +p=b?p:a,s+=2:X +p=m[--r]:X +m[--s]=Q():X +m[--s]=J:X +G(a),s++:(m[++s]=X +b+a:X +b-a:X +b*a:X +b&a:X +b|a:X +b^a:X +b>>a:b now let's move in stages. write a compiler that takes this assembler's code and converts it into darwin arm + +**summary:** an ungolfed c89 compiler from vm assembly to darwin arm64 assembly. every word got its own label, the image was emitted as an addressable data array, x19/x20/x21 held the memory base and the two stack pointers, and computed jumps went through a table of offsets. a literal followed by a jump became a direct branch. `Q` and `G` went through `sxtb`, since char is signed on apple arm64, and `J` was read with `ldrb`. it was verified by assembling with llvm-mc for darwin, then rewriting to linux with sed and running under qemu, where it matched the vm byte for byte. this version couldn't handle self-modifying code. + +### 5 + +> can you write the assembler in assembly and pair the code with the assembly and the assembler so that it can re-wire itself at runtime? or maybe include an interp for self-modification, otherwise fns are exec'd native + +**summary:** both. `aslib.s` is the assembler written in vm assembly as a library (`a.src`, `a.dst`, `a.org`, `a.lab`, then `a.run c`), and with `asmain.s` it's a 568-word standalone assembler. it's byte-identical to the c assembler and reaches a fixpoint when assembling itself. this is also where the `:;comment` trick came from. the compiler became a hybrid. the image is split into basic blocks, each block counts how many of its words differ from the original image, and stores into code go through a slow path that updates the counters. clean blocks run native, while dirty blocks and anything past the image run in an interpreter compiled into the same binary, which hands control back to native code whenever it lands in a clean block. `cc -t` printed `[` and `]` at each switch. the demo rewired a function at runtime to point at freshly assembled code and then restored it. + +### 6 + +> which one do you like more? and which is more performant? +> keep that one, then port to using llvm ir so that it can be platform independent + +**summary:** no reply was sent at the time. the llvm port was dropped when message 7 changed direction. the question was answered at the start of the reply to message 7: the hybrid was the favorite because it kept the vm's semantics exact. the plain compiler was slightly faster on code that never rewrites itself, but it silently ran stale code once anything did. the zygote beats both. + +### 7 + +> can you make it a zygote approach? +> a minimal core loads first, reads and compiles the program (either wholly or partially, for speed) and replaces it with itself. so it's like a jvm but self-contained and on the same level as itself, where the assembler can itself be modified in-memory to assemble different code. for this we need to ditch llvm + +**summary:** the current design, described above. the native core only does jit and glue. it loads the genome (boot loader + assembler, both vm code) at 48000. the boot loader reads source until eof or a backtick, assembles it at 0 and jumps there. compilation is lazy per block, straight from live memory, with superblocks through `z`, fused constant targets, and dispatch chained through a table. invalidation uses the coverage byte map plus a store hook, so self-modified code gets retranslated instead of interpreted. the resident assembler sits at fixed addresses, and `repl.s` rewrites its mnemonic table so `p` means out, then assembles and runs input chunks in a loop, overwriting the same address each time. verified under qemu on linux arm64, including the self-hosted assembler running on the zygote. about 4x faster than the golfed interpreter. + +### 8 + +> damn dawg you made an entire baremetal os? +> +> let's move to cowork. pack everything up alongside a transcript of every message i've sent and your own notes on what you've implemented in an md file, with the code, so i can move it. + +**summary:** not quite an os. it's a userland runtime. it still runs on darwin or linux, uses their mmap for executable memory, and gets i/o from the host through `Q`, `G` and `J`. there's no scheduler, no drivers and no hardware access, so a closer description is a tiny self-hosting jit, in the spirit of a jvm. the first packing attempt bundled every stage's code with a full test script. + +### 9 + +> nono we don't need all the code, just the recent ones. my msgs and your summaries are more important and all should be there + +**summary:** this package. only the zygote stack plus its examples and tests are included, and earlier stages appear only as summaries. the golfed reference vm and assembler are inlined under message 3, because they define the instruction set. + +### 10 + +> we're gonna make this thing cooler. make the syntax C-like whilst still keeping the forth capabilities like super-homiconicity and the likes, add explicit and automatic gc (use a fast approach) and memory management that looks like this: +> every heap object has a classifier u m d, up middle down. up means the object will die at the scope above (becomes m when returned to the parent scope, and it must be returned and cannot be discarded). m means it must be used here and can be passed down (but once it's recasted as u or d it can't be casted back to m in this scope). d means the variable is passed down, and its lifetime ends after it's given for the first time to a scope lower than the current one. +> ask me questions to clarify this approach, then add it to the code and also add a simple static analyzer which checks these lifetimes + +my answers to the clarifying questions, exactly: + +> scope: every { } block +> +> lending an m object to a callee: borrow, no escape (Recommended) +> +> storing a heap object inside another object or a global: container gets ownership. putting a value inside a container requires its umd to match the parent, and from then on, the umd for that is also inherited from the parent (as in if a parent m is casted to d, so is the child heap object) +> +> gc: free lists + mark-sweep (Recommended) +> +> where the compiler lives: resident in vm code. bootstrap it step by step, by writing a c-like in core.c and then rewriting the compiler inside the c-like. also bring back llvm if you think it'll make your job easier +> +> homoiconic features: parse words, quoted code blocks, inline stack code, patchable compiler tables +> +> how an inner block hands a u object up: expression and assign, both +> +> when the analyzer runs: inside the compiler but can be turned off if the code requests it + +**summary:** the genome is now zc, a typeless c-like language, described above. stage 0 is `zc0.c` (kept as its own file next to core.c rather than inside it, so the runtime still has one native file), and the resident compiler is `src/compiler.zc` plus `src/rt.zc`, which reached a byte-identical fixpoint compiling itself. llvm wasn't brought back, since the target is the vm itself. memory went to 1m words, and core.c gained an interpreter for non-arm64 hosts. calls i made without asking: tags go on signatures (`fn f(m a, d b) -> u`) and recasts are `x as u` / `x as d`; a d object touched inside an inner block moves into that block; storing into a borrowed object, a global or untracked memory needs `as d`; handing a u up by assignment only reaches the directly enclosing block; unchecked code gives its objects to the collector. the old asm genome moved to `legacy/`. + +### 11 + +> progress report? +> add types please :) +> mostly as a compiler primitive though, plus type-inspection via reflection +> and a small optional typechecker (default off) +> oh and keep the stuff you write as a history scrapbook +> if it helps, add a path to the hardware which is called "virtual enhancements", and allows up to 512 custom operations to be compiled down via jit into hardware-level speeds and interoped directly into a fast path in the vm to become essentially custom wiring. useful for division and other stuff as a dynamic runtime path + +my answers to the clarifying questions, exactly: + +> types: words + structs + generics +> +> reflection: runtime type of heap objects, function signatures, typeof at compile time +> +> how an enhancement is defined: either actual C or llvm ir code or vm code or micro-ops passed through an optimizer. since for example, micro-ops and vm code are not sufficient for optimizing things like division +> +> scrapbook: notes + code snapshots + +**summary:** the progress report said the previous round was finished and shipped. i then laid out a plan where the core would compile c or llvm ir enhancement sources at runtime with the host toolchain and load the result. part of that reply was stopped by a safety check before it finished, so i stopped and checked in instead of building it. + +### 12 + +> also make the umd syntax like `type.u name`, and casting is multiplication `u*name` +> ideally i'd prefer memory to be unlimited words growing until system oom, but 1m is fine +> core.c plain interp is also nice +> zc0.c own file is fine +> i mentioned llvm for portability to non-arm targets +> yes, d object semantic that was intended +> u object hands up one block and becomes an m, that's fine. the code can make it a u or a d or whatever +> explain your gaps more +> +> wait why safety check? what's wrong with that + +my answers to the clarifying questions, exactly: + +> declarations: c-style, var -> let +> +> tags on untyped values and the names u, m, d: names reserved, bare .u allowed, untype is m by default +> +> llvm: llvm jit backend (Recommended) + +**summary:** on the safety check: nothing was wrong with the request. my best guess was that it tripped on the plan for a program inside the vm handing source text to the system compiler and loading the result into the running process, since that pattern also shows up in malware. with llvm meant for portability, that path wasn't needed. the gaps got the longer explanations that are now under open ends above. then the round itself: + +**syntax.** declarations are c-style (`int x`, `*node.m p`), `let` replaces `var` for untyped and inferred declarations, tags are `.u .m .d` after the type or on their own, recasts are `u*x` / `d*x` / `m*x`, `u m d` are reserved, and an untagged parameter counts as an m lend. + +**types.** a compiler primitive: hash-consed records with structs, generic structs and functions, fixed arrays, function types, `typeof`/`sizeof`, `as` casts and a reflection api, with object headers carrying their type. the typechecker is off by default and turned on with `typecheck on;`. + +**virtual enhancements.** opcode 21 installs, 22 to 533 are the slots. micro-op recipes and lifted vm code go through an optimizer in the core (folding, cse, dce, multiply-high division by constants) and get inlined by both jits. the language's own `/ % <<` now run on them. + +**backends.** a tiered llvm backend (`-DZYLLVM`) for non-arm hosts. + +**bootstrap and history.** it still reaches its fixpoint on all three backends. `history/` holds notes for every stage and code from stages 7, 10 and this one (in 11-types-enhancements). + +### 13 + +> gaps +> 1. when there's a var state change to d, mark that var as dead by default for any other scope that can access it afterwards. u is fine since it just returns +> 2. any suggestions for a cheap fix? +> 3. it's fine, rare occurrence (probably?), suggest a fix +> 4. fix that +> 5. suggest a fix +> 6. fix +> 7. fix + +**summary:** the numbers followed the gap list from my previous chat message. here's what each one got. + +**1. joins.** a variable that became d, or was given away, on any path is dead after the join, and fixup code frees it on the paths that still own it. a loop's exit state is the join of its breaks and its normal exit. giving something away on a path that loops back is still an error. u and borrows don't get the dead treatment. + +**2, 3 and 5.** suggestions only, now under open ends: a quarantine on reuse plus "borrowed from" marks on pointer-typed values for aliases, a stack-depth opcode for the collector, and page-indexed invalidation plus per-slot install tracking for the jit. + +**4. heap.** a buddy allocator. + +**6. stack checks.** all three stacks are checked: data and return at every block exit on all backends, and frames in every prologue. + +**7. the small stuff.** error recovery with up to 20 reported errors, line numbers carried in token lists, a signature prescan over the whole unit so prototypes are optional, storing into a borrowed object moving the object automatically, `?:`, and go-style `switch`. on the side, a 4-token lookahead fixed `**` declarations. + +### 14 + +> how costly is it if we add a counter to each block to see how many times it runs, then run the optimizer on it if it's a hot path? + +> continue + +**summary:** answered without building anything. about 4 instructions per block entry while a block is being counted, and nothing once its optimized translation replaces it. the optimizing pass would reuse the enhancement lifter, optimizer and register allocator. lifting whole blocks would need jz side exits and barriers at calls and i/o. compiling a block that way takes microseconds, against milliseconds for llvm, and keeping values in registers instead of the memory stack likely gives 2 to 4x on tight loops. it's listed under open ends. then the gap fixes continued. + +### 15 + +> write some really cool examples for this language + +> no, wdym building a fresh vm?? just pocs 😭 we already have a compiler and everything + +**summary:** my progress note had said "building a fresh vm", which only meant compiling core.c into a binary to run the examples. eight proofs of concept went into `examples/`: mandel, bf, dump, tree, lang, staged, quine and life, each described under files above and checked by `test.sh` on all three backends. + +### 16 + +> add opt keywords, for example `optim` (or o3, i prefer o3 as the keyword) will cause any vars in a scope to not assign heap type metadata, compiles with opt level o3 and is immediately compiled down to native, and when used with variables they must be always typed and it reduces the perf cost for accessing or modifying them, etc etc. add a `fixed` keyword which says that an array has a fixed size and cannot be resized, etc + +my answers to the clarifying questions, exactly: + +> where o3 goes: functions, blocks and single vars +> +> how far o3 goes: real optimizing tier (Recommended) +> +> what o3 code may give up: type stamps on objects, zero-filling new objects, the address of o3 vars, stack and frame checks, also i don't like the -> return type syntax. please replace it with the c-like syntax +> +> arrays: add growable arrays too (Recommended) + +**summary:** three things changed. + +**return types.** functions are declared c-style now, `int f(int x)`, `*node.u make()`, ` T first(*list l)`, with fn types written `int(int, *char)`. `->` is gone from the compiler, zc0, the tests and the examples. + +**arrays.** `[]T` grows (`push`, `pop`, `resize`, `len`, `cap`), and `fixed []T` can't change size after `new(fixed []T, n)`. both are bounds-checked. `[n]T` stays as it was, plus a compile-time check for constant indexes. + +**o3.** on functions, blocks and single variables, with the trade-offs you picked, and a real optimizing tier in the core, described in the o3 section above. it compiles at install time on arm64 and llvm and has a region interpreter for the plain build. measured gains are 1.5 to 2.8x on the interpreter, 8 to 100x on llvm and 20 to 43x on arm64 (under qemu) for loop-heavy kernels, and much less for call-heavy ones. on the way, the arm64 jit learned to specialize an enhancement on literal inputs, so `x / 7` is a multiply-high everywhere, not only in o3 code. tests for arrays and o3 turned up an old heap bug: freeing a container could merge one of its children into it while it was still being freed, which corrupted a free list. growable arrays hit it every time. it's fixed. diff --git a/docs/history/15-o3/code/bench.sh b/docs/history/15-o3/code/bench.sh new file mode 100755 index 0000000..c5e5a83 --- /dev/null +++ b/docs/history/15-o3/code/bench.sh @@ -0,0 +1,26 @@ +#!/bin/bash +# o3 against plain code: each kernel in tests/bench.zc on each backend, with +# the time to start up and compile (the 'z' run) taken off. needs bc. +cd "$(dirname "$0")"; T=$(mktemp -d); trap 'rm -rf $T' EXIT +gcc -O2 -w -DJTYPE=int core.c io_linux.c -o $T/zy +vms=("$T/zy"); names=("interp") +if command -v llvm-config >/dev/null; then + gcc -O2 -w -DJTYPE=int -DZYLLVM $(llvm-config --cflags) core.c io_linux.c -o $T/zyl $(llvm-config --ldflags --libs) && { vms+=("$T/zyl"); names+=("llvm"); } +fi +if command -v aarch64-linux-gnu-gcc >/dev/null && command -v qemu-aarch64 >/dev/null; then + aarch64-linux-gnu-gcc -O2 -w -DJTYPE=int -static core.c io_linux.c -o $T/zyj && { vms+=("qemu-aarch64 $T/zyj"); names+=("arm64 (qemu)"); } +fi +# seconds per run of kernel $2 on vm $1: the slope between $3 runs and 1 run, +# best of three each, so starting up and compiling cancel out +t(){ local s e; s=$(date +%s.%N); (cat tests/bench.zc; printf '`'$2$3) | $1 >/dev/null; e=$(date +%s.%N); echo "$e - $s" | bc; } +best(){ local b=999 x i; for i in 1 2 3; do x=$(t "$1" $2 $3); b=$(echo "if ($x < $b) $x else $b" | bc); done; echo $b; } +per(){ echo "scale=5; ($(best "$1" $2 $3) - $(best "$1" $2 1)) / ($3 - 1)" | bc; } +printf "%-14s %-8s %10s %10s %8s\n" backend kernel "plain ms" "o3 ms" speedup +for i in "${!vms[@]}"; do + n=41; [ "${names[$i]}" = interp ] && n=5 + for k in "collatz a b" "sieve c d" "digits e f" "fib g h"; do + set -- $k + p=$(per "${vms[$i]}" $2 5); o=$(per "${vms[$i]}" $3 $n) + printf "%-14s %-8s %10.2f %10.2f %7.1fx\n" "${names[$i]}" $1 $(echo "$p * 1000" | bc) $(echo "$o * 1000" | bc) $(echo "scale=2; $p / $o" | bc) + done +done diff --git a/docs/history/15-o3/code/core.c b/docs/history/15-o3/code/core.c new file mode 100644 index 0000000..4e74c93 --- /dev/null +++ b/docs/history/15-o3/code/core.c @@ -0,0 +1,2019 @@ +/* zygote core: puts the genome (the zc compiler and its runtime, both vm code) + into memory and runs it. on arm64, every block of vm code is translated to + arm64 the first time it is reached, straight from live memory. stores that + land on translated words throw those translations away, so code that + rewrites itself, including the resident compiler, just gets retranslated + on the next visit. on other hosts (or with -DZYINTERP) a plain interpreter + with the same semantics runs instead. ZYTRACE=1 logs translations on stderr. */ +#include +#include +#include +#if defined(__aarch64__) && !defined(ZYINTERP) +#define ZYJIT 1 +#include +#ifdef __APPLE__ +#include +#include +#endif +#endif +#include "genome.h" + +#ifndef JTYPE +#define JTYPE char +#endif +char Q(void); +void G(char); +extern JTYPE J; + +#define MW (1 << 20) /* memory, in words */ +#define DS0 (MW - 16) /* data stack grows down from here */ +#define RS0 0xF0000 /* return stack grows up from here */ +#define DSLIM (0xFC000 + 1100) /* data stack overflows below this (a block pushes at most ~1024) */ +#define RSLIM (0xF8000 - 8) /* return stack overflows above this */ +/* why the cpu stopped, when it wasn't a halt */ +static int stopped(int p) +{ + if (p == MW + 1) { fprintf(stderr, "zy: data stack overflow\n"); return 2; } + if (p == MW + 2) { fprintf(stderr, "zy: return stack overflow\n"); return 2; } + return 0; +} +#define CODEW (4 << 20) /* 16 MB of code, in instruction words */ +#define MAXB 65536 +#define BLKW 1024 /* longest block, in vm words */ + +static int m[MW]; +static int trace; + +/* ---- virtual enhancements ---- + up to 512 custom opcodes (22 .. 533), wired in at runtime by the install + opcode (21). an enhancement is a short program over native micro-ops, + written either as a recipe (text) or as straight-line vm code that gets + lifted. both become ssa, go through the optimizer below, and then run in + the interpreter or get inlined by the jit as native code. */ +#define NENH 512 +#define XOP0 22 +enum { U_LI, U_MOV, U_ADD, U_SUB, U_MUL, U_SDIV, U_UDIV, U_SREM, U_UREM, U_AND, U_OR, U_XOR, + U_SHL, U_SHR, U_SAR, U_NEG, U_NOT, U_LT, U_LTU, U_EQ, U_NE, U_MIN, U_MAX, U_MINU, U_MAXU, + U_MULHS, U_MULHU, U_CLZ, U_ABS, U_SEL, U_LD, U_ST, U_N }; +static const char *unames[U_N] = { "li", "mov", "add", "sub", "mul", "sdiv", "udiv", "srem", "urem", + "and", "or", "xor", "shl", "shr", "sar", "neg", "not", "lt", "ltu", "eq", "ne", "min", "max", + "minu", "maxu", "mulhs", "mulhu", "clz", "abs", "sel", "ld", "st" }; +static const signed char uarity[U_N] = { 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 1, 3, 1, 2 }; +typedef struct { int op, d, a, b, c, imm; } Uop; /* ssa: d is a fresh value, a b c are values */ +typedef struct { + int live, kind, nin, nout, n, nv, out[8]; + Uop *u; + signed char *reg; /* physical register per value, for the jit; 0 if it didn't fit */ +} Enh; +static Enh enh[NENH]; +static int need_flush; + +#define MAXUOP 4096 +static Uop ub[MAXUOP]; static int un, unv, uerr; /* the program being built */ +static int unew(int op, int a, int b, int c, int imm) +{ + Uop *u; + if (un >= MAXUOP) { uerr = -5; return 0; } + u = &ub[un++]; u->op = op; u->d = unv++; u->a = a; u->b = b; u->c = c; u->imm = imm; + return u->d; +} +static int mread(int a) { return (unsigned)a < MW ? m[a] : 0; } + +/* recipe text: one statement per line or per ';' + r0 = sdiv r0 r1 dst = op operands, operands are rN, parameter names or numbers + r2 = r0 a copy, or a number + st r1 r0 memory[r1] = r0 + inputs arrive in r0.. (and under the parameter names), outputs leave from r0.. */ +static int parse_recipe(int src, int names, int nin, int nout, int *out) +{ + int cur[16], i, p = src, n, k, op, args[3], na, dst, isst; + char tok[8][32]; int nt; + for (i = 0; i < 16; i++) cur[i] = i < nin ? i : -1; + for (;;) { + /* read one statement into tokens */ + nt = 0; + for (;;) { + int c = mread(p); + while (c == ' ' || c == '\t' || c == '\r') c = mread(++p); + if (!c || c == '\n' || c == ';') break; + if (c == '=') { if (nt < 8) strcpy(tok[nt++], "="); p++; continue; } + n = 0; + while (c && c != ' ' && c != '\t' && c != '\n' && c != ';' && c != '=' && c != '\r') { if (n < 31) tok[nt < 8 ? nt : 7][n++] = (char)c; c = mread(++p); } + if (nt < 8) tok[nt++][n] = 0; + } + if (nt) { + isst = !strcmp(tok[0], "st"); + if (!isst && (nt < 3 || strcmp(tok[1], "="))) return -2; + /* destination register */ + dst = -1; + if (!isst) { + if (tok[0][0] == 'r' && tok[0][1] >= '0' && tok[0][1] <= '9') dst = atoi(tok[0] + 1); + else for (i = 0; i < nin && names; i++) { + int s = mread(names + i), j = 0; + while (mread(s + j) && mread(s + j) == (unsigned char)tok[0][j]) j++; + if (!mread(s + j) && !tok[0][j]) dst = i; + } + if (dst < 0 || dst > 15) return -2; + } + k = isst ? 1 : 2; /* first token after "=" (or after st) */ + op = -1; + if (!isst && nt > 3) { for (i = 0; i < U_N; i++) if (!strcmp(tok[k], unames[i])) op = i; if (op < 0) return -2; k++; } + if (isst) op = U_ST; + na = 0; + for (; k < nt; k++) { + char *t = tok[k], *e; int v = -1; + if (na >= 3) return -2; + if (t[0] == 'r' && t[1] >= '0' && t[1] <= '9') { int r = atoi(t + 1); if (r > 15) return -2; if (cur[r] < 0) cur[r] = unew(U_LI, 0, 0, 0, 0); v = cur[r]; } + else if ((t[0] >= '0' && t[0] <= '9') || t[0] == '-') { long x = strtol(t, &e, 0); if (*e) return -2; v = unew(U_LI, 0, 0, 0, (int)x); } + else { + for (i = 0; i < nin && names; i++) { + int s = mread(names + i), j = 0; + while (mread(s + j) && mread(s + j) == (unsigned char)t[j]) j++; + if (!mread(s + j) && !t[j]) v = cur[i] < 0 ? (cur[i] = unew(U_LI, 0, 0, 0, 0)) : cur[i]; + } + if (v < 0) return -2; + } + args[na++] = v; + } + if (op < 0) { /* x = y */ + if (na != 1) return -2; + cur[dst] = args[0]; + } else { + if (na != (op == U_LI ? 0 : uarity[op])) { + if (!(op == U_LI && na == 1)) return -2; + } + if (op == U_LI) cur[dst] = args[0]; + else if (op == U_ST) unew(U_ST, args[0], args[1], 0, 0); + else cur[dst] = unew(op, args[0], na > 1 ? args[1] : 0, na > 2 ? args[2] : 0, 0); + } + if (uerr) return uerr; + } + if (!mread(p)) break; + p++; + } + for (i = 0; i < nout; i++) { if (cur[i] < 0) cur[i] = unew(U_LI, 0, 0, 0, 0); out[i] = cur[i]; } + return uerr; +} + +/* straight-line vm code from addr up to its ret, run symbolically */ +static int lift_vm(int a, int len, int nin, int nout, int *out) +{ + int st[256], sp = 0, i, w, k, x, y; + for (i = 0; i < nin; i++) st[sp++] = i; + for (i = 0; i < len; i++) { + w = mread(a + i); + if (w & 1) { if (sp >= 256) return -4; st[sp++] = unew(U_LI, 0, 0, 0, w >> 1); continue; } + k = w / 2; + if (k == 9) break; + switch (k) { + case 1: if (sp < 1) return -4; st[sp - 1] = unew(U_LD, st[sp - 1], 0, 0, 0); break; + case 2: if (sp < 2) return -4; unew(U_ST, st[sp - 1], st[sp - 2], 0, 0); sp -= 2; break; + case 3: /* pick needs a constant index */ + if (sp < 1) return -4; + x = st[sp - 1]; + if (x < nin) return -4; + { int j; for (j = 0; j < un && ub[j].d != x; j++); if (j == un || ub[j].op != U_LI) return -4; y = ub[j].imm; } + if (y < 0 || y >= sp - 1) return -4; + st[sp - 1] = st[sp - 2 - y]; + break; + case 4: if (sp < 1) return -4; sp--; break; + case 5: if (sp < 2) return -4; x = st[sp - 1]; st[sp - 1] = st[sp - 2]; st[sp - 2] = x; break; + case 13: case 14: case 15: case 16: case 17: case 18: case 19: case 20: { + static const int map[] = { U_ADD, U_SUB, U_MUL, U_AND, U_OR, U_XOR, U_SAR, U_LT }; + if (sp < 2) return -4; + x = st[sp - 1]; y = st[sp - 2]; sp -= 2; + st[sp++] = unew(map[k - 13], y, x, 0, 0); + break; } + default: return -4; /* control flow, i/o and other enhancements don't lift */ + } + if (uerr) return uerr; + } + if (sp != nout) return -4; + for (i = 0; i < nout; i++) out[i] = st[i]; + return uerr; +} + +/* ---- the optimizer ---- + one forward pass of copy propagation, constant folding, algebraic + simplification, division by constants (the multiply-high trick) and + common subexpression elimination, then dead code removal. */ +static int ev(int op, int a, int b, int c) +{ + unsigned ua = (unsigned)a, ub_ = (unsigned)b; + switch (op) { + case U_ADD: return (int)(ua + ub_); + case U_SUB: return (int)(ua - ub_); + case U_MUL: return (int)(ua * ub_); + case U_SDIV: return b == 0 ? 0 : (a == (int)0x80000000 && b == -1) ? a : a / b; + case U_UDIV: return b == 0 ? 0 : (int)(ua / ub_); + case U_SREM: return b == 0 ? a : (a == (int)0x80000000 && b == -1) ? 0 : a % b; + case U_UREM: return b == 0 ? a : (int)(ua % ub_); + case U_AND: return a & b; + case U_OR: return a | b; + case U_XOR: return a ^ b; + case U_SHL: return (int)(ua << (b & 31)); + case U_SHR: return (int)(ua >> (b & 31)); + case U_SAR: return a >> (b & 31); + case U_NEG: return (int)(0u - ua); + case U_NOT: return ~a; + case U_LT: return a < b; + case U_LTU: return ua < ub_; + case U_EQ: return a == b; + case U_NE: return a != b; + case U_MIN: return a < b ? a : b; + case U_MAX: return a > b ? a : b; + case U_MINU: return ua < ub_ ? a : b; + case U_MAXU: return ua > ub_ ? a : b; + case U_MULHS: return (int)(((long long)a * (long long)b) >> 32); + case U_MULHU: return (int)(((unsigned long long)ua * ub_) >> 32); + case U_CLZ: { int n = 0; if (!ua) return 32; while (!(ua & 0x80000000u)) { ua <<= 1; n++; } return n; } + case U_ABS: return a < 0 ? (int)(0u - ua) : a; + case U_SEL: return a ? b : c; + } + return 0; +} +static void smagic(int d, int *mul, int *sh) /* hacker's delight, signed */ +{ + const unsigned two31 = 0x80000000u; + unsigned ad = d < 0 ? 0u - (unsigned)d : (unsigned)d, t = two31 + ((unsigned)d >> 31); + unsigned anc = t - 1 - t % ad, q1 = two31 / anc, r1 = two31 - q1 * anc, q2 = two31 / ad, r2 = two31 - q2 * ad, delta; + int p = 31; + do { + p++; + q1 *= 2; r1 *= 2; if (r1 >= anc) { q1++; r1 -= anc; } + q2 *= 2; r2 *= 2; if (r2 >= ad) { q2++; r2 -= ad; } + delta = ad - r2; + } while (q1 < delta || (q1 == delta && r1 == 0)); + *mul = (int)(q2 + 1); if (d < 0) *mul = -*mul; + *sh = p - 32; +} +static void umagic(unsigned d, unsigned *mul, int *add, int *sh) /* hacker's delight, unsigned */ +{ + unsigned nc = 0xFFFFFFFFu - (0u - d) % d, q1, r1, q2, r2, delta; + int p = 31; + *add = 0; + q1 = 0x80000000u / nc; r1 = 0x80000000u - q1 * nc; + q2 = 0x7FFFFFFFu / d; r2 = 0x7FFFFFFFu - q2 * d; + do { + p++; + if (r1 >= nc - r1) { q1 = 2 * q1 + 1; r1 = 2 * r1 - nc; } else { q1 = 2 * q1; r1 = 2 * r1; } + if (r2 + 1 >= d - r2) { if (q2 >= 0x7FFFFFFFu) *add = 1; q2 = 2 * q2 + 1; r2 = 2 * r2 + 1 - d; } + else { if (q2 >= 0x80000000u) *add = 1; q2 = 2 * q2; r2 = 2 * r2 + 1; } + delta = d - 1 - r2; + } while (p < 64 && (q1 < delta || (q1 == delta && r1 == 0))); + *mul = q2 + 1; *sh = p - 32; +} + +static Uop ob[MAXUOP]; static int on, onv; +static int *omap, *isk, *kv; /* old value -> new value; constness of new values */ +static int pure(int op) { return op != U_LD && op != U_ST; } +static int ispow2(unsigned x) { return x && !(x & (x - 1)); } +static int lg2(unsigned x) { int n = 0; while (x > 1) { x >>= 1; n++; } return n; } +static int oe(int op, int a, int b, int c, int imm); +static int K(int v) { return oe(U_LI, 0, 0, 0, v); } +static int raw(int op, int a, int b, int c, int imm) +{ + Uop *u; int i; + if (pure(op)) /* cse */ + for (i = 0; i < on; i++) { + u = &ob[i]; + if (u->op == op && u->a == a && u->b == b && u->c == c && u->imm == imm) return u->d; + } + if (on >= MAXUOP) { uerr = -5; return 0; } + u = &ob[on++]; u->op = op; u->d = onv++; u->a = a; u->b = b; u->c = c; u->imm = imm; + isk[u->d] = op == U_LI; kv[u->d] = imm; + return u->d; +} +static int sdivk(int a, int d) /* a / d for a constant d, no divide instruction */ +{ + int mul, sh, q, t; + if (d == 1) return a; + if (d == -1) return oe(U_NEG, a, 0, 0, 0); + if (d == (int)0x80000000) return oe(U_EQ, a, K(d), 0, 0); + if (ispow2((unsigned)(d < 0 ? -d : d))) { + int k = lg2((unsigned)(d < 0 ? -d : d)); + t = oe(U_SAR, a, K(31), 0, 0); + t = oe(U_SHR, t, K(32 - k), 0, 0); + q = oe(U_SAR, oe(U_ADD, a, t, 0, 0), K(k), 0, 0); + return d < 0 ? oe(U_NEG, q, 0, 0, 0) : q; + } + smagic(d, &mul, &sh); + q = oe(U_MULHS, a, K(mul), 0, 0); + if (d > 0 && mul < 0) q = oe(U_ADD, q, a, 0, 0); + if (d < 0 && mul > 0) q = oe(U_SUB, q, a, 0, 0); + if (sh) q = oe(U_SAR, q, K(sh), 0, 0); + return oe(U_ADD, q, oe(U_SHR, q, K(31), 0, 0), 0, 0); +} +static int udivk(int a, unsigned d) +{ + unsigned mul; int add, sh, t; + if (d == 1) return a; + if (ispow2(d)) return oe(U_SHR, a, K(lg2(d)), 0, 0); + if (d > 0x80000000u) return oe(U_XOR, oe(U_LTU, a, K((int)d), 0, 0), K(1), 0, 0); /* a >= d */ + umagic(d, &mul, &add, &sh); + t = oe(U_MULHU, a, K((int)mul), 0, 0); + if (!add) return sh ? oe(U_SHR, t, K(sh), 0, 0) : t; + t = oe(U_ADD, oe(U_SHR, oe(U_SUB, a, t, 0, 0), K(1), 0, 0), t, 0, 0); + return sh > 1 ? oe(U_SHR, t, K(sh - 1), 0, 0) : t; +} +static int oe(int op, int a, int b, int c, int imm) +{ + int ka, kb, A, B; + if (op == U_LI) return raw(U_LI, 0, 0, 0, imm); + if (op == U_MOV) return a; + if (op == U_LD || op == U_ST) return raw(op, a, b, 0, 0); + ka = isk[a]; kb = uarity[op] > 1 && isk[b]; + A = kv[a]; B = kv[b]; + /* everything constant: fold */ + if (ka && (uarity[op] < 2 || kb) && (uarity[op] < 3 || isk[c])) return K(ev(op, A, B, kv[c])); + /* constants go right for commutative ops */ + if (ka && !kb && (op == U_ADD || op == U_MUL || op == U_AND || op == U_OR || op == U_XOR || op == U_EQ || op == U_NE || + op == U_MIN || op == U_MAX || op == U_MINU || op == U_MAXU || op == U_MULHS || op == U_MULHU)) { + int t = a; a = b; b = t; kb = 1; B = A; ka = 0; + } + if (op == U_SEL && ka) return A ? b : c; + if (op == U_SEL && b == c) return b; + if (kb) switch (op) { + case U_ADD: case U_SUB: case U_OR: case U_XOR: case U_SHL: case U_SHR: case U_SAR: + if (!B || (op >= U_SHL && op <= U_SAR && !(B & 31))) return a; + break; + case U_MUL: + if (B == 0) return K(0); + if (B == 1) return a; + if (B == -1) return oe(U_NEG, a, 0, 0, 0); + if (ispow2((unsigned)B)) return oe(U_SHL, a, K(lg2((unsigned)B)), 0, 0); + break; + case U_AND: if (B == 0) return K(0); if (B == -1) return a; break; + case U_SDIV: if (B == 0) return K(0); return sdivk(a, B); + case U_SREM: if (B == 0) return a; if (B == 1 || B == -1) return K(0); + return oe(U_SUB, a, oe(U_MUL, sdivk(a, B), b, 0, 0), 0, 0); + case U_UDIV: if (B == 0) return K(0); return udivk(a, (unsigned)B); + case U_UREM: if (B == 0) return a; + if (ispow2((unsigned)B)) return oe(U_AND, a, K(B - 1), 0, 0); + return oe(U_SUB, a, oe(U_MUL, udivk(a, (unsigned)B), b, 0, 0), 0, 0); + } + if (a == b && uarity[op] == 2) { + if (op == U_SUB || op == U_XOR || op == U_LT || op == U_LTU || op == U_NE) return K(0); + if (op == U_AND || op == U_OR || op == U_MIN || op == U_MAX || op == U_MINU || op == U_MAXU) return a; + if (op == U_EQ) return K(1); + } + return raw(op, a, b, c, 0); +} +/* runs the optimizer over ub[0..un) with nin inputs; leaves the result in e */ +static int optimize(Enh *e, int nin, int *out, int nout) +{ + int i, *live, n = 0; + static int omap_[MAXUOP + 16], isk_[MAXUOP * 4], kv_[MAXUOP * 4]; + omap = omap_; isk = isk_; kv = kv_; + on = 0; onv = nin; + for (i = 0; i < nin; i++) { omap[i] = i; isk[i] = 0; } + for (i = 0; i < un && !uerr; i++) { + Uop *u = &ub[i]; + int a = uarity[u->op] > 0 || u->op == U_ST || u->op == U_LD ? omap[u->a] : 0; + int b = uarity[u->op] > 1 || u->op == U_ST ? omap[u->b] : 0; + int c = uarity[u->op] > 2 ? omap[u->c] : 0; + if (u->op == U_ST) oe(U_ST, a, b, 0, 0); + else omap[u->d] = oe(u->op, a, b, c, u->imm); + } + if (uerr) return uerr; + /* dead code: outputs and stores are live */ + live = calloc(onv + 1, sizeof *live); + for (i = 0; i < nout; i++) { e->out[i] = omap[out[i]]; live[e->out[i]] = 1; } + for (i = on; i--;) { + Uop *u = &ob[i]; + if (u->op != U_ST && !live[u->d]) { u->op = -1; continue; } + if (uarity[u->op] > 0 || u->op == U_ST || u->op == U_LD) live[u->a] = 1; + if (uarity[u->op] > 1 || u->op == U_ST) live[u->b] = 1; + if (uarity[u->op] > 2) live[u->c] = 1; + } + free(live); + e->u = malloc((on + 1) * sizeof(Uop)); + for (i = 0; i < on; i++) if (ob[i].op >= 0) e->u[n++] = ob[i]; + e->n = n; e->nv = onv; e->nin = nin; e->nout = nout; + return 0; +} +/* run an installed enhancement on a downward-growing stack; returns the new top */ +static int *xexec(Enh *e, int *sp) +{ + int vb[256], *v = e->nv <= 256 ? vb : malloc(e->nv * sizeof(int)), i, j; + for (j = 0; j < e->nin; j++) v[j] = sp[e->nin - 1 - j]; + for (i = 0; i < e->n; i++) { + Uop *u = &e->u[i]; + switch (u->op) { + case U_LI: v[u->d] = u->imm; break; + case U_LD: v[u->d] = mread(v[u->a]); break; + case U_ST: if ((unsigned)v[u->a] < MW) m[v[u->a]] = v[u->b]; break; + default: v[u->d] = ev(u->op, v[u->a], v[u->b], v[u->c]); + } + } + sp += e->nin - e->nout; + for (j = 0; j < e->nout; j++) sp[e->nout - 1 - j] = v[e->out[j]]; + if (v != vb) free(v); + return sp; +} +static void xdump(int slot, Enh *e) +{ + int i; + fprintf(stderr, "enh %d: %d in, %d out, %d ops\n", slot, e->nin, e->nout, e->n); + for (i = 0; i < e->n; i++) { + Uop *u = &e->u[i]; + if (u->op == U_ST) fprintf(stderr, " st v%d v%d\n", u->a, u->b); + else if (u->op == U_LI) fprintf(stderr, " v%d = %d\n", u->d, u->imm); + else { fprintf(stderr, " v%d = %s v%d", u->d, unames[u->op], u->a); if (uarity[u->op] > 1) fprintf(stderr, " v%d", u->b); if (uarity[u->op] > 2) fprintf(stderr, " v%d", u->c); fprintf(stderr, "\n"); } + } + for (i = 0; i < e->nout; i++) fprintf(stderr, " out%d = v%d\n", i, e->out[i]); +} +/* registers for the jit: values live in w0..w15, straight-line linear scan. + if they don't fit, reg stays 0 and the jit calls the interpreter instead. */ +static void regalloc(Enh *e) +{ + int *last = malloc(e->nv * sizeof(int)), i, j, k, fr = 0xffff; + signed char *r = malloc(e->nv); + for (i = 0; i < e->nv; i++) { last[i] = -2; r[i] = -1; } + for (i = 0; i < e->n; i++) { + Uop *u = &e->u[i]; + if (uarity[u->op] > 0 || u->op == U_ST || u->op == U_LD) last[u->a] = i; + if (uarity[u->op] > 1 || u->op == U_ST) last[u->b] = i; + if (uarity[u->op] > 2) last[u->c] = i; + } + for (i = 0; i < e->nout; i++) last[e->out[i]] = e->n; +#define TAKE(v) do { for (k = 0; k < 16 && !(fr >> k & 1); k++); if (k == 16) goto fail; fr &= ~(1 << k); r[v] = (signed char)k; } while (0) + for (j = 0; j < e->nin; j++) if (last[j] >= -1 && last[j] != -2) TAKE(j); + for (i = 0; i < e->n; i++) { + Uop *u = &e->u[i]; int ops[3], no = 0; + if (uarity[u->op] > 0 || u->op == U_ST || u->op == U_LD) ops[no++] = u->a; + if (uarity[u->op] > 1 || u->op == U_ST) ops[no++] = u->b; + if (uarity[u->op] > 2) ops[no++] = u->c; + for (j = 0; j < no; j++) if (last[ops[j]] == i && r[ops[j]] >= 0 && (fr >> r[ops[j]] & 1) == 0) fr |= 1 << r[ops[j]]; + if (u->op != U_ST) TAKE(u->d); + if (u->op != U_ST && last[u->d] == -2) fr |= 1 << r[u->d]; /* never used */ + } +#undef TAKE + free(last); e->reg = r; return; +fail: + free(last); free(r); e->reg = 0; +} +/* the install opcode. descriptor: slot, kind (0 recipe, 1 vm code), inputs, + outputs, payload address, payload length, parameter names (or 0). kind 2 + is an o3 region (below). + returns 0, or -1 bad descriptor, -2 recipe syntax, -4 code doesn't lift, + -5 too big. */ +static int o3install(int d); +static int install(int d) +{ + int slot = mread(d), kind = mread(d + 1), nin = mread(d + 2), nout = mread(d + 3); + int pay = mread(d + 4), len = mread(d + 5), names = mread(d + 6), out[8], r; + Enh ne; + if (kind == 2) return o3install(d); + if ((unsigned)slot >= NENH || (unsigned)nin > 8 || (unsigned)nout > 8 || (kind != 0 && kind != 1)) return -1; + un = 0; unv = nin; uerr = 0; + r = kind == 0 ? parse_recipe(pay, names, nin, nout, out) : lift_vm(pay, len, nin, nout, out); + if (r) { if (trace) fprintf(stderr, "enh %d: install failed (%d)\n", slot, r); return r; } + memset(&ne, 0, sizeof ne); + if ((r = optimize(&ne, nin, out, nout))) return r; + ne.kind = kind; ne.live = 1; + regalloc(&ne); + if (enh[slot].live) { free(enh[slot].u); free(enh[slot].reg); } + enh[slot] = ne; + need_flush = 1; + if (trace) xdump(slot, &enh[slot]); + return 0; +} + +/* ---- o3: optimized regions ---- + o3 code comes from the compiler as a region: a range [lo, hi) of vm words + plus up to 7 frame slots that nothing takes the address of (install kind 2: + [-, 2, lo, hi, n, slots...]). the region is cut into basic blocks. each + block is lifted to ssa: the vm stack is tracked symbolically, so only what + is left on it at the end of the block touches memory, the slots live in + registers across blocks (VGET reads one at block entry, the block's end + says what each one holds now), the frame pointer is var 0, and enhancements + are spliced in. every block then goes through the optimizer above. a + backend compiles the whole region at once: arm64, llvm at O3, or the + region interpreter below. the region is entered at lo and at the return + points of its calls. calls, returns, jumps out, and anything a block can't + lift leave it: the slots go back to the frame first, so the code outside + sees the usual memory. stores to its own code drop it; it lifts again on + its next entry. */ +#define ZFP 0xA0000 /* the frame pointer word */ +#define U_VGET U_N /* a var at block entry; imm = var */ +#define U_SLD (U_N + 1) /* the stack word at offset imm from the top at block entry */ +#define RMAXV 8 +#define RMAXE 256 +enum { T_BR, T_CBR, T_JMP, T_CALL, T_RET }; +typedef struct { + int a, n, nv, delta, nso, term, cv, s1, s2, x1, x2; + int vs[RMAXV]; /* each var's value at the end, -1 if unchanged */ + int *so; /* stack words to write at the end: offset, value */ + Uop *u; +} RB; +typedef struct { + int idx, lo, hi, nv, slot[RMAXV], lifted, dead, nonat, nb, ne, maxnv; + int ent[RMAXE], entb[RMAXE]; + RB *b; + void *nat, *stub[RMAXE]; /* the backend's code */ +} Rg; +#define MAXRG 4096 +static Rg *rgs[MAXRG]; +static int nrg; +static unsigned short o3ent[MW]; /* region + 1 at an entry point */ +static unsigned char o3ei[MW]; /* which of its entries */ +static void rforget(Rg *R); /* the backend drops its code */ +static void rmark(Rg *R); /* the backend watches the region's words */ +static void rcompile(Rg *R, int eager); + +static int nargs(int op) { return op >= U_N ? 0 : uarity[op]; } +static void rclear(Rg *R) +{ + int b; + rforget(R); + for (b = 0; b < R->nb; b++) { free(R->b[b].u); free(R->b[b].so); } + free(R->b); R->b = 0; R->nb = 0; R->lifted = 0; +} +static void rdrop_all(void) { int j; for (j = 0; j < nrg; j++) if (rgs[j]->lifted) rclear(rgs[j]); } + +/* lifting one block */ +static Rg *R_; +static unsigned char *lead_; +static int *bidx_, *wl_, nw_; +static int lv_[RMAXV], lw_[RMAXV]; +#define SB 1100 +static int skv[2 * SB], stp, slo, shi; +static unsigned char skk[2 * SB]; +static int afp[MAXUOP * 4 + 16]; /* value -> 1 + its offset from fp, if it is fp + k */ +static int emap[MAXUOP * 4 + 16]; + +static int rvar(int v) +{ + if (lv_[v] < 0) { lv_[v] = raw(U_VGET, 0, 0, 0, v); if (!v) afp[lv_[v]] = 1; } + return lv_[v]; +} +static int slotvar(int off) { int v; for (v = 1; v < R_->nv; v++) if (R_->slot[v] == off) return v; return 0; } +static int rbin(int op, int x, int y, int z) +{ + int r = oe(op, x, y, z, 0); + if (op == U_ADD && !afp[r]) { + if (afp[x] && isk[y]) afp[r] = afp[x] + kv[y]; + else if (afp[y] && isk[x]) afp[r] = afp[y] + kv[x]; + } + return r; +} +static int rld(int a) +{ + int v; + if (isk[a] && kv[a] == ZFP) return rvar(0); + if (afp[a] && (v = slotvar(afp[a] - 1))) return rvar(v); + return oe(U_LD, a, 0, 0, 0); +} +static int rst(int a, int b) +{ + int v; + if (isk[a] && kv[a] == ZFP) return 0; /* moving the frame: not in here */ + if (afp[a] && (v = slotvar(afp[a] - 1))) { lv_[v] = b; lw_[v] = 1; return 1; } + oe(U_ST, a, b, 0, 0); + return 1; +} +static int sget(int o) +{ + if (o < -SB || o >= SB) { uerr = -6; return 0; } + if (!skk[o + SB]) { + if (o < 0) { uerr = -6; return 0; } + skv[o + SB] = raw(U_SLD, 0, 0, 0, o); skk[o + SB] = 1; + } + if (o < slo) slo = o; + if (o >= shi) shi = o + 1; + return skv[o + SB]; +} +static void sset(int o, int v) +{ + if (o < -SB || o >= SB) { uerr = -6; return; } + skv[o + SB] = v; skk[o + SB] = 1; + if (o < slo) slo = o; + if (o >= shi) shi = o + 1; +} +static int spop(void) { return sget(stp++); } +static void spush(int v) { sset(--stp, v); } +static int rblk(int t) { return t >= R_->lo && t < R_->hi && lead_[t - R_->lo] ? bidx_[t - R_->lo] : -1; } +/* an enhancement, spliced into the block */ +static int rsplice(Enh *e) +{ + int i, j; + for (j = e->nin; j--;) emap[j] = spop(); + for (i = 0; i < e->n && !uerr; i++) { + Uop *u = &e->u[i]; + int na = nargs(u->op), a = na > 0 ? emap[u->a] : 0, b = na > 1 ? emap[u->b] : 0, c = na > 2 ? emap[u->c] : 0; + if (u->op == U_LI) emap[u->d] = K(u->imm); + else if (u->op == U_LD) emap[u->d] = rld(a); + else if (u->op == U_ST) { if (!rst(a, b)) uerr = -4; } + else emap[u->d] = rbin(u->op, a, b, c); + } + for (j = 0; j < e->nout; j++) spush(emap[e->out[j]]); + return uerr; +} +static int lift_block(int bi) +{ + static const int map[] = { U_ADD, U_SUB, U_MUL, U_AND, U_OR, U_XOR, U_SAR, U_LT }; + RB *B = &R_->b[bi]; + int i = B->a, w, k, x, y, t, j, o, n = 0, *live; + on = 0; onv = 0; uerr = 0; + memset(afp, 0, sizeof afp); memset(skk, 0, sizeof skk); + stp = slo = shi = 0; + for (j = 0; j < RMAXV; j++) { lv_[j] = -1; lw_[j] = 0; } + B->term = T_JMP; B->cv = -1; B->s1 = B->s2 = -1; B->x1 = B->x2 = 0; + for (;;) { + if (uerr) return uerr; + if (i != B->a && (t = rblk(i)) >= 0) { B->term = T_BR; B->s1 = t; goto done; } + if (i < R_->lo || i >= R_->hi) goto leave_at; + w = m[i]; + if (w & 1) { spush(K(w >> 1)); i++; continue; } + k = w / 2; + switch (k) { + case 0: B->cv = K(-1); goto done; /* halt */ + case 1: x = spop(); spush(rld(x)); break; + case 2: + x = sget(stp); + if (isk[x] && kv[x] == ZFP) goto leave_at; + x = spop(); y = spop(); rst(x, y); + break; + case 3: /* pick: a constant depth */ + x = sget(stp); + if (!isk[x] || kv[x] < 0 || kv[x] > 1024) goto leave_at; + stp++; spush(sget(stp + kv[x])); + break; + case 4: stp++; break; + case 5: x = sget(stp); y = sget(stp + 1); sset(stp, y); sset(stp + 1, x); break; + case 6: B->cv = spop(); B->term = T_CALL; B->x2 = i + 1; goto done; + case 7: + x = spop(); + if (isk[x] && (t = rblk(kv[x])) >= 0) { B->term = T_BR; B->s1 = t; } + else B->cv = x; + goto done; + case 8: + x = sget(stp); + if (!isk[x]) goto leave_at; + stp++; y = spop(); + if (isk[y]) { /* a known condition */ + if (kv[y]) { i++; continue; } + if ((t = rblk(kv[x])) >= 0) { B->term = T_BR; B->s1 = t; } else B->cv = K(kv[x]); + goto done; + } + B->term = T_CBR; B->cv = y; + B->x1 = kv[x]; B->s1 = rblk(kv[x]); + B->x2 = i + 1; B->s2 = rblk(i + 1); + goto done; + case 9: B->term = T_RET; goto done; + case 13: case 14: case 15: case 16: case 17: case 18: case 19: case 20: + x = spop(); y = spop(); spush(rbin(map[k - 13], y, x, 0)); + break; + default: + if (k >= XOP0 && k < XOP0 + NENH && enh[k - XOP0].live) { if (rsplice(&enh[k - XOP0])) return uerr; break; } + goto leave_at; /* i/o, install, unwired: done the usual way */ + } + i++; + } +leave_at: + B->term = T_JMP; B->cv = K(i); +done: + if (uerr) return uerr; + B->delta = stp; + B->so = malloc((shi - slo + 1) * 2 * sizeof(int)); + B->nso = 0; + for (o = stp > slo ? stp : slo; o < shi; o++) + if (skk[o + SB]) { + x = skv[o + SB]; + if (ob[x].op == U_SLD && ob[x].imm == o) continue; /* still the word that was there */ + if (afp[x] && slotvar(afp[x] - 1)) return -7; /* a register var's address can't wait in memory */ + B->so[2 * B->nso] = o; B->so[2 * B->nso + 1] = x; B->nso++; + } + for (j = 0; j < RMAXV; j++) B->vs[j] = -1; + for (j = 1; j < R_->nv; j++) + if (lw_[j] && !(ob[lv_[j]].op == U_VGET && ob[lv_[j]].imm == j)) B->vs[j] = lv_[j]; + /* dead code: what the block leaves behind and its stores are live */ + live = calloc(onv + 1, sizeof *live); + for (j = 0; j < B->nso; j++) live[B->so[2 * j + 1]] = 1; + for (j = 1; j < R_->nv; j++) if (B->vs[j] >= 0) live[B->vs[j]] = 1; + if (B->term == T_CBR || B->term == T_JMP || B->term == T_CALL) live[B->cv] = 1; + for (i = on; i--;) { + Uop *u = &ob[i]; + if (u->op != U_ST && !live[u->d]) { u->op = -1; continue; } + k = nargs(u->op); + if (k > 0) live[u->a] = 1; + if (k > 1) live[u->b] = 1; + if (k > 2) live[u->c] = 1; + } + free(live); + B->u = malloc((on + 1) * sizeof(Uop)); + for (i = 0; i < on; i++) if (ob[i].op >= 0) B->u[n++] = ob[i]; + B->n = n; B->nv = onv; + if (onv > R_->maxnv) R_->maxnv = onv; + return 0; +} +static void addl(int x, int en) +{ + int e; + if (x < R_->lo || x >= R_->hi) return; + if (!lead_[x - R_->lo]) { lead_[x - R_->lo] = 1; wl_[nw_++] = x; } + if (en) { for (e = 0; e < R_->ne && R_->ent[e] != x; e++); if (e == R_->ne && e < RMAXE) R_->ent[R_->ne++] = x; } +} +/* blocks, then each one lifted and optimized */ +static int rlift(Rg *R) +{ + int n = R->hi - R->lo, L, i, w, k, t, b, e, r = 0, cnt; + if (R->lifted) rclear(R); + R_ = R; + lead_ = calloc(n, 1); bidx_ = malloc(n * sizeof(int)); wl_ = malloc(n * sizeof(int)); nw_ = 0; + R->ne = 0; + addl(R->lo, 1); + while (nw_) { + L = wl_[--nw_]; + for (i = L, cnt = 0;;) { + if (i >= R->hi || (i != L && lead_[i - R->lo])) break; + if (cnt++ >= 1000) { addl(i, 0); break; } + w = m[i]; + if (w & 1) { + if (i + 1 < R->hi && !(m[i + 1] & 1) && (k = m[i + 1] / 2) >= 6 && k <= 8) { + t = w >> 1; + if (k == 6) addl(i + 2, 1); /* a call comes back here */ + else if (k == 7) addl(t, 0); + else { addl(t, 0); addl(i + 2, 0); } + break; + } + i++; + continue; + } + k = w / 2; + if (k == 6) { addl(i + 1, 1); break; } + if (k == 8) { addl(i + 1, 0); break; } + if (k == 0 || k == 7 || k == 9) break; + i++; + } + } + for (i = 0, b = 0; i < n; i++) if (lead_[i]) bidx_[i] = b++; + R->nb = b; R->b = calloc(b, sizeof(RB)); R->maxnv = 0; + for (i = 0; i < n; i++) if (lead_[i]) R->b[bidx_[i]].a = R->lo + i; + for (b = 0; b < R->nb && !r; b++) r = lift_block(b); + for (e = 0; e < R->ne; e++) R->entb[e] = bidx_[R->ent[e] - R->lo]; + free(lead_); free(bidx_); free(wl_); + if (r) { + if (trace) fprintf(stderr, "o3: %d..%d doesn't lift (%d)\n", R->lo, R->hi - 1, r); + R->lifted = 1; rclear(R); return r; + } + for (e = 0; e < R->ne; e++) { o3ent[R->ent[e]] = R->idx + 1; o3ei[R->ent[e]] = e; } + R->lifted = 1; + rmark(R); + if (trace) { + int ops = 0; + for (b = 0; b < R->nb; b++) ops += R->b[b].n; + fprintf(stderr, "o3: %d..%d: %d blocks, %d entries, %d vars, %d micro-ops\n", R->lo, R->hi - 1, R->nb, R->ne, R->nv - 1, ops); + if (getenv("ZYO3DUMP")) + for (b = 0; b < R->nb; b++) { + RB *B = &R->b[b]; + fprintf(stderr, " b%d @%d: %d ops, sp %+d, term %d cv v%d s %d/%d x %d/%d\n", b, B->a, B->n, B->delta, B->term, B->cv, B->s1, B->s2, B->x1, B->x2); + for (i = 0; i < B->n; i++) { + Uop *u = &B->u[i]; + if (u->op == U_ST) fprintf(stderr, " st v%d v%d\n", u->a, u->b); + else if (u->op == U_LI) fprintf(stderr, " v%d = %d\n", u->d, u->imm); + else if (u->op == U_VGET) fprintf(stderr, " v%d = var%d\n", u->d, u->imm); + else if (u->op == U_SLD) fprintf(stderr, " v%d = s[%d]\n", u->d, u->imm); + else { fprintf(stderr, " v%d = %s v%d", u->d, unames[u->op], u->a); if (nargs(u->op) > 1) fprintf(stderr, " v%d", u->b); if (nargs(u->op) > 2) fprintf(stderr, " v%d", u->c); fprintf(stderr, "\n"); } + } + for (i = 0; i < B->nso; i++) fprintf(stderr, " s[%d] = v%d\n", B->so[2 * i], B->so[2 * i + 1]); + for (i = 1; i < R->nv; i++) if (B->vs[i] >= 0) fprintf(stderr, " var%d = v%d\n", i, B->vs[i]); + } + } + return 0; +} +/* install kind 2 */ +static int o3install(int d) +{ + int lo = mread(d + 2), hi = mread(d + 3), n = mread(d + 4), i, r; + Rg *R; + if (lo <= 0 || hi <= lo || hi > MW || hi - lo > 1000000 || (unsigned)n > 7 || nrg >= MAXRG) return -1; + R = calloc(1, sizeof *R); + R->idx = nrg; rgs[nrg++] = R; + R->lo = lo; R->hi = hi; R->nv = n + 1; + for (i = 0; i < n; i++) { R->slot[i + 1] = mread(d + 5 + i); if ((unsigned)R->slot[i + 1] >= 4096) { R->dead = 1; return -1; } } + if ((r = rlift(R))) { R->dead = 1; return r; } + rcompile(R, 1); + return 0; +} +/* the region entered at p, lifted again if it has to be, or 0 */ +static Rg *rat(int p) +{ + Rg *R = rgs[o3ent[p] - 1]; + if (R->dead) { o3ent[p] = 0; return 0; } + if (!R->lifted && rlift(R)) { R->dead = 1; o3ent[p] = 0; return 0; } + if (o3ei[p] >= R->ne || R->ent[o3ei[p]] != p) { o3ent[p] = 0; return 0; } + return R; +} + +#ifdef ZYJIT +static unsigned char cov[MW]; /* 1 if a live translation covers the word */ +static void *tab[MW]; /* vm address -> translation */ +static struct { int a, b; } bl[MAXB]; /* live translations cover [a,b); a < 0 is dead */ +static int nbl; +static unsigned *code, *cp, *x_enter, *x_exit, *x_disp, *x_first; +static long S[13]; /* m, s, r, tab, cov, Q, G, &J, store hook, install, enhancement fallback, stack limits */ + +enum { W0 = 0, W1 = 1, X9 = 9, X10, X11, M = 19, SP = 20, RP = 21, TAB = 22, ST = 23 }; + +static void E(unsigned x) { *cp++ = x; } +static void li(int d, int v) +{ + unsigned u = (unsigned)v; + E(0x52800000 | (u & 0xffff) << 5 | d); + if (u >> 16) E(0x72a00000 | (u >> 16) << 5 | d); +} +static void ldr_post(int t, int n, int i) { E(0xb8400400 | (i & 0x1ff) << 12 | n << 5 | t); } +static void ldr_pre(int t, int n, int i) { E(0xb8400c00 | (i & 0x1ff) << 12 | n << 5 | t); } +static void str_pre(int t, int n, int i) { E(0xb8000c00 | (i & 0x1ff) << 12 | n << 5 | t); } +static void str_post(int t, int n, int i) { E(0xb8000400 | (i & 0x1ff) << 12 | n << 5 | t); } +static void ldr_u(int t, int n, int o) { E(0xb9400000 | (o / 4) << 10 | n << 5 | t); } +static void str_u(int t, int n, int o) { E(0xb9000000 | (o / 4) << 10 | n << 5 | t); } +static void ldrx_u(int t, int n, int o) { E(0xf9400000 | (o / 8) << 10 | n << 5 | t); } +static void ldr_rr(int t, int n, int r, int ext) { E(0xb8600800 | r << 16 | ext << 13 | 1 << 12 | n << 5 | t); } +static void str_rr(int t, int n, int r, int ext) { E(0xb8200800 | r << 16 | ext << 13 | 1 << 12 | n << 5 | t); } +static void ldp_post(int t, int u, int n, int i) { E(0x28c00000 | ((i / 4) & 0x7f) << 15 | u << 10 | n << 5 | t); } +static void op3(unsigned op, int d, int n, int r) { E(op | r << 16 | n << 5 | d); } +static void addx_ext(int d, int n, int r, int ext) { E(0x8b200000 | r << 16 | ext << 13 | 2 << 10 | n << 5 | d); } +static void movw(int d, int r) { E(0x2a0003e0 | r << 16 | d); } +static void blr(int n) { E(0xd63f0000 | n << 5); } +static void b_to(unsigned *t) { long o = t - cp; E(0x14000000 | (o & 0x3ffffff)); } +static void cb_fix(unsigned *at, unsigned *to) { *at |= ((to - at) & 0x7ffff) << 5; } +#define SXTW 6 +#define UXTW 2 + +static void build_stubs(void) +{ + cp = code; + x_enter = cp; /* int enter(long *S, void *code) */ + E(0xa9b97bfd); /* stp x29, x30, [sp, #-112]! */ + E(0x910003fd); /* mov x29, sp */ + E(0xa90153f3); /* stp x19, x20, [sp, #16] */ + E(0xa9025bf5); /* stp x21, x22, [sp, #32] */ + E(0xf9001bf7); /* str x23, [sp, #48] */ + E(0xa90467f8); /* stp x24, x25, [sp, #64] */ + E(0xa9056ffa); /* stp x26, x27, [sp, #80] */ + E(0xf90033fc); /* str x28, [sp, #96] */ + E(0xaa0003f7); /* mov x23, x0 */ + ldrx_u(M, ST, 0); + ldrx_u(24, ST, 88); ldrx_u(25, ST, 96); /* x24, x25: the stack limits, as addresses */ + ldr_u(X9, ST, 8); addx_ext(SP, M, X9, UXTW); + ldr_u(X9, ST, 16); addx_ext(RP, M, X9, UXTW); + ldrx_u(TAB, ST, 24); + E(0xd61f0020); /* br x1 */ + x_exit = cp; /* w0 = next vm address */ + op3(0xcb000000, X9, SP, M); E(0xd342fd29); str_u(X9, ST, 8); /* s = (x20 - x19) >> 2 */ + op3(0xcb000000, X9, RP, M); E(0xd342fd29); str_u(X9, ST, 16); + E(0xa94153f3); /* ldp x19, x20, [sp, #16] */ + E(0xa9425bf5); /* ldp x21, x22, [sp, #32] */ + E(0xf9401bf7); /* ldr x23, [sp, #48] */ + E(0xa94467f8); /* ldp x24, x25, [sp, #64] */ + E(0xa9456ffa); /* ldp x26, x27, [sp, #80] */ + E(0xf94033fc); /* ldr x28, [sp, #96] */ + E(0xa8c77bfd); /* ldp x29, x30, [sp], #112 */ + E(0xd65f03c0); /* ret */ + { + unsigned *ovd = cp, *ovr; + li(W0, MW + 1); b_to(x_exit); /* data stack overflow */ + ovr = cp; + li(W0, MW + 2); b_to(x_exit); /* return stack overflow */ + x_disp = cp; /* w0 = target: check the stacks, chain through tab[] or fall back to c */ + E(0xeb18029f); /* cmp x20, x24 */ + E(0x54000003 | ((ovd - cp) & 0x7ffff) << 5); /* b.lo data overflow */ + E(0xeb1902bf); /* cmp x21, x25 */ + E(0x54000002 | ((ovr - cp) & 0x7ffff) << 5); /* b.hs return overflow */ + } + E(0x7144001f); /* cmp w0, #MW */ + E(0x54000002 | ((x_exit - cp) & 0x7ffff) << 5); /* b.hs exit */ + E(0xf8605ac9); /* ldr x9, [x22, w0, uxtw #3] */ + E(0xb4000009 | ((x_exit - cp) & 0x7ffff) << 5); /* cbz x9, exit */ + E(0xd61f0120); /* br x9 */ + x_first = cp; +} + +static void flush(void) +{ + memset(tab, 0, sizeof tab); + memset(cov, 0, sizeof cov); + nbl = 0; + cp = x_first; + rdrop_all(); + if (trace) fprintf(stderr, "jit: flushed\n"); +} + +static void jitwrite(int on) +{ +#ifdef __APPLE__ + pthread_jit_write_protect_np(!on); +#else + (void)on; +#endif +} + +static void syncicache(unsigned *a, unsigned *b) +{ +#ifdef __APPLE__ + sys_icache_invalidate(a, (char *)b - (char *)a); +#else + __builtin___clear_cache((char *)a, (char *)b); +#endif +} + +/* store hook for words that are covered by a translation */ +static int hook(int a, int v) +{ + int j, x, hit = 0; + if (m[a] == v) return 0; + m[a] = v; + for (j = 0; j < nbl; j++) + if (bl[j].a >= 0 && bl[j].a <= a && a < bl[j].b) { + tab[bl[j].a] = 0; + for (x = bl[j].a; x < bl[j].b; x++) cov[x] = 0; + if (trace) fprintf(stderr, "jit: dropped %d..%d\n", bl[j].a, bl[j].b - 1); + bl[j].a = -1; + hit = 1; + } + for (j = 0; j < nrg; j++) + if (rgs[j]->lifted && rgs[j]->lo <= a && a < rgs[j]->hi) { + if (trace) fprintf(stderr, "o3: dropped %d..%d (a store to %d)\n", rgs[j]->lo, rgs[j]->hi - 1, a); + rclear(rgs[j]); + memset(cov, 0, sizeof cov); + hit = 1; + } + for (j = 0; j < nbl; j++) + if (bl[j].a >= 0) for (x = bl[j].a; x < bl[j].b; x++) cov[x] = 1; + for (j = 0; j < nrg; j++) if (rgs[j]->lifted) rmark(rgs[j]); + return hit; +} + +static void op(int i, int k) /* everything except control flow */ +{ + unsigned *a, *b, *c, *z; + switch (k) { + case 1: ldr_u(X9, SP, 0); ldr_rr(X9, M, X9, SXTW); str_u(X9, SP, 0); return; + case 2: /* store: inline unless the target word is translated */ + ldp_post(X9, X10, SP, 8); + E(0x7144013f); /* cmp w9, #MW */ + a = cp; E(0x54000002); /* b.hs done */ + ldrx_u(X11, ST, 32); + E(0x3869496b); /* ldrb w11, [x11, w9, uxtw] */ + b = cp; E(0x3500000b); /* cbnz w11, slow */ + str_rr(X10, M, X9, UXTW); + c = cp; E(0x14000000); /* b done */ + cb_fix(b, cp); /* slow: hook(a, v) */ + movw(W0, X9); movw(W1, X10); ldrx_u(X11, ST, 64); blr(X11); + z = cp; E(0x34000000); /* cbz w0, done */ + li(W0, i + 1); b_to(x_exit); /* a translation died: resume at i+1 in c */ + cb_fix(a, cp); cb_fix(z, cp); + *c |= (cp - c) & 0x3ffffff; + return; + case 3: ldr_u(X9, SP, 0); E(0x9100128a); ldr_rr(X9, X10, X9, SXTW); str_u(X9, SP, 0); return; /* add x10, x20, #4 */ + case 4: E(0x91001294); return; /* add x20, x20, #4 */ + case 5: E(0x29402a89); E(0x2900268a); return; /* ldp w9, w10, [x20]; stp w10, w9, [x20] */ + case 10: ldrx_u(X11, ST, 40); blr(X11); E(0x13001c09); str_pre(X9, SP, -4); return; /* sxtb w9, w0 */ + case 11: + ldrx_u(X9, ST, 56); + if (sizeof J == 1) E(0x39400129); else ldr_u(X9, X9, 0); /* ldrb w9, [x9] */ + str_pre(X9, SP, -4); + return; + case 12: ldr_post(W0, SP, 4); E(0x13001c00); ldrx_u(X11, ST, 48); blr(X11); return; /* sxtb w0, w0 */ + } + ldr_post(X9, SP, 4); + ldr_u(X10, SP, 0); + switch (k) { + case 13: op3(0x0b000000, X10, X10, X9); break; + case 14: op3(0x4b000000, X10, X10, X9); break; + case 15: op3(0x1b007c00, X10, X10, X9); break; + case 16: op3(0x0a000000, X10, X10, X9); break; + case 17: op3(0x2a000000, X10, X10, X9); break; + case 18: op3(0x4a000000, X10, X10, X9); break; + case 19: op3(0x1ac02800, X10, X10, X9); break; + default: E(0x6b09015f); E(0x1a9fa7ea); /* cmp w10, w9; cset w10, lt */ + } + str_u(X10, SP, 0); +} + +/* one micro-op, operands in registers. tmp is a scratch register for the + remainders; x16 and x17 are scratch too. */ +static void emit_uop(int op, int d, int a, int b, int c, int tmp) +{ + switch (op) { + case U_ADD: op3(0x0b000000, d, a, b); break; + case U_SUB: op3(0x4b000000, d, a, b); break; + case U_MUL: op3(0x1b007c00, d, a, b); break; + case U_SDIV: op3(0x1ac00c00, d, a, b); break; + case U_UDIV: op3(0x1ac00800, d, a, b); break; + case U_SREM: op3(0x1ac00c00, tmp, a, b); E(0x1b008000 | b << 16 | a << 10 | tmp << 5 | d); break; /* msub */ + case U_UREM: op3(0x1ac00800, tmp, a, b); E(0x1b008000 | b << 16 | a << 10 | tmp << 5 | d); break; + case U_AND: op3(0x0a000000, d, a, b); break; + case U_OR: op3(0x2a000000, d, a, b); break; + case U_XOR: op3(0x4a000000, d, a, b); break; + case U_SHL: op3(0x1ac02000, d, a, b); break; + case U_SHR: op3(0x1ac02400, d, a, b); break; + case U_SAR: op3(0x1ac02800, d, a, b); break; + case U_NEG: E(0x4b0003e0 | a << 16 | d); break; + case U_NOT: E(0x2a2003e0 | a << 16 | d); break; + case U_LT: E(0x6b00001f | b << 16 | a << 5); E(0x1a9fa7e0 | d); break; /* cmp; cset lt */ + case U_LTU: E(0x6b00001f | b << 16 | a << 5); E(0x1a9f27e0 | d); break; /* cset lo */ + case U_EQ: E(0x6b00001f | b << 16 | a << 5); E(0x1a9f17e0 | d); break; + case U_NE: E(0x6b00001f | b << 16 | a << 5); E(0x1a9f07e0 | d); break; + case U_MIN: E(0x6b00001f | b << 16 | a << 5); E(0x1a800000 | b << 16 | 11 << 12 | a << 5 | d); break; + case U_MAX: E(0x6b00001f | b << 16 | a << 5); E(0x1a800000 | b << 16 | 12 << 12 | a << 5 | d); break; + case U_MINU: E(0x6b00001f | b << 16 | a << 5); E(0x1a800000 | b << 16 | 3 << 12 | a << 5 | d); break; + case U_MAXU: E(0x6b00001f | b << 16 | a << 5); E(0x1a800000 | b << 16 | 8 << 12 | a << 5 | d); break; + case U_MULHS: E(0x9b207c00 | b << 16 | a << 5 | 16); E(0xd360fc00 | 16 << 5 | d); break; /* smull; lsr #32 */ + case U_MULHU: E(0x9ba07c00 | b << 16 | a << 5 | 16); E(0xd360fc00 | 16 << 5 | d); break; + case U_CLZ: E(0x5ac01000 | a << 5 | d); break; + case U_ABS: E(0x7100001f | a << 5); E(0x5a800400 | a << 16 | 10 << 12 | a << 5 | d); break; /* cmp #0; cneg */ + case U_SEL: E(0x7100001f | a << 5); E(0x1a800000 | c << 16 | 1 << 12 | b << 5 | d); break; /* csel ne */ + case U_LD: /* out of range reads 0, like the interpreter */ + E(0x52800011); E(0x7144001f | a << 5); E(0x54000042); ldr_rr(17, M, a, UXTW); movw(d, 17); + break; + case U_ST: E(0x7144001f | a << 5); E(0x54000042); str_rr(b, M, a, UXTW); break; + } +} +/* an enhancement, inlined: inputs from the data stack into w0..w15, the + optimized micro-ops, outputs back. constants are made where they're used, + or folded into the instruction (a fixed address, a small addend). */ +static int ekc[MAXUOP * 4 + 16], ekv[MAXUOP * 4 + 16]; +static void emit_enh_e(Enh *e, int s) +{ + signed char *r = e->reg; + int i, j, delta, na; + if (!r) { /* didn't fit in registers: x0 = data stack, w1 = slot, call the interpreter */ + E(0xaa1403e0); li(W1, s); ldrx_u(X11, ST, 80); blr(X11); E(0xaa0003f4); + return; + } + for (i = 0; i < e->nv; i++) ekc[i] = 0; + for (i = 0; i < e->n; i++) if (e->u[i].op == U_LI) { ekc[e->u[i].d] = 1; ekv[e->u[i].d] = e->u[i].imm; } +#define EOP(x) (ekc[x] ? (li(r[x], ekv[x]), r[x]) : r[x]) + for (j = 0; j < e->nin; j++) if (r[j] >= 0) ldr_u(r[j], SP, 4 * (e->nin - 1 - j)); + for (i = 0; i < e->n; i++) { + Uop *u = &e->u[i]; + int d = u->op == U_ST ? 0 : r[u->d], a = 0, b = 0, c = 0; + if (u->op == U_LI) continue; + if (u->op == U_LD && ekc[u->a] && (unsigned)ekv[u->a] < MW) { + if (ekv[u->a] < 4096) ldr_u(d, M, ekv[u->a] * 4); else ldr_rr(d, M, EOP(u->a), UXTW); + continue; + } + if ((u->op == U_ADD || u->op == U_SUB) && ekc[u->b] && ekv[u->b] >= 0 && ekv[u->b] < 4096) { + E((u->op == U_ADD ? 0x11000000 : 0x51000000) | ekv[u->b] << 10 | EOP(u->a) << 5 | d); + continue; + } + na = nargs(u->op); + if (na > 0) a = EOP(u->a); + if (na > 1) b = EOP(u->b); + if (na > 2) c = EOP(u->c); + emit_uop(u->op, d, a, b, c, 16); + } + delta = 4 * (e->nin - e->nout); + if (delta > 0) E(0x91000294 | delta << 10); + if (delta < 0) E(0xd1000294 | (-delta) << 10); + for (j = 0; j < e->nout; j++) str_u(EOP(e->out[j]), SP, 4 * (e->nout - 1 - j)); +#undef EOP +} +static void emit_enh(int s) { emit_enh_e(&enh[s], s); } +/* lit .. lit xop: the enhancement specialized on the literals that feed its + last inputs, so the optimizer sees them as constants (x / 7 becomes a + multiply, lfa(k) a load and an add). 0 if it worked. */ +static int spec_enh(int s, int *ks, int nk, Enh *te) +{ + Enh *e = &enh[s]; + int nin = e->nin - nk, i, j, out[8], na; + static int mp[MAXUOP * 4 + 16]; + if (e->nv > MAXUOP * 4) return -1; + un = 0; unv = nin; uerr = 0; + for (j = 0; j < nin; j++) mp[j] = j; + for (j = 0; j < nk; j++) mp[nin + j] = unew(U_LI, 0, 0, 0, ks[j]); + for (i = 0; i < e->n; i++) { + Uop *u = &e->u[i]; + na = nargs(u->op); + j = unew(u->op, na > 0 ? mp[u->a] : 0, na > 1 ? mp[u->b] : 0, na > 2 ? mp[u->c] : 0, u->imm); + if (u->op != U_ST) mp[u->d] = j; + } + for (j = 0; j < e->nout; j++) out[j] = mp[e->out[j]]; + memset(te, 0, sizeof *te); + if (uerr || optimize(te, nin, out, e->nout)) return -1; + regalloc(te); + if (!te->reg) { free(te->u); return -1; } + return 0; +} + +/* ---- o3 regions on arm64 ---- + vars live in w0..w7 (w0 is the frame pointer), block values in w8..w14, + w26..w28 and the homes of unused vars. constants have no register: they + are made in a scratch register where they're used (w16, w17, w15), or + folded into the instruction. x15..x17 are scratch. */ +static signed char rreg[MAXUOP * 4 + 16]; +static unsigned char rk[MAXUOP * 4 + 16], rhome[MAXUOP * 4 + 16]; +static int rkv[MAXUOP * 4 + 16], rlast[MAXUOP * 4 + 16]; +static unsigned **rfa; static int *rfb, rnf; /* branches to blocks, fixed up at the end */ + +static void rforget(Rg *R) +{ + int e; + if (R->nat) for (e = 0; e < R->ne; e++) if (tab[R->ent[e]] == R->stub[e]) tab[R->ent[e]] = 0; + R->nat = 0; +} +static void rmark(Rg *R) { int x; for (x = R->lo; x < R->hi; x++) cov[x] = 1; } +static int ralloc(Rg *R, RB *B) +{ + int i, j, k, n = B->n; + unsigned fr = 0x7f00u | 0x1c000000u; /* w8..w14, w26..w28 */ + for (j = R->nv; j < 8; j++) fr |= 1u << j; + for (i = 0; i < B->nv; i++) { rlast[i] = -2; rk[i] = 0; rhome[i] = 0; rreg[i] = -1; } + for (i = 0; i < n; i++) { + Uop *u = &B->u[i]; int na = nargs(u->op); + if (na > 0) rlast[u->a] = i; + if (na > 1) rlast[u->b] = i; + if (na > 2) rlast[u->c] = i; + } + for (j = 0; j < B->nso; j++) rlast[B->so[2 * j + 1]] = n; + for (j = 1; j < R->nv; j++) if (B->vs[j] >= 0) rlast[B->vs[j]] = n; + if (B->term == T_CBR || B->term == T_JMP || B->term == T_CALL) rlast[B->cv] = n; + for (i = 0; i < n; i++) { + Uop *u = &B->u[i]; int na = nargs(u->op), ops[3], d = u->d; + ops[0] = u->a; ops[1] = u->b; ops[2] = u->c; + for (j = 0; j < na; j++) { int x = ops[j]; if (rlast[x] == i && rreg[x] >= 0 && !rhome[x]) fr |= 1u << rreg[x]; } + if (u->op == U_ST) continue; + if (u->op == U_LI) { rk[d] = 1; rkv[d] = u->imm; continue; } + if (u->op == U_VGET) { rreg[d] = (signed char)u->imm; rhome[d] = 1; continue; } + for (k = 0; k < 32 && !(fr >> k & 1); k++); + if (k == 32) return -1; + fr &= ~(1u << k); rreg[d] = (signed char)k; + if (rlast[d] == -2) fr |= 1u << k; + } + return 0; +} +static int ropr(int x, int s) { if (rk[x]) { li(s, rkv[x]); return s; } return rreg[x]; } +/* w t <-> the data stack word o places from the top at block entry */ +static void rsw(int t, int o, int store) +{ + int by = o * 4; + if (by >= 0 && by < 16384) { if (store) str_u(t, SP, by); else ldr_u(t, SP, by); } + else if (by >= -256 && by < 256) E((store ? 0xb8000000u : 0xb8400000u) | (unsigned)(by & 0x1ff) << 12 | SP << 5 | t); /* stur, ldur */ + else { li(16, by); addx_ext(16, SP, 16, SXTW); if (store) str_u(t, 16, 0); else ldr_u(t, 16, 0); } +} +static void addsp(int by) +{ + if (by > 0 && by < 4096) E(0x91000294 | by << 10); + else if (by < 0 && -by < 4096) E(0xd1000294 | (-by) << 10); + else if (by) { li(16, by); addx_ext(SP, SP, 16, SXTW); } +} +static void rspill(Rg *R) +{ + int v; + for (v = 1; v < R->nv; v++) { E(0x11000000 | R->slot[v] << 10 | 16); str_rr(v, M, 16, UXTW); } /* add w16, w0, #slot */ +} +static void rload(Rg *R) +{ + int v; + li(16, ZFP); ldr_rr(0, M, 16, UXTW); + for (v = 1; v < R->nv; v++) { E(0x11000000 | R->slot[v] << 10 | 16); ldr_rr(v, M, 16, UXTW); } +} +static void rexitk(Rg *R, int t) { rspill(R); li(W0, t); b_to(x_disp); } +static void rbranch(int b) { rfa[rnf] = cp; rfb[rnf++] = b; E(0x14000000); } +/* m[a] = v, checked like the jit's own stores: a word some translation covers goes through hook */ +static void rstore(int a, int v) +{ + unsigned *p1, *p2, *p3; + int j; + E(0x7144001f | a << 5); /* cmp wa, #MW */ + p1 = cp; E(0x54000002); /* b.hs done */ + ldrx_u(15, ST, 32); + E(0x38604800 | a << 16 | 15 << 5 | 15); /* ldrb w15, [x15, wa, uxtw] */ + p2 = cp; E(0x35000000 | 15); /* cbnz w15, slow */ + str_rr(v, M, a, UXTW); + p3 = cp; E(0x14000000); + cb_fix(p2, cp); + E(0xd10203ff); /* sub sp, sp, #128 */ + for (j = 0; j < 16; j += 2) E(0xa9000000 | (j / 2 * 2) << 15 | (j + 1) << 10 | 31 << 5 | j); /* stp xj, xj+1, [sp, #8j] */ + movw(15, a); movw(1, v); movw(0, 15); + ldrx_u(16, ST, 64); blr(16); + for (j = 0; j < 16; j += 2) E(0xa9400000 | (j / 2 * 2) << 15 | (j + 1) << 10 | 31 << 5 | j); + E(0x910203ff); /* add sp, sp, #128 */ + cb_fix(p1, cp); + *p3 |= (cp - p3) & 0x3ffffff; +} +static void rpmove(Rg *R, RB *B) +{ + int dst[RMAXV], src[RMAXV], n = 0, j, k, x; + for (j = 1; j < R->nv; j++) + if (B->vs[j] >= 0 && !rk[B->vs[j]] && rreg[B->vs[j]] != j) { dst[n] = j; src[n] = rreg[B->vs[j]]; n++; } + while (n) { + for (j = 0; j < n; j++) { for (k = 0; k < n && src[k] != dst[j]; k++); if (k == n) break; } + if (j < n) { movw(dst[j], src[j]); n--; dst[j] = dst[n]; src[j] = src[n]; continue; } + x = src[0]; movw(16, x); /* a cycle: park one */ + for (k = 0; k < n; k++) if (src[k] == x) src[k] = 16; + } + for (j = 1; j < R->nv; j++) if (B->vs[j] >= 0 && rk[B->vs[j]]) li(j, rkv[B->vs[j]]); +} +static void emit_uop(int op, int d, int a, int b, int c, int tmp); +static int remit(Rg *R, int bi) +{ + RB *B = &R->b[bi]; + int i, j, d, a, b, c; + unsigned *p; + if (ralloc(R, B)) return -1; + for (i = 0; i < B->n; i++) { + Uop *u = &B->u[i]; + switch (u->op) { + case U_LI: case U_VGET: continue; + case U_SLD: rsw(rreg[u->d], u->imm, 0); continue; + case U_LD: + if (rk[u->a] && (unsigned)rkv[u->a] < MW) { /* a fixed address */ + if (rkv[u->a] < 4096) ldr_u(rreg[u->d], M, rkv[u->a] * 4); + else { li(16, rkv[u->a]); ldr_rr(rreg[u->d], M, 16, UXTW); } + continue; + } + break; + case U_ST: a = ropr(u->a, 16); rstore(a, ropr(u->b, 17)); continue; + case U_ADD: case U_SUB: + if (rk[u->b] && rkv[u->b] >= 0 && rkv[u->b] < 4096) { + E((u->op == U_ADD ? 0x11000000 : 0x51000000) | rkv[u->b] << 10 | ropr(u->a, 16) << 5 | rreg[u->d]); + continue; + } + break; + } + d = rreg[u->d]; + a = nargs(u->op) > 0 ? ropr(u->a, 16) : 0; + b = nargs(u->op) > 1 ? ropr(u->b, 17) : 0; + c = nargs(u->op) > 2 ? ropr(u->c, 15) : 0; + emit_uop(u->op, d, a, b, c, 15); + } + for (j = 0; j < B->nso; j++) rsw(ropr(B->so[2 * j + 1], 17), B->so[2 * j], 1); + if (B->term == T_CBR || B->term == T_JMP || B->term == T_CALL) { + if (rk[B->cv]) li(17, rkv[B->cv]); else movw(17, rreg[B->cv]); + } + addsp(B->delta * 4); + rpmove(R, B); + switch (B->term) { + case T_BR: rbranch(B->s1); break; + case T_CBR: + if (B->s1 >= 0) { rfa[rnf] = cp; rfb[rnf++] = B->s1; E(0x34000000 | 17); } /* cbz w17, taken */ + else { p = cp; E(0x35000000 | 17); rexitk(R, B->x1); cb_fix(p, cp); } + if (B->s2 >= 0) rbranch(B->s2); else rexitk(R, B->x2); + break; + case T_CALL: rspill(R); li(16, B->x2); str_post(16, RP, 4); movw(W0, 17); b_to(x_disp); break; + case T_RET: rspill(R); ldr_pre(W0, RP, -4); b_to(x_disp); break; + default: rspill(R); movw(W0, 17); b_to(x_disp); + } + return 0; +} +/* the whole region: entry stubs that load the vars, then the blocks */ +static int rjit(Rg *R, int wr) +{ + unsigned *start = cp, **lab; + int b, e, j, bad = 0; + long need = (long)(R->hi - R->lo) * 96 + (long)R->ne * 32 + 4096; + if (need > (code + CODEW) - cp) return -1; + if (wr) jitwrite(1); + lab = malloc((R->nb + 1) * sizeof *lab); + rfa = malloc((2 * R->nb + R->ne + 4) * sizeof *rfa); rfb = malloc((2 * R->nb + R->ne + 4) * sizeof *rfb); rnf = 0; + for (e = 0; e < R->ne; e++) { R->stub[e] = cp; rload(R); rbranch(R->entb[e]); } + for (b = 0; b < R->nb && !bad; b++) { lab[b] = cp; bad = remit(R, b); } + if (!bad) { + for (j = 0; j < rnf; j++) { + unsigned *at = rfa[j], *to = lab[rfb[j]]; + if ((*at & 0xfc000000u) == 0x14000000u) *at |= (to - at) & 0x3ffffff; else cb_fix(at, to); + } + R->nat = start; + for (e = 0; e < R->ne; e++) tab[R->ent[e]] = R->stub[e]; + syncicache(start, cp); + if (trace) fprintf(stderr, "o3: %d..%d -> %ld bytes of arm64\n", R->lo, R->hi - 1, (long)((char *)cp - (char *)start)); + } else { + cp = start; + if (trace) fprintf(stderr, "o3: %d..%d: a block needs too many registers\n", R->lo, R->hi - 1); + } + free(lab); free(rfa); free(rfb); + if (wr) jitwrite(0); + return bad; +} +static void rcompile(Rg *R, int eager) +{ + if (R->nat || R->nonat) return; + if (rjit(R, eager) && cp - code < CODEW / 2) R->nonat = 1; /* registers, not space: give up on it */ +} + +static int inst_hook(int d) { return install(d); } +static long xrun(long sp, int slot) { return (long)xexec(&enh[slot], (int *)sp); } + +static void *translate(int p) +{ + unsigned *start; + int i = p, n = 0, w, k, t; + if (cp - code > CODEW - 65536 || nbl >= MAXB) flush(); + if (o3ent[p]) { /* an o3 region starts here */ + Rg *R = rat(p); + if (R) { rcompile(R, 0); if (R->nat && tab[p]) return tab[p]; } + } + start = cp; + for (;;) { + if (i >= MW) { E(0x12800000); b_to(x_exit); break; } /* movn w0, #0: halt */ + if (n++ >= BLKW) { li(W0, i); b_to(x_disp); break; } + w = m[i]; + cov[i] = 1; + if (w & 1 && i + 1 < MW && !(m[i + 1] & 1) && (k = m[i + 1] / 2) >= 6 && k <= 8) { + t = (w - 1) / 2; /* literal + call/jmp/jz with a known target */ + cov[i + 1] = 1; + if (k == 8) { + unsigned *a; + ldr_post(X10, SP, 4); + a = cp; E(0x3500000a); /* cbnz w10, fallthrough */ + li(W0, t); b_to(x_disp); + cb_fix(a, cp); + i += 2; + continue; + } + li(X10, i + 2); + if (k == 6) str_post(X10, RP, 4); else str_u(X10, RP, 0); + li(W0, t); b_to(x_disp); + i += 2; + break; + } + if (w & 1) { + int L = 0, x, s, nk, ks[8], j; + Enh te; + while (L < 8 && i + L < MW && (m[i + L] & 1)) L++; + x = i + L < MW ? m[i + L] : 1; + if (!(x & 1) && (s = x / 2 - XOP0) >= 0 && s < NENH && enh[s].live && enh[s].nin) { + nk = L < enh[s].nin ? L : enh[s].nin; + for (j = 0; j < nk; j++) ks[j] = m[i + L - nk + j] >> 1; + if (!spec_enh(s, ks, nk, &te)) { + for (j = 0; j < L - nk; j++) { li(X9, m[i + j] >> 1); str_pre(X9, SP, -4); } + for (j = 0; j <= L; j++) cov[i + j] = 1; + emit_enh_e(&te, s); + free(te.u); free(te.reg); + i += L + 1; n += L; + continue; + } + } + li(X9, (w - 1) / 2); str_pre(X9, SP, -4); i++; continue; + } + k = w / 2; + if (k == 0) { E(0x12800000); b_to(x_exit); i++; break; } + if (k < 0 || k == 6 || k == 7) { + ldr_post(W0, SP, 4); + li(X10, i + 1); + if (k == 6) str_post(X10, RP, 4); else str_u(X10, RP, 0); + if (k < 0) { li(X11, 7 - k); addx_ext(RP, RP, X11, SXTW); } + b_to(x_disp); + i++; + break; + } + if (k == 8) { /* jz: taken leaves, not taken keeps going */ + ldp_post(X9, X10, SP, 8); + E(0x3500006a); /* cbnz w10, +3 */ + movw(W0, X9); + b_to(x_disp); + i++; + continue; + } + if (k == 9) { ldr_pre(W0, RP, -4); b_to(x_disp); i++; break; } + if (k == 21) { /* install: call into c, then return to it so it can flush */ + ldr_u(W0, SP, 0); ldrx_u(X11, ST, 72); blr(X11); str_u(W0, SP, 0); + li(W0, i + 1); b_to(x_exit); i++; break; + } + if (k >= XOP0 && k < XOP0 + NENH && enh[k - XOP0].live) { emit_enh(k - XOP0); i++; continue; } + if (k > 20) { E(0x12800000); b_to(x_exit); i++; break; } /* not wired: halt */ + op(i, k); + i++; + } + bl[nbl].a = p; + bl[nbl++].b = i; + tab[p] = start; + syncicache(start, cp); + if (trace) fprintf(stderr, "jit: %d..%d -> %ld bytes\n", p, i - 1, (long)((char *)cp - (char *)start)); + return start; +} + +#else +/* portable path for hosts without the arm64 jit: a plain interpreter with + the same semantics (shift counts wrap at 32, stores outside memory are + ignored, loads outside memory read 0, leaving memory halts). */ +#ifdef ZYLLVM +/* ---- llvm backend ---- + a tiered jit for hosts the arm64 backend doesn't cover. the interpreter + counts how often each block start is reached; a hot block is translated to + llvm ir (same block rules as the arm64 jit: it ends at call, jump, ret or + halt, and keeps going past a jz), optimized at O2 and compiled with orc. + enhancements are inlined as ir. stores into compiled words drop the blocks + that cover them, so self-modifying code still works. */ +#include +#include +#include +#include +#include +#include +static int HOT = 5000; /* runs before a block gets compiled: ZYHOT */ +static const char *lpasses = "default"; /* ZYPASSES */ +#define LMAXB 65536 +typedef int (*Blk)(int *mem, int *regs); +static Blk lt[MW]; /* address -> compiled block */ +static unsigned short hot[MW]; +static unsigned char lcov[MW]; /* 1 if a compiled block covers the word */ +static struct { int a, b; } lbl[LMAXB]; +static int nlbl, llvm_ok, nblocks; +static LLVMOrcLLJITRef jit; +static LLVMOrcThreadSafeContextRef tsc; +static LLVMContextRef ctx; +static LLVMTypeRef i8t, i32t, i64t, ptrt, i1t; +static LLVMBuilderRef bd; +static LLVMValueRef fn_, vm_, vregs, vs, vr; /* function, memory, regs, s and r slots */ +static LLVMModuleRef mod_; + +static unsigned char rcov[MW]; /* 1 on the words of a lifted o3 region */ +static int rdrop(int a); +static void ldrop_all(void) +{ + rdrop_all(); memset(rcov, 0, sizeof rcov); + memset(lt, 0, sizeof lt); memset(lcov, 0, sizeof lcov); memset(hot, 0, sizeof hot); nlbl = 0; + if (trace) fprintf(stderr, "llvm: dropped every block\n"); +} +/* a store to a word some block covers: write it, drop those blocks */ +static int lhook(int a, int v) +{ + int j, x, hit = 0; + if (m[a] == v) return 0; + m[a] = v; + for (j = 0; j < nlbl; j++) + if (lbl[j].a >= 0 && lbl[j].a <= a && a < lbl[j].b) { + lt[lbl[j].a] = 0; hot[lbl[j].a] = 0; + if (trace) fprintf(stderr, "llvm: dropped %d..%d\n", lbl[j].a, lbl[j].b - 1); + lbl[j].a = -1; hit = 1; + } + if (rcov[a] && rdrop(a)) hit = 1; + if (hit) { + memset(lcov, 0, sizeof lcov); + for (j = 0; j < nlbl; j++) if (lbl[j].a >= 0) for (x = lbl[j].a; x < lbl[j].b; x++) lcov[x] = 1; + for (j = 0; j < nrg; j++) if (rgs[j]->lifted) for (x = rgs[j]->lo; x < rgs[j]->hi; x++) lcov[x] = 1; + } + return hit; +} +static int linstall(int d) { return install(d); } + +static LLVMValueRef K32(int v) { return LLVMConstInt(i32t, (unsigned)v, 0); } +static LLVMValueRef cptr(void *p) { return LLVMConstIntToPtr(LLVMConstInt(i64t, (unsigned long long)(size_t)p, 0), ptrt); } +static LLVMValueRef mslot(LLVMValueRef idx) { return LLVMBuildGEP2(bd, i32t, vm_, &idx, 1, ""); } +static LLVMValueRef ld(LLVMValueRef idx) { return LLVMBuildLoad2(bd, i32t, mslot(idx), ""); } +static void st(LLVMValueRef idx, LLVMValueRef v) { LLVMBuildStore(bd, v, mslot(idx)); } +static LLVMValueRef gets(void) { return LLVMBuildLoad2(bd, i32t, vs, ""); } +static void sets(LLVMValueRef v) { LLVMBuildStore(bd, v, vs); } +static LLVMValueRef add(LLVMValueRef a, int k) { return LLVMBuildAdd(bd, a, K32(k), ""); } +/* a load that reads 0 outside memory */ +static LLVMValueRef safeld(LLVMValueRef a) +{ + LLVMValueRef in = LLVMBuildICmp(bd, LLVMIntULT, a, K32(MW), ""); + LLVMValueRef v = ld(LLVMBuildSelect(bd, in, a, K32(0), "")); + return LLVMBuildSelect(bd, in, v, K32(0), ""); +} +static void push(LLVMValueRef v) { LLVMValueRef s = add(gets(), -1); sets(s); st(s, v); } +/* leave the block: write s and r back, return the next vm address */ +static void leave(LLVMValueRef next) +{ + LLVMValueRef r0 = K32(0), r1 = K32(1); + LLVMBuildStore(bd, gets(), LLVMBuildGEP2(bd, i32t, vregs, &r0, 1, "")); + LLVMBuildStore(bd, LLVMBuildLoad2(bd, i32t, vr, ""), LLVMBuildGEP2(bd, i32t, vregs, &r1, 1, "")); + LLVMBuildRet(bd, next); +} +static LLVMBasicBlockRef newbb(void) { return LLVMAppendBasicBlockInContext(ctx, fn_, ""); } +static LLVMValueRef callp(void *f, LLVMTypeRef ret, LLVMTypeRef *at, int n, LLVMValueRef *args) +{ + LLVMTypeRef ft = LLVMFunctionType(ret, at, (unsigned)n, 0); + return LLVMBuildCall2(bd, ft, cptr(f), args, (unsigned)n, ""); +} +/* one micro-op as ir: the same semantics as ev() */ +static LLVMValueRef ir_op(int op, LLVMValueRef a, LLVMValueRef b, LLVMValueRef c) +{ + LLVMValueRef z = K32(0), q, d, t, c1, c2, r = 0; + LLVMTypeRef i64 = i64t; + switch (op) { + case U_ADD: r = LLVMBuildAdd(bd, a, b, ""); break; + case U_SUB: r = LLVMBuildSub(bd, a, b, ""); break; + case U_MUL: r = LLVMBuildMul(bd, a, b, ""); break; + case U_SDIV: case U_SREM: /* /0 and INT_MIN/-1 are defined, like the cpu */ + c1 = LLVMBuildICmp(bd, LLVMIntEQ, b, z, ""); + c2 = LLVMBuildAnd(bd, LLVMBuildICmp(bd, LLVMIntEQ, a, K32((int)0x80000000), ""), LLVMBuildICmp(bd, LLVMIntEQ, b, K32(-1), ""), ""); + d = LLVMBuildSelect(bd, LLVMBuildOr(bd, c1, c2, ""), K32(1), b, ""); + if (op == U_SDIV) { q = LLVMBuildSDiv(bd, a, d, ""); r = LLVMBuildSelect(bd, c1, z, q, ""); } + else { q = LLVMBuildSRem(bd, a, d, ""); r = LLVMBuildSelect(bd, c1, a, LLVMBuildSelect(bd, c2, z, q, ""), ""); } + break; + case U_UDIV: case U_UREM: + c1 = LLVMBuildICmp(bd, LLVMIntEQ, b, z, ""); + d = LLVMBuildSelect(bd, c1, K32(1), b, ""); + if (op == U_UDIV) r = LLVMBuildSelect(bd, c1, z, LLVMBuildUDiv(bd, a, d, ""), ""); + else r = LLVMBuildSelect(bd, c1, a, LLVMBuildURem(bd, a, d, ""), ""); + break; + case U_AND: r = LLVMBuildAnd(bd, a, b, ""); break; + case U_OR: r = LLVMBuildOr(bd, a, b, ""); break; + case U_XOR: r = LLVMBuildXor(bd, a, b, ""); break; + case U_SHL: r = LLVMBuildShl(bd, a, LLVMBuildAnd(bd, b, K32(31), ""), ""); break; + case U_SHR: r = LLVMBuildLShr(bd, a, LLVMBuildAnd(bd, b, K32(31), ""), ""); break; + case U_SAR: r = LLVMBuildAShr(bd, a, LLVMBuildAnd(bd, b, K32(31), ""), ""); break; + case U_NEG: r = LLVMBuildSub(bd, z, a, ""); break; + case U_NOT: r = LLVMBuildNot(bd, a, ""); break; + case U_LT: r = LLVMBuildZExt(bd, LLVMBuildICmp(bd, LLVMIntSLT, a, b, ""), i32t, ""); break; + case U_LTU: r = LLVMBuildZExt(bd, LLVMBuildICmp(bd, LLVMIntULT, a, b, ""), i32t, ""); break; + case U_EQ: r = LLVMBuildZExt(bd, LLVMBuildICmp(bd, LLVMIntEQ, a, b, ""), i32t, ""); break; + case U_NE: r = LLVMBuildZExt(bd, LLVMBuildICmp(bd, LLVMIntNE, a, b, ""), i32t, ""); break; + case U_MIN: r = LLVMBuildSelect(bd, LLVMBuildICmp(bd, LLVMIntSLT, a, b, ""), a, b, ""); break; + case U_MAX: r = LLVMBuildSelect(bd, LLVMBuildICmp(bd, LLVMIntSGT, a, b, ""), a, b, ""); break; + case U_MINU: r = LLVMBuildSelect(bd, LLVMBuildICmp(bd, LLVMIntULT, a, b, ""), a, b, ""); break; + case U_MAXU: r = LLVMBuildSelect(bd, LLVMBuildICmp(bd, LLVMIntUGT, a, b, ""), a, b, ""); break; + case U_MULHS: case U_MULHU: { + LLVMValueRef x = op == U_MULHS ? LLVMBuildSExt(bd, a, i64, "") : LLVMBuildZExt(bd, a, i64, ""); + LLVMValueRef y = op == U_MULHS ? LLVMBuildSExt(bd, b, i64, "") : LLVMBuildZExt(bd, b, i64, ""); + r = LLVMBuildTrunc(bd, LLVMBuildLShr(bd, LLVMBuildMul(bd, x, y, ""), LLVMConstInt(i64, 32, 0), ""), i32t, ""); + break; } + case U_CLZ: { + unsigned id = LLVMLookupIntrinsicID("llvm.ctlz", 9); + LLVMValueRef f = LLVMGetIntrinsicDeclaration(mod_, id, &i32t, 1), args[2]; + args[0] = a; args[1] = LLVMConstInt(i1t, 0, 0); + r = LLVMBuildCall2(bd, LLVMIntrinsicGetType(ctx, id, &i32t, 1), f, args, 2, ""); + break; } + case U_ABS: t = LLVMBuildICmp(bd, LLVMIntSLT, a, z, ""); r = LLVMBuildSelect(bd, t, LLVMBuildSub(bd, z, a, ""), a, ""); break; + case U_SEL: r = LLVMBuildSelect(bd, LLVMBuildICmp(bd, LLVMIntNE, a, z, ""), b, c, ""); break; + } + return r; +} +/* an enhancement as ir */ +static void ir_enh(Enh *e) +{ + LLVMValueRef *v = calloc(e->nv + 1, sizeof *v), s = gets(); + int i, j; + for (j = 0; j < e->nin; j++) v[j] = ld(add(s, e->nin - 1 - j)); + for (i = 0; i < e->n; i++) { + Uop *u = &e->u[i]; + LLVMValueRef a = u->op == U_LI ? 0 : v[u->a], b = uarity[u->op] > 1 || u->op == U_ST ? v[u->b] : 0, c = uarity[u->op] > 2 ? v[u->c] : 0, r = 0; + switch (u->op) { + case U_LI: r = K32(u->imm); break; + case U_LD: r = safeld(a); break; + case U_ST: { + LLVMBasicBlockRef yes = newbb(), done = newbb(); + LLVMBuildCondBr(bd, LLVMBuildICmp(bd, LLVMIntULT, a, K32(MW), ""), yes, done); + LLVMPositionBuilderAtEnd(bd, yes); st(a, b); LLVMBuildBr(bd, done); + LLVMPositionBuilderAtEnd(bd, done); + break; } + default: r = ir_op(u->op, a, b, c); + } + if (u->op != U_ST) v[u->d] = r; + } + s = add(s, e->nin - e->nout); sets(s); + for (j = 0; j < e->nout; j++) st(add(s, e->nout - 1 - j), v[e->out[j]]); + free(v); +} +static void llvm_init(void) +{ + LLVMErrorRef err; + if (getenv("ZYHOT")) HOT = atoi(getenv("ZYHOT")); + if (HOT < 1) HOT = 1; + if (HOT > 65000) HOT = 65000; + if (getenv("ZYPASSES")) lpasses = getenv("ZYPASSES"); + LLVMInitializeNativeTarget(); LLVMInitializeNativeAsmPrinter(); + if ((err = LLVMOrcCreateLLJIT(&jit, 0))) { char *msg = LLVMGetErrorMessage(err); fprintf(stderr, "llvm: %s\n", msg); LLVMDisposeErrorMessage(msg); return; } + tsc = LLVMOrcCreateNewThreadSafeContext(); + ctx = LLVMOrcThreadSafeContextGetContext(tsc); + i1t = LLVMInt1TypeInContext(ctx); i8t = LLVMInt8TypeInContext(ctx); i32t = LLVMInt32TypeInContext(ctx); + i64t = LLVMInt64TypeInContext(ctx); ptrt = LLVMPointerTypeInContext(ctx, 0); + bd = LLVMCreateBuilderInContext(ctx); + llvm_ok = 1; +} +/* compile the block at p; 0 if it couldn't be */ +static Blk lcompile(int p) +{ + char name[32]; LLVMTypeRef pt[2], ft; LLVMValueRef s, a, b, v, r, args[2]; LLVMTypeRef at[2]; + LLVMOrcExecutorAddress addr; LLVMErrorRef err; LLVMPassBuilderOptionsRef po; + int i = p, n = 0, w, k, t, done = 0; + if (!llvm_ok || nlbl >= LMAXB) return 0; + sprintf(name, "b%d_%d", p, nblocks++); + mod_ = LLVMModuleCreateWithNameInContext(name, ctx); + pt[0] = ptrt; pt[1] = ptrt; + ft = LLVMFunctionType(i32t, pt, 2, 0); + fn_ = LLVMAddFunction(mod_, name, ft); + vm_ = LLVMGetParam(fn_, 0); vregs = LLVMGetParam(fn_, 1); + LLVMPositionBuilderAtEnd(bd, newbb()); + vs = LLVMBuildAlloca(bd, i32t, "s"); vr = LLVMBuildAlloca(bd, i32t, "r"); + { LLVMValueRef r0 = K32(0), r1 = K32(1); + sets(LLVMBuildLoad2(bd, i32t, LLVMBuildGEP2(bd, i32t, vregs, &r0, 1, ""), "")); + LLVMBuildStore(bd, LLVMBuildLoad2(bd, i32t, LLVMBuildGEP2(bd, i32t, vregs, &r1, 1, ""), ""), vr); } + while (!done) { + if (i >= MW) { leave(K32(MW)); break; } + if (n++ >= 1024) { leave(K32(i)); break; } + w = m[i]; + lcov[i] = 1; + if (w & 1) { + /* a literal followed by call, jump or jz: a known target */ + if (i + 1 < MW && !(m[i + 1] & 1) && (k = m[i + 1] / 2) >= 6 && k <= 8) { + t = w >> 1; lcov[i + 1] = 1; + if (k == 8) { + LLVMBasicBlockRef tk = newbb(), go = newbb(); + s = gets(); a = ld(s); sets(add(s, 1)); + LLVMBuildCondBr(bd, LLVMBuildICmp(bd, LLVMIntEQ, a, K32(0), ""), tk, go); + LLVMPositionBuilderAtEnd(bd, tk); leave(K32(t)); + LLVMPositionBuilderAtEnd(bd, go); + i += 2; continue; + } + if (k == 6) { r = LLVMBuildLoad2(bd, i32t, vr, ""); st(r, K32(i + 2)); LLVMBuildStore(bd, add(r, 1), vr); } + leave(K32(t)); i += 2; break; + } + push(K32(w >> 1)); i++; continue; + } + k = w / 2; + s = gets(); + switch (k) { + case 0: leave(K32(MW)); done = 1; break; + case 1: st(s, safeld(ld(s))); break; + case 2: { + LLVMBasicBlockRef in = newbb(), cv = newbb(), plain = newbb(), out = newbb(), quit = newbb(); + a = ld(s); b = ld(add(s, 1)); sets(add(s, 2)); + LLVMBuildCondBr(bd, LLVMBuildICmp(bd, LLVMIntULT, a, K32(MW), ""), in, out); + LLVMPositionBuilderAtEnd(bd, in); + v = LLVMBuildLoad2(bd, i8t, LLVMBuildGEP2(bd, i8t, cptr(lcov), &a, 1, ""), ""); + LLVMBuildCondBr(bd, LLVMBuildICmp(bd, LLVMIntNE, v, LLVMConstInt(i8t, 0, 0), ""), cv, plain); + LLVMPositionBuilderAtEnd(bd, plain); st(a, b); LLVMBuildBr(bd, out); + LLVMPositionBuilderAtEnd(bd, cv); + at[0] = i32t; at[1] = i32t; args[0] = a; args[1] = b; + v = callp((void *)lhook, i32t, at, 2, args); + LLVMBuildCondBr(bd, LLVMBuildICmp(bd, LLVMIntNE, v, K32(0), ""), quit, out); + LLVMPositionBuilderAtEnd(bd, quit); leave(K32(i + 1)); /* a block died: back to the loop */ + LLVMPositionBuilderAtEnd(bd, out); + break; } + case 3: a = ld(s); st(s, safeld(LLVMBuildAdd(bd, add(s, 1), a, ""))); break; + case 4: sets(add(s, 1)); break; + case 5: a = ld(s); b = ld(add(s, 1)); st(s, b); st(add(s, 1), a); break; + case 6: case 7: + a = ld(s); sets(add(s, 1)); + if (k == 6) { r = LLVMBuildLoad2(bd, i32t, vr, ""); st(r, K32(i + 1)); LLVMBuildStore(bd, add(r, 1), vr); } + leave(a); done = 1; break; + case 8: { + LLVMBasicBlockRef tk = newbb(), go = newbb(); + a = ld(s); b = ld(add(s, 1)); sets(add(s, 2)); + LLVMBuildCondBr(bd, LLVMBuildICmp(bd, LLVMIntEQ, b, K32(0), ""), tk, go); + LLVMPositionBuilderAtEnd(bd, tk); leave(a); + LLVMPositionBuilderAtEnd(bd, go); + break; } + case 9: + r = add(LLVMBuildLoad2(bd, i32t, vr, ""), -1); LLVMBuildStore(bd, r, vr); + leave(ld(r)); done = 1; break; + case 10: v = callp((void *)Q, i8t, 0, 0, 0); push(LLVMBuildSExt(bd, v, i32t, "")); break; + case 11: + if (sizeof J == 1) push(LLVMBuildZExt(bd, LLVMBuildLoad2(bd, i8t, cptr(&J), ""), i32t, "")); + else push(LLVMBuildLoad2(bd, i32t, cptr(&J), "")); + break; + case 12: at[0] = i8t; args[0] = LLVMBuildTrunc(bd, ld(s), i8t, ""); callp((void *)G, LLVMVoidTypeInContext(ctx), at, 1, args); sets(add(s, 1)); break; + case 13: case 14: case 15: case 16: case 17: case 18: case 19: case 20: + a = ld(s); b = ld(add(s, 1)); s = add(s, 1); sets(s); + switch (k) { + case 13: v = LLVMBuildAdd(bd, b, a, ""); break; + case 14: v = LLVMBuildSub(bd, b, a, ""); break; + case 15: v = LLVMBuildMul(bd, b, a, ""); break; + case 16: v = LLVMBuildAnd(bd, b, a, ""); break; + case 17: v = LLVMBuildOr(bd, b, a, ""); break; + case 18: v = LLVMBuildXor(bd, b, a, ""); break; + case 19: v = LLVMBuildAShr(bd, b, LLVMBuildAnd(bd, a, K32(31), ""), ""); break; + default: v = LLVMBuildZExt(bd, LLVMBuildICmp(bd, LLVMIntSLT, b, a, ""), i32t, ""); + } + st(s, v); break; + case 21: /* install: then back to the loop, which drops every block */ + at[0] = i32t; args[0] = ld(s); + st(s, callp((void *)linstall, i32t, at, 1, args)); + leave(K32(i + 1)); done = 1; break; + default: + if (k >= XOP0 && k < XOP0 + NENH && enh[k - XOP0].live) { ir_enh(&enh[k - XOP0]); break; } + leave(K32(MW)); done = 1; /* not wired: halt */ + } + i++; + } + lbl[nlbl].a = p; lbl[nlbl++].b = i; + po = LLVMCreatePassBuilderOptions(); + if ((err = LLVMRunPasses(mod_, lpasses, 0, po))) { LLVMConsumeError(err); } + LLVMDisposePassBuilderOptions(po); + if ((err = LLVMOrcLLJITAddLLVMIRModule(jit, LLVMOrcLLJITGetMainJITDylib(jit), LLVMOrcCreateNewThreadSafeModule(mod_, tsc)))) { + char *msg = LLVMGetErrorMessage(err); fprintf(stderr, "llvm: %s\n", msg); LLVMDisposeErrorMessage(msg); return 0; + } + if ((err = LLVMOrcLLJITLookup(jit, &addr, name))) { char *msg = LLVMGetErrorMessage(err); fprintf(stderr, "llvm: %s\n", msg); LLVMDisposeErrorMessage(msg); return 0; } + if (trace) fprintf(stderr, "llvm: %d..%d compiled\n", p, i - 1); + return (Blk)(size_t)addr; +} +#endif + +/* ---- o3 regions without the arm64 jit: compiled by llvm, or run here ---- */ +#ifndef ZYLLVM +static unsigned char rcov[MW]; /* 1 on the words of a lifted region */ +#endif +static int rvals[MAXUOP * 4 + 16]; +static int rdrop(int a) /* a store landed on region code: drop those regions */ +{ + int j, x, hit = 0; + for (j = 0; j < nrg; j++) + if (rgs[j]->lifted && rgs[j]->lo <= a && a < rgs[j]->hi) { + if (trace) fprintf(stderr, "o3: dropped %d..%d (a store to %d: %d)\n", rgs[j]->lo, rgs[j]->hi - 1, a, m[a]); + rclear(rgs[j]); hit = 1; + } + if (hit) { + memset(rcov, 0, sizeof rcov); + for (j = 0; j < nrg; j++) if (rgs[j]->lifted) for (x = rgs[j]->lo; x < rgs[j]->hi; x++) rcov[x] = 1; + } + return hit; +} +#ifdef ZYLLVM +static int lhook(int a, int v); +#define RSTORE(a, b) do { if ((unsigned)(a) < MW) { if (lcov[a]) lhook(a, b); else m[a] = b; } } while (0) +#else +#define RSTORE(a, b) do { if ((unsigned)(a) < MW) { m[a] = b; if (rcov[a]) rdrop(a); } } while (0) +#endif +/* the region interpreter: ssa values in an array, vars in locals */ +static int rexec(Rg *R, int e, int *ps, int *pr) +{ + int var[RMAXV], nv2[RMAXV], *v = rvals, s = *ps, r = *pr, b = R->entb[e], fp, j, i, t, o; + var[0] = fp = mread(ZFP); + for (j = 1; j < R->nv; j++) var[j] = mread(fp + R->slot[j]); + for (;;) { + RB *B = &R->b[b]; + for (i = 0; i < B->n; i++) { + Uop *u = &B->u[i]; + switch (u->op) { + case U_LI: v[u->d] = u->imm; break; + case U_VGET: v[u->d] = var[u->imm]; break; + case U_SLD: v[u->d] = mread(s + u->imm); break; + case U_LD: v[u->d] = mread(v[u->a]); break; + case U_ST: RSTORE(v[u->a], v[u->b]); break; + default: v[u->d] = ev(u->op, v[u->a], v[u->b], v[u->c]); + } + } + for (j = 0; j < B->nso; j++) { o = s + B->so[2 * j]; if ((unsigned)o < MW) m[o] = v[B->so[2 * j + 1]]; } + s += B->delta; + for (j = 1; j < R->nv; j++) if (B->vs[j] >= 0) nv2[j] = v[B->vs[j]]; + for (j = 1; j < R->nv; j++) if (B->vs[j] >= 0) var[j] = nv2[j]; + if (B->term == T_BR) { b = B->s1; continue; } + if (B->term == T_CBR) { + if (!v[B->cv]) { if (B->s1 >= 0) { b = B->s1; continue; } t = B->x1; } + else { if (B->s2 >= 0) { b = B->s2; continue; } t = B->x2; } + } else if (B->term == T_JMP) t = v[B->cv]; + else if (B->term == T_CALL) { m[r++] = B->x2; t = v[B->cv]; } + else t = m[--r]; + break; + } + for (j = 1; j < R->nv; j++) if ((unsigned)(fp + R->slot[j]) < MW) m[fp + R->slot[j]] = var[j]; + *ps = s; *pr = r; + return t; +} +#ifndef ZYLLVM +static void rforget(Rg *R) { (void)R; } +static void rmark(Rg *R) { int x; for (x = R->lo; x < R->hi; x++) rcov[x] = 1; } +static void rcompile(Rg *R, int eager) { (void)R; (void)eager; } +#endif +#ifdef ZYLLVM + +/* ---- o3 regions in llvm: one function per region, vars in allocas (so + mem2reg makes them ssa across blocks), each block's micro-ops as ir, then + the O3 pipeline. f(mem, regs, entry) returns the next vm address. */ +typedef int (*RFn)(int *mem, int *regs, int ent); +static int nregions; +static LLVMValueRef rvv[RMAXV]; +static void rforget(Rg *R) { R->nat = 0; } +static void rmark(Rg *R) { int x; for (x = R->lo; x < R->hi; x++) rcov[x] = lcov[x] = 1; } +static void rspill_ir(Rg *R) +{ + LLVMValueRef fp = LLVMBuildLoad2(bd, i32t, rvv[0], ""); + int j; + for (j = 1; j < R->nv; j++) st(add(fp, R->slot[j]), LLVMBuildLoad2(bd, i32t, rvv[j], "")); +} +static LLVMBasicBlockRef rexit_bb(Rg *R, int t) +{ + LLVMBasicBlockRef here = LLVMGetInsertBlock(bd), x = newbb(); + LLVMPositionBuilderAtEnd(bd, x); + rspill_ir(R); leave(K32(t)); + LLVMPositionBuilderAtEnd(bd, here); + return x; +} +/* m[a] = v, through lhook if some compiled code covers the word */ +static void rstore_ir(LLVMValueRef a, LLVMValueRef b) +{ + LLVMBasicBlockRef in = newbb(), cv = newbb(), plain = newbb(), out = newbb(); + LLVMValueRef v, args[2]; LLVMTypeRef at[2]; + LLVMBuildCondBr(bd, LLVMBuildICmp(bd, LLVMIntULT, a, K32(MW), ""), in, out); + LLVMPositionBuilderAtEnd(bd, in); + v = LLVMBuildLoad2(bd, i8t, LLVMBuildGEP2(bd, i8t, cptr(lcov), &a, 1, ""), ""); + LLVMBuildCondBr(bd, LLVMBuildICmp(bd, LLVMIntNE, v, LLVMConstInt(i8t, 0, 0), ""), cv, plain); + LLVMPositionBuilderAtEnd(bd, plain); st(a, b); LLVMBuildBr(bd, out); + LLVMPositionBuilderAtEnd(bd, cv); + at[0] = i32t; at[1] = i32t; args[0] = a; args[1] = b; + callp((void *)lhook, i32t, at, 2, args); + LLVMBuildBr(bd, out); + LLVMPositionBuilderAtEnd(bd, out); +} +static void *lregion(Rg *R) +{ + char name[40]; LLVMTypeRef pt[3], ft; LLVMValueRef ent, sw, *val, s0, fp, r; + LLVMBasicBlockRef *bb, eb, dflt; + LLVMOrcExecutorAddress addr; LLVMErrorRef err; LLVMPassBuilderOptionsRef po; + int b, i, j, e; + sprintf(name, "o3_%d_%d", R->lo, nregions++); + mod_ = LLVMModuleCreateWithNameInContext(name, ctx); + pt[0] = ptrt; pt[1] = ptrt; pt[2] = i32t; + ft = LLVMFunctionType(i32t, pt, 3, 0); + fn_ = LLVMAddFunction(mod_, name, ft); + vm_ = LLVMGetParam(fn_, 0); vregs = LLVMGetParam(fn_, 1); ent = LLVMGetParam(fn_, 2); + LLVMPositionBuilderAtEnd(bd, newbb()); + vs = LLVMBuildAlloca(bd, i32t, "s"); vr = LLVMBuildAlloca(bd, i32t, "r"); + for (j = 0; j < R->nv; j++) rvv[j] = LLVMBuildAlloca(bd, i32t, ""); + { LLVMValueRef r0 = K32(0), r1 = K32(1); + sets(LLVMBuildLoad2(bd, i32t, LLVMBuildGEP2(bd, i32t, vregs, &r0, 1, ""), "")); + LLVMBuildStore(bd, LLVMBuildLoad2(bd, i32t, LLVMBuildGEP2(bd, i32t, vregs, &r1, 1, ""), ""), vr); } + bb = malloc(R->nb * sizeof *bb); + for (b = 0; b < R->nb; b++) bb[b] = newbb(); + dflt = newbb(); + sw = LLVMBuildSwitch(bd, ent, dflt, (unsigned)R->ne); + for (e = 0; e < R->ne; e++) { + eb = newbb(); + LLVMAddCase(sw, K32(e), eb); + LLVMPositionBuilderAtEnd(bd, eb); + fp = ld(K32(ZFP)); + LLVMBuildStore(bd, fp, rvv[0]); + for (j = 1; j < R->nv; j++) LLVMBuildStore(bd, safeld(add(fp, R->slot[j])), rvv[j]); + LLVMBuildBr(bd, bb[R->entb[e]]); + } + LLVMPositionBuilderAtEnd(bd, dflt); leave(K32(-1)); + val = calloc(R->maxnv + 1, sizeof *val); + for (b = 0; b < R->nb; b++) { + RB *B = &R->b[b]; + LLVMPositionBuilderAtEnd(bd, bb[b]); + s0 = gets(); + for (i = 0; i < B->n; i++) { + Uop *u = &B->u[i]; + LLVMValueRef a = nargs(u->op) > 0 ? val[u->a] : 0, x = nargs(u->op) > 1 ? val[u->b] : 0, c = nargs(u->op) > 2 ? val[u->c] : 0; + switch (u->op) { + case U_LI: val[u->d] = K32(u->imm); break; + case U_VGET: val[u->d] = LLVMBuildLoad2(bd, i32t, rvv[u->imm], ""); break; + case U_SLD: val[u->d] = ld(add(s0, u->imm)); break; + case U_LD: val[u->d] = safeld(a); break; + case U_ST: rstore_ir(a, x); break; + default: val[u->d] = ir_op(u->op, a, x, c); + } + } + for (j = 0; j < B->nso; j++) st(add(s0, B->so[2 * j]), val[B->so[2 * j + 1]]); + sets(add(s0, B->delta)); + for (j = 1; j < R->nv; j++) if (B->vs[j] >= 0) LLVMBuildStore(bd, val[B->vs[j]], rvv[j]); + switch (B->term) { + case T_BR: LLVMBuildBr(bd, bb[B->s1]); break; + case T_CBR: + LLVMBuildCondBr(bd, LLVMBuildICmp(bd, LLVMIntEQ, val[B->cv], K32(0), ""), + B->s1 >= 0 ? bb[B->s1] : rexit_bb(R, B->x1), B->s2 >= 0 ? bb[B->s2] : rexit_bb(R, B->x2)); + break; + case T_CALL: + r = LLVMBuildLoad2(bd, i32t, vr, ""); st(r, K32(B->x2)); LLVMBuildStore(bd, add(r, 1), vr); + rspill_ir(R); leave(val[B->cv]); + break; + case T_RET: + r = add(LLVMBuildLoad2(bd, i32t, vr, ""), -1); LLVMBuildStore(bd, r, vr); + rspill_ir(R); leave(ld(r)); + break; + default: rspill_ir(R); leave(val[B->cv]); + } + } + free(val); free(bb); + po = LLVMCreatePassBuilderOptions(); + if ((err = LLVMRunPasses(mod_, "default", 0, po))) LLVMConsumeError(err); + LLVMDisposePassBuilderOptions(po); + if ((err = LLVMOrcLLJITAddLLVMIRModule(jit, LLVMOrcLLJITGetMainJITDylib(jit), LLVMOrcCreateNewThreadSafeModule(mod_, tsc)))) { + char *msg = LLVMGetErrorMessage(err); fprintf(stderr, "llvm: %s\n", msg); LLVMDisposeErrorMessage(msg); return 0; + } + if ((err = LLVMOrcLLJITLookup(jit, &addr, name))) { char *msg = LLVMGetErrorMessage(err); fprintf(stderr, "llvm: %s\n", msg); LLVMDisposeErrorMessage(msg); return 0; } + if (trace) fprintf(stderr, "o3: %d..%d compiled by llvm\n", R->lo, R->hi - 1); + return (void *)(size_t)addr; +} +static void rcompile(Rg *R, int eager) +{ + (void)eager; + if (R->nat || R->nonat || !llvm_ok) return; + if (!(R->nat = lregion(R))) R->nonat = 1; +} +#endif +#define LD(x) ((unsigned)(x) < MW ? m[x] : 0) +static int run(int p) +{ + int s = DS0, r = RS0, w, a, b, xfer = 1; + unsigned long long steps = 0; +#ifdef ZYLLVM + int regs[2]; + if (!getenv("ZYNOLLVM")) llvm_init(); +#endif + while ((unsigned)p < MW) { + if (xfer && (s < DSLIM || r > RSLIM)) { p = s < DSLIM ? MW + 1 : MW + 2; break; } + if (xfer && o3ent[p]) { /* an o3 region */ + Rg *R = rat(p); + if (R) { +#ifdef ZYLLVM + rcompile(R, 0); + if (R->nat) { regs[0] = s; regs[1] = r; p = ((RFn)R->nat)(m, regs, o3ei[p]); s = regs[0]; r = regs[1]; } + else +#endif + p = rexec(R, o3ei[p], &s, &r); + continue; + } + } +#ifdef ZYLLVM + if (xfer && llvm_ok) { /* at a block start: run it compiled, or count it */ + xfer = 0; + if (!lt[p] && hot[p] < 65535 && ++hot[p] == HOT && !(lt[p] = lcompile(p))) hot[p] = 65535; + if (lt[p]) { + regs[0] = s; regs[1] = r; + p = lt[p](m, regs); + s = regs[0]; r = regs[1]; + if (need_flush) { need_flush = 0; ldrop_all(); } + xfer = 1; + continue; + } + } +#endif + w = m[p++]; + steps++; + if (w & 1) { m[--s] = w >> 1; continue; } + a = m[s]; b = m[s + 1]; + switch (w / 2) { + case 1: m[s] = LD(a); break; + case 2: + if ((unsigned)a < MW) { +#ifdef ZYLLVM + if (lcov[a]) lhook(a, b); else +#else + if (rcov[a]) { m[a] = b; rdrop(a); } else +#endif + m[a] = b; + } + s += 2; break; + case 3: m[s] = LD(s + 1 + a); break; + case 4: s++; break; + case 5: m[s] = b; m[s + 1] = a; break; + case 6: m[r++] = p; p = a; s++; xfer = 1; break; + case 7: p = a; s++; xfer = 1; break; + case 8: if (!b) { p = a; xfer = 1; } s += 2; break; + case 9: p = m[--r]; xfer = 1; break; + case 10: m[--s] = (signed char)Q(); break; + case 11: m[--s] = J; break; + case 12: G((char)a); s++; break; + case 13: m[++s] = b + a; break; + case 14: m[++s] = (int)((unsigned)b - (unsigned)a); break; + case 15: m[++s] = (int)((unsigned)b * (unsigned)a); break; + case 16: m[++s] = b & a; break; + case 17: m[++s] = b | a; break; + case 18: m[++s] = b ^ a; break; + case 19: m[++s] = b >> (a & 31); break; + case 20: m[++s] = b < a; break; + case 21: + m[s] = install(a); +#ifdef ZYLLVM + if (need_flush) { need_flush = 0; ldrop_all(); } +#else + if (need_flush) { need_flush = 0; rdrop_all(); memset(rcov, 0, sizeof rcov); } +#endif + break; + default: + if (w / 2 >= XOP0 && w / 2 < XOP0 + NENH && enh[w / 2 - XOP0].live) s = (int)(xexec(&enh[w / 2 - XOP0], m + s) - m); + else p = MW; /* halt: unwired opcodes and the reserved negative ones */ + } + } + if (trace) fprintf(stderr, "interp: %llu steps\n", steps); + return stopped(p); +} +#endif + +int main(void) +{ + trace = getenv("ZYTRACE") != 0; + memcpy(m + GORG, genome, sizeof genome); +#ifdef ZYJIT + { + int p = GORG; + union { unsigned *u; int (*f)(long *, void *); } enter; +#ifdef __APPLE__ + code = mmap(0, CODEW * 4, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANON | MAP_JIT, -1, 0); +#else + code = mmap(0, CODEW * 4, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); +#endif + if (code == MAP_FAILED) { perror("mmap"); return 1; } + jitwrite(1); + build_stubs(); + jitwrite(0); + syncicache(code, cp); + S[0] = (long)m; S[1] = DS0; S[2] = RS0; S[3] = (long)tab; S[4] = (long)cov; + S[5] = (long)Q; S[6] = (long)G; S[7] = (long)&J; S[8] = (long)hook; + S[9] = (long)inst_hook; S[10] = (long)xrun; + S[11] = (long)(m + DSLIM); S[12] = (long)(m + RSLIM); + enter.u = x_enter; + while ((unsigned)p < MW) { + void *c = tab[p]; + if (!c) { jitwrite(1); c = translate(p); jitwrite(0); } + p = enter.f(S, c); + if (need_flush) { need_flush = 0; jitwrite(1); flush(); jitwrite(0); } + } + return stopped(p); + } +#else + return run(GORG); +#endif + return 0; +} diff --git a/docs/history/15-o3/code/examples/bf.zc b/docs/history/15-o3/code/examples/bf.zc new file mode 100644 index 0000000..384025a --- /dev/null +++ b/docs/history/15-o3/code/examples/bf.zc @@ -0,0 +1,50 @@ +// a brainfuck compiler. it turns a brainfuck program into zc source, hands +// that to the compiler that's sitting in memory, and runs the result, which on +// arm64 means native code. runs of + - < > fold into one statement each. +// give it a program after a backtick, or it runs the hello world below. +let out[30000], n, depth; +fn emit(s) { let i = 0; while (s[i]) { out[n] = s[i]; n++; i++; } return 0; } +fn emitn(int v) { if (v >= 10) emitn(v / 10); out[n] = '0' + v % 10; n++; return 0; } +// how many times does c repeat from s[i] +int run(s, int i, int c) { int k = 0; while (s[i + k] == c) k++; return k; } + +fn translate(s) +{ + int i = 0, k; + n = 0; depth = 0; + emit("let t[30000], p = 0;\n"); + while (s[i]) { + switch (s[i]) { + case '+', '-': + k = run(s, i, s[i]); + emit("t[p] = (t[p] "); emit(s[i] == '+' ? "+ " : "- "); emitn(k); emit(") & 255;\n"); + i = i + k; continue; + case '>', '<': + k = run(s, i, s[i]); + emit("p "); emit(s[i] == '>' ? "+= " : "-= "); emitn(k); emit(";\n"); + i = i + k; continue; + case '[': emit("while (t[p]) {\n"); depth++; + case ']': emit("}\n"); depth--; + case '.': emit("putc(t[p]);\n"); + case ',': emit("t[p] = eof() ? 0 : getc() & 255;\n"); + } + i++; + } + emit("return 0;\n"); + out[n] = 0; + return depth; +} +let hello = "++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++."; +let prog[20000]; +fn main() +{ + let src = hello, k = 0, lines = 0, i = 0; + while (!eof() && k < 19999) { let c = getc(); if (c != 10) { prog[k] = c; k++; } } + prog[k] = 0; + if (k) src = prog; + if (translate(src)) { puts("unbalanced brackets\n"); return 1; } + while (out[i]) { if (out[i] == 10) lines++; i++; } + puts("compiled to "); putn(lines); puts(" lines of zc, running it:\n"); + evals(out); + return 0; +} diff --git a/docs/history/15-o3/code/examples/dump.zc b/docs/history/15-o3/code/examples/dump.zc new file mode 100644 index 0000000..6082213 --- /dev/null +++ b/docs/history/15-o3/code/examples/dump.zc @@ -0,0 +1,32 @@ +// printing any struct with no printing code: the heap knows each object's +// type, and reflection walks its fields +struct person { *char name; int age; }; +struct item { *char name; int count; *person owner; }; + +fn dump(p) +{ + let t = typeof_obj(p), i = 0; + if (!t) { puts("?"); return 0; } + tprint(t); puts(" { "); + while (i < tnfields(t)) { + let ft = tftype(t, i), v = p[tfoff(t, i)]; + if (i) puts(", "); + puts(tfname(t, i)); puts(": "); + if (tkind(ft) == TY_PTR && telem(ft) == typeof(char)) { putc('"'); puts(v); putc('"'); } + else if (tkind(ft) == TY_PTR) { if (v) dump(v); else puts("null"); } + else putn(v); + i++; + } + puts(" }"); + return 0; +} +fn main() +{ + *person ana = new(person); + ana.name = "ana"; ana.age = 30; + *item lamp = new(item); + lamp.name = "lamp"; lamp.count = 3; lamp.owner = ana; + dump(lamp); putc(10); + free(d*lamp); + return 0; +} diff --git a/docs/history/15-o3/code/examples/echo.zc b/docs/history/15-o3/code/examples/echo.zc new file mode 100644 index 0000000..01f28b0 --- /dev/null +++ b/docs/history/15-o3/code/examples/echo.zc @@ -0,0 +1,2 @@ +// everything after the backtick is left unread, for the program itself +fn main() { while (!eof()) putc(getc()); return 0; } diff --git a/docs/history/15-o3/code/examples/fact.zc b/docs/history/15-o3/code/examples/fact.zc new file mode 100644 index 0000000..2a218b2 --- /dev/null +++ b/docs/history/15-o3/code/examples/fact.zc @@ -0,0 +1,4 @@ +fn fact(n) { if (n < 2) return 1; return n * fact(n - 1); } +// prints in binary, like the old asm example +fn bin(n) { if (n > 1) bin(n >> 1); putc('0' + (n & 1)); return 0; } +fn main() { bin(fact(10)); putc(10); putn(fact(10)); putc(10); return 0; } diff --git a/docs/history/15-o3/code/examples/hello.zc b/docs/history/15-o3/code/examples/hello.zc new file mode 100644 index 0000000..f36a810 --- /dev/null +++ b/docs/history/15-o3/code/examples/hello.zc @@ -0,0 +1 @@ +fn main() { puts("hello, world!\n"); return 0; } diff --git a/docs/history/15-o3/code/examples/lang.zc b/docs/history/15-o3/code/examples/lang.zc new file mode 100644 index 0000000..e4d91c5 --- /dev/null +++ b/docs/history/15-o3/code/examples/lang.zc @@ -0,0 +1,53 @@ +// growing the language from inside a program, while it compiles + +// x |> f calls f(x). on a stack machine that's just "call": the value is +// already on the stack, under f's address. +immediate fn pipe_op() { next(); add_binop("|>", 1, "c"); return 0; } +pipe_op + +// unless (c) stmt +immediate fn unless() +{ + let a; + next(); expect(intern("(")); plainuse(expr()); rval(); expect(intern(")")); + emitseq(useq[0], ulen[0]); // not + a = hole(); op(O_JZ); + substmt(); + setlit(a, here); + return 0; +} +// assert (c): the failure message carries the line it was written on +fn assert_fail(int line) { puts("assertion failed on line "); putn(line); putc(10); halt(); return 0; } +immediate fn assert() +{ + let a, l = tline; + next(); expect(intern("(")); plainuse(expr()); rval(); expect(intern(")")); + emitseq(useq[0], ulen[0]); // not: the jump below skips the failure when it holds + a = hole(); op(O_JZ); + lit(l); callname(intern("assert_fail")); op(O_DROP); + setlit(a, here); + return 0; +} +// times N { body }: the body pasted N times, at compile time +immediate fn times() +{ + let n, b, i = 0; + next(); n = tv; next(); + b = grab(); + while (i < n) { feed(qdup(b)); i++; } + free(d*b); + return 0; +} + +fn double(x) { return x * 2; } +fn inc(x) { return x + 1; } +fn main() +{ + putn(5 |> double |> inc |> double); putc(10); + unless (1 > 2) puts("one isn't bigger than two\n"); + times 3 { putc('*'); } + putc(10); + assert(double(4) == 8); + assert(inc(1) == 3); + return 0; +} diff --git a/docs/history/15-o3/code/examples/life.zc b/docs/history/15-o3/code/examples/life.zc new file mode 100644 index 0000000..a43d660 --- /dev/null +++ b/docs/history/15-o3/code/examples/life.zc @@ -0,0 +1,38 @@ +// conway's life on a 10 x 20 torus, with typed 2d arrays in a struct. +// a glider walks diagonally and wraps around the edges. +const H = 10, W = 20; +struct board { [10][20]int c; }; + +int get(*board b, int y, int x) { return b.c[(y + H) % H][(x + W) % W]; } +fn step(*board from, *board to) +{ + for (int y = 0; y < H; y++) + for (int x = 0; x < W; x++) { + int n = 0; + for (int dy = 0 - 1; dy <= 1; dy++) + for (int dx = 0 - 1; dx <= 1; dx++) + if (dy || dx) n += get(from, y + dy, x + dx); + to.c[y][x] = n == 3 || (n == 2 && from.c[y][x]); + } + return 0; +} +fn show(*board b, int gen) +{ + puts("generation "); putn(gen); putc(10); + for (int y = 0; y < H; y++) { for (int x = 0; x < W; x++) putc(b.c[y][x] ? '#' : '.'); putc(10); } + return 0; +} +fn main() +{ + *board a = new(board), b = new(board); + a.c[0][1] = 1; a.c[1][2] = 1; a.c[2][0] = 1; a.c[2][1] = 1; a.c[2][2] = 1; + // two generations a pass, a -> b -> a: owned boards can't be swapped + // through a temporary, since the analyzer can't follow that + for (int g = 0; g <= 40; g += 2) { + if (g % 20 == 0) show(a, g); + step(a, b); + step(b, a); + } + free(d*a); free(d*b); + return 0; +} diff --git a/docs/history/15-o3/code/examples/lists.zc b/docs/history/15-o3/code/examples/lists.zc new file mode 100644 index 0000000..3cf0206 --- /dev/null +++ b/docs/history/15-o3/code/examples/lists.zc @@ -0,0 +1,34 @@ +// ownership in practice: a list owns its boxes. nothing here calls free; +// everything is released at the end of the block that owns it. +.u box(v) { let b = new(1); b[0] = v; return u*b; } +.u mklist(cap) { let l = new(cap + 1); l[0] = 0; return u*l; } +// item m*arrives (it was given down), and goes d*on into the borrowed list +fn push(.m l, .d item) { l[l[0] + 1] = d*item; l[0] = l[0] + 1; return 0; } +fn show(.m l) +{ + let i = 1; + putc('['); + while (i <= l[0]) { if (i > 1) putc(' '); putn(l[i][0]); i++; } + putc(']'); + return 0; +} +.u squares(n) { + let l = mklist(n), i = 0; + while (i < n) { + let b = box(i * i); + push(l, d*b); + i++; + } + return u*l; +} +fn main() +{ + let before = live_objects(); + { + let l = squares(6); + show(l); putc(10); + puts("live: "); putn(live_objects() - before); putc(10); + } + puts("after the block: "); putn(live_objects() - before); putc(10); + return 0; +} diff --git a/docs/history/15-o3/code/examples/mandel.zc b/docs/history/15-o3/code/examples/mandel.zc new file mode 100644 index 0000000..5810397 --- /dev/null +++ b/docs/history/15-o3/code/examples/mandel.zc @@ -0,0 +1,36 @@ +// the mandelbrot set in 16.16 fixed point. the cpu has no fixed-point +// multiply, so one gets wired in: a 32x32 -> 64 bit multiply, keeping the +// middle 32 bits. the jit inlines it as two native multiplies and a shift. +enhance fmul(a, b) micro + "r2 = mul a b; r3 = mulhs a b" + "; r3 = shl r3 16; r2 = shr r2 16; a = or r3 r2"; + +const ONE = 65536, W = 72, H = 28, MAXIT = 40; +let shades = " .,:;-=+*%#@"; + +int escape(int cx, int cy) { + int x = 0, y = 0, i = 0; + while (i < MAXIT) { + int xx = fmul(x, x), yy = fmul(y, y); + if (xx + yy > 4 * ONE) return i; + y = 2 * fmul(x, y) + cy; + x = xx - yy + cx; + i++; + } + return MAXIT; +} +fn main() +{ + int row = 0; + while (row < H) { + int col = 0, cy = (row * 2400 / (H - 1) - 1200) * ONE / 1000; + while (col < W) { + int cx = (col * 3200 / (W - 1) - 2200) * ONE / 1000, it = escape(cx, cy); + putc(it == MAXIT ? '@' : shades[it * 11 / MAXIT]); + col++; + } + putc(10); + row++; + } + return 0; +} diff --git a/docs/history/15-o3/code/examples/primes.zc b/docs/history/15-o3/code/examples/primes.zc new file mode 100644 index 0000000..26df0b5 --- /dev/null +++ b/docs/history/15-o3/code/examples/primes.zc @@ -0,0 +1,38 @@ +// primes below 60000: an o3 sieve over a fixed array, the primes +// gathered into a growable one, then a few questions asked of them. +// with ZYTRACE=1 the o3 lines show each region being lifted and compiled. + +// marks the composites; f[i] ends up 1 for every prime i +o3 int sieve(fixed []int f) +{ + int n = len(f), c = 0; + for (int i = 2; i < n; i++) f[i] = 1; + for (int i = 2; i * i < n; i++) + if (f[i]) for (int j = i * i; j < n; j += i) f[j] = 0; + for (int i = 2; i < n; i++) c += f[i]; + return c; +} +// the largest gap between neighbours, and where it starts +o3 int gap(fixed []int f, *int at) +{ + int n = len(f), last = 2, best = 0, where = 0; + for (int i = 3; i < n; i++) + if (f[i]) { if (i - last > best) { best = i - last; where = last; } last = i; } + *at = where; + return best; +} +fn main() +{ + fixed []int f = new(fixed []int, 60000); + []int ps = new([]int); + int c = sieve(f), at = 0, twins = 0; + for (int i = 2; i < len(f); i++) if (f[i]) push(ps, i); + o3 { for (int k = 1; k < len(ps); k++) if (ps[k] - ps[k - 1] == 2) twins++; } + puts("primes below 60000: "); putn(c); putc(10); + puts("the last five:"); for (int k = len(ps) - 5; k < len(ps); k++) { putc(' '); putn(ps[k]); } putc(10); + puts("twin pairs: "); putn(twins); putc(10); + int g = gap(f, &at); + puts("widest gap: "); putn(g); puts(", after "); putn(at); putc(10); + free(d*f); free(d*ps); + return 0; +} diff --git a/docs/history/15-o3/code/examples/quine.zc b/docs/history/15-o3/code/examples/quine.zc new file mode 100644 index 0000000..23be8cb --- /dev/null +++ b/docs/history/15-o3/code/examples/quine.zc @@ -0,0 +1 @@ +let s="let s=#;fn main(){let i=0;while(s[i]){if(s[i]==35){putc(34);puts(s);putc(34);}else putc(s[i]);i++;}putc(10);return 0;}";fn main(){let i=0;while(s[i]){if(s[i]==35){putc(34);puts(s);putc(34);}else putc(s[i]);i++;}putc(10);return 0;} diff --git a/docs/history/15-o3/code/examples/repl.zc b/docs/history/15-o3/code/examples/repl.zc new file mode 100644 index 0000000..b7b81b8 --- /dev/null +++ b/docs/history/15-o3/code/examples/repl.zc @@ -0,0 +1,27 @@ +// a repl on the resident compiler: each backtick-separated chunk of input is +// compiled as a function body and run. definitions (fn ...) can be loaded too: +// a chunk that starts with "fn" goes through load instead of eval. +let buf[4000]; +fn readchunk() +{ + let n = 0, c; + while (!eof()) { + c = getc(); + if (c == 96) break; + if (n < 3999) { buf[n] = c; n++; } + } + buf[n] = 0; + return n; +} +fn main() +{ + let q, i; + while (!eof()) { + readchunk(); + q = tokens(buf); + if (q[0] && q[1] == T_ID && q[2] == intern("fn")) load(q); + else eval(q); + free(d*q); + } + return 0; +} diff --git a/docs/history/15-o3/code/examples/selfpatch.zc b/docs/history/15-o3/code/examples/selfpatch.zc new file mode 100644 index 0000000..074ccec --- /dev/null +++ b/docs/history/15-o3/code/examples/selfpatch.zc @@ -0,0 +1,17 @@ +// code is data: find the literal 'A' inside f's compiled words and rewrite it. +// on the jit the store lands on translated code, which gets dropped and +// retranslated on the next call. +fn f() { putc('A'); return 0; } +fn litw(v) { return v + v + 1; } // how the vm encodes a literal push +fn main() +{ + let p = f; + f(); + while (*p != litw('A')) p++; + *p = litw('B'); + f(); + *p = litw('C'); + f(); + putc(10); + return 0; +} diff --git a/docs/history/15-o3/code/examples/staged.zc b/docs/history/15-o3/code/examples/staged.zc new file mode 100644 index 0000000..5f6342e --- /dev/null +++ b/docs/history/15-o3/code/examples/staged.zc @@ -0,0 +1,35 @@ +// staged code: a program writes a specialized function as source, compiles it +// while running, and calls it. here, polynomials with their coefficients baked +// in as constants, so there's no loop and no array left at runtime. +let buf[2000], n; +fn put(s) { let i = 0; while (s[i]) { buf[n] = s[i]; n++; i++; } return 0; } +fn putnum(int v) { if (v >= 10) putnum(v / 10); buf[n] = '0' + v % 10; n++; return 0; } + +// coefficients c[0] + c[1] x + ... in horner form: ((c3 x + c2) x + c1) x + c0 +fn specialize(*int c, int deg) +{ + n = 0; + put("fn poly(x) { return "); + for (int i = 0; i < deg; i++) put("("); + if (c[deg] < 0) { put("0 - "); putnum(0 - c[deg]); } else putnum(c[deg]); + for (int i = deg - 1; i >= 0; i--) { + put(") * x "); + if (c[i] < 0) { put("- "); putnum(0 - c[i]); } else { put("+ "); putnum(c[i]); } + } + put("; }"); + buf[n] = 0; + return buf; +} +fn main() +{ + [4]int c; + c[0] = 7; c[1] = 0 - 3; c[2] = 0; c[3] = 2; // 2x^3 - 3x + 7 + let src = specialize(c, 3), q = tokens(src); + puts(src); putc(10); + load(q); // compiled now, into the running program + free(d*q); + let f = sval[lookups("poly")]; // and found by name in the compiler's symbol table + for (int x = 0 - 2; x <= 3; x++) { putn(f(x)); putc(' '); } + putc(10); + return 0; +} diff --git a/docs/history/15-o3/code/examples/tree.zc b/docs/history/15-o3/code/examples/tree.zc new file mode 100644 index 0000000..90a01d4 --- /dev/null +++ b/docs/history/15-o3/code/examples/tree.zc @@ -0,0 +1,28 @@ +// a binary search tree where every node owns its children. there's no free +// anywhere: when the root's block ends, the whole tree goes with it. +struct node { int v; *node left, right; }; + +*node.u leaf(int v) { *node n = new(node); n.v = v; return u*n; } +// n is borrowed; storing a new leaf into it makes n the leaf's owner +fn insert(*node.m n, int v) +{ + if (v < n.v) { if (n.left) insert(n.left, v); else n.left = leaf(v); } + else { if (n.right) insert(n.right, v); else n.right = leaf(v); } + return 0; +} +fn walk(*node n) { if (!n) return 0; walk(n.left); putn(n.v); putc(' '); walk(n.right); return 0; } +int height(*node n) { if (!n) return 0; int l = height(n.left), r = height(n.right); return 1 + (l > r ? l : r); } + +fn main() +{ + let before = live_objects(); + { + *node root = leaf(50); + int x = 7; + for (int i = 0; i < 30; i++) { x = (x * 37 + 11) % 100; insert(root, x); } + walk(root); putc(10); + puts("height "); putn(height(root)); puts(", live objects "); putn(live_objects() - before); putc(10); + } + puts("after the block: "); putn(live_objects() - before); putc(10); + return 0; +} diff --git a/docs/history/15-o3/code/examples/typed.zc b/docs/history/15-o3/code/examples/typed.zc new file mode 100644 index 0000000..964a150 --- /dev/null +++ b/docs/history/15-o3/code/examples/typed.zc @@ -0,0 +1,44 @@ +// types: structs, a generic container, and looking at it all through reflection +typecheck on; +struct point { int x, y; }; +struct vec { int len, cap; *T items; }; + + *vec.u mkvec(int cap) { + *vec v = new(vec); + v.cap = cap; + v.items = new(T, cap); + return u*v; +} +// x is given away (.d): the vector's memory holds it from now on, and the +// collector looks after it + bool push(*vec v, T.d x) { + if (v.len == v.cap) { free(d*x); return 0; } + v.items[v.len] = d*x; + v.len++; + return 1; +} +fn show(word t) +{ + let i = 0; + tprint(t); puts(" is "); putn(tsize(t)); puts(" words: "); + while (i < tnfields(t)) { if (i) puts(", "); puts(tfname(t, i)); puts(" "); tprint(tftype(t, i)); i++; } + putc(10); + return 0; +} +int main() { + *vec<*point> v = mkvec<*point>(4); + int i = 0; + while (i < 3) { + *point p = new(point); + p.x = i; p.y = i * i; + push(v, d*p); + i++; + } + puts("last: "); putn(v.items[2].y); putc(10); + show(typeof(point)); + show(typeof(*v)); + puts("push is "); tprint(sigof(push)); putc(10); + // generic code is compiled once, so what mkvec stamped is vec + puts("the heap says v is a "); tprint(typeof_obj(v)); puts(", and v.items[0] is a "); tprint(typeof_obj(v.items[0])); putc(10); + return 0; +} diff --git a/docs/history/15-o3/code/examples/wiring.zc b/docs/history/15-o3/code/examples/wiring.zc new file mode 100644 index 0000000..d5e64f4 --- /dev/null +++ b/docs/history/15-o3/code/examples/wiring.zc @@ -0,0 +1,19 @@ +// virtual enhancements: custom opcodes wired into the cpu while compiling. +// the recipes run through the core's optimizer; the jit inlines the result. +enhance popcount(x) micro + "r1 = shr x 1; r1 = and r1 0x55555555; x = sub x r1" + "; r1 = shr x 2; r1 = and r1 0x33333333; x = and x 0x33333333; x = add x r1" + "; r1 = shr x 4; x = add x r1; x = and x 0x0f0f0f0f; x = mul x 0x01010101; x = shr x 24"; +enhance div10(x) micro "x = udiv x 10"; // becomes a multiply-high, no divide +enhance hypot2(a, b) vm { 0 $ * ~ 0 $ * + }; // lifted from vm code +enhance 2 minmax(a, b) micro "r2 = min a b; r1 = max a b; r0 = r2"; +let lo, hi; +fn main() +{ + putn(popcount(0xff00ff)); putc(' '); + putn(div10(12345)); putc(' '); + putn(hypot2(3, 4)); putc(' '); + asm { 9 4 minmax hi ! lo ! } + putn(lo); putc('/'); putn(hi); putc(10); + return 0; +} diff --git a/docs/history/15-o3/code/genome.h b/docs/history/15-o3/code/genome.h new file mode 100644 index 0000000..adaca4a --- /dev/null +++ b/docs/history/15-o3/code/genome.h @@ -0,0 +1,4893 @@ +#define GORG 786432 +static const int genome[] = { +1966081,1310721,4,1717361,14,858905,864649,1310721,2,3,28,1310721,4,1310721,2,1835009, +40,1572913,16,1966081,1310721,4,1579223,12,1310721,2,4,1310721,2,2,42,1572933, +14,1,1310721,2,3,26,1310721,4,18,0,0,2,1,786486,0,786480, +786482,786484,97,0,98,0,97,32,61,32,115,100,105,118,32,97, +32,98,0,1,0,2,1,786512,0,786506,786508,786510,97,0,98,0, +97,32,61,32,115,114,101,109,32,97,32,98,0,2,0,2, +1,786538,0,786532,786534,786536,97,0,98,0,97,32,61,32,115,104, +108,32,97,32,98,0,3,0,1,1,786560,0,786557,786558,107,0, +114,49,32,61,32,108,100,32,54,53,53,51,54,48,59,32, +107,32,61,32,97,100,100,32,114,49,32,107,0,4,0,2, +1,786602,0,786596,786598,786600,105,0,110,0,105,32,61,32,108,116, +117,32,105,32,110,59,32,105,32,61,32,120,111,114,32,105, +32,49,0,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1573289,16,1966081, +1310721,4,1579223,12,1310721,2,4,1310721,2,2,24,1,1573311,14,1,1310721, +2,3,26,1310721,4,18,1310721,2,1,28,1310721,4,1310721,2,1835009,40, +1573359,16,1966081,1310721,4,1579223,12,20,1573367,14,1,1310721,2,1,26,1310721, +4,18,1310721,2,1,28,1310721,4,1310721,2,1835009,40,1573415,16,1966081,1310721, +4,1579223,12,22,1573423,14,1,1310721,2,1,26,1310721,4,18,1310721,2, +1,28,1310721,4,1310721,2,1835009,40,1573471,16,1966081,1310721,4,1579223,12,0, +1,1573481,14,1,1310721,2,1,26,1310721,4,18,1310721,2,5,28,1310721, +4,1310721,2,1835009,40,1573529,16,1966081,1310721,4,1579223,12,1310721,2,4,1, +1310721,2,3,26,4,1310721,2,2,1310721,2,3,26,2,26,2,1573635, +16,1310721,2,2,1310721,2,3,26,2,26,2,1573255,12,8,1310721,2, +3,26,1,6,2,1,6,3,26,5,6,4,10,8,8,1573547, +14,1,1573643,14,1,1310721,2,5,26,1310721,4,18,1310721,2,29,28, +1310721,4,1310721,2,1835009,40,1573691,16,1966081,1310721,4,1579223,12,1310721,2,4, +1,1310721,2,27,26,4,1310721,2,2,1,40,1573757,16,91,1573255,12, +8,1310721,2,1,1310721,2,2,28,10,3,6,10,4,8,1310721,2, +2,1,40,1573811,16,1573797,14,50,49,52,55,52,56,51,54,52, +56,0,1573775,1573495,12,8,1,1574025,14,3,1573949,16,1310721,2,3,26, +1310721,2,27,26,1,6,2,1,6,3,26,5,6,4,10,8, +26,97,1310721,2,2,21,46,26,10,3,6,10,4,8,1310721,2, +1310721,2,2,21,44,10,3,6,10,4,8,1310721,2,2,1,6, +1,10,28,34,63,38,3,26,1573945,16,1573949,14,1573811,14,1310721,2, +27,26,2,1574017,16,1310721,2,3,26,1310721,2,27,26,1,6,2, +-1,26,1,6,5,6,4,10,8,26,2,1573255,12,8,1573949,14, +1,1574025,14,1,1310721,2,29,26,1310721,4,18,1310721,2,5,28,1310721, +4,1310721,2,1835009,40,1574073,16,1966081,1310721,4,1579223,12,1310721,2,4,1, +1310721,2,3,26,4,1310721,2,2,1310721,2,3,26,2,26,2,1574153, +16,1310721,2,3,26,1,6,2,1,6,3,26,5,6,4,10, +8,8,1574091,14,1310721,2,3,26,2,1574169,14,1,1310721,2,5,26, +1310721,4,18,1310721,2,7,28,1310721,4,1310721,2,1835009,40,1574217,16,1966081, +1310721,4,1579223,12,1310721,2,3,26,4,1310721,2,4,1,1310721,2,5, +26,4,1310721,2,2,1310721,2,5,26,2,26,2,1574361,16,1310721,2, +2,1310721,2,5,26,2,26,2,1310721,2,3,26,2,1310721,2,5, +26,2,26,2,28,1,6,1,10,28,34,63,38,3,26,1, +6,1,10,28,34,63,38,1,10,28,1574363,14,1,1574405,16,1310721, +2,5,26,1,6,2,1,6,3,26,5,6,4,10,8,8, +1574245,14,1310721,2,2,1310721,2,5,26,2,26,2,1310721,2,3,26, +2,1310721,2,5,26,2,26,2,28,1,6,1,10,28,34,63, +38,3,26,1574477,14,1,1310721,2,7,26,1310721,4,18,1310721,2,9, +28,1310721,4,1310721,2,1835009,40,1574525,16,1966081,1310721,4,1579223,12,1310721,2, +5,26,4,1310721,2,3,26,4,1310721,2,4,1,1310721,2,7,26, +4,1310721,2,7,26,2,1310721,2,5,26,2,40,1574681,16,1310721,2, +2,1310721,2,7,26,2,26,1310721,2,3,26,2,1310721,2,7,26, +2,26,2,10,3,6,10,4,8,1310721,2,7,26,1,6,2, +1,6,3,26,5,6,4,10,8,8,1574563,14,1,1574689,14,1, +1310721,2,9,26,1310721,4,18,1310721,2,9,28,1310721,4,1310721,2,1835009, +40,1574737,16,1966081,1310721,4,1579223,12,1310721,2,5,26,4,1310721,2,3, +26,4,1310721,2,4,1,1310721,2,7,26,4,1310721,2,7,26,2, +1310721,2,5,26,2,40,1574879,16,1310721,2,2,1310721,2,7,26,2, +26,1310721,2,3,26,2,10,3,6,10,4,8,1310721,2,7,26, +1,6,2,1,6,3,26,5,6,4,10,8,8,1574775,14,1, +1574887,14,1,1310721,2,9,26,1310721,4,18,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,1310721,2,11,28,1310721, +4,1310721,2,1835009,40,1575001,16,1966081,1310721,4,1579223,12,1310721,2,3,26, +4,1310721,2,4,1,1310721,2,5,26,4,1,1310721,2,7,26,4, +61,1310721,2,9,26,4,1310721,2,2,1310721,2,3,26,2,40,1575101, +16,1574965,1310721,2,2,10,3,6,10,4,8,1,1575415,14,1310721,2, +9,26,2,1,40,3,36,1575375,16,1310721,2,7,26,1310721,2,7, +26,2,1310721,2,7,26,2,26,1310721,2,2,1310721,2,9,26,2, +38,3,32,26,10,3,6,10,4,8,1310721,2,5,26,1310721,2, +5,26,2,1310721,2,5,26,2,26,10,3,6,10,4,8,1310721, +2,7,26,2,1310721,2,3,26,2,40,3,36,1575337,16,1310721,2, +7,26,1310721,2,7,26,2,1310721,2,3,26,2,28,10,3,6, +10,4,8,1310721,2,5,26,1,6,2,1,6,3,26,5,6, +4,10,8,8,1310721,2,9,26,1,6,2,1,6,-1,26,5, +6,4,10,8,8,1575101,14,1574965,1310721,2,7,26,2,10,3,6, +10,4,8,1310721,2,5,26,2,1575415,14,1,1310721,2,11,26,1310721, +4,18,1310721,2,9,28,1310721,4,1310721,2,1835009,40,1575463,16,1966081,1310721, +4,1579223,12,1310721,2,3,26,4,1310721,2,4,1,1310721,2,5,26, +4,1310721,2,3,26,2,1,28,1,6,1,10,28,34,63,38, +3,26,1575555,16,1574965,1310721,2,2,10,3,6,10,4,8,1,1575791, +14,1310721,2,2,1,40,1575617,16,1310721,2,1,1310721,2,2,28,10, +3,6,10,4,8,1310721,2,5,26,3,10,3,6,10,4,8, +1310721,2,3,26,2,1,40,1575703,16,1310721,2,3,26,1,1310721,2, +3,26,2,28,10,3,6,10,4,8,1310721,2,5,26,1310721,2, +5,26,2,3,36,10,3,6,10,4,8,1310721,2,7,26,1310721, +2,2,1310721,2,3,26,2,1574967,12,10,3,6,10,4,8,1310721, +2,5,26,2,1575775,16,1,1310721,2,7,26,2,28,1575791,14,1310721, +2,7,26,2,1575791,14,1,1310721,2,9,26,1310721,4,18,1310721,2, +7,28,1310721,4,1310721,2,1835009,40,1575839,16,1966081,1310721,4,1579223,12,1310721, +2,3,26,4,1310721,2,4,1310721,2,2,1,40,1310721,2,5,26, +4,1310721,2,3,26,2,1,28,1,6,1,10,28,34,63,38, +3,26,1575923,16,1310721,2,2,1575981,14,1310721,2,2,1310721,2,3,26, +2,1575429,12,8,1310721,2,5,26,2,1575971,16,1,1574965,2,28,1575981, +14,1574965,2,1575981,14,1,1310721,2,7,26,1310721,4,18,1310721,2,5, +28,1310721,4,1310721,2,1835009,40,1576029,16,1966081,1310721,4,1579223,12,1310721,2, +3,26,4,1310721,2,4,1310721,2,2,1574901,1310721,2,3,26,2,63, +32,26,2,30,1576079,14,1,1310721,2,5,26,1310721,4,18,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,1,1310721,2,9,28,1310721,4,1310721,2,1835009,40,1576199,16,1966081,1310721, +4,1579223,12,1310721,2,4,1310721,524289,28,1310721,2,3,26,4,1,1310721, +2,5,26,4,1310721,2,3,26,2,1576495,16,1310721,2,7,26,3, +10,3,6,10,4,8,1310721,2,7,26,2,1310721,2,7,26,2, +26,1310721,2,3,26,2,10,40,3,36,1576359,16,1310721,2,7,26, +1310721,2,7,26,2,1310721,2,7,26,2,26,10,3,6,10,4, +8,1576269,14,1310721,2,2,1310721,2,5,26,2,1310721,2,7,26,2, +26,40,1576407,16,1310721,2,7,26,2,1576503,14,1310721,2,5,26,1310721, +2,5,26,2,1310721,2,7,26,2,26,10,3,6,10,4,8, +1310721,2,3,26,1310721,2,3,26,2,1310721,2,7,26,2,28,10, +3,6,10,4,8,1576233,14,1,1576503,14,1,1310721,2,9,26,1310721, +4,18,1310721,2,5,28,1310721,4,1310721,2,1835009,40,1576551,16,1966081,1310721, +4,1579223,12,1310721,2,3,26,4,1310721,2,4,1310721,2,2,5,26, +1,10,3,6,10,4,8,1310721,2,2,13,26,1576095,1310721,2,3, +26,2,26,2,10,3,6,10,4,8,1310721,2,2,15,26,1, +10,3,6,10,4,8,1576095,1310721,2,3,26,2,26,2,1576711,16, +1576095,1310721,2,3,26,2,26,2,15,26,1310721,2,2,10,3,6, +10,4,8,1576095,1310721,2,3,26,2,26,1310721,2,2,10,3,6, +10,4,8,1,1576751,14,1,1310721,2,5,26,1310721,4,18,1310721,2, +5,28,1310721,4,1310721,2,1835009,40,1576799,16,1966081,1310721,4,1579223,12,1310721, +2,3,26,4,1310721,2,4,1310721,2,2,15,26,2,1576875,16,1310721, +2,2,15,26,2,13,26,1310721,2,2,13,26,2,10,3,6, +10,4,8,1576913,14,1576095,1310721,2,3,26,2,26,1310721,2,2,13, +26,2,10,3,6,10,4,8,1310721,2,2,13,26,2,1576969,16, +1310721,2,2,13,26,2,15,26,1310721,2,2,15,26,2,10,3, +6,10,4,8,1,1576977,14,1,1310721,2,5,26,1310721,4,18,1310721, +2,5,28,1310721,4,1310721,2,1835009,40,1577025,16,1966081,1310721,4,1579223,12, +1310721,2,3,26,4,1310721,2,4,1310721,2,2,1,26,1574901,1310721,2, +3,26,2,26,2,10,3,6,10,4,8,1310721,2,2,3,26, +1310721,2,3,26,2,10,3,6,10,4,8,1310721,2,2,5,26, +1,10,3,6,10,4,8,1310721,2,2,7,26,1,10,3,6, +10,4,8,1310721,2,2,9,26,1,10,3,6,10,4,8,1310721, +2,2,11,26,1,10,3,6,10,4,8,1310721,2,2,1577219,14, +1,1310721,2,5,26,1310721,4,18,1310721,2,9,28,1310721,4,1310721,2, +1835009,40,1577267,16,1966081,1310721,4,1579223,12,3,1310721,2,4,524289,1310721,2, +3,26,4,1574901,1,26,3,10,3,6,10,4,8,1310721,2,2, +65,40,1577411,16,1574901,1310721,2,2,26,1574901,1310721,2,2,3,28,26, +2,1574901,1310721,2,2,3,28,26,2,26,10,3,6,10,4,8, +1310721,2,1,6,2,1,6,3,26,5,6,4,10,8,8,1577307, +14,1576093,1310721,10,3,6,10,4,8,1576095,1,65,1574703,12,8,1310721, +2,3,26,2,1310721,40,1577673,16,1310721,2,5,26,1310721,2,3,26, +2,524289,28,1576165,12,10,3,6,10,4,8,1310721,2,7,26,1, +10,3,6,10,4,8,1574901,1310721,2,7,26,2,26,2,1310721,2, +5,26,2,40,1577587,16,1310721,2,7,26,1,6,2,1,6,3, +26,5,6,4,10,8,8,1577517,14,1310721,2,3,26,2,1310721,2, +7,26,2,1576991,12,1310721,2,7,26,2,1576517,12,8,1310721,2,3, +26,1310721,2,3,26,2,1310721,2,5,26,2,26,10,3,6,10, +4,8,1577439,14,1,1577681,14,1,1310721,2,9,26,1310721,4,18,1310721, +2,9,28,1310721,4,1310721,2,1835009,40,1577729,16,1966081,1310721,4,1579223,12, +1310721,2,4,1310721,2,2,1310721,2,3,26,4,1310721,2,3,26,2, +65,40,1577831,16,1576095,1310721,2,3,26,2,26,2,1,6,1,10, +28,34,63,38,3,26,1,6,1,10,28,34,63,38,1,10, +28,1577833,14,1,1577875,16,1310721,2,3,26,1,6,2,1,6,3, +26,5,6,4,10,8,8,1577751,14,1310721,2,3,26,2,65,40, +3,36,1577903,16,1,1578153,14,1310721,2,5,26,1576095,1310721,2,3,26, +2,26,2,10,3,6,10,4,8,1310721,2,5,26,2,1310721,2, +3,26,2,1576765,12,8,1310721,2,3,26,2,1310721,2,2,10,40, +1578115,16,1310721,2,3,26,1,6,2,1,6,-1,26,5,6,4, +10,8,8,1310721,2,7,26,1310721,2,5,26,2,1574901,1310721,2,3, +26,2,26,2,26,10,3,6,10,4,8,1310721,2,7,26,2, +1310721,2,3,26,2,1576991,12,1310721,2,3,26,2,1576517,12,8,1577965, +14,1310721,2,5,26,2,1310721,2,2,1576991,12,8,1310721,2,5,26, +2,1578153,14,1,1310721,2,9,26,1310721,4,18,0,1310721,2,11,28, +1310721,4,1310721,2,1835009,40,1578203,16,1966081,1310721,4,1579223,12,1310721,2,4, +1310721,2,2,3,26,2,1310721,2,3,26,4,1574901,1310721,2,3,26, +2,26,2,1310721,2,5,26,4,3,1578875,16,1310721,2,7,26,1310721, +2,2,524289,28,10,3,6,10,4,8,1310721,2,5,26,2,1310721, +2,7,26,2,1576165,12,40,3,36,1578331,16,1578875,14,1310721,2,9, +26,524289,1310721,2,7,26,2,1310721,2,5,26,2,36,26,10,3, +6,10,4,8,1310721,2,9,26,2,5,26,2,1,28,1,6, +1,10,28,34,63,38,1,10,28,1578429,16,3,1578501,14,1310721,2, +9,26,2,3,26,2,1310721,2,3,26,2,28,1,6,1,10, +28,34,63,38,1,10,28,1,6,1,10,28,34,63,38,1, +10,28,1578511,16,3,1578583,14,1310721,2,9,26,2,1,26,2,1310721, +2,5,26,2,28,1,6,1,10,28,34,63,38,1,10,28, +1,6,1,10,28,34,63,38,1,10,28,1578591,16,1578875,14,1310721, +2,9,26,2,1310721,2,3,26,2,1576765,12,8,1310721,2,9,26, +2,1310721,2,2,40,1578717,16,1310721,2,2,1,26,1,10,3,6, +10,4,8,1310721,2,2,7,26,1,10,3,6,10,4,8,1310721, +2,1310721,2,9,26,2,10,3,6,10,4,8,1578773,14,1310721,2, +9,26,2,1,26,1,10,3,6,10,4,8,1310721,2,9,26, +2,7,26,1,10,3,6,10,4,8,1310721,2,3,26,1,6, +2,1,6,3,26,5,6,4,10,8,8,1310721,2,5,26,1310721, +2,5,26,2,1310721,2,5,26,2,26,10,3,6,10,4,8, +1310721,2,2,1310721,2,3,26,2,1576991,12,8,1578257,14,1310721,2,2, +1310721,2,3,26,2,1576991,12,1310721,2,3,26,2,1576517,12,8,1578167, +1310721,2,2,1310721,2,5,26,2,26,10,3,6,10,4,8,1, +1578951,14,1,1310721,2,11,26,1310721,4,18,1310721,2,3,28,1310721,4, +1310721,2,1835009,40,1578999,16,1966081,1310721,4,1579223,12,1310721,2,4,1310721,2, +2,524289,13,26,40,3,36,1579065,16,1310721,2,2,1576093,2,40,1, +6,1,10,28,34,63,38,1,10,28,1579067,14,1,1579147,16,1310721, +2,2,7,1,10,28,26,2,1310721,2,2,1459366445,36,28,1,6, +1,10,28,34,63,38,3,26,1,6,1,10,28,34,63,38, +1,10,28,1579149,14,1,1579201,16,1310721,2,2,9,1,10,28,26, +2,3,32,1,6,1,10,28,34,63,38,1,10,28,1579203,14, +1,1579209,14,1,1310721,2,3,26,1310721,4,18,1310721,2,1,28,1310721, +4,1310721,2,1835009,40,1579257,16,1966081,1310721,4,1579223,12,1579313,14,122,121, +58,32,102,114,97,109,101,32,115,116,97,99,107,32,111,118, +101,114,102,108,111,119,10,0,1579261,1573495,12,8,1573437,12,8,1, +1579335,14,1,1310721,2,1,26,1310721,4,18,1310721,2,1,28,1310721,4, +1310721,2,1835009,40,1579383,16,1966081,1310721,4,1579223,12,1579435,14,122,99,58, +32,111,117,116,32,111,102,32,104,101,97,112,32,109,101,109, +111,114,121,10,0,1579387,1573495,12,8,1573437,12,8,1,1579457,14,1, +1310721,2,1,26,1310721,4,18,1310721,2,11,28,1310721,4,1310721,2,1835009, +40,1579505,16,1966081,1310721,4,1579223,12,1310721,2,4,1310721,2,2,13,26, +1310721,2,3,26,4,7,1310721,2,5,26,4,1310721,2,2,1,40, +1579579,16,1310721,2,3,26,13,10,3,6,10,4,8,1574901,1310721,2, +5,26,2,26,2,1310721,2,3,26,2,40,1579649,16,1310721,2,5, +26,1,6,2,1,6,3,26,5,6,4,10,8,8,1579579,14, +1310721,2,7,26,1310721,2,5,26,2,1577695,12,10,3,6,10,4, +8,1310721,2,7,26,2,1,6,1,10,28,34,63,38,3,26, +1579747,16,1576163,2,1,6,1,10,28,34,63,38,1,10,28,1579749, +14,1,1579793,16,1584091,12,8,1310721,2,7,26,1310721,2,5,26,2, +1577695,12,10,3,6,10,4,8,1310721,2,7,26,2,1,6,1, +10,28,34,63,38,3,26,1579833,16,1579349,12,8,1310721,2,9,26, +1310721,2,7,26,2,13,26,10,3,6,10,4,8,1310721,2,7, +26,2,5,26,3,10,3,6,10,4,8,1310721,2,7,26,2, +7,26,1310721,2,9,26,2,1459366445,36,10,3,6,10,4,8,1310721, +2,7,26,2,9,26,1,10,3,6,10,4,8,1310721,2,7, +26,2,11,26,1,10,3,6,10,4,8,1310721,2,9,26,2, +1580007,14,1,1310721,2,11,26,1310721,4,18,1310721,2,7,28,1310721,4, +1310721,2,1835009,40,1580055,16,1966081,1310721,4,1579223,12,1310721,2,3,26,4, +1310721,2,4,1310721,2,2,1579471,12,1310721,2,5,26,4,1310721,2,5, +26,2,3,1,10,28,26,1310721,2,3,26,2,10,3,6,10, +4,8,1310721,2,5,26,2,1,1310721,2,5,26,2,13,1,10, +28,26,2,13,28,1574703,12,8,1310721,2,5,26,2,1580193,14,1, +1310721,2,7,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009, +40,1580241,16,1966081,1310721,4,1579223,12,1310721,2,4,1310721,2,2,1,1580021, +12,1580265,14,1,1310721,2,3,26,1310721,4,18,1310721,2,3,28,1310721, +4,1310721,2,1835009,40,1580313,16,1966081,1310721,4,1579223,12,1310721,2,4,1310721, +2,2,1578965,12,1,6,1,10,28,34,63,38,3,26,1580359,16, +1,1580387,14,1310721,2,2,13,1,10,28,26,2,13,28,1580387,14, +1,1310721,2,3,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2, +1835009,40,1580435,16,1966081,1310721,4,1579223,12,1310721,2,4,1310721,2,2,1578965, +12,1,6,1,10,28,34,63,38,3,26,1580481,16,1,1580505,14, +1310721,2,2,3,1,10,28,26,2,1580505,14,1,1310721,2,3,26, +1310721,4,18,1310721,2,11,28,1310721,4,1310721,2,1835009,40,1580553,16,1966081, +1310721,4,1579223,12,1310721,2,4,1310721,2,2,13,28,1310721,2,3,26, +4,1310721,2,3,26,2,1,26,2,13,28,1310721,2,5,26,4, +1,1310721,2,7,26,4,1310721,2,3,26,2,5,26,5,10,3, +6,10,4,8,1310721,2,7,26,2,1310721,2,5,26,2,40,1580869, +16,1310721,2,9,26,1310721,2,2,1310721,2,7,26,2,26,2,10, +3,6,10,4,8,1310721,2,9,26,2,1578965,12,1580809,16,1310721,2, +9,26,2,5,1,10,28,26,2,1310721,2,2,28,1,6,1, +10,28,34,63,38,3,26,1,6,1,10,28,34,63,38,1, +10,28,1580811,14,1,1580831,16,1310721,2,9,26,2,1580519,12,8,1310721, +2,7,26,1,6,2,1,6,3,26,5,6,4,10,8,8, +1580649,14,1310721,2,3,26,2,1578169,12,8,1,1580893,14,1,1310721,2, +11,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1580941, +16,1966081,1310721,4,1579223,12,1310721,2,4,1310721,2,2,1578965,12,1580973,16, +1310721,2,2,1580519,12,8,1,1580981,14,1,1310721,2,3,26,1310721,4, +18,1310721,2,5,28,1310721,4,1310721,2,1835009,40,1581029,16,1966081,1310721,4, +1579223,12,1310721,2,3,26,4,1310721,2,4,1310721,2,2,1578965,12,1581097, +16,1310721,2,2,5,1,10,28,26,1310721,2,3,26,2,10,3, +6,10,4,8,1310721,2,2,1581109,14,1,1310721,2,5,26,1310721,4, +18,1310721,2,13,28,1310721,4,1310721,2,1835009,40,1581157,16,1966081,1310721,4, +1579223,12,1310721,2,5,26,4,1310721,2,3,26,4,1310721,2,4,9, +1310721,2,5,26,2,1580021,12,1310721,2,7,26,4,1310721,2,3,26, +2,1310721,2,9,26,4,1310721,2,9,26,2,9,40,1581269,16,1310721, +2,9,26,9,10,3,6,10,4,8,1310721,2,11,26,1310721,2, +9,26,2,1310721,2,2,30,1580207,12,10,3,6,10,4,8,1310721, +2,7,26,2,1,26,1310721,2,3,26,2,10,3,6,10,4, +8,1310721,2,7,26,2,3,26,1310721,2,9,26,2,10,3,6, +10,4,8,1310721,2,7,26,2,5,26,1310721,2,11,26,2,1310721, +2,7,26,2,1580995,12,10,3,6,10,4,8,1310721,2,7,26, +2,7,26,1310721,2,2,10,3,6,10,4,8,1310721,2,7,26, +2,1581481,14,1,1310721,2,13,26,1310721,4,18,1310721,2,17,28,1310721, +4,1310721,2,1835009,40,1581529,16,1966081,1310721,4,1579223,12,1310721,2,3,26, +4,1310721,2,4,1310721,2,2,3,26,2,1310721,2,5,26,4,1310721, +2,2,5,26,2,1310721,2,9,26,4,1,1310721,2,11,26,4, +1310721,2,3,26,2,1310721,2,5,26,2,10,40,3,36,1581639,16, +1,1582193,14,1310721,2,5,26,2,1310721,2,3,26,2,40,1581711,16, +1310721,2,5,26,1310721,2,5,26,2,1310721,2,5,26,2,26,10, +3,6,10,4,8,1581639,14,1310721,2,7,26,1310721,2,5,26,2, +1310721,2,2,7,26,2,30,1580207,12,10,3,6,10,4,8,1310721, +2,15,26,1310721,2,2,1,26,2,1310721,2,2,7,26,2,30, +10,3,6,10,4,8,1310721,2,7,26,2,1310721,2,9,26,2, +1310721,2,15,26,2,1574491,12,8,1310721,2,11,26,2,1310721,2,15, +26,2,40,1582095,16,1310721,2,13,26,1310721,2,7,26,2,1310721,2, +11,26,2,26,2,10,3,6,10,4,8,1310721,2,13,26,2, +1578965,12,1582009,16,1310721,2,13,26,2,5,1,10,28,26,2,1310721, +2,9,26,2,28,1,6,1,10,28,34,63,38,3,26,1, +6,1,10,28,34,63,38,1,10,28,1582011,14,1,1582057,16,1310721, +2,13,26,2,5,1,10,28,26,1310721,2,7,26,2,10,3, +6,10,4,8,1310721,2,11,26,1,6,2,1,6,3,26,5, +6,4,10,8,8,1581841,14,1310721,2,2,5,26,1310721,2,7,26, +2,1310721,2,2,1580995,12,10,3,6,10,4,8,1310721,2,2,3, +26,1310721,2,5,26,2,10,3,6,10,4,8,1310721,2,9,26, +2,1580907,12,8,1,1582193,14,1,1310721,2,17,26,1310721,4,18,1310721, +2,5,28,1310721,4,1310721,2,1835009,40,1582241,16,1966081,1310721,4,1579223,12, +1310721,2,3,26,4,1310721,2,4,1582283,14,122,121,58,32,105,110, +100,101,120,32,0,1582261,1573495,12,8,1310721,2,2,1573657,12,8,1582353, +14,32,111,117,116,32,111,102,32,114,97,110,103,101,32,40, +108,101,110,103,116,104,32,0,1582307,1573495,12,8,1310721,2,3,26, +2,1573657,12,8,1582387,14,41,10,0,1582381,1573495,12,8,1573437,12,8, +1,1582409,14,1,1310721,2,5,26,1310721,4,18,1310721,2,5,28,1310721, +4,1310721,2,1835009,40,1582457,16,1966081,1310721,4,1579223,12,1310721,2,3,26, +4,1310721,2,4,1310721,2,3,26,2,1,40,1582497,16,3,1582547,14, +1310721,2,3,26,2,1310721,2,2,1,26,2,40,3,36,1,6, +1,10,28,34,63,38,1,10,28,1582579,16,1310721,2,3,26,2, +1310721,2,2,1,26,2,1582207,12,8,1310721,2,2,5,26,2,1310721, +2,3,26,2,1310721,2,2,7,26,2,30,26,1582623,14,1,1310721, +2,5,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,1835009,40, +1582671,16,1966081,1310721,4,1579223,12,1310721,2,3,26,4,1310721,2,4,1310721, +2,2,1310721,2,2,1,26,2,3,26,1581495,12,8,1310721,2,2, +5,26,2,1310721,2,2,1,26,2,26,1310721,2,3,26,2,10, +3,6,10,4,8,1310721,2,3,26,2,1310721,2,2,5,26,2, +1580995,12,8,1310721,2,2,1,26,1310721,2,2,1,26,2,3,26, +10,3,6,10,4,8,1,1582837,14,1,1310721,2,5,26,1310721,4, +18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1582885,16,1966081,1310721,4, +1579223,12,1310721,2,4,1310721,2,2,1,26,2,1,6,1,10,28, +34,63,38,3,26,1582943,16,3,1,10,28,1,1582207,12,8,1310721, +2,2,1,26,1310721,2,2,1,26,2,3,28,10,3,6,10, +4,8,1310721,2,2,5,26,2,1310721,2,2,1,26,2,26,2, +1583015,14,1,1310721,2,3,26,1310721,4,18,1310721,2,7,28,1310721,4, +1310721,2,1835009,40,1583063,16,1966081,1310721,4,1579223,12,1310721,2,3,26,4, +1310721,2,4,1310721,2,2,1,26,2,1310721,2,2,7,26,2,30, +1310721,2,5,26,4,1310721,2,3,26,2,1,40,1583155,16,1310721,2, +3,26,1,10,3,6,10,4,8,1310721,2,2,1310721,2,3,26, +2,1581495,12,8,1310721,2,3,26,2,1310721,2,2,7,26,2,30, +1310721,2,5,26,2,10,40,1583287,16,1310721,2,2,5,26,2,1310721, +2,5,26,2,26,1,1310721,2,3,26,2,1310721,2,2,7,26, +2,30,1310721,2,5,26,2,28,1574703,12,8,1310721,2,2,1,26, +1310721,2,3,26,2,10,3,6,10,4,8,1,1583327,14,1,1310721, +2,7,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40, +1583375,16,1966081,1310721,4,1579223,12,1310721,2,4,1310721,2,2,1,26,2, +1583399,14,1,1310721,2,3,26,1310721,4,18,1310721,2,3,28,1310721,4, +1310721,2,1835009,40,1583447,16,1966081,1310721,4,1579223,12,1310721,2,4,1310721,2, +2,3,26,2,1583471,14,1,1310721,2,3,26,1310721,4,18,0,0, +0,0,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1583527,16,1966081,1310721, +4,1579223,12,1310721,2,4,1310721,2,2,1578965,12,1,6,1,10,28, +34,63,38,3,26,1583573,16,3,1583617,14,1310721,2,2,9,1,10, +28,26,2,5,32,1,6,1,10,28,34,63,38,1,10,28, +1583627,16,1,1583771,14,1310721,2,2,9,1,10,28,26,1310721,2,2, +9,1,10,28,26,2,5,34,10,3,6,10,4,8,1583485,2, +32769,40,1583747,16,2031617,1583485,2,26,1310721,2,2,10,3,6,10,4, +8,1583485,1,6,2,1,6,3,26,5,6,4,10,8,8,1583763, +14,1583487,3,10,3,6,10,4,8,1,1583771,14,1,1310721,2,3, +26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,1835009,40,1583819,16, +1966081,1310721,4,1579223,12,1310721,2,3,26,4,1310721,2,4,1310721,2,2, +1310721,2,3,26,2,40,1583905,16,1310721,2,2,2,1583493,12,8,1310721, +2,1,6,2,1,6,3,26,5,6,4,10,8,8,1583835,14, +1,1583913,14,1,1310721,2,5,26,1310721,4,18,1310721,2,3,28,1310721, +4,1310721,2,1835009,40,1583961,16,1966081,1310721,4,1579223,12,1583485,2,1584069,16, +1583485,1,6,2,1,6,-1,26,5,6,4,10,8,8,1310721,2, +2031617,1583485,2,26,2,10,3,6,10,4,8,1310721,2,2,1310721,2, +2,1310721,2,2,13,1,10,28,26,2,26,13,28,1583785,12,8, +1583961,14,1,1584077,14,1,1310721,2,3,26,1310721,4,18,1310721,2,7, +28,1310721,4,1310721,2,1835009,40,1584125,16,1966081,1310721,4,1579223,12,1,1310721, +2,5,26,4,1583485,1,10,3,6,10,4,8,1583487,1,10,3, +6,10,4,8,1714063,12,8,1583927,12,8,1583487,2,1584363,16,1583487,1, +10,3,6,10,4,8,1310721,2,524289,10,3,6,10,4,8,1310721, +2,2,1310721,40,1584359,16,1310721,2,2,5,26,2,7,28,1,6, +1,10,28,34,63,38,3,26,1584313,16,1310721,2,2,13,26,1310721, +2,2,1310721,2,2,1,26,2,26,1583785,12,8,1310721,2,1310721,2, +2,1310721,2,2,1,26,2,26,10,3,6,10,4,8,1583927,12, +8,1584223,14,1584181,14,1310721,2,524289,10,3,6,10,4,8,1310721,2, +2,1310721,40,1584639,16,1310721,2,3,26,1310721,2,2,1,26,2,10, +3,6,10,4,8,1310721,2,2,5,26,2,3,28,1,6,1, +10,28,34,63,38,3,26,1584537,16,1310721,2,2,1578169,12,8,1310721, +2,5,26,1,6,2,1,6,3,26,5,6,4,10,8,8, +1310721,2,1578167,2,10,3,6,10,4,8,1584635,14,1310721,2,2,5, +26,2,7,28,1,6,1,10,28,34,63,38,3,26,1584601,16, +1310721,2,2,5,26,3,10,3,6,10,4,8,1310721,2,1310721,2, +2,1310721,2,3,26,2,26,10,3,6,10,4,8,1584381,14,1576159, +1,6,2,1,6,3,26,5,6,4,10,8,8,1576161,1576161,2, +1310721,2,5,26,2,26,10,3,6,10,4,8,1310721,2,5,26, +2,1584713,14,1,1310721,2,7,26,1310721,4,18,1310721,2,5,28,1310721, +4,1310721,2,1835009,40,1584761,16,1966081,1310721,4,1579223,12,524289,1310721,2,4, +1,1310721,2,3,26,4,1310721,2,2,1310721,40,1584889,16,1310721,2,2, +5,26,2,3,32,1584849,16,1310721,2,3,26,1,6,2,1,6, +3,26,5,6,4,10,8,8,1310721,2,1310721,2,2,1310721,2,2, +1,26,2,26,10,3,6,10,4,8,1584781,14,1310721,2,3,26, +2,1584905,14,1,1310721,2,5,26,1310721,4,18,1310721,2,3,28,1310721, +4,1310721,2,1835009,40,1584953,16,1966081,1310721,4,1579223,12,63,1310721,2,4, +1310721,2,2,1,10,40,1585035,16,1576095,1310721,2,2,26,2,1,6, +1,10,28,34,63,38,3,26,1,6,1,10,28,34,63,38, +1,10,28,1585037,14,1,1585075,16,1310721,2,1,6,2,1,6,-1, +26,5,6,4,10,8,8,1584961,14,1576095,1310721,2,2,26,2,1, +6,1,10,28,34,63,38,3,26,1585117,16,1,1585135,14,1574901,1310721, +2,2,26,2,1585135,14,1,1310721,2,3,26,1310721,4,18,0,0, +0,0,0,0,0,0,0,0,0,0,792587,113,64,33,36,95, +126,99,106,122,114,44,63,46,43,45,42,38,124,94,62,60, +37,0,1,0,0,0,0,1,0,0,0,0,0,-1,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,1310721,2,1,28,1310721,4,1310721,2,1835009,40,1588261,16,1966081,1310721,4, +1579223,12,1588285,14,122,99,58,32,108,105,110,101,32,0,1588265,1573495, +12,8,1585229,2,1573657,12,8,1588313,14,58,32,0,1588307,1573495,12,8, +1588223,1,6,2,1,6,3,26,5,6,4,10,8,8,1,1588357, +14,1,1310721,2,1,26,1310721,4,18,1310721,2,1,28,1310721,4,1310721, +2,1835009,40,1588405,16,1966081,1310721,4,1579223,12,21,1573255,12,8,1588223,2, +41,40,3,36,1588509,16,1588495,14,122,99,58,32,116,111,111,32, +109,97,110,121,32,101,114,114,111,114,115,44,32,115,116,111, +112,112,105,110,103,10,0,1588433,1573495,12,8,1573437,12,8,1,1588517, +14,1,1310721,2,1,26,1310721,4,18,1310721,2,1,28,1310721,4,1310721, +2,1835009,40,1588565,16,1966081,1310721,4,1579223,12,1588579,14,122,99,58,32, +0,1588569,1573495,12,8,1588223,2,1573657,12,8,1588223,2,3,28,1,6, +1,10,28,34,63,38,3,26,1588661,16,1588649,14,32,101,114,114, +111,114,10,0,1588633,1573495,12,8,1588691,14,1588683,14,32,101,114,114, +111,114,115,10,0,1588665,1573495,12,8,1573437,12,8,1,1588705,14,1, +1310721,2,1,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009, +40,1588753,16,1966081,1310721,4,1579223,12,1310721,2,4,1588227,12,8,1310721,2, +2,1573495,12,8,21,1573255,12,8,1573437,12,8,1,1588799,14,1,1310721, +2,3,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40, +1588847,16,1966081,1310721,4,1579223,12,1310721,2,4,1588225,2,1,6,1,10, +28,34,63,38,3,26,1588931,16,1587637,2,1,6,1,10,28,34, +63,38,3,26,1,6,1,10,28,34,63,38,1,10,28,1588933, +14,1,1588961,16,1588227,12,8,1310721,2,2,1573495,12,8,1588371,12,8, +1,1588969,14,1,1310721,2,3,26,1310721,4,18,1310721,2,5,28,1310721, +4,1310721,2,1835009,40,1589017,16,1966081,1310721,4,1579223,12,1310721,2,3,26, +4,1310721,2,4,1588225,2,1,6,1,10,28,34,63,38,3,26, +1589111,16,1587637,2,1,6,1,10,28,34,63,38,3,26,1,6, +1,10,28,34,63,38,1,10,28,1589113,14,1,1589175,16,1588227,12, +8,1310721,2,2,1573495,12,8,1589145,14,58,32,0,1589139,1573495,12,8, +1310721,2,3,26,2,1573495,12,8,1588371,12,8,1,1589183,14,1,1310721, +2,5,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40, +1589231,16,1966081,1310721,4,1579223,12,1310721,2,4,1310721,2,2,1588813,12,8, +1588225,3,10,3,6,10,4,8,1585223,2,1,28,1,6,1,10, +28,34,63,38,3,26,1589303,16,1588531,12,8,1601277,12,8,1,1589317, +14,1,1310721,2,3,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721, +2,1835009,40,1589365,16,1966081,1310721,4,1579223,12,1310721,2,3,26,4,1310721, +2,4,1310721,2,2,1310721,2,3,26,2,1588983,12,8,1588225,3,10, +3,6,10,4,8,1585223,2,1,28,1,6,1,10,28,34,63, +38,3,26,1589457,16,1588531,12,8,1601277,12,8,1,1589471,14,1,1310721, +2,5,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40, +1589519,16,1966081,1310721,4,1579223,12,1310721,2,4,1587499,2,1589563,16,1587503,2, +1,6,1,10,28,34,63,38,1,10,28,1589565,14,1,1589581,16, +1310721,2,2,1588813,12,8,1,1589589,14,1,1310721,2,3,26,1310721,4, +18,1310721,2,5,28,1310721,4,1310721,2,1835009,40,1589637,16,1966081,1310721,4, +1579223,12,1310721,2,3,26,4,1310721,2,4,1587499,2,1589691,16,1587503,2, +1,6,1,10,28,34,63,38,1,10,28,1589693,14,1,1589725,16, +1466881,1310721,2,2,26,2,1310721,2,3,26,2,1588983,12,8,1,1589733, +14,1,1310721,2,5,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721, +2,1835009,40,1589781,16,1966081,1310721,4,1579223,12,1310721,2,4,1,1310721,2, +3,26,4,1310721,2,2,2,1589889,16,1310721,2,3,26,1310721,2,3, +26,2,63,30,1310721,2,2,2,26,10,3,6,10,4,8,1310721, +2,1,6,2,1,6,3,26,5,6,4,10,8,8,1589799,14, +1310721,2,3,26,2,16385,3,28,32,1589913,14,1,1310721,2,5,26, +1310721,4,18,1310721,2,9,28,1310721,4,1310721,2,1835009,40,1589961,16,1966081, +1310721,4,1579223,12,1310721,2,4,1310721,2,2,1589747,12,1310721,2,3,26, +4,3,1590151,16,1310721,2,5,26,1311233,1310721,2,3,26,2,26,2, +10,3,6,10,4,8,1310721,2,5,26,2,1,6,1,10,28, +34,63,38,3,26,1590067,16,1590151,14,1310721,2,5,26,2,1310721,2, +2,1574183,12,1590105,16,1310721,2,5,26,2,1590431,14,1310721,2,3,26, +1310721,2,3,26,2,3,26,16385,3,28,32,10,3,6,10,4, +8,1589987,14,1310721,2,7,26,1310721,2,2,1574039,12,10,3,6,10, +4,8,1585151,2,1310721,2,7,26,2,26,5,26,1409025,40,3,36, +1590259,16,1590251,14,115,116,114,105,110,103,32,112,111,111,108,32, +102,117,108,108,0,1590217,1588719,12,8,1585151,2,1,26,1,10,3, +6,10,4,8,1310721,2,5,26,1585151,2,3,26,10,3,6,10, +4,8,1310721,2,5,26,2,1310721,2,2,1310721,2,7,26,2,3, +26,1574491,12,8,1585151,1585151,2,1310721,2,7,26,2,26,5,26,10, +3,6,10,4,8,1311233,1310721,2,3,26,2,26,1310721,2,5,26, +2,10,3,6,10,4,8,1310721,2,5,26,2,1590431,14,1,1310721, +2,9,26,1310721,4,18,1310721,2,9,28,1310721,4,1310721,2,1835009,40, +1590479,16,1966081,1310721,4,1579223,12,1310721,2,5,26,4,1310721,2,3,26, +4,1310721,2,4,1585153,2,1310721,2,7,26,4,1585153,2,8193,40,3, +36,1590583,16,1590575,14,115,121,109,98,111,108,32,116,97,98,108, +101,32,102,117,108,108,0,1590539,1588719,12,8,1585153,1,6,2,1, +6,3,26,5,6,4,10,8,8,1409537,1310721,2,7,26,2,26, +1310721,2,2,10,3,6,10,4,8,1417729,1310721,2,7,26,2,26, +1310721,2,3,26,2,10,3,6,10,4,8,1425921,1310721,2,7,26, +2,26,1310721,2,5,26,2,10,3,6,10,4,8,1434113,1310721,2, +7,26,2,26,1,10,3,6,10,4,8,1442305,1310721,2,7,26, +2,26,3,10,3,6,10,4,8,1450497,1310721,2,7,26,2,26, +1,10,3,6,10,4,8,1559041,1310721,2,7,26,2,26,1,10, +3,6,10,4,8,1458689,1310721,2,7,26,2,26,1310721,2,2,3, +1,10,28,26,2,10,3,6,10,4,8,1310721,2,2,3,1, +10,28,26,1310721,2,7,26,2,3,26,10,3,6,10,4,8, +1310721,2,7,26,2,1590929,14,1,1310721,2,9,26,1310721,4,18,1310721, +2,5,28,1310721,4,1310721,2,1835009,40,1590977,16,1966081,1310721,4,1579223,12, +1310721,2,4,1310721,2,2,3,1,10,28,26,2,3,28,1310721,2, +3,26,4,1310721,2,3,26,2,1,40,3,36,1591263,16,1310721,2, +3,26,2,1585157,2,40,3,36,1591067,16,3,1591129,14,1417729,1310721,2, +3,26,2,26,2,11,28,1,6,1,10,28,34,63,38,3, +26,1,6,1,10,28,34,63,38,1,10,28,1591139,16,3,1591201, +14,1417729,1310721,2,3,26,2,26,2,15,28,1,6,1,10,28, +34,63,38,3,26,1,6,1,10,28,34,63,38,1,10,28, +1591219,16,1310721,2,3,26,2,1591277,14,1310721,2,3,26,1458689,1310721,2, +3,26,2,26,2,3,28,10,3,6,10,4,8,1591015,14,3, +1,10,28,1591277,14,1,1310721,2,5,26,1310721,4,18,1310721,2,3, +28,1310721,4,1310721,2,1835009,40,1591325,16,1966081,1310721,4,1579223,12,1310721,2, +4,1585153,2,1310721,2,2,10,40,1591423,16,1585153,1,6,2,1,6, +-1,26,5,6,4,10,8,8,1409537,1585153,2,26,2,3,1,10, +28,26,1458689,1585153,2,26,2,10,3,6,10,4,8,1591331,14,1, +1591431,14,1,1310721,2,3,26,1310721,4,18,1310721,2,3,28,1310721,4, +1310721,2,1835009,40,1591479,16,1966081,1310721,4,1579223,12,1310721,2,4,1310721,2, +2,1589927,12,1590943,12,1591505,14,1,1310721,2,3,26,1310721,4,18,1310721, +2,7,28,1310721,4,1310721,2,1835009,40,1591553,16,1966081,1310721,4,1579223,12, +1310721,2,3,26,4,1310721,2,4,1585161,2,1310721,2,5,26,4,1310721, +2,5,26,2,1591631,16,1585161,1491457,1310721,2,5,26,2,26,2,10, +3,6,10,4,8,1591773,14,1585159,1,6,2,1,6,3,26,5, +6,4,10,8,8,1310721,2,5,26,1585159,2,10,3,6,10,4, +8,1310721,2,5,26,2,16385,40,3,36,1591773,16,1591765,14,116,111, +111,32,109,97,110,121,32,102,111,114,119,97,114,100,32,114, +101,102,101,114,101,110,99,101,115,0,1591709,1588719,12,8,1475073,1310721, +2,5,26,2,26,1310721,2,2,10,3,6,10,4,8,1491457,1310721, +2,5,26,2,26,1310721,2,3,26,2,10,3,6,10,4,8, +1310721,2,5,26,2,1591857,14,1,1310721,2,7,26,1310721,4,18,1310721, +2,7,28,1310721,4,1310721,2,1835009,40,1591905,16,1966081,1310721,4,1579223,12, +1310721,2,3,26,4,1310721,2,4,1310721,2,2,1592067,16,1475073,1310721,2, +2,26,2,1310721,2,3,26,2,1592451,12,8,1310721,2,5,26,1491457, +1310721,2,2,26,2,10,3,6,10,4,8,1491457,1310721,2,2,26, +1585161,2,10,3,6,10,4,8,1585161,1310721,2,2,10,3,6,10, +4,8,1310721,2,1310721,2,5,26,2,10,3,6,10,4,8,1591921, +14,1,1592075,14,1,1310721,2,7,26,1310721,4,18,1310721,2,3,28, +1310721,4,1310721,2,1835009,40,1592123,16,1966081,1310721,4,1579223,12,1310721,2,4, +1587637,2,1592143,16,1,1592269,14,1587483,2,1587487,2,40,3,36,1592205,16, +1592197,14,112,114,111,103,114,97,109,32,116,111,111,32,98,105, +103,0,1592165,1588719,12,8,1587483,2,1587485,2,26,1310721,2,2,10,3, +6,10,4,8,1587483,1,6,2,1,6,3,26,5,6,4,10, +8,8,1,1592269,14,1,1310721,2,3,26,1310721,4,18,1310721,2,3, +28,1310721,4,1310721,2,1835009,40,1592317,16,1966081,1310721,4,1579223,12,1310721,2, +4,1310721,2,2,1310721,2,2,26,3,26,1592089,12,8,1,1592355,14, +1,1310721,2,3,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2, +1835009,40,1592403,16,1966081,1310721,4,1579223,12,1310721,2,4,1310721,2,2,1310721, +2,2,26,1592089,12,8,1,1592437,14,1,1310721,2,3,26,1310721,4, +18,1310721,2,5,28,1310721,4,1310721,2,1835009,40,1592485,16,1966081,1310721,4, +1579223,12,1310721,2,3,26,4,1310721,2,4,1587637,2,1592515,16,1,1592573, +14,1310721,2,2,1587485,2,26,1310721,2,3,26,2,1310721,2,3,26, +2,26,3,26,10,3,6,10,4,8,1,1592573,14,1,1310721,2, +5,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1592621, +16,1966081,1310721,4,1579223,12,1310721,2,4,1310721,2,2,1592283,12,8,15, +1592369,12,8,1,1592655,14,1,1310721,2,3,26,1310721,4,18,1310721,2, +3,28,1310721,4,1310721,2,1835009,40,1592703,16,1966081,1310721,4,1579223,12,1587483, +2,1310721,2,4,2097153,3,28,1592283,12,8,1310721,2,2,1592737,14,1, +1310721,2,3,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,1835009, +40,1592785,16,1966081,1310721,4,1579223,12,1310721,2,4,1310721,2,2,3,1585229, +2,1590445,12,1310721,2,3,26,4,1442305,1310721,2,3,26,2,26,1, +10,3,6,10,4,8,1310721,2,3,26,2,1592861,14,1,1310721,2, +5,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1592909, +16,1966081,1310721,4,1579223,12,1310721,2,4,1442305,1310721,2,2,26,2,3, +32,1592959,16,1425921,1310721,2,2,26,2,1592283,12,8,1,1593035,14,1587637, +2,1592973,16,1,1593035,14,1450497,1310721,2,2,26,1587483,2,1450497,1310721,2, +2,26,2,1591519,12,10,3,6,10,4,8,2097153,3,28,1592283,12, +8,1,1593035,14,1,1310721,2,3,26,1310721,4,18,1310721,2,5,28, +1310721,4,1310721,2,1835009,40,1593083,16,1966081,1310721,4,1579223,12,1310721,2,4, +1310721,2,2,1590943,12,1310721,2,3,26,4,1310721,2,3,26,2,1, +40,1593157,16,1310721,2,3,26,1310721,2,2,1592751,12,10,3,6,10, +4,8,1310721,2,3,26,2,1592875,12,8,13,1592369,12,8,1,1593189, +14,1,1310721,2,5,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721, +2,1835009,40,1593237,16,1966081,1310721,4,1579223,12,1310721,2,4,1310721,1592283,12, +8,3,1592369,12,8,1310721,2,2,1593289,16,1310721,2,2,1592283,12,8, +27,1592369,12,8,1,1593297,14,1,1310721,2,3,26,1310721,4,18,1310721, +2,5,28,1310721,4,1310721,2,1835009,40,1593345,16,1966081,1310721,4,1579223,12, +1310721,2,4,1558017,1310721,2,2,26,2,33,32,1593543,16,1310721,2,3, +26,1587693,2,1590943,12,10,3,6,10,4,8,1310721,2,3,26,2, +1,40,3,36,1593487,16,1417729,1310721,2,3,26,2,26,2,13,28, +1,6,1,10,28,34,63,38,3,26,1,6,1,10,28,34, +63,38,1,10,28,1593489,14,1,1593543,16,1467905,1310721,2,2,26,2, +1592283,12,8,45,1425921,1310721,2,3,26,2,26,2,26,1592369,12,8, +1,1593565,14,1467905,1310721,2,2,26,2,1593203,12,1593565,14,1,1310721,2, +5,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,1835009,40,1593613, +16,1966081,1310721,4,1579223,12,1310721,2,4,1,1310721,2,3,26,4,1310721, +2,3,26,2,1587723,2,40,1593743,16,1587707,1310721,2,3,26,2,26, +2,1310721,2,2,28,1,6,1,10,28,34,63,38,3,26,1593705, +16,1,1593817,14,1310721,2,3,26,1,6,2,1,6,3,26,5, +6,4,10,8,8,1593631,14,1587723,2,15,40,1593809,16,1587707,1587723,2, +26,1310721,2,2,10,3,6,10,4,8,1587723,1,6,2,1,6, +3,26,5,6,4,10,8,8,1,1593817,14,1,1310721,2,5,26, +1310721,4,18,1310721,2,9,28,1310721,4,1310721,2,1835009,40,1593865,16,1966081, +1310721,4,1579223,12,1310721,2,7,26,4,1310721,2,5,26,4,1310721,2, +3,26,4,1310721,2,4,1587485,2,1593915,16,3,1593959,14,1310721,2,2, +1310721,2,3,26,2,40,3,36,1,6,1,10,28,34,63,38, +1,10,28,1593969,16,1,1594137,14,1588127,1,26,1,10,3,6,10, +4,8,1588127,3,26,5,10,3,6,10,4,8,1588127,5,26,1310721, +2,2,10,3,6,10,4,8,1588127,7,26,1310721,2,3,26,2, +10,3,6,10,4,8,1588127,9,26,1310721,2,7,26,2,10,3, +6,10,4,8,1588127,11,26,1310721,2,5,26,2,1310721,2,7,26, +2,1574491,12,8,1588127,1572879,12,8,1,1594137,14,1,1310721,2,9,26, +1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,1835009,40,1594185,16,1966081, +1310721,4,1579223,12,1,1310721,2,4,1310721,2,2,1588125,2,40,1594345,16, +1310721,2,3,26,1587725,1310721,2,2,21,30,26,10,3,6,10,4, +8,1310721,2,3,26,2,1,26,2,1310721,2,3,26,2,3,26, +2,1310721,2,3,26,2,7,26,1310721,2,3,26,2,5,26,2, +1593831,12,8,1310721,2,1,6,2,1,6,3,26,5,6,4,10, +8,8,1594193,14,1588125,1,10,3,6,10,4,8,1,1594369,14,1, +1310721,2,5,26,1310721,4,18,1310721,2,7,28,1310721,4,1310721,2,1835009, +40,1594417,16,1966081,1310721,4,1579223,12,1310721,2,4,1592669,12,1310721,2,3, +26,4,15,1592369,12,8,1310721,2,5,26,1587483,2,10,3,6,10, +4,8,1310721,2,2,2,1594529,16,1310721,2,2,2,1592089,12,8,1310721, +2,1,6,2,1,6,3,26,5,6,4,10,8,8,1594469,14, +1,1592089,12,8,1310721,2,3,26,2,1587483,2,1592451,12,8,1310721,2, +5,26,2,1592283,12,8,1,1594581,14,1,1310721,2,7,26,1310721,4, +18,1310721,2,1,28,1310721,4,1310721,2,1835009,40,1594629,16,1966081,1310721,4, +1579223,12,1585235,2,1,28,1,6,1,10,28,34,63,38,3,26, +1594835,16,1585241,2,1594675,16,3,1594701,14,1573381,12,1,6,1,10,28, +34,63,38,1,10,28,1594733,16,1585233,3,1,10,28,10,3,6, +10,4,8,1,1595135,14,1585233,1573325,12,511,32,10,3,6,10,4, +8,1585233,2,193,28,1,6,1,10,28,34,63,38,3,26,1594831, +16,1585241,3,10,3,6,10,4,8,1585233,3,1,10,28,10,3, +6,10,4,8,1,1595135,14,1595067,14,1585235,2,7,28,1,6,1, +10,28,34,63,38,3,26,1594981,16,1585233,1585239,2,1585237,2,5,38, +26,2,1585237,2,7,32,17,30,38,511,32,10,3,6,10,4, +8,1585233,2,1594955,16,1585237,1,6,2,1,6,3,26,5,6,4, +10,8,8,1594977,14,1585233,3,1,10,28,10,3,6,10,4,8, +1595067,14,1585233,1585237,2,2,511,32,10,3,6,10,4,8,1585233,2, +1595045,16,1585237,1,6,2,1,6,3,26,5,6,4,10,8,8, +1595067,14,1585233,3,1,10,28,10,3,6,10,4,8,1585233,2,21, +28,1,6,1,10,28,34,63,38,3,26,1595127,16,1585231,1,6, +2,1,6,3,26,5,6,4,10,8,8,1,1595135,14,1,1310721, +2,1,26,1310721,4,18,1310721,2,13,28,1310721,4,1310721,2,1835009,40, +1595183,16,1966081,1310721,4,1579223,12,1310721,2,7,26,4,1310721,2,5,26, +4,1310721,2,3,26,4,1310721,2,4,1585243,1,6,2,1,6,3, +26,5,6,4,10,8,8,1585243,2,65,40,3,36,1595327,16,1595319, +14,115,111,117,114,99,101,115,32,110,101,115,116,101,100,32, +116,111,111,32,100,101,101,112,108,121,0,1595267,1588719,12,8,1310721, +2,9,26,1540609,1585243,2,65,30,26,10,3,6,10,4,8,1310721, +2,9,26,2,1,26,1310721,2,2,10,3,6,10,4,8,1310721, +2,9,26,2,3,26,1310721,2,3,26,2,10,3,6,10,4, +8,1310721,2,9,26,2,5,26,1,10,3,6,10,4,8,1310721, +2,9,26,2,7,26,1,10,3,6,10,4,8,1310721,2,9, +26,2,9,26,1310721,2,5,26,2,10,3,6,10,4,8,1310721, +2,9,26,2,23,26,1310721,2,7,26,2,10,3,6,10,4, +8,1310721,2,9,26,2,11,26,1585223,2,10,3,6,10,4,8, +1310721,2,9,26,2,13,26,1585225,2,10,3,6,10,4,8,1310721, +2,9,26,2,15,26,1585227,2,10,3,6,10,4,8,1310721,2, +9,26,2,17,26,1585233,2,10,3,6,10,4,8,1310721,2,9, +26,2,19,26,1585235,2,10,3,6,10,4,8,1310721,2,9,26, +2,21,26,1585237,2,10,3,6,10,4,8,1310721,2,9,26,2, +25,26,1585231,2,10,3,6,10,4,8,1310721,2,9,26,2,27, +26,1585229,2,10,3,6,10,4,8,1310721,2,9,26,2,29,26, +1587449,2,10,3,6,10,4,8,1310721,2,9,26,2,63,26,1585239, +2,10,3,6,10,4,8,1310721,2,11,26,1,10,3,6,10, +4,8,1310721,2,11,26,2,1587449,2,40,1596167,16,1310721,2,9,26, +2,31,1310721,2,11,26,2,9,30,26,26,1587451,1310721,2,11,26, +2,26,2,10,3,6,10,4,8,1310721,2,9,26,2,33,1310721, +2,11,26,2,9,30,26,26,1587459,1310721,2,11,26,2,26,2, +10,3,6,10,4,8,1310721,2,9,26,2,35,1310721,2,11,26, +2,9,30,26,26,1587467,1310721,2,11,26,2,26,2,10,3,6, +10,4,8,1310721,2,9,26,2,37,1310721,2,11,26,2,9,30, +26,26,1587475,1310721,2,11,26,2,26,2,10,3,6,10,4,8, +1310721,2,11,26,1,6,2,1,6,3,26,5,6,4,10,8, +8,1595877,14,1587449,1,10,3,6,10,4,8,1310721,2,2,3,28, +1,6,1,10,28,34,63,38,3,26,1596259,16,1310721,2,9,26, +2,7,26,1310721,2,3,26,2,1,26,2,10,3,6,10,4, +8,1310721,2,2,1,28,1,6,1,10,28,34,63,38,3,26, +1596347,16,1585235,1,10,3,6,10,4,8,1585241,1,10,3,6,10, +4,8,1585231,3,10,3,6,10,4,8,1594595,12,8,1310721,2,2, +5,28,1,6,1,10,28,34,63,38,3,26,1596443,16,1585235,5, +10,3,6,10,4,8,1585237,1310721,2,3,26,2,10,3,6,10, +4,8,1585231,3,10,3,6,10,4,8,1594595,12,8,1310721,2,2, +7,28,1,6,1,10,28,34,63,38,3,26,1596555,16,1585235,7, +10,3,6,10,4,8,1585239,1310721,2,3,26,2,10,3,6,10, +4,8,1585237,1,10,3,6,10,4,8,1585231,3,10,3,6,10, +4,8,1594595,12,8,1,1596563,14,1,1310721,2,13,26,1310721,4,18, +1310721,2,5,28,1310721,4,1310721,2,1835009,40,1596611,16,1966081,1310721,4,1579223, +12,1540609,1585243,2,65,30,26,1310721,2,4,1,1310721,2,3,26,4, +1585223,1310721,2,2,11,26,2,10,3,6,10,4,8,1585225,1310721,2, +2,13,26,2,10,3,6,10,4,8,1585227,1310721,2,2,15,26, +2,10,3,6,10,4,8,1585233,1310721,2,2,17,26,2,10,3, +6,10,4,8,1585235,1310721,2,2,19,26,2,10,3,6,10,4, +8,1585237,1310721,2,2,21,26,2,10,3,6,10,4,8,1585231,1310721, +2,2,25,26,2,10,3,6,10,4,8,1585229,1310721,2,2,27, +26,2,10,3,6,10,4,8,1587449,1310721,2,2,29,26,2,10, +3,6,10,4,8,1585239,1310721,2,2,63,26,2,10,3,6,10, +4,8,1310721,2,3,26,2,1587449,2,40,1597175,16,1587451,1310721,2,3, +26,2,26,1310721,2,2,31,1310721,2,3,26,2,9,30,26,26, +2,10,3,6,10,4,8,1587459,1310721,2,3,26,2,26,1310721,2, +2,33,1310721,2,3,26,2,9,30,26,26,2,10,3,6,10, +4,8,1587467,1310721,2,3,26,2,26,1310721,2,2,35,1310721,2,3, +26,2,9,30,26,26,2,10,3,6,10,4,8,1587475,1310721,2, +3,26,2,26,1310721,2,2,37,1310721,2,3,26,2,9,30,26, +26,2,10,3,6,10,4,8,1310721,2,3,26,1,6,2,1, +6,3,26,5,6,4,10,8,8,1596901,14,1585243,1,6,2,1, +6,-1,26,5,6,4,10,8,8,1310721,2,2,23,26,2,1597237, +16,1310721,2,2,3,26,2,1580907,12,8,1,1597245,14,1,1310721,2, +5,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,1835009,40,1597293, +16,1966081,1310721,4,1579223,12,1310721,2,4,1597343,14,43,45,42,47,37, +61,60,62,33,38,124,94,126,63,58,46,64,35,36,0,1597303, +1310721,2,3,26,4,1310721,2,3,26,2,2,1597459,16,1310721,2,3, +26,2,2,1310721,2,2,28,1,6,1,10,28,34,63,38,3, +26,1597421,16,3,1597467,14,1310721,2,3,26,1,6,2,1,6,3, +26,5,6,4,10,8,8,1597355,14,1,1597467,14,1,1310721,2,5, +26,1310721,4,18,1310721,2,11,28,1310721,4,1310721,2,1835009,40,1597515,16, +1966081,1310721,4,1579223,12,1310721,2,3,26,4,1310721,2,4,1,1310721,2, +5,26,4,1310721,2,5,26,2,1585169,2,40,1597945,16,1310721,2,7, +26,1547777,1310721,2,5,26,2,26,2,10,3,6,10,4,8,1310721, +2,9,26,1,10,3,6,10,4,8,1310721,2,9,26,2,1310721, +2,2,40,1597731,16,1310721,2,7,26,2,1310721,2,9,26,2,26, +2,1585245,1310721,2,9,26,2,26,2,28,1,6,1,10,28,34, +63,38,3,26,1,6,1,10,28,34,63,38,1,10,28,1597733, +14,1,1597775,16,1310721,2,9,26,1,6,2,1,6,3,26,5, +6,4,10,8,8,1597621,14,1310721,2,9,26,2,1310721,2,2,28, +1,6,1,10,28,34,63,38,3,26,1597895,16,1310721,2,7,26, +2,1310721,2,2,26,2,1310721,2,3,26,2,28,1,6,1,10, +28,34,63,38,3,26,1,6,1,10,28,34,63,38,1,10, +28,1597897,14,1,1597907,16,3,1597953,14,1310721,2,5,26,1,6,2, +1,6,3,26,5,6,4,10,8,8,1597543,14,1,1597953,14,1, +1310721,2,11,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009, +40,1598001,16,1966081,1310721,4,1579223,12,1310721,2,4,1310721,2,2,97,40, +3,36,1598067,16,1310721,2,2,115,10,40,3,36,1,6,1,10, +28,34,63,38,1,10,28,1598069,14,1,1598075,14,1,1310721,2,3, +26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1598123,16, +1966081,1310721,4,1579223,12,1310721,2,4,1310721,2,2,195,40,3,36,1598189, +16,1310721,2,2,245,10,40,3,36,1,6,1,10,28,34,63, +38,1,10,28,1598191,14,1,1598201,16,3,1598285,14,1310721,2,2,131, +40,3,36,1598261,16,1310721,2,2,181,10,40,3,36,1,6,1, +10,28,34,63,38,1,10,28,1598263,14,1,1,6,1,10,28, +34,63,38,1,10,28,1598295,16,3,1598347,14,1310721,2,2,191,28, +1,6,1,10,28,34,63,38,3,26,1,6,1,10,28,34, +63,38,1,10,28,1598353,14,1,1310721,2,3,26,1310721,4,18,1310721, +2,3,28,1310721,4,1310721,2,1835009,40,1598401,16,1966081,1310721,4,1579223,12, +1310721,2,4,1310721,2,2,1597967,12,1598435,16,1310721,2,2,97,28,1598617, +14,1310721,2,2,195,40,3,36,1598495,16,1310721,2,2,205,10,40, +3,36,1,6,1,10,28,34,63,38,1,10,28,1598497,14,1, +1598519,16,1310721,2,2,195,28,21,26,1598617,14,1310721,2,2,131,40, +3,36,1598579,16,1310721,2,2,141,10,40,3,36,1,6,1,10, +28,34,63,38,1,10,28,1598581,14,1,1598603,16,1310721,2,2,131, +28,21,26,1598617,14,3,1,10,28,1598617,14,1,1310721,2,3,26, +1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1598665,16,1966081, +1310721,4,1579223,12,1585233,2,1310721,2,4,1594595,12,8,1310721,2,2,185, +28,1,6,1,10,28,34,63,38,1,10,28,1598727,16,1310721,2, +2,1598965,14,1310721,2,1585233,2,10,3,6,10,4,8,1594595,12,8, +1310721,2,2,221,28,1,6,1,10,28,34,63,38,3,26,1598793, +16,21,1598965,14,1310721,2,2,233,28,1,6,1,10,28,34,63, +38,3,26,1598833,16,19,1598965,14,1310721,2,2,229,28,1,6,1, +10,28,34,63,38,3,26,1598873,16,27,1598965,14,1310721,2,2,97, +28,1,6,1,10,28,34,63,38,3,26,1598913,16,1,1598965,14, +1310721,2,2,203,28,1,6,1,10,28,34,63,38,3,26,1598953, +16,55,1598965,14,1310721,2,2,1598965,14,1,1310721,2,3,26,1310721,4, +18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1599013,16,1966081,1310721,4, +1579223,12,1310721,2,4,1585233,2,1,40,3,36,1599069,16,1585233,2,1597259, +12,1,6,1,10,28,34,63,38,1,10,28,1599071,14,1,1599115, +16,1310721,2,2,1585233,2,1597481,12,1,6,1,10,28,34,63,38, +1,10,28,1599117,14,1,1599187,16,1585245,1310721,2,2,26,1585233,2,10, +3,6,10,4,8,1310721,2,1,6,2,1,6,3,26,5,6, +4,10,8,8,1594595,12,8,1599019,14,1585245,1310721,2,2,26,1,10, +3,6,10,4,8,1585223,9,10,3,6,10,4,8,1585225,1585245,1589927, +12,10,3,6,10,4,8,1,1599255,14,1,1310721,2,3,26,1310721, +4,18,1310721,2,7,28,1310721,4,1310721,2,1835009,40,1599303,16,1966081,1310721, +4,1579223,12,1,1310721,2,4,1585227,3,10,3,6,10,4,8,3, +1599979,16,1585233,2,65,28,1,6,1,10,28,34,63,38,3,26, +1599371,16,3,1599421,14,1585233,2,19,28,1,6,1,10,28,34,63, +38,3,26,1,6,1,10,28,34,63,38,1,10,28,1599431,16, +3,1599481,14,1585233,2,21,28,1,6,1,10,28,34,63,38,3, +26,1,6,1,10,28,34,63,38,1,10,28,1599491,16,3,1599541, +14,1585233,2,27,28,1,6,1,10,28,34,63,38,3,26,1, +6,1,10,28,34,63,38,1,10,28,1599571,16,1594595,12,8,1585227, +1,10,3,6,10,4,8,1599333,14,1585233,2,95,28,1,6,1, +10,28,34,63,38,1,10,28,1599609,16,1599979,14,1594595,12,8,1585233, +2,95,28,1,6,1,10,28,34,63,38,3,26,1599755,16,1585233, +2,1,40,3,36,1599719,16,1585233,2,21,28,1,6,1,10,28, +34,63,38,1,10,28,1,6,1,10,28,34,63,38,1,10, +28,1599721,14,1,1599735,16,1594595,12,8,1599647,14,1585227,1,10,3,6, +10,4,8,1599327,14,1585233,2,85,28,1,6,1,10,28,34,63, +38,3,26,1599923,16,1594595,12,8,1585233,2,1,40,3,36,1599897,16, +1585233,2,85,28,1,6,1,10,28,34,63,38,3,26,1599887,16, +1594595,12,8,1585233,2,95,28,1,6,1,10,28,34,63,38,3, +26,1599883,16,1599897,14,1599893,14,1594595,12,8,1599793,14,1594595,12,8,1585227, +1,10,3,6,10,4,8,1599327,14,1585229,1585231,2,10,3,6,10, +4,8,1585245,1,26,95,10,3,6,10,4,8,3,1598979,12,8, +1,1601263,14,1599327,14,1585229,1585231,2,10,3,6,10,4,8,1585233,2, +1,40,1600047,16,1585223,1,10,3,6,10,4,8,1585225,1,10,3, +6,10,4,8,1,1601263,14,1585233,2,1597967,12,1600469,16,1310721,2,3, +26,1,10,3,6,10,4,8,1585233,2,97,28,1,6,1,10, +28,34,63,38,3,26,1600357,16,1594595,12,8,1585233,2,241,28,1, +6,1,10,28,34,63,38,3,26,1600157,16,3,1600207,14,1585233,2, +177,28,1,6,1,10,28,34,63,38,3,26,1,6,1,10, +28,34,63,38,1,10,28,1600357,16,1594595,12,8,1310721,2,5,26, +1585233,2,1598367,12,10,3,6,10,4,1,40,3,36,1600311,16,1310721, +2,3,26,1310721,2,3,26,2,33,30,1310721,2,5,26,2,26, +10,3,6,10,4,8,1594595,12,8,1600217,14,1585223,3,10,3,6, +10,4,8,1585225,1310721,2,3,26,2,10,3,6,10,4,8,1, +1601263,14,1585233,2,1597967,12,1600423,16,1310721,2,3,26,1310721,2,3,26, +2,21,30,1585233,2,26,97,28,10,3,6,10,4,8,1594595,12, +8,1600357,14,1585223,3,10,3,6,10,4,8,1585225,1310721,2,3,26, +2,10,3,6,10,4,8,1,1601263,14,1585233,2,1598089,12,1600679,16, +1585233,2,1598089,12,1600499,16,3,1600529,14,1585233,2,1597967,12,1,6,1, +10,28,34,63,38,1,10,28,1600613,16,1310721,2,2,2001,40,1600603, +16,1585245,1310721,2,2,26,1585233,2,10,3,6,10,4,8,1310721,2, +1,6,2,1,6,3,26,5,6,4,10,8,8,1594595,12,8, +1600481,14,1585245,1310721,2,2,26,1,10,3,6,10,4,8,1585223,5, +10,3,6,10,4,8,1585225,1585245,1589927,12,10,3,6,10,4,8, +1,1601263,14,1585233,2,79,28,1,6,1,10,28,34,63,38,3, +26,1600853,16,1594595,12,8,1585225,1598631,12,10,3,6,10,4,8,1585233, +2,79,28,1,6,1,10,28,34,63,38,1,10,28,1600825,16, +1600817,14,98,97,100,32,99,104,97,114,97,99,116,101,114,32, +108,105,116,101,114,97,108,0,1600773,1589197,12,8,1594595,12,8,1585223, +3,10,3,6,10,4,8,1,1601263,14,1585233,2,69,28,1,6, +1,10,28,34,63,38,3,26,1601145,16,1594595,12,8,1585233,2,1, +40,3,36,1600963,16,1585233,2,69,28,1,6,1,10,28,34,63, +38,1,10,28,1,6,1,10,28,34,63,38,1,10,28,1600965, +14,1,1601073,16,1310721,2,3,26,1598631,12,10,3,6,10,4,8, +1310721,2,2,2001,40,1601069,16,1585245,1310721,2,2,26,1310721,2,3,26, +2,10,3,6,10,4,8,1310721,2,1,6,2,1,6,3,26, +5,6,4,10,8,8,1600891,14,1594595,12,8,1585245,1310721,2,2,26, +1,10,3,6,10,4,8,1585223,7,10,3,6,10,4,8,1585225, +1585245,1589927,12,10,3,6,10,4,8,1,1601263,14,1585245,1,26,1585233, +2,10,3,6,10,4,8,1594595,12,8,1585245,1,26,2,1597259,12, +1601199,16,3,1598979,12,1601263,14,1585245,3,26,1,10,3,6,10,4, +8,1585223,9,10,3,6,10,4,8,1585225,1585245,1589927,12,10,3,6, +10,4,8,1,1601263,14,1,1310721,2,7,26,1310721,4,18,1310721,2, +3,28,1310721,4,1310721,2,1835009,40,1601311,16,1966081,1310721,4,1579223,12,3, +1310721,2,4,1587449,2,1601651,16,1585223,1587451,1,26,2,10,3,6,10, +4,8,1585225,1587459,1,26,2,10,3,6,10,4,8,1585227,1587467,1, +26,2,10,3,6,10,4,8,1585229,1587475,1,26,2,10,3,6, +10,4,8,1310721,2,2,1587449,2,40,1601617,16,1587451,1310721,2,2,3, +28,26,1587451,1310721,2,2,26,2,10,3,6,10,4,8,1587459,1310721, +2,2,3,28,26,1587459,1310721,2,2,26,2,10,3,6,10,4, +8,1587467,1310721,2,2,3,28,26,1587467,1310721,2,2,26,2,10,3, +6,10,4,8,1587475,1310721,2,2,3,28,26,1587475,1310721,2,2,26, +2,10,3,6,10,4,8,1310721,2,1,6,2,1,6,3,26, +5,6,4,10,8,8,1601415,14,1587449,1,6,2,1,6,-1,26, +5,6,4,10,8,8,1,1601661,14,1602197,12,1601661,14,1,1310721,2, +3,26,1310721,4,18,1310721,2,11,28,1310721,4,1310721,2,1835009,40,1601709, +16,1966081,1310721,4,1579223,12,1310721,2,4,1587449,2,1310721,2,2,10,40, +3,36,1602063,16,1310721,2,3,26,1585223,2,10,3,6,10,4,8, +1310721,2,5,26,1585225,2,10,3,6,10,4,8,1310721,2,7,26, +1585227,2,10,3,6,10,4,8,1310721,2,9,26,1585229,2,10,3, +6,10,4,8,1602197,12,8,1587451,1587449,2,26,1585223,2,10,3,6, +10,4,8,1587459,1587449,2,26,1585225,2,10,3,6,10,4,8,1587467, +1587449,2,26,1585227,2,10,3,6,10,4,8,1587475,1587449,2,26,1585229, +2,10,3,6,10,4,8,1587449,1,6,2,1,6,3,26,5, +6,4,10,8,8,1585223,1310721,2,3,26,2,10,3,6,10,4, +8,1585225,1310721,2,5,26,2,10,3,6,10,4,8,1585227,1310721,2, +7,26,2,10,3,6,10,4,8,1585229,1310721,2,9,26,2,10, +3,6,10,4,8,1601715,14,1587445,1587451,1310721,2,2,26,2,10,3, +6,10,4,8,1587447,1587459,1310721,2,2,26,2,10,3,6,10,4, +8,1,1602123,14,1,1310721,2,11,26,1310721,4,18,1310721,2,1,28, +1310721,4,1310721,2,1835009,40,1602171,16,1966081,1310721,4,1579223,12,1,1601675,12, +1602183,14,1,1310721,2,1,26,1310721,4,18,1310721,2,7,28,1310721,4, +1310721,2,1835009,40,1602231,16,1966081,1310721,4,1579223,12,1310721,2,1540609,1585243,2, +65,30,26,10,3,6,10,4,8,1310721,2,2,1,26,2,3, +28,1,6,1,10,28,34,63,38,1,10,28,1602313,16,1599269,12, +8,1,1602689,14,1310721,2,5,26,1310721,2,2,5,26,2,10,3, +6,10,4,8,1310721,2,5,26,2,1310721,2,2,7,26,2,40, +1602601,16,1310721,2,3,26,1310721,2,2,3,26,2,10,3,6,10, +4,8,1585223,1310721,2,3,26,2,3,1310721,2,5,26,2,7,30, +26,26,2,10,3,6,10,4,8,1585225,1310721,2,3,26,2,5, +1310721,2,5,26,2,7,30,26,26,2,10,3,6,10,4,8, +1585229,1310721,2,3,26,2,7,1310721,2,5,26,2,7,30,26,26, +2,10,3,6,10,4,8,1585227,1,10,3,6,10,4,8,1310721, +2,2,5,26,1310721,2,5,26,2,3,26,10,3,6,10,4, +8,1,1602689,14,1310721,2,2,9,26,2,1,6,1,10,28,34, +63,38,3,26,1602675,16,1585223,1,10,3,6,10,4,8,1585225,1, +10,3,6,10,4,8,1,1602689,14,1596577,12,8,1,1602689,14,1, +1310721,2,7,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009, +40,1602737,16,1966081,1310721,4,1579223,12,1310721,2,4,1585223,2,9,28,1, +6,1,10,28,34,63,38,3,26,1602833,16,1585225,2,1310721,2,2, +28,1,6,1,10,28,34,63,38,3,26,1,6,1,10,28, +34,63,38,1,10,28,1602835,14,1,1602841,14,1,1310721,2,3,26, +1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1602889,16,1966081, +1310721,4,1579223,12,1310721,2,4,1585223,2,5,28,1,6,1,10,28, +34,63,38,3,26,1602985,16,1585225,2,1310721,2,2,28,1,6,1, +10,28,34,63,38,3,26,1,6,1,10,28,34,63,38,1, +10,28,1602987,14,1,1602993,14,1,1310721,2,3,26,1310721,4,18,1310721, +2,3,28,1310721,4,1310721,2,1835009,40,1603041,16,1966081,1310721,4,1579223,12, +1310721,2,4,1585223,2,9,28,1,6,1,10,28,34,63,38,3, +26,1603085,16,3,1603135,14,1585223,2,5,28,1,6,1,10,28,34, +63,38,3,26,1,6,1,10,28,34,63,38,1,10,28,1603197, +16,1585225,2,1310721,2,2,28,1,6,1,10,28,34,63,38,3, +26,1,6,1,10,28,34,63,38,1,10,28,1603199,14,1,1603215, +16,1601277,12,8,3,1603223,14,1,1603223,14,1,1310721,2,3,26,1310721, +4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1603271,16,1966081,1310721, +4,1579223,12,1310721,2,4,1310721,2,2,1603007,12,1,6,1,10,28, +34,63,38,3,26,1603505,16,1588225,2,1,6,1,10,28,34,63, +38,3,26,1603389,16,1587637,2,1,6,1,10,28,34,63,38,3, +26,1,6,1,10,28,34,63,38,1,10,28,1603391,14,1,1603451, +16,1588227,12,8,1603425,14,101,120,112,101,99,116,101,100,32,0, +1603405,1573495,12,8,1310721,2,2,1573495,12,8,1588371,12,8,1588225,3,10, +3,6,10,4,8,1585223,2,1,28,1,6,1,10,28,34,63, +38,3,26,1603505,16,1588531,12,8,1,1603513,14,1,1310721,2,3,26, +1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1603561,16,1966081, +1310721,4,1579223,12,1585225,2,1310721,2,4,1585223,2,5,28,1,6,1, +10,28,34,63,38,1,10,28,1603649,16,1603641,14,101,120,112,101, +99,116,101,100,32,97,32,110,97,109,101,0,1603609,1589197,12,8, +1601277,12,8,1310721,2,2,1603667,14,1,1310721,2,3,26,1310721,4,18, +1310721,2,15,28,1310721,4,1310721,2,1835009,40,1603715,16,1966081,1310721,4,1579223, +12,1310721,2,4,1585171,2,1310721,2,3,26,4,3,1604789,16,1310721,2, +2,2,65,28,1,6,1,10,28,34,63,38,3,26,1603811,16, +1310721,2,1,6,2,1,6,3,26,5,6,4,10,8,8,1603741, +14,1310721,2,2,2,1,6,1,10,28,34,63,38,3,26,1603847, +16,1604789,14,1310721,2,5,26,1,10,3,6,10,4,8,1310721,2, +2,1310721,2,5,26,2,26,2,1603965,16,1310721,2,2,1310721,2,5, +26,2,26,2,65,28,1,6,1,10,28,34,63,38,1,10, +28,1,6,1,10,28,34,63,38,1,10,28,1603967,14,1,1604055, +16,1585245,1310721,2,5,26,2,26,1310721,2,2,1310721,2,5,26,2, +26,2,10,3,6,10,4,8,1310721,2,5,26,1,6,2,1, +6,3,26,5,6,4,10,8,8,1603869,14,1585245,1310721,2,5,26, +2,26,1,10,3,6,10,4,8,1310721,2,1310721,2,2,1310721,2, +5,26,2,26,10,3,6,10,4,8,1310721,2,7,26,1585245,1, +26,2,91,28,1,6,1,10,28,34,63,38,3,26,1604199,16, +1585245,3,26,2,1597967,12,1,6,1,10,28,34,63,38,1,10, +28,1604201,14,1,10,3,6,10,4,8,1585245,1310721,2,7,26,2, +26,2,1597967,12,1604529,16,1310721,2,9,26,1,10,3,6,10,4, +8,1310721,2,11,26,1310721,2,7,26,2,10,3,6,10,4,8, +1585245,1310721,2,11,26,2,26,2,1604403,16,1310721,2,9,26,1310721,2, +9,26,2,21,30,1585245,1310721,2,11,26,2,26,2,26,97,28, +10,3,6,10,4,8,1310721,2,11,26,1,6,2,1,6,3, +26,5,6,4,10,8,8,1604289,14,1310721,2,7,26,2,1604451,16, +1310721,2,9,26,1,1310721,2,9,26,2,28,10,3,6,10,4, +8,1542657,1585171,2,26,1310721,2,9,26,2,1310721,2,9,26,2,26, +3,26,10,3,6,10,4,8,1585171,1,6,2,1,6,3,26, +5,6,4,10,8,8,1604785,14,1310721,2,5,26,2,3,28,1, +6,1,10,28,34,63,38,3,26,1604631,16,1310721,2,13,26,1585245, +1,26,2,1604819,12,10,3,6,10,4,1,40,3,36,1,6, +1,10,28,34,63,38,1,10,28,1604633,14,1,1604711,16,1542657,1585171, +2,26,1310721,2,13,26,2,1310721,2,13,26,2,26,10,3,6, +10,4,8,1585171,1,6,2,1,6,3,26,5,6,4,10,8, +8,1604785,14,1542657,1585171,2,26,-3,10,3,6,10,4,8,1542657,1585171, +2,3,26,26,1585245,1589927,12,10,3,6,10,4,8,1585171,1585171,2, +5,26,10,3,6,10,4,8,1603735,14,1310721,2,3,26,2,1604805, +14,1,1310721,2,15,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721, +2,1835009,40,1604853,16,1966081,1310721,4,1579223,12,1310721,2,4,1,1310721,2, +3,26,4,1585173,2,1310721,2,3,26,2,26,2,1604995,16,1585173,2, +1310721,2,3,26,2,26,2,1310721,2,2,28,1,6,1,10,28, +34,63,38,3,26,1604957,16,1310721,2,3,26,2,1605009,14,1310721,2, +3,26,1,6,2,1,6,3,26,5,6,4,10,8,8,1604871, +14,3,1,10,28,1605009,14,1,1310721,2,5,26,1310721,4,18,1310721, +2,9,28,1310721,4,1310721,2,1835009,40,1605057,16,1966081,1310721,4,1579223,12, +1310721,2,3,26,4,1310721,2,4,1310721,2,2,1310721,2,3,26,2, +26,1310721,2,5,26,4,1310721,2,2,1310721,2,5,26,2,40,1605503, +16,1542657,1310721,2,2,26,2,-3,28,1,6,1,10,28,34,63, +38,3,26,1605451,16,1310721,2,7,26,1542657,1310721,2,2,3,26,26, +2,1590943,12,10,3,6,10,4,8,1310721,2,7,26,2,1,40, +1605261,16,1310721,2,7,26,1542657,1310721,2,2,3,26,26,2,1592751,12, +10,3,6,10,4,8,1417729,1310721,2,7,26,2,26,2,3,28, +1,6,1,10,28,34,63,38,3,26,1605325,16,1310721,2,7,26, +2,1592875,12,8,1605421,14,1417729,1310721,2,7,26,2,26,2,13,28, +1,6,1,10,28,34,63,38,3,26,1605399,16,45,1425921,1310721,2, +7,26,2,26,2,26,1592369,12,8,1605421,14,1425921,1310721,2,7,26, +2,26,2,1592283,12,8,1310721,2,1310721,2,2,5,26,10,3,6, +10,4,8,1605499,14,1542657,1310721,2,2,26,2,1592089,12,8,1310721,2, +1,6,2,1,6,3,26,5,6,4,10,8,8,1605101,14,1, +1605511,14,1,1310721,2,9,26,1310721,4,18,1310721,2,5,28,1310721,4, +1310721,2,1835009,40,1605559,16,1966081,1310721,4,1579223,12,1310721,2,4,1,1310721, +2,3,26,4,1310721,2,3,26,2,1585169,2,40,1605689,16,1547777,1310721, +2,3,26,2,26,2,1310721,2,2,28,1,6,1,10,28,34, +63,38,3,26,1605651,16,1,1605751,14,1310721,2,3,26,1,6,2, +1,6,3,26,5,6,4,10,8,8,1605577,14,1547777,1585169,2,26, +1310721,2,2,10,3,6,10,4,8,1585169,1,6,2,1,6,3, +26,5,6,4,10,8,8,1,1605751,14,1,1310721,2,5,26,1310721, +4,18,1310721,2,5,28,1310721,4,1310721,2,1835009,40,1605799,16,1966081,1310721, +4,1579223,12,1310721,2,4,1585163,2,1310721,2,3,26,4,1310721,2,3, +26,2,1605933,16,1310721,2,3,26,1,6,2,1,6,-1,26,5, +6,4,10,8,8,1546753,1310721,2,3,26,2,26,2,1310721,2,2, +28,1,6,1,10,28,34,63,38,3,26,1605929,16,1310721,2,3, +26,2,1605947,14,1605819,14,3,1,10,28,1605947,14,1,1310721,2,5, +26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,1835009,40,1605995,16, +1966081,1310721,4,1579223,12,1310721,2,4,1585165,2,1310721,2,3,26,4,1310721, +2,3,26,2,1606129,16,1310721,2,3,26,1,6,2,1,6,-1, +26,5,6,4,10,8,8,1547265,1310721,2,3,26,2,26,2,1310721, +2,2,28,1,6,1,10,28,34,63,38,3,26,1606125,16,1310721, +2,3,26,2,1606143,14,1606015,14,3,1,10,28,1606143,14,1,1310721, +2,5,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,1835009,40, +1606191,16,1966081,1310721,4,1579223,12,1310721,2,4,1585167,2,1310721,2,3,26, +4,1310721,2,3,26,2,1606325,16,1310721,2,3,26,1,6,2,1, +6,-1,26,5,6,4,10,8,8,1547457,1310721,2,3,26,2,26, +2,1310721,2,2,28,1,6,1,10,28,34,63,38,3,26,1606321, +16,1310721,2,3,26,2,1606339,14,1606211,14,3,1,10,28,1606339,14, +1,1310721,2,5,26,1310721,4,18,1310721,2,15,28,1310721,4,1310721,2, +1835009,40,1606387,16,1966081,1310721,4,1579223,12,1310721,2,5,26,4,1310721,2, +3,26,4,1310721,2,4,1310721,2,2,1589927,12,1310721,2,7,26,4, +1310721,2,7,26,2,1605765,12,1310721,2,9,26,4,1310721,2,9,26, +2,1,40,1606527,16,1310721,2,9,26,1585163,2,10,3,6,10,4, +8,1585163,1,6,2,1,6,3,26,5,6,4,10,8,8,1546753, +1310721,2,9,26,2,26,1310721,2,7,26,2,10,3,6,10,4, +8,1546881,1310721,2,9,26,2,26,1310721,2,3,26,2,10,3,6, +10,4,8,1310721,2,3,26,2,1,40,1606751,16,1546881,1310721,2,9, +26,2,26,1,1310721,2,3,26,2,28,10,3,6,10,4,8, +1547137,1310721,2,9,26,2,26,1310721,2,3,26,2,10,3,6,10, +4,8,1547009,1310721,2,9,26,2,26,1,10,3,6,10,4,8, +1310721,2,7,26,2,1605525,12,8,1310721,2,9,26,2,1607379,14,1547009, +1310721,2,9,26,2,26,1310721,2,5,26,2,1603681,12,10,3,6, +10,4,8,1547137,1310721,2,9,26,2,26,1585171,2,1547009,1310721,2,9, +26,2,26,2,28,10,3,6,10,4,8,1310721,2,7,26,2, +1605525,12,8,1310721,2,13,26,1310721,2,2,1574039,12,10,3,6,10, +4,8,1585245,2001,26,1310721,2,2,1310721,2,13,26,2,1574491,12,8, +1585245,2001,1310721,2,13,26,2,26,26,123,10,3,6,10,4,8, +1585245,2003,1310721,2,13,26,2,26,26,1,10,3,6,10,4,8, +1310721,2,11,26,1585245,2001,26,1589927,12,10,3,6,10,4,8,1310721, +2,3,26,2,17,40,3,36,1607035,16,3,1607091,14,1310721,2,3, +26,2,11,28,1,6,1,10,28,34,63,38,3,26,1,6, +1,10,28,34,63,38,1,10,28,1607101,16,3,1607157,14,1310721,2, +3,26,2,9,28,1,6,1,10,28,34,63,38,3,26,1, +6,1,10,28,34,63,38,1,10,28,1607167,16,3,1607223,14,1310721, +2,3,26,2,7,28,1,6,1,10,28,34,63,38,3,26, +1,6,1,10,28,34,63,38,1,10,28,1607363,16,1310721,2,11, +26,2,1606157,12,1,40,1607307,16,1547457,1585167,2,26,1310721,2,11,26, +2,10,3,6,10,4,8,1585167,1,6,2,1,6,3,26,5, +6,4,10,8,8,1547585,1310721,2,11,26,2,1606157,12,26,1310721,2, +9,26,2,10,3,6,10,4,8,1310721,2,11,26,2,1605525,12, +8,1310721,2,9,26,2,1607379,14,1,1310721,2,15,26,1310721,4,18, +1310721,2,9,28,1310721,4,1310721,2,1835009,40,1607427,16,1966081,1310721,4,1579223, +12,1310721,2,3,26,4,1310721,2,4,1310721,2,2,1589927,12,1310721,2, +5,26,4,1310721,2,5,26,2,1605961,12,1310721,2,7,26,4,1310721, +2,7,26,2,1,40,1607557,16,1310721,2,7,26,1585165,2,10,3, +6,10,4,8,1585165,1,6,2,1,6,3,26,5,6,4,10, +8,8,1547265,1310721,2,7,26,2,26,1310721,2,5,26,2,10,3, +6,10,4,8,1547329,1310721,2,7,26,2,26,1310721,2,3,26,2, +1603681,12,10,3,6,10,4,8,1547393,1310721,2,7,26,2,26,1585171, +2,1547329,1310721,2,7,26,2,26,2,28,10,3,6,10,4,8, +1310721,2,5,26,2,1605525,12,8,1310721,2,7,26,2,1607713,14,1, +1310721,2,9,26,1310721,4,18,1310721,2,9,28,1310721,4,1310721,2,1835009, +40,1607761,16,1966081,1310721,4,1579223,12,1310721,2,5,26,4,1310721,2,3, +26,4,1310721,2,4,1310721,2,2,1589927,12,11,1310721,2,3,26,2, +1590445,12,1310721,2,7,26,4,1442305,1310721,2,7,26,2,26,3,1310721, +2,5,26,2,34,10,3,6,10,4,8,1310721,2,7,26,2, +1607879,14,1,1310721,2,9,26,1310721,4,18,1310721,2,5,28,1310721,4, +1310721,2,1835009,40,1607927,16,1966081,1310721,4,1579223,12,1310721,2,4,1587491,2, +1310721,2,3,26,4,1310721,2,3,26,2,1587493,2,10,40,1608069,16, +1310721,2,3,26,1,6,2,1,6,-1,26,5,6,4,10,8, +8,1466881,1310721,2,3,26,2,26,2,1310721,2,2,28,1,6,1, +10,28,34,63,38,3,26,1608065,16,1310721,2,3,26,2,1608083,14, +1607947,14,3,1,10,28,1608083,14,1,1310721,2,5,26,1310721,4,18, +1310721,2,5,28,1310721,4,1310721,2,1835009,40,1608131,16,1966081,1310721,4,1579223, +12,1310721,2,4,1587491,2,1310721,2,3,26,4,1587491,2,1025,40,3, +36,1608211,16,1608203,14,116,111,111,32,109,97,110,121,32,108,111, +99,97,108,115,0,1608171,1588719,12,8,1587491,1,6,2,1,6,3, +26,5,6,4,10,8,8,1466881,1310721,2,3,26,2,26,1310721,2, +2,10,3,6,10,4,8,1467905,1310721,2,3,26,2,26,1587497,2, +10,3,6,10,4,8,1468929,1310721,2,3,26,2,26,1587495,2,10, +3,6,10,4,8,1469953,1310721,2,3,26,2,26,1,10,3,6, +10,4,8,1470977,1310721,2,3,26,2,26,1,10,3,6,10,4, +8,1472001,1310721,2,3,26,2,26,1,10,3,6,10,4,8,1473025, +1310721,2,3,26,2,26,1,10,3,6,10,4,8,1474049,1310721,2, +3,26,2,26,1,10,3,6,10,4,8,1556993,1310721,2,3,26, +2,26,1587621,2,10,3,6,10,4,8,1558017,1310721,2,3,26,2, +26,1,10,3,6,10,4,8,1310721,2,3,26,2,1608545,14,1, +1310721,2,5,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009, +40,1608593,16,1966081,1310721,4,1579223,12,1310721,2,4,1469953,1310721,2,2,26, +2,11,28,1,6,1,10,28,34,63,38,3,26,1608671,16,1310721, +2,1470977,1310721,2,2,26,2,10,3,6,10,4,8,1608599,14,1310721, +2,2,1608683,14,1,1310721,2,3,26,1310721,4,18,1310721,2,3,28, +1310721,4,1310721,2,1835009,40,1608731,16,1966081,1310721,4,1579223,12,1310721,2,4, +1469953,1310721,2,2,26,2,1,28,1,6,1,10,28,34,63,38, +1,10,28,1608781,14,1,1310721,2,3,26,1310721,4,18,1310721,2,5, +28,1310721,4,1310721,2,1835009,40,1608829,16,1966081,1310721,4,1579223,12,1310721,2, +4,1472001,1310721,2,2,26,3,10,3,6,10,4,8,1587499,2,1, +6,1,10,28,34,63,38,3,26,1608893,16,1,1609269,14,1310721,2, +3,26,1310721,2,2,1608559,12,10,3,6,10,4,8,1472001,1310721,2, +3,26,2,26,3,10,3,6,10,4,8,1469953,1310721,2,3,26, +2,26,2,9,28,1,6,1,10,28,34,63,38,3,26,1609077, +16,1310721,2,2,1609063,14,117,115,101,100,32,97,102,116,101,114, +32,105,116,32,119,97,115,32,103,105,118,101,110,32,97,119, +97,121,0,1609005,1589603,12,8,1,1609269,14,1469953,1310721,2,3,26,2, +26,2,7,28,1,6,1,10,28,34,63,38,3,26,1609169,16, +1468929,1310721,2,3,26,2,26,2,1587495,2,40,1,6,1,10,28, +34,63,38,1,10,28,1609171,14,1,1609261,16,1468929,1310721,2,3,26, +2,26,1587495,2,10,3,6,10,4,8,1469953,1310721,2,3,26,2, +26,3,10,3,6,10,4,8,1474049,1310721,2,3,26,2,26,3, +10,3,6,10,4,8,1,1609269,14,1,1310721,2,5,26,1310721,4, +18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1609317,16,1966081,1310721,4, +1579223,12,1310721,2,4,1310721,2,2,3,28,1,6,1,10,28,34, +63,38,3,26,1609507,16,1609499,14,97,32,110,101,119,32,111,98, +106,101,99,116,32,99,97,110,39,116,32,98,101,32,117,115, +101,100,32,97,115,32,97,32,112,108,97,105,110,32,118,97, +108,117,101,59,32,98,105,110,100,32,105,116,32,116,111,32, +97,32,110,97,109,101,32,102,105,114,115,116,0,1609361,1589485,12, +8,1310721,2,2,5,40,3,36,1609541,16,1310721,2,2,5,28,1608795, +12,8,1,1609549,14,1,1310721,2,3,26,1310721,4,18,1310721,2,3, +28,1310721,4,1310721,2,1835009,40,1609597,16,1966081,1310721,4,1579223,12,1310721,2, +4,1467905,1310721,2,2,26,2,1593203,12,8,3,1592369,12,8,1587605,2, +1593049,12,8,9,1592369,12,8,1,1609655,14,1,1310721,2,3,26,1310721, +4,18,1310721,2,9,28,1310721,4,1310721,2,1835009,40,1609703,16,1966081,1310721, +4,1579223,12,1310721,2,3,26,4,1310721,2,4,1587491,2,1310721,2,5, +26,4,1587499,2,1,6,1,10,28,34,63,38,3,26,1609767,16, +3,1609813,14,1587503,2,1,6,1,10,28,34,63,38,3,26,1, +6,1,10,28,34,63,38,1,10,28,1609823,16,1,1610383,14,1310721, +2,5,26,2,1587493,2,10,40,1610375,16,1310721,2,5,26,1,6, +2,1,6,-1,26,5,6,4,10,8,8,1468929,1310721,2,5,26, +2,26,2,1310721,2,2,40,1609911,16,1609823,14,1310721,2,7,26,1469953, +1310721,2,5,26,2,26,2,10,3,6,10,4,8,1310721,2,7, +26,2,3,28,1,6,1,10,28,34,63,38,3,26,1610005,16, +1310721,2,5,26,2,1609563,12,8,1610371,14,1310721,2,7,26,2,5, +28,1,6,1,10,28,34,63,38,3,26,1610251,16,1310721,2,5, +26,2,1610239,14,117,32,111,98,106,101,99,116,32,109,117,115, +116,32,108,101,97,118,101,32,116,104,105,115,32,98,108,111, +99,107,32,40,98,108,111,99,107,32,118,97,108,117,101,44, +32,97,115,115,105,103,110,109,101,110,116,32,116,111,32,116, +104,101,32,101,110,99,108,111,115,105,110,103,32,98,108,111, +99,107,44,32,111,114,32,114,101,116,117,114,110,41,0,1610057, +1589603,12,8,1610371,14,1310721,2,7,26,2,7,28,1,6,1,10, +28,34,63,38,3,26,1610371,16,1310721,2,5,26,2,1610363,14,100, +32,111,98,106,101,99,116,32,119,97,115,32,110,101,118,101, +114,32,103,105,118,101,110,32,97,119,97,121,0,1610303,1589603,12, +8,1609823,14,1,1610383,14,1,1310721,2,9,26,1310721,4,18,1310721,2, +3,28,1310721,4,1310721,2,1835009,40,1610431,16,1966081,1310721,4,1579223,12,1310721, +2,4,1310721,2,2,1587491,2,40,1610673,16,1587499,2,1610499,16,1474049,1310721, +2,2,26,2,1,6,1,10,28,34,63,38,1,10,28,1610501, +14,1,1610563,16,1472001,1310721,2,2,26,2,1,6,1,10,28,34, +63,38,3,26,1,6,1,10,28,34,63,38,1,10,28,1610565, +14,1,1610639,16,1466881,1310721,2,2,26,2,1610631,14,109,32,111,98, +106,101,99,116,32,105,115,32,110,101,118,101,114,32,117,115, +101,100,0,1610585,1588983,12,8,1310721,2,1,6,2,1,6,3,26, +5,6,4,10,8,8,1610437,14,1,1610681,14,1,1310721,2,3,26, +1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,1835009,40,1610729,16,1966081, +1310721,4,1579223,12,1310721,2,4,1587493,2,1310721,2,3,26,4,1310721,2, +2,1610397,12,8,1587495,2,3,1609669,12,8,1310721,2,3,26,2,1310721, +2,2,40,1610925,16,1468929,1310721,2,3,26,2,26,2,1587495,2,40, +3,36,1610887,16,1469953,1310721,2,3,26,2,26,9,10,3,6,10, +4,8,1468929,1310721,2,3,26,2,26,1587495,2,3,28,10,3,6, +10,4,8,1310721,2,3,26,1,6,2,1,6,3,26,5,6, +4,10,8,8,1610773,14,1587491,1310721,2,2,10,3,6,10,4,8, +1,1610953,14,1,1310721,2,5,26,1310721,4,18,1310721,2,7,28,1310721, +4,1310721,2,1835009,40,1611001,16,1966081,1310721,4,1579223,12,1587519,2,1310721,2, +4,1587493,2,1310721,2,3,26,4,1310721,2,5,26,1507841,1310721,2,2, +26,10,3,6,10,4,8,1310721,2,5,26,2,1,26,1587493,2, +10,3,6,10,4,8,1310721,2,5,26,2,3,26,1587491,2,10, +3,6,10,4,8,1310721,2,5,26,1310721,2,5,26,2,5,26, +10,3,6,10,4,8,1310721,2,3,26,2,1587491,2,40,1611367,16, +1310721,2,5,26,2,1,26,1469953,1310721,2,3,26,2,26,2,10, +3,6,10,4,8,1310721,2,5,26,2,3,26,1470977,1310721,2,3, +26,2,26,2,10,3,6,10,4,8,1310721,2,5,26,2,5, +26,1468929,1310721,2,3,26,2,26,2,10,3,6,10,4,8,1310721, +2,5,26,1310721,2,5,26,2,7,26,10,3,6,10,4,8, +1310721,2,3,26,1,6,2,1,6,3,26,5,6,4,10,8, +8,1611149,14,1587519,1310721,2,5,26,2,1507841,28,10,3,6,10,4, +8,1587519,2,16001,40,3,36,1611491,16,1611483,14,110,101,115,116,105, +110,103,32,116,111,111,32,100,101,101,112,32,102,111,114,32, +116,104,101,32,97,110,97,108,121,122,101,114,0,1611415,1588719,12, +8,1310721,2,2,1611503,14,1,1310721,2,7,26,1310721,4,18,1310721,2, +3,28,1310721,4,1310721,2,1835009,40,1611551,16,1966081,1310721,4,1579223,12,1310721, +2,4,1587519,1310721,2,2,10,3,6,10,4,8,1,1611585,14,1, +1310721,2,3,26,1310721,4,18,1310721,2,9,28,1310721,4,1310721,2,1835009, +40,1611633,16,1966081,1310721,4,1579223,12,1310721,2,4,1507841,1310721,2,2,26, +1310721,2,3,26,4,1310721,2,3,26,2,1,26,2,1310721,2,5, +26,4,1310721,2,3,26,2,3,26,2,1310721,2,7,26,4,1310721, +2,7,26,2,1587491,2,10,40,1611757,16,1310721,2,7,26,1587491,2, +10,3,6,10,4,8,1310721,2,3,26,1310721,2,3,26,2,5, +26,10,3,6,10,4,8,1310721,2,5,26,2,1310721,2,7,26, +2,40,1612015,16,1469953,1310721,2,5,26,2,26,1310721,2,3,26,2, +1,26,2,10,3,6,10,4,8,1470977,1310721,2,5,26,2,26, +1310721,2,3,26,2,3,26,2,10,3,6,10,4,8,1468929,1310721, +2,5,26,2,26,1310721,2,3,26,2,5,26,2,10,3,6, +10,4,8,1310721,2,3,26,1310721,2,3,26,2,7,26,10,3, +6,10,4,8,1310721,2,5,26,1,6,2,1,6,3,26,5, +6,4,10,8,8,1611791,14,1,1612023,14,1,1310721,2,9,26,1310721, +4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1612071,16,1966081,1310721, +4,1579223,12,1310721,2,4,1310721,2,2,1,28,1,6,1,10,28, +34,63,38,3,26,1612117,16,3,1612169,14,1310721,2,2,9,28,1, +6,1,10,28,34,63,38,3,26,1,6,1,10,28,34,63, +38,1,10,28,1612175,14,1,1310721,2,3,26,1310721,4,18,1310721,2, +11,28,1310721,4,1310721,2,1835009,40,1612223,16,1966081,1310721,4,1579223,12,1310721, +2,4,1507841,1310721,2,2,26,1310721,2,3,26,4,1310721,2,3,26, +2,1,26,2,1310721,2,5,26,4,1310721,2,3,26,2,3,26, +2,1310721,2,7,26,4,1310721,2,7,26,2,1587491,2,10,40,1612347, +16,1310721,2,7,26,1587491,2,10,3,6,10,4,8,1310721,2,3, +26,1310721,2,3,26,2,5,26,10,3,6,10,4,8,1310721,2, +5,26,2,1310721,2,7,26,2,40,1612971,16,1310721,2,9,26,1469953, +1310721,2,5,26,2,26,2,10,3,6,10,4,8,1310721,2,9, +26,2,1612037,12,1612507,16,1310721,2,3,26,2,1,26,2,1612037,12, +1,6,1,10,28,34,63,38,1,10,28,1612509,14,1,1612589,16, +1310721,2,3,26,2,1,26,2,9,28,1,6,1,10,28,34, +63,38,3,26,1612585,16,1469953,1310721,2,5,26,2,26,9,10,3, +6,10,4,8,1612899,14,1310721,2,9,26,2,1310721,2,3,26,2, +1,26,2,28,1,6,1,10,28,34,63,38,1,10,28,1612649, +16,3,1612727,14,1468929,1310721,2,5,26,2,26,2,1310721,2,3,26, +2,5,26,2,28,1,6,1,10,28,34,63,38,1,10,28, +1,6,1,10,28,34,63,38,1,10,28,1612737,16,3,1612881,14, +1310721,2,9,26,2,11,28,1,6,1,10,28,34,63,38,3, +26,1612857,16,1470977,1310721,2,5,26,2,26,2,1310721,2,3,26,2, +3,26,2,28,1,6,1,10,28,34,63,38,1,10,28,1, +6,1,10,28,34,63,38,1,10,28,1612859,14,1,1,6,1, +10,28,34,63,38,1,10,28,1612899,16,1310721,2,5,26,2,1612985, +14,1310721,2,3,26,1310721,2,3,26,2,7,26,10,3,6,10, +4,8,1310721,2,5,26,1,6,2,1,6,3,26,5,6,4, +10,8,8,1612381,14,3,1,10,28,1612985,14,1,1310721,2,11,26, +1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1613033,16,1966081, +1310721,4,1579223,12,1310721,2,4,1310721,2,2,1,28,1,6,1,10, +28,34,63,38,3,26,1613095,16,1613089,14,101,109,112,116,121,0, +1613077,1613421,14,1310721,2,2,3,28,1,6,1,10,28,34,63,38, +3,26,1613143,16,1613137,14,109,0,1613133,1613421,14,1310721,2,2,5,28, +1,6,1,10,28,34,63,38,3,26,1613191,16,1613185,14,117,0, +1613181,1613421,14,1310721,2,2,7,28,1,6,1,10,28,34,63,38, +3,26,1613239,16,1613233,14,100,0,1613229,1613421,14,1310721,2,2,9,28, +1,6,1,10,28,34,63,38,3,26,1613305,16,1613299,14,103,105, +118,101,110,32,97,119,97,121,0,1613277,1613421,14,1310721,2,2,11, +28,1,6,1,10,28,34,63,38,3,26,1613391,16,1613385,14,111, +119,110,101,100,32,98,121,32,97,32,99,111,110,116,97,105, +110,101,114,0,1613343,1613421,14,1613413,14,98,111,114,114,111,119,101, +100,0,1613395,1613421,14,1,1310721,2,3,26,1310721,4,18,1310721,2,7, +28,1310721,4,1310721,2,1835009,40,1613469,16,1966081,1310721,4,1579223,12,1310721,2, +5,26,4,1310721,2,3,26,4,1310721,2,4,1587499,2,1,6,1, +10,28,34,63,38,3,26,1613529,16,3,1613555,14,1588225,2,1,6, +1,10,28,34,63,38,1,10,28,1613565,16,3,1613591,14,1587637,2, +1,6,1,10,28,34,63,38,1,10,28,1613601,16,1,1613821,14, +1588227,12,8,1466881,1310721,2,2,26,2,1573495,12,8,1613635,14,58,32, +0,1613629,1573495,12,8,1310721,2,5,26,2,1573495,12,8,1613707,14,32, +40,111,110,101,32,112,97,116,104,32,108,101,97,118,101,115, +32,105,116,32,0,1613663,1573495,12,8,1310721,2,3,26,2,1612999,12, +1573495,12,8,1613761,14,44,32,97,110,111,116,104,101,114,32,0, +1613739,1573495,12,8,1469953,1310721,2,2,26,2,1612999,12,1573495,12,8,1613799, +14,41,0,1613795,1573495,12,8,1588371,12,8,1,1613821,14,1,1310721,2, +7,26,1310721,4,18,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,1310721,2,5,28,1310721,4,1310721,2,1835009, +40,1614001,16,1966081,1310721,4,1579223,12,1310721,2,3,26,4,1310721,2,4, +1310721,2,3,26,2,1310721,2,2,3,26,2,40,3,36,1614055,16, +1,1614103,14,1310721,2,2,5,7,1310721,2,3,26,2,1310721,2,2, +1,26,2,28,30,26,26,2,1614103,14,1,1310721,2,5,26,1310721, +4,18,1310721,2,5,28,1310721,4,1310721,2,1835009,40,1614151,16,1966081,1310721, +4,1579223,12,1310721,2,3,26,4,1310721,2,4,1310721,2,2,7,7, +1310721,2,3,26,2,1310721,2,2,1,26,2,28,30,26,26,2, +1614215,14,1,1310721,2,5,26,1310721,4,18,1310721,2,5,28,1310721,4, +1310721,2,1835009,40,1614263,16,1966081,1310721,4,1579223,12,1310721,2,3,26,4, +1310721,2,4,1310721,2,2,9,7,1310721,2,3,26,2,1310721,2,2, +1,26,2,28,30,26,26,2,1614327,14,1,1310721,2,5,26,1310721, +4,18,1310721,2,9,28,1310721,4,1310721,2,1835009,40,1614375,16,1966081,1310721, +4,1579223,12,1310721,2,4,1567233,1613835,2,26,1310721,2,3,26,4,1587493, +2,1310721,2,5,26,4,1613835,2,7,26,7,1587491,2,1587493,2,28, +30,26,1572353,1567233,28,40,3,36,1614531,16,1614523,14,116,111,111,32, +109,97,110,121,32,98,114,101,97,107,115,32,102,111,114,32, +116,104,101,32,97,110,97,108,121,122,101,114,0,1614457,1588719,12, +8,1310721,2,3,26,2,1,26,1310721,2,2,10,3,6,10,4, +8,1310721,2,3,26,2,3,26,1587493,2,10,3,6,10,4,8, +1310721,2,3,26,2,5,26,1587491,2,10,3,6,10,4,8,1310721, +2,7,26,1310721,2,3,26,2,7,26,10,3,6,10,4,8, +1310721,2,5,26,2,1587491,2,40,1614875,16,1310721,2,7,26,2,1, +26,1469953,1310721,2,5,26,2,26,2,10,3,6,10,4,8,1310721, +2,7,26,2,3,26,1470977,1310721,2,5,26,2,26,2,10,3, +6,10,4,8,1310721,2,7,26,2,5,26,1468929,1310721,2,5,26, +2,26,2,10,3,6,10,4,8,1310721,2,7,26,1310721,2,7, +26,2,7,26,10,3,6,10,4,8,1310721,2,5,26,1,6, +2,1,6,3,26,5,6,4,10,8,8,1614657,14,1613835,1310721,2, +7,26,2,1567233,28,10,3,6,10,4,8,1310721,2,3,26,2, +1614919,14,1,1310721,2,9,26,1310721,4,18,1310721,2,3,28,1310721,4, +1310721,2,1835009,40,1614967,16,1966081,1310721,4,1579223,12,1310721,2,4,1613965,2, +129,40,3,36,1615065,16,1615057,14,116,111,111,32,109,97,110,121, +32,112,97,116,104,115,32,102,111,114,32,116,104,101,32,97, +110,97,108,121,122,101,114,0,1614993,1588719,12,8,1613837,1613965,2,26, +1310721,2,2,10,3,6,10,4,8,1613965,1,6,2,1,6,3, +26,5,6,4,10,8,8,1,1615127,14,1,1310721,2,3,26,1310721, +4,18,1310721,2,11,28,1310721,4,1310721,2,1835009,40,1615175,16,1966081,1310721, +4,1579223,12,1310721,2,5,26,4,1310721,2,3,26,4,1310721,2,4, +1310721,2,2,1310721,2,5,26,2,1613967,12,1310721,2,7,26,4,1310721, +2,3,26,2,1310721,2,5,26,2,1613967,12,1310721,2,9,26,4, +1310721,2,7,26,2,1612037,12,1615323,16,1310721,2,9,26,2,1612037,12, +1,6,1,10,28,34,63,38,1,10,28,1615325,14,1,1615335,16, +3,1615647,14,1310721,2,7,26,2,1310721,2,9,26,2,28,1,6, +1,10,28,34,63,38,3,26,1615473,16,1310721,2,2,1310721,2,5, +26,2,1614229,12,1310721,2,3,26,2,1310721,2,5,26,2,1614229,12, +28,1,6,1,10,28,34,63,38,3,26,1,6,1,10,28, +34,63,38,1,10,28,1615475,14,1,1615639,16,1310721,2,7,26,2, +11,28,1,6,1,10,28,34,63,38,1,10,28,1615525,16,3, +1615613,14,1310721,2,2,1310721,2,5,26,2,1614117,12,1310721,2,3,26, +2,1310721,2,5,26,2,1614117,12,28,1,6,1,10,28,34,63, +38,3,26,1,6,1,10,28,34,63,38,1,10,28,1,6, +1,10,28,34,63,38,1,10,28,1615641,14,1,1615647,14,1,1310721, +2,11,26,1310721,4,18,1310721,2,21,28,1310721,4,1310721,2,1835009,40, +1615695,16,1966081,1310721,4,1579223,12,1310721,2,3,26,4,1310721,2,4,1587493, +2,1310721,2,5,26,4,1613965,2,1,6,1,10,28,34,63,38, +3,26,1615759,16,1,1617231,14,1310721,2,5,26,2,1310721,2,2,40, +1617223,16,1310721,2,9,26,1613837,1,26,2,10,3,6,10,4,8, +1310721,2,13,26,1310721,2,9,26,2,1310721,2,5,26,2,1613967,12, +10,3,6,10,4,8,1310721,2,15,26,1,10,3,6,10,4, +8,1310721,2,17,26,3,10,3,6,10,4,8,1310721,2,19,26, +3,1,10,28,10,3,6,10,4,8,1310721,2,7,26,1,10, +3,6,10,4,8,1310721,2,7,26,2,1613965,2,40,1616401,16,1310721, +2,11,26,1613837,1310721,2,7,26,2,26,2,1310721,2,5,26,2, +1613967,12,10,3,6,10,4,8,1310721,2,11,26,2,9,28,1, +6,1,10,28,34,63,38,3,26,1616061,16,3,1616117,14,1310721,2, +11,26,2,7,28,1,6,1,10,28,34,63,38,3,26,1, +6,1,10,28,34,63,38,1,10,28,1616143,16,1310721,2,15,26, +3,10,3,6,10,4,8,1310721,2,11,26,2,5,28,1,6, +1,10,28,34,63,38,3,26,1616187,16,3,1616243,14,1310721,2,11, +26,2,13,28,1,6,1,10,28,34,63,38,3,26,1,6, +1,10,28,34,63,38,1,10,28,1616269,16,1310721,2,17,26,1, +10,3,6,10,4,8,1310721,2,9,26,2,1613837,1310721,2,7,26, +2,26,2,1310721,2,5,26,2,1615141,12,1,6,1,10,28,34, +63,38,3,26,1616363,16,1310721,2,19,26,1310721,2,7,26,2,10, +3,6,10,4,8,1310721,2,7,26,1,6,2,1,6,3,26, +5,6,4,10,8,8,1615947,14,1469953,1310721,2,5,26,2,26,1310721, +2,13,26,2,10,3,6,10,4,8,1470977,1310721,2,5,26,2, +26,1310721,2,9,26,2,1310721,2,5,26,2,1614117,12,10,3,6, +10,4,8,1468929,1310721,2,5,26,2,26,1310721,2,9,26,2,1310721, +2,5,26,2,1614229,12,10,3,6,10,4,8,1310721,2,19,26, +2,1,40,1616743,16,1310721,2,13,26,2,1612037,12,1616739,16,1310721,2, +7,26,1,10,3,6,10,4,8,1310721,2,7,26,2,1613965,2, +40,1616739,16,1613837,1310721,2,7,26,2,26,2,1310721,2,5,26,2, +1613967,12,9,28,1,6,1,10,28,34,63,38,3,26,1616701,16, +1469953,1310721,2,5,26,2,26,9,10,3,6,10,4,8,1310721,2, +7,26,1,6,2,1,6,3,26,5,6,4,10,8,8,1616595, +14,1617185,14,1310721,2,15,26,2,1616793,16,1310721,2,17,26,2,1, +6,1,10,28,34,63,38,1,10,28,1616795,14,1,1616831,16,1469953, +1310721,2,5,26,2,26,9,10,3,6,10,4,8,1617185,14,1587499, +2,1616889,16,1588225,2,1,6,1,10,28,34,63,38,3,26,1, +6,1,10,28,34,63,38,1,10,28,1616891,14,1,1616945,16,1587637, +2,1,6,1,10,28,34,63,38,3,26,1,6,1,10,28, +34,63,38,1,10,28,1616947,14,1,1617185,16,1588227,12,8,1466881,1310721, +2,5,26,2,26,2,1573495,12,8,1616989,14,58,32,0,1616983,1573495, +12,8,1310721,2,3,26,2,1573495,12,8,1617061,14,32,40,111,110, +101,32,112,97,116,104,32,108,101,97,118,101,115,32,105,116, +32,0,1617017,1573495,12,8,1310721,2,13,26,2,1612999,12,1573495,12,8, +1617115,14,44,32,97,110,111,116,104,101,114,32,0,1617093,1573495,12, +8,1613837,1310721,2,19,26,2,26,2,1310721,2,5,26,2,1613967,12, +1612999,12,1573495,12,8,1617171,14,41,0,1617167,1573495,12,8,1588371,12,8, +1310721,2,5,26,1,6,2,1,6,3,26,5,6,4,10,8, +8,1615759,14,1,1617231,14,1,1310721,2,21,26,1310721,4,18,1310721,2, +13,28,1310721,4,1310721,2,1835009,40,1617279,16,1966081,1310721,4,1579223,12,1310721, +2,5,26,4,1310721,2,3,26,4,1310721,2,4,1587493,2,1310721,2, +7,26,4,1,1310721,2,9,26,4,1587499,2,1,6,1,10,28, +34,63,38,3,26,1617365,16,1,1617725,14,1310721,2,7,26,2,1310721, +2,3,26,2,40,1617709,16,1310721,2,11,26,1310721,2,2,1310721,2, +7,26,2,1613967,12,10,3,6,10,4,8,1469953,1310721,2,7,26, +2,26,2,9,28,1,6,1,10,28,34,63,38,3,26,1617601, +16,1310721,2,11,26,2,3,28,1,6,1,10,28,34,63,38, +3,26,1617519,16,3,1617575,14,1310721,2,11,26,2,7,28,1,6, +1,10,28,34,63,38,3,26,1,6,1,10,28,34,63,38, +1,10,28,1,6,1,10,28,34,63,38,1,10,28,1617603,14, +1,1617671,16,1310721,2,9,26,1,6,2,1,6,3,26,5,6, +4,10,8,8,1310721,2,5,26,2,1617671,16,1310721,2,7,26,2, +1609563,12,8,1310721,2,7,26,1,6,2,1,6,3,26,5,6, +4,10,8,8,1617365,14,1310721,2,9,26,2,1617725,14,1,1310721,2, +13,26,1310721,4,18,1310721,2,11,28,1310721,4,1310721,2,1835009,40,1617773, +16,1966081,1310721,4,1579223,12,1310721,2,4,1507841,1310721,2,2,26,1310721,2, +3,26,4,1310721,2,3,26,2,1,26,2,1310721,2,5,26,4, +1310721,2,3,26,2,3,26,2,1310721,2,7,26,4,1587499,2,1, +6,1,10,28,34,63,38,3,26,1617885,16,3,1617931,14,1587503,2, +1,6,1,10,28,34,63,38,3,26,1,6,1,10,28,34, +63,38,1,10,28,1617941,16,1,1619307,14,1310721,2,7,26,2,1587491, +2,10,40,1617987,16,1310721,2,7,26,1587491,2,10,3,6,10,4, +8,1310721,2,5,26,2,1310721,2,7,26,2,40,1619299,16,1310721,2, +9,26,1469953,1310721,2,5,26,2,26,2,10,3,6,10,4,8, +1310721,2,9,26,2,1612037,12,1618121,16,1310721,2,3,26,2,1310721,2, +5,26,2,1613967,12,1612037,12,1,6,1,10,28,34,63,38,1, +10,28,1618123,14,1,1618211,16,1310721,2,3,26,2,1310721,2,5,26, +2,1613967,12,9,28,1,6,1,10,28,34,63,38,3,26,1618207, +16,1469953,1310721,2,5,26,2,26,9,10,3,6,10,4,8,1619261, +14,1310721,2,9,26,2,1310721,2,3,26,2,1310721,2,5,26,2, +1613967,12,28,1,6,1,10,28,34,63,38,1,10,28,1618279,16, +3,1618365,14,1468929,1310721,2,5,26,2,26,2,1310721,2,3,26,2, +1310721,2,5,26,2,1614229,12,28,1,6,1,10,28,34,63,38, +1,10,28,1,6,1,10,28,34,63,38,1,10,28,1618375,16, +3,1618527,14,1310721,2,9,26,2,11,28,1,6,1,10,28,34, +63,38,3,26,1618503,16,1470977,1310721,2,5,26,2,26,2,1310721,2, +3,26,2,1310721,2,5,26,2,1614117,12,28,1,6,1,10,28, +34,63,38,1,10,28,1,6,1,10,28,34,63,38,1,10, +28,1618505,14,1,1,6,1,10,28,34,63,38,1,10,28,1619261, +16,1310721,2,9,26,2,9,28,1,6,1,10,28,34,63,38, +3,26,1618575,16,3,1618631,14,1310721,2,9,26,2,7,28,1,6, +1,10,28,34,63,38,3,26,1,6,1,10,28,34,63,38, +1,10,28,1618865,16,1310721,2,5,26,2,1618853,14,105,115,32,103, +105,118,101,110,32,97,119,97,121,32,105,110,115,105,100,101, +32,97,32,108,111,111,112,32,116,104,97,116,32,99,111,109, +101,115,32,98,97,99,107,32,97,114,111,117,110,100,32,116, +111,32,105,116,59,32,103,105,118,101,32,105,116,32,97,119, +97,121,32,111,110,32,97,32,112,97,116,104,32,116,104,97, +116,32,108,101,97,118,101,115,32,116,104,101,32,108,111,111, +112,0,1618649,1589603,12,8,1619261,14,1588225,2,1,6,1,10,28,34, +63,38,3,26,1618943,16,1587637,2,1,6,1,10,28,34,63,38, +3,26,1,6,1,10,28,34,63,38,1,10,28,1618945,14,1, +1619261,16,1588227,12,8,1466881,1310721,2,5,26,2,26,2,1573495,12,8, +1619155,14,58,32,99,104,97,110,103,101,115,32,115,116,97,116, +101,32,105,110,115,105,100,101,32,97,32,108,111,111,112,59, +32,101,97,99,104,32,112,97,115,115,32,104,97,115,32,116, +111,32,108,101,97,118,101,32,105,116,32,97,115,32,105,116, +32,102,111,117,110,100,32,105,116,32,40,105,116,32,119,101, +110,116,32,102,114,111,109,32,0,1618981,1573495,12,8,1310721,2,3, +26,2,1310721,2,5,26,2,1613967,12,1612999,12,1573495,12,8,1619211,14, +32,116,111,32,0,1619201,1573495,12,8,1310721,2,9,26,2,1612999,12, +1573495,12,8,1619247,14,41,0,1619243,1573495,12,8,1588371,12,8,1310721,2, +5,26,1,6,2,1,6,3,26,5,6,4,10,8,8,1617987, +14,1,1619307,14,1,1310721,2,11,26,1310721,4,18,1310721,2,19,28, +1310721,4,1310721,2,1835009,40,1619355,16,1966081,1310721,4,1579223,12,1310721,2,11, +26,4,1310721,2,9,26,4,1310721,2,7,26,4,1310721,2,5,26, +4,1310721,2,3,26,4,1310721,2,4,1310721,2,13,26,1610967,12,10, +3,6,10,4,8,1613965,1,10,3,6,10,4,8,1310721,2,3, +26,2,1619481,16,1507841,1310721,2,2,26,1614933,12,8,1310721,2,7,26, +2,1619515,16,1507841,1310721,2,13,26,2,26,1614933,12,8,1310721,2,9, +26,2,1310721,2,11,26,2,1615661,12,8,1310721,2,7,26,2,1619587, +16,1507841,1310721,2,13,26,2,26,1310721,2,9,26,2,3,1617245,12, +8,1310721,2,15,26,1,10,3,6,10,4,8,1310721,2,3,26, +2,1619669,16,1310721,2,15,26,1507841,1310721,2,2,26,1310721,2,9,26, +2,1,1617245,12,10,3,6,10,4,8,1310721,2,15,26,2,1619787, +16,1310721,2,17,26,1592669,12,10,3,6,10,4,8,15,1592369,12, +8,1310721,2,5,26,2,1587483,2,1592451,12,8,1507841,1310721,2,2,26, +1310721,2,9,26,2,3,1617245,12,8,1310721,2,17,26,2,1587483,2, +1592451,12,8,1619807,14,1310721,2,5,26,2,1587483,2,1592451,12,8,1587503, +1310721,2,3,26,2,1619829,16,3,1619861,14,1310721,2,7,26,2,1, +6,1,10,28,34,63,38,1,10,28,10,3,6,10,4,8, +1,1619881,14,1,1310721,2,19,26,1310721,4,18,1310721,2,9,28,1310721, +4,1310721,2,1835009,40,1619929,16,1966081,1310721,4,1579223,12,1310721,2,3,26, +4,1310721,2,4,1587499,2,1,6,1,10,28,34,63,38,3,26, +1619983,16,1310721,2,2,1620953,14,1310721,2,2,5,40,1620099,16,1620081,14, +111,110,108,121,32,97,32,110,97,109,101,100,32,111,119,110, +101,100,32,111,98,106,101,99,116,32,99,97,110,32,98,101, +32,114,101,99,97,115,116,0,1620001,1589485,12,8,1310721,2,2,1620953, +14,1310721,2,5,26,1310721,2,2,5,28,10,3,6,10,4,8, +1310721,2,7,26,1469953,1310721,2,5,26,2,26,2,10,3,6,10, +4,8,1472001,1310721,2,5,26,2,26,3,10,3,6,10,4,8, +1310721,2,7,26,2,11,28,1,6,1,10,28,34,63,38,3, +26,1620363,16,1310721,2,5,26,2,1620351,14,98,101,108,111,110,103, +115,32,116,111,32,97,32,99,111,110,116,97,105,110,101,114, +59,32,114,101,99,97,115,116,32,116,104,101,32,99,111,110, +116,97,105,110,101,114,32,105,110,115,116,101,97,100,0,1620245, +1589603,12,8,1620941,14,1310721,2,7,26,2,13,28,1,6,1,10, +28,34,63,38,3,26,1620491,16,1310721,2,5,26,2,1620479,14,105, +115,32,98,111,114,114,111,119,101,100,32,97,110,100,32,99, +97,110,39,116,32,98,101,32,114,101,99,97,115,116,0,1620415, +1589603,12,8,1620941,14,1310721,2,7,26,2,1,28,1,6,1,10, +28,34,63,38,3,26,1620599,16,1310721,2,5,26,2,1620587,14,105, +115,110,39,116,32,97,110,32,111,119,110,101,100,32,111,98, +106,101,99,116,0,1620543,1589603,12,8,1620941,14,1310721,2,7,26,2, +9,28,1,6,1,10,28,34,63,38,3,26,1620721,16,1310721,2, +5,26,2,1620709,14,117,115,101,100,32,97,102,116,101,114,32, +105,116,32,119,97,115,32,103,105,118,101,110,32,97,119,97, +121,0,1620651,1589603,12,8,1620941,14,1310721,2,3,26,2,3,28,1, +6,1,10,28,34,63,38,3,26,1620821,16,1310721,2,7,26,2, +3,28,1,6,1,10,28,34,63,38,1,10,28,1,6,1, +10,28,34,63,38,1,10,28,1620823,14,1,1620905,16,1310721,2,5, +26,2,1620893,14,99,97,110,39,116,32,98,101,32,114,101,99, +97,115,116,32,98,97,99,107,32,116,111,32,109,0,1620841,1589603, +12,8,1620941,14,1469953,1310721,2,5,26,2,26,1310721,2,3,26,2, +10,3,6,10,4,8,1310721,2,2,1620953,14,1,1310721,2,9,26, +1310721,4,18,1310721,2,9,28,1310721,4,1310721,2,1835009,40,1621001,16,1966081, +1310721,4,1579223,12,1310721,2,3,26,4,1310721,2,4,1587499,2,1,6, +1,10,28,34,63,38,3,26,1621051,16,1,1622485,14,1310721,2,2, +3,28,1,6,1,10,28,34,63,38,3,26,1621295,16,1310721,2, +3,26,2,5,28,1,6,1,10,28,34,63,38,1,10,28, +1621289,16,1621281,14,97,32,110,101,119,32,111,98,106,101,99,116, +32,99,97,110,32,111,110,108,121,32,103,111,32,115,116,114, +97,105,103,104,116,32,116,111,32,97,32,100,32,112,97,114, +97,109,101,116,101,114,59,32,98,105,110,100,32,105,116,32, +116,111,32,97,32,110,97,109,101,32,102,105,114,115,116,0, +1621129,1589485,12,8,1,1622485,14,1310721,2,2,5,40,1621315,16,3,1621371, +14,1310721,2,2,5,28,1608697,12,1,6,1,10,28,34,63,38, +3,26,1,6,1,10,28,34,63,38,1,10,28,1621381,16,1, +1622485,14,1310721,2,5,26,1310721,2,2,5,28,10,3,6,10,4, +8,1310721,2,7,26,1469953,1310721,2,5,26,2,26,2,10,3,6, +10,4,8,1469953,1310721,2,5,26,2,1608559,12,26,2,9,28,1, +6,1,10,28,34,63,38,3,26,1621581,16,1310721,2,5,26,2, +1621567,14,117,115,101,100,32,97,102,116,101,114,32,105,116,32, +119,97,115,32,103,105,118,101,110,32,97,119,97,121,0,1621509, +1589603,12,8,1,1622485,14,1310721,2,3,26,2,3,10,40,3,36, +1621809,16,1310721,2,7,26,2,7,28,1,6,1,10,28,34,63, +38,3,26,1621787,16,1310721,2,5,26,2,1621773,14,105,115,32,100, +59,32,112,97,115,115,32,105,116,32,116,111,32,97,32,100, +32,112,97,114,97,109,101,116,101,114,32,40,108,101,110,100, +105,110,103,32,105,116,32,119,111,117,108,100,32,108,111,115, +101,32,105,116,41,0,1621657,1589603,12,8,1,1622485,14,1310721,2,5, +26,2,1608795,12,8,1,1622485,14,1310721,2,7,26,2,7,28,1, +6,1,10,28,34,63,38,3,26,1621909,16,1469953,1310721,2,5,26, +2,26,9,10,3,6,10,4,8,1472001,1310721,2,5,26,2,26, +3,10,3,6,10,4,8,1,1622485,14,1310721,2,7,26,2,3, +28,1,6,1,10,28,34,63,38,3,26,1622037,16,1310721,2,5, +26,2,1622025,14,105,115,32,109,59,32,119,114,105,116,101,32, +100,42,120,32,116,111,32,103,105,118,101,32,105,116,32,97, +119,97,121,0,1621961,1589603,12,8,1622477,14,1310721,2,7,26,2,5, +28,1,6,1,10,28,34,63,38,3,26,1622213,16,1310721,2,5, +26,2,1622201,14,105,115,32,117,59,32,119,114,105,116,101,32, +100,42,120,32,116,111,32,103,105,118,101,32,105,116,32,97, +119,97,121,32,105,110,115,116,101,97,100,32,111,102,32,114, +101,116,117,114,110,105,110,103,32,105,116,0,1622089,1589603,12,8, +1622477,14,1310721,2,7,26,2,11,28,1,6,1,10,28,34,63, +38,3,26,1622383,16,1310721,2,5,26,2,1622371,14,98,101,108,111, +110,103,115,32,116,111,32,97,32,99,111,110,116,97,105,110, +101,114,32,97,110,100,32,99,97,110,39,116,32,98,101,32, +103,105,118,101,110,32,97,119,97,121,32,97,108,111,110,101, +0,1622265,1589603,12,8,1622477,14,1310721,2,5,26,2,1622469,14,105,115, +32,98,111,114,114,111,119,101,100,32,97,110,100,32,99,97, +110,39,116,32,98,101,32,103,105,118,101,110,32,97,119,97, +121,0,1622397,1589603,12,8,1,1622485,14,1,1310721,2,9,26,1310721,4, +18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1622533,16,1966081,1310721,4, +1579223,12,1310721,2,4,1467905,1310721,2,2,26,2,1593203,12,8,3,1592369, +12,8,1556993,1310721,2,2,26,2,1,26,2,23,28,1,6,1, +10,28,34,63,38,3,26,1622635,16,5,1592283,12,8,27,1592369,12, +8,3,1592369,12,8,1587607,2,1593049,12,8,1,1622653,14,1,1310721,2, +3,26,1310721,4,18,1310721,2,15,28,1310721,4,1310721,2,1835009,40,1622701, +16,1966081,1310721,4,1579223,12,1310721,2,5,26,4,1310721,2,3,26,4, +1310721,2,4,1587499,2,1,6,1,10,28,34,63,38,3,26,1622761, +16,1,1626207,14,1310721,2,9,26,3,1,10,28,10,3,6,10, +4,8,1310721,2,5,26,2,5,40,3,36,1622855,16,1310721,2,5, +26,2,5,28,1608697,12,1,6,1,10,28,34,63,38,1,10, +28,1622857,14,1,1622895,16,1310721,2,9,26,1310721,2,5,26,2,5, +28,10,3,6,10,4,8,1310721,2,9,26,2,1,40,3,36, +1622987,16,1469953,1310721,2,9,26,2,1608559,12,26,2,9,28,1,6, +1,10,28,34,63,38,3,26,1,6,1,10,28,34,63,38, +1,10,28,1622989,14,1,1623079,16,1310721,2,9,26,2,1623065,14,117, +115,101,100,32,97,102,116,101,114,32,105,116,32,119,97,115, +32,103,105,118,101,110,32,97,119,97,121,0,1623007,1589603,12,8, +1,1626207,14,1310721,2,2,3,28,1,6,1,10,28,34,63,38, +3,26,1624551,16,1310721,2,7,26,1469953,1310721,2,3,26,2,26,2, +10,3,6,10,4,8,1310721,2,9,26,2,1,40,1623229,16,1310721, +2,5,26,2,3,28,1,6,1,10,28,34,63,38,1,10, +28,1,6,1,10,28,34,63,38,1,10,28,1623231,14,1,1623435, +16,1310721,2,7,26,2,1612037,12,1,6,1,10,28,34,63,38, +3,26,1623401,16,1310721,2,3,26,2,1623389,14,115,116,105,108,108, +32,111,119,110,115,32,97,110,32,111,98,106,101,99,116,59, +32,111,118,101,114,119,114,105,116,105,110,103,32,105,116,32, +119,111,117,108,100,32,108,101,97,107,32,105,116,0,1623287,1589603, +12,8,1623429,14,1469953,1310721,2,3,26,2,26,1,10,3,6,10, +4,8,1,1626207,14,1310721,2,7,26,2,1612037,12,1,6,1,10, +28,34,63,38,3,26,1623603,16,1310721,2,3,26,2,1623589,14,115, +116,105,108,108,32,111,119,110,115,32,97,110,32,111,98,106, +101,99,116,59,32,111,118,101,114,119,114,105,116,105,110,103, +32,105,116,32,119,111,117,108,100,32,108,101,97,107,32,105, +116,0,1623487,1589603,12,8,1,1626207,14,1310721,2,5,26,2,3,28, +1,6,1,10,28,34,63,38,3,26,1623801,16,1469953,1310721,2,3, +26,2,26,3,10,3,6,10,4,8,1472001,1310721,2,3,26,2, +26,1,10,3,6,10,4,8,1474049,1310721,2,3,26,2,26,3, +10,3,6,10,4,8,1558017,1310721,2,3,26,2,26,2,31,32, +1623795,16,1469953,1310721,2,3,26,2,26,1558017,1310721,2,3,26,2,26, +2,31,32,10,3,6,10,4,8,1,1626207,14,1310721,2,13,26, +1469953,1310721,2,9,26,2,26,2,10,3,6,10,4,8,1310721,2, +13,26,2,5,28,1,6,1,10,28,34,63,38,3,26,1623959, +16,1468929,1310721,2,3,26,2,26,2,1468929,1310721,2,9,26,2,26, +2,3,28,28,1,6,1,10,28,34,63,38,3,26,1,6, +1,10,28,34,63,38,1,10,28,1623961,14,1,1624181,16,1469953,1310721, +2,3,26,2,26,3,10,3,6,10,4,8,1472001,1310721,2,3, +26,2,26,1,10,3,6,10,4,8,1474049,1310721,2,3,26,2, +26,3,10,3,6,10,4,8,1469953,1310721,2,9,26,2,26,9, +10,3,6,10,4,8,1472001,1310721,2,9,26,2,26,3,10,3, +6,10,4,8,1558017,1310721,2,3,26,2,26,2,31,32,1624175,16, +1469953,1310721,2,3,26,2,26,1558017,1310721,2,3,26,2,26,2,31, +32,10,3,6,10,4,8,1,1626207,14,1310721,2,13,26,2,5, +28,1,6,1,10,28,34,63,38,3,26,1624401,16,1310721,2,9, +26,2,1624389,14,105,115,32,117,59,32,105,116,32,99,97,110, +32,111,110,108,121,32,98,101,32,104,97,110,100,101,100,32, +116,111,32,97,32,118,97,114,105,97,98,108,101,32,111,102, +32,116,104,101,32,101,110,99,108,111,115,105,110,103,32,98, +108,111,99,107,44,32,111,114,32,114,101,116,117,114,110,101, +100,0,1624233,1589603,12,8,1624545,14,1310721,2,9,26,2,1624537,14,105, +115,32,97,110,32,111,119,110,101,100,32,111,98,106,101,99, +116,32,97,110,100,32,99,97,110,39,116,32,98,101,32,99, +111,112,105,101,100,32,105,110,116,111,32,97,110,111,116,104, +101,114,32,118,97,114,105,97,98,108,101,0,1624415,1589603,12,8, +1,1626207,14,1310721,2,2,7,28,1,6,1,10,28,34,63,38, +3,26,1625875,16,1310721,2,11,26,1310721,2,3,26,2,1608559,12,10, +3,6,10,4,8,1310721,2,7,26,1469953,1310721,2,11,26,2,26, +2,10,3,6,10,4,8,1310721,2,3,26,2,1608795,12,8,1310721, +2,5,26,2,3,28,1,6,1,10,28,34,63,38,3,26, +1624731,16,1310721,2,3,26,2,1622499,12,8,1,1626207,14,1310721,2,9, +26,2,1,40,1624755,16,1,1626207,14,1310721,2,13,26,1469953,1310721,2, +9,26,2,26,2,10,3,6,10,4,8,1310721,2,13,26,2, +11,28,1,6,1,10,28,34,63,38,3,26,1624917,16,1310721,2, +9,26,2,1624905,14,97,108,114,101,97,100,121,32,98,101,108, +111,110,103,115,32,116,111,32,97,32,99,111,110,116,97,105, +110,101,114,0,1624843,1589603,12,8,1625869,14,1310721,2,13,26,2,13, +28,1,6,1,10,28,34,63,38,3,26,1625045,16,1310721,2,9, +26,2,1625033,14,105,115,32,98,111,114,114,111,119,101,100,32, +97,110,100,32,99,97,110,39,116,32,98,101,32,115,116,111, +114,101,100,0,1624969,1589603,12,8,1625869,14,1310721,2,7,26,2,13, +28,1,6,1,10,28,34,63,38,3,26,1625371,16,1310721,2,13, +26,2,5,28,1,6,1,10,28,34,63,38,3,26,1625295,16, +1310721,2,9,26,2,1625283,14,105,115,32,117,59,32,105,116,32, +104,97,115,32,116,111,32,108,101,97,118,101,32,116,104,114, +111,117,103,104,32,116,104,101,32,98,108,111,99,107,32,97, +98,111,118,101,44,32,110,111,116,32,105,110,116,111,32,97, +32,98,111,114,114,111,119,101,100,32,111,98,106,101,99,116, +0,1625135,1589603,12,8,1625367,14,1469953,1310721,2,9,26,2,26,9,10, +3,6,10,4,8,1472001,1310721,2,9,26,2,26,3,10,3,6, +10,4,8,1310721,2,3,26,2,1622499,12,8,1625869,14,1310721,2,13, +26,2,1310721,2,7,26,2,28,1,6,1,10,28,34,63,38, +1,10,28,1625577,16,1310721,2,9,26,2,1625565,14,105,116,115,32, +116,97,103,32,100,111,101,115,110,39,116,32,109,97,116,99, +104,32,105,116,115,32,99,111,110,116,97,105,110,101,114,39, +115,32,40,98,111,116,104,32,104,97,118,101,32,116,111,32, +98,101,32,109,44,32,117,32,111,114,32,100,41,0,1625433,1589603, +12,8,1625869,14,1468929,1310721,2,9,26,2,26,2,1468929,1310721,2,11, +26,2,26,2,40,1625761,16,1310721,2,9,26,2,1625749,14,108,105, +118,101,115,32,105,110,32,97,110,32,111,117,116,101,114,32, +98,108,111,99,107,32,116,104,97,110,32,116,104,101,32,99, +111,110,116,97,105,110,101,114,32,105,116,39,115,32,115,116, +111,114,101,100,32,105,110,116,111,0,1625629,1589603,12,8,1625869,14, +1469953,1310721,2,9,26,2,26,11,10,3,6,10,4,8,1470977,1310721, +2,9,26,2,26,1310721,2,3,26,2,10,3,6,10,4,8, +1472001,1310721,2,9,26,2,26,3,10,3,6,10,4,8,1310721,2, +3,26,2,1622499,12,8,1,1626207,14,1310721,2,9,26,2,1,40, +3,36,1626199,16,1469953,1310721,2,9,26,2,26,2,7,28,1,6, +1,10,28,34,63,38,3,26,1626001,16,1469953,1310721,2,9,26,2, +26,9,10,3,6,10,4,8,1472001,1310721,2,9,26,2,26,3, +10,3,6,10,4,8,1626199,14,1310721,2,9,26,2,1626191,14,109, +117,115,116,32,98,101,32,100,32,116,111,32,98,101,32,115, +116,111,114,101,100,32,105,110,32,97,32,103,108,111,98,97, +108,32,111,114,32,105,110,32,117,110,116,114,97,99,107,101, +100,32,109,101,109,111,114,121,32,40,116,104,101,32,99,111, +108,108,101,99,116,111,114,32,116,97,107,101,115,32,105,116, +32,111,118,101,114,41,0,1626015,1589603,12,8,1,1626207,14,1,1310721, +2,15,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,1835009,40, +1626255,16,1966081,1310721,4,1579223,12,1310721,2,4,1587499,2,1,6,1,10, +28,34,63,38,3,26,1626295,16,1,1627221,14,1310721,2,3,26,3, +1,10,28,10,3,6,10,4,8,1310721,2,2,5,40,3,36, +1626381,16,1310721,2,2,5,28,1608697,12,1,6,1,10,28,34,63, +38,1,10,28,1626383,14,1,1626417,16,1310721,2,3,26,1310721,2,2, +5,28,10,3,6,10,4,8,1310721,2,3,26,2,1,40,3, +36,1626509,16,1469953,1310721,2,3,26,2,1608559,12,26,2,9,28,1, +6,1,10,28,34,63,38,3,26,1,6,1,10,28,34,63, +38,1,10,28,1626511,14,1,1626601,16,1310721,2,3,26,2,1626587,14, +117,115,101,100,32,97,102,116,101,114,32,105,116,32,119,97, +115,32,103,105,118,101,110,32,97,119,97,121,0,1626529,1589603,12, +8,1,1627221,14,1587501,2,1626857,16,1310721,2,3,26,2,1,40,3, +36,1626851,16,1469953,1310721,2,3,26,2,26,2,5,28,1,6,1, +10,28,34,63,38,3,26,1626735,16,1469953,1310721,2,3,26,2,26, +9,10,3,6,10,4,8,1472001,1310721,2,3,26,2,26,3,10, +3,6,10,4,8,1626851,14,1310721,2,3,26,2,1626843,14,104,97, +115,32,116,111,32,98,101,32,117,32,116,111,32,98,101,32, +114,101,116,117,114,110,101,100,59,32,119,114,105,116,101,32, +39,114,101,116,117,114,110,32,117,42,120,39,0,1626749,1589603,12, +8,1,1627221,14,1310721,2,2,3,28,1,6,1,10,28,34,63, +38,3,26,1627021,16,1627009,14,114,101,116,117,114,110,115,32,97, +32,110,101,119,32,111,98,106,101,99,116,59,32,103,105,118, +101,32,116,104,101,32,102,117,110,99,116,105,111,110,32,97, +32,46,117,32,114,101,116,117,114,110,32,116,121,112,101,0, +1626895,1589485,12,8,1627213,14,1310721,2,3,26,2,1,40,3,36,1627213, +16,1310721,2,3,26,2,1627205,14,105,115,32,111,119,110,101,100, +32,97,110,100,32,99,97,110,39,116,32,98,101,32,114,101, +116,117,114,110,101,100,32,102,114,111,109,32,97,32,102,117, +110,99,116,105,111,110,32,119,104,111,115,101,32,114,101,116, +117,114,110,32,116,121,112,101,32,105,115,110,39,116,32,46, +117,0,1627057,1589603,12,8,1,1627221,14,1,1310721,2,5,26,1310721,4, +18,1310721,2,5,28,1310721,4,1310721,2,1835009,40,1627269,16,1966081,1310721,4, +1579223,12,1310721,2,4,1587499,2,1,6,1,10,28,34,63,38,3, +26,1627309,16,1,1627937,14,1310721,2,2,3,28,1,6,1,10,28, +34,63,38,3,26,1627349,16,3,1627937,14,1310721,2,2,5,40,1627369, +16,3,1627425,14,1310721,2,2,5,28,1608697,12,1,6,1,10,28, +34,63,38,3,26,1,6,1,10,28,34,63,38,1,10,28, +1627435,16,1,1627937,14,1310721,2,3,26,1310721,2,2,5,28,10,3, +6,10,4,8,1469953,1310721,2,3,26,2,26,2,5,28,1,6, +1,10,28,34,63,38,3,26,1627577,16,1468929,1310721,2,3,26,2, +26,2,1587495,2,28,1,6,1,10,28,34,63,38,3,26,1, +6,1,10,28,34,63,38,1,10,28,1627579,14,1,1627645,16,1469953, +1310721,2,3,26,2,26,9,10,3,6,10,4,8,1472001,1310721,2, +3,26,2,26,3,10,3,6,10,4,8,3,1627937,14,1469953,1310721, +2,3,26,2,26,2,5,28,1,6,1,10,28,34,63,38, +3,26,1627809,16,1310721,2,3,26,2,1627797,14,111,110,108,121,32, +97,32,117,32,111,98,106,101,99,116,32,111,102,32,116,104, +105,115,32,98,108,111,99,107,32,99,97,110,32,98,101,32, +105,116,115,32,118,97,108,117,101,0,1627703,1589603,12,8,1627929,14, +1310721,2,3,26,2,1627921,14,104,97,115,32,116,111,32,98,101, +32,117,32,116,111,32,98,101,32,104,97,110,100,101,100,32, +117,112,32,97,115,32,116,104,101,32,98,108,111,99,107,39, +115,32,118,97,108,117,101,0,1627823,1589603,12,8,1,1627937,14,1, +1310721,2,5,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009, +40,1627985,16,1966081,1310721,4,1579223,12,1310721,2,4,1310721,2,2,3,28, +1,6,1,10,28,34,63,38,3,26,1628141,16,1628129,14,97,32, +110,101,119,32,111,98,106,101,99,116,32,40,111,114,32,97, +32,117,32,114,101,115,117,108,116,41,32,99,97,110,39,116, +32,98,101,32,116,104,114,111,119,110,32,97,119,97,121,0, +1628029,1589485,12,8,1628175,14,1310721,2,2,5,40,3,36,1628175,16,1310721, +2,2,5,28,1608795,12,8,1,1628183,14,1,1310721,2,3,26,1310721, +4,18,1310721,2,5,28,1310721,4,1310721,2,1835009,40,1628231,16,1966081,1310721, +4,1579223,12,1310721,2,4,1587617,2,1310721,2,3,26,4,1587617,2,1310721, +2,2,26,1310721,40,3,36,1628331,16,1628323,14,116,104,101,32,116, +121,112,101,32,97,114,101,97,32,105,115,32,102,117,108,108, +0,1628279,1588719,12,8,1587617,1587617,2,1310721,2,2,26,10,3,6,10, +4,8,1310721,2,3,26,2,1,1310721,2,2,1574703,12,8,1310721,2, +3,26,2,1628397,14,1,1310721,2,5,26,1310721,4,18,1310721,2,11, +28,1310721,4,1310721,2,1835009,40,1628445,16,1966081,1310721,4,1579223,12,1310721,2, +7,26,4,1310721,2,5,26,4,1310721,2,3,26,4,1310721,2,4, +15,1310721,2,7,26,2,26,1628197,12,1310721,2,9,26,4,1310721,2, +9,26,2,1,26,1310721,2,2,10,3,6,10,4,8,1310721,2, +9,26,2,3,26,1310721,2,3,26,2,10,3,6,10,4,8, +1310721,2,9,26,2,5,26,1310721,2,5,26,2,10,3,6,10, +4,8,1587619,2,2049,40,3,36,1628671,16,1628663,14,116,111,111,32, +109,97,110,121,32,116,121,112,101,115,0,1628633,1588719,12,8,1277953, +1587619,2,26,1310721,2,9,26,2,10,3,6,10,4,8,1587619,1, +6,2,1,6,3,26,5,6,4,10,8,8,1310721,2,9,26, +2,1628745,14,1,1310721,2,11,26,1310721,4,18,1310721,2,7,28,1310721, +4,1310721,2,1835009,40,1628793,16,1966081,1310721,4,1579223,12,1310721,2,4,1, +1310721,2,3,26,4,1310721,2,3,26,2,1587619,2,40,1629039,16,1310721, +2,5,26,1277953,1310721,2,3,26,2,26,2,10,3,6,10,4, +8,1310721,2,5,26,2,1,26,2,11,28,1,6,1,10,28, +34,63,38,3,26,1628981,16,1310721,2,5,26,2,9,26,2,1310721, +2,2,28,1,6,1,10,28,34,63,38,3,26,1,6,1, +10,28,34,63,38,1,10,28,1628983,14,1,1629001,16,1310721,2,5, +26,2,1629119,14,1310721,2,3,26,1,6,2,1,6,3,26,5, +6,4,10,8,8,1628811,14,1310721,2,5,26,11,3,1,1,1628411, +12,10,3,6,10,4,8,1310721,2,5,26,2,9,26,1310721,2, +2,10,3,6,10,4,8,1310721,2,5,26,2,1629119,14,1,1310721, +2,7,26,1310721,4,18,1310721,2,9,28,1310721,4,1310721,2,1835009,40, +1629167,16,1966081,1310721,4,1579223,12,1310721,2,3,26,4,1310721,2,4,1, +1310721,2,5,26,4,1310721,2,5,26,2,1587619,2,40,1629503,16,1310721, +2,7,26,1277953,1310721,2,5,26,2,26,2,10,3,6,10,4, +8,1310721,2,7,26,2,1,26,2,13,28,1,6,1,10,28, +34,63,38,3,26,1629365,16,1310721,2,7,26,2,9,26,2,1310721, +2,2,28,1,6,1,10,28,34,63,38,3,26,1,6,1, +10,28,34,63,38,1,10,28,1629367,14,1,1629445,16,1310721,2,7, +26,2,7,26,2,1310721,2,3,26,2,28,1,6,1,10,28, +34,63,38,3,26,1,6,1,10,28,34,63,38,1,10,28, +1629447,14,1,1629465,16,1310721,2,7,26,2,1629639,14,1310721,2,5,26, +1,6,2,1,6,3,26,5,6,4,10,8,8,1629195,14,1310721, +2,7,26,13,1310721,2,3,26,2,1310721,2,2,1633401,12,30,1, +1,1628411,12,10,3,6,10,4,8,1310721,2,7,26,2,9,26, +1310721,2,2,10,3,6,10,4,8,1310721,2,7,26,2,7,26, +1310721,2,3,26,2,10,3,6,10,4,8,1310721,2,7,26,2, +1629639,14,1,1310721,2,9,26,1310721,4,18,1310721,2,15,28,1310721,4, +1310721,2,1835009,40,1629687,16,1966081,1310721,4,1579223,12,1310721,2,7,26,4, +1310721,2,5,26,4,1310721,2,3,26,4,1310721,2,4,1,1310721,2, +9,26,4,1310721,2,9,26,2,1587619,2,40,1630361,16,1310721,2,13, +26,1277953,1310721,2,9,26,2,26,2,10,3,6,10,4,8,1310721, +2,13,26,2,1,26,2,15,28,1,6,1,10,28,34,63, +38,3,26,1629905,16,1310721,2,13,26,2,9,26,2,1310721,2,2, +28,1,6,1,10,28,34,63,38,3,26,1,6,1,10,28, +34,63,38,1,10,28,1629907,14,1,1629985,16,1310721,2,13,26,2, +7,26,2,1310721,2,3,26,2,28,1,6,1,10,28,34,63, +38,3,26,1,6,1,10,28,34,63,38,1,10,28,1629987,14, +1,1630065,16,1310721,2,13,26,2,11,26,2,1310721,2,7,26,2, +28,1,6,1,10,28,34,63,38,3,26,1,6,1,10,28, +34,63,38,1,10,28,1630067,14,1,1630323,16,1310721,2,11,26,1, +10,3,6,10,4,8,1310721,2,11,26,2,1310721,2,3,26,2, +40,1630219,16,1310721,2,13,26,2,15,1310721,2,11,26,2,26,26, +2,1310721,2,5,26,2,1310721,2,11,26,2,26,2,28,1,6, +1,10,28,34,63,38,3,26,1,6,1,10,28,34,63,38, +1,10,28,1630221,14,1,1630263,16,1310721,2,11,26,1,6,2,1, +6,3,26,5,6,4,10,8,8,1630093,14,1310721,2,11,26,2, +1310721,2,3,26,2,28,1,6,1,10,28,34,63,38,3,26, +1630323,16,1310721,2,13,26,2,1630561,14,1310721,2,9,26,1,6,2, +1,6,3,26,5,6,4,10,8,8,1629735,14,1310721,2,13,26, +15,3,1,1310721,2,3,26,2,1628411,12,10,3,6,10,4,8, +1310721,2,13,26,2,9,26,1310721,2,2,10,3,6,10,4,8, +1310721,2,13,26,2,7,26,1310721,2,3,26,2,10,3,6,10, +4,8,1310721,2,13,26,2,11,26,1310721,2,7,26,2,10,3, +6,10,4,8,1310721,2,13,26,2,15,26,1310721,2,5,26,2, +1310721,2,3,26,2,1574491,12,8,1310721,2,13,26,2,1630561,14,1, +1310721,2,15,26,1310721,4,18,1310721,2,9,28,1310721,4,1310721,2,1835009, +40,1630609,16,1966081,1310721,4,1579223,12,1310721,2,3,26,4,1310721,2,4, +1,1310721,2,5,26,4,1310721,2,5,26,2,1587619,2,40,1630945,16, +1310721,2,7,26,1277953,1310721,2,5,26,2,26,2,10,3,6,10, +4,8,1310721,2,7,26,2,1,26,2,23,28,1,6,1,10, +28,34,63,38,3,26,1630807,16,1310721,2,7,26,2,9,26,2, +1310721,2,2,28,1,6,1,10,28,34,63,38,3,26,1,6, +1,10,28,34,63,38,1,10,28,1630809,14,1,1630887,16,1310721,2, +7,26,2,7,26,2,1310721,2,3,26,2,28,1,6,1,10, +28,34,63,38,3,26,1,6,1,10,28,34,63,38,1,10, +28,1630889,14,1,1630907,16,1310721,2,7,26,2,1631061,14,1310721,2,5, +26,1,6,2,1,6,3,26,5,6,4,10,8,8,1630637,14, +1310721,2,7,26,23,3,1,1,1628411,12,10,3,6,10,4,8, +1310721,2,7,26,2,9,26,1310721,2,2,10,3,6,10,4,8, +1310721,2,7,26,2,7,26,1310721,2,3,26,2,10,3,6,10, +4,8,1310721,2,7,26,2,1631061,14,1,1310721,2,9,26,1310721,4, +18,1310721,2,9,28,1310721,4,1310721,2,1835009,40,1631109,16,1966081,1310721,4, +1579223,12,1310721,2,3,26,4,1310721,2,4,1,1310721,2,5,26,4, +1310721,2,5,26,2,1587619,2,40,1631365,16,1310721,2,7,26,1277953,1310721, +2,5,26,2,26,2,10,3,6,10,4,8,1310721,2,7,26, +2,1,26,2,19,28,1,6,1,10,28,34,63,38,3,26, +1631307,16,1310721,2,7,26,2,7,26,2,1310721,2,2,28,1,6, +1,10,28,34,63,38,3,26,1,6,1,10,28,34,63,38, +1,10,28,1631309,14,1,1631327,16,1310721,2,7,26,2,1631453,14,1310721, +2,5,26,1,6,2,1,6,3,26,5,6,4,10,8,8, +1631137,14,1310721,2,7,26,19,3,1310721,2,3,26,2,1,1628411,12, +10,3,6,10,4,8,1310721,2,7,26,2,7,26,1310721,2,2, +10,3,6,10,4,8,1310721,2,7,26,2,1631453,14,1,1310721,2, +9,26,1310721,4,18,1310721,2,15,28,1310721,4,1310721,2,1835009,40,1631501, +16,1966081,1310721,4,1579223,12,1310721,2,5,26,4,1310721,2,3,26,4, +1310721,2,4,1,1310721,2,7,26,4,1310721,2,7,26,2,1587619,2, +40,1632041,16,1310721,2,11,26,1277953,1310721,2,7,26,2,26,2,10, +3,6,10,4,8,1310721,2,11,26,2,1,26,2,17,28,1, +6,1,10,28,34,63,38,3,26,1631709,16,1310721,2,11,26,2, +11,26,2,1310721,2,2,28,1,6,1,10,28,34,63,38,3, +26,1,6,1,10,28,34,63,38,1,10,28,1631711,14,1,1632003, +16,1310721,2,13,26,1310721,2,11,26,2,13,26,2,10,3,6, +10,4,8,1310721,2,9,26,1,10,3,6,10,4,8,1310721,2, +9,26,2,1310721,2,5,26,2,40,1631899,16,1310721,2,13,26,2, +3,1310721,2,9,26,2,26,26,2,1310721,2,3,26,2,1310721,2, +9,26,2,26,2,28,1,6,1,10,28,34,63,38,3,26, +1,6,1,10,28,34,63,38,1,10,28,1631901,14,1,1631943,16, +1310721,2,9,26,1,6,2,1,6,3,26,5,6,4,10,8, +8,1631773,14,1310721,2,9,26,2,1310721,2,5,26,2,28,1,6, +1,10,28,34,63,38,3,26,1632003,16,1310721,2,11,26,2,1632315, +14,1310721,2,7,26,1,6,2,1,6,3,26,5,6,4,10, +8,8,1631539,14,1310721,2,13,26,1310721,2,5,26,2,3,26,1628197, +12,10,3,6,10,4,8,1310721,2,13,26,2,1,26,1310721,2, +5,26,2,10,3,6,10,4,8,1310721,2,13,26,2,3,26, +1310721,2,3,26,2,1310721,2,5,26,2,1574491,12,8,1310721,2,11, +26,17,1,1310721,2,2,5,26,2,1,1628411,12,10,3,6,10, +4,8,1310721,2,11,26,2,7,26,3,1,10,28,10,3,6, +10,4,8,1310721,2,11,26,2,11,26,1310721,2,2,10,3,6, +10,4,8,1310721,2,11,26,2,13,26,1310721,2,13,26,2,10, +3,6,10,4,8,1310721,2,11,26,2,1632315,14,1,1310721,2,15, +26,1310721,4,18,1310721,2,33,28,1310721,4,1310721,2,1835009,40,1632363,16, +1966081,1310721,4,1579223,12,1310721,2,4,1,1310721,2,13,26,4,1310721,2, +2,1,26,2,17,28,1,6,1,10,28,34,63,38,1,10, +28,1632429,16,3,1632471,14,1310721,2,2,7,26,2,1,40,3,36, +1,6,1,10,28,34,63,38,1,10,28,1632485,16,1310721,2,2, +1633387,14,1310721,2,2,7,26,2,5,1,10,28,28,1,6,1, +10,28,34,63,38,3,26,1632655,16,1632613,14,97,32,115,116,114, +117,99,116,32,99,97,110,39,116,32,99,111,110,116,97,105, +110,32,105,116,115,101,108,102,32,98,121,32,118,97,108,117, +101,0,1632535,1588813,12,8,1310721,2,2,7,26,1,10,3,6,10, +4,8,1310721,2,2,1633387,14,1310721,2,2,7,26,5,1,10,28, +10,3,6,10,4,8,1310721,2,3,26,1310721,2,2,11,26,2, +10,3,6,10,4,8,1310721,2,7,26,1310721,2,3,26,2,11, +26,2,10,3,6,10,4,8,1310721,2,5,26,1310721,2,3,26, +2,9,26,2,10,3,6,10,4,8,1310721,2,17,26,1,17, +1574703,12,8,1310721,2,17,26,1310721,2,2,13,26,2,3,26,1310721, +2,2,13,26,2,1,26,2,1574491,12,8,1310721,2,11,26,1310721, +2,7,26,2,7,30,3,26,1628197,12,10,3,6,10,4,8, +1310721,2,9,26,1,10,3,6,10,4,8,1310721,2,9,26,2, +1310721,2,7,26,2,40,1633279,16,1310721,2,15,26,1310721,2,5,26, +2,1310721,2,9,26,2,1310721,2,9,26,2,26,3,26,26,2, +1310721,2,17,26,1633573,12,10,3,6,10,4,8,1310721,2,11,26, +2,1310721,2,9,26,2,7,30,26,1310721,2,5,26,2,1310721,2, +9,26,2,1310721,2,9,26,2,26,26,2,10,3,6,10,4, +8,1310721,2,11,26,2,1310721,2,9,26,2,7,30,3,26,26, +1310721,2,15,26,2,10,3,6,10,4,8,1310721,2,11,26,2, +1310721,2,9,26,2,7,30,5,26,26,1310721,2,13,26,2,10, +3,6,10,4,8,1310721,2,13,26,1310721,2,13,26,2,1310721,2, +15,26,2,1633401,12,26,10,3,6,10,4,8,1310721,2,9,26, +1,6,2,1,6,3,26,5,6,4,10,8,8,1632919,14,1310721, +2,2,9,26,1310721,2,11,26,2,10,3,6,10,4,8,1310721, +2,2,3,26,1310721,2,13,26,2,10,3,6,10,4,8,1310721, +2,2,7,26,1310721,2,7,26,2,10,3,6,10,4,8,1310721, +2,2,1633387,14,1,1310721,2,33,26,1310721,4,18,1310721,2,3,28, +1310721,4,1310721,2,1835009,40,1633435,16,1966081,1310721,4,1579223,12,1310721,2,4, +1310721,2,2,1,26,2,17,28,1,6,1,10,28,34,63,38, +3,26,1633523,16,1310721,2,2,7,26,2,1,40,1,6,1,10, +28,34,63,38,1,10,28,1633525,14,1,1633541,16,1310721,2,2,1632329, +12,8,1310721,2,2,3,26,2,1633559,14,1,1310721,2,3,26,1310721, +4,18,1310721,2,61,28,1310721,4,1310721,2,1835009,40,1633607,16,1966081,1310721, +4,1579223,12,1310721,2,3,26,4,1310721,2,4,1310721,2,2,1,26, +2,1310721,2,5,26,4,1310721,2,5,26,2,19,28,1,6,1, +10,28,34,63,38,3,26,1633751,16,1310721,2,3,26,2,1310721,2, +2,7,26,2,26,2,1633743,16,1310721,2,3,26,2,1310721,2,2, +7,26,2,26,2,1634557,14,1587621,2,1634557,14,1310721,2,5,26,2, +11,28,1,6,1,10,28,34,63,38,3,26,1633823,16,1310721,2, +2,9,26,2,1310721,2,3,26,2,1633573,12,1628759,12,1634557,14,1310721, +2,5,26,2,13,28,1,6,1,10,28,34,63,38,3,26, +1633907,16,1310721,2,2,9,26,2,1310721,2,3,26,2,1633573,12,1310721, +2,2,7,26,2,1629133,12,1634557,14,1310721,2,5,26,2,15,28, +1,6,1,10,28,34,63,38,3,26,1634197,16,1310721,2,9,26, +1310721,2,2,7,26,2,10,3,6,10,4,8,1310721,2,7,26, +1,10,3,6,10,4,8,1310721,2,7,26,2,1310721,2,9,26, +2,40,1634133,16,1310721,2,11,26,1310721,2,7,26,2,26,1310721,2, +2,15,1310721,2,7,26,2,26,26,2,1310721,2,3,26,2,1633573, +12,10,3,6,10,4,8,1310721,2,7,26,1,6,2,1,6, +3,26,5,6,4,10,8,8,1633999,14,1310721,2,2,9,26,2, +1310721,2,3,26,2,1633573,12,1310721,2,9,26,2,1310721,2,11,26, +1310721,2,2,11,26,2,1629653,12,1634557,14,1310721,2,5,26,2,17, +28,1,6,1,10,28,34,63,38,3,26,1634273,16,1310721,2,2, +11,26,2,1,6,1,10,28,34,63,38,1,10,28,1634275,14, +1,1634545,16,1310721,2,59,26,1310721,2,2,13,26,2,10,3,6, +10,4,8,1310721,2,9,26,1310721,2,59,26,2,1,26,2,10, +3,6,10,4,8,1310721,2,7,26,1,10,3,6,10,4,8, +1310721,2,7,26,2,1310721,2,9,26,2,40,1634507,16,1310721,2,11, +26,1310721,2,7,26,2,26,1310721,2,59,26,2,3,1310721,2,7, +26,2,26,26,2,1310721,2,3,26,2,1633573,12,10,3,6,10, +4,8,1310721,2,7,26,1,6,2,1,6,3,26,5,6,4, +10,8,8,1634369,14,1310721,2,2,11,26,2,1310721,2,11,26,1310721, +2,9,26,2,1631467,12,1634557,14,1310721,2,2,1634557,14,1,1310721,2, +61,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1634605, +16,1966081,1310721,4,1579223,12,1310721,2,4,1310721,2,2,1,26,2,3, +28,1,6,1,10,28,34,63,38,3,26,1634657,16,3,1634715,14, +1310721,2,2,1,26,2,5,28,1,6,1,10,28,34,63,38, +3,26,1,6,1,10,28,34,63,38,1,10,28,1634725,16,3, +1634783,14,1310721,2,2,1,26,2,7,28,1,6,1,10,28,34, +63,38,3,26,1,6,1,10,28,34,63,38,1,10,28,1634789, +14,1,1310721,2,3,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721, +2,1835009,40,1634837,16,1966081,1310721,4,1579223,12,1310721,2,4,1310721,2,2, +1,26,2,13,28,1,6,1,10,28,34,63,38,3,26,1634889, +16,3,1634947,14,1310721,2,2,1,26,2,17,28,1,6,1,10, +28,34,63,38,3,26,1,6,1,10,28,34,63,38,1,10, +28,1634953,14,1,1310721,2,3,26,1310721,4,18,1310721,2,3,28,1310721, +4,1310721,2,1835009,40,1635001,16,1966081,1310721,4,1579223,12,1310721,2,4,1310721, +2,2,1,26,2,11,28,1,6,1,10,28,34,63,38,3, +26,1635053,16,3,1635111,14,1310721,2,2,1,26,2,13,28,1,6, +1,10,28,34,63,38,3,26,1,6,1,10,28,34,63,38, +1,10,28,1635117,14,1,1310721,2,3,26,1310721,4,18,1310721,2,3, +28,1310721,4,1310721,2,1835009,40,1635165,16,1966081,1310721,4,1579223,12,1310721,2, +4,1310721,2,2,1,26,2,13,28,1,6,1,10,28,34,63, +38,3,26,1635231,16,1310721,2,2,9,26,2,1628759,12,1635243,14,1310721, +2,2,1635243,14,1,1310721,2,3,26,1310721,4,18,1310721,2,3,28, +1310721,4,1310721,2,1835009,40,1635291,16,1966081,1310721,4,1579223,12,1310721,2,4, +1310721,2,2,1,26,2,11,28,1,6,1,10,28,34,63,38, +3,26,1635343,16,3,1635401,14,1310721,2,2,1,26,2,13,28,1, +6,1,10,28,34,63,38,3,26,1,6,1,10,28,34,63, +38,1,10,28,1635421,16,1310721,2,2,9,26,2,1635431,14,1587621,2, +1635431,14,1,1310721,2,3,26,1310721,4,18,1310721,2,5,28,1310721,4, +1310721,2,1835009,40,1635479,16,1966081,1310721,4,1579223,12,1310721,2,3,26,4, +1310721,2,4,1310721,2,2,1310721,2,3,26,2,28,1,6,1,10, +28,34,63,38,3,26,1635543,16,3,1635597,14,1310721,2,2,1587621,2, +28,1,6,1,10,28,34,63,38,3,26,1,6,1,10,28, +34,63,38,1,10,28,1635607,16,3,1635665,14,1310721,2,3,26,2, +1587621,2,28,1,6,1,10,28,34,63,38,3,26,1,6,1, +10,28,34,63,38,1,10,28,1635675,16,3,1636747,14,1310721,2,2, +1634571,12,1635729,16,1310721,2,3,26,2,1634571,12,1,6,1,10,28, +34,63,38,1,10,28,1635731,14,1,1635741,16,3,1636747,14,1310721,2, +3,26,1310721,2,3,26,2,1635131,12,10,3,6,10,4,8,1310721, +2,2,1,26,2,11,28,1,6,1,10,28,34,63,38,3, +26,1635881,16,1310721,2,3,26,2,1,26,2,11,28,1,6,1, +10,28,34,63,38,3,26,1,6,1,10,28,34,63,38,1, +10,28,1635883,14,1,1636207,16,1310721,2,2,9,26,2,1587621,2,28, +1,6,1,10,28,34,63,38,3,26,1635935,16,3,1635999,14,1310721, +2,3,26,2,9,26,2,1587621,2,28,1,6,1,10,28,34, +63,38,3,26,1,6,1,10,28,34,63,38,1,10,28,1636009, +16,3,1636069,14,1310721,2,2,9,26,2,1587629,2,28,1,6,1, +10,28,34,63,38,3,26,1,6,1,10,28,34,63,38,1, +10,28,1636079,16,3,1636143,14,1310721,2,3,26,2,9,26,2,1587629, +2,28,1,6,1,10,28,34,63,38,3,26,1,6,1,10, +28,34,63,38,1,10,28,1636153,16,3,1636747,14,1310721,2,2,9, +26,2,1310721,2,3,26,2,9,26,2,28,1,6,1,10,28, +34,63,38,3,26,1636747,14,1310721,2,2,1,26,2,11,28,1, +6,1,10,28,34,63,38,3,26,1636253,16,3,1636311,14,1310721,2, +2,1,26,2,15,28,1,6,1,10,28,34,63,38,3,26, +1,6,1,10,28,34,63,38,1,10,28,1636321,16,3,1636379,14, +1310721,2,2,1,26,2,23,28,1,6,1,10,28,34,63,38, +3,26,1,6,1,10,28,34,63,38,1,10,28,1636413,16,1587635, +2,1,6,1,10,28,34,63,38,1,10,28,1636415,14,1,1636425, +16,3,1636747,14,1310721,2,2,1,26,2,23,28,1,6,1,10, +28,34,63,38,3,26,1636531,16,1310721,2,3,26,2,1,26,2, +23,28,1,6,1,10,28,34,63,38,3,26,1,6,1,10, +28,34,63,38,1,10,28,1636533,14,1,1636613,16,1310721,2,2,9, +26,2,1310721,2,3,26,2,9,26,2,28,1,6,1,10,28, +34,63,38,3,26,1,6,1,10,28,34,63,38,1,10,28, +1636615,14,1,1636703,16,1310721,2,2,7,26,2,1636641,16,3,1636699,14, +1310721,2,3,26,2,7,26,2,1,6,1,10,28,34,63,38, +3,26,1,6,1,10,28,34,63,38,1,10,28,1636747,14,1310721, +2,2,1310721,2,3,26,2,28,1,6,1,10,28,34,63,38, +3,26,1636747,14,1,1310721,2,5,26,1310721,4,18,1310721,2,15,28, +1310721,4,1310721,2,1835009,40,1636795,16,1966081,1310721,4,1579223,12,1310721,2,5, +26,4,1310721,2,3,26,4,1310721,2,4,1310721,2,2,1,26,2, +19,28,1,6,1,10,28,34,63,38,3,26,1637033,16,1310721,2, +7,26,1310721,2,2,7,26,2,10,3,6,10,4,8,1310721,2, +5,26,2,1310721,2,7,26,2,26,2,1,6,1,10,28,34, +63,38,3,26,1636991,16,1310721,2,5,26,2,1310721,2,7,26,2, +26,1310721,2,3,26,2,10,3,6,10,4,8,3,1637837,14,1310721, +2,5,26,2,1310721,2,7,26,2,26,2,1310721,2,3,26,2, +1635445,12,1637837,14,1310721,2,3,26,1310721,2,3,26,2,1635131,12,10, +3,6,10,4,8,1310721,2,2,1,26,2,11,28,1,6,1, +10,28,34,63,38,3,26,1637173,16,1310721,2,3,26,2,1,26, +2,11,28,1,6,1,10,28,34,63,38,3,26,1,6,1, +10,28,34,63,38,1,10,28,1637175,14,1,1637277,16,1310721,2,3, +26,2,9,26,2,1587621,2,28,1,6,1,10,28,34,63,38, +3,26,1637231,16,3,1637837,14,1310721,2,2,9,26,2,1310721,2,3, +26,2,9,26,2,1310721,2,5,26,2,1636761,12,1637837,14,1310721,2, +2,1,26,2,17,28,1,6,1,10,28,34,63,38,3,26, +1637355,16,1310721,2,2,11,26,2,1,6,1,10,28,34,63,38, +1,10,28,1637357,14,1,1637427,16,1310721,2,3,26,2,1,26,2, +17,28,1,6,1,10,28,34,63,38,3,26,1,6,1,10, +28,34,63,38,1,10,28,1637429,14,1,1637509,16,1310721,2,3,26, +2,11,26,2,1310721,2,2,11,26,2,28,1,6,1,10,28, +34,63,38,3,26,1,6,1,10,28,34,63,38,1,10,28, +1637511,14,1,1637811,16,1310721,2,11,26,1310721,2,2,13,26,2,10, +3,6,10,4,8,1310721,2,13,26,1310721,2,3,26,2,13,26, +2,10,3,6,10,4,8,1310721,2,9,26,1310721,2,11,26,2, +1,26,2,10,3,6,10,4,8,1310721,2,7,26,1,10,3, +6,10,4,8,1310721,2,7,26,2,1310721,2,9,26,2,40,1637805, +16,1310721,2,11,26,2,3,1310721,2,7,26,2,26,26,2,1310721, +2,13,26,2,3,1310721,2,7,26,2,26,26,2,1310721,2,5, +26,2,1636761,12,1,6,1,10,28,34,63,38,3,26,1637767,16, +1,1637837,14,1310721,2,7,26,1,6,2,1,6,3,26,5,6, +4,10,8,8,1637641,14,3,1637837,14,1310721,2,2,1310721,2,3,26, +2,1635445,12,1637837,14,1,1310721,2,15,26,1310721,4,18,1310721,2,9, +28,1310721,4,1310721,2,1835009,40,1637885,16,1966081,1310721,4,1579223,12,1310721,2, +4,1310721,2,2,1,26,2,1310721,2,3,26,4,1310721,2,3,26, +2,11,28,1,6,1,10,28,34,63,38,3,26,1637979,16,85, +1573255,12,8,1310721,2,2,9,26,2,1637851,12,1639093,14,1310721,2,3, +26,2,23,28,1,6,1,10,28,34,63,38,3,26,1638097,16, +1310721,2,2,7,26,2,1638059,16,1638051,14,102,105,120,101,100,32, +0,1638037,1573495,12,8,1638069,14,91,93,0,1638063,1573495,12,8,1310721,2, +2,9,26,2,1637851,12,1639093,14,1310721,2,3,26,2,13,28,1, +6,1,10,28,34,63,38,3,26,1638189,16,183,1573255,12,8,1310721, +2,2,7,26,2,1573657,12,8,187,1573255,12,8,1310721,2,2,9, +26,2,1637851,12,1639093,14,1310721,2,3,26,2,15,28,1,6,1, +10,28,34,63,38,3,26,1638743,16,1310721,2,2,9,26,2,1587621, +2,28,1,6,1,10,28,34,63,38,3,26,1638291,16,1638279,14, +102,110,0,1638273,1573495,12,8,1638309,14,1310721,2,2,9,26,2,1637851, +12,8,1310721,2,2,11,26,2,3,32,1638347,16,1638339,14,46,117, +0,1638333,1573495,12,8,81,1573255,12,8,1310721,2,5,26,1,10,3, +6,10,4,8,1310721,2,5,26,2,1310721,2,2,7,26,2,40, +1638729,16,1310721,2,5,26,2,1638437,16,1638429,14,44,32,0,1638423,1573495, +12,8,1310721,2,2,15,1310721,2,5,26,2,26,26,2,1637851,12, +8,1310721,2,5,26,2,25,40,1638555,16,1310721,2,2,1310721,2,5, +26,2,1654383,12,3,28,1,6,1,10,28,34,63,38,3,26, +1,6,1,10,28,34,63,38,1,10,28,1638557,14,1,1638579,16, +1638571,14,46,109,0,1638565,1573495,12,8,1310721,2,5,26,2,25,40, +1638667,16,1310721,2,2,1310721,2,5,26,2,1654383,12,5,28,1,6, +1,10,28,34,63,38,3,26,1,6,1,10,28,34,63,38, +1,10,28,1638669,14,1,1638691,16,1638683,14,46,100,0,1638677,1573495,12, +8,1310721,2,5,26,1,6,2,1,6,3,26,5,6,4,10, +8,8,1638377,14,83,1573255,12,8,1,1639093,14,1310721,2,2,5,26, +2,1638781,16,1310721,2,2,5,26,2,1573495,12,8,1638797,14,1638789,14, +63,0,1638785,1573495,12,8,1310721,2,3,26,2,17,28,1,6,1, +10,28,34,63,38,3,26,1638873,16,1310721,2,2,11,26,2,1, +6,1,10,28,34,63,38,1,10,28,1638875,14,1,1639085,16,1310721, +2,7,26,1310721,2,2,13,26,2,10,3,6,10,4,8,121, +1573255,12,8,1310721,2,5,26,1,10,3,6,10,4,8,1310721,2, +5,26,2,1310721,2,7,26,2,1,26,2,40,1639077,16,1310721,2, +5,26,2,1639005,16,1638997,14,44,32,0,1638991,1573495,12,8,1310721,2, +7,26,2,3,1310721,2,5,26,2,26,26,2,1637851,12,8,1310721, +2,5,26,1,6,2,1,6,3,26,5,6,4,10,8,8, +1638941,14,125,1573255,12,8,1,1639093,14,1,1310721,2,9,26,1310721,4, +18,1310721,2,7,28,1310721,4,1310721,2,1835009,40,1639141,16,1966081,1310721,4, +1579223,12,1310721,2,5,26,4,1310721,2,3,26,4,1310721,2,4,1587631, +2,1,6,1,10,28,34,63,38,3,26,1639201,16,3,1639247,14, +1587503,2,1,6,1,10,28,34,63,38,3,26,1,6,1,10, +28,34,63,38,1,10,28,1639257,16,3,1639283,14,1587637,2,1,6, +1,10,28,34,63,38,1,10,28,1639293,16,3,1639319,14,1588225,2, +1,6,1,10,28,34,63,38,1,10,28,1639329,16,1,1639493,14, +1588227,12,8,1639365,14,116,121,112,101,32,101,114,114,111,114,58, +32,0,1639339,1573495,12,8,1310721,2,2,1573495,12,8,1639413,14,58,32, +101,120,112,101,99,116,101,100,32,0,1639389,1573495,12,8,1310721,2, +3,26,2,1637851,12,8,1639455,14,44,32,103,111,116,32,0,1639441, +1573495,12,8,1310721,2,5,26,2,1637851,12,8,1588371,12,8,1,1639493, +14,1,1310721,2,7,26,1310721,4,18,1310721,2,7,28,1310721,4,1310721, +2,1835009,40,1639541,16,1966081,1310721,4,1579223,12,1310721,2,5,26,4,1310721, +2,3,26,4,1310721,2,4,1310721,2,2,1639583,16,3,1639629,14,1587631, +2,1,6,1,10,28,34,63,38,3,26,1,6,1,10,28, +34,63,38,1,10,28,1639639,16,3,1639685,14,1587503,2,1,6,1, +10,28,34,63,38,3,26,1,6,1,10,28,34,63,38,1, +10,28,1639695,16,3,1639721,14,1587637,2,1,6,1,10,28,34,63, +38,1,10,28,1639731,16,3,1639757,14,1588225,2,1,6,1,10,28, +34,63,38,1,10,28,1639767,16,1,1639883,14,1588227,12,8,1639803,14, +116,121,112,101,32,101,114,114,111,114,58,32,0,1639777,1573495,12, +8,1310721,2,3,26,2,1573495,12,8,1639845,14,44,32,103,111,116, +32,0,1639831,1573495,12,8,1310721,2,5,26,2,1637851,12,8,1588371,12, +8,1,1639883,14,1,1310721,2,7,26,1310721,4,18,1310721,2,5,28, +1310721,4,1310721,2,1835009,40,1639931,16,1966081,1310721,4,1579223,12,1310721,2,4, +1587655,2,1310721,2,3,26,4,1310721,2,3,26,2,1640065,16,1310721,2, +3,26,1,6,2,1,6,-1,26,5,6,4,10,8,8,1587639, +1310721,2,3,26,2,26,2,1310721,2,2,28,1,6,1,10,28, +34,63,38,3,26,1640061,16,1310721,2,3,26,2,1640079,14,1639951,14, +3,1,10,28,1640079,14,1,1310721,2,5,26,1310721,4,18,1310721,2, +5,28,1310721,4,1310721,2,1835009,40,1640127,16,1966081,1310721,4,1579223,12,1310721, +2,4,1310721,2,2,1588185,2,28,1,6,1,10,28,34,63,38, +3,26,1640175,16,3,1640357,14,1310721,2,2,1639897,12,1,40,3,36, +1640203,16,3,1640357,14,1310721,2,2,1607893,12,1,40,3,36,1640231,16, +1,1640357,14,1310721,2,3,26,1310721,2,2,1590943,12,10,3,6,10, +4,8,1310721,2,3,26,2,1,40,3,36,1640349,16,1417729,1310721,2, +3,26,2,26,2,15,28,1,6,1,10,28,34,63,38,3, +26,1,6,1,10,28,34,63,38,1,10,28,1640351,14,1,1640357, +14,1,1310721,2,5,26,1310721,4,18,1310721,2,1,28,1310721,4,1310721, +2,1835009,40,1640405,16,1966081,1310721,4,1579223,12,1587665,2,1602703,12,1640441,16, +1585225,1587663,2,10,3,6,10,4,8,1,1640459,14,1587663,2,1603237,12, +8,1,1640459,14,1,1310721,2,1,26,1310721,4,18,1310721,2,53,28, +1310721,4,1310721,2,1835009,40,1640507,16,1966081,1310721,4,1579223,12,1640885,12,1310721, +2,4,1587533,2,1602703,12,1640859,16,1601277,12,8,1310721,2,3,26,1, +10,3,6,10,4,8,1587535,2,1602703,12,1,6,1,10,28,34, +63,38,3,26,1640779,16,3,1640779,16,1310721,2,3,26,2,49,40, +3,36,1640669,16,1640661,14,116,111,111,32,109,97,110,121,32,112, +97,114,97,109,101,116,101,114,115,0,1640621,1588719,12,8,1310721,2, +5,26,1310721,2,3,26,2,26,1640473,12,10,3,6,10,4,8, +1310721,2,3,26,1,6,2,1,6,3,26,5,6,4,10,8, +8,1587545,2,1603007,12,1,6,1,10,28,34,63,38,3,26,1640775, +16,1640779,14,1640589,14,1587535,2,1603237,12,8,1310721,2,1310721,2,2,1310721, +2,3,26,2,1310721,2,5,26,1310721,2,3,26,2,1310721,2,3, +26,2,26,1629653,12,10,3,6,10,4,8,1640517,14,1310721,2,2, +1640871,14,1,1310721,2,53,26,1310721,4,18,1310721,2,27,28,1310721,4, +1310721,2,1835009,40,1640919,16,1966081,1310721,4,1579223,12,1,1310721,2,23,26, +4,1587557,2,1603007,12,1640955,16,1640885,12,1628759,12,1642599,14,1587541,2,1603007, +12,1641145,16,1587543,2,1603007,12,1640993,16,1640885,12,1,1630575,12,1642599,14, +1310721,2,1676587,12,10,3,6,10,4,8,1587543,2,1603237,12,8,1310721, +2,5,26,1640885,12,10,3,6,10,4,8,1310721,2,2,1,40, +1641121,16,1641113,14,97,32,110,101,103,97,116,105,118,101,32,97, +114,114,97,121,32,108,101,110,103,116,104,0,1641065,1588813,12,8, +1310721,2,5,26,2,1310721,2,2,1629133,12,1642599,14,1588185,2,1602855,12, +1641379,16,1601277,12,8,1310721,2,5,26,1640885,12,10,3,6,10,4, +8,1310721,2,5,26,2,1,26,2,23,28,1,6,1,10,28, +34,63,38,3,26,1641257,16,1310721,2,5,26,2,9,26,2,3, +1630575,12,1642599,14,1310721,2,5,26,2,1,26,2,13,28,1,6, +1,10,28,34,63,38,1,10,28,1641365,16,1641357,14,111,110,108, +121,32,97,114,114,97,121,115,32,99,97,110,32,98,101,32, +102,105,120,101,100,0,1641307,1588813,12,8,1310721,2,5,26,2,1642599, +14,1587573,2,1602855,12,1641535,16,1601277,12,8,1587533,2,1602703,12,1,6, +1,10,28,34,63,38,3,26,1641527,16,1641519,14,102,110,32,97, +115,32,97,32,116,121,112,101,32,110,101,101,100,115,32,105, +116,115,32,112,97,114,97,109,101,116,101,114,115,58,32,102, +110,40,105,110,116,41,0,1641433,1589197,12,8,1587621,2,1642599,14,1585223, +2,5,28,1,6,1,10,28,34,63,38,1,10,28,1641621,16, +1641605,14,101,120,112,101,99,116,101,100,32,97,32,116,121,112, +101,0,1641573,1589197,12,8,1587621,2,1642599,14,1310721,2,1585225,2,10,3, +6,10,4,8,1310721,2,25,26,1310721,2,2,1639897,12,10,3,6, +10,4,8,1310721,2,25,26,2,1,40,3,36,1641723,16,1601277,12, +8,1310721,2,25,26,2,1310721,2,2,1631075,12,1642599,14,1310721,2,3, +26,1310721,2,2,1590943,12,10,3,6,10,4,8,1310721,2,3,26, +2,1,40,1641777,16,3,1641841,14,1417729,1310721,2,3,26,2,26,2, +15,28,1,6,1,10,28,34,63,38,1,10,28,1,6,1, +10,28,34,63,38,1,10,28,1641897,16,1310721,2,2,1641881,14,105, +115,110,39,116,32,97,32,116,121,112,101,0,1641855,1589331,12,8, +1587621,2,1642599,14,1601277,12,8,1310721,2,5,26,1425921,1310721,2,3,26, +2,26,2,10,3,6,10,4,8,1310721,2,5,26,2,1,26, +2,21,28,1,6,1,10,28,34,63,38,1,10,28,1641999,16, +1310721,2,5,26,2,1642599,14,1587661,2,1603237,12,8,3,1642421,16,1310721, +2,23,26,2,17,40,3,36,1642097,16,1642089,14,116,111,111,32, +109,97,110,121,32,116,121,112,101,32,97,114,103,117,109,101, +110,116,115,0,1642041,1589197,12,8,1310721,2,7,26,1310721,2,23,26, +2,26,1640473,12,10,3,6,10,4,8,1310721,2,7,26,1310721,2, +23,26,2,26,2,1633401,12,3,28,1,6,1,10,28,34,63, +38,1,10,28,1642347,16,1642339,14,116,121,112,101,32,97,114,103, +117,109,101,110,116,115,32,104,97,118,101,32,116,111,32,98, +101,32,111,110,101,32,119,111,114,100,32,40,97,32,112,111, +105,110,116,101,114,44,32,97,32,110,117,109,98,101,114,44, +32,111,114,32,97,32,112,97,114,97,109,101,116,101,114,41, +0,1642193,1588813,12,8,1310721,2,23,26,1,6,2,1,6,3,26, +5,6,4,10,8,8,1587545,2,1603007,12,1,6,1,10,28,34, +63,38,3,26,1642417,16,1642421,14,1642009,14,1640371,12,8,1310721,2,23, +26,2,1310721,2,5,26,2,7,26,2,28,1,6,1,10,28, +34,63,38,1,10,28,1642561,16,1310721,2,2,1642553,14,119,114,111, +110,103,32,110,117,109,98,101,114,32,111,102,32,116,121,112, +101,32,97,114,103,117,109,101,110,116,115,0,1642491,1588983,12,8, +1310721,2,5,26,2,1310721,2,7,26,1310721,2,23,26,2,1631467,12, +1642599,14,1,1310721,2,27,26,1310721,4,18,1310721,2,3,28,1310721,4, +1310721,2,1835009,40,1642647,16,1966081,1310721,4,1579223,12,1585223,2,9,28,1, +6,1,10,28,34,63,38,3,26,1643285,16,1585225,2,1587541,2,28, +1,6,1,10,28,34,63,38,3,26,1642719,16,3,1642771,14,1585225, +2,1587571,2,28,1,6,1,10,28,34,63,38,3,26,1,6, +1,10,28,34,63,38,1,10,28,1642781,16,3,1643401,14,1585225,2, +1587557,2,28,1,6,1,10,28,34,63,38,3,26,1643279,16,1310721, +2,1,10,3,6,10,4,8,1310721,2,2,7,40,1643011,16,1310721, +2,2,1601675,12,8,1587445,2,9,28,1,6,1,10,28,34,63, +38,3,26,1642947,16,1587447,2,1587557,2,28,1,6,1,10,28,34, +63,38,3,26,1,6,1,10,28,34,63,38,1,10,28,1642949, +14,1,1,6,1,10,28,34,63,38,3,26,1642977,16,1643011,14, +1310721,2,1,6,2,1,6,3,26,5,6,4,10,8,8,1642833, +14,1310721,2,2,1601675,12,8,1587445,2,5,28,1,6,1,10,28, +34,63,38,3,26,1643151,16,1587447,2,1640093,12,1643073,16,3,1643125,14, +1587447,2,1587573,2,28,1,6,1,10,28,34,63,38,3,26,1, +6,1,10,28,34,63,38,1,10,28,1,6,1,10,28,34, +63,38,1,10,28,1643153,14,1,1643163,16,3,1643275,14,1587445,2,9, +28,1,6,1,10,28,34,63,38,3,26,1643251,16,1587447,2,1587541, +2,28,1,6,1,10,28,34,63,38,3,26,1,6,1,10, +28,34,63,38,1,10,28,1643253,14,1,1,6,1,10,28,34, +63,38,1,10,28,1643401,14,1,1643401,14,1585223,2,5,28,1,6, +1,10,28,34,63,38,1,10,28,1643325,16,1,1643401,14,1585225,2, +1587573,2,28,1,6,1,10,28,34,63,38,3,26,1643365,16,3, +1643395,14,1585225,2,1640093,12,1,6,1,10,28,34,63,38,1,10, +28,1643401,14,1,1310721,2,3,26,1310721,4,18,1310721,2,1,28,1310721, +4,1310721,2,1835009,40,1643449,16,1966081,1310721,4,1579223,12,1587657,1,10,3, +6,10,4,8,1587659,1,10,3,6,10,4,8,1587571,2,1602703,12, +1,6,1,10,28,34,63,38,3,26,1643531,16,1587657,1640473,12,10, +3,6,10,4,8,1587571,2,1603007,12,1643767,16,1585225,2,1587587,2,28, +1,6,1,10,28,34,63,38,3,26,1643597,16,1587659,3,10,3, +6,10,4,8,1643761,14,1585225,2,1587591,2,28,1,6,1,10,28, +34,63,38,3,26,1643651,16,1587659,5,10,3,6,10,4,8,1643761, +14,1585225,2,1587589,2,28,1,6,1,10,28,34,63,38,3,26, +1643705,16,1587659,7,10,3,6,10,4,8,1643761,14,1643753,14,97,32, +116,97,103,32,105,115,32,46,117,44,32,46,109,32,111,114, +32,46,100,0,1643709,1589197,12,8,1601277,12,8,1,1643775,14,1,1310721, +2,1,26,1310721,4,18,1310721,2,21,28,1310721,4,1310721,2,1835009,40, +1643823,16,1966081,1310721,4,1579223,12,1,1310721,2,7,26,4,1524225,1310721,2, +9,26,4,1,1310721,2,15,26,4,1,1310721,2,17,26,4,1601277, +12,8,1310721,2,1603527,12,10,3,6,10,4,8,1587661,2,1602703,12, +1644291,16,1601277,12,8,1587655,1,10,3,6,10,4,8,3,1644107,16, +1587655,2,17,40,3,36,1644015,16,1644007,14,116,111,111,32,109,97, +110,121,32,116,121,112,101,32,112,97,114,97,109,101,116,101, +114,115,0,1643957,1589197,12,8,1587639,1587655,2,26,1603527,12,10,3,6, +10,4,8,1587655,1,6,2,1,6,3,26,5,6,4,10,8, +8,1587545,2,1603007,12,1,6,1,10,28,34,63,38,3,26,1644103, +16,1644107,14,1643931,14,1640371,12,8,1310721,2,17,26,3,10,3,6, +10,4,8,1310721,2,3,26,21,1,1310721,2,2,1,1628411,12,10, +3,6,10,4,8,1310721,2,3,26,2,7,26,1587655,2,10,3, +6,10,4,8,1310721,2,13,26,1587655,2,1628197,12,10,3,6,10, +4,8,1310721,2,13,26,2,1587639,1587655,2,1574491,12,8,1310721,2,3, +26,2,13,26,1310721,2,13,26,2,10,3,6,10,4,8,1644361, +14,1310721,2,3,26,17,1,1310721,2,2,1,1628411,12,10,3,6, +10,4,8,1310721,2,3,26,2,7,26,5,1,10,28,10,3, +6,10,4,8,1310721,2,5,26,1310721,2,2,15,1310721,2,3,26, +2,1590445,12,10,3,6,10,4,8,1587537,2,1603237,12,8,1587539,2, +1602703,12,1,6,1,10,28,34,63,38,3,26,1644739,16,1310721,2, +19,26,1640473,12,10,3,6,10,4,8,3,1644725,16,1310721,2,7, +26,2,2001,40,3,36,1644541,16,1644533,14,116,111,111,32,109,97, +110,121,32,102,105,101,108,100,115,0,1644501,1588719,12,8,1310721,2, +9,26,2,1310721,2,7,26,2,1310721,2,7,26,2,26,26,1603527, +12,10,3,6,10,4,8,1310721,2,9,26,2,1310721,2,7,26, +2,1310721,2,7,26,2,26,3,26,26,1310721,2,19,26,2,10, +3,6,10,4,8,1310721,2,7,26,1,6,2,1,6,3,26, +5,6,4,10,8,8,1587545,2,1603007,12,1,6,1,10,28,34, +63,38,3,26,1644721,16,1644725,14,1644469,14,1587547,2,1603237,12,8,1644413, +14,1601277,12,8,1310721,2,17,26,2,1644945,16,1310721,2,13,26,1310721, +2,7,26,2,1310721,2,7,26,2,26,1628197,12,10,3,6,10, +4,8,1310721,2,13,26,2,1310721,2,9,26,2,1310721,2,7,26, +2,1310721,2,7,26,2,26,1574491,12,8,1310721,2,3,26,2,9, +26,1310721,2,13,26,2,10,3,6,10,4,8,1310721,2,3,26, +2,11,26,1310721,2,7,26,2,10,3,6,10,4,8,1587655,1, +10,3,6,10,4,8,1645655,14,1310721,2,13,26,1310721,2,7,26, +2,7,30,3,26,1628197,12,10,3,6,10,4,8,1310721,2,11, +26,1,10,3,6,10,4,8,1310721,2,11,26,2,1310721,2,7, +26,2,40,1645547,16,1310721,2,19,26,1310721,2,9,26,2,1310721,2, +11,26,2,1310721,2,11,26,2,26,3,26,26,2,10,3,6, +10,4,8,1310721,2,19,26,2,1310721,2,3,26,2,28,1,6, +1,10,28,34,63,38,3,26,1645285,16,1645253,14,97,32,115,116, +114,117,99,116,32,99,97,110,39,116,32,99,111,110,116,97, +105,110,32,105,116,115,101,108,102,32,98,121,32,118,97,108, +117,101,59,32,117,115,101,32,97,32,112,111,105,110,116,101, +114,0,1645145,1588813,12,8,1310721,2,19,26,1587621,2,10,3,6,10, +4,8,1310721,2,13,26,2,1310721,2,11,26,2,7,30,26,1310721, +2,9,26,2,1310721,2,11,26,2,1310721,2,11,26,2,26,26, +2,10,3,6,10,4,8,1310721,2,13,26,2,1310721,2,11,26, +2,7,30,3,26,26,1310721,2,19,26,2,10,3,6,10,4, +8,1310721,2,13,26,2,1310721,2,11,26,2,7,30,5,26,26, +1310721,2,15,26,2,10,3,6,10,4,8,1310721,2,15,26,1310721, +2,15,26,2,1310721,2,19,26,2,1633401,12,26,10,3,6,10, +4,8,1310721,2,11,26,1,6,2,1,6,3,26,5,6,4, +10,8,8,1645009,14,1310721,2,3,26,2,9,26,1310721,2,13,26, +2,10,3,6,10,4,8,1310721,2,3,26,2,3,26,1310721,2, +15,26,2,10,3,6,10,4,8,1310721,2,3,26,2,7,26, +1310721,2,7,26,2,10,3,6,10,4,8,1587547,2,1603007,12,8, +1310721,2,5,26,2,1645681,14,1,1310721,2,21,26,1310721,4,18,1310721, +2,3,28,1310721,4,1310721,2,1835009,40,1645729,16,1966081,1310721,4,1579223,12, +1601277,12,8,1310721,2,1603527,12,10,3,6,10,4,8,1587549,2,1603237, +12,8,1310721,2,2,15,1640473,12,1590445,12,8,1587547,2,1603237,12,8, +1,1645801,14,1,1310721,2,3,26,1310721,4,18,1310721,2,1,28,1310721, +4,1310721,2,1835009,40,1645849,16,1966081,1310721,4,1579223,12,1601277,12,8,1587683, +2,1602855,12,1645887,16,1587631,3,10,3,6,10,4,8,1645991,14,1587685, +2,1602855,12,1645919,16,1587631,1,10,3,6,10,4,8,1645991,14,1645983, +14,116,121,112,101,99,104,101,99,107,32,111,110,32,111,114, +32,116,121,112,101,99,104,101,99,107,32,111,102,102,0,1645923, +1589197,12,8,1601277,12,8,1587547,2,1603237,12,8,1,1646015,14,1,1310721, +2,1,26,1310721,4,18,1310721,2,7,28,1310721,4,1310721,2,1835009,40, +1646063,16,1966081,1310721,4,1579223,12,1310721,2,3,26,4,1310721,2,4,1, +1310721,2,5,26,4,1310721,2,2,1632329,12,8,1310721,2,5,26,2, +1310721,2,2,7,26,2,40,1646249,16,1310721,2,2,9,26,2,1310721, +2,5,26,2,7,30,26,2,1310721,2,3,26,2,28,1,6, +1,10,28,34,63,38,3,26,1646211,16,1310721,2,5,26,2,1646263, +14,1310721,2,5,26,1,6,2,1,6,3,26,5,6,4,10, +8,8,1646103,14,3,1,10,28,1646263,14,1,1310721,2,7,26,1310721, +4,18,1310721,2,7,28,1310721,4,1310721,2,1835009,40,1646311,16,1966081,1310721, +4,1579223,12,1587533,2,1603237,12,8,1642613,12,1646383,16,1587571,2,1602703,12, +1,6,1,10,28,34,63,38,3,26,1,6,1,10,28,34, +63,38,1,10,28,1646385,14,1,1646413,16,1310721,2,1640473,12,10,3, +6,10,4,8,1646575,14,1310721,2,3,26,1610967,12,10,3,6,10, +4,8,1587637,1,6,2,1,6,3,26,5,6,4,10,8,8, +1310721,2,5,26,1672327,12,10,3,6,10,4,8,1655683,12,8,1310721, +2,1587633,2,10,3,6,10,4,8,1587637,1,6,2,1,6,-1, +26,5,6,4,10,8,8,1310721,2,3,26,2,1611599,12,8,1310721, +2,3,26,2,1611517,12,8,1587535,2,1603237,12,8,1310721,2,2,1646597, +14,1,1310721,2,7,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721, +2,1835009,40,1646645,16,1966081,1310721,4,1579223,12,1587533,2,1603237,12,8,1310721, +2,1672327,12,10,3,6,10,4,8,1655683,12,8,1310721,2,2,1609283, +12,8,1587633,2,1646703,14,1,1310721,2,3,26,1310721,4,18,1310721,2, +3,28,1310721,4,1310721,2,1835009,40,1646751,16,1966081,1310721,4,1579223,12,1310721, +2,4,1310721,2,2,1,26,2,23,28,1,6,1,10,28,34, +63,38,3,26,1646835,16,1310721,2,2,7,26,2,1,6,1,10, +28,34,63,38,1,10,28,1646837,14,1,1646923,16,1646915,14,97,32, +102,105,120,101,100,32,97,114,114,97,121,32,99,97,110,39, +116,32,103,114,111,119,32,111,114,32,115,104,114,105,110,107, +0,1646845,1588813,12,8,1,1646931,14,1,1310721,2,3,26,1310721,4,18, +1310721,2,3,28,1310721,4,1310721,2,1835009,40,1646979,16,1966081,1310721,4,1579223, +12,1601277,12,8,1310721,2,1646611,12,10,3,6,10,4,8,1587535,2, +1603237,12,8,1310721,2,2,1,26,2,13,28,1,6,1,10,28, +34,63,38,3,26,1647085,16,9,1592369,12,8,1310721,2,2,7,26, +2,1592283,12,8,1647339,14,1310721,2,2,1,26,2,23,28,1,6, +1,10,28,34,63,38,3,26,1647131,16,3,1647185,14,1310721,2,2, +1587621,2,28,1,6,1,10,28,34,63,38,3,26,1,6,1, +10,28,34,63,38,1,10,28,1647227,14,108,101,110,32,110,101, +101,100,115,32,97,110,32,97,114,114,97,121,0,1647189,1310721,2, +2,1639507,12,8,1587701,2,1647311,16,1310721,2,2,1,26,2,23,28, +1,6,1,10,28,34,63,38,3,26,1,6,1,10,28,34, +63,38,1,10,28,1647313,14,1,1647329,16,3,1592369,12,8,1647339,14, +1588187,2,1593049,12,8,1587633,1587623,2,10,3,6,10,4,8,1,1647365, +14,1,1310721,2,3,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721, +2,1835009,40,1647413,16,1966081,1310721,4,1579223,12,1601277,12,8,1310721,2,1646611, +12,10,3,6,10,4,8,1587535,2,1603237,12,8,1310721,2,2,1, +26,2,23,28,1,6,1,10,28,34,63,38,3,26,1647495,16, +3,1647549,14,1310721,2,2,1587621,2,28,1,6,1,10,28,34,63, +38,3,26,1,6,1,10,28,34,63,38,1,10,28,1647585,14, +99,97,112,32,110,101,101,100,115,32,97,32,91,93,84,0, +1647553,1310721,2,2,1639507,12,8,1588189,2,1593049,12,8,1587633,1587623,2,10, +3,6,10,4,8,1,1647635,14,1,1310721,2,3,26,1310721,4,18, +1310721,2,5,28,1310721,4,1310721,2,1835009,40,1647683,16,1966081,1310721,4,1579223, +12,1601277,12,8,1310721,2,1646611,12,10,3,6,10,4,8,1310721,2, +2,1646717,12,8,1310721,2,2,1,26,2,23,28,1,6,1,10, +28,34,63,38,3,26,1647829,16,1310721,2,2,9,26,2,1633401,12, +3,28,1,6,1,10,28,34,63,38,1,10,28,1,6,1, +10,28,34,63,38,1,10,28,1647831,14,1,1647953,16,1647945,14,112, +117,115,104,32,116,97,107,101,115,32,111,110,101,45,119,111, +114,100,32,101,108,101,109,101,110,116,115,59,32,117,115,101, +32,114,101,115,105,122,101,32,102,111,114,32,115,116,114,117, +99,116,115,0,1647839,1588813,12,8,1587545,2,1603237,12,8,1310721,2,3, +26,1672327,12,10,3,6,10,4,8,1655683,12,8,1310721,2,3,26, +2,5,1620967,12,8,1310721,2,2,1,26,2,23,28,1,6,1, +10,28,34,63,38,3,26,1648117,16,1310721,2,2,9,26,2,1587633, +2,1635445,12,1,6,1,10,28,34,63,38,3,26,1,6,1, +10,28,34,63,38,1,10,28,1648119,14,1,1648161,16,1648137,14,112, +117,115,104,0,1648127,1310721,2,2,9,26,2,1587633,2,1639107,12,8, +1587535,2,1603237,12,8,1588195,2,1593049,12,8,1587633,1587623,2,10,3,6, +10,4,8,1,1648207,14,1,1310721,2,5,26,1310721,4,18,1310721,2, +3,28,1310721,4,1310721,2,1835009,40,1648255,16,1966081,1310721,4,1579223,12,1601277, +12,8,1310721,2,1646611,12,10,3,6,10,4,8,1310721,2,2,1646717, +12,8,1587535,2,1603237,12,8,1588197,2,1593049,12,8,1587633,1310721,2,2, +1635257,12,10,3,6,10,4,8,1310721,2,2,1,26,2,23,28, +1,6,1,10,28,34,63,38,3,26,1648403,16,1587633,1310721,2,2, +9,26,2,10,3,6,10,4,8,1,1648411,14,1,1310721,2,3, +26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,1835009,40,1648459,16, +1966081,1310721,4,1579223,12,1601277,12,8,1310721,2,1646611,12,10,3,6,10, +4,8,1310721,2,2,1646717,12,8,1587545,2,1603237,12,8,1310721,2,3, +26,1672327,12,10,3,6,10,4,8,1655683,12,8,1310721,2,3,26, +2,1609283,12,8,1587535,2,1603237,12,8,1588199,2,1593049,12,8,1587633,1587623, +2,10,3,6,10,4,8,1,1648599,14,1,1310721,2,5,26,1310721, +4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1648647,16,1966081,1310721, +4,1579223,12,1601277,12,8,1310721,2,1646277,12,10,3,6,10,4,8, +1310721,2,2,1633401,12,1592283,12,8,1587633,1587623,2,10,3,6,10,4, +8,1,1648715,14,1,1310721,2,3,26,1310721,4,18,1310721,2,3,28, +1310721,4,1310721,2,1835009,40,1648763,16,1966081,1310721,4,1579223,12,1601277,12,8, +1310721,2,1646277,12,10,3,6,10,4,8,1310721,2,2,1592283,12,8, +1587633,1587621,2,10,3,6,10,4,8,1,1648827,14,1,1310721,2,3, +26,1310721,4,18,1310721,2,1,28,1310721,4,1310721,2,1835009,40,1648875,16, +1966081,1310721,4,1579223,12,1587617,1277953,2049,26,10,3,6,10,4,8,1587619, +1,10,3,6,10,4,8,1587621,1,3,1648931,14,119,111,114,100, +0,1648921,1589927,12,1,1628411,12,10,3,6,10,4,8,1587623,3,3, +1648973,14,105,110,116,0,1648965,1589927,12,1,1628411,12,10,3,6,10, +4,8,1587625,5,3,1649017,14,99,104,97,114,0,1649007,1589927,12,1, +1628411,12,10,3,6,10,4,8,1587627,7,3,1649061,14,98,111,111, +108,0,1649051,1589927,12,1,1628411,12,10,3,6,10,4,8,1587629,9, +1,1649105,14,118,111,105,100,0,1649095,1589927,12,1,1628411,12,10,3, +6,10,4,8,1587621,2,5,26,2,15,1587621,2,1590445,12,8,1587623, +2,5,26,2,15,1587623,2,1590445,12,8,1587625,2,5,26,2,15, +1587625,2,1590445,12,8,1587627,2,5,26,2,15,1587627,2,1590445,12,8, +1587629,2,5,26,2,15,1587629,2,1590445,12,8,1587661,1649249,14,60,0, +1649245,1589927,12,10,3,6,10,4,8,1587663,1649277,14,62,0,1649273,1589927, +12,10,3,6,10,4,8,1587665,1649307,14,62,62,0,1649301,1589927,12, +10,3,6,10,4,8,1587667,1649337,14,60,61,0,1649331,1589927,12,10, +3,6,10,4,8,1587669,1649367,14,62,61,0,1649361,1589927,12,10,3, +6,10,4,8,1587671,1649397,14,61,61,0,1649391,1589927,12,10,3,6, +10,4,8,1587673,1649427,14,33,61,0,1649421,1589927,12,10,3,6,10, +4,8,1587675,1649455,14,43,0,1649451,1589927,12,10,3,6,10,4,8, +1587677,1649493,14,115,116,114,117,99,116,0,1649479,1589927,12,10,3,6, +10,4,8,1587679,1649527,14,116,121,112,101,0,1649517,1589927,12,10,3, +6,10,4,8,1587681,1649571,14,116,121,112,101,99,104,101,99,107, +0,1649551,1589927,12,10,3,6,10,4,8,1587683,1649601,14,111,110,0, +1649595,1589927,12,10,3,6,10,4,8,1587685,1649633,14,111,102,102,0, +1649625,1589927,12,10,3,6,10,4,8,1587687,1649665,14,110,101,119,0, +1649657,1589927,12,10,3,6,10,4,8,1587689,1649701,14,110,101,119,95, +116,0,1649689,1589927,12,10,3,6,10,4,8,1587691,1649731,14,111,51, +0,1649725,1589927,12,10,3,6,10,4,8,1587693,1649763,14,108,102,97, +0,1649755,1589927,12,10,3,6,10,4,8,1587695,1649803,14,111,117,116, +115,105,100,101,0,1649787,1589927,12,10,3,6,10,4,8,1587697,1649843, +14,110,101,119,95,114,97,119,0,1649827,1589927,12,10,3,6,10, +4,8,1587699,1649875,14,111,111,98,0,1649867,1589927,12,10,3,6,10, +4,8,1588185,1649911,14,102,105,120,101,100,0,1649899,1589927,12,10,3, +6,10,4,8,1588187,1649945,14,100,108,101,110,0,1649935,1589927,12,10, +3,6,10,4,8,1588189,1649979,14,100,99,97,112,0,1649969,1589927,12, +10,3,6,10,4,8,1588191,1650013,14,100,110,101,119,0,1650003,1589927, +12,10,3,6,10,4,8,1588193,1650045,14,100,97,116,0,1650037,1589927, +12,10,3,6,10,4,8,1588195,1650081,14,100,112,117,115,104,0, +1650069,1589927,12,10,3,6,10,4,8,1588197,1650115,14,100,112,111,112, +0,1650105,1589927,12,10,3,6,10,4,8,1588199,1650155,14,100,114,101, +115,105,122,101,0,1650139,1589927,12,10,3,6,10,4,8,1588159,1, +26,1650187,14,42,0,1650183,1589927,12,10,3,6,10,4,8,1588159,3, +26,1650219,14,47,0,1650215,1589927,12,10,3,6,10,4,8,1588159,5, +26,1650251,14,37,0,1650247,1589927,12,10,3,6,10,4,8,1588159,7, +26,1650283,14,38,0,1650279,1589927,12,10,3,6,10,4,8,1588159,9, +26,1650315,14,124,0,1650311,1589927,12,10,3,6,10,4,8,1588159,11, +26,1650347,14,94,0,1650343,1589927,12,10,3,6,10,4,8,1588159,13, +26,1650381,14,60,60,0,1650375,1589927,12,10,3,6,10,4,8,1588159, +15,26,1587665,2,10,3,6,10,4,8,1588183,17,10,3,6,10, +4,8,1,1650445,14,1,1310721,2,1,26,1310721,4,18,1310721,2,5, +28,1310721,4,1310721,2,1835009,40,1650493,16,1966081,1310721,4,1579223,12,1310721,2, +4,1,1310721,2,3,26,4,1310721,2,3,26,2,1588183,2,40,1650623, +16,1588159,1310721,2,3,26,2,26,2,1310721,2,2,28,1,6,1, +10,28,34,63,38,3,26,1650585,16,3,1650631,14,1310721,2,3,26, +1,6,2,1,6,3,26,5,6,4,10,8,8,1650511,14,1, +1650631,14,1,1310721,2,5,26,1310721,4,18,1310721,2,9,28,1310721,4, +1310721,2,1835009,40,1650679,16,1966081,1310721,4,1579223,12,1310721,2,5,26,4, +1310721,2,3,26,4,1310721,2,4,1546753,1310721,2,2,26,2,1310721,2, +7,26,4,1310721,2,3,26,1310721,2,3,26,2,1635131,12,10,3, +6,10,4,8,1310721,2,5,26,1310721,2,5,26,2,1635131,12,10, +3,6,10,4,8,1547137,1310721,2,2,26,2,1,40,1650991,16,1310721, +2,3,26,2,1634803,12,1,6,1,10,28,34,63,38,3,26, +1650913,16,1310721,2,5,26,2,1634803,12,1,6,1,10,28,34,63, +38,3,26,1,6,1,10,28,34,63,38,1,10,28,1650915,14, +1,1650965,14,38,38,32,97,110,100,32,124,124,32,110,101,101, +100,32,115,99,97,108,97,114,115,0,1650919,1310721,2,3,26,2, +1639507,12,8,1587627,2,1653417,14,1310721,2,7,26,2,1587671,2,28,1, +6,1,10,28,34,63,38,3,26,1651037,16,3,1651095,14,1310721,2, +7,26,2,1587673,2,28,1,6,1,10,28,34,63,38,3,26, +1,6,1,10,28,34,63,38,1,10,28,1651285,16,1310721,2,3, +26,2,1310721,2,5,26,2,1635445,12,1,6,1,10,28,34,63, +38,3,26,1651217,16,1310721,2,5,26,2,1310721,2,3,26,2,1635445, +12,1,6,1,10,28,34,63,38,3,26,1,6,1,10,28, +34,63,38,1,10,28,1651219,14,1,1651277,16,1651249,14,99,111,109, +112,97,114,105,115,111,110,0,1651227,1310721,2,3,26,2,1310721,2, +5,26,2,1639107,12,8,1587627,2,1653417,14,1310721,2,7,26,2,1587661, +2,28,1,6,1,10,28,34,63,38,3,26,1651331,16,3,1651389, +14,1310721,2,7,26,2,1587663,2,28,1,6,1,10,28,34,63, +38,3,26,1,6,1,10,28,34,63,38,1,10,28,1651399,16, +3,1651457,14,1310721,2,7,26,2,1587667,2,28,1,6,1,10,28, +34,63,38,3,26,1,6,1,10,28,34,63,38,1,10,28, +1651467,16,3,1651525,14,1310721,2,7,26,2,1587669,2,28,1,6,1, +10,28,34,63,38,3,26,1,6,1,10,28,34,63,38,1, +10,28,1651955,16,1310721,2,3,26,2,1587621,2,28,1,6,1,10, +28,34,63,38,3,26,1651575,16,3,1651633,14,1310721,2,5,26,2, +1587621,2,28,1,6,1,10,28,34,63,38,3,26,1,6,1, +10,28,34,63,38,1,10,28,1651643,16,3,1651725,14,1310721,2,3, +26,2,1634571,12,1651701,16,1310721,2,5,26,2,1634571,12,1,6,1, +10,28,34,63,38,1,10,28,1651703,14,1,1,6,1,10,28, +34,63,38,1,10,28,1651735,16,3,1651869,14,1310721,2,3,26,2, +1,26,2,11,28,1,6,1,10,28,34,63,38,3,26,1651845, +16,1310721,2,5,26,2,1,26,2,11,28,1,6,1,10,28, +34,63,38,3,26,1,6,1,10,28,34,63,38,1,10,28, +1651847,14,1,1,6,1,10,28,34,63,38,1,10,28,1,6, +1,10,28,34,63,38,3,26,1651947,16,1651919,14,99,111,109,112, +97,114,105,115,111,110,0,1651897,1310721,2,3,26,2,1310721,2,5, +26,2,1639107,12,8,1587627,2,1653417,14,1310721,2,7,26,2,1587675,2, +28,1,6,1,10,28,34,63,38,3,26,1652503,16,1310721,2,3, +26,2,1,26,2,11,28,1,6,1,10,28,34,63,38,3, +26,1652147,16,1310721,2,5,26,2,1634571,12,1652063,16,3,1652121,14,1310721, +2,5,26,2,1587621,2,28,1,6,1,10,28,34,63,38,3, +26,1,6,1,10,28,34,63,38,1,10,28,1,6,1,10, +28,34,63,38,1,10,28,1652149,14,1,1652167,16,1310721,2,3,26, +2,1653417,14,1310721,2,5,26,2,1,26,2,11,28,1,6,1, +10,28,34,63,38,3,26,1652251,16,1310721,2,3,26,2,1634571,12, +1,6,1,10,28,34,63,38,1,10,28,1652253,14,1,1652271,16, +1310721,2,5,26,2,1653417,14,1310721,2,3,26,2,1587621,2,28,1, +6,1,10,28,34,63,38,3,26,1652317,16,3,1652375,14,1310721,2, +5,26,2,1587621,2,28,1,6,1,10,28,34,63,38,3,26, +1,6,1,10,28,34,63,38,1,10,28,1652387,16,1587621,2,1653417, +14,1310721,2,3,26,2,1634571,12,1652445,16,1310721,2,5,26,2,1634571, +12,1,6,1,10,28,34,63,38,1,10,28,1652447,14,1,1652459, +16,1587623,2,1653417,14,1652467,14,43,0,1652463,1310721,2,3,26,2,1310721, +2,5,26,2,1639107,12,8,1587621,2,1653417,14,1310721,2,7,26,2, +1587565,2,28,1,6,1,10,28,34,63,38,3,26,1653071,16,1310721, +2,3,26,2,1,26,2,11,28,1,6,1,10,28,34,63, +38,3,26,1652653,16,1310721,2,5,26,2,1,26,2,11,28,1, +6,1,10,28,34,63,38,3,26,1,6,1,10,28,34,63, +38,1,10,28,1652655,14,1,1652667,16,1587623,2,1653417,14,1310721,2,3, +26,2,1,26,2,11,28,1,6,1,10,28,34,63,38,3, +26,1652819,16,1310721,2,5,26,2,1634571,12,1652735,16,3,1652793,14,1310721, +2,5,26,2,1587621,2,28,1,6,1,10,28,34,63,38,3, +26,1,6,1,10,28,34,63,38,1,10,28,1,6,1,10, +28,34,63,38,1,10,28,1652821,14,1,1652839,16,1310721,2,3,26, +2,1653417,14,1310721,2,3,26,2,1587621,2,28,1,6,1,10,28, +34,63,38,3,26,1652885,16,3,1652943,14,1310721,2,5,26,2,1587621, +2,28,1,6,1,10,28,34,63,38,3,26,1,6,1,10, +28,34,63,38,1,10,28,1652955,16,1587621,2,1653417,14,1310721,2,3, +26,2,1634571,12,1653013,16,1310721,2,5,26,2,1634571,12,1,6,1, +10,28,34,63,38,1,10,28,1653015,14,1,1653027,16,1587623,2,1653417, +14,1653035,14,45,0,1653031,1310721,2,3,26,2,1310721,2,5,26,2, +1639107,12,8,1587621,2,1653417,14,1310721,2,7,26,2,1650459,12,1653407,16, +1310721,2,3,26,2,1587621,2,28,1,6,1,10,28,34,63,38, +3,26,1653135,16,3,1653193,14,1310721,2,5,26,2,1587621,2,28,1, +6,1,10,28,34,63,38,3,26,1,6,1,10,28,34,63, +38,1,10,28,1653205,16,1587621,2,1653417,14,1310721,2,3,26,2,1634571, +12,1653263,16,1310721,2,5,26,2,1634571,12,1,6,1,10,28,34, +63,38,1,10,28,1653265,14,1,1653277,16,1587623,2,1653417,14,1310721,2, +3,26,2,1634571,12,1653325,16,1310721,2,3,26,1310721,2,5,26,2, +10,3,6,10,4,8,1,1653381,14,97,114,105,116,104,109,101, +116,105,99,32,110,101,101,100,115,32,110,117,109,98,101,114, +115,0,1653331,1310721,2,3,26,2,1639507,12,8,1587621,2,1653417,14,1587621, +2,1653417,14,1,1310721,2,9,26,1310721,4,18,1310721,2,3,28,1310721, +4,1310721,2,1835009,40,1653465,16,1966081,1310721,4,1579223,12,1310721,2,4,1310721, +2,2,1,26,2,1653489,14,1,1310721,2,3,26,1310721,4,18,1310721, +2,3,28,1310721,4,1310721,2,1835009,40,1653537,16,1966081,1310721,4,1579223,12, +1310721,2,4,1310721,2,2,1633401,12,1653559,14,1,1310721,2,3,26,1310721, +4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1653607,16,1966081,1310721, +4,1579223,12,1310721,2,4,1310721,2,2,5,26,2,1653631,14,1,1310721, +2,3,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40, +1653679,16,1966081,1310721,4,1579223,12,1310721,2,4,1310721,2,2,7,26,2, +1653703,14,1,1310721,2,3,26,1310721,4,18,1310721,2,3,28,1310721,4, +1310721,2,1835009,40,1653751,16,1966081,1310721,4,1579223,12,1310721,2,4,1310721,2, +2,1632329,12,8,1310721,2,2,7,26,2,1653787,14,1,1310721,2,3, +26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,1835009,40,1653835,16, +1966081,1310721,4,1579223,12,1310721,2,3,26,4,1310721,2,4,1310721,2,2, +1632329,12,8,1310721,2,2,9,26,2,1310721,2,3,26,2,7,30, +26,2,1653899,14,1,1310721,2,5,26,1310721,4,18,1310721,2,5,28, +1310721,4,1310721,2,1835009,40,1653947,16,1966081,1310721,4,1579223,12,1310721,2,3, +26,4,1310721,2,4,1310721,2,2,1632329,12,8,1310721,2,2,9,26, +2,1310721,2,3,26,2,7,30,3,26,26,2,1654015,14,1,1310721, +2,5,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,1835009,40, +1654063,16,1966081,1310721,4,1579223,12,1310721,2,3,26,4,1310721,2,4,1310721, +2,2,1632329,12,8,1310721,2,2,9,26,2,1310721,2,3,26,2, +7,30,5,26,26,2,1654131,14,1,1310721,2,5,26,1310721,4,18, +1310721,2,3,28,1310721,4,1310721,2,1835009,40,1654179,16,1966081,1310721,4,1579223, +12,1310721,2,4,1310721,2,2,9,26,2,1654203,14,1,1310721,2,3, +26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1654251,16, +1966081,1310721,4,1579223,12,1310721,2,4,1310721,2,2,7,26,2,1654275,14, +1,1310721,2,3,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2, +1835009,40,1654323,16,1966081,1310721,4,1579223,12,1310721,2,3,26,4,1310721,2, +4,1310721,2,2,15,1310721,2,3,26,2,26,26,2,1654369,14,1, +1310721,2,5,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,1835009, +40,1654417,16,1966081,1310721,4,1579223,12,1310721,2,3,26,4,1310721,2,4, +1310721,2,2,11,26,2,13,38,1310721,2,3,26,2,1310721,2,3, +26,2,26,38,7,32,1654483,14,1,1310721,2,5,26,1310721,4,18, +1310721,2,3,28,1310721,4,1310721,2,1835009,40,1654531,16,1966081,1310721,4,1579223, +12,1310721,2,4,1310721,2,2,11,26,2,3,32,1654559,14,1,1310721, +2,3,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40, +1654607,16,1966081,1310721,4,1579223,12,1310721,2,4,1310721,2,2,1,26,2, +17,28,1,6,1,10,28,34,63,38,1,10,28,1654661,16,3, +1654715,14,1310721,2,2,11,26,2,1,6,1,10,28,34,63,38, +3,26,1,6,1,10,28,34,63,38,1,10,28,1654725,16,1, +1654749,14,1310721,2,2,13,26,2,1,26,2,1654749,14,1,1310721,2, +3,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,1835009,40,1654797, +16,1966081,1310721,4,1579223,12,1310721,2,3,26,4,1310721,2,4,1310721,2, +2,13,26,2,3,1310721,2,3,26,2,26,26,2,1654849,14,1, +1310721,2,5,26,1310721,4,18,1310721,2,59,28,1310721,4,1310721,2,1835009, +40,1654897,16,1966081,1310721,4,1579223,12,1310721,2,4,1585153,2,1310721,2,3, +26,4,3,1,10,28,1310721,2,5,26,4,1310721,2,3,26,2, +1655193,16,1310721,2,3,26,1,6,2,1,6,-1,26,5,6,4, +10,8,8,1417729,1310721,2,3,26,2,26,2,3,28,1,6,1, +10,28,34,63,38,3,26,1655097,16,1425921,1310721,2,3,26,2,26, +2,1310721,2,2,28,1,6,1,10,28,34,63,38,3,26,1, +6,1,10,28,34,63,38,1,10,28,1655099,14,1,1655149,16,1442305, +1310721,2,3,26,2,26,2,3,32,1,6,1,10,28,34,63, +38,1,10,28,1655151,14,1,1655189,16,1310721,2,5,26,1310721,2,3, +26,2,10,3,6,10,4,8,1655193,14,1654935,14,1310721,2,5,26, +2,1,40,1655217,16,1,1655471,14,1559041,1310721,2,5,26,2,26,2, +1655257,16,1559041,1310721,2,5,26,2,26,2,1655471,14,1310721,2,55,26, +1434113,1310721,2,5,26,2,26,2,3,38,63,32,10,3,6,10, +4,8,1310721,2,57,26,1,10,3,6,10,4,8,1310721,2,57, +26,2,1310721,2,55,26,2,40,1655423,16,1310721,2,7,26,1310721,2, +57,26,2,26,1587621,2,10,3,6,10,4,8,1310721,2,57,26, +1,6,2,1,6,3,26,5,6,4,10,8,8,1655323,14,1587621, +2,1310721,2,55,26,2,1310721,2,7,26,1434113,1310721,2,5,26,2, +26,2,1629653,12,1655471,14,1,1310721,2,59,26,1310721,4,18,1310721,2, +5,28,1310721,4,1310721,2,1835009,40,1655519,16,1966081,1310721,4,1579223,12,1310721, +2,4,1310721,2,2,1591445,12,1310721,2,3,26,4,1310721,2,3,26, +2,1,40,1655569,16,3,1655633,14,1417729,1310721,2,3,26,2,26,2, +3,28,1,6,1,10,28,34,63,38,1,10,28,1,6,1, +10,28,34,63,38,1,10,28,1655643,16,1,1655669,14,1425921,1310721,2, +3,26,2,26,2,1654863,12,1655669,14,1,1310721,2,5,26,1310721,4, +18,1310721,2,1,28,1310721,4,1310721,2,1835009,40,1655717,16,1966081,1310721,4, +1579223,12,1587521,2,1655781,16,1587633,2,1634803,12,1,6,1,10,28,34, +63,38,3,26,1655765,16,3,1592369,12,8,1587521,1,10,3,6,10, +4,8,1,1655789,14,1,1310721,2,1,26,1310721,4,18,1310721,2,11, +28,1310721,4,1310721,2,1835009,40,1655837,16,1966081,1310721,4,1579223,12,1310721,2, +3,26,4,1310721,2,4,1425921,1310721,2,2,26,2,1310721,2,5,26, +4,1587527,2,1310721,2,7,26,4,1587527,1310721,2,3,26,2,10,3, +6,10,4,8,1587529,1,10,3,6,10,4,8,1587633,1587621,2,10, +3,6,10,4,8,1587635,1,10,3,6,10,4,8,1310721,2,5, +26,2,1,6,12,10,8,8,1587527,1310721,2,7,26,2,10,3, +6,10,4,8,1310721,2,9,26,1587529,2,10,3,6,10,4,8, +1587521,1,10,3,6,10,4,8,1587523,1,10,3,6,10,4,8, +1310721,2,9,26,2,1656081,14,1,1310721,2,11,26,1310721,4,18,1310721, +2,1,28,1310721,4,1310721,2,1835009,40,1656129,16,1966081,1310721,4,1579223,12, +1587527,2,1656229,16,1656221,14,116,104,105,115,32,99,97,110,39,116, +32,98,101,32,117,115,101,100,32,105,110,115,105,100,101,32, +97,110,32,101,120,112,114,101,115,115,105,111,110,0,1656141,1588813, +12,8,1,1656237,14,1,1310721,2,1,26,1310721,4,18,1310721,2,13, +28,1310721,4,1310721,2,1835009,40,1656285,16,1966081,1310721,4,1579223,12,1310721,2, +5,26,4,1310721,2,3,26,4,1310721,2,4,1,1310721,2,7,26, +4,1587533,2,1603237,12,8,1587535,2,1602703,12,1,6,1,10,28,34, +63,38,3,26,1656757,16,3,1656757,16,1310721,2,9,26,1672327,12,10, +3,6,10,4,8,1655683,12,8,1310721,2,9,26,2,1310721,2,2, +1310721,2,7,26,2,1310721,2,7,26,2,26,38,7,32,1620967,12, +8,1310721,2,3,26,2,1656519,16,1310721,2,7,26,2,1310721,2,3, +26,2,7,26,2,40,1,6,1,10,28,34,63,38,1,10, +28,1656521,14,1,1656683,16,1310721,2,11,26,1310721,2,3,26,2,15, +1310721,2,7,26,2,26,26,2,10,3,6,10,4,8,1310721,2, +11,26,2,1587633,2,1310721,2,5,26,2,1636761,12,1,6,1,10, +28,34,63,38,3,26,1656683,16,1656647,14,97,114,103,117,109,101, +110,116,0,1656629,1310721,2,11,26,2,1310721,2,5,26,2,1633573,12, +1587633,2,1639107,12,8,1310721,2,7,26,1,6,2,1,6,3,26, +5,6,4,10,8,8,1587545,2,1603007,12,1,6,1,10,28,34, +63,38,3,26,1656753,16,1656757,14,1656365,14,1587535,2,1603237,12,8,1310721, +2,7,26,2,1656783,14,1,1310721,2,13,26,1310721,4,18,1310721,2, +5,28,1310721,4,1310721,2,1835009,40,1656831,16,1966081,1310721,4,1579223,12,1310721, +2,1640473,12,10,3,6,10,4,8,1310721,2,2,1,26,2,23, +28,1,6,1,10,28,34,63,38,3,26,1657217,16,1310721,2,2, +9,26,2,1633401,12,1592283,12,8,1587545,2,1603007,12,1656975,16,1310721,2, +3,26,1672327,12,10,3,6,10,4,8,1655683,12,8,1310721,2,3, +26,2,1609283,12,8,1657111,14,1,1592283,12,8,1310721,2,2,7,26, +2,1657111,16,1657103,14,97,32,102,105,120,101,100,32,97,114,114, +97,121,32,110,101,101,100,115,32,105,116,115,32,108,101,110, +103,116,104,58,32,110,101,119,40,102,105,120,101,100,32,91, +93,84,44,32,110,41,0,1657003,1588813,12,8,1310721,2,2,1592283,12, +8,1587535,2,1603237,12,8,1588191,2,1593049,12,8,1587521,1,10,3,6, +10,4,8,1587523,1,10,3,6,10,4,8,1587633,1310721,2,2,10, +3,6,10,4,8,1587635,1,10,3,6,10,4,8,3,1657737,14, +1587701,2,1657465,16,1587545,2,1603007,12,1657351,16,1310721,2,3,26,1672327,12, +10,3,6,10,4,8,1655683,12,8,1310721,2,3,26,2,1609283,12, +8,1310721,2,2,1633401,12,3,28,1,6,1,10,28,34,63,38, +1,10,28,1657347,16,1310721,2,2,1633401,12,1592283,12,8,31,1592369,12, +8,1657367,14,1310721,2,2,1633401,12,1592283,12,8,1587535,2,1603237,12,8, +1587697,2,1593049,12,8,1587521,1,10,3,6,10,4,8,1587523,1,10, +3,6,10,4,8,1587633,1310721,2,2,1628759,12,10,3,6,10,4, +8,1587635,1,10,3,6,10,4,8,3,1657737,14,1587545,2,1603007,12, +1657609,16,1310721,2,3,26,1672327,12,10,3,6,10,4,8,1655683,12, +8,1310721,2,3,26,2,1609283,12,8,1310721,2,2,1633401,12,3,28, +1,6,1,10,28,34,63,38,1,10,28,1657587,16,1310721,2,2, +1633401,12,1592283,12,8,31,1592369,12,8,1310721,2,2,1,1629133,12,1592283, +12,8,1657637,14,1310721,2,2,1633401,12,1592283,12,8,1310721,2,2,1592283, +12,8,1587535,2,1603237,12,8,1587689,2,1593049,12,8,1587521,1,10,3, +6,10,4,8,1587523,1,10,3,6,10,4,8,1587633,1310721,2,2, +1628759,12,10,3,6,10,4,8,1587635,1,10,3,6,10,4,8, +3,1657737,14,1,1310721,2,5,26,1310721,4,18,1310721,2,27,28,1310721, +4,1310721,2,1835009,40,1657785,16,1966081,1310721,4,1579223,12,1310721,2,4,1, +1310721,2,5,26,4,1,1310721,2,7,26,4,1559041,1310721,2,2,26, +2,1310721,2,9,26,4,1409537,1310721,2,2,26,2,1587687,2,28,1, +6,1,10,28,34,63,38,3,26,1657941,16,1417729,1310721,2,2,26, +2,3,28,1,6,1,10,28,34,63,38,3,26,1,6,1, +10,28,34,63,38,1,10,28,1657943,14,1,1658271,16,1602137,12,8, +1587445,2,9,28,1,6,1,10,28,34,63,38,3,26,1658103,16, +1587447,2,1587557,2,28,1,6,1,10,28,34,63,38,3,26,1658025, +16,3,1658077,14,1587447,2,1587541,2,28,1,6,1,10,28,34,63, +38,3,26,1,6,1,10,28,34,63,38,1,10,28,1,6, +1,10,28,34,63,38,1,10,28,1658105,14,1,1658123,16,1601277,12, +8,1656797,12,1659047,14,1587445,2,5,28,1,6,1,10,28,34,63, +38,3,26,1658251,16,1587447,2,1640093,12,1658173,16,3,1658225,14,1587447,2, +1587573,2,28,1,6,1,10,28,34,63,38,3,26,1,6,1, +10,28,34,63,38,1,10,28,1,6,1,10,28,34,63,38, +1,10,28,1658253,14,1,1658271,16,1601277,12,8,1656797,12,1659047,14,1587701, +2,1658343,16,1409537,1310721,2,2,26,2,1587687,2,28,1,6,1,10, +28,34,63,38,3,26,1,6,1,10,28,34,63,38,1,10, +28,1658345,14,1,1658447,16,1310721,2,5,26,1587697,2,1590943,12,10,3, +6,10,4,8,1310721,2,5,26,2,1,40,3,36,1658425,16,1310721, +2,1310721,2,5,26,2,10,3,6,10,4,8,1310721,2,5,26, +1,10,3,6,10,4,8,1442305,1310721,2,2,26,2,5,32,1658543, +16,1310721,2,5,26,1434113,1310721,2,2,26,2,13,38,10,3,6, +10,4,8,1310721,2,7,26,1434113,1310721,2,2,26,2,3,32,10, +3,6,10,4,8,1658581,14,1442305,1310721,2,2,26,1442305,1310721,2,2, +26,2,9,34,10,3,6,10,4,8,1310721,2,11,26,1,17, +1574703,12,8,1588221,2,1658643,16,1310721,2,11,26,1588205,1588221,2,1574491,12, +8,1588221,1,10,3,6,10,4,8,1310721,2,3,26,1310721,2,5, +26,2,1310721,2,9,26,2,1310721,2,11,26,1656251,12,10,3,6, +10,4,8,1442305,1310721,2,2,26,2,5,32,1658795,16,1310721,2,3, +26,2,1434113,1310721,2,2,26,2,3,38,63,32,28,1,6,1, +10,28,34,63,38,1,10,28,1,6,1,10,28,34,63,38, +1,10,28,1658797,14,1,1658877,16,1409537,1310721,2,2,26,2,1658869,14, +119,114,111,110,103,32,110,117,109,98,101,114,32,111,102,32, +97,114,103,117,109,101,110,116,115,0,1658817,1588983,12,8,1310721,2, +2,1592875,12,8,13,1592369,12,8,1587521,1,10,3,6,10,4,8, +1587523,1,10,3,6,10,4,8,1587635,1,10,3,6,10,4,8, +1587633,1587621,2,10,3,6,10,4,8,1310721,2,9,26,2,1659019,16, +1587633,1310721,2,9,26,2,9,26,2,1310721,2,11,26,1633573,12,10, +3,6,10,4,8,1310721,2,7,26,2,1659039,16,3,1659047,14,1, +1659047,14,1,1310721,2,27,26,1310721,4,18,1310721,2,11,28,1310721,4, +1310721,2,1835009,40,1659095,16,1966081,1310721,4,1579223,12,1310721,2,4,1434113,1310721, +2,2,26,2,31,32,1310721,2,3,26,4,1434113,1310721,2,2,26, +2,9,38,31,32,1310721,2,5,26,4,1,1310721,2,7,26,4, +1587533,2,1603237,12,8,1587535,2,1602703,12,1,6,1,10,28,34,63, +38,3,26,1659337,16,3,1659337,16,1310721,2,9,26,1672327,12,10,3, +6,10,4,8,1655683,12,8,1310721,2,9,26,2,1609283,12,8,1310721, +2,7,26,1,6,2,1,6,3,26,5,6,4,10,8,8, +1587545,2,1603007,12,1,6,1,10,28,34,63,38,3,26,1659333,16, +1659337,14,1659211,14,1587535,2,1603237,12,8,1310721,2,7,26,2,1310721,2, +3,26,2,28,1,6,1,10,28,34,63,38,1,10,28,1659465, +16,1409537,1310721,2,2,26,2,1659457,14,119,114,111,110,103,32,110, +117,109,98,101,114,32,111,102,32,105,110,112,117,116,115,0, +1659411,1588983,12,8,1587521,1,10,3,6,10,4,8,1587523,1,10,3, +6,10,4,8,1587633,1587621,2,10,3,6,10,4,8,1587635,1,10, +3,6,10,4,8,1442305,1310721,2,2,26,2,129,32,1659587,16,1450497, +1310721,2,2,26,2,3,28,1592875,12,8,13,1592369,12,8,1,1659785, +14,45,1425921,1310721,2,2,26,2,26,1592369,12,8,1310721,2,5,26, +2,1,28,1,6,1,10,28,34,63,38,3,26,1659655,16,1, +1592283,12,8,1310721,2,5,26,2,3,10,40,1659777,16,1409537,1310721,2, +2,26,2,1659769,14,108,101,97,118,101,115,32,115,101,118,101, +114,97,108,32,118,97,108,117,101,115,59,32,117,115,101,32, +105,116,32,102,114,111,109,32,97,115,109,0,1659691,1588983,12,8, +1,1659785,14,1,1310721,2,11,26,1310721,4,18,1310721,2,9,28,1310721, +4,1310721,2,1835009,40,1659833,16,1966081,1310721,4,1579223,12,1587521,1,10,3, +6,10,4,8,1587523,1,10,3,6,10,4,8,1587635,1,10,3, +6,10,4,8,1588201,1,10,3,6,10,4,8,1585223,2,3,28, +1,6,1,10,28,34,63,38,3,26,1660045,16,1585225,2,1592283,12, +8,1587635,1585225,2,1,28,1,6,1,10,28,34,63,38,3,26, +10,3,6,10,4,8,1588201,3,10,3,6,10,4,8,1588203,1585225, +2,10,3,6,10,4,8,1587633,1587623,2,10,3,6,10,4,8, +1601277,12,8,1,1662715,14,1585223,2,7,28,1,6,1,10,28,34, +63,38,3,26,1660121,16,1585225,2,1594383,12,8,1601277,12,8,1587633,1587625, +2,1628759,12,10,3,6,10,4,8,1,1662715,14,1585223,2,9,28, +1,6,1,10,28,34,63,38,3,26,1660373,16,1585225,2,1587533,2, +28,1,6,1,10,28,34,63,38,3,26,1660241,16,1601277,12,8, +1310721,2,7,26,1672327,12,10,3,6,10,4,8,1587535,2,1603237,12, +8,1310721,2,7,26,2,1662715,14,1585225,2,1587537,2,28,1,6,1, +10,28,34,63,38,3,26,1660283,16,1675019,12,1662715,14,1660333,14,101, +120,112,101,99,116,101,100,32,97,110,32,101,120,112,114,101, +115,115,105,111,110,0,1660287,1589197,12,8,1,1592283,12,8,1587633,1587621, +2,10,3,6,10,4,8,1,1662715,14,1585223,2,5,28,1,6, +1,10,28,34,63,38,1,10,28,1660497,16,1660457,14,101,120,112, +101,99,116,101,100,32,97,110,32,101,120,112,114,101,115,115, +105,111,110,0,1660411,1589197,12,8,1,1592283,12,8,1587633,1587621,2,10, +3,6,10,4,8,1,1662715,14,1310721,2,1585225,2,10,3,6,10, +4,8,1310721,2,3,26,1310721,2,2,1607893,12,10,3,6,10,4, +8,1310721,2,3,26,2,1,40,3,36,1660963,16,1601277,12,8,1587499, +2,1,6,1,10,28,34,63,38,3,26,1660643,16,1310721,2,3, +26,2,1608697,12,1,6,1,10,28,34,63,38,1,10,28,1660645, +14,1,1660793,16,1310721,2,2,1660785,14,105,115,32,116,114,97,99, +107,101,100,32,98,121,32,116,104,101,32,97,110,97,108,121, +122,101,114,32,97,110,100,32,99,97,110,39,116,32,98,101, +32,117,115,101,100,32,105,110,32,117,110,99,104,101,99,107, +101,100,32,99,111,100,101,0,1660659,1588983,12,8,1310721,2,3,26, +2,1593311,12,8,1587633,1556993,1310721,2,3,26,2,26,2,10,3,6, +10,4,8,1473025,1310721,2,3,26,2,26,2,1660865,16,1,1662715,14, +1587521,3,10,3,6,10,4,8,1587523,3,10,3,6,10,4,8, +1587525,1310721,2,3,26,2,10,3,6,10,4,8,1310721,2,3,26, +2,1608697,12,1660957,16,5,1310721,2,3,26,2,26,1662715,14,1,1662715, +14,1310721,2,5,26,1310721,2,2,1590943,12,10,3,6,10,4,8, +1310721,2,5,26,2,1,40,3,36,1661081,16,1417729,1310721,2,5,26, +2,26,2,11,28,1,6,1,10,28,34,63,38,3,26,1, +6,1,10,28,34,63,38,1,10,28,1661083,14,1,1661239,16,1442305, +1310721,2,5,26,2,26,2,33,32,1,6,1,10,28,34,63, +38,3,26,1661219,16,1310721,2,2,1661211,14,99,97,110,39,116,32, +98,101,32,117,115,101,100,32,105,110,115,105,100,101,32,97, +110,32,101,120,112,114,101,115,115,105,111,110,0,1661141,1589331,12, +8,1310721,2,5,26,2,3,1655803,12,1662715,14,1310721,2,5,26,2, +1,40,3,36,1661327,16,1417729,1310721,2,5,26,2,26,2,15,28, +1,6,1,10,28,34,63,38,3,26,1,6,1,10,28,34, +63,38,1,10,28,1661329,14,1,1661429,16,1310721,2,2,1661389,14,105, +115,32,97,32,116,121,112,101,44,32,110,111,116,32,97,32, +118,97,108,117,101,0,1661343,1589331,12,8,1,1592283,12,8,1587633,1587621, +2,10,3,6,10,4,8,1,1662715,14,1601277,12,8,1587633,1587621,2, +10,3,6,10,4,8,1310721,2,5,26,2,1,40,3,36,1661541, +16,1417729,1310721,2,5,26,2,26,2,9,28,1,6,1,10,28, +34,63,38,3,26,1,6,1,10,28,34,63,38,1,10,28, +1661543,14,1,1661693,16,1425921,1310721,2,5,26,2,26,2,1592283,12,8, +1587633,1587623,2,10,3,6,10,4,8,1587635,1425921,1310721,2,5,26,2, +26,2,1,28,1,6,1,10,28,34,63,38,3,26,10,3, +6,10,4,8,1588201,3,10,3,6,10,4,8,1588203,1425921,1310721,2, +5,26,2,26,2,10,3,6,10,4,8,1,1662715,14,1310721,2, +5,26,2,1,40,3,36,1661781,16,1417729,1310721,2,5,26,2,26, +2,5,28,1,6,1,10,28,34,63,38,3,26,1,6,1, +10,28,34,63,38,1,10,28,1661783,14,1,1661897,16,1425921,1310721,2, +5,26,2,26,2,1592283,12,8,1559041,1310721,2,5,26,2,26,2, +1661859,16,1587633,1559041,1310721,2,5,26,2,26,2,10,3,6,10,4, +8,1587521,3,10,3,6,10,4,8,1587523,5,10,3,6,10,4, +8,1,1662715,14,1310721,2,5,26,2,1,40,3,36,1661985,16,1417729, +1310721,2,5,26,2,26,2,7,28,1,6,1,10,28,34,63, +38,3,26,1,6,1,10,28,34,63,38,1,10,28,1661987,14, +1,1662069,16,1425921,1310721,2,5,26,2,26,2,1592283,12,8,1559041,1310721, +2,5,26,2,26,2,1662063,16,1587633,1559041,1310721,2,5,26,2,26, +2,10,3,6,10,4,8,1,1662715,14,1310721,2,5,26,2,1, +40,3,36,1662157,16,1417729,1310721,2,5,26,2,26,2,13,28,1, +6,1,10,28,34,63,38,3,26,1,6,1,10,28,34,63, +38,1,10,28,1662159,14,1,1662181,16,1310721,2,5,26,2,1659061,12, +1662715,14,1310721,2,5,26,2,1,40,1662229,16,1310721,2,5,26,1310721, +2,2,1592751,12,10,3,6,10,4,8,1587661,2,1602703,12,1662287,16, +1442305,1310721,2,5,26,2,26,2,257,32,1,6,1,10,28,34, +63,38,1,10,28,1662289,14,1,1662611,16,1601277,12,8,1588221,1,10, +3,6,10,4,8,3,1662489,16,1588221,2,17,40,3,36,1662397,16, +1662389,14,116,111,111,32,109,97,110,121,32,116,121,112,101,32, +97,114,103,117,109,101,110,116,115,0,1662341,1589197,12,8,1588205,1588221, +2,26,1640473,12,10,3,6,10,4,8,1588221,1,6,2,1,6, +3,26,5,6,4,10,8,8,1587545,2,1603007,12,1,6,1,10, +28,34,63,38,3,26,1662485,16,1662489,14,1662315,14,1640371,12,8,1587533, +2,1602703,12,1,6,1,10,28,34,63,38,3,26,1662611,16,1662603, +14,101,120,112,101,99,116,101,100,32,40,32,97,102,116,101, +114,32,116,104,101,32,116,121,112,101,32,97,114,103,117,109, +101,110,116,115,0,1662531,1589197,12,8,1587533,2,1602703,12,1662641,16,1310721, +2,5,26,2,1657751,12,1662715,14,1310721,2,5,26,2,1592875,12,8, +1559041,1310721,2,5,26,2,26,2,1662707,16,1587633,1559041,1310721,2,5,26, +2,26,2,10,3,6,10,4,8,1,1662715,14,1,1310721,2,9, +26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,1835009,40,1662763,16, +1966081,1310721,4,1579223,12,1310721,2,3,26,4,1310721,2,4,1587521,2,1, +6,1,10,28,34,63,38,3,26,1662895,16,1662887,14,43,43,32, +97,110,100,32,45,45,32,110,101,101,100,32,115,111,109,101, +116,104,105,110,103,32,116,111,32,97,115,115,105,103,110,32, +116,111,0,1662811,1589197,12,8,1587523,2,3,28,1,6,1,10,28, +34,63,38,3,26,1662957,16,1587499,2,1,6,1,10,28,34,63, +38,1,10,28,1662959,14,1,1662997,16,1587525,2,1608697,12,1,6,1, +10,28,34,63,38,1,10,28,1662999,14,1,1663099,16,1587525,2,1663091, +14,111,119,110,115,32,97,110,32,111,98,106,101,99,116,32, +97,110,100,32,99,97,110,39,116,32,98,101,32,105,110,99, +114,101,109,101,110,116,101,100,0,1663011,1589603,12,8,1587633,2,1634571, +12,1663117,16,3,1663173,14,1587633,2,1,26,2,11,28,1,6,1, +10,28,34,63,38,3,26,1,6,1,10,28,34,63,38,1, +10,28,1663183,16,3,1663235,14,1587633,2,1587621,2,28,1,6,1,10, +28,34,63,38,3,26,1,6,1,10,28,34,63,38,1,10, +28,1663313,14,43,43,32,97,110,100,32,45,45,32,110,101,101, +100,32,97,32,110,117,109,98,101,114,32,111,114,32,97,32, +112,111,105,110,116,101,114,0,1663239,1587633,2,1639507,12,8,1587521,1, +10,3,6,10,4,8,1587523,1,10,3,6,10,4,8,1587635,1, +10,3,6,10,4,8,1,1592283,12,8,7,1592369,12,8,3,1592369, +12,8,1310721,2,2,1663475,16,1,1592283,12,8,7,1592369,12,8,1310721, +2,3,26,2,1592283,12,8,27,1592369,12,8,5,1592283,12,8,7, +1592369,12,8,5,1592369,12,8,1663539,14,1310721,2,3,26,2,1592283,12, +8,27,1592369,12,8,1,1592283,12,8,7,1592369,12,8,5,1592283,12, +8,7,1592369,12,8,5,1592369,12,8,11,1592369,12,8,9,1592369,12, +8,1,1663563,14,1,1310721,2,5,26,1310721,4,18,1310721,2,11,28, +1310721,4,1310721,2,1835009,40,1663611,16,1966081,1310721,4,1579223,12,1310721,2,4, +3,1,10,28,1310721,2,9,26,4,1601277,12,8,1310721,2,3,26, +1603527,12,10,3,6,10,4,8,1655683,12,8,1310721,2,2,3,28, +1,6,1,10,28,34,63,38,3,26,1663841,16,1663829,14,117,115, +101,32,97,32,102,105,101,108,100,32,111,102,32,97,32,110, +101,119,32,111,98,106,101,99,116,32,111,110,108,121,32,97, +102,116,101,114,32,98,105,110,100,105,110,103,32,105,116,32, +116,111,32,97,32,110,97,109,101,0,1663709,1589485,12,8,1663905,14, +1310721,2,2,5,40,3,36,1663905,16,1310721,2,2,5,28,1608795,12, +8,1310721,2,9,26,1310721,2,2,5,28,10,3,6,10,4,8, +1310721,2,5,26,1587633,2,10,3,6,10,4,8,1310721,2,5,26, +2,1,26,2,11,28,1,6,1,10,28,34,63,38,3,26, +1664009,16,1310721,2,5,26,1310721,2,5,26,2,9,26,2,10,3, +6,10,4,8,1310721,2,5,26,2,1,26,2,17,28,1,6, +1,10,28,34,63,38,1,10,28,1664359,16,1588225,2,1,6,1, +10,28,34,63,38,3,26,1664133,16,1587637,2,1,6,1,10,28, +34,63,38,3,26,1,6,1,10,28,34,63,38,1,10,28, +1664135,14,1,1664303,16,1588227,12,8,93,1573255,12,8,1310721,2,3,26, +2,1573495,12,8,1664197,14,58,32,116,104,97,116,39,115,32,97, +32,0,1664173,1573495,12,8,1587633,2,1637851,12,8,1664289,14,44,32,110, +111,116,32,97,32,115,116,114,117,99,116,32,111,114,32,97, +32,112,111,105,110,116,101,114,32,116,111,32,111,110,101,0, +1664219,1573495,12,8,1588371,12,8,1587633,1587621,2,10,3,6,10,4,8, +1587521,3,10,3,6,10,4,8,1587523,9,10,3,6,10,4,8, +1,1664915,14,1310721,2,7,26,1310721,2,5,26,2,1310721,2,3,26, +2,1646029,12,10,3,6,10,4,8,1310721,2,7,26,2,1,40, +1664647,16,1588225,2,1,6,1,10,28,34,63,38,3,26,1664499,16, +1587637,2,1,6,1,10,28,34,63,38,3,26,1,6,1,10, +28,34,63,38,1,10,28,1664501,14,1,1664591,16,1588227,12,8,1310721, +2,5,26,2,1637851,12,8,1664561,14,32,104,97,115,32,110,111, +32,102,105,101,108,100,32,0,1664531,1573495,12,8,1310721,2,3,26, +2,1573495,12,8,1588371,12,8,1587633,1587621,2,10,3,6,10,4,8, +1587521,3,10,3,6,10,4,8,1587523,9,10,3,6,10,4,8, +1,1664915,14,1310721,2,5,26,2,9,26,2,1310721,2,7,26,2, +7,30,5,26,26,2,1664741,16,1310721,2,5,26,2,9,26,2, +1310721,2,7,26,2,7,30,5,26,26,2,1592283,12,8,27,1592369, +12,8,1587633,1310721,2,5,26,2,9,26,2,1310721,2,7,26,2, +7,30,3,26,26,2,10,3,6,10,4,8,1587521,3,10,3, +6,10,4,8,1587635,1,10,3,6,10,4,8,1310721,2,9,26, +2,1,40,3,36,1664891,16,1587523,7,10,3,6,10,4,8,1587525, +1310721,2,9,26,2,10,3,6,10,4,8,1664907,14,1587523,9,10, +3,6,10,4,8,1,1664915,14,1,1310721,2,11,26,1310721,4,18, +1310721,2,31,28,1310721,4,1310721,2,1835009,40,1664963,16,1966081,1310721,4,1579223, +12,1659799,12,1310721,2,4,3,1667999,16,1585223,2,5,28,1,6,1, +10,28,34,63,38,3,26,1665067,16,1585225,2,1587581,2,28,1,6, +1,10,28,34,63,38,3,26,1,6,1,10,28,34,63,38, +1,10,28,1665069,14,1,1665153,16,1601277,12,8,1310721,2,7,26,1640473, +12,10,3,6,10,4,8,1655683,12,8,1587633,1310721,2,7,26,2, +10,3,6,10,4,8,1587523,1,10,3,6,10,4,8,1664973,14, +1585223,2,9,28,1,6,1,10,28,34,63,38,1,10,28,1665197, +16,1310721,2,2,1668011,14,1585225,2,1587541,2,28,1,6,1,10,28, +34,63,38,3,26,1667219,16,1601277,12,8,1655683,12,8,1310721,2,3, +26,3,1,10,28,10,3,6,10,4,8,1310721,2,2,3,28, +1,6,1,10,28,34,63,38,3,26,1665423,16,1665411,14,105,110, +100,101,120,32,97,32,110,101,119,32,111,98,106,101,99,116, +32,111,110,108,121,32,97,102,116,101,114,32,98,105,110,100, +105,110,103,32,105,116,32,116,111,32,97,32,110,97,109,101, +0,1665309,1589485,12,8,1665487,14,1310721,2,2,5,40,3,36,1665487,16, +1310721,2,2,5,28,1608795,12,8,1310721,2,3,26,1310721,2,2,5, +28,10,3,6,10,4,8,1587633,2,1,26,2,23,28,1,6, +1,10,28,34,63,38,3,26,1666193,16,1310721,2,9,26,1587633,2, +9,26,2,10,3,6,10,4,8,1310721,2,1672327,12,10,3,6, +10,4,8,1655683,12,8,1310721,2,2,1609283,12,8,1587633,2,1634571,12, +1665611,16,3,1665663,14,1587633,2,1587621,2,28,1,6,1,10,28,34, +63,38,3,26,1,6,1,10,28,34,63,38,1,10,28,1665723, +14,97,110,32,105,110,100,101,120,32,104,97,115,32,116,111, +32,98,101,32,97,32,110,117,109,98,101,114,0,1665667,1587633,2, +1639507,12,8,1587543,2,1603237,12,8,1310721,2,29,26,1587695,2,1590943,12, +10,3,6,10,4,8,1587701,2,1665825,16,1310721,2,29,26,2,1, +40,3,36,1,6,1,10,28,34,63,38,1,10,28,1665827,14, +1,1665897,16,1417729,1310721,2,29,26,2,26,2,13,28,1,6,1, +10,28,34,63,38,3,26,1,6,1,10,28,34,63,38,1, +10,28,1665899,14,1,1666179,16,1,1592283,12,8,7,1592369,12,8,5, +1592283,12,8,7,1592369,12,8,3,1592369,12,8,45,1425921,1310721,2,29, +26,2,26,2,26,1592369,12,8,1310721,2,29,26,1592669,12,10,3, +6,10,4,8,17,1592369,12,8,3,1592283,12,8,7,1592369,12,8, +3,1592369,12,8,1587699,2,1593049,12,8,1310721,2,29,26,2,1587483,2, +1592451,12,8,11,1592369,12,8,5,1592283,12,8,27,1592369,12,8,3, +1592369,12,8,11,1592369,12,8,1310721,2,9,26,2,1633401,12,3,28, +1,6,1,10,28,34,63,38,1,10,28,1666167,16,1310721,2,9, +26,2,1633401,12,1592283,12,8,31,1592369,12,8,27,1592369,12,8,1666189, +14,1588193,2,1593049,12,8,1667059,14,1587633,2,1634967,12,1666211,16,3,1666263, +14,1587633,2,1587621,2,28,1,6,1,10,28,34,63,38,3,26, +1,6,1,10,28,34,63,38,1,10,28,1666347,14,111,110,108, +121,32,112,111,105,110,116,101,114,115,32,97,110,100,32,97, +114,114,97,121,115,32,99,97,110,32,98,101,32,105,110,100, +101,120,101,100,0,1666267,1587633,2,1639507,12,8,1310721,2,9,26,1587633, +2,1635257,12,10,3,6,10,4,8,1310721,2,5,26,1,3,28, +10,3,6,10,4,8,1587633,2,1,26,2,13,28,1,6,1, +10,28,34,63,38,3,26,1666493,16,1587633,2,7,26,2,1,10, +40,1,6,1,10,28,34,63,38,1,10,28,1666495,14,1,1666529, +16,1310721,2,5,26,1587633,2,7,26,2,10,3,6,10,4,8, +1588201,1,10,3,6,10,4,8,1310721,2,1672327,12,10,3,6,10, +4,8,1655683,12,8,1310721,2,2,1609283,12,8,1310721,2,5,26,2, +1,40,3,36,1666635,16,1588201,2,1,6,1,10,28,34,63,38, +1,10,28,1666637,14,1,1666727,16,1588203,2,1,40,1666659,16,3,1666701, +14,1588203,2,1310721,2,5,26,2,40,3,36,1,6,1,10,28, +34,63,38,1,10,28,1,6,1,10,28,34,63,38,1,10, +28,1666729,14,1,1666827,16,1666819,14,116,104,101,32,105,110,100,101, +120,32,105,115,32,111,117,116,32,111,102,32,114,97,110,103, +101,32,102,111,114,32,116,104,105,115,32,97,114,114,97,121, +0,1666737,1588813,12,8,1587633,2,1634571,12,1666845,16,3,1666897,14,1587633,2, +1587621,2,28,1,6,1,10,28,34,63,38,3,26,1,6,1, +10,28,34,63,38,1,10,28,1666957,14,97,110,32,105,110,100, +101,120,32,104,97,115,32,116,111,32,98,101,32,97,32,110, +117,109,98,101,114,0,1666901,1587633,2,1639507,12,8,1587543,2,1603237,12, +8,1310721,2,9,26,2,1633401,12,3,28,1,6,1,10,28,34, +63,38,1,10,28,1667051,16,1310721,2,9,26,2,1633401,12,1592283,12, +8,31,1592369,12,8,27,1592369,12,8,1587521,3,10,3,6,10,4, +8,1587633,1310721,2,9,26,2,10,3,6,10,4,8,1587635,1,10, +3,6,10,4,8,1310721,2,3,26,2,1,40,3,36,1667181,16, +1587523,7,10,3,6,10,4,8,1587525,1310721,2,3,26,2,10,3, +6,10,4,8,1667197,14,1587523,9,10,3,6,10,4,8,1310721,2, +1,10,3,6,10,4,8,1664973,14,1585225,2,1587571,2,28,1,6, +1,10,28,34,63,38,3,26,1667259,16,3,1667311,14,1585225,2,1587551, +2,28,1,6,1,10,28,34,63,38,3,26,1,6,1,10, +28,34,63,38,1,10,28,1667345,16,1310721,2,1310721,2,2,1663577,12, +10,3,6,10,4,8,1664973,14,1585225,2,1587533,2,28,1,6,1, +10,28,34,63,38,3,26,1667843,16,1655683,12,8,1310721,2,2,1609283, +12,8,1310721,2,11,26,1,10,3,6,10,4,8,1587633,2,1, +26,2,15,28,1,6,1,10,28,34,63,38,3,26,1667485,16, +1310721,2,11,26,1587633,2,10,3,6,10,4,8,1667589,14,1587633,2, +1587621,2,28,1,6,1,10,28,34,63,38,3,26,1667577,14,111, +110,108,121,32,102,117,110,99,116,105,111,110,115,32,99,97, +110,32,98,101,32,99,97,108,108,101,100,0,1667519,1587633,2,1639507, +12,8,1310721,2,13,26,1,17,1574703,12,8,1310721,2,5,26,1, +1310721,2,11,26,2,1310721,2,13,26,1656251,12,10,3,6,10,4, +8,1310721,2,5,26,2,1592283,12,8,7,1592369,12,8,13,1592369,12, +8,11,1592369,12,8,9,1592369,12,8,1587521,1,10,3,6,10,4, +8,1587523,1,10,3,6,10,4,8,1310721,2,1,10,3,6,10, +4,8,1587635,1,10,3,6,10,4,8,1587633,1587621,2,10,3,6, +10,4,8,1310721,2,11,26,2,1667839,16,1587633,1310721,2,11,26,2, +9,26,2,1310721,2,13,26,1633573,12,10,3,6,10,4,8,1664973, +14,1585225,2,1587553,2,28,1,6,1,10,28,34,63,38,3,26, +1667911,16,1601277,12,8,1310721,2,3,3,1662729,12,10,3,6,10,4, +8,1664973,14,1585225,2,1587555,2,28,1,6,1,10,28,34,63,38, +3,26,1667985,16,1601277,12,8,1310721,2,3,3,1,10,28,1662729,12, +10,3,6,10,4,8,1664973,14,1310721,2,2,1668011,14,1664973,14,1310721, +2,2,1668011,14,1,1310721,2,31,26,1310721,4,18,1310721,2,7,28, +1310721,4,1310721,2,1835009,40,1668059,16,1966081,1310721,4,1579223,12,1585223,2,5, +28,1,6,1,10,28,34,63,38,3,26,1668271,16,1585225,2,1587591, +2,28,1,6,1,10,28,34,63,38,3,26,1668131,16,3,1668183, +14,1585225,2,1587587,2,28,1,6,1,10,28,34,63,38,3,26, +1,6,1,10,28,34,63,38,1,10,28,1668193,16,3,1668245,14, +1585225,2,1587589,2,28,1,6,1,10,28,34,63,38,3,26,1, +6,1,10,28,34,63,38,1,10,28,1,6,1,10,28,34, +63,38,1,10,28,1668273,14,1,1668493,16,1310721,2,5,26,3,10, +3,6,10,4,8,1585225,2,1587591,2,28,1,6,1,10,28,34, +63,38,3,26,1668355,16,1310721,2,5,26,5,10,3,6,10,4, +8,1585225,2,1587589,2,28,1,6,1,10,28,34,63,38,3,26, +1668411,16,1310721,2,5,26,7,10,3,6,10,4,8,1601277,12,8, +1587557,2,1603237,12,8,1310721,2,1668025,12,10,3,6,10,4,8,1655683, +12,8,1587523,1,10,3,6,10,4,8,1310721,2,2,1310721,2,5, +26,2,1619895,12,1669891,14,1585223,2,9,28,1,6,1,10,28,34, +63,38,3,26,1669881,16,1585225,2,1587557,2,28,1,6,1,10,28, +34,63,38,3,26,1668859,16,1601277,12,8,1310721,2,1668025,12,10,3, +6,10,4,8,1655683,12,8,1310721,2,2,1609283,12,8,1587633,2,1, +26,2,11,28,1,6,1,10,28,34,63,38,3,26,1668647,16, +3,1668699,14,1587633,2,1587621,2,28,1,6,1,10,28,34,63,38, +3,26,1,6,1,10,28,34,63,38,1,10,28,1668771,14,111, +110,108,121,32,112,111,105,110,116,101,114,115,32,99,97,110, +32,98,101,32,100,101,114,101,102,101,114,101,110,99,101,100, +0,1668703,1587633,2,1639507,12,8,1587633,1587633,2,1635257,12,10,3,6,10, +4,8,1587521,3,10,3,6,10,4,8,1587523,9,10,3,6,10, +4,8,1587635,1,10,3,6,10,4,8,1,1669891,14,1585225,2,1587559, +2,28,1,6,1,10,28,34,63,38,3,26,1669375,16,1601277,12, +8,1310721,2,1668025,12,10,3,6,10,4,8,1587521,2,1,6,1, +10,28,34,63,38,3,26,1669001,16,1587633,2,1634803,12,1,6,1, +10,28,34,63,38,3,26,1,6,1,10,28,34,63,38,1, +10,28,1669003,14,1,1669079,16,1669071,14,38,32,110,101,101,100,115, +32,115,111,109,101,116,104,105,110,103,32,97,100,100,114,101, +115,115,97,98,108,101,0,1669011,1589197,12,8,1587521,2,1669141,16,1587523, +2,3,28,1,6,1,10,28,34,63,38,3,26,1,6,1, +10,28,34,63,38,1,10,28,1669143,14,1,1669187,16,1558017,1587525,2, +26,2,33,32,1,6,1,10,28,34,63,38,1,10,28,1669189, +14,1,1669265,16,1669257,14,97,110,32,111,51,32,118,97,114,105, +97,98,108,101,32,104,97,115,32,110,111,32,97,100,100,114, +101,115,115,0,1669197,1588813,12,8,1310721,2,2,5,40,3,36,1669299, +16,1310721,2,2,5,28,1608795,12,8,1587521,1,10,3,6,10,4, +8,1587523,1,10,3,6,10,4,8,1587635,1,10,3,6,10,4, +8,1587633,1587633,2,1628759,12,10,3,6,10,4,8,1,1669891,14,1585225, +2,1587553,2,28,1,6,1,10,28,34,63,38,3,26,1669433,16, +1601277,12,8,1668025,12,8,1,3,1662729,12,1669891,14,1585225,2,1587555,2, +28,1,6,1,10,28,34,63,38,3,26,1669497,16,1601277,12,8, +1668025,12,8,1,3,1,10,28,1662729,12,1669891,14,1310721,2,3,26, +1585225,2,1605961,12,10,3,6,10,4,8,1310721,2,3,26,2,1, +40,3,36,1669881,16,1601277,12,8,1310721,2,1668025,12,10,3,6,10, +4,8,1655683,12,8,1310721,2,2,1609283,12,8,1547329,1310721,2,3,26, +2,26,2,1547393,1310721,2,3,26,2,26,2,1605023,12,8,1588201,1, +10,3,6,10,4,8,1310721,2,3,26,2,3,10,40,3,36, +1669691,16,1587633,1587627,2,10,3,6,10,4,8,1669827,14,1587633,2,1587621, +2,28,1,6,1,10,28,34,63,38,1,10,28,1669827,16,1587633, +2,1634571,12,1669797,14,116,104,105,115,32,111,112,101,114,97,116, +111,114,32,110,101,101,100,115,32,97,32,110,117,109,98,101, +114,0,1669739,1587633,2,1639507,12,8,1587633,1587623,2,10,3,6,10,4, +8,1587521,1,10,3,6,10,4,8,1587523,1,10,3,6,10,4, +8,1587635,1,10,3,6,10,4,8,1,1669891,14,1664929,12,1669891,14, +1,1310721,2,7,26,1310721,4,18,1310721,2,15,28,1310721,4,1310721,2, +1835009,40,1669939,16,1966081,1310721,4,1579223,12,1310721,2,4,1668025,12,1310721,2, +3,26,4,1585223,2,9,28,1,6,1,10,28,34,63,38,3, +26,1670901,16,1310721,2,5,26,1585225,2,1605765,12,10,3,6,10,4, +8,1310721,2,5,26,2,1,40,1670051,16,1310721,2,3,26,2,1670917, +14,1310721,2,7,26,1546881,1310721,2,5,26,2,26,2,10,3,6, +10,4,8,1310721,2,7,26,2,1310721,2,2,40,1670123,16,1310721,2, +3,26,2,1670917,14,1601277,12,8,1655683,12,8,1310721,2,3,26,2, +1609283,12,8,1310721,2,13,26,1587633,2,10,3,6,10,4,8,1547137, +1310721,2,5,26,2,26,2,3,1,10,28,28,1,6,1,10, +28,34,63,38,3,26,1670423,16,1310721,2,9,26,1592669,12,10,3, +6,10,4,8,17,1592369,12,8,1310721,2,3,26,1310721,2,7,26, +2,3,26,1669905,12,10,3,6,10,4,8,1655683,12,8,1310721,2, +3,26,2,1609283,12,8,1547329,3,26,2,1547393,3,26,2,1605023,12, +8,1310721,2,11,26,1592669,12,10,3,6,10,4,8,15,1592369,12, +8,1310721,2,9,26,2,1587483,2,1592451,12,8,1,1592283,12,8,1310721, +2,11,26,2,1587483,2,1592451,12,8,1670769,14,1547137,1310721,2,5,26, +2,26,2,5,1,10,28,28,1,6,1,10,28,34,63,38, +3,26,1670671,16,1310721,2,9,26,1592669,12,10,3,6,10,4,8, +17,1592369,12,8,3,1592283,12,8,1310721,2,11,26,1592669,12,10,3, +6,10,4,8,15,1592369,12,8,1310721,2,9,26,2,1587483,2,1592451, +12,8,1310721,2,3,26,1310721,2,7,26,2,3,26,1669905,12,10, +3,6,10,4,8,1655683,12,8,1310721,2,3,26,2,1609283,12,8, +1547329,3,26,2,1547393,3,26,2,1605023,12,8,1310721,2,11,26,2, +1587483,2,1592451,12,8,1670769,14,1310721,2,3,26,1310721,2,7,26,2, +3,26,1669905,12,10,3,6,10,4,8,1655683,12,8,1310721,2,3, +26,2,1609283,12,8,1547009,1310721,2,5,26,2,26,2,1547137,1310721,2, +5,26,2,26,2,1605023,12,8,1587633,1310721,2,5,26,2,1310721,2, +13,26,2,1587633,2,1650645,12,10,3,6,10,4,8,1587521,1,10, +3,6,10,4,8,1587523,1,10,3,6,10,4,8,1310721,2,3, +26,1,10,3,6,10,4,8,1587635,1,10,3,6,10,4,8, +1588201,1,10,3,6,10,4,8,1669959,14,1310721,2,3,26,2,1670917, +14,1,1310721,2,15,26,1310721,4,18,1310721,2,1,28,1310721,4,1310721, +2,1835009,40,1670965,16,1966081,1310721,4,1579223,12,11,1592369,12,8,3,1592283, +12,8,7,1592369,12,8,11,1592369,12,8,5,1592369,12,8,1,1671013, +14,1,1310721,2,1,26,1310721,4,18,1310721,2,23,28,1310721,4,1310721, +2,1835009,40,1671061,16,1966081,1310721,4,1579223,12,1310721,2,4,1587503,2,1310721, +2,13,26,4,1587491,2,1310721,2,15,26,4,1655683,12,8,1310721,2, +2,1609283,12,8,1601277,12,8,1310721,2,3,26,1592669,12,10,3,6, +10,4,8,17,1592369,12,8,1310721,2,7,26,1610967,12,10,3,6, +10,4,8,1310721,2,17,26,1672327,12,10,3,6,10,4,8,1655683, +12,8,1310721,2,21,26,1587633,2,10,3,6,10,4,8,1310721,2, +17,26,2,3,28,1,6,1,10,28,34,63,38,1,10,28, +1671285,16,1310721,2,17,26,2,1609283,12,8,1310721,2,11,26,1587503,2, +10,3,6,10,4,8,1310721,2,9,26,1610967,12,10,3,6,10, +4,8,1310721,2,5,26,1592669,12,10,3,6,10,4,8,15,1592369, +12,8,1587561,2,1603237,12,8,1310721,2,3,26,2,1587483,2,1592451,12, +8,1310721,2,7,26,2,1611599,12,8,1587503,1310721,2,13,26,2,10, +3,6,10,4,8,1310721,2,19,26,1672327,12,10,3,6,10,4, +8,1655683,12,8,1310721,2,19,26,2,3,28,1,6,1,10,28, +34,63,38,1,10,28,1671521,16,1310721,2,19,26,2,1609283,12,8, +1310721,2,9,26,2,1310721,2,11,26,2,1310721,2,5,26,2,1587503, +2,1310721,2,15,26,2,1671659,14,101,110,100,115,32,116,104,101, +32,116,119,111,32,115,105,100,101,115,32,111,102,32,63,58, +32,105,110,32,100,105,102,102,101,114,101,110,116,32,115,116, +97,116,101,115,0,1671569,1619321,12,8,1310721,2,7,26,2,1611517,12, +8,1310721,2,21,26,2,1587633,2,1635445,12,1,6,1,10,28,34, +63,38,3,26,1671789,16,1587633,2,1310721,2,21,26,2,1635445,12,1, +6,1,10,28,34,63,38,3,26,1,6,1,10,28,34,63, +38,1,10,28,1671791,14,1,1671861,16,1671839,14,116,104,101,32,116, +119,111,32,115,105,100,101,115,32,111,102,32,63,58,0,1671799, +1310721,2,21,26,2,1587633,2,1639107,12,8,1310721,2,21,26,2,1587621, +2,28,1,6,1,10,28,34,63,38,1,10,28,1671927,16,1587633, +1310721,2,21,26,2,10,3,6,10,4,8,1587521,1,10,3,6, +10,4,8,1587523,1,10,3,6,10,4,8,1587635,1,10,3,6, +10,4,8,1310721,2,17,26,2,3,28,1,6,1,10,28,34, +63,38,3,26,1672073,16,1310721,2,19,26,2,3,28,1,6,1, +10,28,34,63,38,3,26,1,6,1,10,28,34,63,38,1, +10,28,1672075,14,1,1672085,16,3,1672313,14,1310721,2,17,26,2,3, +28,1,6,1,10,28,34,63,38,3,26,1672129,16,3,1672185,14, +1310721,2,19,26,2,3,28,1,6,1,10,28,34,63,38,3, +26,1,6,1,10,28,34,63,38,1,10,28,1672305,16,1672297,14, +98,111,116,104,32,115,105,100,101,115,32,111,102,32,63,58, +32,104,97,118,101,32,116,111,32,98,101,32,110,101,119,32, +111,98,106,101,99,116,115,44,32,111,114,32,110,101,105,116, +104,101,114,0,1672193,1589485,12,8,1,1672313,14,1,1310721,2,23,26, +1310721,4,18,1310721,2,13,28,1310721,4,1310721,2,1835009,40,1672361,16,1966081, +1310721,4,1579223,12,3,1669905,12,1310721,2,4,1585223,2,9,28,1,6, +1,10,28,34,63,38,1,10,28,1672417,16,1310721,2,2,1674511,14, +1585225,2,1587593,2,28,1,6,1,10,28,34,63,38,3,26,1672465, +16,1310721,2,2,1671027,12,1674511,14,1585225,2,1587549,2,28,1,6,1, +10,28,34,63,38,3,26,1673347,16,1587521,2,1,6,1,10,28, +34,63,38,3,26,1672591,16,1672583,14,116,104,105,115,32,99,97, +110,39,116,32,98,101,32,97,115,115,105,103,110,101,100,32, +116,111,0,1672531,1589197,12,8,1310721,2,3,26,1587523,2,10,3,6, +10,4,8,1310721,2,5,26,1587525,2,10,3,6,10,4,8,1310721, +2,11,26,1587633,2,10,3,6,10,4,8,1310721,2,11,26,2, +1634803,12,1672815,16,1672807,14,97,32,119,104,111,108,101,32,115,116, +114,117,99,116,32,111,114,32,97,114,114,97,121,32,99,97, +110,39,116,32,98,101,32,97,115,115,105,103,110,101,100,59, +32,97,115,115,105,103,110,32,105,116,115,32,102,105,101,108, +100,115,0,1672685,1588813,12,8,1601277,12,8,1310721,2,7,26,1587701,2, +1672897,16,1310721,2,3,26,2,3,28,1,6,1,10,28,34,63, +38,3,26,1,6,1,10,28,34,63,38,1,10,28,1672899,14, +1,1672949,16,1558017,1310721,2,5,26,2,26,2,33,32,1,6,1, +10,28,34,63,38,1,10,28,1672951,14,1,1673017,16,1473025,1310721,2, +5,26,2,26,2,1,6,1,10,28,34,63,38,3,26,1, +6,1,10,28,34,63,38,1,10,28,1673019,14,1,10,3,6, +10,4,8,1310721,2,7,26,2,1673053,16,9,1592369,12,8,1310721,2, +9,26,1672327,12,10,3,6,10,4,8,1655683,12,8,1310721,2,11, +26,2,1587633,2,1635445,12,1,6,1,10,28,34,63,38,3,26, +1673173,16,1673151,14,97,115,115,105,103,110,109,101,110,116,0,1673129, +1310721,2,11,26,2,1587633,2,1639107,12,8,1310721,2,7,26,2,1673231, +16,1,1592283,12,8,7,1592369,12,8,1310721,2,5,26,2,1593311,12, +8,5,1592369,12,8,1673237,14,1670931,12,8,1587521,1,10,3,6,10, +4,8,1587523,1,10,3,6,10,4,8,1587633,1310721,2,11,26,2, +10,3,6,10,4,8,1587635,1,10,3,6,10,4,8,1310721,2, +3,26,2,1310721,2,5,26,2,1310721,2,9,26,2,1622667,12,1674511, +14,1310721,2,7,26,1585225,2,1606157,12,10,3,6,10,4,8,1310721, +2,7,26,2,1,40,3,36,1674499,16,1587521,2,1,6,1,10, +28,34,63,38,3,26,1673489,16,1673481,14,116,104,105,115,32,99, +97,110,39,116,32,98,101,32,97,115,115,105,103,110,101,100, +32,116,111,0,1673429,1589197,12,8,1587523,2,3,28,1,6,1,10, +28,34,63,38,3,26,1673551,16,1587499,2,1,6,1,10,28,34, +63,38,1,10,28,1673553,14,1,1673591,16,1587525,2,1608697,12,1,6, +1,10,28,34,63,38,1,10,28,1673593,14,1,1673713,16,1587525,2, +1673705,14,111,119,110,115,32,97,110,32,111,98,106,101,99,116, +59,32,99,111,109,112,111,117,110,100,32,97,115,115,105,103, +110,109,101,110,116,32,119,111,117,108,100,32,108,111,115,101, +32,105,116,0,1673605,1589603,12,8,1310721,2,11,26,1587633,2,10,3, +6,10,4,8,1601277,12,8,1310721,2,5,26,1587701,2,1673813,16,1587523, +2,3,28,1,6,1,10,28,34,63,38,3,26,1,6,1, +10,28,34,63,38,1,10,28,1673815,14,1,1673859,16,1558017,1587525,2, +26,2,33,32,1,6,1,10,28,34,63,38,1,10,28,1673861, +14,1,1673921,16,1473025,1587525,2,26,2,1,6,1,10,28,34,63, +38,3,26,1,6,1,10,28,34,63,38,1,10,28,1673923,14, +1,10,3,6,10,4,8,1310721,2,5,26,2,1674043,16,1310721,2, +5,26,1587525,2,10,3,6,10,4,8,9,1592369,12,8,1310721,2, +5,26,2,1593311,12,8,3,1592369,12,8,1310721,2,5,26,1310721,2, +5,26,2,3,26,10,3,6,10,4,8,1674067,14,1,1592283,12, +8,7,1592369,12,8,3,1592369,12,8,1587521,1,10,3,6,10,4, +8,1310721,2,9,26,1672327,12,10,3,6,10,4,8,1655683,12,8, +1310721,2,9,26,2,1609283,12,8,1547009,1547585,1310721,2,7,26,2,26, +2,26,2,1547137,1547585,1310721,2,7,26,2,26,2,26,2,1605023,12, +8,1310721,2,3,26,1547585,1310721,2,7,26,2,26,2,1310721,2,11, +26,2,1587633,2,1650645,12,10,3,6,10,4,8,1310721,2,11,26, +2,1310721,2,3,26,2,1635445,12,1,6,1,10,28,34,63,38, +3,26,1674353,16,1674325,14,99,111,109,112,111,117,110,100,32,97, +115,115,105,103,110,109,101,110,116,0,1674285,1310721,2,11,26,2, +1310721,2,3,26,2,1639107,12,8,1310721,2,5,26,2,1674415,16,1, +1592283,12,8,7,1592369,12,8,1310721,2,5,26,2,3,28,1593311,12, +8,5,1592369,12,8,1674421,14,1670931,12,8,1587521,1,10,3,6,10, +4,8,1587523,1,10,3,6,10,4,8,1587633,1310721,2,11,26,2, +10,3,6,10,4,8,1587635,1,10,3,6,10,4,8,1,1674511, +14,1310721,2,2,1674511,14,1,1310721,2,13,26,1310721,4,18,1310721,2, +3,28,1310721,4,1310721,2,1835009,40,1674559,16,1966081,1310721,4,1579223,12,1585223, +2,9,28,1,6,1,10,28,34,63,38,3,26,1674723,16,1585225, +2,1587547,2,28,1,6,1,10,28,34,63,38,3,26,1674631,16, +3,1674683,14,1585225,2,1587537,2,28,1,6,1,10,28,34,63,38, +3,26,1,6,1,10,28,34,63,38,1,10,28,1674693,16,3, +1674719,14,1642613,12,1,6,1,10,28,34,63,38,1,10,28,1675005, +14,1585223,2,5,28,1,6,1,10,28,34,63,38,1,10,28, +1674763,16,3,1674801,14,1585225,2,1607893,12,1,40,3,36,1,6,1, +10,28,34,63,38,1,10,28,1674811,16,1,1675005,14,1642613,12,1674825, +16,3,1675005,14,1310721,2,1585225,2,1590943,12,10,3,6,10,4,8, +1310721,2,2,1,40,3,36,1674929,16,1417729,1310721,2,2,26,2,11, +28,1,6,1,10,28,34,63,38,3,26,1,6,1,10,28, +34,63,38,1,10,28,1674931,14,1,1674997,16,1442305,1310721,2,2,26, +2,33,32,1,6,1,10,28,34,63,38,3,26,1,6,1, +10,28,34,63,38,1,10,28,1674999,14,1,1675005,14,1,1310721,2, +3,26,1310721,4,18,1310721,2,9,28,1310721,4,1310721,2,1835009,40,1675053, +16,1966081,1310721,4,1579223,12,1587491,2,1310721,2,4,1,1310721,2,3,26, +4,1,1310721,2,5,26,4,1587623,2,1310721,2,7,26,4,1601277,12, +8,1587495,1,6,2,1,6,3,26,5,6,4,10,8,8,1587505, +1,6,2,1,6,3,26,5,6,4,10,8,8,1587539,2,1602703, +12,1,6,1,10,28,34,63,38,3,26,1675535,16,1585223,2,1, +28,1,6,1,10,28,34,63,38,3,26,1675259,16,1675251,14,109, +105,115,115,105,110,103,32,125,0,1675231,1589197,12,8,1674525,12,1675277, +16,1677555,12,8,1675163,14,1310721,2,3,26,1672327,12,10,3,6,10, +4,8,1655683,12,8,1587547,2,1603007,12,1675369,16,9,1592369,12,8,1310721, +2,3,26,2,1627951,12,8,1310721,2,3,26,1,10,3,6,10, +4,8,1675163,14,1587539,2,1602703,12,1,6,1,10,28,34,63,38, +3,26,1675485,16,1675477,14,101,120,112,101,99,116,101,100,32,59, +32,111,114,32,125,32,97,102,116,101,114,32,97,110,32,101, +120,112,114,101,115,115,105,111,110,0,1675405,1589197,12,8,1310721,2, +5,26,3,10,3,6,10,4,8,1310721,2,7,26,1587633,2,10, +3,6,10,4,8,1675163,14,1601277,12,8,1310721,2,5,26,2,1675593, +16,1310721,2,3,26,1310721,2,3,26,2,1627235,12,10,3,6,10, +4,8,1675601,14,1,1592283,12,8,1310721,2,2,1610695,12,8,1587495,1, +6,2,1,6,-1,26,5,6,4,10,8,8,1587505,1,6,2, +1,6,-1,26,5,6,4,10,8,8,1587521,1,10,3,6,10, +4,8,1587523,1,10,3,6,10,4,8,1587633,1310721,2,7,26,2, +10,3,6,10,4,8,1587635,1,10,3,6,10,4,8,1310721,2, +3,26,2,1675757,14,1,1310721,2,9,26,1310721,4,18,1310721,2,5, +28,1310721,4,1310721,2,1835009,40,1675805,16,1966081,1310721,4,1579223,12,1585223,2, +3,28,1,6,1,10,28,34,63,38,3,26,1675873,16,1310721,2, +1585225,2,10,3,6,10,4,8,1601277,12,8,1310721,2,2,1676573,14, +1587565,2,1603007,12,1675897,16,1,1675771,12,28,1676573,14,1585223,2,9,28, +1,6,1,10,28,34,63,38,3,26,1675989,16,1585225,2,1547265,5, +26,2,28,1,6,1,10,28,34,63,38,3,26,1,6,1, +10,28,34,63,38,1,10,28,1675991,14,1,1676013,16,1601277,12,8, +1675771,12,-1,36,1676573,14,1587533,2,1603007,12,1676065,16,1310721,2,1676587,12, +10,3,6,10,4,8,1587535,2,1603237,12,8,1310721,2,2,1676573,14, +1585223,2,5,28,1,6,1,10,28,34,63,38,3,26,1676513,16, +1310721,2,3,26,1585225,2,1590943,12,10,3,6,10,4,8,1310721,2, +3,26,2,1,40,3,36,1676481,16,1417729,1310721,2,3,26,2,26, +2,9,28,1,6,1,10,28,34,63,38,3,26,1676197,16,3, +1676259,14,1417729,1310721,2,3,26,2,26,2,7,28,1,6,1,10, +28,34,63,38,3,26,1,6,1,10,28,34,63,38,1,10, +28,1676269,16,3,1676383,14,1417729,1310721,2,3,26,2,26,2,3,28, +1,6,1,10,28,34,63,38,3,26,1676359,16,1442305,1310721,2,3, +26,2,26,2,3,32,1,6,1,10,28,34,63,38,1,10, +28,1676361,14,1,1,6,1,10,28,34,63,38,1,10,28,1676393, +16,3,1676455,14,1417729,1310721,2,3,26,2,26,2,5,28,1,6, +1,10,28,34,63,38,3,26,1,6,1,10,28,34,63,38, +1,10,28,1,6,1,10,28,34,63,38,1,10,28,1676483,14, +1,1676513,16,1601277,12,8,1425921,1310721,2,3,26,2,26,2,1676573,14, +1676557,14,101,120,112,101,99,116,101,100,32,97,32,99,111,110, +115,116,97,110,116,0,1676517,1589197,12,8,1,1676573,14,1,1310721,2, +5,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,1835009,40,1676621, +16,1966081,1310721,4,1579223,12,1675771,12,1310721,2,4,1585223,2,9,28,1, +6,1,10,28,34,63,38,3,26,1677275,16,1310721,2,3,26,1585225, +2,10,3,6,10,4,8,1310721,2,3,26,2,1546753,7,26,2, +28,1,6,1,10,28,34,63,38,3,26,1676769,16,1601277,12,8, +1310721,2,1310721,2,2,1675771,12,26,10,3,6,10,4,8,1677271,14, +1310721,2,3,26,2,1546753,9,26,2,28,1,6,1,10,28,34, +63,38,3,26,1676851,16,1601277,12,8,1310721,2,1310721,2,2,1675771,12, +28,10,3,6,10,4,8,1677271,14,1310721,2,3,26,2,1546753,1, +26,2,28,1,6,1,10,28,34,63,38,3,26,1676933,16,1601277, +12,8,1310721,2,1310721,2,2,1675771,12,30,10,3,6,10,4,8, +1677271,14,1310721,2,3,26,2,1546753,31,26,2,28,1,6,1,10, +28,34,63,38,3,26,1677015,16,1601277,12,8,1310721,2,1310721,2,2, +1675771,12,34,10,3,6,10,4,8,1677271,14,1310721,2,3,26,2, +1546753,27,26,2,28,1,6,1,10,28,34,63,38,3,26,1677097, +16,1601277,12,8,1310721,2,1310721,2,2,1675771,12,32,10,3,6,10, +4,8,1677271,14,1310721,2,3,26,2,1546753,11,26,2,28,1,6, +1,10,28,34,63,38,3,26,1677179,16,1601277,12,8,1310721,2,1310721, +2,2,1675771,12,48,10,3,6,10,4,8,1677271,14,1310721,2,3, +26,2,1546753,13,26,2,28,1,6,1,10,28,34,63,38,3, +26,1677261,16,1601277,12,8,1310721,2,1310721,2,2,1675771,12,38,10,3, +6,10,4,8,1677271,14,1310721,2,2,1677287,14,1676631,14,1310721,2,2, +1677287,14,1,1310721,2,5,26,1310721,4,18,1310721,2,1,28,1310721,4, +1310721,2,1835009,40,1677335,16,1966081,1310721,4,1579223,12,1585223,2,1,28,1, +6,1,10,28,34,63,38,1,10,28,1677423,16,1587547,2,1602703,12, +1,6,1,10,28,34,63,38,3,26,1,6,1,10,28,34, +63,38,1,10,28,1677425,14,1,1677483,16,1587539,2,1602703,12,1,6, +1,10,28,34,63,38,3,26,1,6,1,10,28,34,63,38, +1,10,28,1677485,14,1,1677499,16,1601277,12,8,1677335,14,1587547,2,1602703, +12,1677517,16,1601277,12,8,1588225,1,10,3,6,10,4,8,1,1677541, +14,1,1310721,2,1,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721, +2,1835009,40,1677589,16,1966081,1310721,4,1579223,12,1588225,2,1677605,16,1677301,12, +1678077,14,1585223,2,5,28,1,6,1,10,28,34,63,38,3,26, +1677675,16,1585225,2,1607893,12,1,40,1,6,1,10,28,34,63,38, +1,10,28,1677677,14,1,1677859,16,1310721,2,1585225,2,1590943,12,10,3, +6,10,4,8,1310721,2,2,1,40,3,36,1677785,16,1417729,1310721,2, +2,26,2,11,28,1,6,1,10,28,34,63,38,3,26,1, +6,1,10,28,34,63,38,1,10,28,1677787,14,1,1677833,16,1442305, +1310721,2,2,26,2,17,32,1,6,1,10,28,34,63,38,1, +10,28,1677835,14,1,1677859,16,1310721,2,2,1,1655803,12,8,1,1678077, +14,1642613,12,1677881,16,3,1678507,12,8,1,1678077,14,1585223,2,9,28, +1,6,1,10,28,34,63,38,3,26,1678005,16,1585225,2,1587547,2, +28,1,6,1,10,28,34,63,38,3,26,1677959,16,1601277,12,8, +1,1678077,14,1585225,2,1587537,2,28,1,6,1,10,28,34,63,38, +3,26,1678005,16,1678091,12,8,1,1678077,14,1310721,2,3,26,1672327,12, +10,3,6,10,4,8,1655683,12,8,9,1592369,12,8,1587547,2,1603237, +12,8,1310721,2,3,26,2,1627951,12,8,1,1678077,14,1,1310721,2, +5,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1678125, +16,1966081,1310721,4,1579223,12,1587491,2,1310721,2,4,1587537,2,1603237,12,8, +1587495,1,6,2,1,6,3,26,5,6,4,10,8,8,1587539,2, +1602703,12,1,6,1,10,28,34,63,38,3,26,1678279,16,1585223,2, +1,28,1,6,1,10,28,34,63,38,3,26,1678269,16,1678261,14, +109,105,115,115,105,110,103,32,125,0,1678241,1589197,12,8,1677555,12, +8,1678173,14,1310721,2,2,1610695,12,8,1601277,12,8,1587495,1,6,2, +1,6,-1,26,5,6,4,10,8,8,1,1678333,14,1,1310721,2, +3,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1678381, +16,1966081,1310721,4,1579223,12,1587491,2,1310721,2,4,1587537,2,1602703,12,1678411, +16,1678091,12,1678493,14,1587495,1,6,2,1,6,3,26,5,6,4, +10,8,8,1677555,12,8,1310721,2,2,1610695,12,8,1587495,1,6,2, +1,6,-1,26,5,6,4,10,8,8,1,1678493,14,1,1310721,2, +3,26,1310721,4,18,1310721,2,15,28,1310721,4,1310721,2,1835009,40,1678541, +16,1966081,1310721,4,1579223,12,1310721,2,4,1,1310721,2,11,26,4,1, +1310721,2,13,26,4,1310721,2,2,1678737,16,1643415,12,8,1310721,2,11, +26,1587657,2,10,3,6,10,4,8,1310721,2,13,26,1587659,2,10, +3,6,10,4,8,1310721,2,11,26,2,1587629,2,28,1,6,1, +10,28,34,63,38,3,26,1678737,16,1678729,14,97,32,118,97,114, +105,97,98,108,101,32,99,97,110,39,116,32,98,101,32,118, +111,105,100,0,1678679,1588813,12,8,1587701,2,1678751,16,3,1678777,14,1587705, +2,1,6,1,10,28,34,63,38,1,10,28,1678833,16,1310721,2, +2,1,6,1,10,28,34,63,38,3,26,1,6,1,10,28, +34,63,38,1,10,28,1678835,14,1,1678901,16,1678893,14,111,51,32, +118,97,114,105,97,98,108,101,115,32,110,101,101,100,32,97, +32,116,121,112,101,0,1678843,1588813,12,8,3,1680017,16,1310721,2,3, +26,1603527,12,10,3,6,10,4,8,1310721,2,5,26,1310721,2,3, +26,2,1608097,12,10,3,6,10,4,8,1558017,1310721,2,5,26,2, +26,1310721,2,13,26,2,10,3,6,10,4,8,1587701,2,1679015,16, +3,1679041,14,1587705,2,1,6,1,10,28,34,63,38,1,10,28, +1679085,16,1558017,1310721,2,5,26,2,26,1310721,2,13,26,2,33,34, +10,3,6,10,4,8,1587541,2,1603007,12,1679311,16,1310721,2,9,26, +1676587,12,10,3,6,10,4,8,1587543,2,1603237,12,8,1310721,2,11, +26,2,1,6,1,10,28,34,63,38,3,26,1679189,16,1310721,2, +11,26,1587621,2,10,3,6,10,4,8,1473025,1310721,2,5,26,2, +26,3,10,3,6,10,4,8,1556993,1310721,2,5,26,2,26,1310721, +2,11,26,2,1310721,2,9,26,2,1629133,12,10,3,6,10,4, +8,1587497,1587497,2,1556993,1310721,2,5,26,2,26,2,1633401,12,26,10, +3,6,10,4,8,1679581,14,1310721,2,11,26,2,1679365,16,1310721,2, +11,26,2,1634803,12,1,6,1,10,28,34,63,38,1,10,28, +1679367,14,1,1679473,16,1473025,1310721,2,5,26,2,26,3,10,3,6, +10,4,8,1556993,1310721,2,5,26,2,26,1310721,2,11,26,2,10, +3,6,10,4,8,1587497,1587497,2,1310721,2,11,26,2,1633401,12,26, +10,3,6,10,4,8,1679581,14,1587497,1,6,2,1,6,3,26, +5,6,4,10,8,8,1310721,2,11,26,2,1679551,16,1556993,1310721,2, +5,26,2,26,1310721,2,11,26,2,10,3,6,10,4,8,1587701, +2,1679581,16,1467905,1310721,2,5,26,2,26,2,1593579,12,8,1587549,2, +1603007,12,1679977,16,1473025,1310721,2,5,26,2,26,2,1679723,16,1679715,14, +97,110,32,97,114,114,97,121,32,111,114,32,97,32,115,116, +114,117,99,116,32,99,97,110,39,116,32,98,101,32,105,110, +105,116,105,97,108,105,122,101,100,32,119,105,116,104,32,61, +0,1679617,1588813,12,8,1310721,2,7,26,1672327,12,10,3,6,10,4, +8,1655683,12,8,1310721,2,5,26,2,1593311,12,8,5,1592369,12,8, +1310721,2,11,26,2,1679887,16,1310721,2,11,26,2,1587633,2,1635445,12, +1,6,1,10,28,34,63,38,3,26,1679883,16,1679861,14,105,110, +105,116,105,97,108,105,122,101,114,0,1679837,1310721,2,11,26,2, +1587633,2,1639107,12,8,1679949,14,1587633,2,1634571,12,1,6,1,10,28, +34,63,38,3,26,1679949,16,1556993,1310721,2,5,26,2,26,1587633,2, +10,3,6,10,4,8,3,1310721,2,5,26,2,1310721,2,7,26, +2,1622667,12,8,1587545,2,1603007,12,1,6,1,10,28,34,63,38, +3,26,1680013,16,1680017,14,1678901,14,1587547,2,1603237,12,8,1,1680035,14, +1,1310721,2,15,26,1310721,4,18,1310721,2,1,28,1310721,4,1310721,2, +1835009,40,1680083,16,1966081,1310721,4,1579223,12,1656095,12,8,1601277,12,8,1, +1678507,12,1680107,14,1,1310721,2,1,26,1310721,4,18,1310721,2,21,28, +1310721,4,1310721,2,1835009,40,1680155,16,1966081,1310721,4,1579223,12,1587491,2,1310721, +2,15,26,4,1656095,12,8,1601277,12,8,1587533,2,1603237,12,8,1310721, +2,5,26,1672327,12,10,3,6,10,4,8,1655683,12,8,1310721,2, +5,26,2,1609283,12,8,1587535,2,1603237,12,8,1310721,2,1592669,12,10, +3,6,10,4,8,17,1592369,12,8,1310721,2,11,26,1587503,2,10, +3,6,10,4,8,1310721,2,7,26,1610967,12,10,3,6,10,4, +8,1678347,12,8,1310721,2,13,26,1587503,2,10,3,6,10,4,8, +1310721,2,9,26,1610967,12,10,3,6,10,4,8,1587579,2,1602855,12, +1680643,16,1601277,12,8,1310721,2,3,26,1592669,12,10,3,6,10,4, +8,15,1592369,12,8,1310721,2,2,1587483,2,1592451,12,8,1310721,2,7, +26,2,1611599,12,8,1587503,1310721,2,11,26,2,10,3,6,10,4, +8,1678347,12,8,1310721,2,9,26,2,1310721,2,13,26,2,1310721,2, +3,26,2,1587503,2,1310721,2,15,26,2,1680631,14,101,110,100,115, +32,116,104,101,32,98,114,97,110,99,104,101,115,32,111,102, +32,97,110,32,105,102,32,105,110,32,100,105,102,102,101,114, +101,110,116,32,115,116,97,116,101,115,0,1680537,1619321,12,8,1681175, +14,1613965,1,10,3,6,10,4,8,1310721,2,13,26,2,1680693,16, +1507841,1310721,2,9,26,2,26,1614933,12,8,1310721,2,11,26,2,1680727, +16,1507841,1310721,2,7,26,2,26,1614933,12,8,1310721,2,15,26,2, +1680835,14,101,110,100,115,32,116,104,101,32,98,114,97,110,99, +104,101,115,32,111,102,32,97,110,32,105,102,32,105,110,32, +100,105,102,102,101,114,101,110,116,32,115,116,97,116,101,115, +0,1680741,1615661,12,8,1310721,2,13,26,2,1680889,16,1507841,1310721,2,9, +26,2,26,1310721,2,15,26,2,3,1617245,12,8,1310721,2,17,26, +1,10,3,6,10,4,8,1310721,2,11,26,2,1680975,16,1310721,2, +17,26,1507841,1310721,2,7,26,2,26,1310721,2,15,26,2,1,1617245, +12,10,3,6,10,4,8,1310721,2,17,26,2,1681093,16,1310721,2, +19,26,1592669,12,10,3,6,10,4,8,15,1592369,12,8,1310721,2, +2,1587483,2,1592451,12,8,1507841,1310721,2,7,26,2,26,1310721,2,15, +26,2,3,1617245,12,8,1310721,2,19,26,2,1587483,2,1592451,12,8, +1681109,14,1310721,2,2,1587483,2,1592451,12,8,1587503,1310721,2,13,26,2, +1681131,16,3,1681163,14,1310721,2,11,26,2,1,6,1,10,28,34, +63,38,1,10,28,10,3,6,10,4,8,1310721,2,7,26,2, +1611517,12,8,1,1681199,14,1,1310721,2,21,26,1310721,4,18,1310721,2, +7,28,1310721,4,1310721,2,1835009,40,1681247,16,1966081,1310721,4,1579223,12,1310721, +2,3,26,4,1310721,2,4,1552897,1587513,2,17,30,26,1310721,2,5, +26,4,1587513,2,121,40,3,36,1681361,16,1681353,14,108,111,111,112, +115,32,110,101,115,116,101,100,32,116,111,111,32,100,101,101, +112,108,121,0,1681305,1588719,12,8,1310721,2,5,26,2,1,26,1310721, +2,2,10,3,6,10,4,8,1310721,2,5,26,2,3,26,1, +10,3,6,10,4,8,1310721,2,5,26,2,5,26,1587495,2,10, +3,6,10,4,8,1310721,2,5,26,2,7,26,1310721,2,3,26, +2,10,3,6,10,4,8,1310721,2,5,26,2,9,26,1587505,2, +10,3,6,10,4,8,1310721,2,5,26,2,11,26,1587491,2,10, +3,6,10,4,8,1310721,2,5,26,2,13,26,1613835,2,10,3, +6,10,4,8,1587513,1,6,2,1,6,3,26,5,6,4,10, +8,8,1,1681613,14,1,1310721,2,7,26,1310721,4,18,1310721,2,15, +28,1310721,4,1310721,2,1835009,40,1681661,16,1966081,1310721,4,1579223,12,1310721,2, +3,26,4,1310721,2,4,1,1310721,2,13,26,4,1587513,1,6,2, +1,6,-1,26,5,6,4,10,8,8,1310721,2,5,26,1552897,1587513, +2,17,30,26,10,3,6,10,4,8,1310721,2,11,26,1310721,2, +5,26,2,11,26,2,10,3,6,10,4,8,1613965,1,10,3, +6,10,4,8,1310721,2,3,26,2,1,40,3,36,1681843,16,1507841, +1310721,2,3,26,2,26,1614933,12,8,1310721,2,7,26,1567233,1310721,2, +5,26,2,13,26,2,26,10,3,6,10,4,8,1310721,2,9, +26,1567233,1613835,2,26,10,3,6,10,4,8,1310721,2,7,26,2, +1310721,2,9,26,2,40,1682035,16,1310721,2,7,26,2,3,26,1614933, +12,8,1310721,2,7,26,1310721,2,7,26,2,7,26,7,1310721,2, +7,26,2,5,26,2,1310721,2,7,26,2,3,26,2,28,30, +26,10,3,6,10,4,8,1681911,14,1310721,2,11,26,2,1682159,14, +108,101,97,118,101,115,32,116,104,101,32,108,111,111,112,32, +105,110,32,100,105,102,102,101,114,101,110,116,32,115,116,97, +116,101,115,32,111,110,32,100,105,102,102,101,114,101,110,116, +32,112,97,116,104,115,0,1682049,1615661,12,8,1587503,1613965,2,1,10, +40,10,3,6,10,4,8,1310721,2,7,26,1567233,1310721,2,5,26, +2,13,26,2,26,10,3,6,10,4,8,1310721,2,7,26,2, +1310721,2,9,26,2,40,1682527,16,1310721,2,7,26,2,3,26,1310721, +2,11,26,2,1,1617245,12,1682399,16,1310721,2,7,26,2,1,26, +2,1587483,2,1592451,12,8,1310721,2,7,26,2,3,26,1310721,2,11, +26,2,3,1617245,12,8,1310721,2,13,26,1592669,12,1310721,2,13,26, +2,1591519,12,10,3,6,10,4,8,15,1592369,12,8,1682449,14,1310721, +2,13,26,1310721,2,7,26,2,1,26,2,1310721,2,13,26,2, +1591519,12,10,3,6,10,4,8,1310721,2,7,26,1310721,2,7,26, +2,7,26,7,1310721,2,7,26,2,5,26,2,1310721,2,7,26, +2,3,26,2,28,30,26,10,3,6,10,4,8,1682231,14,1310721, +2,2,1,40,3,36,1682561,16,1310721,2,2,1587483,2,1592451,12,8, +1310721,2,3,26,2,1,40,3,36,1682615,16,1507841,1310721,2,3,26, +2,26,1310721,2,11,26,2,3,1617245,12,8,1310721,2,13,26,2, +1587483,2,1591871,12,8,1613835,1310721,2,5,26,2,13,26,2,10,3, +6,10,4,8,1,1682673,14,1,1310721,2,15,26,1310721,4,18,1310721, +2,13,28,1310721,4,1310721,2,1835009,40,1682721,16,1966081,1310721,4,1579223,12, +1587483,2,1310721,2,4,3,1,10,28,1310721,2,7,26,4,1,1310721, +2,11,26,4,1656095,12,8,1601277,12,8,1310721,2,5,26,1610967,12, +10,3,6,10,4,8,1587533,2,1603237,12,8,1585223,2,3,28,1, +6,1,10,28,34,63,38,3,26,1682869,16,1585225,2,1,6,1, +10,28,34,63,38,1,10,28,1682871,14,1,1682997,16,1602137,12,8, +1587445,2,9,28,1,6,1,10,28,34,63,38,3,26,1682969,16, +1587447,2,1587535,2,28,1,6,1,10,28,34,63,38,3,26,1, +6,1,10,28,34,63,38,1,10,28,1682971,14,1,1682997,16,1310721, +2,11,26,3,10,3,6,10,4,8,1310721,2,9,26,1672327,12, +10,3,6,10,4,8,1655683,12,8,1310721,2,9,26,2,1609283,12, +8,1587535,2,1603237,12,8,1310721,2,3,26,1592669,12,10,3,6,10, +4,8,17,1592369,12,8,1310721,2,11,26,2,1,6,1,10,28, +34,63,38,3,26,1683149,16,1587503,2,1,6,1,10,28,34,63, +38,1,10,28,1683151,14,1,1683179,16,1310721,2,7,26,1610967,12,10, +3,6,10,4,8,1310721,2,2,1310721,2,5,26,2,1681213,12,8, +1678347,12,8,1310721,2,5,26,2,1617739,12,8,1310721,2,2,1592587,12, +8,1310721,2,3,26,2,1310721,2,7,26,2,1681627,12,8,1310721,2, +5,26,2,1611517,12,8,1,1683285,14,1,1310721,2,13,26,1310721,4, +18,1310721,2,19,28,1310721,4,1310721,2,1835009,40,1683333,16,1966081,1310721,4, +1579223,12,1587491,2,1310721,2,4,3,1,10,28,1310721,2,5,26,4, +1,1310721,2,17,26,4,1656095,12,8,1601277,12,8,1587533,2,1603237,12, +8,1587495,1,6,2,1,6,3,26,5,6,4,10,8,8,1587575, +2,1602855,12,1683445,16,1680049,12,8,1683561,14,1642613,12,1683465,16,3,1678507, +12,8,1683561,14,1587547,2,1602703,12,1,6,1,10,28,34,63,38, +3,26,1683551,16,1310721,2,15,26,1672327,12,10,3,6,10,4,8, +1655683,12,8,9,1592369,12,8,1310721,2,15,26,2,1627951,12,8,1587547, +2,1603237,12,8,1310721,2,3,26,1610967,12,10,3,6,10,4,8, +1310721,2,7,26,1587483,2,10,3,6,10,4,8,1587547,2,1602703,12, +1,6,1,10,28,34,63,38,3,26,1683881,16,1585223,2,3,28, +1,6,1,10,28,34,63,38,3,26,1683703,16,1585225,2,1,6, +1,10,28,34,63,38,1,10,28,1683705,14,1,1683831,16,1602137,12, +8,1587445,2,9,28,1,6,1,10,28,34,63,38,3,26,1683803, +16,1587447,2,1587547,2,28,1,6,1,10,28,34,63,38,3,26, +1,6,1,10,28,34,63,38,1,10,28,1683805,14,1,1683831,16, +1310721,2,17,26,3,10,3,6,10,4,8,1310721,2,15,26,1672327, +12,10,3,6,10,4,8,1655683,12,8,1310721,2,15,26,2,1609283, +12,8,1683911,14,3,1592283,12,8,1310721,2,17,26,3,10,3,6, +10,4,8,1587547,2,1603237,12,8,1310721,2,9,26,1592669,12,10,3, +6,10,4,8,17,1592369,12,8,1310721,2,17,26,2,1,6,1, +10,28,34,63,38,3,26,1684017,16,1587503,2,1,6,1,10,28, +34,63,38,1,10,28,1684019,14,1,1684047,16,1310721,2,5,26,1610967, +12,10,3,6,10,4,8,1310721,2,11,26,1592669,12,10,3,6, +10,4,8,15,1592369,12,8,1310721,2,13,26,1587483,2,10,3,6, +10,4,8,1587535,2,1602703,12,1,6,1,10,28,34,63,38,3, +26,1684189,16,1310721,2,15,26,1672327,12,10,3,6,10,4,8,1655683, +12,8,9,1592369,12,8,1310721,2,15,26,2,1627951,12,8,1587535,2, +1603237,12,8,1310721,2,7,26,2,1592587,12,8,1310721,2,11,26,2, +1587483,2,1592451,12,8,1310721,2,13,26,2,1310721,2,3,26,2,1681213, +12,8,1678347,12,8,1310721,2,3,26,2,1617739,12,8,1310721,2,13, +26,2,1592587,12,8,1310721,2,9,26,2,1310721,2,5,26,2,1681627, +12,8,1310721,2,3,26,2,1611517,12,8,1310721,2,2,1610695,12,8, +1587495,1,6,2,1,6,-1,26,5,6,4,10,8,8,1,1684389, +14,1,1310721,2,19,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721, +2,1835009,40,1684437,16,1966081,1310721,4,1579223,12,1656095,12,8,1601277,12,8, +1587547,2,1603237,12,8,1587513,2,1587515,2,10,40,3,36,1684563,16,1684549, +14,98,114,101,97,107,32,111,117,116,115,105,100,101,32,97, +32,108,111,111,112,32,111,114,32,97,32,115,119,105,116,99, +104,0,1684483,1588813,12,8,1,1684815,14,1310721,2,1552897,1587513,2,3,28, +17,30,26,10,3,6,10,4,8,1310721,2,2,9,26,2,1587505, +2,28,1,6,1,10,28,34,63,38,1,10,28,1684731,16,1684717, +14,98,114,101,97,107,32,99,97,110,39,116,32,108,101,97, +118,101,32,97,32,98,108,111,99,107,32,101,120,112,114,101, +115,115,105,111,110,0,1684643,1588813,12,8,1,1684815,14,1310721,2,2, +5,26,2,3,26,1,1609669,12,8,1587503,2,1684777,16,1592669,12,1614341, +12,8,1684783,14,1592669,12,8,15,1592369,12,8,1587503,1,10,3,6, +10,4,8,1,1684815,14,1,1310721,2,3,26,1310721,4,18,1310721,2, +5,28,1310721,4,1310721,2,1835009,40,1684863,16,1966081,1310721,4,1579223,12,1587513, +2,3,28,1310721,2,4,1656095,12,8,1601277,12,8,1587547,2,1603237,12, +8,1310721,2,2,1587515,2,40,3,36,1684991,16,1552897,1310721,2,2,17, +30,26,2,3,1,10,28,28,1,6,1,10,28,34,63,38, +3,26,1,6,1,10,28,34,63,38,1,10,28,1684993,14,1, +1685031,16,1310721,2,1,6,2,1,6,-1,26,5,6,4,10,8, +8,1684899,14,1310721,2,2,1587515,2,40,1685113,16,1685099,14,99,111,110, +116,105,110,117,101,32,111,117,116,115,105,100,101,32,97,32, +108,111,111,112,0,1685051,1588813,12,8,1,1685389,14,1310721,2,3,26, +1552897,1310721,2,2,17,30,26,10,3,6,10,4,8,1310721,2,3, +26,2,9,26,2,1587505,2,28,1,6,1,10,28,34,63,38, +1,10,28,1685293,16,1685279,14,99,111,110,116,105,110,117,101,32, +99,97,110,39,116,32,108,101,97,118,101,32,97,32,98,108, +111,99,107,32,101,120,112,114,101,115,115,105,111,110,0,1685199, +1588813,12,8,1,1685389,14,1310721,2,3,26,2,7,26,2,1617739,12, +8,1310721,2,3,26,2,5,26,2,3,26,1,1609669,12,8,1310721, +2,3,26,2,1,26,2,1592587,12,8,1587503,1,10,3,6,10, +4,8,1,1685389,14,1,1310721,2,5,26,1310721,4,18,1310721,2,7, +28,1310721,4,1310721,2,1835009,40,1685437,16,1966081,1310721,4,1579223,12,1310721,2, +3,26,4,1310721,2,4,1587491,2,1310721,2,5,26,4,1310721,2,2, +1611599,12,8,1587503,1310721,2,3,26,2,10,3,6,10,4,8,1587495, +1,6,2,1,6,3,26,5,6,4,10,8,8,1587539,2,1602703, +12,1,6,1,10,28,34,63,38,3,26,1685617,16,1587595,2,1602855, +12,1,6,1,10,28,34,63,38,3,26,1,6,1,10,28, +34,63,38,1,10,28,1685619,14,1,1685677,16,1587597,2,1602855,12,1, +6,1,10,28,34,63,38,3,26,1,6,1,10,28,34,63, +38,1,10,28,1685679,14,1,1685761,16,1585223,2,1,28,1,6,1, +10,28,34,63,38,3,26,1685751,16,1685739,14,109,105,115,115,105, +110,103,32,125,0,1685719,1589197,12,8,1685761,14,1677555,12,8,1685531,14, +1310721,2,5,26,2,1610695,12,8,1587495,1,6,2,1,6,-1,26, +5,6,4,10,8,8,1587503,2,1685831,16,1592669,12,1614341,12,8,15, +1592369,12,8,1587503,1,10,3,6,10,4,8,1,1685855,14,1,1310721, +2,7,26,1310721,4,18,1310721,2,23,28,1310721,4,1310721,2,1835009,40, +1685903,16,1966081,1310721,4,1579223,12,1587491,2,1310721,2,4,3,1,10,28, +1310721,2,11,26,4,3,1,10,28,1310721,2,21,26,4,1656095,12, +8,1601277,12,8,1587533,2,1603237,12,8,1310721,2,5,26,1672327,12,10, +3,6,10,4,8,1655683,12,8,1310721,2,5,26,2,1609283,12,8, +1587535,2,1603237,12,8,1587495,1,6,2,1,6,3,26,5,6,4, +10,8,8,1310721,2,3,26,1,1608097,12,10,3,6,10,4,8, +1587497,1,6,2,1,6,3,26,5,6,4,10,8,8,1467905,1310721, +2,3,26,2,26,2,1593203,12,8,5,1592369,12,8,1310721,2,9, +26,1587503,2,10,3,6,10,4,8,1310721,2,7,26,1610967,12,10, +3,6,10,4,8,3,1,10,28,1310721,2,7,26,2,1681213,12, +8,1587537,2,1603237,12,8,1587539,2,1602703,12,1,6,1,10,28,34, +63,38,3,26,1686927,16,1585223,2,1,28,1,6,1,10,28,34, +63,38,3,26,1686321,16,1686309,14,109,105,115,115,105,110,103,32, +125,0,1686289,1589197,12,8,1686927,14,1587595,2,1602855,12,1686643,16,1601277,12, +8,1310721,2,17,26,1,10,3,6,10,4,8,3,1686531,16,1310721, +2,19,26,1676587,12,10,3,6,10,4,8,1467905,1310721,2,3,26, +2,26,2,1593203,12,8,3,1592369,12,8,1310721,2,19,26,2,1592283, +12,8,29,1592369,12,8,1310721,2,17,26,1592669,12,1310721,2,17,26, +2,1591519,12,10,3,6,10,4,8,17,1592369,12,8,1587545,2,1603007, +12,1,6,1,10,28,34,63,38,3,26,1686527,16,1686531,14,1686361, +14,1587561,2,1603237,12,8,1310721,2,15,26,1592669,12,10,3,6,10, +4,8,15,1592369,12,8,1310721,2,17,26,2,1587483,2,1591871,12,8, +1310721,2,7,26,2,1310721,2,9,26,2,1685403,12,8,1310721,2,15, +26,2,1587483,2,1592451,12,8,1686923,14,1587597,2,1602855,12,1686861,16,1601277, +12,8,1587561,2,1603237,12,8,1310721,2,11,26,2,1,40,3,36, +1686755,16,1686747,14,97,32,115,119,105,116,99,104,32,104,97,115, +32,111,110,101,32,100,101,102,97,117,108,116,0,1686697,1588813,12, +8,1310721,2,13,26,1592669,12,10,3,6,10,4,8,15,1592369,12, +8,1310721,2,11,26,1587483,2,10,3,6,10,4,8,1310721,2,7, +26,2,1310721,2,9,26,2,1685403,12,8,1310721,2,13,26,2,1587483, +2,1592451,12,8,1686923,14,1686915,14,101,120,112,101,99,116,101,100, +32,99,97,115,101,32,111,114,32,100,101,102,97,117,108,116, +0,1686865,1589197,12,8,1686221,14,1601277,12,8,1310721,2,7,26,2,1611599, +12,8,1587503,1310721,2,9,26,2,10,3,6,10,4,8,1310721,2, +11,26,2,1,40,3,36,1687037,16,1310721,2,11,26,2,1592587,12, +8,3,1,10,28,3,1,10,28,1681627,12,8,1687095,14,1310721,2, +21,26,1592669,12,10,3,6,10,4,8,15,1592369,12,8,1310721,2, +21,26,2,1310721,2,7,26,2,1681627,12,8,1310721,2,7,26,2, +1611517,12,8,1310721,2,2,1610695,12,8,1587495,1,6,2,1,6,-1, +26,5,6,4,10,8,8,1,1687159,14,1,1310721,2,23,26,1310721, +4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1687207,16,1966081,1310721, +4,1579223,12,1656095,12,8,1601277,12,8,1587511,2,1,6,1,10,28, +34,63,38,3,26,1687311,16,1687303,14,114,101,116,117,114,110,32, +111,117,116,115,105,100,101,32,97,32,102,117,110,99,116,105, +111,110,0,1687251,1588813,12,8,1587505,2,1687407,16,1687399,14,114,101,116, +117,114,110,32,99,97,110,39,116,32,108,101,97,118,101,32, +97,32,98,108,111,99,107,32,101,120,112,114,101,115,115,105, +111,110,0,1687323,1588813,12,8,1587547,2,1602703,12,1687449,16,1,1592283,12, +8,1310721,2,1,10,3,6,10,4,8,1687677,14,1310721,2,1672327,12, +10,3,6,10,4,8,1655683,12,8,1587531,2,1587629,2,28,1,6, +1,10,28,34,63,38,3,26,1687559,16,1687539,14,114,101,116,117, +114,110,32,118,97,108,117,101,0,1687513,1587629,2,1587633,2,1639107,12, +8,1687677,14,1587531,2,1687625,16,1587531,2,1587633,2,1635445,12,1,6,1, +10,28,34,63,38,3,26,1,6,1,10,28,34,63,38,1, +10,28,1687627,14,1,1687677,16,1687661,14,114,101,116,117,114,110,32, +118,97,108,117,101,0,1687635,1587531,2,1587633,2,1639107,12,8,1587547,2, +1603237,12,8,1310721,2,2,1626221,12,8,3,1,1609669,12,8,1587507,1587483, +2,1587507,2,1591519,12,10,3,6,10,4,8,2097153,3,28,1592283,12, +8,15,1592369,12,8,1587503,1,10,3,6,10,4,8,1,1687779,14, +1,1310721,2,3,26,1310721,4,18,1310721,2,7,28,1310721,4,1310721,2, +1835009,40,1687827,16,1966081,1310721,4,1579223,12,1656095,12,8,1601277,12,8,1587537, +2,1602703,12,1,6,1,10,28,34,63,38,3,26,1687917,16,1587705, +3,10,3,6,10,4,8,3,1678507,12,8,1587705,1,10,3,6, +10,4,8,1,1688481,14,1587701,2,1687931,16,3,1687977,14,1587511,2,1, +6,1,10,28,34,63,38,3,26,1,6,1,10,28,34,63, +38,1,10,28,1687989,16,1678091,12,1688481,14,1310721,2,1587483,2,5,26, +10,3,6,10,4,8,1310721,2,2,1592283,12,8,15,1592369,12,8, +1587701,3,10,3,6,10,4,8,1587723,1,10,3,6,10,4,8, +1310721,2,3,26,1587493,2,10,3,6,10,4,8,1310721,2,3,26, +2,1587491,2,40,1688261,16,1558017,1310721,2,3,26,2,26,2,33,32, +1688195,16,1473025,1310721,2,3,26,2,26,2,1,6,1,10,28,34, +63,38,3,26,1,6,1,10,28,34,63,38,1,10,28,1688197, +14,1,1688223,16,1467905,1310721,2,3,26,2,26,2,1593579,12,8,1310721, +2,3,26,1,6,2,1,6,3,26,5,6,4,10,8,8, +1688089,14,1678091,12,8,1587701,1,10,3,6,10,4,8,1588125,2,41, +40,1688473,16,1310721,2,5,26,1587725,1588125,2,21,30,26,10,3,6, +10,4,8,1310721,2,5,26,2,1,26,1310721,2,2,10,3,6, +10,4,8,1310721,2,5,26,2,3,26,1587483,2,10,3,6,10, +4,8,1310721,2,5,26,2,5,26,1587723,2,10,3,6,10,4, +8,1310721,2,5,26,2,7,26,1587707,1587723,2,1574491,12,8,1588125,1, +6,2,1,6,3,26,5,6,4,10,8,8,1,1688481,14,1, +1310721,2,7,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009, +40,1688529,16,1966081,1310721,4,1579223,12,1587499,2,1310721,2,4,1656095,12,8, +1601277,12,8,1587499,1,10,3,6,10,4,8,1678347,12,8,1587499,1310721, +2,2,10,3,6,10,4,8,1,1688601,14,1,1310721,2,3,26, +1310721,4,18,1310721,2,21,28,1310721,4,1310721,2,1835009,40,1688649,16,1966081, +1310721,4,1579223,12,1,1310721,2,4,1,1310721,2,3,26,4,1554945,1310721, +2,13,26,4,1554945,129,26,1310721,2,15,26,4,1554945,257,26,1310721, +2,17,26,4,1554945,769,26,1310721,2,19,26,4,1587537,2,1603237,12, +8,1587539,2,1602703,12,1,6,1,10,28,34,63,38,3,26,1690925, +16,1585223,2,1,28,1,6,1,10,28,34,63,38,3,26,1688861, +16,1688853,14,117,110,116,101,114,109,105,110,97,116,101,100,32, +97,115,109,32,98,108,111,99,107,0,1688807,1589197,12,8,1585223,2, +3,28,1,6,1,10,28,34,63,38,3,26,1688913,16,1585225,2, +1592283,12,8,1601277,12,8,1688739,14,1585223,2,7,28,1,6,1,10, +28,34,63,38,3,26,1688965,16,1585225,2,1594383,12,8,1601277,12,8, +1688739,14,1587563,2,1602703,12,1689083,16,1601277,12,8,1585223,2,3,28,1, +6,1,10,28,34,63,38,1,10,28,1689063,16,1689055,14,35,32, +110,101,101,100,115,32,97,32,110,117,109,98,101,114,0,1689021, +1589197,12,8,1585225,2,1592089,12,8,1601277,12,8,1688739,14,1587565,2,1602703, +12,1689205,16,1601277,12,8,1585223,2,3,28,1,6,1,10,28,34, +63,38,3,26,1689163,16,1585227,2,1,6,1,10,28,34,63,38, +1,10,28,1689165,14,1,1689193,16,1,1585225,2,28,1592283,12,8,1601277, +12,8,1689201,14,29,1592369,12,8,1688739,14,1587561,2,1602703,12,1689395,16, +1601277,12,8,1310721,2,2,129,40,3,36,1689293,16,1689285,14,116,111, +111,32,109,97,110,121,32,97,115,109,32,108,97,98,101,108, +115,0,1689245,1588719,12,8,1310721,2,13,26,2,1310721,2,2,26,1603527, +12,10,3,6,10,4,8,1310721,2,15,26,2,1310721,2,2,26, +1587483,2,10,3,6,10,4,8,1310721,2,1,6,2,1,6,3, +26,5,6,4,10,8,8,1688739,14,1585225,2,1,26,2,1689465,16, +1585225,2,3,26,2,1,6,1,10,28,34,63,38,3,26,1, +6,1,10,28,34,63,38,1,10,28,1689467,14,1,1689537,16,1310721, +2,9,26,1585225,2,1,26,2,1604819,12,10,3,6,10,4,1, +40,3,36,1,6,1,10,28,34,63,38,1,10,28,1689539,14, +1,1689569,16,1310721,2,9,26,2,1592369,12,8,1601277,12,8,1688739,14, +1585223,2,5,28,1,6,1,10,28,34,63,38,1,10,28,1689649, +16,1689641,14,98,97,100,32,116,111,107,101,110,32,105,110,32, +97,115,109,0,1689607,1589197,12,8,1310721,2,5,26,1585225,2,1607893,12, +10,3,6,10,4,8,1310721,2,5,26,2,1,40,3,36,1689745, +16,1558017,1310721,2,5,26,2,26,2,33,32,1,6,1,10,28, +34,63,38,1,10,28,1689747,14,1,1689813,16,1473025,1310721,2,5,26, +2,26,2,1,6,1,10,28,34,63,38,3,26,1,6,1, +10,28,34,63,38,1,10,28,1689815,14,1,1689893,16,1689885,14,97, +115,109,32,99,97,110,39,116,32,114,101,97,99,104,32,97, +110,32,111,51,32,118,97,114,105,97,98,108,101,0,1689823,1588813, +12,8,1310721,2,5,26,2,1,40,3,36,1689975,16,1472001,1310721,2, +5,26,2,26,3,10,3,6,10,4,8,1467905,1310721,2,5,26, +2,26,2,1593203,12,8,1601277,12,8,1688739,14,1310721,2,7,26,3, +1,10,28,10,3,6,10,4,8,1310721,2,5,26,1,10,3, +6,10,4,8,1310721,2,5,26,2,1310721,2,2,40,1690169,16,1310721, +2,13,26,2,1310721,2,5,26,2,26,2,1585225,2,28,1,6, +1,10,28,34,63,38,3,26,1690131,16,1310721,2,7,26,1310721,2, +5,26,2,10,3,6,10,4,8,1310721,2,5,26,1,6,2, +1,6,3,26,5,6,4,10,8,8,1690025,14,1310721,2,7,26, +2,1,40,3,36,1690231,16,1310721,2,15,26,2,1310721,2,7,26, +2,26,2,1592283,12,8,1601277,12,8,1688739,14,1310721,2,11,26,1585225, +2,1590943,12,10,3,6,10,4,8,1310721,2,11,26,2,1,40, +3,36,1690347,16,1417729,1310721,2,11,26,2,26,2,13,28,1,6, +1,10,28,34,63,38,3,26,1,6,1,10,28,34,63,38, +1,10,28,1690349,14,1,1690389,16,45,1425921,1310721,2,11,26,2,26, +2,26,1592369,12,8,1601277,12,8,1688739,14,1310721,2,11,26,2,1, +40,3,36,1690479,16,1417729,1310721,2,11,26,2,26,2,3,28,1, +6,1,10,28,34,63,38,1,10,28,1,6,1,10,28,34, +63,38,1,10,28,1690481,14,1,1690553,16,1417729,1310721,2,11,26,2, +26,2,11,28,1,6,1,10,28,34,63,38,1,10,28,1, +6,1,10,28,34,63,38,1,10,28,1690555,14,1,1690591,16,1425921, +1310721,2,11,26,2,26,2,1592283,12,8,1601277,12,8,1688739,14,1310721, +2,11,26,2,1,40,3,36,1690679,16,1417729,1310721,2,11,26,2, +26,2,3,28,1,6,1,10,28,34,63,38,3,26,1,6, +1,10,28,34,63,38,1,10,28,1690681,14,1,1690711,16,1310721,2, +11,26,2,1592875,12,8,1601277,12,8,1688739,14,1310721,2,3,26,2, +501,40,3,36,1690805,16,1690797,14,116,111,111,32,109,97,110,121, +32,97,115,109,32,108,97,98,101,108,32,114,101,102,101,114, +101,110,99,101,115,0,1690737,1588719,12,8,1310721,2,17,26,2,1310721, +2,3,26,2,26,1585225,2,10,3,6,10,4,8,1310721,2,19, +26,2,1310721,2,3,26,2,26,1592669,12,10,3,6,10,4,8, +1310721,2,3,26,1,6,2,1,6,3,26,5,6,4,10,8, +8,1601277,12,8,1688739,14,1601277,12,8,1310721,2,5,26,1,10,3, +6,10,4,8,1310721,2,5,26,2,1310721,2,3,26,2,40,1691379, +16,1310721,2,7,26,3,1,10,28,10,3,6,10,4,8,1310721, +2,9,26,1,10,3,6,10,4,8,1310721,2,9,26,2,1310721, +2,2,40,1691193,16,1310721,2,13,26,2,1310721,2,9,26,2,26, +2,1310721,2,17,26,2,1310721,2,5,26,2,26,2,28,1,6, +1,10,28,34,63,38,3,26,1691155,16,1310721,2,7,26,1310721,2, +9,26,2,10,3,6,10,4,8,1310721,2,9,26,1,6,2, +1,6,3,26,5,6,4,10,8,8,1691029,14,1310721,2,7,26, +2,1,40,1691287,16,1310721,2,17,26,2,1310721,2,5,26,2,26, +2,1691279,14,117,110,107,110,111,119,110,32,110,97,109,101,32, +105,110,32,97,115,109,0,1691239,1588983,12,8,1310721,2,19,26,2, +1310721,2,5,26,2,26,2,1310721,2,15,26,2,1310721,2,7,26, +2,26,2,1592451,12,8,1310721,2,5,26,1,6,2,1,6,3, +26,5,6,4,10,8,8,1690953,14,1,1691387,14,1,1310721,2,21, +26,1310721,4,18,1310721,2,1,28,1310721,4,1310721,2,1835009,40,1691435,16, +1966081,1310721,4,1579223,12,1601277,12,8,1688615,12,8,1587529,1,10,3,6, +10,4,8,1,1691471,14,1,1310721,2,1,26,1310721,4,18,1310721,2, +7,28,1310721,4,1310721,2,1835009,40,1691519,16,1966081,1310721,4,1579223,12,1, +1310721,2,4,1,1310721,2,3,26,4,1587537,2,1602703,12,1,6,1, +10,28,34,63,38,3,26,1691605,16,1691597,14,101,120,112,101,99, +116,101,100,32,123,0,1691575,1589197,12,8,1601277,12,8,3,1692027,16, +1585223,2,1,28,1,6,1,10,28,34,63,38,3,26,1691699,16, +1691691,14,117,110,116,101,114,109,105,110,97,116,101,100,32,113, +117,111,116,101,0,1691653,1589197,12,8,1587537,2,1602703,12,1691745,16,1310721, +2,3,26,1,6,2,1,6,3,26,5,6,4,10,8,8, +1587539,2,1602703,12,1691829,16,1310721,2,3,26,2,1,6,1,10,28, +34,63,38,3,26,1691795,16,1692027,14,1310721,2,3,26,1,6,2, +1,6,-1,26,5,6,4,10,8,8,1310721,2,2,5401,40,3, +36,1691889,16,1691881,14,113,117,111,116,101,32,116,111,111,32,108, +111,110,103,0,1691851,1588719,12,8,1524225,1310721,2,2,7,30,26,1585223, +2,10,3,6,10,4,8,1524225,1310721,2,2,7,30,3,26,26, +1585225,2,10,3,6,10,4,8,1524225,1310721,2,2,7,30,5,26, +26,1585229,2,10,3,6,10,4,8,1310721,2,1,6,2,1,6, +3,26,5,6,4,10,8,8,1601277,12,8,1691611,14,1601277,12,8, +1310721,2,5,26,1310721,2,2,7,30,3,26,1580207,12,10,3,6, +10,4,8,1310721,2,5,26,2,1,26,1310721,2,2,10,3,6, +10,4,8,1310721,2,5,26,2,3,26,1524225,1310721,2,2,7,30, +1574491,12,8,1310721,2,5,26,2,1692151,14,1,1310721,2,7,26,1310721, +4,18,1310721,2,7,28,1310721,4,1310721,2,1835009,40,1692199,16,1966081,1310721, +4,1579223,12,1310721,2,4,1310721,2,2,1,26,2,7,30,3,26, +1310721,2,3,26,4,1310721,2,3,26,2,1580207,12,1310721,2,5,26, +4,1310721,2,5,26,2,1310721,2,2,1310721,2,3,26,2,1574491,12, +8,1310721,2,5,26,2,1692307,14,1,1310721,2,7,26,1310721,4,18, +1310721,2,7,28,1310721,4,1310721,2,1835009,40,1692355,16,1966081,1310721,4,1579223, +12,1310721,2,4,1310721,2,2,1,26,2,7,30,3,26,1310721,2, +3,26,4,1310721,2,3,26,2,1580207,12,1310721,2,5,26,4,1310721, +2,5,26,2,1310721,2,2,1310721,2,3,26,2,1574491,12,8,1310721, +2,5,26,2,1692463,14,1,1310721,2,7,26,1310721,4,18,1310721,2, +7,28,1310721,4,1310721,2,1835009,40,1692511,16,1966081,1310721,4,1579223,12,1310721, +2,3,26,4,1310721,2,4,1,1310721,2,5,26,4,1310721,2,5, +26,2,1310721,2,3,26,2,40,1692629,16,1310721,2,2,1310721,2,5, +26,2,26,2,1592089,12,8,1310721,2,5,26,1,6,2,1,6, +3,26,5,6,4,10,8,8,1692539,14,1,1692637,14,1,1310721,2, +7,26,1310721,4,18,1310721,2,9,28,1310721,4,1310721,2,1835009,40,1692685, +16,1966081,1310721,4,1579223,12,1601277,12,8,1310721,2,1691485,12,10,3,6, +10,4,8,1310721,2,3,26,1592669,12,10,3,6,10,4,8,15, +1592369,12,8,1310721,2,5,26,1587483,2,10,3,6,10,4,8,1310721, +2,7,26,1310721,2,2,1,26,2,7,30,3,26,10,3,6, +10,4,8,1310721,2,2,1310721,2,7,26,2,1692477,12,8,1310721,2, +2,1580907,12,8,1310721,2,3,26,2,1587483,2,1592451,12,8,1310721,2, +5,26,2,1592283,12,8,1587609,2,1593049,12,8,1587529,3,10,3,6, +10,4,8,1,1692911,14,1,1310721,2,9,26,1310721,4,18,1310721,2, +7,28,1310721,4,1310721,2,1835009,40,1692959,16,1966081,1310721,4,1579223,12,1310721, +2,4,1,1310721,2,3,26,4,5,1310721,2,2,1,1,1595149,12, +8,1601277,12,8,1585223,2,1,28,1,6,1,10,28,34,63,38, +1,10,28,1693255,16,1310721,2,3,26,2,5401,40,3,36,1693101,16, +1693093,14,116,111,111,32,109,97,110,121,32,116,111,107,101,110, +115,0,1693061,1588719,12,8,1524225,1310721,2,3,26,2,7,30,26,1585223, +2,10,3,6,10,4,8,1524225,1310721,2,3,26,2,7,30,3, +26,26,1585225,2,10,3,6,10,4,8,1524225,1310721,2,3,26,2, +7,30,5,26,26,1585229,2,10,3,6,10,4,8,1310721,2,3, +26,1,6,2,1,6,3,26,5,6,4,10,8,8,1601277,12, +8,1693001,14,1596577,12,8,1310721,2,5,26,1310721,2,3,26,2,7, +30,3,26,1580207,12,10,3,6,10,4,8,1310721,2,5,26,2, +1,26,1310721,2,3,26,2,10,3,6,10,4,8,1310721,2,5, +26,2,3,26,1524225,1310721,2,3,26,2,7,30,1574491,12,8,1310721, +2,5,26,2,1693391,14,1,1310721,2,7,26,1310721,4,18,1310721,2, +3,28,1310721,4,1310721,2,1835009,40,1693439,16,1966081,1310721,4,1579223,12,1310721, +2,4,3,1310721,2,2,3,3,1595149,12,8,1601277,12,8,1,1693477, +14,1,1310721,2,3,26,1310721,4,18,1310721,2,13,28,1310721,4,1310721, +2,1835009,40,1693525,16,1966081,1310721,4,1579223,12,8181,1310721,2,4,8181,1580207, +12,1310721,2,3,26,4,1,1310721,2,5,26,4,1573381,12,1,6, +1,10,28,34,63,38,3,26,1694083,16,1310721,2,7,26,1573325,12, +511,32,10,3,6,10,4,8,1310721,2,7,26,2,193,28,1, +6,1,10,28,34,63,38,3,26,1693659,16,1694083,14,1310721,2,5, +26,2,5,38,5,26,1310721,2,2,40,3,36,1693847,16,1310721,2, +9,26,1310721,2,2,1310721,2,2,26,13,26,1580207,12,10,3,6, +10,4,8,1310721,2,9,26,2,1310721,2,3,26,2,1310721,2,2, +1574491,12,8,1310721,2,3,26,2,1580907,12,8,1310721,2,3,26,1310721, +2,9,26,2,10,3,6,10,4,8,1310721,2,1310721,2,2,1310721, +2,2,26,13,26,10,3,6,10,4,8,1310721,2,11,26,1310721, +2,5,26,2,5,38,10,3,6,10,4,8,1310721,2,5,26, +2,7,32,1,6,1,10,28,34,63,38,3,26,1693955,16,1310721, +2,3,26,2,1310721,2,11,26,2,26,1,10,3,6,10,4, +8,1310721,2,3,26,2,1310721,2,11,26,2,26,1310721,2,3,26, +2,1310721,2,11,26,2,26,2,1310721,2,7,26,2,1310721,2,5, +26,2,7,32,17,30,48,34,10,3,6,10,4,8,1310721,2, +5,26,1,6,2,1,6,3,26,5,6,4,10,8,8,1693561, +14,1310721,2,3,26,2,1310721,2,5,26,2,5,38,26,1310721,2, +3,26,2,1310721,2,5,26,2,5,38,26,2,1574901,1310721,2,5, +26,2,7,32,17,30,26,2,3,28,32,10,3,6,10,4, +8,1310721,2,5,26,2,7,32,1,28,1,6,1,10,28,34, +63,38,3,26,1694261,16,1310721,2,3,26,2,1310721,2,5,26,2, +5,38,26,1,10,3,6,10,4,8,1310721,2,3,26,2,1310721, +2,5,26,2,5,38,3,26,26,1,10,3,6,10,4,8, +1310721,2,3,26,2,1694321,14,1,1310721,2,13,26,1310721,4,18,1310721, +2,3,28,1310721,4,1310721,2,1835009,40,1694369,16,1966081,1310721,4,1579223,12, +1,1310721,2,4,1585223,2,1,28,1,6,1,10,28,34,63,38, +1,10,28,1694577,16,1587661,2,1602703,12,1694457,16,1310721,2,1,6,2, +1,6,3,26,5,6,4,10,8,8,1694541,14,1587663,2,1602703,12, +1694503,16,1310721,2,1,6,2,1,6,-1,26,5,6,4,10,8, +8,1694541,14,1587665,2,1602703,12,1694541,16,1310721,2,1310721,2,2,5,28, +10,3,6,10,4,8,1601277,12,8,1310721,2,2,1,10,40,3, +36,1694573,16,1,1694585,14,1694377,14,1,1694585,14,1,1310721,2,3,26, +1310721,4,18,1310721,2,15,28,1310721,4,1310721,2,1835009,40,1694633,16,1966081, +1310721,4,1579223,12,1310721,2,4,1585225,2,1310721,2,3,26,4,1,1310721, +2,5,26,4,1,1310721,2,7,26,4,1,1310721,2,9,26,4, +1601277,12,8,1601277,12,8,1587535,2,1602703,12,1,6,1,10,28,34, +63,38,3,26,1695445,16,1310721,2,5,26,3,10,3,6,10,4, +8,1310721,2,11,26,3,10,3,6,10,4,8,1585223,2,1,28, +1,6,1,10,28,34,63,38,1,10,28,1695351,16,1587533,2,1602703, +12,1694861,16,1310721,2,11,26,1,6,2,1,6,3,26,5,6, +4,10,8,8,1695341,14,1587535,2,1602703,12,1694949,16,1310721,2,11,26, +1,6,2,1,6,-1,26,5,6,4,10,8,8,1310721,2,11, +26,2,1,6,1,10,28,34,63,38,3,26,1694945,16,1695351,14, +1695341,14,1310721,2,11,26,2,3,28,1,6,1,10,28,34,63, +38,3,26,1695021,16,1587545,2,1602703,12,1,6,1,10,28,34,63, +38,1,10,28,1695023,14,1,1695181,16,1310721,2,5,26,2,25,10, +40,3,36,1695121,16,1310721,2,7,26,1310721,2,7,26,2,1310721,2, +9,26,2,1310721,2,5,26,2,1310721,2,5,26,2,26,5,28, +48,26,10,3,6,10,4,8,1310721,2,5,26,1,6,2,1, +6,3,26,5,6,4,10,8,8,1310721,2,9,26,1,10,3, +6,10,4,8,1695341,14,1310721,2,11,26,2,3,28,1,6,1, +10,28,34,63,38,3,26,1695253,16,1587571,2,1602703,12,1,6,1, +10,28,34,63,38,1,10,28,1695255,14,1,1695341,16,1601277,12,8, +1587587,2,1602855,12,1695303,16,1310721,2,9,26,3,10,3,6,10,4, +8,1695337,14,1587589,2,1602855,12,1695337,16,1310721,2,9,26,5,10,3, +6,10,4,8,1694777,14,1601277,12,8,1694777,14,1310721,2,5,26,2, +25,10,40,3,36,1695445,16,1310721,2,7,26,1310721,2,7,26,2, +1310721,2,9,26,2,1310721,2,5,26,2,1310721,2,5,26,2,26, +5,28,48,26,10,3,6,10,4,8,1601277,12,8,1587537,2,1602703, +12,1,6,1,10,28,34,63,38,3,26,1695537,16,1587547,2,1602703, +12,1,6,1,10,28,34,63,38,3,26,1,6,1,10,28, +34,63,38,1,10,28,1695539,14,1,1695549,16,1,1695983,14,1310721,2, +13,26,1310721,2,3,26,2,1590943,12,10,3,6,10,4,8,1310721, +2,13,26,2,1,40,1695607,16,3,1695645,14,1310721,2,13,26,2, +1585155,2,40,1,6,1,10,28,34,63,38,1,10,28,1695655,16, +3,1695719,14,1417729,1310721,2,13,26,2,26,2,3,28,1,6,1, +10,28,34,63,38,1,10,28,1,6,1,10,28,34,63,38, +1,10,28,1695729,16,3,1695771,14,1442305,1310721,2,13,26,2,26,2, +3,32,1,6,1,10,28,34,63,38,1,10,28,1695809,16,1310721, +2,13,26,1310721,2,3,26,2,1592751,12,10,3,6,10,4,8, +1442305,1310721,2,13,26,2,26,2,5,32,1,6,1,10,28,34, +63,38,3,26,1695975,16,1434113,1310721,2,13,26,2,26,1310721,2,2, +1310721,2,5,26,2,26,1310721,2,5,26,2,26,1310721,2,7,26, +2,129,30,26,10,3,6,10,4,8,1442305,1310721,2,13,26,2, +26,1442305,1310721,2,13,26,2,26,2,5,34,513,34,10,3,6, +10,4,8,1,1695983,14,1,1310721,2,15,26,1310721,4,18,1310721,2, +13,28,1310721,4,1310721,2,1835009,40,1696031,16,1966081,1310721,4,1579223,12,1310721, +2,3,26,4,1310721,2,4,1,1310721,2,5,26,4,1588225,2,1310721, +2,7,26,4,1588223,2,1310721,2,9,26,4,1,1310721,2,11,26, +4,1587637,1,6,2,1,6,3,26,5,6,4,10,8,8,1310721, +2,2,1310721,2,3,26,2,1,1,1595149,12,8,1601277,12,8,1585223, +2,1,28,1,6,1,10,28,34,63,38,1,10,28,1696699,16, +1587537,2,1602703,12,1696243,16,1310721,2,5,26,1,6,2,1,6,3, +26,5,6,4,10,8,8,1696689,14,1587539,2,1602703,12,1696315,16,1310721, +2,5,26,1,6,2,1,6,-1,26,5,6,4,10,8,8, +1310721,2,11,26,1,10,3,6,10,4,8,1696689,14,1310721,2,5, +26,2,1,10,40,3,36,1696689,16,1587547,2,1602703,12,1696377,16,1310721, +2,11,26,1,10,3,6,10,4,8,1696689,14,1587571,2,1602703,12, +1696515,16,1602137,12,8,1587445,2,5,28,1,6,1,10,28,34,63, +38,3,26,1696483,16,1587447,2,1587591,2,28,1,6,1,10,28,34, +63,38,3,26,1,6,1,10,28,34,63,38,1,10,28,1696485, +14,1,1696511,16,1310721,2,11,26,3,10,3,6,10,4,8,1696689, +14,1585223,2,5,28,1,6,1,10,28,34,63,38,3,26,1696689, +16,1602137,12,8,1587445,2,9,28,1,6,1,10,28,34,63,38, +3,26,1696641,16,1587447,2,1587533,2,28,1,6,1,10,28,34,63, +38,3,26,1,6,1,10,28,34,63,38,1,10,28,1696643,14, +1,1696689,16,1310721,2,11,26,2,1694599,12,8,1310721,2,11,26,1, +10,3,6,10,4,8,1696159,14,1601277,12,8,1696159,14,1596577,12,8, +1587637,1,6,2,1,6,-1,26,5,6,4,10,8,8,1588225,1310721, +2,7,26,2,10,3,6,10,4,8,1588223,1310721,2,9,26,2, +10,3,6,10,4,8,1,1696789,14,1,1310721,2,13,26,1310721,4, +18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1696837,16,1966081,1310721,4, +1579223,12,1693491,12,1310721,2,4,7,1310721,2,2,1695997,12,8,7,1310721, +2,2,1,1,1595149,12,8,1601277,12,8,1711115,12,8,1585155,2,1711215, +12,8,1596577,12,8,1310721,2,2,1580907,12,8,1,1696927,14,1,1310721, +2,3,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40, +1696975,16,1966081,1310721,4,1579223,12,1548801,1587517,2,49,30,26,1310721,2,4, +1587517,2,61,40,3,36,1697075,16,1697067,14,99,111,109,112,105,108, +101,115,32,110,101,115,116,101,100,32,116,111,111,32,100,101, +101,112,108,121,0,1697013,1588719,12,8,1587517,1,6,2,1,6,3, +26,5,6,4,10,8,8,1310721,2,2,1,26,1587491,2,10,3, +6,10,4,8,1310721,2,2,3,26,1587493,2,10,3,6,10,4, +8,1310721,2,2,5,26,1587495,2,10,3,6,10,4,8,1310721,2, +2,7,26,1587497,2,10,3,6,10,4,8,1310721,2,2,9,26, +1587499,2,10,3,6,10,4,8,1310721,2,2,11,26,1587501,2,10, +3,6,10,4,8,1310721,2,2,13,26,1587503,2,10,3,6,10, +4,8,1310721,2,2,15,26,1587505,2,10,3,6,10,4,8,1310721, +2,2,17,26,1587507,2,10,3,6,10,4,8,1310721,2,2,19, +26,1587509,2,10,3,6,10,4,8,1310721,2,2,21,26,1587511,2, +10,3,6,10,4,8,1310721,2,2,23,26,1587513,2,10,3,6, +10,4,8,1310721,2,2,25,26,1587515,2,10,3,6,10,4,8, +1310721,2,2,27,26,1587521,2,10,3,6,10,4,8,1310721,2,2, +29,26,1587523,2,10,3,6,10,4,8,1310721,2,2,31,26,1587525, +2,10,3,6,10,4,8,1310721,2,2,33,26,1587527,2,10,3, +6,10,4,8,1310721,2,2,35,26,1587529,2,10,3,6,10,4, +8,1310721,2,2,37,26,1587519,2,10,3,6,10,4,8,1310721,2, +2,39,26,1587531,2,10,3,6,10,4,8,1310721,2,2,41,26, +1587655,2,10,3,6,10,4,8,1310721,2,2,43,26,1587633,2,10, +3,6,10,4,8,1310721,2,2,45,26,1587639,5,1574491,12,8,1, +1697703,14,1,1310721,2,3,26,1310721,4,18,1310721,2,3,28,1310721,4, +1310721,2,1835009,40,1697751,16,1966081,1310721,4,1579223,12,1587517,1,6,2,1, +6,-1,26,5,6,4,10,8,8,1310721,2,1548801,1587517,2,49,30, +26,10,3,6,10,4,8,1587491,1310721,2,2,1,26,2,10,3, +6,10,4,8,1587493,1310721,2,2,3,26,2,10,3,6,10,4, +8,1587495,1310721,2,2,5,26,2,10,3,6,10,4,8,1587497,1310721, +2,2,7,26,2,10,3,6,10,4,8,1587499,1310721,2,2,9, +26,2,10,3,6,10,4,8,1587501,1310721,2,2,11,26,2,10, +3,6,10,4,8,1587503,1310721,2,2,13,26,2,10,3,6,10, +4,8,1587505,1310721,2,2,15,26,2,10,3,6,10,4,8,1587507, +1310721,2,2,17,26,2,10,3,6,10,4,8,1587509,1310721,2,2, +19,26,2,10,3,6,10,4,8,1587511,1310721,2,2,21,26,2, +10,3,6,10,4,8,1587513,1310721,2,2,23,26,2,10,3,6, +10,4,8,1587515,1310721,2,2,25,26,2,10,3,6,10,4,8, +1587521,1310721,2,2,27,26,2,10,3,6,10,4,8,1587523,1310721,2, +2,29,26,2,10,3,6,10,4,8,1587525,1310721,2,2,31,26, +2,10,3,6,10,4,8,1587527,1310721,2,2,33,26,2,10,3, +6,10,4,8,1587529,1310721,2,2,35,26,2,10,3,6,10,4, +8,1587519,1310721,2,2,37,26,2,10,3,6,10,4,8,1587531,1310721, +2,2,39,26,2,10,3,6,10,4,8,1587655,1310721,2,2,41, +26,2,10,3,6,10,4,8,1587633,1310721,2,2,43,26,2,10, +3,6,10,4,8,1587639,1310721,2,2,45,26,5,1574491,12,8,1, +1698407,14,1,1310721,2,3,26,1310721,4,18,1310721,2,5,28,1310721,4, +1310721,2,1835009,40,1698455,16,1966081,1310721,4,1579223,12,1310721,2,3,26,4, +1310721,2,4,1587493,1587491,2,10,3,6,10,4,8,1587495,3,10,3, +6,10,4,8,1587497,1,10,3,6,10,4,8,1587499,1310721,2,2, +1698563,16,1585221,2,1,6,1,10,28,34,63,38,1,10,28,1698565, +14,1,10,3,6,10,4,8,1587501,1310721,2,3,26,2,10,3, +6,10,4,8,1587531,1,10,3,6,10,4,8,1587503,3,10,3, +6,10,4,8,1587505,1,10,3,6,10,4,8,1587507,1,10,3, +6,10,4,8,1587511,3,10,3,6,10,4,8,1587515,1587513,2,10, +3,6,10,4,8,1587521,1,10,3,6,10,4,8,1587523,1,10, +3,6,10,4,8,1,1698739,14,1,1310721,2,5,26,1310721,4,18, +1310721,2,3,28,1310721,4,1310721,2,1835009,40,1698787,16,1966081,1310721,4,1579223, +12,1310721,1592283,12,8,3,1592369,12,8,1587509,1592669,12,10,3,6,10, +4,8,29,1592369,12,8,1310721,1592283,12,8,5,1592369,12,8,1587701,2, +1698859,16,1,1698977,14,1310721,1592283,12,8,3,1592369,12,8,1835009,1592283,12, +8,41,1592369,12,8,1310721,2,1592669,12,10,3,6,10,4,8,17, +1592369,12,8,1966081,1592283,12,8,1310721,1592283,12,8,5,1592369,12,8,1587599, +2,1593049,12,8,1310721,2,2,1587483,2,1592451,12,8,1,1698977,14,1, +1310721,2,3,26,1310721,4,18,1310721,2,1,28,1310721,4,1310721,2,1835009, +40,1699025,16,1966081,1310721,4,1579223,12,1,1592283,12,8,1587507,2,1587483,2, +1591871,12,8,1310721,1592283,12,8,3,1592369,12,8,1587497,2,1592283,12,8, +27,1592369,12,8,1310721,1592283,12,8,5,1592369,12,8,19,1592369,12,8, +1587509,2,1587497,2,1592451,12,8,1587511,1,10,3,6,10,4,8,1587491, +1587493,2,10,3,6,10,4,8,1,1699161,14,1,1310721,2,1,26, +1310721,4,18,1310721,2,1,28,1310721,4,1310721,2,1835009,40,1699209,16,1966081, +1310721,4,1579223,12,1587537,2,1603237,12,8,1587539,2,1602703,12,1,6,1, +10,28,34,63,38,3,26,1699375,16,1585223,2,1,28,1,6,1, +10,28,34,63,38,3,26,1699365,16,1699357,14,109,105,115,115,105, +110,103,32,125,32,97,116,32,116,104,101,32,101,110,100,32, +111,102,32,97,32,102,117,110,99,116,105,111,110,0,1699287,1589197, +12,8,1677555,12,8,1699219,14,1587493,2,1610397,12,8,1587499,2,1699423,16, +1587503,2,1,6,1,10,28,34,63,38,1,10,28,1699425,14,1, +1699557,16,3,3,1609669,12,8,1587501,2,1699557,16,1699549,14,97,32,102, +117,110,99,116,105,111,110,32,114,101,116,117,114,110,105,110, +103,32,46,117,32,104,97,115,32,116,111,32,101,110,100,32, +119,105,116,104,32,97,32,114,101,116,117,114,110,0,1699451,1589485, +12,8,1601277,12,8,1,1699571,14,1,1310721,2,1,26,1310721,4,18, +1310721,2,175,28,1310721,4,1310721,2,1835009,40,1699619,16,1966081,1310721,4,1579223, +12,1310721,2,7,26,4,1310721,2,5,26,4,1310721,2,3,26,4, +1310721,2,4,1,1310721,2,11,26,4,1,1310721,2,13,26,4,1, +1310721,2,165,26,4,1587703,2,1310721,2,169,26,4,1587703,1,10,3, +6,10,4,8,1310721,2,3,26,2,1699741,16,3,1699767,14,1587655,2, +1,6,1,10,28,34,63,38,1,10,28,1699793,16,1310721,2,165, +26,3,10,3,6,10,4,8,1310721,2,9,26,1310721,2,7,26, +2,1590943,12,10,3,6,10,4,8,1310721,2,9,26,2,1,40, +1699851,16,3,1699889,14,1310721,2,9,26,2,1585155,2,40,1,6,1, +10,28,34,63,38,1,10,28,1699899,16,3,1699963,14,1417729,1310721,2, +9,26,2,26,2,3,28,1,6,1,10,28,34,63,38,1, +10,28,1,6,1,10,28,34,63,38,1,10,28,1699973,16,3, +1700015,14,1442305,1310721,2,9,26,2,26,2,3,32,1,6,1,10, +28,34,63,38,1,10,28,1700085,16,1310721,2,9,26,1310721,2,7, +26,2,3,1,1590445,12,10,3,6,10,4,8,1442305,1310721,2,9, +26,2,26,1,10,3,6,10,4,8,1587533,2,1603237,12,8,1587535, +2,1602703,12,1,6,1,10,28,34,63,38,3,26,1701587,16,3, +1701587,16,1310721,2,15,26,1,10,3,6,10,4,8,1310721,2,117, +26,1310721,2,11,26,2,26,1587621,2,10,3,6,10,4,8,1587571, +2,1602703,12,1,6,1,10,28,34,63,38,3,26,1700563,16,1585223, +2,9,28,1,6,1,10,28,34,63,38,3,26,1700373,16,1585225, +2,1587557,2,28,1,6,1,10,28,34,63,38,3,26,1700295,16, +3,1700347,14,1585225,2,1587541,2,28,1,6,1,10,28,34,63,38, +3,26,1,6,1,10,28,34,63,38,1,10,28,1,6,1, +10,28,34,63,38,1,10,28,1700375,14,1,1700385,16,3,1700537,14, +1585223,2,5,28,1,6,1,10,28,34,63,38,3,26,1700513,16, +1585225,2,1587573,2,28,1,6,1,10,28,34,63,38,3,26,1700457, +16,3,1700487,14,1585225,2,1640093,12,1,6,1,10,28,34,63,38, +1,10,28,1,6,1,10,28,34,63,38,1,10,28,1700515,14, +1,1,6,1,10,28,34,63,38,1,10,28,1,6,1,10, +28,34,63,38,1,10,28,1700565,14,1,1700751,16,1310721,2,117,26, +1310721,2,11,26,2,26,1640473,12,10,3,6,10,4,8,1310721,2, +165,26,3,10,3,6,10,4,8,1310721,2,117,26,1310721,2,11, +26,2,26,2,1634803,12,1700751,16,1700743,14,115,116,114,117,99,116, +115,32,97,110,100,32,97,114,114,97,121,115,32,97,114,101, +32,112,97,115,115,101,100,32,98,121,32,112,111,105,110,116, +101,114,0,1700661,1588813,12,8,1587571,2,1603007,12,1700957,16,1585225,2,1587587, +2,28,1,6,1,10,28,34,63,38,3,26,1700823,16,1310721,2, +15,26,3,10,3,6,10,4,8,1700951,14,1585225,2,1587589,2,28, +1,6,1,10,28,34,63,38,3,26,1700883,16,1310721,2,15,26, +5,10,3,6,10,4,8,1700951,14,1700943,14,97,32,112,97,114, +97,109,101,116,101,114,32,116,97,103,32,105,115,32,46,109, +32,111,114,32,46,100,0,1700887,1589197,12,8,1601277,12,8,1310721,2, +21,26,1310721,2,11,26,2,26,1603527,12,10,3,6,10,4,8, +1310721,2,169,26,2,1701081,16,1310721,2,117,26,1310721,2,11,26,2, +26,2,1587621,2,28,1,6,1,10,28,34,63,38,3,26,1, +6,1,10,28,34,63,38,1,10,28,1701083,14,1,1701151,16,1701143, +14,111,51,32,112,97,114,97,109,101,116,101,114,115,32,110, +101,101,100,32,97,32,116,121,112,101,0,1701091,1588813,12,8,1310721, +2,11,26,2,25,40,3,36,1701209,16,1310721,2,15,26,2,1, +6,1,10,28,34,63,38,1,10,28,1701211,14,1,1701313,16,1701305, +14,111,110,108,121,32,116,104,101,32,102,105,114,115,116,32, +49,50,32,112,97,114,97,109,101,116,101,114,115,32,99,97, +110,32,98,101,32,116,97,103,103,101,100,0,1701219,1589197,12,8, +1310721,2,69,26,1310721,2,11,26,2,26,1310721,2,15,26,2,10, +3,6,10,4,8,1310721,2,11,26,2,25,40,1701439,16,1310721,2, +13,26,1310721,2,13,26,2,1310721,2,15,26,2,1310721,2,11,26, +2,1310721,2,11,26,2,26,48,26,10,3,6,10,4,8,1310721, +2,11,26,1,6,2,1,6,3,26,5,6,4,10,8,8, +1310721,2,11,26,2,49,40,3,36,1701547,16,1701539,14,116,111,111, +32,109,97,110,121,32,112,97,114,97,109,101,116,101,114,115, +0,1701499,1589197,12,8,1587545,2,1603007,12,1,6,1,10,28,34,63, +38,3,26,1701583,16,1701587,14,1700127,14,1587535,2,1603237,12,8,1310721,2, +17,26,1310721,2,5,26,2,1310721,2,11,26,2,26,1310721,2,11, +26,2,26,1310721,2,13,26,2,129,30,26,10,3,6,10,4, +8,1442305,1310721,2,9,26,2,26,2,5,32,1701757,16,1442305,1310721,2, +9,26,2,26,2,513,32,1,6,1,10,28,34,63,38,3, +26,1,6,1,10,28,34,63,38,1,10,28,1701759,14,1,1701919, +16,1434113,1310721,2,9,26,2,26,2,1310721,2,17,26,2,28,1, +6,1,10,28,34,63,38,1,10,28,1701915,16,1310721,2,7,26, +2,1701907,14,100,111,101,115,110,39,116,32,109,97,116,99,104, +32,105,116,115,32,101,97,114,108,105,101,114,32,100,101,99, +108,97,114,97,116,105,111,110,0,1701831,1588983,12,8,1702207,14,1442305, +1310721,2,9,26,2,26,2,9,32,1702021,16,1310721,2,13,26,2, +1701963,16,3,1701995,14,1310721,2,5,26,2,1,6,1,10,28,34, +63,38,1,10,28,1,6,1,10,28,34,63,38,1,10,28, +1702023,14,1,1702207,16,1310721,2,7,26,2,1702199,14,119,97,115,32, +99,97,108,108,101,100,32,98,101,102,111,114,101,32,105,116, +115,32,116,97,103,115,32,119,101,114,101,32,100,101,99,108, +97,114,101,100,59,32,112,117,116,32,97,32,112,114,111,116, +111,116,121,112,101,32,97,98,111,118,101,32,116,104,101,32, +102,105,114,115,116,32,99,97,108,108,0,1702041,1588983,12,8,1434113, +1310721,2,9,26,2,26,1310721,2,17,26,2,10,3,6,10,4, +8,1442305,1310721,2,9,26,2,26,1442305,1310721,2,9,26,2,26,2, +5,34,1442305,1310721,2,9,26,2,26,2,513,32,28,10,3,6, +10,4,8,1587655,2,1702365,16,1442305,1310721,2,9,26,2,26,1442305,1310721, +2,9,26,2,26,2,257,34,10,3,6,10,4,8,1310721,2, +165,26,2,1702735,16,1310721,2,3,26,2,1,6,1,10,28,34, +63,38,3,26,1702437,16,1310721,2,3,26,1587621,2,10,3,6,10, +4,8,1310721,2,167,26,1310721,2,3,26,2,1310721,2,11,26,2, +1310721,2,117,26,1310721,2,17,26,2,1629653,12,10,3,6,10,4, +8,1559041,1310721,2,9,26,2,26,2,1702595,16,1559041,1310721,2,9,26, +2,26,2,1310721,2,167,26,2,28,1,6,1,10,28,34,63, +38,1,10,28,1,6,1,10,28,34,63,38,1,10,28,1702597, +14,1,1702699,16,1310721,2,7,26,2,1702691,14,100,111,101,115,110, +39,116,32,109,97,116,99,104,32,105,116,115,32,101,97,114, +108,105,101,114,32,100,101,99,108,97,114,97,116,105,111,110, +0,1702615,1588983,12,8,1559041,1310721,2,9,26,2,26,1310721,2,167,26, +2,10,3,6,10,4,8,1587547,2,1603007,12,1702777,16,1587655,1,10, +3,6,10,4,8,1310721,2,9,26,2,1703951,14,1442305,1310721,2,9, +26,2,26,2,3,32,1702857,16,1310721,2,7,26,2,1702849,14,105, +115,32,100,101,102,105,110,101,100,32,116,119,105,99,101,0, +1702815,1588983,12,8,1425921,1310721,2,9,26,2,26,1587483,2,10,3,6, +10,4,8,1442305,1310721,2,9,26,2,26,1442305,1310721,2,9,26,2, +26,2,3,34,10,3,6,10,4,8,1450497,1310721,2,9,26,2, +26,2,1587483,2,1591871,12,8,1450497,1310721,2,9,26,2,26,1,10, +3,6,10,4,8,1310721,2,2,1310721,2,5,26,2,1698421,12,8, +1310721,2,165,26,2,1703047,16,1587531,1310721,2,3,26,2,10,3,6, +10,4,8,1588125,1,10,3,6,10,4,8,1310721,2,169,26,2, +1703109,16,1587701,3,10,3,6,10,4,8,1587723,1,10,3,6,10, +4,8,1698753,12,8,1310721,2,19,26,1,10,3,6,10,4,8, +1310721,2,19,26,2,1310721,2,11,26,2,40,1703643,16,1310721,2,15, +26,1310721,2,21,26,1310721,2,19,26,2,26,2,1608097,12,10,3, +6,10,4,8,1587497,1,6,2,1,6,3,26,5,6,4,10, +8,8,1556993,1310721,2,15,26,2,26,1310721,2,117,26,1310721,2,19, +26,2,26,2,10,3,6,10,4,8,1310721,2,169,26,2,1703349, +16,1558017,1310721,2,15,26,2,26,33,10,3,6,10,4,8,1467905, +1310721,2,15,26,2,26,2,1593579,12,8,1587499,2,1703429,16,1310721,2, +69,26,1310721,2,19,26,2,26,2,3,28,1,6,1,10,28, +34,63,38,3,26,1,6,1,10,28,34,63,38,1,10,28, +1703431,14,1,1703463,16,1469953,1310721,2,15,26,2,26,13,10,3,6, +10,4,8,1587499,2,1703543,16,1310721,2,69,26,1310721,2,19,26,2, +26,2,5,28,1,6,1,10,28,34,63,38,3,26,1,6, +1,10,28,34,63,38,1,10,28,1703545,14,1,1703605,16,1469953,1310721, +2,15,26,2,26,3,10,3,6,10,4,8,1474049,1310721,2,15, +26,2,26,3,10,3,6,10,4,8,1310721,2,19,26,1,6, +2,1,6,3,26,5,6,4,10,8,8,1703137,14,1310721,2,19, +26,1310721,2,11,26,2,10,3,6,10,4,8,1310721,2,19,26, +2,1703749,16,1310721,2,19,26,1,6,2,1,6,-1,26,5,6, +4,10,8,8,1310721,2,19,26,2,1593203,12,8,5,1592369,12,8, +1703673,14,1310721,2,169,26,2,1703815,16,1310721,2,171,26,1587483,2,5, +26,10,3,6,10,4,8,1310721,2,171,26,2,1592283,12,8,15, +1592369,12,8,1699175,12,8,1310721,2,173,26,1587483,2,10,3,6,10, +4,8,1698991,12,8,1310721,2,169,26,2,1703913,16,1587701,1,10,3, +6,10,4,8,1310721,2,171,26,2,1310721,2,173,26,2,1587707,1587723, +2,1593831,12,8,1594151,12,8,1587655,1,10,3,6,10,4,8,1310721, +2,9,26,2,1703951,14,1,1310721,2,175,26,1310721,4,18,1310721,2, +3,28,1310721,4,1310721,2,1835009,40,1703999,16,1966081,1310721,4,1579223,12,1310721, +2,4,1601277,12,8,1,1,1603527,12,1704043,12,1704029,14,1,1310721,2, +3,26,1310721,4,18,1310721,2,13,28,1310721,4,1310721,2,1835009,40,1704077, +16,1966081,1310721,4,1579223,12,1310721,2,5,26,4,1310721,2,3,26,4, +1310721,2,4,1310721,2,3,26,2,1704221,16,1704213,14,116,97,103,115, +32,103,111,32,111,110,32,108,111,99,97,108,115,32,97,110, +100,32,112,97,114,97,109,101,116,101,114,115,44,32,110,111, +116,32,103,108,111,98,97,108,115,0,1704121,1588813,12,8,1310721,2, +2,1587629,2,28,1,6,1,10,28,34,63,38,3,26,1704319,16, +1704311,14,97,32,118,97,114,105,97,98,108,101,32,99,97,110, +39,116,32,98,101,32,118,111,105,100,0,1704261,1588813,12,8,3, +1705235,16,1587541,2,1603007,12,1704611,16,1310721,2,9,26,1676587,12,10,3, +6,10,4,8,1587543,2,1603237,12,8,1310721,2,11,26,1310721,2,5, +26,2,7,1587483,2,1590445,12,10,3,6,10,4,8,1310721,2,2, +1,6,1,10,28,34,63,38,3,26,1704461,16,1310721,2,1587621,2, +10,3,6,10,4,8,1559041,1310721,2,11,26,2,26,1310721,2,2, +1310721,2,9,26,2,1629133,12,10,3,6,10,4,8,1310721,2,9, +26,1559041,1310721,2,11,26,2,26,2,1633401,12,10,3,6,10,4, +8,1310721,2,9,26,2,1704607,16,1,1592089,12,8,1310721,2,9,26, +1,6,2,1,6,-1,26,5,6,4,10,8,8,1704547,14,1705171, +14,1310721,2,2,1704657,16,1310721,2,2,1634803,12,1,6,1,10,28, +34,63,38,1,10,28,1704659,14,1,1704829,16,1310721,2,11,26,1310721, +2,5,26,2,7,1587483,2,1590445,12,10,3,6,10,4,8,1559041, +1310721,2,11,26,2,26,1310721,2,2,10,3,6,10,4,8,1310721, +2,9,26,1310721,2,2,1633401,12,10,3,6,10,4,8,1310721,2, +9,26,2,1704825,16,1,1592089,12,8,1310721,2,9,26,1,6,2, +1,6,-1,26,5,6,4,10,8,8,1704765,14,1705171,14,1310721,2, +7,26,1587483,2,10,3,6,10,4,8,1310721,2,11,26,1310721,2, +5,26,2,5,1310721,2,7,26,2,1590445,12,10,3,6,10,4, +8,1310721,2,2,1704941,16,1559041,1310721,2,11,26,2,26,1310721,2,2, +10,3,6,10,4,8,1,1592089,12,8,1587549,2,1603007,12,1705171,16, +1585223,2,7,28,1,6,1,10,28,34,63,38,3,26,1705139,16, +1310721,2,7,26,2,1587485,2,26,1587483,2,10,3,6,10,4,8, +1310721,2,9,26,1585225,2,10,3,6,10,4,8,1310721,2,9,26, +2,2,1705121,16,1310721,2,9,26,2,2,1592089,12,8,1310721,2,9, +26,1,6,2,1,6,3,26,5,6,4,10,8,8,1705049,14, +1,1592089,12,8,1601277,12,8,1705171,14,1310721,2,7,26,2,1587485,2, +26,1676587,12,10,3,6,10,4,8,1587545,2,1603007,12,1,6,1, +10,28,34,63,38,3,26,1705207,16,1705235,14,1310721,2,5,26,1603527, +12,10,3,6,10,4,8,1704319,14,1587547,2,1603237,12,8,1,1705253, +14,1,1310721,2,13,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721, +2,1835009,40,1705301,16,1966081,1310721,4,1579223,12,1601277,12,8,3,1705401,16, +1310721,2,1603527,12,10,3,6,10,4,8,1587549,2,1603237,12,8,1310721, +2,2,9,1676587,12,1590445,12,8,1587545,2,1603007,12,1,6,1,10, +28,34,63,38,3,26,1705397,16,1705401,14,1705307,14,1587547,2,1603237,12, +8,1,1705419,14,1,1310721,2,3,26,1310721,4,18,1310721,2,43,28, +1310721,4,1310721,2,1835009,40,1705467,16,1966081,1310721,4,1579223,12,1,1310721,2, +3,26,4,3,1310721,2,5,26,4,1,1310721,2,13,26,4,1, +1310721,2,15,26,4,1601277,12,8,1585223,2,3,28,1,6,1,10, +28,34,63,38,3,26,1705723,16,1585225,2,1,40,1705571,16,3,1705603, +14,1585225,2,17,10,40,1,6,1,10,28,34,63,38,1,10, +28,1705693,16,1705685,14,97,110,32,101,110,104,97,110,99,101,109, +101,110,116,32,104,97,115,32,97,116,32,109,111,115,116,32, +56,32,111,117,116,112,117,116,115,0,1705611,1588813,12,8,1310721,2, +5,26,1585225,2,10,3,6,10,4,8,1601277,12,8,1310721,2,1603527, +12,10,3,6,10,4,8,1587533,2,1603237,12,8,1587535,2,1602703,12, +1,6,1,10,28,34,63,38,3,26,1706027,16,3,1706027,16,1310721, +2,3,26,2,17,28,1,6,1,10,28,34,63,38,3,26, +1705917,16,1705909,14,97,110,32,101,110,104,97,110,99,101,109,101, +110,116,32,116,97,107,101,115,32,97,116,32,109,111,115,116, +32,56,32,105,110,112,117,116,115,0,1705833,1589197,12,8,1310721,2, +27,26,1310721,2,3,26,2,26,1603527,12,10,3,6,10,4,8, +1310721,2,3,26,1,6,2,1,6,3,26,5,6,4,10,8, +8,1587545,2,1603007,12,1,6,1,10,28,34,63,38,3,26,1706023, +16,1706027,14,1705785,14,1587535,2,1603237,12,8,1310721,2,7,26,1587483,2, +10,3,6,10,4,8,1310721,2,17,26,1,10,3,6,10,4, +8,1310721,2,17,26,2,15,40,1706147,16,1,1592089,12,8,1310721,2, +17,26,1,6,2,1,6,3,26,5,6,4,10,8,8,1706083, +14,1310721,2,9,26,1587483,2,10,3,6,10,4,8,1310721,2,17, +26,1,10,3,6,10,4,8,1310721,2,17,26,2,1310721,2,3, +26,2,40,1706265,16,1,1592089,12,8,1310721,2,17,26,1,6,2, +1,6,3,26,5,6,4,10,8,8,1706193,14,1310721,2,17,26, +1,10,3,6,10,4,8,1310721,2,17,26,2,1310721,2,3,26, +2,40,1706517,16,1310721,2,9,26,2,1310721,2,17,26,2,26,1587485, +2,26,1587483,2,10,3,6,10,4,8,1310721,2,19,26,1310721,2, +27,26,1310721,2,17,26,2,26,2,10,3,6,10,4,8,1310721, +2,19,26,2,2,1706471,16,1310721,2,19,26,2,2,1592089,12,8, +1310721,2,19,26,1,6,2,1,6,3,26,5,6,4,10,8, +8,1706399,14,1,1592089,12,8,1310721,2,17,26,1,6,2,1,6, +3,26,5,6,4,10,8,8,1706287,14,1587613,2,1603007,12,1706813,16, +1310721,2,11,26,1587483,2,10,3,6,10,4,8,1585223,2,7,28, +1,6,1,10,28,34,63,38,1,10,28,1706663,16,1706655,14,101, +120,112,101,99,116,101,100,32,116,104,101,32,114,101,99,105, +112,101,32,97,115,32,97,32,115,116,114,105,110,103,0,1706591, +1589197,12,8,1585223,2,7,28,1,6,1,10,28,34,63,38,3, +26,1706801,16,1310721,2,19,26,1585225,2,10,3,6,10,4,8,1310721, +2,19,26,2,2,1706791,16,1310721,2,19,26,2,2,1592089,12,8, +1310721,2,19,26,1,6,2,1,6,3,26,5,6,4,10,8, +8,1706719,14,1601277,12,8,1706663,14,1,1592089,12,8,1706979,14,1587615,2, +1603007,12,1706925,16,1310721,2,11,26,1587483,2,10,3,6,10,4,8, +1688615,12,8,19,1592369,12,8,1310721,2,13,26,1587483,2,1310721,2,11, +26,2,28,10,3,6,10,4,8,1310721,2,15,26,3,10,3, +6,10,4,8,1706979,14,1706971,14,101,120,112,101,99,116,101,100, +32,109,105,99,114,111,32,111,114,32,118,109,0,1706929,1589197,12, +8,1310721,2,23,26,1585149,2,10,3,6,10,4,8,1310721,2,23, +26,2,1025,40,3,36,1707109,16,1707101,14,97,108,108,32,53,49, +50,32,101,110,104,97,110,99,101,109,101,110,116,32,115,108, +111,116,115,32,97,114,101,32,116,97,107,101,110,0,1707029,1588719, +12,8,1585149,1,6,2,1,6,3,26,5,6,4,10,8,8, +1310721,2,19,26,1310721,2,7,26,2,1587485,2,26,10,3,6,10, +4,8,1310721,2,19,26,2,1,26,1310721,2,23,26,2,10,3, +6,10,4,8,1310721,2,19,26,2,3,26,1310721,2,15,26,2, +10,3,6,10,4,8,1310721,2,19,26,2,5,26,1310721,2,3, +26,2,10,3,6,10,4,8,1310721,2,19,26,2,7,26,1310721, +2,5,26,2,10,3,6,10,4,8,1310721,2,19,26,2,9, +26,1310721,2,11,26,2,10,3,6,10,4,8,1310721,2,19,26, +2,11,26,1310721,2,13,26,2,10,3,6,10,4,8,1310721,2, +19,26,2,13,26,1310721,2,9,26,2,10,3,6,10,4,8, +1310721,2,21,26,1310721,2,2,13,1310721,2,23,26,2,1590445,12,10, +3,6,10,4,8,1434113,1310721,2,21,26,2,26,1310721,2,3,26, +2,1310721,2,5,26,2,33,30,26,1310721,2,7,26,2,513,30, +26,10,3,6,10,4,8,1587579,2,1603007,12,1707667,16,1310721,2,25, +26,1585225,2,1590943,12,10,3,6,10,4,8,1310721,2,25,26,2, +1,40,1707621,16,1310721,2,25,26,1585225,2,1592751,12,10,3,6,10, +4,8,1601277,12,8,1450497,1310721,2,21,26,2,26,1310721,2,25,26, +2,3,26,10,3,6,10,4,8,1587547,2,1603237,12,8,1587485,2, +1,6,1,10,28,34,63,38,3,26,1707987,16,1310721,2,25,26, +1310721,2,7,26,2,1572879,12,10,3,6,10,4,8,1310721,2,25, +26,2,1,40,1707987,16,1450497,1310721,2,21,26,2,26,2,1,6, +1,10,28,34,63,38,3,26,1707941,16,1310721,2,2,1707933,14,116, +104,101,32,101,110,104,97,110,99,101,109,101,110,116,32,100, +105,100,110,39,116,32,105,110,115,116,97,108,108,32,40,115, +101,101,32,105,110,115,116,97,108,108,39,115,32,99,111,100, +101,115,32,105,110,32,99,111,114,101,46,99,41,0,1707807,1588983, +12,8,1442305,1310721,2,21,26,2,26,1442305,1310721,2,21,26,2,26, +2,129,34,10,3,6,10,4,8,1,1707995,14,1,1310721,2,43, +26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1708043,16, +1966081,1310721,4,1579223,12,1,1310721,2,4,1585223,2,1,28,1,6,1, +10,28,34,63,38,1,10,28,1708933,16,1587537,2,1602703,12,1708131,16, +1310721,2,1,6,2,1,6,3,26,5,6,4,10,8,8,1708923, +14,1587539,2,1602703,12,1708209,16,1310721,2,1,6,2,1,6,-1,26, +5,6,4,10,8,8,1310721,2,2,1,40,1708205,16,1310721,2,1, +10,3,6,10,4,8,1708923,14,1310721,2,2,1,6,1,10,28, +34,63,38,3,26,1708293,16,1585223,2,5,28,1,6,1,10,28, +34,63,38,3,26,1,6,1,10,28,34,63,38,1,10,28, +1708295,14,1,1708913,16,1585225,2,1587573,2,28,1,6,1,10,28,34, +63,38,3,26,1708339,16,3,1708391,14,1585225,2,1587575,2,28,1,6, +1,10,28,34,63,38,3,26,1,6,1,10,28,34,63,38, +1,10,28,1708401,16,3,1708453,14,1585225,2,1587577,2,28,1,6,1, +10,28,34,63,38,3,26,1,6,1,10,28,34,63,38,1, +10,28,1708463,16,3,1708515,14,1585225,2,1587677,2,28,1,6,1,10, +28,34,63,38,3,26,1,6,1,10,28,34,63,38,1,10, +28,1708525,16,3,1708577,14,1585225,2,1587679,2,28,1,6,1,10,28, +34,63,38,3,26,1,6,1,10,28,34,63,38,1,10,28, +1708587,16,3,1708639,14,1585225,2,1587611,2,28,1,6,1,10,28,34, +63,38,3,26,1,6,1,10,28,34,63,38,1,10,28,1708649, +16,3,1708701,14,1585225,2,1587681,2,28,1,6,1,10,28,34,63, +38,3,26,1,6,1,10,28,34,63,38,1,10,28,1708711,16, +3,1708763,14,1585225,2,1587583,2,28,1,6,1,10,28,34,63,38, +3,26,1,6,1,10,28,34,63,38,1,10,28,1708773,16,3, +1708825,14,1585225,2,1587585,2,28,1,6,1,10,28,34,63,38,3, +26,1,6,1,10,28,34,63,38,1,10,28,1708835,16,3,1708887, +14,1585225,2,1587691,2,28,1,6,1,10,28,34,63,38,3,26, +1,6,1,10,28,34,63,38,1,10,28,1,6,1,10,28, +34,63,38,1,10,28,1708915,14,1,1708923,16,1708933,14,1601277,12,8, +1708051,14,1588225,1,10,3,6,10,4,8,1,1708957,14,1,1310721,2, +3,26,1310721,4,18,1310721,2,7,28,1310721,4,1310721,2,1835009,40,1709005, +16,1966081,1310721,4,1579223,12,1310721,2,3,26,4,1310721,2,4,1587655,1, +10,3,6,10,4,8,1310721,2,3,26,2,1709267,16,1587485,2,1709177, +16,1709169,14,112,97,114,115,101,32,119,111,114,100,115,32,99, +97,110,39,116,32,98,101,32,100,101,102,105,110,101,100,32, +119,104,105,108,101,32,98,117,105,108,100,105,110,103,32,97, +110,32,105,109,97,103,101,0,1709063,1588813,12,8,1310721,2,5,26, +1409537,1310721,2,2,26,2,11,1425921,1310721,2,2,26,2,1590445,12,10, +3,6,10,4,8,1442305,1310721,2,5,26,2,26,3,17,34,33, +34,65,34,10,3,6,10,4,8,1,1709275,14,1,1310721,2,7, +26,1310721,4,18,1310721,2,11,28,1310721,4,1310721,2,1835009,40,1709323,16, +1966081,1310721,4,1579223,12,3,1310721,2,4,1,1310721,2,3,26,4,1588225, +2,1709359,16,1708009,12,1711101,14,1587583,2,1602855,12,1709429,16,1601277,12,8, +1587547,2,1603007,12,1709411,16,1585221,1,10,3,6,10,4,8,1,1711101, +14,1310721,2,1,10,3,6,10,4,8,1587585,2,1602855,12,1709469,16, +1601277,12,8,1310721,2,3,26,3,10,3,6,10,4,8,1587703,1, +10,3,6,10,4,8,1587691,2,1602855,12,1709519,16,1601277,12,8,1587703, +3,10,3,6,10,4,8,1587655,1,10,3,6,10,4,8,1587661, +2,1602703,12,1709735,16,1601277,12,8,3,1709729,16,1587655,2,17,40,3, +36,1709637,16,1709629,14,116,111,111,32,109,97,110,121,32,116,121, +112,101,32,112,97,114,97,109,101,116,101,114,115,0,1709579,1588719, +12,8,1587639,1587655,2,26,1603527,12,10,3,6,10,4,8,1587655,1, +6,2,1,6,3,26,5,6,4,10,8,8,1587545,2,1603007,12, +1,6,1,10,28,34,63,38,3,26,1709725,16,1709729,14,1709553,14, +1640371,12,8,1587573,2,1602855,12,1709939,16,1602137,12,8,1587445,2,9,28, +1,6,1,10,28,34,63,38,3,26,1709841,16,1587447,2,1587533,2, +28,1,6,1,10,28,34,63,38,3,26,1,6,1,10,28, +34,63,38,1,10,28,1709843,14,1,1,6,1,10,28,34,63, +38,3,26,1709939,16,1601277,12,8,1310721,2,9,26,1603527,12,10,3, +6,10,4,8,1310721,2,2,1,1,1310721,2,9,26,2,1699585,12, +1310721,2,3,26,2,1708971,12,1711101,14,1587655,2,1709953,16,3,1710001,14, +1310721,2,2,1,6,1,10,28,34,63,38,3,26,1,6,1, +10,28,34,63,38,1,10,28,1710011,16,3,1710043,14,1310721,2,3, +26,2,1,6,1,10,28,34,63,38,1,10,28,1710053,16,3, +1710079,14,1587703,2,1,6,1,10,28,34,63,38,1,10,28,1710407, +16,1643415,12,8,1310721,2,9,26,1603527,12,10,3,6,10,4,8, +1587659,2,1710177,16,1587659,2,5,28,1,6,1,10,28,34,63,38, +1,10,28,1,6,1,10,28,34,63,38,1,10,28,1710179,14, +1,1710247,16,1710239,14,116,104,101,32,111,110,108,121,32,114,101, +116,117,114,110,32,116,97,103,32,105,115,32,46,117,0,1710187, +1588813,12,8,1587533,2,1602703,12,1,6,1,10,28,34,63,38,3, +26,1710337,16,1710323,14,101,120,112,101,99,116,101,100,32,97,32, +102,117,110,99,116,105,111,110,0,1710283,1589197,12,8,1,1711101,14, +1310721,2,2,1587657,2,1587659,2,5,28,1,6,1,10,28,34,63, +38,3,26,1310721,2,9,26,2,1699585,12,1310721,2,3,26,2,1708971, +12,1711101,14,1587575,2,1602855,12,1710429,16,1,1703965,12,1711101,14,1587677,2, +1602855,12,1710453,16,1643789,12,8,1,1711101,14,1587679,2,1602855,12,1710473,16, +1645695,12,1711101,14,1587681,2,1602855,12,1710493,16,1645815,12,1711101,14,1587611,2, +1602855,12,1710513,16,1705433,12,1711101,14,1587577,2,1602855,12,1710533,16,1705267,12, +1711101,14,1585223,2,5,28,1,6,1,10,28,34,63,38,3,26, +1710763,16,1310721,2,5,26,1585225,2,1590943,12,10,3,6,10,4,8, +1310721,2,5,26,2,1,40,3,36,1710681,16,1417729,1310721,2,5,26, +2,26,2,11,28,1,6,1,10,28,34,63,38,3,26,1, +6,1,10,28,34,63,38,1,10,28,1710683,14,1,1710733,16,1442305, +1310721,2,5,26,2,26,2,65,32,1,6,1,10,28,34,63, +38,1,10,28,1710735,14,1,1710763,16,1310721,2,5,26,2,1,1655803, +12,8,1,1711101,14,1642613,12,1711035,16,1643415,12,8,1310721,2,9,26, +1603527,12,10,3,6,10,4,8,1587533,2,1602703,12,1711009,16,1587659,2, +1710877,16,1587659,2,5,28,1,6,1,10,28,34,63,38,1,10, +28,1,6,1,10,28,34,63,38,1,10,28,1710879,14,1,1710947, +16,1710939,14,116,104,101,32,111,110,108,121,32,114,101,116,117, +114,110,32,116,97,103,32,105,115,32,46,117,0,1710887,1588813,12, +8,1310721,2,2,1587657,2,1587659,2,5,28,1,6,1,10,28,34, +63,38,3,26,1310721,2,9,26,2,1699585,12,1,1708971,12,1711101,14, +1587657,2,1587659,2,1310721,2,9,26,2,1704043,12,1711101,14,1711085,14,101, +120,112,101,99,116,101,100,32,97,32,100,101,99,108,97,114, +97,116,105,111,110,0,1711039,1589197,12,8,1,1711101,14,1,1310721,2, +11,26,1310721,4,18,1310721,2,1,28,1310721,4,1310721,2,1835009,40,1711149, +16,1966081,1310721,4,1579223,12,1585223,2,1,28,1,6,1,10,28,34, +63,38,1,10,28,1711193,16,1709289,12,8,1711149,14,1,1711201,14,1, +1310721,2,1,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,1835009, +40,1711249,16,1966081,1310721,4,1579223,12,1310721,2,4,1310721,2,2,1310721,2, +3,26,4,1588225,1,10,3,6,10,4,8,1310721,2,3,26,2, +1585153,2,40,1711619,16,1417729,1310721,2,3,26,2,26,2,3,28,1, +6,1,10,28,34,63,38,3,26,1711417,16,1442305,1310721,2,3,26, +2,26,2,3,32,1,6,1,10,28,34,63,38,3,26,1, +6,1,10,28,34,63,38,1,10,28,1711419,14,1,1711465,16,1450497, +1310721,2,3,26,2,26,2,1,6,1,10,28,34,63,38,1, +10,28,1711467,14,1,1711581,16,1585229,1425921,1310721,2,3,26,2,26,2, +10,3,6,10,4,8,1409537,1310721,2,3,26,2,26,2,1711573,14, +105,115,32,117,115,101,100,32,98,117,116,32,110,101,118,101, +114,32,100,101,102,105,110,101,100,0,1711521,1588983,12,8,1310721,2, +3,26,1,6,2,1,6,3,26,5,6,4,10,8,8,1711287, +14,1588223,2,1711633,16,1588531,12,8,1,1711641,14,1,1310721,2,5,26, +1310721,4,18,1310721,2,11,28,1310721,4,1310721,2,1835009,40,1711689,16,1966081, +1310721,4,1579223,12,1310721,2,3,26,4,1310721,2,4,3,1,10,28, +1310721,2,5,26,4,1,1310721,2,7,26,4,1585153,2,1310721,2,9, +26,4,1696941,12,8,1587511,2,1711795,16,1310721,2,5,26,1592669,12,10, +3,6,10,4,8,15,1592369,12,8,1310721,2,3,26,2,1711823,16, +3,1310721,2,2,1695997,12,8,3,1310721,2,2,1,1,1595149,12,8, +1601277,12,8,1310721,2,3,26,2,1711871,16,1711115,12,8,1712025,14,1310721, +2,7,26,1587483,2,10,3,6,10,4,8,3,1,1698421,12,8, +1698753,12,8,1585223,2,1,28,1,6,1,10,28,34,63,38,1, +10,28,1711955,16,1677555,12,8,1711911,14,1587493,2,1610397,12,8,1587499,2, +1712003,16,1587503,2,1,6,1,10,28,34,63,38,1,10,28,1712005, +14,1,1712019,16,3,3,1609669,12,8,1698991,12,8,1596577,12,8,1310721, +2,9,26,2,1711215,12,8,1310721,2,5,26,2,1,40,3,36, +1712089,16,1310721,2,5,26,2,1587483,2,1592451,12,8,1587483,2,1587489,2, +10,40,1712123,16,1587489,1587483,2,10,3,6,10,4,8,1697717,12,8, +1310721,2,7,26,2,1712145,14,1,1310721,2,11,26,1310721,4,18,1310721, +2,3,28,1310721,4,1310721,2,1835009,40,1712193,16,1966081,1310721,4,1579223,12, +1310721,2,4,1310721,2,2,1,1711655,12,1712217,14,1,1310721,2,3,26, +1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1712265,16,1966081, +1310721,4,1579223,12,1310721,2,4,1310721,2,2,3,1711655,12,8,1,1712293, +14,1,1310721,2,3,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721, +2,1835009,40,1712341,16,1966081,1310721,4,1579223,12,1310721,2,4,1310721,2,2, +1,1711655,12,1310721,2,3,26,4,1310721,2,3,26,2,1,6,12, +10,8,1712395,14,1,1310721,2,5,26,1310721,4,18,1310721,2,7,28, +1310721,4,1310721,2,1835009,40,1712443,16,1966081,1310721,4,1579223,12,1310721,2,4, +1310721,2,2,1692925,12,1310721,2,3,26,4,1310721,2,3,26,2,1712307, +12,1310721,2,5,26,4,1310721,2,3,26,2,1580907,12,8,1310721,2, +5,26,2,1712525,14,1,1310721,2,7,26,1310721,4,18,1310721,2,7, +28,1310721,4,1310721,2,1835009,40,1712573,16,1966081,1310721,4,1579223,12,1310721,2, +4,1310721,2,2,1310721,2,3,26,4,1310721,2,3,26,2,1585153,2, +40,1712993,16,1417729,1310721,2,3,26,2,26,2,3,40,3,36,1712695, +16,1417729,1310721,2,3,26,2,26,2,9,10,40,3,36,1,6, +1,10,28,34,63,38,1,10,28,1712697,14,1,1712707,16,3,1712769, +14,1417729,1310721,2,3,26,2,26,2,13,28,1,6,1,10,28, +34,63,38,3,26,1,6,1,10,28,34,63,38,1,10,28, +1712955,16,1417729,1310721,2,3,26,2,26,2,1592089,12,8,1425921,1310721,2, +3,26,2,26,2,1592089,12,8,1434113,1310721,2,3,26,2,26,2, +1592089,12,8,1310721,2,5,26,1409537,1310721,2,3,26,2,26,2,10, +3,6,10,4,8,1310721,2,5,26,2,2,1712947,16,1310721,2,5, +26,2,2,1592089,12,8,1310721,2,5,26,1,6,2,1,6,3, +26,5,6,4,10,8,8,1712875,14,1,1592089,12,8,1310721,2,3, +26,1,6,2,1,6,3,26,5,6,4,10,8,8,1712595,14, +1,1592089,12,8,1,1713009,14,1,1310721,2,7,26,1310721,4,18,1310721, +2,7,28,1310721,4,1310721,2,1835009,40,1713057,16,1966081,1310721,4,1579223,12, +1310721,2,4,1310721,2,2,2,1713405,16,1310721,2,5,26,1310721,2,2, +7,26,1589927,12,10,3,6,10,4,8,1310721,2,3,26,1310721,2, +5,26,2,1310721,2,2,1,26,2,1310721,2,2,3,26,2,1590445, +12,10,3,6,10,4,8,1434113,1310721,2,3,26,2,26,1310721,2, +2,5,26,2,10,3,6,10,4,8,1442305,1310721,2,3,26,2, +26,3,5,34,10,3,6,10,4,8,1310721,2,2,1,26,2, +13,28,1,6,1,10,28,34,63,38,3,26,1713355,16,1310721,2, +2,3,26,2,1585149,2,40,3,36,1713333,16,1585149,1310721,2,2,3, +26,2,3,26,10,3,6,10,4,8,1310721,2,2,5,26,2, +17,38,1572879,12,8,1310721,2,1310721,2,2,7,26,1310721,2,2,7, +26,1574039,12,26,3,26,10,3,6,10,4,8,1713063,14,1,1713413, +14,1,1310721,2,7,26,1310721,4,18,1310721,2,21,28,1310721,4,1310721, +2,1835009,40,1713461,16,1966081,1310721,4,1579223,12,1310721,2,3,26,4,1310721, +2,4,1587483,2,1310721,2,5,26,4,1587485,2,1310721,2,7,26,4, +1587487,2,1310721,2,9,26,4,1585155,2,1310721,2,11,26,4,1585157,2, +1310721,2,13,26,4,1585153,2,1310721,2,15,26,4,1585149,2,1310721,2, +17,26,4,1585155,1585153,2,10,3,6,10,4,8,1585157,1585153,2,10, +3,6,10,4,8,1585149,1,10,3,6,10,4,8,1587483,1310721,2, +2,10,3,6,10,4,8,1587485,1310721,2,3,26,2,1310721,2,2, +28,10,3,6,10,4,8,1587487,1310721,2,2,393217,26,10,3,6, +10,4,8,1966081,1592283,12,8,1310721,1592283,12,8,5,1592369,12,8,1587601, +2,1592751,12,1592875,12,8,15,1592369,12,8,1,1592089,12,8,1,1592089, +12,8,1696803,12,8,1310721,2,3,26,2,11,26,1587483,2,10,3, +6,10,4,8,1585155,2,1712539,12,8,1310721,2,3,26,2,13,26, +1587483,2,10,3,6,10,4,8,1310721,2,19,26,1587483,2,1310721,2, +2,28,10,3,6,10,4,8,1310721,2,15,26,2,1591291,12,8, +1585155,1310721,2,11,26,2,10,3,6,10,4,8,1585157,1310721,2,13, +26,2,10,3,6,10,4,8,1587483,1310721,2,5,26,2,10,3, +6,10,4,8,1587485,1310721,2,7,26,2,10,3,6,10,4,8, +1587487,1310721,2,9,26,2,10,3,6,10,4,8,1585149,1310721,2,17, +26,2,10,3,6,10,4,8,1310721,2,19,26,2,1714049,14,1, +1310721,2,21,26,1310721,4,18,1310721,2,1,28,1310721,4,1310721,2,1835009, +40,1714097,16,1966081,1310721,4,1579223,12,1,1587489,2,1583785,12,8,1587485,2, +1,6,1,10,28,34,63,38,3,26,1714175,16,1587483,2,1587489,2, +10,40,1,6,1,10,28,34,63,38,1,10,28,1714177,14,1, +1714195,16,1587489,2,1587483,2,1583785,12,8,1572865,1572865,13,26,2,1583785,12, +8,1540609,1540609,2049,26,1583785,12,8,1310721,2,1966081,1583785,12,8,2064385,2097121, +1583785,12,8,1,1714255,14,1,1310721,2,1,26,1310721,4,18,1310721,2, +1,28,1310721,4,1310721,2,1835009,40,1714303,16,1966081,1310721,4,1579223,12,1585151, +1327617,10,3,6,10,4,8,1311233,1,16385,1574703,12,8,1585153,1,10, +3,6,10,4,8,1585159,1,10,3,6,10,4,8,1585161,1,10, +3,6,10,4,8,1585163,1,10,3,6,10,4,8,1585165,1,10, +3,6,10,4,8,1585167,1,10,3,6,10,4,8,1585169,1,10, +3,6,10,4,8,1585171,1,10,3,6,10,4,8,1587513,1,10, +3,6,10,4,8,1587517,1,10,3,6,10,4,8,1587519,1,10, +3,6,10,4,8,1587491,1,10,3,6,10,4,8,1587493,1,10, +3,6,10,4,8,1587533,1714549,14,40,0,1714545,1589927,12,10,3,6, +10,4,8,1587535,1714577,14,41,0,1714573,1589927,12,10,3,6,10,4, +8,1587537,1714605,14,123,0,1714601,1589927,12,10,3,6,10,4,8,1587539, +1714633,14,125,0,1714629,1589927,12,10,3,6,10,4,8,1587541,1714661,14, +91,0,1714657,1589927,12,10,3,6,10,4,8,1587543,1714689,14,93,0, +1714685,1589927,12,10,3,6,10,4,8,1587545,1714717,14,44,0,1714713,1589927, +12,10,3,6,10,4,8,1587547,1714745,14,59,0,1714741,1589927,12,10, +3,6,10,4,8,1587549,1714773,14,61,0,1714769,1589927,12,10,3,6, +10,4,8,1587551,1714803,14,45,62,0,1714797,1589927,12,10,3,6,10, +4,8,1587553,1714833,14,43,43,0,1714827,1589927,12,10,3,6,10,4, +8,1587555,1714863,14,45,45,0,1714857,1589927,12,10,3,6,10,4,8, +1587557,1714891,14,42,0,1714887,1589927,12,10,3,6,10,4,8,1587559,1714919, +14,38,0,1714915,1589927,12,10,3,6,10,4,8,1587561,1714947,14,58, +0,1714943,1589927,12,10,3,6,10,4,8,1587563,1714975,14,35,0,1714971, +1589927,12,10,3,6,10,4,8,1587565,1715003,14,45,0,1714999,1589927,12, +10,3,6,10,4,8,1587571,1715031,14,46,0,1715027,1589927,12,10,3, +6,10,4,8,1587593,1715059,14,63,0,1715055,1589927,12,10,3,6,10, +4,8,1587595,1715093,14,99,97,115,101,0,1715083,1589927,12,10,3,6, +10,4,8,1587597,1715133,14,100,101,102,97,117,108,116,0,1715117,1589927, +12,10,3,6,10,4,8,1587567,1715163,14,38,38,0,1715157,1589927,12, +10,3,6,10,4,8,1587569,1715193,14,124,124,0,1715187,1589927,12,10, +3,6,10,4,8,1587573,1715223,14,102,110,0,1715217,1589927,12,10,3, +6,10,4,8,1587575,1715255,14,108,101,116,0,1715247,1589927,12,10,3, +6,10,4,8,1587577,1715291,14,99,111,110,115,116,0,1715279,1589927,12, +10,3,6,10,4,8,1587579,1715325,14,101,108,115,101,0,1715315,1589927, +12,10,3,6,10,4,8,1587581,1715355,14,97,115,0,1715349,1589927,12, +10,3,6,10,4,8,1587583,1715399,14,117,110,99,104,101,99,107, +101,100,0,1715379,1589927,12,10,3,6,10,4,8,1587585,1715443,14,105, +109,109,101,100,105,97,116,101,0,1715423,1589927,12,10,3,6,10, +4,8,1587587,1715471,14,109,0,1715467,1589927,12,10,3,6,10,4,8, +1587589,1715499,14,100,0,1715495,1589927,12,10,3,6,10,4,8,1587591,1715527, +14,117,0,1715523,1589927,12,10,3,6,10,4,8,1587601,1715563,14,115, +116,97,114,116,0,1715551,1589927,12,10,3,6,10,4,8,1587603,1715597, +14,109,97,105,110,0,1715587,1589927,12,10,3,6,10,4,8,1587605, +1715631,14,102,114,101,101,0,1715621,1589927,12,10,3,6,10,4,8, +1587607,1715663,14,111,119,110,0,1715655,1589927,12,10,3,6,10,4,8, +1587609,1715697,14,113,110,101,119,0,1715687,1589927,12,10,3,6,10,4, +8,1587599,1715751,14,102,114,97,109,101,95,111,118,101,114,102,108, +111,119,0,1715721,1589927,12,10,3,6,10,4,8,1587611,1715791,14,101, +110,104,97,110,99,101,0,1715775,1589927,12,10,3,6,10,4,8, +1587613,1715827,14,109,105,99,114,111,0,1715815,1589927,12,10,3,6,10, +4,8,1587615,1715857,14,118,109,0,1715851,1589927,12,10,3,6,10,4, +8,1,1715883,14,1,1310721,2,1,26,1310721,4,18,1310721,2,1,28, +1310721,4,1310721,2,1835009,40,1715931,16,1966081,1310721,4,1579223,12,1715939,14,33, +0,1715935,1715987,14,48,32,36,32,48,32,126,32,45,32,124,32, +51,49,32,62,32,49,32,43,0,1715945,1607393,12,8,1716005,14,33, +33,0,1715999,1716057,14,48,32,36,32,48,32,126,32,45,32,124, +32,51,49,32,62,32,48,32,126,32,45,0,1716011,1607393,12,8, +1716073,14,126,0,1716069,1716089,14,45,49,32,94,0,1716079,1607393,12,8, +1716105,14,45,0,1716101,1716123,14,48,32,126,32,45,0,1716111,1607393,12, +8,1716139,14,42,0,1716135,21,1716151,14,42,0,1716147,1606353,12,8,1716167, +14,47,0,1716163,21,1716185,14,105,100,105,118,0,1716175,1606353,12,8, +1716201,14,37,0,1716197,21,1716219,14,105,109,111,100,0,1716209,1606353,12, +8,1716235,14,43,0,1716231,19,1716247,14,43,0,1716243,1606353,12,8,1716263, +14,45,0,1716259,19,1716275,14,45,0,1716271,1606353,12,8,1716293,14,60, +60,0,1716287,17,1716311,14,105,115,104,108,0,1716301,1606353,12,8,1716329, +14,62,62,0,1716323,17,1716341,14,62,0,1716337,1606353,12,8,1716357,14, +60,0,1716353,15,1716369,14,60,0,1716365,1606353,12,8,1716385,14,62,0, +1716381,15,1716401,14,126,32,60,0,1716393,1606353,12,8,1716419,14,60,61, +0,1716413,15,1716443,14,126,32,60,32,49,32,94,0,1716427,1606353,12, +8,1716461,14,62,61,0,1716455,15,1716481,14,60,32,49,32,94,0, +1716469,1606353,12,8,1716499,14,61,61,0,1716493,13,1716553,14,45,32,48, +32,36,32,48,32,126,32,45,32,124,32,51,49,32,62,32, +49,32,43,0,1716507,1606353,12,8,1716571,14,33,61,0,1716565,13,1716629, +14,45,32,48,32,36,32,48,32,126,32,45,32,124,32,51, +49,32,62,32,48,32,126,32,45,0,1716579,1606353,12,8,1716645,14, +38,0,1716641,11,1716657,14,38,0,1716653,1606353,12,8,1716673,14,94,0, +1716669,9,1716685,14,94,0,1716681,1606353,12,8,1716701,14,124,0,1716697,7, +1716713,14,124,0,1716709,1606353,12,8,1716731,14,38,38,0,1716725,3,1, +10,28,1,1606353,12,8,1716759,14,124,124,0,1716753,5,1,10,28, +1,1606353,12,8,1587549,2,1605525,12,8,1587551,2,1605525,12,8,1587553,2, +1605525,12,8,1587555,2,1605525,12,8,1716829,14,108,101,116,0,1716821,1680049, +17,1607727,12,8,1716851,14,105,102,0,1716845,1680121,17,1607727,12,8,1716879, +14,119,104,105,108,101,0,1716867,1682687,17,1607727,12,8,1716903,14,102, +111,114,0,1716895,1683299,17,1607727,12,8,1716933,14,115,119,105,116,99, +104,0,1716919,1685869,17,1607727,12,8,1716961,14,98,114,101,97,107,0, +1716949,1684403,17,1607727,12,8,1716995,14,99,111,110,116,105,110,117,101, +0,1716977,1684829,17,1607727,12,8,1717025,14,114,101,116,117,114,110,0, +1717011,1687173,17,1607727,12,8,1717061,14,117,110,99,104,101,99,107,101, +100,0,1717041,1688495,17,1607727,12,8,1717085,14,97,115,109,0,1717077,1691401, +17,33,34,1607727,12,8,1717117,14,113,117,111,116,101,0,1717105,1692651, +33,1607727,12,8,1717147,14,115,105,122,101,111,102,0,1717133,1648613,33, +1607727,12,8,1717177,14,116,121,112,101,111,102,0,1717163,1648729,33,1607727, +12,8,1717199,14,111,51,0,1717193,1687793,17,1607727,12,8,1717223,14,108, +101,110,0,1717215,1646945,33,1607727,12,8,1717247,14,99,97,112,0,1717239, +1647379,33,1607727,12,8,1717273,14,112,117,115,104,0,1717263,1647649,33,1607727, +12,8,1717297,14,112,111,112,0,1717289,1648221,33,1607727,12,8,1717327,14, +114,101,115,105,122,101,0,1717313,1648425,33,1607727,12,8,1,1717347,14, +1,1310721,2,1,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2, +1835009,40,1717395,16,1966081,1310721,4,1579223,12,1577233,12,8,1714269,12,8,1572865, +11,26,2,1713023,12,8,1715897,12,8,1648841,12,8,1587483,1,10,3, +6,10,4,8,1587485,1,10,3,6,10,4,8,1587487,524289,10,3, +6,10,4,8,1587489,1,10,3,6,10,4,8,1585155,1585153,2,10, +3,6,10,4,8,1585157,1,10,3,6,10,4,8,1696803,12,8, +1587483,2,1587489,2,10,40,1717571,16,1587489,1587483,2,10,3,6,10,4, +8,1310721,2,1587603,2,1590943,12,10,3,6,10,4,8,1310721,2,2, +1,40,3,36,1717675,16,1417729,1310721,2,2,26,2,3,28,1,6, +1,10,28,34,63,38,3,26,1,6,1,10,28,34,63,38, +1,10,28,1717677,14,1,1717723,16,1442305,1310721,2,2,26,2,3,32, +1,6,1,10,28,34,63,38,1,10,28,1717725,14,1,1717783,16, +1310721,2,3,26,1425921,1310721,2,2,26,2,10,3,6,10,4,8, +1310721,2,3,26,2,1,6,12,10,8,8,1573437,12,8,1,1717797, +14,1,1310721,2,5,26,1310721,4,18,1,858680,0,115,116,97,114, +116,0,1,786439,2,105,110,115,116,97,108,108,0,1,786627,2, +112,117,116,99,0,1,786662,0,103,101,116,99,0,1,786690,0, +101,111,102,0,1,786718,0,104,97,108,116,0,1,786747,2,112, +117,116,115,0,1,786828,2,112,117,116,110,0,1,787019,2,115, +116,114,108,101,110,0,1,787091,4,115,116,114,101,113,0,1, +787245,6,119,99,111,112,121,0,1,787351,6,119,102,105,108,108, +0,1,787483,4,122,117,100,105,118,0,1,787714,4,122,100,105, +118,0,1,787902,4,122,109,111,100,0,1,787997,4,122,115,104, +108,0,1,788082,2,116,111,112,115,105,122,101,0,1,788258,4, +102,112,117,115,104,0,1,788382,4,102,117,110,108,105,110,107, +0,1,788495,4,109,107,98,108,111,99,107,0,1,788616,0,104, +101,97,112,95,105,110,105,116,0,1,788847,2,98,97,108,108, +111,99,0,1,789084,2,98,102,114,101,101,0,1,789482,2,105, +115,111,98,106,0,1,789611,0,102,114,97,109,101,95,111,118, +101,114,102,108,111,119,0,1,789674,0,111,111,109,0,1,789735, +3,110,101,119,95,114,97,119,0,1,790010,5,110,101,119,95, +116,0,1,790103,3,110,101,119,0,1,790139,2,111,115,105,122, +101,0,1,790200,2,116,121,112,101,111,102,95,111,98,106,0, +1,790259,2,114,101,108,101,97,115,101,0,1,790453,130,102,114, +101,101,0,1,790497,4,111,119,110,0,1,790561,7,100,110,101, +119,0,1,790747,4,100,103,114,111,119,0,1,791103,4,111,111, +98,0,1,791211,4,100,97,116,0,1,791318,516,100,112,117,115, +104,0,1,791425,2,100,112,111,112,0,1,791514,4,100,114,101, +115,105,122,101,0,1,791670,2,100,108,101,110,0,1,791706,2, +100,99,97,112,0,1,791746,2,109,97,114,107,0,1,791892,4, +109,97,114,107,114,97,110,103,101,0,1,791963,0,100,114,97, +105,110,0,1,857031,0,103,99,95,114,111,111,116,115,0,1, +792045,0,103,99,0,1,792363,0,108,105,118,101,95,111,98,106, +101,99,116,115,0,1,792459,0,108,97,114,103,101,115,116,95, +102,114,101,101,0,1,794113,0,101,114,114,97,116,0,1,794185, +0,101,114,114,101,110,100,0,1,794265,0,103,105,118,101,117, +112,0,1,794359,2,102,97,116,97,108,0,1,794406,2,115,101, +114,114,0,1,794491,4,115,101,114,114,110,0,1,794598,2,100, +105,101,0,1,794665,4,100,105,101,110,0,1,794742,2,99,101, +114,114,0,1,794801,4,99,101,114,114,110,0,1,794873,2,104, +97,115,104,115,0,1,794963,2,105,110,116,101,114,110,0,1, +795222,6,97,100,100,115,121,109,0,1,795471,2,108,111,111,107, +117,112,0,1,795645,2,112,111,112,115,121,109,115,0,1,795722, +2,108,111,111,107,117,112,115,0,1,795759,4,102,105,120,110, +101,119,0,1,795935,4,102,105,120,97,108,108,0,1,796044,2, +101,109,105,116,0,1,796141,2,108,105,116,0,1,796184,2,111, +112,0,1,796225,4,115,101,116,108,105,116,0,1,796293,2,106, +109,112,0,1,796334,0,104,111,108,101,0,1,796375,2,102,119, +100,102,110,0,1,796437,2,102,110,114,101,102,0,1,796524,2, +99,97,108,108,110,97,109,101,0,1,796601,2,108,97,100,100, +114,0,1,796655,2,118,97,100,100,114,0,1,796789,2,97,100, +100,115,108,0,1,796915,8,111,51,114,101,103,105,111,110,0, +1,797075,0,111,51,102,108,117,115,104,0,1,797191,2,115,116, +114,108,105,116,0,1,797297,0,110,101,120,116,99,104,0,1, +797574,8,115,114,99,95,112,117,115,104,0,1,798288,0,115,114, +99,95,112,111,112,0,1,798629,2,105,115,111,112,99,104,0, +1,798740,4,111,112,112,114,101,102,105,120,0,1,798983,2,105, +115,100,105,103,0,1,799044,2,105,115,97,108,0,1,799183,2, +104,101,120,118,0,1,799315,0,101,115,99,99,104,0,1,799489, +2,111,112,108,101,120,0,1,799634,0,108,101,120,0,1,800638, +0,110,101,120,116,0,1,800837,2,112,101,101,107,97,116,0, +1,801068,0,112,101,101,107,0,1,801098,0,110,101,120,116,48, +0,1,801351,2,105,115,95,111,112,0,1,801427,2,105,115,95, +105,100,0,1,801503,2,97,99,99,101,112,116,0,1,801618,2, +101,120,112,101,99,116,0,1,801763,0,110,97,109,101,0,1, +801840,2,115,101,113,97,115,109,0,1,802409,2,109,110,101,109, +105,100,120,0,1,802511,4,101,109,105,116,115,101,113,0,1, +802762,2,97,100,100,107,111,112,0,1,802882,2,98,105,110,102, +105,110,100,0,1,802980,2,117,110,102,105,110,100,0,1,803078, +2,97,115,103,102,105,110,100,0,1,803176,6,97,100,100,95, +98,105,110,111,112,0,1,803696,4,97,100,100,95,117,110,111, +112,0,1,803863,6,97,100,100,95,107,119,0,1,803946,2,102, +105,110,100,108,111,99,0,1,804048,2,110,101,119,108,111,99, +0,1,804279,2,108,114,111,111,116,0,1,804348,2,116,114,97, +99,107,101,100,0,1,804397,2,116,111,117,99,104,0,1,804641, +2,112,108,97,105,110,117,115,101,0,1,804781,2,102,114,101, +101,118,97,114,0,1,804834,4,101,120,105,116,95,118,97,114, +115,0,1,805198,2,117,110,117,115,101,100,95,99,104,101,99, +107,0,1,805347,2,98,108,111,99,107,95,99,108,111,115,101, +0,1,805483,0,115,110,97,112,95,112,117,115,104,0,1,805758, +2,115,110,97,112,95,112,111,112,0,1,805799,2,115,110,97, +112,95,114,101,115,116,111,114,101,0,1,806018,2,101,109,112, +116,121,0,1,806094,2,115,110,97,112,95,99,109,112,0,1, +806499,2,115,116,97,116,101,110,97,109,101,0,1,806717,6,109, +101,114,103,101,95,101,114,114,0,1,806983,4,106,115,116,0, +1,807058,4,106,111,119,0,1,807114,4,106,104,109,0,1,807170, +2,101,120,105,116,95,114,101,99,0,1,807466,2,106,97,100, +100,0,1,807570,6,106,115,97,109,101,0,1,807830,4,106,111, +105,110,0,1,808622,6,102,105,120,117,112,115,0,1,808869,2, +98,97,99,107,101,100,103,101,0,1,809660,12,106,111,105,110, +50,0,1,809947,4,114,101,99,97,115,116,0,1,810483,4,97, +114,103,99,104,101,99,107,0,1,811249,2,101,109,105,116,111, +119,110,0,1,811333,6,97,115,115,105,103,110,99,104,101,99, +107,0,1,813110,2,114,101,116,99,104,101,99,107,0,1,813617, +2,104,97,110,100,117,112,0,1,813975,2,100,105,115,99,97, +114,100,0,1,814098,2,116,97,108,108,111,99,0,1,814205,8, +116,110,101,119,0,1,814379,2,116,112,116,114,0,1,814566,4, +116,97,114,114,0,1,814826,8,116,102,110,0,1,815287,4,116, +100,121,110,0,1,815537,4,116,118,97,114,0,1,815733,6,116, +105,110,115,116,0,1,816164,2,116,114,101,115,111,108,118,101, +0,1,816700,2,116,115,122,0,1,816786,4,115,117,98,115,116, +0,1,817285,2,105,115,110,117,109,0,1,817401,2,105,115,97, +103,103,0,1,817483,2,105,115,112,116,114,0,1,817565,2,100, +101,99,97,121,0,1,817628,2,116,101,108,101,109,0,1,817722, +4,99,111,109,112,97,116,0,1,818380,6,117,110,105,102,121, +0,1,818925,2,116,112,114,105,110,116,0,1,819553,6,116,99, +101,114,114,0,1,819753,6,116,99,110,101,101,100,0,1,819948, +2,116,112,102,105,110,100,0,1,820046,2,105,115,116,121,112, +101,110,97,109,101,0,1,820185,0,99,108,111,115,101,97,110, +103,108,101,0,1,820236,0,112,116,121,112,101,0,1,820442,0, +112,116,121,112,101,48,0,1,821306,0,116,115,116,97,114,116, +0,1,821707,0,100,116,121,112,101,0,1,821894,0,107,119,95, +115,116,114,117,99,116,0,1,822847,0,107,119,95,116,121,112, +101,0,1,822907,0,107,119,95,116,121,112,101,99,104,101,99, +107,0,1,823014,4,116,102,105,110,100,102,105,101,108,100,0, +1,823138,0,116,121,112,101,97,114,103,0,1,823305,0,97,114, +114,97,114,103,0,1,823358,2,110,111,103,114,111,119,0,1, +823472,0,107,119,95,108,101,110,0,1,823689,0,107,119,95,99, +97,112,0,1,823824,0,107,119,95,112,117,115,104,0,1,824110, +0,107,119,95,112,111,112,0,1,824212,0,107,119,95,114,101, +115,105,122,101,0,1,824306,0,107,119,95,115,105,122,101,111, +102,0,1,824364,0,107,119,95,116,121,112,101,111,102,0,1, +824420,0,116,121,112,101,115,95,105,110,105,116,0,1,825229,2, +105,115,110,117,109,111,112,0,1,825322,6,98,105,110,116,121, +112,101,0,1,826715,2,116,107,105,110,100,0,1,826751,2,116, +115,105,122,101,0,1,826786,2,116,110,97,109,101,0,1,826822, +2,116,108,101,110,0,1,826858,2,116,110,102,105,101,108,100, +115,0,1,826900,4,116,102,110,97,109,101,0,1,826956,4,116, +102,116,121,112,101,0,1,827014,4,116,102,111,102,102,0,1, +827072,2,116,114,101,116,0,1,827108,2,116,110,112,97,114,97, +109,115,0,1,827144,4,116,112,97,114,97,109,0,1,827191,4, +116,112,97,114,97,109,116,97,103,0,1,827248,2,116,114,101, +116,117,0,1,827286,2,116,110,97,114,103,115,0,1,827381,4, +116,97,114,103,0,1,827431,2,115,105,103,111,102,0,1,827742, +2,115,105,103,111,102,115,0,1,827841,0,114,118,97,108,0, +1,827901,4,107,119,99,97,108,108,0,1,828047,0,115,116,109, +116,111,110,108,121,0,1,828125,6,97,114,103,115,0,1,828398, +0,110,101,119,116,121,112,101,100,0,1,828875,2,99,97,108, +108,0,1,829530,2,101,110,104,99,97,108,108,0,1,829899,0, +112,114,105,109,97,114,121,0,1,831364,4,105,110,99,100,101, +99,0,1,831788,2,102,105,101,108,100,0,1,832464,0,112,111, +115,116,102,105,120,0,1,834012,0,117,110,97,114,121,0,1, +834952,2,98,105,110,0,1,835465,0,115,116,111,114,101,107,101, +101,112,0,1,835513,2,116,101,114,110,97,114,121,0,1,836163, +0,101,120,112,114,0,1,837262,0,115,116,109,116,115,116,97, +114,116,0,1,837509,0,98,108,111,99,107,101,120,112,114,0, +1,837885,0,99,112,114,105,109,0,1,838293,0,99,101,120,112, +114,0,1,838650,0,114,101,115,121,110,99,0,1,838777,0,115, +116,109,116,0,1,839045,0,98,108,111,99,107,0,1,839173,0, +115,117,98,115,116,109,116,0,1,839253,2,108,111,99,97,108, +100,101,99,108,0,1,840024,0,107,119,95,118,97,114,0,1, +840060,0,107,119,95,105,102,0,1,840606,4,108,111,111,112,95, +112,117,115,104,0,1,840813,4,108,111,111,112,95,101,110,100, +0,1,841343,0,107,119,95,119,104,105,108,101,0,1,841649,0, +107,119,95,102,111,114,0,1,842201,0,107,119,95,98,114,101, +97,107,0,1,842414,0,107,119,95,99,111,110,116,105,110,117, +101,0,1,842701,4,99,97,115,101,98,111,100,121,0,1,842934, +0,107,119,95,115,119,105,116,99,104,0,1,843586,0,107,119, +95,114,101,116,117,114,110,0,1,843896,0,107,119,95,111,51, +0,1,844247,0,107,119,95,117,110,99,104,101,99,107,101,100, +0,1,844307,0,97,115,109,98,108,111,99,107,0,1,845700,0, +107,119,95,97,115,109,0,1,845742,1,103,114,97,98,0,1, +846082,3,113,110,101,119,0,1,846160,67,113,100,117,112,0,1, +846238,68,119,99,111,112,121,116,111,0,1,846325,0,107,119,95, +113,117,111,116,101,0,1,846462,3,116,111,107,101,110,115,0, +1,846702,130,102,101,101,100,0,1,846745,0,114,101,97,100,115, +114,99,0,1,847167,0,112,115,107,105,112,97,110,103,108,101, +0,1,847299,2,112,114,101,115,99,97,110,95,102,110,0,1, +847998,4,112,114,101,115,99,97,110,0,1,848401,0,117,110,105, +116,95,115,116,100,105,110,0,1,848470,0,99,116,120,95,112, +117,115,104,0,1,848858,0,99,116,120,95,112,111,112,0,1, +849210,4,102,110,95,98,101,103,105,110,0,1,849376,0,112,114, +111,108,111,103,117,101,0,1,849495,0,101,112,105,108,111,103, +117,101,0,1,849587,0,98,111,100,121,0,1,849792,8,102,110, +100,101,102,0,1,851982,2,103,100,101,99,108,0,1,852021,6, +103,100,101,99,108,50,0,1,852633,0,103,99,111,110,115,116, +0,1,852716,0,101,110,104,100,101,102,0,1,854004,0,116,111, +112,115,121,110,99,0,1,854485,4,102,110,100,111,110,101,0, +1,854644,0,116,111,112,105,116,101,109,0,1,855557,0,117,110, +105,116,95,116,111,112,0,1,855607,2,117,110,105,116,95,101, +110,100,0,1,855827,4,99,111,109,112,105,108,101,95,108,105, +115,116,0,1,856079,2,99,111,109,112,105,108,101,0,1,856115, +2,108,111,97,100,0,1,856153,2,101,118,97,108,0,1,856204, +2,101,118,97,108,115,0,1,856269,2,98,108,111,98,95,101, +109,105,116,0,1,856511,2,98,108,111,98,95,108,111,97,100, +0,1,856713,4,98,117,105,108,100,95,105,109,97,103,101,0, +1,857134,0,122,99,95,105,110,105,116,0,1,857948,0,122,99, +95,116,97,98,108,101,115,0,4,1048576,0,77,87,0,4,262144, +0,72,69,65,80,95,76,79,0,4,655360,0,72,69,65,80, +95,72,73,0,4,655360,0,90,70,80,0,4,786432,0,71,79, +82,71,0,4,917504,0,70,66,79,84,0,4,983040,0,70,84, +79,80,0,4,983040,0,82,83,48,0,4,1015808,0,77,65,82, +75,48,0,4,16384,0,77,65,82,75,78,0,4,1032192,0,68, +83,76,79,0,4,1048560,0,68,83,72,73,0,4,729683222,0,77, +65,71,73,67,0,6,0,201337106,105,100,105,118,0,6,1,201343762, +105,109,111,100,0,6,2,201350418,105,115,104,108,0,6,3,201356817, +108,102,97,0,6,4,201366802,111,117,116,115,105,100,101,0,3, +787450,0,112,111,119,50,0,2,787482,0,122,114,101,109,0,4, +6,0,72,68,82,0,2,788046,0,104,98,117,109,112,0,3, +788047,0,102,114,101,101,108,0,2,788079,0,103,99,95,114,117, +110,115,0,2,788080,0,103,99,95,102,114,101,101,100,0,2, +788081,0,103,99,95,97,117,116,111,0,2,789083,0,102,101,110, +100,0,2,791742,0,109,115,95,116,111,112,0,2,791743,0,109, +115,95,111,118,101,114,0,2,791744,0,103,99,95,108,111,0, +2,791745,0,103,99,95,104,105,0,4,8192,0,73,72,78,0, +4,655616,0,82,95,73,72,0,4,663808,0,82,95,80,79,79, +76,0,4,704512,0,82,95,80,79,79,76,69,78,68,0,4, +4096,0,78,83,0,4,704768,0,82,95,83,89,77,0,4,704768, +0,115,110,97,109,101,0,4,708864,0,115,107,105,110,100,0, +4,712960,0,115,118,97,108,0,4,717056,0,115,115,105,103,0, +4,721152,0,115,102,108,97,103,0,4,725248,0,115,102,105,120, +0,4,729344,0,115,110,101,120,116,0,4,512,0,78,76,0, +4,733440,0,108,110,97,109,101,0,4,733952,0,108,115,108,111, +116,0,4,734464,0,108,104,111,109,101,0,4,734976,0,108,115, +116,97,116,101,0,4,735488,0,108,111,119,110,101,114,0,4, +736000,0,108,117,115,101,100,0,4,736512,0,108,97,114,114,0, +4,737024,0,108,111,119,110,0,4,8192,0,78,70,0,4,737536, +0,102,115,105,116,101,0,4,745728,0,102,110,101,120,116,0, +4,753920,0,82,95,83,78,65,80,0,4,762112,0,82,95,81, +66,85,70,0,4,770304,0,82,95,83,82,67,0,4,771328,0, +82,95,83,69,81,0,4,773376,0,98,110,97,109,101,0,4, +773440,0,98,112,114,101,99,0,4,773504,0,98,115,101,113,0, +4,773568,0,98,108,101,110,0,4,773632,0,117,110,97,109,101, +0,4,773664,0,117,115,101,113,0,4,773696,0,117,108,101,110, +0,4,773728,0,97,110,97,109,101,0,4,773792,0,97,98,105, +110,0,4,773888,0,107,111,112,115,0,4,774400,0,82,95,67, +84,88,0,4,776448,0,82,95,76,79,79,80,0,4,777472,0, +82,95,65,83,77,0,4,0,0,84,95,69,79,70,0,4, +1,0,84,95,78,85,77,0,4,2,0,84,95,73,68,0, +4,3,0,84,95,83,84,82,0,4,4,0,84,95,79,80, +0,4,1,0,75,95,70,78,0,4,2,0,75,95,71,86, +65,82,0,4,3,0,75,95,71,65,82,82,0,4,4,0, +75,95,67,79,78,83,84,0,4,5,0,75,95,75,87,0, +4,6,0,75,95,69,78,72,0,4,7,0,75,95,84,89, +80,69,0,4,1,0,70,95,68,69,70,0,4,2,0,70, +95,83,73,71,0,4,4,0,70,95,85,78,84,89,80,69, +68,0,4,8,0,70,95,83,84,77,84,0,4,16,0,70, +95,69,88,80,82,0,4,32,0,70,95,84,79,80,0,4, +64,0,70,95,70,65,76,76,66,65,67,75,0,4,128,0, +70,95,71,69,78,0,4,256,0,70,95,80,82,69,0,4, +0,0,83,95,80,76,65,73,78,0,4,1,0,83,95,77, +0,4,2,0,83,95,85,0,4,3,0,83,95,68,0,4, +4,0,83,95,68,69,65,68,0,4,5,0,83,95,67,72, +73,76,68,0,4,6,0,83,95,66,79,82,82,79,87,0, +4,0,0,67,95,80,76,65,73,78,0,4,1,0,67,95, +70,82,69,83,72,0,4,2,0,67,95,86,65,82,0,4, +0,0,76,86,95,78,79,78,69,0,4,1,0,76,86,95, +76,79,67,65,76,0,4,2,0,76,86,95,71,76,79,66, +65,76,0,4,3,0,76,86,95,69,76,69,77,0,4,4, +0,76,86,95,79,84,72,69,82,0,4,0,0,79,95,81, +0,4,1,0,79,95,76,68,0,4,2,0,79,95,83,84, +0,4,3,0,79,95,80,73,67,75,0,4,4,0,79,95, +68,82,79,80,0,4,5,0,79,95,83,87,65,80,0,4, +6,0,79,95,67,65,76,76,0,4,7,0,79,95,74,77, +80,0,4,8,0,79,95,74,90,0,4,9,0,79,95,82, +69,84,0,4,13,0,79,95,65,68,68,0,4,14,0,79, +95,83,85,66,0,4,20,0,79,95,76,84,0,4,22,0, +88,79,80,48,0,4,-2,0,83,69,81,82,69,70,0,2, +792574,0,110,101,110,104,0,2,792575,0,112,111,111,108,116,111, +112,0,2,792576,0,110,115,121,109,0,2,792577,0,117,98,97, +115,101,0,2,792578,0,118,105,115,108,111,0,2,792579,0,110, +102,105,120,117,0,2,792580,0,102,105,120,102,114,101,101,0, +2,792581,0,110,98,105,110,0,2,792582,0,110,117,110,0,2, +792583,0,110,97,115,103,0,2,792584,0,110,107,111,112,0,2, +792585,0,115,101,113,116,111,112,0,2,792586,0,109,110,101,109, +0,2,792610,0,99,104,101,99,107,95,111,110,0,2,792611,0, +116,107,0,2,792612,0,116,118,0,2,792613,0,116,103,108,117, +101,0,2,792614,0,116,108,105,110,101,0,2,792615,0,108,105, +110,101,0,2,792616,0,99,104,0,2,792617,0,99,104,107,105, +110,100,0,2,792618,0,99,104,112,116,114,0,2,792619,0,99, +104,98,97,115,101,0,2,792620,0,113,100,111,110,101,0,2, +792621,0,105,115,112,0,3,792622,0,116,98,117,102,0,2,793722, +0,112,107,95,116,107,0,2,793723,0,112,107,95,116,118,0, +2,793724,0,112,113,95,110,0,3,793725,0,112,113,95,116,107, +0,3,793729,0,112,113,95,116,118,0,3,793733,0,112,113,95, +103,108,0,3,793737,0,112,113,95,108,110,0,2,793741,0,104, +101,114,101,0,2,793742,0,104,100,101,108,116,97,0,2,793743, +0,104,108,105,109,105,116,0,2,793744,0,112,116,111,112,0, +2,793745,0,110,108,111,99,0,2,793746,0,108,98,97,115,101, +0,2,793747,0,102,100,101,112,116,104,0,2,793748,0,102,115, +108,111,116,115,0,2,793749,0,102,99,104,107,0,2,793750,0, +102,114,101,116,0,2,793751,0,108,105,118,101,0,2,793752,0, +98,120,100,101,112,116,104,0,2,793753,0,101,112,105,99,104, +97,105,110,0,2,793754,0,102,102,114,97,109,101,0,2,793755, +0,105,110,102,110,0,2,793756,0,108,115,112,0,2,793757,0, +108,111,111,112,98,97,115,101,0,2,793758,0,99,116,120,115, +112,0,2,793759,0,115,110,97,112,116,111,112,0,2,793760,0, +108,118,0,2,793761,0,108,118,107,0,2,793762,0,108,118,105, +0,2,793763,0,107,119,95,101,120,112,114,0,2,793764,0,120, +99,108,115,0,2,793765,0,102,116,121,0,2,793766,0,115,95, +108,112,0,2,793767,0,115,95,114,112,0,2,793768,0,115,95, +108,98,0,2,793769,0,115,95,114,98,0,2,793770,0,115,95, +108,107,0,2,793771,0,115,95,114,107,0,2,793772,0,115,95, +99,111,109,109,97,0,2,793773,0,115,95,115,101,109,105,0, +2,793774,0,115,95,101,113,0,2,793775,0,115,95,97,114,114, +111,119,0,2,793776,0,115,95,105,110,99,0,2,793777,0,115, +95,100,101,99,0,2,793778,0,115,95,115,116,97,114,0,2, +793779,0,115,95,97,109,112,0,2,793780,0,115,95,99,111,108, +111,110,0,2,793781,0,115,95,104,97,115,104,0,2,793782,0, +115,95,109,105,110,117,115,0,2,793783,0,115,95,97,110,100, +97,110,100,0,2,793784,0,115,95,111,114,111,114,0,2,793785, +0,115,95,100,111,116,0,2,793786,0,115,95,102,110,0,2, +793787,0,115,95,118,97,114,0,2,793788,0,115,95,99,111,110, +115,116,0,2,793789,0,115,95,101,108,115,101,0,2,793790,0, +115,95,97,115,0,2,793791,0,115,95,117,110,99,104,101,99, +107,101,100,0,2,793792,0,115,95,105,109,109,101,100,105,97, +116,101,0,2,793793,0,115,95,109,0,2,793794,0,115,95,100, +0,2,793795,0,115,95,117,0,2,793796,0,115,95,113,0,2, +793797,0,115,95,99,97,115,101,0,2,793798,0,115,95,100,101, +102,97,117,108,116,0,2,793799,0,115,95,102,111,118,102,0, +2,793800,0,115,95,115,116,97,114,116,0,2,793801,0,115,95, +109,97,105,110,0,2,793802,0,115,95,102,114,101,101,0,2, +793803,0,115,95,111,119,110,0,2,793804,0,115,95,113,110,101, +119,0,2,793805,0,115,95,101,110,104,97,110,99,101,0,2, +793806,0,115,95,109,105,99,114,111,0,2,793807,0,115,95,118, +109,0,4,0,0,84,89,95,87,79,82,68,0,4,1,0, +84,89,95,73,78,84,0,4,2,0,84,89,95,67,72,65, +82,0,4,3,0,84,89,95,66,79,79,76,0,4,4,0, +84,89,95,86,79,73,68,0,4,5,0,84,89,95,80,84, +82,0,4,6,0,84,89,95,65,82,82,0,4,7,0,84, +89,95,70,78,0,4,8,0,84,89,95,83,84,82,85,67, +84,0,4,9,0,84,89,95,86,65,82,0,4,10,0,84, +89,95,71,69,78,0,4,11,0,84,89,95,68,89,78,0, +4,638976,0,82,95,84,89,0,4,655360,0,82,95,84,89,69, +78,68,0,4,1024,0,78,84,76,0,4,778496,0,108,116,121, +112,101,0,4,779008,0,108,116,97,103,0,4,779520,0,115,116, +121,112,101,0,2,793808,0,116,121,116,111,112,0,2,793809,0, +110,116,108,0,2,793810,0,116,119,111,114,100,0,2,793811,0, +116,105,110,116,0,2,793812,0,116,99,104,97,114,0,2,793813, +0,116,98,111,111,108,0,2,793814,0,116,118,111,105,100,0, +2,793815,0,116,99,95,111,110,0,2,793816,0,120,116,121,0, +2,793817,0,120,122,101,114,111,0,2,793818,0,100,114,121,0, +3,793819,0,116,112,110,0,2,793827,0,110,116,112,0,2,793828, +0,100,116,121,0,2,793829,0,100,116,97,103,0,2,793830,0, +115,95,108,116,0,2,793831,0,115,95,103,116,0,2,793832,0, +115,95,115,104,114,0,2,793833,0,115,95,108,101,0,2,793834, +0,115,95,103,101,0,2,793835,0,115,95,101,113,101,113,0, +2,793836,0,115,95,110,101,0,2,793837,0,115,95,112,108,117, +115,0,2,793838,0,115,95,115,116,114,117,99,116,0,2,793839, +0,115,95,116,121,112,101,0,2,793840,0,115,95,116,121,112, +101,99,104,101,99,107,0,2,793841,0,115,95,111,110,0,2, +793842,0,115,95,111,102,102,0,2,793843,0,115,95,110,101,119, +0,2,793844,0,115,95,110,101,119,95,116,0,2,793845,0,115, +95,111,51,0,2,793846,0,115,95,108,102,97,0,2,793847,0, +115,95,111,117,116,115,105,100,101,0,2,793848,0,115,95,110, +101,119,95,114,97,119,0,2,793849,0,115,95,111,111,98,0, +2,793850,0,111,51,111,110,0,2,793851,0,111,51,110,120,116, +0,2,793852,0,111,51,111,110,101,0,3,793853,0,111,51,115, +108,0,2,793861,0,110,111,51,115,108,0,3,793862,0,111,51, +112,101,110,100,0,2,794062,0,110,111,51,112,101,110,100,0, +3,794063,0,111,51,100,115,99,0,3,794079,0,110,117,109,111, +112,115,0,2,794091,0,110,110,117,109,111,112,115,0,2,794092, +0,115,95,102,105,120,101,100,0,2,794093,0,115,95,100,108, +101,110,0,2,794094,0,115,95,100,99,97,112,0,2,794095,0, +115,95,100,110,101,119,0,2,794096,0,115,95,100,97,116,0, +2,794097,0,115,95,100,112,117,115,104,0,2,794098,0,115,95, +100,112,111,112,0,2,794099,0,115,95,100,114,101,115,105,122, +101,0,2,794100,0,120,99,111,110,115,116,0,2,794101,0,120, +99,118,97,108,0,3,794102,0,103,98,105,110,100,0,2,794110, +0,110,103,98,105,110,100,0,2,794111,0,110,101,114,114,0, +2,794112,0,112,97,110,105,99,0,4,783616,0,82,95,69,88, +73,84,0,4,786176,0,82,95,69,88,73,84,69,78,68,0, +2,806917,0,101,120,116,111,112,0,3,806918,0,106,112,115,0, +2,806982,0,110,106,112,115,0,0 +}; +#define GLEN 78217 diff --git a/docs/history/15-o3/code/io_linux.c b/docs/history/15-o3/code/io_linux.c new file mode 100644 index 0000000..7ea34e0 --- /dev/null +++ b/docs/history/15-o3/code/io_linux.c @@ -0,0 +1,6 @@ +#include +int J; +static int pk(void){int c=getchar();ungetc(c,stdin);return c==EOF;} +char Q(void){int c=getchar();J=pk();return c;} +void G(char c){putchar(c);} +__attribute__((constructor)) static void init(void){J=pk();} diff --git a/docs/history/15-o3/code/mkgenome.c b/docs/history/15-o3/code/mkgenome.c new file mode 100644 index 0000000..8fb2cc5 --- /dev/null +++ b/docs/history/15-o3/code/mkgenome.c @@ -0,0 +1,16 @@ +/* turns a genome image (little-endian 32-bit words on stdin, as written by + tools/build.zc) into genome.h */ +#include +#define GORG 0xC0000 +int main(void) +{ + unsigned char b[4]; int n = 0; + printf("#define GORG %d\nstatic const int genome[] = {", GORG); + while (fread(b, 1, 4, stdin) == 4) { + int w = (int)(b[0] | b[1] << 8 | b[2] << 16 | (unsigned)b[3] << 24); + printf("%s%d", !n ? "\n" : n % 16 ? "," : ",\n", w); + n++; + } + printf("\n};\n#define GLEN %d\n", n); + return n ? 0 : 1; +} diff --git a/docs/history/15-o3/code/src/compiler.zc b/docs/history/15-o3/code/src/compiler.zc new file mode 100644 index 0000000..6e2388b --- /dev/null +++ b/docs/history/15-o3/code/src/compiler.zc @@ -0,0 +1,2832 @@ +// ============================================================================ +// compiler.zc: the resident zc compiler. it lives in the genome next to the +// runtime, compiles source straight to vm words in memory, and is visible to +// the programs it compiles: every function and global here can be called or +// patched by name, and parse words (immediate fns) run inside it. +// ============================================================================ + +// ---- compiler data region (fixed addresses, 0xA0100 .. 0xBE100) ---- +const IHN = 8192; +const R_IH = 0xA0100; // intern hash table +const R_POOL = 0xA2100; // interned strings: [symbol head] chars 0 +const R_POOLEND = 0xAC000; +const NS = 4096; // symbol table, one array per field +const R_SYM = 0xAC100; +const sname = 0xAC100, skind = 0xAD100, sval = 0xAE100, ssig = 0xAF100; +const sflag = 0xB0100, sfix = 0xB1100, snext = 0xB2100; +const NL = 512; // locals, one array per field +const lname = 0xB3100, lslot = 0xB3300, lhome = 0xB3500, lstate = 0xB3700; +const lowner = 0xB3900, lused = 0xB3B00, larr = 0xB3D00, lown = 0xB3F00; // lown: has owned an object +const NF = 8192; // patch nodes: a site and a link +const fsite = 0xB4100, fnext = 0xB6100; +const R_SNAP = 0xB8100; // analyzer snapshots +const R_QBUF = 0xBA100; // token buffer for quote and grab +const R_SRC = 0xBC100; // input source stack, 32 words per frame +const R_SEQ = 0xBC500; // operator code sequences +const bname = 0xBCD00, bprec = 0xBCD40, bseq = 0xBCD80, blen = 0xBCDC0; // binary operators +const uname = 0xBCE00, useq = 0xBCE20, ulen = 0xBCE40; // unary operators +const aname = 0xBCE60, abin = 0xBCEA0; // compound assignment +const kops = 0xBCF00; // every operator spelling the lexer knows +const R_CTX = 0xBD100; // saved compiler contexts for nested compiles +const R_LOOP = 0xBD900; // loop stack, 8 words per loop or switch +const R_ASM = 0xBDD00; // asm block labels + +// ---- token kinds, symbol kinds and flags ---- +const T_EOF = 0, T_NUM = 1, T_ID = 2, T_STR = 3, T_OP = 4; +const K_FN = 1, K_GVAR = 2, K_GARR = 3, K_CONST = 4, K_KW = 5, K_ENH = 6, K_TYPE = 7; +const F_DEF = 1, F_SIG = 2, F_UNTYPED = 4, F_STMT = 8, F_EXPR = 16, F_TOP = 32, F_FALLBACK = 64, F_GEN = 128, F_PRE = 256; +// variable states for the lifetime analyzer +const S_PLAIN = 0, S_M = 1, S_U = 2, S_D = 3, S_DEAD = 4, S_CHILD = 5, S_BORROW = 6; +// expression classes: plain word, a new object, or a bare variable (C_VAR + index) +const C_PLAIN = 0, C_FRESH = 1, C_VAR = 2; +// lvalue kinds +const LV_NONE = 0, LV_LOCAL = 1, LV_GLOBAL = 2, LV_ELEM = 3, LV_OTHER = 4; +// opcodes +const O_Q = 0, O_LD = 1, O_ST = 2, O_PICK = 3, O_DROP = 4, O_SWAP = 5, O_CALL = 6, O_JMP = 7; +const O_JZ = 8, O_RET = 9, O_ADD = 13, O_SUB = 14, O_LT = 20; +const XOP0 = 22; // first enhancement opcode +const SEQREF = -2; // in an operator sequence: the next word names a symbol + +// ---- state ---- +let nenh, pooltop, nsym, ubase, vislo, nfixu, fixfree; +let nbin, nun, nasg, nkop, seqtop; +let mnem = "q@!$_~cjzr,?.+-*&|^><%"; +let check_on = 1; // set to 0 to switch the analyzer off for what follows +let tk, tv, tglue, tline, line = 1, ch, chkind, chptr, chbase, qdone, isp = -1; +let tbuf[1100]; +let pk_tk, pk_tv, pq_n, pq_tk[4], pq_tv[4], pq_gl[4], pq_ln[4]; // up to 4 tokens of lookahead +let here, hdelta, hlimit, ptop; +let nloc, lbase, fdepth, fslots, fchk, fret, live, bxdepth, epichain, fframe, infn; +let lsp, loopbase, ctxsp, snaptop; +let lv, lvk, lvi, kw_expr, xcls, fty; +// interned spellings the parser compares against +let s_lp, s_rp, s_lb, s_rb, s_lk, s_rk, s_comma, s_semi, s_eq, s_arrow, s_inc, s_dec; +let s_star, s_amp, s_colon, s_hash, s_minus, s_andand, s_oror; +let s_dot, s_fn, s_var, s_const, s_else, s_as, s_unchecked, s_immediate, s_m, s_d, s_u; +let s_q, s_case, s_default, s_fovf, s_start, s_main, s_free, s_own, s_qnew, s_enhance, s_micro, s_vm; + +// ---- types: kinds, tables and parser state (see the types section) ---- +const TY_WORD = 0, TY_INT = 1, TY_CHAR = 2, TY_BOOL = 3, TY_VOID = 4, TY_PTR = 5, TY_ARR = 6; +const TY_FN = 7, TY_STRUCT = 8, TY_VAR = 9, TY_GEN = 10, TY_DYN = 11; // dyn: []T, a = element, n = 1 if fixed +const R_TY = 0x9C000, R_TYEND = 0xA0000, NTL = 1024; // the first NTL words list every record +const ltype = 0xBE100, ltag = 0xBE300, stype = 0xBE500; // type per local, declared tag per local, type per symbol +let tytop, ntl, tword, tint, tchar, tbool, tvoid; +let tc_on; // the optional typechecker, off unless a program says typecheck on; +let xty, xzero, dry; // type of the last expression, was it a literal 0, and dry runs (typeof) +let tpn[8], ntp; // type parameters in scope +let dty, dtag; // result of dtype() +let s_lt, s_gt, s_shr, s_le, s_ge, s_eqeq, s_ne, s_plus, s_struct, s_type, s_typecheck, s_on, s_off, s_new, s_new_t; +let s_o3, s_lfa, s_outside, s_new_raw, s_oob; +let o3on, o3nxt, o3one, o3sl[8], no3sl, o3pend[200], no3pend, o3dsc[16]; // o3: in an o3 region; the promoted slots; o3 blocks waiting for the function's end +let numops[12], nnumops, s_fixed, s_dlen, s_dcap, s_dnew, s_dat, s_dpush, s_dpop, s_dresize, xconst, xcval; +let gbind[8], ngbind; // explicit type arguments for the next call: f(...) + +// ---- errors ---- +// the compiler reports up to 20 errors before it stops. a syntax error +// (die) skips the token it happened on and sets panic, which skips ahead to +// the next ; or } (or the next top-level item) and keeps quiet until then. +// other errors (serr) just report: the parse is still in step. +let nerr, panic; +fn errat() { puts("zc: line "); putn(tline); puts(": "); nerr++; return 0; } +fn errend() { putc(10); if (nerr >= 20) { puts("zc: too many errors, stopping\n"); halt(); } return 0; } +fn giveup() { puts("zc: "); putn(nerr); if (nerr == 1) puts(" error\n"); else puts(" errors\n"); halt(); return 0; } +fn fatal(msg) { errat(); puts(msg); putc(10); halt(); return 0; } +fn serr(msg) { if (!panic && !dry) { errat(); puts(msg); errend(); } return 0; } +fn serrn(name, msg) { if (!panic && !dry) { errat(); puts(name); puts(": "); puts(msg); errend(); } return 0; } +fn die(msg) { serr(msg); panic = 1; if (tk == T_EOF) giveup(); next(); return 0; } +fn dien(name, msg) { serrn(name, msg); panic = 1; if (tk == T_EOF) giveup(); next(); return 0; } +// analyzer errors only count in checked, reachable code +fn cerr(msg) { if (fchk && live) serr(msg); return 0; } +fn cerrn(i, msg) { if (fchk && live) serrn(lname[i], msg); return 0; } + +// ---- interning ---- +fn hashs(s) { let h = 0; while (*s) { h = h * 31 + *s; s++; } return h & (IHN - 1); } +fn intern(s) +{ + let h = hashs(s), p, n; + while (1) { + p = R_IH[h]; + if (!p) break; + if (streq(p, s)) return p; + h = (h + 1) & (IHN - 1); + } + n = strlen(s); + if (pooltop + n + 2 >= R_POOLEND) fatal("string pool full"); + pooltop[0] = 0; + p = pooltop + 1; + wcopy(p, s, n + 1); + pooltop = pooltop + n + 2; + R_IH[h] = p; + return p; +} + +// ---- symbols ---- +// each interned name keeps the index (+1) of its newest symbol in p[-1], and +// each symbol links to the one it shadows, so lookup is one step. +fn addsym(name, kind, val) +{ + let i = nsym; + if (nsym >= NS) fatal("symbol table full"); + nsym++; + sname[i] = name; skind[i] = kind; sval[i] = val; ssig[i] = 0; sflag[i] = F_DEF; sfix[i] = 0; stype[i] = 0; + snext[i] = name[-1]; + name[-1] = i + 1; + return i; +} +// while an image builds, names below vislo are hidden, except keywords +fn lookup(name) +{ + let i = name[-1] - 1; + while (i >= 0) { + if (i >= vislo || skind[i] == K_KW || skind[i] == K_TYPE) return i; + i = snext[i] - 1; + } + return -1; +} +fn popsyms(to) { while (nsym > to) { nsym--; sname[nsym][-1] = snext[nsym]; } return 0; } +fn lookups(s) { return lookup(intern(s)); } + +// patch nodes +fn fixnew(site, link) +{ + let i = fixfree; + if (i) fixfree = fnext[i]; + else { nfixu++; i = nfixu; if (i >= NF) fatal("too many forward references"); } + fsite[i] = site; fnext[i] = link; + return i; +} +// patch every lit in a chain to point at v, and recycle the nodes +fn fixall(head, v) +{ + let n; + while (head) { setlit(fsite[head], v); n = fnext[head]; fnext[head] = fixfree; fixfree = head; head = n; } + return 0; +} + +// ---- code emission ---- +fn emit(w) { if (dry) return 0; if (here >= hlimit) fatal("program too big"); *(here + hdelta) = w; here++; return 0; } +fn lit(v) { emit(v + v + 1); return 0; } +fn op(k) { emit(k + k); return 0; } +fn setlit(at, v) { if (dry) return 0; *(at + hdelta) = v + v + 1; return 0; } +fn jmp(t) { lit(t); op(O_JMP); return 0; } +// a lit that will be patched later; returns its address +fn hole() { let a = here; lit(MW - 1); return a; } +fn fwdfn(name) { let s = addsym(name, K_FN, tline); sflag[s] = 0; return s; } // the line of the first use, until it's defined +fn fnref(s) +{ + if (sflag[s] & F_DEF) { lit(sval[s]); return 0; } + if (dry) return 0; + sfix[s] = fixnew(here, sfix[s]); + lit(MW - 1); // a jump to MW-1 halts if it ever runs unpatched + return 0; +} +fn callname(name) { let s = lookup(name); if (s < 0) s = fwdfn(name); fnref(s); op(O_CALL); return 0; } +fn laddr(k) { lit(ZFP); op(O_LD); if (k) { lit(k); op(O_ADD); } return 0; } +// an o3 variable's address: lfa(k) is fp + k as one enhancement, which the +// jit specializes on the literal k +fn vaddr(i) +{ + let s; + if (ltag[i] & 16) { s = lookup(s_lfa); if (s >= 0 && skind[s] == K_ENH) { lit(lslot[i]); op(XOP0 + sval[s]); return 0; } } + return laddr(lslot[i]); +} +// the slots an o3 region keeps in registers (the cpu takes 7) +fn addsl(k) +{ + let i = 0; + while (i < no3sl) { if (o3sl[i] == k) return 0; i++; } + if (no3sl < 7) { o3sl[no3sl] = k; no3sl++; } + return 0; +} +// hands a region to the cpu: install kind 2 is [-, 2, lo, hi, n, slots...] +fn o3region(lo, hi, sl, n) +{ + if (hdelta || lo >= hi) return 0; + o3dsc[0] = 0; o3dsc[1] = 2; o3dsc[2] = lo; o3dsc[3] = hi; o3dsc[4] = n; + wcopy(o3dsc + 5, sl, n); + install(o3dsc); + return 0; +} +// o3 blocks go to the cpu once their function is done, so every jump in them is final +fn o3flush() +{ + let i = 0, p; + while (i < no3pend) { p = o3pend + i * 10; o3region(p[0], p[1], p + 3, p[2]); i++; } + no3pend = 0; + return 0; +} +fn strlit(s) +{ + let j = hole(), a; + op(O_JMP); + a = here; + while (*s) { emit(*s); s++; } + emit(0); + setlit(j, here); + lit(a); + return 0; +} + +// ---- input sources ---- +// frame: 0 kind (0 stdin, 1 token list, 2 string) 1 object 2 index 3 end +// 4 transparent 5..7 saved token 8..10 saved char state 11 owned 12 saved line +// 13 saved token line 14 saved lookahead count 15..30 saved lookahead 31 saved char buffer +fn nextch() +{ + if (chkind == 0) { + if (qdone || eof()) { ch = -1; return 0; } + ch = getc() & 255; + if (ch == 96) { qdone = 1; ch = -1; return 0; } // a backtick ends the source + } else if (chkind == 3) { + ch = (chbase[chptr >> 2] >> ((chptr & 3) * 8)) & 255; + if (ch) chptr++; else ch = -1; + } else { + ch = *chptr & 255; + if (ch) chptr++; else ch = -1; + } + if (ch == 10) line++; + return 0; +} +fn src_push(kind, obj, trans, owned) +{ + let f, i; + isp++; + if (isp >= 32) fatal("sources nested too deeply"); + f = R_SRC + isp * 32; + f[0] = kind; f[1] = obj; f[2] = 0; f[3] = 0; f[4] = trans; f[11] = owned; + f[5] = tk; f[6] = tv; f[7] = tglue; f[8] = ch; f[9] = chkind; f[10] = chptr; + f[12] = line; f[13] = tline; f[14] = pq_n; f[31] = chbase; + i = 0; while (i < pq_n) { f[15 + i * 4] = pq_tk[i]; f[16 + i * 4] = pq_tv[i]; f[17 + i * 4] = pq_gl[i]; f[18 + i * 4] = pq_ln[i]; i++; } + pq_n = 0; + if (kind == 1) f[3] = obj[0]; + if (kind == 0) { chkind = 0; qdone = 0; line = 1; nextch(); } + if (kind == 2) { chkind = 2; chptr = obj; line = 1; nextch(); } + if (kind == 3) { chkind = 3; chbase = obj; chptr = 0; line = 1; nextch(); } + return 0; +} +fn src_pop() +{ + let f = R_SRC + isp * 32, i = 0; + tk = f[5]; tv = f[6]; tglue = f[7]; ch = f[8]; chkind = f[9]; chptr = f[10]; + line = f[12]; tline = f[13]; pq_n = f[14]; chbase = f[31]; + while (i < pq_n) { pq_tk[i] = f[15 + i * 4]; pq_tv[i] = f[16 + i * 4]; pq_gl[i] = f[17 + i * 4]; pq_ln[i] = f[18 + i * 4]; i++; } + isp--; + if (f[11]) free(f[1]); + return 0; +} +fn isopch(c) +{ + let s = "+-*/%=<>!&|^~?:.@#$"; + while (*s) { if (*s == c) return 1; s++; } + return 0; +} +// is buf[0..n) followed by c the start of some operator the lexer knows? +fn opprefix(n, c) +{ + let i = 0, k, j; + while (i < nkop) { + k = kops[i]; + j = 0; + while (j < n && k[j] == tbuf[j]) j++; + if (j == n && k[n] == c) return 1; + i++; + } + return 0; +} +fn isdig(c) { return c >= '0' && c <= '9'; } +fn isal(c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'; } +fn hexv(c) +{ + if (isdig(c)) return c - '0'; + if (c >= 'a' && c <= 'f') return c - 'a' + 10; + if (c >= 'A' && c <= 'F') return c - 'A' + 10; + return -1; +} +fn escch() +{ + let c = ch; + nextch(); + if (c != 92) return c; + c = ch; + nextch(); + if (c == 'n') return 10; + if (c == 't') return 9; + if (c == 'r') return 13; + if (c == '0') return 0; + if (c == 'e') return 27; + return c; +} +fn oplex(n) +{ + while (ch >= 0 && isopch(ch) && opprefix(n, ch)) { tbuf[n] = ch; n++; nextch(); } + tbuf[n] = 0; + tk = T_OP; tv = intern(tbuf); + return 0; +} +fn lex() +{ + let n = 0, v, dg; + tglue = 1; + while (1) { + while (ch == 32 || ch == 9 || ch == 10 || ch == 13) { nextch(); tglue = 0; } + if (ch != '/') break; + nextch(); + if (ch == '/') { while (ch >= 0 && ch != 10) nextch(); tglue = 0; continue; } + if (ch == '*') { + nextch(); + while (ch >= 0) { if (ch == '*') { nextch(); if (ch == '/') break; } else nextch(); } + nextch(); tglue = 0; continue; + } + tline = line; tbuf[0] = '/'; oplex(1); return 0; + } + tline = line; + if (ch < 0) { tk = T_EOF; tv = 0; return 0; } + if (isdig(ch)) { + v = 0; + if (ch == '0') { + nextch(); + if (ch == 'x' || ch == 'X') { + nextch(); + while ((dg = hexv(ch)) >= 0) { v = v * 16 + dg; nextch(); } + tk = T_NUM; tv = v; return 0; + } + } + while (isdig(ch)) { v = v * 10 + ch - '0'; nextch(); } + tk = T_NUM; tv = v; return 0; + } + if (isal(ch)) { + while (isal(ch) || isdig(ch)) { if (n < 1000) { tbuf[n] = ch; n++; } nextch(); } + tbuf[n] = 0; + tk = T_ID; tv = intern(tbuf); return 0; + } + if (ch == 39) { nextch(); tv = escch(); if (ch != 39) die("bad character literal"); nextch(); tk = T_NUM; return 0; } + if (ch == 34) { + nextch(); + while (ch >= 0 && ch != 34) { v = escch(); if (n < 1000) { tbuf[n] = v; n++; } } + nextch(); + tbuf[n] = 0; + tk = T_STR; tv = intern(tbuf); return 0; + } + tbuf[0] = ch; + nextch(); + if (isopch(tbuf[0])) return oplex(1); + tbuf[1] = 0; + tk = T_OP; tv = intern(tbuf); + return 0; +} +// one token of lookahead: peek() fills pk_*, next() takes it first +fn next() +{ + let i = 1; + if (pq_n) { + tk = pq_tk[0]; tv = pq_tv[0]; tglue = pq_gl[0]; tline = pq_ln[0]; + while (i < pq_n) { pq_tk[i - 1] = pq_tk[i]; pq_tv[i - 1] = pq_tv[i]; pq_gl[i - 1] = pq_gl[i]; pq_ln[i - 1] = pq_ln[i]; i++; } + pq_n--; + return 0; + } + return next0(); +} +// the k-th token after the current one (k from 0 to 3), into pk_tk and pk_tv +fn peekat(k) +{ + let a, b, c, e; + while (pq_n <= k) { + a = tk; b = tv; c = tglue; e = tline; + next0(); + pq_tk[pq_n] = tk; pq_tv[pq_n] = tv; pq_gl[pq_n] = tglue; pq_ln[pq_n] = tline; pq_n++; + tk = a; tv = b; tglue = c; tline = e; + } + pk_tk = pq_tk[k]; pk_tv = pq_tv[k]; + return 0; +} +fn peek() { return peekat(0); } +fn next0() +{ + let f, q, i; + f = R_SRC + isp * 32; + if (f[0] != 1) { lex(); return 0; } + i = f[2]; + if (i < f[3]) { q = f[1]; tk = q[1 + i * 3]; tv = q[2 + i * 3]; tline = q[3 + i * 3]; tglue = 0; f[2] = i + 1; return 0; } + if (!f[4]) { tk = T_EOF; tv = 0; return 0; } + src_pop(); // a fed list ran out: continue with the token that followed it + return 0; +} +fn is_op(s) { return tk == T_OP && tv == s; } +fn is_id(s) { return tk == T_ID && tv == s; } +fn accept(s) { if ((tk == T_OP || tk == T_ID) && tv == s) { next(); return 1; } return 0; } +fn expect(s) { if (!accept(s)) { if (!panic && !dry) { errat(); puts("expected "); puts(s); errend(); } panic = 1; if (tk == T_EOF) giveup(); } return 0; } +fn name() +{ + let n = tv; + if (tk != T_ID) die("expected a name"); + next(); + return n; +} + +// ---- operator tables ---- +// a sequence is written like asm: one-char mnemonics, numbers, and names +// (resolved when the operator is used, so image builds stay self-contained) +fn seqasm(s) +{ + let start = seqtop, n, neg, v, k, c; + while (1) { + while (*s == 32) s++; + if (!*s) break; + n = 0; + while (s[n] && s[n] != 32) { tbuf[n] = s[n]; n++; } + tbuf[n] = 0; + s = s + n; + neg = tbuf[0] == '-' && isdig(tbuf[1]); + if (isdig(tbuf[neg])) { + v = 0; k = neg; + while (tbuf[k]) { v = v * 10 + tbuf[k] - '0'; k++; } + if (neg) v = 0 - v; + R_SEQ[seqtop] = v + v + 1; seqtop++; + } else if (n == 1 && (c = mnemidx(tbuf[0])) >= 0) { + R_SEQ[seqtop] = c + c; seqtop++; + } else { + R_SEQ[seqtop] = SEQREF; R_SEQ[seqtop + 1] = intern(tbuf); seqtop = seqtop + 2; + } + } + return start; +} +fn mnemidx(c) { let i = 0; while (mnem[i]) { if (mnem[i] == c) return i; i++; } return -1; } +fn emitseq(a, n) +{ + let e = a + n, s; + while (a < e) { + if (R_SEQ[a] == SEQREF) { + s = lookup(R_SEQ[a + 1]); + if (s < 0) s = fwdfn(R_SEQ[a + 1]); + if (skind[s] == K_FN) fnref(s); else if (skind[s] == K_ENH) op(XOP0 + sval[s]); else lit(sval[s]); + a = a + 2; + } else { emit(R_SEQ[a]); a++; } + } + return 0; +} +fn addkop(p) { let i = 0; while (i < nkop) { if (kops[i] == p) return 0; i++; } kops[nkop] = p; nkop++; return 0; } +fn binfind(p) { let i = nbin; while (i) { i--; if (bname[i] == p) return i; } return -1; } +fn unfind(p) { let i = nun; while (i) { i--; if (uname[i] == p) return i; } return -1; } +fn asgfind(p) { let i = nasg; while (i) { i--; if (aname[i] == p) return i; } return -1; } +// add or replace a binary operator. code is a sequence string, or 0 for the +// built-in && (prec 2) and || (prec 1) behaviour, chosen by -1 / -2 in prec. +fn add_binop(nm, prec, code) +{ + let p = intern(nm), i = binfind(p), a, n; + if (i < 0) { i = nbin; nbin++; } + bname[i] = p; bprec[i] = prec; + if (prec < 0) { bprec[i] = 0 - prec; blen[i] = prec; bseq[i] = 0; addkop(p); return i; } + bseq[i] = seqasm(code); blen[i] = seqtop - bseq[i]; + addkop(p); + // and its compound assignment, x op= y + n = strlen(nm); + wcopy(tbuf + 1000, nm, n); + tbuf[1000 + n] = '='; tbuf[1001 + n] = 0; + a = intern(tbuf + 1000); + if (prec >= 8 || prec == 5 || prec == 4 || prec == 3) { + if (asgfind(a) < 0) { aname[nasg] = a; nasg++; } + abin[asgfind(a)] = i; + addkop(a); + } + return i; +} +fn add_unop(nm, code) +{ + let p = intern(nm), i = unfind(p); + if (i < 0) { i = nun; nun++; } + uname[i] = p; useq[i] = seqasm(code); ulen[i] = seqtop - useq[i]; + addkop(p); + return i; +} +fn add_kw(nm, handler, flags) { let s = addsym(intern(nm), K_KW, handler); sflag[s] = F_DEF | flags; return s; } + +// ---- locals and the analyzer's view of them ---- +fn findloc(n) { let i = nloc; while (i > lbase) { i--; if (lname[i] == n) return i; } return -1; } +fn newloc(n) +{ + let i = nloc; + if (nloc >= NL) fatal("too many locals"); + nloc++; + lname[i] = n; lslot[i] = fslots; lhome[i] = fdepth; lstate[i] = S_PLAIN; + lowner[i] = 0; lused[i] = 0; larr[i] = 0; lown[i] = 0; ltype[i] = tword; ltag[i] = 0; + return i; +} +fn lroot(i) { while (lstate[i] == S_CHILD) i = lowner[i]; return i; } +fn tracked(i) { return lstate[i] != S_PLAIN; } +// a use that doesn't give the object away +fn touch(i) +{ + let r; + lused[i] = 1; + if (!fchk) return 0; + r = lroot(i); + lused[r] = 1; + if (lstate[r] == S_DEAD) { cerrn(i, "used after it was given away"); return 0; } + // a d object that shows up in a deeper block is given to that block + if (lstate[r] == S_D && lhome[r] < fdepth) { lhome[r] = fdepth; lstate[r] = S_M; lown[r] = 1; } + return 0; +} +fn plainuse(c) +{ + if (c == C_FRESH) cerr("a new object can't be used as a plain value; bind it to a name first"); + if (c >= C_VAR) touch(c - C_VAR); + return C_PLAIN; +} +fn freevar(i) { laddr(lslot[i]); op(O_LD); callname(s_free); op(O_DROP); return 0; } +// leaving every block deeper than or equal to dep: free what is owned, and +// complain about what should have left already. final means the block really +// ends here rather than an early exit. +fn exit_vars(dep, final) +{ + let i = nloc, st; + if (!fchk || !live) return 0; + while (i > lbase) { + i--; + if (lhome[i] < dep) continue; + st = lstate[i]; + if (st == S_M) freevar(i); else if (st == S_U) cerrn(i, "u object must leave this block (block value, assignment to the enclosing block, or return)"); + else if (st == S_D) cerrn(i, "d object was never given away"); + } + return 0; +} +// an m object has to be used somewhere in the block that owns it, on any path +fn unused_check(from) +{ + while (from < nloc) { if (fchk && lown[from] && !lused[from]) serrn(lname[from], "m object is never used"); from++; } + return 0; +} +fn block_close(save) +{ + let i = lbase; + unused_check(save); + exit_vars(fdepth, 1); + while (i < save) { if (lhome[i] >= fdepth) { lstate[i] = S_DEAD; lhome[i] = fdepth - 1; } i++; } + nloc = save; + return 0; +} + +// snapshots: [lbase] [nloc] then state, owner, home per local +fn snap_push() +{ + let b = snaptop, i = lbase, p; + p = R_SNAP + b; + p[0] = lbase; p[1] = nloc; + p = p + 2; + while (i < nloc) { p[0] = lstate[i]; p[1] = lowner[i]; p[2] = lhome[i]; p = p + 3; i++; } + snaptop = p - R_SNAP; + if (snaptop >= 8000) fatal("nesting too deep for the analyzer"); + return b; +} +fn snap_pop(b) { snaptop = b; return 0; } +fn snap_restore(b) +{ + let p = R_SNAP + b, i = p[0], e = p[1]; + if (e > nloc) e = nloc; + p = p + 2; + while (i < e) { lstate[i] = p[0]; lowner[i] = p[1]; lhome[i] = p[2]; p = p + 3; i++; } + return 0; +} +fn empty(s) { return s == S_PLAIN || s == S_DEAD; } +// compare the current states with a snapshot: -1 if they agree, else the +// first local that differs. plain and dead agree and merge to dead. +fn snap_cmp(b) +{ + let p = R_SNAP + b, i = p[0], e = p[1], s; + if (e > nloc) e = nloc; + p = p + 2; + while (i < e) { + s = lstate[i]; + if (empty(s) && empty(p[0])) { if (p[0] == S_DEAD) lstate[i] = S_DEAD; } + else if (s != p[0] || lhome[i] != p[2] || (s == S_CHILD && lowner[i] != p[1])) return i; + p = p + 3; i++; + } + return -1; +} +fn statename(s) +{ + if (s == S_PLAIN) return "empty"; + if (s == S_M) return "m"; + if (s == S_U) return "u"; + if (s == S_D) return "d"; + if (s == S_DEAD) return "given away"; + if (s == S_CHILD) return "owned by a container"; + return "borrowed"; +} +fn merge_err(i, sa, what) +{ + if (!fchk || panic || dry) return 0; + errat(); puts(lname[i]); puts(": "); puts(what); + puts(" (one path leaves it "); puts(statename(sa)); puts(", another "); puts(statename(lstate[i])); puts(")"); + errend(); + return 0; +} +// after an if: the current state is the else path, sa is the then path + +// ---- joins ---- +// where control flow comes back together (after an if, a loop, a switch or +// ?:) every variable has to agree across the paths that reach the join. one +// exception: a variable that became d, or was given away, on some path is dead +// after the join, and the paths where it's still alive free it on the way in +// (fixups). u objects and borrows don't get this treatment: a u object still +// has to leave, and a borrow never changes. +const R_EXIT = 0xBF500, R_EXITEND = 0xBFF00; // exit records: the states at each break +let extop, jps[64], njps; // the paths being joined: [lbase, nloc, triples] +fn jst(p, i) { if (i >= p[1]) return S_PLAIN; return p[2 + 3 * (i - p[0])]; } +fn jow(p, i) { return p[3 + 3 * (i - p[0])]; } +fn jhm(p, i) { return p[4 + 3 * (i - p[0])]; } +// the states at a break, with the jump to patch: [site] then a snapshot +fn exit_rec(site) +{ + let p = R_EXIT + extop, i = lbase, q; + if (extop + 3 + 3 * (nloc - lbase) >= R_EXITEND - R_EXIT) fatal("too many breaks for the analyzer"); + p[0] = site; p[1] = lbase; p[2] = nloc; + q = p + 3; + while (i < nloc) { q[0] = lstate[i]; q[1] = lowner[i]; q[2] = lhome[i]; q = q + 3; i++; } + extop = q - R_EXIT; + return p; +} +fn jadd(p) { if (njps >= 64) fatal("too many paths for the analyzer"); jps[njps] = p; njps++; return 0; } +fn jsame(p, q, i) +{ + let s = jst(p, i), t = jst(q, i); + if (empty(s) && empty(t)) return 1; + return s == t && jhm(p, i) == jhm(q, i) && (s != S_CHILD || jow(p, i) == jow(q, i)); +} +// join the paths in jps over the locals [lbase, n0) into the current states +fn join(n0, what) +{ + let i = lbase, j, p, s, s0, dead, okdead, bad; + if (!njps) return 0; + while (i < n0) { + p = jps[0]; s0 = jst(p, i); + dead = 0; okdead = 1; bad = -1; + j = 0; + while (j < njps) { + s = jst(jps[j], i); + if (s == S_DEAD || s == S_D) dead = 1; + if (s == S_U || s == S_BORROW) okdead = 0; + if (!jsame(p, jps[j], i)) bad = j; + j++; + } + lstate[i] = s0; lowner[i] = jow(p, i); lhome[i] = jhm(p, i); + if (bad < 0) { + if (empty(s0)) { j = 0; while (j < njps) { if (jst(jps[j], i) == S_DEAD) lstate[i] = S_DEAD; j++; } } + } else if (dead && okdead) lstate[i] = S_DEAD; + else if (fchk && !panic && !dry) { + errat(); puts(lname[i]); puts(": "); puts(what); + puts(" (one path leaves it "); puts(statename(s0)); puts(", another "); puts(statename(jst(jps[bad], i))); puts(")"); + errend(); + } + i++; + } + return 0; +} +// on path p: count (and with doit, emit) the frees for what the join made dead but p still owns +fn fixups(p, n0, doit) +{ + let i = lbase, n = 0, s; + if (!fchk) return 0; + while (i < n0) { + s = jst(p, i); + if (lstate[i] == S_DEAD && (s == S_M || s == S_D)) { n++; if (doit) freevar(i); } + i++; + } + return n; +} +// a jump back to the top of a loop: whatever the loop owns has to be as it was +fn backedge(s0) +{ + let p = R_SNAP + s0, i = p[0], e = p[1], s; + if (!fchk || !live) return 0; + if (e > nloc) e = nloc; + while (i < e) { + s = lstate[i]; + if (empty(s) && empty(jst(p, i))) { if (jst(p, i) == S_DEAD) lstate[i] = S_DEAD; } + else if (s != jst(p, i) || lhome[i] != jhm(p, i) || (s == S_CHILD && lowner[i] != jow(p, i))) { + if (s == S_DEAD || s == S_D) cerrn(i, "is given away inside a loop that comes back around to it; give it away on a path that leaves the loop"); + else if (!panic && !dry) { + errat(); puts(lname[i]); puts(": changes state inside a loop; each pass has to leave it as it found it (it went from "); + puts(statename(jst(p, i))); puts(" to "); puts(statename(s)); puts(")"); errend(); + } + } + i++; + } + return 0; +} +// joins two paths that end at the same point, like the branches of if/else: +// path a jumped (through the hole at ja) and path b falls through to here +fn join2(sa, livea, ja, livebv, n0, what) +{ + let sb, nt, cj; + sb = snap_push(); + njps = 0; if (livea) jadd(R_SNAP + sa); if (livebv) jadd(R_SNAP + sb); + join(n0, what); + if (livebv) fixups(R_SNAP + sb, n0, 1); + nt = 0; if (livea) nt = fixups(R_SNAP + sa, n0, 0); + if (nt) { cj = hole(); op(O_JMP); setlit(ja, here); fixups(R_SNAP + sa, n0, 1); setlit(cj, here); } + else setlit(ja, here); + live = livea || livebv; + return 0; +} + +// ---- analyzer rules for the places values flow ---- +// recast: x as u / x as d / x as m +fn recast(c, t) +{ + let i, st; + if (!fchk) return c; + if (c < C_VAR) { cerr("only a named owned object can be recast"); return c; } + i = c - C_VAR; + st = lstate[i]; + lused[i] = 1; + if (st == S_CHILD) cerrn(i, "belongs to a container; recast the container instead"); + else if (st == S_BORROW) cerrn(i, "is borrowed and can't be recast"); + else if (st == S_PLAIN) cerrn(i, "isn't an owned object"); + else if (st == S_DEAD) cerrn(i, "used after it was given away"); + else if (t == S_M && st != S_M) cerrn(i, "can't be recast back to m"); + else lstate[i] = t; + return c; +} +// an argument for a parameter tagged t (0 plain, 1 m, 2 d) +fn argcheck(c, t) +{ + let i, st; + if (!fchk) return 0; + if (c == C_FRESH) { if (t != 2) cerr("a new object can only go straight to a d parameter; bind it to a name first"); return 0; } + if (c < C_VAR || !tracked(c - C_VAR)) return 0; + i = c - C_VAR; + st = lstate[i]; + if (lstate[lroot(i)] == S_DEAD) { cerrn(i, "used after it was given away"); return 0; } + if (t <= 1) { // untagged parameters are m: a lend + if (st == S_D) { cerrn(i, "is d; pass it to a d parameter (lending it would lose it)"); return 0; } + touch(i); + return 0; + } + if (st == S_D) { lstate[i] = S_DEAD; lused[i] = 1; return 0; } + if (st == S_M) cerrn(i, "is m; write d*x to give it away"); + else if (st == S_U) cerrn(i, "is u; write d*x to give it away instead of returning it"); + else if (st == S_CHILD) cerrn(i, "belongs to a container and can't be given away alone"); + else cerrn(i, "is borrowed and can't be given away"); + return 0; +} +fn emitown(p) +{ + laddr(lslot[p]); op(O_LD); + if (ltype[p][0] == TY_DYN) { lit(2); op(O_ADD); op(O_LD); } // a []T's elements live in its items object + callname(s_own); + return 0; +} +// a store of class rc into an lvalue of kind k (local or container index i) +fn assigncheck(k, i, rc) +{ + let st, j, r, js; + if (!fchk) return C_PLAIN; + j = -1; + if (rc >= C_VAR && tracked(rc - C_VAR)) j = rc - C_VAR; + if (j >= 0 && lstate[lroot(j)] == S_DEAD) { cerrn(j, "used after it was given away"); return C_PLAIN; } + if (k == LV_LOCAL) { + st = lstate[i]; + if (j < 0 && rc != C_FRESH) { + if (!empty(st)) cerrn(i, "still owns an object; overwriting it would leak it"); + else lstate[i] = S_PLAIN; + return C_PLAIN; + } + if (!empty(st)) { cerrn(i, "still owns an object; overwriting it would leak it"); return C_PLAIN; } + if (rc == C_FRESH) { lstate[i] = S_M; lused[i] = 0; lown[i] = 1; if (ltag[i] & 15) lstate[i] = ltag[i] & 15; return C_PLAIN; } + js = lstate[j]; + if (js == S_U && lhome[i] == lhome[j] - 1) { // handing a u object up one block + lstate[i] = S_M; lused[i] = 0; lown[i] = 1; lstate[j] = S_DEAD; lused[j] = 1; if (ltag[i] & 15) lstate[i] = ltag[i] & 15; + return C_PLAIN; + } + if (js == S_U) cerrn(j, "is u; it can only be handed to a variable of the enclosing block, or returned"); + else cerrn(j, "is an owned object and can't be copied into another variable"); + return C_PLAIN; + } + if (k == LV_ELEM) { + r = lroot(i); + st = lstate[r]; + touch(i); + if (rc == C_FRESH) { emitown(i); return C_PLAIN; } + if (j < 0) return C_PLAIN; + js = lstate[j]; + if (js == S_CHILD) cerrn(j, "already belongs to a container"); + else if (js == S_BORROW) cerrn(j, "is borrowed and can't be stored"); + else if (st == S_BORROW) { + if (js == S_U) cerrn(j, "is u; it has to leave through the block above, not into a borrowed object"); + else { lstate[j] = S_DEAD; lused[j] = 1; emitown(i); } // storing it gives it away + } else if (js != st) cerrn(j, "its tag doesn't match its container's (both have to be m, u or d)"); + else if (lhome[j] < lhome[r]) cerrn(j, "lives in an outer block than the container it's stored into"); + else { lstate[j] = S_CHILD; lowner[j] = i; lused[j] = 1; emitown(i); } + return C_PLAIN; + } + // a global, or memory the analyzer doesn't track: the collector takes it + if (j >= 0) { + if (lstate[j] == S_D) { lstate[j] = S_DEAD; lused[j] = 1; } + else cerrn(j, "must be d to be stored in a global or in untracked memory (the collector takes it over)"); + } + return C_PLAIN; +} +fn retcheck(c) +{ + let i; + if (!fchk) return 0; + i = -1; + if (c >= C_VAR && tracked(c - C_VAR)) i = c - C_VAR; + if (i >= 0 && lstate[lroot(i)] == S_DEAD) { cerrn(i, "used after it was given away"); return 0; } + if (fret) { + if (i >= 0) { + if (lstate[i] == S_U) { lstate[i] = S_DEAD; lused[i] = 1; } + else cerrn(i, "has to be u to be returned; write 'return u*x'"); + } + return 0; + } + if (c == C_FRESH) cerr("returns a new object; give the function a .u return type"); + else if (i >= 0) cerrn(i, "is owned and can't be returned from a function whose return type isn't .u"); + return 0; +} +// the last expression of a block expression +fn handup(c) +{ + let i; + if (!fchk) return C_PLAIN; + if (c == C_FRESH) return C_FRESH; + if (c < C_VAR || !tracked(c - C_VAR)) return C_PLAIN; + i = c - C_VAR; + if (lstate[i] == S_U && lhome[i] == fdepth) { lstate[i] = S_DEAD; lused[i] = 1; return C_FRESH; } + if (lstate[i] == S_U) cerrn(i, "only a u object of this block can be its value"); + else cerrn(i, "has to be u to be handed up as the block's value"); + return C_PLAIN; +} +fn discard(c) +{ + if (c == C_FRESH) cerr("a new object (or a u result) can't be thrown away"); + else if (c >= C_VAR) touch(c - C_VAR); + return 0; +} + +// ---- types ---- +// types are a compiler primitive: records in the type area, hash-consed so +// the same type is always the same pointer and comparing types is comparing +// words. untyped code is typed word, which goes with anything. the records +// stay in memory after compiling, so programs can inspect them. +// t[0] kind t[1] size in words t[2] name t[3] n t[4] a t[5] b t[6] c t[7..] extra +// ptr: a = element arr: a = element, n = length +// fn: a = return type, n = parameter count, b = signature (u m d tags), extra = parameter types +// struct: n = field count (-1 until laid out), a = fields (name, type, offset), +// b = generic template, c = type arguments [n, t1, t2 ...] +// var: a type parameter, n = its index +// gen: a generic struct: n = parameter count, a = fields (name, type), b = field count, c = parameter names + + +fn talloc(n) { let p = tytop; if (tytop + n >= R_TYEND) fatal("the type area is full"); tytop = tytop + n; wfill(p, 0, n); return p; } +fn tnew(kind, size, nm, extra) +{ + let t = talloc(7 + extra); + t[0] = kind; t[1] = size; t[2] = nm; + if (ntl >= NTL) fatal("too many types"); + R_TY[ntl] = t; ntl++; + return t; +} +fn tptr(e) +{ + let i = 0, t; + while (i < ntl) { t = R_TY[i]; if (t[0] == TY_PTR && t[4] == e) return t; i++; } + t = tnew(TY_PTR, 1, 0, 0); t[4] = e; + return t; +} +fn tarr(e, n) +{ + let i = 0, t; + while (i < ntl) { t = R_TY[i]; if (t[0] == TY_ARR && t[4] == e && t[3] == n) return t; i++; } + t = tnew(TY_ARR, n * tsz(e), 0, 0); t[4] = e; t[3] = n; + return t; +} +fn tfn(ret, np, ps, sig) +{ + let i = 0, j, t; + while (i < ntl) { + t = R_TY[i]; + if (t[0] == TY_FN && t[4] == ret && t[3] == np && t[5] == sig) { + j = 0; while (j < np && t[7 + j] == ps[j]) j++; + if (j == np) return t; + } + i++; + } + t = tnew(TY_FN, 1, 0, np); t[4] = ret; t[3] = np; t[5] = sig; + wcopy(t + 7, ps, np); + return t; +} +// []T, or fixed []T +fn tdyn(e, f) +{ + let i = 0, t; + while (i < ntl) { t = R_TY[i]; if (t[0] == TY_DYN && t[4] == e && t[3] == f) return t; i++; } + t = tnew(TY_DYN, 1, 0, 0); t[4] = e; t[3] = f; + return t; +} +fn tvar(i, nm) +{ + let k = 0, t; + while (k < ntl) { t = R_TY[k]; if (t[0] == TY_VAR && t[3] == i) return t; k++; } + t = tnew(TY_VAR, 1, nm, 0); t[3] = i; + return t; +} +fn tinst(g, args, n) +{ + let i = 0, j, t, v; + while (i < ntl) { + t = R_TY[i]; + if (t[0] == TY_STRUCT && t[5] == g) { + v = t[6]; j = 0; while (j < n && v[1 + j] == args[j]) j++; + if (j == n) return t; + } + i++; + } + v = talloc(n + 1); v[0] = n; wcopy(v + 1, args, n); + t = tnew(TY_STRUCT, 0, g[2], 0); t[3] = -1; t[5] = g; t[6] = v; + return t; +} +// lay out a generic instance on first use +fn tresolve(t) +{ + let g, f, n, i, fs, off = 0, ft, bs[8]; + if (t[0] != TY_STRUCT || t[3] >= 0) return t; + if (t[3] == -2) { serr("a struct can't contain itself by value"); t[3] = 0; return t; } + t[3] = -2; + g = t[5]; n = g[5]; f = g[4]; + wfill(bs, 0, 8); wcopy(bs, t[6] + 1, t[6][0]); + fs = talloc(n * 3 + 1); + i = 0; + while (i < n) { + ft = subst(f[i + i + 1], bs); + fs[i * 3] = f[i + i]; fs[i * 3 + 1] = ft; fs[i * 3 + 2] = off; + off = off + tsz(ft); + i++; + } + t[4] = fs; t[1] = off; t[3] = n; + return t; +} +fn tsz(t) { if (t[0] == TY_STRUCT && t[3] < 0) tresolve(t); return t[1]; } +// replace type parameters with what they were bound to (unbound ones become word) +fn subst(t, bs) +{ + let k = t[0], i, n, v[24], vec; + if (k == TY_VAR) { if (bs[t[3]]) return bs[t[3]]; return tword; } + if (k == TY_PTR) return tptr(subst(t[4], bs)); + if (k == TY_ARR) return tarr(subst(t[4], bs), t[3]); + if (k == TY_FN) { n = t[3]; i = 0; while (i < n) { v[i] = subst(t[7 + i], bs); i++; } return tfn(subst(t[4], bs), n, v, t[5]); } + if (k == TY_STRUCT && t[5]) { vec = t[6]; n = vec[0]; i = 0; while (i < n) { v[i] = subst(vec[1 + i], bs); i++; } return tinst(t[5], v, n); } + return t; +} +fn isnum(t) { return t[0] == TY_INT || t[0] == TY_CHAR || t[0] == TY_BOOL; } +fn isagg(t) { return t[0] == TY_ARR || t[0] == TY_STRUCT; } +fn isptr(t) { return t[0] == TY_PTR || t[0] == TY_ARR; } +fn decay(t) { if (t[0] == TY_ARR) return tptr(t[4]); return t; } +fn telem(t) { if (t[0] == TY_PTR || t[0] == TY_ARR) return t[4]; return tword; } +// can a value of type src go where dst is expected? +fn compat(dst, src) +{ + if (dst == src || dst == tword || src == tword) return 1; + if (isnum(dst) && isnum(src)) return 1; + src = decay(src); + if (dst[0] == TY_PTR && src[0] == TY_PTR) { + if (dst[4] == tword || src[4] == tword || dst[4] == tvoid || src[4] == tvoid) return 1; + return dst[4] == src[4]; + } + if ((dst[0] == TY_PTR || dst[0] == TY_FN || dst[0] == TY_DYN) && xzero) return 1; // a literal 0 is a null pointer + if (dst[0] == TY_DYN && src[0] == TY_DYN && dst[4] == src[4]) return dst[3] || !src[3]; // a growable array can't hold a fixed one + return dst == src; +} +fn unify(p, a, bs) +{ + let i, n, pv, av; + if (p[0] == TY_VAR) { i = p[3]; if (!bs[i]) { bs[i] = a; return 1; } return compat(bs[i], a); } + a = decay(a); + if (p[0] == TY_PTR && a[0] == TY_PTR) { if (a[4] == tword) return 1; return unify(p[4], a[4], bs); } + if (p[0] == TY_STRUCT && p[5] && a[0] == TY_STRUCT && a[5] == p[5]) { + pv = p[6]; av = a[6]; n = pv[0]; i = 0; + while (i < n) { if (!unify(pv[1 + i], av[1 + i], bs)) return 0; i++; } + return 1; + } + return compat(p, a); +} +fn tprint(t) +{ + let k = t[0], i, v; + if (k == TY_PTR) { putc('*'); return tprint(t[4]); } + if (k == TY_DYN) { if (t[3]) puts("fixed "); puts("[]"); return tprint(t[4]); } + if (k == TY_ARR) { putc('['); putn(t[3]); putc(']'); return tprint(t[4]); } + if (k == TY_FN) { + if (t[4] == tword) puts("fn"); else tprint(t[4]); + if (t[5] & 1) puts(".u"); + putc('('); + i = 0; + while (i < t[3]) { + if (i) puts(", "); + tprint(t[7 + i]); + if (i < 12 && tparamtag(t, i) == 1) puts(".m"); + if (i < 12 && tparamtag(t, i) == 2) puts(".d"); + i++; + } + putc(')'); + return 0; + } + if (t[2]) puts(t[2]); else puts("?"); + if (k == TY_STRUCT && t[5]) { + v = t[6]; putc('<'); + i = 0; while (i < v[0]) { if (i) puts(", "); tprint(v[1 + i]); i++; } + putc('>'); + } + return 0; +} +fn tcerr(what, want, got) +{ + if (!tc_on || !live || dry || panic) return 0; + errat(); puts("type error: "); puts(what); + puts(": expected "); tprint(want); puts(", got "); tprint(got); + errend(); + return 0; +} +fn tcneed(ok, what, got) +{ + if (ok || !tc_on || !live || dry || panic) return 0; + errat(); puts("type error: "); puts(what); puts(", got "); tprint(got); + errend(); + return 0; +} +fn tpfind(n) { let i = ntp; while (i) { i--; if (tpn[i] == n) return i; } return -1; } +fn istypename(n) +{ + let s; + if (n == s_fixed) return 1; + if (tpfind(n) >= 0) return 1; + if (findloc(n) >= 0) return 0; + s = lookup(n); + return s >= 0 && skind[s] == K_TYPE; +} +// the closing > of a type argument list; splits >> in two +fn closeangle() +{ + if (is_op(s_shr)) { tv = s_gt; return 0; } + expect(s_gt); + return 0; +} +// a type, then any number of (params) after it: int(int, int) is a function +// returning int, fn(int) one returning a plain word +fn ptype() +{ + let t = ptype0(), np, ps[24]; + while (is_op(s_lp)) { + next(); np = 0; + if (!is_op(s_rp)) { while (1) { if (np >= 24) fatal("too many parameters"); ps[np] = ptype(); np++; if (!accept(s_comma)) break; } } + expect(s_rp); + t = tfn(t, np, ps, np + np); + } + return t; +} +fn ptype0() +{ + let n, s, t, args[8], na = 0, i; + if (accept(s_star)) return tptr(ptype0()); + if (accept(s_lk)) { + if (accept(s_rk)) return tdyn(ptype0(), 0); // []T: grows + n = cexpr(); expect(s_rk); t = ptype0(); if (n < 0) serr("a negative array length"); return tarr(t, n); + } + if (is_id(s_fixed)) { // fixed []T can't grow; fixed [n]T says what [n]T already means + next(); t = ptype0(); + if (t[0] == TY_DYN) return tdyn(t[4], 1); + if (t[0] != TY_ARR) serr("only arrays can be fixed"); + return t; + } + if (is_id(s_fn)) { next(); if (!is_op(s_lp)) die("fn as a type needs its parameters: fn(int)"); return tword; } + if (tk != T_ID) { die("expected a type"); return tword; } + n = tv; + i = tpfind(n); + if (i >= 0) { next(); return tvar(i, n); } + s = lookup(n); + if (s < 0 || skind[s] != K_TYPE) { dien(n, "isn't a type"); return tword; } + next(); + t = sval[s]; + if (t[0] != TY_GEN) return t; + expect(s_lt); + while (1) { + if (na >= 8) die("too many type arguments"); + args[na] = ptype(); + if (tsz(args[na]) != 1) serr("type arguments have to be one word (a pointer, a number, or a parameter)"); + na++; + if (!accept(s_comma)) break; + } + closeangle(); + if (na != t[3]) serrn(n, "wrong number of type arguments"); + return tinst(t, args, na); +} +// does a declaration start here? (a type, or a bare .tag) +fn tstart() +{ + let k; + if (tk == T_OP) { + if (tv == s_lk || tv == s_dot) return 1; + if (tv == s_star) { // *T name, **T name ...: look past the stars + k = 0; + while (k < 3) { peekat(k); if (!(pk_tk == T_OP && pk_tv == s_star)) break; k++; } + peekat(k); + return (pk_tk == T_ID && (istypename(pk_tv) || pk_tv == s_fn)) || (pk_tk == T_OP && pk_tv == s_lk); + } + return 0; + } + if (tk != T_ID) return 0; + return tv == s_fn || istypename(tv); +} +// [type] [.tag] +fn dtype() +{ + dty = 0; dtag = 0; + if (!is_op(s_dot)) dty = ptype(); + if (accept(s_dot)) { + if (tv == s_m) dtag = S_M; else if (tv == s_u) dtag = S_U; else if (tv == s_d) dtag = S_D; else die("a tag is .u, .m or .d"); + next(); + } + return 0; +} +// struct name { type field, ... } or struct name { ... } +fn kw_struct() +{ + let n, t, s, nf = 0, buf = R_QBUF, i, fs, off = 0, gen = 0, ft; + next(); + n = name(); + if (is_op(s_lt)) { // generic + next(); ntp = 0; + while (1) { if (ntp >= 8) die("too many type parameters"); tpn[ntp] = name(); ntp++; if (!accept(s_comma)) break; } + closeangle(); + gen = 1; + t = tnew(TY_GEN, 0, n, 0); t[3] = ntp; + fs = talloc(ntp); wcopy(fs, tpn, ntp); t[6] = fs; + } else { t = tnew(TY_STRUCT, 0, n, 0); t[3] = -2; } // -2 while its fields are read + s = addsym(n, K_TYPE, t); + expect(s_lb); + while (!is_op(s_rb)) { + ft = ptype(); + while (1) { + if (nf >= 1000) fatal("too many fields"); + buf[nf + nf] = name(); buf[nf + nf + 1] = ft; nf++; + if (!accept(s_comma)) break; + } + expect(s_semi); + } + next(); + if (gen) { + fs = talloc(nf + nf); wcopy(fs, buf, nf + nf); + t[4] = fs; t[5] = nf; + ntp = 0; + } else { + fs = talloc(nf * 3 + 1); + i = 0; + while (i < nf) { + ft = buf[i + i + 1]; + if (ft == t) { serr("a struct can't contain itself by value; use a pointer"); ft = tword; } + fs[i * 3] = buf[i + i]; fs[i * 3 + 1] = ft; fs[i * 3 + 2] = off; + off = off + tsz(ft); + i++; + } + t[4] = fs; t[1] = off; t[3] = nf; + } + accept(s_semi); + return s; +} +// type name = T; +fn kw_type() { let n; next(); n = name(); expect(s_eq); addsym(n, K_TYPE, ptype()); expect(s_semi); return 0; } +// typecheck on; / typecheck off; +fn kw_typecheck() +{ + next(); + if (is_id(s_on)) tc_on = 1; else if (is_id(s_off)) tc_on = 0; else die("typecheck on or typecheck off"); + next(); expect(s_semi); + return 0; +} +// the field of a struct type, or -1 +fn tfindfield(t, n) { let i = 0; tresolve(t); while (i < t[3]) { if (t[4][i * 3] == n) return i; i++; } return -1; } +// sizeof(T or expr), typeof(T or expr): the expression form is compiled dry, so it never runs +fn typearg() +{ + let t, sb, c; + expect(s_lp); + if (tstart() && !is_op(s_dot)) t = ptype(); + else { + sb = snap_push(); dry++; + c = expr(); rval(); + t = xty; + dry--; snap_restore(sb); snap_pop(sb); + } + expect(s_rp); + return t; +} +// len(a), cap(a), push(a, x), pop(a), resize(a, n): growable arrays. +// len of a [n]T is just n. +fn arrarg() +{ + let c; + expect(s_lp); c = expr(); rval(); plainuse(c); + return xty; +} +fn nogrow(t) { if (t[0] == TY_DYN && t[3]) serr("a fixed array can't grow or shrink"); return 0; } +fn kw_len() +{ + let t; + next(); t = arrarg(); expect(s_rp); + if (t[0] == TY_ARR) { op(O_DROP); lit(t[3]); } + else { tcneed(t[0] == TY_DYN || t == tword, "len needs an array", t); if (o3on && t[0] == TY_DYN) op(O_LD); else callname(s_dlen); } + xty = tint; + return 0; +} +fn kw_cap() { let t; next(); t = arrarg(); expect(s_rp); tcneed(t[0] == TY_DYN || t == tword, "cap needs a []T", t); callname(s_dcap); xty = tint; return 0; } +fn kw_push() +{ + let t, c; + next(); t = arrarg(); nogrow(t); + if (t[0] == TY_DYN && tsz(t[4]) != 1) serr("push takes one-word elements; use resize for structs"); + expect(s_comma); c = expr(); rval(); argcheck(c, 2); + if (t[0] == TY_DYN && !compat(t[4], xty)) tcerr("push", t[4], xty); + expect(s_rp); + callname(s_dpush); xty = tint; + return 0; +} +fn kw_pop() { let t; next(); t = arrarg(); nogrow(t); expect(s_rp); callname(s_dpop); xty = telem(t); if (t[0] == TY_DYN) xty = t[4]; return 0; } +fn kw_resize() { let t, c; next(); t = arrarg(); nogrow(t); expect(s_comma); c = expr(); rval(); plainuse(c); expect(s_rp); callname(s_dresize); xty = tint; return 0; } +fn kw_sizeof() { let t; next(); t = typearg(); lit(tsz(t)); xty = tint; return 0; } +fn kw_typeof() { let t; next(); t = typearg(); lit(t); xty = tword; return 0; } +fn types_init() +{ + tytop = R_TY + NTL; ntl = 0; + tword = tnew(TY_WORD, 1, intern("word"), 0); + tint = tnew(TY_INT, 1, intern("int"), 0); + tchar = tnew(TY_CHAR, 1, intern("char"), 0); + tbool = tnew(TY_BOOL, 1, intern("bool"), 0); + tvoid = tnew(TY_VOID, 0, intern("void"), 0); + addsym(tword[2], K_TYPE, tword); addsym(tint[2], K_TYPE, tint); addsym(tchar[2], K_TYPE, tchar); + addsym(tbool[2], K_TYPE, tbool); addsym(tvoid[2], K_TYPE, tvoid); + s_lt = intern("<"); s_gt = intern(">"); s_shr = intern(">>"); s_le = intern("<="); s_ge = intern(">="); + s_eqeq = intern("=="); s_ne = intern("!="); s_plus = intern("+"); + s_struct = intern("struct"); s_type = intern("type"); s_typecheck = intern("typecheck"); + s_on = intern("on"); s_off = intern("off"); s_new = intern("new"); s_new_t = intern("new_t"); + s_o3 = intern("o3"); s_lfa = intern("lfa"); s_outside = intern("outside"); s_new_raw = intern("new_raw"); s_oob = intern("oob"); + s_fixed = intern("fixed"); s_dlen = intern("dlen"); s_dcap = intern("dcap"); s_dnew = intern("dnew"); s_dat = intern("dat"); + s_dpush = intern("dpush"); s_dpop = intern("dpop"); s_dresize = intern("dresize"); + numops[0] = intern("*"); numops[1] = intern("/"); numops[2] = intern("%"); numops[3] = intern("&"); + numops[4] = intern("|"); numops[5] = intern("^"); numops[6] = intern("<<"); numops[7] = s_shr; nnumops = 8; + return 0; +} +fn isnumop(o) { let i = 0; while (i < nnumops) { if (numops[i] == o) return 1; i++; } return 0; } +// the type of a binary operation, checking it when the typechecker is on +fn bintype(b, lt, rt) +{ + let o = bname[b]; + lt = decay(lt); rt = decay(rt); + if (blen[b] < 0) { tcneed(!isagg(lt) && !isagg(rt), "&& and || need scalars", lt); return tbool; } + if (o == s_eqeq || o == s_ne) { if (!compat(lt, rt) && !compat(rt, lt)) tcerr("comparison", lt, rt); return tbool; } + if (o == s_lt || o == s_gt || o == s_le || o == s_ge) { + if (!(lt == tword || rt == tword || (isnum(lt) && isnum(rt)) || (lt[0] == TY_PTR && rt[0] == TY_PTR))) tcerr("comparison", lt, rt); + return tbool; + } + if (o == s_plus) { + if (lt[0] == TY_PTR && (isnum(rt) || rt == tword)) return lt; + if (rt[0] == TY_PTR && isnum(lt)) return rt; + if (lt == tword || rt == tword) return tword; + if (isnum(lt) && isnum(rt)) return tint; + tcerr("+", lt, rt); return tword; + } + if (o == s_minus) { + if (lt[0] == TY_PTR && rt[0] == TY_PTR) return tint; + if (lt[0] == TY_PTR && (isnum(rt) || rt == tword)) return lt; + if (lt == tword || rt == tword) return tword; + if (isnum(lt) && isnum(rt)) return tint; + tcerr("-", lt, rt); return tword; + } + if (isnumop(o)) { + if (lt == tword || rt == tword) return tword; + if (isnum(lt) && isnum(rt)) return tint; + if (isnum(lt)) lt = rt; + tcneed(0, "arithmetic needs numbers", lt); return tword; + } + return tword; // operators a program added +} + +// ---- reflection ---- +// for programs (and parse words) looking at types at runtime +fn tkind(t) { return t[0]; } +fn tsize(t) { return tsz(t); } +fn tname(t) { return t[2]; } +fn tlen(t) { return t[3]; } +fn tnfields(t) { tresolve(t); return t[3]; } +fn tfname(t, i) { tresolve(t); return t[4][i * 3]; } +fn tftype(t, i) { tresolve(t); return t[4][i * 3 + 1]; } +fn tfoff(t, i) { tresolve(t); return t[4][i * 3 + 2]; } +fn tret(t) { return t[4]; } +fn tnparams(t) { return t[3]; } +fn tparam(t, i) { return t[7 + i]; } +fn tparamtag(t, i) { return ((t[5] >> 6) >> (i + i)) & 3; } // 0 untagged (m), 1 .m, 2 .d +fn tretu(t) { return t[5] & 1; } +fn tnargs(t) { if (t[0] != TY_STRUCT || !t[5]) return 0; return t[6][0]; } +fn targ(t, i) { return t[6][1 + i]; } +// the type of a function, by address. untyped functions get one made from their signature +fn sigof(f) +{ + let i = nsym, s = -1, ws[24], n, k; + while (i) { i--; if (skind[i] == K_FN && sval[i] == f && (sflag[i] & F_DEF)) { s = i; break; } } + if (s < 0) return 0; + if (stype[s]) return stype[s]; + n = (ssig[s] >> 1) & 31; k = 0; + while (k < n) { ws[k] = tword; k++; } + return tfn(tword, n, ws, ssig[s]); +} +fn sigofs(nm) { let s = lookups(nm); if (s < 0 || skind[s] != K_FN) return 0; return sigof(sval[s]); } + +// ---- expressions ---- +// every expression leaves its value on the stack, returns its analyzer class, +// and sets xty to its type. lv means the stack holds an address that hasn't +// been loaded yet; structs and arrays are never loaded, their address is +// their value. +fn rval() { if (lv) { if (!isagg(xty)) op(O_LD); lv = 0; } return 0; } +fn kwcall(s, e) +{ + let f = sval[s], old = kw_expr, c; + kw_expr = e; xcls = C_PLAIN; xty = tword; xzero = 0; + f(); + kw_expr = old; + c = xcls; + lv = 0; lvk = LV_NONE; + return c; +} +fn stmtonly() { if (kw_expr) serr("this can't be used inside an expression"); return 0; } +// the arguments of a call. ft is the callee's type (or 0), bs the generic bindings +fn args(tags, ft, bs) +{ + let np = 0, c, pt; + expect(s_lp); + if (!is_op(s_rp)) { + while (1) { + c = expr(); rval(); + argcheck(c, (tags >> (np + np)) & 3); + if (ft && np < ft[3]) { pt = ft[7 + np]; if (!unify(pt, xty, bs)) tcerr("argument", subst(pt, bs), xty); } + np++; + if (!accept(s_comma)) break; + } + } + expect(s_rp); + return np; +} +// new(T) and new(T, count): a typed object, its type stamped into the header +fn newtyped() +{ + let t, c; + t = ptype(); + if (t[0] == TY_DYN) { // a growable array: element size, length, type + lit(tsz(t[4])); + if (accept(s_comma)) { c = expr(); rval(); plainuse(c); } + else { lit(0); if (t[3]) serr("a fixed array needs its length: new(fixed []T, n)"); } + lit(t); + expect(s_rp); + callname(s_dnew); + lv = 0; lvk = LV_NONE; xty = t; xzero = 0; + return C_FRESH; + } + if (o3on) { // o3: new_raw(words), no stamp, no zeroing + if (accept(s_comma)) { c = expr(); rval(); plainuse(c); if (tsz(t) != 1) { lit(tsz(t)); op(15); } } + else lit(tsz(t)); + expect(s_rp); + callname(s_new_raw); + lv = 0; lvk = LV_NONE; xty = tptr(t); xzero = 0; + return C_FRESH; + } + if (accept(s_comma)) { + c = expr(); rval(); plainuse(c); + if (tsz(t) != 1) { lit(tsz(t)); op(15); } + lit(tarr(t, 0)); + } else { lit(tsz(t)); lit(t); } + expect(s_rp); + callname(s_new_t); + lv = 0; lvk = LV_NONE; xty = tptr(t); xzero = 0; + return C_FRESH; +} +fn call(s) +{ + let np, tags = 0, ret = 0, ft = stype[s], bs[8]; + if (sname[s] == s_new && skind[s] == K_FN) { + peek(); + if (pk_tk == T_OP && (pk_tv == s_star || pk_tv == s_lk)) { next(); return newtyped(); } + if (pk_tk == T_ID && (istypename(pk_tv) || pk_tv == s_fn)) { next(); return newtyped(); } + } + if (o3on && sname[s] == s_new) { tags = lookup(s_new_raw); if (tags >= 0) s = tags; tags = 0; } + if (sflag[s] & F_SIG) { tags = ssig[s] >> 6; ret = ssig[s] & 1; } + else sflag[s] = sflag[s] | F_UNTYPED; + wfill(bs, 0, 8); + if (ngbind) { wcopy(bs, gbind, ngbind); ngbind = 0; } + np = args(tags, ft, bs); + if ((sflag[s] & F_SIG) && np != ((ssig[s] >> 1) & 31)) serrn(sname[s], "wrong number of arguments"); + fnref(s); op(O_CALL); + lv = 0; lvk = LV_NONE; xzero = 0; + xty = tword; + if (ft) xty = subst(ft[4], bs); + if (ret) return C_FRESH; + return C_PLAIN; +} +// an enhancement used like a function: the arguments, then its one opcode +fn enhcall(s) +{ + let nin = ssig[s] & 15, nout = (ssig[s] >> 4) & 15, n = 0, c; + expect(s_lp); + if (!is_op(s_rp)) { while (1) { c = expr(); rval(); plainuse(c); n++; if (!accept(s_comma)) break; } } + expect(s_rp); + if (n != nin) serrn(sname[s], "wrong number of inputs"); + lv = 0; lvk = LV_NONE; xty = tword; xzero = 0; + if (sflag[s] & F_FALLBACK) { fnref(sfix[s] - 1); op(O_CALL); return C_PLAIN; } + op(XOP0 + sval[s]); + if (nout == 0) lit(0); + if (nout > 1) serrn(sname[s], "leaves several values; use it from asm"); + return C_PLAIN; +} +fn primary() +{ + let n, i, s, c; + lv = 0; lvk = LV_NONE; xzero = 0; + xconst = 0; + if (tk == T_NUM) { lit(tv); xzero = tv == 0; xconst = 1; xcval = tv; xty = tint; next(); return C_PLAIN; } + if (tk == T_STR) { strlit(tv); next(); xty = tptr(tchar); return C_PLAIN; } + if (tk == T_OP) { + if (tv == s_lp) { next(); c = expr(); expect(s_rp); return c; } + if (tv == s_lb) return blockexpr(); + die("expected an expression"); lit(0); xty = tword; return C_PLAIN; + } + if (tk != T_ID) { die("expected an expression"); lit(0); xty = tword; return C_PLAIN; } + n = tv; + i = findloc(n); + if (i >= 0) { + next(); + if (!fchk && tracked(i)) serrn(n, "is tracked by the analyzer and can't be used in unchecked code"); + vaddr(i); + xty = ltype[i]; + if (larr[i]) return C_PLAIN; + lv = 1; lvk = LV_LOCAL; lvi = i; + if (tracked(i)) return C_VAR + i; + return C_PLAIN; + } + s = lookup(n); + if (s >= 0 && skind[s] == K_KW) { + if (!(sflag[s] & F_EXPR)) dien(n, "can't be used inside an expression"); + return kwcall(s, 1); + } + if (s >= 0 && skind[s] == K_TYPE) { dien(n, "is a type, not a value"); lit(0); xty = tword; return C_PLAIN; } + next(); + xty = tword; + if (s >= 0 && skind[s] == K_CONST) { lit(sval[s]); xty = tint; xzero = sval[s] == 0; xconst = 1; xcval = sval[s]; return C_PLAIN; } + if (s >= 0 && skind[s] == K_GVAR) { lit(sval[s]); if (stype[s]) xty = stype[s]; lv = 1; lvk = LV_GLOBAL; return C_PLAIN; } + if (s >= 0 && skind[s] == K_GARR) { lit(sval[s]); if (stype[s]) xty = stype[s]; return C_PLAIN; } + if (s >= 0 && skind[s] == K_ENH) return enhcall(s); + if (s < 0) s = fwdfn(n); + if (is_op(s_lt) && (sflag[s] & F_GEN)) { // f(...) + next(); ngbind = 0; + while (1) { if (ngbind >= 8) die("too many type arguments"); gbind[ngbind] = ptype(); ngbind++; if (!accept(s_comma)) break; } + closeangle(); + if (!is_op(s_lp)) die("expected ( after the type arguments"); + } + if (is_op(s_lp)) return call(s); + fnref(s); + if (stype[s]) xty = stype[s]; + return C_PLAIN; +} +// ++ and -- on the address at the top of the stack +fn incdec(post, dlt) +{ + if (!lv) die("++ and -- need something to assign to"); + if (lvk == LV_LOCAL && fchk && tracked(lvi)) cerrn(lvi, "owns an object and can't be incremented"); + tcneed(isnum(xty) || xty[0] == TY_PTR || xty == tword, "++ and -- need a number or a pointer", xty); + lv = 0; lvk = LV_NONE; xzero = 0; + lit(0); op(O_PICK); op(O_LD); + if (post) { lit(0); op(O_PICK); lit(dlt); op(O_ADD); lit(2); op(O_PICK); op(O_ST); } + else { lit(dlt); op(O_ADD); lit(0); op(O_PICK); lit(2); op(O_PICK); op(O_ST); } + op(O_SWAP); op(O_DROP); + return C_PLAIN; +} +// p.x and p->x: a field of a struct, or of the struct a pointer points at +fn field(c) +{ + let n, st, i, b = -1; + next(); + n = name(); + rval(); + if (c == C_FRESH) cerr("use a field of a new object only after binding it to a name"); + else if (c >= C_VAR) { touch(c - C_VAR); b = c - C_VAR; } + st = xty; + if (st[0] == TY_PTR) st = st[4]; + if (st[0] != TY_STRUCT) { + if (!panic && !dry) { errat(); putc('.'); puts(n); puts(": that's a "); tprint(xty); puts(", not a struct or a pointer to one"); errend(); } + xty = tword; lv = 1; lvk = LV_OTHER; return C_PLAIN; + } + i = tfindfield(st, n); + if (i < 0) { + if (!panic && !dry) { errat(); tprint(st); puts(" has no field "); puts(n); errend(); } + xty = tword; lv = 1; lvk = LV_OTHER; return C_PLAIN; + } + if (st[4][i * 3 + 2]) { lit(st[4][i * 3 + 2]); op(O_ADD); } + xty = st[4][i * 3 + 1]; + lv = 1; xzero = 0; + if (b >= 0) { lvk = LV_ELEM; lvi = b; } else lvk = LV_OTHER; + return C_PLAIN; +} +fn postfix() +{ + let c = primary(), b, n, t, et, ft, bs[8], k; + while (1) { + if (tk == T_ID && tv == s_as) { // e as T: same bits, another type + next(); t = ptype(); rval(); xty = t; lvk = LV_NONE; + continue; + } + if (tk != T_OP) return c; + if (tv == s_lk) { + next(); + rval(); + b = -1; + if (c == C_FRESH) cerr("index a new object only after binding it to a name"); + else if (c >= C_VAR) { touch(c - C_VAR); b = c - C_VAR; } + if (xty[0] == TY_DYN) { // a growable array: bounds-checked, through the runtime + et = xty[4]; + c = expr(); rval(); plainuse(c); + tcneed(isnum(xty) || xty == tword, "an index has to be a number", xty); + expect(s_rk); + k = lookup(s_outside); + if (o3on && k >= 0 && skind[k] == K_ENH) { // h i: check i against h[0], then h[2] + i * size + lit(0); op(O_PICK); lit(2); op(O_PICK); op(O_LD); op(XOP0 + sval[k]); + k = hole(); op(O_JZ); + lit(1); op(O_PICK); op(O_LD); callname(s_oob); + setlit(k, here); + op(O_SWAP); lit(2); op(O_ADD); op(O_LD); op(O_SWAP); + if (tsz(et) != 1) { lit(tsz(et)); op(15); } + op(O_ADD); + } else callname(s_dat); + } else { + tcneed(isptr(xty) || xty == tword, "only pointers and arrays can be indexed", xty); + et = telem(xty); + n = 0 - 1; if (xty[0] == TY_ARR && xty[3] > 0) n = xty[3]; + xconst = 0; + c = expr(); rval(); plainuse(c); + if (n >= 0 && xconst && (xcval < 0 || xcval >= n)) serr("the index is out of range for this array"); + tcneed(isnum(xty) || xty == tword, "an index has to be a number", xty); + expect(s_rk); + if (tsz(et) != 1) { lit(tsz(et)); op(15); } + op(O_ADD); + } + lv = 1; xty = et; xzero = 0; + if (b >= 0) { lvk = LV_ELEM; lvi = b; } else lvk = LV_OTHER; + c = C_PLAIN; + continue; + } + if (tv == s_dot || tv == s_arrow) { c = field(c); continue; } + if (tv == s_lp) { // call through a value + rval(); plainuse(c); + ft = 0; + if (xty[0] == TY_FN) ft = xty; + else tcneed(xty == tword, "only functions can be called", xty); + wfill(bs, 0, 8); + n = args(0, ft, bs); + lit(n); op(O_PICK); op(O_CALL); op(O_SWAP); op(O_DROP); + lv = 0; lvk = LV_NONE; c = C_PLAIN; xzero = 0; + xty = tword; + if (ft) xty = subst(ft[4], bs); + continue; + } + if (tv == s_inc) { next(); c = incdec(1, 1); continue; } + if (tv == s_dec) { next(); c = incdec(1, -1); continue; } + return c; + } + return c; +} +fn unary() +{ + let c, un, t; + if (tk == T_ID && (tv == s_u || tv == s_m || tv == s_d)) { // u*x, d*x, m*x: recast + t = S_M; if (tv == s_u) t = S_U; if (tv == s_d) t = S_D; + next(); expect(s_star); + c = unary(); rval(); + lvk = LV_NONE; + return recast(c, t); + } + if (tk == T_OP) { + if (tv == s_star) { + next(); c = unary(); rval(); plainuse(c); + tcneed(xty[0] == TY_PTR || xty == tword, "only pointers can be dereferenced", xty); + xty = telem(xty); + lv = 1; lvk = LV_OTHER; xzero = 0; + return C_PLAIN; + } + if (tv == s_amp) { + next(); c = unary(); + if (!lv && !isagg(xty)) die("& needs something addressable"); + if (lv && lvk == LV_LOCAL && (ltag[lvi] & 16)) serr("an o3 variable has no address"); + if (c >= C_VAR) touch(c - C_VAR); + lv = 0; lvk = LV_NONE; xzero = 0; + xty = tptr(xty); + return C_PLAIN; + } + if (tv == s_inc) { next(); unary(); return incdec(0, 1); } + if (tv == s_dec) { next(); unary(); return incdec(0, -1); } + un = unfind(tv); + if (un >= 0) { + next(); c = unary(); rval(); plainuse(c); + emitseq(useq[un], ulen[un]); + xconst = 0; + if (un <= 1) xty = tbool; + else if (xty != tword) { tcneed(isnum(xty), "this operator needs a number", xty); xty = tint; } + lv = 0; lvk = LV_NONE; xzero = 0; + return C_PLAIN; + } + } + return postfix(); +} +fn bin(minp) +{ + let c = unary(), b, p, a, j, lt; + while (tk == T_OP) { + b = binfind(tv); + if (b < 0) return c; + p = bprec[b]; + if (p < minp) return c; + next(); rval(); plainuse(c); + lt = xty; + if (blen[b] == -1) { // && : 0 if the left side is 0, else the right side as 0 or 1 + a = hole(); op(O_JZ); + c = bin(p + 1); rval(); plainuse(c); + emitseq(useq[1], ulen[1]); // !! normalizes + j = hole(); op(O_JMP); + setlit(a, here); lit(0); setlit(j, here); + } else if (blen[b] == -2) { // || : 1 if the left side isn't 0, else the right side as 0 or 1 + a = hole(); op(O_JZ); + lit(1); j = hole(); op(O_JMP); + setlit(a, here); + c = bin(p + 1); rval(); plainuse(c); + emitseq(useq[1], ulen[1]); + setlit(j, here); + } else { + c = bin(p + 1); rval(); plainuse(c); + emitseq(bseq[b], blen[b]); + } + xty = bintype(b, lt, xty); + lv = 0; lvk = LV_NONE; c = C_PLAIN; xzero = 0; xconst = 0; + } + return c; +} +// [addr v] -> [v], storing v at addr +fn storekeep() { op(O_SWAP); lit(1); op(O_PICK); op(O_SWAP); op(O_ST); return 0; } +// c ? a : b +fn ternary(c) +{ + let a, b, s0, sa, livea, live0 = live, n0 = nloc, ca, cb, ta; + rval(); plainuse(c); + next(); + a = hole(); op(O_JZ); + s0 = snap_push(); + ca = expr(); rval(); ta = xty; + if (ca != C_FRESH) plainuse(ca); + livea = live; sa = snap_push(); + b = hole(); op(O_JMP); + expect(s_colon); + setlit(a, here); + snap_restore(s0); live = live0; + cb = expr(); rval(); + if (cb != C_FRESH) plainuse(cb); + join2(sa, livea, b, live, n0, "ends the two sides of ?: in different states"); + snap_pop(s0); + if (!compat(ta, xty) && !compat(xty, ta)) tcerr("the two sides of ?:", ta, xty); + if (ta != tword) xty = ta; + lv = 0; lvk = LV_NONE; xzero = 0; + if (ca == C_FRESH && cb == C_FRESH) return C_FRESH; + if (ca == C_FRESH || cb == C_FRESH) cerr("both sides of ?: have to be new objects, or neither"); + return C_PLAIN; +} +fn expr() +{ + let c = bin(1), k, i, a, rc, lt; + if (tk != T_OP) return c; + if (tv == s_q) return ternary(c); + if (tv == s_eq) { + if (!lv) die("this can't be assigned to"); + k = lvk; i = lvi; lt = xty; + if (isagg(lt)) serr("a whole struct or array can't be assigned; assign its fields"); + next(); + a = o3on && k == LV_LOCAL && (ltag[i] & 16) && !larr[i]; + if (a) op(O_DROP); // an o3 variable: its address comes after the value, so it never waits on the stack across a branch + rc = expr(); rval(); + if (!compat(lt, xty)) tcerr("assignment", lt, xty); + if (a) { lit(0); op(O_PICK); vaddr(i); op(O_ST); } else storekeep(); + lv = 0; lvk = LV_NONE; xty = lt; xzero = 0; + return assigncheck(k, i, rc); + } + a = asgfind(tv); + if (a >= 0) { + if (!lv) die("this can't be assigned to"); + if (lvk == LV_LOCAL && fchk && tracked(lvi)) cerrn(lvi, "owns an object; compound assignment would lose it"); + lt = xty; + next(); + i = o3on && lvk == LV_LOCAL && (ltag[lvi] & 16) && !larr[lvi]; + if (i) { i = lvi; op(O_DROP); vaddr(i); op(O_LD); i = i + 1; } + else { lit(0); op(O_PICK); op(O_LD); } + lv = 0; + rc = expr(); rval(); plainuse(rc); + emitseq(bseq[abin[a]], blen[abin[a]]); + k = bintype(abin[a], lt, xty); + if (!compat(lt, k)) tcerr("compound assignment", lt, k); + if (i) { lit(0); op(O_PICK); vaddr(i - 1); op(O_ST); } else storekeep(); + lv = 0; lvk = LV_NONE; xty = lt; xzero = 0; + return C_PLAIN; + } + return c; +} +// does the current token start a statement rather than an expression? +fn stmtstart() +{ + let s; + if (tk == T_OP) return tv == s_semi || tv == s_lb || tstart(); + if (tk != T_ID || findloc(tv) >= 0) return 0; + if (tstart()) return 1; + s = lookup(tv); + return s >= 0 && skind[s] == K_KW && !(sflag[s] & F_EXPR); +} +// { stmts; tail } as an expression: its value is the tail, or 0 +fn blockexpr() +{ + let save = nloc, c = C_PLAIN, tail = 0, t = tint; + next(); + fdepth++; bxdepth++; + while (!is_op(s_rb)) { + if (tk == T_EOF) die("missing }"); + if (stmtstart()) { stmt(); continue; } + c = expr(); rval(); + if (accept(s_semi)) { op(O_DROP); discard(c); c = C_PLAIN; continue; } + if (!is_op(s_rb)) die("expected ; or } after an expression"); + tail = 1; t = xty; + } + next(); + if (tail) c = handup(c); else lit(0); + block_close(save); + fdepth--; bxdepth--; + lv = 0; lvk = LV_NONE; xty = t; xzero = 0; + return c; +} + +// ---- constant expressions ---- +fn cprim() +{ + let v, s; + if (tk == T_NUM) { v = tv; next(); return v; } + if (accept(s_minus)) return 0 - cprim(); + if (tk == T_OP && tv == uname[2]) { next(); return ~cprim(); } + if (accept(s_lp)) { v = cexpr(); expect(s_rp); return v; } + if (tk == T_ID) { + s = lookup(tv); + if (s >= 0 && (skind[s] == K_CONST || skind[s] == K_GARR || (skind[s] == K_FN && (sflag[s] & F_DEF)) || skind[s] == K_GVAR)) { next(); return sval[s]; } + } + die("expected a constant"); + return 0; +} +fn cexpr() +{ + let v = cprim(), o; + while (tk == T_OP) { + o = tv; + if (o == bname[3]) { next(); v = v + cprim(); } + else if (o == bname[4]) { next(); v = v - cprim(); } + else if (o == bname[0]) { next(); v = v * cprim(); } + else if (o == bname[15]) { next(); v = v | cprim(); } + else if (o == bname[13]) { next(); v = v & cprim(); } + else if (o == bname[5]) { next(); v = v << cprim(); } + else if (o == bname[6]) { next(); v = v >> cprim(); } + else return v; + } + return v; +} + +// ---- statements ---- +// after a syntax error: skip to the end of the statement +fn resync() +{ + while (tk != T_EOF && !is_op(s_semi) && !is_op(s_rb)) next(); + if (is_op(s_semi)) next(); + panic = 0; + return 0; +} +fn stmt() +{ + let s, c; + if (panic) return resync(); + if (tk == T_ID && findloc(tv) < 0) { + s = lookup(tv); + if (s >= 0 && skind[s] == K_KW && (sflag[s] & F_STMT)) { kwcall(s, 0); return 0; } + } + if (tstart()) { localdecl(1); return 0; } + if (tk == T_OP) { + if (tv == s_semi) { next(); return 0; } + if (tv == s_lb) { block(); return 0; } + } + c = expr(); rval(); op(O_DROP); + expect(s_semi); + discard(c); + return 0; +} +fn block() +{ + let save = nloc; + expect(s_lb); + fdepth++; + while (!is_op(s_rb)) { if (tk == T_EOF) die("missing }"); stmt(); } + block_close(save); // before the }, so its line is the one reported + next(); + fdepth--; + return 0; +} +// the body of if/while/for: its own block, even without braces +fn substmt() +{ + let save = nloc; + if (is_op(s_lb)) return block(); + fdepth++; + stmt(); + block_close(save); + fdepth--; + return 0; +} +// let a = e, b[10]; or, typed: int a = 1, b; *node.m p = new(node); .u r; +// let infers pointer, struct and function types from the initializer and +// keeps numbers as plain words. +fn localdecl(typed) +{ + let n, i, c, k, t = 0, tg = 0; + if (typed) { dtype(); t = dty; tg = dtag; if (t == tvoid) serr("a variable can't be void"); } + if ((o3on || o3one) && !typed) serr("o3 variables need a type"); + while (1) { + n = name(); + i = newloc(n); + ltag[i] = tg; + if (o3on || o3one) ltag[i] = tg | 16; + if (accept(s_lk)) { + k = cexpr(); expect(s_rk); + if (!t) t = tword; + larr[i] = 1; ltype[i] = tarr(t, k); fslots = fslots + tsz(ltype[i]); + } else if (t && isagg(t)) { larr[i] = 1; ltype[i] = t; fslots = fslots + tsz(t); } + else { fslots++; if (t) ltype[i] = t; if (o3on) addsl(lslot[i]); } + if (accept(s_eq)) { + if (larr[i]) serr("an array or a struct can't be initialized with ="); + c = expr(); rval(); vaddr(i); op(O_ST); + if (t) { if (!compat(t, xty)) tcerr("initializer", t, xty); } + else if (!isnum(xty)) ltype[i] = xty; + assigncheck(LV_LOCAL, i, c); + } + if (!accept(s_comma)) break; + } + expect(s_semi); + return 0; +} +fn kw_var() { stmtonly(); next(); return localdecl(0); } +fn kw_if() +{ + let a, b, c, s0, sa, live0, livea, n0 = nloc, nt, cj; + stmtonly(); + next(); + expect(s_lp); c = expr(); rval(); plainuse(c); expect(s_rp); + a = hole(); op(O_JZ); + live0 = live; + s0 = snap_push(); + substmt(); + livea = live; sa = snap_push(); + if (is_id(s_else)) { + next(); + b = hole(); op(O_JMP); + setlit(a, here); + snap_restore(s0); live = live0; + substmt(); + join2(sa, livea, b, live, n0, "ends the branches of an if in different states"); + } else { + // the path that skips the then branch still has the states from before it + njps = 0; if (livea) jadd(R_SNAP + sa); if (live0) jadd(R_SNAP + s0); + join(n0, "ends the branches of an if in different states"); + if (livea) fixups(R_SNAP + sa, n0, 1); + nt = 0; if (live0) nt = fixups(R_SNAP + s0, n0, 0); + if (nt) { cj = hole(); op(O_JMP); setlit(a, here); fixups(R_SNAP + s0, n0, 1); setlit(cj, here); } + else setlit(a, here); + live = livea || live0; + } + snap_pop(s0); + return 0; +} +// loop frame: 0 continue target 1 break chain 2 depth 3 snapshot 4 block-expression depth +// loop frame: 0 continue target (-1 for a switch) 2 depth 3 snapshot at the top +// 4 block-expression depth 5 locals at the start 6 its first exit record +fn loop_push(cont, s0) +{ + let f = R_LOOP + lsp * 8; + if (lsp >= 60) fatal("loops nested too deeply"); + f[0] = cont; f[1] = 0; f[2] = fdepth; f[3] = s0; f[4] = bxdepth; f[5] = nloc; f[6] = extop; + lsp++; + return 0; +} +// the end of a loop or switch: join its exits (the fall-out path sc through +// the jump at a, if there is one, and every break) and lay out each path's +// fixups on its way to the end +fn loop_end(a, sc) +{ + let f, p, e, n0, outs = 0; + lsp--; + f = R_LOOP + lsp * 8; + n0 = f[5]; + njps = 0; + if (sc >= 0) jadd(R_SNAP + sc); + p = R_EXIT + f[6]; e = R_EXIT + extop; + while (p < e) { jadd(p + 1); p = p + 3 + 3 * (p[2] - p[1]); } + join(n0, "leaves the loop in different states on different paths"); + live = njps > 0; + p = R_EXIT + f[6]; + while (p < e) { + if (fixups(p + 1, n0, 0)) { setlit(p[0], here); fixups(p + 1, n0, 1); outs = fixnew(hole(), outs); op(O_JMP); } + else outs = fixnew(p[0], outs); + p = p + 3 + 3 * (p[2] - p[1]); + } + if (a >= 0) setlit(a, here); + if (sc >= 0) fixups(R_SNAP + sc, n0, 1); + fixall(outs, here); + extop = f[6]; + return 0; +} +fn kw_while() +{ + let c0 = here, a, s0, sc = -1, c, inf = 0; + stmtonly(); + next(); + s0 = snap_push(); + expect(s_lp); + if (tk == T_NUM && tv) { peek(); if (pk_tk == T_OP && pk_tv == s_rp) inf = 1; } // while (1): no way out but break + c = expr(); rval(); plainuse(c); expect(s_rp); + a = hole(); op(O_JZ); + if (!inf && live) sc = snap_push(); + loop_push(c0, s0); + substmt(); + backedge(s0); + jmp(c0); + loop_end(a, sc); + snap_pop(s0); + return 0; +} +fn kw_for() +{ + let save = nloc, s0, sc = -1, c0, a, b, st, c, inf = 0; + stmtonly(); + next(); + expect(s_lp); + fdepth++; + if (is_id(s_var)) kw_var(); + else if (tstart()) localdecl(1); + else { if (!is_op(s_semi)) { c = expr(); rval(); op(O_DROP); discard(c); } expect(s_semi); } + s0 = snap_push(); + c0 = here; + if (!is_op(s_semi)) { + if (tk == T_NUM && tv) { peek(); if (pk_tk == T_OP && pk_tv == s_semi) inf = 1; } + c = expr(); rval(); plainuse(c); + } else { lit(1); inf = 1; } + expect(s_semi); + a = hole(); op(O_JZ); + if (!inf && live) sc = snap_push(); + b = hole(); op(O_JMP); + st = here; + if (!is_op(s_rp)) { c = expr(); rval(); op(O_DROP); discard(c); } + expect(s_rp); + jmp(c0); + setlit(b, here); + loop_push(st, s0); + substmt(); + backedge(s0); + jmp(st); + loop_end(a, sc); + snap_pop(s0); + block_close(save); + fdepth--; + return 0; +} +fn kw_break() +{ + let f; + stmtonly(); + next(); expect(s_semi); + if (lsp <= loopbase) { serr("break outside a loop or a switch"); return 0; } + f = R_LOOP + (lsp - 1) * 8; + if (f[4] != bxdepth) { serr("break can't leave a block expression"); return 0; } + exit_vars(f[2] + 1, 0); + if (live) exit_rec(hole()); else hole(); + op(O_JMP); + live = 0; + return 0; +} +fn kw_continue() +{ + let j = lsp - 1, f; + stmtonly(); + next(); expect(s_semi); + while (j >= loopbase && R_LOOP[j * 8] == -1) j--; // past any switch + if (j < loopbase) { serr("continue outside a loop"); return 0; } + f = R_LOOP + j * 8; + if (f[4] != bxdepth) { serr("continue can't leave a block expression"); return 0; } + backedge(f[3]); + exit_vars(f[2] + 1, 0); + jmp(f[0]); + live = 0; + return 0; +} +// switch (e) { case 1, 2: ... case 'x': ... default: ... } +// cases don't fall through: each one ends where the next begins, and break +// leaves the switch early. case labels are constants. +fn casebody(s0, live0) +{ + let save = nloc; + snap_restore(s0); live = live0; + fdepth++; + while (!is_op(s_rb) && !is_id(s_case) && !is_id(s_default)) { if (tk == T_EOF) { die("missing }"); break; } stmt(); } + block_close(save); + fdepth--; + if (live) { exit_rec(hole()); op(O_JMP); } // the end of a case leaves the switch + live = 0; + return 0; +} +fn kw_switch() +{ + let save = nloc, tmp, c, s0, live0, defent = -1, skip, nxt, bj, v, a = -1; + stmtonly(); + next(); + expect(s_lp); c = expr(); rval(); plainuse(c); expect(s_rp); + fdepth++; + tmp = newloc(0); fslots++; // a hidden local holds the value + laddr(lslot[tmp]); op(O_ST); + live0 = live; + s0 = snap_push(); + loop_push(-1, s0); + expect(s_lb); + while (!is_op(s_rb)) { + if (tk == T_EOF) { die("missing }"); break; } + if (is_id(s_case)) { + next(); + bj = 0; + while (1) { + v = cexpr(); + laddr(lslot[tmp]); op(O_LD); lit(v); op(O_SUB); + bj = fixnew(hole(), bj); op(O_JZ); + if (!accept(s_comma)) break; + } + expect(s_colon); + nxt = hole(); op(O_JMP); + fixall(bj, here); + casebody(s0, live0); + setlit(nxt, here); + } else if (is_id(s_default)) { + next(); expect(s_colon); + if (defent >= 0) serr("a switch has one default"); + skip = hole(); op(O_JMP); // only reached when nothing matched + defent = here; + casebody(s0, live0); + setlit(skip, here); + } else die("expected case or default"); + } + next(); + snap_restore(s0); live = live0; + if (defent >= 0) { jmp(defent); loop_end(-1, -1); } + else { a = hole(); op(O_JMP); loop_end(a, s0); } // nothing matched: out with the states from the top + snap_pop(s0); + block_close(save); + fdepth--; + return 0; +} +fn kw_return() +{ + let c; + stmtonly(); + next(); + if (!infn) serr("return outside a function"); + if (bxdepth) serr("return can't leave a block expression"); + if (is_op(s_semi)) { lit(0); c = C_PLAIN; } + else { + c = expr(); rval(); + if (fty == tvoid) tcerr("return value", tvoid, xty); + else if (fty && !compat(fty, xty)) tcerr("return value", fty, xty); + } + expect(s_semi); + retcheck(c); + exit_vars(1, 0); + epichain = fixnew(here, epichain); + lit(MW - 1); op(O_JMP); + live = 0; + return 0; +} +// o3 { ... }: an optimized region. o3 T x = e;: one o3 variable +fn kw_o3() +{ + let lo, i, p; + stmtonly(); + next(); + if (!is_op(s_lb)) { o3one = 1; localdecl(1); o3one = 0; return 0; } + if (o3on || !infn) return block(); + lo = here + 2; lit(lo); op(O_JMP); + o3on = 1; no3sl = 0; + i = lbase; + while (i < nloc) { if ((ltag[i] & 16) && !larr[i]) addsl(lslot[i]); i++; } // o3 variables declared outside + block(); + o3on = 0; + if (no3pend < 20) { p = o3pend + no3pend * 10; p[0] = lo; p[1] = here; p[2] = no3sl; wcopy(p + 3, o3sl, no3sl); no3pend++; } + return 0; +} +fn kw_unchecked() +{ + let old = fchk; + stmtonly(); + next(); + fchk = 0; + substmt(); + fchk = old; + return 0; +} + +// ---- asm blocks: raw vm words, one per token ---- +// numbers push, -n pushes a negative number, #n emits a raw word, one-char +// tokens from the mnemonic table are opcodes, names push the address of a +// variable or function, :name defines a label for this block. +fn asmblock() +{ + let nl = 0, nr = 0, i, k, c, s, ln = R_ASM, la = R_ASM + 64, rn = R_ASM + 128, ra = R_ASM + 384; + expect(s_lb); + while (!is_op(s_rb)) { + if (tk == T_EOF) die("unterminated asm block"); + if (tk == T_NUM) { lit(tv); next(); continue; } + if (tk == T_STR) { strlit(tv); next(); continue; } + if (is_op(s_hash)) { next(); if (tk != T_NUM) die("# needs a number"); emit(tv); next(); continue; } + if (is_op(s_minus)) { next(); if (tk == T_NUM && tglue) { lit(0 - tv); next(); } else op(O_SUB); continue; } + if (is_op(s_colon)) { next(); if (nl >= 64) fatal("too many asm labels"); ln[nl] = name(); la[nl] = here; nl++; continue; } + if (tv[0] && !tv[1] && (c = mnemidx(tv[0])) >= 0) { op(c); next(); continue; } + if (tk != T_ID) die("bad token in asm"); + i = findloc(tv); + if (i >= 0 && (ltag[i] & 16) && !larr[i]) serr("asm can't reach an o3 variable"); + if (i >= 0) { lused[i] = 1; laddr(lslot[i]); next(); continue; } + k = -1; i = 0; + while (i < nl) { if (ln[i] == tv) k = i; i++; } + if (k >= 0) { lit(la[k]); next(); continue; } + s = lookup(tv); + if (s >= 0 && skind[s] == K_ENH) { op(XOP0 + sval[s]); next(); continue; } + if (s >= 0 && skind[s] != K_FN && skind[s] != K_KW) { lit(sval[s]); next(); continue; } + if (s >= 0 && skind[s] == K_FN) { fnref(s); next(); continue; } + if (nr >= 250) fatal("too many asm label references"); + rn[nr] = tv; ra[nr] = hole(); nr++; next(); // a label further down this block + } + next(); + i = 0; + while (i < nr) { + k = -1; c = 0; + while (c < nl) { if (ln[c] == rn[i]) k = c; c++; } + if (k < 0) serrn(rn[i], "unknown name in asm"); + setlit(ra[i], la[k]); + i++; + } + return 0; +} +fn kw_asm() { next(); asmblock(); xcls = C_PLAIN; return 0; } + +// ---- quote: code as data ---- +// a token list is an object: [n] then kind, value, line for each token. names, +// strings and operators are interned pointers. +.u grab() +{ + let n = 0, depth = 0, q; + if (!is_op(s_lb)) die("expected {"); + next(); + while (1) { + if (tk == T_EOF) die("unterminated quote"); + if (is_op(s_lb)) depth++; + if (is_op(s_rb)) { if (!depth) break; depth--; } + if (n >= 2700) fatal("quote too long"); + R_QBUF[n * 3] = tk; R_QBUF[n * 3 + 1] = tv; R_QBUF[n * 3 + 2] = tline; n++; + next(); + } + next(); + q = new(n * 3 + 1); + q[0] = n; + wcopy(q + 1, R_QBUF, n * 3); + return u*q; +} +unchecked .u qnew(src) { let n = src[0] * 3 + 1, q = new(n); wcopy(q, src, n); return q; } +.u qdup(.m q) { let n = q[0] * 3 + 1, r = new(n); wcopy(r, q, n); return u*r; } +fn wcopyto(.m q, n) { let i = 0; while (i < n) { emit(q[i]); i++; } return 0; } +fn kw_quote() +{ + let q, j, a, n; + next(); + q = grab(); + j = hole(); op(O_JMP); + a = here; + n = q[0] * 3 + 1; + wcopyto(q, n); + free(d*q); + setlit(j, here); + lit(a); + callname(s_qnew); + xcls = C_FRESH; + return 0; +} +// tokens of a string, as a token list +.u tokens(s) +{ + let n = 0, q; + src_push(2, s, 0, 0); + next(); + while (tk != T_EOF) { + if (n >= 2700) fatal("too many tokens"); + R_QBUF[n * 3] = tk; R_QBUF[n * 3 + 1] = tv; R_QBUF[n * 3 + 2] = tline; n++; + next(); + } + src_pop(); + q = new(n * 3 + 1); + q[0] = n; + wcopy(q + 1, R_QBUF, n * 3); + return u*q; +} +// feed a token list into the input: the parser reads it next, then carries +// on where it was. the list is freed once it has been read. +unchecked fn feed(.d q) { src_push(1, q, 1, 1); next(); return 0; } + +// ---- reading a unit and scanning it for signatures ---- +// a unit from stdin is read whole first (4 chars to a word), then scanned +// once for function signatures, so a function's tags are known before its first +// call even when it's defined further down. +unchecked fn readsrc() +{ + let cap = 4090, b = new(4090), n = 0, c, nb, w; + while (!eof()) { + c = getc() & 255; + if (c == 96) break; + if ((n >> 2) + 2 >= cap) { nb = new(cap + cap + 6); wcopy(nb, b, cap); free(b); b = nb; cap = cap + cap + 6; } + w = n >> 2; + if (!(n & 3)) b[w] = 0; + b[w] = b[w] | (c << ((n & 3) * 8)); + n++; + } + b[n >> 2] = b[n >> 2] & (pow2[(n & 3) * 8] - 1); // the 0 at the end + if ((n & 3) == 0) b[n >> 2] = 0; + b[(n >> 2) + 1] = 0; + return b; +} +// skip a <...> group (type parameters) +fn pskipangle() +{ + let dp = 0; + while (tk != T_EOF) { + if (is_op(s_lt)) dp++; + else if (is_op(s_gt)) dp--; + else if (is_op(s_shr)) dp = dp - 2; + next(); + if (dp <= 0) return 0; + } + return 0; +} +// at the name after fn: read the signature's tags and record them +fn prescan_fn(ret) +{ + let n = tv, np = 0, tags = 0, t = 0, dp, s; + next(); + next(); // the ( + if (!is_op(s_rp)) { + np = 1; dp = 1; + while (tk != T_EOF) { + if (is_op(s_lp)) dp++; + else if (is_op(s_rp)) { dp--; if (!dp) break; } + else if (dp == 1 && is_op(s_comma)) { if (np <= 12) tags = tags + (t << (np + np - 2)); np++; t = 0; } + else if (dp == 1 && is_op(s_dot)) { next(); if (is_id(s_m)) t = 1; else if (is_id(s_d)) t = 2; continue; } + next(); + } + if (np <= 12) tags = tags + (t << (np + np - 2)); + } + next(); + if (!is_op(s_lb) && !is_op(s_semi)) return 0; // not a function: an enhancement, a call in a parse word... + s = lookup(n); + if (s < 0 || s < ubase || skind[s] != K_FN || (sflag[s] & F_DEF)) s = fwdfn(n); + if (!(sflag[s] & F_SIG)) { ssig[s] = ret + np + np + tags * 64; sflag[s] = sflag[s] | F_SIG | F_PRE; } + return 0; +} +fn prescan(kind, obj) +{ + let depth = 0, sp = panic, sn = nerr, pend = 0; + dry++; + src_push(kind, obj, 0, 0); + next(); + while (tk != T_EOF) { + if (is_op(s_lb)) depth++; + else if (is_op(s_rb)) { depth--; pend = 0; } + else if (depth <= 0) { + if (is_op(s_semi)) pend = 0; + else if (is_op(s_dot)) { peek(); if (pk_tk == T_ID && pk_tv == s_u) pend = 1; } + else if (tk == T_ID) { peek(); if (pk_tk == T_OP && pk_tv == s_lp) { prescan_fn(pend); pend = 0; continue; } } + } + next(); + } + src_pop(); + dry--; panic = sp; nerr = sn; + return 0; +} +// compile a whole unit from stdin +fn unit_stdin() +{ + let src = readsrc(); + prescan(3, src); + src_push(3, src, 0, 0); + next(); + unit_top(); + unit_end(ubase); + src_pop(); + free(src); + return 0; +} + +// ---- nested compiles ---- +// compile() and load() can run while another function is being compiled (from +// a parse word). the function context is saved, the new code is emitted in +// place with a jump around it, and then everything is put back. +fn ctx_push() +{ + let f = R_CTX + ctxsp * 24; + if (ctxsp >= 30) fatal("compiles nested too deeply"); + ctxsp++; + f[0] = nloc; f[1] = lbase; f[2] = fdepth; f[3] = fslots; f[4] = fchk; f[5] = fret; f[6] = live; + f[7] = bxdepth; f[8] = epichain; f[9] = fframe; f[10] = infn; f[11] = lsp; f[12] = loopbase; + f[13] = lv; f[14] = lvk; f[15] = lvi; f[16] = kw_expr; f[17] = xcls; f[18] = snaptop; + f[19] = fty; f[20] = ntp; f[21] = xty; wcopy(f + 22, tpn, 2); + return 0; +} +fn ctx_pop() +{ + let f; + ctxsp--; + f = R_CTX + ctxsp * 24; + nloc = f[0]; lbase = f[1]; fdepth = f[2]; fslots = f[3]; fchk = f[4]; fret = f[5]; live = f[6]; + bxdepth = f[7]; epichain = f[8]; fframe = f[9]; infn = f[10]; lsp = f[11]; loopbase = f[12]; + lv = f[13]; lvk = f[14]; lvi = f[15]; kw_expr = f[16]; xcls = f[17]; snaptop = f[18]; + fty = f[19]; ntp = f[20]; xty = f[21]; wcopy(tpn, f + 22, 2); + return 0; +} + +// ---- functions and the top level ---- +fn fn_begin(chk, ret) +{ + lbase = nloc; fdepth = 1; fslots = 0; fchk = chk && check_on; fret = ret; fty = 0; + live = 1; bxdepth = 0; epichain = 0; infn = 1; loopbase = lsp; lv = 0; lvk = LV_NONE; + return 0; +} +// fp -= frame size, then check the frame stack: past FBOT, reset fp and stop +fn prologue() +{ + let k; + lit(ZFP); op(O_LD); fframe = hole(); op(O_SUB); lit(ZFP); op(O_ST); + if (o3on) return 0; // o3 gave up the frame check + lit(ZFP); op(O_LD); lit(FBOT); op(O_LT); k = hole(); op(O_JZ); + lit(FTOP); lit(ZFP); op(O_ST); callname(s_fovf); + setlit(k, here); + return 0; +} +fn epilogue() +{ + lit(0); + fixall(epichain, here); + lit(ZFP); op(O_LD); lit(fslots); op(O_ADD); lit(ZFP); op(O_ST); op(O_RET); + setlit(fframe, fslots); + infn = 0; + nloc = lbase; + return 0; +} +fn body() +{ + expect(s_lb); + while (!is_op(s_rb)) { if (tk == T_EOF) die("missing } at the end of a function"); stmt(); } + unused_check(lbase); + if (fchk && live) { exit_vars(1, 1); if (fret) cerr("a function returning .u has to end with a return"); } + next(); + return 0; +} +// the rest of a function, after its return type and name: +// int f(int a, *node.m n, .d x, b) { ... } *list.u mk(int n) { ... } +// rty is 0 for fn (a plain word), ret is 1 for a .u return +fn fndef(chk, rty, ret, n) +{ + let s, np = 0, tags = 0, t, sig, i, pn[24], pt[24], pty[24], typed = 0, ft, o3f = o3nxt, lo, hi; + o3nxt = 0; + if (rty || ntp) typed = 1; + s = lookup(n); + if (s < 0 || s < ubase || skind[s] != K_FN || (sflag[s] & F_DEF)) { s = addsym(n, K_FN, 0); sflag[s] = 0; } + expect(s_lp); + if (!is_op(s_rp)) { + while (1) { + t = 0; + pty[np] = tword; + if (!is_op(s_dot) && ((tk == T_OP && (tv == s_star || tv == s_lk)) || (tk == T_ID && (tv == s_fn || istypename(tv))))) { + pty[np] = ptype(); typed = 1; + if (isagg(pty[np])) serr("structs and arrays are passed by pointer"); + } + if (accept(s_dot)) { // .m lends, .d takes over + if (tv == s_m) t = 1; else if (tv == s_d) t = 2; else die("a parameter tag is .m or .d"); + next(); + } + pn[np] = name(); + if (o3f && pty[np] == tword) serr("o3 parameters need a type"); + if (np >= 12 && t) die("only the first 12 parameters can be tagged"); + pt[np] = t; + if (np < 12) tags = tags + (t << (np + np)); + np++; + if (np >= 24) die("too many parameters"); + if (!accept(s_comma)) break; + } + } + expect(s_rp); + sig = ret + np + np + tags * 64; + if ((sflag[s] & F_SIG) && !(sflag[s] & F_PRE)) { if (ssig[s] != sig) serrn(n, "doesn't match its earlier declaration"); } + else if ((sflag[s] & F_UNTYPED) && (tags || ret)) serrn(n, "was called before its tags were declared; put a prototype above the first call"); + ssig[s] = sig; + sflag[s] = (sflag[s] | F_SIG) - (sflag[s] & F_PRE); + if (ntp) sflag[s] = sflag[s] | F_GEN; + if (typed) { + if (!rty) rty = tword; + ft = tfn(rty, np, pty, sig); + if (stype[s] && stype[s] != ft) serrn(n, "doesn't match its earlier declaration"); + stype[s] = ft; + } + if (accept(s_semi)) { ntp = 0; return s; } + if (sflag[s] & F_DEF) serrn(n, "is defined twice"); + sval[s] = here; + sflag[s] = sflag[s] | F_DEF; + fixall(sfix[s], here); sfix[s] = 0; + fn_begin(chk, ret); + if (typed) fty = rty; + no3pend = 0; + if (o3f) { o3on = 1; no3sl = 0; } + prologue(); + i = 0; + while (i < np) { + t = newloc(pn[i]); fslots++; + ltype[t] = pty[i]; + if (o3f) { ltag[t] = 16; addsl(lslot[t]); } + if (fchk && pt[i] == 1) lstate[t] = S_BORROW; + if (fchk && pt[i] == 2) { lstate[t] = S_M; lown[t] = 1; } + i++; + } + i = np; + while (i) { i--; laddr(i); op(O_ST); } + if (o3f) { lo = here + 2; lit(lo); op(O_JMP); } // the region starts here: a jump, so the cpu enters it + body(); + hi = here; + epilogue(); + if (o3f) { o3on = 0; o3region(lo, hi, o3sl, no3sl); } + o3flush(); + ntp = 0; + return s; +} +// let g = 1, buf[10]; int g = 3; [10]int buf; point origin; +fn gdecl(typed) { next(); return gdecl2(0, 0, name()); } +fn gdecl2(t, tg, n) +{ + let a, k, s; + if (tg) serr("tags go on locals and parameters, not globals"); + if (t == tvoid) serr("a variable can't be void"); + while (1) { + if (accept(s_lk)) { + k = cexpr(); expect(s_rk); + s = addsym(n, K_GARR, here); + if (!t) t = tword; + stype[s] = tarr(t, k); + k = tsz(stype[s]); + while (k) { emit(0); k--; } + } else if (t && isagg(t)) { + s = addsym(n, K_GARR, here); stype[s] = t; + k = tsz(t); + while (k) { emit(0); k--; } + } else { + a = here; + s = addsym(n, K_GVAR, a); + if (t) stype[s] = t; + emit(0); + if (accept(s_eq)) { + if (tk == T_STR) { *(a + hdelta) = here; k = tv; while (*k) { emit(*k); k++; } emit(0); next(); } + else *(a + hdelta) = cexpr(); + } + } + if (!accept(s_comma)) break; + n = name(); + } + expect(s_semi); + return 0; +} +fn gconst() +{ + let n; + next(); + while (1) { n = name(); expect(s_eq); addsym(n, K_CONST, cexpr()); if (!accept(s_comma)) break; } + expect(s_semi); + return 0; +} +// enhance [n] name(a, b) micro "recipe" [else fn]; n outputs, 1 if left out +// enhance [n] name(a, b) vm { asm } [else fn]; +// lays out the descriptor the install opcode reads, and wires it in right +// away (an image wires its own in when it boots). +fn enhdef() +{ + let n, np = 0, nout = 1, dsc, na, pay, len = 0, kind = 0, i, p, s, slot, r, pn[8]; + next(); + if (tk == T_NUM) { if (tv < 0 || tv > 8) serr("an enhancement has at most 8 outputs"); nout = tv; next(); } // enhance 2 divmod(a, b) + n = name(); + expect(s_lp); + if (!is_op(s_rp)) { while (1) { if (np == 8) die("an enhancement takes at most 8 inputs"); pn[np] = name(); np++; if (!accept(s_comma)) break; } } + expect(s_rp); + dsc = here; i = 0; while (i < 7) { emit(0); i++; } + na = here; i = 0; while (i < np) { emit(0); i++; } + i = 0; + while (i < np) { *(na + i + hdelta) = here; p = pn[i]; while (*p) { emit(*p); p++; } emit(0); i++; } + if (accept(s_micro)) { + pay = here; + if (tk != T_STR) die("expected the recipe as a string"); + while (tk == T_STR) { p = tv; while (*p) { emit(*p); p++; } next(); } + emit(0); + } else if (accept(s_vm)) { pay = here; asmblock(); op(O_RET); len = here - pay; kind = 1; } + else die("expected micro or vm"); + slot = nenh; + if (slot >= 512) fatal("all 512 enhancement slots are taken"); + nenh++; + p = dsc + hdelta; + p[0] = slot; p[1] = kind; p[2] = np; p[3] = nout; p[4] = pay; p[5] = len; p[6] = na; + s = addsym(n, K_ENH, slot); + ssig[s] = np + nout * 16 + dsc * 256; + if (accept(s_else)) { r = lookup(tv); if (r < 0) r = fwdfn(tv); next(); sfix[s] = r + 1; } + expect(s_semi); + if (!hdelta) { + r = install(dsc); + if (r < 0) { + if (!sfix[s]) serrn(n, "the enhancement didn't install (see install's codes in core.c)"); + sflag[s] = sflag[s] | F_FALLBACK; + } + } + return 0; +} +// after a syntax error at the top level: skip to something that starts a declaration +fn topsync() +{ + let depth = 0; + while (tk != T_EOF) { + if (is_op(s_lb)) depth++; + else if (is_op(s_rb)) { depth--; if (depth < 0) depth = 0; } + else if (!depth && tk == T_ID && (tv == s_fn || tv == s_var || tv == s_const || tv == s_struct || tv == s_type + || tv == s_enhance || tv == s_typecheck || tv == s_unchecked || tv == s_immediate || tv == s_o3)) break; + next(); + } + panic = 0; + return 0; +} +fn fndone(s, imm) +{ + let k; + ntp = 0; + if (imm) { + if (hdelta) serr("parse words can't be defined while building an image"); + k = addsym(sname[s], K_KW, sval[s]); + sflag[k] = F_DEF | F_STMT | F_EXPR | F_TOP; + } + return 0; +} +fn topitem() +{ + let chk = 1, imm = 0, s, k, n; + if (panic) return topsync(); + if (is_id(s_unchecked)) { + next(); + if (accept(s_semi)) { check_on = 0; return 0; } // unchecked; switches the analyzer off from here on + chk = 0; + } + if (is_id(s_immediate)) { next(); imm = 1; } + o3nxt = 0; + if (is_id(s_o3)) { next(); o3nxt = 1; } + ntp = 0; + if (is_op(s_lt)) { // before a generic function + next(); + while (1) { if (ntp >= 8) fatal("too many type parameters"); tpn[ntp] = name(); ntp++; if (!accept(s_comma)) break; } + closeangle(); + } + if (is_id(s_fn)) { peek(); if (!(pk_tk == T_OP && pk_tv == s_lp)) { next(); n = name(); return fndone(fndef(chk, 0, 0, n), imm); } } + if (ntp || !chk || imm || o3nxt) { // has to be a function, with a return type + dtype(); n = name(); + if (dtag && dtag != S_U) serr("the only return tag is .u"); + if (!is_op(s_lp)) { die("expected a function"); return 0; } + return fndone(fndef(chk, dty, dtag == S_U, n), imm); + } + if (is_id(s_var)) return gdecl(0); + if (is_id(s_struct)) { kw_struct(); return 0; } + if (is_id(s_type)) return kw_type(); + if (is_id(s_typecheck)) return kw_typecheck(); + if (is_id(s_enhance)) return enhdef(); + if (is_id(s_const)) return gconst(); + if (tk == T_ID) { + s = lookup(tv); + if (s >= 0 && skind[s] == K_KW && (sflag[s] & F_TOP)) { kwcall(s, 0); return 0; } + } + if (tstart()) { // int f(...) { } or int g = 1; + dtype(); n = name(); + if (is_op(s_lp)) { + if (dtag && dtag != S_U) serr("the only return tag is .u"); + return fndone(fndef(chk, dty, dtag == S_U, n), 0); + } + return gdecl2(dty, dtag, n); + } + die("expected a declaration"); + return 0; +} +fn unit_top() { while (tk != T_EOF) topitem(); return 0; } +fn unit_end(from) +{ + let i = from; + panic = 0; + while (i < nsym) { + if (skind[i] == K_FN && !(sflag[i] & F_DEF) && sfix[i]) { tline = sval[i]; serrn(sname[i], "is used but never defined"); } + i++; + } + if (nerr) giveup(); + return 0; +} + +// compile a token list as the body of a new function, or (top) as top-level +// declarations. returns the entry address of the function. +unchecked fn compile_list(q, top) +{ + let hop = -1, entry = 0, from = nsym; + ctx_push(); + if (infn) { hop = hole(); op(O_JMP); } + if (top) prescan(1, q); + src_push(1, q, 0, 0); + next(); + if (top) unit_top(); + else { + entry = here; + fn_begin(1, 0); + prologue(); + while (tk != T_EOF) stmt(); + unused_check(lbase); + if (fchk && live) exit_vars(1, 1); + epilogue(); + } + src_pop(); + unit_end(from); + if (hop >= 0) setlit(hop, here); + if (here > ptop) ptop = here; + ctx_pop(); + return entry; +} +fn compile(q) { return compile_list(q, 0); } +fn load(q) { compile_list(q, 1); return 0; } +fn eval(q) { let f = compile_list(q, 0); return f(); } +fn evals(s) { let q = tokens(s), r = eval(q); free(d*q); return r; } + +// ---- symbol blob ---- +// an image ends with its own symbol table so the genome knows its names: +// kind, value, signature, name chars, 0 ... then a 0 kind. +fn blob_emit(from) +{ + let i = from, p; + while (i < nsym) { + if ((skind[i] >= K_FN && skind[i] <= K_CONST) || skind[i] == K_ENH) { + emit(skind[i]); emit(sval[i]); emit(ssig[i]); + p = sname[i]; + while (*p) { emit(*p); p++; } + emit(0); + } + i++; + } + emit(0); + return 0; +} +fn blob_load(p) +{ + let s, n; + while (*p) { + n = intern(p + 3); + s = addsym(n, p[0], p[1]); + ssig[s] = p[2]; + sflag[s] = F_DEF | F_SIG; + if (p[0] == K_ENH) { if (p[1] >= nenh) nenh = p[1] + 1; install(p[2] >> 8); } + p = p + 3 + strlen(p + 3) + 1; + } + return 0; +} +// compile the rest of stdin as a genome image that will run at org, writing +// it to dst. only the source's own names are visible while it compiles. +// returns the image length in words. +fn build_image(org, dst) +{ + let sh = here, sd = hdelta, sl = hlimit, sub = ubase, svl = vislo, sns = nsym, sen = nenh, n; + ubase = nsym; vislo = nsym; nenh = 0; + here = org; hdelta = dst - org; hlimit = org + 0x30000; + lit(FTOP); lit(ZFP); op(O_ST); fnref(fwdfn(s_start)); op(O_JMP); emit(0); emit(0); + unit_stdin(); + *(dst + 5) = here; + blob_emit(ubase); + *(dst + 6) = here; + n = here - org; + popsyms(sns); + ubase = sub; vislo = svl; here = sh; hdelta = sd; hlimit = sl; nenh = sen; + return n; +} + +// ---- setup ---- +fn gc_roots() +{ + markrange(0, ptop); + if (!hdelta && here > ptop) markrange(ptop, here); // globals of a unit still compiling + markrange(GORG, GORG[6]); + markrange(R_SRC, R_SRC + 1024); + markrange(*ZFP, FTOP); + markrange(DSLO, DSHI); + return 0; +} +fn zc_init() +{ + pooltop = R_POOL; + wfill(R_IH, 0, IHN); + nsym = 0; nfixu = 0; fixfree = 0; nbin = 0; nun = 0; nasg = 0; nkop = 0; seqtop = 0; + lsp = 0; ctxsp = 0; snaptop = 0; nloc = 0; lbase = 0; + s_lp = intern("("); s_rp = intern(")"); s_lb = intern("{"); s_rb = intern("}"); + s_lk = intern("["); s_rk = intern("]"); s_comma = intern(","); s_semi = intern(";"); + s_eq = intern("="); s_arrow = intern("->"); s_inc = intern("++"); s_dec = intern("--"); + s_star = intern("*"); s_amp = intern("&"); s_colon = intern(":"); s_hash = intern("#"); + s_minus = intern("-"); s_dot = intern("."); s_q = intern("?"); s_case = intern("case"); s_default = intern("default"); s_andand = intern("&&"); s_oror = intern("||"); + s_fn = intern("fn"); s_var = intern("let"); s_const = intern("const"); s_else = intern("else"); + s_as = intern("as"); s_unchecked = intern("unchecked"); s_immediate = intern("immediate"); + s_m = intern("m"); s_d = intern("d"); s_u = intern("u"); + s_start = intern("start"); s_main = intern("main"); s_free = intern("free"); + s_own = intern("own"); s_qnew = intern("qnew"); s_fovf = intern("frame_overflow"); + s_enhance = intern("enhance"); s_micro = intern("micro"); s_vm = intern("vm"); + return 0; +} +// operators and keywords. these are plain tables in memory: a program can +// add operators, change precedences or replace keywords with its own. +fn zc_tables() +{ + add_unop("!", "0 $ 0 ~ - | 31 > 1 +"); // index 0 + add_unop("!!", "0 $ 0 ~ - | 31 > 0 ~ -"); // index 1: normalize to 0 or 1 + add_unop("~", "-1 ^"); // index 2 + add_unop("-", "0 ~ -"); + add_binop("*", 10, "*"); // the first few indexes are used by cexpr + add_binop("/", 10, "idiv"); // native divide, wired in by rt.zc + add_binop("%", 10, "imod"); + add_binop("+", 9, "+"); // 3 + add_binop("-", 9, "-"); // 4 + add_binop("<<", 8, "ishl"); // 5 + add_binop(">>", 8, ">"); // 6 + add_binop("<", 7, "<"); + add_binop(">", 7, "~ <"); + add_binop("<=", 7, "~ < 1 ^"); + add_binop(">=", 7, "< 1 ^"); + add_binop("==", 6, "- 0 $ 0 ~ - | 31 > 1 +"); + add_binop("!=", 6, "- 0 $ 0 ~ - | 31 > 0 ~ -"); + add_binop("&", 5, "&"); // 13 + add_binop("^", 4, "^"); + add_binop("|", 3, "|"); // 15 + add_binop("&&", -1, 0); + add_binop("||", -2, 0); + addkop(s_eq); addkop(s_arrow); addkop(s_inc); addkop(s_dec); + add_kw("let", kw_var, F_STMT); + add_kw("if", kw_if, F_STMT); + add_kw("while", kw_while, F_STMT); + add_kw("for", kw_for, F_STMT); + add_kw("switch", kw_switch, F_STMT); + add_kw("break", kw_break, F_STMT); + add_kw("continue", kw_continue, F_STMT); + add_kw("return", kw_return, F_STMT); + add_kw("unchecked", kw_unchecked, F_STMT); + add_kw("asm", kw_asm, F_STMT | F_EXPR); + add_kw("quote", kw_quote, F_EXPR); + add_kw("sizeof", kw_sizeof, F_EXPR); + add_kw("typeof", kw_typeof, F_EXPR); + add_kw("o3", kw_o3, F_STMT); + add_kw("len", kw_len, F_EXPR); + add_kw("cap", kw_cap, F_EXPR); + add_kw("push", kw_push, F_EXPR); + add_kw("pop", kw_pop, F_EXPR); + add_kw("resize", kw_resize, F_EXPR); + return 0; +} +fn start() +{ + let s, f; + heap_init(); + zc_init(); + blob_load(GORG[5]); + zc_tables(); + types_init(); + here = 0; hdelta = 0; hlimit = HEAP_LO; ptop = 0; + ubase = nsym; vislo = 0; + unit_stdin(); + if (here > ptop) ptop = here; + s = lookup(s_main); + if (s >= 0 && skind[s] == K_FN && (sflag[s] & F_DEF)) { f = sval[s]; f(); } + halt(); + return 0; +} diff --git a/docs/history/15-o3/code/src/rt.zc b/docs/history/15-o3/code/src/rt.zc new file mode 100644 index 0000000..d85ba8e --- /dev/null +++ b/docs/history/15-o3/code/src/rt.zc @@ -0,0 +1,290 @@ +// ============================================================================ +// rt.zc: the runtime half of the genome. memory map, i/o, arithmetic helpers, +// the heap (size-class free lists) and the mark-sweep collector. +// ============================================================================ + +// ---- memory map (words) ---- +const MW = 0x100000; // 1m words of memory +const HEAP_LO = 0x40000; // heap: 0x40000 .. 0xA0000 +const HEAP_HI = 0xA0000; +const ZFP = 0xA0000; // frame pointer word, used by every prologue +const GORG = 0xC0000; // the genome (this code) lives here +const FBOT = 0xE0000; // frame stack: FBOT .. FTOP, grows down +const FTOP = 0xF0000; +const RS0 = 0xF0000; // return stack grows up from here +const MARK0 = 0xF8000; // mark stack for the collector +const MARKN = 0x4000; +const DSLO = 0xFC000; // data stack: DSLO .. MW-16, grows down +const DSHI = 0xFFFF0; +const MAGIC = 0x2B7E1516; + +// ---- virtual enhancements ---- +// install(desc) wires a custom opcode into the cpu (the % opcode). these three +// give the language native division and left shift (zdiv, zmod and zshl +// below are the same thing done in software). they come first, since the +// compiler resolves / % and << to them by name. +fn install(desc) { return asm { desc @ % }; } +enhance idiv(a, b) micro "a = sdiv a b"; +enhance imod(a, b) micro "a = srem a b"; +enhance ishl(a, b) micro "a = shl a b"; +// for o3: an o3 variable's address (fp + k), and a bounds check (1 if i isn't in 0..n-1) +enhance lfa(k) micro "r1 = ld 655360; k = add r1 k"; +enhance outside(i, n) micro "i = ltu i n; i = xor i 1"; + +// ---- i/o ---- +fn putc(ch) { asm { ch @ . } return 0; } +fn getc() { return asm { , }; } +fn eof() { return asm { ? }; } +fn halt() { asm { q } return 0; } +fn puts(s) { let i = 0; while (s[i]) { putc(s[i]); i++; } return 0; } +fn putn(n) +{ + let b[12], i = 0; + if (n < 0) { putc('-'); n = 0 - n; } + if (n < 0) { puts("2147483648"); return 0; } + while (1) { b[i++] = '0' + n % 10; n = n / 10; if (!n) break; } + while (i) putc(b[--i]); + return 0; +} +fn strlen(s) { let n = 0; while (s[n]) n++; return n; } +fn streq(a, b) { let i = 0; while (a[i] && a[i] == b[i]) i++; return a[i] == b[i]; } +fn wcopy(dst, src, n) { let i = 0; while (i < n) { dst[i] = src[i]; i++; } return 0; } +fn wfill(dst, v, n) { let i = 0; while (i < n) { dst[i] = v; i++; } return 0; } + +// ---- arithmetic the hardware doesn't have ---- +let pow2[32]; +let zrem; +fn zudiv(n, dv) +{ + let q = 0, r = 0, i = 30; + if (n < dv) { zrem = n; return 0; } + while (i >= 0) { + r = r + r + ((n >> i) & 1); + q = q + q; + if (r >= dv) { r = r - dv; q++; } + i--; + } + zrem = r; + return q; +} +fn zdiv(a, b) +{ + let s = 0, q; + if (b == 0) { zrem = a; return 0; } + if (a < 0) { a = 0 - a; s = 1; } + if (b < 0) { b = 0 - b; s = s ^ 1; } + q = zudiv(a, b); + if (s) return 0 - q; + return q; +} +fn zmod(a, b) { let neg = a < 0; if (b == 0) return a; zdiv(a, b); if (neg) return 0 - zrem; return zrem; } +fn zshl(a, b) { return a * pow2[b & 31]; } + +// ---- heap ---- +// a buddy allocator. the heap is cut into aligned power-of-two chunks, every +// block is a power of two words, and a freed block merges with its buddy +// (the other half of the block they were split from) whenever that one is +// free too, so freed space can be reused at any size. every block has a 6 +// word header before the pointer the program sees: +// p[-6] block size p[-5] size class p[-4] flags (0 free, 1 live, 3 marked) +// p[-3] p ^ MAGIC p[-2] owner (the container that owns it, or 0) +// p[-1] its type (a type record from new(T), or 0 from new(n)) +// free blocks sit on a doubly linked list per class, linked through p[0], p[1]. +const HDR = 6; +let hbump, freel[32], gc_runs, gc_freed, gc_auto = 1; + +// the size of the top-level chunk holding heap offset off +fn topsize(off) +{ + let h = HEAP_HI - HEAP_LO, base = 0, sz; + while (h) { + sz = 1; + while (sz + sz <= h) sz = sz + sz; + if (off < base + sz) return sz; + base = base + sz; h = h - sz; + } + return 0; +} +fn fpush(b, k) { b[2] = 0; b[6] = freel[k]; b[7] = 0; if (freel[k]) freel[k][7] = b; freel[k] = b; return 0; } +fn funlink(b, k) +{ + if (b[7]) b[7][6] = b[6]; else freel[k] = b[6]; + if (b[6]) b[6][7] = b[7]; + return 0; +} +fn mkblock(b, k) { b[0] = pow2[k]; b[1] = k; b[2] = 0; b[3] = 0; b[4] = 0; b[5] = 0; return b; } +fn heap_init() +{ + let i = 1, b = HEAP_LO, sz, k; + pow2[0] = 1; + while (i < 32) { pow2[i] = pow2[i - 1] + pow2[i - 1]; i++; } + hbump = HEAP_HI; + wfill(freel, 0, 32); + while (b < HEAP_HI) { + sz = topsize(b - HEAP_LO); k = 0; + while (pow2[k] < sz) k++; + fpush(mkblock(b, k), k); + b = b + sz; + } + return 0; +} +// a free block of class k, split down from a bigger one if needed; 0 if none +fn balloc(k) +{ + let j = k, b, h; + while (j < 32 && !freel[j]) j++; + if (j >= 32) return 0; + b = freel[j]; + funlink(b, j); + while (j > k) { j--; h = b + pow2[j]; fpush(mkblock(h, j), j); } + mkblock(b, k); + return b; +} +let fend; // end of the block bfree last made, for the sweep +// put block b back, merging it with its buddy as long as it can +fn bfree(b) +{ + let k = b[1], sz = pow2[k], off, bud; + while (1) { + off = b - HEAP_LO; + if (sz >= topsize(off)) break; + bud = HEAP_LO + (off ^ sz); + if (bud[2] != 0 || bud[1] != k || bud[0] != sz) break; + funlink(bud, k); + if (bud < b) { b[0] = 0; b[3] = 0; b = bud; } // the upper header is inside the merged block now + else { bud[0] = 0; bud[3] = 0; } + k++; sz = sz + sz; + mkblock(b, k); + } + fpush(mkblock(b, k), k); + fend = b + sz; + return 0; +} +fn isobj(x) { return x >= HEAP_LO + HDR && x < hbump && x[-3] == (x ^ MAGIC) && (x[-4] & 1); } + +// every prologue checks the frame stack; on overflow it resets the frame +// pointer (so this can run at all) and stops +fn frame_overflow() { puts("zy: frame stack overflow\n"); halt(); return 0; } +fn oom() { puts("zc: out of heap memory\n"); halt(); return 0; } +// n words, untyped and not zeroed: what new gives o3 code +unchecked .u new_raw(n) +{ + let need = n + HDR, k = 3, b, p; + if (n < 0) need = HDR; + while (pow2[k] < need) k++; + b = balloc(k); + if (!b && gc_auto) { gc(); b = balloc(k); } + if (!b) oom(); + p = b + HDR; + b[2] = 1; b[3] = p ^ MAGIC; b[4] = 0; b[5] = 0; + return p; +} +// n words typed t, zeroed +unchecked .u new_t(n, t) +{ + let p = new_raw(n); + p[-1] = t; + wfill(p, 0, p[-6] - HDR); + return p; +} +unchecked .u new(n) { return new_t(n, 0); } +fn osize(p) { if (!isobj(p)) return 0; return p[-6] - HDR; } +fn typeof_obj(p) { if (!isobj(p)) return 0; return p[-1]; } + +// frees p and, recursively, every object p owns +fn release(p) +{ + let b = p - HDR, n = b[0] - HDR, i = 0, w; + b[2] = 2; // going: not live, and not free yet either, so a child's buddy check can't merge into it + while (i < n) { w = p[i]; if (isobj(w) && w[-2] == p) release(w); i++; } + bfree(b); + return 0; +} +unchecked fn free(.d p) { if (isobj(p)) release(p); return 0; } +fn own(c, p) { if (isobj(c)) c[-2] = p; return c; } + +// ---- growable arrays ---- +// a []T is a handle: [len, cap, items, element size]. items is an object the +// handle owns; growing swaps it for a bigger one and moves its children over. +unchecked .u dnew(es, n, t) +{ + let h = new_t(4, t), c = n, it; + if (c < 4) c = 4; + it = new(c * es); + h[0] = n; h[1] = c; h[2] = own(it, h); h[3] = es; + return h; +} +unchecked fn dgrow(h, need) +{ + let c = h[1], it, old = h[2], i = 0, w, n; + if (need <= c) return 0; + while (c < need) c = c + c; + it = new(c * h[3]); + n = h[0] * h[3]; + wcopy(it, old, n); + while (i < n) { w = it[i]; if (isobj(w) && w[-2] == old) w[-2] = it; i++; } // the children move with the elements + h[2] = own(it, h); h[1] = c; + free(old); + return 0; +} +fn oob(i, n) { puts("zy: index "); putn(i); puts(" out of range (length "); putn(n); puts(")\n"); halt(); return 0; } +unchecked fn dat(h, i) { if (i < 0 || i >= h[0]) oob(i, h[0]); return h[2] + i * h[3]; } +unchecked fn dpush(h, .d x) { dgrow(h, h[0] + 1); h[2][h[0]] = x; own(x, h[2]); h[0] = h[0] + 1; return 0; } +unchecked fn dpop(h) { if (!h[0]) oob(-1, 0); h[0] = h[0] - 1; return h[2][h[0]]; } +unchecked fn dresize(h, n) +{ + let old = h[0] * h[3]; + if (n < 0) n = 0; + dgrow(h, n); + if (n * h[3] > old) wfill(h[2] + old, 0, n * h[3] - old); + h[0] = n; + return 0; +} +fn dlen(h) { return h[0]; } +fn dcap(h) { return h[1]; } + +// ---- mark-sweep ---- +// conservative and non-moving. roots: the program area (globals live +// between functions), the genome's globals, the compiler's input stack, +// the live part of the frame stack and the whole data stack region. +let ms_top, ms_over, gc_lo, gc_hi; +fn mark(w) +{ + if (!isobj(w) || (w[-4] & 2)) return 0; + w[-4] = w[-4] | 2; + if (ms_top < MARKN) { MARK0[ms_top] = w; ms_top++; } else ms_over = 1; + return 0; +} +fn markrange(a, b) { while (a < b) { mark(*a); a++; } return 0; } +fn drain() +{ + let p; + while (ms_top) { ms_top--; p = MARK0[ms_top]; markrange(p, p + p[-6] - HDR); } + return 0; +} +fn gc_roots(); +unchecked fn gc() +{ + let b, sz, n = 0; + ms_top = 0; ms_over = 0; + gc_roots(); + drain(); + while (ms_over) { // the mark stack overflowed: rescan marked objects + ms_over = 0; + b = HEAP_LO; + while (b < HEAP_HI) { if (b[2] == 3) markrange(b + HDR, b + b[0]); b = b + b[0]; drain(); } + } + b = HEAP_LO; + while (b < HEAP_HI) { + sz = b[0]; + if (b[2] == 1) { bfree(b); n++; b = fend; } // merging may swallow what follows + else { if (b[2] == 3) b[2] = 1; b = b + sz; } + } + gc_runs++; + gc_freed = gc_freed + n; + return n; +} +// number of live objects, for tests +fn live_objects() { let b = HEAP_LO, n = 0; while (b < HEAP_HI) { if (b[2] & 1) n++; b = b + b[0]; } return n; } +// the biggest block that's free right now, in words +fn largest_free() { let k = 31; while (k > 0 && !freel[k]) k--; if (!freel[k]) return 0; return pow2[k]; } diff --git a/docs/history/15-o3/code/test.sh b/docs/history/15-o3/code/test.sh new file mode 100755 index 0000000..a4cf785 --- /dev/null +++ b/docs/history/15-o3/code/test.sh @@ -0,0 +1,88 @@ +#!/bin/bash +# full check: bootstraps the compiler from zc0, verifies the fixpoint, then runs +# the examples and tests on the portable interpreter (host), the arm64 jit under +# qemu, and the llvm backend. needs gcc; the jit half needs aarch64-linux-gnu-gcc +# and qemu-aarch64, the llvm half needs llvm-config and libLLVM (each is skipped +# if missing). +cd "$(dirname "$0")"; T=$(mktemp -d); trap 'rm -rf $T' EXIT +ok=0; bad=0 +chk(){ if [ "$1" = "$2" ]; then echo "ok $3"; ok=$((ok+1)); else echo "FAIL $3: got [$1]"; bad=$((bad+1)); fi; } +src(){ cat src/rt.zc src/compiler.zc; } +selfc(){ (cat tools/build.zc; printf '`'; src) | $1 > $2; } + +# bootstrap: zc0 (c) -> g0, g0 compiles compiler.zc -> g1, g1 compiles it -> g2 +gcc -O1 -w zc0.c -o $T/zc0 && gcc -O1 -w mkgenome.c -o $T/mkg || exit 1 +mkdir $T/s0 $T/s1 +cp core.c io_linux.c $T/s0/; cp core.c io_linux.c $T/s1/ +src | $T/zc0 > $T/s0/genome.h || exit 1 +gcc -O2 -w -DJTYPE=int $T/s0/core.c $T/s0/io_linux.c -o $T/zy0 +selfc $T/zy0 $T/g1.bin; $T/mkg < $T/g1.bin > $T/s1/genome.h +gcc -O2 -w -DJTYPE=int $T/s1/core.c $T/s1/io_linux.c -o $T/zy1 +selfc $T/zy1 $T/g2.bin +cmp -s $T/g1.bin $T/g2.bin && chk 1 1 "fixpoint: the compiler compiles itself to the same $(($(stat -c %s $T/g1.bin) / 4)) words" || chk 0 1 "fixpoint" +cmp -s $T/s1/genome.h genome.h && chk 1 1 "genome.h is up to date" || chk 0 1 "genome.h is stale (cp the bootstrap output over it)" + +run(){ # $1 = vm command, $2 = label + local vm="$1" l="$2" + chk "$($vm < examples/hello.zc)" "hello, world!" "$l hello" + chk "$($vm < examples/fact.zc | tr '\n' '/')" "1101110101111100000000/3628800/" "$l factorial" + chk "$( (cat examples/echo.zc; printf '`abc\n') | $vm)" "abc" "$l echo after the backtick" + chk "$($vm < examples/selfpatch.zc)" "ABC" "$l self-patching code" + chk "$($vm < examples/lists.zc | tr '\n' '/')" "[0 1 4 9 16 25]/live: 7/after the block: 0/" "$l ownership frees everything" + chk "$( (cat examples/repl.zc; printf '`fn sq(x) { return x * x; }`putn(sq(7)); putc(10);`puts("hi\\n");') | $vm | tr '\n' '/')" "49/hi/" "$l repl on the resident compiler" + chk "$($vm < examples/typed.zc | tr '\n' '/')" "last: 4/point is 2 words: x int, y int/vec<*point> is 3 words: len int, cap int, items **point/push is bool(*vec, T.d)/the heap says v is a vec, and v.items[0] is a point/" "$l typed example" + chk "$($vm < examples/wiring.zc)" "16 1234 25 4/9" "$l enhancement example" + chk "$($vm < tests/basics.zc | tr '\n' '/')" "285 610 42 512/hey 65 -3 -2 1024 -8/12456/757/9 100-101/5/ok/" "$l language basics" + chk "$($vm < tests/umd.zc)" "100 6 0 15 99 5 5" "$l u m d lifetimes" + chk "$($vm < tests/gc.zc)" "1000 1 900 1 199900" "$l mark-sweep collector" + chk "$($vm < tests/meta.zc | tr '\n' '/')" "1024 19/unless ok/rrr/15 42/7/42/7/144/" "$l parse words, quote, eval, load, operator tables" + chk "$($vm < tests/misc.zc | tr '\n' '/')" "45/54321/ok/" "$l compile-time eval, patched mnemonics, unchecked" + chk "$($vm < tests/types.zc | tr '\n' '/')" "12 5 2 15 56 42 7 14 3/rect *rect *list *list.u(int) T(*list) fn(word.d)/a:point@0 b:point@2 tag:int@4 /point 1 1/" "$l types, generics, reflection" + chk "$($vm < tests/typeok.zc)" "14" "$l a program the typechecker accepts" + chk "$($vm < tests/typeoff.zc)" "6" "$l the typechecker is off by default" + chk "$($vm < tests/enhance.zc)" "100 0 343 3,2 5 -2 0" "$l virtual enhancements" + chk "$($vm < tests/joins.zc)" "0 0 5 80 8 3 9 0" "$l joins free what one path gave away; switch, ?:, no prototypes" + chk "$($vm < tests/buddy.zc)" "131072 262144 1 0" "$l buddy allocator merges freed blocks" + chk "$($vm < tests/borrowmove.zc)" "6" "$l storing into a borrowed object moves it" + chk "$($vm < tests/manyerrors.zc | tr '\n' '/')" "zc: line 3: expected an expression/zc: line 4: x: m object is never used/zc: line 5: type error: initializer: expected *char, got int/zc: line 6: expected ;/zc: 4 errors/" "$l several errors in one run" + chk "$($vm < tests/overflow_return.zc 2>&1)" "zy: return stack overflow" "$l return stack overflow is caught" + chk "$($vm < tests/overflow_data.zc 2>&1)" "zy: data stack overflow" "$l data stack overflow is caught" + chk "$($vm < tests/overflow_frame.zc 2>&1)" "zy: frame stack overflow" "$l frame stack overflow is caught" + chk "$($vm < examples/mandel.zc 2>&1 | md5sum | cut -c1-32)" "f41aecbf2c844168050413cfa832db0e" "$l example: mandelbrot with a fixed-point enhancement" + chk "$($vm < examples/bf.zc 2>&1 | md5sum | cut -c1-32)" "223570983752b17f7ece32e93d5e19e4" "$l example: brainfuck compiled at runtime" + chk "$($vm < examples/dump.zc 2>&1 | md5sum | cut -c1-32)" "757774b00f6b3cd0bfbcba66448a8619" "$l example: struct printer through reflection" + chk "$($vm < examples/tree.zc 2>&1 | md5sum | cut -c1-32)" "b86a2e4e9e31acc294a4ddf86be863f2" "$l example: owning tree frees itself" + chk "$($vm < examples/lang.zc 2>&1 | md5sum | cut -c1-32)" "aa1a18c78b5200129c7c2b315f163d28" "$l example: pipe operator, unless, assert, times" + chk "$($vm < examples/staged.zc 2>&1 | md5sum | cut -c1-32)" "6f790a125c48b0cc7a897cee73ee5627" "$l example: staged code compiled while running" + chk "$($vm < examples/quine.zc 2>&1 | md5sum | cut -c1-32)" "c868ecb9dd7b05224a1bd8a4c32ae788" "$l example: quine" + chk "$($vm < examples/primes.zc 2>&1 | md5sum | cut -c1-32)" "814faffc70dad0ae520c0671c33e73d4" "$l example: o3 prime sieve" + chk "$($vm < examples/life.zc 2>&1 | md5sum | cut -c1-32)" "aefb036b6f9ad1dadfc3e6bb47a6885d" "$l example: game of life" + chk "$($vm < tests/arrays.zc | tr '\n' '/')" "100 128 9801 9801 99 3 4 12 19 8 19 28/0/" "$l growable and fixed arrays" + chk "$($vm < tests/o3.zc)" "3142 42 43 9 57 98781 520625 10192 -18782 1152" "$l o3 regions: loops, calls, recursion, switch, arrays, more vars than registers" + chk "$($vm < tests/o3_bounds.zc 2>&1)" "zy: index 7 out of range (length 5)" "$l o3 bounds check" + local r; r=$(tests/runerr.sh "$vm" tests/errors | tail -1) + chk "$r" "rejected: $(ls tests/errors/*.zc | wc -l) passed, 0 failed" "$l analyzer rejects every bad program" + r=$(tests/runerr.sh "$vm" tests/typeerrors | tail -1) + chk "$r" "rejected: $(ls tests/typeerrors/*.zc | wc -l) passed, 0 failed" "$l typechecker rejects every bad program" + r=$(tests/runerr.sh "$vm" tests/langerrors | tail -1) + chk "$r" "rejected: $(ls tests/langerrors/*.zc | wc -l) passed, 0 failed" "$l o3 and array misuse is rejected" +} +gcc -O2 -w -DJTYPE=int core.c io_linux.c -o $T/zy +run $T/zy "interp" +chk "$(ZYTRACE=1 $T/zy < tests/o3.zc 2>&1 | grep -c 'micro-ops$')" "8" "every o3 region lifts" +if command -v aarch64-linux-gnu-gcc >/dev/null && command -v qemu-aarch64 >/dev/null; then + aarch64-linux-gnu-gcc -O2 -w -DJTYPE=int -static core.c io_linux.c -o $T/zyj || exit 1 + printf '#!/bin/sh\nexec qemu-aarch64 %s "$@"\n' $T/zyj > $T/jit; chmod +x $T/jit + run $T/jit "jit" + chk "$(ZYTRACE=1 $T/jit < tests/o3.zc 2>&1 | grep -c 'bytes of arm64')" "8" "jit compiles every o3 region to arm64" + selfc $T/jit $T/gj.bin; cmp -s $T/g1.bin $T/gj.bin && chk 1 1 "jit self-compile matches" || chk 0 1 "jit self-compile matches" +else echo "skip jit (no aarch64 toolchain or qemu)"; fi +if command -v llvm-config >/dev/null; then + gcc -O2 -w -DJTYPE=int -DZYLLVM $(llvm-config --cflags) core.c io_linux.c -o $T/zyl $(llvm-config --ldflags --libs) || exit 1 + run $T/zyl "llvm" + chk "$(ZYTRACE=1 $T/zyl < tests/o3.zc 2>&1 | grep -c 'compiled by llvm')" "8" "llvm compiles every o3 region" + printf '#!/bin/sh\nZYHOT=1 exec %s "$@"\n' $T/zyl > $T/llall; chmod +x $T/llall + chk "$($T/llall < tests/types.zc | md5sum)" "$($T/zy < tests/types.zc | md5sum)" "llvm compiling every block agrees" + selfc $T/zyl $T/gl.bin; cmp -s $T/g1.bin $T/gl.bin && chk 1 1 "llvm self-compile matches" || chk 0 1 "llvm self-compile matches" +else echo "skip llvm (no llvm-config)"; fi +echo "$ok passed, $bad failed"; [ $bad = 0 ] diff --git a/docs/history/15-o3/code/tests/arrays.zc b/docs/history/15-o3/code/tests/arrays.zc new file mode 100644 index 0000000..f8d46d8 --- /dev/null +++ b/docs/history/15-o3/code/tests/arrays.zc @@ -0,0 +1,24 @@ +// growable arrays and fixed ones +struct point { int x, y; }; +fn main() +{ + let before = live_objects(); + { + []int a = new([]int); + for (int i = 0; i < 100; i++) push(a, i * i); + putn(len(a)); putc(' '); putn(cap(a)); putc(' '); putn(a[99]); putc(' '); + putn(pop(a)); putc(' '); putn(len(a)); putc(' '); + resize(a, 3); putn(len(a)); putc(' '); putn(a[2]); putc(' '); + fixed []int f = new(fixed []int, 5); + f[4] = 7; putn(f[4] + len(f)); putc(' '); + []point ps = new([]point, 2); + ps[1].y = 9; resize(ps, 10); putn(ps[1].y + len(ps)); putc(' '); + [8]int st; putn(len(st)); putc(' '); + []*point owned = new([]*point); + for (int i = 0; i < 20; i++) { *point p = new(point); p.x = i; push(owned, d*p); } + putn(owned[19].x); putc(' '); + putn(live_objects() - before); putc(10); + } + putn(live_objects() - before); putc(10); + return 0; +} diff --git a/docs/history/15-o3/code/tests/basics.zc b/docs/history/15-o3/code/tests/basics.zc new file mode 100644 index 0000000..c1f1faf --- /dev/null +++ b/docs/history/15-o3/code/tests/basics.zc @@ -0,0 +1,22 @@ +let table[10]; +let greeting = "hey"; +const N = 10, BIG = 0x100 * 2; +fn fib(n) { if (n < 2) return n; return fib(n - 1) + fib(n - 2); } +fn twice(f, x) { return f(f(x)); } +fn inc(x) { return x + 1; } +fn main() +{ + let i, s = 0, a[5]; + for (i = 0; i < N; i++) table[i] = i * i; + for (let j = 0; j < N; j++) s += table[j]; + putn(s); putc(' '); putn(fib(15)); putc(' '); putn(twice(inc, 40)); putc(' '); putn(BIG); putc(10); + puts(greeting); putc(' '); putn('A'); putc(' '); putn(-17 / 5); putc(' '); putn(-17 % 5); putc(' '); putn(1 << 10); putc(' '); putn(-64 >> 3); putc(10); + i = 0; while (1) { i++; if (i == 3) continue; if (i > 6) break; putn(i); } putc(10); + a[0] = 5; a[1] = a[0]++; a[2] = ++a[0]; putn(a[0]); putn(a[1]); putn(a[2]); putc(10); + let b = { let t = 3; t * t }; + putn(b); putc(' '); putn(3 > 2 && 2 > 1); putn(0 || 0); putn(!5); putn(~0); putn(5 != 5); putn(4 >= 4); putc(10); + let x = asm { 2 3 + }; + putn(x); putc(10); + asm { 'o' . 'k' . 10 . } + return 0; +} diff --git a/docs/history/15-o3/code/tests/bench.zc b/docs/history/15-o3/code/tests/bench.zc new file mode 100644 index 0000000..a7e53f5 --- /dev/null +++ b/docs/history/15-o3/code/tests/bench.zc @@ -0,0 +1,30 @@ +// four kernels with and without o3; bench.sh runs each one on each backend +int csum(int lim) { int s = 0; for (int n = 1; n < lim; n++) { int x = n; while (x != 1) { if (x & 1) x = 3 * x + 1; else x = x / 2; s++; } } return s; } +o3 int csum3(int lim) { int s = 0; for (int n = 1; n < lim; n++) { int x = n; while (x != 1) { if (x & 1) x = 3 * x + 1; else x = x / 2; s++; } } return s; } +int sieve(fixed []int f) { int c = 0, n = len(f); for (int i = 0; i < n; i++) f[i] = 1; for (int i = 2; i < n; i++) if (f[i]) { c++; for (int j = i + i; j < n; j += i) f[j] = 0; } return c; } +o3 int sieve3(fixed []int f) { int c = 0, n = len(f); for (int i = 0; i < n; i++) f[i] = 1; for (int i = 2; i < n; i++) if (f[i]) { c++; for (int j = i + i; j < n; j += i) f[j] = 0; } return c; } +int digits(int n) { int s = 0; for (int i = 0; i < n; i++) { int x = i; while (x) { s = s + x % 10; x = x / 10; } } return s; } +o3 int digits3(int n) { int s = 0; for (int i = 0; i < n; i++) { int x = i; while (x) { s = s + x % 10; x = x / 10; } } return s; } +int fib(int n) { if (n < 2) return n; return fib(n - 1) + fib(n - 2); } +o3 int fib3(int n) { if (n < 2) return n; return fib3(n - 1) + fib3(n - 2); } +// input: a letter (the kernel; z runs none) and how many times to run it +fn main() +{ + fixed []int f = new(fixed []int, 50000); + let which = getc(), reps = 0, ch = getc(), r = 0; + while (ch >= '0' && ch <= '9') { reps = reps * 10 + ch - '0'; ch = getc(); } + while (reps > 0) { + if (which == 'a') r = csum(30000); + if (which == 'b') r = csum3(30000); + if (which == 'c') r = sieve(f); + if (which == 'd') r = sieve3(f); + if (which == 'e') r = digits(300000); + if (which == 'f') r = digits3(300000); + if (which == 'g') r = fib(22); + if (which == 'h') r = fib3(22); + reps--; + } + putn(r); putc(10); + free(d*f); + return 0; +} diff --git a/docs/history/15-o3/code/tests/borrowmove.zc b/docs/history/15-o3/code/tests/borrowmove.zc new file mode 100644 index 0000000..43f27b2 --- /dev/null +++ b/docs/history/15-o3/code/tests/borrowmove.zc @@ -0,0 +1,2 @@ +fn put(.m box, int v) { *int c = new(int); *c = v; box[0] = c; return 0; } +fn main() { let b = new(1); put(b, 6); putn(*(b[0] as *int)); putc(10); return 0; } diff --git a/docs/history/15-o3/code/tests/buddy.zc b/docs/history/15-o3/code/tests/buddy.zc new file mode 100644 index 0000000..2000598 --- /dev/null +++ b/docs/history/15-o3/code/tests/buddy.zc @@ -0,0 +1,15 @@ +// the buddy allocator: 20000 small blocks, freed, merge back into one big one +let keep[20000]; +fn main() +{ + let i = 0, p; + while (i < 20000) { keep[i] = new(2); i++; } // 20000 8-word blocks + putn(largest_free()); putc(' '); + i = 0; while (i < 20000) { free(keep[i]); i++; } + putn(largest_free()); putc(' '); + p = new(200000); // only possible if the small blocks merged back + putn(osize(p) >= 200000); putc(' '); + free(d*p); + putn(live_objects()); putc(10); + return 0; +} diff --git a/docs/history/15-o3/code/tests/enhance.zc b/docs/history/15-o3/code/tests/enhance.zc new file mode 100644 index 0000000..33eb85b --- /dev/null +++ b/docs/history/15-o3/code/tests/enhance.zc @@ -0,0 +1,29 @@ +// custom opcodes: recipes, lifted vm code, several outputs, and a fallback +enhance clampadd(a, b) micro "a = add a b; a = min a 100; a = max a 0"; +enhance cube(x) vm { 0 $ 0 $ * * }; +enhance 2 divmod(a, b) micro "r2 = sdiv a b" "\n r1 = srem a b\n r0 = r2"; +enhance div7(a) micro "a = sdiv a 7"; +enhance urem10(a) micro "a = urem a 10"; +enhance hi(a, b) micro "a = mulhu a b"; +enhance noisy(x) vm { 0 $ . } else soft_noisy; // i/o can't be lifted, so calls use the fallback +fn soft_noisy(x) { return x + 1; } +let qq, rr, seed = 7; +fn rnd() { seed = seed * 1103515245 + 12345; return seed ^ (seed >> 11); } +fn main() +{ + let i = 0, a, bad = 0; + putn(clampadd(70, 50)); putc(' '); putn(clampadd(-50, 2)); putc(' '); putn(cube(7)); putc(' '); + asm { 17 5 divmod rr ! qq ! } + putn(qq); putc(','); putn(rr); putc(' '); + putn(noisy(4)); putc(' '); + putn(hi(-1, -1)); putc(' '); + while (i < 5000) { + a = rnd(); + if (div7(a) != zdiv(a, 7)) bad++; + if (urem10(a) < 0 || urem10(a) > 9) bad++; + if (a / (i + 1) != zdiv(a, i + 1)) bad++; + i++; + } + putn(bad); putc(10); + return 0; +} diff --git a/docs/history/15-o3/code/tests/errors/back_to_m.zc b/docs/history/15-o3/code/tests/errors/back_to_m.zc new file mode 100644 index 0000000..b8c160f --- /dev/null +++ b/docs/history/15-o3/code/tests/errors/back_to_m.zc @@ -0,0 +1,2 @@ +// expect: x: can't be recast back to m +fn main() { let x = new(3); d*x; m*x; return 0; } diff --git a/docs/history/15-o3/code/tests/errors/borrow_escape.zc b/docs/history/15-o3/code/tests/errors/borrow_escape.zc new file mode 100644 index 0000000..ce24480 --- /dev/null +++ b/docs/history/15-o3/code/tests/errors/borrow_escape.zc @@ -0,0 +1,3 @@ +// expect: p: is owned and can't be returned +fn f(.m p) { return p; } +fn main() { let x = new(1); f(x); return 0; } diff --git a/docs/history/15-o3/code/tests/errors/branch_mismatch.zc b/docs/history/15-o3/code/tests/errors/branch_mismatch.zc new file mode 100644 index 0000000..bd19968 --- /dev/null +++ b/docs/history/15-o3/code/tests/errors/branch_mismatch.zc @@ -0,0 +1,3 @@ +// expect: x: ends the branches of an if in different states +fn take(.d p) { return p[0]; } +fn main() { let x = new(1); if (x[0]) take(d*x); else u*x; return 0; } diff --git a/docs/history/15-o3/code/tests/errors/child_dies.zc b/docs/history/15-o3/code/tests/errors/child_dies.zc new file mode 100644 index 0000000..67caca9 --- /dev/null +++ b/docs/history/15-o3/code/tests/errors/child_dies.zc @@ -0,0 +1,3 @@ +// expect: c: used after it was given away +fn take(.d p) { return p[0]; } +fn main() { let p = new(2), c = new(1); p[0] = c; take(d*p); return c[0]; } diff --git a/docs/history/15-o3/code/tests/errors/child_recast.zc b/docs/history/15-o3/code/tests/errors/child_recast.zc new file mode 100644 index 0000000..c9ccd62 --- /dev/null +++ b/docs/history/15-o3/code/tests/errors/child_recast.zc @@ -0,0 +1,2 @@ +// expect: c: belongs to a container; recast the container instead +fn main() { let p = new(2), c = new(1); p[0] = c; d*c; return 0; } diff --git a/docs/history/15-o3/code/tests/errors/copy_owned.zc b/docs/history/15-o3/code/tests/errors/copy_owned.zc new file mode 100644 index 0000000..862c7ef --- /dev/null +++ b/docs/history/15-o3/code/tests/errors/copy_owned.zc @@ -0,0 +1,2 @@ +// expect: x: is an owned object and can't be copied +fn main() { let x = new(1); let y = x; return y[0]; } diff --git a/docs/history/15-o3/code/tests/errors/d_into_block.zc b/docs/history/15-o3/code/tests/errors/d_into_block.zc new file mode 100644 index 0000000..2d7f526 --- /dev/null +++ b/docs/history/15-o3/code/tests/errors/d_into_block.zc @@ -0,0 +1,2 @@ +// expect: x: used after it was given away +fn main() { let x = new(1); d*x; { x[0] = 5; } return x[0]; } diff --git a/docs/history/15-o3/code/tests/errors/d_kept.zc b/docs/history/15-o3/code/tests/errors/d_kept.zc new file mode 100644 index 0000000..dcbfb86 --- /dev/null +++ b/docs/history/15-o3/code/tests/errors/d_kept.zc @@ -0,0 +1,2 @@ +// expect: x: d object was never given away +fn main() { let x = new(3); d*x; x[0] = 1; return 0; } diff --git a/docs/history/15-o3/code/tests/errors/discard_u.zc b/docs/history/15-o3/code/tests/errors/discard_u.zc new file mode 100644 index 0000000..9293378 --- /dev/null +++ b/docs/history/15-o3/code/tests/errors/discard_u.zc @@ -0,0 +1,3 @@ +// expect: can't be thrown away +.u mk() { let p = new(1); return u*p; } +fn main() { mk(); return 0; } diff --git a/docs/history/15-o3/code/tests/errors/give_m.zc b/docs/history/15-o3/code/tests/errors/give_m.zc new file mode 100644 index 0000000..cd67896 --- /dev/null +++ b/docs/history/15-o3/code/tests/errors/give_m.zc @@ -0,0 +1,3 @@ +// expect: x: is m; write d*x +fn take(.d p) { return p[0]; } +fn main() { let x = new(3); take(x); return 0; } diff --git a/docs/history/15-o3/code/tests/errors/global_store.zc b/docs/history/15-o3/code/tests/errors/global_store.zc new file mode 100644 index 0000000..dc0e546 --- /dev/null +++ b/docs/history/15-o3/code/tests/errors/global_store.zc @@ -0,0 +1,3 @@ +// expect: x: must be d to be stored in a global +let g; +fn main() { let x = new(1); g = x; return 0; } diff --git a/docs/history/15-o3/code/tests/errors/leak_overwrite.zc b/docs/history/15-o3/code/tests/errors/leak_overwrite.zc new file mode 100644 index 0000000..d19b5fd --- /dev/null +++ b/docs/history/15-o3/code/tests/errors/leak_overwrite.zc @@ -0,0 +1,2 @@ +// expect: x: still owns an object +fn main() { let x = new(1); x = new(2); return x[0]; } diff --git a/docs/history/15-o3/code/tests/errors/lend_d.zc b/docs/history/15-o3/code/tests/errors/lend_d.zc new file mode 100644 index 0000000..55ac7e0 --- /dev/null +++ b/docs/history/15-o3/code/tests/errors/lend_d.zc @@ -0,0 +1,4 @@ +// expect: x: is d; pass it to a d parameter +fn look(.m p) { return p[0]; } +fn take(.d p) { return p[0]; } +fn main() { let x = new(3); d*x; look(x); take(x); return 0; } diff --git a/docs/history/15-o3/code/tests/errors/loop_move.zc b/docs/history/15-o3/code/tests/errors/loop_move.zc new file mode 100644 index 0000000..df3c94a --- /dev/null +++ b/docs/history/15-o3/code/tests/errors/loop_move.zc @@ -0,0 +1,3 @@ +// expect: x: is given away inside a loop that comes back around to it +fn take(.d p) { return p[0]; } +fn main() { let x = new(1); let i = 0; d*x; while (i < 3) { take(x); i++; } return 0; } diff --git a/docs/history/15-o3/code/tests/errors/plain_return.zc b/docs/history/15-o3/code/tests/errors/plain_return.zc new file mode 100644 index 0000000..45fb545 --- /dev/null +++ b/docs/history/15-o3/code/tests/errors/plain_return.zc @@ -0,0 +1,3 @@ +// expect: returns a new object; give the function a .u return type +fn mk() { return new(1); } +fn main() { return mk(); } diff --git a/docs/history/15-o3/code/tests/errors/return_m.zc b/docs/history/15-o3/code/tests/errors/return_m.zc new file mode 100644 index 0000000..b5ef519 --- /dev/null +++ b/docs/history/15-o3/code/tests/errors/return_m.zc @@ -0,0 +1,3 @@ +// expect: x: has to be u to be returned +.u mk() { let x = new(1); return x; } +fn main() { let y = mk(); return y[0]; } diff --git a/docs/history/15-o3/code/tests/errors/tag_mismatch.zc b/docs/history/15-o3/code/tests/errors/tag_mismatch.zc new file mode 100644 index 0000000..079f249 --- /dev/null +++ b/docs/history/15-o3/code/tests/errors/tag_mismatch.zc @@ -0,0 +1,2 @@ +// expect: c: its tag doesn't match its container's +fn main() { let p = new(2), c = new(1); u*p; p[0] = c; return 0; } diff --git a/docs/history/15-o3/code/tests/errors/u_dropped.zc b/docs/history/15-o3/code/tests/errors/u_dropped.zc new file mode 100644 index 0000000..e1ca68a --- /dev/null +++ b/docs/history/15-o3/code/tests/errors/u_dropped.zc @@ -0,0 +1,2 @@ +// expect: x: u object must leave this block +fn main() { let x = new(3); u*x; return 0; } diff --git a/docs/history/15-o3/code/tests/errors/u_skip_level.zc b/docs/history/15-o3/code/tests/errors/u_skip_level.zc new file mode 100644 index 0000000..6caeeb3 --- /dev/null +++ b/docs/history/15-o3/code/tests/errors/u_skip_level.zc @@ -0,0 +1,2 @@ +// expect: t: is u; it can only be handed to a variable of the enclosing block +fn main() { let r; { { let t = new(1); u*t; r = t; } } return r[0]; } diff --git a/docs/history/15-o3/code/tests/errors/unchecked_touch.zc b/docs/history/15-o3/code/tests/errors/unchecked_touch.zc new file mode 100644 index 0000000..141639d --- /dev/null +++ b/docs/history/15-o3/code/tests/errors/unchecked_touch.zc @@ -0,0 +1,2 @@ +// expect: x: is tracked by the analyzer and can't be used in unchecked code +fn main() { let x = new(1); unchecked { x[0] = 1; } return 0; } diff --git a/docs/history/15-o3/code/tests/errors/unused.zc b/docs/history/15-o3/code/tests/errors/unused.zc new file mode 100644 index 0000000..64aff1f --- /dev/null +++ b/docs/history/15-o3/code/tests/errors/unused.zc @@ -0,0 +1,2 @@ +// expect: x: m object is never used +fn main() { let x = new(3); return 0; } diff --git a/docs/history/15-o3/code/tests/errors/use_after_give.zc b/docs/history/15-o3/code/tests/errors/use_after_give.zc new file mode 100644 index 0000000..8a10922 --- /dev/null +++ b/docs/history/15-o3/code/tests/errors/use_after_give.zc @@ -0,0 +1,3 @@ +// expect: x: used after it was given away +fn take(.d p) { return p[0]; } +fn main() { let x = new(3); take(d*x); return x[0]; } diff --git a/docs/history/15-o3/code/tests/gc.zc b/docs/history/15-o3/code/tests/gc.zc new file mode 100644 index 0000000..41f14a0 --- /dev/null +++ b/docs/history/15-o3/code/tests/gc.zc @@ -0,0 +1,20 @@ +let keep; +unchecked fn churn(n) +{ + let i = 0, p; + while (i < n) { p = new(10); p[0] = i; if (i % 100 == 0) keep = p; i++; } + return 0; +} +fn main() +{ + let base = live_objects(), f; + churn(1000); + putn(live_objects() - base); putc(' '); + f = gc(); + putn(f >= 990); putc(' '); // conservative: a stale word or two may keep one alive + putn(keep[0]); putc(' '); // the one a global still points at survives + churn(200000); // 3.2m words through a 393k word heap: needs the automatic collector + putn(gc_runs > 1); putc(' '); + putn(keep[0]); putc(10); + return 0; +} diff --git a/docs/history/15-o3/code/tests/joins.zc b/docs/history/15-o3/code/tests/joins.zc new file mode 100644 index 0000000..913929b --- /dev/null +++ b/docs/history/15-o3/code/tests/joins.zc @@ -0,0 +1,32 @@ +// joins: something given away on one path is dead afterwards, and the paths +// that still own it free it on the way in. nothing here leaks. +fn take(.d p) { return p[0]; } +fn later(.d p); // not needed any more, but still allowed +*int.u mk(int v) { *int p = new(int); *p = v; return u*p; } +fn count(int n) { let s = 0; switch (n % 4) { case 0: s = 10; case 1, 2: s = 20; break; default: s = 30; } return s; } +fn main() +{ + let base = live_objects(), i = 0, hits = 0, k; + while (i < 50) { + let x = mk(i); + if (i % 3 == 0) take(d*x); // else: freed here by the fixup + i++; + } + putn(live_objects() - base); putc(' '); + // give away and break: fine now; the normal exit frees it + i = 0; + let y = mk(7); + while (i < 10) { if (i == 4) { take(d*y); break; } i++; } + putn(live_objects() - base); putc(' '); + let z = mk(5); + while (1) { if (z[0] == 5) { hits = take(d*z); break; } } + putn(hits); putc(' '); + putn(count(0) + count(1) + count(2) + count(3)); putc(' '); + k = i > 3 ? i * 2 : 0 - 1; + putn(k); putc(' '); + putn(forward(new(int))); putc(' '); // tagged and defined below: the prescan knows .d + **int pp; *int q = new(int); *q = 9; pp = &q; putn(**pp); free(d*q); putc(' '); + putn(live_objects() - base); putc(10); + return 0; +} +fn forward(*int.d p) { return 3 + *p; } diff --git a/docs/history/15-o3/code/tests/langerrors/const_index.zc b/docs/history/15-o3/code/tests/langerrors/const_index.zc new file mode 100644 index 0000000..03e927b --- /dev/null +++ b/docs/history/15-o3/code/tests/langerrors/const_index.zc @@ -0,0 +1,2 @@ +// expect: the index is out of range for this array +fn main() { [4]int a; a[4] = 1; return 0; } diff --git a/docs/history/15-o3/code/tests/langerrors/fixed_nolen.zc b/docs/history/15-o3/code/tests/langerrors/fixed_nolen.zc new file mode 100644 index 0000000..b79c31e --- /dev/null +++ b/docs/history/15-o3/code/tests/langerrors/fixed_nolen.zc @@ -0,0 +1,2 @@ +// expect: a fixed array needs its length +fn main() { fixed []int f = new(fixed []int); free(d*f); return 0; } diff --git a/docs/history/15-o3/code/tests/langerrors/fixed_push.zc b/docs/history/15-o3/code/tests/langerrors/fixed_push.zc new file mode 100644 index 0000000..a19bcb4 --- /dev/null +++ b/docs/history/15-o3/code/tests/langerrors/fixed_push.zc @@ -0,0 +1,2 @@ +// expect: a fixed array can't grow or shrink +fn main() { fixed []int f = new(fixed []int, 3); push(f, 1); free(d*f); return 0; } diff --git a/docs/history/15-o3/code/tests/langerrors/fixed_to_dyn.zc b/docs/history/15-o3/code/tests/langerrors/fixed_to_dyn.zc new file mode 100644 index 0000000..483f62f --- /dev/null +++ b/docs/history/15-o3/code/tests/langerrors/fixed_to_dyn.zc @@ -0,0 +1,4 @@ +// expect: type error: argument: expected []int, got fixed []int +typecheck on; +int take([]int.m g) { return len(g); } +fn main() { fixed []int f = new(fixed []int, 3); take(f); free(d*f); return 0; } diff --git a/docs/history/15-o3/code/tests/langerrors/o3_addr.zc b/docs/history/15-o3/code/tests/langerrors/o3_addr.zc new file mode 100644 index 0000000..815253d --- /dev/null +++ b/docs/history/15-o3/code/tests/langerrors/o3_addr.zc @@ -0,0 +1,2 @@ +// expect: an o3 variable has no address +fn main() { o3 int z = 2; *int q = &z; return *q; } diff --git a/docs/history/15-o3/code/tests/langerrors/o3_asm.zc b/docs/history/15-o3/code/tests/langerrors/o3_asm.zc new file mode 100644 index 0000000..c8afb44 --- /dev/null +++ b/docs/history/15-o3/code/tests/langerrors/o3_asm.zc @@ -0,0 +1,2 @@ +// expect: asm can't reach an o3 variable +fn main() { o3 int zz = 2; asm { zz @ _ } return 0; } diff --git a/docs/history/15-o3/code/tests/langerrors/o3_let.zc b/docs/history/15-o3/code/tests/langerrors/o3_let.zc new file mode 100644 index 0000000..d2aff9a --- /dev/null +++ b/docs/history/15-o3/code/tests/langerrors/o3_let.zc @@ -0,0 +1,2 @@ +// expect: o3 variables need a type +fn main() { o3 { let x = 1; x++; } return 0; } diff --git a/docs/history/15-o3/code/tests/langerrors/o3_param.zc b/docs/history/15-o3/code/tests/langerrors/o3_param.zc new file mode 100644 index 0000000..5694720 --- /dev/null +++ b/docs/history/15-o3/code/tests/langerrors/o3_param.zc @@ -0,0 +1,3 @@ +// expect: o3 parameters need a type +o3 int f(x) { return x; } +fn main() { return f(1); } diff --git a/docs/history/15-o3/code/tests/manyerrors.zc b/docs/history/15-o3/code/tests/manyerrors.zc new file mode 100644 index 0000000..f4c0c68 --- /dev/null +++ b/docs/history/15-o3/code/tests/manyerrors.zc @@ -0,0 +1,6 @@ +// several errors in one run: a syntax error, an analyzer error, a type error +typecheck on; +fn f() { let a = ; return 0; } +fn g() { let x = new(3); return 0; } +int h() { *char s = 5; return 1; } +fn main() { return 0 } diff --git a/docs/history/15-o3/code/tests/meta.zc b/docs/history/15-o3/code/tests/meta.zc new file mode 100644 index 0000000..bc89454 --- /dev/null +++ b/docs/history/15-o3/code/tests/meta.zc @@ -0,0 +1,46 @@ +fn ipow(a, b) { let r = 1; while (b > 0) { r *= a; b--; } return r; } +// a parse word that adds an operator to the compiler's tables +immediate fn pow_op() { next(); add_binop("**", 11, "ipow c"); return 0; } +pow_op + +// unless (cond) stmt: a parse word that emits code itself +immediate fn unless() +{ + let a; + next(); + expect(intern("(")); rval(); plainuse(expr()); rval(); expect(intern(")")); + emitseq(useq[0], ulen[0]); // not + a = hole(); op(O_JZ); // skip the body when the condition was true + substmt(); + setlit(a, here); + return 0; +} +// repeat N { body }: a macro, built from token lists +immediate fn repeat() +{ + let n, body, i = 0; + next(); n = tv; next(); + body = grab(); + while (i < n) { feed(qdup(body)); i++; } + free(d*body); + return 0; +} +fn main() +{ + putn(2 ** 10); putc(' '); putn(3 + 2 ** 3 * 2); putc(10); + unless (1 > 2) puts("unless ok\n"); + unless (2 > 1) puts("wrong\n"); + repeat 3 { putc('r'); } + putc(10); + let q = quote { putn(40 + 2); putc(10); return 7; }; + putn(q[0]); putc(' '); + putn(eval(q)); putc(10); + let f = compile(q); + putn(f()); putc(10); + free(d*q); + let defs = quote { fn sq(x) { return x * x; } }; + load(defs); + free(d*defs); + putn(evals("return sq(12);")); putc(10); + return 0; +} diff --git a/docs/history/15-o3/code/tests/misc.zc b/docs/history/15-o3/code/tests/misc.zc new file mode 100644 index 0000000..25fc53b --- /dev/null +++ b/docs/history/15-o3/code/tests/misc.zc @@ -0,0 +1,30 @@ +// ct { ... } runs a block while compiling and leaves its result as a constant +immediate fn ct() +{ + let q, v; + next(); + q = grab(); + v = eval(q); + free(d*q); + lit(v); + return 0; +} +// teach the asm syntax that p means out, like the old repl demo +immediate fn teach() { next(); mnem[12] = 'p'; return 0; } +teach + +fn countdown() +{ + asm { 5 :top 0 $ '0' + p 1 - 0 $ done z top j :done _ 10 p } + return 0; +} +fn main() +{ + putn(ct { let i = 0, s = 0; while (i < 10) { s += i; i++; } return s; }); putc(10); + countdown(); + puts(lazy()); putc(10); + return 0; +} +// the analyzer is off from here on +unchecked; +fn lazy() { let p = new(3); p[0] = 'o'; p[1] = 'k'; return p; } diff --git a/docs/history/15-o3/code/tests/o3.zc b/docs/history/15-o3/code/tests/o3.zc new file mode 100644 index 0000000..296d1d5 --- /dev/null +++ b/docs/history/15-o3/code/tests/o3.zc @@ -0,0 +1,83 @@ +// o3 regions: every kind of control flow, against the same code without o3 +struct pt { int x; int y; }; +int g = 3; +int sq(int x) { return x * x; } +o3 int collatz(int n) +{ + int steps = 0; + while (n != 1) { if (n % 2 == 0) n = n / 2; else n = 3 * n + 1; steps++; } + return steps; +} +o3 int ack(int am, int n) +{ + if (am == 0) return n + 1; + if (n == 0) return ack(am - 1, 1); + return ack(am - 1, ack(am, n - 1)); +} +o3 int many(int a, int b, int c) +{ + int dd = a + b, e = b + c, f = c + a, h = dd * e, i2 = e * f, j = f * dd, k = h + i2 + j, l = k / 7; + for (int t = 0; t < 5; t++) { dd += t; e -= t; f = f ^ t; l = l + dd * e - f; } + return dd + e + f + h + i2 + j + k + l; +} +o3 int mix(int n) +{ + int acc = 0; + for (int i = 0; i < n; i++) { + if (i % 3 == 0 && i % 5 == 0) acc += 15; + else if (i % 3 == 0 || i > 90) acc += 3; + else acc = acc + (i & 1 ? sq(i) : g); + switch (i % 4) { case 0: acc += 1; case 1: acc -= 2; default: acc = acc ^ 1; } + if (acc > 100000) break; + if (i == 7) continue; + acc = acc + i / 10 - i % 7 + (i << 2) - (i >> 1); + } + return acc; +} +o3 int dyn(int n) +{ + []int a = new([]int, n); + fixed []int f = new(fixed []int, n); + int s = 0; + for (int i = 0; i < len(a); i++) { a[i] = i * i; f[i] = n - i; } + for (int i = 0; i < n; i++) s = s + a[i] * f[i]; + free(d*a); free(d*f); + return s; +} +o3 int pts(int n) +{ + *pt p = new(pt); + int s = 0; + p->x = 1; p->y = 2; + for (int i = 0; i < n; i++) { p->x = p->x + p->y; p->y = p->x - p->y; s = s + p->x % 1000; } + free(d*p); + return s; +} +o3 int divs(int x) +{ + int s = 0; + for (int i = -50; i < 50; i++) s = s + (x + i) / 7 + (x + i) % 7 + (x - i) / -3 + i / 16 + i % 16; + return s; +} +fn main() +{ + o3 int lone = 2; + let r = 0; + lone = lone * 21; + o3 { + int i = 0, s = 0; + while (i < 100) { s = s + collatz(i + 1); i++; } + putn(s); putc(' '); + putn(lone); putc(' '); + lone++; + } + putn(lone); putc(' '); + putn(ack(2, 3)); putc(' '); + putn(many(1, 2, 3)); putc(' '); + putn(mix(100)); putc(' '); + putn(dyn(50)); putc(' '); + putn(pts(30)); putc(' '); + putn(divs(1000)); putc(' '); + putn(divs(-77)); putc(10); + return 0; +} diff --git a/docs/history/15-o3/code/tests/o3_bounds.zc b/docs/history/15-o3/code/tests/o3_bounds.zc new file mode 100644 index 0000000..fb347c0 --- /dev/null +++ b/docs/history/15-o3/code/tests/o3_bounds.zc @@ -0,0 +1,2 @@ +// an index outside a []T inside o3 code: caught by the inline check +fn main() { []int a = new([]int, 5); o3 { int i = 7; a[i] = 1; } free(d*a); return 0; } diff --git a/docs/history/15-o3/code/tests/overflow_data.zc b/docs/history/15-o3/code/tests/overflow_data.zc new file mode 100644 index 0000000..3042f0a --- /dev/null +++ b/docs/history/15-o3/code/tests/overflow_data.zc @@ -0,0 +1 @@ +fn main() { while (1) asm { 1 } return 0; } diff --git a/docs/history/15-o3/code/tests/overflow_frame.zc b/docs/history/15-o3/code/tests/overflow_frame.zc new file mode 100644 index 0000000..3897bf4 --- /dev/null +++ b/docs/history/15-o3/code/tests/overflow_frame.zc @@ -0,0 +1,2 @@ +fn big(n) { let a[500]; a[0] = n; return big(n + 1) + a[0]; } +fn main() { return big(0); } diff --git a/docs/history/15-o3/code/tests/overflow_return.zc b/docs/history/15-o3/code/tests/overflow_return.zc new file mode 100644 index 0000000..9d5c622 --- /dev/null +++ b/docs/history/15-o3/code/tests/overflow_return.zc @@ -0,0 +1,2 @@ +fn deep(n) { return deep(n + 1) + 1; } +fn main() { return deep(0); } diff --git a/docs/history/15-o3/code/tests/runerr.sh b/docs/history/15-o3/code/tests/runerr.sh new file mode 100755 index 0000000..23ea054 --- /dev/null +++ b/docs/history/15-o3/code/tests/runerr.sh @@ -0,0 +1,10 @@ +#!/bin/bash +# runs every .zc in a folder (tests/errors by default) and checks the compiler +# rejects it with the message on its first line: // expect: ... +vm=${1:-zy}; dir=${2:-$(dirname "$0")/errors}; pass=0; fail=0 +for f in "$dir"/*.zc; do + want=$(head -1 "$f" | sed 's#^// expect: ##') + got=$($vm < "$f" 2>&1 | head -1) + if [[ "$got" == *"$want"* ]]; then pass=$((pass+1)); else fail=$((fail+1)); echo "FAIL $(basename $f): got [$got]"; fi +done +echo "rejected: $pass passed, $fail failed" diff --git a/docs/history/15-o3/code/tests/typeerrors/arg.zc b/docs/history/15-o3/code/tests/typeerrors/arg.zc new file mode 100644 index 0000000..909672a --- /dev/null +++ b/docs/history/15-o3/code/tests/typeerrors/arg.zc @@ -0,0 +1,5 @@ +// expect: type error: argument: expected *point, got *int +typecheck on; +struct point { int x, y; }; +int f(*point p) { return p.x; } +fn main() { *int q = new(int); f(q); free(d*q); return 0; } diff --git a/docs/history/15-o3/code/tests/typeerrors/arith.zc b/docs/history/15-o3/code/tests/typeerrors/arith.zc new file mode 100644 index 0000000..76e3111 --- /dev/null +++ b/docs/history/15-o3/code/tests/typeerrors/arith.zc @@ -0,0 +1,3 @@ +// expect: type error: arithmetic needs numbers, got *int +typecheck on; +fn main() { *int p = 0; int x = p * 2; return x; } diff --git a/docs/history/15-o3/code/tests/typeerrors/assign.zc b/docs/history/15-o3/code/tests/typeerrors/assign.zc new file mode 100644 index 0000000..8b8a888 --- /dev/null +++ b/docs/history/15-o3/code/tests/typeerrors/assign.zc @@ -0,0 +1,3 @@ +// expect: type error: initializer: expected *char, got int +typecheck on; +fn main() { *char s = 5; return 0; } diff --git a/docs/history/15-o3/code/tests/typeerrors/call.zc b/docs/history/15-o3/code/tests/typeerrors/call.zc new file mode 100644 index 0000000..7b03dbf --- /dev/null +++ b/docs/history/15-o3/code/tests/typeerrors/call.zc @@ -0,0 +1,3 @@ +// expect: type error: only functions can be called, got int +typecheck on; +fn main() { int k = 3; return k(1); } diff --git a/docs/history/15-o3/code/tests/typeerrors/deref.zc b/docs/history/15-o3/code/tests/typeerrors/deref.zc new file mode 100644 index 0000000..ed030f5 --- /dev/null +++ b/docs/history/15-o3/code/tests/typeerrors/deref.zc @@ -0,0 +1,3 @@ +// expect: type error: only pointers can be dereferenced, got int +typecheck on; +fn main() { int x = 3; return *x; } diff --git a/docs/history/15-o3/code/tests/typeerrors/field.zc b/docs/history/15-o3/code/tests/typeerrors/field.zc new file mode 100644 index 0000000..ba03697 --- /dev/null +++ b/docs/history/15-o3/code/tests/typeerrors/field.zc @@ -0,0 +1,3 @@ +// expect: point has no field z +struct point { int x, y; }; +fn main() { point p; return p.z; } diff --git a/docs/history/15-o3/code/tests/typeerrors/generic.zc b/docs/history/15-o3/code/tests/typeerrors/generic.zc new file mode 100644 index 0000000..6d4c4da --- /dev/null +++ b/docs/history/15-o3/code/tests/typeerrors/generic.zc @@ -0,0 +1,4 @@ +// expect: type error: argument: expected int, got *char +typecheck on; + T same(T a, T b) { return a; } +fn main() { return same(1, "x"); } diff --git a/docs/history/15-o3/code/tests/typeerrors/notstruct.zc b/docs/history/15-o3/code/tests/typeerrors/notstruct.zc new file mode 100644 index 0000000..0dff6bd --- /dev/null +++ b/docs/history/15-o3/code/tests/typeerrors/notstruct.zc @@ -0,0 +1,2 @@ +// expect: .x: that's a int, not a struct or a pointer to one +fn main() { int n = 1; return n.x; } diff --git a/docs/history/15-o3/code/tests/typeerrors/ret.zc b/docs/history/15-o3/code/tests/typeerrors/ret.zc new file mode 100644 index 0000000..be951d5 --- /dev/null +++ b/docs/history/15-o3/code/tests/typeerrors/ret.zc @@ -0,0 +1,4 @@ +// expect: type error: return value: expected bool, got *char +typecheck on; +bool f() { return "no"; } +fn main() { return f(); } diff --git a/docs/history/15-o3/code/tests/typeerrors/voidret.zc b/docs/history/15-o3/code/tests/typeerrors/voidret.zc new file mode 100644 index 0000000..86c9c17 --- /dev/null +++ b/docs/history/15-o3/code/tests/typeerrors/voidret.zc @@ -0,0 +1,4 @@ +// expect: type error: return value: expected void, got int +typecheck on; +void f() { return 3; } +fn main() { f(); return 0; } diff --git a/docs/history/15-o3/code/tests/typeoff.zc b/docs/history/15-o3/code/tests/typeoff.zc new file mode 100644 index 0000000..e44e450 --- /dev/null +++ b/docs/history/15-o3/code/tests/typeoff.zc @@ -0,0 +1,2 @@ +// the typechecker is off unless asked for: this mismatch compiles and runs +fn main() { *char s = 5; putn(s as int + 1); putc(10); return 0; } diff --git a/docs/history/15-o3/code/tests/typeok.zc b/docs/history/15-o3/code/tests/typeok.zc new file mode 100644 index 0000000..2d1997b --- /dev/null +++ b/docs/history/15-o3/code/tests/typeok.zc @@ -0,0 +1,21 @@ +// passes the typechecker: words mix with everything, literal 0 is null, +// arrays decay to pointers, generic calls bind their parameters +typecheck on; +struct pair { A a; B b; }; + *pair.u mk(A x, B y) { *pair p = new(pair); p.a = x; p.b = y; return u*p; } + A left(*pair p) { return p.a; } +int strlen2(*char s) { int n = 0; while (s[n]) n++; return n; } +int main() { + *pair p = mk(3, "four"); + int a = left(p); + *char s = p.b; + [8]char buf; + *char q = buf; + *int nothing = 0; + let w = 5; + int mixed = w + 1; + q[0] = 'h'; q[1] = 0; + putn(a + strlen2(s) + strlen2(buf) + mixed); putc(10); + free(d*p); + return 0; +} diff --git a/docs/history/15-o3/code/tests/types.zc b/docs/history/15-o3/code/tests/types.zc new file mode 100644 index 0000000..3408d10 --- /dev/null +++ b/docs/history/15-o3/code/tests/types.zc @@ -0,0 +1,56 @@ +struct point { int x, y; }; +struct rect { point a, b; int tag; }; +struct list { int len; *T items; }; +struct node { int v; *node next; }; +type num = int; + +int counter = 5; +[4]int squares; +point origin; + +int area(*rect r) { return (r.b.x - r.a.x) * (r.b.y - r.a.y); } + *list.u mklist(int cap) { + *list l = new(list); + l.len = 0; + l.items = new(T, cap) as *T; + return u*l; +} + T first(*list l) { return l.items[0]; } +int sum(*node n) { int s = 0; while (n) { s += n.v; n = n.next; } return s; } + +fn main() +{ + rect r; + r.a.x = 1; r.a.y = 2; r.b.x = 4; r.b.y = 6; + putn(area(&r)); putc(' '); + putn(sizeof(rect)); putc(' '); putn(sizeof(point)); putc(' '); putn(sizeof([3]rect)); putc(' '); + let p = new(point); // let infers *point + p.x = 7; p->y = 8; + putn(p.x * p.y); putc(' '); + free(d*p); + *list l = mklist(4); + l.items[0] = 42; l.len = 1; + putn(first(l)); putc(' '); + free(d*l); + *node a = new(node), b = new(node); + a.v = 3; b.v = 4; a.next = b; + putn(sum(a)); putc(' '); + free(d*a); // b went with it: a.next = b made a its owner + [3]point pts; + pts[2].y = 9; + putn(pts[2].y + counter); putc(' '); + num k = 3; + putn(k); putc(10); + + // reflection + tprint(typeof(r)); putc(' '); tprint(typeof(&r)); putc(' '); tprint(typeof(l)); putc(' '); + tprint(sigof(mklist)); putc(' '); tprint(sigof(first)); putc(' '); tprint(sigofs("free")); putc(10); + let t = typeof(r), i = 0; + while (i < tnfields(t)) { puts(tfname(t, i)); putc(':'); tprint(tftype(t, i)); putc('@'); putn(tfoff(t, i)); putc(' '); i++; } + putc(10); + *point q = new(point); + tprint(typeof_obj(q)); putc(' '); putn(typeof_obj(q) == typeof(point)); putc(' '); putn(tkind(typeof_obj(q)) == TY_STRUCT); + free(d*q); + putc(10); + return 0; +} diff --git a/docs/history/15-o3/code/tests/umd.zc b/docs/history/15-o3/code/tests/umd.zc new file mode 100644 index 0000000..497c9fc --- /dev/null +++ b/docs/history/15-o3/code/tests/umd.zc @@ -0,0 +1,51 @@ +// lists as [len, items...], items owned by the list +.u mklist(n) { + let l = new(n + 1); + l[0] = 0; + return u*l; +} +fn push(.m l, .d item) { l[l[0] + 1] = d*item; l[0] = l[0] + 1; return 0; } +fn sum(.m l) { let s = 0, i = 1; while (i <= l[0]) { s += l[i][0]; i++; } return s; } +.u box(v) { let b = new(1); b[0] = v; return u*b; } +fn consume(.d b) { return b[0]; } + +fn main() +{ + let base = live_objects(); + { + let l = mklist(8); // u from mklist arrives m*here + let i = 0; + while (i < 5) { + let b = box(i * 10); // m in the loop body + d*b; // give it down + push(l, b); + i++; + } + putn(sum(l)); putc(' '); + putn(live_objects() - base); putc(' '); // list + 5 boxes + } + putn(live_objects() - base); putc(' '); // freed at the block's end, children too + + // u handed up one block by assignment, and as a block value + let outer; + { + let t = box(7); + u*t; + outer = t; + } + let v = { let w = box(8); u*w }; + putn(outer[0] + v[0]); putc(' '); + + // d given away to a callee + let g = box(99); + putn(consume(d*g)); putc(' '); + + // containers: child takes its parent's tag + let p = new(2), c = new(1); + c[0] = 5; + p[0] = c; + p[1] = new(3); + putn(p[0][0]); putc(' '); + putn(live_objects() - base); putc(10); + return 0; +} diff --git a/docs/history/15-o3/code/tools/build.zc b/docs/history/15-o3/code/tools/build.zc new file mode 100644 index 0000000..a38e97f --- /dev/null +++ b/docs/history/15-o3/code/tools/build.zc @@ -0,0 +1,13 @@ +// compiles the rest of stdin (after the backtick) as a genome image at GORG +// and writes it to stdout as little-endian 32-bit words. +const OUT = 0x10000; +fn main() +{ + let n = build_image(GORG, OUT), i = 0, w; + while (i < n) { + w = OUT[i]; + putc(w); putc(w >> 8); putc(w >> 16); putc(w >> 24); + i++; + } + return 0; +} diff --git a/docs/history/15-o3/code/zc0.c b/docs/history/15-o3/code/zc0.c new file mode 100644 index 0000000..7615cd7 --- /dev/null +++ b/docs/history/15-o3/code/zc0.c @@ -0,0 +1,461 @@ +/* zc0: stage 0 of the bootstrap. a small c compiler for the subset of zc that + compiler.zc is written in. it reads zc source on stdin and writes a genome + header (vm words placed at GORG) on stdout, in the same image layout the + resident compiler's image mode produces. no lifetime analysis, no quote, no + parse words, no block expressions: those only exist in compiler.zc. tags + (m, d, -> u) and casts (as u, as d) are parsed and recorded in signatures + but not checked. */ +#include +#include +#include +#include + +#define GORG 0xC0000 +#define ZFP 0xA0000 /* frame pointer word */ +#define FTOP 0xF0000 /* frame stack grows down from here */ +#define MAXW 0x40000 + +enum { T_EOF, T_NUM, T_ID, T_STR, T_OP }; +enum { K_FN = 1, K_GVAR, K_GARR, K_CONST, K_KW, K_ENH }; +enum { O_Q, O_LD, O_ST, O_PICK, O_DROP, O_SWAP, O_CALL, O_JMP, O_JZ, O_RET, + O_IN, O_EMPTY, O_OUT, O_ADD, O_SUB, O_MUL, O_AND, O_OR, O_XOR, O_SHR, O_LT }; +static const char *mn = "q@!$_~cjzr,?.+-*&|^><%"; + +static char *src; static int pos, line = 1; +static int tk, tv, glue; static char ts[4096]; /* current token */ + +static int img[MAXW], here = GORG; +static void emit(int w) { if (here - GORG >= MAXW) { fprintf(stderr, "image too big\n"); exit(1); } img[here++ - GORG] = w; } +static void lit(int v) { emit((int)((unsigned)v * 2 + 1)); } +static void op(int k) { emit(k * 2); } +static void setlit(int at, int v) { img[at - GORG] = (int)((unsigned)v * 2 + 1); } + +static void die(const char *m) { fprintf(stderr, "zc0: line %d: %s (at '%s')\n", line, m, ts); exit(1); } + +/* ---- lexer ---- */ +static const char *ops3[] = { "<<=", ">>=", 0 }; +static const char *ops2[] = { "==", "!=", "<=", ">=", "<<", ">>", "&&", "||", "++", "--", "+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "->", 0 }; +static int esc(void) +{ + int c = src[pos++]; + if (c != '\\') return c; + c = src[pos++]; + switch (c) { case 'n': return 10; case 't': return 9; case 'r': return 13; case '0': return 0; case 'e': return 27; } + return c; +} +static void next(void) +{ + int ws = 0, i; + for (;;) { + while (src[pos] && isspace((unsigned char)src[pos])) { if (src[pos] == '\n') line++; pos++; ws = 1; } + if (src[pos] == '/' && src[pos + 1] == '/') { while (src[pos] && src[pos] != '\n') pos++; ws = 1; continue; } + if (src[pos] == '/' && src[pos + 1] == '*') { pos += 2; while (src[pos] && !(src[pos] == '*' && src[pos + 1] == '/')) { if (src[pos] == '\n') line++; pos++; } if (src[pos]) pos += 2; ws = 1; continue; } + break; + } + glue = !ws; + if (!src[pos] || src[pos] == '`') { tk = T_EOF; strcpy(ts, ""); return; } + if (isdigit((unsigned char)src[pos])) { + char *e; tv = (int)strtoul(src + pos, &e, 0); memcpy(ts, src + pos, e - src - pos); ts[e - src - pos] = 0; pos = e - src; tk = T_NUM; return; + } + if (isalpha((unsigned char)src[pos]) || src[pos] == '_') { + i = 0; while (isalnum((unsigned char)src[pos]) || src[pos] == '_') ts[i++] = src[pos++]; + ts[i] = 0; tk = T_ID; return; + } + if (src[pos] == '\'') { pos++; tv = esc(); if (src[pos++] != '\'') die("bad char literal"); tk = T_NUM; strcpy(ts, "'c'"); return; } + if (src[pos] == '"') { pos++; i = 0; while (src[pos] && src[pos] != '"') ts[i++] = (char)esc(); ts[i] = 0; pos++; tk = T_STR; return; } + tk = T_OP; + for (i = 0; ops3[i]; i++) if (!strncmp(src + pos, ops3[i], 3)) { strcpy(ts, ops3[i]); pos += 3; return; } + for (i = 0; ops2[i]; i++) if (!strncmp(src + pos, ops2[i], 2)) { strcpy(ts, ops2[i]); pos += 2; return; } + ts[0] = src[pos++]; ts[1] = 0; +} +static int is(const char *s) { return (tk == T_OP || tk == T_ID) && !strcmp(ts, s); } +static int accept(const char *s) { if (is(s)) { next(); return 1; } return 0; } +static void expect(const char *s) { if (!accept(s)) { char b[64]; sprintf(b, "expected '%s'", s); die(b); } } +static char *ident(void) { static char b[8][256]; static int k; char *r = b[k++ & 7]; if (tk != T_ID) die("expected a name"); strcpy(r, ts); next(); return r; } + +/* ---- symbols ---- */ +typedef struct { char name[64]; int kind, val, sig, defd; } Sym; +static Sym sy[4096]; static int nsy; +typedef struct { int at, sym; } Fix; +static Fix fx[65536]; static int nfx; +static Sym *find(const char *n) { int i; for (i = nsy; i--;) if (!strcmp(sy[i].name, n)) return &sy[i]; return 0; } +static Sym *add(const char *n, int kind, int val) +{ + Sym *s = find(n); + if (s && s->kind == K_FN && kind == K_FN && !s->defd) { s->val = val; return s; } /* prototype or forward use */ + s = &sy[nsy++]; strncpy(s->name, n, 63); s->kind = kind; s->val = val; s->defd = 1; s->sig = 0; return s; +} +static void fnref(const char *n) /* push a function address, fixing it up later if needed */ +{ + Sym *s = find(n); + if (!s) { s = add(n, K_FN, 0); s->defd = 0; } + if (s->kind != K_FN) die("not a function"); + if (s->defd) { lit(s->val); return; } + fx[nfx].at = here; fx[nfx++].sym = (int)(s - sy); lit(0); +} + +/* ---- locals ---- */ +typedef struct { char name[64]; int slot, arr; } Loc; +static Loc lo[1024]; static int nlo, nslot, infn; +static Loc *findl(const char *n) { int i; for (i = nlo; i--;) if (!strcmp(lo[i].name, n)) return &lo[i]; return 0; } +static void laddr(int k) { lit(ZFP); op(O_LD); if (k) { lit(k); op(O_ADD); } } + +/* ---- operator tables ---- */ +static void seq_isz(void) { lit(0); op(O_PICK); lit(0); op(O_SWAP); op(O_SUB); op(O_OR); lit(31); op(O_SHR); lit(1); op(O_ADD); } +static void seq_nz(void) { lit(0); op(O_PICK); lit(0); op(O_SWAP); op(O_SUB); op(O_OR); lit(31); op(O_SHR); lit(0); op(O_SWAP); op(O_SUB); } +static int binprec(void) +{ + static const char *n[] = { "*", "/", "%", "+", "-", "<<", ">>", "<", ">", "<=", ">=", "==", "!=", "&", "^", "|", "&&", "||", 0 }; + static int p[] = { 10, 10, 10, 9, 9, 8, 8, 7, 7, 7, 7, 6, 6, 5, 4, 3, 2, 1 }; + int i; + if (tk != T_OP) return 0; + for (i = 0; n[i]; i++) if (!strcmp(ts, n[i])) return p[i]; + return 0; +} +static void binop(const char *o) +{ + if (!strcmp(o, "*")) op(O_MUL); + else if (!strcmp(o, "/")) { fnref("zdiv"); op(O_CALL); } + else if (!strcmp(o, "%")) { fnref("zmod"); op(O_CALL); } + else if (!strcmp(o, "+")) op(O_ADD); + else if (!strcmp(o, "-")) op(O_SUB); + else if (!strcmp(o, "<<")) { fnref("zshl"); op(O_CALL); } + else if (!strcmp(o, ">>")) op(O_SHR); + else if (!strcmp(o, "<")) op(O_LT); + else if (!strcmp(o, ">")) { op(O_SWAP); op(O_LT); } + else if (!strcmp(o, "<=")) { op(O_SWAP); op(O_LT); lit(1); op(O_XOR); } + else if (!strcmp(o, ">=")) { op(O_LT); lit(1); op(O_XOR); } + else if (!strcmp(o, "==")) { op(O_SUB); seq_isz(); } + else if (!strcmp(o, "!=")) { op(O_SUB); seq_nz(); } + else if (!strcmp(o, "&")) op(O_AND); + else if (!strcmp(o, "^")) op(O_XOR); + else if (!strcmp(o, "|")) op(O_OR); + else die("bad operator"); +} + +/* ---- constant expressions (globals, array sizes, const) ---- */ +static int cexpr(void); +static int cprim(void) +{ + int v; Sym *s; + if (tk == T_NUM) { v = tv; next(); return v; } + if (accept("-")) return -cprim(); + if (accept("~")) return ~cprim(); + if (accept("(")) { v = cexpr(); expect(")"); return v; } + if (tk == T_ID && (s = find(ts)) && (s->kind == K_CONST || (s->kind != K_GVAR && s->defd))) { next(); return s->val; } + die("constant expected"); return 0; +} +static int cexpr(void) +{ + int v = cprim(); + for (;;) { + if (accept("+")) v += cprim(); else if (accept("-")) v -= cprim(); + else if (accept("*")) v *= cprim(); else if (accept("|")) v |= cprim(); + else if (accept("&")) v &= cprim(); else if (accept("<<")) v <<= cprim(); + else if (accept(">>")) v >>= cprim(); else return v; + } +} + +/* ---- expressions ---- */ +static int lv; /* the stack top is an address not yet loaded */ +static void rval(void) { if (lv) { op(O_LD); lv = 0; } } +static void expr(void); +static void strlit(void) /* inline string: jump over it, push its address */ +{ + int j = here, a, i, n = (int)strlen(ts); + lit(0); op(O_JMP); a = here; + for (i = 0; i < n; i++) emit((unsigned char)ts[i]); + emit(0); setlit(j, here); lit(a); next(); +} +static int args(void) { int n = 0; expect("("); if (!accept(")")) { do { expr(); rval(); n++; } while (accept(",")); expect(")"); } return n; } +static void asmblock(void); +static void primary(void) +{ + char *n; Loc *l; Sym *s; + lv = 0; + if (tk == T_NUM) { lit(tv); next(); return; } + if (tk == T_STR) { strlit(); return; } + if (accept("(")) { expr(); expect(")"); return; } + if (is("asm")) { next(); asmblock(); return; } + n = ident(); + if ((l = findl(n))) { laddr(l->slot); lv = !l->arr; return; } + s = find(n); + if (s && s->kind == K_CONST) { lit(s->val); return; } + if (s && s->kind == K_GVAR) { lit(s->val); lv = 1; return; } + if (s && s->kind == K_GARR) { lit(s->val); return; } + if (is("(")) { args(); fnref(n); op(O_CALL); return; } + fnref(n); +} +static void incdec(int post, int d) +{ + if (!lv) die("++/-- needs an lvalue"); + lv = 0; + if (post) { lit(0); op(O_PICK); op(O_LD); lit(0); op(O_PICK); lit(d); op(O_ADD); lit(2); op(O_PICK); op(O_ST); op(O_SWAP); op(O_DROP); } + else { lit(0); op(O_PICK); op(O_LD); lit(d); op(O_ADD); lit(0); op(O_PICK); lit(2); op(O_PICK); op(O_ST); op(O_SWAP); op(O_DROP); } +} +static void postfix(void) +{ + int n; + primary(); + for (;;) { + if (accept("[")) { rval(); expr(); rval(); expect("]"); op(O_ADD); lv = 1; } + else if (is("(")) { rval(); n = args(); lit(n); op(O_PICK); op(O_CALL); op(O_SWAP); op(O_DROP); } + else if (accept("++")) incdec(1, 1); + else if (accept("--")) incdec(1, -1); + else if (accept("as")) ident(); + else return; + } +} +static void unary(void) +{ + if (accept("-")) { unary(); rval(); lit(0); op(O_SWAP); op(O_SUB); } + else if (accept("!")) { unary(); rval(); seq_isz(); } + else if (accept("~")) { unary(); rval(); lit(-1); op(O_XOR); } + else if (accept("*")) { unary(); rval(); lv = 1; } + else if (accept("&")) { unary(); if (!lv) die("& needs an lvalue"); lv = 0; } + else if (accept("++")) { unary(); incdec(0, 1); } + else if (accept("--")) { unary(); incdec(0, -1); } + else if (tk == T_ID && (!strcmp(ts, "u") || !strcmp(ts, "m") || !strcmp(ts, "d"))) { next(); expect("*"); unary(); } /* u*x: a recast, no code */ + else postfix(); +} +static void bin(int minp) +{ + int p, a, b; char o[8]; + unary(); + while ((p = binprec()) && p >= minp) { + strcpy(o, ts); next(); rval(); + if (!strcmp(o, "&&")) { + a = here; lit(0); op(O_JZ); bin(p + 1); rval(); seq_nz(); + b = here; lit(0); op(O_JMP); setlit(a, here); lit(0); setlit(b, here); + } else if (!strcmp(o, "||")) { + a = here; lit(0); op(O_JZ); lit(1); b = here; lit(0); op(O_JMP); + setlit(a, here); bin(p + 1); rval(); seq_nz(); setlit(b, here); + } else { bin(p + 1); rval(); binop(o); } + lv = 0; + } +} +static void expr(void) +{ + char o[8]; + bin(1); + if (is("=")) { + if (!lv) die("assignment needs an lvalue"); + next(); expr(); rval(); + op(O_SWAP); lit(1); op(O_PICK); op(O_SWAP); op(O_ST); lv = 0; + } else if (tk == T_OP && strlen(ts) >= 2 && ts[strlen(ts) - 1] == '=' && strcmp(ts, "==") && strcmp(ts, "!=") && strcmp(ts, "<=") && strcmp(ts, ">=")) { + if (!lv) die("assignment needs an lvalue"); + strcpy(o, ts); o[strlen(o) - 1] = 0; next(); + lit(0); op(O_PICK); op(O_LD); lv = 0; expr(); rval(); binop(o); + op(O_SWAP); lit(1); op(O_PICK); op(O_SWAP); op(O_ST); lv = 0; + } +} + +/* ---- asm blocks: raw vm words ---- */ +static void asmblock(void) +{ + char ln[128][64], rn[256][64]; int la[128], nl = 0, ra[256], nr = 0, i, k; const char *c; + Loc *l; Sym *s; + expect("{"); + while (!accept("}")) { + if (tk == T_EOF) die("unterminated asm"); + if (tk == T_NUM) { lit(tv); next(); continue; } + if (tk == T_STR) { strlit(); continue; } + if (accept("#")) { if (tk != T_NUM) die("# needs a number"); emit(tv); next(); continue; } + if (is("-")) { next(); if (tk == T_NUM && glue) { lit(-tv); next(); } else op(O_SUB); continue; } + if (accept(":")) { strcpy(ln[nl], ident()); la[nl++] = here; continue; } + if (strlen(ts) == 1 && (c = strchr(mn, ts[0]))) { op((int)(c - mn)); next(); continue; } + if (tk != T_ID) die("bad asm token"); + if ((l = findl(ts))) { laddr(l->slot); next(); continue; } + if ((s = find(ts)) && s->kind != K_FN) { lit(s->val); next(); continue; } + if (s) { fnref(ts); next(); continue; } + strcpy(rn[nr], ts); ra[nr++] = here; lit(0); next(); /* a label of this block */ + } + for (i = 0; i < nr; i++) { + for (k = -1; ++k < nl && strcmp(ln[k], rn[i]);) ; + if (k == nl) die("unknown asm label"); + setlit(ra[i], la[k]); + } +} + +/* ---- statements ---- */ +static int brk[256][64], nbrk[256], cont[256], nloop; +static int epi[1024], nepi; +static void stmt(void); +static void block(void) +{ + int save = nlo; + expect("{"); + while (!accept("}")) { if (tk == T_EOF) die("missing }"); stmt(); } + nlo = save; +} +static void jmp_to(int t) { lit(t); op(O_JMP); } +static void loopbody(int contaddr) +{ + nbrk[nloop] = 0; cont[nloop++] = contaddr; + stmt(); + nloop--; +} +static void patchbreaks(void) { int i; for (i = 0; i < nbrk[nloop]; i++) setlit(brk[nloop][i], here); } +static void stmt(void) +{ + int a, b, c, d; + if (accept(";")) return; + if (is("{")) { block(); return; } + if (accept("let") || accept("var")) { + do { + Loc *l = &lo[nlo++]; strcpy(l->name, ident()); l->arr = 0; + if (accept("[")) { int n = cexpr(); expect("]"); nslot += n; l->slot = nslot - n; l->arr = 1; } + else l->slot = nslot++; + if (accept("=")) { expr(); rval(); laddr(l->slot); op(O_ST); } + } while (accept(",")); + expect(";"); return; + } + if (accept("unchecked")) { stmt(); return; } + if (accept("if")) { + expect("("); expr(); rval(); expect(")"); + a = here; lit(0); op(O_JZ); stmt(); + if (accept("else")) { b = here; lit(0); op(O_JMP); setlit(a, here); stmt(); setlit(b, here); } + else setlit(a, here); + return; + } + if (accept("while")) { + c = here; expect("("); expr(); rval(); expect(")"); + a = here; lit(0); op(O_JZ); + loopbody(c); jmp_to(c); setlit(a, here); patchbreaks(); return; + } + if (accept("for")) { + int save = nlo; + expect("("); + if (!is(";")) { if (is("let") || is("var")) { stmt(); goto cond; } expr(); rval(); op(O_DROP); } + expect(";"); + cond: c = here; + if (!is(";")) { expr(); rval(); } else lit(1); + expect(";"); + a = here; lit(0); op(O_JZ); b = here; lit(0); op(O_JMP); + d = here; + if (!is(")")) { expr(); rval(); op(O_DROP); } + expect(")"); jmp_to(c); + setlit(b, here); + loopbody(d); jmp_to(d); setlit(a, here); patchbreaks(); + nlo = save; return; + } + if (accept("break")) { if (!nloop) die("break outside a loop"); brk[nloop - 1][nbrk[nloop - 1]++] = here; lit(0); op(O_JMP); expect(";"); return; } + if (accept("continue")) { if (!nloop) die("continue outside a loop"); jmp_to(cont[nloop - 1]); expect(";"); return; } + if (accept("return")) { + if (!is(";")) { expr(); rval(); } else lit(0); + expect(";"); epi[nepi++] = here; lit(0); op(O_JMP); return; + } + if (is("asm")) { next(); asmblock(); return; } + expr(); rval(); op(O_DROP); expect(";"); +} + +/* ---- top level ---- */ +static void fndef(int ret) +{ + char *n = ident(); Sym *s; int np = 0, tags = 0, t, frame, i; + char pn[16][64]; + s = find(n); + if (!s || s->kind != K_FN) { s = add(n, K_FN, 0); s->defd = 0; } + expect("("); + if (!accept(")")) { + do { + t = 0; + if (accept(".")) { /* .m name, .d name */ + if (is("m")) t = 1; else if (is("d")) t = 2; else die("a parameter tag is .m or .d"); + next(); + } + if (np < 12) tags += t << (2 * np); + strcpy(pn[np++], ident()); + } while (accept(",")); + expect(")"); + } + s->sig = ret + 2 * np + 64 * tags; + if (accept(";")) return; /* prototype */ + if (s->defd) die("function defined twice"); + s->val = here; s->defd = 1; + nlo = 0; nslot = 0; nepi = 0; infn = 1; + for (i = 0; i < np; i++) { strcpy(lo[nlo].name, pn[i]); lo[nlo].arr = 0; lo[nlo++].slot = nslot++; } + /* prologue: fp -= frame, then pop the arguments into their slots */ + lit(ZFP); op(O_LD); frame = here; lit(0); op(O_SUB); lit(ZFP); op(O_ST); + for (i = np; i--;) { laddr(i); op(O_ST); } + block(); + lit(0); + for (i = 0; i < nepi; i++) setlit(epi[i], here); + lit(ZFP); op(O_LD); lit(nslot); op(O_ADD); lit(ZFP); op(O_ST); op(O_RET); + setlit(frame, nslot); + infn = 0; +} +/* enhance name(params) [-> n] micro "recipe"; or ... vm { asm }; + emits the install descriptor; the booted genome installs it */ +static int nenh; +static void enhdef(void) +{ + char *n, pn[8][64]; int np = 0, nout = 1, d, na, pay, len = 0, i, j; Sym *s; + if (tk == T_NUM) { nout = tv; next(); } /* enhance 2 name(...) */ + n = ident(); + expect("("); + if (!accept(")")) { do { if (np == 8) die("at most 8 inputs"); strcpy(pn[np++], ident()); } while (accept(",")); expect(")"); } + d = here; for (i = 0; i < 7; i++) emit(0); + na = here; for (i = 0; i < np; i++) emit(0); + for (i = 0; i < np; i++) { img[na + i - GORG] = here; for (j = 0; pn[i][j]; j++) emit((unsigned char)pn[i][j]); emit(0); } + if (accept("micro")) { pay = here; while (tk == T_STR) { for (j = 0; ts[j]; j++) emit((unsigned char)ts[j]); next(); } emit(0); } + else if (accept("vm")) { pay = here; asmblock(); op(O_RET); len = here - pay; img[d + 1 - GORG] = 1; } + else die("expected micro or vm"); + img[d - GORG] = nenh; img[d + 2 - GORG] = np; img[d + 3 - GORG] = nout; + img[d + 4 - GORG] = pay; img[d + 5 - GORG] = len; img[d + 6 - GORG] = na; + s = add(n, K_ENH, nenh++); s->sig = np + nout * 16 + d * 256; + expect(";"); +} +static void top(void) +{ + while (tk != T_EOF) { + if (accept("unchecked")) continue; + if (accept("fn")) { fndef(0); continue; } + if (accept(".")) { if (strcmp(ident(), "u")) die("the only return tag is .u"); fndef(1); continue; } /* .u name(...) */ + if (accept("const")) { do { char *n = ident(); expect("="); add(n, K_CONST, cexpr()); } while (accept(",")); expect(";"); continue; } + if (accept("enhance")) { enhdef(); continue; } + if (accept("let") || accept("var")) { + do { + char *n = ident(); + if (accept("[")) { int k = cexpr(), a = here; expect("]"); while (k--) emit(0); add(n, K_GARR, a); } + else { + int a = here; add(n, K_GVAR, a); emit(0); + if (accept("=")) { + if (tk == T_STR) { int i, l = (int)strlen(ts); img[a - GORG] = here; for (i = 0; i < l; i++) emit((unsigned char)ts[i]); emit(0); next(); } + else img[a - GORG] = cexpr(); + } + } + } while (accept(",")); + expect(";"); continue; + } + die("expected fn, var or const"); + } +} + +int main(void) +{ + static char buf[1 << 22]; int n = (int)fread(buf, 1, sizeof buf - 1, stdin), i, j; Sym *st; + buf[n] = 0; src = buf; next(); + /* image header: set the frame pointer, jump to start, then the symbol blob pointer and image end */ + lit(FTOP); lit(ZFP); op(O_ST); fnref("start"); op(O_JMP); emit(0); emit(0); + top(); + for (i = 0; i < nfx; i++) { + Sym *s = &sy[fx[i].sym]; + if (!s->defd) { fprintf(stderr, "zc0: undefined function %s\n", s->name); return 1; } + setlit(fx[i].at, s->val); + } + if (!(st = find("start")) || !st->defd) { fprintf(stderr, "zc0: no start\n"); return 1; } + /* symbol blob: kind, value, sig, name chars, 0 ... then a 0 kind */ + img[5] = here; + for (i = 0; i < nsy; i++) { + emit(sy[i].kind); emit(sy[i].val); emit(sy[i].sig); + for (j = 0; sy[i].name[j]; j++) emit((unsigned char)sy[i].name[j]); + emit(0); + } + emit(0); + img[6] = here; + printf("#define GORG %d\n#define GLEN %d\nstatic const int genome[] = {", GORG, here - GORG); + for (i = 0; i < here - GORG; i++) printf("%s%d", !i ? "\n" : i % 16 ? "," : ",\n", img[i]); + printf("\n};\n"); + return 0; +} diff --git a/docs/history/15-o3/notes.md b/docs/history/15-o3/notes.md new file mode 100644 index 0000000..c18d50b --- /dev/null +++ b/docs/history/15-o3/notes.md @@ -0,0 +1,49 @@ +# o3, growable arrays, c-style return types + +code/ next to this file is the tree at the end of this stage. + +## message 15 + +> write some really cool examples for this language + +> no, wdym building a fresh vm?? just pocs 😭 we already have a compiler and everything + +the examples (mandel, bf, dump, tree, lang, staged, quine, life). "building a fresh vm" in my progress note only meant compiling core.c into a binary to run them. no snapshot of its own: they're all in this stage's code. + +## message 16 + +> add opt keywords, for example `optim` (or o3, i prefer o3 as the keyword) will cause any vars in a scope to not assign heap type metadata, compiles with opt level o3 and is immediately compiled down to native, and when used with variables they must be always typed and it reduces the perf cost for accessing or modifying them, etc etc. add a `fixed` keyword which says that an array has a fixed size and cannot be resized, etc + +answers: functions, blocks and single vars; a real optimizing tier; give up type stamps, zero-filling, the address of o3 vars, stack and frame checks; c-like return types instead of `->`; growable arrays too. + +## working notes + +order of work: return syntax first (it touches every test), then arrays, then o3 in the compiler, then the core. + +**return types.** `topitem` reads a type and a name, then decides between function and global by whether `(` follows. `fn name(` stays the untyped form. generics moved to a `` prefix, because `T first(...)` would need the type parameter before it's declared. fn types became `int(int, int)`: `ptype()` takes a postfix parameter list after any type. the prescan looks for `NAME (` ... `)` followed by `{` or `;`, carrying a pending `.u`. + +**arrays.** a `[]T` is a 4-word handle `[len, cap, items, element size]` that owns `items`. growing copies into a new items object and moves the children's owner fields over. `fixed` is a flag on the same type record (TY_DYN, `t[3]`), so `fixed []T` and `[]T` are different hash-consed types and compat allows fixed <- growable but not the reverse. + +**o3, compiler side.** the region is the code range plus the frame slots of its typed scalar locals. a function region starts after the prologue's parameter stores, at a `jmp` to the next word, so the cpu enters it through dispatch. it ends where the epilogue starts, so `return` is a jump out. block regions are queued and installed when the function ends, since a `return` inside the block is still a hole until the epilogue exists. ltag bit 16 marks o3 variables (the tag itself is the low 4 bits). + +first bug: `t = t + a[j]` with the bounds check in the middle. the address of `t` was pushed before the right-hand side, crossed the check's branch on the memory stack, and the store in the next block went to the frame instead of the register. fixed in the compiler: assignment to an o3 variable in o3 code drops the address, evaluates the value, and only then pushes the address. compound assignment the same way. the lifter also refuses any block that would leave a register variable's address on the stack, so the same mistake can't come back silently. + +**o3, core side.** blocks start at the region's start, at literal jump targets, after a `jz` and after a call. the leader pass walks from the start, so inline strings (jumped over) never get lifted. per block, the lifter runs the vm code symbolically: a value table keyed by stack offset from the entry top, lazily loading entry words (SLD), and `fp + k` is tracked so loads and stores to promoted slots become variable reads and writes. what's left on the stack at the end, minus words still holding what they held at entry, gets stored. var 0 is the frame pointer, read once at entry. it never changes inside a region, since a store to the frame pointer word ends the block. + +arm64: vars in w0..w7, temps from w8..w14 and w26..w28 (x26..x28 now saved by the enter stub), x15..x17 scratch, constants have no register. the end of a block does the stack stores, moves the condition to w17, adjusts x20, and resolves the var moves as a parallel move (a cycle parks one value in w16). exits spill the vars to the frame and go through x_disp, so the stack checks still run between regions and normal code. stores check the coverage byte and call the hook with x0..x15 saved on the native stack. + +llvm: vars as allocas, one basic block per region block, a switch on the entry index, O3 pipeline. most of the speed comes from mem2reg and llvm's loop passes once there's no stack traffic. + +second bug, not o3's: the region's coverage byte made a stray store visible. a store of 10 into address 16 of the first function, every time a growable array was freed. `release` set the container's flags to 0 (free) before releasing its children, and a child whose buddy was the container then merged into it and unlinked it from a free list it wasn't on, writing through its data words. the fix is a third state (2, going) during release. the bug had been there since the buddy allocator; lists just rarely had a child adjacent to its parent. + +**literal specialization.** with `lfa(k)` as an enhancement for single o3 variables in normal code, the plain jit emitted `li k; push; pop; li ZFP; ld with range check; add`, no better than the 4 vm ops. so the jit now looks for literals feeding an enhancement's last inputs, re-runs the optimizer with them as constants, and folds constant addresses and small addends into instructions. `x / 7` in ordinary code got the multiply-high sequence from this too. + +**numbers** (bench.sh, ms per run, start-up removed by taking the slope between 1 and n runs): + +``` +interp collatz 309.63 -> 209.87 sieve 45.58 -> 15.95 digits 191.32 -> 92.29 fib 6.70 -> 5.50 +llvm collatz 27.69 -> 3.48 sieve 17.32 -> 0.17 digits 24.72 -> 1.60 fib 1.82 -> 0.84 +arm64 (qemu) collatz 327.33 -> 16.78 sieve 90.23 -> 3.85 digits 147.67 -> 3.46 fib 22.62 -> 5.73 +``` + +left for later: direct calls between regions, picking promoted vars by use count, caching a fixed array's length and items, cross-block optimization in the core's own optimizer, o3 inside images. diff --git a/docs/history/17-o3-boundaries/code/README.md b/docs/history/17-o3-boundaries/code/README.md new file mode 100644 index 0000000..7b91935 --- /dev/null +++ b/docs/history/17-o3-boundaries/code/README.md @@ -0,0 +1,528 @@ +# tinyvm zygote: notes and transcript + +a tiny stack cpu that started as a c89 code golf entry and ended up as a self-hosting runtime. a small native core runs vm code: jit-compiled to arm64, compiled through llvm on other hosts if you build with it, or interpreted. the genome it boots is a compiler for zc, a c-like language that keeps the forth habits. the compiler lives in vm memory next to your program, and your program can call into it, patch its tables, add syntax to it and compile new code while it runs. heap objects carry an ownership tag (u, m or d) that a static analyzer inside the compiler checks, with deterministic frees at scope ends and a mark-sweep collector underneath. types are a compiler primitive, with structs, generics, reflection and an optional typechecker. a program can also wire up to 512 custom opcodes into the cpu (virtual enhancements), which the jits inline as native code. and code marked `o3` skips the stack machine: the core compiles it as a whole region, with its variables in registers. + +this folder has the latest code. `history/` is the scrapbook: notes for every stage, plus frozen code from the stages that still have it. + +## files + +`core.c` is the native core and the only native code at runtime: the arm64 jit, the llvm backend (behind `-DZYLLVM`), the portable interpreter, the enhancement unit (recipe parser, vm-code lifter, optimizer and lowering) and the o3 region compiler. `src/rt.zc` is the runtime (i/o, the native division enhancements, the heap and the collector) and `src/compiler.zc` is the compiler, both written in zc. together they are the genome. `zc0.c` is stage 0 of the bootstrap, a compiler in c for the subset of zc that `compiler.zc` is written in. `tools/build.zc` makes the resident compiler compile its own source into a fresh image, and `mkgenome.c` turns that image into `genome.h`, which is generated and checked in. `examples/` has the small ones (hello, factorial, echo, self-patching code, an owning list, a repl, a typed program, an enhancement demo) and some bigger proofs of concept: + +- `mandel.zc`: the mandelbrot set in 16.16 fixed point, with the fixed-point multiply wired in as a custom opcode. +- `bf.zc`: a brainfuck compiler. it turns brainfuck into zc source, has the resident compiler compile it, and runs it (native code on the jit). it takes a program after a backtick, or runs hello world. +- `dump.zc`: prints any heap struct, nested ones included, with no printing code, by walking its type through reflection. +- `tree.zc`: a binary search tree whose nodes own their children. there's no `free` anywhere, and the live object count drops back to 0 when the root's block ends. +- `lang.zc`: grows the language while it compiles: a `|>` pipe operator (one table entry, since on a stack machine it's just `call`), `unless`, an `assert` that knows its source line, and `times n { }`. +- `staged.zc`: writes a polynomial as source with its coefficients baked in, compiles it while running, and finds it by name. +- `quine.zc`: prints its own source. +- `life.zc`: conway's life on a typed 2d board, with a glider wrapping around a torus. +- `primes.zc`: an o3 sieve over a fixed array, the primes gathered into a growable one, then twin primes and the widest gap. + +`tests/` has feature tests, `tests/errors/` (programs the lifetime analyzer has to reject), `tests/typeerrors/` (programs the typechecker has to reject) and `tests/langerrors/` (o3 and array misuse). each of those starts with the message it expects, and `tests/runerr.sh` runs a folder of them. `test.sh` does the whole bootstrap and runs everything on all three backends. `bench.sh` times o3 against plain code on each backend. `history/` is the scrapbook. + +## build + +``` +cc -O2 core.c io_linux.c -o zy # io_linux.c provides Q, G and J +./zy `). `ZYNOLLVM=1` turns the llvm tier off. with `ZYTRACE=1`, `ZYO3DUMP=1` also prints every o3 block's micro-ops. + +regenerating the genome after changing `src/`: + +``` +cc zc0.c -o zc0 && cc mkgenome.c -o mkgenome +cat src/rt.zc src/compiler.zc | ./zc0 >genome.h # stage 0, from c +cc -O2 core.c io_linux.c -o zy +(cat tools/build.zc; printf '`'; cat src/rt.zc src/compiler.zc) | ./zy | ./mkgenome >g.h +mv g.h genome.h && cc -O2 core.c io_linux.c -o zy # now built by itself +``` + +run the last two lines once more and `genome.h` should come out byte for byte the same. `test.sh` checks that, on every backend. + +## zc, the language + +it reads like c. underneath every value is a 32 bit word and memory is word addressed, so `p + 1` is the next word whatever `p` points at, and strings are one char per word with a 0 at the end. types are optional: untyped code is typed `word`, which goes with anything. + +``` +const N = 10; +[N]int table; // a global array +int fib(int n) { if (n < 2) return n; return fib(n - 1) + fib(n - 2); } +fn main() { + for (int i = 0; i < N; i++) table[i] = fib(i); + let sq = { let t = table[5]; t * t }; // a block is an expression too, its last line is its value + let x = asm { 2 3 + }; // raw vm code, leaves one value + putn(sq + x); putc(10); + return 0; +} +``` + +declarations are c-style, type first: `int a = 1, b;`, `*node p = new(node);`, `[4]char buf;`, `point origin;`. `let` declares without a type: it infers pointer, struct and function types from the initializer and keeps numbers as plain words, and `let a[10];` is an untyped array. statements are declarations, `if`/`else`, `while`, `for`, `switch`, `break`, `continue`, `return`, `{ }` blocks, `asm { }` and expressions. `switch (e) { case 1, 2: ... default: ... }` takes constant labels, and its cases don't fall through: each ends where the next begins, and `break` leaves early. operators are c's with c's precedence, including `c ? a : b`, plus `p.x` and `p->x` (the same thing: `.` goes through a pointer on its own), `e as T` to change a type without changing the bits, and the casts below. comparisons and `&& ||` give 0 or 1. `/`, `%` and `<<` compile to native enhancement opcodes. character literals, `0x` hex and the usual escapes are there. + +functions are declared like c, return type first: `int f(int x)`, `*node.u make()`, `.u grab()` (a tag on its own), `void log(*char s)`. `fn f(a, b)` is the untyped form, returning a word. a generic function lists its type parameters after its name, `T first(*list l)`, the same place a call gives explicit ones, `first(l)`. the return type can use them before they're declared: while it's read, an unknown name is taken as a type parameter, and the list after the name has to confirm it. function types are written the same way, `int(int, *char)`, and `fn(int)` for one that returns a word. the earlier `fn f(...) -> T` spelling is gone. + +arrays come in three kinds. `[n]T` has its size in the type and lives in place. `[]T` is a growable array: a small handle object that owns its items, made with `new([]T)` or `new([]T, n)`, indexed with a bounds check, and changed with `push(a, x)`, `pop(a)` and `resize(a, n)`. `len(a)` works on both (for `[n]T` it's the constant) and `cap(a)` gives the room before the next grow. `fixed []T` is a `[]T` that can't change size: `new(fixed []T, n)` needs the length, and `push`, `pop` and `resize` on it are compile errors. a `fixed []T` can't be passed where a `[]T` is expected, the other way round is fine, and a constant index outside a `[n]T` is caught at compile time. the items belong to the array, so freeing it frees them, and objects stored into a `[]*T` are its children like any container's. + +a unit is read whole and scanned for function signatures before it compiles, so calls can come before definitions, tags included, and prototypes are optional. `main` runs after the whole program has compiled. every function returns one word, 0 if it falls off the end. + +builtins from the runtime: `putc`, `getc`, `eof`, `puts`, `putn`, `halt`, `strlen`, `streq`, `wcopy`, `wfill`, `new`, `new_t`, `new_raw`, `free`, `gc`, `isobj`, `osize`, `typeof_obj`, `live_objects`, `install`, `zdiv`, `zmod`, `zshl`. + +## memory model: u, m and d + +`new(n)` or `new(T)` gives back a fresh object. bind it to a local and the local owns it, tagged **m**. tags live on local variables and parameters, the analyzer follows them through the code, and the compiler inserts `free` calls where owned objects die. a tag is written after the type, `*node.u p`, or alone, `.d x`. `u`, `m` and `d` are reserved words. + +**m** means the object belongs to this block and has to be used in it. you can lend it to callees as often as you like, and when the block ends it's freed. an m object that's never used anywhere in its block is an error. + +**d** means the object is going down. the first time it's handed to a lower scope its life here is over: passing it to a `.d` parameter, or touching it inside an inner block, which then owns it as m and frees it when that block ends. using it afterwards is an error, and so is ending the block with a d object that never left. + +**u** means the object is going up. it has to leave this block for the one above, where it arrives as m. it leaves through `return` (the function's return type has to carry `.u`, like `*node.u make()` or just `.u make()`), as the value of a block expression, or by assignment to a variable of the directly enclosing block. letting it fall out of scope, or throwing away a u result, is an error. + +recasting is multiplication by the tag: `u*x`, `d*x`, `m*x`. it only goes away from m: m can become u or d (and u and d can swap), but nothing goes back to m in the same scope. a declaration can carry a tag too, `.u r;`, and then whatever lands in it gets that tag. + +parameters: an untagged parameter is m from the caller's side, a lend, and the callee sees a plain word. `.m` makes it a checked borrow: the callee can read and write it but can't free it, return it, recast it or store it anywhere. `.d` hands the object over, and it arrives as m. + +``` +*int.u box(int v) { *int b = new(int); *b = v; return u*b; } +fn add(*list l, *int.d item) { l.items[l.len] = d*item; l.len++; return 0; } +``` + +containers own what you store in them. `p[i] = c` or `p.f = c` needs `c` to carry the same tag as `p` (or be a fresh object), and from then on `c` follows `p`: if `p` is recast, so is `c`, if `p` is given away `c` goes with it, and when `p` is freed its children are freed too. a child can't be recast or given away on its own. storing into an object you only borrowed gives the child away: it's dead here afterwards, and the caller's object owns it. the runtime records the owner in the child's header, so freeing a container frees exactly the objects it owns. + +globals, plain pointers and arrays aren't tracked. storing an owned object there needs `d*`, and the mark-sweep collector takes it over. a fresh object can go straight in. + +where paths join (after an `if`, a loop, a `switch` or `?:`) every variable has to be in the same state on every path that reaches the join, with one exception. a variable that became d, or was given away, on some path is dead afterwards, and the compiler frees it on the paths where it's still alive, on their way in. a loop's exit state is the join of its `break`s and its normal exit, so giving something away and breaking is fine. what a loop can't do is give something away on a path that comes back around to the top, since the next pass would use it dead. a u object doesn't get the dead treatment: it still has to leave. + +### the analyzer + +it runs inside the compiler on every function, including code compiled at runtime by `eval` and friends, and reports each violation with a line number and the variable's name. the compiler keeps going after errors, up to 20: a syntax error skips to the next `;` or `}` (or the next top-level declaration), and analyzer and type errors just get reported, since the parse is still in step. a unit with errors doesn't run. `tests/errors/` has one small program per rule. to turn it off: `unchecked fn f() { }` for one function, `unchecked { }` for a block, and a top-level `unchecked;` for the rest of the unit (a parse word can also set `check_on = 0`). unchecked code doesn't track anything, so what it allocates belongs to the collector, and checked variables can't be touched from inside an unchecked block. + +## types + +types are a compiler primitive. the compiler keeps them as records in a type area at 0x9C000, hash-consed so the same type is always the same pointer. they stay in memory after compiling, so programs and parse words can look at them. + +``` +struct point { int x, y; }; +struct list { int len; *T items; }; +type num = int; +T first(*list l) { return l.items[0]; } +*list.u mk(int cap) { *list l = new(list); l.items = new(T, cap); return u*l; } +... +*list<*point> ps = mk<*point>(8); // explicit type arguments, or inferred from the arguments +``` + +the kinds are `word`, `int`, `char`, `bool`, `void`, pointers `*T`, arrays `[n]T`, `[]T` and `fixed []T`, function types `int(int, *char)`, structs, generic structs and generic functions. structs and arrays live in place (in a frame, a global or inside another struct), and their value is their address, so they're passed around by pointer. `sizeof` counts words. type arguments have to be one word (a number, a pointer or another parameter), so a generic function is compiled once and works for every instance. indexing scales by the element's size, while plain pointer arithmetic stays in words, like b. `new(T)` and `new(T, n)` stamp the type into the object's header. + +### reflection + +`typeof(e)` and `typeof(T)` give a type record and `sizeof(...)` its size. both work on any expression, which is compiled dry and never runs. at runtime: `tkind(t)` (one of the `TY_` constants), `tsize`, `tname`, `tlen`, `tnfields`, `tfname(t, i)`, `tftype`, `tfoff`, `tret`, `tnparams`, `tparam`, `tparamtag` (the u/m/d tag of a parameter), `tretu`, `tnargs` and `targ` for generic instances, and `tprint(t)`. `sigof(f)` gives the type of a function by address (untyped ones get a signature built from their arity and tags), `sigofs("name")` by name, and `typeof_obj(p)` reads the type stamped on a heap object (0 for an o3 object that hasn't been fattened yet). + +### the typechecker + +off by default. `typecheck on;` turns it on for what follows and `typecheck off;` turns it back off (a parse word can flip `tc_on`). it checks initializers, assignments, arguments (unifying generic parameters), return values, arithmetic, comparisons, dereferencing, indexing and calls. `word` matches anything, numbers mix with each other, a literal 0 is a null pointer, and arrays decay to pointers. it stops at the first error, printing what it expected and what it got. `tests/typeerrors/` has one per rule. some things are checked whatever the setting, because the compiler needs them to generate code: field names, and field access on something that isn't a struct. + +## virtual enhancements + +the cpu has room for 512 custom opcodes (22 to 533), and opcode 21 (`%` in asm) installs one at runtime. it's the closest a soft cpu gets to rewiring itself. an enhancement takes up to 8 inputs off the data stack and leaves up to 8 outputs, and it's written one of two ways: + +``` +enhance clamp(x, lo, hi) micro "x = max x lo; x = min x hi"; +enhance 2 divmod(a, b) micro "r2 = sdiv a b; r1 = srem a b; r0 = r2"; // the number is how many outputs +enhance cube(x) vm { 0 $ 0 $ * * }; +enhance noisy(x) vm { 0 $ . } else soft_noisy; // can't be lifted (it does i/o), so calls use the fallback +``` + +a micro recipe works on registers `r0`..`r15`, which the inputs arrive in (also under the parameter names), and the outputs leave from `r0` up. the micro-ops are `li mov add sub mul sdiv udiv srem urem and or xor shl shr sar neg not lt ltu eq ne min max minu maxu mulhs mulhu clz abs sel ld st`. a `vm` body is straight-line vm code that gets lifted by running it symbolically. both turn into ssa, and the core's optimizer folds constants, simplifies algebra, removes common subexpressions and dead code, and turns division by a constant into a multiply-high sequence (`ZYTRACE=1` prints the result). the arm64 jit then inlines the code at every use with registers allocated, the llvm backend inlines it as ir, and the interpreter runs the optimized list. if something doesn't fit in registers, the jit calls the interpreter for that op. when literals feed an enhancement's last inputs (`x / 7` is `x 7 idiv`), the arm64 jit specializes that use on them: the literals become constants, the optimizer runs again, and `x / 7` turns into a multiply-high and two shifts instead of a divide. + +an enhancement is called like a function when it has one output, or by name from asm. `enhance` installs at compile time, so the wiring is there before `main` runs, and a parse word can use it right away. an image installs its own enhancements when it boots, from its symbol table. that's how `/`, `%` and `<<` work: `rt.zc` declares `idiv`, `imod` and `ishl` as one-instruction recipes. `install(desc)` is the raw interface, and its descriptor format is documented in `core.c`. stores done by an enhancement don't invalidate compiled code, so use them for data. + +this replaces my first plan for enhancements, which compiled c or llvm ir sources at runtime. part of that reply got stopped by a safety check, and once it turned out llvm was meant for portability, it moved into the backend below instead. + +## o3 + +`o3` marks code for the optimizing tier. it goes on a function, a block or a single variable: + +``` +o3 int sum(fixed []int a) { int s = 0; for (int i = 0; i < len(a); i++) s += a[i]; return s; } +fn main() { + o3 int hits = 0; // one o3 variable + o3 { for (int i = 0; i < 1000; i++) if (i % 7 == 3) hits++; } + ... +} +``` + +what it gives up, as asked: variables declared in o3 code must be typed (`let` and untyped parameters are errors), they have no address (`&x` and naming one from `asm` are errors), `new` in o3 code returns raw memory with no type stamp and no zero fill (`new_raw`), and an o3 function skips the frame stack check in its prologue. what it gets: the compiler hands each o3 function body and each o3 block to the core as a region (install kind 2: the range of vm words plus its typed scalar variables' frame slots, each with a register), and the core compiles the region right away. the cpu has 7 registers for variables. a variable takes the one most recently freed by a scope that closed, or the next unused one, so loop counters in sibling loops share a register, and a variable declared when all 7 are busy stays in memory. a register shared by several slots gets written back to all of them when the region exits, and they never hold different live values, since their scopes don't overlap. + +inside the core, a region is cut into basic blocks at jump targets, branch fallthroughs and call return points. each block is lifted to ssa the way enhancements are: the vm stack becomes values, and only what's still on it at the end of the block is written to memory. the frame pointer and the promoted slots become variables that live in registers across the whole region, so `i++` on an o3 int is one add. enhancements are spliced into the block, so `/`, `%` and `<<` become one instruction or a magic-number sequence. every block then goes through the same optimizer (folding, algebra, cse, dce, division by constants). in o3 code `a[i]` on a `[]T` is compiled inline with its bounds check (`outside(i, len)`, one enhancement), and `len(a)` is a load. a `fixed []T` in an o3 variable does better: its items pointer and length can't change, so the compiler gives it two hidden o3 variables holding them. they're loaded when the region starts and whenever the array variable is assigned, so indexing is a compare against a register and an indexed load, with no loads through the handle. + +the backends: on arm64 the region becomes one piece of native code. variables sit in w0..w7, block values in w8..w14 and w26..w28, constants are folded into instructions where they fit, loads from a fixed address are one `ldr`, and blocks branch to each other directly with no dispatch and no stack checks. on the llvm build the region becomes one function, with the variables in allocas that mem2reg turns into ssa across blocks, and it goes through llvm's O3 pipeline. on the plain interpreter, a small region interpreter runs the optimized blocks with the variables in locals. + +calls, returns, jumps out of the region, i/o, `install`, a store to the frame pointer and anything else a block can't lift all leave the region: the variables are written back to the frame first, so outside code sees ordinary memory. a call's return point is an entry, which loads them again. stores inside a region still check the coverage map, so writing to compiled code works, and a store into the region's own code drops it (it lifts again at its next entry). to keep variable addresses from waiting on the stack across a branch, assignment to an o3 variable in o3 code evaluates the value first and the address after. the lifter refuses a block that would leave such an address on the stack, rather than miscompile it. single o3 variables outside o3 code are reached through `lfa(k)`, an enhancement for `fp + k`, which the arm64 jit specializes into a load and an add. + +`bench.sh` runs four kernels from `tests/bench.zc` with and without `o3`. milliseconds per run, with start-up and compile time removed by timing 1 run against enough runs to take more than a second (arm64 is under qemu on an x86 host, so only its ratios mean much, and the machine was noisy enough that the small numbers move by 20% between runs): + +``` +backend kernel plain ms o3 ms speedup +interp collatz 324.90 240.61 1.4x +interp sieve 47.08 21.71 2.2x +interp digits 160.89 100.82 1.6x +interp fib 3.48 4.09 0.8x +llvm collatz 47.03 3.34 14.1x +llvm sieve 10.77 0.28 38.9x +llvm digits 18.93 1.45 13.1x +llvm fib 0.87 1.01 0.9x +arm64 (qemu) collatz 323.89 13.04 24.8x +arm64 (qemu) sieve 56.84 1.74 32.6x +arm64 (qemu) digits 147.06 4.17 35.3x +arm64 (qemu) fib 5.19 5.71 0.9x +``` + +fib gains nothing and can lose a little, since every call leaves the region through the dispatcher and comes back in through an entry stub, which costs about what the body saves. + +### leaving o3: fattening + +going into o3 is free: o3 code reads normal objects the same way and ignores their metadata. coming out is where the work is, since an object made by `new` in o3 code has no type stamp and garbage after what was asked for. `new_raw` keeps one hint for later: the header's type word holds -(n + 1), the number of words asked for, so the object stays recognizably raw and its real size is known. + +the compiler fattens at the points where o3 data reaches code that expects the metadata, using the static type it knows there: storing a pointer anywhere other than an o3 variable (a normal local, a global, a field, an element), passing one to a function that isn't o3 (`free` excepted), returning one from o3 code, `push` into a growable array, and, at the end of an o3 block, whatever the o3 variables declared outside it hold. `fatten(p, t)` (in the compiler, so it can read type records) stamps the type, as `[0]T` when more than one element was asked for, zeroes the slack, and then follows every typed pointer, field, array element and growable array element inside, fattening what it finds. objects that are already fat stop the walk, so building a list in o3 costs one stamp per node, and a depth limit of 32 stops cycles through growable arrays. untyped words carry no hint, so pointers kept in `word` variables stay raw until typed code sees them. + +## the llvm backend + +a tiered jit for hosts the arm64 backend doesn't cover. the interpreter counts how often each block start gets reached, and a hot block is translated to llvm ir using the same block rules as the arm64 jit: it ends at call, jump, ret or halt, keeps going past a jz, and literal targets are fused. the ir goes through the O2 pipeline and gets compiled by orc's lljit. stores into compiled words drop the blocks covering them, like on arm64. it pays off for long-running code: a loop-and-recursion benchmark runs about 3.3x faster than the interpreter. short bursty work like the self-compile is slower, since compile time dominates there. `ZYHOT=1` compiles every block, which is a good stress test, and the test suite runs that way too. + +## the heap + +0x40000 to 0x9BFFF, managed by a buddy allocator. the heap is split into aligned power-of-two chunks, every block is a power of two words, and a freed block merges with its buddy whenever that one is free too, so freed space can be reused at any size. (stage 15 fixed a bug here: `release` marked a container free before freeing its children, so a child whose buddy was the container merged into it while the container was still being freed, and corrupted a free list. growable arrays hit it every time, since a handle and its items are often buddies.) allocation finds the smallest free block that fits and splits it down. free blocks sit on a doubly linked list per class, so taking a buddy off its list during a merge is constant time. checked code frees deterministically. under that runs a mark-sweep collector, conservative and non-moving, which handles globals, unchecked code and anything given to it. it runs on `gc()`, which returns how many objects it freed, or when an allocation doesn't fit. the sweep frees through the same merging path. + +## code as data + +the compiler is in memory with your program and every name in it is callable. + +`asm { }` emits raw vm words: numbers push, `-5` pushes a negative, `#n` emits a raw word, one-char tokens from the mnemonic table are opcodes, `:name` defines a label local to the block, enhancement names emit their opcode, and other names push the address of a variable or function. + +`quote { ... }` is a token list as a fresh object: `[n, kind, value, line, kind, value, line, ...]`, so errors inside a macro expansion point at the line the tokens came from, where names, strings and operators are interned pointers (`intern("while")` gets one). `tokens(s)` makes one from a string, and `qdup(q)` copies one. + +`eval(q)` compiles a token list as a function body and runs it. `compile(q)` returns its address instead, `load(q)` compiles top-level declarations, and `evals(s)` runs `eval` on a string. + +parse words: `immediate fn name() { }` runs at compile time whenever `name` shows up as a statement, inside an expression or at the top level. it starts with the current token on its own name and works the compiler directly: `next()`, `peek()` and `peekat(k)` (up to 4 tokens ahead), `tk`/`tv` (current token kind and value), `expect`, `accept`, `expr()`, `stmt()`, `substmt()`, `ptype()`, `emit`, `lit`, `op`, `hole`/`setlit` for jumps, and `here`. `grab()` reads a `{ }` group into a token list, and `feed(q)` pushes a list back into the input, which is all a macro needs. parse words can call `eval` partway through a compile: the compiler saves its state and emits the new function in place with a jump around it. the built-in keywords are entries in the same table, so a parse word called `while` replaces `while`. + +tables: `add_binop("**", 11, "ipow c")` adds or replaces a binary operator with a precedence and its code, written like asm (names resolve when it's used). `add_unop` does the same for prefix operators, and the lexer learns new spellings from both. `mnem` is the asm mnemonic table, `q@!$_~cjzr,?.+-*&|^><%`, and can be rewritten. + +## the bootstrap + +`zc0.c` compiles the subset `compiler.zc` uses (no analyzer, types, quote, parse words or block expressions, but it does lay out `enhance` declarations) into a first genome. that genome compiles `compiler.zc` again on the vm. that's where the real compiler, the analyzer and native division first meet their own source, and `compiler.zc` passes its own lifetime checks. the result compiles itself into identical words, on all three backends, and that's the `genome.h` that ships (84118 words). an image build (`build_image(org, dst)`) compiles with only the image's own names visible (plus keywords and builtin types), writes code meant for one address into another, and appends a symbol table so the booted genome knows its own names and enhancements. + +## the instruction set + +every word is 32 bits. an odd word is a literal that pushes `w>>1` (31 bits, sign extended). an even word is an opcode, `w/2`. there's a data stack and a return stack, both stored inside memory. + +``` +0 q halt 5 ~ swap 10 , in (Q) 15 * mul 20 < less +1 @ load 6 c call 11 ? empty (J) 16 & and 21 % install +2 ! store 7 j jump 12 . out (G) 17 | or 22..533 enhancements +3 $ pick 8 z jz 13 + add 18 ^ xor +4 _ drop 9 r ret 14 - sub 19 > shr +``` + +stack effects: `!` is `(v addr --)`, `$` is `(n -- copy of the nth item)` so `0 $` is dup, `z` is `(cond addr --)` and jumps when cond is 0, `c` and `j` take their target from the stack, and `%` is `(descriptor -- status)`. there's still no div or mod in the base set, because a divider can't finish in one cycle in hardware. an enhancement can add one. + +## memory map and abi + +memory is 1m (1048576) words. it could grow until the host runs out (literals already reach 1g words), but that would mean reserving the jit tables with mmap instead of static arrays, and 1m is plenty for now. + +``` +0x00000.. the program: code, with globals between functions +0x40000..0x9BFFF heap +0x9C000..0x9FFFF type records +0xA0000 frame pointer word +0xA0100..0xBFFFF compiler data: interned names, symbol table, locals, tables +0xC0000.. genome: 0xC0000 entry, 0xC0005 symbol table address, 0xC0006 image end +0xE0000..0xEFFFF frame stack, grows down from 0xF0000 +0xF0000.. return stack, grows up +0xF8000.. mark stack +0xFC000..0xFFFF0 data stack, grows down +``` + +calls push the arguments left to right and the callee's prologue pops them into its frame. locals live in the frame at `*0xA0000 + slot`. every function leaves exactly one word on the data stack. heap objects have a 6 word header: size, size class, flags, a check word, owner, type. + +## how the core works + +registers: x19 holds the memory base, x20 the data stack pointer, x21 the return stack pointer, x22 the translation table and x23 the state block (memory, s, r, table, coverage map, Q, G, &J, store hook, install hook, enhancement fallback). o3 regions also use x26..x28, which the entry stub saves. + +compilation is lazy. the first time control reaches an address, that block is translated from live memory. a block ends at call, jump, ret or halt. a `z` doesn't end it: the not-taken side keeps compiling, which makes superblocks. a literal followed by `c`, `j` or `z` is fused into a constant target. every exit goes through a dispatch stub that checks both stack pointers against their limits (kept in x24 and x25), bounds-checks the target, loads `tab[t]`, and branches straight to it when it's already compiled. otherwise it returns to c, which compiles the block and re-enters. enhancements are inlined: inputs load from the data stack into w0..w15, the optimized micro-ops run in registers with x16 and x17 as scratch, and the outputs go back. an install calls into c and then returns to the loop, which flushes every translation, since code built before the install may have inlined the old version. a flush drops o3 regions too, and they lift again on their next entry. when the loop reaches an o3 entry point, it compiles the region (or reuses it) instead of translating a block. + +self-modification is handled with a byte map of which words some live translation covers. a store checks that byte inline: an uncovered target, the usual case, gets a plain store. a covered target calls the hook, which drops every translation containing that word and makes the jit code return to c at the next instruction, so even patches to the very next word take effect. writing back the same value is a no-op. if the 16 mb code buffer fills, everything is flushed. + +the stacks are checked. the data and return stacks are checked at every block exit on all three backends, so the cost is a couple of compares per block rather than per push. a block pushes at most about 1024 words, which the data stack limit leaves room for. overflowing either one stops the cpu with `zy: data stack overflow` or `zy: return stack overflow`. the frame stack is checked in every function prologue, which resets the frame pointer and stops with `zy: frame stack overflow`. + +platform: the darwin path uses `MAP_JIT`, `pthread_jit_write_protect_np` and `sys_icache_invalidate`. linux uses rwx mmap and `__builtin___clear_cache`. only linux arm64 has actually been run, under qemu. the darwin path follows apple's documented recipe but hasn't been tested, and under the hardened runtime it also needs the allow-jit entitlement. every host shares the same semantics: shift counts wrap at 32, stores outside memory are ignored, loads outside memory read 0, and jumping outside memory or to an unwired opcode halts. + +## test results + +`test.sh` passes all 131 checks on x86-64 linux: the fixpoint, an up-to-date `genome.h`, and then every example and feature test on the interpreter, on the arm64 jit under qemu, and on the llvm backend, all with identical output. it also covers joins and fixup frees (with a leak count), `switch` and `?:`, the buddy allocator, several errors in one run, all three stack overflows, growable and fixed arrays, and `tests/o3.zc` (loops, recursion, calls inside loops, `switch`, `&& ||`, structs through pointers, arrays, division by constants, and a function with more variables than registers), whose output matches the same file with `o3` taken out, register sharing between scopes with a cached fixed array, and fattening (a list returned from o3 typed all the way down, a struct assigned out of an o3 block and its pointer field). it checks that every o3 region in that test lifts, and gets compiled on the jit and on llvm, that the o3 bounds check fires, and that all 22 programs in `tests/errors/`, all 10 in `tests/typeerrors/` and all 9 in `tests/langerrors/` get rejected with the expected message on each backend, that llvm agrees when it compiles every block, and that the self-compile matches byte for byte on all three. separately, 80000 random divisions through enhancements matched the software routine, covering divide by zero, INT_MIN/-1, negatives and the magic-number path for constants. + +## open ends + +fixed in stage 13 (details in `history/13-gap-fixes`): branch and loop joins, free lists that never merged (now a buddy allocator), missing stack checks, stopping at the first error, macro line numbers, required prototypes, `d*` for storing into borrowed objects, `**` declarations, and `?:` and `switch`. still open: + +**untracked aliases.** the analyzer only follows bare variable names, so `let q = p + 0;` or `let c = list[1];` is a plain word nobody tracks, and it dangles once the owner is freed. two cheap fixes, and they combine well. at runtime: don't reuse a freed block right away. `free` would put it on a quarantine list, and the collector's next sweep only releases quarantined blocks that no word points at. the conservative mark already finds such words, so that's maybe 30 lines in rt.zc. a stale alias then reads a dead but intact block, and a second free through it is a no-op, because the header's flags say free. at compile time: now that there are types, a pointer-typed value computed from a tracked variable (`p + k`, `p.field`, `p[i]` when the element is a pointer) can carry a "borrowed from p" mark into the local it's stored in. using that local after `p` dies becomes an error. that's the same machinery children already use. + +**the conservative collector.** it scans the whole data stack region, because the vm can't read its own stack pointer, and stale words keep garbage alive a little longer. the fix is one base opcode that pushes the data stack depth, which is trivial in hardware since the register already exists. then the collector scans only the live part. zeroing a frame in the prologue would do the same for the frame stack, at a few words per call. + +**coarse invalidation.** a store to a covered word rescans every block, and an install flushes everything. three fixes, in order of payoff. first, index blocks by page (say 64 words): a covered store only looks at the blocks touching its page, and dropping a block only rebuilds the coverage bytes for that block. second, record which enhancement slots each translation inlines, so an install only drops those. third, on llvm, give each block its own resource tracker, so dropped code actually gets freed. + +**automatic tiering.** o3 is the explicit version of what you asked about in message 14. doing it without the keyword means a run counter per block (about 4 instructions per block entry) and, for a hot loop, finding its region and its variables without the compiler's help. the hard part is the variables: o3 can keep them in registers because the compiler promises nothing takes their address, and code without that promise would need the lifter to prove it. + +**o3's own gaps.** calls always leave the region through the dispatcher, so call-heavy code gains nothing (fib above). a direct call from one compiled region into another, with the variables spilled only for the slots the callee can't see, would fix most of that. a value that stays on the vm stack across a branch (like an index across its bounds check) goes through memory, since blocks only share variables. the core's own optimizer works one block at a time (no loop-invariant motion or cross-block cse); llvm does that on its build. regions aren't built into images, so `rt.zc` and the compiler can't use o3. and o3 code that rewrites its own region sees the change at the next entry, not right away. + +**generics are erased.** a generic function is compiled once, so the type stamped by `new(T)` inside it says `T`, and type arguments have to be one word. + +**the rest.** the prescan only knows tags, not types, so a call to a typed function that's defined further down gets `word` as its result type until the definition shows up. the llvm backend compiles one module per block, so it warms up slowly. stores done by enhancements don't invalidate compiled code. a parse word that changes the syntax can confuse the prescan. it only records signatures, though, and a real definition always overrides what the prescan guessed. + +--- + +## transcript + +each of my messages is quoted exactly, followed by a summary of the reply. + +### 1 + +> codegolf a tiny cpu vm in C89. the goal is to achieve the lowest character count possible. include an assembler as well (scored separately) and a hello world program (not scored, but elegance and simplicity is a bonus) +> it must be capable of re-entrant code and a realistically workable instruction set. + +**summary:** a two-stack forth-style machine. the vm was 384 bytes: one ternary chain inside `while(o=m[p++])`, the top two stack items cached in `a` and `b`, the program loaded with `read(open(...))` so it needs no headers, and both stacks inside the 65536-word memory. it had 22 opcodes including div and mod, a two-word literal, and jump/call targets popped from the stack. the assembler was 348 bytes and two-pass, using `strcspn` instead of `strchr` so the implicit int return stays safe on 64-bit. hello world was a pointer loop over a string with a zero terminator. re-entrancy was shown with a recursive factorial plus a recursive decimal printer. + +### 2 + +> your cpu should also be realistically close to hardware (translating it to logical circuitry hardware should be fairly easy) and sport an acceptably fast execution (if hypothetically ported to hardware, not in software) +> +> assume i/o is handled by two external functions, `char Q()` (getchar) and `void G(char)` (putchar) and a state variable J (boolean, shows if input is empty). +> +> don't be afraid to use macros. + +**summary:** no reply was sent to this message at the time. it was answered together with message 3. + +### 3 + +> pro tip: if you define a macro as `!o--`, you can save 2 characters per if condition +> oh and btw, newlines are free + +**summary:** used the tip as `#define X !--o?`, with pre-decrement because opcode 0 (halt) is already handled by the loop, and with the `?` folded into the macro and free newlines used as separators. the hardware-oriented redesign: bit 0 of each word is a literal flag, so literals take one cycle like the j1 cpu. div and mod were dropped because they can't finish in one cycle and were replaced by an arithmetic shift right, giving 8 alu ops that decode from 3 bits. every instruction does a fixed amount of work with at most one data memory access, and call and jump share a datapath (`r-=o`). i/o now goes through `Q`, `G` and a `?` op that reads `J`. the result was the vm at 339 bytes and the assembler at 346. the final golfed code: + +```c +#define X !--o? +int m[1<<16],p,s=65e3,r=6e4,o,a,b;main(c,v)char**v;{read(open(v[1],0),m,s);while(o=m[p++])a=m[s],b=m[s+1],o&1?m[--s]=o>>1:(o/=2,X +m[s]=m[a]:X +m[a]=b,s+=2:X +m[s]=m[s-~a]:X +s++:X +m[s]=b,m[s+1]=a:(o-=2)<1?m[r]=p,r-=o,p=a,s++:X +p=b?p:a,s+=2:X +p=m[--r]:X +m[--s]=Q():X +m[--s]=J:X +G(a),s++:(m[++s]=X +b+a:X +b-a:X +b*a:X +b&a:X +b|a:X +b^a:X +b>>a:b now let's move in stages. write a compiler that takes this assembler's code and converts it into darwin arm + +**summary:** an ungolfed c89 compiler from vm assembly to darwin arm64 assembly. every word got its own label, the image was emitted as an addressable data array, x19/x20/x21 held the memory base and the two stack pointers, and computed jumps went through a table of offsets. a literal followed by a jump became a direct branch. `Q` and `G` went through `sxtb`, since char is signed on apple arm64, and `J` was read with `ldrb`. it was verified by assembling with llvm-mc for darwin, then rewriting to linux with sed and running under qemu, where it matched the vm byte for byte. this version couldn't handle self-modifying code. + +### 5 + +> can you write the assembler in assembly and pair the code with the assembly and the assembler so that it can re-wire itself at runtime? or maybe include an interp for self-modification, otherwise fns are exec'd native + +**summary:** both. `aslib.s` is the assembler written in vm assembly as a library (`a.src`, `a.dst`, `a.org`, `a.lab`, then `a.run c`), and with `asmain.s` it's a 568-word standalone assembler. it's byte-identical to the c assembler and reaches a fixpoint when assembling itself. this is also where the `:;comment` trick came from. the compiler became a hybrid. the image is split into basic blocks, each block counts how many of its words differ from the original image, and stores into code go through a slow path that updates the counters. clean blocks run native, while dirty blocks and anything past the image run in an interpreter compiled into the same binary, which hands control back to native code whenever it lands in a clean block. `cc -t` printed `[` and `]` at each switch. the demo rewired a function at runtime to point at freshly assembled code and then restored it. + +### 6 + +> which one do you like more? and which is more performant? +> keep that one, then port to using llvm ir so that it can be platform independent + +**summary:** no reply was sent at the time. the llvm port was dropped when message 7 changed direction. the question was answered at the start of the reply to message 7: the hybrid was the favorite because it kept the vm's semantics exact. the plain compiler was slightly faster on code that never rewrites itself, but it silently ran stale code once anything did. the zygote beats both. + +### 7 + +> can you make it a zygote approach? +> a minimal core loads first, reads and compiles the program (either wholly or partially, for speed) and replaces it with itself. so it's like a jvm but self-contained and on the same level as itself, where the assembler can itself be modified in-memory to assemble different code. for this we need to ditch llvm + +**summary:** the current design, described above. the native core only does jit and glue. it loads the genome (boot loader + assembler, both vm code) at 48000. the boot loader reads source until eof or a backtick, assembles it at 0 and jumps there. compilation is lazy per block, straight from live memory, with superblocks through `z`, fused constant targets, and dispatch chained through a table. invalidation uses the coverage byte map plus a store hook, so self-modified code gets retranslated instead of interpreted. the resident assembler sits at fixed addresses, and `repl.s` rewrites its mnemonic table so `p` means out, then assembles and runs input chunks in a loop, overwriting the same address each time. verified under qemu on linux arm64, including the self-hosted assembler running on the zygote. about 4x faster than the golfed interpreter. + +### 8 + +> damn dawg you made an entire baremetal os? +> +> let's move to cowork. pack everything up alongside a transcript of every message i've sent and your own notes on what you've implemented in an md file, with the code, so i can move it. + +**summary:** not quite an os. it's a userland runtime. it still runs on darwin or linux, uses their mmap for executable memory, and gets i/o from the host through `Q`, `G` and `J`. there's no scheduler, no drivers and no hardware access, so a closer description is a tiny self-hosting jit, in the spirit of a jvm. the first packing attempt bundled every stage's code with a full test script. + +### 9 + +> nono we don't need all the code, just the recent ones. my msgs and your summaries are more important and all should be there + +**summary:** this package. only the zygote stack plus its examples and tests are included, and earlier stages appear only as summaries. the golfed reference vm and assembler are inlined under message 3, because they define the instruction set. + +### 10 + +> we're gonna make this thing cooler. make the syntax C-like whilst still keeping the forth capabilities like super-homiconicity and the likes, add explicit and automatic gc (use a fast approach) and memory management that looks like this: +> every heap object has a classifier u m d, up middle down. up means the object will die at the scope above (becomes m when returned to the parent scope, and it must be returned and cannot be discarded). m means it must be used here and can be passed down (but once it's recasted as u or d it can't be casted back to m in this scope). d means the variable is passed down, and its lifetime ends after it's given for the first time to a scope lower than the current one. +> ask me questions to clarify this approach, then add it to the code and also add a simple static analyzer which checks these lifetimes + +my answers to the clarifying questions, exactly: + +> scope: every { } block +> +> lending an m object to a callee: borrow, no escape (Recommended) +> +> storing a heap object inside another object or a global: container gets ownership. putting a value inside a container requires its umd to match the parent, and from then on, the umd for that is also inherited from the parent (as in if a parent m is casted to d, so is the child heap object) +> +> gc: free lists + mark-sweep (Recommended) +> +> where the compiler lives: resident in vm code. bootstrap it step by step, by writing a c-like in core.c and then rewriting the compiler inside the c-like. also bring back llvm if you think it'll make your job easier +> +> homoiconic features: parse words, quoted code blocks, inline stack code, patchable compiler tables +> +> how an inner block hands a u object up: expression and assign, both +> +> when the analyzer runs: inside the compiler but can be turned off if the code requests it + +**summary:** the genome is now zc, a typeless c-like language, described above. stage 0 is `zc0.c` (kept as its own file next to core.c rather than inside it, so the runtime still has one native file), and the resident compiler is `src/compiler.zc` plus `src/rt.zc`, which reached a byte-identical fixpoint compiling itself. llvm wasn't brought back, since the target is the vm itself. memory went to 1m words, and core.c gained an interpreter for non-arm64 hosts. calls i made without asking: tags go on signatures (`fn f(m a, d b) -> u`) and recasts are `x as u` / `x as d`; a d object touched inside an inner block moves into that block; storing into a borrowed object, a global or untracked memory needs `as d`; handing a u up by assignment only reaches the directly enclosing block; unchecked code gives its objects to the collector. the old asm genome moved to `legacy/`. + +### 11 + +> progress report? +> add types please :) +> mostly as a compiler primitive though, plus type-inspection via reflection +> and a small optional typechecker (default off) +> oh and keep the stuff you write as a history scrapbook +> if it helps, add a path to the hardware which is called "virtual enhancements", and allows up to 512 custom operations to be compiled down via jit into hardware-level speeds and interoped directly into a fast path in the vm to become essentially custom wiring. useful for division and other stuff as a dynamic runtime path + +my answers to the clarifying questions, exactly: + +> types: words + structs + generics +> +> reflection: runtime type of heap objects, function signatures, typeof at compile time +> +> how an enhancement is defined: either actual C or llvm ir code or vm code or micro-ops passed through an optimizer. since for example, micro-ops and vm code are not sufficient for optimizing things like division +> +> scrapbook: notes + code snapshots + +**summary:** the progress report said the previous round was finished and shipped. i then laid out a plan where the core would compile c or llvm ir enhancement sources at runtime with the host toolchain and load the result. part of that reply was stopped by a safety check before it finished, so i stopped and checked in instead of building it. + +### 12 + +> also make the umd syntax like `type.u name`, and casting is multiplication `u*name` +> ideally i'd prefer memory to be unlimited words growing until system oom, but 1m is fine +> core.c plain interp is also nice +> zc0.c own file is fine +> i mentioned llvm for portability to non-arm targets +> yes, d object semantic that was intended +> u object hands up one block and becomes an m, that's fine. the code can make it a u or a d or whatever +> explain your gaps more +> +> wait why safety check? what's wrong with that + +my answers to the clarifying questions, exactly: + +> declarations: c-style, var -> let +> +> tags on untyped values and the names u, m, d: names reserved, bare .u allowed, untype is m by default +> +> llvm: llvm jit backend (Recommended) + +**summary:** on the safety check: nothing was wrong with the request. my best guess was that it tripped on the plan for a program inside the vm handing source text to the system compiler and loading the result into the running process, since that pattern also shows up in malware. with llvm meant for portability, that path wasn't needed. the gaps got the longer explanations that are now under open ends above. then the round itself: + +**syntax.** declarations are c-style (`int x`, `*node.m p`), `let` replaces `var` for untyped and inferred declarations, tags are `.u .m .d` after the type or on their own, recasts are `u*x` / `d*x` / `m*x`, `u m d` are reserved, and an untagged parameter counts as an m lend. + +**types.** a compiler primitive: hash-consed records with structs, generic structs and functions, fixed arrays, function types, `typeof`/`sizeof`, `as` casts and a reflection api, with object headers carrying their type. the typechecker is off by default and turned on with `typecheck on;`. + +**virtual enhancements.** opcode 21 installs, 22 to 533 are the slots. micro-op recipes and lifted vm code go through an optimizer in the core (folding, cse, dce, multiply-high division by constants) and get inlined by both jits. the language's own `/ % <<` now run on them. + +**backends.** a tiered llvm backend (`-DZYLLVM`) for non-arm hosts. + +**bootstrap and history.** it still reaches its fixpoint on all three backends. `history/` holds notes for every stage and code from stages 7, 10 and this one (in 11-types-enhancements). + +### 13 + +> gaps +> 1. when there's a var state change to d, mark that var as dead by default for any other scope that can access it afterwards. u is fine since it just returns +> 2. any suggestions for a cheap fix? +> 3. it's fine, rare occurrence (probably?), suggest a fix +> 4. fix that +> 5. suggest a fix +> 6. fix +> 7. fix + +**summary:** the numbers followed the gap list from my previous chat message. here's what each one got. + +**1. joins.** a variable that became d, or was given away, on any path is dead after the join, and fixup code frees it on the paths that still own it. a loop's exit state is the join of its breaks and its normal exit. giving something away on a path that loops back is still an error. u and borrows don't get the dead treatment. + +**2, 3 and 5.** suggestions only, now under open ends: a quarantine on reuse plus "borrowed from" marks on pointer-typed values for aliases, a stack-depth opcode for the collector, and page-indexed invalidation plus per-slot install tracking for the jit. + +**4. heap.** a buddy allocator. + +**6. stack checks.** all three stacks are checked: data and return at every block exit on all backends, and frames in every prologue. + +**7. the small stuff.** error recovery with up to 20 reported errors, line numbers carried in token lists, a signature prescan over the whole unit so prototypes are optional, storing into a borrowed object moving the object automatically, `?:`, and go-style `switch`. on the side, a 4-token lookahead fixed `**` declarations. + +### 14 + +> how costly is it if we add a counter to each block to see how many times it runs, then run the optimizer on it if it's a hot path? + +> continue + +**summary:** answered without building anything. about 4 instructions per block entry while a block is being counted, and nothing once its optimized translation replaces it. the optimizing pass would reuse the enhancement lifter, optimizer and register allocator. lifting whole blocks would need jz side exits and barriers at calls and i/o. compiling a block that way takes microseconds, against milliseconds for llvm, and keeping values in registers instead of the memory stack likely gives 2 to 4x on tight loops. it's listed under open ends. then the gap fixes continued. + +### 15 + +> write some really cool examples for this language + +> no, wdym building a fresh vm?? just pocs 😭 we already have a compiler and everything + +**summary:** my progress note had said "building a fresh vm", which only meant compiling core.c into a binary to run the examples. eight proofs of concept went into `examples/`: mandel, bf, dump, tree, lang, staged, quine and life, each described under files above and checked by `test.sh` on all three backends. + +### 16 + +> add opt keywords, for example `optim` (or o3, i prefer o3 as the keyword) will cause any vars in a scope to not assign heap type metadata, compiles with opt level o3 and is immediately compiled down to native, and when used with variables they must be always typed and it reduces the perf cost for accessing or modifying them, etc etc. add a `fixed` keyword which says that an array has a fixed size and cannot be resized, etc + +my answers to the clarifying questions, exactly: + +> where o3 goes: functions, blocks and single vars +> +> how far o3 goes: real optimizing tier (Recommended) +> +> what o3 code may give up: type stamps on objects, zero-filling new objects, the address of o3 vars, stack and frame checks, also i don't like the -> return type syntax. please replace it with the c-like syntax +> +> arrays: add growable arrays too (Recommended) + +**summary:** three things changed. + +**return types.** functions are declared c-style now, `int f(int x)`, `*node.u make()`, with fn types written `int(int, *char)`. `->` is gone from the compiler, zc0, the tests and the examples. + +**arrays.** `[]T` grows (`push`, `pop`, `resize`, `len`, `cap`), and `fixed []T` can't change size after `new(fixed []T, n)`. both are bounds-checked. `[n]T` stays as it was, plus a compile-time check for constant indexes. + +**o3.** on functions, blocks and single variables, with the trade-offs you picked, and a real optimizing tier in the core, described in the o3 section above. it compiles at install time on arm64 and llvm and has a region interpreter for the plain build. measured gains are 1.5 to 2.8x on the interpreter, 8 to 100x on llvm and 20 to 43x on arm64 (under qemu) for loop-heavy kernels, and much less for call-heavy ones. on the way, the arm64 jit learned to specialize an enhancement on literal inputs, so `x / 7` is a multiply-high everywhere, not only in o3 code. tests for arrays and o3 turned up an old heap bug: freeing a container could merge one of its children into it while it was still being freed, which corrupted a free list. growable arrays hit it every time. it's fixed. + +### 17 + +> generics should go after fn name and before parens +> we also need some machinery for converting values to/from o3. to is pretty easy, we just hand over the data, and o3 ignores the fat metadata. from is a bit harder, there needs to be a system to get type hints and fatten the metadata for the packed data +> variables just get the latest free register, it's fine for now +> add the fixed array once per region thing +> +> also if you have any tips on how to make the memory system better (aka avoiding gc and leaks/uaf as much as possible) do tell + +**summary:** generics went back after the name, `T first(...)`, with the return type allowed to use them before the list declares them. o3 got fattening on the way out (the section above): `new_raw` keeps the requested size in the type word, and the compiler calls `fatten` with the static type wherever o3 data reaches normal code. registers now follow scopes, so a closed scope's register goes to the next variable. fixed arrays in o3 variables keep their items and length in two hidden register variables. the memory tips were answered in chat, not built. diff --git a/docs/history/17-o3-boundaries/code/bench.sh b/docs/history/17-o3-boundaries/code/bench.sh new file mode 100755 index 0000000..b1f268c --- /dev/null +++ b/docs/history/17-o3-boundaries/code/bench.sh @@ -0,0 +1,36 @@ +#!/bin/bash +# o3 against plain code: each kernel in tests/bench.zc on each backend, with +# the time to start up and compile (the 'z' run) taken off. needs bc. +cd "$(dirname "$0")"; T=$(mktemp -d); trap 'rm -rf $T' EXIT +gcc -O2 -w -DJTYPE=int core.c io_linux.c -o $T/zy +vms=("$T/zy"); names=("interp") +if command -v llvm-config >/dev/null; then + gcc -O2 -w -DJTYPE=int -DZYLLVM $(llvm-config --cflags) core.c io_linux.c -o $T/zyl $(llvm-config --ldflags --libs) && { vms+=("$T/zyl"); names+=("llvm"); } +fi +if command -v aarch64-linux-gnu-gcc >/dev/null && command -v qemu-aarch64 >/dev/null; then + aarch64-linux-gnu-gcc -O2 -w -DJTYPE=int -static core.c io_linux.c -o $T/zyj && { vms+=("qemu-aarch64 $T/zyj"); names+=("arm64 (qemu)"); } +fi +# seconds per run of kernel $2 on vm $1: the slope between $3 runs and 1 run, +# best of three each, so starting up and compiling cancel out +t(){ local s e; s=$(date +%s.%N); (cat tests/bench.zc; printf '`%s%s' $2 $3) | $1 >/dev/null; e=$(date +%s.%N); echo "$e - $s" | bc; } +best(){ local b=999 x i; for i in 1 2 3; do x=$(t "$1" $2 $3); b=$(echo "if ($x < $b) $x else $b" | bc); done; echo $b; } +# grows the run count until the difference is at least a second, so the noise +# of starting up is small next to it +per(){ + local one n=4 many + one=$(best "$1" $2 1) + while :; do + many=$(best "$1" $2 $n) + if [ "$(echo "$many - $one > 1" | bc)" = 1 ] || [ $n -ge 4096 ]; then break; fi + n=$((n * 4)) + done + echo "scale=6; ($many - $one) / ($n - 1)" | bc +} +printf "%-14s %-8s %10s %10s %8s\n" backend kernel "plain ms" "o3 ms" speedup +for i in "${!vms[@]}"; do + for k in "collatz a b" "sieve c d" "digits e f" "fib g h"; do + set -- $k + p=$(per "${vms[$i]}" $2); o=$(per "${vms[$i]}" $3) + printf "%-14s %-8s %10.2f %10.2f %7.1fx\n" "${names[$i]}" $1 $(echo "$p * 1000" | bc) $(echo "$o * 1000" | bc) $(echo "scale=2; $p / $o" | bc) + done +done diff --git a/docs/history/17-o3-boundaries/code/core.c b/docs/history/17-o3-boundaries/code/core.c new file mode 100644 index 0000000..7f4aa33 --- /dev/null +++ b/docs/history/17-o3-boundaries/code/core.c @@ -0,0 +1,2030 @@ +/* zygote core: puts the genome (the zc compiler and its runtime, both vm code) + into memory and runs it. on arm64, every block of vm code is translated to + arm64 the first time it is reached, straight from live memory. stores that + land on translated words throw those translations away, so code that + rewrites itself, including the resident compiler, just gets retranslated + on the next visit. on other hosts (or with -DZYINTERP) a plain interpreter + with the same semantics runs instead. ZYTRACE=1 logs translations on stderr. */ +#include +#include +#include +#if defined(__aarch64__) && !defined(ZYINTERP) +#define ZYJIT 1 +#include +#ifdef __APPLE__ +#include +#include +#endif +#endif +#include "genome.h" + +#ifndef JTYPE +#define JTYPE char +#endif +char Q(void); +void G(char); +extern JTYPE J; + +#define MW (1 << 20) /* memory, in words */ +#define DS0 (MW - 16) /* data stack grows down from here */ +#define RS0 0xF0000 /* return stack grows up from here */ +#define DSLIM (0xFC000 + 1100) /* data stack overflows below this (a block pushes at most ~1024) */ +#define RSLIM (0xF8000 - 8) /* return stack overflows above this */ +/* why the cpu stopped, when it wasn't a halt */ +static int stopped(int p) +{ + if (p == MW + 1) { fprintf(stderr, "zy: data stack overflow\n"); return 2; } + if (p == MW + 2) { fprintf(stderr, "zy: return stack overflow\n"); return 2; } + return 0; +} +#define CODEW (4 << 20) /* 16 MB of code, in instruction words */ +#define MAXB 65536 +#define BLKW 1024 /* longest block, in vm words */ + +static int m[MW]; +static int trace; + +/* ---- virtual enhancements ---- + up to 512 custom opcodes (22 .. 533), wired in at runtime by the install + opcode (21). an enhancement is a short program over native micro-ops, + written either as a recipe (text) or as straight-line vm code that gets + lifted. both become ssa, go through the optimizer below, and then run in + the interpreter or get inlined by the jit as native code. */ +#define NENH 512 +#define XOP0 22 +enum { U_LI, U_MOV, U_ADD, U_SUB, U_MUL, U_SDIV, U_UDIV, U_SREM, U_UREM, U_AND, U_OR, U_XOR, + U_SHL, U_SHR, U_SAR, U_NEG, U_NOT, U_LT, U_LTU, U_EQ, U_NE, U_MIN, U_MAX, U_MINU, U_MAXU, + U_MULHS, U_MULHU, U_CLZ, U_ABS, U_SEL, U_LD, U_ST, U_N }; +static const char *unames[U_N] = { "li", "mov", "add", "sub", "mul", "sdiv", "udiv", "srem", "urem", + "and", "or", "xor", "shl", "shr", "sar", "neg", "not", "lt", "ltu", "eq", "ne", "min", "max", + "minu", "maxu", "mulhs", "mulhu", "clz", "abs", "sel", "ld", "st" }; +static const signed char uarity[U_N] = { 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 1, 3, 1, 2 }; +typedef struct { int op, d, a, b, c, imm; } Uop; /* ssa: d is a fresh value, a b c are values */ +typedef struct { + int live, kind, nin, nout, n, nv, out[8]; + Uop *u; + signed char *reg; /* physical register per value, for the jit; 0 if it didn't fit */ +} Enh; +static Enh enh[NENH]; +static int need_flush; + +#define MAXUOP 4096 +static Uop ub[MAXUOP]; static int un, unv, uerr; /* the program being built */ +static int unew(int op, int a, int b, int c, int imm) +{ + Uop *u; + if (un >= MAXUOP) { uerr = -5; return 0; } + u = &ub[un++]; u->op = op; u->d = unv++; u->a = a; u->b = b; u->c = c; u->imm = imm; + return u->d; +} +static int mread(int a) { return (unsigned)a < MW ? m[a] : 0; } + +/* recipe text: one statement per line or per ';' + r0 = sdiv r0 r1 dst = op operands, operands are rN, parameter names or numbers + r2 = r0 a copy, or a number + st r1 r0 memory[r1] = r0 + inputs arrive in r0.. (and under the parameter names), outputs leave from r0.. */ +static int parse_recipe(int src, int names, int nin, int nout, int *out) +{ + int cur[16], i, p = src, n, k, op, args[3], na, dst, isst; + char tok[8][32]; int nt; + for (i = 0; i < 16; i++) cur[i] = i < nin ? i : -1; + for (;;) { + /* read one statement into tokens */ + nt = 0; + for (;;) { + int c = mread(p); + while (c == ' ' || c == '\t' || c == '\r') c = mread(++p); + if (!c || c == '\n' || c == ';') break; + if (c == '=') { if (nt < 8) strcpy(tok[nt++], "="); p++; continue; } + n = 0; + while (c && c != ' ' && c != '\t' && c != '\n' && c != ';' && c != '=' && c != '\r') { if (n < 31) tok[nt < 8 ? nt : 7][n++] = (char)c; c = mread(++p); } + if (nt < 8) tok[nt++][n] = 0; + } + if (nt) { + isst = !strcmp(tok[0], "st"); + if (!isst && (nt < 3 || strcmp(tok[1], "="))) return -2; + /* destination register */ + dst = -1; + if (!isst) { + if (tok[0][0] == 'r' && tok[0][1] >= '0' && tok[0][1] <= '9') dst = atoi(tok[0] + 1); + else for (i = 0; i < nin && names; i++) { + int s = mread(names + i), j = 0; + while (mread(s + j) && mread(s + j) == (unsigned char)tok[0][j]) j++; + if (!mread(s + j) && !tok[0][j]) dst = i; + } + if (dst < 0 || dst > 15) return -2; + } + k = isst ? 1 : 2; /* first token after "=" (or after st) */ + op = -1; + if (!isst && nt > 3) { for (i = 0; i < U_N; i++) if (!strcmp(tok[k], unames[i])) op = i; if (op < 0) return -2; k++; } + if (isst) op = U_ST; + na = 0; + for (; k < nt; k++) { + char *t = tok[k], *e; int v = -1; + if (na >= 3) return -2; + if (t[0] == 'r' && t[1] >= '0' && t[1] <= '9') { int r = atoi(t + 1); if (r > 15) return -2; if (cur[r] < 0) cur[r] = unew(U_LI, 0, 0, 0, 0); v = cur[r]; } + else if ((t[0] >= '0' && t[0] <= '9') || t[0] == '-') { long x = strtol(t, &e, 0); if (*e) return -2; v = unew(U_LI, 0, 0, 0, (int)x); } + else { + for (i = 0; i < nin && names; i++) { + int s = mread(names + i), j = 0; + while (mread(s + j) && mread(s + j) == (unsigned char)t[j]) j++; + if (!mread(s + j) && !t[j]) v = cur[i] < 0 ? (cur[i] = unew(U_LI, 0, 0, 0, 0)) : cur[i]; + } + if (v < 0) return -2; + } + args[na++] = v; + } + if (op < 0) { /* x = y */ + if (na != 1) return -2; + cur[dst] = args[0]; + } else { + if (na != (op == U_LI ? 0 : uarity[op])) { + if (!(op == U_LI && na == 1)) return -2; + } + if (op == U_LI) cur[dst] = args[0]; + else if (op == U_ST) unew(U_ST, args[0], args[1], 0, 0); + else cur[dst] = unew(op, args[0], na > 1 ? args[1] : 0, na > 2 ? args[2] : 0, 0); + } + if (uerr) return uerr; + } + if (!mread(p)) break; + p++; + } + for (i = 0; i < nout; i++) { if (cur[i] < 0) cur[i] = unew(U_LI, 0, 0, 0, 0); out[i] = cur[i]; } + return uerr; +} + +/* straight-line vm code from addr up to its ret, run symbolically */ +static int lift_vm(int a, int len, int nin, int nout, int *out) +{ + int st[256], sp = 0, i, w, k, x, y; + for (i = 0; i < nin; i++) st[sp++] = i; + for (i = 0; i < len; i++) { + w = mread(a + i); + if (w & 1) { if (sp >= 256) return -4; st[sp++] = unew(U_LI, 0, 0, 0, w >> 1); continue; } + k = w / 2; + if (k == 9) break; + switch (k) { + case 1: if (sp < 1) return -4; st[sp - 1] = unew(U_LD, st[sp - 1], 0, 0, 0); break; + case 2: if (sp < 2) return -4; unew(U_ST, st[sp - 1], st[sp - 2], 0, 0); sp -= 2; break; + case 3: /* pick needs a constant index */ + if (sp < 1) return -4; + x = st[sp - 1]; + if (x < nin) return -4; + { int j; for (j = 0; j < un && ub[j].d != x; j++); if (j == un || ub[j].op != U_LI) return -4; y = ub[j].imm; } + if (y < 0 || y >= sp - 1) return -4; + st[sp - 1] = st[sp - 2 - y]; + break; + case 4: if (sp < 1) return -4; sp--; break; + case 5: if (sp < 2) return -4; x = st[sp - 1]; st[sp - 1] = st[sp - 2]; st[sp - 2] = x; break; + case 13: case 14: case 15: case 16: case 17: case 18: case 19: case 20: { + static const int map[] = { U_ADD, U_SUB, U_MUL, U_AND, U_OR, U_XOR, U_SAR, U_LT }; + if (sp < 2) return -4; + x = st[sp - 1]; y = st[sp - 2]; sp -= 2; + st[sp++] = unew(map[k - 13], y, x, 0, 0); + break; } + default: return -4; /* control flow, i/o and other enhancements don't lift */ + } + if (uerr) return uerr; + } + if (sp != nout) return -4; + for (i = 0; i < nout; i++) out[i] = st[i]; + return uerr; +} + +/* ---- the optimizer ---- + one forward pass of copy propagation, constant folding, algebraic + simplification, division by constants (the multiply-high trick) and + common subexpression elimination, then dead code removal. */ +static int ev(int op, int a, int b, int c) +{ + unsigned ua = (unsigned)a, ub_ = (unsigned)b; + switch (op) { + case U_ADD: return (int)(ua + ub_); + case U_SUB: return (int)(ua - ub_); + case U_MUL: return (int)(ua * ub_); + case U_SDIV: return b == 0 ? 0 : (a == (int)0x80000000 && b == -1) ? a : a / b; + case U_UDIV: return b == 0 ? 0 : (int)(ua / ub_); + case U_SREM: return b == 0 ? a : (a == (int)0x80000000 && b == -1) ? 0 : a % b; + case U_UREM: return b == 0 ? a : (int)(ua % ub_); + case U_AND: return a & b; + case U_OR: return a | b; + case U_XOR: return a ^ b; + case U_SHL: return (int)(ua << (b & 31)); + case U_SHR: return (int)(ua >> (b & 31)); + case U_SAR: return a >> (b & 31); + case U_NEG: return (int)(0u - ua); + case U_NOT: return ~a; + case U_LT: return a < b; + case U_LTU: return ua < ub_; + case U_EQ: return a == b; + case U_NE: return a != b; + case U_MIN: return a < b ? a : b; + case U_MAX: return a > b ? a : b; + case U_MINU: return ua < ub_ ? a : b; + case U_MAXU: return ua > ub_ ? a : b; + case U_MULHS: return (int)(((long long)a * (long long)b) >> 32); + case U_MULHU: return (int)(((unsigned long long)ua * ub_) >> 32); + case U_CLZ: { int n = 0; if (!ua) return 32; while (!(ua & 0x80000000u)) { ua <<= 1; n++; } return n; } + case U_ABS: return a < 0 ? (int)(0u - ua) : a; + case U_SEL: return a ? b : c; + } + return 0; +} +static void smagic(int d, int *mul, int *sh) /* hacker's delight, signed */ +{ + const unsigned two31 = 0x80000000u; + unsigned ad = d < 0 ? 0u - (unsigned)d : (unsigned)d, t = two31 + ((unsigned)d >> 31); + unsigned anc = t - 1 - t % ad, q1 = two31 / anc, r1 = two31 - q1 * anc, q2 = two31 / ad, r2 = two31 - q2 * ad, delta; + int p = 31; + do { + p++; + q1 *= 2; r1 *= 2; if (r1 >= anc) { q1++; r1 -= anc; } + q2 *= 2; r2 *= 2; if (r2 >= ad) { q2++; r2 -= ad; } + delta = ad - r2; + } while (q1 < delta || (q1 == delta && r1 == 0)); + *mul = (int)(q2 + 1); if (d < 0) *mul = -*mul; + *sh = p - 32; +} +static void umagic(unsigned d, unsigned *mul, int *add, int *sh) /* hacker's delight, unsigned */ +{ + unsigned nc = 0xFFFFFFFFu - (0u - d) % d, q1, r1, q2, r2, delta; + int p = 31; + *add = 0; + q1 = 0x80000000u / nc; r1 = 0x80000000u - q1 * nc; + q2 = 0x7FFFFFFFu / d; r2 = 0x7FFFFFFFu - q2 * d; + do { + p++; + if (r1 >= nc - r1) { q1 = 2 * q1 + 1; r1 = 2 * r1 - nc; } else { q1 = 2 * q1; r1 = 2 * r1; } + if (r2 + 1 >= d - r2) { if (q2 >= 0x7FFFFFFFu) *add = 1; q2 = 2 * q2 + 1; r2 = 2 * r2 + 1 - d; } + else { if (q2 >= 0x80000000u) *add = 1; q2 = 2 * q2; r2 = 2 * r2 + 1; } + delta = d - 1 - r2; + } while (p < 64 && (q1 < delta || (q1 == delta && r1 == 0))); + *mul = q2 + 1; *sh = p - 32; +} + +static Uop ob[MAXUOP]; static int on, onv; +static int *omap, *isk, *kv; /* old value -> new value; constness of new values */ +static int pure(int op) { return op != U_LD && op != U_ST; } +static int ispow2(unsigned x) { return x && !(x & (x - 1)); } +static int lg2(unsigned x) { int n = 0; while (x > 1) { x >>= 1; n++; } return n; } +static int oe(int op, int a, int b, int c, int imm); +static int K(int v) { return oe(U_LI, 0, 0, 0, v); } +static int raw(int op, int a, int b, int c, int imm) +{ + Uop *u; int i; + if (pure(op)) /* cse */ + for (i = 0; i < on; i++) { + u = &ob[i]; + if (u->op == op && u->a == a && u->b == b && u->c == c && u->imm == imm) return u->d; + } + if (on >= MAXUOP) { uerr = -5; return 0; } + u = &ob[on++]; u->op = op; u->d = onv++; u->a = a; u->b = b; u->c = c; u->imm = imm; + isk[u->d] = op == U_LI; kv[u->d] = imm; + return u->d; +} +static int sdivk(int a, int d) /* a / d for a constant d, no divide instruction */ +{ + int mul, sh, q, t; + if (d == 1) return a; + if (d == -1) return oe(U_NEG, a, 0, 0, 0); + if (d == (int)0x80000000) return oe(U_EQ, a, K(d), 0, 0); + if (ispow2((unsigned)(d < 0 ? -d : d))) { + int k = lg2((unsigned)(d < 0 ? -d : d)); + t = oe(U_SAR, a, K(31), 0, 0); + t = oe(U_SHR, t, K(32 - k), 0, 0); + q = oe(U_SAR, oe(U_ADD, a, t, 0, 0), K(k), 0, 0); + return d < 0 ? oe(U_NEG, q, 0, 0, 0) : q; + } + smagic(d, &mul, &sh); + q = oe(U_MULHS, a, K(mul), 0, 0); + if (d > 0 && mul < 0) q = oe(U_ADD, q, a, 0, 0); + if (d < 0 && mul > 0) q = oe(U_SUB, q, a, 0, 0); + if (sh) q = oe(U_SAR, q, K(sh), 0, 0); + return oe(U_ADD, q, oe(U_SHR, q, K(31), 0, 0), 0, 0); +} +static int udivk(int a, unsigned d) +{ + unsigned mul; int add, sh, t; + if (d == 1) return a; + if (ispow2(d)) return oe(U_SHR, a, K(lg2(d)), 0, 0); + if (d > 0x80000000u) return oe(U_XOR, oe(U_LTU, a, K((int)d), 0, 0), K(1), 0, 0); /* a >= d */ + umagic(d, &mul, &add, &sh); + t = oe(U_MULHU, a, K((int)mul), 0, 0); + if (!add) return sh ? oe(U_SHR, t, K(sh), 0, 0) : t; + t = oe(U_ADD, oe(U_SHR, oe(U_SUB, a, t, 0, 0), K(1), 0, 0), t, 0, 0); + return sh > 1 ? oe(U_SHR, t, K(sh - 1), 0, 0) : t; +} +static int oe(int op, int a, int b, int c, int imm) +{ + int ka, kb, A, B; + if (op == U_LI) return raw(U_LI, 0, 0, 0, imm); + if (op == U_MOV) return a; + if (op == U_LD || op == U_ST) return raw(op, a, b, 0, 0); + ka = isk[a]; kb = uarity[op] > 1 && isk[b]; + A = kv[a]; B = kv[b]; + /* everything constant: fold */ + if (ka && (uarity[op] < 2 || kb) && (uarity[op] < 3 || isk[c])) return K(ev(op, A, B, kv[c])); + /* constants go right for commutative ops */ + if (ka && !kb && (op == U_ADD || op == U_MUL || op == U_AND || op == U_OR || op == U_XOR || op == U_EQ || op == U_NE || + op == U_MIN || op == U_MAX || op == U_MINU || op == U_MAXU || op == U_MULHS || op == U_MULHU)) { + int t = a; a = b; b = t; kb = 1; B = A; ka = 0; + } + if (op == U_SEL && ka) return A ? b : c; + if (op == U_SEL && b == c) return b; + if (kb) switch (op) { + case U_ADD: case U_SUB: case U_OR: case U_XOR: case U_SHL: case U_SHR: case U_SAR: + if (!B || (op >= U_SHL && op <= U_SAR && !(B & 31))) return a; + break; + case U_MUL: + if (B == 0) return K(0); + if (B == 1) return a; + if (B == -1) return oe(U_NEG, a, 0, 0, 0); + if (ispow2((unsigned)B)) return oe(U_SHL, a, K(lg2((unsigned)B)), 0, 0); + break; + case U_AND: if (B == 0) return K(0); if (B == -1) return a; break; + case U_SDIV: if (B == 0) return K(0); return sdivk(a, B); + case U_SREM: if (B == 0) return a; if (B == 1 || B == -1) return K(0); + return oe(U_SUB, a, oe(U_MUL, sdivk(a, B), b, 0, 0), 0, 0); + case U_UDIV: if (B == 0) return K(0); return udivk(a, (unsigned)B); + case U_UREM: if (B == 0) return a; + if (ispow2((unsigned)B)) return oe(U_AND, a, K(B - 1), 0, 0); + return oe(U_SUB, a, oe(U_MUL, udivk(a, (unsigned)B), b, 0, 0), 0, 0); + } + if (a == b && uarity[op] == 2) { + if (op == U_SUB || op == U_XOR || op == U_LT || op == U_LTU || op == U_NE) return K(0); + if (op == U_AND || op == U_OR || op == U_MIN || op == U_MAX || op == U_MINU || op == U_MAXU) return a; + if (op == U_EQ) return K(1); + } + return raw(op, a, b, c, 0); +} +/* runs the optimizer over ub[0..un) with nin inputs; leaves the result in e */ +static int optimize(Enh *e, int nin, int *out, int nout) +{ + int i, *live, n = 0; + static int omap_[MAXUOP + 16], isk_[MAXUOP * 4], kv_[MAXUOP * 4]; + omap = omap_; isk = isk_; kv = kv_; + on = 0; onv = nin; + for (i = 0; i < nin; i++) { omap[i] = i; isk[i] = 0; } + for (i = 0; i < un && !uerr; i++) { + Uop *u = &ub[i]; + int a = uarity[u->op] > 0 || u->op == U_ST || u->op == U_LD ? omap[u->a] : 0; + int b = uarity[u->op] > 1 || u->op == U_ST ? omap[u->b] : 0; + int c = uarity[u->op] > 2 ? omap[u->c] : 0; + if (u->op == U_ST) oe(U_ST, a, b, 0, 0); + else omap[u->d] = oe(u->op, a, b, c, u->imm); + } + if (uerr) return uerr; + /* dead code: outputs and stores are live */ + live = calloc(onv + 1, sizeof *live); + for (i = 0; i < nout; i++) { e->out[i] = omap[out[i]]; live[e->out[i]] = 1; } + for (i = on; i--;) { + Uop *u = &ob[i]; + if (u->op != U_ST && !live[u->d]) { u->op = -1; continue; } + if (uarity[u->op] > 0 || u->op == U_ST || u->op == U_LD) live[u->a] = 1; + if (uarity[u->op] > 1 || u->op == U_ST) live[u->b] = 1; + if (uarity[u->op] > 2) live[u->c] = 1; + } + free(live); + e->u = malloc((on + 1) * sizeof(Uop)); + for (i = 0; i < on; i++) if (ob[i].op >= 0) e->u[n++] = ob[i]; + e->n = n; e->nv = onv; e->nin = nin; e->nout = nout; + return 0; +} +/* run an installed enhancement on a downward-growing stack; returns the new top */ +static int *xexec(Enh *e, int *sp) +{ + int vb[256], *v = e->nv <= 256 ? vb : malloc(e->nv * sizeof(int)), i, j; + for (j = 0; j < e->nin; j++) v[j] = sp[e->nin - 1 - j]; + for (i = 0; i < e->n; i++) { + Uop *u = &e->u[i]; + switch (u->op) { + case U_LI: v[u->d] = u->imm; break; + case U_LD: v[u->d] = mread(v[u->a]); break; + case U_ST: if ((unsigned)v[u->a] < MW) m[v[u->a]] = v[u->b]; break; + default: v[u->d] = ev(u->op, v[u->a], v[u->b], v[u->c]); + } + } + sp += e->nin - e->nout; + for (j = 0; j < e->nout; j++) sp[e->nout - 1 - j] = v[e->out[j]]; + if (v != vb) free(v); + return sp; +} +static void xdump(int slot, Enh *e) +{ + int i; + fprintf(stderr, "enh %d: %d in, %d out, %d ops\n", slot, e->nin, e->nout, e->n); + for (i = 0; i < e->n; i++) { + Uop *u = &e->u[i]; + if (u->op == U_ST) fprintf(stderr, " st v%d v%d\n", u->a, u->b); + else if (u->op == U_LI) fprintf(stderr, " v%d = %d\n", u->d, u->imm); + else { fprintf(stderr, " v%d = %s v%d", u->d, unames[u->op], u->a); if (uarity[u->op] > 1) fprintf(stderr, " v%d", u->b); if (uarity[u->op] > 2) fprintf(stderr, " v%d", u->c); fprintf(stderr, "\n"); } + } + for (i = 0; i < e->nout; i++) fprintf(stderr, " out%d = v%d\n", i, e->out[i]); +} +/* registers for the jit: values live in w0..w15, straight-line linear scan. + if they don't fit, reg stays 0 and the jit calls the interpreter instead. */ +static void regalloc(Enh *e) +{ + int *last = malloc(e->nv * sizeof(int)), i, j, k, fr = 0xffff; + signed char *r = malloc(e->nv); + for (i = 0; i < e->nv; i++) { last[i] = -2; r[i] = -1; } + for (i = 0; i < e->n; i++) { + Uop *u = &e->u[i]; + if (uarity[u->op] > 0 || u->op == U_ST || u->op == U_LD) last[u->a] = i; + if (uarity[u->op] > 1 || u->op == U_ST) last[u->b] = i; + if (uarity[u->op] > 2) last[u->c] = i; + } + for (i = 0; i < e->nout; i++) last[e->out[i]] = e->n; +#define TAKE(v) do { for (k = 0; k < 16 && !(fr >> k & 1); k++); if (k == 16) goto fail; fr &= ~(1 << k); r[v] = (signed char)k; } while (0) + for (j = 0; j < e->nin; j++) if (last[j] >= -1 && last[j] != -2) TAKE(j); + for (i = 0; i < e->n; i++) { + Uop *u = &e->u[i]; int ops[3], no = 0; + if (uarity[u->op] > 0 || u->op == U_ST || u->op == U_LD) ops[no++] = u->a; + if (uarity[u->op] > 1 || u->op == U_ST) ops[no++] = u->b; + if (uarity[u->op] > 2) ops[no++] = u->c; + for (j = 0; j < no; j++) if (last[ops[j]] == i && r[ops[j]] >= 0 && (fr >> r[ops[j]] & 1) == 0) fr |= 1 << r[ops[j]]; + if (u->op != U_ST) TAKE(u->d); + if (u->op != U_ST && last[u->d] == -2) fr |= 1 << r[u->d]; /* never used */ + } +#undef TAKE + free(last); e->reg = r; return; +fail: + free(last); free(r); e->reg = 0; +} +/* the install opcode. descriptor: slot, kind (0 recipe, 1 vm code), inputs, + outputs, payload address, payload length, parameter names (or 0). kind 2 + is an o3 region (below). + returns 0, or -1 bad descriptor, -2 recipe syntax, -4 code doesn't lift, + -5 too big. */ +static int o3install(int d); +static int install(int d) +{ + int slot = mread(d), kind = mread(d + 1), nin = mread(d + 2), nout = mread(d + 3); + int pay = mread(d + 4), len = mread(d + 5), names = mread(d + 6), out[8], r; + Enh ne; + if (kind == 2) return o3install(d); + if ((unsigned)slot >= NENH || (unsigned)nin > 8 || (unsigned)nout > 8 || (kind != 0 && kind != 1)) return -1; + un = 0; unv = nin; uerr = 0; + r = kind == 0 ? parse_recipe(pay, names, nin, nout, out) : lift_vm(pay, len, nin, nout, out); + if (r) { if (trace) fprintf(stderr, "enh %d: install failed (%d)\n", slot, r); return r; } + memset(&ne, 0, sizeof ne); + if ((r = optimize(&ne, nin, out, nout))) return r; + ne.kind = kind; ne.live = 1; + regalloc(&ne); + if (enh[slot].live) { free(enh[slot].u); free(enh[slot].reg); } + enh[slot] = ne; + need_flush = 1; + if (trace) xdump(slot, &enh[slot]); + return 0; +} + +/* ---- o3: optimized regions ---- + o3 code comes from the compiler as a region: a range [lo, hi) of vm words + plus the frame slots that nothing takes the address of, each with one of 7 + registers (install kind 2: [-, 2, lo, hi, n, slot, register, ...]). the region is cut into basic blocks. each + block is lifted to ssa: the vm stack is tracked symbolically, so only what + is left on it at the end of the block touches memory, the slots live in + registers across blocks (VGET reads one at block entry, the block's end + says what each one holds now), the frame pointer is var 0, and enhancements + are spliced in. every block then goes through the optimizer above. a + backend compiles the whole region at once: arm64, llvm at O3, or the + region interpreter below. the region is entered at lo and at the return + points of its calls. a register can stand for several slots (variables + whose scopes don't overlap), and spilling writes it to all of them. calls, + returns, jumps out, and anything a block can't + lift leave it: the slots go back to the frame first, so the code outside + sees the usual memory. stores to its own code drop it; it lifts again on + its next entry. */ +#define ZFP 0xA0000 /* the frame pointer word */ +#define U_VGET U_N /* a var at block entry; imm = var */ +#define U_SLD (U_N + 1) /* the stack word at offset imm from the top at block entry */ +#define RMAXV 8 +#define RMAXE 256 +enum { T_BR, T_CBR, T_JMP, T_CALL, T_RET }; +typedef struct { + int a, n, nv, delta, nso, term, cv, s1, s2, x1, x2; + int vs[RMAXV]; /* each var's value at the end, -1 if unchanged */ + int *so; /* stack words to write at the end: offset, value */ + Uop *u; +} RB; +typedef struct { + int idx, lo, hi, nv, slot[RMAXV], lifted, dead, nonat, nb, ne, maxnv; + int np, ps[32], pv[32]; /* frame slot -> var; vars in scopes that never overlap share one */ + int ent[RMAXE], entb[RMAXE]; + RB *b; + void *nat, *stub[RMAXE]; /* the backend's code */ +} Rg; +#define MAXRG 4096 +static Rg *rgs[MAXRG]; +static int nrg; +static unsigned short o3ent[MW]; /* region + 1 at an entry point */ +static unsigned char o3ei[MW]; /* which of its entries */ +static void rforget(Rg *R); /* the backend drops its code */ +static void rmark(Rg *R); /* the backend watches the region's words */ +static void rcompile(Rg *R, int eager); + +static int nargs(int op) { return op >= U_N ? 0 : uarity[op]; } +static void rclear(Rg *R) +{ + int b; + rforget(R); + for (b = 0; b < R->nb; b++) { free(R->b[b].u); free(R->b[b].so); } + free(R->b); R->b = 0; R->nb = 0; R->lifted = 0; +} +static void rdrop_all(void) { int j; for (j = 0; j < nrg; j++) if (rgs[j]->lifted) rclear(rgs[j]); } + +/* lifting one block */ +static Rg *R_; +static unsigned char *lead_; +static int *bidx_, *wl_, nw_; +static int lv_[RMAXV], lw_[RMAXV]; +#define SB 1100 +static int skv[2 * SB], stp, slo, shi; +static unsigned char skk[2 * SB]; +static int afp[MAXUOP * 4 + 16]; /* value -> 1 + its offset from fp, if it is fp + k */ +static int emap[MAXUOP * 4 + 16]; + +static int rvar(int v) +{ + if (lv_[v] < 0) { lv_[v] = raw(U_VGET, 0, 0, 0, v); if (!v) afp[lv_[v]] = 1; } + return lv_[v]; +} +static int slotvar(int off) { int j; for (j = 0; j < R_->np; j++) if (R_->ps[j] == off) return R_->pv[j]; return 0; } +static int rbin(int op, int x, int y, int z) +{ + int r = oe(op, x, y, z, 0); + if (op == U_ADD && !afp[r]) { + if (afp[x] && isk[y]) afp[r] = afp[x] + kv[y]; + else if (afp[y] && isk[x]) afp[r] = afp[y] + kv[x]; + } + return r; +} +static int rld(int a) +{ + int v; + if (isk[a] && kv[a] == ZFP) return rvar(0); + if (afp[a] && (v = slotvar(afp[a] - 1))) return rvar(v); + return oe(U_LD, a, 0, 0, 0); +} +static int rst(int a, int b) +{ + int v; + if (isk[a] && kv[a] == ZFP) return 0; /* moving the frame: not in here */ + if (afp[a] && (v = slotvar(afp[a] - 1))) { lv_[v] = b; lw_[v] = 1; return 1; } + oe(U_ST, a, b, 0, 0); + return 1; +} +static int sget(int o) +{ + if (o < -SB || o >= SB) { uerr = -6; return 0; } + if (!skk[o + SB]) { + if (o < 0) { uerr = -6; return 0; } + skv[o + SB] = raw(U_SLD, 0, 0, 0, o); skk[o + SB] = 1; + } + if (o < slo) slo = o; + if (o >= shi) shi = o + 1; + return skv[o + SB]; +} +static void sset(int o, int v) +{ + if (o < -SB || o >= SB) { uerr = -6; return; } + skv[o + SB] = v; skk[o + SB] = 1; + if (o < slo) slo = o; + if (o >= shi) shi = o + 1; +} +static int spop(void) { return sget(stp++); } +static void spush(int v) { sset(--stp, v); } +static int rblk(int t) { return t >= R_->lo && t < R_->hi && lead_[t - R_->lo] ? bidx_[t - R_->lo] : -1; } +/* an enhancement, spliced into the block */ +static int rsplice(Enh *e) +{ + int i, j; + for (j = e->nin; j--;) emap[j] = spop(); + for (i = 0; i < e->n && !uerr; i++) { + Uop *u = &e->u[i]; + int na = nargs(u->op), a = na > 0 ? emap[u->a] : 0, b = na > 1 ? emap[u->b] : 0, c = na > 2 ? emap[u->c] : 0; + if (u->op == U_LI) emap[u->d] = K(u->imm); + else if (u->op == U_LD) emap[u->d] = rld(a); + else if (u->op == U_ST) { if (!rst(a, b)) uerr = -4; } + else emap[u->d] = rbin(u->op, a, b, c); + } + for (j = 0; j < e->nout; j++) spush(emap[e->out[j]]); + return uerr; +} +static int lift_block(int bi) +{ + static const int map[] = { U_ADD, U_SUB, U_MUL, U_AND, U_OR, U_XOR, U_SAR, U_LT }; + RB *B = &R_->b[bi]; + int i = B->a, w, k, x, y, t, j, o, n = 0, *live; + on = 0; onv = 0; uerr = 0; + memset(afp, 0, sizeof afp); memset(skk, 0, sizeof skk); + stp = slo = shi = 0; + for (j = 0; j < RMAXV; j++) { lv_[j] = -1; lw_[j] = 0; } + B->term = T_JMP; B->cv = -1; B->s1 = B->s2 = -1; B->x1 = B->x2 = 0; + for (;;) { + if (uerr) return uerr; + if (i != B->a && (t = rblk(i)) >= 0) { B->term = T_BR; B->s1 = t; goto done; } + if (i < R_->lo || i >= R_->hi) goto leave_at; + w = m[i]; + if (w & 1) { spush(K(w >> 1)); i++; continue; } + k = w / 2; + switch (k) { + case 0: B->cv = K(-1); goto done; /* halt */ + case 1: x = spop(); spush(rld(x)); break; + case 2: + x = sget(stp); + if (isk[x] && kv[x] == ZFP) goto leave_at; + x = spop(); y = spop(); rst(x, y); + break; + case 3: /* pick: a constant depth */ + x = sget(stp); + if (!isk[x] || kv[x] < 0 || kv[x] > 1024) goto leave_at; + stp++; spush(sget(stp + kv[x])); + break; + case 4: stp++; break; + case 5: x = sget(stp); y = sget(stp + 1); sset(stp, y); sset(stp + 1, x); break; + case 6: B->cv = spop(); B->term = T_CALL; B->x2 = i + 1; goto done; + case 7: + x = spop(); + if (isk[x] && (t = rblk(kv[x])) >= 0) { B->term = T_BR; B->s1 = t; } + else B->cv = x; + goto done; + case 8: + x = sget(stp); + if (!isk[x]) goto leave_at; + stp++; y = spop(); + if (isk[y]) { /* a known condition */ + if (kv[y]) { i++; continue; } + if ((t = rblk(kv[x])) >= 0) { B->term = T_BR; B->s1 = t; } else B->cv = K(kv[x]); + goto done; + } + B->term = T_CBR; B->cv = y; + B->x1 = kv[x]; B->s1 = rblk(kv[x]); + B->x2 = i + 1; B->s2 = rblk(i + 1); + goto done; + case 9: B->term = T_RET; goto done; + case 13: case 14: case 15: case 16: case 17: case 18: case 19: case 20: + x = spop(); y = spop(); spush(rbin(map[k - 13], y, x, 0)); + break; + default: + if (k >= XOP0 && k < XOP0 + NENH && enh[k - XOP0].live) { if (rsplice(&enh[k - XOP0])) return uerr; break; } + goto leave_at; /* i/o, install, unwired: done the usual way */ + } + i++; + } +leave_at: + B->term = T_JMP; B->cv = K(i); +done: + if (uerr) return uerr; + B->delta = stp; + B->so = malloc((shi - slo + 1) * 2 * sizeof(int)); + B->nso = 0; + for (o = stp > slo ? stp : slo; o < shi; o++) + if (skk[o + SB]) { + x = skv[o + SB]; + if (ob[x].op == U_SLD && ob[x].imm == o) continue; /* still the word that was there */ + if (afp[x] && slotvar(afp[x] - 1)) return -7; /* a register var's address can't wait in memory */ + B->so[2 * B->nso] = o; B->so[2 * B->nso + 1] = x; B->nso++; + } + for (j = 0; j < RMAXV; j++) B->vs[j] = -1; + for (j = 1; j < R_->nv; j++) + if (lw_[j] && !(ob[lv_[j]].op == U_VGET && ob[lv_[j]].imm == j)) B->vs[j] = lv_[j]; + /* dead code: what the block leaves behind and its stores are live */ + live = calloc(onv + 1, sizeof *live); + for (j = 0; j < B->nso; j++) live[B->so[2 * j + 1]] = 1; + for (j = 1; j < R_->nv; j++) if (B->vs[j] >= 0) live[B->vs[j]] = 1; + if (B->term == T_CBR || B->term == T_JMP || B->term == T_CALL) live[B->cv] = 1; + for (i = on; i--;) { + Uop *u = &ob[i]; + if (u->op != U_ST && !live[u->d]) { u->op = -1; continue; } + k = nargs(u->op); + if (k > 0) live[u->a] = 1; + if (k > 1) live[u->b] = 1; + if (k > 2) live[u->c] = 1; + } + free(live); + B->u = malloc((on + 1) * sizeof(Uop)); + for (i = 0; i < on; i++) if (ob[i].op >= 0) B->u[n++] = ob[i]; + B->n = n; B->nv = onv; + if (onv > R_->maxnv) R_->maxnv = onv; + return 0; +} +static void addl(int x, int en) +{ + int e; + if (x < R_->lo || x >= R_->hi) return; + if (!lead_[x - R_->lo]) { lead_[x - R_->lo] = 1; wl_[nw_++] = x; } + if (en) { for (e = 0; e < R_->ne && R_->ent[e] != x; e++); if (e == R_->ne && e < RMAXE) R_->ent[R_->ne++] = x; } +} +/* blocks, then each one lifted and optimized */ +static int rlift(Rg *R) +{ + int n = R->hi - R->lo, L, i, w, k, t, b, e, r = 0, cnt; + if (R->lifted) rclear(R); + R_ = R; + lead_ = calloc(n, 1); bidx_ = malloc(n * sizeof(int)); wl_ = malloc(n * sizeof(int)); nw_ = 0; + R->ne = 0; + addl(R->lo, 1); + while (nw_) { + L = wl_[--nw_]; + for (i = L, cnt = 0;;) { + if (i >= R->hi || (i != L && lead_[i - R->lo])) break; + if (cnt++ >= 1000) { addl(i, 0); break; } + w = m[i]; + if (w & 1) { + if (i + 1 < R->hi && !(m[i + 1] & 1) && (k = m[i + 1] / 2) >= 6 && k <= 8) { + t = w >> 1; + if (k == 6) addl(i + 2, 1); /* a call comes back here */ + else if (k == 7) addl(t, 0); + else { addl(t, 0); addl(i + 2, 0); } + break; + } + i++; + continue; + } + k = w / 2; + if (k == 6) { addl(i + 1, 1); break; } + if (k == 8) { addl(i + 1, 0); break; } + if (k == 0 || k == 7 || k == 9) break; + i++; + } + } + for (i = 0, b = 0; i < n; i++) if (lead_[i]) bidx_[i] = b++; + R->nb = b; R->b = calloc(b, sizeof(RB)); R->maxnv = 0; + for (i = 0; i < n; i++) if (lead_[i]) R->b[bidx_[i]].a = R->lo + i; + for (b = 0; b < R->nb && !r; b++) r = lift_block(b); + for (e = 0; e < R->ne; e++) R->entb[e] = bidx_[R->ent[e] - R->lo]; + free(lead_); free(bidx_); free(wl_); + if (r) { + if (trace) fprintf(stderr, "o3: %d..%d doesn't lift (%d)\n", R->lo, R->hi - 1, r); + R->lifted = 1; rclear(R); return r; + } + for (e = 0; e < R->ne; e++) { o3ent[R->ent[e]] = R->idx + 1; o3ei[R->ent[e]] = e; } + R->lifted = 1; + rmark(R); + if (trace) { + int ops = 0; + for (b = 0; b < R->nb; b++) ops += R->b[b].n; + fprintf(stderr, "o3: %d..%d: %d blocks, %d entries, %d vars, %d micro-ops\n", R->lo, R->hi - 1, R->nb, R->ne, R->nv - 1, ops); + if (getenv("ZYO3DUMP")) + for (b = 0; b < R->nb; b++) { + RB *B = &R->b[b]; + fprintf(stderr, " b%d @%d: %d ops, sp %+d, term %d cv v%d s %d/%d x %d/%d\n", b, B->a, B->n, B->delta, B->term, B->cv, B->s1, B->s2, B->x1, B->x2); + for (i = 0; i < B->n; i++) { + Uop *u = &B->u[i]; + if (u->op == U_ST) fprintf(stderr, " st v%d v%d\n", u->a, u->b); + else if (u->op == U_LI) fprintf(stderr, " v%d = %d\n", u->d, u->imm); + else if (u->op == U_VGET) fprintf(stderr, " v%d = var%d\n", u->d, u->imm); + else if (u->op == U_SLD) fprintf(stderr, " v%d = s[%d]\n", u->d, u->imm); + else { fprintf(stderr, " v%d = %s v%d", u->d, unames[u->op], u->a); if (nargs(u->op) > 1) fprintf(stderr, " v%d", u->b); if (nargs(u->op) > 2) fprintf(stderr, " v%d", u->c); fprintf(stderr, "\n"); } + } + for (i = 0; i < B->nso; i++) fprintf(stderr, " s[%d] = v%d\n", B->so[2 * i], B->so[2 * i + 1]); + for (i = 1; i < R->nv; i++) if (B->vs[i] >= 0) fprintf(stderr, " var%d = v%d\n", i, B->vs[i]); + } + } + return 0; +} +/* install kind 2 */ +static int o3install(int d) +{ + int lo = mread(d + 2), hi = mread(d + 3), n = mread(d + 4), i, r; + Rg *R; + if (lo <= 0 || hi <= lo || hi > MW || hi - lo > 1000000 || (unsigned)n > 32 || nrg >= MAXRG) return -1; + R = calloc(1, sizeof *R); + R->idx = nrg; rgs[nrg++] = R; + R->lo = lo; R->hi = hi; R->nv = 1; R->np = n; + for (i = 1; i < RMAXV; i++) R->slot[i] = -1; + for (i = 0; i < n; i++) { /* pairs: frame slot, register 1..7 */ + int s = mread(d + 5 + 2 * i), v = mread(d + 6 + 2 * i); + if ((unsigned)s >= 4096 || v < 1 || v >= RMAXV) { R->dead = 1; return -1; } + R->ps[i] = s; R->pv[i] = v; + if (R->slot[v] < 0) R->slot[v] = s; + if (v >= R->nv) R->nv = v + 1; + } + for (i = 1; i < R->nv; i++) if (R->slot[i] < 0) R->slot[i] = R->ps[0]; + if ((r = rlift(R))) { R->dead = 1; return r; } + rcompile(R, 1); + return 0; +} +/* the region entered at p, lifted again if it has to be, or 0 */ +static Rg *rat(int p) +{ + Rg *R = rgs[o3ent[p] - 1]; + if (R->dead) { o3ent[p] = 0; return 0; } + if (!R->lifted && rlift(R)) { R->dead = 1; o3ent[p] = 0; return 0; } + if (o3ei[p] >= R->ne || R->ent[o3ei[p]] != p) { o3ent[p] = 0; return 0; } + return R; +} + +#ifdef ZYJIT +static unsigned char cov[MW]; /* 1 if a live translation covers the word */ +static void *tab[MW]; /* vm address -> translation */ +static struct { int a, b; } bl[MAXB]; /* live translations cover [a,b); a < 0 is dead */ +static int nbl; +static unsigned *code, *cp, *x_enter, *x_exit, *x_disp, *x_first; +static long S[13]; /* m, s, r, tab, cov, Q, G, &J, store hook, install, enhancement fallback, stack limits */ + +enum { W0 = 0, W1 = 1, X9 = 9, X10, X11, M = 19, SP = 20, RP = 21, TAB = 22, ST = 23 }; + +static void E(unsigned x) { *cp++ = x; } +static void li(int d, int v) +{ + unsigned u = (unsigned)v; + E(0x52800000 | (u & 0xffff) << 5 | d); + if (u >> 16) E(0x72a00000 | (u >> 16) << 5 | d); +} +static void ldr_post(int t, int n, int i) { E(0xb8400400 | (i & 0x1ff) << 12 | n << 5 | t); } +static void ldr_pre(int t, int n, int i) { E(0xb8400c00 | (i & 0x1ff) << 12 | n << 5 | t); } +static void str_pre(int t, int n, int i) { E(0xb8000c00 | (i & 0x1ff) << 12 | n << 5 | t); } +static void str_post(int t, int n, int i) { E(0xb8000400 | (i & 0x1ff) << 12 | n << 5 | t); } +static void ldr_u(int t, int n, int o) { E(0xb9400000 | (o / 4) << 10 | n << 5 | t); } +static void str_u(int t, int n, int o) { E(0xb9000000 | (o / 4) << 10 | n << 5 | t); } +static void ldrx_u(int t, int n, int o) { E(0xf9400000 | (o / 8) << 10 | n << 5 | t); } +static void ldr_rr(int t, int n, int r, int ext) { E(0xb8600800 | r << 16 | ext << 13 | 1 << 12 | n << 5 | t); } +static void str_rr(int t, int n, int r, int ext) { E(0xb8200800 | r << 16 | ext << 13 | 1 << 12 | n << 5 | t); } +static void ldp_post(int t, int u, int n, int i) { E(0x28c00000 | ((i / 4) & 0x7f) << 15 | u << 10 | n << 5 | t); } +static void op3(unsigned op, int d, int n, int r) { E(op | r << 16 | n << 5 | d); } +static void addx_ext(int d, int n, int r, int ext) { E(0x8b200000 | r << 16 | ext << 13 | 2 << 10 | n << 5 | d); } +static void movw(int d, int r) { E(0x2a0003e0 | r << 16 | d); } +static void blr(int n) { E(0xd63f0000 | n << 5); } +static void b_to(unsigned *t) { long o = t - cp; E(0x14000000 | (o & 0x3ffffff)); } +static void cb_fix(unsigned *at, unsigned *to) { *at |= ((to - at) & 0x7ffff) << 5; } +#define SXTW 6 +#define UXTW 2 + +static void build_stubs(void) +{ + cp = code; + x_enter = cp; /* int enter(long *S, void *code) */ + E(0xa9b97bfd); /* stp x29, x30, [sp, #-112]! */ + E(0x910003fd); /* mov x29, sp */ + E(0xa90153f3); /* stp x19, x20, [sp, #16] */ + E(0xa9025bf5); /* stp x21, x22, [sp, #32] */ + E(0xf9001bf7); /* str x23, [sp, #48] */ + E(0xa90467f8); /* stp x24, x25, [sp, #64] */ + E(0xa9056ffa); /* stp x26, x27, [sp, #80] */ + E(0xf90033fc); /* str x28, [sp, #96] */ + E(0xaa0003f7); /* mov x23, x0 */ + ldrx_u(M, ST, 0); + ldrx_u(24, ST, 88); ldrx_u(25, ST, 96); /* x24, x25: the stack limits, as addresses */ + ldr_u(X9, ST, 8); addx_ext(SP, M, X9, UXTW); + ldr_u(X9, ST, 16); addx_ext(RP, M, X9, UXTW); + ldrx_u(TAB, ST, 24); + E(0xd61f0020); /* br x1 */ + x_exit = cp; /* w0 = next vm address */ + op3(0xcb000000, X9, SP, M); E(0xd342fd29); str_u(X9, ST, 8); /* s = (x20 - x19) >> 2 */ + op3(0xcb000000, X9, RP, M); E(0xd342fd29); str_u(X9, ST, 16); + E(0xa94153f3); /* ldp x19, x20, [sp, #16] */ + E(0xa9425bf5); /* ldp x21, x22, [sp, #32] */ + E(0xf9401bf7); /* ldr x23, [sp, #48] */ + E(0xa94467f8); /* ldp x24, x25, [sp, #64] */ + E(0xa9456ffa); /* ldp x26, x27, [sp, #80] */ + E(0xf94033fc); /* ldr x28, [sp, #96] */ + E(0xa8c77bfd); /* ldp x29, x30, [sp], #112 */ + E(0xd65f03c0); /* ret */ + { + unsigned *ovd = cp, *ovr; + li(W0, MW + 1); b_to(x_exit); /* data stack overflow */ + ovr = cp; + li(W0, MW + 2); b_to(x_exit); /* return stack overflow */ + x_disp = cp; /* w0 = target: check the stacks, chain through tab[] or fall back to c */ + E(0xeb18029f); /* cmp x20, x24 */ + E(0x54000003 | ((ovd - cp) & 0x7ffff) << 5); /* b.lo data overflow */ + E(0xeb1902bf); /* cmp x21, x25 */ + E(0x54000002 | ((ovr - cp) & 0x7ffff) << 5); /* b.hs return overflow */ + } + E(0x7144001f); /* cmp w0, #MW */ + E(0x54000002 | ((x_exit - cp) & 0x7ffff) << 5); /* b.hs exit */ + E(0xf8605ac9); /* ldr x9, [x22, w0, uxtw #3] */ + E(0xb4000009 | ((x_exit - cp) & 0x7ffff) << 5); /* cbz x9, exit */ + E(0xd61f0120); /* br x9 */ + x_first = cp; +} + +static void flush(void) +{ + memset(tab, 0, sizeof tab); + memset(cov, 0, sizeof cov); + nbl = 0; + cp = x_first; + rdrop_all(); + if (trace) fprintf(stderr, "jit: flushed\n"); +} + +static void jitwrite(int on) +{ +#ifdef __APPLE__ + pthread_jit_write_protect_np(!on); +#else + (void)on; +#endif +} + +static void syncicache(unsigned *a, unsigned *b) +{ +#ifdef __APPLE__ + sys_icache_invalidate(a, (char *)b - (char *)a); +#else + __builtin___clear_cache((char *)a, (char *)b); +#endif +} + +/* store hook for words that are covered by a translation */ +static int hook(int a, int v) +{ + int j, x, hit = 0; + if (m[a] == v) return 0; + m[a] = v; + for (j = 0; j < nbl; j++) + if (bl[j].a >= 0 && bl[j].a <= a && a < bl[j].b) { + tab[bl[j].a] = 0; + for (x = bl[j].a; x < bl[j].b; x++) cov[x] = 0; + if (trace) fprintf(stderr, "jit: dropped %d..%d\n", bl[j].a, bl[j].b - 1); + bl[j].a = -1; + hit = 1; + } + for (j = 0; j < nrg; j++) + if (rgs[j]->lifted && rgs[j]->lo <= a && a < rgs[j]->hi) { + if (trace) fprintf(stderr, "o3: dropped %d..%d (a store to %d)\n", rgs[j]->lo, rgs[j]->hi - 1, a); + rclear(rgs[j]); + memset(cov, 0, sizeof cov); + hit = 1; + } + for (j = 0; j < nbl; j++) + if (bl[j].a >= 0) for (x = bl[j].a; x < bl[j].b; x++) cov[x] = 1; + for (j = 0; j < nrg; j++) if (rgs[j]->lifted) rmark(rgs[j]); + return hit; +} + +static void op(int i, int k) /* everything except control flow */ +{ + unsigned *a, *b, *c, *z; + switch (k) { + case 1: ldr_u(X9, SP, 0); ldr_rr(X9, M, X9, SXTW); str_u(X9, SP, 0); return; + case 2: /* store: inline unless the target word is translated */ + ldp_post(X9, X10, SP, 8); + E(0x7144013f); /* cmp w9, #MW */ + a = cp; E(0x54000002); /* b.hs done */ + ldrx_u(X11, ST, 32); + E(0x3869496b); /* ldrb w11, [x11, w9, uxtw] */ + b = cp; E(0x3500000b); /* cbnz w11, slow */ + str_rr(X10, M, X9, UXTW); + c = cp; E(0x14000000); /* b done */ + cb_fix(b, cp); /* slow: hook(a, v) */ + movw(W0, X9); movw(W1, X10); ldrx_u(X11, ST, 64); blr(X11); + z = cp; E(0x34000000); /* cbz w0, done */ + li(W0, i + 1); b_to(x_exit); /* a translation died: resume at i+1 in c */ + cb_fix(a, cp); cb_fix(z, cp); + *c |= (cp - c) & 0x3ffffff; + return; + case 3: ldr_u(X9, SP, 0); E(0x9100128a); ldr_rr(X9, X10, X9, SXTW); str_u(X9, SP, 0); return; /* add x10, x20, #4 */ + case 4: E(0x91001294); return; /* add x20, x20, #4 */ + case 5: E(0x29402a89); E(0x2900268a); return; /* ldp w9, w10, [x20]; stp w10, w9, [x20] */ + case 10: ldrx_u(X11, ST, 40); blr(X11); E(0x13001c09); str_pre(X9, SP, -4); return; /* sxtb w9, w0 */ + case 11: + ldrx_u(X9, ST, 56); + if (sizeof J == 1) E(0x39400129); else ldr_u(X9, X9, 0); /* ldrb w9, [x9] */ + str_pre(X9, SP, -4); + return; + case 12: ldr_post(W0, SP, 4); E(0x13001c00); ldrx_u(X11, ST, 48); blr(X11); return; /* sxtb w0, w0 */ + } + ldr_post(X9, SP, 4); + ldr_u(X10, SP, 0); + switch (k) { + case 13: op3(0x0b000000, X10, X10, X9); break; + case 14: op3(0x4b000000, X10, X10, X9); break; + case 15: op3(0x1b007c00, X10, X10, X9); break; + case 16: op3(0x0a000000, X10, X10, X9); break; + case 17: op3(0x2a000000, X10, X10, X9); break; + case 18: op3(0x4a000000, X10, X10, X9); break; + case 19: op3(0x1ac02800, X10, X10, X9); break; + default: E(0x6b09015f); E(0x1a9fa7ea); /* cmp w10, w9; cset w10, lt */ + } + str_u(X10, SP, 0); +} + +/* one micro-op, operands in registers. tmp is a scratch register for the + remainders; x16 and x17 are scratch too. */ +static void emit_uop(int op, int d, int a, int b, int c, int tmp) +{ + switch (op) { + case U_ADD: op3(0x0b000000, d, a, b); break; + case U_SUB: op3(0x4b000000, d, a, b); break; + case U_MUL: op3(0x1b007c00, d, a, b); break; + case U_SDIV: op3(0x1ac00c00, d, a, b); break; + case U_UDIV: op3(0x1ac00800, d, a, b); break; + case U_SREM: op3(0x1ac00c00, tmp, a, b); E(0x1b008000 | b << 16 | a << 10 | tmp << 5 | d); break; /* msub */ + case U_UREM: op3(0x1ac00800, tmp, a, b); E(0x1b008000 | b << 16 | a << 10 | tmp << 5 | d); break; + case U_AND: op3(0x0a000000, d, a, b); break; + case U_OR: op3(0x2a000000, d, a, b); break; + case U_XOR: op3(0x4a000000, d, a, b); break; + case U_SHL: op3(0x1ac02000, d, a, b); break; + case U_SHR: op3(0x1ac02400, d, a, b); break; + case U_SAR: op3(0x1ac02800, d, a, b); break; + case U_NEG: E(0x4b0003e0 | a << 16 | d); break; + case U_NOT: E(0x2a2003e0 | a << 16 | d); break; + case U_LT: E(0x6b00001f | b << 16 | a << 5); E(0x1a9fa7e0 | d); break; /* cmp; cset lt */ + case U_LTU: E(0x6b00001f | b << 16 | a << 5); E(0x1a9f27e0 | d); break; /* cset lo */ + case U_EQ: E(0x6b00001f | b << 16 | a << 5); E(0x1a9f17e0 | d); break; + case U_NE: E(0x6b00001f | b << 16 | a << 5); E(0x1a9f07e0 | d); break; + case U_MIN: E(0x6b00001f | b << 16 | a << 5); E(0x1a800000 | b << 16 | 11 << 12 | a << 5 | d); break; + case U_MAX: E(0x6b00001f | b << 16 | a << 5); E(0x1a800000 | b << 16 | 12 << 12 | a << 5 | d); break; + case U_MINU: E(0x6b00001f | b << 16 | a << 5); E(0x1a800000 | b << 16 | 3 << 12 | a << 5 | d); break; + case U_MAXU: E(0x6b00001f | b << 16 | a << 5); E(0x1a800000 | b << 16 | 8 << 12 | a << 5 | d); break; + case U_MULHS: E(0x9b207c00 | b << 16 | a << 5 | 16); E(0xd360fc00 | 16 << 5 | d); break; /* smull; lsr #32 */ + case U_MULHU: E(0x9ba07c00 | b << 16 | a << 5 | 16); E(0xd360fc00 | 16 << 5 | d); break; + case U_CLZ: E(0x5ac01000 | a << 5 | d); break; + case U_ABS: E(0x7100001f | a << 5); E(0x5a800400 | a << 16 | 10 << 12 | a << 5 | d); break; /* cmp #0; cneg */ + case U_SEL: E(0x7100001f | a << 5); E(0x1a800000 | c << 16 | 1 << 12 | b << 5 | d); break; /* csel ne */ + case U_LD: /* out of range reads 0, like the interpreter */ + E(0x52800011); E(0x7144001f | a << 5); E(0x54000042); ldr_rr(17, M, a, UXTW); movw(d, 17); + break; + case U_ST: E(0x7144001f | a << 5); E(0x54000042); str_rr(b, M, a, UXTW); break; + } +} +/* an enhancement, inlined: inputs from the data stack into w0..w15, the + optimized micro-ops, outputs back. constants are made where they're used, + or folded into the instruction (a fixed address, a small addend). */ +static int ekc[MAXUOP * 4 + 16], ekv[MAXUOP * 4 + 16]; +static void emit_enh_e(Enh *e, int s) +{ + signed char *r = e->reg; + int i, j, delta, na; + if (!r) { /* didn't fit in registers: x0 = data stack, w1 = slot, call the interpreter */ + E(0xaa1403e0); li(W1, s); ldrx_u(X11, ST, 80); blr(X11); E(0xaa0003f4); + return; + } + for (i = 0; i < e->nv; i++) ekc[i] = 0; + for (i = 0; i < e->n; i++) if (e->u[i].op == U_LI) { ekc[e->u[i].d] = 1; ekv[e->u[i].d] = e->u[i].imm; } +#define EOP(x) (ekc[x] ? (li(r[x], ekv[x]), r[x]) : r[x]) + for (j = 0; j < e->nin; j++) if (r[j] >= 0) ldr_u(r[j], SP, 4 * (e->nin - 1 - j)); + for (i = 0; i < e->n; i++) { + Uop *u = &e->u[i]; + int d = u->op == U_ST ? 0 : r[u->d], a = 0, b = 0, c = 0; + if (u->op == U_LI) continue; + if (u->op == U_LD && ekc[u->a] && (unsigned)ekv[u->a] < MW) { + if (ekv[u->a] < 4096) ldr_u(d, M, ekv[u->a] * 4); else ldr_rr(d, M, EOP(u->a), UXTW); + continue; + } + if ((u->op == U_ADD || u->op == U_SUB) && ekc[u->b] && ekv[u->b] >= 0 && ekv[u->b] < 4096) { + E((u->op == U_ADD ? 0x11000000 : 0x51000000) | ekv[u->b] << 10 | EOP(u->a) << 5 | d); + continue; + } + na = nargs(u->op); + if (na > 0) a = EOP(u->a); + if (na > 1) b = EOP(u->b); + if (na > 2) c = EOP(u->c); + emit_uop(u->op, d, a, b, c, 16); + } + delta = 4 * (e->nin - e->nout); + if (delta > 0) E(0x91000294 | delta << 10); + if (delta < 0) E(0xd1000294 | (-delta) << 10); + for (j = 0; j < e->nout; j++) str_u(EOP(e->out[j]), SP, 4 * (e->nout - 1 - j)); +#undef EOP +} +static void emit_enh(int s) { emit_enh_e(&enh[s], s); } +/* lit .. lit xop: the enhancement specialized on the literals that feed its + last inputs, so the optimizer sees them as constants (x / 7 becomes a + multiply, lfa(k) a load and an add). 0 if it worked. */ +static int spec_enh(int s, int *ks, int nk, Enh *te) +{ + Enh *e = &enh[s]; + int nin = e->nin - nk, i, j, out[8], na; + static int mp[MAXUOP * 4 + 16]; + if (e->nv > MAXUOP * 4) return -1; + un = 0; unv = nin; uerr = 0; + for (j = 0; j < nin; j++) mp[j] = j; + for (j = 0; j < nk; j++) mp[nin + j] = unew(U_LI, 0, 0, 0, ks[j]); + for (i = 0; i < e->n; i++) { + Uop *u = &e->u[i]; + na = nargs(u->op); + j = unew(u->op, na > 0 ? mp[u->a] : 0, na > 1 ? mp[u->b] : 0, na > 2 ? mp[u->c] : 0, u->imm); + if (u->op != U_ST) mp[u->d] = j; + } + for (j = 0; j < e->nout; j++) out[j] = mp[e->out[j]]; + memset(te, 0, sizeof *te); + if (uerr || optimize(te, nin, out, e->nout)) return -1; + regalloc(te); + if (!te->reg) { free(te->u); return -1; } + return 0; +} + +/* ---- o3 regions on arm64 ---- + vars live in w0..w7 (w0 is the frame pointer), block values in w8..w14, + w26..w28 and the homes of unused vars. constants have no register: they + are made in a scratch register where they're used (w16, w17, w15), or + folded into the instruction. x15..x17 are scratch. */ +static signed char rreg[MAXUOP * 4 + 16]; +static unsigned char rk[MAXUOP * 4 + 16], rhome[MAXUOP * 4 + 16]; +static int rkv[MAXUOP * 4 + 16], rlast[MAXUOP * 4 + 16]; +static unsigned **rfa; static int *rfb, rnf; /* branches to blocks, fixed up at the end */ + +static void rforget(Rg *R) +{ + int e; + if (R->nat) for (e = 0; e < R->ne; e++) if (tab[R->ent[e]] == R->stub[e]) tab[R->ent[e]] = 0; + R->nat = 0; +} +static void rmark(Rg *R) { int x; for (x = R->lo; x < R->hi; x++) cov[x] = 1; } +static int ralloc(Rg *R, RB *B) +{ + int i, j, k, n = B->n; + unsigned fr = 0x7f00u | 0x1c000000u; /* w8..w14, w26..w28 */ + for (j = R->nv; j < 8; j++) fr |= 1u << j; + for (i = 0; i < B->nv; i++) { rlast[i] = -2; rk[i] = 0; rhome[i] = 0; rreg[i] = -1; } + for (i = 0; i < n; i++) { + Uop *u = &B->u[i]; int na = nargs(u->op); + if (na > 0) rlast[u->a] = i; + if (na > 1) rlast[u->b] = i; + if (na > 2) rlast[u->c] = i; + } + for (j = 0; j < B->nso; j++) rlast[B->so[2 * j + 1]] = n; + for (j = 1; j < R->nv; j++) if (B->vs[j] >= 0) rlast[B->vs[j]] = n; + if (B->term == T_CBR || B->term == T_JMP || B->term == T_CALL) rlast[B->cv] = n; + for (i = 0; i < n; i++) { + Uop *u = &B->u[i]; int na = nargs(u->op), ops[3], d = u->d; + ops[0] = u->a; ops[1] = u->b; ops[2] = u->c; + for (j = 0; j < na; j++) { int x = ops[j]; if (rlast[x] == i && rreg[x] >= 0 && !rhome[x]) fr |= 1u << rreg[x]; } + if (u->op == U_ST) continue; + if (u->op == U_LI) { rk[d] = 1; rkv[d] = u->imm; continue; } + if (u->op == U_VGET) { rreg[d] = (signed char)u->imm; rhome[d] = 1; continue; } + for (k = 0; k < 32 && !(fr >> k & 1); k++); + if (k == 32) return -1; + fr &= ~(1u << k); rreg[d] = (signed char)k; + if (rlast[d] == -2) fr |= 1u << k; + } + return 0; +} +static int ropr(int x, int s) { if (rk[x]) { li(s, rkv[x]); return s; } return rreg[x]; } +/* w t <-> the data stack word o places from the top at block entry */ +static void rsw(int t, int o, int store) +{ + int by = o * 4; + if (by >= 0 && by < 16384) { if (store) str_u(t, SP, by); else ldr_u(t, SP, by); } + else if (by >= -256 && by < 256) E((store ? 0xb8000000u : 0xb8400000u) | (unsigned)(by & 0x1ff) << 12 | SP << 5 | t); /* stur, ldur */ + else { li(16, by); addx_ext(16, SP, 16, SXTW); if (store) str_u(t, 16, 0); else ldr_u(t, 16, 0); } +} +static void addsp(int by) +{ + if (by > 0 && by < 4096) E(0x91000294 | by << 10); + else if (by < 0 && -by < 4096) E(0xd1000294 | (-by) << 10); + else if (by) { li(16, by); addx_ext(SP, SP, 16, SXTW); } +} +static void rspill(Rg *R) +{ + int v; + for (v = 0; v < R->np; v++) { E(0x11000000 | R->ps[v] << 10 | 16); str_rr(R->pv[v], M, 16, UXTW); } /* add w16, w0, #slot */ +} +static void rload(Rg *R) +{ + int v; + li(16, ZFP); ldr_rr(0, M, 16, UXTW); + for (v = 1; v < R->nv; v++) { E(0x11000000 | R->slot[v] << 10 | 16); ldr_rr(v, M, 16, UXTW); } +} +static void rexitk(Rg *R, int t) { rspill(R); li(W0, t); b_to(x_disp); } +static void rbranch(int b) { rfa[rnf] = cp; rfb[rnf++] = b; E(0x14000000); } +/* m[a] = v, checked like the jit's own stores: a word some translation covers goes through hook */ +static void rstore(int a, int v) +{ + unsigned *p1, *p2, *p3; + int j; + E(0x7144001f | a << 5); /* cmp wa, #MW */ + p1 = cp; E(0x54000002); /* b.hs done */ + ldrx_u(15, ST, 32); + E(0x38604800 | a << 16 | 15 << 5 | 15); /* ldrb w15, [x15, wa, uxtw] */ + p2 = cp; E(0x35000000 | 15); /* cbnz w15, slow */ + str_rr(v, M, a, UXTW); + p3 = cp; E(0x14000000); + cb_fix(p2, cp); + E(0xd10203ff); /* sub sp, sp, #128 */ + for (j = 0; j < 16; j += 2) E(0xa9000000 | (j / 2 * 2) << 15 | (j + 1) << 10 | 31 << 5 | j); /* stp xj, xj+1, [sp, #8j] */ + movw(15, a); movw(1, v); movw(0, 15); + ldrx_u(16, ST, 64); blr(16); + for (j = 0; j < 16; j += 2) E(0xa9400000 | (j / 2 * 2) << 15 | (j + 1) << 10 | 31 << 5 | j); + E(0x910203ff); /* add sp, sp, #128 */ + cb_fix(p1, cp); + *p3 |= (cp - p3) & 0x3ffffff; +} +static void rpmove(Rg *R, RB *B) +{ + int dst[RMAXV], src[RMAXV], n = 0, j, k, x; + for (j = 1; j < R->nv; j++) + if (B->vs[j] >= 0 && !rk[B->vs[j]] && rreg[B->vs[j]] != j) { dst[n] = j; src[n] = rreg[B->vs[j]]; n++; } + while (n) { + for (j = 0; j < n; j++) { for (k = 0; k < n && src[k] != dst[j]; k++); if (k == n) break; } + if (j < n) { movw(dst[j], src[j]); n--; dst[j] = dst[n]; src[j] = src[n]; continue; } + x = src[0]; movw(16, x); /* a cycle: park one */ + for (k = 0; k < n; k++) if (src[k] == x) src[k] = 16; + } + for (j = 1; j < R->nv; j++) if (B->vs[j] >= 0 && rk[B->vs[j]]) li(j, rkv[B->vs[j]]); +} +static void emit_uop(int op, int d, int a, int b, int c, int tmp); +static int remit(Rg *R, int bi) +{ + RB *B = &R->b[bi]; + int i, j, d, a, b, c; + unsigned *p; + if (ralloc(R, B)) return -1; + for (i = 0; i < B->n; i++) { + Uop *u = &B->u[i]; + switch (u->op) { + case U_LI: case U_VGET: continue; + case U_SLD: rsw(rreg[u->d], u->imm, 0); continue; + case U_LD: + if (rk[u->a] && (unsigned)rkv[u->a] < MW) { /* a fixed address */ + if (rkv[u->a] < 4096) ldr_u(rreg[u->d], M, rkv[u->a] * 4); + else { li(16, rkv[u->a]); ldr_rr(rreg[u->d], M, 16, UXTW); } + continue; + } + break; + case U_ST: a = ropr(u->a, 16); rstore(a, ropr(u->b, 17)); continue; + case U_ADD: case U_SUB: + if (rk[u->b] && rkv[u->b] >= 0 && rkv[u->b] < 4096) { + E((u->op == U_ADD ? 0x11000000 : 0x51000000) | rkv[u->b] << 10 | ropr(u->a, 16) << 5 | rreg[u->d]); + continue; + } + break; + } + d = rreg[u->d]; + a = nargs(u->op) > 0 ? ropr(u->a, 16) : 0; + b = nargs(u->op) > 1 ? ropr(u->b, 17) : 0; + c = nargs(u->op) > 2 ? ropr(u->c, 15) : 0; + emit_uop(u->op, d, a, b, c, 15); + } + for (j = 0; j < B->nso; j++) rsw(ropr(B->so[2 * j + 1], 17), B->so[2 * j], 1); + if (B->term == T_CBR || B->term == T_JMP || B->term == T_CALL) { + if (rk[B->cv]) li(17, rkv[B->cv]); else movw(17, rreg[B->cv]); + } + addsp(B->delta * 4); + rpmove(R, B); + switch (B->term) { + case T_BR: rbranch(B->s1); break; + case T_CBR: + if (B->s1 >= 0) { rfa[rnf] = cp; rfb[rnf++] = B->s1; E(0x34000000 | 17); } /* cbz w17, taken */ + else { p = cp; E(0x35000000 | 17); rexitk(R, B->x1); cb_fix(p, cp); } + if (B->s2 >= 0) rbranch(B->s2); else rexitk(R, B->x2); + break; + case T_CALL: rspill(R); li(16, B->x2); str_post(16, RP, 4); movw(W0, 17); b_to(x_disp); break; + case T_RET: rspill(R); ldr_pre(W0, RP, -4); b_to(x_disp); break; + default: rspill(R); movw(W0, 17); b_to(x_disp); + } + return 0; +} +/* the whole region: entry stubs that load the vars, then the blocks */ +static int rjit(Rg *R, int wr) +{ + unsigned *start = cp, **lab; + int b, e, j, bad = 0; + long need = (long)(R->hi - R->lo) * 96 + (long)R->ne * 32 + 4096; + if (need > (code + CODEW) - cp) return -1; + if (wr) jitwrite(1); + lab = malloc((R->nb + 1) * sizeof *lab); + rfa = malloc((2 * R->nb + R->ne + 4) * sizeof *rfa); rfb = malloc((2 * R->nb + R->ne + 4) * sizeof *rfb); rnf = 0; + for (e = 0; e < R->ne; e++) { R->stub[e] = cp; rload(R); rbranch(R->entb[e]); } + for (b = 0; b < R->nb && !bad; b++) { lab[b] = cp; bad = remit(R, b); } + if (!bad) { + for (j = 0; j < rnf; j++) { + unsigned *at = rfa[j], *to = lab[rfb[j]]; + if ((*at & 0xfc000000u) == 0x14000000u) *at |= (to - at) & 0x3ffffff; else cb_fix(at, to); + } + R->nat = start; + for (e = 0; e < R->ne; e++) tab[R->ent[e]] = R->stub[e]; + syncicache(start, cp); + if (trace) fprintf(stderr, "o3: %d..%d -> %ld bytes of arm64\n", R->lo, R->hi - 1, (long)((char *)cp - (char *)start)); + } else { + cp = start; + if (trace) fprintf(stderr, "o3: %d..%d: a block needs too many registers\n", R->lo, R->hi - 1); + } + free(lab); free(rfa); free(rfb); + if (wr) jitwrite(0); + return bad; +} +static void rcompile(Rg *R, int eager) +{ + if (R->nat || R->nonat) return; + if (rjit(R, eager) && cp - code < CODEW / 2) R->nonat = 1; /* registers, not space: give up on it */ +} + +static int inst_hook(int d) { return install(d); } +static long xrun(long sp, int slot) { return (long)xexec(&enh[slot], (int *)sp); } + +static void *translate(int p) +{ + unsigned *start; + int i = p, n = 0, w, k, t; + if (cp - code > CODEW - 65536 || nbl >= MAXB) flush(); + if (o3ent[p]) { /* an o3 region starts here */ + Rg *R = rat(p); + if (R) { rcompile(R, 0); if (R->nat && tab[p]) return tab[p]; } + } + start = cp; + for (;;) { + if (i >= MW) { E(0x12800000); b_to(x_exit); break; } /* movn w0, #0: halt */ + if (n++ >= BLKW) { li(W0, i); b_to(x_disp); break; } + w = m[i]; + cov[i] = 1; + if (w & 1 && i + 1 < MW && !(m[i + 1] & 1) && (k = m[i + 1] / 2) >= 6 && k <= 8) { + t = (w - 1) / 2; /* literal + call/jmp/jz with a known target */ + cov[i + 1] = 1; + if (k == 8) { + unsigned *a; + ldr_post(X10, SP, 4); + a = cp; E(0x3500000a); /* cbnz w10, fallthrough */ + li(W0, t); b_to(x_disp); + cb_fix(a, cp); + i += 2; + continue; + } + li(X10, i + 2); + if (k == 6) str_post(X10, RP, 4); else str_u(X10, RP, 0); + li(W0, t); b_to(x_disp); + i += 2; + break; + } + if (w & 1) { + int L = 0, x, s, nk, ks[8], j; + Enh te; + while (L < 8 && i + L < MW && (m[i + L] & 1)) L++; + x = i + L < MW ? m[i + L] : 1; + if (!(x & 1) && (s = x / 2 - XOP0) >= 0 && s < NENH && enh[s].live && enh[s].nin) { + nk = L < enh[s].nin ? L : enh[s].nin; + for (j = 0; j < nk; j++) ks[j] = m[i + L - nk + j] >> 1; + if (!spec_enh(s, ks, nk, &te)) { + for (j = 0; j < L - nk; j++) { li(X9, m[i + j] >> 1); str_pre(X9, SP, -4); } + for (j = 0; j <= L; j++) cov[i + j] = 1; + emit_enh_e(&te, s); + free(te.u); free(te.reg); + i += L + 1; n += L; + continue; + } + } + li(X9, (w - 1) / 2); str_pre(X9, SP, -4); i++; continue; + } + k = w / 2; + if (k == 0) { E(0x12800000); b_to(x_exit); i++; break; } + if (k < 0 || k == 6 || k == 7) { + ldr_post(W0, SP, 4); + li(X10, i + 1); + if (k == 6) str_post(X10, RP, 4); else str_u(X10, RP, 0); + if (k < 0) { li(X11, 7 - k); addx_ext(RP, RP, X11, SXTW); } + b_to(x_disp); + i++; + break; + } + if (k == 8) { /* jz: taken leaves, not taken keeps going */ + ldp_post(X9, X10, SP, 8); + E(0x3500006a); /* cbnz w10, +3 */ + movw(W0, X9); + b_to(x_disp); + i++; + continue; + } + if (k == 9) { ldr_pre(W0, RP, -4); b_to(x_disp); i++; break; } + if (k == 21) { /* install: call into c, then return to it so it can flush */ + ldr_u(W0, SP, 0); ldrx_u(X11, ST, 72); blr(X11); str_u(W0, SP, 0); + li(W0, i + 1); b_to(x_exit); i++; break; + } + if (k >= XOP0 && k < XOP0 + NENH && enh[k - XOP0].live) { emit_enh(k - XOP0); i++; continue; } + if (k > 20) { E(0x12800000); b_to(x_exit); i++; break; } /* not wired: halt */ + op(i, k); + i++; + } + bl[nbl].a = p; + bl[nbl++].b = i; + tab[p] = start; + syncicache(start, cp); + if (trace) fprintf(stderr, "jit: %d..%d -> %ld bytes\n", p, i - 1, (long)((char *)cp - (char *)start)); + return start; +} + +#else +/* portable path for hosts without the arm64 jit: a plain interpreter with + the same semantics (shift counts wrap at 32, stores outside memory are + ignored, loads outside memory read 0, leaving memory halts). */ +#ifdef ZYLLVM +/* ---- llvm backend ---- + a tiered jit for hosts the arm64 backend doesn't cover. the interpreter + counts how often each block start is reached; a hot block is translated to + llvm ir (same block rules as the arm64 jit: it ends at call, jump, ret or + halt, and keeps going past a jz), optimized at O2 and compiled with orc. + enhancements are inlined as ir. stores into compiled words drop the blocks + that cover them, so self-modifying code still works. */ +#include +#include +#include +#include +#include +#include +static int HOT = 5000; /* runs before a block gets compiled: ZYHOT */ +static const char *lpasses = "default"; /* ZYPASSES */ +#define LMAXB 65536 +typedef int (*Blk)(int *mem, int *regs); +static Blk lt[MW]; /* address -> compiled block */ +static unsigned short hot[MW]; +static unsigned char lcov[MW]; /* 1 if a compiled block covers the word */ +static struct { int a, b; } lbl[LMAXB]; +static int nlbl, llvm_ok, nblocks; +static LLVMOrcLLJITRef jit; +static LLVMOrcThreadSafeContextRef tsc; +static LLVMContextRef ctx; +static LLVMTypeRef i8t, i32t, i64t, ptrt, i1t; +static LLVMBuilderRef bd; +static LLVMValueRef fn_, vm_, vregs, vs, vr; /* function, memory, regs, s and r slots */ +static LLVMModuleRef mod_; + +static unsigned char rcov[MW]; /* 1 on the words of a lifted o3 region */ +static int rdrop(int a); +static void ldrop_all(void) +{ + rdrop_all(); memset(rcov, 0, sizeof rcov); + memset(lt, 0, sizeof lt); memset(lcov, 0, sizeof lcov); memset(hot, 0, sizeof hot); nlbl = 0; + if (trace) fprintf(stderr, "llvm: dropped every block\n"); +} +/* a store to a word some block covers: write it, drop those blocks */ +static int lhook(int a, int v) +{ + int j, x, hit = 0; + if (m[a] == v) return 0; + m[a] = v; + for (j = 0; j < nlbl; j++) + if (lbl[j].a >= 0 && lbl[j].a <= a && a < lbl[j].b) { + lt[lbl[j].a] = 0; hot[lbl[j].a] = 0; + if (trace) fprintf(stderr, "llvm: dropped %d..%d\n", lbl[j].a, lbl[j].b - 1); + lbl[j].a = -1; hit = 1; + } + if (rcov[a] && rdrop(a)) hit = 1; + if (hit) { + memset(lcov, 0, sizeof lcov); + for (j = 0; j < nlbl; j++) if (lbl[j].a >= 0) for (x = lbl[j].a; x < lbl[j].b; x++) lcov[x] = 1; + for (j = 0; j < nrg; j++) if (rgs[j]->lifted) for (x = rgs[j]->lo; x < rgs[j]->hi; x++) lcov[x] = 1; + } + return hit; +} +static int linstall(int d) { return install(d); } + +static LLVMValueRef K32(int v) { return LLVMConstInt(i32t, (unsigned)v, 0); } +static LLVMValueRef cptr(void *p) { return LLVMConstIntToPtr(LLVMConstInt(i64t, (unsigned long long)(size_t)p, 0), ptrt); } +static LLVMValueRef mslot(LLVMValueRef idx) { return LLVMBuildGEP2(bd, i32t, vm_, &idx, 1, ""); } +static LLVMValueRef ld(LLVMValueRef idx) { return LLVMBuildLoad2(bd, i32t, mslot(idx), ""); } +static void st(LLVMValueRef idx, LLVMValueRef v) { LLVMBuildStore(bd, v, mslot(idx)); } +static LLVMValueRef gets(void) { return LLVMBuildLoad2(bd, i32t, vs, ""); } +static void sets(LLVMValueRef v) { LLVMBuildStore(bd, v, vs); } +static LLVMValueRef add(LLVMValueRef a, int k) { return LLVMBuildAdd(bd, a, K32(k), ""); } +/* a load that reads 0 outside memory */ +static LLVMValueRef safeld(LLVMValueRef a) +{ + LLVMValueRef in = LLVMBuildICmp(bd, LLVMIntULT, a, K32(MW), ""); + LLVMValueRef v = ld(LLVMBuildSelect(bd, in, a, K32(0), "")); + return LLVMBuildSelect(bd, in, v, K32(0), ""); +} +static void push(LLVMValueRef v) { LLVMValueRef s = add(gets(), -1); sets(s); st(s, v); } +/* leave the block: write s and r back, return the next vm address */ +static void leave(LLVMValueRef next) +{ + LLVMValueRef r0 = K32(0), r1 = K32(1); + LLVMBuildStore(bd, gets(), LLVMBuildGEP2(bd, i32t, vregs, &r0, 1, "")); + LLVMBuildStore(bd, LLVMBuildLoad2(bd, i32t, vr, ""), LLVMBuildGEP2(bd, i32t, vregs, &r1, 1, "")); + LLVMBuildRet(bd, next); +} +static LLVMBasicBlockRef newbb(void) { return LLVMAppendBasicBlockInContext(ctx, fn_, ""); } +static LLVMValueRef callp(void *f, LLVMTypeRef ret, LLVMTypeRef *at, int n, LLVMValueRef *args) +{ + LLVMTypeRef ft = LLVMFunctionType(ret, at, (unsigned)n, 0); + return LLVMBuildCall2(bd, ft, cptr(f), args, (unsigned)n, ""); +} +/* one micro-op as ir: the same semantics as ev() */ +static LLVMValueRef ir_op(int op, LLVMValueRef a, LLVMValueRef b, LLVMValueRef c) +{ + LLVMValueRef z = K32(0), q, d, t, c1, c2, r = 0; + LLVMTypeRef i64 = i64t; + switch (op) { + case U_ADD: r = LLVMBuildAdd(bd, a, b, ""); break; + case U_SUB: r = LLVMBuildSub(bd, a, b, ""); break; + case U_MUL: r = LLVMBuildMul(bd, a, b, ""); break; + case U_SDIV: case U_SREM: /* /0 and INT_MIN/-1 are defined, like the cpu */ + c1 = LLVMBuildICmp(bd, LLVMIntEQ, b, z, ""); + c2 = LLVMBuildAnd(bd, LLVMBuildICmp(bd, LLVMIntEQ, a, K32((int)0x80000000), ""), LLVMBuildICmp(bd, LLVMIntEQ, b, K32(-1), ""), ""); + d = LLVMBuildSelect(bd, LLVMBuildOr(bd, c1, c2, ""), K32(1), b, ""); + if (op == U_SDIV) { q = LLVMBuildSDiv(bd, a, d, ""); r = LLVMBuildSelect(bd, c1, z, q, ""); } + else { q = LLVMBuildSRem(bd, a, d, ""); r = LLVMBuildSelect(bd, c1, a, LLVMBuildSelect(bd, c2, z, q, ""), ""); } + break; + case U_UDIV: case U_UREM: + c1 = LLVMBuildICmp(bd, LLVMIntEQ, b, z, ""); + d = LLVMBuildSelect(bd, c1, K32(1), b, ""); + if (op == U_UDIV) r = LLVMBuildSelect(bd, c1, z, LLVMBuildUDiv(bd, a, d, ""), ""); + else r = LLVMBuildSelect(bd, c1, a, LLVMBuildURem(bd, a, d, ""), ""); + break; + case U_AND: r = LLVMBuildAnd(bd, a, b, ""); break; + case U_OR: r = LLVMBuildOr(bd, a, b, ""); break; + case U_XOR: r = LLVMBuildXor(bd, a, b, ""); break; + case U_SHL: r = LLVMBuildShl(bd, a, LLVMBuildAnd(bd, b, K32(31), ""), ""); break; + case U_SHR: r = LLVMBuildLShr(bd, a, LLVMBuildAnd(bd, b, K32(31), ""), ""); break; + case U_SAR: r = LLVMBuildAShr(bd, a, LLVMBuildAnd(bd, b, K32(31), ""), ""); break; + case U_NEG: r = LLVMBuildSub(bd, z, a, ""); break; + case U_NOT: r = LLVMBuildNot(bd, a, ""); break; + case U_LT: r = LLVMBuildZExt(bd, LLVMBuildICmp(bd, LLVMIntSLT, a, b, ""), i32t, ""); break; + case U_LTU: r = LLVMBuildZExt(bd, LLVMBuildICmp(bd, LLVMIntULT, a, b, ""), i32t, ""); break; + case U_EQ: r = LLVMBuildZExt(bd, LLVMBuildICmp(bd, LLVMIntEQ, a, b, ""), i32t, ""); break; + case U_NE: r = LLVMBuildZExt(bd, LLVMBuildICmp(bd, LLVMIntNE, a, b, ""), i32t, ""); break; + case U_MIN: r = LLVMBuildSelect(bd, LLVMBuildICmp(bd, LLVMIntSLT, a, b, ""), a, b, ""); break; + case U_MAX: r = LLVMBuildSelect(bd, LLVMBuildICmp(bd, LLVMIntSGT, a, b, ""), a, b, ""); break; + case U_MINU: r = LLVMBuildSelect(bd, LLVMBuildICmp(bd, LLVMIntULT, a, b, ""), a, b, ""); break; + case U_MAXU: r = LLVMBuildSelect(bd, LLVMBuildICmp(bd, LLVMIntUGT, a, b, ""), a, b, ""); break; + case U_MULHS: case U_MULHU: { + LLVMValueRef x = op == U_MULHS ? LLVMBuildSExt(bd, a, i64, "") : LLVMBuildZExt(bd, a, i64, ""); + LLVMValueRef y = op == U_MULHS ? LLVMBuildSExt(bd, b, i64, "") : LLVMBuildZExt(bd, b, i64, ""); + r = LLVMBuildTrunc(bd, LLVMBuildLShr(bd, LLVMBuildMul(bd, x, y, ""), LLVMConstInt(i64, 32, 0), ""), i32t, ""); + break; } + case U_CLZ: { + unsigned id = LLVMLookupIntrinsicID("llvm.ctlz", 9); + LLVMValueRef f = LLVMGetIntrinsicDeclaration(mod_, id, &i32t, 1), args[2]; + args[0] = a; args[1] = LLVMConstInt(i1t, 0, 0); + r = LLVMBuildCall2(bd, LLVMIntrinsicGetType(ctx, id, &i32t, 1), f, args, 2, ""); + break; } + case U_ABS: t = LLVMBuildICmp(bd, LLVMIntSLT, a, z, ""); r = LLVMBuildSelect(bd, t, LLVMBuildSub(bd, z, a, ""), a, ""); break; + case U_SEL: r = LLVMBuildSelect(bd, LLVMBuildICmp(bd, LLVMIntNE, a, z, ""), b, c, ""); break; + } + return r; +} +/* an enhancement as ir */ +static void ir_enh(Enh *e) +{ + LLVMValueRef *v = calloc(e->nv + 1, sizeof *v), s = gets(); + int i, j; + for (j = 0; j < e->nin; j++) v[j] = ld(add(s, e->nin - 1 - j)); + for (i = 0; i < e->n; i++) { + Uop *u = &e->u[i]; + LLVMValueRef a = u->op == U_LI ? 0 : v[u->a], b = uarity[u->op] > 1 || u->op == U_ST ? v[u->b] : 0, c = uarity[u->op] > 2 ? v[u->c] : 0, r = 0; + switch (u->op) { + case U_LI: r = K32(u->imm); break; + case U_LD: r = safeld(a); break; + case U_ST: { + LLVMBasicBlockRef yes = newbb(), done = newbb(); + LLVMBuildCondBr(bd, LLVMBuildICmp(bd, LLVMIntULT, a, K32(MW), ""), yes, done); + LLVMPositionBuilderAtEnd(bd, yes); st(a, b); LLVMBuildBr(bd, done); + LLVMPositionBuilderAtEnd(bd, done); + break; } + default: r = ir_op(u->op, a, b, c); + } + if (u->op != U_ST) v[u->d] = r; + } + s = add(s, e->nin - e->nout); sets(s); + for (j = 0; j < e->nout; j++) st(add(s, e->nout - 1 - j), v[e->out[j]]); + free(v); +} +static void llvm_init(void) +{ + LLVMErrorRef err; + if (getenv("ZYHOT")) HOT = atoi(getenv("ZYHOT")); + if (HOT < 1) HOT = 1; + if (HOT > 65000) HOT = 65000; + if (getenv("ZYPASSES")) lpasses = getenv("ZYPASSES"); + LLVMInitializeNativeTarget(); LLVMInitializeNativeAsmPrinter(); + if ((err = LLVMOrcCreateLLJIT(&jit, 0))) { char *msg = LLVMGetErrorMessage(err); fprintf(stderr, "llvm: %s\n", msg); LLVMDisposeErrorMessage(msg); return; } + tsc = LLVMOrcCreateNewThreadSafeContext(); + ctx = LLVMOrcThreadSafeContextGetContext(tsc); + i1t = LLVMInt1TypeInContext(ctx); i8t = LLVMInt8TypeInContext(ctx); i32t = LLVMInt32TypeInContext(ctx); + i64t = LLVMInt64TypeInContext(ctx); ptrt = LLVMPointerTypeInContext(ctx, 0); + bd = LLVMCreateBuilderInContext(ctx); + llvm_ok = 1; +} +/* compile the block at p; 0 if it couldn't be */ +static Blk lcompile(int p) +{ + char name[32]; LLVMTypeRef pt[2], ft; LLVMValueRef s, a, b, v, r, args[2]; LLVMTypeRef at[2]; + LLVMOrcExecutorAddress addr; LLVMErrorRef err; LLVMPassBuilderOptionsRef po; + int i = p, n = 0, w, k, t, done = 0; + if (!llvm_ok || nlbl >= LMAXB) return 0; + sprintf(name, "b%d_%d", p, nblocks++); + mod_ = LLVMModuleCreateWithNameInContext(name, ctx); + pt[0] = ptrt; pt[1] = ptrt; + ft = LLVMFunctionType(i32t, pt, 2, 0); + fn_ = LLVMAddFunction(mod_, name, ft); + vm_ = LLVMGetParam(fn_, 0); vregs = LLVMGetParam(fn_, 1); + LLVMPositionBuilderAtEnd(bd, newbb()); + vs = LLVMBuildAlloca(bd, i32t, "s"); vr = LLVMBuildAlloca(bd, i32t, "r"); + { LLVMValueRef r0 = K32(0), r1 = K32(1); + sets(LLVMBuildLoad2(bd, i32t, LLVMBuildGEP2(bd, i32t, vregs, &r0, 1, ""), "")); + LLVMBuildStore(bd, LLVMBuildLoad2(bd, i32t, LLVMBuildGEP2(bd, i32t, vregs, &r1, 1, ""), ""), vr); } + while (!done) { + if (i >= MW) { leave(K32(MW)); break; } + if (n++ >= 1024) { leave(K32(i)); break; } + w = m[i]; + lcov[i] = 1; + if (w & 1) { + /* a literal followed by call, jump or jz: a known target */ + if (i + 1 < MW && !(m[i + 1] & 1) && (k = m[i + 1] / 2) >= 6 && k <= 8) { + t = w >> 1; lcov[i + 1] = 1; + if (k == 8) { + LLVMBasicBlockRef tk = newbb(), go = newbb(); + s = gets(); a = ld(s); sets(add(s, 1)); + LLVMBuildCondBr(bd, LLVMBuildICmp(bd, LLVMIntEQ, a, K32(0), ""), tk, go); + LLVMPositionBuilderAtEnd(bd, tk); leave(K32(t)); + LLVMPositionBuilderAtEnd(bd, go); + i += 2; continue; + } + if (k == 6) { r = LLVMBuildLoad2(bd, i32t, vr, ""); st(r, K32(i + 2)); LLVMBuildStore(bd, add(r, 1), vr); } + leave(K32(t)); i += 2; break; + } + push(K32(w >> 1)); i++; continue; + } + k = w / 2; + s = gets(); + switch (k) { + case 0: leave(K32(MW)); done = 1; break; + case 1: st(s, safeld(ld(s))); break; + case 2: { + LLVMBasicBlockRef in = newbb(), cv = newbb(), plain = newbb(), out = newbb(), quit = newbb(); + a = ld(s); b = ld(add(s, 1)); sets(add(s, 2)); + LLVMBuildCondBr(bd, LLVMBuildICmp(bd, LLVMIntULT, a, K32(MW), ""), in, out); + LLVMPositionBuilderAtEnd(bd, in); + v = LLVMBuildLoad2(bd, i8t, LLVMBuildGEP2(bd, i8t, cptr(lcov), &a, 1, ""), ""); + LLVMBuildCondBr(bd, LLVMBuildICmp(bd, LLVMIntNE, v, LLVMConstInt(i8t, 0, 0), ""), cv, plain); + LLVMPositionBuilderAtEnd(bd, plain); st(a, b); LLVMBuildBr(bd, out); + LLVMPositionBuilderAtEnd(bd, cv); + at[0] = i32t; at[1] = i32t; args[0] = a; args[1] = b; + v = callp((void *)lhook, i32t, at, 2, args); + LLVMBuildCondBr(bd, LLVMBuildICmp(bd, LLVMIntNE, v, K32(0), ""), quit, out); + LLVMPositionBuilderAtEnd(bd, quit); leave(K32(i + 1)); /* a block died: back to the loop */ + LLVMPositionBuilderAtEnd(bd, out); + break; } + case 3: a = ld(s); st(s, safeld(LLVMBuildAdd(bd, add(s, 1), a, ""))); break; + case 4: sets(add(s, 1)); break; + case 5: a = ld(s); b = ld(add(s, 1)); st(s, b); st(add(s, 1), a); break; + case 6: case 7: + a = ld(s); sets(add(s, 1)); + if (k == 6) { r = LLVMBuildLoad2(bd, i32t, vr, ""); st(r, K32(i + 1)); LLVMBuildStore(bd, add(r, 1), vr); } + leave(a); done = 1; break; + case 8: { + LLVMBasicBlockRef tk = newbb(), go = newbb(); + a = ld(s); b = ld(add(s, 1)); sets(add(s, 2)); + LLVMBuildCondBr(bd, LLVMBuildICmp(bd, LLVMIntEQ, b, K32(0), ""), tk, go); + LLVMPositionBuilderAtEnd(bd, tk); leave(a); + LLVMPositionBuilderAtEnd(bd, go); + break; } + case 9: + r = add(LLVMBuildLoad2(bd, i32t, vr, ""), -1); LLVMBuildStore(bd, r, vr); + leave(ld(r)); done = 1; break; + case 10: v = callp((void *)Q, i8t, 0, 0, 0); push(LLVMBuildSExt(bd, v, i32t, "")); break; + case 11: + if (sizeof J == 1) push(LLVMBuildZExt(bd, LLVMBuildLoad2(bd, i8t, cptr(&J), ""), i32t, "")); + else push(LLVMBuildLoad2(bd, i32t, cptr(&J), "")); + break; + case 12: at[0] = i8t; args[0] = LLVMBuildTrunc(bd, ld(s), i8t, ""); callp((void *)G, LLVMVoidTypeInContext(ctx), at, 1, args); sets(add(s, 1)); break; + case 13: case 14: case 15: case 16: case 17: case 18: case 19: case 20: + a = ld(s); b = ld(add(s, 1)); s = add(s, 1); sets(s); + switch (k) { + case 13: v = LLVMBuildAdd(bd, b, a, ""); break; + case 14: v = LLVMBuildSub(bd, b, a, ""); break; + case 15: v = LLVMBuildMul(bd, b, a, ""); break; + case 16: v = LLVMBuildAnd(bd, b, a, ""); break; + case 17: v = LLVMBuildOr(bd, b, a, ""); break; + case 18: v = LLVMBuildXor(bd, b, a, ""); break; + case 19: v = LLVMBuildAShr(bd, b, LLVMBuildAnd(bd, a, K32(31), ""), ""); break; + default: v = LLVMBuildZExt(bd, LLVMBuildICmp(bd, LLVMIntSLT, b, a, ""), i32t, ""); + } + st(s, v); break; + case 21: /* install: then back to the loop, which drops every block */ + at[0] = i32t; args[0] = ld(s); + st(s, callp((void *)linstall, i32t, at, 1, args)); + leave(K32(i + 1)); done = 1; break; + default: + if (k >= XOP0 && k < XOP0 + NENH && enh[k - XOP0].live) { ir_enh(&enh[k - XOP0]); break; } + leave(K32(MW)); done = 1; /* not wired: halt */ + } + i++; + } + lbl[nlbl].a = p; lbl[nlbl++].b = i; + po = LLVMCreatePassBuilderOptions(); + if ((err = LLVMRunPasses(mod_, lpasses, 0, po))) { LLVMConsumeError(err); } + LLVMDisposePassBuilderOptions(po); + if ((err = LLVMOrcLLJITAddLLVMIRModule(jit, LLVMOrcLLJITGetMainJITDylib(jit), LLVMOrcCreateNewThreadSafeModule(mod_, tsc)))) { + char *msg = LLVMGetErrorMessage(err); fprintf(stderr, "llvm: %s\n", msg); LLVMDisposeErrorMessage(msg); return 0; + } + if ((err = LLVMOrcLLJITLookup(jit, &addr, name))) { char *msg = LLVMGetErrorMessage(err); fprintf(stderr, "llvm: %s\n", msg); LLVMDisposeErrorMessage(msg); return 0; } + if (trace) fprintf(stderr, "llvm: %d..%d compiled\n", p, i - 1); + return (Blk)(size_t)addr; +} +#endif + +/* ---- o3 regions without the arm64 jit: compiled by llvm, or run here ---- */ +#ifndef ZYLLVM +static unsigned char rcov[MW]; /* 1 on the words of a lifted region */ +#endif +static int rvals[MAXUOP * 4 + 16]; +static int rdrop(int a) /* a store landed on region code: drop those regions */ +{ + int j, x, hit = 0; + for (j = 0; j < nrg; j++) + if (rgs[j]->lifted && rgs[j]->lo <= a && a < rgs[j]->hi) { + if (trace) fprintf(stderr, "o3: dropped %d..%d (a store to %d: %d)\n", rgs[j]->lo, rgs[j]->hi - 1, a, m[a]); + rclear(rgs[j]); hit = 1; + } + if (hit) { + memset(rcov, 0, sizeof rcov); + for (j = 0; j < nrg; j++) if (rgs[j]->lifted) for (x = rgs[j]->lo; x < rgs[j]->hi; x++) rcov[x] = 1; + } + return hit; +} +#ifdef ZYLLVM +static int lhook(int a, int v); +#define RSTORE(a, b) do { if ((unsigned)(a) < MW) { if (lcov[a]) lhook(a, b); else m[a] = b; } } while (0) +#else +#define RSTORE(a, b) do { if ((unsigned)(a) < MW) { m[a] = b; if (rcov[a]) rdrop(a); } } while (0) +#endif +/* the region interpreter: ssa values in an array, vars in locals */ +static int rexec(Rg *R, int e, int *ps, int *pr) +{ + int var[RMAXV], nv2[RMAXV], *v = rvals, s = *ps, r = *pr, b = R->entb[e], fp, j, i, t, o; + var[0] = fp = mread(ZFP); + for (j = 1; j < R->nv; j++) var[j] = mread(fp + R->slot[j]); + for (;;) { + RB *B = &R->b[b]; + for (i = 0; i < B->n; i++) { + Uop *u = &B->u[i]; + switch (u->op) { + case U_LI: v[u->d] = u->imm; break; + case U_VGET: v[u->d] = var[u->imm]; break; + case U_SLD: v[u->d] = mread(s + u->imm); break; + case U_LD: v[u->d] = mread(v[u->a]); break; + case U_ST: RSTORE(v[u->a], v[u->b]); break; + default: v[u->d] = ev(u->op, v[u->a], v[u->b], v[u->c]); + } + } + for (j = 0; j < B->nso; j++) { o = s + B->so[2 * j]; if ((unsigned)o < MW) m[o] = v[B->so[2 * j + 1]]; } + s += B->delta; + for (j = 1; j < R->nv; j++) if (B->vs[j] >= 0) nv2[j] = v[B->vs[j]]; + for (j = 1; j < R->nv; j++) if (B->vs[j] >= 0) var[j] = nv2[j]; + if (B->term == T_BR) { b = B->s1; continue; } + if (B->term == T_CBR) { + if (!v[B->cv]) { if (B->s1 >= 0) { b = B->s1; continue; } t = B->x1; } + else { if (B->s2 >= 0) { b = B->s2; continue; } t = B->x2; } + } else if (B->term == T_JMP) t = v[B->cv]; + else if (B->term == T_CALL) { m[r++] = B->x2; t = v[B->cv]; } + else t = m[--r]; + break; + } + for (j = 0; j < R->np; j++) if ((unsigned)(fp + R->ps[j]) < MW) m[fp + R->ps[j]] = var[R->pv[j]]; + *ps = s; *pr = r; + return t; +} +#ifndef ZYLLVM +static void rforget(Rg *R) { (void)R; } +static void rmark(Rg *R) { int x; for (x = R->lo; x < R->hi; x++) rcov[x] = 1; } +static void rcompile(Rg *R, int eager) { (void)R; (void)eager; } +#endif +#ifdef ZYLLVM + +/* ---- o3 regions in llvm: one function per region, vars in allocas (so + mem2reg makes them ssa across blocks), each block's micro-ops as ir, then + the O3 pipeline. f(mem, regs, entry) returns the next vm address. */ +typedef int (*RFn)(int *mem, int *regs, int ent); +static int nregions; +static LLVMValueRef rvv[RMAXV]; +static void rforget(Rg *R) { R->nat = 0; } +static void rmark(Rg *R) { int x; for (x = R->lo; x < R->hi; x++) rcov[x] = lcov[x] = 1; } +static void rspill_ir(Rg *R) +{ + LLVMValueRef fp = LLVMBuildLoad2(bd, i32t, rvv[0], ""); + int j; + for (j = 0; j < R->np; j++) st(add(fp, R->ps[j]), LLVMBuildLoad2(bd, i32t, rvv[R->pv[j]], "")); +} +static LLVMBasicBlockRef rexit_bb(Rg *R, int t) +{ + LLVMBasicBlockRef here = LLVMGetInsertBlock(bd), x = newbb(); + LLVMPositionBuilderAtEnd(bd, x); + rspill_ir(R); leave(K32(t)); + LLVMPositionBuilderAtEnd(bd, here); + return x; +} +/* m[a] = v, through lhook if some compiled code covers the word */ +static void rstore_ir(LLVMValueRef a, LLVMValueRef b) +{ + LLVMBasicBlockRef in = newbb(), cv = newbb(), plain = newbb(), out = newbb(); + LLVMValueRef v, args[2]; LLVMTypeRef at[2]; + LLVMBuildCondBr(bd, LLVMBuildICmp(bd, LLVMIntULT, a, K32(MW), ""), in, out); + LLVMPositionBuilderAtEnd(bd, in); + v = LLVMBuildLoad2(bd, i8t, LLVMBuildGEP2(bd, i8t, cptr(lcov), &a, 1, ""), ""); + LLVMBuildCondBr(bd, LLVMBuildICmp(bd, LLVMIntNE, v, LLVMConstInt(i8t, 0, 0), ""), cv, plain); + LLVMPositionBuilderAtEnd(bd, plain); st(a, b); LLVMBuildBr(bd, out); + LLVMPositionBuilderAtEnd(bd, cv); + at[0] = i32t; at[1] = i32t; args[0] = a; args[1] = b; + callp((void *)lhook, i32t, at, 2, args); + LLVMBuildBr(bd, out); + LLVMPositionBuilderAtEnd(bd, out); +} +static void *lregion(Rg *R) +{ + char name[40]; LLVMTypeRef pt[3], ft; LLVMValueRef ent, sw, *val, s0, fp, r; + LLVMBasicBlockRef *bb, eb, dflt; + LLVMOrcExecutorAddress addr; LLVMErrorRef err; LLVMPassBuilderOptionsRef po; + int b, i, j, e; + sprintf(name, "o3_%d_%d", R->lo, nregions++); + mod_ = LLVMModuleCreateWithNameInContext(name, ctx); + pt[0] = ptrt; pt[1] = ptrt; pt[2] = i32t; + ft = LLVMFunctionType(i32t, pt, 3, 0); + fn_ = LLVMAddFunction(mod_, name, ft); + vm_ = LLVMGetParam(fn_, 0); vregs = LLVMGetParam(fn_, 1); ent = LLVMGetParam(fn_, 2); + LLVMPositionBuilderAtEnd(bd, newbb()); + vs = LLVMBuildAlloca(bd, i32t, "s"); vr = LLVMBuildAlloca(bd, i32t, "r"); + for (j = 0; j < R->nv; j++) rvv[j] = LLVMBuildAlloca(bd, i32t, ""); + { LLVMValueRef r0 = K32(0), r1 = K32(1); + sets(LLVMBuildLoad2(bd, i32t, LLVMBuildGEP2(bd, i32t, vregs, &r0, 1, ""), "")); + LLVMBuildStore(bd, LLVMBuildLoad2(bd, i32t, LLVMBuildGEP2(bd, i32t, vregs, &r1, 1, ""), ""), vr); } + bb = malloc(R->nb * sizeof *bb); + for (b = 0; b < R->nb; b++) bb[b] = newbb(); + dflt = newbb(); + sw = LLVMBuildSwitch(bd, ent, dflt, (unsigned)R->ne); + for (e = 0; e < R->ne; e++) { + eb = newbb(); + LLVMAddCase(sw, K32(e), eb); + LLVMPositionBuilderAtEnd(bd, eb); + fp = ld(K32(ZFP)); + LLVMBuildStore(bd, fp, rvv[0]); + for (j = 1; j < R->nv; j++) LLVMBuildStore(bd, safeld(add(fp, R->slot[j])), rvv[j]); + LLVMBuildBr(bd, bb[R->entb[e]]); + } + LLVMPositionBuilderAtEnd(bd, dflt); leave(K32(-1)); + val = calloc(R->maxnv + 1, sizeof *val); + for (b = 0; b < R->nb; b++) { + RB *B = &R->b[b]; + LLVMPositionBuilderAtEnd(bd, bb[b]); + s0 = gets(); + for (i = 0; i < B->n; i++) { + Uop *u = &B->u[i]; + LLVMValueRef a = nargs(u->op) > 0 ? val[u->a] : 0, x = nargs(u->op) > 1 ? val[u->b] : 0, c = nargs(u->op) > 2 ? val[u->c] : 0; + switch (u->op) { + case U_LI: val[u->d] = K32(u->imm); break; + case U_VGET: val[u->d] = LLVMBuildLoad2(bd, i32t, rvv[u->imm], ""); break; + case U_SLD: val[u->d] = ld(add(s0, u->imm)); break; + case U_LD: val[u->d] = safeld(a); break; + case U_ST: rstore_ir(a, x); break; + default: val[u->d] = ir_op(u->op, a, x, c); + } + } + for (j = 0; j < B->nso; j++) st(add(s0, B->so[2 * j]), val[B->so[2 * j + 1]]); + sets(add(s0, B->delta)); + for (j = 1; j < R->nv; j++) if (B->vs[j] >= 0) LLVMBuildStore(bd, val[B->vs[j]], rvv[j]); + switch (B->term) { + case T_BR: LLVMBuildBr(bd, bb[B->s1]); break; + case T_CBR: + LLVMBuildCondBr(bd, LLVMBuildICmp(bd, LLVMIntEQ, val[B->cv], K32(0), ""), + B->s1 >= 0 ? bb[B->s1] : rexit_bb(R, B->x1), B->s2 >= 0 ? bb[B->s2] : rexit_bb(R, B->x2)); + break; + case T_CALL: + r = LLVMBuildLoad2(bd, i32t, vr, ""); st(r, K32(B->x2)); LLVMBuildStore(bd, add(r, 1), vr); + rspill_ir(R); leave(val[B->cv]); + break; + case T_RET: + r = add(LLVMBuildLoad2(bd, i32t, vr, ""), -1); LLVMBuildStore(bd, r, vr); + rspill_ir(R); leave(ld(r)); + break; + default: rspill_ir(R); leave(val[B->cv]); + } + } + free(val); free(bb); + po = LLVMCreatePassBuilderOptions(); + if ((err = LLVMRunPasses(mod_, "default", 0, po))) LLVMConsumeError(err); + LLVMDisposePassBuilderOptions(po); + if ((err = LLVMOrcLLJITAddLLVMIRModule(jit, LLVMOrcLLJITGetMainJITDylib(jit), LLVMOrcCreateNewThreadSafeModule(mod_, tsc)))) { + char *msg = LLVMGetErrorMessage(err); fprintf(stderr, "llvm: %s\n", msg); LLVMDisposeErrorMessage(msg); return 0; + } + if ((err = LLVMOrcLLJITLookup(jit, &addr, name))) { char *msg = LLVMGetErrorMessage(err); fprintf(stderr, "llvm: %s\n", msg); LLVMDisposeErrorMessage(msg); return 0; } + if (trace) fprintf(stderr, "o3: %d..%d compiled by llvm\n", R->lo, R->hi - 1); + return (void *)(size_t)addr; +} +static void rcompile(Rg *R, int eager) +{ + (void)eager; + if (R->nat || R->nonat || !llvm_ok) return; + if (!(R->nat = lregion(R))) R->nonat = 1; +} +#endif +#define LD(x) ((unsigned)(x) < MW ? m[x] : 0) +static int run(int p) +{ + int s = DS0, r = RS0, w, a, b, xfer = 1; + unsigned long long steps = 0; +#ifdef ZYLLVM + int regs[2]; + if (!getenv("ZYNOLLVM")) llvm_init(); +#endif + while ((unsigned)p < MW) { + if (xfer && (s < DSLIM || r > RSLIM)) { p = s < DSLIM ? MW + 1 : MW + 2; break; } + if (xfer && o3ent[p]) { /* an o3 region */ + Rg *R = rat(p); + if (R) { +#ifdef ZYLLVM + rcompile(R, 0); + if (R->nat) { regs[0] = s; regs[1] = r; p = ((RFn)R->nat)(m, regs, o3ei[p]); s = regs[0]; r = regs[1]; } + else +#endif + p = rexec(R, o3ei[p], &s, &r); + continue; + } + } +#ifdef ZYLLVM + if (xfer && llvm_ok) { /* at a block start: run it compiled, or count it */ + xfer = 0; + if (!lt[p] && hot[p] < 65535 && ++hot[p] == HOT && !(lt[p] = lcompile(p))) hot[p] = 65535; + if (lt[p]) { + regs[0] = s; regs[1] = r; + p = lt[p](m, regs); + s = regs[0]; r = regs[1]; + if (need_flush) { need_flush = 0; ldrop_all(); } + xfer = 1; + continue; + } + } +#endif + w = m[p++]; + steps++; + if (w & 1) { m[--s] = w >> 1; continue; } + a = m[s]; b = m[s + 1]; + switch (w / 2) { + case 1: m[s] = LD(a); break; + case 2: + if ((unsigned)a < MW) { +#ifdef ZYLLVM + if (lcov[a]) lhook(a, b); else +#else + if (rcov[a]) { m[a] = b; rdrop(a); } else +#endif + m[a] = b; + } + s += 2; break; + case 3: m[s] = LD(s + 1 + a); break; + case 4: s++; break; + case 5: m[s] = b; m[s + 1] = a; break; + case 6: m[r++] = p; p = a; s++; xfer = 1; break; + case 7: p = a; s++; xfer = 1; break; + case 8: if (!b) { p = a; xfer = 1; } s += 2; break; + case 9: p = m[--r]; xfer = 1; break; + case 10: m[--s] = (signed char)Q(); break; + case 11: m[--s] = J; break; + case 12: G((char)a); s++; break; + case 13: m[++s] = b + a; break; + case 14: m[++s] = (int)((unsigned)b - (unsigned)a); break; + case 15: m[++s] = (int)((unsigned)b * (unsigned)a); break; + case 16: m[++s] = b & a; break; + case 17: m[++s] = b | a; break; + case 18: m[++s] = b ^ a; break; + case 19: m[++s] = b >> (a & 31); break; + case 20: m[++s] = b < a; break; + case 21: + m[s] = install(a); +#ifdef ZYLLVM + if (need_flush) { need_flush = 0; ldrop_all(); } +#else + if (need_flush) { need_flush = 0; rdrop_all(); memset(rcov, 0, sizeof rcov); } +#endif + break; + default: + if (w / 2 >= XOP0 && w / 2 < XOP0 + NENH && enh[w / 2 - XOP0].live) s = (int)(xexec(&enh[w / 2 - XOP0], m + s) - m); + else p = MW; /* halt: unwired opcodes and the reserved negative ones */ + } + } + if (trace) fprintf(stderr, "interp: %llu steps\n", steps); + return stopped(p); +} +#endif + +int main(void) +{ + trace = getenv("ZYTRACE") != 0; + memcpy(m + GORG, genome, sizeof genome); +#ifdef ZYJIT + { + int p = GORG; + union { unsigned *u; int (*f)(long *, void *); } enter; +#ifdef __APPLE__ + code = mmap(0, CODEW * 4, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANON | MAP_JIT, -1, 0); +#else + code = mmap(0, CODEW * 4, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); +#endif + if (code == MAP_FAILED) { perror("mmap"); return 1; } + jitwrite(1); + build_stubs(); + jitwrite(0); + syncicache(code, cp); + S[0] = (long)m; S[1] = DS0; S[2] = RS0; S[3] = (long)tab; S[4] = (long)cov; + S[5] = (long)Q; S[6] = (long)G; S[7] = (long)&J; S[8] = (long)hook; + S[9] = (long)inst_hook; S[10] = (long)xrun; + S[11] = (long)(m + DSLIM); S[12] = (long)(m + RSLIM); + enter.u = x_enter; + while ((unsigned)p < MW) { + void *c = tab[p]; + if (!c) { jitwrite(1); c = translate(p); jitwrite(0); } + p = enter.f(S, c); + if (need_flush) { need_flush = 0; jitwrite(1); flush(); jitwrite(0); } + } + return stopped(p); + } +#else + return run(GORG); +#endif + return 0; +} diff --git a/docs/history/17-o3-boundaries/code/examples/bf.zc b/docs/history/17-o3-boundaries/code/examples/bf.zc new file mode 100644 index 0000000..384025a --- /dev/null +++ b/docs/history/17-o3-boundaries/code/examples/bf.zc @@ -0,0 +1,50 @@ +// a brainfuck compiler. it turns a brainfuck program into zc source, hands +// that to the compiler that's sitting in memory, and runs the result, which on +// arm64 means native code. runs of + - < > fold into one statement each. +// give it a program after a backtick, or it runs the hello world below. +let out[30000], n, depth; +fn emit(s) { let i = 0; while (s[i]) { out[n] = s[i]; n++; i++; } return 0; } +fn emitn(int v) { if (v >= 10) emitn(v / 10); out[n] = '0' + v % 10; n++; return 0; } +// how many times does c repeat from s[i] +int run(s, int i, int c) { int k = 0; while (s[i + k] == c) k++; return k; } + +fn translate(s) +{ + int i = 0, k; + n = 0; depth = 0; + emit("let t[30000], p = 0;\n"); + while (s[i]) { + switch (s[i]) { + case '+', '-': + k = run(s, i, s[i]); + emit("t[p] = (t[p] "); emit(s[i] == '+' ? "+ " : "- "); emitn(k); emit(") & 255;\n"); + i = i + k; continue; + case '>', '<': + k = run(s, i, s[i]); + emit("p "); emit(s[i] == '>' ? "+= " : "-= "); emitn(k); emit(";\n"); + i = i + k; continue; + case '[': emit("while (t[p]) {\n"); depth++; + case ']': emit("}\n"); depth--; + case '.': emit("putc(t[p]);\n"); + case ',': emit("t[p] = eof() ? 0 : getc() & 255;\n"); + } + i++; + } + emit("return 0;\n"); + out[n] = 0; + return depth; +} +let hello = "++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++."; +let prog[20000]; +fn main() +{ + let src = hello, k = 0, lines = 0, i = 0; + while (!eof() && k < 19999) { let c = getc(); if (c != 10) { prog[k] = c; k++; } } + prog[k] = 0; + if (k) src = prog; + if (translate(src)) { puts("unbalanced brackets\n"); return 1; } + while (out[i]) { if (out[i] == 10) lines++; i++; } + puts("compiled to "); putn(lines); puts(" lines of zc, running it:\n"); + evals(out); + return 0; +} diff --git a/docs/history/17-o3-boundaries/code/examples/dump.zc b/docs/history/17-o3-boundaries/code/examples/dump.zc new file mode 100644 index 0000000..6082213 --- /dev/null +++ b/docs/history/17-o3-boundaries/code/examples/dump.zc @@ -0,0 +1,32 @@ +// printing any struct with no printing code: the heap knows each object's +// type, and reflection walks its fields +struct person { *char name; int age; }; +struct item { *char name; int count; *person owner; }; + +fn dump(p) +{ + let t = typeof_obj(p), i = 0; + if (!t) { puts("?"); return 0; } + tprint(t); puts(" { "); + while (i < tnfields(t)) { + let ft = tftype(t, i), v = p[tfoff(t, i)]; + if (i) puts(", "); + puts(tfname(t, i)); puts(": "); + if (tkind(ft) == TY_PTR && telem(ft) == typeof(char)) { putc('"'); puts(v); putc('"'); } + else if (tkind(ft) == TY_PTR) { if (v) dump(v); else puts("null"); } + else putn(v); + i++; + } + puts(" }"); + return 0; +} +fn main() +{ + *person ana = new(person); + ana.name = "ana"; ana.age = 30; + *item lamp = new(item); + lamp.name = "lamp"; lamp.count = 3; lamp.owner = ana; + dump(lamp); putc(10); + free(d*lamp); + return 0; +} diff --git a/docs/history/17-o3-boundaries/code/examples/echo.zc b/docs/history/17-o3-boundaries/code/examples/echo.zc new file mode 100644 index 0000000..01f28b0 --- /dev/null +++ b/docs/history/17-o3-boundaries/code/examples/echo.zc @@ -0,0 +1,2 @@ +// everything after the backtick is left unread, for the program itself +fn main() { while (!eof()) putc(getc()); return 0; } diff --git a/docs/history/17-o3-boundaries/code/examples/fact.zc b/docs/history/17-o3-boundaries/code/examples/fact.zc new file mode 100644 index 0000000..2a218b2 --- /dev/null +++ b/docs/history/17-o3-boundaries/code/examples/fact.zc @@ -0,0 +1,4 @@ +fn fact(n) { if (n < 2) return 1; return n * fact(n - 1); } +// prints in binary, like the old asm example +fn bin(n) { if (n > 1) bin(n >> 1); putc('0' + (n & 1)); return 0; } +fn main() { bin(fact(10)); putc(10); putn(fact(10)); putc(10); return 0; } diff --git a/docs/history/17-o3-boundaries/code/examples/hello.zc b/docs/history/17-o3-boundaries/code/examples/hello.zc new file mode 100644 index 0000000..f36a810 --- /dev/null +++ b/docs/history/17-o3-boundaries/code/examples/hello.zc @@ -0,0 +1 @@ +fn main() { puts("hello, world!\n"); return 0; } diff --git a/docs/history/17-o3-boundaries/code/examples/lang.zc b/docs/history/17-o3-boundaries/code/examples/lang.zc new file mode 100644 index 0000000..e4d91c5 --- /dev/null +++ b/docs/history/17-o3-boundaries/code/examples/lang.zc @@ -0,0 +1,53 @@ +// growing the language from inside a program, while it compiles + +// x |> f calls f(x). on a stack machine that's just "call": the value is +// already on the stack, under f's address. +immediate fn pipe_op() { next(); add_binop("|>", 1, "c"); return 0; } +pipe_op + +// unless (c) stmt +immediate fn unless() +{ + let a; + next(); expect(intern("(")); plainuse(expr()); rval(); expect(intern(")")); + emitseq(useq[0], ulen[0]); // not + a = hole(); op(O_JZ); + substmt(); + setlit(a, here); + return 0; +} +// assert (c): the failure message carries the line it was written on +fn assert_fail(int line) { puts("assertion failed on line "); putn(line); putc(10); halt(); return 0; } +immediate fn assert() +{ + let a, l = tline; + next(); expect(intern("(")); plainuse(expr()); rval(); expect(intern(")")); + emitseq(useq[0], ulen[0]); // not: the jump below skips the failure when it holds + a = hole(); op(O_JZ); + lit(l); callname(intern("assert_fail")); op(O_DROP); + setlit(a, here); + return 0; +} +// times N { body }: the body pasted N times, at compile time +immediate fn times() +{ + let n, b, i = 0; + next(); n = tv; next(); + b = grab(); + while (i < n) { feed(qdup(b)); i++; } + free(d*b); + return 0; +} + +fn double(x) { return x * 2; } +fn inc(x) { return x + 1; } +fn main() +{ + putn(5 |> double |> inc |> double); putc(10); + unless (1 > 2) puts("one isn't bigger than two\n"); + times 3 { putc('*'); } + putc(10); + assert(double(4) == 8); + assert(inc(1) == 3); + return 0; +} diff --git a/docs/history/17-o3-boundaries/code/examples/life.zc b/docs/history/17-o3-boundaries/code/examples/life.zc new file mode 100644 index 0000000..a43d660 --- /dev/null +++ b/docs/history/17-o3-boundaries/code/examples/life.zc @@ -0,0 +1,38 @@ +// conway's life on a 10 x 20 torus, with typed 2d arrays in a struct. +// a glider walks diagonally and wraps around the edges. +const H = 10, W = 20; +struct board { [10][20]int c; }; + +int get(*board b, int y, int x) { return b.c[(y + H) % H][(x + W) % W]; } +fn step(*board from, *board to) +{ + for (int y = 0; y < H; y++) + for (int x = 0; x < W; x++) { + int n = 0; + for (int dy = 0 - 1; dy <= 1; dy++) + for (int dx = 0 - 1; dx <= 1; dx++) + if (dy || dx) n += get(from, y + dy, x + dx); + to.c[y][x] = n == 3 || (n == 2 && from.c[y][x]); + } + return 0; +} +fn show(*board b, int gen) +{ + puts("generation "); putn(gen); putc(10); + for (int y = 0; y < H; y++) { for (int x = 0; x < W; x++) putc(b.c[y][x] ? '#' : '.'); putc(10); } + return 0; +} +fn main() +{ + *board a = new(board), b = new(board); + a.c[0][1] = 1; a.c[1][2] = 1; a.c[2][0] = 1; a.c[2][1] = 1; a.c[2][2] = 1; + // two generations a pass, a -> b -> a: owned boards can't be swapped + // through a temporary, since the analyzer can't follow that + for (int g = 0; g <= 40; g += 2) { + if (g % 20 == 0) show(a, g); + step(a, b); + step(b, a); + } + free(d*a); free(d*b); + return 0; +} diff --git a/docs/history/17-o3-boundaries/code/examples/lists.zc b/docs/history/17-o3-boundaries/code/examples/lists.zc new file mode 100644 index 0000000..3cf0206 --- /dev/null +++ b/docs/history/17-o3-boundaries/code/examples/lists.zc @@ -0,0 +1,34 @@ +// ownership in practice: a list owns its boxes. nothing here calls free; +// everything is released at the end of the block that owns it. +.u box(v) { let b = new(1); b[0] = v; return u*b; } +.u mklist(cap) { let l = new(cap + 1); l[0] = 0; return u*l; } +// item m*arrives (it was given down), and goes d*on into the borrowed list +fn push(.m l, .d item) { l[l[0] + 1] = d*item; l[0] = l[0] + 1; return 0; } +fn show(.m l) +{ + let i = 1; + putc('['); + while (i <= l[0]) { if (i > 1) putc(' '); putn(l[i][0]); i++; } + putc(']'); + return 0; +} +.u squares(n) { + let l = mklist(n), i = 0; + while (i < n) { + let b = box(i * i); + push(l, d*b); + i++; + } + return u*l; +} +fn main() +{ + let before = live_objects(); + { + let l = squares(6); + show(l); putc(10); + puts("live: "); putn(live_objects() - before); putc(10); + } + puts("after the block: "); putn(live_objects() - before); putc(10); + return 0; +} diff --git a/docs/history/17-o3-boundaries/code/examples/mandel.zc b/docs/history/17-o3-boundaries/code/examples/mandel.zc new file mode 100644 index 0000000..5810397 --- /dev/null +++ b/docs/history/17-o3-boundaries/code/examples/mandel.zc @@ -0,0 +1,36 @@ +// the mandelbrot set in 16.16 fixed point. the cpu has no fixed-point +// multiply, so one gets wired in: a 32x32 -> 64 bit multiply, keeping the +// middle 32 bits. the jit inlines it as two native multiplies and a shift. +enhance fmul(a, b) micro + "r2 = mul a b; r3 = mulhs a b" + "; r3 = shl r3 16; r2 = shr r2 16; a = or r3 r2"; + +const ONE = 65536, W = 72, H = 28, MAXIT = 40; +let shades = " .,:;-=+*%#@"; + +int escape(int cx, int cy) { + int x = 0, y = 0, i = 0; + while (i < MAXIT) { + int xx = fmul(x, x), yy = fmul(y, y); + if (xx + yy > 4 * ONE) return i; + y = 2 * fmul(x, y) + cy; + x = xx - yy + cx; + i++; + } + return MAXIT; +} +fn main() +{ + int row = 0; + while (row < H) { + int col = 0, cy = (row * 2400 / (H - 1) - 1200) * ONE / 1000; + while (col < W) { + int cx = (col * 3200 / (W - 1) - 2200) * ONE / 1000, it = escape(cx, cy); + putc(it == MAXIT ? '@' : shades[it * 11 / MAXIT]); + col++; + } + putc(10); + row++; + } + return 0; +} diff --git a/docs/history/17-o3-boundaries/code/examples/primes.zc b/docs/history/17-o3-boundaries/code/examples/primes.zc new file mode 100644 index 0000000..26df0b5 --- /dev/null +++ b/docs/history/17-o3-boundaries/code/examples/primes.zc @@ -0,0 +1,38 @@ +// primes below 60000: an o3 sieve over a fixed array, the primes +// gathered into a growable one, then a few questions asked of them. +// with ZYTRACE=1 the o3 lines show each region being lifted and compiled. + +// marks the composites; f[i] ends up 1 for every prime i +o3 int sieve(fixed []int f) +{ + int n = len(f), c = 0; + for (int i = 2; i < n; i++) f[i] = 1; + for (int i = 2; i * i < n; i++) + if (f[i]) for (int j = i * i; j < n; j += i) f[j] = 0; + for (int i = 2; i < n; i++) c += f[i]; + return c; +} +// the largest gap between neighbours, and where it starts +o3 int gap(fixed []int f, *int at) +{ + int n = len(f), last = 2, best = 0, where = 0; + for (int i = 3; i < n; i++) + if (f[i]) { if (i - last > best) { best = i - last; where = last; } last = i; } + *at = where; + return best; +} +fn main() +{ + fixed []int f = new(fixed []int, 60000); + []int ps = new([]int); + int c = sieve(f), at = 0, twins = 0; + for (int i = 2; i < len(f); i++) if (f[i]) push(ps, i); + o3 { for (int k = 1; k < len(ps); k++) if (ps[k] - ps[k - 1] == 2) twins++; } + puts("primes below 60000: "); putn(c); putc(10); + puts("the last five:"); for (int k = len(ps) - 5; k < len(ps); k++) { putc(' '); putn(ps[k]); } putc(10); + puts("twin pairs: "); putn(twins); putc(10); + int g = gap(f, &at); + puts("widest gap: "); putn(g); puts(", after "); putn(at); putc(10); + free(d*f); free(d*ps); + return 0; +} diff --git a/docs/history/17-o3-boundaries/code/examples/quine.zc b/docs/history/17-o3-boundaries/code/examples/quine.zc new file mode 100644 index 0000000..23be8cb --- /dev/null +++ b/docs/history/17-o3-boundaries/code/examples/quine.zc @@ -0,0 +1 @@ +let s="let s=#;fn main(){let i=0;while(s[i]){if(s[i]==35){putc(34);puts(s);putc(34);}else putc(s[i]);i++;}putc(10);return 0;}";fn main(){let i=0;while(s[i]){if(s[i]==35){putc(34);puts(s);putc(34);}else putc(s[i]);i++;}putc(10);return 0;} diff --git a/docs/history/17-o3-boundaries/code/examples/repl.zc b/docs/history/17-o3-boundaries/code/examples/repl.zc new file mode 100644 index 0000000..b7b81b8 --- /dev/null +++ b/docs/history/17-o3-boundaries/code/examples/repl.zc @@ -0,0 +1,27 @@ +// a repl on the resident compiler: each backtick-separated chunk of input is +// compiled as a function body and run. definitions (fn ...) can be loaded too: +// a chunk that starts with "fn" goes through load instead of eval. +let buf[4000]; +fn readchunk() +{ + let n = 0, c; + while (!eof()) { + c = getc(); + if (c == 96) break; + if (n < 3999) { buf[n] = c; n++; } + } + buf[n] = 0; + return n; +} +fn main() +{ + let q, i; + while (!eof()) { + readchunk(); + q = tokens(buf); + if (q[0] && q[1] == T_ID && q[2] == intern("fn")) load(q); + else eval(q); + free(d*q); + } + return 0; +} diff --git a/docs/history/17-o3-boundaries/code/examples/selfpatch.zc b/docs/history/17-o3-boundaries/code/examples/selfpatch.zc new file mode 100644 index 0000000..074ccec --- /dev/null +++ b/docs/history/17-o3-boundaries/code/examples/selfpatch.zc @@ -0,0 +1,17 @@ +// code is data: find the literal 'A' inside f's compiled words and rewrite it. +// on the jit the store lands on translated code, which gets dropped and +// retranslated on the next call. +fn f() { putc('A'); return 0; } +fn litw(v) { return v + v + 1; } // how the vm encodes a literal push +fn main() +{ + let p = f; + f(); + while (*p != litw('A')) p++; + *p = litw('B'); + f(); + *p = litw('C'); + f(); + putc(10); + return 0; +} diff --git a/docs/history/17-o3-boundaries/code/examples/staged.zc b/docs/history/17-o3-boundaries/code/examples/staged.zc new file mode 100644 index 0000000..5f6342e --- /dev/null +++ b/docs/history/17-o3-boundaries/code/examples/staged.zc @@ -0,0 +1,35 @@ +// staged code: a program writes a specialized function as source, compiles it +// while running, and calls it. here, polynomials with their coefficients baked +// in as constants, so there's no loop and no array left at runtime. +let buf[2000], n; +fn put(s) { let i = 0; while (s[i]) { buf[n] = s[i]; n++; i++; } return 0; } +fn putnum(int v) { if (v >= 10) putnum(v / 10); buf[n] = '0' + v % 10; n++; return 0; } + +// coefficients c[0] + c[1] x + ... in horner form: ((c3 x + c2) x + c1) x + c0 +fn specialize(*int c, int deg) +{ + n = 0; + put("fn poly(x) { return "); + for (int i = 0; i < deg; i++) put("("); + if (c[deg] < 0) { put("0 - "); putnum(0 - c[deg]); } else putnum(c[deg]); + for (int i = deg - 1; i >= 0; i--) { + put(") * x "); + if (c[i] < 0) { put("- "); putnum(0 - c[i]); } else { put("+ "); putnum(c[i]); } + } + put("; }"); + buf[n] = 0; + return buf; +} +fn main() +{ + [4]int c; + c[0] = 7; c[1] = 0 - 3; c[2] = 0; c[3] = 2; // 2x^3 - 3x + 7 + let src = specialize(c, 3), q = tokens(src); + puts(src); putc(10); + load(q); // compiled now, into the running program + free(d*q); + let f = sval[lookups("poly")]; // and found by name in the compiler's symbol table + for (int x = 0 - 2; x <= 3; x++) { putn(f(x)); putc(' '); } + putc(10); + return 0; +} diff --git a/docs/history/17-o3-boundaries/code/examples/tree.zc b/docs/history/17-o3-boundaries/code/examples/tree.zc new file mode 100644 index 0000000..90a01d4 --- /dev/null +++ b/docs/history/17-o3-boundaries/code/examples/tree.zc @@ -0,0 +1,28 @@ +// a binary search tree where every node owns its children. there's no free +// anywhere: when the root's block ends, the whole tree goes with it. +struct node { int v; *node left, right; }; + +*node.u leaf(int v) { *node n = new(node); n.v = v; return u*n; } +// n is borrowed; storing a new leaf into it makes n the leaf's owner +fn insert(*node.m n, int v) +{ + if (v < n.v) { if (n.left) insert(n.left, v); else n.left = leaf(v); } + else { if (n.right) insert(n.right, v); else n.right = leaf(v); } + return 0; +} +fn walk(*node n) { if (!n) return 0; walk(n.left); putn(n.v); putc(' '); walk(n.right); return 0; } +int height(*node n) { if (!n) return 0; int l = height(n.left), r = height(n.right); return 1 + (l > r ? l : r); } + +fn main() +{ + let before = live_objects(); + { + *node root = leaf(50); + int x = 7; + for (int i = 0; i < 30; i++) { x = (x * 37 + 11) % 100; insert(root, x); } + walk(root); putc(10); + puts("height "); putn(height(root)); puts(", live objects "); putn(live_objects() - before); putc(10); + } + puts("after the block: "); putn(live_objects() - before); putc(10); + return 0; +} diff --git a/docs/history/17-o3-boundaries/code/examples/typed.zc b/docs/history/17-o3-boundaries/code/examples/typed.zc new file mode 100644 index 0000000..3bba399 --- /dev/null +++ b/docs/history/17-o3-boundaries/code/examples/typed.zc @@ -0,0 +1,44 @@ +// types: structs, a generic container, and looking at it all through reflection +typecheck on; +struct point { int x, y; }; +struct vec { int len, cap; *T items; }; + +*vec.u mkvec(int cap) { + *vec v = new(vec); + v.cap = cap; + v.items = new(T, cap); + return u*v; +} +// x is given away (.d): the vector's memory holds it from now on, and the +// collector looks after it +bool push(*vec v, T.d x) { + if (v.len == v.cap) { free(d*x); return 0; } + v.items[v.len] = d*x; + v.len++; + return 1; +} +fn show(word t) +{ + let i = 0; + tprint(t); puts(" is "); putn(tsize(t)); puts(" words: "); + while (i < tnfields(t)) { if (i) puts(", "); puts(tfname(t, i)); puts(" "); tprint(tftype(t, i)); i++; } + putc(10); + return 0; +} +int main() { + *vec<*point> v = mkvec<*point>(4); + int i = 0; + while (i < 3) { + *point p = new(point); + p.x = i; p.y = i * i; + push(v, d*p); + i++; + } + puts("last: "); putn(v.items[2].y); putc(10); + show(typeof(point)); + show(typeof(*v)); + puts("push is "); tprint(sigof(push)); putc(10); + // generic code is compiled once, so what mkvec stamped is vec + puts("the heap says v is a "); tprint(typeof_obj(v)); puts(", and v.items[0] is a "); tprint(typeof_obj(v.items[0])); putc(10); + return 0; +} diff --git a/docs/history/17-o3-boundaries/code/examples/wiring.zc b/docs/history/17-o3-boundaries/code/examples/wiring.zc new file mode 100644 index 0000000..d5e64f4 --- /dev/null +++ b/docs/history/17-o3-boundaries/code/examples/wiring.zc @@ -0,0 +1,19 @@ +// virtual enhancements: custom opcodes wired into the cpu while compiling. +// the recipes run through the core's optimizer; the jit inlines the result. +enhance popcount(x) micro + "r1 = shr x 1; r1 = and r1 0x55555555; x = sub x r1" + "; r1 = shr x 2; r1 = and r1 0x33333333; x = and x 0x33333333; x = add x r1" + "; r1 = shr x 4; x = add x r1; x = and x 0x0f0f0f0f; x = mul x 0x01010101; x = shr x 24"; +enhance div10(x) micro "x = udiv x 10"; // becomes a multiply-high, no divide +enhance hypot2(a, b) vm { 0 $ * ~ 0 $ * + }; // lifted from vm code +enhance 2 minmax(a, b) micro "r2 = min a b; r1 = max a b; r0 = r2"; +let lo, hi; +fn main() +{ + putn(popcount(0xff00ff)); putc(' '); + putn(div10(12345)); putc(' '); + putn(hypot2(3, 4)); putc(' '); + asm { 9 4 minmax hi ! lo ! } + putn(lo); putc('/'); putn(hi); putc(10); + return 0; +} diff --git a/docs/history/17-o3-boundaries/code/genome.h b/docs/history/17-o3-boundaries/code/genome.h new file mode 100644 index 0000000..46aa7aa --- /dev/null +++ b/docs/history/17-o3-boundaries/code/genome.h @@ -0,0 +1,5262 @@ +#define GORG 786432 +static const int genome[] = { +1966081,1310721,4,1728715,14,864582,870550,1310721,2,3,28,1310721,4,1310721,2,1835009, +40,1572913,16,1966081,1310721,4,1579223,12,1310721,2,4,1310721,2,2,42,1572933, +14,1,1310721,2,3,26,1310721,4,18,0,0,2,1,786486,0,786480, +786482,786484,97,0,98,0,97,32,61,32,115,100,105,118,32,97, +32,98,0,1,0,2,1,786512,0,786506,786508,786510,97,0,98,0, +97,32,61,32,115,114,101,109,32,97,32,98,0,2,0,2, +1,786538,0,786532,786534,786536,97,0,98,0,97,32,61,32,115,104, +108,32,97,32,98,0,3,0,1,1,786560,0,786557,786558,107,0, +114,49,32,61,32,108,100,32,54,53,53,51,54,48,59,32, +107,32,61,32,97,100,100,32,114,49,32,107,0,4,0,2, +1,786602,0,786596,786598,786600,105,0,110,0,105,32,61,32,108,116, +117,32,105,32,110,59,32,105,32,61,32,120,111,114,32,105, +32,49,0,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1573289,16,1966081, +1310721,4,1579223,12,1310721,2,4,1310721,2,2,24,1,1573311,14,1,1310721, +2,3,26,1310721,4,18,1310721,2,1,28,1310721,4,1310721,2,1835009,40, +1573359,16,1966081,1310721,4,1579223,12,20,1573367,14,1,1310721,2,1,26,1310721, +4,18,1310721,2,1,28,1310721,4,1310721,2,1835009,40,1573415,16,1966081,1310721, +4,1579223,12,22,1573423,14,1,1310721,2,1,26,1310721,4,18,1310721,2, +1,28,1310721,4,1310721,2,1835009,40,1573471,16,1966081,1310721,4,1579223,12,0, +1,1573481,14,1,1310721,2,1,26,1310721,4,18,1310721,2,5,28,1310721, +4,1310721,2,1835009,40,1573529,16,1966081,1310721,4,1579223,12,1310721,2,4,1, +1310721,2,3,26,4,1310721,2,2,1310721,2,3,26,2,26,2,1573635, +16,1310721,2,2,1310721,2,3,26,2,26,2,1573255,12,8,1310721,2, +3,26,1,6,2,1,6,3,26,5,6,4,10,8,8,1573547, +14,1,1573643,14,1,1310721,2,5,26,1310721,4,18,1310721,2,29,28, +1310721,4,1310721,2,1835009,40,1573691,16,1966081,1310721,4,1579223,12,1310721,2,4, +1,1310721,2,27,26,4,1310721,2,2,1,40,1573757,16,91,1573255,12, +8,1310721,2,1,1310721,2,2,28,10,3,6,10,4,8,1310721,2, +2,1,40,1573811,16,1573797,14,50,49,52,55,52,56,51,54,52, +56,0,1573775,1573495,12,8,1,1574025,14,3,1573949,16,1310721,2,3,26, +1310721,2,27,26,1,6,2,1,6,3,26,5,6,4,10,8, +26,97,1310721,2,2,21,46,26,10,3,6,10,4,8,1310721,2, +1310721,2,2,21,44,10,3,6,10,4,8,1310721,2,2,1,6, +1,10,28,34,63,38,3,26,1573945,16,1573949,14,1573811,14,1310721,2, +27,26,2,1574017,16,1310721,2,3,26,1310721,2,27,26,1,6,2, +-1,26,1,6,5,6,4,10,8,26,2,1573255,12,8,1573949,14, +1,1574025,14,1,1310721,2,29,26,1310721,4,18,1310721,2,5,28,1310721, +4,1310721,2,1835009,40,1574073,16,1966081,1310721,4,1579223,12,1310721,2,4,1, +1310721,2,3,26,4,1310721,2,2,1310721,2,3,26,2,26,2,1574153, +16,1310721,2,3,26,1,6,2,1,6,3,26,5,6,4,10, +8,8,1574091,14,1310721,2,3,26,2,1574169,14,1,1310721,2,5,26, +1310721,4,18,1310721,2,7,28,1310721,4,1310721,2,1835009,40,1574217,16,1966081, +1310721,4,1579223,12,1310721,2,3,26,4,1310721,2,4,1,1310721,2,5, +26,4,1310721,2,2,1310721,2,5,26,2,26,2,1574361,16,1310721,2, +2,1310721,2,5,26,2,26,2,1310721,2,3,26,2,1310721,2,5, +26,2,26,2,28,1,6,1,10,28,34,63,38,3,26,1, +6,1,10,28,34,63,38,1,10,28,1574363,14,1,1574405,16,1310721, +2,5,26,1,6,2,1,6,3,26,5,6,4,10,8,8, +1574245,14,1310721,2,2,1310721,2,5,26,2,26,2,1310721,2,3,26, +2,1310721,2,5,26,2,26,2,28,1,6,1,10,28,34,63, +38,3,26,1574477,14,1,1310721,2,7,26,1310721,4,18,1310721,2,9, +28,1310721,4,1310721,2,1835009,40,1574525,16,1966081,1310721,4,1579223,12,1310721,2, +5,26,4,1310721,2,3,26,4,1310721,2,4,1,1310721,2,7,26, +4,1310721,2,7,26,2,1310721,2,5,26,2,40,1574681,16,1310721,2, +2,1310721,2,7,26,2,26,1310721,2,3,26,2,1310721,2,7,26, +2,26,2,10,3,6,10,4,8,1310721,2,7,26,1,6,2, +1,6,3,26,5,6,4,10,8,8,1574563,14,1,1574689,14,1, +1310721,2,9,26,1310721,4,18,1310721,2,9,28,1310721,4,1310721,2,1835009, +40,1574737,16,1966081,1310721,4,1579223,12,1310721,2,5,26,4,1310721,2,3, +26,4,1310721,2,4,1,1310721,2,7,26,4,1310721,2,7,26,2, +1310721,2,5,26,2,40,1574879,16,1310721,2,2,1310721,2,7,26,2, +26,1310721,2,3,26,2,10,3,6,10,4,8,1310721,2,7,26, +1,6,2,1,6,3,26,5,6,4,10,8,8,1574775,14,1, +1574887,14,1,1310721,2,9,26,1310721,4,18,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,1310721,2,11,28,1310721, +4,1310721,2,1835009,40,1575001,16,1966081,1310721,4,1579223,12,1310721,2,3,26, +4,1310721,2,4,1,1310721,2,5,26,4,1,1310721,2,7,26,4, +61,1310721,2,9,26,4,1310721,2,2,1310721,2,3,26,2,40,1575101, +16,1574965,1310721,2,2,10,3,6,10,4,8,1,1575415,14,1310721,2, +9,26,2,1,40,3,36,1575375,16,1310721,2,7,26,1310721,2,7, +26,2,1310721,2,7,26,2,26,1310721,2,2,1310721,2,9,26,2, +38,3,32,26,10,3,6,10,4,8,1310721,2,5,26,1310721,2, +5,26,2,1310721,2,5,26,2,26,10,3,6,10,4,8,1310721, +2,7,26,2,1310721,2,3,26,2,40,3,36,1575337,16,1310721,2, +7,26,1310721,2,7,26,2,1310721,2,3,26,2,28,10,3,6, +10,4,8,1310721,2,5,26,1,6,2,1,6,3,26,5,6, +4,10,8,8,1310721,2,9,26,1,6,2,1,6,-1,26,5, +6,4,10,8,8,1575101,14,1574965,1310721,2,7,26,2,10,3,6, +10,4,8,1310721,2,5,26,2,1575415,14,1,1310721,2,11,26,1310721, +4,18,1310721,2,9,28,1310721,4,1310721,2,1835009,40,1575463,16,1966081,1310721, +4,1579223,12,1310721,2,3,26,4,1310721,2,4,1,1310721,2,5,26, +4,1310721,2,3,26,2,1,28,1,6,1,10,28,34,63,38, +3,26,1575555,16,1574965,1310721,2,2,10,3,6,10,4,8,1,1575791, +14,1310721,2,2,1,40,1575617,16,1310721,2,1,1310721,2,2,28,10, +3,6,10,4,8,1310721,2,5,26,3,10,3,6,10,4,8, +1310721,2,3,26,2,1,40,1575703,16,1310721,2,3,26,1,1310721,2, +3,26,2,28,10,3,6,10,4,8,1310721,2,5,26,1310721,2, +5,26,2,3,36,10,3,6,10,4,8,1310721,2,7,26,1310721, +2,2,1310721,2,3,26,2,1574967,12,10,3,6,10,4,8,1310721, +2,5,26,2,1575775,16,1,1310721,2,7,26,2,28,1575791,14,1310721, +2,7,26,2,1575791,14,1,1310721,2,9,26,1310721,4,18,1310721,2, +7,28,1310721,4,1310721,2,1835009,40,1575839,16,1966081,1310721,4,1579223,12,1310721, +2,3,26,4,1310721,2,4,1310721,2,2,1,40,1310721,2,5,26, +4,1310721,2,3,26,2,1,28,1,6,1,10,28,34,63,38, +3,26,1575923,16,1310721,2,2,1575981,14,1310721,2,2,1310721,2,3,26, +2,1575429,12,8,1310721,2,5,26,2,1575971,16,1,1574965,2,28,1575981, +14,1574965,2,1575981,14,1,1310721,2,7,26,1310721,4,18,1310721,2,5, +28,1310721,4,1310721,2,1835009,40,1576029,16,1966081,1310721,4,1579223,12,1310721,2, +3,26,4,1310721,2,4,1310721,2,2,1574901,1310721,2,3,26,2,63, +32,26,2,30,1576079,14,1,1310721,2,5,26,1310721,4,18,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,1,1310721,2,9,28,1310721,4,1310721,2,1835009,40,1576199,16,1966081,1310721, +4,1579223,12,1310721,2,4,1310721,524289,28,1310721,2,3,26,4,1,1310721, +2,5,26,4,1310721,2,3,26,2,1576495,16,1310721,2,7,26,3, +10,3,6,10,4,8,1310721,2,7,26,2,1310721,2,7,26,2, +26,1310721,2,3,26,2,10,40,3,36,1576359,16,1310721,2,7,26, +1310721,2,7,26,2,1310721,2,7,26,2,26,10,3,6,10,4, +8,1576269,14,1310721,2,2,1310721,2,5,26,2,1310721,2,7,26,2, +26,40,1576407,16,1310721,2,7,26,2,1576503,14,1310721,2,5,26,1310721, +2,5,26,2,1310721,2,7,26,2,26,10,3,6,10,4,8, +1310721,2,3,26,1310721,2,3,26,2,1310721,2,7,26,2,28,10, +3,6,10,4,8,1576233,14,1,1576503,14,1,1310721,2,9,26,1310721, +4,18,1310721,2,5,28,1310721,4,1310721,2,1835009,40,1576551,16,1966081,1310721, +4,1579223,12,1310721,2,3,26,4,1310721,2,4,1310721,2,2,5,26, +1,10,3,6,10,4,8,1310721,2,2,13,26,1576095,1310721,2,3, +26,2,26,2,10,3,6,10,4,8,1310721,2,2,15,26,1, +10,3,6,10,4,8,1576095,1310721,2,3,26,2,26,2,1576711,16, +1576095,1310721,2,3,26,2,26,2,15,26,1310721,2,2,10,3,6, +10,4,8,1576095,1310721,2,3,26,2,26,1310721,2,2,10,3,6, +10,4,8,1,1576751,14,1,1310721,2,5,26,1310721,4,18,1310721,2, +5,28,1310721,4,1310721,2,1835009,40,1576799,16,1966081,1310721,4,1579223,12,1310721, +2,3,26,4,1310721,2,4,1310721,2,2,15,26,2,1576875,16,1310721, +2,2,15,26,2,13,26,1310721,2,2,13,26,2,10,3,6, +10,4,8,1576913,14,1576095,1310721,2,3,26,2,26,1310721,2,2,13, +26,2,10,3,6,10,4,8,1310721,2,2,13,26,2,1576969,16, +1310721,2,2,13,26,2,15,26,1310721,2,2,15,26,2,10,3, +6,10,4,8,1,1576977,14,1,1310721,2,5,26,1310721,4,18,1310721, +2,5,28,1310721,4,1310721,2,1835009,40,1577025,16,1966081,1310721,4,1579223,12, +1310721,2,3,26,4,1310721,2,4,1310721,2,2,1,26,1574901,1310721,2, +3,26,2,26,2,10,3,6,10,4,8,1310721,2,2,3,26, +1310721,2,3,26,2,10,3,6,10,4,8,1310721,2,2,5,26, +1,10,3,6,10,4,8,1310721,2,2,7,26,1,10,3,6, +10,4,8,1310721,2,2,9,26,1,10,3,6,10,4,8,1310721, +2,2,11,26,1,10,3,6,10,4,8,1310721,2,2,1577219,14, +1,1310721,2,5,26,1310721,4,18,1310721,2,9,28,1310721,4,1310721,2, +1835009,40,1577267,16,1966081,1310721,4,1579223,12,3,1310721,2,4,524289,1310721,2, +3,26,4,1574901,1,26,3,10,3,6,10,4,8,1310721,2,2, +65,40,1577411,16,1574901,1310721,2,2,26,1574901,1310721,2,2,3,28,26, +2,1574901,1310721,2,2,3,28,26,2,26,10,3,6,10,4,8, +1310721,2,1,6,2,1,6,3,26,5,6,4,10,8,8,1577307, +14,1576093,1310721,10,3,6,10,4,8,1576095,1,65,1574703,12,8,1310721, +2,3,26,2,1310721,40,1577673,16,1310721,2,5,26,1310721,2,3,26, +2,524289,28,1576165,12,10,3,6,10,4,8,1310721,2,7,26,1, +10,3,6,10,4,8,1574901,1310721,2,7,26,2,26,2,1310721,2, +5,26,2,40,1577587,16,1310721,2,7,26,1,6,2,1,6,3, +26,5,6,4,10,8,8,1577517,14,1310721,2,3,26,2,1310721,2, +7,26,2,1576991,12,1310721,2,7,26,2,1576517,12,8,1310721,2,3, +26,1310721,2,3,26,2,1310721,2,5,26,2,26,10,3,6,10, +4,8,1577439,14,1,1577681,14,1,1310721,2,9,26,1310721,4,18,1310721, +2,9,28,1310721,4,1310721,2,1835009,40,1577729,16,1966081,1310721,4,1579223,12, +1310721,2,4,1310721,2,2,1310721,2,3,26,4,1310721,2,3,26,2, +65,40,1577831,16,1576095,1310721,2,3,26,2,26,2,1,6,1,10, +28,34,63,38,3,26,1,6,1,10,28,34,63,38,1,10, +28,1577833,14,1,1577875,16,1310721,2,3,26,1,6,2,1,6,3, +26,5,6,4,10,8,8,1577751,14,1310721,2,3,26,2,65,40, +3,36,1577903,16,1,1578153,14,1310721,2,5,26,1576095,1310721,2,3,26, +2,26,2,10,3,6,10,4,8,1310721,2,5,26,2,1310721,2, +3,26,2,1576765,12,8,1310721,2,3,26,2,1310721,2,2,10,40, +1578115,16,1310721,2,3,26,1,6,2,1,6,-1,26,5,6,4, +10,8,8,1310721,2,7,26,1310721,2,5,26,2,1574901,1310721,2,3, +26,2,26,2,26,10,3,6,10,4,8,1310721,2,7,26,2, +1310721,2,3,26,2,1576991,12,1310721,2,3,26,2,1576517,12,8,1577965, +14,1310721,2,5,26,2,1310721,2,2,1576991,12,8,1310721,2,5,26, +2,1578153,14,1,1310721,2,9,26,1310721,4,18,0,1310721,2,11,28, +1310721,4,1310721,2,1835009,40,1578203,16,1966081,1310721,4,1579223,12,1310721,2,4, +1310721,2,2,3,26,2,1310721,2,3,26,4,1574901,1310721,2,3,26, +2,26,2,1310721,2,5,26,4,3,1578875,16,1310721,2,7,26,1310721, +2,2,524289,28,10,3,6,10,4,8,1310721,2,5,26,2,1310721, +2,7,26,2,1576165,12,40,3,36,1578331,16,1578875,14,1310721,2,9, +26,524289,1310721,2,7,26,2,1310721,2,5,26,2,36,26,10,3, +6,10,4,8,1310721,2,9,26,2,5,26,2,1,28,1,6, +1,10,28,34,63,38,1,10,28,1578429,16,3,1578501,14,1310721,2, +9,26,2,3,26,2,1310721,2,3,26,2,28,1,6,1,10, +28,34,63,38,1,10,28,1,6,1,10,28,34,63,38,1, +10,28,1578511,16,3,1578583,14,1310721,2,9,26,2,1,26,2,1310721, +2,5,26,2,28,1,6,1,10,28,34,63,38,1,10,28, +1,6,1,10,28,34,63,38,1,10,28,1578591,16,1578875,14,1310721, +2,9,26,2,1310721,2,3,26,2,1576765,12,8,1310721,2,9,26, +2,1310721,2,2,40,1578717,16,1310721,2,2,1,26,1,10,3,6, +10,4,8,1310721,2,2,7,26,1,10,3,6,10,4,8,1310721, +2,1310721,2,9,26,2,10,3,6,10,4,8,1578773,14,1310721,2, +9,26,2,1,26,1,10,3,6,10,4,8,1310721,2,9,26, +2,7,26,1,10,3,6,10,4,8,1310721,2,3,26,1,6, +2,1,6,3,26,5,6,4,10,8,8,1310721,2,5,26,1310721, +2,5,26,2,1310721,2,5,26,2,26,10,3,6,10,4,8, +1310721,2,2,1310721,2,3,26,2,1576991,12,8,1578257,14,1310721,2,2, +1310721,2,3,26,2,1576991,12,1310721,2,3,26,2,1576517,12,8,1578167, +1310721,2,2,1310721,2,5,26,2,26,10,3,6,10,4,8,1, +1578951,14,1,1310721,2,11,26,1310721,4,18,1310721,2,3,28,1310721,4, +1310721,2,1835009,40,1578999,16,1966081,1310721,4,1579223,12,1310721,2,4,1310721,2, +2,524289,13,26,40,3,36,1579065,16,1310721,2,2,1576093,2,40,1, +6,1,10,28,34,63,38,1,10,28,1579067,14,1,1579147,16,1310721, +2,2,7,1,10,28,26,2,1310721,2,2,1459366445,36,28,1,6, +1,10,28,34,63,38,3,26,1,6,1,10,28,34,63,38, +1,10,28,1579149,14,1,1579201,16,1310721,2,2,9,1,10,28,26, +2,3,32,1,6,1,10,28,34,63,38,1,10,28,1579203,14, +1,1579209,14,1,1310721,2,3,26,1310721,4,18,1310721,2,1,28,1310721, +4,1310721,2,1835009,40,1579257,16,1966081,1310721,4,1579223,12,1579313,14,122,121, +58,32,102,114,97,109,101,32,115,116,97,99,107,32,111,118, +101,114,102,108,111,119,10,0,1579261,1573495,12,8,1573437,12,8,1, +1579335,14,1,1310721,2,1,26,1310721,4,18,1310721,2,1,28,1310721,4, +1310721,2,1835009,40,1579383,16,1966081,1310721,4,1579223,12,1579435,14,122,99,58, +32,111,117,116,32,111,102,32,104,101,97,112,32,109,101,109, +111,114,121,10,0,1579387,1573495,12,8,1573437,12,8,1,1579457,14,1, +1310721,2,1,26,1310721,4,18,1310721,2,11,28,1310721,4,1310721,2,1835009, +40,1579505,16,1966081,1310721,4,1579223,12,1310721,2,4,1310721,2,2,13,26, +1310721,2,3,26,4,7,1310721,2,5,26,4,1310721,2,2,1,40, +1579579,16,1310721,2,3,26,13,10,3,6,10,4,8,1574901,1310721,2, +5,26,2,26,2,1310721,2,3,26,2,40,1579649,16,1310721,2,5, +26,1,6,2,1,6,3,26,5,6,4,10,8,8,1579579,14, +1310721,2,7,26,1310721,2,5,26,2,1577695,12,10,3,6,10,4, +8,1310721,2,7,26,2,1,6,1,10,28,34,63,38,3,26, +1579747,16,1576163,2,1,6,1,10,28,34,63,38,1,10,28,1579749, +14,1,1579793,16,1584157,12,8,1310721,2,7,26,1310721,2,5,26,2, +1577695,12,10,3,6,10,4,8,1310721,2,7,26,2,1,6,1, +10,28,34,63,38,3,26,1579833,16,1579349,12,8,1310721,2,9,26, +1310721,2,7,26,2,13,26,10,3,6,10,4,8,1310721,2,7, +26,2,5,26,3,10,3,6,10,4,8,1310721,2,7,26,2, +7,26,1310721,2,9,26,2,1459366445,36,10,3,6,10,4,8,1310721, +2,7,26,2,9,26,1,10,3,6,10,4,8,1310721,2,7, +26,2,11,26,1,1310721,2,2,28,3,28,10,3,6,10,4, +8,1310721,2,9,26,2,1580019,14,1,1310721,2,11,26,1310721,4,18, +1310721,2,7,28,1310721,4,1310721,2,1835009,40,1580067,16,1966081,1310721,4,1579223, +12,1310721,2,3,26,4,1310721,2,4,1310721,2,2,1579471,12,1310721,2, +5,26,4,1310721,2,5,26,2,3,1,10,28,26,1310721,2,3, +26,2,10,3,6,10,4,8,1310721,2,5,26,2,1,1310721,2, +5,26,2,13,1,10,28,26,2,13,28,1574703,12,8,1310721,2, +5,26,2,1580205,14,1,1310721,2,7,26,1310721,4,18,1310721,2,3, +28,1310721,4,1310721,2,1835009,40,1580253,16,1966081,1310721,4,1579223,12,1310721,2, +4,1310721,2,2,1,1580033,12,1580277,14,1,1310721,2,3,26,1310721,4, +18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1580325,16,1966081,1310721,4, +1579223,12,1310721,2,4,1310721,2,2,1578965,12,1,6,1,10,28,34, +63,38,3,26,1580371,16,1,1580399,14,1310721,2,2,13,1,10,28, +26,2,13,28,1580399,14,1,1310721,2,3,26,1310721,4,18,1310721,2, +3,28,1310721,4,1310721,2,1835009,40,1580447,16,1966081,1310721,4,1579223,12,1310721, +2,4,1310721,2,2,1578965,12,1,6,1,10,28,34,63,38,3, +26,1580493,16,3,1580537,14,1310721,2,2,3,1,10,28,26,2,1, +40,1,6,1,10,28,34,63,38,1,10,28,1580547,16,1,1580571, +14,1310721,2,2,3,1,10,28,26,2,1580571,14,1,1310721,2,3, +26,1310721,4,18,1310721,2,11,28,1310721,4,1310721,2,1835009,40,1580619,16, +1966081,1310721,4,1579223,12,1310721,2,4,1310721,2,2,13,28,1310721,2,3, +26,4,1310721,2,3,26,2,1,26,2,13,28,1310721,2,5,26, +4,1,1310721,2,7,26,4,1310721,2,3,26,2,5,26,5,10, +3,6,10,4,8,1310721,2,7,26,2,1310721,2,5,26,2,40, +1580935,16,1310721,2,9,26,1310721,2,2,1310721,2,7,26,2,26,2, +10,3,6,10,4,8,1310721,2,9,26,2,1578965,12,1580875,16,1310721, +2,9,26,2,5,1,10,28,26,2,1310721,2,2,28,1,6, +1,10,28,34,63,38,3,26,1,6,1,10,28,34,63,38, +1,10,28,1580877,14,1,1580897,16,1310721,2,9,26,2,1580585,12,8, +1310721,2,7,26,1,6,2,1,6,3,26,5,6,4,10,8, +8,1580715,14,1310721,2,3,26,2,1578169,12,8,1,1580959,14,1,1310721, +2,11,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40, +1581007,16,1966081,1310721,4,1579223,12,1310721,2,4,1310721,2,2,1578965,12,1581039, +16,1310721,2,2,1580585,12,8,1,1581047,14,1,1310721,2,3,26,1310721, +4,18,1310721,2,5,28,1310721,4,1310721,2,1835009,40,1581095,16,1966081,1310721, +4,1579223,12,1310721,2,3,26,4,1310721,2,4,1310721,2,2,1578965,12, +1581163,16,1310721,2,2,5,1,10,28,26,1310721,2,3,26,2,10, +3,6,10,4,8,1310721,2,2,1581175,14,1,1310721,2,5,26,1310721, +4,18,1310721,2,13,28,1310721,4,1310721,2,1835009,40,1581223,16,1966081,1310721, +4,1579223,12,1310721,2,5,26,4,1310721,2,3,26,4,1310721,2,4, +9,1310721,2,5,26,2,1580033,12,1310721,2,7,26,4,1310721,2,3, +26,2,1310721,2,9,26,4,1310721,2,9,26,2,9,40,1581335,16, +1310721,2,9,26,9,10,3,6,10,4,8,1310721,2,11,26,1310721, +2,9,26,2,1310721,2,2,30,1580219,12,10,3,6,10,4,8, +1310721,2,7,26,2,1,26,1310721,2,3,26,2,10,3,6,10, +4,8,1310721,2,7,26,2,3,26,1310721,2,9,26,2,10,3, +6,10,4,8,1310721,2,7,26,2,5,26,1310721,2,11,26,2, +1310721,2,7,26,2,1581061,12,10,3,6,10,4,8,1310721,2,7, +26,2,7,26,1310721,2,2,10,3,6,10,4,8,1310721,2,7, +26,2,1581547,14,1,1310721,2,13,26,1310721,4,18,1310721,2,17,28, +1310721,4,1310721,2,1835009,40,1581595,16,1966081,1310721,4,1579223,12,1310721,2,3, +26,4,1310721,2,4,1310721,2,2,3,26,2,1310721,2,5,26,4, +1310721,2,2,5,26,2,1310721,2,9,26,4,1,1310721,2,11,26, +4,1310721,2,3,26,2,1310721,2,5,26,2,10,40,3,36,1581705, +16,1,1582259,14,1310721,2,5,26,2,1310721,2,3,26,2,40,1581777, +16,1310721,2,5,26,1310721,2,5,26,2,1310721,2,5,26,2,26, +10,3,6,10,4,8,1581705,14,1310721,2,7,26,1310721,2,5,26, +2,1310721,2,2,7,26,2,30,1580219,12,10,3,6,10,4,8, +1310721,2,15,26,1310721,2,2,1,26,2,1310721,2,2,7,26,2, +30,10,3,6,10,4,8,1310721,2,7,26,2,1310721,2,9,26, +2,1310721,2,15,26,2,1574491,12,8,1310721,2,11,26,2,1310721,2, +15,26,2,40,1582161,16,1310721,2,13,26,1310721,2,7,26,2,1310721, +2,11,26,2,26,2,10,3,6,10,4,8,1310721,2,13,26, +2,1578965,12,1582075,16,1310721,2,13,26,2,5,1,10,28,26,2, +1310721,2,9,26,2,28,1,6,1,10,28,34,63,38,3,26, +1,6,1,10,28,34,63,38,1,10,28,1582077,14,1,1582123,16, +1310721,2,13,26,2,5,1,10,28,26,1310721,2,7,26,2,10, +3,6,10,4,8,1310721,2,11,26,1,6,2,1,6,3,26, +5,6,4,10,8,8,1581907,14,1310721,2,2,5,26,1310721,2,7, +26,2,1310721,2,2,1581061,12,10,3,6,10,4,8,1310721,2,2, +3,26,1310721,2,5,26,2,10,3,6,10,4,8,1310721,2,9, +26,2,1580973,12,8,1,1582259,14,1,1310721,2,17,26,1310721,4,18, +1310721,2,5,28,1310721,4,1310721,2,1835009,40,1582307,16,1966081,1310721,4,1579223, +12,1310721,2,3,26,4,1310721,2,4,1582349,14,122,121,58,32,105, +110,100,101,120,32,0,1582327,1573495,12,8,1310721,2,2,1573657,12,8, +1582419,14,32,111,117,116,32,111,102,32,114,97,110,103,101,32, +40,108,101,110,103,116,104,32,0,1582373,1573495,12,8,1310721,2,3, +26,2,1573657,12,8,1582453,14,41,10,0,1582447,1573495,12,8,1573437,12, +8,1,1582475,14,1,1310721,2,5,26,1310721,4,18,1310721,2,5,28, +1310721,4,1310721,2,1835009,40,1582523,16,1966081,1310721,4,1579223,12,1310721,2,3, +26,4,1310721,2,4,1310721,2,3,26,2,1,40,1582563,16,3,1582613, +14,1310721,2,3,26,2,1310721,2,2,1,26,2,40,3,36,1, +6,1,10,28,34,63,38,1,10,28,1582645,16,1310721,2,3,26, +2,1310721,2,2,1,26,2,1582273,12,8,1310721,2,2,5,26,2, +1310721,2,3,26,2,1310721,2,2,7,26,2,30,26,1582689,14,1, +1310721,2,5,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,1835009, +40,1582737,16,1966081,1310721,4,1579223,12,1310721,2,3,26,4,1310721,2,4, +1310721,2,2,1310721,2,2,1,26,2,3,26,1581561,12,8,1310721,2, +2,5,26,2,1310721,2,2,1,26,2,26,1310721,2,3,26,2, +10,3,6,10,4,8,1310721,2,3,26,2,1310721,2,2,5,26, +2,1581061,12,8,1310721,2,2,1,26,1310721,2,2,1,26,2,3, +26,10,3,6,10,4,8,1,1582903,14,1,1310721,2,5,26,1310721, +4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1582951,16,1966081,1310721, +4,1579223,12,1310721,2,4,1310721,2,2,1,26,2,1,6,1,10, +28,34,63,38,3,26,1583009,16,3,1,10,28,1,1582273,12,8, +1310721,2,2,1,26,1310721,2,2,1,26,2,3,28,10,3,6, +10,4,8,1310721,2,2,5,26,2,1310721,2,2,1,26,2,26, +2,1583081,14,1,1310721,2,3,26,1310721,4,18,1310721,2,7,28,1310721, +4,1310721,2,1835009,40,1583129,16,1966081,1310721,4,1579223,12,1310721,2,3,26, +4,1310721,2,4,1310721,2,2,1,26,2,1310721,2,2,7,26,2, +30,1310721,2,5,26,4,1310721,2,3,26,2,1,40,1583221,16,1310721, +2,3,26,1,10,3,6,10,4,8,1310721,2,2,1310721,2,3, +26,2,1581561,12,8,1310721,2,3,26,2,1310721,2,2,7,26,2, +30,1310721,2,5,26,2,10,40,1583353,16,1310721,2,2,5,26,2, +1310721,2,5,26,2,26,1,1310721,2,3,26,2,1310721,2,2,7, +26,2,30,1310721,2,5,26,2,28,1574703,12,8,1310721,2,2,1, +26,1310721,2,3,26,2,10,3,6,10,4,8,1,1583393,14,1, +1310721,2,7,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009, +40,1583441,16,1966081,1310721,4,1579223,12,1310721,2,4,1310721,2,2,1,26, +2,1583465,14,1,1310721,2,3,26,1310721,4,18,1310721,2,3,28,1310721, +4,1310721,2,1835009,40,1583513,16,1966081,1310721,4,1579223,12,1310721,2,4,1310721, +2,2,3,26,2,1583537,14,1,1310721,2,3,26,1310721,4,18,0, +0,0,0,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1583593,16,1966081, +1310721,4,1579223,12,1310721,2,4,1310721,2,2,1578965,12,1,6,1,10, +28,34,63,38,3,26,1583639,16,3,1583683,14,1310721,2,2,9,1, +10,28,26,2,5,32,1,6,1,10,28,34,63,38,1,10, +28,1583693,16,1,1583837,14,1310721,2,2,9,1,10,28,26,1310721,2, +2,9,1,10,28,26,2,5,34,10,3,6,10,4,8,1583551, +2,32769,40,1583813,16,2031617,1583551,2,26,1310721,2,2,10,3,6,10, +4,8,1583551,1,6,2,1,6,3,26,5,6,4,10,8,8, +1583829,14,1583553,3,10,3,6,10,4,8,1,1583837,14,1,1310721,2, +3,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,1835009,40,1583885, +16,1966081,1310721,4,1579223,12,1310721,2,3,26,4,1310721,2,4,1310721,2, +2,1310721,2,3,26,2,40,1583971,16,1310721,2,2,2,1583559,12,8, +1310721,2,1,6,2,1,6,3,26,5,6,4,10,8,8,1583901, +14,1,1583979,14,1,1310721,2,5,26,1310721,4,18,1310721,2,3,28, +1310721,4,1310721,2,1835009,40,1584027,16,1966081,1310721,4,1579223,12,1583551,2,1584135, +16,1583551,1,6,2,1,6,-1,26,5,6,4,10,8,8,1310721, +2,2031617,1583551,2,26,2,10,3,6,10,4,8,1310721,2,2,1310721, +2,2,1310721,2,2,13,1,10,28,26,2,26,13,28,1583851,12, +8,1584027,14,1,1584143,14,1,1310721,2,3,26,1310721,4,18,1310721,2, +7,28,1310721,4,1310721,2,1835009,40,1584191,16,1966081,1310721,4,1579223,12,1, +1310721,2,5,26,4,1583551,1,10,3,6,10,4,8,1583553,1,10, +3,6,10,4,8,1725417,12,8,1583993,12,8,1583553,2,1584429,16,1583553, +1,10,3,6,10,4,8,1310721,2,524289,10,3,6,10,4,8, +1310721,2,2,1310721,40,1584425,16,1310721,2,2,5,26,2,7,28,1, +6,1,10,28,34,63,38,3,26,1584379,16,1310721,2,2,13,26, +1310721,2,2,1310721,2,2,1,26,2,26,1583851,12,8,1310721,2,1310721, +2,2,1310721,2,2,1,26,2,26,10,3,6,10,4,8,1583993, +12,8,1584289,14,1584247,14,1310721,2,524289,10,3,6,10,4,8,1310721, +2,2,1310721,40,1584705,16,1310721,2,3,26,1310721,2,2,1,26,2, +10,3,6,10,4,8,1310721,2,2,5,26,2,3,28,1,6, +1,10,28,34,63,38,3,26,1584603,16,1310721,2,2,1578169,12,8, +1310721,2,5,26,1,6,2,1,6,3,26,5,6,4,10,8, +8,1310721,2,1578167,2,10,3,6,10,4,8,1584701,14,1310721,2,2, +5,26,2,7,28,1,6,1,10,28,34,63,38,3,26,1584667, +16,1310721,2,2,5,26,3,10,3,6,10,4,8,1310721,2,1310721, +2,2,1310721,2,3,26,2,26,10,3,6,10,4,8,1584447,14, +1576159,1,6,2,1,6,3,26,5,6,4,10,8,8,1576161,1576161, +2,1310721,2,5,26,2,26,10,3,6,10,4,8,1310721,2,5, +26,2,1584779,14,1,1310721,2,7,26,1310721,4,18,1310721,2,5,28, +1310721,4,1310721,2,1835009,40,1584827,16,1966081,1310721,4,1579223,12,524289,1310721,2, +4,1,1310721,2,3,26,4,1310721,2,2,1310721,40,1584955,16,1310721,2, +2,5,26,2,3,32,1584915,16,1310721,2,3,26,1,6,2,1, +6,3,26,5,6,4,10,8,8,1310721,2,1310721,2,2,1310721,2, +2,1,26,2,26,10,3,6,10,4,8,1584847,14,1310721,2,3, +26,2,1584971,14,1,1310721,2,5,26,1310721,4,18,1310721,2,3,28, +1310721,4,1310721,2,1835009,40,1585019,16,1966081,1310721,4,1579223,12,63,1310721,2, +4,1310721,2,2,1,10,40,1585101,16,1576095,1310721,2,2,26,2,1, +6,1,10,28,34,63,38,3,26,1,6,1,10,28,34,63, +38,1,10,28,1585103,14,1,1585141,16,1310721,2,1,6,2,1,6, +-1,26,5,6,4,10,8,8,1585027,14,1576095,1310721,2,2,26,2, +1,6,1,10,28,34,63,38,3,26,1585183,16,1,1585201,14,1574901, +1310721,2,2,26,2,1585201,14,1,1310721,2,3,26,1310721,4,18,0, +0,0,0,0,0,0,0,0,0,0,0,792620,113,64,33,36, +95,126,99,106,122,114,44,63,46,43,45,42,38,124,94,62, +60,37,0,1,0,0,0,0,1,0,0,0,0,0,-1,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,1310721,2,1, +28,1310721,4,1310721,2,1835009,40,1590621,16,1966081,1310721,4,1579223,12,1590645,14, +122,99,58,32,108,105,110,101,32,0,1590625,1573495,12,8,1585295,2, +1573657,12,8,1590673,14,58,32,0,1590667,1573495,12,8,1590583,1,6,2, +1,6,3,26,5,6,4,10,8,8,1,1590717,14,1,1310721,2, +1,26,1310721,4,18,1310721,2,1,28,1310721,4,1310721,2,1835009,40,1590765, +16,1966081,1310721,4,1579223,12,21,1573255,12,8,1590583,2,41,40,3,36, +1590869,16,1590855,14,122,99,58,32,116,111,111,32,109,97,110,121, +32,101,114,114,111,114,115,44,32,115,116,111,112,112,105,110, +103,10,0,1590793,1573495,12,8,1573437,12,8,1,1590877,14,1,1310721,2, +1,26,1310721,4,18,1310721,2,1,28,1310721,4,1310721,2,1835009,40,1590925, +16,1966081,1310721,4,1579223,12,1590939,14,122,99,58,32,0,1590929,1573495,12, +8,1590583,2,1573657,12,8,1590583,2,3,28,1,6,1,10,28,34, +63,38,3,26,1591021,16,1591009,14,32,101,114,114,111,114,10,0, +1590993,1573495,12,8,1591051,14,1591043,14,32,101,114,114,111,114,115,10, +0,1591025,1573495,12,8,1573437,12,8,1,1591065,14,1,1310721,2,1,26, +1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1591113,16,1966081, +1310721,4,1579223,12,1310721,2,4,1590587,12,8,1310721,2,2,1573495,12,8, +21,1573255,12,8,1573437,12,8,1,1591159,14,1,1310721,2,3,26,1310721, +4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1591207,16,1966081,1310721, +4,1579223,12,1310721,2,4,1590585,2,1,6,1,10,28,34,63,38, +3,26,1591291,16,1587703,2,1,6,1,10,28,34,63,38,3,26, +1,6,1,10,28,34,63,38,1,10,28,1591293,14,1,1591321,16, +1590587,12,8,1310721,2,2,1573495,12,8,1590731,12,8,1,1591329,14,1, +1310721,2,3,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,1835009, +40,1591377,16,1966081,1310721,4,1579223,12,1310721,2,3,26,4,1310721,2,4, +1590585,2,1,6,1,10,28,34,63,38,3,26,1591471,16,1587703,2, +1,6,1,10,28,34,63,38,3,26,1,6,1,10,28,34, +63,38,1,10,28,1591473,14,1,1591535,16,1590587,12,8,1310721,2,2, +1573495,12,8,1591505,14,58,32,0,1591499,1573495,12,8,1310721,2,3,26, +2,1573495,12,8,1590731,12,8,1,1591543,14,1,1310721,2,5,26,1310721, +4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1591591,16,1966081,1310721, +4,1579223,12,1310721,2,4,1310721,2,2,1591173,12,8,1590585,3,10,3, +6,10,4,8,1585289,2,1,28,1,6,1,10,28,34,63,38, +3,26,1591663,16,1590891,12,8,1604999,12,8,1,1591677,14,1,1310721,2, +3,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,1835009,40,1591725, +16,1966081,1310721,4,1579223,12,1310721,2,3,26,4,1310721,2,4,1310721,2, +2,1310721,2,3,26,2,1591343,12,8,1590585,3,10,3,6,10,4, +8,1585289,2,1,28,1,6,1,10,28,34,63,38,3,26,1591817, +16,1590891,12,8,1604999,12,8,1,1591831,14,1,1310721,2,5,26,1310721, +4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1591879,16,1966081,1310721, +4,1579223,12,1310721,2,4,1587565,2,1591923,16,1587569,2,1,6,1,10, +28,34,63,38,1,10,28,1591925,14,1,1591941,16,1310721,2,2,1591173, +12,8,1,1591949,14,1,1310721,2,3,26,1310721,4,18,1310721,2,5, +28,1310721,4,1310721,2,1835009,40,1591997,16,1966081,1310721,4,1579223,12,1310721,2, +3,26,4,1310721,2,4,1587565,2,1592051,16,1587569,2,1,6,1,10, +28,34,63,38,1,10,28,1592053,14,1,1592085,16,1466881,1310721,2,2, +26,2,1310721,2,3,26,2,1591343,12,8,1,1592093,14,1,1310721,2, +5,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,1835009,40,1592141, +16,1966081,1310721,4,1579223,12,1310721,2,4,1,1310721,2,3,26,4,1310721, +2,2,2,1592249,16,1310721,2,3,26,1310721,2,3,26,2,63,30, +1310721,2,2,2,26,10,3,6,10,4,8,1310721,2,1,6,2, +1,6,3,26,5,6,4,10,8,8,1592159,14,1310721,2,3,26, +2,16385,3,28,32,1592273,14,1,1310721,2,5,26,1310721,4,18,1310721, +2,9,28,1310721,4,1310721,2,1835009,40,1592321,16,1966081,1310721,4,1579223,12, +1310721,2,4,1310721,2,2,1592107,12,1310721,2,3,26,4,3,1592511,16, +1310721,2,5,26,1311233,1310721,2,3,26,2,26,2,10,3,6,10, +4,8,1310721,2,5,26,2,1,6,1,10,28,34,63,38,3, +26,1592427,16,1592511,14,1310721,2,5,26,2,1310721,2,2,1574183,12,1592465, +16,1310721,2,5,26,2,1592791,14,1310721,2,3,26,1310721,2,3,26, +2,3,26,16385,3,28,32,10,3,6,10,4,8,1592347,14,1310721, +2,7,26,1310721,2,2,1574039,12,10,3,6,10,4,8,1585217,2, +1310721,2,7,26,2,26,5,26,1409025,40,3,36,1592619,16,1592611,14, +115,116,114,105,110,103,32,112,111,111,108,32,102,117,108,108, +0,1592577,1591079,12,8,1585217,2,1,26,1,10,3,6,10,4,8, +1310721,2,5,26,1585217,2,3,26,10,3,6,10,4,8,1310721,2, +5,26,2,1310721,2,2,1310721,2,7,26,2,3,26,1574491,12,8, +1585217,1585217,2,1310721,2,7,26,2,26,5,26,10,3,6,10,4, +8,1311233,1310721,2,3,26,2,26,1310721,2,5,26,2,10,3,6, +10,4,8,1310721,2,5,26,2,1592791,14,1,1310721,2,9,26,1310721, +4,18,1310721,2,9,28,1310721,4,1310721,2,1835009,40,1592839,16,1966081,1310721, +4,1579223,12,1310721,2,5,26,4,1310721,2,3,26,4,1310721,2,4, +1585219,2,1310721,2,7,26,4,1585219,2,8193,40,3,36,1592943,16,1592935, +14,115,121,109,98,111,108,32,116,97,98,108,101,32,102,117, +108,108,0,1592899,1591079,12,8,1585219,1,6,2,1,6,3,26,5, +6,4,10,8,8,1409537,1310721,2,7,26,2,26,1310721,2,2,10, +3,6,10,4,8,1417729,1310721,2,7,26,2,26,1310721,2,3,26, +2,10,3,6,10,4,8,1425921,1310721,2,7,26,2,26,1310721,2, +5,26,2,10,3,6,10,4,8,1434113,1310721,2,7,26,2,26, +1,10,3,6,10,4,8,1442305,1310721,2,7,26,2,26,3,10, +3,6,10,4,8,1450497,1310721,2,7,26,2,26,1,10,3,6, +10,4,8,1559041,1310721,2,7,26,2,26,1,10,3,6,10,4, +8,1458689,1310721,2,7,26,2,26,1310721,2,2,3,1,10,28,26, +2,10,3,6,10,4,8,1310721,2,2,3,1,10,28,26,1310721, +2,7,26,2,3,26,10,3,6,10,4,8,1310721,2,7,26, +2,1593289,14,1,1310721,2,9,26,1310721,4,18,1310721,2,5,28,1310721, +4,1310721,2,1835009,40,1593337,16,1966081,1310721,4,1579223,12,1310721,2,4,1310721, +2,2,3,1,10,28,26,2,3,28,1310721,2,3,26,4,1310721, +2,3,26,2,1,40,3,36,1593623,16,1310721,2,3,26,2,1585223, +2,40,3,36,1593427,16,3,1593489,14,1417729,1310721,2,3,26,2,26, +2,11,28,1,6,1,10,28,34,63,38,3,26,1,6,1, +10,28,34,63,38,1,10,28,1593499,16,3,1593561,14,1417729,1310721,2, +3,26,2,26,2,15,28,1,6,1,10,28,34,63,38,3, +26,1,6,1,10,28,34,63,38,1,10,28,1593579,16,1310721,2, +3,26,2,1593637,14,1310721,2,3,26,1458689,1310721,2,3,26,2,26, +2,3,28,10,3,6,10,4,8,1593375,14,3,1,10,28,1593637, +14,1,1310721,2,5,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721, +2,1835009,40,1593685,16,1966081,1310721,4,1579223,12,1310721,2,4,1585219,2,1310721, +2,2,10,40,1593783,16,1585219,1,6,2,1,6,-1,26,5,6, +4,10,8,8,1409537,1585219,2,26,2,3,1,10,28,26,1458689,1585219, +2,26,2,10,3,6,10,4,8,1593691,14,1,1593791,14,1,1310721, +2,3,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40, +1593839,16,1966081,1310721,4,1579223,12,1310721,2,4,1310721,2,2,1592287,12,1593303, +12,1593865,14,1,1310721,2,3,26,1310721,4,18,1310721,2,7,28,1310721, +4,1310721,2,1835009,40,1593913,16,1966081,1310721,4,1579223,12,1310721,2,3,26, +4,1310721,2,4,1585227,2,1310721,2,5,26,4,1310721,2,5,26,2, +1593991,16,1585227,1491457,1310721,2,5,26,2,26,2,10,3,6,10,4, +8,1594133,14,1585225,1,6,2,1,6,3,26,5,6,4,10,8, +8,1310721,2,5,26,1585225,2,10,3,6,10,4,8,1310721,2,5, +26,2,16385,40,3,36,1594133,16,1594125,14,116,111,111,32,109,97, +110,121,32,102,111,114,119,97,114,100,32,114,101,102,101,114, +101,110,99,101,115,0,1594069,1591079,12,8,1475073,1310721,2,5,26,2, +26,1310721,2,2,10,3,6,10,4,8,1491457,1310721,2,5,26,2, +26,1310721,2,3,26,2,10,3,6,10,4,8,1310721,2,5,26, +2,1594217,14,1,1310721,2,7,26,1310721,4,18,1310721,2,7,28,1310721, +4,1310721,2,1835009,40,1594265,16,1966081,1310721,4,1579223,12,1310721,2,3,26, +4,1310721,2,4,1310721,2,2,1594427,16,1475073,1310721,2,2,26,2,1310721, +2,3,26,2,1594811,12,8,1310721,2,5,26,1491457,1310721,2,2,26, +2,10,3,6,10,4,8,1491457,1310721,2,2,26,1585227,2,10,3, +6,10,4,8,1585227,1310721,2,2,10,3,6,10,4,8,1310721,2, +1310721,2,5,26,2,10,3,6,10,4,8,1594281,14,1,1594435,14, +1,1310721,2,7,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2, +1835009,40,1594483,16,1966081,1310721,4,1579223,12,1310721,2,4,1587703,2,1594503,16, +1,1594629,14,1587549,2,1587553,2,40,3,36,1594565,16,1594557,14,112,114, +111,103,114,97,109,32,116,111,111,32,98,105,103,0,1594525,1591079, +12,8,1587549,2,1587551,2,26,1310721,2,2,10,3,6,10,4,8, +1587549,1,6,2,1,6,3,26,5,6,4,10,8,8,1,1594629, +14,1,1310721,2,3,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721, +2,1835009,40,1594677,16,1966081,1310721,4,1579223,12,1310721,2,4,1310721,2,2, +1310721,2,2,26,3,26,1594449,12,8,1,1594715,14,1,1310721,2,3, +26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1594763,16, +1966081,1310721,4,1579223,12,1310721,2,4,1310721,2,2,1310721,2,2,26,1594449, +12,8,1,1594797,14,1,1310721,2,3,26,1310721,4,18,1310721,2,5, +28,1310721,4,1310721,2,1835009,40,1594845,16,1966081,1310721,4,1579223,12,1310721,2, +3,26,4,1310721,2,4,1587703,2,1594875,16,1,1594933,14,1310721,2,2, +1587551,2,26,1310721,2,3,26,2,1310721,2,3,26,2,26,3,26, +10,3,6,10,4,8,1,1594933,14,1,1310721,2,5,26,1310721,4, +18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1594981,16,1966081,1310721,4, +1579223,12,1310721,2,4,1310721,2,2,1594643,12,8,15,1594729,12,8,1, +1595015,14,1,1310721,2,3,26,1310721,4,18,1310721,2,3,28,1310721,4, +1310721,2,1835009,40,1595063,16,1966081,1310721,4,1579223,12,1587549,2,1310721,2,4, +2097153,3,28,1594643,12,8,1310721,2,2,1595097,14,1,1310721,2,3,26, +1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,1835009,40,1595145,16,1966081, +1310721,4,1579223,12,1310721,2,4,1310721,2,2,3,1585295,2,1592805,12,1310721, +2,3,26,4,1442305,1310721,2,3,26,2,26,1,10,3,6,10, +4,8,1310721,2,3,26,2,1595221,14,1,1310721,2,5,26,1310721,4, +18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1595269,16,1966081,1310721,4, +1579223,12,1310721,2,4,1442305,1310721,2,2,26,2,3,32,1595319,16,1425921, +1310721,2,2,26,2,1594643,12,8,1,1595395,14,1587703,2,1595333,16,1, +1595395,14,1450497,1310721,2,2,26,1587549,2,1450497,1310721,2,2,26,2,1593879, +12,10,3,6,10,4,8,2097153,3,28,1594643,12,8,1,1595395,14, +1,1310721,2,3,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2, +1835009,40,1595443,16,1966081,1310721,4,1579223,12,1310721,2,4,1310721,2,2,1593303, +12,1310721,2,3,26,4,1310721,2,3,26,2,1,40,1595517,16,1310721, +2,3,26,1310721,2,2,1595111,12,10,3,6,10,4,8,1310721,2, +3,26,2,1595235,12,8,13,1594729,12,8,1,1595549,14,1,1310721,2, +5,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1595597, +16,1966081,1310721,4,1579223,12,1310721,2,4,1310721,1594643,12,8,3,1594729,12, +8,1310721,2,2,1595649,16,1310721,2,2,1594643,12,8,27,1594729,12,8, +1,1595657,14,1,1310721,2,3,26,1310721,4,18,1310721,2,5,28,1310721, +4,1310721,2,1835009,40,1595705,16,1966081,1310721,4,1579223,12,1310721,2,4,1558017, +1310721,2,2,26,2,33,32,1595903,16,1310721,2,3,26,1587761,2,1593303, +12,10,3,6,10,4,8,1310721,2,3,26,2,1,40,3,36, +1595847,16,1417729,1310721,2,3,26,2,26,2,13,28,1,6,1,10, +28,34,63,38,3,26,1,6,1,10,28,34,63,38,1,10, +28,1595849,14,1,1595903,16,1467905,1310721,2,2,26,2,1594643,12,8,45, +1425921,1310721,2,3,26,2,26,2,26,1594729,12,8,1,1595925,14,1467905, +1310721,2,2,26,2,1595563,12,1595925,14,1,1310721,2,5,26,1310721,4, +18,1310721,2,1,28,1310721,4,1310721,2,1835009,40,1595973,16,1966081,1310721,4, +1579223,12,1589483,1,10,3,6,10,4,8,1589485,3,10,3,6,10, +4,8,1587903,1,10,3,6,10,4,8,1,1596029,14,1,1310721,2, +1,26,1310721,4,18,1310721,2,1,28,1310721,4,1310721,2,1835009,40,1596077, +16,1966081,1310721,4,1579223,12,1589483,2,1596127,16,1589483,1,6,2,1,6, +-1,26,5,6,4,10,8,8,1589467,1589483,2,26,2,1596187,14,1589485, +2,17,40,1596179,16,1589485,1,6,2,1,6,3,26,5,6,4, +10,8,8,1589485,2,3,28,1596187,14,1,1596187,14,1,1310721,2,1, +26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,1835009,40,1596235,16, +1966081,1310721,4,1579223,12,1310721,2,4,1558017,1310721,2,2,26,1558017,1310721,2, +2,26,2,1558017,1310721,2,2,26,2,3585,32,28,10,3,6,10, +4,8,1587769,2,1,6,1,10,28,34,63,38,3,26,1596327,16, +3,1596361,14,1473025,1310721,2,2,26,2,1,6,1,10,28,34,63, +38,1,10,28,1596371,16,3,1596405,14,1587903,2,65,40,3,36,1, +6,1,10,28,34,63,38,1,10,28,1596415,16,1,1596627,14,1310721, +2,3,26,1596043,12,10,3,6,10,4,8,1310721,2,3,26,2, +1,6,1,10,28,34,63,38,3,26,1596479,16,1,1596627,14,1558017, +1310721,2,2,26,1558017,1310721,2,2,26,2,1310721,2,3,26,2,513, +30,26,10,3,6,10,4,8,1587775,1587903,2,26,1467905,1310721,2,2, +26,2,10,3,6,10,4,8,1587839,1587903,2,26,1310721,2,3,26, +2,10,3,6,10,4,8,1587903,1,6,2,1,6,3,26,5, +6,4,10,8,8,1,1596627,14,1,1310721,2,5,26,1310721,4,18, +1310721,2,5,28,1310721,4,1310721,2,1835009,40,1596675,16,1966081,1310721,4,1579223, +12,1310721,2,4,1587769,2,1,6,1,10,28,34,63,38,3,26, +1596715,16,3,1596781,14,1556993,1310721,2,2,26,2,1,26,2,23,28, +1,6,1,10,28,34,63,38,1,10,28,1,6,1,10,28, +34,63,38,1,10,28,1596791,16,3,1596851,14,1556993,1310721,2,2,26, +2,7,26,2,1,6,1,10,28,34,63,38,3,26,1,6, +1,10,28,34,63,38,1,10,28,1596861,16,1,1597091,14,1310721,2, +3,26,1590513,2,1611819,12,10,3,6,10,4,8,1587563,1,6,2, +1,6,3,26,5,6,4,10,8,8,1558017,1310721,2,3,26,2, +26,33,10,3,6,10,4,8,1310721,2,3,26,2,1596201,12,8, +1590515,2,1611819,12,8,1587563,1,6,2,1,6,3,26,5,6,4, +10,8,8,1558017,1310721,2,3,26,2,3,26,26,33,10,3,6, +10,4,8,1310721,2,3,26,2,3,26,1596201,12,8,1589487,1310721,2, +2,26,1310721,2,3,26,2,10,3,6,10,4,8,1,1597091,14, +1,1310721,2,5,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2, +1835009,40,1597139,16,1966081,1310721,4,1579223,12,1310721,2,4,1589487,1310721,2,2, +26,2,1310721,2,3,26,4,1310721,2,3,26,2,1,6,1,10, +28,34,63,38,3,26,1597207,16,1,1597339,14,1310721,2,2,1595671,12, +8,3,1594729,12,8,5,1594643,12,8,27,1594729,12,8,3,1594729,12, +8,1310721,2,3,26,2,1595671,12,8,5,1594729,12,8,1310721,2,2, +1595671,12,8,3,1594729,12,8,3,1594729,12,8,1310721,2,3,26,2, +3,26,1595671,12,8,5,1594729,12,8,1,1597339,14,1,1310721,2,5, +26,1310721,4,18,1310721,2,13,28,1310721,4,1310721,2,1835009,40,1597387,16, +1966081,1310721,4,1579223,12,1310721,2,9,26,4,1310721,2,7,26,4,1310721, +2,5,26,4,1310721,2,3,26,4,1310721,2,4,1,1310721,2,11, +26,4,1587551,2,1597459,16,3,1597503,14,1310721,2,2,1310721,2,3,26, +2,40,3,36,1,6,1,10,28,34,63,38,1,10,28,1597513, +16,1,1597845,14,1589307,1,26,1,10,3,6,10,4,8,1589307,3, +26,5,10,3,6,10,4,8,1589307,5,26,1310721,2,2,10,3, +6,10,4,8,1589307,7,26,1310721,2,3,26,2,10,3,6,10, +4,8,1589307,9,26,1310721,2,9,26,2,10,3,6,10,4,8, +1310721,2,11,26,2,1310721,2,9,26,2,40,1597829,16,1589307,11,1310721, +2,11,26,2,26,1310721,2,11,26,2,26,26,1310721,2,5,26, +2,1310721,2,11,26,2,26,2,10,3,6,10,4,8,1589307,13, +1310721,2,11,26,2,26,1310721,2,11,26,2,26,26,1310721,2,7, +26,2,1310721,2,11,26,2,26,2,10,3,6,10,4,8,1310721, +2,11,26,1,6,2,1,6,3,26,5,6,4,10,8,8, +1597633,14,1589307,1572879,12,8,1,1597845,14,1,1310721,2,13,26,1310721,4, +18,1310721,2,5,28,1310721,4,1310721,2,1835009,40,1597893,16,1966081,1310721,4, +1579223,12,1,1310721,2,4,1310721,2,2,1589305,2,40,1598067,16,1310721,2, +3,26,1587905,1310721,2,2,135,30,26,10,3,6,10,4,8,1310721, +2,3,26,2,1,26,2,1310721,2,3,26,2,3,26,2,1310721, +2,3,26,2,7,26,1310721,2,3,26,2,71,26,1310721,2,3, +26,2,5,26,2,1597353,12,8,1310721,2,1,6,2,1,6,3, +26,5,6,4,10,8,8,1597901,14,1589305,1,10,3,6,10,4, +8,1,1598091,14,1,1310721,2,5,26,1310721,4,18,1310721,2,7,28, +1310721,4,1310721,2,1835009,40,1598139,16,1966081,1310721,4,1579223,12,1310721,2,4, +1595029,12,1310721,2,3,26,4,15,1594729,12,8,1310721,2,5,26,1587549, +2,10,3,6,10,4,8,1310721,2,2,2,1598251,16,1310721,2,2, +2,1594449,12,8,1310721,2,1,6,2,1,6,3,26,5,6,4, +10,8,8,1598191,14,1,1594449,12,8,1310721,2,3,26,2,1587549,2, +1594811,12,8,1310721,2,5,26,2,1594643,12,8,1,1598303,14,1,1310721, +2,7,26,1310721,4,18,1310721,2,1,28,1310721,4,1310721,2,1835009,40, +1598351,16,1966081,1310721,4,1579223,12,1585301,2,1,28,1,6,1,10,28, +34,63,38,3,26,1598557,16,1585307,2,1598397,16,3,1598423,14,1573381,12, +1,6,1,10,28,34,63,38,1,10,28,1598455,16,1585299,3,1, +10,28,10,3,6,10,4,8,1,1598857,14,1585299,1573325,12,511,32, +10,3,6,10,4,8,1585299,2,193,28,1,6,1,10,28,34, +63,38,3,26,1598553,16,1585307,3,10,3,6,10,4,8,1585299,3, +1,10,28,10,3,6,10,4,8,1,1598857,14,1598789,14,1585301,2, +7,28,1,6,1,10,28,34,63,38,3,26,1598703,16,1585299,1585305, +2,1585303,2,5,38,26,2,1585303,2,7,32,17,30,38,511,32, +10,3,6,10,4,8,1585299,2,1598677,16,1585303,1,6,2,1,6, +3,26,5,6,4,10,8,8,1598699,14,1585299,3,1,10,28,10, +3,6,10,4,8,1598789,14,1585299,1585303,2,2,511,32,10,3,6, +10,4,8,1585299,2,1598767,16,1585303,1,6,2,1,6,3,26,5, +6,4,10,8,8,1598789,14,1585299,3,1,10,28,10,3,6,10, +4,8,1585299,2,21,28,1,6,1,10,28,34,63,38,3,26, +1598849,16,1585297,1,6,2,1,6,3,26,5,6,4,10,8,8, +1,1598857,14,1,1310721,2,1,26,1310721,4,18,1310721,2,13,28,1310721, +4,1310721,2,1835009,40,1598905,16,1966081,1310721,4,1579223,12,1310721,2,7,26, +4,1310721,2,5,26,4,1310721,2,3,26,4,1310721,2,4,1585309,1, +6,2,1,6,3,26,5,6,4,10,8,8,1585309,2,65,40, +3,36,1599049,16,1599041,14,115,111,117,114,99,101,115,32,110,101, +115,116,101,100,32,116,111,111,32,100,101,101,112,108,121,0, +1598989,1591079,12,8,1310721,2,9,26,1540609,1585309,2,65,30,26,10,3, +6,10,4,8,1310721,2,9,26,2,1,26,1310721,2,2,10,3, +6,10,4,8,1310721,2,9,26,2,3,26,1310721,2,3,26,2, +10,3,6,10,4,8,1310721,2,9,26,2,5,26,1,10,3, +6,10,4,8,1310721,2,9,26,2,7,26,1,10,3,6,10, +4,8,1310721,2,9,26,2,9,26,1310721,2,5,26,2,10,3, +6,10,4,8,1310721,2,9,26,2,23,26,1310721,2,7,26,2, +10,3,6,10,4,8,1310721,2,9,26,2,11,26,1585289,2,10, +3,6,10,4,8,1310721,2,9,26,2,13,26,1585291,2,10,3, +6,10,4,8,1310721,2,9,26,2,15,26,1585293,2,10,3,6, +10,4,8,1310721,2,9,26,2,17,26,1585299,2,10,3,6,10, +4,8,1310721,2,9,26,2,19,26,1585301,2,10,3,6,10,4, +8,1310721,2,9,26,2,21,26,1585303,2,10,3,6,10,4,8, +1310721,2,9,26,2,25,26,1585297,2,10,3,6,10,4,8,1310721, +2,9,26,2,27,26,1585295,2,10,3,6,10,4,8,1310721,2, +9,26,2,29,26,1587515,2,10,3,6,10,4,8,1310721,2,9, +26,2,63,26,1585305,2,10,3,6,10,4,8,1310721,2,11,26, +1,10,3,6,10,4,8,1310721,2,11,26,2,1587515,2,40,1599889, +16,1310721,2,9,26,2,31,1310721,2,11,26,2,9,30,26,26, +1587517,1310721,2,11,26,2,26,2,10,3,6,10,4,8,1310721,2, +9,26,2,33,1310721,2,11,26,2,9,30,26,26,1587525,1310721,2, +11,26,2,26,2,10,3,6,10,4,8,1310721,2,9,26,2, +35,1310721,2,11,26,2,9,30,26,26,1587533,1310721,2,11,26,2, +26,2,10,3,6,10,4,8,1310721,2,9,26,2,37,1310721,2, +11,26,2,9,30,26,26,1587541,1310721,2,11,26,2,26,2,10, +3,6,10,4,8,1310721,2,11,26,1,6,2,1,6,3,26, +5,6,4,10,8,8,1599599,14,1587515,1,10,3,6,10,4,8, +1310721,2,2,3,28,1,6,1,10,28,34,63,38,3,26,1599981, +16,1310721,2,9,26,2,7,26,1310721,2,3,26,2,1,26,2, +10,3,6,10,4,8,1310721,2,2,1,28,1,6,1,10,28, +34,63,38,3,26,1600069,16,1585301,1,10,3,6,10,4,8,1585307, +1,10,3,6,10,4,8,1585297,3,10,3,6,10,4,8,1598317, +12,8,1310721,2,2,5,28,1,6,1,10,28,34,63,38,3, +26,1600165,16,1585301,5,10,3,6,10,4,8,1585303,1310721,2,3,26, +2,10,3,6,10,4,8,1585297,3,10,3,6,10,4,8,1598317, +12,8,1310721,2,2,7,28,1,6,1,10,28,34,63,38,3, +26,1600277,16,1585301,7,10,3,6,10,4,8,1585305,1310721,2,3,26, +2,10,3,6,10,4,8,1585303,1,10,3,6,10,4,8,1585297, +3,10,3,6,10,4,8,1598317,12,8,1,1600285,14,1,1310721,2, +13,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,1835009,40,1600333, +16,1966081,1310721,4,1579223,12,1540609,1585309,2,65,30,26,1310721,2,4,1, +1310721,2,3,26,4,1585289,1310721,2,2,11,26,2,10,3,6,10, +4,8,1585291,1310721,2,2,13,26,2,10,3,6,10,4,8,1585293, +1310721,2,2,15,26,2,10,3,6,10,4,8,1585299,1310721,2,2, +17,26,2,10,3,6,10,4,8,1585301,1310721,2,2,19,26,2, +10,3,6,10,4,8,1585303,1310721,2,2,21,26,2,10,3,6, +10,4,8,1585297,1310721,2,2,25,26,2,10,3,6,10,4,8, +1585295,1310721,2,2,27,26,2,10,3,6,10,4,8,1587515,1310721,2, +2,29,26,2,10,3,6,10,4,8,1585305,1310721,2,2,63,26, +2,10,3,6,10,4,8,1310721,2,3,26,2,1587515,2,40,1600897, +16,1587517,1310721,2,3,26,2,26,1310721,2,2,31,1310721,2,3,26, +2,9,30,26,26,2,10,3,6,10,4,8,1587525,1310721,2,3, +26,2,26,1310721,2,2,33,1310721,2,3,26,2,9,30,26,26, +2,10,3,6,10,4,8,1587533,1310721,2,3,26,2,26,1310721,2, +2,35,1310721,2,3,26,2,9,30,26,26,2,10,3,6,10, +4,8,1587541,1310721,2,3,26,2,26,1310721,2,2,37,1310721,2,3, +26,2,9,30,26,26,2,10,3,6,10,4,8,1310721,2,3, +26,1,6,2,1,6,3,26,5,6,4,10,8,8,1600623,14, +1585309,1,6,2,1,6,-1,26,5,6,4,10,8,8,1310721,2, +2,23,26,2,1600959,16,1310721,2,2,3,26,2,1580973,12,8,1, +1600967,14,1,1310721,2,5,26,1310721,4,18,1310721,2,5,28,1310721,4, +1310721,2,1835009,40,1601015,16,1966081,1310721,4,1579223,12,1310721,2,4,1601065,14, +43,45,42,47,37,61,60,62,33,38,124,94,126,63,58,46, +64,35,36,0,1601025,1310721,2,3,26,4,1310721,2,3,26,2,2, +1601181,16,1310721,2,3,26,2,2,1310721,2,2,28,1,6,1,10, +28,34,63,38,3,26,1601143,16,3,1601189,14,1310721,2,3,26,1, +6,2,1,6,3,26,5,6,4,10,8,8,1601077,14,1,1601189, +14,1,1310721,2,5,26,1310721,4,18,1310721,2,11,28,1310721,4,1310721, +2,1835009,40,1601237,16,1966081,1310721,4,1579223,12,1310721,2,3,26,4,1310721, +2,4,1,1310721,2,5,26,4,1310721,2,5,26,2,1585235,2,40, +1601667,16,1310721,2,7,26,1547777,1310721,2,5,26,2,26,2,10,3, +6,10,4,8,1310721,2,9,26,1,10,3,6,10,4,8,1310721, +2,9,26,2,1310721,2,2,40,1601453,16,1310721,2,7,26,2,1310721, +2,9,26,2,26,2,1585311,1310721,2,9,26,2,26,2,28,1, +6,1,10,28,34,63,38,3,26,1,6,1,10,28,34,63, +38,1,10,28,1601455,14,1,1601497,16,1310721,2,9,26,1,6,2, +1,6,3,26,5,6,4,10,8,8,1601343,14,1310721,2,9,26, +2,1310721,2,2,28,1,6,1,10,28,34,63,38,3,26,1601617, +16,1310721,2,7,26,2,1310721,2,2,26,2,1310721,2,3,26,2, +28,1,6,1,10,28,34,63,38,3,26,1,6,1,10,28, +34,63,38,1,10,28,1601619,14,1,1601629,16,3,1601675,14,1310721,2, +5,26,1,6,2,1,6,3,26,5,6,4,10,8,8,1601265, +14,1,1601675,14,1,1310721,2,11,26,1310721,4,18,1310721,2,3,28, +1310721,4,1310721,2,1835009,40,1601723,16,1966081,1310721,4,1579223,12,1310721,2,4, +1310721,2,2,97,40,3,36,1601789,16,1310721,2,2,115,10,40,3, +36,1,6,1,10,28,34,63,38,1,10,28,1601791,14,1,1601797, +14,1,1310721,2,3,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721, +2,1835009,40,1601845,16,1966081,1310721,4,1579223,12,1310721,2,4,1310721,2,2, +195,40,3,36,1601911,16,1310721,2,2,245,10,40,3,36,1,6, +1,10,28,34,63,38,1,10,28,1601913,14,1,1601923,16,3,1602007, +14,1310721,2,2,131,40,3,36,1601983,16,1310721,2,2,181,10,40, +3,36,1,6,1,10,28,34,63,38,1,10,28,1601985,14,1, +1,6,1,10,28,34,63,38,1,10,28,1602017,16,3,1602069,14, +1310721,2,2,191,28,1,6,1,10,28,34,63,38,3,26,1, +6,1,10,28,34,63,38,1,10,28,1602075,14,1,1310721,2,3, +26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1602123,16, +1966081,1310721,4,1579223,12,1310721,2,4,1310721,2,2,1601689,12,1602157,16,1310721, +2,2,97,28,1602339,14,1310721,2,2,195,40,3,36,1602217,16,1310721, +2,2,205,10,40,3,36,1,6,1,10,28,34,63,38,1, +10,28,1602219,14,1,1602241,16,1310721,2,2,195,28,21,26,1602339,14, +1310721,2,2,131,40,3,36,1602301,16,1310721,2,2,141,10,40,3, +36,1,6,1,10,28,34,63,38,1,10,28,1602303,14,1,1602325, +16,1310721,2,2,131,28,21,26,1602339,14,3,1,10,28,1602339,14, +1,1310721,2,3,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2, +1835009,40,1602387,16,1966081,1310721,4,1579223,12,1585299,2,1310721,2,4,1598317,12, +8,1310721,2,2,185,28,1,6,1,10,28,34,63,38,1,10, +28,1602449,16,1310721,2,2,1602687,14,1310721,2,1585299,2,10,3,6,10, +4,8,1598317,12,8,1310721,2,2,221,28,1,6,1,10,28,34, +63,38,3,26,1602515,16,21,1602687,14,1310721,2,2,233,28,1,6, +1,10,28,34,63,38,3,26,1602555,16,19,1602687,14,1310721,2,2, +229,28,1,6,1,10,28,34,63,38,3,26,1602595,16,27,1602687, +14,1310721,2,2,97,28,1,6,1,10,28,34,63,38,3,26, +1602635,16,1,1602687,14,1310721,2,2,203,28,1,6,1,10,28,34, +63,38,3,26,1602675,16,55,1602687,14,1310721,2,2,1602687,14,1,1310721, +2,3,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40, +1602735,16,1966081,1310721,4,1579223,12,1310721,2,4,1585299,2,1,40,3,36, +1602791,16,1585299,2,1600981,12,1,6,1,10,28,34,63,38,1,10, +28,1602793,14,1,1602837,16,1310721,2,2,1585299,2,1601203,12,1,6,1, +10,28,34,63,38,1,10,28,1602839,14,1,1602909,16,1585311,1310721,2, +2,26,1585299,2,10,3,6,10,4,8,1310721,2,1,6,2,1, +6,3,26,5,6,4,10,8,8,1598317,12,8,1602741,14,1585311,1310721, +2,2,26,1,10,3,6,10,4,8,1585289,9,10,3,6,10, +4,8,1585291,1585311,1592287,12,10,3,6,10,4,8,1,1602977,14,1, +1310721,2,3,26,1310721,4,18,1310721,2,7,28,1310721,4,1310721,2,1835009, +40,1603025,16,1966081,1310721,4,1579223,12,1,1310721,2,4,1585293,3,10,3, +6,10,4,8,3,1603701,16,1585299,2,65,28,1,6,1,10,28, +34,63,38,3,26,1603093,16,3,1603143,14,1585299,2,19,28,1,6, +1,10,28,34,63,38,3,26,1,6,1,10,28,34,63,38, +1,10,28,1603153,16,3,1603203,14,1585299,2,21,28,1,6,1,10, +28,34,63,38,3,26,1,6,1,10,28,34,63,38,1,10, +28,1603213,16,3,1603263,14,1585299,2,27,28,1,6,1,10,28,34, +63,38,3,26,1,6,1,10,28,34,63,38,1,10,28,1603293, +16,1598317,12,8,1585293,1,10,3,6,10,4,8,1603055,14,1585299,2, +95,28,1,6,1,10,28,34,63,38,1,10,28,1603331,16,1603701, +14,1598317,12,8,1585299,2,95,28,1,6,1,10,28,34,63,38, +3,26,1603477,16,1585299,2,1,40,3,36,1603441,16,1585299,2,21,28, +1,6,1,10,28,34,63,38,1,10,28,1,6,1,10,28, +34,63,38,1,10,28,1603443,14,1,1603457,16,1598317,12,8,1603369,14, +1585293,1,10,3,6,10,4,8,1603049,14,1585299,2,85,28,1,6, +1,10,28,34,63,38,3,26,1603645,16,1598317,12,8,1585299,2,1, +40,3,36,1603619,16,1585299,2,85,28,1,6,1,10,28,34,63, +38,3,26,1603609,16,1598317,12,8,1585299,2,95,28,1,6,1,10, +28,34,63,38,3,26,1603605,16,1603619,14,1603615,14,1598317,12,8,1603515, +14,1598317,12,8,1585293,1,10,3,6,10,4,8,1603049,14,1585295,1585297, +2,10,3,6,10,4,8,1585311,1,26,95,10,3,6,10,4, +8,3,1602701,12,8,1,1604985,14,1603049,14,1585295,1585297,2,10,3,6, +10,4,8,1585299,2,1,40,1603769,16,1585289,1,10,3,6,10,4, +8,1585291,1,10,3,6,10,4,8,1,1604985,14,1585299,2,1601689,12, +1604191,16,1310721,2,3,26,1,10,3,6,10,4,8,1585299,2,97, +28,1,6,1,10,28,34,63,38,3,26,1604079,16,1598317,12,8, +1585299,2,241,28,1,6,1,10,28,34,63,38,3,26,1603879,16, +3,1603929,14,1585299,2,177,28,1,6,1,10,28,34,63,38,3, +26,1,6,1,10,28,34,63,38,1,10,28,1604079,16,1598317,12, +8,1310721,2,5,26,1585299,2,1602089,12,10,3,6,10,4,1,40, +3,36,1604033,16,1310721,2,3,26,1310721,2,3,26,2,33,30,1310721, +2,5,26,2,26,10,3,6,10,4,8,1598317,12,8,1603939,14, +1585289,3,10,3,6,10,4,8,1585291,1310721,2,3,26,2,10,3, +6,10,4,8,1,1604985,14,1585299,2,1601689,12,1604145,16,1310721,2,3, +26,1310721,2,3,26,2,21,30,1585299,2,26,97,28,10,3,6, +10,4,8,1598317,12,8,1604079,14,1585289,3,10,3,6,10,4,8, +1585291,1310721,2,3,26,2,10,3,6,10,4,8,1,1604985,14,1585299, +2,1601811,12,1604401,16,1585299,2,1601811,12,1604221,16,3,1604251,14,1585299,2, +1601689,12,1,6,1,10,28,34,63,38,1,10,28,1604335,16,1310721, +2,2,2001,40,1604325,16,1585311,1310721,2,2,26,1585299,2,10,3,6, +10,4,8,1310721,2,1,6,2,1,6,3,26,5,6,4,10, +8,8,1598317,12,8,1604203,14,1585311,1310721,2,2,26,1,10,3,6, +10,4,8,1585289,5,10,3,6,10,4,8,1585291,1585311,1592287,12,10, +3,6,10,4,8,1,1604985,14,1585299,2,79,28,1,6,1,10, +28,34,63,38,3,26,1604575,16,1598317,12,8,1585291,1602353,12,10,3, +6,10,4,8,1585299,2,79,28,1,6,1,10,28,34,63,38, +1,10,28,1604547,16,1604539,14,98,97,100,32,99,104,97,114,97, +99,116,101,114,32,108,105,116,101,114,97,108,0,1604495,1591557,12, +8,1598317,12,8,1585289,3,10,3,6,10,4,8,1,1604985,14,1585299, +2,69,28,1,6,1,10,28,34,63,38,3,26,1604867,16,1598317, +12,8,1585299,2,1,40,3,36,1604685,16,1585299,2,69,28,1,6, +1,10,28,34,63,38,1,10,28,1,6,1,10,28,34,63, +38,1,10,28,1604687,14,1,1604795,16,1310721,2,3,26,1602353,12,10, +3,6,10,4,8,1310721,2,2,2001,40,1604791,16,1585311,1310721,2,2, +26,1310721,2,3,26,2,10,3,6,10,4,8,1310721,2,1,6, +2,1,6,3,26,5,6,4,10,8,8,1604613,14,1598317,12,8, +1585311,1310721,2,2,26,1,10,3,6,10,4,8,1585289,7,10,3, +6,10,4,8,1585291,1585311,1592287,12,10,3,6,10,4,8,1,1604985, +14,1585311,1,26,1585299,2,10,3,6,10,4,8,1598317,12,8,1585311, +1,26,2,1600981,12,1604921,16,3,1602701,12,1604985,14,1585311,3,26,1, +10,3,6,10,4,8,1585289,9,10,3,6,10,4,8,1585291,1585311, +1592287,12,10,3,6,10,4,8,1,1604985,14,1,1310721,2,7,26, +1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1605033,16,1966081, +1310721,4,1579223,12,3,1310721,2,4,1587515,2,1605373,16,1585289,1587517,1,26, +2,10,3,6,10,4,8,1585291,1587525,1,26,2,10,3,6,10, +4,8,1585293,1587533,1,26,2,10,3,6,10,4,8,1585295,1587541,1, +26,2,10,3,6,10,4,8,1310721,2,2,1587515,2,40,1605339,16, +1587517,1310721,2,2,3,28,26,1587517,1310721,2,2,26,2,10,3,6, +10,4,8,1587525,1310721,2,2,3,28,26,1587525,1310721,2,2,26,2, +10,3,6,10,4,8,1587533,1310721,2,2,3,28,26,1587533,1310721,2, +2,26,2,10,3,6,10,4,8,1587541,1310721,2,2,3,28,26, +1587541,1310721,2,2,26,2,10,3,6,10,4,8,1310721,2,1,6, +2,1,6,3,26,5,6,4,10,8,8,1605137,14,1587515,1,6, +2,1,6,-1,26,5,6,4,10,8,8,1,1605383,14,1605919,12, +1605383,14,1,1310721,2,3,26,1310721,4,18,1310721,2,11,28,1310721,4, +1310721,2,1835009,40,1605431,16,1966081,1310721,4,1579223,12,1310721,2,4,1587515,2, +1310721,2,2,10,40,3,36,1605785,16,1310721,2,3,26,1585289,2,10, +3,6,10,4,8,1310721,2,5,26,1585291,2,10,3,6,10,4, +8,1310721,2,7,26,1585293,2,10,3,6,10,4,8,1310721,2,9, +26,1585295,2,10,3,6,10,4,8,1605919,12,8,1587517,1587515,2,26, +1585289,2,10,3,6,10,4,8,1587525,1587515,2,26,1585291,2,10,3, +6,10,4,8,1587533,1587515,2,26,1585293,2,10,3,6,10,4,8, +1587541,1587515,2,26,1585295,2,10,3,6,10,4,8,1587515,1,6,2, +1,6,3,26,5,6,4,10,8,8,1585289,1310721,2,3,26,2, +10,3,6,10,4,8,1585291,1310721,2,5,26,2,10,3,6,10, +4,8,1585293,1310721,2,7,26,2,10,3,6,10,4,8,1585295,1310721, +2,9,26,2,10,3,6,10,4,8,1605437,14,1587511,1587517,1310721,2, +2,26,2,10,3,6,10,4,8,1587513,1587525,1310721,2,2,26,2, +10,3,6,10,4,8,1,1605845,14,1,1310721,2,11,26,1310721,4, +18,1310721,2,1,28,1310721,4,1310721,2,1835009,40,1605893,16,1966081,1310721,4, +1579223,12,1,1605397,12,1605905,14,1,1310721,2,1,26,1310721,4,18,1310721, +2,7,28,1310721,4,1310721,2,1835009,40,1605953,16,1966081,1310721,4,1579223,12, +1310721,2,1540609,1585309,2,65,30,26,10,3,6,10,4,8,1310721,2, +2,1,26,2,3,28,1,6,1,10,28,34,63,38,1,10, +28,1606035,16,1602991,12,8,1,1606411,14,1310721,2,5,26,1310721,2,2, +5,26,2,10,3,6,10,4,8,1310721,2,5,26,2,1310721,2, +2,7,26,2,40,1606323,16,1310721,2,3,26,1310721,2,2,3,26, +2,10,3,6,10,4,8,1585289,1310721,2,3,26,2,3,1310721,2, +5,26,2,7,30,26,26,2,10,3,6,10,4,8,1585291,1310721, +2,3,26,2,5,1310721,2,5,26,2,7,30,26,26,2,10, +3,6,10,4,8,1585295,1310721,2,3,26,2,7,1310721,2,5,26, +2,7,30,26,26,2,10,3,6,10,4,8,1585293,1,10,3, +6,10,4,8,1310721,2,2,5,26,1310721,2,5,26,2,3,26, +10,3,6,10,4,8,1,1606411,14,1310721,2,2,9,26,2,1, +6,1,10,28,34,63,38,3,26,1606397,16,1585289,1,10,3,6, +10,4,8,1585291,1,10,3,6,10,4,8,1,1606411,14,1600299,12, +8,1,1606411,14,1,1310721,2,7,26,1310721,4,18,1310721,2,3,28, +1310721,4,1310721,2,1835009,40,1606459,16,1966081,1310721,4,1579223,12,1310721,2,4, +1585289,2,9,28,1,6,1,10,28,34,63,38,3,26,1606555,16, +1585291,2,1310721,2,2,28,1,6,1,10,28,34,63,38,3,26, +1,6,1,10,28,34,63,38,1,10,28,1606557,14,1,1606563,14, +1,1310721,2,3,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2, +1835009,40,1606611,16,1966081,1310721,4,1579223,12,1310721,2,4,1585289,2,5,28, +1,6,1,10,28,34,63,38,3,26,1606707,16,1585291,2,1310721,2, +2,28,1,6,1,10,28,34,63,38,3,26,1,6,1,10, +28,34,63,38,1,10,28,1606709,14,1,1606715,14,1,1310721,2,3, +26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1606763,16, +1966081,1310721,4,1579223,12,1310721,2,4,1585289,2,9,28,1,6,1,10, +28,34,63,38,3,26,1606807,16,3,1606857,14,1585289,2,5,28,1, +6,1,10,28,34,63,38,3,26,1,6,1,10,28,34,63, +38,1,10,28,1606919,16,1585291,2,1310721,2,2,28,1,6,1,10, +28,34,63,38,3,26,1,6,1,10,28,34,63,38,1,10, +28,1606921,14,1,1606937,16,1604999,12,8,3,1606945,14,1,1606945,14,1, +1310721,2,3,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009, +40,1606993,16,1966081,1310721,4,1579223,12,1310721,2,4,1310721,2,2,1606729,12, +1,6,1,10,28,34,63,38,3,26,1607227,16,1590585,2,1,6, +1,10,28,34,63,38,3,26,1607111,16,1587703,2,1,6,1,10, +28,34,63,38,3,26,1,6,1,10,28,34,63,38,1,10, +28,1607113,14,1,1607173,16,1590587,12,8,1607147,14,101,120,112,101,99, +116,101,100,32,0,1607127,1573495,12,8,1310721,2,2,1573495,12,8,1590731, +12,8,1590585,3,10,3,6,10,4,8,1585289,2,1,28,1,6, +1,10,28,34,63,38,3,26,1607227,16,1590891,12,8,1,1607235,14, +1,1310721,2,3,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2, +1835009,40,1607283,16,1966081,1310721,4,1579223,12,1585291,2,1310721,2,4,1585289,2, +5,28,1,6,1,10,28,34,63,38,1,10,28,1607371,16,1607363, +14,101,120,112,101,99,116,101,100,32,97,32,110,97,109,101, +0,1607331,1591557,12,8,1604999,12,8,1310721,2,2,1607389,14,1,1310721,2, +3,26,1310721,4,18,1310721,2,15,28,1310721,4,1310721,2,1835009,40,1607437, +16,1966081,1310721,4,1579223,12,1310721,2,4,1585237,2,1310721,2,3,26,4, +3,1608511,16,1310721,2,2,2,65,28,1,6,1,10,28,34,63, +38,3,26,1607533,16,1310721,2,1,6,2,1,6,3,26,5,6, +4,10,8,8,1607463,14,1310721,2,2,2,1,6,1,10,28,34, +63,38,3,26,1607569,16,1608511,14,1310721,2,5,26,1,10,3,6, +10,4,8,1310721,2,2,1310721,2,5,26,2,26,2,1607687,16,1310721, +2,2,1310721,2,5,26,2,26,2,65,28,1,6,1,10,28, +34,63,38,1,10,28,1,6,1,10,28,34,63,38,1,10, +28,1607689,14,1,1607777,16,1585311,1310721,2,5,26,2,26,1310721,2,2, +1310721,2,5,26,2,26,2,10,3,6,10,4,8,1310721,2,5, +26,1,6,2,1,6,3,26,5,6,4,10,8,8,1607591,14, +1585311,1310721,2,5,26,2,26,1,10,3,6,10,4,8,1310721,2, +1310721,2,2,1310721,2,5,26,2,26,10,3,6,10,4,8,1310721, +2,7,26,1585311,1,26,2,91,28,1,6,1,10,28,34,63, +38,3,26,1607921,16,1585311,3,26,2,1601689,12,1,6,1,10,28, +34,63,38,1,10,28,1607923,14,1,10,3,6,10,4,8,1585311, +1310721,2,7,26,2,26,2,1601689,12,1608251,16,1310721,2,9,26,1, +10,3,6,10,4,8,1310721,2,11,26,1310721,2,7,26,2,10, +3,6,10,4,8,1585311,1310721,2,11,26,2,26,2,1608125,16,1310721, +2,9,26,1310721,2,9,26,2,21,30,1585311,1310721,2,11,26,2, +26,2,26,97,28,10,3,6,10,4,8,1310721,2,11,26,1, +6,2,1,6,3,26,5,6,4,10,8,8,1608011,14,1310721,2, +7,26,2,1608173,16,1310721,2,9,26,1,1310721,2,9,26,2,28, +10,3,6,10,4,8,1542657,1585237,2,26,1310721,2,9,26,2,1310721, +2,9,26,2,26,3,26,10,3,6,10,4,8,1585237,1,6, +2,1,6,3,26,5,6,4,10,8,8,1608507,14,1310721,2,5, +26,2,3,28,1,6,1,10,28,34,63,38,3,26,1608353,16, +1310721,2,13,26,1585311,1,26,2,1608541,12,10,3,6,10,4,1, +40,3,36,1,6,1,10,28,34,63,38,1,10,28,1608355,14, +1,1608433,16,1542657,1585237,2,26,1310721,2,13,26,2,1310721,2,13,26, +2,26,10,3,6,10,4,8,1585237,1,6,2,1,6,3,26, +5,6,4,10,8,8,1608507,14,1542657,1585237,2,26,-3,10,3,6, +10,4,8,1542657,1585237,2,3,26,26,1585311,1592287,12,10,3,6,10, +4,8,1585237,1585237,2,5,26,10,3,6,10,4,8,1607457,14,1310721, +2,3,26,2,1608527,14,1,1310721,2,15,26,1310721,4,18,1310721,2, +5,28,1310721,4,1310721,2,1835009,40,1608575,16,1966081,1310721,4,1579223,12,1310721, +2,4,1,1310721,2,3,26,4,1585239,2,1310721,2,3,26,2,26, +2,1608717,16,1585239,2,1310721,2,3,26,2,26,2,1310721,2,2,28, +1,6,1,10,28,34,63,38,3,26,1608679,16,1310721,2,3,26, +2,1608731,14,1310721,2,3,26,1,6,2,1,6,3,26,5,6, +4,10,8,8,1608593,14,3,1,10,28,1608731,14,1,1310721,2,5, +26,1310721,4,18,1310721,2,9,28,1310721,4,1310721,2,1835009,40,1608779,16, +1966081,1310721,4,1579223,12,1310721,2,3,26,4,1310721,2,4,1310721,2,2, +1310721,2,3,26,2,26,1310721,2,5,26,4,1310721,2,2,1310721,2, +5,26,2,40,1609225,16,1542657,1310721,2,2,26,2,-3,28,1,6, +1,10,28,34,63,38,3,26,1609173,16,1310721,2,7,26,1542657,1310721, +2,2,3,26,26,2,1593303,12,10,3,6,10,4,8,1310721,2, +7,26,2,1,40,1608983,16,1310721,2,7,26,1542657,1310721,2,2,3, +26,26,2,1595111,12,10,3,6,10,4,8,1417729,1310721,2,7,26, +2,26,2,3,28,1,6,1,10,28,34,63,38,3,26,1609047, +16,1310721,2,7,26,2,1595235,12,8,1609143,14,1417729,1310721,2,7,26, +2,26,2,13,28,1,6,1,10,28,34,63,38,3,26,1609121, +16,45,1425921,1310721,2,7,26,2,26,2,26,1594729,12,8,1609143,14, +1425921,1310721,2,7,26,2,26,2,1594643,12,8,1310721,2,1310721,2,2, +5,26,10,3,6,10,4,8,1609221,14,1542657,1310721,2,2,26,2, +1594449,12,8,1310721,2,1,6,2,1,6,3,26,5,6,4,10, +8,8,1608823,14,1,1609233,14,1,1310721,2,9,26,1310721,4,18,1310721, +2,5,28,1310721,4,1310721,2,1835009,40,1609281,16,1966081,1310721,4,1579223,12, +1310721,2,4,1,1310721,2,3,26,4,1310721,2,3,26,2,1585235,2, +40,1609411,16,1547777,1310721,2,3,26,2,26,2,1310721,2,2,28,1, +6,1,10,28,34,63,38,3,26,1609373,16,1,1609473,14,1310721,2, +3,26,1,6,2,1,6,3,26,5,6,4,10,8,8,1609299, +14,1547777,1585235,2,26,1310721,2,2,10,3,6,10,4,8,1585235,1, +6,2,1,6,3,26,5,6,4,10,8,8,1,1609473,14,1, +1310721,2,5,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,1835009, +40,1609521,16,1966081,1310721,4,1579223,12,1310721,2,4,1585229,2,1310721,2,3, +26,4,1310721,2,3,26,2,1609655,16,1310721,2,3,26,1,6,2, +1,6,-1,26,5,6,4,10,8,8,1546753,1310721,2,3,26,2, +26,2,1310721,2,2,28,1,6,1,10,28,34,63,38,3,26, +1609651,16,1310721,2,3,26,2,1609669,14,1609541,14,3,1,10,28,1609669, +14,1,1310721,2,5,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721, +2,1835009,40,1609717,16,1966081,1310721,4,1579223,12,1310721,2,4,1585231,2,1310721, +2,3,26,4,1310721,2,3,26,2,1609851,16,1310721,2,3,26,1, +6,2,1,6,-1,26,5,6,4,10,8,8,1547265,1310721,2,3, +26,2,26,2,1310721,2,2,28,1,6,1,10,28,34,63,38, +3,26,1609847,16,1310721,2,3,26,2,1609865,14,1609737,14,3,1,10, +28,1609865,14,1,1310721,2,5,26,1310721,4,18,1310721,2,5,28,1310721, +4,1310721,2,1835009,40,1609913,16,1966081,1310721,4,1579223,12,1310721,2,4,1585233, +2,1310721,2,3,26,4,1310721,2,3,26,2,1610047,16,1310721,2,3, +26,1,6,2,1,6,-1,26,5,6,4,10,8,8,1547457,1310721, +2,3,26,2,26,2,1310721,2,2,28,1,6,1,10,28,34, +63,38,3,26,1610043,16,1310721,2,3,26,2,1610061,14,1609933,14,3, +1,10,28,1610061,14,1,1310721,2,5,26,1310721,4,18,1310721,2,15, +28,1310721,4,1310721,2,1835009,40,1610109,16,1966081,1310721,4,1579223,12,1310721,2, +5,26,4,1310721,2,3,26,4,1310721,2,4,1310721,2,2,1592287,12, +1310721,2,7,26,4,1310721,2,7,26,2,1609487,12,1310721,2,9,26, +4,1310721,2,9,26,2,1,40,1610249,16,1310721,2,9,26,1585229,2, +10,3,6,10,4,8,1585229,1,6,2,1,6,3,26,5,6, +4,10,8,8,1546753,1310721,2,9,26,2,26,1310721,2,7,26,2, +10,3,6,10,4,8,1546881,1310721,2,9,26,2,26,1310721,2,3, +26,2,10,3,6,10,4,8,1310721,2,3,26,2,1,40,1610473, +16,1546881,1310721,2,9,26,2,26,1,1310721,2,3,26,2,28,10, +3,6,10,4,8,1547137,1310721,2,9,26,2,26,1310721,2,3,26, +2,10,3,6,10,4,8,1547009,1310721,2,9,26,2,26,1,10, +3,6,10,4,8,1310721,2,7,26,2,1609247,12,8,1310721,2,9, +26,2,1611101,14,1547009,1310721,2,9,26,2,26,1310721,2,5,26,2, +1607403,12,10,3,6,10,4,8,1547137,1310721,2,9,26,2,26,1585237, +2,1547009,1310721,2,9,26,2,26,2,28,10,3,6,10,4,8, +1310721,2,7,26,2,1609247,12,8,1310721,2,13,26,1310721,2,2,1574039, +12,10,3,6,10,4,8,1585311,2001,26,1310721,2,2,1310721,2,13, +26,2,1574491,12,8,1585311,2001,1310721,2,13,26,2,26,26,123,10, +3,6,10,4,8,1585311,2003,1310721,2,13,26,2,26,26,1,10, +3,6,10,4,8,1310721,2,11,26,1585311,2001,26,1592287,12,10,3, +6,10,4,8,1310721,2,3,26,2,17,40,3,36,1610757,16,3, +1610813,14,1310721,2,3,26,2,11,28,1,6,1,10,28,34,63, +38,3,26,1,6,1,10,28,34,63,38,1,10,28,1610823,16, +3,1610879,14,1310721,2,3,26,2,9,28,1,6,1,10,28,34, +63,38,3,26,1,6,1,10,28,34,63,38,1,10,28,1610889, +16,3,1610945,14,1310721,2,3,26,2,7,28,1,6,1,10,28, +34,63,38,3,26,1,6,1,10,28,34,63,38,1,10,28, +1611085,16,1310721,2,11,26,2,1609879,12,1,40,1611029,16,1547457,1585233,2, +26,1310721,2,11,26,2,10,3,6,10,4,8,1585233,1,6,2, +1,6,3,26,5,6,4,10,8,8,1547585,1310721,2,11,26,2, +1609879,12,26,1310721,2,9,26,2,10,3,6,10,4,8,1310721,2, +11,26,2,1609247,12,8,1310721,2,9,26,2,1611101,14,1,1310721,2, +15,26,1310721,4,18,1310721,2,9,28,1310721,4,1310721,2,1835009,40,1611149, +16,1966081,1310721,4,1579223,12,1310721,2,3,26,4,1310721,2,4,1310721,2, +2,1592287,12,1310721,2,5,26,4,1310721,2,5,26,2,1609683,12,1310721, +2,7,26,4,1310721,2,7,26,2,1,40,1611279,16,1310721,2,7, +26,1585231,2,10,3,6,10,4,8,1585231,1,6,2,1,6,3, +26,5,6,4,10,8,8,1547265,1310721,2,7,26,2,26,1310721,2, +5,26,2,10,3,6,10,4,8,1547329,1310721,2,7,26,2,26, +1310721,2,3,26,2,1607403,12,10,3,6,10,4,8,1547393,1310721,2, +7,26,2,26,1585237,2,1547329,1310721,2,7,26,2,26,2,28,10, +3,6,10,4,8,1310721,2,5,26,2,1609247,12,8,1310721,2,7, +26,2,1611435,14,1,1310721,2,9,26,1310721,4,18,1310721,2,9,28, +1310721,4,1310721,2,1835009,40,1611483,16,1966081,1310721,4,1579223,12,1310721,2,5, +26,4,1310721,2,3,26,4,1310721,2,4,1310721,2,2,1592287,12,11, +1310721,2,3,26,2,1592805,12,1310721,2,7,26,4,1442305,1310721,2,7, +26,2,26,3,1310721,2,5,26,2,34,10,3,6,10,4,8, +1310721,2,7,26,2,1611601,14,1,1310721,2,9,26,1310721,4,18,1310721, +2,5,28,1310721,4,1310721,2,1835009,40,1611649,16,1966081,1310721,4,1579223,12, +1310721,2,4,1587557,2,1310721,2,3,26,4,1310721,2,3,26,2,1587559, +2,10,40,1611791,16,1310721,2,3,26,1,6,2,1,6,-1,26, +5,6,4,10,8,8,1466881,1310721,2,3,26,2,26,2,1310721,2, +2,28,1,6,1,10,28,34,63,38,3,26,1611787,16,1310721,2, +3,26,2,1611805,14,1611669,14,3,1,10,28,1611805,14,1,1310721,2, +5,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,1835009,40,1611853, +16,1966081,1310721,4,1579223,12,1310721,2,4,1587557,2,1310721,2,3,26,4, +1587557,2,1025,40,3,36,1611933,16,1611925,14,116,111,111,32,109,97, +110,121,32,108,111,99,97,108,115,0,1611893,1591079,12,8,1587557,1, +6,2,1,6,3,26,5,6,4,10,8,8,1466881,1310721,2,3, +26,2,26,1310721,2,2,10,3,6,10,4,8,1467905,1310721,2,3, +26,2,26,1587563,2,10,3,6,10,4,8,1468929,1310721,2,3,26, +2,26,1587561,2,10,3,6,10,4,8,1469953,1310721,2,3,26,2, +26,1,10,3,6,10,4,8,1470977,1310721,2,3,26,2,26,1, +10,3,6,10,4,8,1472001,1310721,2,3,26,2,26,1,10,3, +6,10,4,8,1473025,1310721,2,3,26,2,26,1,10,3,6,10, +4,8,1474049,1310721,2,3,26,2,26,1,10,3,6,10,4,8, +1556993,1310721,2,3,26,2,26,1587687,2,10,3,6,10,4,8,1558017, +1310721,2,3,26,2,26,1,10,3,6,10,4,8,1589487,1310721,2, +3,26,2,26,1,10,3,6,10,4,8,1310721,2,3,26,2, +1612295,14,1,1310721,2,5,26,1310721,4,18,1310721,2,3,28,1310721,4, +1310721,2,1835009,40,1612343,16,1966081,1310721,4,1579223,12,1310721,2,4,1469953,1310721, +2,2,26,2,11,28,1,6,1,10,28,34,63,38,3,26, +1612421,16,1310721,2,1470977,1310721,2,2,26,2,10,3,6,10,4,8, +1612349,14,1310721,2,2,1612433,14,1,1310721,2,3,26,1310721,4,18,1310721, +2,3,28,1310721,4,1310721,2,1835009,40,1612481,16,1966081,1310721,4,1579223,12, +1310721,2,4,1469953,1310721,2,2,26,2,1,28,1,6,1,10,28, +34,63,38,1,10,28,1612531,14,1,1310721,2,3,26,1310721,4,18, +1310721,2,5,28,1310721,4,1310721,2,1835009,40,1612579,16,1966081,1310721,4,1579223, +12,1310721,2,4,1472001,1310721,2,2,26,3,10,3,6,10,4,8, +1587565,2,1,6,1,10,28,34,63,38,3,26,1612643,16,1,1613019, +14,1310721,2,3,26,1310721,2,2,1612309,12,10,3,6,10,4,8, +1472001,1310721,2,3,26,2,26,3,10,3,6,10,4,8,1469953,1310721, +2,3,26,2,26,2,9,28,1,6,1,10,28,34,63,38, +3,26,1612827,16,1310721,2,2,1612813,14,117,115,101,100,32,97,102, +116,101,114,32,105,116,32,119,97,115,32,103,105,118,101,110, +32,97,119,97,121,0,1612755,1591963,12,8,1,1613019,14,1469953,1310721,2, +3,26,2,26,2,7,28,1,6,1,10,28,34,63,38,3, +26,1612919,16,1468929,1310721,2,3,26,2,26,2,1587561,2,40,1,6, +1,10,28,34,63,38,1,10,28,1612921,14,1,1613011,16,1468929,1310721, +2,3,26,2,26,1587561,2,10,3,6,10,4,8,1469953,1310721,2, +3,26,2,26,3,10,3,6,10,4,8,1474049,1310721,2,3,26, +2,26,3,10,3,6,10,4,8,1,1613019,14,1,1310721,2,5, +26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1613067,16, +1966081,1310721,4,1579223,12,1310721,2,4,1310721,2,2,3,28,1,6,1, +10,28,34,63,38,3,26,1613257,16,1613249,14,97,32,110,101,119, +32,111,98,106,101,99,116,32,99,97,110,39,116,32,98,101, +32,117,115,101,100,32,97,115,32,97,32,112,108,97,105,110, +32,118,97,108,117,101,59,32,98,105,110,100,32,105,116,32, +116,111,32,97,32,110,97,109,101,32,102,105,114,115,116,0, +1613111,1591845,12,8,1310721,2,2,5,40,3,36,1613291,16,1310721,2,2, +5,28,1612545,12,8,1,1613299,14,1,1310721,2,3,26,1310721,4,18, +1310721,2,3,28,1310721,4,1310721,2,1835009,40,1613347,16,1966081,1310721,4,1579223, +12,1310721,2,4,1467905,1310721,2,2,26,2,1595563,12,8,3,1594729,12, +8,1587671,2,1595409,12,8,9,1594729,12,8,1,1613405,14,1,1310721,2, +3,26,1310721,4,18,1310721,2,9,28,1310721,4,1310721,2,1835009,40,1613453, +16,1966081,1310721,4,1579223,12,1310721,2,3,26,4,1310721,2,4,1587557,2, +1310721,2,5,26,4,1587565,2,1,6,1,10,28,34,63,38,3, +26,1613517,16,3,1613563,14,1587569,2,1,6,1,10,28,34,63,38, +3,26,1,6,1,10,28,34,63,38,1,10,28,1613573,16,1, +1614133,14,1310721,2,5,26,2,1587559,2,10,40,1614125,16,1310721,2,5, +26,1,6,2,1,6,-1,26,5,6,4,10,8,8,1468929,1310721, +2,5,26,2,26,2,1310721,2,2,40,1613661,16,1613573,14,1310721,2, +7,26,1469953,1310721,2,5,26,2,26,2,10,3,6,10,4,8, +1310721,2,7,26,2,3,28,1,6,1,10,28,34,63,38,3, +26,1613755,16,1310721,2,5,26,2,1613313,12,8,1614121,14,1310721,2,7, +26,2,5,28,1,6,1,10,28,34,63,38,3,26,1614001,16, +1310721,2,5,26,2,1613989,14,117,32,111,98,106,101,99,116,32, +109,117,115,116,32,108,101,97,118,101,32,116,104,105,115,32, +98,108,111,99,107,32,40,98,108,111,99,107,32,118,97,108, +117,101,44,32,97,115,115,105,103,110,109,101,110,116,32,116, +111,32,116,104,101,32,101,110,99,108,111,115,105,110,103,32, +98,108,111,99,107,44,32,111,114,32,114,101,116,117,114,110, +41,0,1613807,1591963,12,8,1614121,14,1310721,2,7,26,2,7,28,1, +6,1,10,28,34,63,38,3,26,1614121,16,1310721,2,5,26,2, +1614113,14,100,32,111,98,106,101,99,116,32,119,97,115,32,110, +101,118,101,114,32,103,105,118,101,110,32,97,119,97,121,0, +1614053,1591963,12,8,1613573,14,1,1614133,14,1,1310721,2,9,26,1310721,4, +18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1614181,16,1966081,1310721,4, +1579223,12,1310721,2,4,1310721,2,2,1587557,2,40,1614423,16,1587565,2,1614249, +16,1474049,1310721,2,2,26,2,1,6,1,10,28,34,63,38,1, +10,28,1614251,14,1,1614313,16,1472001,1310721,2,2,26,2,1,6,1, +10,28,34,63,38,3,26,1,6,1,10,28,34,63,38,1, +10,28,1614315,14,1,1614389,16,1466881,1310721,2,2,26,2,1614381,14,109, +32,111,98,106,101,99,116,32,105,115,32,110,101,118,101,114, +32,117,115,101,100,0,1614335,1591343,12,8,1310721,2,1,6,2,1, +6,3,26,5,6,4,10,8,8,1614187,14,1,1614431,14,1,1310721, +2,3,26,1310721,4,18,1310721,2,7,28,1310721,4,1310721,2,1835009,40, +1614479,16,1966081,1310721,4,1579223,12,1310721,2,4,1310721,2,2,1310721,2,3, +26,4,1587769,2,1614775,16,1310721,2,3,26,2,1587557,2,40,1614775,16, +1310721,2,5,26,1558017,1310721,2,3,26,2,26,2,17,38,15,32, +10,3,6,10,4,8,1558017,1310721,2,3,26,2,26,2,33,32, +1614633,16,1310721,2,5,26,2,1,6,1,10,28,34,63,38,1, +10,28,1614635,14,1,1614673,16,1589483,2,17,40,1,6,1,10,28, +34,63,38,1,10,28,1614675,14,1,1614737,16,1589467,1589483,2,26,1310721, +2,5,26,2,10,3,6,10,4,8,1589483,1,6,2,1,6, +3,26,5,6,4,10,8,8,1310721,2,3,26,1,6,2,1, +6,3,26,5,6,4,10,8,8,1614509,14,1310721,2,3,26,1587559, +2,10,3,6,10,4,8,1310721,2,2,1614147,12,8,1587561,2,3, +1613419,12,8,1310721,2,3,26,2,1310721,2,2,40,1614975,16,1468929,1310721, +2,3,26,2,26,2,1587561,2,40,3,36,1614937,16,1469953,1310721,2, +3,26,2,26,9,10,3,6,10,4,8,1468929,1310721,2,3,26, +2,26,1587561,2,3,28,10,3,6,10,4,8,1310721,2,3,26, +1,6,2,1,6,3,26,5,6,4,10,8,8,1614823,14,1587557, +1310721,2,2,10,3,6,10,4,8,1,1615003,14,1,1310721,2,7, +26,1310721,4,18,1310721,2,7,28,1310721,4,1310721,2,1835009,40,1615051,16, +1966081,1310721,4,1579223,12,1587585,2,1310721,2,4,1587559,2,1310721,2,3,26, +4,1310721,2,5,26,1507841,1310721,2,2,26,10,3,6,10,4,8, +1310721,2,5,26,2,1,26,1587559,2,10,3,6,10,4,8,1310721, +2,5,26,2,3,26,1587557,2,10,3,6,10,4,8,1310721,2, +5,26,1310721,2,5,26,2,5,26,10,3,6,10,4,8,1310721, +2,3,26,2,1587557,2,40,1615417,16,1310721,2,5,26,2,1,26, +1469953,1310721,2,3,26,2,26,2,10,3,6,10,4,8,1310721,2, +5,26,2,3,26,1470977,1310721,2,3,26,2,26,2,10,3,6, +10,4,8,1310721,2,5,26,2,5,26,1468929,1310721,2,3,26,2, +26,2,10,3,6,10,4,8,1310721,2,5,26,1310721,2,5,26, +2,7,26,10,3,6,10,4,8,1310721,2,3,26,1,6,2, +1,6,3,26,5,6,4,10,8,8,1615199,14,1587585,1310721,2,5, +26,2,1507841,28,10,3,6,10,4,8,1587585,2,16001,40,3,36, +1615541,16,1615533,14,110,101,115,116,105,110,103,32,116,111,111,32, +100,101,101,112,32,102,111,114,32,116,104,101,32,97,110,97, +108,121,122,101,114,0,1615465,1591079,12,8,1310721,2,2,1615553,14,1, +1310721,2,7,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009, +40,1615601,16,1966081,1310721,4,1579223,12,1310721,2,4,1587585,1310721,2,2,10, +3,6,10,4,8,1,1615635,14,1,1310721,2,3,26,1310721,4,18, +1310721,2,9,28,1310721,4,1310721,2,1835009,40,1615683,16,1966081,1310721,4,1579223, +12,1310721,2,4,1507841,1310721,2,2,26,1310721,2,3,26,4,1310721,2, +3,26,2,1,26,2,1310721,2,5,26,4,1310721,2,3,26,2, +3,26,2,1310721,2,7,26,4,1310721,2,7,26,2,1587557,2,10, +40,1615807,16,1310721,2,7,26,1587557,2,10,3,6,10,4,8,1310721, +2,3,26,1310721,2,3,26,2,5,26,10,3,6,10,4,8, +1310721,2,5,26,2,1310721,2,7,26,2,40,1616065,16,1469953,1310721,2, +5,26,2,26,1310721,2,3,26,2,1,26,2,10,3,6,10, +4,8,1470977,1310721,2,5,26,2,26,1310721,2,3,26,2,3,26, +2,10,3,6,10,4,8,1468929,1310721,2,5,26,2,26,1310721,2, +3,26,2,5,26,2,10,3,6,10,4,8,1310721,2,3,26, +1310721,2,3,26,2,7,26,10,3,6,10,4,8,1310721,2,5, +26,1,6,2,1,6,3,26,5,6,4,10,8,8,1615841,14, +1,1616073,14,1,1310721,2,9,26,1310721,4,18,1310721,2,3,28,1310721, +4,1310721,2,1835009,40,1616121,16,1966081,1310721,4,1579223,12,1310721,2,4,1310721, +2,2,1,28,1,6,1,10,28,34,63,38,3,26,1616167,16, +3,1616219,14,1310721,2,2,9,28,1,6,1,10,28,34,63,38, +3,26,1,6,1,10,28,34,63,38,1,10,28,1616225,14,1, +1310721,2,3,26,1310721,4,18,1310721,2,11,28,1310721,4,1310721,2,1835009, +40,1616273,16,1966081,1310721,4,1579223,12,1310721,2,4,1507841,1310721,2,2,26, +1310721,2,3,26,4,1310721,2,3,26,2,1,26,2,1310721,2,5, +26,4,1310721,2,3,26,2,3,26,2,1310721,2,7,26,4,1310721, +2,7,26,2,1587557,2,10,40,1616397,16,1310721,2,7,26,1587557,2, +10,3,6,10,4,8,1310721,2,3,26,1310721,2,3,26,2,5, +26,10,3,6,10,4,8,1310721,2,5,26,2,1310721,2,7,26, +2,40,1617021,16,1310721,2,9,26,1469953,1310721,2,5,26,2,26,2, +10,3,6,10,4,8,1310721,2,9,26,2,1616087,12,1616557,16,1310721, +2,3,26,2,1,26,2,1616087,12,1,6,1,10,28,34,63, +38,1,10,28,1616559,14,1,1616639,16,1310721,2,3,26,2,1,26, +2,9,28,1,6,1,10,28,34,63,38,3,26,1616635,16,1469953, +1310721,2,5,26,2,26,9,10,3,6,10,4,8,1616949,14,1310721, +2,9,26,2,1310721,2,3,26,2,1,26,2,28,1,6,1, +10,28,34,63,38,1,10,28,1616699,16,3,1616777,14,1468929,1310721,2, +5,26,2,26,2,1310721,2,3,26,2,5,26,2,28,1,6, +1,10,28,34,63,38,1,10,28,1,6,1,10,28,34,63, +38,1,10,28,1616787,16,3,1616931,14,1310721,2,9,26,2,11,28, +1,6,1,10,28,34,63,38,3,26,1616907,16,1470977,1310721,2,5, +26,2,26,2,1310721,2,3,26,2,3,26,2,28,1,6,1, +10,28,34,63,38,1,10,28,1,6,1,10,28,34,63,38, +1,10,28,1616909,14,1,1,6,1,10,28,34,63,38,1,10, +28,1616949,16,1310721,2,5,26,2,1617035,14,1310721,2,3,26,1310721,2, +3,26,2,7,26,10,3,6,10,4,8,1310721,2,5,26,1, +6,2,1,6,3,26,5,6,4,10,8,8,1616431,14,3,1, +10,28,1617035,14,1,1310721,2,11,26,1310721,4,18,1310721,2,3,28, +1310721,4,1310721,2,1835009,40,1617083,16,1966081,1310721,4,1579223,12,1310721,2,4, +1310721,2,2,1,28,1,6,1,10,28,34,63,38,3,26,1617145, +16,1617139,14,101,109,112,116,121,0,1617127,1617471,14,1310721,2,2,3, +28,1,6,1,10,28,34,63,38,3,26,1617193,16,1617187,14,109, +0,1617183,1617471,14,1310721,2,2,5,28,1,6,1,10,28,34,63, +38,3,26,1617241,16,1617235,14,117,0,1617231,1617471,14,1310721,2,2,7, +28,1,6,1,10,28,34,63,38,3,26,1617289,16,1617283,14,100, +0,1617279,1617471,14,1310721,2,2,9,28,1,6,1,10,28,34,63, +38,3,26,1617355,16,1617349,14,103,105,118,101,110,32,97,119,97, +121,0,1617327,1617471,14,1310721,2,2,11,28,1,6,1,10,28,34, +63,38,3,26,1617441,16,1617435,14,111,119,110,101,100,32,98,121, +32,97,32,99,111,110,116,97,105,110,101,114,0,1617393,1617471,14, +1617463,14,98,111,114,114,111,119,101,100,0,1617445,1617471,14,1,1310721, +2,3,26,1310721,4,18,1310721,2,7,28,1310721,4,1310721,2,1835009,40, +1617519,16,1966081,1310721,4,1579223,12,1310721,2,5,26,4,1310721,2,3,26, +4,1310721,2,4,1587565,2,1,6,1,10,28,34,63,38,3,26, +1617579,16,3,1617605,14,1590585,2,1,6,1,10,28,34,63,38,1, +10,28,1617615,16,3,1617641,14,1587703,2,1,6,1,10,28,34,63, +38,1,10,28,1617651,16,1,1617871,14,1590587,12,8,1466881,1310721,2,2, +26,2,1573495,12,8,1617685,14,58,32,0,1617679,1573495,12,8,1310721,2, +5,26,2,1573495,12,8,1617757,14,32,40,111,110,101,32,112,97, +116,104,32,108,101,97,118,101,115,32,105,116,32,0,1617713,1573495, +12,8,1310721,2,3,26,2,1617049,12,1573495,12,8,1617811,14,44,32, +97,110,111,116,104,101,114,32,0,1617789,1573495,12,8,1469953,1310721,2, +2,26,2,1617049,12,1573495,12,8,1617849,14,41,0,1617845,1573495,12,8, +1590731,12,8,1,1617871,14,1,1310721,2,7,26,1310721,4,18,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1310721,2,5,28,1310721,4,1310721,2,1835009,40,1618051,16,1966081,1310721,4,1579223, +12,1310721,2,3,26,4,1310721,2,4,1310721,2,3,26,2,1310721,2, +2,3,26,2,40,3,36,1618105,16,1,1618153,14,1310721,2,2,5, +7,1310721,2,3,26,2,1310721,2,2,1,26,2,28,30,26,26, +2,1618153,14,1,1310721,2,5,26,1310721,4,18,1310721,2,5,28,1310721, +4,1310721,2,1835009,40,1618201,16,1966081,1310721,4,1579223,12,1310721,2,3,26, +4,1310721,2,4,1310721,2,2,7,7,1310721,2,3,26,2,1310721,2, +2,1,26,2,28,30,26,26,2,1618265,14,1,1310721,2,5,26, +1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,1835009,40,1618313,16,1966081, +1310721,4,1579223,12,1310721,2,3,26,4,1310721,2,4,1310721,2,2,9, +7,1310721,2,3,26,2,1310721,2,2,1,26,2,28,30,26,26, +2,1618377,14,1,1310721,2,5,26,1310721,4,18,1310721,2,9,28,1310721, +4,1310721,2,1835009,40,1618425,16,1966081,1310721,4,1579223,12,1310721,2,4,1567233, +1617885,2,26,1310721,2,3,26,4,1587559,2,1310721,2,5,26,4,1617885, +2,7,26,7,1587557,2,1587559,2,28,30,26,1572353,1567233,28,40,3, +36,1618581,16,1618573,14,116,111,111,32,109,97,110,121,32,98,114, +101,97,107,115,32,102,111,114,32,116,104,101,32,97,110,97, +108,121,122,101,114,0,1618507,1591079,12,8,1310721,2,3,26,2,1, +26,1310721,2,2,10,3,6,10,4,8,1310721,2,3,26,2,3, +26,1587559,2,10,3,6,10,4,8,1310721,2,3,26,2,5,26, +1587557,2,10,3,6,10,4,8,1310721,2,7,26,1310721,2,3,26, +2,7,26,10,3,6,10,4,8,1310721,2,5,26,2,1587557,2, +40,1618925,16,1310721,2,7,26,2,1,26,1469953,1310721,2,5,26,2, +26,2,10,3,6,10,4,8,1310721,2,7,26,2,3,26,1470977, +1310721,2,5,26,2,26,2,10,3,6,10,4,8,1310721,2,7, +26,2,5,26,1468929,1310721,2,5,26,2,26,2,10,3,6,10, +4,8,1310721,2,7,26,1310721,2,7,26,2,7,26,10,3,6, +10,4,8,1310721,2,5,26,1,6,2,1,6,3,26,5,6, +4,10,8,8,1618707,14,1617885,1310721,2,7,26,2,1567233,28,10,3, +6,10,4,8,1310721,2,3,26,2,1618969,14,1,1310721,2,9,26, +1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1619017,16,1966081, +1310721,4,1579223,12,1310721,2,4,1618015,2,129,40,3,36,1619115,16,1619107, +14,116,111,111,32,109,97,110,121,32,112,97,116,104,115,32, +102,111,114,32,116,104,101,32,97,110,97,108,121,122,101,114, +0,1619043,1591079,12,8,1617887,1618015,2,26,1310721,2,2,10,3,6,10, +4,8,1618015,1,6,2,1,6,3,26,5,6,4,10,8,8, +1,1619177,14,1,1310721,2,3,26,1310721,4,18,1310721,2,11,28,1310721, +4,1310721,2,1835009,40,1619225,16,1966081,1310721,4,1579223,12,1310721,2,5,26, +4,1310721,2,3,26,4,1310721,2,4,1310721,2,2,1310721,2,5,26, +2,1618017,12,1310721,2,7,26,4,1310721,2,3,26,2,1310721,2,5, +26,2,1618017,12,1310721,2,9,26,4,1310721,2,7,26,2,1616087,12, +1619373,16,1310721,2,9,26,2,1616087,12,1,6,1,10,28,34,63, +38,1,10,28,1619375,14,1,1619385,16,3,1619697,14,1310721,2,7,26, +2,1310721,2,9,26,2,28,1,6,1,10,28,34,63,38,3, +26,1619523,16,1310721,2,2,1310721,2,5,26,2,1618279,12,1310721,2,3, +26,2,1310721,2,5,26,2,1618279,12,28,1,6,1,10,28,34, +63,38,3,26,1,6,1,10,28,34,63,38,1,10,28,1619525, +14,1,1619689,16,1310721,2,7,26,2,11,28,1,6,1,10,28, +34,63,38,1,10,28,1619575,16,3,1619663,14,1310721,2,2,1310721,2, +5,26,2,1618167,12,1310721,2,3,26,2,1310721,2,5,26,2,1618167, +12,28,1,6,1,10,28,34,63,38,3,26,1,6,1,10, +28,34,63,38,1,10,28,1,6,1,10,28,34,63,38,1, +10,28,1619691,14,1,1619697,14,1,1310721,2,11,26,1310721,4,18,1310721, +2,21,28,1310721,4,1310721,2,1835009,40,1619745,16,1966081,1310721,4,1579223,12, +1310721,2,3,26,4,1310721,2,4,1587559,2,1310721,2,5,26,4,1618015, +2,1,6,1,10,28,34,63,38,3,26,1619809,16,1,1621281,14, +1310721,2,5,26,2,1310721,2,2,40,1621273,16,1310721,2,9,26,1617887, +1,26,2,10,3,6,10,4,8,1310721,2,13,26,1310721,2,9, +26,2,1310721,2,5,26,2,1618017,12,10,3,6,10,4,8,1310721, +2,15,26,1,10,3,6,10,4,8,1310721,2,17,26,3,10, +3,6,10,4,8,1310721,2,19,26,3,1,10,28,10,3,6, +10,4,8,1310721,2,7,26,1,10,3,6,10,4,8,1310721,2, +7,26,2,1618015,2,40,1620451,16,1310721,2,11,26,1617887,1310721,2,7, +26,2,26,2,1310721,2,5,26,2,1618017,12,10,3,6,10,4, +8,1310721,2,11,26,2,9,28,1,6,1,10,28,34,63,38, +3,26,1620111,16,3,1620167,14,1310721,2,11,26,2,7,28,1,6, +1,10,28,34,63,38,3,26,1,6,1,10,28,34,63,38, +1,10,28,1620193,16,1310721,2,15,26,3,10,3,6,10,4,8, +1310721,2,11,26,2,5,28,1,6,1,10,28,34,63,38,3, +26,1620237,16,3,1620293,14,1310721,2,11,26,2,13,28,1,6,1, +10,28,34,63,38,3,26,1,6,1,10,28,34,63,38,1, +10,28,1620319,16,1310721,2,17,26,1,10,3,6,10,4,8,1310721, +2,9,26,2,1617887,1310721,2,7,26,2,26,2,1310721,2,5,26, +2,1619191,12,1,6,1,10,28,34,63,38,3,26,1620413,16,1310721, +2,19,26,1310721,2,7,26,2,10,3,6,10,4,8,1310721,2, +7,26,1,6,2,1,6,3,26,5,6,4,10,8,8,1619997, +14,1469953,1310721,2,5,26,2,26,1310721,2,13,26,2,10,3,6, +10,4,8,1470977,1310721,2,5,26,2,26,1310721,2,9,26,2,1310721, +2,5,26,2,1618167,12,10,3,6,10,4,8,1468929,1310721,2,5, +26,2,26,1310721,2,9,26,2,1310721,2,5,26,2,1618279,12,10, +3,6,10,4,8,1310721,2,19,26,2,1,40,1620793,16,1310721,2, +13,26,2,1616087,12,1620789,16,1310721,2,7,26,1,10,3,6,10, +4,8,1310721,2,7,26,2,1618015,2,40,1620789,16,1617887,1310721,2,7, +26,2,26,2,1310721,2,5,26,2,1618017,12,9,28,1,6,1, +10,28,34,63,38,3,26,1620751,16,1469953,1310721,2,5,26,2,26, +9,10,3,6,10,4,8,1310721,2,7,26,1,6,2,1,6, +3,26,5,6,4,10,8,8,1620645,14,1621235,14,1310721,2,15,26, +2,1620843,16,1310721,2,17,26,2,1,6,1,10,28,34,63,38, +1,10,28,1620845,14,1,1620881,16,1469953,1310721,2,5,26,2,26,9, +10,3,6,10,4,8,1621235,14,1587565,2,1620939,16,1590585,2,1,6, +1,10,28,34,63,38,3,26,1,6,1,10,28,34,63,38, +1,10,28,1620941,14,1,1620995,16,1587703,2,1,6,1,10,28,34, +63,38,3,26,1,6,1,10,28,34,63,38,1,10,28,1620997, +14,1,1621235,16,1590587,12,8,1466881,1310721,2,5,26,2,26,2,1573495, +12,8,1621039,14,58,32,0,1621033,1573495,12,8,1310721,2,3,26,2, +1573495,12,8,1621111,14,32,40,111,110,101,32,112,97,116,104,32, +108,101,97,118,101,115,32,105,116,32,0,1621067,1573495,12,8,1310721, +2,13,26,2,1617049,12,1573495,12,8,1621165,14,44,32,97,110,111, +116,104,101,114,32,0,1621143,1573495,12,8,1617887,1310721,2,19,26,2, +26,2,1310721,2,5,26,2,1618017,12,1617049,12,1573495,12,8,1621221,14, +41,0,1621217,1573495,12,8,1590731,12,8,1310721,2,5,26,1,6,2, +1,6,3,26,5,6,4,10,8,8,1619809,14,1,1621281,14,1, +1310721,2,21,26,1310721,4,18,1310721,2,13,28,1310721,4,1310721,2,1835009, +40,1621329,16,1966081,1310721,4,1579223,12,1310721,2,5,26,4,1310721,2,3, +26,4,1310721,2,4,1587559,2,1310721,2,7,26,4,1,1310721,2,9, +26,4,1587565,2,1,6,1,10,28,34,63,38,3,26,1621415,16, +1,1621775,14,1310721,2,7,26,2,1310721,2,3,26,2,40,1621759,16, +1310721,2,11,26,1310721,2,2,1310721,2,7,26,2,1618017,12,10,3, +6,10,4,8,1469953,1310721,2,7,26,2,26,2,9,28,1,6, +1,10,28,34,63,38,3,26,1621651,16,1310721,2,11,26,2,3, +28,1,6,1,10,28,34,63,38,3,26,1621569,16,3,1621625,14, +1310721,2,11,26,2,7,28,1,6,1,10,28,34,63,38,3, +26,1,6,1,10,28,34,63,38,1,10,28,1,6,1,10, +28,34,63,38,1,10,28,1621653,14,1,1621721,16,1310721,2,9,26, +1,6,2,1,6,3,26,5,6,4,10,8,8,1310721,2,5, +26,2,1621721,16,1310721,2,7,26,2,1613313,12,8,1310721,2,7,26, +1,6,2,1,6,3,26,5,6,4,10,8,8,1621415,14,1310721, +2,9,26,2,1621775,14,1,1310721,2,13,26,1310721,4,18,1310721,2, +11,28,1310721,4,1310721,2,1835009,40,1621823,16,1966081,1310721,4,1579223,12,1310721, +2,4,1507841,1310721,2,2,26,1310721,2,3,26,4,1310721,2,3,26, +2,1,26,2,1310721,2,5,26,4,1310721,2,3,26,2,3,26, +2,1310721,2,7,26,4,1587565,2,1,6,1,10,28,34,63,38, +3,26,1621935,16,3,1621981,14,1587569,2,1,6,1,10,28,34,63, +38,3,26,1,6,1,10,28,34,63,38,1,10,28,1621991,16, +1,1623357,14,1310721,2,7,26,2,1587557,2,10,40,1622037,16,1310721,2, +7,26,1587557,2,10,3,6,10,4,8,1310721,2,5,26,2,1310721, +2,7,26,2,40,1623349,16,1310721,2,9,26,1469953,1310721,2,5,26, +2,26,2,10,3,6,10,4,8,1310721,2,9,26,2,1616087,12, +1622171,16,1310721,2,3,26,2,1310721,2,5,26,2,1618017,12,1616087,12, +1,6,1,10,28,34,63,38,1,10,28,1622173,14,1,1622261,16, +1310721,2,3,26,2,1310721,2,5,26,2,1618017,12,9,28,1,6, +1,10,28,34,63,38,3,26,1622257,16,1469953,1310721,2,5,26,2, +26,9,10,3,6,10,4,8,1623311,14,1310721,2,9,26,2,1310721, +2,3,26,2,1310721,2,5,26,2,1618017,12,28,1,6,1,10, +28,34,63,38,1,10,28,1622329,16,3,1622415,14,1468929,1310721,2,5, +26,2,26,2,1310721,2,3,26,2,1310721,2,5,26,2,1618279,12, +28,1,6,1,10,28,34,63,38,1,10,28,1,6,1,10, +28,34,63,38,1,10,28,1622425,16,3,1622577,14,1310721,2,9,26, +2,11,28,1,6,1,10,28,34,63,38,3,26,1622553,16,1470977, +1310721,2,5,26,2,26,2,1310721,2,3,26,2,1310721,2,5,26, +2,1618167,12,28,1,6,1,10,28,34,63,38,1,10,28,1, +6,1,10,28,34,63,38,1,10,28,1622555,14,1,1,6,1, +10,28,34,63,38,1,10,28,1623311,16,1310721,2,9,26,2,9, +28,1,6,1,10,28,34,63,38,3,26,1622625,16,3,1622681,14, +1310721,2,9,26,2,7,28,1,6,1,10,28,34,63,38,3, +26,1,6,1,10,28,34,63,38,1,10,28,1622915,16,1310721,2, +5,26,2,1622903,14,105,115,32,103,105,118,101,110,32,97,119, +97,121,32,105,110,115,105,100,101,32,97,32,108,111,111,112, +32,116,104,97,116,32,99,111,109,101,115,32,98,97,99,107, +32,97,114,111,117,110,100,32,116,111,32,105,116,59,32,103, +105,118,101,32,105,116,32,97,119,97,121,32,111,110,32,97, +32,112,97,116,104,32,116,104,97,116,32,108,101,97,118,101, +115,32,116,104,101,32,108,111,111,112,0,1622699,1591963,12,8,1623311, +14,1590585,2,1,6,1,10,28,34,63,38,3,26,1622993,16,1587703, +2,1,6,1,10,28,34,63,38,3,26,1,6,1,10,28, +34,63,38,1,10,28,1622995,14,1,1623311,16,1590587,12,8,1466881,1310721, +2,5,26,2,26,2,1573495,12,8,1623205,14,58,32,99,104,97, +110,103,101,115,32,115,116,97,116,101,32,105,110,115,105,100, +101,32,97,32,108,111,111,112,59,32,101,97,99,104,32,112, +97,115,115,32,104,97,115,32,116,111,32,108,101,97,118,101, +32,105,116,32,97,115,32,105,116,32,102,111,117,110,100,32, +105,116,32,40,105,116,32,119,101,110,116,32,102,114,111,109, +32,0,1623031,1573495,12,8,1310721,2,3,26,2,1310721,2,5,26,2, +1618017,12,1617049,12,1573495,12,8,1623261,14,32,116,111,32,0,1623251,1573495, +12,8,1310721,2,9,26,2,1617049,12,1573495,12,8,1623297,14,41,0, +1623293,1573495,12,8,1590731,12,8,1310721,2,5,26,1,6,2,1,6, +3,26,5,6,4,10,8,8,1622037,14,1,1623357,14,1,1310721,2, +11,26,1310721,4,18,1310721,2,19,28,1310721,4,1310721,2,1835009,40,1623405, +16,1966081,1310721,4,1579223,12,1310721,2,11,26,4,1310721,2,9,26,4, +1310721,2,7,26,4,1310721,2,5,26,4,1310721,2,3,26,4,1310721, +2,4,1310721,2,13,26,1615017,12,10,3,6,10,4,8,1618015,1, +10,3,6,10,4,8,1310721,2,3,26,2,1623531,16,1507841,1310721,2, +2,26,1618983,12,8,1310721,2,7,26,2,1623565,16,1507841,1310721,2,13, +26,2,26,1618983,12,8,1310721,2,9,26,2,1310721,2,11,26,2, +1619711,12,8,1310721,2,7,26,2,1623637,16,1507841,1310721,2,13,26,2, +26,1310721,2,9,26,2,3,1621295,12,8,1310721,2,15,26,1,10, +3,6,10,4,8,1310721,2,3,26,2,1623719,16,1310721,2,15,26, +1507841,1310721,2,2,26,1310721,2,9,26,2,1,1621295,12,10,3,6, +10,4,8,1310721,2,15,26,2,1623837,16,1310721,2,17,26,1595029,12, +10,3,6,10,4,8,15,1594729,12,8,1310721,2,5,26,2,1587549, +2,1594811,12,8,1507841,1310721,2,2,26,1310721,2,9,26,2,3,1621295, +12,8,1310721,2,17,26,2,1587549,2,1594811,12,8,1623857,14,1310721,2, +5,26,2,1587549,2,1594811,12,8,1587569,1310721,2,3,26,2,1623879,16, +3,1623911,14,1310721,2,7,26,2,1,6,1,10,28,34,63,38, +1,10,28,10,3,6,10,4,8,1,1623931,14,1,1310721,2,19, +26,1310721,4,18,1310721,2,9,28,1310721,4,1310721,2,1835009,40,1623979,16, +1966081,1310721,4,1579223,12,1310721,2,3,26,4,1310721,2,4,1587565,2,1, +6,1,10,28,34,63,38,3,26,1624033,16,1310721,2,2,1625003,14, +1310721,2,2,5,40,1624149,16,1624131,14,111,110,108,121,32,97,32, +110,97,109,101,100,32,111,119,110,101,100,32,111,98,106,101, +99,116,32,99,97,110,32,98,101,32,114,101,99,97,115,116, +0,1624051,1591845,12,8,1310721,2,2,1625003,14,1310721,2,5,26,1310721,2, +2,5,28,10,3,6,10,4,8,1310721,2,7,26,1469953,1310721,2, +5,26,2,26,2,10,3,6,10,4,8,1472001,1310721,2,5,26, +2,26,3,10,3,6,10,4,8,1310721,2,7,26,2,11,28, +1,6,1,10,28,34,63,38,3,26,1624413,16,1310721,2,5,26, +2,1624401,14,98,101,108,111,110,103,115,32,116,111,32,97,32, +99,111,110,116,97,105,110,101,114,59,32,114,101,99,97,115, +116,32,116,104,101,32,99,111,110,116,97,105,110,101,114,32, +105,110,115,116,101,97,100,0,1624295,1591963,12,8,1624991,14,1310721,2, +7,26,2,13,28,1,6,1,10,28,34,63,38,3,26,1624541, +16,1310721,2,5,26,2,1624529,14,105,115,32,98,111,114,114,111, +119,101,100,32,97,110,100,32,99,97,110,39,116,32,98,101, +32,114,101,99,97,115,116,0,1624465,1591963,12,8,1624991,14,1310721,2, +7,26,2,1,28,1,6,1,10,28,34,63,38,3,26,1624649, +16,1310721,2,5,26,2,1624637,14,105,115,110,39,116,32,97,110, +32,111,119,110,101,100,32,111,98,106,101,99,116,0,1624593,1591963, +12,8,1624991,14,1310721,2,7,26,2,9,28,1,6,1,10,28, +34,63,38,3,26,1624771,16,1310721,2,5,26,2,1624759,14,117,115, +101,100,32,97,102,116,101,114,32,105,116,32,119,97,115,32, +103,105,118,101,110,32,97,119,97,121,0,1624701,1591963,12,8,1624991, +14,1310721,2,3,26,2,3,28,1,6,1,10,28,34,63,38, +3,26,1624871,16,1310721,2,7,26,2,3,28,1,6,1,10,28, +34,63,38,1,10,28,1,6,1,10,28,34,63,38,1,10, +28,1624873,14,1,1624955,16,1310721,2,5,26,2,1624943,14,99,97,110, +39,116,32,98,101,32,114,101,99,97,115,116,32,98,97,99, +107,32,116,111,32,109,0,1624891,1591963,12,8,1624991,14,1469953,1310721,2, +5,26,2,26,1310721,2,3,26,2,10,3,6,10,4,8,1310721, +2,2,1625003,14,1,1310721,2,9,26,1310721,4,18,1310721,2,9,28, +1310721,4,1310721,2,1835009,40,1625051,16,1966081,1310721,4,1579223,12,1310721,2,3, +26,4,1310721,2,4,1587565,2,1,6,1,10,28,34,63,38,3, +26,1625101,16,1,1626535,14,1310721,2,2,3,28,1,6,1,10,28, +34,63,38,3,26,1625345,16,1310721,2,3,26,2,5,28,1,6, +1,10,28,34,63,38,1,10,28,1625339,16,1625331,14,97,32,110, +101,119,32,111,98,106,101,99,116,32,99,97,110,32,111,110, +108,121,32,103,111,32,115,116,114,97,105,103,104,116,32,116, +111,32,97,32,100,32,112,97,114,97,109,101,116,101,114,59, +32,98,105,110,100,32,105,116,32,116,111,32,97,32,110,97, +109,101,32,102,105,114,115,116,0,1625179,1591845,12,8,1,1626535,14, +1310721,2,2,5,40,1625365,16,3,1625421,14,1310721,2,2,5,28,1612447, +12,1,6,1,10,28,34,63,38,3,26,1,6,1,10,28, +34,63,38,1,10,28,1625431,16,1,1626535,14,1310721,2,5,26,1310721, +2,2,5,28,10,3,6,10,4,8,1310721,2,7,26,1469953,1310721, +2,5,26,2,26,2,10,3,6,10,4,8,1469953,1310721,2,5, +26,2,1612309,12,26,2,9,28,1,6,1,10,28,34,63,38, +3,26,1625631,16,1310721,2,5,26,2,1625617,14,117,115,101,100,32, +97,102,116,101,114,32,105,116,32,119,97,115,32,103,105,118, +101,110,32,97,119,97,121,0,1625559,1591963,12,8,1,1626535,14,1310721, +2,3,26,2,3,10,40,3,36,1625859,16,1310721,2,7,26,2, +7,28,1,6,1,10,28,34,63,38,3,26,1625837,16,1310721,2, +5,26,2,1625823,14,105,115,32,100,59,32,112,97,115,115,32, +105,116,32,116,111,32,97,32,100,32,112,97,114,97,109,101, +116,101,114,32,40,108,101,110,100,105,110,103,32,105,116,32, +119,111,117,108,100,32,108,111,115,101,32,105,116,41,0,1625707, +1591963,12,8,1,1626535,14,1310721,2,5,26,2,1612545,12,8,1,1626535, +14,1310721,2,7,26,2,7,28,1,6,1,10,28,34,63,38, +3,26,1625959,16,1469953,1310721,2,5,26,2,26,9,10,3,6,10, +4,8,1472001,1310721,2,5,26,2,26,3,10,3,6,10,4,8, +1,1626535,14,1310721,2,7,26,2,3,28,1,6,1,10,28,34, +63,38,3,26,1626087,16,1310721,2,5,26,2,1626075,14,105,115,32, +109,59,32,119,114,105,116,101,32,100,42,120,32,116,111,32, +103,105,118,101,32,105,116,32,97,119,97,121,0,1626011,1591963,12, +8,1626527,14,1310721,2,7,26,2,5,28,1,6,1,10,28,34, +63,38,3,26,1626263,16,1310721,2,5,26,2,1626251,14,105,115,32, +117,59,32,119,114,105,116,101,32,100,42,120,32,116,111,32, +103,105,118,101,32,105,116,32,97,119,97,121,32,105,110,115, +116,101,97,100,32,111,102,32,114,101,116,117,114,110,105,110, +103,32,105,116,0,1626139,1591963,12,8,1626527,14,1310721,2,7,26,2, +11,28,1,6,1,10,28,34,63,38,3,26,1626433,16,1310721,2, +5,26,2,1626421,14,98,101,108,111,110,103,115,32,116,111,32, +97,32,99,111,110,116,97,105,110,101,114,32,97,110,100,32, +99,97,110,39,116,32,98,101,32,103,105,118,101,110,32,97, +119,97,121,32,97,108,111,110,101,0,1626315,1591963,12,8,1626527,14, +1310721,2,5,26,2,1626519,14,105,115,32,98,111,114,114,111,119, +101,100,32,97,110,100,32,99,97,110,39,116,32,98,101,32, +103,105,118,101,110,32,97,119,97,121,0,1626447,1591963,12,8,1, +1626535,14,1,1310721,2,9,26,1310721,4,18,1310721,2,3,28,1310721,4, +1310721,2,1835009,40,1626583,16,1966081,1310721,4,1579223,12,1310721,2,4,1467905,1310721, +2,2,26,2,1595563,12,8,3,1594729,12,8,1556993,1310721,2,2,26, +2,1,26,2,23,28,1,6,1,10,28,34,63,38,3,26, +1626685,16,5,1594643,12,8,27,1594729,12,8,3,1594729,12,8,1587673,2, +1595409,12,8,1587769,2,1626737,16,1587699,2,1658673,12,1,6,1,10,28, +34,63,38,1,10,28,1626739,14,1,1626763,16,1587699,2,1594643,12,8, +1590517,2,1595409,12,8,1,1626771,14,1,1310721,2,3,26,1310721,4,18, +1310721,2,15,28,1310721,4,1310721,2,1835009,40,1626819,16,1966081,1310721,4,1579223, +12,1310721,2,5,26,4,1310721,2,3,26,4,1310721,2,4,1587565,2, +1,6,1,10,28,34,63,38,3,26,1626879,16,1,1630325,14,1310721, +2,9,26,3,1,10,28,10,3,6,10,4,8,1310721,2,5, +26,2,5,40,3,36,1626973,16,1310721,2,5,26,2,5,28,1612447, +12,1,6,1,10,28,34,63,38,1,10,28,1626975,14,1,1627013, +16,1310721,2,9,26,1310721,2,5,26,2,5,28,10,3,6,10, +4,8,1310721,2,9,26,2,1,40,3,36,1627105,16,1469953,1310721,2, +9,26,2,1612309,12,26,2,9,28,1,6,1,10,28,34,63, +38,3,26,1,6,1,10,28,34,63,38,1,10,28,1627107,14, +1,1627197,16,1310721,2,9,26,2,1627183,14,117,115,101,100,32,97, +102,116,101,114,32,105,116,32,119,97,115,32,103,105,118,101, +110,32,97,119,97,121,0,1627125,1591963,12,8,1,1630325,14,1310721,2, +2,3,28,1,6,1,10,28,34,63,38,3,26,1628669,16,1310721, +2,7,26,1469953,1310721,2,3,26,2,26,2,10,3,6,10,4, +8,1310721,2,9,26,2,1,40,1627347,16,1310721,2,5,26,2,3, +28,1,6,1,10,28,34,63,38,1,10,28,1,6,1,10, +28,34,63,38,1,10,28,1627349,14,1,1627553,16,1310721,2,7,26, +2,1616087,12,1,6,1,10,28,34,63,38,3,26,1627519,16,1310721, +2,3,26,2,1627507,14,115,116,105,108,108,32,111,119,110,115, +32,97,110,32,111,98,106,101,99,116,59,32,111,118,101,114, +119,114,105,116,105,110,103,32,105,116,32,119,111,117,108,100, +32,108,101,97,107,32,105,116,0,1627405,1591963,12,8,1627547,14,1469953, +1310721,2,3,26,2,26,1,10,3,6,10,4,8,1,1630325,14, +1310721,2,7,26,2,1616087,12,1,6,1,10,28,34,63,38,3, +26,1627721,16,1310721,2,3,26,2,1627707,14,115,116,105,108,108,32, +111,119,110,115,32,97,110,32,111,98,106,101,99,116,59,32, +111,118,101,114,119,114,105,116,105,110,103,32,105,116,32,119, +111,117,108,100,32,108,101,97,107,32,105,116,0,1627605,1591963,12, +8,1,1630325,14,1310721,2,5,26,2,3,28,1,6,1,10,28, +34,63,38,3,26,1627919,16,1469953,1310721,2,3,26,2,26,3,10, +3,6,10,4,8,1472001,1310721,2,3,26,2,26,1,10,3,6, +10,4,8,1474049,1310721,2,3,26,2,26,3,10,3,6,10,4, +8,1558017,1310721,2,3,26,2,26,2,31,32,1627913,16,1469953,1310721,2, +3,26,2,26,1558017,1310721,2,3,26,2,26,2,31,32,10,3, +6,10,4,8,1,1630325,14,1310721,2,13,26,1469953,1310721,2,9,26, +2,26,2,10,3,6,10,4,8,1310721,2,13,26,2,5,28, +1,6,1,10,28,34,63,38,3,26,1628077,16,1468929,1310721,2,3, +26,2,26,2,1468929,1310721,2,9,26,2,26,2,3,28,28,1, +6,1,10,28,34,63,38,3,26,1,6,1,10,28,34,63, +38,1,10,28,1628079,14,1,1628299,16,1469953,1310721,2,3,26,2,26, +3,10,3,6,10,4,8,1472001,1310721,2,3,26,2,26,1,10, +3,6,10,4,8,1474049,1310721,2,3,26,2,26,3,10,3,6, +10,4,8,1469953,1310721,2,9,26,2,26,9,10,3,6,10,4, +8,1472001,1310721,2,9,26,2,26,3,10,3,6,10,4,8,1558017, +1310721,2,3,26,2,26,2,31,32,1628293,16,1469953,1310721,2,3,26, +2,26,1558017,1310721,2,3,26,2,26,2,31,32,10,3,6,10, +4,8,1,1630325,14,1310721,2,13,26,2,5,28,1,6,1,10, +28,34,63,38,3,26,1628519,16,1310721,2,9,26,2,1628507,14,105, +115,32,117,59,32,105,116,32,99,97,110,32,111,110,108,121, +32,98,101,32,104,97,110,100,101,100,32,116,111,32,97,32, +118,97,114,105,97,98,108,101,32,111,102,32,116,104,101,32, +101,110,99,108,111,115,105,110,103,32,98,108,111,99,107,44, +32,111,114,32,114,101,116,117,114,110,101,100,0,1628351,1591963,12, +8,1628663,14,1310721,2,9,26,2,1628655,14,105,115,32,97,110,32, +111,119,110,101,100,32,111,98,106,101,99,116,32,97,110,100, +32,99,97,110,39,116,32,98,101,32,99,111,112,105,101,100, +32,105,110,116,111,32,97,110,111,116,104,101,114,32,118,97, +114,105,97,98,108,101,0,1628533,1591963,12,8,1,1630325,14,1310721,2, +2,7,28,1,6,1,10,28,34,63,38,3,26,1629993,16,1310721, +2,11,26,1310721,2,3,26,2,1612309,12,10,3,6,10,4,8, +1310721,2,7,26,1469953,1310721,2,11,26,2,26,2,10,3,6,10, +4,8,1310721,2,3,26,2,1612545,12,8,1310721,2,5,26,2,3, +28,1,6,1,10,28,34,63,38,3,26,1628849,16,1310721,2,3, +26,2,1626549,12,8,1,1630325,14,1310721,2,9,26,2,1,40,1628873, +16,1,1630325,14,1310721,2,13,26,1469953,1310721,2,9,26,2,26,2, +10,3,6,10,4,8,1310721,2,13,26,2,11,28,1,6,1, +10,28,34,63,38,3,26,1629035,16,1310721,2,9,26,2,1629023,14, +97,108,114,101,97,100,121,32,98,101,108,111,110,103,115,32, +116,111,32,97,32,99,111,110,116,97,105,110,101,114,0,1628961, +1591963,12,8,1629987,14,1310721,2,13,26,2,13,28,1,6,1,10, +28,34,63,38,3,26,1629163,16,1310721,2,9,26,2,1629151,14,105, +115,32,98,111,114,114,111,119,101,100,32,97,110,100,32,99, +97,110,39,116,32,98,101,32,115,116,111,114,101,100,0,1629087, +1591963,12,8,1629987,14,1310721,2,7,26,2,13,28,1,6,1,10, +28,34,63,38,3,26,1629489,16,1310721,2,13,26,2,5,28,1, +6,1,10,28,34,63,38,3,26,1629413,16,1310721,2,9,26,2, +1629401,14,105,115,32,117,59,32,105,116,32,104,97,115,32,116, +111,32,108,101,97,118,101,32,116,104,114,111,117,103,104,32, +116,104,101,32,98,108,111,99,107,32,97,98,111,118,101,44, +32,110,111,116,32,105,110,116,111,32,97,32,98,111,114,114, +111,119,101,100,32,111,98,106,101,99,116,0,1629253,1591963,12,8, +1629485,14,1469953,1310721,2,9,26,2,26,9,10,3,6,10,4,8, +1472001,1310721,2,9,26,2,26,3,10,3,6,10,4,8,1310721,2, +3,26,2,1626549,12,8,1629987,14,1310721,2,13,26,2,1310721,2,7, +26,2,28,1,6,1,10,28,34,63,38,1,10,28,1629695,16, +1310721,2,9,26,2,1629683,14,105,116,115,32,116,97,103,32,100, +111,101,115,110,39,116,32,109,97,116,99,104,32,105,116,115, +32,99,111,110,116,97,105,110,101,114,39,115,32,40,98,111, +116,104,32,104,97,118,101,32,116,111,32,98,101,32,109,44, +32,117,32,111,114,32,100,41,0,1629551,1591963,12,8,1629987,14,1468929, +1310721,2,9,26,2,26,2,1468929,1310721,2,11,26,2,26,2,40, +1629879,16,1310721,2,9,26,2,1629867,14,108,105,118,101,115,32,105, +110,32,97,110,32,111,117,116,101,114,32,98,108,111,99,107, +32,116,104,97,110,32,116,104,101,32,99,111,110,116,97,105, +110,101,114,32,105,116,39,115,32,115,116,111,114,101,100,32, +105,110,116,111,0,1629747,1591963,12,8,1629987,14,1469953,1310721,2,9,26, +2,26,11,10,3,6,10,4,8,1470977,1310721,2,9,26,2,26, +1310721,2,3,26,2,10,3,6,10,4,8,1472001,1310721,2,9,26, +2,26,3,10,3,6,10,4,8,1310721,2,3,26,2,1626549,12, +8,1,1630325,14,1310721,2,9,26,2,1,40,3,36,1630317,16,1469953, +1310721,2,9,26,2,26,2,7,28,1,6,1,10,28,34,63, +38,3,26,1630119,16,1469953,1310721,2,9,26,2,26,9,10,3,6, +10,4,8,1472001,1310721,2,9,26,2,26,3,10,3,6,10,4, +8,1630317,14,1310721,2,9,26,2,1630309,14,109,117,115,116,32,98, +101,32,100,32,116,111,32,98,101,32,115,116,111,114,101,100, +32,105,110,32,97,32,103,108,111,98,97,108,32,111,114,32, +105,110,32,117,110,116,114,97,99,107,101,100,32,109,101,109, +111,114,121,32,40,116,104,101,32,99,111,108,108,101,99,116, +111,114,32,116,97,107,101,115,32,105,116,32,111,118,101,114, +41,0,1630133,1591963,12,8,1,1630325,14,1,1310721,2,15,26,1310721,4, +18,1310721,2,5,28,1310721,4,1310721,2,1835009,40,1630373,16,1966081,1310721,4, +1579223,12,1310721,2,4,1587565,2,1,6,1,10,28,34,63,38,3, +26,1630413,16,1,1631339,14,1310721,2,3,26,3,1,10,28,10,3, +6,10,4,8,1310721,2,2,5,40,3,36,1630499,16,1310721,2,2, +5,28,1612447,12,1,6,1,10,28,34,63,38,1,10,28,1630501, +14,1,1630535,16,1310721,2,3,26,1310721,2,2,5,28,10,3,6, +10,4,8,1310721,2,3,26,2,1,40,3,36,1630627,16,1469953,1310721, +2,3,26,2,1612309,12,26,2,9,28,1,6,1,10,28,34, +63,38,3,26,1,6,1,10,28,34,63,38,1,10,28,1630629, +14,1,1630719,16,1310721,2,3,26,2,1630705,14,117,115,101,100,32, +97,102,116,101,114,32,105,116,32,119,97,115,32,103,105,118, +101,110,32,97,119,97,121,0,1630647,1591963,12,8,1,1631339,14,1587567, +2,1630975,16,1310721,2,3,26,2,1,40,3,36,1630969,16,1469953,1310721, +2,3,26,2,26,2,5,28,1,6,1,10,28,34,63,38, +3,26,1630853,16,1469953,1310721,2,3,26,2,26,9,10,3,6,10, +4,8,1472001,1310721,2,3,26,2,26,3,10,3,6,10,4,8, +1630969,14,1310721,2,3,26,2,1630961,14,104,97,115,32,116,111,32, +98,101,32,117,32,116,111,32,98,101,32,114,101,116,117,114, +110,101,100,59,32,119,114,105,116,101,32,39,114,101,116,117, +114,110,32,117,42,120,39,0,1630867,1591963,12,8,1,1631339,14,1310721, +2,2,3,28,1,6,1,10,28,34,63,38,3,26,1631139,16, +1631127,14,114,101,116,117,114,110,115,32,97,32,110,101,119,32, +111,98,106,101,99,116,59,32,103,105,118,101,32,116,104,101, +32,102,117,110,99,116,105,111,110,32,97,32,46,117,32,114, +101,116,117,114,110,32,116,121,112,101,0,1631013,1591845,12,8,1631331, +14,1310721,2,3,26,2,1,40,3,36,1631331,16,1310721,2,3,26, +2,1631323,14,105,115,32,111,119,110,101,100,32,97,110,100,32, +99,97,110,39,116,32,98,101,32,114,101,116,117,114,110,101, +100,32,102,114,111,109,32,97,32,102,117,110,99,116,105,111, +110,32,119,104,111,115,101,32,114,101,116,117,114,110,32,116, +121,112,101,32,105,115,110,39,116,32,46,117,0,1631175,1591963,12, +8,1,1631339,14,1,1310721,2,5,26,1310721,4,18,1310721,2,5,28, +1310721,4,1310721,2,1835009,40,1631387,16,1966081,1310721,4,1579223,12,1310721,2,4, +1587565,2,1,6,1,10,28,34,63,38,3,26,1631427,16,1,1632055, +14,1310721,2,2,3,28,1,6,1,10,28,34,63,38,3,26, +1631467,16,3,1632055,14,1310721,2,2,5,40,1631487,16,3,1631543,14,1310721, +2,2,5,28,1612447,12,1,6,1,10,28,34,63,38,3,26, +1,6,1,10,28,34,63,38,1,10,28,1631553,16,1,1632055,14, +1310721,2,3,26,1310721,2,2,5,28,10,3,6,10,4,8,1469953, +1310721,2,3,26,2,26,2,5,28,1,6,1,10,28,34,63, +38,3,26,1631695,16,1468929,1310721,2,3,26,2,26,2,1587561,2,28, +1,6,1,10,28,34,63,38,3,26,1,6,1,10,28,34, +63,38,1,10,28,1631697,14,1,1631763,16,1469953,1310721,2,3,26,2, +26,9,10,3,6,10,4,8,1472001,1310721,2,3,26,2,26,3, +10,3,6,10,4,8,3,1632055,14,1469953,1310721,2,3,26,2,26, +2,5,28,1,6,1,10,28,34,63,38,3,26,1631927,16,1310721, +2,3,26,2,1631915,14,111,110,108,121,32,97,32,117,32,111, +98,106,101,99,116,32,111,102,32,116,104,105,115,32,98,108, +111,99,107,32,99,97,110,32,98,101,32,105,116,115,32,118, +97,108,117,101,0,1631821,1591963,12,8,1632047,14,1310721,2,3,26,2, +1632039,14,104,97,115,32,116,111,32,98,101,32,117,32,116,111, +32,98,101,32,104,97,110,100,101,100,32,117,112,32,97,115, +32,116,104,101,32,98,108,111,99,107,39,115,32,118,97,108, +117,101,0,1631941,1591963,12,8,1,1632055,14,1,1310721,2,5,26,1310721, +4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1632103,16,1966081,1310721, +4,1579223,12,1310721,2,4,1310721,2,2,3,28,1,6,1,10,28, +34,63,38,3,26,1632259,16,1632247,14,97,32,110,101,119,32,111, +98,106,101,99,116,32,40,111,114,32,97,32,117,32,114,101, +115,117,108,116,41,32,99,97,110,39,116,32,98,101,32,116, +104,114,111,119,110,32,97,119,97,121,0,1632147,1591845,12,8,1632293, +14,1310721,2,2,5,40,3,36,1632293,16,1310721,2,2,5,28,1612545, +12,8,1,1632301,14,1,1310721,2,3,26,1310721,4,18,1310721,2,5, +28,1310721,4,1310721,2,1835009,40,1632349,16,1966081,1310721,4,1579223,12,1310721,2, +4,1587683,2,1310721,2,3,26,4,1587683,2,1310721,2,2,26,1310721,40, +3,36,1632449,16,1632441,14,116,104,101,32,116,121,112,101,32,97, +114,101,97,32,105,115,32,102,117,108,108,0,1632397,1591079,12,8, +1587683,1587683,2,1310721,2,2,26,10,3,6,10,4,8,1310721,2,3, +26,2,1,1310721,2,2,1574703,12,8,1310721,2,3,26,2,1632515,14, +1,1310721,2,5,26,1310721,4,18,1310721,2,11,28,1310721,4,1310721,2, +1835009,40,1632563,16,1966081,1310721,4,1579223,12,1310721,2,7,26,4,1310721,2, +5,26,4,1310721,2,3,26,4,1310721,2,4,15,1310721,2,7,26, +2,26,1632315,12,1310721,2,9,26,4,1310721,2,9,26,2,1,26, +1310721,2,2,10,3,6,10,4,8,1310721,2,9,26,2,3,26, +1310721,2,3,26,2,10,3,6,10,4,8,1310721,2,9,26,2, +5,26,1310721,2,5,26,2,10,3,6,10,4,8,1587685,2,2049, +40,3,36,1632789,16,1632781,14,116,111,111,32,109,97,110,121,32, +116,121,112,101,115,0,1632751,1591079,12,8,1277953,1587685,2,26,1310721,2, +9,26,2,10,3,6,10,4,8,1587685,1,6,2,1,6,3, +26,5,6,4,10,8,8,1310721,2,9,26,2,1632863,14,1,1310721, +2,11,26,1310721,4,18,1310721,2,7,28,1310721,4,1310721,2,1835009,40, +1632911,16,1966081,1310721,4,1579223,12,1310721,2,4,1,1310721,2,3,26,4, +1310721,2,3,26,2,1587685,2,40,1633157,16,1310721,2,5,26,1277953,1310721, +2,3,26,2,26,2,10,3,6,10,4,8,1310721,2,5,26, +2,1,26,2,11,28,1,6,1,10,28,34,63,38,3,26, +1633099,16,1310721,2,5,26,2,9,26,2,1310721,2,2,28,1,6, +1,10,28,34,63,38,3,26,1,6,1,10,28,34,63,38, +1,10,28,1633101,14,1,1633119,16,1310721,2,5,26,2,1633237,14,1310721, +2,3,26,1,6,2,1,6,3,26,5,6,4,10,8,8, +1632929,14,1310721,2,5,26,11,3,1,1,1632529,12,10,3,6,10, +4,8,1310721,2,5,26,2,9,26,1310721,2,2,10,3,6,10, +4,8,1310721,2,5,26,2,1633237,14,1,1310721,2,7,26,1310721,4, +18,1310721,2,9,28,1310721,4,1310721,2,1835009,40,1633285,16,1966081,1310721,4, +1579223,12,1310721,2,3,26,4,1310721,2,4,1,1310721,2,5,26,4, +1310721,2,5,26,2,1587685,2,40,1633621,16,1310721,2,7,26,1277953,1310721, +2,5,26,2,26,2,10,3,6,10,4,8,1310721,2,7,26, +2,1,26,2,13,28,1,6,1,10,28,34,63,38,3,26, +1633483,16,1310721,2,7,26,2,9,26,2,1310721,2,2,28,1,6, +1,10,28,34,63,38,3,26,1,6,1,10,28,34,63,38, +1,10,28,1633485,14,1,1633563,16,1310721,2,7,26,2,7,26,2, +1310721,2,3,26,2,28,1,6,1,10,28,34,63,38,3,26, +1,6,1,10,28,34,63,38,1,10,28,1633565,14,1,1633583,16, +1310721,2,7,26,2,1633757,14,1310721,2,5,26,1,6,2,1,6, +3,26,5,6,4,10,8,8,1633313,14,1310721,2,7,26,13,1310721, +2,3,26,2,1310721,2,2,1637519,12,30,1,1,1632529,12,10,3, +6,10,4,8,1310721,2,7,26,2,9,26,1310721,2,2,10,3, +6,10,4,8,1310721,2,7,26,2,7,26,1310721,2,3,26,2, +10,3,6,10,4,8,1310721,2,7,26,2,1633757,14,1,1310721,2, +9,26,1310721,4,18,1310721,2,15,28,1310721,4,1310721,2,1835009,40,1633805, +16,1966081,1310721,4,1579223,12,1310721,2,7,26,4,1310721,2,5,26,4, +1310721,2,3,26,4,1310721,2,4,1,1310721,2,9,26,4,1310721,2, +9,26,2,1587685,2,40,1634479,16,1310721,2,13,26,1277953,1310721,2,9, +26,2,26,2,10,3,6,10,4,8,1310721,2,13,26,2,1, +26,2,15,28,1,6,1,10,28,34,63,38,3,26,1634023,16, +1310721,2,13,26,2,9,26,2,1310721,2,2,28,1,6,1,10, +28,34,63,38,3,26,1,6,1,10,28,34,63,38,1,10, +28,1634025,14,1,1634103,16,1310721,2,13,26,2,7,26,2,1310721,2, +3,26,2,28,1,6,1,10,28,34,63,38,3,26,1,6, +1,10,28,34,63,38,1,10,28,1634105,14,1,1634183,16,1310721,2, +13,26,2,11,26,2,1310721,2,7,26,2,28,1,6,1,10, +28,34,63,38,3,26,1,6,1,10,28,34,63,38,1,10, +28,1634185,14,1,1634441,16,1310721,2,11,26,1,10,3,6,10,4, +8,1310721,2,11,26,2,1310721,2,3,26,2,40,1634337,16,1310721,2, +13,26,2,15,1310721,2,11,26,2,26,26,2,1310721,2,5,26, +2,1310721,2,11,26,2,26,2,28,1,6,1,10,28,34,63, +38,3,26,1,6,1,10,28,34,63,38,1,10,28,1634339,14, +1,1634381,16,1310721,2,11,26,1,6,2,1,6,3,26,5,6, +4,10,8,8,1634211,14,1310721,2,11,26,2,1310721,2,3,26,2, +28,1,6,1,10,28,34,63,38,3,26,1634441,16,1310721,2,13, +26,2,1634679,14,1310721,2,9,26,1,6,2,1,6,3,26,5, +6,4,10,8,8,1633853,14,1310721,2,13,26,15,3,1,1310721,2, +3,26,2,1632529,12,10,3,6,10,4,8,1310721,2,13,26,2, +9,26,1310721,2,2,10,3,6,10,4,8,1310721,2,13,26,2, +7,26,1310721,2,3,26,2,10,3,6,10,4,8,1310721,2,13, +26,2,11,26,1310721,2,7,26,2,10,3,6,10,4,8,1310721, +2,13,26,2,15,26,1310721,2,5,26,2,1310721,2,3,26,2, +1574491,12,8,1310721,2,13,26,2,1634679,14,1,1310721,2,15,26,1310721, +4,18,1310721,2,9,28,1310721,4,1310721,2,1835009,40,1634727,16,1966081,1310721, +4,1579223,12,1310721,2,3,26,4,1310721,2,4,1,1310721,2,5,26, +4,1310721,2,5,26,2,1587685,2,40,1635063,16,1310721,2,7,26,1277953, +1310721,2,5,26,2,26,2,10,3,6,10,4,8,1310721,2,7, +26,2,1,26,2,23,28,1,6,1,10,28,34,63,38,3, +26,1634925,16,1310721,2,7,26,2,9,26,2,1310721,2,2,28,1, +6,1,10,28,34,63,38,3,26,1,6,1,10,28,34,63, +38,1,10,28,1634927,14,1,1635005,16,1310721,2,7,26,2,7,26, +2,1310721,2,3,26,2,28,1,6,1,10,28,34,63,38,3, +26,1,6,1,10,28,34,63,38,1,10,28,1635007,14,1,1635025, +16,1310721,2,7,26,2,1635179,14,1310721,2,5,26,1,6,2,1, +6,3,26,5,6,4,10,8,8,1634755,14,1310721,2,7,26,23, +3,1,1,1632529,12,10,3,6,10,4,8,1310721,2,7,26,2, +9,26,1310721,2,2,10,3,6,10,4,8,1310721,2,7,26,2, +7,26,1310721,2,3,26,2,10,3,6,10,4,8,1310721,2,7, +26,2,1635179,14,1,1310721,2,9,26,1310721,4,18,1310721,2,9,28, +1310721,4,1310721,2,1835009,40,1635227,16,1966081,1310721,4,1579223,12,1310721,2,3, +26,4,1310721,2,4,1,1310721,2,5,26,4,1310721,2,5,26,2, +1587685,2,40,1635483,16,1310721,2,7,26,1277953,1310721,2,5,26,2,26, +2,10,3,6,10,4,8,1310721,2,7,26,2,1,26,2,19, +28,1,6,1,10,28,34,63,38,3,26,1635425,16,1310721,2,7, +26,2,7,26,2,1310721,2,2,28,1,6,1,10,28,34,63, +38,3,26,1,6,1,10,28,34,63,38,1,10,28,1635427,14, +1,1635445,16,1310721,2,7,26,2,1635571,14,1310721,2,5,26,1,6, +2,1,6,3,26,5,6,4,10,8,8,1635255,14,1310721,2,7, +26,19,3,1310721,2,3,26,2,1,1632529,12,10,3,6,10,4, +8,1310721,2,7,26,2,7,26,1310721,2,2,10,3,6,10,4, +8,1310721,2,7,26,2,1635571,14,1,1310721,2,9,26,1310721,4,18, +1310721,2,15,28,1310721,4,1310721,2,1835009,40,1635619,16,1966081,1310721,4,1579223, +12,1310721,2,5,26,4,1310721,2,3,26,4,1310721,2,4,1,1310721, +2,7,26,4,1310721,2,7,26,2,1587685,2,40,1636159,16,1310721,2, +11,26,1277953,1310721,2,7,26,2,26,2,10,3,6,10,4,8, +1310721,2,11,26,2,1,26,2,17,28,1,6,1,10,28,34, +63,38,3,26,1635827,16,1310721,2,11,26,2,11,26,2,1310721,2, +2,28,1,6,1,10,28,34,63,38,3,26,1,6,1,10, +28,34,63,38,1,10,28,1635829,14,1,1636121,16,1310721,2,13,26, +1310721,2,11,26,2,13,26,2,10,3,6,10,4,8,1310721,2, +9,26,1,10,3,6,10,4,8,1310721,2,9,26,2,1310721,2, +5,26,2,40,1636017,16,1310721,2,13,26,2,3,1310721,2,9,26, +2,26,26,2,1310721,2,3,26,2,1310721,2,9,26,2,26,2, +28,1,6,1,10,28,34,63,38,3,26,1,6,1,10,28, +34,63,38,1,10,28,1636019,14,1,1636061,16,1310721,2,9,26,1, +6,2,1,6,3,26,5,6,4,10,8,8,1635891,14,1310721,2, +9,26,2,1310721,2,5,26,2,28,1,6,1,10,28,34,63, +38,3,26,1636121,16,1310721,2,11,26,2,1636433,14,1310721,2,7,26, +1,6,2,1,6,3,26,5,6,4,10,8,8,1635657,14,1310721, +2,13,26,1310721,2,5,26,2,3,26,1632315,12,10,3,6,10, +4,8,1310721,2,13,26,2,1,26,1310721,2,5,26,2,10,3, +6,10,4,8,1310721,2,13,26,2,3,26,1310721,2,3,26,2, +1310721,2,5,26,2,1574491,12,8,1310721,2,11,26,17,1,1310721,2, +2,5,26,2,1,1632529,12,10,3,6,10,4,8,1310721,2,11, +26,2,7,26,3,1,10,28,10,3,6,10,4,8,1310721,2, +11,26,2,11,26,1310721,2,2,10,3,6,10,4,8,1310721,2, +11,26,2,13,26,1310721,2,13,26,2,10,3,6,10,4,8, +1310721,2,11,26,2,1636433,14,1,1310721,2,15,26,1310721,4,18,1310721, +2,33,28,1310721,4,1310721,2,1835009,40,1636481,16,1966081,1310721,4,1579223,12, +1310721,2,4,1,1310721,2,13,26,4,1310721,2,2,1,26,2,17, +28,1,6,1,10,28,34,63,38,1,10,28,1636547,16,3,1636589, +14,1310721,2,2,7,26,2,1,40,3,36,1,6,1,10,28, +34,63,38,1,10,28,1636603,16,1310721,2,2,1637505,14,1310721,2,2, +7,26,2,5,1,10,28,28,1,6,1,10,28,34,63,38, +3,26,1636773,16,1636731,14,97,32,115,116,114,117,99,116,32,99, +97,110,39,116,32,99,111,110,116,97,105,110,32,105,116,115, +101,108,102,32,98,121,32,118,97,108,117,101,0,1636653,1591173,12, +8,1310721,2,2,7,26,1,10,3,6,10,4,8,1310721,2,2, +1637505,14,1310721,2,2,7,26,5,1,10,28,10,3,6,10,4, +8,1310721,2,3,26,1310721,2,2,11,26,2,10,3,6,10,4, +8,1310721,2,7,26,1310721,2,3,26,2,11,26,2,10,3,6, +10,4,8,1310721,2,5,26,1310721,2,3,26,2,9,26,2,10, +3,6,10,4,8,1310721,2,17,26,1,17,1574703,12,8,1310721,2, +17,26,1310721,2,2,13,26,2,3,26,1310721,2,2,13,26,2, +1,26,2,1574491,12,8,1310721,2,11,26,1310721,2,7,26,2,7, +30,3,26,1632315,12,10,3,6,10,4,8,1310721,2,9,26,1, +10,3,6,10,4,8,1310721,2,9,26,2,1310721,2,7,26,2, +40,1637397,16,1310721,2,15,26,1310721,2,5,26,2,1310721,2,9,26, +2,1310721,2,9,26,2,26,3,26,26,2,1310721,2,17,26,1637691, +12,10,3,6,10,4,8,1310721,2,11,26,2,1310721,2,9,26, +2,7,30,26,1310721,2,5,26,2,1310721,2,9,26,2,1310721,2, +9,26,2,26,26,2,10,3,6,10,4,8,1310721,2,11,26, +2,1310721,2,9,26,2,7,30,3,26,26,1310721,2,15,26,2, +10,3,6,10,4,8,1310721,2,11,26,2,1310721,2,9,26,2, +7,30,5,26,26,1310721,2,13,26,2,10,3,6,10,4,8, +1310721,2,13,26,1310721,2,13,26,2,1310721,2,15,26,2,1637519,12, +26,10,3,6,10,4,8,1310721,2,9,26,1,6,2,1,6, +3,26,5,6,4,10,8,8,1637037,14,1310721,2,2,9,26,1310721, +2,11,26,2,10,3,6,10,4,8,1310721,2,2,3,26,1310721, +2,13,26,2,10,3,6,10,4,8,1310721,2,2,7,26,1310721, +2,7,26,2,10,3,6,10,4,8,1310721,2,2,1637505,14,1, +1310721,2,33,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009, +40,1637553,16,1966081,1310721,4,1579223,12,1310721,2,4,1310721,2,2,1,26, +2,17,28,1,6,1,10,28,34,63,38,3,26,1637641,16,1310721, +2,2,7,26,2,1,40,1,6,1,10,28,34,63,38,1, +10,28,1637643,14,1,1637659,16,1310721,2,2,1636447,12,8,1310721,2,2, +3,26,2,1637677,14,1,1310721,2,3,26,1310721,4,18,1310721,2,61, +28,1310721,4,1310721,2,1835009,40,1637725,16,1966081,1310721,4,1579223,12,1310721,2, +3,26,4,1310721,2,4,1310721,2,2,1,26,2,1310721,2,5,26, +4,1310721,2,5,26,2,19,28,1,6,1,10,28,34,63,38, +3,26,1637869,16,1310721,2,3,26,2,1310721,2,2,7,26,2,26, +2,1637861,16,1310721,2,3,26,2,1310721,2,2,7,26,2,26,2, +1638675,14,1587687,2,1638675,14,1310721,2,5,26,2,11,28,1,6,1, +10,28,34,63,38,3,26,1637941,16,1310721,2,2,9,26,2,1310721, +2,3,26,2,1637691,12,1632877,12,1638675,14,1310721,2,5,26,2,13, +28,1,6,1,10,28,34,63,38,3,26,1638025,16,1310721,2,2, +9,26,2,1310721,2,3,26,2,1637691,12,1310721,2,2,7,26,2, +1633251,12,1638675,14,1310721,2,5,26,2,15,28,1,6,1,10,28, +34,63,38,3,26,1638315,16,1310721,2,9,26,1310721,2,2,7,26, +2,10,3,6,10,4,8,1310721,2,7,26,1,10,3,6,10, +4,8,1310721,2,7,26,2,1310721,2,9,26,2,40,1638251,16,1310721, +2,11,26,1310721,2,7,26,2,26,1310721,2,2,15,1310721,2,7, +26,2,26,26,2,1310721,2,3,26,2,1637691,12,10,3,6,10, +4,8,1310721,2,7,26,1,6,2,1,6,3,26,5,6,4, +10,8,8,1638117,14,1310721,2,2,9,26,2,1310721,2,3,26,2, +1637691,12,1310721,2,9,26,2,1310721,2,11,26,1310721,2,2,11,26, +2,1633771,12,1638675,14,1310721,2,5,26,2,17,28,1,6,1,10, +28,34,63,38,3,26,1638391,16,1310721,2,2,11,26,2,1,6, +1,10,28,34,63,38,1,10,28,1638393,14,1,1638663,16,1310721,2, +59,26,1310721,2,2,13,26,2,10,3,6,10,4,8,1310721,2, +9,26,1310721,2,59,26,2,1,26,2,10,3,6,10,4,8, +1310721,2,7,26,1,10,3,6,10,4,8,1310721,2,7,26,2, +1310721,2,9,26,2,40,1638625,16,1310721,2,11,26,1310721,2,7,26, +2,26,1310721,2,59,26,2,3,1310721,2,7,26,2,26,26,2, +1310721,2,3,26,2,1637691,12,10,3,6,10,4,8,1310721,2,7, +26,1,6,2,1,6,3,26,5,6,4,10,8,8,1638487,14, +1310721,2,2,11,26,2,1310721,2,11,26,1310721,2,9,26,2,1635585, +12,1638675,14,1310721,2,2,1638675,14,1,1310721,2,61,26,1310721,4,18, +1310721,2,3,28,1310721,4,1310721,2,1835009,40,1638723,16,1966081,1310721,4,1579223, +12,1310721,2,4,1310721,2,2,1,26,2,3,28,1,6,1,10, +28,34,63,38,3,26,1638775,16,3,1638833,14,1310721,2,2,1,26, +2,5,28,1,6,1,10,28,34,63,38,3,26,1,6,1, +10,28,34,63,38,1,10,28,1638843,16,3,1638901,14,1310721,2,2, +1,26,2,7,28,1,6,1,10,28,34,63,38,3,26,1, +6,1,10,28,34,63,38,1,10,28,1638907,14,1,1310721,2,3, +26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1638955,16, +1966081,1310721,4,1579223,12,1310721,2,4,1310721,2,2,1,26,2,13,28, +1,6,1,10,28,34,63,38,3,26,1639007,16,3,1639065,14,1310721, +2,2,1,26,2,17,28,1,6,1,10,28,34,63,38,3, +26,1,6,1,10,28,34,63,38,1,10,28,1639071,14,1,1310721, +2,3,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40, +1639119,16,1966081,1310721,4,1579223,12,1310721,2,4,1310721,2,2,1,26,2, +11,28,1,6,1,10,28,34,63,38,3,26,1639171,16,3,1639229, +14,1310721,2,2,1,26,2,13,28,1,6,1,10,28,34,63, +38,3,26,1,6,1,10,28,34,63,38,1,10,28,1639235,14, +1,1310721,2,3,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2, +1835009,40,1639283,16,1966081,1310721,4,1579223,12,1310721,2,4,1310721,2,2,1, +26,2,13,28,1,6,1,10,28,34,63,38,3,26,1639349,16, +1310721,2,2,9,26,2,1632877,12,1639361,14,1310721,2,2,1639361,14,1, +1310721,2,3,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009, +40,1639409,16,1966081,1310721,4,1579223,12,1310721,2,4,1310721,2,2,1,26, +2,11,28,1,6,1,10,28,34,63,38,3,26,1639461,16,3, +1639519,14,1310721,2,2,1,26,2,13,28,1,6,1,10,28,34, +63,38,3,26,1,6,1,10,28,34,63,38,1,10,28,1639539, +16,1310721,2,2,9,26,2,1639549,14,1587687,2,1639549,14,1,1310721,2, +3,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,1835009,40,1639597, +16,1966081,1310721,4,1579223,12,1310721,2,3,26,4,1310721,2,4,1310721,2, +2,1310721,2,3,26,2,28,1,6,1,10,28,34,63,38,3, +26,1639661,16,3,1639715,14,1310721,2,2,1587687,2,28,1,6,1,10, +28,34,63,38,3,26,1,6,1,10,28,34,63,38,1,10, +28,1639725,16,3,1639783,14,1310721,2,3,26,2,1587687,2,28,1,6, +1,10,28,34,63,38,3,26,1,6,1,10,28,34,63,38, +1,10,28,1639793,16,3,1640865,14,1310721,2,2,1638689,12,1639847,16,1310721, +2,3,26,2,1638689,12,1,6,1,10,28,34,63,38,1,10, +28,1639849,14,1,1639859,16,3,1640865,14,1310721,2,3,26,1310721,2,3, +26,2,1639249,12,10,3,6,10,4,8,1310721,2,2,1,26,2, +11,28,1,6,1,10,28,34,63,38,3,26,1639999,16,1310721,2, +3,26,2,1,26,2,11,28,1,6,1,10,28,34,63,38, +3,26,1,6,1,10,28,34,63,38,1,10,28,1640001,14,1, +1640325,16,1310721,2,2,9,26,2,1587687,2,28,1,6,1,10,28, +34,63,38,3,26,1640053,16,3,1640117,14,1310721,2,3,26,2,9, +26,2,1587687,2,28,1,6,1,10,28,34,63,38,3,26,1, +6,1,10,28,34,63,38,1,10,28,1640127,16,3,1640187,14,1310721, +2,2,9,26,2,1587695,2,28,1,6,1,10,28,34,63,38, +3,26,1,6,1,10,28,34,63,38,1,10,28,1640197,16,3, +1640261,14,1310721,2,3,26,2,9,26,2,1587695,2,28,1,6,1, +10,28,34,63,38,3,26,1,6,1,10,28,34,63,38,1, +10,28,1640271,16,3,1640865,14,1310721,2,2,9,26,2,1310721,2,3, +26,2,9,26,2,28,1,6,1,10,28,34,63,38,3,26, +1640865,14,1310721,2,2,1,26,2,11,28,1,6,1,10,28,34, +63,38,3,26,1640371,16,3,1640429,14,1310721,2,2,1,26,2,15, +28,1,6,1,10,28,34,63,38,3,26,1,6,1,10,28, +34,63,38,1,10,28,1640439,16,3,1640497,14,1310721,2,2,1,26, +2,23,28,1,6,1,10,28,34,63,38,3,26,1,6,1, +10,28,34,63,38,1,10,28,1640531,16,1587701,2,1,6,1,10, +28,34,63,38,1,10,28,1640533,14,1,1640543,16,3,1640865,14,1310721, +2,2,1,26,2,23,28,1,6,1,10,28,34,63,38,3, +26,1640649,16,1310721,2,3,26,2,1,26,2,23,28,1,6,1, +10,28,34,63,38,3,26,1,6,1,10,28,34,63,38,1, +10,28,1640651,14,1,1640731,16,1310721,2,2,9,26,2,1310721,2,3, +26,2,9,26,2,28,1,6,1,10,28,34,63,38,3,26, +1,6,1,10,28,34,63,38,1,10,28,1640733,14,1,1640821,16, +1310721,2,2,7,26,2,1640759,16,3,1640817,14,1310721,2,3,26,2, +7,26,2,1,6,1,10,28,34,63,38,3,26,1,6,1, +10,28,34,63,38,1,10,28,1640865,14,1310721,2,2,1310721,2,3, +26,2,28,1,6,1,10,28,34,63,38,3,26,1640865,14,1, +1310721,2,5,26,1310721,4,18,1310721,2,15,28,1310721,4,1310721,2,1835009, +40,1640913,16,1966081,1310721,4,1579223,12,1310721,2,5,26,4,1310721,2,3, +26,4,1310721,2,4,1310721,2,2,1,26,2,19,28,1,6,1, +10,28,34,63,38,3,26,1641151,16,1310721,2,7,26,1310721,2,2, +7,26,2,10,3,6,10,4,8,1310721,2,5,26,2,1310721,2, +7,26,2,26,2,1,6,1,10,28,34,63,38,3,26,1641109, +16,1310721,2,5,26,2,1310721,2,7,26,2,26,1310721,2,3,26, +2,10,3,6,10,4,8,3,1641955,14,1310721,2,5,26,2,1310721, +2,7,26,2,26,2,1310721,2,3,26,2,1639563,12,1641955,14,1310721, +2,3,26,1310721,2,3,26,2,1639249,12,10,3,6,10,4,8, +1310721,2,2,1,26,2,11,28,1,6,1,10,28,34,63,38, +3,26,1641291,16,1310721,2,3,26,2,1,26,2,11,28,1,6, +1,10,28,34,63,38,3,26,1,6,1,10,28,34,63,38, +1,10,28,1641293,14,1,1641395,16,1310721,2,3,26,2,9,26,2, +1587687,2,28,1,6,1,10,28,34,63,38,3,26,1641349,16,3, +1641955,14,1310721,2,2,9,26,2,1310721,2,3,26,2,9,26,2, +1310721,2,5,26,2,1640879,12,1641955,14,1310721,2,2,1,26,2,17, +28,1,6,1,10,28,34,63,38,3,26,1641473,16,1310721,2,2, +11,26,2,1,6,1,10,28,34,63,38,1,10,28,1641475,14, +1,1641545,16,1310721,2,3,26,2,1,26,2,17,28,1,6,1, +10,28,34,63,38,3,26,1,6,1,10,28,34,63,38,1, +10,28,1641547,14,1,1641627,16,1310721,2,3,26,2,11,26,2,1310721, +2,2,11,26,2,28,1,6,1,10,28,34,63,38,3,26, +1,6,1,10,28,34,63,38,1,10,28,1641629,14,1,1641929,16, +1310721,2,11,26,1310721,2,2,13,26,2,10,3,6,10,4,8, +1310721,2,13,26,1310721,2,3,26,2,13,26,2,10,3,6,10, +4,8,1310721,2,9,26,1310721,2,11,26,2,1,26,2,10,3, +6,10,4,8,1310721,2,7,26,1,10,3,6,10,4,8,1310721, +2,7,26,2,1310721,2,9,26,2,40,1641923,16,1310721,2,11,26, +2,3,1310721,2,7,26,2,26,26,2,1310721,2,13,26,2,3, +1310721,2,7,26,2,26,26,2,1310721,2,5,26,2,1640879,12,1, +6,1,10,28,34,63,38,3,26,1641885,16,1,1641955,14,1310721,2, +7,26,1,6,2,1,6,3,26,5,6,4,10,8,8,1641759, +14,3,1641955,14,1310721,2,2,1310721,2,3,26,2,1639563,12,1641955,14, +1,1310721,2,15,26,1310721,4,18,1310721,2,9,28,1310721,4,1310721,2, +1835009,40,1642003,16,1966081,1310721,4,1579223,12,1310721,2,4,1310721,2,2,1, +26,2,1310721,2,3,26,4,1310721,2,3,26,2,11,28,1,6, +1,10,28,34,63,38,3,26,1642097,16,85,1573255,12,8,1310721,2, +2,9,26,2,1641969,12,1643211,14,1310721,2,3,26,2,23,28,1, +6,1,10,28,34,63,38,3,26,1642215,16,1310721,2,2,7,26, +2,1642177,16,1642169,14,102,105,120,101,100,32,0,1642155,1573495,12,8, +1642187,14,91,93,0,1642181,1573495,12,8,1310721,2,2,9,26,2,1641969, +12,1643211,14,1310721,2,3,26,2,13,28,1,6,1,10,28,34, +63,38,3,26,1642307,16,183,1573255,12,8,1310721,2,2,7,26,2, +1573657,12,8,187,1573255,12,8,1310721,2,2,9,26,2,1641969,12,1643211, +14,1310721,2,3,26,2,15,28,1,6,1,10,28,34,63,38, +3,26,1642861,16,1310721,2,2,9,26,2,1587687,2,28,1,6,1, +10,28,34,63,38,3,26,1642409,16,1642397,14,102,110,0,1642391,1573495, +12,8,1642427,14,1310721,2,2,9,26,2,1641969,12,8,1310721,2,2, +11,26,2,3,32,1642465,16,1642457,14,46,117,0,1642451,1573495,12,8, +81,1573255,12,8,1310721,2,5,26,1,10,3,6,10,4,8,1310721, +2,5,26,2,1310721,2,2,7,26,2,40,1642847,16,1310721,2,5, +26,2,1642555,16,1642547,14,44,32,0,1642541,1573495,12,8,1310721,2,2, +15,1310721,2,5,26,2,26,26,2,1641969,12,8,1310721,2,5,26, +2,25,40,1642673,16,1310721,2,2,1310721,2,5,26,2,1662085,12,3, +28,1,6,1,10,28,34,63,38,3,26,1,6,1,10,28, +34,63,38,1,10,28,1642675,14,1,1642697,16,1642689,14,46,109,0, +1642683,1573495,12,8,1310721,2,5,26,2,25,40,1642785,16,1310721,2,2, +1310721,2,5,26,2,1662085,12,5,28,1,6,1,10,28,34,63, +38,3,26,1,6,1,10,28,34,63,38,1,10,28,1642787,14, +1,1642809,16,1642801,14,46,100,0,1642795,1573495,12,8,1310721,2,5,26, +1,6,2,1,6,3,26,5,6,4,10,8,8,1642495,14,83, +1573255,12,8,1,1643211,14,1310721,2,2,5,26,2,1642899,16,1310721,2, +2,5,26,2,1573495,12,8,1642915,14,1642907,14,63,0,1642903,1573495,12, +8,1310721,2,3,26,2,17,28,1,6,1,10,28,34,63,38, +3,26,1642991,16,1310721,2,2,11,26,2,1,6,1,10,28,34, +63,38,1,10,28,1642993,14,1,1643203,16,1310721,2,7,26,1310721,2, +2,13,26,2,10,3,6,10,4,8,121,1573255,12,8,1310721,2, +5,26,1,10,3,6,10,4,8,1310721,2,5,26,2,1310721,2, +7,26,2,1,26,2,40,1643195,16,1310721,2,5,26,2,1643123,16, +1643115,14,44,32,0,1643109,1573495,12,8,1310721,2,7,26,2,3,1310721, +2,5,26,2,26,26,2,1641969,12,8,1310721,2,5,26,1,6, +2,1,6,3,26,5,6,4,10,8,8,1643059,14,125,1573255,12, +8,1,1643211,14,1,1310721,2,9,26,1310721,4,18,1310721,2,7,28, +1310721,4,1310721,2,1835009,40,1643259,16,1966081,1310721,4,1579223,12,1310721,2,5, +26,4,1310721,2,3,26,4,1310721,2,4,1587697,2,1,6,1,10, +28,34,63,38,3,26,1643319,16,3,1643365,14,1587569,2,1,6,1, +10,28,34,63,38,3,26,1,6,1,10,28,34,63,38,1, +10,28,1643375,16,3,1643401,14,1587703,2,1,6,1,10,28,34,63, +38,1,10,28,1643411,16,3,1643437,14,1590585,2,1,6,1,10,28, +34,63,38,1,10,28,1643447,16,1,1643611,14,1590587,12,8,1643483,14, +116,121,112,101,32,101,114,114,111,114,58,32,0,1643457,1573495,12, +8,1310721,2,2,1573495,12,8,1643531,14,58,32,101,120,112,101,99, +116,101,100,32,0,1643507,1573495,12,8,1310721,2,3,26,2,1641969,12, +8,1643573,14,44,32,103,111,116,32,0,1643559,1573495,12,8,1310721,2, +5,26,2,1641969,12,8,1590731,12,8,1,1643611,14,1,1310721,2,7, +26,1310721,4,18,1310721,2,7,28,1310721,4,1310721,2,1835009,40,1643659,16, +1966081,1310721,4,1579223,12,1310721,2,5,26,4,1310721,2,3,26,4,1310721, +2,4,1310721,2,2,1643701,16,3,1643747,14,1587697,2,1,6,1,10, +28,34,63,38,3,26,1,6,1,10,28,34,63,38,1,10, +28,1643757,16,3,1643803,14,1587569,2,1,6,1,10,28,34,63,38, +3,26,1,6,1,10,28,34,63,38,1,10,28,1643813,16,3, +1643839,14,1587703,2,1,6,1,10,28,34,63,38,1,10,28,1643849, +16,3,1643875,14,1590585,2,1,6,1,10,28,34,63,38,1,10, +28,1643885,16,1,1644001,14,1590587,12,8,1643921,14,116,121,112,101,32, +101,114,114,111,114,58,32,0,1643895,1573495,12,8,1310721,2,3,26, +2,1573495,12,8,1643963,14,44,32,103,111,116,32,0,1643949,1573495,12, +8,1310721,2,5,26,2,1641969,12,8,1590731,12,8,1,1644001,14,1, +1310721,2,7,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,1835009, +40,1644049,16,1966081,1310721,4,1579223,12,1310721,2,4,1587721,2,1310721,2,3, +26,4,1310721,2,3,26,2,1644183,16,1310721,2,3,26,1,6,2, +1,6,-1,26,5,6,4,10,8,8,1587705,1310721,2,3,26,2, +26,2,1310721,2,2,28,1,6,1,10,28,34,63,38,3,26, +1644179,16,1310721,2,3,26,2,1644197,14,1644069,14,3,1,10,28,1644197, +14,1,1310721,2,5,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721, +2,1835009,40,1644245,16,1966081,1310721,4,1579223,12,1310721,2,4,1310721,2,2, +1590545,2,28,1,6,1,10,28,34,63,38,3,26,1644293,16,3, +1644475,14,1310721,2,2,1644015,12,1,40,3,36,1644321,16,3,1644475,14, +1310721,2,2,1611615,12,1,40,3,36,1644349,16,1,1644475,14,1310721,2, +3,26,1310721,2,2,1593303,12,10,3,6,10,4,8,1310721,2,3, +26,2,1,40,3,36,1644467,16,1417729,1310721,2,3,26,2,26,2, +15,28,1,6,1,10,28,34,63,38,3,26,1,6,1,10, +28,34,63,38,1,10,28,1644469,14,1,1644475,14,1,1310721,2,5, +26,1310721,4,18,1310721,2,1,28,1310721,4,1310721,2,1835009,40,1644523,16, +1966081,1310721,4,1579223,12,1587733,2,1606425,12,1644559,16,1585291,1587731,2,10,3, +6,10,4,8,1,1644577,14,1587731,2,1606959,12,8,1,1644577,14,1, +1310721,2,1,26,1310721,4,18,1310721,2,53,28,1310721,4,1310721,2,1835009, +40,1644625,16,1966081,1310721,4,1579223,12,1645003,12,1310721,2,4,1587599,2,1606425, +12,1644977,16,1604999,12,8,1310721,2,3,26,1,10,3,6,10,4, +8,1587601,2,1606425,12,1,6,1,10,28,34,63,38,3,26,1644897, +16,3,1644897,16,1310721,2,3,26,2,49,40,3,36,1644787,16,1644779, +14,116,111,111,32,109,97,110,121,32,112,97,114,97,109,101, +116,101,114,115,0,1644739,1591079,12,8,1310721,2,5,26,1310721,2,3, +26,2,26,1644591,12,10,3,6,10,4,8,1310721,2,3,26,1, +6,2,1,6,3,26,5,6,4,10,8,8,1587611,2,1606729,12, +1,6,1,10,28,34,63,38,3,26,1644893,16,1644897,14,1644707,14, +1587601,2,1606959,12,8,1310721,2,1310721,2,2,1310721,2,3,26,2,1310721, +2,5,26,1310721,2,3,26,2,1310721,2,3,26,2,26,1633771,12, +10,3,6,10,4,8,1644635,14,1310721,2,2,1644989,14,1,1310721,2, +53,26,1310721,4,18,1310721,2,27,28,1310721,4,1310721,2,1835009,40,1645037, +16,1966081,1310721,4,1579223,12,1,1310721,2,23,26,4,1587623,2,1606729,12, +1645073,16,1645003,12,1632877,12,1646967,14,1587607,2,1606729,12,1645263,16,1587609,2, +1606729,12,1645111,16,1645003,12,1,1634693,12,1646967,14,1310721,2,1685483,12,10, +3,6,10,4,8,1587609,2,1606959,12,8,1310721,2,5,26,1645003,12, +10,3,6,10,4,8,1310721,2,2,1,40,1645239,16,1645231,14,97, +32,110,101,103,97,116,105,118,101,32,97,114,114,97,121,32, +108,101,110,103,116,104,0,1645183,1591173,12,8,1310721,2,5,26,2, +1310721,2,2,1633251,12,1646967,14,1590545,2,1606577,12,1645497,16,1604999,12,8, +1310721,2,5,26,1645003,12,10,3,6,10,4,8,1310721,2,5,26, +2,1,26,2,23,28,1,6,1,10,28,34,63,38,3,26, +1645375,16,1310721,2,5,26,2,9,26,2,3,1634693,12,1646967,14,1310721, +2,5,26,2,1,26,2,13,28,1,6,1,10,28,34,63, +38,1,10,28,1645483,16,1645475,14,111,110,108,121,32,97,114,114, +97,121,115,32,99,97,110,32,98,101,32,102,105,120,101,100, +0,1645425,1591173,12,8,1310721,2,5,26,2,1646967,14,1587639,2,1606577,12, +1645653,16,1604999,12,8,1587599,2,1606425,12,1,6,1,10,28,34,63, +38,3,26,1645645,16,1645637,14,102,110,32,97,115,32,97,32,116, +121,112,101,32,110,101,101,100,115,32,105,116,115,32,112,97, +114,97,109,101,116,101,114,115,58,32,102,110,40,105,110,116, +41,0,1645551,1591557,12,8,1587687,2,1646967,14,1585289,2,5,28,1,6, +1,10,28,34,63,38,1,10,28,1645739,16,1645723,14,101,120,112, +101,99,116,101,100,32,97,32,116,121,112,101,0,1645691,1591557,12, +8,1587687,2,1646967,14,1310721,2,1585291,2,10,3,6,10,4,8,1310721, +2,25,26,1310721,2,2,1644015,12,10,3,6,10,4,8,1310721,2, +25,26,2,1,40,3,36,1645841,16,1604999,12,8,1310721,2,25,26, +2,1310721,2,2,1635193,12,1646967,14,1310721,2,3,26,1310721,2,2,1593303, +12,10,3,6,10,4,8,1587723,2,1645993,16,1310721,2,3,26,2, +1,40,1645903,16,3,1645967,14,1417729,1310721,2,3,26,2,26,2,15, +28,1,6,1,10,28,34,63,38,1,10,28,1,6,1,10, +28,34,63,38,1,10,28,1,6,1,10,28,34,63,38,1, +10,28,1645995,14,1,1646033,16,1587721,2,17,40,1,6,1,10,28, +34,63,38,1,10,28,1646035,14,1,1646121,16,1587705,1587721,2,26,1310721, +2,2,10,3,6,10,4,8,1587721,1,6,2,1,6,3,26, +5,6,4,10,8,8,1604999,12,8,1587721,2,3,28,1310721,2,2, +1635193,12,1646967,14,1310721,2,3,26,2,1,40,1646145,16,3,1646209,14, +1417729,1310721,2,3,26,2,26,2,15,28,1,6,1,10,28,34, +63,38,1,10,28,1,6,1,10,28,34,63,38,1,10,28, +1646265,16,1310721,2,2,1646249,14,105,115,110,39,116,32,97,32,116, +121,112,101,0,1646223,1591691,12,8,1587687,2,1646967,14,1604999,12,8,1310721, +2,5,26,1425921,1310721,2,3,26,2,26,2,10,3,6,10,4, +8,1310721,2,5,26,2,1,26,2,21,28,1,6,1,10,28, +34,63,38,1,10,28,1646367,16,1310721,2,5,26,2,1646967,14,1587729, +2,1606959,12,8,3,1646789,16,1310721,2,23,26,2,17,40,3,36, +1646465,16,1646457,14,116,111,111,32,109,97,110,121,32,116,121,112, +101,32,97,114,103,117,109,101,110,116,115,0,1646409,1591557,12,8, +1310721,2,7,26,1310721,2,23,26,2,26,1644591,12,10,3,6,10, +4,8,1310721,2,7,26,1310721,2,23,26,2,26,2,1637519,12,3, +28,1,6,1,10,28,34,63,38,1,10,28,1646715,16,1646707,14, +116,121,112,101,32,97,114,103,117,109,101,110,116,115,32,104, +97,118,101,32,116,111,32,98,101,32,111,110,101,32,119,111, +114,100,32,40,97,32,112,111,105,110,116,101,114,44,32,97, +32,110,117,109,98,101,114,44,32,111,114,32,97,32,112,97, +114,97,109,101,116,101,114,41,0,1646561,1591173,12,8,1310721,2,23, +26,1,6,2,1,6,3,26,5,6,4,10,8,8,1587611,2, +1606729,12,1,6,1,10,28,34,63,38,3,26,1646785,16,1646789,14, +1646377,14,1644489,12,8,1310721,2,23,26,2,1310721,2,5,26,2,7, +26,2,28,1,6,1,10,28,34,63,38,1,10,28,1646929,16, +1310721,2,2,1646921,14,119,114,111,110,103,32,110,117,109,98,101, +114,32,111,102,32,116,121,112,101,32,97,114,103,117,109,101, +110,116,115,0,1646859,1591343,12,8,1310721,2,5,26,2,1310721,2,7, +26,1310721,2,23,26,2,1635585,12,1646967,14,1,1310721,2,27,26,1310721, +4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1647015,16,1966081,1310721, +4,1579223,12,1585289,2,9,28,1,6,1,10,28,34,63,38,3, +26,1647653,16,1585291,2,1587607,2,28,1,6,1,10,28,34,63,38, +3,26,1647087,16,3,1647139,14,1585291,2,1587637,2,28,1,6,1,10, +28,34,63,38,3,26,1,6,1,10,28,34,63,38,1,10, +28,1647149,16,3,1647769,14,1585291,2,1587623,2,28,1,6,1,10,28, +34,63,38,3,26,1647647,16,1310721,2,1,10,3,6,10,4,8, +1310721,2,2,7,40,1647379,16,1310721,2,2,1605397,12,8,1587511,2,9, +28,1,6,1,10,28,34,63,38,3,26,1647315,16,1587513,2,1587623, +2,28,1,6,1,10,28,34,63,38,3,26,1,6,1,10, +28,34,63,38,1,10,28,1647317,14,1,1,6,1,10,28,34, +63,38,3,26,1647345,16,1647379,14,1310721,2,1,6,2,1,6,3, +26,5,6,4,10,8,8,1647201,14,1310721,2,2,1605397,12,8,1587511, +2,5,28,1,6,1,10,28,34,63,38,3,26,1647519,16,1587513, +2,1644211,12,1647441,16,3,1647493,14,1587513,2,1587639,2,28,1,6,1, +10,28,34,63,38,3,26,1,6,1,10,28,34,63,38,1, +10,28,1,6,1,10,28,34,63,38,1,10,28,1647521,14,1, +1647531,16,3,1647643,14,1587511,2,9,28,1,6,1,10,28,34,63, +38,3,26,1647619,16,1587513,2,1587607,2,28,1,6,1,10,28,34, +63,38,3,26,1,6,1,10,28,34,63,38,1,10,28,1647621, +14,1,1,6,1,10,28,34,63,38,1,10,28,1647769,14,1, +1647769,14,1585289,2,5,28,1,6,1,10,28,34,63,38,1,10, +28,1647693,16,1,1647769,14,1585291,2,1587639,2,28,1,6,1,10,28, +34,63,38,3,26,1647733,16,3,1647763,14,1585291,2,1644211,12,1,6, +1,10,28,34,63,38,1,10,28,1647769,14,1,1310721,2,3,26, +1310721,4,18,1310721,2,1,28,1310721,4,1310721,2,1835009,40,1647817,16,1966081, +1310721,4,1579223,12,1587725,1,10,3,6,10,4,8,1587727,1,10,3, +6,10,4,8,1587637,2,1606425,12,1,6,1,10,28,34,63,38, +3,26,1647899,16,1587725,1644591,12,10,3,6,10,4,8,1587637,2,1606729, +12,1648135,16,1585291,2,1587653,2,28,1,6,1,10,28,34,63,38, +3,26,1647965,16,1587727,3,10,3,6,10,4,8,1648129,14,1585291,2, +1587657,2,28,1,6,1,10,28,34,63,38,3,26,1648019,16,1587727, +5,10,3,6,10,4,8,1648129,14,1585291,2,1587655,2,28,1,6, +1,10,28,34,63,38,3,26,1648073,16,1587727,7,10,3,6,10, +4,8,1648129,14,1648121,14,97,32,116,97,103,32,105,115,32,46, +117,44,32,46,109,32,111,114,32,46,100,0,1648077,1591557,12,8, +1604999,12,8,1,1648143,14,1,1310721,2,1,26,1310721,4,18,1310721,2, +21,28,1310721,4,1310721,2,1835009,40,1648191,16,1966081,1310721,4,1579223,12,1, +1310721,2,7,26,4,1524225,1310721,2,9,26,4,1,1310721,2,15,26, +4,1,1310721,2,17,26,4,1604999,12,8,1310721,2,1607249,12,10,3, +6,10,4,8,1587729,2,1606425,12,1648659,16,1604999,12,8,1587721,1,10, +3,6,10,4,8,3,1648475,16,1587721,2,17,40,3,36,1648383,16, +1648375,14,116,111,111,32,109,97,110,121,32,116,121,112,101,32, +112,97,114,97,109,101,116,101,114,115,0,1648325,1591557,12,8,1587705, +1587721,2,26,1607249,12,10,3,6,10,4,8,1587721,1,6,2,1, +6,3,26,5,6,4,10,8,8,1587611,2,1606729,12,1,6,1, +10,28,34,63,38,3,26,1648471,16,1648475,14,1648299,14,1644489,12,8, +1310721,2,17,26,3,10,3,6,10,4,8,1310721,2,3,26,21, +1,1310721,2,2,1,1632529,12,10,3,6,10,4,8,1310721,2,3, +26,2,7,26,1587721,2,10,3,6,10,4,8,1310721,2,13,26, +1587721,2,1632315,12,10,3,6,10,4,8,1310721,2,13,26,2,1587705, +1587721,2,1574491,12,8,1310721,2,3,26,2,13,26,1310721,2,13,26, +2,10,3,6,10,4,8,1648729,14,1310721,2,3,26,17,1,1310721, +2,2,1,1632529,12,10,3,6,10,4,8,1310721,2,3,26,2, +7,26,5,1,10,28,10,3,6,10,4,8,1310721,2,5,26, +1310721,2,2,15,1310721,2,3,26,2,1592805,12,10,3,6,10,4, +8,1587603,2,1606959,12,8,1587605,2,1606425,12,1,6,1,10,28,34, +63,38,3,26,1649107,16,1310721,2,19,26,1644591,12,10,3,6,10, +4,8,3,1649093,16,1310721,2,7,26,2,2001,40,3,36,1648909,16, +1648901,14,116,111,111,32,109,97,110,121,32,102,105,101,108,100, +115,0,1648869,1591079,12,8,1310721,2,9,26,2,1310721,2,7,26,2, +1310721,2,7,26,2,26,26,1607249,12,10,3,6,10,4,8,1310721, +2,9,26,2,1310721,2,7,26,2,1310721,2,7,26,2,26,3, +26,26,1310721,2,19,26,2,10,3,6,10,4,8,1310721,2,7, +26,1,6,2,1,6,3,26,5,6,4,10,8,8,1587611,2, +1606729,12,1,6,1,10,28,34,63,38,3,26,1649089,16,1649093,14, +1648837,14,1587613,2,1606959,12,8,1648781,14,1604999,12,8,1310721,2,17,26, +2,1649313,16,1310721,2,13,26,1310721,2,7,26,2,1310721,2,7,26, +2,26,1632315,12,10,3,6,10,4,8,1310721,2,13,26,2,1310721, +2,9,26,2,1310721,2,7,26,2,1310721,2,7,26,2,26,1574491, +12,8,1310721,2,3,26,2,9,26,1310721,2,13,26,2,10,3, +6,10,4,8,1310721,2,3,26,2,11,26,1310721,2,7,26,2, +10,3,6,10,4,8,1587721,1,10,3,6,10,4,8,1650023,14, +1310721,2,13,26,1310721,2,7,26,2,7,30,3,26,1632315,12,10, +3,6,10,4,8,1310721,2,11,26,1,10,3,6,10,4,8, +1310721,2,11,26,2,1310721,2,7,26,2,40,1649915,16,1310721,2,19, +26,1310721,2,9,26,2,1310721,2,11,26,2,1310721,2,11,26,2, +26,3,26,26,2,10,3,6,10,4,8,1310721,2,19,26,2, +1310721,2,3,26,2,28,1,6,1,10,28,34,63,38,3,26, +1649653,16,1649621,14,97,32,115,116,114,117,99,116,32,99,97,110, +39,116,32,99,111,110,116,97,105,110,32,105,116,115,101,108, +102,32,98,121,32,118,97,108,117,101,59,32,117,115,101,32, +97,32,112,111,105,110,116,101,114,0,1649513,1591173,12,8,1310721,2, +19,26,1587687,2,10,3,6,10,4,8,1310721,2,13,26,2,1310721, +2,11,26,2,7,30,26,1310721,2,9,26,2,1310721,2,11,26, +2,1310721,2,11,26,2,26,26,2,10,3,6,10,4,8,1310721, +2,13,26,2,1310721,2,11,26,2,7,30,3,26,26,1310721,2, +19,26,2,10,3,6,10,4,8,1310721,2,13,26,2,1310721,2, +11,26,2,7,30,5,26,26,1310721,2,15,26,2,10,3,6, +10,4,8,1310721,2,15,26,1310721,2,15,26,2,1310721,2,19,26, +2,1637519,12,26,10,3,6,10,4,8,1310721,2,11,26,1,6, +2,1,6,3,26,5,6,4,10,8,8,1649377,14,1310721,2,3, +26,2,9,26,1310721,2,13,26,2,10,3,6,10,4,8,1310721, +2,3,26,2,3,26,1310721,2,15,26,2,10,3,6,10,4, +8,1310721,2,3,26,2,7,26,1310721,2,7,26,2,10,3,6, +10,4,8,1587613,2,1606729,12,8,1310721,2,5,26,2,1650049,14,1, +1310721,2,21,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009, +40,1650097,16,1966081,1310721,4,1579223,12,1604999,12,8,1310721,2,1607249,12,10, +3,6,10,4,8,1587615,2,1606959,12,8,1310721,2,2,15,1644591,12, +1592805,12,8,1587613,2,1606959,12,8,1,1650169,14,1,1310721,2,3,26, +1310721,4,18,1310721,2,1,28,1310721,4,1310721,2,1835009,40,1650217,16,1966081, +1310721,4,1579223,12,1604999,12,8,1587751,2,1606577,12,1650255,16,1587697,3,10, +3,6,10,4,8,1650359,14,1587753,2,1606577,12,1650287,16,1587697,1,10, +3,6,10,4,8,1650359,14,1650351,14,116,121,112,101,99,104,101, +99,107,32,111,110,32,111,114,32,116,121,112,101,99,104,101, +99,107,32,111,102,102,0,1650291,1591557,12,8,1604999,12,8,1587613,2, +1606959,12,8,1,1650383,14,1,1310721,2,1,26,1310721,4,18,1310721,2, +7,28,1310721,4,1310721,2,1835009,40,1650431,16,1966081,1310721,4,1579223,12,1310721, +2,3,26,4,1310721,2,4,1,1310721,2,5,26,4,1310721,2,2, +1636447,12,8,1310721,2,5,26,2,1310721,2,2,7,26,2,40,1650617, +16,1310721,2,2,9,26,2,1310721,2,5,26,2,7,30,26,2, +1310721,2,3,26,2,28,1,6,1,10,28,34,63,38,3,26, +1650579,16,1310721,2,5,26,2,1650631,14,1310721,2,5,26,1,6,2, +1,6,3,26,5,6,4,10,8,8,1650471,14,3,1,10,28, +1650631,14,1,1310721,2,7,26,1310721,4,18,1310721,2,7,28,1310721,4, +1310721,2,1835009,40,1650679,16,1966081,1310721,4,1579223,12,1587599,2,1606959,12,8, +1646981,12,1650751,16,1587637,2,1606425,12,1,6,1,10,28,34,63,38, +3,26,1,6,1,10,28,34,63,38,1,10,28,1650753,14,1, +1650781,16,1310721,2,1644591,12,10,3,6,10,4,8,1650943,14,1310721,2, +3,26,1615017,12,10,3,6,10,4,8,1587703,1,6,2,1,6, +3,26,5,6,4,10,8,8,1310721,2,5,26,1681059,12,10,3, +6,10,4,8,1663385,12,8,1310721,2,1587699,2,10,3,6,10,4, +8,1587703,1,6,2,1,6,-1,26,5,6,4,10,8,8,1310721, +2,3,26,2,1615649,12,8,1310721,2,3,26,2,1615567,12,8,1587601, +2,1606959,12,8,1310721,2,2,1650965,14,1,1310721,2,7,26,1310721,4, +18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1651013,16,1966081,1310721,4, +1579223,12,1587599,2,1606959,12,8,1310721,2,1681059,12,10,3,6,10,4, +8,1590511,3,1,10,28,10,3,6,10,4,8,1587769,2,1651103,16, +1587587,2,1,6,1,10,28,34,63,38,1,10,28,1651105,14,1, +1651163,16,1587589,2,3,28,1,6,1,10,28,34,63,38,3,26, +1,6,1,10,28,34,63,38,1,10,28,1651165,14,1,1651205,16, +1589487,1587591,2,26,2,1,6,1,10,28,34,63,38,1,10,28, +1651207,14,1,1651229,16,1590511,1587591,2,10,3,6,10,4,8,1663385,12, +8,1310721,2,2,1613033,12,8,1587699,2,1651257,14,1,1310721,2,3,26, +1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1651305,16,1966081, +1310721,4,1579223,12,1310721,2,4,1310721,2,2,1,26,2,23,28,1, +6,1,10,28,34,63,38,3,26,1651389,16,1310721,2,2,7,26, +2,1,6,1,10,28,34,63,38,1,10,28,1651391,14,1,1651477, +16,1651469,14,97,32,102,105,120,101,100,32,97,114,114,97,121, +32,99,97,110,39,116,32,103,114,111,119,32,111,114,32,115, +104,114,105,110,107,0,1651399,1591173,12,8,1,1651485,14,1,1310721,2, +3,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1651533, +16,1966081,1310721,4,1579223,12,1604999,12,8,1310721,2,1650979,12,10,3,6, +10,4,8,1587601,2,1606959,12,8,1310721,2,2,1,26,2,13,28, +1,6,1,10,28,34,63,38,3,26,1651639,16,9,1594729,12,8, +1310721,2,2,7,26,2,1594643,12,8,1651949,14,1310721,2,2,1,26, +2,23,28,1,6,1,10,28,34,63,38,3,26,1651685,16,3, +1651739,14,1310721,2,2,1587687,2,28,1,6,1,10,28,34,63,38, +3,26,1,6,1,10,28,34,63,38,1,10,28,1651781,14,108, +101,110,32,110,101,101,100,115,32,97,110,32,97,114,114,97, +121,0,1651743,1310721,2,2,1643625,12,8,1590511,2,1,40,3,36,1651851, +16,9,1594729,12,8,1589487,1590511,2,26,2,3,26,1595671,12,8,3, +1594729,12,8,1651949,14,1587769,2,1651921,16,1310721,2,2,1,26,2,23, +28,1,6,1,10,28,34,63,38,3,26,1,6,1,10,28, +34,63,38,1,10,28,1651923,14,1,1651939,16,3,1594729,12,8,1651949, +14,1590547,2,1595409,12,8,1587699,1587689,2,10,3,6,10,4,8,1, +1651975,14,1,1310721,2,3,26,1310721,4,18,1310721,2,3,28,1310721,4, +1310721,2,1835009,40,1652023,16,1966081,1310721,4,1579223,12,1604999,12,8,1310721,2, +1650979,12,10,3,6,10,4,8,1587601,2,1606959,12,8,1310721,2,2, +1,26,2,23,28,1,6,1,10,28,34,63,38,3,26,1652105, +16,3,1652159,14,1310721,2,2,1587687,2,28,1,6,1,10,28,34, +63,38,3,26,1,6,1,10,28,34,63,38,1,10,28,1652195, +14,99,97,112,32,110,101,101,100,115,32,97,32,91,93,84, +0,1652163,1310721,2,2,1643625,12,8,1590549,2,1595409,12,8,1587699,1587689,2, +10,3,6,10,4,8,1,1652245,14,1,1310721,2,3,26,1310721,4, +18,1310721,2,5,28,1310721,4,1310721,2,1835009,40,1652293,16,1966081,1310721,4, +1579223,12,1604999,12,8,1310721,2,1650979,12,10,3,6,10,4,8,1310721, +2,2,1651271,12,8,1310721,2,2,1,26,2,23,28,1,6,1, +10,28,34,63,38,3,26,1652439,16,1310721,2,2,9,26,2,1637519, +12,3,28,1,6,1,10,28,34,63,38,1,10,28,1,6, +1,10,28,34,63,38,1,10,28,1652441,14,1,1652563,16,1652555,14, +112,117,115,104,32,116,97,107,101,115,32,111,110,101,45,119, +111,114,100,32,101,108,101,109,101,110,116,115,59,32,117,115, +101,32,114,101,115,105,122,101,32,102,111,114,32,115,116,114, +117,99,116,115,0,1652449,1591173,12,8,1587611,2,1606959,12,8,1310721,2, +3,26,1681059,12,10,3,6,10,4,8,1663385,12,8,1310721,2,3, +26,2,5,1625017,12,8,1310721,2,2,1,26,2,23,28,1,6, +1,10,28,34,63,38,3,26,1652727,16,1310721,2,2,9,26,2, +1587699,2,1639563,12,1,6,1,10,28,34,63,38,3,26,1,6, +1,10,28,34,63,38,1,10,28,1652729,14,1,1652771,16,1652747,14, +112,117,115,104,0,1652737,1310721,2,2,9,26,2,1587699,2,1643225,12, +8,1587769,2,1652841,16,1310721,2,2,1,26,2,23,28,1,6,1, +10,28,34,63,38,3,26,1,6,1,10,28,34,63,38,1, +10,28,1652843,14,1,1652889,16,1310721,2,2,9,26,2,1658673,12,1, +6,1,10,28,34,63,38,1,10,28,1652891,14,1,1652923,16,1310721, +2,2,9,26,2,1594643,12,8,1590517,2,1595409,12,8,1587601,2,1606959, +12,8,1590555,2,1595409,12,8,1587699,1587689,2,10,3,6,10,4,8, +1,1652969,14,1,1310721,2,5,26,1310721,4,18,1310721,2,3,28,1310721, +4,1310721,2,1835009,40,1653017,16,1966081,1310721,4,1579223,12,1604999,12,8,1310721, +2,1650979,12,10,3,6,10,4,8,1310721,2,2,1651271,12,8,1587601, +2,1606959,12,8,1590557,2,1595409,12,8,1587699,1310721,2,2,1639375,12,10, +3,6,10,4,8,1310721,2,2,1,26,2,23,28,1,6,1, +10,28,34,63,38,3,26,1653165,16,1587699,1310721,2,2,9,26,2, +10,3,6,10,4,8,1,1653173,14,1,1310721,2,3,26,1310721,4, +18,1310721,2,5,28,1310721,4,1310721,2,1835009,40,1653221,16,1966081,1310721,4, +1579223,12,1604999,12,8,1310721,2,1650979,12,10,3,6,10,4,8,1310721, +2,2,1651271,12,8,1587611,2,1606959,12,8,1310721,2,3,26,1681059,12, +10,3,6,10,4,8,1663385,12,8,1310721,2,3,26,2,1613033,12, +8,1587601,2,1606959,12,8,1590559,2,1595409,12,8,1587699,1587689,2,10,3, +6,10,4,8,1,1653361,14,1,1310721,2,5,26,1310721,4,18,1310721, +2,3,28,1310721,4,1310721,2,1835009,40,1653409,16,1966081,1310721,4,1579223,12, +1604999,12,8,1310721,2,1650645,12,10,3,6,10,4,8,1310721,2,2, +1637519,12,1594643,12,8,1587699,1587689,2,10,3,6,10,4,8,1,1653477, +14,1,1310721,2,3,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721, +2,1835009,40,1653525,16,1966081,1310721,4,1579223,12,1604999,12,8,1310721,2,1650645, +12,10,3,6,10,4,8,1310721,2,2,1594643,12,8,1587699,1587687,2, +10,3,6,10,4,8,1,1653589,14,1,1310721,2,3,26,1310721,4, +18,1310721,2,1,28,1310721,4,1310721,2,1835009,40,1653637,16,1966081,1310721,4, +1579223,12,1587683,1277953,2049,26,10,3,6,10,4,8,1587685,1,10,3, +6,10,4,8,1587687,1,3,1653693,14,119,111,114,100,0,1653683,1592287, +12,1,1632529,12,10,3,6,10,4,8,1587689,3,3,1653735,14,105, +110,116,0,1653727,1592287,12,1,1632529,12,10,3,6,10,4,8,1587691, +5,3,1653779,14,99,104,97,114,0,1653769,1592287,12,1,1632529,12,10, +3,6,10,4,8,1587693,7,3,1653823,14,98,111,111,108,0,1653813, +1592287,12,1,1632529,12,10,3,6,10,4,8,1587695,9,1,1653867,14, +118,111,105,100,0,1653857,1592287,12,1,1632529,12,10,3,6,10,4, +8,1587687,2,5,26,2,15,1587687,2,1592805,12,8,1587689,2,5,26, +2,15,1587689,2,1592805,12,8,1587691,2,5,26,2,15,1587691,2,1592805, +12,8,1587693,2,5,26,2,15,1587693,2,1592805,12,8,1587695,2,5, +26,2,15,1587695,2,1592805,12,8,1587729,1654011,14,60,0,1654007,1592287,12, +10,3,6,10,4,8,1587731,1654039,14,62,0,1654035,1592287,12,10,3, +6,10,4,8,1587733,1654069,14,62,62,0,1654063,1592287,12,10,3,6, +10,4,8,1587735,1654099,14,60,61,0,1654093,1592287,12,10,3,6,10, +4,8,1587737,1654129,14,62,61,0,1654123,1592287,12,10,3,6,10,4, +8,1587739,1654159,14,61,61,0,1654153,1592287,12,10,3,6,10,4,8, +1587741,1654189,14,33,61,0,1654183,1592287,12,10,3,6,10,4,8,1587743, +1654217,14,43,0,1654213,1592287,12,10,3,6,10,4,8,1587745,1654255,14, +115,116,114,117,99,116,0,1654241,1592287,12,10,3,6,10,4,8, +1587747,1654289,14,116,121,112,101,0,1654279,1592287,12,10,3,6,10,4, +8,1587749,1654333,14,116,121,112,101,99,104,101,99,107,0,1654313,1592287, +12,10,3,6,10,4,8,1587751,1654363,14,111,110,0,1654357,1592287,12, +10,3,6,10,4,8,1587753,1654395,14,111,102,102,0,1654387,1592287,12, +10,3,6,10,4,8,1587755,1654427,14,110,101,119,0,1654419,1592287,12, +10,3,6,10,4,8,1587757,1654463,14,110,101,119,95,116,0,1654451, +1592287,12,10,3,6,10,4,8,1590513,1654501,14,35,105,116,101,109, +115,0,1654487,1592287,12,10,3,6,10,4,8,1590515,1654535,14,35,108, +101,110,0,1654525,1592287,12,10,3,6,10,4,8,1590517,1654573,14,102, +97,116,116,101,110,0,1654559,1592287,12,10,3,6,10,4,8,1587759, +1654603,14,111,51,0,1654597,1592287,12,10,3,6,10,4,8,1587761,1654635, +14,108,102,97,0,1654627,1592287,12,10,3,6,10,4,8,1587763,1654675, +14,111,117,116,115,105,100,101,0,1654659,1592287,12,10,3,6,10, +4,8,1587765,1654715,14,110,101,119,95,114,97,119,0,1654699,1592287,12, +10,3,6,10,4,8,1587767,1654747,14,111,111,98,0,1654739,1592287,12, +10,3,6,10,4,8,1590545,1654783,14,102,105,120,101,100,0,1654771, +1592287,12,10,3,6,10,4,8,1590547,1654817,14,100,108,101,110,0, +1654807,1592287,12,10,3,6,10,4,8,1590549,1654851,14,100,99,97,112, +0,1654841,1592287,12,10,3,6,10,4,8,1590551,1654885,14,100,110,101, +119,0,1654875,1592287,12,10,3,6,10,4,8,1590553,1654917,14,100,97, +116,0,1654909,1592287,12,10,3,6,10,4,8,1590555,1654953,14,100,112, +117,115,104,0,1654941,1592287,12,10,3,6,10,4,8,1590557,1654987,14, +100,112,111,112,0,1654977,1592287,12,10,3,6,10,4,8,1590559,1655027, +14,100,114,101,115,105,122,101,0,1655011,1592287,12,10,3,6,10, +4,8,1590519,1,26,1655059,14,42,0,1655055,1592287,12,10,3,6,10, +4,8,1590519,3,26,1655091,14,47,0,1655087,1592287,12,10,3,6,10, +4,8,1590519,5,26,1655123,14,37,0,1655119,1592287,12,10,3,6,10, +4,8,1590519,7,26,1655155,14,38,0,1655151,1592287,12,10,3,6,10, +4,8,1590519,9,26,1655187,14,124,0,1655183,1592287,12,10,3,6,10, +4,8,1590519,11,26,1655219,14,94,0,1655215,1592287,12,10,3,6,10, +4,8,1590519,13,26,1655253,14,60,60,0,1655247,1592287,12,10,3,6, +10,4,8,1590519,15,26,1587733,2,10,3,6,10,4,8,1590543,17, +10,3,6,10,4,8,1,1655317,14,1,1310721,2,1,26,1310721,4, +18,1310721,2,5,28,1310721,4,1310721,2,1835009,40,1655365,16,1966081,1310721,4, +1579223,12,1310721,2,4,1,1310721,2,3,26,4,1310721,2,3,26,2, +1590543,2,40,1655495,16,1590519,1310721,2,3,26,2,26,2,1310721,2,2, +28,1,6,1,10,28,34,63,38,3,26,1655457,16,3,1655503,14, +1310721,2,3,26,1,6,2,1,6,3,26,5,6,4,10,8, +8,1655383,14,1,1655503,14,1,1310721,2,5,26,1310721,4,18,1310721,2, +9,28,1310721,4,1310721,2,1835009,40,1655551,16,1966081,1310721,4,1579223,12,1310721, +2,5,26,4,1310721,2,3,26,4,1310721,2,4,1546753,1310721,2,2, +26,2,1310721,2,7,26,4,1310721,2,3,26,1310721,2,3,26,2, +1639249,12,10,3,6,10,4,8,1310721,2,5,26,1310721,2,5,26, +2,1639249,12,10,3,6,10,4,8,1547137,1310721,2,2,26,2,1, +40,1655863,16,1310721,2,3,26,2,1638921,12,1,6,1,10,28,34, +63,38,3,26,1655785,16,1310721,2,5,26,2,1638921,12,1,6,1, +10,28,34,63,38,3,26,1,6,1,10,28,34,63,38,1, +10,28,1655787,14,1,1655837,14,38,38,32,97,110,100,32,124,124, +32,110,101,101,100,32,115,99,97,108,97,114,115,0,1655791,1310721, +2,3,26,2,1643625,12,8,1587693,2,1658289,14,1310721,2,7,26,2, +1587739,2,28,1,6,1,10,28,34,63,38,3,26,1655909,16,3, +1655967,14,1310721,2,7,26,2,1587741,2,28,1,6,1,10,28,34, +63,38,3,26,1,6,1,10,28,34,63,38,1,10,28,1656157, +16,1310721,2,3,26,2,1310721,2,5,26,2,1639563,12,1,6,1, +10,28,34,63,38,3,26,1656089,16,1310721,2,5,26,2,1310721,2, +3,26,2,1639563,12,1,6,1,10,28,34,63,38,3,26,1, +6,1,10,28,34,63,38,1,10,28,1656091,14,1,1656149,16,1656121, +14,99,111,109,112,97,114,105,115,111,110,0,1656099,1310721,2,3, +26,2,1310721,2,5,26,2,1643225,12,8,1587693,2,1658289,14,1310721,2, +7,26,2,1587729,2,28,1,6,1,10,28,34,63,38,3,26, +1656203,16,3,1656261,14,1310721,2,7,26,2,1587731,2,28,1,6,1, +10,28,34,63,38,3,26,1,6,1,10,28,34,63,38,1, +10,28,1656271,16,3,1656329,14,1310721,2,7,26,2,1587735,2,28,1, +6,1,10,28,34,63,38,3,26,1,6,1,10,28,34,63, +38,1,10,28,1656339,16,3,1656397,14,1310721,2,7,26,2,1587737,2, +28,1,6,1,10,28,34,63,38,3,26,1,6,1,10,28, +34,63,38,1,10,28,1656827,16,1310721,2,3,26,2,1587687,2,28, +1,6,1,10,28,34,63,38,3,26,1656447,16,3,1656505,14,1310721, +2,5,26,2,1587687,2,28,1,6,1,10,28,34,63,38,3, +26,1,6,1,10,28,34,63,38,1,10,28,1656515,16,3,1656597, +14,1310721,2,3,26,2,1638689,12,1656573,16,1310721,2,5,26,2,1638689, +12,1,6,1,10,28,34,63,38,1,10,28,1656575,14,1,1, +6,1,10,28,34,63,38,1,10,28,1656607,16,3,1656741,14,1310721, +2,3,26,2,1,26,2,11,28,1,6,1,10,28,34,63, +38,3,26,1656717,16,1310721,2,5,26,2,1,26,2,11,28,1, +6,1,10,28,34,63,38,3,26,1,6,1,10,28,34,63, +38,1,10,28,1656719,14,1,1,6,1,10,28,34,63,38,1, +10,28,1,6,1,10,28,34,63,38,3,26,1656819,16,1656791,14, +99,111,109,112,97,114,105,115,111,110,0,1656769,1310721,2,3,26, +2,1310721,2,5,26,2,1643225,12,8,1587693,2,1658289,14,1310721,2,7, +26,2,1587743,2,28,1,6,1,10,28,34,63,38,3,26,1657375, +16,1310721,2,3,26,2,1,26,2,11,28,1,6,1,10,28, +34,63,38,3,26,1657019,16,1310721,2,5,26,2,1638689,12,1656935,16, +3,1656993,14,1310721,2,5,26,2,1587687,2,28,1,6,1,10,28, +34,63,38,3,26,1,6,1,10,28,34,63,38,1,10,28, +1,6,1,10,28,34,63,38,1,10,28,1657021,14,1,1657039,16, +1310721,2,3,26,2,1658289,14,1310721,2,5,26,2,1,26,2,11, +28,1,6,1,10,28,34,63,38,3,26,1657123,16,1310721,2,3, +26,2,1638689,12,1,6,1,10,28,34,63,38,1,10,28,1657125, +14,1,1657143,16,1310721,2,5,26,2,1658289,14,1310721,2,3,26,2, +1587687,2,28,1,6,1,10,28,34,63,38,3,26,1657189,16,3, +1657247,14,1310721,2,5,26,2,1587687,2,28,1,6,1,10,28,34, +63,38,3,26,1,6,1,10,28,34,63,38,1,10,28,1657259, +16,1587687,2,1658289,14,1310721,2,3,26,2,1638689,12,1657317,16,1310721,2, +5,26,2,1638689,12,1,6,1,10,28,34,63,38,1,10,28, +1657319,14,1,1657331,16,1587689,2,1658289,14,1657339,14,43,0,1657335,1310721,2, +3,26,2,1310721,2,5,26,2,1643225,12,8,1587687,2,1658289,14,1310721, +2,7,26,2,1587631,2,28,1,6,1,10,28,34,63,38,3, +26,1657943,16,1310721,2,3,26,2,1,26,2,11,28,1,6,1, +10,28,34,63,38,3,26,1657525,16,1310721,2,5,26,2,1,26, +2,11,28,1,6,1,10,28,34,63,38,3,26,1,6,1, +10,28,34,63,38,1,10,28,1657527,14,1,1657539,16,1587689,2,1658289, +14,1310721,2,3,26,2,1,26,2,11,28,1,6,1,10,28, +34,63,38,3,26,1657691,16,1310721,2,5,26,2,1638689,12,1657607,16, +3,1657665,14,1310721,2,5,26,2,1587687,2,28,1,6,1,10,28, +34,63,38,3,26,1,6,1,10,28,34,63,38,1,10,28, +1,6,1,10,28,34,63,38,1,10,28,1657693,14,1,1657711,16, +1310721,2,3,26,2,1658289,14,1310721,2,3,26,2,1587687,2,28,1, +6,1,10,28,34,63,38,3,26,1657757,16,3,1657815,14,1310721,2, +5,26,2,1587687,2,28,1,6,1,10,28,34,63,38,3,26, +1,6,1,10,28,34,63,38,1,10,28,1657827,16,1587687,2,1658289, +14,1310721,2,3,26,2,1638689,12,1657885,16,1310721,2,5,26,2,1638689, +12,1,6,1,10,28,34,63,38,1,10,28,1657887,14,1,1657899, +16,1587689,2,1658289,14,1657907,14,45,0,1657903,1310721,2,3,26,2,1310721, +2,5,26,2,1643225,12,8,1587687,2,1658289,14,1310721,2,7,26,2, +1655331,12,1658279,16,1310721,2,3,26,2,1587687,2,28,1,6,1,10, +28,34,63,38,3,26,1658007,16,3,1658065,14,1310721,2,5,26,2, +1587687,2,28,1,6,1,10,28,34,63,38,3,26,1,6,1, +10,28,34,63,38,1,10,28,1658077,16,1587687,2,1658289,14,1310721,2, +3,26,2,1638689,12,1658135,16,1310721,2,5,26,2,1638689,12,1,6, +1,10,28,34,63,38,1,10,28,1658137,14,1,1658149,16,1587689,2, +1658289,14,1310721,2,3,26,2,1638689,12,1658197,16,1310721,2,3,26,1310721, +2,5,26,2,10,3,6,10,4,8,1,1658253,14,97,114,105, +116,104,109,101,116,105,99,32,110,101,101,100,115,32,110,117, +109,98,101,114,115,0,1658203,1310721,2,3,26,2,1643625,12,8,1587687, +2,1658289,14,1587687,2,1658289,14,1,1310721,2,9,26,1310721,4,18,1310721, +2,3,28,1310721,4,1310721,2,1835009,40,1658337,16,1966081,1310721,4,1579223,12, +1310721,2,4,1310721,2,2,1,26,2,1658361,14,1,1310721,2,3,26, +1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1658409,16,1966081, +1310721,4,1579223,12,1310721,2,4,1310721,2,2,1637519,12,1658431,14,1,1310721, +2,3,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40, +1658479,16,1966081,1310721,4,1579223,12,1310721,2,4,1310721,2,2,5,26,2, +1658503,14,1,1310721,2,3,26,1310721,4,18,1310721,2,3,28,1310721,4, +1310721,2,1835009,40,1658551,16,1966081,1310721,4,1579223,12,1310721,2,4,1310721,2, +2,7,26,2,1658575,14,1,1310721,2,3,26,1310721,4,18,1310721,2, +3,28,1310721,4,1310721,2,1835009,40,1658623,16,1966081,1310721,4,1579223,12,1310721, +2,4,1310721,2,2,1636447,12,8,1310721,2,2,7,26,2,1658659,14, +1,1310721,2,3,26,1310721,4,18,1310721,2,9,28,1310721,4,1310721,2, +1835009,40,1658707,16,1966081,1310721,4,1579223,12,1310721,2,4,1310721,2,2,1, +26,2,1310721,2,3,26,4,1310721,2,3,26,2,11,28,1,6, +1,10,28,34,63,38,3,26,1658779,16,3,1658835,14,1310721,2,3, +26,2,23,28,1,6,1,10,28,34,63,38,3,26,1,6, +1,10,28,34,63,38,1,10,28,1658845,16,3,1659163,14,1310721,2, +3,26,2,13,28,1,6,1,10,28,34,63,38,3,26,1658903, +16,1310721,2,2,9,26,2,1658673,12,1659163,14,1310721,2,3,26,2, +17,28,1,6,1,10,28,34,63,38,3,26,1658999,16,1310721,2, +2,11,26,2,1,6,1,10,28,34,63,38,3,26,1,6, +1,10,28,34,63,38,1,10,28,1659001,14,1,1659155,16,1310721,2, +7,26,1310721,2,2,1658589,12,10,3,6,10,4,8,1310721,2,5, +26,1,10,3,6,10,4,8,1310721,2,5,26,2,1310721,2,7, +26,2,40,1659155,16,1310721,2,2,1310721,2,5,26,2,1661615,12,1658673, +12,1659117,16,3,1659163,14,1310721,2,5,26,1,6,2,1,6,3, +26,5,6,4,10,8,8,1659057,14,1,1659163,14,1,1310721,2,9, +26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,1835009,40,1659211,16, +1966081,1310721,4,1579223,12,1310721,2,3,26,4,1310721,2,4,1310721,2,2, +1310721,2,3,26,2,65,1659277,12,8,1310721,2,2,1659263,14,1,1310721, +2,5,26,1310721,4,18,1310721,2,17,28,1310721,4,1310721,2,1835009,40, +1659311,16,1966081,1310721,4,1579223,12,1310721,2,5,26,4,1310721,2,3,26, +4,1310721,2,4,1310721,2,5,26,2,1,10,40,3,36,1659367,16, +3,1659419,14,1310721,2,3,26,2,1,6,1,10,28,34,63,38, +3,26,1,6,1,10,28,34,63,38,1,10,28,1659429,16,3, +1659481,14,1310721,2,2,1578965,12,1,6,1,10,28,34,63,38,3, +26,1,6,1,10,28,34,63,38,1,10,28,1659491,16,1,1660611, +14,1310721,2,3,26,2,1,26,2,23,28,1,6,1,10,28, +34,63,38,3,26,1659795,16,1310721,2,3,26,2,9,26,2,1658673, +12,1659789,16,1310721,2,15,26,1310721,2,3,26,2,9,26,2,1637519, +12,10,3,6,10,4,8,1310721,2,9,26,1310721,2,2,1,26, +2,10,3,6,10,4,8,1310721,2,13,26,1,10,3,6,10, +4,8,1310721,2,13,26,2,1310721,2,9,26,2,40,1659789,16,1310721, +2,2,5,26,2,1310721,2,13,26,2,1310721,2,15,26,2,30, +26,1310721,2,3,26,2,9,26,2,1310721,2,5,26,2,3,28, +1660625,12,8,1310721,2,13,26,1,6,2,1,6,3,26,5,6, +4,10,8,8,1659653,14,1,1660611,14,1310721,2,3,26,2,1,26, +2,11,28,1,6,1,10,28,34,63,38,1,10,28,1659847,16, +3,1659895,14,1310721,2,2,3,1,10,28,26,2,1,40,3,36, +1,6,1,10,28,34,63,38,1,10,28,1659905,16,1,1660611,14, +1310721,2,7,26,1310721,2,3,26,2,9,26,2,10,3,6,10, +4,8,1310721,2,9,26,1,1310721,2,2,3,1,10,28,26,2, +28,3,28,10,3,6,10,4,8,1310721,2,15,26,1310721,2,7, +26,2,1637519,12,10,3,6,10,4,8,1310721,2,15,26,2,3, +40,1660061,16,1310721,2,15,26,3,10,3,6,10,4,8,1310721,2, +7,26,2,1587687,2,28,1,6,1,10,28,34,63,38,3,26, +1660107,16,3,1660169,14,1310721,2,7,26,2,1,26,2,19,28,1, +6,1,10,28,34,63,38,3,26,1,6,1,10,28,34,63, +38,1,10,28,1660207,16,1310721,2,2,3,1,10,28,26,1,10, +3,6,10,4,8,1660321,14,1310721,2,9,26,2,1310721,2,15,26, +2,10,40,1660283,16,1310721,2,2,3,1,10,28,26,1310721,2,7, +26,2,1,1633251,12,10,3,6,10,4,8,1660321,14,1310721,2,2, +3,1,10,28,26,1310721,2,7,26,2,10,3,6,10,4,8, +1310721,2,2,1580291,12,1310721,2,9,26,2,10,40,1660397,16,1310721,2, +2,1310721,2,9,26,2,26,1,1310721,2,2,1580291,12,1310721,2,9, +26,2,28,1574703,12,8,1310721,2,7,26,2,1658673,12,1660603,16,1310721, +2,11,26,1310721,2,9,26,2,1310721,2,15,26,2,44,10,3, +6,10,4,8,1310721,2,13,26,1,10,3,6,10,4,8,1310721, +2,13,26,2,1310721,2,11,26,2,40,1660603,16,1310721,2,2,1310721, +2,13,26,2,1310721,2,15,26,2,30,26,1310721,2,7,26,2, +1310721,2,5,26,2,3,28,1660625,12,8,1310721,2,13,26,1,6, +2,1,6,3,26,5,6,4,10,8,8,1660479,14,1,1660611,14, +1,1310721,2,17,26,1310721,4,18,1310721,2,19,28,1310721,4,1310721,2, +1835009,40,1660659,16,1966081,1310721,4,1579223,12,1310721,2,5,26,4,1310721,2, +3,26,4,1310721,2,4,1310721,2,3,26,2,1,26,2,1310721,2, +7,26,4,1310721,2,7,26,2,11,28,1,6,1,10,28,34, +63,38,3,26,1660755,16,3,1660811,14,1310721,2,7,26,2,23,28, +1,6,1,10,28,34,63,38,3,26,1,6,1,10,28,34, +63,38,1,10,28,1660855,16,1310721,2,2,1,26,2,1310721,2,3, +26,2,1310721,2,5,26,2,1659277,12,1661489,14,1310721,2,7,26,2, +13,28,1,6,1,10,28,34,63,38,3,26,1661123,16,1310721,2, +11,26,1310721,2,3,26,2,7,26,2,10,3,6,10,4,8, +1310721,2,17,26,1310721,2,3,26,2,9,26,2,1637519,12,10,3, +6,10,4,8,1310721,2,9,26,1,10,3,6,10,4,8,1310721, +2,9,26,2,1310721,2,11,26,2,40,1661117,16,1310721,2,2,1310721, +2,9,26,2,1310721,2,17,26,2,30,26,1310721,2,3,26,2, +9,26,2,1310721,2,5,26,2,1660625,12,8,1310721,2,9,26,1, +6,2,1,6,3,26,5,6,4,10,8,8,1660991,14,1,1661489, +14,1310721,2,7,26,2,17,28,1,6,1,10,28,34,63,38, +3,26,1661223,16,1310721,2,3,26,2,11,26,2,1,6,1,10, +28,34,63,38,3,26,1,6,1,10,28,34,63,38,1,10, +28,1661225,14,1,1661481,16,1310721,2,11,26,1310721,2,3,26,2,1658589, +12,10,3,6,10,4,8,1310721,2,9,26,1,10,3,6,10, +4,8,1310721,2,9,26,2,1310721,2,11,26,2,40,1661481,16,1310721, +2,13,26,1310721,2,3,26,2,1310721,2,9,26,2,1661731,12,10, +3,6,10,4,8,1310721,2,15,26,1310721,2,3,26,2,1310721,2, +9,26,2,1661615,12,10,3,6,10,4,8,1310721,2,2,1310721,2, +13,26,2,26,1310721,2,15,26,2,1310721,2,5,26,2,1660625,12, +8,1310721,2,9,26,1,6,2,1,6,3,26,5,6,4,10, +8,8,1661285,14,1,1661489,14,1,1310721,2,19,26,1310721,4,18,1310721, +2,5,28,1310721,4,1310721,2,1835009,40,1661537,16,1966081,1310721,4,1579223,12, +1310721,2,3,26,4,1310721,2,4,1310721,2,2,1636447,12,8,1310721,2, +2,9,26,2,1310721,2,3,26,2,7,30,26,2,1661601,14,1, +1310721,2,5,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,1835009, +40,1661649,16,1966081,1310721,4,1579223,12,1310721,2,3,26,4,1310721,2,4, +1310721,2,2,1636447,12,8,1310721,2,2,9,26,2,1310721,2,3,26, +2,7,30,3,26,26,2,1661717,14,1,1310721,2,5,26,1310721,4, +18,1310721,2,5,28,1310721,4,1310721,2,1835009,40,1661765,16,1966081,1310721,4, +1579223,12,1310721,2,3,26,4,1310721,2,4,1310721,2,2,1636447,12,8, +1310721,2,2,9,26,2,1310721,2,3,26,2,7,30,5,26,26, +2,1661833,14,1,1310721,2,5,26,1310721,4,18,1310721,2,3,28,1310721, +4,1310721,2,1835009,40,1661881,16,1966081,1310721,4,1579223,12,1310721,2,4,1310721, +2,2,9,26,2,1661905,14,1,1310721,2,3,26,1310721,4,18,1310721, +2,3,28,1310721,4,1310721,2,1835009,40,1661953,16,1966081,1310721,4,1579223,12, +1310721,2,4,1310721,2,2,7,26,2,1661977,14,1,1310721,2,3,26, +1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,1835009,40,1662025,16,1966081, +1310721,4,1579223,12,1310721,2,3,26,4,1310721,2,4,1310721,2,2,15, +1310721,2,3,26,2,26,26,2,1662071,14,1,1310721,2,5,26,1310721, +4,18,1310721,2,5,28,1310721,4,1310721,2,1835009,40,1662119,16,1966081,1310721, +4,1579223,12,1310721,2,3,26,4,1310721,2,4,1310721,2,2,11,26, +2,13,38,1310721,2,3,26,2,1310721,2,3,26,2,26,38,7, +32,1662185,14,1,1310721,2,5,26,1310721,4,18,1310721,2,3,28,1310721, +4,1310721,2,1835009,40,1662233,16,1966081,1310721,4,1579223,12,1310721,2,4,1310721, +2,2,11,26,2,3,32,1662261,14,1,1310721,2,3,26,1310721,4, +18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1662309,16,1966081,1310721,4, +1579223,12,1310721,2,4,1310721,2,2,1,26,2,17,28,1,6,1, +10,28,34,63,38,1,10,28,1662363,16,3,1662417,14,1310721,2,2, +11,26,2,1,6,1,10,28,34,63,38,3,26,1,6,1, +10,28,34,63,38,1,10,28,1662427,16,1,1662451,14,1310721,2,2, +13,26,2,1,26,2,1662451,14,1,1310721,2,3,26,1310721,4,18, +1310721,2,5,28,1310721,4,1310721,2,1835009,40,1662499,16,1966081,1310721,4,1579223, +12,1310721,2,3,26,4,1310721,2,4,1310721,2,2,13,26,2,3, +1310721,2,3,26,2,26,26,2,1662551,14,1,1310721,2,5,26,1310721, +4,18,1310721,2,59,28,1310721,4,1310721,2,1835009,40,1662599,16,1966081,1310721, +4,1579223,12,1310721,2,4,1585219,2,1310721,2,3,26,4,3,1,10, +28,1310721,2,5,26,4,1310721,2,3,26,2,1662895,16,1310721,2,3, +26,1,6,2,1,6,-1,26,5,6,4,10,8,8,1417729,1310721, +2,3,26,2,26,2,3,28,1,6,1,10,28,34,63,38, +3,26,1662799,16,1425921,1310721,2,3,26,2,26,2,1310721,2,2,28, +1,6,1,10,28,34,63,38,3,26,1,6,1,10,28,34, +63,38,1,10,28,1662801,14,1,1662851,16,1442305,1310721,2,3,26,2, +26,2,3,32,1,6,1,10,28,34,63,38,1,10,28,1662853, +14,1,1662891,16,1310721,2,5,26,1310721,2,3,26,2,10,3,6, +10,4,8,1662895,14,1662637,14,1310721,2,5,26,2,1,40,1662919,16, +1,1663173,14,1559041,1310721,2,5,26,2,26,2,1662959,16,1559041,1310721,2, +5,26,2,26,2,1663173,14,1310721,2,55,26,1434113,1310721,2,5,26, +2,26,2,3,38,63,32,10,3,6,10,4,8,1310721,2,57, +26,1,10,3,6,10,4,8,1310721,2,57,26,2,1310721,2,55, +26,2,40,1663125,16,1310721,2,7,26,1310721,2,57,26,2,26,1587687, +2,10,3,6,10,4,8,1310721,2,57,26,1,6,2,1,6, +3,26,5,6,4,10,8,8,1663025,14,1587687,2,1310721,2,55,26, +2,1310721,2,7,26,1434113,1310721,2,5,26,2,26,2,1633771,12,1663173, +14,1,1310721,2,59,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721, +2,1835009,40,1663221,16,1966081,1310721,4,1579223,12,1310721,2,4,1310721,2,2, +1593805,12,1310721,2,3,26,4,1310721,2,3,26,2,1,40,1663271,16, +3,1663335,14,1417729,1310721,2,3,26,2,26,2,3,28,1,6,1, +10,28,34,63,38,1,10,28,1,6,1,10,28,34,63,38, +1,10,28,1663345,16,1,1663371,14,1425921,1310721,2,3,26,2,26,2, +1662565,12,1663371,14,1,1310721,2,5,26,1310721,4,18,1310721,2,1,28, +1310721,4,1310721,2,1835009,40,1663419,16,1966081,1310721,4,1579223,12,1587587,2,1663483, +16,1587699,2,1638921,12,1,6,1,10,28,34,63,38,3,26,1663467, +16,3,1594729,12,8,1587587,1,10,3,6,10,4,8,1,1663491,14, +1,1310721,2,1,26,1310721,4,18,1310721,2,11,28,1310721,4,1310721,2, +1835009,40,1663539,16,1966081,1310721,4,1579223,12,1310721,2,3,26,4,1310721,2, +4,1425921,1310721,2,2,26,2,1310721,2,5,26,4,1587593,2,1310721,2, +7,26,4,1587593,1310721,2,3,26,2,10,3,6,10,4,8,1587595, +1,10,3,6,10,4,8,1587699,1587687,2,10,3,6,10,4,8, +1587701,1,10,3,6,10,4,8,1310721,2,5,26,2,1,6,12, +10,8,8,1587593,1310721,2,7,26,2,10,3,6,10,4,8,1310721, +2,9,26,1587595,2,10,3,6,10,4,8,1587587,1,10,3,6, +10,4,8,1587589,1,10,3,6,10,4,8,1310721,2,9,26,2, +1663783,14,1,1310721,2,11,26,1310721,4,18,1310721,2,1,28,1310721,4, +1310721,2,1835009,40,1663831,16,1966081,1310721,4,1579223,12,1587593,2,1663931,16,1663923, +14,116,104,105,115,32,99,97,110,39,116,32,98,101,32,117, +115,101,100,32,105,110,115,105,100,101,32,97,110,32,101,120, +112,114,101,115,115,105,111,110,0,1663843,1591173,12,8,1,1663939,14, +1,1310721,2,1,26,1310721,4,18,1310721,2,17,28,1310721,4,1310721,2, +1835009,40,1663987,16,1966081,1310721,4,1579223,12,1310721,2,7,26,4,1310721,2, +5,26,4,1310721,2,3,26,4,1310721,2,4,1,1310721,2,9,26, +4,1587599,2,1606959,12,8,1587601,2,1606425,12,1,6,1,10,28,34, +63,38,3,26,1664603,16,3,1664603,16,1310721,2,11,26,1681059,12,10, +3,6,10,4,8,1663385,12,8,1310721,2,15,26,1587699,2,10,3, +6,10,4,8,1310721,2,11,26,2,1310721,2,2,1310721,2,9,26, +2,1310721,2,9,26,2,26,38,7,32,1625017,12,8,1310721,2,7, +26,2,1664241,16,1310721,2,15,26,2,1658673,12,1,6,1,10,28, +34,63,38,1,10,28,1664243,14,1,1664297,16,1310721,2,15,26,2, +1594643,12,8,1590517,2,1595409,12,8,1587699,1310721,2,15,26,2,10,3, +6,10,4,8,1310721,2,3,26,2,1664365,16,1310721,2,9,26,2, +1310721,2,3,26,2,7,26,2,40,1,6,1,10,28,34,63, +38,1,10,28,1664367,14,1,1664529,16,1310721,2,13,26,1310721,2,3, +26,2,15,1310721,2,9,26,2,26,26,2,10,3,6,10,4, +8,1310721,2,13,26,2,1587699,2,1310721,2,5,26,2,1640879,12,1, +6,1,10,28,34,63,38,3,26,1664529,16,1664493,14,97,114,103, +117,109,101,110,116,0,1664475,1310721,2,13,26,2,1310721,2,5,26, +2,1637691,12,1587699,2,1643225,12,8,1310721,2,9,26,1,6,2,1, +6,3,26,5,6,4,10,8,8,1587611,2,1606729,12,1,6,1, +10,28,34,63,38,3,26,1664599,16,1664603,14,1664077,14,1587601,2,1606959, +12,8,1310721,2,9,26,2,1664629,14,1,1310721,2,17,26,1310721,4, +18,1310721,2,5,28,1310721,4,1310721,2,1835009,40,1664677,16,1966081,1310721,4, +1579223,12,1310721,2,1644591,12,10,3,6,10,4,8,1310721,2,2,1, +26,2,23,28,1,6,1,10,28,34,63,38,3,26,1665063,16, +1310721,2,2,9,26,2,1637519,12,1594643,12,8,1587611,2,1606729,12,1664821, +16,1310721,2,3,26,1681059,12,10,3,6,10,4,8,1663385,12,8, +1310721,2,3,26,2,1613033,12,8,1664957,14,1,1594643,12,8,1310721,2, +2,7,26,2,1664957,16,1664949,14,97,32,102,105,120,101,100,32, +97,114,114,97,121,32,110,101,101,100,115,32,105,116,115,32, +108,101,110,103,116,104,58,32,110,101,119,40,102,105,120,101, +100,32,91,93,84,44,32,110,41,0,1664849,1591173,12,8,1310721,2, +2,1594643,12,8,1587601,2,1606959,12,8,1590551,2,1595409,12,8,1587587,1, +10,3,6,10,4,8,1587589,1,10,3,6,10,4,8,1587699,1310721, +2,2,10,3,6,10,4,8,1587701,1,10,3,6,10,4,8, +3,1665583,14,1587769,2,1665311,16,1587611,2,1606729,12,1665197,16,1310721,2,3, +26,1681059,12,10,3,6,10,4,8,1663385,12,8,1310721,2,3,26, +2,1613033,12,8,1310721,2,2,1637519,12,3,28,1,6,1,10,28, +34,63,38,1,10,28,1665193,16,1310721,2,2,1637519,12,1594643,12,8, +31,1594729,12,8,1665213,14,1310721,2,2,1637519,12,1594643,12,8,1587601,2, +1606959,12,8,1587765,2,1595409,12,8,1587587,1,10,3,6,10,4,8, +1587589,1,10,3,6,10,4,8,1587699,1310721,2,2,1632877,12,10,3, +6,10,4,8,1587701,1,10,3,6,10,4,8,3,1665583,14,1587611, +2,1606729,12,1665455,16,1310721,2,3,26,1681059,12,10,3,6,10,4, +8,1663385,12,8,1310721,2,3,26,2,1613033,12,8,1310721,2,2,1637519, +12,3,28,1,6,1,10,28,34,63,38,1,10,28,1665433,16, +1310721,2,2,1637519,12,1594643,12,8,31,1594729,12,8,1310721,2,2,1, +1633251,12,1594643,12,8,1665483,14,1310721,2,2,1637519,12,1594643,12,8,1310721, +2,2,1594643,12,8,1587601,2,1606959,12,8,1587757,2,1595409,12,8,1587587, +1,10,3,6,10,4,8,1587589,1,10,3,6,10,4,8,1587699, +1310721,2,2,1632877,12,10,3,6,10,4,8,1587701,1,10,3,6, +10,4,8,3,1665583,14,1,1310721,2,5,26,1310721,4,18,1310721,2, +27,28,1310721,4,1310721,2,1835009,40,1665631,16,1966081,1310721,4,1579223,12,1310721, +2,4,1,1310721,2,5,26,4,1,1310721,2,7,26,4,1559041,1310721, +2,2,26,2,1310721,2,9,26,4,1409537,1310721,2,2,26,2,1587755, +2,28,1,6,1,10,28,34,63,38,3,26,1665787,16,1417729,1310721, +2,2,26,2,3,28,1,6,1,10,28,34,63,38,3,26, +1,6,1,10,28,34,63,38,1,10,28,1665789,14,1,1666117,16, +1605859,12,8,1587511,2,9,28,1,6,1,10,28,34,63,38,3, +26,1665949,16,1587513,2,1587623,2,28,1,6,1,10,28,34,63,38, +3,26,1665871,16,3,1665923,14,1587513,2,1587607,2,28,1,6,1,10, +28,34,63,38,3,26,1,6,1,10,28,34,63,38,1,10, +28,1,6,1,10,28,34,63,38,1,10,28,1665951,14,1,1665969, +16,1604999,12,8,1664643,12,1667095,14,1587511,2,5,28,1,6,1,10, +28,34,63,38,3,26,1666097,16,1587513,2,1644211,12,1666019,16,3,1666071, +14,1587513,2,1587639,2,28,1,6,1,10,28,34,63,38,3,26, +1,6,1,10,28,34,63,38,1,10,28,1,6,1,10,28, +34,63,38,1,10,28,1666099,14,1,1666117,16,1604999,12,8,1664643,12, +1667095,14,1587769,2,1666189,16,1409537,1310721,2,2,26,2,1587755,2,28,1, +6,1,10,28,34,63,38,3,26,1,6,1,10,28,34,63, +38,1,10,28,1666191,14,1,1666293,16,1310721,2,5,26,1587765,2,1593303, +12,10,3,6,10,4,8,1310721,2,5,26,2,1,40,3,36, +1666271,16,1310721,2,1310721,2,5,26,2,10,3,6,10,4,8,1310721, +2,5,26,1,10,3,6,10,4,8,1442305,1310721,2,2,26,2, +5,32,1666389,16,1310721,2,5,26,1434113,1310721,2,2,26,2,13,38, +10,3,6,10,4,8,1310721,2,7,26,1434113,1310721,2,2,26,2, +3,32,10,3,6,10,4,8,1666427,14,1442305,1310721,2,2,26,1442305, +1310721,2,2,26,2,9,34,10,3,6,10,4,8,1310721,2,11, +26,1,17,1574703,12,8,1590581,2,1666489,16,1310721,2,11,26,1590565,1590581, +2,1574491,12,8,1590581,1,10,3,6,10,4,8,1310721,2,3,26, +1,10,3,6,10,4,8,1587769,2,1666581,16,1442305,1310721,2,2,26, +2,1025,32,1,6,1,10,28,34,63,38,3,26,1,6,1, +10,28,34,63,38,1,10,28,1666583,14,1,1666653,16,1409537,1310721,2, +2,26,2,1587671,2,28,1,6,1,10,28,34,63,38,1,10, +28,1,6,1,10,28,34,63,38,1,10,28,1666655,14,1,1666681, +16,1310721,2,3,26,3,10,3,6,10,4,8,1310721,2,3,26, +1310721,2,5,26,2,1310721,2,9,26,2,1310721,2,11,26,1310721,2, +3,26,2,1663953,12,10,3,6,10,4,8,1442305,1310721,2,2,26, +2,5,32,1666843,16,1310721,2,3,26,2,1434113,1310721,2,2,26,2, +3,38,63,32,28,1,6,1,10,28,34,63,38,1,10,28, +1,6,1,10,28,34,63,38,1,10,28,1666845,14,1,1666925,16, +1409537,1310721,2,2,26,2,1666917,14,119,114,111,110,103,32,110,117, +109,98,101,114,32,111,102,32,97,114,103,117,109,101,110,116, +115,0,1666865,1591343,12,8,1310721,2,2,1595235,12,8,13,1594729,12,8, +1587587,1,10,3,6,10,4,8,1587589,1,10,3,6,10,4,8, +1587701,1,10,3,6,10,4,8,1587699,1587687,2,10,3,6,10,4, +8,1310721,2,9,26,2,1667067,16,1587699,1310721,2,9,26,2,9,26, +2,1310721,2,11,26,1637691,12,10,3,6,10,4,8,1310721,2,7, +26,2,1667087,16,3,1667095,14,1,1667095,14,1,1310721,2,27,26,1310721, +4,18,1310721,2,11,28,1310721,4,1310721,2,1835009,40,1667143,16,1966081,1310721, +4,1579223,12,1310721,2,4,1434113,1310721,2,2,26,2,31,32,1310721,2, +3,26,4,1434113,1310721,2,2,26,2,9,38,31,32,1310721,2,5, +26,4,1,1310721,2,7,26,4,1587599,2,1606959,12,8,1587601,2,1606425, +12,1,6,1,10,28,34,63,38,3,26,1667385,16,3,1667385,16, +1310721,2,9,26,1681059,12,10,3,6,10,4,8,1663385,12,8,1310721, +2,9,26,2,1613033,12,8,1310721,2,7,26,1,6,2,1,6, +3,26,5,6,4,10,8,8,1587611,2,1606729,12,1,6,1,10, +28,34,63,38,3,26,1667381,16,1667385,14,1667259,14,1587601,2,1606959,12, +8,1310721,2,7,26,2,1310721,2,3,26,2,28,1,6,1,10, +28,34,63,38,1,10,28,1667513,16,1409537,1310721,2,2,26,2,1667505, +14,119,114,111,110,103,32,110,117,109,98,101,114,32,111,102, +32,105,110,112,117,116,115,0,1667459,1591343,12,8,1587587,1,10,3, +6,10,4,8,1587589,1,10,3,6,10,4,8,1587699,1587687,2,10, +3,6,10,4,8,1587701,1,10,3,6,10,4,8,1442305,1310721,2, +2,26,2,129,32,1667635,16,1450497,1310721,2,2,26,2,3,28,1595235, +12,8,13,1594729,12,8,1,1667833,14,45,1425921,1310721,2,2,26,2, +26,1594729,12,8,1310721,2,5,26,2,1,28,1,6,1,10,28, +34,63,38,3,26,1667703,16,1,1594643,12,8,1310721,2,5,26,2, +3,10,40,1667825,16,1409537,1310721,2,2,26,2,1667817,14,108,101,97, +118,101,115,32,115,101,118,101,114,97,108,32,118,97,108,117, +101,115,59,32,117,115,101,32,105,116,32,102,114,111,109,32, +97,115,109,0,1667739,1591343,12,8,1,1667833,14,1,1310721,2,11,26, +1310721,4,18,1310721,2,9,28,1310721,4,1310721,2,1835009,40,1667881,16,1966081, +1310721,4,1579223,12,1587587,1,10,3,6,10,4,8,1587589,1,10,3, +6,10,4,8,1587701,1,10,3,6,10,4,8,1590561,1,10,3, +6,10,4,8,1585289,2,3,28,1,6,1,10,28,34,63,38, +3,26,1668093,16,1585291,2,1594643,12,8,1587701,1585291,2,1,28,1,6, +1,10,28,34,63,38,3,26,10,3,6,10,4,8,1590561,3, +10,3,6,10,4,8,1590563,1585291,2,10,3,6,10,4,8,1587699, +1587689,2,10,3,6,10,4,8,1604999,12,8,1,1670763,14,1585289,2, +7,28,1,6,1,10,28,34,63,38,3,26,1668169,16,1585291,2, +1598105,12,8,1604999,12,8,1587699,1587691,2,1632877,12,10,3,6,10,4, +8,1,1670763,14,1585289,2,9,28,1,6,1,10,28,34,63,38, +3,26,1668421,16,1585291,2,1587599,2,28,1,6,1,10,28,34,63, +38,3,26,1668289,16,1604999,12,8,1310721,2,7,26,1681059,12,10,3, +6,10,4,8,1587601,2,1606959,12,8,1310721,2,7,26,2,1670763,14, +1585291,2,1587603,2,28,1,6,1,10,28,34,63,38,3,26,1668331, +16,1683915,12,1670763,14,1668381,14,101,120,112,101,99,116,101,100,32, +97,110,32,101,120,112,114,101,115,115,105,111,110,0,1668335,1591557, +12,8,1,1594643,12,8,1587699,1587687,2,10,3,6,10,4,8,1, +1670763,14,1585289,2,5,28,1,6,1,10,28,34,63,38,1,10, +28,1668545,16,1668505,14,101,120,112,101,99,116,101,100,32,97,110, +32,101,120,112,114,101,115,115,105,111,110,0,1668459,1591557,12,8, +1,1594643,12,8,1587699,1587687,2,10,3,6,10,4,8,1,1670763,14, +1310721,2,1585291,2,10,3,6,10,4,8,1310721,2,3,26,1310721,2, +2,1611615,12,10,3,6,10,4,8,1310721,2,3,26,2,1,40, +3,36,1669011,16,1604999,12,8,1587565,2,1,6,1,10,28,34,63, +38,3,26,1668691,16,1310721,2,3,26,2,1612447,12,1,6,1,10, +28,34,63,38,1,10,28,1668693,14,1,1668841,16,1310721,2,2,1668833, +14,105,115,32,116,114,97,99,107,101,100,32,98,121,32,116, +104,101,32,97,110,97,108,121,122,101,114,32,97,110,100,32, +99,97,110,39,116,32,98,101,32,117,115,101,100,32,105,110, +32,117,110,99,104,101,99,107,101,100,32,99,111,100,101,0, +1668707,1591343,12,8,1310721,2,3,26,2,1595671,12,8,1587699,1556993,1310721,2, +3,26,2,26,2,10,3,6,10,4,8,1473025,1310721,2,3,26, +2,26,2,1668913,16,1,1670763,14,1587587,3,10,3,6,10,4,8, +1587589,3,10,3,6,10,4,8,1587591,1310721,2,3,26,2,10,3, +6,10,4,8,1310721,2,3,26,2,1612447,12,1669005,16,5,1310721,2, +3,26,2,26,1670763,14,1,1670763,14,1310721,2,5,26,1310721,2,2, +1593303,12,10,3,6,10,4,8,1310721,2,5,26,2,1,40,3, +36,1669129,16,1417729,1310721,2,5,26,2,26,2,11,28,1,6,1, +10,28,34,63,38,3,26,1,6,1,10,28,34,63,38,1, +10,28,1669131,14,1,1669287,16,1442305,1310721,2,5,26,2,26,2,33, +32,1,6,1,10,28,34,63,38,3,26,1669267,16,1310721,2,2, +1669259,14,99,97,110,39,116,32,98,101,32,117,115,101,100,32, +105,110,115,105,100,101,32,97,110,32,101,120,112,114,101,115, +115,105,111,110,0,1669189,1591691,12,8,1310721,2,5,26,2,3,1663505, +12,1670763,14,1310721,2,5,26,2,1,40,3,36,1669375,16,1417729,1310721, +2,5,26,2,26,2,15,28,1,6,1,10,28,34,63,38, +3,26,1,6,1,10,28,34,63,38,1,10,28,1669377,14,1, +1669477,16,1310721,2,2,1669437,14,105,115,32,97,32,116,121,112,101, +44,32,110,111,116,32,97,32,118,97,108,117,101,0,1669391,1591691, +12,8,1,1594643,12,8,1587699,1587687,2,10,3,6,10,4,8,1, +1670763,14,1604999,12,8,1587699,1587687,2,10,3,6,10,4,8,1310721,2, +5,26,2,1,40,3,36,1669589,16,1417729,1310721,2,5,26,2,26, +2,9,28,1,6,1,10,28,34,63,38,3,26,1,6,1, +10,28,34,63,38,1,10,28,1669591,14,1,1669741,16,1425921,1310721,2, +5,26,2,26,2,1594643,12,8,1587699,1587689,2,10,3,6,10,4, +8,1587701,1425921,1310721,2,5,26,2,26,2,1,28,1,6,1,10, +28,34,63,38,3,26,10,3,6,10,4,8,1590561,3,10,3, +6,10,4,8,1590563,1425921,1310721,2,5,26,2,26,2,10,3,6, +10,4,8,1,1670763,14,1310721,2,5,26,2,1,40,3,36,1669829, +16,1417729,1310721,2,5,26,2,26,2,5,28,1,6,1,10,28, +34,63,38,3,26,1,6,1,10,28,34,63,38,1,10,28, +1669831,14,1,1669945,16,1425921,1310721,2,5,26,2,26,2,1594643,12,8, +1559041,1310721,2,5,26,2,26,2,1669907,16,1587699,1559041,1310721,2,5,26, +2,26,2,10,3,6,10,4,8,1587587,3,10,3,6,10,4, +8,1587589,5,10,3,6,10,4,8,1,1670763,14,1310721,2,5,26, +2,1,40,3,36,1670033,16,1417729,1310721,2,5,26,2,26,2,7, +28,1,6,1,10,28,34,63,38,3,26,1,6,1,10,28, +34,63,38,1,10,28,1670035,14,1,1670117,16,1425921,1310721,2,5,26, +2,26,2,1594643,12,8,1559041,1310721,2,5,26,2,26,2,1670111,16, +1587699,1559041,1310721,2,5,26,2,26,2,10,3,6,10,4,8,1, +1670763,14,1310721,2,5,26,2,1,40,3,36,1670205,16,1417729,1310721,2, +5,26,2,26,2,13,28,1,6,1,10,28,34,63,38,3, +26,1,6,1,10,28,34,63,38,1,10,28,1670207,14,1,1670229, +16,1310721,2,5,26,2,1667109,12,1670763,14,1310721,2,5,26,2,1, +40,1670277,16,1310721,2,5,26,1310721,2,2,1595111,12,10,3,6,10, +4,8,1587729,2,1606425,12,1670335,16,1442305,1310721,2,5,26,2,26,2, +257,32,1,6,1,10,28,34,63,38,1,10,28,1670337,14,1, +1670659,16,1604999,12,8,1590581,1,10,3,6,10,4,8,3,1670537,16, +1590581,2,17,40,3,36,1670445,16,1670437,14,116,111,111,32,109,97, +110,121,32,116,121,112,101,32,97,114,103,117,109,101,110,116, +115,0,1670389,1591557,12,8,1590565,1590581,2,26,1644591,12,10,3,6,10, +4,8,1590581,1,6,2,1,6,3,26,5,6,4,10,8,8, +1587611,2,1606729,12,1,6,1,10,28,34,63,38,3,26,1670533,16, +1670537,14,1670363,14,1644489,12,8,1587599,2,1606425,12,1,6,1,10,28, +34,63,38,3,26,1670659,16,1670651,14,101,120,112,101,99,116,101, +100,32,40,32,97,102,116,101,114,32,116,104,101,32,116,121, +112,101,32,97,114,103,117,109,101,110,116,115,0,1670579,1591557,12, +8,1587599,2,1606425,12,1670689,16,1310721,2,5,26,2,1665597,12,1670763,14, +1310721,2,5,26,2,1595235,12,8,1559041,1310721,2,5,26,2,26,2, +1670755,16,1587699,1559041,1310721,2,5,26,2,26,2,10,3,6,10,4, +8,1,1670763,14,1,1310721,2,9,26,1310721,4,18,1310721,2,5,28, +1310721,4,1310721,2,1835009,40,1670811,16,1966081,1310721,4,1579223,12,1310721,2,3, +26,4,1310721,2,4,1587587,2,1,6,1,10,28,34,63,38,3, +26,1670943,16,1670935,14,43,43,32,97,110,100,32,45,45,32,110, +101,101,100,32,115,111,109,101,116,104,105,110,103,32,116,111, +32,97,115,115,105,103,110,32,116,111,0,1670859,1591557,12,8,1587589, +2,3,28,1,6,1,10,28,34,63,38,3,26,1671005,16,1587565, +2,1,6,1,10,28,34,63,38,1,10,28,1671007,14,1,1671045, +16,1587591,2,1612447,12,1,6,1,10,28,34,63,38,1,10,28, +1671047,14,1,1671147,16,1587591,2,1671139,14,111,119,110,115,32,97,110, +32,111,98,106,101,99,116,32,97,110,100,32,99,97,110,39, +116,32,98,101,32,105,110,99,114,101,109,101,110,116,101,100, +0,1671059,1591963,12,8,1587699,2,1638689,12,1671165,16,3,1671221,14,1587699,2, +1,26,2,11,28,1,6,1,10,28,34,63,38,3,26,1, +6,1,10,28,34,63,38,1,10,28,1671231,16,3,1671283,14,1587699, +2,1587687,2,28,1,6,1,10,28,34,63,38,3,26,1,6, +1,10,28,34,63,38,1,10,28,1671361,14,43,43,32,97,110, +100,32,45,45,32,110,101,101,100,32,97,32,110,117,109,98, +101,114,32,111,114,32,97,32,112,111,105,110,116,101,114,0, +1671287,1587699,2,1643625,12,8,1587587,1,10,3,6,10,4,8,1587589,1, +10,3,6,10,4,8,1587701,1,10,3,6,10,4,8,1,1594643, +12,8,7,1594729,12,8,3,1594729,12,8,1310721,2,2,1671523,16,1, +1594643,12,8,7,1594729,12,8,1310721,2,3,26,2,1594643,12,8,27, +1594729,12,8,5,1594643,12,8,7,1594729,12,8,5,1594729,12,8,1671587, +14,1310721,2,3,26,2,1594643,12,8,27,1594729,12,8,1,1594643,12, +8,7,1594729,12,8,5,1594643,12,8,7,1594729,12,8,5,1594729,12, +8,11,1594729,12,8,9,1594729,12,8,1,1671611,14,1,1310721,2,5, +26,1310721,4,18,1310721,2,11,28,1310721,4,1310721,2,1835009,40,1671659,16, +1966081,1310721,4,1579223,12,1310721,2,4,3,1,10,28,1310721,2,9,26, +4,1604999,12,8,1310721,2,3,26,1607249,12,10,3,6,10,4,8, +1663385,12,8,1310721,2,2,3,28,1,6,1,10,28,34,63,38, +3,26,1671889,16,1671877,14,117,115,101,32,97,32,102,105,101,108, +100,32,111,102,32,97,32,110,101,119,32,111,98,106,101,99, +116,32,111,110,108,121,32,97,102,116,101,114,32,98,105,110, +100,105,110,103,32,105,116,32,116,111,32,97,32,110,97,109, +101,0,1671757,1591845,12,8,1671953,14,1310721,2,2,5,40,3,36,1671953, +16,1310721,2,2,5,28,1612545,12,8,1310721,2,9,26,1310721,2,2, +5,28,10,3,6,10,4,8,1310721,2,5,26,1587699,2,10,3, +6,10,4,8,1310721,2,5,26,2,1,26,2,11,28,1,6, +1,10,28,34,63,38,3,26,1672057,16,1310721,2,5,26,1310721,2, +5,26,2,9,26,2,10,3,6,10,4,8,1310721,2,5,26, +2,1,26,2,17,28,1,6,1,10,28,34,63,38,1,10, +28,1672407,16,1590585,2,1,6,1,10,28,34,63,38,3,26,1672181, +16,1587703,2,1,6,1,10,28,34,63,38,3,26,1,6,1, +10,28,34,63,38,1,10,28,1672183,14,1,1672351,16,1590587,12,8, +93,1573255,12,8,1310721,2,3,26,2,1573495,12,8,1672245,14,58,32, +116,104,97,116,39,115,32,97,32,0,1672221,1573495,12,8,1587699,2, +1641969,12,8,1672337,14,44,32,110,111,116,32,97,32,115,116,114, +117,99,116,32,111,114,32,97,32,112,111,105,110,116,101,114, +32,116,111,32,111,110,101,0,1672267,1573495,12,8,1590731,12,8,1587699, +1587687,2,10,3,6,10,4,8,1587587,3,10,3,6,10,4,8, +1587589,9,10,3,6,10,4,8,1,1672963,14,1310721,2,7,26,1310721, +2,5,26,2,1310721,2,3,26,2,1650397,12,10,3,6,10,4, +8,1310721,2,7,26,2,1,40,1672695,16,1590585,2,1,6,1,10, +28,34,63,38,3,26,1672547,16,1587703,2,1,6,1,10,28,34, +63,38,3,26,1,6,1,10,28,34,63,38,1,10,28,1672549, +14,1,1672639,16,1590587,12,8,1310721,2,5,26,2,1641969,12,8,1672609, +14,32,104,97,115,32,110,111,32,102,105,101,108,100,32,0, +1672579,1573495,12,8,1310721,2,3,26,2,1573495,12,8,1590731,12,8,1587699, +1587687,2,10,3,6,10,4,8,1587587,3,10,3,6,10,4,8, +1587589,9,10,3,6,10,4,8,1,1672963,14,1310721,2,5,26,2, +9,26,2,1310721,2,7,26,2,7,30,5,26,26,2,1672789,16, +1310721,2,5,26,2,9,26,2,1310721,2,7,26,2,7,30,5, +26,26,2,1594643,12,8,27,1594729,12,8,1587699,1310721,2,5,26,2, +9,26,2,1310721,2,7,26,2,7,30,3,26,26,2,10,3, +6,10,4,8,1587587,3,10,3,6,10,4,8,1587701,1,10,3, +6,10,4,8,1310721,2,9,26,2,1,40,3,36,1672939,16,1587589, +7,10,3,6,10,4,8,1587591,1310721,2,9,26,2,10,3,6, +10,4,8,1672955,14,1587589,9,10,3,6,10,4,8,1,1672963,14, +1,1310721,2,11,26,1310721,4,18,1310721,2,33,28,1310721,4,1310721,2, +1835009,40,1673011,16,1966081,1310721,4,1579223,12,1667847,12,1310721,2,4,3,1676731, +16,1585289,2,5,28,1,6,1,10,28,34,63,38,3,26,1673115, +16,1585291,2,1587647,2,28,1,6,1,10,28,34,63,38,3,26, +1,6,1,10,28,34,63,38,1,10,28,1673117,14,1,1673201,16, +1604999,12,8,1310721,2,7,26,1644591,12,10,3,6,10,4,8,1663385, +12,8,1587699,1310721,2,7,26,2,10,3,6,10,4,8,1587589,1, +10,3,6,10,4,8,1673021,14,1585289,2,9,28,1,6,1,10, +28,34,63,38,1,10,28,1673245,16,1310721,2,2,1676743,14,1585291,2, +1587607,2,28,1,6,1,10,28,34,63,38,3,26,1675947,16,1310721, +2,31,26,1,10,3,6,10,4,8,1587769,2,1673339,16,1587587,2, +1,6,1,10,28,34,63,38,1,10,28,1673341,14,1,1673399,16, +1587589,2,3,28,1,6,1,10,28,34,63,38,3,26,1,6, +1,10,28,34,63,38,1,10,28,1673401,14,1,1673441,16,1589487,1587591, +2,26,2,1,6,1,10,28,34,63,38,1,10,28,1673443,14, +1,1673477,16,1310721,2,31,26,1589487,1587591,2,26,2,10,3,6,10, +4,8,1604999,12,8,1663385,12,8,1310721,2,3,26,3,1,10,28, +10,3,6,10,4,8,1310721,2,2,3,28,1,6,1,10,28, +34,63,38,3,26,1673669,16,1673657,14,105,110,100,101,120,32,97, +32,110,101,119,32,111,98,106,101,99,116,32,111,110,108,121, +32,97,102,116,101,114,32,98,105,110,100,105,110,103,32,105, +116,32,116,111,32,97,32,110,97,109,101,0,1673555,1591845,12,8, +1673733,14,1310721,2,2,5,40,3,36,1673733,16,1310721,2,2,5,28, +1612545,12,8,1310721,2,3,26,1310721,2,2,5,28,10,3,6,10, +4,8,1587699,2,1,26,2,23,28,1,6,1,10,28,34,63, +38,3,26,1674921,16,1310721,2,9,26,1587699,2,9,26,2,10,3, +6,10,4,8,1310721,2,31,26,2,1673857,16,1587763,2,1593303,12,1, +40,3,36,1,6,1,10,28,34,63,38,1,10,28,1673859,14, +1,1673871,16,9,1594729,12,8,1310721,2,1681059,12,10,3,6,10,4, +8,1663385,12,8,1310721,2,2,1613033,12,8,1587699,2,1638689,12,1673927,16, +3,1673979,14,1587699,2,1587687,2,28,1,6,1,10,28,34,63,38, +3,26,1,6,1,10,28,34,63,38,1,10,28,1674039,14,97, +110,32,105,110,100,101,120,32,104,97,115,32,116,111,32,98, +101,32,97,32,110,117,109,98,101,114,0,1673983,1587699,2,1643625,12, +8,1587609,2,1606959,12,8,1310721,2,29,26,1587763,2,1593303,12,10,3, +6,10,4,8,1310721,2,31,26,2,1674147,16,1310721,2,29,26,2, +1,40,3,36,1,6,1,10,28,34,63,38,1,10,28,1674149, +14,1,1674219,16,1417729,1310721,2,29,26,2,26,2,13,28,1,6, +1,10,28,34,63,38,3,26,1,6,1,10,28,34,63,38, +1,10,28,1674221,14,1,1674501,16,1,1594643,12,8,7,1594729,12,8, +1310721,2,31,26,2,3,26,1595671,12,8,3,1594729,12,8,45,1425921, +1310721,2,29,26,2,26,2,26,1594729,12,8,1310721,2,29,26,1595029, +12,10,3,6,10,4,8,17,1594729,12,8,1310721,2,31,26,2, +3,26,1595671,12,8,3,1594729,12,8,1587767,2,1595409,12,8,1310721,2, +29,26,2,1587549,2,1594811,12,8,1310721,2,31,26,2,1595671,12,8, +3,1594729,12,8,11,1594729,12,8,1310721,2,9,26,2,1637519,12,3, +28,1,6,1,10,28,34,63,38,1,10,28,1674489,16,1310721,2, +9,26,2,1637519,12,1594643,12,8,31,1594729,12,8,27,1594729,12,8, +1674917,14,1587769,2,1674553,16,1310721,2,29,26,2,1,40,3,36,1, +6,1,10,28,34,63,38,1,10,28,1674555,14,1,1674625,16,1417729, +1310721,2,29,26,2,26,2,13,28,1,6,1,10,28,34,63, +38,3,26,1,6,1,10,28,34,63,38,1,10,28,1674627,14, +1,1674907,16,1,1594643,12,8,7,1594729,12,8,5,1594643,12,8,7, +1594729,12,8,3,1594729,12,8,45,1425921,1310721,2,29,26,2,26,2, +26,1594729,12,8,1310721,2,29,26,1595029,12,10,3,6,10,4,8, +17,1594729,12,8,3,1594643,12,8,7,1594729,12,8,3,1594729,12,8, +1587767,2,1595409,12,8,1310721,2,29,26,2,1587549,2,1594811,12,8,11, +1594729,12,8,5,1594643,12,8,27,1594729,12,8,3,1594729,12,8,11, +1594729,12,8,1310721,2,9,26,2,1637519,12,3,28,1,6,1,10, +28,34,63,38,1,10,28,1674895,16,1310721,2,9,26,2,1637519,12, +1594643,12,8,31,1594729,12,8,27,1594729,12,8,1674917,14,1590553,2,1595409, +12,8,1675787,14,1587699,2,1639085,12,1674939,16,3,1674991,14,1587699,2,1587687, +2,28,1,6,1,10,28,34,63,38,3,26,1,6,1,10, +28,34,63,38,1,10,28,1675075,14,111,110,108,121,32,112,111, +105,110,116,101,114,115,32,97,110,100,32,97,114,114,97,121, +115,32,99,97,110,32,98,101,32,105,110,100,101,120,101,100, +0,1674995,1587699,2,1643625,12,8,1310721,2,9,26,1587699,2,1639375,12,10, +3,6,10,4,8,1310721,2,5,26,1,3,28,10,3,6,10, +4,8,1587699,2,1,26,2,13,28,1,6,1,10,28,34,63, +38,3,26,1675221,16,1587699,2,7,26,2,1,10,40,1,6,1, +10,28,34,63,38,1,10,28,1675223,14,1,1675257,16,1310721,2,5, +26,1587699,2,7,26,2,10,3,6,10,4,8,1590561,1,10,3, +6,10,4,8,1310721,2,1681059,12,10,3,6,10,4,8,1663385,12, +8,1310721,2,2,1613033,12,8,1310721,2,5,26,2,1,40,3,36, +1675363,16,1590561,2,1,6,1,10,28,34,63,38,1,10,28,1675365, +14,1,1675455,16,1590563,2,1,40,1675387,16,3,1675429,14,1590563,2,1310721, +2,5,26,2,40,3,36,1,6,1,10,28,34,63,38,1, +10,28,1,6,1,10,28,34,63,38,1,10,28,1675457,14,1, +1675555,16,1675547,14,116,104,101,32,105,110,100,101,120,32,105,115, +32,111,117,116,32,111,102,32,114,97,110,103,101,32,102,111, +114,32,116,104,105,115,32,97,114,114,97,121,0,1675465,1591173,12, +8,1587699,2,1638689,12,1675573,16,3,1675625,14,1587699,2,1587687,2,28,1, +6,1,10,28,34,63,38,3,26,1,6,1,10,28,34,63, +38,1,10,28,1675685,14,97,110,32,105,110,100,101,120,32,104, +97,115,32,116,111,32,98,101,32,97,32,110,117,109,98,101, +114,0,1675629,1587699,2,1643625,12,8,1587609,2,1606959,12,8,1310721,2,9, +26,2,1637519,12,3,28,1,6,1,10,28,34,63,38,1,10, +28,1675779,16,1310721,2,9,26,2,1637519,12,1594643,12,8,31,1594729,12, +8,27,1594729,12,8,1587587,3,10,3,6,10,4,8,1587699,1310721,2, +9,26,2,10,3,6,10,4,8,1587701,1,10,3,6,10,4, +8,1310721,2,3,26,2,1,40,3,36,1675909,16,1587589,7,10,3, +6,10,4,8,1587591,1310721,2,3,26,2,10,3,6,10,4,8, +1675925,14,1587589,9,10,3,6,10,4,8,1310721,2,1,10,3,6, +10,4,8,1673021,14,1585291,2,1587637,2,28,1,6,1,10,28,34, +63,38,3,26,1675987,16,3,1676039,14,1585291,2,1587617,2,28,1,6, +1,10,28,34,63,38,3,26,1,6,1,10,28,34,63,38, +1,10,28,1676073,16,1310721,2,1310721,2,2,1671625,12,10,3,6,10, +4,8,1673021,14,1585291,2,1587599,2,28,1,6,1,10,28,34,63, +38,3,26,1676575,16,1663385,12,8,1310721,2,2,1613033,12,8,1310721,2, +11,26,1,10,3,6,10,4,8,1587699,2,1,26,2,15,28, +1,6,1,10,28,34,63,38,3,26,1676213,16,1310721,2,11,26, +1587699,2,10,3,6,10,4,8,1676317,14,1587699,2,1587687,2,28,1, +6,1,10,28,34,63,38,3,26,1676305,14,111,110,108,121,32, +102,117,110,99,116,105,111,110,115,32,99,97,110,32,98,101, +32,99,97,108,108,101,100,0,1676247,1587699,2,1643625,12,8,1310721,2, +13,26,1,17,1574703,12,8,1310721,2,5,26,1,1310721,2,11,26, +2,1310721,2,13,26,1587769,2,1663953,12,10,3,6,10,4,8,1310721, +2,5,26,2,1594643,12,8,7,1594729,12,8,13,1594729,12,8,11, +1594729,12,8,9,1594729,12,8,1587587,1,10,3,6,10,4,8,1587589, +1,10,3,6,10,4,8,1310721,2,1,10,3,6,10,4,8, +1587701,1,10,3,6,10,4,8,1587699,1587687,2,10,3,6,10,4, +8,1310721,2,11,26,2,1676571,16,1587699,1310721,2,11,26,2,9,26, +2,1310721,2,13,26,1637691,12,10,3,6,10,4,8,1673021,14,1585291, +2,1587619,2,28,1,6,1,10,28,34,63,38,3,26,1676643,16, +1604999,12,8,1310721,2,3,3,1670777,12,10,3,6,10,4,8,1673021, +14,1585291,2,1587621,2,28,1,6,1,10,28,34,63,38,3,26, +1676717,16,1604999,12,8,1310721,2,3,3,1,10,28,1670777,12,10,3, +6,10,4,8,1673021,14,1310721,2,2,1676743,14,1673021,14,1310721,2,2, +1676743,14,1,1310721,2,33,26,1310721,4,18,1310721,2,7,28,1310721,4, +1310721,2,1835009,40,1676791,16,1966081,1310721,4,1579223,12,1585289,2,5,28,1, +6,1,10,28,34,63,38,3,26,1677003,16,1585291,2,1587657,2,28, +1,6,1,10,28,34,63,38,3,26,1676863,16,3,1676915,14,1585291, +2,1587653,2,28,1,6,1,10,28,34,63,38,3,26,1,6, +1,10,28,34,63,38,1,10,28,1676925,16,3,1676977,14,1585291,2, +1587655,2,28,1,6,1,10,28,34,63,38,3,26,1,6,1, +10,28,34,63,38,1,10,28,1,6,1,10,28,34,63,38, +1,10,28,1677005,14,1,1677225,16,1310721,2,5,26,3,10,3,6, +10,4,8,1585291,2,1587657,2,28,1,6,1,10,28,34,63,38, +3,26,1677087,16,1310721,2,5,26,5,10,3,6,10,4,8,1585291, +2,1587655,2,28,1,6,1,10,28,34,63,38,3,26,1677143,16, +1310721,2,5,26,7,10,3,6,10,4,8,1604999,12,8,1587623,2, +1606959,12,8,1310721,2,1676757,12,10,3,6,10,4,8,1663385,12,8, +1587589,1,10,3,6,10,4,8,1310721,2,2,1310721,2,5,26,2, +1623945,12,1678623,14,1585289,2,9,28,1,6,1,10,28,34,63,38, +3,26,1678613,16,1585291,2,1587623,2,28,1,6,1,10,28,34,63, +38,3,26,1677591,16,1604999,12,8,1310721,2,1676757,12,10,3,6,10, +4,8,1663385,12,8,1310721,2,2,1613033,12,8,1587699,2,1,26,2, +11,28,1,6,1,10,28,34,63,38,3,26,1677379,16,3,1677431, +14,1587699,2,1587687,2,28,1,6,1,10,28,34,63,38,3,26, +1,6,1,10,28,34,63,38,1,10,28,1677503,14,111,110,108, +121,32,112,111,105,110,116,101,114,115,32,99,97,110,32,98, +101,32,100,101,114,101,102,101,114,101,110,99,101,100,0,1677435, +1587699,2,1643625,12,8,1587699,1587699,2,1639375,12,10,3,6,10,4,8, +1587587,3,10,3,6,10,4,8,1587589,9,10,3,6,10,4,8, +1587701,1,10,3,6,10,4,8,1,1678623,14,1585291,2,1587625,2,28, +1,6,1,10,28,34,63,38,3,26,1678107,16,1604999,12,8,1310721, +2,1676757,12,10,3,6,10,4,8,1587587,2,1,6,1,10,28, +34,63,38,3,26,1677733,16,1587699,2,1638921,12,1,6,1,10,28, +34,63,38,3,26,1,6,1,10,28,34,63,38,1,10,28, +1677735,14,1,1677811,16,1677803,14,38,32,110,101,101,100,115,32,115, +111,109,101,116,104,105,110,103,32,97,100,100,114,101,115,115, +97,98,108,101,0,1677743,1591557,12,8,1587587,2,1677873,16,1587589,2,3, +28,1,6,1,10,28,34,63,38,3,26,1,6,1,10,28, +34,63,38,1,10,28,1677875,14,1,1677919,16,1558017,1587591,2,26,2, +33,32,1,6,1,10,28,34,63,38,1,10,28,1677921,14,1, +1677997,16,1677989,14,97,110,32,111,51,32,118,97,114,105,97,98, +108,101,32,104,97,115,32,110,111,32,97,100,100,114,101,115, +115,0,1677929,1591173,12,8,1310721,2,2,5,40,3,36,1678031,16,1310721, +2,2,5,28,1612545,12,8,1587587,1,10,3,6,10,4,8,1587589, +1,10,3,6,10,4,8,1587701,1,10,3,6,10,4,8,1587699, +1587699,2,1632877,12,10,3,6,10,4,8,1,1678623,14,1585291,2,1587619, +2,28,1,6,1,10,28,34,63,38,3,26,1678165,16,1604999,12, +8,1676757,12,8,1,3,1670777,12,1678623,14,1585291,2,1587621,2,28,1, +6,1,10,28,34,63,38,3,26,1678229,16,1604999,12,8,1676757,12, +8,1,3,1,10,28,1670777,12,1678623,14,1310721,2,3,26,1585291,2, +1609683,12,10,3,6,10,4,8,1310721,2,3,26,2,1,40,3, +36,1678613,16,1604999,12,8,1310721,2,1676757,12,10,3,6,10,4,8, +1663385,12,8,1310721,2,2,1613033,12,8,1547329,1310721,2,3,26,2,26, +2,1547393,1310721,2,3,26,2,26,2,1608745,12,8,1590561,1,10,3, +6,10,4,8,1310721,2,3,26,2,3,10,40,3,36,1678423,16, +1587699,1587693,2,10,3,6,10,4,8,1678559,14,1587699,2,1587687,2,28, +1,6,1,10,28,34,63,38,1,10,28,1678559,16,1587699,2,1638689, +12,1678529,14,116,104,105,115,32,111,112,101,114,97,116,111,114, +32,110,101,101,100,115,32,97,32,110,117,109,98,101,114,0, +1678471,1587699,2,1643625,12,8,1587699,1587689,2,10,3,6,10,4,8,1587587, +1,10,3,6,10,4,8,1587589,1,10,3,6,10,4,8,1587701, +1,10,3,6,10,4,8,1,1678623,14,1672977,12,1678623,14,1,1310721, +2,7,26,1310721,4,18,1310721,2,15,28,1310721,4,1310721,2,1835009,40, +1678671,16,1966081,1310721,4,1579223,12,1310721,2,4,1676757,12,1310721,2,3,26, +4,1585289,2,9,28,1,6,1,10,28,34,63,38,3,26,1679633, +16,1310721,2,5,26,1585291,2,1609487,12,10,3,6,10,4,8,1310721, +2,5,26,2,1,40,1678783,16,1310721,2,3,26,2,1679649,14,1310721, +2,7,26,1546881,1310721,2,5,26,2,26,2,10,3,6,10,4, +8,1310721,2,7,26,2,1310721,2,2,40,1678855,16,1310721,2,3,26, +2,1679649,14,1604999,12,8,1663385,12,8,1310721,2,3,26,2,1613033,12, +8,1310721,2,13,26,1587699,2,10,3,6,10,4,8,1547137,1310721,2, +5,26,2,26,2,3,1,10,28,28,1,6,1,10,28,34, +63,38,3,26,1679155,16,1310721,2,9,26,1595029,12,10,3,6,10, +4,8,17,1594729,12,8,1310721,2,3,26,1310721,2,7,26,2,3, +26,1678637,12,10,3,6,10,4,8,1663385,12,8,1310721,2,3,26, +2,1613033,12,8,1547329,3,26,2,1547393,3,26,2,1608745,12,8,1310721, +2,11,26,1595029,12,10,3,6,10,4,8,15,1594729,12,8,1310721, +2,9,26,2,1587549,2,1594811,12,8,1,1594643,12,8,1310721,2,11, +26,2,1587549,2,1594811,12,8,1679501,14,1547137,1310721,2,5,26,2,26, +2,5,1,10,28,28,1,6,1,10,28,34,63,38,3,26, +1679403,16,1310721,2,9,26,1595029,12,10,3,6,10,4,8,17,1594729, +12,8,3,1594643,12,8,1310721,2,11,26,1595029,12,10,3,6,10, +4,8,15,1594729,12,8,1310721,2,9,26,2,1587549,2,1594811,12,8, +1310721,2,3,26,1310721,2,7,26,2,3,26,1678637,12,10,3,6, +10,4,8,1663385,12,8,1310721,2,3,26,2,1613033,12,8,1547329,3, +26,2,1547393,3,26,2,1608745,12,8,1310721,2,11,26,2,1587549,2, +1594811,12,8,1679501,14,1310721,2,3,26,1310721,2,7,26,2,3,26, +1678637,12,10,3,6,10,4,8,1663385,12,8,1310721,2,3,26,2, +1613033,12,8,1547009,1310721,2,5,26,2,26,2,1547137,1310721,2,5,26, +2,26,2,1608745,12,8,1587699,1310721,2,5,26,2,1310721,2,13,26, +2,1587699,2,1655517,12,10,3,6,10,4,8,1587587,1,10,3,6, +10,4,8,1587589,1,10,3,6,10,4,8,1310721,2,3,26,1, +10,3,6,10,4,8,1587701,1,10,3,6,10,4,8,1590561,1, +10,3,6,10,4,8,1678691,14,1310721,2,3,26,2,1679649,14,1, +1310721,2,15,26,1310721,4,18,1310721,2,1,28,1310721,4,1310721,2,1835009, +40,1679697,16,1966081,1310721,4,1579223,12,11,1594729,12,8,3,1594643,12,8, +7,1594729,12,8,11,1594729,12,8,5,1594729,12,8,1,1679745,14,1, +1310721,2,1,26,1310721,4,18,1310721,2,23,28,1310721,4,1310721,2,1835009, +40,1679793,16,1966081,1310721,4,1579223,12,1310721,2,4,1587569,2,1310721,2,13, +26,4,1587557,2,1310721,2,15,26,4,1663385,12,8,1310721,2,2,1613033, +12,8,1604999,12,8,1310721,2,3,26,1595029,12,10,3,6,10,4, +8,17,1594729,12,8,1310721,2,7,26,1615017,12,10,3,6,10,4, +8,1310721,2,17,26,1681059,12,10,3,6,10,4,8,1663385,12,8, +1310721,2,21,26,1587699,2,10,3,6,10,4,8,1310721,2,17,26, +2,3,28,1,6,1,10,28,34,63,38,1,10,28,1680017,16, +1310721,2,17,26,2,1613033,12,8,1310721,2,11,26,1587569,2,10,3, +6,10,4,8,1310721,2,9,26,1615017,12,10,3,6,10,4,8, +1310721,2,5,26,1595029,12,10,3,6,10,4,8,15,1594729,12,8, +1587627,2,1606959,12,8,1310721,2,3,26,2,1587549,2,1594811,12,8,1310721, +2,7,26,2,1615649,12,8,1587569,1310721,2,13,26,2,10,3,6, +10,4,8,1310721,2,19,26,1681059,12,10,3,6,10,4,8,1663385, +12,8,1310721,2,19,26,2,3,28,1,6,1,10,28,34,63, +38,1,10,28,1680253,16,1310721,2,19,26,2,1613033,12,8,1310721,2, +9,26,2,1310721,2,11,26,2,1310721,2,5,26,2,1587569,2,1310721, +2,15,26,2,1680391,14,101,110,100,115,32,116,104,101,32,116, +119,111,32,115,105,100,101,115,32,111,102,32,63,58,32,105, +110,32,100,105,102,102,101,114,101,110,116,32,115,116,97,116, +101,115,0,1680301,1623371,12,8,1310721,2,7,26,2,1615567,12,8,1310721, +2,21,26,2,1587699,2,1639563,12,1,6,1,10,28,34,63,38, +3,26,1680521,16,1587699,2,1310721,2,21,26,2,1639563,12,1,6,1, +10,28,34,63,38,3,26,1,6,1,10,28,34,63,38,1, +10,28,1680523,14,1,1680593,16,1680571,14,116,104,101,32,116,119,111, +32,115,105,100,101,115,32,111,102,32,63,58,0,1680531,1310721,2, +21,26,2,1587699,2,1643225,12,8,1310721,2,21,26,2,1587687,2,28, +1,6,1,10,28,34,63,38,1,10,28,1680659,16,1587699,1310721,2, +21,26,2,10,3,6,10,4,8,1587587,1,10,3,6,10,4, +8,1587589,1,10,3,6,10,4,8,1587701,1,10,3,6,10,4, +8,1310721,2,17,26,2,3,28,1,6,1,10,28,34,63,38, +3,26,1680805,16,1310721,2,19,26,2,3,28,1,6,1,10,28, +34,63,38,3,26,1,6,1,10,28,34,63,38,1,10,28, +1680807,14,1,1680817,16,3,1681045,14,1310721,2,17,26,2,3,28,1, +6,1,10,28,34,63,38,3,26,1680861,16,3,1680917,14,1310721,2, +19,26,2,3,28,1,6,1,10,28,34,63,38,3,26,1, +6,1,10,28,34,63,38,1,10,28,1681037,16,1681029,14,98,111, +116,104,32,115,105,100,101,115,32,111,102,32,63,58,32,104, +97,118,101,32,116,111,32,98,101,32,110,101,119,32,111,98, +106,101,99,116,115,44,32,111,114,32,110,101,105,116,104,101, +114,0,1680925,1591845,12,8,1,1681045,14,1,1310721,2,23,26,1310721,4, +18,1310721,2,13,28,1310721,4,1310721,2,1835009,40,1681093,16,1966081,1310721,4, +1579223,12,3,1678637,12,1310721,2,4,1585289,2,9,28,1,6,1,10, +28,34,63,38,1,10,28,1681149,16,1310721,2,2,1683407,14,1585291,2, +1587659,2,28,1,6,1,10,28,34,63,38,3,26,1681197,16,1310721, +2,2,1679759,12,1683407,14,1585291,2,1587615,2,28,1,6,1,10,28, +34,63,38,3,26,1682243,16,1587587,2,1,6,1,10,28,34,63, +38,3,26,1681323,16,1681315,14,116,104,105,115,32,99,97,110,39, +116,32,98,101,32,97,115,115,105,103,110,101,100,32,116,111, +0,1681263,1591557,12,8,1310721,2,3,26,1587589,2,10,3,6,10,4, +8,1310721,2,5,26,1587591,2,10,3,6,10,4,8,1310721,2,11, +26,1587699,2,10,3,6,10,4,8,1310721,2,11,26,2,1638921,12, +1681547,16,1681539,14,97,32,119,104,111,108,101,32,115,116,114,117, +99,116,32,111,114,32,97,114,114,97,121,32,99,97,110,39, +116,32,98,101,32,97,115,115,105,103,110,101,100,59,32,97, +115,115,105,103,110,32,105,116,115,32,102,105,101,108,100,115, +0,1681417,1591173,12,8,1604999,12,8,1310721,2,7,26,1587769,2,1681629,16, +1310721,2,3,26,2,3,28,1,6,1,10,28,34,63,38,3, +26,1,6,1,10,28,34,63,38,1,10,28,1681631,14,1,1681681, +16,1558017,1310721,2,5,26,2,26,2,33,32,1,6,1,10,28, +34,63,38,1,10,28,1681683,14,1,1681749,16,1473025,1310721,2,5,26, +2,26,2,1,6,1,10,28,34,63,38,3,26,1,6,1, +10,28,34,63,38,1,10,28,1681751,14,1,10,3,6,10,4, +8,1310721,2,7,26,2,1681785,16,9,1594729,12,8,1310721,2,9,26, +1681059,12,10,3,6,10,4,8,1663385,12,8,1310721,2,11,26,2, +1587699,2,1639563,12,1,6,1,10,28,34,63,38,3,26,1681905,16, +1681883,14,97,115,115,105,103,110,109,101,110,116,0,1681861,1310721,2, +11,26,2,1587699,2,1643225,12,8,1310721,2,7,26,2,1681979,16,1, +1594643,12,8,7,1594729,12,8,1310721,2,5,26,2,1595671,12,8,5, +1594729,12,8,1310721,2,5,26,2,1597105,12,8,1682133,14,1587769,2,1682049, +16,1310721,2,3,26,2,7,28,1,6,1,10,28,34,63,38, +1,10,28,1,6,1,10,28,34,63,38,1,10,28,1682051,14, +1,1682095,16,1310721,2,11,26,2,1658673,12,1,6,1,10,28,34, +63,38,1,10,28,1682097,14,1,1682127,16,1310721,2,11,26,2,1594643, +12,8,1590517,2,1595409,12,8,1679663,12,8,1587587,1,10,3,6,10, +4,8,1587589,1,10,3,6,10,4,8,1587699,1310721,2,11,26,2, +10,3,6,10,4,8,1587701,1,10,3,6,10,4,8,1310721,2, +3,26,2,1310721,2,5,26,2,1310721,2,9,26,2,1626785,12,1683407, +14,1310721,2,7,26,1585291,2,1609879,12,10,3,6,10,4,8,1310721, +2,7,26,2,1,40,3,36,1683395,16,1587587,2,1,6,1,10, +28,34,63,38,3,26,1682385,16,1682377,14,116,104,105,115,32,99, +97,110,39,116,32,98,101,32,97,115,115,105,103,110,101,100, +32,116,111,0,1682325,1591557,12,8,1587589,2,3,28,1,6,1,10, +28,34,63,38,3,26,1682447,16,1587565,2,1,6,1,10,28,34, +63,38,1,10,28,1682449,14,1,1682487,16,1587591,2,1612447,12,1,6, +1,10,28,34,63,38,1,10,28,1682489,14,1,1682609,16,1587591,2, +1682601,14,111,119,110,115,32,97,110,32,111,98,106,101,99,116, +59,32,99,111,109,112,111,117,110,100,32,97,115,115,105,103, +110,109,101,110,116,32,119,111,117,108,100,32,108,111,115,101, +32,105,116,0,1682501,1591963,12,8,1310721,2,11,26,1587699,2,10,3, +6,10,4,8,1604999,12,8,1310721,2,5,26,1587769,2,1682709,16,1587589, +2,3,28,1,6,1,10,28,34,63,38,3,26,1,6,1, +10,28,34,63,38,1,10,28,1682711,14,1,1682755,16,1558017,1587591,2, +26,2,33,32,1,6,1,10,28,34,63,38,1,10,28,1682757, +14,1,1682817,16,1473025,1587591,2,26,2,1,6,1,10,28,34,63, +38,3,26,1,6,1,10,28,34,63,38,1,10,28,1682819,14, +1,10,3,6,10,4,8,1310721,2,5,26,2,1682939,16,1310721,2, +5,26,1587591,2,10,3,6,10,4,8,9,1594729,12,8,1310721,2, +5,26,2,1595671,12,8,3,1594729,12,8,1310721,2,5,26,1310721,2, +5,26,2,3,26,10,3,6,10,4,8,1682963,14,1,1594643,12, +8,7,1594729,12,8,3,1594729,12,8,1587587,1,10,3,6,10,4, +8,1310721,2,9,26,1681059,12,10,3,6,10,4,8,1663385,12,8, +1310721,2,9,26,2,1613033,12,8,1547009,1547585,1310721,2,7,26,2,26, +2,26,2,1547137,1547585,1310721,2,7,26,2,26,2,26,2,1608745,12, +8,1310721,2,3,26,1547585,1310721,2,7,26,2,26,2,1310721,2,11, +26,2,1587699,2,1655517,12,10,3,6,10,4,8,1310721,2,11,26, +2,1310721,2,3,26,2,1639563,12,1,6,1,10,28,34,63,38, +3,26,1683249,16,1683221,14,99,111,109,112,111,117,110,100,32,97, +115,115,105,103,110,109,101,110,116,0,1683181,1310721,2,11,26,2, +1310721,2,3,26,2,1643225,12,8,1310721,2,5,26,2,1683311,16,1, +1594643,12,8,7,1594729,12,8,1310721,2,5,26,2,3,28,1595671,12, +8,5,1594729,12,8,1683317,14,1679663,12,8,1587587,1,10,3,6,10, +4,8,1587589,1,10,3,6,10,4,8,1587699,1310721,2,11,26,2, +10,3,6,10,4,8,1587701,1,10,3,6,10,4,8,1,1683407, +14,1310721,2,2,1683407,14,1,1310721,2,13,26,1310721,4,18,1310721,2, +3,28,1310721,4,1310721,2,1835009,40,1683455,16,1966081,1310721,4,1579223,12,1585289, +2,9,28,1,6,1,10,28,34,63,38,3,26,1683619,16,1585291, +2,1587613,2,28,1,6,1,10,28,34,63,38,3,26,1683527,16, +3,1683579,14,1585291,2,1587603,2,28,1,6,1,10,28,34,63,38, +3,26,1,6,1,10,28,34,63,38,1,10,28,1683589,16,3, +1683615,14,1646981,12,1,6,1,10,28,34,63,38,1,10,28,1683901, +14,1585289,2,5,28,1,6,1,10,28,34,63,38,1,10,28, +1683659,16,3,1683697,14,1585291,2,1611615,12,1,40,3,36,1,6,1, +10,28,34,63,38,1,10,28,1683707,16,1,1683901,14,1646981,12,1683721, +16,3,1683901,14,1310721,2,1585291,2,1593303,12,10,3,6,10,4,8, +1310721,2,2,1,40,3,36,1683825,16,1417729,1310721,2,2,26,2,11, +28,1,6,1,10,28,34,63,38,3,26,1,6,1,10,28, +34,63,38,1,10,28,1683827,14,1,1683893,16,1442305,1310721,2,2,26, +2,33,32,1,6,1,10,28,34,63,38,3,26,1,6,1, +10,28,34,63,38,1,10,28,1683895,14,1,1683901,14,1,1310721,2, +3,26,1310721,4,18,1310721,2,9,28,1310721,4,1310721,2,1835009,40,1683949, +16,1966081,1310721,4,1579223,12,1587557,2,1310721,2,4,1,1310721,2,3,26, +4,1,1310721,2,5,26,4,1587689,2,1310721,2,7,26,4,1604999,12, +8,1587561,1,6,2,1,6,3,26,5,6,4,10,8,8,1587571, +1,6,2,1,6,3,26,5,6,4,10,8,8,1587605,2,1606425, +12,1,6,1,10,28,34,63,38,3,26,1684431,16,1585289,2,1, +28,1,6,1,10,28,34,63,38,3,26,1684155,16,1684147,14,109, +105,115,115,105,110,103,32,125,0,1684127,1591557,12,8,1683421,12,1684173, +16,1686451,12,8,1684059,14,1310721,2,3,26,1681059,12,10,3,6,10, +4,8,1663385,12,8,1587613,2,1606729,12,1684265,16,9,1594729,12,8,1310721, +2,3,26,2,1632069,12,8,1310721,2,3,26,1,10,3,6,10, +4,8,1684059,14,1587605,2,1606425,12,1,6,1,10,28,34,63,38, +3,26,1684381,16,1684373,14,101,120,112,101,99,116,101,100,32,59, +32,111,114,32,125,32,97,102,116,101,114,32,97,110,32,101, +120,112,114,101,115,115,105,111,110,0,1684301,1591557,12,8,1310721,2, +5,26,3,10,3,6,10,4,8,1310721,2,7,26,1587699,2,10, +3,6,10,4,8,1684059,14,1604999,12,8,1310721,2,5,26,2,1684489, +16,1310721,2,3,26,1310721,2,3,26,2,1631353,12,10,3,6,10, +4,8,1684497,14,1,1594643,12,8,1310721,2,2,1614445,12,8,1587561,1, +6,2,1,6,-1,26,5,6,4,10,8,8,1587571,1,6,2, +1,6,-1,26,5,6,4,10,8,8,1587587,1,10,3,6,10, +4,8,1587589,1,10,3,6,10,4,8,1587699,1310721,2,7,26,2, +10,3,6,10,4,8,1587701,1,10,3,6,10,4,8,1310721,2, +3,26,2,1684653,14,1,1310721,2,9,26,1310721,4,18,1310721,2,5, +28,1310721,4,1310721,2,1835009,40,1684701,16,1966081,1310721,4,1579223,12,1585289,2, +3,28,1,6,1,10,28,34,63,38,3,26,1684769,16,1310721,2, +1585291,2,10,3,6,10,4,8,1604999,12,8,1310721,2,2,1685469,14, +1587631,2,1606729,12,1684793,16,1,1684667,12,28,1685469,14,1585289,2,9,28, +1,6,1,10,28,34,63,38,3,26,1684885,16,1585291,2,1547265,5, +26,2,28,1,6,1,10,28,34,63,38,3,26,1,6,1, +10,28,34,63,38,1,10,28,1684887,14,1,1684909,16,1604999,12,8, +1684667,12,-1,36,1685469,14,1587599,2,1606729,12,1684961,16,1310721,2,1685483,12, +10,3,6,10,4,8,1587601,2,1606959,12,8,1310721,2,2,1685469,14, +1585289,2,5,28,1,6,1,10,28,34,63,38,3,26,1685409,16, +1310721,2,3,26,1585291,2,1593303,12,10,3,6,10,4,8,1310721,2, +3,26,2,1,40,3,36,1685377,16,1417729,1310721,2,3,26,2,26, +2,9,28,1,6,1,10,28,34,63,38,3,26,1685093,16,3, +1685155,14,1417729,1310721,2,3,26,2,26,2,7,28,1,6,1,10, +28,34,63,38,3,26,1,6,1,10,28,34,63,38,1,10, +28,1685165,16,3,1685279,14,1417729,1310721,2,3,26,2,26,2,3,28, +1,6,1,10,28,34,63,38,3,26,1685255,16,1442305,1310721,2,3, +26,2,26,2,3,32,1,6,1,10,28,34,63,38,1,10, +28,1685257,14,1,1,6,1,10,28,34,63,38,1,10,28,1685289, +16,3,1685351,14,1417729,1310721,2,3,26,2,26,2,5,28,1,6, +1,10,28,34,63,38,3,26,1,6,1,10,28,34,63,38, +1,10,28,1,6,1,10,28,34,63,38,1,10,28,1685379,14, +1,1685409,16,1604999,12,8,1425921,1310721,2,3,26,2,26,2,1685469,14, +1685453,14,101,120,112,101,99,116,101,100,32,97,32,99,111,110, +115,116,97,110,116,0,1685413,1591557,12,8,1,1685469,14,1,1310721,2, +5,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,1835009,40,1685517, +16,1966081,1310721,4,1579223,12,1684667,12,1310721,2,4,1585289,2,9,28,1, +6,1,10,28,34,63,38,3,26,1686171,16,1310721,2,3,26,1585291, +2,10,3,6,10,4,8,1310721,2,3,26,2,1546753,7,26,2, +28,1,6,1,10,28,34,63,38,3,26,1685665,16,1604999,12,8, +1310721,2,1310721,2,2,1684667,12,26,10,3,6,10,4,8,1686167,14, +1310721,2,3,26,2,1546753,9,26,2,28,1,6,1,10,28,34, +63,38,3,26,1685747,16,1604999,12,8,1310721,2,1310721,2,2,1684667,12, +28,10,3,6,10,4,8,1686167,14,1310721,2,3,26,2,1546753,1, +26,2,28,1,6,1,10,28,34,63,38,3,26,1685829,16,1604999, +12,8,1310721,2,1310721,2,2,1684667,12,30,10,3,6,10,4,8, +1686167,14,1310721,2,3,26,2,1546753,31,26,2,28,1,6,1,10, +28,34,63,38,3,26,1685911,16,1604999,12,8,1310721,2,1310721,2,2, +1684667,12,34,10,3,6,10,4,8,1686167,14,1310721,2,3,26,2, +1546753,27,26,2,28,1,6,1,10,28,34,63,38,3,26,1685993, +16,1604999,12,8,1310721,2,1310721,2,2,1684667,12,32,10,3,6,10, +4,8,1686167,14,1310721,2,3,26,2,1546753,11,26,2,28,1,6, +1,10,28,34,63,38,3,26,1686075,16,1604999,12,8,1310721,2,1310721, +2,2,1684667,12,48,10,3,6,10,4,8,1686167,14,1310721,2,3, +26,2,1546753,13,26,2,28,1,6,1,10,28,34,63,38,3, +26,1686157,16,1604999,12,8,1310721,2,1310721,2,2,1684667,12,38,10,3, +6,10,4,8,1686167,14,1310721,2,2,1686183,14,1685527,14,1310721,2,2, +1686183,14,1,1310721,2,5,26,1310721,4,18,1310721,2,1,28,1310721,4, +1310721,2,1835009,40,1686231,16,1966081,1310721,4,1579223,12,1585289,2,1,28,1, +6,1,10,28,34,63,38,1,10,28,1686319,16,1587613,2,1606425,12, +1,6,1,10,28,34,63,38,3,26,1,6,1,10,28,34, +63,38,1,10,28,1686321,14,1,1686379,16,1587605,2,1606425,12,1,6, +1,10,28,34,63,38,3,26,1,6,1,10,28,34,63,38, +1,10,28,1686381,14,1,1686395,16,1604999,12,8,1686231,14,1587613,2,1606425, +12,1686413,16,1604999,12,8,1590585,1,10,3,6,10,4,8,1,1686437, +14,1,1310721,2,1,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721, +2,1835009,40,1686485,16,1966081,1310721,4,1579223,12,1590585,2,1686501,16,1686197,12, +1686973,14,1585289,2,5,28,1,6,1,10,28,34,63,38,3,26, +1686571,16,1585291,2,1611615,12,1,40,1,6,1,10,28,34,63,38, +1,10,28,1686573,14,1,1686755,16,1310721,2,1585291,2,1593303,12,10,3, +6,10,4,8,1310721,2,2,1,40,3,36,1686681,16,1417729,1310721,2, +2,26,2,11,28,1,6,1,10,28,34,63,38,3,26,1, +6,1,10,28,34,63,38,1,10,28,1686683,14,1,1686729,16,1442305, +1310721,2,2,26,2,17,32,1,6,1,10,28,34,63,38,1, +10,28,1686731,14,1,1686755,16,1310721,2,2,1,1663505,12,8,1,1686973, +14,1646981,12,1686777,16,3,1687403,12,8,1,1686973,14,1585289,2,9,28, +1,6,1,10,28,34,63,38,3,26,1686901,16,1585291,2,1587613,2, +28,1,6,1,10,28,34,63,38,3,26,1686855,16,1604999,12,8, +1,1686973,14,1585291,2,1587603,2,28,1,6,1,10,28,34,63,38, +3,26,1686901,16,1686987,12,8,1,1686973,14,1310721,2,3,26,1681059,12, +10,3,6,10,4,8,1663385,12,8,9,1594729,12,8,1587613,2,1606959, +12,8,1310721,2,3,26,2,1632069,12,8,1,1686973,14,1,1310721,2, +5,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1687021, +16,1966081,1310721,4,1579223,12,1587557,2,1310721,2,4,1587603,2,1606959,12,8, +1587561,1,6,2,1,6,3,26,5,6,4,10,8,8,1587605,2, +1606425,12,1,6,1,10,28,34,63,38,3,26,1687175,16,1585289,2, +1,28,1,6,1,10,28,34,63,38,3,26,1687165,16,1687157,14, +109,105,115,115,105,110,103,32,125,0,1687137,1591557,12,8,1686451,12, +8,1687069,14,1310721,2,2,1614445,12,8,1604999,12,8,1587561,1,6,2, +1,6,-1,26,5,6,4,10,8,8,1,1687229,14,1,1310721,2, +3,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1687277, +16,1966081,1310721,4,1579223,12,1587557,2,1310721,2,4,1587603,2,1606425,12,1687307, +16,1686987,12,1687389,14,1587561,1,6,2,1,6,3,26,5,6,4, +10,8,8,1686451,12,8,1310721,2,2,1614445,12,8,1587561,1,6,2, +1,6,-1,26,5,6,4,10,8,8,1,1687389,14,1,1310721,2, +3,26,1310721,4,18,1310721,2,15,28,1310721,4,1310721,2,1835009,40,1687437, +16,1966081,1310721,4,1579223,12,1310721,2,4,1,1310721,2,11,26,4,1, +1310721,2,13,26,4,1310721,2,2,1687633,16,1647783,12,8,1310721,2,11, +26,1587725,2,10,3,6,10,4,8,1310721,2,13,26,1587727,2,10, +3,6,10,4,8,1310721,2,11,26,2,1587695,2,28,1,6,1, +10,28,34,63,38,3,26,1687633,16,1687625,14,97,32,118,97,114, +105,97,98,108,101,32,99,97,110,39,116,32,98,101,32,118, +111,105,100,0,1687575,1591173,12,8,1587769,2,1687647,16,3,1687673,14,1587773, +2,1,6,1,10,28,34,63,38,1,10,28,1687729,16,1310721,2, +2,1,6,1,10,28,34,63,38,3,26,1,6,1,10,28, +34,63,38,1,10,28,1687731,14,1,1687797,16,1687789,14,111,51,32, +118,97,114,105,97,98,108,101,115,32,110,101,101,100,32,97, +32,116,121,112,101,0,1687739,1591173,12,8,3,1688939,16,1310721,2,3, +26,1607249,12,10,3,6,10,4,8,1310721,2,5,26,1310721,2,3, +26,2,1611819,12,10,3,6,10,4,8,1558017,1310721,2,5,26,2, +26,1310721,2,13,26,2,10,3,6,10,4,8,1587769,2,1687911,16, +3,1687937,14,1587773,2,1,6,1,10,28,34,63,38,1,10,28, +1687981,16,1558017,1310721,2,5,26,2,26,1310721,2,13,26,2,33,34, +10,3,6,10,4,8,1587607,2,1606729,12,1688207,16,1310721,2,9,26, +1685483,12,10,3,6,10,4,8,1587609,2,1606959,12,8,1310721,2,11, +26,2,1,6,1,10,28,34,63,38,3,26,1688085,16,1310721,2, +11,26,1587687,2,10,3,6,10,4,8,1473025,1310721,2,5,26,2, +26,3,10,3,6,10,4,8,1556993,1310721,2,5,26,2,26,1310721, +2,11,26,2,1310721,2,9,26,2,1633251,12,10,3,6,10,4, +8,1587563,1587563,2,1556993,1310721,2,5,26,2,26,2,1637519,12,26,10, +3,6,10,4,8,1688487,14,1310721,2,11,26,2,1688261,16,1310721,2, +11,26,2,1638921,12,1,6,1,10,28,34,63,38,1,10,28, +1688263,14,1,1688369,16,1473025,1310721,2,5,26,2,26,3,10,3,6, +10,4,8,1556993,1310721,2,5,26,2,26,1310721,2,11,26,2,10, +3,6,10,4,8,1587563,1587563,2,1310721,2,11,26,2,1637519,12,26, +10,3,6,10,4,8,1688487,14,1587563,1,6,2,1,6,3,26, +5,6,4,10,8,8,1310721,2,11,26,2,1688447,16,1556993,1310721,2, +5,26,2,26,1310721,2,11,26,2,10,3,6,10,4,8,1587769, +2,1688487,16,1310721,2,5,26,2,1596201,12,8,1310721,2,5,26,2, +1596641,12,8,1587615,2,1606729,12,1688899,16,1473025,1310721,2,5,26,2,26, +2,1688629,16,1688621,14,97,110,32,97,114,114,97,121,32,111,114, +32,97,32,115,116,114,117,99,116,32,99,97,110,39,116,32, +98,101,32,105,110,105,116,105,97,108,105,122,101,100,32,119, +105,116,104,32,61,0,1688523,1591173,12,8,1310721,2,7,26,1681059,12, +10,3,6,10,4,8,1663385,12,8,1310721,2,5,26,2,1595671,12, +8,5,1594729,12,8,1310721,2,5,26,2,1597105,12,8,1310721,2,11, +26,2,1688809,16,1310721,2,11,26,2,1587699,2,1639563,12,1,6,1, +10,28,34,63,38,3,26,1688805,16,1688783,14,105,110,105,116,105, +97,108,105,122,101,114,0,1688759,1310721,2,11,26,2,1587699,2,1643225, +12,8,1688871,14,1587699,2,1638689,12,1,6,1,10,28,34,63,38, +3,26,1688871,16,1556993,1310721,2,5,26,2,26,1587699,2,10,3,6, +10,4,8,3,1310721,2,5,26,2,1310721,2,7,26,2,1626785,12, +8,1587611,2,1606729,12,1,6,1,10,28,34,63,38,3,26,1688935, +16,1688939,14,1687797,14,1587613,2,1606959,12,8,1,1688957,14,1,1310721,2, +15,26,1310721,4,18,1310721,2,1,28,1310721,4,1310721,2,1835009,40,1689005, +16,1966081,1310721,4,1579223,12,1663797,12,8,1604999,12,8,1,1687403,12,1689029, +14,1,1310721,2,1,26,1310721,4,18,1310721,2,21,28,1310721,4,1310721, +2,1835009,40,1689077,16,1966081,1310721,4,1579223,12,1587557,2,1310721,2,15,26, +4,1663797,12,8,1604999,12,8,1587599,2,1606959,12,8,1310721,2,5,26, +1681059,12,10,3,6,10,4,8,1663385,12,8,1310721,2,5,26,2, +1613033,12,8,1587601,2,1606959,12,8,1310721,2,1595029,12,10,3,6,10, +4,8,17,1594729,12,8,1310721,2,11,26,1587569,2,10,3,6,10, +4,8,1310721,2,7,26,1615017,12,10,3,6,10,4,8,1687243,12, +8,1310721,2,13,26,1587569,2,10,3,6,10,4,8,1310721,2,9, +26,1615017,12,10,3,6,10,4,8,1587645,2,1606577,12,1689565,16,1604999, +12,8,1310721,2,3,26,1595029,12,10,3,6,10,4,8,15,1594729, +12,8,1310721,2,2,1587549,2,1594811,12,8,1310721,2,7,26,2,1615649, +12,8,1587569,1310721,2,11,26,2,10,3,6,10,4,8,1687243,12, +8,1310721,2,9,26,2,1310721,2,13,26,2,1310721,2,3,26,2, +1587569,2,1310721,2,15,26,2,1689553,14,101,110,100,115,32,116,104, +101,32,98,114,97,110,99,104,101,115,32,111,102,32,97,110, +32,105,102,32,105,110,32,100,105,102,102,101,114,101,110,116, +32,115,116,97,116,101,115,0,1689459,1623371,12,8,1690097,14,1618015,1, +10,3,6,10,4,8,1310721,2,13,26,2,1689615,16,1507841,1310721,2, +9,26,2,26,1618983,12,8,1310721,2,11,26,2,1689649,16,1507841,1310721, +2,7,26,2,26,1618983,12,8,1310721,2,15,26,2,1689757,14,101, +110,100,115,32,116,104,101,32,98,114,97,110,99,104,101,115, +32,111,102,32,97,110,32,105,102,32,105,110,32,100,105,102, +102,101,114,101,110,116,32,115,116,97,116,101,115,0,1689663,1619711, +12,8,1310721,2,13,26,2,1689811,16,1507841,1310721,2,9,26,2,26, +1310721,2,15,26,2,3,1621295,12,8,1310721,2,17,26,1,10,3, +6,10,4,8,1310721,2,11,26,2,1689897,16,1310721,2,17,26,1507841, +1310721,2,7,26,2,26,1310721,2,15,26,2,1,1621295,12,10,3, +6,10,4,8,1310721,2,17,26,2,1690015,16,1310721,2,19,26,1595029, +12,10,3,6,10,4,8,15,1594729,12,8,1310721,2,2,1587549,2, +1594811,12,8,1507841,1310721,2,7,26,2,26,1310721,2,15,26,2,3, +1621295,12,8,1310721,2,19,26,2,1587549,2,1594811,12,8,1690031,14,1310721, +2,2,1587549,2,1594811,12,8,1587569,1310721,2,13,26,2,1690053,16,3, +1690085,14,1310721,2,11,26,2,1,6,1,10,28,34,63,38,1, +10,28,10,3,6,10,4,8,1310721,2,7,26,2,1615567,12,8, +1,1690121,14,1,1310721,2,21,26,1310721,4,18,1310721,2,7,28,1310721, +4,1310721,2,1835009,40,1690169,16,1966081,1310721,4,1579223,12,1310721,2,3,26, +4,1310721,2,4,1552897,1587579,2,17,30,26,1310721,2,5,26,4,1587579, +2,121,40,3,36,1690283,16,1690275,14,108,111,111,112,115,32,110, +101,115,116,101,100,32,116,111,111,32,100,101,101,112,108,121, +0,1690227,1591079,12,8,1310721,2,5,26,2,1,26,1310721,2,2,10, +3,6,10,4,8,1310721,2,5,26,2,3,26,1,10,3,6, +10,4,8,1310721,2,5,26,2,5,26,1587561,2,10,3,6,10, +4,8,1310721,2,5,26,2,7,26,1310721,2,3,26,2,10,3, +6,10,4,8,1310721,2,5,26,2,9,26,1587571,2,10,3,6, +10,4,8,1310721,2,5,26,2,11,26,1587557,2,10,3,6,10, +4,8,1310721,2,5,26,2,13,26,1617885,2,10,3,6,10,4, +8,1587579,1,6,2,1,6,3,26,5,6,4,10,8,8,1, +1690535,14,1,1310721,2,7,26,1310721,4,18,1310721,2,15,28,1310721,4, +1310721,2,1835009,40,1690583,16,1966081,1310721,4,1579223,12,1310721,2,3,26,4, +1310721,2,4,1,1310721,2,13,26,4,1587579,1,6,2,1,6,-1, +26,5,6,4,10,8,8,1310721,2,5,26,1552897,1587579,2,17,30, +26,10,3,6,10,4,8,1310721,2,11,26,1310721,2,5,26,2, +11,26,2,10,3,6,10,4,8,1618015,1,10,3,6,10,4, +8,1310721,2,3,26,2,1,40,3,36,1690765,16,1507841,1310721,2,3, +26,2,26,1618983,12,8,1310721,2,7,26,1567233,1310721,2,5,26,2, +13,26,2,26,10,3,6,10,4,8,1310721,2,9,26,1567233,1617885, +2,26,10,3,6,10,4,8,1310721,2,7,26,2,1310721,2,9, +26,2,40,1690957,16,1310721,2,7,26,2,3,26,1618983,12,8,1310721, +2,7,26,1310721,2,7,26,2,7,26,7,1310721,2,7,26,2, +5,26,2,1310721,2,7,26,2,3,26,2,28,30,26,10,3, +6,10,4,8,1690833,14,1310721,2,11,26,2,1691081,14,108,101,97, +118,101,115,32,116,104,101,32,108,111,111,112,32,105,110,32, +100,105,102,102,101,114,101,110,116,32,115,116,97,116,101,115, +32,111,110,32,100,105,102,102,101,114,101,110,116,32,112,97, +116,104,115,0,1690971,1619711,12,8,1587569,1618015,2,1,10,40,10,3, +6,10,4,8,1310721,2,7,26,1567233,1310721,2,5,26,2,13,26, +2,26,10,3,6,10,4,8,1310721,2,7,26,2,1310721,2,9, +26,2,40,1691449,16,1310721,2,7,26,2,3,26,1310721,2,11,26, +2,1,1621295,12,1691321,16,1310721,2,7,26,2,1,26,2,1587549,2, +1594811,12,8,1310721,2,7,26,2,3,26,1310721,2,11,26,2,3, +1621295,12,8,1310721,2,13,26,1595029,12,1310721,2,13,26,2,1593879,12, +10,3,6,10,4,8,15,1594729,12,8,1691371,14,1310721,2,13,26, +1310721,2,7,26,2,1,26,2,1310721,2,13,26,2,1593879,12,10, +3,6,10,4,8,1310721,2,7,26,1310721,2,7,26,2,7,26, +7,1310721,2,7,26,2,5,26,2,1310721,2,7,26,2,3,26, +2,28,30,26,10,3,6,10,4,8,1691153,14,1310721,2,2,1, +40,3,36,1691483,16,1310721,2,2,1587549,2,1594811,12,8,1310721,2,3, +26,2,1,40,3,36,1691537,16,1507841,1310721,2,3,26,2,26,1310721, +2,11,26,2,3,1621295,12,8,1310721,2,13,26,2,1587549,2,1594231, +12,8,1617885,1310721,2,5,26,2,13,26,2,10,3,6,10,4, +8,1,1691595,14,1,1310721,2,15,26,1310721,4,18,1310721,2,13,28, +1310721,4,1310721,2,1835009,40,1691643,16,1966081,1310721,4,1579223,12,1587549,2,1310721, +2,4,3,1,10,28,1310721,2,7,26,4,1,1310721,2,11,26, +4,1663797,12,8,1604999,12,8,1310721,2,5,26,1615017,12,10,3,6, +10,4,8,1587599,2,1606959,12,8,1585289,2,3,28,1,6,1,10, +28,34,63,38,3,26,1691791,16,1585291,2,1,6,1,10,28,34, +63,38,1,10,28,1691793,14,1,1691919,16,1605859,12,8,1587511,2,9, +28,1,6,1,10,28,34,63,38,3,26,1691891,16,1587513,2,1587601, +2,28,1,6,1,10,28,34,63,38,3,26,1,6,1,10, +28,34,63,38,1,10,28,1691893,14,1,1691919,16,1310721,2,11,26, +3,10,3,6,10,4,8,1310721,2,9,26,1681059,12,10,3,6, +10,4,8,1663385,12,8,1310721,2,9,26,2,1613033,12,8,1587601,2, +1606959,12,8,1310721,2,3,26,1595029,12,10,3,6,10,4,8,17, +1594729,12,8,1310721,2,11,26,2,1,6,1,10,28,34,63,38, +3,26,1692071,16,1587569,2,1,6,1,10,28,34,63,38,1,10, +28,1692073,14,1,1692101,16,1310721,2,7,26,1615017,12,10,3,6,10, +4,8,1310721,2,2,1310721,2,5,26,2,1690135,12,8,1687243,12,8, +1310721,2,5,26,2,1621789,12,8,1310721,2,2,1594947,12,8,1310721,2, +3,26,2,1310721,2,7,26,2,1690549,12,8,1310721,2,5,26,2, +1615567,12,8,1,1692207,14,1,1310721,2,13,26,1310721,4,18,1310721,2, +19,28,1310721,4,1310721,2,1835009,40,1692255,16,1966081,1310721,4,1579223,12,1587557, +2,1310721,2,4,3,1,10,28,1310721,2,5,26,4,1,1310721,2, +17,26,4,1663797,12,8,1604999,12,8,1587599,2,1606959,12,8,1587561,1, +6,2,1,6,3,26,5,6,4,10,8,8,1587641,2,1606577,12, +1692367,16,1688971,12,8,1692483,14,1646981,12,1692387,16,3,1687403,12,8,1692483, +14,1587613,2,1606425,12,1,6,1,10,28,34,63,38,3,26,1692473, +16,1310721,2,15,26,1681059,12,10,3,6,10,4,8,1663385,12,8, +9,1594729,12,8,1310721,2,15,26,2,1632069,12,8,1587613,2,1606959,12, +8,1310721,2,3,26,1615017,12,10,3,6,10,4,8,1310721,2,7, +26,1587549,2,10,3,6,10,4,8,1587613,2,1606425,12,1,6,1, +10,28,34,63,38,3,26,1692803,16,1585289,2,3,28,1,6,1, +10,28,34,63,38,3,26,1692625,16,1585291,2,1,6,1,10,28, +34,63,38,1,10,28,1692627,14,1,1692753,16,1605859,12,8,1587511,2, +9,28,1,6,1,10,28,34,63,38,3,26,1692725,16,1587513,2, +1587613,2,28,1,6,1,10,28,34,63,38,3,26,1,6,1, +10,28,34,63,38,1,10,28,1692727,14,1,1692753,16,1310721,2,17, +26,3,10,3,6,10,4,8,1310721,2,15,26,1681059,12,10,3, +6,10,4,8,1663385,12,8,1310721,2,15,26,2,1613033,12,8,1692833, +14,3,1594643,12,8,1310721,2,17,26,3,10,3,6,10,4,8, +1587613,2,1606959,12,8,1310721,2,9,26,1595029,12,10,3,6,10,4, +8,17,1594729,12,8,1310721,2,17,26,2,1,6,1,10,28,34, +63,38,3,26,1692939,16,1587569,2,1,6,1,10,28,34,63,38, +1,10,28,1692941,14,1,1692969,16,1310721,2,5,26,1615017,12,10,3, +6,10,4,8,1310721,2,11,26,1595029,12,10,3,6,10,4,8, +15,1594729,12,8,1310721,2,13,26,1587549,2,10,3,6,10,4,8, +1587601,2,1606425,12,1,6,1,10,28,34,63,38,3,26,1693111,16, +1310721,2,15,26,1681059,12,10,3,6,10,4,8,1663385,12,8,9, +1594729,12,8,1310721,2,15,26,2,1632069,12,8,1587601,2,1606959,12,8, +1310721,2,7,26,2,1594947,12,8,1310721,2,11,26,2,1587549,2,1594811, +12,8,1310721,2,13,26,2,1310721,2,3,26,2,1690135,12,8,1687243, +12,8,1310721,2,3,26,2,1621789,12,8,1310721,2,13,26,2,1594947, +12,8,1310721,2,9,26,2,1310721,2,5,26,2,1690549,12,8,1310721, +2,3,26,2,1615567,12,8,1310721,2,2,1614445,12,8,1587561,1,6, +2,1,6,-1,26,5,6,4,10,8,8,1,1693311,14,1,1310721, +2,19,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40, +1693359,16,1966081,1310721,4,1579223,12,1663797,12,8,1604999,12,8,1587613,2,1606959, +12,8,1587579,2,1587581,2,10,40,3,36,1693485,16,1693471,14,98,114, +101,97,107,32,111,117,116,115,105,100,101,32,97,32,108,111, +111,112,32,111,114,32,97,32,115,119,105,116,99,104,0,1693405, +1591173,12,8,1,1693737,14,1310721,2,1552897,1587579,2,3,28,17,30,26, +10,3,6,10,4,8,1310721,2,2,9,26,2,1587571,2,28,1, +6,1,10,28,34,63,38,1,10,28,1693653,16,1693639,14,98,114, +101,97,107,32,99,97,110,39,116,32,108,101,97,118,101,32, +97,32,98,108,111,99,107,32,101,120,112,114,101,115,115,105, +111,110,0,1693565,1591173,12,8,1,1693737,14,1310721,2,2,5,26,2, +3,26,1,1613419,12,8,1587569,2,1693699,16,1595029,12,1618391,12,8,1693705, +14,1595029,12,8,15,1594729,12,8,1587569,1,10,3,6,10,4,8, +1,1693737,14,1,1310721,2,3,26,1310721,4,18,1310721,2,5,28,1310721, +4,1310721,2,1835009,40,1693785,16,1966081,1310721,4,1579223,12,1587579,2,3,28, +1310721,2,4,1663797,12,8,1604999,12,8,1587613,2,1606959,12,8,1310721,2, +2,1587581,2,40,3,36,1693913,16,1552897,1310721,2,2,17,30,26,2, +3,1,10,28,28,1,6,1,10,28,34,63,38,3,26,1, +6,1,10,28,34,63,38,1,10,28,1693915,14,1,1693953,16,1310721, +2,1,6,2,1,6,-1,26,5,6,4,10,8,8,1693821,14, +1310721,2,2,1587581,2,40,1694035,16,1694021,14,99,111,110,116,105,110, +117,101,32,111,117,116,115,105,100,101,32,97,32,108,111,111, +112,0,1693973,1591173,12,8,1,1694311,14,1310721,2,3,26,1552897,1310721,2, +2,17,30,26,10,3,6,10,4,8,1310721,2,3,26,2,9, +26,2,1587571,2,28,1,6,1,10,28,34,63,38,1,10,28, +1694215,16,1694201,14,99,111,110,116,105,110,117,101,32,99,97,110, +39,116,32,108,101,97,118,101,32,97,32,98,108,111,99,107, +32,101,120,112,114,101,115,115,105,111,110,0,1694121,1591173,12,8, +1,1694311,14,1310721,2,3,26,2,7,26,2,1621789,12,8,1310721,2, +3,26,2,5,26,2,3,26,1,1613419,12,8,1310721,2,3,26, +2,1,26,2,1594947,12,8,1587569,1,10,3,6,10,4,8,1, +1694311,14,1,1310721,2,5,26,1310721,4,18,1310721,2,7,28,1310721,4, +1310721,2,1835009,40,1694359,16,1966081,1310721,4,1579223,12,1310721,2,3,26,4, +1310721,2,4,1587557,2,1310721,2,5,26,4,1310721,2,2,1615649,12,8, +1587569,1310721,2,3,26,2,10,3,6,10,4,8,1587561,1,6,2, +1,6,3,26,5,6,4,10,8,8,1587605,2,1606425,12,1,6, +1,10,28,34,63,38,3,26,1694539,16,1587661,2,1606577,12,1,6, +1,10,28,34,63,38,3,26,1,6,1,10,28,34,63,38, +1,10,28,1694541,14,1,1694599,16,1587663,2,1606577,12,1,6,1,10, +28,34,63,38,3,26,1,6,1,10,28,34,63,38,1,10, +28,1694601,14,1,1694683,16,1585289,2,1,28,1,6,1,10,28,34, +63,38,3,26,1694673,16,1694661,14,109,105,115,115,105,110,103,32, +125,0,1694641,1591557,12,8,1694683,14,1686451,12,8,1694453,14,1310721,2,5, +26,2,1614445,12,8,1587561,1,6,2,1,6,-1,26,5,6,4, +10,8,8,1587569,2,1694753,16,1595029,12,1618391,12,8,15,1594729,12,8, +1587569,1,10,3,6,10,4,8,1,1694777,14,1,1310721,2,7,26, +1310721,4,18,1310721,2,23,28,1310721,4,1310721,2,1835009,40,1694825,16,1966081, +1310721,4,1579223,12,1587557,2,1310721,2,4,3,1,10,28,1310721,2,11, +26,4,3,1,10,28,1310721,2,21,26,4,1663797,12,8,1604999,12, +8,1587599,2,1606959,12,8,1310721,2,5,26,1681059,12,10,3,6,10, +4,8,1663385,12,8,1310721,2,5,26,2,1613033,12,8,1587601,2,1606959, +12,8,1587561,1,6,2,1,6,3,26,5,6,4,10,8,8, +1310721,2,3,26,1,1611819,12,10,3,6,10,4,8,1587563,1,6, +2,1,6,3,26,5,6,4,10,8,8,1467905,1310721,2,3,26, +2,26,2,1595563,12,8,5,1594729,12,8,1310721,2,9,26,1587569,2, +10,3,6,10,4,8,1310721,2,7,26,1615017,12,10,3,6,10, +4,8,3,1,10,28,1310721,2,7,26,2,1690135,12,8,1587603,2, +1606959,12,8,1587605,2,1606425,12,1,6,1,10,28,34,63,38,3, +26,1695849,16,1585289,2,1,28,1,6,1,10,28,34,63,38,3, +26,1695243,16,1695231,14,109,105,115,115,105,110,103,32,125,0,1695211, +1591557,12,8,1695849,14,1587661,2,1606577,12,1695565,16,1604999,12,8,1310721,2, +17,26,1,10,3,6,10,4,8,3,1695453,16,1310721,2,19,26, +1685483,12,10,3,6,10,4,8,1467905,1310721,2,3,26,2,26,2, +1595563,12,8,3,1594729,12,8,1310721,2,19,26,2,1594643,12,8,29, +1594729,12,8,1310721,2,17,26,1595029,12,1310721,2,17,26,2,1593879,12, +10,3,6,10,4,8,17,1594729,12,8,1587611,2,1606729,12,1,6, +1,10,28,34,63,38,3,26,1695449,16,1695453,14,1695283,14,1587627,2, +1606959,12,8,1310721,2,15,26,1595029,12,10,3,6,10,4,8,15, +1594729,12,8,1310721,2,17,26,2,1587549,2,1594231,12,8,1310721,2,7, +26,2,1310721,2,9,26,2,1694325,12,8,1310721,2,15,26,2,1587549, +2,1594811,12,8,1695845,14,1587663,2,1606577,12,1695783,16,1604999,12,8,1587627, +2,1606959,12,8,1310721,2,11,26,2,1,40,3,36,1695677,16,1695669, +14,97,32,115,119,105,116,99,104,32,104,97,115,32,111,110, +101,32,100,101,102,97,117,108,116,0,1695619,1591173,12,8,1310721,2, +13,26,1595029,12,10,3,6,10,4,8,15,1594729,12,8,1310721,2, +11,26,1587549,2,10,3,6,10,4,8,1310721,2,7,26,2,1310721, +2,9,26,2,1694325,12,8,1310721,2,13,26,2,1587549,2,1594811,12, +8,1695845,14,1695837,14,101,120,112,101,99,116,101,100,32,99,97, +115,101,32,111,114,32,100,101,102,97,117,108,116,0,1695787,1591557, +12,8,1695143,14,1604999,12,8,1310721,2,7,26,2,1615649,12,8,1587569, +1310721,2,9,26,2,10,3,6,10,4,8,1310721,2,11,26,2, +1,40,3,36,1695959,16,1310721,2,11,26,2,1594947,12,8,3,1, +10,28,3,1,10,28,1690549,12,8,1696017,14,1310721,2,21,26,1595029, +12,10,3,6,10,4,8,15,1594729,12,8,1310721,2,21,26,2, +1310721,2,7,26,2,1690549,12,8,1310721,2,7,26,2,1615567,12,8, +1310721,2,2,1614445,12,8,1587561,1,6,2,1,6,-1,26,5,6, +4,10,8,8,1,1696081,14,1,1310721,2,23,26,1310721,4,18,1310721, +2,3,28,1310721,4,1310721,2,1835009,40,1696129,16,1966081,1310721,4,1579223,12, +1663797,12,8,1604999,12,8,1587577,2,1,6,1,10,28,34,63,38, +3,26,1696233,16,1696225,14,114,101,116,117,114,110,32,111,117,116, +115,105,100,101,32,97,32,102,117,110,99,116,105,111,110,0, +1696173,1591173,12,8,1587571,2,1696329,16,1696321,14,114,101,116,117,114,110, +32,99,97,110,39,116,32,108,101,97,118,101,32,97,32,98, +108,111,99,107,32,101,120,112,114,101,115,115,105,111,110,0, +1696245,1591173,12,8,1587613,2,1606425,12,1696371,16,1,1594643,12,8,1310721,2, +1,10,3,6,10,4,8,1696703,14,1310721,2,1681059,12,10,3,6, +10,4,8,1663385,12,8,1587597,2,1587695,2,28,1,6,1,10,28, +34,63,38,3,26,1696481,16,1696461,14,114,101,116,117,114,110,32, +118,97,108,117,101,0,1696435,1587695,2,1587699,2,1643225,12,8,1696599,14, +1587597,2,1696547,16,1587597,2,1587699,2,1639563,12,1,6,1,10,28,34, +63,38,3,26,1,6,1,10,28,34,63,38,1,10,28,1696549, +14,1,1696599,16,1696583,14,114,101,116,117,114,110,32,118,97,108, +117,101,0,1696557,1587597,2,1587699,2,1643225,12,8,1587769,2,1696637,16,1587597, +2,1,6,1,10,28,34,63,38,1,10,28,1696639,14,1,1696677, +16,1587597,2,1658673,12,1,6,1,10,28,34,63,38,1,10,28, +1696679,14,1,1696703,16,1587597,2,1594643,12,8,1590517,2,1595409,12,8,1587613, +2,1606959,12,8,1310721,2,2,1630339,12,8,3,1,1613419,12,8,1587573, +1587549,2,1587573,2,1593879,12,10,3,6,10,4,8,2097153,3,28,1594643, +12,8,15,1594729,12,8,1587569,1,10,3,6,10,4,8,1,1696805, +14,1,1310721,2,3,26,1310721,4,18,1310721,2,11,28,1310721,4,1310721, +2,1835009,40,1696853,16,1966081,1310721,4,1579223,12,1663797,12,8,1604999,12,8, +1587603,2,1606425,12,1,6,1,10,28,34,63,38,3,26,1696943,16, +1587773,3,10,3,6,10,4,8,3,1687403,12,8,1587773,1,10,3, +6,10,4,8,1,1697879,14,1587769,2,1696957,16,3,1697003,14,1587577,2, +1,6,1,10,28,34,63,38,3,26,1,6,1,10,28,34, +63,38,1,10,28,1697015,16,1686987,12,1697879,14,1310721,2,1587549,2,5, +26,10,3,6,10,4,8,1310721,2,2,1594643,12,8,15,1594729,12, +8,1587769,3,10,3,6,10,4,8,1595939,12,8,1310721,2,7,26, +1587557,2,10,3,6,10,4,8,1310721,2,9,26,1587557,2,10,3, +6,10,4,8,1310721,2,3,26,1587559,2,10,3,6,10,4,8, +1310721,2,3,26,2,1310721,2,9,26,2,40,1697289,16,1558017,1310721,2, +3,26,2,26,2,33,32,1697251,16,1310721,2,3,26,2,1596201,12, +8,1310721,2,3,26,2,1596641,12,8,1310721,2,3,26,2,1597105,12, +8,1310721,2,3,26,1,6,2,1,6,3,26,5,6,4,10, +8,8,1697153,14,1686987,12,8,1587769,1,10,3,6,10,4,8,1589305, +2,21,40,1697527,16,1310721,2,5,26,1587905,1589305,2,135,30,26,10, +3,6,10,4,8,1310721,2,5,26,2,1,26,1310721,2,2,10, +3,6,10,4,8,1310721,2,5,26,2,3,26,1587549,2,10,3, +6,10,4,8,1310721,2,5,26,2,5,26,1587903,2,10,3,6, +10,4,8,1310721,2,5,26,2,7,26,1587775,1587903,2,1574491,12,8, +1310721,2,5,26,2,71,26,1587839,1587903,2,1574491,12,8,1589305,1,6, +2,1,6,3,26,5,6,4,10,8,8,1587557,1310721,2,7,26, +2,10,3,6,10,4,8,1310721,2,3,26,1587559,2,10,3,6, +10,4,8,1310721,2,3,26,2,1310721,2,9,26,2,40,1697871,16, +1558017,1310721,2,3,26,2,26,1558017,1310721,2,3,26,2,26,2,1558017, +1310721,2,3,26,2,26,2,3585,32,28,10,3,6,10,4,8, +1589487,1310721,2,3,26,2,26,1,10,3,6,10,4,8,1558017,1310721, +2,3,26,2,26,2,33,32,1697763,16,1556993,1310721,2,3,26,2, +26,2,1658673,12,1,6,1,10,28,34,63,38,1,10,28,1697765, +14,1,1697833,16,1310721,2,3,26,2,1595671,12,8,3,1594729,12,8, +1556993,1310721,2,3,26,2,26,2,1594643,12,8,1590517,2,1595409,12,8, +9,1594729,12,8,1310721,2,3,26,1,6,2,1,6,3,26,5, +6,4,10,8,8,1697575,14,1,1697879,14,1,1310721,2,11,26,1310721, +4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1697927,16,1966081,1310721, +4,1579223,12,1587565,2,1310721,2,4,1663797,12,8,1604999,12,8,1587565,1, +10,3,6,10,4,8,1687243,12,8,1587565,1310721,2,2,10,3,6, +10,4,8,1,1697999,14,1,1310721,2,3,26,1310721,4,18,1310721,2, +21,28,1310721,4,1310721,2,1835009,40,1698047,16,1966081,1310721,4,1579223,12,1, +1310721,2,4,1,1310721,2,3,26,4,1554945,1310721,2,13,26,4,1554945, +129,26,1310721,2,15,26,4,1554945,257,26,1310721,2,17,26,4,1554945, +769,26,1310721,2,19,26,4,1587603,2,1606959,12,8,1587605,2,1606425,12, +1,6,1,10,28,34,63,38,3,26,1700323,16,1585289,2,1,28, +1,6,1,10,28,34,63,38,3,26,1698259,16,1698251,14,117,110, +116,101,114,109,105,110,97,116,101,100,32,97,115,109,32,98, +108,111,99,107,0,1698205,1591557,12,8,1585289,2,3,28,1,6,1, +10,28,34,63,38,3,26,1698311,16,1585291,2,1594643,12,8,1604999,12, +8,1698137,14,1585289,2,7,28,1,6,1,10,28,34,63,38,3, +26,1698363,16,1585291,2,1598105,12,8,1604999,12,8,1698137,14,1587629,2,1606425, +12,1698481,16,1604999,12,8,1585289,2,3,28,1,6,1,10,28,34, +63,38,1,10,28,1698461,16,1698453,14,35,32,110,101,101,100,115, +32,97,32,110,117,109,98,101,114,0,1698419,1591557,12,8,1585291,2, +1594449,12,8,1604999,12,8,1698137,14,1587631,2,1606425,12,1698603,16,1604999,12, +8,1585289,2,3,28,1,6,1,10,28,34,63,38,3,26,1698561, +16,1585293,2,1,6,1,10,28,34,63,38,1,10,28,1698563,14, +1,1698591,16,1,1585291,2,28,1594643,12,8,1604999,12,8,1698599,14,29, +1594729,12,8,1698137,14,1587627,2,1606425,12,1698793,16,1604999,12,8,1310721,2, +2,129,40,3,36,1698691,16,1698683,14,116,111,111,32,109,97,110, +121,32,97,115,109,32,108,97,98,101,108,115,0,1698643,1591079,12, +8,1310721,2,13,26,2,1310721,2,2,26,1607249,12,10,3,6,10, +4,8,1310721,2,15,26,2,1310721,2,2,26,1587549,2,10,3,6, +10,4,8,1310721,2,1,6,2,1,6,3,26,5,6,4,10, +8,8,1698137,14,1585291,2,1,26,2,1698863,16,1585291,2,3,26,2, +1,6,1,10,28,34,63,38,3,26,1,6,1,10,28,34, +63,38,1,10,28,1698865,14,1,1698935,16,1310721,2,9,26,1585291,2, +1,26,2,1608541,12,10,3,6,10,4,1,40,3,36,1,6, +1,10,28,34,63,38,1,10,28,1698937,14,1,1698967,16,1310721,2, +9,26,2,1594729,12,8,1604999,12,8,1698137,14,1585289,2,5,28,1, +6,1,10,28,34,63,38,1,10,28,1699047,16,1699039,14,98,97, +100,32,116,111,107,101,110,32,105,110,32,97,115,109,0,1699005, +1591557,12,8,1310721,2,5,26,1585291,2,1611615,12,10,3,6,10,4, +8,1310721,2,5,26,2,1,40,3,36,1699143,16,1558017,1310721,2,5, +26,2,26,2,33,32,1,6,1,10,28,34,63,38,1,10, +28,1699145,14,1,1699211,16,1473025,1310721,2,5,26,2,26,2,1,6, +1,10,28,34,63,38,3,26,1,6,1,10,28,34,63,38, +1,10,28,1699213,14,1,1699291,16,1699283,14,97,115,109,32,99,97, +110,39,116,32,114,101,97,99,104,32,97,110,32,111,51,32, +118,97,114,105,97,98,108,101,0,1699221,1591173,12,8,1310721,2,5, +26,2,1,40,3,36,1699373,16,1472001,1310721,2,5,26,2,26,3, +10,3,6,10,4,8,1467905,1310721,2,5,26,2,26,2,1595563,12, +8,1604999,12,8,1698137,14,1310721,2,7,26,3,1,10,28,10,3, +6,10,4,8,1310721,2,5,26,1,10,3,6,10,4,8,1310721, +2,5,26,2,1310721,2,2,40,1699567,16,1310721,2,13,26,2,1310721, +2,5,26,2,26,2,1585291,2,28,1,6,1,10,28,34,63, +38,3,26,1699529,16,1310721,2,7,26,1310721,2,5,26,2,10,3, +6,10,4,8,1310721,2,5,26,1,6,2,1,6,3,26,5, +6,4,10,8,8,1699423,14,1310721,2,7,26,2,1,40,3,36, +1699629,16,1310721,2,15,26,2,1310721,2,7,26,2,26,2,1594643,12, +8,1604999,12,8,1698137,14,1310721,2,11,26,1585291,2,1593303,12,10,3, +6,10,4,8,1310721,2,11,26,2,1,40,3,36,1699745,16,1417729, +1310721,2,11,26,2,26,2,13,28,1,6,1,10,28,34,63, +38,3,26,1,6,1,10,28,34,63,38,1,10,28,1699747,14, +1,1699787,16,45,1425921,1310721,2,11,26,2,26,2,26,1594729,12,8, +1604999,12,8,1698137,14,1310721,2,11,26,2,1,40,3,36,1699877,16, +1417729,1310721,2,11,26,2,26,2,3,28,1,6,1,10,28,34, +63,38,1,10,28,1,6,1,10,28,34,63,38,1,10,28, +1699879,14,1,1699951,16,1417729,1310721,2,11,26,2,26,2,11,28,1, +6,1,10,28,34,63,38,1,10,28,1,6,1,10,28,34, +63,38,1,10,28,1699953,14,1,1699989,16,1425921,1310721,2,11,26,2, +26,2,1594643,12,8,1604999,12,8,1698137,14,1310721,2,11,26,2,1, +40,3,36,1700077,16,1417729,1310721,2,11,26,2,26,2,3,28,1, +6,1,10,28,34,63,38,3,26,1,6,1,10,28,34,63, +38,1,10,28,1700079,14,1,1700109,16,1310721,2,11,26,2,1595235,12, +8,1604999,12,8,1698137,14,1310721,2,3,26,2,501,40,3,36,1700203, +16,1700195,14,116,111,111,32,109,97,110,121,32,97,115,109,32, +108,97,98,101,108,32,114,101,102,101,114,101,110,99,101,115, +0,1700135,1591079,12,8,1310721,2,17,26,2,1310721,2,3,26,2,26, +1585291,2,10,3,6,10,4,8,1310721,2,19,26,2,1310721,2,3, +26,2,26,1595029,12,10,3,6,10,4,8,1310721,2,3,26,1, +6,2,1,6,3,26,5,6,4,10,8,8,1604999,12,8,1698137, +14,1604999,12,8,1310721,2,5,26,1,10,3,6,10,4,8,1310721, +2,5,26,2,1310721,2,3,26,2,40,1700777,16,1310721,2,7,26, +3,1,10,28,10,3,6,10,4,8,1310721,2,9,26,1,10, +3,6,10,4,8,1310721,2,9,26,2,1310721,2,2,40,1700591,16, +1310721,2,13,26,2,1310721,2,9,26,2,26,2,1310721,2,17,26, +2,1310721,2,5,26,2,26,2,28,1,6,1,10,28,34,63, +38,3,26,1700553,16,1310721,2,7,26,1310721,2,9,26,2,10,3, +6,10,4,8,1310721,2,9,26,1,6,2,1,6,3,26,5, +6,4,10,8,8,1700427,14,1310721,2,7,26,2,1,40,1700685,16, +1310721,2,17,26,2,1310721,2,5,26,2,26,2,1700677,14,117,110, +107,110,111,119,110,32,110,97,109,101,32,105,110,32,97,115, +109,0,1700637,1591343,12,8,1310721,2,19,26,2,1310721,2,5,26,2, +26,2,1310721,2,15,26,2,1310721,2,7,26,2,26,2,1594811,12, +8,1310721,2,5,26,1,6,2,1,6,3,26,5,6,4,10, +8,8,1700351,14,1,1700785,14,1,1310721,2,21,26,1310721,4,18,1310721, +2,1,28,1310721,4,1310721,2,1835009,40,1700833,16,1966081,1310721,4,1579223,12, +1604999,12,8,1698013,12,8,1587595,1,10,3,6,10,4,8,1,1700869, +14,1,1310721,2,1,26,1310721,4,18,1310721,2,7,28,1310721,4,1310721, +2,1835009,40,1700917,16,1966081,1310721,4,1579223,12,1,1310721,2,4,1,1310721, +2,3,26,4,1587603,2,1606425,12,1,6,1,10,28,34,63,38, +3,26,1701003,16,1700995,14,101,120,112,101,99,116,101,100,32,123, +0,1700973,1591557,12,8,1604999,12,8,3,1701425,16,1585289,2,1,28,1, +6,1,10,28,34,63,38,3,26,1701097,16,1701089,14,117,110,116, +101,114,109,105,110,97,116,101,100,32,113,117,111,116,101,0, +1701051,1591557,12,8,1587603,2,1606425,12,1701143,16,1310721,2,3,26,1,6, +2,1,6,3,26,5,6,4,10,8,8,1587605,2,1606425,12,1701227, +16,1310721,2,3,26,2,1,6,1,10,28,34,63,38,3,26, +1701193,16,1701425,14,1310721,2,3,26,1,6,2,1,6,-1,26,5, +6,4,10,8,8,1310721,2,2,5401,40,3,36,1701287,16,1701279,14, +113,117,111,116,101,32,116,111,111,32,108,111,110,103,0,1701249, +1591079,12,8,1524225,1310721,2,2,7,30,26,1585289,2,10,3,6,10, +4,8,1524225,1310721,2,2,7,30,3,26,26,1585291,2,10,3,6, +10,4,8,1524225,1310721,2,2,7,30,5,26,26,1585295,2,10,3, +6,10,4,8,1310721,2,1,6,2,1,6,3,26,5,6,4, +10,8,8,1604999,12,8,1701009,14,1604999,12,8,1310721,2,5,26,1310721, +2,2,7,30,3,26,1580219,12,10,3,6,10,4,8,1310721,2, +5,26,2,1,26,1310721,2,2,10,3,6,10,4,8,1310721,2, +5,26,2,3,26,1524225,1310721,2,2,7,30,1574491,12,8,1310721,2, +5,26,2,1701549,14,1,1310721,2,7,26,1310721,4,18,1310721,2,7, +28,1310721,4,1310721,2,1835009,40,1701597,16,1966081,1310721,4,1579223,12,1310721,2, +4,1310721,2,2,1,26,2,7,30,3,26,1310721,2,3,26,4, +1310721,2,3,26,2,1580219,12,1310721,2,5,26,4,1310721,2,5,26, +2,1310721,2,2,1310721,2,3,26,2,1574491,12,8,1310721,2,5,26, +2,1701705,14,1,1310721,2,7,26,1310721,4,18,1310721,2,7,28,1310721, +4,1310721,2,1835009,40,1701753,16,1966081,1310721,4,1579223,12,1310721,2,4,1310721, +2,2,1,26,2,7,30,3,26,1310721,2,3,26,4,1310721,2, +3,26,2,1580219,12,1310721,2,5,26,4,1310721,2,5,26,2,1310721, +2,2,1310721,2,3,26,2,1574491,12,8,1310721,2,5,26,2,1701861, +14,1,1310721,2,7,26,1310721,4,18,1310721,2,7,28,1310721,4,1310721, +2,1835009,40,1701909,16,1966081,1310721,4,1579223,12,1310721,2,3,26,4,1310721, +2,4,1,1310721,2,5,26,4,1310721,2,5,26,2,1310721,2,3, +26,2,40,1702027,16,1310721,2,2,1310721,2,5,26,2,26,2,1594449, +12,8,1310721,2,5,26,1,6,2,1,6,3,26,5,6,4, +10,8,8,1701937,14,1,1702035,14,1,1310721,2,7,26,1310721,4,18, +1310721,2,9,28,1310721,4,1310721,2,1835009,40,1702083,16,1966081,1310721,4,1579223, +12,1604999,12,8,1310721,2,1700883,12,10,3,6,10,4,8,1310721,2, +3,26,1595029,12,10,3,6,10,4,8,15,1594729,12,8,1310721,2, +5,26,1587549,2,10,3,6,10,4,8,1310721,2,7,26,1310721,2, +2,1,26,2,7,30,3,26,10,3,6,10,4,8,1310721,2, +2,1310721,2,7,26,2,1701875,12,8,1310721,2,2,1580973,12,8,1310721, +2,3,26,2,1587549,2,1594811,12,8,1310721,2,5,26,2,1594643,12, +8,1587675,2,1595409,12,8,1587595,3,10,3,6,10,4,8,1,1702309, +14,1,1310721,2,9,26,1310721,4,18,1310721,2,7,28,1310721,4,1310721, +2,1835009,40,1702357,16,1966081,1310721,4,1579223,12,1310721,2,4,1,1310721,2, +3,26,4,5,1310721,2,2,1,1,1598871,12,8,1604999,12,8,1585289, +2,1,28,1,6,1,10,28,34,63,38,1,10,28,1702653,16, +1310721,2,3,26,2,5401,40,3,36,1702499,16,1702491,14,116,111,111, +32,109,97,110,121,32,116,111,107,101,110,115,0,1702459,1591079,12, +8,1524225,1310721,2,3,26,2,7,30,26,1585289,2,10,3,6,10, +4,8,1524225,1310721,2,3,26,2,7,30,3,26,26,1585291,2,10, +3,6,10,4,8,1524225,1310721,2,3,26,2,7,30,5,26,26, +1585295,2,10,3,6,10,4,8,1310721,2,3,26,1,6,2,1, +6,3,26,5,6,4,10,8,8,1604999,12,8,1702399,14,1600299,12, +8,1310721,2,5,26,1310721,2,3,26,2,7,30,3,26,1580219,12, +10,3,6,10,4,8,1310721,2,5,26,2,1,26,1310721,2,3, +26,2,10,3,6,10,4,8,1310721,2,5,26,2,3,26,1524225, +1310721,2,3,26,2,7,30,1574491,12,8,1310721,2,5,26,2,1702789, +14,1,1310721,2,7,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721, +2,1835009,40,1702837,16,1966081,1310721,4,1579223,12,1310721,2,4,3,1310721,2, +2,3,3,1598871,12,8,1604999,12,8,1,1702875,14,1,1310721,2,3, +26,1310721,4,18,1310721,2,13,28,1310721,4,1310721,2,1835009,40,1702923,16, +1966081,1310721,4,1579223,12,8181,1310721,2,4,8181,1580219,12,1310721,2,3,26, +4,1,1310721,2,5,26,4,1573381,12,1,6,1,10,28,34,63, +38,3,26,1703481,16,1310721,2,7,26,1573325,12,511,32,10,3,6, +10,4,8,1310721,2,7,26,2,193,28,1,6,1,10,28,34, +63,38,3,26,1703057,16,1703481,14,1310721,2,5,26,2,5,38,5, +26,1310721,2,2,40,3,36,1703245,16,1310721,2,9,26,1310721,2,2, +1310721,2,2,26,13,26,1580219,12,10,3,6,10,4,8,1310721,2, +9,26,2,1310721,2,3,26,2,1310721,2,2,1574491,12,8,1310721,2, +3,26,2,1580973,12,8,1310721,2,3,26,1310721,2,9,26,2,10, +3,6,10,4,8,1310721,2,1310721,2,2,1310721,2,2,26,13,26, +10,3,6,10,4,8,1310721,2,11,26,1310721,2,5,26,2,5, +38,10,3,6,10,4,8,1310721,2,5,26,2,7,32,1,6, +1,10,28,34,63,38,3,26,1703353,16,1310721,2,3,26,2,1310721, +2,11,26,2,26,1,10,3,6,10,4,8,1310721,2,3,26, +2,1310721,2,11,26,2,26,1310721,2,3,26,2,1310721,2,11,26, +2,26,2,1310721,2,7,26,2,1310721,2,5,26,2,7,32,17, +30,48,34,10,3,6,10,4,8,1310721,2,5,26,1,6,2, +1,6,3,26,5,6,4,10,8,8,1702959,14,1310721,2,3,26, +2,1310721,2,5,26,2,5,38,26,1310721,2,3,26,2,1310721,2, +5,26,2,5,38,26,2,1574901,1310721,2,5,26,2,7,32,17, +30,26,2,3,28,32,10,3,6,10,4,8,1310721,2,5,26, +2,7,32,1,28,1,6,1,10,28,34,63,38,3,26,1703659, +16,1310721,2,3,26,2,1310721,2,5,26,2,5,38,26,1,10, +3,6,10,4,8,1310721,2,3,26,2,1310721,2,5,26,2,5, +38,3,26,26,1,10,3,6,10,4,8,1310721,2,3,26,2, +1703719,14,1,1310721,2,13,26,1310721,4,18,1310721,2,3,28,1310721,4, +1310721,2,1835009,40,1703767,16,1966081,1310721,4,1579223,12,1,1310721,2,4,1585289, +2,1,28,1,6,1,10,28,34,63,38,1,10,28,1703975,16, +1587729,2,1606425,12,1703855,16,1310721,2,1,6,2,1,6,3,26,5, +6,4,10,8,8,1703939,14,1587731,2,1606425,12,1703901,16,1310721,2,1, +6,2,1,6,-1,26,5,6,4,10,8,8,1703939,14,1587733,2, +1606425,12,1703939,16,1310721,2,1310721,2,2,5,28,10,3,6,10,4, +8,1604999,12,8,1310721,2,2,1,10,40,3,36,1703971,16,1,1703983, +14,1703775,14,1,1703983,14,1,1310721,2,3,26,1310721,4,18,1310721,2, +17,28,1310721,4,1310721,2,1835009,40,1704031,16,1966081,1310721,4,1579223,12,1310721, +2,5,26,4,1310721,2,3,26,4,1310721,2,4,1,1310721,2,7, +26,4,1,1310721,2,9,26,4,1,1310721,2,11,26,4,1604999,12, +8,1587601,2,1606425,12,1,6,1,10,28,34,63,38,3,26,1704843, +16,1310721,2,7,26,3,10,3,6,10,4,8,1310721,2,13,26, +3,10,3,6,10,4,8,1585289,2,1,28,1,6,1,10,28, +34,63,38,1,10,28,1704749,16,1587599,2,1606425,12,1704259,16,1310721,2, +13,26,1,6,2,1,6,3,26,5,6,4,10,8,8,1704739, +14,1587601,2,1606425,12,1704347,16,1310721,2,13,26,1,6,2,1,6, +-1,26,5,6,4,10,8,8,1310721,2,13,26,2,1,6,1, +10,28,34,63,38,3,26,1704343,16,1704749,14,1704739,14,1310721,2,13, +26,2,3,28,1,6,1,10,28,34,63,38,3,26,1704419,16, +1587611,2,1606425,12,1,6,1,10,28,34,63,38,1,10,28,1704421, +14,1,1704579,16,1310721,2,7,26,2,25,10,40,3,36,1704519,16, +1310721,2,9,26,1310721,2,9,26,2,1310721,2,11,26,2,1310721,2, +7,26,2,1310721,2,7,26,2,26,5,28,48,26,10,3,6, +10,4,8,1310721,2,7,26,1,6,2,1,6,3,26,5,6, +4,10,8,8,1310721,2,11,26,1,10,3,6,10,4,8,1704739, +14,1310721,2,13,26,2,3,28,1,6,1,10,28,34,63,38, +3,26,1704651,16,1587637,2,1606425,12,1,6,1,10,28,34,63,38, +1,10,28,1704653,14,1,1704739,16,1604999,12,8,1587653,2,1606577,12,1704701, +16,1310721,2,11,26,3,10,3,6,10,4,8,1704735,14,1587655,2, +1606577,12,1704735,16,1310721,2,11,26,5,10,3,6,10,4,8,1704175, +14,1604999,12,8,1704175,14,1310721,2,7,26,2,25,10,40,3,36, +1704843,16,1310721,2,9,26,1310721,2,9,26,2,1310721,2,11,26,2, +1310721,2,7,26,2,1310721,2,7,26,2,26,5,28,48,26,10, +3,6,10,4,8,1604999,12,8,1587603,2,1606425,12,1,6,1,10, +28,34,63,38,3,26,1704935,16,1587613,2,1606425,12,1,6,1,10, +28,34,63,38,3,26,1,6,1,10,28,34,63,38,1,10, +28,1704937,14,1,1704947,16,1,1705441,14,1310721,2,15,26,1310721,2,3, +26,2,1593303,12,10,3,6,10,4,8,1310721,2,15,26,2,1, +40,1705005,16,3,1705043,14,1310721,2,15,26,2,1585221,2,40,1,6, +1,10,28,34,63,38,1,10,28,1705053,16,3,1705117,14,1417729,1310721, +2,15,26,2,26,2,3,28,1,6,1,10,28,34,63,38, +1,10,28,1,6,1,10,28,34,63,38,1,10,28,1705127,16, +3,1705169,14,1442305,1310721,2,15,26,2,26,2,3,32,1,6,1, +10,28,34,63,38,1,10,28,1705207,16,1310721,2,15,26,1310721,2, +3,26,2,1595111,12,10,3,6,10,4,8,1442305,1310721,2,15,26, +2,26,2,5,32,1,6,1,10,28,34,63,38,3,26,1705373, +16,1434113,1310721,2,15,26,2,26,1310721,2,2,1310721,2,7,26,2, +26,1310721,2,7,26,2,26,1310721,2,9,26,2,129,30,26,10, +3,6,10,4,8,1442305,1310721,2,15,26,2,26,1442305,1310721,2,15, +26,2,26,2,5,34,513,34,10,3,6,10,4,8,1310721,2, +5,26,2,1705433,16,1442305,1310721,2,15,26,2,26,1442305,1310721,2,15, +26,2,26,2,1025,34,10,3,6,10,4,8,1,1705441,14,1, +1310721,2,17,26,1310721,4,18,1310721,2,19,28,1310721,4,1310721,2,1835009, +40,1705489,16,1966081,1310721,4,1579223,12,1310721,2,3,26,4,1310721,2,4, +1,1310721,2,5,26,4,1590585,2,1310721,2,7,26,4,1590583,2,1310721, +2,9,26,4,1,1310721,2,11,26,4,1,1310721,2,13,26,4, +1587703,1,6,2,1,6,3,26,5,6,4,10,8,8,1310721,2, +2,1310721,2,3,26,2,1,1,1598871,12,8,1604999,12,8,1585289,2, +1,28,1,6,1,10,28,34,63,38,1,10,28,1706783,16,1587603, +2,1606425,12,1705713,16,1310721,2,5,26,1,6,2,1,6,3,26, +5,6,4,10,8,8,1706773,14,1587605,2,1606425,12,1705807,16,1310721,2, +5,26,1,6,2,1,6,-1,26,5,6,4,10,8,8,1310721, +2,11,26,1,10,3,6,10,4,8,1310721,2,13,26,1,10, +3,6,10,4,8,1706773,14,1310721,2,5,26,2,1,10,40,3, +36,1706773,16,1587613,2,1606425,12,1705891,16,1310721,2,11,26,1,10,3, +6,10,4,8,1310721,2,13,26,1,10,3,6,10,4,8,1706773, +14,1587637,2,1606425,12,1706029,16,1605859,12,8,1587511,2,5,28,1,6, +1,10,28,34,63,38,3,26,1705997,16,1587513,2,1587657,2,28,1, +6,1,10,28,34,63,38,3,26,1,6,1,10,28,34,63, +38,1,10,28,1705999,14,1,1706025,16,1310721,2,11,26,3,10,3, +6,10,4,8,1706773,14,1587759,2,1606577,12,1706067,16,1310721,2,13,26, +3,10,3,6,10,4,8,1706773,14,1585289,2,5,28,1,6,1, +10,28,34,63,38,3,26,1706773,16,1605859,12,8,1587511,2,9,28, +1,6,1,10,28,34,63,38,3,26,1706193,16,1587513,2,1587599,2, +28,1,6,1,10,28,34,63,38,3,26,1,6,1,10,28, +34,63,38,1,10,28,1706195,14,1,1706313,16,1310721,2,15,26,1585291, +2,10,3,6,10,4,8,1604999,12,8,1310721,2,11,26,2,1310721, +2,15,26,2,1310721,2,13,26,2,1703997,12,8,1310721,2,11,26, +1,10,3,6,10,4,8,1310721,2,13,26,1,10,3,6,10, +4,8,1705629,14,1587511,2,9,28,1,6,1,10,28,34,63,38, +3,26,1706401,16,1587513,2,1587729,2,28,1,6,1,10,28,34,63, +38,3,26,1,6,1,10,28,34,63,38,1,10,28,1706403,14, +1,1706773,16,1310721,2,15,26,1585291,2,10,3,6,10,4,8,1604999, +12,8,1310721,2,17,26,1,10,3,6,10,4,8,1585289,2,1, +28,1,6,1,10,28,34,63,38,1,10,28,1706677,16,1587729,2, +1606425,12,1706543,16,1310721,2,17,26,1,6,2,1,6,3,26,5, +6,4,10,8,8,1706639,14,1587731,2,1606425,12,1706593,16,1310721,2,17, +26,1,6,2,1,6,-1,26,5,6,4,10,8,8,1706639,14, +1587733,2,1606425,12,1706639,16,1310721,2,17,26,1310721,2,17,26,2,5, +28,10,3,6,10,4,8,1604999,12,8,1310721,2,17,26,2,1, +10,40,3,36,1706673,16,1706677,14,1706459,14,1587599,2,1606425,12,1706769,16, +1310721,2,11,26,2,1310721,2,15,26,2,1310721,2,13,26,2,1703997, +12,8,1310721,2,11,26,1,10,3,6,10,4,8,1310721,2,13, +26,1,10,3,6,10,4,8,1705629,14,1604999,12,8,1705629,14,1600299, +12,8,1587703,1,6,2,1,6,-1,26,5,6,4,10,8,8, +1590585,1310721,2,7,26,2,10,3,6,10,4,8,1590583,1310721,2,9, +26,2,10,3,6,10,4,8,1,1706873,14,1,1310721,2,19,26, +1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1706921,16,1966081, +1310721,4,1579223,12,1702889,12,1310721,2,4,7,1310721,2,2,1705455,12,8, +7,1310721,2,2,1,1,1598871,12,8,1604999,12,8,1722469,12,8,1585221, +2,1722569,12,8,1600299,12,8,1310721,2,2,1580973,12,8,1,1707011,14, +1,1310721,2,3,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2, +1835009,40,1707059,16,1966081,1310721,4,1579223,12,1548801,1587583,2,49,30,26,1310721, +2,4,1587583,2,61,40,3,36,1707159,16,1707151,14,99,111,109,112, +105,108,101,115,32,110,101,115,116,101,100,32,116,111,111,32, +100,101,101,112,108,121,0,1707097,1591079,12,8,1587583,1,6,2,1, +6,3,26,5,6,4,10,8,8,1310721,2,2,1,26,1587557,2, +10,3,6,10,4,8,1310721,2,2,3,26,1587559,2,10,3,6, +10,4,8,1310721,2,2,5,26,1587561,2,10,3,6,10,4,8, +1310721,2,2,7,26,1587563,2,10,3,6,10,4,8,1310721,2,2, +9,26,1587565,2,10,3,6,10,4,8,1310721,2,2,11,26,1587567, +2,10,3,6,10,4,8,1310721,2,2,13,26,1587569,2,10,3, +6,10,4,8,1310721,2,2,15,26,1587571,2,10,3,6,10,4, +8,1310721,2,2,17,26,1587573,2,10,3,6,10,4,8,1310721,2, +2,19,26,1587575,2,10,3,6,10,4,8,1310721,2,2,21,26, +1587577,2,10,3,6,10,4,8,1310721,2,2,23,26,1587579,2,10, +3,6,10,4,8,1310721,2,2,25,26,1587581,2,10,3,6,10, +4,8,1310721,2,2,27,26,1587587,2,10,3,6,10,4,8,1310721, +2,2,29,26,1587589,2,10,3,6,10,4,8,1310721,2,2,31, +26,1587591,2,10,3,6,10,4,8,1310721,2,2,33,26,1587593,2, +10,3,6,10,4,8,1310721,2,2,35,26,1587595,2,10,3,6, +10,4,8,1310721,2,2,37,26,1587585,2,10,3,6,10,4,8, +1310721,2,2,39,26,1587597,2,10,3,6,10,4,8,1310721,2,2, +41,26,1587721,2,10,3,6,10,4,8,1310721,2,2,43,26,1587699, +2,10,3,6,10,4,8,1310721,2,2,45,26,1587705,5,1574491,12, +8,1,1707787,14,1,1310721,2,3,26,1310721,4,18,1310721,2,3,28, +1310721,4,1310721,2,1835009,40,1707835,16,1966081,1310721,4,1579223,12,1587583,1,6, +2,1,6,-1,26,5,6,4,10,8,8,1310721,2,1548801,1587583,2, +49,30,26,10,3,6,10,4,8,1587557,1310721,2,2,1,26,2, +10,3,6,10,4,8,1587559,1310721,2,2,3,26,2,10,3,6, +10,4,8,1587561,1310721,2,2,5,26,2,10,3,6,10,4,8, +1587563,1310721,2,2,7,26,2,10,3,6,10,4,8,1587565,1310721,2, +2,9,26,2,10,3,6,10,4,8,1587567,1310721,2,2,11,26, +2,10,3,6,10,4,8,1587569,1310721,2,2,13,26,2,10,3, +6,10,4,8,1587571,1310721,2,2,15,26,2,10,3,6,10,4, +8,1587573,1310721,2,2,17,26,2,10,3,6,10,4,8,1587575,1310721, +2,2,19,26,2,10,3,6,10,4,8,1587577,1310721,2,2,21, +26,2,10,3,6,10,4,8,1587579,1310721,2,2,23,26,2,10, +3,6,10,4,8,1587581,1310721,2,2,25,26,2,10,3,6,10, +4,8,1587587,1310721,2,2,27,26,2,10,3,6,10,4,8,1587589, +1310721,2,2,29,26,2,10,3,6,10,4,8,1587591,1310721,2,2, +31,26,2,10,3,6,10,4,8,1587593,1310721,2,2,33,26,2, +10,3,6,10,4,8,1587595,1310721,2,2,35,26,2,10,3,6, +10,4,8,1587585,1310721,2,2,37,26,2,10,3,6,10,4,8, +1587597,1310721,2,2,39,26,2,10,3,6,10,4,8,1587721,1310721,2, +2,41,26,2,10,3,6,10,4,8,1587699,1310721,2,2,43,26, +2,10,3,6,10,4,8,1587705,1310721,2,2,45,26,5,1574491,12, +8,1,1708491,14,1,1310721,2,3,26,1310721,4,18,1310721,2,5,28, +1310721,4,1310721,2,1835009,40,1708539,16,1966081,1310721,4,1579223,12,1310721,2,3, +26,4,1310721,2,4,1587559,1587557,2,10,3,6,10,4,8,1587561,3, +10,3,6,10,4,8,1587563,1,10,3,6,10,4,8,1587565,1310721, +2,2,1708647,16,1585287,2,1,6,1,10,28,34,63,38,1,10, +28,1708649,14,1,10,3,6,10,4,8,1587567,1310721,2,3,26,2, +10,3,6,10,4,8,1587597,1,10,3,6,10,4,8,1587569,3, +10,3,6,10,4,8,1587571,1,10,3,6,10,4,8,1587573,1, +10,3,6,10,4,8,1587577,3,10,3,6,10,4,8,1587581,1587579, +2,10,3,6,10,4,8,1587587,1,10,3,6,10,4,8,1587589, +1,10,3,6,10,4,8,1,1708823,14,1,1310721,2,5,26,1310721, +4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1708871,16,1966081,1310721, +4,1579223,12,1310721,1594643,12,8,3,1594729,12,8,1587575,1595029,12,10,3, +6,10,4,8,29,1594729,12,8,1310721,1594643,12,8,5,1594729,12,8, +1587769,2,1708943,16,1,1709061,14,1310721,1594643,12,8,3,1594729,12,8,1835009, +1594643,12,8,41,1594729,12,8,1310721,2,1595029,12,10,3,6,10,4, +8,17,1594729,12,8,1966081,1594643,12,8,1310721,1594643,12,8,5,1594729,12, +8,1587665,2,1595409,12,8,1310721,2,2,1587549,2,1594811,12,8,1,1709061, +14,1,1310721,2,3,26,1310721,4,18,1310721,2,1,28,1310721,4,1310721, +2,1835009,40,1709109,16,1966081,1310721,4,1579223,12,1,1594643,12,8,1587573,2, +1587549,2,1594231,12,8,1310721,1594643,12,8,3,1594729,12,8,1587563,2,1594643, +12,8,27,1594729,12,8,1310721,1594643,12,8,5,1594729,12,8,19,1594729, +12,8,1587575,2,1587563,2,1594811,12,8,1587577,1,10,3,6,10,4, +8,1587557,1587559,2,10,3,6,10,4,8,1,1709245,14,1,1310721,2, +1,26,1310721,4,18,1310721,2,1,28,1310721,4,1310721,2,1835009,40,1709293, +16,1966081,1310721,4,1579223,12,1587603,2,1606959,12,8,1587605,2,1606425,12,1, +6,1,10,28,34,63,38,3,26,1709459,16,1585289,2,1,28,1, +6,1,10,28,34,63,38,3,26,1709449,16,1709441,14,109,105,115, +115,105,110,103,32,125,32,97,116,32,116,104,101,32,101,110, +100,32,111,102,32,97,32,102,117,110,99,116,105,111,110,0, +1709371,1591557,12,8,1686451,12,8,1709303,14,1587559,2,1614147,12,8,1587565,2, +1709507,16,1587569,2,1,6,1,10,28,34,63,38,1,10,28,1709509, +14,1,1709641,16,3,3,1613419,12,8,1587567,2,1709641,16,1709633,14,97, +32,102,117,110,99,116,105,111,110,32,114,101,116,117,114,110, +105,110,103,32,46,117,32,104,97,115,32,116,111,32,101,110, +100,32,119,105,116,104,32,97,32,114,101,116,117,114,110,0, +1709535,1591845,12,8,1604999,12,8,1,1709655,14,1,1310721,2,1,26,1310721, +4,18,1310721,2,175,28,1310721,4,1310721,2,1835009,40,1709703,16,1966081,1310721, +4,1579223,12,1310721,2,7,26,4,1310721,2,5,26,4,1310721,2,3, +26,4,1310721,2,4,1,1310721,2,11,26,4,1,1310721,2,13,26, +4,1,1310721,2,165,26,4,1587771,2,1310721,2,169,26,4,1587771,1, +10,3,6,10,4,8,1310721,2,3,26,2,1709825,16,3,1709851,14, +1587721,2,1,6,1,10,28,34,63,38,1,10,28,1709877,16,1310721, +2,165,26,3,10,3,6,10,4,8,1310721,2,9,26,1310721,2, +7,26,2,1593303,12,10,3,6,10,4,8,1310721,2,9,26,2, +1,40,1709935,16,3,1709973,14,1310721,2,9,26,2,1585221,2,40,1, +6,1,10,28,34,63,38,1,10,28,1709983,16,3,1710047,14,1417729, +1310721,2,9,26,2,26,2,3,28,1,6,1,10,28,34,63, +38,1,10,28,1,6,1,10,28,34,63,38,1,10,28,1710057, +16,3,1710099,14,1442305,1310721,2,9,26,2,26,2,3,32,1,6, +1,10,28,34,63,38,1,10,28,1710169,16,1310721,2,9,26,1310721, +2,7,26,2,3,1,1592805,12,10,3,6,10,4,8,1442305,1310721, +2,9,26,2,26,1,10,3,6,10,4,8,1587599,2,1606959,12, +8,1587601,2,1606425,12,1,6,1,10,28,34,63,38,3,26,1711671, +16,3,1711671,16,1310721,2,15,26,1,10,3,6,10,4,8,1310721, +2,117,26,1310721,2,11,26,2,26,1587687,2,10,3,6,10,4, +8,1587637,2,1606425,12,1,6,1,10,28,34,63,38,3,26,1710647, +16,1585289,2,9,28,1,6,1,10,28,34,63,38,3,26,1710457, +16,1585291,2,1587623,2,28,1,6,1,10,28,34,63,38,3,26, +1710379,16,3,1710431,14,1585291,2,1587607,2,28,1,6,1,10,28,34, +63,38,3,26,1,6,1,10,28,34,63,38,1,10,28,1, +6,1,10,28,34,63,38,1,10,28,1710459,14,1,1710469,16,3, +1710621,14,1585289,2,5,28,1,6,1,10,28,34,63,38,3,26, +1710597,16,1585291,2,1587639,2,28,1,6,1,10,28,34,63,38,3, +26,1710541,16,3,1710571,14,1585291,2,1644211,12,1,6,1,10,28,34, +63,38,1,10,28,1,6,1,10,28,34,63,38,1,10,28, +1710599,14,1,1,6,1,10,28,34,63,38,1,10,28,1,6, +1,10,28,34,63,38,1,10,28,1710649,14,1,1710835,16,1310721,2, +117,26,1310721,2,11,26,2,26,1644591,12,10,3,6,10,4,8, +1310721,2,165,26,3,10,3,6,10,4,8,1310721,2,117,26,1310721, +2,11,26,2,26,2,1638921,12,1710835,16,1710827,14,115,116,114,117, +99,116,115,32,97,110,100,32,97,114,114,97,121,115,32,97, +114,101,32,112,97,115,115,101,100,32,98,121,32,112,111,105, +110,116,101,114,0,1710745,1591173,12,8,1587637,2,1606729,12,1711041,16,1585291, +2,1587653,2,28,1,6,1,10,28,34,63,38,3,26,1710907,16, +1310721,2,15,26,3,10,3,6,10,4,8,1711035,14,1585291,2,1587655, +2,28,1,6,1,10,28,34,63,38,3,26,1710967,16,1310721,2, +15,26,5,10,3,6,10,4,8,1711035,14,1711027,14,97,32,112, +97,114,97,109,101,116,101,114,32,116,97,103,32,105,115,32, +46,109,32,111,114,32,46,100,0,1710971,1591557,12,8,1604999,12,8, +1310721,2,21,26,1310721,2,11,26,2,26,1607249,12,10,3,6,10, +4,8,1310721,2,169,26,2,1711165,16,1310721,2,117,26,1310721,2,11, +26,2,26,2,1587687,2,28,1,6,1,10,28,34,63,38,3, +26,1,6,1,10,28,34,63,38,1,10,28,1711167,14,1,1711235, +16,1711227,14,111,51,32,112,97,114,97,109,101,116,101,114,115, +32,110,101,101,100,32,97,32,116,121,112,101,0,1711175,1591173,12, +8,1310721,2,11,26,2,25,40,3,36,1711293,16,1310721,2,15,26, +2,1,6,1,10,28,34,63,38,1,10,28,1711295,14,1,1711397, +16,1711389,14,111,110,108,121,32,116,104,101,32,102,105,114,115, +116,32,49,50,32,112,97,114,97,109,101,116,101,114,115,32, +99,97,110,32,98,101,32,116,97,103,103,101,100,0,1711303,1591557, +12,8,1310721,2,69,26,1310721,2,11,26,2,26,1310721,2,15,26, +2,10,3,6,10,4,8,1310721,2,11,26,2,25,40,1711523,16, +1310721,2,13,26,1310721,2,13,26,2,1310721,2,15,26,2,1310721,2, +11,26,2,1310721,2,11,26,2,26,48,26,10,3,6,10,4, +8,1310721,2,11,26,1,6,2,1,6,3,26,5,6,4,10, +8,8,1310721,2,11,26,2,49,40,3,36,1711631,16,1711623,14,116, +111,111,32,109,97,110,121,32,112,97,114,97,109,101,116,101, +114,115,0,1711583,1591557,12,8,1587611,2,1606729,12,1,6,1,10,28, +34,63,38,3,26,1711667,16,1711671,14,1710211,14,1587601,2,1606959,12,8, +1310721,2,17,26,1310721,2,5,26,2,1310721,2,11,26,2,26,1310721, +2,11,26,2,26,1310721,2,13,26,2,129,30,26,10,3,6, +10,4,8,1442305,1310721,2,9,26,2,26,2,5,32,1711841,16,1442305, +1310721,2,9,26,2,26,2,513,32,1,6,1,10,28,34,63, +38,3,26,1,6,1,10,28,34,63,38,1,10,28,1711843,14, +1,1712003,16,1434113,1310721,2,9,26,2,26,2,1310721,2,17,26,2, +28,1,6,1,10,28,34,63,38,1,10,28,1711999,16,1310721,2, +7,26,2,1711991,14,100,111,101,115,110,39,116,32,109,97,116, +99,104,32,105,116,115,32,101,97,114,108,105,101,114,32,100, +101,99,108,97,114,97,116,105,111,110,0,1711915,1591343,12,8,1712291, +14,1442305,1310721,2,9,26,2,26,2,9,32,1712105,16,1310721,2,13, +26,2,1712047,16,3,1712079,14,1310721,2,5,26,2,1,6,1,10, +28,34,63,38,1,10,28,1,6,1,10,28,34,63,38,1, +10,28,1712107,14,1,1712291,16,1310721,2,7,26,2,1712283,14,119,97, +115,32,99,97,108,108,101,100,32,98,101,102,111,114,101,32, +105,116,115,32,116,97,103,115,32,119,101,114,101,32,100,101, +99,108,97,114,101,100,59,32,112,117,116,32,97,32,112,114, +111,116,111,116,121,112,101,32,97,98,111,118,101,32,116,104, +101,32,102,105,114,115,116,32,99,97,108,108,0,1712125,1591343,12, +8,1434113,1310721,2,9,26,2,26,1310721,2,17,26,2,10,3,6, +10,4,8,1442305,1310721,2,9,26,2,26,1442305,1310721,2,9,26,2, +26,2,5,34,1442305,1310721,2,9,26,2,26,2,513,32,28,10, +3,6,10,4,8,1587721,2,1712449,16,1442305,1310721,2,9,26,2,26, +1442305,1310721,2,9,26,2,26,2,257,34,10,3,6,10,4,8, +1310721,2,165,26,2,1712819,16,1310721,2,3,26,2,1,6,1,10, +28,34,63,38,3,26,1712521,16,1310721,2,3,26,1587687,2,10,3, +6,10,4,8,1310721,2,167,26,1310721,2,3,26,2,1310721,2,11, +26,2,1310721,2,117,26,1310721,2,17,26,2,1633771,12,10,3,6, +10,4,8,1559041,1310721,2,9,26,2,26,2,1712679,16,1559041,1310721,2, +9,26,2,26,2,1310721,2,167,26,2,28,1,6,1,10,28, +34,63,38,1,10,28,1,6,1,10,28,34,63,38,1,10, +28,1712681,14,1,1712783,16,1310721,2,7,26,2,1712775,14,100,111,101, +115,110,39,116,32,109,97,116,99,104,32,105,116,115,32,101, +97,114,108,105,101,114,32,100,101,99,108,97,114,97,116,105, +111,110,0,1712699,1591343,12,8,1559041,1310721,2,9,26,2,26,1310721,2, +167,26,2,10,3,6,10,4,8,1587613,2,1606729,12,1712861,16,1587721, +1,10,3,6,10,4,8,1310721,2,9,26,2,1714221,14,1442305,1310721, +2,9,26,2,26,2,3,32,1712941,16,1310721,2,7,26,2,1712933, +14,105,115,32,100,101,102,105,110,101,100,32,116,119,105,99, +101,0,1712899,1591343,12,8,1425921,1310721,2,9,26,2,26,1587549,2,10, +3,6,10,4,8,1442305,1310721,2,9,26,2,26,1442305,1310721,2,9, +26,2,26,2,3,34,10,3,6,10,4,8,1450497,1310721,2,9, +26,2,26,2,1587549,2,1594231,12,8,1450497,1310721,2,9,26,2,26, +1,10,3,6,10,4,8,1310721,2,2,1310721,2,5,26,2,1708505, +12,8,1310721,2,165,26,2,1713131,16,1587597,1310721,2,3,26,2,10, +3,6,10,4,8,1589305,1,10,3,6,10,4,8,1310721,2,169, +26,2,1713229,16,1587769,3,10,3,6,10,4,8,1595939,12,8,1442305, +1310721,2,9,26,2,26,1442305,1310721,2,9,26,2,26,2,1025,34, +10,3,6,10,4,8,1708837,12,8,1310721,2,19,26,1,10,3, +6,10,4,8,1310721,2,19,26,2,1310721,2,11,26,2,40,1713757, +16,1310721,2,15,26,1310721,2,21,26,1310721,2,19,26,2,26,2, +1611819,12,10,3,6,10,4,8,1587563,1,6,2,1,6,3,26, +5,6,4,10,8,8,1556993,1310721,2,15,26,2,26,1310721,2,117, +26,1310721,2,19,26,2,26,2,10,3,6,10,4,8,1310721,2, +169,26,2,1713463,16,1558017,1310721,2,15,26,2,26,33,10,3,6, +10,4,8,1310721,2,15,26,2,1596201,12,8,1587565,2,1713543,16,1310721, +2,69,26,1310721,2,19,26,2,26,2,3,28,1,6,1,10, +28,34,63,38,3,26,1,6,1,10,28,34,63,38,1,10, +28,1713545,14,1,1713577,16,1469953,1310721,2,15,26,2,26,13,10,3, +6,10,4,8,1587565,2,1713657,16,1310721,2,69,26,1310721,2,19,26, +2,26,2,5,28,1,6,1,10,28,34,63,38,3,26,1, +6,1,10,28,34,63,38,1,10,28,1713659,14,1,1713719,16,1469953, +1310721,2,15,26,2,26,3,10,3,6,10,4,8,1474049,1310721,2, +15,26,2,26,3,10,3,6,10,4,8,1310721,2,19,26,1, +6,2,1,6,3,26,5,6,4,10,8,8,1713257,14,1310721,2, +19,26,1310721,2,11,26,2,10,3,6,10,4,8,1310721,2,19, +26,2,1713863,16,1310721,2,19,26,1,6,2,1,6,-1,26,5, +6,4,10,8,8,1310721,2,19,26,2,1595563,12,8,5,1594729,12, +8,1713787,14,1310721,2,169,26,2,1714083,16,1310721,2,171,26,1587549,2, +5,26,10,3,6,10,4,8,1310721,2,171,26,2,1594643,12,8, +15,1594729,12,8,1310721,2,19,26,1,10,3,6,10,4,8,1310721, +2,19,26,2,1310721,2,11,26,2,40,1714083,16,1310721,2,15,26, +1587559,2,1310721,2,19,26,2,26,10,3,6,10,4,8,1310721,2, +15,26,2,1596641,12,8,1310721,2,15,26,2,1597105,12,8,1310721,2, +19,26,1,6,2,1,6,3,26,5,6,4,10,8,8,1713951, +14,1709259,12,8,1310721,2,173,26,1587549,2,10,3,6,10,4,8, +1709075,12,8,1310721,2,169,26,2,1714183,16,1587769,1,10,3,6,10, +4,8,1310721,2,171,26,2,1310721,2,173,26,2,1587775,1587839,1587903,2, +1597353,12,8,1597859,12,8,1587721,1,10,3,6,10,4,8,1310721,2, +9,26,2,1714221,14,1,1310721,2,175,26,1310721,4,18,1310721,2,3, +28,1310721,4,1310721,2,1835009,40,1714269,16,1966081,1310721,4,1579223,12,1310721,2, +4,1604999,12,8,1,1,1607249,12,1714313,12,1714299,14,1,1310721,2,3, +26,1310721,4,18,1310721,2,13,28,1310721,4,1310721,2,1835009,40,1714347,16, +1966081,1310721,4,1579223,12,1310721,2,5,26,4,1310721,2,3,26,4,1310721, +2,4,1310721,2,3,26,2,1714491,16,1714483,14,116,97,103,115,32, +103,111,32,111,110,32,108,111,99,97,108,115,32,97,110,100, +32,112,97,114,97,109,101,116,101,114,115,44,32,110,111,116, +32,103,108,111,98,97,108,115,0,1714391,1591173,12,8,1310721,2,2, +1587695,2,28,1,6,1,10,28,34,63,38,3,26,1714589,16,1714581, +14,97,32,118,97,114,105,97,98,108,101,32,99,97,110,39, +116,32,98,101,32,118,111,105,100,0,1714531,1591173,12,8,3,1715505, +16,1587607,2,1606729,12,1714881,16,1310721,2,9,26,1685483,12,10,3,6, +10,4,8,1587609,2,1606959,12,8,1310721,2,11,26,1310721,2,5,26, +2,7,1587549,2,1592805,12,10,3,6,10,4,8,1310721,2,2,1, +6,1,10,28,34,63,38,3,26,1714731,16,1310721,2,1587687,2,10, +3,6,10,4,8,1559041,1310721,2,11,26,2,26,1310721,2,2,1310721, +2,9,26,2,1633251,12,10,3,6,10,4,8,1310721,2,9,26, +1559041,1310721,2,11,26,2,26,2,1637519,12,10,3,6,10,4,8, +1310721,2,9,26,2,1714877,16,1,1594449,12,8,1310721,2,9,26,1, +6,2,1,6,-1,26,5,6,4,10,8,8,1714817,14,1715441,14, +1310721,2,2,1714927,16,1310721,2,2,1638921,12,1,6,1,10,28,34, +63,38,1,10,28,1714929,14,1,1715099,16,1310721,2,11,26,1310721,2, +5,26,2,7,1587549,2,1592805,12,10,3,6,10,4,8,1559041,1310721, +2,11,26,2,26,1310721,2,2,10,3,6,10,4,8,1310721,2, +9,26,1310721,2,2,1637519,12,10,3,6,10,4,8,1310721,2,9, +26,2,1715095,16,1,1594449,12,8,1310721,2,9,26,1,6,2,1, +6,-1,26,5,6,4,10,8,8,1715035,14,1715441,14,1310721,2,7, +26,1587549,2,10,3,6,10,4,8,1310721,2,11,26,1310721,2,5, +26,2,5,1310721,2,7,26,2,1592805,12,10,3,6,10,4,8, +1310721,2,2,1715211,16,1559041,1310721,2,11,26,2,26,1310721,2,2,10, +3,6,10,4,8,1,1594449,12,8,1587615,2,1606729,12,1715441,16,1585289, +2,7,28,1,6,1,10,28,34,63,38,3,26,1715409,16,1310721, +2,7,26,2,1587551,2,26,1587549,2,10,3,6,10,4,8,1310721, +2,9,26,1585291,2,10,3,6,10,4,8,1310721,2,9,26,2, +2,1715391,16,1310721,2,9,26,2,2,1594449,12,8,1310721,2,9,26, +1,6,2,1,6,3,26,5,6,4,10,8,8,1715319,14,1, +1594449,12,8,1604999,12,8,1715441,14,1310721,2,7,26,2,1587551,2,26, +1685483,12,10,3,6,10,4,8,1587611,2,1606729,12,1,6,1,10, +28,34,63,38,3,26,1715477,16,1715505,14,1310721,2,5,26,1607249,12, +10,3,6,10,4,8,1714589,14,1587613,2,1606959,12,8,1,1715523,14, +1,1310721,2,13,26,1310721,4,18,1310721,2,3,28,1310721,4,1310721,2, +1835009,40,1715571,16,1966081,1310721,4,1579223,12,1604999,12,8,3,1715671,16,1310721, +2,1607249,12,10,3,6,10,4,8,1587615,2,1606959,12,8,1310721,2, +2,9,1685483,12,1592805,12,8,1587611,2,1606729,12,1,6,1,10,28, +34,63,38,3,26,1715667,16,1715671,14,1715577,14,1587613,2,1606959,12,8, +1,1715689,14,1,1310721,2,3,26,1310721,4,18,1310721,2,43,28,1310721, +4,1310721,2,1835009,40,1715737,16,1966081,1310721,4,1579223,12,1,1310721,2,3, +26,4,3,1310721,2,5,26,4,1,1310721,2,13,26,4,1,1310721, +2,15,26,4,1604999,12,8,1585289,2,3,28,1,6,1,10,28, +34,63,38,3,26,1715993,16,1585291,2,1,40,1715841,16,3,1715873,14, +1585291,2,17,10,40,1,6,1,10,28,34,63,38,1,10,28, +1715963,16,1715955,14,97,110,32,101,110,104,97,110,99,101,109,101, +110,116,32,104,97,115,32,97,116,32,109,111,115,116,32,56, +32,111,117,116,112,117,116,115,0,1715881,1591173,12,8,1310721,2,5, +26,1585291,2,10,3,6,10,4,8,1604999,12,8,1310721,2,1607249,12, +10,3,6,10,4,8,1587599,2,1606959,12,8,1587601,2,1606425,12,1, +6,1,10,28,34,63,38,3,26,1716297,16,3,1716297,16,1310721,2, +3,26,2,17,28,1,6,1,10,28,34,63,38,3,26,1716187, +16,1716179,14,97,110,32,101,110,104,97,110,99,101,109,101,110, +116,32,116,97,107,101,115,32,97,116,32,109,111,115,116,32, +56,32,105,110,112,117,116,115,0,1716103,1591557,12,8,1310721,2,27, +26,1310721,2,3,26,2,26,1607249,12,10,3,6,10,4,8,1310721, +2,3,26,1,6,2,1,6,3,26,5,6,4,10,8,8, +1587611,2,1606729,12,1,6,1,10,28,34,63,38,3,26,1716293,16, +1716297,14,1716055,14,1587601,2,1606959,12,8,1310721,2,7,26,1587549,2,10, +3,6,10,4,8,1310721,2,17,26,1,10,3,6,10,4,8, +1310721,2,17,26,2,15,40,1716417,16,1,1594449,12,8,1310721,2,17, +26,1,6,2,1,6,3,26,5,6,4,10,8,8,1716353,14, +1310721,2,9,26,1587549,2,10,3,6,10,4,8,1310721,2,17,26, +1,10,3,6,10,4,8,1310721,2,17,26,2,1310721,2,3,26, +2,40,1716535,16,1,1594449,12,8,1310721,2,17,26,1,6,2,1, +6,3,26,5,6,4,10,8,8,1716463,14,1310721,2,17,26,1, +10,3,6,10,4,8,1310721,2,17,26,2,1310721,2,3,26,2, +40,1716787,16,1310721,2,9,26,2,1310721,2,17,26,2,26,1587551,2, +26,1587549,2,10,3,6,10,4,8,1310721,2,19,26,1310721,2,27, +26,1310721,2,17,26,2,26,2,10,3,6,10,4,8,1310721,2, +19,26,2,2,1716741,16,1310721,2,19,26,2,2,1594449,12,8,1310721, +2,19,26,1,6,2,1,6,3,26,5,6,4,10,8,8, +1716669,14,1,1594449,12,8,1310721,2,17,26,1,6,2,1,6,3, +26,5,6,4,10,8,8,1716557,14,1587679,2,1606729,12,1717083,16,1310721, +2,11,26,1587549,2,10,3,6,10,4,8,1585289,2,7,28,1, +6,1,10,28,34,63,38,1,10,28,1716933,16,1716925,14,101,120, +112,101,99,116,101,100,32,116,104,101,32,114,101,99,105,112, +101,32,97,115,32,97,32,115,116,114,105,110,103,0,1716861,1591557, +12,8,1585289,2,7,28,1,6,1,10,28,34,63,38,3,26, +1717071,16,1310721,2,19,26,1585291,2,10,3,6,10,4,8,1310721,2, +19,26,2,2,1717061,16,1310721,2,19,26,2,2,1594449,12,8,1310721, +2,19,26,1,6,2,1,6,3,26,5,6,4,10,8,8, +1716989,14,1604999,12,8,1716933,14,1,1594449,12,8,1717249,14,1587681,2,1606729, +12,1717195,16,1310721,2,11,26,1587549,2,10,3,6,10,4,8,1698013, +12,8,19,1594729,12,8,1310721,2,13,26,1587549,2,1310721,2,11,26, +2,28,10,3,6,10,4,8,1310721,2,15,26,3,10,3,6, +10,4,8,1717249,14,1717241,14,101,120,112,101,99,116,101,100,32, +109,105,99,114,111,32,111,114,32,118,109,0,1717199,1591557,12,8, +1310721,2,23,26,1585215,2,10,3,6,10,4,8,1310721,2,23,26, +2,1025,40,3,36,1717379,16,1717371,14,97,108,108,32,53,49,50, +32,101,110,104,97,110,99,101,109,101,110,116,32,115,108,111, +116,115,32,97,114,101,32,116,97,107,101,110,0,1717299,1591079,12, +8,1585215,1,6,2,1,6,3,26,5,6,4,10,8,8,1310721, +2,19,26,1310721,2,7,26,2,1587551,2,26,10,3,6,10,4, +8,1310721,2,19,26,2,1,26,1310721,2,23,26,2,10,3,6, +10,4,8,1310721,2,19,26,2,3,26,1310721,2,15,26,2,10, +3,6,10,4,8,1310721,2,19,26,2,5,26,1310721,2,3,26, +2,10,3,6,10,4,8,1310721,2,19,26,2,7,26,1310721,2, +5,26,2,10,3,6,10,4,8,1310721,2,19,26,2,9,26, +1310721,2,11,26,2,10,3,6,10,4,8,1310721,2,19,26,2, +11,26,1310721,2,13,26,2,10,3,6,10,4,8,1310721,2,19, +26,2,13,26,1310721,2,9,26,2,10,3,6,10,4,8,1310721, +2,21,26,1310721,2,2,13,1310721,2,23,26,2,1592805,12,10,3, +6,10,4,8,1434113,1310721,2,21,26,2,26,1310721,2,3,26,2, +1310721,2,5,26,2,33,30,26,1310721,2,7,26,2,513,30,26, +10,3,6,10,4,8,1587645,2,1606729,12,1717937,16,1310721,2,25,26, +1585291,2,1593303,12,10,3,6,10,4,8,1310721,2,25,26,2,1, +40,1717891,16,1310721,2,25,26,1585291,2,1595111,12,10,3,6,10,4, +8,1604999,12,8,1450497,1310721,2,21,26,2,26,1310721,2,25,26,2, +3,26,10,3,6,10,4,8,1587613,2,1606959,12,8,1587551,2,1, +6,1,10,28,34,63,38,3,26,1718257,16,1310721,2,25,26,1310721, +2,7,26,2,1572879,12,10,3,6,10,4,8,1310721,2,25,26, +2,1,40,1718257,16,1450497,1310721,2,21,26,2,26,2,1,6,1, +10,28,34,63,38,3,26,1718211,16,1310721,2,2,1718203,14,116,104, +101,32,101,110,104,97,110,99,101,109,101,110,116,32,100,105, +100,110,39,116,32,105,110,115,116,97,108,108,32,40,115,101, +101,32,105,110,115,116,97,108,108,39,115,32,99,111,100,101, +115,32,105,110,32,99,111,114,101,46,99,41,0,1718077,1591343,12, +8,1442305,1310721,2,21,26,2,26,1442305,1310721,2,21,26,2,26,2, +129,34,10,3,6,10,4,8,1,1718265,14,1,1310721,2,43,26, +1310721,4,18,1310721,2,3,28,1310721,4,1310721,2,1835009,40,1718313,16,1966081, +1310721,4,1579223,12,1,1310721,2,4,1585289,2,1,28,1,6,1,10, +28,34,63,38,1,10,28,1719203,16,1587603,2,1606425,12,1718401,16,1310721, +2,1,6,2,1,6,3,26,5,6,4,10,8,8,1719193,14, +1587605,2,1606425,12,1718479,16,1310721,2,1,6,2,1,6,-1,26,5, +6,4,10,8,8,1310721,2,2,1,40,1718475,16,1310721,2,1,10, +3,6,10,4,8,1719193,14,1310721,2,2,1,6,1,10,28,34, +63,38,3,26,1718563,16,1585289,2,5,28,1,6,1,10,28,34, +63,38,3,26,1,6,1,10,28,34,63,38,1,10,28,1718565, +14,1,1719183,16,1585291,2,1587639,2,28,1,6,1,10,28,34,63, +38,3,26,1718609,16,3,1718661,14,1585291,2,1587641,2,28,1,6,1, +10,28,34,63,38,3,26,1,6,1,10,28,34,63,38,1, +10,28,1718671,16,3,1718723,14,1585291,2,1587643,2,28,1,6,1,10, +28,34,63,38,3,26,1,6,1,10,28,34,63,38,1,10, +28,1718733,16,3,1718785,14,1585291,2,1587745,2,28,1,6,1,10,28, +34,63,38,3,26,1,6,1,10,28,34,63,38,1,10,28, +1718795,16,3,1718847,14,1585291,2,1587747,2,28,1,6,1,10,28,34, +63,38,3,26,1,6,1,10,28,34,63,38,1,10,28,1718857, +16,3,1718909,14,1585291,2,1587677,2,28,1,6,1,10,28,34,63, +38,3,26,1,6,1,10,28,34,63,38,1,10,28,1718919,16, +3,1718971,14,1585291,2,1587749,2,28,1,6,1,10,28,34,63,38, +3,26,1,6,1,10,28,34,63,38,1,10,28,1718981,16,3, +1719033,14,1585291,2,1587649,2,28,1,6,1,10,28,34,63,38,3, +26,1,6,1,10,28,34,63,38,1,10,28,1719043,16,3,1719095, +14,1585291,2,1587651,2,28,1,6,1,10,28,34,63,38,3,26, +1,6,1,10,28,34,63,38,1,10,28,1719105,16,3,1719157,14, +1585291,2,1587759,2,28,1,6,1,10,28,34,63,38,3,26,1, +6,1,10,28,34,63,38,1,10,28,1,6,1,10,28,34, +63,38,1,10,28,1719185,14,1,1719193,16,1719203,14,1604999,12,8,1718321, +14,1590585,1,10,3,6,10,4,8,1,1719227,14,1,1310721,2,3, +26,1310721,4,18,1310721,2,39,28,1310721,4,1310721,2,1835009,40,1719275,16, +1966081,1310721,4,1579223,12,1,1310721,2,5,26,4,1587729,2,1606425,12,1, +6,1,10,28,34,63,38,3,26,1719395,16,1587721,2,1719373,16,1587705, +1,26,2,1719365,14,105,115,110,39,116,32,97,32,116,121,112, +101,0,1719339,1591343,12,8,1587721,1,10,3,6,10,4,8,1,1720165, +14,1604999,12,8,3,1719601,16,1310721,2,5,26,2,17,40,3,36, +1719491,16,1719483,14,116,111,111,32,109,97,110,121,32,116,121,112, +101,32,112,97,114,97,109,101,116,101,114,115,0,1719433,1591079,12, +8,1310721,2,7,26,1310721,2,5,26,2,26,1607249,12,10,3,6, +10,4,8,1310721,2,5,26,1,6,2,1,6,3,26,5,6, +4,10,8,8,1587611,2,1606729,12,1,6,1,10,28,34,63,38, +3,26,1719597,16,1719601,14,1719401,14,1644489,12,8,1310721,2,23,26,1, +17,1574703,12,8,1310721,2,1,10,3,6,10,4,8,1310721,2,2, +1587721,2,40,1720033,16,1310721,2,3,26,1,10,3,6,10,4,8, +1310721,2,3,26,2,1310721,2,5,26,2,40,1719791,16,1310721,2,7, +26,1310721,2,3,26,2,26,2,1587705,1310721,2,2,26,2,28,1, +6,1,10,28,34,63,38,1,10,28,1,6,1,10,28,34, +63,38,1,10,28,1719793,14,1,1719835,16,1310721,2,3,26,1,6, +2,1,6,3,26,5,6,4,10,8,8,1719681,14,1310721,2,3, +26,2,1310721,2,5,26,2,28,1,6,1,10,28,34,63,38, +3,26,1719935,16,1587705,1310721,2,2,26,2,1719923,14,105,115,110,39, +116,32,97,32,116,121,112,101,0,1719897,1591343,12,8,1719999,14,1310721, +2,23,26,1310721,2,2,26,1310721,2,3,26,2,1310721,2,7,26, +1310721,2,3,26,2,26,2,1635193,12,10,3,6,10,4,8,1310721, +2,1,6,2,1,6,3,26,5,6,4,10,8,8,1719643,14, +1587721,2,1720071,16,1587725,2,1,6,1,10,28,34,63,38,1,10, +28,1720073,14,1,1720107,16,1587725,1587725,2,1310721,2,23,26,1637691,12,10, +3,6,10,4,8,1587705,1310721,2,7,26,1310721,2,5,26,2,1574491, +12,8,1587721,1310721,2,5,26,2,10,3,6,10,4,8,1,1720165, +14,1,1310721,2,39,26,1310721,4,18,1310721,2,1,28,1310721,4,1310721, +2,1835009,40,1720213,16,1966081,1310721,4,1579223,12,1587723,3,10,3,6,10, +4,8,1647783,12,8,1587723,1,10,3,6,10,4,8,1,1720259,14, +1,1310721,2,1,26,1310721,4,18,1310721,2,7,28,1310721,4,1310721,2, +1835009,40,1720307,16,1966081,1310721,4,1579223,12,1310721,2,3,26,4,1310721,2, +4,1587721,1,10,3,6,10,4,8,1310721,2,3,26,2,1720569,16, +1587551,2,1720479,16,1720471,14,112,97,114,115,101,32,119,111,114,100, +115,32,99,97,110,39,116,32,98,101,32,100,101,102,105,110, +101,100,32,119,104,105,108,101,32,98,117,105,108,100,105,110, +103,32,97,110,32,105,109,97,103,101,0,1720365,1591173,12,8,1310721, +2,5,26,1409537,1310721,2,2,26,2,11,1425921,1310721,2,2,26,2, +1592805,12,10,3,6,10,4,8,1442305,1310721,2,5,26,2,26,3, +17,34,33,34,65,34,10,3,6,10,4,8,1,1720577,14,1, +1310721,2,7,26,1310721,4,18,1310721,2,11,28,1310721,4,1310721,2,1835009, +40,1720625,16,1966081,1310721,4,1579223,12,3,1310721,2,4,1,1310721,2,3, +26,4,1590585,2,1720661,16,1718279,12,1722455,14,1587649,2,1606577,12,1720731,16, +1604999,12,8,1587613,2,1606729,12,1720713,16,1585287,1,10,3,6,10,4, +8,1,1722455,14,1310721,2,1,10,3,6,10,4,8,1587651,2,1606577, +12,1720771,16,1604999,12,8,1310721,2,3,26,3,10,3,6,10,4, +8,1587771,1,10,3,6,10,4,8,1587759,2,1606577,12,1720821,16,1604999, +12,8,1587771,3,10,3,6,10,4,8,1587721,1,10,3,6,10, +4,8,1587639,2,1606577,12,1721063,16,1605859,12,8,1587511,2,9,28,1, +6,1,10,28,34,63,38,3,26,1720943,16,1587513,2,1587599,2,28, +1,6,1,10,28,34,63,38,3,26,1,6,1,10,28,34, +63,38,1,10,28,1720945,14,1,1,6,1,10,28,34,63,38, +3,26,1721063,16,1604999,12,8,1310721,2,9,26,1607249,12,10,3,6, +10,4,8,1587725,1,10,3,6,10,4,8,1719241,12,8,1310721,2, +2,1,1,1310721,2,9,26,2,1709669,12,1310721,2,3,26,2,1720273, +12,1722455,14,1310721,2,2,1,6,1,10,28,34,63,38,3,26, +1721099,16,3,1721131,14,1310721,2,3,26,2,1,6,1,10,28,34, +63,38,1,10,28,1721141,16,3,1721167,14,1587771,2,1,6,1,10, +28,34,63,38,1,10,28,1721501,16,1720179,12,8,1310721,2,9,26, +1607249,12,10,3,6,10,4,8,1719241,12,8,1587727,2,1721271,16,1587727, +2,5,28,1,6,1,10,28,34,63,38,1,10,28,1,6, +1,10,28,34,63,38,1,10,28,1721273,14,1,1721341,16,1721333,14, +116,104,101,32,111,110,108,121,32,114,101,116,117,114,110,32, +116,97,103,32,105,115,32,46,117,0,1721281,1591173,12,8,1587599,2, +1606425,12,1,6,1,10,28,34,63,38,3,26,1721431,16,1721417,14, +101,120,112,101,99,116,101,100,32,97,32,102,117,110,99,116, +105,111,110,0,1721377,1591557,12,8,1,1722455,14,1310721,2,2,1587725,2, +1587727,2,5,28,1,6,1,10,28,34,63,38,3,26,1310721,2, +9,26,2,1709669,12,1310721,2,3,26,2,1720273,12,1722455,14,1587641,2, +1606577,12,1721523,16,1,1714235,12,1722455,14,1587745,2,1606577,12,1721547,16,1648157, +12,8,1,1722455,14,1587747,2,1606577,12,1721567,16,1650063,12,1722455,14,1587749, +2,1606577,12,1721587,16,1650183,12,1722455,14,1587677,2,1606577,12,1721607,16,1715703, +12,1722455,14,1587643,2,1606577,12,1721627,16,1715537,12,1722455,14,1585289,2,5, +28,1,6,1,10,28,34,63,38,3,26,1721857,16,1310721,2,5, +26,1585291,2,1593303,12,10,3,6,10,4,8,1310721,2,5,26,2, +1,40,3,36,1721775,16,1417729,1310721,2,5,26,2,26,2,11,28, +1,6,1,10,28,34,63,38,3,26,1,6,1,10,28,34, +63,38,1,10,28,1721777,14,1,1721827,16,1442305,1310721,2,5,26,2, +26,2,65,32,1,6,1,10,28,34,63,38,1,10,28,1721829, +14,1,1721857,16,1310721,2,5,26,2,1,1663505,12,8,1,1722455,14, +1646981,12,1721871,16,3,1721901,14,1587623,2,1606425,12,1,6,1,10,28, +34,63,38,1,10,28,1721911,16,3,1722005,14,1585289,2,5,28,1, +6,1,10,28,34,63,38,3,26,1721981,16,1585291,2,1593303,12,1, +40,1,6,1,10,28,34,63,38,1,10,28,1721983,14,1,1, +6,1,10,28,34,63,38,1,10,28,1722389,16,1720179,12,8,1310721, +2,9,26,1607249,12,10,3,6,10,4,8,1587599,2,1606425,12,1722057, +16,3,1722087,14,1587729,2,1606425,12,1,6,1,10,28,34,63,38, +1,10,28,1722293,16,1719241,12,8,1587727,2,1722161,16,1587727,2,5,28, +1,6,1,10,28,34,63,38,1,10,28,1,6,1,10,28, +34,63,38,1,10,28,1722163,14,1,1722231,16,1722223,14,116,104,101, +32,111,110,108,121,32,114,101,116,117,114,110,32,116,97,103, +32,105,115,32,46,117,0,1722171,1591173,12,8,1310721,2,2,1587725,2, +1587727,2,5,28,1,6,1,10,28,34,63,38,3,26,1310721,2, +9,26,2,1709669,12,1,1720273,12,1722455,14,1587721,2,1722363,16,1587705,1, +26,2,1722339,14,105,115,110,39,116,32,97,32,116,121,112,101, +0,1722313,1591343,12,8,1587721,1,10,3,6,10,4,8,1587725,2,1587727, +2,1310721,2,9,26,2,1714313,12,1722455,14,1722439,14,101,120,112,101, +99,116,101,100,32,97,32,100,101,99,108,97,114,97,116,105, +111,110,0,1722393,1591557,12,8,1,1722455,14,1,1310721,2,11,26,1310721, +4,18,1310721,2,1,28,1310721,4,1310721,2,1835009,40,1722503,16,1966081,1310721, +4,1579223,12,1585289,2,1,28,1,6,1,10,28,34,63,38,1, +10,28,1722547,16,1720591,12,8,1722503,14,1,1722555,14,1,1310721,2,1, +26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,1835009,40,1722603,16, +1966081,1310721,4,1579223,12,1310721,2,4,1310721,2,2,1310721,2,3,26,4, +1590585,1,10,3,6,10,4,8,1310721,2,3,26,2,1585219,2,40, +1722973,16,1417729,1310721,2,3,26,2,26,2,3,28,1,6,1,10, +28,34,63,38,3,26,1722771,16,1442305,1310721,2,3,26,2,26,2, +3,32,1,6,1,10,28,34,63,38,3,26,1,6,1,10, +28,34,63,38,1,10,28,1722773,14,1,1722819,16,1450497,1310721,2,3, +26,2,26,2,1,6,1,10,28,34,63,38,1,10,28,1722821, +14,1,1722935,16,1585295,1425921,1310721,2,3,26,2,26,2,10,3,6, +10,4,8,1409537,1310721,2,3,26,2,26,2,1722927,14,105,115,32, +117,115,101,100,32,98,117,116,32,110,101,118,101,114,32,100, +101,102,105,110,101,100,0,1722875,1591343,12,8,1310721,2,3,26,1, +6,2,1,6,3,26,5,6,4,10,8,8,1722641,14,1590583,2, +1722987,16,1590891,12,8,1,1722995,14,1,1310721,2,5,26,1310721,4,18, +1310721,2,11,28,1310721,4,1310721,2,1835009,40,1723043,16,1966081,1310721,4,1579223, +12,1310721,2,3,26,4,1310721,2,4,3,1,10,28,1310721,2,5, +26,4,1,1310721,2,7,26,4,1585219,2,1310721,2,9,26,4,1707025, +12,8,1587577,2,1723149,16,1310721,2,5,26,1595029,12,10,3,6,10, +4,8,15,1594729,12,8,1310721,2,3,26,2,1723177,16,3,1310721,2, +2,1705455,12,8,3,1310721,2,2,1,1,1598871,12,8,1604999,12,8, +1310721,2,3,26,2,1723225,16,1722469,12,8,1723379,14,1310721,2,7,26, +1587549,2,10,3,6,10,4,8,3,1,1708505,12,8,1708837,12,8, +1585289,2,1,28,1,6,1,10,28,34,63,38,1,10,28,1723309, +16,1686451,12,8,1723265,14,1587559,2,1614147,12,8,1587565,2,1723357,16,1587569, +2,1,6,1,10,28,34,63,38,1,10,28,1723359,14,1,1723373, +16,3,3,1613419,12,8,1709075,12,8,1600299,12,8,1310721,2,9,26, +2,1722569,12,8,1310721,2,5,26,2,1,40,3,36,1723443,16,1310721, +2,5,26,2,1587549,2,1594811,12,8,1587549,2,1587555,2,10,40,1723477, +16,1587555,1587549,2,10,3,6,10,4,8,1707801,12,8,1310721,2,7, +26,2,1723499,14,1,1310721,2,11,26,1310721,4,18,1310721,2,3,28, +1310721,4,1310721,2,1835009,40,1723547,16,1966081,1310721,4,1579223,12,1310721,2,4, +1310721,2,2,1,1723009,12,1723571,14,1,1310721,2,3,26,1310721,4,18, +1310721,2,3,28,1310721,4,1310721,2,1835009,40,1723619,16,1966081,1310721,4,1579223, +12,1310721,2,4,1310721,2,2,3,1723009,12,8,1,1723647,14,1,1310721, +2,3,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,1835009,40, +1723695,16,1966081,1310721,4,1579223,12,1310721,2,4,1310721,2,2,1,1723009,12, +1310721,2,3,26,4,1310721,2,3,26,2,1,6,12,10,8,1723749, +14,1,1310721,2,5,26,1310721,4,18,1310721,2,7,28,1310721,4,1310721, +2,1835009,40,1723797,16,1966081,1310721,4,1579223,12,1310721,2,4,1310721,2,2, +1702323,12,1310721,2,3,26,4,1310721,2,3,26,2,1723661,12,1310721,2, +5,26,4,1310721,2,3,26,2,1580973,12,8,1310721,2,5,26,2, +1723879,14,1,1310721,2,7,26,1310721,4,18,1310721,2,7,28,1310721,4, +1310721,2,1835009,40,1723927,16,1966081,1310721,4,1579223,12,1310721,2,4,1310721,2, +2,1310721,2,3,26,4,1310721,2,3,26,2,1585219,2,40,1724347,16, +1417729,1310721,2,3,26,2,26,2,3,40,3,36,1724049,16,1417729,1310721, +2,3,26,2,26,2,9,10,40,3,36,1,6,1,10,28, +34,63,38,1,10,28,1724051,14,1,1724061,16,3,1724123,14,1417729,1310721, +2,3,26,2,26,2,13,28,1,6,1,10,28,34,63,38, +3,26,1,6,1,10,28,34,63,38,1,10,28,1724309,16,1417729, +1310721,2,3,26,2,26,2,1594449,12,8,1425921,1310721,2,3,26,2, +26,2,1594449,12,8,1434113,1310721,2,3,26,2,26,2,1594449,12,8, +1310721,2,5,26,1409537,1310721,2,3,26,2,26,2,10,3,6,10, +4,8,1310721,2,5,26,2,2,1724301,16,1310721,2,5,26,2,2, +1594449,12,8,1310721,2,5,26,1,6,2,1,6,3,26,5,6, +4,10,8,8,1724229,14,1,1594449,12,8,1310721,2,3,26,1,6, +2,1,6,3,26,5,6,4,10,8,8,1723949,14,1,1594449,12, +8,1,1724363,14,1,1310721,2,7,26,1310721,4,18,1310721,2,7,28, +1310721,4,1310721,2,1835009,40,1724411,16,1966081,1310721,4,1579223,12,1310721,2,4, +1310721,2,2,2,1724759,16,1310721,2,5,26,1310721,2,2,7,26,1592287, +12,10,3,6,10,4,8,1310721,2,3,26,1310721,2,5,26,2, +1310721,2,2,1,26,2,1310721,2,2,3,26,2,1592805,12,10,3, +6,10,4,8,1434113,1310721,2,3,26,2,26,1310721,2,2,5,26, +2,10,3,6,10,4,8,1442305,1310721,2,3,26,2,26,3,5, +34,10,3,6,10,4,8,1310721,2,2,1,26,2,13,28,1, +6,1,10,28,34,63,38,3,26,1724709,16,1310721,2,2,3,26, +2,1585215,2,40,3,36,1724687,16,1585215,1310721,2,2,3,26,2,3, +26,10,3,6,10,4,8,1310721,2,2,5,26,2,17,38,1572879, +12,8,1310721,2,1310721,2,2,7,26,1310721,2,2,7,26,1574039,12, +26,3,26,10,3,6,10,4,8,1724417,14,1,1724767,14,1,1310721, +2,7,26,1310721,4,18,1310721,2,21,28,1310721,4,1310721,2,1835009,40, +1724815,16,1966081,1310721,4,1579223,12,1310721,2,3,26,4,1310721,2,4,1587549, +2,1310721,2,5,26,4,1587551,2,1310721,2,7,26,4,1587553,2,1310721, +2,9,26,4,1585221,2,1310721,2,11,26,4,1585223,2,1310721,2,13, +26,4,1585219,2,1310721,2,15,26,4,1585215,2,1310721,2,17,26,4, +1585221,1585219,2,10,3,6,10,4,8,1585223,1585219,2,10,3,6,10, +4,8,1585215,1,10,3,6,10,4,8,1587549,1310721,2,2,10,3, +6,10,4,8,1587551,1310721,2,3,26,2,1310721,2,2,28,10,3, +6,10,4,8,1587553,1310721,2,2,393217,26,10,3,6,10,4,8, +1966081,1594643,12,8,1310721,1594643,12,8,5,1594729,12,8,1587667,2,1595111,12, +1595235,12,8,15,1594729,12,8,1,1594449,12,8,1,1594449,12,8,1706887, +12,8,1310721,2,3,26,2,11,26,1587549,2,10,3,6,10,4, +8,1585221,2,1723893,12,8,1310721,2,3,26,2,13,26,1587549,2,10, +3,6,10,4,8,1310721,2,19,26,1587549,2,1310721,2,2,28,10, +3,6,10,4,8,1310721,2,15,26,2,1593651,12,8,1585221,1310721,2, +11,26,2,10,3,6,10,4,8,1585223,1310721,2,13,26,2,10, +3,6,10,4,8,1587549,1310721,2,5,26,2,10,3,6,10,4, +8,1587551,1310721,2,7,26,2,10,3,6,10,4,8,1587553,1310721,2, +9,26,2,10,3,6,10,4,8,1585215,1310721,2,17,26,2,10, +3,6,10,4,8,1310721,2,19,26,2,1725403,14,1,1310721,2,21, +26,1310721,4,18,1310721,2,1,28,1310721,4,1310721,2,1835009,40,1725451,16, +1966081,1310721,4,1579223,12,1,1587555,2,1583851,12,8,1587551,2,1,6,1, +10,28,34,63,38,3,26,1725529,16,1587549,2,1587555,2,10,40,1, +6,1,10,28,34,63,38,1,10,28,1725531,14,1,1725549,16,1587555, +2,1587549,2,1583851,12,8,1572865,1572865,13,26,2,1583851,12,8,1540609,1540609, +2049,26,1583851,12,8,1310721,2,1966081,1583851,12,8,2064385,2097121,1583851,12,8, +1,1725609,14,1,1310721,2,1,26,1310721,4,18,1310721,2,1,28,1310721, +4,1310721,2,1835009,40,1725657,16,1966081,1310721,4,1579223,12,1585217,1327617,10,3, +6,10,4,8,1311233,1,16385,1574703,12,8,1585219,1,10,3,6,10, +4,8,1585225,1,10,3,6,10,4,8,1585227,1,10,3,6,10, +4,8,1585229,1,10,3,6,10,4,8,1585231,1,10,3,6,10, +4,8,1585233,1,10,3,6,10,4,8,1585235,1,10,3,6,10, +4,8,1585237,1,10,3,6,10,4,8,1587579,1,10,3,6,10, +4,8,1587583,1,10,3,6,10,4,8,1587585,1,10,3,6,10, +4,8,1587557,1,10,3,6,10,4,8,1587559,1,10,3,6,10, +4,8,1587599,1725903,14,40,0,1725899,1592287,12,10,3,6,10,4,8, +1587601,1725931,14,41,0,1725927,1592287,12,10,3,6,10,4,8,1587603,1725959, +14,123,0,1725955,1592287,12,10,3,6,10,4,8,1587605,1725987,14,125, +0,1725983,1592287,12,10,3,6,10,4,8,1587607,1726015,14,91,0,1726011, +1592287,12,10,3,6,10,4,8,1587609,1726043,14,93,0,1726039,1592287,12, +10,3,6,10,4,8,1587611,1726071,14,44,0,1726067,1592287,12,10,3, +6,10,4,8,1587613,1726099,14,59,0,1726095,1592287,12,10,3,6,10, +4,8,1587615,1726127,14,61,0,1726123,1592287,12,10,3,6,10,4,8, +1587617,1726157,14,45,62,0,1726151,1592287,12,10,3,6,10,4,8,1587619, +1726187,14,43,43,0,1726181,1592287,12,10,3,6,10,4,8,1587621,1726217, +14,45,45,0,1726211,1592287,12,10,3,6,10,4,8,1587623,1726245,14, +42,0,1726241,1592287,12,10,3,6,10,4,8,1587625,1726273,14,38,0, +1726269,1592287,12,10,3,6,10,4,8,1587627,1726301,14,58,0,1726297,1592287, +12,10,3,6,10,4,8,1587629,1726329,14,35,0,1726325,1592287,12,10, +3,6,10,4,8,1587631,1726357,14,45,0,1726353,1592287,12,10,3,6, +10,4,8,1587637,1726385,14,46,0,1726381,1592287,12,10,3,6,10,4, +8,1587659,1726413,14,63,0,1726409,1592287,12,10,3,6,10,4,8,1587661, +1726447,14,99,97,115,101,0,1726437,1592287,12,10,3,6,10,4,8, +1587663,1726487,14,100,101,102,97,117,108,116,0,1726471,1592287,12,10,3, +6,10,4,8,1587633,1726517,14,38,38,0,1726511,1592287,12,10,3,6, +10,4,8,1587635,1726547,14,124,124,0,1726541,1592287,12,10,3,6,10, +4,8,1587639,1726577,14,102,110,0,1726571,1592287,12,10,3,6,10,4, +8,1587641,1726609,14,108,101,116,0,1726601,1592287,12,10,3,6,10,4, +8,1587643,1726645,14,99,111,110,115,116,0,1726633,1592287,12,10,3,6, +10,4,8,1587645,1726679,14,101,108,115,101,0,1726669,1592287,12,10,3, +6,10,4,8,1587647,1726709,14,97,115,0,1726703,1592287,12,10,3,6, +10,4,8,1587649,1726753,14,117,110,99,104,101,99,107,101,100,0, +1726733,1592287,12,10,3,6,10,4,8,1587651,1726797,14,105,109,109,101, +100,105,97,116,101,0,1726777,1592287,12,10,3,6,10,4,8,1587653, +1726825,14,109,0,1726821,1592287,12,10,3,6,10,4,8,1587655,1726853,14, +100,0,1726849,1592287,12,10,3,6,10,4,8,1587657,1726881,14,117,0, +1726877,1592287,12,10,3,6,10,4,8,1587667,1726917,14,115,116,97,114, +116,0,1726905,1592287,12,10,3,6,10,4,8,1587669,1726951,14,109,97, +105,110,0,1726941,1592287,12,10,3,6,10,4,8,1587671,1726985,14,102, +114,101,101,0,1726975,1592287,12,10,3,6,10,4,8,1587673,1727017,14, +111,119,110,0,1727009,1592287,12,10,3,6,10,4,8,1587675,1727051,14, +113,110,101,119,0,1727041,1592287,12,10,3,6,10,4,8,1587665,1727105, +14,102,114,97,109,101,95,111,118,101,114,102,108,111,119,0, +1727075,1592287,12,10,3,6,10,4,8,1587677,1727145,14,101,110,104,97, +110,99,101,0,1727129,1592287,12,10,3,6,10,4,8,1587679,1727181,14, +109,105,99,114,111,0,1727169,1592287,12,10,3,6,10,4,8,1587681, +1727211,14,118,109,0,1727205,1592287,12,10,3,6,10,4,8,1,1727237, +14,1,1310721,2,1,26,1310721,4,18,1310721,2,1,28,1310721,4,1310721, +2,1835009,40,1727285,16,1966081,1310721,4,1579223,12,1727293,14,33,0,1727289,1727341, +14,48,32,36,32,48,32,126,32,45,32,124,32,51,49,32, +62,32,49,32,43,0,1727299,1611115,12,8,1727359,14,33,33,0,1727353, +1727411,14,48,32,36,32,48,32,126,32,45,32,124,32,51,49, +32,62,32,48,32,126,32,45,0,1727365,1611115,12,8,1727427,14,126, +0,1727423,1727443,14,45,49,32,94,0,1727433,1611115,12,8,1727459,14,45, +0,1727455,1727477,14,48,32,126,32,45,0,1727465,1611115,12,8,1727493,14, +42,0,1727489,21,1727505,14,42,0,1727501,1610075,12,8,1727521,14,47,0, +1727517,21,1727539,14,105,100,105,118,0,1727529,1610075,12,8,1727555,14,37, +0,1727551,21,1727573,14,105,109,111,100,0,1727563,1610075,12,8,1727589,14, +43,0,1727585,19,1727601,14,43,0,1727597,1610075,12,8,1727617,14,45,0, +1727613,19,1727629,14,45,0,1727625,1610075,12,8,1727647,14,60,60,0,1727641, +17,1727665,14,105,115,104,108,0,1727655,1610075,12,8,1727683,14,62,62, +0,1727677,17,1727695,14,62,0,1727691,1610075,12,8,1727711,14,60,0,1727707, +15,1727723,14,60,0,1727719,1610075,12,8,1727739,14,62,0,1727735,15,1727755, +14,126,32,60,0,1727747,1610075,12,8,1727773,14,60,61,0,1727767,15, +1727797,14,126,32,60,32,49,32,94,0,1727781,1610075,12,8,1727815,14, +62,61,0,1727809,15,1727835,14,60,32,49,32,94,0,1727823,1610075,12, +8,1727853,14,61,61,0,1727847,13,1727907,14,45,32,48,32,36,32, +48,32,126,32,45,32,124,32,51,49,32,62,32,49,32,43, +0,1727861,1610075,12,8,1727925,14,33,61,0,1727919,13,1727983,14,45,32, +48,32,36,32,48,32,126,32,45,32,124,32,51,49,32,62, +32,48,32,126,32,45,0,1727933,1610075,12,8,1727999,14,38,0,1727995, +11,1728011,14,38,0,1728007,1610075,12,8,1728027,14,94,0,1728023,9,1728039, +14,94,0,1728035,1610075,12,8,1728055,14,124,0,1728051,7,1728067,14,124, +0,1728063,1610075,12,8,1728085,14,38,38,0,1728079,3,1,10,28,1, +1610075,12,8,1728113,14,124,124,0,1728107,5,1,10,28,1,1610075,12, +8,1587615,2,1609247,12,8,1587617,2,1609247,12,8,1587619,2,1609247,12,8, +1587621,2,1609247,12,8,1728183,14,108,101,116,0,1728175,1688971,17,1611449,12, +8,1728205,14,105,102,0,1728199,1689043,17,1611449,12,8,1728233,14,119,104, +105,108,101,0,1728221,1691609,17,1611449,12,8,1728257,14,102,111,114,0, +1728249,1692221,17,1611449,12,8,1728287,14,115,119,105,116,99,104,0,1728273, +1694791,17,1611449,12,8,1728315,14,98,114,101,97,107,0,1728303,1693325,17, +1611449,12,8,1728349,14,99,111,110,116,105,110,117,101,0,1728331,1693751, +17,1611449,12,8,1728379,14,114,101,116,117,114,110,0,1728365,1696095,17, +1611449,12,8,1728415,14,117,110,99,104,101,99,107,101,100,0,1728395, +1697893,17,1611449,12,8,1728439,14,97,115,109,0,1728431,1700799,17,33,34, +1611449,12,8,1728471,14,113,117,111,116,101,0,1728459,1702049,33,1611449,12, +8,1728501,14,115,105,122,101,111,102,0,1728487,1653375,33,1611449,12,8, +1728531,14,116,121,112,101,111,102,0,1728517,1653491,33,1611449,12,8,1728553, +14,111,51,0,1728547,1696819,17,1611449,12,8,1728577,14,108,101,110,0, +1728569,1651499,33,1611449,12,8,1728601,14,99,97,112,0,1728593,1651989,33,1611449, +12,8,1728627,14,112,117,115,104,0,1728617,1652259,33,1611449,12,8,1728651, +14,112,111,112,0,1728643,1652983,33,1611449,12,8,1728681,14,114,101,115, +105,122,101,0,1728667,1653187,33,1611449,12,8,1,1728701,14,1,1310721,2, +1,26,1310721,4,18,1310721,2,5,28,1310721,4,1310721,2,1835009,40,1728749, +16,1966081,1310721,4,1579223,12,1577233,12,8,1725623,12,8,1572865,11,26,2, +1724377,12,8,1727251,12,8,1653603,12,8,1587549,1,10,3,6,10,4, +8,1587551,1,10,3,6,10,4,8,1587553,524289,10,3,6,10,4, +8,1587555,1,10,3,6,10,4,8,1585221,1585219,2,10,3,6,10, +4,8,1585223,1,10,3,6,10,4,8,1706887,12,8,1587549,2,1587555, +2,10,40,1728925,16,1587555,1587549,2,10,3,6,10,4,8,1310721,2, +1587669,2,1593303,12,10,3,6,10,4,8,1310721,2,2,1,40,3, +36,1729029,16,1417729,1310721,2,2,26,2,3,28,1,6,1,10,28, +34,63,38,3,26,1,6,1,10,28,34,63,38,1,10,28, +1729031,14,1,1729077,16,1442305,1310721,2,2,26,2,3,32,1,6,1, +10,28,34,63,38,1,10,28,1729079,14,1,1729137,16,1310721,2,3, +26,1425921,1310721,2,2,26,2,10,3,6,10,4,8,1310721,2,3, +26,2,1,6,12,10,8,8,1573437,12,8,1,1729151,14,1,1310721, +2,5,26,1310721,4,18,1,864357,0,115,116,97,114,116,0,1, +786439,2,105,110,115,116,97,108,108,0,1,786627,2,112,117,116, +99,0,1,786662,0,103,101,116,99,0,1,786690,0,101,111,102, +0,1,786718,0,104,97,108,116,0,1,786747,2,112,117,116,115, +0,1,786828,2,112,117,116,110,0,1,787019,2,115,116,114,108, +101,110,0,1,787091,4,115,116,114,101,113,0,1,787245,6,119, +99,111,112,121,0,1,787351,6,119,102,105,108,108,0,1,787483, +4,122,117,100,105,118,0,1,787714,4,122,100,105,118,0,1, +787902,4,122,109,111,100,0,1,787997,4,122,115,104,108,0,1, +788082,2,116,111,112,115,105,122,101,0,1,788258,4,102,112,117, +115,104,0,1,788382,4,102,117,110,108,105,110,107,0,1,788495, +4,109,107,98,108,111,99,107,0,1,788616,0,104,101,97,112, +95,105,110,105,116,0,1,788847,2,98,97,108,108,111,99,0, +1,789084,2,98,102,114,101,101,0,1,789482,2,105,115,111,98, +106,0,1,789611,0,102,114,97,109,101,95,111,118,101,114,102, +108,111,119,0,1,789674,0,111,111,109,0,1,789735,3,110,101, +119,95,114,97,119,0,1,790016,5,110,101,119,95,116,0,1, +790109,3,110,101,119,0,1,790145,2,111,115,105,122,101,0,1, +790206,2,116,121,112,101,111,102,95,111,98,106,0,1,790292,2, +114,101,108,101,97,115,101,0,1,790486,130,102,114,101,101,0, +1,790530,4,111,119,110,0,1,790594,7,100,110,101,119,0,1, +790780,4,100,103,114,111,119,0,1,791136,4,111,111,98,0,1, +791244,4,100,97,116,0,1,791351,516,100,112,117,115,104,0,1, +791458,2,100,112,111,112,0,1,791547,4,100,114,101,115,105,122, +101,0,1,791703,2,100,108,101,110,0,1,791739,2,100,99,97, +112,0,1,791779,2,109,97,114,107,0,1,791925,4,109,97,114, +107,114,97,110,103,101,0,1,791996,0,100,114,97,105,110,0, +1,862708,0,103,99,95,114,111,111,116,115,0,1,792078,0,103, +99,0,1,792396,0,108,105,118,101,95,111,98,106,101,99,116, +115,0,1,792492,0,108,97,114,103,101,115,116,95,102,114,101, +101,0,1,795293,0,101,114,114,97,116,0,1,795365,0,101,114, +114,101,110,100,0,1,795445,0,103,105,118,101,117,112,0,1, +795539,2,102,97,116,97,108,0,1,795586,2,115,101,114,114,0, +1,795671,4,115,101,114,114,110,0,1,795778,2,100,105,101,0, +1,795845,4,100,105,101,110,0,1,795922,2,99,101,114,114,0, +1,795981,4,99,101,114,114,110,0,1,796053,2,104,97,115,104, +115,0,1,796143,2,105,110,116,101,114,110,0,1,796402,6,97, +100,100,115,121,109,0,1,796651,2,108,111,111,107,117,112,0, +1,796825,2,112,111,112,115,121,109,115,0,1,796902,2,108,111, +111,107,117,112,115,0,1,796939,4,102,105,120,110,101,119,0, +1,797115,4,102,105,120,97,108,108,0,1,797224,2,101,109,105, +116,0,1,797321,2,108,105,116,0,1,797364,2,111,112,0,1, +797405,4,115,101,116,108,105,116,0,1,797473,2,106,109,112,0, +1,797514,0,104,111,108,101,0,1,797555,2,102,119,100,102,110, +0,1,797617,2,102,110,114,101,102,0,1,797704,2,99,97,108, +108,110,97,109,101,0,1,797781,2,108,97,100,100,114,0,1, +797835,2,118,97,100,100,114,0,1,797969,0,111,51,114,101,115, +101,116,0,1,798021,0,111,51,116,97,107,101,0,1,798100,2, +111,51,112,114,111,109,111,116,101,0,1,798320,2,111,51,109, +107,99,97,99,104,101,0,1,798552,2,111,51,114,101,102,114, +101,115,104,0,1,798676,10,111,51,114,101,103,105,111,110,0, +1,798929,0,111,51,102,108,117,115,104,0,1,799052,2,115,116, +114,108,105,116,0,1,799158,0,110,101,120,116,99,104,0,1, +799435,8,115,114,99,95,112,117,115,104,0,1,800149,0,115,114, +99,95,112,111,112,0,1,800490,2,105,115,111,112,99,104,0, +1,800601,4,111,112,112,114,101,102,105,120,0,1,800844,2,105, +115,100,105,103,0,1,800905,2,105,115,97,108,0,1,801044,2, +104,101,120,118,0,1,801176,0,101,115,99,99,104,0,1,801350, +2,111,112,108,101,120,0,1,801495,0,108,101,120,0,1,802499, +0,110,101,120,116,0,1,802698,2,112,101,101,107,97,116,0, +1,802929,0,112,101,101,107,0,1,802959,0,110,101,120,116,48, +0,1,803212,2,105,115,95,111,112,0,1,803288,2,105,115,95, +105,100,0,1,803364,2,97,99,99,101,112,116,0,1,803479,2, +101,120,112,101,99,116,0,1,803624,0,110,97,109,101,0,1, +803701,2,115,101,113,97,115,109,0,1,804270,2,109,110,101,109, +105,100,120,0,1,804372,4,101,109,105,116,115,101,113,0,1, +804623,2,97,100,100,107,111,112,0,1,804743,2,98,105,110,102, +105,110,100,0,1,804841,2,117,110,102,105,110,100,0,1,804939, +2,97,115,103,102,105,110,100,0,1,805037,6,97,100,100,95, +98,105,110,111,112,0,1,805557,4,97,100,100,95,117,110,111, +112,0,1,805724,6,97,100,100,95,107,119,0,1,805807,2,102, +105,110,100,108,111,99,0,1,805909,2,110,101,119,108,111,99, +0,1,806154,2,108,114,111,111,116,0,1,806223,2,116,114,97, +99,107,101,100,0,1,806272,2,116,111,117,99,104,0,1,806516, +2,112,108,97,105,110,117,115,101,0,1,806656,2,102,114,101, +101,118,97,114,0,1,806709,4,101,120,105,116,95,118,97,114, +115,0,1,807073,2,117,110,117,115,101,100,95,99,104,101,99, +107,0,1,807222,2,98,108,111,99,107,95,99,108,111,115,101, +0,1,807508,0,115,110,97,112,95,112,117,115,104,0,1,807783, +2,115,110,97,112,95,112,111,112,0,1,807824,2,115,110,97, +112,95,114,101,115,116,111,114,101,0,1,808043,2,101,109,112, +116,121,0,1,808119,2,115,110,97,112,95,99,109,112,0,1, +808524,2,115,116,97,116,101,110,97,109,101,0,1,808742,6,109, +101,114,103,101,95,101,114,114,0,1,809008,4,106,115,116,0, +1,809083,4,106,111,119,0,1,809139,4,106,104,109,0,1,809195, +2,101,120,105,116,95,114,101,99,0,1,809491,2,106,97,100, +100,0,1,809595,6,106,115,97,109,101,0,1,809855,4,106,111, +105,110,0,1,810647,6,102,105,120,117,112,115,0,1,810894,2, +98,97,99,107,101,100,103,101,0,1,811685,12,106,111,105,110, +50,0,1,811972,4,114,101,99,97,115,116,0,1,812508,4,97, +114,103,99,104,101,99,107,0,1,813274,2,101,109,105,116,111, +119,110,0,1,813392,6,97,115,115,105,103,110,99,104,101,99, +107,0,1,815169,2,114,101,116,99,104,101,99,107,0,1,815676, +2,104,97,110,100,117,112,0,1,816034,2,100,105,115,99,97, +114,100,0,1,816157,2,116,97,108,108,111,99,0,1,816264,8, +116,110,101,119,0,1,816438,2,116,112,116,114,0,1,816625,4, +116,97,114,114,0,1,816885,8,116,102,110,0,1,817346,4,116, +100,121,110,0,1,817596,4,116,118,97,114,0,1,817792,6,116, +105,110,115,116,0,1,818223,2,116,114,101,115,111,108,118,101, +0,1,818759,2,116,115,122,0,1,818845,4,115,117,98,115,116, +0,1,819344,2,105,115,110,117,109,0,1,819460,2,105,115,97, +103,103,0,1,819542,2,105,115,112,116,114,0,1,819624,2,100, +101,99,97,121,0,1,819687,2,116,101,108,101,109,0,1,819781, +4,99,111,109,112,97,116,0,1,820439,6,117,110,105,102,121, +0,1,820984,2,116,112,114,105,110,116,0,1,821612,6,116,99, +101,114,114,0,1,821812,6,116,99,110,101,101,100,0,1,822007, +2,116,112,102,105,110,100,0,1,822105,2,105,115,116,121,112, +101,110,97,109,101,0,1,822244,0,99,108,111,115,101,97,110, +103,108,101,0,1,822295,0,112,116,121,112,101,0,1,822501,0, +112,116,121,112,101,48,0,1,823490,0,116,115,116,97,114,116, +0,1,823891,0,100,116,121,112,101,0,1,824078,0,107,119,95, +115,116,114,117,99,116,0,1,825031,0,107,119,95,116,121,112, +101,0,1,825091,0,107,119,95,116,121,112,101,99,104,101,99, +107,0,1,825198,4,116,102,105,110,100,102,105,101,108,100,0, +1,825322,0,116,121,112,101,97,114,103,0,1,825489,0,97,114, +114,97,114,103,0,1,825635,2,110,111,103,114,111,119,0,1, +825749,0,107,119,95,108,101,110,0,1,825994,0,107,119,95,99, +97,112,0,1,826129,0,107,119,95,112,117,115,104,0,1,826491, +0,107,119,95,112,111,112,0,1,826593,0,107,119,95,114,101, +115,105,122,101,0,1,826687,0,107,119,95,115,105,122,101,111, +102,0,1,826745,0,107,119,95,116,121,112,101,111,102,0,1, +826801,0,116,121,112,101,115,95,105,110,105,116,0,1,827665,2, +105,115,110,117,109,111,112,0,1,827758,6,98,105,110,116,121, +112,101,0,1,829151,2,116,107,105,110,100,0,1,829187,2,116, +115,105,122,101,0,1,829222,2,116,110,97,109,101,0,1,829258, +2,116,108,101,110,0,1,829294,2,116,110,102,105,101,108,100, +115,0,1,829336,2,104,97,115,112,116,114,0,1,829588,4,102, +97,116,116,101,110,0,1,829638,6,102,97,116,49,0,1,830312, +6,102,97,116,105,110,0,1,830751,4,116,102,110,97,109,101, +0,1,830807,4,116,102,116,121,112,101,0,1,830865,4,116,102, +111,102,102,0,1,830923,2,116,114,101,116,0,1,830959,2,116, +110,112,97,114,97,109,115,0,1,830995,4,116,112,97,114,97, +109,0,1,831042,4,116,112,97,114,97,109,116,97,103,0,1, +831099,2,116,114,101,116,117,0,1,831137,2,116,110,97,114,103, +115,0,1,831232,4,116,97,114,103,0,1,831282,2,115,105,103, +111,102,0,1,831593,2,115,105,103,111,102,115,0,1,831692,0, +114,118,97,108,0,1,831752,4,107,119,99,97,108,108,0,1, +831898,0,115,116,109,116,111,110,108,121,0,1,831976,8,97,114, +103,115,0,1,832321,0,110,101,119,116,121,112,101,100,0,1, +832798,2,99,97,108,108,0,1,833554,2,101,110,104,99,97,108, +108,0,1,833923,0,112,114,105,109,97,114,121,0,1,835388,4, +105,110,99,100,101,99,0,1,835812,2,102,105,101,108,100,0, +1,836488,0,112,111,115,116,102,105,120,0,1,838378,0,117,110, +97,114,121,0,1,839318,2,98,105,110,0,1,839831,0,115,116, +111,114,101,107,101,101,112,0,1,839879,2,116,101,114,110,97, +114,121,0,1,840529,0,101,120,112,114,0,1,841710,0,115,116, +109,116,115,116,97,114,116,0,1,841957,0,98,108,111,99,107, +101,120,112,114,0,1,842333,0,99,112,114,105,109,0,1,842741, +0,99,101,120,112,114,0,1,843098,0,114,101,115,121,110,99, +0,1,843225,0,115,116,109,116,0,1,843493,0,98,108,111,99, +107,0,1,843621,0,115,117,98,115,116,109,116,0,1,843701,2, +108,111,99,97,108,100,101,99,108,0,1,844485,0,107,119,95, +118,97,114,0,1,844521,0,107,119,95,105,102,0,1,845067,4, +108,111,111,112,95,112,117,115,104,0,1,845274,4,108,111,111, +112,95,101,110,100,0,1,845804,0,107,119,95,119,104,105,108, +101,0,1,846110,0,107,119,95,102,111,114,0,1,846662,0,107, +119,95,98,114,101,97,107,0,1,846875,0,107,119,95,99,111, +110,116,105,110,117,101,0,1,847162,4,99,97,115,101,98,111, +100,121,0,1,847395,0,107,119,95,115,119,105,116,99,104,0, +1,848047,0,107,119,95,114,101,116,117,114,110,0,1,848409,0, +107,119,95,111,51,0,1,848946,0,107,119,95,117,110,99,104, +101,99,107,101,100,0,1,849006,0,97,115,109,98,108,111,99, +107,0,1,850399,0,107,119,95,97,115,109,0,1,850441,1,103, +114,97,98,0,1,850781,3,113,110,101,119,0,1,850859,67,113, +100,117,112,0,1,850937,68,119,99,111,112,121,116,111,0,1, +851024,0,107,119,95,113,117,111,116,101,0,1,851161,3,116,111, +107,101,110,115,0,1,851401,130,102,101,101,100,0,1,851444,0, +114,101,97,100,115,114,99,0,1,851866,0,112,115,107,105,112, +97,110,103,108,101,0,1,851998,6,112,114,101,115,99,97,110, +95,102,110,0,1,852727,4,112,114,101,115,99,97,110,0,1, +853443,0,117,110,105,116,95,115,116,100,105,110,0,1,853512,0, +99,116,120,95,112,117,115,104,0,1,853900,0,99,116,120,95, +112,111,112,0,1,854252,4,102,110,95,98,101,103,105,110,0, +1,854418,0,112,114,111,108,111,103,117,101,0,1,854537,0,101, +112,105,108,111,103,117,101,0,1,854629,0,98,111,100,121,0, +1,854834,8,102,110,100,101,102,0,1,857117,2,103,100,101,99, +108,0,1,857156,6,103,100,101,99,108,50,0,1,857768,0,103, +99,111,110,115,116,0,1,857851,0,101,110,104,100,101,102,0, +1,859139,0,116,111,112,115,121,110,99,0,1,859620,0,103,101, +110,112,97,114,97,109,115,0,1,860089,0,114,101,116,116,121, +112,101,0,1,860136,4,102,110,100,111,110,101,0,1,860295,0, +116,111,112,105,116,101,109,0,1,861234,0,117,110,105,116,95, +116,111,112,0,1,861284,2,117,110,105,116,95,101,110,100,0, +1,861504,4,99,111,109,112,105,108,101,95,108,105,115,116,0, +1,861756,2,99,111,109,112,105,108,101,0,1,861792,2,108,111, +97,100,0,1,861830,2,101,118,97,108,0,1,861881,2,101,118, +97,108,115,0,1,861946,2,98,108,111,98,95,101,109,105,116, +0,1,862188,2,98,108,111,98,95,108,111,97,100,0,1,862390, +4,98,117,105,108,100,95,105,109,97,103,101,0,1,862811,0, +122,99,95,105,110,105,116,0,1,863625,0,122,99,95,116,97, +98,108,101,115,0,4,1048576,0,77,87,0,4,262144,0,72,69, +65,80,95,76,79,0,4,655360,0,72,69,65,80,95,72,73, +0,4,655360,0,90,70,80,0,4,786432,0,71,79,82,71,0, +4,917504,0,70,66,79,84,0,4,983040,0,70,84,79,80,0, +4,983040,0,82,83,48,0,4,1015808,0,77,65,82,75,48,0, +4,16384,0,77,65,82,75,78,0,4,1032192,0,68,83,76,79, +0,4,1048560,0,68,83,72,73,0,4,729683222,0,77,65,71,73, +67,0,6,0,201337106,105,100,105,118,0,6,1,201343762,105,109,111, +100,0,6,2,201350418,105,115,104,108,0,6,3,201356817,108,102,97, +0,6,4,201366802,111,117,116,115,105,100,101,0,3,787450,0,112, +111,119,50,0,2,787482,0,122,114,101,109,0,4,6,0,72, +68,82,0,2,788046,0,104,98,117,109,112,0,3,788047,0,102, +114,101,101,108,0,2,788079,0,103,99,95,114,117,110,115,0, +2,788080,0,103,99,95,102,114,101,101,100,0,2,788081,0,103, +99,95,97,117,116,111,0,2,789083,0,102,101,110,100,0,2, +791775,0,109,115,95,116,111,112,0,2,791776,0,109,115,95,111, +118,101,114,0,2,791777,0,103,99,95,108,111,0,2,791778,0, +103,99,95,104,105,0,4,8192,0,73,72,78,0,4,655616,0, +82,95,73,72,0,4,663808,0,82,95,80,79,79,76,0,4, +704512,0,82,95,80,79,79,76,69,78,68,0,4,4096,0,78, +83,0,4,704768,0,82,95,83,89,77,0,4,704768,0,115,110, +97,109,101,0,4,708864,0,115,107,105,110,100,0,4,712960,0, +115,118,97,108,0,4,717056,0,115,115,105,103,0,4,721152,0, +115,102,108,97,103,0,4,725248,0,115,102,105,120,0,4,729344, +0,115,110,101,120,116,0,4,512,0,78,76,0,4,733440,0, +108,110,97,109,101,0,4,733952,0,108,115,108,111,116,0,4, +734464,0,108,104,111,109,101,0,4,734976,0,108,115,116,97,116, +101,0,4,735488,0,108,111,119,110,101,114,0,4,736000,0,108, +117,115,101,100,0,4,736512,0,108,97,114,114,0,4,737024,0, +108,111,119,110,0,4,8192,0,78,70,0,4,737536,0,102,115, +105,116,101,0,4,745728,0,102,110,101,120,116,0,4,753920,0, +82,95,83,78,65,80,0,4,762112,0,82,95,81,66,85,70, +0,4,770304,0,82,95,83,82,67,0,4,771328,0,82,95,83, +69,81,0,4,773376,0,98,110,97,109,101,0,4,773440,0,98, +112,114,101,99,0,4,773504,0,98,115,101,113,0,4,773568,0, +98,108,101,110,0,4,773632,0,117,110,97,109,101,0,4,773664, +0,117,115,101,113,0,4,773696,0,117,108,101,110,0,4,773728, +0,97,110,97,109,101,0,4,773792,0,97,98,105,110,0,4, +773888,0,107,111,112,115,0,4,774400,0,82,95,67,84,88,0, +4,776448,0,82,95,76,79,79,80,0,4,777472,0,82,95,65, +83,77,0,4,0,0,84,95,69,79,70,0,4,1,0,84, +95,78,85,77,0,4,2,0,84,95,73,68,0,4,3,0, +84,95,83,84,82,0,4,4,0,84,95,79,80,0,4,1, +0,75,95,70,78,0,4,2,0,75,95,71,86,65,82,0, +4,3,0,75,95,71,65,82,82,0,4,4,0,75,95,67, +79,78,83,84,0,4,5,0,75,95,75,87,0,4,6,0, +75,95,69,78,72,0,4,7,0,75,95,84,89,80,69,0, +4,1,0,70,95,68,69,70,0,4,2,0,70,95,83,73, +71,0,4,4,0,70,95,85,78,84,89,80,69,68,0,4, +8,0,70,95,83,84,77,84,0,4,16,0,70,95,69,88, +80,82,0,4,32,0,70,95,84,79,80,0,4,64,0,70, +95,70,65,76,76,66,65,67,75,0,4,128,0,70,95,71, +69,78,0,4,256,0,70,95,80,82,69,0,4,512,0,70, +95,79,51,0,4,0,0,83,95,80,76,65,73,78,0,4, +1,0,83,95,77,0,4,2,0,83,95,85,0,4,3,0, +83,95,68,0,4,4,0,83,95,68,69,65,68,0,4,5, +0,83,95,67,72,73,76,68,0,4,6,0,83,95,66,79, +82,82,79,87,0,4,0,0,67,95,80,76,65,73,78,0, +4,1,0,67,95,70,82,69,83,72,0,4,2,0,67,95, +86,65,82,0,4,0,0,76,86,95,78,79,78,69,0,4, +1,0,76,86,95,76,79,67,65,76,0,4,2,0,76,86, +95,71,76,79,66,65,76,0,4,3,0,76,86,95,69,76, +69,77,0,4,4,0,76,86,95,79,84,72,69,82,0,4, +0,0,79,95,81,0,4,1,0,79,95,76,68,0,4,2, +0,79,95,83,84,0,4,3,0,79,95,80,73,67,75,0, +4,4,0,79,95,68,82,79,80,0,4,5,0,79,95,83, +87,65,80,0,4,6,0,79,95,67,65,76,76,0,4,7, +0,79,95,74,77,80,0,4,8,0,79,95,74,90,0,4, +9,0,79,95,82,69,84,0,4,13,0,79,95,65,68,68, +0,4,14,0,79,95,83,85,66,0,4,20,0,79,95,76, +84,0,4,22,0,88,79,80,48,0,4,-2,0,83,69,81, +82,69,70,0,2,792607,0,110,101,110,104,0,2,792608,0,112, +111,111,108,116,111,112,0,2,792609,0,110,115,121,109,0,2, +792610,0,117,98,97,115,101,0,2,792611,0,118,105,115,108,111, +0,2,792612,0,110,102,105,120,117,0,2,792613,0,102,105,120, +102,114,101,101,0,2,792614,0,110,98,105,110,0,2,792615,0, +110,117,110,0,2,792616,0,110,97,115,103,0,2,792617,0,110, +107,111,112,0,2,792618,0,115,101,113,116,111,112,0,2,792619, +0,109,110,101,109,0,2,792643,0,99,104,101,99,107,95,111, +110,0,2,792644,0,116,107,0,2,792645,0,116,118,0,2,792646, +0,116,103,108,117,101,0,2,792647,0,116,108,105,110,101,0, +2,792648,0,108,105,110,101,0,2,792649,0,99,104,0,2,792650, +0,99,104,107,105,110,100,0,2,792651,0,99,104,112,116,114, +0,2,792652,0,99,104,98,97,115,101,0,2,792653,0,113,100, +111,110,101,0,2,792654,0,105,115,112,0,3,792655,0,116,98, +117,102,0,2,793755,0,112,107,95,116,107,0,2,793756,0,112, +107,95,116,118,0,2,793757,0,112,113,95,110,0,3,793758,0, +112,113,95,116,107,0,3,793762,0,112,113,95,116,118,0,3, +793766,0,112,113,95,103,108,0,3,793770,0,112,113,95,108,110, +0,2,793774,0,104,101,114,101,0,2,793775,0,104,100,101,108, +116,97,0,2,793776,0,104,108,105,109,105,116,0,2,793777,0, +112,116,111,112,0,2,793778,0,110,108,111,99,0,2,793779,0, +108,98,97,115,101,0,2,793780,0,102,100,101,112,116,104,0, +2,793781,0,102,115,108,111,116,115,0,2,793782,0,102,99,104, +107,0,2,793783,0,102,114,101,116,0,2,793784,0,108,105,118, +101,0,2,793785,0,98,120,100,101,112,116,104,0,2,793786,0, +101,112,105,99,104,97,105,110,0,2,793787,0,102,102,114,97, +109,101,0,2,793788,0,105,110,102,110,0,2,793789,0,108,115, +112,0,2,793790,0,108,111,111,112,98,97,115,101,0,2,793791, +0,99,116,120,115,112,0,2,793792,0,115,110,97,112,116,111, +112,0,2,793793,0,108,118,0,2,793794,0,108,118,107,0,2, +793795,0,108,118,105,0,2,793796,0,107,119,95,101,120,112,114, +0,2,793797,0,120,99,108,115,0,2,793798,0,102,116,121,0, +2,793799,0,115,95,108,112,0,2,793800,0,115,95,114,112,0, +2,793801,0,115,95,108,98,0,2,793802,0,115,95,114,98,0, +2,793803,0,115,95,108,107,0,2,793804,0,115,95,114,107,0, +2,793805,0,115,95,99,111,109,109,97,0,2,793806,0,115,95, +115,101,109,105,0,2,793807,0,115,95,101,113,0,2,793808,0, +115,95,97,114,114,111,119,0,2,793809,0,115,95,105,110,99, +0,2,793810,0,115,95,100,101,99,0,2,793811,0,115,95,115, +116,97,114,0,2,793812,0,115,95,97,109,112,0,2,793813,0, +115,95,99,111,108,111,110,0,2,793814,0,115,95,104,97,115, +104,0,2,793815,0,115,95,109,105,110,117,115,0,2,793816,0, +115,95,97,110,100,97,110,100,0,2,793817,0,115,95,111,114, +111,114,0,2,793818,0,115,95,100,111,116,0,2,793819,0,115, +95,102,110,0,2,793820,0,115,95,118,97,114,0,2,793821,0, +115,95,99,111,110,115,116,0,2,793822,0,115,95,101,108,115, +101,0,2,793823,0,115,95,97,115,0,2,793824,0,115,95,117, +110,99,104,101,99,107,101,100,0,2,793825,0,115,95,105,109, +109,101,100,105,97,116,101,0,2,793826,0,115,95,109,0,2, +793827,0,115,95,100,0,2,793828,0,115,95,117,0,2,793829,0, +115,95,113,0,2,793830,0,115,95,99,97,115,101,0,2,793831, +0,115,95,100,101,102,97,117,108,116,0,2,793832,0,115,95, +102,111,118,102,0,2,793833,0,115,95,115,116,97,114,116,0, +2,793834,0,115,95,109,97,105,110,0,2,793835,0,115,95,102, +114,101,101,0,2,793836,0,115,95,111,119,110,0,2,793837,0, +115,95,113,110,101,119,0,2,793838,0,115,95,101,110,104,97, +110,99,101,0,2,793839,0,115,95,109,105,99,114,111,0,2, +793840,0,115,95,118,109,0,4,0,0,84,89,95,87,79,82, +68,0,4,1,0,84,89,95,73,78,84,0,4,2,0,84, +89,95,67,72,65,82,0,4,3,0,84,89,95,66,79,79, +76,0,4,4,0,84,89,95,86,79,73,68,0,4,5,0, +84,89,95,80,84,82,0,4,6,0,84,89,95,65,82,82, +0,4,7,0,84,89,95,70,78,0,4,8,0,84,89,95, +83,84,82,85,67,84,0,4,9,0,84,89,95,86,65,82, +0,4,10,0,84,89,95,71,69,78,0,4,11,0,84,89, +95,68,89,78,0,4,638976,0,82,95,84,89,0,4,655360,0, +82,95,84,89,69,78,68,0,4,1024,0,78,84,76,0,4, +778496,0,108,116,121,112,101,0,4,779008,0,108,116,97,103,0, +4,779520,0,115,116,121,112,101,0,2,793841,0,116,121,116,111, +112,0,2,793842,0,110,116,108,0,2,793843,0,116,119,111,114, +100,0,2,793844,0,116,105,110,116,0,2,793845,0,116,99,104, +97,114,0,2,793846,0,116,98,111,111,108,0,2,793847,0,116, +118,111,105,100,0,2,793848,0,116,99,95,111,110,0,2,793849, +0,120,116,121,0,2,793850,0,120,122,101,114,111,0,2,793851, +0,100,114,121,0,3,793852,0,116,112,110,0,2,793860,0,110, +116,112,0,2,793861,0,116,118,97,117,116,111,0,2,793862,0, +100,116,121,0,2,793863,0,100,116,97,103,0,2,793864,0,115, +95,108,116,0,2,793865,0,115,95,103,116,0,2,793866,0,115, +95,115,104,114,0,2,793867,0,115,95,108,101,0,2,793868,0, +115,95,103,101,0,2,793869,0,115,95,101,113,101,113,0,2, +793870,0,115,95,110,101,0,2,793871,0,115,95,112,108,117,115, +0,2,793872,0,115,95,115,116,114,117,99,116,0,2,793873,0, +115,95,116,121,112,101,0,2,793874,0,115,95,116,121,112,101, +99,104,101,99,107,0,2,793875,0,115,95,111,110,0,2,793876, +0,115,95,111,102,102,0,2,793877,0,115,95,110,101,119,0, +2,793878,0,115,95,110,101,119,95,116,0,2,793879,0,115,95, +111,51,0,2,793880,0,115,95,108,102,97,0,2,793881,0,115, +95,111,117,116,115,105,100,101,0,2,793882,0,115,95,110,101, +119,95,114,97,119,0,2,793883,0,115,95,111,111,98,0,2, +793884,0,111,51,111,110,0,2,793885,0,111,51,110,120,116,0, +2,793886,0,111,51,111,110,101,0,3,793887,0,111,51,115,108, +0,3,793919,0,111,51,114,103,0,2,793951,0,110,111,51,115, +108,0,3,793952,0,111,51,112,101,110,100,0,2,794652,0,110, +111,51,112,101,110,100,0,3,794653,0,111,51,100,115,99,0, +3,794733,0,111,51,102,114,101,101,0,2,794741,0,110,111,51, +102,114,101,101,0,2,794742,0,111,51,110,114,101,103,0,3, +794743,0,111,51,99,97,99,104,101,0,2,795255,0,97,114,103, +108,111,99,0,2,795256,0,115,95,104,105,116,101,109,115,0, +2,795257,0,115,95,104,108,101,110,0,2,795258,0,115,95,102, +97,116,116,101,110,0,3,795259,0,110,117,109,111,112,115,0, +2,795271,0,110,110,117,109,111,112,115,0,2,795272,0,115,95, +102,105,120,101,100,0,2,795273,0,115,95,100,108,101,110,0, +2,795274,0,115,95,100,99,97,112,0,2,795275,0,115,95,100, +110,101,119,0,2,795276,0,115,95,100,97,116,0,2,795277,0, +115,95,100,112,117,115,104,0,2,795278,0,115,95,100,112,111, +112,0,2,795279,0,115,95,100,114,101,115,105,122,101,0,2, +795280,0,120,99,111,110,115,116,0,2,795281,0,120,99,118,97, +108,0,3,795282,0,103,98,105,110,100,0,2,795290,0,110,103, +98,105,110,100,0,2,795291,0,110,101,114,114,0,2,795292,0, +112,97,110,105,99,0,4,783616,0,82,95,69,88,73,84,0, +4,786176,0,82,95,69,88,73,84,69,78,68,0,2,808942,0, +101,120,116,111,112,0,3,808943,0,106,112,115,0,2,809007,0, +110,106,112,115,0,0 +}; +#define GLEN 84118 diff --git a/docs/history/17-o3-boundaries/code/io_linux.c b/docs/history/17-o3-boundaries/code/io_linux.c new file mode 100644 index 0000000..7ea34e0 --- /dev/null +++ b/docs/history/17-o3-boundaries/code/io_linux.c @@ -0,0 +1,6 @@ +#include +int J; +static int pk(void){int c=getchar();ungetc(c,stdin);return c==EOF;} +char Q(void){int c=getchar();J=pk();return c;} +void G(char c){putchar(c);} +__attribute__((constructor)) static void init(void){J=pk();} diff --git a/docs/history/17-o3-boundaries/code/mkgenome.c b/docs/history/17-o3-boundaries/code/mkgenome.c new file mode 100644 index 0000000..8fb2cc5 --- /dev/null +++ b/docs/history/17-o3-boundaries/code/mkgenome.c @@ -0,0 +1,16 @@ +/* turns a genome image (little-endian 32-bit words on stdin, as written by + tools/build.zc) into genome.h */ +#include +#define GORG 0xC0000 +int main(void) +{ + unsigned char b[4]; int n = 0; + printf("#define GORG %d\nstatic const int genome[] = {", GORG); + while (fread(b, 1, 4, stdin) == 4) { + int w = (int)(b[0] | b[1] << 8 | b[2] << 16 | (unsigned)b[3] << 24); + printf("%s%d", !n ? "\n" : n % 16 ? "," : ",\n", w); + n++; + } + printf("\n};\n#define GLEN %d\n", n); + return n ? 0 : 1; +} diff --git a/docs/history/17-o3-boundaries/code/src/compiler.zc b/docs/history/17-o3-boundaries/code/src/compiler.zc new file mode 100644 index 0000000..1ac0c4b --- /dev/null +++ b/docs/history/17-o3-boundaries/code/src/compiler.zc @@ -0,0 +1,2980 @@ +// ============================================================================ +// compiler.zc: the resident zc compiler. it lives in the genome next to the +// runtime, compiles source straight to vm words in memory, and is visible to +// the programs it compiles: every function and global here can be called or +// patched by name, and parse words (immediate fns) run inside it. +// ============================================================================ + +// ---- compiler data region (fixed addresses, 0xA0100 .. 0xBE100) ---- +const IHN = 8192; +const R_IH = 0xA0100; // intern hash table +const R_POOL = 0xA2100; // interned strings: [symbol head] chars 0 +const R_POOLEND = 0xAC000; +const NS = 4096; // symbol table, one array per field +const R_SYM = 0xAC100; +const sname = 0xAC100, skind = 0xAD100, sval = 0xAE100, ssig = 0xAF100; +const sflag = 0xB0100, sfix = 0xB1100, snext = 0xB2100; +const NL = 512; // locals, one array per field +const lname = 0xB3100, lslot = 0xB3300, lhome = 0xB3500, lstate = 0xB3700; +const lowner = 0xB3900, lused = 0xB3B00, larr = 0xB3D00, lown = 0xB3F00; // lown: has owned an object +const NF = 8192; // patch nodes: a site and a link +const fsite = 0xB4100, fnext = 0xB6100; +const R_SNAP = 0xB8100; // analyzer snapshots +const R_QBUF = 0xBA100; // token buffer for quote and grab +const R_SRC = 0xBC100; // input source stack, 32 words per frame +const R_SEQ = 0xBC500; // operator code sequences +const bname = 0xBCD00, bprec = 0xBCD40, bseq = 0xBCD80, blen = 0xBCDC0; // binary operators +const uname = 0xBCE00, useq = 0xBCE20, ulen = 0xBCE40; // unary operators +const aname = 0xBCE60, abin = 0xBCEA0; // compound assignment +const kops = 0xBCF00; // every operator spelling the lexer knows +const R_CTX = 0xBD100; // saved compiler contexts for nested compiles +const R_LOOP = 0xBD900; // loop stack, 8 words per loop or switch +const R_ASM = 0xBDD00; // asm block labels + +// ---- token kinds, symbol kinds and flags ---- +const T_EOF = 0, T_NUM = 1, T_ID = 2, T_STR = 3, T_OP = 4; +const K_FN = 1, K_GVAR = 2, K_GARR = 3, K_CONST = 4, K_KW = 5, K_ENH = 6, K_TYPE = 7; +const F_DEF = 1, F_SIG = 2, F_UNTYPED = 4, F_STMT = 8, F_EXPR = 16, F_TOP = 32, F_FALLBACK = 64, F_GEN = 128, F_PRE = 256, F_O3 = 512; +// variable states for the lifetime analyzer +const S_PLAIN = 0, S_M = 1, S_U = 2, S_D = 3, S_DEAD = 4, S_CHILD = 5, S_BORROW = 6; +// expression classes: plain word, a new object, or a bare variable (C_VAR + index) +const C_PLAIN = 0, C_FRESH = 1, C_VAR = 2; +// lvalue kinds +const LV_NONE = 0, LV_LOCAL = 1, LV_GLOBAL = 2, LV_ELEM = 3, LV_OTHER = 4; +// opcodes +const O_Q = 0, O_LD = 1, O_ST = 2, O_PICK = 3, O_DROP = 4, O_SWAP = 5, O_CALL = 6, O_JMP = 7; +const O_JZ = 8, O_RET = 9, O_ADD = 13, O_SUB = 14, O_LT = 20; +const XOP0 = 22; // first enhancement opcode +const SEQREF = -2; // in an operator sequence: the next word names a symbol + +// ---- state ---- +let nenh, pooltop, nsym, ubase, vislo, nfixu, fixfree; +let nbin, nun, nasg, nkop, seqtop; +let mnem = "q@!$_~cjzr,?.+-*&|^><%"; +let check_on = 1; // set to 0 to switch the analyzer off for what follows +let tk, tv, tglue, tline, line = 1, ch, chkind, chptr, chbase, qdone, isp = -1; +let tbuf[1100]; +let pk_tk, pk_tv, pq_n, pq_tk[4], pq_tv[4], pq_gl[4], pq_ln[4]; // up to 4 tokens of lookahead +let here, hdelta, hlimit, ptop; +let nloc, lbase, fdepth, fslots, fchk, fret, live, bxdepth, epichain, fframe, infn; +let lsp, loopbase, ctxsp, snaptop; +let lv, lvk, lvi, kw_expr, xcls, fty; +// interned spellings the parser compares against +let s_lp, s_rp, s_lb, s_rb, s_lk, s_rk, s_comma, s_semi, s_eq, s_arrow, s_inc, s_dec; +let s_star, s_amp, s_colon, s_hash, s_minus, s_andand, s_oror; +let s_dot, s_fn, s_var, s_const, s_else, s_as, s_unchecked, s_immediate, s_m, s_d, s_u; +let s_q, s_case, s_default, s_fovf, s_start, s_main, s_free, s_own, s_qnew, s_enhance, s_micro, s_vm; + +// ---- types: kinds, tables and parser state (see the types section) ---- +const TY_WORD = 0, TY_INT = 1, TY_CHAR = 2, TY_BOOL = 3, TY_VOID = 4, TY_PTR = 5, TY_ARR = 6; +const TY_FN = 7, TY_STRUCT = 8, TY_VAR = 9, TY_GEN = 10, TY_DYN = 11; // dyn: []T, a = element, n = 1 if fixed +const R_TY = 0x9C000, R_TYEND = 0xA0000, NTL = 1024; // the first NTL words list every record +const ltype = 0xBE100, ltag = 0xBE300, stype = 0xBE500; // type per local, declared tag per local, type per symbol +let tytop, ntl, tword, tint, tchar, tbool, tvoid; +let tc_on; // the optional typechecker, off unless a program says typecheck on; +let xty, xzero, dry; // type of the last expression, was it a literal 0, and dry runs (typeof) +let tpn[8], ntp; // type parameters in scope +let tvauto; // reading a return type: unknown names are type parameters declared after the function's name +let dty, dtag; // result of dtype() +let s_lt, s_gt, s_shr, s_le, s_ge, s_eqeq, s_ne, s_plus, s_struct, s_type, s_typecheck, s_on, s_off, s_new, s_new_t; +let s_o3, s_lfa, s_outside, s_new_raw, s_oob; +let o3on, o3nxt, o3one, o3sl[32], o3rg[32], no3sl, o3pend[700], no3pend, o3dsc[80]; +let o3free[8], no3free, o3nreg, o3cache[512], argloc, s_hitems, s_hlen, s_fatten; // registers freed by closed scopes; local -> its fixed array's cached items (len next to it) // o3: in an o3 region; the promoted slots; o3 blocks waiting for the function's end +let numops[12], nnumops, s_fixed, s_dlen, s_dcap, s_dnew, s_dat, s_dpush, s_dpop, s_dresize, xconst, xcval; +let gbind[8], ngbind; // explicit type arguments for the next call: f(...) + +// ---- errors ---- +// the compiler reports up to 20 errors before it stops. a syntax error +// (die) skips the token it happened on and sets panic, which skips ahead to +// the next ; or } (or the next top-level item) and keeps quiet until then. +// other errors (serr) just report: the parse is still in step. +let nerr, panic; +fn errat() { puts("zc: line "); putn(tline); puts(": "); nerr++; return 0; } +fn errend() { putc(10); if (nerr >= 20) { puts("zc: too many errors, stopping\n"); halt(); } return 0; } +fn giveup() { puts("zc: "); putn(nerr); if (nerr == 1) puts(" error\n"); else puts(" errors\n"); halt(); return 0; } +fn fatal(msg) { errat(); puts(msg); putc(10); halt(); return 0; } +fn serr(msg) { if (!panic && !dry) { errat(); puts(msg); errend(); } return 0; } +fn serrn(name, msg) { if (!panic && !dry) { errat(); puts(name); puts(": "); puts(msg); errend(); } return 0; } +fn die(msg) { serr(msg); panic = 1; if (tk == T_EOF) giveup(); next(); return 0; } +fn dien(name, msg) { serrn(name, msg); panic = 1; if (tk == T_EOF) giveup(); next(); return 0; } +// analyzer errors only count in checked, reachable code +fn cerr(msg) { if (fchk && live) serr(msg); return 0; } +fn cerrn(i, msg) { if (fchk && live) serrn(lname[i], msg); return 0; } + +// ---- interning ---- +fn hashs(s) { let h = 0; while (*s) { h = h * 31 + *s; s++; } return h & (IHN - 1); } +fn intern(s) +{ + let h = hashs(s), p, n; + while (1) { + p = R_IH[h]; + if (!p) break; + if (streq(p, s)) return p; + h = (h + 1) & (IHN - 1); + } + n = strlen(s); + if (pooltop + n + 2 >= R_POOLEND) fatal("string pool full"); + pooltop[0] = 0; + p = pooltop + 1; + wcopy(p, s, n + 1); + pooltop = pooltop + n + 2; + R_IH[h] = p; + return p; +} + +// ---- symbols ---- +// each interned name keeps the index (+1) of its newest symbol in p[-1], and +// each symbol links to the one it shadows, so lookup is one step. +fn addsym(name, kind, val) +{ + let i = nsym; + if (nsym >= NS) fatal("symbol table full"); + nsym++; + sname[i] = name; skind[i] = kind; sval[i] = val; ssig[i] = 0; sflag[i] = F_DEF; sfix[i] = 0; stype[i] = 0; + snext[i] = name[-1]; + name[-1] = i + 1; + return i; +} +// while an image builds, names below vislo are hidden, except keywords +fn lookup(name) +{ + let i = name[-1] - 1; + while (i >= 0) { + if (i >= vislo || skind[i] == K_KW || skind[i] == K_TYPE) return i; + i = snext[i] - 1; + } + return -1; +} +fn popsyms(to) { while (nsym > to) { nsym--; sname[nsym][-1] = snext[nsym]; } return 0; } +fn lookups(s) { return lookup(intern(s)); } + +// patch nodes +fn fixnew(site, link) +{ + let i = fixfree; + if (i) fixfree = fnext[i]; + else { nfixu++; i = nfixu; if (i >= NF) fatal("too many forward references"); } + fsite[i] = site; fnext[i] = link; + return i; +} +// patch every lit in a chain to point at v, and recycle the nodes +fn fixall(head, v) +{ + let n; + while (head) { setlit(fsite[head], v); n = fnext[head]; fnext[head] = fixfree; fixfree = head; head = n; } + return 0; +} + +// ---- code emission ---- +fn emit(w) { if (dry) return 0; if (here >= hlimit) fatal("program too big"); *(here + hdelta) = w; here++; return 0; } +fn lit(v) { emit(v + v + 1); return 0; } +fn op(k) { emit(k + k); return 0; } +fn setlit(at, v) { if (dry) return 0; *(at + hdelta) = v + v + 1; return 0; } +fn jmp(t) { lit(t); op(O_JMP); return 0; } +// a lit that will be patched later; returns its address +fn hole() { let a = here; lit(MW - 1); return a; } +fn fwdfn(name) { let s = addsym(name, K_FN, tline); sflag[s] = 0; return s; } // the line of the first use, until it's defined +fn fnref(s) +{ + if (sflag[s] & F_DEF) { lit(sval[s]); return 0; } + if (dry) return 0; + sfix[s] = fixnew(here, sfix[s]); + lit(MW - 1); // a jump to MW-1 halts if it ever runs unpatched + return 0; +} +fn callname(name) { let s = lookup(name); if (s < 0) s = fwdfn(name); fnref(s); op(O_CALL); return 0; } +fn laddr(k) { lit(ZFP); op(O_LD); if (k) { lit(k); op(O_ADD); } return 0; } +// an o3 variable's address: lfa(k) is fp + k as one enhancement, which the +// jit specializes on the literal k +fn vaddr(i) +{ + let s; + if (ltag[i] & 16) { s = lookup(s_lfa); if (s >= 0 && skind[s] == K_ENH) { lit(lslot[i]); op(XOP0 + sval[s]); return 0; } } + return laddr(lslot[i]); +} +// registers for an o3 region's variables: the cpu has 7. a variable takes the +// one its region freed last (when a scope closed), or the next unused one +fn o3reset() { no3free = 0; o3nreg = 1; no3sl = 0; return 0; } +fn o3take() { if (no3free) { no3free--; return o3free[no3free]; } if (o3nreg < 8) { o3nreg++; return o3nreg - 1; } return 0; } +fn o3promote(i) +{ + let r; + ltag[i] = ltag[i] - (ltag[i] & 1792); + if (!o3on || larr[i] || no3sl >= 32) return 0; + r = o3take(); + if (!r) return 0; + ltag[i] = ltag[i] + r * 256; + o3sl[no3sl] = lslot[i]; o3rg[no3sl] = r; no3sl++; + return 0; +} +// a fixed []T can't change its items or its length, so o3 code keeps both in +// two hidden variables, loaded when the array variable is set (or when the +// region starts), and indexes through them +fn o3mkcache(i) +{ + let k; + if (!o3on || ltype[i][0] != TY_DYN || !ltype[i][3]) return 0; + k = newloc(s_hitems); fslots++; ltag[k] = 16; o3promote(k); + newloc(s_hlen); fslots++; ltag[k + 1] = 16; o3promote(k + 1); + o3cache[i] = k; + return 0; +} +fn o3refresh(i) +{ + let k = o3cache[i]; + if (!k) return 0; + vaddr(i); op(O_LD); lit(2); op(O_ADD); op(O_LD); vaddr(k); op(O_ST); + vaddr(i); op(O_LD); op(O_LD); vaddr(k + 1); op(O_ST); + return 0; +} +// hands a region to the cpu: install kind 2 is [-, 2, lo, hi, n, slot, register, ...] +fn o3region(lo, hi, sl, rg, n) +{ + let i = 0; + if (hdelta || lo >= hi) return 0; + o3dsc[0] = 0; o3dsc[1] = 2; o3dsc[2] = lo; o3dsc[3] = hi; o3dsc[4] = n; + while (i < n) { o3dsc[5 + i + i] = sl[i]; o3dsc[6 + i + i] = rg[i]; i++; } + install(o3dsc); + return 0; +} +// o3 blocks go to the cpu once their function is done, so every jump in them is final +fn o3flush() +{ + let i = 0, p; + while (i < no3pend) { p = o3pend + i * 67; o3region(p[0], p[1], p + 3, p + 35, p[2]); i++; } + no3pend = 0; + return 0; +} +fn strlit(s) +{ + let j = hole(), a; + op(O_JMP); + a = here; + while (*s) { emit(*s); s++; } + emit(0); + setlit(j, here); + lit(a); + return 0; +} + +// ---- input sources ---- +// frame: 0 kind (0 stdin, 1 token list, 2 string) 1 object 2 index 3 end +// 4 transparent 5..7 saved token 8..10 saved char state 11 owned 12 saved line +// 13 saved token line 14 saved lookahead count 15..30 saved lookahead 31 saved char buffer +fn nextch() +{ + if (chkind == 0) { + if (qdone || eof()) { ch = -1; return 0; } + ch = getc() & 255; + if (ch == 96) { qdone = 1; ch = -1; return 0; } // a backtick ends the source + } else if (chkind == 3) { + ch = (chbase[chptr >> 2] >> ((chptr & 3) * 8)) & 255; + if (ch) chptr++; else ch = -1; + } else { + ch = *chptr & 255; + if (ch) chptr++; else ch = -1; + } + if (ch == 10) line++; + return 0; +} +fn src_push(kind, obj, trans, owned) +{ + let f, i; + isp++; + if (isp >= 32) fatal("sources nested too deeply"); + f = R_SRC + isp * 32; + f[0] = kind; f[1] = obj; f[2] = 0; f[3] = 0; f[4] = trans; f[11] = owned; + f[5] = tk; f[6] = tv; f[7] = tglue; f[8] = ch; f[9] = chkind; f[10] = chptr; + f[12] = line; f[13] = tline; f[14] = pq_n; f[31] = chbase; + i = 0; while (i < pq_n) { f[15 + i * 4] = pq_tk[i]; f[16 + i * 4] = pq_tv[i]; f[17 + i * 4] = pq_gl[i]; f[18 + i * 4] = pq_ln[i]; i++; } + pq_n = 0; + if (kind == 1) f[3] = obj[0]; + if (kind == 0) { chkind = 0; qdone = 0; line = 1; nextch(); } + if (kind == 2) { chkind = 2; chptr = obj; line = 1; nextch(); } + if (kind == 3) { chkind = 3; chbase = obj; chptr = 0; line = 1; nextch(); } + return 0; +} +fn src_pop() +{ + let f = R_SRC + isp * 32, i = 0; + tk = f[5]; tv = f[6]; tglue = f[7]; ch = f[8]; chkind = f[9]; chptr = f[10]; + line = f[12]; tline = f[13]; pq_n = f[14]; chbase = f[31]; + while (i < pq_n) { pq_tk[i] = f[15 + i * 4]; pq_tv[i] = f[16 + i * 4]; pq_gl[i] = f[17 + i * 4]; pq_ln[i] = f[18 + i * 4]; i++; } + isp--; + if (f[11]) free(f[1]); + return 0; +} +fn isopch(c) +{ + let s = "+-*/%=<>!&|^~?:.@#$"; + while (*s) { if (*s == c) return 1; s++; } + return 0; +} +// is buf[0..n) followed by c the start of some operator the lexer knows? +fn opprefix(n, c) +{ + let i = 0, k, j; + while (i < nkop) { + k = kops[i]; + j = 0; + while (j < n && k[j] == tbuf[j]) j++; + if (j == n && k[n] == c) return 1; + i++; + } + return 0; +} +fn isdig(c) { return c >= '0' && c <= '9'; } +fn isal(c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'; } +fn hexv(c) +{ + if (isdig(c)) return c - '0'; + if (c >= 'a' && c <= 'f') return c - 'a' + 10; + if (c >= 'A' && c <= 'F') return c - 'A' + 10; + return -1; +} +fn escch() +{ + let c = ch; + nextch(); + if (c != 92) return c; + c = ch; + nextch(); + if (c == 'n') return 10; + if (c == 't') return 9; + if (c == 'r') return 13; + if (c == '0') return 0; + if (c == 'e') return 27; + return c; +} +fn oplex(n) +{ + while (ch >= 0 && isopch(ch) && opprefix(n, ch)) { tbuf[n] = ch; n++; nextch(); } + tbuf[n] = 0; + tk = T_OP; tv = intern(tbuf); + return 0; +} +fn lex() +{ + let n = 0, v, dg; + tglue = 1; + while (1) { + while (ch == 32 || ch == 9 || ch == 10 || ch == 13) { nextch(); tglue = 0; } + if (ch != '/') break; + nextch(); + if (ch == '/') { while (ch >= 0 && ch != 10) nextch(); tglue = 0; continue; } + if (ch == '*') { + nextch(); + while (ch >= 0) { if (ch == '*') { nextch(); if (ch == '/') break; } else nextch(); } + nextch(); tglue = 0; continue; + } + tline = line; tbuf[0] = '/'; oplex(1); return 0; + } + tline = line; + if (ch < 0) { tk = T_EOF; tv = 0; return 0; } + if (isdig(ch)) { + v = 0; + if (ch == '0') { + nextch(); + if (ch == 'x' || ch == 'X') { + nextch(); + while ((dg = hexv(ch)) >= 0) { v = v * 16 + dg; nextch(); } + tk = T_NUM; tv = v; return 0; + } + } + while (isdig(ch)) { v = v * 10 + ch - '0'; nextch(); } + tk = T_NUM; tv = v; return 0; + } + if (isal(ch)) { + while (isal(ch) || isdig(ch)) { if (n < 1000) { tbuf[n] = ch; n++; } nextch(); } + tbuf[n] = 0; + tk = T_ID; tv = intern(tbuf); return 0; + } + if (ch == 39) { nextch(); tv = escch(); if (ch != 39) die("bad character literal"); nextch(); tk = T_NUM; return 0; } + if (ch == 34) { + nextch(); + while (ch >= 0 && ch != 34) { v = escch(); if (n < 1000) { tbuf[n] = v; n++; } } + nextch(); + tbuf[n] = 0; + tk = T_STR; tv = intern(tbuf); return 0; + } + tbuf[0] = ch; + nextch(); + if (isopch(tbuf[0])) return oplex(1); + tbuf[1] = 0; + tk = T_OP; tv = intern(tbuf); + return 0; +} +// one token of lookahead: peek() fills pk_*, next() takes it first +fn next() +{ + let i = 1; + if (pq_n) { + tk = pq_tk[0]; tv = pq_tv[0]; tglue = pq_gl[0]; tline = pq_ln[0]; + while (i < pq_n) { pq_tk[i - 1] = pq_tk[i]; pq_tv[i - 1] = pq_tv[i]; pq_gl[i - 1] = pq_gl[i]; pq_ln[i - 1] = pq_ln[i]; i++; } + pq_n--; + return 0; + } + return next0(); +} +// the k-th token after the current one (k from 0 to 3), into pk_tk and pk_tv +fn peekat(k) +{ + let a, b, c, e; + while (pq_n <= k) { + a = tk; b = tv; c = tglue; e = tline; + next0(); + pq_tk[pq_n] = tk; pq_tv[pq_n] = tv; pq_gl[pq_n] = tglue; pq_ln[pq_n] = tline; pq_n++; + tk = a; tv = b; tglue = c; tline = e; + } + pk_tk = pq_tk[k]; pk_tv = pq_tv[k]; + return 0; +} +fn peek() { return peekat(0); } +fn next0() +{ + let f, q, i; + f = R_SRC + isp * 32; + if (f[0] != 1) { lex(); return 0; } + i = f[2]; + if (i < f[3]) { q = f[1]; tk = q[1 + i * 3]; tv = q[2 + i * 3]; tline = q[3 + i * 3]; tglue = 0; f[2] = i + 1; return 0; } + if (!f[4]) { tk = T_EOF; tv = 0; return 0; } + src_pop(); // a fed list ran out: continue with the token that followed it + return 0; +} +fn is_op(s) { return tk == T_OP && tv == s; } +fn is_id(s) { return tk == T_ID && tv == s; } +fn accept(s) { if ((tk == T_OP || tk == T_ID) && tv == s) { next(); return 1; } return 0; } +fn expect(s) { if (!accept(s)) { if (!panic && !dry) { errat(); puts("expected "); puts(s); errend(); } panic = 1; if (tk == T_EOF) giveup(); } return 0; } +fn name() +{ + let n = tv; + if (tk != T_ID) die("expected a name"); + next(); + return n; +} + +// ---- operator tables ---- +// a sequence is written like asm: one-char mnemonics, numbers, and names +// (resolved when the operator is used, so image builds stay self-contained) +fn seqasm(s) +{ + let start = seqtop, n, neg, v, k, c; + while (1) { + while (*s == 32) s++; + if (!*s) break; + n = 0; + while (s[n] && s[n] != 32) { tbuf[n] = s[n]; n++; } + tbuf[n] = 0; + s = s + n; + neg = tbuf[0] == '-' && isdig(tbuf[1]); + if (isdig(tbuf[neg])) { + v = 0; k = neg; + while (tbuf[k]) { v = v * 10 + tbuf[k] - '0'; k++; } + if (neg) v = 0 - v; + R_SEQ[seqtop] = v + v + 1; seqtop++; + } else if (n == 1 && (c = mnemidx(tbuf[0])) >= 0) { + R_SEQ[seqtop] = c + c; seqtop++; + } else { + R_SEQ[seqtop] = SEQREF; R_SEQ[seqtop + 1] = intern(tbuf); seqtop = seqtop + 2; + } + } + return start; +} +fn mnemidx(c) { let i = 0; while (mnem[i]) { if (mnem[i] == c) return i; i++; } return -1; } +fn emitseq(a, n) +{ + let e = a + n, s; + while (a < e) { + if (R_SEQ[a] == SEQREF) { + s = lookup(R_SEQ[a + 1]); + if (s < 0) s = fwdfn(R_SEQ[a + 1]); + if (skind[s] == K_FN) fnref(s); else if (skind[s] == K_ENH) op(XOP0 + sval[s]); else lit(sval[s]); + a = a + 2; + } else { emit(R_SEQ[a]); a++; } + } + return 0; +} +fn addkop(p) { let i = 0; while (i < nkop) { if (kops[i] == p) return 0; i++; } kops[nkop] = p; nkop++; return 0; } +fn binfind(p) { let i = nbin; while (i) { i--; if (bname[i] == p) return i; } return -1; } +fn unfind(p) { let i = nun; while (i) { i--; if (uname[i] == p) return i; } return -1; } +fn asgfind(p) { let i = nasg; while (i) { i--; if (aname[i] == p) return i; } return -1; } +// add or replace a binary operator. code is a sequence string, or 0 for the +// built-in && (prec 2) and || (prec 1) behaviour, chosen by -1 / -2 in prec. +fn add_binop(nm, prec, code) +{ + let p = intern(nm), i = binfind(p), a, n; + if (i < 0) { i = nbin; nbin++; } + bname[i] = p; bprec[i] = prec; + if (prec < 0) { bprec[i] = 0 - prec; blen[i] = prec; bseq[i] = 0; addkop(p); return i; } + bseq[i] = seqasm(code); blen[i] = seqtop - bseq[i]; + addkop(p); + // and its compound assignment, x op= y + n = strlen(nm); + wcopy(tbuf + 1000, nm, n); + tbuf[1000 + n] = '='; tbuf[1001 + n] = 0; + a = intern(tbuf + 1000); + if (prec >= 8 || prec == 5 || prec == 4 || prec == 3) { + if (asgfind(a) < 0) { aname[nasg] = a; nasg++; } + abin[asgfind(a)] = i; + addkop(a); + } + return i; +} +fn add_unop(nm, code) +{ + let p = intern(nm), i = unfind(p); + if (i < 0) { i = nun; nun++; } + uname[i] = p; useq[i] = seqasm(code); ulen[i] = seqtop - useq[i]; + addkop(p); + return i; +} +fn add_kw(nm, handler, flags) { let s = addsym(intern(nm), K_KW, handler); sflag[s] = F_DEF | flags; return s; } + +// ---- locals and the analyzer's view of them ---- +fn findloc(n) { let i = nloc; while (i > lbase) { i--; if (lname[i] == n) return i; } return -1; } +fn newloc(n) +{ + let i = nloc; + if (nloc >= NL) fatal("too many locals"); + nloc++; + lname[i] = n; lslot[i] = fslots; lhome[i] = fdepth; lstate[i] = S_PLAIN; + lowner[i] = 0; lused[i] = 0; larr[i] = 0; lown[i] = 0; ltype[i] = tword; ltag[i] = 0; o3cache[i] = 0; + return i; +} +fn lroot(i) { while (lstate[i] == S_CHILD) i = lowner[i]; return i; } +fn tracked(i) { return lstate[i] != S_PLAIN; } +// a use that doesn't give the object away +fn touch(i) +{ + let r; + lused[i] = 1; + if (!fchk) return 0; + r = lroot(i); + lused[r] = 1; + if (lstate[r] == S_DEAD) { cerrn(i, "used after it was given away"); return 0; } + // a d object that shows up in a deeper block is given to that block + if (lstate[r] == S_D && lhome[r] < fdepth) { lhome[r] = fdepth; lstate[r] = S_M; lown[r] = 1; } + return 0; +} +fn plainuse(c) +{ + if (c == C_FRESH) cerr("a new object can't be used as a plain value; bind it to a name first"); + if (c >= C_VAR) touch(c - C_VAR); + return C_PLAIN; +} +fn freevar(i) { laddr(lslot[i]); op(O_LD); callname(s_free); op(O_DROP); return 0; } +// leaving every block deeper than or equal to dep: free what is owned, and +// complain about what should have left already. final means the block really +// ends here rather than an early exit. +fn exit_vars(dep, final) +{ + let i = nloc, st; + if (!fchk || !live) return 0; + while (i > lbase) { + i--; + if (lhome[i] < dep) continue; + st = lstate[i]; + if (st == S_M) freevar(i); else if (st == S_U) cerrn(i, "u object must leave this block (block value, assignment to the enclosing block, or return)"); + else if (st == S_D) cerrn(i, "d object was never given away"); + } + return 0; +} +// an m object has to be used somewhere in the block that owns it, on any path +fn unused_check(from) +{ + while (from < nloc) { if (fchk && lown[from] && !lused[from]) serrn(lname[from], "m object is never used"); from++; } + return 0; +} +fn block_close(save) +{ + let i = save, k; + if (o3on) while (i < nloc) { k = (ltag[i] >> 8) & 7; if ((ltag[i] & 16) && k && no3free < 8) { o3free[no3free] = k; no3free++; } i++; } + i = lbase; + unused_check(save); + exit_vars(fdepth, 1); + while (i < save) { if (lhome[i] >= fdepth) { lstate[i] = S_DEAD; lhome[i] = fdepth - 1; } i++; } + nloc = save; + return 0; +} + +// snapshots: [lbase] [nloc] then state, owner, home per local +fn snap_push() +{ + let b = snaptop, i = lbase, p; + p = R_SNAP + b; + p[0] = lbase; p[1] = nloc; + p = p + 2; + while (i < nloc) { p[0] = lstate[i]; p[1] = lowner[i]; p[2] = lhome[i]; p = p + 3; i++; } + snaptop = p - R_SNAP; + if (snaptop >= 8000) fatal("nesting too deep for the analyzer"); + return b; +} +fn snap_pop(b) { snaptop = b; return 0; } +fn snap_restore(b) +{ + let p = R_SNAP + b, i = p[0], e = p[1]; + if (e > nloc) e = nloc; + p = p + 2; + while (i < e) { lstate[i] = p[0]; lowner[i] = p[1]; lhome[i] = p[2]; p = p + 3; i++; } + return 0; +} +fn empty(s) { return s == S_PLAIN || s == S_DEAD; } +// compare the current states with a snapshot: -1 if they agree, else the +// first local that differs. plain and dead agree and merge to dead. +fn snap_cmp(b) +{ + let p = R_SNAP + b, i = p[0], e = p[1], s; + if (e > nloc) e = nloc; + p = p + 2; + while (i < e) { + s = lstate[i]; + if (empty(s) && empty(p[0])) { if (p[0] == S_DEAD) lstate[i] = S_DEAD; } + else if (s != p[0] || lhome[i] != p[2] || (s == S_CHILD && lowner[i] != p[1])) return i; + p = p + 3; i++; + } + return -1; +} +fn statename(s) +{ + if (s == S_PLAIN) return "empty"; + if (s == S_M) return "m"; + if (s == S_U) return "u"; + if (s == S_D) return "d"; + if (s == S_DEAD) return "given away"; + if (s == S_CHILD) return "owned by a container"; + return "borrowed"; +} +fn merge_err(i, sa, what) +{ + if (!fchk || panic || dry) return 0; + errat(); puts(lname[i]); puts(": "); puts(what); + puts(" (one path leaves it "); puts(statename(sa)); puts(", another "); puts(statename(lstate[i])); puts(")"); + errend(); + return 0; +} +// after an if: the current state is the else path, sa is the then path + +// ---- joins ---- +// where control flow comes back together (after an if, a loop, a switch or +// ?:) every variable has to agree across the paths that reach the join. one +// exception: a variable that became d, or was given away, on some path is dead +// after the join, and the paths where it's still alive free it on the way in +// (fixups). u objects and borrows don't get this treatment: a u object still +// has to leave, and a borrow never changes. +const R_EXIT = 0xBF500, R_EXITEND = 0xBFF00; // exit records: the states at each break +let extop, jps[64], njps; // the paths being joined: [lbase, nloc, triples] +fn jst(p, i) { if (i >= p[1]) return S_PLAIN; return p[2 + 3 * (i - p[0])]; } +fn jow(p, i) { return p[3 + 3 * (i - p[0])]; } +fn jhm(p, i) { return p[4 + 3 * (i - p[0])]; } +// the states at a break, with the jump to patch: [site] then a snapshot +fn exit_rec(site) +{ + let p = R_EXIT + extop, i = lbase, q; + if (extop + 3 + 3 * (nloc - lbase) >= R_EXITEND - R_EXIT) fatal("too many breaks for the analyzer"); + p[0] = site; p[1] = lbase; p[2] = nloc; + q = p + 3; + while (i < nloc) { q[0] = lstate[i]; q[1] = lowner[i]; q[2] = lhome[i]; q = q + 3; i++; } + extop = q - R_EXIT; + return p; +} +fn jadd(p) { if (njps >= 64) fatal("too many paths for the analyzer"); jps[njps] = p; njps++; return 0; } +fn jsame(p, q, i) +{ + let s = jst(p, i), t = jst(q, i); + if (empty(s) && empty(t)) return 1; + return s == t && jhm(p, i) == jhm(q, i) && (s != S_CHILD || jow(p, i) == jow(q, i)); +} +// join the paths in jps over the locals [lbase, n0) into the current states +fn join(n0, what) +{ + let i = lbase, j, p, s, s0, dead, okdead, bad; + if (!njps) return 0; + while (i < n0) { + p = jps[0]; s0 = jst(p, i); + dead = 0; okdead = 1; bad = -1; + j = 0; + while (j < njps) { + s = jst(jps[j], i); + if (s == S_DEAD || s == S_D) dead = 1; + if (s == S_U || s == S_BORROW) okdead = 0; + if (!jsame(p, jps[j], i)) bad = j; + j++; + } + lstate[i] = s0; lowner[i] = jow(p, i); lhome[i] = jhm(p, i); + if (bad < 0) { + if (empty(s0)) { j = 0; while (j < njps) { if (jst(jps[j], i) == S_DEAD) lstate[i] = S_DEAD; j++; } } + } else if (dead && okdead) lstate[i] = S_DEAD; + else if (fchk && !panic && !dry) { + errat(); puts(lname[i]); puts(": "); puts(what); + puts(" (one path leaves it "); puts(statename(s0)); puts(", another "); puts(statename(jst(jps[bad], i))); puts(")"); + errend(); + } + i++; + } + return 0; +} +// on path p: count (and with doit, emit) the frees for what the join made dead but p still owns +fn fixups(p, n0, doit) +{ + let i = lbase, n = 0, s; + if (!fchk) return 0; + while (i < n0) { + s = jst(p, i); + if (lstate[i] == S_DEAD && (s == S_M || s == S_D)) { n++; if (doit) freevar(i); } + i++; + } + return n; +} +// a jump back to the top of a loop: whatever the loop owns has to be as it was +fn backedge(s0) +{ + let p = R_SNAP + s0, i = p[0], e = p[1], s; + if (!fchk || !live) return 0; + if (e > nloc) e = nloc; + while (i < e) { + s = lstate[i]; + if (empty(s) && empty(jst(p, i))) { if (jst(p, i) == S_DEAD) lstate[i] = S_DEAD; } + else if (s != jst(p, i) || lhome[i] != jhm(p, i) || (s == S_CHILD && lowner[i] != jow(p, i))) { + if (s == S_DEAD || s == S_D) cerrn(i, "is given away inside a loop that comes back around to it; give it away on a path that leaves the loop"); + else if (!panic && !dry) { + errat(); puts(lname[i]); puts(": changes state inside a loop; each pass has to leave it as it found it (it went from "); + puts(statename(jst(p, i))); puts(" to "); puts(statename(s)); puts(")"); errend(); + } + } + i++; + } + return 0; +} +// joins two paths that end at the same point, like the branches of if/else: +// path a jumped (through the hole at ja) and path b falls through to here +fn join2(sa, livea, ja, livebv, n0, what) +{ + let sb, nt, cj; + sb = snap_push(); + njps = 0; if (livea) jadd(R_SNAP + sa); if (livebv) jadd(R_SNAP + sb); + join(n0, what); + if (livebv) fixups(R_SNAP + sb, n0, 1); + nt = 0; if (livea) nt = fixups(R_SNAP + sa, n0, 0); + if (nt) { cj = hole(); op(O_JMP); setlit(ja, here); fixups(R_SNAP + sa, n0, 1); setlit(cj, here); } + else setlit(ja, here); + live = livea || livebv; + return 0; +} + +// ---- analyzer rules for the places values flow ---- +// recast: x as u / x as d / x as m +fn recast(c, t) +{ + let i, st; + if (!fchk) return c; + if (c < C_VAR) { cerr("only a named owned object can be recast"); return c; } + i = c - C_VAR; + st = lstate[i]; + lused[i] = 1; + if (st == S_CHILD) cerrn(i, "belongs to a container; recast the container instead"); + else if (st == S_BORROW) cerrn(i, "is borrowed and can't be recast"); + else if (st == S_PLAIN) cerrn(i, "isn't an owned object"); + else if (st == S_DEAD) cerrn(i, "used after it was given away"); + else if (t == S_M && st != S_M) cerrn(i, "can't be recast back to m"); + else lstate[i] = t; + return c; +} +// an argument for a parameter tagged t (0 plain, 1 m, 2 d) +fn argcheck(c, t) +{ + let i, st; + if (!fchk) return 0; + if (c == C_FRESH) { if (t != 2) cerr("a new object can only go straight to a d parameter; bind it to a name first"); return 0; } + if (c < C_VAR || !tracked(c - C_VAR)) return 0; + i = c - C_VAR; + st = lstate[i]; + if (lstate[lroot(i)] == S_DEAD) { cerrn(i, "used after it was given away"); return 0; } + if (t <= 1) { // untagged parameters are m: a lend + if (st == S_D) { cerrn(i, "is d; pass it to a d parameter (lending it would lose it)"); return 0; } + touch(i); + return 0; + } + if (st == S_D) { lstate[i] = S_DEAD; lused[i] = 1; return 0; } + if (st == S_M) cerrn(i, "is m; write d*x to give it away"); + else if (st == S_U) cerrn(i, "is u; write d*x to give it away instead of returning it"); + else if (st == S_CHILD) cerrn(i, "belongs to a container and can't be given away alone"); + else cerrn(i, "is borrowed and can't be given away"); + return 0; +} +fn emitown(p) +{ + laddr(lslot[p]); op(O_LD); + if (ltype[p][0] == TY_DYN) { lit(2); op(O_ADD); op(O_LD); } // a []T's elements live in its items object + callname(s_own); + if (o3on && hasptr(xty)) { lit(xty); callname(s_fatten); } + return 0; +} +// a store of class rc into an lvalue of kind k (local or container index i) +fn assigncheck(k, i, rc) +{ + let st, j, r, js; + if (!fchk) return C_PLAIN; + j = -1; + if (rc >= C_VAR && tracked(rc - C_VAR)) j = rc - C_VAR; + if (j >= 0 && lstate[lroot(j)] == S_DEAD) { cerrn(j, "used after it was given away"); return C_PLAIN; } + if (k == LV_LOCAL) { + st = lstate[i]; + if (j < 0 && rc != C_FRESH) { + if (!empty(st)) cerrn(i, "still owns an object; overwriting it would leak it"); + else lstate[i] = S_PLAIN; + return C_PLAIN; + } + if (!empty(st)) { cerrn(i, "still owns an object; overwriting it would leak it"); return C_PLAIN; } + if (rc == C_FRESH) { lstate[i] = S_M; lused[i] = 0; lown[i] = 1; if (ltag[i] & 15) lstate[i] = ltag[i] & 15; return C_PLAIN; } + js = lstate[j]; + if (js == S_U && lhome[i] == lhome[j] - 1) { // handing a u object up one block + lstate[i] = S_M; lused[i] = 0; lown[i] = 1; lstate[j] = S_DEAD; lused[j] = 1; if (ltag[i] & 15) lstate[i] = ltag[i] & 15; + return C_PLAIN; + } + if (js == S_U) cerrn(j, "is u; it can only be handed to a variable of the enclosing block, or returned"); + else cerrn(j, "is an owned object and can't be copied into another variable"); + return C_PLAIN; + } + if (k == LV_ELEM) { + r = lroot(i); + st = lstate[r]; + touch(i); + if (rc == C_FRESH) { emitown(i); return C_PLAIN; } + if (j < 0) return C_PLAIN; + js = lstate[j]; + if (js == S_CHILD) cerrn(j, "already belongs to a container"); + else if (js == S_BORROW) cerrn(j, "is borrowed and can't be stored"); + else if (st == S_BORROW) { + if (js == S_U) cerrn(j, "is u; it has to leave through the block above, not into a borrowed object"); + else { lstate[j] = S_DEAD; lused[j] = 1; emitown(i); } // storing it gives it away + } else if (js != st) cerrn(j, "its tag doesn't match its container's (both have to be m, u or d)"); + else if (lhome[j] < lhome[r]) cerrn(j, "lives in an outer block than the container it's stored into"); + else { lstate[j] = S_CHILD; lowner[j] = i; lused[j] = 1; emitown(i); } + return C_PLAIN; + } + // a global, or memory the analyzer doesn't track: the collector takes it + if (j >= 0) { + if (lstate[j] == S_D) { lstate[j] = S_DEAD; lused[j] = 1; } + else cerrn(j, "must be d to be stored in a global or in untracked memory (the collector takes it over)"); + } + return C_PLAIN; +} +fn retcheck(c) +{ + let i; + if (!fchk) return 0; + i = -1; + if (c >= C_VAR && tracked(c - C_VAR)) i = c - C_VAR; + if (i >= 0 && lstate[lroot(i)] == S_DEAD) { cerrn(i, "used after it was given away"); return 0; } + if (fret) { + if (i >= 0) { + if (lstate[i] == S_U) { lstate[i] = S_DEAD; lused[i] = 1; } + else cerrn(i, "has to be u to be returned; write 'return u*x'"); + } + return 0; + } + if (c == C_FRESH) cerr("returns a new object; give the function a .u return type"); + else if (i >= 0) cerrn(i, "is owned and can't be returned from a function whose return type isn't .u"); + return 0; +} +// the last expression of a block expression +fn handup(c) +{ + let i; + if (!fchk) return C_PLAIN; + if (c == C_FRESH) return C_FRESH; + if (c < C_VAR || !tracked(c - C_VAR)) return C_PLAIN; + i = c - C_VAR; + if (lstate[i] == S_U && lhome[i] == fdepth) { lstate[i] = S_DEAD; lused[i] = 1; return C_FRESH; } + if (lstate[i] == S_U) cerrn(i, "only a u object of this block can be its value"); + else cerrn(i, "has to be u to be handed up as the block's value"); + return C_PLAIN; +} +fn discard(c) +{ + if (c == C_FRESH) cerr("a new object (or a u result) can't be thrown away"); + else if (c >= C_VAR) touch(c - C_VAR); + return 0; +} + +// ---- types ---- +// types are a compiler primitive: records in the type area, hash-consed so +// the same type is always the same pointer and comparing types is comparing +// words. untyped code is typed word, which goes with anything. the records +// stay in memory after compiling, so programs can inspect them. +// t[0] kind t[1] size in words t[2] name t[3] n t[4] a t[5] b t[6] c t[7..] extra +// ptr: a = element arr: a = element, n = length +// fn: a = return type, n = parameter count, b = signature (u m d tags), extra = parameter types +// struct: n = field count (-1 until laid out), a = fields (name, type, offset), +// b = generic template, c = type arguments [n, t1, t2 ...] +// var: a type parameter, n = its index +// gen: a generic struct: n = parameter count, a = fields (name, type), b = field count, c = parameter names + + +fn talloc(n) { let p = tytop; if (tytop + n >= R_TYEND) fatal("the type area is full"); tytop = tytop + n; wfill(p, 0, n); return p; } +fn tnew(kind, size, nm, extra) +{ + let t = talloc(7 + extra); + t[0] = kind; t[1] = size; t[2] = nm; + if (ntl >= NTL) fatal("too many types"); + R_TY[ntl] = t; ntl++; + return t; +} +fn tptr(e) +{ + let i = 0, t; + while (i < ntl) { t = R_TY[i]; if (t[0] == TY_PTR && t[4] == e) return t; i++; } + t = tnew(TY_PTR, 1, 0, 0); t[4] = e; + return t; +} +fn tarr(e, n) +{ + let i = 0, t; + while (i < ntl) { t = R_TY[i]; if (t[0] == TY_ARR && t[4] == e && t[3] == n) return t; i++; } + t = tnew(TY_ARR, n * tsz(e), 0, 0); t[4] = e; t[3] = n; + return t; +} +fn tfn(ret, np, ps, sig) +{ + let i = 0, j, t; + while (i < ntl) { + t = R_TY[i]; + if (t[0] == TY_FN && t[4] == ret && t[3] == np && t[5] == sig) { + j = 0; while (j < np && t[7 + j] == ps[j]) j++; + if (j == np) return t; + } + i++; + } + t = tnew(TY_FN, 1, 0, np); t[4] = ret; t[3] = np; t[5] = sig; + wcopy(t + 7, ps, np); + return t; +} +// []T, or fixed []T +fn tdyn(e, f) +{ + let i = 0, t; + while (i < ntl) { t = R_TY[i]; if (t[0] == TY_DYN && t[4] == e && t[3] == f) return t; i++; } + t = tnew(TY_DYN, 1, 0, 0); t[4] = e; t[3] = f; + return t; +} +fn tvar(i, nm) +{ + let k = 0, t; + while (k < ntl) { t = R_TY[k]; if (t[0] == TY_VAR && t[3] == i) return t; k++; } + t = tnew(TY_VAR, 1, nm, 0); t[3] = i; + return t; +} +fn tinst(g, args, n) +{ + let i = 0, j, t, v; + while (i < ntl) { + t = R_TY[i]; + if (t[0] == TY_STRUCT && t[5] == g) { + v = t[6]; j = 0; while (j < n && v[1 + j] == args[j]) j++; + if (j == n) return t; + } + i++; + } + v = talloc(n + 1); v[0] = n; wcopy(v + 1, args, n); + t = tnew(TY_STRUCT, 0, g[2], 0); t[3] = -1; t[5] = g; t[6] = v; + return t; +} +// lay out a generic instance on first use +fn tresolve(t) +{ + let g, f, n, i, fs, off = 0, ft, bs[8]; + if (t[0] != TY_STRUCT || t[3] >= 0) return t; + if (t[3] == -2) { serr("a struct can't contain itself by value"); t[3] = 0; return t; } + t[3] = -2; + g = t[5]; n = g[5]; f = g[4]; + wfill(bs, 0, 8); wcopy(bs, t[6] + 1, t[6][0]); + fs = talloc(n * 3 + 1); + i = 0; + while (i < n) { + ft = subst(f[i + i + 1], bs); + fs[i * 3] = f[i + i]; fs[i * 3 + 1] = ft; fs[i * 3 + 2] = off; + off = off + tsz(ft); + i++; + } + t[4] = fs; t[1] = off; t[3] = n; + return t; +} +fn tsz(t) { if (t[0] == TY_STRUCT && t[3] < 0) tresolve(t); return t[1]; } +// replace type parameters with what they were bound to (unbound ones become word) +fn subst(t, bs) +{ + let k = t[0], i, n, v[24], vec; + if (k == TY_VAR) { if (bs[t[3]]) return bs[t[3]]; return tword; } + if (k == TY_PTR) return tptr(subst(t[4], bs)); + if (k == TY_ARR) return tarr(subst(t[4], bs), t[3]); + if (k == TY_FN) { n = t[3]; i = 0; while (i < n) { v[i] = subst(t[7 + i], bs); i++; } return tfn(subst(t[4], bs), n, v, t[5]); } + if (k == TY_STRUCT && t[5]) { vec = t[6]; n = vec[0]; i = 0; while (i < n) { v[i] = subst(vec[1 + i], bs); i++; } return tinst(t[5], v, n); } + return t; +} +fn isnum(t) { return t[0] == TY_INT || t[0] == TY_CHAR || t[0] == TY_BOOL; } +fn isagg(t) { return t[0] == TY_ARR || t[0] == TY_STRUCT; } +fn isptr(t) { return t[0] == TY_PTR || t[0] == TY_ARR; } +fn decay(t) { if (t[0] == TY_ARR) return tptr(t[4]); return t; } +fn telem(t) { if (t[0] == TY_PTR || t[0] == TY_ARR) return t[4]; return tword; } +// can a value of type src go where dst is expected? +fn compat(dst, src) +{ + if (dst == src || dst == tword || src == tword) return 1; + if (isnum(dst) && isnum(src)) return 1; + src = decay(src); + if (dst[0] == TY_PTR && src[0] == TY_PTR) { + if (dst[4] == tword || src[4] == tword || dst[4] == tvoid || src[4] == tvoid) return 1; + return dst[4] == src[4]; + } + if ((dst[0] == TY_PTR || dst[0] == TY_FN || dst[0] == TY_DYN) && xzero) return 1; // a literal 0 is a null pointer + if (dst[0] == TY_DYN && src[0] == TY_DYN && dst[4] == src[4]) return dst[3] || !src[3]; // a growable array can't hold a fixed one + return dst == src; +} +fn unify(p, a, bs) +{ + let i, n, pv, av; + if (p[0] == TY_VAR) { i = p[3]; if (!bs[i]) { bs[i] = a; return 1; } return compat(bs[i], a); } + a = decay(a); + if (p[0] == TY_PTR && a[0] == TY_PTR) { if (a[4] == tword) return 1; return unify(p[4], a[4], bs); } + if (p[0] == TY_STRUCT && p[5] && a[0] == TY_STRUCT && a[5] == p[5]) { + pv = p[6]; av = a[6]; n = pv[0]; i = 0; + while (i < n) { if (!unify(pv[1 + i], av[1 + i], bs)) return 0; i++; } + return 1; + } + return compat(p, a); +} +fn tprint(t) +{ + let k = t[0], i, v; + if (k == TY_PTR) { putc('*'); return tprint(t[4]); } + if (k == TY_DYN) { if (t[3]) puts("fixed "); puts("[]"); return tprint(t[4]); } + if (k == TY_ARR) { putc('['); putn(t[3]); putc(']'); return tprint(t[4]); } + if (k == TY_FN) { + if (t[4] == tword) puts("fn"); else tprint(t[4]); + if (t[5] & 1) puts(".u"); + putc('('); + i = 0; + while (i < t[3]) { + if (i) puts(", "); + tprint(t[7 + i]); + if (i < 12 && tparamtag(t, i) == 1) puts(".m"); + if (i < 12 && tparamtag(t, i) == 2) puts(".d"); + i++; + } + putc(')'); + return 0; + } + if (t[2]) puts(t[2]); else puts("?"); + if (k == TY_STRUCT && t[5]) { + v = t[6]; putc('<'); + i = 0; while (i < v[0]) { if (i) puts(", "); tprint(v[1 + i]); i++; } + putc('>'); + } + return 0; +} +fn tcerr(what, want, got) +{ + if (!tc_on || !live || dry || panic) return 0; + errat(); puts("type error: "); puts(what); + puts(": expected "); tprint(want); puts(", got "); tprint(got); + errend(); + return 0; +} +fn tcneed(ok, what, got) +{ + if (ok || !tc_on || !live || dry || panic) return 0; + errat(); puts("type error: "); puts(what); puts(", got "); tprint(got); + errend(); + return 0; +} +fn tpfind(n) { let i = ntp; while (i) { i--; if (tpn[i] == n) return i; } return -1; } +fn istypename(n) +{ + let s; + if (n == s_fixed) return 1; + if (tpfind(n) >= 0) return 1; + if (findloc(n) >= 0) return 0; + s = lookup(n); + return s >= 0 && skind[s] == K_TYPE; +} +// the closing > of a type argument list; splits >> in two +fn closeangle() +{ + if (is_op(s_shr)) { tv = s_gt; return 0; } + expect(s_gt); + return 0; +} +// a type, then any number of (params) after it: int(int, int) is a function +// returning int, fn(int) one returning a plain word +fn ptype() +{ + let t = ptype0(), np, ps[24]; + while (is_op(s_lp)) { + next(); np = 0; + if (!is_op(s_rp)) { while (1) { if (np >= 24) fatal("too many parameters"); ps[np] = ptype(); np++; if (!accept(s_comma)) break; } } + expect(s_rp); + t = tfn(t, np, ps, np + np); + } + return t; +} +fn ptype0() +{ + let n, s, t, args[8], na = 0, i; + if (accept(s_star)) return tptr(ptype0()); + if (accept(s_lk)) { + if (accept(s_rk)) return tdyn(ptype0(), 0); // []T: grows + n = cexpr(); expect(s_rk); t = ptype0(); if (n < 0) serr("a negative array length"); return tarr(t, n); + } + if (is_id(s_fixed)) { // fixed []T can't grow; fixed [n]T says what [n]T already means + next(); t = ptype0(); + if (t[0] == TY_DYN) return tdyn(t[4], 1); + if (t[0] != TY_ARR) serr("only arrays can be fixed"); + return t; + } + if (is_id(s_fn)) { next(); if (!is_op(s_lp)) die("fn as a type needs its parameters: fn(int)"); return tword; } + if (tk != T_ID) { die("expected a type"); return tword; } + n = tv; + i = tpfind(n); + if (i >= 0) { next(); return tvar(i, n); } + s = lookup(n); + if (tvauto && (s < 0 || skind[s] != K_TYPE) && ntp < 8) { tpn[ntp] = n; ntp++; next(); return tvar(ntp - 1, n); } // T in T first(...) + if (s < 0 || skind[s] != K_TYPE) { dien(n, "isn't a type"); return tword; } + next(); + t = sval[s]; + if (t[0] != TY_GEN) return t; + expect(s_lt); + while (1) { + if (na >= 8) die("too many type arguments"); + args[na] = ptype(); + if (tsz(args[na]) != 1) serr("type arguments have to be one word (a pointer, a number, or a parameter)"); + na++; + if (!accept(s_comma)) break; + } + closeangle(); + if (na != t[3]) serrn(n, "wrong number of type arguments"); + return tinst(t, args, na); +} +// does a declaration start here? (a type, or a bare .tag) +fn tstart() +{ + let k; + if (tk == T_OP) { + if (tv == s_lk || tv == s_dot) return 1; + if (tv == s_star) { // *T name, **T name ...: look past the stars + k = 0; + while (k < 3) { peekat(k); if (!(pk_tk == T_OP && pk_tv == s_star)) break; k++; } + peekat(k); + return (pk_tk == T_ID && (istypename(pk_tv) || pk_tv == s_fn)) || (pk_tk == T_OP && pk_tv == s_lk); + } + return 0; + } + if (tk != T_ID) return 0; + return tv == s_fn || istypename(tv); +} +// [type] [.tag] +fn dtype() +{ + dty = 0; dtag = 0; + if (!is_op(s_dot)) dty = ptype(); + if (accept(s_dot)) { + if (tv == s_m) dtag = S_M; else if (tv == s_u) dtag = S_U; else if (tv == s_d) dtag = S_D; else die("a tag is .u, .m or .d"); + next(); + } + return 0; +} +// struct name { type field, ... } or struct name { ... } +fn kw_struct() +{ + let n, t, s, nf = 0, buf = R_QBUF, i, fs, off = 0, gen = 0, ft; + next(); + n = name(); + if (is_op(s_lt)) { // generic + next(); ntp = 0; + while (1) { if (ntp >= 8) die("too many type parameters"); tpn[ntp] = name(); ntp++; if (!accept(s_comma)) break; } + closeangle(); + gen = 1; + t = tnew(TY_GEN, 0, n, 0); t[3] = ntp; + fs = talloc(ntp); wcopy(fs, tpn, ntp); t[6] = fs; + } else { t = tnew(TY_STRUCT, 0, n, 0); t[3] = -2; } // -2 while its fields are read + s = addsym(n, K_TYPE, t); + expect(s_lb); + while (!is_op(s_rb)) { + ft = ptype(); + while (1) { + if (nf >= 1000) fatal("too many fields"); + buf[nf + nf] = name(); buf[nf + nf + 1] = ft; nf++; + if (!accept(s_comma)) break; + } + expect(s_semi); + } + next(); + if (gen) { + fs = talloc(nf + nf); wcopy(fs, buf, nf + nf); + t[4] = fs; t[5] = nf; + ntp = 0; + } else { + fs = talloc(nf * 3 + 1); + i = 0; + while (i < nf) { + ft = buf[i + i + 1]; + if (ft == t) { serr("a struct can't contain itself by value; use a pointer"); ft = tword; } + fs[i * 3] = buf[i + i]; fs[i * 3 + 1] = ft; fs[i * 3 + 2] = off; + off = off + tsz(ft); + i++; + } + t[4] = fs; t[1] = off; t[3] = nf; + } + accept(s_semi); + return s; +} +// type name = T; +fn kw_type() { let n; next(); n = name(); expect(s_eq); addsym(n, K_TYPE, ptype()); expect(s_semi); return 0; } +// typecheck on; / typecheck off; +fn kw_typecheck() +{ + next(); + if (is_id(s_on)) tc_on = 1; else if (is_id(s_off)) tc_on = 0; else die("typecheck on or typecheck off"); + next(); expect(s_semi); + return 0; +} +// the field of a struct type, or -1 +fn tfindfield(t, n) { let i = 0; tresolve(t); while (i < t[3]) { if (t[4][i * 3] == n) return i; i++; } return -1; } +// sizeof(T or expr), typeof(T or expr): the expression form is compiled dry, so it never runs +fn typearg() +{ + let t, sb, c; + expect(s_lp); + if (tstart() && !is_op(s_dot)) t = ptype(); + else { + sb = snap_push(); dry++; + c = expr(); rval(); + t = xty; + dry--; snap_restore(sb); snap_pop(sb); + } + expect(s_rp); + return t; +} +// len(a), cap(a), push(a, x), pop(a), resize(a, n): growable arrays. +// len of a [n]T is just n. +fn arrarg() +{ + let c; + expect(s_lp); c = expr(); + argloc = -1; + if (o3on && lv && lvk == LV_LOCAL && o3cache[lvi]) argloc = lvi; + rval(); plainuse(c); + return xty; +} +fn nogrow(t) { if (t[0] == TY_DYN && t[3]) serr("a fixed array can't grow or shrink"); return 0; } +fn kw_len() +{ + let t; + next(); t = arrarg(); expect(s_rp); + if (t[0] == TY_ARR) { op(O_DROP); lit(t[3]); } + else { tcneed(t[0] == TY_DYN || t == tword, "len needs an array", t); if (argloc >= 0) { op(O_DROP); vaddr(o3cache[argloc] + 1); op(O_LD); } else if (o3on && t[0] == TY_DYN) op(O_LD); else callname(s_dlen); } + xty = tint; + return 0; +} +fn kw_cap() { let t; next(); t = arrarg(); expect(s_rp); tcneed(t[0] == TY_DYN || t == tword, "cap needs a []T", t); callname(s_dcap); xty = tint; return 0; } +fn kw_push() +{ + let t, c; + next(); t = arrarg(); nogrow(t); + if (t[0] == TY_DYN && tsz(t[4]) != 1) serr("push takes one-word elements; use resize for structs"); + expect(s_comma); c = expr(); rval(); argcheck(c, 2); + if (t[0] == TY_DYN && !compat(t[4], xty)) tcerr("push", t[4], xty); + if (o3on && t[0] == TY_DYN && hasptr(t[4])) { lit(t[4]); callname(s_fatten); } + expect(s_rp); + callname(s_dpush); xty = tint; + return 0; +} +fn kw_pop() { let t; next(); t = arrarg(); nogrow(t); expect(s_rp); callname(s_dpop); xty = telem(t); if (t[0] == TY_DYN) xty = t[4]; return 0; } +fn kw_resize() { let t, c; next(); t = arrarg(); nogrow(t); expect(s_comma); c = expr(); rval(); plainuse(c); expect(s_rp); callname(s_dresize); xty = tint; return 0; } +fn kw_sizeof() { let t; next(); t = typearg(); lit(tsz(t)); xty = tint; return 0; } +fn kw_typeof() { let t; next(); t = typearg(); lit(t); xty = tword; return 0; } +fn types_init() +{ + tytop = R_TY + NTL; ntl = 0; + tword = tnew(TY_WORD, 1, intern("word"), 0); + tint = tnew(TY_INT, 1, intern("int"), 0); + tchar = tnew(TY_CHAR, 1, intern("char"), 0); + tbool = tnew(TY_BOOL, 1, intern("bool"), 0); + tvoid = tnew(TY_VOID, 0, intern("void"), 0); + addsym(tword[2], K_TYPE, tword); addsym(tint[2], K_TYPE, tint); addsym(tchar[2], K_TYPE, tchar); + addsym(tbool[2], K_TYPE, tbool); addsym(tvoid[2], K_TYPE, tvoid); + s_lt = intern("<"); s_gt = intern(">"); s_shr = intern(">>"); s_le = intern("<="); s_ge = intern(">="); + s_eqeq = intern("=="); s_ne = intern("!="); s_plus = intern("+"); + s_struct = intern("struct"); s_type = intern("type"); s_typecheck = intern("typecheck"); + s_on = intern("on"); s_off = intern("off"); s_new = intern("new"); s_new_t = intern("new_t"); + s_hitems = intern("#items"); s_hlen = intern("#len"); s_fatten = intern("fatten"); + s_o3 = intern("o3"); s_lfa = intern("lfa"); s_outside = intern("outside"); s_new_raw = intern("new_raw"); s_oob = intern("oob"); + s_fixed = intern("fixed"); s_dlen = intern("dlen"); s_dcap = intern("dcap"); s_dnew = intern("dnew"); s_dat = intern("dat"); + s_dpush = intern("dpush"); s_dpop = intern("dpop"); s_dresize = intern("dresize"); + numops[0] = intern("*"); numops[1] = intern("/"); numops[2] = intern("%"); numops[3] = intern("&"); + numops[4] = intern("|"); numops[5] = intern("^"); numops[6] = intern("<<"); numops[7] = s_shr; nnumops = 8; + return 0; +} +fn isnumop(o) { let i = 0; while (i < nnumops) { if (numops[i] == o) return 1; i++; } return 0; } +// the type of a binary operation, checking it when the typechecker is on +fn bintype(b, lt, rt) +{ + let o = bname[b]; + lt = decay(lt); rt = decay(rt); + if (blen[b] < 0) { tcneed(!isagg(lt) && !isagg(rt), "&& and || need scalars", lt); return tbool; } + if (o == s_eqeq || o == s_ne) { if (!compat(lt, rt) && !compat(rt, lt)) tcerr("comparison", lt, rt); return tbool; } + if (o == s_lt || o == s_gt || o == s_le || o == s_ge) { + if (!(lt == tword || rt == tword || (isnum(lt) && isnum(rt)) || (lt[0] == TY_PTR && rt[0] == TY_PTR))) tcerr("comparison", lt, rt); + return tbool; + } + if (o == s_plus) { + if (lt[0] == TY_PTR && (isnum(rt) || rt == tword)) return lt; + if (rt[0] == TY_PTR && isnum(lt)) return rt; + if (lt == tword || rt == tword) return tword; + if (isnum(lt) && isnum(rt)) return tint; + tcerr("+", lt, rt); return tword; + } + if (o == s_minus) { + if (lt[0] == TY_PTR && rt[0] == TY_PTR) return tint; + if (lt[0] == TY_PTR && (isnum(rt) || rt == tword)) return lt; + if (lt == tword || rt == tword) return tword; + if (isnum(lt) && isnum(rt)) return tint; + tcerr("-", lt, rt); return tword; + } + if (isnumop(o)) { + if (lt == tword || rt == tword) return tword; + if (isnum(lt) && isnum(rt)) return tint; + if (isnum(lt)) lt = rt; + tcneed(0, "arithmetic needs numbers", lt); return tword; + } + return tword; // operators a program added +} + +// ---- reflection ---- +// for programs (and parse words) looking at types at runtime +fn tkind(t) { return t[0]; } +fn tsize(t) { return tsz(t); } +fn tname(t) { return t[2]; } +fn tlen(t) { return t[3]; } +fn tnfields(t) { tresolve(t); return t[3]; } +// ---- fattening ---- +// o3's new_raw stamps no type: the header's type word holds -(n + 1), n the +// words asked for. where o3 data reaches normal code (a store outside o3 +// variables, an argument to a normal function, a return, the end of an o3 +// block) the compiler knows its static type t and calls fatten(p, t), which +// stamps the type, zeroes the slack, and does the same for every object p +// reaches through typed pointers. it returns p. +fn hasptr(t) +{ + let k = t[0], i, n; + if (k == TY_PTR || k == TY_DYN) return 1; + if (k == TY_ARR) return hasptr(t[4]); + if (k == TY_STRUCT && !t[5]) { n = tnfields(t); i = 0; while (i < n) { if (hasptr(tftype(t, i))) return 1; i++; } } + return 0; +} +fn fatten(p, t) { fat1(p, t, 32); return p; } +fn fat1(p, t, dl) +{ + let e, n, k, i, es; + if (dl <= 0 || !t || !isobj(p)) return 0; + if (t[0] == TY_DYN) { // a handle is always fat; what it holds may not be + if (hasptr(t[4])) { es = tsz(t[4]); n = p[0]; i = 0; while (i < n) { fatin(p[2] + i * es, t[4], dl - 1); i++; } } + return 0; + } + if (t[0] != TY_PTR || p[-1] >= 0) return 0; + e = t[4]; n = 0 - p[-1] - 1; es = tsz(e); + if (es < 1) es = 1; + if (e == tword || e[0] == TY_VAR) p[-1] = 0; + else if (n > es) p[-1] = tarr(e, 0); + else p[-1] = e; + if (osize(p) > n) wfill(p + n, 0, osize(p) - n); + if (hasptr(e)) { k = n / es; i = 0; while (i < k) { fatin(p + i * es, e, dl - 1); i++; } } + return 0; +} +fn fatin(a, t, dl) +{ + let k = t[0], i, n, o, ft, es; + if (k == TY_PTR || k == TY_DYN) return fat1(a[0], t, dl); + if (k == TY_ARR) { n = t[3]; es = tsz(t[4]); i = 0; while (i < n) { fatin(a + i * es, t[4], dl); i++; } return 0; } + if (k == TY_STRUCT && !t[5]) { n = tnfields(t); i = 0; while (i < n) { o = tfoff(t, i); ft = tftype(t, i); fatin(a + o, ft, dl); i++; } } + return 0; +} +fn tfname(t, i) { tresolve(t); return t[4][i * 3]; } +fn tftype(t, i) { tresolve(t); return t[4][i * 3 + 1]; } +fn tfoff(t, i) { tresolve(t); return t[4][i * 3 + 2]; } +fn tret(t) { return t[4]; } +fn tnparams(t) { return t[3]; } +fn tparam(t, i) { return t[7 + i]; } +fn tparamtag(t, i) { return ((t[5] >> 6) >> (i + i)) & 3; } // 0 untagged (m), 1 .m, 2 .d +fn tretu(t) { return t[5] & 1; } +fn tnargs(t) { if (t[0] != TY_STRUCT || !t[5]) return 0; return t[6][0]; } +fn targ(t, i) { return t[6][1 + i]; } +// the type of a function, by address. untyped functions get one made from their signature +fn sigof(f) +{ + let i = nsym, s = -1, ws[24], n, k; + while (i) { i--; if (skind[i] == K_FN && sval[i] == f && (sflag[i] & F_DEF)) { s = i; break; } } + if (s < 0) return 0; + if (stype[s]) return stype[s]; + n = (ssig[s] >> 1) & 31; k = 0; + while (k < n) { ws[k] = tword; k++; } + return tfn(tword, n, ws, ssig[s]); +} +fn sigofs(nm) { let s = lookups(nm); if (s < 0 || skind[s] != K_FN) return 0; return sigof(sval[s]); } + +// ---- expressions ---- +// every expression leaves its value on the stack, returns its analyzer class, +// and sets xty to its type. lv means the stack holds an address that hasn't +// been loaded yet; structs and arrays are never loaded, their address is +// their value. +fn rval() { if (lv) { if (!isagg(xty)) op(O_LD); lv = 0; } return 0; } +fn kwcall(s, e) +{ + let f = sval[s], old = kw_expr, c; + kw_expr = e; xcls = C_PLAIN; xty = tword; xzero = 0; + f(); + kw_expr = old; + c = xcls; + lv = 0; lvk = LV_NONE; + return c; +} +fn stmtonly() { if (kw_expr) serr("this can't be used inside an expression"); return 0; } +// the arguments of a call. ft is the callee's type (or 0), bs the generic bindings +fn args(tags, ft, bs, fat) +{ + let np = 0, c, pt, t; + expect(s_lp); + if (!is_op(s_rp)) { + while (1) { + c = expr(); rval(); t = xty; + argcheck(c, (tags >> (np + np)) & 3); + if (fat && hasptr(t)) { lit(t); callname(s_fatten); xty = t; } // o3 data handed to normal code + if (ft && np < ft[3]) { pt = ft[7 + np]; if (!unify(pt, xty, bs)) tcerr("argument", subst(pt, bs), xty); } + np++; + if (!accept(s_comma)) break; + } + } + expect(s_rp); + return np; +} +// new(T) and new(T, count): a typed object, its type stamped into the header +fn newtyped() +{ + let t, c; + t = ptype(); + if (t[0] == TY_DYN) { // a growable array: element size, length, type + lit(tsz(t[4])); + if (accept(s_comma)) { c = expr(); rval(); plainuse(c); } + else { lit(0); if (t[3]) serr("a fixed array needs its length: new(fixed []T, n)"); } + lit(t); + expect(s_rp); + callname(s_dnew); + lv = 0; lvk = LV_NONE; xty = t; xzero = 0; + return C_FRESH; + } + if (o3on) { // o3: new_raw(words), no stamp, no zeroing + if (accept(s_comma)) { c = expr(); rval(); plainuse(c); if (tsz(t) != 1) { lit(tsz(t)); op(15); } } + else lit(tsz(t)); + expect(s_rp); + callname(s_new_raw); + lv = 0; lvk = LV_NONE; xty = tptr(t); xzero = 0; + return C_FRESH; + } + if (accept(s_comma)) { + c = expr(); rval(); plainuse(c); + if (tsz(t) != 1) { lit(tsz(t)); op(15); } + lit(tarr(t, 0)); + } else { lit(tsz(t)); lit(t); } + expect(s_rp); + callname(s_new_t); + lv = 0; lvk = LV_NONE; xty = tptr(t); xzero = 0; + return C_FRESH; +} +fn call(s) +{ + let np, tags = 0, ret = 0, ft = stype[s], bs[8]; + if (sname[s] == s_new && skind[s] == K_FN) { + peek(); + if (pk_tk == T_OP && (pk_tv == s_star || pk_tv == s_lk)) { next(); return newtyped(); } + if (pk_tk == T_ID && (istypename(pk_tv) || pk_tv == s_fn)) { next(); return newtyped(); } + } + if (o3on && sname[s] == s_new) { tags = lookup(s_new_raw); if (tags >= 0) s = tags; tags = 0; } + if (sflag[s] & F_SIG) { tags = ssig[s] >> 6; ret = ssig[s] & 1; } + else sflag[s] = sflag[s] | F_UNTYPED; + wfill(bs, 0, 8); + if (ngbind) { wcopy(bs, gbind, ngbind); ngbind = 0; } + np = 0; + if (o3on && !(sflag[s] & F_O3) && sname[s] != s_free) np = 1; // o3 calling normal code: fatten the arguments + np = args(tags, ft, bs, np); + if ((sflag[s] & F_SIG) && np != ((ssig[s] >> 1) & 31)) serrn(sname[s], "wrong number of arguments"); + fnref(s); op(O_CALL); + lv = 0; lvk = LV_NONE; xzero = 0; + xty = tword; + if (ft) xty = subst(ft[4], bs); + if (ret) return C_FRESH; + return C_PLAIN; +} +// an enhancement used like a function: the arguments, then its one opcode +fn enhcall(s) +{ + let nin = ssig[s] & 15, nout = (ssig[s] >> 4) & 15, n = 0, c; + expect(s_lp); + if (!is_op(s_rp)) { while (1) { c = expr(); rval(); plainuse(c); n++; if (!accept(s_comma)) break; } } + expect(s_rp); + if (n != nin) serrn(sname[s], "wrong number of inputs"); + lv = 0; lvk = LV_NONE; xty = tword; xzero = 0; + if (sflag[s] & F_FALLBACK) { fnref(sfix[s] - 1); op(O_CALL); return C_PLAIN; } + op(XOP0 + sval[s]); + if (nout == 0) lit(0); + if (nout > 1) serrn(sname[s], "leaves several values; use it from asm"); + return C_PLAIN; +} +fn primary() +{ + let n, i, s, c; + lv = 0; lvk = LV_NONE; xzero = 0; + xconst = 0; + if (tk == T_NUM) { lit(tv); xzero = tv == 0; xconst = 1; xcval = tv; xty = tint; next(); return C_PLAIN; } + if (tk == T_STR) { strlit(tv); next(); xty = tptr(tchar); return C_PLAIN; } + if (tk == T_OP) { + if (tv == s_lp) { next(); c = expr(); expect(s_rp); return c; } + if (tv == s_lb) return blockexpr(); + die("expected an expression"); lit(0); xty = tword; return C_PLAIN; + } + if (tk != T_ID) { die("expected an expression"); lit(0); xty = tword; return C_PLAIN; } + n = tv; + i = findloc(n); + if (i >= 0) { + next(); + if (!fchk && tracked(i)) serrn(n, "is tracked by the analyzer and can't be used in unchecked code"); + vaddr(i); + xty = ltype[i]; + if (larr[i]) return C_PLAIN; + lv = 1; lvk = LV_LOCAL; lvi = i; + if (tracked(i)) return C_VAR + i; + return C_PLAIN; + } + s = lookup(n); + if (s >= 0 && skind[s] == K_KW) { + if (!(sflag[s] & F_EXPR)) dien(n, "can't be used inside an expression"); + return kwcall(s, 1); + } + if (s >= 0 && skind[s] == K_TYPE) { dien(n, "is a type, not a value"); lit(0); xty = tword; return C_PLAIN; } + next(); + xty = tword; + if (s >= 0 && skind[s] == K_CONST) { lit(sval[s]); xty = tint; xzero = sval[s] == 0; xconst = 1; xcval = sval[s]; return C_PLAIN; } + if (s >= 0 && skind[s] == K_GVAR) { lit(sval[s]); if (stype[s]) xty = stype[s]; lv = 1; lvk = LV_GLOBAL; return C_PLAIN; } + if (s >= 0 && skind[s] == K_GARR) { lit(sval[s]); if (stype[s]) xty = stype[s]; return C_PLAIN; } + if (s >= 0 && skind[s] == K_ENH) return enhcall(s); + if (s < 0) s = fwdfn(n); + if (is_op(s_lt) && (sflag[s] & F_GEN)) { // f(...) + next(); ngbind = 0; + while (1) { if (ngbind >= 8) die("too many type arguments"); gbind[ngbind] = ptype(); ngbind++; if (!accept(s_comma)) break; } + closeangle(); + if (!is_op(s_lp)) die("expected ( after the type arguments"); + } + if (is_op(s_lp)) return call(s); + fnref(s); + if (stype[s]) xty = stype[s]; + return C_PLAIN; +} +// ++ and -- on the address at the top of the stack +fn incdec(post, dlt) +{ + if (!lv) die("++ and -- need something to assign to"); + if (lvk == LV_LOCAL && fchk && tracked(lvi)) cerrn(lvi, "owns an object and can't be incremented"); + tcneed(isnum(xty) || xty[0] == TY_PTR || xty == tword, "++ and -- need a number or a pointer", xty); + lv = 0; lvk = LV_NONE; xzero = 0; + lit(0); op(O_PICK); op(O_LD); + if (post) { lit(0); op(O_PICK); lit(dlt); op(O_ADD); lit(2); op(O_PICK); op(O_ST); } + else { lit(dlt); op(O_ADD); lit(0); op(O_PICK); lit(2); op(O_PICK); op(O_ST); } + op(O_SWAP); op(O_DROP); + return C_PLAIN; +} +// p.x and p->x: a field of a struct, or of the struct a pointer points at +fn field(c) +{ + let n, st, i, b = -1; + next(); + n = name(); + rval(); + if (c == C_FRESH) cerr("use a field of a new object only after binding it to a name"); + else if (c >= C_VAR) { touch(c - C_VAR); b = c - C_VAR; } + st = xty; + if (st[0] == TY_PTR) st = st[4]; + if (st[0] != TY_STRUCT) { + if (!panic && !dry) { errat(); putc('.'); puts(n); puts(": that's a "); tprint(xty); puts(", not a struct or a pointer to one"); errend(); } + xty = tword; lv = 1; lvk = LV_OTHER; return C_PLAIN; + } + i = tfindfield(st, n); + if (i < 0) { + if (!panic && !dry) { errat(); tprint(st); puts(" has no field "); puts(n); errend(); } + xty = tword; lv = 1; lvk = LV_OTHER; return C_PLAIN; + } + if (st[4][i * 3 + 2]) { lit(st[4][i * 3 + 2]); op(O_ADD); } + xty = st[4][i * 3 + 1]; + lv = 1; xzero = 0; + if (b >= 0) { lvk = LV_ELEM; lvi = b; } else lvk = LV_OTHER; + return C_PLAIN; +} +fn postfix() +{ + let c = primary(), b, n, t, et, ft, bs[8], k, ci; + while (1) { + if (tk == T_ID && tv == s_as) { // e as T: same bits, another type + next(); t = ptype(); rval(); xty = t; lvk = LV_NONE; + continue; + } + if (tk != T_OP) return c; + if (tv == s_lk) { + ci = 0; + if (o3on && lv && lvk == LV_LOCAL && o3cache[lvi]) ci = o3cache[lvi]; + next(); + rval(); + b = -1; + if (c == C_FRESH) cerr("index a new object only after binding it to a name"); + else if (c >= C_VAR) { touch(c - C_VAR); b = c - C_VAR; } + if (xty[0] == TY_DYN) { // a growable array: bounds-checked, through the runtime + et = xty[4]; + if (ci && lookup(s_outside) >= 0) op(O_DROP); + c = expr(); rval(); plainuse(c); + tcneed(isnum(xty) || xty == tword, "an index has to be a number", xty); + expect(s_rk); + k = lookup(s_outside); + if (ci && k >= 0 && skind[k] == K_ENH) { // a fixed array in o3: through its cached length and items + lit(0); op(O_PICK); vaddr(ci + 1); op(O_LD); op(XOP0 + sval[k]); + k = hole(); op(O_JZ); + vaddr(ci + 1); op(O_LD); callname(s_oob); + setlit(k, here); + vaddr(ci); op(O_LD); op(O_SWAP); + if (tsz(et) != 1) { lit(tsz(et)); op(15); } + op(O_ADD); + } else if (o3on && k >= 0 && skind[k] == K_ENH) { // h i: check i against h[0], then h[2] + i * size + lit(0); op(O_PICK); lit(2); op(O_PICK); op(O_LD); op(XOP0 + sval[k]); + k = hole(); op(O_JZ); + lit(1); op(O_PICK); op(O_LD); callname(s_oob); + setlit(k, here); + op(O_SWAP); lit(2); op(O_ADD); op(O_LD); op(O_SWAP); + if (tsz(et) != 1) { lit(tsz(et)); op(15); } + op(O_ADD); + } else callname(s_dat); + } else { + tcneed(isptr(xty) || xty == tword, "only pointers and arrays can be indexed", xty); + et = telem(xty); + n = 0 - 1; if (xty[0] == TY_ARR && xty[3] > 0) n = xty[3]; + xconst = 0; + c = expr(); rval(); plainuse(c); + if (n >= 0 && xconst && (xcval < 0 || xcval >= n)) serr("the index is out of range for this array"); + tcneed(isnum(xty) || xty == tword, "an index has to be a number", xty); + expect(s_rk); + if (tsz(et) != 1) { lit(tsz(et)); op(15); } + op(O_ADD); + } + lv = 1; xty = et; xzero = 0; + if (b >= 0) { lvk = LV_ELEM; lvi = b; } else lvk = LV_OTHER; + c = C_PLAIN; + continue; + } + if (tv == s_dot || tv == s_arrow) { c = field(c); continue; } + if (tv == s_lp) { // call through a value + rval(); plainuse(c); + ft = 0; + if (xty[0] == TY_FN) ft = xty; + else tcneed(xty == tword, "only functions can be called", xty); + wfill(bs, 0, 8); + n = args(0, ft, bs, o3on); + lit(n); op(O_PICK); op(O_CALL); op(O_SWAP); op(O_DROP); + lv = 0; lvk = LV_NONE; c = C_PLAIN; xzero = 0; + xty = tword; + if (ft) xty = subst(ft[4], bs); + continue; + } + if (tv == s_inc) { next(); c = incdec(1, 1); continue; } + if (tv == s_dec) { next(); c = incdec(1, -1); continue; } + return c; + } + return c; +} +fn unary() +{ + let c, un, t; + if (tk == T_ID && (tv == s_u || tv == s_m || tv == s_d)) { // u*x, d*x, m*x: recast + t = S_M; if (tv == s_u) t = S_U; if (tv == s_d) t = S_D; + next(); expect(s_star); + c = unary(); rval(); + lvk = LV_NONE; + return recast(c, t); + } + if (tk == T_OP) { + if (tv == s_star) { + next(); c = unary(); rval(); plainuse(c); + tcneed(xty[0] == TY_PTR || xty == tword, "only pointers can be dereferenced", xty); + xty = telem(xty); + lv = 1; lvk = LV_OTHER; xzero = 0; + return C_PLAIN; + } + if (tv == s_amp) { + next(); c = unary(); + if (!lv && !isagg(xty)) die("& needs something addressable"); + if (lv && lvk == LV_LOCAL && (ltag[lvi] & 16)) serr("an o3 variable has no address"); + if (c >= C_VAR) touch(c - C_VAR); + lv = 0; lvk = LV_NONE; xzero = 0; + xty = tptr(xty); + return C_PLAIN; + } + if (tv == s_inc) { next(); unary(); return incdec(0, 1); } + if (tv == s_dec) { next(); unary(); return incdec(0, -1); } + un = unfind(tv); + if (un >= 0) { + next(); c = unary(); rval(); plainuse(c); + emitseq(useq[un], ulen[un]); + xconst = 0; + if (un <= 1) xty = tbool; + else if (xty != tword) { tcneed(isnum(xty), "this operator needs a number", xty); xty = tint; } + lv = 0; lvk = LV_NONE; xzero = 0; + return C_PLAIN; + } + } + return postfix(); +} +fn bin(minp) +{ + let c = unary(), b, p, a, j, lt; + while (tk == T_OP) { + b = binfind(tv); + if (b < 0) return c; + p = bprec[b]; + if (p < minp) return c; + next(); rval(); plainuse(c); + lt = xty; + if (blen[b] == -1) { // && : 0 if the left side is 0, else the right side as 0 or 1 + a = hole(); op(O_JZ); + c = bin(p + 1); rval(); plainuse(c); + emitseq(useq[1], ulen[1]); // !! normalizes + j = hole(); op(O_JMP); + setlit(a, here); lit(0); setlit(j, here); + } else if (blen[b] == -2) { // || : 1 if the left side isn't 0, else the right side as 0 or 1 + a = hole(); op(O_JZ); + lit(1); j = hole(); op(O_JMP); + setlit(a, here); + c = bin(p + 1); rval(); plainuse(c); + emitseq(useq[1], ulen[1]); + setlit(j, here); + } else { + c = bin(p + 1); rval(); plainuse(c); + emitseq(bseq[b], blen[b]); + } + xty = bintype(b, lt, xty); + lv = 0; lvk = LV_NONE; c = C_PLAIN; xzero = 0; xconst = 0; + } + return c; +} +// [addr v] -> [v], storing v at addr +fn storekeep() { op(O_SWAP); lit(1); op(O_PICK); op(O_SWAP); op(O_ST); return 0; } +// c ? a : b +fn ternary(c) +{ + let a, b, s0, sa, livea, live0 = live, n0 = nloc, ca, cb, ta; + rval(); plainuse(c); + next(); + a = hole(); op(O_JZ); + s0 = snap_push(); + ca = expr(); rval(); ta = xty; + if (ca != C_FRESH) plainuse(ca); + livea = live; sa = snap_push(); + b = hole(); op(O_JMP); + expect(s_colon); + setlit(a, here); + snap_restore(s0); live = live0; + cb = expr(); rval(); + if (cb != C_FRESH) plainuse(cb); + join2(sa, livea, b, live, n0, "ends the two sides of ?: in different states"); + snap_pop(s0); + if (!compat(ta, xty) && !compat(xty, ta)) tcerr("the two sides of ?:", ta, xty); + if (ta != tword) xty = ta; + lv = 0; lvk = LV_NONE; xzero = 0; + if (ca == C_FRESH && cb == C_FRESH) return C_FRESH; + if (ca == C_FRESH || cb == C_FRESH) cerr("both sides of ?: have to be new objects, or neither"); + return C_PLAIN; +} +fn expr() +{ + let c = bin(1), k, i, a, rc, lt; + if (tk != T_OP) return c; + if (tv == s_q) return ternary(c); + if (tv == s_eq) { + if (!lv) die("this can't be assigned to"); + k = lvk; i = lvi; lt = xty; + if (isagg(lt)) serr("a whole struct or array can't be assigned; assign its fields"); + next(); + a = o3on && k == LV_LOCAL && (ltag[i] & 16) && !larr[i]; + if (a) op(O_DROP); // an o3 variable: its address comes after the value, so it never waits on the stack across a branch + rc = expr(); rval(); + if (!compat(lt, xty)) tcerr("assignment", lt, xty); + if (a) { lit(0); op(O_PICK); vaddr(i); op(O_ST); o3refresh(i); } + else { + if (o3on && k != LV_ELEM && hasptr(lt)) { lit(lt); callname(s_fatten); } // o3 data going somewhere normal code can see + storekeep(); + } + lv = 0; lvk = LV_NONE; xty = lt; xzero = 0; + return assigncheck(k, i, rc); + } + a = asgfind(tv); + if (a >= 0) { + if (!lv) die("this can't be assigned to"); + if (lvk == LV_LOCAL && fchk && tracked(lvi)) cerrn(lvi, "owns an object; compound assignment would lose it"); + lt = xty; + next(); + i = o3on && lvk == LV_LOCAL && (ltag[lvi] & 16) && !larr[lvi]; + if (i) { i = lvi; op(O_DROP); vaddr(i); op(O_LD); i = i + 1; } + else { lit(0); op(O_PICK); op(O_LD); } + lv = 0; + rc = expr(); rval(); plainuse(rc); + emitseq(bseq[abin[a]], blen[abin[a]]); + k = bintype(abin[a], lt, xty); + if (!compat(lt, k)) tcerr("compound assignment", lt, k); + if (i) { lit(0); op(O_PICK); vaddr(i - 1); op(O_ST); } else storekeep(); + lv = 0; lvk = LV_NONE; xty = lt; xzero = 0; + return C_PLAIN; + } + return c; +} +// does the current token start a statement rather than an expression? +fn stmtstart() +{ + let s; + if (tk == T_OP) return tv == s_semi || tv == s_lb || tstart(); + if (tk != T_ID || findloc(tv) >= 0) return 0; + if (tstart()) return 1; + s = lookup(tv); + return s >= 0 && skind[s] == K_KW && !(sflag[s] & F_EXPR); +} +// { stmts; tail } as an expression: its value is the tail, or 0 +fn blockexpr() +{ + let save = nloc, c = C_PLAIN, tail = 0, t = tint; + next(); + fdepth++; bxdepth++; + while (!is_op(s_rb)) { + if (tk == T_EOF) die("missing }"); + if (stmtstart()) { stmt(); continue; } + c = expr(); rval(); + if (accept(s_semi)) { op(O_DROP); discard(c); c = C_PLAIN; continue; } + if (!is_op(s_rb)) die("expected ; or } after an expression"); + tail = 1; t = xty; + } + next(); + if (tail) c = handup(c); else lit(0); + block_close(save); + fdepth--; bxdepth--; + lv = 0; lvk = LV_NONE; xty = t; xzero = 0; + return c; +} + +// ---- constant expressions ---- +fn cprim() +{ + let v, s; + if (tk == T_NUM) { v = tv; next(); return v; } + if (accept(s_minus)) return 0 - cprim(); + if (tk == T_OP && tv == uname[2]) { next(); return ~cprim(); } + if (accept(s_lp)) { v = cexpr(); expect(s_rp); return v; } + if (tk == T_ID) { + s = lookup(tv); + if (s >= 0 && (skind[s] == K_CONST || skind[s] == K_GARR || (skind[s] == K_FN && (sflag[s] & F_DEF)) || skind[s] == K_GVAR)) { next(); return sval[s]; } + } + die("expected a constant"); + return 0; +} +fn cexpr() +{ + let v = cprim(), o; + while (tk == T_OP) { + o = tv; + if (o == bname[3]) { next(); v = v + cprim(); } + else if (o == bname[4]) { next(); v = v - cprim(); } + else if (o == bname[0]) { next(); v = v * cprim(); } + else if (o == bname[15]) { next(); v = v | cprim(); } + else if (o == bname[13]) { next(); v = v & cprim(); } + else if (o == bname[5]) { next(); v = v << cprim(); } + else if (o == bname[6]) { next(); v = v >> cprim(); } + else return v; + } + return v; +} + +// ---- statements ---- +// after a syntax error: skip to the end of the statement +fn resync() +{ + while (tk != T_EOF && !is_op(s_semi) && !is_op(s_rb)) next(); + if (is_op(s_semi)) next(); + panic = 0; + return 0; +} +fn stmt() +{ + let s, c; + if (panic) return resync(); + if (tk == T_ID && findloc(tv) < 0) { + s = lookup(tv); + if (s >= 0 && skind[s] == K_KW && (sflag[s] & F_STMT)) { kwcall(s, 0); return 0; } + } + if (tstart()) { localdecl(1); return 0; } + if (tk == T_OP) { + if (tv == s_semi) { next(); return 0; } + if (tv == s_lb) { block(); return 0; } + } + c = expr(); rval(); op(O_DROP); + expect(s_semi); + discard(c); + return 0; +} +fn block() +{ + let save = nloc; + expect(s_lb); + fdepth++; + while (!is_op(s_rb)) { if (tk == T_EOF) die("missing }"); stmt(); } + block_close(save); // before the }, so its line is the one reported + next(); + fdepth--; + return 0; +} +// the body of if/while/for: its own block, even without braces +fn substmt() +{ + let save = nloc; + if (is_op(s_lb)) return block(); + fdepth++; + stmt(); + block_close(save); + fdepth--; + return 0; +} +// let a = e, b[10]; or, typed: int a = 1, b; *node.m p = new(node); .u r; +// let infers pointer, struct and function types from the initializer and +// keeps numbers as plain words. +fn localdecl(typed) +{ + let n, i, c, k, t = 0, tg = 0; + if (typed) { dtype(); t = dty; tg = dtag; if (t == tvoid) serr("a variable can't be void"); } + if ((o3on || o3one) && !typed) serr("o3 variables need a type"); + while (1) { + n = name(); + i = newloc(n); + ltag[i] = tg; + if (o3on || o3one) ltag[i] = tg | 16; + if (accept(s_lk)) { + k = cexpr(); expect(s_rk); + if (!t) t = tword; + larr[i] = 1; ltype[i] = tarr(t, k); fslots = fslots + tsz(ltype[i]); + } else if (t && isagg(t)) { larr[i] = 1; ltype[i] = t; fslots = fslots + tsz(t); } + else { fslots++; if (t) ltype[i] = t; if (o3on) { o3promote(i); o3mkcache(i); } } + if (accept(s_eq)) { + if (larr[i]) serr("an array or a struct can't be initialized with ="); + c = expr(); rval(); vaddr(i); op(O_ST); o3refresh(i); + if (t) { if (!compat(t, xty)) tcerr("initializer", t, xty); } + else if (!isnum(xty)) ltype[i] = xty; + assigncheck(LV_LOCAL, i, c); + } + if (!accept(s_comma)) break; + } + expect(s_semi); + return 0; +} +fn kw_var() { stmtonly(); next(); return localdecl(0); } +fn kw_if() +{ + let a, b, c, s0, sa, live0, livea, n0 = nloc, nt, cj; + stmtonly(); + next(); + expect(s_lp); c = expr(); rval(); plainuse(c); expect(s_rp); + a = hole(); op(O_JZ); + live0 = live; + s0 = snap_push(); + substmt(); + livea = live; sa = snap_push(); + if (is_id(s_else)) { + next(); + b = hole(); op(O_JMP); + setlit(a, here); + snap_restore(s0); live = live0; + substmt(); + join2(sa, livea, b, live, n0, "ends the branches of an if in different states"); + } else { + // the path that skips the then branch still has the states from before it + njps = 0; if (livea) jadd(R_SNAP + sa); if (live0) jadd(R_SNAP + s0); + join(n0, "ends the branches of an if in different states"); + if (livea) fixups(R_SNAP + sa, n0, 1); + nt = 0; if (live0) nt = fixups(R_SNAP + s0, n0, 0); + if (nt) { cj = hole(); op(O_JMP); setlit(a, here); fixups(R_SNAP + s0, n0, 1); setlit(cj, here); } + else setlit(a, here); + live = livea || live0; + } + snap_pop(s0); + return 0; +} +// loop frame: 0 continue target 1 break chain 2 depth 3 snapshot 4 block-expression depth +// loop frame: 0 continue target (-1 for a switch) 2 depth 3 snapshot at the top +// 4 block-expression depth 5 locals at the start 6 its first exit record +fn loop_push(cont, s0) +{ + let f = R_LOOP + lsp * 8; + if (lsp >= 60) fatal("loops nested too deeply"); + f[0] = cont; f[1] = 0; f[2] = fdepth; f[3] = s0; f[4] = bxdepth; f[5] = nloc; f[6] = extop; + lsp++; + return 0; +} +// the end of a loop or switch: join its exits (the fall-out path sc through +// the jump at a, if there is one, and every break) and lay out each path's +// fixups on its way to the end +fn loop_end(a, sc) +{ + let f, p, e, n0, outs = 0; + lsp--; + f = R_LOOP + lsp * 8; + n0 = f[5]; + njps = 0; + if (sc >= 0) jadd(R_SNAP + sc); + p = R_EXIT + f[6]; e = R_EXIT + extop; + while (p < e) { jadd(p + 1); p = p + 3 + 3 * (p[2] - p[1]); } + join(n0, "leaves the loop in different states on different paths"); + live = njps > 0; + p = R_EXIT + f[6]; + while (p < e) { + if (fixups(p + 1, n0, 0)) { setlit(p[0], here); fixups(p + 1, n0, 1); outs = fixnew(hole(), outs); op(O_JMP); } + else outs = fixnew(p[0], outs); + p = p + 3 + 3 * (p[2] - p[1]); + } + if (a >= 0) setlit(a, here); + if (sc >= 0) fixups(R_SNAP + sc, n0, 1); + fixall(outs, here); + extop = f[6]; + return 0; +} +fn kw_while() +{ + let c0 = here, a, s0, sc = -1, c, inf = 0; + stmtonly(); + next(); + s0 = snap_push(); + expect(s_lp); + if (tk == T_NUM && tv) { peek(); if (pk_tk == T_OP && pk_tv == s_rp) inf = 1; } // while (1): no way out but break + c = expr(); rval(); plainuse(c); expect(s_rp); + a = hole(); op(O_JZ); + if (!inf && live) sc = snap_push(); + loop_push(c0, s0); + substmt(); + backedge(s0); + jmp(c0); + loop_end(a, sc); + snap_pop(s0); + return 0; +} +fn kw_for() +{ + let save = nloc, s0, sc = -1, c0, a, b, st, c, inf = 0; + stmtonly(); + next(); + expect(s_lp); + fdepth++; + if (is_id(s_var)) kw_var(); + else if (tstart()) localdecl(1); + else { if (!is_op(s_semi)) { c = expr(); rval(); op(O_DROP); discard(c); } expect(s_semi); } + s0 = snap_push(); + c0 = here; + if (!is_op(s_semi)) { + if (tk == T_NUM && tv) { peek(); if (pk_tk == T_OP && pk_tv == s_semi) inf = 1; } + c = expr(); rval(); plainuse(c); + } else { lit(1); inf = 1; } + expect(s_semi); + a = hole(); op(O_JZ); + if (!inf && live) sc = snap_push(); + b = hole(); op(O_JMP); + st = here; + if (!is_op(s_rp)) { c = expr(); rval(); op(O_DROP); discard(c); } + expect(s_rp); + jmp(c0); + setlit(b, here); + loop_push(st, s0); + substmt(); + backedge(s0); + jmp(st); + loop_end(a, sc); + snap_pop(s0); + block_close(save); + fdepth--; + return 0; +} +fn kw_break() +{ + let f; + stmtonly(); + next(); expect(s_semi); + if (lsp <= loopbase) { serr("break outside a loop or a switch"); return 0; } + f = R_LOOP + (lsp - 1) * 8; + if (f[4] != bxdepth) { serr("break can't leave a block expression"); return 0; } + exit_vars(f[2] + 1, 0); + if (live) exit_rec(hole()); else hole(); + op(O_JMP); + live = 0; + return 0; +} +fn kw_continue() +{ + let j = lsp - 1, f; + stmtonly(); + next(); expect(s_semi); + while (j >= loopbase && R_LOOP[j * 8] == -1) j--; // past any switch + if (j < loopbase) { serr("continue outside a loop"); return 0; } + f = R_LOOP + j * 8; + if (f[4] != bxdepth) { serr("continue can't leave a block expression"); return 0; } + backedge(f[3]); + exit_vars(f[2] + 1, 0); + jmp(f[0]); + live = 0; + return 0; +} +// switch (e) { case 1, 2: ... case 'x': ... default: ... } +// cases don't fall through: each one ends where the next begins, and break +// leaves the switch early. case labels are constants. +fn casebody(s0, live0) +{ + let save = nloc; + snap_restore(s0); live = live0; + fdepth++; + while (!is_op(s_rb) && !is_id(s_case) && !is_id(s_default)) { if (tk == T_EOF) { die("missing }"); break; } stmt(); } + block_close(save); + fdepth--; + if (live) { exit_rec(hole()); op(O_JMP); } // the end of a case leaves the switch + live = 0; + return 0; +} +fn kw_switch() +{ + let save = nloc, tmp, c, s0, live0, defent = -1, skip, nxt, bj, v, a = -1; + stmtonly(); + next(); + expect(s_lp); c = expr(); rval(); plainuse(c); expect(s_rp); + fdepth++; + tmp = newloc(0); fslots++; // a hidden local holds the value + laddr(lslot[tmp]); op(O_ST); + live0 = live; + s0 = snap_push(); + loop_push(-1, s0); + expect(s_lb); + while (!is_op(s_rb)) { + if (tk == T_EOF) { die("missing }"); break; } + if (is_id(s_case)) { + next(); + bj = 0; + while (1) { + v = cexpr(); + laddr(lslot[tmp]); op(O_LD); lit(v); op(O_SUB); + bj = fixnew(hole(), bj); op(O_JZ); + if (!accept(s_comma)) break; + } + expect(s_colon); + nxt = hole(); op(O_JMP); + fixall(bj, here); + casebody(s0, live0); + setlit(nxt, here); + } else if (is_id(s_default)) { + next(); expect(s_colon); + if (defent >= 0) serr("a switch has one default"); + skip = hole(); op(O_JMP); // only reached when nothing matched + defent = here; + casebody(s0, live0); + setlit(skip, here); + } else die("expected case or default"); + } + next(); + snap_restore(s0); live = live0; + if (defent >= 0) { jmp(defent); loop_end(-1, -1); } + else { a = hole(); op(O_JMP); loop_end(a, s0); } // nothing matched: out with the states from the top + snap_pop(s0); + block_close(save); + fdepth--; + return 0; +} +fn kw_return() +{ + let c; + stmtonly(); + next(); + if (!infn) serr("return outside a function"); + if (bxdepth) serr("return can't leave a block expression"); + if (is_op(s_semi)) { lit(0); c = C_PLAIN; } + else { + c = expr(); rval(); + if (fty == tvoid) tcerr("return value", tvoid, xty); + else if (fty && !compat(fty, xty)) tcerr("return value", fty, xty); + if (o3on && fty && hasptr(fty)) { lit(fty); callname(s_fatten); } + } + expect(s_semi); + retcheck(c); + exit_vars(1, 0); + epichain = fixnew(here, epichain); + lit(MW - 1); op(O_JMP); + live = 0; + return 0; +} +// o3 { ... }: an optimized region. o3 T x = e;: one o3 variable +fn kw_o3() +{ + let lo, i, p, save, outer; + stmtonly(); + next(); + if (!is_op(s_lb)) { o3one = 1; localdecl(1); o3one = 0; return 0; } + if (o3on || !infn) return block(); + lo = here + 2; lit(lo); op(O_JMP); + o3on = 1; o3reset(); + save = nloc; outer = nloc; + i = lbase; + while (i < outer) { if (ltag[i] & 16) { o3promote(i); o3mkcache(i); o3refresh(i); } i++; } // o3 variables declared outside + block(); + o3on = 0; + if (no3pend < 10) { + p = o3pend + no3pend * 67; p[0] = lo; p[1] = here; p[2] = no3sl; + wcopy(p + 3, o3sl, no3sl); wcopy(p + 35, o3rg, no3sl); no3pend++; + } + nloc = save; // the hidden cache variables + i = lbase; + while (i < outer) { + ltag[i] = ltag[i] - (ltag[i] & 1792); o3cache[i] = 0; + if ((ltag[i] & 16) && hasptr(ltype[i])) { vaddr(i); op(O_LD); lit(ltype[i]); callname(s_fatten); op(O_DROP); } // leaving o3: fatten what they hold + i++; + } + return 0; +} +fn kw_unchecked() +{ + let old = fchk; + stmtonly(); + next(); + fchk = 0; + substmt(); + fchk = old; + return 0; +} + +// ---- asm blocks: raw vm words, one per token ---- +// numbers push, -n pushes a negative number, #n emits a raw word, one-char +// tokens from the mnemonic table are opcodes, names push the address of a +// variable or function, :name defines a label for this block. +fn asmblock() +{ + let nl = 0, nr = 0, i, k, c, s, ln = R_ASM, la = R_ASM + 64, rn = R_ASM + 128, ra = R_ASM + 384; + expect(s_lb); + while (!is_op(s_rb)) { + if (tk == T_EOF) die("unterminated asm block"); + if (tk == T_NUM) { lit(tv); next(); continue; } + if (tk == T_STR) { strlit(tv); next(); continue; } + if (is_op(s_hash)) { next(); if (tk != T_NUM) die("# needs a number"); emit(tv); next(); continue; } + if (is_op(s_minus)) { next(); if (tk == T_NUM && tglue) { lit(0 - tv); next(); } else op(O_SUB); continue; } + if (is_op(s_colon)) { next(); if (nl >= 64) fatal("too many asm labels"); ln[nl] = name(); la[nl] = here; nl++; continue; } + if (tv[0] && !tv[1] && (c = mnemidx(tv[0])) >= 0) { op(c); next(); continue; } + if (tk != T_ID) die("bad token in asm"); + i = findloc(tv); + if (i >= 0 && (ltag[i] & 16) && !larr[i]) serr("asm can't reach an o3 variable"); + if (i >= 0) { lused[i] = 1; laddr(lslot[i]); next(); continue; } + k = -1; i = 0; + while (i < nl) { if (ln[i] == tv) k = i; i++; } + if (k >= 0) { lit(la[k]); next(); continue; } + s = lookup(tv); + if (s >= 0 && skind[s] == K_ENH) { op(XOP0 + sval[s]); next(); continue; } + if (s >= 0 && skind[s] != K_FN && skind[s] != K_KW) { lit(sval[s]); next(); continue; } + if (s >= 0 && skind[s] == K_FN) { fnref(s); next(); continue; } + if (nr >= 250) fatal("too many asm label references"); + rn[nr] = tv; ra[nr] = hole(); nr++; next(); // a label further down this block + } + next(); + i = 0; + while (i < nr) { + k = -1; c = 0; + while (c < nl) { if (ln[c] == rn[i]) k = c; c++; } + if (k < 0) serrn(rn[i], "unknown name in asm"); + setlit(ra[i], la[k]); + i++; + } + return 0; +} +fn kw_asm() { next(); asmblock(); xcls = C_PLAIN; return 0; } + +// ---- quote: code as data ---- +// a token list is an object: [n] then kind, value, line for each token. names, +// strings and operators are interned pointers. +.u grab() +{ + let n = 0, depth = 0, q; + if (!is_op(s_lb)) die("expected {"); + next(); + while (1) { + if (tk == T_EOF) die("unterminated quote"); + if (is_op(s_lb)) depth++; + if (is_op(s_rb)) { if (!depth) break; depth--; } + if (n >= 2700) fatal("quote too long"); + R_QBUF[n * 3] = tk; R_QBUF[n * 3 + 1] = tv; R_QBUF[n * 3 + 2] = tline; n++; + next(); + } + next(); + q = new(n * 3 + 1); + q[0] = n; + wcopy(q + 1, R_QBUF, n * 3); + return u*q; +} +unchecked .u qnew(src) { let n = src[0] * 3 + 1, q = new(n); wcopy(q, src, n); return q; } +.u qdup(.m q) { let n = q[0] * 3 + 1, r = new(n); wcopy(r, q, n); return u*r; } +fn wcopyto(.m q, n) { let i = 0; while (i < n) { emit(q[i]); i++; } return 0; } +fn kw_quote() +{ + let q, j, a, n; + next(); + q = grab(); + j = hole(); op(O_JMP); + a = here; + n = q[0] * 3 + 1; + wcopyto(q, n); + free(d*q); + setlit(j, here); + lit(a); + callname(s_qnew); + xcls = C_FRESH; + return 0; +} +// tokens of a string, as a token list +.u tokens(s) +{ + let n = 0, q; + src_push(2, s, 0, 0); + next(); + while (tk != T_EOF) { + if (n >= 2700) fatal("too many tokens"); + R_QBUF[n * 3] = tk; R_QBUF[n * 3 + 1] = tv; R_QBUF[n * 3 + 2] = tline; n++; + next(); + } + src_pop(); + q = new(n * 3 + 1); + q[0] = n; + wcopy(q + 1, R_QBUF, n * 3); + return u*q; +} +// feed a token list into the input: the parser reads it next, then carries +// on where it was. the list is freed once it has been read. +unchecked fn feed(.d q) { src_push(1, q, 1, 1); next(); return 0; } + +// ---- reading a unit and scanning it for signatures ---- +// a unit from stdin is read whole first (4 chars to a word), then scanned +// once for function signatures, so a function's tags are known before its first +// call even when it's defined further down. +unchecked fn readsrc() +{ + let cap = 4090, b = new(4090), n = 0, c, nb, w; + while (!eof()) { + c = getc() & 255; + if (c == 96) break; + if ((n >> 2) + 2 >= cap) { nb = new(cap + cap + 6); wcopy(nb, b, cap); free(b); b = nb; cap = cap + cap + 6; } + w = n >> 2; + if (!(n & 3)) b[w] = 0; + b[w] = b[w] | (c << ((n & 3) * 8)); + n++; + } + b[n >> 2] = b[n >> 2] & (pow2[(n & 3) * 8] - 1); // the 0 at the end + if ((n & 3) == 0) b[n >> 2] = 0; + b[(n >> 2) + 1] = 0; + return b; +} +// skip a <...> group (type parameters) +fn pskipangle() +{ + let dp = 0; + while (tk != T_EOF) { + if (is_op(s_lt)) dp++; + else if (is_op(s_gt)) dp--; + else if (is_op(s_shr)) dp = dp - 2; + next(); + if (dp <= 0) return 0; + } + return 0; +} +// at the name after fn: read the signature's tags and record them +fn prescan_fn(ret, n, o3) +{ + let np = 0, tags = 0, t = 0, dp, s; + next(); // the ( + if (!is_op(s_rp)) { + np = 1; dp = 1; + while (tk != T_EOF) { + if (is_op(s_lp)) dp++; + else if (is_op(s_rp)) { dp--; if (!dp) break; } + else if (dp == 1 && is_op(s_comma)) { if (np <= 12) tags = tags + (t << (np + np - 2)); np++; t = 0; } + else if (dp == 1 && is_op(s_dot)) { next(); if (is_id(s_m)) t = 1; else if (is_id(s_d)) t = 2; continue; } + next(); + } + if (np <= 12) tags = tags + (t << (np + np - 2)); + } + next(); + if (!is_op(s_lb) && !is_op(s_semi)) return 0; // not a function: an enhancement, a call in a parse word... + s = lookup(n); + if (s < 0 || s < ubase || skind[s] != K_FN || (sflag[s] & F_DEF)) s = fwdfn(n); + if (!(sflag[s] & F_SIG)) { ssig[s] = ret + np + np + tags * 64; sflag[s] = sflag[s] | F_SIG | F_PRE; } + if (o3) sflag[s] = sflag[s] | F_O3; + return 0; +} +fn prescan(kind, obj) +{ + let depth = 0, sp = panic, sn = nerr, pend = 0, po3 = 0, n, dep; + dry++; + src_push(kind, obj, 0, 0); + next(); + while (tk != T_EOF) { + if (is_op(s_lb)) depth++; + else if (is_op(s_rb)) { depth--; pend = 0; po3 = 0; } + else if (depth <= 0) { + if (is_op(s_semi)) { pend = 0; po3 = 0; } + else if (is_op(s_dot)) { peek(); if (pk_tk == T_ID && pk_tv == s_u) pend = 1; } + else if (is_id(s_o3)) po3 = 1; + else if (tk == T_ID) { + peek(); + if (pk_tk == T_OP && pk_tv == s_lp) { n = tv; next(); prescan_fn(pend, n, po3); pend = 0; po3 = 0; continue; } + if (pk_tk == T_OP && pk_tv == s_lt) { // list in a type, or first( a generic function + n = tv; next(); dep = 0; + while (tk != T_EOF) { + if (is_op(s_lt)) dep++; else if (is_op(s_gt)) dep--; else if (is_op(s_shr)) dep = dep - 2; + next(); + if (dep <= 0) break; + } + if (is_op(s_lp)) { prescan_fn(pend, n, po3); pend = 0; po3 = 0; } + continue; + } + } + } + next(); + } + src_pop(); + dry--; panic = sp; nerr = sn; + return 0; +} +// compile a whole unit from stdin +fn unit_stdin() +{ + let src = readsrc(); + prescan(3, src); + src_push(3, src, 0, 0); + next(); + unit_top(); + unit_end(ubase); + src_pop(); + free(src); + return 0; +} + +// ---- nested compiles ---- +// compile() and load() can run while another function is being compiled (from +// a parse word). the function context is saved, the new code is emitted in +// place with a jump around it, and then everything is put back. +fn ctx_push() +{ + let f = R_CTX + ctxsp * 24; + if (ctxsp >= 30) fatal("compiles nested too deeply"); + ctxsp++; + f[0] = nloc; f[1] = lbase; f[2] = fdepth; f[3] = fslots; f[4] = fchk; f[5] = fret; f[6] = live; + f[7] = bxdepth; f[8] = epichain; f[9] = fframe; f[10] = infn; f[11] = lsp; f[12] = loopbase; + f[13] = lv; f[14] = lvk; f[15] = lvi; f[16] = kw_expr; f[17] = xcls; f[18] = snaptop; + f[19] = fty; f[20] = ntp; f[21] = xty; wcopy(f + 22, tpn, 2); + return 0; +} +fn ctx_pop() +{ + let f; + ctxsp--; + f = R_CTX + ctxsp * 24; + nloc = f[0]; lbase = f[1]; fdepth = f[2]; fslots = f[3]; fchk = f[4]; fret = f[5]; live = f[6]; + bxdepth = f[7]; epichain = f[8]; fframe = f[9]; infn = f[10]; lsp = f[11]; loopbase = f[12]; + lv = f[13]; lvk = f[14]; lvi = f[15]; kw_expr = f[16]; xcls = f[17]; snaptop = f[18]; + fty = f[19]; ntp = f[20]; xty = f[21]; wcopy(tpn, f + 22, 2); + return 0; +} + +// ---- functions and the top level ---- +fn fn_begin(chk, ret) +{ + lbase = nloc; fdepth = 1; fslots = 0; fchk = chk && check_on; fret = ret; fty = 0; + live = 1; bxdepth = 0; epichain = 0; infn = 1; loopbase = lsp; lv = 0; lvk = LV_NONE; + return 0; +} +// fp -= frame size, then check the frame stack: past FBOT, reset fp and stop +fn prologue() +{ + let k; + lit(ZFP); op(O_LD); fframe = hole(); op(O_SUB); lit(ZFP); op(O_ST); + if (o3on) return 0; // o3 gave up the frame check + lit(ZFP); op(O_LD); lit(FBOT); op(O_LT); k = hole(); op(O_JZ); + lit(FTOP); lit(ZFP); op(O_ST); callname(s_fovf); + setlit(k, here); + return 0; +} +fn epilogue() +{ + lit(0); + fixall(epichain, here); + lit(ZFP); op(O_LD); lit(fslots); op(O_ADD); lit(ZFP); op(O_ST); op(O_RET); + setlit(fframe, fslots); + infn = 0; + nloc = lbase; + return 0; +} +fn body() +{ + expect(s_lb); + while (!is_op(s_rb)) { if (tk == T_EOF) die("missing } at the end of a function"); stmt(); } + unused_check(lbase); + if (fchk && live) { exit_vars(1, 1); if (fret) cerr("a function returning .u has to end with a return"); } + next(); + return 0; +} +// the rest of a function, after its return type and name: +// int f(int a, *node.m n, .d x, b) { ... } *list.u mk(int n) { ... } +// rty is 0 for fn (a plain word), ret is 1 for a .u return +fn fndef(chk, rty, ret, n) +{ + let s, np = 0, tags = 0, t, sig, i, pn[24], pt[24], pty[24], typed = 0, ft, o3f = o3nxt, lo, hi; + o3nxt = 0; + if (rty || ntp) typed = 1; + s = lookup(n); + if (s < 0 || s < ubase || skind[s] != K_FN || (sflag[s] & F_DEF)) { s = addsym(n, K_FN, 0); sflag[s] = 0; } + expect(s_lp); + if (!is_op(s_rp)) { + while (1) { + t = 0; + pty[np] = tword; + if (!is_op(s_dot) && ((tk == T_OP && (tv == s_star || tv == s_lk)) || (tk == T_ID && (tv == s_fn || istypename(tv))))) { + pty[np] = ptype(); typed = 1; + if (isagg(pty[np])) serr("structs and arrays are passed by pointer"); + } + if (accept(s_dot)) { // .m lends, .d takes over + if (tv == s_m) t = 1; else if (tv == s_d) t = 2; else die("a parameter tag is .m or .d"); + next(); + } + pn[np] = name(); + if (o3f && pty[np] == tword) serr("o3 parameters need a type"); + if (np >= 12 && t) die("only the first 12 parameters can be tagged"); + pt[np] = t; + if (np < 12) tags = tags + (t << (np + np)); + np++; + if (np >= 24) die("too many parameters"); + if (!accept(s_comma)) break; + } + } + expect(s_rp); + sig = ret + np + np + tags * 64; + if ((sflag[s] & F_SIG) && !(sflag[s] & F_PRE)) { if (ssig[s] != sig) serrn(n, "doesn't match its earlier declaration"); } + else if ((sflag[s] & F_UNTYPED) && (tags || ret)) serrn(n, "was called before its tags were declared; put a prototype above the first call"); + ssig[s] = sig; + sflag[s] = (sflag[s] | F_SIG) - (sflag[s] & F_PRE); + if (ntp) sflag[s] = sflag[s] | F_GEN; + if (typed) { + if (!rty) rty = tword; + ft = tfn(rty, np, pty, sig); + if (stype[s] && stype[s] != ft) serrn(n, "doesn't match its earlier declaration"); + stype[s] = ft; + } + if (accept(s_semi)) { ntp = 0; return s; } + if (sflag[s] & F_DEF) serrn(n, "is defined twice"); + sval[s] = here; + sflag[s] = sflag[s] | F_DEF; + fixall(sfix[s], here); sfix[s] = 0; + fn_begin(chk, ret); + if (typed) fty = rty; + no3pend = 0; + if (o3f) { o3on = 1; o3reset(); sflag[s] = sflag[s] | F_O3; } + prologue(); + i = 0; + while (i < np) { + t = newloc(pn[i]); fslots++; + ltype[t] = pty[i]; + if (o3f) { ltag[t] = 16; o3promote(t); } + if (fchk && pt[i] == 1) lstate[t] = S_BORROW; + if (fchk && pt[i] == 2) { lstate[t] = S_M; lown[t] = 1; } + i++; + } + i = np; + while (i) { i--; laddr(i); op(O_ST); } + if (o3f) { // the region starts here: a jump, so the cpu enters it + lo = here + 2; lit(lo); op(O_JMP); + i = 0; while (i < np) { t = lbase + i; o3mkcache(t); o3refresh(t); i++; } + } + body(); + hi = here; + epilogue(); + if (o3f) { o3on = 0; o3region(lo, hi, o3sl, o3rg, no3sl); } + o3flush(); + ntp = 0; + return s; +} +// let g = 1, buf[10]; int g = 3; [10]int buf; point origin; +fn gdecl(typed) { next(); return gdecl2(0, 0, name()); } +fn gdecl2(t, tg, n) +{ + let a, k, s; + if (tg) serr("tags go on locals and parameters, not globals"); + if (t == tvoid) serr("a variable can't be void"); + while (1) { + if (accept(s_lk)) { + k = cexpr(); expect(s_rk); + s = addsym(n, K_GARR, here); + if (!t) t = tword; + stype[s] = tarr(t, k); + k = tsz(stype[s]); + while (k) { emit(0); k--; } + } else if (t && isagg(t)) { + s = addsym(n, K_GARR, here); stype[s] = t; + k = tsz(t); + while (k) { emit(0); k--; } + } else { + a = here; + s = addsym(n, K_GVAR, a); + if (t) stype[s] = t; + emit(0); + if (accept(s_eq)) { + if (tk == T_STR) { *(a + hdelta) = here; k = tv; while (*k) { emit(*k); k++; } emit(0); next(); } + else *(a + hdelta) = cexpr(); + } + } + if (!accept(s_comma)) break; + n = name(); + } + expect(s_semi); + return 0; +} +fn gconst() +{ + let n; + next(); + while (1) { n = name(); expect(s_eq); addsym(n, K_CONST, cexpr()); if (!accept(s_comma)) break; } + expect(s_semi); + return 0; +} +// enhance [n] name(a, b) micro "recipe" [else fn]; n outputs, 1 if left out +// enhance [n] name(a, b) vm { asm } [else fn]; +// lays out the descriptor the install opcode reads, and wires it in right +// away (an image wires its own in when it boots). +fn enhdef() +{ + let n, np = 0, nout = 1, dsc, na, pay, len = 0, kind = 0, i, p, s, slot, r, pn[8]; + next(); + if (tk == T_NUM) { if (tv < 0 || tv > 8) serr("an enhancement has at most 8 outputs"); nout = tv; next(); } // enhance 2 divmod(a, b) + n = name(); + expect(s_lp); + if (!is_op(s_rp)) { while (1) { if (np == 8) die("an enhancement takes at most 8 inputs"); pn[np] = name(); np++; if (!accept(s_comma)) break; } } + expect(s_rp); + dsc = here; i = 0; while (i < 7) { emit(0); i++; } + na = here; i = 0; while (i < np) { emit(0); i++; } + i = 0; + while (i < np) { *(na + i + hdelta) = here; p = pn[i]; while (*p) { emit(*p); p++; } emit(0); i++; } + if (accept(s_micro)) { + pay = here; + if (tk != T_STR) die("expected the recipe as a string"); + while (tk == T_STR) { p = tv; while (*p) { emit(*p); p++; } next(); } + emit(0); + } else if (accept(s_vm)) { pay = here; asmblock(); op(O_RET); len = here - pay; kind = 1; } + else die("expected micro or vm"); + slot = nenh; + if (slot >= 512) fatal("all 512 enhancement slots are taken"); + nenh++; + p = dsc + hdelta; + p[0] = slot; p[1] = kind; p[2] = np; p[3] = nout; p[4] = pay; p[5] = len; p[6] = na; + s = addsym(n, K_ENH, slot); + ssig[s] = np + nout * 16 + dsc * 256; + if (accept(s_else)) { r = lookup(tv); if (r < 0) r = fwdfn(tv); next(); sfix[s] = r + 1; } + expect(s_semi); + if (!hdelta) { + r = install(dsc); + if (r < 0) { + if (!sfix[s]) serrn(n, "the enhancement didn't install (see install's codes in core.c)"); + sflag[s] = sflag[s] | F_FALLBACK; + } + } + return 0; +} +// after a syntax error at the top level: skip to something that starts a declaration +fn topsync() +{ + let depth = 0; + while (tk != T_EOF) { + if (is_op(s_lb)) depth++; + else if (is_op(s_rb)) { depth--; if (depth < 0) depth = 0; } + else if (!depth && tk == T_ID && (tv == s_fn || tv == s_var || tv == s_const || tv == s_struct || tv == s_type + || tv == s_enhance || tv == s_typecheck || tv == s_unchecked || tv == s_immediate || tv == s_o3)) break; + next(); + } + panic = 0; + return 0; +} +// after a function's name: declares its type parameters. any the return +// type used were taken on trust; now they get their real numbers +fn genparams() +{ + let i, j, n = 0, ps[8], bs[8]; + if (!is_op(s_lt)) { if (ntp) serrn(tpn[0], "isn't a type"); ntp = 0; return 0; } + next(); + while (1) { if (n >= 8) fatal("too many type parameters"); ps[n] = name(); n++; if (!accept(s_comma)) break; } + closeangle(); + wfill(bs, 0, 8); + i = 0; + while (i < ntp) { + j = 0; + while (j < n && ps[j] != tpn[i]) j++; + if (j == n) serrn(tpn[i], "isn't a type"); else bs[i] = tvar(j, ps[j]); + i++; + } + if (ntp && dty) dty = subst(dty, bs); + wcopy(tpn, ps, n); ntp = n; + return 0; +} +// the return type of a function or the type of a global, with unknown names allowed for now +fn rettype() { tvauto = 1; dtype(); tvauto = 0; return 0; } +fn fndone(s, imm) +{ + let k; + ntp = 0; + if (imm) { + if (hdelta) serr("parse words can't be defined while building an image"); + k = addsym(sname[s], K_KW, sval[s]); + sflag[k] = F_DEF | F_STMT | F_EXPR | F_TOP; + } + return 0; +} +fn topitem() +{ + let chk = 1, imm = 0, s, k, n; + if (panic) return topsync(); + if (is_id(s_unchecked)) { + next(); + if (accept(s_semi)) { check_on = 0; return 0; } // unchecked; switches the analyzer off from here on + chk = 0; + } + if (is_id(s_immediate)) { next(); imm = 1; } + o3nxt = 0; + if (is_id(s_o3)) { next(); o3nxt = 1; } + ntp = 0; + if (is_id(s_fn)) { peek(); if (!(pk_tk == T_OP && pk_tv == s_lp)) { next(); n = name(); dty = 0; genparams(); return fndone(fndef(chk, 0, 0, n), imm); } } + if (!chk || imm || o3nxt) { // has to be a function, with a return type + rettype(); n = name(); genparams(); + if (dtag && dtag != S_U) serr("the only return tag is .u"); + if (!is_op(s_lp)) { die("expected a function"); return 0; } + return fndone(fndef(chk, dty, dtag == S_U, n), imm); + } + if (is_id(s_var)) return gdecl(0); + if (is_id(s_struct)) { kw_struct(); return 0; } + if (is_id(s_type)) return kw_type(); + if (is_id(s_typecheck)) return kw_typecheck(); + if (is_id(s_enhance)) return enhdef(); + if (is_id(s_const)) return gconst(); + if (tk == T_ID) { + s = lookup(tv); + if (s >= 0 && skind[s] == K_KW && (sflag[s] & F_TOP)) { kwcall(s, 0); return 0; } + } + if (tstart() || is_op(s_star) || (tk == T_ID && lookup(tv) < 0)) { // int f(...) { }, T first(...) { }, int g = 1; + rettype(); n = name(); + if (is_op(s_lp) || is_op(s_lt)) { + genparams(); + if (dtag && dtag != S_U) serr("the only return tag is .u"); + return fndone(fndef(chk, dty, dtag == S_U, n), 0); + } + if (ntp) { serrn(tpn[0], "isn't a type"); ntp = 0; } + return gdecl2(dty, dtag, n); + } + die("expected a declaration"); + return 0; +} +fn unit_top() { while (tk != T_EOF) topitem(); return 0; } +fn unit_end(from) +{ + let i = from; + panic = 0; + while (i < nsym) { + if (skind[i] == K_FN && !(sflag[i] & F_DEF) && sfix[i]) { tline = sval[i]; serrn(sname[i], "is used but never defined"); } + i++; + } + if (nerr) giveup(); + return 0; +} + +// compile a token list as the body of a new function, or (top) as top-level +// declarations. returns the entry address of the function. +unchecked fn compile_list(q, top) +{ + let hop = -1, entry = 0, from = nsym; + ctx_push(); + if (infn) { hop = hole(); op(O_JMP); } + if (top) prescan(1, q); + src_push(1, q, 0, 0); + next(); + if (top) unit_top(); + else { + entry = here; + fn_begin(1, 0); + prologue(); + while (tk != T_EOF) stmt(); + unused_check(lbase); + if (fchk && live) exit_vars(1, 1); + epilogue(); + } + src_pop(); + unit_end(from); + if (hop >= 0) setlit(hop, here); + if (here > ptop) ptop = here; + ctx_pop(); + return entry; +} +fn compile(q) { return compile_list(q, 0); } +fn load(q) { compile_list(q, 1); return 0; } +fn eval(q) { let f = compile_list(q, 0); return f(); } +fn evals(s) { let q = tokens(s), r = eval(q); free(d*q); return r; } + +// ---- symbol blob ---- +// an image ends with its own symbol table so the genome knows its names: +// kind, value, signature, name chars, 0 ... then a 0 kind. +fn blob_emit(from) +{ + let i = from, p; + while (i < nsym) { + if ((skind[i] >= K_FN && skind[i] <= K_CONST) || skind[i] == K_ENH) { + emit(skind[i]); emit(sval[i]); emit(ssig[i]); + p = sname[i]; + while (*p) { emit(*p); p++; } + emit(0); + } + i++; + } + emit(0); + return 0; +} +fn blob_load(p) +{ + let s, n; + while (*p) { + n = intern(p + 3); + s = addsym(n, p[0], p[1]); + ssig[s] = p[2]; + sflag[s] = F_DEF | F_SIG; + if (p[0] == K_ENH) { if (p[1] >= nenh) nenh = p[1] + 1; install(p[2] >> 8); } + p = p + 3 + strlen(p + 3) + 1; + } + return 0; +} +// compile the rest of stdin as a genome image that will run at org, writing +// it to dst. only the source's own names are visible while it compiles. +// returns the image length in words. +fn build_image(org, dst) +{ + let sh = here, sd = hdelta, sl = hlimit, sub = ubase, svl = vislo, sns = nsym, sen = nenh, n; + ubase = nsym; vislo = nsym; nenh = 0; + here = org; hdelta = dst - org; hlimit = org + 0x30000; + lit(FTOP); lit(ZFP); op(O_ST); fnref(fwdfn(s_start)); op(O_JMP); emit(0); emit(0); + unit_stdin(); + *(dst + 5) = here; + blob_emit(ubase); + *(dst + 6) = here; + n = here - org; + popsyms(sns); + ubase = sub; vislo = svl; here = sh; hdelta = sd; hlimit = sl; nenh = sen; + return n; +} + +// ---- setup ---- +fn gc_roots() +{ + markrange(0, ptop); + if (!hdelta && here > ptop) markrange(ptop, here); // globals of a unit still compiling + markrange(GORG, GORG[6]); + markrange(R_SRC, R_SRC + 1024); + markrange(*ZFP, FTOP); + markrange(DSLO, DSHI); + return 0; +} +fn zc_init() +{ + pooltop = R_POOL; + wfill(R_IH, 0, IHN); + nsym = 0; nfixu = 0; fixfree = 0; nbin = 0; nun = 0; nasg = 0; nkop = 0; seqtop = 0; + lsp = 0; ctxsp = 0; snaptop = 0; nloc = 0; lbase = 0; + s_lp = intern("("); s_rp = intern(")"); s_lb = intern("{"); s_rb = intern("}"); + s_lk = intern("["); s_rk = intern("]"); s_comma = intern(","); s_semi = intern(";"); + s_eq = intern("="); s_arrow = intern("->"); s_inc = intern("++"); s_dec = intern("--"); + s_star = intern("*"); s_amp = intern("&"); s_colon = intern(":"); s_hash = intern("#"); + s_minus = intern("-"); s_dot = intern("."); s_q = intern("?"); s_case = intern("case"); s_default = intern("default"); s_andand = intern("&&"); s_oror = intern("||"); + s_fn = intern("fn"); s_var = intern("let"); s_const = intern("const"); s_else = intern("else"); + s_as = intern("as"); s_unchecked = intern("unchecked"); s_immediate = intern("immediate"); + s_m = intern("m"); s_d = intern("d"); s_u = intern("u"); + s_start = intern("start"); s_main = intern("main"); s_free = intern("free"); + s_own = intern("own"); s_qnew = intern("qnew"); s_fovf = intern("frame_overflow"); + s_enhance = intern("enhance"); s_micro = intern("micro"); s_vm = intern("vm"); + return 0; +} +// operators and keywords. these are plain tables in memory: a program can +// add operators, change precedences or replace keywords with its own. +fn zc_tables() +{ + add_unop("!", "0 $ 0 ~ - | 31 > 1 +"); // index 0 + add_unop("!!", "0 $ 0 ~ - | 31 > 0 ~ -"); // index 1: normalize to 0 or 1 + add_unop("~", "-1 ^"); // index 2 + add_unop("-", "0 ~ -"); + add_binop("*", 10, "*"); // the first few indexes are used by cexpr + add_binop("/", 10, "idiv"); // native divide, wired in by rt.zc + add_binop("%", 10, "imod"); + add_binop("+", 9, "+"); // 3 + add_binop("-", 9, "-"); // 4 + add_binop("<<", 8, "ishl"); // 5 + add_binop(">>", 8, ">"); // 6 + add_binop("<", 7, "<"); + add_binop(">", 7, "~ <"); + add_binop("<=", 7, "~ < 1 ^"); + add_binop(">=", 7, "< 1 ^"); + add_binop("==", 6, "- 0 $ 0 ~ - | 31 > 1 +"); + add_binop("!=", 6, "- 0 $ 0 ~ - | 31 > 0 ~ -"); + add_binop("&", 5, "&"); // 13 + add_binop("^", 4, "^"); + add_binop("|", 3, "|"); // 15 + add_binop("&&", -1, 0); + add_binop("||", -2, 0); + addkop(s_eq); addkop(s_arrow); addkop(s_inc); addkop(s_dec); + add_kw("let", kw_var, F_STMT); + add_kw("if", kw_if, F_STMT); + add_kw("while", kw_while, F_STMT); + add_kw("for", kw_for, F_STMT); + add_kw("switch", kw_switch, F_STMT); + add_kw("break", kw_break, F_STMT); + add_kw("continue", kw_continue, F_STMT); + add_kw("return", kw_return, F_STMT); + add_kw("unchecked", kw_unchecked, F_STMT); + add_kw("asm", kw_asm, F_STMT | F_EXPR); + add_kw("quote", kw_quote, F_EXPR); + add_kw("sizeof", kw_sizeof, F_EXPR); + add_kw("typeof", kw_typeof, F_EXPR); + add_kw("o3", kw_o3, F_STMT); + add_kw("len", kw_len, F_EXPR); + add_kw("cap", kw_cap, F_EXPR); + add_kw("push", kw_push, F_EXPR); + add_kw("pop", kw_pop, F_EXPR); + add_kw("resize", kw_resize, F_EXPR); + return 0; +} +fn start() +{ + let s, f; + heap_init(); + zc_init(); + blob_load(GORG[5]); + zc_tables(); + types_init(); + here = 0; hdelta = 0; hlimit = HEAP_LO; ptop = 0; + ubase = nsym; vislo = 0; + unit_stdin(); + if (here > ptop) ptop = here; + s = lookup(s_main); + if (s >= 0 && skind[s] == K_FN && (sflag[s] & F_DEF)) { f = sval[s]; f(); } + halt(); + return 0; +} diff --git a/docs/history/17-o3-boundaries/code/src/rt.zc b/docs/history/17-o3-boundaries/code/src/rt.zc new file mode 100644 index 0000000..63409b3 --- /dev/null +++ b/docs/history/17-o3-boundaries/code/src/rt.zc @@ -0,0 +1,291 @@ +// ============================================================================ +// rt.zc: the runtime half of the genome. memory map, i/o, arithmetic helpers, +// the heap (size-class free lists) and the mark-sweep collector. +// ============================================================================ + +// ---- memory map (words) ---- +const MW = 0x100000; // 1m words of memory +const HEAP_LO = 0x40000; // heap: 0x40000 .. 0xA0000 +const HEAP_HI = 0xA0000; +const ZFP = 0xA0000; // frame pointer word, used by every prologue +const GORG = 0xC0000; // the genome (this code) lives here +const FBOT = 0xE0000; // frame stack: FBOT .. FTOP, grows down +const FTOP = 0xF0000; +const RS0 = 0xF0000; // return stack grows up from here +const MARK0 = 0xF8000; // mark stack for the collector +const MARKN = 0x4000; +const DSLO = 0xFC000; // data stack: DSLO .. MW-16, grows down +const DSHI = 0xFFFF0; +const MAGIC = 0x2B7E1516; + +// ---- virtual enhancements ---- +// install(desc) wires a custom opcode into the cpu (the % opcode). these three +// give the language native division and left shift (zdiv, zmod and zshl +// below are the same thing done in software). they come first, since the +// compiler resolves / % and << to them by name. +fn install(desc) { return asm { desc @ % }; } +enhance idiv(a, b) micro "a = sdiv a b"; +enhance imod(a, b) micro "a = srem a b"; +enhance ishl(a, b) micro "a = shl a b"; +// for o3: an o3 variable's address (fp + k), and a bounds check (1 if i isn't in 0..n-1) +enhance lfa(k) micro "r1 = ld 655360; k = add r1 k"; +enhance outside(i, n) micro "i = ltu i n; i = xor i 1"; + +// ---- i/o ---- +fn putc(ch) { asm { ch @ . } return 0; } +fn getc() { return asm { , }; } +fn eof() { return asm { ? }; } +fn halt() { asm { q } return 0; } +fn puts(s) { let i = 0; while (s[i]) { putc(s[i]); i++; } return 0; } +fn putn(n) +{ + let b[12], i = 0; + if (n < 0) { putc('-'); n = 0 - n; } + if (n < 0) { puts("2147483648"); return 0; } + while (1) { b[i++] = '0' + n % 10; n = n / 10; if (!n) break; } + while (i) putc(b[--i]); + return 0; +} +fn strlen(s) { let n = 0; while (s[n]) n++; return n; } +fn streq(a, b) { let i = 0; while (a[i] && a[i] == b[i]) i++; return a[i] == b[i]; } +fn wcopy(dst, src, n) { let i = 0; while (i < n) { dst[i] = src[i]; i++; } return 0; } +fn wfill(dst, v, n) { let i = 0; while (i < n) { dst[i] = v; i++; } return 0; } + +// ---- arithmetic the hardware doesn't have ---- +let pow2[32]; +let zrem; +fn zudiv(n, dv) +{ + let q = 0, r = 0, i = 30; + if (n < dv) { zrem = n; return 0; } + while (i >= 0) { + r = r + r + ((n >> i) & 1); + q = q + q; + if (r >= dv) { r = r - dv; q++; } + i--; + } + zrem = r; + return q; +} +fn zdiv(a, b) +{ + let s = 0, q; + if (b == 0) { zrem = a; return 0; } + if (a < 0) { a = 0 - a; s = 1; } + if (b < 0) { b = 0 - b; s = s ^ 1; } + q = zudiv(a, b); + if (s) return 0 - q; + return q; +} +fn zmod(a, b) { let neg = a < 0; if (b == 0) return a; zdiv(a, b); if (neg) return 0 - zrem; return zrem; } +fn zshl(a, b) { return a * pow2[b & 31]; } + +// ---- heap ---- +// a buddy allocator. the heap is cut into aligned power-of-two chunks, every +// block is a power of two words, and a freed block merges with its buddy +// (the other half of the block they were split from) whenever that one is +// free too, so freed space can be reused at any size. every block has a 6 +// word header before the pointer the program sees: +// p[-6] block size p[-5] size class p[-4] flags (0 free, 1 live, 3 marked) +// p[-3] p ^ MAGIC p[-2] owner (the container that owns it, or 0) +// p[-1] its type (a type record from new(T), 0 from new(n), -(n + 1) from o3's new_raw) +// free blocks sit on a doubly linked list per class, linked through p[0], p[1]. +const HDR = 6; +let hbump, freel[32], gc_runs, gc_freed, gc_auto = 1; + +// the size of the top-level chunk holding heap offset off +fn topsize(off) +{ + let h = HEAP_HI - HEAP_LO, base = 0, sz; + while (h) { + sz = 1; + while (sz + sz <= h) sz = sz + sz; + if (off < base + sz) return sz; + base = base + sz; h = h - sz; + } + return 0; +} +fn fpush(b, k) { b[2] = 0; b[6] = freel[k]; b[7] = 0; if (freel[k]) freel[k][7] = b; freel[k] = b; return 0; } +fn funlink(b, k) +{ + if (b[7]) b[7][6] = b[6]; else freel[k] = b[6]; + if (b[6]) b[6][7] = b[7]; + return 0; +} +fn mkblock(b, k) { b[0] = pow2[k]; b[1] = k; b[2] = 0; b[3] = 0; b[4] = 0; b[5] = 0; return b; } +fn heap_init() +{ + let i = 1, b = HEAP_LO, sz, k; + pow2[0] = 1; + while (i < 32) { pow2[i] = pow2[i - 1] + pow2[i - 1]; i++; } + hbump = HEAP_HI; + wfill(freel, 0, 32); + while (b < HEAP_HI) { + sz = topsize(b - HEAP_LO); k = 0; + while (pow2[k] < sz) k++; + fpush(mkblock(b, k), k); + b = b + sz; + } + return 0; +} +// a free block of class k, split down from a bigger one if needed; 0 if none +fn balloc(k) +{ + let j = k, b, h; + while (j < 32 && !freel[j]) j++; + if (j >= 32) return 0; + b = freel[j]; + funlink(b, j); + while (j > k) { j--; h = b + pow2[j]; fpush(mkblock(h, j), j); } + mkblock(b, k); + return b; +} +let fend; // end of the block bfree last made, for the sweep +// put block b back, merging it with its buddy as long as it can +fn bfree(b) +{ + let k = b[1], sz = pow2[k], off, bud; + while (1) { + off = b - HEAP_LO; + if (sz >= topsize(off)) break; + bud = HEAP_LO + (off ^ sz); + if (bud[2] != 0 || bud[1] != k || bud[0] != sz) break; + funlink(bud, k); + if (bud < b) { b[0] = 0; b[3] = 0; b = bud; } // the upper header is inside the merged block now + else { bud[0] = 0; bud[3] = 0; } + k++; sz = sz + sz; + mkblock(b, k); + } + fpush(mkblock(b, k), k); + fend = b + sz; + return 0; +} +fn isobj(x) { return x >= HEAP_LO + HDR && x < hbump && x[-3] == (x ^ MAGIC) && (x[-4] & 1); } + +// every prologue checks the frame stack; on overflow it resets the frame +// pointer (so this can run at all) and stops +fn frame_overflow() { puts("zy: frame stack overflow\n"); halt(); return 0; } +fn oom() { puts("zc: out of heap memory\n"); halt(); return 0; } +// n words, untyped and not zeroed: what new gives o3 code. the type word +// keeps -(n + 1), so fatten (in the compiler) can type it later +unchecked .u new_raw(n) +{ + let need = n + HDR, k = 3, b, p; + if (n < 0) need = HDR; + while (pow2[k] < need) k++; + b = balloc(k); + if (!b && gc_auto) { gc(); b = balloc(k); } + if (!b) oom(); + p = b + HDR; + b[2] = 1; b[3] = p ^ MAGIC; b[4] = 0; b[5] = 0 - n - 1; + return p; +} +// n words typed t, zeroed +unchecked .u new_t(n, t) +{ + let p = new_raw(n); + p[-1] = t; + wfill(p, 0, p[-6] - HDR); + return p; +} +unchecked .u new(n) { return new_t(n, 0); } +fn osize(p) { if (!isobj(p)) return 0; return p[-6] - HDR; } +fn typeof_obj(p) { if (!isobj(p) || p[-1] < 0) return 0; return p[-1]; } + +// frees p and, recursively, every object p owns +fn release(p) +{ + let b = p - HDR, n = b[0] - HDR, i = 0, w; + b[2] = 2; // going: not live, and not free yet either, so a child's buddy check can't merge into it + while (i < n) { w = p[i]; if (isobj(w) && w[-2] == p) release(w); i++; } + bfree(b); + return 0; +} +unchecked fn free(.d p) { if (isobj(p)) release(p); return 0; } +fn own(c, p) { if (isobj(c)) c[-2] = p; return c; } + +// ---- growable arrays ---- +// a []T is a handle: [len, cap, items, element size]. items is an object the +// handle owns; growing swaps it for a bigger one and moves its children over. +unchecked .u dnew(es, n, t) +{ + let h = new_t(4, t), c = n, it; + if (c < 4) c = 4; + it = new(c * es); + h[0] = n; h[1] = c; h[2] = own(it, h); h[3] = es; + return h; +} +unchecked fn dgrow(h, need) +{ + let c = h[1], it, old = h[2], i = 0, w, n; + if (need <= c) return 0; + while (c < need) c = c + c; + it = new(c * h[3]); + n = h[0] * h[3]; + wcopy(it, old, n); + while (i < n) { w = it[i]; if (isobj(w) && w[-2] == old) w[-2] = it; i++; } // the children move with the elements + h[2] = own(it, h); h[1] = c; + free(old); + return 0; +} +fn oob(i, n) { puts("zy: index "); putn(i); puts(" out of range (length "); putn(n); puts(")\n"); halt(); return 0; } +unchecked fn dat(h, i) { if (i < 0 || i >= h[0]) oob(i, h[0]); return h[2] + i * h[3]; } +unchecked fn dpush(h, .d x) { dgrow(h, h[0] + 1); h[2][h[0]] = x; own(x, h[2]); h[0] = h[0] + 1; return 0; } +unchecked fn dpop(h) { if (!h[0]) oob(-1, 0); h[0] = h[0] - 1; return h[2][h[0]]; } +unchecked fn dresize(h, n) +{ + let old = h[0] * h[3]; + if (n < 0) n = 0; + dgrow(h, n); + if (n * h[3] > old) wfill(h[2] + old, 0, n * h[3] - old); + h[0] = n; + return 0; +} +fn dlen(h) { return h[0]; } +fn dcap(h) { return h[1]; } + +// ---- mark-sweep ---- +// conservative and non-moving. roots: the program area (globals live +// between functions), the genome's globals, the compiler's input stack, +// the live part of the frame stack and the whole data stack region. +let ms_top, ms_over, gc_lo, gc_hi; +fn mark(w) +{ + if (!isobj(w) || (w[-4] & 2)) return 0; + w[-4] = w[-4] | 2; + if (ms_top < MARKN) { MARK0[ms_top] = w; ms_top++; } else ms_over = 1; + return 0; +} +fn markrange(a, b) { while (a < b) { mark(*a); a++; } return 0; } +fn drain() +{ + let p; + while (ms_top) { ms_top--; p = MARK0[ms_top]; markrange(p, p + p[-6] - HDR); } + return 0; +} +fn gc_roots(); +unchecked fn gc() +{ + let b, sz, n = 0; + ms_top = 0; ms_over = 0; + gc_roots(); + drain(); + while (ms_over) { // the mark stack overflowed: rescan marked objects + ms_over = 0; + b = HEAP_LO; + while (b < HEAP_HI) { if (b[2] == 3) markrange(b + HDR, b + b[0]); b = b + b[0]; drain(); } + } + b = HEAP_LO; + while (b < HEAP_HI) { + sz = b[0]; + if (b[2] == 1) { bfree(b); n++; b = fend; } // merging may swallow what follows + else { if (b[2] == 3) b[2] = 1; b = b + sz; } + } + gc_runs++; + gc_freed = gc_freed + n; + return n; +} +// number of live objects, for tests +fn live_objects() { let b = HEAP_LO, n = 0; while (b < HEAP_HI) { if (b[2] & 1) n++; b = b + b[0]; } return n; } +// the biggest block that's free right now, in words +fn largest_free() { let k = 31; while (k > 0 && !freel[k]) k--; if (!freel[k]) return 0; return pow2[k]; } diff --git a/docs/history/17-o3-boundaries/code/test.sh b/docs/history/17-o3-boundaries/code/test.sh new file mode 100755 index 0000000..533042d --- /dev/null +++ b/docs/history/17-o3-boundaries/code/test.sh @@ -0,0 +1,90 @@ +#!/bin/bash +# full check: bootstraps the compiler from zc0, verifies the fixpoint, then runs +# the examples and tests on the portable interpreter (host), the arm64 jit under +# qemu, and the llvm backend. needs gcc; the jit half needs aarch64-linux-gnu-gcc +# and qemu-aarch64, the llvm half needs llvm-config and libLLVM (each is skipped +# if missing). +cd "$(dirname "$0")"; T=$(mktemp -d); trap 'rm -rf $T' EXIT +ok=0; bad=0 +chk(){ if [ "$1" = "$2" ]; then echo "ok $3"; ok=$((ok+1)); else echo "FAIL $3: got [$1]"; bad=$((bad+1)); fi; } +src(){ cat src/rt.zc src/compiler.zc; } +selfc(){ (cat tools/build.zc; printf '`'; src) | $1 > $2; } + +# bootstrap: zc0 (c) -> g0, g0 compiles compiler.zc -> g1, g1 compiles it -> g2 +gcc -O1 -w zc0.c -o $T/zc0 && gcc -O1 -w mkgenome.c -o $T/mkg || exit 1 +mkdir $T/s0 $T/s1 +cp core.c io_linux.c $T/s0/; cp core.c io_linux.c $T/s1/ +src | $T/zc0 > $T/s0/genome.h || exit 1 +gcc -O2 -w -DJTYPE=int $T/s0/core.c $T/s0/io_linux.c -o $T/zy0 +selfc $T/zy0 $T/g1.bin; $T/mkg < $T/g1.bin > $T/s1/genome.h +gcc -O2 -w -DJTYPE=int $T/s1/core.c $T/s1/io_linux.c -o $T/zy1 +selfc $T/zy1 $T/g2.bin +cmp -s $T/g1.bin $T/g2.bin && chk 1 1 "fixpoint: the compiler compiles itself to the same $(($(stat -c %s $T/g1.bin) / 4)) words" || chk 0 1 "fixpoint" +cmp -s $T/s1/genome.h genome.h && chk 1 1 "genome.h is up to date" || chk 0 1 "genome.h is stale (cp the bootstrap output over it)" + +run(){ # $1 = vm command, $2 = label + local vm="$1" l="$2" + chk "$($vm < examples/hello.zc)" "hello, world!" "$l hello" + chk "$($vm < examples/fact.zc | tr '\n' '/')" "1101110101111100000000/3628800/" "$l factorial" + chk "$( (cat examples/echo.zc; printf '`abc\n') | $vm)" "abc" "$l echo after the backtick" + chk "$($vm < examples/selfpatch.zc)" "ABC" "$l self-patching code" + chk "$($vm < examples/lists.zc | tr '\n' '/')" "[0 1 4 9 16 25]/live: 7/after the block: 0/" "$l ownership frees everything" + chk "$( (cat examples/repl.zc; printf '`fn sq(x) { return x * x; }`putn(sq(7)); putc(10);`puts("hi\\n");') | $vm | tr '\n' '/')" "49/hi/" "$l repl on the resident compiler" + chk "$($vm < examples/typed.zc | tr '\n' '/')" "last: 4/point is 2 words: x int, y int/vec<*point> is 3 words: len int, cap int, items **point/push is bool(*vec, T.d)/the heap says v is a vec, and v.items[0] is a point/" "$l typed example" + chk "$($vm < examples/wiring.zc)" "16 1234 25 4/9" "$l enhancement example" + chk "$($vm < tests/basics.zc | tr '\n' '/')" "285 610 42 512/hey 65 -3 -2 1024 -8/12456/757/9 100-101/5/ok/" "$l language basics" + chk "$($vm < tests/umd.zc)" "100 6 0 15 99 5 5" "$l u m d lifetimes" + chk "$($vm < tests/gc.zc)" "1000 1 900 1 199900" "$l mark-sweep collector" + chk "$($vm < tests/meta.zc | tr '\n' '/')" "1024 19/unless ok/rrr/15 42/7/42/7/144/" "$l parse words, quote, eval, load, operator tables" + chk "$($vm < tests/misc.zc | tr '\n' '/')" "45/54321/ok/" "$l compile-time eval, patched mnemonics, unchecked" + chk "$($vm < tests/types.zc | tr '\n' '/')" "12 5 2 15 56 42 7 14 3/rect *rect *list *list.u(int) T(*list) fn(word.d)/a:point@0 b:point@2 tag:int@4 /point 1 1/" "$l types, generics, reflection" + chk "$($vm < tests/typeok.zc)" "14" "$l a program the typechecker accepts" + chk "$($vm < tests/typeoff.zc)" "6" "$l the typechecker is off by default" + chk "$($vm < tests/enhance.zc)" "100 0 343 3,2 5 -2 0" "$l virtual enhancements" + chk "$($vm < tests/joins.zc)" "0 0 5 80 8 3 9 0" "$l joins free what one path gave away; switch, ?:, no prototypes" + chk "$($vm < tests/buddy.zc)" "131072 262144 1 0" "$l buddy allocator merges freed blocks" + chk "$($vm < tests/borrowmove.zc)" "6" "$l storing into a borrowed object moves it" + chk "$($vm < tests/manyerrors.zc | tr '\n' '/')" "zc: line 3: expected an expression/zc: line 4: x: m object is never used/zc: line 5: type error: initializer: expected *char, got int/zc: line 6: expected ;/zc: 4 errors/" "$l several errors in one run" + chk "$($vm < tests/overflow_return.zc 2>&1)" "zy: return stack overflow" "$l return stack overflow is caught" + chk "$($vm < tests/overflow_data.zc 2>&1)" "zy: data stack overflow" "$l data stack overflow is caught" + chk "$($vm < tests/overflow_frame.zc 2>&1)" "zy: frame stack overflow" "$l frame stack overflow is caught" + chk "$($vm < examples/mandel.zc 2>&1 | md5sum | cut -c1-32)" "f41aecbf2c844168050413cfa832db0e" "$l example: mandelbrot with a fixed-point enhancement" + chk "$($vm < examples/bf.zc 2>&1 | md5sum | cut -c1-32)" "223570983752b17f7ece32e93d5e19e4" "$l example: brainfuck compiled at runtime" + chk "$($vm < examples/dump.zc 2>&1 | md5sum | cut -c1-32)" "757774b00f6b3cd0bfbcba66448a8619" "$l example: struct printer through reflection" + chk "$($vm < examples/tree.zc 2>&1 | md5sum | cut -c1-32)" "b86a2e4e9e31acc294a4ddf86be863f2" "$l example: owning tree frees itself" + chk "$($vm < examples/lang.zc 2>&1 | md5sum | cut -c1-32)" "aa1a18c78b5200129c7c2b315f163d28" "$l example: pipe operator, unless, assert, times" + chk "$($vm < examples/staged.zc 2>&1 | md5sum | cut -c1-32)" "6f790a125c48b0cc7a897cee73ee5627" "$l example: staged code compiled while running" + chk "$($vm < examples/quine.zc 2>&1 | md5sum | cut -c1-32)" "c868ecb9dd7b05224a1bd8a4c32ae788" "$l example: quine" + chk "$($vm < examples/primes.zc 2>&1 | md5sum | cut -c1-32)" "814faffc70dad0ae520c0671c33e73d4" "$l example: o3 prime sieve" + chk "$($vm < examples/life.zc 2>&1 | md5sum | cut -c1-32)" "aefb036b6f9ad1dadfc3e6bb47a6885d" "$l example: game of life" + chk "$($vm < tests/arrays.zc | tr '\n' '/')" "100 128 9801 9801 99 3 4 12 19 8 19 28/0/" "$l growable and fixed arrays" + chk "$($vm < tests/o3.zc)" "3142 42 43 9 57 98781 520625 10192 -18782 1152" "$l o3 regions: loops, calls, recursion, switch, arrays, more vars than registers" + chk "$($vm < tests/o3scopes.zc)" "116" "$l o3 registers shared by scopes, cached fixed arrays" + chk "$($vm < tests/o3fat.zc)" "node node 3 box node [0]int" "$l o3 data gets fattened on the way out" + chk "$($vm < tests/o3_bounds.zc 2>&1)" "zy: index 7 out of range (length 5)" "$l o3 bounds check" + local r; r=$(tests/runerr.sh "$vm" tests/errors | tail -1) + chk "$r" "rejected: $(ls tests/errors/*.zc | wc -l) passed, 0 failed" "$l analyzer rejects every bad program" + r=$(tests/runerr.sh "$vm" tests/typeerrors | tail -1) + chk "$r" "rejected: $(ls tests/typeerrors/*.zc | wc -l) passed, 0 failed" "$l typechecker rejects every bad program" + r=$(tests/runerr.sh "$vm" tests/langerrors | tail -1) + chk "$r" "rejected: $(ls tests/langerrors/*.zc | wc -l) passed, 0 failed" "$l o3 and array misuse is rejected" +} +gcc -O2 -w -DJTYPE=int core.c io_linux.c -o $T/zy +run $T/zy "interp" +chk "$(ZYTRACE=1 $T/zy < tests/o3.zc 2>&1 | grep -c 'micro-ops$')" "8" "every o3 region lifts" +if command -v aarch64-linux-gnu-gcc >/dev/null && command -v qemu-aarch64 >/dev/null; then + aarch64-linux-gnu-gcc -O2 -w -DJTYPE=int -static core.c io_linux.c -o $T/zyj || exit 1 + printf '#!/bin/sh\nexec qemu-aarch64 %s "$@"\n' $T/zyj > $T/jit; chmod +x $T/jit + run $T/jit "jit" + chk "$(ZYTRACE=1 $T/jit < tests/o3.zc 2>&1 | grep -c 'bytes of arm64')" "8" "jit compiles every o3 region to arm64" + selfc $T/jit $T/gj.bin; cmp -s $T/g1.bin $T/gj.bin && chk 1 1 "jit self-compile matches" || chk 0 1 "jit self-compile matches" +else echo "skip jit (no aarch64 toolchain or qemu)"; fi +if command -v llvm-config >/dev/null; then + gcc -O2 -w -DJTYPE=int -DZYLLVM $(llvm-config --cflags) core.c io_linux.c -o $T/zyl $(llvm-config --ldflags --libs) || exit 1 + run $T/zyl "llvm" + chk "$(ZYTRACE=1 $T/zyl < tests/o3.zc 2>&1 | grep -c 'compiled by llvm')" "8" "llvm compiles every o3 region" + printf '#!/bin/sh\nZYHOT=1 exec %s "$@"\n' $T/zyl > $T/llall; chmod +x $T/llall + chk "$($T/llall < tests/types.zc | md5sum)" "$($T/zy < tests/types.zc | md5sum)" "llvm compiling every block agrees" + selfc $T/zyl $T/gl.bin; cmp -s $T/g1.bin $T/gl.bin && chk 1 1 "llvm self-compile matches" || chk 0 1 "llvm self-compile matches" +else echo "skip llvm (no llvm-config)"; fi +echo "$ok passed, $bad failed"; [ $bad = 0 ] diff --git a/docs/history/17-o3-boundaries/code/tests/arrays.zc b/docs/history/17-o3-boundaries/code/tests/arrays.zc new file mode 100644 index 0000000..f8d46d8 --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tests/arrays.zc @@ -0,0 +1,24 @@ +// growable arrays and fixed ones +struct point { int x, y; }; +fn main() +{ + let before = live_objects(); + { + []int a = new([]int); + for (int i = 0; i < 100; i++) push(a, i * i); + putn(len(a)); putc(' '); putn(cap(a)); putc(' '); putn(a[99]); putc(' '); + putn(pop(a)); putc(' '); putn(len(a)); putc(' '); + resize(a, 3); putn(len(a)); putc(' '); putn(a[2]); putc(' '); + fixed []int f = new(fixed []int, 5); + f[4] = 7; putn(f[4] + len(f)); putc(' '); + []point ps = new([]point, 2); + ps[1].y = 9; resize(ps, 10); putn(ps[1].y + len(ps)); putc(' '); + [8]int st; putn(len(st)); putc(' '); + []*point owned = new([]*point); + for (int i = 0; i < 20; i++) { *point p = new(point); p.x = i; push(owned, d*p); } + putn(owned[19].x); putc(' '); + putn(live_objects() - before); putc(10); + } + putn(live_objects() - before); putc(10); + return 0; +} diff --git a/docs/history/17-o3-boundaries/code/tests/basics.zc b/docs/history/17-o3-boundaries/code/tests/basics.zc new file mode 100644 index 0000000..c1f1faf --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tests/basics.zc @@ -0,0 +1,22 @@ +let table[10]; +let greeting = "hey"; +const N = 10, BIG = 0x100 * 2; +fn fib(n) { if (n < 2) return n; return fib(n - 1) + fib(n - 2); } +fn twice(f, x) { return f(f(x)); } +fn inc(x) { return x + 1; } +fn main() +{ + let i, s = 0, a[5]; + for (i = 0; i < N; i++) table[i] = i * i; + for (let j = 0; j < N; j++) s += table[j]; + putn(s); putc(' '); putn(fib(15)); putc(' '); putn(twice(inc, 40)); putc(' '); putn(BIG); putc(10); + puts(greeting); putc(' '); putn('A'); putc(' '); putn(-17 / 5); putc(' '); putn(-17 % 5); putc(' '); putn(1 << 10); putc(' '); putn(-64 >> 3); putc(10); + i = 0; while (1) { i++; if (i == 3) continue; if (i > 6) break; putn(i); } putc(10); + a[0] = 5; a[1] = a[0]++; a[2] = ++a[0]; putn(a[0]); putn(a[1]); putn(a[2]); putc(10); + let b = { let t = 3; t * t }; + putn(b); putc(' '); putn(3 > 2 && 2 > 1); putn(0 || 0); putn(!5); putn(~0); putn(5 != 5); putn(4 >= 4); putc(10); + let x = asm { 2 3 + }; + putn(x); putc(10); + asm { 'o' . 'k' . 10 . } + return 0; +} diff --git a/docs/history/17-o3-boundaries/code/tests/bench.zc b/docs/history/17-o3-boundaries/code/tests/bench.zc new file mode 100644 index 0000000..a7e53f5 --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tests/bench.zc @@ -0,0 +1,30 @@ +// four kernels with and without o3; bench.sh runs each one on each backend +int csum(int lim) { int s = 0; for (int n = 1; n < lim; n++) { int x = n; while (x != 1) { if (x & 1) x = 3 * x + 1; else x = x / 2; s++; } } return s; } +o3 int csum3(int lim) { int s = 0; for (int n = 1; n < lim; n++) { int x = n; while (x != 1) { if (x & 1) x = 3 * x + 1; else x = x / 2; s++; } } return s; } +int sieve(fixed []int f) { int c = 0, n = len(f); for (int i = 0; i < n; i++) f[i] = 1; for (int i = 2; i < n; i++) if (f[i]) { c++; for (int j = i + i; j < n; j += i) f[j] = 0; } return c; } +o3 int sieve3(fixed []int f) { int c = 0, n = len(f); for (int i = 0; i < n; i++) f[i] = 1; for (int i = 2; i < n; i++) if (f[i]) { c++; for (int j = i + i; j < n; j += i) f[j] = 0; } return c; } +int digits(int n) { int s = 0; for (int i = 0; i < n; i++) { int x = i; while (x) { s = s + x % 10; x = x / 10; } } return s; } +o3 int digits3(int n) { int s = 0; for (int i = 0; i < n; i++) { int x = i; while (x) { s = s + x % 10; x = x / 10; } } return s; } +int fib(int n) { if (n < 2) return n; return fib(n - 1) + fib(n - 2); } +o3 int fib3(int n) { if (n < 2) return n; return fib3(n - 1) + fib3(n - 2); } +// input: a letter (the kernel; z runs none) and how many times to run it +fn main() +{ + fixed []int f = new(fixed []int, 50000); + let which = getc(), reps = 0, ch = getc(), r = 0; + while (ch >= '0' && ch <= '9') { reps = reps * 10 + ch - '0'; ch = getc(); } + while (reps > 0) { + if (which == 'a') r = csum(30000); + if (which == 'b') r = csum3(30000); + if (which == 'c') r = sieve(f); + if (which == 'd') r = sieve3(f); + if (which == 'e') r = digits(300000); + if (which == 'f') r = digits3(300000); + if (which == 'g') r = fib(22); + if (which == 'h') r = fib3(22); + reps--; + } + putn(r); putc(10); + free(d*f); + return 0; +} diff --git a/docs/history/17-o3-boundaries/code/tests/borrowmove.zc b/docs/history/17-o3-boundaries/code/tests/borrowmove.zc new file mode 100644 index 0000000..43f27b2 --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tests/borrowmove.zc @@ -0,0 +1,2 @@ +fn put(.m box, int v) { *int c = new(int); *c = v; box[0] = c; return 0; } +fn main() { let b = new(1); put(b, 6); putn(*(b[0] as *int)); putc(10); return 0; } diff --git a/docs/history/17-o3-boundaries/code/tests/buddy.zc b/docs/history/17-o3-boundaries/code/tests/buddy.zc new file mode 100644 index 0000000..2000598 --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tests/buddy.zc @@ -0,0 +1,15 @@ +// the buddy allocator: 20000 small blocks, freed, merge back into one big one +let keep[20000]; +fn main() +{ + let i = 0, p; + while (i < 20000) { keep[i] = new(2); i++; } // 20000 8-word blocks + putn(largest_free()); putc(' '); + i = 0; while (i < 20000) { free(keep[i]); i++; } + putn(largest_free()); putc(' '); + p = new(200000); // only possible if the small blocks merged back + putn(osize(p) >= 200000); putc(' '); + free(d*p); + putn(live_objects()); putc(10); + return 0; +} diff --git a/docs/history/17-o3-boundaries/code/tests/enhance.zc b/docs/history/17-o3-boundaries/code/tests/enhance.zc new file mode 100644 index 0000000..33eb85b --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tests/enhance.zc @@ -0,0 +1,29 @@ +// custom opcodes: recipes, lifted vm code, several outputs, and a fallback +enhance clampadd(a, b) micro "a = add a b; a = min a 100; a = max a 0"; +enhance cube(x) vm { 0 $ 0 $ * * }; +enhance 2 divmod(a, b) micro "r2 = sdiv a b" "\n r1 = srem a b\n r0 = r2"; +enhance div7(a) micro "a = sdiv a 7"; +enhance urem10(a) micro "a = urem a 10"; +enhance hi(a, b) micro "a = mulhu a b"; +enhance noisy(x) vm { 0 $ . } else soft_noisy; // i/o can't be lifted, so calls use the fallback +fn soft_noisy(x) { return x + 1; } +let qq, rr, seed = 7; +fn rnd() { seed = seed * 1103515245 + 12345; return seed ^ (seed >> 11); } +fn main() +{ + let i = 0, a, bad = 0; + putn(clampadd(70, 50)); putc(' '); putn(clampadd(-50, 2)); putc(' '); putn(cube(7)); putc(' '); + asm { 17 5 divmod rr ! qq ! } + putn(qq); putc(','); putn(rr); putc(' '); + putn(noisy(4)); putc(' '); + putn(hi(-1, -1)); putc(' '); + while (i < 5000) { + a = rnd(); + if (div7(a) != zdiv(a, 7)) bad++; + if (urem10(a) < 0 || urem10(a) > 9) bad++; + if (a / (i + 1) != zdiv(a, i + 1)) bad++; + i++; + } + putn(bad); putc(10); + return 0; +} diff --git a/docs/history/17-o3-boundaries/code/tests/errors/back_to_m.zc b/docs/history/17-o3-boundaries/code/tests/errors/back_to_m.zc new file mode 100644 index 0000000..b8c160f --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tests/errors/back_to_m.zc @@ -0,0 +1,2 @@ +// expect: x: can't be recast back to m +fn main() { let x = new(3); d*x; m*x; return 0; } diff --git a/docs/history/17-o3-boundaries/code/tests/errors/borrow_escape.zc b/docs/history/17-o3-boundaries/code/tests/errors/borrow_escape.zc new file mode 100644 index 0000000..ce24480 --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tests/errors/borrow_escape.zc @@ -0,0 +1,3 @@ +// expect: p: is owned and can't be returned +fn f(.m p) { return p; } +fn main() { let x = new(1); f(x); return 0; } diff --git a/docs/history/17-o3-boundaries/code/tests/errors/branch_mismatch.zc b/docs/history/17-o3-boundaries/code/tests/errors/branch_mismatch.zc new file mode 100644 index 0000000..bd19968 --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tests/errors/branch_mismatch.zc @@ -0,0 +1,3 @@ +// expect: x: ends the branches of an if in different states +fn take(.d p) { return p[0]; } +fn main() { let x = new(1); if (x[0]) take(d*x); else u*x; return 0; } diff --git a/docs/history/17-o3-boundaries/code/tests/errors/child_dies.zc b/docs/history/17-o3-boundaries/code/tests/errors/child_dies.zc new file mode 100644 index 0000000..67caca9 --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tests/errors/child_dies.zc @@ -0,0 +1,3 @@ +// expect: c: used after it was given away +fn take(.d p) { return p[0]; } +fn main() { let p = new(2), c = new(1); p[0] = c; take(d*p); return c[0]; } diff --git a/docs/history/17-o3-boundaries/code/tests/errors/child_recast.zc b/docs/history/17-o3-boundaries/code/tests/errors/child_recast.zc new file mode 100644 index 0000000..c9ccd62 --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tests/errors/child_recast.zc @@ -0,0 +1,2 @@ +// expect: c: belongs to a container; recast the container instead +fn main() { let p = new(2), c = new(1); p[0] = c; d*c; return 0; } diff --git a/docs/history/17-o3-boundaries/code/tests/errors/copy_owned.zc b/docs/history/17-o3-boundaries/code/tests/errors/copy_owned.zc new file mode 100644 index 0000000..862c7ef --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tests/errors/copy_owned.zc @@ -0,0 +1,2 @@ +// expect: x: is an owned object and can't be copied +fn main() { let x = new(1); let y = x; return y[0]; } diff --git a/docs/history/17-o3-boundaries/code/tests/errors/d_into_block.zc b/docs/history/17-o3-boundaries/code/tests/errors/d_into_block.zc new file mode 100644 index 0000000..2d7f526 --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tests/errors/d_into_block.zc @@ -0,0 +1,2 @@ +// expect: x: used after it was given away +fn main() { let x = new(1); d*x; { x[0] = 5; } return x[0]; } diff --git a/docs/history/17-o3-boundaries/code/tests/errors/d_kept.zc b/docs/history/17-o3-boundaries/code/tests/errors/d_kept.zc new file mode 100644 index 0000000..dcbfb86 --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tests/errors/d_kept.zc @@ -0,0 +1,2 @@ +// expect: x: d object was never given away +fn main() { let x = new(3); d*x; x[0] = 1; return 0; } diff --git a/docs/history/17-o3-boundaries/code/tests/errors/discard_u.zc b/docs/history/17-o3-boundaries/code/tests/errors/discard_u.zc new file mode 100644 index 0000000..9293378 --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tests/errors/discard_u.zc @@ -0,0 +1,3 @@ +// expect: can't be thrown away +.u mk() { let p = new(1); return u*p; } +fn main() { mk(); return 0; } diff --git a/docs/history/17-o3-boundaries/code/tests/errors/give_m.zc b/docs/history/17-o3-boundaries/code/tests/errors/give_m.zc new file mode 100644 index 0000000..cd67896 --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tests/errors/give_m.zc @@ -0,0 +1,3 @@ +// expect: x: is m; write d*x +fn take(.d p) { return p[0]; } +fn main() { let x = new(3); take(x); return 0; } diff --git a/docs/history/17-o3-boundaries/code/tests/errors/global_store.zc b/docs/history/17-o3-boundaries/code/tests/errors/global_store.zc new file mode 100644 index 0000000..dc0e546 --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tests/errors/global_store.zc @@ -0,0 +1,3 @@ +// expect: x: must be d to be stored in a global +let g; +fn main() { let x = new(1); g = x; return 0; } diff --git a/docs/history/17-o3-boundaries/code/tests/errors/leak_overwrite.zc b/docs/history/17-o3-boundaries/code/tests/errors/leak_overwrite.zc new file mode 100644 index 0000000..d19b5fd --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tests/errors/leak_overwrite.zc @@ -0,0 +1,2 @@ +// expect: x: still owns an object +fn main() { let x = new(1); x = new(2); return x[0]; } diff --git a/docs/history/17-o3-boundaries/code/tests/errors/lend_d.zc b/docs/history/17-o3-boundaries/code/tests/errors/lend_d.zc new file mode 100644 index 0000000..55ac7e0 --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tests/errors/lend_d.zc @@ -0,0 +1,4 @@ +// expect: x: is d; pass it to a d parameter +fn look(.m p) { return p[0]; } +fn take(.d p) { return p[0]; } +fn main() { let x = new(3); d*x; look(x); take(x); return 0; } diff --git a/docs/history/17-o3-boundaries/code/tests/errors/loop_move.zc b/docs/history/17-o3-boundaries/code/tests/errors/loop_move.zc new file mode 100644 index 0000000..df3c94a --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tests/errors/loop_move.zc @@ -0,0 +1,3 @@ +// expect: x: is given away inside a loop that comes back around to it +fn take(.d p) { return p[0]; } +fn main() { let x = new(1); let i = 0; d*x; while (i < 3) { take(x); i++; } return 0; } diff --git a/docs/history/17-o3-boundaries/code/tests/errors/plain_return.zc b/docs/history/17-o3-boundaries/code/tests/errors/plain_return.zc new file mode 100644 index 0000000..45fb545 --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tests/errors/plain_return.zc @@ -0,0 +1,3 @@ +// expect: returns a new object; give the function a .u return type +fn mk() { return new(1); } +fn main() { return mk(); } diff --git a/docs/history/17-o3-boundaries/code/tests/errors/return_m.zc b/docs/history/17-o3-boundaries/code/tests/errors/return_m.zc new file mode 100644 index 0000000..b5ef519 --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tests/errors/return_m.zc @@ -0,0 +1,3 @@ +// expect: x: has to be u to be returned +.u mk() { let x = new(1); return x; } +fn main() { let y = mk(); return y[0]; } diff --git a/docs/history/17-o3-boundaries/code/tests/errors/tag_mismatch.zc b/docs/history/17-o3-boundaries/code/tests/errors/tag_mismatch.zc new file mode 100644 index 0000000..079f249 --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tests/errors/tag_mismatch.zc @@ -0,0 +1,2 @@ +// expect: c: its tag doesn't match its container's +fn main() { let p = new(2), c = new(1); u*p; p[0] = c; return 0; } diff --git a/docs/history/17-o3-boundaries/code/tests/errors/u_dropped.zc b/docs/history/17-o3-boundaries/code/tests/errors/u_dropped.zc new file mode 100644 index 0000000..e1ca68a --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tests/errors/u_dropped.zc @@ -0,0 +1,2 @@ +// expect: x: u object must leave this block +fn main() { let x = new(3); u*x; return 0; } diff --git a/docs/history/17-o3-boundaries/code/tests/errors/u_skip_level.zc b/docs/history/17-o3-boundaries/code/tests/errors/u_skip_level.zc new file mode 100644 index 0000000..6caeeb3 --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tests/errors/u_skip_level.zc @@ -0,0 +1,2 @@ +// expect: t: is u; it can only be handed to a variable of the enclosing block +fn main() { let r; { { let t = new(1); u*t; r = t; } } return r[0]; } diff --git a/docs/history/17-o3-boundaries/code/tests/errors/unchecked_touch.zc b/docs/history/17-o3-boundaries/code/tests/errors/unchecked_touch.zc new file mode 100644 index 0000000..141639d --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tests/errors/unchecked_touch.zc @@ -0,0 +1,2 @@ +// expect: x: is tracked by the analyzer and can't be used in unchecked code +fn main() { let x = new(1); unchecked { x[0] = 1; } return 0; } diff --git a/docs/history/17-o3-boundaries/code/tests/errors/unused.zc b/docs/history/17-o3-boundaries/code/tests/errors/unused.zc new file mode 100644 index 0000000..64aff1f --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tests/errors/unused.zc @@ -0,0 +1,2 @@ +// expect: x: m object is never used +fn main() { let x = new(3); return 0; } diff --git a/docs/history/17-o3-boundaries/code/tests/errors/use_after_give.zc b/docs/history/17-o3-boundaries/code/tests/errors/use_after_give.zc new file mode 100644 index 0000000..8a10922 --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tests/errors/use_after_give.zc @@ -0,0 +1,3 @@ +// expect: x: used after it was given away +fn take(.d p) { return p[0]; } +fn main() { let x = new(3); take(d*x); return x[0]; } diff --git a/docs/history/17-o3-boundaries/code/tests/gc.zc b/docs/history/17-o3-boundaries/code/tests/gc.zc new file mode 100644 index 0000000..41f14a0 --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tests/gc.zc @@ -0,0 +1,20 @@ +let keep; +unchecked fn churn(n) +{ + let i = 0, p; + while (i < n) { p = new(10); p[0] = i; if (i % 100 == 0) keep = p; i++; } + return 0; +} +fn main() +{ + let base = live_objects(), f; + churn(1000); + putn(live_objects() - base); putc(' '); + f = gc(); + putn(f >= 990); putc(' '); // conservative: a stale word or two may keep one alive + putn(keep[0]); putc(' '); // the one a global still points at survives + churn(200000); // 3.2m words through a 393k word heap: needs the automatic collector + putn(gc_runs > 1); putc(' '); + putn(keep[0]); putc(10); + return 0; +} diff --git a/docs/history/17-o3-boundaries/code/tests/joins.zc b/docs/history/17-o3-boundaries/code/tests/joins.zc new file mode 100644 index 0000000..913929b --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tests/joins.zc @@ -0,0 +1,32 @@ +// joins: something given away on one path is dead afterwards, and the paths +// that still own it free it on the way in. nothing here leaks. +fn take(.d p) { return p[0]; } +fn later(.d p); // not needed any more, but still allowed +*int.u mk(int v) { *int p = new(int); *p = v; return u*p; } +fn count(int n) { let s = 0; switch (n % 4) { case 0: s = 10; case 1, 2: s = 20; break; default: s = 30; } return s; } +fn main() +{ + let base = live_objects(), i = 0, hits = 0, k; + while (i < 50) { + let x = mk(i); + if (i % 3 == 0) take(d*x); // else: freed here by the fixup + i++; + } + putn(live_objects() - base); putc(' '); + // give away and break: fine now; the normal exit frees it + i = 0; + let y = mk(7); + while (i < 10) { if (i == 4) { take(d*y); break; } i++; } + putn(live_objects() - base); putc(' '); + let z = mk(5); + while (1) { if (z[0] == 5) { hits = take(d*z); break; } } + putn(hits); putc(' '); + putn(count(0) + count(1) + count(2) + count(3)); putc(' '); + k = i > 3 ? i * 2 : 0 - 1; + putn(k); putc(' '); + putn(forward(new(int))); putc(' '); // tagged and defined below: the prescan knows .d + **int pp; *int q = new(int); *q = 9; pp = &q; putn(**pp); free(d*q); putc(' '); + putn(live_objects() - base); putc(10); + return 0; +} +fn forward(*int.d p) { return 3 + *p; } diff --git a/docs/history/17-o3-boundaries/code/tests/langerrors/const_index.zc b/docs/history/17-o3-boundaries/code/tests/langerrors/const_index.zc new file mode 100644 index 0000000..03e927b --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tests/langerrors/const_index.zc @@ -0,0 +1,2 @@ +// expect: the index is out of range for this array +fn main() { [4]int a; a[4] = 1; return 0; } diff --git a/docs/history/17-o3-boundaries/code/tests/langerrors/fixed_nolen.zc b/docs/history/17-o3-boundaries/code/tests/langerrors/fixed_nolen.zc new file mode 100644 index 0000000..b79c31e --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tests/langerrors/fixed_nolen.zc @@ -0,0 +1,2 @@ +// expect: a fixed array needs its length +fn main() { fixed []int f = new(fixed []int); free(d*f); return 0; } diff --git a/docs/history/17-o3-boundaries/code/tests/langerrors/fixed_push.zc b/docs/history/17-o3-boundaries/code/tests/langerrors/fixed_push.zc new file mode 100644 index 0000000..a19bcb4 --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tests/langerrors/fixed_push.zc @@ -0,0 +1,2 @@ +// expect: a fixed array can't grow or shrink +fn main() { fixed []int f = new(fixed []int, 3); push(f, 1); free(d*f); return 0; } diff --git a/docs/history/17-o3-boundaries/code/tests/langerrors/fixed_to_dyn.zc b/docs/history/17-o3-boundaries/code/tests/langerrors/fixed_to_dyn.zc new file mode 100644 index 0000000..483f62f --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tests/langerrors/fixed_to_dyn.zc @@ -0,0 +1,4 @@ +// expect: type error: argument: expected []int, got fixed []int +typecheck on; +int take([]int.m g) { return len(g); } +fn main() { fixed []int f = new(fixed []int, 3); take(f); free(d*f); return 0; } diff --git a/docs/history/17-o3-boundaries/code/tests/langerrors/gen_undeclared.zc b/docs/history/17-o3-boundaries/code/tests/langerrors/gen_undeclared.zc new file mode 100644 index 0000000..919fb51 --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tests/langerrors/gen_undeclared.zc @@ -0,0 +1,3 @@ +// expect: T: isn't a type +T first(*int l) { return l[0]; } +fn main() { return 0; } diff --git a/docs/history/17-o3-boundaries/code/tests/langerrors/o3_addr.zc b/docs/history/17-o3-boundaries/code/tests/langerrors/o3_addr.zc new file mode 100644 index 0000000..815253d --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tests/langerrors/o3_addr.zc @@ -0,0 +1,2 @@ +// expect: an o3 variable has no address +fn main() { o3 int z = 2; *int q = &z; return *q; } diff --git a/docs/history/17-o3-boundaries/code/tests/langerrors/o3_asm.zc b/docs/history/17-o3-boundaries/code/tests/langerrors/o3_asm.zc new file mode 100644 index 0000000..c8afb44 --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tests/langerrors/o3_asm.zc @@ -0,0 +1,2 @@ +// expect: asm can't reach an o3 variable +fn main() { o3 int zz = 2; asm { zz @ _ } return 0; } diff --git a/docs/history/17-o3-boundaries/code/tests/langerrors/o3_let.zc b/docs/history/17-o3-boundaries/code/tests/langerrors/o3_let.zc new file mode 100644 index 0000000..d2aff9a --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tests/langerrors/o3_let.zc @@ -0,0 +1,2 @@ +// expect: o3 variables need a type +fn main() { o3 { let x = 1; x++; } return 0; } diff --git a/docs/history/17-o3-boundaries/code/tests/langerrors/o3_param.zc b/docs/history/17-o3-boundaries/code/tests/langerrors/o3_param.zc new file mode 100644 index 0000000..5694720 --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tests/langerrors/o3_param.zc @@ -0,0 +1,3 @@ +// expect: o3 parameters need a type +o3 int f(x) { return x; } +fn main() { return f(1); } diff --git a/docs/history/17-o3-boundaries/code/tests/manyerrors.zc b/docs/history/17-o3-boundaries/code/tests/manyerrors.zc new file mode 100644 index 0000000..f4c0c68 --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tests/manyerrors.zc @@ -0,0 +1,6 @@ +// several errors in one run: a syntax error, an analyzer error, a type error +typecheck on; +fn f() { let a = ; return 0; } +fn g() { let x = new(3); return 0; } +int h() { *char s = 5; return 1; } +fn main() { return 0 } diff --git a/docs/history/17-o3-boundaries/code/tests/meta.zc b/docs/history/17-o3-boundaries/code/tests/meta.zc new file mode 100644 index 0000000..bc89454 --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tests/meta.zc @@ -0,0 +1,46 @@ +fn ipow(a, b) { let r = 1; while (b > 0) { r *= a; b--; } return r; } +// a parse word that adds an operator to the compiler's tables +immediate fn pow_op() { next(); add_binop("**", 11, "ipow c"); return 0; } +pow_op + +// unless (cond) stmt: a parse word that emits code itself +immediate fn unless() +{ + let a; + next(); + expect(intern("(")); rval(); plainuse(expr()); rval(); expect(intern(")")); + emitseq(useq[0], ulen[0]); // not + a = hole(); op(O_JZ); // skip the body when the condition was true + substmt(); + setlit(a, here); + return 0; +} +// repeat N { body }: a macro, built from token lists +immediate fn repeat() +{ + let n, body, i = 0; + next(); n = tv; next(); + body = grab(); + while (i < n) { feed(qdup(body)); i++; } + free(d*body); + return 0; +} +fn main() +{ + putn(2 ** 10); putc(' '); putn(3 + 2 ** 3 * 2); putc(10); + unless (1 > 2) puts("unless ok\n"); + unless (2 > 1) puts("wrong\n"); + repeat 3 { putc('r'); } + putc(10); + let q = quote { putn(40 + 2); putc(10); return 7; }; + putn(q[0]); putc(' '); + putn(eval(q)); putc(10); + let f = compile(q); + putn(f()); putc(10); + free(d*q); + let defs = quote { fn sq(x) { return x * x; } }; + load(defs); + free(d*defs); + putn(evals("return sq(12);")); putc(10); + return 0; +} diff --git a/docs/history/17-o3-boundaries/code/tests/misc.zc b/docs/history/17-o3-boundaries/code/tests/misc.zc new file mode 100644 index 0000000..25fc53b --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tests/misc.zc @@ -0,0 +1,30 @@ +// ct { ... } runs a block while compiling and leaves its result as a constant +immediate fn ct() +{ + let q, v; + next(); + q = grab(); + v = eval(q); + free(d*q); + lit(v); + return 0; +} +// teach the asm syntax that p means out, like the old repl demo +immediate fn teach() { next(); mnem[12] = 'p'; return 0; } +teach + +fn countdown() +{ + asm { 5 :top 0 $ '0' + p 1 - 0 $ done z top j :done _ 10 p } + return 0; +} +fn main() +{ + putn(ct { let i = 0, s = 0; while (i < 10) { s += i; i++; } return s; }); putc(10); + countdown(); + puts(lazy()); putc(10); + return 0; +} +// the analyzer is off from here on +unchecked; +fn lazy() { let p = new(3); p[0] = 'o'; p[1] = 'k'; return p; } diff --git a/docs/history/17-o3-boundaries/code/tests/o3.zc b/docs/history/17-o3-boundaries/code/tests/o3.zc new file mode 100644 index 0000000..296d1d5 --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tests/o3.zc @@ -0,0 +1,83 @@ +// o3 regions: every kind of control flow, against the same code without o3 +struct pt { int x; int y; }; +int g = 3; +int sq(int x) { return x * x; } +o3 int collatz(int n) +{ + int steps = 0; + while (n != 1) { if (n % 2 == 0) n = n / 2; else n = 3 * n + 1; steps++; } + return steps; +} +o3 int ack(int am, int n) +{ + if (am == 0) return n + 1; + if (n == 0) return ack(am - 1, 1); + return ack(am - 1, ack(am, n - 1)); +} +o3 int many(int a, int b, int c) +{ + int dd = a + b, e = b + c, f = c + a, h = dd * e, i2 = e * f, j = f * dd, k = h + i2 + j, l = k / 7; + for (int t = 0; t < 5; t++) { dd += t; e -= t; f = f ^ t; l = l + dd * e - f; } + return dd + e + f + h + i2 + j + k + l; +} +o3 int mix(int n) +{ + int acc = 0; + for (int i = 0; i < n; i++) { + if (i % 3 == 0 && i % 5 == 0) acc += 15; + else if (i % 3 == 0 || i > 90) acc += 3; + else acc = acc + (i & 1 ? sq(i) : g); + switch (i % 4) { case 0: acc += 1; case 1: acc -= 2; default: acc = acc ^ 1; } + if (acc > 100000) break; + if (i == 7) continue; + acc = acc + i / 10 - i % 7 + (i << 2) - (i >> 1); + } + return acc; +} +o3 int dyn(int n) +{ + []int a = new([]int, n); + fixed []int f = new(fixed []int, n); + int s = 0; + for (int i = 0; i < len(a); i++) { a[i] = i * i; f[i] = n - i; } + for (int i = 0; i < n; i++) s = s + a[i] * f[i]; + free(d*a); free(d*f); + return s; +} +o3 int pts(int n) +{ + *pt p = new(pt); + int s = 0; + p->x = 1; p->y = 2; + for (int i = 0; i < n; i++) { p->x = p->x + p->y; p->y = p->x - p->y; s = s + p->x % 1000; } + free(d*p); + return s; +} +o3 int divs(int x) +{ + int s = 0; + for (int i = -50; i < 50; i++) s = s + (x + i) / 7 + (x + i) % 7 + (x - i) / -3 + i / 16 + i % 16; + return s; +} +fn main() +{ + o3 int lone = 2; + let r = 0; + lone = lone * 21; + o3 { + int i = 0, s = 0; + while (i < 100) { s = s + collatz(i + 1); i++; } + putn(s); putc(' '); + putn(lone); putc(' '); + lone++; + } + putn(lone); putc(' '); + putn(ack(2, 3)); putc(' '); + putn(many(1, 2, 3)); putc(' '); + putn(mix(100)); putc(' '); + putn(dyn(50)); putc(' '); + putn(pts(30)); putc(' '); + putn(divs(1000)); putc(' '); + putn(divs(-77)); putc(10); + return 0; +} diff --git a/docs/history/17-o3-boundaries/code/tests/o3_bounds.zc b/docs/history/17-o3-boundaries/code/tests/o3_bounds.zc new file mode 100644 index 0000000..fb347c0 --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tests/o3_bounds.zc @@ -0,0 +1,2 @@ +// an index outside a []T inside o3 code: caught by the inline check +fn main() { []int a = new([]int, 5); o3 { int i = 7; a[i] = 1; } free(d*a); return 0; } diff --git a/docs/history/17-o3-boundaries/code/tests/o3fat.zc b/docs/history/17-o3-boundaries/code/tests/o3fat.zc new file mode 100644 index 0000000..313c87c --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tests/o3fat.zc @@ -0,0 +1,22 @@ +// o3 objects reaching normal code get their types: through a return (all the way down the list), an assignment, a store into a field +struct node { int v; *node next; }; +struct box { [3]*node slots; int n; }; +unchecked o3 *node build(int n) +{ + *node head = 0; + for (int i = 0; i < n; i++) { *node k = new(node); k.v = i; k.next = head; head = k; } + return head; +} +unchecked fn main() +{ + *node l = build(5); + tprint(typeof_obj(l)); putc(' '); tprint(typeof_obj(l.next.next.next)); putc(' '); + putn(l.next.v); putc(' '); + *box b = 0; + o3 { b = new(box); b.slots[1] = new(node); b.n = 2; } + tprint(typeof_obj(b)); putc(' '); tprint(typeof_obj(b.slots[1])); putc(' '); + *int raw = 0; + o3 { raw = new(int, 6); } + tprint(typeof_obj(raw)); putc(10); + return 0; +} diff --git a/docs/history/17-o3-boundaries/code/tests/o3scopes.zc b/docs/history/17-o3-boundaries/code/tests/o3scopes.zc new file mode 100644 index 0000000..9457c77 --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tests/o3scopes.zc @@ -0,0 +1,19 @@ +// registers reused by scopes that never overlap, and a fixed array read through its cached items and length +o3 int scopes(fixed []int f) +{ + int total = 0; + for (int a = 0; a < 3; a++) { int x = a * 2; total += x; } + for (int b = 0; b < 3; b++) { int y = b * 3; total += y; } + for (int c = 0; c < 3; c++) { int z = c * 5; total += z; } + for (int e = 0; e < len(f); e++) { int w = f[e]; total += w; } + for (int g = 0; g < 4; g++) { int h = g + 1, q = h * h, r = q - 1; total += r; } + return total; +} +fn main() +{ + fixed []int f = new(fixed []int, 4); + for (int i = 0; i < 4; i++) f[i] = 10 * i; + putn(scopes(f)); putc(10); + free(d*f); + return 0; +} diff --git a/docs/history/17-o3-boundaries/code/tests/overflow_data.zc b/docs/history/17-o3-boundaries/code/tests/overflow_data.zc new file mode 100644 index 0000000..3042f0a --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tests/overflow_data.zc @@ -0,0 +1 @@ +fn main() { while (1) asm { 1 } return 0; } diff --git a/docs/history/17-o3-boundaries/code/tests/overflow_frame.zc b/docs/history/17-o3-boundaries/code/tests/overflow_frame.zc new file mode 100644 index 0000000..3897bf4 --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tests/overflow_frame.zc @@ -0,0 +1,2 @@ +fn big(n) { let a[500]; a[0] = n; return big(n + 1) + a[0]; } +fn main() { return big(0); } diff --git a/docs/history/17-o3-boundaries/code/tests/overflow_return.zc b/docs/history/17-o3-boundaries/code/tests/overflow_return.zc new file mode 100644 index 0000000..9d5c622 --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tests/overflow_return.zc @@ -0,0 +1,2 @@ +fn deep(n) { return deep(n + 1) + 1; } +fn main() { return deep(0); } diff --git a/docs/history/17-o3-boundaries/code/tests/runerr.sh b/docs/history/17-o3-boundaries/code/tests/runerr.sh new file mode 100755 index 0000000..23ea054 --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tests/runerr.sh @@ -0,0 +1,10 @@ +#!/bin/bash +# runs every .zc in a folder (tests/errors by default) and checks the compiler +# rejects it with the message on its first line: // expect: ... +vm=${1:-zy}; dir=${2:-$(dirname "$0")/errors}; pass=0; fail=0 +for f in "$dir"/*.zc; do + want=$(head -1 "$f" | sed 's#^// expect: ##') + got=$($vm < "$f" 2>&1 | head -1) + if [[ "$got" == *"$want"* ]]; then pass=$((pass+1)); else fail=$((fail+1)); echo "FAIL $(basename $f): got [$got]"; fi +done +echo "rejected: $pass passed, $fail failed" diff --git a/docs/history/17-o3-boundaries/code/tests/typeerrors/arg.zc b/docs/history/17-o3-boundaries/code/tests/typeerrors/arg.zc new file mode 100644 index 0000000..909672a --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tests/typeerrors/arg.zc @@ -0,0 +1,5 @@ +// expect: type error: argument: expected *point, got *int +typecheck on; +struct point { int x, y; }; +int f(*point p) { return p.x; } +fn main() { *int q = new(int); f(q); free(d*q); return 0; } diff --git a/docs/history/17-o3-boundaries/code/tests/typeerrors/arith.zc b/docs/history/17-o3-boundaries/code/tests/typeerrors/arith.zc new file mode 100644 index 0000000..76e3111 --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tests/typeerrors/arith.zc @@ -0,0 +1,3 @@ +// expect: type error: arithmetic needs numbers, got *int +typecheck on; +fn main() { *int p = 0; int x = p * 2; return x; } diff --git a/docs/history/17-o3-boundaries/code/tests/typeerrors/assign.zc b/docs/history/17-o3-boundaries/code/tests/typeerrors/assign.zc new file mode 100644 index 0000000..8b8a888 --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tests/typeerrors/assign.zc @@ -0,0 +1,3 @@ +// expect: type error: initializer: expected *char, got int +typecheck on; +fn main() { *char s = 5; return 0; } diff --git a/docs/history/17-o3-boundaries/code/tests/typeerrors/call.zc b/docs/history/17-o3-boundaries/code/tests/typeerrors/call.zc new file mode 100644 index 0000000..7b03dbf --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tests/typeerrors/call.zc @@ -0,0 +1,3 @@ +// expect: type error: only functions can be called, got int +typecheck on; +fn main() { int k = 3; return k(1); } diff --git a/docs/history/17-o3-boundaries/code/tests/typeerrors/deref.zc b/docs/history/17-o3-boundaries/code/tests/typeerrors/deref.zc new file mode 100644 index 0000000..ed030f5 --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tests/typeerrors/deref.zc @@ -0,0 +1,3 @@ +// expect: type error: only pointers can be dereferenced, got int +typecheck on; +fn main() { int x = 3; return *x; } diff --git a/docs/history/17-o3-boundaries/code/tests/typeerrors/field.zc b/docs/history/17-o3-boundaries/code/tests/typeerrors/field.zc new file mode 100644 index 0000000..ba03697 --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tests/typeerrors/field.zc @@ -0,0 +1,3 @@ +// expect: point has no field z +struct point { int x, y; }; +fn main() { point p; return p.z; } diff --git a/docs/history/17-o3-boundaries/code/tests/typeerrors/generic.zc b/docs/history/17-o3-boundaries/code/tests/typeerrors/generic.zc new file mode 100644 index 0000000..49f2c10 --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tests/typeerrors/generic.zc @@ -0,0 +1,4 @@ +// expect: type error: argument: expected int, got *char +typecheck on; +T same(T a, T b) { return a; } +fn main() { return same(1, "x"); } diff --git a/docs/history/17-o3-boundaries/code/tests/typeerrors/notstruct.zc b/docs/history/17-o3-boundaries/code/tests/typeerrors/notstruct.zc new file mode 100644 index 0000000..0dff6bd --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tests/typeerrors/notstruct.zc @@ -0,0 +1,2 @@ +// expect: .x: that's a int, not a struct or a pointer to one +fn main() { int n = 1; return n.x; } diff --git a/docs/history/17-o3-boundaries/code/tests/typeerrors/ret.zc b/docs/history/17-o3-boundaries/code/tests/typeerrors/ret.zc new file mode 100644 index 0000000..be951d5 --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tests/typeerrors/ret.zc @@ -0,0 +1,4 @@ +// expect: type error: return value: expected bool, got *char +typecheck on; +bool f() { return "no"; } +fn main() { return f(); } diff --git a/docs/history/17-o3-boundaries/code/tests/typeerrors/voidret.zc b/docs/history/17-o3-boundaries/code/tests/typeerrors/voidret.zc new file mode 100644 index 0000000..86c9c17 --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tests/typeerrors/voidret.zc @@ -0,0 +1,4 @@ +// expect: type error: return value: expected void, got int +typecheck on; +void f() { return 3; } +fn main() { f(); return 0; } diff --git a/docs/history/17-o3-boundaries/code/tests/typeoff.zc b/docs/history/17-o3-boundaries/code/tests/typeoff.zc new file mode 100644 index 0000000..e44e450 --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tests/typeoff.zc @@ -0,0 +1,2 @@ +// the typechecker is off unless asked for: this mismatch compiles and runs +fn main() { *char s = 5; putn(s as int + 1); putc(10); return 0; } diff --git a/docs/history/17-o3-boundaries/code/tests/typeok.zc b/docs/history/17-o3-boundaries/code/tests/typeok.zc new file mode 100644 index 0000000..93462bd --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tests/typeok.zc @@ -0,0 +1,21 @@ +// passes the typechecker: words mix with everything, literal 0 is null, +// arrays decay to pointers, generic calls bind their parameters +typecheck on; +struct pair { A a; B b; }; +*pair.u mk(A x, B y) { *pair p = new(pair); p.a = x; p.b = y; return u*p; } +A left(*pair p) { return p.a; } +int strlen2(*char s) { int n = 0; while (s[n]) n++; return n; } +int main() { + *pair p = mk(3, "four"); + int a = left(p); + *char s = p.b; + [8]char buf; + *char q = buf; + *int nothing = 0; + let w = 5; + int mixed = w + 1; + q[0] = 'h'; q[1] = 0; + putn(a + strlen2(s) + strlen2(buf) + mixed); putc(10); + free(d*p); + return 0; +} diff --git a/docs/history/17-o3-boundaries/code/tests/types.zc b/docs/history/17-o3-boundaries/code/tests/types.zc new file mode 100644 index 0000000..c988499 --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tests/types.zc @@ -0,0 +1,56 @@ +struct point { int x, y; }; +struct rect { point a, b; int tag; }; +struct list { int len; *T items; }; +struct node { int v; *node next; }; +type num = int; + +int counter = 5; +[4]int squares; +point origin; + +int area(*rect r) { return (r.b.x - r.a.x) * (r.b.y - r.a.y); } +*list.u mklist(int cap) { + *list l = new(list); + l.len = 0; + l.items = new(T, cap) as *T; + return u*l; +} +T first(*list l) { return l.items[0]; } +int sum(*node n) { int s = 0; while (n) { s += n.v; n = n.next; } return s; } + +fn main() +{ + rect r; + r.a.x = 1; r.a.y = 2; r.b.x = 4; r.b.y = 6; + putn(area(&r)); putc(' '); + putn(sizeof(rect)); putc(' '); putn(sizeof(point)); putc(' '); putn(sizeof([3]rect)); putc(' '); + let p = new(point); // let infers *point + p.x = 7; p->y = 8; + putn(p.x * p.y); putc(' '); + free(d*p); + *list l = mklist(4); + l.items[0] = 42; l.len = 1; + putn(first(l)); putc(' '); + free(d*l); + *node a = new(node), b = new(node); + a.v = 3; b.v = 4; a.next = b; + putn(sum(a)); putc(' '); + free(d*a); // b went with it: a.next = b made a its owner + [3]point pts; + pts[2].y = 9; + putn(pts[2].y + counter); putc(' '); + num k = 3; + putn(k); putc(10); + + // reflection + tprint(typeof(r)); putc(' '); tprint(typeof(&r)); putc(' '); tprint(typeof(l)); putc(' '); + tprint(sigof(mklist)); putc(' '); tprint(sigof(first)); putc(' '); tprint(sigofs("free")); putc(10); + let t = typeof(r), i = 0; + while (i < tnfields(t)) { puts(tfname(t, i)); putc(':'); tprint(tftype(t, i)); putc('@'); putn(tfoff(t, i)); putc(' '); i++; } + putc(10); + *point q = new(point); + tprint(typeof_obj(q)); putc(' '); putn(typeof_obj(q) == typeof(point)); putc(' '); putn(tkind(typeof_obj(q)) == TY_STRUCT); + free(d*q); + putc(10); + return 0; +} diff --git a/docs/history/17-o3-boundaries/code/tests/umd.zc b/docs/history/17-o3-boundaries/code/tests/umd.zc new file mode 100644 index 0000000..497c9fc --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tests/umd.zc @@ -0,0 +1,51 @@ +// lists as [len, items...], items owned by the list +.u mklist(n) { + let l = new(n + 1); + l[0] = 0; + return u*l; +} +fn push(.m l, .d item) { l[l[0] + 1] = d*item; l[0] = l[0] + 1; return 0; } +fn sum(.m l) { let s = 0, i = 1; while (i <= l[0]) { s += l[i][0]; i++; } return s; } +.u box(v) { let b = new(1); b[0] = v; return u*b; } +fn consume(.d b) { return b[0]; } + +fn main() +{ + let base = live_objects(); + { + let l = mklist(8); // u from mklist arrives m*here + let i = 0; + while (i < 5) { + let b = box(i * 10); // m in the loop body + d*b; // give it down + push(l, b); + i++; + } + putn(sum(l)); putc(' '); + putn(live_objects() - base); putc(' '); // list + 5 boxes + } + putn(live_objects() - base); putc(' '); // freed at the block's end, children too + + // u handed up one block by assignment, and as a block value + let outer; + { + let t = box(7); + u*t; + outer = t; + } + let v = { let w = box(8); u*w }; + putn(outer[0] + v[0]); putc(' '); + + // d given away to a callee + let g = box(99); + putn(consume(d*g)); putc(' '); + + // containers: child takes its parent's tag + let p = new(2), c = new(1); + c[0] = 5; + p[0] = c; + p[1] = new(3); + putn(p[0][0]); putc(' '); + putn(live_objects() - base); putc(10); + return 0; +} diff --git a/docs/history/17-o3-boundaries/code/tools/build.zc b/docs/history/17-o3-boundaries/code/tools/build.zc new file mode 100644 index 0000000..a38e97f --- /dev/null +++ b/docs/history/17-o3-boundaries/code/tools/build.zc @@ -0,0 +1,13 @@ +// compiles the rest of stdin (after the backtick) as a genome image at GORG +// and writes it to stdout as little-endian 32-bit words. +const OUT = 0x10000; +fn main() +{ + let n = build_image(GORG, OUT), i = 0, w; + while (i < n) { + w = OUT[i]; + putc(w); putc(w >> 8); putc(w >> 16); putc(w >> 24); + i++; + } + return 0; +} diff --git a/docs/history/17-o3-boundaries/code/zc0.c b/docs/history/17-o3-boundaries/code/zc0.c new file mode 100644 index 0000000..7615cd7 --- /dev/null +++ b/docs/history/17-o3-boundaries/code/zc0.c @@ -0,0 +1,461 @@ +/* zc0: stage 0 of the bootstrap. a small c compiler for the subset of zc that + compiler.zc is written in. it reads zc source on stdin and writes a genome + header (vm words placed at GORG) on stdout, in the same image layout the + resident compiler's image mode produces. no lifetime analysis, no quote, no + parse words, no block expressions: those only exist in compiler.zc. tags + (m, d, -> u) and casts (as u, as d) are parsed and recorded in signatures + but not checked. */ +#include +#include +#include +#include + +#define GORG 0xC0000 +#define ZFP 0xA0000 /* frame pointer word */ +#define FTOP 0xF0000 /* frame stack grows down from here */ +#define MAXW 0x40000 + +enum { T_EOF, T_NUM, T_ID, T_STR, T_OP }; +enum { K_FN = 1, K_GVAR, K_GARR, K_CONST, K_KW, K_ENH }; +enum { O_Q, O_LD, O_ST, O_PICK, O_DROP, O_SWAP, O_CALL, O_JMP, O_JZ, O_RET, + O_IN, O_EMPTY, O_OUT, O_ADD, O_SUB, O_MUL, O_AND, O_OR, O_XOR, O_SHR, O_LT }; +static const char *mn = "q@!$_~cjzr,?.+-*&|^><%"; + +static char *src; static int pos, line = 1; +static int tk, tv, glue; static char ts[4096]; /* current token */ + +static int img[MAXW], here = GORG; +static void emit(int w) { if (here - GORG >= MAXW) { fprintf(stderr, "image too big\n"); exit(1); } img[here++ - GORG] = w; } +static void lit(int v) { emit((int)((unsigned)v * 2 + 1)); } +static void op(int k) { emit(k * 2); } +static void setlit(int at, int v) { img[at - GORG] = (int)((unsigned)v * 2 + 1); } + +static void die(const char *m) { fprintf(stderr, "zc0: line %d: %s (at '%s')\n", line, m, ts); exit(1); } + +/* ---- lexer ---- */ +static const char *ops3[] = { "<<=", ">>=", 0 }; +static const char *ops2[] = { "==", "!=", "<=", ">=", "<<", ">>", "&&", "||", "++", "--", "+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "->", 0 }; +static int esc(void) +{ + int c = src[pos++]; + if (c != '\\') return c; + c = src[pos++]; + switch (c) { case 'n': return 10; case 't': return 9; case 'r': return 13; case '0': return 0; case 'e': return 27; } + return c; +} +static void next(void) +{ + int ws = 0, i; + for (;;) { + while (src[pos] && isspace((unsigned char)src[pos])) { if (src[pos] == '\n') line++; pos++; ws = 1; } + if (src[pos] == '/' && src[pos + 1] == '/') { while (src[pos] && src[pos] != '\n') pos++; ws = 1; continue; } + if (src[pos] == '/' && src[pos + 1] == '*') { pos += 2; while (src[pos] && !(src[pos] == '*' && src[pos + 1] == '/')) { if (src[pos] == '\n') line++; pos++; } if (src[pos]) pos += 2; ws = 1; continue; } + break; + } + glue = !ws; + if (!src[pos] || src[pos] == '`') { tk = T_EOF; strcpy(ts, ""); return; } + if (isdigit((unsigned char)src[pos])) { + char *e; tv = (int)strtoul(src + pos, &e, 0); memcpy(ts, src + pos, e - src - pos); ts[e - src - pos] = 0; pos = e - src; tk = T_NUM; return; + } + if (isalpha((unsigned char)src[pos]) || src[pos] == '_') { + i = 0; while (isalnum((unsigned char)src[pos]) || src[pos] == '_') ts[i++] = src[pos++]; + ts[i] = 0; tk = T_ID; return; + } + if (src[pos] == '\'') { pos++; tv = esc(); if (src[pos++] != '\'') die("bad char literal"); tk = T_NUM; strcpy(ts, "'c'"); return; } + if (src[pos] == '"') { pos++; i = 0; while (src[pos] && src[pos] != '"') ts[i++] = (char)esc(); ts[i] = 0; pos++; tk = T_STR; return; } + tk = T_OP; + for (i = 0; ops3[i]; i++) if (!strncmp(src + pos, ops3[i], 3)) { strcpy(ts, ops3[i]); pos += 3; return; } + for (i = 0; ops2[i]; i++) if (!strncmp(src + pos, ops2[i], 2)) { strcpy(ts, ops2[i]); pos += 2; return; } + ts[0] = src[pos++]; ts[1] = 0; +} +static int is(const char *s) { return (tk == T_OP || tk == T_ID) && !strcmp(ts, s); } +static int accept(const char *s) { if (is(s)) { next(); return 1; } return 0; } +static void expect(const char *s) { if (!accept(s)) { char b[64]; sprintf(b, "expected '%s'", s); die(b); } } +static char *ident(void) { static char b[8][256]; static int k; char *r = b[k++ & 7]; if (tk != T_ID) die("expected a name"); strcpy(r, ts); next(); return r; } + +/* ---- symbols ---- */ +typedef struct { char name[64]; int kind, val, sig, defd; } Sym; +static Sym sy[4096]; static int nsy; +typedef struct { int at, sym; } Fix; +static Fix fx[65536]; static int nfx; +static Sym *find(const char *n) { int i; for (i = nsy; i--;) if (!strcmp(sy[i].name, n)) return &sy[i]; return 0; } +static Sym *add(const char *n, int kind, int val) +{ + Sym *s = find(n); + if (s && s->kind == K_FN && kind == K_FN && !s->defd) { s->val = val; return s; } /* prototype or forward use */ + s = &sy[nsy++]; strncpy(s->name, n, 63); s->kind = kind; s->val = val; s->defd = 1; s->sig = 0; return s; +} +static void fnref(const char *n) /* push a function address, fixing it up later if needed */ +{ + Sym *s = find(n); + if (!s) { s = add(n, K_FN, 0); s->defd = 0; } + if (s->kind != K_FN) die("not a function"); + if (s->defd) { lit(s->val); return; } + fx[nfx].at = here; fx[nfx++].sym = (int)(s - sy); lit(0); +} + +/* ---- locals ---- */ +typedef struct { char name[64]; int slot, arr; } Loc; +static Loc lo[1024]; static int nlo, nslot, infn; +static Loc *findl(const char *n) { int i; for (i = nlo; i--;) if (!strcmp(lo[i].name, n)) return &lo[i]; return 0; } +static void laddr(int k) { lit(ZFP); op(O_LD); if (k) { lit(k); op(O_ADD); } } + +/* ---- operator tables ---- */ +static void seq_isz(void) { lit(0); op(O_PICK); lit(0); op(O_SWAP); op(O_SUB); op(O_OR); lit(31); op(O_SHR); lit(1); op(O_ADD); } +static void seq_nz(void) { lit(0); op(O_PICK); lit(0); op(O_SWAP); op(O_SUB); op(O_OR); lit(31); op(O_SHR); lit(0); op(O_SWAP); op(O_SUB); } +static int binprec(void) +{ + static const char *n[] = { "*", "/", "%", "+", "-", "<<", ">>", "<", ">", "<=", ">=", "==", "!=", "&", "^", "|", "&&", "||", 0 }; + static int p[] = { 10, 10, 10, 9, 9, 8, 8, 7, 7, 7, 7, 6, 6, 5, 4, 3, 2, 1 }; + int i; + if (tk != T_OP) return 0; + for (i = 0; n[i]; i++) if (!strcmp(ts, n[i])) return p[i]; + return 0; +} +static void binop(const char *o) +{ + if (!strcmp(o, "*")) op(O_MUL); + else if (!strcmp(o, "/")) { fnref("zdiv"); op(O_CALL); } + else if (!strcmp(o, "%")) { fnref("zmod"); op(O_CALL); } + else if (!strcmp(o, "+")) op(O_ADD); + else if (!strcmp(o, "-")) op(O_SUB); + else if (!strcmp(o, "<<")) { fnref("zshl"); op(O_CALL); } + else if (!strcmp(o, ">>")) op(O_SHR); + else if (!strcmp(o, "<")) op(O_LT); + else if (!strcmp(o, ">")) { op(O_SWAP); op(O_LT); } + else if (!strcmp(o, "<=")) { op(O_SWAP); op(O_LT); lit(1); op(O_XOR); } + else if (!strcmp(o, ">=")) { op(O_LT); lit(1); op(O_XOR); } + else if (!strcmp(o, "==")) { op(O_SUB); seq_isz(); } + else if (!strcmp(o, "!=")) { op(O_SUB); seq_nz(); } + else if (!strcmp(o, "&")) op(O_AND); + else if (!strcmp(o, "^")) op(O_XOR); + else if (!strcmp(o, "|")) op(O_OR); + else die("bad operator"); +} + +/* ---- constant expressions (globals, array sizes, const) ---- */ +static int cexpr(void); +static int cprim(void) +{ + int v; Sym *s; + if (tk == T_NUM) { v = tv; next(); return v; } + if (accept("-")) return -cprim(); + if (accept("~")) return ~cprim(); + if (accept("(")) { v = cexpr(); expect(")"); return v; } + if (tk == T_ID && (s = find(ts)) && (s->kind == K_CONST || (s->kind != K_GVAR && s->defd))) { next(); return s->val; } + die("constant expected"); return 0; +} +static int cexpr(void) +{ + int v = cprim(); + for (;;) { + if (accept("+")) v += cprim(); else if (accept("-")) v -= cprim(); + else if (accept("*")) v *= cprim(); else if (accept("|")) v |= cprim(); + else if (accept("&")) v &= cprim(); else if (accept("<<")) v <<= cprim(); + else if (accept(">>")) v >>= cprim(); else return v; + } +} + +/* ---- expressions ---- */ +static int lv; /* the stack top is an address not yet loaded */ +static void rval(void) { if (lv) { op(O_LD); lv = 0; } } +static void expr(void); +static void strlit(void) /* inline string: jump over it, push its address */ +{ + int j = here, a, i, n = (int)strlen(ts); + lit(0); op(O_JMP); a = here; + for (i = 0; i < n; i++) emit((unsigned char)ts[i]); + emit(0); setlit(j, here); lit(a); next(); +} +static int args(void) { int n = 0; expect("("); if (!accept(")")) { do { expr(); rval(); n++; } while (accept(",")); expect(")"); } return n; } +static void asmblock(void); +static void primary(void) +{ + char *n; Loc *l; Sym *s; + lv = 0; + if (tk == T_NUM) { lit(tv); next(); return; } + if (tk == T_STR) { strlit(); return; } + if (accept("(")) { expr(); expect(")"); return; } + if (is("asm")) { next(); asmblock(); return; } + n = ident(); + if ((l = findl(n))) { laddr(l->slot); lv = !l->arr; return; } + s = find(n); + if (s && s->kind == K_CONST) { lit(s->val); return; } + if (s && s->kind == K_GVAR) { lit(s->val); lv = 1; return; } + if (s && s->kind == K_GARR) { lit(s->val); return; } + if (is("(")) { args(); fnref(n); op(O_CALL); return; } + fnref(n); +} +static void incdec(int post, int d) +{ + if (!lv) die("++/-- needs an lvalue"); + lv = 0; + if (post) { lit(0); op(O_PICK); op(O_LD); lit(0); op(O_PICK); lit(d); op(O_ADD); lit(2); op(O_PICK); op(O_ST); op(O_SWAP); op(O_DROP); } + else { lit(0); op(O_PICK); op(O_LD); lit(d); op(O_ADD); lit(0); op(O_PICK); lit(2); op(O_PICK); op(O_ST); op(O_SWAP); op(O_DROP); } +} +static void postfix(void) +{ + int n; + primary(); + for (;;) { + if (accept("[")) { rval(); expr(); rval(); expect("]"); op(O_ADD); lv = 1; } + else if (is("(")) { rval(); n = args(); lit(n); op(O_PICK); op(O_CALL); op(O_SWAP); op(O_DROP); } + else if (accept("++")) incdec(1, 1); + else if (accept("--")) incdec(1, -1); + else if (accept("as")) ident(); + else return; + } +} +static void unary(void) +{ + if (accept("-")) { unary(); rval(); lit(0); op(O_SWAP); op(O_SUB); } + else if (accept("!")) { unary(); rval(); seq_isz(); } + else if (accept("~")) { unary(); rval(); lit(-1); op(O_XOR); } + else if (accept("*")) { unary(); rval(); lv = 1; } + else if (accept("&")) { unary(); if (!lv) die("& needs an lvalue"); lv = 0; } + else if (accept("++")) { unary(); incdec(0, 1); } + else if (accept("--")) { unary(); incdec(0, -1); } + else if (tk == T_ID && (!strcmp(ts, "u") || !strcmp(ts, "m") || !strcmp(ts, "d"))) { next(); expect("*"); unary(); } /* u*x: a recast, no code */ + else postfix(); +} +static void bin(int minp) +{ + int p, a, b; char o[8]; + unary(); + while ((p = binprec()) && p >= minp) { + strcpy(o, ts); next(); rval(); + if (!strcmp(o, "&&")) { + a = here; lit(0); op(O_JZ); bin(p + 1); rval(); seq_nz(); + b = here; lit(0); op(O_JMP); setlit(a, here); lit(0); setlit(b, here); + } else if (!strcmp(o, "||")) { + a = here; lit(0); op(O_JZ); lit(1); b = here; lit(0); op(O_JMP); + setlit(a, here); bin(p + 1); rval(); seq_nz(); setlit(b, here); + } else { bin(p + 1); rval(); binop(o); } + lv = 0; + } +} +static void expr(void) +{ + char o[8]; + bin(1); + if (is("=")) { + if (!lv) die("assignment needs an lvalue"); + next(); expr(); rval(); + op(O_SWAP); lit(1); op(O_PICK); op(O_SWAP); op(O_ST); lv = 0; + } else if (tk == T_OP && strlen(ts) >= 2 && ts[strlen(ts) - 1] == '=' && strcmp(ts, "==") && strcmp(ts, "!=") && strcmp(ts, "<=") && strcmp(ts, ">=")) { + if (!lv) die("assignment needs an lvalue"); + strcpy(o, ts); o[strlen(o) - 1] = 0; next(); + lit(0); op(O_PICK); op(O_LD); lv = 0; expr(); rval(); binop(o); + op(O_SWAP); lit(1); op(O_PICK); op(O_SWAP); op(O_ST); lv = 0; + } +} + +/* ---- asm blocks: raw vm words ---- */ +static void asmblock(void) +{ + char ln[128][64], rn[256][64]; int la[128], nl = 0, ra[256], nr = 0, i, k; const char *c; + Loc *l; Sym *s; + expect("{"); + while (!accept("}")) { + if (tk == T_EOF) die("unterminated asm"); + if (tk == T_NUM) { lit(tv); next(); continue; } + if (tk == T_STR) { strlit(); continue; } + if (accept("#")) { if (tk != T_NUM) die("# needs a number"); emit(tv); next(); continue; } + if (is("-")) { next(); if (tk == T_NUM && glue) { lit(-tv); next(); } else op(O_SUB); continue; } + if (accept(":")) { strcpy(ln[nl], ident()); la[nl++] = here; continue; } + if (strlen(ts) == 1 && (c = strchr(mn, ts[0]))) { op((int)(c - mn)); next(); continue; } + if (tk != T_ID) die("bad asm token"); + if ((l = findl(ts))) { laddr(l->slot); next(); continue; } + if ((s = find(ts)) && s->kind != K_FN) { lit(s->val); next(); continue; } + if (s) { fnref(ts); next(); continue; } + strcpy(rn[nr], ts); ra[nr++] = here; lit(0); next(); /* a label of this block */ + } + for (i = 0; i < nr; i++) { + for (k = -1; ++k < nl && strcmp(ln[k], rn[i]);) ; + if (k == nl) die("unknown asm label"); + setlit(ra[i], la[k]); + } +} + +/* ---- statements ---- */ +static int brk[256][64], nbrk[256], cont[256], nloop; +static int epi[1024], nepi; +static void stmt(void); +static void block(void) +{ + int save = nlo; + expect("{"); + while (!accept("}")) { if (tk == T_EOF) die("missing }"); stmt(); } + nlo = save; +} +static void jmp_to(int t) { lit(t); op(O_JMP); } +static void loopbody(int contaddr) +{ + nbrk[nloop] = 0; cont[nloop++] = contaddr; + stmt(); + nloop--; +} +static void patchbreaks(void) { int i; for (i = 0; i < nbrk[nloop]; i++) setlit(brk[nloop][i], here); } +static void stmt(void) +{ + int a, b, c, d; + if (accept(";")) return; + if (is("{")) { block(); return; } + if (accept("let") || accept("var")) { + do { + Loc *l = &lo[nlo++]; strcpy(l->name, ident()); l->arr = 0; + if (accept("[")) { int n = cexpr(); expect("]"); nslot += n; l->slot = nslot - n; l->arr = 1; } + else l->slot = nslot++; + if (accept("=")) { expr(); rval(); laddr(l->slot); op(O_ST); } + } while (accept(",")); + expect(";"); return; + } + if (accept("unchecked")) { stmt(); return; } + if (accept("if")) { + expect("("); expr(); rval(); expect(")"); + a = here; lit(0); op(O_JZ); stmt(); + if (accept("else")) { b = here; lit(0); op(O_JMP); setlit(a, here); stmt(); setlit(b, here); } + else setlit(a, here); + return; + } + if (accept("while")) { + c = here; expect("("); expr(); rval(); expect(")"); + a = here; lit(0); op(O_JZ); + loopbody(c); jmp_to(c); setlit(a, here); patchbreaks(); return; + } + if (accept("for")) { + int save = nlo; + expect("("); + if (!is(";")) { if (is("let") || is("var")) { stmt(); goto cond; } expr(); rval(); op(O_DROP); } + expect(";"); + cond: c = here; + if (!is(";")) { expr(); rval(); } else lit(1); + expect(";"); + a = here; lit(0); op(O_JZ); b = here; lit(0); op(O_JMP); + d = here; + if (!is(")")) { expr(); rval(); op(O_DROP); } + expect(")"); jmp_to(c); + setlit(b, here); + loopbody(d); jmp_to(d); setlit(a, here); patchbreaks(); + nlo = save; return; + } + if (accept("break")) { if (!nloop) die("break outside a loop"); brk[nloop - 1][nbrk[nloop - 1]++] = here; lit(0); op(O_JMP); expect(";"); return; } + if (accept("continue")) { if (!nloop) die("continue outside a loop"); jmp_to(cont[nloop - 1]); expect(";"); return; } + if (accept("return")) { + if (!is(";")) { expr(); rval(); } else lit(0); + expect(";"); epi[nepi++] = here; lit(0); op(O_JMP); return; + } + if (is("asm")) { next(); asmblock(); return; } + expr(); rval(); op(O_DROP); expect(";"); +} + +/* ---- top level ---- */ +static void fndef(int ret) +{ + char *n = ident(); Sym *s; int np = 0, tags = 0, t, frame, i; + char pn[16][64]; + s = find(n); + if (!s || s->kind != K_FN) { s = add(n, K_FN, 0); s->defd = 0; } + expect("("); + if (!accept(")")) { + do { + t = 0; + if (accept(".")) { /* .m name, .d name */ + if (is("m")) t = 1; else if (is("d")) t = 2; else die("a parameter tag is .m or .d"); + next(); + } + if (np < 12) tags += t << (2 * np); + strcpy(pn[np++], ident()); + } while (accept(",")); + expect(")"); + } + s->sig = ret + 2 * np + 64 * tags; + if (accept(";")) return; /* prototype */ + if (s->defd) die("function defined twice"); + s->val = here; s->defd = 1; + nlo = 0; nslot = 0; nepi = 0; infn = 1; + for (i = 0; i < np; i++) { strcpy(lo[nlo].name, pn[i]); lo[nlo].arr = 0; lo[nlo++].slot = nslot++; } + /* prologue: fp -= frame, then pop the arguments into their slots */ + lit(ZFP); op(O_LD); frame = here; lit(0); op(O_SUB); lit(ZFP); op(O_ST); + for (i = np; i--;) { laddr(i); op(O_ST); } + block(); + lit(0); + for (i = 0; i < nepi; i++) setlit(epi[i], here); + lit(ZFP); op(O_LD); lit(nslot); op(O_ADD); lit(ZFP); op(O_ST); op(O_RET); + setlit(frame, nslot); + infn = 0; +} +/* enhance name(params) [-> n] micro "recipe"; or ... vm { asm }; + emits the install descriptor; the booted genome installs it */ +static int nenh; +static void enhdef(void) +{ + char *n, pn[8][64]; int np = 0, nout = 1, d, na, pay, len = 0, i, j; Sym *s; + if (tk == T_NUM) { nout = tv; next(); } /* enhance 2 name(...) */ + n = ident(); + expect("("); + if (!accept(")")) { do { if (np == 8) die("at most 8 inputs"); strcpy(pn[np++], ident()); } while (accept(",")); expect(")"); } + d = here; for (i = 0; i < 7; i++) emit(0); + na = here; for (i = 0; i < np; i++) emit(0); + for (i = 0; i < np; i++) { img[na + i - GORG] = here; for (j = 0; pn[i][j]; j++) emit((unsigned char)pn[i][j]); emit(0); } + if (accept("micro")) { pay = here; while (tk == T_STR) { for (j = 0; ts[j]; j++) emit((unsigned char)ts[j]); next(); } emit(0); } + else if (accept("vm")) { pay = here; asmblock(); op(O_RET); len = here - pay; img[d + 1 - GORG] = 1; } + else die("expected micro or vm"); + img[d - GORG] = nenh; img[d + 2 - GORG] = np; img[d + 3 - GORG] = nout; + img[d + 4 - GORG] = pay; img[d + 5 - GORG] = len; img[d + 6 - GORG] = na; + s = add(n, K_ENH, nenh++); s->sig = np + nout * 16 + d * 256; + expect(";"); +} +static void top(void) +{ + while (tk != T_EOF) { + if (accept("unchecked")) continue; + if (accept("fn")) { fndef(0); continue; } + if (accept(".")) { if (strcmp(ident(), "u")) die("the only return tag is .u"); fndef(1); continue; } /* .u name(...) */ + if (accept("const")) { do { char *n = ident(); expect("="); add(n, K_CONST, cexpr()); } while (accept(",")); expect(";"); continue; } + if (accept("enhance")) { enhdef(); continue; } + if (accept("let") || accept("var")) { + do { + char *n = ident(); + if (accept("[")) { int k = cexpr(), a = here; expect("]"); while (k--) emit(0); add(n, K_GARR, a); } + else { + int a = here; add(n, K_GVAR, a); emit(0); + if (accept("=")) { + if (tk == T_STR) { int i, l = (int)strlen(ts); img[a - GORG] = here; for (i = 0; i < l; i++) emit((unsigned char)ts[i]); emit(0); next(); } + else img[a - GORG] = cexpr(); + } + } + } while (accept(",")); + expect(";"); continue; + } + die("expected fn, var or const"); + } +} + +int main(void) +{ + static char buf[1 << 22]; int n = (int)fread(buf, 1, sizeof buf - 1, stdin), i, j; Sym *st; + buf[n] = 0; src = buf; next(); + /* image header: set the frame pointer, jump to start, then the symbol blob pointer and image end */ + lit(FTOP); lit(ZFP); op(O_ST); fnref("start"); op(O_JMP); emit(0); emit(0); + top(); + for (i = 0; i < nfx; i++) { + Sym *s = &sy[fx[i].sym]; + if (!s->defd) { fprintf(stderr, "zc0: undefined function %s\n", s->name); return 1; } + setlit(fx[i].at, s->val); + } + if (!(st = find("start")) || !st->defd) { fprintf(stderr, "zc0: no start\n"); return 1; } + /* symbol blob: kind, value, sig, name chars, 0 ... then a 0 kind */ + img[5] = here; + for (i = 0; i < nsy; i++) { + emit(sy[i].kind); emit(sy[i].val); emit(sy[i].sig); + for (j = 0; sy[i].name[j]; j++) emit((unsigned char)sy[i].name[j]); + emit(0); + } + emit(0); + img[6] = here; + printf("#define GORG %d\n#define GLEN %d\nstatic const int genome[] = {", GORG, here - GORG); + for (i = 0; i < here - GORG; i++) printf("%s%d", !i ? "\n" : i % 16 ? "," : ",\n", img[i]); + printf("\n};\n"); + return 0; +} diff --git a/docs/history/17-o3-boundaries/notes.md b/docs/history/17-o3-boundaries/notes.md new file mode 100644 index 0000000..d2f27b4 --- /dev/null +++ b/docs/history/17-o3-boundaries/notes.md @@ -0,0 +1,48 @@ +# generics after the name, leaving o3, registers by scope, cached fixed arrays + +code/ next to this file is the tree at the end of this stage. + +## message 17 + +> generics should go after fn name and before parens +> we also need some machinery for converting values to/from o3. to is pretty easy, we just hand over the data, and o3 ignores the fat metadata. from is a bit harder, there needs to be a system to get type hints and fatten the metadata for the packed data +> variables just get the latest free register, it's fine for now +> add the fixed array once per region thing +> +> also if you have any tips on how to make the memory system better (aka avoiding gc and leaks/uaf as much as possible) do tell + +## working notes + +**generics after the name.** the reason they had moved in front last stage was that `T first(...)` uses T before declaring it. the fix: while a top-level return type is read, an unknown name becomes a provisional type parameter (numbered by first use), and the `<...>` after the name renumbers them with subst. a provisional one that the list doesn't confirm, or a global whose type used one, is "T: isn't a type". the prescan learned that `NAME<...>(` is a function too: at depth 0 it skips a balanced `<...>` and checks for `(`, which also steps over `list` in return types harmlessly. + +**registers by scope.** the compiler keeps a free list of the 7 registers per region. a declaration takes the one freed last, or the next unused one; block_close gives back the registers of its scope's o3 locals. so the descriptor became (slot, register) pairs, several slots can share a register, and the core spills a register to every slot it stands for (harmless, the dead ones are never read again) and loads it from the first. the bits live in ltag (8..10), next to the o3 flag (16) and the tag (low 4). + +**cached fixed arrays.** two hidden o3 locals per `fixed []T` variable (`#items`, `#len`, names a program can't write), created when the variable is declared in o3 code, or at the start of the region for parameters and for o3 variables from outside an o3 block. refreshed after every assignment to the array variable in o3 code and at region start. indexing compares against the len variable and adds to the items variable. careful with o3 function parameters: their caches can't be created in the parameter loop, since the prologue stores parameter i to slot i and a hidden local there would shift them; they're made after the `jmp lo`, inside the region. + +**fattening.** "packed data" here is o3's new_raw object: header, no type, no zeroing. the hint that survives is the size asked for, kept as -(n + 1) in the type word (typeof_obj reads that as 0). everything else comes from the compiler's static types at the boundary. the boundaries picked: stores of pointer-typed values anywhere but an o3 local, arguments to functions not known to be o3 (F_O3, also set by the prescan when `o3` comes before a function), returns from o3 code, push, container stores (after own), and the end of an o3 block for outer o3 variables. fatten is in compiler.zc, not rt.zc, because it walks type records with tnfields/tftype/tfoff. it stops at fat objects, so walks are cheap after the first, and dyn handles (always fat) are walked with a depth limit. + +zc0 bug found on the way: `fatin(a + tfoff(t, i), tftype(t, i), dl)` (calls nested in an argument list next to arithmetic) made zc0 report "undefined function dl". rewritten with temporaries rather than fixing zc0. also `d` is reserved in zc0 too (`d*x`), so a local named d is a parse error there. + +**numbers** after this stage (ms per run, start-up removed): + +``` +backend kernel plain ms o3 ms speedup +interp collatz 324.90 240.61 1.4x +interp sieve 47.08 21.71 2.2x +interp digits 160.89 100.82 1.6x +interp fib 3.48 4.09 0.8x +llvm collatz 47.03 3.34 14.1x +llvm sieve 10.77 0.28 38.9x +llvm digits 18.93 1.45 13.1x +llvm fib 0.87 1.01 0.9x +arm64 (qemu) collatz 323.89 13.04 24.8x +arm64 (qemu) sieve 56.84 1.74 32.6x +arm64 (qemu) digits 147.06 4.17 35.3x +arm64 (qemu) fib 5.19 5.71 0.9x +``` + +the sieve got about 2x on arm64 from the cache (3.85 -> 1.74 ms). fib is flat: each call exits and re-enters the region, and that costs what the body saves. the earlier "3.9x" for fib was noise; bench.sh now grows the run count until a timing difference is over a second. + +## memory tips (answered in chat, not built) + +arenas per block for m objects that provably don't escape; borrowed-from marks on derived pointers so aliases are checked; a quarantine for freed blocks plus a debug mode that checks the header on every deref; allocation sites in a debug header so leaks report where they came from; explicit shared ownership (a counted tag) instead of the collector for the cases that really need sharing; and making unchecked code the only way into the collector. diff --git a/docs/history/18-aliases/notes.md b/docs/history/18-aliases/notes.md new file mode 100644 index 0000000..eb0afc0 --- /dev/null +++ b/docs/history/18-aliases/notes.md @@ -0,0 +1,13 @@ +# tracked aliases + +the open end was untracked aliases: `let q = p + 0` or `let c = list[1]` became a plain word, and it dangled once the owner was freed. + +## what landed + +the analyzer reuses the child link. a pointer-typed value computed from a tracked variable is stored as `S_CHILD` with no `lown` bit: `p + k` and `p - k` (not a pointer difference), `p.field` when the field is a pointer, and `p[i]` when the element is a pointer. untyped `p + k` on an owned object counts too, since that variable is a pointer even when its type is still `word`. using the alias after the owner dies is the same error a child already gets, "used after it was given away". an alias is not an owner, so it can be copied, incremented and passed on, and the scope-end free doesn't free it. an alias that outlives the block its owner was declared in is marked dead when that block closes. + +`free` no longer returns the block to the buddy lists. it leaves the header marked free and the check word in place, so `isobj` fails and a second free is a no-op, but the bytes stay. the mark treats a word with that check word and a free flag as a quarantined object and traces it. the sweep merges a quarantined block back only when nothing pointed at it. a reachable one stays quarantined, still intact. the compiler zeros a local after the free it inserts, and `start` scrubs the data stack and collects once compilation is done, so the compiler's own garbage doesn't sit on the heap forever. + +## tests + +`tests/alias.zc` uses an alias while the owner is alive (arithmetic, a pointer field, an index). `tests/errors/alias_plus.zc`, `alias_index.zc`, `alias_field.zc` and `alias_scope.zc` are the uses after the owner dies. `tests/quarantine.zc` is unchecked: after `free`, churn that would have reused the block, the old words are still there, `free` through the alias doesn't smash them, and a collection while an exact pointer is still live keeps the block. an interior pointer (`p + k` with `k` not zero) does not match the check word, so a later collection reclaims that block; exact pointers (`p + 0`, a pointer field, a pointer element) keep it. an untyped index stays a plain word, since the element might be an integer, and quarantine is what covers that case. diff --git a/docs/history/19-tools/notes.md b/docs/history/19-tools/notes.md new file mode 100644 index 0000000..6989b80 --- /dev/null +++ b/docs/history/19-tools/notes.md @@ -0,0 +1,25 @@ +# memory debug, a bit more c, and a small host surface + +stage 18 tracks an alias that is still a name in the analyzer, and quarantines a block so a stale exact pointer reads intact bytes. this stage is the next layer: diagnostics you turn on, syntax people reach for, and enough of a host to write a small tool. + +## memory debug + +`memdebug on` (or `mem_set(1|4)`) records the source line of each `new` in a 64-slot table. the slot stores `p ^ MAGIC`, so the collector does not treat the table as a root. `memreport` prints `leak: line N W words` for slots whose block is still live and returns the count. W is the buddy payload, header included in the block but not in the count. + +checks walk at most 64 words back from a heap address to a header whose payload covers it. a dead header is `zy: use after free from line N` and the program stops. `free` of a block that is already quarantined prints `zy: double free from line N` and returns. the site stays until the storage is reused, which is why the second free still knows the line. + +bit 2 fills the payload with 57005 after children are released. bit 4 teaches the mark to accept an interior pointer the same way, so `p + k` keeps the block. it is off by default: a stale integer in that window would pin a block and the self-compile doesn't want that. programs that never say `memdebug` compile and run as before. + +`gc` zeros its own locals before scanning frames. an uninitialized slot was a root, and a leftover pointer from `free` was enough to keep a buddy chunk from merging. + +## syntax + +`do`/`while`, `enum` constants (the name is an int type), and `(T)e` for a simple type. `include "path"` reads a file with `readfile` and compiles it on the source stack the compiler already had, signatures first. `u`, `m` and `d` stay reserved. + +## host + +a store of an op to word `0xA0002` is a syscall: open, close, read, write, getenv, time. arguments are the next three words. `lib/std.zc` is not in the genome. it has atoi, itoa, strcat, starts, env, now and writefile. `examples/cli.zc` prints the length and first character of `$ZC_FILE`. + +## left alone + +the collector still scans the whole data stack, because the vm can't read its stack pointer. an alias that leaves a function, or an untyped index, is still not a compile-time borrow. interior pinning is opt-in and only 64 words deep. `include` is a path the host can open, not a search path. o3 stores don't hit the syscall gate. casts the parser can't see in a few tokens stay on `as`. diff --git a/docs/history/20-words/notes.md b/docs/history/20-words/notes.md new file mode 100644 index 0000000..ae63b79 --- /dev/null +++ b/docs/history/20-words/notes.md @@ -0,0 +1,22 @@ +# 20 — 64-bit words + +the vm word went from 32 bits to 64. memory is still word-addressed, and the map (heap, frames, stacks, `GORG`, the syscall gate) is the same word indices. each cell is 8 bytes on the host. there is no dual-width flag. + +## what changed + +- the image is little-endian 64-bit words: `zc0`, `mkgenome` and `tools/build.zc` read and write 8 bytes per word. `genome.h` is `int64_t`. +- a literal is still an odd word. `w >> 1` is a 63-bit signed immediate. the resident `lit` (`v + v + 1`) did not need a new formula. zero tests (`!`, `!!`, `==`, `!=`) shift by 63, in `zc0` and in the operator recipes. +- the interpreter, the enhancement evaluator and the o3 region interpreter use 64-bit cells. shifts mask with 63. `mulhs` / `mulhu` are the high half of a 128-bit product. `clz` of 0 is 64. signed and unsigned division by a constant use the 64-bit magic-number sequence, and `INT64_MIN / -1` stays defined. +- the arm64 jit loads and stores 8-byte cells, does alu in the x registers, and builds 64-bit immediates with up to four `movk` halfwords. the stack index is a byte offset shifted by 3. the llvm path uses i64 for vm values and i128 for the multiply-high. neither backend was executed here (no `aarch64` toolchain, no `llvm-config`); both were typechecked as far as this host allows, and the jit path compiles. +- `zshl` and the software divider cover 64 bits. `putn` and `itoa` print the full magnitude, including the minimum word. the mandelbrot fixed-point multiply is a multiply and an arithmetic shift, which matches the old 16.16 picture. the popcount recipe uses 64-bit masks; `popcount(0xff00ff)` is still 16. +- calling convention is unchanged: arguments are words, frame slots are word indices, and the signature packs the same small fields (`(ssig >> 1) & 31` is still the arity, not a shift width). the 6-word object header, buddy classes and the heap word counts are unchanged. `tests/buddy.zc` still expects `131072 262144 1 0`. + +## abi + +images, `genome.h` and any saved binary are not compatible with the 32-bit machine. source that fits in a 63-bit signed literal recompiles. the minimum word is not itself a literal. + +this is still not a C abi. a character is still one word, not one byte. the compiler's own source buffer still packs four characters into a word. calls do not match a host calling convention. + +## check + +`test.sh` on the interpreter: fixpoint at 91158 words, `genome.h` matches, 57 checks, 0 failed. that includes aliases, quarantine, memdebug, syscalls, `lib/std.zc`, o3, and `tests/wide.zc` (`1 << 40` and equality of that value). diff --git a/docs/history/21-micro/notes.md b/docs/history/21-micro/notes.md new file mode 100644 index 0000000..77c8d8b --- /dev/null +++ b/docs/history/21-micro/notes.md @@ -0,0 +1,23 @@ +# 21 — microcore + +a second interpreter, small enough to carry the genome to a new target. `core.c` is unchanged and still the jit, the llvm backend, o3 and the enhancement optimizer. + +## what it is + +`micro.c` (467 lines) is the base machine: 1m words, the two stacks, opcodes 0..21, call and return, and a copy of `genome.h` at `GORG`. it includes `stdint.h` and `genome.h` and nothing else. compiled with `-ffreestanding`, the only undefined symbols are the five hooks in `micro_port.h`: read a byte, write a byte, end of input, the syscall gate, and a diagnostic line. bss must be cleared before `micro_run`. the image is still 8 mb. on a cpu without a 64-bit divider the compiler's division helpers get linked as well; the object built here does not reference them. + +`port_linux.c` is the hosted port the suite runs (stdin, stdout, the six syscalls, stderr for the overflow lines). `port_stub.c` is the bare-metal sketch: a uart transmit register, input already finished, and every syscall returning -1. + +## enhancements + +`/` `%` `<<` are not base opcodes. the genome installs `idiv`, `imod`, `ishl`, `lfa` and `outside` through opcode 21 at boot, and user programs install more. a built-in for only the first three would miss `lfa` and `outside`, which o3 code emits as ordinary opcodes, and would miss `enhance` in the tests. pointing the operators at `zdiv` in `rt.zc` would make a different genome from the one `core.c` runs. so the microcore lowers a recipe, or a straight-line `vm` body, to a flat micro-op list and runs it. no constant folding, no multiply-high division. `x / 7` is a divide; the quotient matches. a `vm` body with i/o or a branch fails install with -4, so an `else` fallback still runs. install kind 2 (an o3 region) returns 0 and is ignored. the bytecode of that region still runs, which is why the o3 tests print the same numbers. + +the caps: 128 ops and 256 values in one enhancement, 4096 ops in the pool. reinstalling the most recent slot reuses its pool space; other reinstalls leak until the pool is full, and then install returns -5. an enhancement's stores do not enter the syscall gate, same as `core.c`. + +## left out + +no jit, no llvm, no o3 register interpreter, no coverage map, no threads. speed is the plain interpreter, slower still where `core.c` rewrote a constant divide. still not a C ABI. a character is still one word. the compiler's source buffer still packs four characters into a word. + +## check + +`test.sh` runs the interpreter suite and then the same examples and feature tests on `micro.c` linked with `port_linux.c`. output matches, including the self-compile against the interpreter's image. `micro.c` and `port_stub.c` compile with `-ffreestanding`. diff --git a/docs/history/22-size/notes.md b/docs/history/22-size/notes.md new file mode 100644 index 0000000..b282189 --- /dev/null +++ b/docs/history/22-size/notes.md @@ -0,0 +1,82 @@ +# 22 — memory size + +the machine was a fixed 1,048,576 words, 8 mb, and the map was copied as hex into `core.c`, `micro.c`, `zc0.c`, `mkgenome.c`, `src/rt.zc`, `src/compiler.zc` and `tools/build.zc`. a board with a couple of megabytes of ram cannot boot that. + +## the choice + +addresses in the genome are literals. a `const` is folded into the symbol table and into every instruction that uses it. the boot stub is `lit FTOP; lit ZFP; store; jump start`, and `lfa` is the recipe string `ld 655360`. three ways to support another size: + +1. one map the genome reads at boot, and every address computed from it. that is a different instruction stream. the 1m image would not stay byte-identical, and the whole compiler would be adding bases it currently treats as constants. +2. one set of formulas of `MW` (`HEAP_LO = MW>>2`, `GORG = MW-(MW>>2)`, …) shared by every size. those formulas reproduce the 1m map, and `const SYSGATE = ZFP + 2` was compiled both ways and the self-hosted images matched. they do not leave room below 1m words. the compiler's tables are about 0x24000 words and the image is about 91000. scaling the 1m gaps shrinks the tables with the machine, and at 262144 words the gap from the frame pointer to the genome is 32768 words, which cannot hold them. +3. one genome per size, from one layout. the default sources keep the historical numbers. a smaller build is a copy of those sources with a different const block, compiled by `zc0` against the same `memmap.h` the c cores use. + +(3) is what landed. it is the smallest change that keeps the 1m image byte-identical. the cost is that a size is a rebuild, not a boot flag: the 1m `genome.h` does not run in the small core, and the small image does not run in the 1m core. `tools/genmap.c` writes the directory (`memmap.h`, `rt.zc`, and for the compiler profile `compiler.zc`). `lfa`'s immediate is rewritten there and nowhere else; the checked-in recipe stays `ld 655360`. + +`SYSGATE`, the type area, the local-type tables and the exit records are formulas in the shared sources (`ZFP + 2`, `ZFP - 16384`, `R_ASM + 1024`, `stype + NS`). they fold to the old numbers, so the default image does not change. `genmap` rewrites `ZFP - 16384` to `ZFP - 8192` in the small copy only, and lowers the snapshot cap from 8000 to 1000 and the quote cap from 2700 to 300 there too. those three literals stay in `src/compiler.zc`. + +the 1m cores read `memmap.h`. `MW` has to be a multiple of 4096 so the arm64 `cmp xn, #MW` still fits in a shifted imm12. at 1<<20 that instruction is still `0xf144001f`. + +## what was measured + +on the 1m compiler, after compiling one program (the peaks, not the totals at `main`, except where the counter does not shrink): + +| | symbols | pool words | snapshot | quote tokens | code words | +|---|---|---|---|---|---| +| hello | 689 | 5695 | | | 171 | +| types | 701 | 5823 | | | 1016 | +| o3 | 700 | 5827 | 99 | | 1649 | +| meta | 697 | 5811 | 22 | 14 | 685 | +| life | 696 | 5781 | 182 | | 852 | +| joins | 695 | 5773 | 68 | | 736 | + +an empty `main` is already ~688 symbols and ~5700 pool words: that is the compiler itself. the data stack high water on the 1m machine was 1028 words for every program tried, including a self-compile. frame depth peaked at 451 during that self-compile and under 350 compiling tests. return depth peaked at 80. the interpreter's data-stack check sits 1100 words above `DSLO`, so the data region has to be at least about 2200 words or a shallow program looks like an overflow. + +`tests/gc.zc` keeps 1000 live 16-word blocks (16000 words) through its first loop, then allocates far past the heap so the collector runs. the live-object count only matches the 1m output if that first loop does not collect. + +## the floors + +**full compiler: 172032 words, 1376256 bytes.** `genmap small`. stage 27 raised the genome reservation from 90000 to 92000 words, and that machine is now 176128 words. + +``` +0 .. 8192 program +8192 .. 28672 heap (20480) +28672 .. 36864 types (8192), not overlapping the heap +36864 ZFP, syscall gate at 36866 +37120 .. 75264 compiler tables +75520 genome, 87661 words (zc0), ends at 163181 +165520 .. 167568 frame +167568 .. 168080 return +168080 .. 168592 mark (512) +168592 .. 172016 data +``` + +verified on the microcore and on `core.c`, output matched the 1m interpreter: hello, fact, lists, typed, wiring, life, basics, wide, gc, types, alias, meta, arrays, joins, enhance, o3, csyn, umd, quarantine, and all three stack overflows. both microcores (this one and the run-only one) compile with `-ffreestanding`. + +**run-only: 32768 words, 262144 bytes.** `genmap run`, then `rt.zc` + `src/run.zc` + one program, compiled by `zc0`. no resident compiler and none of its tables. `src/run.zc` supplies `start` and `gc_roots`. + +``` +0 .. 512 unused (code is in the genome) +512 .. 16896 heap (16384) +16928 ZFP, syscall gate at 16930 +17152 genome +27392 .. 28416 frame +28416 .. 28672 return +28672 .. 28928 mark (256) +28928 .. 32752 data +``` + +hello is 9458 words, fact 9558, wide 9523, selfpatch 9578, echo 9458, gc 9666. gc's image ends at 26818, 574 words below the frame stack. output matched, including echo reading `abc` from stdin (there is no backtick, the program is already compiled). + +256 kb cannot hold the compiler. 87661 words of image is about 700 kb before a heap, tables or a stack. + +## limits + +- `examples/primes.zc` wants a 60000-word array, a 65536-word buddy block. the small heap is 20480 words and the program stops with `zc: out of heap memory`. the compiler is not the problem; grow the heap in `place_small` and MW follows. +- the small machine does not self-host. `tools/build.zc` writes at word `0x10000`, and `build_image` allows `org + 0x30000` words. low memory here is 8192 words. producing the small image is `zc0`'s job, which is also why the small image is 87661 words and the self-hosted 1m image is 91158: those two compilers do not emit identical bytes, only the self-hosted fixpoint does. +- `tests/buddy.zc` expects `131072 262144`, the 1m heap's top-level chunks. left out of the subset. +- run-only source has to be the `zc0` subset. `tests/basics.zc` uses a block expression and `zc0` rejects it. types, `o3` and `quote` are out too. +- the small compiler will die, rather than smash a neighbour, past 1000 snapshot words, 300 quote tokens, 1024 symbols or an 8192-word string pool. the peaks above are well under those. a program that is a lot bigger than `life.zc` or `o3.zc` may need a knob turned up in `genmap`. +- the 1m type area still overlaps the top of the heap. that is historical and the image depends on it. the small map does not overlap. +- still not a C ABI. a character is still one word. the microcore still has no o3 fast path. + +stage 25 folded `place_small` and `place_run` into one layout. the sizes above are now `genmap --preset small` and `genmap --preset run`, and a different heap is `--preset small --heap N`. see `history/25-genmap`. diff --git a/docs/history/23-baremetal/notes.md b/docs/history/23-baremetal/notes.md new file mode 100644 index 0000000..eed69fe --- /dev/null +++ b/docs/history/23-baremetal/notes.md @@ -0,0 +1,85 @@ +# 23 — bare metal + +the microcore already compiled with `-ffreestanding` and `port_stub.c`, and it already fit in 256KB when the program was baked in. nothing had executed that image. the stub's `uart_putc` was empty. this stage boots it on QEMU's `virt` board, with no OS, and the program's own output on the emulated UART. + +## which machines + +the host is x86_64. what was installable: + +- `gcc-riscv64-linux-gnu` and `gcc-aarch64-linux-gnu` (gcc 13) +- `qemu-system-misc` (8.2.2), which provides `qemu-system-riscv64` +- `qemu-system-arm` (8.2.2), which provides `qemu-system-aarch64` + +`qemu-aarch64`, the linux-user emulator the jit section of `test.sh` looks for, is still not installed. a system emulator does not satisfy that check, and the jit stayed skipped. + +two boards booted. + +**RISC-V `virt`.** `qemu-system-riscv64 -machine virt -bios none -cpu rv64 -m 64 -nographic -monitor none -serial stdio -kernel bare.elf`. `-bios none` means no OpenSBI, so the core comes up in machine mode at the ELF entry. RAM is at `0x80000000`. the linker script claims 48MB of it. the UART is a 16550 at `0x10000000` (byte registers: holding/receive at 0, interrupt enable at 1, FIFO control at 2, line control at 3, line status at 5; receive-ready is bit 0, transmit-empty is bit 5). power-off is the SiFive test device at `0x100000`. writing `0x5555` makes QEMU exit 0. writing `0x13333` is `FINISHER_FAIL` with status 1. + +**AArch64 `virt`.** `qemu-system-aarch64 -machine virt -cpu cortex-a57 -m 64 -nographic -monitor none -serial stdio -kernel bare.elf`. RAM is at `0x40000000`, same 48MB in the linker, 64MB given to QEMU. the UART is a PL011 at `0x09000000`, 32-bit registers, the flag register at offset `0x18` (receive FIFO empty is bit 4, transmit FIFO full is bit 5). power-off is PSCI `SYSTEM_OFF`: `hvc #0` with `x0 = 0x84000008`. that call does not take a status, so a guest that returns an error from `micro_run` still looks like a clean exit. the UART transcript is the result that matters. + +this QEMU starts that ELF at EL1. the startup still checks `CurrentEL` and, at EL2, sets `HCR_EL2.RW`, `SPSR_EL2 = 0x3c5`, and `eret`s to EL1h. that path was not taken on the boots below. `-bios none` is not on the command line. on this 8.2 build it is a filename, and QEMU exits with `Could not find ROM image 'none'`. `-kernel` of an ELF is loaded and entered directly. + +**Cortex-M was not tried.** the word is `int64_t` and the run-only bss is about 400KB before the genome in rodata. `qemu-system-arm`'s small boards (Stellaris `lm3s6965`, 64KB of SRAM) cannot hold it, and a 32-bit core would do every cell in software. `virt` has the RAM and a 64-bit CPU, so that is the port. + +## the image + +`baremetal/start_*.S` sets a 64KB stack, zeroes bss eight bytes at a time, and calls `bare_main`. `baremetal/port.c` is the five `micro_port.h` hooks. syscalls return -1. `baremetal/run.sh` is the build and the QEMU command, with `timeout` so a hang does not stick (120 seconds for `run`, 90 for `compile`). + +two modes. + +`run` asks `genmap` for the 32768-word map, compiles `rt.zc` plus `src/run.zc` plus the program with `zc0`, and links that `genome.h` into the ELF with `-DBARE_INPUT=0`. `micro_eof` is true and `micro_getc` returns -1. the program is already in the image. + +`compile` copies the default `genome.h` and `memmap.h` (1,048,576 words, the self-hosted compiler) and links with `-DBARE_INPUT=1`. the script pipes the source and a trailing backtick into the UART. `virt` has 64MB, so the 8MB machine fits and there was no reason to boot the 172032-word compiler instead. the copies sit next to `micro.c` because `#include "genome.h"` searches the including file's directory first. + +the cross compile is `-ffreestanding -fno-pic -nostdlib -static -O2`. RISC-V adds `-march=rv64gc -mabi=lp64d -mcmodel=medany -mno-relax`. AArch64 adds `-march=armv8-a`. `--build-id=none` keeps the linker from warning about a discarded note. + +## what the first boots did + +RISC-V hello printed `hello, world!` on the first try and QEMU exited 0. the 16550 needed no setup for that, and it must stay that way. an early port wrote the line-control and FIFO-enable registers. QEMU has already delivered the first source bytes by then, and enabling the FIFO dropped the `f` of `fn`. the compiler then said `zc: line 1: n: isn't a type`. `uart_init` on RISC-V is empty. + +AArch64 hello produced nothing and sat until `timeout`. the instruction trace showed `_start`, the bss clear, `bare_main`, then `micro_run`, then a jump to guest address `0x200`, which is the synchronous-exception vector when `VBAR_EL1` is 0. the faulting instruction was `ldr q0`, the `-O2` copy of the genome. `CPACR_EL1.FPEN` is 0 out of reset, so SIMD traps. startup now sets `FPEN` to 3 (and clears `CPTR_EL2.TFP` on the EL2 path) and issues an `isb`. RISC-V sets `mstatus.FS` for the same reason; that core had not emitted a float op yet, but the abi is `lp64d`. + +after those two fixes, both boards printed the same bytes as the hosted interpreter. + +run-only, no input: + +``` +hello, world! +1101110101111100000000 +3628800 +1099511627776 1 1 1 -1 +1000 1 900 1 199900 +``` + +those are `examples/hello.zc`, `examples/fact.zc`, `tests/wide.zc` and `tests/gc.zc`. `gc` took 28 seconds on RISC-V and 32 on AArch64. the second `churn(200000)` is a lot of interpreted allocation, and a 20 second timeout cut it off after `1000 1 900`. + +the resident compiler, source plus a backtick on the UART, for hello, fact and wide, printed the same three results (fact's two lines, wide's one). the compiler image itself does not depend on which file is piped in. + +## sizes + +`size` on the ELF, text and data added together. bss is the word array, the enhancement tables and the 64KB stack. + +| image | ELF bytes | text+data | bss | +|---|---|---|---| +| RISC-V run, hello | 87704 | 81741 | 410664 | +| RISC-V run, fact | 88504 | 82541 | 410664 | +| AArch64 run, hello | 149704 | 82248 | 410656 | +| RISC-V compiler | 741624 | 735565 | 8537128 | +| AArch64 compiler | 803616 | 736040 | 8537136 | + +the AArch64 file is larger than its text and data because the load segment's alignment is 64KB. the compiler's text is mostly the default genome, 91158 words, 729264 bytes. run-only bss is the 256KB machine. compiler bss is the 8MB one. + +## input, and what was left out + +`micro_eof` is not allowed to block. the compiler calls it after every `getc`, including the backtick that ends the unit. if eof waited for another byte, a source that ends in a backtick would hang. an empty FIFO therefore means "a byte may still arrive", not "the file is over". the one real end-of-file byte is `0x04`, and only when it is already sitting in the FIFO: it is consumed and not returned to the program. a program that loops on `eof()`, the way `examples/echo.zc` does, needs that byte queued behind the payload. the 16550 FIFO holds 16 bytes, so a longer stream can make the peek see `0x04` too early or not at all. echo-until-eof is not in the bare-metal checks. the backtick protocol does not need it. + +other limits, unchanged from the hosted microcore except where the board forces them: + +- every syscall returns -1 +- install kind 2 (an o3 region) succeeds and is ignored; the bytecode runs +- diagnostics share the UART with program output +- a run-only program has to be the `zc0` subset +- 256KB still cannot hold the compiler +- not a C ABI, and a character is still one word +- AArch64 power-off cannot report a guest failure status diff --git a/docs/history/24-backends/notes.md b/docs/history/24-backends/notes.md new file mode 100644 index 0000000..2512369 --- /dev/null +++ b/docs/history/24-backends/notes.md @@ -0,0 +1,49 @@ +# 24 — the arm64 jit and the llvm backend, actually run + +both backends were updated when the word went to 64 bits, and neither had been executed since. the jit section of `test.sh` wants `aarch64-linux-gnu-gcc` and `qemu-aarch64` (linux-user). `qemu-system-aarch64` does not satisfy that check. the llvm section wants `llvm-config`. this host had the cross gcc and the system emulators from the bare-metal stage, and not the other two. they were installed here: `qemu-user` 8.2.2 (provides `/usr/bin/qemu-aarch64`), `qemu-user-binfmt`, `libc6-dev-arm64-cross` (the sysroot a static aarch64 link needs), and `llvm-dev` (LLVM 18.1.3, `llvm-config` works). clang 18.1.3 was already present. the suite links the llvm binary with gcc, not clang. + +## how each one was built and run + +the hosted interpreter, for the comparison, is the usual host binary: `gcc -O2 -w -DJTYPE=int core.c io_linux.c`. host gcc is 13.3.0, the machine is x86_64. + +the jit: + +``` +aarch64-linux-gnu-gcc -O2 -w -DJTYPE=int -static core.c io_linux.c -o zyj +qemu-aarch64 zyj < program.zc +``` + +`aarch64-linux-gnu-gcc` is 13.3.0. `-static` is what `test.sh` passes, so the binary does not need the aarch64 dynamic loader at run time. the smoke binary was 1,428,296 bytes. `ZYTRACE=1` counts lines containing `bytes of arm64`. + +the llvm backend: + +``` +gcc -O2 -w -DJTYPE=int -DZYLLVM $(llvm-config --cflags) core.c io_linux.c \ + -o zyl $(llvm-config --ldflags --libs) +``` + +`llvm-config` is 18.1.3. `ZYTRACE=1` counts lines containing `compiled by llvm`. `ZYHOT=1` compiles every block, not only the hot ones, and `test.sh` checks that `tests/types.zc` then matches the interpreter's stdout by md5. + +## what matched + +a short smoke, before the full script: `examples/hello.zc`, `examples/fact.zc`, `tests/wide.zc` and `tests/o3.zc` on both backends printed the same stdout as the interpreter. `tests/o3.zc` lifted 8 regions on each (`bytes of arm64`, `compiled by llvm`). + +then `bash test.sh`, which is the existing list, not a new one. the fixpoint stayed 91158 words and `genome.h` matched. the interpreter, the microcore, the 172032-word compiler, the 32768-word run-only image, and both virt boards (7 boots each) passed as before. the jit ran that same `run` list under qemu-user, plus the 8-region count and a self-compile compared with `g1.bin`. the llvm binary ran the same list, plus the 8-region count, the `ZYHOT=1` md5, and its own self-compile. the second full run, after the fix below, was 292 passed, 0 failed. + +## the one failure + +the first full run was 291 passed, 1 failed: `jit virtual enhancements`, stdout `100 0 343 3,2 ` instead of `100 0 343 3,2 5 -2 0`. exit status 0. the interpreter and the llvm backend both printed the full line. + +`tests/enhance.zc` installs `noisy` as vm code whose body does a `putc`. `lift_vm` refuses i/o and `install` returns `-4`. the compiler, when that return is negative, sets the fallback bit and emits a call to `soft_noisy` instead of the opcode. the prints that did come out are `clampadd`, `cube` and `divmod`, which install cleanly. the halt is the next call, `noisy(4)`. + +opcode 21 in the jit is a `blr` to `inst_hook`, which returns `int`, then a 64-bit store of `x0` onto the data stack. on aarch64 a write to `w0` zeroes bits 63:32. `-4` in `w0` is `0x00000000fffffffc` in `x0`, which as a cell is 4294967292, and `r < 0` in the compiler is false. the fallback bit stays clear, the compiler emits the opcode, the slot was never installed, and the translator's unwired path is `movn x0, #0` and a branch to the exit stub. that is a clean halt, which is why the status was 0 and the line simply stopped. + +the llvm path does not have this hole. its install helper is `static Cell linstall(Cell d) { return install((int)d); }`, and the C conversion sign-extends. the interpreter assigns `install`'s `int` straight into a `Cell`. + +the fix is one instruction after that `blr`: `sxtw x0, w0` (`0x93407c00`), then the store. `tests/enhance.zc` under `qemu-aarch64` then printed `100 0 343 3,2 5 -2 0`, and `examples/wiring.zc` still printed `16 1234 25 4/9`. the genome did not change. the instruction is in the translator, so a Darwin build of the same core gets it too, but that build was not run. + +## what was not run + +- the Darwin jit. `core.c` takes a different path there (`MAP_JIT`, `pthread_jit_write_protect_np`, `sys_icache_invalidate`). this machine is x86_64 Linux. qemu-user executes the linux aarch64 ELF; it does not execute that path. +- real aarch64 hardware. every jit instruction ran inside qemu-user 8.2.2. +- the jit and llvm backends on the small and run-only maps, and on the bare-metal boards. `test.sh` runs those maps on the interpreter and the microcore. the two native backends stay on the default 1,048,576-word genome. diff --git a/docs/history/25-genmap/notes.md b/docs/history/25-genmap/notes.md new file mode 100644 index 0000000..65e322e --- /dev/null +++ b/docs/history/25-genmap/notes.md @@ -0,0 +1,28 @@ +# 25 — one memory-size layout + +the sizes lived in four places that had to be kept in step by hand: the numbers in `memmap.h`, the `// @map-rt` block in `src/rt.zc`, the `// @map-cc` block in `src/compiler.zc`, and two functions in `tools/genmap.c` (`place_small`, `place_run`). there were no extra checked-in copies of the runtime. the history snapshots stay as they were. `src/run.zc` is the run-only boot stub, not a second map, and it stayed. + +`tools/genmap.c` is now one packer. a `Spec` says whether this is a compiler or a run-only image, and the sizes that actually differ: heap low and heap length, a type region, a pad before the frame-pointer word, the genome reservation, and the frame, return, mark and data stacks. compiler tables (hash, pool, symbols, locals, forward refs, snapshots, quotes, source stack, operator sequences, contexts, loop stack) have a tight default and a historical one. `layout` places the regions from the bottom. `--words` pins MW and gives whatever is left to the data stack. without it, MW is the packed size rounded up to a multiple of 4096, which is what the arm64 `cmp xn, #MW` encoding needs. + +## presets + +`--preset small` and `--preset run` are the two maps from stage 22. `--preset default` is the historical 1,048,576-word machine. that one is not the tight compiler stretched to 1m words. the type area still overlaps the top of the heap (`ZFP == HEAP_HI`, and `R_TY = ZFP - 16384` stays in the source), the tables stay large (string pool 40704 words, 4096 symbols, snapshot and quote buffers 8192), and the gaps between the genome and the frame stack stay where they were. packing those sizes reproduces the old addresses: heap `0x40000..0xA0000`, tables `0xA0100..0xBFF00`, `GORG 0xC0000`, frame `0xE0000..0xF0000`, data `0xFC000..0xFFFF0`. + +the checked-in sources are still that default, so `cat src/rt.zc src/compiler.zc | zc0` is the same compile as before. `genmap --preset default --check` reads `memmap.h` and the two const blocks and exits non-zero if a number disagrees. the small copy still rewrites, in the copy only, `ZFP - 16384` to `ZFP - 8192`, the snapshot cap 8000 to 1000, and both quote caps 2700 to 300. `lfa` is still the string `ld 655360` in `src/rt.zc`. the copy gets `ld` of that build's `ZFP`. + +## the same bytes another way + +the flag form of the two old profiles was diffed against `--preset`. same `memmap.h`, `rt.zc`, `compiler.zc` and `map.txt`: + +``` +genmap --mode compiler --name small --heap-lo 8192 --heap 20480 --types 8192 \ + --genome 90000 --frame 2048 --return 512 --mark 512 --data 3072 \ + --snap-cap 1000 --quote-cap 300 -o build/small +genmap --mode run --words 32768 -o build/run +``` + +`genmap small DIR` and `genmap run DIR` still work. `test.sh` and `baremetal/run.sh` call the new flags. a bigger heap for `primes.zc` is `genmap --preset small --heap 65536`, and MW follows. + +what did not change: the 1m genome, the 172032-word compiler, the 32768-word run-only image. a `--mode compiler --words 1048576` without the default preset is a different machine (tight tables, type region of its own, packed from the bottom). it is not a substitute for `--preset default`. + +`test.sh` after this stage: 295 passed, 0 failed. the three new checks are the default `--check` and the two flag-form diffs. the fixpoint stayed 91158 words. diff --git a/docs/history/26-bareapi/notes.md b/docs/history/26-bareapi/notes.md new file mode 100644 index 0000000..c7294f4 --- /dev/null +++ b/docs/history/26-bareapi/notes.md @@ -0,0 +1,25 @@ +# 26 — bare-metal syscalls + +the virt port's `micro_syscall` returned -1 for every op. the gate itself was already there (`SYSGATE`, the same numbers the hosted core uses), and `port_stub.c` still returns -1, so a board with no devices keeps building. `port_linux.c` and `core.c`'s `hostcall` grew the three new numbers too: 7 exits the process, 8 is a counter, 9 reports `MW` and the heap. the genome did not change. the test programs pass the numbers as literals, so `src/rt.zc` stays as it was. + +## console, and eof that waits + +`examples/echo.zc` is `while (!eof()) putc(getc())`. on the host, `micro_eof` blocks in `getchar` until the next byte or a real end. the old UART port returned immediately, and an empty FIFO counted as "not the end", so a `0x04` that had not arrived yet was read by `getc` as a failure and then printed. it also could not see an end that was sitting behind more than 16 bytes, which is the 16550's FIFO. + +`BARE_INPUT=1` now waits in both `micro_getc` and `micro_eof` until a byte is in the register. `0x04` is the end and is not returned. a pause with an empty FIFO is just a wait, so the length of the stream does not matter. `run` mode is still `BARE_INPUT=0`: eof is true, getc is -1, and a program that never reads does not sit on the UART. + +the compiler's backtick is unchanged. the opcode that reads a byte then probes eof, and that probe used to deadlock if it waited, because the probe runs before the compiler has noticed the backtick. `compile` therefore writes the source, a backtick, and a `0x04`. the probe after the backtick finds the `0x04` and returns. hello, fact and wide compiled over the UART still print what they printed before. + +`io` mode is a run-only image with the UART open. the script appends `0x04` to stdin. both boards, fed `abcdefghijklmnopqrstuvwxyz0123456789` (36 bytes, past the FIFO), printed that string and exited 0. the hosted interpreter, given the same letters after the backtick, printed the same string. + +## the rest of the gate + +time is the hardware counter. RISC-V is `rdtime` (the virt machine's `mtime`). AArch64 is `cntvct_el0`. neither is unix seconds, so `tests/sys.zc` is still a hosted test. `tests/bareio.zc` reads it, spins, reads it again, and prints `tick` when the second value is at least the first and neither is negative. both boards did. + +exit is a status, not only success or failure. RISC-V writes the SiFive test device at `0x100000`: `0x5555` exits 0, and `(status << 16) | 0x3333` exits with that status. the high half is 16 bits. AArch64 is semihosting `SYS_EXIT`: `hlt #0xf000` with `x0 = 0x18` and `x1` pointing at `{0x20026, status}` (`ADP_Stopped_ApplicationExit`). the first try hung because the stack pointer was unset in a throwaway ELF, and because PSCI `SYSTEM_OFF` cannot carry a status. the boot path already has a stack. QEMU is started with `-semihosting-config enable=on,target=native`. `tests/bareexit.zc` prints `bye` and calls exit 3. both boards exited 3 and did not print `still`. a guest that returns from `micro_run` with an error now exits 1 on AArch64 too. + +open, close, read and write are four RAM files, 128 bytes each, names up to 31 characters. they are not the host filesystem. `tests/bareio.zc` writes `zy` to `note` and reads it back: `2 zy`. getenv answers `BOARD` with `riscv` or `aarch64` and -1 for anything else. random is an xorshift seeded from the counter, and it is never -1. memory op 0 is `MW`, which on the run-only image is 32768. the line from both boards was `tick 2 zy rand board 32768`. + +`port_stub.c` and a freestanding compile of it are unchanged in behaviour. the linux port still serves real files for ops 1 to 4. + +`test.sh` runs the echo, the bareio line, the exit text and the exit code on each board that is present. with both virt boards, that is eight checks on top of the 295 from stage 25. the full script passed 303, and failed 0. the fixpoint is still 91158 words. diff --git a/docs/history/27-float/notes.md b/docs/history/27-float/notes.md new file mode 100644 index 0000000..4420a32 --- /dev/null +++ b/docs/history/27-float/notes.md @@ -0,0 +1,19 @@ +# 27 — floating point + +a float is an IEEE-754 binary64 in one 64-bit word. an odd literal only has 63 bits, and bit 0 of a double is part of the number, so a float literal is not an odd word. opcode 600 (`flit`, the even word 1200) is followed by the raw bits, and every decoder consumes that word inside the instruction so a payload with bit 0 set is not executed. 601..611 are add, sub, mul, div, eq, lt, le, int-to-float, float-to-int (trunc toward zero), print and parse. they sit above 533, so the enhancement slots do not move and an old image still means what it meant. + +every target here has an FPU: x86_64 SSE2, AArch64, and RV64 with the D extension. the interpreter, the microcore and both bare-metal images use the host `double` (gcc emits the hardware op). the arm64 jit emits `fadd`/`fsub`/`fmul`/`fdiv`, `fcmp` plus `cset` (`mi` and `ls`, not `lt` and `le`, because an unordered compare must not count as less), `scvtf` and `fcvtzs`. the llvm backend uses `fadd` and the rest on a double, bitcast to and from i64. print and parse are `fops.h`, not libc, so a board with no C library writes the same text. the form is an optional minus, the integer digits, a dot, and six fractional digits. a tie in the seventh digit rounds away from zero. the non-finite texts are `nan`, `inf` and `-inf`. a parsed significand keeps at most 15 digits. magnitudes past about 1e32 print as `inf` because the scaled integer no longer fits in 128 bits. + +the resident lexer stays inside the zc0 subset: it copies the spelling into `tbuf` and runs opcode 611 (`asm { tbuf #1222 }`), which is the same parser zc0 calls in C. neither compiler source contains a float literal. codegen emits `flit` plus the bits. a local that is assigned a float gets bit 5 of its tag, and a later load is still a float. one integer operand of `+ - * /` or a comparison is converted. the result of a comparison is an int. a call's result is not known to be a float, `++` and `--` stay integer adds and clear the flag, and o3 does not lift these opcodes (the region runs as bytecode). `0.0` is false where a word is tested, because its bits are zero. `-0.0` is true, because the sign bit is set. `f2i` of a value outside int64 saturates in the interpreter, the microcore and the arm64 jit; the llvm conversion is only promised for values that fit, which is all the tests use. + +`tests/float.zc` is in the zc0 subset, so the same source runs on the resident compiler, the microcore, the arm64 jit, the llvm backend, the small compiler, the run-only image, and both virt boards (run-only and, on the boards, compiled by the resident compiler). the line is + +``` +4.000000 4.500000 3.000000 2.500000 2.500000 0.001500 0.100000 -0.125000 inf -inf nan 1 1 1 0 0 3 -3 3.000000 -2.000000 2 +``` + +`1 + 1.5` is the second `2.500000`. `10 / 4` at the end is still integer division. + +the image grew from 91158 words to 93712. the fixpoint holds. the small compiler's hole was 90000 words and the new image is 90138, so the reservation is 92000 and that machine is 176128 words instead of 172032. the heap, the tables and the run-only map are the same. + +`test.sh` after this stage: 315 passed, 0 failed. the twelve new checks are the float line on the interpreter, the microcore, the arm64 jit and the llvm backend, the small microcore and `core.c` at that size, the run-only image, both virt boards running the program and compiling it, and one llvm run with `ZYHOT=1` so every float block is compiled. the line matched on all of them. diff --git a/docs/history/28-bytes/notes.md b/docs/history/28-bytes/notes.md new file mode 100644 index 0000000..24a67fa --- /dev/null +++ b/docs/history/28-bytes/notes.md @@ -0,0 +1,21 @@ +# 28 — byte addressing + +memory stays an array of 64-bit words. a byte address is a second numbering of that same image: word `w` occupies bytes `w << 3` through `(w << 3) + 7`, and byte 0 of a word is its low 8 bits. `baddr` and `waddr` convert. the odd/even tag is only for code words (a literal is an odd word, an opcode is even), so a byte address is a plain integer and can be odd. `u`, `m` and `d` stay on the word pointer of a heap object. a byte address into a payload is not itself a tracked owner. + +the opcodes sit above the floats, so the enhancement slots do not move: + +``` +612 ld8 613 st8 614 ld16 615 st16 616 ld32 617 st32 +``` + +16- and 32-bit accesses are little-endian and may cross a word boundary. an access that does not lie entirely inside memory loads 0 and stores nothing. the runtime names are `ld8`, `st8`, `ld16`, `st16`, `ld32`, `st32`. `bops.h` is the one implementation. the interpreter, the microcore and the llvm backend call it. the arm64 jit calls the same functions, so a spanning store is one path on every target. a byte store of a word some translation covers goes through the same hook as opcode 2, and the block is dropped. it does not run a syscall. the gate is still a word store at `SYSGATE`. + +`tests/bytes.zc` checks a spanning 16-bit store, a 32-bit store, the two words those stores build, an address off the end of memory, a 1000-byte checksum (30768), and a rewrite of the literal in `f` from `A` to `B` to `C` by storing one byte of that code word. on the jit that store drops the translation twice. the line is the same on the interpreter, the microcore, the arm64 jit, llvm with `ZYHOT=1`, the small compiler, the run-only image, and both virt boards. + +`tests/bench_bytes.zc` is a run-only image, so the timing does not include the resident compiler. it walks 4000 bytes of a 512-byte buffer, repeated, and the slope between one repetition and many is the loop. the checksum of one repetition is 123504. + +the run-only hole was 10240 words. the byte runtime plus `tests/bytes.zc` is 10365, so the reservation is 11264 and that machine is still 32768 words. the frame, return and mark stacks moved up by 1024 and the data stack gave up those words. hello on that map is 9742 words. the fixpoint image grew from 93712 words to 94083. + +`test.sh` after this stage: 327 passed, 0 failed. the twelve new checks are the byte line on the interpreter, the microcore, the arm64 jit and the llvm backend, the small microcore and `core.c` at that size, the run-only image, both virt boards running the program and compiling it, and one llvm run with `ZYHOT=1`. + +`bench_bytes.sh` times a run-only image, 4000 byte loads per repetition, slope of 1 repetition against 40. checksum 123504. on this host: interpreter 1.24 ms, microcore 1.20 ms, arm64 jit under qemu-user 0.53 ms, llvm 0.50 ms. the llvm slope includes compiling the loop once, because one repetition stays under the default hot threshold and forty do not. diff --git a/docs/history/29-strings/notes.md b/docs/history/29-strings/notes.md new file mode 100644 index 0000000..9d96087 --- /dev/null +++ b/docs/history/29-strings/notes.md @@ -0,0 +1,21 @@ +# 29 — byte strings + +word strings stay one character per word, 0 terminated. `"hi"` did not change, and neither did ops 1 to 5. a new literal, `b"..."`, is a packed string on the byte addressing from stage 28. the `b` has to sit against the quote; `b "hi"` is still a name plus a word string. + +the layout is the same for a literal in the code and an object on the heap. word 0 is the length, not counting the trailing 0. the bytes start at the next word, eight per word, little-endian, and a 0 byte follows the last character. when the length is a multiple of 8, including 0, that 0 occupies a word of its own. a literal is a jump over the data, the length as a raw word, the packed words, then a literal of the length word's address. `snew(n)` is `new(1 + ((n + 8) >> 3))`, which is one length word plus enough words for the bytes and the trailing 0. + +the runtime names are `slen`, `sbyte`, `snew`, `scat`, `sslice`, `scmp`, `seqs`, `slt`, `sle`, `bputs`, `ston` and `ntos`. `sbyte` returns 0 outside the length. `sslice` takes an exclusive end and clamps. `scmp` returns -1, 0 or 1 over unsigned bytes. `ntos` of the minimum word copies the word-string `"-9223372036854775808"` into a byte string. heap results are fresh owned objects, so the analyzer requires a `let`. they are not shared yet. + +the resident compiler and `zc0` both track which value is a byte string. on the resident compiler that is bit 6 of the local's tag. `len` of one calls `slen`. `s[i]` calls `sbyte` and is not a place to store. `s[i:j]` calls `sslice`. `+` and the six comparisons require a byte string on both sides; one side is an error. `puts` of one byte string calls `bputs`. `++` and `+=` clear the bit. + +ops 10 to 13 are the packed versions of open, read, write and getenv. the pointer they take is `baddr(s + 1)`, the payload, not the object. the hosted core, the linux microcore port and the virt-board port implement them. the stub port still returns -1. the RAM file store on the boards is unchanged, so `tests/bstr_io.zc` stays hosted. `lib/std.zc` keeps `atoi`, `itoa`, `strcat`, `starts`, `env` and `writefile`, and adds `batoi`, `bitoa`, `bstarts`, `benv` and `bwritefile`. + +the 1m map stores type records in the last 16384 words of the heap, and the type list is the first words of that range. holding those words as a buddy block does not survive `types_init`, which writes the list over the header, and the next large `new` was then handed the type records. the self-host died at `let pow2[64]` with `program too big` because `tword`'s size word had become the source text `= 0;`. the compiler now cuts the allocator off at `R_TY` after `heap_init` and leaves it cut. the sweep stops at the same line, so it does not walk a header that is really the type list. `tests/buddy.zc` still prints `131072 262144 1 0`: the bottom chunk is untouched, and the 20000 small blocks fragment it the same way. the small map keeps its type area above the heap, so the cut does not apply there. + +`tests/strings.zc` prints `hi! 3 3 104 i! 1 1 1 -42 42 -42 11 1 -9223372036854775808 0 1 3 10 1 1 1 W ab`. `examples/strings.zc` prints `hello, zc` and `9 42`. the fixpoint image is 100011 words, 800088 bytes. the stage-0 image of the same sources is 96068 words. the run-only reservation is 14336 words in a 36864-word machine; `tests/bytes.zc` is the largest of those images at 13191 words, and hello is 12568. the small compiler's stage-0 image is 96067 words against a reservation of 97280, in a 180224-word machine. + +`tests/bench_strings.zc` is a run-only image. one repetition scans the 36-character literal 200 times, concatenates a `!`, and slices `[1:8]`. the checksum is 674542. `bench_strings.sh` times that without the resident compiler. on this host: interpreter 5.499 ms, microcore 5.22 ms, arm64 jit under qemu-user 3.171 ms, llvm 0.235 ms. the llvm slope is the compiled loop; the first repetition pays to compile it. + +`test.sh` after this stage: 350 passed, 0 failed. the twenty-three new checks are the string line, `examples/strings.zc`, and `tests/bstr_io.zc` on the interpreter, the microcore, the arm64 jit and the llvm backend; the string line and the example on the small microcore and on `core.c` at that size; the run-only image of both; both virt boards running `tests/strings.zc` and compiling it; and one llvm run with `ZYHOT=1`. `tests/bstr_io.zc` is hosted only. the fixpoint is the same 100011 words on the interpreter, the microcore, the jit and llvm. + +what is still open: heap strings are uniquely owned, not shared, so a structure cannot point at one substring from two places without copying. there is no mutation through `s[i]`. `ston` stops at the first non-digit and does not say so. comparison is unsigned bytes, not a character set. part 3 is the collector that can share these objects. diff --git a/docs/history/30-sharing/notes.md b/docs/history/30-sharing/notes.md new file mode 100644 index 0000000..ff1671f --- /dev/null +++ b/docs/history/30-sharing/notes.md @@ -0,0 +1,17 @@ +# 30 — shared structure + +the heap already mark-sweeps. this stage adds objects many pointers can hold, collected by that sweep when none of them remain. there is no second heap and no reference count. a cycle is just a mark that sees the same cell twice. + +bit 8 of the header flags (`F_SHARE`, value 8) means shared. bits 0..2 stay what they were: live, releasing or marked, and the pin bit on a quarantined block something still points at. `share(p)` sets bit 8, clears the owner, and leaves the object live. `free` of a shared object returns without quarantining and without poisoning. `own` will not record an owner for one, and `release` will not follow one, so a unique container can point at a shared child without freeing it. the sweep masks the low three bits: an unmarked live cell is freed whether or not bit 8 is set, and a marked cell is restored to live with bit 8 kept. unique `free` still quarantines, and the sweep still returns a quarantined block only when nothing points at it. memdebug's allocation note is the one `new` already writes. `car`, `cdr` and `tget` call `checkptr` when bit 1 of the memdebug flags is on, so a shared cell is checked the same way as any other heap pointer. + +`cons(a, b)` and `triple(a, b, c)` allocate, publish any child that is a heap object, and publish the cell. publishing a child means a later `free` from its old owner does not clear a cell that still points at it. `snew`, `scat`, `sslice` and `ntos` return shared strings, so a byte string is a value you can copy. code literals `b"..."` are not heap objects; they live in the image. `is_shared` reads the bit. + +`tests/share.zc` builds two cells on one pair, a string held by two cells, a cycle, and a triple, then drops every root, scrubs the data stack and collects. the line is `1 2 1 4 1 1 1 1 42 1 1 1 6 0`. the last 0 is `live_objects` back to the baseline, on the resident compiler and on a run-only image. `tests/share_dbg.zc` turns memdebug on, frees a shared cell, and reads it back: `7 1`. + +the fixpoint image is 100757 words. the run-only reservation stays 14336 in the 36864-word machine; `tests/bytes.zc` is the largest of those images at 13871 words. the small compiler's stage-0 image is 96736 words against 97280, in the 180224-word machine. neither hole moved. the type-area cut from stage 29 stays: the 1m compiler still does not hand 0x9C000..0x9FFFF back to the allocator. + +`tests/bench_share.zc` is a run-only image. it builds one 40-cell spine, hangs two parents off it, and walks the spine 200 times. the checksum of one repetition is 156003. `bench_share.sh` times that without the resident compiler. on this host: interpreter 2.939 ms, microcore 2.679 ms, arm64 jit under qemu-user 1.733 ms, llvm 0.434 ms. + +`test.sh` after this stage: 368 passed, 0 failed. the eighteen new checks are the sharing line and the memdebug line on the interpreter, the microcore, the arm64 jit and the llvm backend; both of those on the small microcore and on `core.c` at that size; the run-only sharing line; both virt boards running `tests/share.zc` and compiling it; and one llvm run with `ZYHOT=1`. the fixpoint is the same 100757 words on the interpreter, the microcore, the jit and llvm. + +what is still open: a store through a shared pointer is not trapped, so immutability is the discipline of `car`, `cdr` and `tget` rather than a hardware check. the cycle test uses that store on purpose. the collector is still conservative, so a stale stack word can keep a cell alive until `scrub`. a shared object is not quarantined, so after the sweep reclaims it a leftover pointer is dangling; `checkptr` reports that on the next `car`. there is no write barrier and no compaction. diff --git a/docs/history/31-fpu/notes.md b/docs/history/31-fpu/notes.md new file mode 100644 index 0000000..5fe01d7 --- /dev/null +++ b/docs/history/31-fpu/notes.md @@ -0,0 +1,23 @@ +# 31 — hardware float, and a soft fallback + +float arithmetic was already a host `double` on the interpreter and the microcore, and the arm64 jit and llvm already lowered 601..609 themselves. this stage makes that split explicit and adds the path for a machine with no FPU. + +the arm64 jit emits the FPU directly: `fadd`, `fsub`, `fmul`, `fdiv`, `fcmp` plus `cset` (`mi` and `ls`, so unordered is not less), `scvtf` and `fcvtzs`. print and parse stay calls, because they are formatting. llvm builds `fadd`/`fsub`/`fmul`/`fdiv`, `fcmp` (`oeq`/`olt`/`ole`), `sitofp` and `fptosi` on a double bitcast to i64. neither backend calls a C helper for those opcodes. + +the interpreter and the microcore go through `fops.h`. by default that is a `volatile double`, so the C compiler emits the FPU rather than folding the operation: x86_64 `addsd`/`mulsd`/`ucomisd`/`cvtsi2sd`/`cvttsd2si`, AArch64 `fadd`/`fmul`/`fcmp`/`scvtf`/`fcvtzs`, RV64 `fadd.d`/`fmul.d`/`flt.d`/`fcvt.d.l`/`fcvt.l.d` with `-mabi=lp64d`. the virt boards already set the FPU live before any C runs. RISC-V writes `mstatus.FS` to Dirty (`0x6000`). AArch64 clears the FP trap at EL2 and sets `CPACR_EL1.FPEN`. both images are built hard-float (`-march=rv64gc -mabi=lp64d`, `-march=armv8-a`). + +`-DZY_SOFTFLOAT` replaces those entry points with an integer IEEE binary64. round ties to even. a NaN becomes a canonical quiet NaN; every other finite result matches the host FPU, including signed zero, subnormals and overflow to infinity. the parser scales with the same add/mul/div, so a soft build and a hard build read a literal the same way. there is still no libc. an AArch64 compile of that path with `-mgeneral-regs-only` is free of `fadd` and of `bl` into a helper. + +which path a build takes: + +- hosted interpreter, x86_64: hardware, SSE2, via `fops.h` +- hosted microcore, x86_64: the same +- arm64 jit under qemu-user: native FPU instructions in the translator +- llvm backend: native `double` ops +- RISC-V virt: FPU enabled in the boot stub, `fadd.d` in the microcore +- AArch64 virt: FPU enabled in the boot stub, `fadd` in the microcore +- `-DZY_SOFTFLOAT`: integer fallback, selected at build time. the float test line is the same + +`test.sh` compiles one interpreter that way and checks `tests/float.zc`. the line is unchanged, so the fixpoint is still 100757 words. the suite after this stage was 369 passed, 0 failed. the one new check is that soft-float interpreter. + +what is still open: a call result, a global, a field, a parameter and a return are not yet known to be floats. `-0.0` is still true in `if`, because the sign bit is set. `o3` still does not lift 600..611. magnitudes past about 1e32 still print as `inf`, and a parsed significand is still at most 15 digits. diff --git a/docs/history/32-ftype/notes.md b/docs/history/32-ftype/notes.md new file mode 100644 index 0000000..bc119f6 --- /dev/null +++ b/docs/history/32-ftype/notes.md @@ -0,0 +1,17 @@ +# 32 — float is a type + +a float used to be a bit on a local. a call, a global, a field, a parameter and a return forgot it, so the next `+` was an integer add unless the programmer wrote the literal again. `-0.0` was true in `if`, because the test was the integer one and the sign bit is set. `1.field` was a float, because a dot after digits was always a fraction. + +`float` is type kind 12. a literal, a local declared `float`, a global, a field, an index, a dereference, a parameter and a call result are floats. assigning an int into a float converts with opcode 608, and assigning a float into an int converts with 609. a `word` keeps the bits. `+ - * /` and the six comparisons promote whichever side is not already a float. `++` stays an integer add. + +the vm's `jz` is still an integer test. where a float is used as a condition (`if`, `while`, `for`, `do`, `?:`, `&&`, `||`, `!`) the compiler emits a compare with `+0` and inverts it, so both zeros are false and every other value, including a NaN, is true. `zc0` emits the same test, so a run-only image does too. the spelling `-0.0` is unary minus of `0.0`, and `0 - 0` is `+0`; a product such as `0.0 * -1.0` is a real negative zero, and that is the value the test covers. + +`1.field` is an integer and a field. the dot is a fraction only when a digit or an exponent follows it (`1.5`, `1.`, `1.e2`, `1.+2`). a letter is a field name. an integer or a word used as the base resolves a field name that belongs to exactly one struct in that program, and the address is the base plus the field's offset. `tests/ftype.zc` prints `&(1.field)` as 2, because `field` is the second word of `cell`, and it reads and writes that field through a word that holds an object's address. + +`tests/ftruth.zc` is in the zc0 subset. the line is `00111000110107`. `tests/ftype.zc` needs the resident compiler. the line is `3.500000 10.500000 3.500000 4.250000 5.250000 4.250000 6.500000 2 4 4.250000 8.500000 1 4`. + +the fixpoint image is 103348 words, 826784 bytes. the stage-0 image is 99284 words. the small compiler's stage-0 image is 99283 words, so its reservation moved from 97280 to 100352 and that machine is now 184320 words. the run-only reservation stays 14336 in the 36864-word machine. `tests/ftruth.zc` there is 13693 words. + +`test.sh` after this stage is 391 passed, 0 failed. the new checks are the negative-zero line and the float-type line on the interpreter, the microcore, the arm64 jit and the llvm backend; both lines on the small microcore and on `core.c` at that size; the run-only image of the negative-zero program; both virt boards running that program and compiling it and the float-type program; a soft-float interpreter on the negative-zero line; and `ZYHOT=1` on both. + +what is still open: `o3` does not lift 600..609, and a region that contains a float literal can be misread because the lifter does not skip the payload word. magnitudes past about 1e32 still print as `inf`, and a parsed significand is still at most 15 digits. diff --git a/docs/history/33-fo3/notes.md b/docs/history/33-fo3/notes.md new file mode 100644 index 0000000..60d515d --- /dev/null +++ b/docs/history/33-fo3/notes.md @@ -0,0 +1,24 @@ +# 33 — o3 lifts floats + +`o3` used to leave 600..609 in the bytecode, and a float literal could split a region: the word after opcode 600 is raw bits, and a payload of zero is the halt opcode if the lifter reads it as an instruction. discovery and the block lifter now consume that word themselves. print and parse (610 and 611) still leave the region. + +the lifted ops are `fadd`, `fsub`, `fmul`, `fdiv`, `feq`, `flt`, `fle`, `i2f` and `f2i`. two constant operands fold by running that same operation, so the fold is the IEEE result, including a NaN and a signed zero. nothing rewrites `x - x` to zero, `x + 0` to `x`, or `x * 1` to `x`. + +a float variable's install word is negative. that home is a `d` register on arm64 and a `double` alloca on llvm, so a loop that only adds and multiplies does not move the value through an integer register. an integer variable still lives in an `x` register. a freed register keeps its class, because one var index cannot be both. the interpreter runs the same micro-ops through `fops.h`, which is the host FPU unless the build selected `-DZY_SOFTFLOAT`. + +`tests/fo3.zc` prints `5997 5997 4.000000 nan 0 3.250000 1 0 0`. the two 5997s are `(int)` of the same polynomial, plain and `o3`, at 2000 steps. `1.5 + 2.5` folds to 4. `nan - nan` stays `nan`. a negative zero is false in `if` and `while` inside the region. `1.0 < 2.0` is 1, and a NaN is not less than 1. `tests/o3.zc` still lifts 8 regions. + +the fixpoint image is 103655 words, 829240 bytes. the stage-0 image is 99591 words. the small compiler's stage-0 image is 99590 words, inside the 100352 reservation (762 words of room). the run-only map did not move. + +`tests/fbench.zc` times `poly(20000)` without bootstrapping: the slope between 1 repetition and N drops startup and compile time. `(int)` of one call is 59997. before the lift, an o3 copy of that loop did not finish, because the payload of `0.0` was read as halt, so the before number is the plain bytecode. milliseconds per call: + +- interpreter, before 5.318, after (o3) 0.869, about 6.1 times +- llvm, before 0.167, after 0.037, about 4.5 times +- arm64 jit under qemu-user, before 1.620, after 0.292, about 5.5 times +- microcore, before 5.194, after 5.054. it does not lift o3, so both are the bytecode + +the plain loop after the change is the same speed as before (interpreter 5.268, llvm 0.163, arm64 1.625, microcore 5.179). + +`test.sh` after this stage is 401 passed, 0 failed. + +what was still open here, and closed in stage 34: magnitudes past about 1e32 printed as `inf`, and a parsed significand stopped at 15 digits. diff --git a/docs/history/34-ftext/notes.md b/docs/history/34-ftext/notes.md new file mode 100644 index 0000000..6f0c7ae --- /dev/null +++ b/docs/history/34-ftext/notes.md @@ -0,0 +1,11 @@ +# 34 — float text + +opcode 610 still prints six digits after the dot, and a tie in the next digit still rounds away from zero, so the existing float line does not move. it no longer gives up past about 1e32: the scaled magnitude is a base-2^32 integer, so a finite value prints its digits. `-0` stays `-0.000000`, and a nan is `nan` whatever its sign bit. + +opcode 618 (the word 1236) pops `(bits prec mode)`. mode 0 is the shortest spelling that parses back to the same bits: a plain decimal when the exponent is between -4 and 15, otherwise scientific (`1e+308`, `1e-10`, `-0`). mode 1 is fixed, `prec` digits after the dot. mode 2 is scientific, `prec` significant digits. `prec` is capped at 17. print and parse stay out of an o3 region, the same way 610 and 611 do. + +the parser keeps the digits of the word it is given (eighty, past that a sticky bit) and converts `significand * 10^exp` with the same integer arithmetic. ties round to even, which is why `9007199254740993` and `9007199254740992` are one double and the next integer is not. `inf`, `-inf`, `nan` and `-nan` are accepted, and an exponent past the double range is an infinity or a zero. nothing here calls libc, so the text does not depend on the host printf. the arm64 jit and llvm call the same C function the interpreter does; the arithmetic opcodes are still native. + +`tests/ftext.zc` prints `1e+308 1e-10 -0 inf -inf nan 0.1 1.50 1.250e+0 100000000000000000000.000000 1e+40 1111111111`. the ten ones are bit checks: `1.5` against `3/2`, `0.1` against `1/10`, the infinities, negative zero, the 17-digit tie, a subnormal, an underflow to zero, and a signed nan. the compiler and the runtime were not rebuilt for this; `fops.h` is the whole conversion. + +`test.sh` after this stage is 414 passed, 0 failed. diff --git a/docs/history/35-bench/notes.md b/docs/history/35-bench/notes.md new file mode 100644 index 0000000..914bbba --- /dev/null +++ b/docs/history/35-bench/notes.md @@ -0,0 +1,82 @@ +# 35 — zc against C + +two programs, each written in zc and in C, timed without bootstrapping the compiler. the sieve is the integer loop, where o3 and the llvm backend should look good. the tree is the allocator and the call, where they should not. + +`bench/sieve.zc` and `bench/sieve.c` count primes below 100000. one word per flag, marks start at `i * i`, checksum 9592. zc has three kernels: a plain `fixed []int` (bounds-checked), the same function marked `o3`, and an `o3` loop through a raw `*int` with no per-index check. C has the same word loop, plus a byte-flag loop that is a different representation. `bench/trees.zc` and `bench/trees.c` build a full binary tree of depth 12 and walk it. checksum 8191, the node count. zc `new(node)` / `free`, the same marked `o3`, and one array used as an arena of pairs. C uses `malloc` / `free`, and the same arena shape. a node in zc is two pointers plus the 6-word buddy header, which rounds to 8 words (64 bytes). the C node is two pointers (16 bytes). + +the number is the slope between 1 repetition and N, so process startup falls out, and so does the time zc spends compiling the benchmark. five slopes, median, min and max. pinned to one cpu with `taskset`. the vms are built at `-O3`. C is `gcc` and `clang` at `-O3 -march=native`, then again with `-flto`, and gcc again with `-fprofile-generate` / `-fprofile-use`. LTO and PGO stayed inside the spread on both programs, so the tables below use the plain `-O3 -march=native` line for C and mention the others only where they differ. `ZYHOT=1` on the llvm vm, so every block is compiled, and each o3 region is compiled with llvm's O3 pipeline at install time. the arm64 jit is `qemu-aarch64` 8.2.2 running an aarch64 binary on this x86 host. that is emulation, not arm64 hardware. the microcore is not in the table: it does not lift o3. + +the machine: Linux 6.12.94+ x86_64, KVM, 4 vCPUs. `/proc/cpuinfo` says Intel Xeon, family 6 model 207; the hypervisor leaves the model name as "Intel(R) Xeon(R) Processor". gcc 13.3.0, clang 18.1.3, llvm 18.1.3. + +the llvm tree rows were retimed with about 3 seconds of kernel work (n = 220, 155 and 38419). the first calibration aimed at 0.8 seconds and undershot, because compiling the benchmark under `ZYHOT=1` takes about 9 seconds and a short slope drowns in that jitter. `bench/run.py` now grows N until the measured gap actually reaches the target. the other rows already had a tight spread at 0.8 seconds, except the llvm raw sieve, whose five samples still all sit between 1.4x and 1.7x the gcc sieve. + +## sieve + +milliseconds per count of the primes below 100000. + +| path | median | min | max | +| --- | ---: | ---: | ---: | +| gcc -O3 -march=native, word flags | 0.149 | 0.149 | 0.150 | +| clang -O3 -march=native, word flags | 0.157 | 0.157 | 0.173 | +| gcc -O3 -march=native -flto, byte flags | 0.081 | 0.080 | 0.081 | +| zc interpreter, plain, bounds-checked | 214.3 | 212.8 | 215.7 | +| zc interpreter, o3, bounds-checked | 22.92 | 22.91 | 22.93 | +| zc interpreter, o3, raw pointer | 14.36 | 14.26 | 15.84 | +| zc llvm, ZYHOT=1, o3 raw pointer | 0.219 | 0.209 | 0.256 | +| zc llvm, ZYHOT=1, o3 bounds-checked | 0.640 | 0.633 | 0.667 | +| zc arm64 jit, qemu-user, o3 raw pointer | 2.094 | 2.075 | 2.102 | + +gcc with `-flto` was 0.149, gcc with PGO was 0.149, clang with `-flto` was 0.158. + +on the same word algorithm, the llvm backend is about 1.5x the best C. that is the strength. the interpreter running the lifted blocks is about 65x slower than that llvm region (14.4 ms against 0.22 ms), and the plain bytecode is about 9x slower again than the lifted blocks (214 ms against 23 ms). the bounds check costs 1.6x on the interpreter and 2.9x once llvm has made the arithmetic cheap (0.64 ms against 0.22 ms). + +the arm64 column is qemu-user. 2.09 ms is 14x the native gcc number and only 6.9x faster than the native interpreter running the same lifted blocks. both of those comparisons are skewed: qemu is translating aarch64, and the interpreter is x86 host code. the ratio that is safe to read is jit against itself. it is not a measurement of arm64 hardware. + +the byte sieve is the same algorithm in 1 byte per flag. gcc runs it in 0.081 ms, 1.8x faster than the word sieve. zc cannot take that path on the fast tier: byte loads and stores are opcodes 612..617, and `lift_block` leaves the region on anything it does not recognize. + +## trees + +milliseconds per tree of depth 12, built and walked, then released. + +| path | median | min | max | +| --- | ---: | ---: | ---: | +| gcc -O3 -march=native, malloc | 0.092 | 0.091 | 0.092 | +| clang -O3 -march=native, malloc | 0.110 | 0.110 | 0.111 | +| gcc -O3 -march=native -flto, arena | 0.0144 | 0.0143 | 0.0144 | +| clang -O3 -march=native -flto, arena | 0.0308 | 0.0304 | 0.0311 | +| zc interpreter, plain new/free | 178.1 | 177.6 | 179.4 | +| zc interpreter, o3 new/free | 269.4 | 269.2 | 273.9 | +| zc interpreter, o3 arena | 2.108 | 2.102 | 2.164 | +| zc llvm, ZYHOT=1, plain new/free | 13.69 | 13.14 | 14.60 | +| zc llvm, ZYHOT=1, o3 new/free | 23.70 | 21.67 | 28.14 | +| zc llvm, ZYHOT=1, o3 arena | 0.282 | 0.269 | 0.291 | +| zc arm64 jit, qemu-user, plain new/free | 80.83 | 80.64 | 83.65 | +| zc arm64 jit, qemu-user, o3 arena | 1.667 | 1.624 | 1.691 | + +gcc malloc with `-flto` was 0.093, with PGO 0.092. clang malloc with `-flto` was 0.110. + +`new` / `free` on the llvm backend is about 150x gcc `malloc` (13.7 ms against 0.092 ms). the same backend walking an arena, with no allocator in the loop, is 0.28 ms, about 20x gcc's arena (0.014 ms) and about 50x faster than zc's own `new`. so most of the tree gap is the allocator, and what is left after the allocator is gone is the call. marking the tree `o3` makes it slower: 269 ms against 178 ms on the interpreter, 24 ms against 14 ms on llvm. `new` in o3 code is `new_raw`, and every return and every store of a child calls `fatten` and `own`, which are ordinary calls out of the region. + +`free` only quarantines. the timed loop does not call `gc` itself; the next `new` sweeps when an 8-word block will not fit. an earlier single slope that called `gc` on every tree was about twice as slow. forty iterations completed without `scrub`. the sweep still scans the whole data-stack area (`gc_roots` marks `DSLO`..`DSHI`), so a shape that leaves a pointer in an unused slot would pin the quarantined nodes. `scrub` only overwrites 1024 words below the stack pointer. + +under qemu the arena jit (1.67 ms) is barely faster than the interpreter's arena (2.11 ms). the body between calls is a handful of micro-ops, and every call returns to the driver. the sieve, whose inner loop never calls, is where the jit pays off. + +## what is missing, by impact + +1. the buddy allocator. `new_raw` in `src/rt.zc` adds a 6-word header and rounds up to a power of two, so a two-pointer node is 64 bytes. `free` quarantines instead of pushing the block back on its size-class list; the next failed allocation runs `gc`, which walks the whole heap. on llvm that is the difference between 13.7 ms and 0.28 ms, and against gcc `malloc` it is about 150x. fix: a bump allocator the program can opt into (the arena kernel is that, written by hand), and a free path that returns a block the program has already dropped to the free list without a full sweep. stamp the type in `new` so the header does not have to be repaired later. + +2. the call ABI. `lift_block` ends the block at a call (opcode 6, `T_CALL`). `run` spills the region, returns to the C driver, dispatches the callee, and re-enters at the return point. a depth-12 tree is 8191 calls to build and 8191 calls to walk, and the llvm arena is still 20x gcc's arena (0.28 ms against 0.014 ms). the same exit is why `o3` cannot speed the tree up. fix: when the callee is an o3 region, compile a native call to its entry with the arguments in registers, and only spill to the vm stack when the callee is ordinary bytecode. + +3. bounds checks on `fixed []T`. the o3 index path in `src/compiler.zc` compares the index with `outside` and, on the failure path, calls `oob`. in the lifted sieve that compare spills the index to the data stack and reloads it on the way back, on every in-bounds store. llvm pays 2.9x for it (0.64 ms against 0.22 ms). fix: keep the index in a register across the check, and drop the check when the loop bound was already compared against the length. a raw `*int` is the escape hatch that already exists; the checked form should not be this far from it. + +4. the lifted blocks are too small, and nothing vectorizes. the raw sieve's inner step is a compare block, an empty block, a store block and an add block, four dispatches per flag. that is the interpreter's 14 ms. llvm's O3 pipeline closes most of it (down to 0.22 ms) but does not match gcc, which fills and counts with `ymm` stores and `vpaddd` (0.15 ms, 1.5x faster). fix: merge an empty block into its successor and rotate the loop before codegen, so both `rexec` and llvm see one block per iteration. gcc's leftover advantage is the vectorizer; a region whose loops are single blocks gives llvm's vectorizer a chance at the fill and the count. + +5. `fatten` and `own` on the way out of o3. storing a fresh child emits a call to `own`, and because the value has pointer fields the compiler also calls `fatten` (`emitown`, and again on an o3 return). `fatten` stamps the type `new_raw` left blank and walks children that are already stamped. the o3 tree is about 1.5x slower than the plain one on the interpreter, and about 1.7x slower on llvm, for that. fix: pass the type into the allocation so the header is born stamped, and inline `p[-2] = owner` as a store in the region when memdebug is off. do not fatten a pointer that was allocated as that type a few instructions earlier. + +6. byte addressing is not on the fast path. opcodes 612..617 fall into the `leave_at` default in `lift_block`, so a byte sieve would run as bytecode even inside `o3`. the C byte sieve shows the prize is about 1.8x on this size, less than the gaps above, and it grows for a bigger limit where the word sieve stops fitting in cache. fix: lift `ld8` / `st8` / `ld16` / `st16` / `ld32` / `st32` the same way the word load and store are lifted, in the interpreter, the arm64 jit and the llvm region. + +7. the collector's roots, and quarantine. `gc_roots` marks every word from `DSLO` to `DSHI`, not just the live stack, so a pointer left above the stack pointer keeps a freed block quarantined forever. `scrub` clears 1024 words and is easy to forget; this tree happened to overwrite those slots before the sweep. fix: mark from the stack pointer to the top, and treat a block the program freed, whose pointer it has cleared, as immediately reusable. + +8. library and language gaps the two programs ran into. there is no arena and no packed array in `lib/std.zc`, so the fast tree is an index bump written out by hand and the fast sieve is a word per flag. a small arena and a packed byte buffer in the library would make those the ordinary spelling. `u`, `m` and `d` are still reserved as bare words, so the first draft of the tree would not compile with a parameter named `d` (`if (d > 0)` is read as a tag). fix: only treat those letters as tags after a dot. `new` inside `o3` does not zero its memory (`new_raw`); the leaves had to store null children or the walk would read garbage. fix: have the compiler emit those zero stores in the region, or zero pointer fields in `new_raw` when the type is known. + +the register file is part of the same ceiling. a region has seven variable registers (`RMAXV`). the bounds-checked sieve already uses all seven. a loop with one more live value stays in memory, and the arm64 jit gives up on a block that needs more temporary registers than it has. raising that, or spilling inside the region instead of abandoning it, matters once the calls and the checks above are cheap enough for register pressure to show. diff --git a/docs/history/36-alloc/notes.md b/docs/history/36-alloc/notes.md new file mode 100644 index 0000000..aff6965 --- /dev/null +++ b/docs/history/36-alloc/notes.md @@ -0,0 +1,47 @@ +# 36 — allocator + +the tree was paying for a quarantine and a sweep on every `new`. `free` left the block intact until `gc` walked the heap. a uniquely owned pointer does not need that. `free(d*x)`, and the free the analyzer emits when an owned local dies, now call `recycle`. children go first, then the block is pushed onto its size-class list. there is no buddy walk and no heap scan. a plain `free`, the one an alias might still see, still quarantines. `tests/quarantine.zc` is that contract, and it still prints `0 1234`. + +the header lost the class word. size is `p[-5]`, and the class sits in the high bits of the flags word, so `p[-4]` through `p[-1]` are still flags, check word, owner and type. a two-word node is still 8 words: five header words plus two payload words is 7, and the smallest block is 8. a three-word node fits in 8 instead of 16. `new(int)` therefore reports a payload of 3 in `memreport`, and `tests/memleak.zc` expects that. the slack past the size `new` was asked for is zeroed, so a later walk does not follow an uninitialized word. the words that were asked for stay uninitialized in `new_raw`. + +`arena_new(n)`, `arena_alloc` and `arena_reset` are a bump allocator over one owned block. the pointers it hands out are interior. resetting the bump reuses them. `tests/arena.zc` and `tests/recycle.zc` cover both. + +`<<` is an enhancement, and `heap_init` runs before the image installs enhancements, so the class is packed with a multiply by `pow2[16]` rather than a shift. + +the stage-0 image is 100933 words and the fixpoint is 105074. the small compiler's stage-0 image is 100932, so its reservation moved from 100352 to 102400. that machine is still 184320 words; the extra reservation came out of the data-stack slack. the run-only image of `tests/bytes.zc` is 15030 words, so that reservation moved from 14336 to 15600. the machine is still 36864 words. + +the suite is 422 passed, 0 failed: interpreter, microcore, soft-float, the small and run-only maps, both virt boards, the arm64 jit and llvm. + +## sieve + +the sieve does not allocate in the loop. it stayed inside the previous spread. milliseconds per count, median of five slopes. the before column is `history/35-bench`. + +| path | before | after | +| --- | ---: | ---: | +| gcc -O3 -march=native, word flags | 0.149 | 0.149 | +| gcc -O3 -march=native -flto, byte flags | 0.081 | 0.081 | +| zc interpreter, plain, bounds-checked | 214.3 | 214.2 | +| zc interpreter, o3, bounds-checked | 22.92 | 22.89 | +| zc interpreter, o3, raw pointer | 14.36 | 14.63 | +| zc llvm, ZYHOT=1, o3 raw pointer | 0.219 | 0.233 | +| zc llvm, ZYHOT=1, o3 bounds-checked | 0.640 | 0.626 | +| zc arm64 jit, qemu-user, o3 raw pointer | 2.094 | 2.066 | + +## trees + +milliseconds per tree of depth 12. same kernels as stage 35. gcc on this run was 0.091 for malloc and 0.0143 for the arena. + +| path | before | after | +| --- | ---: | ---: | +| gcc -O3 -march=native, malloc | 0.092 | 0.091 | +| gcc -O3 -march=native -flto, arena | 0.0144 | 0.0143 | +| zc interpreter, plain new/free | 178.1 | 76.37 | +| zc interpreter, o3 new/free | 269.4 | 175.3 | +| zc interpreter, o3 arena | 2.108 | 2.111 | +| zc llvm, ZYHOT=1, plain new/free | 13.69 | 6.91 | +| zc llvm, ZYHOT=1, o3 new/free | 23.70 | 15.73 | +| zc llvm, ZYHOT=1, o3 arena | 0.282 | 0.270 | +| zc arm64 jit, qemu-user, plain new/free | 80.83 | 44.85 | +| zc arm64 jit, qemu-user, o3 arena | 1.667 | 1.554 | + +plain `new` / `free` is about 2.3x faster on the interpreter (76 ms against 178) and about 2.0x on llvm (6.9 ms against 13.7). the arm64 jit under qemu dropped from 81 ms to 45 ms. llvm against gcc `malloc` is about 76x, down from about 150x. the arena rows did not move: that kernel never calls the allocator. the o3 tree is still slower than the plain one (175 ms against 76, and 15.7 ms against 6.9), because `fatten` and `own` are still calls on every child. diff --git a/docs/history/37-call/notes.md b/docs/history/37-call/notes.md new file mode 100644 index 0000000..e135411 --- /dev/null +++ b/docs/history/37-call/notes.md @@ -0,0 +1,43 @@ +# 37 — calls + +an o3 function calling another o3 function was leaving the region, running the prologue, and coming back through the epilogue. the return also called `fatten` whenever the type had a pointer. both are boundary work. a call that stays inside o3 is not a boundary. + +the region descriptor now carries the frame size, the arity, and the prologue address. a bare o3 block stores -1 for that address, so a real function whose prologue is at word 0 still gets registered. the caller builds the frame, copies the arguments, and enters the callee's region at its first block. the callee's prologue and epilogue do not run. a return to the caller puts the frame pointer back. `fatten` runs only when the value actually crosses into code that is not o3: the call site in ordinary code, an argument passed to an ordinary function (`free` and `recycle` still excepted), a store out of an o3 variable, and the end of an o3 block. `tests/o3call.zc` checks the split. `inner` returns a raw header (`p[-1] < 0`), and the same function called from `main` comes back fat. + +on the interpreter and on llvm, that entry is a direct call into the region. a callee that is ordinary bytecode is pushed on the vm return stack and run by the same driver that runs everything else, so a hot `new` stays compiled. on the arm64 jit the o3 callee is a `blr` to its entry stub. the return address lives in a small link stack, not in a trip out to c and back through `x_enter`. + +the fixpoint is 105293 words. the stage-0 image is 101152, still inside the 102400-word small reservation. the run-only image did not change. the suite is 426 passed, 0 failed: interpreter, microcore, soft-float, the small and run-only maps, both virt boards, the arm64 jit and llvm. + +## sieve + +the inner loops do not call. milliseconds per count, median of five slopes. the before column is `history/36-alloc`. + +| path | before | after | +| --- | ---: | ---: | +| gcc -O3 -march=native, word flags | 0.149 | 0.148 | +| gcc -O3 -march=native -flto, byte flags | 0.081 | 0.081 | +| zc interpreter, plain, bounds-checked | 214.2 | 202.3 | +| zc interpreter, o3, bounds-checked | 22.89 | 21.66 | +| zc interpreter, o3, raw pointer | 14.63 | 14.35 | +| zc llvm, ZYHOT=1, o3 raw pointer | 0.233 | 0.249 | +| zc llvm, ZYHOT=1, o3 bounds-checked | 0.626 | 0.745 | +| zc arm64 jit, qemu-user, o3 raw pointer | 2.066 | 2.103 | + +## trees + +milliseconds per tree of depth 12. same kernels as stage 36. gcc on this run was 0.091 for malloc and 0.0143 for the arena. + +| path | before | after | +| --- | ---: | ---: | +| gcc -O3 -march=native, malloc | 0.091 | 0.091 | +| gcc -O3 -march=native -flto, arena | 0.0143 | 0.0143 | +| zc interpreter, plain new/free | 76.37 | 72.59 | +| zc interpreter, o3 new/free | 175.3 | 162.1 | +| zc interpreter, o3 arena | 2.111 | 1.426 | +| zc llvm, ZYHOT=1, plain new/free | 6.91 | 7.78 | +| zc llvm, ZYHOT=1, o3 new/free | 15.73 | 16.43 | +| zc llvm, ZYHOT=1, o3 arena | 0.270 | 0.195 | +| zc arm64 jit, qemu-user, plain new/free | 44.85 | 43.58 | +| zc arm64 jit, qemu-user, o3 arena | 1.554 | 0.624 | + +the arena kernel is the one this change is aimed at: recursive o3 calls and no allocator. it dropped from 2.111 ms to 1.426 on the interpreter, from 0.270 ms to 0.195 on llvm, and from 1.554 ms to 0.624 on the arm64 jit under qemu. the o3 `new` / `free` tree is still slower than the plain one (162 ms against 73, and 16.4 ms against 7.8). `fatten` and `own` are still calls on every child store. diff --git a/docs/history/38-bounds/notes.md b/docs/history/38-bounds/notes.md new file mode 100644 index 0000000..698d15b --- /dev/null +++ b/docs/history/38-bounds/notes.md @@ -0,0 +1,43 @@ +# 38 — bounds + +the o3 index of a `fixed []T` compared the index with `outside` and left that index on the data stack across the branch. the lifter reloaded it from a stack slot on the way back, on every in-bounds store. when the index is a local, both sides of the branch now load it from the variable. the failure path is the only spill, and it only exists to hand `oob` its two arguments. + +a `for` whose test is `i < b`, whose step is `i++` or `i += 1`, and whose `b` holds `len` of that fixed array, drops the check. `i` has to be known non-negative, and the body must not have stored `i`, `b`, or the array yet. `i * i < n` is not that test, and `j += i` is not a step of one: a product can wrap negative, so the inner sieve loop keeps the check. `i = -1` keeps it. `f[i + 1]` keeps it, because that index is not the induction variable. the same proof applies to plain bytecode, which then computes the address without calling `dat`. `tests/bounds.zc` is the sum and a small sieve. `tests/bounds_lo.zc` and `tests/bounds_hi.zc` are the two checks that have to stay. + +the fixpoint is 109545 words. the stage-0 image is 105232, and the small machine's copy is 105231, so that reservation moved from 102400 to 110592. the small machine is now 192512 words. the run-only image did not change. the suite is 438 passed, 0 failed: interpreter, microcore, soft-float, the small and run-only maps, both virt boards, the arm64 jit and llvm. + +## sieve + +milliseconds per count, median of five slopes. the before column is `history/37-call`. the raw pointer does not check, so it is the control. + +| path | before | after | +| --- | ---: | ---: | +| gcc -O3 -march=native, word flags | 0.148 | 0.148 | +| gcc -O3 -march=native -flto, byte flags | 0.081 | 0.080 | +| zc interpreter, plain, bounds-checked | 202.3 | 144.2 | +| zc interpreter, o3, bounds-checked | 21.66 | 18.42 | +| zc interpreter, o3, raw pointer | 14.35 | 14.22 | +| zc llvm, ZYHOT=1, o3 raw pointer | 0.249 | 0.254 | +| zc llvm, ZYHOT=1, o3 bounds-checked | 0.745 | 0.430 | +| zc arm64 jit, qemu-user, o3 raw pointer | 2.103 | 2.080 | + +the fill and the count are `i = 2; i < n; i++` with `n = len(f)`, so those checks are gone. the inner `j = i * i; j < n; j += i` still checks, and the index stays in its variable across that check. on llvm the checked sieve went from 0.745 ms to 0.430 ms. the raw pointer is 0.254 ms. the gap that is left is the check itself, and the empty blocks the loop still splits into. + +## trees + +milliseconds per tree of depth 12. gcc on this run was 0.091 for malloc and 0.0143 for the arena. these kernels do not index a `fixed []T` in a proved loop. a dump of their four o3 regions is the same blocks, at the same addresses, as the previous compiler. + +| path | before | after | +| --- | ---: | ---: | +| gcc -O3 -march=native, malloc | 0.091 | 0.091 | +| gcc -O3 -march=native -flto, arena | 0.0143 | 0.0143 | +| zc interpreter, plain new/free | 72.59 | 72.73 | +| zc interpreter, o3 new/free | 162.1 | 162.0 | +| zc interpreter, o3 arena | 1.426 | 1.429 | +| zc llvm, ZYHOT=1, plain new/free | 7.78 | 7.32 | +| zc llvm, ZYHOT=1, o3 new/free | 16.43 | 18.31 | +| zc llvm, ZYHOT=1, o3 arena | 0.195 | 0.184 | +| zc arm64 jit, qemu-user, plain new/free | 43.58 | 44.21 | +| zc arm64 jit, qemu-user, o3 arena | 0.624 | 0.838 | + +the interpreter arena stayed at 1.43 ms. the arm64 arena moved under qemu, from 0.624 ms to 0.838 ms, on code this pass did not change. diff --git a/docs/history/39-blocks/notes.md b/docs/history/39-blocks/notes.md new file mode 100644 index 0000000..5133381 --- /dev/null +++ b/docs/history/39-blocks/notes.md @@ -0,0 +1,43 @@ +# 39 — blocks + +a `for` left the lifter as a test, an empty jump, a body and a step. the raw sieve's inner loop was four dispatches per flag. after the blocks exist and before any backend runs, three cleanups share one cfg. + +an empty jump (no ops, no stack, no variable write) is retargeted at its successor, including an entry that landed on it. a jump to a block that has no other predecessor, and that is not itself an entry, is pasted onto that predecessor: the two stacks are empty, and a read of a variable the first half already wrote uses that value. a header whose test is a branch, and whose one edge is a straight run of those jumps back to the header, is rotated. the run plus a copy of the test becomes one block. the zero edge of that test still leaves, and the other edge branches to the block itself. the header's first test stays in front of it, so a loop that runs zero times never enters. a body that contains another branch is left alone, which is the inner sieve's bounds check and the `if (f[i])` around it. a block nothing can reach becomes a jump to itself, so it is not a second predecessor of the live loop. + +the raw sieve's fill, its inner `j += i` store, and its count are each one block per iteration. the checked sieve's fill and count are too. its inner loop is the test, the bounds branch, and one block that stores and steps. the same blocks are what the interpreter, the arm64 jit and llvm compile. the fixpoint is still 109545 words. this pass is in the core, and the genome did not change. the suite is 438 passed, 0 failed: interpreter, microcore, soft-float, the small and run-only maps, both virt boards, the arm64 jit and llvm. + +## sieve + +milliseconds per count, median of five slopes. the before column is `history/38-bounds`. plain bytecode is the control: this pass only reshapes o3 regions. + +| path | before | after | +| --- | ---: | ---: | +| gcc -O3 -march=native, word flags | 0.148 | 0.167 | +| gcc -O3 -march=native -flto, byte flags | 0.080 | 0.080 | +| zc interpreter, plain, bounds-checked | 144.2 | 144.1 | +| zc interpreter, o3, bounds-checked | 18.42 | 12.46 | +| zc interpreter, o3, raw pointer | 14.22 | 7.97 | +| zc llvm, ZYHOT=1, o3 raw pointer | 0.254 | 0.227 | +| zc llvm, ZYHOT=1, o3 bounds-checked | 0.430 | 0.414 | +| zc arm64 jit, qemu-user, o3 raw pointer | 2.080 | 1.931 | + +the interpreter's raw pointer went from 14.22 ms to 7.97 ms, and the checked o3 sieve from 18.42 ms to 12.46 ms. the arm64 jit went from 2.080 ms to 1.931 ms. gcc's word flags came out at 0.167 ms on this run, against 0.148 ms last time, and the byte flags stayed at 0.080 ms. the plain zc sieve, which is still bytecode, stayed at 144 ms. + +`bench/run.sh` timed the llvm raw pointer at 315 repetitions. one of the five trials came out negative, and the median of that set is 0.399 ms (spread -0.72 to 0.88). a slope from 4000 repetitions to 16000 on the same binary is 0.227 ms (0.224 to 0.238), and that is the after column. the checked sieve's long slope is 0.439 ms (0.430 to 0.483), next to the run's 0.414 ms. the inner checked loop still branches on the bounds test, so llvm did not get a single block there, and the raw pointer stayed near a quarter of a millisecond rather than the byte-flag 0.080 ms. + +## trees + +milliseconds per tree of depth 12. gcc on this run was 0.091 for malloc and 0.0144 for the arena. these kernels are calls and recursion. a straight counted loop is not where they spend the time. + +| path | before | after | +| --- | ---: | ---: | +| gcc -O3 -march=native, malloc | 0.091 | 0.091 | +| gcc -O3 -march=native -flto, arena | 0.0143 | 0.0144 | +| zc interpreter, plain new/free | 72.73 | 75.69 | +| zc interpreter, o3 new/free | 162.0 | 158.6 | +| zc interpreter, o3 arena | 1.429 | 1.413 | +| zc llvm, ZYHOT=1, plain new/free | 7.32 | 7.58 | +| zc llvm, ZYHOT=1, o3 new/free | 18.31 | 17.68 | +| zc llvm, ZYHOT=1, o3 arena | 0.184 | 0.159 | +| zc arm64 jit, qemu-user, plain new/free | 44.21 | 44.87 | +| zc arm64 jit, qemu-user, o3 arena | 0.838 | 0.836 | diff --git a/docs/history/40-fatten/notes.md b/docs/history/40-fatten/notes.md new file mode 100644 index 0000000..c7ff0fd --- /dev/null +++ b/docs/history/40-fatten/notes.md @@ -0,0 +1,41 @@ +# 40 — fatten + +typed `new(T)` now stamps the header and zeroes the payload inside an o3 region, the same path `new(T)` takes outside one. a child store writes `p[-2] = owner` in the region when memdebug is off. a shared child keeps the owner it already has, and a store through null does not land. the store skips `fatten` when that value was just allocated, or came back from an o3 function whose type has a pointer. `fatten` still runs where o3 data reaches code that is not o3: a store into a normal local or a global, an argument to a function that is not o3, the call site where ordinary code calls an o3 function, `push` into a growable array, and the end of an o3 block. a call or a return from o3 to o3 does none of that. an untyped `new(n)` in o3 is still `new_raw`, with `-(n + 1)` in the type word and the requested words left uninitialized. + +`tests/o3call.zc` now prints `0 0 4 23 55 node`: both the o3 caller and the ordinary caller see a stamped header. the fixpoint is 109799 words. the suite is 438 passed, 0 failed: interpreter, microcore, soft-float, the small and run-only maps, both virt boards, the arm64 jit and llvm. + +## sieve + +milliseconds per count, median of five slopes. the before column is `history/39-blocks`. this change does not touch the sieve's loops. + +| path | before | after | +| --- | ---: | ---: | +| gcc -O3 -march=native, word flags | 0.167 | 0.151 | +| gcc -O3 -march=native -flto, byte flags | 0.080 | 0.084 | +| zc interpreter, plain, bounds-checked | 144.1 | 146.8 | +| zc interpreter, o3, bounds-checked | 12.46 | 12.75 | +| zc interpreter, o3, raw pointer | 7.97 | 8.09 | +| zc llvm, ZYHOT=1, o3 raw pointer | 0.227 | 0.233 | +| zc llvm, ZYHOT=1, o3 bounds-checked | 0.414 | 0.444 | +| zc arm64 jit, qemu-user, o3 raw pointer | 1.931 | 1.936 | + +`bench/run.sh` timed the llvm raw pointer at 216 repetitions. one trial came out negative, and the median of that set is 0.172 ms (spread -0.12 to 1.14). the checked sieve's run was 45 repetitions and the median was -1.23 ms (spread -5.09 to 7.41). a slope from 4000 repetitions to 16000 on the same binary is 0.233 ms for the raw pointer (0.230 to 0.241) and 0.444 ms for the checked sieve (0.434 to 0.462). those two are the after column. they sit next to the previous long slopes, 0.227 ms and 0.439 ms. + +## trees + +milliseconds per tree of depth 12. the o3 `new` / `free` tree is the one this change is for. gcc on this run was 0.091 for malloc and 0.0144 for the arena. + +| path | before | after | +| --- | ---: | ---: | +| gcc -O3 -march=native, malloc | 0.091 | 0.091 | +| gcc -O3 -march=native -flto, arena | 0.0144 | 0.0144 | +| zc interpreter, plain new/free | 75.69 | 72.97 | +| zc interpreter, o3 new/free | 158.6 | 64.89 | +| zc interpreter, o3 arena | 1.413 | 1.411 | +| zc llvm, ZYHOT=1, plain new/free | 7.58 | 7.42 | +| zc llvm, ZYHOT=1, o3 new/free | 17.68 | 6.49 | +| zc llvm, ZYHOT=1, o3 arena | 0.159 | 0.175 | +| zc arm64 jit, qemu-user, plain new/free | 44.87 | 44.12 | +| zc arm64 jit, qemu-user, o3 arena | 0.836 | 0.780 | + +on the interpreter the o3 tree went from 158.6 ms to 64.89 ms, and the plain tree on the same run was 72.97 ms. on llvm the o3 tree went from 17.68 ms to 6.49 ms, and the plain tree was 7.42 ms. each node is one call to `new_t`, an owner store in the region, and the two recursive calls. the arena rows are the same shape as last time. diff --git a/docs/history/41-obytes/notes.md b/docs/history/41-obytes/notes.md new file mode 100644 index 0000000..cf3280d --- /dev/null +++ b/docs/history/41-obytes/notes.md @@ -0,0 +1,39 @@ +# 41 — byte ops in o3 + +opcodes 612..617 (`ld8`, `st8`, and the 16- and 32-bit pair) lift. a call to one of those six from o3 code emits the opcode instead of leaving the region. the interpreter, the arm64 jit and llvm each run it: an out-of-range load is 0, an out-of-range store does nothing, and a store into a word a translation covers goes through the same hook as a word store, so a patch of the program's own code still drops that translation. the byte cases sit on the default path of the region interpreter, so the switch that runs the word sieve stays the size it was. + +`tests/o3bytes.zc` writes twenty bytes, reads them back at 8, 16 and 32 bits, rejects an address off either end of memory, and patches a `putc` literal from inside a region. it prints `190 4660 7 2055 16909060 0 0` and then `ABC`. the fixpoint is 110426 words. the suite is 442 passed, 0 failed: interpreter, microcore, soft-float, the small and run-only maps, both virt boards, the arm64 jit and llvm. + +## sieve + +milliseconds per count, median of five slopes. the before column is `history/40-fatten`. the kernels are still one word per flag. this pass does not change them. + +| path | before | after | +| --- | ---: | ---: | +| gcc -O3 -march=native, word flags | 0.151 | 0.150 | +| gcc -O3 -march=native -flto, byte flags | 0.084 | 0.081 | +| zc interpreter, plain, bounds-checked | 146.8 | 145.6 | +| zc interpreter, o3, bounds-checked | 12.75 | 13.22 | +| zc interpreter, o3, raw pointer | 8.09 | 7.87 | +| zc llvm, ZYHOT=1, o3 raw pointer | 0.233 | 0.233 | +| zc llvm, ZYHOT=1, o3 bounds-checked | 0.444 | 0.438 | +| zc arm64 jit, qemu-user, o3 raw pointer | 1.936 | 1.963 | + +`bench/run.sh` timed the llvm raw pointer at 3684 repetitions, median 0.250 ms (0.176 to 0.286), and the checked sieve at 1460 repetitions, median 0.423 ms (0.230 to 0.558). a slope from 4000 repetitions to 16000 on the same binary is 0.233 ms for the raw pointer (0.208 to 0.237) and 0.438 ms for the checked sieve (0.432 to 0.477). those two are the after column. the interpreter's raw pointer went from 8.09 ms to 7.87 ms. the checked o3 sieve went from 12.75 ms to 13.22 ms. gcc's byte flags were 0.081 ms. + +## trees + +milliseconds per tree of depth 12. these kernels do not touch a byte. gcc on this run was 0.094 for malloc and 0.0144 for the arena. the o3 tree stays ahead of the plain one. + +| path | before | after | +| --- | ---: | ---: | +| gcc -O3 -march=native, malloc | 0.091 | 0.094 | +| gcc -O3 -march=native -flto, arena | 0.0144 | 0.0144 | +| zc interpreter, plain new/free | 72.97 | 72.86 | +| zc interpreter, o3 new/free | 64.89 | 65.55 | +| zc interpreter, o3 arena | 1.411 | 1.378 | +| zc llvm, ZYHOT=1, plain new/free | 7.42 | 8.08 | +| zc llvm, ZYHOT=1, o3 new/free | 6.49 | 6.95 | +| zc llvm, ZYHOT=1, o3 arena | 0.175 | 0.163 | +| zc arm64 jit, qemu-user, plain new/free | 44.12 | 44.15 | +| zc arm64 jit, qemu-user, o3 arena | 0.780 | 0.704 | diff --git a/docs/history/42-roots/notes.md b/docs/history/42-roots/notes.md new file mode 100644 index 0000000..628b3db --- /dev/null +++ b/docs/history/42-roots/notes.md @@ -0,0 +1,41 @@ +# 42 — roots + +opcode 619 pushes the data-stack index, the first live word. `gc_roots` marks from that index to the top, in the compiler and in the run-only image. a pointer sitting in a word the program has already dropped is not a root, so the next collection reclaims the block. `scrub` zeroes the unused region from `DSLO` up to the live top. the last 16 words are the ones the loop itself is using, and those are pushed as zeros. `free` still quarantines. a block something live still points at stays readable. + +`tests/roots.zc` plants a pointer at the bottom of the data stack, drops the local, collects, and prints `1 0`: the object is gone, and scrub cleared the planted word. the fixpoint is 110446 words. the suite is 448 passed, 0 failed: interpreter, microcore, soft-float, the small and run-only maps, both virt boards, the arm64 jit and llvm. + +## sieve + +milliseconds per count, median of five slopes. the before column is `history/41-obytes`. the sieve does not collect. + +| path | before | after | +| --- | ---: | ---: | +| gcc -O3 -march=native, word flags | 0.150 | 0.149 | +| gcc -O3 -march=native -flto, byte flags | 0.081 | 0.080 | +| zc interpreter, plain, bounds-checked | 145.6 | 144.2 | +| zc interpreter, o3, bounds-checked | 13.22 | 11.11 | +| zc interpreter, o3, raw pointer | 7.87 | 6.87 | +| zc llvm, ZYHOT=1, o3 raw pointer | 0.233 | 0.252 | +| zc llvm, ZYHOT=1, o3 bounds-checked | 0.438 | 0.439 | +| zc arm64 jit, qemu-user, o3 raw pointer | 1.963 | 1.920 | + +the interpreter's raw pointer went from 7.87 ms to 6.87 ms, and the checked o3 sieve from 13.22 ms to 11.11 ms. `bench/run.sh` timed the llvm raw pointer at 0.225 ms (0.177 to 0.244, 3517 repetitions) and the checked sieve at 0.396 ms (0.364 to 0.634, 1905 repetitions). a slope from 4000 repetitions to 16000 on the same binary is 0.252 ms for the raw pointer (0.230 to 0.287) and 0.439 ms for the checked sieve (0.427 to 0.441). those two are the after column. gcc's byte flags were 0.080 ms. + +## trees + +milliseconds per tree of depth 12. gcc on this run was 0.091 for malloc and 0.0143 for the arena. the o3 tree stays ahead of the plain one. + +| path | before | after | +| --- | ---: | ---: | +| gcc -O3 -march=native, malloc | 0.094 | 0.091 | +| gcc -O3 -march=native -flto, arena | 0.0144 | 0.0143 | +| zc interpreter, plain new/free | 72.86 | 72.52 | +| zc interpreter, o3 new/free | 65.55 | 64.46 | +| zc interpreter, o3 arena | 1.378 | 1.353 | +| zc llvm, ZYHOT=1, plain new/free | 8.08 | 7.43 | +| zc llvm, ZYHOT=1, o3 new/free | 6.95 | 6.83 | +| zc llvm, ZYHOT=1, o3 arena | 0.163 | 0.183 | +| zc arm64 jit, qemu-user, plain new/free | 44.15 | 43.82 | +| zc arm64 jit, qemu-user, o3 arena | 0.704 | 0.702 | + +`bench/run.sh` timed the llvm plain tree at 211 repetitions, median 7.21 ms (4.32 to 7.62), and the o3 tree at 111 repetitions, median 8.30 ms (5.74 to 11.94). a slope from 400 repetitions to 1600 on the same binary is 7.43 ms for the plain tree (7.20 to 7.60) and 6.83 ms for the o3 tree (6.75 to 7.13). those two are the after column. on the interpreter the o3 tree is 64.46 ms and the plain tree is 72.52 ms. diff --git a/docs/history/43-stdlib/notes.md b/docs/history/43-stdlib/notes.md new file mode 100644 index 0000000..65fbebc --- /dev/null +++ b/docs/history/43-stdlib/notes.md @@ -0,0 +1,47 @@ +# 43 — names, an arena, a byte buffer + +`u`, `m` and `d` are ordinary names in the resident compiler. a tag is still `.u`, `.m` or `.d`. `u*x` with no space before the star is still a recast; a space is multiplication. `zc0.c` keeps the old rule, so the runtime and the compiler still do not use those letters as names. `tests/names.zc` prints `12 1 8`. `tests/umd.zc` is unchanged. + +untyped `new(n)` inside o3 calls `new_oz`. that zeroes the words that were asked for and leaves the type word at `-(n + 1)`, so a later `fatten` can still stamp it. `new_raw` itself does not zero those words, so typed `new` does not zero them twice. `tests/o3zero.zc` reads the header inside the region and prints `1`. + +`lib/std.zc` adds `arena`, `aalloc` and `areset` over the bump allocator, and `bnew`, `blen`, `bcap`, `bget` and `bset` for a packed byte buffer. `tests/stdbuf.zc` prints `4 1 266 8`. + +the fixpoint is 110623 words. the stage-0 image is 106266, still inside the 110592-word small reservation. the suite is 460 passed, 0 failed: interpreter, microcore, soft-float, the small and run-only maps, both virt boards, the arm64 jit and llvm. + +the kernels below are the same programs as `history/42-roots`. zeroing an untyped `new` does not sit on the typed-new tree, and the sieve does not allocate. + +## sieve + +milliseconds per count, median of five slopes. the before column is `history/42-roots`. + +| path | before | after | +| --- | ---: | ---: | +| gcc -O3 -march=native, word flags | 0.149 | 0.148 | +| gcc -O3 -march=native -flto, byte flags | 0.080 | 0.080 | +| zc interpreter, plain, bounds-checked | 144.2 | 144.9 | +| zc interpreter, o3, bounds-checked | 11.11 | 11.08 | +| zc interpreter, o3, raw pointer | 6.87 | 6.86 | +| zc llvm, ZYHOT=1, o3 raw pointer | 0.252 | 0.235 | +| zc llvm, ZYHOT=1, o3 bounds-checked | 0.439 | 0.435 | +| zc arm64 jit, qemu-user, o3 raw pointer | 1.920 | 1.936 | + +`bench/run.sh` timed the llvm raw pointer at 0.219 ms (0.083 to 0.286, 3135 repetitions) and the checked sieve at 0.400 ms (0.168 to 0.480, 1923 repetitions). a slope from 4000 repetitions to 16000 on the same binary is 0.235 ms for the raw pointer (0.227 to 0.246) and 0.435 ms for the checked sieve (0.429 to 0.440). those two are the after column. gcc's byte flags were 0.080 ms. + +## trees + +milliseconds per tree of depth 12. gcc on this run was 0.091 for malloc and 0.0144 for the arena. the o3 tree stays ahead of the plain one. + +| path | before | after | +| --- | ---: | ---: | +| gcc -O3 -march=native, malloc | 0.091 | 0.091 | +| gcc -O3 -march=native -flto, arena | 0.0143 | 0.0144 | +| zc interpreter, plain new/free | 72.52 | 73.02 | +| zc interpreter, o3 new/free | 64.46 | 64.62 | +| zc interpreter, o3 arena | 1.353 | 1.305 | +| zc llvm, ZYHOT=1, plain new/free | 7.43 | 7.50 | +| zc llvm, ZYHOT=1, o3 new/free | 6.83 | 6.99 | +| zc llvm, ZYHOT=1, o3 arena | 0.183 | 0.172 | +| zc arm64 jit, qemu-user, plain new/free | 43.82 | 43.88 | +| zc arm64 jit, qemu-user, o3 arena | 0.702 | 0.724 | + +`bench/run.sh` timed the llvm plain tree at 47 repetitions, median 6.89 ms (1.66 to 7.67), and the o3 tree at 115 repetitions, median 7.14 ms (6.82 to 7.61). a slope from 400 repetitions to 1600 on the same binary is 7.50 ms for the plain tree (7.46 to 7.63) and 6.99 ms for the o3 tree (6.94 to 7.06). those two are the after column. on the interpreter the o3 tree is 64.62 ms and the plain tree is 73.02 ms. diff --git a/docs/history/44-check-mode/notes.md b/docs/history/44-check-mode/notes.md new file mode 100644 index 0000000..2cb0493 --- /dev/null +++ b/docs/history/44-check-mode/notes.md @@ -0,0 +1,33 @@ +# 44 — check mode, methods, closures, postfix pointers, wasm + +the language server stopped being its own program. `src/compiler.zc` grew a check mode, `lsp/server.zc` wraps it, and everything the editor shows comes from the compiler's own run over the document. + +## check mode + +`cmode` turns errors into records (`[severity, line, offset, length, text]`) instead of printing, and the compiler keeps going. `chkabort` makes `next` return end of file so the loops unwind after a fatal error, and a fuel counter halts a run that never unwinds. `expect` puts "expected x" at the end of the previous token. tokens carry their place (`toff`, `toklen`), packed with the line into one word that the quote lists and the lookahead queue carry along, so a hint can point at a token that was read earlier. check mode re-initialises the heap, symbols and tables for every document, so one check can't leak into the next. + +four sets of names (locals, parameters, type parameters, fields) are recorded as the compiler sees them declared, which is how the highlighter tells a field from a variable without a second parser. + +## hints + +a variable never read, a `let` that could be a `const`, multiplying by a power of two, a number with no name, `new` inside a loop, a small `new` that never leaves its function, an object given away once right after it was made, an owned object that outlives its last use by four lines. they are reported only in check mode and don't change the code that comes out. + +## methods and closures + +`fn name(params) { body }` inside a struct is a function called `Struct.name` with a hidden `Struct* this` first. `p.name(args)` calls it. `fn(a, b) { ... }` in an expression captures the variables it uses by value into a block whose first three words are a trampoline, so the block's address is a callable value. + +## postfix pointers + +`ptype0` reads a type and then any stars after it, so `int*`, `vec*` and `point**` work, and the prefix star still does. `tprint` writes the new form. + +## same-line braces + +every `.zc` under src, lib, lsp, tools, bench, examples and tests (apart from the tests whose expected output names a line) moved to `fn name() {`. the history folders are untouched. + +## web + +`web/build.sh` builds the microcore to webassembly with clang's wasm32 target. `web/zc.js` and `web/index.html` run a program in the browser. the port is one small file, plus a 64 by 64 to 128 bit multiply the core uses. + +## numbers + +the fixpoint is 123810 words (the slot is 131072). the stage-0 image is 119013, so the small map's genome reservation went from 110592 to 126976 words. the suite is 426 passed, 0 failed on this machine: interpreter, microcore, soft-float, small and run-only maps, arm64 jit under qemu, llvm, the javascript vm, webassembly and the language server. the baremetal boards were skipped (no cross compilers here). the windows scripts were edited but not run. diff --git a/docs/history/45-fast-alloc/notes.md b/docs/history/45-fast-alloc/notes.md new file mode 100644 index 0000000..4ef5182 --- /dev/null +++ b/docs/history/45-fast-alloc/notes.md @@ -0,0 +1,13 @@ +# 45 — no prefix pointers, a fast path for constant-size allocation, maps that fit, a makefile + +**prefix pointer types are gone.** `*int` as a type is an error now; `int*` is the only form, and a star in front of an expression is only a dereference. the parser lost its prefix branch in `ptype1`, `tstart`, `caststart`, `new(...)` and the parameter detection, casts learned `(int*)x` and `(node**)x`, and every source (src, lib, lsp, tools, bench, examples, tests) was converted by a script and checked by the suite. a pointer to an array or a function type is spelled through a `type` alias. + +**allocator primitives.** a `new` with a constant size computes its size class at compile time and calls `new_k`. a typed `new` of a pointer-free struct is freed with `recycle_leaf`, which re-checks type and class at run time. the hold test in `new_k` is "does this block overlap the held range" rather than "is there a held range", because the default map always has one (the compiler's type area) and the blanket test sent every call down the slow path. `tests/allocfast.zc` covers zeroing, the owner case and the live count. + +**maps that fit.** `genmap --grow` and `tools/automap.sh` size a map to the program. the run-only images stopped fitting the fixed 36864-word map once the runtime grew; now each is laid out for its own length. `baremetal/run.sh` had stale paths from before the reorganisation and was fixed on the way. + +**makefile, and no binaries in the tree.** `make` builds into `bin/`; `bin/`, `build/` and `web/zc.wasm` are ignored. the task list file was removed once everything on it was done. + +**formatted and commented.** `src/rt.zc`, `src/compiler.zc` and `lsp/server.zc` were rewritten to one layout (one statement per line, a body of more than one simple statement opened out, short single-statement ifs kept on one line), checked by confirming the genome came out byte for byte the same. then the two core files got a pass of explanation: an overview at the top of each saying how the pieces fit, a note before every function that was not self-evident, and the analyzer's model of a local (what each state means) written down once where the locals are declared. the whole pass was comment-only, so the genome is still identical. one thing to know when writing comments in zc: a backtick ends the source even inside a comment, so none can appear in these files. + +fixpoint 125702 words. suite 440 passed, 0 failed here. diff --git a/docs/history/46-c-abi/notes.md b/docs/history/46-c-abi/notes.md new file mode 100644 index 0000000..cc53f28 --- /dev/null +++ b/docs/history/46-c-abi/notes.md @@ -0,0 +1,17 @@ +# 46 — a backtick that means it, native calls, and a bigger default map + +**the backtick.** a backtick ends the source only outside a comment, a string and a character literal. `srcend` keeps a small state (normal, line comment, block comment, string, char) across `nextch`, so a backtick inside a comment no longer cuts the program off. three comments in the compiler that had one were the failure that showed it (the fixpoint broke), and they were reworded anyway. + +**a parse word has to read.** a handler run by `kwcall` that never took a token would be called again on the same token forever. `next` counts tokens, and `kwcall` compares the count before and after: no progress is an error ("a parse word has to read its own name with next()"). a fuzz run found it. `tests/langerrors/parseword_noread.zc` keeps it. + +**lexer.** `isopch` is a 31-bit mask test and `isidc` a range test, `intern` checks the first character before hashing, `readsrc` has a fast path for stdin. the compiler compiles itself about 12% faster. the 64-bit literal that first stood in for the mask lost its top bit (`lit(v)` is `2v+1`), which is why the mask is 31 bits wide. + +**llvm gets the target.** the module carries the host's triple and data layout, functions carry attributes, and element addresses are `inbounds` GEPs. `noalias` is not valid on vm memory because host helpers read and write it. on the bench kernels the code was already near native, so nothing moved. + +**native calls.** `extern double pow(double, double);` defines a zc function that calls the c function. the compiler reads a c prototype (`ckind` maps c spellings to kinds), writes the symbol name, a signature word and room for the arguments into the code stream, compiles a small stub from text (the same way `include` compiles a file) that stores its arguments and does `syscall(14, block)`. the host looks the symbol up with `dlsym` (the math library is opened if the first lookup misses) and calls it through a prototype of six longs and eight doubles ending in `...`, so integer and float registers fill independently and a variadic callee sees its register count. kinds: long, double, byte pointer, int, float, word pointer, unsigned int, short, unsigned short, char, unsigned char, and `str` for a zc byte string. a pointer into vm memory is `(char *)m + byte address`, which works because vm memory is one flat array; anything outside the array passes through, so a `malloc` result is an opaque number. `as` now sets the float flag for a cast to `float`, which the stub's result needs. + +**callbacks.** `cfunc(f)` asks gate op 16 for a native pointer. there are 16 prebuilt c functions per result class (long, double, float), each taking six longs and eight doubles; calling one packs the arguments by the signature, pushes them on the vm data stack under the stacks as they were at the gate store, pushes a sentinel return address and runs `vm_until`, the same nested run an o3 call uses. the signature comes from the function's declared types, so a typed `o3` function is entered at its compiled region. the stack pointers are published by the gate store: a global in the interpreter, a store from the alloca in an llvm block, and in the jit the same two words (`S[1]`, `S[2]`) the exit stub writes. it passes under the interpreter, llvm with every block compiled (`ZYHOT=1`) and the arm64 jit under qemu (a dynamic binary: a static one has no `dlsym`). + +**not done.** a call from o3 code to an extern leaves the region like any call to ordinary code. lowering it to a direct call inside a region, so llvm could see the callee and its argument types, would need a variadic micro-op in the lifter, the optimizer, the rexec loop, the arm64 emitter and the llvm lowering, which today all assume at most two inputs. structs by value, more than 6 integer or 8 float arguments, and a callback that outlives its native call are not supported. + +**the default map is 1081344 words.** the genome (`rt.zc` plus `compiler.zc`) came to 134045 words, past the 131072 that the default map reserved. the default preset of `genmap` now reserves 0x28000 words for it and everything above moves up by 0x8000, with `memmap.h`, the const block in `rt.zc` and the javascript vm's constants following. the small maps are separate presets and did not change. diff --git a/docs/history/47-limits/notes.md b/docs/history/47-limits/notes.md new file mode 100644 index 0000000..6fa29ba --- /dev/null +++ b/docs/history/47-limits/notes.md @@ -0,0 +1,15 @@ +# 47 — the limits come off: a heap that grows, native calls the optimizer sees, sized types, more micro-ops + +**a heap that grows.** the machine now has two sizes. PW is the end of the fixed part (code, compiler tables, genome, stacks) and MW is the host's total memory, 0x8000000 words by default. the heap starts at PW with 0x60000 words and grows at run time: when `new_raw` finds no block big enough it calls `heap_grow`, which appends an aligned piece (a few times the request, at least 0x40000 words, never past MW) through the same free code the buddy merge uses. the host's array is static and untouched until the heap reaches into it, so a small program costs what it did before. `heap_reset` (the compiler cutting the allocator off below its type area) holds growth off for that process, and the language server's fixed map has growth switched off. `tools/genmap` emits PW, CODE_END and HEAP_GROW; a build can choose its total with `-DMW=`. memory facts come from `syscall(9, k)`. + +**host-assisted bulk work.** filling, copying and scanning a block were loops in zc. ops 18 (fill), 19 (copy) and 20 (does the block hold a heap pointer, for `release`) do each in one call on hosts that know them, and answer -1 elsewhere, in which case the zc loop runs. freeing a large block stopped being the slow part of the grow test. + +**native calls.** up to 15 arguments (registers first, then the c stack), opcode 620 lets the lifter see a native call whose callee and signature are constants, so an o3 region keeps the call in the generated code: a call instruction on arm64, a direct call to the symbol's address on llvm, which means llvm can inline across it and turns the common libm functions on doubles into its own intrinsics. the dispatcher round trip is gone for those. + +**sized integers.** `i8 u8 i16 u16 i32 u32`. a scalar is a word that is cut to size at every store; arrays and pointers of them are packed (8, 4 or 2 to a word, little endian) with the byte ops, indexing yields a "packed twin" whose lvalue is a byte address. pointer arithmetic stays in words and `sizeof` counts words. no new vm opcodes: the byte ops from stage 28 already lift in o3, so packed access gets the optimizer and both jits for free. not done: packed struct fields, growable `[]u8`, taking the address of one element. + +**f32.** the same machinery with a float: a variable holds a value rounded to single precision, an array or pointer of f32 is packed two to a word. the rounding is two new micro-ops. + +**micro-ops.** `ctz popcnt bswap rotl rotr` and `f2s s2f` join the set. the interpreter, the microcore and the javascript vm (integers only) evaluate them; arm64 uses rbit+clz, the vector popcount, rev, rorv and fcvt; llvm uses `cttz ctpop bswap fshl fshr` and fptrunc/fpext. `rt.zc` wraps them as enhancements (`ctz(x)`, `popcnt(x)`, ...). a bug worth remembering: the interpreter's enhancement runner treated every op number at or above `U_LD8` as a byte op, so a new op past the end of the list ran as nothing. it is bounded now. + +**what this stage did not do.** the invalidation of translated code is still coarse (a store to a covered word rescans every block and an install flushes all), o3 calls still leave a region through the dispatcher, there is no automatic tiering, and the windows scripts and the bare metal runs were not re-run after the map change. they are listed under "open ends" in the readme. diff --git a/docs/history/48-landing/notes.md b/docs/history/48-landing/notes.md new file mode 100644 index 0000000..f4465e3 --- /dev/null +++ b/docs/history/48-landing/notes.md @@ -0,0 +1,7 @@ +# stage 48: landing the leftovers + +done: packed struct fields, growable arrays of sized integers and f32, f32 in extern and cfunc signatures, page-indexed invalidation with counted coverage, per-slot install invalidation, per-block llvm resource trackers freed when no compiled code is running, a direct-mapped function cache and a short call path for llvm o3 calls (fib bench 0.69 s to 0.53 s), and a faster self-modification path (bench/selfmod.zc about 7x on the jit). + +not done: structs by value in native calls, an inline llvm-to-llvm call, loop-invariant motion and cross-block cse in the core's optimizer, automatic tiering. the address of a packed element is declined on purpose. + +the stale test langerrors/pk_dyn.zc (which expected growable []sized to be rejected) was removed. diff --git a/docs/history/49-generics/notes.md b/docs/history/49-generics/notes.md new file mode 100644 index 0000000..311d82d --- /dev/null +++ b/docs/history/49-generics/notes.md @@ -0,0 +1,5 @@ +# 49 generics + +what landed: generics are monomorphized instead of erased. templates keep their tokens, each set of type arguments gets its own compiled copy named name, with any size of type and any number of parameters (record), plus methods in generic structs. llvm gets an inline call path. the core gets a global constant lattice pass with branch folding. o3 push and pop are inline. the default memory map is bigger (pw 0x1c5000, heap up to 0x225000) and the small preset was resized (genome 163840, ns 1280). + +what did not: register-passing direct llvm calls, loop-invariant motion and cross-block cse in the core, automatic tiering, structs by value in native calls, and a table that grows at run time (addresses are literals in the genome, so each size is its own image). a template must be written before the first call with type arguments. the js vm and windows scripts were not touched beyond the map constants. diff --git a/docs/history/50-adt/notes.md b/docs/history/50-adt/notes.md new file mode 100644 index 0000000..a9e04da --- /dev/null +++ b/docs/history/50-adt/notes.md @@ -0,0 +1,9 @@ +# 50 adt + +what was asked: tagged enums and pattern matching (task 34). the four design questions were answered: payload syntax `circle { int r; }`, match form `case circle(r):`, a missing member in a switch is a warning, and the tag is the word at offset 0. see design/adt.md. + +what landed: an enum can give its members fields. the enum becomes one struct: a tag word, then every member's fields, each named `member_field`, so every value is as big as the largest member. `shape.circle(3)` is a generated static constructor that returns an owned `shape*`. a switch over a `shape*` matches with `case circle(r):`, binds the fields in that arm only, and reads them through the same field access as any struct. a switch that leaves out a member, with no `default`, prints a warning on the line of the switch. the coverage check keeps a bit set of the tags a switch covers. two enums may use the same member name, since each keeps its own fields. + +checks: tests/tagenum.zc, tests/tagenum_warn.zc, tests/tagenum_two.zc (including a case inside an o3 loop), and three langerrors (explicit values on a member, a wrong number of bindings, a wrong number of constructor arguments). the fast suite passed 126 of 126, the lsp protocol checks 41 of 41 and the wasm checks 10 of 10. the enum output matched on the interpreter, the llvm backend and the arm64 jit under qemu. + +what did not: members with explicit values are rejected, not supported. a member with fields can't be matched without its binding list (the arm must bind every field). the constructor's owned result can't be passed straight to a borrowing parameter (the existing rule for `new`), so it has to go through a name first. the warning only fires with no `default`. the llvm and jit runs were done by hand, not from the test script. diff --git a/docs/history/51-llvm-inline-stdlib/notes.md b/docs/history/51-llvm-inline-stdlib/notes.md new file mode 100644 index 0000000..c061f7b --- /dev/null +++ b/docs/history/51-llvm-inline-stdlib/notes.md @@ -0,0 +1,19 @@ +# 51 llvm inline, stdlib, host syscalls + +what was asked: finish the open tasks in any order (task 19 llvm inlining of o3 calls, task 38 stdlib expansion with a host syscall notation), then fuzz every part of the toolchain. + +what landed: +- llvm inlining of o3-to-o3 calls (task 19). a callee is inlined into the caller's native code behind a check that its region still holds, so a self-modifying callee falls back to the native entry. checked against the interpreter on every example and test with ZYHOT=1 (117 files, 0 diffs). a call-heavy helper benchmark took 2.46 s with ZYNOINLINE=1 and 2.29 s with inlining (about 7 percent). +- stdlib (task 38): Text gains len, eq, copy, find, lower, upper and trim; a new Num group gains abs, min, max, clamp, gcd, pow, isqrt and sign. tests/stdmore.zc. +- host syscall notation (task 38): SYS_RAW (op 40) runs any linux syscall on the host. the block holds the number and six arguments, and the result is the kernel's or -errno. Host.raw(nr, a, b, c, d, e, f) in lib/std.zc builds the block. it is host-only and bypasses the vm's checks, so it is a deliberate escape hatch. tests/rawsys.zc, and tests/rawpid.zc on x86-64 only (syscall numbers differ on arm64). +- the full make test passed: 571 passed, 0 failed. + +also landed: cross-block copy propagation in the global pass (task 20). a copy fact (C_COPY) says a variable holds what another holds at block entry, a read of the copy becomes a read of the source, and a block that writes the source drops the fact. tests/o3copy.zc; the full suite and an llvm ZYHOT=1 comparison over 120 files agree. + +automatic tiering was dropped by decision (tasks 25 and 35). it needed a calling-convention change for unannotated functions; design/tiering.md has the analysis. task 17 is closed with the perf work that landed. + +fuzzing (scratchpad campaign, every target): +- sanitizer builds rebuilt from the current tree (zc0, zy, zym, zyl, lsp, mkgenome, genmap). +- found: under ASan only, an unbounded o3 recursion grows host memory until the OOM killer stops it. the plain, -O0, -O1, -O2 and UBSan builds stop with "frame stack overflow". the ASan build's o3 path does not hit the frame check. the reproducer is `o3 int f(int n) { return f(n + 1) + 1; }`. +- found: a zy vs zym difference on an unchecked program that exhausts the heap. the microcore has a fixed map and reports out of heap memory where the hosted heap grows. the diff harness now skips runs that hit a resource limit. +- the harness now has an RSS watchdog (2.5 GB) so a runaway child is killed instead of taking the host down. diff --git a/docs/history/52-closures/notes.md b/docs/history/52-closures/notes.md new file mode 100644 index 0000000..335923e --- /dev/null +++ b/docs/history/52-closures/notes.md @@ -0,0 +1,14 @@ +# 52 closures + +closures compiled and built their value, but calling one halted the program silently with exit 0. + +cause: the vm only runs code inside the code area (the interpreter loop runs while `pc < PW`). a closure's body was copied into a heap block, so a call into it left the loop without an error. + +fix (`src/compiler.zc`): +- the closure block is a heap value whose word 0 is the body's entry. captured variables start at word 3, and the env the body receives is block + 3, so the body reads capture `i` at env + `i` as before. +- a call through a value compares the callee with `HEAP_LO`. below it, the call goes straight to the function as before. at or above it, the call pushes block + 3 as the env, loads the entry from word 0, and calls that. both paths meet at the same result handling. +- a capture of a variable from further out is chained through each closure in between (`clocap_at`). `clo_via` records the slot in the enclosing closure's environment that the new closure copies from. + +checks: `tests/closure.zc` (interpreter, in `tests/test.sh`), `examples/learn/25-closures.zc` (the guide), and the full suite (604 checks, 0 failed), with the fixpoint at 230551 words. + +what closures still don't do: captures are by value, a closure can't be handed to a c function as a callback, and a closure passed directly to a parameter that isn't `.d` is rejected by the analyzer as an owned object, the same as any new object. diff --git a/docs/history/53-typed-source/notes.md b/docs/history/53-typed-source/notes.md new file mode 100644 index 0000000..1e5134d --- /dev/null +++ b/docs/history/53-typed-source/notes.md @@ -0,0 +1,16 @@ +# 53 typed source and the diagnostics cache + +asked: type everything. the compiler, the runtime and the language server had untyped `fn` and `let` in most places, and the stdlib had one untyped method. + +built: +- src/rt.zc, src/compiler.zc, lsp/server.zc: every function header and every local and global `let` now has a type. parameters and returns that hold an address are `word`, the rest are `int`, and the `.u` return tag stays as it was (`word .u new(int n)`). `.m` and `.d` parameter tags are kept too. +- lib/std.zc: `fn end()` in struct `capture` is `word end()`. +- the proof that nothing changed: zc0's image of the typed sources is byte-identical to the untyped one (`cat src/rt.zc src/compiler.zc | bin/zc0` against the original). the full genome fixpoint is 232070 words (it was 230551 before the typing: zy generates different code for typed declarations). +- editors/vscode: diagnostics are cached under a sha256 key per document (client/diagcache.js). the key covers the server build, the path and text, and every file the document includes, transitively. a cached key is answered without asking the server, which hears the text only when hover or highlighting needs it. an included file saved or changed on disk checks its dependents again. the cache is saved in the extension's storage. tests/diagcache_test.js checks the rules, tests/ext_cache_test.js runs the client end to end on the built-in server. +- tests/lsp_test.js: tests/undefined_call.zc is an intentional error, like manyerrors.zc, so the clean check skips it. + +what went wrong on the way, kept for the next person: +- the first typer dropped `.d` and `.m` parameter tags when it rewrote headers. zc0 writes those tags into the symbol table's signature word, so `free(.d p)` changed from 130 to 2. the typer now keeps the tags, and word-typed parameters and returns turned out to be harmless. +- the typer grouped `let` items by type, which moved some arrays ahead of scalars. zc0 numbers locals by declaration order, so the frame slots of those variables moved (a 5 became a 21 in the image). the typer now keeps source order, and the image matches again. +- a bare `.u name(...)` header is not a type word, so the first typer missed it. `unchecked .u readfile(path)` and the others were typed in a second pass. +- the built-in wasm server cannot read include files (every include is "can't read that include"). the native server reads them. this is an existing limit, and the cache keys do not depend on it. diff --git a/docs/history/README.md b/docs/history/README.md new file mode 100644 index 0000000..b38f981 --- /dev/null +++ b/docs/history/README.md @@ -0,0 +1,50 @@ +# history + +the scrapbook. one folder per stage, each with notes.md: what was asked, what got built, and the working notes from the time, kept as written and only added to. stages that still have their code keep a frozen copy in code/: + +- 01-golf, 02-hardware, 04-darwin-compiler, 05-hybrid, 06-choosing: notes only, their code wasn't kept +- 07-zygote: notes and code (the asm genome and the arm64-only core, as packed in stages 8 and 9) +- 08-packaging: notes only +- 10-zc: notes and code (zc, u m d, the collector, the self-hosting compiler) +- 11-types-enhancements: notes and code (types, generics, reflection, the typechecker, virtual enhancements, the llvm backend) +- 13-gap-fixes: notes and code (joins with fixup frees, the buddy heap, stack checks, error recovery, the prescan, ?: and switch) +- 15-o3: notes and code (the examples, c-style return types, growable and fixed arrays, the o3 optimizing tier, literal-specialized enhancements, the release fix) +- 17-o3-boundaries: notes and code (generics after the name, fattening o3 data on the way out, registers by scope, cached fixed arrays) +- 18-aliases: notes only (borrowed-from marks on derived pointers, quarantine on free) +- 19-tools: notes only (memory debug, do/while, enum, casts, include, syscalls, lib/std.zc) +- 20-words: notes only (the vm word widened from 32 bits to 64) +- 21-micro: notes only (a portable microcore for the same genome) +- 22-size: notes only (a build-time memory map, a 1.31 mb compiler, a 256 kb run-only image) +- 23-baremetal: notes only (the microcore booting on qemu-system, riscv and aarch64 virt, UART in and out) +- 24-backends: notes only (the arm64 jit under qemu-user, and the llvm backend, run against the interpreter) +- 25-genmap: notes only (one layout for every memory size) +- 26-bareapi: notes only (syscall gate on the virt boards: console, clock, exit, files) +- 27-float: notes only (IEEE binary64 on the interpreter, both jits, the microcore and both virt boards) +- 28-bytes: notes only (byte addresses beside word addresses, opcodes 612..617) +- 29-strings: notes only (packed byte strings, `b"..."`, length, slice, concat, and byte syscalls) +- 30-sharing: notes only (shared cons cells and strings, collected by the existing sweep) +- 31-fpu: notes only (hardware float ops where an FPU exists, and a soft-float fallback) +- 32-ftype: notes only (float is a type: calls, globals, fields, parameters, returns; -0.0 is false; 1.field is a field) +- 33-fo3: notes only (o3 lifts float ops: IEEE-safe folds, FP registers, no x-x rewrite) +- 34-ftext: notes only (libc-free print and parse of the full double range) +- 35-bench: notes only (a word sieve and a binary tree, timed against C) +- 36-alloc: notes only (recycle onto the size-class list, a 5-word header, a bump arena) +- 37-call: notes only (o3 calls o3 by entering the region, and fatten only at a real boundary) +- 38-bounds: notes only (a proved index stays in a register, and a +1 loop drops the check) +- 39-blocks: notes only (empty jumps folded, a straight loop rotated to one block) +- 40-fatten: notes only (typed new stamps the header, and a child store writes the owner in the region) +- 41-obytes: notes only (ld8 through st32 lift inside an o3 region) +- 42-roots: notes only (the collector marks from the live stack index, and scrub clears the rest) +- 43-stdlib: notes only (u, m and d are names, an arena and a packed byte buffer, and untyped new inside o3 is zeroed) +- 44-check-mode: notes only (the language server is the compiler in check mode, struct methods and closures, postfix pointer types, same-line braces, a webassembly build) +- 45-fast-alloc: notes only (no prefix pointers, constant-size allocation fast path, maps sized to their program, a makefile, the core sources formatted and explained) +- 46-c-abi: notes only (the backtick is lexical, a parse word must read, lexer speed-ups, llvm target info, extern and cfunc native calls, a default map with a bigger genome slot) +- 47-limits: notes only (a heap that grows to the host's memory, host fill/copy/scan, native calls the optimizer sees, sized integers and f32 with packed arrays, ctz popcnt bswap rotl rotr f2s s2f) + +the code folders are snapshots. they're not built or tested from here, the top level is the live tree. +- 48 landing: packed fields, []sized, f32 extern, page-indexed invalidation, llvm trackers, faster o3 calls +- 49 generics: monomorphized multi-parameter generics and generic methods, llvm inline calls, global const-prop in o3, bigger default map +- 50 adt: tagged enums with fields, constructors, `case member(a, b):` matching, and a coverage warning +- 51 llvm-inline-stdlib: llvm inlining of o3 calls, stdlib text and num helpers, host syscalls through SYS_RAW, and the fuzz campaign's findings +- 52 closures: closure calls work (the block is never run; a call into a heap closure goes through its entry word), captures chain through enclosing closures, and the closure test joins the suite +- 53 typed source: every function, local and global in the compiler, the runtime and the language server is typed (zc0's image does not change), and the extension caches diagnostics by a sha256 of the tree they cover diff --git a/docs/history/baremetal-measurements.md b/docs/history/baremetal-measurements.md new file mode 100644 index 0000000..ef1ecd8 --- /dev/null +++ b/docs/history/baremetal-measurements.md @@ -0,0 +1,55 @@ +# earlier bare-metal measurements + +these measurements describe the builds recorded at the time; they are not current size guarantees. + +## bare metal + +`baremetal/run.sh` builds a freestanding image and boots it on QEMU's `virt` board, headless, with the UART on stdio. there is no kernel and no libc. startup sets the stack, zeroes bss, and calls `bare_main`, which calls `micro_run`. two targets, picked because a cross gcc and a system emulator both install from the distro packages: + +``` +baremetal/run.sh ready riscv # exits 0 when the tools exist +baremetal/run.sh riscv run examples/hello.zc +baremetal/run.sh riscv io examples/echo.zc # stdin, then 0x04 +baremetal/run.sh riscv compile examples/fact.zc +baremetal/run.sh aarch64 run tests/gc.zc +baremetal/run.sh aarch64 compile tests/wide.zc +``` + +`run` is the 36864-word image from `genmap --preset run`: `rt.zc`, `src/run.zc` and the program, compiled by `zc0` into the ELF. nothing is read. `io` is that same image with the UART open: the script's stdin is the program's input and a `0x04` is appended, which `eof()` reports and `getc()` does not return. `compile` is the default 1,048,576-word genome. the script writes the source, a backtick, and a `0x04`. the backtick ends the unit, the same rule as the hosted compiler, and the `0x04` is what the probe after that backtick is waiting on. `test.sh` runs these, on each target whose tools are present, and skips the one that isn't. a missing `timeout(1)`, cross gcc or `qemu-system-*` is a skip, not a failure. + +the guest is the same two word maps as the hosted builds. what changes per target is where RAM, the UART and the power-off device sit. + +RISC-V, `qemu-system-riscv64 -machine virt -bios none -cpu rv64 -m 64`. machine mode, no OpenSBI. RAM starts at `0x80000000`; the linker uses 48MB of it. the UART is a 16550 at `0x10000000`, polled through the line-status register. exit is the SiFive test device at `0x100000`: `0x5555` makes QEMU exit 0, and `(status << 16) | 0x3333` exits with that status (`0x13333` is status 1, `0x33333` is status 3). the clock is `rdtime`. `mstatus.FS` is set so a compiler that emits a float op does not trap. + +AArch64, `qemu-system-aarch64 -machine virt -cpu cortex-a57 -m 64`. RAM starts at `0x40000000`, again 48MB in the linker and 64MB given to QEMU. the UART is a PL011 at `0x09000000` (flag register at `0x18`). this QEMU 8.2 starts the ELF at EL1, and `-bios none` is not passed: that build looks for a file named `none` and refuses. `-kernel` of the ELF is enough, and the CPU begins at the entry point. if it had come up at EL2 the startup drops to EL1h before touching the stack. SIMD is turned on in `CPACR_EL1`, because `-O2` copies the genome with `q` registers and a disabled FPU takes an exception at the vector base plus `0x200` (that is what the first boot did, before there was any UART output). exit is semihosting `SYS_EXIT` (`hlt #0xf000`, `x0 = 0x18`, `x1` pointing at `{0x20026, status}`), and the command line passes `-semihosting-config enable=on,target=native`. PSCI `SYSTEM_OFF` cannot carry a status, which is why it is not the exit path. the clock is `cntvct_el0`. the UART line is the thing to read. + +both images are linked with `-ffreestanding -nostdlib -static`. the cross compilers are `riscv64-linux-gnu-gcc` (`-march=rv64gc -mabi=lp64d -mcmodel=medany -mno-relax`) and `aarch64-linux-gnu-gcc` (`-march=armv8-a`). QEMU is `-nographic -monitor none -serial stdio`, so the bytes on stdout are the bytes the guest wrote. `run` is killed after 120 seconds, `compile` after 90. `tests/gc.zc` on the run-only image needs about 30 seconds; the others finish in about a second. + +image sizes, from `size` and the ELF file length. bss is the machine plus the enhancement tables and a 64KB stack. the AArch64 file is bigger than its text and data because the load segment is aligned to 64KB. + +| | ELF file | text+data | bss | +|---|---|---|---| +| RISC-V, run, hello | 87704 | 81741 | 410664 | +| RISC-V, run, fact | 88504 | 82541 | 410664 | +| AArch64, run, hello | 149704 | 82248 | 410656 | +| RISC-V, resident compiler | 741624 | 735565 | 8537128 | +| AArch64, resident compiler | 803616 | 736040 | 8537136 | + +the compiler file does not change with the program fed over the UART. most of its text is the default genome, 103655 words, 829240 bytes. the run-only bss is the 294912-byte machine; the compiler bss is the 8MB one. both fit in the 64MB QEMU gives `virt`. + +what booted, and matched the hosted interpreter on the same source: `hello`, `fact`, `wide` and `gc` as run-only images, and `hello`, `fact` and `wide` compiled by the resident compiler over the UART, on both boards. `gc` printed `1000 1 900 1 199900`. + +a Cortex-M was not ported. the word is 64 bits and the run-only bss is already about 400KB, before the genome in rodata. the boards `qemu-system-arm` actually models at that size (the Stellaris `lm3s6965` has 64KB) cannot hold it, and a 32-bit core would be emulating every cell in software. + +limits, on top of the ones in the smaller-machines section: + +- the syscall gate is the same one as the hosted core. on these boards, open/close/read/write are a RAM file store (4 files, 128 bytes, 31-character names), `getenv("BOARD")` is `riscv` or `aarch64`, time is the hardware counter rather than unix seconds, exit takes a status, random is a counter seeded from that clock, and memory info returns `MW`, the heap bounds and the byte size. the stub port still returns -1 for every op. a file is not the host's filesystem, and the counter is not wall time, so `tests/sys.zc` stays a hosted test. +- the microcore still has no o3 fast path. the bytecode runs. +- diagnostics (`zy: data stack overflow` and the rest) go out the same UART as program output. +- the 16550 is left at QEMU's reset settings. writing the line-control or FIFO register after the source has started arriving drops the first byte, and `fn` becomes `n`, which the compiler reports as `n: isn't a type`. +- end of input is `0x04`. `micro_eof` and `micro_getc` wait until a byte is actually in the UART, so an empty FIFO is a pause and not the end, and a stream longer than the 16-byte 16550 FIFO still reaches its `0x04`. that byte is not delivered. the compiler still ends a unit on a backtick; `compile` sends `0x04` after it so the probe that follows the backtick returns. `examples/echo.zc` fed 36 letters under `io` prints those letters on both boards. +- the 192512-word compiler is not what these boards boot. `virt` has room for the default genome, and that is the one the `compile` mode uses. 294912 bytes still cannot hold a compiler. +- no native calls (the c abi needs `dlsym`), and a character is still one word. + +the notes in `history/23-baremetal` have the boot log and the places the first images hung. + diff --git a/docs/history/maintenance-notes.md b/docs/history/maintenance-notes.md new file mode 100644 index 0000000..2a2bed4 --- /dev/null +++ b/docs/history/maintenance-notes.md @@ -0,0 +1,30 @@ +# historical test results and maintenance notes + +this material records earlier builds. current work and verification are tracked in TODO.md. + +## test results + +the whole of `test.sh` is 614 checks on a machine with gcc, qemu-user, llvm, node and clang's wasm32 target (572 passed, 0 failed; the baremetal boards need cross compilers and are skipped without them). on the interpreter alone it starts with the fixpoint (236652 words), an up-to-date `genome.h`, and then every example and feature test, including a shift past bit 31 and equality of a value above 2^31. it also covers joins and fixup frees (with a leak count), `switch` and `?:`, the buddy allocator, aliases used while their owner is alive, quarantine (a freed block stays intact for a stale alias, a second free does nothing, and a collection keeps a block a word still points at), `do`/`while` with `enum` and a cast, `include` and the string helpers, syscalls, memdebug leaks, use-after-free (including through an interior pointer), double free, payload poison and interior pinning, several errors in one run, all three stack overflows, growable and fixed arrays, and `tests/o3.zc` (loops, recursion, calls inside loops, `switch`, `&& ||`, structs through pointers, arrays, division by constants, and a function with more variables than registers). it checks that every o3 region in that test lifts, that the o3 bounds check fires, and that all 26 programs in `tests/errors/` (including derived aliases used after their owner dies), all 10 in `tests/typeerrors/` and all 9 in `tests/langerrors/` get rejected with the expected message. the same script then runs that list on the microcore, checks that the microcore's self-compile matches the interpreter's image, and checks that `micro.c` and `port_stub.c` compile with `-ffreestanding`. that is 113 checks with the jit and llvm skipped. it then builds the 339968-word compiler and runs 22 programs on the small microcore and again on `core.c` at that size, builds the 32768-word run-only image for six programs, and checks that both reduced microcores compile freestanding. that is 165 checks with the jit, llvm and bare-metal boots skipped. when the cross gcc and `qemu-system-*` for a target exist, `test.sh` adds 7 boots on that target (run-only hello, fact, wide and gc, then the resident compiler fed hello, fact and wide). both virt boards were present here, which is 14 more. the jit needs `qemu-aarch64`, the linux-user emulator, which `qemu-system-aarch64` is not. the llvm backend needs `llvm-config`. those two were not executed in the environment that cut the word to 64 bits. they were executed later (stage 24), on this host, and the full script then passed 292 checks. stage 25 added three more (the default map matches the checked-in sources, and the flag form of `genmap` reproduces the small and run maps), 295 passed, 0 failed. stage 26 added eight more (echo until eof, the clock and RAM-file line, the exit text and the exit code, on each virt board), 303 passed, 0 failed. stage 27 added twelve more: the float line on the interpreter, the microcore, the arm64 jit and the llvm backend, the same line on the small microcore and on `core.c` at that size, the run-only image, both virt boards running the program and compiling it, and one llvm run with `ZYHOT=1` so every float block is compiled. 315 passed, 0 failed. stage 28 added twelve more, the same shape of check for byte loads and stores, including a one-byte patch of a live literal. 327 passed, 0 failed. stage 29 added twenty-three more for packed byte strings: the string line, the example, and the hosted byte-file check on the interpreter, the microcore, the arm64 jit and the llvm backend; the string line and the example on the small microcore and on `core.c` at that size; the run-only image of both; both virt boards running the program and compiling it; and one llvm run with `ZYHOT=1`. 350 passed, 0 failed. stage 30 added eighteen more for shared cells: the sharing line and the memdebug line on the interpreter, the microcore, the arm64 jit and the llvm backend; both of those on the small microcore and on `core.c` at that size; the run-only sharing line; both virt boards running it and compiling it; and one llvm run with `ZYHOT=1`. 368 passed, 0 failed. stage 31 added one more: a soft-float interpreter matches the float line. 369 passed, 0 failed. stage 32 added twenty-two more: negative zero is false, and a float stays a float through calls, globals and fields, on the interpreter, the microcore, the arm64 jit and the llvm backend; both of those on the small microcore and on `core.c` at that size; the run-only image of the negative-zero program; both virt boards running that program and compiling it and the float-type program; a soft-float check of negative zero; and llvm runs with `ZYHOT=1` for both. 391 passed, 0 failed. stage 33 added ten more for o3 floats: the lifted kernel matches the plain one, a constant expression folds, a NaN minus itself stays a NaN, and negative zero is false inside the region, on the interpreter, the microcore, the arm64 jit and the llvm backend; the same line on the small microcore and on `core.c` at that size; both virt boards compiling it; a soft-float interpreter; and one llvm run with `ZYHOT=1`. 401 passed, 0 failed. stage 34 added thirteen more for the float text: shortest, fixed and scientific forms, a 17-digit tie, and the full finite range, on the interpreter, the microcore, the arm64 jit and the llvm backend; the same line on the small microcore and on `core.c` at that size; the run-only image; both virt boards running it and compiling it; a soft-float interpreter; and one llvm run with `ZYHOT=1`. 414 passed, 0 failed. the jit binary is `aarch64-linux-gnu-gcc -O2 -w -DJTYPE=int -static core.c io_linux.c` (gcc 13.3.0, static, against the aarch64 sysroot) run by `qemu-aarch64` 8.2.2. the llvm binary is host gcc 13.3.0 with `-DZYLLVM` and LLVM 18.1.3 (`llvm-config --cflags --ldflags --libs`). both ran the same program list as the interpreter and self-compiled to the same image. `ZYTRACE=1` on `tests/o3.zc` printed 8 `bytes of arm64` lines and 8 `compiled by llvm` lines. `ZYHOT=1` on the llvm binary made `tests/types.zc` match the interpreter byte for byte. the first jit run failed one check: `tests/enhance.zc` printed `100 0 343 3,2` and then halted. opcode 21 calls a C function that returns a 32-bit `int`. a write to `w0` zeroes the high half of `x0`, so the failure code `-4` (the `noisy` enhancement, whose body does i/o and does not lift) was stored as a large positive cell. the compiler treated the install as a success, emitted the unwired opcode, and the jit halted. the translator now does `sxtw x0, w0` before storing that cell. the Darwin jit (`MAP_JIT` and the write-protect around each flush) was not run. this host is x86_64 Linux, and qemu-user is the arm64 cpu the jit actually executed on. the 64-bit image is not binary-compatible with a 32-bit genome. + +## open ends + +status after stage 48: done from the older lists below are native calls that the optimizer sees, a heap that is no longer limited by the initial map, host-assisted fill, copy and scan, sized integers and f32 with packed arrays and packed struct fields, growable `[]sized`, f32 in `extern` and `cfunc`, the micro-ops `ctz popcnt bswap rotl rotr f2s s2f`, page-indexed invalidation, per-slot install invalidation and per-block llvm resource trackers (the self-modifying benchmark in `bench/selfmod.zc` runs about 7 times faster on the jit). o3 calls on llvm take a shorter path into the callee (about 25 percent off a call-bound loop). not done: struct by value and struct returns on apple arm64, a fully inline llvm-to-llvm o3 call (calls still go through a c helper), loop-invariant motion and cross-block cse in the core's own optimizer, automatic tiering without the `o3` keyword, floats and the f2s and s2f ops in the javascript vm, and re-running the windows scripts and the bare metal boards after the map change. + +fixed in stage 13 (details in `history/13-gap-fixes`): branch and loop joins, free lists that never merged (now a buddy allocator), missing stack checks, stopping at the first error, macro line numbers, required prototypes, `d*` for storing into borrowed objects, `**` declarations, and `?:` and `switch`. fixed in stage 18 (details in `history/18-aliases`): untracked aliases. a pointer computed from a tracked variable (`p + k`, a pointer field, an index whose element is a pointer) is stored as a borrow of that variable, on the same child link the analyzer already keeps, so using it after the owner dies is an error. `free` quarantines the block; the next sweep only reclaims quarantined blocks nothing points at, so a stale alias reads a dead but intact block and a second free is a no-op. fixed in stage 19 (details in `history/19-tools`): memory debug (header checks, allocation lines, `memreport`, poison, interior pinning), `do`/`while`, `enum`, C casts, `include`, a small syscall surface and `lib/std.zc`. fixed in stage 20 (details in `history/20-words`): every cell is 64 bits, on the interpreter, the arm64 jit and the llvm path. fixed in stage 21 (details in `history/21-micro`): `micro.c` is a portable interpreter for the same genome, with the host behind `micro_port.h`. fixed in stage 22 (details in `history/22-size`): the memory map is chosen at build time, with a 221184-word compiler and a 32768-word run-only image. fixed in stage 23 (details in `history/23-baremetal`): the microcore boots with no OS on QEMU's RISC-V and AArch64 `virt` boards and prints on the emulated UART. fixed in stage 24 (details in `history/24-backends`): the arm64 jit and the llvm backend were actually executed, and the jit's install return is sign-extended so a failed enhancement takes its fallback. fixed in stage 25 (details in `history/25-genmap`): one layout generates every memory size. the small compiler, the run-only image and the historical 1m map are presets of it. fixed in stage 26 (details in `history/26-bareapi`): the virt boards answer the syscall gate (console EOF, a hardware clock, exit status, a RAM file store, random, memory info) and `port_stub.c` still returns -1. fixed in stage 27 (details in `history/27-float`): IEEE-754 binary64 on the interpreter, the arm64 jit, the llvm backend, the microcore and both virt boards. the literal is opcode 600 plus the raw bits, and `fops.h` prints and parses the same text everywhere. fixed in stage 28 (details in `history/28-bytes`): byte addresses beside word addresses, opcodes 612..617. fixed in stage 29 (details in `history/29-strings`): packed byte strings (`b"..."`) beside the word strings, with length, index, slice, concat, comparison, `ston`/`ntos`, and syscall ops 10..13. fixed in stage 30 (details in `history/30-sharing`): shared cons cells and strings on the existing heap. bit 8 of the header is the share bit, `free` does not quarantine one, and the sweep collects it when nothing points at it. `LEARN.md` walks through the language. the c abi came later (see "c abi"). a word string is still one character per word; a byte string is eight characters per word. the microcore does not run o3 as a fast path. the small compiler does not self-host, and a 60000-word array does not fit in its heap. still open: + +**the conservative collector.** opcode 619 pushes the data-stack index, and the collector marks from there to the top, so a word the program has already dropped is not a root. `scrub` zeroes the unused region from the bottom up to the live top. the frame stack is still scanned whole. zeroing a frame in the prologue would do the same there, at a few words per call. + +**invalidation.** done in stage 48; see the self-modification paragraph in the jit section. what remains is only that a covered word whose count saturates at 255 is never decremented back to zero until a full flush, which costs a hook call on stores to it and nothing else. + +**automatic tiering.** dropped. the o3 keyword stays the way to get a native region for a function. the reasons (the region record, the calling convention for dynamic arrays, the compile-time o3 checks) are in design/tiering.md. + +**o3's own gaps.** on the jit, an o3 call to another compiled region is a direct call. on llvm a call to a compiled region is now inlined as a spill, a check, a frame setup and an indirect call through the callee's native entry, with no reload on the fast path (fib went from 0.69 s to 0.39 s), and the c helper is only the slow path. it is not yet a register-passing direct call, which costs about 12 ns where c costs 3 to 4. the core now has a global pass after the per block one: a constant lattice per variable across the region's blocks, refolded through the same emitter, so a branch on a known value is folded away. push and pop on a growable array are inline. a value that stays on the vm stack across a branch goes through memory, since blocks only share variables. there is still no loop-invariant motion or cross-block cse in the core (llvm does it on its build), no automatic tiering, and struct by value and struct returns in native calls are done for x86-64 and arm64 (see the extern note above), not for apple arm64. regions aren't built into images, so `rt.zc` and the compiler can't use o3, and o3 code that rewrites its own region sees the change at the next entry. `ZYNOGLOBAL=1` turns the global pass off. + +**generics are checked at their instances.** a generic function's body is not compiled where it is written: its tokens are kept and each set of type arguments gets its own copy, compiled after the item that asked for it, so an error inside a template body shows up when something instantiates it (like a c++ template), and a generic name used as a value has the template's type and halts if it is ever called. a struct passed to a generic parameter by value is passed by pointer, as everywhere else in zc. the template has to be written before the first call that gives it type arguments, since a call to a name that is not yet known as generic is read as an ordinary forward call. an unbound parameter is `word`. + +**check mode and closures.** a closure copies what it uses when it is made, so a closure that changes a captured variable changes its own copy, not the function's. the hints are heuristics on one function at a time: they don't follow a value through a call, and a stack buffer hint doesn't check that the size is what you wanted. after a fatal error in check mode (running out of locals, say) the compiler unwinds and can print one follow-on error. if a document makes the check run for 3 million tokens without unwinding the server stops, which hasn't happened outside of a deliberately broken parse word. the windows scripts were updated by reading, not run. + +**the rest.** the prescan only knows tags, not types, so a call to a typed function that's defined further down gets `word` as its result type until the definition shows up. the llvm backend compiles one module per block, so it warms up slowly. stores done by enhancements don't invalidate compiled code. a parse word that changes the syntax can confuse the prescan. it only records signatures, though, and a real definition always overrides what the prescan guessed. + +--- + diff --git a/docs/history/transcript.md b/docs/history/transcript.md new file mode 100644 index 0000000..9c9f451 --- /dev/null +++ b/docs/history/transcript.md @@ -0,0 +1,257 @@ +# development transcript + +these are historical requests and replies, kept as originally recorded. commands and feature descriptions here may refer to earlier layouts. use the current README.md and LEARN.md for instructions. + + +each of my messages is quoted exactly, followed by a summary of the reply. + +### 1 + +> codegolf a tiny cpu vm in C89. the goal is to achieve the lowest character count possible. include an assembler as well (scored separately) and a hello world program (not scored, but elegance and simplicity is a bonus) +> it must be capable of re-entrant code and a realistically workable instruction set. + +**summary:** a two-stack forth-style machine. the vm was 384 bytes: one ternary chain inside `while(o=m[p++])`, the top two stack items cached in `a` and `b`, the program loaded with `read(open(...))` so it needs no headers, and both stacks inside the 65536-word memory. it had 22 opcodes including div and mod, a two-word literal, and jump/call targets popped from the stack. the assembler was 348 bytes and two-pass, using `strcspn` instead of `strchr` so the implicit int return stays safe on 64-bit. hello world was a pointer loop over a string with a zero terminator. re-entrancy was shown with a recursive factorial plus a recursive decimal printer. + +### 2 + +> your cpu should also be realistically close to hardware (translating it to logical circuitry hardware should be fairly easy) and sport an acceptably fast execution (if hypothetically ported to hardware, not in software) +> +> assume i/o is handled by two external functions, `char Q()` (getchar) and `void G(char)` (putchar) and a state variable J (boolean, shows if input is empty). +> +> don't be afraid to use macros. + +**summary:** no reply was sent to this message at the time. it was answered together with message 3. + +### 3 + +> pro tip: if you define a macro as `!o--`, you can save 2 characters per if condition +> oh and btw, newlines are free + +**summary:** used the tip as `#define X !--o?`, with pre-decrement because opcode 0 (halt) is already handled by the loop, and with the `?` folded into the macro and free newlines used as separators. the hardware-oriented redesign: bit 0 of each word is a literal flag, so literals take one cycle like the j1 cpu. div and mod were dropped because they can't finish in one cycle and were replaced by an arithmetic shift right, giving 8 alu ops that decode from 3 bits. every instruction does a fixed amount of work with at most one data memory access, and call and jump share a datapath (`r-=o`). i/o now goes through `Q`, `G` and a `?` op that reads `J`. the result was the vm at 339 bytes and the assembler at 346. the final golfed code: + +```c +#define X !--o? +int m[1<<16],p,s=65e3,r=6e4,o,a,b;main(c,v)char**v;{read(open(v[1],0),m,s);while(o=m[p++])a=m[s],b=m[s+1],o&1?m[--s]=o>>1:(o/=2,X +m[s]=m[a]:X +m[a]=b,s+=2:X +m[s]=m[s-~a]:X +s++:X +m[s]=b,m[s+1]=a:(o-=2)<1?m[r]=p,r-=o,p=a,s++:X +p=b?p:a,s+=2:X +p=m[--r]:X +m[--s]=Q():X +m[--s]=J:X +G(a),s++:(m[++s]=X +b+a:X +b-a:X +b*a:X +b&a:X +b|a:X +b^a:X +b>>a:b now let's move in stages. write a compiler that takes this assembler's code and converts it into darwin arm + +**summary:** an ungolfed c89 compiler from vm assembly to darwin arm64 assembly. every word got its own label, the image was emitted as an addressable data array, x19/x20/x21 held the memory base and the two stack pointers, and computed jumps went through a table of offsets. a literal followed by a jump became a direct branch. `Q` and `G` went through `sxtb`, since char is signed on apple arm64, and `J` was read with `ldrb`. it was verified by assembling with llvm-mc for darwin, then rewriting to linux with sed and running under qemu, where it matched the vm byte for byte. this version couldn't handle self-modifying code. + +### 5 + +> can you write the assembler in assembly and pair the code with the assembly and the assembler so that it can re-wire itself at runtime? or maybe include an interp for self-modification, otherwise fns are exec'd native + +**summary:** both. `aslib.s` is the assembler written in vm assembly as a library (`a.src`, `a.dst`, `a.org`, `a.lab`, then `a.run c`), and with `asmain.s` it's a 568-word standalone assembler. it's byte-identical to the c assembler and reaches a fixpoint when assembling itself. this is also where the `:;comment` trick came from. the compiler became a hybrid. the image is split into basic blocks, each block counts how many of its words differ from the original image, and stores into code go through a slow path that updates the counters. clean blocks run native, while dirty blocks and anything past the image run in an interpreter compiled into the same binary, which hands control back to native code whenever it lands in a clean block. `cc -t` printed `[` and `]` at each switch. the demo rewired a function at runtime to point at freshly assembled code and then restored it. + +### 6 + +> which one do you like more? and which is more performant? +> keep that one, then port to using llvm ir so that it can be platform independent + +**summary:** no reply was sent at the time. the llvm port was dropped when message 7 changed direction. the question was answered at the start of the reply to message 7: the hybrid was the favorite because it kept the vm's semantics exact. the plain compiler was slightly faster on code that never rewrites itself, but it silently ran stale code once anything did. the zygote beats both. + +### 7 + +> can you make it a zygote approach? +> a minimal core loads first, reads and compiles the program (either wholly or partially, for speed) and replaces it with itself. so it's like a jvm but self-contained and on the same level as itself, where the assembler can itself be modified in-memory to assemble different code. for this we need to ditch llvm + +**summary:** the current design, described above. the native core only does jit and glue. it loads the genome (boot loader + assembler, both vm code) at 48000. the boot loader reads source until eof or a backtick, assembles it at 0 and jumps there. compilation is lazy per block, straight from live memory, with superblocks through `z`, fused constant targets, and dispatch chained through a table. invalidation uses the coverage byte map plus a store hook, so self-modified code gets retranslated instead of interpreted. the resident assembler sits at fixed addresses, and `repl.s` rewrites its mnemonic table so `p` means out, then assembles and runs input chunks in a loop, overwriting the same address each time. verified under qemu on linux arm64, including the self-hosted assembler running on the zygote. about 4x faster than the golfed interpreter. + +### 8 + +> damn dawg you made an entire baremetal os? +> +> let's move to cowork. pack everything up alongside a transcript of every message i've sent and your own notes on what you've implemented in an md file, with the code, so i can move it. + +**summary:** not quite an os. it's a userland runtime. it still runs on darwin or linux, uses their mmap for executable memory, and gets i/o from the host through `Q`, `G` and `J`. there's no scheduler, no drivers and no hardware access, so a closer description is a tiny self-hosting jit, in the spirit of a jvm. the first packing attempt bundled every stage's code with a full test script. + +### 9 + +> nono we don't need all the code, just the recent ones. my msgs and your summaries are more important and all should be there + +**summary:** this package. only the zygote stack plus its examples and tests are included, and earlier stages appear only as summaries. the golfed reference vm and assembler are inlined under message 3, because they define the instruction set. + +### 10 + +> we're gonna make this thing cooler. make the syntax C-like whilst still keeping the forth capabilities like super-homiconicity and the likes, add explicit and automatic gc (use a fast approach) and memory management that looks like this: +> every heap object has a classifier u m d, up middle down. up means the object will die at the scope above (becomes m when returned to the parent scope, and it must be returned and cannot be discarded). m means it must be used here and can be passed down (but once it's recasted as u or d it can't be casted back to m in this scope). d means the variable is passed down, and its lifetime ends after it's given for the first time to a scope lower than the current one. +> ask me questions to clarify this approach, then add it to the code and also add a simple static analyzer which checks these lifetimes + +my answers to the clarifying questions, exactly: + +> scope: every { } block +> +> lending an m object to a callee: borrow, no escape (Recommended) +> +> storing a heap object inside another object or a global: container gets ownership. putting a value inside a container requires its umd to match the parent, and from then on, the umd for that is also inherited from the parent (as in if a parent m is casted to d, so is the child heap object) +> +> gc: free lists + mark-sweep (Recommended) +> +> where the compiler lives: resident in vm code. bootstrap it step by step, by writing a c-like in core.c and then rewriting the compiler inside the c-like. also bring back llvm if you think it'll make your job easier +> +> homoiconic features: parse words, quoted code blocks, inline stack code, patchable compiler tables +> +> how an inner block hands a u object up: expression and assign, both +> +> when the analyzer runs: inside the compiler but can be turned off if the code requests it + +**summary:** the genome is now zc, a typeless c-like language, described above. stage 0 is `zc0.c` (kept as its own file next to core.c rather than inside it, so the runtime still has one native file), and the resident compiler is `src/compiler.zc` plus `src/rt.zc`, which reached a byte-identical fixpoint compiling itself. llvm wasn't brought back, since the target is the vm itself. memory went to 1m words, and core.c gained an interpreter for non-arm64 hosts. calls i made without asking: tags go on signatures (`fn f(m a, d b) -> u`) and recasts are `x as u` / `x as d`; a d object touched inside an inner block moves into that block; storing into a borrowed object, a global or untracked memory needs `as d`; handing a u up by assignment only reaches the directly enclosing block; unchecked code gives its objects to the collector. the old asm genome moved to `legacy/`. + +### 11 + +> progress report? +> add types please :) +> mostly as a compiler primitive though, plus type-inspection via reflection +> and a small optional typechecker (default off) +> oh and keep the stuff you write as a history scrapbook +> if it helps, add a path to the hardware which is called "virtual enhancements", and allows up to 512 custom operations to be compiled down via jit into hardware-level speeds and interoped directly into a fast path in the vm to become essentially custom wiring. useful for division and other stuff as a dynamic runtime path + +my answers to the clarifying questions, exactly: + +> types: words + structs + generics +> +> reflection: runtime type of heap objects, function signatures, typeof at compile time +> +> how an enhancement is defined: either actual C or llvm ir code or vm code or micro-ops passed through an optimizer. since for example, micro-ops and vm code are not sufficient for optimizing things like division +> +> scrapbook: notes + code snapshots + +**summary:** the progress report said the previous round was finished and shipped. i then laid out a plan where the core would compile c or llvm ir enhancement sources at runtime with the host toolchain and load the result. part of that reply was stopped by a safety check before it finished, so i stopped and checked in instead of building it. + +### 12 + +> also make the umd syntax like `type.u name`, and casting is multiplication `u*name` +> ideally i'd prefer memory to be unlimited words growing until system oom, but 1m is fine +> core.c plain interp is also nice +> zc0.c own file is fine +> i mentioned llvm for portability to non-arm targets +> yes, d object semantic that was intended +> u object hands up one block and becomes an m, that's fine. the code can make it a u or a d or whatever +> explain your gaps more +> +> wait why safety check? what's wrong with that + +my answers to the clarifying questions, exactly: + +> declarations: c-style, var -> let +> +> tags on untyped values and the names u, m, d: names reserved, bare .u allowed, untype is m by default +> +> llvm: llvm jit backend (Recommended) + +**summary:** on the safety check: nothing was wrong with the request. my best guess was that it tripped on the plan for a program inside the vm handing source text to the system compiler and loading the result into the running process, since that pattern also shows up in malware. with llvm meant for portability, that path wasn't needed. the gaps got the longer explanations that are now under open ends above. then the round itself: + +**syntax.** declarations are c-style (`int x`, `*node.m p`), `let` replaces `var` for untyped and inferred declarations, tags are `.u .m .d` after the type or on their own, recasts are `u*x` / `d*x` / `m*x`, `u m d` are reserved, and an untagged parameter counts as an m lend. + +**types.** a compiler primitive: hash-consed records with structs, generic structs and functions, fixed arrays, function types, `typeof`/`sizeof`, `as` casts and a reflection api, with object headers carrying their type. the typechecker is off by default and turned on with `typecheck on;`. + +**virtual enhancements.** opcode 21 installs, 22 to 533 are the slots. micro-op recipes and lifted vm code go through an optimizer in the core (folding, cse, dce, multiply-high division by constants) and get inlined by both jits. the language's own `/ % <<` now run on them. + +**backends.** a tiered llvm backend (`-DZYLLVM`) for non-arm hosts. + +**bootstrap and history.** it still reaches its fixpoint on all three backends. `history/` holds notes for every stage and code from stages 7, 10 and this one (in 11-types-enhancements). + +### 13 + +> gaps +> 1. when there's a var state change to d, mark that var as dead by default for any other scope that can access it afterwards. u is fine since it just returns +> 2. any suggestions for a cheap fix? +> 3. it's fine, rare occurrence (probably?), suggest a fix +> 4. fix that +> 5. suggest a fix +> 6. fix +> 7. fix + +**summary:** the numbers followed the gap list from my previous chat message. here's what each one got. + +**1. joins.** a variable that became d, or was given away, on any path is dead after the join, and fixup code frees it on the paths that still own it. a loop's exit state is the join of its breaks and its normal exit. giving something away on a path that loops back is still an error. u and borrows don't get the dead treatment. + +**2, 3 and 5.** suggestions only, now under open ends: a quarantine on reuse plus "borrowed from" marks on pointer-typed values for aliases, a stack-depth opcode for the collector, and page-indexed invalidation plus per-slot install tracking for the jit. + +**4. heap.** a buddy allocator. + +**6. stack checks.** all three stacks are checked: data and return at every block exit on all backends, and frames in every prologue. + +**7. the small stuff.** error recovery with up to 20 reported errors, line numbers carried in token lists, a signature prescan over the whole unit so prototypes are optional, storing into a borrowed object moving the object automatically, `?:`, and go-style `switch`. on the side, a 4-token lookahead fixed `**` declarations. + +### 14 + +> how costly is it if we add a counter to each block to see how many times it runs, then run the optimizer on it if it's a hot path? + +> continue + +**summary:** answered without building anything. about 4 instructions per block entry while a block is being counted, and nothing once its optimized translation replaces it. the optimizing pass would reuse the enhancement lifter, optimizer and register allocator. lifting whole blocks would need jz side exits and barriers at calls and i/o. compiling a block that way takes microseconds, against milliseconds for llvm, and keeping values in registers instead of the memory stack likely gives 2 to 4x on tight loops. it's listed under open ends. then the gap fixes continued. + +### 15 + +> write some really cool examples for this language + +> no, wdym building a fresh vm?? just pocs 😭 we already have a compiler and everything + +**summary:** my progress note had said "building a fresh vm", which only meant compiling core.c into a binary to run the examples. eight proofs of concept went into `examples/`: mandel, bf, dump, tree, lang, staged, quine and life, each described under files above and checked by `test.sh` on all three backends. + +### 16 + +> add opt keywords, for example `optim` (or o3, i prefer o3 as the keyword) will cause any vars in a scope to not assign heap type metadata, compiles with opt level o3 and is immediately compiled down to native, and when used with variables they must be always typed and it reduces the perf cost for accessing or modifying them, etc etc. add a `fixed` keyword which says that an array has a fixed size and cannot be resized, etc + +my answers to the clarifying questions, exactly: + +> where o3 goes: functions, blocks and single vars +> +> how far o3 goes: real optimizing tier (Recommended) +> +> what o3 code may give up: type stamps on objects, zero-filling new objects, the address of o3 vars, stack and frame checks, also i don't like the -> return type syntax. please replace it with the c-like syntax +> +> arrays: add growable arrays too (Recommended) + +**summary:** three things changed. + +**return types.** functions are declared c-style now, `int f(int x)`, `*node.u make()`, with fn types written `int(int, *char)`. `->` is gone from the compiler, zc0, the tests and the examples. + +**arrays.** `[]T` grows (`push`, `pop`, `resize`, `len`, `cap`), and `fixed []T` can't change size after `new(fixed []T, n)`. both are bounds-checked. `[n]T` stays as it was, plus a compile-time check for constant indexes. + +**o3.** on functions, blocks and single variables, with the trade-offs you picked, and a real optimizing tier in the core, described in the o3 section above. it compiles at install time on arm64 and llvm and has a region interpreter for the plain build. measured gains are 1.5 to 2.8x on the interpreter, 8 to 100x on llvm and 20 to 43x on arm64 (under qemu) for loop-heavy kernels, and much less for call-heavy ones. on the way, the arm64 jit learned to specialize an enhancement on literal inputs, so `x / 7` is a multiply-high everywhere, not only in o3 code. tests for arrays and o3 turned up an old heap bug: freeing a container could merge one of its children into it while it was still being freed, which corrupted a free list. growable arrays hit it every time. it's fixed. + +### 17 + +> generics should go after fn name and before parens +> we also need some machinery for converting values to/from o3. to is pretty easy, we just hand over the data, and o3 ignores the fat metadata. from is a bit harder, there needs to be a system to get type hints and fatten the metadata for the packed data +> variables just get the latest free register, it's fine for now +> add the fixed array once per region thing +> +> also if you have any tips on how to make the memory system better (aka avoiding gc and leaks/uaf as much as possible) do tell + +**summary:** generics went back after the name, `T first(...)`, with the return type allowed to use them before the list declares them. o3 got fattening on the way out (the section above): `new_raw` keeps the requested size in the type word, and the compiler calls `fatten` with the static type wherever o3 data reaches normal code. registers now follow scopes, so a closed scope's register goes to the next variable. fixed arrays in o3 variables keep their items and length in two hidden register variables. the memory tips were answered in chat, not built. + + +### 18 + +> there's been changes. run a consistency pass, fix comment stylings (change the cringe dry ones to the format you used), bug fixes, fix the lsp, write tests for it, then impl the remaining points in todo.md +> +> the analyzer should be in tandem with the compiler, and immediate words / syntax modifications should affect it just the same. same with the syntax highlighter. + +**summary:** the language server is the compiler now. `src/compiler.zc` has a check mode (errors become records, compiling goes on, hints are reported), and `lsp/server.zc` is only the json-rpc layer, the document store and the token writer on top of it, so parse words and syntax changes apply to the check and the highlighting exactly as they do in a build. the old standalone analyzer (`lsp/zclsp.zc`) is gone. struct methods with `this` and closures are real compiler features (they were only recognised by the old analyzer), pointer types can be written `int*`, `vec*`, `point**` (the prefix form still parses), braces are on the same line across the sources, and there is a webassembly build of the microcore with a browser page. the rest of the sweep is in `history/44-check-mode`. diff --git a/editors/vscode/.vscodeignore b/editors/vscode/.vscodeignore new file mode 100644 index 0000000..f784523 --- /dev/null +++ b/editors/vscode/.vscodeignore @@ -0,0 +1 @@ +# everything in this folder ships: the client, the server image, the grammar and the readme diff --git a/editors/vscode/README.md b/editors/vscode/README.md new file mode 100644 index 0000000..d60194e --- /dev/null +++ b/editors/vscode/README.md @@ -0,0 +1,30 @@ +# zc for vs code + +this extension connects vscode to the zc language server. the server runs the compiler in check mode, so diagnostics and semantic highlighting reflect the language the compiler actually parses, including syntax introduced with parse words. + +features include compiler diagnostics, optimization hints, semantic highlighting, hover documentation and immediate textmate highlighting while the server starts. + +## install + +there is no build step for local installation. package the extension with `bash src/tools/mkvsix.sh`, then choose “extensions: install from vsix” in vscode. the package includes a webassembly server; if that file is unavailable, the extension can run the server in the tinyvm javascript vm, which is slower. a native server can be selected with `zc.lsp.serverPath`. + +## settings + +```json +{ + "zc.lsp.serverPath": "", + "zc.trace.server": "messages" +} +``` + +an empty server path uses the bundled server. if the server exits, the extension restarts it and resends open documents. + +## diagnostics cache + +diagnostics are cached per document using a sha256 key that includes the server build, document path and text, and files read through `include`. included files are resolved from the workspace folder and from the including file's directory. saving an included file triggers checks for open documents that use it. cached diagnostics are restored from extension storage when vscode starts; workspaces with more than 300 files are not cached. + +## limitations + +the bundled webassembly server cannot read included files from disk. use a native server with `zc.lsp.serverPath` when diagnostics need filesystem access for includes. hover does not follow declarations reached through `include` yet. + +see `tests/drivers/lsp_test.js` for protocol tests and `src/tools/mkvsix.sh` for packaging. diff --git a/editors/vscode/client/diagcache.js b/editors/vscode/client/diagcache.js new file mode 100644 index 0000000..6e77207 --- /dev/null +++ b/editors/vscode/client/diagcache.js @@ -0,0 +1,137 @@ +// the diagnostics cache. a document's diagnostics are kept under a sha256 key +// that covers the server build, the document's path and text, and every file +// the document reads through include, transitively. the same key means the same +// tree, so the diagnostics kept for it are the ones the server would give, and +// the client can show them without asking the server again. +// +// nothing here uses vscode, so the rules can be tested with plain node +// (tests/drivers/diagcache_test.js). + +const crypto = require('crypto'); +const fs = require('fs'); +const path = require('path'); + +const MAX_FILES = 300; // a tree larger than this is not cached: its key would not cover all of it +const MAX_ENTRIES = 500; // tree states kept in the cache; the least recently used goes first + +function sha256(data) { + return crypto.createHash('sha256').update(data).digest('hex'); +} + +// the sha256 of a file's bytes, or 'missing' when there is no such file +function fileHash(p) { + try { + return sha256(fs.readFileSync(p)); + } catch (e) { + return 'missing'; + } +} + +// a short stamp for the server build: the sha256 of its file, or 'none' +function fileStamp(p) { + if (!p) return 'none'; + const h = fileHash(p); + return h === 'missing' ? 'none' : h; +} + +// the names a source file includes, as written in it +function includeNames(text) { + const names = []; + const tokens = /\/\/[^\n]*|\/\*[\s\S]*?\*\/|"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|[a-zA-Z_][a-zA-Z_0-9]*/g; + let include = false, m; + while ((m = tokens.exec(text)) !== null) { + const token = m[0]; + if (token.startsWith('//') || token.startsWith('/*')) continue; + if (include && token.startsWith('"')) { + names.push(token.slice(1, -1).replace(/\\([\\"nrt0])/g, (_, c) => ({ n: '\n', r: '\r', t: '\t', 0: '\0' })[c] || c)); + } + include = token === 'include'; + } + return names; +} + +// every file the document reads, transitively, as path -> sha256 (or 'missing'). +// an include is tried relative to the workspace folder and to the directory of +// the file that has it, since the compiler opens it relative to the working +// directory. both candidates are listed, existing or not, so creating one +// later changes the key. complete is false when the tree is too large to cover. +function sourceTree(text, docPath, workspace) { + const files = new Map(); + const queue = [{ text, dir: path.dirname(docPath) }]; + for (let index = 0; index < queue.length; index++) { + const cur = queue[index]; + for (const name of includeNames(cur.text)) { + const bases = new Set([workspace, cur.dir].filter(Boolean)); + for (const base of bases) { + const p = path.resolve(base, name); + if (files.has(p)) continue; + if (files.size >= MAX_FILES) return { files, complete: false }; + let body = null; + try { body = fs.readFileSync(p); } catch (e) { body = null; } + if (body === null) { files.set(p, 'missing'); continue; } + files.set(p, sha256(body)); + queue.push({ text: body.toString('utf8'), dir: path.dirname(p) }); + } + } + } + return { files, complete: true }; +} + +// the key for a document and the files it reads. key is null when the tree is +// too large to cover, and then nothing is cached. deps lists every file the +// tree names (existing or not), so a change to any of them can be noticed. +function diagKey(stamp, text, docPath, workspace) { + const tree = sourceTree(text, docPath, workspace); + const deps = [...tree.files.keys()].sort(); + if (!tree.complete) return { key: null, deps }; + const parts = [stamp, docPath, sha256(text)]; + for (const p of deps) parts.push(p + '=' + tree.files.get(p)); + return { key: sha256(parts.join('\n')), deps }; +} + +// the cache itself: key -> diagnostics, least recently used first. saved is +// what a previous session stored (plain JSON), so entries carry over; a key +// from another server build never matches. past max entries the least recently +// used one is dropped. +class DiagCache { + constructor(saved, max) { + this.max = max || MAX_ENTRIES; + this.map = new Map(); + if (saved && typeof saved === 'object') { + for (const key of Object.keys(saved)) { + if (Array.isArray(saved[key])) this.map.set(key, saved[key]); + } + } + while (this.map.size > this.max) this.map.delete(this.map.keys().next().value); + } + + // the diagnostics kept under key, or null; using them makes them the newest + get(key) { + if (key === null || key === undefined || !this.map.has(key)) return null; + const diags = this.map.get(key); + this.map.delete(key); + this.map.set(key, diags); + return diags; + } + + // keeps diags under key; returns false when there is no key to keep them under + put(key, diags) { + if (key === null || key === undefined) return false; + this.map.delete(key); + this.map.set(key, diags); + while (this.map.size > this.max) this.map.delete(this.map.keys().next().value); + return true; + } + + size() { + return this.map.size; + } + + toJSON() { + const out = {}; + for (const [key, diags] of this.map) out[key] = diags; + return out; + } +} + +module.exports = { sha256, fileHash, fileStamp, includeNames, sourceTree, diagKey, DiagCache, MAX_FILES }; diff --git a/editors/vscode/client/extension.js b/editors/vscode/client/extension.js new file mode 100644 index 0000000..c51b12e --- /dev/null +++ b/editors/vscode/client/extension.js @@ -0,0 +1,537 @@ +// vs code client for the zc language server. the server is the real compiler +// running in check mode, so diagnostics, hints and highlighting follow what the +// compiler does, including syntax a program adds to itself with parse words. +// +// everything the client does is written to the output channel "ZC Language +// Server" (View > Output, then pick it from the list). zc.trace.server picks how +// much: "off" logs only problems, "messages" (the default) logs each request and +// response with its time, and "verbose" also logs the bodies. the command +// "zc: show language server log" opens the channel and "zc: restart language +// server" starts the server again. + +const vscode = require('vscode'); +const cp = require('child_process'); +const path = require('path'); +const fs = require('fs'); +const { vmProcess } = require('./vmproc'); +const { wasmProcess } = require('./wasmproc'); +const { DiagCache, diagKey, fileStamp, sha256 } = require('./diagcache'); +const { hoverContents } = require('./hover'); + +const CHANNEL = 'ZC Language Server'; +const SLOW_MS = 2000; // a request that takes longer than this is logged as slow +const INIT_WAIT_MS = 15000; // no answer to initialize by then is logged as a stall +const CACHE_STATE = 'zc.diagnosticCache'; // the globalState entry the diagnostics cache is saved in + +let out = null; +let trace = 'messages'; +let proc = null; +let generation = 0; // each spawned server has its own; a stale exit is ignored +let stopping = false; // set while the server is being shut down on purpose +let ready = false; +let diagnostics = null; +let nextId = 1; +let nextDocVersion = 1; // unique across close and reopen, even when the editor resets its version +let restarts = 0; +const pending = new Map(); +// the diagnostics cache (client/diagcache.js). the server is asked only when a +// document's key is not in the cache, and the state below says what it holds +let store = null; // the extension's globalState, where the cache is saved +let cache = new DiagCache(null); +let serverStamp = 'none'; // identifies the server build; it is part of every key +const docKeys = new Map(); // uri -> key of the document as it is now (null: not cacheable) +const docDeps = new Map(); // uri -> the files the document reads, for checking it again when one changes +const serverKeys = new Map(); // uri -> key of the text the server holds +const sentKeys = new Map(); // uri -> keys sent to the server whose diagnostics have not come back +const docVersions = new Map(); // uri -> the last revision sent, including uncacheable documents +const opened = new Set(); // uris the server has open +let saveTimer = null; +let initTimer = null; +let inbox = Buffer.alloc(0); +let stats = { requests: 0, tokens: 0, hovers: 0, diagnostics: 0, errors: 0, cacheHits: 0, cacheMisses: 0 }; +const changed = new vscode.EventEmitter(); + +// the server's legend, in its order +const types = ['keyword', 'type', 'function', 'variable', 'string', 'number', 'operator', 'comment', 'parameter', 'property', 'macro', 'enumMember']; +const legend = new vscode.SemanticTokensLegend(types, []); + +function stamp() { + const d = new Date(); + return d.toTimeString().slice(0, 8) + '.' + String(d.getMilliseconds()).padStart(3, '0'); +} + +// writes one line to the channel. level is 'problem' (always shown), 'message' +// (the default) or 'verbose' +function log(level, text) { + if (!out) return; + const rank = { off: 0, messages: 1, verbose: 2 }; + const want = { problem: 0, message: 1, verbose: 2 }[level]; + if (want > (rank[trace] ?? 1) && level !== 'problem') return; + out.appendLine(stamp() + ' ' + (level === 'problem' ? 'problem ' : '') + text); +} + +function short(s, n) { + s = String(s); + return s.length > n ? s.slice(0, n) + '...(' + s.length + ' bytes)' : s; +} + +// the native server when zc.lsp.serverPath names one; otherwise null, and the +// server runs in the extension's own vm (server/ holds it and its image) +function findServer() { + const configured = vscode.workspace.getConfiguration('zc').get('lsp.serverPath'); + if (configured && fs.existsSync(configured)) return configured; + if (configured) { + log('problem', 'zc.lsp.serverPath does not exist: ' + configured + '; using the built-in server'); + vscode.window.showWarningMessage('zc: zc.lsp.serverPath does not exist; running the server in the extension instead'); + } + return null; +} + +function send(msg) { + if (!proc || !proc.stdin.writable) { + log('problem', 'dropped ' + (msg.method || 'a reply') + ': no running server'); + return; + } + const body = Buffer.from(JSON.stringify(msg), 'utf8'); + if (msg.method) { + if (trace === 'verbose') log('verbose', '-> ' + msg.method + ' ' + short(JSON.stringify(msg.params), 300)); + else log('message', '-> ' + msg.method + (msg.id !== undefined ? ' #' + msg.id : '')); + } + proc.stdin.write(Buffer.concat([Buffer.from('Content-Length: ' + body.length + '\r\n\r\n'), body])); +} + +function request(method, params) { + return new Promise((resolve, reject) => { + if (!proc || !proc.stdin.writable) { reject({ message: 'server unavailable' }); return; } + const id = nextId++; + const t0 = Date.now(); + stats.requests++; + pending.set(id, { resolve, reject, method, t0 }); + send({ jsonrpc: '2.0', id, method, params }); + }); +} + +function rejectPending(message) { + for (const p of pending.values()) p.reject({ message }); + pending.clear(); +} + +// lengths are in bytes, so the buffer stays bytes until a whole message is in +function onData(chunk) { + inbox = Buffer.concat([inbox, chunk]); + while (true) { + const end = inbox.indexOf('\r\n\r\n'); + if (end < 0) return; + const m = /Content-Length:\s*(\d+)/i.exec(inbox.slice(0, end).toString('ascii')); + if (!m) { + log('problem', 'server sent bytes without a Content-Length header: ' + short(inbox.slice(0, end).toString('utf8'), 200)); + inbox = inbox.slice(end + 4); + continue; + } + const start = end + 4, n = parseInt(m[1], 10); + if (inbox.length < start + n) return; + const body = inbox.slice(start, start + n).toString('utf8'); + inbox = inbox.slice(start + n); + try { + onMessage(JSON.parse(body)); + } catch (e) { + stats.errors++; + log('problem', 'bad message from server (' + e.message + '): ' + short(body, 300)); + } + } +} + +function onMessage(msg) { + if (msg.id !== undefined && pending.has(msg.id)) { + const p = pending.get(msg.id); + pending.delete(msg.id); + const ms = Date.now() - p.t0; + if (msg.error) { + stats.errors++; + log('problem', p.method + ' #' + msg.id + ' failed after ' + ms + ' ms: ' + short(JSON.stringify(msg.error), 300)); + p.reject(msg.error); + return; + } + if (ms > SLOW_MS) log('problem', p.method + ' #' + msg.id + ' was slow: ' + ms + ' ms'); + else log('message', '<- ' + p.method + ' #' + msg.id + ' in ' + ms + ' ms'); + if (trace === 'verbose') log('verbose', '<- result ' + short(JSON.stringify(msg.result), 300)); + p.resolve(msg.result); + return; + } + if (msg.method === 'textDocument/publishDiagnostics' && msg.params) { + onPublish(msg.params.uri, msg.params.diagnostics || [], msg.params.version); + return; + } + if (msg.method) log('message', '<- notification ' + msg.method + (msg.method.startsWith('window/') ? ' ' + short(JSON.stringify(msg.params), 200) : '')); +} + +const isZc = doc => doc.languageId === 'zc'; + +function workspaceDir() { + const f = vscode.workspace.workspaceFolders; + return f && f[0] ? f[0].uri.fsPath : null; +} + +function shortKey(key) { + return key ? key.slice(0, 12) : 'none'; +} + +// the server build the cache keys are for: the native server's file, or the +// built-in server's files +function serverStampFor(bin) { + if (bin) return fileStamp(bin); + const dir = path.join(__dirname, '..', 'server'); + return sha256(['lsp.wasm', 'lsp.json', 'tinyvm.js'].map(f => fileStamp(path.join(dir, f))).join(',')); +} + +// the key of a document as it is now, and what it reads (for checking it again when one of those changes) +function keyOf(doc) { + const uri = doc.uri.toString(); + const r = diagKey(serverStamp, doc.getText(), doc.uri.fsPath, workspaceDir()); + docKeys.set(uri, r.key); + docDeps.set(uri, r.deps); + return r.key; +} + +function showDiags(uri, raw) { + const list = raw.map(d => { + const r = d.range; + const sev = d.severity === 4 ? vscode.DiagnosticSeverity.Hint + : d.severity === 3 ? vscode.DiagnosticSeverity.Information + : d.severity === 2 ? vscode.DiagnosticSeverity.Warning + : vscode.DiagnosticSeverity.Error; + const diag = new vscode.Diagnostic(new vscode.Range(r.start.line, r.start.character, r.end.line, r.end.character), d.message, sev); + diag.source = d.source || 'zc'; + return diag; + }); + stats.diagnostics += list.length; + log('message', 'diagnostics for ' + uri + ': ' + list.length); + diagnostics.set(vscode.Uri.parse(uri), list); + changed.fire(); +} + +// the server's answer for a text it was sent. it is cached under the key that text had, +// and shown only when that is still the document's key +function onPublish(uri, raw, version) { + if (!opened.has(uri)) return; + const q = sentKeys.get(uri); + const index = q ? (version === undefined ? 0 : q.findIndex(entry => entry.version === version)) : -1; + if (!q || !q.length || index < 0) { + log('message', 'unrequested diagnostics for ' + uri + ': ignored'); + return; + } + const { key, version: sentVersion } = q.splice(index, 1)[0]; + if (key && cache.put(key, raw)) saveCacheSoon(); + if (docKeys.get(uri) === key && (key || docVersions.get(uri) === sentVersion)) showDiags(uri, raw); + else log('message', 'diagnostics for ' + uri + ' under an old key: not shown'); +} + +// shows the cached diagnostics for doc when its key is in the cache; true when it did +function showCached(doc) { + const uri = doc.uri.toString(); + const key = keyOf(doc); + const hit = cache.get(key); + if (!hit) return false; + stats.cacheHits++; + log('message', 'diagnostics for ' + uri + ' from the cache (' + shortKey(key) + ')'); + showDiags(uri, hit); + return true; +} + +// sends doc's text to the server: didOpen the first time, didChange after that +function sendDoc(doc, key) { + if (!proc || !proc.stdin.writable || !ready) return false; + const uri = doc.uri.toString(); + const q = sentKeys.get(uri) || []; + const version = nextDocVersion++; + docVersions.set(uri, version); + q.push({ key, version }); + sentKeys.set(uri, q); + serverKeys.set(uri, key); + if (!opened.has(uri)) { + opened.add(uri); + send({ jsonrpc: '2.0', method: 'textDocument/didOpen', params: { textDocument: { uri, languageId: 'zc', version, text: doc.getText() } } }); + } else { + send({ jsonrpc: '2.0', method: 'textDocument/didChange', params: { textDocument: { uri, version }, contentChanges: [{ text: doc.getText() }] } }); + } + return true; +} + +// brings doc's diagnostics up to date. a key in the cache answers without the server, which +// then only hears the text when something asks it about the document (see ensureServer). +// otherwise the server gets the text and its answer is cached under the key +function syncDoc(doc) { + if (!isZc(doc)) return; + const uri = doc.uri.toString(); + if (showCached(doc)) return; + const key = docKeys.get(uri); + if (key !== null && key !== undefined && serverKeys.get(uri) === key && opened.has(uri)) return; + if (!ready) { log('message', 'not sending ' + uri + ' yet: server not ready'); return; } + stats.cacheMisses++; + sendDoc(doc, key === undefined ? keyOf(doc) : key); +} + +// the server must hold doc's current text before a request about it (hover, tokens) +function ensureServer(doc) { + if (!isZc(doc) || !ready) return false; + const uri = doc.uri.toString(); + const key = keyOf(doc); + if (key !== null && serverKeys.get(uri) === key && opened.has(uri)) return true; + return sendDoc(doc, key); +} + +// a file another open document reads changed on disk: that document is checked again +function resyncDependents(fsPath) { + for (const doc of vscode.workspace.textDocuments) { + if (!isZc(doc) || doc.uri.fsPath === fsPath) continue; + const deps = docDeps.get(doc.uri.toString()); + if (deps && deps.includes(fsPath)) { + log('message', 'included file changed: checking ' + doc.uri.toString() + ' again'); + syncDoc(doc); + } + } +} + +// the cache is written after a short pause, so typing does not write it on every key +function saveCacheSoon() { + if (!store) return; + if (saveTimer) clearTimeout(saveTimer); + saveTimer = setTimeout(() => { + saveTimer = null; + try { + Promise.resolve(store.update(CACHE_STATE, cache.toJSON())).catch(e => log('problem', 'could not save the diagnostics cache: ' + e.message)); + } catch (e) { + log('problem', 'could not save the diagnostics cache: ' + e.message); + } + }, 500); +} + +// the server's answer to initialize is the first sign it works; without one the +// log says so, and the usual cause is a server that is still loading +// a native server that does not answer is stopped and the built-in server runs +// instead. a built-in server that does not answer is only logged +function watchInit(gen, native) { + if (initTimer) clearTimeout(initTimer); + initTimer = setTimeout(() => { + initTimer = null; + if (gen !== generation || ready || stopping || !proc) return; + if (native) { + log('problem', 'the native server at ' + zcServerPath() + ' did not answer initialize after ' + (INIT_WAIT_MS / 1000) + ' s; using the built-in server. clear zc.lsp.serverPath to stop seeing this'); + forceBuiltin = true; + generation++; + if (proc) { try { proc.kill(); } catch (e) { /* already gone */ } } + proc = null; + rejectPending('server replaced after initialize timeout'); + startServer(); + } else { + log('problem', 'no answer to initialize after ' + (INIT_WAIT_MS / 1000) + ' s; the server is still starting or has stalled'); + } + }, INIT_WAIT_MS); +} + +function zcServerPath() { + return vscode.workspace.getConfiguration('zc').get('lsp.serverPath') || ''; +} + +// forceBuiltin is set when a native server did not answer: the built-in one runs instead +let forceBuiltin = false; + +function startServer() { + const gen = ++generation; + stopping = false; + const bin = forceBuiltin ? null : findServer(); + ready = false; + inbox = Buffer.alloc(0); + // a new server holds no documents and has answered nothing yet + opened.clear(); + serverKeys.clear(); + sentKeys.clear(); + docVersions.clear(); + serverStamp = serverStampFor(bin); + const t0 = Date.now(); + try { + if (bin) { + log('message', 'starting the native server: ' + bin); + proc = cp.spawn(bin, [], { cwd: vscode.workspace.workspaceFolders ? vscode.workspace.workspaceFolders[0].uri.fsPath : undefined, stdio: ['pipe', 'pipe', 'pipe'] }); + } else { + const dir = path.join(__dirname, '..', 'server'); + const wasm = path.join(dir, 'lsp.wasm'); + // the wasm server is the fast one; the javascript vm is kept for a build without it + if (fs.existsSync(wasm)) { + log('message', 'starting the built-in server (wasm) from ' + wasm); + proc = wasmProcess(wasm, { log: (level, text) => log(level, 'wasm: ' + text) }); + } else { + log('message', 'starting the built-in server (javascript vm) from ' + dir); + proc = vmProcess(dir, { log: (level, text) => log(level, 'vm: ' + text) }); + } + log('message', 'built-in server started in ' + (Date.now() - t0) + ' ms'); + } + } catch (e) { + log('problem', 'could not start the server: ' + e.stack); + vscode.window.showErrorMessage('zc: could not start the language server: ' + e.message + ' (see the ZC Language Server output)'); + return; + } + proc.stdout.on('data', data => { if (gen === generation) onData(data); }); + proc.stderr.on('data', d => log('problem', 'server stderr: ' + short(d.toString(), 500))); + proc.on('error', err => { + if (gen !== generation) return; + ready = false; + rejectPending('server process error: ' + err.message); + log('problem', 'server process error: ' + err.stack); + vscode.window.showErrorMessage('zc: could not launch the language server: ' + err.message + ' (see the ZC Language Server output)'); + }); + proc.on('exit', (code, signal) => { + if (gen !== generation) return; + if (initTimer) { clearTimeout(initTimer); initTimer = null; } + if (stopping) { log('message', 'server stopped (code ' + code + ')'); ready = false; proc = null; rejectPending('server stopped'); return; } + log('problem', 'server exited with code ' + code + (signal ? ' signal ' + signal : '') + ' after ' + (Date.now() - t0) + ' ms'); + ready = false; + proc = null; + rejectPending('server exited'); + // a crash restarts the server, and the open documents are sent again once it is up + if (restarts < 5) { + restarts++; + log('problem', 'restarting the server (' + restarts + ' of 5)'); + setTimeout(() => { if (gen === generation) startServer(); }, 300); + } else { + log('problem', 'giving up after 5 restarts; use "zc: restart language server" to try again'); + } + }); + watchInit(gen, !!bin); + request('initialize', { processId: process.pid, capabilities: {} }).then(res => { + if (gen !== generation || stopping || !proc) return; + if (initTimer) { clearTimeout(initTimer); initTimer = null; } + const caps = res && res.capabilities ? Object.keys(res.capabilities) : []; + log('message', 'server ready: capabilities ' + caps.join(', ') + ' (initialized in ' + (Date.now() - t0) + ' ms)'); + send({ jsonrpc: '2.0', method: 'initialized', params: {} }); + ready = true; + const docs = vscode.workspace.textDocuments; + log('message', 'syncing ' + docs.filter(isZc).length + ' open zc document(s)'); + docs.forEach(syncDoc); + }).catch(err => log('problem', 'initialize failed: ' + short(JSON.stringify(err), 300))); +} + +function stopServer() { + if (initTimer) { clearTimeout(initTimer); initTimer = null; } + if (!proc) { rejectPending('server stopped'); return; } + const p = proc; + log('message', 'stopping the server'); + stopping = true; + ready = false; + rejectPending('server stopping'); + request('shutdown', null).catch(() => {}); + send({ jsonrpc: '2.0', method: 'exit' }); + setTimeout(() => { if (proc === p) { proc.kill(); proc = null; ready = false; rejectPending('server stopped'); } }, 500); +} + +function activate(context) { + out = vscode.window.createOutputChannel(CHANNEL); + context.subscriptions.push(out); + trace = vscode.workspace.getConfiguration('zc').get('trace.server') || 'messages'; + log('message', 'extension activated; trace level ' + trace); + + diagnostics = vscode.languages.createDiagnosticCollection('zc'); + context.subscriptions.push(diagnostics); + store = context.globalState; + cache = new DiagCache(store ? store.get(CACHE_STATE) : null); + // the stamp is taken without findServer, which would warn about a missing server path a second time + const configured = vscode.workspace.getConfiguration('zc').get('lsp.serverPath'); + serverStamp = serverStampFor(configured && fs.existsSync(configured) ? configured : null); + // the open documents whose diagnostics are cached show them before the server starts + vscode.workspace.textDocuments.forEach(d => { if (isZc(d)) showCached(d); }); + startServer(); + + // a .zc file changed on disk (saved elsewhere, checked out, deleted): the open documents that include it are checked again + const watcher = vscode.workspace.createFileSystemWatcher('**/*.zc'); + const recheck = uri => resyncDependents(uri.fsPath); + context.subscriptions.push(watcher, watcher.onDidChange(recheck), watcher.onDidCreate(recheck), watcher.onDidDelete(recheck)); + + context.subscriptions.push( + vscode.workspace.onDidChangeConfiguration(e => { + if (e.affectsConfiguration('zc.trace.server')) { + trace = vscode.workspace.getConfiguration('zc').get('trace.server') || 'messages'; + log('message', 'trace level is now ' + trace); + } + }), + vscode.workspace.onDidOpenTextDocument(syncDoc), + vscode.workspace.onDidChangeTextDocument(e => syncDoc(e.document)), + vscode.workspace.onDidSaveTextDocument(doc => { + if (!isZc(doc)) return; + syncDoc(doc); + resyncDependents(doc.uri.fsPath); + }), + vscode.workspace.onDidCloseTextDocument(doc => { + if (!isZc(doc)) return; + const uri = doc.uri.toString(); + diagnostics.delete(doc.uri); + docKeys.delete(uri); + docDeps.delete(uri); + serverKeys.delete(uri); + sentKeys.delete(uri); + docVersions.delete(uri); + if (opened.delete(uri) && ready) send({ jsonrpc: '2.0', method: 'textDocument/didClose', params: { textDocument: { uri } } }); + }), + vscode.commands.registerCommand('zc.showLog', () => out.show(true)), + vscode.commands.registerCommand('zc.restart', () => { + log('message', 'restarting the server by request'); + restarts = 0; + stopServer(); + setTimeout(startServer, 600); + }) + ); + + // highlighting: the server's tokens, on top of the grammar in syntaxes/ + const tokens = { + onDidChangeSemanticTokens: changed.event, + async provideDocumentSemanticTokens(doc) { + if (!isZc(doc)) return null; + if (!ready) { log('message', 'no semantic tokens for ' + doc.uri.toString() + ': server not ready'); return null; } + const t0 = Date.now(); + ensureServer(doc); + try { + const res = await request('textDocument/semanticTokens/full', { textDocument: { uri: doc.uri.toString() } }); + if (!res || !res.data) { log('message', 'semantic tokens: none for ' + doc.uri.toString()); return null; } + const builder = new vscode.SemanticTokensBuilder(legend); + let line = 0, col = 0, count = 0; + for (let i = 0; i < res.data.length; i += 5) { + line += res.data[i]; + col = res.data[i] === 0 ? col + res.data[i + 1] : res.data[i + 1]; + builder.push(line, col, res.data[i + 2], res.data[i + 3], res.data[i + 4]); + count++; + } + stats.tokens += count; + log('message', 'semantic tokens: ' + count + ' for ' + doc.uri.toString() + ' in ' + (Date.now() - t0) + ' ms'); + return builder.build(); + } catch (err) { + log('problem', 'semantic tokens failed for ' + doc.uri.toString() + ': ' + short(JSON.stringify(err), 300)); + return null; + } + } + }; + context.subscriptions.push(vscode.languages.registerDocumentSemanticTokensProvider({ language: 'zc' }, tokens, legend)); + + // hover: the docs the compiler stores for a name (docs in the image) + const hover = { + async provideHover(doc, position) { + if (!isZc(doc) || !ready) return null; + ensureServer(doc); + try { + const res = await request('textDocument/hover', { textDocument: { uri: doc.uri.toString() }, position: { line: position.line, character: position.character } }); + stats.hovers++; + const contents = hoverContents(vscode, res && res.contents); + log('message', 'hover at ' + position.line + ':' + position.character + ': ' + (contents.length ? 'text' : 'nothing')); + return contents.length ? new vscode.Hover(contents) : null; + } catch (err) { + return null; + } + } + }; + context.subscriptions.push(vscode.languages.registerHoverProvider({ language: 'zc' }, hover)); +} + +function deactivate() { + log('message', 'extension deactivated: ' + JSON.stringify(stats)); + stopServer(); +} + +module.exports = { activate, deactivate }; diff --git a/editors/vscode/client/hover.js b/editors/vscode/client/hover.js new file mode 100644 index 0000000..24181c5 --- /dev/null +++ b/editors/vscode/client/hover.js @@ -0,0 +1,21 @@ +// Convert protocol hover contents to VS Code MarkdownString objects. +// MarkupContent, legacy MarkedString, and arrays are all valid LSP responses. +function hoverContents(vscode, contents) { + const entries = Array.isArray(contents) ? contents : [contents]; + const result = []; + for (const entry of entries) { + if (!entry) continue; + const value = typeof entry === 'string' ? entry : entry.value; + if (typeof value !== 'string' || !value) continue; + const md = new vscode.MarkdownString(); + if (typeof entry === 'object' && entry.kind === 'plaintext') md.appendText(value); + else if (typeof entry === 'object' && typeof entry.language === 'string') md.appendCodeblock(value, entry.language); + else md.appendMarkdown(value); + md.supportHtml = false; + md.isTrusted = false; + result.push(md); + } + return result; +} + +module.exports = { hoverContents }; diff --git a/editors/vscode/client/vmproc.js b/editors/vscode/client/vmproc.js new file mode 100644 index 0000000..d4806e4 --- /dev/null +++ b/editors/vscode/client/vmproc.js @@ -0,0 +1,81 @@ +// runs the language server inside the extension, in the tinyvm javascript vm, +// so nothing has to be built or installed for the server to start. it presents +// the same surface the client uses on a child process: stdin to write, stdout +// and stderr to listen to, an exit event and kill(). +// +// the vm is streaming: the server runs until it wants input, the client's bytes +// are fed in and the server goes on from there. each step is synchronous, so a +// long check holds the extension host for as long as it takes. +// +// opts.log(level, text) gets what the vm does: the image it loads, each halt, +// and the messages the server writes to its error stream. +const path = require('path'); +const { EventEmitter } = require('events'); + +function vmProcess(dir, opts = {}) { + const log = opts.log || (() => {}); + const { TinyVM } = require(path.join(dir, 'tinyvm.js')); + const image = require(path.join(dir, 'lsp.json')); + const proc = new EventEmitter(); + const out = new EventEmitter(); + const err = new EventEmitter(); + const pending = []; + let dead = false; + let bytesIn = 0, bytesOut = 0, steps = 0; + + const vm = new TinyVM({ + streaming: true, + memoryWords: image.layout.HEAP_HI, // the initial heap; the machine grows on the heap's request + layout: image.layout, + onOutput: b => { pending.push(b); }, + onMessage: m => { + log('problem', 'server says: ' + String(m).slice(0, 500)); + err.emit('data', Buffer.from(m, 'utf8')); + }, + }); + vm.loadGenome(image.words, image.gorg); + log('message', 'loaded the image: ' + image.words.length + ' words, code at ' + image.gorg); + + const flush = () => { + if (pending.length) { + const chunk = Buffer.from(pending); + bytesOut += chunk.length; + pending.length = 0; + out.emit('data', chunk); + } + }; + const after = status => { + steps++; + flush(); + if (status !== 3 && !dead) { // it halted: the server has exited + dead = true; + log(status === 0 ? 'message' : 'problem', 'the vm halted with status ' + status + ' after ' + steps + ' steps, ' + bytesIn + ' bytes in, ' + bytesOut + ' out'); + proc.emit('exit', status); + } + }; + + proc.stdin = { + writable: true, + write(buf) { + if (dead) { log('problem', 'write to a halted vm dropped (' + buf.length + ' bytes)'); return false; } + bytesIn += buf.length; + vm.feed(buf); + after(vm.resume()); + return true; + }, + }; + proc.stdout = out; + proc.stderr = err; + proc.kill = () => { + if (dead) return; + dead = true; + log('message', 'vm killed after ' + steps + ' steps'); + proc.emit('exit', null); + }; + const t0 = Date.now(); + after(vm.run(image.gorg)); + log('message', 'vm ran to its first wait in ' + (Date.now() - t0) + ' ms'); + return proc; +} + +module.exports = { vmProcess }; diff --git a/editors/vscode/client/wasmproc.js b/editors/vscode/client/wasmproc.js new file mode 100644 index 0000000..0aa674a --- /dev/null +++ b/editors/vscode/client/wasmproc.js @@ -0,0 +1,96 @@ +// runs the language server as WebAssembly (server/lsp.wasm, built by +// src/tools/mklspwasm.sh): the microcore with the server's image inside. it presents +// the same surface as vmProcess: stdin to write, stdout and stderr to listen to, +// an exit event and kill(). it is much faster than the javascript vm, because +// the server's 64-bit words are native here and not bigints. +// +// the module is synchronous to load and to run, so the server starts in the +// extension host's own thread. a step runs until the server waits for input, +// then returns, the way vmProcess does. +// +// opts.log(level, text) gets what happens: the module loaded, each halt, and the +// messages the server writes to its error stream. +const fs = require('fs'); +const { EventEmitter } = require('events'); + +const CHUNK = 65536; // the most a single feed can carry (wasm_chunk_cap) + +function wasmProcess(file, opts = {}) { + const log = opts.log || (() => {}); + const t0 = Date.now(); + const bytes = fs.readFileSync(file); + const mod = new WebAssembly.Module(bytes); + log('message', 'compiled ' + file + ' (' + bytes.length + ' bytes) in ' + (Date.now() - t0) + ' ms'); + + const proc = new EventEmitter(); + const out = new EventEmitter(); + const err = new EventEmitter(); + let outBytes = [], msgBytes = []; + let dead = false, steps = 0, bytesIn = 0, bytesOut = 0; + + const inst = new WebAssembly.Instance(mod, { + env: { + js_putc: c => { outBytes.push(c & 255); }, + js_msg: c => { msgBytes.push(c & 255); }, + }, + }); + const x = inst.exports; + if (x.wasm_chunk_cap() < CHUNK) throw new Error('lsp.wasm has a smaller input chunk than this host expects'); + + const flush = () => { + if (outBytes.length) { + const chunk = Buffer.from(outBytes); + outBytes = []; + bytesOut += chunk.length; + out.emit('data', chunk); + } + if (msgBytes.length) { + const text = Buffer.from(msgBytes).toString('utf8'); + msgBytes = []; + log('problem', 'server says: ' + text.trim().slice(0, 500)); + err.emit('data', Buffer.from(text, 'utf8')); + } + }; + const after = status => { + steps++; + flush(); + if (status !== 3 && !dead) { // it halted: the server has exited + dead = true; + log(status === 0 ? 'message' : 'problem', 'the server halted with status ' + status + ' after ' + steps + ' steps, ' + bytesIn + ' bytes in, ' + bytesOut + ' out'); + proc.emit('exit', status); + } + }; + + proc.stdin = { + writable: true, + write(buf) { + if (dead) { log('problem', 'write to a halted server dropped (' + buf.length + ' bytes)'); return false; } + bytesIn += buf.length; + const chunkAt = x.wasm_chunk(); + let off = 0; + while (off < buf.length) { + const n = Math.min(CHUNK, buf.length - off); + new Uint8Array(x.memory.buffer).set(buf.subarray(off, off + n), chunkAt); + if (x.wasm_feed(n) !== 0) { log('problem', 'the server input is full; dropped ' + (buf.length - off) + ' bytes'); return false; } + off += n; + } + after(x.wasm_resume()); + return true; + }, + }; + proc.stdout = out; + proc.stderr = err; + proc.kill = () => { + if (dead) return; + dead = true; + log('message', 'server killed after ' + steps + ' steps'); + proc.emit('exit', null); + }; + + const t1 = Date.now(); + after(x.wasm_start()); + log('message', 'server ran to its first wait in ' + (Date.now() - t1) + ' ms'); + return proc; +} + +module.exports = { wasmProcess }; diff --git a/editors/vscode/language-configuration.json b/editors/vscode/language-configuration.json new file mode 100644 index 0000000..d7876c9 --- /dev/null +++ b/editors/vscode/language-configuration.json @@ -0,0 +1,30 @@ +{ + "comments": { + "lineComment": "//", + "blockComment": ["/*", "*/"] + }, + "brackets": [ + ["{", "}"], + ["[", "]"], + ["(", ")"] + ], + "autoClosingPairs": [ + { "open": "{", "close": "}" }, + { "open": "[", "close": "]" }, + { "open": "(", "close": ")" }, + { "open": "\"", "close": "\"", "notIn": ["string"] }, + { "open": "'", "close": "'", "notIn": ["string", "comment"] } + ], + "surroundingPairs": [ + ["{", "}"], + ["[", "]"], + ["(", ")"], + ["\"", "\""], + ["'", "'"] + ], + "wordPattern": "(-?\\d*\\.\\d\\w*)|([^\\`\\~\\!\\@\\#\\%\\^\\&\\*\\(\\)\\-\\=\\+\\[\\{\\]\\}\\\\\\|\\;\\:\\'\\\"\\,\\.\\<\\>\\/\\?\\s]+)", + "indentationRules": { + "increaseIndentPattern": "^.*(\\{[^}\"']*|\\([^)\"']*|\\[[^\\]\"']*)$", + "decreaseIndentPattern": "^\\s*(\\}|\\)|\\])" + } +} diff --git a/editors/vscode/package.json b/editors/vscode/package.json new file mode 100644 index 0000000..c3bb0d9 --- /dev/null +++ b/editors/vscode/package.json @@ -0,0 +1,127 @@ +{ + "name": "zc-language-support", + "displayName": "TinyVM ZC Language Support", + "description": "Language server for ZC in TinyVM: the compiler's own errors, optimization hints, hover docs and highlighting. runs in the extension, no install needed. see the ZC Language Server output for logs.", + "version": "3.3.0", + "publisher": "tinyvm", + "engines": { + "vscode": "^1.75.0" + }, + "categories": [ + "Programming Languages" + ], + "keywords": [ + "zc", + "tinyvm", + "zygote", + "compiler", + "lifetimes" + ], + "activationEvents": [ + "onLanguage:zc" + ], + "main": "./client/extension.js", + "contributes": { + "languages": [ + { + "id": "zc", + "aliases": [ + "ZC", + "zc", + "Zygote C" + ], + "extensions": [ + ".zc" + ], + "configuration": "./language-configuration.json" + } + ], + "grammars": [ + { + "language": "zc", + "scopeName": "source.zc", + "path": "./syntaxes/zc.tmLanguage.json" + } + ], + "semanticTokenScopes": [ + { + "language": "zc", + "scopes": { + "keyword": [ + "keyword.control.zc" + ], + "type": [ + "entity.name.type.zc", + "storage.type.zc" + ], + "function": [ + "entity.name.function.zc" + ], + "variable": [ + "variable.name.zc" + ], + "string": [ + "string.quoted.double.zc" + ], + "number": [ + "constant.numeric.zc" + ], + "operator": [ + "keyword.operator.zc" + ], + "comment": [ + "comment.line.double-slash.zc" + ], + "parameter": [ + "variable.parameter.zc" + ], + "property": [ + "variable.other.property.zc" + ], + "macro": [ + "entity.name.function.macro.zc" + ], + "enumMember": [ + "variable.other.enummember.zc" + ] + } + } + ], + "configuration": { + "type": "object", + "title": "ZC Language Server", + "properties": { + "zc.lsp.serverPath": { + "type": "string", + "default": "", + "description": "Optional. A native zclsp executable to use instead of the built-in server (build it with src/tools/mklsp.sh). Empty runs the server inside the extension." + }, + "zc.trace.server": { + "type": "string", + "enum": [ + "off", + "messages", + "verbose" + ], + "default": "messages", + "enumDescriptions": [ + "log only problems", + "log each request and response with its time (default)", + "also log the bodies of messages" + ], + "description": "How much the zc language server logs to the ZC Language Server output channel." + } + } + }, + "commands": [ + { + "command": "zc.showLog", + "title": "zc: show language server log" + }, + { + "command": "zc.restart", + "title": "zc: restart language server" + } + ] + } +} diff --git a/editors/vscode/syntaxes/zc.tmLanguage.json b/editors/vscode/syntaxes/zc.tmLanguage.json new file mode 100644 index 0000000..49f7b37 --- /dev/null +++ b/editors/vscode/syntaxes/zc.tmLanguage.json @@ -0,0 +1,175 @@ +{ + "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json", + "name": "ZC", + "scopeName": "source.zc", + "patterns": [ + { "include": "#comments" }, + { "include": "#strings" }, + { "include": "#numbers" }, + { "include": "#ownership" }, + { "include": "#keywords" }, + { "include": "#types" }, + { "include": "#builtins" }, + { "include": "#constants" }, + { "include": "#function-declarations" }, + { "include": "#function-calls" }, + { "include": "#operators" } + ], + "repository": { + "comments": { + "patterns": [ + { + "name": "comment.line.double-slash.zc", + "match": "//.*$" + }, + { + "name": "comment.block.zc", + "begin": "/\\*", + "end": "\\*/" + } + ] + }, + "strings": { + "patterns": [ + { + "name": "string.quoted.double.byte.zc", + "begin": "b\"", + "end": "\"", + "patterns": [{ "name": "constant.character.escape.zc", "match": "\\\\." }] + }, + { + "name": "string.quoted.double.zc", + "begin": "\"", + "end": "\"", + "patterns": [{ "name": "constant.character.escape.zc", "match": "\\\\." }] + }, + { + "name": "string.quoted.single.zc", + "begin": "'", + "end": "'", + "patterns": [{ "name": "constant.character.escape.zc", "match": "\\\\." }] + } + ] + }, + "numbers": { + "patterns": [ + { + "name": "constant.numeric.hex.zc", + "match": "\\b0[xX][0-9a-fA-F]+\\b" + }, + { + "name": "constant.numeric.float.zc", + "match": "\\b[0-9]+\\.[0-9]+([eE][+-]?[0-9]+)?\\b|\\b[0-9]+[eE][+-]?[0-9]+\\b" + }, + { + "name": "constant.numeric.decimal.zc", + "match": "\\b[0-9]+\\b" + } + ] + }, + "ownership": { + "patterns": [ + { + "name": "storage.modifier.lifetime.tag.zc", + "match": "\\.(u|m|d)\\b" + }, + { + "name": "keyword.operator.lifetime.recast.zc", + "match": "\\b(u|m|d)\\*(?=[a-zA-Z_])" + } + ] + }, + "keywords": { + "patterns": [ + { + "name": "keyword.control.zc", + "match": "\\b(if|else|while|do|for|switch|case|default|break|continue|return)\\b" + }, + { + "name": "keyword.declaration.zc", + "match": "\\b(fn|let|struct|type|enum|const|enhance|micro|vm)\\b" + }, + { + "name": "keyword.other.zc", + "match": "\\b(as|unchecked|asm|quote|sizeof|typeof|len|cap|push|pop|resize|o3|include|memdebug|fixed|typecheck)\\b" + }, + { + "name": "keyword.operator.memory.zc", + "match": "\\b(new|free)\\b" + } + ] + }, + "types": { + "patterns": [ + { + "name": "storage.type.primitive.zc", + "match": "\\b(int|char|word|bool|void|float)\\b" + } + ] + }, + "builtins": { + "patterns": [ + { + "name": "support.function.builtin.zc", + "match": "\\b(putc|getc|eof|halt|puts|putn|strlen|streq|wcopy|wfill|new_t|new_raw|new_oz|recycle|gc|isobj|osize|typeof_obj|live_objects|install|zdiv|zmod|zshl|mem_set|memreport|checkptr|syscall|readfile|bopen|bread|bwrite|bgetenv|ld8|st8|ld16|st16|ld32|st32|slen|sbyte|snew|sput|scat|sslice|bputs|ston|ntos|atoi|itoa|strcat|starts|env|now|writefile)\\b" + } + ] + }, + "constants": { + "patterns": [ + { + "name": "support.constant.builtin.zc", + "match": "\\b(MW|HEAP_LO|HEAP_HI|ZFP|GORG|FBOT|FTOP|RS0|DSLO|DSHI|SYSGATE|MAGIC|SYS_OPEN|SYS_CLOSE|SYS_READ|SYS_WRITE|SYS_GETENV|SYS_TIME)\\b" + } + ] + }, + "function-declarations": { + "patterns": [ + { + "match": "\\bfn\\s+([a-zA-Z_][a-zA-Z0-9_]*)\\s*\\(", + "captures": { + "1": { "name": "entity.name.function.zc" } + } + } + ] + }, + "function-calls": { + "patterns": [ + { + "match": "\\b([a-zA-Z_][a-zA-Z0-9_]*)\\s*\\(", + "captures": { + "1": { "name": "entity.name.function.call.zc" } + } + } + ] + }, + "operators": { + "patterns": [ + { + "name": "keyword.operator.comparison.zc", + "match": "==|!=|<=|>=|<|>" + }, + { + "name": "keyword.operator.logical.zc", + "match": "&&|\\|\\||!" + }, + { + "name": "keyword.operator.arithmetic.zc", + "match": "\\+|-|\\*|/|%|\\+\\+|--" + }, + { + "name": "keyword.operator.bitwise.zc", + "match": "&|\\||\\^|~|<<|>>" + }, + { + "name": "keyword.operator.assignment.zc", + "match": "=|\\+=|-=|\\*=|/=|%=|&=|\\|=|\\^=|<<=|>>=" + }, + { + "name": "keyword.operator.member.zc", + "match": "->|\\." + } + ] + } + } +} diff --git a/examples/bf.zc b/examples/bf.zc new file mode 100644 index 0000000..5169b2b --- /dev/null +++ b/examples/bf.zc @@ -0,0 +1,48 @@ +// a brainfuck compiler. it turns a brainfuck program into zc source, hands +// that to the compiler that's sitting in memory, and runs the result, which on +// arm64 means native code. runs of + - < > fold into one statement each. +// give it a program after a backtick, or it runs the hello world below. +let out[30000], n, depth; +fn emit(s) { let i = 0; while (s[i]) { out[n] = s[i]; n++; i++; } return 0; } +fn emitn(int v) { if (v >= 10) emitn(v / 10); out[n] = '0' + v % 10; n++; return 0; } +// how many times does c repeat from s[i] +int run(s, int i, int c) { int k = 0; while (s[i + k] == c) k++; return k; } + +fn translate(s) { + int i = 0, k; + n = 0; depth = 0; + emit("let t[30000], p = 0;\n"); + while (s[i]) { + switch (s[i]) { + case '+', '-': + k = run(s, i, s[i]); + emit("t[p] = (t[p] "); emit(s[i] == '+' ? "+ " : "- "); emitn(k); emit(") & 255;\n"); + i = i + k; continue; + case '>', '<': + k = run(s, i, s[i]); + emit("p "); emit(s[i] == '>' ? "+= " : "-= "); emitn(k); emit(";\n"); + i = i + k; continue; + case '[': emit("while (t[p]) {\n"); depth++; + case ']': emit("}\n"); depth--; + case '.': emit("put_c(t[p]);\n"); + case ',': emit("t[p] = eof() ? 0 : get_c() & 255;\n"); + } + i++; + } + emit("return 0;\n"); + out[n] = 0; + return depth; +} +let hello = "++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++."; +let prog[20000]; +fn main() { + let src = hello, k = 0, lines = 0, i = 0; + while (!eof() && k < 19999) { let c = get_c(); if (c != 10) { prog[k] = c; k++; } } + prog[k] = 0; + if (k) src = prog; + if (translate(src)) { put_s("unbalanced brackets\n"); return 1; } + while (out[i]) { if (out[i] == 10) lines++; i++; } + put_s("compiled to "); put_n(lines); put_s(" lines of zc, running it:\n"); + evals(out); + return 0; +} diff --git a/examples/cli.zc b/examples/cli.zc new file mode 100644 index 0000000..113a0da --- /dev/null +++ b/examples/cli.zc @@ -0,0 +1,12 @@ +// print the length and first character of the file named by ZC_FILE +include "src/lib/std.zc"; +fn main() { + let name[80], s, n; + n = Host.env("ZC_FILE", name, 80); + if (n < 0) { put_s("no file\n"); return 1; } + s = readfile(name); + if (!s) { put_s("can't read\n"); return 1; } + put_n(len_w(s)); put_c(' '); put_c(s[0]); put_c(10); + free(d*s); + return 0; +} diff --git a/examples/cli2.zc b/examples/cli2.zc new file mode 100644 index 0000000..0c3de12 --- /dev/null +++ b/examples/cli2.zc @@ -0,0 +1,29 @@ +// the command line, the environment, and capturing what a program writes. +// run it as: ./bin/zy examples/cli2.zc one two (the first is this file's path +// when zy is given one, otherwise , exactly as argv[0] is in c) +include "src/lib/std.zc"; + +fn main() { + put_s("argv: "); + for (int i = 0; i < argc; i++) { put_s(argv[i]); if (i + 1 < argc) put_s(" "); } + put_c(10); + + // envp holds NAME=value strings, so a program can walk all of them + let n = 0; + for (int i = 0; i < envc; i++) if (Text.starts(envp[i], "HOME=")) n = n + 1; + put_s("HOME is set: "); put_n(n > 0); put_c(10); + + // the same name the old way, one lookup instead of a walk + let v[80]; + put_n(Host.env("HOME", v, 80) > 0); put_c(10); + + // capture what the program writes, instead of printing it. every put_c, + // put_s and put_n from here on lands in the buffer. + let buf = capture.start(); + put_s("this goes into the buffer, not the terminal"); + put_c(10); + buf.end(); // stops the capture and terminates the string + put_s("captured "); put_n(len_w(buf)); put_s(" characters: "); + put_s(buf); + return 0; +} \ No newline at end of file diff --git a/examples/dump.zc b/examples/dump.zc new file mode 100644 index 0000000..df9a864 --- /dev/null +++ b/examples/dump.zc @@ -0,0 +1,30 @@ +// printing any struct with no printing code: the heap knows each object's +// type, and reflection walks its fields +struct person { char* name; int age; }; +struct item { char* name; int count; person* owner; }; + +fn dump(p) { + let t = typeof_obj(p), i = 0; + if (!t) { put_s("?"); return 0; } + tprint(t); put_s(" { "); + while (i < tnfields(t)) { + let ft = tftype(t, i), v = p[tfoff(t, i)]; + if (i) put_s(", "); + put_s(tfname(t, i)); put_s(": "); + if (tkind(ft) == TY_PTR && telem(ft) == typeof(char)) { put_c('"'); put_s(v); put_c('"'); } + else if (tkind(ft) == TY_PTR) { if (v) dump(v); else put_s("null"); } + else put_n(v); + i++; + } + put_s(" }"); + return 0; +} +fn main() { + person* ana = new(person); + ana.name = "ana"; ana.age = 30; + item* lamp = new(item); + lamp.name = "lamp"; lamp.count = 3; lamp.owner = ana; + dump(lamp); put_c(10); + free(d*lamp); + return 0; +} diff --git a/examples/echo.zc b/examples/echo.zc new file mode 100644 index 0000000..79eb877 --- /dev/null +++ b/examples/echo.zc @@ -0,0 +1,2 @@ +// everything after the backtick is left unread, for the program itself +fn main() { while (!eof()) put_c(get_c()); return 0; } diff --git a/examples/fact.zc b/examples/fact.zc new file mode 100644 index 0000000..1790900 --- /dev/null +++ b/examples/fact.zc @@ -0,0 +1,4 @@ +fn fact(n) { if (n < 2) return 1; return n * fact(n - 1); } +// prints in binary, like the old asm example +fn bin(n) { if (n > 1) bin(n >> 1); put_c('0' + (n & 1)); return 0; } +fn main() { bin(fact(10)); put_c(10); put_n(fact(10)); put_c(10); return 0; } diff --git a/examples/hello.zc b/examples/hello.zc new file mode 100644 index 0000000..c0a0a97 --- /dev/null +++ b/examples/hello.zc @@ -0,0 +1 @@ +fn main() { put_s("hello, world!\n"); return 0; } diff --git a/examples/lang.zc b/examples/lang.zc new file mode 100644 index 0000000..6e992b4 --- /dev/null +++ b/examples/lang.zc @@ -0,0 +1,49 @@ +// growing the language from inside a program, while it compiles + +// x |> f calls f(x). on a stack machine that's just "call": the value is +// already on the stack, under f's address. +immediate fn pipe_op() { next(); add_binop("|>", 1, "c"); return 0; } +pipe_op + +// unless (c) stmt +immediate fn unless() { + let a; + next(); expect(intern("(")); plainuse(expr()); rval(); expect(intern(")")); + emitseq(useq[0], ulen[0]); // not + a = hole(); op(O_JZ); + substmt(); + setlit(a, here); + return 0; +} +// assert (c): the failure message carries the line it was written on +fn assert_fail(int line) { put_s("assertion failed on line "); put_n(line); put_c(10); halt(); return 0; } +immediate fn assert() { + let a, l = tline; + next(); expect(intern("(")); plainuse(expr()); rval(); expect(intern(")")); + emitseq(useq[0], ulen[0]); // not: the jump below skips the failure when it holds + a = hole(); op(O_JZ); + lit(l); callname(intern("assert_fail")); op(O_DROP); + setlit(a, here); + return 0; +} +// times N { body }: the body pasted N times, at compile time +immediate fn times() { + let n, b, i = 0; + next(); n = tv; next(); + b = grab(); + while (i < n) { feed(qdup(b)); i++; } + free(d*b); + return 0; +} + +fn double(x) { return x * 2; } +fn inc(x) { return x + 1; } +fn main() { + put_n(5 |> double |> inc |> double); put_c(10); + unless (1 > 2) put_s("one isn't bigger than two\n"); + times 3 { put_c('*'); } + put_c(10); + assert(double(4) == 8); + assert(inc(1) == 3); + return 0; +} diff --git a/examples/learn/01-hello.out b/examples/learn/01-hello.out new file mode 100644 index 0000000..270c611 --- /dev/null +++ b/examples/learn/01-hello.out @@ -0,0 +1 @@ +hello, world! diff --git a/examples/learn/01-hello.zc b/examples/learn/01-hello.zc new file mode 100644 index 0000000..10cf4bf --- /dev/null +++ b/examples/learn/01-hello.zc @@ -0,0 +1,4 @@ +fn main() { + put_s("hello, world!\n"); + return 0; +} diff --git a/examples/learn/02-loops.out b/examples/learn/02-loops.out new file mode 100644 index 0000000..1e8b314 --- /dev/null +++ b/examples/learn/02-loops.out @@ -0,0 +1 @@ +6 diff --git a/examples/learn/02-loops.zc b/examples/learn/02-loops.zc new file mode 100644 index 0000000..832e32f --- /dev/null +++ b/examples/learn/02-loops.zc @@ -0,0 +1,6 @@ +fn main() { + let i = 0, n = 0; + while (i < 4) { n = n + i; i++; } + put_n(n); put_c(10); + return 0; +} diff --git a/examples/learn/03-do-continue.out b/examples/learn/03-do-continue.out new file mode 100644 index 0000000..b1bd38b --- /dev/null +++ b/examples/learn/03-do-continue.out @@ -0,0 +1 @@ +13 diff --git a/examples/learn/03-do-continue.zc b/examples/learn/03-do-continue.zc new file mode 100644 index 0000000..70959f4 --- /dev/null +++ b/examples/learn/03-do-continue.zc @@ -0,0 +1,6 @@ +fn main() { + int n = 0, i = 0; + do { i++; if (i == 2) continue; n = n + i; } while (i < 5); + put_n(n); put_c(10); + return 0; +} diff --git a/examples/learn/04-enums.out b/examples/learn/04-enums.out new file mode 100644 index 0000000..ead6e4a --- /dev/null +++ b/examples/learn/04-enums.out @@ -0,0 +1 @@ +0 5 6 diff --git a/examples/learn/04-enums.zc b/examples/learn/04-enums.zc new file mode 100644 index 0000000..8f80e3c --- /dev/null +++ b/examples/learn/04-enums.zc @@ -0,0 +1,5 @@ +enum color { RED, GREEN = 5, BLUE }; +fn main() { + put_n(RED); put_c(32); put_n(GREEN); put_c(32); put_n(BLUE); put_c(10); + return 0; +} diff --git a/examples/learn/05-small-ints.out b/examples/learn/05-small-ints.out new file mode 100644 index 0000000..2a9bc9e --- /dev/null +++ b/examples/learn/05-small-ints.out @@ -0,0 +1,3 @@ +4 +13 +44 diff --git a/examples/learn/05-small-ints.zc b/examples/learn/05-small-ints.zc new file mode 100644 index 0000000..b288181 --- /dev/null +++ b/examples/learn/05-small-ints.zc @@ -0,0 +1,9 @@ +fn main() { + u8 a = 250; a = a + 10; + put_n(a); put_c(10); + put_n(sizeof([100]u8)); put_c(10); + u8* px = new(u8, 4096); + px[10] = 300; + put_n(px[10]); put_c(10); + return 0; +} diff --git a/examples/learn/06-floats.out b/examples/learn/06-floats.out new file mode 100644 index 0000000..9406425 --- /dev/null +++ b/examples/learn/06-floats.out @@ -0,0 +1 @@ +4.000000 diff --git a/examples/learn/06-floats.zc b/examples/learn/06-floats.zc new file mode 100644 index 0000000..06d23fa --- /dev/null +++ b/examples/learn/06-floats.zc @@ -0,0 +1,6 @@ +fn put_f(x) { asm { x @ #1220 } return 0; } +fn main() { + let a = 1.5; + put_f(a + 2.5); put_c(10); + return 0; +} diff --git a/examples/learn/07-bytes.out b/examples/learn/07-bytes.out new file mode 100644 index 0000000..209c2ea --- /dev/null +++ b/examples/learn/07-bytes.out @@ -0,0 +1 @@ +17 8721 diff --git a/examples/learn/07-bytes.zc b/examples/learn/07-bytes.zc new file mode 100644 index 0000000..7d06282 --- /dev/null +++ b/examples/learn/07-bytes.zc @@ -0,0 +1,7 @@ +fn main() { + let w = 0; + st8(addr_b(&w), 0x11); + st8(addr_b(&w) + 1, 0x22); + put_n(ld8(addr_b(&w))); put_c(32); put_n(w); put_c(10); + return 0; +} diff --git a/examples/learn/08-byte-strings.out b/examples/learn/08-byte-strings.out new file mode 100644 index 0000000..1e10779 --- /dev/null +++ b/examples/learn/08-byte-strings.out @@ -0,0 +1,2 @@ +hello, zc +9 42 diff --git a/examples/learn/08-byte-strings.zc b/examples/learn/08-byte-strings.zc new file mode 100644 index 0000000..0959ceb --- /dev/null +++ b/examples/learn/08-byte-strings.zc @@ -0,0 +1,8 @@ +fn main() { + let name = b"zc"; + let hi = b"hello, " + name; + put_s(hi); put_c(10); + put_n(len(hi)); put_c(32); + put_n(s_to_n(b"40") + 2); put_c(10); + return 0; +} diff --git a/examples/learn/09-shared.out b/examples/learn/09-shared.out new file mode 100644 index 0000000..2fb73a0 --- /dev/null +++ b/examples/learn/09-shared.out @@ -0,0 +1 @@ +1 1 diff --git a/examples/learn/09-shared.zc b/examples/learn/09-shared.zc new file mode 100644 index 0000000..ab3ca06 --- /dev/null +++ b/examples/learn/09-shared.zc @@ -0,0 +1,8 @@ +fn main() { + let tail = cons(1, 2); + let left = cons(tail, 3); + let right = cons(tail, 4); + put_n(car(car(left))); put_c(32); + put_n(car(left) == car(right)); put_c(10); + return 0; +} diff --git a/examples/learn/10-owned.out b/examples/learn/10-owned.out new file mode 100644 index 0000000..7f8f011 --- /dev/null +++ b/examples/learn/10-owned.out @@ -0,0 +1 @@ +7 diff --git a/examples/learn/10-owned.zc b/examples/learn/10-owned.zc new file mode 100644 index 0000000..3aff9a4 --- /dev/null +++ b/examples/learn/10-owned.zc @@ -0,0 +1,6 @@ +fn main() { + int* p = new(int); + *p = 7; + put_n(*p); put_c(10); + return 0; +} diff --git a/examples/learn/11-tags.out b/examples/learn/11-tags.out new file mode 100644 index 0000000..b8626c4 --- /dev/null +++ b/examples/learn/11-tags.out @@ -0,0 +1 @@ +4 diff --git a/examples/learn/11-tags.zc b/examples/learn/11-tags.zc new file mode 100644 index 0000000..08edb58 --- /dev/null +++ b/examples/learn/11-tags.zc @@ -0,0 +1,6 @@ +int*.u box(int v) { int* b = new(int); *b = v; return u*b; } +fn main() { + int* p = box(4); + put_n(*p); put_c(10); + return 0; +} diff --git a/examples/learn/12-aliases.out b/examples/learn/12-aliases.out new file mode 100644 index 0000000..b8626c4 --- /dev/null +++ b/examples/learn/12-aliases.out @@ -0,0 +1 @@ +4 diff --git a/examples/learn/12-aliases.zc b/examples/learn/12-aliases.zc new file mode 100644 index 0000000..abb39d5 --- /dev/null +++ b/examples/learn/12-aliases.zc @@ -0,0 +1,8 @@ +struct node { int v; node* next; }; +fn main() { + node* a = new(node), b = new(node); + a.v = 3; b.v = 4; a.next = b; + node* c = a.next; + put_n(c.v); put_c(10); + return 0; +} diff --git a/examples/learn/13-memdebug.out b/examples/learn/13-memdebug.out new file mode 100644 index 0000000..c168a7b --- /dev/null +++ b/examples/learn/13-memdebug.out @@ -0,0 +1,2 @@ +leak: line 4 3 words +1 diff --git a/examples/learn/13-memdebug.zc b/examples/learn/13-memdebug.zc new file mode 100644 index 0000000..80ee0da --- /dev/null +++ b/examples/learn/13-memdebug.zc @@ -0,0 +1,10 @@ +memdebug on; +int* g; +fn main() { + int* p = new(int); + *p = 1; + g = d*p; + put_n(memreport()); + put_c(10); + return 0; +} diff --git a/examples/learn/14-include.out b/examples/learn/14-include.out new file mode 100644 index 0000000..1e8b314 --- /dev/null +++ b/examples/learn/14-include.out @@ -0,0 +1 @@ +6 diff --git a/examples/learn/14-include.zc b/examples/learn/14-include.zc new file mode 100644 index 0000000..c126389 --- /dev/null +++ b/examples/learn/14-include.zc @@ -0,0 +1,7 @@ +include "src/lib/std.zc"; +fn main() { + let s = readfile("tests/data/hi.txt"); + put_n(len_w(s)); put_c(10); + free(d*s); + return 0; +} diff --git a/examples/learn/15-generics.out b/examples/learn/15-generics.out new file mode 100644 index 0000000..ec63514 --- /dev/null +++ b/examples/learn/15-generics.out @@ -0,0 +1 @@ +9 diff --git a/examples/learn/15-generics.zc b/examples/learn/15-generics.zc new file mode 100644 index 0000000..dfc4833 --- /dev/null +++ b/examples/learn/15-generics.zc @@ -0,0 +1,9 @@ +struct record { K key; V val; }; +struct point { int x; int y; }; +K keyof(record* r) { return r.key; } +fn main() { + record* r = new(record); + r.key = 9; + put_n(keyof(r)); put_c(10); + return 0; +} diff --git a/examples/learn/16-tagged-enums.out b/examples/learn/16-tagged-enums.out new file mode 100644 index 0000000..0b2b75d --- /dev/null +++ b/examples/learn/16-tagged-enums.out @@ -0,0 +1,2 @@ +27 +10 diff --git a/examples/learn/16-tagged-enums.zc b/examples/learn/16-tagged-enums.zc new file mode 100644 index 0000000..ba430f4 --- /dev/null +++ b/examples/learn/16-tagged-enums.zc @@ -0,0 +1,18 @@ +enum shape { circle { int r; }, rect { int w; int h; }, dot }; +int area(shape* s) { + switch (s) { + case circle(r): return r * r * 3; + case rect(w, h): return w * h; + case dot: return 0; + } + return -1; +} +fn main() { + shape* s = shape.circle(3); + put_n(area(s)); put_c(10); + free(d*s); + shape* t = shape.rect(2, 5); + put_n(area(t)); put_c(10); + free(d*t); + return 0; +} diff --git a/examples/learn/17-switch.out b/examples/learn/17-switch.out new file mode 100644 index 0000000..f719efd --- /dev/null +++ b/examples/learn/17-switch.out @@ -0,0 +1 @@ +two diff --git a/examples/learn/17-switch.zc b/examples/learn/17-switch.zc new file mode 100644 index 0000000..f03aba9 --- /dev/null +++ b/examples/learn/17-switch.zc @@ -0,0 +1,6 @@ +fn main() { + int k = 2; + switch (k) { case 1: put_s("one"); break; case 2: put_s("two"); break; default: put_s("other"); } + put_c(10); + return 0; +} diff --git a/examples/learn/18-o3.out b/examples/learn/18-o3.out new file mode 100644 index 0000000..11c5ab3 --- /dev/null +++ b/examples/learn/18-o3.out @@ -0,0 +1,2 @@ +30 +143 diff --git a/examples/learn/18-o3.zc b/examples/learn/18-o3.zc new file mode 100644 index 0000000..cb2237a --- /dev/null +++ b/examples/learn/18-o3.zc @@ -0,0 +1,10 @@ +o3 int sum(fixed []int a) { int s = 0; for (int i = 0; i < len(a); i++) s += a[i]; return s; } +fn main() { + fixed []int a = new(fixed []int, 5); + for (int i = 0; i < 5; i++) a[i] = i * i; + put_n(sum(a)); put_c(10); + o3 int hits = 0; + o3 { for (int i = 0; i < 1000; i++) if (i % 7 == 3) hits++; } + put_n(hits); put_c(10); + return 0; +} diff --git a/examples/learn/19-growable.out b/examples/learn/19-growable.out new file mode 100644 index 0000000..4a9aeea --- /dev/null +++ b/examples/learn/19-growable.out @@ -0,0 +1,3 @@ +2 +5 +3 diff --git a/examples/learn/19-growable.zc b/examples/learn/19-growable.zc new file mode 100644 index 0000000..e5ed8de --- /dev/null +++ b/examples/learn/19-growable.zc @@ -0,0 +1,13 @@ +struct cnt { + int n; + static int make() { return 3; } + int get() { return this.n; } +}; +fn main() { + []int xs = new([]int); + push(xs, 4); push(xs, 5); + put_n(len(xs)); put_c(10); + put_n(pop(xs)); put_c(10); + put_n(cnt.make()); put_c(10); + return 0; +} diff --git a/examples/learn/20-text-num.out b/examples/learn/20-text-num.out new file mode 100644 index 0000000..7b3cce8 --- /dev/null +++ b/examples/learn/20-text-num.out @@ -0,0 +1,2 @@ +6 1024 7 +124 diff --git a/examples/learn/20-text-num.zc b/examples/learn/20-text-num.zc new file mode 100644 index 0000000..027b721 --- /dev/null +++ b/examples/learn/20-text-num.zc @@ -0,0 +1,6 @@ +include "src/lib/std.zc"; +fn main() { + put_n(Num.gcd(12, 18)); put_c(32); put_n(Num.pow(2, 10)); put_c(32); put_n(Num.isqrt(50)); put_c(10); + put_n(Text.atoi("123") + 1); put_c(10); + return 0; +} diff --git a/examples/learn/21-host.out b/examples/learn/21-host.out new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/examples/learn/21-host.out @@ -0,0 +1 @@ +1 diff --git a/examples/learn/21-host.zc b/examples/learn/21-host.zc new file mode 100644 index 0000000..550e747 --- /dev/null +++ b/examples/learn/21-host.zc @@ -0,0 +1,5 @@ +include "src/lib/std.zc"; +fn main() { + put_n(Host.now() > 0); put_c(10); + return 0; +} diff --git a/examples/learn/22-raw-x86.out b/examples/learn/22-raw-x86.out new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/examples/learn/22-raw-x86.out @@ -0,0 +1 @@ +1 diff --git a/examples/learn/22-raw-x86.zc b/examples/learn/22-raw-x86.zc new file mode 100644 index 0000000..e274aee --- /dev/null +++ b/examples/learn/22-raw-x86.zc @@ -0,0 +1,5 @@ +include "src/lib/std.zc"; +fn main() { + put_n(Host.raw(39, 0, 0, 0, 0, 0, 0) > 0); put_c(10); + return 0; +} diff --git a/examples/learn/23-c-abi.out b/examples/learn/23-c-abi.out new file mode 100644 index 0000000..0701ad5 --- /dev/null +++ b/examples/learn/23-c-abi.out @@ -0,0 +1,2 @@ +1.414214 +1 3 5 9 diff --git a/examples/learn/23-c-abi.zc b/examples/learn/23-c-abi.zc new file mode 100644 index 0000000..3df4b75 --- /dev/null +++ b/examples/learn/23-c-abi.zc @@ -0,0 +1,12 @@ +fn put_f(x) { asm { x @ #1220 } return 0; } +extern double sqrt(double); +extern void qsort(word *base, long n, long size, void *cmp); +int cmp(int* a, int* b) { return *a - *b; } +fn main() { + let a[4]; + a[0] = 5; a[1] = 3; a[2] = 9; a[3] = 1; + put_f(sqrt(2.0)); put_c(10); + qsort(a, 4, 8, cfunc(cmp)); + put_n(a[0]); put_c(32); put_n(a[1]); put_c(32); put_n(a[2]); put_c(32); put_n(a[3]); put_c(10); + return 0; +} diff --git a/examples/learn/24-backtick.out b/examples/learn/24-backtick.out new file mode 100644 index 0000000..81bf396 --- /dev/null +++ b/examples/learn/24-backtick.out @@ -0,0 +1 @@ +ab diff --git a/examples/learn/24-backtick.zc b/examples/learn/24-backtick.zc new file mode 100644 index 0000000..0317631 --- /dev/null +++ b/examples/learn/24-backtick.zc @@ -0,0 +1,5 @@ +fn main() { + put_c(get_c()); put_c(get_c()); put_c(10); + return 0; +} +`abc diff --git a/examples/learn/25-closures.out b/examples/learn/25-closures.out new file mode 100644 index 0000000..b04b592 --- /dev/null +++ b/examples/learn/25-closures.out @@ -0,0 +1,2 @@ +11 +15 diff --git a/examples/learn/25-closures.zc b/examples/learn/25-closures.zc new file mode 100644 index 0000000..72c236d --- /dev/null +++ b/examples/learn/25-closures.zc @@ -0,0 +1,8 @@ +fn apply(f, x) { return f(x); } +fn main() { + let k = 10; + let add = fn(x) { return x + k; }; + put_n(add(1)); put_c(10); + put_n(apply(add, 5)); put_c(10); + return 0; +} diff --git a/examples/learn/26-closure-state.out b/examples/learn/26-closure-state.out new file mode 100644 index 0000000..33aea5d --- /dev/null +++ b/examples/learn/26-closure-state.out @@ -0,0 +1,3 @@ +109 +1 5 +0 diff --git a/examples/learn/26-closure-state.zc b/examples/learn/26-closure-state.zc new file mode 100644 index 0000000..f3ddf50 --- /dev/null +++ b/examples/learn/26-closure-state.zc @@ -0,0 +1,13 @@ +fn main() { + let base = 2; + let outer = fn(x) { + let inner = fn(y) { return y + base + x; }; + return inner(100); + }; + put_n(outer(7)); put_c(10); + let count = 0; + let tick = fn(d) { count = count + d; return count; }; + put_n(tick(1)); put_c(32); put_n(tick(4)); put_c(10); + put_n(count); put_c(10); + return 0; +} diff --git a/examples/life.zc b/examples/life.zc new file mode 100644 index 0000000..6ac34e0 --- /dev/null +++ b/examples/life.zc @@ -0,0 +1,35 @@ +// conway's life on a 10 x 20 torus, with typed 2d arrays in a struct. +// a glider walks diagonally and wraps around the edges. +const H = 10, W = 20; +struct board { [10][20]int c; }; + +int get(board* b, int y, int x) { return b.c[(y + H) % H][(x + W) % W]; } +fn step(board* from, board* to) { + for (int y = 0; y < H; y++) + for (int x = 0; x < W; x++) { + int n = 0; + for (int dy = 0 - 1; dy <= 1; dy++) + for (int dx = 0 - 1; dx <= 1; dx++) + if (dy || dx) n += get(from, y + dy, x + dx); + to.c[y][x] = n == 3 || (n == 2 && from.c[y][x]); + } + return 0; +} +fn show(board* b, int gen) { + put_s("generation "); put_n(gen); put_c(10); + for (int y = 0; y < H; y++) { for (int x = 0; x < W; x++) put_c(b.c[y][x] ? '#' : '.'); put_c(10); } + return 0; +} +fn main() { + board* a = new(board), b = new(board); + a.c[0][1] = 1; a.c[1][2] = 1; a.c[2][0] = 1; a.c[2][1] = 1; a.c[2][2] = 1; + // two generations a pass, a -> b -> a: owned boards can't be swapped + // through a temporary, since the analyzer can't follow that + for (int g = 0; g <= 40; g += 2) { + if (g % 20 == 0) show(a, g); + step(a, b); + step(b, a); + } + free(d*a); free(d*b); + return 0; +} diff --git a/examples/lists.zc b/examples/lists.zc new file mode 100644 index 0000000..02059f3 --- /dev/null +++ b/examples/lists.zc @@ -0,0 +1,32 @@ +// ownership in practice: a list owns its boxes. nothing here calls free; +// everything is released at the end of the block that owns it. +.u box(v) { let b = new(1); b[0] = v; return u*b; } +.u mklist(cap) { let l = new(cap + 1); l[0] = 0; return u*l; } +// item m*arrives (it was given down), and goes d*on into the borrowed list +fn push(.m l, .d item) { l[l[0] + 1] = d*item; l[0] = l[0] + 1; return 0; } +fn show(.m l) { + let i = 1; + put_c('['); + while (i <= l[0]) { if (i > 1) put_c(' '); put_n(l[i][0]); i++; } + put_c(']'); + return 0; +} +.u squares(n) { + let l = mklist(n), i = 0; + while (i < n) { + let b = box(i * i); + push(l, d*b); + i++; + } + return u*l; +} +fn main() { + let before = live_objects(); + { + let l = squares(6); + show(l); put_c(10); + put_s("live: "); put_n(live_objects() - before); put_c(10); + } + put_s("after the block: "); put_n(live_objects() - before); put_c(10); + return 0; +} diff --git a/examples/mandel.zc b/examples/mandel.zc new file mode 100644 index 0000000..0a8f69e --- /dev/null +++ b/examples/mandel.zc @@ -0,0 +1,32 @@ +// the mandelbrot set in 16.16 fixed point. a word is 64 bits, so the product +// fits and the point sits 16 bits down. the jit inlines it as a multiply and a shift. +enhance fmul(a, b) micro "a = mul a b; a = sar a 16"; + +const ONE = 65536, W = 72, H = 28, MAXIT = 40; +let shades = " .,:;-=+*%#@"; + +int escape(int cx, int cy) { + int x = 0, y = 0, i = 0; + while (i < MAXIT) { + int xx = fmul(x, x), yy = fmul(y, y); + if (xx + yy > 4 * ONE) return i; + y = 2 * fmul(x, y) + cy; + x = xx - yy + cx; + i++; + } + return MAXIT; +} +fn main() { + int row = 0; + while (row < H) { + int col = 0, cy = (row * 2400 / (H - 1) - 1200) * ONE / 1000; + while (col < W) { + int cx = (col * 3200 / (W - 1) - 2200) * ONE / 1000, it = escape(cx, cy); + put_c(it == MAXIT ? '@' : shades[it * 11 / MAXIT]); + col++; + } + put_c(10); + row++; + } + return 0; +} diff --git a/examples/opt_test.zc b/examples/opt_test.zc new file mode 100644 index 0000000..c581283 --- /dev/null +++ b/examples/opt_test.zc @@ -0,0 +1,54 @@ +// a tour of the compiler's optimization hints. the program is valid and runs; +// the hints only show up in the editor (or the lsp cli), never in a normal build. +// each function below triggers one of them. + +fn sink(.d p) { free(d*p); return 0; } + +// a variable that is written and never read +fn dead() { + let unused = 5; + return 1; +} + +// never reassigned, so it could be a const +fn constant() { + let width = 40; + return width + 2; +} + +// multiplying by a power of two is a shift +fn shifty(n) { return n * 8; } + +// a number with no name +fn magic(n) { return n + 1234; } + +// new inside a loop allocates every pass +fn looping() { + let i = 0; + while (i < 3) { + let buf = new(10); + buf[0] = i; + sink(d*buf); + i++; + } + return i; +} + +// a small buffer that never leaves the function can live on the stack +fn scratch() { + let buf = new(8); + buf[0] = 7; + return buf[0]; +} + +// a consuming call takes the object after its allocation +fn handoff() { + let tmp = new(4); + sink(d*tmp); + return 0; +} + +fn main() { + put_n(dead() + constant() + shifty(2) + magic(0) + looping() + scratch() + handoff()); put_c(10); + return 0; +} diff --git a/examples/primes.zc b/examples/primes.zc new file mode 100644 index 0000000..43ae2e8 --- /dev/null +++ b/examples/primes.zc @@ -0,0 +1,35 @@ +// primes below 60000: an o3 sieve over a fixed array, the primes +// gathered into a growable one, then a few questions asked of them. +// with ZYTRACE=1 the o3 lines show each region being lifted and compiled. + +// marks the composites; f[i] ends up 1 for every prime i +o3 int sieve(fixed []int f) { + int n = len(f), c = 0; + for (int i = 2; i < n; i++) f[i] = 1; + for (int i = 2; i * i < n; i++) + if (f[i]) for (int j = i * i; j < n; j += i) f[j] = 0; + for (int i = 2; i < n; i++) c += f[i]; + return c; +} +// the largest gap between neighbours, and where it starts +o3 int gap(fixed []int f, int* at) { + int n = len(f), last = 2, best = 0, where = 0; + for (int i = 3; i < n; i++) + if (f[i]) { if (i - last > best) { best = i - last; where = last; } last = i; } + *at = where; + return best; +} +fn main() { + fixed []int f = new(fixed []int, 60000); + []int ps = new([]int); + int c = sieve(f), at = 0, twins = 0; + for (int i = 2; i < len(f); i++) if (f[i]) push(ps, i); + o3 { for (int k = 1; k < len(ps); k++) if (ps[k] - ps[k - 1] == 2) twins++; } + put_s("primes below 60000: "); put_n(c); put_c(10); + put_s("the last five:"); for (int k = len(ps) - 5; k < len(ps); k++) { put_c(' '); put_n(ps[k]); } put_c(10); + put_s("twin pairs: "); put_n(twins); put_c(10); + int g = gap(f, &at); + put_s("widest gap: "); put_n(g); put_s(", after "); put_n(at); put_c(10); + free(d*f); free(d*ps); + return 0; +} diff --git a/examples/quine.zc b/examples/quine.zc new file mode 100644 index 0000000..9c69e69 --- /dev/null +++ b/examples/quine.zc @@ -0,0 +1 @@ +let s="let s=#;fn main(){let i=0;while(s[i]){if(s[i]==35){put_c(34);put_s(s);put_c(34);}else put_c(s[i]);i++;}put_c(10);return 0;}";fn main(){let i=0;while(s[i]){if(s[i]==35){put_c(34);put_s(s);put_c(34);}else put_c(s[i]);i++;}put_c(10);return 0;} diff --git a/examples/repl.zc b/examples/repl.zc new file mode 100644 index 0000000..235063f --- /dev/null +++ b/examples/repl.zc @@ -0,0 +1,25 @@ +// a repl on the resident compiler: each backtick-separated chunk of input is +// compiled as a function body and run. definitions (fn ...) can be loaded too: +// a chunk that starts with "fn" goes through load instead of eval. +let buf[4000]; +fn readchunk() { + let n = 0, c; + while (!eof()) { + c = get_c(); + if (c == 96) break; + if (n < 3999) { buf[n] = c; n++; } + } + buf[n] = 0; + return n; +} +fn main() { + let q, i; + while (!eof()) { + readchunk(); + q = tokens(buf); + if (q[0] && q[1] == T_ID && q[2] == intern("fn")) load(q); + else eval(q); + free(d*q); + } + return 0; +} diff --git a/examples/selfpatch.zc b/examples/selfpatch.zc new file mode 100644 index 0000000..e3f821d --- /dev/null +++ b/examples/selfpatch.zc @@ -0,0 +1,16 @@ +// code is data: find the literal 'A' inside f's compiled words and rewrite it. +// on the jit the store lands on translated code, which gets dropped and +// retranslated on the next call. +fn f() { put_c('A'); return 0; } +fn litw(v) { return v + v + 1; } // how the vm encodes a literal push +fn main() { + let p = f; + f(); + while (*p != litw('A')) p++; + *p = litw('B'); + f(); + *p = litw('C'); + f(); + put_c(10); + return 0; +} diff --git a/examples/staged.zc b/examples/staged.zc new file mode 100644 index 0000000..a180ae3 --- /dev/null +++ b/examples/staged.zc @@ -0,0 +1,33 @@ +// staged code: a program writes a specialized function as source, compiles it +// while running, and calls it. here, polynomials with their coefficients baked +// in as constants, so there's no loop and no array left at runtime. +let buf[2000], n; +fn put(s) { let i = 0; while (s[i]) { buf[n] = s[i]; n++; i++; } return 0; } +fn putnum(int v) { if (v >= 10) putnum(v / 10); buf[n] = '0' + v % 10; n++; return 0; } + +// coefficients c[0] + c[1] x + ... in horner form: ((c3 x + c2) x + c1) x + c0 +fn specialize(int* c, int deg) { + n = 0; + put("fn poly(x) { return "); + for (int i = 0; i < deg; i++) put("("); + if (c[deg] < 0) { put("0 - "); putnum(0 - c[deg]); } else putnum(c[deg]); + for (int i = deg - 1; i >= 0; i--) { + put(") * x "); + if (c[i] < 0) { put("- "); putnum(0 - c[i]); } else { put("+ "); putnum(c[i]); } + } + put("; }"); + buf[n] = 0; + return buf; +} +fn main() { + [4]int c; + c[0] = 7; c[1] = 0 - 3; c[2] = 0; c[3] = 2; // 2x^3 - 3x + 7 + let src = specialize(c, 3), q = tokens(src); + put_s(src); put_c(10); + load(q); // compiled now, into the running program + free(d*q); + let f = sval[lookups("poly")]; // and found by name in the compiler's symbol table + for (int x = 0 - 2; x <= 3; x++) { put_n(f(x)); put_c(' '); } + put_c(10); + return 0; +} diff --git a/examples/strings.zc b/examples/strings.zc new file mode 100644 index 0000000..0959ceb --- /dev/null +++ b/examples/strings.zc @@ -0,0 +1,8 @@ +fn main() { + let name = b"zc"; + let hi = b"hello, " + name; + put_s(hi); put_c(10); + put_n(len(hi)); put_c(32); + put_n(s_to_n(b"40") + 2); put_c(10); + return 0; +} diff --git a/examples/struct_closure_opt.zc b/examples/struct_closure_opt.zc new file mode 100644 index 0000000..67315bf --- /dev/null +++ b/examples/struct_closure_opt.zc @@ -0,0 +1,47 @@ +// struct methods, closures and ownership in one file, written so the +// compiler's hints have something to say about it. it runs as it is. + +struct point { + let x, y; + + // methods get a hidden first parameter called this + fn dist(other) { + let dx = this.x - other.x; + let dy = this.y - other.y; + return dx * dx + dy * dy; + } +} + +fn sink(.d p) { free(d*p); return 0; } + +fn features() { + // a closure copies the variables it uses when it is made + let base = 100; + let add = fn(a, b) { return a + b + base; }; + let total = add(10, 20); + + let p1 = new(point); + p1.x = 5; p1.y = 12; + let p2 = new(point); + p2.x = 0; p2.y = 0; + + // made and given away on the next line, so it never needed a name + let tmp = new(2); + sink(d*tmp); + + // allocated again on every pass of the loop + let i = 0; + while (i < 3) { + let scratch = new(10); + scratch[0] = i; + sink(d*scratch); + i++; + } + + return total + p1.dist(p2); +} + +fn main() { + put_n(features()); put_c(10); + return 0; +} diff --git a/examples/tree.zc b/examples/tree.zc new file mode 100644 index 0000000..9ef515e --- /dev/null +++ b/examples/tree.zc @@ -0,0 +1,26 @@ +// a binary search tree where every node owns its children. there's no free +// anywhere: when the root's block ends, the whole tree goes with it. +struct node { int v; node* left, right; }; + +node*.u leaf(int v) { node* n = new(node); n.v = v; return u*n; } +// n is borrowed; storing a new leaf into it makes n the leaf's owner +fn insert(node*.m n, int v) { + if (v < n.v) { if (n.left) insert(n.left, v); else n.left = leaf(v); } + else { if (n.right) insert(n.right, v); else n.right = leaf(v); } + return 0; +} +fn walk(node* n) { if (!n) return 0; walk(n.left); put_n(n.v); put_c(' '); walk(n.right); return 0; } +int height(node* n) { if (!n) return 0; int l = height(n.left), r = height(n.right); return 1 + (l > r ? l : r); } + +fn main() { + let before = live_objects(); + { + node* root = leaf(50); + int x = 7; + for (int i = 0; i < 30; i++) { x = (x * 37 + 11) % 100; insert(root, x); } + walk(root); put_c(10); + put_s("height "); put_n(height(root)); put_s(", live objects "); put_n(live_objects() - before); put_c(10); + } + put_s("after the block: "); put_n(live_objects() - before); put_c(10); + return 0; +} diff --git a/examples/typed.zc b/examples/typed.zc new file mode 100644 index 0000000..44aff88 --- /dev/null +++ b/examples/typed.zc @@ -0,0 +1,51 @@ +// types: structs, a generic container, and looking at it all through reflection. +// a pointer type put_s its star after the type, like c: int*, point*, vec*. +typecheck on; +struct point { int x, y; }; +struct vec { int len, cap; T* items; }; + +vec*.u mkvec(int cap) { + vec* v = new(vec); + v.cap = cap; + v.items = new(T, cap); + return u*v; +} + +// x is given away (.d): the vector's memory holds it from now on, and the +// collector looks after it +bool push(vec* v, T.d x) { + if (v.len == v.cap) { free(d*x); return 0; } + v.items[v.len] = d*x; + v.len++; + return 1; +} + +fn show(word t) { + let i = 0; + tprint(t); put_s(" is "); put_n(tsize(t)); put_s(" words: "); + while (i < tnfields(t)) { + if (i) put_s(", "); + put_s(tfname(t, i)); put_s(" "); tprint(tftype(t, i)); + i++; + } + put_c(10); + return 0; +} + +int main() { + vec* v = mkvec(4); + int i = 0; + while (i < 3) { + point* p = new(point); + p.x = i; p.y = i * i; + push(v, d*p); + i++; + } + put_s("last: "); put_n(v.items[2].y); put_c(10); + show(typeof(point)); + show(typeof(*v)); + put_s("push is "); tprint(sigof(push)); put_c(10); + // each set of type arguments gets its own copy of mkvec, so what it stamped is vec + put_s("the heap says v is a "); tprint(typeof_obj(v)); put_s(", and v.items[0] is a "); tprint(typeof_obj(v.items[0])); put_c(10); + return 0; +} diff --git a/examples/wiring.zc b/examples/wiring.zc new file mode 100644 index 0000000..d10ecf5 --- /dev/null +++ b/examples/wiring.zc @@ -0,0 +1,18 @@ +// virtual enhancements: custom opcodes wired into the cpu while compiling. +// the recipes run through the core's optimizer; the jit inlines the result. +enhance popcount(x) micro + "r1 = shr x 1; r1 = and r1 0x5555555555555555; x = sub x r1" + "; r1 = shr x 2; r1 = and r1 0x3333333333333333; x = and x 0x3333333333333333; x = add x r1" + "; r1 = shr x 4; x = add x r1; x = and x 0x0f0f0f0f0f0f0f0f; x = mul x 0x0101010101010101; x = shr x 56"; +enhance div10(x) micro "x = udiv x 10"; // becomes a multiply-high, no divide +enhance hypot2(a, b) vm { 0 $ * ~ 0 $ * + }; // lifted from vm code +enhance 2 minmax(a, b) micro "r2 = min a b; r1 = max a b; r0 = r2"; +let lo, hi; +fn main() { + put_n(popcount(0xff00ff)); put_c(' '); + put_n(div10(12345)); put_c(' '); + put_n(hypot2(3, 4)); put_c(' '); + asm { 9 4 minmax hi ! lo ! } + put_n(lo); put_c('/'); put_n(hi); put_c(10); + return 0; +} \ No newline at end of file diff --git a/src/baremetal/link_aarch64.ld b/src/baremetal/link_aarch64.ld new file mode 100644 index 0000000..6740f21 --- /dev/null +++ b/src/baremetal/link_aarch64.ld @@ -0,0 +1,37 @@ +OUTPUT_ARCH(aarch64) +OUTPUT_FORMAT(elf64-littleaarch64) +ENTRY(_start) + +/* virt RAM starts at 0x40000000. The PL011 UART is at 0x09000000, outside this. */ +MEMORY +{ + RAM (rwx) : ORIGIN = 0x40000000, LENGTH = 48M +} + +SECTIONS +{ + .text : { + KEEP(*(.text.init)) + *(.text .text.*) + } > RAM + .rodata : { + *(.rodata .rodata.*) + } > RAM + .data : { + *(.data .data.*) + } > RAM + .bss : { + . = ALIGN(16); + _sbss = .; + *(.bss .bss.*) + *(COMMON) + . = ALIGN(16); + _ebss = .; + } > RAM + .stack : { + . = ALIGN(16); + . += 64K; + _stack_top = .; + } > RAM + /DISCARD/ : { *(.eh_frame*) *(.comment) *(.note*) } +} diff --git a/src/baremetal/link_riscv.ld b/src/baremetal/link_riscv.ld new file mode 100644 index 0000000..cd470a4 --- /dev/null +++ b/src/baremetal/link_riscv.ld @@ -0,0 +1,39 @@ +OUTPUT_ARCH(riscv) +OUTPUT_FORMAT(elf64-littleriscv) +ENTRY(_start) + +MEMORY +{ + RAM (rwx) : ORIGIN = 0x80000000, LENGTH = 48M +} + +SECTIONS +{ + .text : { + KEEP(*(.text.init)) + *(.text .text.*) + } > RAM + .rodata : { + *(.rodata .rodata.*) + *(.srodata .srodata.*) + } > RAM + .data : { + *(.data .data.*) + *(.sdata .sdata.*) + } > RAM + .bss : { + . = ALIGN(16); + _sbss = .; + *(.bss .bss.*) + *(.sbss .sbss.*) + *(COMMON) + . = ALIGN(16); + _ebss = .; + } > RAM + .stack : { + . = ALIGN(16); + . += 64K; + _stack_top = .; + } > RAM + /DISCARD/ : { *(.eh_frame*) *(.comment) *(.note*) *(.riscv.attributes) } +} diff --git a/src/baremetal/port.c b/src/baremetal/port.c new file mode 100644 index 0000000..636c1ea --- /dev/null +++ b/src/baremetal/port.c @@ -0,0 +1,339 @@ +/* UART port for the microcore. No libc. BARE_RISCV is the 16550 at + 0x10000000 on qemu's riscv virt. BARE_AARCH64 is the PL011 at 0x09000000 + on qemu's aarch64 virt. + + BARE_INPUT=0 (a program baked into the image, nothing typed): eof is + true and getc is -1. BARE_INPUT=1: bytes come from the UART. getc and + eof both wait until a byte is there. 0x04 (EOT) is end of input and is + not delivered. A gap is not the end, so a stream longer than the UART + FIFO still echoes until EOT. The compiler's backtick still ends a unit; + the byte after it is the program's first input, or EOT when the program + reads nothing. + + Syscalls, same numbers as the hosted ports: + 1 open 2 close 3 read 4 write a small RAM file store + 5 getenv BOARD is "riscv" or "aarch64" + 6 time the hardware counter, not unix seconds + 7 exit(status) + 8 random + 9 memory 0 micro_mem_words(), 1 HEAP_LO, 2 HEAP_HI, 3 micro_mem_words()*8 + 10 bopen 11 bread 12 bwrite 13 bgetenv + 10..13 use packed bytes. Anything else is -1. */ +#include "micro_port.h" +#include "bops.h" + +#ifndef BARE_INPUT +#define BARE_INPUT 0 +#endif + +#if defined(BARE_RISCV) +static volatile unsigned char *const uart = (volatile unsigned char *)0x10000000; +#define UART_LSR 5 +#define LSR_RX 0x01 +#define LSR_TX 0x20 + +static void uart_init(void) +{ + /* QEMU's 16550 already speaks 8N1. Rewriting LCR or FCR after + bytes have arrived drops the first one, and the source is + already in the FIFO by the time this runs. */ +} + +static int rx_ready(void) { return uart[UART_LSR] & LSR_RX; } +static void uart_write(unsigned char c) +{ + while ((uart[UART_LSR] & LSR_TX) == 0) ; + uart[0] = c; +} +static int uart_read(void) +{ + while (!rx_ready()) ; + return uart[0]; +} +static unsigned long timer(void) +{ + unsigned long t; + __asm__ volatile("rdtime %0" : "=r"(t)); + return t; +} +static void exit_status(int code) +{ + volatile unsigned *test = (volatile unsigned *)0x100000; + /* PASS exits 0. FAIL's high half is the status (16 bits). */ + if (code == 0) *test = 0x5555; + else *test = ((unsigned)(code & 0xffff) << 16) | 0x3333; + for (;;) ; +} +#elif defined(BARE_AARCH64) +static volatile unsigned *const uart = (volatile unsigned *)0x09000000; +#define UART_FR 6 /* 0x18 / 4 */ +#define FR_RXFE 0x10 +#define FR_TXFF 0x20 + +static void uart_init(void) { } +static int rx_ready(void) { return (uart[UART_FR] & FR_RXFE) == 0; } +static void uart_write(unsigned char c) +{ + while (uart[UART_FR] & FR_TXFF) ; + uart[0] = c; +} +static int uart_read(void) +{ + while (!rx_ready()) ; + return (int)uart[0]; +} +static unsigned long timer(void) +{ + unsigned long t; + __asm__ volatile("mrs %0, cntvct_el0" : "=r"(t)); + return t; +} +static void exit_status(int code) +{ + /* SYS_EXIT. x1 points at {ADP_Stopped_ApplicationExit, status}. + QEMU has to be started with -semihosting; PSCI SYSTEM_OFF cannot + carry a status. */ + unsigned long blk[2]; + register unsigned long x0 __asm__("x0") = 0x18; + register unsigned long x1 __asm__("x1"); + blk[0] = 0x20026ul; + blk[1] = (unsigned long)code; + x1 = (unsigned long)blk; + __asm__ volatile("hlt #0xf000" :: "r"(x0), "r"(x1) : "memory"); + for (;;) ; +} +#else +#error define BARE_RISCV or BARE_AARCH64 +#endif + +static int saw_end, have_peek, peekc; + +void micro_putc(int c) { uart_write((unsigned char)c); } + +void micro_msg(const char *s) +{ + while (*s) uart_write((unsigned char)*s++); +} + +/* the machine's memory on this board: the arena below, which is the board's RAM as + memmap.h sizes it. a board's RAM is a fixed amount of hardware, so this is the one + place the port has a size; it returns NULL past it. */ +/* physical backing is separate from the vm's configurable growth limit. */ +#ifndef MICRO_PORT_WORDS +#define MICRO_PORT_WORDS (HEAP_HI > PW ? HEAP_HI : PW) +#endif +static Cell arena[MICRO_PORT_WORDS]; + +Cell *micro_mem_alloc(UCell words) +{ + if (words > MICRO_PORT_WORDS) return 0; + return arena; +} + +static int inmem(Cell a) { return (UCell)a < (UCell)micro_mem_words(); } + +static int zcstr(Cell *mem, Cell addr, char *dst, int max) +{ + int i = 0, c; + if (max < 1) return -1; + if (!inmem(addr)) { dst[0] = 0; return -1; } + while (i < max - 1 && inmem(addr + i)) { + c = (int)mem[addr + i]; + if (!c) break; + dst[i++] = (char)(c & 255); + } + dst[i] = 0; + return i; +} + +static int streq(const char *a, const char *b) +{ + int i = 0; + while (a[i] && a[i] == b[i]) i++; + return a[i] == b[i]; +} + +#define NFILES 4 +#define FCAP 128 +#define NLEN 32 +static struct { + char name[NLEN]; + unsigned char data[FCAP]; + int len, pos; +} ramf[NFILES]; + +static int ram_slot(const char *name, int create) +{ + int i, free_i = -1, n; + for (i = 0; i < NFILES; i++) { + if (!ramf[i].name[0]) { if (free_i < 0) free_i = i; continue; } + if (streq(ramf[i].name, name)) return i; + } + if (!create || free_i < 0) return -1; + for (n = 0; name[n] && n < NLEN - 1; n++) ramf[free_i].name[n] = name[n]; + ramf[free_i].name[n] = 0; + ramf[free_i].len = 0; + ramf[free_i].pos = 0; + return free_i; +} + +static UCell rng; +static Cell rnd(void) +{ + if (!rng) rng = timer() ^ 0x9E3779B97F4A7C15ull; + if (!rng) rng = 0x9E3779B97F4A7C15ull; + rng ^= rng << 13; + rng ^= rng >> 7; + rng ^= rng << 17; + if ((Cell)rng == -1) rng ^= 0x100; + return (Cell)rng; +} + +static void board_name(char *dst) +{ +#if defined(BARE_RISCV) + dst[0] = 'r'; dst[1] = 'i'; dst[2] = 's'; dst[3] = 'c'; dst[4] = 'v'; dst[5] = 0; +#else + dst[0] = 'a'; dst[1] = 'a'; dst[2] = 'r'; dst[3] = 'c'; dst[4] = 'h'; + dst[5] = '6'; dst[6] = '4'; dst[7] = 0; +#endif +} + +Cell micro_syscall(Cell *mem, Cell op, Cell a, Cell b, Cell c) +{ + char path[NLEN]; + int fd, n, i; + switch ((int)op) { + case 1: + if (zcstr(mem, a, path, NLEN) < 0 || !path[0]) return -1; + fd = ram_slot(path, b == 1); + if (fd < 0) return -1; + if (b == 1) { ramf[fd].len = 0; ramf[fd].pos = 0; } + else ramf[fd].pos = 0; + return fd; + case 2: + if ((UCell)a >= NFILES || !ramf[a].name[0]) return -1; + ramf[a].pos = 0; + return 0; + case 3: + if ((UCell)a >= NFILES || !ramf[a].name[0] || c < 0 || !inmem(b)) return -1; + n = 0; + while (n < c && ramf[a].pos < ramf[a].len && inmem(b + n)) { + mem[b + n] = ramf[a].data[ramf[a].pos++]; + n++; + } + return n; + case 4: + if ((UCell)a >= NFILES || !ramf[a].name[0] || c < 0 || !inmem(b)) return -1; + n = 0; + while (n < c && ramf[a].len < FCAP && inmem(b + n)) { + ramf[a].data[ramf[a].len++] = (unsigned char)mem[b + n]; + n++; + } + ramf[a].pos = ramf[a].len; + return n; + case 5: { + char board[8], *e; + if (zcstr(mem, a, path, NLEN) < 0) return -1; + board_name(board); + e = streq(path, "BOARD") ? board : 0; + if (!e) return -1; + if (c < 1 || !inmem(b)) return -1; + i = 0; + while (e[i] && i < c - 1 && inmem(b + i)) { mem[b + i] = (unsigned char)e[i]; i++; } + if (inmem(b + i)) mem[b + i] = 0; + return i; + } + case 6: + return (Cell)timer(); + case 7: + exit_status((int)a); + return 0; + case 8: + return rnd(); + case 9: + if (a == 0) return micro_mem_words(); + if (a == 1) return HEAP_LO; + if (a == 2) return HEAP_HI; + if (a == 3) return (Cell)micro_mem_words() * 8; + return -1; + case 10: + if (bop_cstr(mem, a, path, NLEN, micro_mem_words()) < 0 || !path[0]) return -1; + fd = ram_slot(path, b == 1); + if (fd < 0) return -1; + if (b == 1) { ramf[fd].len = 0; ramf[fd].pos = 0; } + else ramf[fd].pos = 0; + return fd; + case 11: + if ((UCell)a >= NFILES || !ramf[a].name[0] || c < 0 || (c > 0 && !bop_ok(b, 1, micro_mem_words()))) return -1; + n = 0; + while (n < c && ramf[a].pos < ramf[a].len && bop_ok(b + n, 1, micro_mem_words())) { + bop_put1(mem, b + n, ramf[a].data[ramf[a].pos++]); + n++; + } + return n; + case 12: + if ((UCell)a >= NFILES || !ramf[a].name[0] || c < 0 || (c > 0 && !bop_ok(b, 1, micro_mem_words()))) return -1; + n = 0; + while (n < c && ramf[a].len < FCAP && bop_ok(b + n, 1, micro_mem_words())) { + ramf[a].data[ramf[a].len++] = (unsigned char)bop_load(mem, b + n, 1); + n++; + } + ramf[a].pos = ramf[a].len; + return n; + case 13: { + char board[8], *e; + if (bop_cstr(mem, a, path, NLEN, micro_mem_words()) < 0) return -1; + board_name(board); + e = streq(path, "BOARD") ? board : 0; + if (!e) return -1; + if (c < 1 || !bop_ok(b, 1, micro_mem_words())) return -1; + i = 0; + while (e[i] && i < c - 1 && bop_ok(b + i, 1, micro_mem_words())) { bop_put1(mem, b + i, (unsigned char)e[i]); i++; } + if (bop_ok(b + i, 1, micro_mem_words())) bop_put1(mem, b + i, 0); + return i; + } + case 21: /* argc: a board has no command line, so alone */ + return 1; + case 22: { /* argv: entry 0 is , as in zy(1) */ + static const char lead[] = ""; + if (a != 0) return -1; + if (!b) return 6; + if (c < 7 || !inmem(b) || !inmem(b + 6)) return -1; + for (i = 0; i < 6; i++) mem[b + i] = (unsigned char)lead[i]; + mem[b + 6] = 0; + return 6; + } + case 23: /* envc: a board has no environment */ + return 0; + default: + return -1; + } +} + +int micro_getc(void) +{ + int ch; + if (!BARE_INPUT || saw_end) return -1; + if (have_peek) { have_peek = 0; ch = peekc; } + else ch = uart_read(); + if (ch == 4) { saw_end = 1; return -1; } + return ch; +} + +int micro_eof(void) +{ + if (!BARE_INPUT || saw_end) return 1; + if (!have_peek) { + peekc = uart_read(); /* wait: an empty FIFO is not the end */ + have_peek = 1; + } + if (peekc == 4) { saw_end = 1; have_peek = 0; return 1; } + return 0; +} + +void bare_main(void) +{ + uart_init(); + exit_status(micro_run() != 0); +} diff --git a/src/baremetal/run.sh b/src/baremetal/run.sh new file mode 100755 index 0000000..d6ac333 --- /dev/null +++ b/src/baremetal/run.sh @@ -0,0 +1,104 @@ +#!/bin/bash +# Build a freestanding microcore and boot it on qemu-system-*. +# src/baremetal/run.sh ready riscv|aarch64 +# src/baremetal/run.sh riscv|aarch64 run examples/hello.zc +# src/baremetal/run.sh riscv|aarch64 io examples/echo.zc +# src/baremetal/run.sh riscv|aarch64 compile examples/hello.zc +# run: the program is compiled into the image. nothing is read. +# io: the same image, but stdin is the program's input and 0x04 ends it. +# compile: the default 1,048,576-word genome. the source, a backtick, and +# 0x04 are written to the UART. the backtick ends the unit; the +# 0x04 is what eof() waits for after that. +# Prints the program's UART output and nothing else. Exits 0 on a clean +# power-off. A missing toolchain exits 0 from `ready` and 1 otherwise. +set -euo pipefail +cd "$(dirname "$0")/../.." + +ready() { + case "$1" in + riscv) command -v riscv64-linux-gnu-gcc >/dev/null && command -v qemu-system-riscv64 >/dev/null ;; + aarch64) command -v aarch64-linux-gnu-gcc >/dev/null && command -v qemu-system-aarch64 >/dev/null ;; + *) return 1 ;; + esac +} + +if [ "${1:-}" = ready ]; then + ready "${2:-}" + exit $? +fi +if [ $# -ne 3 ]; then + echo "usage: src/baremetal/run.sh riscv|aarch64 run|io|compile FILE" >&2 + exit 2 +fi +target=$1 mode=$2 src=$3 +ready "$target" || { echo "baremetal: missing toolchain for $target" >&2; exit 1; } +[ -f "$src" ] || { echo "baremetal: no $src" >&2; exit 1; } + +case "$target" in +riscv) + cc=riscv64-linux-gnu-gcc + qemu=qemu-system-riscv64 + cflags="-march=rv64gc -mabi=lp64d -mcmodel=medany -mno-relax" + asm=src/baremetal/start_riscv.S + ld=src/baremetal/link_riscv.ld + def=-DBARE_RISCV + qargs=(-machine virt -bios none -cpu rv64 -m 64) + ;; +aarch64) + cc=aarch64-linux-gnu-gcc + qemu=qemu-system-aarch64 + cflags="-march=armv8-a" + asm=src/baremetal/start_aarch64.S + ld=src/baremetal/link_aarch64.ld + def=-DBARE_AARCH64 + qargs=(-machine virt -cpu cortex-a57 -m 64 -semihosting-config enable=on,target=native) + ;; +*) echo "baremetal: unknown target $target" >&2; exit 2 ;; +esac + +D=$(mktemp -d) +trap 'rm -rf "$D"' EXIT +common="$cflags -ffreestanding -fno-pic -fno-pie -fno-builtin -fno-unwind-tables -fno-asynchronous-unwind-tables -O2 -Wall -Wextra" + +if [ "$mode" = run ] || [ "$mode" = io ]; then + # the map is sized to the program, so a bigger runtime or program just gets a bigger board + gcc -O2 -w src/tools/genmap.c -o "$D/genmap" + GENMAP="$D/genmap" bash src/tools/automap.sh run "$D/img" src/runtime/run.zc "$src" >"$D/genmap.log" 2>&1 || { cat "$D/genmap.log" >&2; exit 1; } + cp src/micro/micro.c src/micro/micro_port.h src/core/fops.h src/core/bops.h src/core/stack.h "$D/img/" + "$cc" $common $def -I"$D/img" -c "$D/img/micro.c" -o "$D/micro.o" + if [ "$mode" = io ]; then + "$cc" $common $def -DBARE_INPUT=1 -I"$D/img" -c src/baremetal/port.c -o "$D/port.o" + timeout=60 + else + "$cc" $common $def -DBARE_INPUT=0 -I"$D/img" -c src/baremetal/port.c -o "$D/port.o" + timeout=120 + fi +elif [ "$mode" = compile ]; then + cp src/micro/micro.c src/micro/micro_port.h src/core/memmap.h src/core/genome.h src/core/fops.h src/core/bops.h src/core/stack.h "$D/" + "$cc" $common $def -I"$D" -c "$D/micro.c" -o "$D/micro.o" + "$cc" $common $def -DBARE_INPUT=1 -I"$D" -c src/baremetal/port.c -o "$D/port.o" + timeout=90 +else + echo "baremetal: mode is run, io or compile" >&2 + exit 2 +fi +"$cc" $common $def -c "$asm" -o "$D/start.o" +"$cc" $common $def -nostdlib -static -Wl,--build-id=none -Wl,--no-warn-rwx-segments -T "$ld" -o "$D/bare.elf" "$D/start.o" "$D/port.o" "$D/micro.o" +if [ "${BARE_SIZE:-}" = 1 ]; then + size=$(wc -c < "$D/bare.elf") + text=$(size "$D/bare.elf" | awk 'NR==2{print $1+$2}') + bss=$(size "$D/bare.elf" | awk 'NR==2{print $3}') + echo "elf $size bytes, text+data $text, bss $bss" >&2 +fi + +# -serial stdio is the UART. The monitor stays off so it cannot eat bytes. +runq() { + timeout "$timeout" "$qemu" "${qargs[@]}" -nographic -monitor none -serial stdio -kernel "$D/bare.elf" +} +if [ "$mode" = compile ]; then + (cat "$src"; printf '`\004') | runq +elif [ "$mode" = io ]; then + (cat; printf '\004') | runq +else + runq tokens (nextch, lex1, next/peek). a token is a +// kind (tk), a value (tv: number or interned name) and a +// place (line, offset, length). sources nest: stdin, a +// token list for macros, a string for eval. +// 2 symbols every name is interned once in a string pool; symbols +// (functions, globals, constants, keywords, types) hang +// off the interned name and shadow one another. +// 3 types records in the type area, hash-consed, so comparing two +// types is comparing two words. typing is optional: an +// untyped program is all "word". +// 4 analyzer the ownership/lifetime pass. it runs while the parser +// emits code, tracking for every local whether it owns an +// object in this block (m), moves one up and out (u), or +// moves one down into a lower scope (d). borrowing is +// tracked separately. the pass inserts frees, and joins +// merge that knowledge at +// if/else, loops and switches. +// 5 expressions precedence climbing over the operator tables, which a +// program can extend at compile time. +// 6 statements blocks, control flow, switch, asm blocks, quote/grab. +// 7 functions signatures, prologues, closures, methods, generics, +// fattening (typed new) and the top level. +// 8 setup the keyword table, built-in names, entry point. +// 9 check mode the same compiler run over an in-memory document for the +// language server: errors and hints become records. +// +// the compiler emits vm words directly into memory at "here" (there is no +// intermediate tree). a forward reference emits a placeholder literal and +// remembers the site on a patch chain; defining the name patches the chain. +// the compiler is itself written in zc and lives in the genome next to rt.zc, +// so a program being compiled can call any function in this file, and +// immediate fns (parse words) run in the middle of compilation and can read +// tokens, emit code, or add operators. a few conventions that recur: +// - "plain word": most values are untyped 64-bit words. a pointer is just a +// word that happens to be a heap address. +// - a code word is lit(v) = 2v+1 (odd) or op(k) = 2k (even). see lit and op. +// - names ending in an s_ prefix are interned spellings the parser compares +// against by pointer, which is why intern exists. +// - serr reports and carries on; die reports and skips input to the next +// ; or }. nothing in here raises or unwinds. +// - a backtick outside a comment or a literal ends the source (srcend). zc0, +// the c bootstrap, reads the same way, so a backtick in a comment is fine. +// +// ---- compiler data region (@map-cc) ---- +// src/tools/genmap computes these addresses from the compiler table sizes for each +// build. later regions (the type area, local types, exit records) are computed +// from these names. + +// @map-cc + +const int IHN = 0x8000; +const word R_IH = 0x90100; +const word R_POOL = 0x98100; +const word R_POOLEND = 0xD8100; +const int NS = 0x4000; +const word R_SYM = 0xD8200; +const word sname_BASE = 0xD8200; +const word skind_BASE = 0xDC200; +const word sval_BASE = 0xE0200; +const word ssig_BASE = 0xE4200; +const word sflag_BASE = 0xE8200; +const word sfix_BASE = 0xEC200; +const word snext_BASE = 0xF0200; +const int NL = 0x200; +const word lname_BASE = 0xF4200; +const word lslot_BASE = 0xF4400; +const word lhome_BASE = 0xF4600; +const word lstate_BASE = 0xF4800; +const word lowner_BASE = 0xF4A00; +const word lused_BASE = 0xF4C00; +const word larr_BASE = 0xF4E00; +const word lown_BASE = 0xF5000; +const int NF = 0x8000; +const word fsite_BASE = 0xF5200; +const word fnext_BASE = 0xFD200; +const word R_SNAP_BASE = 0x105200; +const word R_QBUF_BASE = 0x109200; +const word R_SRC_BASE = 0x10D200; +const word R_SEQ_BASE = 0x10D600; +const word bname_BASE = 0x10DE00; +const word bprec_BASE = 0x10DE40; +const word bseq_BASE = 0x10DE80; +const word blen_BASE = 0x10DEC0; +const word uname_BASE = 0x10DF00; +const word useq_BASE = 0x10DF20; +const word ulen_BASE = 0x10DF40; +const word aname_BASE = 0x10DF60; +const word abin_BASE = 0x10DFA0; +const word kops_BASE = 0x10E000; +const word R_CTX_BASE = 0x10E200; +const word R_LOOP_BASE = 0x10EA00; +const word R_ASM = 0x10EE00; +const int ASM_LABELS = 0x40; +const int ASM_REFS = 0x100; +const int ASM_WORDS = 0x400; +const int EXIT_WORDS = 0xA00; +const int ENUM_MEMBERS = 0x80; +const int TAGGED_ENUMS = 0x10; +const int STRUCT_METHODS = 0x20; +const int GENERATED_WORDS = 0x2000; +const int RANGE_FACTS = 0x20; +const int JOIN_PATHS = 0x40; +const int O3_REGIONS = 0xA; +// @end-map + +// table bases start in the generated map; growth copies columns without changing indices. +word sname = sname_BASE; +word skind = skind_BASE; +word sval = sval_BASE; +word ssig = ssig_BASE; +word sflag = sflag_BASE; +word sfix = sfix_BASE; +word snext = snext_BASE; +word lname = lname_BASE; +word lslot = lslot_BASE; +word lhome = lhome_BASE; +word lstate = lstate_BASE; +word lowner = lowner_BASE; +word lused = lused_BASE; +word larr = larr_BASE; +word lown = lown_BASE; +word fsite = fsite_BASE; +word fnext = fnext_BASE; +int symcap = NS, loccap = NL, fixcap = NF; + +const word R_TY = ZFP - 16384, R_TYEND = ZFP; +// interned text and type records never move: only their index vectors grow. +word namehash = R_IH, poolend = R_POOLEND; +word poolblocks, typeblocks; +int hashcap = IHN, namecount, poolchunk = R_POOLEND - R_POOL; +word typelist = R_TY, tyend = R_TYEND; +int typecap = 1024; + +// growing scratch regions retain offsets used by nested parser frames. +word R_SNAP = R_SNAP_BASE; int R_SNAP_cap = R_QBUF_BASE - R_SNAP_BASE; +word R_QBUF = R_QBUF_BASE; int R_QBUF_cap = R_SRC_BASE - R_QBUF_BASE; +word R_SRC = R_SRC_BASE; int R_SRC_cap = R_SEQ_BASE - R_SRC_BASE; +word R_SEQ = R_SEQ_BASE; int R_SEQ_cap = bname_BASE - R_SEQ_BASE; +word R_CTX = R_CTX_BASE; int R_CTX_cap = R_LOOP_BASE - R_CTX_BASE; +word R_LOOP = R_LOOP_BASE; int R_LOOP_cap = R_ASM - R_LOOP_BASE; +[60]word lpfact_initial; word lpfact = lpfact_initial; int loopfacts_cap = 60; + +// operator columns grow independently of their initial map reservations. +word bname = bname_BASE; +word bprec = bprec_BASE; +word bseq = bseq_BASE; +word blen = blen_BASE; +word uname = uname_BASE; +word useq = useq_BASE; +word ulen = ulen_BASE; +word aname = aname_BASE; +word abin = abin_BASE; +word kops = kops_BASE; +int bincap = bprec_BASE - bname_BASE, uncap = useq_BASE - uname_BASE, asgcap = abin_BASE - aname_BASE, kopcap = R_CTX_BASE - kops_BASE; +// ---- token kinds, symbol kinds and flags ---- +const int T_EOF = 0, T_NUM = 1, T_ID = 2, T_STR = 3, T_OP = 4, T_FLT = 5, T_BSTR = 6; +const int K_FN = 1, K_GVAR = 2, K_GARR = 3, K_CONST = 4, K_KW = 5, K_ENH = 6, K_TYPE = 7, K_DOC = 8; +const int F_DEF = 1, F_SIG = 2, F_UNTYPED = 4, F_STMT = 8, F_EXPR = 16, F_TOP = 32, F_FALLBACK = 64, F_GEN = 128, F_PRE = 256, F_O3 = 512, F_EXT = 1024, F_TPL = 2048, F_STATIC = 4096, F_DECL = 8192, F_RETOWN = 16384, F_EARLY = 32768; +// variable states for the lifetime analyzer +const int S_PLAIN = 0, S_M = 1, S_U = 2, S_D = 3, S_DEAD = 4, S_CHILD = 5, S_BORROW = 6; +// expression classes: plain word, a new object, or a bare variable (C_VAR + index) +const int C_PLAIN = 0, C_FRESH = 1, C_VAR = 2; +// lvalue kinds +const int LV_NONE = 0, LV_LOCAL = 1, LV_GLOBAL = 2, LV_ELEM = 3, LV_OTHER = 4; +// opcodes +const int O_Q = 0, O_LD = 1, O_ST = 2, O_PICK = 3, O_DROP = 4, O_SWAP = 5, O_CALL = 6, O_JMP = 7; +const int O_JZ = 8, O_RET = 9, O_ADD = 13, O_SUB = 14, O_LT = 20; +const int XOP0 = 22; // first enhancement opcode +const int OP_FLIT = 600, OP_FADD = 601, OP_FSUB = 602, OP_FMUL = 603, OP_FDIV = 604; +const int OP_FEQ = 605, OP_FLT = 606, OP_FLE = 607, OP_I2F = 608, OP_F2I = 609; +const int SEQWORD = -4; // in an operator sequence: the next word is a full-width literal +const int SEQREF = -2; // in an operator sequence: the next word names a symbol + +// ---- state ---- +int chk_heap; +int nenh; word pooltop; int nsym, ubase, vislo, nfixu, fixfree; +int nbin, nun, nasg, nkop, seqtop; +char* mnem = "q@!$_~cjzr,?.+-*&|^><%"; +int check_on = 1; // set to 0 to switch the analyzer off for what follows +int tk; word tv; int tglue, tline, line = 1, ch, chkind; word chptr, chbase; int qdone, isp = -1, pendot; +// where the current token sits in its source (chars from the start, -1 for a token list), how long it is, and where the one before it ended +int toff = -1, toklen, tpend = -1, tpline; +int ntok; // tokens taken so far, so a parse word that reads nothing can be caught +int qst, qesc, qp; // srcend's reading state: normal, line comment, block comment, string, char literal +[1100]word tbuf_initial; word tbuf = tbuf_initial; int tbufcap = 1100; +// doc comments: the comment lines directly above a declaration (no blank line +// between them or before the declaration) are its documentation. the lexer +// collects them in dbuf, dlast is the line of the last one, and dline the +// line of the token that follows them. only when docs are on. +int docs_on; // docs on; keeps them: a symbol "doc name" holds a byte string +[2100]word dbuf_initial; word dbuf = dbuf_initial; int docbufcap = 2100; int dn, dlast, dline, docpending; +[2100]word dsnap_initial; word dsnap = dsnap_initial; int docsnapcap = 2100; int dsn, dnm; [300]word dnmb_initial; word dnmb = dnmb_initial; int docnamecap = 300; +int pk_tk, pk_tv, pq_n; [4]word pq_tk; [4]word pq_tv; [4]word pq_gl; [4]word pq_ln; // up to 4 tokens of lookahead +int here, hdelta, hlimit, ptop; +int code_chunk = CODE_END; +word image_buffer, image_words; int image_active, image_org, image_capacity, image_size; +int nloc, lbase, fdepth, fslots, fchk, fret, live, bxdepth, epichain, fframe, infn; +int finsym, finfer; // the function being compiled, and whether its owned return is still to be inferred (no .u) +int stubgen; // 1 while an extern's generated stub is compiled: the stub gets no hints +int xbor = -1; // local a pointer value is borrowed from, or -1 +int mdbg, mdbg_flags; // memdebug: compile checks in, and the flags start() turns on +int lsp, loopbase, ctxsp, snaptop; +int lv, lvk, lvi, kw_expr, xcls, fty; +// interned spellings the parser compares against +int s_lp, s_rp, s_lb, s_rb, s_lk, s_rk, s_comma, s_semi, s_eq, s_arrow, s_inc, s_dec; +int s_star, s_slash, s_amp, s_colon, s_hash, s_minus, s_andand, s_oror; +int s_dot, s_fn, s_var, s_const, s_else, s_as, s_unchecked, s_immediate, s_m, s_d, s_u; +int s_q, s_case, s_default, s_fovf, s_start, s_main, s_free, s_recycle, s_own, s_qnew, s_enhance, s_micro, s_vm; +int s_frame_floor, s_frame_top, s_frame_node, s_frame_busy, s_frame_guard, s_frame_grow, s_frame_leave; +int s_do, s_while, s_enum, s_include, s_memdebug, s_scrub, s_alloc, s_check, s_memset, s_static; + +// ---- types: kinds, tables and parser state (see the types section) ---- +const int TY_WORD = 0, TY_INT = 1, TY_CHAR = 2, TY_BOOL = 3, TY_VOID = 4, TY_PTR = 5, TY_ARR = 6; +// dyn: []T, a = element, n = 1 if fixed +const int TY_FN = 7, TY_STRUCT = 8, TY_VAR = 9, TY_GEN = 10, TY_DYN = 11, TY_FLOAT = 12; +const int NTL = 1024; // the first NTL words list every record +// type per local, declared tag per local, type per symbol +const word LTYPE_BASE = R_ASM + ASM_WORDS, LTAG_BASE = LTYPE_BASE + NL, STYPE_BASE = LTAG_BASE + NL; +word ltype = LTYPE_BASE, ltag = LTAG_BASE, stype = STYPE_BASE; +int tytop, ntl; word tword, tint, tchar, tbool, tvoid, tfloat, tf32; +int ckst, ckn; // extern: the zc struct a by-value parameter names (ckind), and its name +int tc_on; // the optional typechecker, off unless a program says typecheck on; +// type, literal 0, float, byte string, dry runs (typeof), unique giveaway +word xty; int xzero, xfloat, xstr, dry, xgive; +// the type facts of annotate mode (see type facts below): the tables, and where the declaration being read starts +const int NIF = 431, IFP = 24, IFW = 80, IFN = 4096, IFT = 32768; +int ifon; word ifs, ifr; int nif; word ifx; int nifx; word ift; int nift; word ifcur; int ifkp, ifkk, ifcall, ifgk, ifstrict; +int ifrcap = NIF, ifxcap = IFN, iftcap = IFT; +int fldst, fldi; // the one struct a bare address's field name resolved to +[8]word tpn; int ntp; // type parameters in scope +// reading a return type: unknown names are type parameters declared after the function's name +int tvauto; +int dty, dtag; // result of dtype() +int s_lt, s_gt, s_shr, s_le, s_ge, s_eqeq, s_ne, s_plus, s_struct, s_type, s_typecheck, s_docs, s_on, s_off, s_new, s_new_t; +int s_o3, s_lfa, s_outside, s_new_raw, s_new_oz, s_oob; +int s_ld8, s_st8, s_ld16, s_st16, s_ld32, s_st32; +// ---- o3 region state ---- + +// each promoted slot maps to a register; pending regions use 67-word records and a descriptor buffer. +int o3on, o3nxt, o3one; [32]word o3sl; [32]word o3rg; int no3sl; [O3_REGIONS * 67]word o3pend_initial; word o3pend = o3pend_initial; int o3pendcap = O3_REGIONS, no3pend; [80]word o3dsc; int o3fs, o3na, o3fn0; +// o3free holds registers released by closed scopes; o3cache holds the items pointer and length slots for fixed arrays. +[8]word o3free; int no3free, o3nreg; [NL]word o3cache_initial; word o3cache = o3cache_initial; int argloc, s_hitems, s_hlen, s_fatten; +// operator names used by numeric type checks. +[12]word numops; int nnumops, s_fixed, s_dlen, s_dcap, s_dnew, s_dat, s_dpush, s_dpop, s_dresize, xconst, xcval; +int xlenloc, arrloc, fortest, forstep, ft_iv, ft_bd, ft_ir, ft_nops, fs_ok, xstamp; +// local lower bound / len(array); one proof record per live loop +int nfact, factcap = RANGE_FACTS; [RANGE_FACTS]word floc_initial, flo_initial, flen_initial; word floc = floc_initial, flo = flo_initial, flen = flen_initial; +int s_len_s, s_at_s, s_slice_s, s_cat_s, s_eq_s, s_lt_s, s_le_s, s_put_bs, s_put_s, s_n_to_s, s_new_s; +int mdg; [8]word mdn; // the type parameters of the generic struct whose methods are being compiled +[8]word gbind; int ngbind; // explicit type arguments for the next call: f(...) +// generic functions. the template (type parameters stay variables) is compiled once, as before, +// and its tokens are kept; a call whose type arguments are all known gets an instance, the same +// tokens compiled again with the parameters bound to those types, so new(T) and sizeof(T) +// inside it mean the real type. instances are compiled after the item that asked for them. +int grec; word gbuf; int gn, gcap; // the tokens of the top-level item being read +int gnew, gnew_np; [8]word gnew_nm; // a generic function was just defined +word gtab; int gt_n, gtcap = 64, gicap = 128; word gt_sym, gt_tok, gt_np; int gt_nm; // the columns are allocated at the first generic function +int gi_n; word gi_t, gi_sym, gi_ar, gi_done; +int ginst; [8]word gnm; [8]word gib; int gng, ginsn; // compiling an instance: the names and what they stand for +int s_this; +// closures being compiled, innermost last: the enclosing function's locals +// (lo .. hi), how many variables the body has captured so far (names and the +// enclosing local each came from), and the body's own hidden env parameter +const int NCLO = 8, NCAP = 32; +int nclo, closure_cap = NCLO, capture_width = NCAP; +[NCLO]word clo_lo_initial; word clo_lo = clo_lo_initial; +[NCLO]word clo_hi_initial; word clo_hi = clo_hi_initial; +[NCLO]word clo_n_initial; word clo_n = clo_n_initial; +[NCLO]word clo_env_initial; word clo_env = clo_env_initial; +[NCLO]word clo_ret_initial; word clo_ret = clo_ret_initial; +[256]word clo_name_initial; word clo_name = clo_name_initial; +[256]word clo_loc_initial; word clo_loc = clo_loc_initial; +[256]word clo_via_initial; word clo_via = clo_via_initial; + + +// ---- errors ---- +// the compiler reports up to 20 errors before it stops. a syntax error +// (die) skips the token it happened on and sets panic, which skips ahead to +// the next ; or } (or the next top-level item) and keeps quiet until then. +// other errors (serr) just report: the parse is still in step. +// +// check mode (cmode) is for the language server, which runs the real compiler +// over a document it holds in memory. nothing is printed and nothing halts: +// each error is written into dgbuf as a record [severity, line, offset, +// length, chars...] and giveup raises chkabort, which makes the lexer return +// end of file so every parsing loop winds down by itself. +int nerr, panic; +// bits kept in a local's ltag for the hints: assigned after its declaration, +// read, declared with let, initialized with a literal; the size of the object +// a new gave it (bits 16 to 39); and how many times it was used (from bit 56) +const int LASG = 0x1000, LREAD = 0x2000, LDECL = 0x4000, LCONST = 0x8000, LCNT1 = 0x100000000000000; +const int NEWTYPED = 0x800000; +[NL]word lpos_initial; word lpos = lpos_initial; [NL]word lnsz_initial; word lnsz = lnsz_initial; [NL]word lleaf_initial; word lleaf = lleaf_initial; [NL]word lrsoff_initial; word lrsoff = lrsoff_initial; [NL]word lrskind_initial; word lrskind = lrskind_initial; int newsz, newleaf, nomagic, callpos; +[NL]word lptr_initial; word lptr = lptr_initial; // annotate mode: the local was indexed or dereferenced, so it holds an address +// storage facts are shared by aliases. they describe one allocation, not a +// variable slot: [length, lifetime, kind, allocation place, end place, owner]. +// lifetime is 0 unknown, 1 live, 2 ended; kind is 1 stack, 2 heap, 3 array handle. +[NL]word lafact_initial; word lafact = lafact_initial; +[NL]word laaddr_initial; word laaddr = laaddr_initial; word xaf, argfact; +// local name lookup keeps a head for each spelling and a shadow link per slot. +[NL]word lprev_initial; word lprev = lprev_initial; +[256]word lkeys_initial; word lkeys = lkeys_initial; +[256]word lheads_initial; word lheads = lheads_initial; int lkeycap = 256, lkeyn; +int s_new_k, s_recycle_leaf; +// dgprev: the next record sits at the end of the previous token +int cmode, chkabort, chkfuel; word dgbuf, dggrow; int dgcap, dgpos, dgn, dgopen, dgprev, dgmode; +// expand a diagnostic in progress, keeping its output offset within the buffer. +int dg_expand() { + dgbuf = chk_resize(dgbuf, dgpos + 5 + outn, dgcap * 2); + dgcap = dgcap * 2; outbuf = dgbuf + dgpos + 5; outcap = dgcap - dgpos - 5; + return 0; +} +// diagnostic records for check mode. dg_open starts a record [severity, line, +// offset, length, text length, text...] at the current token (or just past the +// previous one when dgprev is set) and redirects the error text into it with +// outmode 2; dg_close seals it with the length of the text and restores output. +// severity 1 is an error, 2 a warning, and 4 a hint. +int dg_close() { + if (!dgopen) return 0; + dgbuf[dgpos + 4] = outn; dgpos = dgpos + 5 + outn; dgn++; + dgopen = 0; outmode = dgmode; outgrow = dggrow; + return 0; +} +// open a check-mode diagnostic record of the given severity. +int dg_open(int sev) { + dg_close(); + if (dgpos + 205 > dgcap) { + int cap = dgcap; while (cap < dgpos + 205) cap = cap * 2; + dgbuf = chk_resize(dgbuf, dgpos, cap); dgcap = cap; + } + if (dgprev && tpend >= 0) { + dgbuf[dgpos + 1] = tpline; + dgbuf[dgpos + 2] = tpend - 1; + dgbuf[dgpos + 3] = 1; + } + else { dgbuf[dgpos + 1] = tline; dgbuf[dgpos + 2] = toff; dgbuf[dgpos + 3] = toklen; } + dgbuf[dgpos] = sev; + outbuf = dgbuf + dgpos + 5; outn = 0; outcap = dgcap - dgpos - 5; + dggrow = outgrow; outgrow = dg_expand; + outmode = 2; dgopen = 1; + return 0; +} +[256]word dgseen_initial; word dgseen = dgseen_initial; int dgseen_cap = 256, dgseen_n; +// start and stop a check run. chk_begin points the diagnostics at a buffer +// the caller owns; chk_end flushes any open record and restores output. +int chk_begin(int buf, int cap) { + cmode = 1; chkabort = 0; chkfuel = 0; dgbuf = buf; dgcap = cap; dgpos = 0; dgn = 0; dgopen = 0; + nerr = 0; panic = 0; dgmode = outmode; fill_w(dgseen, 0, dgseen_cap); dgseen_n = 0; + return 0; +} +// end check mode and put the output back the way it was. +int chk_end() { + dg_close(); + cmode = 0; + outmode = dgmode; + return 0; +} +// a hint is a finding that isn't an error: the program still compiles, and +// only check mode reports it. hint_begin(place) opens the record at a packed +// place (see tpack) and says whether to write the text; dg_close ends it. +// one hint per source position. a growing set keeps large documents linear +// instead of rescanning every previous diagnostic for each local. +int dg_hash(int key, int cap) { return ((key ^ (key >> 21)) * 0x9E37) & (cap - 1); } +int dg_dup() { + int key = ((toff + 1) << 24) | tline, i, k, cap; word old; + if (dgseen_n + dgseen_n >= dgseen_cap) { + old = dgseen; cap = dgseen_cap * 2; dgseen = chk_resize(0, 0, cap); + i = 0; + while (i < dgseen_cap) { + if (old[i]) { k = dg_hash(old[i], cap); while (dgseen[k]) k = (k + 1) & (cap - 1); dgseen[k] = old[i]; } + i++; + } + dgseen_cap = cap; + } + i = dg_hash(key, dgseen_cap); + while (dgseen[i]) { if (dgseen[i] == key) return 1; i = (i + 1) & (dgseen_cap - 1); } + dgseen[i] = key; dgseen_n++; return 0; +} +// start a check-mode hint at token position p, or say no when hints are off. +int hint_begin(int p) { + int sv, ok; + if (!cmode || dry || panic || chkabort || stubgen) return 0; + sv = tpack(); tunpack(p); + ok = !dg_dup(); + if (ok) { dg_open(4); ok = dgopen; } + tunpack(sv); + return ok; +} +// error plumbing. errat prints the "zc: line N:" prefix (or opens a record in +// check mode) and counts the error; errend ends the line and gives up after +// 20; giveup prints the total and halts (check mode: sets chkabort, which +// makes the lexer return end of file so the parse unwinds). fatal reports and +// stops at once; serr just reports; die reports and also skips input to the +// next ; or } (panic) so one mistake does not become a screen of errors. +int errat() { + if (cmode) { + dg_open(1); + } else { + put_s("zc: line "); + put_n(tline); + put_s(": "); + } + nerr++; + return 0; +} +// finish an error line; the compiler stops after 20 errors. +int errend() { + if (cmode) { dg_close(); if (nerr >= 20) chkabort = 1; return 0; } + put_c(10); + if (nerr >= 20) { put_s("zc: too many errors, stopping\n"); halt(); } + return 0; +} +// stop compiling after errors and report how many there were. +int giveup() { + if (cmode) { dg_close(); chkabort = 1; return 0; } + put_s("zc: "); put_n(nerr); if (nerr == 1) put_s(" error\n"); else put_s(" errors\n"); halt(); + return 0; +} +// report a fatal error and stop, or record it in check mode. +int fatal(int msg) { + errat(); put_s(msg); + if (cmode) { dg_close(); chkabort = 1; return 0; } + put_c(10); halt(); + return 0; +} +// report a syntax error at the current token, once per recovery. +int serr(int msg) { + if (!panic && !dry) { + errat(); + put_s(msg); + errend(); + } + return 0; +} +// report an error that names something, then the message. +int serrn(int name, int msg) { + if (!panic && !dry) { + errat(); + put_s(name); + put_s(": "); + put_s(msg); + errend(); + } + return 0; +} +// report an error and skip to the next top-level item. +int die(int msg) { + serr(msg); + panic = 1; + if (tk == T_EOF) giveup(); + next(); + return 0; +} +// what the parser is looking at, for the "found" part of a message +int tokshow() { + if (tk == T_EOF) { put_s("the end of the file"); return 0; } + if (tk == T_NUM) { put_s("the number "); put_n(tv); return 0; } + if (tk == T_STR) { put_s("a string"); return 0; } + if (tk == T_BSTR) { put_s("a byte string"); return 0; } + if (tk == T_FLT) { put_s("a float"); return 0; } + put_s("'"); put_s(tv); put_s("'"); + return 0; +} +// like die, and says what was found instead +int diefound(int msg) { + if (!panic && !dry) { + errat(); + put_s(msg); put_s(", found "); tokshow(); + errend(); + } + panic = 1; + if (tk == T_EOF) giveup(); + next(); + return 0; +} +// die with a name shown before the message. +int dien(int name, int msg) { + serrn(name, msg); + panic = 1; + if (tk == T_EOF) giveup(); + next(); + return 0; +} +// four classification bits live next to each interned spelling. this works +// for names in later pool chunks too, without indexing by a fixed pool offset. +const int LSW = 1; +word lsets; +int lsmark(int set, int n) { + if (lsets) n[-2] = n[-2] | (1 << set); + return 0; +} +word lsget(int set, int n) { + if (!lsets) return 0; + return (n[-2] >> set) & 1; +} +// name resolution for the language server. while a check runs over the document +// at rsbase, every name a use or a declaration resolves to is written into rsrec +// as a record: the byte offset, kind (RS_LOCAL, RS_PARAM, RS_SYM or RS_FIELD), +// declaration or symbol, and storage facts at that position. a local names its declaration by offset +// (-1 when it has none), a symbol names its kind and flags (see rs_sym). the +// compiler's own scopes decide, so the highlighter and hover see locals and +// parameters first, then globals and built-ins, the way the compiler does. +const int RS_LOCAL = 1, RS_PARAM = 2, RS_SYM = 3, RS_FIELD = 4, RS_CAP = 32768, RS_W = 9; +int lt_on, lt_n, ltcap = 256, ltxtcap = 2048; +word lt_off, lt_at, lt_len, lt_type, ltxt; int ltxn; +int ht_n, htcap = 256; word ht_start, ht_end, ht_type; +int fieldn, fieldcap = 128; word fieldtypes, fieldnames, fieldoffs; +int field_note(word t, word n, int off) { + if (!lt_on || isp != 0 || dry) return 0; + if (!fieldtypes || fieldn == fieldcap) { + if (fieldtypes) fieldcap = fieldcap * 2; + fieldtypes = chk_resize(fieldtypes, fieldn, fieldcap); + fieldnames = chk_resize(fieldnames, fieldn, fieldcap); + fieldoffs = chk_resize(fieldoffs, fieldn, fieldcap); + } + fieldtypes[fieldn] = t; fieldnames[fieldn] = n; fieldoffs[fieldn] = off; fieldn++; + return 0; +} +int field_decl(word t, word n) { + int k = 0; + while (k < fieldn) { if (fieldtypes[k] == t && fieldnames[k] == n) return fieldoffs[k]; k++; } + return 0 - 1; +} +word rsrec, rstmp; [32]word pnoffs; int rsn, rscap, rsbase, nameoff; +// resize check-mode tables; their global pointers keep them visible to gc. +unchecked word chk_resize(word old, int count, int capacity) { + word p = new(capacity); + p[-4] = p[-4] | F_INTERNAL; + fill_w(p, 0, capacity); + if (old && count) copy_w(p, old, count); + return p; +} +// lexer and documentation buffers grow before storing the next character. +int token_char(int n, int c) { + if (n + 2 > tbufcap) { tbuf = chk_resize(tbuf, n, tbufcap * 2); tbufcap = tbufcap * 2; } + tbuf[n] = c; return n + 1; +} +int doc_char(int c) { + if (dn + 2 > docbufcap) { dbuf = chk_resize(dbuf, dn, docbufcap * 2); docbufcap = docbufcap * 2; } + dbuf[dn] = c; dn++; return 0; +} +int doc_name_room(int need) { + if (need <= docnamecap) return 0; + while (docnamecap < need) docnamecap = docnamecap * 2; + dnmb = chk_resize(dnmb, 0, docnamecap); return 0; +} +// reserve compiler columns at execution time. indices remain valid after a copy. +int reserve_symbols(int need) { + int cap = symcap; + if (need <= cap) return cap; + while (cap < need) cap = cap * 2; + sname = chk_resize(sname, nsym, cap); + skind = chk_resize(skind, nsym, cap); + sval = chk_resize(sval, nsym, cap); + ssig = chk_resize(ssig, nsym, cap); + sflag = chk_resize(sflag, nsym, cap); + sfix = chk_resize(sfix, nsym, cap); + snext = chk_resize(snext, nsym, cap); + stype = chk_resize(stype, nsym, cap); + if (ifs) ifs = chk_resize(ifs, symcap, cap); + symcap = cap; return cap; +} +int reserve_locals(int need) { + int cap = loccap; + if (need <= cap) return cap; + while (cap < need) cap = cap * 2; + lname = chk_resize(lname, nloc, cap); + lslot = chk_resize(lslot, nloc, cap); + lhome = chk_resize(lhome, nloc, cap); + lstate = chk_resize(lstate, nloc, cap); + lowner = chk_resize(lowner, nloc, cap); + lused = chk_resize(lused, nloc, cap); + larr = chk_resize(larr, nloc, cap); + lown = chk_resize(lown, nloc, cap); + ltype = chk_resize(ltype, nloc, cap); + ltag = chk_resize(ltag, nloc, cap); + lpos = chk_resize(lpos, nloc, cap); + lnsz = chk_resize(lnsz, nloc, cap); + lleaf = chk_resize(lleaf, nloc, cap); + lrsoff = chk_resize(lrsoff, nloc, cap); + lrskind = chk_resize(lrskind, nloc, cap); + lptr = chk_resize(lptr, nloc, cap); + lafact = chk_resize(lafact, nloc, cap); laaddr = chk_resize(laaddr, nloc, cap); lprev = chk_resize(lprev, nloc, cap); + o3cache = chk_resize(o3cache, nloc, cap); + loccap = cap; return cap; +} +int reserve_fixes(int need) { + int cap = fixcap; + if (need <= cap) return cap; + while (cap < need) cap = cap * 2; + fsite = chk_resize(fsite, fixcap, cap); + fnext = chk_resize(fnext, fixcap, cap); + fixcap = cap; return cap; +} +const word R_EXIT_BASE = STYPE_BASE + NS, R_EXITEND = R_EXIT_BASE + EXIT_WORDS; +word R_EXIT = R_EXIT_BASE; int R_EXIT_cap = EXIT_WORDS; +int reserve_R_SNAP(int need) { + int cap = R_SNAP_cap; + if (need <= cap) return cap; + while (cap < need) cap = cap * 2; + R_SNAP = chk_resize(R_SNAP, R_SNAP_cap, cap); R_SNAP_cap = cap; + return cap; +} +int reserve_R_QBUF(int need) { + int cap = R_QBUF_cap; + if (need <= cap) return cap; + while (cap < need) cap = cap * 2; + R_QBUF = chk_resize(R_QBUF, R_QBUF_cap, cap); R_QBUF_cap = cap; + return cap; +} +int reserve_R_SRC(int need) { + int cap = R_SRC_cap; + if (need <= cap) return cap; + while (cap < need) cap = cap * 2; + R_SRC = chk_resize(R_SRC, R_SRC_cap, cap); R_SRC_cap = cap; + return cap; +} +int reserve_R_SEQ(int need) { + int cap = R_SEQ_cap; + if (need <= cap) return cap; + while (cap < need) cap = cap * 2; + R_SEQ = chk_resize(R_SEQ, R_SEQ_cap, cap); R_SEQ_cap = cap; + return cap; +} +int reserve_R_CTX(int need) { + int cap = R_CTX_cap; + if (need <= cap) return cap; + while (cap < need) cap = cap * 2; + R_CTX = chk_resize(R_CTX, R_CTX_cap, cap); R_CTX_cap = cap; + return cap; +} +int reserve_R_LOOP(int need) { + int cap = R_LOOP_cap; + if (need <= cap) return cap; + while (cap < need) cap = cap * 2; + R_LOOP = chk_resize(R_LOOP, R_LOOP_cap, cap); R_LOOP_cap = cap; + if (cap / 8 > loopfacts_cap) { lpfact = chk_resize(lpfact, loopfacts_cap, cap / 8); loopfacts_cap = cap / 8; } + return cap; +} +int reserve_R_EXIT(int need) { + int cap = R_EXIT_cap; + if (need <= cap) return cap; + while (cap < need) cap = cap * 2; + R_EXIT = chk_resize(R_EXIT, R_EXIT_cap, cap); R_EXIT_cap = cap; + return cap; +} +int reserve_binary(int need) { + int cap = bincap; + if (need <= cap) return cap; + while (cap < need) cap = cap * 2; + bname = chk_resize(bname, nbin, cap); + bprec = chk_resize(bprec, nbin, cap); + bseq = chk_resize(bseq, nbin, cap); + blen = chk_resize(blen, nbin, cap); + bincap = cap; return cap; +} +int reserve_unary(int need) { + int cap = uncap; + if (need <= cap) return cap; + while (cap < need) cap = cap * 2; + uname = chk_resize(uname, nun, cap); + useq = chk_resize(useq, nun, cap); + ulen = chk_resize(ulen, nun, cap); + uncap = cap; return cap; +} +int reserve_assignment(int need) { + int cap = asgcap; + if (need <= cap) return cap; + while (cap < need) cap = cap * 2; + aname = chk_resize(aname, nasg, cap); + abin = chk_resize(abin, nasg, cap); + asgcap = cap; return cap; +} +int reserve_operators(int need) { + int cap = kopcap; + if (need <= cap) return cap; + while (cap < need) cap = cap * 2; + kops = chk_resize(kops, nkop, cap); + kopcap = cap; return cap; +} +// write a record for the name at byte offset off. ref is what a local names (its +// declaration's offset, or -1); sym is the symbol a RS_SYM record names, or -1. +int rs_put(int off, int kind, int ref, int sym) { + int at; + if (!rsrec || !cmode || dry || chkind != 3 || chbase != rsbase || off < 0) return 0; + if (rsn >= rscap) { + rsrec = chk_resize(rsrec, rsn * RS_W, rscap * 2 * RS_W); + rstmp = chk_resize(0, 0, rscap * 2 * RS_W); + rscap = rscap * 2; + } + at = rsn * RS_W; + rsrec[at] = off; rsrec[at + 1] = kind; rsrec[at + 2] = ref; rsrec[at + 3] = sym; + fill_w(rsrec + at + 4, 0, RS_W - 4); + rsn++; + return 0; +} +// write a record for the name token that is current. +int rs_note(int kind, int ref) { return rs_put(toff, kind, ref, 0 - 1); } +// the flags of every symbol record, read again once the check has defined the +// whole document (a function used before its definition is defined by now). +int rs_fix() { + int i = 0; + while (i < rsn) { + if (rsrec[i * RS_W + 1] == RS_SYM && rsrec[i * RS_W + 3] >= 0) rsrec[i * RS_W + 2] = rs_sym(rsrec[i * RS_W + 3]); + i++; + } + return 0; +} +// what a symbol is, as a record keeps it: its kind, plus 16 for a symbol the +// document declares (not a built-in) and 32 when that symbol has a definition +int rs_sym(int s) { + int v = skind[s]; + if (s >= ubase) v = v | 16; + if (sflag[s] & F_DEF) v = v | 32; + return v; +} +// copies record a of from to record b of to (the merge sort moves records this way). +int rs_move(word from, int a, word to, int b) { + copy_w(to + b * RS_W, from + a * RS_W, RS_W); + return 0; +} +// sort the records by offset: a bottom-up merge sort through rstmp, so a long +// document costs n log n, whatever order its names come in. +int rs_sort() { + int w = 1, lo, mid, hi, i, j, k, n = rsn; + if (n < 2) return 0; + while (w < n) { + lo = 0; + while (lo < n) { + mid = lo + w; if (mid > n) mid = n; + hi = lo + w * 2; if (hi > n) hi = n; + i = lo; j = mid; k = lo; + while (i < mid && j < hi) { + if (rsrec[j * RS_W] < rsrec[i * RS_W]) { rs_move(rsrec, j, rstmp, k); j++; } + else { rs_move(rsrec, i, rstmp, k); i++; } + k++; + } + while (i < mid) { rs_move(rsrec, i, rstmp, k); i++; k++; } + while (j < hi) { rs_move(rsrec, j, rstmp, k); j++; k++; } + lo = hi; + } + i = 0; + while (i < n * RS_W) { rsrec[i] = rstmp[i]; i++; } + w = w * 2; + } + return 0; +} +// analyzer errors only count in checked, reachable code +int cerr(int msg) { + if (fchk && live) serr(msg); + return 0; +} +// report an analyzer error about local i, named in the message. +int cerrn(int i, int msg) { + if (fchk && live) serrn(lname[i], msg); + return 0; +} + +// ---- interning ---- +// the pool: an open addressing hash table (R_IH) of pointers into a string +// pool (R_POOL). each spelling has two spare words: its symbol-chain head at +// p[-1] and editor classification bits at p[-2]. equal text returns the same +// stable handle, which lookup can use directly. +int hashs(word s) { + int h = 0; + while (*s) { h = h * 31 + *s; s++; } + return h & (hashcap - 1); +} +// rehash the index without moving strings or their symbol-chain heads. +int reserve_names(int need) { + int cap = hashcap, previous = hashcap, i = 0, h; word p, old; + if (need <= cap) return cap; + while (cap < need) cap = cap * 2; + old = namehash; + namehash = chk_resize(0, 0, cap); + hashcap = cap; + while (i < previous) { + p = old[i]; + if (p) { h = hashs(p); while (namehash[h]) h = (h + 1) & (hashcap - 1); namehash[h] = p; } + i++; + } + return cap; +} +// return the interned copy of a spelling. new chunks preserve every old handle. +word intern(word s) { + int h; word p; int n; + if ((namecount + 1) * 2 >= hashcap) reserve_names(hashcap * 2); + h = hashs(s); + while (1) { + p = namehash[h]; + if (!p) break; + if (p[0] == s[0] && eq_w(p, s)) return p; + h = (h + 1) & (hashcap - 1); + } + n = len_w(s); + if (pooltop + n + 3 > poolend) { + if (poolchunk < n + 3) poolchunk = n + 3; + else if (poolchunk < 0x20000000) poolchunk = poolchunk * 2; + p = chk_resize(0, 0, poolchunk + 1); p[0] = poolblocks; poolblocks = p; + pooltop = p + 1; poolend = pooltop + poolchunk; + } + pooltop[0] = 0; pooltop[1] = 0; + p = pooltop + 2; + copy_w(p, s, n + 1); + pooltop = pooltop + n + 3; + namehash[h] = p; namecount++; + return p; +} + +// ---- symbols ---- +// each interned name keeps the index (+1) of its newest symbol in p[-1], and +// each symbol links to the one it shadows, so lookup is one step. +int addsym(word name, int kind, int val) { + int i = nsym; + if (nsym >= symcap) reserve_symbols(nsym + 1); + nsym++; + sname[i] = name; + skind[i] = kind; + sval[i] = val; + ssig[i] = 0; + sflag[i] = F_DEF; + sfix[i] = 0; + stype[i] = 0; + snext[i] = name[-1]; + name[-1] = i + 1; + return i; +} +// while an image builds, names below vislo are hidden, except keywords +int lookup(word name) { + int i = name[-1] - 1; + while (i >= 0) { + if (i >= vislo || skind[i] == K_KW || skind[i] == K_TYPE) return i; + i = snext[i] - 1; + } + return -1; +} +// drop the symbols above index to, which ends a scope. +int popsyms(int to) { + while (nsym > to) { nsym--; sname[nsym][-1] = snext[nsym]; } + return 0; +} +// look a name up from a plain string. +word lookups(int s) { return lookup(intern(s)); } + +// patch nodes +int fixnew(int site, int link) { + int i = fixfree; + if (i) fixfree = fnext[i]; + else { nfixu++; i = nfixu; if (i >= fixcap) reserve_fixes(i + 1); } + fsite[i] = site; fnext[i] = link; + return i; +} +// patch every lit in a chain to point at v, and recycle the nodes +int fixall(int head, int v) { + int n; + while (head) { + setlit(fsite[head], v); + n = fnext[head]; + fnext[head] = fixfree; + fixfree = head; + head = n; + } + return 0; +} + +// ---- code emission ---- +// code goes straight into memory at "here". hdelta shifts where it is written +// (an image build emits at one address and stores at another, see nested +// compiles), and dry turns emission off entirely, which typeof and similar +// queries use to type an expression without producing code. +// reserve contiguous words before recording an address or writing a data block. +// new runtime segments are linked by jumps; earlier code and globals stay put. +int reserve_code(int need) { + word p; int cap, top; + if (dry) return 0; + if (need < 0 || need > 0x7fffffff - 8) fatal("bad code reservation"); + if (image_active) { + if (need <= hlimit - here) return hlimit - here; + cap = image_capacity; + while (cap < here - image_org + need) { + if (cap > (0x7fffffff - image_org) / 2) fatal("image too big"); + cap = cap * 2; + } + image_buffer = chk_resize(image_buffer, here - image_org, cap); + image_capacity = cap; hdelta = image_buffer - image_org; hlimit = image_org + cap; + return hlimit - here; + } + if (need <= hlimit - here - 2) return hlimit - here - 2; + if (here + 1 >= hlimit) fatal("no room for a code link"); + cap = code_chunk; + if (cap < need + 5) cap = need + 5; + p = chk_resize(0, 0, cap); top = p + p[-5] - HDR; + if (syscall(9, 9, top, 0) < top) fatal("code address reservation failed"); + p[0] = code_blocks; p[1] = p + 3; p[2] = top; code_blocks = p; + *here = (p + 3) * 2 + 1; here[1] = O_JMP * 2; + here = p + 3; hlimit = top; + if (cap < 0x20000000) code_chunk = cap * 2; + return hlimit - here - 2; +} +// a region handed to an optimizer must fit in one contiguous segment. +int code_contiguous(int lo, int hi) { + word p = code_blocks; + if (image_active || (lo >= 0 && hi <= CODE_END)) return 1; + while (p) { if (lo >= p[1] && hi <= p[2] - 2) return 1; p = p[0]; } + return 0; +} +int emit(int w) { + if (dry) return 0; + if (w == OP_FLIT * 2) reserve_code(2); else reserve_code(1); + *(here + hdelta) = w; + here++; + return 0; +} +// a vm code word with the low bit set pushes the value in its upper bits +// (v + v + 1); an even word is an opcode (k + k). jmp pushes a target and jumps. +int lit(int v) { + if ((v >> 62) != 0 && (v >> 62) != -1) { + reserve_code(5); + emit((v >> 32) * 2 + 1); emit(0x100000000 * 2 + 1); op(15); + emit((v & 0xFFFFFFFF) * 2 + 1); op(17); + } else emit(v + v + 1); + return 0; +} +// emit one vm opcode. +int op(int k) { emit(k + k); return 0; } +// fill the literal placeholder at address at with value v. +int setlit(int at, int v) { + if (dry) return 0; + if ((v >> 62) != 0 && (v >> 62) != -1) fatal("literal patch too large"); + *(at + hdelta) = v + v + 1; + return 0; +} +// emit a jump to the address t. +int jmp(int t) { + lit(t); + op(O_JMP); + return 0; +} +// a lit that will be patched later; returns its address +int hole() { + int a; + reserve_code(1); a = here; + lit(MW - 1); + return a; +} +// forward references. calling a function that is not defined yet creates a +// symbol with F_DEF clear whose value is the place of its first use (for the +// "never defined" error). each call site emits a placeholder literal and adds +// itself to the symbol's chain (sfix); defining the function runs fixall to +// patch every site. MW-1 is a jump target that halts, so an unpatched call +// stops the machine instead of running garbage. +int fwdfn_at(int name, int p) { + int s = addsym(name, K_FN, p); + sflag[s] = 0; + return s; +} +// the place of the first use (line, and offset above bit 24), until it's defined +int fwdfn(int name) { + return fwdfn_at(name, tpack()); +} +// emit a reference to function s, patched later if it is not defined yet. +int fnref(int s) { + if (sflag[s] & F_DEF) { lit(sval[s]); return 0; } + if (dry) return 0; + reserve_code(1); sfix[s] = fixnew(here, sfix[s]); + lit(MW - 1); // a jump to MW-1 halts if it ever runs unpatched + return 0; +} +// emit a call to the function with this name, making a forward slot if it is not defined. +int callname(int name) { + int s = lookup(name); + if (s < 0) s = fwdfn(name); + fnref(s); + op(O_CALL); + return 0; +} +// the address of local slot k: the frame pointer lives in the word ZFP, and a +// local is fp + k. (o3 locals live in cpu registers instead; see vaddr.) +int laddr(int k) { + lit(ZFP); + op(O_LD); + if (k) { lit(k); op(O_ADD); } + return 0; +} +// an o3 variable's address: lfa(k) is fp + k as one enhancement, which the +// jit specializes on the literal k +word vaddr(int i) { + int s; + if (ltag[i] & 16) { + s = lookup(s_lfa); + if (s >= 0 && skind[s] == K_ENH) { + lit(lslot[i]); + op(XOP0 + sval[s]); + return 0; + } + } + return laddr(lslot[i]); +} +// registers for an o3 region's variables: the cpu has 7. a variable takes the +// one its region freed last (when a scope closed), or the next unused one +// ---- o3: the optimizing region compiler's bookkeeping ---- +// code inside an o3 region (or a function marked o3) keeps hot locals in the +// cpu's registers. the compiler does not change what it emits for them; it +// records which frame slot maps to which register and hands the finished +// region to the core through install(). the core compiles the whole region +// with those slots held in registers and writes them back to the frame when +// control leaves it. there are 7 registers, so locals past that stay in +// memory. +int o3reset() { + no3free = 0; + o3nreg = 1; + no3sl = 0; + return 0; +} +// a freed register keeps its class. a float home is a d register and an int +// home is an x register, and one var index cannot be both, so a number that +// was a float is only handed out again as a float. +int o3take(int fp) { + int i = 0, k, w = -1; + while (i < no3free) { + k = o3free[i]; + if (fp && k < 0) w = i; + if (!fp && k > 0) w = i; + i++; + } + if (w >= 0) { + k = o3free[w]; + no3free--; + o3free[w] = o3free[no3free]; + if (k < 0) return 0 - k; + return k; + } + if (o3nreg < 8) { o3nreg++; return o3nreg - 1; } + return 0; +} +// true when a type is a plain number that o3 can keep in a register. +int o3plain(word t) { + if (!t) return 0; + if (t[0] == TY_INT || t[0] == TY_CHAR || t[0] == TY_BOOL || t[0] == TY_FLOAT) return 1; + return 0; +} +// move a local into an o3 register when the region can keep it there. +int o3promote(int i) { + int r, fp; + ltag[i] = ltag[i] - (ltag[i] & 1792); + if (!o3on || larr[i] || no3sl >= 32) return 0; + fp = isfl(ltype[i]); + r = o3take(fp); + if (!r) return 0; + ltag[i] = ltag[i] + r * 256; + o3sl[no3sl] = lslot[i]; + if (o3plain(ltype[i])) r = r + 16; // 16 marks a plain number: the core need not write it back before a call + if (fp) r = 0 - r; // the install word is negative when the home is a d register + o3rg[no3sl] = r; no3sl++; + return 0; +} +// a fixed []T can't change its items or its length, so o3 code keeps both in +// two hidden variables, loaded when the array variable is set (or when the +// region starts), and indexes through them +int o3mkcache(int i) { + int k; + if (!o3on || ltype[i][0] != TY_DYN || !ltype[i][3] || tbytes(ltype[i][4])) return 0; + k = newloc(s_hitems); fslots++; ltag[k] = 16; o3promote(k); + newloc(s_hlen); fslots++; ltag[k + 1] = 16; o3promote(k + 1); + o3cache[i] = k; + return 0; +} +// reload the o3 register cache of a local from its memory slot. +int o3refresh(int i) { + int k = o3cache[i]; + if (!k) return 0; + vaddr(i); op(O_LD); lit(2); op(O_ADD); op(O_LD); vaddr(k); op(O_ST); + vaddr(i); op(O_LD); op(O_LD); vaddr(k + 1); op(O_ST); + return 0; +} +// hands a region to the cpu: install kind 2 is [-, 2, lo, hi, n, slot, register, ..., fslots, argc, fnstart] +// fslots, argc and the prologue address sit in o3fs, o3na, o3fn0. a bare o3 block leaves the prologue at -1, so a function that starts at address 0 still binds. +int o3region(int lo, int hi, word sl, word rg, int n) { + int i = 0; + if (hdelta || lo >= hi || !code_contiguous(lo, hi)) return 0; + o3dsc[0] = 0; o3dsc[1] = 2; o3dsc[2] = lo; o3dsc[3] = hi; o3dsc[4] = n; + while (i < n) { o3dsc[5 + i + i] = sl[i]; o3dsc[6 + i + i] = rg[i]; i++; } + o3dsc[5 + n + n] = o3fs; o3dsc[6 + n + n] = o3na; o3dsc[7 + n + n] = o3fn0; + install(o3dsc); + return 0; +} +// o3 blocks go to the cpu once their function is done, so every jump in them is final +int o3flush() { + int i = 0; word p; + o3fs = 0; o3na = 0; o3fn0 = -1; /* -1 is not a function; a prologue at address 0 is */ + while (i < no3pend) { p = o3pend + i * 67; o3region(p[0], p[1], p + 3, p + 35, p[2]); i++; } + no3pend = 0; + return 0; +} +// a string literal: the characters (one per word, then a 0) are emitted into +// the code stream behind a jump, and the literal's value is their address +int strlit(word s) { + int j = hole(), a; + op(O_JMP); + reserve_code(len_w(s) + 1); a = here; + while (*s) { emit(*s); s++; } + emit(0); + setlit(j, here); + lit(a); + return 0; +} + +// ---- input sources ---- +// frame: 0 kind (0 stdin, 1 token list, 2 string) 1 object 2 index 3 end +// 4 transparent 5..7 saved token 8..10 saved char state 11 owned 12 saved line +// 13 saved token line 14 saved lookahead count 15..30 saved lookahead 31 saved char buffer +// a backtick ends the source: it is how a program tells the compiler where its +// own text stops and the program's input begins. but only a backtick the +// lexer would read as a token counts. srcend is fed every character of a +// stdin source in order and follows just enough of the lexer's rules to know +// whether it is inside a // comment, a /* */ comment, a string or a char +// literal (a backslash escapes the next character in the last two). it +// returns 1 for a backtick outside all of them. readsrc and nextch both use +// it, so a backtick in a comment or a string is just a character. +int srcend(int c) { + if (qst == 0) { + if (c == 96) return 1; + if (c == 34) qst = 3; + else if (c == 39) qst = 4; + else if (c == '*' && qp == '/') { qst = 2; qp = 0; return 0; } + else if (c == '/' && qp == '/') { qst = 1; qp = 0; return 0; } + qp = c; + return 0; + } + if (qst == 1) { + if (c == 10) { qst = 0; qp = 0; } + return 0; + } + if (qst == 2) { + if (c == '/' && qp == '*') { qst = 0; qp = 0; return 0; } + qp = c; + return 0; + } + if (qesc) { qesc = 0; return 0; } + if (c == 92) { qesc = 1; return 0; } + if ((qst == 3 && c == 34) || (qst == 4 && c == 39)) { qst = 0; qp = 0; } + return 0; +} +// the character level of the lexer: ch is the current char (-1 at the end), +// and chkind says where it comes from (0 stdin, 3 a byte string, anything +// else a word string). a stdin source ends at a backtick (see srcend). +int nextch() { + if (chkind == 3) { // the usual case: the unit readsrc packed + ch = (chbase[chptr >> 2] >> ((chptr & 3) * 8)) & 255; + if (ch) chptr++; else ch = -1; + } else if (chkind == 0) { + if (qdone || eof()) { ch = -1; return 0; } + ch = get_c() & 255; + if (srcend(ch)) { qdone = 1; ch = -1; return 0; } // a backtick ends the source + } else { + ch = *chptr & 255; + if (ch) chptr++; else ch = -1; + } + if (ch == 10) line++; + return 0; +} +// the place of a token travels with its line: line in the low 24 bits, then +// the length (16 bits), the offset + 1 (21 bits) and its glue (2 bits, so a +// replayed token list still tells u*x from a product) +int tpack() { return tline | (toklen << 24) | ((toff + 1) << 40) | ((tglue & 3) << 61); } +// restore the token position that tpack saved in a word. +int tunpack(int x) { + tline = x & 0xFFFFFF; + toklen = (x >> 24) & 0xFFFF; + toff = ((x >> 40) & 0x1FFFFF) - 1; + tglue = (x >> 61) & 3; + return 0; +} +// chars consumed so far in the source being read: the index of the char in ch +// (or of the end, once the source is used up). a stdin source reports -1. +int cpos() { + if (chkind == 3) { if (ch < 0) return chptr; return chptr - 1; } + if (chkind == 2) { if (ch < 0) return chptr - chbase; return chptr - chbase - 1; } + return 0 - 1; +} +// push a new input source. the current token, char state and lookahead are +// saved into a 32 word frame so src_pop can put them back exactly. kind 0 is +// stdin, 1 a token list (obj[0] is the count, then triples of kind, value and +// packed place: this is how macros, quote/grab and feed replay code), 2 a +// word string and 3 a byte string. trans means "transparent": when a token +// list runs out the reader pops it and carries on in the source below, which +// is what lets a fed list splice into the text around it. owned means the +// frame frees obj when it pops. +int src_push(int kind, word obj, int trans, int owned) { + word f; int i; + isp++; + reserve_R_SRC((isp + 1) * 32); + f = R_SRC + isp * 32; + f[0] = kind; f[1] = obj; f[2] = 0; f[3] = 0; f[4] = trans; f[11] = owned; + f[5] = tk; f[6] = tv; f[7] = tglue; f[8] = ch; f[9] = chkind; f[10] = chptr; + f[12] = line; f[13] = tpack(); f[14] = pq_n; f[31] = chbase; + i = 0; + while (i < pq_n) { + f[15 + i * 4] = pq_tk[i]; + f[16 + i * 4] = pq_tv[i]; + f[17 + i * 4] = pq_gl[i]; + f[18 + i * 4] = pq_ln[i]; + i++; + } + pq_n = 0; + if (kind == 1) f[3] = obj[0]; + if (kind == 0) { chkind = 0; qdone = 0; qst = 0; qp = 0; qesc = 0; line = 1; nextch(); } + if (kind == 2) { chkind = 2; chptr = obj; chbase = obj; line = 1; nextch(); } + if (kind == 3) { chkind = 3; chbase = obj; chptr = 0; line = 1; nextch(); } + return 0; +} +// leave the current source and restore the one beneath it +int src_pop() { + word f = R_SRC + isp * 32; int i = 0; + tk = f[5]; tv = f[6]; tglue = f[7]; ch = f[8]; chkind = f[9]; chptr = f[10]; + line = f[12]; tunpack(f[13]); pq_n = f[14]; chbase = f[31]; + while (i < pq_n) { + pq_tk[i] = f[15 + i * 4]; + pq_tv[i] = f[16 + i * 4]; + pq_gl[i] = f[17 + i * 4]; + pq_ln[i] = f[18 + i * 4]; + i++; + } + isp--; + if (f[11]) free(f[1]); + f[1] = 0; // a dead source pointer would pin a quarantined block + return 0; +} +// the lexer's character classes. an operator is built one character at a time +// for as long as the text so far is still a prefix of some known operator +// (opprefix), so a program that adds an operator with add_binop changes how +// the next source is split into tokens without any other bookkeeping. +// the operator characters are + - * / % = < > ! & | ^ ~ ? : . @ # $ . those below +// 64 are a bit mask indexed from 33, the rest are four compares +int isopch(int c) { + if (c < 33 || c > 126) return 0; + if (c < 64) return (0x7a00763d >> (c - 33)) & 1; + return c == 64 || c == 94 || c == 124 || c == 126; +} +// is buf[0..n) followed by c the start of some operator the lexer knows? +int opprefix(int n, int c) { + int i = 0; word k; int j; + while (i < nkop) { + k = kops[i]; + j = 0; + while (j < n && k[j] == tbuf[j]) j++; + if (j == n && k[n] == c) return 1; + i++; + } + return 0; +} +// true for a decimal digit. +int isdig(int c) { return c >= '0' && c <= '9'; } +// true for a letter or an underscore. +int isal(int c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'; } +// a letter, digit or _ : the characters after the first one of a name +int isidc(int c) { + if (c >= 'a') return c <= 'z'; + if (c >= 'A') return c <= 'Z' || c == '_'; + return c >= '0' && c <= '9'; +} +// the value of a hex digit, or a value past 15 when c is not one. +int hexv(int c) { + if (isdig(c)) return c - '0'; + if (c >= 'a' && c <= 'f') return c - 'a' + 10; + if (c >= 'A' && c <= 'F') return c - 'A' + 10; + return -1; +} +// one possibly escaped character of a string or char literal +int escch() { + int c = ch; + nextch(); + if (c != 92) return c; + c = ch; + nextch(); + if (c == 'n') return 10; + if (c == 't') return 9; + if (c == 'r') return 13; + if (c == '0') return 0; + if (c == 'e') return 27; + return c; +} +// finish lexing an operator whose first n characters are in tbuf +int oplex(int n) { + while (ch >= 0 && isopch(ch) && opprefix(n, ch)) { n = token_char(n, ch); nextch(); } + tbuf[n] = 0; + tk = T_OP; tv = intern(tbuf); + return 0; +} +// per-local type flags kept in ltag. these are tiny accessors; the whole bit +// layout is: 0..3 the u/m/d tag, 4 o3, 5 float, 6 byte string, 8..10 the o3 +// register, 12.. the hint bits (see LASG and friends). +// bit 5 of ltag: this local holds a float. the low bits are the u/m/d tag and bit 4 is o3. +word getflt(int i) { return (ltag[i] >> 5) & 1; } +// mark local i as holding a float, or clear the mark. +int setflt(int i, int f) { + if (f) ltag[i] = ltag[i] | 32; + else ltag[i] = ltag[i] - (ltag[i] & 32); + return 0; +} +// bit 6 of ltag: this local holds a byte string. bit 5 is the float. +word getstr(int i) { return (ltag[i] >> 6) & 1; } +// mark local i as holding a string, or clear the mark. +int setstr(int i, int f) { + if (f) ltag[i] = ltag[i] | 64; + else ltag[i] = ltag[i] - (ltag[i] & 64); + return 0; +} +// b"...": length word, then bytes packed 8 per word, little-endian, and a 0 +// after the last byte. a jump skips the data. the address pushed is the length. +int bstrlit(word s) { + int j = hole(), a, n = 0, i = 0, w, k; + op(O_JMP); + while (s[n]) n++; + reserve_code((n >> 3) + 2); a = here; + emit(n); + while (i < n) { + w = 0; k = 0; + while (k < 8 && i < n) { w = w | (s[i] << (k * 8)); k++; i++; } + emit(w); + } + if (!(n & 7)) emit(0); + setlit(j, here); + lit(a); + return 0; +} +// ---- float literals and float code ---- +// a float is one word holding the ieee bits. the lexer collects the literal's +// text in tbuf and fbits asks the core (opcode 611) to convert it, so the +// compiler needs no float parser of its own. float operations are single vm +// opcodes (OP_FADD ...). an int operand is converted on the fly, and a +// comparison leaves a plain 0/1 behind. xfloat says whether the expression +// just compiled is a float. +int fbits() { return asm { tbuf @ #1222 }; } +// collect an exponent (e+3) onto the literal in tbuf; n is the length so far +int takeexp(int n) { + if (ch != 'e' && ch != 'E') return n; + n = token_char(n, ch); + nextch(); + if (ch == '+' || ch == '-') { n = token_char(n, ch); nextch(); } + if (!isdig(ch)) { die("bad float"); return n; } + while (isdig(ch)) { n = token_char(n, ch); nextch(); } + return n; +} +// the literal in tbuf[0..n) is a float: hand it back as a T_FLT token +int floatok(int n) { + tbuf[n] = 0; + tk = T_FLT; + tv = fbits(); + return 0; +} +// the binary operators that have a float form +int isfop(int o) { + if (o == s_plus || o == s_minus || o == s_star || o == s_slash) return 1; + if (o == s_lt || o == s_gt || o == s_le || o == s_ge || o == s_eqeq || o == s_ne) return 1; + return 0; +} +// emit float arithmetic or comparison o for operands that are floats (lf, rf). +// an int left operand sits under the right one, so it is converted with +// swap / i2f / swap. > and >= are < and <= with the operands swapped, and != +// is == followed by not. +int femit(int o, int lf, int rf) { + if (!lf) { op(O_SWAP); op(OP_I2F); op(O_SWAP); } + if (!rf) op(OP_I2F); + if (o == s_plus) { op(OP_FADD); xfloat = 1; return 0; } + if (o == s_minus) { op(OP_FSUB); xfloat = 1; return 0; } + if (o == s_star) { op(OP_FMUL); xfloat = 1; return 0; } + if (o == s_slash) { op(OP_FDIV); xfloat = 1; return 0; } + if (o == s_eqeq) { op(OP_FEQ); xfloat = 0; return 0; } + if (o == s_ne) { op(OP_FEQ); lit(1); op(18); xfloat = 0; return 0; } + if (o == s_lt) { op(OP_FLT); xfloat = 0; return 0; } + if (o == s_gt) { op(O_SWAP); op(OP_FLT); xfloat = 0; return 0; } + if (o == s_le) { op(OP_FLE); xfloat = 0; return 0; } + if (o == s_ge) { op(O_SWAP); op(OP_FLE); xfloat = 0; return 0; } + xfloat = 0; + return 0; +} +// true when a type is a float, the same as isfl. +word isfloat(int t) { return isfl(t); } +// float, f32, and f32's packed twin: everything computed as a double +word isfl(word t) { return t && t[0] == TY_FLOAT; } +// ±0 is false, like 0.0. every other float, including NaN, is true. the vm's +// jz stays an integer test, so a float is compared with +0 first. +int ftest() { + if (!xfloat) return 0; + op(OP_FLIT); emit(0); op(OP_FEQ); lit(1); op(18); + xfloat = 0; xty = tbool; + return 0; +} +// a float stored into an int becomes an int, and an int stored into a float +// becomes a float. word keeps the bits and drops the float mark. +int fcoerce(word dst) { + if (!dst || dst[0] == TY_VAR) return 0; + if (isfl(dst)) { + if (!xfloat) { op(OP_I2F); xfloat = 1; xty = tfloat; } + // an f32 variable holds a value rounded to single precision; a packed + // element is rounded when pkst stores it + if (dst == tf32 && xty != tf32 && !ispk(xty)) { xenh("zf2s"); xenh("zs2f"); xty = tf32; } + return 0; + } + if (!xfloat) { + if (tbytes(dst) && !samesz(dst, xty)) tnorm(dst); + return 0; + } + if (isnum(dst)) { op(OP_F2I); xfloat = 0; xty = dst; if (tbytes(dst)) tnorm(dst); return 0; } + if (dst == tword) xfloat = 0; + return 0; +} +// sized integers. a value stored into a variable, field, parameter or array +// element of type t is cut to size first: unsigned types are masked and signed +// ones are masked and then sign-extended (xor the sign bit, subtract it). +// samesz says a value of type b is already in range for type a. +word samesz(word a, word b) { return tbytes(a) && tbytes(b) == tbytes(a) && (a[6] & 1) == (b[6] & 1); } +// narrow the integer on the stack to the width of its type. +int tnorm(word t) { + int b = tbytes(t), s; + if (!b || t[0] != TY_INT) return 0; + lit((1 << (8 * b)) - 1); op(16); + if (t[6] & 1) { s = 1 << (8 * b - 1); lit(s); op(18); lit(s); op(O_SUB); } + return 0; +} +// [byte address] -> [value]: load a packed element of type t (the twin) +int pkld(word t) { + int b = t[5]; + if (b == 1) op(612); else if (b == 2) op(614); else op(616); + pkfix(t); + return 0; +} +// a zero extended packed element on the stack becomes the value of type t +int pkfix(word t) { + int b = t[5], s; + if (t[0] == TY_FLOAT) { xenh("zs2f"); return 0; } // f32: the bits widen to a double + if (t[6] & 1) { s = 1 << (8 * b - 1); lit(s); op(18); lit(s); op(O_SUB); } + return 0; +} +// [value byte-address] -> []: store a packed element of type t +int pkst(word t) { + int b = t[5]; + if (t[0] == TY_FLOAT) { op(O_SWAP); xenh("zf2s"); op(O_SWAP); op(617); return 0; } // f32: round the double to single bits + if (b == 1) op(613); else if (b == 2) op(615); else op(617); + return 0; +} +// an integer address and a field name: the name has to belong to one struct. +// field lookup on a bare address, for untyped code such as p.x where p is a +// plain word. if exactly one struct in the type area has a field called n, that +// is the one meant; with two or more it is ambiguous (the caller reports it). +int fieldscan(int n) { + int i = 0; word t; int k, hit = 0; + fldst = 0; fldi = -1; + while (i < ntl) { + t = typelist[i]; + if (t[0] == TY_STRUCT && t[3] >= 0) { + k = 0; + while (k < t[3]) { + if (t[4][k * 3] == n) { + if (hit) return 2; + hit = 1; fldst = t; fldi = k; + } + k++; + } + } + i++; + } + return hit; +} +// a // comment, with ch on its second slash. one that starts a line joins the +// doc comment being collected, if the line above was part of it, or starts a +// new one; a comment after code on the same line is not documentation. +int doccomment(int nl) { + int cl = line; + nextch(); + if (!nl) { while (ch >= 0 && ch != 10) nextch(); return 0; } + if (dn && dlast != cl - 1) dn = 0; + if (ch == 32) nextch(); + while (ch >= 0 && ch != 10) { doc_char(ch); nextch(); } + while (dn && (dbuf[dn - 1] == 32 || dbuf[dn - 1] == 9 || dbuf[dn - 1] == 13)) dn--; + doc_char(10); + dlast = cl; docpending = 1; + return 0; +} +// block documentation can end on the declaration's line. remove the optional +// leading star on each line, while leaving markdown inside the comment intact. +int docblock(int nl) { + int cl = line, bol = 1, c; + if (!nl || (dn && dlast != cl - 1)) dn = 0; + nextch(); + if (ch == '*') { nextch(); if (ch == '/') { nextch(); return 0; } } + while (ch >= 0) { + if (bol) { + while (ch == 32 || ch == 9) nextch(); + if (ch == '*') { nextch(); if (ch == '/') { cl = line; nextch(); break; } if (ch == 32) nextch(); } + bol = 0; + } + c = ch; nextch(); + if (c == '*' && ch == '/') { cl = line; nextch(); break; } + doc_char(c); + if (c == 10) bol = 1; + } + while (dn && (dbuf[dn - 1] == 32 || dbuf[dn - 1] == 9 || dbuf[dn - 1] == 10 || dbuf[dn - 1] == 13)) dn--; + doc_char(10); dlast = cl; docpending = 1; + return 0; +} +// the lexer proper: skip white space and comments, then read one token into +// tk / tv with its place in tline / toff. numbers (decimal, hex, floats), +// names, char and string literals (b"..." is a byte string), and operators +// all start here. tglue is 1 when the token touches the one before it with no +// white space, 0 when it does not, and 3 for a char literal (so the magic +// number hint can leave 'a' alone). asm blocks use it to read -5 as one +// literal. +int lex1() { + int n = 0, v, dg, nl; + if (pendot) { + pendot = 0; tglue = 1; tline = line; toff = cpos(); + tbuf[0] = '.'; tbuf[1] = 0; tk = T_OP; tv = intern(tbuf); + return 0; + } + tglue = 1; + nl = tline != line; + while (1) { + while (ch == 32 || ch == 9 || ch == 10 || ch == 13) { if (ch == 10) nl = 1; nextch(); tglue = 0; } + if (ch != '/') break; + nextch(); + if (ch == '/') { + if (docs_on) doccomment(nl); else while (ch >= 0 && ch != 10) nextch(); + tglue = 0; continue; + } + if (ch == '*') { + if (docs_on) { docblock(nl); tglue = 0; continue; } + nextch(); + while (ch >= 0) { if (ch == '*') { nextch(); if (ch == '/') break; } else nextch(); } + nextch(); tglue = 0; continue; + } + tline = line; toff = cpos() - 1; tbuf[0] = '/'; oplex(1); return 0; + } + tline = line; toff = cpos(); + if (docpending) { + dline = 0; + if (dn && (tline == dlast || tline == dlast + 1)) dline = tline; + docpending = 0; + } + if (ch < 0) { tk = T_EOF; tv = 0; return 0; } + if (isdig(ch)) { + v = 0; n = 0; + if (ch == '0') { + nextch(); + if (ch == 'x' || ch == 'X') { + nextch(); + while ((dg = hexv(ch)) >= 0) { v = v * 16 + dg; nextch(); } + tk = T_NUM; tv = v; return 0; + } + tbuf[0] = '0'; n = 1; + } + while (isdig(ch)) { n = token_char(n, ch); v = v * 10 + ch - '0'; nextch(); } + if (ch == 'e' || ch == 'E') { n = takeexp(n); return floatok(n); } + if (ch == '.') { + nextch(); + if (isdig(ch) || ch == 'e' || ch == 'E') { + n = token_char(n, '.'); + while (isdig(ch)) { n = token_char(n, ch); nextch(); } + return floatok(takeexp(n)); + } + if (isal(ch)) { pendot = 1; tk = T_NUM; tv = v; return 0; } // 1.field + n = token_char(n, '.'); + return floatok(n); + } + tk = T_NUM; tv = v; return 0; + } + if (isal(ch)) { + while (isidc(ch)) { n = token_char(n, ch); nextch(); } + tbuf[n] = 0; + if (n == 1 && tbuf[0] == 'b' && ch == 34) { + n = 0; nextch(); + while (ch >= 0 && ch != 34) { v = escch(); n = token_char(n, v); } + nextch(); + tbuf[n] = 0; + tk = T_BSTR; tv = intern(tbuf); return 0; + } + tk = T_ID; tv = intern(tbuf); return 0; + } + if (ch == 39) { + nextch(); + tv = escch(); + if (ch != 39) die("bad character literal"); + nextch(); + tk = T_NUM; + tglue = 3; + return 0; + } + if (ch == 34) { + nextch(); + while (ch >= 0 && ch != 34) { v = escch(); n = token_char(n, v); } + nextch(); + tbuf[n] = 0; + tk = T_STR; tv = intern(tbuf); return 0; + } + if (ch == '.') { + nextch(); + if (isdig(ch)) { + n = 0; tbuf[0] = '.'; n = 1; + while (isdig(ch)) { n = token_char(n, ch); nextch(); } + return floatok(takeexp(n)); + } + tbuf[0] = '.'; + if (isopch(ch) && opprefix(1, ch)) return oplex(1); + tbuf[1] = 0; + tk = T_OP; tv = intern(tbuf); + return 0; + } + tbuf[0] = ch; + nextch(); + if (isopch(tbuf[0])) return oplex(1); + tbuf[1] = 0; + tk = T_OP; tv = intern(tbuf); + return 0; +} +// lex1 plus the token's length, so the language server knows its extent +int lex() { + lex1(); + if (toff < 0) toklen = 0; else toklen = cpos() - toff; + return 0; +} +// one token of lookahead: peek() fills pk_*, next() takes it first +int next() { + int i = 1; + ntok++; + if (toff >= 0) { tpend = toff + toklen; tpline = tline; } + if (pq_n) { + tk = pq_tk[0]; tv = pq_tv[0]; tglue = pq_gl[0]; tunpack(pq_ln[0]); + while (i < pq_n) { + pq_tk[i - 1] = pq_tk[i]; + pq_tv[i - 1] = pq_tv[i]; + pq_gl[i - 1] = pq_gl[i]; + pq_ln[i - 1] = pq_ln[i]; + i++; + } + pq_n--; + if (grec) grec_add(); + return 0; + } + next0(); + if (grec) grec_add(); + return 0; +} +// the k-th token after the current one (k from 0 to 3), into pk_tk and pk_tv +int peekat(int k) { + int a, b, c, e; + while (pq_n <= k) { + a = tk; b = tv; c = tglue; e = tpack(); + next0(); + pq_tk[pq_n] = tk; pq_tv[pq_n] = tv; pq_gl[pq_n] = tglue; pq_ln[pq_n] = tpack(); pq_n++; + tk = a; tv = b; tglue = c; tunpack(e); + } + pk_tk = pq_tk[k]; pk_tv = pq_tv[k]; + return 0; +} +// look at the next token without consuming it. +int peek() { return peekat(0); } +// the lowest level of "next token": from a token list if one is the current +// source, otherwise from the lexer. in check mode after an abort it returns +// end of file forever, which unwinds every parsing loop without any special +// case in them. +int next0() { + word f, q; int i; + if (chkabort) { tk = T_EOF; tv = 0; chkfuel++; if (chkfuel > 3000000) halt(); return 0; } + f = R_SRC + isp * 32; + if (f[0] != 1) { lex(); return 0; } + i = f[2]; + if (i < f[3]) { + q = f[1]; + tk = q[1 + i * 3]; + tv = q[2 + i * 3]; + tunpack(q[3 + i * 3]); + f[2] = i + 1; + return 0; + } + if (!f[4]) { tk = T_EOF; tv = 0; return 0; } + src_pop(); // a fed list ran out: continue with the token that followed it + return 0; +} +// token tests. s is an interned spelling, so these are pointer compares. +int is_op(int s) { return tk == T_OP && tv == s; } +// true when the current token is the name s. +int is_id(int s) { return tk == T_ID && tv == s; } +// consume the current token if it is the operator or name s, and say whether it did. +int accept(int s) { + if ((tk == T_OP || tk == T_ID) && tv == s) { next(); return 1; } + return 0; +} +// accept(s) consumes the token if it is s. expect reports "expected s" at the +// end of the previous token (dgprev) when it is missing, and goes into panic. +int expect(int s) { + if (!accept(s)) { + if (!panic && !dry) { + dgprev = 1; + errat(); + dgprev = 0; + put_s("expected '"); put_s(s); put_s("', found "); tokshow(); + errend(); + } + panic = 1; + if (tk == T_EOF) giveup(); + } + return 0; +} +// read an identifier and return its interned handle +int name() { + int n = tv; + if (tk != T_ID) diefound("expected a name"); + nameoff = toff; + next(); + return n; +} + +// ---- operator tables ---- +// a sequence is written like asm: one-char mnemonics, numbers, and names +// (resolved when the operator is used, so image builds stay self-contained) +int seqasm(word s) { + int start = seqtop, n, neg, v, k, c; + while (1) { + while (*s == 32) s++; + if (!*s) break; + n = 0; + while (s[n] && s[n] != 32) { n = token_char(n, s[n]); } + tbuf[n] = 0; + s = s + n; + reserve_R_SEQ(seqtop + 2); + neg = tbuf[0] == '-' && isdig(tbuf[1]); + if (isdig(tbuf[neg])) { + v = 0; k = neg; + while (tbuf[k]) { v = v * 10 + tbuf[k] - '0'; k++; } + if (neg) v = 0 - v; + if ((v >> 62) != 0 && (v >> 62) != -1) { R_SEQ[seqtop] = SEQWORD; R_SEQ[seqtop + 1] = v; seqtop = seqtop + 2; } + else { R_SEQ[seqtop] = v + v + 1; seqtop++; } + } else if (n == 1 && (c = mnemidx(tbuf[0])) >= 0) { + R_SEQ[seqtop] = c + c; seqtop++; + } else { + R_SEQ[seqtop] = SEQREF; R_SEQ[seqtop + 1] = intern(tbuf); seqtop = seqtop + 2; + } + } + return start; +} +// the index of an assembler mnemonic in the table, or -1 when there is none. +int mnemidx(int c) { + int i = 0; + while (mnem[i]) { + if (mnem[i] == c) return i; + i++; + } + return -1; +} +// emit n words of a stored sequence, resolving its references. +int emitseq(int a, int n) { + int e = a + n, s; + while (a < e) { + if (R_SEQ[a] == SEQWORD) { lit(R_SEQ[a + 1]); a = a + 2; } + else if (R_SEQ[a] == SEQREF) { + s = lookup(R_SEQ[a + 1]); + if (s < 0) s = fwdfn(R_SEQ[a + 1]); + if (skind[s] == K_FN) { + fnref(s); + } else if (skind[s] == K_ENH) op(XOP0 + sval[s]); else lit(sval[s]); + a = a + 2; + } else { emit(R_SEQ[a]); a++; } + } + return 0; +} +// add an operator name to the table of known operators, once. +int addkop(int p) { + int i = 0; + while (i < nkop) { + if (kops[i] == p) return 0; + i++; + } + reserve_operators(nkop + 1); + kops[nkop] = p; + nkop++; + return 0; +} +// find a binary operator by its name. +int binfind(int p) { + int i = nbin; + while (i) { + i--; + if (bname[i] == p) return i; + } + return -1; +} +// find a unary operator by its name. +int unfind(int p) { + int i = nun; + while (i) { + i--; + if (uname[i] == p) return i; + } + return -1; +} +// find an assignment operator by its name. +int asgfind(int p) { + int i = nasg; + while (i) { + i--; + if (aname[i] == p) return i; + } + return -1; +} +// add or replace a binary operator. code is a sequence string, or 0 for the +// built-in && (prec 2) and || (prec 1) behaviour, chosen by -1 / -2 in prec. +int add_binop(int nm, int prec, int code) { + int p = intern(nm), i = binfind(p), a, n; + if (i < 0) { reserve_binary(nbin + 1); i = nbin; nbin++; } + bname[i] = p; bprec[i] = prec; + if (prec < 0) { bprec[i] = 0 - prec; blen[i] = prec; bseq[i] = 0; addkop(p); return i; } + bseq[i] = seqasm(code); blen[i] = seqtop - bseq[i]; + addkop(p); + // and its compound assignment, x op= y + n = len_w(nm); + while (1002 + n > tbufcap) { tbuf = chk_resize(tbuf, 0, tbufcap * 2); tbufcap = tbufcap * 2; } + copy_w(tbuf + 1000, nm, n); + tbuf[1000 + n] = '='; tbuf[1001 + n] = 0; + a = intern(tbuf + 1000); + if (prec >= 8 || prec == 5 || prec == 4 || prec == 3) { + if (asgfind(a) < 0) { reserve_assignment(nasg + 1); aname[nasg] = a; nasg++; } + abin[asgfind(a)] = i; + addkop(a); + } + return i; +} +// register a unary operator and the sequence it expands to. +int add_unop(int nm, int code) { + int p = intern(nm), i = unfind(p); + if (i < 0) { reserve_unary(nun + 1); i = nun; nun++; } + uname[i] = p; useq[i] = seqasm(code); ulen[i] = seqtop - useq[i]; + addkop(p); + return i; +} +// register a keyword with its handler and flags. +int add_kw(int nm, int handler, int flags) { + int s = addsym(intern(nm), K_KW, handler); + sflag[s] = F_DEF | flags; + return s; +} + +// ---- locals and the analyzer's view of them ---- +// these warnings add no ownership rules. facts are discarded at control-flow +// joins and after unknown effects, so a possible failure is never called definite. +word af_new(int length, int kind, int place) { + word f; + if (dry || stubgen) return 0; + f = talloc(6); f[0] = length; f[1] = 1; f[2] = kind; + f[3] = place; f[4] = 0; f[5] = -1; + return f; +} +int af_forget() { + int i = lbase; + while (i < nloc) { lafact[i] = 0; i++; } + xaf = 0; return 0; +} +// an unknown callee may change a passed allocation's length or lifetime. +int af_escape(word f) { + if (f) { f[0] = -1; f[1] = 0; } + return 0; +} +int af_store(int i) { + if (!dry) lafact[i] = xaf; + return 0; +} +int af_record(int off, word f) { + int at; + if (!f || !cmode || dry || chkind != 3 || chbase != rsbase || !rsn) return 0; + at = (rsn - 1) * RS_W; + if (rsrec[at] != off) return 0; + copy_w(rsrec + at + 4, f, 5); return 0; +} +// keep declaration previews as well as the facts at each use site. +int af_declaration(int off, word f) { + int i = rsn; + if (!f || !cmode || dry || chkind != 3 || chbase != rsbase) return 0; + while (i) { i--; if (rsrec[i * RS_W] == off) { copy_w(rsrec + i * RS_W + 4, f, 5); return 0; } } + return 0; +} +int af_warning(int place) { + int sv; + if (dry || panic || chkabort || stubgen || !live) return 0; + sv = tpack(); tunpack(place); + if (cmode) dg_open(2); + else { put_s("zc: line "); put_n(tline); put_s(": warning: "); } + tunpack(sv); return 1; +} +int af_warning_end() { if (cmode) dg_close(); else put_c(10); return 0; } +int af_access(word f, int place) { + if (!f || f[1] != 2 || !af_warning(place)) return 0; + put_s("use after free: this storage's lifetime ended on line "); + put_n(f[4] & 0xFFFFFF); af_warning_end(); return 0; +} +int af_index(word f, int place) { + if (!f || f[1] == 2 || f[0] < 0 || !xconst || (xcval >= 0 && xcval < f[0])) return 0; + if (!af_warning(place)) return 0; + put_s("out-of-bounds access: index "); put_n(xcval); + put_s(" is outside length "); put_n(f[0]); af_warning_end(); return 0; +} +// freeing an alias ends the same allocation as freeing its original name. +int af_end(word f, int place) { + if (f && !dry && live) { f[1] = 2; f[4] = place; } + return 0; +} +int af_close(int save) { + int i = save; word f; + if (dry) return 0; + while (i < nloc) { + f = laaddr[i]; if (f) af_end(f, tpack()); + f = lafact[i]; + if (f && fchk && lstate[i] == S_M && lown[i]) af_end(f, tpack()); + i++; + } + return 0; +} +// the analyzer's model of a local. locals are numbered in declaration order +// and every fact about one lives in a parallel array indexed by that number +// (see the data region). for ownership the ones that matter are: +// lstate what the local holds right now: +// S_PLAIN nothing tracked (a number, or untracked memory) +// S_M owns an object; the compiler frees it when the block ends +// S_U owns one that has to leave the block (return it, make +// it the block's value, or assign it to the enclosing block) +// S_D was handed in to be given away, and hasn't been yet +// S_DEAD the object is gone (given away); any use is an error +// S_CHILD the object belongs to a container, lowner says which local +// S_BORROW a .m parameter: usable but not freeable or storable +// lhome the block depth that owns the object (fdepth where it was declared) +// lown 1 once the local has owned an object, lused the line of its last use +// ltag the tag written on the declaration, plus the flag bits (float, +// byte string, o3 register, hint bits) +// fchk says the function being compiled is analyzed at all (unchecked turns +// it off) and live says the code just emitted is reachable. ownership rules +// return when fchk is off; definite storage warnings also inspect unchecked code. +// findloc looks a name up among the current function's locals, innermost first. +// name heads and shadow links keep declaration and use lookup independent of +// the number of earlier locals. dropping a scope restores the previous heads. +int local_bucket(int n, int cap) { return ((n ^ (n >> 16)) * 0x9E37) & (cap - 1); } +int local_key(int n) { + int k = local_bucket(n, lkeycap); + while (lkeys[k] && lkeys[k] != n) k = (k + 1) & (lkeycap - 1); + return k; +} +int local_bind(int n, int i) { + int k, j, at, cap; word keys, heads; + if (lkeyn + lkeyn >= lkeycap) { + keys = lkeys; heads = lheads; cap = lkeycap * 2; + lkeys = chk_resize(0, 0, cap); lheads = chk_resize(0, 0, cap); + j = 0; + while (j < lkeycap) { + if (keys[j]) { at = local_bucket(keys[j], cap); while (lkeys[at]) at = (at + 1) & (cap - 1); lkeys[at] = keys[j]; lheads[at] = heads[j]; } + j++; + } + lkeycap = cap; + } + k = local_key(n); + if (!lkeys[k]) { lkeys[k] = n; lheads[k] = -1; lkeyn++; } + lprev[i] = lheads[k]; lheads[k] = i; return 0; +} +int local_drop(int save) { + int i = nloc, k; + while (i > save) { i--; k = local_key(lname[i]); lheads[k] = lprev[i]; } + nloc = save; return 0; +} +int findloc(int n) { + int k = local_key(n), i; + if (!lkeys[k]) return -1; + i = lheads[k]; + while (i >= nloc) i = lprev[i]; + if (i >= lbase) return i; + return -1; +} +// declare a local: take the next slot in the frame (fslots) and reset every +// per-local fact so a reused index never inherits the old variable's state +int newloc(int n) { + int i = nloc; + if (nloc >= loccap) reserve_locals(nloc + 1); + nloc++; + lname[i] = n; local_bind(n, i); lslot[i] = fslots; lhome[i] = fdepth; lstate[i] = S_PLAIN; lsmark(0, n); + lowner[i] = 0; + lrsoff[i] = 0 - 1; lrskind[i] = RS_LOCAL; + lused[i] = 0; + larr[i] = 0; + lown[i] = 0; + ltype[i] = tword; + ltag[i] = 0; + o3cache[i] = 0; + lafact[i] = 0; laaddr[i] = 0; + return i; +} +// a local's known lower bound, and the array whose length it holds. -1 is unknown. +// the table is dropped as scopes close, so a reused slot does not keep the old fact. +word getlo(int i) { + int k = 0; + while (k < nfact) { + if (floc[k] == i) return flo[k]; + k++; + } + return -1; +} +// the length of the recorded fact for line i, or -1 when there is none. +word getln(int i) { + int k = 0; + while (k < nfact) { + if (floc[k] == i) return flen[k]; + k++; + } + return -1; +} +// record the range lo to lo+ln for line i. +int putfact(int i, int lo, int ln) { + int k = 0; + while (k < nfact) { if (floc[k] == i) { flo[k] = lo; flen[k] = ln; return 0; } k++; } + if (lo < 0 && ln < 0) return 0; + if (nfact >= factcap) { + floc = chk_resize(floc, nfact, factcap * 2); flo = chk_resize(flo, nfact, factcap * 2); flen = chk_resize(flen, nfact, factcap * 2); + factcap = factcap * 2; + } + floc[nfact] = i; flo[nfact] = lo; flen[nfact] = ln; nfact++; + return 0; +} +// drop the recorded facts from line from onward. +int fdrop(int from) { + int k = 0, w = 0; + while (k < nfact) { + if (floc[k] < from) { + if (w != k) { floc[w] = floc[k]; flo[w] = flo[k]; flen[w] = flen[k]; } + w++; + } + k++; + } + nfact = w; + return 0; +} +// a loop fact stores three full local indices: the +// induction variable, its bound and the array the bound is the length of. a +// loop "for (i = 0; i < len(a); i++)" records (i, bound, a) in lpfact, which +// is how proven() knows that a[i] needs no bounds check inside it. +int pfact(int iv, int bd, int ar) { word p = talloc(3); p[0] = iv; p[1] = bd; p[2] = ar; return p; } +// the induction variable of a loop proof. +int p_iv(int w) { return w[0]; } +// the array's bound in a loop proof. +int p_bd(int w) { return w[1]; } +// the array a loop proof refers to. +int p_ar(int w) { return w[2]; } +// a store to the induction variable, the bound, or the array ends the proof +int bpoison(int i) { + int k = lsp, w; + while (k > loopbase) { + k--; + w = lpfact[k]; + if (w >= 0 && (p_iv(w) == i || p_bd(w) == i || p_ar(w) == i)) lpfact[k] = -1; + } + return 0; +} +// note a store to a local: a constant or a length it now holds. +int note_store(int i) { + int lo = -1, ln = -1; + if (xconst && xcval >= 0) lo = xcval; + if (xlenloc >= 0) ln = xlenloc; + putfact(i, lo, ln); + bpoison(i); + return 0; +} +// i >= 0 and i < len(a), proved by the loop test and a step of +1 +int proven(int a, int ix) { + int k = lsp, w; + if (ltype[a][0] != TY_DYN || !ltype[a][3] || tbytes(ltype[a][4])) return 0; + while (k > loopbase) { + k--; + w = lpfact[k]; + if (w >= 0 && p_iv(w) == ix && p_ar(w) == a) return 1; + } + return 0; +} +// follow child links up to the local that really owns the object +int lroot(int i) { + while (lstate[i] == S_CHILD) i = lowner[i]; + return i; +} +// true when a local has an ownership state the analyzer follows. +word tracked(int i) { return lstate[i] != S_PLAIN; } +// a child link with nothing owned: a pointer computed from some other variable +word isalias(int i) { return lstate[i] == S_CHILD && !lown[i]; } +// record the last-use line and saturating count read by the hints ("only +// handed off once"). +int luse(int i) { + lused[i] = tline; + if ((ltag[i] >> 56) < 100) ltag[i] = ltag[i] + LCNT1; + return 0; +} +// touch records a use of a tracked local and rejects dead objects. a d object +// read in an inner block moves there, where the block owns it as m. +int touch(int i) { + int r; + luse(i); + ltag[i] = ltag[i] | LREAD; + if (!fchk) return 0; + r = lroot(i); + luse(r); + if (lstate[r] == S_DEAD) { cerrn(i, "used after it was given away"); return 0; } + // a d object that shows up in a deeper block is given to that block + if (lstate[r] == S_D && lhome[r] < fdepth) { lhome[r] = fdepth; lstate[r] = S_M; lown[r] = 1; } + return 0; +} +// an expression of class c used as a plain value (a lend, an operand). a new +// object can't be used that way, since nothing would own it. +int plainuse(int c) { + if (c == C_FRESH) cerr("a new object can't be used as a plain value; bind it to a name first"); + if (c >= C_VAR) touch(c - C_VAR); + return C_PLAIN; +} +// the size class of an n word object, for a constant-size new +int szclass(int n) { + int k = 3; + while (pow2[k] < n + HDR) k++; + return k; +} +// freeing an owned local. one that holds a typed object with no pointer +// fields goes back through recycle_leaf, which knows its class +int freevar(int i) { + laddr(lslot[i]); op(O_LD); + if (lleaf[i] && !o3on && !dry && lookup(s_recycle_leaf) >= 0) { + lit(lleaf[i]); + lit(szclass(lnsz[i])); + callname(s_recycle_leaf); + } + else callname(s_recycle); op(O_DROP); lit(0); laddr(lslot[i]); op(O_ST); return 0; +} +// leaving every block deeper than or equal to dep: free what is owned, and +// complain about what should have left already. final means the block really +// ends here rather than an early exit. +int exit_vars(int dep, int final) { + int i = nloc, st; + if (!fchk || !live) return 0; + while (i > lbase) { + i--; + if (lhome[i] < dep) continue; + st = lstate[i]; + if (st == S_M) { + freevar(i); + } else if (st == S_U) { + cerrn(i, "u object must leave this block (block value, assignment to the enclosing block, or return)"); + } + else if (st == S_D) cerrn(i, "d object was never given away"); + } + return 0; +} +// an m object has to be used somewhere in the block that owns it, on any path +// block ends. +int unused_check(int from) { + while (from < nloc) { + if (fchk && lown[from] && !lused[from]) serrn(lname[from], "m object is never used"); + from++; + } + return 0; +} +// what a block's own variables show once it ends (check mode only) +int hint_name(int i, int msg) { + if (!hint_begin(lpos[i])) return 0; + put_s(lname[i]); put_s(msg); + dg_close(); + return 0; +} +// report the locals declared from token from onward that were never used. +int local_hints(int from) { + int i = from, f; + if (!cmode || dry || panic || chkabort || stubgen) return 0; + while (i < nloc) { + f = ltag[i]; + if ((f & LDECL) && !larr[i]) { + if (!(f & LREAD)) hint_name(i, " is never read"); + else if ((f & LCONST) && !(f & LASG)) hint_name(i, " is never reassigned; make it a const"); + else if (fchk && lstate[i] == S_M && lown[i] && lnsz[i] > 0 && lnsz[i] <= 64) hint_name(i, " never leaves this function; a stack buffer (let name[n]) avoids the heap"); + else if (fchk && lstate[i] == S_M && lown[i] && lused[i] && tline - lused[i] >= 4) hint_name(i, " is last used several lines above the end of its block; close the block sooner so it is freed sooner"); + } + i++; + } + return 0; +} +// block_close runs the analyzer's end-of-block work in order: hints for the +// block's variables, o3 registers go back to the pool, an m object that was +// never used is an error, exit_vars frees what is owned and complains about +// what should have left, aliases whose owner dies are marked dead, then the +// block's locals are dropped. unused_check comes first because an unused m +// object is an error, not a free. +int block_close(int save) { + int i = save, k; + af_close(save); + local_hints(save); + if (o3on) { + while (i < nloc) { + k = (ltag[i] >> 8) & 7; + if ((ltag[i] & 16) && k && no3free < 8) { + if (getflt(i)) k = 0 - k; + o3free[no3free] = k; + no3free++; + } + i++; + } + } + i = lbase; + unused_check(save); + exit_vars(fdepth, 1); + alias_end(fdepth, save); + while (i < save) { if (lhome[i] >= fdepth) { lstate[i] = S_DEAD; lhome[i] = fdepth - 1; } i++; } + local_drop(save); + fdrop(save); + return 0; +} + +// an alias in an outer block whose owner is about to disappear dies with it +int alias_end(int dep, int save) { + int i, r; + if (!fchk) return 0; + i = lbase; + while (i < save) { + if (isalias(i)) { + r = lroot(i); + if (r >= save || lhome[r] >= dep || lstate[r] == S_DEAD) lstate[i] = S_DEAD; + } + i++; + } + return 0; +} +// snapshots: [lbase] [nloc] then state, owner, home per local +// save the states of all current locals; returns the snapshot's offset +int snap_push() { + int b = snaptop, i = lbase; word p; + af_forget(); + reserve_R_SNAP(b + 2 + 3 * (nloc - lbase)); + p = R_SNAP + b; + p[0] = lbase; p[1] = nloc; + p = p + 2; + while (i < nloc) { p[0] = lstate[i]; p[1] = lowner[i]; p[2] = lhome[i]; p = p + 3; i++; } + snaptop = p - R_SNAP; + return b; +} +// drop the snapshot at base b. +int snap_pop(int b) { snaptop = b; return 0; } +// restore the local states saved in snapshot b. +int snap_restore(int b) { + word p = R_SNAP + b; int i = p[0], e = p[1]; + af_forget(); + if (e > nloc) e = nloc; + p = p + 2; + while (i < e) { lstate[i] = p[0]; lowner[i] = p[1]; lhome[i] = p[2]; p = p + 3; i++; } + return 0; +} +// true for a plain or dead local state, which merge to dead. +int empty(int s) { return s == S_PLAIN || s == S_DEAD; } +// compare the current states with a snapshot: -1 if they agree, else the +// first local that differs. plain and dead agree and merge to dead. +int snap_cmp(int b) { + word p = R_SNAP + b; int i = p[0], e = p[1], s; + if (e > nloc) e = nloc; + p = p + 2; + while (i < e) { + s = lstate[i]; + if (empty(s) && empty(p[0])) { if (p[0] == S_DEAD) lstate[i] = S_DEAD; } + else if (s != p[0] || lhome[i] != p[2] || (s == S_CHILD && lowner[i] != p[1])) return i; + p = p + 3; i++; + } + return -1; +} +// the state names used in join and loop messages +int statename(int s) { + if (s == S_PLAIN) return "empty"; + if (s == S_M) return "m"; + if (s == S_U) return "u"; + if (s == S_D) return "d"; + if (s == S_DEAD) return "given away"; + if (s == S_CHILD) return "owned by a container"; + return "borrowed"; +} +// report that local i ends two paths in different states +int merge_err(int i, int sa, int what) { + if (!fchk || panic || dry) return 0; + errat(); put_s(lname[i]); put_s(": "); put_s(what); + put_s(" (one path leaves it "); + put_s(statename(sa)); + put_s(", another "); + put_s(statename(lstate[i])); + put_s(")"); + errend(); + return 0; +} + +// ---- snapshots and comparing paths ---- +// to analyze an if, the compiler snapshots the locals' states before the +// branch, compiles the then path, restores the snapshot for the else path, and +// then compares or merges the two. a snapshot is [lbase, nloc] followed by +// state, owner and home for each local; the stack of them is at R_SNAP. +// ---- joins ---- +// where control flow comes back together (after an if, a loop, a switch or +// ?:) every variable has to agree across the paths that reach the join. one +// exception: a variable that became d, or was given away, on some path is dead +// after the join, and the paths where it's still alive free it on the way in +// (fixups). u objects and borrows don't get this treatment: a u object still +// has to leave, and a borrow never changes. + +int extop; [JOIN_PATHS]word jps_initial; word jps = jps_initial; int jpscap = JOIN_PATHS, njps; // the paths being joined: [lbase, nloc, triples] +// accessors on a path record p (a snapshot or an exit record): the state, +// owner and home of local i on that path. locals declared after the snapshot +// read as plain. +word jst(word p, int i) { + if (i >= p[1]) return S_PLAIN; + return p[2 + 3 * (i - p[0])]; +} +// the owner recorded in a jump's state for local i. +word jow(word p, int i) { return p[3 + 3 * (i - p[0])]; } +// the jump's marker for local i, in the same record as jow. +word jhm(word p, int i) { return p[4 + 3 * (i - p[0])]; } +// the states at a break, with the jump to patch: [site] then a snapshot +word exit_rec(int site) { + word p; int i = lbase; word q; + reserve_R_EXIT(extop + 3 + 3 * (nloc - lbase)); p = R_EXIT + extop; + p[0] = site; p[1] = lbase; p[2] = nloc; + q = p + 3; + while (i < nloc) { q[0] = lstate[i]; q[1] = lowner[i]; q[2] = lhome[i]; q = q + 3; i++; } + extop = q - R_EXIT; + return p; +} +// jps is the set of paths about to be joined. jadd adds one, jsame compares +// what two paths say about a local, and join folds them all into the current +// states (reporting any local that disagrees) +int jadd(int p) { + if (njps >= jpscap) { jps = chk_resize(jps, njps, jpscap * 2); jpscap = jpscap * 2; } + jps[njps] = p; + njps++; + return 0; +} +// true when the states of two jumps agree for local i. +word jsame(int p, int q, int i) { + int s = jst(p, i), t = jst(q, i); + if (empty(s) && empty(t)) return 1; + return s == t && jhm(p, i) == jhm(q, i) && (s != S_CHILD || jow(p, i) == jow(q, i)); +} +// join the paths in jps over the locals [lbase, n0) into the current states +int join(int n0, int what) { + int i = lbase, j, p, s, s0, dead, okdead, bad; + af_forget(); + if (!njps) return 0; + while (i < n0) { + p = jps[0]; s0 = jst(p, i); + dead = 0; okdead = 1; bad = -1; + j = 0; + while (j < njps) { + s = jst(jps[j], i); + if (s == S_DEAD || s == S_D) dead = 1; + if (s == S_U || s == S_BORROW) okdead = 0; + if (!jsame(p, jps[j], i)) bad = j; + j++; + } + lstate[i] = s0; lowner[i] = jow(p, i); lhome[i] = jhm(p, i); + if (bad < 0) { + if (empty(s0)) { + j = 0; + while (j < njps) { + if (jst(jps[j], i) == S_DEAD) lstate[i] = S_DEAD; + j++; + } + } + } else if (dead && okdead) lstate[i] = S_DEAD; + else if (fchk && !panic && !dry) { + errat(); put_s(lname[i]); put_s(": "); put_s(what); + put_s(" (one path leaves it "); + put_s(statename(s0)); + put_s(", another "); + put_s(statename(jst(jps[bad], i))); + put_s(")"); + errend(); + } + i++; + } + return 0; +} +// on path p: count (and with doit, emit) the frees for what the join made dead but p still owns +int fixups(int p, int n0, int doit) { + int i = lbase, n = 0, s; + if (!fchk) return 0; + while (i < n0) { + s = jst(p, i); + if (lstate[i] == S_DEAD && (s == S_M || s == S_D)) { n++; if (doit) freevar(i); } + i++; + } + return n; +} +// a jump back to the top of a loop: whatever the loop owns has to be as it was +int backedge(int s0) { + word p = R_SNAP + s0; int i = p[0], e = p[1], s; + if (!fchk || !live) return 0; + if (e > nloc) e = nloc; + while (i < e) { + s = lstate[i]; + if (empty(s) && empty(jst(p, i))) { if (jst(p, i) == S_DEAD) lstate[i] = S_DEAD; } + else if (s != jst(p, i) || lhome[i] != jhm(p, i) || (s == S_CHILD && lowner[i] != jow(p, i))) { + if (s == S_DEAD || s == S_D) { + cerrn(i, "is given away inside a loop that comes back around to it; give it away on a path that leaves the loop"); + } + else if (!panic && !dry) { + errat(); + put_s(lname[i]); + put_s(": changes state inside a loop; each pass has to leave it as it found it (it went from "); + put_s(statename(jst(p, i))); put_s(" to "); put_s(statename(s)); put_s(")"); errend(); + } + } + i++; + } + return 0; +} +// joins two paths that end at the same point, like the branches of if/else: +// path a jumped (through the hole at ja) and path b falls through to here +// (the then path's snapshot is sa; livea / livebv say whether each path can +// reach the join at all, since a path that returned is not part of it) +int join2(int sa, int livea, int ja, int livebv, int n0, int what) { + int sb, nt, cj; + sb = snap_push(); + njps = 0; if (livea) jadd(R_SNAP + sa); if (livebv) jadd(R_SNAP + sb); + join(n0, what); + if (livebv) fixups(R_SNAP + sb, n0, 1); + nt = 0; if (livea) nt = fixups(R_SNAP + sa, n0, 0); + if (nt) { cj = hole(); op(O_JMP); setlit(ja, here); fixups(R_SNAP + sa, n0, 1); setlit(cj, here); } + else setlit(ja, here); + live = livea || livebv; + return 0; +} + +// the rules below are the heart of the analyzer. each is a function the parser +// calls at one kind of place where a value moves: recast (u*x), argument +// passing, assignment, return, block values, and discarding. the argument +// c they take is an expression class: C_PLAIN (an ordinary word), C_FRESH (a +// new object nobody owns yet) or C_VAR + n (a bare local, number n), which is +// how the analyzer knows the thing being moved is a variable it tracks. +// ---- analyzer rules for the places values flow ---- +// recast: x as u / x as d / x as m +int recast(int c, int t) { + int i, st; + if (c == C_FRESH && t == S_M) return C_PLAIN; // m*f(): the unnamed result is let go on purpose, so it can be discarded + if (!fchk) return c; + if (c < C_VAR) { cerr("only a named owned object can be recast"); return c; } + i = c - C_VAR; + st = lstate[i]; + luse(i); + if (st == S_CHILD) cerrn(i, "belongs to a container; recast the container instead"); + else if (st == S_BORROW) cerrn(i, "is borrowed and can't be recast"); + else if (st == S_PLAIN) cerrn(i, "isn't an owned object"); + else if (st == S_DEAD) cerrn(i, "used after it was given away"); + else if (t == S_M && st != S_M) cerrn(i, "can't be recast back to m"); + else lstate[i] = t; + return c; +} +// an argument for a parameter tagged t (0 plain, 1 m, 2 d) +int argcheck(int c, int t) { + int i, st; + if (!fchk) return 0; + if (c == C_FRESH) { + if (t != 2) { + cerr("a new object can only go straight to a d parameter; bind it to a name first"); + } else { + xgive = 1; + } + return 0; + } + if (c < C_VAR || !tracked(c - C_VAR)) return 0; + i = c - C_VAR; + st = lstate[i]; + if (lstate[lroot(i)] == S_DEAD) { cerrn(i, "used after it was given away"); return 0; } + if (t <= 1) { // untagged parameters are m: a lend + if (st == S_D) { + cerrn(i, "is d; pass it to a d parameter (lending it would lose it)"); + return 0; + } + touch(i); + return 0; + } + if (st == S_D) { lstate[i] = S_DEAD; luse(i); xgive = 1; return 0; } + if (st == S_M) cerrn(i, "is m; write d*x to give it away"); + else if (st == S_U) cerrn(i, "is u; write d*x to give it away instead of returning it"); + else if (st == S_CHILD) cerrn(i, "belongs to a container and can't be given away alone"); + else cerrn(i, "is borrowed and can't be given away"); + return 0; +} +// child, owner on the stack (owner on top). write owner into the child's +// header and leave the child. a shared child keeps the owner it has. a null +// child's address is out of range and the store does not land. +// o3 code can't call own(), so this is own() inlined: skip the store when the +// child is shared (flag bit 8) and otherwise write the owner into the child's +// header word p[-2] +int o3own() { + int sk, st; + lit(1); op(O_PICK); lit(4); op(O_SUB); op(O_LD); lit(8); op(16); + sk = hole(); op(O_JZ); + op(O_DROP); + st = hole(); op(O_JMP); + setlit(sk, here); + lit(1); op(O_PICK); lit(2); op(O_SUB); op(O_SWAP); op(O_SWAP); op(O_ST); + setlit(st, here); + return 0; +} +// storing a child into container local p: emit the call that records the +// owner in the child's header. a growable array's elements live in its items +// object, so that is the owner there. +int emitown(int p) { + laddr(lslot[p]); op(O_LD); + // a []T's elements live in its items object + if (ltype[p][0] == TY_DYN) { + lit(2); + op(O_ADD); + op(O_LD); + } + if (o3on && !mdbg) o3own(); + else callname(s_own); + if (o3on && hasptr(xty) && !xstamp) { lit(xty); callname(s_fatten); } + return 0; +} +// a store of class rc into an lvalue of kind k (local or container index i) +int assigncheck(int k, int i, int rc) { + int st, j, r, js, bor; + bor = xbor; xbor = -1; + if (!fchk) return C_PLAIN; + j = -1; + if (rc >= C_VAR && tracked(rc - C_VAR)) j = rc - C_VAR; + if (j >= 0 && lstate[lroot(j)] == S_DEAD) { + cerrn(j, "used after it was given away"); + return C_PLAIN; + } + if (k == LV_LOCAL) { + st = lstate[i]; + if (isalias(i)) st = S_PLAIN; // a borrow isn't an owner; overwriting it drops the alias + if (j < 0 && rc != C_FRESH) { + if (!empty(st)) cerrn(i, "still owns an object; overwriting it would leak it"); + else if (bor >= 0) { lstate[i] = S_CHILD; lowner[i] = bor; lown[i] = 0; } + else lstate[i] = S_PLAIN; + return C_PLAIN; + } + if (!empty(st)) { + cerrn(i, "still owns an object; overwriting it would leak it"); + return C_PLAIN; + } + if (rc == C_FRESH) { + lstate[i] = S_M; + lused[i] = 0; + lown[i] = 1; + if (ltag[i] & 15) lstate[i] = ltag[i] & 15; + return C_PLAIN; + } + js = lstate[j]; + if (js == S_U && lhome[i] == lhome[j] - 1) { // handing a u object up one block + lstate[i] = S_M; + lused[i] = 0; + lown[i] = 1; + lstate[j] = S_DEAD; + luse(j); + if (ltag[i] & 15) lstate[i] = ltag[i] & 15; + return C_PLAIN; + } + if (js == S_U) { + cerrn(j, "is u; it can only be handed to a variable of the enclosing block, or returned"); + } + else cerrn(j, "is an owned object and can't be copied into another variable"); + return C_PLAIN; + } + if (k == LV_ELEM) { + r = lroot(i); + st = lstate[r]; + touch(i); + if (rc == C_FRESH) { emitown(i); return C_PLAIN; } + if (j < 0) return C_PLAIN; + js = lstate[j]; + if (js == S_CHILD) cerrn(j, "already belongs to a container"); + else if (js == S_BORROW) cerrn(j, "is borrowed and can't be stored"); + else if (st == S_BORROW) { + if (js == S_U) { + cerrn(j, "is u; it has to leave through the block above, not into a borrowed object"); + } + else { lstate[j] = S_DEAD; luse(j); emitown(i); } // storing it gives it away + } else if (js != st) cerrn(j, "its tag doesn't match its container's (both have to be m, u or d)"); + else if (lhome[j] < lhome[r]) cerrn(j, "lives in an outer block than the container it's stored into"); + else { lstate[j] = S_CHILD; lowner[j] = i; luse(j); emitown(i); } + return C_PLAIN; + } + // a global, or memory the analyzer doesn't track: the collector takes it + if (j >= 0) { + if (lstate[j] == S_D) { lstate[j] = S_DEAD; luse(j); } + else cerrn(j, "must be d to be stored in a global or in untracked memory (the collector takes it over)"); + } + return C_PLAIN; +} +// return c: an owned object (d, or u after u*x) goes up with the return, and the +// function's return is owned: declared with .u, or inferred from this return when +// the function has no .u (its callers compiled after this point see it). an m +// object is lent, not given, so returning it is an error, and so is a plain word +// from an owned function that was called before its body. +int retcheck(int c) { + int i; + if (!fchk) return 0; + i = -1; + if (c >= C_VAR && tracked(c - C_VAR)) i = c - C_VAR; + if (i >= 0 && lstate[lroot(i)] == S_DEAD) { cerrn(i, "used after it was given away"); return 0; } + if (i >= 0 && (lstate[i] == S_D || (lstate[i] == S_M && lown[i])) && (fret || finfer)) lstate[i] = S_U; // returning an owned object (a d object, or a local that owns one) gives it up, as u*x does + if (!fret && finfer && (c == C_FRESH || (i >= 0 && lstate[i] == S_U))) { + if (sflag[finsym] & F_EARLY) serrn(sname[finsym], "is called before its body, so its owned return can't be known there; declare the return type with .u"); + ssig[finsym] = ssig[finsym] | 1; // the return is owned: callers read it from the signature + sflag[finsym] = sflag[finsym] | F_RETOWN; + fret = 1; finfer = 0; + } + if (fret) { + if (i >= 0) { + if (lstate[i] == S_U) { lstate[i] = S_DEAD; luse(i); } + else cerrn(i, "has to be u to be returned"); + } + return 0; + } + if (c == C_FRESH) cerr("returns a new object; give the function a .u return type"); + else if (i >= 0) cerrn(i, "is owned and can't be returned from a function whose return type isn't .u"); + return 0; +} +// the last expression of a block expression +int handup(int c) { + int i; + if (!fchk) return C_PLAIN; + if (c == C_FRESH) return C_FRESH; + if (c < C_VAR || !tracked(c - C_VAR)) return C_PLAIN; + i = c - C_VAR; + if (lstate[i] == S_U && lhome[i] == fdepth) { lstate[i] = S_DEAD; luse(i); return C_FRESH; } + if (lstate[i] == S_U) cerrn(i, "only a u object of this block can be its value"); + else cerrn(i, "has to be u to be handed up as the block's value"); + return C_PLAIN; +} +// throw away an expression result, which is an error for a fresh object. +int discard(int c) { + if (c == C_FRESH) cerr("a new object (or a u result) can't be thrown away"); + else if (c >= C_VAR) touch(c - C_VAR); + xbor = -1; + return 0; +} + +// ---- types ---- +// types are a compiler primitive: records in the type area, hash-consed so +// the same type is always the same pointer and comparing types is comparing +// words. untyped code is typed word, which goes with anything. the records +// stay in memory after compiling, so programs can inspect them. +// t[0] kind t[1] size in words t[2] name t[3] n t[4] a t[5] b t[6] c t[7..] extra +// ptr: a = element arr: a = element, n = length +// fn: a = return type, n = parameter count, b = signature (u m d tags), extra = parameter types +// struct: n = field count (-1 until laid out), a = fields (name, type, offset), +// b = generic template, c = type arguments [n, t1, t2 ...] +// var: a type parameter, n = its index +// gen: a generic struct: n = parameter count, a = fields (name, type), b = field count, c = parameter names + + +// the type area is a bump allocator of its own (tytop .. R_TYEND). talloc +// hands out zeroed words, tnew makes a record and adds it to the list of all +// types (R_TY), and the constructors below look that list up first, which is +// the hash-consing: asking for the same pointer type twice returns the same +// record. the list is searched linearly, which is fine for the few hundred +// types a program has. +unchecked int talloc(int n) { + int p = tytop; + if (tytop + n > tyend) { + p = new(n + 16385); p[-4] = p[-4] | F_INTERNAL; p[0] = typeblocks; typeblocks = p; + p++; tytop = p; tyend = p + n + 16384; + } + tytop = tytop + n; + fill_w(p, 0, n); + return p; +} +// make a type record of the given kind, size and name in the type area. +word tnew(int kind, int size, int nm, int extra) { + word t = talloc(7 + extra); + t[0] = kind; t[1] = size; t[2] = nm; + if (ntl >= typecap) { typelist = chk_resize(typelist, ntl, typecap * 2); typecap = typecap * 2; } + typelist[ntl] = t; ntl++; + return t; +} +// the pointer type to type e, reused when it already exists. +word tptr(int e) { + int i = 0; word t; + while (i < ntl) { t = typelist[i]; if (t[0] == TY_PTR && t[4] == e) return t; i++; } + t = tnew(TY_PTR, 1, 0, 0); t[4] = e; + return t; +} +// sized integers (i8 u8 i16 u16 i32 u32) are TY_INT records with t[5] = bytes, +// t[6] bit 0 = signed, and t[4] = the "packed view" twin: the same name with +// t[6] bit 1 set. a variable, parameter or field of a sized type is an +// ordinary word that is cut to size whenever it is stored. an element of an +// array or a pointer of a sized type is stored packed, 8, 4 or 2 to a word, +// and indexing yields the twin, whose lvalue is a byte address. +word tbytes(word t) { if (t[0] == TY_INT || t[0] == TY_FLOAT) return t[5]; return 0; } +// true for a packed integer or float type. +word ispk(word t) { return (t[0] == TY_INT || t[0] == TY_FLOAT) && (t[6] & 2); } +// the type a packed type stores its elements as, or the type itself. +word tpk(word t) { if ((t[0] == TY_INT || t[0] == TY_FLOAT) && t[4]) return t[4]; return t; } +// an enhancement used by the compiler itself, inline when the runtime has it +int xenh(int name) { + int s = lookup(intern(name)); + if (s >= 0 && skind[s] == K_ENH) { op(XOP0 + sval[s]); return 0; } + callname(intern(name)); + return 0; +} +// words an array of n elements of e takes: packed for a sized integer +word tasz(word e, int n) { + if (tbytes(e)) return (n * e[5] + 7) >> 3; + return n * tsz(e); +} +// the fixed array type of n elements of type e, reused when it already exists. +word tarr(int e, int n) { + int i = 0; word t; + while (i < ntl) { t = typelist[i]; if (t[0] == TY_ARR && t[4] == e && t[3] == n) return t; i++; } + t = tnew(TY_ARR, tasz(e, n), 0, 0); t[4] = e; t[3] = n; + return t; +} +// a sized integer type and its packed twin +word tsized(int name, int bytes, int sgn) { + word t = tnew(TY_INT, 1, intern(name), 0), twin = tnew(TY_INT, 1, t[2], 0); + t[5] = bytes; t[6] = sgn; t[4] = twin; + twin[5] = bytes; twin[6] = sgn | 2; + addsym(t[2], K_TYPE, t); + return t; +} +// the function type with this return, parameter list and signature. +word tfn(int ret, int np, word ps, int sig) { + int i = 0, j; word t; + while (i < ntl) { + t = typelist[i]; + if (t[0] == TY_FN && t[4] == ret && t[3] == np && t[5] == sig) { + j = 0; while (j < np && t[7 + j] == ps[j]) j++; + if (j == np) return t; + } + i++; + } + t = tnew(TY_FN, 1, 0, np); t[4] = ret; t[3] = np; t[5] = sig; + copy_w(t + 7, ps, np); + return t; +} +// []T, or fixed []T +word tdyn(int e, int f) { + int i = 0; word t; + while (i < ntl) { t = typelist[i]; if (t[0] == TY_DYN && t[4] == e && t[3] == f) return t; i++; } + t = tnew(TY_DYN, 1, 0, 0); t[4] = e; t[3] = f; + return t; +} +// the type variable with index i, reused when it already exists. +word tvar(int i, int nm) { + int k = 0; word t; + while (k < ntl) { t = typelist[k]; if (t[0] == TY_VAR && t[3] == i) return t; k++; } + t = tnew(TY_VAR, 1, nm, 0); t[3] = i; + return t; +} +// the instance of generic struct g with type arguments args[0..n). found if it +// already exists, otherwise created unlaid-out (n = -1), so that mentioning +// vec inside vec's own methods does not recurse; tresolve lays it out. +word tinst(word g, word args, int n) { + int i = 0, j; word t, v; + while (i < ntl) { + t = typelist[i]; + if (t[0] == TY_STRUCT && t[5] == g) { + v = t[6]; j = 0; while (j < n && v[1 + j] == args[j]) j++; + if (j == n) return t; + } + i++; + } + v = talloc(n + 1); v[0] = n; copy_w(v + 1, args, n); + t = tnew(TY_STRUCT, 0, g[2], 0); t[3] = -1; t[5] = g; t[6] = v; + return t; +} +// lay out a generic instance on first use +word tresolve(word t) { + word g, f; int n, i; word fs; int off = 0, ft; [8]word bs; + if (t[0] != TY_STRUCT || t[3] >= 0) return t; + if (t[3] == -2) { serr("a struct can't contain itself by value"); t[3] = 0; return t; } + t[3] = -2; + g = t[5]; n = g[5]; f = g[4]; + fill_w(bs, 0, 8); copy_w(bs, t[6] + 1, t[6][0]); + fs = talloc(n * 3 + 1); + i = 0; + while (i < n) { + ft = subst(f[i + i + 1], bs); + fs[i * 3] = f[i + i]; fs[i * 3 + 1] = ft; fs[i * 3 + 2] = off; + off = off + tsz(ft); + i++; + } + t[4] = fs; t[1] = off; t[3] = n; + return t; +} +// size of a type in words, laying out a generic instance first if it needs it +word tsz(word t) { + if (t[0] == TY_STRUCT && t[3] < 0) tresolve(t); + return t[1]; +} +// replace type parameters with what they were bound to (unbound ones become word) +word subst(word t, word bs) { + int k = t[0], i, n; [24]word v; word vec; + if (k == TY_VAR) { if (bs[t[3]]) return bs[t[3]]; return tword; } + if (k == TY_PTR) return tptr(subst(t[4], bs)); + if (k == TY_ARR) return tarr(subst(t[4], bs), t[3]); + if (k == TY_FN) { + n = t[3]; + i = 0; + while (i < n) { v[i] = subst(t[7 + i], bs); i++; } + return tfn(subst(t[4], bs), n, v, t[5]); + } + if (k == TY_STRUCT && t[5]) { + vec = t[6]; + n = vec[0]; + i = 0; + while (i < n) { v[i] = subst(vec[1 + i], bs); i++; } + return tinst(t[5], v, n); + } + return t; +} +// small type predicates. an array decays to a pointer to its element where a +// value is wanted, and telem is what indexing a pointer or array yields. +word isnum(word t) { return t[0] == TY_INT || t[0] == TY_CHAR || t[0] == TY_BOOL; } +// true for an aggregate type: an array or a struct. +word isagg(word t) { return t[0] == TY_ARR || t[0] == TY_STRUCT; } +// true for a pointer or an array type. +word isptr(word t) { return t[0] == TY_PTR || t[0] == TY_ARR; } +// the pointer an array type turns into when used as a value. +word decay(word t) { + if (t[0] == TY_ARR) return tptr(t[4]); + return t; +} +// the element type of a pointer or array, or word for anything else. +word telem(word t) { + if (t[0] == TY_PTR || t[0] == TY_ARR) return t[4]; + return tword; +} +// can a value of type src go where dst is expected? +// type compatibility, used by the optional typechecker. word is compatible with +// everything (untyped code stays legal), numbers convert freely, pointers need +// the same element unless one side is untyped or void, and a literal 0 is a +// null pointer. +word compat(word dst, word src) { + if (dst == src || dst == tword || src == tword) return 1; + if (dst[0] == TY_PTR && src[0] == TY_STRUCT && dst[4] == src) return 1; // an in-place struct supplies its address + if (isen(dst) || isen(src)) { // an enum takes only its own enum; an enum still becomes an int + if (isen(dst) && isen(src)) return dst == src; + if (isen(dst)) return 0; + } + if (isnum(dst) && isnum(src)) return 1; + if (isfl(dst) && isfl(src)) return 1; + src = decay(src); + if (dst[0] == TY_PTR && src[0] == TY_PTR) { + if (dst[4] == tword || src[4] == tword || dst[4] == tvoid || src[4] == tvoid) return 1; + return dst[4] == src[4]; + } + if ((dst[0] == TY_PTR || dst[0] == TY_FN || dst[0] == TY_DYN) && xzero) return 1; // a literal 0 is a null pointer + if (dst[0] == TY_DYN && src[0] == TY_DYN && dst[4] == src[4]) return dst[3] || !src[3]; // a growable array can't hold a fixed one + return dst == src; +} +// match pattern type p (which may contain type parameters) against actual type +// a, filling the bindings bs. this is how a call to a generic function infers +// T from its arguments. +word unify(word p, word a, word bs) { + int i, n; word pv, av; + if (p[0] == TY_VAR) { i = p[3]; if (!bs[i]) { bs[i] = a; return 1; } return compat(bs[i], a); } + a = decay(a); + if (p[0] == TY_PTR && a[0] == TY_PTR) { if (a[4] == tword) return 1; return unify(p[4], a[4], bs); } + if (p[0] == TY_STRUCT && p[5] && a[0] == TY_STRUCT && a[5] == p[5]) { + pv = p[6]; av = a[6]; n = pv[0]; i = 0; + while (i < n) { if (!unify(pv[1 + i], av[1 + i], bs)) return 0; i++; } + return 1; + } + return compat(p, a); +} +// print a type the way it is written in source. a pointer to an array or a +// function has no source spelling (T* would read as an array of pointers; give +// it a type alias), so it prints with a leading star for the reader's benefit. +word tprint(word t) { + int k = t[0], i; word v; + if (k == TY_PTR) { // T*; a pointer to an array or a function keeps the prefix form, since T* would read as an array of pointers + v = t[4][0]; + if (v == TY_ARR || v == TY_DYN || v == TY_FN) { put_c('*'); return tprint(t[4]); } + tprint(t[4]); put_c('*'); + return 0; + } + if (k == TY_DYN) { if (t[3]) put_s("fixed "); put_s("[]"); return tprint(t[4]); } + if (k == TY_ARR) { put_c('['); put_n(t[3]); put_c(']'); return tprint(t[4]); } + if (k == TY_FN) { + if (t[4] == tword) put_s("fn"); else tprint(t[4]); + if (t[5] & 1) put_s(".u"); + put_c('('); + i = 0; + while (i < t[3]) { + if (i) put_s(", "); + tprint(t[7 + i]); + if (i < 12 && tparamtag(t, i) == 1) put_s(".m"); + if (i < 12 && tparamtag(t, i) == 2) put_s(".d"); + i++; + } + put_c(')'); + return 0; + } + if (t[2]) put_s(t[2]); else put_s("?"); + if (k == TY_STRUCT && t[5]) { + v = t[6]; put_c('<'); + i = 0; while (i < v[0]) { if (i) put_s(", "); tprint(v[1 + i]); i++; } + put_c('>'); + } + return 0; +} +// typechecker errors. they only fire when "typecheck on" is set, in reachable +// code, and never during a dry run. +int tcerr(int what, int want, int got) { + if (!tc_on || !live || dry || panic) return 0; + errat(); put_s("type error: "); put_s(what); + put_s(": expected "); tprint(want); put_s(", got "); tprint(got); + errend(); + return 0; +} +// report a type error when ok is false, showing the type it got. +int tcneed(int ok, int what, int got) { + if (ok || !tc_on || !live || dry || panic) return 0; + errat(); put_s("type error: "); put_s(what); put_s(", got "); tprint(got); + errend(); + return 0; +} +// the type parameters in scope (tpn): a generic function or struct body sees +// T as a type. istypename says whether a name starts a type here, which is how +// the parser tells a declaration from an expression. +int tpfind(int n) { + int i = ntp; + while (i) { + i--; + if (tpn[i] == n) return i; + } + return -1; +} +// true when the name n is a type, a type parameter or a generic type. +word istypename(int n) { + int s; + if (n == s_fixed) return 1; + if (ginst && gfind(n) >= 0) return 1; + if (tpfind(n) >= 0) return 1; + if (findloc(n) >= 0) return 0; + s = lookup(n); + return s >= 0 && skind[s] == K_TYPE; +} +// the closing > of a type argument list; splits >> in two +int closeangle() { + if (is_op(s_shr)) { tv = s_gt; return 0; } + expect(s_gt); + return 0; +} +// a type, then any number of (params) after it: int(int, int) is a function +// returning int, fn(int) one returning a plain word +// ---- parsing types ---- +// grammar, outermost first: +// ptype a type, then any number of (params): int(int, int) is a function type +// ptype0 a type with its postfix stars: int*, vec*, point** +// ptype1 a base: [n]T, []T, fixed []T, a type name, or a generic instance +// the first thing in ptype's comment applies here too: fn(int) is a function +// returning a plain word. +int ptype() { + int start = toff, t = ptype0(), np; [24]word ps; + while (is_op(s_lp)) { + next(); np = 0; + if (!is_op(s_rp)) { + while (1) { + if (np >= 24) fatal("too many parameters"); + ps[np] = ptype(); + np++; + if (!accept(s_comma)) break; + } + } + expect(s_rp); + t = tfn(t, np, ps, np + np); + } + ht_note(start, toff, t); + return t; +} +// a type with any stars after it: int*, vec*, point** +int ptype0() { + int t = ptype1(); + while (is_op(s_star)) { next(); t = tptr(t); } + return t; +} +// parse one type with no trailing function parts. +word ptype1() { + int n, s; word t; [8]word args; int na = 0, i; + if (accept(s_lk)) { + if (accept(s_rk)) { // []T: grows + t = ptype0(); + return tdyn(t, 0); + } + n = cexpr(); + expect(s_rk); + t = ptype0(); + if (n < 0) serr("a negative array length"); + return tarr(t, n); + } + if (is_id(s_fixed)) { // fixed []T can't grow; fixed [n]T says what [n]T already means + next(); t = ptype0(); + if (t[0] == TY_DYN) return tdyn(t[4], 1); + if (t[0] != TY_ARR) serr("only arrays can be fixed"); + return t; + } + if (is_id(s_fn)) { + next(); + if (!is_op(s_lp)) die("fn as a type needs its parameters: fn(int)"); + return tword; + } + if (tk != T_ID) { diefound("expected a type"); return tword; } + n = tv; + if (ginst) { + i = gfind(n); + if (i >= 0) { next(); return gib[i]; } + } + i = tpfind(n); + if (i >= 0) { next(); return tvar(i, n); } + s = lookup(n); + // T in T first(...) + if (tvauto && (s < 0 || skind[s] != K_TYPE) && ntp < 8) { + tpn[ntp] = n; + lsmark(2, n); + ntp++; + next(); + return tvar(ntp - 1, n); + } + if (s < 0 || skind[s] != K_TYPE) { dien(n, "isn't a type"); return tword; } + next(); + t = sval[s]; + if (t[0] != TY_GEN) return t; + expect(s_lt); + while (1) { + if (na >= 8) die("too many type arguments"); + args[na] = ptype(); + if (args[na] == tvoid) serr("a type argument can't be void"); + na++; + if (!accept(s_comma)) break; + } + closeangle(); + if (na != t[3]) serrn(n, "wrong number of type arguments"); + return tinst(t, args, na); +} +// does a declaration start here? (a type, or a bare .tag) +// does a declaration start at the current token? (a type name, [, or a bare .tag) +int tstart() { + if (tk == T_OP) { + if (tv == s_lk || tv == s_dot) return 1; + return 0; + } + if (tk != T_ID) return 0; + if (tv == s_fn) return 1; + if (!istypename(tv)) return 0; + // Type.name(...) at the start of a statement is a static call, not a declaration: + // a declaration's dot is followed by a tag, u, m or d + peekat(0); + if (pk_tk == T_OP && pk_tv == s_dot) { + peekat(1); + if (pk_tk == T_ID && pk_tv != s_u && pk_tv != s_m && pk_tv != s_d) return 0; + } + return 1; +} +// [type] [.tag] +// parse a declaration's [type] [.tag] into dty and dtag +int dtype() { + dty = 0; dtag = 0; + if (!is_op(s_dot)) dty = ptype(); + if (accept(s_dot)) { + if (tv == s_m) { + dtag = S_M; + } else if (tv == s_u) { + dtag = S_U; + } else if (tv == s_d) dtag = S_D; else die("a tag is .u, .m or .d"); + next(); + } + return 0; +} +// struct name { type field, ... } or struct name { ... } +// the struct keyword. fields go in a scratch buffer first (name, type pairs), +// then are laid out: each field takes tsz(type) words, so offsets are words. +// methods are collected as token lists while the struct is read (grabfn) and +// compiled only when the struct is complete, so a method can use every field +// and call its siblings. a generic struct keeps its fields unresolved and +// can't have methods; each instance is laid out later by tresolve. +int kw_struct() { + int n; word t; int s, nf = 0; word buf = chk_resize(0, 0, 64); int bufcap = 64, i; word fs; int off = 0, gen = 0, ft, nm = 0, mcap = STRUCT_METHODS; word mn = chk_resize(0, 0, mcap), mq = chk_resize(0, 0, mcap), mst = chk_resize(0, 0, mcap), mty = chk_resize(0, 0, mcap), mrt = chk_resize(0, 0, mcap), mrg = chk_resize(0, 0, mcap); int bo, k, stat = 0, fname = 0; + next(); + n = name(); + if (is_op(s_lt)) { // generic + next(); ntp = 0; + while (1) { + if (ntp >= 8) die("too many type parameters"); + tpn[ntp] = name(); + lsmark(2, tpn[ntp]); + ntp++; + if (!accept(s_comma)) break; + } + closeangle(); + gen = 1; + t = tnew(TY_GEN, 0, n, 0); t[3] = ntp; + fs = talloc(ntp); copy_w(fs, tpn, ntp); t[6] = fs; + } else { t = tnew(TY_STRUCT, 0, n, 0); t[3] = -2; } // -2 while its fields are read + s = addsym(n, K_TYPE, t); + expect(s_lb); + while (!is_op(s_rb)) { + if (tk == T_EOF) { die("missing }"); break; } + stat = 0; + if (is_id(s_static)) { // static fn name(params) { body }, or static T name(params) { body }: a function of the struct, called as Struct.name(args), with no this + next(); + stat = 1; + } + if (is_id(s_fn)) { // fn name(params) { body }: a method, compiled once the struct is complete + next(); + if (nm >= mcap) { + mcap = mcap * 2; mn = chk_resize(mn, nm, mcap); mq = chk_resize(mq, nm, mcap); + mst = chk_resize(mst, nm, mcap); mty = chk_resize(mty, nm, mcap); + mrt = chk_resize(mrt, nm, mcap); mrg = chk_resize(mrg, nm, mcap); + } + mn[nm] = name(); + mst[nm] = stat; + mq[nm] = grabfn(); + mty[nm] = 0; + k = methdecl(n, mn[nm]); + if (stat) sflag[k] = sflag[k] | F_STATIC; + nm++; + continue; + } + // let x, y; fields are plain words + fname = 0; + if (is_id(s_var)) { + if (stat) die("static must be followed by fn or a typed method"); + next(); + ft = tword; + } else { + // T name(params) { body } is a typed method; T name, ...; is a typed field + rettype(); + fname = name(); + if (is_op(s_lp)) { + if (nm >= mcap) { + mcap = mcap * 2; mn = chk_resize(mn, nm, mcap); mq = chk_resize(mq, nm, mcap); + mst = chk_resize(mst, nm, mcap); mty = chk_resize(mty, nm, mcap); + mrt = chk_resize(mrt, nm, mcap); mrg = chk_resize(mrg, nm, mcap); + } + mn[nm] = fname; + mst[nm] = stat; + mq[nm] = grabfn(); + mty[nm] = 1; mrt[nm] = dty; mrg[nm] = dtag == S_U; + if (dtag && dtag != S_U) serr("the only return tag is .u"); + k = methdecl(n, fname); + if (stat) sflag[k] = sflag[k] | F_STATIC; + nm++; + continue; + } + if (stat) die("static must be followed by fn or a typed method"); + if (dtag) serr("a field has no tag"); + ft = dty; + } + while (1) { + if ((nf + 1) * 2 > bufcap) { buf = chk_resize(buf, nf * 2, bufcap * 2); bufcap = bufcap * 2; } + if (fname) { buf[nf + nf] = fname; fname = 0; } else buf[nf + nf] = name(); + lsmark(3, buf[nf + nf]); buf[nf + nf + 1] = ft; + field_note(t, buf[nf + nf], nameoff); lt_note(nameoff, ft); rs_put(nameoff, RS_FIELD, nameoff, 0); nf++; + if (!accept(s_comma)) break; + } + expect(s_semi); + } + next(); + if (gen) { + fs = talloc(nf + nf); copy_w(fs, buf, nf + nf); + t[4] = fs; t[5] = nf; + mdg = ntp; copy_w(mdn, tpn, ntp); + ntp = 0; + } else { + fs = talloc(nf * 3 + 1); + i = 0; + bo = 0; + while (i < nf) { + ft = buf[i + i + 1]; + if (ft == t) { serr("a struct can't contain itself by value; use a pointer"); ft = tword; } + fs[i * 3] = buf[i + i]; fs[i * 3 + 1] = ft; + // a sized field packs into the current word at its own alignment; its + // offset is recorded as minus (byte offset + 1). anything else starts a word + k = tbytes(ft); + if (k) { bo = (bo + k - 1) / k * k; fs[i * 3 + 2] = 0 - bo - 1; bo = bo + k; } + else { bo = (bo + 7) / 8 * 8; fs[i * 3 + 2] = bo / 8; bo = bo + tsz(ft) * 8; } + i++; + } + off = (bo + 7) / 8; + t[4] = fs; t[1] = off; t[3] = nf; + } + accept(s_semi); + i = 0; + while (i < nm) { methdef(n, mn[i], mq[i], mst[i], mty[i], mrt[i], mrg[i]); i++; } + mdg = 0; + return s; +} +// ---- methods ---- +// a method is an ordinary function named Struct.name whose first parameter is +// this, a pointer to the struct. e.method(a) calls Struct.name(e, a). the +// receiver's struct comes from its type, or, for an untyped word, from the +// one struct that has a method of that name. +// the symbol for a method is the interned string "Struct.name" +word methname(word sn, word mn) { + word b = chk_resize(0, 0, len_w(sn) + len_w(mn) + 2); int i = 0, j = 0; + while (sn[j]) { b[i] = sn[j]; i++; j++; } + b[i] = '.'; i++; + j = 0; + while (mn[j]) { b[i] = mn[j]; i++; j++; } + b[i] = 0; + return intern(b); +} +// the method called n on struct type t, as a symbol index, or -1. methwhich +// (below) does the untyped version: search every struct for one with that +// method, which is unique or ambiguous (-2). +int methof(word t, int n) { + int s = lookup(methname(t[2], n)); + if (s >= 0 && skind[s] == K_FN) return s; + return 0 - 1; +} +// the one method called n, -1 for none, -2 for several structs having one +int methscan(int n) { + int i = 0; word t; int s, hit = 0 - 1; + while (i < ntl) { + t = typelist[i]; + if (t[0] == TY_STRUCT && t[3] >= 0) { + s = methof(t, n); + if (s >= 0) { if (hit >= 0) return 0 - 2; hit = s; } + } + i++; + } + return hit; +} +// declare Struct.name now, so methods can call each other in any order +// (the declaration is what makes mutual recursion between methods work: the +// symbol exists with no body yet, and calls to it go on the patch chain) +int methdecl(int sn, int mn) { + int nm = methname(sn, mn), s = lookup(nm); + if (s >= ubase && s >= 0 && skind[s] == K_FN) { + serrn(mn, "is defined twice in this struct"); + return s; + } + s = addsym(nm, K_FN, 0); + sflag[s] = 0; + return s; +} +// the tokens of a method, from its ( to the } that closes its body, as a token list +// a method body is not parsed when the struct is read. its tokens are copied +// into a token list (kind, value, place triples) up to the matching brace, and +// methdef replays them later. the place travels with each token, so errors +// inside a method still point at the right line. +unchecked word grabfn() { + int cap = 768; word q = new(cap); int n = 0, depth = 0; word nq; + q[0] = 0; + if (!is_op(s_lp)) { die("expected ( after the method name"); return q; } + while (tk != T_EOF) { + if (n * 3 + 4 > cap) { + nq = new(cap + cap); + copy_w(nq, q, cap); + free(q); + q = nq; + cap = cap + cap; + } + q[1 + n * 3] = tk; q[2 + n * 3] = tv; q[3 + n * 3] = tpack(); n++; + if (is_op(s_lb)) depth++; + else if (is_op(s_rb)) { depth--; if (depth <= 0) { next(); break; } } + next(); + } + if (tk == T_EOF && depth > 0) die("missing } at the end of a method"); + q[0] = n; + return q; +} +// compile a method by feeding the parser fn Struct.name(Struct* this, rest...) +// build the token list "fn Struct.name ( Struct* this , rest..." from the +// method's saved tokens, push it as an input source and parse it as an +// ordinary top-level function. a method is therefore just a function whose +// first parameter is this; nothing else about it is special. +unchecked int tkput(word r, int k, int kind, int val, int pl) { + r[1 + k * 3] = kind; r[2 + k * 3] = val; r[3 + k * 3] = pl; + return k + 1; +} +// compile a struct method once the struct is complete. +unchecked int methdef(int sn, int mn, word q, int stat, int typed, int rty, int rtag) { + int n = q[0]; word r; int i, k = 0, lnp; + if (!n) { free(q); return 0; } + r = new((n + 8 + mdg * 4) * 3 + 1); + lnp = q[3]; + r[1] = T_ID; r[2] = s_fn; r[3] = lnp; k = 1; + r[1 + k * 3] = T_ID; r[2 + k * 3] = methname(sn, mn); r[3 + k * 3] = lnp; k++; + if (mdg) { // a method of a generic struct is a generic function over the struct's parameters: fn S.m(S* this, ...) + k = tkput(r, k, T_OP, s_lt, lnp); + i = 0; + while (i < mdg) { + if (i) k = tkput(r, k, T_OP, s_comma, lnp); + k = tkput(r, k, T_ID, mdn[i], lnp); + i++; + } + k = tkput(r, k, T_OP, s_gt, lnp); + } + r[1 + k * 3] = q[1]; r[2 + k * 3] = q[2]; r[3 + k * 3] = q[3]; k++; + if (stat && mdg) die("a static function can't be in a generic struct"); + if (!stat) { + r[1 + k * 3] = T_ID; r[2 + k * 3] = sn; r[3 + k * 3] = lnp; k++; + if (mdg) { + k = tkput(r, k, T_OP, s_lt, lnp); + i = 0; + while (i < mdg) { + if (i) k = tkput(r, k, T_OP, s_comma, lnp); + k = tkput(r, k, T_ID, mdn[i], lnp); + i++; + } + k = tkput(r, k, T_OP, s_gt, lnp); + } + r[1 + k * 3] = T_OP; r[2 + k * 3] = s_star; r[3 + k * 3] = lnp; k++; + r[1 + k * 3] = T_ID; r[2 + k * 3] = s_this; r[3 + k * 3] = lnp; k++; + } + if (!stat && n > 1 && !(q[1 + 3] == T_OP && q[2 + 3] == s_rp)) { + r[1 + k * 3] = T_OP; + r[2 + k * 3] = s_comma; + r[3 + k * 3] = lnp; + k++; + } + i = 1; + while (i < n) { + r[1 + k * 3] = q[1 + i * 3]; + r[2 + k * 3] = q[2 + i * 3]; + r[3 + k * 3] = q[3 + i * 3]; + k++; + i++; + } + r[0] = k; + free(q); + src_push(1, r, 0, 1); + next(); + if (typed) { + // a typed method: skip the fn and the name, then compile from the ( with the declared return type + if (mdg) die("a typed method of a generic struct is not supported yet"); + next(); + next(); + ifkp = tpack(); ifkk = 0 - 1; if (isp != 0) ifkk = 0; + o3nxt = 0; ntp = 0; + fndone(fndef(1, rty, rtag, methname(sn, mn)), 0); + } else topitem(); + if (tk != T_EOF) die("unexpected tokens after a method"); + src_pop(); + return 0; +} +// the call e.m(args): the receiver is on the stack and becomes this +// e.m(args): by now the receiver is on the stack, so it simply becomes the +// first argument. the call is checked against the method's signature if it +// has been compiled, and its result class is fresh if the method returns .u. +int methcall(int s, word rst) { + af_forget(); + int np, tags = 0, ret = 0; word ft = stype[s]; [8]word bs; + if (sflag[s] & F_SIG) { tags = ssig[s] >> 6; ret = ssig[s] & 1; } + else sflag[s] = sflag[s] | F_UNTYPED; + if (sflag[s] & F_RETOWN) ret = 1; + fill_w(bs, 0, 8); + if ((sflag[s] & F_GEN) && ft && rst && rst[0] == TY_STRUCT) unify(ft[7], tptr(rst), bs); // a method of a generic struct: the receiver says what its parameters are + np = args(tags >> 2, 0, bs, 0) + 1; + if ((sflag[s] & F_SIG) && np != ((ssig[s] >> 1) & 31)) serrn(sname[s], "wrong number of arguments"); + if (sflag[s] & F_GEN) s = ginstance(s, bs); + fnref(s); op(O_CALL); + lv = 0; + lvk = LV_NONE; + xzero = 0; + xbor = -1; + xconst = 0; + xfloat = 0; + xstr = 0; + xlenloc = -1; + xstamp = 0; + xty = tword; + if (ft) xty = subst(ft[4], bs); + if (isfl(xty)) xfloat = 1; + if (ret) return C_FRESH; + return C_PLAIN; +} +// type name = T; +// the type keyword makes an alias, which is also how a pointer to an array or +// function type gets a name. +int kw_type() { + int n; + next(); + n = name(); + expect(s_eq); + addsym(n, K_TYPE, ptype()); + expect(s_semi); + return 0; +} +// (T) or (T*) ahead of the current '(', within the lookahead we have +// the parser sees ( and has to decide between a parenthesized expression and a +// cast. a cast is ( type stars ) so peek ahead through the stars to the ). +int caststart() { + int k; + peek(); + if (pk_tk == T_ID && (istypename(pk_tv) || pk_tv == s_fn)) { + k = 1; peekat(k); + while (pk_tk == T_OP && pk_tv == s_star && k < 3) { k++; peekat(k); } // (int*)x, (node**)x + return pk_tk == T_OP && pk_tv == s_rp; + } + return 0; +} +// memdebug on; checks and interior pinning. memdebug scrub; also poisons frees. +// memdebug off; stops both. at the top level, start() applies it before main. +// ---- statements that only configure the compiler ---- +int kw_memdebug() { + int f = 0; + stmtonly(); + next(); + if (is_id(s_on)) { mdbg = 1; f = 5; } + else if (is_id(s_scrub)) { mdbg = 1; f = 7; } + else if (is_id(s_off)) { mdbg = 0; f = 0; } + else die("memdebug on, memdebug scrub or memdebug off"); + next(); + expect(s_semi); + if (infn) { lit(f); callname(s_memset); op(O_DROP); } + else mdbg_flags = f; + return 0; +} +// enum name { A, B = 5, C }; the names are global constants, the name is an int type +// the type of an enum: an int record that the typechecker keeps apart from the +// plain ints. t[6] bit 4 marks it; the members carry it in stype +word tenum(int n) { word t = tnew(TY_INT, 1, n, 0); t[6] = 16; return t; } +// true for a type made by an enum declaration. +word isen(word t) { return t[0] == TY_INT && (t[6] & 16); } +// enum name { A, B = 5 }: each member is a constant, plain (A) and scoped (name.A) +// member records for tagged enums and for plain enums (pm_en is -1 for a plain +// member). a member's payload fields are at most 8. +// the text a tagged enum or an extern stub is generated into +[GENERATED_WORDS]word ebuf_initial; word ebuf = ebuf_initial; int ebcap = GENERATED_WORDS, ebn; +[ENUM_MEMBERS]word pm_name_initial, pm_en_initial, pm_tag_initial, pm_nf_initial, pm_val_initial, pm_exp_initial; +word pm_name = pm_name_initial, pm_en = pm_en_initial, pm_tag = pm_tag_initial, pm_nf = pm_nf_initial, pm_val = pm_val_initial, pm_exp = pm_exp_initial; int pmcap = ENUM_MEMBERS, npm; +[ENUM_MEMBERS * 8]word pm_fnm_initial, pm_ft_initial, pm_off_initial; +word pm_fnm = pm_fnm_initial, pm_ft = pm_ft_initial, pm_off = pm_off_initial; +[TAGGED_ENUMS]word pe_st_initial, pe_first_initial, pe_n_initial, pe_name_initial; +word pe_st = pe_st_initial, pe_first = pe_first_initial, pe_n = pe_n_initial, pe_name = pe_name_initial; int pecap = TAGGED_ENUMS, npe; +// grow whole member rows, keeping their payload-field indices stable. +int reserve_enum_members(int need) { + int cap = pmcap; + if (need <= cap) return cap; + while (cap < need) cap = cap * 2; + pm_name = chk_resize(pm_name, npm, cap); pm_en = chk_resize(pm_en, npm, cap); + pm_tag = chk_resize(pm_tag, npm, cap); pm_nf = chk_resize(pm_nf, npm, cap); + pm_val = chk_resize(pm_val, npm, cap); pm_exp = chk_resize(pm_exp, npm, cap); + pm_fnm = chk_resize(pm_fnm, npm * 8, cap * 8); pm_ft = chk_resize(pm_ft, npm * 8, cap * 8); pm_off = chk_resize(pm_off, npm * 8, cap * 8); + pmcap = cap; return cap; +} +// grow the tagged-enum index independently of its member rows. +int reserve_tagged_enums(int need) { + int cap = pecap; + if (need <= cap) return cap; + while (cap < need) cap = cap * 2; + pe_st = chk_resize(pe_st, npe, cap); pe_first = chk_resize(pe_first, npe, cap); + pe_n = chk_resize(pe_n, npe, cap); pe_name = chk_resize(pe_name, npe, cap); + pecap = cap; return cap; +} +// a field's c-like spelling in a generated struct +int ftext(int t) { if (t == tint) return "int"; if (t == tfloat) return "float"; return "let"; } +// the member named n of a tagged enum, or -1 +int pm_find(int n) { + int gi = npm - 1; + while (gi >= 0) { if (pm_name[gi] == n && pm_en[gi] >= 0) return gi; gi--; } + return -1; +} +// the tagged enum a type is a pointer to, plus one; 0 for any other type +int ptagged(word t) { + int e = 0; + if (!t || t[0] != TY_PTR) return 0; + while (e < npe) { if (t[4] == pe_st[e]) return e + 1; e++; } + return 0; +} +// enum name { A, B = 5 } is a plain enum: each member is a constant, plain (A) +// and scoped (name.A). a member with a block of fields makes it a tagged enum +// (see enum_tagged): enum name { circle { int r; }, dot }. +int kw_enum() { + int n, en = 0, first = npm, cnt = 0, tagged = 0, gi, nf, v = 0, et = 0, sk, ft, fname; + stmtonly(); + next(); + if (tk == T_ID) { en = tv; next(); } + expect(s_lb); + while (!is_op(s_rb)) { + if (tk == T_EOF) { die("missing }"); break; } + reserve_enum_members(npm + 1); + gi = npm; npm++; + n = name(); + pm_name[gi] = n; pm_tag[gi] = cnt; pm_nf[gi] = 0; pm_en[gi] = -1; pm_val[gi] = 0; pm_exp[gi] = 0; + if (is_op(s_lb)) { + tagged = 1; next(); + while (!is_op(s_rb)) { + if (tk == T_EOF) { die("missing }"); break; } + if (pm_nf[gi] >= 8) { fatal("too many fields in a member"); break; } + if (is_id(s_var)) { next(); ft = tword; } + else { rettype(); ft = dty; if (dtag) serr("a payload field has no tag"); } + if (ft != tint && ft != tfloat && ft != tword) serr("a payload field is int, float or let"); + fname = name(); + nf = gi * 8 + pm_nf[gi]; + pm_fnm[nf] = fname; pm_ft[nf] = ft; pm_nf[gi]++; + expect(s_semi); + } + expect(s_rb); + } else if (accept(s_eq)) { pm_val[gi] = cexpr(); pm_exp[gi] = 1; } + cnt++; + if (!accept(s_comma)) break; + if (is_op(s_rb)) break; + } + expect(s_rb); + expect(s_semi); + if (tagged) return enum_tagged(en, first, cnt); + if (en) { et = tenum(en); addsym(en, K_TYPE, et); } + for (gi = first; gi < npm; gi++) { + if (pm_exp[gi]) v = pm_val[gi]; + sk = addsym(pm_name[gi], K_CONST, v); + stype[sk] = et; + if (en) { sk = addsym(methname(en, pm_name[gi]), K_CONST, v); stype[sk] = et; } + v++; + } + return 0; +} +// a tagged enum: one struct, with the tag as word 0 and the fields of every +// member after it, each named member_field. a member's constructor is a static +// function on the enum, en.member(fields...), that allocates the struct, sets +// the tag and the member's fields, and returns it owned. the struct is generated +// as source and compiled here, so its methods are ordinary zc. +int enum_tagged(int en, int first, int cnt) { + int gi, j, nf, w = 1; word buf; int sk; + if (!en) { serr("a tagged enum needs a name"); return 0; } + reserve_tagged_enums(npe + 1); + if (cnt > 63) { fatal("a tagged enum has at most 63 members"); return 0; } + for (gi = first; gi < npm; gi++) if (pm_exp[gi]) serr("a tagged enum's members take no explicit values"); + ebn = 0; ebuf[0] = 0; + ebs("struct "); ebs(en); ebs(" { int tag; "); + for (gi = first; gi < npm; gi++) { + for (j = 0; j < pm_nf[gi]; j++) { + nf = gi * 8 + j; + pm_off[nf] = w; w++; + ebs(ftext(pm_ft[nf])); ebs(" "); ebs(pm_name[gi]); ebs("_"); ebs(pm_fnm[nf]); ebs("; "); + } + } + for (gi = first; gi < npm; gi++) { + ebs("static "); ebs(en); ebs("*.u "); ebs(pm_name[gi]); ebs("("); + for (j = 0; j < pm_nf[gi]; j++) { + nf = gi * 8 + j; + if (j) ebs(", "); + if (pm_ft[nf] == tint) ebs("int "); else if (pm_ft[nf] == tfloat) ebs("float "); + ebs(pm_fnm[nf]); + } + ebs(") { "); ebs(en); ebs("* o = new("); ebs(en); ebs("); o.tag = "); ebn10(pm_tag[gi]); ebs("; "); + for (j = 0; j < pm_nf[gi]; j++) { + nf = gi * 8 + j; + ebs("o."); ebs(pm_name[gi]); ebs("_"); ebs(pm_fnm[nf]); ebs(" = "); ebs(pm_fnm[nf]); ebs("; "); + } + ebs("return u*o; } "); + } + ebs("}"); + buf = new(ebn + 2); + copy_w(buf, ebuf, ebn + 1); + src_push(2, buf, 0, 0); + next(); + stubgen = 1; + while (tk != T_EOF) topitem(); + stubgen = 0; + src_pop(); + free(d*buf); + sk = lookup(en); + if (sk < 0 || skind[sk] != K_TYPE) { serr("the tagged enum didn't make its struct"); return 0; } + pe_st[npe] = sval[sk]; pe_first[npe] = first; pe_n[npe] = cnt; pe_name[npe] = en; + for (gi = first; gi < npm; gi++) { + pm_en[gi] = npe; + sk = addsym(pm_name[gi], K_CONST, pm_tag[gi]); + stype[sk] = 0; + } + npe++; + return 0; +} +// a switch over a tagged enum that leaves out a member, with no default, is a +// warning, on the line of the switch (sl). e is the enum, cov the bit set of +// the tags the switch covers. +int cover_warn(int e, int cov, int sl) { + int k, gi, first = 1; + if (dry || panic || cmode) return 0; + for (k = 0; k < pe_n[e]; k++) { + if (k < 63 && ((cov >> k) & 1)) continue; + if (first) { put_s("zc: line "); put_n(sl); put_s(": warning: this switch doesn't cover "); first = 0; } + else put_s(", "); + gi = pe_first[e] + k; + put_s(pm_name[gi]); + } + if (!first) put_c(10); + return 0; +} +// include "path"; reads the file and compiles it here, signatures first +int kw_include() { + int path, buf; + stmtonly(); + next(); + if (tk != T_STR) { die("include needs a string"); return 0; } + path = tv; + next(); + expect(s_semi); + buf = readfile(path); + if (!buf) { serr("can't read that include"); return 0; } + prescan(2, buf); + src_push(2, buf, 0, 0); + next(); + while (tk != T_EOF) topitem(); + src_pop(); + return 0; +} +// typecheck on; / typecheck off; +int kw_typecheck() { + next(); + if (is_id(s_on)) { + tc_on = 1; + } else if (is_id(s_off)) tc_on = 0; else die("typecheck on or typecheck off"); + next(); expect(s_semi); + return 0; +} +// docs on; keeps the doc comments of what follows in the image, docs off; stops +int kw_docs() { + next(); + if (is_id(s_on)) { + docs_on = 1; + } else if (is_id(s_off)) docs_on = 0; else die("docs on or docs off"); + next(); expect(s_semi); + return 0; +} +// the doc symbol of a name: the symbol "doc name" that holds its byte string, +// or -1. the chain is walked by hand because lookup hides what is below vislo +int docsym(word nm) { + int i, k = 0; word p = "doc "; + doc_name_room(len_w(nm) + 5); + while (p[k]) { dnmb[k] = p[k]; k++; } + i = 0; while (nm[i]) { dnmb[k] = nm[i]; k++; i++; } + dnmb[k] = 0; + nm = intern(dnmb); + i = nm[-1] - 1; + while (i >= 0) { + if (skind[i] == K_DOC) return i; + i = snext[i] - 1; + } + return -1; +} +// the packed documentation of a name (a byte string) or 0 +word docofs(int nm) { + int s = docsym(nm); + if (s < 0) return 0; + return sval[s]; +} +// the documentation of a function, by address +word docof(int f) { + int i = nsym; + while (i) { + i--; + if (skind[i] == K_FN && sval[i] == f && (sflag[i] & F_DEF)) return docofs(sname[i]); + } + return 0; +} +// documentation uses stable packed objects. symbols keep them alive until an +// image copies their bytes after its code; ordinary docofs calls work too. +word doc_pack(word text, int length) { + int i = 0, k, c, w, nw = (length + 7) >> 3; word p = chk_resize(0, 0, nw + 2); + p[-4] = p[-4] | F_SHARE | F_INTERNAL; p[0] = length; + while (i < length) { + w = 0; k = 0; + while (k < 8 && i < length) { c = text[i]; if (c > 255) c = 63; w = w | (c << (k * 8)); k++; i++; } + p[1 + ((i - 1) >> 3)] = w; + } + return p; +} +// attach the doc comment above a declaration at name n. +int doc_attach(word n) { + int i = 0, w, k, c, b, nw; + if (!dsn || !n || dry || ginst) return 0; + b = doc_pack(dsnap, dsn); + doc_name_room(len_w(n) + 5); + i = 0; while ("doc "[i]) { dnmb[i] = "doc "[i]; i++; } + k = 0; while (n[k]) { dnmb[i] = n[k]; i++; k++; } + dnmb[i] = 0; + addsym(intern(dnmb), K_DOC, b); + dsn = 0; + return 0; +} +// the signature of a function as source text, "int put_c(int ch)", kept in the +// image as the symbol "sig name" when docs are on, so the language server can +// show it for any genome function, with or without a doc comment. the text is +// printed through put_c with the output sent to sgb (the same redirect the +// check uses for a parse word that prints) +[400]word sgb_initial; word sgb = sgb_initial; int sigcap = 400; +int sig_print(word n, int rty, int np, word pn, word pty, int typed) { + int i = 0; + if (typed) { tprint(rty); put_c(' '); } else put_s("fn "); + put_s(n); put_c('('); + while (i < np) { + if (i) put_s(", "); + if (typed) { tprint(pty[i]); put_c(' '); } + put_s(pn[i]); i++; + } + put_c(')'); return 0; +} +// attach a complete signature, measured before capture. +int sig_attach(word n, int rty, int np, word pn, word pty, int typed) { + int i = 0, k, b, nw, sm, sb, sn, sc, sl, w, c; + if (!docs_on || dry || ginst || ntp || panic) return 0; + sm = outmode; sb = outbuf; sn = outn; sc = outcap; + outmode = 3; outn = 0; sig_print(n, rty, np, pn, pty, typed); + if (outn > sigcap) { while (outn > sigcap) sigcap = sigcap * 2; sgb = chk_resize(sgb, 0, sigcap); } + outmode = 2; outbuf = sgb; outn = 0; outcap = sigcap; + sig_print(n, rty, np, pn, pty, typed); + sl = outn; + outmode = sm; outbuf = sb; outn = sn; outcap = sc; + b = doc_pack(sgb, sl); + doc_name_room(len_w(n) + 5); + i = 0; while ("sig "[i]) { dnmb[i] = "sig "[i]; i++; } + k = 0; while (n[k]) { dnmb[i] = n[k]; i++; k++; } + dnmb[i] = 0; + addsym(intern(dnmb), K_DOC, b); + return 0; +} +// the field of a struct type, or -1 +// ---- built-ins that look like functions ---- +// sizeof and typeof take a type or an expression; the expression form is +// compiled with dry set so it contributes only its type. len, cap, push, pop +// and resize work on growable arrays ([]T handles in rt.zc); arrarg parses +// their first argument and remembers which local it was, so the o3 and +// bounds-proof machinery can see it. +int tfindfield(word t, int n) { + int i = 0; + tresolve(t); + while (i < t[3]) { + if (t[4][i * 3] == n) return i; + i++; + } + return -1; +} +// sizeof(T or expr), typeof(T or expr): the expression form is compiled dry, so it never runs +int typearg() { + int t, sb, c, ob; + expect(s_lp); + if (tstart() && !is_op(s_dot)) t = ptype(); + else { + sb = snap_push(); ob = xbor; dry++; + c = expr(); rval(); + t = xty; + dry--; xbor = ob; snap_restore(sb); snap_pop(sb); + } + expect(s_rp); + return t; +} +// len(a), cap(a), push(a, x), pop(a), resize(a, n): growable arrays. +// len of a [n]T is just n. +word arrarg() { + int c; + expect(s_lp); c = expr(); + argloc = -1; arrloc = -1; + if (lv && lvk == LV_LOCAL && xty[0] == TY_DYN) { + arrloc = lvi; + if (o3on && o3cache[lvi]) argloc = lvi; + } + rval(); plainuse(c); af_access(xaf, tpack()); + return xty; +} +// report an error when a fixed array is asked to grow or shrink. +int nogrow(word t) { + if (t[0] == TY_DYN && t[3]) serr("a fixed array can't grow or shrink"); + return 0; +} +// compile len(a): the length of an array. +int kw_len() { + word t; int a; + next(); t = arrarg(); a = arrloc; expect(s_rp); + if (xstr) { + callname(s_len_s); + xty = tint; + xstr = 0; + xfloat = 0; + xlenloc = -1; + xstamp = 0; + return 0; + } + if (t[0] == TY_ARR) { op(O_DROP); lit(t[3]); } + else { tcneed(t[0] == TY_DYN || t == tword, "len needs an array", t); if (argloc >= 0) { op(O_DROP); vaddr(o3cache[argloc] + 1); op(O_LD); } else if (o3on && t[0] == TY_DYN) op(O_LD); else callname(s_dlen); } + xty = tint; + xlenloc = -1; xstamp = 0; + if (a >= 0 && t[0] == TY_DYN && t[3]) xlenloc = a; // the whole expression is len of a fixed array + return 0; +} +// compile cap(a): the capacity of a growable array. +int kw_cap() { + word t; + next(); + t = arrarg(); + expect(s_rp); + tcneed(t[0] == TY_DYN || t == tword, "cap needs a []T", t); + callname(s_dcap); + xty = tint; + return 0; +} +// compile push(a, v): add an element to the end of a growable array. +int kw_push() { + word t; int c; + next(); t = arrarg(); af_escape(xaf); nogrow(t); + if (t[0] == TY_DYN && tsz(t[4]) != 1) serr("push takes one-word elements; use resize for structs"); + expect(s_comma); c = expr(); rval(); argcheck(c, 2); + if (t[0] == TY_DYN && !compat(t[4], xty)) tcerr("push", t[4], xty); + if (o3on && t[0] == TY_DYN && hasptr(t[4])) { lit(t[4]); callname(s_fatten); } + expect(s_rp); + if (t[0] == TY_DYN && tbytes(t[4])) { // packed: an f32 goes in as its 32 bits, the rest are cut by the store + if (isfl(t[4])) { if (!xfloat) op(OP_I2F); xenh("zf2s"); } + callname(intern("dpushb")); xty = tint; xfloat = 0; + return 0; + } + if (o3on && t[0] == TY_DYN && !hasptr(t[4]) && tsz(t[4]) == 1) { pushfast(); xty = tint; xfloat = 0; return 0; } + callname(s_dpush); xty = tint; + return 0; +} +// pop in o3 code, inline: a non-empty array gives up its last word, an empty one is dpop's report +int popfast() { + int k, e; + lit(0); op(O_PICK); op(O_LD); + k = hole(); op(O_JZ); + lit(0); op(O_PICK); op(O_LD); lit(1); op(O_SUB); lit(1); op(O_PICK); op(O_ST); + lit(0); op(O_PICK); op(O_LD); + lit(1); op(O_PICK); lit(2); op(O_ADD); op(O_LD); + op(O_ADD); op(O_LD); + op(O_SWAP); op(O_DROP); + e = hole(); op(O_JMP); + setlit(k, here); + callname(s_dpop); + setlit(e, here); + return 0; +} +// push of a plain one-word element in o3 code, inline: when len < cap the word goes +// straight into the items and len moves up, otherwise dpush grows the array +int pushfast() { + int k, e; + lit(1); op(O_PICK); op(O_LD); + lit(2); op(O_PICK); lit(1); op(O_ADD); op(O_LD); + op(O_LT); + k = hole(); op(O_JZ); + lit(1); op(O_PICK); lit(2); op(O_ADD); op(O_LD); + lit(2); op(O_PICK); op(O_LD); op(O_ADD); + op(O_ST); + lit(0); op(O_PICK); op(O_LD); lit(1); op(O_ADD); lit(1); op(O_PICK); op(O_ST); + op(O_DROP); lit(0); + e = hole(); op(O_JMP); + setlit(k, here); + callname(s_dpush); + setlit(e, here); + return 0; +} +// compile pop(a): remove and return the last element of a growable array. +int kw_pop() { + word t; + next(); + t = arrarg(); af_escape(xaf); + nogrow(t); + expect(s_rp); + if (t[0] == TY_DYN && tbytes(t[4])) { // packed: read zero extended, then sign extend or widen + callname(intern("dpopb")); + xty = t[4]; xfloat = 0; + pkfix(xty); + if (isfl(xty)) xfloat = 1; + return 0; + } + if (o3on && t[0] == TY_DYN && tsz(t[4]) == 1) popfast(); else callname(s_dpop); + xty = telem(t); + if (t[0] == TY_DYN) xty = t[4]; + xfloat = 0; + if (isfl(xty)) xfloat = 1; + return 0; +} +// compile resize(a, n): set the length of a growable array. +int kw_resize() { + word t; int c; + next(); + t = arrarg(); af_escape(xaf); + nogrow(t); + expect(s_comma); + c = expr(); + rval(); + plainuse(c); + expect(s_rp); + if (t[0] == TY_DYN && tbytes(t[4])) callname(intern("dresizeb")); else callname(s_dresize); + xty = tint; + return 0; +} +// compile sizeof(T) or sizeof(e): the size of a type or expression in words. +int kw_sizeof() { + int t; + next(); + t = typearg(); + lit(tsz(t)); + xty = tint; + return 0; +} +// compile typeof(e): the type of an expression, as a value. +int kw_typeof() { + int t; + next(); + t = typearg(); + lit(t); + xty = tword; + return 0; +} +// ---- c abi: extern and cfunc ---- +// extern double pow(double, double); or with parameter names, and an +// optional "= "cname"" to give the zc function another name than the c symbol. +// the declaration is read with c's spelling of types and turned into an +// ordinary zc function, so a call to it is a call like any other. the function +// fills a block of words that sits in the code stream (the symbol's name, the +// signature, a cache slot, then room for the arguments) and stores its arguments there, +// and gate op 14 makes the native call (see the c abi section of core.c). +// a signature is 4 bits per kind: the result, then each argument: +// 0 void 1 long 2 double 3 byte pointer 4 int 5 float 6 word pointer +// 7 unsigned int 8 short 9 unsigned short 10 char 11 unsigned char +// 12 str, a zc byte string (b"..."), given to c as the address of its bytes +// a double or float is a zc float. any pointer is a byte address (what +// addr_b(s + 1) gives for a byte string), except "word *", which is a word +// address (a zc object or array). native results come back the same way. +// append the string s to the text being generated for an extern stub. +int eb_char(int c) { + if (ebn + 2 > ebcap) { ebcap = ebcap * 2; ebuf = chk_resize(ebuf, ebn, ebcap); } + ebuf[ebn] = c; ebn++; ebuf[ebn] = 0; return 0; +} +int ebs(word s) { + while (*s) { eb_char(*s); s++; } + ebuf[ebn] = 0; + return 0; +} +// append the decimal digits of n to the generated text. +int ebn10(int n) { + if (n >= 10) ebn10(n / 10); + eb_char(48 + n % 10); + ebuf[ebn] = 0; + return 0; +} +// true for a c type keyword, like long, char or double. +int ctypeword(int w) { + return eq_w(w, "unsigned") || eq_w(w, "signed") || eq_w(w, "long") || eq_w(w, "short") || eq_w(w, "int") || eq_w(w, "char") || eq_w(w, "double") || eq_w(w, "float") || eq_w(w, "void") || eq_w(w, "const") || eq_w(w, "volatile"); +} +// a c type at the current token: its signature kind, or -1 after an error. +// skipname also eats a parameter name that follows. +// a plain zc struct a native call takes by value: every field is one word (a long, +// a double or a pointer), so word g of the struct is eightbyte g. returns its +// word count, or -1 after an error. +int cstruct_ok(word st, int nm) { + int i = 0, n; word ft; + tresolve(st); + n = st[1]; + if (st[0] != TY_STRUCT || st[5] || n < 1 || n > 15 || st[3] != n) { serrn(nm, "can't be passed by value to a native call: it needs one word fields, at most 15 words"); return 0 - 1; } + while (i < n) { + ft = st[4][i * 3 + 1]; + if (tbytes(ft) || tsz(ft) != 1 || ft[0] == TY_STRUCT || ft[0] == TY_ARR || st[4][i * 3 + 2] != i) { serrn(nm, "can't be passed by value to a native call: every field has to be one word (long, double or a pointer)"); return 0 - 1; } + i++; + } + return n; +} +// the native signature kind of the c type at the current token, or -1 after an error. +int ckind(int skipname) { + int w, uns = 0, lng = 0, shrt = 0, chr = 0, ints = 0, dbl = 0, flt = 0, vd = 0, other = 0, ptr = 0, seen = 0, k = -1, sw = 0, sk, nw; + ckst = 0; ckn = 0; + while (tk == T_ID) { + w = tv; + if (eq_w(w, "const") || eq_w(w, "volatile") || eq_w(w, "restrict")) { next(); continue; } + if (eq_w(w, "struct") || eq_w(w, "enum") || eq_w(w, "union")) { + next(); + if (tk == T_ID) { if (eq_w(w, "struct")) sw = tv; next(); } + other = 1; seen = 1; + continue; + } + if (seen && !ctypeword(w)) break; + if (eq_w(w, "unsigned")) uns = 1; + else if (eq_w(w, "signed")) ints = 1; + else if (eq_w(w, "long")) lng++; + else if (eq_w(w, "short")) shrt = 1; + else if (eq_w(w, "char")) chr = 1; + else if (eq_w(w, "int")) ints = 1; + else if (eq_w(w, "double") || eq_w(w, "f64")) dbl = 1; + else if (eq_w(w, "float") || eq_w(w, "f32")) flt = 1; + else if (eq_w(w, "void")) vd = 1; + else other = w; + seen = 1; + next(); + } + if (!seen) { die("expected a c type"); return 0 - 1; } + while (is_op(s_star)) { + ptr = 1; next(); + while (is_id(intern("const"))) next(); + } + if (skipname && tk == T_ID) next(); + if (ptr) { + if (other && other != 1 && eq_w(other, "word")) return 6; + return 3; + } + if (dbl) return 2; + if (flt) return 5; + if (vd) return 0; + if (chr) { if (uns) return 11; return 10; } + if (shrt) { if (uns) return 9; return 8; } + if (lng) return 1; + if (ints || uns) { if (uns) return 7; return 4; } + if (other == 1) { + if (!sw) { serr("a struct has to be passed by pointer"); return 0 - 1; } + sk = lookup(sw); + if (sk < 0 || skind[sk] != K_TYPE || sval[sk][0] != TY_STRUCT) { serrn(sw, "isn't a zc struct: a native call takes a zc struct by value, declared with word fields"); return 0 - 1; } + nw = cstruct_ok(sval[sk], sw); + if (nw < 0) return 0 - 1; + ckst = sval[sk]; ckn = sw; + if (nw <= 2) return 13; // in registers when they fit (sysv: 16 bytes or less) + return 14; // in memory: on the stack, word after word + } + if (eq_w(other, "str")) return 12; + if (eq_w(other, "size_t") || eq_w(other, "ssize_t") || eq_w(other, "intptr_t") || eq_w(other, "uintptr_t") || eq_w(other, "ptrdiff_t") || eq_w(other, "int64_t") || eq_w(other, "uint64_t") || eq_w(other, "off_t") || eq_w(other, "time_t") || eq_w(other, "word")) return 1; + if (eq_w(other, "int32_t") || eq_w(other, "i32")) return 4; + if (eq_w(other, "uint32_t") || eq_w(other, "u32")) return 7; + if (eq_w(other, "int16_t") || eq_w(other, "i16")) return 8; + if (eq_w(other, "uint16_t") || eq_w(other, "u16")) return 9; + if (eq_w(other, "int8_t") || eq_w(other, "i8")) return 10; + if (eq_w(other, "uint8_t") || eq_w(other, "u8") || eq_w(other, "byte") || eq_w(other, "bool") || eq_w(other, "_Bool")) return 11; + serrn(other, "isn't a c type extern knows (use long, int, double, a pointer, ...)"); + return 0 - 1; +} +// true for a native kind that travels in a float register. +int ckindfloat(int k) { return k == 2 || k == 5; } +// parse an extern declaration: a c function with its signature, and a zc stub for it. +unchecked int kw_extern() { + int rk; [16]word ks; [16]word kst; [16]word ksn; int np = 0, k, fname; word cname; int i, a, j, n, w, sig, bad = 0; word buf; int pos, len, g; word st, rst; int rn, rsd, sret = 0; + stmtonly(); + next(); + rk = ckind(0); + // a struct result: the stub makes it (new) and passes it as the first item; the + // call block's word 2 says how many words it has and which of them are floats + if (rk >= 13) { + sret = 1; rst = ckst; rn = ckn; + if (rst[1] > 7) { serr("a native call can return at most 7 words"); rk = 0 - 1; } + } + fname = name(); + cname = fname; + expect(s_lp); + if (!is_op(s_rp)) { + while (1) { + k = ckind(1); + if (k < 0) { bad = 1; break; } + if (k == 0 && np == 0 && is_op(s_rp)) break; // (void) + if (k == 0) { serr("a parameter can't be void"); bad = 1; break; } + if (np >= 15) { serr("a native call takes at most 15 arguments"); bad = 1; break; } + ks[np] = k; kst[np] = ckst; ksn[np] = ckn; np++; + if (!accept(s_comma)) break; + } + } + expect(s_rp); + if (accept(s_eq)) { + if (tk != T_STR) { die("expected the c name as a string"); return 0; } + cname = tv; + next(); + } + expect(s_semi); + if (bad || rk < 0 || panic) return 0; + if (sret && np > 14) { serr("a native call that returns a struct takes at most 14 arguments"); return 0; } + rsd = 0; + if (sret) { + rsd = rst[1]; g = 0; + while (g < rst[1]) { + if (rst[4][g * 3 + 1] == tfloat) rsd = rsd | (2 << (4 + 4 * g)); else rsd = rsd | (1 << (4 + 4 * g)); + g++; + } + } + // the block, then the name as packed bytes, then the block's own address + // (the word before the stub's entry, which is how a call site finds it): + // [0] the name's byte address, [1] the signature, [2] a lookup cache the + // host fills in, [3..17] the arguments, [18..] the name + // the arguments' nibbles: one per argument. a struct is a group: code 13 (in + // registers if they fit) or 14 (in memory), its length in eightbytes, then one + // class nibble per eightbyte (2 for a float word, 1 otherwise). the group is one + // stack item: the struct's address + sig = rk; + pos = 1; i = 0; + while (i < np) { + if (ks[i] < 13) { + sig = sig | (ks[i] << (4 * pos)); pos++; + } else { + st = kst[i]; len = st[1]; + if (pos + 1 + len > 15) { serr("the arguments of this native call are too many words"); return 0; } + sig = sig | (ks[i] << (4 * pos)); pos++; + sig = sig | (len << (4 * pos)); pos++; + g = 0; + while (g < len) { + if (st[4][g * 3 + 1] == tfloat) sig = sig | (2 << (4 * pos)); else sig = sig | (1 << (4 * pos)); + pos++; g++; + } + } + i++; + } + j = hole(); + op(O_JMP); + reserve_code(20 + (len_w(cname) >> 3)); a = here; + emit((a + 18) << 3); + emit(sig); + emit(rsd << 32); // the cache's half is the host's; this half is the result's descriptor + i = 3; + while (i < 18) { emit(0); i++; } + n = 0; + while (cname[n]) n++; + i = 0; + while (i < n) { + w = 0; k = 0; + while (k < 8 && i < n) { w = w | (cname[i] << (k * 8)); k++; i++; } + emit(w); + } + if (!(n & 7)) emit(0); + emit(a); + setlit(j, here); + // the stub, compiled as if it had been written in the source. it stays + // for the places that take the function's address; a direct call to it + // compiles to the native-call opcode instead (see call) + ebn = 0; ebuf[0] = 0; + if (sret) { ebs("unchecked "); ebs(rn); ebs("*.u "); } + else if (rk == 5) ebs("f32 "); else if (ckindfloat(rk)) ebs("float "); else ebs("int "); + ebs(fname); ebs("("); + i = 0; + while (i < np) { + if (i) ebs(", "); + if (ks[i] >= 13) { ebs(ksn[i]); ebs(" p"); } else if (ks[i] == 5) ebs("f32 p"); else if (ckindfloat(ks[i])) ebs("float p"); else ebs("int p"); + ebn10(i); + i++; + } + ebs(") { let b = "); ebn10(a); ebs(";"); + if (sret) { ebs(" "); ebs(rn); ebs("* r = new("); ebs(rn); ebs("); b[3] = r;"); } + i = 0; + while (i < np) { ebs(" b["); ebn10(i + 3 + sret); ebs("] = p"); ebn10(i); ebs(";"); i++; } + if (sret) ebs(" syscall(14, b, 0, 0); return r; }"); + else { + ebs(" return syscall(14, b, 0, 0)"); + if (rk == 5) ebs(" as f32"); else if (ckindfloat(rk)) ebs(" as float"); + ebs("; }"); + } + buf = new(ebn + 2); + copy_w(buf, ebuf, ebn + 1); + src_push(2, buf, 0, 0); + next(); + stubgen = 1; // the stub is ours, not the document's: its locals get no hints + while (tk != T_EOF) topitem(); + stubgen = 0; + src_pop(); + free(buf); + k = lookup(fname); + if (k >= 0 && skind[k] == K_FN && (sflag[k] & F_DEF) && !sret) sflag[k] = sflag[k] | F_EXT; // a struct result is called as its stub + return 0; +} +// the c kind of a zc type, for cfunc +int ckindof(word t) { + if (t == tfloat) return 2; + if (t == tf32) return 5; + if (t[0] == TY_INT && t[5]) { // i8 u8 i16 u16 i32 u32 pass and come back at their width + if (t[5] == 1) { if (t[6] & 1) return 10; return 11; } + if (t[5] == 2) { if (t[6] & 1) return 8; return 9; } + if (t[6] & 1) return 4; + return 7; + } + if (t == tvoid) return 0; + if (t[0] == TY_PTR || t[0] == TY_ARR || t[0] == TY_DYN) return 6; + return 1; +} +// cfunc(f): a native function pointer that runs the zc function f, with the +// c signature its declared types give (float is a double, f32 a c float, i8..u32 their width, a pointer is a word +// address, anything else a long). the pointer can be given to c code, which +// may call it while a native call from zc is running. +int kw_cfunc() { + int s; word t; int sig = 1, i, nm; + next(); + expect(s_lp); + if (tk != T_ID) { die("cfunc needs the name of a function"); return 0; } + s = lookup(tv); + if (s < 0 || skind[s] != K_FN) { serrn(tv, "isn't a function"); return 0; } + t = sigof(sval[s]); + if (!t) t = stype[s]; + if (t && t[0] == TY_FN) { + sig = ckindof(t[4]); + i = 0; + if (t[3] > 14) serrn(tv, "has more than 14 parameters"); + while (i < t[3] && i < 14) { sig = sig | (ckindof(t[7 + i]) << (4 * (i + 1))); i++; } + } else { + i = (ssig[s] >> 1) & 31; + while (i > 0) { i--; sig = sig | (1 << (4 * (i + 1))); } + } + expr(); rval(); + lit(sig); + callname(intern("cbmake")); + expect(s_rp); + xty = tint; xfloat = 0; + return 0; +} +// intern the built-in types and the spellings the parser compares against. +// the s_ names are plain globals holding interned handles, so a check like +// is_id(s_fn) is one pointer compare. +int types_init() { + word f32p; + tytop = R_TY + NTL; tyend = R_TYEND; typeblocks = 0; ntl = 0; fieldn = 0; + if (R_TY + typecap > R_TYEND && typelist == R_TY) typelist = chk_resize(0, 0, typecap); + tword = tnew(TY_WORD, 1, intern("word"), 0); + tint = tnew(TY_INT, 1, intern("int"), 0); + tchar = tnew(TY_CHAR, 1, intern("char"), 0); + tbool = tnew(TY_BOOL, 1, intern("bool"), 0); + tvoid = tnew(TY_VOID, 0, intern("void"), 0); + tfloat = tnew(TY_FLOAT, 1, intern("float"), 0); + addsym(tword[2], K_TYPE, tword); addsym(tint[2], K_TYPE, tint); addsym(tchar[2], K_TYPE, tchar); + addsym(tbool[2], K_TYPE, tbool); addsym(tvoid[2], K_TYPE, tvoid); addsym(tfloat[2], K_TYPE, tfloat); + addsym(intern("f64"), K_TYPE, tfloat); + // f32: single precision. a variable holds the rounded value as a double; an element + // of an array or pointer of f32 is packed two to a word, like the sized integers + f32p = tnew(TY_FLOAT, 1, intern("f32"), 0); + tf32 = tnew(TY_FLOAT, 1, f32p[2], 0); + tf32[5] = 4; tf32[4] = f32p; f32p[5] = 4; f32p[6] = 2; + addsym(tf32[2], K_TYPE, tf32); + tsized("i8", 1, 1); addsym(intern("byte"), K_TYPE, tsized("u8", 1, 0)); + tsized("i16", 2, 1); tsized("u16", 2, 0); tsized("i32", 4, 1); tsized("u32", 4, 0); + s_lt = intern("<"); + s_gt = intern(">"); + s_shr = intern(">>"); + s_le = intern("<="); + s_ge = intern(">="); + s_eqeq = intern("=="); s_ne = intern("!="); s_plus = intern("+"); + s_struct = intern("struct"); s_type = intern("type"); s_typecheck = intern("typecheck"); s_docs = intern("docs"); + s_on = intern("on"); + s_off = intern("off"); + s_new = intern("new"); + s_new_t = intern("new_t"); + s_new_k = intern("new_k"); + s_recycle_leaf = intern("recycle_leaf"); + s_hitems = intern("#items"); s_hlen = intern("#len"); s_fatten = intern("fatten"); + s_this = intern("this"); + s_o3 = intern("o3"); + s_lfa = intern("lfa"); + s_outside = intern("outside"); + s_new_raw = intern("new_raw"); + s_oob = intern("oob"); + s_new_oz = intern("new_oz"); + s_ld8 = intern("ld8"); s_st8 = intern("st8"); s_ld16 = intern("ld16"); s_st16 = intern("st16"); + s_ld32 = intern("ld32"); s_st32 = intern("st32"); + s_fixed = intern("fixed"); + s_dlen = intern("dlen"); + s_dcap = intern("dcap"); + s_dnew = intern("dnew"); + s_dat = intern("dat"); + s_len_s = intern("len_s"); + s_at_s = intern("at_s"); + s_slice_s = intern("slice_s"); + s_cat_s = intern("cat_s"); + s_eq_s = intern("eq_s"); + s_lt_s = intern("lt_s"); + s_le_s = intern("le_s"); + s_put_bs = intern("put_bs"); + s_put_s = intern("put_s"); + s_n_to_s = intern("n_to_s"); s_new_s = intern("new_s"); + s_dpush = intern("dpush"); s_dpop = intern("dpop"); s_dresize = intern("dresize"); + numops[0] = intern("*"); numops[1] = intern("/"); numops[2] = intern("%"); numops[3] = intern("&"); + numops[4] = intern("|"); + numops[5] = intern("^"); + numops[6] = intern("<<"); + numops[7] = s_shr; + nnumops = 8; + return 0; +} +// true for an operator that works on numbers. +int isnumop(int o) { + int i = 0; + while (i < nnumops) { + if (numops[i] == o) return 1; + i++; + } + return 0; +} +// the type of a binary operation, checking it when the typechecker is on +// the result type of binary operator b, which is also where the typechecker +// complains: pointer plus int is that pointer, pointer minus pointer is an int, +// word on either side wins, and a float on either side makes arithmetic float. +word bintype(int b, word lt, word rt) { + int o = bname[b]; + lt = decay(lt); rt = decay(rt); + if (blen[b] < 0) { tcneed(!isagg(lt) && !isagg(rt), "&& and || need scalars", lt); return tbool; } + if (isfl(lt) || isfl(rt)) { + if (o == s_plus || o == s_minus || o == s_star || o == s_slash) { + if (lt[0] == TY_PTR || rt[0] == TY_PTR) { tcerr("+", lt, rt); return tword; } + return tfloat; + } + if (o == s_eqeq || o == s_ne || o == s_lt || o == s_gt || o == s_le || o == s_ge) return tbool; + } + if (o == s_eqeq || o == s_ne) { + if (!compat(lt, rt) && !compat(rt, lt)) tcerr("comparison", lt, rt); + return tbool; + } + if (o == s_lt || o == s_gt || o == s_le || o == s_ge) { + if (!(lt == tword || rt == tword || (isnum(lt) && isnum(rt)) || (lt[0] == TY_PTR && rt[0] == TY_PTR))) { + tcerr("comparison", lt, rt); + } + return tbool; + } + if (o == s_plus) { + if (lt[0] == TY_PTR && (isnum(rt) || rt == tword)) return lt; + if (rt[0] == TY_PTR && isnum(lt)) return rt; + if (lt == tword || rt == tword) return tword; + if (isnum(lt) && isnum(rt)) return tint; + tcerr("+", lt, rt); return tword; + } + if (o == s_minus) { + if (lt[0] == TY_PTR && rt[0] == TY_PTR) return tint; + if (lt[0] == TY_PTR && (isnum(rt) || rt == tword)) return lt; + if (lt == tword || rt == tword) return tword; + if (isnum(lt) && isnum(rt)) return tint; + tcerr("-", lt, rt); return tword; + } + if (isnumop(o)) { + if (lt == tword || rt == tword) return tword; + if (isnum(lt) && isnum(rt)) return tint; + if (isnum(lt)) lt = rt; + tcneed(0, "arithmetic needs numbers", lt); return tword; + } + return tword; // operators a program added +} + +// ---- reflection ---- +// for programs (and parse words) looking at types at runtime +word tkind(word t) { return t[0]; } +// the size of a type in words. +word tsize(int t) { return tsz(t); } +// the name of a type. +word tname(word t) { return t[2]; } +// the length of an array type. +word tlen(word t) { return t[3]; } +// the number of fields of a struct type. +word tnfields(word t) { tresolve(t); return t[3]; } +// ---- fattening ---- +// typed new stamps the header, in o3 the same as outside it. fatten is for a +// value that still has new_raw's blank type word when it reaches code that +// isn't o3: a store into a normal local or a global, an argument to a +// function that isn't o3, the call site where non-o3 code calls an o3 +// function whose result has pointers, and the end of an o3 block. a child +// store of a value new(T) just allocated, or that an o3 function returned, +// does not call fatten: the header is already that type. a call or return +// from o3 to o3 does not fatten. fatten(p, t) stamps the type, zeroes the +// slack, and follows every typed pointer. it returns p. +// a type whose objects can never hold a pointer: numbers, chars, bools, and +// arrays and plain structs of them. word, generics and anything unknown say no +// two questions about a type's shape, both answered structurally: +// isleaf can an object of this type never hold a heap pointer? the +// allocator uses it to pick the recycle_leaf fast path (no children +// to walk, so the block goes straight back on its size-class list). +// hasptr can it hold one? fatten uses it to decide whether stamping a +// type has to follow pointers. +word isleaf(word t) { + int k = t[0], i, n; + if (k == TY_INT || k == TY_CHAR || k == TY_BOOL || k == TY_FLOAT) return 1; + if (k == TY_ARR) return isleaf(t[4]); + if (k == TY_STRUCT && !t[5]) { + n = tnfields(t); + i = 0; + while (i < n) { + if (!isleaf(tftype(t, i))) return 0; + i++; + } + return 1; + } + return 0; +} +// true when a value of type t holds a pointer that fattening must follow. +word hasptr(word t) { + int k = t[0], i, n; + if (k == TY_PTR || k == TY_DYN) return 1; + if (k == TY_ARR) return hasptr(t[4]); + if (k == TY_STRUCT && !t[5]) { + n = tnfields(t); + i = 0; + while (i < n) { + if (hasptr(tftype(t, i))) return 1; + i++; + } + } + return 0; +} +// stamp p with its type t, then the pointers it holds. +int fatten(int p, int t) { fat1(p, t, 32); return p; } +// stamp p with type t if it has no type yet, then follow its pointers. dl is a +// depth limit so a cycle through growable arrays ends. a raw header holds +// -(n + 1) for an object of n words, which tells how many elements to type. +int fat1(word p, word t, int dl) { + word e; int n, k, i, es; + if (dl <= 0 || !t || !isobj(p)) return 0; + if (t[0] == TY_DYN) { // a handle is always fat; what it holds may not be + if (hasptr(t[4])) { + es = tsz(t[4]); + n = p[0]; + i = 0; + while (i < n) { fatin(p[2] + i * es, t[4], dl - 1); i++; } + } + return 0; + } + if (t[0] != TY_PTR || p[-1] >= 0) return 0; + e = t[4]; n = 0 - p[-1] - 1; es = tsz(e); + if (es < 1) es = 1; + if (e == tword || e[0] == TY_VAR) p[-1] = 0; + else if (n > es) p[-1] = tarr(e, 0); + else p[-1] = e; + if (osize(p) > n) fill_w(p + n, 0, osize(p) - n); + if (hasptr(e)) { k = n / es; i = 0; while (i < k) { fatin(p + i * es, e, dl - 1); i++; } } + return 0; +} +// fatten the value stored at address a, which has type t: a pointer or handle +// is fattened itself, an array or struct is walked element by element +word fatin(word a, word t, int dl) { + int k = t[0], i, n, o, ft, es; + if (k == TY_PTR || k == TY_DYN) return fat1(a[0], t, dl); + if (k == TY_ARR) { + if (!hasptr(t[4])) return 0; + n = t[3]; + es = tsz(t[4]); + i = 0; + while (i < n) { fatin(a + i * es, t[4], dl); i++; } + return 0; + } + if (k == TY_STRUCT && !t[5]) { + n = tnfields(t); + i = 0; + while (i < n) { + o = tfoff(t, i); + ft = tftype(t, i); + if (o >= 0) fatin(a + o, ft, dl); + i++; + } + } + return 0; +} +// reflection accessors over type records (see the layout under types). they +// resolve a generic instance first, so asking about vec works. +word tfname(word t, int i) { tresolve(t); return t[4][i * 3]; } +// the type of field i of a struct. +word tftype(word t, int i) { tresolve(t); return t[4][i * 3 + 1]; } +// the offset of field i of a struct. +word tfoff(word t, int i) { tresolve(t); return t[4][i * 3 + 2]; } +// the return type of a function type. +word tret(word t) { return t[4]; } +// the number of parameters of a function type. +word tnparams(word t) { return t[3]; } +// the type of parameter i of a function type. +word tparam(word t, int i) { return t[7 + i]; } +// the tag of parameter i: 0 untagged, 1 .m, 2 .d. +word tparamtag(word t, int i) { return ((t[5] >> 6) >> (i + i)) & 3; } // 0 untagged (m), 1 .m, 2 .d +// true when a function type returns an owned object (.u). +word tretu(word t) { return t[5] & 1; } +// the number of type arguments a generic struct instance has. +word tnargs(word t) { + if (t[0] != TY_STRUCT || !t[5]) return 0; + return t[6][0]; +} +// type argument i of a generic struct instance. +word targ(word t, int i) { return t[6][1 + i]; } +// the type of a function, by address. untyped functions get one made from their signature +// ---- function types by address ---- +word sigof(int f) { + int i = nsym, s = -1; [24]word ws; int n, k; + while (i) { i--; if (skind[i] == K_FN && sval[i] == f && (sflag[i] & F_DEF)) { s = i; break; } } + if (s < 0) return 0; + if (stype[s]) return stype[s]; + n = (ssig[s] >> 1) & 31; k = 0; + while (k < n) { ws[k] = tword; k++; } + return tfn(tword, n, ws, ssig[s]); +} +// the function type of the function named nm, or 0 when there is none. +word sigofs(int nm) { + int s = lookups(nm); + if (s < 0 || skind[s] != K_FN) return 0; + return sigof(sval[s]); +} + +// ---- expressions ---- +// every expression leaves its value on the stack, returns its analyzer class, +// and sets xty to its type. lv means the stack holds an address that hasn't +// been loaded yet; structs and arrays are never loaded, their address is +// their value. +// memdebug: the address on top of the stack, checked before a heap load +// (dbg_addr duplicates the address on top and calls the runtime's checkptr on +// the copy, so memdebug catches a load from a freed object) +int dbg_addr() { + if (mdbg && !dry) { + lit(0); + op(O_PICK); + callname(s_check); + op(O_DROP); + } + return 0; +} +// turn an lvalue into a value. when lv is set the stack holds the address of a +// variable, field or element that has not been loaded; rval loads it, unless +// the thing is an array or struct, whose address is its value. every place +// that needs a value calls this after parsing an expression. +int rval() { + if (lv && lvk == LV_LOCAL) ltag[lvi] = ltag[lvi] | LREAD; + if (lv) { + if (ispk(xty)) pkld(xty); + else if (!isagg(xty)) { dbg_addr(); op(O_LD); } + lv = 0; + } + return 0; +} +// run a keyword's handler (kw_len, kw_struct, a parse word...) from the spot +// where the parser met it. e is 1 in expression position, where statement-only +// keywords refuse to run. the handler reports the class of what it produced +// in xcls. +int kwcall(int s, int e) { + int f = sval[s], old = kw_expr, c, n0 = ntok; + kw_expr = e; xcls = C_PLAIN; xty = tword; xzero = 0; + f(); xaf = 0; + // a handler that never reads a token (not even its own name) would be called + // again on the same token forever, so it is an error, and the token is skipped + if (ntok == n0) die("a parse word has to read its own name with next()"); + xbor = -1; + kw_expr = old; + c = xcls; + lv = 0; lvk = LV_NONE; + return c; +} +// report an error when a statement is used inside an expression. +int stmtonly() { + if (kw_expr) serr("this can't be used inside an expression"); + return 0; +} +// the arguments of a call. ft is the callee's type (or 0), bs the generic bindings +// (tags packs two bits per parameter: 0 lend, 1 .m, 2 .d. bs collects type +// parameter bindings as the argument types are unified with the signature. +// fat asks for every pointer argument to be fattened first, for o3 code +// calling code that is not o3.) +int args(int tags, word ft, int bs, int fat) { + int np = 0, c, pt, t, cs = ifcall, freeing = 0, harmless = 0; word first = 0; + if (cs >= 0 && cs < ubase) { + freeing = sname[cs] == s_free || sname[cs] == s_recycle; + harmless = sname[cs] == s_put_s || eq_w(sname[cs], "put_n") || eq_w(sname[cs], "put_c") || eq_w(sname[cs], "len") || eq_w(sname[cs], "cap"); + } + xgive = 0; ifcall = 0 - 1; + expect(s_lp); + if (!is_op(s_rp)) { + while (1) { + c = expr(); rval(); + if (!np) first = xaf; + if (!freeing && !harmless) af_escape(xaf); + if (cs >= 0) if_arg(cs, np, xty, xzero); + if (ft && np < ft[3]) fcoerce(subst(ft[7 + np], bs)); + t = xty; + argcheck(c, (tags >> (np + np)) & 3); + // o3 data handed to normal code + if (fat && hasptr(t)) { + lit(t); + callname(s_fatten); + xty = t; + } + if (ft && np < ft[3]) { + pt = ft[7 + np]; + if (!unify(pt, xty, bs)) tcerr("argument", subst(pt, bs), xty); + } + np++; + if (!accept(s_comma)) break; + } + } + expect(s_rp); + argfact = first; xaf = 0; + return np; +} +// memdebug records the source line on the next new, via the runtime global +// memdebug: store the current line in the runtime's alloc_line just before an +// allocation, so a leak report can say where the object came from +int stamp_site() { + int s; + if (!mdbg || dry || o3on) return 0; + s = lookup(s_alloc); + if (s < 0) return 0; + lit(tline); lit(sval[s]); op(O_ST); + return 0; +} +// new(T) and new(T, count): a typed object, its type stamped into the header +// new(T), new(T, n), new([]T), new(fixed []T, n). a growable array goes to +// dnew. otherwise the call is new_t(size, type), unless the size is known at +// compile time and we are not in o3 or a dry run: then the size class is +// computed here and the call is new_k(size, type, class), which pops a block +// straight off the class list. newsz and newleaf record what was allocated so +// the variable it lands in can later be freed with the matching fast path. +int newtyped() { + word t; int c, fast = 0, length = 1, place = tpack(); + t = ptype(); + if (t[0] == TY_DYN) { // a growable array: element size, length, type + if (tbytes(t[4])) lit(0 - tbytes(t[4])); else lit(tsz(t[4])); // packed elements say so with a negative size: minus their bytes + if (accept(s_comma)) { c = expr(); rval(); plainuse(c); length = -1; if (xconst && xcval >= 0) length = xcval; } + else { length = 0; lit(0); if (t[3]) serr("a fixed array needs its length: new(fixed []T, n)"); } + lit(t); + expect(s_rp); + stamp_site(); + callname(s_dnew); + xaf = af_new(length, 3, place); + lv = 0; lvk = LV_NONE; xty = t; xzero = 0; xstamp = 1; + return C_FRESH; + } + if (accept(s_comma)) { + c = expr(); rval(); plainuse(c); + length = -1; if (xconst && xcval >= 0) length = xcval; + if (tbytes(t)) { // packed: n elements take (n * bytes + 7) / 8 words + if (tbytes(t) != 1) { lit(tbytes(t)); op(15); } + lit(7); op(O_ADD); lit(3); op(19); + } else if (tsz(t) != 1) { lit(tsz(t)); op(15); } + lit(tarr(t, 0)); + newsz = -1; newleaf = 0; + } else { + lit(tsz(t)); lit(t); + newsz = tsz(t); newleaf = 0; + if (isleaf(t)) newleaf = t; + if (!o3on && !dry && lookup(s_new_k) >= 0) { lit(szclass(newsz)); fast = 1; } + } + expect(s_rp); + stamp_site(); + if (fast) callname(s_new_k); else callname(s_new_t); + xaf = af_new(length, 2, place); + lv = 0; lvk = LV_NONE; xty = tptr(t); xzero = 0; xstamp = 1; + return C_FRESH; +} +// ld8/st8 and the 16- and 32-bit pair. 0 when this call should stay a call. +// a store's opcode is odd: the arguments arrive address then value, and the +// opcode wants the address on top. +// (the byte-access functions become single opcodes inside o3 code) +int byteop(int s) { + if (sname[s] == s_ld8) return 612; + if (sname[s] == s_st8) return 613; + if (sname[s] == s_ld16) return 614; + if (sname[s] == s_st16) return 615; + if (sname[s] == s_ld32) return 616; + if (sname[s] == s_st32) return 617; + return 0; +} +// ---- type facts ---- +// annotate mode (the language server asks for it, ifon) collects the types the +// untyped declarations of a document could be given, as edits: a place, how +// many chars it replaces (0 inserts) and the text. a local gets the type of +// its initializer, a parameter the type every call passes it, and a function +// the type of what it returns. only types that change nothing are offered: no +// floats and no sized integers, because a typed one converts or cuts to size +// where the untyped word did not, so the annotated program is the same program. +// the declarations are found while they compile, the calls too, and the +// edits are written at the end of the unit, when every call has been seen. +// a type that can be written in front of a name and changes nothing +word ifpure(word t) { + int k; + if (!t || t == tword || t == tvoid) return 0; + k = t[0]; + if (k == TY_INT) return t[5] == 0; + if (k == TY_CHAR || k == TY_BOOL || k == TY_STRUCT) return t[2] != 0; + if (k == TY_PTR) { + k = t[4][0]; + if (k == TY_ARR || k == TY_DYN || k == TY_FN) return 0; + return t[4] == tword || ifpure(t[4]); + } + if (k == TY_ARR || k == TY_DYN) return t[4] == tword || ifpure(t[4]); + return 0; +} +// one edit: the token at place p is replaced by the type t and then the text +// after (len < 0: the token's own length; 0: nothing is replaced) +int fact_add(int p, int len, int t, int after) { + int sv, o, at, n; word e; int osm = outmode, osb = outbuf, osn = outn, osc = outcap; + // the text is printed with tprint into the pool: output mode 2 writes into outbuf + if (!ifx || dry || panic || chkabort) return 0; + sv = tpack(); tunpack(p); o = toff; if (len < 0) len = toklen; tunpack(sv); + if (o < 0) return 0; + at = nift; + outn = 0; outmode = 3; tprint(t); put_s(after); n = outn; + if (nifx >= ifxcap) { ifxcap = ifxcap * 2; ifx = chk_resize(ifx, nifx * 4, ifxcap * 4); } + if (at + n > iftcap) { while (at + n > iftcap) iftcap = iftcap * 2; ift = chk_resize(ift, at, iftcap); } + outbuf = ift + at; outn = 0; outcap = n; outmode = 2; + tprint(t); put_s(after); + n = outn; + outmode = osm; outbuf = osb; outn = osn; outcap = osc; + e = ifx + nifx * 4; + e[0] = o; e[1] = len; e[2] = at; e[3] = n; + nifx++; nift = at + n; + return 0; +} +// the record of function symbol s: calls can come before the definition +word if_get(int s) { + int id; word r; + if (!ifs || s < 0 || s >= symcap) return 0; + id = ifs[s]; + if (id) return ifr[id - 1]; + if (nif >= ifrcap) { ifrcap = ifrcap * 2; ifr = chk_resize(ifr, nif, ifrcap); } + nif++; ifs[s] = nif; + r = chk_resize(0, 0, IFW); ifr[nif - 1] = r; + fill_w(r, 0, IFW); + r[0] = s; + return r; +} +// a state is 0 (nothing seen), a type, or -1 (no single type): p points at it +int if_join(word p, int t) { + if (*p == 0 - 1) return 0; + if (!ifpure(t)) *p = 0 - 1; + else if (!*p) *p = t; + else if (*p != t) *p = 0 - 1; + return 0; +} +// argument j of a call of s has the type t (a literal 0 says nothing: it is a +// null pointer as often as a number) +int if_arg(int s, int j, int t, int zero) { + int r; + if (!ifs || dry || j >= IFP || zero) return 0; + r = if_get(s); + if (r) if_join(r + 8 + 3 * j, t); + return 0; +} +// a function at the top level starts: its header is at ifkp and ifkk says how +// to edit it (1: fn becomes the type, 2: the type goes in front of a .u) +int if_begin(int s, int k) { + word r = if_get(s); + ifcur = 0; + if (!r) return 0; + r[2] = 1; r[5] = ifkp; r[6] = k; + ifcur = r; + return 0; +} +// parameter j: p is where its type would go, kind 0 for a typed one, 1 to +// insert the type and a space, 2 to insert it right in front of a tag +int if_param(int j, int p, int kind) { + word r = ifcur; + if (!r || j >= IFP) return 0; + r[8 + 3 * j] = 0; r[9 + 3 * j] = p; r[10 + 3 * j] = kind; + if (kind) r[7] = j + 1; + return 0; +} +// a return statement: t is what it returns, zero if that is a bare 0 +int if_ret(int t, int zero) { + word r = ifcur; + if (!r || nclo || dry) return 0; + r[1] = 1; + if (zero) r[4] = 1; else if_join(r + 3, t); + return 0; +} +// the function ends: which of its parameters were used as addresses +int if_fin() { + word r = ifcur; int j = 0; + while (j < r[7]) { + if (lptr[lbase + j]) r[10 + 3 * j] = r[10 + 3 * j] | 4; + j++; + } + return 0; +} +// the edits of every function, once all the calls have been seen +int if_emit() { + int i = 0, j; word r; int t, k; + while (i < nif) { + r = ifr[i]; + i++; + if (!r[2]) continue; + j = 0; + while (j < r[7]) { + k = r[10 + 3 * j]; t = r[8 + 3 * j]; + if ((k & 3) && !t && (k & 4)) t = tptr(tword); // no call says more, but the body uses it as an address + if ((k & 3) == 1 && t > 0) fact_add(r[9 + 3 * j], 0, t, " "); + else if ((k & 3) == 2 && t > 0) fact_add(r[9 + 3 * j], 0, t, ""); + j++; + } + t = r[3]; + if (r[1] && t != 0 - 1) { + if (!t && r[4]) t = tint; + if (t && r[6] == 1) fact_add(r[5], 0 - 1, t, ""); + else if (t && r[6] == 2) fact_add(r[5], 0, t, ""); + } + } + return 0; +} +// ---- hints ---- +// findings that are not errors. each is written only in check mode, through +// hint_begin, and none of them changes the code the compiler emits. +int magic(int v) { + if (!hint_begin(tpack())) return 0; + put_s("the number "); put_n(v); put_s(" has no name; a const says what it means"); + dg_close(); + return 0; +} +// warn that a new inside a loop allocates on every pass. +int newloop(int p) { + if (!hint_begin(p)) return 0; + put_s("new inside a loop allocates on every pass; allocate once above the loop if the object can be reused"); + dg_close(); + return 0; +} +// hint when a shift by v is past the width of the value. +int shifthint(int p, int v) { + int k = 0; + if (!hint_begin(p)) return 0; + while ((1 << k) < v) k++; + put_s("multiplying by "); put_n(v); put_s(" is a shift; write << "); put_n(k); + dg_close(); + return 0; +} +// a call to function symbol s, with the callee name already consumed and the +// ( next. in order: +// 1. new(...) with a type argument is redirected to newtyped +// 2. in o3 code, untyped new becomes new_oz and the byte accessors turn into +// opcodes +// 3. the arguments are parsed against the signature (args), checking each +// against its parameter tag; generic type parameters are bound here +// 4. free whose argument was a given-away last pointer becomes recycle; a +// constant-size new becomes new_k with its class; put_s of a byte string +// becomes put_bs +// 5. the call is emitted (fnref pushes the address, or a patch site if the +// function isn't defined yet) +// 6. the result's type and class are set: .u functions return C_FRESH, which +// is how the analyzer knows the caller now owns a new object +int call(int s) { + int np, tags = 0, ret = 0; word ft = stype[s]; [8]word bs; int rs, rt = 0, kop, cp = callpos, original = s; word storage; + if (sname[s] == s_new && skind[s] == K_FN) { + peek(); + if (pk_tk == T_OP && pk_tv == s_lk) { next(); return newtyped(); } + if (pk_tk == T_ID && (istypename(pk_tv) || pk_tv == s_fn)) { next(); return newtyped(); } + } + if (o3on && sname[s] == s_new) { tags = lookup(s_new_oz); if (tags >= 0) s = tags; tags = 0; } + if (o3on) { + kop = byteop(s); + if (kop) { + np = args(0, 0, bs, 0); + if (kop & 1) { + if (np != 2) serrn(sname[s], "wrong number of arguments"); + op(O_SWAP); op(kop); lit(0); + } else { + if (np != 1) serrn(sname[s], "wrong number of arguments"); + op(kop); + } + lv = 0; lvk = LV_NONE; xty = tword; xzero = 0; xbor = -1; + xfloat = 0; xstr = 0; xlenloc = -1; xstamp = 0; + return C_PLAIN; + } + } + if (sflag[s] & F_SIG) { tags = ssig[s] >> 6; ret = ssig[s] & 1; } + else sflag[s] = sflag[s] | F_UNTYPED; + if (sflag[s] & F_RETOWN) ret = 1; + fill_w(bs, 0, 8); + if (ngbind) { copy_w(bs, gbind, ngbind); ngbind = 0; } + np = 0; + if (o3on && !(sflag[s] & F_O3) && sname[s] != s_free && sname[s] != s_recycle) np = 1; // o3 calling normal code: fatten the arguments + ifcall = s; + np = args(tags, ft, bs, np); + storage = argfact; + if (original < ubase && (sname[original] == s_free || sname[original] == s_recycle)) af_end(storage, cp); + if (sflag[s] & F_GEN) s = ginstance(s, bs); + if (sname[s] == s_free && xgive) { // the last pointer: straight back to the size-class list + rs = lookup(s_recycle); + if (rs >= 0) s = rs; + } + if ((sflag[s] & F_SIG) && np != ((ssig[s] >> 1) & 31)) serrn(sname[s], "wrong number of arguments"); + if (sname[s] == s_new) { + stamp_site(); newsz = -1; newleaf = 0; + if (np == 1 && xconst && xcval >= 0) newsz = xcval; + xaf = af_new(newsz, 2, cp); + if (lsp > 0) newloop(cp); + // a constant size: the class is known now, so use the allocator's direct path + if (newsz >= 0 && !o3on && !dry && lookup(s_new_k) >= 0) { + lit(0); + lit(szclass(newsz)); + s = lookup(s_new_k); + } + } + if (sname[s] == s_put_s && np == 1 && xstr) { + s = lookup(s_put_bs); + if (s < 0) s = fwdfn(s_put_bs); + } + if (!(sflag[s] & F_DEF) || s == finsym) sflag[s] = sflag[s] | F_EARLY; // called before its body: an owned return is not inferred for it + if (sflag[s] & F_EXT) { lit(sval[s][-1]); op(620); } // an extern: the native call opcode, with the block's address + else { fnref(s); op(O_CALL); } + if (!o3on && (sflag[s] & F_O3) && ft) { // non-o3 called o3: fatten the result here, not inside the callee + rt = subst(ft[4], bs); + if (hasptr(rt)) { lit(rt); callname(s_fatten); } + } + xstr = 0; + if (sname[s] == s_cat_s || sname[s] == s_slice_s || sname[s] == s_n_to_s || sname[s] == s_new_s) { + xstr = 1; + } + lv = 0; lvk = LV_NONE; xzero = 0; xbor = -1; xconst = 0; + xty = tword; + if (ft) xty = subst(ft[4], bs); + xfloat = 0; + if (isfl(xty)) xfloat = 1; + xlenloc = -1; xstamp = 0; + if (o3on && (sflag[s] & F_O3) && ft && hasptr(xty)) xstamp = 1; + if (ret) return C_FRESH; + return C_PLAIN; +} +// an enhancement used like a function: the arguments, then its one opcode +// an enhancement (a custom opcode declared with "enhance") called like a +// function. it compiles to the one opcode, or to a call of its fallback +// function when the cpu does not have the opcode. +int enhcall(int s) { + int nin = ssig[s] & 15, nout = (ssig[s] >> 4) & 15, n = 0, c; + expect(s_lp); + if (!is_op(s_rp)) { + while (1) { + c = expr(); + rval(); + plainuse(c); + n++; + if (!accept(s_comma)) break; + } + } + expect(s_rp); + if (n != nin) serrn(sname[s], "wrong number of inputs"); + lv = 0; lvk = LV_NONE; xty = tword; xzero = 0; xbor = -1; xlenloc = -1; xstamp = 0; + if (sflag[s] & F_FALLBACK) { fnref(sfix[s] - 1); op(O_CALL); return C_PLAIN; } + op(XOP0 + sval[s]); + if (nout == 0) lit(0); + if (nout > 1) serrn(sname[s], "leaves several values; use it from asm"); + return C_PLAIN; +} +// ---- closures ---- +// fn(a, b) { ... } is a function whose last, hidden parameter is a pointer to +// its environment. the closure value is a heap block: word 0 is the body's +// entry, and the captured variables start at word 3. the block itself is never +// run as code. callable_code distinguishes plain function addresses, including +// linked heap code segments, from closure blocks. a plain function is called +// as it is. a closure is called with block + 3 as its env, at the entry in +// word 0. a captured variable is copied in when the closure is made; the body +// reads and writes its own copy. +// grow closure frames and capture rows while keeping closure and capture indices. +int reserve_closures(int need) { + int cap = closure_cap; + if (need <= cap) return cap; + while (cap < need) cap = cap * 2; + clo_lo = chk_resize(clo_lo, nclo, cap); + clo_hi = chk_resize(clo_hi, nclo, cap); + clo_n = chk_resize(clo_n, nclo, cap); + clo_env = chk_resize(clo_env, nclo, cap); + clo_ret = chk_resize(clo_ret, nclo, cap); + clo_name = chk_resize(clo_name, nclo * capture_width, cap * capture_width); + clo_loc = chk_resize(clo_loc, nclo * capture_width, cap * capture_width); + clo_via = chk_resize(clo_via, nclo * capture_width, cap * capture_width); + closure_cap = cap; return cap; +} +int reserve_captures(int need) { + int cap = capture_width, i = 0; word names, locals, via; + if (need <= cap) return cap; + while (cap < need) cap = cap * 2; + names = chk_resize(0, 0, closure_cap * cap); + locals = chk_resize(0, 0, closure_cap * cap); + via = chk_resize(0, 0, closure_cap * cap); + while (i < nclo) { + copy_w(names + i * cap, clo_name + i * capture_width, clo_n[i]); + copy_w(locals + i * cap, clo_loc + i * capture_width, clo_n[i]); + copy_w(via + i * cap, clo_via + i * capture_width, clo_n[i]); i++; + } + clo_name = names; clo_loc = locals; clo_via = via; capture_width = cap; return cap; +} +// find or create the capture slot for variable n in the innermost closure +// being compiled: if the name is one of the enclosing function's locals, it is +// recorded (once) and the body reads it from its own environment copy. +// a name from further out is captured through each closure in between: the +// closure that encloses this one captures it too, and this one copies it from +// that closure's environment (clo_via is the slot there, -1 for a local). +word clocap(int n) { return clocap_at(nclo - 1, n); } +// the slot of the name n in closure c's captures, adding it when the closure has +// not captured it yet. returns the capture index, or -1 when no such name is visible. +word clocap_at(int c, int n) { + int i = 0, j, base = c * capture_width, r; + while (i < clo_n[c]) { if (clo_name[base + i] == n) return i; i++; } + j = clo_hi[c]; + while (j > clo_lo[c]) { + j--; + if (lname[j] != n) continue; + if (larr[j]) serrn(n, "an array can't be captured; capture a pointer to it"); + reserve_captures(clo_n[c] + 1); base = c * capture_width; + clo_name[base + clo_n[c]] = n; clo_loc[base + clo_n[c]] = j; clo_via[base + clo_n[c]] = 0 - 1; clo_n[c]++; + return clo_n[c] - 1; + } + if (c == 0) return 0 - 1; + r = clocap_at(c - 1, n); base = c * capture_width; + if (r < 0) return 0 - 1; + reserve_captures(clo_n[c] + 1); base = c * capture_width; + clo_name[base + clo_n[c]] = n; clo_loc[base + clo_n[c]] = clo_loc[(c - 1) * capture_width + r]; clo_via[base + clo_n[c]] = r; clo_n[c]++; + return clo_n[c] - 1; +} +// fn(params) { body } as an expression. the body is compiled in place behind +// a jump (hop) as a nested function with a hidden last parameter, env. when +// the code that creates the closure runs, it allocates the block (code, then +// captured values), writes the three code words and copies each captured +// variable in. ctx_push / ctx_pop save and restore the enclosing function's +// compiler state around the body. +int closure() { + int np = 0; [20]word pn; [20]word pty; int hop, entry, i, t, envl, ncap, c, result; + reserve_closures(nclo + 1); + if (o3on) serr("closures can't be made inside an o3 region"); + next(); + expect(s_lp); + if (!is_op(s_rp)) { + while (1) { + pty[np] = tword; + if (!is_op(s_dot) && ((tk == T_OP && tv == s_lk) || (tk == T_ID && (tv == s_fn || istypename(tv))))) { + pty[np] = ptype(); + } + pnoffs[np] = toff; rs_note(RS_PARAM, toff); pn[np] = name(); + np++; + if (np >= 20) { die("too many parameters"); break; } + if (!accept(s_comma)) break; + } + } + expect(s_rp); + ctx_push(); ifcur = 0; ifcall = -1; + clo_lo[nclo] = lbase; clo_hi[nclo] = nloc; clo_n[nclo] = 0; clo_ret[nclo] = 0; + nclo++; + hop = hole(); op(O_JMP); + entry = here; + fn_begin(1, 0); + prologue(); + i = 0; + while (i < np) { + t = newloc(pn[i]); lrskind[t] = RS_PARAM; lrsoff[t] = pnoffs[i]; fslots++; lsmark(1, pn[i]); + ltype[t] = pty[i]; + if (isfl(pty[i])) setflt(t, 1); + i++; + } + envl = newloc(intern("")); fslots++; + clo_env[nclo - 1] = envl; + i = np + 1; + while (i) { i--; laddr(i); op(O_ST); } + body(); + result = clo_ret[nclo - 1]; + if (result <= 0 || live) result = tword; + epilogue(); + nclo--; + c = nclo; + ncap = clo_n[c]; + setlit(hop, here); + ctx_pop(); + // the block: [push env][push body][jump] and the captured values + // the block: word 0 is the body's entry, words 3 on are the captured values. + // a call through the closure pushes block + 3 as the env and jumps to word 0. + lit(3 + ncap); callname(s_new); + lit(entry); lit(1); op(O_PICK); op(O_ST); + i = 0; + while (i < ncap) { + t = clo_loc[c * capture_width + i]; + if (clo_via[c * capture_width + i] < 0) { touch(t); vaddr(t); op(O_LD); } + else { vaddr(clo_env[c - 1]); op(O_LD); lit(clo_via[c * capture_width + i]); op(O_ADD); op(O_LD); } + lit(1); op(O_PICK); lit(3 + i); op(O_ADD); op(O_ST); + i++; + } + lv = 0; + lvk = LV_NONE; + xzero = 0; + xbor = -1; + xconst = 0; + xfloat = 0; + xstr = 0; + xlenloc = -1; + xstamp = 0; + xty = tfn(result, np, pty, np + np); + return C_FRESH; +} +// ---- the expression grammar ---- +// precedence, loosest to tightest: +// expr (assignment, ?:) -> bin (binary operators by precedence) -> +// unary (prefix operators, casts, recasts) -> postfix (index, field, call, +// ++ --, as) -> primary (literals, names, parentheses, blocks, closures) +// each level leaves its value on the stack and sets the x* globals: xty is the +// type, xfloat / xstr say the value is a float or byte string, xzero and +// xconst mark a literal 0 or constant (xcval holds it), xbor names the local a +// pointer value was borrowed from, and lv / lvk / lvi say the stack holds an +// unloaded address and what it is the address of. +// +// primary: one operand. a number, string, parenthesized expression, block +// expression, closure, or a name. a name is looked up in this order: a local +// of the current function, a variable captured by an enclosing closure, then +// the symbol table (keyword, constant, global, enhancement, function). a local +// the analyzer tracks returns C_VAR + its index so the caller can tell which +// variable the value is. +int primary() { + int n, i, s, c, k; [8]word bs0; int ft, mn, ms; + lv = 0; lvk = LV_NONE; xzero = 0; xbor = -1; + xconst = 0; xfloat = 0; xstr = 0; xlenloc = -1; xstamp = 0; xaf = 0; + if (tk == T_FLT) { op(OP_FLIT); emit(tv); xfloat = 1; xty = tfloat; next(); return C_PLAIN; } + if (tk == T_NUM) { + if (cmode && tv >= 100 && tglue != 3 && !nomagic && (tv & (tv - 1)) && (tv & (tv + 1))) { + magic(tv); + } + lit(tv); xzero = tv == 0; xconst = 1; xcval = tv; xty = tint; next(); return C_PLAIN; + } + if (tk == T_BSTR) { bstrlit(tv); xstr = 1; xty = tword; next(); return C_PLAIN; } + if (tk == T_STR) { strlit(tv); next(); xty = tptr(tchar); return C_PLAIN; } + if (tk == T_OP) { + if (tv == s_lp) { next(); c = expr(); expect(s_rp); return c; } + if (tv == s_lb) return blockexpr(); + diefound("expected an expression"); lit(0); xty = tword; return C_PLAIN; + } + if (tk != T_ID) { diefound("expected an expression"); lit(0); xty = tword; return C_PLAIN; } + if (tv == s_fn) { peek(); if (pk_tk == T_OP && pk_tv == s_lp) return closure(); } + n = tv; callpos = tpack(); + i = findloc(n); + if (i < 0 && nclo) { + k = clocap(n); + if (k >= 0) { + rs_note(RS_LOCAL, 0 - 1); + next(); + i = nclo - 1; + vaddr(clo_env[i]); op(O_LD); lit(k); op(O_ADD); + i = clo_loc[i * capture_width + k]; + xty = ltype[i]; xfloat = getflt(i); if (isfl(xty)) xfloat = 1; xstr = getstr(i); + lv = 1; lvk = LV_OTHER; + return C_PLAIN; + } + } + if (i >= 0) { + luse(i); + rs_note(lrskind[i], lrsoff[i]); + xaf = lafact[i]; af_record(toff, xaf); + next(); + if (!fchk && tracked(i)) { + serrn(n, "is tracked by the analyzer and can't be used in unchecked code"); + } + vaddr(i); + xty = ltype[i]; + if (larr[i]) return C_PLAIN; + xfloat = getflt(i); + if (isfl(ltype[i])) xfloat = 1; + xstr = getstr(i); + lv = 1; lvk = LV_LOCAL; lvi = i; + if (isalias(i)) { touch(i); xbor = lroot(i); return C_PLAIN; } + if (tracked(i)) return C_VAR + i; + return C_PLAIN; + } + s = lookup(n); + if (s >= 0) rs_put(toff, RS_SYM, rs_sym(s), s); + if (s >= 0 && skind[s] == K_KW) { + if (!(sflag[s] & F_EXPR)) dien(n, "can't be used inside an expression"); + return kwcall(s, 1); + } + if (s >= 0 && skind[s] == K_TYPE) { + next(); + if (is_op(s_dot)) { // Struct.name(args): a static function of the struct + next(); + mn = name(); + ms = lookup(methname(n, mn)); + if (ms >= 0 && skind[ms] == K_CONST) { // Enum.MEMBER + lit(sval[ms]); + xty = tint; + if (stype[ms]) xty = stype[ms]; + xzero = sval[ms] == 0; + xconst = 1; + xcval = sval[ms]; + return C_PLAIN; + } + if (ms >= 0 && skind[ms] == K_FN && (sflag[ms] & F_STATIC)) { + if (!is_op(s_lp)) die("a static function is called with ( after its name"); + return call(ms); + } + serrn(mn, "is not a static function of this struct"); + lit(0); + xty = tword; + return C_PLAIN; + } + dien(n, "is a type, not a value"); + lit(0); + xty = tword; + return C_PLAIN; + } + next(); + xty = tword; + if (s >= 0 && skind[s] == K_CONST) { + lit(sval[s]); + xty = tint; + if (stype[s]) xty = stype[s]; + xzero = sval[s] == 0; + xconst = 1; + xcval = sval[s]; + return C_PLAIN; + } + if (s >= 0 && skind[s] == K_GVAR) { + lit(sval[s]); + if (stype[s]) xty = stype[s]; + if (isfl(xty)) xfloat = 1; + lv = 1; + lvk = LV_GLOBAL; + return C_PLAIN; + } + if (s >= 0 && skind[s] == K_GARR) { lit(sval[s]); if (stype[s]) xty = stype[s]; return C_PLAIN; } + if (s >= 0 && skind[s] == K_ENH) return enhcall(s); + if (s < 0) s = fwdfn_at(n, callpos); + if (is_op(s_lt) && (sflag[s] & F_GEN)) { // f(...) + next(); ngbind = 0; + while (1) { + if (ngbind >= 8) die("too many type arguments"); + gbind[ngbind] = ptype(); + ngbind++; + if (!accept(s_comma)) break; + } + closeangle(); + if (!is_op(s_lp)) { // f alone is that instance, as a function value + fill_w(bs0, 0, 8); copy_w(bs0, gbind, ngbind); ngbind = 0; + ft = stype[s]; + s = ginstance(s, bs0); + fnref(s); + xty = tword; + if (ft) xty = subst(ft, bs0); + return C_PLAIN; + } + } + if (is_op(s_lp)) return call(s); + fnref(s); + if (stype[s]) xty = stype[s]; + return C_PLAIN; +} +// ++ and -- on the address at the top of the stack. post says the old value +// is the result; dlt is +1 or -1. it also updates the loop-bound facts: a +// decrement or a store of an unknown value ends any proof about the variable. +int incdec(int post, int dlt) { + int t; + if (lv && lvk == LV_LOCAL) lafact[lvi] = 0; xaf = 0; + if (!lv) die("++ and -- need something to assign to"); + if (lvk == LV_LOCAL && fchk && tracked(lvi) && !isalias(lvi)) { + cerrn(lvi, "owns an object and can't be incremented"); + } + tcneed(isnum(xty) || xty[0] == TY_PTR || xty == tword, "++ and -- need a number or a pointer", xty); + if (lvk == LV_LOCAL) { + ltag[lvi] = ltag[lvi] | LASG; + setflt(lvi, 0); setstr(lvi, 0); + if (forstep && lvi == ft_iv && dlt == 1) fs_ok = 1; + if (dlt < 0) putfact(lvi, -1, -1); else putfact(lvi, getlo(lvi), -1); + bpoison(lvi); + } + lv = 0; lvk = LV_NONE; xzero = 0; xfloat = 0; xstr = 0; + t = xty; + lit(0); op(O_PICK); + if (ispk(t)) pkld(t); else { dbg_addr(); op(O_LD); } + // a sized integer wraps: the new value is cut to size before it is stored + if (post) { + lit(0); op(O_PICK); lit(dlt); op(O_ADD); + if (tbytes(t)) tnorm(t); + lit(2); op(O_PICK); + if (ispk(t)) pkst(t); else op(O_ST); + } else { + lit(dlt); op(O_ADD); + if (tbytes(t)) tnorm(t); + lit(0); op(O_PICK); lit(2); op(O_PICK); + if (ispk(t)) pkst(t); else op(O_ST); + } + op(O_SWAP); op(O_DROP); + return C_PLAIN; +} +// p.x and p->x: a field of a struct, or of the struct a pointer points at +// the field access after a dot or arrow: c is the class of the left side. a +// field of a typed struct is its offset added to the address. an untyped word +// is searched for the one struct that has that field name (fieldscan). if the +// name is followed by ( it is a method call instead. a pointer-valued field +// of a tracked variable is a borrow of that variable (xbor). +word field(int c) { + int n, fieldoff; word st; + af_access(xaf, tpack()); xaf = 0; int i, b = -1, k = 0, scanned = 0, ms; + next(); + n = name(); fieldoff = nameoff; + rval(); + if (c == C_FRESH) cerr("use a field of a new object only after binding it to a name"); + else if (c >= C_VAR) { touch(c - C_VAR); b = c - C_VAR; } + st = xty; + if (st[0] == TY_PTR) st = st[4]; + if (is_op(s_lp)) { // e.name(...): a method, unless the struct has a field of that name + ms = 0 - 1; + if (st[0] == TY_STRUCT) { if (tfindfield(st, n) < 0) ms = methof(st, n); } + else if (st == tword || isnum(st)) { if (fieldscan(n) == 0) ms = methscan(n); } + if (ms == 0 - 2) { serrn(n, "is a method of more than one struct"); ms = 0 - 1; } + if (ms >= 0) return methcall(ms, st); + } + if (st[0] != TY_STRUCT && (st == tword || isnum(st))) { + k = fieldscan(n); + if (k == 1) { st = fldst; scanned = 1; } + else if (k > 1) { + if (!panic && !dry) { + errat(); + put_c('.'); + put_s(n); + put_s(" is a field of more than one struct"); + errend(); + } + xty = tword; lv = 1; lvk = LV_OTHER; xbor = -1; return C_PLAIN; + } + } + if (st[0] != TY_STRUCT) { + if (!panic && !dry) { + errat(); + put_c('.'); + put_s(n); + put_s(": that's a "); + tprint(xty); + put_s(", not a struct or a pointer to one"); + errend(); + } + xty = tword; lv = 1; lvk = LV_OTHER; xbor = -1; return C_PLAIN; + } + if (scanned) i = fldi; else i = tfindfield(st, n); + if (i < 0) { + if (!panic && !dry) { errat(); tprint(st); put_s(" has no field "); put_s(n); errend(); } + xty = tword; lv = 1; lvk = LV_OTHER; xbor = -1; return C_PLAIN; + } + rs_put(fieldoff, RS_FIELD, field_decl(st, n), st[4][i * 3 + 1]); + k = st[4][i * 3 + 2]; + xty = st[4][i * 3 + 1]; + if (k < 0) { // a packed field: the lvalue is a byte address, of the packed twin + lit(8); op(15); + k = 0 - k - 1; + if (k) { lit(k); op(O_ADD); } + xty = tpk(xty); + } else if (k) { lit(k); op(O_ADD); } + if (xty[0] == TY_PTR) { if (b >= 0) xbor = lroot(b); } + else xbor = -1; + lv = 1; xzero = 0; + if (b >= 0) { lvk = LV_ELEM; lvi = b; } else lvk = LV_OTHER; + return C_PLAIN; +} +// array indexing, address arithmetic. mulsz scales an index by the element +// size. the addr_ functions build the address of element ix of a growable +// array in the different situations the bounds-check removal produces: the +// stack holds handle and index (addr_stk), the items pointer is cached in a +// hidden o3 variable (addr_cached), or only the handle is available +// (addr_hdr). range_jz is the inline bounds check, one "outside" enhancement +// and a branch to the oob report. +int mulsz(int et) { + if (tsz(et) != 1) { lit(tsz(et)); op(15); } + return 0; +} +// address of an element of a stack array, from the index on the stack. +int addr_stk(int et) { + op(O_SWAP); + lit(2); + op(O_ADD); + op(O_LD); + op(O_SWAP); + mulsz(et); + op(O_ADD); + return 0; +} +// address of an element of an array whose base is cached in a register. +int addr_cached(int ci, int ix, int et) { + vaddr(ci); + op(O_LD); + vaddr(ix); + op(O_LD); + mulsz(et); + op(O_ADD); + return 0; +} +// address of an element of a growable array, reading its header. +int addr_hdr(int al, int ix, int et) { + vaddr(al); op(O_LD); lit(2); op(O_ADD); op(O_LD); + vaddr(ix); op(O_LD); mulsz(et); op(O_ADD); + return 0; +} +// reload the index and the length on each side of the branch, so neither is a stack temporary +int range_jz(int ix, int src, int fld) { + int k, s; + vaddr(ix); op(O_LD); vaddr(src); op(O_LD); if (fld) op(O_LD); + s = lookup(s_outside); op(XOP0 + sval[s]); + k = hole(); op(O_JZ); + vaddr(ix); op(O_LD); vaddr(src); op(O_LD); if (fld) op(O_LD); + callname(s_oob); + setlit(k, here); + return 0; +} +// suffixes after a primary, in a loop: e as T, [index], .field, ->field, +// (call through a value), ++ and --. the index case is the long one. a byte +// string takes an index or a slice. a growable array is checked: through dat() +// normally, but inline when the compiler can prove or cheaply test the bound +// (o3 code with the cache variables, or an index the loop test already +// compared with the length). a plain pointer or fixed array is just base + +// index * size, and a constant index against a known length is checked at +// compile time. +int postfix() { + int c = primary(), b, n, t, et; word ft; [8]word bs; int k, ci, was, al, ix, enh, cz, cj, place; word storage; + while (1) { + if (tk == T_ID && tv == s_as) { // e as T: same bits, another type + next(); t = ptype(); rval(); xaf = 0; + if (tbytes(t) && !xfloat && !samesz(t, xty)) { tnorm(t); xconst = 0; } // x as u8 cuts the value to size + xty = t; xfloat = 0; lvk = LV_NONE; + if (isfl(t)) xfloat = 1; + continue; + } + if (tk != T_OP) return c; + if (tv == s_lk) { + storage = xaf; place = tpack(); af_access(storage, place); + was = xstr; + ci = 0; + if (lv && lvk == LV_LOCAL) lptr[lvi] = 1; + if (o3on && lv && lvk == LV_LOCAL && o3cache[lvi]) ci = o3cache[lvi]; + next(); + rval(); + if (was) { + c = expr(); rval(); plainuse(c); + if (accept(s_colon)) { + c = expr(); rval(); plainuse(c); + expect(s_rk); + callname(s_slice_s); + lv = 0; lvk = LV_NONE; xty = tword; xfloat = 0; xstr = 1; xzero = 0; xbor = -1; + c = C_PLAIN; + continue; + } + expect(s_rk); + callname(s_at_s); + lv = 0; lvk = LV_NONE; xty = tint; xfloat = 0; xstr = 0; xzero = 0; xbor = -1; + c = C_PLAIN; + continue; + } + b = -1; + if (c == C_FRESH) cerr("index a new object only after binding it to a name"); + else if (c >= C_VAR) { touch(c - C_VAR); b = c - C_VAR; } + if (xty[0] == TY_DYN && tbytes(xty[4])) { // packed elements: the runtime checks and gives the byte address + et = xty[4]; + c = expr(); rval(); plainuse(c); + af_index(storage, place); + tcneed(isnum(xty) || xty == tword, "an index has to be a number", xty); + expect(s_rk); + callname(intern("datb")); + et = tpk(et); + } else if (xty[0] == TY_DYN) { // a growable array: bounds-checked, through the runtime + et = xty[4]; + al = -1; + if (lvk == LV_LOCAL) al = lvi; + if (ci && !mdbg && lookup(s_outside) >= 0) op(O_DROP); + c = expr(); + ix = -1; + if (lv && lvk == LV_LOCAL) ix = lvi; + rval(); plainuse(c); + af_index(storage, place); + tcneed(isnum(xty) || xty == tword, "an index has to be a number", xty); + expect(s_rk); + k = lookup(s_outside); + enh = k >= 0 && skind[k] == K_ENH; + if (!mdbg && ix >= 0 && al >= 0 && proven(al, ix)) { // the loop test already compared this index with the length + if (ci) { vaddr(ci); op(O_LD); op(O_SWAP); mulsz(et); op(O_ADD); } + else addr_stk(et); + } else if (ci && !mdbg && enh && ix >= 0) { + op(O_DROP); + range_jz(ix, ci + 1, 0); + addr_cached(ci, ix, et); + } else if (ci && !mdbg && enh) { + lit(0); op(O_PICK); vaddr(ci + 1); op(O_LD); op(XOP0 + sval[k]); + k = hole(); op(O_JZ); + vaddr(ci + 1); op(O_LD); callname(s_oob); + setlit(k, here); + vaddr(ci); op(O_LD); op(O_SWAP); + mulsz(et); op(O_ADD); + } else if (!mdbg && o3on && enh && ix >= 0 && al >= 0) { + op(O_DROP); op(O_DROP); + range_jz(ix, al, 1); + addr_hdr(al, ix, et); + } else if (!mdbg && o3on && enh && ix >= 0) { + op(O_DROP); + vaddr(ix); op(O_LD); + lit(1); op(O_PICK); op(O_LD); op(XOP0 + sval[k]); + k = hole(); op(O_JZ); + vaddr(ix); op(O_LD); lit(1); op(O_PICK); op(O_LD); callname(s_oob); + setlit(k, here); + lit(2); op(O_ADD); op(O_LD); vaddr(ix); op(O_LD); + mulsz(et); op(O_ADD); + } else if (!mdbg && o3on && enh) { + lit(0); op(O_PICK); lit(2); op(O_PICK); op(O_LD); op(XOP0 + sval[k]); + k = hole(); op(O_JZ); + lit(1); op(O_PICK); op(O_LD); callname(s_oob); + setlit(k, here); + op(O_SWAP); lit(2); op(O_ADD); op(O_LD); op(O_SWAP); + mulsz(et); op(O_ADD); + } else callname(s_dat); + } else { + tcneed(isptr(xty) || xty == tword, "only pointers and arrays can be indexed", xty); + et = telem(xty); + n = 0 - 1; if (xty[0] == TY_ARR && xty[3] > 0) n = xty[3]; + xconst = 0; + c = expr(); rval(); plainuse(c); + if (n < 0) af_index(storage, place); + if (n >= 0 && xconst && (xcval < 0 || xcval >= n)) { + serr("the index is out of range for this array"); + } + tcneed(isnum(xty) || xty == tword, "an index has to be a number", xty); + expect(s_rk); + if (tbytes(et)) { // packed: byte address = base * 8 + index * bytes + if (tbytes(et) != 1) { lit(tbytes(et)); op(15); } + op(O_SWAP); lit(8); op(15); op(O_ADD); + et = tpk(et); + } else { + if (tsz(et) != 1) { lit(tsz(et)); op(15); } + op(O_ADD); + } + } + xaf = 0; + lv = 1; xty = et; xzero = 0; xconst = 0; xlenloc = -1; xstamp = 0; + if (xty[0] == TY_PTR) { if (b >= 0) xbor = lroot(b); } + else xbor = -1; + if (b >= 0) { lvk = LV_ELEM; lvi = b; } else lvk = LV_OTHER; + c = C_PLAIN; + xfloat = 0; if (isfl(xty)) xfloat = 1; xstr = 0; + continue; + } + if (tv == s_dot || tv == s_arrow) { + xconst = 0; + c = field(c); + xfloat = 0; + if (isfl(xty)) xfloat = 1; + xstr = 0; + continue; + } + if (tv == s_lp) { // call through a value + rval(); plainuse(c); + ft = 0; + if (xty[0] == TY_FN) ft = xty; + else tcneed(xty == tword, "only functions can be called", xty); + fill_w(bs, 0, 8); + n = args(0, ft, bs, o3on); + // raw code can live in a heap segment; a closure has an environment block + lit(n); op(O_PICK); callname(intern("callable_code")); + cz = hole(); op(O_JZ); + lit(n); op(O_PICK); op(O_CALL); + cj = hole(); op(O_JMP); + setlit(cz, here); + lit(n); op(O_PICK); lit(3); op(O_ADD); // env: the captures start 3 words into the block + lit(n + 1); op(O_PICK); op(O_LD); // the body's entry, word 0 of the block + op(O_CALL); + setlit(cj, here); + op(O_SWAP); op(O_DROP); + lv = 0; lvk = LV_NONE; c = C_PLAIN; xzero = 0; xbor = -1; xconst = 0; + xty = tword; + if (ft) xty = subst(ft[4], bs); + xfloat = 0; if (isfl(xty)) xfloat = 1; xstr = 0; + continue; + } + if (tv == s_inc) { next(); c = incdec(1, 1); continue; } + if (tv == s_dec) { next(); c = incdec(1, -1); continue; } + return c; + } + return c; +} +// prefix operators: the u*x / d*x / m*x recasts (the star has to touch the +// tag), casts like (int)x and (node*)x, dereference, address-of, prefix ++ and +// --, and any unary operator a program added (- ! ~ built in). +int unary() { + int c, un, t, place, constant, value; word storage; + if (tk == T_ID && (tv == s_u || tv == s_m || tv == s_d)) { // u*x, d*x, m*x: recast only when * is glued + peek(); + if (pk_tk == T_OP && pk_tv == s_star && pq_gl[0]) { + t = S_M; if (tv == s_u) t = S_U; if (tv == s_d) t = S_D; + next(); expect(s_star); + c = unary(); rval(); + lvk = LV_NONE; + return recast(c, t); + } + } + if (tk == T_OP && tv == s_lp && caststart()) { // (int)x, (node*)x: same bits, the type changes + next(); + t = ptype(); + expect(s_rp); + c = unary(); rval(); plainuse(c); xaf = 0; + if (isfl(t)) { if (!xfloat) { op(OP_I2F); xfloat = 1; } if (t == tf32) { xenh("zf2s"); xenh("zs2f"); } } + else if (xfloat && isnum(t)) { op(OP_F2I); xfloat = 0; } + else xfloat = 0; + if (tbytes(t) && !samesz(t, xty)) { tnorm(t); xconst = 0; } // (u8)x cuts the value to size + xty = t; lv = 0; lvk = LV_NONE; xzero = 0; xbor = -1; + return C_PLAIN; + } + if (tk == T_OP) { + if (tv == s_star) { + place = tpack(); next(); c = unary(); + af_access(xaf, place); xaf = 0; + if (lv && lvk == LV_LOCAL) lptr[lvi] = 1; + rval(); plainuse(c); + tcneed(xty[0] == TY_PTR || xty == tword, "only pointers can be dereferenced", xty); + xty = telem(xty); + if (tbytes(xty)) { lit(8); op(15); xty = tpk(xty); } // the first packed element: a byte address + lv = 1; + lvk = LV_OTHER; + xzero = 0; + xbor = -1; + xfloat = 0; + if (isfl(xty)) xfloat = 1; + xstr = 0; + return C_PLAIN; + } + if (tv == s_amp) { + place = tpack(); next(); c = unary(); + storage = 0; + if (lv && lvk == LV_LOCAL) { + storage = laaddr[lvi]; + if (!storage) { storage = af_new(1, 1, place); laaddr[lvi] = storage; if (storage) storage[5] = lvi; } + af_forget(); + } + else if (isagg(xty)) storage = 0; + if (!lv && !isagg(xty)) die("& needs something addressable"); + if (lv && ispk(xty)) serr("an element of a sized-integer array has no address; it is a byte inside a word"); + if (lv && lvk == LV_LOCAL && (ltag[lvi] & 16)) serr("an o3 variable has no address"); + if (lv && lvk == LV_LOCAL) ltag[lvi] = ltag[lvi] | LREAD | LASG; + if (c >= C_VAR) touch(c - C_VAR); + lv = 0; lvk = LV_NONE; xzero = 0; xbor = -1; xfloat = 0; xstr = 0; + xty = tptr(xty); xaf = storage; + return C_PLAIN; + } + if (tv == s_inc) { next(); unary(); return incdec(0, 1); } + if (tv == s_dec) { next(); unary(); return incdec(0, -1); } + un = unfind(tv); + if (un >= 0) { + next(); c = unary(); rval(); plainuse(c); + constant = xconst && !xfloat && uname[un] == s_minus; value = xcval; xaf = 0; + if (un <= 1) ftest(); + if (uname[un] == s_minus && xfloat) { + op(OP_FLIT); + emit(0); + op(O_SWAP); + op(OP_FSUB); + xty = tfloat; + } + else { + emitseq(useq[un], ulen[un]); xfloat = 0; + if (un <= 1) xty = tbool; + else if (xty != tword && !isfl(xty)) { tcneed(isnum(xty), "this operator needs a number", xty); xty = tint; } + } + xstr = 0; + xconst = constant; if (constant) xcval = 0 - value; xlenloc = -1; xstamp = 0; + lv = 0; lvk = LV_NONE; xzero = 0; xbor = -1; + return C_PLAIN; + } + } + return postfix(); +} +// binary operators by precedence climbing: parse an operand, then while the +// next token is a binary operator of at least minp, parse its right side at one +// level tighter and emit its code sequence from the operator table. && and || +// short-circuit with jumps; floats get their own opcodes; a * or / by a +// constant that is a power of two gets a shift hint in check mode. +int bin(int minp) { + int c = unary(), b, p, a, j, lt, lb, rb, src, lf, rf, ls, rs, fresh, top = 0, il = -1, opp; + if (fortest == 1) { fortest = 2; top = 1; ft_nops = 0; } + while (tk == T_OP) { + b = binfind(tv); + if (b < 0) return c; + p = bprec[b]; + if (p < minp) return c; + lb = -1; + if (c >= C_VAR && tracked(c - C_VAR)) lb = lroot(c - C_VAR); else lb = xbor; + lf = xfloat; ls = xstr; fresh = 0; + il = -1; + if (top && lv && lvk == LV_LOCAL) il = lvi; + opp = tpack(); + next(); rval(); plainuse(c); + lt = xty; + if (isfl(lt)) lf = 1; + src = -1; + if (blen[b] == -1) { // && : 0 if the left side is 0, else the right side as 0 or 1 + af_forget(); + ftest(); + a = hole(); op(O_JZ); + c = bin(p + 1); rval(); plainuse(c); + ftest(); + emitseq(useq[1], ulen[1]); // !! normalizes + j = hole(); op(O_JMP); + setlit(a, here); lit(0); setlit(j, here); + xfloat = 0; xstr = 0; + } else if (blen[b] == -2) { // || : 1 if the left side isn't 0, else the right side as 0 or 1 + af_forget(); + ftest(); + a = hole(); op(O_JZ); + lit(1); j = hole(); op(O_JMP); + setlit(a, here); + c = bin(p + 1); rval(); plainuse(c); + ftest(); + emitseq(useq[1], ulen[1]); + setlit(j, here); + xfloat = 0; xstr = 0; + } else { + c = bin(p + 1); + rb = -1; + if (c >= C_VAR && tracked(c - C_VAR)) rb = lroot(c - C_VAR); else rb = xbor; + if (top) { ft_ir = -1; if (lv && lvk == LV_LOCAL) ft_ir = lvi; } + rval(); plainuse(c); + if (cmode && bname[b] == s_star && xconst && xcval >= 2 && !(xcval & (xcval - 1)) && !lf) { + shifthint(opp, xcval); + } + rf = xfloat; rs = xstr; + if (isfl(xty)) rf = 1; + if ((ls || rs) && (bname[b] == s_plus || bname[b] == s_eqeq || bname[b] == s_ne || bname[b] == s_lt || bname[b] == s_gt || bname[b] == s_le || bname[b] == s_ge)) { + if (!(ls && rs)) serr("a byte string needs a byte string on the other side"); + if (bname[b] == s_plus) { callname(s_cat_s); xstr = 1; xfloat = 0; } + else if (bname[b] == s_eqeq) { callname(s_eq_s); xstr = 0; xfloat = 0; } + else if (bname[b] == s_ne) { callname(s_eq_s); lit(1); op(18); xstr = 0; xfloat = 0; } + else if (bname[b] == s_lt) { callname(s_lt_s); xstr = 0; xfloat = 0; } + else if (bname[b] == s_gt) { op(O_SWAP); callname(s_lt_s); xstr = 0; xfloat = 0; } + else if (bname[b] == s_le) { callname(s_le_s); xstr = 0; xfloat = 0; } + else { op(O_SWAP); callname(s_le_s); xstr = 0; xfloat = 0; } + } else if ((lf || rf) && isfop(bname[b])) { femit(bname[b], lf, rf); xstr = 0; } + else { emitseq(bseq[b], blen[b]); xfloat = 0; xstr = 0; } + if (bname[b] == s_plus || bname[b] == s_minus) { + if (lb >= 0) src = lb; else src = rb; + if (bname[b] == s_minus && lb >= 0 && rb >= 0) src = -1; // pointer difference is a distance + } + } + if (blen[b] < 0) af_forget(); + xty = bintype(b, lt, xty); + if (src >= 0 && xty[0] != TY_PTR && xty != tword) src = -1; + xbor = src; + if (top) { + ft_nops++; + if (ft_nops > 1) ft_iv = -1; + else if (bname[b] == s_lt && blen[b] > 0 && il >= 0 && ft_ir >= 0 && !lf && !rf && !ls && !rs) { ft_iv = il; ft_bd = ft_ir; } + } + lv = 0; lvk = LV_NONE; c = C_PLAIN; xzero = 0; xconst = 0; xlenloc = -1; xstamp = 0; xaf = 0; + if (fresh) c = C_FRESH; + } + return c; +} +// ---- assignment, conditionals and blocks as expressions ---- +int storekeep(int t) { + if (mdbg && !dry && !ispk(t)) { lit(1); op(O_PICK); callname(s_check); op(O_DROP); } + op(O_SWAP); lit(1); op(O_PICK); op(O_SWAP); + if (ispk(t)) pkst(t); else op(O_ST); + return 0; +} +// c ? a : b. both sides are analyzed from the same starting state (a snapshot, +// restored for the second side) and then joined, so a variable can't be given +// away on one side only. both sides are fresh objects, or neither is. +int ternary(int c) { + int a, b, s0, sa, livea, live0 = live, n0 = nloc, ca, cb, ta, ba, bb; + rval(); plainuse(c); + ftest(); + next(); + a = hole(); op(O_JZ); + s0 = snap_push(); + ca = expr(); rval(); ta = xty; ba = xbor; + if (ca != C_FRESH) plainuse(ca); + livea = live; sa = snap_push(); + b = hole(); op(O_JMP); + expect(s_colon); + setlit(a, here); + snap_restore(s0); live = live0; + cb = expr(); rval(); bb = xbor; + if (cb != C_FRESH) plainuse(cb); + join2(sa, livea, b, live, n0, "ends the two sides of ?: in different states"); + snap_pop(s0); + if (isfl(ta) && !isfl(xty)) { op(OP_I2F); xfloat = 1; xty = tfloat; } + if (!compat(ta, xty) && !compat(xty, ta)) tcerr("the two sides of ?:", ta, xty); + if (ta != tword && !isfl(ta)) xty = ta; + else if (isfl(ta) || isfl(xty)) { xty = tfloat; xfloat = 1; } + if (ba != bb) xbor = -1; + lv = 0; lvk = LV_NONE; xzero = 0; + if (ca == C_FRESH && cb == C_FRESH) return C_FRESH; + if (ca == C_FRESH || cb == C_FRESH) cerr("both sides of ?: have to be new objects, or neither"); + return C_PLAIN; +} +// the top of the expression grammar: a binary expression, then possibly ?: or +// an assignment. plain = stores the value and leaves it on the stack; the +// analyzer's assigncheck decides what the store means for ownership (a fresh +// object makes the target an owner, a u object moves up a block, and so on). +// compound assignment (+= etc.) loads the old value, applies the operator +// sequence from the table and stores the result. an o3 variable is stored with +// the value first and the address after, so no address waits on the stack +// across a branch. +int expr() { + int c = bin(1), k, i, a, rc, lt, lf, rf, fk; + if (tk != T_OP) return c; + if (tv == s_q) return ternary(c); + if (tv == s_eq) { + if (!lv) die("this can't be assigned to"); + k = lvk; i = lvi; lt = xty; + if (isagg(lt)) serr("a whole struct or array can't be assigned; assign its fields"); + next(); + a = o3on && k == LV_LOCAL && (ltag[i] & 16) && !larr[i]; + if (a) op(O_DROP); // an o3 variable: its address comes after the value, so it never waits on the stack across a branch + rc = expr(); rval(); + if (k != LV_LOCAL) af_escape(xaf); + fcoerce(lt); + if (k == LV_LOCAL) { + ltag[i] = ltag[i] | LASG; + setflt(i, xfloat); + setstr(i, xstr); + note_store(i); af_store(i); + } + if (!compat(lt, xty)) tcerr("assignment", lt, xty); + if (a) { lit(0); op(O_PICK); vaddr(i); op(O_ST); o3refresh(i); } + else { + if (o3on && k != LV_ELEM && hasptr(lt)) { lit(lt); callname(s_fatten); } // o3 data going somewhere normal code can see + storekeep(lt); + } + lv = 0; lvk = LV_NONE; xty = lt; xzero = 0; + return assigncheck(k, i, rc); + } + a = asgfind(tv); + if (a >= 0) { + if (!lv) die("this can't be assigned to"); + if (lvk == LV_LOCAL && fchk && tracked(lvi) && !isalias(lvi)) { + cerrn(lvi, "owns an object; compound assignment would lose it"); + } + lt = xty; + fk = -1; lf = xfloat; + if (lvk == LV_LOCAL) fk = lvi; + if (isfl(lt)) lf = 1; + next(); + i = o3on && lvk == LV_LOCAL && (ltag[lvi] & 16) && !larr[lvi]; + if (i) { i = lvi; op(O_DROP); vaddr(i); op(O_LD); i = i + 1; } + else if (ispk(lt)) { lit(0); op(O_PICK); pkld(lt); } + else { lit(0); op(O_PICK); op(O_LD); } + lv = 0; + rc = expr(); rval(); plainuse(rc); + rf = xfloat; + if ((lf || rf) && isfop(bname[abin[a]])) femit(bname[abin[a]], lf, rf); + else { emitseq(bseq[abin[a]], blen[abin[a]]); xfloat = 0; } + fcoerce(lt); + if (fk >= 0) { + lafact[fk] = 0; xaf = 0; + ltag[fk] = ltag[fk] | LASG; + setflt(fk, xfloat); setstr(fk, 0); + if (forstep && fk == ft_iv && bname[abin[a]] == s_plus && xconst && xcval == 1) fs_ok = 1; + if (bname[abin[a]] == s_plus && xconst && xcval > 0) putfact(fk, getlo(fk), -1); + else putfact(fk, -1, -1); + bpoison(fk); + } + k = bintype(abin[a], lt, xty); + if (!compat(lt, k)) tcerr("compound assignment", lt, k); + if (i) { lit(0); op(O_PICK); vaddr(i - 1); op(O_ST); } else storekeep(lt); + lv = 0; lvk = LV_NONE; xty = lt; xzero = 0; + return C_PLAIN; + } + return c; +} +// does the current token start a statement rather than an expression? +// a block as an expression: { stmts; tail }. it opens a scope like any block, +// so its variables are freed when it closes, and the tail is how a u object +// leaves the block with a value (handup). +word stmtstart() { + int s; + if (tk == T_OP) return tv == s_semi || tv == s_lb || tstart(); + if (tk != T_ID || findloc(tv) >= 0) return 0; + if (tstart()) return 1; + s = lookup(tv); + return s >= 0 && skind[s] == K_KW && !(sflag[s] & F_EXPR); +} +// { stmts; tail } as an expression: its value is the tail, or 0 +int blockexpr() { + int save = nloc, c = C_PLAIN, tail = 0, t = tint; + next(); + fdepth++; bxdepth++; + while (!is_op(s_rb)) { + if (tk == T_EOF) { die("missing }"); break; } + if (stmtstart()) { stmt(); continue; } + c = expr(); rval(); + if (accept(s_semi)) { op(O_DROP); discard(c); c = C_PLAIN; continue; } + if (!is_op(s_rb)) die("expected ; or } after an expression"); + tail = 1; t = xty; + } + next(); + if (tail) c = handup(c); else { lit(0); xbor = -1; xfloat = 0; xstr = 0; } + if (xbor >= save) xbor = -1; // that local dies with this block; the pointer leaves untracked + block_close(save); + fdepth--; bxdepth--; + lv = 0; lvk = LV_NONE; xty = t; xzero = 0; + return c; +} + +// ---- constant expressions ---- +// constant expressions, evaluated while compiling: numbers, named constants, +// addresses of globals and functions, unary minus and ~, parentheses, and the +// operators + - * | & << >>. used for array lengths, enum values, const +// initializers and asm operands. +word cprim() { + int v, s, mc; + if (tk == T_NUM) { v = tv; next(); return v; } + if (accept(s_minus)) return 0 - cprim(); + if (tk == T_OP && tv == uname[2]) { next(); return ~cprim(); } + if (accept(s_lp)) { v = cexpr(); expect(s_rp); return v; } + if (tk == T_ID) { + s = lookup(tv); + if (s >= 0 && (skind[s] == K_CONST || skind[s] == K_GARR || (skind[s] == K_FN && (sflag[s] & F_DEF)) || skind[s] == K_GVAR)) { + next(); + return sval[s]; + } + if (s >= 0 && skind[s] == K_TYPE) { // Enum.MEMBER in a constant expression + peekat(0); + if (pk_tk == T_OP && pk_tv == s_dot) { + peekat(1); + if (pk_tk == T_ID) { + mc = lookup(methname(tv, pk_tv)); + if (mc >= 0 && skind[mc] == K_CONST) { next(); next(); next(); return sval[mc]; } + } + } + } + } + die("expected a constant"); + return 0; +} +// compile an expression: a primary, then the operators that follow it. +int cexpr() { + int v = cprim(), o; + while (tk == T_OP) { + o = tv; + if (o == bname[3]) { next(); v = v + cprim(); } + else if (o == bname[4]) { next(); v = v - cprim(); } + else if (o == bname[0]) { next(); v = v * cprim(); } + else if (o == bname[15]) { next(); v = v | cprim(); } + else if (o == bname[13]) { next(); v = v & cprim(); } + else if (o == bname[5]) { next(); v = v << cprim(); } + else if (o == bname[6]) { next(); v = v >> cprim(); } + else return v; + } + return v; +} + +// ---- statements ---- +// after a syntax error: skip to the end of the statement +// statements. stmt is the dispatcher: a keyword statement (if, while, return, +// a parse word...), a declaration, an empty statement, a block, or an +// expression statement whose value is dropped. after a syntax error panic is +// set and stmt calls resync to skip to the next ; or }. +int resync() { + while (tk != T_EOF && !is_op(s_semi) && !is_op(s_rb)) next(); + if (is_op(s_semi)) next(); + panic = 0; + return 0; +} +// compile one statement. +int stmt() { + int s, c; + if (panic) return resync(); + if (tk == T_ID && findloc(tv) < 0) { + s = lookup(tv); + if (s >= 0 && skind[s] == K_KW && (sflag[s] & F_STMT)) { kwcall(s, 0); return 0; } + } + if (tstart()) { localdecl(1); return 0; } + if (tk == T_OP) { + if (tv == s_semi) { next(); return 0; } + if (tv == s_lb) { block(); return 0; } + } + c = expr(); rval(); op(O_DROP); + expect(s_semi); + discard(c); + return 0; +} +// compile a block, with its own scope for the locals it declares. +int block() { + int save = nloc; + expect(s_lb); + fdepth++; + while (!is_op(s_rb)) { if (tk == T_EOF) { die("missing }"); break; } stmt(); } + block_close(save); // before the }, so its line is the one reported + next(); + fdepth--; + return 0; +} +// the body of if/while/for: its own block, even without braces +// (the body of if/while/for is always its own block, even without braces, so +// variables declared in it die with it) +int substmt() { + int save = nloc; + if (is_op(s_lb)) return block(); + fdepth++; + stmt(); + block_close(save); + fdepth--; + return 0; +} +// let a = e, b[10]; or, typed: int a = 1, b; node*.m p = new(node); .u r; +// let infers pointer, struct and function types from the initializer and +// keeps numbers as plain words. +// (each declared name gets a slot; an initializer is compiled and stored, and +// assigncheck gives the new local its analyzer state. an initializer that is a +// constant number marks the local as a const candidate for the hints, and a +// new records the size and leaf-ness for the matching recycle.) +// the type of each untyped local while the language server checks a document: +// the byte offset of its name in the document, and the type's text in ltxt +// (one character per word). the server turns it into the hover of the local. +// record a parsed type expression so arrays, stars, and function types have hovers. +int ht_note(int start, int end, word t) { + if (!lt_on || isp != 0 || dry || !t || start < 0 || end <= start) return 0; + if (!ht_start || ht_n == htcap) { + if (ht_start) htcap = htcap * 2; + ht_start = chk_resize(ht_start, ht_n, htcap); + ht_end = chk_resize(ht_end, ht_n, htcap); + ht_type = chk_resize(ht_type, ht_n, htcap); + } + ht_start[ht_n] = start; ht_end[ht_n] = end; ht_type[ht_n] = t; ht_n++; + return 0; +} +// record the type of a local at byte offset o for the language server. +int lt_note(int o, int t) { + int sm, sb, sn, sc, sl, need; + if (!lt_on || isp != 0 || dry || o < 0 || !t) return 0; + if (!lt_off || lt_n == ltcap) { + if (lt_off) ltcap = ltcap * 2; + lt_off = chk_resize(lt_off, lt_n, ltcap); lt_at = chk_resize(lt_at, lt_n, ltcap); + lt_len = chk_resize(lt_len, lt_n, ltcap); lt_type = chk_resize(lt_type, lt_n, ltcap); + } + sm = outmode; sb = outbuf; sn = outn; sc = outcap; + outmode = 3; outn = 0; tprint(t); need = ltxn + outn; + if (!ltxt || need > ltxtcap) { + while (need > ltxtcap) ltxtcap = ltxtcap * 2; + ltxt = chk_resize(ltxt, ltxn, ltxtcap); + } + outmode = 2; outbuf = ltxt; outn = ltxn; outcap = ltxtcap; + tprint(t); + sl = outn; + outmode = sm; outbuf = sb; outn = sn; outcap = sc; + lt_off[lt_n] = o; lt_at[lt_n] = ltxn; lt_len[lt_n] = sl - ltxn; lt_type[lt_n] = t; lt_n++; + ltxn = sl; + return 0; +} +// compile a local declaration, typed or let-style. +int localdecl(int typed) { + int n, i, c, k, t = 0, tg = 0, pp, kc, kp = ifkp, gty = 0, gok = !typed && ifon && !dry, o; + if (typed) { dtype(); t = dty; tg = dtag; if (t == tvoid) serr("a variable can't be void"); } + if ((o3on || o3one) && !typed) serr("o3 variables need a type"); + while (1) { + pp = tpack(); + o = toff; + rs_note(RS_LOCAL, o); + n = name(); + i = newloc(n); + lrsoff[i] = o; + ltag[i] = tg | LDECL; lpos[i] = pp; lnsz[i] = 0; lleaf[i] = 0; + if (o3on || o3one) ltag[i] = ltag[i] | 16; + if (accept(s_lk)) { + k = cexpr(); expect(s_rk); + if (!t) t = tword; + larr[i] = 1; ltype[i] = tarr(t, k); fslots = fslots + tsz(ltype[i]); + if (gok) { if (!gty) gty = tint; else if (gty != tint) gok = 0; } + } else if (t && isagg(t)) { larr[i] = 1; ltype[i] = t; fslots = fslots + tsz(t); } + else { fslots++; if (t) ltype[i] = t; if (isfl(t)) setflt(i, 1); if (o3on) { o3promote(i); o3mkcache(i); } } + if (accept(s_eq)) { + if (larr[i]) serr("an array or a struct can't be initialized with ="); + newsz = -1; newleaf = 0; if (tk == T_NUM) nomagic = 1; + c = expr(); rval(); nomagic = 0; kc = xconst && !xfloat; + if (kc) ltag[i] = ltag[i] | LCONST; + if (c == C_FRESH) { lnsz[i] = newsz; lleaf[i] = newleaf; } + if (t) fcoerce(t); + vaddr(i); + op(O_ST); + o3refresh(i); + setflt(i, xfloat); + setstr(i, xstr); + note_store(i); af_store(i); + if (t) { if (!compat(t, xty)) tcerr("initializer", t, xty); } + else if (!isnum(xty)) { + if (xty[0] == TY_ARR) ltype[i] = decay(xty); + else if (xty[0] == TY_STRUCT) ltype[i] = tptr(xty); + else ltype[i] = xty; + } + assigncheck(LV_LOCAL, i, c); + if (gok) { k = xty; if (isagg(k)) k = ltype[i]; if (!ifpure(k) || (gty && gty != k)) gok = 0; else gty = k; } + } else if (gok && !larr[i]) gok = 0; // no initializer: nothing says what it is + if (larr[i]) { k = 1; if (ltype[i][0] == TY_ARR) k = ltype[i][3]; lafact[i] = af_new(k, 1, pp); laaddr[i] = lafact[i]; if (lafact[i]) lafact[i][5] = i; } + af_declaration(o, lafact[i]); + kc = tword; + if (ltype[i]) kc = ltype[i]; + lt_note(((pp >> 40) & 0x1FFFFF) - 1, kc); + if (!accept(s_comma)) break; + } + expect(s_semi); + if (gok && gty && kp) fact_add(kp, 0 - 1, gty, ""); + return 0; +} +// compile a variable declaration. +int kw_var() { + stmtonly(); + ifkp = 0; + if (isp == 0) ifkp = tpack(); // only the document itself: an include has other offsets + next(); + return localdecl(0); +} +// if / else. the shape of every branching construct is the same: snapshot the +// analyzer state, compile one path, restore the snapshot, compile the other, +// then join the two end states (join2). a path that does not reach the join +// (it returned or broke) is left out. without an else the "other path" is +// the state before the branch. +int kw_if() { + int a, b, c, s0, sa, live0, livea, n0 = nloc, nt, cj; + stmtonly(); + next(); + expect(s_lp); c = expr(); rval(); plainuse(c); ftest(); expect(s_rp); + a = hole(); op(O_JZ); + live0 = live; + s0 = snap_push(); + substmt(); + livea = live; sa = snap_push(); + if (is_id(s_else)) { + next(); + b = hole(); op(O_JMP); + setlit(a, here); + snap_restore(s0); live = live0; + substmt(); + join2(sa, livea, b, live, n0, "ends the branches of an if in different states"); + } else { + // the path that skips the then branch still has the states from before it + njps = 0; if (livea) jadd(R_SNAP + sa); if (live0) jadd(R_SNAP + s0); + join(n0, "ends the branches of an if in different states"); + if (livea) fixups(R_SNAP + sa, n0, 1); + nt = 0; if (live0) nt = fixups(R_SNAP + s0, n0, 0); + if (nt) { + cj = hole(); + op(O_JMP); + setlit(a, here); + fixups(R_SNAP + s0, n0, 1); + setlit(cj, here); + } + else setlit(a, here); + live = livea || live0; + } + snap_pop(s0); + return 0; +} +// loop frame: 0 continue target (-1 for a switch) 2 depth 3 snapshot at the top +// 4 block-expression depth 5 locals at the start 6 its first exit record +// loops and switches share a frame stack (R_LOOP, 8 words each). a break adds +// an exit record (the analyzer state at that point) with a jump to patch; +// loop_end joins all of them with the normal fall-out and patches the jumps. +int loop_push(int cont, int s0) { + word f; + reserve_R_LOOP((lsp + 1) * 8); + if (lsp >= loopfacts_cap) { lpfact = chk_resize(lpfact, loopfacts_cap, loopfacts_cap * 2); loopfacts_cap = loopfacts_cap * 2; } + f = R_LOOP + lsp * 8; + f[0] = cont; + f[1] = 0; + f[2] = fdepth; + f[3] = s0; + f[4] = bxdepth; + f[5] = nloc; + f[6] = extop; + f[7] = 0; + lpfact[lsp] = -1; + lsp++; + return 0; +} +// the end of a loop or switch: join its exits (the fall-out path sc through +// the jump at a, if there is one, and every break) and lay out each path's +// fixups on its way to the end +int loop_end(int a, int sc) { + word f, p; int e, n0, outs = 0; + lsp--; + f = R_LOOP + lsp * 8; + n0 = f[5]; + njps = 0; + if (sc >= 0) jadd(R_SNAP + sc); + p = R_EXIT + f[6]; e = R_EXIT + extop; + while (p < e) { jadd(p + 1); p = p + 3 + 3 * (p[2] - p[1]); } + join(n0, "leaves the loop in different states on different paths"); + live = njps > 0; + p = R_EXIT + f[6]; + while (p < e) { + if (fixups(p + 1, n0, 0)) { + setlit(p[0], here); + fixups(p + 1, n0, 1); + outs = fixnew(hole(), outs); + op(O_JMP); + } + else outs = fixnew(p[0], outs); + p = p + 3 + 3 * (p[2] - p[1]); + } + if (a >= 0) setlit(a, here); + if (sc >= 0) fixups(R_SNAP + sc, n0, 1); + fixall(outs, here); + extop = f[6]; + return 0; +} +// while. the back edge is checked against the snapshot taken at the top, so a +// loop body that gives something away and comes around again is an error. a +// literal while (1) has no fall-out path, only its breaks. +int kw_while() { + int c0 = here, a, s0, sc = -1, c, inf = 0; + stmtonly(); + next(); + s0 = snap_push(); + expect(s_lp); + // while (1): no way out but break + if (tk == T_NUM && tv) { + peek(); + if (pk_tk == T_OP && pk_tv == s_rp) inf = 1; + } + c = expr(); rval(); plainuse(c); ftest(); expect(s_rp); + a = hole(); op(O_JZ); + if (!inf && live) sc = snap_push(); + loop_push(c0, s0); + substmt(); + backedge(s0); + jmp(c0); + loop_end(a, sc); + snap_pop(s0); + return 0; +} +// for (init; test; step) body. the code is laid out test first, then a jump +// over the step into the body, and the step jumps back to the test, so the +// step is compiled before the body but runs after it. while the test and the +// step are parsed, fortest and forstep record the shape "i < bound ... i++": +// if it holds and the bound is len(a) of a fixed array, the body is told that +// a[i] is in range (lpfact), and its bounds check disappears. +int kw_for() { + int save = nloc, s0, sc = -1, c0, a, b, st, c, inf = 0; + stmtonly(); + next(); + expect(s_lp); + fdepth++; + if (is_id(s_var)) kw_var(); + else if (tstart()) localdecl(1); + else { if (!is_op(s_semi)) { c = expr(); rval(); op(O_DROP); discard(c); } expect(s_semi); } + s0 = snap_push(); + c0 = here; + ft_iv = -1; fs_ok = 0; fortest = 1; + if (!is_op(s_semi)) { + if (tk == T_NUM && tv) { peek(); if (pk_tk == T_OP && pk_tv == s_semi) inf = 1; } + c = expr(); rval(); plainuse(c); ftest(); + } else { lit(1); inf = 1; xfloat = 0; } + fortest = 0; + expect(s_semi); + a = hole(); op(O_JZ); + if (!inf && live) sc = snap_push(); + b = hole(); op(O_JMP); + st = here; + forstep = 1; + if (!is_op(s_rp)) { c = expr(); rval(); op(O_DROP); discard(c); } + forstep = 0; + expect(s_rp); + jmp(c0); + setlit(b, here); + loop_push(st, s0); + if (ft_iv >= 0 && ft_bd >= 0 && fs_ok && getlo(ft_iv) >= 0 && getln(ft_bd) >= 0) { + lpfact[lsp - 1] = pfact(ft_iv, ft_bd, getln(ft_bd)); + } + substmt(); + backedge(s0); + jmp(st); + loop_end(a, sc); + snap_pop(s0); + block_close(save); + fdepth--; + return 0; +} +// do stmt while (cond); the body runs once before the test. continue jumps +// to the test, which is patched in after the body (f[0] == -2 marks that). +int kw_do() { + int body, s0, sc = -1, a, c; word f; int inf = 0; + stmtonly(); + next(); + s0 = snap_push(); + body = here; + loop_push(-2, s0); + substmt(); + f = R_LOOP + (lsp - 1) * 8; + fixall(f[7], here); + backedge(s0); + if (!is_id(s_while)) die("expected while after do"); + next(); + expect(s_lp); + if (tk == T_NUM && tv) { peek(); if (pk_tk == T_OP && pk_tv == s_rp) inf = 1; } + c = expr(); rval(); plainuse(c); ftest(); + expect(s_rp); + expect(s_semi); + if (!inf && live) sc = snap_push(); + a = hole(); op(O_JZ); + jmp(body); + loop_end(a, sc); + snap_pop(s0); + return 0; +} +// break and continue unwind the scopes they leave (exit_vars frees what those +// blocks own on the way out). break records the analyzer state for the join +// at the end of the loop; continue checks the state against the loop top. +int kw_break() { + word f; + stmtonly(); + next(); expect(s_semi); + if (lsp <= loopbase) { serr("break outside a loop or a switch"); return 0; } + f = R_LOOP + (lsp - 1) * 8; + if (f[4] != bxdepth) { serr("break can't leave a block expression"); return 0; } + exit_vars(f[2] + 1, 0); + if (live) exit_rec(hole()); else hole(); + op(O_JMP); + live = 0; + return 0; +} +// compile continue: jump to the test of the innermost loop. +int kw_continue() { + int j = lsp - 1; word f; + stmtonly(); + next(); expect(s_semi); + while (j >= loopbase && R_LOOP[j * 8] == -1) j--; // past any switch + if (j < loopbase) { serr("continue outside a loop"); return 0; } + f = R_LOOP + j * 8; + if (f[4] != bxdepth) { serr("continue can't leave a block expression"); return 0; } + backedge(f[3]); + exit_vars(f[2] + 1, 0); + if (f[0] == -2) { f[7] = fixnew(hole(), f[7]); op(O_JMP); } // do/while: the condition isn't placed yet + else jmp(f[0]); + live = 0; + return 0; +} +// switch (e) { case 1, 2: ... case 'x': ... default: ... } +// cases don't fall through: each one ends where the next begins, and break +// leaves the switch early. case labels are constants. +// one case's statements, analyzed from the state at the top of the switch. +// the end of a case jumps out of the switch, so cases never fall through. +int casebody(int s0, int live0) { + int save = nloc; + snap_restore(s0); live = live0; + fdepth++; + while (!is_op(s_rb) && !is_id(s_case) && !is_id(s_default)) { + if (tk == T_EOF) { die("missing }"); break; } + stmt(); + } + block_close(save); + fdepth--; + if (live) { exit_rec(hole()); op(O_JMP); } // the end of a case leaves the switch + live = 0; + return 0; +} +// compile a switch statement. +int kw_switch() { + int sl = tline; + int save = nloc, tmp, c, s0, live0, defent = -1, skip, nxt, bj, v, a = -1, st, tg = 0, cov = 0, gi, nb = 0, j, arm, bi; + [8]word pbn; [8]word pbl; + stmtonly(); + next(); + expect(s_lp); c = expr(); st = xty; rval(); plainuse(c); expect(s_rp); + tg = ptagged(st); + fdepth++; + tmp = newloc(0); fslots++; // a hidden local holds the value + laddr(lslot[tmp]); op(O_ST); + live0 = live; + s0 = snap_push(); + loop_push(-1, s0); + expect(s_lb); + while (!is_op(s_rb)) { + if (tk == T_EOF) { die("missing }"); break; } + if (is_id(s_case)) { + next(); + bj = 0; + gi = -1; + peek(); + if (tg && tk == T_ID && pk_tk == T_OP && pk_tv == s_lp) gi = pm_find(tv); + if (gi >= 0) { + // case member(a, b): a member of a tagged enum, its fields bound to the names + next(); next(); + nb = 0; + while (!is_op(s_rp)) { + if (nb >= 8) { die("too many names in a case"); break; } + pbn[nb] = name(); nb++; + if (!accept(s_comma)) break; + } + expect(s_rp); + if (nb != pm_nf[gi]) serr("a case binds every field of its member"); + v = pm_tag[gi]; + if (v < 63) cov = cov | (1 << v); + laddr(lslot[tmp]); op(O_LD); op(O_LD); lit(v); op(O_SUB); + bj = fixnew(hole(), bj); op(O_JZ); + expect(s_colon); + arm = nloc; + for (j = 0; j < nb && j < pm_nf[gi]; j++) { + bi = newloc(pbn[j]); fslots++; + ltype[bi] = pm_ft[gi * 8 + j]; + if (isfl(ltype[bi])) setflt(bi, 1); + pbl[j] = bi; + } + nxt = hole(); op(O_JMP); + fixall(bj, here); + for (j = 0; j < nb && j < pm_nf[gi]; j++) { + laddr(lslot[tmp]); op(O_LD); lit(pm_off[gi * 8 + j]); op(O_ADD); op(O_LD); + vaddr(pbl[j]); op(O_ST); + } + casebody(s0, live0); + block_close(arm); + setlit(nxt, here); + } else { + while (1) { + v = cexpr(); + if (tg && v >= 0 && v < 63) cov = cov | (1 << v); + laddr(lslot[tmp]); op(O_LD); + if (tg) op(O_LD); + lit(v); op(O_SUB); + bj = fixnew(hole(), bj); op(O_JZ); + if (!accept(s_comma)) break; + } + expect(s_colon); + nxt = hole(); op(O_JMP); + fixall(bj, here); + casebody(s0, live0); + setlit(nxt, here); + } + } else if (is_id(s_default)) { + next(); expect(s_colon); + if (defent >= 0) serr("a switch has one default"); + skip = hole(); op(O_JMP); // only reached when nothing matched + defent = here; + casebody(s0, live0); + setlit(skip, here); + } else die("expected case or default"); + } + next(); + snap_restore(s0); live = live0; + if (tg && defent < 0) cover_warn(tg - 1, cov, sl); + if (defent >= 0) { jmp(defent); loop_end(-1, -1); } + else { a = hole(); op(O_JMP); loop_end(a, s0); } // nothing matched: out with the states from the top + snap_pop(s0); + block_close(save); + fdepth--; + return 0; +} +// return: coerce and check the value, run the analyzer's return rule, free +// everything the function owns (exit_vars from depth 1), and jump to the +// epilogue, which is a patch chain resolved when the function ends. +int kw_return() { + int c, returned = tint; + stmtonly(); + next(); + if (!infn) serr("return outside a function"); + if (bxdepth) serr("return can't leave a block expression"); + if (is_op(s_semi)) { lit(0); c = C_PLAIN; if_ret(0, 1); } + else { + c = expr(); rval(); + returned = xty; + if_ret(xty, xzero); + if (fty && fty != tvoid) fcoerce(fty); + if (fty == tvoid) tcerr("return value", tvoid, xty); + else if (fty && !compat(fty, xty)) tcerr("return value", fty, xty); + } + if (nclo) { + if (!clo_ret[nclo - 1]) clo_ret[nclo - 1] = returned; + else if (clo_ret[nclo - 1] != returned) clo_ret[nclo - 1] = 0 - 1; + } + expect(s_semi); + retcheck(c); + exit_vars(1, 0); + epichain = fixnew(here, epichain); + lit(MW - 1); op(O_JMP); + live = 0; + return 0; +} +// o3 { ... }: an optimized region. o3 T x = e;: one o3 variable +// (a block is compiled as normal code behind a jump and handed to the core +// as a region once the function is complete: o3pend queues them. variables +// declared outside that are o3 get registers too, and anything with a pointer +// in it is fattened when control leaves the region.) +int kw_o3() { + int lo, i; word p; int save, outer; + stmtonly(); + next(); + if (!is_op(s_lb)) { o3one = 1; localdecl(1); o3one = 0; return 0; } + if (o3on || !infn) return block(); + reserve_code(2); lo = here + 2; lit(lo); op(O_JMP); + o3on = 1; o3reset(); + save = nloc; outer = nloc; + i = lbase; + // o3 variables declared outside + while (i < outer) { + if (ltag[i] & 16) { + o3promote(i); + o3mkcache(i); + o3refresh(i); + } + i++; + } + block(); + o3on = 0; + if (no3pend >= o3pendcap) { o3pend = chk_resize(o3pend, no3pend * 67, o3pendcap * 2 * 67); o3pendcap = o3pendcap * 2; } + { + p = o3pend + no3pend * 67; p[0] = lo; p[1] = here; p[2] = no3sl; + copy_w(p + 3, o3sl, no3sl); copy_w(p + 35, o3rg, no3sl); no3pend++; + } + local_drop(save); fdrop(save); // the hidden cache variables + i = lbase; + while (i < outer) { + ltag[i] = ltag[i] - (ltag[i] & 1792); o3cache[i] = 0; + // leaving o3: fatten what they hold + if ((ltag[i] & 16) && hasptr(ltype[i])) { + vaddr(i); + op(O_LD); + lit(ltype[i]); + callname(s_fatten); + op(O_DROP); + } + i++; + } + return 0; +} +// unchecked turns the analyzer off for one statement +int kw_unchecked() { + int old = fchk; + stmtonly(); + next(); + fchk = 0; + substmt(); + fchk = old; + return 0; +} + +// ---- asm blocks: raw vm words, one per token ---- +// numbers push, -n pushes a negative number, #n emits a raw word, one-char +// tokens from the mnemonic table are opcodes, names push the address of a +// variable or function, :name defines a label for this block. +// (labels are local to the block: :name defines one, a later name refers to it, +// and references to labels not yet defined are resolved when the block ends) +word asm_names = R_ASM, asm_sites = R_ASM + ASM_LABELS; +word asm_refs = R_ASM + ASM_LABELS + ASM_LABELS, asm_fixes = R_ASM + ASM_LABELS + ASM_LABELS + ASM_REFS; +int asm_labelcap = ASM_LABELS, asm_refcap = ASM_REFS; +int reserve_asm_labels(int need) { + int cap = asm_labelcap; + if (need <= cap) return cap; + while (cap < need) cap = cap * 2; + asm_names = chk_resize(asm_names, asm_labelcap, cap); asm_sites = chk_resize(asm_sites, asm_labelcap, cap); + asm_labelcap = cap; return cap; +} +int reserve_asm_references(int need) { + int cap = asm_refcap; + if (need <= cap) return cap; + while (cap < need) cap = cap * 2; + asm_refs = chk_resize(asm_refs, asm_refcap, cap); asm_fixes = chk_resize(asm_fixes, asm_refcap, cap); + asm_refcap = cap; return cap; +} +int asmblock() { + af_forget(); + int nl = 0, nr = 0, i, k, c, s; word ln = asm_names, la = asm_sites, rn = asm_refs, ra = asm_fixes; + expect(s_lb); + while (!is_op(s_rb)) { + if (tk == T_EOF) { die("unterminated asm block"); break; } + if (tk == T_FLT) { op(OP_FLIT); emit(tv); next(); continue; } + if (tk == T_NUM) { lit(tv); next(); continue; } + if (tk == T_BSTR) { bstrlit(tv); next(); continue; } + if (tk == T_STR) { strlit(tv); next(); continue; } + if (is_op(s_hash)) { + next(); + if (tk != T_NUM) die("# needs a number"); + emit(tv); + next(); + continue; + } + if (is_op(s_minus)) { + next(); + if (tk == T_NUM && tglue) { + lit(0 - tv); + next(); + } else { + op(O_SUB); + } + continue; + } + if (is_op(s_colon)) { + next(); + reserve_asm_labels(nl + 1); ln = asm_names; la = asm_sites; + ln[nl] = name(); + la[nl] = here; + nl++; + continue; + } + if (tv[0] && !tv[1] && (c = mnemidx(tv[0])) >= 0) { op(c); next(); continue; } + if (tk != T_ID) die("bad token in asm"); + i = findloc(tv); + if (i >= 0 && (ltag[i] & 16) && !larr[i]) serr("asm can't reach an o3 variable"); + if (i >= 0) { luse(i); ltag[i] = ltag[i] | LREAD | LASG; laddr(lslot[i]); next(); continue; } + k = -1; i = 0; + while (i < nl) { if (ln[i] == tv) k = i; i++; } + if (k >= 0) { lit(la[k]); next(); continue; } + s = lookup(tv); + if (s >= 0 && skind[s] == K_ENH) { op(XOP0 + sval[s]); next(); continue; } + if (s >= 0 && skind[s] != K_FN && skind[s] != K_KW) { lit(sval[s]); next(); continue; } + if (s >= 0 && skind[s] == K_FN) { fnref(s); next(); continue; } + reserve_asm_references(nr + 1); rn = asm_refs; ra = asm_fixes; + rn[nr] = tv; ra[nr] = hole(); nr++; next(); // a label further down this block + } + next(); + i = 0; + while (i < nr) { + k = -1; c = 0; + while (c < nl) { if (ln[c] == rn[i]) k = c; c++; } + if (k < 0) serrn(rn[i], "unknown name in asm"); + setlit(ra[i], la[k]); + i++; + } + return 0; +} +// virtual enhancement bodies are recipes read as a flat range by the host. +int contiguous_asm() { + word q = grab(), wrapped; int n = q[0], i = 0, need = 2, entry, at; + while (i < n) { + need = need + 16; + if (q[1 + i * 3] == T_STR || q[1 + i * 3] == T_BSTR) need = need + len_w(q[2 + i * 3]); + i++; + } + reserve_code(need); entry = here; + wrapped = chk_resize(0, 0, n * 3 + 7); wrapped[0] = n + 2; + wrapped[1] = T_OP; wrapped[2] = s_lb; wrapped[3] = tpack(); + copy_w(wrapped + 4, q + 1, n * 3); at = 4 + n * 3; + wrapped[at] = T_OP; wrapped[at + 1] = s_rb; wrapped[at + 2] = tpack(); + src_push(1, wrapped, 0, 0); next(); asmblock(); src_pop(); op(O_RET); + free(d*q); return entry; +} +// asm as a keyword (statement or expression): the block's own stack effect is +// the program's responsibility, so the result is a plain word +int kw_asm() { + next(); + asmblock(); + xcls = C_PLAIN; + return 0; +} + +// ---- quote: code as data ---- +// a token list is an object: [n] then kind, value, line for each token. names, +// strings and operators are interned pointers. +// grab reads the { ... } after quote or times (a parse word) into a token list, +// stopping at the matching brace. it buffers the tokens in R_QBUF, then copies +// them to a fresh object. quote compiles a list into the program as data and +// returns a copy at run time; feed (below) pushes a list back as input. +word .u grab() { + int n = 0, depth = 0; word q; + if (!is_op(s_lb)) die("expected {"); + next(); + while (1) { + if (tk == T_EOF) { die("unterminated quote"); break; } + if (is_op(s_lb)) depth++; + if (is_op(s_rb)) { if (!depth) break; depth--; } + reserve_R_QBUF((n + 1) * 3); + R_QBUF[n * 3] = tk; R_QBUF[n * 3 + 1] = tv; R_QBUF[n * 3 + 2] = tline; n++; + next(); + } + next(); + q = new(n * 3 + 1); + q[0] = n; + copy_w(q + 1, R_QBUF, n * 3); + return u*q; +} +unchecked word .u qnew(word src) { int n = src[0] * 3 + 1; word q = new(n); copy_w(q, src, n); return q; } +word .u qdup(.m q) { int n = q[0] * 3 + 1; word r = new(n); copy_w(r, q, n); return u*r; } +// emit n words copied from the array q. +int wcopyto(.m q, int n) { + int i = 0; reserve_code(n); + while (i < n) { emit(q[i]); i++; } + return 0; +} +// quote { tokens }: the list is emitted into the code behind a jump, and the +// expression's value is a fresh copy of it (qnew), so a program can edit its +// copy without touching the original +int kw_quote() { + word q; int j, a, n; + next(); + q = grab(); + j = hole(); op(O_JMP); + n = q[0] * 3 + 1; reserve_code(n); a = here; + wcopyto(q, n); + free(d*q); + setlit(j, here); + lit(a); + callname(s_qnew); + xcls = C_FRESH; + return 0; +} +// tokens of a string, as a token list +word .u tokens(int s) { + int n = 0; word q; + src_push(2, s, 0, 0); + next(); + while (tk != T_EOF) { + reserve_R_QBUF((n + 1) * 3); + R_QBUF[n * 3] = tk; R_QBUF[n * 3 + 1] = tv; R_QBUF[n * 3 + 2] = tline; n++; + next(); + } + src_pop(); + q = new(n * 3 + 1); + q[0] = n; + copy_w(q + 1, R_QBUF, n * 3); + return u*q; +} +// feed a token list into the input: the parser reads it next, then carries +// on where it was. the list is freed once it has been read. +unchecked int feed(.d q) { + src_push(1, q, 1, 1); + next(); + return 0; +} + +// ---- reading a unit and scanning it for signatures ---- +// a unit from stdin is read whole first (4 chars to a word), then scanned +// once for function signatures, so a function's tags are known before its first +// call even when it's defined further down. +// read the whole unit from stdin, up to a backtick (outside comments and +// literals, see srcend) or end of input, packing 4 +// characters to a word, so prescan can run over it and then the real parse can +// run over the same text +unchecked word readsrc() { + int cap = 4090; word b; int n = 0, c; word nb; int w; + b = new(4090); + qst = 0; qp = 0; qesc = 0; + while (!asm { ? }) { + c = asm { , } & 255; + // srcend only has anything to decide at a quote, a slash or a star (or + // inside a comment or literal); every other character in plain code just + // clears the "previous char was a slash" memory + if (qst || c == 34 || c == 39 || c == '/' || c == '*' || c == 96) { if (srcend(c)) break; } + else qp = 0; + if ((n >> 2) + 2 >= cap) { + nb = new(cap + cap + 6); + copy_w(nb, b, cap); + free(b); + b = nb; + cap = cap + cap + 6; + } + w = n >> 2; + if (!(n & 3)) b[w] = 0; + b[w] = b[w] | (c << ((n & 3) * 8)); + n++; + } + b[n >> 2] = b[n >> 2] & (pow2[(n & 3) * 8] - 1); // the 0 at the end + if ((n & 3) == 0) b[n >> 2] = 0; + b[(n >> 2) + 1] = 0; + return b; +} +// skip a <...> group (type parameters) +// prescan: before a unit is compiled, one dry pass over it records the +// signature (parameter tags, .u return, o3) of every function declared in it, +// so a call that appears before its definition is checked against the tags +// the callee will have. it only needs the shape of a declaration, not its body. +int pskipangle() { + int dp = 0; + while (tk != T_EOF) { + if (is_op(s_lt)) dp++; + else if (is_op(s_gt)) dp--; + else if (is_op(s_shr)) dp = dp - 2; + next(); + if (dp <= 0) return 0; + } + return 0; +} +// at the name after fn: read the signature's tags and record them +// prescan: at the ( after a function name, read the parameter list and record +// the signature packed into ssig (bit 0 .u return, bits 1..5 parameter count, +// two bits per parameter from bit 6) +int prescan_fn(int ret, int n, int o3) { + int np = 0, tags = 0, t = 0, dp, s; + next(); // the ( + if (!is_op(s_rp)) { + np = 1; dp = 1; + while (tk != T_EOF) { + if (is_op(s_lp)) dp++; + else if (is_op(s_rp)) { dp--; if (!dp) break; } + else if (dp == 1 && is_op(s_comma)) { if (np <= 12) tags = tags + (t << (np + np - 2)); np++; t = 0; } + else if (dp == 1 && is_op(s_dot)) { next(); if (is_id(s_m)) t = 1; else if (is_id(s_d)) t = 2; continue; } + next(); + } + if (np <= 12) tags = tags + (t << (np + np - 2)); + } + next(); + if (!is_op(s_lb) && !is_op(s_semi)) return 0; // not a function: an enhancement, a call in a parse word... + if (!dnm) dnm = n; + s = lookup(n); + if (s < 0 || s < ubase || skind[s] != K_FN || (sflag[s] & F_DEF)) s = fwdfn(n); + if (!(sflag[s] & F_SIG)) { + ssig[s] = ret + np + np + tags * 64; + sflag[s] = sflag[s] | F_SIG | F_PRE; + } + if (o3) sflag[s] = sflag[s] | F_O3; + return 0; +} +// scan ahead, without emitting code, to learn what a block of tokens contains. +int prescan(int kind, int obj) { + int depth = 0, sp = panic, sn = nerr, pend = 0, po3 = 0, n, dep; + dry++; + src_push(kind, obj, 0, 0); + next(); + while (tk != T_EOF) { + if (is_op(s_lb)) depth++; + else if (is_op(s_rb)) { depth--; pend = 0; po3 = 0; } + else if (depth <= 0) { + if (is_op(s_semi)) { pend = 0; po3 = 0; } + else if (is_op(s_dot)) { peek(); if (pk_tk == T_ID && pk_tv == s_u) pend = 1; } + else if (is_id(s_o3)) po3 = 1; + else if (tk == T_ID) { + peek(); + if (pk_tk == T_OP && pk_tv == s_lp) { + n = tv; + next(); + prescan_fn(pend, n, po3); + pend = 0; + po3 = 0; + continue; + } + if (pk_tk == T_OP && pk_tv == s_lt) { // list in a type, or first( a generic function + n = tv; next(); dep = 0; + while (tk != T_EOF) { + if (is_op(s_lt)) { + dep++; + } else if (is_op(s_gt)) { + dep--; + } else if (is_op(s_shr)) dep = dep - 2; + next(); + if (dep <= 0) break; + } + if (is_op(s_lp)) { prescan_fn(pend, n, po3); pend = 0; po3 = 0; } + continue; + } + } + } + next(); + } + src_pop(); + dry--; panic = sp; nerr = sn; + return 0; +} +// compile a whole unit from stdin +int unit_stdin() { + int src = readsrc(); + prescan(3, src); + src_push(3, src, 0, 0); + next(); + unit_top(); + unit_end(ubase); + src_pop(); + free(src); + return 0; +} + +// ---- nested compiles ---- +// compile() and load() can run while another function is being compiled (from +// a parse word). the function context is saved, the new code is emitted in +// place with a jump around it, and then everything is put back. +// the context frame is 34 words: everything about the function being compiled +// that a nested compile would otherwise clobber (locals, depth, frame size, +// loop stack position, the expression-state globals). closures use the same +// pair. +const int CTX_WORDS = 34; +int ctx_push() { + af_forget(); + word f; + reserve_R_CTX((ctxsp + 1) * CTX_WORDS); f = R_CTX + ctxsp * CTX_WORDS; + ctxsp++; + f[0] = nloc; f[1] = lbase; f[2] = fdepth; f[3] = fslots; f[4] = fchk; f[5] = fret; f[6] = live; + f[7] = bxdepth; f[8] = epichain; f[9] = fframe; f[10] = infn; f[11] = lsp; f[12] = loopbase; + f[13] = lv; f[14] = lvk; f[15] = lvi; f[16] = kw_expr; f[17] = xcls; f[18] = snaptop; + f[19] = fty; f[20] = ntp; f[21] = xty; copy_w(f + 22, tpn, 8); + f[30] = finsym; f[31] = finfer; f[32] = ifcur; f[33] = ifcall; + return 0; +} +// pop the innermost function context. +int ctx_pop() { + word f; + ctxsp--; + f = R_CTX + ctxsp * CTX_WORDS; + local_drop(f[0]); + fdrop(nloc); + lbase = f[1]; + fdepth = f[2]; + fslots = f[3]; + fchk = f[4]; + fret = f[5]; + live = f[6]; + bxdepth = f[7]; epichain = f[8]; fframe = f[9]; infn = f[10]; lsp = f[11]; loopbase = f[12]; + lv = f[13]; lvk = f[14]; lvi = f[15]; kw_expr = f[16]; xcls = f[17]; snaptop = f[18]; + fty = f[19]; ntp = f[20]; xty = f[21]; copy_w(tpn, f + 22, 8); + finsym = f[30]; finfer = f[31]; ifcur = f[32]; ifcall = f[33]; + return 0; +} + +// ---- functions and the top level ---- +// ---- a function, start to finish ---- +// fndef reads the header, then: fn_begin resets the per-function state, +// prologue emits the frame setup, the parameters are declared and stored from +// the stack into their slots, body parses the statements, and epilogue emits +// the shared exit (return jumps here through the epichain patch list). the +// frame size is not known until the end, so the prologue's subtract is a hole +// that epilogue fills with fslots. +// fn_begin: chk is 0 for unchecked, ret is 1 for a .u return +int fn_begin(int chk, int ret) { + lbase = nloc; fdepth = 1; fslots = 0; fchk = chk && check_on; fret = ret; fty = 0; finfer = 0; + live = 1; bxdepth = 0; epichain = 0; infn = 1; loopbase = lsp; lv = 0; lvk = LV_NONE; xbor = -1; + return 0; +} +// allocate the frame, retaining the caller's pointer until any growth finishes. +int prologue() { + int k, busy, done, helper = !nclo && (sname[finsym] == s_frame_grow || sname[finsym] == s_frame_leave); + lit(ZFP); op(O_LD); + if (!helper) { lit(0); op(O_PICK); } + fframe = hole(); op(O_SUB); lit(ZFP); op(O_ST); + if (helper) return 0; + lit(sval[lookup(s_frame_busy)]); op(O_LD); busy = hole(); op(O_JZ); + done = hole(); op(O_JMP); setlit(busy, here); + lit(ZFP); op(O_LD); lit(sval[lookup(s_frame_floor)]); op(O_LD); + lit(sval[lookup(s_frame_guard)]); op(O_LD); op(O_ADD); op(O_LT); k = hole(); op(O_JZ); + lit(ZFP); op(O_LD); lit(1); op(O_PICK); lit(ZFP); op(O_ST); + callname(s_frame_grow); lit(ZFP); op(O_ST); + busy = hole(); op(O_JMP); + setlit(k, here); setlit(done, here); op(O_DROP); setlit(busy, here); + return 0; +} +// emit the function epilogue, patching every return to it. +int epilogue() { + int k, node, done, helper = !nclo && (sname[finsym] == s_frame_grow || sname[finsym] == s_frame_leave); + lit(0); + fixall(epichain, here); + lit(ZFP); op(O_LD); lit(fslots); op(O_ADD); lit(ZFP); op(O_ST); + if (!helper) { + lit(sval[lookup(s_frame_node)]); op(O_LD); node = hole(); op(O_JZ); + lit(ZFP); op(O_LD); lit(sval[lookup(s_frame_top)]); op(O_LD); op(O_SUB); k = hole(); op(O_JZ); + done = hole(); op(O_JMP); setlit(k, here); + callname(s_frame_leave); lit(ZFP); op(O_ST); setlit(done, here); setlit(node, here); + } + op(O_RET); + setlit(fframe, fslots); + infn = 0; + local_drop(lbase); + fdrop(lbase); + return 0; +} +// the statements of a function between its braces, then the end-of-function +// checks: hints, unused m objects, anything still owned is freed, and a .u +// function that falls off the end is an error +int body() { + expect(s_lb); + while (!is_op(s_rb)) { + if (tk == T_EOF) { die("missing } at the end of a function"); break; } + stmt(); + } + local_hints(lbase); + unused_check(lbase); + if (ifcur) if_fin(); + if (ifcur && live && ifcur[1]) ifcur[4] = 1; // falling off the end gives 0 + if (fchk && live) { + exit_vars(1, 1); + if (fret) cerr("a function returning .u has to end with a return"); + } + next(); + return 0; +} +// the rest of a function, after its return type and name: +// int f(int a, node*.m n, .d x, b) { ... } *list.u mk(int n) { ... } +// rty is 0 for fn (a plain word), ret is 1 for a .u return +// (this is also where a function's signature is packed into ssig and its type +// into stype, checked against any earlier prototype or prescan result, and +// where the o3 region for an o3 function is handed to the core) +int fndef(int chk, int rty, int ret, int n) { + int s, np = 0, tags = 0, t, sig, i; [24]word pn; [24]word pt; [24]word pty; int typed = 0, ft, o3f = o3nxt, lo, hi, pk; + o3nxt = 0; + if (rty || ntp) typed = 1; + if (!dnm) dnm = n; + s = lookup(n); + if (s < 0 || s < ubase || skind[s] != K_FN || (sflag[s] & F_DEF)) { + s = addsym(n, K_FN, 0); + sflag[s] = 0; + } + ifcur = 0; + if (ifon && ifkk && !ginst && !ntp && !dry) { + if (ifkk > 0 && !rty) if_begin(s, ifkk); else if_begin(s, 0); + } + ifkk = 0; + expect(s_lp); + if (!is_op(s_rp)) { + while (1) { + t = 0; + pty[np] = tword; + pk = 0; + if (!is_op(s_dot) && ((tk == T_OP && tv == s_lk) || (tk == T_ID && (tv == s_fn || istypename(tv))))) { + pty[np] = ptype(); typed = 1; pk = 1; + if (isagg(pty[np])) { + if (ginst) pty[np] = tptr(pty[np]); else if (!stubgen) serr("structs and arrays are passed by pointer"); + // (an extern stub takes its struct by value, which is an address here) + } + } + if (ifcur) { + if (pk) if_param(np, tpack(), 0); + else if (is_op(s_dot)) if_param(np, tpack(), 2); + else if_param(np, tpack(), 1); + } + if (accept(s_dot)) { // .m lends, .d takes over + if (tv == s_m) { + t = 1; + } else if (tv == s_d) t = 2; else die("a parameter tag is .m or .d"); + next(); + } + pnoffs[np] = toff; rs_note(RS_PARAM, toff); pn[np] = name(); + if (o3f && pty[np] == tword) serr("o3 parameters need a type"); + if (np >= 12 && t) die("only the first 12 parameters can be tagged"); + pt[np] = t; + if (np < 12) tags = tags + (t << (np + np)); + np++; + if (np >= 24) die("too many parameters"); + if (!accept(s_comma)) break; + } + } + expect(s_rp); + sig = ret + np + np + tags * 64; + if ((sflag[s] & F_SIG) && !(sflag[s] & F_PRE)) { + if (ssig[s] != sig) serrn(n, "doesn't match its earlier declaration"); + } + else if ((sflag[s] & F_UNTYPED) && (tags || ret)) serrn(n, "was called before its tags were declared; put a prototype above the first call"); + ssig[s] = sig; + sflag[s] = (sflag[s] | F_SIG) - (sflag[s] & F_PRE); + if (ntp) { sflag[s] = sflag[s] | F_GEN; if (!ginst) gtmake(s); } + if (typed) { + if (!rty) rty = tword; + ft = tfn(rty, np, pty, sig); + if (stype[s] && stype[s] != ft) serrn(n, "doesn't match its earlier declaration"); + stype[s] = ft; + } + sig_attach(n, rty, np, pn, pty, typed); + if (accept(s_semi)) { sflag[s] = sflag[s] | F_DECL; ntp = 0; return s; } + if (sflag[s] & F_DEF) serrn(n, "is defined twice"); + if (!ginst && ntp) { // a generic function is not compiled here: its tokens are kept and each set of type arguments gets its own copy + if (sflag[s] & F_TPL) serrn(n, "is defined twice"); + sflag[s] = sflag[s] | F_TPL; + gnew = gtmake(s); + gskip(); + ntp = 0; + sval[s] = here; // the name still has an address and a type, so reflection can ask for them; it halts if it is ever jumped to + sflag[s] = sflag[s] | F_DEF; + fixall(sfix[s], here); sfix[s] = 0; + lit(MW - 1); op(O_JMP); + return s; + } + sval[s] = here; + sflag[s] = sflag[s] | F_DEF; + fixall(sfix[s], here); sfix[s] = 0; + fn_begin(chk, ret); + finsym = s; finfer = !ret; + if (typed) fty = rty; + no3pend = 0; + if (o3f) { o3on = 1; o3reset(); sflag[s] = sflag[s] | F_O3; } + prologue(); + i = 0; + while (i < np) { + t = newloc(pn[i]); lrskind[t] = RS_PARAM; lrsoff[t] = pnoffs[i]; fslots++; lsmark(1, pn[i]); + ltype[t] = pty[i]; lptr[t] = 0; + lt_note(pnoffs[i], pty[i]); + if (o3f) { ltag[t] = 16; o3promote(t); } + if (isfl(pty[i])) setflt(t, 1); + if (fchk && pt[i] == 1) lstate[t] = S_BORROW; + if (fchk && pt[i] == 2) { lstate[t] = S_M; lown[t] = 1; } + i++; + } + i = np; + while (i) { i--; laddr(i); op(O_ST); } + if (o3f) { // the region starts here: a jump, so the cpu enters it + reserve_code(2); lo = here + 2; lit(lo); op(O_JMP); + i = 0; while (i < np) { t = lbase + i; o3mkcache(t); o3refresh(t); i++; } + } + body(); + hi = here; + epilogue(); + if (o3f) { + o3on = 0; + o3fs = fslots; + o3na = np; + o3fn0 = sval[s]; + o3region(lo, hi, o3sl, o3rg, no3sl); + } + o3flush(); + ntp = 0; ifcur = 0; + return s; +} +// let g = 1, buf[10]; int g = 3; [10]int buf; point origin; +// ---- globals, constants, enhancements ---- +// a global's storage is emitted in place among the code (one word, or the +// whole array), and the symbol's value is its address. a string initializer +// points at characters emitted right after. +int gdecl(int typed) { + ifgk = 0; + if (isp == 0) ifgk = tpack(); + next(); + return gdecl2(0, 0, name()); +} +// compile the rest of a global declaration: its type, name and initial value. +int gdecl2(int t, int tg, int n) { + int a, ng = nameoff; word k, arraytype; int s, kp = ifgk, gty = 0, gok = ifon && !t && !dry; + ifgk = 0; + if (!dnm) dnm = n; + if (tg) serr("tags go on locals and parameters, not globals"); + if (t == tvoid) serr("a variable can't be void"); + while (1) { + if (accept(s_lk)) { + k = cexpr(); expect(s_rk); + if (!t) t = tword; + arraytype = tarr(t, k); k = tsz(arraytype); reserve_code(k); + s = addsym(n, K_GARR, here); rs_put(ng, RS_SYM, rs_sym(s), s); stype[s] = arraytype; + while (k) { emit(0); k--; } + if (gok) { if (!gty) gty = tint; else if (gty != tint) gok = 0; } + } else if (t && isagg(t)) { + k = tsz(t); reserve_code(k); + s = addsym(n, K_GARR, here); rs_put(ng, RS_SYM, rs_sym(s), s); stype[s] = t; + while (k) { emit(0); k--; } + } else { + reserve_code(1); a = here; + s = addsym(n, K_GVAR, a); rs_put(ng, RS_SYM, rs_sym(s), s); + if (t) stype[s] = t; + emit(0); + if (accept(s_eq)) { + if (tk == T_STR) { + reserve_code(len_w(tv) + 1); *(a + hdelta) = here; + k = tv; + while (*k) { emit(*k); k++; } + emit(0); + next(); + if (gok) { if (!gty) gty = tptr(tchar); else if (gty != tptr(tchar)) gok = 0; } + } + else { + *(a + hdelta) = cnorm(t, cexpr()); + if (gok) { if (!gty) gty = tint; else if (gty != tint) gok = 0; } + } + } else if (gok) gok = 0; // no initializer: nothing says what it is + } + if (stype[s]) lt_note(ng, stype[s]); else lt_note(ng, tword); + if (!accept(s_comma)) break; + ng = toff; n = name(); + } + expect(s_semi); + if (gok && gty && kp) fact_add(kp, 0 - 1, gty, ""); + return 0; +} +// a constant cut to the sized integer type t, the way tnorm does at run time +int cnorm(word t, int v) { + int b, mask, sg; + if (!t) return v; + b = tbytes(t); + if (!b || t[0] != TY_INT) return v; + mask = (1 << (8 * b)) - 1; + v = v & mask; + if (t[6] & 1) { sg = 1 << (8 * b - 1); v = (v ^ sg) - sg; } + return v; +} +// const A = 1, B = A + 2; +// const [type] NAME = e, NAME = e; the type is optional and is read and dropped, as in zc0 +int gconst() { + int n; + next(); + if (tk == T_ID) { peekat(0); if (pk_tk == T_ID || (pk_tk == T_OP && pk_tv == s_star)) ptype(); } + while (1) { n = name(); if (!dnm) dnm = n; expect(s_eq); addsym(n, K_CONST, cexpr()); if (!accept(s_comma)) break; } + expect(s_semi); + return 0; +} +// enhance [n] name(a, b) micro "recipe" [else fn]; n outputs, 1 if left out +// enhance [n] name(a, b) vm { asm } [else fn]; +// lays out the descriptor the install opcode reads, and wires it in right +// away (an image wires its own in when it boots). +int enhdef() { + int n, np = 0, nout = 1, dsc, na, pay, len = 0, kind = 0, i; word p; int s, slot, r; [8]word pn; + next(); + // enhance 2 divmod(a, b) + if (tk == T_NUM) { + if (tv < 0 || tv > 8) serr("an enhancement has at most 8 outputs"); + nout = tv; + next(); + } + n = name(); + expect(s_lp); + if (!is_op(s_rp)) { + while (1) { + if (np == 8) die("an enhancement takes at most 8 inputs"); + pnoffs[np] = toff; rs_note(RS_PARAM, toff); pn[np] = name(); + np++; + if (!accept(s_comma)) break; + } + } + expect(s_rp); + len = 7 + np; i = 0; while (i < np) { len = len + len_w(pn[i]) + 1; i++; } + reserve_code(len); len = 0; dsc = here; i = 0; while (i < 7) { emit(0); i++; } + na = here; i = 0; while (i < np) { emit(0); i++; } + i = 0; + while (i < np) { *(na + i + hdelta) = here; p = pn[i]; while (*p) { emit(*p); p++; } emit(0); i++; } + if (accept(s_micro)) { + if (tk != T_STR) die("expected the recipe as a string"); + ebn = 0; while (tk == T_STR) { ebs(tv); next(); } + reserve_code(ebn + 1); pay = here; wcopyto(ebuf, ebn + 1); + } else if (accept(s_vm)) { pay = contiguous_asm(); len = here - pay; kind = 1; } + else die("expected micro or vm"); + slot = nenh; + if (slot >= 512) fatal("all 512 enhancement slots are taken"); + nenh++; + p = dsc + hdelta; + p[0] = slot; p[1] = kind; p[2] = np; p[3] = nout; p[4] = pay; p[5] = len; p[6] = na; + s = addsym(n, K_ENH, slot); + ssig[s] = np + nout * 16 + dsc * 256; + if (accept(s_else)) { r = lookup(tv); if (r < 0) r = fwdfn(tv); next(); sfix[s] = r + 1; } + expect(s_semi); + if (!hdelta) { + r = install(dsc); + if (r < 0) { + if (!sfix[s]) serrn(n, "the enhancement didn't install (see install's codes in core.c)"); + sflag[s] = sflag[s] | F_FALLBACK; + } + } + return 0; +} +// recovery at the top level: skip to the next token that can start a +// declaration, outside any braces +int topsync() { + int depth = 0; + while (tk != T_EOF) { + if (is_op(s_lb)) depth++; + else if (is_op(s_rb)) { depth--; if (depth < 0) depth = 0; } + else if (!depth && tk == T_ID && (tv == s_fn || tv == s_var || tv == s_const || tv == s_struct || tv == s_type + || tv == s_enhance || tv == s_typecheck || tv == s_docs || tv == s_unchecked || tv == s_immediate || tv == s_o3 + || tv == s_memdebug || tv == s_enum || tv == s_include)) break; + next(); + } + panic = 0; + return 0; +} +// after a function's name: declares its type parameters. any the return +// type used were taken on trust; now they get their real numbers +int genparams() { + int i, j, n = 0; [8]word ps; [8]word bs; + if (!is_op(s_lt)) { if (ntp) serrn(tpn[0], "isn't a type"); ntp = 0; return 0; } + next(); + while (1) { + if (n >= 8) fatal("too many type parameters"); + ps[n] = name(); + n++; + if (!accept(s_comma)) break; + } + closeangle(); + fill_w(bs, 0, 8); + i = 0; + while (i < ntp) { + j = 0; + while (j < n && ps[j] != tpn[i]) j++; + if (j == n) serrn(tpn[i], "isn't a type"); else bs[i] = tvar(j, ps[j]); + i++; + } + if (ntp && dty) dty = subst(dty, bs); + copy_w(tpn, ps, n); ntp = n; + if (ginst) ntp = 0; + return 0; +} +// the return type of a function or the type of a global, with unknown names allowed for now +int rettype() { + tvauto = 1; + dtype(); + tvauto = 0; + return 0; +} +// finish a function declaration. an immediate fn also becomes a keyword +// (kind K_KW whose handler is the function), which is how parse words work: +// the parser calls it with kwcall the moment it meets the name. +int fndone(int s, int imm) { + int k; + ntp = 0; + if (imm) { + if (hdelta) serr("parse words can't be defined while building an image"); + k = addsym(sname[s], K_KW, sval[s]); + sflag[k] = F_DEF | F_STMT | F_EXPR | F_TOP; + } + return 0; +} +// one top-level item. this is the dispatcher for everything a unit can +// contain: unchecked / immediate / o3 prefixes, fn declarations (with or +// without a leading return type), globals, struct, type, enum, include, +// typecheck, memdebug, enhance, const, and parse words used at the top level. +// anything else is a typed global or function: a type, a name, and then +// ( or < means function, otherwise a global. +int topitem0() { + int r, i; + dsn = 0; dnm = 0; + if (docs_on && dn && dline == tline && !ginst) { + if (dn > docsnapcap) { docsnapcap = dn; dsnap = chk_resize(dsnap, 0, docsnapcap); } + i = 0; while (i < dn) { dsnap[i] = dbuf[i]; i++; } + dsn = dn; dline = 0; + } + r = topitem1(); + if (dsn && dnm) doc_attach(dnm); + dsn = 0; dnm = 0; + return r; +} +// compile one top-level item, after the first check of its keyword. +int topitem1() { + int chk = 1, imm = 0, s, k, n; + if (panic) return topsync(); + if (is_id(s_unchecked)) { + next(); + if (accept(s_semi)) { check_on = 0; return 0; } // unchecked; switches the analyzer off from here on + chk = 0; + } + if (is_id(s_immediate)) { next(); imm = 1; } + o3nxt = 0; + if (is_id(s_o3)) { next(); o3nxt = 1; } + ntp = 0; ifkk = 0; + if (is_id(s_fn)) { + peek(); + if (!(pk_tk == T_OP && pk_tv == s_lp)) { + ifkp = tpack(); ifkk = 1; + if (isp != 0) ifkk = 0; + next(); + n = name(); + if (ginst) n = ginsn; + dty = 0; + genparams(); + return fndone(fndef(chk, 0, 0, n), imm); + } + } + if (!chk || imm || o3nxt) { // has to be a function, with a return type + ifkp = tpack(); ifkk = 0 - 1; if (is_op(s_dot)) ifkk = 2; + if (isp != 0) ifkk = 0; + rettype(); n = name(); if (ginst) n = ginsn; genparams(); + if (dtag && dtag != S_U) serr("the only return tag is .u"); + if (!is_op(s_lp)) { die("expected a function"); return 0; } + return fndone(fndef(chk, dty, dtag == S_U, n), imm); + } + if (is_id(s_var)) return gdecl(0); + if (is_id(s_struct)) { kw_struct(); return 0; } + if (is_id(s_type)) return kw_type(); + if (is_id(s_typecheck)) return kw_typecheck(); + if (is_id(s_docs)) return kw_docs(); + if (is_id(s_memdebug)) return kw_memdebug(); + if (is_id(s_enum)) return kw_enum(); + if (is_id(s_include)) return kw_include(); + if (is_id(s_enhance)) return enhdef(); + if (is_id(s_const)) return gconst(); + if (tk == T_ID) { + s = lookup(tv); + if (s >= 0 && skind[s] == K_KW && (sflag[s] & F_TOP)) { kwcall(s, 0); return 0; } + } + if (tstart() || (tk == T_ID && lookup(tv) < 0)) { // int f(...) { }, T first(...) { }, int g = 1; + ifkp = tpack(); ifkk = 0 - 1; if (is_op(s_dot)) ifkk = 2; + if (isp != 0) ifkk = 0; + rettype(); n = name(); + if (is_op(s_lp) || is_op(s_lt)) { + if (ginst) n = ginsn; + genparams(); + if (dtag && dtag != S_U) serr("the only return tag is .u"); + return fndone(fndef(chk, dty, dtag == S_U, n), 0); + } + if (ntp) { serrn(tpn[0], "isn't a type"); ntp = 0; } + return gdecl2(dty, dtag, n); + } + die("expected a declaration"); + return 0; +} +// ---- generic functions: templates and instances ---- +// which type parameter does name n stand for while an instance is compiled +int gfind(int n) { + int i = 0; + while (i < gng) { + if (gnm[i] == n) return i; + i++; + } + return 0 - 1; +} +// does a type still mention a type parameter? +word hasvar(word t) { + int k = t[0], i; word v; + if (k == TY_VAR) return 1; + if (k == TY_PTR || k == TY_ARR || k == TY_DYN) return hasvar(t[4]); + if (k == TY_FN) { + if (hasvar(t[4])) return 1; + i = 0; + while (i < t[3]) { + if (hasvar(t[7 + i])) return 1; + i++; + } + return 0; + } + if (k == TY_STRUCT && t[5]) { + v = t[6]; i = 0; + while (i < v[0]) { + if (hasvar(v[1 + i])) return 1; + i++; + } + } + return 0; +} +// add the current token to the item being recorded +unchecked int grec_add() { + word nb; + if (gn * 3 + 4 > gcap) { + nb = new(gcap + gcap); + copy_w(nb, gbuf, gcap); + free(gbuf); + gbuf = nb; + gcap = gcap + gcap; + } + gbuf[1 + gn * 3] = tk; gbuf[2 + gn * 3] = tv; gbuf[3 + gn * 3] = tpack(); + gn++; + return 0; +} +// the template entry for generic function s, made on first sight of its header +unchecked int ginit() { + gtab = 1; + gt_sym = chk_resize(0, 0, gtcap); gt_tok = chk_resize(0, 0, gtcap); gt_np = chk_resize(0, 0, gtcap); + gt_nm = chk_resize(0, 0, gtcap * 8); + gi_t = chk_resize(0, 0, gicap); gi_sym = chk_resize(0, 0, gicap); gi_done = chk_resize(0, 0, gicap); + gi_ar = chk_resize(0, 0, gicap * 8); + return 0; +} +int reserve_generics(int need) { + int cap = gtcap; + if (need <= cap) return cap; + while (cap < need) cap = cap * 2; + gt_sym = chk_resize(gt_sym, gt_n, cap); gt_tok = chk_resize(gt_tok, gt_n, cap); + gt_np = chk_resize(gt_np, gt_n, cap); gt_nm = chk_resize(gt_nm, gt_n * 8, cap * 8); + gtcap = cap; return cap; +} +int reserve_instances(int need) { + int cap = gicap; + if (need <= cap) return cap; + while (cap < need) cap = cap * 2; + gi_t = chk_resize(gi_t, gi_n, cap); gi_sym = chk_resize(gi_sym, gi_n, cap); + gi_done = chk_resize(gi_done, gi_n, cap); gi_ar = chk_resize(gi_ar, gi_n * 8, cap * 8); + gicap = cap; return cap; +} +// make the generic instance of the function or struct s for its type arguments. +unchecked int gtmake(int s) { + int k = 0; + if (!gtab) ginit(); + while (k < gt_n) { + if (gt_sym[k] == s) break; + k++; + } + if (k == gt_n) { + if (gt_n >= gtcap) reserve_generics(gt_n + 1); + gt_n++; + gt_sym[k] = s; gt_tok[k] = 0; + } + gt_np[k] = ntp; + copy_w(gt_nm + k * 8, tpn, ntp); + return k; +} +// skip a function body without compiling it: from the { to its matching } +int gskip() { + int depth = 0; + if (!is_op(s_lb)) { die("expected { to start the function"); return 0; } + while (tk != T_EOF) { + if (is_op(s_lb)) depth++; + else if (is_op(s_rb)) { + depth--; + if (depth <= 0) { next(); break; } + } + next(); + } + return 0; +} +// the item just read defined generic function gnew: keep its tokens +unchecked int gattach(word buf, int n) { + n--; // the last token belongs to the next item + if (nerr) { free(buf); return 0; } + buf[0] = n; + gt_tok[gnew] = buf; + return 0; +} +// the symbol for template s with these type arguments, made on first use and +// queued to be compiled once the template's tokens are in. a type parameter +// that nothing bound is a plain word. +word ginstance(int s, word bs) { + int k = 0, t = 0 - 1, np, i, j, ns; word nm; [24]word dg; int nd; word a; + if (dry || !gtab) return s; + while (k < gt_n) { + if (gt_sym[k] == s) { t = k; break; } + k++; + } + if (t < 0) return s; + np = gt_np[t]; + i = 0; + while (i < np) { + if (!bs[i]) bs[i] = tword; + if (hasvar(bs[i])) return s; + i++; + } + k = 0; + while (k < gi_n) { + if (gi_t[k] == t) { + j = 0; + while (j < np && gi_ar[k * 8 + j] == bs[j]) j++; + if (j == np) return gi_sym[k]; + } + k++; + } + if (gi_n >= gicap) reserve_instances(gi_n + 1); + i = 0; j = 0; + a = sname[s]; nm = chk_resize(0, 0, len_w(a) + np * 18 + 3); + while (a[j]) { nm[i] = a[j]; i++; j++; } + nm[i] = '<'; i++; + k = 0; + while (k < np) { + if (k) { nm[i] = ','; i++; } + a = bs[k]; nd = 0; + while (a && nd < 20) { dg[nd] = '0' + (a & 15); nd++; a = a >> 4; } + while (nd) { nd--; nm[i] = dg[nd]; i++; } + k++; + } + nm[i] = '>'; i++; nm[i] = 0; + ns = addsym(intern(nm), K_FN, tpack()); + sflag[ns] = F_SIG | (sflag[s] & F_O3); + ssig[ns] = ssig[s]; + k = gi_n; gi_n++; + gi_t[k] = t; gi_sym[k] = ns; gi_done[k] = 0; + copy_w(gi_ar + k * 8, bs, np); + return ns; +} +// compile one instance: replay the template's tokens with its type +// parameters bound +unchecked int gcompile(int k) { + int t = gi_t[k]; + ginst = 1; gng = gt_np[t]; + copy_w(gnm, gt_nm + t * 8, 8); + copy_w(gib, gi_ar + k * 8, 8); + ginsn = sname[gi_sym[k]]; + src_push(1, gt_tok[t], 0, 0); + next(); + topitem0(); + if (tk != T_EOF) die("unexpected tokens after a generic function"); + src_pop(); + ginst = 0; gng = 0; + return 0; +} +// compile the instances asked for so far whose templates have been read, and +// any they ask for in turn +int gdrain() { + int k, more = 1; + if (infn || ginst) return 0; + if (panic) return 0; + while (more) { + more = 0; + k = 0; + while (k < gi_n) { + if (!gi_done[k] && gt_tok[gi_t[k]]) { + gi_done[k] = 1; + gcompile(k); + more = 1; + } + k++; + } + } + return 0; +} +// one top-level item, recording its tokens so that a generic function's can be kept +int topitem() { + int sb = gbuf, sn = gn, sc = gcap, sr = grec, sg = gnew, rb, rn; + if (ginst) return topitem0(); + gbuf = new(256); gcap = 256; gn = 0; grec = 1; gnew = 0 - 1; + grec_add(); + topitem0(); + rb = gbuf; rn = gn; + grec = sr; gbuf = sb; gn = sn; gcap = sc; + if (gnew >= 0) gattach(rb, rn); else free(rb); + gnew = sg; + gdrain(); + return 0; +} +// a unit is compiled item by item; unit_end then reports any function that +// was called but never defined, and stops if any error was found +int unit_top() { + while (tk != T_EOF) topitem(); + return 0; +} +// the edit distance between two names (levenshtein), or 99 when one is too long +word editdist(word a, word b) { + int la = len_w(a), lb = len_w(b), i, j, x, y, z, last, row[65]; + if (la > 60 || lb > 60) return 99; + j = 0; while (j <= lb) { row[j] = j; j++; } + i = 1; + while (i <= la) { + last = row[0]; row[0] = i; j = 1; + while (j <= lb) { + x = row[j] + 1; + y = row[j - 1] + 1; + z = last + (a[i - 1] != b[j - 1]); + last = row[j]; + if (y < x) x = y; + if (z < x) x = z; + row[j] = x; + j++; + } + i++; + } + return row[lb]; +} +// the defined function, global or constant whose name is closest to n, if it +// is close enough to be a likely typo; 0 when none is +int similar(int n) { + int j = 0, best = 0, bd = 99, dist, k, lim = len_w(n) / 3; + if (lim < 1) lim = 1; + if (lim > 3) lim = 3; + while (j < nsym) { + k = skind[j]; + if (k == K_FN || k == K_GVAR || k == K_GARR || k == K_CONST) { + if (sflag[j] & F_DEF) { + if (sname[j] != n) { + dist = editdist(n, sname[j]); + if (dist < bd) { bd = dist; best = sname[j]; } + } + } + } + j++; + } + if (bd > lim) return 0; + return best; +} +// end a compile unit from token from onward, and report what it defined. +int unit_end(int from) { + int i = from, best; + panic = 0; + while (i < nsym) { + // a bodyless declaration (fn f();) promises a definition elsewhere. in check mode that + // elsewhere is another file of the same build, which this document can't see + if (skind[i] == K_FN && !(sflag[i] & F_DEF) && sfix[i] && !(cmode && (sflag[i] & F_DECL))) { + tunpack(sval[i]); + best = similar(sname[i]); + if (!dry) { + errat(); + put_s(sname[i]); put_s(" is used but never defined"); + if (best) { put_s("; did you mean '"); put_s(best); put_s("'?"); } + errend(); + } + } + i++; + } + if (nerr) giveup(); + return 0; +} + +// compile a token list as the body of a new function, or (top) as top-level +// declarations. returns the entry address of the function. +// (this is what eval, compile and load are built on. a function body made this +// way is emitted behind a jump when another function is being compiled, so +// compile() works inside a parse word.) +unchecked int compile_list(int q, int top) { + int hop = -1, entry = 0, from = nsym; + ctx_push(); ifcur = 0; ifcall = -1; + if (infn) { hop = hole(); op(O_JMP); } + if (top) prescan(1, q); + src_push(1, q, 0, 0); + next(); + if (top) unit_top(); + else { + entry = here; + fn_begin(1, 0); + prologue(); + while (tk != T_EOF) stmt(); + unused_check(lbase); + if (fchk && live) exit_vars(1, 1); + epilogue(); + } + src_pop(); + unit_end(from); + if (hop >= 0) setlit(hop, here); + if (here > ptop) ptop = here; + ctx_pop(); + return entry; +} +// compile a token list into a function and return its address. +int compile(int q) { return compile_list(q, 0); } +// compile a token list and run its top-level code. +int load(int q) { compile_list(q, 1); return 0; } +// compile a token list and run it, returning its result. +int eval(int q) { int f = compile_list(q, 0); return f(); } +// compile the source string s and run it, returning its result. +int evals(int s) { + word q = tokens(s); int r = eval(q); + free(d*q); + return r; +} + +// ---- symbol blob ---- +// an image ends with its own symbol table so the genome knows its names: +// kind, value, signature, name chars, 0 ... then a 0 kind. +// emit the names defined since symbol index from (functions, globals, +// constants, enhancements) as the image's trailing symbol table +int blob_emit(int from) { + int i = from, table, words = 1; word p; int b, k; + while (i < nsym) { + if (skind[i] == K_DOC) { b = sval[i]; words = words + 2 + (b[0] >> 3); } + if ((skind[i] >= K_FN && skind[i] <= K_CONST) || skind[i] == K_ENH || skind[i] == K_DOC) words = words + 4 + len_w(sname[i]); + i++; + } + reserve_code(words); i = from; + // the docs first: each one's bytes go in the image here, and its symbol + // takes their address (the bytes come after the code, so no code moves) + while (i < nsym) { + if (skind[i] == K_DOC) { + b = sval[i]; + sval[i] = here; + emit(b[0]); + k = 0; + while (k < ((b[0] + 7) >> 3)) { emit(b[1 + k]); k++; } + if (!(b[0] & 7)) emit(0); + } + i++; + } + table = here; i = from; + while (i < nsym) { + if ((skind[i] >= K_FN && skind[i] <= K_CONST) || skind[i] == K_ENH || skind[i] == K_DOC) { + emit(skind[i]); emit(sval[i]); emit(ssig[i]); + p = sname[i]; + while (*p) { emit(*p); p++; } + emit(0); + } + i++; + } + emit(0); + return table; +} +// the reverse: read a blob and define its names, so a running genome knows its +// own functions by name +// hide: leave out the language server's names. functions are entered before +// constants and globals in the blob, so the server's functions come after +// chk_mark but the runtime's constants and globals come after it too: the +// cut is by name, the functions after chk_mark, and everything from the +// server's marker const (LSP_SERVER, the first thing src/lsp/server.zc declares) +int blob_load(word p, int hide) { + int s, n, past = 0, after = 0, skip, mkn = intern("chk_mark"), srvn = intern("LSP_SERVER"); + while (*p) { + n = intern(p + 3); + if (hide && n == srvn) after = 1; + skip = hide && (after || (past && p[0] == K_FN)); + if (hide && n == mkn) past = 1; + if (!skip) { + s = addsym(n, p[0], p[1]); + ssig[s] = p[2]; + sflag[s] = F_DEF | F_SIG; + if (p[0] == K_ENH) { if (p[1] >= nenh) nenh = p[1] + 1; install(p[2] >> 8); } + } + p = p + 3 + len_w(p + 3) + 1; + } + return 0; +} +// compile the rest of stdin for org into a growing output buffer. the image's +// addresses remain logical addresses at org while its temporary storage grows. +// image_words and image_size expose the finished image; initial is its first +// buffer reservation. the matching host layout must still fit the finished image. +int build_image_grow(int org, int initial) { + int sh = here, sd = hdelta, sl = hlimit, sub = ubase, svl = vislo, sns = nsym, sen = nenh, n; + word saved_buffer = image_buffer; int saved_active = image_active, saved_org = image_org, saved_capacity = image_capacity; + if (org < 0 || initial < 7 || initial > 0x7fffffff - org) fatal("bad image reservation"); + image_buffer = chk_resize(0, 0, initial); image_active = 1; image_org = org; image_capacity = initial; + ubase = nsym; vislo = nsym; nenh = 0; + here = org; hdelta = image_buffer - org; hlimit = org + initial; + lit(FTOP); lit(ZFP); op(O_ST); fnref(fwdfn(s_start)); op(O_JMP); emit(0); emit(0); + unit_stdin(); + n = blob_emit(ubase); + image_buffer[5] = n; + image_buffer[6] = here; + n = here - org; image_words = image_buffer; image_size = n; + popsyms(sns); + ubase = sub; vislo = svl; here = sh; hdelta = sd; hlimit = sl; nenh = sen; + image_buffer = saved_buffer; image_active = saved_active; image_org = saved_org; image_capacity = saved_capacity; + return n; +} +// retain the explicit destination interface for callers that already own storage. +// use build_image_grow and image_words when the output must grow independently. +int build_image(int org, int dst) { + int n = build_image_grow(org, 4096), words = syscall(9, 0, 0, 0); + if (dst < 0 || n > words - dst) fatal("image destination too small"); + copy_w(dst, image_words, n); + return n; +} + +// ---- setup ---- +// the collector's roots for the compiler's half of the memory map: the program +// area (globals live between functions), the genome's globals, the input source +// frames, the live frame stack and the live data stack. rt.zc declares this +// and calls it from gc(). +int gc_roots() { + int end = ptop; if (end > CODE_END) end = CODE_END; + markrange(0, end); + if (!hdelta && here > ptop && ptop < CODE_END) { end = here; if (end > CODE_END) end = CODE_END; markrange(ptop, end); } // globals of a unit still compiling + markrange(GORG, GORG[6]); + markrange(R_IH, R_POOLEND); + markrange(sval, sval + nsym); + markrange(R_TY, R_TYEND); + markrange(R_SRC, R_SRC + R_SRC_cap); + gc_frames(); + markrange(dsp(), data_top); + return 0; +} +// start-up in two parts. zc_init clears the intern table and interns the +// spellings the parser compares against; zc_tables fills the operator and +// keyword tables. an operator is a name, a precedence and a code sequence in +// the vm's own one-letter mnemonics (see seqasm), so "<=" is "~ < 1 ^": swap, +// less-than, flip the bit. +int zc_init() { + pooltop = R_POOL; + poolend = R_POOLEND; poolblocks = 0; namecount = 0; + fill_w(namehash, 0, hashcap); + nsym = 0; nfixu = 0; fixfree = 0; nbin = 0; nun = 0; nasg = 0; nkop = 0; seqtop = 0; + lsp = 0; ctxsp = 0; snaptop = 0; nloc = 0; lbase = 0; npm = 0; npe = 0; fill_w(lkeys, 0, lkeycap); lkeyn = 0; + s_lp = intern("("); s_rp = intern(")"); s_lb = intern("{"); s_rb = intern("}"); + s_lk = intern("["); s_rk = intern("]"); s_comma = intern(","); s_semi = intern(";"); + s_eq = intern("="); s_arrow = intern("->"); s_inc = intern("++"); s_dec = intern("--"); + s_star = intern("*"); + s_slash = intern("/"); + s_amp = intern("&"); + s_colon = intern(":"); + s_hash = intern("#"); + s_minus = intern("-"); + s_dot = intern("."); + s_q = intern("?"); + s_case = intern("case"); + s_default = intern("default"); + s_andand = intern("&&"); + s_oror = intern("||"); + s_fn = intern("fn"); s_var = intern("let"); s_const = intern("const"); s_else = intern("else"); + s_as = intern("as"); s_unchecked = intern("unchecked"); s_immediate = intern("immediate"); + s_m = intern("m"); s_d = intern("d"); s_u = intern("u"); s_static = intern("static"); + s_start = intern("start"); + s_main = intern("main"); + s_free = intern("free"); + s_recycle = intern("recycle"); + s_do = intern("do"); + s_while = intern("while"); + s_enum = intern("enum"); + s_include = intern("include"); + s_memdebug = intern("memdebug"); s_scrub = intern("scrub"); s_alloc = intern("alloc_line"); + s_check = intern("checkptr"); s_memset = intern("mem_set"); + s_own = intern("own"); s_qnew = intern("qnew"); s_fovf = intern("frame_overflow"); + s_frame_floor = intern("frame_floor"); s_frame_top = intern("frame_top"); s_frame_node = intern("frame_node"); + s_frame_busy = intern("frame_busy"); s_frame_guard = intern("frame_guard"); + s_frame_grow = intern("frame_grow"); s_frame_leave = intern("frame_leave"); + s_enhance = intern("enhance"); s_micro = intern("micro"); s_vm = intern("vm"); + return 0; +} +// operators and keywords. these are plain tables in memory: a program can +// add operators, change precedences or replace keywords with its own. +// (the order of the first binary operators matters: cexpr refers to * + - << >> +// & | by their index, and the comments mark which ones) +int zc_tables() { + add_unop("!", "0 $ 0 ~ - | 63 > 1 +"); // index 0 + add_unop("!!", "0 $ 0 ~ - | 63 > 0 ~ -"); // index 1: normalize to 0 or 1 + add_unop("~", "-1 ^"); // index 2 + add_unop("-", "0 ~ -"); + add_binop("*", 10, "*"); // the first few indexes are used by cexpr + add_binop("/", 10, "idiv"); // native divide, wired in by rt.zc + add_binop("%", 10, "imod"); + add_binop("+", 9, "+"); // 3 + add_binop("-", 9, "-"); // 4 + add_binop("<<", 8, "ishl"); // 5 + add_binop(">>", 8, ">"); // 6 + add_binop("<", 7, "<"); + add_binop(">", 7, "~ <"); + add_binop("<=", 7, "~ < 1 ^"); + add_binop(">=", 7, "< 1 ^"); + add_binop("==", 6, "- 0 $ 0 ~ - | 63 > 1 +"); + add_binop("!=", 6, "- 0 $ 0 ~ - | 63 > 0 ~ -"); + add_binop("&", 5, "&"); // 13 + add_binop("^", 4, "^"); + add_binop("|", 3, "|"); // 15 + add_binop("&&", -1, 0); + add_binop("||", -2, 0); + addkop(s_eq); addkop(s_arrow); addkop(s_inc); addkop(s_dec); + add_kw("let", kw_var, F_STMT); + add_kw("if", kw_if, F_STMT); + add_kw("while", kw_while, F_STMT); + add_kw("for", kw_for, F_STMT); + add_kw("do", kw_do, F_STMT); + add_kw("enum", kw_enum, F_STMT | F_TOP); + add_kw("include", kw_include, F_TOP); + add_kw("memdebug", kw_memdebug, F_STMT | F_TOP); + add_kw("switch", kw_switch, F_STMT); + add_kw("break", kw_break, F_STMT); + add_kw("continue", kw_continue, F_STMT); + add_kw("return", kw_return, F_STMT); + add_kw("unchecked", kw_unchecked, F_STMT); + add_kw("asm", kw_asm, F_STMT | F_EXPR); + add_kw("quote", kw_quote, F_EXPR); + add_kw("sizeof", kw_sizeof, F_EXPR); + add_kw("typeof", kw_typeof, F_EXPR); + add_kw("extern", kw_extern, F_TOP); + add_kw("cfunc", kw_cfunc, F_EXPR); + add_kw("o3", kw_o3, F_STMT); + add_kw("len", kw_len, F_EXPR); + add_kw("cap", kw_cap, F_EXPR); + add_kw("push", kw_push, F_EXPR); + add_kw("pop", kw_pop, F_EXPR); + add_kw("resize", kw_resize, F_EXPR); + return 0; +} +// the default map's type records sit in the last 16384 words of the heap. +// the type list is the first words of that range, so it cannot be kept aside +// as a buddy block: types_init would overwrite the header and the allocator +// would hand the range out again. cut the heap off at R_TY until the unit +// has been compiled, then put the tail back for the program. +int heldk; +// the size class of the type area, for the memory map. +int type_class() { + int k = 0, n = R_TYEND - R_TY; + while (k < 31 && pow2[k] < n) k++; + if (pow2[k] != n) return 0 - 1; + return k; +} +// keep the type area out of the collector's reach while it is in use. +int shield_types() { + int k; + heldk = 0 - 1; + if (R_TY <= HEAP_LO || R_TY >= HEAP_HI || R_TYEND > HEAP_HI) return 0; + k = type_class(); + if (k < 0) return 0; + if (R_TY + pow2[k] != R_TYEND) return 0; + if (heap_reset(R_TY)) heldk = k; + return 0; +} +// let the collector see the type area again. +int unshield_types() { + if (heldk >= 0) { heap_uncut(R_TY, heldk); heldk = 0 - 1; } + return 0; +} +// the genome's entry point, run when the image boots: set up the heap and +// the compiler, then either run the language server (a genome that carries it) +// or compile the unit on stdin, collect the garbage that compiling made, and +// call main(). +int start() { + int s, f; + heap_init(); chk_heap = 1; + shield_types(); + zc_init(); + blob_load(GORG[5], 0); + zc_tables(); + types_init(); + here = 0; hdelta = 0; hlimit = CODE_END; ptop = 0; + ubase = nsym; vislo = 0; + // a genome that carries the language server runs it instead of compiling stdin + s = lookup(intern("lsp_main")); + if (s >= 0 && skind[s] == K_FN && (sflag[s] & F_DEF)) { f = sval[s]; f(); halt(); } + unit_stdin(); + if (here > ptop) ptop = here; + scrub(); // compilation's stale words would keep quarantined blocks alive + gc(); + // the type area stays out of the allocator. main() may compile again + // (the self-host does) and a later new() would otherwise reuse it. + if (mdbg_flags) mem_set(mdbg_flags); + // a unit that carries src/lib/std.zc has a libinit to fill argc/argv/envc/envp + // with. it runs before main, the same way lsp_main runs instead of stdin. + s = lookup(intern("libinit")); + if (s >= 0 && skind[s] == K_FN && (sflag[s] & F_DEF)) { f = sval[s]; f(); } + s = lookup(s_main); + if (s >= 0 && skind[s] == K_FN && (sflag[s] & F_DEF)) { f = sval[s]; f(); } + halt(); + return 0; +} + +// ---- check mode: the language server ---- +// the server runs this compiler, unchanged, over a document it holds in +// memory. chk_unit compiles it as a whole unit, in a machine set up the way +// start() sets one up (the genome's own names, the default operators and +// keywords), so the document's parse words, operators and types change what +// the rest of it means exactly as they would in a real build. the symbols +// the document made stay in the tables afterwards for the highlighter. +// chk_mark is the last function of the compiler proper, and LSP_SERVER (in +// src/lsp/server.zc) the first constant of the server: the server's names are +// hidden from the document, the runtime's and the compiler's are not. +int chk_mark() { return 0; } +// reset check mode for a new document. +int chk_reset() { + code_blocks = 0; code_chunk = CODE_END; + isp = -1; + docs_on = 0; dn = 0; dline = 0; docpending = 0; dsn = 0; dnm = 0; + pq_n = 0; + pendot = 0; + qdone = 0; + ch = 0; + chkind = 0; + line = 1; + tk = 0; + tv = 0; + tglue = 0; + tline = 1; + toff = 0 - 1; toklen = 0; tpend = 0 - 1; tpline = 1; + infn = 0; + fdepth = 0; + fslots = 0; + fchk = 0; + fret = 0; + live = 0; + bxdepth = 0; + epichain = 0; + fframe = 0; + xbor = 0 - 1; xaf = 0; argfact = 0; + mdbg = 0; mdbg_flags = 0; lv = 0; lvk = 0; lvi = 0; kw_expr = 0; xcls = 0; fty = 0; + check_on = 1; tc_on = ifstrict; dry = 0; ntp = 0; tvauto = 0; ngbind = 0; xgive = 0; xconst = 0; + o3on = 0; o3nxt = 0; no3pend = 0; no3sl = 0; no3free = 0; o3nreg = 0; o3na = 0; o3fn0 = 0; + nfact = 0; fortest = 0; forstep = 0; argloc = 0; arrloc = 0; njps = 0; extop = 0; nnumops = 0; + nenh = 0; panic = 0; nerr = 0; dgprev = 0; nclo = 0; newsz = 0 - 1; nomagic = 0; callpos = 0; + grec = 0; gnew = 0 - 1; gt_n = 0; gi_n = 0; ginst = 0; gtab = 0; + return 0; +} +// src: the document, 4 chars to a word and a 0 at the end. buf, cap: where +// the records go. sets: 4 * LSW words for the name sets. lim: code may grow up +// to here. every block the caller holds is a heap object reachable from its +// globals, so the check's garbage is collected and nothing is cut off. +// check one document in check mode and fill the records it made. +int chk_unit(int src, int buf, int cap, int sets, int lim) { + chk_reset(); + chk_begin(buf, cap); + lsets = sets; + if (sets) fill_w(sets, 0, 4 * LSW); + if (!chk_heap) { heap_init(); chk_heap = 1; } else gc(); // the caller's blocks are reachable from its globals, the rest goes + rsn = 0; rsbase = src; + if (!rsrec) { rscap = RS_CAP; rsrec = new(RS_CAP * RS_W); rstmp = new(RS_CAP * RS_W); } + ifs = 0; ifx = 0; nifx = 0; nift = 0; nif = 0; ifcur = 0; ifkk = 0; ifgk = 0; ifcall = 0 - 1; + if (ifon) { + ifrcap = NIF; ifxcap = IFN; iftcap = IFT; + ifs = new(symcap); ifr = chk_resize(0, 0, ifrcap); ifx = chk_resize(0, 0, ifxcap * 4); ift = chk_resize(0, 0, iftcap); + fill_w(ifs, 0, symcap); + } + zc_init(); + blob_load(GORG[5], 1); + zc_tables(); + types_init(); + here = 0; hdelta = 0; hlimit = lim; ptop = 0; + ubase = nsym; vislo = 0; + prescan(3, src); + src_push(3, src, 0, 0); + next(); + unit_top(); + rs_fix(); + unit_end(ubase); + if (ifx) if_emit(); + src_pop(); + rs_sort(); + chk_end(); + return 0; +} +// walk a document's tokens with the lexer as the check left it. hl_begin +// loads the first token into tk, tv, toff and toklen; next() moves on; hl_end +// restores the previous state. +// the syntax highlighter's view of the lexer. it re-lexes the document after +// the check, so the tokens it sees are cut exactly where the compiler's were +// (parse words that changed the operators included). +int hl_begin(int src) { + chkabort = 0; isp = -1; pq_n = 0; pendot = 0; qdone = 0; panic = 0; + dry++; + src_push(3, src, 0, 0); + next(); + return 0; +} +// end a highlight pass: leave the source and the dry run. +int hl_end() { + src_pop(); + dry--; + isp = -1; + pq_n = 0; + return 0; +} diff --git a/src/core/bops.h b/src/core/bops.h new file mode 100644 index 0000000..a7ef0d9 --- /dev/null +++ b/src/core/bops.h @@ -0,0 +1,87 @@ +/* little-endian bytes of a word-addressed cell array. + byte address b names bits [8b, 8b+8) of the flat image: word b>>3, + low byte first. a byte address is an ordinary integer. the odd/even + tag is only for code words, and ownership tags stay on word pointers. + wider loads and stores are little-endian and may cross a word. an + access that does not lie entirely inside the image is refused. */ +#ifndef BOPS_H +#define BOPS_H +#include + +static int bop_ok(int64_t addr, int n, int64_t nwords) +{ + uint64_t bytes, a, k; + if (n <= 0 || n > 8 || addr < 0 || nwords < 1) return 0; + bytes = (uint64_t)nwords << 3; + a = (uint64_t)addr; + k = (uint64_t)n; + if (a >= bytes || k > bytes - a) return 0; + return 1; +} + +static int64_t bop_load(const int64_t *mem, int64_t addr, int n) +{ + int64_t v = 0; + int i; + for (i = 0; i < n; i++) { + int64_t a = addr + i; + int sh = (int)((a & 7) << 3); + v |= (int64_t)(((uint64_t)mem[a >> 3] >> sh) & 255) << (i * 8); + } + return v; +} + +/* fold n bytes of val into the words they touch. *w0 is the first word. + returns how many words changed (1 or 2). does not write mem. */ +static int bop_merge(const int64_t *mem, int64_t addr, int64_t val, int n, int64_t *out, int64_t *w0) +{ + int i, nw; + int64_t base = addr >> 3; + nw = (int)(((addr + n - 1) >> 3) - base) + 1; + *w0 = base; + out[0] = mem[base]; + if (nw > 1) out[1] = mem[base + 1]; + for (i = 0; i < n; i++) { + int64_t a = addr + i; + int wi = (int)((a >> 3) - base); + int sh = (int)((a & 7) << 3); + uint64_t mask = (uint64_t)255 << sh; + uint64_t byte = ((uint64_t)val >> (i * 8)) & 255; + out[wi] = (int64_t)(((uint64_t)out[wi] & ~mask) | (byte << sh)); + } + return nw; +} + +static void bop_put1(int64_t *mem, int64_t addr, int byte) +{ + int64_t out[2], w0; + int n, i; + n = bop_merge(mem, addr, byte & 255, 1, out, &w0); + for (i = 0; i < n; i++) mem[w0 + i] = out[i]; +} + +/* copy a NUL-terminated byte string into dst. returns the length, or -1 + when the address is outside the image. */ +static int bop_cstr(const int64_t *mem, int64_t addr, char *dst, int max, int64_t nwords) +{ + int i = 0, c; + if (max < 1) return -1; + if (!bop_ok(addr, 1, nwords)) { dst[0] = 0; return -1; } + while (i < max - 1 && bop_ok(addr + i, 1, nwords)) { + c = (int)bop_load(mem, addr + i, 1); + if (!c) break; + dst[i++] = (char)c; + } + dst[i] = 0; + return i; +} + +/* how many of the next n bytes, starting at addr, lie inside the image */ +static int bop_span(int64_t addr, int n, int64_t nwords) +{ + int i; + if (n <= 0) return 0; + for (i = 0; i < n; i++) if (!bop_ok(addr + i, 1, nwords)) return i; + return n; +} +#endif diff --git a/src/core/core.c b/src/core/core.c new file mode 100644 index 0000000..2f6032a --- /dev/null +++ b/src/core/core.c @@ -0,0 +1,5889 @@ +/* zygote core: puts the genome (the zc compiler and its runtime, both vm code) + into memory and runs it. on arm64, every block of vm code is translated to + arm64 the first time it is reached, straight from live memory. stores that + land on translated words throw those translations away, so code that + rewrites itself, including the resident compiler, just gets retranslated + on the next visit. on other hosts (or with -DZYINTERP) a plain interpreter + with the same semantics runs instead. ZYTRACE=1 logs translations on stderr. */ +#include +#include +#include +#include +#include +#include +#ifdef _WIN32 +#include +#include +#ifndef O_BINARY +#define O_BINARY _O_BINARY +#endif +#if defined(_MSC_VER) +#define open _open +#define close _close +#define read _read +#define write _write +#endif +#else +#include +#ifndef O_BINARY +#define O_BINARY 0 +#endif +#endif +#include +#if (defined(__linux__) || defined(__APPLE__)) && !defined(__wasm__) && !defined(ZY_NOFFI) +#define ZYFFI 1 +#include +#endif +#if defined(__aarch64__) && !defined(ZYINTERP) +#define ZYJIT 1 +#include +#ifdef __APPLE__ +#include +#include +#endif +#endif +#if defined(__linux__) && defined(__GLIBC__) && !defined(__wasm__) && !defined(ZYJIT) && !defined(ZY_NOBIGSTACK) +#define ZYBIGSTACK 1 +#include +#include +#endif +#ifdef USE_LSP_GENOME +#include "lsp_genome.h" +#elif defined(GENOME_H) +#include GENOME_H +#else +#include "genome.h" +#endif +#include "memmap.h" +#include "fops.h" +#include "bops.h" + +#ifndef JTYPE +#define JTYPE char +#endif +char Q(void); +void G(char); +extern JTYPE J; + +typedef int64_t Cell; /* one vm word. memory stays word-addressed. */ +typedef uint64_t UCell; +enum { DEFAULT_MEMORY_LIMIT = MW }; +static int memory_limit = DEFAULT_MEMORY_LIMIT; +static int memory_words; +static int memory_growth_limit; +/* bounds baked into native blocks use the reservation, whose base never moves. */ +#undef MW +#define MW memory_limit +/* MW, the stacks and SYSGATE come from memmap.h. the default is 1<<20. */ +/* stop values stay outside every executable address reservation. */ +#define PC_HALT 0x7ffffff0 +#define PC_DATA_OVERFLOW (PC_HALT + 1) +#define PC_RETURN_OVERFLOW (PC_HALT + 2) +#define PC_FRAME_OVERFLOW (PC_HALT + 3) +static Cell code_words; +static int code_reserve(Cell end); +static int code_range_live(Cell start, Cell count); +/* why the cpu stopped, when it wasn't a halt */ +static int stopped(int p) +{ + if (p == PC_DATA_OVERFLOW) { fprintf(stderr, "zy: data stack overflow\n"); return 2; } + if (p == PC_RETURN_OVERFLOW) { fprintf(stderr, "zy: return stack overflow\n"); return 2; } + if (p == PC_FRAME_OVERFLOW) { fprintf(stderr, "zy: frame stack overflow\n"); return 2; } + return 0; +} +#define CODEW (4 << 20) /* 16 MB of code, in instruction words */ +#define MAXB 65536 +#define BLKW 1024 /* longest block, in vm words */ + +static Cell *m; +static Cell frame_floor_word = ZFP + 6, frame_top_word = ZFP + 7, frame_guard_word = ZFP + 8; +static Cell frame_bound(void) { return m[frame_floor_word] + m[frame_guard_word]; } + +static int b_ok(Cell addr, int n) { return bop_ok(addr, n, MW); } +static Cell b_load(Cell addr, int n) +{ + if (!b_ok(addr, n)) return 0; + return bop_load(m, addr, n); +} +static void b_commit(Cell w, Cell v); +static void b_store(Cell addr, Cell val, int n) +{ + Cell out[2], w0; + int nw, i; + if (!b_ok(addr, n)) return; + nw = bop_merge(m, addr, val, n, out, &w0); + for (i = 0; i < nw; i++) b_commit(w0 + i, out[i]); +} +static Cell bld8(Cell a) { return b_load(a, 1); } +static Cell bld16(Cell a) { return b_load(a, 2); } +static Cell bld32(Cell a) { return b_load(a, 4); } +static void bst8(Cell a, Cell v) { b_store(a, v, 1); } +static void bst16(Cell a, Cell v) { b_store(a, v, 2); } +static void bst32(Cell a, Cell v) { b_store(a, v, 4); } + +static void fop_print(Cell bits) +{ + char buf[400]; + int n = fop_format((uint64_t)bits, buf), i; + for (i = 0; i < n; i++) G(buf[i]); +} +/* opcode 618: (bits prec mode --). mode 0 shortest, 1 fixed, 2 scientific. */ +static void fop_print_mode(Cell bits, Cell prec, Cell mode) +{ + char buf[400]; + int n = fop_format_mode((uint64_t)bits, mode, prec, buf), i; + for (i = 0; i < n; i++) G(buf[i]); +} +static Cell fop_parse_cell(Cell addr) +{ + if ((UCell)addr >= (UCell)MW) return 0; + return (Cell)fop_parse_words(m + addr, (UCell)MW - (UCell)addr); +} +static int trace; + +/* the command line and the environment, kept for ops 21 to 24. a program read + from stdin has no file name, so argv[0] is , as in zy(1). the rest of + the host's argv follows it. the environment is a bare char **, so its count + is counted once at startup. */ +#ifndef _WIN32 +extern char **environ; +#endif +static int zy_argc; +static char **zy_argv; +static int zy_envc; +static char **zy_envp; + +static void zy_args(int argc, char **argv) +{ + char **e; + int n = 0; +#ifdef _WIN32 + e = _environ; +#else + e = environ; +#endif + while (e && e[n]) n++; + zy_envp = e; zy_envc = n; + zy_argv = argv; + zy_argc = argc > 0 ? argc : 1; +} + +/* syscall gate: a store of the op to SYSGATE runs hostcall. args sit in the + next three words, and the result replaces the op. see rt.zc. */ +static int zcstr(int addr, char *dst, int max) +{ + int i = 0, c; + if (max < 1) return -1; + if ((unsigned)addr >= MW) { dst[0] = 0; return -1; } + while (i < max - 1 && (unsigned)(addr + i) < MW) { + c = (int)m[addr + i]; + if (!c) break; + dst[i++] = (char)c; + } + dst[i] = 0; + return i; +} +static int inmem(Cell a) { return (UCell)a < (UCell)MW; } +static int stack_memory_grow(UCell words) +{ + if (words > (UCell)memory_growth_limit) return 0; + if (words > (UCell)memory_words) memory_words = (int)words; + return 1; +} +#include "stack.h" + +/* copy entry a of a char ** into vm words, one character per word, terminated + like SYS_GETENV. a zero buffer asks for the length only, which is how the + caller sizes the string before fetching it. lead, when given, replaces entry + zero: a program read from stdin has no file name, so argv[0] is . */ +static Cell vecstr(char **v, int count, Cell a, Cell b, Cell c, const char *lead) +{ + char *e; + int i = 0; + if (a < 0 || a >= (Cell)count) return -1; + e = (a == 0 && lead) ? (char *)lead : v[a]; + if (!e) return -1; + if (!b) { while (e[i]) i++; return i; } + if (c < 1 || !inmem(b)) return -1; + while (e[i] && i < c - 1 && inmem(b + i)) { m[b + i] = (unsigned char)e[i]; i++; } + if (inmem(b + i)) m[b + i] = 0; + return i; +} +static Cell host_rand(void) +{ + static uint64_t s; + if (!s) s = (uint64_t)time(0) ^ 0x9E3779B97F4A7C15ull; + if (!s) s = 0x9E3779B97F4A7C15ull; + s ^= s << 13; s ^= s >> 7; s ^= s << 17; + if ((Cell)s == -1) s ^= 0x100; + return (Cell)s; +} +/* c abi: ops 14..18 of the gate. see the ffi section further down. cb_s and + cb_r are the vm stack pointers as of the gate store, so that a callback from + native code can run vm code on top of them. */ +static Cell cb_s, cb_r; +static Cell ffi_call(Cell blk); +static Cell ffi_find(Cell name); +static Cell ffi_cbmake(Cell entry, Cell sig); +static Cell ffi_load(Cell name); +static Cell hostcall(Cell op, Cell a, Cell b, Cell c) +{ + char path[1024], tmp[512]; + int n, i, got; +#ifdef ZY_SANDBOX + /* a build for checking code the user has not run yet (the language server): compile-time + code may read what it includes, but it may not write files, exit, load or call C code, + or make raw system calls */ + if (op == 4 || op == 7 || op == 12 || (op >= 14 && op <= 17) || op == 40) return -1; + if ((op == 1 || op == 10) && b != 0) return -1; +#endif + switch (op) { + case 1: + if (zcstr(a, path, 1024) < 0) return -1; + if (b == 1) return open(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644); + return open(path, O_RDONLY | O_BINARY); + case 2: + return close((int)a); + case 3: + if (c < 0 || !inmem(b)) return -1; + n = 0; + while (n < c) { + i = c - n; + if (i > 512) i = 512; + if (!inmem(b + n)) break; + if (!inmem(b + n + i - 1)) i = (int)(MW - (b + n)); + if (i <= 0) break; + got = (int)read((int)a, tmp, (size_t)i); + if (got <= 0) break; + for (i = 0; i < got; i++) m[b + n + i] = (unsigned char)tmp[i]; + n += got; + } + return n; + case 4: + if (c < 0 || !inmem(b)) return -1; + n = 0; + while (n < c) { + i = c - n; + if (i > 512) i = 512; + if (!inmem(b + n)) break; + if (!inmem(b + n + i - 1)) i = (int)(MW - (b + n)); + if (i <= 0) break; + for (got = 0; got < i; got++) tmp[got] = (char)m[b + n + got]; + got = (int)write((int)a, tmp, (size_t)i); + if (got <= 0) break; + n += got; + } + return n; + case 5: { + char *e; + if (zcstr(a, path, 1024) < 0) return -1; + e = getenv(path); + if (!e) return -1; + if (c < 1 || !inmem(b)) return -1; + i = 0; + while (e[i] && i < c - 1 && inmem(b + i)) { m[b + i] = (unsigned char)e[i]; i++; } + if (inmem(b + i)) m[b + i] = 0; + return i; + } + case 6: + return (Cell)time(0); + case 7: + exit((int)a); + case 8: + return host_rand(); + case 9: + if (a == 0) return memory_words; + if (a == 1) return HEAP_LO; + if (a == 2) return HEAP_HI; + if (a == 3) return (Cell)memory_words * 8; + if (a == 4) { + if (b < 0 || b > memory_growth_limit) return -1; + if (b > memory_words) memory_words = (int)b; + return memory_words; + } + if (a == 5) { + if (b < memory_words || b > MW) return -1; + memory_growth_limit = (int)b; return b; + } + if (a == 6) { + if (!stack_register(b)) return -1; + frame_floor_word = m[b]; frame_top_word = m[b + 1]; frame_guard_word = m[b + 2]; + return 0; + } + if (a == 7 || a == 8) return stack_expand(a == 8, b, &cb_s, &cb_r); + if (a == 9) return code_reserve(b); + return -1; + case 10: + if (bop_cstr(m, a, path, 1024, MW) < 0) return -1; + if (b == 1) return open(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644); + return open(path, O_RDONLY | O_BINARY); + case 11: + if (c < 0 || (c > 0 && !bop_ok(b, 1, MW))) return -1; + n = 0; + while (n < c) { + i = (int)(c - n); + if (i > 512) i = 512; + i = bop_span(b + n, i, MW); + if (i <= 0) break; + got = (int)read((int)a, tmp, (size_t)i); + if (got <= 0) break; + for (i = 0; i < got; i++) bop_put1(m, b + n + i, (unsigned char)tmp[i]); + n += got; + } + return n; + case 12: + if (c < 0 || (c > 0 && !bop_ok(b, 1, MW))) return -1; + n = 0; + while (n < c) { + i = (int)(c - n); + if (i > 512) i = 512; + i = bop_span(b + n, i, MW); + if (i <= 0) break; + for (got = 0; got < i; got++) tmp[got] = (char)bop_load(m, b + n + got, 1); + got = (int)write((int)a, tmp, (size_t)i); + if (got <= 0) break; + n += got; + } + return n; + case 13: { + char *e; + if (bop_cstr(m, a, path, 1024, MW) < 0) return -1; + e = getenv(path); + if (!e) return -1; + if (c < 1 || !bop_ok(b, 1, MW)) return -1; + i = 0; + while (e[i] && i < c - 1 && bop_ok(b + i, 1, MW)) { bop_put1(m, b + i, (unsigned char)e[i]); i++; } + if (bop_ok(b + i, 1, MW)) bop_put1(m, b + i, 0); + return i; + } + case 14: return ffi_call(a); + case 15: return ffi_find(a); + case 16: return ffi_cbmake(a, b); + case 17: return ffi_load(a); + case 18: { /* fill words: a = dst, b = value, c = count. above the program area only + (no translation covers it), and -1 means "do it yourself" */ + Cell i; + if (a < PW || code_range_live(a, c) || c <= 0 || c > MW - a) return -1; + for (i = 0; i < c; i++) m[a + i] = b; + return c; } + case 19: /* copy words: a = dst, b = src, c = count. a forward copy that overlaps + itself (dst inside the source) is left to the caller's loop */ + if (a < PW || code_range_live(a, c) || b < 0 || c <= 0 || c > MW - a || c > MW - b) return -1; + if (a > b && a < b + c) return -1; + memmove(m + a, m + b, (size_t)c * sizeof(Cell)); + return c; + case 20: { /* scan for heap pointers: a = first word, b = count, c = hbump. the index of + the first word that could be the payload address of an allocated block (a + heap address whose check word at w[-3] is w ^ MAGIC), or b if there is none. + the allocator's header is 5 words and its magic 0x2B7E1516 (src/runtime/rt.zc); the + caller still checks the flags, this only skips the words that are plainly + not pointers. -1 if the range is not in memory */ + Cell i, w, lim = c > MW ? MW : c; + if (a < 0 || b <= 0 || b > MW - a) return -1; + for (i = 0; i < b; i++) { + w = m[a + i]; + if (w >= HEAP_LO + 5 && w < lim && m[w - 3] == (w ^ 0x2B7E1516)) return i; + } + return b; } + case 21: /* argc: the host's argument count, or 1 for alone */ + return (Cell)zy_argc; + case 22: /* argv: a = index, b = buffer, c = capacity. a zero buffer asks for the + length only, which is how the caller sizes the string before it fetches + it. the string is copied one character per word and terminated, like + SYS_GETENV. -1 if there is no such argument */ + return vecstr(zy_argv, zy_argc, a, b, c, ""); + case 23: /* envc: how many entries the environment has */ + return (Cell)zy_envc; + case 24: /* envp: one entry of the environment, shaped like argv */ + return vecstr(zy_envp, zy_envc, a, b, c, 0); + case 40: /* raw host syscall: a = address of seven words, the syscall number and then its six arguments. the result is the kernel's, or -errno. linux only; elsewhere -1 */ +#ifdef __linux__ + { + long w[7], r; + int k; + if (!inmem(a) || !inmem(a + 6)) return -1; + for (k = 0; k < 7; k++) w[k] = (long)m[a + k]; + r = syscall(w[0], w[1], w[2], w[3], w[4], w[5], w[6]); + if (r == -1) return -(Cell)errno; + return (Cell)r; + } +#else + return -1; +#endif + default: + return -1; + } +} + +/* ---- virtual enhancements ---- + up to 512 custom opcodes (22 .. 533), wired in at runtime by the install + opcode (21). an enhancement is a short program over native micro-ops, + written either as a recipe (text) or as straight-line vm code that gets + lifted. both become ssa, go through the optimizer below, and then run in + the interpreter or get inlined by the jit as native code. */ +#define NENH 512 +#define XOP0 22 +enum { U_LI, U_MOV, U_ADD, U_SUB, U_MUL, U_SDIV, U_UDIV, U_SREM, U_UREM, U_AND, U_OR, U_XOR, + U_SHL, U_SHR, U_SAR, U_NEG, U_NOT, U_LT, U_LTU, U_EQ, U_NE, U_MIN, U_MAX, U_MINU, U_MAXU, + U_MULHS, U_MULHU, U_CLZ, U_ABS, U_SEL, U_LD, U_ST, + U_FADD, U_FSUB, U_FMUL, U_FDIV, U_FEQ, U_FLT, U_FLE, U_I2F, U_F2I, + U_LD8, U_LD16, U_LD32, U_ST8, U_ST16, U_ST32, + U_NARG, U_NCALL, /* a native call is one narg per argument (imm = its position) and then the ncall (imm = the call's descriptor) */ + U_CTZ, U_POPCNT, U_BSWAP, U_ROTL, U_ROTR, /* bit tricks: count trailing zeros, population count, byte swap, rotates */ + U_F2S, U_S2F, /* binary64 bits to binary32 bits (rounded) and back (exact) */ + U_N }; +static const char *unames[U_N] = { "li", "mov", "add", "sub", "mul", "sdiv", "udiv", "srem", "urem", + "and", "or", "xor", "shl", "shr", "sar", "neg", "not", "lt", "ltu", "eq", "ne", "min", "max", + "minu", "maxu", "mulhs", "mulhu", "clz", "abs", "sel", "ld", "st", + "fadd", "fsub", "fmul", "fdiv", "feq", "flt", "fle", "i2f", "f2i", + "ld8", "ld16", "ld32", "st8", "st16", "st32", "narg", "ncall", + "ctz", "popcnt", "bswap", "rotl", "rotr", "f2s", "s2f" }; +static const signed char uarity[U_N] = { 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 1, 3, 1, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 2, 2, 2, 1, 0, 1, 1, 1, 2, 2, 1, 1 }; +static int is_store(int op) +{ + return op == U_ST || op == U_ST8 || op == U_ST16 || op == U_ST32; +} +/* ops that matter even when nothing reads their value: a store, an argument + handed to a native call, the call. none of them has a result a later pass + may reuse or drop */ +static int is_eff(int op) +{ + return is_store(op) || op == U_NARG || op == U_NCALL; +} +static int is_memop(int op) +{ + return is_eff(op) || op == U_LD || op == U_LD8 || op == U_LD16 || op == U_LD32; +} +static int bwidth(int op) +{ + if (op == U_LD8 || op == U_ST8) return 1; + if (op == U_LD16 || op == U_ST16) return 2; + if (op == U_LD32 || op == U_ST32) return 4; + return 0; +} +typedef struct { int op, d, a, b, c; Cell imm; } Uop; /* ssa: d is a fresh value, a b c are values */ +typedef struct { + int live, kind, nin, nout, n, nv, out[8]; + Uop *u; + signed char *reg; /* physical register per value, for the jit; 0 if it didn't fit */ +} Enh; +static Enh enh[NENH]; +static int need_flush; + +#define MAXUOP 4096 +static Uop ub[MAXUOP]; static int un, unv, uerr; /* the program being built */ +static int unew(int op, int a, int b, int c, Cell imm) +{ + Uop *u; + if (un >= MAXUOP) { uerr = -5; return 0; } + u = &ub[un++]; u->op = op; u->d = unv++; u->a = a; u->b = b; u->c = c; u->imm = imm; + return u->d; +} +static Cell mread(int a) { return (unsigned)a < MW ? m[a] : 0; } + +/* recipe text: one statement per line or per ';' + r0 = sdiv r0 r1 dst = op operands, operands are rN, parameter names or numbers + r2 = r0 a copy, or a number + st r1 r0 memory[r1] = r0 + inputs arrive in r0.. (and under the parameter names), outputs leave from r0.. */ +static int parse_recipe(int src, int names, int nin, int nout, int *out) +{ + int cur[16], i, p = src, n, k, op, args[3], na, dst, isst; + char tok[8][32]; int nt; + for (i = 0; i < 16; i++) cur[i] = i < nin ? i : -1; + for (;;) { + /* read one statement into tokens */ + nt = 0; + for (;;) { + int c = mread(p); + while (c == ' ' || c == '\t' || c == '\r') c = mread(++p); + if (!c || c == '\n' || c == ';') break; + if (c == '=') { if (nt < 8) strcpy(tok[nt++], "="); p++; continue; } + n = 0; + while (c && c != ' ' && c != '\t' && c != '\n' && c != ';' && c != '=' && c != '\r') { if (n < 31) tok[nt < 8 ? nt : 7][n++] = (char)c; c = mread(++p); } + if (nt < 8) tok[nt++][n] = 0; + } + if (nt) { + isst = !strcmp(tok[0], "st"); + if (!isst && (nt < 3 || strcmp(tok[1], "="))) return -2; + /* destination register */ + dst = -1; + if (!isst) { + if (tok[0][0] == 'r' && tok[0][1] >= '0' && tok[0][1] <= '9') dst = atoi(tok[0] + 1); + else for (i = 0; i < nin && names; i++) { + int s = mread(names + i), j = 0; + while (mread(s + j) && mread(s + j) == (unsigned char)tok[0][j]) j++; + if (!mread(s + j) && !tok[0][j]) dst = i; + } + if (dst < 0 || dst > 15) return -2; + } + k = isst ? 1 : 2; /* first token after "=" (or after st) */ + op = -1; + if (!isst && nt > 3) { for (i = 0; i < U_N; i++) if (!strcmp(tok[k], unames[i])) op = i; if (op < 0) return -2; k++; } + if (isst) op = U_ST; + na = 0; + for (; k < nt; k++) { + char *t = tok[k]; int v = -1; + if (na >= 3) return -2; + if (t[0] == 'r' && t[1] >= '0' && t[1] <= '9') { int r = atoi(t + 1); if (r > 15) return -2; if (cur[r] < 0) cur[r] = unew(U_LI, 0, 0, 0, 0); v = cur[r]; } + else if ((t[0] >= '0' && t[0] <= '9') || t[0] == '-') { char *e; Cell x = (Cell)strtoll(t, &e, 0); if (*e) return -2; v = unew(U_LI, 0, 0, 0, x); } + else { + for (i = 0; i < nin && names; i++) { + int s = mread(names + i), j = 0; + while (mread(s + j) && mread(s + j) == (unsigned char)t[j]) j++; + if (!mread(s + j) && !t[j]) v = cur[i] < 0 ? (cur[i] = unew(U_LI, 0, 0, 0, 0)) : cur[i]; + } + if (v < 0) return -2; + } + args[na++] = v; + } + if (op < 0) { /* x = y */ + if (na != 1) return -2; + cur[dst] = args[0]; + } else { + if (na != (op == U_LI ? 0 : uarity[op])) { + if (!(op == U_LI && na == 1)) return -2; + } + if (op == U_LI) cur[dst] = args[0]; + else if (is_store(op)) unew(op, args[0], args[1], 0, 0); + else cur[dst] = unew(op, args[0], na > 1 ? args[1] : 0, na > 2 ? args[2] : 0, 0); + } + if (uerr) return uerr; + } + if (!mread(p)) break; + p++; + } + for (i = 0; i < nout; i++) { if (cur[i] < 0) cur[i] = unew(U_LI, 0, 0, 0, 0); out[i] = cur[i]; } + return uerr; +} + +/* straight-line vm code from addr up to its ret, run symbolically */ +static int lift_vm(int a, int len, int nin, int nout, int *out) +{ + int st[256], sp = 0, i, k, x, y; Cell w; + for (i = 0; i < nin; i++) st[sp++] = i; + for (i = 0; i < len; i++) { + w = mread(a + i); + if (w & 1) { if (sp >= 256) return -4; st[sp++] = unew(U_LI, 0, 0, 0, w >> 1); continue; } + k = w / 2; + if (k == 9) break; + switch (k) { + case 1: if (sp < 1) return -4; st[sp - 1] = unew(U_LD, st[sp - 1], 0, 0, 0); break; + case 2: if (sp < 2) return -4; unew(U_ST, st[sp - 1], st[sp - 2], 0, 0); sp -= 2; break; + case 3: /* pick needs a constant index */ + if (sp < 1) return -4; + x = st[sp - 1]; + if (x < nin) return -4; + { int j; for (j = 0; j < un && ub[j].d != x; j++); if (j == un || ub[j].op != U_LI) return -4; y = (int)ub[j].imm; } + if (y < 0 || y >= sp - 1) return -4; + st[sp - 1] = st[sp - 2 - y]; + break; + case 4: if (sp < 1) return -4; sp--; break; + case 5: if (sp < 2) return -4; x = st[sp - 1]; st[sp - 1] = st[sp - 2]; st[sp - 2] = x; break; + case 13: case 14: case 15: case 16: case 17: case 18: case 19: case 20: { + static const int map[] = { U_ADD, U_SUB, U_MUL, U_AND, U_OR, U_XOR, U_SAR, U_LT }; + if (sp < 2) return -4; + x = st[sp - 1]; y = st[sp - 2]; sp -= 2; + st[sp++] = unew(map[k - 13], y, x, 0, 0); + break; } + default: return -4; /* control flow, i/o and other enhancements don't lift */ + } + if (uerr) return uerr; + } + if (sp != nout) return -4; + for (i = 0; i < nout; i++) out[i] = st[i]; + return uerr; +} + +/* ---- the optimizer ---- + one forward pass of copy propagation, constant folding, algebraic + simplification, division by constants (the multiply-high trick) and + common subexpression elimination, then dead code removal. */ +static Cell ev(int op, Cell a, Cell b, Cell c) +{ + UCell ua = (UCell)a, ub_ = (UCell)b; + switch (op) { + case U_ADD: return (Cell)(ua + ub_); + case U_SUB: return (Cell)(ua - ub_); + case U_MUL: return (Cell)(ua * ub_); + case U_SDIV: return b == 0 ? 0 : (a == INT64_MIN && b == -1) ? a : a / b; + case U_UDIV: return b == 0 ? 0 : (Cell)(ua / ub_); + case U_SREM: return b == 0 ? a : (a == INT64_MIN && b == -1) ? 0 : a % b; + case U_UREM: return b == 0 ? a : (Cell)(ua % ub_); + case U_AND: return a & b; + case U_OR: return a | b; + case U_XOR: return a ^ b; + case U_SHL: return (Cell)(ua << (b & 63)); + case U_SHR: return (Cell)(ua >> (b & 63)); + case U_SAR: return a >> (b & 63); + case U_NEG: return (Cell)(0ull - ua); + case U_NOT: return ~a; + case U_LT: return a < b; + case U_LTU: return ua < ub_; + case U_EQ: return a == b; + case U_NE: return a != b; + case U_MIN: return a < b ? a : b; + case U_MAX: return a > b ? a : b; + case U_MINU: return ua < ub_ ? a : b; + case U_MAXU: return ua > ub_ ? a : b; + case U_MULHS: return (Cell)(((__int128)a * (__int128)b) >> 64); + case U_MULHU: return (Cell)(((unsigned __int128)ua * ub_) >> 64); + case U_CLZ: { int n = 0; if (!ua) return 64; while (!(ua & (1ull << 63))) { ua <<= 1; n++; } return n; } + case U_ABS: return a < 0 ? (Cell)(0ull - ua) : a; + case U_CTZ: return ua ? __builtin_ctzll(ua) : 64; + case U_POPCNT: return __builtin_popcountll(ua); + case U_BSWAP: return (Cell)__builtin_bswap64(ua); + case U_ROTL: return (Cell)((ua << (b & 63)) | (ua >> ((64 - (b & 63)) & 63))); + case U_ROTR: return (Cell)((ua >> (b & 63)) | (ua << ((64 - (b & 63)) & 63))); + case U_F2S: return (Cell)fop_f2s((uint64_t)a); + case U_S2F: return (Cell)fop_s2f((uint64_t)a); + case U_SEL: return a ? b : c; + case U_FADD: return (Cell)fop_add((uint64_t)a, (uint64_t)b); + case U_FSUB: return (Cell)fop_sub((uint64_t)a, (uint64_t)b); + case U_FMUL: return (Cell)fop_mul((uint64_t)a, (uint64_t)b); + case U_FDIV: return (Cell)fop_div((uint64_t)a, (uint64_t)b); + case U_FEQ: return fop_eq((uint64_t)a, (uint64_t)b); + case U_FLT: return fop_lt((uint64_t)a, (uint64_t)b); + case U_FLE: return fop_le((uint64_t)a, (uint64_t)b); + case U_I2F: return (Cell)fop_i2f(a); + case U_F2I: return fop_f2i((uint64_t)a); + } + return 0; +} +static void smagic(Cell d, Cell *mul, int *sh) /* hacker's delight, signed, 64-bit */ +{ + const UCell two63 = 1ull << 63; + UCell ad = d < 0 ? 0ull - (UCell)d : (UCell)d, t = two63 + ((UCell)d >> 63); + UCell anc = t - 1 - t % ad, q1 = two63 / anc, r1 = two63 - q1 * anc, q2 = two63 / ad, r2 = two63 - q2 * ad, delta; + int p = 63; + do { + p++; + q1 *= 2; r1 *= 2; if (r1 >= anc) { q1++; r1 -= anc; } + q2 *= 2; r2 *= 2; if (r2 >= ad) { q2++; r2 -= ad; } + delta = ad - r2; + } while (q1 < delta || (q1 == delta && r1 == 0)); + *mul = (Cell)(q2 + 1); if (d < 0) *mul = -*mul; + *sh = p - 64; +} +static void umagic(UCell d, UCell *mul, int *add, int *sh) /* hacker's delight, unsigned, 64-bit */ +{ + UCell nc = ~0ull - (0ull - d) % d, q1, r1, q2, r2, delta; + const UCell two63 = 1ull << 63; + int p = 63; + *add = 0; + q1 = two63 / nc; r1 = two63 - q1 * nc; + q2 = (two63 - 1) / d; r2 = (two63 - 1) - q2 * d; + do { + p++; + if (r1 >= nc - r1) { q1 = 2 * q1 + 1; r1 = 2 * r1 - nc; } else { q1 = 2 * q1; r1 = 2 * r1; } + if (r2 + 1 >= d - r2) { if (q2 >= two63 - 1) *add = 1; q2 = 2 * q2 + 1; r2 = 2 * r2 + 1 - d; } + else { if (q2 >= two63) *add = 1; q2 = 2 * q2; r2 = 2 * r2 + 1; } + delta = d - 1 - r2; + } while (p < 128 && (q1 < delta || (q1 == delta && r1 == 0))); + *mul = q2 + 1; *sh = p - 64; +} + +static Uop ob[MAXUOP]; static int on, onv; +static int *omap, *isk; static Cell *kv; /* old value -> new value; constness of new values */ +static int pure(int op) { return !is_memop(op); } +static int ispow2(UCell x) { return x && !(x & (x - 1)); } +static int lg2(UCell x) { int n = 0; while (x > 1) { x >>= 1; n++; } return n; } +static int oe(int op, int a, int b, int c, Cell imm); +static int K(Cell v) { return oe(U_LI, 0, 0, 0, v); } +static int raw(int op, int a, int b, int c, Cell imm) +{ + Uop *u; int i; + if (pure(op)) /* cse */ + for (i = 0; i < on; i++) { + u = &ob[i]; + if (u->op == op && u->a == a && u->b == b && u->c == c && u->imm == imm) return u->d; + } + if (on >= MAXUOP) { uerr = -5; return 0; } + u = &ob[on++]; u->op = op; u->d = onv++; u->a = a; u->b = b; u->c = c; u->imm = imm; + isk[u->d] = op == U_LI; kv[u->d] = imm; + return u->d; +} +static int sdivk(int a, Cell d) /* a / d for a constant d, no divide instruction */ +{ + Cell mul; int sh, q, t, k; + UCell ad; + if (d == 1) return a; + if (d == -1) return oe(U_NEG, a, 0, 0, 0); + if (d == INT64_MIN) return oe(U_EQ, a, K(d), 0, 0); + ad = (UCell)(d < 0 ? -d : d); + if (ispow2(ad)) { + k = lg2(ad); + t = oe(U_SAR, a, K(63), 0, 0); + t = oe(U_SHR, t, K(64 - k), 0, 0); + q = oe(U_SAR, oe(U_ADD, a, t, 0, 0), K(k), 0, 0); + return d < 0 ? oe(U_NEG, q, 0, 0, 0) : q; + } + smagic(d, &mul, &sh); + q = oe(U_MULHS, a, K(mul), 0, 0); + if (d > 0 && mul < 0) q = oe(U_ADD, q, a, 0, 0); + if (d < 0 && mul > 0) q = oe(U_SUB, q, a, 0, 0); + if (sh) q = oe(U_SAR, q, K(sh), 0, 0); + return oe(U_ADD, q, oe(U_SHR, q, K(63), 0, 0), 0, 0); +} +static int udivk(int a, UCell d) +{ + UCell mul; int add, sh, t; + if (d == 1) return a; + if (ispow2(d)) return oe(U_SHR, a, K(lg2(d)), 0, 0); + if (d > (1ull << 63)) return oe(U_XOR, oe(U_LTU, a, K((Cell)d), 0, 0), K(1), 0, 0); /* a >= d */ + umagic(d, &mul, &add, &sh); + t = oe(U_MULHU, a, K((Cell)mul), 0, 0); + if (!add) return sh ? oe(U_SHR, t, K(sh), 0, 0) : t; + t = oe(U_ADD, oe(U_SHR, oe(U_SUB, a, t, 0, 0), K(1), 0, 0), t, 0, 0); + return sh > 1 ? oe(U_SHR, t, K(sh - 1), 0, 0) : t; +} +static int oe(int op, int a, int b, int c, Cell imm) +{ + int ka, kb; Cell A, B; + if (op == U_LI) return raw(U_LI, 0, 0, 0, imm); + if (op == U_MOV) return a; + if (is_memop(op)) return raw(op, a, b, 0, 0); + ka = isk[a]; kb = uarity[op] > 1 && isk[b]; + A = kv[a]; B = kv[b]; + /* everything constant: fold */ + if (ka && (uarity[op] < 2 || kb) && (uarity[op] < 3 || isk[c])) return K(ev(op, A, B, kv[c])); + /* constants go right for commutative ops */ + if (ka && !kb && (op == U_ADD || op == U_MUL || op == U_AND || op == U_OR || op == U_XOR || op == U_EQ || op == U_NE || + op == U_MIN || op == U_MAX || op == U_MINU || op == U_MAXU || op == U_MULHS || op == U_MULHU || + op == U_FADD || op == U_FMUL || op == U_FEQ)) { + int t = a; a = b; b = t; kb = 1; B = A; ka = 0; + } + if (op == U_SEL && ka) return A ? b : c; + if (op == U_SEL && b == c) return b; + if (kb) switch (op) { + case U_ADD: case U_SUB: case U_OR: case U_XOR: case U_SHL: case U_SHR: case U_SAR: + if (!B || (op >= U_SHL && op <= U_SAR && !(B & 63))) return a; + break; + case U_MUL: + if (B == 0) return K(0); + if (B == 1) return a; + if (B == -1) return oe(U_NEG, a, 0, 0, 0); + if (B > 0 && ispow2((UCell)B)) return oe(U_SHL, a, K(lg2((UCell)B)), 0, 0); + break; + case U_AND: if (B == 0) return K(0); if (B == -1) return a; break; + case U_SDIV: if (B == 0) return K(0); return sdivk(a, B); + case U_SREM: if (B == 0) return a; if (B == 1 || B == -1) return K(0); + return oe(U_SUB, a, oe(U_MUL, sdivk(a, B), b, 0, 0), 0, 0); + case U_UDIV: if (B == 0) return K(0); return udivk(a, (UCell)B); + case U_UREM: if (B == 0) return a; + if (B > 0 && ispow2((UCell)B)) return oe(U_AND, a, K(B - 1), 0, 0); + return oe(U_SUB, a, oe(U_MUL, udivk(a, (UCell)B), b, 0, 0), 0, 0); + } + /* two equal operands. not applied to the float ops: x - x is a NaN when x is, + and x == x is false for a NaN. a pair of constant floats already folded. */ + if (a == b && uarity[op] == 2) { + if (op == U_SUB || op == U_XOR || op == U_LT || op == U_LTU || op == U_NE) return K(0); + if (op == U_AND || op == U_OR || op == U_MIN || op == U_MAX || op == U_MINU || op == U_MAXU) return a; + if (op == U_EQ) return K(1); + } + return raw(op, a, b, c, 0); +} +/* runs the optimizer over ub[0..un) with nin inputs; leaves the result in e */ +static int optimize(Enh *e, int nin, int *out, int nout) +{ + int i, *live, n = 0; + static int omap_[MAXUOP + 16], isk_[MAXUOP * 4]; static Cell kv_[MAXUOP * 4]; + omap = omap_; isk = isk_; kv = kv_; + on = 0; onv = nin; + for (i = 0; i < nin; i++) { omap[i] = i; isk[i] = 0; } + for (i = 0; i < un && !uerr; i++) { + Uop *u = &ub[i]; + int a = uarity[u->op] > 0 || u->op == U_ST || u->op == U_LD ? omap[u->a] : 0; + int b = uarity[u->op] > 1 || u->op == U_ST ? omap[u->b] : 0; + int c = uarity[u->op] > 2 ? omap[u->c] : 0; + if (u->op == U_ST) oe(U_ST, a, b, 0, 0); + else omap[u->d] = oe(u->op, a, b, c, u->imm); + } + if (uerr) return uerr; + /* dead code: outputs and stores are live */ + live = calloc(onv + 1, sizeof *live); + for (i = 0; i < nout; i++) { e->out[i] = omap[out[i]]; live[e->out[i]] = 1; } + for (i = on; i--;) { + Uop *u = &ob[i]; + if (!is_store(u->op) && !live[u->d]) { u->op = -1; continue; } + if (uarity[u->op] > 0 || is_memop(u->op)) live[u->a] = 1; + if (uarity[u->op] > 1 || is_store(u->op)) live[u->b] = 1; + if (uarity[u->op] > 2) live[u->c] = 1; + } + free(live); + e->u = malloc((on + 1) * sizeof(Uop)); + for (i = 0; i < on; i++) if (ob[i].op >= 0) e->u[n++] = ob[i]; + e->n = n; e->nv = onv; e->nin = nin; e->nout = nout; + return 0; +} +/* run an installed enhancement on a downward-growing stack; returns the new top */ +static Cell *xexec(Enh *e, Cell *sp) +{ + Cell vb[256], *v = e->nv <= 256 ? vb : malloc(e->nv * sizeof(Cell)), i, j; + for (j = 0; j < e->nin; j++) v[j] = sp[e->nin - 1 - j]; + for (i = 0; i < e->n; i++) { + Uop *u = &e->u[i]; + switch (u->op) { + case U_LI: v[u->d] = u->imm; break; + case U_LD: v[u->d] = mread(v[u->a]); break; + case U_ST: if (inmem(v[u->a])) m[v[u->a]] = v[u->b]; break; + default: + if (u->op >= U_LD8 && u->op <= U_ST32) { + if (u->op == U_LD8) v[u->d] = bld8(v[u->a]); + else if (u->op == U_LD16) v[u->d] = bld16(v[u->a]); + else if (u->op == U_LD32) v[u->d] = bld32(v[u->a]); + else if (u->op == U_ST8) bst8(v[u->a], v[u->b]); + else if (u->op == U_ST16) bst16(v[u->a], v[u->b]); + else if (u->op == U_ST32) bst32(v[u->a], v[u->b]); + break; + } + v[u->d] = ev(u->op, v[u->a], v[u->b], v[u->c]); + } + } + sp += e->nin - e->nout; + for (j = 0; j < e->nout; j++) sp[e->nout - 1 - j] = v[e->out[j]]; + if (v != vb) free(v); + return sp; +} +static void xdump(int slot, Enh *e) +{ + int i; + fprintf(stderr, "enh %d: %d in, %d out, %d ops\n", slot, e->nin, e->nout, e->n); + for (i = 0; i < e->n; i++) { + Uop *u = &e->u[i]; + if (u->op == U_ST) fprintf(stderr, " st v%d v%d\n", u->a, u->b); + else if (u->op == U_LI) fprintf(stderr, " v%d = %lld\n", u->d, (long long)u->imm); + else { fprintf(stderr, " v%d = %s v%d", u->d, unames[u->op], u->a); if (uarity[u->op] > 1) fprintf(stderr, " v%d", u->b); if (uarity[u->op] > 2) fprintf(stderr, " v%d", u->c); fprintf(stderr, "\n"); } + } + for (i = 0; i < e->nout; i++) fprintf(stderr, " out%d = v%d\n", i, e->out[i]); +} +/* registers for the jit: values live in x0..x15, straight-line linear scan. + if they don't fit, reg stays 0 and the jit calls the interpreter instead. */ +static void regalloc(Enh *e) +{ + int *last = malloc(e->nv * sizeof(int)), i, j, k, fr = 0xffff; + signed char *r = malloc(e->nv); + for (i = 0; i < e->nv; i++) { last[i] = -2; r[i] = -1; } + for (i = 0; i < e->n; i++) { + Uop *u = &e->u[i]; + if (uarity[u->op] > 0 || u->op == U_ST || u->op == U_LD) last[u->a] = i; + if (uarity[u->op] > 1 || u->op == U_ST) last[u->b] = i; + if (uarity[u->op] > 2) last[u->c] = i; + } + for (i = 0; i < e->nout; i++) last[e->out[i]] = e->n; +#define TAKE(v) do { for (k = 0; k < 16 && !(fr >> k & 1); k++); if (k == 16) goto fail; fr &= ~(1 << k); r[v] = (signed char)k; } while (0) + for (j = 0; j < e->nin; j++) if (last[j] >= -1 && last[j] != -2) TAKE(j); + for (i = 0; i < e->n; i++) { + Uop *u = &e->u[i]; int ops[3], no = 0; + if (uarity[u->op] > 0 || u->op == U_ST || u->op == U_LD) ops[no++] = u->a; + if (uarity[u->op] > 1 || u->op == U_ST) ops[no++] = u->b; + if (uarity[u->op] > 2) ops[no++] = u->c; + for (j = 0; j < no; j++) if (last[ops[j]] == i && r[ops[j]] >= 0 && (fr >> r[ops[j]] & 1) == 0) fr |= 1 << r[ops[j]]; + if (!is_store(u->op)) TAKE(u->d); + if (!is_store(u->op) && last[u->d] == -2) fr |= 1 << r[u->d]; /* never used */ + } +#undef TAKE + free(last); e->reg = r; return; +fail: + free(last); free(r); e->reg = 0; +} +/* the install opcode. descriptor: slot, kind (0 recipe, 1 vm code), inputs, + outputs, payload address, payload length, parameter names (or 0). kind 2 + is an o3 region (below). + returns 0, or -1 bad descriptor, -2 recipe syntax, -4 code doesn't lift, + -5 too big. */ +static int o3install(int d); +static void slot_changed(int slot); +static int install(int d) +{ + int slot = mread(d), kind = mread(d + 1), nin = mread(d + 2), nout = mread(d + 3); + int pay = mread(d + 4), len = mread(d + 5), names = mread(d + 6), out[8], r; + Enh ne; + if (kind == 2) return o3install(d); + if ((unsigned)slot >= NENH || (unsigned)nin > 8 || (unsigned)nout > 8 || (kind != 0 && kind != 1)) return -1; + un = 0; unv = nin; uerr = 0; + r = kind == 0 ? parse_recipe(pay, names, nin, nout, out) : lift_vm(pay, len, nin, nout, out); + if (r) { if (trace) fprintf(stderr, "enh %d: install failed (%d)\n", slot, r); return r; } + memset(&ne, 0, sizeof ne); + if ((r = optimize(&ne, nin, out, nout))) return r; + ne.kind = kind; ne.live = 1; + regalloc(&ne); + if (enh[slot].live) { free(enh[slot].u); free(enh[slot].reg); } + enh[slot] = ne; + slot_changed(slot); + if (trace) xdump(slot, &enh[slot]); + return 0; +} + +/* ---- o3: optimized regions ---- + o3 code comes from the compiler as a region: a range [lo, hi) of vm words + plus the frame slots that nothing takes the address of, each with one of 7 + registers (install kind 2: [-, 2, lo, hi, n, slot, register, ...]). the region is cut into basic blocks. each + block is lifted to ssa: the vm stack is tracked symbolically, so only what + is left on it at the end of the block touches memory, the slots live in + registers across blocks (VGET reads one at block entry, the block's end + says what each one holds now), the frame pointer is var 0, and enhancements + are spliced in. empty jumps are folded and a straight loop is rotated, so + the backends see one block per iteration when the body itself doesn't + branch. a backend compiles the whole region at once: arm64, llvm at O3, or the + region interpreter below. the region is entered at lo and at the return + points of its calls. a register can stand for several slots (variables + whose scopes don't overlap), and spilling writes it to all of them. calls, + returns, jumps out, and anything a block can't + lift leave it: the slots go back to the frame first, so the code outside + sees the usual memory. stores to its own code drop it; it lifts again on + its next entry. */ +/* ZFP is the frame pointer word from memmap.h */ +#define U_VGET U_N /* a var at block entry; imm = var */ +#define U_SLD (U_N + 1) /* the stack word at offset imm from the top at block entry */ +#define RMAXV 8 +#define RMAXE 256 +enum { T_BR, T_CBR, T_JMP, T_CALL, T_RET }; +typedef struct { + int a, n, nv, delta, nso, term, cv, s1, s2, x1, x2; + int vs[RMAXV]; /* each var's value at the end, -1 if unchanged */ + int *so; /* stack words to write at the end: offset, value */ + Uop *u; +} RB; +typedef struct { + int idx, lo, hi, nv, nv0, slot[RMAXV], lifted, dead, nonat, nb, ne, maxnv; + int np, ps[32], pv[32]; /* frame slot -> var; vars in scopes that never overlap share one */ + unsigned char fv[RMAXV]; /* 1: this var's home is a d register, not an x register */ + unsigned char seen[RMAXV]; + unsigned char sc[RMAXV]; /* 1: every local that lives in this var is a plain number (int, char, bool, float), never a pointer */ + int ent[RMAXE], entb[RMAXE]; + RB *b; + void *nat, *dnat, *stub[RMAXE]; /* the backend's code; dnat is the register-passing entry of a function region */ + int fslots, argc, fn; /* callable o3 function: frame, arity, prologue address */ + int marked; /* the backend is watching the region's words */ + unsigned char dep[NENH / 8]; /* enhancement slots spliced into the lifted code */ + void *rt; /* llvm: the resource tracker that owns the region's code */ + unsigned gen; /* bumped every time the region's code is dropped: a copy made from an older one is stale */ +} Rg; +#define MAXRG 4096 +static Rg *rgs[MAXRG]; +static int nrg; +static int fn_pc[MAXRG]; +static Rg *fn_rg_[MAXRG]; +static Rg *fn_cache[1024]; +static int nfn; + +/* ---- bookkeeping for translated code, shared by the backends ---- + a translation covers a range of vm words [a,b). three things have to stay + cheap when code is rewritten while it runs. which translation a store + hits: ranges are indexed by 64-word page, so a covered store looks at the + handful of ranges on its page. which words are still covered after one + goes: the coverage bytes are counters (a word two translations share counts + 2; 255 sticks), so dropping a translation subtracts its own range and + nothing is rebuilt. and what an install invalidates: a translation records + the enhancement slots it inlined, or found unwired, so an install drops + those and keeps the rest. */ +#define PGSH 6 +#define NPG ((code_words >> PGSH) + 1) +#define MAXPGN (1 << 19) +#define MAXDEP (1 << 18) +static struct { int a, b; } ix_blk[MAXB]; +static int ix_n; +static int *pg_head, pg_free, pg_top, pg_live; /* node numbers are stored plus one: 0 is none */ +static struct { int blk, next; } pg_node[MAXPGN]; +static int dep_head[NENH + 1], dep_n; /* slot NENH: depends on every install */ +static struct { int blk, next; } dep_node[MAXDEP]; +static int drop_epoch; /* changes whenever any translation dies */ +static void be_drop(int j); /* the backend forgets block j's code and coverage */ +static void runmark(Rg *R); /* the backend stops watching the region's words */ +static void rclear(Rg *R); +static void cinc(unsigned char *c, int lo, int hi) { if (hi > code_words) hi = (int)code_words; for (; lo < hi; lo++) if (c[lo] != 255) c[lo]++; } +static void cdec(unsigned char *c, int lo, int hi) { if (hi > code_words) hi = (int)code_words; for (; lo < hi; lo++) if (c[lo] && c[lo] != 255) c[lo]--; } +static void ix_reset(void) +{ + memset(pg_head, 0, NPG * sizeof *pg_head); memset(dep_head, 0, sizeof dep_head); + pg_free = pg_top = pg_live = dep_n = ix_n = 0; + drop_epoch++; +} +static int ix_room(void) { return ix_n < MAXB && pg_live + 24 < MAXPGN && dep_n + 300 < MAXDEP; } +static int ix_add(int a, int b) +{ + int j = ix_n++, pg, nd; + ix_blk[j].a = a; ix_blk[j].b = b; + for (pg = a >> PGSH; pg <= (b - 1) >> PGSH; pg++) { + if (pg_free) { nd = pg_free - 1; pg_free = pg_node[nd].next; } else nd = pg_top++; + pg_node[nd].blk = j; pg_node[nd].next = pg_head[pg]; pg_head[pg] = nd + 1; pg_live++; + } + return j; +} +static void ix_kill(int j) +{ + if (ix_blk[j].a < 0) return; + be_drop(j); + ix_blk[j].a = -1; + drop_epoch++; +} +/* drop every translation whose range holds addr; dead entries on the page are unlinked on the way */ +static int ix_drop_at(int addr) +{ + int *pp = &pg_head[addr >> PGSH], hit = 0, nd, j; + while (*pp) { + nd = *pp - 1; j = pg_node[nd].blk; + if (ix_blk[j].a < 0) { *pp = pg_node[nd].next; pg_node[nd].next = pg_free; pg_free = nd + 1; pg_live--; continue; } + if (ix_blk[j].a <= addr && addr < ix_blk[j].b) { ix_kill(j); hit++; continue; } + pp = &pg_node[nd].next; + } + return hit; +} +static void dep_add(int slot, int j) +{ + if ((unsigned)slot > NENH) slot = NENH; + dep_node[dep_n].blk = j; dep_node[dep_n].next = dep_head[slot]; dep_head[slot] = ++dep_n; +} +/* slot was (re)wired: drop what used it, or found it unwired, and nothing else */ +static void slot_changed(int slot) +{ + int d, h, j; + for (h = 0; h < 2; h++) { + int *hp = &dep_head[h ? NENH : slot]; + for (d = *hp; d; d = dep_node[d - 1].next) ix_kill(dep_node[d - 1].blk); + *hp = 0; + } + for (j = 0; j < nrg; j++) + if (rgs[j]->lifted && (rgs[j]->dep[slot >> 3] & (1 << (slot & 7)))) { rclear(rgs[j]); drop_epoch++; } +} +static Cell halt_at = -1; /* a nested call stopped the machine: operand or return overflow */ +static unsigned short *o3ent; /* region + 1 at an entry point */ +static unsigned char *o3ei; /* which of its entries */ +static int o3_ready(Rg *C); +static int o3_exec_region(Rg *C, int entry, Cell *ps, Cell *pr); +static int vm_until(int pc, int stop_eq, int stop_ge, Rg *re, Cell *ps, Cell *pr); +static void o3_bind(Rg *R) +{ + int i; + if (R->fn < 0) return; /* -1: a bare block. 0 is a real function at address 0 */ + fn_cache[R->fn & 1023] = R; + for (i = 0; i < nfn; i++) if (fn_pc[i] == R->fn) { fn_rg_[i] = R; return; } + if (nfn < MAXRG) { fn_pc[nfn] = R->fn; fn_rg_[nfn++] = R; } +} +static Rg *fnlookup(int pc) +{ + int i; + Rg *R = fn_cache[pc & 1023]; + if (R && R->fn == pc && !R->dead) return R; + for (i = 0; i < nfn; i++) if (fn_pc[i] == pc) { + R = fn_rg_[i]; + if (R && !R->dead) { fn_cache[pc & 1023] = R; return R; } + } + return 0; +} +/* a call made from inside a region. an o3 callee gets a frame and is entered + at its region, and its epilogue is not run: fp comes back here. anything + else is an ordinary call, pushed on the vm return stack and run until that + address is the pc again. */ +/* what is left of a call once the callee's region has returned `back`: a + return address at or past the end of the function is the normal exit (the + epilogue's lit 0 is pushed when it fell off the end), anything else is + code to run on the vm until the function is left */ +static void o3_finish(Rg *C, int back, Cell *ps, Cell *pr) +{ + for (;;) { + if (halt_at >= 0 || back < 0 || (unsigned)back >= (unsigned)MW) { + if (halt_at < 0) halt_at = back < 0 ? PC_HALT : back; + m[ZFP] = mread(ZFP) + C->fslots; + return; + } + if (back >= C->hi) { + if (back == C->hi) { /* fell off the end: the epilogue's lit 0 */ + Cell sp = *ps - 1; + if ((unsigned)sp < (unsigned)MW) { m[sp] = 0; *ps = sp; } + } + m[ZFP] = mread(ZFP) + C->fslots; + return; + } + if (back >= C->lo && o3ent[back] == C->idx + 1) { + back = o3_exec_region(C, o3ei[back], ps, pr); + continue; + } + back = vm_until(back, -1, C->hi, C, ps, pr); + } +} +/* the llvm code calls this one with the callee and the address it returned, + when the quick exit did not apply */ +static void o3_finish_c(Cell *ps, Cell *pr, Rg *C, Cell back) { o3_finish(C, (int)back, ps, pr); } +static void o3_invoke(Cell *ps, Cell *pr, Cell target, Cell retpc) +{ + Rg *C = fnlookup((int)target); + int back, fp, i, s; + if (halt_at >= 0) return; + if (*ps < stack_data_limit() + 16) { + if (stack_expand(0, stack_get(SC_DATA_TOP) - stack_get(SC_DATA_LOW) + 1, ps, pr) < 0) { halt_at = PC_DATA_OVERFLOW; return; } + } + if (!C || mread(ZFP) - C->fslots < frame_bound() || !o3_ready(C)) { + if ((unsigned)*pr < (unsigned)MW) m[(*pr)++] = retpc; + back = vm_until((int)target, (int)retpc, -1, 0, ps, pr); + if (back < 0 || (unsigned)back >= (unsigned)code_words) halt_at = back < 0 ? PC_HALT : back; + return; + } + fp = (int)mread(ZFP) - C->fslots; + m[ZFP] = fp; + s = (int)*ps; + for (i = C->argc; i-- > 0; ) { + if ((unsigned)s < (unsigned)MW && (unsigned)(fp + i) < (unsigned)MW) m[fp + i] = m[s]; + s++; + } + *ps = s; + back = o3_exec_region(C, 0, ps, pr); + o3_finish(C, back, ps, pr); +} +/* ---- c abi ---- + gate ops 14..17 and opcode 620 let vm code call native functions and hand + native code functions of its own. vm memory is one flat array of words, so + a vm byte address b is the host pointer (char *)m + b and a word address w + is (char *)m + 8 * w. an argument or result of pointer kind is converted + that way when it lies inside the array, and passed through untouched (an + opaque host pointer, a malloc result) when it does not. 0 is NULL both + ways. + + a signature is a word of 4-bit kinds: the return kind first, then one per + argument (at most 15), and a 0 kind ends the list. + 0 void 1 long (any 64-bit integer) 2 double 3 byte pointer + 4 int (32-bit, sign extended) 5 float (32-bit, widened to a double word) + 6 word pointer 7 unsigned int 8 short 9 unsigned short + 10 char 11 unsigned char (small kinds are extended on the way back) + 12 byte string object (the word address of its length word; the + native side gets the address of its first byte) + integers fill the platform's integer argument registers (6 on x86-64, 8 on + arm64), doubles the 8 fp registers, and whatever does not fit goes on the + native stack in argument order: the call goes through a prototype of that + many integer and fp parameters followed by nine stack words, so the + platform's own rules place every argument. + op 14 (blk): blk is a block of words: [0] the symbol name as a byte + address, [1] the signature, [2] a lookup cache the host keeps, [3...] the + arguments in order. the result is the native result as a word (the bits, + for a double). a missing symbol prints a message and gives -1. + op 15 (name): 1 if the symbol resolves, else 0. + op 16 (entry, sig): a native function pointer that calls the vm function at + entry with the arguments the signature names, or 0. the vm code runs on top + of the stacks as they were when the native call that is running started, so + the pointer is only good while some native call from the vm is running. + op 17 (path): dlopen a library so later lookups can find its symbols. + opcode 620 (a0 ... an-1 blk -- r) is op 14 with the arguments on the stack + and the block's signature saying how many; o3 lifts it to the micro-ops + narg and ncall, and the llvm backend turns those into a direct typed call. */ +#ifdef ZYFFI +#if defined(__aarch64__) +#define FFI_NI 8 +#define FFI_IT Cell, Cell, Cell, Cell, Cell, Cell, Cell, Cell +#define FFI_IN Cell a, Cell b, Cell c, Cell d, Cell e, Cell f, Cell g, Cell h +#define FFI_IV a, b, c, d, e, f, g, h +#define FFI_IC(iv) iv[0], iv[1], iv[2], iv[3], iv[4], iv[5], iv[6], iv[7] +#else +#define FFI_NI 6 +#define FFI_IT Cell, Cell, Cell, Cell, Cell, Cell +#define FFI_IN Cell a, Cell b, Cell c, Cell d, Cell e, Cell f +#define FFI_IV a, b, c, d, e, f +#define FFI_IC(iv) iv[0], iv[1], iv[2], iv[3], iv[4], iv[5] +#endif +#define FFI_NS 15 +#define FFI_DT double, double, double, double, double, double, double, double +#define FFI_ST Cell, Cell, Cell, Cell, Cell, Cell, Cell, Cell, Cell, Cell, Cell, Cell, Cell, Cell, Cell +typedef Cell (*FfiI)(FFI_IT, FFI_DT, FFI_ST, ...); +typedef double (*FfiD)(FFI_IT, FFI_DT, FFI_ST, ...); +typedef float (*FfiF)(FFI_IT, FFI_DT, FFI_ST, ...); +#define FFI_SYMS 256 +#define FFI_LIBS 16 +static struct { char name[96]; void *p; } ffi_sym[FFI_SYMS]; +static int ffi_nsym; +static void *ffi_lib[FFI_LIBS]; +static int ffi_nlib, ffi_tried_libm; +static void *ffi_lookup(const char *name) +{ + int i; + void *p; + for (i = 0; i < ffi_nsym; i++) if (!strcmp(ffi_sym[i].name, name)) return ffi_sym[i].p; + p = dlsym(RTLD_DEFAULT, name); + for (i = 0; !p && i < ffi_nlib; i++) p = dlsym(ffi_lib[i], name); + if (!p && !ffi_tried_libm) { /* the math library is not linked into every host */ + void *h; + ffi_tried_libm = 1; + h = dlopen("libm.so.6", RTLD_NOW | RTLD_GLOBAL); + if (!h) h = dlopen("libm.dylib", RTLD_NOW | RTLD_GLOBAL); + if (h && ffi_nlib < FFI_LIBS) ffi_lib[ffi_nlib++] = h; + for (i = 0; !p && i < ffi_nlib; i++) p = dlsym(ffi_lib[i], name); + } + if (p && strlen(name) < sizeof ffi_sym[0].name && ffi_nsym < FFI_SYMS) { + strcpy(ffi_sym[ffi_nsym].name, name); + ffi_sym[ffi_nsym++].p = p; + } + return p; +} +static Cell ffi_name(Cell a, char *dst, int max) +{ + return bop_cstr(m, a, dst, max, MW); +} +static Cell ffi_find(Cell a) +{ + char name[96]; + if (ffi_name(a, name, 96) <= 0) return 0; + return ffi_lookup(name) ? 1 : 0; +} +static Cell ffi_load(Cell a) +{ + char path[512]; + void *h; + if (ffi_name(a, path, 512) <= 0 || ffi_nlib >= FFI_LIBS) return 0; + h = dlopen(path, RTLD_NOW | RTLD_GLOBAL); + if (!h) return 0; + ffi_lib[ffi_nlib++] = h; + return 1; +} +static Cell ffi_in(Cell v, int kind) /* a vm value as the native argument */ +{ + if (kind == 3 && v && (UCell)v < (UCell)MW * 8) return (Cell)((char *)m + v); + if (kind == 6 && v && (UCell)v < (UCell)MW) return (Cell)((char *)m + v * 8); + if (kind == 12 && v && (UCell)v + 1 < (UCell)MW) return (Cell)((char *)m + (v + 1) * 8); + return v; +} +static Cell ffi_out(Cell v, int kind) /* a native result as a vm value */ +{ + char *p = (char *)v, *lo = (char *)m, *hi = (char *)m + (size_t)MW * 8; + if (kind == 4) return (Cell)(int)v; + if (kind == 7) return (Cell)(uint32_t)v; + if (kind == 8) return (Cell)(int16_t)v; + if (kind == 9) return (Cell)(uint16_t)v; + if (kind == 10) return (Cell)(int8_t)v; + if (kind == 11) return (Cell)(uint8_t)v; + if ((kind == 3 || kind == 6) && p >= lo && p < hi) return kind == 3 ? (Cell)(p - lo) : (Cell)((p - lo) >> 3); + return v; +} +static double ffi_d(Cell v) { double d; memcpy(&d, &v, 8); return d; } +static Cell ffi_bits(double d) { Cell v; memcpy(&v, &d, 8); return v; } +/* a signature's arguments are nibbles after the result. code 13 (a struct in + registers) and code 14 (a struct in memory) start a group: the next nibble is + its length in eightbytes, and each eightbyte is one more nibble (1 integer, + 2 sse). every other code is one argument. on the stack a group is one item: + the vm address of the struct, and its eightbytes are the words from there on. + every argument, a group included, is one item (one word of the stack or of a + call block's slots) */ +#define FFI_GROUP_REG 13 +#define FFI_GROUP_MEM 14 +static int ffi_hasgroup(Cell sig) +{ + int p; + for (p = 1; p <= 15; p++) { + int k = (int)((sig >> (4 * p)) & 15); + if (!k) break; + if (k == FFI_GROUP_REG || k == FFI_GROUP_MEM) return 1; + } + return 0; +} +static int ffi_nargs(Cell sig) +{ + int n = 0, p = 1; + while (p <= 15) { + int k = (int)((sig >> (4 * p)) & 15); + if (!k) break; + if (k == FFI_GROUP_REG || k == FFI_GROUP_MEM) { + int len = (int)((sig >> (4 * (p + 1))) & 15); + n++; + p += 2 + len; + } else { n++; p++; } + } + return n; +} +/* the symbol a block names. word 2 remembers which table slot answered last + time (plus one), so a repeated call costs one string compare. a stale + number from another run fails the compare and goes through the lookup */ +static void *ffi_blkfn(Cell blk) +{ + char name[96]; + void *f; + int k; + if (blk < 0 || blk + 18 >= MW) return 0; + if (ffi_name(m[blk], name, 96) <= 0) return 0; + k = (int)(m[blk + 2] & 0xffffffffLL); + if (k > 0 && k <= ffi_nsym && !strcmp(ffi_sym[k - 1].name, name)) return ffi_sym[k - 1].p; + f = ffi_lookup(name); + for (k = 0; f && k < ffi_nsym; k++) if (ffi_sym[k].p == f && !strcmp(ffi_sym[k].name, name)) { m[blk + 2] = (m[blk + 2] & ~0xffffffffLL) | (k + 1); break; } + if (!f) fprintf(stderr, "zy: no native symbol %s\n", name); + return f; +} +/* a struct result. the call block's word 2 holds the host's cache in its low half + and the result's descriptor in its high half: the struct's length in words, then + one class nibble per word (1 integer, 2 float). the result's memory is the first + item of the call (the stub makes it), and the words go there. which words travel + in memory is the host's rule (ffi_retmem); the rest come back in registers, and + the c function type that returns the matching struct gets them there */ +typedef struct { Cell a, b; } FfiSII; +typedef struct { double a, b; } FfiSDD; +typedef struct { Cell a; double b; } FfiSID; +typedef struct { double a; Cell b; } FfiSDI; +typedef struct { double d[1]; } FfiSF1; +typedef struct { double d[2]; } FfiSF2; +typedef struct { double d[3]; } FfiSF3; +typedef struct { double d[4]; } FfiSF4; +typedef struct { Cell w[7]; } FfiSBIG; +typedef FfiSII (*FfiRII)(FFI_IT, FFI_DT, FFI_ST, ...); +typedef FfiSDD (*FfiRDD)(FFI_IT, FFI_DT, FFI_ST, ...); +typedef FfiSID (*FfiRID)(FFI_IT, FFI_DT, FFI_ST, ...); +typedef FfiSDI (*FfiRDI)(FFI_IT, FFI_DT, FFI_ST, ...); +typedef FfiSF1 (*FfiRF1)(FFI_IT, FFI_DT, FFI_ST, ...); +typedef FfiSF2 (*FfiRF2)(FFI_IT, FFI_DT, FFI_ST, ...); +typedef FfiSF3 (*FfiRF3)(FFI_IT, FFI_DT, FFI_ST, ...); +typedef FfiSF4 (*FfiRF4)(FFI_IT, FFI_DT, FFI_ST, ...); +typedef FfiSBIG (*FfiRBIG)(FFI_IT, FFI_DT, FFI_ST, ...); +#define FFI_CALL(T) ((T)f)(FFI_IC(iv), dv[0], dv[1], dv[2], dv[3], dv[4], dv[5], dv[6], dv[7], sv[0], sv[1], sv[2], sv[3], sv[4], sv[5], sv[6], sv[7], sv[8], sv[9], sv[10], sv[11], sv[12], sv[13], sv[14]) +/* does the host send this result through memory? sysv: more than two words. aapcs64: + more than two words unless it is a float struct of up to four words, which comes + back in the v registers, and an integer struct of up to two */ +static int ffi_retmem(Cell rdesc) +{ + int n = (int)(rdesc & 15), g; + if (n <= 2) return 0; +#if defined(__aarch64__) + if (n > 4) return 1; + for (g = 0; g < n; g++) if (((rdesc >> (4 + 4 * g)) & 15) != 2) return 1; + return 0; +#else + return 1; +#endif +} +/* the call that returns a struct: the words land in m at res */ +static void ffi_call_ret(void *f, Cell rdesc, Cell *iv, double *dv, Cell *sv, Cell res) +{ + int n = (int)(rdesc & 15), g, c[8], hfa = 1; + for (g = 0; g < n; g++) { c[g] = (int)((rdesc >> (4 + 4 * g)) & 15); if (c[g] != 2) hfa = 0; } +#if defined(__x86_64__) || defined(__aarch64__) + if (ffi_retmem(rdesc)) { +#if defined(__x86_64__) + FFI_CALL(FfiI); /* iv[0] is the hidden pointer (ffi_invoke set it); the callee writes there */ +#else + { FfiSBIG s = FFI_CALL(FfiRBIG); for (g = 0; g < n; g++) m[res + g] = s.w[g]; } +#endif + return; + } +#if defined(__aarch64__) + if (hfa && n == 1) { FfiSF1 s = FFI_CALL(FfiRF1); m[res] = ffi_bits(s.d[0]); } + else if (hfa && n == 2) { FfiSF2 s = FFI_CALL(FfiRF2); m[res] = ffi_bits(s.d[0]); m[res + 1] = ffi_bits(s.d[1]); } + else if (hfa && n == 3) { FfiSF3 s = FFI_CALL(FfiRF3); for (g = 0; g < 3; g++) m[res + g] = ffi_bits(s.d[g]); } + else if (hfa && n == 4) { FfiSF4 s = FFI_CALL(FfiRF4); for (g = 0; g < 4; g++) m[res + g] = ffi_bits(s.d[g]); } + else { FfiSII s = FFI_CALL(FfiRII); m[res] = s.a; if (n > 1) m[res + 1] = s.b; } +#else + if (n == 1) { if (c[0] == 2) m[res] = ffi_bits(FFI_CALL(FfiD)); else m[res] = FFI_CALL(FfiI); } + else if (c[0] == 1 && c[1] == 1) { FfiSII s = FFI_CALL(FfiRII); m[res] = s.a; m[res + 1] = s.b; } + else if (c[0] == 2 && c[1] == 2) { FfiSDD s = FFI_CALL(FfiRDD); m[res] = ffi_bits(s.a); m[res + 1] = ffi_bits(s.b); } + else if (c[0] == 1) { FfiSID s = FFI_CALL(FfiRID); m[res] = s.a; m[res + 1] = ffi_bits(s.b); } + else { FfiSDI s = FFI_CALL(FfiRDI); m[res] = ffi_bits(s.a); m[res + 1] = s.b; } +#endif +#else + (void)f; (void)rdesc; (void)iv; (void)dv; (void)sv; (void)res; (void)hfa; + fprintf(stderr, "zy: a struct result needs x86-64 or arm64 in this build\n"); +#endif +} +/* call f with the vm values av, by the signature. integer-class arguments take + the integer registers in turn, fp ones the fp registers, and what is left + over lands in the stack words in order (a float32 is the low half of a + word, as it would sit in a register) */ +/* darwin puts unnamed arguments on the stack. these standard stdio entry + points have known fixed prefixes even when an extern uses a zc alias. */ +static int ffi_varargs(void *f) +{ +#if defined(__APPLE__) && defined(__aarch64__) + static const char *names[] = { "printf", "fprintf", "sprintf", "snprintf" }; + static const int fixed[] = { 1, 2, 2, 3 }; + static void *entry[4]; static int ready; + int i; + if (!ready) { for (i = 0; i < 4; i++) entry[i] = dlsym(RTLD_DEFAULT, names[i]); ready = 1; } + for (i = 0; i < 4; i++) if (f == entry[i]) return fixed[i]; +#else + (void)f; +#endif + return -1; +} +static Cell ffi_invoke(void *f, Cell sig, const Cell *av, Cell rdesc) +{ + Cell iv[8] = {0, 0, 0, 0, 0, 0, 0, 0}, sv[FFI_NS] = {0, 0, 0, 0, 0, 0, 0, 0, 0}, r, w; + Cell cbuf[225]; /* copies of the structs passed by reference (aapcs64) */ + int nc = 0; + double dv[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + int ni = 0, nd = 0, ns = 0, i = 0, j = 0, p, k, rk = (int)(sig & 15), fixed = ffi_varargs(f); + int sret = rk == FFI_GROUP_REG || rk == FFI_GROUP_MEM, rn = (int)(rdesc & 15); + Cell res = 0; + if (sret) { + if (rn < 1 || rn > 7) return -1; + res = av[0]; + if (res < 0 || res + rn > MW) return -1; + j = 1; +#if defined(__x86_64__) + if (ffi_retmem(rdesc)) { iv[0] = (Cell)(intptr_t)(m + res); ni = 1; } +#endif + } + for (p = 1; p <= 15; p++) { + k = (int)((sig >> (4 * p)) & 15); + if (!k) break; + if (k == FFI_GROUP_REG || k == FFI_GROUP_MEM) { + /* a struct: one item, its vm address. its eightbytes take registers only as + a whole, else all of them go on the stack, in order (the sysv rule). only + the sysv classes are done so far */ +#if defined(__x86_64__) || defined(__aarch64__) + int len = (int)((sig >> (4 * (p + 1))) & 15), g, need_i = 0, need_d = 0; + Cell base = av[j]; + if (base < 0 || base + len > MW || len < 1 || len > 15) return -1; + for (g = 0; g < len; g++) { + if (((sig >> (4 * (p + 2 + g))) & 15) == 2) need_d++; else need_i++; + } +#if defined(__aarch64__) + /* aapcs64: a homogeneous float struct of up to four words goes in the v + registers, one word each; another struct of two words or less goes in + the x registers; a bigger one is passed by reference (a copy, whose + address is the integer argument). a struct that does not fit takes the + rest of its register class, so the later arguments go on the stack */ + if (need_i == 0 && len <= 4) { + if (nd + len <= 8) { for (g = 0; g < len; g++) dv[nd++] = ffi_d(m[base + g]); } + else { nd = 8; for (g = 0; g < len; g++) { if (ns >= FFI_NS) return -1; sv[ns++] = m[base + g]; } } + } else if (len <= 2) { + if (ni + len <= FFI_NI) { for (g = 0; g < len; g++) iv[ni++] = m[base + g]; } + else { ni = FFI_NI; for (g = 0; g < len; g++) { if (ns >= FFI_NS) return -1; sv[ns++] = m[base + g]; } } + } else { + Cell *cp = cbuf + nc; + for (g = 0; g < len; g++) { if (nc >= 225) return -1; cbuf[nc++] = m[base + g]; } + if (ni < FFI_NI) iv[ni++] = (Cell)(intptr_t)cp; + else if (ns < FFI_NS) sv[ns++] = (Cell)(intptr_t)cp; + else return -1; + } +#else + if (k == FFI_GROUP_REG && ni + need_i <= FFI_NI && nd + need_d <= 8) { + for (g = 0; g < len; g++) { + if (((sig >> (4 * (p + 2 + g))) & 15) == 2) dv[nd++] = ffi_d(m[base + g]); + else iv[ni++] = m[base + g]; + } + } else { + for (g = 0; g < len; g++) { + if (ns >= FFI_NS) return -1; + sv[ns++] = m[base + g]; + } + } +#endif + j++; + p += 1 + len; + continue; +#else + fprintf(stderr, "zy: a struct by value needs x86-64 or arm64 in this build\n"); + return -1; +#endif + } + if (fixed >= 0 && p > fixed) { + if (ns >= FFI_NS) return -1; + if (k == 5) w = ffi_bits((double)(float)ffi_d(av[j])); + else if (k == 2) w = av[j]; + else w = ffi_in(av[j], k); + sv[ns++] = w; j++; continue; + } + if (k == 2 || k == 5) { + if (k == 5) { + float fl = (float)ffi_d(av[j]); + uint32_t u; + memcpy(&u, &fl, 4); + w = (Cell)u; + } else w = av[j]; + if (nd < 8) dv[nd++] = ffi_d(w); + else if (ns < FFI_NS) sv[ns++] = w; + else return -1; + } else { + w = ffi_in(av[j], k); + if (ni < FFI_NI) iv[ni++] = w; + else if (ns < FFI_NS) sv[ns++] = w; + else return -1; + } + j++; + } + (void)i; + if (sret) { ffi_call_ret(f, rdesc, iv, dv, sv, res); return 0; } + if (rk == 2) r = ffi_bits(FFI_CALL(FfiD)); + else if (rk == 5) r = ffi_bits((double)FFI_CALL(FfiF)); + else { + r = FFI_CALL(FfiI); + r = rk ? ffi_out(r, rk) : 0; + } + return r; +} +static Cell ffi_call(Cell blk) +{ + void *f = ffi_blkfn(blk); + Cell av[15]; + int i; + if (!f) return -1; + for (i = 0; i < 15; i++) av[i] = m[blk + 3 + i]; + return ffi_invoke(f, m[blk + 1], av, (Cell)((UCell)m[blk + 2] >> 32)); +} +/* opcode 620 on the data stack: blk on top, then the arguments, the last one + next. returns the new stack top, with the result on it. cb_r has to be set + by the caller (the interpreter's r, an llvm block's r) */ +static Cell ncall_exec(Cell s) +{ + Cell blk, sig, av[15], res = -1; + int n, i; + void *f; + if ((UCell)s >= (UCell)MW) return s; + blk = m[s]; + if (blk < 0 || blk + 18 >= MW) return s + 1; + sig = m[blk + 1]; + n = ffi_nargs(sig); + if (s + n >= MW) return s + 1; + for (i = 0; i < n; i++) av[i] = m[s + n - i]; + f = ffi_blkfn(blk); + cb_s = s + n + 1; + if ((sig & 15) >= 13) res = -1; /* a struct result is only made by the stub's syscall */ + else if (f) res = ffi_invoke(f, sig, av, 0); + m[s + n] = res; + return s + n; +} +/* the jit's form: sp and rp are host pointers into m */ +static long ncall_host(long sp, long rp) +{ + Cell s = ((Cell *)sp - m); + cb_r = (Cell *)rp - m; + return (long)(m + ncall_exec(s)); +} + +/* callbacks. each slot is a native function of the c shapes (the integer and + fp registers and nine stack words in, one result out), one set per result + class. calling it pushes the arguments on the vm data stack as vm code + expects them, pushes a return address that stops the nested run, runs the + vm function and pops its result. */ +#define FFI_CBS 16 +static struct { Cell entry, sig; int used; } ffi_cb[3][FFI_CBS]; +static Cell ffi_enter(int cls, int k, Cell *iv, double *dv, Cell *sv) +{ + Cell s = cb_s, r = cb_r, sig = ffi_cb[cls][k].sig, res = 0, sent = 1, w; + int i, kind, ni = 0, nd = 0, ns = 0, n = 0, back; + Cell args[16]; + if (halt_at >= 0 || (unsigned)(s - 20) >= (unsigned)MW || (unsigned)r >= (unsigned)MW) return 0; + for (i = 0; i < 15; i++) { + kind = (int)((sig >> (4 * (i + 1))) & 15); + if (!kind) break; + if (kind == 2 || kind == 5) { + w = nd < 8 ? ffi_bits(dv[nd++]) : ns < FFI_NS ? sv[ns++] : 0; + if (kind == 5) { + uint32_t u = (uint32_t)w; + float fl; + memcpy(&fl, &u, 4); + w = ffi_bits((double)fl); + } + args[n++] = w; + } else { + w = ni < FFI_NI ? iv[ni++] : ns < FFI_NS ? sv[ns++] : 0; + args[n++] = ffi_out(w, kind); + } + } + for (i = 0; i < n; i++) m[--s] = args[i]; + m[r++] = sent; + back = vm_until((int)ffi_cb[cls][k].entry, (int)sent, -1, 0, &s, &r); + if (back < 0 || (unsigned)back >= (unsigned)code_words) halt_at = back < 0 ? PC_HALT : back; + if (halt_at >= 0) return 0; + if ((unsigned)s < (unsigned)MW) res = m[s++]; + cb_s = s; cb_r = r; + kind = (int)(sig & 15); + if (cls == 0) return kind ? ffi_in(res, kind) : 0; + return res; +} +#define FFI_ARGS FFI_IN, double x0, double x1, double x2, double x3, double x4, double x5, double x6, double x7, Cell s0, Cell s1, Cell s2, Cell s3, Cell s4, Cell s5, Cell s6, Cell s7, Cell s8 +#define FFI_PACK Cell iv[8] = {FFI_IV}; double dv[8] = {x0, x1, x2, x3, x4, x5, x6, x7}; Cell sv[FFI_NS] = {s0, s1, s2, s3, s4, s5, s6, s7, s8} +#define FFI_SLOT(K) \ + static Cell ffi_i##K(FFI_ARGS) { FFI_PACK; return ffi_enter(0, K, iv, dv, sv); } \ + static double ffi_d##K(FFI_ARGS) { FFI_PACK; return ffi_d(ffi_enter(1, K, iv, dv, sv)); } \ + static float ffi_f##K(FFI_ARGS) { FFI_PACK; return (float)ffi_d(ffi_enter(2, K, iv, dv, sv)); } +#define FFI_ALL(X) X(0) X(1) X(2) X(3) X(4) X(5) X(6) X(7) X(8) X(9) X(10) X(11) X(12) X(13) X(14) X(15) +FFI_ALL(FFI_SLOT) +#define FFI_PICK(K) (void *)ffi_i##K, +#define FFI_PICKD(K) (void *)ffi_d##K, +#define FFI_PICKF(K) (void *)ffi_f##K, +static void *ffi_fn[3][FFI_CBS] = { { FFI_ALL(FFI_PICK) }, { FFI_ALL(FFI_PICKD) }, { FFI_ALL(FFI_PICKF) } }; +static Cell ffi_cbmake(Cell entry, Cell sig) +{ + int cls = (sig & 15) == 2 ? 1 : (sig & 15) == 5 ? 2 : 0, k; + for (k = 0; k < FFI_CBS; k++) + if (ffi_cb[cls][k].used && ffi_cb[cls][k].entry == entry && ffi_cb[cls][k].sig == sig) return (Cell)ffi_fn[cls][k]; + for (k = 0; k < FFI_CBS; k++) + if (!ffi_cb[cls][k].used) { + ffi_cb[cls][k].used = 1; ffi_cb[cls][k].entry = entry; ffi_cb[cls][k].sig = sig; + return (Cell)ffi_fn[cls][k]; + } + return 0; +} +/* a native call the o3 lifter has resolved: the micro-ops narg and ncall carry + an index into this table. imm of narg is the argument's position */ +typedef struct { void *f; Cell sig; int n; char name[48]; } Ncd; +static Ncd ncd[256]; +static int nncd; +static int ncd_get(Cell blk) +{ + void *f; + Cell sig; + char name[96]; + int i; + if (blk < 0 || blk + 18 >= MW) return -1; + if (ffi_name(m[blk], name, 96) <= 0) return -1; + f = ffi_lookup(name); + if (!f) return -1; + sig = m[blk + 1]; + if (ffi_hasgroup(sig) || (sig & 15) >= 13) return -1; /* the o3 lifter reads one nibble per argument; a struct goes through ncall_exec */ + for (i = 0; i < nncd; i++) if (ncd[i].f == f && ncd[i].sig == sig) return i; + if (nncd >= 256) return -1; + ncd[nncd].f = f; ncd[nncd].sig = sig; ncd[nncd].n = ffi_nargs(sig); + strncpy(ncd[nncd].name, name, 47); + ncd[nncd].name[47] = 0; + return nncd++; +} +#else +static Cell ffi_call(Cell blk) { return -1; } +static Cell ffi_find(Cell name) { return 0; } +static Cell ffi_cbmake(Cell entry, Cell sig) { return 0; } +static Cell ffi_load(Cell name) { return 0; } +static Cell ncall_exec(Cell s) +{ + Cell blk, sig; + int n = 0; + if ((UCell)s >= (UCell)MW) return s; + blk = m[s]; + if (blk < 0 || blk + 18 >= MW) return s + 1; + sig = m[blk + 1]; + while (n < 15 && ((sig >> (4 * (n + 1))) & 15)) n++; + m[s + n] = -1; + return s + n; +} +static long ncall_host(long sp, long rp) { return (long)(m + ncall_exec((Cell *)sp - m)); } +static int ncd_get(Cell blk) { return -1; } +#endif + +static void rforget(Rg *R); /* the backend drops its code */ +static void rmark(Rg *R); /* the backend watches the region's words */ +static void rcompile(Rg *R, int eager); + +static int nargs(int op) { return op >= U_N ? 0 : uarity[op]; } +static int op_fpdest(int op) +{ + return op == U_FADD || op == U_FSUB || op == U_FMUL || op == U_FDIV || op == U_I2F || op == U_S2F; +} +/* operand 0 is a, 1 is b. comparisons read floats and write an integer. */ +static int op_fpuse(int op, int which) +{ + if (op == U_FADD || op == U_FSUB || op == U_FMUL || op == U_FDIV || op == U_FEQ || op == U_FLT || op == U_FLE) return 1; + if ((op == U_F2I || op == U_F2S) && which == 0) return 1; + return 0; +} +/* 1 when the value should stay in an FP register: a float result, a float + variable, or a load/constant that is only ever used as a float. a use that + also wants an integer (a branch, an int variable) keeps the bits in a GPR + and the float use converts at that instruction. */ +static void class_fp(Rg *R, RB *B, unsigned char *fp) +{ + int i, j, *fu, *gu, n = B->nv; + fu = calloc(n + 1, sizeof(int)); + gu = calloc(n + 1, sizeof(int)); + memset(fp, 0, n); + for (i = 0; i < B->n; i++) { + Uop *u = &B->u[i]; + int na = nargs(u->op); + if (na > 0) { if (op_fpuse(u->op, 0)) fu[u->a]++; else gu[u->a]++; } + if (na > 1) { if (op_fpuse(u->op, 1)) fu[u->b]++; else gu[u->b]++; } + if (na > 2) gu[u->c]++; + } + for (j = 1; j < R->nv; j++) if (B->vs[j] >= 0) { + if (R->fv[j]) fu[B->vs[j]]++; else gu[B->vs[j]]++; + } + if ((B->term == T_CBR || B->term == T_JMP || B->term == T_CALL) && B->cv >= 0) gu[B->cv]++; + for (i = 0; i < B->n; i++) { + Uop *u = &B->u[i]; + int d = u->d; + if (is_store(u->op) || u->op == U_NARG) continue; + if (u->op == U_VGET) { if ((unsigned)u->imm < RMAXV && R->fv[u->imm]) fp[d] = 1; continue; } + if (op_fpdest(u->op) || (fu[d] && !gu[d])) fp[d] = 1; + } + free(fu); free(gu); +} +static void rclear(Rg *R) +{ + int b; + if (R->marked) { runmark(R); R->marked = 0; } + memset(R->dep, 0, sizeof R->dep); + rforget(R); + for (b = 0; b < R->nb; b++) { free(R->b[b].u); free(R->b[b].so); } + free(R->b); R->b = 0; R->nb = 0; R->lifted = 0; + R->gen++; +} +static void rdrop_all(void) { int j; for (j = 0; j < nrg; j++) if (rgs[j]->lifted) rclear(rgs[j]); } + +/* lifting one block */ +static Rg *R_; +static unsigned char *lead_; +static int *bidx_, *wl_, nw_; +static int lv_[RMAXV], lw_[RMAXV]; +#define SB 1100 +static int skv[2 * SB], stp, slo, shi; +static unsigned char skk[2 * SB]; +static int afp[MAXUOP * 4 + 16]; /* value -> 1 + its offset from fp, if it is fp + k */ +static int emap[MAXUOP * 4 + 16]; + +static int rvar(int v) +{ + if (lv_[v] < 0) { lv_[v] = raw(U_VGET, 0, 0, 0, v); if (!v) afp[lv_[v]] = 1; } + return lv_[v]; +} +static int slotvar(int off) { int j; for (j = 0; j < R_->np; j++) if (R_->ps[j] == off) return R_->pv[j]; return 0; } +static int rbin(int op, int x, int y, int z) +{ + int r = oe(op, x, y, z, 0); + if (op == U_ADD && !afp[r]) { + if (afp[x] && isk[y]) afp[r] = afp[x] + kv[y]; + else if (afp[y] && isk[x]) afp[r] = afp[y] + kv[x]; + } + return r; +} +static int rld(int a) +{ + int v; + if (isk[a] && kv[a] == ZFP) return rvar(0); + if (afp[a] && (v = slotvar(afp[a] - 1))) return rvar(v); + return oe(U_LD, a, 0, 0, 0); +} +static int rst(int a, int b) +{ + int v; + if (isk[a] && kv[a] == ZFP) return 0; /* moving the frame: not in here */ + if (afp[a] && (v = slotvar(afp[a] - 1))) { lv_[v] = b; lw_[v] = 1; return 1; } + oe(U_ST, a, b, 0, 0); + return 1; +} +static int sget(int o) +{ + if (o < -SB || o >= SB) { uerr = -6; return 0; } + if (!skk[o + SB]) { + if (o < 0) { uerr = -6; return 0; } + skv[o + SB] = raw(U_SLD, 0, 0, 0, o); skk[o + SB] = 1; + } + if (o < slo) slo = o; + if (o >= shi) shi = o + 1; + return skv[o + SB]; +} +static void sset(int o, int v) +{ + if (o < -SB || o >= SB) { uerr = -6; return; } + skv[o + SB] = v; skk[o + SB] = 1; + if (o < slo) slo = o; + if (o >= shi) shi = o + 1; +} +static int spop(void) { return sget(stp++); } +static void spush(int v) { sset(--stp, v); } +static int rblk(int t) { return t >= R_->lo && t < R_->hi && lead_[t - R_->lo] ? bidx_[t - R_->lo] : -1; } +/* an enhancement, spliced into the block */ +static int rsplice(Enh *e) +{ + int i, j; + for (j = e->nin; j--;) emap[j] = spop(); + for (i = 0; i < e->n && !uerr; i++) { + Uop *u = &e->u[i]; + int na = nargs(u->op), a = na > 0 ? emap[u->a] : 0, b = na > 1 ? emap[u->b] : 0, c = na > 2 ? emap[u->c] : 0; + if (u->op == U_LI) emap[u->d] = K(u->imm); + else if (u->op == U_LD) emap[u->d] = rld(a); + else if (u->op == U_ST) { if (!rst(a, b)) uerr = -4; } + else emap[u->d] = rbin(u->op, a, b, c); + } + for (j = 0; j < e->nout; j++) spush(emap[e->out[j]]); + return uerr; +} +static int lift_block(int bi) +{ + static const int map[] = { U_ADD, U_SUB, U_MUL, U_AND, U_OR, U_XOR, U_SAR, U_LT }; + RB *B = &R_->b[bi]; + int i = B->a, k, x, y, t, j, o, n = 0, *live; + Cell w; + on = 0; onv = 0; uerr = 0; + memset(afp, 0, sizeof afp); memset(skk, 0, sizeof skk); + stp = slo = shi = 0; + for (j = 0; j < RMAXV; j++) { lv_[j] = -1; lw_[j] = 0; } + B->term = T_JMP; B->cv = -1; B->s1 = B->s2 = -1; B->x1 = B->x2 = 0; + for (;;) { + if (uerr) return uerr; + if (i != B->a && (t = rblk(i)) >= 0) { B->term = T_BR; B->s1 = t; goto done; } + if (i < R_->lo || i >= R_->hi) goto leave_at; + w = m[i]; + if (w & 1) { spush(K(w >> 1)); i++; continue; } + k = (int)(w >> 1); + switch (k) { + case 0: B->cv = K(-1); goto done; /* halt */ + case 1: x = spop(); spush(rld(x)); break; + case 2: + x = sget(stp); + if (isk[x] && kv[x] == ZFP) goto leave_at; + x = spop(); y = spop(); rst(x, y); + break; + case 3: /* pick: a constant depth */ + x = sget(stp); + if (!isk[x] || kv[x] < 0 || kv[x] > 1024) goto leave_at; + stp++; spush(sget(stp + kv[x])); + break; + case 4: stp++; break; + case 5: x = sget(stp); y = sget(stp + 1); sset(stp, y); sset(stp + 1, x); break; + case 6: B->cv = spop(); B->term = T_CALL; B->x2 = i + 1; goto done; + case 7: + x = spop(); + if (isk[x] && (t = rblk(kv[x])) >= 0) { B->term = T_BR; B->s1 = t; } + else B->cv = x; + goto done; + case 8: + x = sget(stp); + if (!isk[x]) goto leave_at; + stp++; y = spop(); + if (isk[y]) { /* a known condition */ + if (kv[y]) { i++; continue; } + if ((t = rblk(kv[x])) >= 0) { B->term = T_BR; B->s1 = t; } else B->cv = K(kv[x]); + goto done; + } + B->term = T_CBR; B->cv = y; + B->x1 = kv[x]; B->s1 = rblk(kv[x]); + B->x2 = i + 1; B->s2 = rblk(i + 1); + goto done; + case 9: B->term = T_RET; goto done; + case 13: case 14: case 15: case 16: case 17: case 18: case 19: case 20: + x = spop(); y = spop(); spush(rbin(map[k - 13], y, x, 0)); + break; + case 600: /* flit: the next word is the raw bits, even when it looks like halt */ + if (i + 1 >= code_words) goto leave_at; + spush(K(m[i + 1])); + i += 2; + continue; + case 601: case 602: case 603: case 604: case 605: case 606: case 607: { + static const int fmap[] = { U_FADD, U_FSUB, U_FMUL, U_FDIV, U_FEQ, U_FLT, U_FLE }; + x = spop(); y = spop(); spush(rbin(fmap[k - 601], y, x, 0)); + break; } + case 608: x = spop(); spush(rbin(U_I2F, x, 0, 0)); break; + case 609: x = spop(); spush(rbin(U_F2I, x, 0, 0)); break; + case 620: { /* a native call: the block address is a constant, so the callee and its signature are known now */ +#ifdef ZYJIT + goto leave_at; +#else + int di, na, av[15]; + x = sget(stp); + if (!isk[x] || (di = ncd_get(kv[x])) < 0) goto leave_at; + na = ncd[di].n; + stp++; + for (j = na; j-- > 0;) av[j] = spop(); + for (j = 0; j < na; j++) raw(U_NARG, av[j], 0, 0, j); + spush(raw(U_NCALL, 0, 0, 0, di)); + break; +#endif + } + case 612: x = spop(); spush(raw(U_LD8, x, 0, 0, 0)); break; + case 614: x = spop(); spush(raw(U_LD16, x, 0, 0, 0)); break; + case 616: x = spop(); spush(raw(U_LD32, x, 0, 0, 0)); break; + case 613: x = spop(); y = spop(); raw(U_ST8, x, y, 0, 0); break; + case 615: x = spop(); y = spop(); raw(U_ST16, x, y, 0, 0); break; + case 617: x = spop(); y = spop(); raw(U_ST32, x, y, 0, 0); break; + default: + if (k >= XOP0 && k < XOP0 + NENH && enh[k - XOP0].live) { R_->dep[(k - XOP0) >> 3] |= 1 << ((k - XOP0) & 7); if (rsplice(&enh[k - XOP0])) return uerr; break; } + goto leave_at; /* i/o, install, unwired: done the usual way */ + } + i++; + } +leave_at: + B->term = T_JMP; B->cv = K(i); +done: + if (uerr) return uerr; + B->delta = stp; + B->so = malloc((shi - slo + 1) * 2 * sizeof(int)); + B->nso = 0; + for (o = stp > slo ? stp : slo; o < shi; o++) + if (skk[o + SB]) { + x = skv[o + SB]; + if (ob[x].op == U_SLD && ob[x].imm == o) continue; /* still the word that was there */ + if (afp[x] && slotvar(afp[x] - 1)) return -7; /* a register var's address can't wait in memory */ + B->so[2 * B->nso] = o; B->so[2 * B->nso + 1] = x; B->nso++; + } + for (j = 0; j < RMAXV; j++) B->vs[j] = -1; + for (j = 1; j < R_->nv; j++) + if (lw_[j] && !(ob[lv_[j]].op == U_VGET && ob[lv_[j]].imm == j)) B->vs[j] = lv_[j]; + /* dead code: what the block leaves behind and its stores are live */ + live = calloc(onv + 1, sizeof *live); + for (j = 0; j < B->nso; j++) live[B->so[2 * j + 1]] = 1; + for (j = 1; j < R_->nv; j++) if (B->vs[j] >= 0) live[B->vs[j]] = 1; + if (B->term == T_CBR || B->term == T_JMP || B->term == T_CALL) live[B->cv] = 1; + for (i = on; i--;) { + Uop *u = &ob[i]; + if (!is_eff(u->op) && !live[u->d]) { u->op = -1; continue; } + k = nargs(u->op); + if (k > 0) live[u->a] = 1; + if (k > 1) live[u->b] = 1; + if (k > 2) live[u->c] = 1; + } + free(live); + B->u = malloc((on + 1) * sizeof(Uop)); + for (i = 0; i < on; i++) if (ob[i].op >= 0) B->u[n++] = ob[i]; + B->n = n; B->nv = onv; + if (onv > R_->maxnv) R_->maxnv = onv; + return 0; +} +/* ---- shape: empty jumps, straight-line merges, rotated loops ---- + the for-loop the compiler emits is a test, an empty jump, a body and a + step: four dispatches per iteration. fold the empty jump, paste a block + onto its only predecessor, and rotate a straight loop so the hot path is + one block that branches to itself. a call, a return, a stack word, or a + second branch stays where it is. an entry is never pasted over. */ +static int bempty(Rg *R, int b) +{ + RB *B = &R->b[b]; + int j; + if (B->n || B->nso || B->delta || B->term != T_BR) return 0; + if (B->s1 < 0 || B->s1 == b) return 0; + for (j = 0; j < R->nv; j++) if (B->vs[j] >= 0) return 0; + return 1; +} +static int bfollow(Rg *R, int b) +{ + unsigned char *seen = calloc(R->nb, 1); + int n = 0; + if (!seen) return b; + while ((unsigned)b < (unsigned)R->nb && bempty(R, b) && !seen[b] && n++ < R->nb) { + seen[b] = 1; + b = R->b[b].s1; + } + free(seen); + return b; +} +static int breach(Rg *R, unsigned char *reach) +{ + int *st = malloc((R->nb + 1) * sizeof(int)), n = 0, e, b; + if (!st) return -1; + for (e = 0; e < R->ne; e++) { + b = R->entb[e]; + if ((unsigned)b < (unsigned)R->nb && !reach[b]) { reach[b] = 1; st[n++] = b; } + } + while (n) { + RB *B; + b = st[--n]; + B = &R->b[b]; + if ((B->term == T_BR || B->term == T_CBR) && (unsigned)B->s1 < (unsigned)R->nb && !reach[B->s1]) { + reach[B->s1] = 1; st[n++] = B->s1; + } + if (B->term == T_CBR && (unsigned)B->s2 < (unsigned)R->nb && !reach[B->s2]) { + reach[B->s2] = 1; st[n++] = B->s2; + } + } + free(st); + return 0; +} +static int rempty(Rg *R) +{ + int b, e, ch = 0, *to = malloc(R->nb * sizeof(int)); + if (!to) return 0; + for (b = 0; b < R->nb; b++) { + int d = bfollow(R, b); + to[b] = (d != b && (unsigned)d < (unsigned)R->nb && !bempty(R, d)) ? d : b; + } + for (b = 0; b < R->nb; b++) { + RB *B = &R->b[b]; + if ((unsigned)B->s1 < (unsigned)R->nb && to[B->s1] != B->s1) { B->s1 = to[B->s1]; ch = 1; } + if ((unsigned)B->s2 < (unsigned)R->nb && to[B->s2] != B->s2) { B->s2 = to[B->s2]; ch = 1; } + } + for (e = 0; e < R->ne; e++) if ((unsigned)R->entb[e] < (unsigned)R->nb && to[R->entb[e]] != R->entb[e]) { + R->entb[e] = to[R->entb[e]]; ch = 1; + } + free(to); + return ch; +} +static int bmapat(int *map, int nv, int x, int *ok) +{ + if ((unsigned)x >= (unsigned)nv || map[x] < 0) { *ok = 0; return 0; } + return map[x]; +} +/* paste the blocks in order. a VGET of a var this paste already wrote is the + written value, not a fresh read of the block's entry. the last block's + branch is the branch of the result. */ +static int rb_build(Rg *R, int *ix, int nix, RB *dst) +{ + int cap = 0, i, j, n = 0, nid = 0, cur[RMAXV], wrote[RMAXV]; + Uop *o; + for (i = 0; i < nix; i++) { + if ((unsigned)ix[i] >= (unsigned)R->nb) return -1; + cap += R->b[ix[i]].n; + } + if (cap > 2048 || !nix) return -1; + o = malloc((cap + 1) * sizeof(Uop)); + if (!o) return -1; + for (j = 0; j < RMAXV; j++) { cur[j] = -1; wrote[j] = 0; } + for (i = 0; i < nix; i++) { + RB *S = &R->b[ix[i]]; + int nvmap = S->nv > 0 ? S->nv : 1, k, ok; + int *map = malloc(nvmap * sizeof(int)); + if (!map) { free(o); return -1; } + for (k = 0; k < nvmap; k++) map[k] = -1; + for (k = 0; k < S->n; k++) { + Uop u = S->u[k]; + int na = nargs(u.op); + ok = 1; + if (u.op == U_VGET && (unsigned)u.imm < RMAXV && cur[u.imm] >= 0) { + if ((unsigned)u.d >= (unsigned)nvmap) { free(map); free(o); return -1; } + map[u.d] = cur[u.imm]; + continue; + } + if (na > 0) u.a = bmapat(map, nvmap, u.a, &ok); + if (na > 1) u.b = bmapat(map, nvmap, u.b, &ok); + if (na > 2) u.c = bmapat(map, nvmap, u.c, &ok); + if (!ok || n >= cap) { free(map); free(o); return -1; } + if (!is_store(u.op)) { + if ((unsigned)u.d >= (unsigned)nvmap || nid >= 4096) { free(map); free(o); return -1; } + map[u.d] = nid; + u.d = nid++; + } else u.d = 0; + o[n++] = u; + } + for (j = 0; j < R->nv; j++) if (S->vs[j] >= 0) { + if ((unsigned)S->vs[j] >= (unsigned)nvmap || map[S->vs[j]] < 0) { free(map); free(o); return -1; } + cur[j] = map[S->vs[j]]; wrote[j] = 1; + } + if (i + 1 == nix) { + dst->term = S->term; + dst->s1 = S->s1; dst->s2 = S->s2; dst->x1 = S->x1; dst->x2 = S->x2; + if (S->cv >= 0) { + if ((unsigned)S->cv >= (unsigned)nvmap || map[S->cv] < 0) { free(map); free(o); return -1; } + dst->cv = map[S->cv]; + } else dst->cv = -1; + } + free(map); + } + dst->u = o; dst->n = n; dst->nv = nid > 0 ? nid : 1; + dst->nso = 0; dst->so = 0; dst->delta = 0; dst->a = R->b[ix[0]].a; + for (j = 0; j < RMAXV; j++) dst->vs[j] = wrote[j] ? cur[j] : -1; + if (dst->nv > R->maxnv) R->maxnv = dst->nv; + return 0; +} +static void addpred(int *np, int *pr, int s, int from) +{ + np[s]++; + if (np[s] == 1) pr[s] = from; + else if (pr[s] != from) pr[s] = -2; +} +static int rmerge_one(Rg *R) +{ + unsigned char *reach = calloc(R->nb, 1), *ent = calloc(R->nb, 1); + int *np = calloc(R->nb, sizeof(int)), *pr = malloc(R->nb * sizeof(int)); + int b, e, did = 0; + if (!reach || !ent || !np || !pr || breach(R, reach)) goto out; + for (b = 0; b < R->nb; b++) pr[b] = -1; + for (e = 0; e < R->ne; e++) if ((unsigned)R->entb[e] < (unsigned)R->nb) ent[R->entb[e]] = 1; + for (b = 0; b < R->nb; b++) if (reach[b]) { + RB *B = &R->b[b]; + if ((B->term == T_BR || B->term == T_CBR) && (unsigned)B->s1 < (unsigned)R->nb) addpred(np, pr, B->s1, b); + if (B->term == T_CBR && (unsigned)B->s2 < (unsigned)R->nb) addpred(np, pr, B->s2, b); + } + for (b = 0; b < R->nb && !did; b++) { + RB *A = &R->b[b], built; + int s, ix[2]; + if (!reach[b] || A->term != T_BR) continue; + s = A->s1; + if ((unsigned)s >= (unsigned)R->nb || s == b || !reach[s] || ent[s]) continue; + if (np[s] != 1 || pr[s] != b) continue; + if (A->nso || A->delta || R->b[s].nso || R->b[s].delta) continue; + ix[0] = b; ix[1] = s; + memset(&built, 0, sizeof built); + if (rb_build(R, ix, 2, &built)) continue; + free(A->u); free(A->so); + built.a = A->a; + R->b[b] = built; + did = 1; + } +out: + free(reach); free(ent); free(np); free(pr); + return did; +} +/* length of the straight run of single-predecessor jumps from s back to h, + or 0 when s is not that kind of back edge. the other edge may leave the + loop and come back in later through some other header; that is still an + exit of this loop, and the straight edge can be rotated. */ +static int try_chain(Rg *R, int h, int s, int *ch, int *np, int *pr, unsigned char *ent) +{ + int n = 0, prev = h, guard = 0; + if (s < 0 || s == h) return 0; + while (s != h) { + RB *B; + if ((unsigned)s >= (unsigned)R->nb || ++guard > R->nb) return 0; + B = &R->b[s]; + if (ent[s] || np[s] != 1 || pr[s] != prev || B->nso || B->delta || B->term != T_BR) return 0; + ch[n++] = s; + prev = s; + s = B->s1; + } + return n; +} +static int rrotate_one(Rg *R) +{ + unsigned char *reach = calloc(R->nb, 1), *ent = calloc(R->nb, 1); + int *np = calloc(R->nb, sizeof(int)), *pr = malloc(R->nb * sizeof(int)); + int *ch1 = malloc(R->nb * sizeof(int)), *ch2 = malloc(R->nb * sizeof(int)); + int b, e, did = 0; + if (!reach || !ent || !np || !pr || !ch1 || !ch2 || breach(R, reach)) goto out; + for (b = 0; b < R->nb; b++) pr[b] = -1; + for (e = 0; e < R->ne; e++) if ((unsigned)R->entb[e] < (unsigned)R->nb) ent[R->entb[e]] = 1; + for (b = 0; b < R->nb; b++) if (reach[b]) { + RB *B = &R->b[b]; + if ((B->term == T_BR || B->term == T_CBR) && (unsigned)B->s1 < (unsigned)R->nb) addpred(np, pr, B->s1, b); + if (B->term == T_CBR && (unsigned)B->s2 < (unsigned)R->nb) addpred(np, pr, B->s2, b); + } + for (b = 0; b < R->nb && !did; b++) { + RB *H = &R->b[b], built, *grown; + int n1, n2, n, back, L, *ch, *ix, i; + if (!reach[b] || H->term != T_CBR || H->nso || H->delta || H->cv < 0) continue; + n1 = try_chain(R, b, H->s1, ch1, np, pr, ent); + n2 = try_chain(R, b, H->s2, ch2, np, pr, ent); + if (n2 > 0 && n1 == 0) { n = n2; ch = ch2; back = 2; } + else if (n1 > 0 && n2 == 0) { n = n1; ch = ch1; back = 1; } + else continue; + ix = malloc((n + 1) * sizeof(int)); + if (!ix) continue; + for (i = 0; i < n; i++) ix[i] = ch[i]; + ix[n] = b; + memset(&built, 0, sizeof built); + if (rb_build(R, ix, n + 1, &built)) { free(ix); continue; } + free(ix); + grown = realloc(R->b, (R->nb + 1) * sizeof(RB)); + if (!grown) { free(built.u); continue; } + R->b = grown; + L = R->nb; + R->b[L] = built; + R->nb++; + if (back == 2) { R->b[L].s2 = L; R->b[b].s2 = L; } + else { R->b[L].s1 = L; R->b[b].s1 = L; } + did = 1; + } +out: + free(reach); free(ent); free(np); free(pr); free(ch1); free(ch2); + return did; +} +static void rdead(Rg *R) +{ + unsigned char *reach = calloc(R->nb, 1); + int b; + if (!reach || breach(R, reach)) { free(reach); return; } + for (b = 0; b < R->nb; b++) if (!reach[b]) { + R->b[b].term = T_BR; + R->b[b].s1 = b; + R->b[b].s2 = -1; + R->b[b].cv = -1; + } + free(reach); +} +static void rshape(Rg *R) +{ + int i, guard; + if (R->nb < 1) return; + for (i = 0; i < 4; i++) if (!rempty(R)) break; + guard = R->nb + 4; + for (i = 0; i < guard; i++) if (!rmerge_one(R)) break; + guard = R->nb + 4; + for (i = 0; i < guard; i++) if (!rrotate_one(R)) break; + guard = R->nb + 4; + for (i = 0; i < guard; i++) if (!rmerge_one(R)) break; + for (i = 0; i < 4; i++) if (!rempty(R)) break; + rdead(R); +} +/* ---- the global pass ---- + a block only knows its own values, but the variables carry facts from one + block to the next. this pass runs a forward dataflow over the blocks for the + one fact that pays off everywhere: a variable that holds the same constant + on every path into a block. such a read becomes a constant, the block is + emitted again through the same folding and strength reduction the lifter + uses (so x / step turns into a multiply once step is known, and an add of + two known values disappears), and a branch on a known value becomes a jump. + the second fact is a copy: on every path a variable holds what another + variable holds, and neither has been written since. a read of the copy + becomes a read of its source, so the copy can stop being kept at all. a + block that writes the source drops the copies of it. the blocks that no + longer have a way in are dropped by the shaping that follows, and it + repeats while something changes. */ +enum { C_UNSEEN, C_CONST, C_VAR, C_COPY }; +/* C_COPY: the variable holds the same value as variable v (both at the same point) */ +static int copy_ok(const Rg *R, int j, int k) { return j > 0 && j < RMAXV && k > 0 && k < R->nv && !R->fv[j] && !R->fv[k]; } +typedef struct { unsigned char st[RMAXV]; Cell v[RMAXV]; } CIn; +static int g_isk[MAXUOP * 4]; static Cell g_kv[MAXUOP * 4]; static int g_omap[MAXUOP + 16]; +static int cjoin(CIn *d, const CIn *s) +{ + int j, ch = 0; + for (j = 1; j < RMAXV; j++) { + if (s->st[j] == C_UNSEEN) continue; + if (d->st[j] == C_UNSEEN) { d->st[j] = s->st[j]; d->v[j] = s->v[j]; ch = 1; } + else if (d->st[j] != C_VAR && (s->st[j] == C_VAR || s->st[j] != d->st[j] || s->v[j] != d->v[j])) { d->st[j] = C_VAR; ch = 1; } + } + return ch; +} +/* does the block write variable k (a write other than the plain entry read)? */ +static int written_here(const RB *B, int k) +{ + if (k <= 0 || k >= RMAXV) return 1; + if (B->vs[k] < 0) return 0; + return !(B->u[B->vs[k]].op == U_VGET && B->u[B->vs[k]].imm == k); +} +/* what each variable holds when the block ends, given what it held at the start */ +static void cblock(Rg *R, RB *B, const CIn *in, CIn *out) +{ + int i, j, na; + unsigned char *kc = calloc(B->nv + 1, 1); + Cell *kw = calloc(B->nv + 1, sizeof(Cell)); + int *cpk = calloc(B->nv + 1, sizeof(int)); /* cpk[v] = k + 1 when v is a plain read of the entry value of k */ + *out = *in; + if (!kc || !kw || !cpk) { for (j = 1; j < RMAXV; j++) out->st[j] = C_VAR; free(kc); free(kw); free(cpk); return; } + for (i = 0; i < B->n; i++) { + Uop *u = &B->u[i]; + if (is_store(u->op) || u->op == U_NARG) continue; + if ((unsigned)u->d >= (unsigned)B->nv) continue; + if (u->op == U_LI) { kc[u->d] = 1; kw[u->d] = u->imm; continue; } + if (u->op == U_VGET) { + j = (int)u->imm; + if (j > 0 && j < RMAXV && in->st[j] == C_CONST && !R->fv[j]) { kc[u->d] = 1; kw[u->d] = in->v[j]; } + if (j > 0 && j < RMAXV && in->st[j] == C_COPY && copy_ok(R, j, (int)in->v[j])) cpk[u->d] = (int)in->v[j] + 1; + else if (j > 0 && j < RMAXV) cpk[u->d] = j + 1; + continue; + } + if (u->op >= U_N || is_memop(u->op)) continue; + na = nargs(u->op); + if ((na > 0 && !kc[u->a]) || (na > 1 && !kc[u->b]) || (na > 2 && !kc[u->c])) continue; + kc[u->d] = 1; kw[u->d] = ev(u->op, na > 0 ? kw[u->a] : 0, na > 1 ? kw[u->b] : 0, na > 2 ? kw[u->c] : 0); + } + /* a variable the block writes is a copy of k when its value is the entry value of k and k is not written here */ + for (j = 1; j < R->nv; j++) if (B->vs[j] >= 0) { + int w = B->vs[j], k; + if (kc[w] && !R->fv[j]) { out->st[j] = C_CONST; out->v[j] = kw[w]; } + else if (w < B->nv && cpk[w] && (k = cpk[w] - 1) != j && copy_ok(R, j, k) && !written_here(B, k)) { out->st[j] = C_COPY; out->v[j] = k; } + else out->st[j] = C_VAR; + } + /* a copy of a variable this block writes is gone; so is a copy whose variable the block writes */ + for (j = 1; j < RMAXV; j++) if (out->st[j] == C_COPY && written_here(B, (int)out->v[j])) out->st[j] = C_VAR; + free(kc); free(kw); free(cpk); +} +/* emit the block again with the known variables as constants. returns 1 if it changed, -1 if it could not be done */ +static int rb_refold(Rg *R, RB *B, const CIn *in) +{ + int i, j, na, any = 0, n = 0, *live, *mp, bad = 0; + int *sisk = isk, *somap = omap; Cell *skv = kv; + Uop *nu; + for (i = 0; i < B->n; i++) if (B->u[i].op == U_VGET) { + j = (int)B->u[i].imm; + if (j > 0 && j < RMAXV && in->st[j] == C_CONST && !R->fv[j]) any = 1; + if (j > 0 && j < RMAXV && in->st[j] == C_COPY && copy_ok(R, j, (int)in->v[j])) any = 1; + } + if (!any) return 0; + for (j = 0; j < B->nso; j++) if ((unsigned)B->so[2 * j + 1] >= (unsigned)B->nv) bad = 1; + for (j = 1; j < R->nv; j++) if (B->vs[j] >= B->nv) bad = 1; + if (B->cv >= B->nv) bad = 1; + for (i = 0; i < B->n; i++) if ((unsigned)B->u[i].d >= (unsigned)B->nv && !is_store(B->u[i].op) && B->u[i].op != U_NARG) bad = 1; + if (bad) return 0; + mp = malloc((B->nv + 1) * sizeof(int)); + if (!mp) return 0; + isk = g_isk; kv = g_kv; omap = g_omap; + on = 0; onv = 0; uerr = 0; + for (i = 0; i < B->n && !uerr; i++) { + Uop *u = &B->u[i]; + na = u->op >= U_N ? 0 : nargs(u->op); + if (u->op == U_VGET) { + j = (int)u->imm; + if (j > 0 && j < RMAXV && in->st[j] == C_CONST && !R->fv[j]) mp[u->d] = K(in->v[j]); + else if (j > 0 && j < RMAXV && in->st[j] == C_COPY && copy_ok(R, j, (int)in->v[j])) mp[u->d] = raw(U_VGET, 0, 0, 0, in->v[j]); + else mp[u->d] = raw(U_VGET, 0, 0, 0, u->imm); + } else if (u->op == U_SLD) mp[u->d] = raw(U_SLD, 0, 0, 0, u->imm); + else if (u->op == U_LI) mp[u->d] = K(u->imm); + else if (is_store(u->op)) oe(u->op, mp[u->a], mp[u->b], 0, 0); + else if (u->op == U_NARG) raw(U_NARG, mp[u->a], 0, 0, u->imm); + else if (u->op == U_NCALL) mp[u->d] = raw(U_NCALL, 0, 0, 0, u->imm); + else mp[u->d] = oe(u->op, na > 0 ? mp[u->a] : 0, na > 1 ? mp[u->b] : 0, na > 2 ? mp[u->c] : 0, u->imm); + } + if (uerr) { isk = sisk; kv = skv; omap = somap; free(mp); return 0; } + live = calloc(onv + 2, sizeof *live); + nu = malloc((on + 1) * sizeof(Uop)); + if (!live || !nu) { free(live); free(nu); isk = sisk; kv = skv; omap = somap; free(mp); return 0; } + for (j = 0; j < B->nso; j++) B->so[2 * j + 1] = mp[B->so[2 * j + 1]]; + for (j = 1; j < R->nv; j++) if (B->vs[j] >= 0) B->vs[j] = mp[B->vs[j]]; + if (B->cv >= 0) B->cv = mp[B->cv]; + /* a branch on a known value is a jump, when the way it goes stays in the region; otherwise the exit */ + if (B->term == T_CBR && B->cv >= 0 && g_isk[B->cv]) { + int zero = g_kv[B->cv] == 0, to = zero ? B->s1 : B->s2, x = zero ? B->x1 : B->x2; + if (to >= 0) { B->term = T_BR; B->s1 = to; B->s2 = -1; B->cv = -1; } + else { B->cv = K(x); B->term = T_JMP; B->s1 = B->s2 = -1; if (onv + 2 > 0) { free(live); live = calloc(onv + 2, sizeof *live); free(nu); nu = malloc((on + 1) * sizeof(Uop)); } } + } + for (j = 1; j < R->nv; j++) if (B->vs[j] >= 0 && ob[B->vs[j]].op == U_VGET && ob[B->vs[j]].imm == j) B->vs[j] = -1; + for (j = 0; j < B->nso; j++) live[B->so[2 * j + 1]] = 1; + for (j = 1; j < R->nv; j++) if (B->vs[j] >= 0) live[B->vs[j]] = 1; + if ((B->term == T_CBR || B->term == T_JMP || B->term == T_CALL) && B->cv >= 0) live[B->cv] = 1; + for (i = on; i--;) { + Uop *u = &ob[i]; + if (!is_eff(u->op) && !live[u->d]) { u->op = -1; continue; } + na = u->op >= U_N ? 0 : nargs(u->op); + if (is_store(u->op)) na = 2; + if (u->op == U_NARG) na = 1; + if (na > 0) live[u->a] = 1; + if (na > 1) live[u->b] = 1; + if (na > 2) live[u->c] = 1; + } + for (i = 0; i < on; i++) if (ob[i].op >= 0) nu[n++] = ob[i]; + free(B->u); B->u = nu; B->n = n; B->nv = onv; + if (onv > R->maxnv) R->maxnv = onv; + free(live); free(mp); + isk = sisk; kv = skv; omap = somap; + return 1; +} +static void rglobal(Rg *R) +{ + int round, b, e, s, changed, sweeps, did; + if (R->nb < 1 || R->nb > 600) return; + for (round = 0; round < 4; round++) { + CIn *in = calloc(R->nb, sizeof(CIn)), out; + unsigned char *reached = calloc(R->nb, 1); + if (!in || !reached) { free(in); free(reached); return; } + for (e = 0; e < R->ne; e++) { + b = R->entb[e]; + if ((unsigned)b < (unsigned)R->nb) { + int j; + reached[b] = 1; + for (j = 1; j < RMAXV; j++) in[b].st[j] = C_VAR; + } + } + sweeps = 0; changed = 1; + while (changed && sweeps++ < 64) { + changed = 0; + for (b = 0; b < R->nb; b++) { + RB *B = &R->b[b]; + if (!reached[b]) continue; + cblock(R, B, &in[b], &out); + if (B->term != T_BR && B->term != T_CBR) continue; + for (s = 0; s < 2; s++) { + int t = s ? B->s2 : B->s1; + if (s && B->term != T_CBR) break; + if ((unsigned)t >= (unsigned)R->nb) continue; + if (!reached[t]) { reached[t] = 1; in[t] = out; changed = 1; } + else if (cjoin(&in[t], &out)) changed = 1; + } + } + } + did = 0; + for (b = 0; b < R->nb; b++) if (reached[b]) did |= rb_refold(R, &R->b[b], &in[b]); + free(in); free(reached); + if (!did) return; + rshape(R); + } +} +/* ---- loops, shared values and dead variables ---- + what the per-block lifter and the constant pass can't see is work that is + done again: an expression inside a loop whose operands don't change in it, + the same expression (or the same load, when nothing stored in between) in + two blocks one of which always runs before the other, and a variable that + is written and then written again before anyone reads it. blocks share + only variables, so a value that has to cross a block edge needs one: the + passes below take a spare register for it (a region has at most 7). each + step moves one expression and the analysis is run again, so a later step + sees what the earlier one did. */ +typedef struct { + int nb, *idom, *rn, *rpo, nrpo, *dep, nl; + int *pp, *pl; /* predecessors of b: pl[pp[b] .. pp[b + 1]); the root, nb, is left out */ + unsigned char *reach, *isent; + struct { int h, n, bad; unsigned char *in; } *lp; +} Cfg; +static int csucc(Rg *R, int b, int *o) +{ + int n = 0, e; + if (b == R->nb) { + for (e = 0; e < R->ne; e++) if ((unsigned)R->entb[e] < (unsigned)R->nb) o[n++] = R->entb[e]; + return n; + } + { + RB *B = &R->b[b]; + if ((B->term == T_BR || B->term == T_CBR) && (unsigned)B->s1 < (unsigned)R->nb) o[n++] = B->s1; + if (B->term == T_CBR && (unsigned)B->s2 < (unsigned)R->nb) o[n++] = B->s2; + } + return n; +} +static void cfg_free(Cfg *G) +{ + int i; + if (!G) return; + for (i = 0; i < G->nl; i++) free(G->lp[i].in); + free(G->lp); free(G->idom); free(G->rn); free(G->rpo); free(G->dep); free(G->pp); free(G->pl); free(G->reach); free(G->isent); free(G); +} +static int cdom(const Cfg *G, int a, int b) +{ + int n = 0; + while (b != a) { + if (b >= G->nb || b < 0 || G->idom[b] < 0 || ++n > G->nb + 2) return 0; + b = G->idom[b]; + } + return 1; +} +static Cfg *cfg_build(Rg *R) +{ + Cfg *G = calloc(1, sizeof *G); + int nb = R->nb, b, i, n, e, k, sp, succ[RMAXE + 2], changed, *po, *stn, *sti, npo = 0, guard; + if (!G) return 0; + G->nb = nb; + G->idom = malloc((nb + 1) * sizeof(int)); G->rn = malloc((nb + 1) * sizeof(int)); G->rpo = malloc((nb + 2) * sizeof(int)); + G->dep = calloc(nb + 1, sizeof(int)); G->pp = calloc(nb + 2, sizeof(int)); + G->reach = calloc(nb + 1, 1); G->isent = calloc(nb + 1, 1); + po = malloc((nb + 2) * sizeof(int)); stn = malloc((nb + 2) * sizeof(int)); sti = malloc((nb + 2) * sizeof(int)); + if (!G->idom || !G->rn || !G->rpo || !G->dep || !G->pp || !G->reach || !G->isent || !po || !stn || !sti) { free(po); free(stn); free(sti); cfg_free(G); return 0; } + for (b = 0; b <= nb; b++) { G->idom[b] = -1; G->rn[b] = -1; } + for (e = 0; e < R->ne; e++) if ((unsigned)R->entb[e] < (unsigned)nb) G->isent[R->entb[e]] = 1; + /* postorder from the root */ + sp = 0; stn[0] = nb; sti[0] = 0; G->reach[nb] = 1; + while (sp >= 0) { + n = csucc(R, stn[sp], succ); + if (sti[sp] < n) { + int s = succ[sti[sp]++]; + if (!G->reach[s]) { G->reach[s] = 1; sp++; stn[sp] = s; sti[sp] = 0; } + } else { po[npo++] = stn[sp]; sp--; } + } + G->nrpo = npo; + for (i = 0; i < npo; i++) { G->rpo[i] = po[npo - 1 - i]; G->rn[G->rpo[i]] = i; } + free(po); free(stn); free(sti); + /* predecessors (the root is not one) */ + for (b = 0; b < nb; b++) if (G->reach[b]) { n = csucc(R, b, succ); for (i = 0; i < n; i++) G->pp[succ[i] + 1]++; } + for (b = 0; b < nb; b++) G->pp[b + 1] += G->pp[b]; + G->pl = malloc((G->pp[nb] + 1) * sizeof(int)); + { + int *fill = calloc(nb + 1, sizeof(int)); + if (!G->pl || !fill) { free(fill); cfg_free(G); return 0; } + for (b = 0; b < nb; b++) if (G->reach[b]) { n = csucc(R, b, succ); for (i = 0; i < n; i++) { int s = succ[i]; G->pl[G->pp[s] + fill[s]++] = b; } } + free(fill); + } + /* dominators: the root dominates the entries, and the entries are roots of the graph */ + G->idom[nb] = nb; + guard = 0; changed = 1; + while (changed && guard++ < 64) { + changed = 0; + for (k = 1; k < npo; k++) { + int nw = -1, p, j; + b = G->rpo[k]; + if (G->isent[b]) nw = nb; + for (j = G->pp[b]; j < G->pp[b + 1]; j++) { + p = G->pl[j]; + if (G->idom[p] < 0) continue; + if (nw < 0) { nw = p; continue; } + { + int x = p, y = nw, g2 = 0; + while (x != y && g2++ < 2 * nb + 4) { + while (G->rn[x] > G->rn[y] && g2++ < 2 * nb + 4) x = G->idom[x]; + while (G->rn[y] > G->rn[x] && g2++ < 2 * nb + 4) y = G->idom[y]; + } + nw = x; + } + } + if (nw >= 0 && G->idom[b] != nw) { G->idom[b] = nw; changed = 1; } + } + } + if (changed) { cfg_free(G); return 0; } + /* natural loops: a back edge is one whose target dominates its source */ + G->lp = calloc(nb + 1, sizeof *G->lp); + if (!G->lp) { cfg_free(G); return 0; } + for (b = 0; b < nb; b++) if (G->reach[b]) { + n = csucc(R, b, succ); + for (i = 0; i < n; i++) { + int h = succ[i], li, *stk, ns = 0, x, j; + if (!cdom(G, h, b)) continue; + for (li = 0; li < G->nl && G->lp[li].h != h; li++); + if (li == G->nl) { + G->lp[li].h = h; G->lp[li].in = calloc(nb + 1, 1); G->nl++; + if (!G->lp[li].in) { cfg_free(G); return 0; } + G->lp[li].in[h] = 1; G->lp[li].n = 1; + } + stk = malloc((nb + 1) * sizeof(int)); + if (!stk) { cfg_free(G); return 0; } + if (!G->lp[li].in[b]) { G->lp[li].in[b] = 1; G->lp[li].n++; stk[ns++] = b; } + while (ns) { + x = stk[--ns]; + for (j = G->pp[x]; j < G->pp[x + 1]; j++) { + int p = G->pl[j]; + if (G->reach[p] && !G->lp[li].in[p]) { G->lp[li].in[p] = 1; G->lp[li].n++; stk[ns++] = p; } + } + } + free(stk); + } + } + for (i = 0; i < G->nl; i++) for (b = 0; b < nb; b++) if (G->lp[i].in[b]) { + G->dep[b]++; + if (G->isent[b]) G->lp[i].bad = 1; + } + return G; +} +static int w10(int d) { return d <= 0 ? 1 : d == 1 ? 10 : d == 2 ? 100 : d == 3 ? 1000 : 10000; } +static int opcost(int op) +{ + switch (op) { + case U_LI: case U_VGET: case U_SLD: return 0; + case U_LD: case U_LD8: case U_LD16: case U_LD32: case U_MUL: case U_MULHS: case U_MULHU: case U_FMUL: return 3; + case U_SDIV: case U_UDIV: case U_SREM: case U_UREM: case U_FDIV: return 10; + default: return 1; + } +} +/* value numbering: a table of what each number stands for */ +#define GOP_MEM (U_N + 2) +#define GOP_PHI (U_N + 3) +#define GOP_ENT (U_N + 4) +#define GOP_PHIM (U_N + 5) +#define GNMAX (1 << 17) +typedef struct { int op, a, b, c, next; Cell imm; } GN; +static GN *gn_; static int gn_n, *gh_, gn_over; +static int gn_init(void) +{ + if (!gn_) { gn_ = malloc(GNMAX * sizeof(GN)); gh_ = malloc(GNMAX * sizeof(int)); } + if (!gn_ || !gh_) return -1; + gn_n = 0; gn_over = 0; memset(gh_, 0, GNMAX * sizeof(int)); + return 0; +} +static int gnum(int op, int a, int b, int c, Cell imm) +{ + unsigned h; int i; + if (op == U_ADD || op == U_MUL || op == U_AND || op == U_OR || op == U_XOR || op == U_EQ || op == U_NE || + op == U_MIN || op == U_MAX || op == U_MINU || op == U_MAXU || op == U_MULHS || op == U_MULHU || + op == U_FADD || op == U_FMUL || op == U_FEQ) if (a > b) { int t = a; a = b; b = t; } + h = ((unsigned)op * 2654435761u) ^ ((unsigned)a * 40503u) ^ ((unsigned)b * 69069u) ^ ((unsigned)c * 1103515245u) ^ (unsigned)(imm * 0x9e3779b1u) ^ (unsigned)((UCell)imm >> 32); + h %= GNMAX; + for (i = gh_[h]; i; i = gn_[i - 1].next) + if (gn_[i - 1].op == op && gn_[i - 1].a == a && gn_[i - 1].b == b && gn_[i - 1].c == c && gn_[i - 1].imm == imm) return i; + if (gn_n >= GNMAX - 1) { gn_over = 1; return 1; } + gn_[gn_n].op = op; gn_[gn_n].a = a; gn_[gn_n].b = b; gn_[gn_n].c = c; gn_[gn_n].imm = imm; + gn_[gn_n].next = gh_[h]; gh_[h] = ++gn_n; + return gn_n; +} +typedef struct { RB *src; int val, var; } Hoist; /* write the value src computes into var, in this block */ +typedef struct { int idx, var; } Export; /* after the block is built, var := what its idx-th op computed */ +/* the value val of block S built again inside the block being emitted */ +static int matv(const RB *P, const int *mp, RB *S, const int *def, int *memo, int val) +{ + Uop *u = &S->u[def[val]]; + int na, a = 0, b = 0, c = 0, r; + if (memo[val] >= 0) return memo[val]; + if (u->op == U_LI) r = K(u->imm); + else if (u->op == U_VGET) { + int j = (int)u->imm; + r = j >= 0 && j < RMAXV && P->vs[j] >= 0 ? mp[P->vs[j]] : raw(U_VGET, 0, 0, 0, u->imm); + } else { + na = nargs(u->op); + if (na > 0) a = matv(P, mp, S, def, memo, u->a); + if (na > 1) b = matv(P, mp, S, def, memo, u->b); + if (na > 2) c = matv(P, mp, S, def, memo, u->c); + r = oe(u->op, a, b, c, u->imm); + } + memo[val] = r; + return r; +} +/* the block again through the folding emitter. repl[i] (when nonzero) makes the i-th op a read of that + variable; the hoists and exports give variables new values at the end. returns 1 if it was rebuilt */ +static int rb_emit(Rg *R, RB *B, const int *repl, const Hoist *H, int nh, const Export *X, int nx) +{ + int i, j, na, n = 0, *live, *mp, bad = 0, *hv = 0, *xv = 0; + int *sisk = isk, *somap = omap; Cell *skv = kv; + Uop *nu; + for (j = 0; j < B->nso; j++) if ((unsigned)B->so[2 * j + 1] >= (unsigned)B->nv) bad = 1; + for (j = 1; j < RMAXV; j++) if (B->vs[j] >= B->nv) bad = 1; + if (B->cv >= B->nv) bad = 1; + for (i = 0; i < B->n; i++) if ((unsigned)B->u[i].d >= (unsigned)B->nv && !is_store(B->u[i].op) && B->u[i].op != U_NARG) bad = 1; + if (bad) return 0; + mp = malloc((B->nv + 1) * sizeof(int)); + hv = malloc((nh + 1) * sizeof(int)); xv = malloc((nx + 1) * sizeof(int)); + if (!mp || !hv || !xv) { free(mp); free(hv); free(xv); return 0; } + isk = g_isk; kv = g_kv; omap = g_omap; + on = 0; onv = 0; uerr = 0; + for (i = 0; i < B->n && !uerr; i++) { + Uop *u = &B->u[i]; + na = u->op >= U_N ? 0 : nargs(u->op); + if (repl && repl[i] && !is_store(u->op) && u->op != U_NARG && u->op != U_NCALL) mp[u->d] = raw(U_VGET, 0, 0, 0, repl[i]); + else if (u->op == U_VGET) mp[u->d] = raw(U_VGET, 0, 0, 0, u->imm); + else if (u->op == U_SLD) mp[u->d] = raw(U_SLD, 0, 0, 0, u->imm); + else if (u->op == U_LI) mp[u->d] = K(u->imm); + else if (is_store(u->op)) oe(u->op, mp[u->a], mp[u->b], 0, 0); + else if (u->op == U_NARG) raw(U_NARG, mp[u->a], 0, 0, u->imm); + else if (u->op == U_NCALL) mp[u->d] = raw(U_NCALL, 0, 0, 0, u->imm); + else mp[u->d] = oe(u->op, na > 0 ? mp[u->a] : 0, na > 1 ? mp[u->b] : 0, na > 2 ? mp[u->c] : 0, u->imm); + } + for (j = 0; j < nh && !uerr; j++) { + RB *S = H[j].src; + int *def = malloc((S->nv + 1) * sizeof(int)), *memo = malloc((S->nv + 1) * sizeof(int)); + if (!def || !memo) { free(def); free(memo); uerr = -5; break; } + for (i = 0; i < S->nv; i++) { def[i] = 0; memo[i] = -1; } + for (i = 0; i < S->n; i++) if (!is_store(S->u[i].op) && S->u[i].op != U_NARG && (unsigned)S->u[i].d < (unsigned)S->nv) def[S->u[i].d] = i; + hv[j] = matv(B, mp, S, def, memo, H[j].val); + free(def); free(memo); + } + for (j = 0; j < nx; j++) xv[j] = mp[B->u[X[j].idx].d]; + if (uerr) { isk = sisk; kv = skv; omap = somap; free(mp); free(hv); free(xv); return 0; } + live = calloc(onv + 2, sizeof *live); + nu = malloc((on + 1) * sizeof(Uop)); + if (!live || !nu) { free(live); free(nu); isk = sisk; kv = skv; omap = somap; free(mp); free(hv); free(xv); return 0; } + for (j = 0; j < B->nso; j++) B->so[2 * j + 1] = mp[B->so[2 * j + 1]]; + for (j = 1; j < RMAXV; j++) if (B->vs[j] >= 0) B->vs[j] = mp[B->vs[j]]; + if (B->cv >= 0) B->cv = mp[B->cv]; + for (j = 0; j < nh; j++) B->vs[H[j].var] = hv[j]; + for (j = 0; j < nx; j++) B->vs[X[j].var] = xv[j]; + for (j = 1; j < RMAXV; j++) if (B->vs[j] >= 0 && ob[B->vs[j]].op == U_VGET && ob[B->vs[j]].imm == j) B->vs[j] = -1; + for (j = 0; j < B->nso; j++) live[B->so[2 * j + 1]] = 1; + for (j = 1; j < RMAXV; j++) if (B->vs[j] >= 0) live[B->vs[j]] = 1; + if ((B->term == T_CBR || B->term == T_JMP || B->term == T_CALL) && B->cv >= 0) live[B->cv] = 1; + for (i = on; i--;) { + Uop *u = &ob[i]; + if (!is_eff(u->op) && !live[u->d]) { u->op = -1; continue; } + na = u->op >= U_N ? 0 : nargs(u->op); + if (is_store(u->op)) na = 2; + if (u->op == U_NARG) na = 1; + if (na > 0) live[u->a] = 1; + if (na > 1) live[u->b] = 1; + if (na > 2) live[u->c] = 1; + } + for (i = 0; i < on; i++) if (ob[i].op >= 0) nu[n++] = ob[i]; + free(B->u); B->u = nu; B->n = n; B->nv = onv; + if (onv > R->maxnv) R->maxnv = onv; + free(live); free(mp); free(hv); free(xv); + isk = sisk; kv = skv; omap = somap; + return 1; +} +/* a new variable for a value that has to cross blocks: 0 if the region has no register left */ +static int newcarry(Rg *R) +{ + int v; + if (R->nv >= RMAXV) return 0; + v = R->nv++; + R->slot[v] = R->np ? R->ps[0] : 0; R->fv[v] = 0; R->sc[v] = 1; + return v; +} +/* ---- hoisting: an expression in a loop whose operands the loop doesn't change is built once, in front of it ---- */ +static int licm_loop(Rg *R, Cfg *G, int li) +{ + int nb = R->nb, b, i, j, k, W = 0, hasmem = 0, h = G->lp[li].h, nc = 0, best = -1, P = -1, nout = 0, outp[64]; + unsigned char *in = G->lp[li].in; + struct { int kid, cost, b, d; long long save; } cand[64]; + long long bestsave = 0; + if (G->lp[li].bad || R->nv >= RMAXV) return 0; + for (b = 0; b < nb; b++) if (in[b]) { + RB *B = &R->b[b]; + for (j = 1; j < RMAXV; j++) if (B->vs[j] >= 0) W |= 1 << j; + for (i = 0; i < B->n; i++) if (is_store(B->u[i].op) || B->u[i].op == U_NARG || B->u[i].op == U_NCALL) hasmem = 1; + if (B->term == T_CALL) return 0; + } + for (j = G->pp[h]; j < G->pp[h + 1]; j++) if (!in[G->pl[j]]) { if (nout < 64) outp[nout] = G->pl[j]; nout++; } + if (nout < 1 || nout > 64) return 0; + if (gn_init()) return 0; + for (b = 0; b < nb; b++) if (in[b]) { + RB *B = &R->b[b]; + int n = B->nv + 1, *kid = calloc(n, sizeof(int)), *cst = calloc(n, sizeof(int)), *used = calloc(n, sizeof(int)); + unsigned char *inv = calloc(n, 1), *fpv = calloc(n, 1); + if (!kid || !cst || !used || !inv || !fpv) { free(kid); free(cst); free(used); free(inv); free(fpv); return 0; } + class_fp(R, B, fpv); + for (i = 0; i < B->n; i++) { + Uop *u = &B->u[i]; + int d = u->d, na; + if (is_store(u->op) || u->op == U_NARG || u->op == U_NCALL || (unsigned)d >= (unsigned)B->nv) continue; + if (u->op == U_LI) { inv[d] = 1; kid[d] = gnum(U_LI, 0, 0, 0, u->imm); } + else if (u->op == U_VGET) { + j = (int)u->imm; + inv[d] = j >= 0 && j < RMAXV && !(W >> j & 1) && !fpv[d]; + kid[d] = gnum(U_VGET, 0, 0, 0, u->imm); + } else if (u->op == U_SLD || u->op >= U_N) inv[d] = 0; + else { + na = nargs(u->op); + inv[d] = (na < 1 || inv[u->a]) && (na < 2 || inv[u->b]) && (na < 3 || inv[u->c]) && !fpv[d]; + if (is_memop(u->op) && hasmem) inv[d] = 0; + if (inv[d]) { + kid[d] = gnum(u->op, na > 0 ? kid[u->a] : 0, na > 1 ? kid[u->b] : 0, na > 2 ? kid[u->c] : 0, u->imm); + cst[d] = opcost(u->op) + (na > 0 ? cst[u->a] : 0) + (na > 1 ? cst[u->b] : 0) + (na > 2 ? cst[u->c] : 0); + } + } + } + for (i = 0; i < B->n; i++) { + Uop *u = &B->u[i]; + int na; + if (is_store(u->op)) { used[u->a] = 1; used[u->b] = 1; continue; } + if (u->op == U_NARG) { used[u->a] = 1; continue; } + if (u->op == U_NCALL || (unsigned)u->d >= (unsigned)B->nv || inv[u->d]) continue; + na = u->op >= U_N ? 0 : nargs(u->op); + if (na > 0) used[u->a] = 1; + if (na > 1) used[u->b] = 1; + if (na > 2) used[u->c] = 1; + } + for (j = 1; j < RMAXV; j++) if (B->vs[j] >= 0) used[B->vs[j]] = 1; + for (j = 0; j < B->nso; j++) used[B->so[2 * j + 1]] = 1; + if (B->cv >= 0 && (B->term == T_CBR || B->term == T_JMP || B->term == T_CALL)) used[B->cv] = 1; + for (i = 0; i < B->n; i++) { + Uop *u = &B->u[i]; + int d = u->d; + if (is_store(u->op) || u->op == U_NARG || u->op == U_NCALL || (unsigned)d >= (unsigned)B->nv) continue; + if (!inv[d] || !used[d] || cst[d] < 2 || u->op == U_LI || u->op == U_VGET) continue; + for (k = 0; k < nc && cand[k].kid != kid[d]; k++); + if (k == nc) { + if (nc >= 64) continue; + cand[nc].kid = kid[d]; cand[nc].cost = cst[d]; cand[nc].b = b; cand[nc].d = d; cand[nc].save = 0; nc++; + } + cand[k].save += (long long)cst[d] * w10(G->dep[b]); + } + free(kid); free(cst); free(used); free(inv); free(fpv); + } + if (gn_over) return 0; + for (k = 0; k < nc; k++) { + long long net = cand[k].save - (long long)(cand[k].cost + 1) * w10(G->dep[h] - 1); + if (net > bestsave) { bestsave = net; best = k; } + } + if (best < 0 || bestsave < 3) return 0; + /* where it goes: the one block outside the loop that falls into it, or a new one */ + if (nout == 1) { + RB *Pb = &R->b[outp[0]]; + if ((Pb->term == T_BR && Pb->s1 == h) || Pb->term == T_CBR) P = outp[0]; + } + if (P < 0) { + RB *grown = realloc(R->b, (nb + 1) * sizeof(RB)), *N; + if (!grown) return 0; + R->b = grown; + N = &R->b[nb]; + memset(N, 0, sizeof *N); + N->a = R->b[h].a; N->u = malloc(sizeof(Uop)); N->so = malloc(sizeof(int)); N->n = 0; N->nv = 1; N->term = T_BR; N->s1 = h; N->s2 = -1; N->cv = -1; + for (j = 0; j < RMAXV; j++) N->vs[j] = -1; + for (j = 0; j < nout; j++) { + RB *Q = &R->b[outp[j]]; + if (Q->s1 == h) Q->s1 = nb; + if (Q->term == T_CBR && Q->s2 == h) Q->s2 = nb; + } + R->nb = nb + 1; + P = nb; + } + { + int s = newcarry(R); + Hoist hs; + RB *src = &R->b[cand[best].b]; + if (!s) return 0; + hs.src = src; hs.val = cand[best].d; hs.var = s; + if (!rb_emit(R, &R->b[P], 0, &hs, 1, 0, 0)) { R->nv--; return 0; } + /* every block of the loop that builds the same thing reads the new variable instead */ + for (b = 0; b < R->nb; b++) if (b < nb && in[b]) { + RB *B = &R->b[b]; + int n = B->nv + 1, *kid = calloc(n, sizeof(int)), *rp = calloc(B->n + 1, sizeof(int)), any = 0; + unsigned char *inv = calloc(n, 1), *fpv = calloc(n, 1); + if (!kid || !rp || !inv || !fpv) { free(kid); free(rp); free(inv); free(fpv); continue; } + class_fp(R, B, fpv); + for (i = 0; i < B->n; i++) { + Uop *u = &B->u[i]; + int d = u->d, na; + if (is_store(u->op) || u->op == U_NARG || u->op == U_NCALL || (unsigned)d >= (unsigned)B->nv) continue; + if (u->op == U_LI) { inv[d] = 1; kid[d] = gnum(U_LI, 0, 0, 0, u->imm); } + else if (u->op == U_VGET) { j = (int)u->imm; inv[d] = j >= 0 && j < RMAXV && !(W >> j & 1) && !fpv[d]; kid[d] = gnum(U_VGET, 0, 0, 0, u->imm); } + else if (u->op == U_SLD || u->op >= U_N) inv[d] = 0; + else { + na = nargs(u->op); + inv[d] = (na < 1 || inv[u->a]) && (na < 2 || inv[u->b]) && (na < 3 || inv[u->c]) && !fpv[d]; + if (is_memop(u->op) && hasmem) inv[d] = 0; + if (inv[d]) { + kid[d] = gnum(u->op, na > 0 ? kid[u->a] : 0, na > 1 ? kid[u->b] : 0, na > 2 ? kid[u->c] : 0, u->imm); + if (kid[d] == cand[best].kid && u->op != U_LI && u->op != U_VGET) { rp[i] = s; any = 1; } + } + } + } + if (any) rb_emit(R, B, rp, 0, 0, 0, 0); + free(kid); free(rp); free(inv); free(fpv); + } + } + return 1; +} + +/* ---- shared expressions: the same value built in two blocks, one of which always runs first ---- */ +typedef struct { int g[RMAXV]; int mem; } GS; +static void gtrans(Rg *R, int b, const GS *in, GS *out, int *vg, int *vc) +{ + RB *B = &R->b[b]; + int i, j, mem = in->mem; + for (i = 0; i < B->n; i++) { + Uop *u = &B->u[i]; + int op = u->op, d = u->d, na; + if (op == U_NARG) continue; + if (is_store(op)) { mem = gnum(GOP_MEM, b, i, 0, 0); continue; } + if ((unsigned)d >= (unsigned)B->nv) continue; + vc[d] = 0; + if (op == U_LI) vg[d] = gnum(U_LI, 0, 0, 0, u->imm); + else if (op == U_VGET) vg[d] = in->g[(int)u->imm & (RMAXV - 1)]; + else if (op == U_SLD) vg[d] = gnum(U_SLD, b, 0, 0, u->imm); + else if (op == U_NCALL) { vg[d] = gnum(U_NCALL, b, i, 0, 0); mem = gnum(GOP_MEM, b, i, 1, 0); } + else if (op == U_LD || op == U_LD8 || op == U_LD16 || op == U_LD32) { vg[d] = gnum(op, vg[u->a], mem, 0, 0); vc[d] = opcost(op) + vc[u->a]; } + else { + na = nargs(op); + vg[d] = gnum(op, na > 0 ? vg[u->a] : 0, na > 1 ? vg[u->b] : 0, na > 2 ? vg[u->c] : 0, u->imm); + vc[d] = opcost(op) + (na > 0 ? vc[u->a] : 0) + (na > 1 ? vc[u->b] : 0) + (na > 2 ? vc[u->c] : 0); + } + } + *out = *in; + for (j = 1; j < RMAXV; j++) if (B->vs[j] >= 0 && (unsigned)B->vs[j] < (unsigned)B->nv) out->g[j] = vg[B->vs[j]]; + out->mem = mem; +} +static int gjoin(GS *d, const GS *s, int blk) +{ + int v, ch = 0, p; + for (v = 0; v < RMAXV; v++) if (d->g[v] != s->g[v]) { + p = gnum(GOP_PHI, blk, v, 0, 0); + if (d->g[v] != p) { d->g[v] = p; ch = 1; } + } + if (d->mem != s->mem) { + p = gnum(GOP_PHIM, blk, 0, 0, 0); + if (d->mem != p) { d->mem = p; ch = 1; } + } + return ch; +} +typedef struct { int g, b, i, cost; } Site; +static int sitecmp(const void *x, const void *y) +{ + const Site *a = x, *b = y; + if (a->g != b->g) return a->g < b->g ? -1 : 1; + if (a->b != b->b) return a->b < b->b ? -1 : 1; + return a->i - b->i; +} +static int cse_one(Rg *R, Cfg *G) +{ + int nb = R->nb, b, i, e, k, sweeps, changed, ns = 0, capn = 0, s, did = 0; + GS *in = 0, *out = 0; + unsigned char *seen = 0; + int **vgs = 0, **vcs = 0; + Site *sites = 0; + if (R->nv >= RMAXV || gn_init()) return 0; + in = calloc(nb, sizeof(GS)); out = calloc(nb, sizeof(GS)); seen = calloc(nb, 1); + vgs = calloc(nb, sizeof(int *)); vcs = calloc(nb, sizeof(int *)); + if (!in || !out || !seen || !vgs || !vcs) goto fin; + for (b = 0; b < nb; b++) { + vgs[b] = calloc(R->b[b].nv + 1, sizeof(int)); vcs[b] = calloc(R->b[b].nv + 1, sizeof(int)); + if (!vgs[b] || !vcs[b]) goto fin; + } + for (e = 0; e < R->ne; e++) { + b = R->entb[e]; + if ((unsigned)b >= (unsigned)nb) continue; + for (k = 0; k < RMAXV; k++) in[b].g[k] = gnum(GOP_ENT, b, k, 0, 0); + in[b].mem = gnum(GOP_ENT, b, RMAXV, 0, 0); + seen[b] = 1; + } + for (sweeps = 0, changed = 1; changed && sweeps < 80; sweeps++) { + changed = 0; + for (k = 0; k < G->nrpo; k++) { + int sc[2], nsu, t; + b = G->rpo[k]; + if (b >= nb || !seen[b]) continue; + gtrans(R, b, &in[b], &out[b], vgs[b], vcs[b]); + nsu = 0; + if (R->b[b].term == T_BR || R->b[b].term == T_CBR) if ((unsigned)R->b[b].s1 < (unsigned)nb) sc[nsu++] = R->b[b].s1; + if (R->b[b].term == T_CBR && (unsigned)R->b[b].s2 < (unsigned)nb) sc[nsu++] = R->b[b].s2; + for (t = 0; t < nsu; t++) { + int sx = sc[t]; + if (!seen[sx]) { seen[sx] = 1; in[sx] = out[b]; changed = 1; } + else if (gjoin(&in[sx], &out[b], sx)) changed = 1; + } + } + if (gn_over) goto fin; + } + if (changed) goto fin; + for (b = 0; b < nb; b++) if (seen[b]) gtrans(R, b, &in[b], &out[b], vgs[b], vcs[b]); + if (gn_over) goto fin; + /* every place that builds something worth sharing */ + for (b = 0; b < nb; b++) if (seen[b]) { + RB *B = &R->b[b]; + unsigned char *fpv = calloc(B->nv + 1, 1); + if (!fpv) goto fin; + class_fp(R, B, fpv); + for (i = 0; i < B->n; i++) { + Uop *u = &B->u[i]; + int d = u->d; + if (is_store(u->op) || u->op == U_NARG || u->op == U_NCALL || u->op == U_LI || u->op == U_VGET || u->op == U_SLD || u->op >= U_N) continue; + if ((unsigned)d >= (unsigned)B->nv || fpv[d] || vcs[b][d] < 3) continue; + if (ns >= capn) { + Site *g2; + capn = capn ? capn * 2 : 256; + g2 = realloc(sites, capn * sizeof(Site)); + if (!g2) { free(fpv); goto fin; } + sites = g2; + } + sites[ns].g = vgs[b][d]; sites[ns].b = b; sites[ns].i = i; sites[ns].cost = vcs[b][d]; ns++; + } + free(fpv); + } + if (ns > 1) qsort(sites, ns, sizeof(Site), sitecmp); /* no sites: sites may be null, which qsort must not get */ + { + long long bestsave = 0; + int bs = -1, be = -1, bd = -1; + for (s = 0; s < ns; s = e) { + long long save = 0; + int d0 = -1, q; + for (e = s; e < ns && sites[e].g == sites[s].g; e++); + if (e - s < 2) continue; + /* the site that comes first on every path to the others */ + for (q = s; q < e && d0 < 0; q++) { + int r2, good = 1; + for (r2 = s; r2 < e && good; r2++) { + if (r2 == q) continue; + if (sites[r2].b == sites[q].b) { if (sites[r2].i < sites[q].i) good = 0; } + else if (!cdom(G, sites[q].b, sites[r2].b)) good = 0; + } + if (good) d0 = q; + } + if (d0 < 0) continue; + for (q = s; q < e; q++) if (q != d0 && sites[q].b != sites[d0].b) save += (long long)sites[q].cost * w10(G->dep[sites[q].b]); + save -= (long long)(sites[d0].cost + 1) * w10(G->dep[sites[d0].b]) / 4 + w10(G->dep[sites[d0].b]); + if (save > bestsave) { bestsave = save; bs = s; be = e; bd = d0; } + } + if (bs >= 0 && bestsave >= 3) { + int v = newcarry(R), q; + if (v) { + Export ex; + ex.idx = sites[bd].i; ex.var = v; + if (!rb_emit(R, &R->b[sites[bd].b], 0, 0, 0, &ex, 1)) { R->nv--; goto fin; } + for (q = bs; q < be; q++) { + RB *B = &R->b[sites[q].b]; + int *rp, q2; + if (sites[q].b == sites[bd].b) continue; + if (q > bs && sites[q - 1].b == sites[q].b) continue; /* a block is rebuilt once, for all its sites */ + rp = calloc(B->n + 1, sizeof(int)); + if (!rp) continue; + for (q2 = q; q2 < be && sites[q2].b == sites[q].b; q2++) rp[sites[q2].i] = v; + rb_emit(R, B, rp, 0, 0, 0, 0); + free(rp); + } + did = 1; + } + } + } +fin: + if (vgs) for (b = 0; b < nb; b++) free(vgs[b]); + if (vcs) for (b = 0; b < nb; b++) free(vcs[b]); + free(vgs); free(vcs); free(in); free(out); free(seen); free(sites); + return did; +} +/* ---- variables written and never read ---- */ +static int rdead_vars(Rg *R) +{ + int nb = R->nb, b, i, j, sweeps, changed, did = 0, nreal = R->nv0 ? R->nv0 : R->nv; + unsigned char *gen, *kill, *lin, *lout; + unsigned allreal = 0; + for (j = 1; j < nreal; j++) allreal |= 1u << j; + gen = calloc(nb, 1); kill = calloc(nb, 1); lin = calloc(nb, 1); lout = calloc(nb, 1); + if (!gen || !kill || !lin || !lout) { free(gen); free(kill); free(lin); free(lout); return 0; } + for (b = 0; b < nb; b++) { + RB *B = &R->b[b]; + for (i = 0; i < B->n; i++) if (B->u[i].op == U_VGET && B->u[i].imm >= 0 && B->u[i].imm < RMAXV) gen[b] |= 1u << B->u[i].imm; + for (j = 1; j < RMAXV; j++) if (B->vs[j] >= 0) kill[b] |= 1u << j; + } + for (sweeps = 0, changed = 1; changed && sweeps < 64; sweeps++) { + changed = 0; + for (b = nb; b--;) { + RB *B = &R->b[b]; + unsigned o = 0; + int s, x; + if (B->term == T_BR) o = (unsigned)B->s1 < (unsigned)nb ? lin[B->s1] : allreal; + else if (B->term == T_CBR) { + for (s = 0; s < 2; s++) { + int t = s ? B->s2 : B->s1, xa = s ? B->x2 : B->x1; + if ((unsigned)t < (unsigned)nb) o |= lin[t]; + else o |= (R->fn >= 0 && xa >= R->hi) ? 0 : allreal; + } + } else if (B->term == T_JMP) { + int dead = 0; + if (R->fn >= 0 && B->cv >= 0) for (x = 0; x < B->n; x++) if (B->u[x].d == B->cv && B->u[x].op == U_LI && B->u[x].imm >= R->hi) dead = 1; + o = dead ? 0 : allreal; + } else if (B->term == T_CALL) { + int ee; + o = allreal; + for (ee = 0; ee < R->ne; ee++) if (R->ent[ee] == B->x2 && (unsigned)R->entb[ee] < (unsigned)nb) { o = lin[R->entb[ee]]; break; } + } else o = allreal; + if (o != lout[b]) { lout[b] = (unsigned char)o; changed = 1; } + x = gen[b] | (lout[b] & ~kill[b]); + if (x != lin[b]) { lin[b] = (unsigned char)x; changed = 1; } + } + } + if (changed) { free(gen); free(kill); free(lin); free(lout); return 0; } + for (b = 0; b < nb; b++) { + RB *B = &R->b[b]; + int any = 0; + for (j = 1; j < RMAXV; j++) if (B->vs[j] >= 0 && !(lout[b] >> j & 1)) { B->vs[j] = -1; any = 1; } + if (any) { rb_emit(R, B, 0, 0, 0, 0, 0); did = 1; } + } + free(gen); free(kill); free(lin); free(lout); + return did; +} +static int rshare(Rg *R) +{ + int iter, did = 0; + if (R->nb < 1 || R->nb > 300 || getenv("ZYNOSHARE")) return 0; + for (iter = 0; iter < 8; iter++) { + Cfg *G = cfg_build(R); + int li, one = 0, ord[256], n, x, y; + if (!G) break; + n = G->nl < 256 ? G->nl : 256; + for (x = 0; x < n; x++) ord[x] = x; + for (x = 1; x < n; x++) for (y = x; y > 0 && G->lp[ord[y]].n < G->lp[ord[y - 1]].n; y--) { int t = ord[y]; ord[y] = ord[y - 1]; ord[y - 1] = t; } + for (li = 0; li < n && !one; li++) one = licm_loop(R, G, ord[li]); + if (!one) one = cse_one(R, G); + cfg_free(G); + if (!one) break; + did = 1; + } + if (rdead_vars(R)) did = 1; + return did; +} +static void addl(int x, int en) +{ + int e; + if (x < R_->lo || x >= R_->hi) return; + if (!lead_[x - R_->lo]) { lead_[x - R_->lo] = 1; wl_[nw_++] = x; } + if (en) { for (e = 0; e < R_->ne && R_->ent[e] != x; e++); if (e == R_->ne && e < RMAXE) R_->ent[R_->ne++] = x; } +} +/* blocks, then each one lifted and optimized */ +static int rlift(Rg *R) +{ + int n = R->hi - R->lo, L, i, k, t, b, e, r = 0, cnt; + Cell w; + if (R->lifted) rclear(R); + if (R->nv0) R->nv = R->nv0; + R_ = R; + lead_ = calloc(n, 1); bidx_ = malloc(n * sizeof(int)); wl_ = malloc(n * sizeof(int)); nw_ = 0; + R->ne = 0; + addl(R->lo, 1); + while (nw_) { + L = wl_[--nw_]; + for (i = L, cnt = 0;;) { + if (i >= R->hi || (i != L && lead_[i - R->lo])) break; + if (cnt++ >= 1000) { addl(i, 0); break; } + w = m[i]; + if (w & 1) { + if (i + 1 < R->hi && !(m[i + 1] & 1) && (k = (int)(m[i + 1] >> 1)) >= 6 && k <= 8) { + t = (int)(w >> 1); + if (k == 6) addl(i + 2, 1); /* a call comes back here */ + else if (k == 7) addl(t, 0); + else { addl(t, 0); addl(i + 2, 0); } + break; + } + i++; + continue; + } + k = (int)(w >> 1); + if (k == 600) { i += 2; continue; } /* payload is data, not an opcode */ + if (k == 6) { addl(i + 1, 1); break; } + if (k == 8) { addl(i + 1, 0); break; } + if (k == 0 || k == 7 || k == 9) break; + i++; + } + } + for (i = 0, b = 0; i < n; i++) if (lead_[i]) bidx_[i] = b++; + R->nb = b; R->b = calloc(b, sizeof(RB)); R->maxnv = 0; + for (i = 0; i < n; i++) if (lead_[i]) R->b[bidx_[i]].a = R->lo + i; + for (b = 0; b < R->nb && !r; b++) r = lift_block(b); + for (e = 0; e < R->ne; e++) R->entb[e] = bidx_[R->ent[e] - R->lo]; + free(lead_); free(bidx_); free(wl_); + if (r) { + if (trace) fprintf(stderr, "o3: %d..%d doesn't lift (%d)\n", R->lo, R->hi - 1, r); + R->lifted = 1; rclear(R); return r; + } + rshape(R); + if (!getenv("ZYNOGLOBAL")) rglobal(R); + if (rshare(R)) rshape(R); + for (e = 0; e < R->ne; e++) { o3ent[R->ent[e]] = R->idx + 1; o3ei[R->ent[e]] = e; } + R->lifted = 1; + rmark(R); + if (trace) { + int ops = 0; + for (b = 0; b < R->nb; b++) ops += R->b[b].n; + fprintf(stderr, "o3: %d..%d: %d blocks, %d entries, %d vars, %d micro-ops\n", R->lo, R->hi - 1, R->nb, R->ne, R->nv - 1, ops); + if (getenv("ZYO3DUMP")) + for (b = 0; b < R->nb; b++) { + RB *B = &R->b[b]; + fprintf(stderr, " b%d @%d: %d ops, sp %+d, term %d cv v%d s %d/%d x %d/%d\n", b, B->a, B->n, B->delta, B->term, B->cv, B->s1, B->s2, B->x1, B->x2); + for (i = 0; i < B->n; i++) { + Uop *u = &B->u[i]; + if (u->op == U_ST) fprintf(stderr, " st v%d v%d\n", u->a, u->b); + else if (u->op == U_LI) fprintf(stderr, " v%d = %lld\n", u->d, (long long)u->imm); + else if (u->op == U_VGET) fprintf(stderr, " v%d = var%d\n", u->d, u->imm); + else if (u->op == U_SLD) fprintf(stderr, " v%d = s[%d]\n", u->d, u->imm); + else { fprintf(stderr, " v%d = %s v%d", u->d, unames[u->op], u->a); if (nargs(u->op) > 1) fprintf(stderr, " v%d", u->b); if (nargs(u->op) > 2) fprintf(stderr, " v%d", u->c); fprintf(stderr, "\n"); } + } + for (i = 0; i < B->nso; i++) fprintf(stderr, " s[%d] = v%d\n", B->so[2 * i], B->so[2 * i + 1]); + for (i = 1; i < R->nv; i++) if (B->vs[i] >= 0) fprintf(stderr, " var%d = v%d\n", i, B->vs[i]); + } + } + return 0; +} +/* install kind 2 */ +static int o3install(int d) +{ + int lo = mread(d + 2), hi = mread(d + 3), n = mread(d + 4), i, r; + Rg *R; + if (lo <= 0 || hi <= lo || hi > code_words || hi - lo > 1000000 || (unsigned)n > 32 || nrg >= MAXRG) return -1; + R = calloc(1, sizeof *R); + R->idx = nrg; rgs[nrg++] = R; + R->lo = lo; R->hi = hi; R->nv = 1; R->np = n; + for (i = 1; i < RMAXV; i++) R->slot[i] = -1; + for (i = 0; i < n; i++) { /* pairs: frame slot, register 1..7 */ + int s = mread(d + 5 + 2 * i), v = mread(d + 6 + 2 * i), fp = 0, pl = 0; + if (v < 0) { fp = 1; v = -v; } /* a negative register is a d-register home */ + if (v >= 16) { pl = 1; v -= 16; } + if ((unsigned)s >= 4096 || v < 1 || v >= RMAXV) { R->dead = 1; return -1; } + R->fv[v] = (unsigned char)fp; + R->sc[v] = (unsigned char)((R->sc[v] || !R->seen[v]) && pl); + R->seen[v] = 1; + R->ps[i] = s; R->pv[i] = v; + if (R->slot[v] < 0) R->slot[v] = s; + if (v >= R->nv) R->nv = v + 1; + } + for (i = 1; i < R->nv; i++) if (R->slot[i] < 0) R->slot[i] = R->ps[0]; + R->nv0 = R->nv; + R->fslots = mread(d + 5 + 2 * n); + R->argc = mread(d + 6 + 2 * n); + R->fn = mread(d + 7 + 2 * n); + if ((unsigned)R->argc > 32 || R->fslots < 0) { R->dead = 1; return -1; } + if ((r = rlift(R))) { R->dead = 1; return r; } + o3_bind(R); + rcompile(R, 1); + return 0; +} +/* the region entered at p, lifted again if it has to be, or 0 */ +static Rg *rat(int p) +{ + Rg *R = rgs[o3ent[p] - 1]; + if (R->dead) { o3ent[p] = 0; return 0; } + if (!R->lifted && rlift(R)) { R->dead = 1; o3ent[p] = 0; return 0; } + if (o3ei[p] >= R->ne || R->ent[o3ei[p]] != p) { o3ent[p] = 0; return 0; } + return R; +} + +#ifdef ZYJIT +static unsigned char *cov; /* how many live translations cover the word (255 sticks) */ +static void **tab; /* vm address -> translation */ +static void be_drop(int j) { tab[ix_blk[j].a] = 0; cdec(cov, ix_blk[j].a, ix_blk[j].b); if (trace) fprintf(stderr, "jit: dropped %d..%d\n", ix_blk[j].a, ix_blk[j].b - 1); } +static unsigned *code, *cp, *x_enter, *x_exit, *x_disp, *x_first; +static long S[14]; /* memory, stacks, hooks, limits, and the target saved on overflow */ +static void o3_invoke_jit(Cell *ps, Cell *pr, Cell target, Cell retpc) +{ + o3_invoke(ps, pr, target, retpc); + S[11] = (long)(m + stack_data_limit()); S[12] = (long)(m + stack_return_limit()); +} + +enum { W0 = 0, W1 = 1, X9 = 9, X10, X11, M = 19, SP = 20, RP = 21, TAB = 22, ST = 23 }; + +static void E(unsigned x) { *cp++ = x; } +static void li(int d, Cell v) /* movz/movk x, four halfwords; a vm immediate */ +{ + UCell u = (UCell)v; + E(0xd2800000 | ((unsigned)(u & 0xffff) << 5) | d); + if (u >> 16) E(0xf2a00000 | ((unsigned)((u >> 16) & 0xffff) << 5) | d); + if (u >> 32) E(0xf2c00000 | ((unsigned)((u >> 32) & 0xffff) << 5) | d); + if (u >> 48) E(0xf2e00000 | ((unsigned)((u >> 48) & 0xffff) << 5) | d); +} +/* executable bounds are read again after a compiler grows its code storage. */ +static void cmp_code(unsigned rn) +{ + int tmp = X9; + li(tmp, (Cell)(uintptr_t)&code_words); E(0xf9400000u | ((unsigned)tmp << 5) | tmp); + E(0xeb00001fu | ((unsigned)tmp << 16) | (rn << 5)); +} +/* cmp xn, #MW through a scratch register: a data address is in range. MW can be + far past what an immediate encodes, since the heap grows into it. */ +static void cmp_mw(unsigned rn, int tmp) +{ + li(tmp, MW); + E(0xeb00001fu | ((unsigned)tmp << 16) | (rn << 5)); +} +static void ldr_post(int t, int n, int i) { E(0xf8400400 | (i & 0x1ff) << 12 | n << 5 | t); } +static void ldr_pre(int t, int n, int i) { E(0xf8400c00 | (i & 0x1ff) << 12 | n << 5 | t); } +static void str_pre(int t, int n, int i) { E(0xf8000c00 | (i & 0x1ff) << 12 | n << 5 | t); } +static void str_post(int t, int n, int i) { E(0xf8000400 | (i & 0x1ff) << 12 | n << 5 | t); } +static void ldr_u(int t, int n, int o) { E(0xf9400000 | (o / 8) << 10 | n << 5 | t); } /* 8-byte cells */ +static void str_u(int t, int n, int o) { E(0xf9000000 | (o / 8) << 10 | n << 5 | t); } +static void ldr_d(int t, int n, int o) { E(0xfd400000 | (o / 8) << 10 | n << 5 | t); } /* ldr Dt, [Xn, #o] */ +static void str_d(int t, int n, int o) { E(0xfd000000 | (o / 8) << 10 | n << 5 | t); } +static void ldr_d_rr(int t, int n, int r) { E(0xfc605800 | r << 16 | n << 5 | t); } /* ldr Dt, [Xn, Wr, uxtw #3] */ +static void str_d_rr(int t, int n, int r) { E(0xfc205800 | r << 16 | n << 5 | t); } +static void fmov_dx(int d, int n) { E(0x9e670000 | n << 5 | d); } /* fmov Dd, Xn */ +static void fmov_xd(int d, int n) { E(0x9e660000 | n << 5 | d); } /* fmov Xd, Dn */ +static void fmov_dd(int d, int n) { E(0x1e604000 | n << 5 | d); } /* fmov Dd, Dn */ +static void ldrx_u(int t, int n, int o) { E(0xf9400000 | (o / 8) << 10 | n << 5 | t); } +static void ldr_rr(int t, int n, int r, int ext) { E(0xf8600800 | r << 16 | ext << 13 | 1 << 12 | n << 5 | t); } +static void str_rr(int t, int n, int r, int ext) { E(0xf8200800 | r << 16 | ext << 13 | 1 << 12 | n << 5 | t); } +static void ldp_post(int t, int u, int n, int i) { E(0xa8c00000 | ((i / 8) & 0x7f) << 15 | u << 10 | n << 5 | t); } +static void op3(unsigned op, int d, int n, int r) { E(op | r << 16 | n << 5 | d); } +static void addx_ext(int d, int n, int r, int ext, int sh) { E(0x8b200000 | r << 16 | ext << 13 | sh << 10 | n << 5 | d); } +static void movw(int d, int r) { E(0xaa0003e0 | r << 16 | d); } /* mov xd, xr */ +static void blr(int n) { E(0xd63f0000 | n << 5); } +static void b_to(unsigned *t) { long o = t - cp; E(0x14000000 | (o & 0x3ffffff)); } +static void cb_fix(unsigned *at, unsigned *to) { *at |= ((to - at) & 0x7ffff) << 5; } +#define SXTW 6 +#define UXTW 2 + +static void build_stubs(void) +{ + cp = code; + x_enter = cp; /* int enter(long *S, void *code) */ + E(0xa9b97bfd); /* stp x29, x30, [sp, #-112]! */ + E(0x910003fd); /* mov x29, sp */ + E(0xa90153f3); /* stp x19, x20, [sp, #16] */ + E(0xa9025bf5); /* stp x21, x22, [sp, #32] */ + E(0xf9001bf7); /* str x23, [sp, #48] */ + E(0xa90467f8); /* stp x24, x25, [sp, #64] */ + E(0xa9056ffa); /* stp x26, x27, [sp, #80] */ + E(0xf90033fc); /* str x28, [sp, #96] */ + E(0xaa0003f7); /* mov x23, x0 */ + ldrx_u(M, ST, 0); + ldrx_u(24, ST, 88); ldrx_u(25, ST, 96); /* x24, x25: the stack limits, as addresses */ + ldr_u(X9, ST, 8); addx_ext(SP, M, X9, UXTW, 3); /* sp = m + s*8 */ + ldr_u(X9, ST, 16); addx_ext(RP, M, X9, UXTW, 3); + ldrx_u(TAB, ST, 24); + E(0xd61f0020); /* br x1 */ + x_exit = cp; /* x0 = next vm address */ + op3(0xcb000000, X9, SP, M); E(0xd343fd29); str_u(X9, ST, 8); /* s = (x20 - x19) >> 3 */ + op3(0xcb000000, X9, RP, M); E(0xd343fd29); str_u(X9, ST, 16); + E(0xa94153f3); /* ldp x19, x20, [sp, #16] */ + E(0xa9425bf5); /* ldp x21, x22, [sp, #32] */ + E(0xf9401bf7); /* ldr x23, [sp, #48] */ + E(0xa94467f8); /* ldp x24, x25, [sp, #64] */ + E(0xa9456ffa); /* ldp x26, x27, [sp, #80] */ + E(0xf94033fc); /* ldr x28, [sp, #96] */ + E(0xa8c77bfd); /* ldp x29, x30, [sp], #112 */ + E(0xd65f03c0); /* ret */ + { + unsigned *ovd = cp, *ovr; + li(W0, PC_DATA_OVERFLOW); b_to(x_exit); /* data stack overflow */ + ovr = cp; + li(W0, PC_RETURN_OVERFLOW); b_to(x_exit); /* return stack overflow */ + x_disp = cp; /* w0 = target: check the stacks, chain through tab[] or fall back to c */ + str_u(0, ST, 104); + E(0xeb18029f); /* cmp x20, x24 */ + E(0x54000003 | ((ovd - cp) & 0x7ffff) << 5); /* b.lo data overflow */ + E(0xeb1902bf); /* cmp x21, x25 */ + E(0x54000002 | ((ovr - cp) & 0x7ffff) << 5); /* b.hs return overflow */ + } + cmp_code(0); /* compare the current code reservation */ + E(0x54000002 | ((x_exit - cp) & 0x7ffff) << 5); /* b.hs exit */ + ldrx_u(TAB, ST, 24); + E(0xf8605ac9); /* ldr x9, [x22, w0, uxtw #3] */ + E(0xb4000009 | ((x_exit - cp) & 0x7ffff) << 5); /* cbz x9, exit */ + E(0xd61f0120); /* br x9 */ + x_first = cp; +} + +static void flush(void) +{ + memset(tab, 0, (size_t)code_words * sizeof *tab); + rdrop_all(); + memset(cov, 0, (size_t)code_words); + cov[SYSGATE] = 255; + ix_reset(); + cp = x_first; + if (trace) fprintf(stderr, "jit: flushed\n"); +} + +static void jitwrite(int on) +{ +#ifdef __APPLE__ + pthread_jit_write_protect_np(!on); +#else + (void)on; +#endif +} + +static void syncicache(unsigned *a, unsigned *b) +{ +#ifdef __APPLE__ + sys_icache_invalidate(a, (char *)b - (char *)a); +#else + __builtin___clear_cache((char *)a, (char *)b); +#endif +} + +/* store hook for words that are covered by a translation */ +static int hook(Cell a, Cell v) +{ + int j, hit = 0; + if (a == SYSGATE) { + int moving = v == 9 && (m[a + 1] == 7 || m[a + 1] == 8); + cb_s = S[1]; cb_r = S[2]; m[a] = hostcall(v, m[a + 1], m[a + 2], m[a + 3]); + if (moving) { S[1] = cb_s; S[2] = cb_r; } + S[11] = (long)(m + stack_data_limit()); S[12] = (long)(m + stack_return_limit()); + S[3] = (long)tab; + return 0; + } + if (m[a] == v) return 0; + m[a] = v; + hit = ix_drop_at((int)a); + for (j = 0; j < nrg; j++) + if (rgs[j]->lifted && rgs[j]->lo <= a && a < rgs[j]->hi) { + if (trace) fprintf(stderr, "o3: dropped %d..%d (a store to %d)\n", rgs[j]->lo, rgs[j]->hi - 1, (int)a); + rclear(rgs[j]); + hit = 1; + } + if (hit) drop_epoch++; + return hit; +} + +/* float ops 601..611 and 618. 600 (flit) is handled by translate, which eats the raw bits. */ +static void fjit(int k) +{ + unsigned opw = 0; + if (k == 601) opw = 0x1e602820; /* fadd d0, d1, d0 */ + else if (k == 602) opw = 0x1e603820; /* fsub */ + else if (k == 603) opw = 0x1e600820; /* fmul */ + else if (k == 604) opw = 0x1e601820; /* fdiv */ + if (opw) { + E(0xfd400280); /* ldr d0, [x20] */ + E(0xfd400681); /* ldr d1, [x20, #8] */ + E(opw); + E(0x91002294); /* add x20, x20, #8 */ + E(0xfd000280); /* str d0, [x20] */ + return; + } + if (k >= 605 && k <= 607) { + E(0xfd400280); + E(0xfd400681); + E(0x1e602020); /* fcmp d1, d0: left ? right. mi/ls, not lt/le: unordered is not less */ + if (k == 605) E(0x9a9f17ea); /* cset x10, eq */ + else if (k == 606) E(0x9a9f57ea); /* cset x10, mi */ + else E(0x9a9f87ea); /* cset x10, ls */ + E(0x91002294); + str_u(X10, SP, 0); + return; + } + if (k == 608) { ldr_u(X9, SP, 0); E(0x9e620120); E(0xfd000280); return; } /* scvtf d0, x9; str d0 */ + if (k == 609) { E(0xfd400280); E(0x9e78000a); str_u(X10, SP, 0); return; } /* fcvtzs x10, d0 */ + if (k == 610) { ldr_post(0, SP, 8); li(X11, (Cell)(uintptr_t)fop_print); blr(X11); return; } + if (k == 618) { /* (bits prec mode --) */ + ldr_u(2, SP, 0); + ldr_u(1, SP, 8); + ldr_u(0, SP, 16); + E(0x91006294); /* add x20, x20, #24 */ + li(X11, (Cell)(uintptr_t)fop_print_mode); + blr(X11); + return; + } + ldr_u(0, SP, 0); li(X11, (Cell)(uintptr_t)fop_parse_cell); blr(X11); str_u(0, SP, 0); +} + +/* byte ops 612..617. the address is a byte index into m[], little-endian. */ +static void bjit(int k) +{ + Cell fn; + if (k == 612 || k == 614 || k == 616) { + ldr_u(0, SP, 0); + if (k == 612) fn = (Cell)(uintptr_t)bld8; + else if (k == 614) fn = (Cell)(uintptr_t)bld16; + else fn = (Cell)(uintptr_t)bld32; + li(X11, fn); blr(X11); str_u(0, SP, 0); + return; + } + ldp_post(0, 1, SP, 16); + if (k == 613) fn = (Cell)(uintptr_t)bst8; + else if (k == 615) fn = (Cell)(uintptr_t)bst16; + else fn = (Cell)(uintptr_t)bst32; + li(X11, fn); blr(X11); +} + +static void op(int i, int k) /* everything except control flow */ +{ + unsigned *a, *b, *c, *z; + switch (k) { + case 1: + ldr_u(X9, SP, 0); cmp_mw(9, X11); + a = cp; E(0x54000002); /* b.hs zero */ + ldr_rr(X9, M, X9, UXTW); + b = cp; E(0x14000000); + cb_fix(a, cp); li(X9, 0); + *b |= (cp - b) & 0x3ffffff; + str_u(X9, SP, 0); return; + case 2: /* store: inline unless the target word is translated */ + ldp_post(X9, X10, SP, 16); + cmp_mw(9, X11); /* cmp x9, MW */ + a = cp; E(0x54000002); /* b.hs done */ + ldrx_u(X11, ST, 32); + E(0x3869496b); /* ldrb w11, [x11, w9, uxtw] */ + b = cp; E(0x3500000b); /* cbnz w11, slow */ + str_rr(X10, M, X9, UXTW); + c = cp; E(0x14000000); /* b done */ + cb_fix(b, cp); /* slow: hook(a, v) */ + movw(W0, X9); movw(W1, X10); + /* publish s and r (as exit does) so a syscall that calls back into the vm + can find the stacks. the hook reads them for a gate store */ + op3(0xcb000000, X9, SP, M); E(0xd343fd29); str_u(X9, ST, 8); + op3(0xcb000000, X9, RP, M); E(0xd343fd29); str_u(X9, ST, 16); + ldrx_u(X11, ST, 64); blr(X11); + ldr_u(X9, ST, 8); addx_ext(SP, M, X9, UXTW, 3); + ldr_u(X9, ST, 16); addx_ext(RP, M, X9, UXTW, 3); + ldrx_u(24, ST, 88); ldrx_u(25, ST, 96); ldrx_u(TAB, ST, 24); + z = cp; E(0x34000000); /* cbz w0, done: hook's int result */ + li(W0, i + 1); b_to(x_exit); /* a translation died: resume at i+1 in c */ + cb_fix(a, cp); cb_fix(z, cp); + *c |= (cp - c) & 0x3ffffff; + return; + case 3: /* pick uses a word address, with the same bounds as load */ + ldr_u(X9, SP, 0); + op3(0xcb000000, X10, SP, M); E(0xd343fd4a); /* (sp - m) / 8 */ + E(0x9100054a); op3(0x8b000000, X9, X10, X9); + cmp_mw(9, X11); + a = cp; E(0x54000002); + ldr_rr(X9, M, X9, UXTW); + b = cp; E(0x14000000); + cb_fix(a, cp); li(X9, 0); + *b |= (cp - b) & 0x3ffffff; + str_u(X9, SP, 0); return; + case 4: E(0x91002294); return; /* add x20, x20, #8 */ + case 5: E(0xa9402a89); E(0xa900268a); return; /* ldp x9, x10, [x20]; stp x10, x9, [x20] */ + case 10: ldrx_u(X11, ST, 40); blr(X11); E(0x93401c09); str_pre(X9, SP, -8); return; /* sxtb x9, w0 */ + case 11: + ldrx_u(X9, ST, 56); + if (sizeof J == 1) E(0x39400129); else E(0xb9400129); /* ldrb or ldr w: J is a host flag, not a vm word */ + str_pre(X9, SP, -8); + return; + case 12: ldr_post(W0, SP, 8); E(0x93401c00); ldrx_u(X11, ST, 48); blr(X11); return; /* sxtb x0, w0 */ + } + ldr_post(X9, SP, 8); + ldr_u(X10, SP, 0); + switch (k) { + case 13: op3(0x8b000000, X10, X10, X9); break; + case 14: op3(0xcb000000, X10, X10, X9); break; + case 15: op3(0x9b007c00, X10, X10, X9); break; + case 16: op3(0x8a000000, X10, X10, X9); break; + case 17: op3(0xaa000000, X10, X10, X9); break; + case 18: op3(0xca000000, X10, X10, X9); break; + case 19: op3(0x9ac02800, X10, X10, X9); break; + default: E(0xeb09015f); E(0x9a9fa7ea); /* cmp x10, x9; cset x10, lt */ + } + str_u(X10, SP, 0); +} + +/* one micro-op, operands in registers. tmp is a scratch register for the + remainders; x16 and x17 are scratch too. */ +/* a byte address is an index into the flat image. out of range loads 0 and + stores nothing. a store into a word some translation covers calls the + same helper as opcode 2, so the block is dropped. x16, x17 and the saved link register are scratch, + and the address and value are parked on the machine stack across that. */ +static void emit_byte(int op, int d, int a, int b) +{ + int n = bwidth(op), j, ar; + unsigned *oob, *cov1, *nocross, *cov2, *over; + Cell fn; + if (!n) return; + str_pre(30, 31, -16); /* x18 is reserved on darwin; borrow the saved link register */ + if (!is_store(op)) { + ar = a; + if (d == a) { movw(17, a); ar = 17; } + E(0xd2800000 | d); /* mov xd, #0 */ + li(30, ((Cell)MW << 3) - (n - 1)); + E(0xeb1e001f | (ar << 5)); /* cmp xAr, x30 */ + oob = cp; E(0x54000002); /* b.hs done */ + E(0x8b000000 | (ar << 16) | (M << 5) | 17); /* add x17, x19, xAr */ + if (n == 1) E(0x39400000 | (17 << 5) | d); + else if (n == 2) E(0x79400000 | (17 << 5) | d); + else E(0xb9400000 | (17 << 5) | d); + cb_fix(oob, cp); + ldr_post(30, 31, 16); + return; + } + str_pre(a, 31, -16); + str_u(b, 31, 8); + li(16, ((Cell)MW << 3) - (n - 1)); + ldr_u(17, 31, 0); + E(0xeb10023f); /* cmp x17, x16 */ + oob = cp; E(0x54000002); /* b.hs pop */ + E(0xd343fe30); /* lsr x16, x17, #3 */ + ldrx_u(30, ST, 32); + E(0x38704bde); /* ldrb w30, [x30, w16, uxtw] */ + cov1 = cp; E(0x35000000 | 30); /* cbnz w30, slow */ + E(0x92400a3e); /* and x30, x17, #7 */ + E(0x91000000 | (n << 10) | (30 << 5) | 30); /* add x30, x30, #n */ + E(0xf10023df); /* cmp x30, #8 */ + nocross = cp; E(0x54000009); /* b.ls inline */ + E(0x91000610); /* add x16, x16, #1 */ + ldrx_u(30, ST, 32); + E(0x38704bde); /* ldrb w30, [x30, w16, uxtw] */ + cov2 = cp; E(0x35000000 | 30); /* cbnz w30, slow */ + cb_fix(nocross, cp); /* inline */ + ldr_u(17, 31, 0); + ldr_u(16, 31, 8); + E(0x8b11027e); /* add x30, x19, x17 */ + if (n == 1) E(0x390003d0); + else if (n == 2) E(0x790003d0); + else E(0xb90003d0); /* str w16, [x30] */ + over = cp; E(0x14000000); /* b pop */ + cb_fix(cov1, cp); + cb_fix(cov2, cp); /* slow */ + E(0xd10203ff); /* sub sp, sp, #128 */ + for (j = 0; j < 16; j += 2) E(0xa9000000 | (j / 2 * 2) << 15 | (j + 1) << 10 | 31 << 5 | j); + ldr_u(0, 31, 128); + ldr_u(1, 31, 136); + fn = n == 1 ? (Cell)(uintptr_t)bst8 : n == 2 ? (Cell)(uintptr_t)bst16 : (Cell)(uintptr_t)bst32; + li(16, fn); blr(16); + for (j = 0; j < 16; j += 2) E(0xa9400000 | (j / 2 * 2) << 15 | (j + 1) << 10 | 31 << 5 | j); + E(0x910203ff); /* add sp, sp, #128 */ + *over |= (cp - over) & 0x3ffffff; + cb_fix(oob, cp); /* pop */ + E(0x910043ff); /* add sp, sp, #16 */ + ldr_post(30, 31, 16); + (void)d; +} +static void emit_uop(int op, int d, int a, int b, int c, int tmp) +{ + switch (op) { + case U_ADD: op3(0x8b000000, d, a, b); break; + case U_SUB: op3(0xcb000000, d, a, b); break; + case U_MUL: op3(0x9b007c00, d, a, b); break; + case U_SDIV: op3(0x9ac00c00, d, a, b); break; + case U_UDIV: op3(0x9ac00800, d, a, b); break; + case U_SREM: op3(0x9ac00c00, tmp, a, b); E(0x9b008000 | b << 16 | a << 10 | tmp << 5 | d); break; /* msub */ + case U_UREM: op3(0x9ac00800, tmp, a, b); E(0x9b008000 | b << 16 | a << 10 | tmp << 5 | d); break; + case U_AND: op3(0x8a000000, d, a, b); break; + case U_OR: op3(0xaa000000, d, a, b); break; + case U_XOR: op3(0xca000000, d, a, b); break; + case U_SHL: op3(0x9ac02000, d, a, b); break; + case U_SHR: op3(0x9ac02400, d, a, b); break; + case U_SAR: op3(0x9ac02800, d, a, b); break; + case U_NEG: E(0xcb0003e0 | a << 16 | d); break; + case U_NOT: E(0xaa2003e0 | a << 16 | d); break; + case U_LT: E(0xeb00001f | b << 16 | a << 5); E(0x9a9fa7e0 | d); break; /* cmp; cset lt */ + case U_LTU: E(0xeb00001f | b << 16 | a << 5); E(0x9a9f27e0 | d); break; /* cset lo */ + case U_EQ: E(0xeb00001f | b << 16 | a << 5); E(0x9a9f17e0 | d); break; + case U_NE: E(0xeb00001f | b << 16 | a << 5); E(0x9a9f07e0 | d); break; + case U_MIN: E(0xeb00001f | b << 16 | a << 5); E(0x9a800000 | b << 16 | 11 << 12 | a << 5 | d); break; + case U_MAX: E(0xeb00001f | b << 16 | a << 5); E(0x9a800000 | b << 16 | 12 << 12 | a << 5 | d); break; + case U_MINU: E(0xeb00001f | b << 16 | a << 5); E(0x9a800000 | b << 16 | 3 << 12 | a << 5 | d); break; + case U_MAXU: E(0xeb00001f | b << 16 | a << 5); E(0x9a800000 | b << 16 | 8 << 12 | a << 5 | d); break; + case U_MULHS: E(0x9b407c00 | b << 16 | a << 5 | d); break; /* smulh */ + case U_MULHU: E(0x9bc07c00 | b << 16 | a << 5 | d); break; /* umulh */ + case U_CLZ: E(0xdac01000 | a << 5 | d); break; + case U_CTZ: E(0xdac00000 | a << 5 | 16); E(0xdac01000 | 16 << 5 | d); break; /* rbit; clz */ + case U_BSWAP: E(0xdac00c00 | a << 5 | d); break; /* rev */ + case U_POPCNT: + fmov_dx(16, a); E(0x0e205800 | 16 << 5 | 16); /* cnt v16.8b */ + E(0x0e31b800 | 16 << 5 | 16); /* addv b16, v16.8b */ + E(0x0e013c00 | 16 << 5 | d); break; /* umov w, v16.b[0] */ + case U_ROTR: op3(0x9ac02c00, d, a, b); break; + case U_ROTL: E(0xcb0003e0 | b << 16 | 16); op3(0x9ac02c00, d, a, 16); break; /* neg x16, b; ror */ + case U_F2S: fmov_dx(16, a); E(0x1e624000 | 16 << 5 | 16); E(0x1e260000 | 16 << 5 | d); break; /* fcvt s16, d16; fmov w, s */ + case U_S2F: E(0x1e270000 | a << 5 | 16); E(0x1e22c000 | 16 << 5 | 16); E(0x9e660000 | 16 << 5 | d); break; /* fmov s16, w; fcvt d16, s16; fmov x, d16 */ + case U_ABS: E(0xf100001f | a << 5); E(0xda800400 | a << 16 | 10 << 12 | a << 5 | d); break; /* cmp #0; cneg */ + case U_SEL: E(0xf100001f | a << 5); E(0x9a800000 | c << 16 | 1 << 12 | b << 5 | d); break; /* csel ne */ + case U_FADD: case U_FSUB: case U_FMUL: case U_FDIV: { /* operands arrive in GPRs; enhancements have no d homes */ + unsigned base = op == U_FADD ? 0x1e602800u : op == U_FSUB ? 0x1e603800u : op == U_FMUL ? 0x1e600800u : 0x1e601800u; + fmov_dx(16, a); fmov_dx(17, b); E(base | 17u << 16 | 16u << 5 | 16u); fmov_xd(d, 16); break; } + case U_FEQ: case U_FLT: case U_FLE: + fmov_dx(16, a); fmov_dx(17, b); E(0x1e602000 | 17u << 16 | 16u << 5); + E((op == U_FEQ ? 0x9a9f17e0u : op == U_FLT ? 0x9a9f57e0u : 0x9a9f87e0u) | d); break; + case U_I2F: E(0x9e620000 | a << 5 | 16); fmov_xd(d, 16); break; /* scvtf d16, Xa */ + case U_F2I: fmov_dx(16, a); E(0x9e780000 | 16 << 5 | d); break; /* fcvtzs Xd, d16 */ + case U_LD: /* out of range reads 0, like the interpreter */ + E(0xd2800011); cmp_mw(a, 16); E(0x54000042); ldr_rr(17, M, a, UXTW); movw(d, 17); + break; + case U_ST: cmp_mw(a, 16); E(0x54000042); str_rr(b, M, a, UXTW); break; + case U_LD8: case U_LD16: case U_LD32: case U_ST8: case U_ST16: case U_ST32: + emit_byte(op, d, a, b); break; + } +} +/* an enhancement, inlined: inputs from the data stack into x0..x15, the + optimized micro-ops, outputs back. constants are made where they're used, + or folded into the instruction (a fixed address, a small addend). */ +static int ekc[MAXUOP * 4 + 16]; static Cell ekv[MAXUOP * 4 + 16]; +static void emit_enh_e(Enh *e, int s) +{ + signed char *r = e->reg; + int i, j, delta, na; + if (!r) { /* didn't fit in registers: x0 = data stack, w1 = slot, call the interpreter */ + E(0xaa1403e0); li(W1, s); ldrx_u(X11, ST, 80); blr(X11); E(0xaa0003f4); + return; + } + for (i = 0; i < e->nv; i++) ekc[i] = 0; + for (i = 0; i < e->n; i++) if (e->u[i].op == U_LI) { ekc[e->u[i].d] = 1; ekv[e->u[i].d] = e->u[i].imm; } +#define EOP(x) (ekc[x] ? (li(r[x], ekv[x]), r[x]) : r[x]) + for (j = 0; j < e->nin; j++) if (r[j] >= 0) ldr_u(r[j], SP, 8 * (e->nin - 1 - j)); + for (i = 0; i < e->n; i++) { + Uop *u = &e->u[i]; + int d = is_store(u->op) ? 0 : r[u->d], a = 0, b = 0, c = 0; + if (u->op == U_LI) continue; + if (u->op == U_LD && ekc[u->a] && inmem(ekv[u->a])) { + if (ekv[u->a] < 4096) ldr_u(d, M, (int)ekv[u->a] * 8); else ldr_rr(d, M, EOP(u->a), UXTW); + continue; + } + if ((u->op == U_ADD || u->op == U_SUB) && ekc[u->b] && ekv[u->b] >= 0 && ekv[u->b] < 4096) { + E((u->op == U_ADD ? 0x91000000 : 0xd1000000) | (unsigned)ekv[u->b] << 10 | EOP(u->a) << 5 | d); + continue; + } + na = nargs(u->op); + if (na > 0) a = EOP(u->a); + if (na > 1) b = EOP(u->b); + if (na > 2) c = EOP(u->c); + emit_uop(u->op, d, a, b, c, 16); + } + delta = 8 * (e->nin - e->nout); + if (delta > 0) E(0x91000294 | delta << 10); + if (delta < 0) E(0xd1000294 | (-delta) << 10); + for (j = 0; j < e->nout; j++) str_u(EOP(e->out[j]), SP, 8 * (e->nout - 1 - j)); +#undef EOP +} +static void emit_enh(int s) { emit_enh_e(&enh[s], s); } +/* lit .. lit xop: the enhancement specialized on the literals that feed its + last inputs, so the optimizer sees them as constants (x / 7 becomes a + multiply, lfa(k) a load and an add). 0 if it worked. */ +static int spec_enh(int s, Cell *ks, int nk, Enh *te) +{ + Enh *e = &enh[s]; + int nin = e->nin - nk, i, j, out[8], na; + static int mp[MAXUOP * 4 + 16]; + if (e->nv > MAXUOP * 4) return -1; + un = 0; unv = nin; uerr = 0; + for (j = 0; j < nin; j++) mp[j] = j; + for (j = 0; j < nk; j++) mp[nin + j] = unew(U_LI, 0, 0, 0, ks[j]); + for (i = 0; i < e->n; i++) { + Uop *u = &e->u[i]; + na = nargs(u->op); + j = unew(u->op, na > 0 ? mp[u->a] : 0, na > 1 ? mp[u->b] : 0, na > 2 ? mp[u->c] : 0, u->imm); + if (u->op != U_ST) mp[u->d] = j; + } + for (j = 0; j < e->nout; j++) out[j] = mp[e->out[j]]; + memset(te, 0, sizeof *te); + if (uerr || optimize(te, nin, out, e->nout)) return -1; + regalloc(te); + if (!te->reg) { free(te->u); return -1; } + return 0; +} + +/* ---- o3 regions on arm64 ---- + int vars live in x0..x7 (x0 is the frame pointer) and float vars in the + d register of the same number. block values use x8..x14 and w26..w28, or + d8..d15 when the value is a float, so a chain of float ops does not move + through an integer register. constants have no register: they are made in + a scratch (x16/x17 or d16/d17) where they're used. */ +static signed char rreg[MAXUOP * 4 + 16]; +static unsigned char rk[MAXUOP * 4 + 16], rhome[MAXUOP * 4 + 16], rfp[MAXUOP * 4 + 16]; +static Cell rkv[MAXUOP * 4 + 16]; static int rlast[MAXUOP * 4 + 16]; +static unsigned **rfa; static int *rfb, rnf; /* branches to blocks, fixed up at the end */ + +#define LINKN 1024 +static void *linkstk[LINKN]; +static Cell jit_depth; +typedef struct { void *stub; int fslots; int argc; } FnNat; +static FnNat *fn_nat; +static void rforget(Rg *R) +{ + int e; + if (R->nat) for (e = 0; e < R->ne; e++) if (tab[R->ent[e]] == R->stub[e]) tab[R->ent[e]] = 0; + if ((unsigned)R->fn < (unsigned)code_words) fn_nat[R->fn].stub = 0; + R->nat = 0; +} +static void rmark(Rg *R) { R->marked = 1; cinc(cov, R->lo, R->hi); } +static void runmark(Rg *R) { cdec(cov, R->lo, R->hi); } +static int ralloc(Rg *R, RB *B) +{ + int i, j, k, n = B->n; + unsigned fr = 0x7f00u | 0x1c000000u; /* w8..w14, w26..w28 */ + unsigned ffr = 0xff00u; /* d8..d15 */ + unsigned char *want; + for (j = R->nv; j < 8; j++) fr |= 1u << j; + for (j = 1; j < R->nv && j < 8; j++) if (R->fv[j]) fr |= 1u << j; /* that x register is free */ + for (j = 1; j < 8; j++) if (!R->fv[j]) ffr |= 1u << j; + if (B->nv > MAXUOP * 4) return -1; + want = calloc(B->nv + 1, 1); + class_fp(R, B, want); + for (i = 0; i < B->nv; i++) { rlast[i] = -2; rk[i] = 0; rhome[i] = 0; rreg[i] = -1; rfp[i] = want[i]; } + for (i = 0; i < n; i++) { + Uop *u = &B->u[i]; int na = nargs(u->op); + if (na > 0) rlast[u->a] = i; + if (na > 1) rlast[u->b] = i; + if (na > 2) rlast[u->c] = i; + } + for (j = 0; j < B->nso; j++) rlast[B->so[2 * j + 1]] = n; + for (j = 1; j < R->nv; j++) if (B->vs[j] >= 0) rlast[B->vs[j]] = n; + if (B->term == T_CBR || B->term == T_JMP || B->term == T_CALL) rlast[B->cv] = n; + for (i = 0; i < n; i++) { + Uop *u = &B->u[i]; int na = nargs(u->op), ops[3], d = u->d, asfp; + ops[0] = u->a; ops[1] = u->b; ops[2] = u->c; + for (j = 0; j < na; j++) { + int x = ops[j]; + if (rlast[x] == i && rreg[x] >= 0 && !rhome[x]) { + if (rfp[x]) ffr |= 1u << rreg[x]; else fr |= 1u << rreg[x]; + } + } + if (is_store(u->op)) continue; + if (u->op == U_LI) { rk[d] = 1; rkv[d] = u->imm; continue; } + if (u->op == U_VGET) { + rreg[d] = (signed char)u->imm; rhome[d] = 1; + rfp[d] = (unsigned)u->imm < RMAXV && R->fv[u->imm]; + continue; + } + asfp = rfp[d]; + if (asfp) { + for (k = 0; k < 32 && !(ffr >> k & 1); k++); + if (k == 32) { free(want); return -1; } + ffr &= ~(1u << k); + } else { + for (k = 0; k < 32 && !(fr >> k & 1); k++); + if (k == 32) { free(want); return -1; } + fr &= ~(1u << k); + } + rreg[d] = (signed char)k; + if (rlast[d] == -2) { if (asfp) ffr |= 1u << k; else fr |= 1u << k; } + } + free(want); + return 0; +} +/* a GPR holding the bits. a float-resident value is moved across. */ +static int rgpr(int x, int s) +{ + if (rk[x]) { li(s, rkv[x]); return s; } + if (rfp[x]) { fmov_xd(s, rreg[x]); return s; } + return rreg[x]; +} +/* a d register holding the bits. scratch s is d16 or d17. */ +static int rdf(int x, int s) +{ + if (rfp[x] && !rk[x]) return rreg[x]; + if (rk[x]) { li(16, rkv[x]); fmov_dx(s, 16); return s; } + fmov_dx(s, rreg[x]); + return s; +} +static int ropr(int x, int s) { return rgpr(x, s); } +/* w t <-> the data stack word o places from the top at block entry */ +static void rsw(int t, int o, int store) +{ + int by = o * 8; + if (by >= 0 && by < 32768) { if (store) str_u(t, SP, by); else ldr_u(t, SP, by); } + else if (by >= -256 && by < 256) E((store ? 0xf8000000u : 0xf8400000u) | (unsigned)(by & 0x1ff) << 12 | SP << 5 | t); /* stur, ldur */ + else { li(16, by); addx_ext(16, SP, 16, SXTW, 0); if (store) str_u(t, 16, 0); else ldr_u(t, 16, 0); } +} +static void rswd(int t, int o, int store) +{ + int by = o * 8; + if (by >= 0 && by < 32768) { if (store) str_d(t, SP, by); else ldr_d(t, SP, by); } + else { li(16, by); addx_ext(16, SP, 16, SXTW, 0); if (store) str_d(t, 16, 0); else ldr_d(t, 16, 0); } +} +static void addsp(int by) +{ + if (by > 0 && by < 4096) E(0x91000294 | by << 10); + else if (by < 0 && -by < 4096) E(0xd1000294 | (-by) << 10); + else if (by) { li(16, by); addx_ext(SP, SP, 16, SXTW, 0); } +} +static void rspill(Rg *R) +{ + int v; + for (v = 0; v < R->np; v++) { /* add x16, x0, #slot */ + E(0x91000000 | R->ps[v] << 10 | 16); + if (R->fv[R->pv[v]]) str_d_rr(R->pv[v], M, 16); else str_rr(R->pv[v], M, 16, UXTW); + } +} +static void rload(Rg *R) +{ + int v; + li(16, ZFP); ldr_rr(0, M, 16, UXTW); + for (v = 1; v < R->nv; v++) { + E(0x91000000 | R->slot[v] << 10 | 16); + if (R->fv[v]) ldr_d_rr(v, M, 16); else ldr_rr(v, M, 16, UXTW); + } +} +/* a direct o3 call blrs the callee's entry stub. the stub saves x30 in linkstk + when jit_depth is nonzero, and the return reloads it and rets. depth 0 leaves + through x_exit, which is how a region entered from bytecode returns. + x17 is the vm address being left. vars are already spilled. a fall off the + end (pc == hi) pushes the implicit 0. the frame is given back either way. */ +static void rfinish(Rg *R) +{ + unsigned *ne, *z; + /* entered from bytecode: the epilogue is still ahead of us and it + pushes the implicit 0 and puts the frame back. a direct caller + skipped that epilogue, so do both here and ret. */ + li(16, (Cell)(uintptr_t)&jit_depth); + ldr_u(X9, 16, 0); + z = cp; E(0xb4000000 | X9); /* cbz x9, leave to the epilogue */ + li(16, R->hi); + E(0xeb10023f); /* cmp x17, x16 */ + ne = cp; E(0x54000001); /* b.ne value already pushed */ + E(0xd1002294); /* sub x20, x20, #8 */ + E(0xf900029f); /* str xzr, [x20] */ + cb_fix(ne, cp); + if (R->fslots) { + li(16, ZFP); + ldr_rr(10, M, 16, UXTW); + if (R->fslots > 0 && R->fslots < 4096) E(0x91000000 | (R->fslots << 10) | (10 << 5) | 10); + else { li(11, R->fslots); E(0x8b0b014a); } /* add x10, x10, x11 */ + str_rr(10, M, 16, UXTW); + } + li(16, (Cell)(uintptr_t)&jit_depth); + ldr_u(X9, 16, 0); + E(0xd1000529); /* sub x9, x9, #1 */ + str_u(X9, 16, 0); + li(16, (Cell)(uintptr_t)linkstk); + E(0x8b090e10); /* add x16, x16, x9, lsl #3 */ + ldr_u(30, 16, 0); + E(0xd65f03c0); /* ret */ + cb_fix(z, cp); + movw(W0, 17); + b_to(x_exit); +} +static void rexitk(Rg *R, int t) +{ + rspill(R); + if (t >= R->hi) { li(17, t); rfinish(R); } + else { li(W0, t); b_to(x_disp); } +} +/* o3-to-o3 is a blr from this region to the callee's entry, with the frame + built here. anything else is an ordinary vm call: the return address goes + on the vm return stack and x_disp runs the callee. */ +static void rcall(Rg *R, RB *B) +{ + unsigned *to_slow, *is_null, *too_deep, *too_small, *cz; + int e, hopped; + rspill(R); + li(16, (Cell)(uintptr_t)&fn_nat); ldr_u(16, 16, 0); + cmp_code(17); + to_slow = cp; E(0x54000002); /* b.hs slow */ + E(0x8b11120c); /* add x12, x16, x17, lsl #4 */ + ldr_u(X9, 12, 0); + is_null = cp; E(0xb4000000 | X9); /* cbz x9, slow */ + li(16, (Cell)(uintptr_t)&jit_depth); + ldr_u(10, 16, 0); + E(0xf110015f); /* cmp x10, #1024 */ + too_deep = cp; E(0x54000002); /* b.hs slow */ + E(0xb940098a); /* ldr w10, [x12, #8] fslots */ + E(0xb9400d8b); /* ldr w11, [x12, #12] argc */ + li(16, ZFP); + ldr_rr(13, M, 16, UXTW); + E(0xcb0a01ad); /* sub x13, x13, x10 */ + li(16, (Cell)(uintptr_t)&frame_floor_word); ldr_u(16, 16, 0); ldr_rr(14, M, 16, UXTW); + li(16, (Cell)(uintptr_t)&frame_guard_word); ldr_u(16, 16, 0); ldr_rr(15, M, 16, UXTW); + op3(0x8b000000, 14, 14, 15); E(0xeb0e01bf); /* cmp x13, x14 */ + too_small = cp; E(0x54000003); /* b.lo slow: bytecode can grow the frame */ + str_pre(X9, 31, -16); + li(16, ZFP); + str_rr(13, M, 16, UXTW); + cz = cp; E(0x34000000 | 11); /* cbz w11, copied */ + E(0x5100056b); /* sub w11, w11, #1 */ + ldr_post(14, SP, 8); + E(0x8b0b01af); /* add x15, x13, x11 */ + str_rr(14, M, 15, UXTW); + b_to(cz); + cb_fix(cz, cp); + ldr_post(X9, 31, 16); /* stub */ + li(16, (Cell)(uintptr_t)&jit_depth); + ldr_u(10, 16, 0); + E(0x9100054a); /* add x10, x10, #1 */ + str_u(10, 16, 0); + blr(X9); + hopped = 0; + for (e = 0; e < R->ne; e++) if (R->ent[e] == B->x2) { b_to((unsigned *)R->stub[e]); hopped = 1; break; } + if (!hopped) { li(W0, B->x2); b_to(x_disp); } + cb_fix(to_slow, cp); + cb_fix(is_null, cp); + cb_fix(too_deep, cp); + cb_fix(too_small, cp); + /* nested bytecode runs with its own direct-call depth and returns here. */ + op3(0xcb000000, X9, SP, M); E(0xd343fd29); str_u(X9, ST, 8); + op3(0xcb000000, X9, RP, M); E(0xd343fd29); str_u(X9, ST, 16); + E(0x91000000 | (8 << 10) | (ST << 5)); + E(0x91000000 | (16 << 10) | (ST << 5) | 1); + movw(2, 17); li(3, B->x2); li(16, (Cell)(uintptr_t)o3_invoke_jit); blr(16); + ldr_u(X9, ST, 8); E(0x8b090e74); /* sp = m + state[1] * 8 */ + ldr_u(X9, ST, 16); E(0x8b090e75); /* rp = m + state[2] * 8 */ + ldrx_u(24, ST, 88); ldrx_u(25, ST, 96); ldrx_u(TAB, ST, 24); + li(16, (Cell)(uintptr_t)&halt_at); ldr_u(X9, 16, 0); E(0xf100013f); + cz = cp; E(0x5400000b); /* b.lt resumed */ + movw(W0, X9); b_to(x_exit); cb_fix(cz, cp); + hopped = 0; + for (e = 0; e < R->ne; e++) if (R->ent[e] == B->x2) { b_to((unsigned *)R->stub[e]); hopped = 1; break; } + if (!hopped) { li(W0, B->x2); b_to(x_disp); } +} +static void rbranch(int b) { rfa[rnf] = cp; rfb[rnf++] = b; E(0x14000000); } +/* m[a] = v, checked like the jit's own stores: a word some translation covers goes through hook */ +static void rstore(int a, int v, int fp) +{ + unsigned *p1, *p2, *p3; + int j; + cmp_mw(a, 15); /* cmp xa, MW */ + p1 = cp; E(0x54000002); /* b.hs done */ + ldrx_u(15, ST, 32); + E(0x38604800 | a << 16 | 15 << 5 | 15); /* ldrb w15, [x15, wa, uxtw] */ + p2 = cp; E(0x35000000 | 15); /* cbnz w15, slow */ + if (fp) str_d_rr(v, M, a); else str_rr(v, M, a, UXTW); + p3 = cp; E(0x14000000); + cb_fix(p2, cp); + E(0xd10203ff); /* sub sp, sp, #128 */ + for (j = 0; j < 16; j += 2) E(0xa9000000 | (j / 2 * 2) << 15 | (j + 1) << 10 | 31 << 5 | j); /* stp xj, xj+1, [sp, #8j] */ + movw(15, a); if (fp) fmov_xd(1, v); else movw(1, v); movw(0, 15); + ldrx_u(16, ST, 64); blr(16); + for (j = 0; j < 16; j += 2) E(0xa9400000 | (j / 2 * 2) << 15 | (j + 1) << 10 | 31 << 5 | j); + E(0x910203ff); /* add sp, sp, #128 */ + cb_fix(p1, cp); + *p3 |= (cp - p3) & 0x3ffffff; +} +static void rpmove(Rg *R, RB *B) +{ + int dst[RMAXV], src[RMAXV], n = 0, j, k, x, s; + for (j = 1; j < R->nv; j++) if (R->fv[j] && B->vs[j] >= 0) { + s = B->vs[j]; + if (rk[s]) { li(16, rkv[s]); fmov_dx(j, 16); continue; } + if (!rfp[s]) { fmov_dx(j, rreg[s]); continue; } + if (rreg[s] == j) continue; + dst[n] = j; src[n] = rreg[s]; n++; + } + while (n) { + for (j = 0; j < n; j++) { for (k = 0; k < n && src[k] != dst[j]; k++); if (k == n) break; } + if (j < n) { fmov_dd(dst[j], src[j]); n--; dst[j] = dst[n]; src[j] = src[n]; continue; } + x = src[0]; fmov_dd(16, x); + for (k = 0; k < n; k++) if (src[k] == x) src[k] = 16; + } + n = 0; + for (j = 1; j < R->nv; j++) if (!R->fv[j] && B->vs[j] >= 0) { + s = B->vs[j]; + if (rk[s]) { li(j, rkv[s]); continue; } + if (rfp[s]) { fmov_xd(j, rreg[s]); continue; } + if (rreg[s] == j) continue; + dst[n] = j; src[n] = rreg[s]; n++; + } + while (n) { + for (j = 0; j < n; j++) { for (k = 0; k < n && src[k] != dst[j]; k++); if (k == n) break; } + if (j < n) { movw(dst[j], src[j]); n--; dst[j] = dst[n]; src[j] = src[n]; continue; } + x = src[0]; movw(16, x); + for (k = 0; k < n; k++) if (src[k] == x) src[k] = 16; + } +} +static void emit_uop(int op, int d, int a, int b, int c, int tmp); +static int remit(Rg *R, int bi) +{ + RB *B = &R->b[bi]; + int i, j, d, a, b, c; + unsigned *p; + if (ralloc(R, B)) return -1; + for (i = 0; i < B->n; i++) { + Uop *u = &B->u[i]; + switch (u->op) { + case U_LI: case U_VGET: continue; + case U_SLD: + if (rfp[u->d] && !rk[u->d]) rswd(rreg[u->d], (int)u->imm, 0); + else rsw(rreg[u->d], (int)u->imm, 0); + continue; + case U_LD: + if (rfp[u->d] && !rk[u->d]) { + if (rk[u->a] && inmem(rkv[u->a])) { + if (rkv[u->a] < 4096) ldr_d(rreg[u->d], M, (int)rkv[u->a] * 8); + else { li(16, rkv[u->a]); ldr_d_rr(rreg[u->d], M, 16); } + } else ldr_d_rr(rreg[u->d], M, rgpr(u->a, 16)); + continue; + } + if (rk[u->a] && inmem(rkv[u->a])) { /* a fixed address */ + if (rkv[u->a] < 4096) ldr_u(rreg[u->d], M, (int)rkv[u->a] * 8); + else { li(16, rkv[u->a]); ldr_rr(rreg[u->d], M, 16, UXTW); } + continue; + } + break; + case U_ST: + a = rgpr(u->a, 16); + if (rfp[u->b] && !rk[u->b]) rstore(a, rreg[u->b], 1); + else rstore(a, rgpr(u->b, 17), 0); + continue; + case U_LD8: case U_LD16: case U_LD32: + emit_byte(u->op, rreg[u->d], rgpr(u->a, 16), 0); + continue; + case U_ST8: case U_ST16: case U_ST32: + emit_byte(u->op, 0, rgpr(u->a, 16), rgpr(u->b, 17)); + continue; + case U_FADD: case U_FSUB: case U_FMUL: case U_FDIV: { + unsigned base = u->op == U_FADD ? 0x1e602800u : u->op == U_FSUB ? 0x1e603800u : u->op == U_FMUL ? 0x1e600800u : 0x1e601800u; + int da = rdf(u->a, 16), db = rdf(u->b, 17); + E(base | (unsigned)db << 16 | (unsigned)da << 5 | (unsigned)rreg[u->d]); + continue; } + case U_FEQ: case U_FLT: case U_FLE: { + unsigned cset = u->op == U_FEQ ? 0x9a9f17e0u : u->op == U_FLT ? 0x9a9f57e0u : 0x9a9f87e0u; + int da = rdf(u->a, 16), db = rdf(u->b, 17); + E(0x1e602000u | (unsigned)db << 16 | (unsigned)da << 5); + E(cset | (unsigned)rreg[u->d]); + continue; } + case U_I2F: E(0x9e620000u | (unsigned)rgpr(u->a, 16) << 5 | (unsigned)rreg[u->d]); continue; + case U_F2I: E(0x9e780000u | (unsigned)rdf(u->a, 16) << 5 | (unsigned)rreg[u->d]); continue; + case U_F2S: E(0x1e624000u | (unsigned)rdf(u->a, 16) << 5 | 16u); E(0x1e260000u | 16u << 5 | (unsigned)rreg[u->d]); continue; /* fcvt s16, d; fmov w, s16 */ + case U_S2F: E(0x1e270000u | (unsigned)rgpr(u->a, 16) << 5 | 16u); E(0x1e22c000u | 16u << 5 | (unsigned)rreg[u->d]); continue; /* fmov s16, w; fcvt d, s16 */ + case U_ADD: case U_SUB: + if (rk[u->b] && rkv[u->b] >= 0 && rkv[u->b] < 4096) { + E((u->op == U_ADD ? 0x91000000 : 0xd1000000) | (unsigned)rkv[u->b] << 10 | ropr(u->a, 16) << 5 | rreg[u->d]); + continue; + } + break; + } + d = rreg[u->d]; + a = nargs(u->op) > 0 ? ropr(u->a, 16) : 0; + b = nargs(u->op) > 1 ? ropr(u->b, 17) : 0; + c = nargs(u->op) > 2 ? ropr(u->c, 15) : 0; + emit_uop(u->op, d, a, b, c, 15); + } + for (j = 0; j < B->nso; j++) { + int v = B->so[2 * j + 1]; + if (rfp[v] && !rk[v]) rswd(rreg[v], B->so[2 * j], 1); + else rsw(rgpr(v, 17), B->so[2 * j], 1); + } + if (B->term == T_CBR || B->term == T_JMP || B->term == T_CALL) { + if (rk[B->cv]) li(17, rkv[B->cv]); + else if (rfp[B->cv]) fmov_xd(17, rreg[B->cv]); + else movw(17, rreg[B->cv]); + } + addsp(B->delta * 8); + rpmove(R, B); + switch (B->term) { + case T_BR: rbranch(B->s1); break; + case T_CBR: + if (B->s1 >= 0) { rfa[rnf] = cp; rfb[rnf++] = B->s1; E(0xb4000000 | 17); } /* cbz x17, taken */ + else { p = cp; E(0xb5000000 | 17); rexitk(R, B->x1); cb_fix(p, cp); } + if (B->s2 >= 0) rbranch(B->s2); else rexitk(R, B->x2); + break; + case T_CALL: rcall(R, B); break; + case T_RET: rspill(R); ldr_pre(W0, RP, -8); b_to(x_disp); break; + default: { + unsigned *hs; + rspill(R); li(16, R->hi); E(0xeb10023f); /* cmp x17, x16 */ + hs = cp; E(0x54000002); /* b.hs finish: the epilogue, or past the region */ + movw(W0, 17); b_to(x_disp); + cb_fix(hs, cp); + rfinish(R); + break; } + } + return 0; +} +/* the whole region: entry stubs that load the vars, then the blocks */ +static int rjit(Rg *R, int wr) +{ + unsigned *start = cp, **lab; + int b, e, j, bad = 0; + long need = (long)(R->hi - R->lo) * 160 + (long)R->ne * 64 + 8192; + if (need > (code + CODEW) - cp) return -1; + if (wr) jitwrite(1); + lab = malloc((R->nb + 1) * sizeof *lab); + rfa = malloc((2 * R->nb + R->ne + 4) * sizeof *rfa); rfb = malloc((2 * R->nb + R->ne + 4) * sizeof *rfb); rnf = 0; + for (e = 0; e < R->ne; e++) { + unsigned *skip; + R->stub[e] = cp; + if (e == 0) { /* a direct caller left its return address in x30 and bumped jit_depth */ + li(16, (Cell)(uintptr_t)&jit_depth); + ldr_u(X9, 16, 0); + skip = cp; E(0xb4000000 | X9); /* cbz x9, plain entry */ + E(0xd100052a); /* sub x10, x9, #1 */ + li(16, (Cell)(uintptr_t)linkstk); + E(0x8b0a0e10); /* add x16, x16, x10, lsl #3 */ + str_u(30, 16, 0); + cb_fix(skip, cp); + } + rload(R); rbranch(R->entb[e]); + } + for (b = 0; b < R->nb && !bad; b++) { lab[b] = cp; bad = remit(R, b); } + if (!bad) { + for (j = 0; j < rnf; j++) { + unsigned *at = rfa[j], *to = lab[rfb[j]]; + if ((*at & 0xfc000000u) == 0x14000000u) *at |= (to - at) & 0x3ffffff; else cb_fix(at, to); + } + R->nat = start; + for (e = 0; e < R->ne; e++) tab[R->ent[e]] = R->stub[e]; + if ((unsigned)R->fn < (unsigned)code_words) { + fn_nat[R->fn].stub = R->stub[0]; + fn_nat[R->fn].fslots = R->fslots; + fn_nat[R->fn].argc = R->argc; + } + syncicache(start, cp); + if (trace) fprintf(stderr, "o3: %d..%d -> %ld bytes of arm64\n", R->lo, R->hi - 1, (long)((char *)cp - (char *)start)); + } else { + cp = start; + if (trace) fprintf(stderr, "o3: %d..%d: a block needs too many registers\n", R->lo, R->hi - 1); + } + free(lab); free(rfa); free(rfb); + if (wr) jitwrite(0); + return bad; +} +static void rcompile(Rg *R, int eager) +{ + if (R->nat || R->nonat) return; + if (rjit(R, eager) && cp - code < CODEW / 2) R->nonat = 1; /* registers, not space: give up on it */ +} + +static int inst_hook(int d) { return install(d); } +static long xrun(long sp, int slot) { return (long)xexec(&enh[slot], (Cell *)sp); } + +static void *translate(int p) +{ + unsigned *start; + int i = p, n = 0, k, t, used[64], nused = 0, j2, ju; + Cell w, x; + if (cp - code > CODEW - 65536 || !ix_room()) flush(); + if (o3ent[p]) { /* an o3 region starts here */ + Rg *R = rat(p); + if (R) { rcompile(R, 0); if (R->nat && tab[p]) return tab[p]; } + } + start = cp; + for (;;) { + if (i >= code_words) { E(0x92800000); b_to(x_exit); break; } /* movn x0, #0: halt */ + if (n++ >= BLKW) { li(W0, i); b_to(x_disp); break; } + w = m[i]; + if (w & 1 && i + 1 < code_words && !(m[i + 1] & 1) && (k = (int)(m[i + 1] / 2)) >= 6 && k <= 8) { + t = (int)((w - 1) / 2); /* literal + call/jmp/jz with a known target */ + if (k == 8) { + unsigned *a; + ldr_post(X10, SP, 8); + a = cp; E(0xb500000a); /* cbnz x10, fallthrough */ + li(W0, t); b_to(x_disp); + cb_fix(a, cp); + i += 2; + continue; + } + li(X10, i + 2); + if (k == 6) str_post(X10, RP, 8); else str_u(X10, RP, 0); + li(W0, t); b_to(x_disp); + i += 2; + break; + } + if (w & 1) { + int L = 0, s, nk, j; + Cell ks[8]; + Enh te; + while (L < 8 && i + L < code_words && (m[i + L] & 1)) L++; + x = i + L < code_words ? m[i + L] : 1; + if (!(x & 1) && (s = (int)(x / 2) - XOP0) >= 0 && s < NENH && enh[s].live && enh[s].nin) { + nk = L < enh[s].nin ? L : enh[s].nin; + for (j = 0; j < nk; j++) ks[j] = m[i + L - nk + j] >> 1; + if (!spec_enh(s, ks, nk, &te)) { + for (j = 0; j < L - nk; j++) { li(X9, m[i + j] >> 1); str_pre(X9, SP, -8); } + if (nused < 64) used[nused++] = s; else used[0] = -1; + emit_enh_e(&te, s); + free(te.u); free(te.reg); + i += L + 1; n += L; + continue; + } + } + li(X9, (w - 1) / 2); str_pre(X9, SP, -8); i++; continue; + } + k = (int)(w / 2); + if (k == 0) { E(0x92800000); b_to(x_exit); i++; break; } + if (k < 0 || k == 6 || k == 7) { + ldr_post(W0, SP, 8); + li(X10, i + 1); + if (k == 6) str_post(X10, RP, 8); else str_u(X10, RP, 0); + if (k < 0) { li(X11, 7 - k); addx_ext(RP, RP, X11, SXTW, 3); } + b_to(x_disp); + i++; + break; + } + if (k == 8) { /* jz: taken leaves, not taken keeps going */ + ldp_post(X9, X10, SP, 16); + E(0xb500006a); /* cbnz x10, +3 */ + movw(W0, X9); + b_to(x_disp); + i++; + continue; + } + if (k == 9) { ldr_pre(W0, RP, -8); b_to(x_disp); i++; break; } + if (k == 21) { /* install: call into c, then return to it so it can flush */ + ldr_u(W0, SP, 0); ldrx_u(X11, ST, 72); blr(X11); + E(0x93407c00); /* sxtw x0, w0: Wn writes zero the high half, so -4 would look positive */ + str_u(W0, SP, 0); + li(W0, i + 1); b_to(x_exit); i++; break; + } + if (k == 600) { /* flit: the next word is raw IEEE bits, even when bit 0 is set */ + if (i + 1 >= code_words) { E(0x92800000); b_to(x_exit); i++; break; } + li(X9, m[i + 1]); + str_pre(X9, SP, -8); + i += 2; + continue; + } + if ((k >= 601 && k <= 611) || k == 618) { fjit(k); i++; continue; } + if (k >= 612 && k <= 617) { bjit(k); i++; continue; } + if (k == 619) { /* push the data-stack index: the word s points at */ + op3(0xcb000000, X9, SP, M); /* sub x9, x20, x19 */ + E(0xd343fd29); /* lsr x9, x9, #3 */ + str_pre(X9, SP, -8); + i++; + continue; + } + if (k == 620) { /* a native call: the helper pops the arguments and pushes the result */ + movw(0, SP); movw(1, RP); + li(X11, (Cell)(uintptr_t)ncall_host); blr(X11); + movw(SP, 0); + i++; + continue; + } + if (k >= XOP0 && k < XOP0 + NENH && enh[k - XOP0].live) { if (nused < 64) used[nused++] = k - XOP0; else used[0] = -1; emit_enh(k - XOP0); i++; continue; } + if (k >= XOP0 && k < XOP0 + NENH) { if (nused < 64) used[nused++] = k - XOP0; else used[0] = -1; } /* unwired now, but an install makes this a different block */ + if (k > 20) { E(0x92800000); b_to(x_exit); i++; break; } /* not wired: halt */ + op(i, k); + i++; + } + j2 = ix_add(p, i); + cinc(cov, p, i); + if (nused && used[0] < 0) dep_add(NENH, j2); + else for (ju = 0; ju < nused; ju++) dep_add(used[ju], j2); + tab[p] = start; + syncicache(start, cp); + if (trace) fprintf(stderr, "jit: %d..%d -> %ld bytes\n", p, i - 1, (long)((char *)cp - (char *)start)); + return start; +} +static int o3_ready(Rg *C) +{ + if (!C || C->dead) return 0; + if (!C->lifted && rlift(C)) { C->dead = 1; return 0; } + if (!C->lifted || C->dead) return 0; + rcompile(C, 1); + return C->nat != 0; +} +static int vm_until(int pc, int stop_eq, int stop_ge, Rg *re, Cell *ps, Cell *pr) +{ + union { unsigned *u; int (*f)(long *, void *); } enter; + void *saved_eq = 0, *saved_ent[RMAXE]; + int i, nent = 0, flushed = 0, ep0 = drop_epoch; + Cell return_depth = *pr - (stack_control ? stack_get(SC_RETURN_LOW) : RS0) - 1; + Cell saved_depth = jit_depth, *saved_links = 0; + if (saved_depth) { + saved_links = malloc((size_t)saved_depth * sizeof *saved_links); + if (!saved_links) return PC_FRAME_OVERFLOW; + memcpy(saved_links, linkstk, (size_t)saved_depth * sizeof *saved_links); + } + jit_depth = 0; + enter.u = x_enter; + if (stop_eq >= 0 && stop_eq < code_words) { saved_eq = tab[stop_eq]; tab[stop_eq] = 0; } + if (re) { + nent = re->ne; if (nent > RMAXE) nent = RMAXE; + for (i = 0; i < nent; i++) { saved_ent[i] = tab[re->ent[i]]; tab[re->ent[i]] = 0; } + } + S[1] = *ps; S[2] = *pr; + while ((unsigned)pc < (unsigned)code_words || pc == PC_DATA_OVERFLOW || pc == PC_RETURN_OVERFLOW) { + void *c; + if (pc == PC_DATA_OVERFLOW || pc == PC_RETURN_OVERFLOW) { + Cell sp = S[1], rp = S[2]; int ret = pc == PC_RETURN_OVERFLOW; + if (!stack_control || stack_expand(ret, stack_get(ret ? SC_RETURN_TOP : SC_DATA_TOP) - stack_get(ret ? SC_RETURN_LOW : SC_DATA_LOW) + 1, &sp, &rp) < 0) break; + S[1] = sp; S[2] = rp; S[11] = (long)(m + stack_data_limit()); S[12] = (long)(m + stack_return_limit()); + pc = (int)S[13]; continue; + } + if (stop_eq >= 0 && pc == stop_eq && S[2] - (stack_control ? stack_get(SC_RETURN_LOW) : RS0) == return_depth) break; + if (stop_ge >= 0 && pc >= stop_ge) break; + if (re && pc >= re->lo && pc < re->hi && o3ent[pc] == re->idx + 1) break; + c = tab[pc]; + if (!c) { + jitwrite(1); c = translate(pc); jitwrite(0); + if (stop_eq >= 0 && stop_eq < code_words) tab[stop_eq] = 0; + if (re) for (i = 0; i < nent; i++) tab[re->ent[i]] = 0; + } + if (!c) break; + pc = enter.f(S, c); + if (need_flush) { need_flush = 0; jitwrite(1); flush(); jitwrite(0); } + } + *ps = S[1]; *pr = S[2]; + jit_depth = saved_depth; + if (saved_links) { memcpy(linkstk, saved_links, (size_t)saved_depth * sizeof *saved_links); free(saved_links); } + flushed = drop_epoch != ep0; + if (!flushed) { + if (stop_eq >= 0 && stop_eq < code_words) tab[stop_eq] = saved_eq; + if (re) for (i = 0; i < nent; i++) tab[re->ent[i]] = saved_ent[i]; + } + return pc; +} +static int o3_exec_region(Rg *C, int entry, Cell *ps, Cell *pr) +{ + union { unsigned *u; int (*f)(long *, void *); } enter; + void *codep; + int back; + Cell saved_depth, *saved_links = 0; + if (!C || C->dead || entry < 0 || entry >= C->ne) return -1; + if (!C->lifted && rlift(C)) { C->dead = 1; return -1; } + rcompile(C, 1); + codep = C->nat ? C->stub[entry] : 0; + if (!codep) return -1; + enter.u = x_enter; + S[1] = *ps; S[2] = *pr; + saved_depth = jit_depth; + if (saved_depth) { + saved_links = malloc((size_t)saved_depth * sizeof *saved_links); + if (!saved_links) return PC_FRAME_OVERFLOW; + memcpy(saved_links, linkstk, (size_t)saved_depth * sizeof *saved_links); + } + jit_depth = 0; + back = enter.f(S, codep); + jit_depth = saved_depth; + if (saved_links) { memcpy(linkstk, saved_links, (size_t)saved_depth * sizeof *saved_links); free(saved_links); } + *ps = S[1]; *pr = S[2]; + return back; +} + +#else +/* portable path for hosts without the arm64 jit: a plain interpreter with + the same semantics (shift counts wrap at 64, stores outside memory are + ignored, loads outside memory read 0, leaving memory halts). */ +#ifdef ZYLLVM +/* ---- llvm backend ---- + a tiered jit for hosts the arm64 backend doesn't cover. the interpreter + counts how often each block start is reached; a hot block is translated to + llvm ir (same block rules as the arm64 jit: it ends at call, jump, ret or + halt, and keeps going past a jz), optimized at O2 and compiled with orc. + enhancements are inlined as ir. stores into compiled words drop the blocks + that cover them, so self-modifying code still works. */ +#include +#include +#include +#include +#include +#include +#include +#include +static int HOT = 5000; /* runs before a block gets compiled: ZYHOT */ +static const char *lpasses = "default"; /* ZYPASSES */ +#define LMAXB 65536 +typedef Cell (*Blk)(Cell *mem, Cell *regs); +static Blk *lt; /* address -> compiled block */ +static unsigned short *hot; +static unsigned char *lcov; /* how many compiled blocks cover the word (255 sticks) */ +static void *lrt[MAXB]; /* each block's resource tracker: removing it frees the block's code */ +static int llvm_ok, nblocks; +static LLVMOrcLLJITRef jit; +static LLVMTargetMachineRef tm_; /* the host cpu, so the pass pipeline's cost models know the target */ +static LLVMOrcThreadSafeContextRef tsc; +static LLVMContextRef ctx; +static LLVMTypeRef i8t, i32t, i64t, ptrt, i1t, f64t; +static LLVMBuilderRef bd; +static LLVMValueRef rvv[RMAXV]; +static LLVMValueRef fn_, vm_, vregs, vs, vr; /* function, memory, regs, s and r slots */ +static LLVMTypeRef l_dret; /* what a register-passing function returns: the next address and s */ +static int l_dm; /* the function being built is the register-passing one */ +static Cell d_r; /* r, left here by a register-passing function that exits abnormally */ +static LLVMModuleRef mod_; + +static unsigned char *rcov; /* how many lifted o3 regions cover the word */ +static int rdrop(int a); +/* code that is dropped is freed when no compiled code is running: a block can + drop itself (it stores into its own words), and a callback can nest */ +static int ccode; +static LLVMOrcResourceTrackerRef pend_rt[1024]; +static int npend_rt; +static void rt_sweep(void) +{ + LLVMErrorRef err; + if (ccode) return; + while (npend_rt > 0) { + LLVMOrcResourceTrackerRef rt = pend_rt[--npend_rt]; + if ((err = LLVMOrcResourceTrackerRemove(rt))) LLVMConsumeError(err); + LLVMOrcReleaseResourceTracker(rt); + } +} +static void rt_queue(void *rt) +{ + if (!rt) return; + if (npend_rt >= 1024) { rt_sweep(); if (npend_rt >= 1024) { LLVMOrcReleaseResourceTracker((LLVMOrcResourceTrackerRef)rt); return; } } + pend_rt[npend_rt++] = (LLVMOrcResourceTrackerRef)rt; +} +static void be_drop(int j) +{ + int a = ix_blk[j].a; + lt[a] = 0; hot[a] = 0; + cdec(lcov, a, ix_blk[j].b); + rt_queue(lrt[j]); lrt[j] = 0; + if (trace) fprintf(stderr, "llvm: dropped %d..%d\n", a, ix_blk[j].b - 1); +} +static void ldrop_all(void) +{ + int j; + rdrop_all(); + for (j = 0; j < ix_n; j++) if (ix_blk[j].a >= 0) { rt_queue(lrt[j]); lrt[j] = 0; } + memset(rcov, 0, (size_t)code_words); + memset(lt, 0, (size_t)code_words * sizeof *lt); memset(lcov, 0, (size_t)code_words); memset(hot, 0, (size_t)code_words * sizeof *hot); ix_reset(); + if (trace) fprintf(stderr, "llvm: dropped every block\n"); +} +/* a store to a word some block covers: write it, drop those blocks */ +static int lhook(Cell a, Cell v) +{ + int hit; + if (m[a] == v) return 0; + m[a] = v; + hit = ix_drop_at((int)a); + if (rcov[a] && rdrop((int)a)) hit = 1; + if (hit) drop_epoch++; + return hit; +} +static Cell linstall(Cell d) { return install((int)d); } + +static LLVMValueRef K32(int v) { return LLVMConstInt(i32t, (unsigned)v, 0); } +static LLVMValueRef Kw(Cell v) { return LLVMConstInt(i64t, (unsigned long long)v, 0); } +static LLVMValueRef cptr(void *p) { return LLVMConstIntToPtr(LLVMConstInt(i64t, (unsigned long long)(size_t)p, 0), ptrt); } +static LLVMValueRef mslot(LLVMValueRef idx) { return LLVMBuildInBoundsGEP2(bd, i64t, vm_, &idx, 1, ""); } +static LLVMValueRef ld(LLVMValueRef idx) { return LLVMBuildLoad2(bd, i64t, mslot(idx), ""); } +static void st(LLVMValueRef idx, LLVMValueRef v) { LLVMBuildStore(bd, v, mslot(idx)); } +static LLVMValueRef stack_ir(void) { return LLVMBuildLoad2(bd, i64t, vs, ""); } +static void set_stack_ir(LLVMValueRef v) { LLVMBuildStore(bd, v, vs); } +static LLVMValueRef add(LLVMValueRef a, int k) { return LLVMBuildAdd(bd, a, Kw(k), ""); } +/* a load that reads 0 outside memory */ +static LLVMValueRef safeld(LLVMValueRef a) +{ + LLVMValueRef in = LLVMBuildICmp(bd, LLVMIntULT, a, Kw(MW), ""); + LLVMValueRef v = ld(LLVMBuildSelect(bd, in, a, Kw(0), "")); + return LLVMBuildSelect(bd, in, v, Kw(0), ""); +} +static void push(LLVMValueRef v) { LLVMValueRef s = add(stack_ir(), -1); set_stack_ir(s); st(s, v); } +/* leave the block: write s and r back, return the next vm address */ +static void leave(LLVMValueRef next) +{ + LLVMValueRef r0 = Kw(0), r1 = Kw(1); + if (l_dm) { + LLVMValueRef agg; + LLVMBuildStore(bd, LLVMBuildLoad2(bd, i64t, vr, ""), cptr(&d_r)); + agg = LLVMBuildInsertValue(bd, LLVMGetUndef(l_dret), next, 0, ""); + agg = LLVMBuildInsertValue(bd, agg, stack_ir(), 1, ""); + LLVMBuildRet(bd, agg); + return; + } + LLVMBuildStore(bd, stack_ir(), LLVMBuildGEP2(bd, i64t, vregs, &r0, 1, "")); + LLVMBuildStore(bd, LLVMBuildLoad2(bd, i64t, vr, ""), LLVMBuildGEP2(bd, i64t, vregs, &r1, 1, "")); + LLVMBuildRet(bd, next); +} +/* a function region returns for good: its frame is dead, so its variables are not written back */ +static void leave_ok(LLVMValueRef next) +{ + if (l_dm) { + LLVMValueRef agg = LLVMBuildInsertValue(bd, LLVMGetUndef(l_dret), next, 0, ""); + agg = LLVMBuildInsertValue(bd, agg, stack_ir(), 1, ""); + LLVMBuildRet(bd, agg); + return; + } + LLVMBuildStore(bd, LLVMBuildLoad2(bd, i64t, rvv[0], ""), mslot(Kw(ZFP))); + leave(next); +} +static LLVMBasicBlockRef newbb(void) { return LLVMAppendBasicBlockInContext(ctx, fn_, ""); } +static LLVMValueRef callp(void *f, LLVMTypeRef ret, LLVMTypeRef *at, int n, LLVMValueRef *args) +{ + LLVMTypeRef ft = LLVMFunctionType(ret, at, (unsigned)n, 0); + return LLVMBuildCall2(bd, ft, cptr(f), args, (unsigned)n, ""); +} +/* one micro-op as ir: the same semantics as ev() */ +static LLVMValueRef ir_op(int op, LLVMValueRef a, LLVMValueRef b, LLVMValueRef c) +{ + LLVMValueRef z = Kw(0), q, d, t, c1, c2, r = 0; + LLVMTypeRef i128 = LLVMIntTypeInContext(ctx, 128); + switch (op) { + case U_ADD: r = LLVMBuildAdd(bd, a, b, ""); break; + case U_SUB: r = LLVMBuildSub(bd, a, b, ""); break; + case U_MUL: r = LLVMBuildMul(bd, a, b, ""); break; + case U_SDIV: case U_SREM: /* /0 and INT64_MIN/-1 are defined, like the cpu */ + c1 = LLVMBuildICmp(bd, LLVMIntEQ, b, z, ""); + c2 = LLVMBuildAnd(bd, LLVMBuildICmp(bd, LLVMIntEQ, a, Kw(INT64_MIN), ""), LLVMBuildICmp(bd, LLVMIntEQ, b, Kw(-1), ""), ""); + d = LLVMBuildSelect(bd, LLVMBuildOr(bd, c1, c2, ""), Kw(1), b, ""); + if (op == U_SDIV) { q = LLVMBuildSDiv(bd, a, d, ""); r = LLVMBuildSelect(bd, c1, z, q, ""); } + else { q = LLVMBuildSRem(bd, a, d, ""); r = LLVMBuildSelect(bd, c1, a, LLVMBuildSelect(bd, c2, z, q, ""), ""); } + break; + case U_UDIV: case U_UREM: + c1 = LLVMBuildICmp(bd, LLVMIntEQ, b, z, ""); + d = LLVMBuildSelect(bd, c1, Kw(1), b, ""); + if (op == U_UDIV) r = LLVMBuildSelect(bd, c1, z, LLVMBuildUDiv(bd, a, d, ""), ""); + else r = LLVMBuildSelect(bd, c1, a, LLVMBuildURem(bd, a, d, ""), ""); + break; + case U_AND: r = LLVMBuildAnd(bd, a, b, ""); break; + case U_OR: r = LLVMBuildOr(bd, a, b, ""); break; + case U_XOR: r = LLVMBuildXor(bd, a, b, ""); break; + case U_SHL: r = LLVMBuildShl(bd, a, LLVMBuildAnd(bd, b, Kw(63), ""), ""); break; + case U_SHR: r = LLVMBuildLShr(bd, a, LLVMBuildAnd(bd, b, Kw(63), ""), ""); break; + case U_SAR: r = LLVMBuildAShr(bd, a, LLVMBuildAnd(bd, b, Kw(63), ""), ""); break; + case U_NEG: r = LLVMBuildSub(bd, z, a, ""); break; + case U_NOT: r = LLVMBuildNot(bd, a, ""); break; + case U_LT: r = LLVMBuildZExt(bd, LLVMBuildICmp(bd, LLVMIntSLT, a, b, ""), i64t, ""); break; + case U_LTU: r = LLVMBuildZExt(bd, LLVMBuildICmp(bd, LLVMIntULT, a, b, ""), i64t, ""); break; + case U_EQ: r = LLVMBuildZExt(bd, LLVMBuildICmp(bd, LLVMIntEQ, a, b, ""), i64t, ""); break; + case U_NE: r = LLVMBuildZExt(bd, LLVMBuildICmp(bd, LLVMIntNE, a, b, ""), i64t, ""); break; + case U_MIN: r = LLVMBuildSelect(bd, LLVMBuildICmp(bd, LLVMIntSLT, a, b, ""), a, b, ""); break; + case U_MAX: r = LLVMBuildSelect(bd, LLVMBuildICmp(bd, LLVMIntSGT, a, b, ""), a, b, ""); break; + case U_MINU: r = LLVMBuildSelect(bd, LLVMBuildICmp(bd, LLVMIntULT, a, b, ""), a, b, ""); break; + case U_MAXU: r = LLVMBuildSelect(bd, LLVMBuildICmp(bd, LLVMIntUGT, a, b, ""), a, b, ""); break; + case U_MULHS: case U_MULHU: { + LLVMValueRef x = op == U_MULHS ? LLVMBuildSExt(bd, a, i128, "") : LLVMBuildZExt(bd, a, i128, ""); + LLVMValueRef y = op == U_MULHS ? LLVMBuildSExt(bd, b, i128, "") : LLVMBuildZExt(bd, b, i128, ""); + r = LLVMBuildTrunc(bd, LLVMBuildLShr(bd, LLVMBuildMul(bd, x, y, ""), LLVMConstInt(i128, 64, 0), ""), i64t, ""); + break; } + case U_CLZ: { + unsigned id = LLVMLookupIntrinsicID("llvm.ctlz", 9); + LLVMValueRef f = LLVMGetIntrinsicDeclaration(mod_, id, &i64t, 1), args[2]; + args[0] = a; args[1] = LLVMConstInt(i1t, 0, 0); + r = LLVMBuildCall2(bd, LLVMIntrinsicGetType(ctx, id, &i64t, 1), f, args, 2, ""); + break; } + case U_CTZ: case U_POPCNT: case U_BSWAP: { + const char *nm = op == U_CTZ ? "llvm.cttz" : op == U_POPCNT ? "llvm.ctpop" : "llvm.bswap"; + unsigned id = LLVMLookupIntrinsicID(nm, strlen(nm)); + LLVMValueRef f = LLVMGetIntrinsicDeclaration(mod_, id, &i64t, 1), args[2]; + args[0] = a; args[1] = LLVMConstInt(i1t, 0, 0); + r = LLVMBuildCall2(bd, LLVMIntrinsicGetType(ctx, id, &i64t, 1), f, args, op == U_CTZ ? 2 : 1, ""); + break; } + case U_ROTL: case U_ROTR: { + const char *nm = op == U_ROTL ? "llvm.fshl" : "llvm.fshr"; + unsigned id = LLVMLookupIntrinsicID(nm, strlen(nm)); + LLVMValueRef f = LLVMGetIntrinsicDeclaration(mod_, id, &i64t, 1), args[3]; + args[0] = a; args[1] = a; args[2] = LLVMBuildAnd(bd, b, Kw(63), ""); + r = LLVMBuildCall2(bd, LLVMIntrinsicGetType(ctx, id, &i64t, 1), f, args, 3, ""); + break; } + case U_F2S: { + LLVMTypeRef f32t = LLVMFloatTypeInContext(ctx), i32t = LLVMInt32TypeInContext(ctx); + LLVMValueRef s = LLVMBuildFPTrunc(bd, LLVMBuildBitCast(bd, a, f64t, ""), f32t, ""); + r = LLVMBuildZExt(bd, LLVMBuildBitCast(bd, s, i32t, ""), i64t, ""); + break; } + case U_S2F: { + LLVMTypeRef f32t = LLVMFloatTypeInContext(ctx), i32t = LLVMInt32TypeInContext(ctx); + LLVMValueRef s = LLVMBuildBitCast(bd, LLVMBuildTrunc(bd, a, i32t, ""), f32t, ""); + r = LLVMBuildBitCast(bd, LLVMBuildFPExt(bd, s, f64t, ""), i64t, ""); + break; } + case U_ABS: t = LLVMBuildICmp(bd, LLVMIntSLT, a, z, ""); r = LLVMBuildSelect(bd, t, LLVMBuildSub(bd, z, a, ""), a, ""); break; + case U_SEL: r = LLVMBuildSelect(bd, LLVMBuildICmp(bd, LLVMIntNE, a, z, ""), b, c, ""); break; + case U_FADD: case U_FSUB: case U_FMUL: case U_FDIV: case U_FEQ: case U_FLT: case U_FLE: case U_I2F: case U_F2I: { + LLVMValueRef da = LLVMBuildBitCast(bd, a, f64t, ""), db = b ? LLVMBuildBitCast(bd, b, f64t, "") : 0, dv = 0; + if (op == U_FADD) dv = LLVMBuildFAdd(bd, da, db, ""); + else if (op == U_FSUB) dv = LLVMBuildFSub(bd, da, db, ""); + else if (op == U_FMUL) dv = LLVMBuildFMul(bd, da, db, ""); + else if (op == U_FDIV) dv = LLVMBuildFDiv(bd, da, db, ""); + else if (op == U_FEQ) dv = LLVMBuildZExt(bd, LLVMBuildFCmp(bd, LLVMRealOEQ, da, db, ""), i64t, ""); + else if (op == U_FLT) dv = LLVMBuildZExt(bd, LLVMBuildFCmp(bd, LLVMRealOLT, da, db, ""), i64t, ""); + else if (op == U_FLE) dv = LLVMBuildZExt(bd, LLVMBuildFCmp(bd, LLVMRealOLE, da, db, ""), i64t, ""); + else if (op == U_I2F) dv = LLVMBuildSIToFP(bd, a, f64t, ""); + else dv = LLVMBuildFPToSI(bd, da, i64t, ""); + if (op == U_FADD || op == U_FSUB || op == U_FMUL || op == U_FDIV || op == U_I2F) dv = LLVMBuildBitCast(bd, dv, i64t, ""); + r = dv; + break; } + } + return r; +} +/* byte address into the flat image. an out-of-range load is 0. a store that + misses the image does nothing, and one that touches translated code goes + through the same helper as a word store so the translation is dropped. */ +static LLVMValueRef bload_ir(LLVMValueRef addr, int n) +{ + LLVMTypeRef ty = LLVMIntTypeInContext(ctx, (unsigned)n * 8); + LLVMValueRef lim = Kw(((Cell)MW << 3) - (n - 1)); + LLVMValueRef in = LLVMBuildICmp(bd, LLVMIntULT, addr, lim, ""); + LLVMValueRef safe = LLVMBuildSelect(bd, in, addr, Kw(0), ""); + LLVMValueRef p = LLVMBuildGEP2(bd, i8t, vm_, &safe, 1, ""); + LLVMValueRef ldv = LLVMBuildLoad2(bd, ty, p, ""); + LLVMValueRef z; + LLVMSetAlignment(ldv, 1); + z = LLVMBuildZExt(bd, ldv, i64t, ""); + return LLVMBuildSelect(bd, in, z, Kw(0), ""); +} +static void bstore_ir(LLVMValueRef addr, LLVMValueRef val, int n) +{ + LLVMTypeRef ty = LLVMIntTypeInContext(ctx, (unsigned)n * 8); + LLVMBasicBlockRef inb, callb, inl, out; + LLVMValueRef lim, word, endw, c1, c2, covered, p, stv, args[2]; + LLVMTypeRef at[2]; + void *fn = n == 1 ? (void *)bst8 : n == 2 ? (void *)bst16 : (void *)bst32; + lim = Kw(((Cell)MW << 3) - (n - 1)); + inb = newbb(); callb = newbb(); inl = newbb(); out = newbb(); + LLVMBuildCondBr(bd, LLVMBuildICmp(bd, LLVMIntULT, addr, lim, ""), inb, out); + LLVMPositionBuilderAtEnd(bd, inb); + word = LLVMBuildLShr(bd, addr, Kw(3), ""); + endw = LLVMBuildLShr(bd, LLVMBuildAdd(bd, addr, Kw(n - 1), ""), Kw(3), ""); + c1 = LLVMBuildLoad2(bd, i8t, LLVMBuildGEP2(bd, i8t, cptr(lcov), &word, 1, ""), ""); + c2 = LLVMBuildLoad2(bd, i8t, LLVMBuildGEP2(bd, i8t, cptr(lcov), &endw, 1, ""), ""); + covered = LLVMBuildOr(bd, LLVMBuildICmp(bd, LLVMIntNE, c1, LLVMConstInt(i8t, 0, 0), ""), + LLVMBuildICmp(bd, LLVMIntNE, c2, LLVMConstInt(i8t, 0, 0), ""), ""); + LLVMBuildCondBr(bd, covered, callb, inl); + LLVMPositionBuilderAtEnd(bd, callb); + at[0] = i64t; at[1] = i64t; args[0] = addr; args[1] = val; + callp(fn, LLVMVoidTypeInContext(ctx), at, 2, args); + LLVMBuildBr(bd, out); + LLVMPositionBuilderAtEnd(bd, inl); + p = LLVMBuildGEP2(bd, i8t, vm_, &addr, 1, ""); + stv = LLVMBuildStore(bd, LLVMBuildTrunc(bd, val, ty, ""), p); + LLVMSetAlignment(stv, 1); + LLVMBuildBr(bd, out); + LLVMPositionBuilderAtEnd(bd, out); +} +/* an enhancement as ir */ +static void ir_enh(Enh *e) +{ + LLVMValueRef *v = calloc(e->nv + 1, sizeof *v), s = stack_ir(); + int i, j; + for (j = 0; j < e->nin; j++) v[j] = ld(add(s, e->nin - 1 - j)); + for (i = 0; i < e->n; i++) { + Uop *u = &e->u[i]; + LLVMValueRef a = u->op == U_LI ? 0 : v[u->a], b = uarity[u->op] > 1 || is_store(u->op) ? v[u->b] : 0, c = uarity[u->op] > 2 ? v[u->c] : 0, r = 0; + switch (u->op) { + case U_LI: r = Kw(u->imm); break; + case U_LD: r = safeld(a); break; + case U_ST: { + LLVMBasicBlockRef yes = newbb(), done = newbb(); + LLVMBuildCondBr(bd, LLVMBuildICmp(bd, LLVMIntULT, a, Kw(MW), ""), yes, done); + LLVMPositionBuilderAtEnd(bd, yes); st(a, b); LLVMBuildBr(bd, done); + LLVMPositionBuilderAtEnd(bd, done); + break; } + case U_LD8: case U_LD16: case U_LD32: r = bload_ir(a, bwidth(u->op)); break; + case U_ST8: case U_ST16: case U_ST32: bstore_ir(a, b, bwidth(u->op)); break; + default: r = ir_op(u->op, a, b, c); + } + if (!is_store(u->op)) v[u->d] = r; + } + s = add(s, e->nin - e->nout); set_stack_ir(s); + for (j = 0; j < e->nout; j++) st(add(s, e->nout - 1 - j), v[e->out[j]]); + free(v); +} +static void llvm_init(void) +{ + LLVMErrorRef err; + if (llvm_ok) return; /* a nested run() must keep the jit it already built */ + if (getenv("ZYHOT")) HOT = atoi(getenv("ZYHOT")); + if (HOT < 1) HOT = 1; + if (HOT > 65000) HOT = 65000; + if (getenv("ZYPASSES")) lpasses = getenv("ZYPASSES"); + LLVMInitializeNativeTarget(); LLVMInitializeNativeAsmPrinter(); + if ((err = LLVMOrcCreateLLJIT(&jit, 0))) { char *msg = LLVMGetErrorMessage(err); fprintf(stderr, "llvm: %s\n", msg); LLVMDisposeErrorMessage(msg); return; } +#if LLVM_VERSION_MAJOR >= 23 + ctx = LLVMContextCreate(); + tsc = LLVMOrcCreateNewThreadSafeContextFromLLVMContext(ctx); +#else + tsc = LLVMOrcCreateNewThreadSafeContext(); + ctx = LLVMOrcThreadSafeContextGetContext(tsc); +#endif + { /* a target machine for the same host the jit generates for: llvm's vectorizer, + unroller and inliner price instructions from it, and the passes run before + the module reaches the jit, so without one they would price for nothing */ + LLVMTargetRef tg; char *e = 0, *cpu = LLVMGetHostCPUName(), *feat = LLVMGetHostCPUFeatures(); + const char *tr = LLVMOrcLLJITGetTripleString(jit); + if (!LLVMGetTargetFromTriple(tr, &tg, &e)) + tm_ = LLVMCreateTargetMachine(tg, tr, cpu, feat, LLVMCodeGenLevelAggressive, LLVMRelocDefault, LLVMCodeModelJITDefault); + else LLVMDisposeMessage(e); + LLVMDisposeMessage(cpu); LLVMDisposeMessage(feat); + } + i1t = LLVMInt1TypeInContext(ctx); i8t = LLVMInt8TypeInContext(ctx); i32t = LLVMInt32TypeInContext(ctx); + i64t = LLVMInt64TypeInContext(ctx); f64t = LLVMDoubleTypeInContext(ctx); ptrt = LLVMPointerTypeInContext(ctx, 0); + bd = LLVMCreateBuilderInContext(ctx); + llvm_ok = 1; +} +/* a new module for the jit's triple and data layout, and a function with what is + known about its two pointers: the vm memory is MW aligned words, the register + pair is two. no noalias: the host helpers it calls read and write that memory. */ +static void lfunc_attrs(LLVMValueRef f) +{ + static unsigned kn, ka, kd, knu; + LLVMValueRef p0 = LLVMGetParam(f, 0); + (void)p0; + if (!kn) { + kn = LLVMGetEnumAttributeKindForName("nonnull", 7); + ka = LLVMGetEnumAttributeKindForName("align", 5); + kd = LLVMGetEnumAttributeKindForName("dereferenceable", 15); + knu = LLVMGetEnumAttributeKindForName("nounwind", 8); + } + LLVMAddAttributeAtIndex(f, 1, LLVMCreateEnumAttribute(ctx, kn, 0)); + LLVMAddAttributeAtIndex(f, 1, LLVMCreateEnumAttribute(ctx, ka, 8)); + LLVMAddAttributeAtIndex(f, 1, LLVMCreateEnumAttribute(ctx, kd, (unsigned long long)MW * 8)); + LLVMAddAttributeAtIndex(f, 2, LLVMCreateEnumAttribute(ctx, kn, 0)); + LLVMAddAttributeAtIndex(f, 2, LLVMCreateEnumAttribute(ctx, ka, 8)); + LLVMAddAttributeAtIndex(f, 2, LLVMCreateEnumAttribute(ctx, kd, 16)); + LLVMAddAttributeAtIndex(f, (LLVMAttributeIndex)-1, LLVMCreateEnumAttribute(ctx, knu, 0)); +} +static void lmodule_target(void) +{ + LLVMSetTarget(mod_, LLVMOrcLLJITGetTripleString(jit)); + LLVMSetDataLayout(mod_, LLVMOrcLLJITGetDataLayoutStr(jit)); +} +static void lrun_passes(const char *pipeline) +{ + LLVMErrorRef err; + LLVMPassBuilderOptionsRef po = LLVMCreatePassBuilderOptions(); + if ((err = LLVMRunPasses(mod_, pipeline, tm_, po))) LLVMConsumeError(err); + LLVMDisposePassBuilderOptions(po); +} +/* compile the block at p; 0 if it couldn't be */ +static Blk lcompile(int p) +{ + char name[32]; LLVMTypeRef pt[2], ft; LLVMValueRef s, a, b, v, r, args[2]; LLVMTypeRef at[2]; + LLVMOrcExecutorAddress addr; LLVMErrorRef err; LLVMPassBuilderOptionsRef po; + int i = p, n = 0, k, t, done = 0, used[64], nused = 0, jb, ju; Cell w; + LLVMOrcResourceTrackerRef rt; + if (!llvm_ok) return 0; + if (!ix_room()) ldrop_all(); + sprintf(name, "b%d_%d", p, nblocks++); + mod_ = LLVMModuleCreateWithNameInContext(name, ctx); + lmodule_target(); + pt[0] = ptrt; pt[1] = ptrt; + ft = LLVMFunctionType(i64t, pt, 2, 0); + fn_ = LLVMAddFunction(mod_, name, ft); + lfunc_attrs(fn_); + vm_ = LLVMGetParam(fn_, 0); vregs = LLVMGetParam(fn_, 1); + LLVMPositionBuilderAtEnd(bd, newbb()); + vs = LLVMBuildAlloca(bd, i64t, "s"); vr = LLVMBuildAlloca(bd, i64t, "r"); + { LLVMValueRef r0 = Kw(0), r1 = Kw(1); + set_stack_ir(LLVMBuildLoad2(bd, i64t, LLVMBuildGEP2(bd, i64t, vregs, &r0, 1, ""), "")); + LLVMBuildStore(bd, LLVMBuildLoad2(bd, i64t, LLVMBuildGEP2(bd, i64t, vregs, &r1, 1, ""), ""), vr); } + while (!done) { + if (i >= code_words) { leave(Kw(PC_HALT)); break; } + if (n++ >= 1024) { leave(Kw(i)); break; } + w = m[i]; + if (w & 1) { + /* a literal followed by call, jump or jz: a known target */ + if (i + 1 < code_words && !(m[i + 1] & 1) && (k = (int)(m[i + 1] / 2)) >= 6 && k <= 8) { + t = (int)(w >> 1); + if (k == 8) { + LLVMBasicBlockRef tk = newbb(), go = newbb(); + s = stack_ir(); a = ld(s); set_stack_ir(add(s, 1)); + LLVMBuildCondBr(bd, LLVMBuildICmp(bd, LLVMIntEQ, a, Kw(0), ""), tk, go); + LLVMPositionBuilderAtEnd(bd, tk); leave(Kw(t)); + LLVMPositionBuilderAtEnd(bd, go); + i += 2; continue; + } + if (k == 6) { r = LLVMBuildLoad2(bd, i64t, vr, ""); st(r, Kw(i + 2)); LLVMBuildStore(bd, add(r, 1), vr); } + leave(Kw(t)); i += 2; break; + } + push(Kw(w >> 1)); i++; continue; + } + k = (int)(w / 2); + s = stack_ir(); + switch (k) { + case 0: leave(Kw(PC_HALT)); done = 1; break; + case 1: st(s, safeld(ld(s))); break; + case 2: { + LLVMBasicBlockRef rng = newbb(), in = newbb(), cv = newbb(), plain = newbb(), out = newbb(), quit = newbb(), gate = newbb(); + LLVMTypeRef gt[4]; + LLVMValueRef gv[4]; + a = ld(s); b = ld(add(s, 1)); set_stack_ir(add(s, 2)); + LLVMBuildCondBr(bd, LLVMBuildICmp(bd, LLVMIntEQ, a, Kw(SYSGATE), ""), gate, rng); + LLVMPositionBuilderAtEnd(bd, gate); + gt[0] = gt[1] = gt[2] = gt[3] = i64t; + gv[0] = b; gv[1] = ld(Kw(SYSGATE + 1)); gv[2] = ld(Kw(SYSGATE + 2)); gv[3] = ld(Kw(SYSGATE + 3)); + LLVMBuildStore(bd, stack_ir(), cptr(&cb_s)); + LLVMBuildStore(bd, LLVMBuildLoad2(bd, i64t, vr, ""), cptr(&cb_r)); + st(Kw(SYSGATE), callp((void *)hostcall, i64t, gt, 4, gv)); + { + LLVMBasicBlockRef moved = newbb(); + LLVMValueRef moving = LLVMBuildAnd(bd, LLVMBuildICmp(bd, LLVMIntEQ, b, Kw(9), ""), + LLVMBuildOr(bd, LLVMBuildICmp(bd, LLVMIntEQ, gv[1], Kw(7), ""), LLVMBuildICmp(bd, LLVMIntEQ, gv[1], Kw(8), ""), ""), ""); + LLVMBuildCondBr(bd, moving, moved, out); + LLVMPositionBuilderAtEnd(bd, moved); + set_stack_ir(LLVMBuildLoad2(bd, i64t, cptr(&cb_s), "")); + LLVMBuildStore(bd, LLVMBuildLoad2(bd, i64t, cptr(&cb_r), ""), vr); + LLVMBuildBr(bd, out); + } + LLVMPositionBuilderAtEnd(bd, rng); + LLVMBuildCondBr(bd, LLVMBuildICmp(bd, LLVMIntULT, a, Kw(MW), ""), in, out); + LLVMPositionBuilderAtEnd(bd, in); + v = LLVMBuildLoad2(bd, i8t, LLVMBuildGEP2(bd, i8t, cptr(lcov), &a, 1, ""), ""); + LLVMBuildCondBr(bd, LLVMBuildICmp(bd, LLVMIntNE, v, LLVMConstInt(i8t, 0, 0), ""), cv, plain); + LLVMPositionBuilderAtEnd(bd, plain); st(a, b); LLVMBuildBr(bd, out); + LLVMPositionBuilderAtEnd(bd, cv); + at[0] = i64t; at[1] = i64t; args[0] = a; args[1] = b; + v = callp((void *)lhook, i32t, at, 2, args); + LLVMBuildCondBr(bd, LLVMBuildICmp(bd, LLVMIntNE, v, K32(0), ""), quit, out); + LLVMPositionBuilderAtEnd(bd, quit); leave(Kw(i + 1)); /* a block died: back to the loop */ + LLVMPositionBuilderAtEnd(bd, out); + break; } + case 3: a = ld(s); st(s, safeld(LLVMBuildAdd(bd, add(s, 1), a, ""))); break; + case 4: set_stack_ir(add(s, 1)); break; + case 5: a = ld(s); b = ld(add(s, 1)); st(s, b); st(add(s, 1), a); break; + case 6: case 7: + a = ld(s); set_stack_ir(add(s, 1)); + if (k == 6) { r = LLVMBuildLoad2(bd, i64t, vr, ""); st(r, Kw(i + 1)); LLVMBuildStore(bd, add(r, 1), vr); } + leave(a); done = 1; break; + case 8: { + LLVMBasicBlockRef tk = newbb(), go = newbb(); + a = ld(s); b = ld(add(s, 1)); set_stack_ir(add(s, 2)); + LLVMBuildCondBr(bd, LLVMBuildICmp(bd, LLVMIntEQ, b, Kw(0), ""), tk, go); + LLVMPositionBuilderAtEnd(bd, tk); leave(a); + LLVMPositionBuilderAtEnd(bd, go); + break; } + case 9: + r = add(LLVMBuildLoad2(bd, i64t, vr, ""), -1); LLVMBuildStore(bd, r, vr); + leave(ld(r)); done = 1; break; + case 10: v = callp((void *)Q, i8t, 0, 0, 0); push(LLVMBuildSExt(bd, v, i64t, "")); break; + case 11: + if (sizeof J == 1) push(LLVMBuildZExt(bd, LLVMBuildLoad2(bd, i8t, cptr(&J), ""), i64t, "")); + else push(LLVMBuildSExt(bd, LLVMBuildLoad2(bd, i32t, cptr(&J), ""), i64t, "")); + break; + case 12: at[0] = i8t; args[0] = LLVMBuildTrunc(bd, ld(s), i8t, ""); callp((void *)G, LLVMVoidTypeInContext(ctx), at, 1, args); set_stack_ir(add(s, 1)); break; + case 13: case 14: case 15: case 16: case 17: case 18: case 19: case 20: + a = ld(s); b = ld(add(s, 1)); s = add(s, 1); set_stack_ir(s); + switch (k) { + case 13: v = LLVMBuildAdd(bd, b, a, ""); break; + case 14: v = LLVMBuildSub(bd, b, a, ""); break; + case 15: v = LLVMBuildMul(bd, b, a, ""); break; + case 16: v = LLVMBuildAnd(bd, b, a, ""); break; + case 17: v = LLVMBuildOr(bd, b, a, ""); break; + case 18: v = LLVMBuildXor(bd, b, a, ""); break; + case 19: v = LLVMBuildAShr(bd, b, LLVMBuildAnd(bd, a, Kw(63), ""), ""); break; + default: v = LLVMBuildZExt(bd, LLVMBuildICmp(bd, LLVMIntSLT, b, a, ""), i64t, ""); + } + st(s, v); break; + case 21: /* install: then back to the loop, which drops every block */ + at[0] = i64t; args[0] = ld(s); + st(s, callp((void *)linstall, i64t, at, 1, args)); + leave(Kw(i + 1)); done = 1; break; + case 600: /* flit: next word is raw bits */ + if (i + 1 >= code_words) { leave(Kw(PC_HALT)); done = 1; break; } + i++; push(Kw(m[i])); break; + case 601: case 602: case 603: case 604: case 605: case 606: case 607: { + LLVMValueRef da, db, dv; + a = ld(s); b = ld(add(s, 1)); s = add(s, 1); set_stack_ir(s); + da = LLVMBuildBitCast(bd, a, f64t, ""); + db = LLVMBuildBitCast(bd, b, f64t, ""); + if (k == 601) dv = LLVMBuildFAdd(bd, db, da, ""); + else if (k == 602) dv = LLVMBuildFSub(bd, db, da, ""); + else if (k == 603) dv = LLVMBuildFMul(bd, db, da, ""); + else if (k == 604) dv = LLVMBuildFDiv(bd, db, da, ""); + else if (k == 605) dv = LLVMBuildZExt(bd, LLVMBuildFCmp(bd, LLVMRealOEQ, db, da, ""), i64t, ""); + else if (k == 606) dv = LLVMBuildZExt(bd, LLVMBuildFCmp(bd, LLVMRealOLT, db, da, ""), i64t, ""); + else dv = LLVMBuildZExt(bd, LLVMBuildFCmp(bd, LLVMRealOLE, db, da, ""), i64t, ""); + if (k <= 604) dv = LLVMBuildBitCast(bd, dv, i64t, ""); + st(s, dv); + break; } + case 608: + st(s, LLVMBuildBitCast(bd, LLVMBuildSIToFP(bd, ld(s), f64t, ""), i64t, "")); + break; + case 609: + st(s, LLVMBuildFPToSI(bd, LLVMBuildBitCast(bd, ld(s), f64t, ""), i64t, "")); + break; + case 610: { + LLVMTypeRef ft = i64t; LLVMValueRef arg = ld(s); + callp((void *)fop_print, LLVMVoidTypeInContext(ctx), &ft, 1, &arg); + set_stack_ir(add(s, 1)); + break; } + case 611: { + LLVMTypeRef ft = i64t; LLVMValueRef arg = ld(s); + st(s, callp((void *)fop_parse_cell, i64t, &ft, 1, &arg)); + break; } + case 618: { /* (bits prec mode --) */ + LLVMTypeRef ft[3]; + LLVMValueRef args3[3]; + ft[0] = ft[1] = ft[2] = i64t; + args3[0] = ld(add(s, 2)); + args3[1] = ld(add(s, 1)); + args3[2] = ld(s); + callp((void *)fop_print_mode, LLVMVoidTypeInContext(ctx), ft, 3, args3); + set_stack_ir(add(s, 3)); + break; } + case 612: case 614: case 616: { + LLVMTypeRef ft = i64t; LLVMValueRef arg = ld(s); + void *f = k == 612 ? (void *)bld8 : k == 614 ? (void *)bld16 : (void *)bld32; + st(s, callp(f, i64t, &ft, 1, &arg)); + break; } + case 619: push(s); break; + case 620: { + LLVMTypeRef ft = i64t; LLVMValueRef arg = s; + LLVMBuildStore(bd, LLVMBuildLoad2(bd, i64t, vr, ""), cptr(&cb_r)); + set_stack_ir(callp((void *)ncall_exec, i64t, &ft, 1, &arg)); + break; } + case 613: case 615: case 617: { + LLVMTypeRef ft[2], vt = LLVMVoidTypeInContext(ctx); + LLVMValueRef args[2]; + void *f = k == 613 ? (void *)bst8 : k == 615 ? (void *)bst16 : (void *)bst32; + ft[0] = i64t; ft[1] = i64t; + args[0] = ld(s); args[1] = ld(add(s, 1)); + callp(f, vt, ft, 2, args); + set_stack_ir(add(s, 2)); + break; } + default: + if (k >= XOP0 && k < XOP0 + NENH) { if (nused < 64) used[nused++] = k - XOP0; else used[0] = -1; } + if (k >= XOP0 && k < XOP0 + NENH && enh[k - XOP0].live) { ir_enh(&enh[k - XOP0]); break; } + leave(Kw(PC_HALT)); done = 1; /* not wired: halt */ + } + i++; + } + jb = ix_add(p, i); + cinc(lcov, p, i); + if (nused && used[0] < 0) dep_add(NENH, jb); + else for (ju = 0; ju < nused; ju++) dep_add(used[ju], jb); + lrun_passes(lpasses); + rt = LLVMOrcJITDylibCreateResourceTracker(LLVMOrcLLJITGetMainJITDylib(jit)); + if ((err = LLVMOrcLLJITAddLLVMIRModuleWithRT(jit, rt, LLVMOrcCreateNewThreadSafeModule(mod_, tsc)))) { + char *msg = LLVMGetErrorMessage(err); fprintf(stderr, "llvm: %s\n", msg); LLVMDisposeErrorMessage(msg); LLVMOrcReleaseResourceTracker(rt); return 0; + } + lrt[jb] = rt; + if ((err = LLVMOrcLLJITLookup(jit, &addr, name))) { char *msg = LLVMGetErrorMessage(err); fprintf(stderr, "llvm: %s\n", msg); LLVMDisposeErrorMessage(msg); return 0; } + if (trace) fprintf(stderr, "llvm: %d..%d compiled\n", p, i - 1); + return (Blk)(size_t)addr; +} +#endif + +/* ---- o3 regions without the arm64 jit: compiled by llvm, or run here ---- */ +#ifndef ZYLLVM +static unsigned char *rcov; /* 1 on the words of a lifted region */ +#endif +static Cell rvals[MAXUOP * 4 + 16]; +#define RPOOL 40 +static Cell rpool[RPOOL][MAXUOP * 4 + 16]; +static int rpool_n; +static int rdrop(int a) /* a store landed on region code: drop those regions */ +{ + int j, hit = 0; + for (j = 0; j < nrg; j++) + if (rgs[j]->lifted && rgs[j]->lo <= a && a < rgs[j]->hi) { + if (trace) fprintf(stderr, "o3: dropped %d..%d (a store to %d: %d)\n", rgs[j]->lo, rgs[j]->hi - 1, a, m[a]); + rclear(rgs[j]); hit = 1; + } + return hit; +} +#ifdef ZYLLVM +static int lhook(Cell a, Cell v); +#define RSTORE(a, b) do { if (inmem(a)) { if (lcov[(int)(a)]) lhook(a, b); else m[a] = (Cell)(b); } } while (0) +#else +#define RSTORE(a, b) do { if ((unsigned)(a) < MW) { m[a] = b; if (rcov[a]) rdrop(a); } } while (0) +#endif +/* the region interpreter: ssa values in an array, vars in locals */ +static int rexec(Rg *R, int e, int *ps, int *pr) +{ + Cell var[RMAXV], nv2[RMAXV], *v, t = 0, npend[16]; + int s = *ps, r = *pr, b = R->entb[e], fp, j, i, o, pooled = 0, mine = 0; + if (rpool_n < RPOOL) { v = rpool[rpool_n++]; pooled = 1; } + else { v = malloc((MAXUOP * 4 + 16) * sizeof(Cell)); if (v) mine = 1; else v = rvals; } + var[0] = fp = mread(ZFP); + for (j = 1; j < R->nv; j++) var[j] = mread(fp + R->slot[j]); + for (;;) { + RB *B = &R->b[b]; + for (i = 0; i < B->n; i++) { + Uop *u = &B->u[i]; + switch (u->op) { + case U_LI: v[u->d] = u->imm; break; + case U_VGET: v[u->d] = var[u->imm]; break; + case U_SLD: v[u->d] = mread(s + u->imm); break; + case U_LD: v[u->d] = mread(v[u->a]); break; + case U_ST: RSTORE(v[u->a], v[u->b]); break; + case U_NARG: npend[u->imm] = v[u->a]; break; + case U_NCALL: +#ifdef ZYFFI + cb_s = s; cb_r = r; + v[u->d] = ffi_invoke(ncd[u->imm].f, ncd[u->imm].sig, npend, 0); +#endif + break; + default: + if (u->op >= U_LD8 && u->op <= U_ST32) { + if (u->op == U_LD8) v[u->d] = bld8(v[u->a]); + else if (u->op == U_LD16) v[u->d] = bld16(v[u->a]); + else if (u->op == U_LD32) v[u->d] = bld32(v[u->a]); + else if (u->op == U_ST8) bst8(v[u->a], v[u->b]); + else if (u->op == U_ST16) bst16(v[u->a], v[u->b]); + else if (u->op == U_ST32) bst32(v[u->a], v[u->b]); + break; + } + v[u->d] = ev(u->op, v[u->a], v[u->b], v[u->c]); + } + } + for (j = 0; j < B->nso; j++) { o = s + B->so[2 * j]; if ((unsigned)o < MW) m[o] = v[B->so[2 * j + 1]]; } + s += B->delta; + for (j = 1; j < R->nv; j++) if (B->vs[j] >= 0) nv2[j] = v[B->vs[j]]; + for (j = 1; j < R->nv; j++) if (B->vs[j] >= 0) var[j] = nv2[j]; + if (B->term == T_BR) { b = B->s1; continue; } + if (B->term == T_CBR) { + if (!v[B->cv]) { if (B->s1 >= 0) { b = B->s1; continue; } t = B->x1; } + else { if (B->s2 >= 0) { b = B->s2; continue; } t = B->x2; } + } else if (B->term == T_JMP) t = v[B->cv]; + else if (B->term == T_CALL) { + Cell cs, cr; + for (j = 0; j < R->np; j++) if ((unsigned)(fp + R->ps[j]) < MW) m[fp + R->ps[j]] = var[R->pv[j]]; + cs = s; cr = r; + o3_invoke(&cs, &cr, v[B->cv], B->x2); + s = (int)cs; r = (int)cr; + if (halt_at >= 0) { t = halt_at; break; } + var[0] = fp = mread(ZFP); + for (j = 1; j < R->nv; j++) var[j] = mread(fp + R->slot[j]); + if ((unsigned)B->x2 < (unsigned)code_words && o3ent[B->x2] == R->idx + 1) { b = R->entb[o3ei[B->x2]]; continue; } + t = B->x2; + break; + } + else t = m[--r]; + break; + } + for (j = 0; j < R->np; j++) if ((unsigned)(fp + R->ps[j]) < MW) m[fp + R->ps[j]] = var[R->pv[j]]; + *ps = s; *pr = r; + if (pooled) rpool_n--; + if (mine) free(v); + return (int)t; +} +#ifndef ZYLLVM +static void be_drop(int j) { (void)j; } +static void rforget(Rg *R) { (void)R; } +static void rmark(Rg *R) { R->marked = 1; cinc(rcov, R->lo, R->hi); } +static void runmark(Rg *R) { cdec(rcov, R->lo, R->hi); } +static void rcompile(Rg *R, int eager) { (void)R; (void)eager; } +#endif +#ifdef ZYLLVM + +/* ---- o3 regions in llvm: one function per region, vars in allocas (so + mem2reg makes them ssa across blocks), each block's micro-ops as ir, then + the O3 pipeline. f(mem, regs, entry) returns the next vm address. */ +typedef Cell (*RFn)(Cell *mem, Cell *regs, int ent); +static int nregions; +static void rforget(Rg *R) { rt_queue(R->rt); R->rt = 0; R->nat = 0; R->dnat = 0; } +static void rmark(Rg *R) { R->marked = 1; cinc(rcov, R->lo, R->hi); cinc(lcov, R->lo, R->hi); } +static void runmark(Rg *R) { cdec(rcov, R->lo, R->hi); cdec(lcov, R->lo, R->hi); } +static LLVMValueRef as_f64(LLVMValueRef v, int fp) { return fp ? v : LLVMBuildBitCast(bd, v, f64t, ""); } +static LLVMValueRef as_i64(LLVMValueRef v, int fp) { return fp ? LLVMBuildBitCast(bd, v, i64t, "") : v; } +/* the variables go back to their frame slots. before a call from inside the + region only the ones that may hold a pointer need to (the rest are never + looked at again by anything outside: the region goes on with its own + copies), and the frame pointer word is not touched */ +static void rspill_vars(Rg *R, int ptr_only) +{ + LLVMValueRef fp = LLVMBuildLoad2(bd, i64t, rvv[0], ""); + int j; + for (j = 0; j < R->np; j++) { + int v = R->pv[j]; + if (ptr_only && R->sc[v]) continue; + LLVMValueRef bits = R->fv[v] ? LLVMBuildBitCast(bd, LLVMBuildLoad2(bd, f64t, rvv[v], ""), i64t, "") : LLVMBuildLoad2(bd, i64t, rvv[v], ""); + st(add(fp, R->ps[j]), bits); + } +} +static void rspill_ir(Rg *R) +{ + st(Kw(ZFP), LLVMBuildLoad2(bd, i64t, rvv[0], "")); + rspill_vars(R, 0); +} +static LLVMBasicBlockRef rexit_bb(Rg *R, int t) +{ + LLVMBasicBlockRef here = LLVMGetInsertBlock(bd), x = newbb(); + LLVMPositionBuilderAtEnd(bd, x); + if (R->fn >= 0 && t >= R->hi) leave_ok(Kw(t)); + else { rspill_ir(R); leave(Kw(t)); } + LLVMPositionBuilderAtEnd(bd, here); + return x; +} +/* m[a] = v, through lhook if some compiled code covers the word */ +static void rstore_ir(LLVMValueRef a, LLVMValueRef b) +{ + LLVMBasicBlockRef in = newbb(), cv = newbb(), plain = newbb(), out = newbb(); + LLVMValueRef v, args[2]; LLVMTypeRef at[2]; + LLVMBuildCondBr(bd, LLVMBuildICmp(bd, LLVMIntULT, a, Kw(MW), ""), in, out); + LLVMPositionBuilderAtEnd(bd, in); + v = LLVMBuildLoad2(bd, i8t, LLVMBuildGEP2(bd, i8t, cptr(lcov), &a, 1, ""), ""); + LLVMBuildCondBr(bd, LLVMBuildICmp(bd, LLVMIntNE, v, LLVMConstInt(i8t, 0, 0), ""), cv, plain); + LLVMPositionBuilderAtEnd(bd, plain); st(a, b); LLVMBuildBr(bd, out); + LLVMPositionBuilderAtEnd(bd, cv); + at[0] = i64t; at[1] = i64t; args[0] = a; args[1] = b; + callp((void *)lhook, i32t, at, 2, args); + LLVMBuildBr(bd, out); + LLVMPositionBuilderAtEnd(bd, out); +} +#ifdef ZYFFI +/* a native call as ir. the callee and its signature are known, so llvm sees a + call with the real argument types and lays out registers and stack by the + platform's rules itself (no limit on the number of arguments). pointer + kinds are converted from vm addresses to host addresses inline. the + common libm functions on doubles become llvm's own intrinsics, which the + backend can turn into one instruction (sqrt) or fold. ai and af are each + argument as an i64 and as a double. s0 and rcur are the stacks as of the + start of the block, for callbacks. */ +static LLVMValueRef lptr_in(LLVMValueRef v, int kind) +{ + LLVMValueRef mb = Kw((Cell)(uintptr_t)m), host, inr, nz = LLVMBuildICmp(bd, LLVMIntNE, v, Kw(0), ""); + if (kind == 3) { + inr = LLVMBuildICmp(bd, LLVMIntULT, v, Kw((Cell)MW * 8), ""); + host = LLVMBuildSelect(bd, inr, LLVMBuildAdd(bd, mb, v, ""), v, ""); + } else if (kind == 6) { + inr = LLVMBuildICmp(bd, LLVMIntULT, v, Kw(MW), ""); + host = LLVMBuildSelect(bd, inr, LLVMBuildAdd(bd, mb, LLVMBuildShl(bd, v, Kw(3), ""), ""), v, ""); + } else { + LLVMValueRef v1 = LLVMBuildAdd(bd, v, Kw(1), ""); + inr = LLVMBuildICmp(bd, LLVMIntULT, v1, Kw(MW), ""); + host = LLVMBuildSelect(bd, inr, LLVMBuildAdd(bd, mb, LLVMBuildShl(bd, v1, Kw(3), ""), ""), v, ""); + } + return LLVMBuildSelect(bd, nz, host, Kw(0), ""); +} +static LLVMValueRef lptr_out(LLVMValueRef p, int kind) +{ + LLVMValueRef mb = Kw((Cell)(uintptr_t)m), off = LLVMBuildSub(bd, p, mb, ""); + LLVMValueRef inr = LLVMBuildICmp(bd, LLVMIntULT, off, Kw((Cell)MW * 8), ""); + if (kind == 6) off = LLVMBuildLShr(bd, off, Kw(3), ""); + return LLVMBuildSelect(bd, inr, off, p, ""); +} +static void lext_attr(LLVMValueRef call, unsigned idx, int sign) +{ + const char *nm = sign ? "signext" : "zeroext"; + unsigned kid = LLVMGetEnumAttributeKindForName(nm, strlen(nm)); + LLVMAddCallSiteAttribute(call, idx, LLVMCreateEnumAttribute(ctx, kid, 0)); +} +static LLVMValueRef lncall(int di, LLVMValueRef *ai, LLVMValueRef *af, LLVMValueRef s0, LLVMValueRef rcur) +{ + Ncd *d = &ncd[di]; + LLVMTypeRef pt[15], rt, ft, f32t = LLVMFloatTypeInContext(ctx), i16t = LLVMInt16TypeInContext(ctx); + LLVMValueRef av[15], call, res; + int i, k, rk = (int)(d->sig & 15), allf = rk == 2, na = d->n; + LLVMBuildStore(bd, s0, cptr(&cb_s)); + LLVMBuildStore(bd, rcur, cptr(&cb_r)); + for (i = 0; i < na; i++) { + k = (int)((d->sig >> (4 * (i + 1))) & 15); + if (k != 2) allf = 0; + switch (k) { + case 2: pt[i] = f64t; av[i] = af[i]; break; + case 5: pt[i] = f32t; av[i] = LLVMBuildFPTrunc(bd, af[i], f32t, ""); break; + case 4: case 7: pt[i] = i32t; av[i] = LLVMBuildTrunc(bd, ai[i], i32t, ""); break; + case 8: case 9: pt[i] = i16t; av[i] = LLVMBuildTrunc(bd, ai[i], i16t, ""); break; + case 10: case 11: pt[i] = i8t; av[i] = LLVMBuildTrunc(bd, ai[i], i8t, ""); break; + case 3: case 6: case 12: pt[i] = i64t; av[i] = lptr_in(ai[i], k); break; + default: pt[i] = i64t; av[i] = ai[i]; break; + } + } + rt = rk == 0 ? LLVMVoidTypeInContext(ctx) : rk == 2 ? f64t : rk == 5 ? f32t : (rk == 4 || rk == 7) ? i32t : (rk == 8 || rk == 9) ? i16t : (rk == 10 || rk == 11) ? i8t : i64t; + if (allf && na >= 1 && na <= 3) { /* libm on doubles: the intrinsic */ + static const struct { const char *c, *ll; int n; } lm[] = { + { "sqrt", "llvm.sqrt.f64", 1 }, { "fabs", "llvm.fabs.f64", 1 }, { "floor", "llvm.floor.f64", 1 }, + { "ceil", "llvm.ceil.f64", 1 }, { "trunc", "llvm.trunc.f64", 1 }, { "round", "llvm.round.f64", 1 }, + { "sin", "llvm.sin.f64", 1 }, { "cos", "llvm.cos.f64", 1 }, { "exp", "llvm.exp.f64", 1 }, + { "exp2", "llvm.exp2.f64", 1 }, { "log", "llvm.log.f64", 1 }, { "log2", "llvm.log2.f64", 1 }, + { "log10", "llvm.log10.f64", 1 }, { "pow", "llvm.pow.f64", 2 }, { "copysign", "llvm.copysign.f64", 2 }, + { "fma", "llvm.fma.f64", 3 } }; + for (i = 0; i < (int)(sizeof lm / sizeof lm[0]); i++) + if (lm[i].n == na && !strcmp(lm[i].c, d->name)) { + LLVMTypeRef it = LLVMFunctionType(f64t, pt, (unsigned)na, 0); + LLVMValueRef fn = LLVMGetNamedFunction(mod_, lm[i].ll); + if (!fn) fn = LLVMAddFunction(mod_, lm[i].ll, it); + return LLVMBuildBitCast(bd, LLVMBuildCall2(bd, it, fn, av, (unsigned)na, ""), i64t, ""); + } + } +#if defined(__x86_64__) + ft = LLVMFunctionType(rt, pt, (unsigned)na, 1); /* a vararg type makes llvm set al, which a variadic callee reads */ +#else + { + int fixed = ffi_varargs(d->f); + if (fixed >= 0 && fixed <= na) { + for (i = fixed; i < na; i++) { + if (pt[i] == f32t) { av[i] = LLVMBuildFPExt(bd, av[i], f64t, ""); pt[i] = f64t; } + else if (pt[i] == i8t || pt[i] == i16t) { + int kind = (int)((d->sig >> (4 * (i + 1))) & 15); + av[i] = kind == 9 || kind == 11 ? LLVMBuildZExt(bd, av[i], i32t, "") : LLVMBuildSExt(bd, av[i], i32t, ""); pt[i] = i32t; + } + } + ft = LLVMFunctionType(rt, pt, (unsigned)fixed, 1); + } else ft = LLVMFunctionType(rt, pt, (unsigned)na, 0); + } +#endif + call = LLVMBuildCall2(bd, ft, cptr(d->f), av, (unsigned)na, ""); + for (i = 0; i < na; i++) { + k = (int)((d->sig >> (4 * (i + 1))) & 15); + if (k == 4 || k == 8 || k == 10) lext_attr(call, (unsigned)i + 1, 1); + if (k == 7 || k == 9 || k == 11) lext_attr(call, (unsigned)i + 1, 0); + } + switch (rk) { + case 0: res = Kw(0); break; + case 2: res = LLVMBuildBitCast(bd, call, i64t, ""); break; + case 5: res = LLVMBuildBitCast(bd, LLVMBuildFPExt(bd, call, f64t, ""), i64t, ""); break; + case 4: res = LLVMBuildSExt(bd, call, i64t, ""); break; + case 8: case 10: res = LLVMBuildSExt(bd, call, i64t, ""); break; + case 7: case 9: case 11: res = LLVMBuildZExt(bd, call, i64t, ""); break; + case 3: case 6: res = lptr_out(call, rk); break; + default: res = call; break; + } + return res; +} +#endif +static LLVMValueRef l_dfn; /* the register-passing function being built or declared for this region */ +static int has_ret(Rg *R) +{ + int b; + for (b = 0; b < R->nb; b++) if (R->b[b].term == T_RET) return 1; + return 0; +} +/* an o3 call to a register-passing callee is inlined: the callee's body is built again into + the caller's module as an internal function, so llvm can inline it there. the copy is good + for one generation of the callee (rclear bumps gen whenever the callee's code is dropped), and + the caller checks that generation before the fast path, so a stale copy is never entered. */ +#define INL_MAX 8 /* copies in one module */ +#define INL_BLOCKS 24 /* largest callee copied */ +typedef struct { Rg *C; unsigned gen; LLVMValueRef fn; } InlEnt; +static InlEnt inl_tab[INL_MAX]; +static int ninl, inl_depth; +static void lbody(Rg *R, int direct, LLVMValueRef fnv); +static LLVMValueRef inline_fn(Rg *C, unsigned *gen) +{ + static int off = -1, inl_n; + LLVMValueRef sfn, sdfn, svm, svregs, svs, svr, nf, srv[RMAXV]; + LLVMBasicBlockRef at; + LLVMTypeRef dpt[6]; + char nm[24]; + int i, j, sdm; + if (off < 0) off = getenv("ZYNOINLINE") != 0; + if (off || inl_depth || !C->lifted || !C->dnat || C->nb > INL_BLOCKS || has_ret(C)) return 0; + for (i = 0; i < ninl; i++) if (inl_tab[i].C == C && inl_tab[i].gen == C->gen) { *gen = C->gen; return inl_tab[i].fn; } + if (ninl >= INL_MAX) return 0; + at = LLVMGetInsertBlock(bd); sfn = fn_; sdfn = l_dfn; sdm = l_dm; + svm = vm_; svregs = vregs; svs = vs; svr = vr; + memcpy(srv, rvv, sizeof rvv); + for (j = 0; j < 6; j++) dpt[j] = i64t; + sprintf(nm, "inl_%d", inl_n++); + nf = LLVMAddFunction(mod_, nm, LLVMFunctionType(l_dret, dpt, 6, 0)); + LLVMSetLinkage(nf, LLVMInternalLinkage); + l_dfn = nf; + inl_depth++; + lbody(C, 1, nf); + inl_depth--; + fn_ = sfn; l_dfn = sdfn; l_dm = sdm; + vm_ = svm; vregs = svregs; vs = svs; vr = svr; + memcpy(rvv, srv, sizeof rvv); + LLVMPositionBuilderAtEnd(bd, at); + inl_tab[ninl].C = C; inl_tab[ninl].gen = C->gen; inl_tab[ninl].fn = nf; ninl++; + *gen = C->gen; + return nf; +} +/* one function for the region. the plain one is f(mem, regs, entry) and takes + the vm's own protocol: s and r in regs, variables from the frame. the + register-passing one is fd(s, r, fp, a0, a1, a2), entered only at the top of + a function by another region: the arguments are the variables, the frame + pointer is a value, and it returns the next address and s. */ +/* the callee of a call to a known address, if it is a function region that takes its + arguments in registers */ +static Rg *dcallee(LLVMValueRef tgt) +{ + Rg *C; + if (!LLVMIsAConstantInt(tgt)) return 0; + C = fnlookup((int)LLVMConstIntGetZExtValue(tgt)); + if (!C || C->dead || C->fn < 0 || C->argc > 3 || C->fslots < 0) return 0; + return C; +} +static void lbody(Rg *R, int direct, LLVMValueRef fnv) +{ + LLVMTypeRef pt[3], ft; LLVMValueRef sw = 0, *val, s0, fp, r, ent = 0, rg2; + LLVMBasicBlockRef *bb, *ebs, eb, dflt = 0; + int b, i, j, e; + LLVMValueRef npi[16], npf[16], dev[4]; + int deo[4], nde = 0; + Rg *Cp = 0; + (void)pt; (void)ft; + fn_ = fnv; + l_dm = direct; + LLVMPositionBuilderAtEnd(bd, newbb()); + vs = LLVMBuildAlloca(bd, i64t, "s"); vr = LLVMBuildAlloca(bd, i64t, "r"); + rg2 = LLVMBuildAlloca(bd, LLVMArrayType(i64t, 2), "regs"); + for (j = 0; j < R->nv; j++) rvv[j] = LLVMBuildAlloca(bd, j && R->fv[j] ? f64t : i64t, ""); + bb = malloc(R->nb * sizeof *bb); + ebs = calloc(R->ne + 1, sizeof *ebs); + for (b = 0; b < R->nb; b++) bb[b] = newbb(); + if (direct) { + vm_ = cptr(m); vregs = 0; + LLVMBuildStore(bd, LLVMGetParam(fn_, 0), vs); + LLVMBuildStore(bd, LLVMGetParam(fn_, 1), vr); + LLVMBuildStore(bd, LLVMGetParam(fn_, 2), rvv[0]); + for (j = 1; j < R->nv; j++) { + LLVMValueRef bits = Kw(0); + if (R->slot[j] >= 0 && R->slot[j] < R->argc && R->slot[j] < 3) bits = LLVMGetParam(fn_, 3 + R->slot[j]); + if (R->fv[j]) bits = LLVMBuildBitCast(bd, bits, f64t, ""); + LLVMBuildStore(bd, bits, rvv[j]); + } + LLVMBuildBr(bd, bb[R->entb[0]]); + } else { + vm_ = LLVMGetParam(fn_, 0); vregs = LLVMGetParam(fn_, 1); ent = LLVMGetParam(fn_, 2); + { LLVMValueRef r0 = Kw(0), r1 = Kw(1); + set_stack_ir(LLVMBuildLoad2(bd, i64t, LLVMBuildGEP2(bd, i64t, vregs, &r0, 1, ""), "")); + LLVMBuildStore(bd, LLVMBuildLoad2(bd, i64t, LLVMBuildGEP2(bd, i64t, vregs, &r1, 1, ""), ""), vr); } + dflt = newbb(); + sw = LLVMBuildSwitch(bd, ent, dflt, (unsigned)R->ne); + } + for (e = direct ? 1 : 0; e < R->ne; e++) { + eb = ebs[e] = newbb(); + if (!direct) LLVMAddCase(sw, K32(e), eb); + LLVMPositionBuilderAtEnd(bd, eb); + fp = ld(Kw(ZFP)); + LLVMBuildStore(bd, fp, rvv[0]); + for (j = 1; j < R->nv; j++) { + LLVMValueRef bits = safeld(add(fp, R->slot[j])); + if (R->fv[j]) bits = LLVMBuildBitCast(bd, bits, f64t, ""); + LLVMBuildStore(bd, bits, rvv[j]); + } + LLVMBuildBr(bd, bb[R->entb[e]]); + } + if (!direct) { LLVMPositionBuilderAtEnd(bd, dflt); leave(Kw(-1)); } + val = calloc(R->maxnv + 1, sizeof *val); + for (b = 0; b < R->nb; b++) { + RB *B = &R->b[b]; + unsigned char *fpv = calloc(B->nv + 1, 1); + class_fp(R, B, fpv); + LLVMPositionBuilderAtEnd(bd, bb[b]); + s0 = stack_ir(); + for (i = 0; i < B->n; i++) { + Uop *u = &B->u[i]; + int na = nargs(u->op); + LLVMValueRef a = na > 0 ? val[u->a] : 0, x = na > 1 ? val[u->b] : 0, c = na > 2 ? val[u->c] : 0; + LLVMValueRef ai = na > 0 ? as_i64(a, fpv[u->a]) : 0, af = na > 0 ? as_f64(a, fpv[u->a]) : 0; + LLVMValueRef xi = na > 1 ? as_i64(x, fpv[u->b]) : 0, xf = na > 1 ? as_f64(x, fpv[u->b]) : 0; + LLVMValueRef ci = na > 2 ? as_i64(c, fpv[u->c]) : 0; + switch (u->op) { + case U_LI: val[u->d] = Kw(u->imm); if (fpv[u->d]) val[u->d] = LLVMBuildBitCast(bd, val[u->d], f64t, ""); break; + case U_VGET: + val[u->d] = LLVMBuildLoad2(bd, (unsigned)u->imm < RMAXV && R->fv[u->imm] ? f64t : i64t, rvv[u->imm], ""); + break; + case U_SLD: val[u->d] = ld(add(s0, (int)u->imm)); if (fpv[u->d]) val[u->d] = LLVMBuildBitCast(bd, val[u->d], f64t, ""); break; + case U_LD: val[u->d] = safeld(ai); if (fpv[u->d]) val[u->d] = LLVMBuildBitCast(bd, val[u->d], f64t, ""); break; + case U_ST: rstore_ir(ai, xi); break; + case U_NARG: npi[u->imm] = ai; npf[u->imm] = af; break; + case U_NCALL: +#ifdef ZYFFI + val[u->d] = lncall((int)u->imm, npi, npf, s0, LLVMBuildLoad2(bd, i64t, vr, "")); + if (fpv[u->d]) val[u->d] = LLVMBuildBitCast(bd, val[u->d], f64t, ""); +#endif + break; + case U_LD8: case U_LD16: case U_LD32: val[u->d] = bload_ir(ai, bwidth(u->op)); if (fpv[u->d]) val[u->d] = LLVMBuildBitCast(bd, val[u->d], f64t, ""); break; + case U_ST8: case U_ST16: case U_ST32: bstore_ir(ai, xi, bwidth(u->op)); break; + case U_FADD: val[u->d] = LLVMBuildFAdd(bd, af, xf, ""); break; + case U_FSUB: val[u->d] = LLVMBuildFSub(bd, af, xf, ""); break; + case U_FMUL: val[u->d] = LLVMBuildFMul(bd, af, xf, ""); break; + case U_FDIV: val[u->d] = LLVMBuildFDiv(bd, af, xf, ""); break; + case U_FEQ: val[u->d] = LLVMBuildZExt(bd, LLVMBuildFCmp(bd, LLVMRealOEQ, af, xf, ""), i64t, ""); break; + case U_FLT: val[u->d] = LLVMBuildZExt(bd, LLVMBuildFCmp(bd, LLVMRealOLT, af, xf, ""), i64t, ""); break; + case U_FLE: val[u->d] = LLVMBuildZExt(bd, LLVMBuildFCmp(bd, LLVMRealOLE, af, xf, ""), i64t, ""); break; + case U_I2F: val[u->d] = LLVMBuildSIToFP(bd, ai, f64t, ""); break; + case U_F2I: val[u->d] = LLVMBuildFPToSI(bd, af, i64t, ""); break; + default: val[u->d] = ir_op(u->op, ai, xi, ci); if (fpv[u->d]) val[u->d] = LLVMBuildBitCast(bd, val[u->d], f64t, ""); break; + } + } + Cp = B->term == T_CALL && B->cv >= 0 ? dcallee(as_i64(val[B->cv], fpv[B->cv])) : 0; + nde = 0; + for (j = 0; j < B->nso; j++) { + int off = B->so[2 * j]; + LLVMValueRef sv_ = as_i64(val[B->so[2 * j + 1]], fpv[B->so[2 * j + 1]]); + if (Cp && off >= B->delta && off < B->delta + Cp->argc) { deo[nde] = off; dev[nde++] = sv_; continue; } /* the register call does not need them in memory */ + st(add(s0, off), sv_); + } + set_stack_ir(add(s0, B->delta)); + for (j = 1; j < R->nv; j++) if (B->vs[j] >= 0) { + LLVMValueRef bits = R->fv[j] ? as_f64(val[B->vs[j]], fpv[B->vs[j]]) : as_i64(val[B->vs[j]], fpv[B->vs[j]]); + LLVMBuildStore(bd, bits, rvv[j]); + } + switch (B->term) { + case T_BR: LLVMBuildBr(bd, bb[B->s1]); break; + case T_CBR: + LLVMBuildCondBr(bd, LLVMBuildICmp(bd, LLVMIntEQ, as_i64(val[B->cv], fpv[B->cv]), Kw(0), ""), + B->s1 >= 0 ? bb[B->s1] : rexit_bb(R, B->x1), B->s2 >= 0 ? bb[B->s2] : rexit_bb(R, B->x2)); + break; + case T_CALL: { + LLVMTypeRef at[4]; LLVMValueRef args[4]; LLVMBasicBlockRef bad, okb, slowb, fastb, helpb, finb, eqb, doneb, chk; int ee; + LLVMValueRef tgt = as_i64(val[B->cv], fpv[B->cv]), natp = 0, fpn = 0, sv = 0, ret = 0; + Rg *C = 0; + rspill_vars(R, 1); + for (ee = 0; ee < R->ne; ee++) if (R->ent[ee] == B->x2) break; + if (LLVMIsAConstantInt(tgt)) { + C = fnlookup((int)LLVMConstIntGetZExtValue(tgt)); + if (C && (C->dead || C->argc > 12 || C->fslots < 0)) C = 0; + } + slowb = newbb(); okb = newbb(); chk = newbb(); + if (C && C->fn >= 0 && C->argc <= 3) { + /* a callee that is a function region: its arguments go in registers, its frame is a value, + and it comes back with the next address and s. the callee may not be compiled yet, or the + frame or the stack may be about to run out: the helper takes those */ + LLVMBasicBlockRef dfast = newbb(), dnorm = newbb(), dabn = newbb(), deq = newbb(), dcont = newbb(); + LLVMValueRef dn, cond, fpc, a3[3], da[6], res, rret, rs2, okf, tmp0, tmp1; + LLVMTypeRef dt = LLVMFunctionType(l_dret, (LLVMTypeRef[]){ i64t, i64t, i64t, i64t, i64t, i64t }, 6, 0); + int ai2; + unsigned igen = 0; + LLVMValueRef inl = C == R ? 0 : inline_fn(C, &igen); + if (C == R) dn = l_dfn; + else if (inl) dn = inl; + else dn = LLVMBuildLoad2(bd, ptrt, cptr(&C->dnat), ""); + fpc = LLVMBuildLoad2(bd, i64t, rvv[0], ""); + fpn = LLVMBuildSub(bd, fpc, Kw(C->fslots), ""); + sv = stack_ir(); + cond = LLVMBuildICmp(bd, LLVMIntUGE, fpn, + LLVMBuildAdd(bd, ld(LLVMBuildLoad2(bd, i64t, cptr(&frame_floor_word), "")), ld(LLVMBuildLoad2(bd, i64t, cptr(&frame_guard_word), "")), ""), ""); + cond = LLVMBuildAnd(bd, cond, LLVMBuildICmp(bd, LLVMIntULT, fpn, ld(LLVMBuildLoad2(bd, i64t, cptr(&frame_top_word), "")), ""), ""); + cond = LLVMBuildAnd(bd, cond, LLVMBuildICmp(bd, LLVMIntUGE, add(sv, -16), Kw(DSLIM), ""), ""); + cond = LLVMBuildAnd(bd, cond, LLVMBuildICmp(bd, LLVMIntULT, sv, Kw(DSHI), ""), ""); + if (inl) cond = LLVMBuildAnd(bd, cond, LLVMBuildICmp(bd, LLVMIntEQ, LLVMBuildLoad2(bd, i32t, cptr(&C->gen), ""), K32((int)igen), ""), ""); + else if (C != R) cond = LLVMBuildAnd(bd, cond, LLVMBuildICmp(bd, LLVMIntNE, LLVMBuildPtrToInt(bd, dn, i64t, ""), Kw(0), ""), ""); + LLVMBuildCondBr(bd, cond, dfast, slowb); + LLVMPositionBuilderAtEnd(bd, dfast); + for (ai2 = 0; ai2 < 3; ai2++) { + int off = B->delta + C->argc - 1 - ai2, q; + a3[ai2] = Kw(0); + if (ai2 >= C->argc) continue; + a3[ai2] = 0; + for (q = 0; q < nde; q++) if (deo[q] == off) a3[ai2] = dev[q]; + if (!a3[ai2]) a3[ai2] = ld(add(sv, C->argc - 1 - ai2)); + } + da[0] = add(sv, C->argc); da[1] = LLVMBuildLoad2(bd, i64t, vr, ""); + da[2] = fpn; da[3] = a3[0]; da[4] = a3[1]; da[5] = a3[2]; + res = LLVMBuildCall2(bd, dt, dn, da, 6, ""); + rret = LLVMBuildExtractValue(bd, res, 0, ""); + rs2 = LLVMBuildExtractValue(bd, res, 1, ""); + okf = LLVMBuildAnd(bd, LLVMBuildICmp(bd, LLVMIntUGE, rret, Kw(C->hi), ""), LLVMBuildICmp(bd, LLVMIntULT, rret, Kw(MW), ""), ""); + LLVMBuildCondBr(bd, okf, dnorm, dabn); + LLVMPositionBuilderAtEnd(bd, dnorm); + set_stack_ir(rs2); + LLVMBuildCondBr(bd, LLVMBuildICmp(bd, LLVMIntEQ, rret, Kw(C->hi), ""), deq, dcont); + LLVMPositionBuilderAtEnd(bd, deq); + { LLVMValueRef sn = add(stack_ir(), -1); st(sn, Kw(0)); set_stack_ir(sn); } + LLVMBuildBr(bd, dcont); + LLVMPositionBuilderAtEnd(bd, dcont); + if (ee < R->ne) LLVMBuildBr(bd, bb[R->entb[ee]]); else leave(Kw(B->x2)); + LLVMPositionBuilderAtEnd(bd, dabn); + tmp0 = LLVMBuildGEP2(bd, i64t, rg2, (LLVMValueRef[]){ Kw(0) }, 1, ""); + tmp1 = LLVMBuildGEP2(bd, i64t, rg2, (LLVMValueRef[]){ Kw(1) }, 1, ""); + LLVMBuildStore(bd, rs2, tmp0); + LLVMBuildStore(bd, LLVMBuildLoad2(bd, i64t, cptr(&d_r), ""), tmp1); + at[0] = ptrt; at[1] = ptrt; at[2] = ptrt; at[3] = i64t; + args[0] = tmp0; args[1] = tmp1; args[2] = cptr(C); args[3] = rret; + callp((void *)o3_finish_c, LLVMVoidTypeInContext(ctx), at, 4, args); + set_stack_ir(LLVMBuildLoad2(bd, i64t, tmp0, "")); + LLVMBuildStore(bd, LLVMBuildLoad2(bd, i64t, tmp1, ""), vr); + LLVMBuildBr(bd, chk); + (void)fastb; (void)helpb; (void)finb; (void)eqb; (void)doneb; (void)natp; (void)ret; + } else if (C) { + /* a callee that is already compiled is entered from here: a frame below this one, + the arguments copied across, a call through its entry. the other cases (not + compiled yet, a frame outside memory) take the helper */ + LLVMValueRef cond, fz; + fastb = newbb(); helpb = newbb(); finb = newbb(); eqb = newbb(); doneb = newbb(); + natp = LLVMBuildLoad2(bd, ptrt, cptr(&C->nat), ""); + fz = LLVMBuildLoad2(bd, i64t, rvv[0], ""); + fpn = LLVMBuildSub(bd, fz, Kw(C->fslots), ""); + sv = stack_ir(); + cond = LLVMBuildICmp(bd, LLVMIntNE, LLVMBuildPtrToInt(bd, natp, i64t, ""), Kw(0), ""); + cond = LLVMBuildAnd(bd, cond, LLVMBuildICmp(bd, LLVMIntUGE, fpn, + LLVMBuildAdd(bd, ld(LLVMBuildLoad2(bd, i64t, cptr(&frame_floor_word), "")), ld(LLVMBuildLoad2(bd, i64t, cptr(&frame_guard_word), "")), ""), ""), ""); + cond = LLVMBuildAnd(bd, cond, LLVMBuildICmp(bd, LLVMIntULT, fpn, Kw(MW - 64), ""), ""); + cond = LLVMBuildAnd(bd, cond, LLVMBuildICmp(bd, LLVMIntULT, add(sv, -1), Kw(MW - 64), ""), ""); + LLVMBuildCondBr(bd, cond, fastb, slowb); + LLVMPositionBuilderAtEnd(bd, fastb); + { + LLVMTypeRef ct[3]; LLVMValueRef ca[3], r1, haltv, okf, sn, rv; + int i2; + st(Kw(ZFP), fpn); + for (i2 = 0; i2 < C->argc; i2++) st(add(fpn, i2), ld(add(sv, C->argc - 1 - i2))); + r1 = Kw(1); + LLVMBuildStore(bd, add(sv, C->argc), rg2); + LLVMBuildStore(bd, LLVMBuildLoad2(bd, i64t, vr, ""), LLVMBuildGEP2(bd, i64t, rg2, &r1, 1, "")); + ct[0] = ptrt; ct[1] = ptrt; ct[2] = i32t; + ca[0] = vm_; ca[1] = rg2; ca[2] = K32(0); + ret = LLVMBuildCall2(bd, LLVMFunctionType(i64t, ct, 3, 0), natp, ca, 3, ""); + set_stack_ir(LLVMBuildLoad2(bd, i64t, rg2, "")); + LLVMBuildStore(bd, LLVMBuildLoad2(bd, i64t, LLVMBuildGEP2(bd, i64t, rg2, &r1, 1, ""), ""), vr); + haltv = LLVMBuildLoad2(bd, i64t, cptr(&halt_at), ""); + okf = LLVMBuildAnd(bd, LLVMBuildICmp(bd, LLVMIntUGE, ret, Kw(C->hi), ""), LLVMBuildICmp(bd, LLVMIntULT, ret, Kw(MW), ""), ""); + okf = LLVMBuildAnd(bd, okf, LLVMBuildICmp(bd, LLVMIntSLT, haltv, Kw(0), ""), ""); + LLVMBuildCondBr(bd, okf, finb, helpb); + LLVMPositionBuilderAtEnd(bd, finb); + LLVMBuildCondBr(bd, LLVMBuildICmp(bd, LLVMIntEQ, ret, Kw(C->hi), ""), eqb, doneb); + LLVMPositionBuilderAtEnd(bd, eqb); + sn = add(stack_ir(), -1); st(sn, Kw(0)); set_stack_ir(sn); + LLVMBuildBr(bd, doneb); + LLVMPositionBuilderAtEnd(bd, doneb); + rv = ld(Kw(ZFP)); + st(Kw(ZFP), add(rv, C->fslots)); + if (ee < R->ne) LLVMBuildBr(bd, bb[R->entb[ee]]); else leave(Kw(B->x2)); + LLVMPositionBuilderAtEnd(bd, helpb); + { + LLVMValueRef t0 = LLVMBuildGEP2(bd, i64t, rg2, (LLVMValueRef[]){ Kw(0) }, 1, ""), t1 = LLVMBuildGEP2(bd, i64t, rg2, (LLVMValueRef[]){ Kw(1) }, 1, ""); + LLVMBuildStore(bd, stack_ir(), t0); LLVMBuildStore(bd, LLVMBuildLoad2(bd, i64t, vr, ""), t1); + at[0] = ptrt; at[1] = ptrt; at[2] = ptrt; at[3] = i64t; + args[0] = t0; args[1] = t1; args[2] = cptr(C); args[3] = ret; + callp((void *)o3_finish_c, LLVMVoidTypeInContext(ctx), at, 4, args); + set_stack_ir(LLVMBuildLoad2(bd, i64t, t0, "")); LLVMBuildStore(bd, LLVMBuildLoad2(bd, i64t, t1, ""), vr); + } + LLVMBuildBr(bd, chk); + } + } else LLVMBuildBr(bd, slowb); + LLVMPositionBuilderAtEnd(bd, slowb); + { int q; for (q = 0; q < nde; q++) st(add(s0, deo[q]), dev[q]); } + st(Kw(ZFP), LLVMBuildLoad2(bd, i64t, rvv[0], "")); + { + LLVMValueRef t0 = LLVMBuildGEP2(bd, i64t, rg2, (LLVMValueRef[]){ Kw(0) }, 1, ""), t1 = LLVMBuildGEP2(bd, i64t, rg2, (LLVMValueRef[]){ Kw(1) }, 1, ""); + LLVMBuildStore(bd, stack_ir(), t0); LLVMBuildStore(bd, LLVMBuildLoad2(bd, i64t, vr, ""), t1); + at[0] = ptrt; at[1] = ptrt; at[2] = i64t; at[3] = i64t; + args[0] = t0; args[1] = t1; + args[2] = tgt; args[3] = Kw(B->x2); + callp((void *)o3_invoke, LLVMVoidTypeInContext(ctx), at, 4, args); + set_stack_ir(LLVMBuildLoad2(bd, i64t, t0, "")); LLVMBuildStore(bd, LLVMBuildLoad2(bd, i64t, t1, ""), vr); + } + LLVMBuildBr(bd, chk); + LLVMPositionBuilderAtEnd(bd, chk); + bad = newbb(); + LLVMBuildCondBr(bd, LLVMBuildICmp(bd, LLVMIntSGE, LLVMBuildLoad2(bd, i64t, cptr(&halt_at), ""), Kw(0), ""), bad, okb); + LLVMPositionBuilderAtEnd(bd, bad); leave(l_dm ? Kw(-1) : LLVMBuildLoad2(bd, i64t, cptr(&halt_at), "")); /* a direct callee says -1: any address could look like a return */ + LLVMPositionBuilderAtEnd(bd, okb); + if (ee < R->ne) LLVMBuildBr(bd, bb[R->entb[ee]]); else leave(Kw(B->x2)); + (void)fpn; (void)sv; + break; } + case T_RET: + r = add(LLVMBuildLoad2(bd, i64t, vr, ""), -1); LLVMBuildStore(bd, r, vr); + rspill_ir(R); leave(ld(r)); + break; + default: { + LLVMValueRef tv = as_i64(val[B->cv], fpv[B->cv]); + if (R->fn >= 0 && LLVMIsAConstantInt(tv) && (long long)LLVMConstIntGetSExtValue(tv) >= R->hi) leave_ok(tv); + else { rspill_ir(R); leave(tv); } + } + } + free(fpv); + } + free(val); free(bb); free(ebs); + l_dm = 0; +} +static void *lregion(Rg *R) +{ + char name[40], dname[48]; LLVMTypeRef pt[3], ft, dpt[6]; LLVMValueRef f1, f2 = 0; + LLVMOrcExecutorAddress addr; LLVMErrorRef err; + int dir, i; + sprintf(name, "o3_%d_%d", R->lo, nregions++); + sprintf(dname, "%s_d", name); + mod_ = LLVMModuleCreateWithNameInContext(name, ctx); + ninl = 0; + lmodule_target(); + pt[0] = ptrt; pt[1] = ptrt; pt[2] = i32t; + ft = LLVMFunctionType(i64t, pt, 3, 0); + f1 = LLVMAddFunction(mod_, name, ft); + lfunc_attrs(f1); + dir = R->fn >= 0 && R->argc <= 3 && R->fslots >= 0 && !has_ret(R); + if (dir) { + static unsigned knu; + LLVMTypeRef rt2[2]; + rt2[0] = i64t; rt2[1] = i64t; + l_dret = LLVMStructTypeInContext(ctx, rt2, 2, 0); + for (i = 0; i < 6; i++) dpt[i] = i64t; + f2 = LLVMAddFunction(mod_, dname, LLVMFunctionType(l_dret, dpt, 6, 0)); + if (!knu) knu = LLVMGetEnumAttributeKindForName("nounwind", 8); + LLVMAddAttributeAtIndex(f2, (LLVMAttributeIndex)-1, LLVMCreateEnumAttribute(ctx, knu, 0)); + } + l_dfn = f2; + lbody(R, 0, f1); + if (dir) lbody(R, 1, f2); + l_dfn = 0; + lrun_passes("default"); + if (getenv("ZYLLIR")) LLVMDumpModule(mod_); + { LLVMOrcResourceTrackerRef rt = LLVMOrcJITDylibCreateResourceTracker(LLVMOrcLLJITGetMainJITDylib(jit)); + if ((err = LLVMOrcLLJITAddLLVMIRModuleWithRT(jit, rt, LLVMOrcCreateNewThreadSafeModule(mod_, tsc)))) { + char *msg = LLVMGetErrorMessage(err); fprintf(stderr, "llvm: %s\n", msg); LLVMDisposeErrorMessage(msg); LLVMOrcReleaseResourceTracker(rt); return 0; + } + R->rt = rt; } + if ((err = LLVMOrcLLJITLookup(jit, &addr, name))) { char *msg = LLVMGetErrorMessage(err); fprintf(stderr, "llvm: %s\n", msg); LLVMDisposeErrorMessage(msg); return 0; } + if (dir) { + LLVMOrcExecutorAddress da; + if (!(err = LLVMOrcLLJITLookup(jit, &da, dname))) R->dnat = (void *)(size_t)da; else LLVMConsumeError(err); + } + if (trace) fprintf(stderr, "o3: %d..%d compiled by llvm%s\n", R->lo, R->hi - 1, R->dnat ? " (with a register entry)" : ""); + return (void *)(size_t)addr; +} +static void rcompile(Rg *R, int eager) +{ + (void)eager; + if (R->nat || R->nonat || !llvm_ok) return; + if (!(R->nat = lregion(R))) R->nonat = 1; +} +#endif +#define LD(x) (inmem(x) ? m[x] : 0) +static int stop_eq = -1, stop_ge = -1, stop_return_depth = -1, run_nest; +static Rg *stop_rg; +static Cell run_s, run_r; +static int run(int p) +{ + int s = run_nest ? (int)run_s : DS0, r = run_nest ? (int)run_r : RS0, xfer = 1; + Cell w, a, b; + unsigned long long steps = 0; +#ifdef ZYLLVM + Cell regs[2]; + if (!getenv("ZYNOLLVM")) llvm_init(); +#endif + while ((unsigned)p < (unsigned)code_words) { + if (run_nest) { + if (stop_eq >= 0 && p == stop_eq && r - (stack_control ? stack_get(SC_RETURN_LOW) : RS0) == stop_return_depth) break; + if (stop_ge >= 0 && p >= stop_ge) break; + if (stop_rg && p >= stop_rg->lo && p < stop_rg->hi && o3ent[p] == stop_rg->idx + 1) break; + } + if (xfer && (s < stack_data_limit() || r > stack_return_limit())) { + Cell sp = s, rp = r; + int ret = r > stack_return_limit(); + if (!stack_control || stack_expand(ret, stack_get(ret ? SC_RETURN_TOP : SC_DATA_TOP) - stack_get(ret ? SC_RETURN_LOW : SC_DATA_LOW) + 1, &sp, &rp) < 0) { p = ret ? PC_RETURN_OVERFLOW : PC_DATA_OVERFLOW; break; } + s = (int)sp; r = (int)rp; + } + if (xfer && o3ent[p]) { /* an o3 region */ + Rg *R = rat(p); + if (R) { +#ifdef ZYLLVM + rcompile(R, 0); + if (R->nat) { regs[0] = s; regs[1] = r; ccode++; p = (int)((RFn)R->nat)(m, regs, o3ei[p]); ccode--; rt_sweep(); s = (int)regs[0]; r = (int)regs[1]; } + else +#endif + p = rexec(R, o3ei[p], &s, &r); + continue; + } + } +#ifdef ZYLLVM + if (xfer && llvm_ok) { /* at a block start: run it compiled, or count it */ + xfer = 0; + if (!lt[p] && hot[p] < 65535 && ++hot[p] == HOT && !(lt[p] = lcompile(p))) hot[p] = 65535; + if (lt[p]) { + regs[0] = s; regs[1] = r; + ccode++; p = lt[p](m, regs); ccode--; + s = regs[0]; r = regs[1]; + rt_sweep(); + if (need_flush) { need_flush = 0; ldrop_all(); } + xfer = 1; + continue; + } + } +#endif + w = m[p++]; + steps++; + if (w & 1) { m[--s] = w >> 1; continue; } + a = m[s]; b = m[s + 1]; + switch ((int)(w >> 1)) { + case 1: m[s] = LD(a); break; + case 2: + if (a == SYSGATE) { + int moving = b == 9 && (m[a + 1] == 7 || m[a + 1] == 8); + cb_s = s + 2; cb_r = r; m[a] = hostcall(b, m[a + 1], m[a + 2], m[a + 3]); + if (moving) { s = (int)cb_s; r = (int)cb_r; } else s += 2; + break; + } + if (inmem(a)) { +#ifdef ZYLLVM + if (lcov[a]) lhook(a, b); else +#else + if (rcov[a]) { m[a] = b; rdrop(a); } else +#endif + m[a] = b; + } + s += 2; break; + case 3: m[s] = LD(s + 1 + a); break; + case 4: s++; break; + case 5: m[s] = b; m[s + 1] = a; break; + case 6: m[r++] = p; p = a; s++; xfer = 1; break; + case 7: p = a; s++; xfer = 1; break; + case 8: if (!b) { p = a; xfer = 1; } s += 2; break; + case 9: p = m[--r]; xfer = 1; break; + case 10: m[--s] = (signed char)Q(); break; + case 11: m[--s] = J; break; + case 12: G((char)a); s++; break; + case 13: m[++s] = b + a; break; + case 14: m[++s] = b - a; break; + case 15: m[++s] = b * a; break; + case 16: m[++s] = b & a; break; + case 17: m[++s] = b | a; break; + case 18: m[++s] = b ^ a; break; + case 19: m[++s] = b >> (a & 63); break; + case 20: m[++s] = b < a; break; + case 600: + if (p >= code_words) { p = PC_HALT; break; } + m[--s] = m[p++]; + break; + case 601: m[++s] = (Cell)fop_add((uint64_t)b, (uint64_t)a); break; + case 602: m[++s] = (Cell)fop_sub((uint64_t)b, (uint64_t)a); break; + case 603: m[++s] = (Cell)fop_mul((uint64_t)b, (uint64_t)a); break; + case 604: m[++s] = (Cell)fop_div((uint64_t)b, (uint64_t)a); break; + case 605: m[++s] = fop_eq((uint64_t)b, (uint64_t)a); break; + case 606: m[++s] = fop_lt((uint64_t)b, (uint64_t)a); break; + case 607: m[++s] = fop_le((uint64_t)b, (uint64_t)a); break; + case 608: m[s] = (Cell)fop_i2f(a); break; + case 609: m[s] = fop_f2i((uint64_t)a); break; + case 610: fop_print(a); s++; break; + case 611: m[s] = fop_parse_cell(a); break; + case 618: fop_print_mode(m[s + 2], b, a); s += 3; break; + case 612: m[s] = bld8(a); break; + case 613: bst8(a, b); s += 2; break; + case 614: m[s] = bld16(a); break; + case 615: bst16(a, b); s += 2; break; + case 616: m[s] = bld32(a); break; + case 617: bst32(a, b); s += 2; break; + case 619: { Cell t = s; m[--s] = t; break; } + case 620: cb_r = r; s = (int)ncall_exec(s); break; + case 21: + m[s] = install(a); +#ifdef ZYLLVM + if (need_flush) { need_flush = 0; ldrop_all(); } +#else + if (need_flush) { need_flush = 0; rdrop_all(); memset(rcov, 0, (size_t)code_words); } +#endif + break; + default: + if ((int)(w >> 1) >= XOP0 && (int)(w >> 1) < XOP0 + NENH && enh[(int)(w >> 1) - XOP0].live) s = (int)(xexec(&enh[(int)(w >> 1) - XOP0], m + s) - m); + else p = PC_HALT; /* halt: unwired opcodes and the reserved negative ones */ + } + } + if (run_nest) { run_s = s; run_r = r; return p; } + halt_at = -1; + if (trace) fprintf(stderr, "interp: %llu steps\n", steps); + return stopped(p); +} +static int vm_until(int pc, int seq, int sge, Rg *re, Cell *ps, Cell *pr) +{ + int pe = stop_eq, pg = stop_ge, pd = stop_return_depth, pn = run_nest, back; + Rg *old = stop_rg; + Cell ss = run_s, rr = run_r; + stop_eq = seq; stop_ge = sge; stop_return_depth = (int)(*pr - (stack_control ? stack_get(SC_RETURN_LOW) : RS0) - 1); stop_rg = re; + run_nest = 1; run_s = *ps; run_r = *pr; + back = run(pc); + *ps = run_s; *pr = run_r; + stop_eq = pe; stop_ge = pg; stop_return_depth = pd; stop_rg = old; run_nest = pn; run_s = ss; run_r = rr; + return back; +} +static int o3_ready(Rg *C) +{ + if (!C || C->dead) return 0; + if (!C->lifted && rlift(C)) { C->dead = 1; return 0; } + return C->lifted && !C->dead; +} +static int o3_exec_region(Rg *C, int entry, Cell *ps, Cell *pr) +{ + int s, r, back; + if (!C || C->dead || entry < 0 || entry >= C->ne) return -1; + if (!C->lifted && rlift(C)) { C->dead = 1; return -1; } +#ifdef ZYLLVM + rcompile(C, 0); + if (C->nat) { + typedef Cell (*RFn2)(Cell *, Cell *, int); + Cell regs[2]; + regs[0] = *ps; regs[1] = *pr; + back = (int)((RFn2)C->nat)(m, regs, entry); + *ps = regs[0]; *pr = regs[1]; + return back; + } +#endif + s = (int)*ps; r = (int)*pr; + back = rexec(C, entry, &s, &r); + *ps = s; *pr = r; + return back; +} +#endif + +/* a byte store writes whole words through the same hook as opcode 2, so a + translation covering either word is dropped. it does not run a syscall: + the gate is a word store at SYSGATE. */ +static void b_commit(Cell w, Cell v) +{ + if ((unsigned)w >= (unsigned)MW) return; + if (w == SYSGATE) { m[w] = v; return; } +#if defined(ZYJIT) + if (cov[(int)w]) hook(w, v); else m[w] = v; +#elif defined(ZYLLVM) + if (lcov[(int)w]) lhook(w, v); else m[w] = v; +#else + if (rcov[(int)w]) { m[w] = v; rdrop((int)w); } else m[w] = v; +#endif +} + +#ifdef ZYBIGSTACK +static ucontext_t uc_main, uc_vm; +static int vm_rc; +static void vm_entry(void) { vm_rc = run(GORG); } +#endif +static int memory_option(const char *name, int fallback, int minimum) +{ + const char *s = getenv(name); + char *end; + unsigned long long words; + if (!s) return fallback; + errno = 0; words = strtoull(s, &end, 0); + if (errno || !*s || *end || words < (unsigned)minimum || words > 0x7fffffff) { + fprintf(stderr, "zy: invalid %s (minimum %d words)\n", name, minimum); + return -1; + } + return (int)words; +} +/* reserve all address-indexed metadata together; a denied request leaves it intact. */ +static int code_reserve(Cell end) +{ + Cell cap = code_words ? code_words : PW; + void *slots[6], *previous[6], *fresh[6]; size_t widths[6], oldn[6], newn[6]; int count = 0, i, j; + if (end <= 0 || end > memory_words || end >= PC_HALT) return -1; + if (end <= code_words) return (int)code_words; + while (cap < end) { if (cap > memory_words / 2) { cap = memory_words; break; } cap *= 2; } + if (cap >= PC_HALT) cap = PC_HALT - 1; + if (cap < end) return -1; +#define CODE_COLUMN(name, before, after) do { slots[count] = &name; previous[count] = name; widths[count] = sizeof *name; oldn[count] = (size_t)(before); newn[count] = (size_t)(after); count++; } while (0) + CODE_COLUMN(pg_head, code_words ? NPG : 0, (cap >> PGSH) + 1); + CODE_COLUMN(o3ent, code_words, cap); CODE_COLUMN(o3ei, code_words, cap); +#ifdef ZYJIT + CODE_COLUMN(tab, code_words, cap); CODE_COLUMN(fn_nat, code_words, cap); +#elif defined(ZYLLVM) + CODE_COLUMN(lt, code_words, cap); CODE_COLUMN(hot, code_words, cap); +#endif +#undef CODE_COLUMN + for (i = 0; i < count; i++) { + fresh[i] = calloc(newn[i], widths[i]); + if (!fresh[i]) { for (j = 0; j < i; j++) free(fresh[j]); return -1; } + if (oldn[i]) memcpy(fresh[i], previous[i], oldn[i] * widths[i]); + } + for (i = 0; i < count; i++) { free(previous[i]); memcpy(slots[i], &fresh[i], sizeof fresh[i]); } + code_words = cap; +#ifdef ZYJIT + S[3] = (long)tab; +#endif + return (int)cap; +} +/* bulk writes use the ordinary store path when they would rewrite live code. */ +static int code_range_live(Cell start, Cell count) +{ + Cell end, i; + if (start < 0 || count <= 0 || start >= code_words) return 0; + end = count > code_words - start ? code_words : start + count; + for (i = start; i < end; i++) { +#ifdef ZYJIT + if (cov[i]) return 1; +#else + if (rcov[i]) return 1; +#ifdef ZYLLVM + if (lcov[i]) return 1; +#endif +#endif + } + return 0; +} + +int main(int argc, char **argv) +{ +#ifdef _WIN32 + _setmode(_fileno(stdin), _O_BINARY); + _setmode(_fileno(stdout), _O_BINARY); +#endif + zy_args(argc, argv); + memory_limit = memory_option("TINYVM_MAX_MEMORY_WORDS", DEFAULT_MEMORY_LIMIT, HEAP_HI > PW ? HEAP_HI : PW); + if (memory_limit < 0) return 1; + memory_growth_limit = memory_limit; + memory_words = memory_option("TINYVM_MEMORY_WORDS", HEAP_HI > PW ? HEAP_HI : PW, HEAP_HI > PW ? HEAP_HI : PW); + if (memory_words < 0) return 1; + if (memory_words > memory_limit) { fprintf(stderr, "zy: initial memory exceeds its limit\n"); return 1; } + m = calloc((size_t)MW, sizeof *m); +#ifdef ZYJIT + cov = calloc((size_t)MW, 1); + if (!cov) { fprintf(stderr, "zy: cannot reserve translation coverage\n"); return 1; } +#else + rcov = calloc((size_t)MW, 1); +#ifdef ZYLLVM + lcov = calloc((size_t)MW, 1); + if (!lcov) { fprintf(stderr, "zy: cannot reserve translation coverage\n"); return 1; } +#endif + if (!rcov) { fprintf(stderr, "zy: cannot reserve region coverage\n"); return 1; } +#endif + if (!m) { fprintf(stderr, "zy: cannot reserve vm memory\n"); return 1; } + if (code_reserve(PW) < 0) { fprintf(stderr, "zy: cannot reserve code metadata\n"); return 1; } + trace = getenv("ZYTRACE") != 0; + if ((unsigned)GORG + (unsigned)GLEN > (unsigned)PW || (unsigned)GORG + (unsigned)GLEN > (unsigned)FBOT) { + fprintf(stderr, "zy: genome does not fit\n"); + return 1; + } + memcpy(m + GORG, genome, sizeof genome); + m[ZFP + 6] = FBOT; m[ZFP + 7] = FTOP; m[ZFP + 8] = 1024; +#ifdef ZYJIT + { + int p = GORG; + union { unsigned *u; int (*f)(long *, void *); } enter; +#ifdef __APPLE__ + code = mmap(0, CODEW * 4, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANON | MAP_JIT, -1, 0); +#else + code = mmap(0, CODEW * 4, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); +#endif + if (code == MAP_FAILED) { perror("mmap"); return 1; } + jitwrite(1); + build_stubs(); + jitwrite(0); + syncicache(code, cp); + S[0] = (long)m; S[1] = DS0; S[2] = RS0; S[3] = (long)tab; S[4] = (long)cov; + S[5] = (long)Q; S[6] = (long)G; S[7] = (long)&J; S[8] = (long)hook; + S[9] = (long)inst_hook; S[10] = (long)xrun; + S[11] = (long)(m + DSLIM); S[12] = (long)(m + RSLIM); + cov[SYSGATE] = 1; + enter.u = x_enter; + while ((unsigned)p < (unsigned)code_words || p == PC_DATA_OVERFLOW || p == PC_RETURN_OVERFLOW) { + if (p == PC_DATA_OVERFLOW || p == PC_RETURN_OVERFLOW) { + Cell sp = S[1], rp = S[2]; int ret = p == PC_RETURN_OVERFLOW; + if (!stack_control || stack_expand(ret, stack_get(ret ? SC_RETURN_TOP : SC_DATA_TOP) - stack_get(ret ? SC_RETURN_LOW : SC_DATA_LOW) + 1, &sp, &rp) < 0) break; + S[1] = sp; S[2] = rp; + S[11] = (long)(m + stack_data_limit()); S[12] = (long)(m + stack_return_limit()); + p = (int)S[13]; continue; + } + void *c = tab[p]; + if (!c) { jitwrite(1); c = translate(p); jitwrite(0); } + p = enter.f(S, c); + if (need_flush) { need_flush = 0; jitwrite(1); flush(); jitwrite(0); } + } + return stopped(p); + } +#elif defined(ZYBIGSTACK) + { + /* o3 calls nest on the host stack, one native frame per call: give the machine a + stack as deep as its own frame stack can go (untouched pages cost nothing) */ + size_t sz = (size_t)1 << 29; + void *stk = mmap(0, sz, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0); + if (stk != MAP_FAILED) { + getcontext(&uc_vm); + uc_vm.uc_stack.ss_sp = stk; uc_vm.uc_stack.ss_size = sz; uc_vm.uc_link = &uc_main; + makecontext(&uc_vm, vm_entry, 0); + swapcontext(&uc_main, &uc_vm); + return vm_rc; + } + return run(GORG); + } +#else + return run(GORG); +#endif + return 0; +} diff --git a/src/core/fops.h b/src/core/fops.h new file mode 100644 index 0000000..9213603 --- /dev/null +++ b/src/core/fops.h @@ -0,0 +1,1048 @@ +/* IEEE-754 binary64, packed in one 64-bit word. + a float on the stack is the raw 64 bits. the odd/even tag is only for the + instruction stream, so a float literal is two words: opcode 600 (the even + word 1200) and then the raw bits, which the decoder must consume itself + because bit 0 is often set. + + two arithmetic paths, chosen when the image is built: + - hardware (the default). fop_* are volatile host doubles, so gcc emits + the FPU (x86_64 SSE2, AArch64 FP, RV64 D). the arm64 jit and the llvm + backend do not call these for 601..609; they emit fadd/fmul/fcmp/scvtf + and the matching llvm double ops. bare metal turns the FPU on in the + boot stub and compiles this file with a hard-float ABI. + - soft (compile with -DZY_SOFTFLOAT). integer IEEE, round ties to even, + no host double. a port with no FPU selects this. print and parse stay + on the integer side either way, so the text does not depend on libc. + + print and parse are integer, on both paths, so the text does not depend + on libc or the host libm. opcode 610 prints six digits after the dot and + rounds a tie in the next digit away from zero. opcode 618 (the word 1236) + takes (bits prec mode --): mode 0 is the shortest spelling that parses + back to the same bits, mode 1 is fixed (prec digits after the dot) and + mode 2 is scientific (prec significant digits). prec is capped at 17. + the parser keeps the digits it is given, rounds ties to even, and accepts + an exponent, inf and nan. */ +#ifndef FOPS_H +#define FOPS_H +#include + +#ifdef ZY_SOFTFLOAT +/* ---- soft-float: binary64 in integer registers, selected with -DZY_SOFTFLOAT ---- */ +typedef struct { uint64_t sig; int exp; int sign; int cls; } FopSF; /* cls 0 finite, 1 inf, 2 nan */ + +static int fop_clz(uint64_t x) +{ + int n = 0; + if (!x) return 64; + if (!(x & 0xffffffff00000000ull)) { n += 32; x <<= 32; } + if (!(x & 0xffff000000000000ull)) { n += 16; x <<= 16; } + if (!(x & 0xff00000000000000ull)) { n += 8; x <<= 8; } + if (!(x & 0xf000000000000000ull)) { n += 4; x <<= 4; } + if (!(x & 0xc000000000000000ull)) { n += 2; x <<= 2; } + if (!(x & 0x8000000000000000ull)) n++; + return n; +} +static int fop_clz128(unsigned __int128 x) +{ + uint64_t hi = (uint64_t)(x >> 64); + if (hi) return fop_clz(hi); + return 64 + fop_clz((uint64_t)x); +} +static void fop_dec(uint64_t u, FopSF *o) +{ + int eb = (int)((u >> 52) & 0x7ff); + uint64_t frac = u & 0x000fffffffffffffull; + o->sign = (int)(u >> 63); + o->sig = frac; + o->exp = 0; + if (eb == 0x7ff) { o->cls = frac ? 2 : 1; return; } + o->cls = 0; + if (!eb) { o->exp = -1074; return; } + o->sig = frac | (1ull << 52); + o->exp = eb - 1023 - 52; +} +/* value = sig * 2^exp, ties to even. sticky is bits below sig. + a magnitude below 2^-1075 is a zero; that exact power is the tie between + zero and the smallest subnormal, and it rounds back to zero. */ +static uint64_t fop_sf_round(int sign, int exp, uint64_t sig, int sticky) +{ + int round = 0, bit, topw; + uint64_t sgn = (uint64_t)sign << 63; + if (!sig) return sgn; + topw = exp + 63 - fop_clz(sig); + if (topw < -1075) return sgn; + if (topw == -1075) return (sticky || (sig & (sig - 1))) ? sgn | 1 : sgn; + while (sig < (1ull << 52) && exp > -1074) { sig <<= 1; exp--; } + while (sig >= (1ull << 53) || (exp < -1074 && sig)) { + bit = (int)(sig & 1); + sig >>= 1; + exp++; + sticky = sticky || round; + round = bit; + } + if (round && (sticky || (sig & 1))) { + sig++; + if (sig >= (1ull << 53)) { sig >>= 1; exp++; } + } + if (!sig) return sgn; + if (exp > 971) return sgn | 0x7ff0000000000000ull; /* exp + 52 > 1023 */ + if (sig < (1ull << 52)) return sgn | sig; + return sgn | ((uint64_t)(exp + 52 + 1023) << 52) | (sig & 0x000fffffffffffffull); +} +static uint64_t fop_add(uint64_t x, uint64_t y) +{ + FopSF a, b; + unsigned __int128 as, bs, sum; + int sh, sticky = 0, sign, lz; + uint64_t top; + fop_dec(x, &a); + fop_dec(y, &b); + if (a.cls == 2 || b.cls == 2) return 0x7ff8000000000000ull; + if (a.cls == 1 && b.cls == 1) { + if (a.sign != b.sign) return 0x7ff8000000000000ull; + return ((uint64_t)a.sign << 63) | 0x7ff0000000000000ull; + } + if (a.cls == 1) return ((uint64_t)a.sign << 63) | 0x7ff0000000000000ull; + if (b.cls == 1) return ((uint64_t)b.sign << 63) | 0x7ff0000000000000ull; + if (!a.sig && !b.sig) return (a.sign && b.sign) ? 0x8000000000000000ull : 0; + if (!a.sig) return y; + if (!b.sig) return x; + as = (unsigned __int128)a.sig << 60; + bs = (unsigned __int128)b.sig << 60; + if (a.exp < b.exp) { FopSF t = a; a = b; b = t; as = bs; bs = (unsigned __int128)b.sig << 60; } + sh = a.exp - b.exp; + if (sh >= 128) { sticky = b.sig != 0; bs = 0; } + else if (sh) { + sticky = (bs & (((unsigned __int128)1 << sh) - 1)) != 0; + bs >>= sh; + } + if (a.sign == b.sign) { sum = as + bs; sign = a.sign; } + else if (as == bs) return 0; + else if (as > bs) { sum = as - bs; sign = a.sign; } + else { sum = bs - as; sign = b.sign; } + lz = fop_clz128(sum); + sum <<= lz; + top = (uint64_t)(sum >> 64); + if ((uint64_t)sum) sticky = 1; + return fop_sf_round(sign, a.exp + 4 - lz, top, sticky); +} +static uint64_t fop_sub(uint64_t x, uint64_t y) { return fop_add(x, y ^ 0x8000000000000000ull); } +static uint64_t fop_mul(uint64_t x, uint64_t y) +{ + FopSF a, b; + unsigned __int128 prod; + int sign, lz, sticky = 0; + uint64_t top; + fop_dec(x, &a); + fop_dec(y, &b); + sign = a.sign ^ b.sign; + if (a.cls == 2 || b.cls == 2) return 0x7ff8000000000000ull; + if ((a.cls == 1 && !b.sig) || (b.cls == 1 && !a.sig)) return 0x7ff8000000000000ull; + if (a.cls == 1 || b.cls == 1) return ((uint64_t)sign << 63) | 0x7ff0000000000000ull; + if (!a.sig || !b.sig) return (uint64_t)sign << 63; + prod = (unsigned __int128)a.sig * b.sig; + lz = fop_clz128(prod); + prod <<= lz; + top = (uint64_t)(prod >> 64); + if ((uint64_t)prod) sticky = 1; + return fop_sf_round(sign, a.exp + b.exp + 64 - lz, top, sticky); +} +static uint64_t fop_div(uint64_t x, uint64_t y) +{ + FopSF a, b; + unsigned __int128 num, q128; + int sign, lz, sticky; + fop_dec(x, &a); + fop_dec(y, &b); + sign = a.sign ^ b.sign; + if (a.cls == 2 || b.cls == 2) return 0x7ff8000000000000ull; + if (a.cls == 1 && b.cls == 1) return 0x7ff8000000000000ull; + if (a.cls == 1) return ((uint64_t)sign << 63) | 0x7ff0000000000000ull; + if (!b.sig || b.cls == 1) { + if (!b.sig && !a.sig) return 0x7ff8000000000000ull; + if (!b.sig) return ((uint64_t)sign << 63) | 0x7ff0000000000000ull; + return (uint64_t)sign << 63; /* finite / inf */ + } + if (!a.sig) return (uint64_t)sign << 63; + /* (sig << 64) / sig fits in 128 bits even for a subnormal divisor of 1. */ + num = (unsigned __int128)a.sig << 64; + q128 = num / b.sig; + sticky = (num % b.sig) != 0; + lz = fop_clz128(q128); + q128 <<= lz; + if ((uint64_t)q128) sticky = 1; + return fop_sf_round(sign, a.exp - b.exp - lz, (uint64_t)(q128 >> 64), sticky); +} +static int fop_eq(uint64_t x, uint64_t y) +{ + FopSF a, b; + fop_dec(x, &a); + fop_dec(y, &b); + if (a.cls == 2 || b.cls == 2) return 0; + if (!a.cls && !b.cls && !a.sig && !b.sig) return 1; + return x == y; +} +static int fop_lt(uint64_t x, uint64_t y) +{ + FopSF a, b; + int mag; + fop_dec(x, &a); + fop_dec(y, &b); + if (a.cls == 2 || b.cls == 2) return 0; + if (a.cls == 1 && b.cls == 1) return a.sign > b.sign; + if (a.cls == 1) return a.sign; + if (b.cls == 1) return !b.sign; + if (!a.sig && !b.sig) return 0; + if (a.sign != b.sign) return a.sign; + if (a.exp != b.exp) mag = a.exp < b.exp; + else mag = a.sig < b.sig; + if (a.exp == b.exp && a.sig == b.sig) return 0; + return a.sign ? !mag : mag; +} +static int fop_le(uint64_t x, uint64_t y) { return fop_eq(x, y) || fop_lt(x, y); } +static uint64_t fop_i2f(int64_t i) +{ + uint64_t mag; + if (!i) return 0; + if (i < 0) { + mag = (i == (int64_t)((uint64_t)1 << 63)) ? ((uint64_t)1 << 63) : (uint64_t)(-i); + return fop_sf_round(1, 0, mag, 0); + } + return fop_sf_round(0, 0, (uint64_t)i, 0); +} +static int64_t fop_f2i(uint64_t u) +{ + FopSF a; + uint64_t mag; + fop_dec(u, &a); + if (a.cls == 2 || !a.sig) return 0; + if (a.cls == 1 || a.exp >= 63) return a.sign ? (int64_t)((uint64_t)1 << 63) : 9223372036854775807LL; + if (a.exp >= 0) { + if (a.exp > 0 && a.sig > (~0ull >> a.exp)) return a.sign ? (int64_t)((uint64_t)1 << 63) : 9223372036854775807LL; + mag = a.sig << a.exp; + } else { + if (-a.exp >= 64) return 0; + mag = a.sig >> -a.exp; + } + if (!a.sign) { + if (mag > 9223372036854775807ull) return 9223372036854775807LL; + return (int64_t)mag; + } + if (mag > 9223372036854775808ull) return (int64_t)((uint64_t)1 << 63); + if (mag == 9223372036854775808ull) return (int64_t)((uint64_t)1 << 63); + return -(int64_t)mag; +} + +#else +static double fop_d(uint64_t u) +{ + union { uint64_t u; double d; } x; + x.u = u; + return x.d; +} +static uint64_t fop_u(double d) +{ + union { uint64_t u; double d; } x; + x.d = d; + return x.u; +} +static uint64_t fop_add(uint64_t x, uint64_t y) +{ + volatile double a = fop_d(x), b = fop_d(y), c = a + b; + return fop_u(c); +} +static uint64_t fop_sub(uint64_t x, uint64_t y) +{ + volatile double a = fop_d(x), b = fop_d(y), c = a - b; + return fop_u(c); +} +static uint64_t fop_mul(uint64_t x, uint64_t y) +{ + volatile double a = fop_d(x), b = fop_d(y), c = a * b; + return fop_u(c); +} +static uint64_t fop_div(uint64_t x, uint64_t y) +{ + volatile double a = fop_d(x), b = fop_d(y), c = a / b; + return fop_u(c); +} +static int fop_eq(uint64_t x, uint64_t y) +{ + volatile double a = fop_d(x), b = fop_d(y); + return a == b; +} +static int fop_lt(uint64_t x, uint64_t y) +{ + volatile double a = fop_d(x), b = fop_d(y); + return a < b; +} +static int fop_le(uint64_t x, uint64_t y) +{ + volatile double a = fop_d(x), b = fop_d(y); + return a <= b; +} +static uint64_t fop_i2f(int64_t i) +{ + volatile double d = (double)i; + return fop_u(d); +} +/* trunc toward 0. values outside int64 saturate, and a nan becomes 0. + tests stay in range, where this matches a hardware fcvtzs. */ +static int64_t fop_f2i(uint64_t x) +{ + volatile double d = fop_d(x); + if (!(d == d)) return 0; + if (d >= 9223372036854775808.0) return 9223372036854775807LL; + if (d <= -9223372036854775808.0) return (int64_t)((uint64_t)1 << 63); + return (int64_t)d; +} +#endif + +/* binary64 <-> binary32, in integer arithmetic so every build agrees (no libm, + no compiler runtime). f2s rounds to nearest even and takes a double's bits to + a single's bits (in the low 32); s2f is exact. a nan keeps its sign and the top + of its payload, and comes out quiet. */ +static uint64_t fop_f2s(uint64_t x) +{ + uint64_t sign = (x >> 63) << 31, frac = x & 0xFFFFFFFFFFFFFull, sig, q, rem, half; + int e = (int)((x >> 52) & 0x7FF), E, shift; + if (e == 0x7FF) { + if (!frac) return sign | 0x7F800000u; + return sign | 0x7FC00000u | (frac >> 29); + } + if (e == 0) return sign; /* zero, or a subnormal double: far below the smallest single */ + E = e - 1023; + sig = frac | (1ull << 52); + if (E > 127) return sign | 0x7F800000u; + shift = 29; + if (E < -126) { shift = 29 + (-126 - E); if (shift > 54) return sign; } + q = sig >> shift; + rem = sig & ((1ull << shift) - 1); + half = 1ull << (shift - 1); + if (rem > half || (rem == half && (q & 1))) q++; + if (shift == 29) { + if (q == (1ull << 24)) { q >>= 1; E++; if (E > 127) return sign | 0x7F800000u; } + return sign | ((uint64_t)(E + 127) << 23) | (q & 0x7FFFFF); + } + return sign | q; /* a subnormal; if it rounded up to the smallest normal these are that normal's bits */ +} +static uint64_t fop_s2f(uint64_t x) +{ + uint64_t sign, frac; + int e, p; + x &= 0xFFFFFFFFull; + sign = (x >> 31) << 63; + frac = x & 0x7FFFFF; + e = (int)((x >> 23) & 0xFF); + if (e == 0xFF) { + if (!frac) return sign | 0x7FF0000000000000ull; + return sign | 0x7FF8000000000000ull | (frac << 29); + } + if (e == 0) { + if (!frac) return sign; + p = 22; + while (!(frac >> p)) p--; + frac = (frac << (52 - p)) & 0xFFFFFFFFFFFFFull; + return sign | ((uint64_t)(p - 149 + 1023) << 52) | frac; + } + return sign | ((uint64_t)(e - 127 + 1023) << 52) | (frac << 29); +} + +/* decimal conversion in base 2^32. 96 limbs is room for a 120-digit + significand times 5^445, which covers every double and the digits a + 127-character literal can hold. copies go through a volatile store so a + freestanding build does not emit memcpy. */ +#define FOP_BI 96 +typedef struct { uint32_t d[FOP_BI]; int n; } FopBi; + +static void bi_norm(FopBi *a) +{ + while (a->n > 0 && !a->d[a->n - 1]) a->n--; +} +static void bi_clear(FopBi *a) { a->n = 0; } +static void bi_copy(FopBi *dst, const FopBi *src) +{ + volatile uint32_t *w = dst->d; + int i; + dst->n = src->n; + for (i = 0; i < src->n; i++) w[i] = src->d[i]; +} +static void bi_set(FopBi *a, uint64_t v) +{ + a->n = 0; + if (!v) return; + a->d[0] = (uint32_t)v; + a->n = 1; + if (v >> 32) { a->d[1] = (uint32_t)(v >> 32); a->n = 2; } +} +static void bi_add_u32(FopBi *a, uint32_t v) +{ + uint64_t c = v; + int i = 0; + if (!v) return; + while (c) { + if (i >= FOP_BI) return; + if (i == a->n) a->d[a->n++] = 0; + c += a->d[i]; + a->d[i++] = (uint32_t)c; + c >>= 32; + } +} +static void bi_mul_u32(FopBi *a, uint32_t m) +{ + uint64_t c = 0; + int i; + if (!m || !a->n) { a->n = 0; return; } + for (i = 0; i < a->n; i++) { + c += (uint64_t)a->d[i] * m; + a->d[i] = (uint32_t)c; + c >>= 32; + } + if (c && a->n < FOP_BI) a->d[a->n++] = (uint32_t)c; +} +static void bi_mul(FopBi *a, const FopBi *b) +{ + FopBi r; + int i, j; + uint64_t c; + if (!a->n || !b->n) { a->n = 0; return; } + bi_clear(&r); + r.n = a->n + b->n; + if (r.n > FOP_BI) r.n = FOP_BI; + for (i = 0; i < r.n; i++) r.d[i] = 0; + for (i = 0; i < a->n; i++) { + c = 0; + for (j = 0; j < b->n && i + j < FOP_BI; j++) { + c += (uint64_t)r.d[i + j] + (uint64_t)a->d[i] * b->d[j]; + r.d[i + j] = (uint32_t)c; + c >>= 32; + } + if (c && i + b->n < r.n) r.d[i + b->n] = (uint32_t)c; + } + bi_norm(&r); + bi_copy(a, &r); +} +static void bi_pow5(FopBi *out, int e) +{ + FopBi base, acc; + bi_set(&acc, 1); + bi_set(&base, 5); + if (e < 0) e = 0; + while (e) { + if (e & 1) bi_mul(&acc, &base); + e >>= 1; + if (e) bi_mul(&base, &base); + } + bi_copy(out, &acc); +} +static int bi_bitlen(const FopBi *a) +{ + uint32_t x; + int n; + if (!a->n) return 0; + x = a->d[a->n - 1]; + n = (a->n - 1) * 32; + while (x) { n++; x >>= 1; } + return n; +} +static int bi_bit(const FopBi *a, int i) +{ + if (i < 0 || (i >> 5) >= a->n) return 0; + return (int)((a->d[i >> 5] >> (i & 31)) & 1); +} +static int bi_low_any(const FopBi *a, int nbits) +{ + int i, w, b; + if (nbits <= 0) return 0; + w = nbits >> 5; + b = nbits & 31; + for (i = 0; i < w && i < a->n; i++) if (a->d[i]) return 1; + if (b && w < a->n && (a->d[w] & ((1u << b) - 1))) return 1; + return 0; +} +static uint64_t bi_extract(const FopBi *a, int lo, int nbits) +{ + uint64_t r = 0; + int i; + if (nbits > 63) nbits = 63; + for (i = 0; i < nbits; i++) if (bi_bit(a, lo + i)) r |= (uint64_t)1 << i; + return r; +} +static void bi_shl(FopBi *a, int s) +{ + int w, b, i; + uint32_t prev; + if (s <= 0 || !a->n) return; + w = s >> 5; + b = s & 31; + if (a->n + w + (b ? 1 : 0) > FOP_BI) { + for (i = 0; i < FOP_BI; i++) a->d[i] = 0xffffffffu; + a->n = FOP_BI; + return; + } + if (b) { + prev = 0; + for (i = 0; i < a->n; i++) { + uint32_t cur = a->d[i]; + a->d[i] = (cur << b) | prev; + prev = cur >> (32 - b); + } + if (prev) a->d[a->n++] = prev; + } + if (w) { + for (i = a->n - 1; i >= 0; i--) a->d[i + w] = a->d[i]; + for (i = 0; i < w; i++) a->d[i] = 0; + a->n += w; + } +} +static int bi_cmp(const FopBi *a, const FopBi *b) +{ + int i; + if (a->n != b->n) return a->n < b->n ? -1 : 1; + for (i = a->n - 1; i >= 0; i--) if (a->d[i] != b->d[i]) return a->d[i] < b->d[i] ? -1 : 1; + return 0; +} +static void bi_sub(FopBi *a, const FopBi *b) +{ + uint64_t br = 0; + int i; + for (i = 0; i < a->n; i++) { + uint64_t av = a->d[i]; + uint64_t bv = (uint64_t)(i < b->n ? b->d[i] : 0) + br; + if (av < bv) { a->d[i] = (uint32_t)(av + (1ull << 32) - bv); br = 1; } + else { a->d[i] = (uint32_t)(av - bv); br = 0; } + } + bi_norm(a); +} +/* q = floor(num/den), rem = num % den. den must be nonzero. */ +static void bi_divmod(const FopBi *num, const FopBi *den, FopBi *q, FopBi *rem) +{ + int i, nb = bi_bitlen(num); + bi_clear(q); + bi_clear(rem); + for (i = nb - 1; i >= 0; i--) { + bi_shl(rem, 1); + if (bi_bit(num, i)) bi_add_u32(rem, 1); + if (bi_cmp(rem, den) >= 0) { + bi_sub(rem, den); + { + int w = i >> 5; + while (q->n <= w) q->d[q->n++] = 0; + q->d[w] |= 1u << (i & 31); + } + } + } + bi_norm(q); +} +/* half up: remainder >= half the divisor increments the quotient. */ +static void bi_div_halfup(const FopBi *num, const FopBi *den, FopBi *q) +{ + FopBi rem, twice; + bi_divmod(num, den, q, &rem); + bi_copy(&twice, &rem); + bi_shl(&twice, 1); + if (bi_cmp(&twice, den) >= 0) bi_add_u32(q, 1); +} +static uint32_t bi_div10(FopBi *a) +{ + uint64_t rem = 0; + int i; + for (i = a->n - 1; i >= 0; i--) { + rem = (rem << 32) | a->d[i]; + a->d[i] = (uint32_t)(rem / 10); + rem %= 10; + } + bi_norm(a); + return (uint32_t)rem; +} +static int bi_to_digits(const FopBi *a, char *dig) +{ + FopBi x; + char rev[400]; + int n = 0, i; + bi_copy(&x, a); + if (!x.n) { dig[0] = '0'; dig[1] = 0; return 1; } + while (x.n && n < 390) rev[n++] = (char)('0' + (int)bi_div10(&x)); + for (i = 0; i < n; i++) dig[i] = rev[n - 1 - i]; + dig[n] = 0; + return n; +} +/* right shift by s, then +1 if the highest discarded bit was set. */ +static void bi_shr_halfup(const FopBi *a, int s, FopBi *dst) +{ + int up, w, b, i, len; + if (s <= 0) { bi_copy(dst, a); return; } + len = bi_bitlen(a); + up = bi_bit(a, s - 1); + if (s >= len) { + bi_clear(dst); + if (up) bi_set(dst, 1); + return; + } + w = s >> 5; + b = s & 31; + dst->n = 0; + if (!b) { + for (i = w; i < a->n; i++) dst->d[dst->n++] = a->d[i]; + } else { + for (i = w; i < a->n; i++) { + uint32_t cur = a->d[i] >> b; + if (i + 1 < a->n) cur |= a->d[i + 1] << (32 - b); + dst->d[dst->n++] = cur; + } + } + bi_norm(dst); + if (up) bi_add_u32(dst, 1); +} + +/* q * 2^scale, plus a nonzero fraction below bit 0 when extra is set. + round ties to even. a pure power of two at 2^-1075 is the tie between + zero and the smallest subnormal, and it rounds back to zero. */ +static uint64_t fop_qbits(const FopBi *q, int scale, int extra, int neg) +{ + uint64_t sgn = (uint64_t)neg << 63; + int top, unbiased, low, roundb, sticky; + uint64_t sig; + if (!q->n) return sgn; + top = bi_bitlen(q) - 1; + unbiased = top + scale; + if (unbiased > 1023) return sgn | 0x7ff0000000000000ull; + if (unbiased < -1075) return sgn; + if (unbiased == -1075) { + if (!extra && !bi_low_any(q, top)) return sgn; + return sgn | 1; + } + if (unbiased < -1022) { + int rsh = -(scale + 1074); + if (rsh >= 0) { + sig = bi_extract(q, rsh, top - rsh + 1); + roundb = rsh > 0 ? bi_bit(q, rsh - 1) : 0; + sticky = extra || (rsh > 1 && bi_low_any(q, rsh - 1)); + } else { + sig = bi_extract(q, 0, top + 1) << (-rsh); + roundb = 0; + sticky = extra; + } + if (roundb && (sticky || (sig & 1))) sig++; + if (sig >= (1ull << 52)) return sgn | (1ull << 52); + return sgn | sig; + } + low = top - 52; + if (low <= 0) { + sig = bi_extract(q, 0, top + 1); + if (low < 0) sig <<= -low; + roundb = 0; + sticky = extra; + } else { + sig = bi_extract(q, low, 53); + roundb = bi_bit(q, low - 1); + sticky = extra || bi_low_any(q, low - 1); + } + if (roundb && (sticky || (sig & 1))) { + sig++; + if (sig == (1ull << 53)) { + sig >>= 1; + unbiased++; + if (unbiased > 1023) return sgn | 0x7ff0000000000000ull; + } + } + return sgn | ((uint64_t)(unbiased + 1023) << 52) | (sig & 0xfffffffffffffull); +} + +/* sig * 10^exp10, with sticky meaning the true value is strictly larger. */ +static uint64_t fop_dec_bits(const FopBi *sig, int exp10, int neg, int sticky) +{ + FopBi num, den, q, rem; + int bexp, approx, target, sh, guard = 0; + int sgn = neg ? 1 : 0; + if (!sig->n) return sgn ? 0x8000000000000000ull : 0; + if (exp10 >= 0) { + bi_copy(&num, sig); + bi_pow5(&den, exp10); + bi_mul(&num, &den); + bi_set(&den, 1); + bexp = exp10; + } else { + bi_copy(&num, sig); + bi_pow5(&den, -exp10); + bexp = exp10; + } + approx = bi_bitlen(&num) - bi_bitlen(&den) + bexp; + if (approx > 1080) return sgn ? 0xfff0000000000000ull : 0x7ff0000000000000ull; + if (approx < -1140) return sgn ? 0x8000000000000000ull : 0; +again: + if (approx < -902) target = -1088; + else target = approx - 80; + target -= guard * 32; + sh = bexp - target; + { + FopBi n2, d2; + bi_copy(&n2, &num); + bi_copy(&d2, &den); + if (sh >= 0) bi_shl(&n2, sh); + else bi_shl(&d2, -sh); + bi_divmod(&n2, &d2, &q, &rem); + { + int top = q.n ? bi_bitlen(&q) - 1 : 0; + int low = top - 52; + int unbiased = q.n ? top + target : -9999; + if (q.n && unbiased >= -1022 && low < 4 && guard < 4) { guard++; goto again; } + } + return fop_qbits(&q, target, sticky || rem.n != 0, sgn); + } +} + +/* parse directly from byte text or vm words, without truncating a lexer buffer. */ +typedef struct { const char *bytes; const int64_t *words; uint64_t length; } FopText; +static char fop_char(FopText *s, uint64_t at) +{ + if (s->words) return at < s->length ? (char)(s->words[at] & 255) : 0; + return s->bytes[at]; +} +static int fop_word(FopText *s, uint64_t p, const char *w) +{ + while (*w) { + char c = fop_char(s, p); + if (c >= 'A' && c <= 'Z') c = (char)(c - 'A' + 'a'); + if (c != *w) return 0; + p++; + w++; + } + return fop_char(s, p) == 0; +} + +static uint64_t fop_parse_text(FopText *s) +{ + uint64_t p = 0; + int neg = 0, exp10 = 0, nexp = 0, eneg = 0, frac = 0, saw = 0, nd = 0, sticky = 0; + int dec; + FopBi sig; + if (fop_char(s, p) == '+') p++; + else if (fop_char(s, p) == '-') { neg = 1; p++; } + if (fop_word(s, p, "inf")) return neg ? 0xfff0000000000000ull : 0x7ff0000000000000ull; + if (fop_word(s, p, "nan")) return neg ? 0xfff8000000000000ull : 0x7ff8000000000000ull; + bi_clear(&sig); + while (fop_char(s, p) == '.' || (fop_char(s, p) >= '0' && fop_char(s, p) <= '9')) { + if (fop_char(s, p) == '.') { + if (frac) break; + frac = 1; + p++; + continue; + } + saw = 1; + if (fop_char(s, p) == '0' && !sig.n) { + if (frac) exp10--; + } else if (nd < 80) { + bi_mul_u32(&sig, 10); + bi_add_u32(&sig, (uint32_t)(fop_char(s, p) - '0')); + nd++; + if (frac) exp10--; + } else { + if (fop_char(s, p) != '0') sticky = 1; + if (!frac) exp10++; + } + p++; + } + if (fop_char(s, p) == 'e' || fop_char(s, p) == 'E') { + p++; + if (fop_char(s, p) == '+') p++; + else if (fop_char(s, p) == '-') { eneg = 1; p++; } + while (fop_char(s, p) >= '0' && fop_char(s, p) <= '9') { + if (nexp < 1000000) nexp = nexp * 10 + (fop_char(s, p) - '0'); + p++; + } + if (eneg) exp10 -= nexp; + else exp10 += nexp; + } + if (!saw || !sig.n) return neg ? 0x8000000000000000ull : 0; + dec = exp10 + nd - 1; + if (dec > 309) return neg ? 0xfff0000000000000ull : 0x7ff0000000000000ull; + if (dec < -326) return neg ? 0x8000000000000000ull : 0; + return fop_dec_bits(&sig, exp10, neg, sticky); +} + +static uint64_t fop_parse(const char *bytes) +{ + FopText text = { bytes, 0, 0 }; + return fop_parse_text(&text); +} +static uint64_t fop_parse_words(const int64_t *words, uint64_t length) +{ + FopText text = { 0, words, length }; + return length ? fop_parse_text(&text) : 0; +} + +static void fop_decode(uint64_t bits, uint64_t *sig, int *e2, int *neg, int *cls) +{ + uint64_t exp = (bits >> 52) & 0x7ff; + uint64_t frac = bits & 0x000fffffffffffffull; + *neg = (int)(bits >> 63); + *cls = 0; + *sig = 0; + *e2 = 0; + if (exp == 0x7ff) { *cls = frac ? 2 : 1; return; } + if (!exp) { + *sig = frac; + *e2 = -1074; + return; + } + *sig = frac | (1ull << 52); + *e2 = (int)exp - 1023 - 52; +} + +/* x ? 10^k. x = sig * 2^e2, sig > 0. */ +static int fop_cmp10(uint64_t sig, int e2, int k) +{ + FopBi a, b; + int q; + if (k >= 0) { + bi_set(&a, sig); + bi_pow5(&b, k); + if (e2 >= k) bi_shl(&a, e2 - k); + else bi_shl(&b, k - e2); + return bi_cmp(&a, &b); + } + q = -k; + bi_set(&a, sig); + bi_pow5(&b, q); + bi_mul(&a, &b); + if (e2 + q >= 0) { + bi_shl(&a, e2 + q); + if (!a.n) return -1; + if (a.n == 1 && a.d[0] == 1) return 0; + return 1; + } + bi_set(&b, 1); + bi_shl(&b, -(e2 + q)); + return bi_cmp(&a, &b); +} + +/* no __builtin_clzll: a freestanding riscv link has no __clzdi2. */ +static int fop_nlz(uint64_t x) +{ + int n = 0; + if (!x) return 64; + if (!(x & 0xffffffff00000000ull)) { n += 32; x <<= 32; } + if (!(x & 0xffff000000000000ull)) { n += 16; x <<= 16; } + if (!(x & 0xff00000000000000ull)) { n += 8; x <<= 8; } + if (!(x & 0xf000000000000000ull)) { n += 4; x <<= 4; } + if (!(x & 0xc000000000000000ull)) { n += 2; x <<= 2; } + if (!(x & 0x8000000000000000ull)) n++; + return n; +} +static int fop_decexp(uint64_t sig, int e2) +{ + int log2 = 63 - fop_nlz(sig) + e2; + int est, guard = 0; + if (log2 >= 0) est = (int)(((unsigned __int128)log2 * 1292913986ull) >> 32); + else { + unsigned __int128 m = (unsigned __int128)(-log2) * 1292913986ull; + est = -(int)(m >> 32); + if (m & 0xffffffffull) est--; + } + while (guard++ < 12 && fop_cmp10(sig, e2, est + 1) >= 0) est++; + guard = 0; + while (guard++ < 12 && fop_cmp10(sig, e2, est) < 0) est--; + return est; +} + +/* round_half_up(sig * 2^e2 * 10^scale) as an integer. */ +static void fop_scale(uint64_t sig, int e2, int scale, FopBi *mag) +{ + FopBi num, den; + int i, q10; + if (scale >= 0) { + bi_set(&num, sig); + for (i = 0; i < scale; i++) bi_mul_u32(&num, 5); + if (e2 + scale >= 0) bi_shl(&num, e2 + scale); + else { bi_shr_halfup(&num, -(e2 + scale), mag); return; } + bi_copy(mag, &num); + return; + } + q10 = -scale; + bi_set(&num, sig); + bi_pow5(&den, q10); + if (e2 >= q10) bi_shl(&num, e2 - q10); + else bi_shl(&den, q10 - e2); + bi_div_halfup(&num, &den, mag); +} + +static int fop_putu(char *out, int n, int v) +{ + char b[16]; + int i = 0, j; + if (v < 0) v = -v; + if (!v) { out[n++] = '0'; return n; } + while (v) { b[i++] = (char)('0' + v % 10); v /= 10; } + for (j = i - 1; j >= 0; j--) out[n++] = b[j]; + return n; +} + +/* plain when the exponent is in [-4, 15], otherwise scientific. */ +static int fop_spell(const char *dig, int nd, int dec, int neg, int scientific, char *out) +{ + int n = 0, i; + if (neg) out[n++] = '-'; + if (!scientific) { + if (dec >= 0) { + int whole = dec + 1; + if (nd <= whole) { + for (i = 0; i < nd; i++) out[n++] = dig[i]; + for (; i < whole; i++) out[n++] = '0'; + } else { + for (i = 0; i < whole; i++) out[n++] = dig[i]; + out[n++] = '.'; + for (; i < nd; i++) out[n++] = dig[i]; + } + } else { + out[n++] = '0'; + out[n++] = '.'; + for (i = 0; i < -dec - 1; i++) out[n++] = '0'; + for (i = 0; i < nd; i++) out[n++] = dig[i]; + } + } else { + out[n++] = dig[0]; + if (nd > 1) { + out[n++] = '.'; + for (i = 1; i < nd; i++) out[n++] = dig[i]; + } + out[n++] = 'e'; + out[n++] = dec < 0 ? '-' : '+'; + n = fop_putu(out, n, dec < 0 ? -dec : dec); + } + out[n] = 0; + return n; +} + +static void fop_sigdigs(uint64_t sig, int e2, int prec, char *dig, int *dec) +{ + int d = fop_decexp(sig, e2); + FopBi mag; + char tmp[48]; + int nd = 0, i, guard = 0; + while (guard++ < 8) { + int zeros = 1; + fop_scale(sig, e2, prec - 1 - d, &mag); + nd = bi_to_digits(&mag, tmp); + if (nd == prec) break; + if (nd == prec + 1) { + for (i = 1; i < nd; i++) if (tmp[i] != '0') zeros = 0; + if (tmp[0] == '1' && zeros) { + d++; + dig[0] = '1'; + for (i = 1; i < prec; i++) dig[i] = '0'; + dig[prec] = 0; + *dec = d; + return; + } + } + if (nd > prec) d += nd - prec; + else d -= prec - nd; + } + if (nd > prec) nd = prec; + for (i = 0; i < prec - nd; i++) dig[i] = '0'; + for (i = 0; i < nd && i < prec; i++) dig[prec - nd + i] = tmp[i]; + dig[prec] = 0; + *dec = d; +} + +static int fop_format_fixed(uint64_t sig, int e2, int neg, int prec, char *out) +{ + FopBi t, mag; + char dig[400]; + int n = 0, nd, i, whole; + bi_set(&t, sig ? sig : 0); + if (!sig) bi_clear(&mag); + else { + int i5; + for (i5 = 0; i5 < prec; i5++) bi_mul_u32(&t, 5); + if (e2 + prec >= 0) { bi_shl(&t, e2 + prec); bi_copy(&mag, &t); } + else bi_shr_halfup(&t, -(e2 + prec), &mag); + } + if (neg) out[n++] = '-'; + nd = bi_to_digits(&mag, dig); + if (nd <= prec) { + out[n++] = '0'; + if (prec) { + out[n++] = '.'; + for (i = 0; i < prec - nd; i++) out[n++] = '0'; + for (i = 0; i < nd; i++) out[n++] = dig[i]; + } + } else { + whole = nd - prec; + for (i = 0; i < whole && n < 380; i++) out[n++] = dig[i]; + if (prec && n < 390) { + out[n++] = '.'; + for (i = 0; i < prec; i++) out[n++] = dig[whole + i]; + } + } + out[n] = 0; + return n; +} + +static int fop_special_text(int cls, int neg, char *out) +{ + int n = 0; + if (cls == 2) { out[0] = 'n'; out[1] = 'a'; out[2] = 'n'; out[3] = 0; return 3; } + if (neg) out[n++] = '-'; + out[n++] = 'i'; out[n++] = 'n'; out[n++] = 'f'; + out[n] = 0; + return n; +} + +/* mode 0 shortest, 1 fixed, 2 scientific. out must hold 400 bytes. */ +static int fop_format_mode(uint64_t bits, int64_t mode, int64_t prec, char *out) +{ + uint64_t sig; + int e2, neg, cls, n, k, dec, scientific; + char dig[24], buf[80]; + fop_decode(bits, &sig, &e2, &neg, &cls); + if (cls) return fop_special_text(cls, neg, out); + if (mode == 1) { + if (prec < 0) prec = 0; + if (prec > 17) prec = 17; + return fop_format_fixed(sig, e2, neg, (int)prec, out); + } + if (!sig) { + if (mode == 2) { + int p = (int)prec; + int i; + if (p < 1) p = 1; + if (p > 17) p = 17; + n = 0; + if (neg) out[n++] = '-'; + out[n++] = '0'; + if (p > 1) { + out[n++] = '.'; + for (i = 1; i < p; i++) out[n++] = '0'; + } + out[n++] = 'e'; out[n++] = '+'; out[n++] = '0'; + out[n] = 0; + return n; + } + out[0] = '0'; + if (neg) { out[0] = '-'; out[1] = '0'; out[2] = 0; return 2; } + out[1] = 0; + return 1; + } + if (mode == 2) { + int p = (int)prec; + if (p < 1) p = 1; + if (p > 17) p = 17; + fop_sigdigs(sig, e2, p, dig, &dec); + return fop_spell(dig, p, dec, neg, 1, out); + } + for (k = 1; k <= 18; k++) { + fop_sigdigs(sig, e2, k, dig, &dec); + scientific = !(dec >= -4 && dec <= 15); + fop_spell(dig, k, dec, neg, scientific, buf); + if (k == 18 || fop_parse(buf) == bits) return fop_spell(dig, k, dec, neg, scientific, out); + } + return fop_spell(dig, 18, dec, neg, 1, out); +} + +static int fop_format(uint64_t bits, char *out) +{ + return fop_format_mode(bits, 1, 6, out); +} +#endif diff --git a/src/core/io_linux.c b/src/core/io_linux.c new file mode 100644 index 0000000..8a024aa --- /dev/null +++ b/src/core/io_linux.c @@ -0,0 +1,69 @@ +#include +#ifdef _WIN32 +#include +#include +#else +#include +#include +#endif + +int J; + +#ifdef _WIN32 +static int pk(void){int c=getchar();ungetc(c,stdin);return c==EOF;} +char Q(void){int c=getchar();J=pk();return c;} +#else +/* input is read here, not through stdio, so the end-of-input flag J never blocks. + a client that keeps the pipe open (an editor talking to the language server) sends + one message and waits for the answer: peeking for the next byte after the last + one would wait for a message that comes only after the answer. so J is 1 only + when the end is known, and 0 when more may come. the output is flushed before + any read that can wait, for the same reason. */ +static unsigned char ib[65536]; +static int ip, in, ieof; +/* make a byte available. wait is 0 to look without waiting. returns 1 when one is ready */ +static int fill(int wait) +{ + int n; + if (ip < in) return 1; + if (ieof) return 0; + if (!wait) { + struct pollfd f; f.fd = 0; f.events = POLLIN; f.revents = 0; + if (poll(&f, 1, 0) <= 0) return 0; /* nothing yet: not the end, not a byte */ + } else fflush(stdout); + n = (int)read(0, ib, sizeof ib); + if (n <= 0) { ieof = 1; return 0; } + ip = 0; in = n; + return 1; +} +static int pk(void) { if (ip < in) return 0; if (ieof) return 1; fill(0); return ieof; } +char Q(void) +{ + int c; + if (!fill(1)) { J = 1; return (char)-1; } + c = ib[ip++]; + J = pk(); + return (char)c; +} +#endif +void G(char c){putchar(c);} + +static void init_io(void) { +#ifdef _WIN32 + _setmode(_fileno(stdin), _O_BINARY); + _setmode(_fileno(stdout), _O_BINARY); + J = pk(); +#else + J = pk(); +#endif +} + +#if defined(__GNUC__) || defined(__clang__) +__attribute__((constructor)) static void init(void) { init_io(); } +#elif defined(_MSC_VER) +#pragma section(".CRT$XCU", read) +static void __cdecl msc_init(void) { init_io(); } +__declspec(allocate(".CRT$XCU")) static void (__cdecl *msc_init_fn)(void) = msc_init; +#else +static void init(void) { init_io(); } +#endif diff --git a/src/core/io_win.c b/src/core/io_win.c new file mode 100644 index 0000000..e197353 --- /dev/null +++ b/src/core/io_win.c @@ -0,0 +1,25 @@ +/* Windows I/O port for tinyvm: provides Q, G and J with binary stdio */ +#include +#include +#include + +int J; +static int pk(void) { int c = _fgetc_nolock(stdin); if (c != EOF) _ungetc_nolock(c, stdin); return c == EOF; } +char Q(void) { int c = _fgetc_nolock(stdin); J = pk(); return (char)c; } +void G(char c) { _fputc_nolock(c, stdout); } + +static void init_io(void) { + _setmode(_fileno(stdin), _O_BINARY); + _setmode(_fileno(stdout), _O_BINARY); + J = pk(); +} + +#if defined(__GNUC__) || defined(__clang__) +__attribute__((constructor)) static void init(void) { init_io(); } +#elif defined(_MSC_VER) +#pragma section(".CRT$XCU", read) +static void __cdecl msc_init(void) { init_io(); } +__declspec(allocate(".CRT$XCU")) static void (__cdecl *msc_init_fn)(void) = msc_init; +#else +static void init(void) { init_io(); } +#endif diff --git a/src/core/memmap.h b/src/core/memmap.h new file mode 100644 index 0000000..b2ee787 --- /dev/null +++ b/src/core/memmap.h @@ -0,0 +1,53 @@ +/* machine map, in words. the default has the code area, the compiler tables, + the genome and the stacks first (PW words in all) and the heap last: it + starts at PW and grows at run time, up to MW words of memory. MW is the + host's memory size; the array behind it is untouched until the heap grows + into it. a smaller board is a different build: src/tools/genmap writes a + directory whose memmap.h and genome agree, and the C cores are compiled + against that directory. do not edit the numbers by hand in one file only. + + include genome.h before this header when both are in the translation + unit. the linked image's GORG then wins; it has to be the same value + this header would have defined. + + PW is the first word past the initial stacks. emitted code can add heap + segments, and native code-address tables grow with those reservations. + MW bounds data addresses. a fixed map (HEAP_GROW 0) has MW == PW. + + DSLIM and RSLIM are the interpreter's stack checks. a straight-line + block can push about 1024 words after the check, which is why the data + limit sits 1100 words above DSLO. the genome's prologue checks the current frame segment and adds another + segment when needed. */ +#ifndef MEMMAP_H +#define MEMMAP_H +#define PW 0x1C5000 +#define CODE_END 0x80000 +#define HEAP_LO 0x1C5000 +#define HEAP_HI 0x225000 +#define HEAP_GROW 1 +#ifndef MW +#define MW 0x8000000 +#endif +#if MW < HEAP_HI +#error MW is below the end of the initial heap +#endif +#define ZFP 0x90000 +#ifdef GORG +#if GORG != 0x114100 +#error genome GORG does not match this map +#endif +#else +#define GORG 0x114100 +#endif +#define FBOT 0x164100 +#define FTOP 0x1A4100 +#define RS0 0x1A4100 +#define MARK0 0x1B4100 +#define MARKN 0x8000 +#define DSLO 0x1BC100 +#define DSHI 0x1C4FF0 +#define DS0 DSHI +#define SYSGATE (ZFP + 2) +#define DSLIM (DSLO + 1100) +#define RSLIM (MARK0 - 8) +#endif diff --git a/src/core/stack.h b/src/core/stack.h new file mode 100644 index 0000000..49f347c --- /dev/null +++ b/src/core/stack.h @@ -0,0 +1,112 @@ +/* growing operand and return stacks share the runtime's buddy allocator. + allocation here cannot call vm code or collect: an overflowing return stack + has no room for an allocator call. the runtime registers its allocator and + stack globals once the heap is ready. addresses are vm word indices. */ +#ifndef TINYVM_STACK_H +#define TINYVM_STACK_H + +static Cell stack_control; +enum { + SC_FRAME_FLOOR, SC_FRAME_TOP, SC_FRAME_GUARD, + SC_DATA_LOW, SC_DATA_TOP, SC_DATA_NODE, + SC_RETURN_LOW, SC_RETURN_TOP, SC_RETURN_NODE, + SC_FREE, SC_STOP, SC_BUMP, SC_MAX, SC_HOLD_LOW, SC_HOLD_HIGH, SC_CUT, SC_GC_ACTIVE, SC_COUNT +}; + +/* the including core supplies stack_memory_grow and refreshes m if it moves. */ +static Cell stack_get(int slot) { Cell p = m[stack_control + slot]; return inmem(p) ? m[p] : 0; } +static void stack_set(int slot, Cell value) { Cell p = m[stack_control + slot]; if (inmem(p)) m[p] = value; } +static int stack_register(Cell control) +{ + int i; + if (control <= 0 || !inmem(control + SC_COUNT - 1)) return 0; + for (i = 0; i < SC_COUNT; i++) if (!inmem(m[control + i])) return 0; + if (!inmem(m[control + SC_FREE] + 31)) return 0; + stack_control = control; + return 1; +} +static void stack_push(Cell b, int k) +{ + Cell list = m[stack_control + SC_FREE], head; + if (k < 3 || k >= 31 || b < 0 || !inmem(b + 6) || list < 0 || !inmem(list + k)) return; + head = m[list + k]; + if (head && (head < 0 || !inmem(head + 6))) return; + m[b] = (Cell)1 << k; m[b + 1] = (Cell)k << 16; m[b + 2] = 0; + m[b + 3] = 0; m[b + 4] = 0; m[b + 5] = head; m[b + 6] = 0; + if (head) m[head + 6] = b; + m[list + k] = b; +} +static int stack_heap_grow(int k) +{ + Cell old = stack_get(SC_STOP), h = old - HEAP_LO, block = (Cell)1 << k; + Cell need = ((h + block - 1) / block + 1) * block, size = h * 2, off, part; + int c; + if (h <= 0 || (h & 7) || !HEAP_GROW || !stack_get(SC_MAX) || stack_get(SC_CUT) || stack_get(SC_HOLD_HIGH)) return 0; + if (size < need) size = need; + if (HEAP_LO + size > 0x7fffffff || !stack_memory_grow((UCell)(HEAP_LO + size))) { + size = need; + if (HEAP_LO + size > 0x7fffffff || !stack_memory_grow((UCell)(HEAP_LO + size))) return 0; + } + stack_set(SC_STOP, HEAP_LO + size); stack_set(SC_BUMP, HEAP_LO + size); + stack_set(SC_MAX, HEAP_LO + size); + off = h; + while (off < size) { + part = 8; c = 3; + while (part * 2 <= size - off && !(off % (part * 2))) { part *= 2; c++; } + stack_push(HEAP_LO + off, c); off += part; + } + return 1; +} +static Cell stack_allocate(Cell words) +{ + Cell list, b, next, other, p, i; + int k = 3, j, grown = 0; + if (!stack_control || words < 0 || words > 0x3ffffffa) return 0; + while (((Cell)1 << k) < words + 5) k++; + for (;;) { + list = m[stack_control + SC_FREE]; j = k; + if (list < 0 || !inmem(list + 31)) return 0; + while (j < 31 && !m[list + j]) j++; + if (j == 31) { + if (grown || !stack_heap_grow(k)) return 0; + grown = 1; continue; + } + b = m[list + j]; + if (b < 0 || !inmem(b + ((Cell)1 << j) - 1) || m[b] != ((Cell)1 << j) || (m[b + 1] & 65535)) return 0; + next = m[b + 5]; m[list + j] = next; + if (next) { if (!inmem(next + 6)) return 0; m[next + 6] = 0; } + while (j > k) { j--; other = b + ((Cell)1 << j); stack_push(other, j); } + if (stack_get(SC_HOLD_HIGH) && b < stack_get(SC_HOLD_HIGH) && b + ((Cell)1 << k) > stack_get(SC_HOLD_LOW)) { + m[b] = (Cell)1 << k; m[b + 1] = ((Cell)k << 16) | 2; + m[b + 2] = 0; m[b + 5] = 0; m[b + 6] = 0; continue; + } + p = b + 5; + m[b] = (Cell)1 << k; m[b + 1] = ((Cell)k << 16) | 1 | 512 | 1024 | (stack_get(SC_GC_ACTIVE) ? 2 : 0); + m[b + 2] = p ^ 0x2B7E1516; m[b + 3] = 0; m[b + 4] = -words - 1; + for (i = p; i < b + ((Cell)1 << k); i++) m[i] = 0; + return p; + } +} +static Cell stack_expand(int is_return, Cell minimum, Cell *sp, Cell *rp) +{ + int low_slot = is_return ? SC_RETURN_LOW : SC_DATA_LOW, top_slot = low_slot + 1, node_slot = low_slot + 2; + Cell low, top, capacity, used, node, oldnode, i, source; + if (!stack_control) return -1; + low = stack_get(low_slot); top = stack_get(top_slot); capacity = top - low; + used = is_return ? *rp - low : top - *sp; + if (capacity <= 0 || used < 0 || used > capacity + (is_return ? 8 : 0) || minimum < 0 || minimum > 0x3ffffff0) return -1; + if (minimum <= capacity) return capacity; + if (minimum < capacity * 2) minimum = capacity * 2; + node = stack_allocate(minimum + 16); + if (!node) return -1; + source = is_return ? low : *sp; + for (i = 0; i < used; i++) m[node + (is_return ? i : minimum - used + i)] = m[source + i]; + oldnode = stack_get(node_slot); + stack_set(low_slot, node); stack_set(top_slot, node + minimum); stack_set(node_slot, node); + if (is_return) *rp = node + used; else *sp = node + minimum - used; + if (oldnode >= 5 && inmem(oldnode - 4)) stack_push(oldnode - 5, (int)((m[oldnode - 4] >> 16) & 31)); + return minimum; +} +static Cell stack_data_limit(void) { return stack_control ? stack_get(SC_DATA_LOW) + 1100 : DSLIM; } +static Cell stack_return_limit(void) { return stack_control ? stack_get(SC_RETURN_TOP) : RSLIM; } +#endif diff --git a/src/js/extract_genome.js b/src/js/extract_genome.js new file mode 100644 index 0000000..5cb2b38 --- /dev/null +++ b/src/js/extract_genome.js @@ -0,0 +1,45 @@ +const fs = require('fs'); +const path = require('path'); + +const srcPath = path.join(__dirname, '..', 'core', 'genome.h'); +const outPath = path.join(__dirname, 'genome.json'); + +const text = fs.readFileSync(srcPath, 'utf8'); +const gorgMatch = text.match(/#define\s+GORG\s+(\d+)/); +const glenMatch = text.match(/#define\s+GLEN\s+(\d+)/); + +const gorg = parseInt(gorgMatch[1], 10); +const glen = parseInt(glenMatch[1], 10); + +const arrMatch = text.match(/genome\[\]\s*=\s*\{([\s\S]*?)\};/); +if (!arrMatch) { + console.error('Could not find genome array in genome.h'); + process.exit(1); +} + +const words = arrMatch[1] + .split(',') + .map(s => s.trim()) + .filter(s => s.length > 0) + .map(s => s); // keep as string numbers + +console.log(`Parsed genome: GORG=${gorg}, GLEN=${words.length}`); + +if (words.length !== glen) throw new Error('genome length does not match GLEN'); + +// the compiler's map, from src/core/memmap.h, as typescript constants: the js vm reads +// them instead of its own copies. the values are computed here, from the map. +const { mapValues } = require('./mapvalues.js'); +const names = ['MW', 'PW', 'ZFP', 'GORG', 'DS0', 'DSLIM', 'RS0', 'RSLIM', 'SYSGATE', 'HEAP_LO', 'HEAP_HI']; +const map = mapValues(path.join(__dirname, '..', 'core', 'memmap.h'), names); +if (map.GORG !== gorg) throw new Error('genome GORG ' + gorg + ' is not the map GORG ' + map.GORG); +fs.writeFileSync(outPath, JSON.stringify({ gorg, glen, words })); +console.log(`saved genome to ${outPath}`); +const lines = names.map(k => ` ${k}: ${map[k]},`).join('\n'); +fs.writeFileSync(path.join(__dirname, 'layout.ts'), + '// generated by src/js/extract_genome.js from src/core/memmap.h. do not edit.\n' + + 'export const MAP = {\n' + lines + '\n} as const;\n'); +console.log(`Saved the map to ${path.join(__dirname, 'layout.ts')}`); +fs.writeFileSync(path.join(__dirname, 'layout.js'), + '// generated by src/js/extract_genome.js from src/core/memmap.h. do not edit.\n' + + 'exports.MAP = ' + JSON.stringify(map, null, 4) + ';\n'); diff --git a/src/js/extract_lsp.js b/src/js/extract_lsp.js new file mode 100644 index 0000000..6f53ebc --- /dev/null +++ b/src/js/extract_lsp.js @@ -0,0 +1,21 @@ +// writes src/js/lsp.json: the language server's image (build/lsp/genome.h, made by +// src/tools/mklsp.sh) and the map it was built for, read from build/lsp/map/memmap.h. +// the vm needs the layout, because the server's map is not the compiler's. +const fs = require('fs'); +const path = require('path'); + +const root = path.join(__dirname, '..', '..'); +const genome = fs.readFileSync(path.join(root, 'build', 'lsp', 'genome.h'), 'utf8'); + +const { mapValues } = require('./mapvalues.js'); +const layout = mapValues(path.join(root, 'build', 'lsp', 'map', 'memmap.h'), ['GORG', 'DS0', 'DSLIM', 'RS0', 'RSLIM', 'SYSGATE', 'ZFP', 'HEAP_LO', 'HEAP_HI']); + +const gorg = parseInt(genome.match(/#define\s+GORG\s+(\d+)/)[1], 10); +const glen = parseInt(genome.match(/#define\s+GLEN\s+(\d+)/)[1], 10); +const arr = genome.match(/genome\[\]\s*=\s*\{([\s\S]*?)\};/); +const words = arr[1].split(',').map(s => s.trim()).filter(s => s.length > 0); +if (words.length !== glen) throw new Error('genome has ' + words.length + ' words, GLEN says ' + glen); +if (gorg !== layout.GORG) throw new Error('genome GORG ' + gorg + ' is not the map GORG ' + layout.GORG); + +fs.writeFileSync(path.join(__dirname, 'lsp.json'), JSON.stringify({ gorg, glen, layout, words })); +console.log('wrote src/js/lsp.json: genome ' + glen + ' words at ' + gorg + ', heap ' + layout.HEAP_LO + '..' + layout.HEAP_HI); diff --git a/src/js/mapvalues.js b/src/js/mapvalues.js new file mode 100644 index 0000000..f701ba2 --- /dev/null +++ b/src/js/mapvalues.js @@ -0,0 +1,22 @@ +// reads the values of a machine map from its memmap.h. the #define lines may use +// other names and small sums (ZFP + 2, DSLO + 1100), so each value is evaluated +// with its names substituted. shared by extract_genome.js and extract_lsp.js. +const fs = require('fs'); + +function mapValues(mapPath, names) { + const text = fs.readFileSync(mapPath, 'utf8'); + const defs = {}; + for (const m of text.matchAll(/^#define[ \t]+(\w+)[ \t]+(.+)$/gm)) defs[m[1]] = m[2].trim(); + function value(name, depth = 0) { + if (depth > 20) throw new Error('map loop at ' + name); + const expr = defs[name]; + if (expr === undefined) throw new Error(mapPath + ' has no ' + name); + const sub = expr.replace(/\b[A-Za-z_]\w*\b/g, id => '(' + value(id, depth + 1) + ')'); + return Function('return (' + sub + ');')(); + } + const out = {}; + for (const k of names) out[k] = value(k); + return out; +} + +module.exports = { mapValues }; diff --git a/src/js/tinyvm.ts b/src/js/tinyvm.ts new file mode 100644 index 0000000..ab1e4da --- /dev/null +++ b/src/js/tinyvm.ts @@ -0,0 +1,946 @@ +/** + * the typescript and javascript vm follows the portable microcore. + * it uses 64-bit word memory, + * stacks, opcodes, hostcalls, enhancements, float and byte operations. + */ + +// node's own globals, for the command line runner at the end of this file. they are +// declared here and required where used, so building needs no @types/node +declare const process: any, require: any, module: any, __dirname: string, Buffer: any; +const fs: any = typeof require === 'function' ? require('fs') : undefined; +const path: any = typeof require === 'function' ? require('path') : undefined; + +// the compiler's map, computed from src/core/memmap.h at build time (src/js/layout.ts) +import { MAP } from './layout'; + +// float bits stay in vm words; arithmetic uses javascript's binary64 numbers. +const floatView = new DataView(new ArrayBuffer(8)); +function floatNumber(bits: bigint): number { + floatView.setBigInt64(0, bits, true); return floatView.getFloat64(0, true); +} +function floatBits(value: number): bigint { + floatView.setFloat64(0, value, true); return floatView.getBigInt64(0, true); +} +function floatInteger(bits: bigint): bigint { + const value = floatNumber(bits); + if (Number.isNaN(value)) return 0n; + if (value >= 9223372036854775808) return 9223372036854775807n; + if (value <= -9223372036854775808) return -9223372036854775808n; + return BigInt(Math.trunc(value)); +} +function floatSingle(bits: bigint): bigint { + const raw = BigInt.asUintN(64, bits), frac = raw & 0xfffffffffffffn; + if (((raw >> 52n) & 2047n) === 2047n && frac) return ((raw >> 63n) << 31n) | 0x7fc00000n | (frac >> 29n); + floatView.setFloat32(0, floatNumber(bits), true); return BigInt(floatView.getUint32(0, true)); +} +function floatDouble(bits: bigint): bigint { + const raw = bits & 0xffffffffn, frac = raw & 0x7fffffn; + if (((raw >> 23n) & 255n) === 255n && frac) return BigInt.asIntN(64, ((raw >> 31n) << 63n) | 0x7ff8000000000000n | (frac << 29n)); + floatView.setUint32(0, Number(raw), true); return floatBits(floatView.getFloat32(0, true)); +} +function parseFloatBits(text: string): bigint { + if (/^[+-]?inf$/i.test(text)) return floatBits(text[0] === '-' ? -Infinity : Infinity); + if (/^[+-]?nan$/i.test(text)) return BigInt.asIntN(64, text[0] === '-' ? 0xfff8000000000000n : 0x7ff8000000000000n); + const match = /^[+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?/.exec(text); + return floatBits(match ? Number(match[0]) : 0); +} +// exact decimal scaling keeps fixed and scientific output consistent with fops.h. +function formatFloat(bits: bigint, mode: bigint, precision: bigint): string { + const raw = BigInt.asUintN(64, bits), negative = (raw >> 63n) !== 0n; + const sign = negative ? '-' : '', exponent = Number((raw >> 52n) & 2047n), fraction = raw & 0xfffffffffffffn; + if (exponent === 2047) return fraction ? 'nan' : sign + 'inf'; + const sig = exponent ? fraction | (1n << 52n) : fraction, shift = exponent ? exponent - 1075 : -1074; + const scale = (places: number): string => { + let numerator = sig, denominator = 1n; + if (shift >= 0) numerator <<= BigInt(shift); else denominator <<= BigInt(-shift); + if (places >= 0) numerator *= 10n ** BigInt(places); else denominator *= 10n ** BigInt(-places); + return ((numerator * 2n + denominator) / (denominator * 2n)).toString(); + }; + if (mode === 1n) { + const places = Number(precision < 0n ? 0n : precision > 17n ? 17n : precision); + const digits = scale(places).padStart(places + 1, '0'); + return sign + (places ? digits.slice(0, -places) + '.' + digits.slice(-places) : digits); + } + const scientific = mode === 2n; + const places = Number(precision < 1n ? 1n : precision > 17n ? 17n : precision); + if (!sig) return sign + (scientific ? '0' + (places > 1 ? '.' + '0'.repeat(places - 1) : '') + 'e+0' : '0'); + const spell = (digits: string, decimal: number, useExponent: boolean): string => { + if (useExponent) return sign + digits[0] + (digits.length > 1 ? '.' + digits.slice(1) : '') + 'e' + (decimal < 0 ? '-' : '+') + Math.abs(decimal); + if (decimal < 0) return sign + '0.' + '0'.repeat(-decimal - 1) + digits; + const whole = decimal + 1; + return sign + (digits.length <= whole ? digits + '0'.repeat(whole - digits.length) : digits.slice(0, whole) + '.' + digits.slice(whole)); + }; + for (let count = scientific ? places : 1; count <= (scientific ? places : 18); count++) { + let decimal = Math.floor(Math.log10(Math.abs(floatNumber(bits)))); + let digits = scale(count - 1 - decimal); + while (digits.length !== count) { decimal += digits.length - count; digits = scale(count - 1 - decimal); } + const text = spell(digits, decimal, scientific || decimal < -4 || decimal > 15); + if (scientific || floatBits(Number(text)) === bits || count === 18) return text; + } + return sign + '0'; +} + +// the machine starts at the initial heap's end and grows on the heap's request (syscall 9, 4) +export const DEFAULT_MW = MAP.HEAP_HI; +export const GORG = MAP.GORG; +export const DS0 = MAP.DS0; +export const DSLIM = MAP.DSLIM; +export const RS0 = MAP.RS0; +export const RSLIM = MAP.RSLIM; +export const SYSGATE = MAP.SYSGATE; +export const XOP0 = 22; +export const NENH = 512; + +// micro-op opcode constants +export const U_LI = 0, U_MOV = 1, U_ADD = 2, U_SUB = 3, U_MUL = 4, U_SDIV = 5, U_UDIV = 6, U_SREM = 7, U_UREM = 8; +export const U_AND = 9, U_OR = 10, U_XOR = 11, U_SHL = 12, U_SHR = 13, U_SAR = 14, U_NEG = 15, U_NOT = 16; +export const U_LT = 17, U_LTU = 18, U_EQ = 19, U_NE = 20, U_MIN = 21, U_MAX = 22, U_MINU = 23, U_MAXU = 24; +export const U_MULHS = 25, U_MULHU = 26, U_CLZ = 27, U_ABS = 28, U_SEL = 29, U_LD = 30, U_ST = 31, + U_CTZ = 32, U_POPCNT = 33, U_BSWAP = 34, U_ROTL = 35, U_ROTR = 36, U_F2S = 37, U_S2F = 38; + +export const UNAMES = [ + "li", "mov", "add", "sub", "mul", "sdiv", "udiv", "srem", "urem", + "and", "or", "xor", "shl", "shr", "sar", "neg", "not", + "lt", "ltu", "eq", "ne", "min", "max", "minu", "maxu", + "mulhs", "mulhu", "clz", "abs", "sel", "ld", "st", + "ctz", "popcnt", "bswap", "rotl", "rotr", "f2s", "s2f" +]; + +/** where the image sits and how the map is laid out. the compiler's own genome + (the default) and the language server's have different maps, so the layout + is data, not constants: a program built for another map passes its own. */ +export interface Layout { + GORG: number; + DS0: number; + DSLIM: number; + RS0: number; + RSLIM: number; + SYSGATE: number; + ZFP: number; + HEAP_LO: number; + HEAP_HI: number; +} +export const DEFAULT_LAYOUT: Layout = { + GORG, DS0, DSLIM, RS0, RSLIM, SYSGATE, ZFP: MAP.ZFP, HEAP_LO: MAP.HEAP_LO, HEAP_HI: MAP.HEAP_HI, +}; +/** run() returns this when a streaming program waits for input. */ +export const BLOCKED = 3; + +export interface VMOptions { + memoryWords?: number; + maxMemoryWords?: number; + /** the map the program was built for; the default is the compiler's. */ + layout?: Partial; + /** streaming: input arrives in pieces (feed) and a get_c with nothing + buffered suspends the program instead of reading the end. resume() goes + on from there. eof() is true only once close() has been called. */ + streaming?: boolean; + input?: string | Uint8Array; + onOutput?: (byte: number) => void; + onMessage?: (msg: string) => void; + /** command line for the zc program, ops 21 and 22. empty means the program + was read from stdin, so argv[0] is on its own. */ + args?: string[]; + /** environment entries, ops 23 and 24, each a NAME=value string. */ + env?: string[]; +} + +export interface Enhancement { + live: boolean; + nin: number; + nout: number; + ops: MicroOp[]; + out: number[]; + nv: number; +} + +export interface MicroOp { + op: number; + d: number; + a: number; + b: number; + c: number; + imm: bigint; +} + +export class TinyVM { + public mem: BigInt64Array; + public bytes: Uint8Array; + public view: DataView; + public mw: number; + + private inputBuffer: Uint8Array = new Uint8Array(0); + private inputPos: number = 0; + private outputBuffer: number[] = []; + private onOutput?: (byte: number) => void; + private onMessage?: (msg: string) => void; + + private enh: Enhancement[] = []; + public L: Layout; + private streaming: boolean = false; + private closed: boolean = false; + private blocked: boolean = false; + private rp: number = 0; + private rs: number = 0; + private rr: number = 0; + private memoryLimit: number; + private stackControl = 0; + private args: string[]; + private env: string[]; + + constructor(options: VMOptions = {}) { + this.L = Object.assign({}, DEFAULT_LAYOUT, options.layout || {}); + const minimumWords = Math.max(this.L.HEAP_HI, this.L.DS0 + 16); + this.mw = options.memoryWords ?? minimumWords; + this.memoryLimit = options.maxMemoryWords ?? 0x7fffffff; + if (!Number.isSafeInteger(this.mw) || this.mw < minimumWords || + !Number.isSafeInteger(this.memoryLimit) || this.memoryLimit < this.mw || this.memoryLimit > 0x7fffffff) { + throw new RangeError('invalid initial memory or growth limit'); + } + this.streaming = !!options.streaming; + const buffer = new ArrayBuffer(this.mw * 8); + this.mem = new BigInt64Array(buffer); + this.bytes = new Uint8Array(buffer); + this.view = new DataView(buffer); + + this.onOutput = options.onOutput; + this.onMessage = options.onMessage; + this.args = options.args || []; + this.env = options.env || []; + + if (options.input) { + this.setInput(options.input); + } + + for (let i = 0; i < NENH; i++) { + this.enh.push({ live: false, nin: 0, nout: 0, ops: [], out: [], nv: 0 }); + } + } + + public setInput(input: string | Uint8Array): void { + if (typeof input === 'string') { + this.inputBuffer = new TextEncoder().encode(input); + } else { + this.inputBuffer = input; + } + this.inputPos = 0; + } + + /** streaming input: more bytes after what is already here. */ + public feed(input: string | Uint8Array): void { + const more = typeof input === 'string' ? new TextEncoder().encode(input) : input; + const rest = this.inputBuffer.subarray(this.inputPos); + const joined = new Uint8Array(rest.length + more.length); + joined.set(rest, 0); + joined.set(more, rest.length); + this.inputBuffer = joined; + this.inputPos = 0; + } + + /** no more input will come: a streaming program now reads the end. */ + public close(): void { + this.closed = true; + } + + /** true once the input is used up and nothing more can come. */ + private atEof(): boolean { + if (this.streaming && !this.closed) return false; + return this.inputPos >= this.inputBuffer.length; + } + + public loadGenome(words: (number | string | bigint)[], org: number = this.L.GORG): void { + for (let i = 0; i < words.length; i++) { + this.mem[org + i] = BigInt(words[i]); + } + } + + public getOutput(): string { + return new TextDecoder().decode(new Uint8Array(this.outputBuffer)); + } + + // stack controls and allocator headers use the same layout as stack.h. + private stackGet(slot: number): number { + return Number(this.mem[Number(this.mem[this.stackControl + slot])]); + } + private stackSet(slot: number, value: number): void { + this.mem[Number(this.mem[this.stackControl + slot])] = BigInt(value); + } + private stackRegister(control: number): boolean { + if (!Number.isSafeInteger(control) || control <= 0 || control + 16 >= this.mw) return false; + for (let i = 0; i < 17; i++) { + const address = Number(this.mem[control + i]); + if (!Number.isSafeInteger(address) || address < 0 || address >= this.mw) return false; + } + if (Number(this.mem[control + 9]) + 31 >= this.mw) return false; + this.stackControl = control; + return true; + } + private stackPush(block: number, sizeClass: number): void { + const list = Number(this.mem[this.stackControl + 9]), head = this.mem[list + sizeClass]; + this.mem[block] = BigInt(2 ** sizeClass); this.mem[block + 1] = BigInt(sizeClass * 65536); + this.mem[block + 2] = this.mem[block + 3] = this.mem[block + 4] = 0n; + this.mem[block + 5] = head; this.mem[block + 6] = 0n; + if (head) this.mem[Number(head) + 6] = BigInt(block); + this.mem[list + sizeClass] = BigInt(block); + } + private stackHeapGrow(sizeClass: number): boolean { + const heap = this.stackGet(10) - this.L.HEAP_LO, block = 2 ** sizeClass; + const need = (Math.ceil(heap / block) + 1) * block; + let size = Math.max(heap * 2, need); + if (!this.stackGet(12) || this.stackGet(15) || this.stackGet(14)) return false; + try { if (this.grow(this.L.HEAP_LO + size) < 0) throw new RangeError('stack growth limit'); } + catch { + size = need; + try { if (this.grow(this.L.HEAP_LO + size) < 0) return false; } catch { return false; } + } + this.stackSet(10, this.L.HEAP_LO + size); this.stackSet(11, this.L.HEAP_LO + size); + this.stackSet(12, this.L.HEAP_LO + size); + let offset = heap; + while (offset < size) { + let part = 8, k = 3; + while (part * 2 <= size - offset && offset % (part * 2) === 0) { part *= 2; k++; } + this.stackPush(this.L.HEAP_LO + offset, k); offset += part; + } + return true; + } + private stackAllocate(words: number): number { + if (!this.stackControl || words < 0 || words > 0x3ffffffa) return 0; + let k = 3, grown = false; + while (2 ** k < words + 5) k++; + while (true) { + const list = Number(this.mem[this.stackControl + 9]); + let j = k; + while (j < 31 && !this.mem[list + j]) j++; + if (j === 31) { + if (grown || !this.stackHeapGrow(k)) return 0; + grown = true; continue; + } + const block = Number(this.mem[list + j]); + if (block < 0 || block + 2 ** j > this.mw || this.mem[block] !== BigInt(2 ** j) || (this.mem[block + 1] & 65535n)) return 0; + const next = this.mem[block + 5]; this.mem[list + j] = next; + if (next) { if (Number(next) + 6 >= this.mw) return 0; this.mem[Number(next) + 6] = 0n; } + while (j > k) { j--; this.stackPush(block + 2 ** j, j); } + if (this.stackGet(14) && block < this.stackGet(14) && block + 2 ** k > this.stackGet(13)) { + this.mem[block] = BigInt(2 ** k); this.mem[block + 1] = BigInt(k * 65536 + 2); + this.mem[block + 2] = this.mem[block + 5] = this.mem[block + 6] = 0n; continue; + } + const pointer = block + 5; + this.mem[block] = BigInt(2 ** k); this.mem[block + 1] = BigInt(k * 65536 + 1537 + (this.stackGet(16) ? 2 : 0)); + this.mem[block + 2] = BigInt(pointer) ^ 0x2B7E1516n; this.mem[block + 3] = 0n; this.mem[block + 4] = -BigInt(words) - 1n; + this.mem.fill(0n, pointer, block + 2 ** k); + return pointer; + } + } + private stackExpand(isReturn: boolean, minimum: number, s: number, r: number): number[] | null { + if (!this.stackControl || !Number.isSafeInteger(minimum) || minimum < 0 || minimum > 0x3ffffff0) return null; + const slot = isReturn ? 6 : 3, low = this.stackGet(slot), top = this.stackGet(slot + 1); + const capacity = top - low, used = isReturn ? r - low : top - s; + if (capacity <= 0 || used < 0 || used > capacity + (isReturn ? 8 : 0)) return null; + if (minimum <= capacity) return [s, r, capacity]; + minimum = Math.max(minimum, capacity * 2); + const node = this.stackAllocate(minimum + 16); + if (!node) return null; + const source = isReturn ? low : s, destination = isReturn ? node : node + minimum - used; + this.mem.copyWithin(destination, source, source + used); + const old = this.stackGet(slot + 2); + this.stackSet(slot, node); this.stackSet(slot + 1, node + minimum); this.stackSet(slot + 2, node); + if (old) this.stackPush(old - 5, Number((this.mem[old - 4] >> 16n) & 31n)); + this.L.DSLIM = this.stackGet(3) + 1100; this.L.RSLIM = this.stackGet(7); + return isReturn ? [s, node + used, minimum] : [node + minimum - used, r, minimum]; + } + + /** the next byte, -1 at the end, or BLOCKED when a streaming program has + to wait for more input. */ + private get_c(): number { + if (this.inputPos >= this.inputBuffer.length) { + if (this.streaming && !this.closed) return BLOCKED; + return -1; + } + return this.inputBuffer[this.inputPos++]; + } + + private put_c(c: number): void { + const b = c & 0xFF; + this.outputBuffer.push(b); + if (this.onOutput) { + this.onOutput(b); + } + } + + /** the machine's memory grows to words; the old words stay where they were. returns the new size. */ + private grow(words: number): number { + if (!Number.isSafeInteger(words) || words < 0 || words > this.memoryLimit) return -1; + if (words <= this.mw) return this.mw; + const buf = new ArrayBuffer(words * 8); + new Uint8Array(buf).set(this.bytes.subarray(0, this.mw * 8)); + this.mem = new BigInt64Array(buf); + this.bytes = new Uint8Array(buf); + this.view = new DataView(buf); + this.mw = words; + return words; + } + + private syscall(op: bigint, a: bigint, b: bigint, c: bigint): bigint { + const opNum = Number(op); + switch (opNum) { + case 1: // open + return -1n; + case 2: // close + return 0n; + case 3: // read + return 0n; + case 4: { // write(fd, buf, n) + const n = Number(c); + const bufAddr = Number(b); + for (let i = 0; i < n; i++) { + const ch = Number(this.mem[bufAddr + i] & 0xFFn); + this.put_c(ch); + } + return c; + } + case 5: // getenv + return -1n; + case 6: // time + return BigInt(Math.floor(Date.now() / 1000)); + case 7: // exit + return 0n; + case 8: // rand + return BigInt(Math.floor(Math.random() * 0x7FFFFFFF)); + case 9: { // memory info; a = 4 grows the machine to b words (the heap asks for it) + if (a === 4n) { + try { return BigInt(this.grow(Number(b))); } catch { return -1n; } + } + if (a === 0n) return BigInt(this.mw); + if (a === 1n) return BigInt(this.L.HEAP_LO); + if (a === 2n) return BigInt(this.L.HEAP_HI); + if (a === 3n) return BigInt(this.mw * 8); + if (a === 5n) { + const words = Number(b); + if (!Number.isSafeInteger(words) || words < this.mw || words > 0x7fffffff) return -1n; + this.memoryLimit = words; return b; + } + if (a === 9n) return b > 0n && b <= BigInt(this.mw) ? BigInt(this.mw) : -1n; + if (a === 6n) return this.stackRegister(Number(b)) ? 0n : -1n; + if (a === 7n || a === 8n) { + const moved = this.stackExpand(a === 8n, Number(b), this.rs, this.rr); + if (!moved) return -1n; + [this.rs, this.rr] = moved; + return BigInt(moved[2]); + } + return -1n; + } + case 18: { // fill heap words without running one vm store per word + if (a < BigInt(this.L.HEAP_LO) || c <= 0n || a > BigInt(this.mw) || c > BigInt(this.mw) - a) return -1n; + this.mem.fill(b, Number(a), Number(a + c)); return c; + } + case 19: { // forward word copy; a forward overlap keeps the vm-loop path + if (a < BigInt(this.L.HEAP_LO) || b < 0n || c <= 0n || a > BigInt(this.mw) || b > BigInt(this.mw) || c > BigInt(this.mw) - a || c > BigInt(this.mw) - b) return -1n; + if (a > b && a < b + c) return -1n; + this.mem.copyWithin(Number(a), Number(b), Number(b + c)); return c; + } + case 20: { // skip words that cannot be live heap pointers during collection + if (a < 0n || b < 0n || a > BigInt(this.mw) || b > BigInt(this.mw) - a) return -1n; + const begin = Number(a), count = Number(b), low = BigInt(this.L.HEAP_LO + 5); + for (let i = 0; i < count; i++) { + const word = this.mem[begin + i]; + if (word >= low && word < c && word < BigInt(this.mw) && this.mem[Number(word) - 3] === (word ^ 0x2b7e1516n)) return BigInt(i); + } + return b; + } + case 21: // argc: on its own when there is no program file name + return BigInt(this.args.length ? this.args.length : 1); + case 22: { // argv(i, buf, cap): a zero buffer asks for the length only + const i = Number(a); + const s = (i === 0 && this.args.length === 0) ? '' : this.args[i]; + if (s === undefined) return -1n; + if (b === 0n) return BigInt(s.length); + const addr = Number(b); + const cap = Number(c); + if (cap < 1 || addr < 0 || addr + cap > this.mw) return -1n; + const n = Math.min(s.length, cap - 1); + for (let k = 0; k < n; k++) this.mem[addr + k] = BigInt(s.charCodeAt(k)); + this.mem[addr + n] = 0n; + return BigInt(n); + } + case 23: // envc + return BigInt(this.env.length); + case 24: { // envp, shaped like argv + const s = this.env[Number(a)]; + if (s === undefined) return -1n; + if (b === 0n) return BigInt(s.length); + const addr = Number(b); + const cap = Number(c); + if (cap < 1 || addr < 0 || addr + cap > this.mw) return -1n; + const n = Math.min(s.length, cap - 1); + for (let k = 0; k < n; k++) this.mem[addr + k] = BigInt(s.charCodeAt(k)); + this.mem[addr + n] = 0n; + return BigInt(n); + } + default: + return -1n; + } + } + + private evMicroOp(op: number, a: bigint, b: bigint, c: bigint): bigint { + const ua = BigInt.asUintN(64, a); + const ub = BigInt.asUintN(64, b); + const sh = Number(b & 63n); + + switch (op) { + case U_LI: return a; + case U_MOV: return a; + case U_ADD: return BigInt.asIntN(64, a + b); + case U_SUB: return BigInt.asIntN(64, a - b); + case U_MUL: return BigInt.asIntN(64, a * b); + case U_SDIV: return b === 0n ? 0n : BigInt.asIntN(64, a / b); + case U_UDIV: return ub === 0n ? 0n : BigInt.asIntN(64, BigInt(ua / ub)); + case U_SREM: return b === 0n ? a : BigInt.asIntN(64, a % b); + case U_UREM: return ub === 0n ? a : BigInt.asIntN(64, BigInt(ua % ub)); + case U_AND: return a & b; + case U_OR: return a | b; + case U_XOR: return a ^ b; + case U_SHL: return BigInt.asIntN(64, a << BigInt(sh)); + case U_SHR: return BigInt.asIntN(64, BigInt(ua >> BigInt(sh))); + case U_SAR: return a >> BigInt(sh); + case U_NEG: return BigInt.asIntN(64, -a); + case U_NOT: return ~a; + case U_LT: return a < b ? 1n : 0n; + case U_LTU: return ua < ub ? 1n : 0n; + case U_EQ: return a === b ? 1n : 0n; + case U_NE: return a !== b ? 1n : 0n; + case U_MIN: return a < b ? a : b; + case U_MAX: return a > b ? a : b; + case U_MINU: return ua < ub ? a : b; + case U_MAXU: return ua > ub ? a : b; + case U_MULHS: return BigInt.asIntN(64, (a * b) >> 64n); + case U_MULHU: return BigInt.asIntN(64, (ua * ub) >> 64n); + case U_CLZ: return ua === 0n ? 64n : BigInt(64 - ua.toString(2).length); + case U_CTZ: { if (ua === 0n) return 64n; let n = 0n, x = ua; while (!(x & 1n)) { x >>= 1n; n++; } return n; } + case U_POPCNT: { let n = 0n, x = ua; while (x) { x &= x - 1n; n++; } return n; } + case U_BSWAP: { let r = 0n, x = ua; for (let i = 0; i < 8; i++) { r = (r << 8n) | (x & 0xffn); x >>= 8n; } return BigInt.asIntN(64, r); } + case U_ROTL: return BigInt.asIntN(64, (ua << BigInt(sh)) | (ua >> BigInt((64 - sh) & 63))); + case U_ROTR: return BigInt.asIntN(64, (ua >> BigInt(sh)) | (ua << BigInt((64 - sh) & 63))); + case U_F2S: return floatSingle(a); + case U_S2F: return floatDouble(a); + case U_ABS: return a < 0n ? BigInt.asIntN(64, -a) : a; + case U_SEL: return a !== 0n ? b : c; + default: return 0n; + } + } + + private wordText(address: number): string { + let text = ''; + while (address >= 0 && address < this.mw) { + const value = this.mem[address++]; + if (!value) return text; + text += String.fromCharCode(Number(value & 255n)); + } + throw new Error('recipe string outside memory'); + } + + private parseRecipe(pay: number, namesAddr: number, nin: number, nout: number): { ops: MicroOp[], out: number[], nv: number } { + const ops: MicroOp[] = [], names = new Map(); + const current = Array.from({ length: 16 }, (_, i) => i < nin ? i : -1); + const arity = [0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 3, 1, 2, 1, 1, 1, 2, 2, 1, 1]; + let next = nin; + const append = (op: number, args: number[], imm = 0n): number => { + const d = next++; + ops.push({ op, d, a: args[0] ?? 0, b: args[1] ?? 0, c: args[2] ?? 0, imm }); + return d; + }; + for (let i = 0; i < nin; i++) names.set(this.wordText(Number(this.mem[namesAddr + i])), i); + const register = (name: string): number => { + const match = /^r(\d+)$/.exec(name); + const index = match ? Number(match[1]) : names.get(name); + if (index === undefined || index < 0 || index > 15) throw new Error('unknown recipe register'); + return index; + }; + const value = (index: number): number => { + if (current[index] < 0) current[index] = append(U_LI, [], 0n); + return current[index]; + }; + const operand = (token: string): number => { + const numeric = /^([+-]?)(0[xX][0-9a-fA-F]+|\d+)$/.exec(token); + if (numeric) return append(U_LI, [], BigInt.asIntN(64, (numeric[1] === '-' ? -1n : 1n) * BigInt(numeric[2]))); + return value(register(token)); + }; + for (const statement of this.wordText(pay).split(/[;\n]/).map(text => text.trim()).filter(Boolean)) { + const store = /^st(?:\s|$)/.test(statement); + const match = store ? null : /^(\S+)\s*=\s*(.+)$/.exec(statement); + if (!store && !match) throw new Error('malformed recipe assignment'); + const destination = store ? -1 : register(match![1]); + const tokens = (store ? statement.slice(2) : match![2]).trim().split(/\s+/); + let op = store ? U_ST : -1; + if (!store && tokens.length > 1) { + op = UNAMES.indexOf(tokens.shift()!); + if (op < 0) throw new Error('unknown recipe operation'); + } + const args = tokens.map(operand); + if (op < 0 || op === U_MOV || op === U_LI) { + if (args.length !== 1) throw new Error('wrong recipe arity'); + current[destination] = args[0]; + } else { + if (args.length !== arity[op]) throw new Error('wrong recipe arity'); + const result = append(op, args); + if (!store) current[destination] = result; + } + } + return { ops, out: Array.from({ length: nout }, (_, i) => value(i)), nv: next }; + } + + private lowerVM(pay: number, length: number, nin: number, nout: number): { ops: MicroOp[], out: number[], nv: number } { + const ops: MicroOp[] = [], stack = Array.from({ length: nin }, (_, i) => i); + const map = [U_ADD, U_SUB, U_MUL, U_AND, U_OR, U_XOR, U_SAR, U_LT]; + const constants = new Map(); + let next = nin; + const append = (op: number, args: number[], imm = 0n): number => { + const d = next++; + ops.push({ op, d, a: args[0] ?? 0, b: args[1] ?? 0, c: 0, imm }); + if (op === U_LI) constants.set(d, imm); + return d; + }; + const require = (count: number): void => { if (stack.length < count) throw new Error('invalid vm recipe stack'); }; + for (let i = 0; i < length; i++) { + const word = this.mem[pay + i]; + if (word & 1n) { stack.push(append(U_LI, [], word >> 1n)); continue; } + const op = Number(word >> 1n); + if (op === 9) break; + if (op === 1) { require(1); stack.push(append(U_LD, [stack.pop()!])); } + else if (op === 2) { require(2); const address = stack.pop()!, value = stack.pop()!; append(U_ST, [address, value]); } + else if (op === 3) { + require(1); + const depth = constants.get(stack.pop()!); + if (depth === undefined || depth < 0n || depth >= BigInt(stack.length)) throw new Error('invalid vm recipe pick'); + stack.push(stack[stack.length - 1 - Number(depth)]); + } else if (op === 4) { require(1); stack.pop(); } + else if (op === 5) { require(2); const a = stack.pop()!, b = stack.pop()!; stack.push(a, b); } + else if (op >= 13 && op <= 20) { require(2); const a = stack.pop()!, b = stack.pop()!; stack.push(append(map[op - 13], [b, a])); } + else throw new Error('unsupported vm recipe operation'); + } + if (stack.length !== nout) throw new Error('wrong vm recipe output count'); + return { ops, out: stack, nv: next }; + } + + private install(descAddr: bigint): bigint { + const addr = Number(descAddr); + if (!Number.isSafeInteger(addr) || addr < 0 || addr + 6 >= this.mw) return -1n; + const slot = Number(this.mem[addr]), kind = Number(this.mem[addr + 1]); + // optimizer-region descriptors leave micro-op slots intact on portable hosts. + if (kind === 2) return 0n; + const nin = Number(this.mem[addr + 2]), nout = Number(this.mem[addr + 3]); + const pay = Number(this.mem[addr + 4]), length = Number(this.mem[addr + 5]), names = Number(this.mem[addr + 6]); + if (slot < 0 || slot >= NENH || nin < 0 || nin > 8 || nout < 0 || nout > 8 || (kind !== 0 && kind !== 1)) return -1n; + if (pay < 0 || pay >= this.mw || names < 0 || names + nin > this.mw) return -1n; + if (kind === 1 && (length < 0 || length > 100000 || length > this.mw - pay)) return -5n; + try { + const parsed = kind === 0 ? this.parseRecipe(pay, names, nin, nout) : this.lowerVM(pay, length, nin, nout); + this.enh[slot] = { live: true, nin, nout, ...parsed }; + return 0n; + } catch { + return kind === 0 ? -2n : -4n; + } + } + + private xrun(enh: Enhancement, s: number): number { + const xv: bigint[] = new Array(enh.nv).fill(0n); + + for (let j = 0; j < enh.nin; j++) { + xv[j] = this.mem[s + enh.nin - 1 - j]; + } + + for (const u of enh.ops) { + if (u.op === U_LI) { + xv[u.d] = u.imm; + } else if (u.op === U_LD) { + const addr = Number(xv[u.a]); + xv[u.d] = (addr >= 0 && addr < this.mw) ? this.mem[addr] : 0n; + } else if (u.op === U_ST) { + const addr = Number(xv[u.a]); + if (addr >= 0 && addr < this.mw) this.mem[addr] = xv[u.b]; + } else { + xv[u.d] = this.evMicroOp(u.op, xv[u.a], xv[u.b], xv[u.c]); + } + } + + s = s + enh.nin - enh.nout; + for (let j = 0; j < enh.nout; j++) { + this.mem[s + enh.nout - 1 - j] = xv[enh.out[j]]; + } + return s; + } + + /** runs the image from entryPc. returns 0 when it halts, BLOCKED when a + streaming program waits for input (then feed() and resume()). */ + public run(entryPc: number = this.L.GORG): number { + return this.exec(entryPc, this.L.DS0, this.L.RS0); + } + + /** goes on from a BLOCKED run, once input has been fed. */ + public resume(): number { + if (!this.blocked) return 0; + this.blocked = false; + return this.exec(this.rp, this.rs, this.rr); + } + + private exec(p0: number, s0: number, r0: number): number { + let p = p0; + let s = s0; + let r = r0; + + // the machine's memory can grow in a syscall, so these are read again after one + let mem = this.mem; + let bytes = this.bytes; + let view = this.view; + let mw = this.mw; + + while (p < mw) { + if (s < this.L.DSLIM || r > this.L.RSLIM) { + const isReturn = r > this.L.RSLIM, slot = isReturn ? 6 : 3; + const moved = this.stackControl ? this.stackExpand(isReturn, this.stackGet(slot + 1) - this.stackGet(slot) + 1, s, r) : null; + if (!moved) { + if (this.onMessage) this.onMessage(isReturn ? "zy: return stack overflow\n" : "zy: data stack overflow\n"); + return 2; + } + [s, r] = moved; + mem = this.mem; bytes = this.bytes; view = this.view; mw = this.mw; + } + + const w = mem[p++]; + if ((w & 1n) !== 0n) { + mem[--s] = w >> 1n; + continue; + } + + const op = Number(w >> 1n); + const a = mem[s]; + const b = mem[s + 1]; + + switch (op) { + case 1: // ld + mem[s] = (Number(a) >= 0 && Number(a) < mw) ? mem[Number(a)] : 0n; + break; + case 2: // st + if (Number(a) === this.L.SYSGATE) { + this.rs = s + 2; this.rr = r; + const result = this.syscall(b, mem[Number(a) + 1], mem[Number(a) + 2], mem[Number(a) + 3]); + mem = this.mem; bytes = this.bytes; view = this.view; mw = this.mw; // a grow may have moved them + mem[Number(a)] = result; + s = this.rs; r = this.rr; + break; + } else if (Number(a) >= 0 && Number(a) < mw) { + mem[Number(a)] = b; + } + s += 2; + break; + case 3: { // pick follows the same bounds as a word load + const address = s + 1 + Number(a); + mem[s] = address >= 0 && address < mw ? mem[address] : 0n; + break; + } + case 4: // drop + s++; + break; + case 5: // swap + mem[s] = b; + mem[s + 1] = a; + break; + case 6: // call + mem[r++] = BigInt(p); + p = Number(a); + s++; + break; + case 7: // jmp + p = Number(a); + s++; + break; + case 8: // jz + if (b === 0n) p = Number(a); + s += 2; + break; + case 9: // ret + p = Number(mem[--r]); + break; + case 10: { // get_c + const ch = this.get_c(); + if (ch === BLOCKED) { + // the get_c op runs again when there is input: back to it, stacks as they are + this.rp = p - 1; this.rs = s; this.rr = r; this.blocked = true; + return BLOCKED; + } + mem[--s] = BigInt(ch); + break; + } + case 11: // eof + mem[--s] = this.atEof() ? 1n : 0n; + break; + case 12: // put_c + this.put_c(Number(a & 0xFFn)); + s++; + break; + case 13: // add + mem[++s] = BigInt.asIntN(64, b + a); + break; + case 14: // sub + mem[++s] = BigInt.asIntN(64, b - a); + break; + case 15: // mul + mem[++s] = BigInt.asIntN(64, b * a); + break; + case 16: // and + mem[++s] = b & a; + break; + case 17: // or + mem[++s] = b | a; + break; + case 18: // xor + mem[++s] = b ^ a; + break; + case 19: // sar + mem[++s] = b >> (a & 63n); + break; + case 20: // lt + mem[++s] = b < a ? 1n : 0n; + break; + case 21: // install + mem[s] = this.install(a); + break; + + // float operations use the same stack effects as the portable core. + case 600: + if (p >= mw) return 0; + mem[--s] = mem[p++]; break; + case 601: mem[++s] = floatBits(floatNumber(b) + floatNumber(a)); break; + case 602: mem[++s] = floatBits(floatNumber(b) - floatNumber(a)); break; + case 603: mem[++s] = floatBits(floatNumber(b) * floatNumber(a)); break; + case 604: mem[++s] = floatBits(floatNumber(b) / floatNumber(a)); break; + case 605: mem[++s] = floatNumber(b) === floatNumber(a) ? 1n : 0n; break; + case 606: mem[++s] = floatNumber(b) < floatNumber(a) ? 1n : 0n; break; + case 607: mem[++s] = floatNumber(b) <= floatNumber(a) ? 1n : 0n; break; + case 608: mem[s] = floatBits(Number(a)); break; + case 609: mem[s] = floatInteger(a); break; + case 610: { + const text = formatFloat(a, 1n, 6n); + for (const char of text) this.put_c(char.charCodeAt(0)); + s++; break; + } + case 611: { + let text = '', address = Number(a); + while (address >= 0 && address < mw && mem[address]) text += String.fromCharCode(Number(mem[address++] & 255n)); + mem[s] = parseFloatBits(text); break; + } + case 618: { + const text = formatFloat(mem[s + 2], a, b); + for (const char of text) this.put_c(char.charCodeAt(0)); + s += 3; break; + } + + // byte ops + case 612: { // ld8 + const addr = Number(a); + mem[s] = (addr >= 0 && addr < mw * 8) ? BigInt(bytes[addr]) : 0n; + break; + } + case 613: { // st8 + const addr = Number(a); + if (addr >= 0 && addr < mw * 8) bytes[addr] = Number(b & 0xFFn); + s += 2; + break; + } + case 614: { // ld16 + const addr = Number(a); + mem[s] = (addr >= 0 && addr + 1 < mw * 8) ? BigInt(view.getUint16(addr, true)) : 0n; + break; + } + case 615: { // st16 + const addr = Number(a); + if (addr >= 0 && addr + 1 < mw * 8) view.setUint16(addr, Number(b & 0xFFFFn), true); + s += 2; + break; + } + case 616: { // ld32 + const addr = Number(a); + mem[s] = (addr >= 0 && addr + 3 < mw * 8) ? BigInt(view.getUint32(addr, true)) : 0n; + break; + } + case 617: { // st32 + const addr = Number(a); + if (addr >= 0 && addr + 3 < mw * 8) view.setUint32(addr, Number(b & 0xFFFFFFFFn), true); + s += 2; + break; + } + case 619: // push s + mem[--s] = BigInt(s + 1); + break; + case 620: { // a native call: no native code here, pop the arguments and answer -1 + const blk = Number(a); + const sig = blk >= 0 && blk + 1 < mw ? BigInt.asUintN(64, mem[blk + 1]) : 0n; + let n = 0; + while (n < 15 && ((sig >> BigInt(4 * (n + 1))) & 15n)) n++; + s += n; + mem[s] = -1n; + break; + } + + default: + // custom enhancement + if (op >= XOP0 && op < XOP0 + NENH && this.enh[op - XOP0].live) { + s = this.xrun(this.enh[op - XOP0], s); + } else { + p = mw; + } + break; + } + } + + return 0; + } +} + +// command line runner: node src/js/tinyvm.js , or the source on stdin +if (typeof require === 'function' && typeof module !== 'undefined' && require.main === module) { + const genomePath = path.join(__dirname, 'genome.json'); + if (!fs.existsSync(genomePath)) { + console.error('Error: genome.json not found. Run extract_genome.js first.'); + process.exit(1); + } + + const genomeData = JSON.parse(fs.readFileSync(genomePath, 'utf8')); + let inputSource = ''; + + if (process.argv.length > 2) { + const filePath = process.argv[2]; + if (fs.existsSync(filePath)) { + inputSource = fs.readFileSync(filePath, 'utf8'); + } else { + console.error(`Error: File not found: ${filePath}`); + process.exit(1); + } + } else { + try { + inputSource = fs.readFileSync(0, 'utf8'); + } catch (e) { + console.error('Usage: node tinyvm.js '); + process.exit(1); + } + } + + const environmentWords = (name: string): number | undefined => { + const value = process.env[name]; + if (value === undefined) return undefined; + if (!/^(?:[0-9]+|0x[0-9a-f]+)$/i.test(value)) throw new RangeError('invalid ' + name); + return Number(value); + }; + const vm = new TinyVM({ + memoryWords: environmentWords('TINYVM_MEMORY_WORDS'), + maxMemoryWords: environmentWords('TINYVM_MAX_MEMORY_WORDS'), + onOutput: (byte: number) => process.stdout.write(Buffer.from([byte])), + onMessage: (msg: string) => process.stderr.write(msg) + }); + + vm.loadGenome(genomeData.words, genomeData.gorg); + vm.setInput(inputSource); + const code = vm.run(genomeData.gorg); + process.exit(code); +} + +if (typeof module !== 'undefined') module.exports = { TinyVM, DEFAULT_MW, GORG }; diff --git a/src/lib/std.zc b/src/lib/std.zc new file mode 100644 index 0000000..375f403 --- /dev/null +++ b/src/lib/std.zc @@ -0,0 +1,455 @@ +// small helpers for strings, files, the clock and byte buffers. not part of the +// genome; include it with: include "src/lib/std.zc"; +// syscall numbers (SYS_OPEN and the rest) live in the runtime. +// a string here is a word string, one character per word and a 0 at the end, +// which is char*. the Bytes group takes packed byte strings (b"..."), whose +// type is a plain word. +// the helpers are grouped in structs, and each is a static function, so it is +// called as Group.name(args): Text.itoa(n, buf), Host.now(), Buf.get(b, 0). +// the constructors arena(n) and bnew(n) and the argv helpers are plain +// functions, because an owned result has to carry its .u tag. + +// ---- the command line and the environment ---- + +// argc and argv, envc and envp, the way c hands them to main. a zc string is +// one character per word, so an array of them is []char*. libinit() fills +// these and the runtime calls it before main, if the program has one (an +// image built by build_image, or a check-mode document, does not). +int argc; +[]char* argv; +int envc; +[]char* envp; + +// one entry of a vector syscall, as a fresh string, or 0 past the end. +// op: the syscall that fetches an entry (SYS_ARG or SYS_ENV) +// i: which entry +// returns: the entry, owned by the caller; 0 when there is no entry i +unchecked char*.u argstr(int op, int i) { + int n = syscall(op, i, 0, 0); + char* s; + if (n < 0) return 0; + s = new(n + 1); + syscall(op, i, s, n + 1); + return s; +} + +// every entry of a vector syscall, in order. +// cntop: the syscall that counts the entries (SYS_ARGC or SYS_ENVC) +// stop: the syscall that fetches one (SYS_ARG or SYS_ENV) +// returns: a new array of the strings, owned by the caller +unchecked []char*.u argsvec(int cntop, int stop) { + int n = syscall(cntop, 0, 0, 0), i = 0; + []char* a = new([]char*, 0); + while (i < n) { push(a, argstr(stop, i)); i++; } + return a; +} + +// reads the command line and the environment into argc, argv, envc and envp. +// it stays a plain function: the compiler looks it up by name. +// returns: 0 +int libinit() { + argv = argsvec(SYS_ARGC, SYS_ARG); + envp = argsvec(SYS_ENVC, SYS_ENV); + argc = len(argv); envc = len(envp); + return 0; +} + +// ---- capturing output ---- + +// capture.start() redirects everything the program writes into a string and +// hands it back. writes go to the buffer instead of the terminal until end() +// stops it, so the buffer is a string once end() has run: +// let buf = capture.start(); +// put_s("hello world!"); // nothing on the terminal +// buf.end(); +// put_s(buf); // hello world +// one capture at a time, and the buffer belongs to whoever holds it. +struct capture { + // starts a capture of the output. + // returns: the buffer, a word on purpose: the method end() is looked up on + // any receiver the compiler cannot type, and a typed one has none + static word start() { + int cap = 256; + outmode = 2; outn = 0; outcap = cap; + outbuf = new(cap); + return outbuf; + } + + // stops the capture and terminates the buffer. + // returns: the string it collected (this is the buffer) + word end() { + outbuf[outn] = 0; + outmode = 0; outbuf = 0; outn = 0; outcap = 0; + return this; + } +} + +// ---- word strings ---- + +struct Text { + // the number written in decimal at the start of s. a leading '-' makes it + // negative and the first character that is not a digit ends it. + // s: the string to read + // returns: the number, 0 when s starts with no digit + static int atoi(char* s) { + int n = 0, i = 0, sign = 1; + if (s[0] == '-') { sign = 0 - 1; i = 1; } + while (s[i] >= '0' && s[i] <= '9') { n = n * 10 + s[i] - '0'; i++; } + return n * sign; + } + + // writes n in decimal into dst, with a 0 after it. + // n: the number + // dst: where the text goes; 24 words is always enough + // returns: how many characters were written, not counting the 0 + static int itoa(int n, char* dst) { + int b[24], i = 0, j = 0, neg = 0; + if (!n) { dst[0] = '0'; dst[1] = 0; return 1; } + if (n < 0) { neg = 1; n = 0 - n; } + if (n < 0) { copy_w(dst, "-9223372036854775808", 21); return 20; } + while (n) { b[i] = '0' + n % 10; i++; n = n / 10; } + if (neg) { dst[0] = '-'; j = 1; } + while (i) { i--; dst[j] = b[i]; j++; } + dst[j] = 0; + return j; + } + + // joins two strings. + // dst: where the result goes, with room for both and a 0; not a or b + // a: the first string + // b: the second string + // returns: the length of the result + static int cat(char* dst, char* a, char* b) { + int i = 0, j = 0; + while (a[i]) { dst[j] = a[i]; i++; j++; } + i = 0; + while (b[i]) { dst[j] = b[i]; i++; j++; } + dst[j] = 0; + return j; + } + + // does s begin with pfx? + // s: the string to test + // pfx: the beginning to look for + // returns: 1 if it does (always, for an empty pfx), 0 if not + static int starts(char* s, char* pfx) { + int i = 0; + while (pfx[i]) { if (s[i] != pfx[i]) return 0; i++; } + return 1; + } + + // how many characters come before the 0. + // s: the string + // returns: the length, not counting the 0 + static int len(char* s) { + int n = 0; + while (s[n]) n++; + return n; + } + + // are the two strings the same, character for character? + // a, b: the strings to compare + // returns: 1 if they are equal, 0 if not + static int eq(char* a, char* b) { + int i = 0; + while (a[i] && a[i] == b[i]) i++; + return a[i] == b[i]; + } + + // copies s into dst, with a 0 after it. + // dst: where the copy goes, with room for s and the 0 + // s: the string to copy + // returns: the length of the copy + static int copy(char* dst, char* s) { + int i = 0; + while (s[i]) { dst[i] = s[i]; i++; } + dst[i] = 0; + return i; + } + + // where pat first appears in s. + // s: the string to search + // pat: the string to look for; an empty pat is found at 0 + // returns: the index of the first match, or -1 when there is none + static int find(char* s, char* pat) { + int i = 0, j; + if (!pat[0]) return 0; + while (s[i]) { + j = 0; + while (pat[j] && s[i + j] == pat[j]) j++; + if (!pat[j]) return i; + i++; + } + return 0 - 1; + } + + // changes the ASCII letters of s to lower case, in place. + // s: the string to change + // returns: the length of s + static int lower(char* s) { + int i = 0; + while (s[i]) { + if (s[i] >= 'A' && s[i] <= 'Z') s[i] = s[i] + 32; + i++; + } + return i; + } + + // changes the ASCII letters of s to upper case, in place. + // s: the string to change + // returns: the length of s + static int upper(char* s) { + int i = 0; + while (s[i]) { + if (s[i] >= 'a' && s[i] <= 'z') s[i] = s[i] - 32; + i++; + } + return i; + } + + // removes the spaces, tabs and line breaks from both ends of s, in place. + // s: the string to change + // returns: the length of what is left + static int trim(char* s) { + int n = 0, a = 0, b, i = 0, c; + while (s[n]) n++; + while (a < n && (s[a] == ' ' || s[a] == '\t' || s[a] == '\n' || s[a] == '\r')) a++; + b = n; + while (b > a) { + c = s[b - 1]; + if (!(c == ' ' || c == '\t' || c == '\n' || c == '\r')) break; + b--; + } + n = b - a; + while (i < n) { s[i] = s[i + a]; i++; } + s[n] = 0; + return n; + } +} + +// ---- numbers ---- +// plain integer helpers. none of them can fail: an out of range result wraps +// the same way the arithmetic does. +struct Num { + // the absolute value of n. + static int abs(int n) { + if (n < 0) return 0 - n; + return n; + } + + // the smaller of a and b. + static int min(int a, int b) { + if (a < b) return a; + return b; + } + + // the larger of a and b. + static int max(int a, int b) { + if (a > b) return a; + return b; + } + + // v, kept between lo and hi. + // v: the value + // lo, hi: the bounds, lo first + static int clamp(int v, int lo, int hi) { + if (v < lo) return lo; + if (v > hi) return hi; + return v; + } + + // the greatest common divisor of two numbers, always 0 or more. + static int gcd(int a, int b) { + if (a < 0) a = 0 - a; + if (b < 0) b = 0 - b; + int t; + while (b) { t = a % b; a = b; b = t; } + return a; + } + + // b raised to the power e, for e of 0 or more. e below 0 gives 1. + static int pow(int b, int e) { + int r = 1; + while (e > 0) { r = r * b; e--; } + return r; + } + + // the whole square root of n, rounded down. n below 0 gives 0. + static int isqrt(int n) { + int lo = 0, hi = 3037000499, mid; + if (n <= 0) return 0; + while (lo < hi) { + mid = lo + (hi - lo + 1) / 2; + if (mid <= n / mid) lo = mid; else hi = mid - 1; + } + return lo; + } + + // -1, 0 or 1 by the sign of n. + static int sign(int n) { + if (n < 0) return 0 - 1; + if (n > 0) return 1; + return 0; + } +} + +// ---- the host ---- + +// the argument block for Host.raw: the number, then its six arguments. +int rawblk[7]; + +struct Host { + // calls any linux system call on the host. this is the escape hatch below + // the named helpers: the number is the kernel's, so the meaning of each + // argument depends on the call. a pointer argument is an address in the + // program's memory, and only the host reads it, so a string or buffer has + // to be passed as its address. + // it runs the call on the real machine, not inside the vm, so a program + // that uses it can do whatever the process can do. only use it where that + // is wanted. + // nr: the syscall number (x86-64 and arm64 numbers differ, so check the host) + // a..f: the six arguments, unused ones 0 + // returns: the kernel's result, or -errno when the call fails + static int raw(int nr, int a, int b, int c, int d, int e, int f) { + rawblk[0] = nr; rawblk[1] = a; rawblk[2] = b; rawblk[3] = c; + rawblk[4] = d; rawblk[5] = e; rawblk[6] = f; + return syscall(SYS_RAW, rawblk, 0, 0); + } + + // the value of an environment variable. + // name: the variable's name + // dst: where the value goes, a word string + // n: how many words dst has + // returns: the length of the value, -1 if the variable is not set + static int env(char* name, char* dst, int n) { return syscall(SYS_GETENV, name, dst, n); } + + // the clock. + // returns: the time in seconds since 1970 + static int now() { return syscall(SYS_TIME, 0, 0, 0); } + + // writes a string to a file, replacing what was there. + // path: the file's name + // s: the text to write + // returns: how many bytes were written, -1 if the file could not be opened + static int writefile(char* path, char* s) { + int fd = syscall(SYS_OPEN, path, 1, 0), n; + if (fd < 0) return -1; + n = syscall(SYS_WRITE, fd, s, len_w(s)); + syscall(SYS_CLOSE, fd, 0, 0); + return n; + } +} + +// ---- byte strings ---- +// the same helpers for packed byte strings. word strings above are unchanged. +// a byte string is a word whose first word is its length (see len_s). + +struct Bytes { + // like Text.atoi, for a byte string. + // s: the byte string + // returns: the number + static int atoi(word s) { return s_to_n(s); } + + // like Text.itoa, but the text is made fresh. + // n: the number + // returns: a new byte string + static word itoa(int n) { return n_to_s(n); } + + // like Text.starts, for byte strings. + // s: the byte string to test + // pfx: the beginning to look for + // returns: 1 if s begins with pfx, 0 if not + static int starts(word s, word pfx) { + int i = 0; + if (len_s(pfx) > len_s(s)) return 0; + while (i < len_s(pfx)) { if (at_s(s, i) != at_s(pfx, i)) return 0; i++; } + return 1; + } + + // like Host.env, for byte strings. + // name: the variable's name, a byte string + // dst: where the value goes, a word buffer + // n: how many bytes dst can take + // returns: the length of the value, -1 if the variable is not set + static int env(word name, word dst, int n) { return bgetenv(name, dst, n); } + + // like Host.writefile, for a byte string. + // path: the file's name, a byte string + // s: the bytes to write + // returns: how many bytes were written, -1 if the file could not be opened + static int writefile(word path, word s) { + int fd = bopen(path, 1), n; + if (fd < 0) return -1; + n = bwrite(fd, s, len_s(s)); + syscall(SYS_CLOSE, fd, 0, 0); + return n; + } +} + +// ---- arenas ---- + +// a region that is freed all at once. +// n: its size in words +// returns: the arena, owned by the caller +int .u arena(int n) { return arena_new(n); } + +struct Arena { + // takes n words from an arena. + // a: the arena + // n: how many words + // returns: the address of the first, 0 if the arena is full + static int alloc(word a, int n) { return arena_alloc(a, n); } + + // gives every block of an arena back at once. + // a: the arena + // returns: 0 + static int reset(word a) { return arena_reset(a); } +} + +// ---- growable byte buffers ---- + +// a byte buffer that knows its length and its capacity: two words, then the +// bytes, packed. +// n: the capacity in bytes +// returns: the buffer, owned by the caller, empty +word .u bnew(int n) { + int words; + word p; + if (n < 0) n = 0; + words = (n + 7) >> 3; + p = new(2 + words); + p[0] = 0; + p[1] = n; + return u*p; +} + +struct Buf { + // how many bytes a buffer holds. + // b: the buffer + // returns: its length + static int length(word b) { return b[0]; } + + // how many bytes a buffer can hold. + // b: the buffer + // returns: its capacity + static int capacity(word b) { return b[1]; } + + // one byte of a buffer. + // b: the buffer + // i: the position + // returns: the byte, 0 when i is outside the buffer + static int get(word b, int i) { if (i < 0 || i >= b[0]) return 0; return ld8(addr_b(b + 2) + i); } + + // sets one byte of a buffer. setting the byte just past the end makes the + // buffer one longer. + // b: the buffer + // i: the position, below the capacity + // v: the value; only its low 8 bits are kept + // returns: the byte stored, 0 when i is outside the capacity + static int set(word b, int i, int v) { + if (i < 0 || i >= b[1]) return 0; + st8(addr_b(b + 2) + i, v & 255); + if (i >= b[0]) b[0] = i + 1; + return v & 255; + } +} diff --git a/src/lsp/server.zc b/src/lsp/server.zc new file mode 100644 index 0000000..98e71c3 --- /dev/null +++ b/src/lsp/server.zc @@ -0,0 +1,1929 @@ +// ============================================================================ +// server.zc: the language server. it is appended to the genome after the +// compiler (cat src/runtime/rt.zc src/compiler/compiler.zc src/lsp/server.zc | zc0), so it does not +// analyze zc with a second implementation of the grammar: it runs the +// compiler itself over the document, in check mode (see chk_unit in +// compiler.zc), and reports what the compiler said. a parse word, an +// operator or a type the document defines changes how the rest of the +// document is read, for the checker and for the highlighter, because both are +// the compiler. it speaks json-rpc on stdin and stdout, and when stdin is not +// a protocol stream but a source file it prints the diagnostics as text. +// ============================================================================ + +// the first constant the server declares: the blob cut for a document starts +// here (blob_load, compiler.zc), so this name must stay the first one of the server. +const int LSP_SERVER = 1; + +// the compiler's own functions, declared here so this file checks on its own +// in an editor: the build places them in front of this file, so these lines only +// promise what is already defined there +int chk_unit(int src, int buf, int cap, int sets, int lim); +int hl_begin(int src); +int hl_end(); + +// ---- memory ---- +// nothing here has a fixed address. a document is a heap block of its text, the +// diagnostics and the check's name sets are blocks too, and a check collects +// what the last one left behind (chk_unit). a document compiles into the +// program region from 0 up to CODE_END, which is the map's own. + +const int DGCAP = 12000; +word dgb, sets; int jsize, tcap; +int dgbcap = DGCAP; + +const int MAXDOC = 16, URIW = 160; + +// ---- output ---- +// every message is written twice: once to count its bytes for the header, +// once for real. wc is the only thing that touches the terminal. +int wmode, wcnt; +// writes one byte of output. while a message is being sized the byte is only +// counted; otherwise it goes to the terminal. +int wc(int c) { + if (wmode) wcnt++; else raw_c(c); + return 0; +} +// writes a zero-terminated word string, one byte per character. +int ws(word s) { + int i = 0; + while (s[i]) { wc(s[i]); i++; } + return 0; +} +// writes n in decimal, with a minus sign when it is negative. +int wn(int n) { + [24]word b; int i = 0; + if (n < 0) { wc('-'); n = 0 - n; } + while (1) { b[i] = '0' + n % 10; i++; n = n / 10; if (!n) break; } + while (i) { i--; wc(b[i]); } + return 0; +} +// writes byte c inside a json string: quotes and backslashes are escaped, and +// the control characters become their escapes (\n, \t, \u00xx) so the text stays one line. +int wesc(int c) { + if (c == 34) { wc(92); wc(34); return 0; } + if (c == 92) { wc(92); wc(92); return 0; } + if (c == 10) { wc(92); wc('n'); return 0; } + if (c == 13) { wc(92); wc('r'); return 0; } + if (c == 9) { wc(92); wc('t'); return 0; } + if (c < 32) { + ws("\\u00"); + wc("0123456789abcdef"[c >> 4]); + wc("0123456789abcdef"[c & 15]); + return 0; + } + wc(c); + return 0; +} +// a json string from n chars at p, one per word +int wjs(word p, int n) { + int i = 0; + wc(34); + while (i < n) { wesc(p[i]); i++; } + wc(34); + return 0; +} +// sends one message. gen writes its body: it runs once to count the bytes for the +// Content-Length header, and once more to write them. +int send(int gen) { + wmode = 1; wcnt = 0; gen(); + wmode = 0; + ws("Content-Length: "); wn(wcnt); ws("\r\n\r\n"); + gen(); + return 0; +} + +// ---- documents ---- +// a document is its text, four bytes to a word, in a heap block of its own +// (doff), with a zero word after the last byte so the lexer sees the end. a +// replaced text is a new block, and the old one is garbage for the collector. + +int nodoc, doccap = MAXDOC; +word duri, dulen, doff, dsize, dver, dgen, dlive; +// Grow a heap buffer while retaining its contents. Globals keep these buffers rooted. +unchecked word lsp_grow(word old, int count, int capacity) { + word p = new(capacity); + fill_w(p, 0, capacity); + if (old && count) copy_w(p, old, count); + return p; +} +// Document columns grow together; each URI has its own string allocation. +int docs_grow() { + int next = doccap * 2; + duri = lsp_grow(duri, doccap, next); dulen = lsp_grow(dulen, doccap, next); + doff = lsp_grow(doff, doccap, next); dsize = lsp_grow(dsize, doccap, next); + dver = lsp_grow(dver, doccap, next); dgen = lsp_grow(dgen, doccap, next); + dlive = lsp_grow(dlive, doccap, next); + doccap = next; + return 0; +} +// the byte at offset i of a text. texts are packed four bytes to a word, low byte first. +word dc(word base, int i) { return (base[i >> 2] >> ((i & 3) * 8)) & 255; } +// the document with the uri p (n bytes long), or -1 when the server has no such document. +int dfind(word p, int n) { + int i = 0, k; + while (i < doccap) { + if (dlive[i] && dulen[i] == n) { + k = 0; + while (k < n && duri[i][k] == p[k]) k++; + if (k == n) return i; + } + i++; + } + return 0 - 1; +} +// takes a free document slot for the uri p (n bytes long), growing the columns +// when needed. commit_text sets its text after this. +int docnew(word p, int n) { + int i = 0, k; + while (1) { + if (i == doccap) docs_grow(); + if (!dlive[i]) { + duri[i] = lsp_grow(0, 0, n + 1); + k = 0; while (k < n) { duri[i][k] = p[k]; k++; } + dulen[i] = n; dlive[i] = 1; doff[i] = 0; dsize[i] = 0; dver[i] = 0; dgen[i] = 0; + return i; + } + i++; + } + return 0 - 1; +} +// the text, its terminator and a spare zero word +int dwords(int n) { return (n >> 2) + 2; } +// ---- the check ---- +int chk_i, chk_gen; // the document the compiler's tables describe, and its generation +// checks document i with the compiler in check mode. the diagnostics are left in +// dgb, and the types of the untyped lets are recorded while it runs (lt_on). +int check_doc(int i) { + chk_i = i; chk_gen = dgen[i]; + outmode = 1; + lt_on = 1; lt_n = 0; ltxn = 0; ht_n = 0; + chk_unit(doff[i], dgb, dgbcap, sets, CODE_END); + dgb = dgbuf; dgbcap = dgcap; + lt_on = 0; + outmode = 0; + return 0; +} +// the entry of the untyped local declared at byte offset o, or -1 +int lt_find(int o) { + int k = 0; + while (k < lt_n) { if (lt_off[k] == o) return k; k++; } + return 0 - 1; +} +int ensure_checked(int i) { + if (chk_i != i || chk_gen != dgen[i]) check_doc(i); + return 0; +} + +// ---- positions ---- +// a cursor over the document that turns byte offsets into the line and the +// utf-16 column the protocol wants. it only moves forward, or starts over. +int pbase, plen, poff, pline, pcol; +// starts the position cursor at the top of document i. +int pc_reset(int i) { + pbase = doff[i]; + plen = dsize[i]; + poff = 0; + pline = 0; + pcol = 0; + return 0; +} +// moves the position cursor forward to byte offset off, counting lines and the +// utf-16 columns the protocol uses (a character of four bytes takes two columns). +// a cursor that is past off starts over from the top. +int pc_to(int off) { + int b; + if (off < poff) { poff = 0; pline = 0; pcol = 0; } + if (off > plen) off = plen; + while (poff < off) { + b = dc(pbase, poff); + if (b == 10) { pline++; pcol = 0; } + else if (b < 128 || b >= 192) { if (b >= 240) pcol = pcol + 2; else pcol++; } + poff++; + } + return 0; +} +// the byte offset where the 1-based line l starts, or the end of the text when +// there is no such line. +int line_off(int l) { + int o = 0; + while (l > 1 && o < plen) { if (dc(pbase, o) == 10) l--; o++; } + return o; +} + +// ---- diagnostics ---- +int gd; // the document being reported +// writes a json position, {line, character}, for byte offset off. +int wpos(int off) { + pc_to(off); + ws("{\"line\":"); + wn(pline); + ws(",\"character\":"); + wn(pcol); + wc('}'); + return 0; +} +// writes the publishDiagnostics message for document gd from dgb. each record +// becomes a range (the whole line when the compiler only knew the line), a +// severity and its message. +int gen_diag() { + int at = 0, first = 1, sev, rln, off, rlen, n, o, e; + ws("{\"jsonrpc\":\"2.0\",\"method\":\"textDocument/publishDiagnostics\",\"params\":{\"uri\":"); + wjs(duri[gd], dulen[gd]); + ws(",\"version\":"); wn(dver[gd]); + ws(",\"diagnostics\":["); + pc_reset(gd); + while (at < dgpos) { + sev = dgb[at]; rln = dgb[at + 1]; off = dgb[at + 2]; rlen = dgb[at + 3]; n = dgb[at + 4]; + if (off < 0) { // the compiler only knew the line: mark its text + o = line_off(rln); + while (o < plen && (dc(pbase, o) == 32 || dc(pbase, o) == 9)) o++; + e = o; + while (e < plen && dc(pbase, e) != 10) e++; + if (e > o && dc(pbase, e - 1) == 13) e--; + } else { + o = off; e = off + rlen; + if (o >= plen) { // the end of the file: the last thing before it + o = plen; + while (o > 0 && (dc(pbase, o - 1) == 32 || dc(pbase, o - 1) == 10 || dc(pbase, o - 1) == 13 || dc(pbase, o - 1) == 9)) { + o--; + } + if (o > 0) o--; + e = o + 1; + } + if (e <= o) e = o + 1; + } + if (e > plen) e = plen; + if (o > e) o = e; + if (!first) wc(','); + first = 0; + ws("{\"range\":{\"start\":"); wpos(o); ws(",\"end\":"); wpos(e); + ws("},\"severity\":"); wn(sev); ws(",\"source\":\"zc\",\"message\":"); + wjs(dgb + at + 5, n); + wc('}'); + at = at + 5 + n; + } + ws("]}}"); + return 0; +} +// sends the diagnostics of document i. +int publish(int i) { + gd = i; + send(gen_diag); + return 0; +} + +// ---- semantic tokens ---- +// the legend: keyword type function variable string number operator comment +// parameter property macro enumMember +const int TT_KW = 0, TT_TYPE = 1, TT_FN = 2, TT_VAR = 3, TT_STR = 4, TT_NUM = 5, TT_OP = 6, TT_COM = 7, TT_PARAM = 8, TT_PROP = 9, TT_MACRO = 10, TT_ENUM = 11; +int tfirst, tprevl, tprevc; +// one token on one line +int tok_out(int off, int end, int ty) { + int l, c, len; + pc_to(off); l = pline; c = pcol; + pc_to(end); len = pcol - c; + if (len <= 0) return 0; + if (!tfirst) wc(','); + tfirst = 0; + if (l == tprevl) { wn(0); wc(','); wn(c - tprevc); } else { wn(l - tprevl); wc(','); wn(c); } + wc(','); wn(len); wc(','); wn(ty); ws(",0"); + tprevl = l; tprevc = c; + return 0; +} +// a range that may cross lines is cut at the line ends +int range_out(int off, int end, int ty) { + int e; + while (off < end) { + e = off; + while (e < end && dc(pbase, e) != 10) e++; + if (e > off && dc(pbase, e - 1) == 13) tok_out(off, e - 1, ty); else tok_out(off, e, ty); + off = e + 1; + } + return 0; +} +// between two tokens there is only space and comments +int gap_out(int from, int to) { + int o = from, e, c; + while (o < to) { + c = dc(pbase, o); + if (c == '/' && dc(pbase, o + 1) == '/') { + e = o; while (e < to && dc(pbase, e) != 10) e++; + range_out(o, e, TT_COM); o = e; + } else if (c == '/' && dc(pbase, o + 1) == '*') { + e = o + 2; + while (e < plen && !(dc(pbase, e) == '*' && dc(pbase, e + 1) == '/')) e++; + e = e + 2; if (e > plen) e = plen; + range_out(o, e, TT_COM); o = e; + } else o++; + } + return 0; +} +// is c an operator character, which the highlighter marks as an operator. +int isopc(int c) { + word s = "+-*/%=<>!&|^~?@#$"; + int i = 0; + while (s[i]) { if (s[i] == c) return 1; i++; } + return 0; +} +// is the name n one of the words the highlighter shows as keywords even when the +// compiler has no record for it. +int iskeyword(int n) { + return n == s_fn || n == s_var || n == s_const || n == s_else || n == s_as || n == s_unchecked || n == s_immediate + || n == s_case || n == s_default || n == s_do || n == s_while || n == s_enum || n == s_include || n == s_memdebug + || n == s_enhance || n == s_micro || n == s_vm || n == s_struct || n == s_type || n == s_typecheck; +} +// the record of the name at byte offset o (see rs_note in the compiler), or -1. +// the records are sorted by offset, so this is a binary search. +int rs_find(int o) { + int lo = 0, hi = rsn - 1, mid, v; + while (lo <= hi) { + mid = (lo + hi) / 2; + v = rsrec[mid * RS_W]; + if (v == o) return mid; + if (v < o) lo = mid + 1; else hi = mid - 1; + } + return 0 - 1; +} +// rs_cur is the cursor of the highlighter's pass over the records, which move +// forward with the tokens. +int rs_cur; +// the class of an identifier at byte offset o: what the compiler resolved it to +// when it checked the document, so a local named like a built-in is a variable. +// classify guesses from the name alone, and only serves names with no record. +int rs_class(int o, int n, int pt, int pv) { + int r = -1, k, v, sk; + while (rs_cur < rsn && rsrec[rs_cur * RS_W] < o) rs_cur++; + if (rs_cur < rsn && rsrec[rs_cur * RS_W] == o) r = rs_cur; + if (r < 0) return classify(n, pt, pv); + k = rsrec[r * RS_W + 1]; + if (k == RS_LOCAL) return TT_VAR; + if (k == RS_PARAM) return TT_PARAM; + if (k == RS_FIELD) return TT_PROP; + v = rsrec[r * RS_W + 2]; + sk = v & 15; + if (sk == K_KW) { if (v & 16) return TT_MACRO; return TT_KW; } + if (sk == K_TYPE) return TT_TYPE; + if (sk == K_FN) { if (v & 32) return TT_FN; return TT_VAR; } + if (sk == K_ENH) return TT_FN; + if (sk == K_CONST) return TT_ENUM; + return TT_VAR; +} +// the class of an identifier. pt, pv: the token before it +int classify(int n, int pt, int pv) { + int s, nx = 0; + // a name after fn is a declaration; a name after . or -> that is called is a method + if (pt == T_ID && pv == s_fn) return TT_FN; + if (pt == T_OP && pv == s_dot) { + if (n == s_m || n == s_d || n == s_u) return TT_KW; + peek(); + if (pk_tk == T_OP && pk_tv == s_lp) return TT_FN; + return TT_PROP; + } + if (pt == T_OP && pv == s_arrow) { + peek(); + if (pk_tk == T_OP && pk_tv == s_lp) return TT_FN; + return TT_PROP; + } + if (iskeyword(n)) return TT_KW; + peek(); + if (pk_tk == T_OP && pk_tv == s_lp) nx = 1; + s = lookup(n); + if (s >= 0) { + if (skind[s] == K_KW) { if (s >= ubase) return TT_MACRO; return TT_KW; } + if (skind[s] == K_TYPE) return TT_TYPE; + if (skind[s] == K_FN && !(sflag[s] & F_DEF)) return TT_VAR; // never defined: the check says so + if (skind[s] == K_FN || skind[s] == K_ENH) { + if (nx) return TT_FN; + if (lsget(1, n)) return TT_PARAM; + if (lsget(0, n)) return TT_VAR; + return TT_FN; + } + if (skind[s] == K_CONST) return TT_ENUM; + if (lsget(1, n)) return TT_PARAM; + return TT_VAR; + } + if (lsget(2, n)) return TT_TYPE; + if (lsget(1, n)) return TT_PARAM; + if (lsget(0, n)) return TT_VAR; + if (lsget(3, n)) return TT_PROP; + return TT_VAR; +} +// writes the semantic tokens of document gd: every token in order, each with the +// class the compiler resolved it to (rs_class), and numbers, strings and operators +// by their kind. comments are left to the editor's own grammar. +int gen_tokens() { + int last = 0, ty, pt = 0, pv = 0, o, e, c; + ws("{\"jsonrpc\":\"2.0\",\"id\":"); wid(); ws(",\"result\":{\"data\":["); + pc_reset(gd); + tfirst = 1; tprevl = 0; tprevc = 0; rs_cur = 0; + hl_begin(doff[gd]); + while (tk != T_EOF) { + o = toff; e = toff + toklen; + if (toklen > 0 && o >= last) { + gap_out(last, o); + ty = 0 - 1; + if (tk == T_ID) ty = rs_class(o, tv, pt, pv); + else if (tk == T_NUM) { + c = dc(pbase, o); + if (c == 39) ty = TT_STR; else ty = TT_NUM; + } else if (tk == T_FLT) ty = TT_NUM; + else if (tk == T_STR || tk == T_BSTR) ty = TT_STR; + else if (tk == T_OP) { if (isopc(dc(pbase, o))) ty = TT_OP; } + if (ty >= 0) range_out(o, e, ty); + last = e; + } + pt = tk; pv = tv; + next(); + } + gap_out(last, plen); + hl_end(); + ws("]}}"); + return 0; +} + +// ---- json in ---- +// the body of a message is read straight off stdin a char at a time and +// never held whole: the fields the server cares about are picked out as they +// go by, and a document's text is decoded into the store as it arrives. +int jleft, jpk; +int hline, hchar, hn, hoff, hcursor; word hov; int hovcap = 2300; word htb; int htbc = 80, htn, ann_name_at; word hname; int hnamecap = 80; int hdl, hdle, hcs; +word ann_text; int ann_n; +word mbuf, idbuf, jk, kbuf; int mcap = 64, idcap = 64, jkcap = 16, kcap = 40; int mn, idn, idtype; word ubuf; int ucap = URIW; int un, jver, kn; +word tbase; int tn, thave, tover, tuseen; +// the next byte of the json body. the body is read from stdin and stops after +// jleft bytes; -1 means the end. junget returns a byte to the next jgetc. +int jgetc() { + int c; + if (jpk != 0 - 2) { c = jpk; jpk = 0 - 2; return c; } + if (jleft <= 0) return 0 - 1; + jleft--; + if (eof()) { jleft = 0; return 0 - 1; } + return get_c() & 255; +} +// return byte c, so that the next jgetc reads it again. +int junget(int c) { jpk = c; return 0; } +// the next byte of the json body that is not white space. +int jws() { + int c = jgetc(); + while (c == 32 || c == 9 || c == 10 || c == 13) c = jgetc(); + return c; +} +// where the decoded bytes of a string go +int jput(int sink, int b) { + word at; + if (sink == 1) { + if (mn + 1 >= mcap) { mbuf = lsp_grow(mbuf, mn, mcap * 2); mcap = mcap * 2; } + mbuf[mn] = b; mn++; + } + else if (sink == 2) { + if (idn + 1 >= idcap) { idbuf = lsp_grow(idbuf, idn, idcap * 2); idcap = idcap * 2; } + idbuf[idn] = b; idn++; + } + else if (sink == 3) { + if (un + 1 >= ucap) { ubuf = lsp_grow(ubuf, un, ucap * 2); ucap = ucap * 2; } + ubuf[un] = b; un++; + } + else if (sink == 4) { + if ((tn >> 2) + 2 >= tcap) { tover = 1; return 0; } + at = tbase + (tn >> 2); + if ((tn & 3) == 0) at[0] = 0; + at[0] = at[0] | (b << ((tn & 3) * 8)); + tn++; + } else if (sink == 5) { + if (kn + 1 >= kcap) { kbuf = lsp_grow(kbuf, kn, kcap * 2); kcap = kcap * 2; } + kbuf[kn] = b; kn++; + } + return 0; +} +// writes the code point cp as utf-8 into sink (one byte at a time through jput). +int jutf8(int sink, int cp) { + if (cp < 128) { jput(sink, cp); return 0; } + if (cp < 2048) { jput(sink, 192 | (cp >> 6)); jput(sink, 128 | (cp & 63)); return 0; } + if (cp < 65536) { + jput(sink, 224 | (cp >> 12)); + jput(sink, 128 | ((cp >> 6) & 63)); + jput(sink, 128 | (cp & 63)); + return 0; + } + jput(sink, 240 | (cp >> 18)); + jput(sink, 128 | ((cp >> 12) & 63)); + jput(sink, 128 | ((cp >> 6) & 63)); + jput(sink, 128 | (cp & 63)); + return 0; +} +// reads the four hex digits of a \u escape and returns their value. when a byte +// is not a hex digit, the digits read so far are returned and that byte goes back. +int jhex4() { + int v = 0, i = 0, c, h; + while (i < 4) { + c = jgetc(); h = hexv(c); + if (h < 0) { junget(c); return v; } + v = v * 16 + h; i++; + } + return v; +} +// the rest of a string, the opening quote already read +int jstr(int sink) { + int c, e, cp, lo; + while (1) { + c = jgetc(); + if (c < 0 || c == 34) return 0; + if (c != 92) { jput(sink, c); continue; } + e = jgetc(); + if (e == 'n') jput(sink, 10); + else if (e == 't') jput(sink, 9); + else if (e == 'r') jput(sink, 13); + else if (e == 'b') jput(sink, 8); + else if (e == 'f') jput(sink, 12); + else if (e == 'u') { + cp = jhex4(); + if (cp >= 55296 && cp < 56320) { // a high surrogate: the low half follows as another \u + c = jgetc(); + if (c == 92) { + c = jgetc(); + if (c == 'u') { lo = jhex4(); cp = 65536 + ((cp - 55296) << 10) + (lo - 56320); } + } + } + jutf8(sink, cp); + } else if (e >= 0) jput(sink, e); + } + return 0; +} +// the code of the json key in kbuf, for the keys the server reads (0 for the others). +int kcode() { + if (eq_w(kbuf, "method")) return 1; + if (eq_w(kbuf, "id")) return 2; + if (eq_w(kbuf, "params")) return 3; + if (eq_w(kbuf, "textDocument")) return 4; + if (eq_w(kbuf, "uri")) return 5; + if (eq_w(kbuf, "text")) return 6; + if (eq_w(kbuf, "contentChanges")) return 7; + if (eq_w(kbuf, "version")) return 8; + if (eq_w(kbuf, "position")) return 9; + if (eq_w(kbuf, "line")) return 10; + if (eq_w(kbuf, "character")) return 11; + return 0; +} +// is the key code one of the first dep keys on the json path to the value being read. +int janc(int dep, int code) { + int i = 0; + while (i < dep) { + if (jk[i] == code) return 1; + i++; + } + return 0; +} +// reads one json value at nesting depth dep. the keys the server reads (key) go to +// their fields, and objects and arrays are walked for the ones inside them. +int jval(int key, int dep) { + int c; word k; int sink, neg; + if (dep >= jkcap) { jk = lsp_grow(jk, jkcap, jkcap * 2); jkcap = jkcap * 2; } + jk[dep] = key; + c = jws(); + if (c == '{') { + while (1) { + c = jws(); + if (c < 0 || c == '}') return 0; + if (c == ',') continue; + if (c != 34) continue; + kn = 0; jstr(5); kbuf[kn] = 0; + k = kcode(); + c = jws(); // the colon + jval(k, dep + 1); + } + } + if (c == '[') { + while (1) { + c = jws(); + if (c < 0 || c == ']') return 0; + // a '}' inside an array is malformed: stop here and leave it for the enclosing object, + // since a bare value stops at '}' without taking it, and looping would never end + if (c == '}') { junget(c); return 0; } + if (c == ',') continue; + junget(c); + jval(key, dep + 1); + } + } + if (c == 34) { + sink = 0; + if (key == 1 && dep == 1) { sink = 1; mn = 0; } + else if (key == 2 && dep == 1) { sink = 2; idn = 0; idtype = 2; } + else if (key == 5 && janc(dep, 4)) { sink = 3; un = 0; tuseen = 1; } + else if (key == 6 && (janc(dep, 7) || janc(dep, 4))) { sink = 4; thave = 1; tover = 0; tbase = text_start(); tn = 0; } + jstr(sink); + if (sink == 1) mbuf[mn] = 0; + if (sink == 2) idbuf[idn] = 0; + if (sink == 3) ubuf[un] = 0; + if (sink == 4) { + k = tbase + (tn >> 2); // zero the rest of the last word and the one after it + if ((tn & 3) == 0) k[0] = 0; + k[1] = 0; + } + return 0; + } + if (c < 0) return 0; + // a number, true, false or null + sink = 0; + if (key == 2 && dep == 1) { sink = 2; idn = 0; idtype = 1; } + k = 0; neg = 0; + while (c >= 0 && c != ',' && c != '}' && c != ']' && c != 32 && c != 10 && c != 13 && c != 9) { + if (sink == 2) jput(2, c); + if ((key == 8 && janc(dep, 4)) || ((key == 10 || key == 11) && janc(dep, 9))) { + if (c == '-') { + neg = 1; + } else if (isdig(c)) k = k * 10 + c - '0'; + } + c = jgetc(); + } + junget(c); + if (sink == 2) { + idbuf[idn] = 0; + if (eq_w(idbuf, "null")) idtype = 0; + } + if (key == 8 && janc(dep, 4)) { if (neg) k = 0 - k; jver = k; } + if (key == 10 && janc(dep, 9)) hline = k; + if (key == 11 && janc(dep, 9)) hchar = k; + return 0; +} +// the text of a document being read goes into a block that holds the whole +// message: decoded text is never longer than the message it came in +word text_start() { + tcap = dwords(jsize); + tbase = new(tcap); + return tbase; +} + +// ---- messages in ---- +word lbuf; int lcap = 160; int rfirst = 0 - 1; +// one header line, without the line end. returns its length, or -1 at the end of the input +int rl() { + int n = 0, c; + while (1) { + if (rfirst >= 0) { c = rfirst; rfirst = 0 - 1; } + else { + if (eof()) { if (!n) return 0 - 1; return n; } + c = get_c() & 255; + } + if (c == 10) return n; + if (c != 13) { + if (n + 1 >= lcap) { lbuf = lsp_grow(lbuf, n, lcap * 2); lcap = lcap * 2; } + lbuf[n] = c; n++; + } + } + return n; +} +// the lower case of an ascii letter. other bytes are unchanged. +int lower(int c) { + if (c >= 'A' && c <= 'Z') return c + 32; + return c; +} +// does the header line in lbuf start with content-length: (in any case). +int is_clen(int n) { + if (n < 15) return 0; + word s = "content-length:"; int i = 0; + while (s[i]) { if (lower(lbuf[i]) != s[i]) return 0; i++; } + return 1; +} +int inited, shut, quit; +// read one message into the field buffers. returns 0 at the end of the input +int read_message() { + int n, size = 0 - 1, i; + while (1) { + n = rl(); + if (n < 0) return 0; + if (n == 0) { if (size >= 0) break; else continue; } + if (is_clen(n)) { + i = 15; size = 0; + while (i < n && (lbuf[i] == 32 || lbuf[i] == 9)) i++; + while (i < n && isdig(lbuf[i])) { size = size * 10 + lbuf[i] - '0'; i++; } + } + } + jleft = size; jpk = 0 - 2; jsize = size; + mn = 0; mbuf[0] = 0; idn = 0; idbuf[0] = 0; idtype = 0; un = 0; ubuf[0] = 0; jver = 0; + thave = 0; tover = 0; tuseen = 0; tn = 0; tbase = 0; hline = 0; hchar = 0; + jval(0, 0); + while (jleft > 0) jgetc(); // whatever the parser left behind + return 1; +} + +// ---- messages out ---- +int wid() { + if (idtype == 2) { + wjs(idbuf, idn); + } else if (idtype == 1) ws(idbuf); else ws("null"); + return 0; +} +// the reply to initialize: the server's capabilities, which are text sync, hover, +// and semantic tokens with their legend. +int gen_init() { + ws("{\"jsonrpc\":\"2.0\",\"id\":"); wid(); + ws(",\"result\":{\"capabilities\":{\"textDocumentSync\":{\"openClose\":true,\"change\":1,\"save\":{\"includeText\":true}},"); + ws("\"hoverProvider\":true,\"semanticTokensProvider\":{\"legend\":{\"tokenTypes\":[\"keyword\",\"type\",\"function\",\"variable\",\"string\",\"number\",\"operator\",\"comment\",\"parameter\",\"property\",\"macro\",\"enumMember\"],\"tokenModifiers\":[]},\"full\":true}},"); + ws("\"serverInfo\":{\"name\":\"zc\",\"version\":\"2\"}}}"); + return 0; +} +// a reply with a null result, for the request id in the message. +int gen_null() { + ws("{\"jsonrpc\":\"2.0\",\"id\":"); + wid(); + ws(",\"result\":null}"); + return 0; +} +// the error code the next error reply carries. +int errcode; +// an error reply with the code errcode and a matching message. +int gen_error() { + ws("{\"jsonrpc\":\"2.0\",\"id\":"); + wid(); + ws(",\"error\":{\"code\":"); + wn(errcode); + ws(",\"message\":"); + if (errcode == 0 - 32601) ws("\"method not found\""); else ws("\"server not initialized\""); + ws("}}"); + return 0; +} +// a semantic tokens reply with no tokens, for a document the server does not have. +int gen_empty() { + ws("{\"jsonrpc\":\"2.0\",\"id\":"); + wid(); + ws(",\"result\":{\"data\":[]}}"); + return 0; +} +// sends an error reply with the code given. +int reply_error(int code) { + errcode = code; + send(gen_error); + return 0; +} + +// ---- hover ---- +// the documentation of the name under the cursor: the comment lines directly +// above its declaration in the document, or, for what the compiler and the +// standard library define, the docs stored in the image +int hid(int c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_'; } +// the identifier at hline, hchar of document i, into hname. returns its length +int hover_word(int i) { + int base = doff[i], size = dsize[i], o = 0, ln = 0, col = 0, c, step, width, b, e, n = 0; + hcursor = 0 - 1; hname[0] = 0; + if (hline < 0 || hchar < 0) return 0; + while (ln < hline && o < size) { if (dc(base, o) == 10) ln++; o++; } + if (ln != hline) return 0; + // the protocol counts utf-16 units; the document stores utf-8 bytes. + while (col < hchar && o < size) { + c = dc(base, o); + if (c == 10 || c == 13) return 0; + step = 1; width = 1; + if (c >= 192 && c < 224) step = 2; + else if (c >= 224 && c < 240) step = 3; + else if (c >= 240) { step = 4; width = 2; } + o = o + step; col = col + width; + } + if (col != hchar) return 0; + hcursor = o; + if (o >= size || !hid(dc(base, o))) return 0; + b = o; + while (b > 0 && hid(dc(base, b - 1))) b--; + e = o; + while (e < size && hid(dc(base, e))) e++; + if (e - b + 1 > hnamecap) { hnamecap = e - b + 1; hname = lsp_grow(0, 0, hnamecap); } + hoff = b; + while (b < e) { hname[n] = dc(base, b); n++; b++; } + hname[n] = 0; + return n; +} +// is c a delimiter that ends the name in a top-level declaration +int hdelim(int c) { return c == '(' || c == '=' || c == ';' || c == ',' || c == '[' || c == '<' || c == '{'; } +// does the line at ls (ending before le) declare hname. only a top-level line +// counts (hover_scan has already checked it starts in column 0). the name is +// the word just before the line's first delimiter, so a keyword or type on the +// line (int, fixed, fn) is never the declared name, and a use of the name +// further along the line is not a declaration +word hover_decl(int base, int ls, int le) { + int dl = ls, e, b, k = 0; + while (dl < le && !hdelim(dc(base, dl))) dl++; + if (dl >= le) return 0; + e = dl; + while (e > ls && (dc(base, e - 1) == 32 || dc(base, e - 1) == 9)) e--; + b = e; + while (b > ls && hid(dc(base, b - 1))) b--; + if (b == e) return 0; + while (hname[k] && b + k < e && dc(base, b + k) == hname[k]) k++; + return !hname[k] && b + k == e; +} +// the top-level declaration of hname, found by its line: hdl and hdle are the +// line's start and end, and hcs is the start of the comment block directly above +// it (hcs is -1 when none) which runs to the line at hdl. a declaration needs +// no comment, so the signature still shows without one. +int hover_scan(int i) { + int base = doff[i], size = dsize[i], ls = 0, le, cs = 0 - 1, c0; + while (ls < size) { + le = ls; + while (le < size && dc(base, le) != 10) le++; + c0 = dc(base, ls); + if (c0 == '/' && ls + 1 < le && dc(base, ls + 1) == '/') { + if (cs < 0) cs = ls; + } else { + if (le > ls && c0 != 32 && c0 != 9 && hover_decl(base, ls, le)) { + hdl = ls; hdle = le; hcs = cs; + return 1; + } + cs = 0 - 1; + } + ls = le + 1; + } + return 0; +} +// one character of the hover text, up to its cap +int hov_put(int c) { + if (hn == hovcap) { hov = lsp_grow(hov, hn, hovcap * 2); hovcap = hovcap * 2; } + hov[hn] = c; hn++; + return 0; +} +// comment ranges are scanned once per document version, outside strings and chars. +int hc_i = -1, hc_gen = -1, hc_n, hc_cap = 64; +word hc_start, hc_end, hc_kind; +int hover_comments(int i) { + int o = 0, size = dsize[i], base = doff[i], c, start, kind, bol = 1, quote; + if (hc_i == i && hc_gen == dgen[i]) return 0; + hc_i = i; hc_gen = dgen[i]; hc_n = 0; + while (o < size) { + c = dc(base, o); + if (c == 10) { bol = 1; o++; continue; } + if (c == 32 || c == 9 || c == 13) { o++; continue; } + if (c == 34 || c == 39) { + quote = c; o++; + while (o < size && dc(base, o) != quote) { if (dc(base, o) == 92) o++; o++; } + o++; bol = 0; continue; + } + kind = 0; start = o; + if (c == '/' && dc(base, o + 1) == '/') { + kind = 1; if (bol) kind = 2; + o = o + 2; while (o < size && dc(base, o) != 10) o++; + } else if (c == '/' && dc(base, o + 1) == '*') { + kind = 3; o = o + 2; + while (o < size && !(dc(base, o) == '*' && dc(base, o + 1) == '/')) { if (dc(base, o) == 10) bol = 1; o++; } + if (o < size) o = o + 2; + } else { o++; bol = 0; continue; } + if (!hc_start || hc_n == hc_cap) { + if (hc_start) hc_cap = hc_cap * 2; + hc_start = lsp_grow(hc_start, hc_n, hc_cap); hc_end = lsp_grow(hc_end, hc_n, hc_cap); hc_kind = lsp_grow(hc_kind, hc_n, hc_cap); + } + hc_start[hc_n] = start; hc_end[hc_n] = o; hc_kind[hc_n] = kind; hc_n++; + } + return 0; +} +// whitespace between documentation and its target may cross one line, not a blank line. +int doc_gap(int base, int from, int to) { + int c, lines = 0; + while (from < to) { + c = dc(base, from); from++; + if (c == 10) { lines++; if (lines > 1) return 0; } + else if (c != 32 && c != 9 && c != 13) return 0; + } + return 1; +} +// remove comment delimiters and optional line-leading stars; retain markdown. +int hover_comment_text(int i, int index) { + int base = doff[i], o = hc_start[index] + 2, end = hc_end[index], bol = 1, c, before = hn; + if (hc_kind[index] == 3) { + if (end >= o + 2 && dc(base, end - 2) == '*' && dc(base, end - 1) == '/') end = end - 2; + if (o < end && dc(base, o) == '*') o++; + } + while (o < end && (dc(base, o) == 32 || dc(base, o) == 9 || dc(base, o) == 10 || dc(base, o) == 13)) o++; + while (end > o && (dc(base, end - 1) == 32 || dc(base, end - 1) == 9 || dc(base, end - 1) == 10 || dc(base, end - 1) == 13)) end--; + if (o == end) return 0; + hov_text("\n\n"); + while (o < end) { + if (bol && hc_kind[index] == 3) { + while (o < end && (dc(base, o) == 32 || dc(base, o) == 9)) o++; + if (o < end && dc(base, o) == '*') { o++; if (dc(base, o) == 32) o++; } + bol = 0; + } + if (o >= end) break; + c = dc(base, o); o++; + if (c != 13) hov_put(c); + if (c == 10) bol = 1; + } + return hn - before; +} +// attach leading docs and adjacent block docs to this exact declaration name. +int hover_docs(int i, int head, int name, int length) { + int k, first = -1, last = -1, pos = head, base = doff[i]; + hover_comments(i); + k = hc_n - 1; + while (k >= 0 && hc_end[k] > head) k--; + while (k >= 0 && hc_kind[k] != 1 && doc_gap(base, hc_end[k], pos)) { + if (last < 0) last = k; + first = k; pos = hc_start[k]; k--; + } + if (first >= 0) { k = first; while (k <= last) { hover_comment_text(i, k); k++; } } + k = 0; + while (k < hc_n) { + if (hc_kind[k] == 3 && (k < first || k > last)) { + if (hc_start[k] >= head && hc_end[k] <= name && doc_gap(base, hc_end[k], name)) hover_comment_text(i, k); + else if (hc_start[k] >= name + length && doc_gap(base, name + length, hc_start[k])) hover_comment_text(i, k); + } + k++; + } + return 0; +} +// the markdown for a declaration: its signature as a code block, then the +// comment block above it, without the slashes, if there is one +int hover_fill(int i) { + int base = doff[i], o = hdl, c, cs, ls, typed = 0, k, start, end; + hn = 0; + while (o < hdle && dc(base, o) != '{' && dc(base, o) != ';' && dc(base, o) != '=') o++; + while (o > hdl && (dc(base, o - 1) == 32 || dc(base, o - 1) == 9)) o--; + hov_put('`'); hov_put('`'); hov_put('`'); hov_put('z'); hov_put('c'); hov_put(10); + c = hdl; + // an untyped fn: the return type the body gives it replaces the fn + if (dc(base, hdl) == 'f' && dc(base, hdl + 1) == 'n' && dc(base, hdl + 2) == 32) { + ensure_annot(i); + k = hdl + 3; + while (k < hdle && (dc(base, k) == 32 || dc(base, k) == 9)) k++; + if (ann_type(i, k)) { + start = ann_name_at; + while (start > 0 && dc(ann_text, start - 1) != 10 && dc(ann_text, start - 1) != ';' && dc(ann_text, start - 1) != '}') start--; + while (start < ann_name_at && (dc(ann_text, start) == 32 || dc(ann_text, start) == 9)) start++; + end = ann_name_at; + while (end < ann_n && dc(ann_text, end) != '{' && dc(ann_text, end) != ';') end++; + while (end > start && (dc(ann_text, end - 1) == 32 || dc(ann_text, end - 1) == 9 || dc(ann_text, end - 1) == 10 || dc(ann_text, end - 1) == 13)) end--; + k = start; while (k < end) { hov_put(dc(ann_text, k)); k++; } + hov_text("\n```"); htn = 0; hover_docs(i, hdl, hdl, 0); + return hn; + } + htn = 0; + } + while (c < o) { hov_put(dc(base, c)); c++; } + hov_put(10); hov_put('`'); hov_put('`'); hov_put('`'); + hover_docs(i, hdl, hdl, 0); + return hn; +} +// the hover reply: the markdown in hov, or null when there is nothing to show. +int gen_hover() { + ws("{\"jsonrpc\":\"2.0\",\"id\":"); wid(); + if (!hn) { ws(",\"result\":null}"); return 0; } + ws(",\"result\":{\"contents\":{\"kind\":\"markdown\",\"value\":"); + wjs(hov, hn); + ws("}}}"); + return 0; +} +// the doc of a name from the image's own symbol blob, or 0. the checks reset the +// symbol table (zc_init, chk_mark), so the docs are read from the blob itself, +// which stays in the image +word blobdoc(word nm) { + word p = GORG[5]; int k; + while (*p) { + if (p[0] == K_DOC && p[3] == 'd' && p[4] == 'o' && p[5] == 'c' && p[6] == ' ') { + k = 0; + while (nm[k] && p[7 + k] == nm[k]) k++; + if (!nm[k] && !p[7 + k]) return p[1]; + } + p = p + 3 + len_w(p + 3) + 1; + } + return 0; +} +// the signature of a name from the image's own symbol blob, or 0 +word blobsig(word nm) { + word p = GORG[5]; int k; + while (*p) { + if (p[0] == K_DOC && p[3] == 's' && p[4] == 'i' && p[5] == 'g' && p[6] == ' ') { + k = 0; + while (nm[k] && p[7 + k] == nm[k]) k++; + if (!nm[k] && !p[7 + k]) return p[1]; + } + p = p + 3 + len_w(p + 3) + 1; + } + return 0; +} +// the start of the line before the one starting at ls, or -1 at the top +int hl_prev(int base, int ls) { + int q; + if (ls <= 0) return 0 - 1; + q = ls - 1; + while (q > 0 && dc(base, q - 1) != 10) q--; + return q; +} +// the last declaration of hname above the hovered line, inside the top-level +// item that holds it: a parameter of its header, or a local (a line of the form +// type name = ..., so a plain use of the name does not count). the text, marked +// as a parameter or a local, goes into hov as a code block. returns 1 when found. +int hover_local(int i) { + int base = doff[i], size = dsize[i], hs = 0, ln = 0, o = 0, ts = 0 - 1, q, c, hl, le, e, b, k, a, found = 0, kind = 0, sb = 0, se = 0, da = 0 - 1; + while (ln < hline && o < size) { if (dc(base, o) == 10) ln++; o++; } + hs = o; + // the top-level line: the hovered line if it starts in column 0, else the + // nearest such line above it (a line that starts in column 0, not a comment) + q = hs; + c = dc(base, q); + if (c != 10 && c != 32 && c != 9 && !(c == '/' && dc(base, q + 1) == '/')) ts = q; + while (ts < 0) { + q = hl_prev(base, q); + if (q < 0) break; + c = dc(base, q); + if (c == 10 || c == 32 || c == 9) continue; + if (c == '/' && dc(base, q + 1) == '/') continue; + ts = q; + } + if (ts < 0) return 0; + // the header line: a parameter named hname is the last word of its segment + hl = ts; + le = hl; + while (le < size && dc(base, le) != 10 && dc(base, le) != '{' && dc(base, le) != ';') le++; + o = hl; + // the segments of the header, one at a time + o = hl; + while (o < le && dc(base, o) != '(') o++; + o++; + while (o < le) { + while (o < le && (dc(base, o) == 32 || dc(base, o) == 9)) o++; + sb = o; + while (o < le && dc(base, o) != ',' && dc(base, o) != ')') o++; + se = o; + while (se > sb && (dc(base, se - 1) == 32 || dc(base, se - 1) == 9)) se--; + e = se; + b = e; + while (b > sb && hid(dc(base, b - 1))) b--; + if (b < e) { // a typed parameter has its type before the name, an untyped one is the name alone + k = 0; + while (hname[k] && b + k < e && dc(base, b + k) == hname[k]) k++; + if (!hname[k] && b + k == e) { found = 1; kind = 1; break; } + } + if (o < le && dc(base, o) == ')') break; + o++; + } + if (found) { hover_put_sig(base, sb, se, kind, 0 - 1); return 1; } + // the body: the last declaration of hname from the item's next line through the + // hovered line (a declaration on the hovered line itself counts) + o = ts; + while (o <= hs) { + le = o; + while (le < hs && dc(base, le) != 10) le++; + if (le == hs) while (le < size && dc(base, le) != 10) le++; + // le is the end of this line; skip the blank and comment lines + q = o; + while (q < le && (dc(base, q) == 32 || dc(base, q) == 9)) q++; + if (q < le && !(dc(base, q) == '/' && dc(base, q + 1) == '/') && q > o && !hl_kw(base, q)) { + e = hdeclend(base, q, le); + if (e < le) { + b = e; + while (b > q && (dc(base, b - 1) == 32 || dc(base, b - 1) == 9)) b--; + a = b; + while (a > q && hid(dc(base, a - 1))) a--; + if (a < b && a > q) { + k = 0; + while (hname[k] && a + k < b && dc(base, a + k) == hname[k]) k++; + if (!hname[k] && a + k == b && (dc(base, a - 1) == 32 || dc(base, a - 1) == 9)) { found = 1; kind = 2; da = a; sb = q; se = e; while (se > sb && (dc(base, se - 1) == 32 || dc(base, se - 1) == 9)) se--; } + } + } + } + if (le >= hs) break; + o = le + 1; + } + if (found) { + k = 0 - 1; + if (kind == 2) k = lt_find(da); + hover_put_sig(base, sb, se, kind, k); + return 1; + } + return 0; +} +// does the word from q to e equal w +word hl_is(int base, int q, int e, word w) { + int k = 0; + while (w[k] && q + k < e && dc(base, q + k) == w[k]) k++; + return !w[k] && q + k == e; +} +// does a statement start at q with a keyword that cannot begin a declaration, +// so that "return i;" is not taken for a declaration of i +word hl_kw(int base, int q) { + int e = q; + while (hid(dc(base, e))) e++; + return hl_is(base, q, e, "return") || hl_is(base, q, e, "if") || hl_is(base, q, e, "while") || hl_is(base, q, e, "else") || hl_is(base, q, e, "break") || hl_is(base, q, e, "continue"); +} +// where a declaration's name ends on the line from q: at = ; , { ( or a [ that +// follows a name (let b[24]); the [ of a type (fixed []int) is passed over +int hdeclend(int base, int q, int le) { + int e = q, c; + while (e < le) { + c = dc(base, e); + if (c == '=' || c == ';' || c == ',' || c == '{' || c == '(' || c == ')') return e; + if (c == '[' && e > q && hid(dc(base, e - 1))) return e; + e++; + } + return le; +} +const int AROUNDS = 6; +word abase, aerr; int nbase, aerrcap; +word afx, aft, amask; int anf, afcap, atcap; +word awp; int awn; +word ann_half; +// the inferred types of the untyped declarations. annotate_doc runs the annotate +// rounds on a document (in doc slot 0, which is put back afterwards), and the +// text it ends with is kept in ann_text. ann_type then finds, for a name the +// document declares untyped, the type annotate gave that name: the text between +// the token before it in the annotated text and the name, after the last ( , { ; or =. +int ann_i = -1, ann_gen = -1, ai_end; +// runs the annotate rounds on document i. the rounds run in doc slot 0, and the +// slots they use are put back afterwards. the text they end with is kept in +// ann_text, with its length in ann_n. +int annotate_doc(int i) { + int round = 0, n, k, sv_size, sv_live, sv_ulen, sv_gen, sv_slot1 = 0, sv_live1, sv_ulen1, sv_gen1; + word sv_base, sv_base1; + sv_base = doff[0]; sv_size = dsize[0]; sv_live = dlive[0]; sv_ulen = dulen[0]; sv_gen = dgen[0]; + sv_base1 = doff[1]; sv_slot1 = dsize[1]; sv_live1 = dlive[1]; sv_ulen1 = dulen[1]; sv_gen1 = dgen[1]; + dlive[0] = 1; dulen[0] = 0; + doff[0] = doff[i]; dsize[0] = dsize[i]; dgen[0] = sv_gen + 1; + ifstrict = 1; + check_doc(0); + nbase = err_lines(abase); + while (round < AROUNDS) { + ifon = 1; check_doc(0); ifon = 0; + save_facts(); + if (!anf) break; + k = 0; while (k < anf) { amask[k] = 0; k++; } + fresh_half(); + try_range(0, anf); + n = apply_masked(ann_half); + if (!n) break; + doff[0] = ann_half; dsize[0] = awn; dgen[0] = dgen[0] + 1; + round++; + } + ifstrict = 0; ifon = 0; + ann_text = doff[0]; ann_n = dsize[0]; + doff[0] = sv_base; dsize[0] = sv_size; dlive[0] = sv_live; dulen[0] = sv_ulen; dgen[0] = sv_gen; + doff[1] = sv_base1; dsize[1] = sv_slot1; dlive[1] = sv_live1; dulen[1] = sv_ulen1; dgen[1] = sv_gen1; + chk_i = -1; + return 0; +} +// annotate for the current state of document i, once per state of it +int ensure_annot(int i) { + if (ann_i == i && ann_gen == dgen[i]) return 0; + annotate_doc(i); + ann_i = i; ann_gen = dgen[i]; + return 0; +} +// the next identifier of base at or after pos: its start, with its end in ai_end. +// comments, strings and numbers are passed over. -1 when there is none. +int ai_next(word base, int size, int pos) { + int c, st, q; + while (pos < size) { + c = dc(base, pos); + if (c == '/' && pos + 1 < size && dc(base, pos + 1) == '/') { + while (pos < size && dc(base, pos) != 10) pos++; + } else if (c == '/' && pos + 1 < size && dc(base, pos + 1) == '*') { + pos = pos + 2; + while (pos < size && !(dc(base, pos) == '*' && dc(base, pos + 1) == '/')) pos++; + if (pos < size) pos = pos + 2; + } else if (c == '"' || c == 39) { + q = c; pos++; + while (pos < size && dc(base, pos) != q && dc(base, pos) != 10) { if (dc(base, pos) == 92) pos++; pos++; } + pos++; + } else if (c >= '0' && c <= '9') { + while (pos < size && hid(dc(base, pos))) pos++; + } else if (hid(c)) { + st = pos; + while (pos < size && hid(dc(base, pos))) pos++; + ai_end = pos; + return st; + } else pos++; + } + return 0 - 1; +} +// do the words a[as..ae) of base a and b[bs..be) of base b read the same +int ai_same(word a, int as, int ae, word b, int bs, int be) { + int k = 0; + if (ae - as != be - bs) return 0; + while (as + k < ae) { if (dc(a, as + k) != dc(b, bs + k)) return 0; k++; } + return 1; +} +// is the word at s..e fn or let, which annotate replaces with a type +int ai_kw(word base, int s, int e) { + int k = 0; + if (e - s == 2 && dc(base, s) == 'f' && dc(base, s + 1) == 'n') return 1; + if (e - s == 3 && dc(base, s) == 'l' && dc(base, s + 1) == 'e' && dc(base, s + 2) == 't') return 1; + return k; +} +// is the name at offset o of document i declared untyped: a parameter after ( or +// , and a local after let or a function after fn. only those can gain a type +int ann_untyped(int i, int o) { + word base = doff[i]; + int p = o - 1; + while (p >= 0) { + while (p >= 0 && (dc(base, p) == 32 || dc(base, p) == 9 || dc(base, p) == 10 || dc(base, p) == 13)) p--; + if (p >= 1 && dc(base, p) == '/' && dc(base, p - 1) == '*') { + p = p - 2; while (p >= 1 && !(dc(base, p - 1) == '/' && dc(base, p) == '*')) p--; + p = p - 2; + } else break; + } + if (p < 0) return 0; + if (dc(base, p) == '(' || dc(base, p) == ',') return 1; + if (p >= 2 && dc(base, p) == 't' && dc(base, p - 1) == 'e' && dc(base, p - 2) == 'l') return 1; + if (p >= 1 && dc(base, p) == 'n' && dc(base, p - 1) == 'f') return 1; + return 0; +} +// the type annotate gave the name declared at offset o of document i, in htb +// (htn bytes), or none. the names of the two texts are walked in step: the +// annotated text has the same names in the same order, plus the types it put in. +int ann_type(int i, int o) { + word ob = doff[i], ab = ann_text; + int osz = dsize[i], asz = ann_n, op = 0, ap = 0, prev = 0, os, oe, as, ae, cut, k, n = 0, c; + htn = 0; + while (1) { + os = ai_next(ob, osz, op); + if (os < 0) return 0; + oe = ai_end; + op = oe; + if (ai_kw(ob, os, oe)) continue; + while (1) { + as = ai_next(ab, asz, ap); + if (as < 0) return 0; + ae = ai_end; + ap = ae; + if (ai_same(ob, os, oe, ab, as, ae)) break; + } + if (os == o) { + ann_name_at = as; + cut = prev; + k = prev; + while (k < as) { c = dc(ab, k); if (c == '(' || c == ',' || c == '{' || c == ';' || c == '=' || c == '}' || c == 10) cut = k + 1; k++; } + while (cut < as && (dc(ab, cut) == 32 || dc(ab, cut) == 9 || dc(ab, cut) == 10)) cut++; + k = cut; + while (k < as) { + if (dc(ab, k) == '/' && dc(ab, k + 1) == '*') { + k = k + 2; while (k < as && !(dc(ab, k) == '*' && dc(ab, k + 1) == '/')) k++; + k = k + 2; continue; + } + if (n == htbc) { htb = lsp_grow(htb, n, htbc * 2); htbc = htbc * 2; } + htb[n] = dc(ab, k); n++; k++; + } + while (n > 0 && (htb[n - 1] == 32 || htb[n - 1] == 9 || htb[n - 1] == 10)) n--; + htn = n; + if (htn == 2 && htb[0] == 'f' && htb[1] == 'n') htn = 0; // annotate left a fn alone + if (htn == 3 && htb[0] == 'l' && htb[1] == 'e' && htb[2] == 't') htn = 0; + return htn; + } + prev = ae; + } + return 0; +} +// the markdown of a local or parameter: its declaration text, then what it is +int hover_put_sig(int base, int sb, int se, int kind, int lk) { + int k = sb; + hn = 0; + hov_put('`'); hov_put('`'); hov_put('`'); hov_put('z'); hov_put('c'); hov_put(10); + while (k < se) { hov_put(dc(base, k)); k++; } + if (kind == 1) { + hov_put(' '); hov_put(' '); hov_put('/'); hov_put('/'); hov_put(' '); k = 0; while ("parameter"[k]) { hov_put("parameter"[k]); k++; } + if (htn > 0) { k = 0; while ("; type "[k]) { hov_put("; type "[k]); k++; } k = 0; while (k < htn) { hov_put(htb[k]); k++; } } + } + else { + hov_put(' '); hov_put(' '); hov_put('/'); hov_put('/'); hov_put(' '); k = 0; while ("local"[k]) { hov_put("local"[k]); k++; } + if (lk >= 0) { k = 0; while ("; type "[k]) { hov_put("; type "[k]); k++; } k = lt_at[lk]; while (k < lt_at[lk] + lt_len[lk]) { hov_put(ltxt[k]); k++; } } + else if (htn > 0) { k = 0; while ("; type "[k]) { hov_put("; type "[k]); k++; } k = 0; while (k < htn) { hov_put(htb[k]); k++; } } + } + hov_put(10); hov_put('`'); hov_put('`'); hov_put('`'); + return hn; +} +// a genome name: its signature as a code block, then its doc if the image has one +int hover_blob(int i) { + int ss, ds, k = 0, n = 0; + ss = blobsig(hname); + ds = blobdoc(hname); + hn = 0; + if (ss) { + hov_put('`'); hov_put('`'); hov_put('`'); hov_put('z'); hov_put('c'); hov_put(10); + while (k < len_s(ss)) { hov_put(at_s(ss, k)); k++; } + hov_put(10); hov_put('`'); hov_put('`'); hov_put('`'); + } + if (ds) { + if (ss) { hov_put(10); hov_put(10); } + k = 0; + while (k < len_s(ds)) { hov_put(at_s(ds, k)); k++; } + } + return hn; +} +// does the hovered word equal w +word hname_is(word w) { + int k = 0; + while (w[k] && hname[k] == w[k]) k++; + return !w[k] && !hname[k]; +} +// append a word string, or a compiler type, to the markdown buffer. +int hov_text(word s) { int k = 0; while (s[k]) { hov_put(s[k]); k++; } return 0; } +int hov_type(word t) { + int sm = outmode, sb = outbuf, sn = outn, sc = outcap, need; + outmode = 3; outn = 0; tprint(t); need = hn + outn; + if (need > hovcap) { + while (need > hovcap) hovcap = hovcap * 2; + hov = lsp_grow(hov, hn, hovcap); + } + outmode = 2; outbuf = hov; outn = hn; outcap = hovcap; tprint(t); hn = outn; + outmode = sm; outbuf = sb; outn = sn; outcap = sc; + return 0; +} +// explain composite types without relying on their source spelling. +int hover_type_info(word t) { + int kind, k; + if (!t) return 0; + kind = t[0]; + if (kind == TY_PTR) { + hov_text("\n\na pointer to `"); hov_type(t[4]); hov_text("`. the pointer occupies one word; the pointed-to storage is separate."); + } else if (kind == TY_ARR) { + hov_text("\n\nan array stored in place, with a fixed length of "); + k = t[3]; hov_number(k); hov_text(" elements of type `"); hov_type(t[4]); hov_text("`. storage: "); hov_number(tsz(t)); hov_text(" words."); + } else if (kind == TY_DYN) { + if (t[3]) hov_text("\n\nan array handle with a fixed runtime length. "); + else hov_text("\n\na growable array handle. "); + hov_text("element type: `"); hov_type(t[4]); hov_text("`. `len` gives the current length and `cap` gives capacity."); + if (t[3]) hov_text(" `push`, `pop`, and `resize` cannot change its length."); + } else if (kind == TY_STRUCT) { + hov_text("\n\na struct stored in place. storage: "); hov_number(tsz(t)); hov_text(" words."); + if (t[3] > 0) { + hov_text("\n\nfields:\n\n```zc\n"); + k = 0; + while (k < t[3]) { hov_type(t[4][k * 3 + 1]); hov_put(' '); hov_text(t[4][k * 3]); hov_text(";\n"); k++; } + hov_text("```"); + } + } else if (kind == TY_FN) hov_text("\n\na callable value. the type lists its result and parameter types."); + return 0; +} +// decimal numbers in markdown use the ordinary printer through the capture buffer. +int hov_number(int n) { + int sm = outmode, sb = outbuf, sn = outn, sc = outcap; + while (hn + 24 > hovcap) { hov = lsp_grow(hov, hn, hovcap * 2); hovcap = hovcap * 2; } + outmode = 2; outbuf = hov; outn = hn; outcap = hovcap; put_n(n); hn = outn; + outmode = sm; outbuf = sb; outn = sn; outcap = sc; + return 0; +} +// a parsed type includes its brackets and stars, so those characters hover too. +int hover_type_span(int i) { + int k = 0, best = 0 - 1, width = 0, end; word t; + while (k < ht_n) { + end = ht_end[k]; + while (end > ht_start[k] && (dc(doff[i], end - 1) == 32 || dc(doff[i], end - 1) == 9 || dc(doff[i], end - 1) == 10 || dc(doff[i], end - 1) == 13)) end--; + if (hcursor >= ht_start[k] && hcursor < end && end - ht_start[k] > width) { best = k; width = end - ht_start[k]; } + k++; + } + if (best < 0) return 0; + t = ht_type[best]; + hov_text("```zc\n"); hov_type(t); hov_text("\n```"); hover_type_info(t); + k = kwtext(); + if (k && (hname_is("word") || hname_is("int") || hname_is("char") || hname_is("bool") || hname_is("void") || hname_is("float") || hname_is("f32") || hname_is("f64") || hname_is("byte") || hname_is("u8") || hname_is("i8") || hname_is("u16") || hname_is("i16") || hname_is("u32") || hname_is("i32"))) { + while (*k && *k != '~') k++; + if (*k) { hov_text("\n\n"); hov_text(k + 1); } + } + return 1; +} +// the text of a keyword or a built-in word, from the language guide, or 0. this +// one table holds every keyword the compiler registers and the built-ins that +// have their own form. the form comes first, then a ~, then what it does (the ~ +// separates the two). a name the table does not hold gets the image's docs. +int kwtext() { + if (hname_is("word")) return "word~one 64-bit vm word; the default type for untyped integer values and word-sized storage"; + if (hname_is("int")) return "int~one signed, word-sized integer; arrays of int use one word per element"; + if (hname_is("char")) return "char~one word holding a character code; arrays of char use one word per element"; + if (hname_is("bool")) return "bool~one word for a boolean value; comparison and logical operators produce 0 or 1"; + if (hname_is("void")) return "void~marks a function with no meaningful result; the vm still uses a one-word return convention"; + if (hname_is("float")) return "float~one-word ieee binary64 value; arrays of float use one word per element"; + if (hname_is("f64")) return "f64~alias for float, a one-word ieee binary64 value"; + if (hname_is("f32")) return "f32~binary32 value rounded when stored; arrays and pointers pack two elements per word"; + if (hname_is("u8")) return "u8~unsigned 8-bit integer, range 0..255; values narrow on store and arrays or pointers pack eight elements per word"; + if (hname_is("i8")) return "i8~signed 8-bit integer, range -128..127; values narrow on store and arrays or pointers pack eight elements per word"; + if (hname_is("u16")) return "u16~unsigned 16-bit integer, range 0..65535; values narrow on store and arrays or pointers pack four elements per word"; + if (hname_is("i16")) return "i16~signed 16-bit integer, range -32768..32767; values narrow on store and arrays or pointers pack four elements per word"; + if (hname_is("u32")) return "u32~unsigned 32-bit integer, range 0..4294967295; values narrow on store and arrays or pointers pack two elements per word"; + if (hname_is("i32")) return "i32~signed 32-bit integer, range -2147483648..2147483647; values narrow on store and arrays or pointers pack two elements per word"; + if (hname_is("byte")) return "byte~alias for u8, an unsigned 8-bit integer packed in arrays and pointers"; + if (hname_is("fixed")) return "fixed~marks an array whose length cannot change; fixed arrays cannot grow or shrink"; + if (hname_is("new")) return "new(T) or new(T, n)~returns a fresh object on the heap. bound to a local, the local owns it (tagged m) and the block frees it when it ends, so you don't write free yourself."; + if (hname_is("free")) return "free(d*p)~frees a heap object. the block is kept until the next collection finds nothing pointing at it, and a second free does nothing. free takes an object tagged d, so a checked call is free(d*p)."; + if (hname_is("sizeof")) return "sizeof(e) or sizeof(T)~the size in words, found by compiling the expression dry, so it is not run. a packed array counts (n * bytes + 7) / 8 words."; + if (hname_is("typeof")) return "typeof(e) or typeof(T)~the type record of an expression or a type, found by compiling it dry. tkind, tsize, tname and the other t functions read it at runtime."; + if (hname_is("syscall")) return "syscall(op, a, b, c)~a call to the host through the gate word. the ops are SYS_OPEN, SYS_CLOSE, SYS_READ, SYS_WRITE, SYS_GETENV, SYS_TIME and the rest of the named ones, and SYS_RAW (40) runs any linux syscall on the host. o3 code does not go through the gate, so call syscall from ordinary code."; + if (hname_is("len")) return "len(a)~the number of items in an array, growable or fixed."; + if (hname_is("cap")) return "cap(a)~the number of items a growable array can hold before it has to grow."; + if (hname_is("push")) return "push(a, x)~adds x to the end of a growable array, growing it when needed. a growable array owns its items."; + if (hname_is("pop")) return "pop(a)~removes the last item of a growable array and gives it back."; + if (hname_is("resize")) return "resize(a, n)~sets the length of a growable array to n, growing or shrinking it."; + if (hname_is("o3")) return "o3~marks code for the optimizing tier. it goes on a function, a block or a single variable, and its variables live in registers."; + if (hname_is("fn")) return "fn name(a, b) { }~declares a function whose return is a word. a typed function is written like c, int f(int x) { }."; + if (hname_is("let")) return "let name = e~declares a local whose type comes from its value. a number stays a plain word, and a pointer or a struct keeps its type."; + if (hname_is("const")) return "const [type] NAME = e~a named constant. its value is worked out while compiling, so it can size an array or label a case."; + if (hname_is("else")) return "else~the other branch of an if. after an enhance's body, else names the fallback function that runs where the enhancement can't."; + if (hname_is("as")) return "e as T~changes the type of an expression without changing its bits."; + if (hname_is("unchecked")) return "unchecked fn f() { } / unchecked { } / unchecked;~turns the lifetime analyzer off for a function, a block, or the rest of the unit. the code is not checked, so the analyzer's rules don't apply to it."; + if (hname_is("immediate")) return "immediate fn name() { }~a parse word. it runs during compilation whenever its name shows up as a statement, inside an expression or at the top level."; + if (hname_is("case")) return "case v:~a label in a switch. each case ends where the next one begins, so cases don't fall through."; + if (hname_is("default")) return "default:~the arm of a switch that runs when no case matches."; + if (hname_is("do")) return "do { } while (cond);~a loop that runs its body once before the test."; + if (hname_is("while")) return "while (cond) { }~a loop that tests the condition before each pass."; + if (hname_is("for")) return "for (init; cond; step) { }~a loop with its start, test and step written on one line."; + if (hname_is("if")) return "if (cond) { } else { }~runs one branch or the other, by the condition."; + if (hname_is("switch")) return "switch (e) { case 1, 2: ... default: ... }~a branch on constant labels. the cases don't fall through, and break leaves early."; + if (hname_is("break")) return "break;~leaves the innermost loop or switch."; + if (hname_is("continue")) return "continue;~goes on to the next pass of the innermost loop."; + if (hname_is("return")) return "return e;~leaves the function with a value. a u object leaves with u*x, and the function's return is then owned."; + if (hname_is("enum")) return "enum name { A, B = 5 };~global constants. under typecheck on they are a type of their own, and a member can carry fields to make a constructor."; + if (hname_is("struct")) return "struct name { fields };~a record type. struct name { } makes it generic, one copy for each set of type arguments."; + if (hname_is("type")) return "type name = T;~an alias for a type, such as type row = [3]int; for a pointer to an array."; + if (hname_is("include")) return "include \"path\";~reads a file and compiles it at that point, its signatures first."; + if (hname_is("memdebug")) return "memdebug on; / memdebug off; / memdebug scrub;~header checks, allocation sites and leak reports for the rest of the file. scrub also fills a freed payload with 0xDEAD."; + if (hname_is("typecheck")) return "typecheck on; / typecheck off;~turns on type checks for the code that follows, enums included."; + if (hname_is("docs")) return "docs on; / docs off;~keeps the doc comments above declarations in the image, so the language server can show them on hover."; + if (hname_is("micro")) return "enhance name(args) micro \"recipe\";~the body of an enhancement, written as micro-ops on registers r0 to r15. the inputs arrive in r0 and up, and the outputs leave from r0."; + if (hname_is("vm")) return "enhance name(args) vm { ... };~the body of an enhancement, written as stack machine asm. it becomes one opcode when the core lifts it."; + if (hname_is("enhance")) return "enhance name(args) micro \"...\" or vm { ... };~installs a custom opcode at compile time, with up to 8 inputs and 8 outputs. an enhancement with one output can be called like a function."; + if (hname_is("asm")) return "asm { ... }~a block of stack machine asm, and asm operands can be used inside expressions."; + if (hname_is("quote")) return "quote { tokens }~a token list as a fresh object. a parse word can read it and build code from it."; + if (hname_is("extern")) return "extern type name(params);~declares a c function on the host and calls it through the native call."; + if (hname_is("cfunc")) return "cfunc(f)~a native function pointer that runs the zc function f when c calls it."; + return 0; +} +// the markdown of a keyword: its form as a code block, then what it does +int kw_put(word s) { + int k = 0; + hov_put('`'); hov_put('`'); hov_put('`'); hov_put('z'); hov_put('c'); hov_put(10); + while (s[k] && s[k] != '~') { hov_put(s[k]); k++; } + hov_put(10); hov_put('`'); hov_put('`'); hov_put('`'); + if (s[k]) { + hov_put(10); hov_put(10); k++; + while (s[k]) { hov_put(s[k]); k++; } + } + return hn; +} +// does the line from q declare hname as a method: the name is followed by (, and +// what comes before it is a type, fn or static, and nothing that makes it a call +int hm_decl(int base, int q, int le) { + int po = q, ns, e, k = 0, c; + while (po < le && dc(base, po) != '(') po++; + if (po >= le) return 0; + e = po; + ns = e; + while (ns > q && hid(dc(base, ns - 1))) ns--; + if (ns == e || ns == q) return 0; + while (hname[k] && ns + k < e && dc(base, ns + k) == hname[k]) k++; + if (hname[k] || ns + k != e) return 0; + if (dc(base, ns - 1) != 32 && dc(base, ns - 1) != 9) return 0; + k = q; + while (k < ns) { + c = dc(base, k); + if (c == '=' || c == '.' || c == ';' || c == '(' || c == ',' || c == '{' || c == '}') return 0; + k++; + } + if (hl_kw(base, q)) return 0; + return 1; +} +// the markdown of a method or a static function of a struct: its line as the +// signature, then the comment lines right above it. the methods are indented in +// the struct body, so hover_scan (which reads column 0) does not find them. +// returns 1 when the hovered word is such a name. +int hover_method(int i) { + int base = doff[i], size = dsize[i], ls = 0, le, q, p, e, k, cs, pv, found = 0, sg, se, n; + while (ls < size && !found) { + le = ls; + while (le < size && dc(base, le) != 10) le++; + q = ls; + while (q < le && (dc(base, q) == 32 || dc(base, q) == 9)) q++; + if (q > ls && hm_decl(base, q, le)) { found = 1; sg = q; se = q; } + if (!found) ls = le + 1; + } + if (!found) return 0; + // the signature: up to the body, a ; or the end of the line + se = sg; + while (se < size && dc(base, se) != 10 && dc(base, se) != '{' && dc(base, se) != ';') se++; + while (se > sg && (dc(base, se - 1) == 32 || dc(base, se - 1) == 9)) se--; + hn = 0; + hov_put('`'); hov_put('`'); hov_put('`'); hov_put('z'); hov_put('c'); hov_put(10); + k = sg; + while (k < se) { hov_put(dc(base, k)); k++; } + hov_put(10); hov_put('`'); hov_put('`'); hov_put('`'); + hover_docs(i, sg, sg, 0); + return 1; +} +// the markdown of a local at its declaration: the name's declaration line, with +// the type an untyped let was given. o is the byte offset of the declared name. +int hover_named(int i, int o, int kind) { + int base = doff[i], end = o, head = o, k, entry, untyped; word t = 0; + while (end < dsize[i] && hid(dc(base, end))) end++; + if (end == o) return 0; + untyped = ann_untyped(i, o); htn = 0; + if (untyped) { ensure_annot(i); ann_type(i, o); ensure_checked(i); } + entry = lt_find(o); + if (entry >= 0) t = lt_type[entry]; + k = 0; + while (k < ht_n) { if (ht_end[k] == o && ht_start[k] < head) head = ht_start[k]; k++; } + if (untyped && kind != 1) { + k = o - 1; + while (k >= 0 && (dc(base, k) == 32 || dc(base, k) == 9)) k--; + if (k >= 2 && dc(base, k) == 't' && dc(base, k - 1) == 'e' && dc(base, k - 2) == 'l') head = k - 2; + } + hn = 0; hov_text("```zc\n"); + if (untyped && kind != 1) hov_text("let "); + else if (!untyped && t) { hov_type(t); hov_put(' '); } + k = o; while (k < end) { hov_put(dc(base, k)); k++; } + if (kind == 1) hov_text(" // parameter"); + else if (kind == 3) hov_text(" // global"); + else if (kind == 4) hov_text(" // field"); + else hov_text(" // local"); + if (htn || entry >= 0) { + hov_text("; type "); + if (htn) { k = 0; while (k < htn) { hov_put(htb[k]); k++; } } + else { k = lt_at[entry]; while (k < lt_at[entry] + lt_len[entry]) { hov_put(ltxt[k]); k++; } } + } + hov_text("\n```"); hover_type_info(t); hover_docs(i, head, o, end - o); + if (hoff != o) hover_docs(i, hoff, hoff, len_w(hname)); + htn = 0; + return 1; +} +int hover_at_decl(int i, int o) { return hover_named(i, o, 2); } +// storage facts are copied at the reference's position, before later statements +// can change them. unknown facts stay unknown in the preview. +int hover_storage(int r) { + word p, t = 0; int entry, kind; + if (r < 0 || !hn) return 0; + p = rsrec + r * RS_W; kind = p[6]; + if (!kind) { + if (p[1] == RS_LOCAL || p[1] == RS_PARAM || p[1] == RS_FIELD) { + entry = lt_find(p[2]); if (entry >= 0) t = lt_type[entry]; + } else if (p[1] == RS_SYM && p[3] >= 0) t = stype[p[3]]; + if (!t || (t[0] != TY_PTR && t[0] != TY_ARR && t[0] != TY_DYN)) return 0; + hov_text("\n\nsize: "); + if (t[0] == TY_ARR) { hov_number(t[3]); hov_text(" elements"); } + else hov_text("unknown"); + hov_text(". lifetime: unknown at this position."); return 0; + } + hov_text("\n\nsize: "); + if (p[4] >= 0) { hov_number(p[4]); hov_text(" elements"); } + else hov_text("unknown"); + hov_text(". lifetime: "); + if (p[5] == 2) { hov_text("ended on line "); hov_number(p[8] & 0xFFFFFF); } + else if (p[5] == 1) { + if (kind == 1) hov_text("stack storage; ends when its declaring scope exits"); + else hov_text("heap storage; live here, ends when freed or its owner releases it"); + } else hov_text("unknown after a call or control-flow change"); + if (p[7]) { hov_text(". storage begins on line "); hov_number(p[7] & 0xFFFFFF); } + hov_text("."); return 0; +} +// the hover of a name that is not a local. kind is 1 for a built-in (the guide's +// text, then the image's docs), 2 for a symbol the document declares (its +// declaration, a method, then the image's docs), and 0 when no record says (the +// guide, then the text of the document, as before) +int hover_global(int i, int kind) { + int kt; + if (kind != 2) { kt = kwtext(); if (kt) return kw_put(kt); } + if (kind == 1) { hover_blob(i); return hn; } + if (hover_scan(i)) { hover_fill(i); return hn; } + if (hover_method(i)) return hn; + if (kind == 0 && hover_local(i)) return hn; + hover_blob(i); + return hn; +} +// the hover at the cursor. the compiler's record of the name decides what it +// is: a local is its declaration, a parameter the header that names it, and a +// symbol is looked up as that kind of symbol. a name with no record is found by +// its text, as before. +int do_hover(int i) { + int r, k = 0, v = 0, has_word, s, q, o; + hn = 0; + ensure_checked(i); + has_word = hover_word(i); + if (hover_type_span(i)) { send(gen_hover); return 0; } + if (has_word) { + r = rs_find(hoff); + if (r >= 0) { k = rsrec[r * RS_W + 1]; v = rsrec[r * RS_W + 2]; } + if (k == RS_LOCAL && v >= 0) hover_at_decl(i, v); + else if (k == RS_PARAM && v >= 0) hover_named(i, v, 1); + else if (k == RS_FIELD && v >= 0) hover_named(i, v, 4); + else if (k == RS_LOCAL) hover_local(i); + else if (k == RS_SYM) { + if ((v & 15) == K_GVAR || (v & 15) == K_GARR) { + s = rsrec[r * RS_W + 3]; q = 0; o = 0 - 1; + while (q < rsn) { if (rsrec[q * RS_W + 3] == s && lt_find(rsrec[q * RS_W]) >= 0) { o = rsrec[q * RS_W]; break; } q++; } + if (o >= 0) hover_named(i, o, 3); else if (v & 16) hover_global(i, 2); else hover_global(i, 1); + } else if (v & 16) hover_global(i, 2); else hover_global(i, 1); + } + else hover_global(i, 0); + r = rs_find(hoff); hover_storage(r); + } + send(gen_hover); + return 0; +} + +// ---- dispatch ---- +// the text just read belongs to the document named by the uri +int commit_text() { + int i; + if (!thave || !tuseen) return 0 - 1; + i = dfind(ubuf, un); + if (i < 0) i = docnew(ubuf, un); + if (i < 0) return 0 - 1; + doff[i] = tbase; dsize[i] = tn; dver[i] = jver; dgen[i]++; + return i; +} +// handles one message: the lifecycle (initialize, shutdown, exit), changes to a +// document (which are checked and published), and the requests for semantic +// tokens and hovers. +int handle() { + int i; + if (eq_w(mbuf, "initialize")) { inited = 1; send(gen_init); return 0; } + if (eq_w(mbuf, "exit")) { quit = 1; return 0; } + if (eq_w(mbuf, "shutdown")) { shut = 1; send(gen_null); return 0; } + if (!inited) { if (idtype) reply_error(0 - 32002); return 0; } + if (eq_w(mbuf, "textDocument/didOpen") || eq_w(mbuf, "textDocument/didChange") || eq_w(mbuf, "textDocument/didSave")) { + i = commit_text(); + if (i < 0 && tuseen) i = dfind(ubuf, un); + if (i >= 0) { check_doc(i); publish(i); } + return 0; + } + if (eq_w(mbuf, "textDocument/didClose")) { + i = dfind(ubuf, un); + if (i >= 0) { + dgpos = 0; publish(i); // an empty list clears its markers + dlive[i] = 0; dsize[i] = 0; doff[i] = 0; duri[i] = 0; + chk_i = 0 - 1; + hc_i = 0 - 1; ann_i = 0 - 1; + } + return 0; + } + if (eq_w(mbuf, "textDocument/semanticTokens/full")) { + i = dfind(ubuf, un); + if (i < 0) { send(gen_empty); return 0; } + ensure_checked(i); + gd = i; send(gen_tokens); + return 0; + } + if (eq_w(mbuf, "textDocument/hover")) { + i = dfind(ubuf, un); + if (i < 0) { send(gen_null); return 0; } + do_hover(i); + return 0; + } + if (idtype) reply_error(0 - 32601); + return 0; +} + +// ---- command line ---- +// a source file on stdin instead of a protocol stream: print what the +// compiler found, one line each. with --annotate, print the file with the +// types the compiler can see written in +unchecked int load_stdin(int first) { + int n = 0, c = first, cap = 4096; word nb; int k; + tbase = new(cap); + while (c >= 0) { + if ((n >> 2) + 4 >= cap) { + nb = new(cap * 2); k = 0; + while (k < cap) { nb[k] = tbase[k]; k++; } + tbase = nb; cap = cap * 2; + } + if ((n & 3) == 0) tbase[n >> 2] = 0; + tbase[n >> 2] = tbase[n >> 2] | (c << ((n & 3) * 8)); + n++; + if (eof()) break; + c = get_c() & 255; + } + if (n & 3) { } else tbase[n >> 2] = 0; + tbase[(n >> 2) + 1] = 0; + dlive[0] = 1; doff[0] = tbase; dsize[0] = n; dgen[0] = 1; dulen[0] = 0; + return n; +} +// zclsp given a source file on stdin prints its diagnostics as file:line:column +// lines, one per error or hint, and says so when there are none. +int cli(int first) { + int at = 0, i, o; + load_stdin(first); + check_doc(0); + pc_reset(0); + while (at < dgpos) { + o = dgb[at + 2]; + if (o < 0) { put_s("line "); put_n(dgb[at + 1]); } + else { pc_to(o); put_n(pline + 1); put_c(':'); put_n(pcol + 1); } + if (dgb[at] == 1) put_s(": error: "); else if (dgb[at] == 2) put_s(": warning: "); else put_s(": hint: "); + i = 0; while (i < dgb[at + 4]) { put_c(dgb[at + 5 + i]); i++; } + put_c(10); + at = at + 5 + dgb[at + 4]; + } + if (!dgpos) put_s("no errors\n"); + return 0; +} + +// ---- annotate ---- +// zclsp --annotate < file.zc prints the file with the types the compiler can +// see written in: locals from their initializers, parameters from what every +// call passes them or from being used as an address, returns from what the +// function returns (the type facts of compiler.zc). the typechecker is on for +// it, and every edit has to leave the file with no error it didn't have, so +// what comes out compiles wherever the input did. when a batch of edits +// fails, the batch is cut in halves until the bad edits are found and left +// out. run it again and the types it just wrote let the next round see more. +int aw_start(int p) { awp = p; awn = 0; return 0; } +// appends byte c to the output being built at awp (awn bytes so far). +int aw(int c) { + if ((awn & 3) == 0) awp[awn >> 2] = 0; + awp[awn >> 2] = awp[awn >> 2] | ((c & 255) << ((awn & 3) * 8)); + awn++; + return 0; +} +// ends the output being built: a zero word follows the last byte, so it reads as a text. +int aw_end() { + if ((awn & 3) == 0) awp[awn >> 2] = 0; + awp[(awn >> 2) + 1] = 0; + return 0; +} +// the lines of the errors the last check found +int err_lines(word dst) { + int at = 0, n = 0, need = dgpos / 5 + 1, baseline = dst == abase; + if (need > aerrcap) { + while (need > aerrcap) { if (!aerrcap) aerrcap = 512; else aerrcap = aerrcap * 2; } + abase = lsp_grow(abase, nbase, aerrcap); aerr = lsp_grow(aerr, 0, aerrcap); + } + if (baseline) dst = abase; else dst = aerr; + while (at < dgpos) { + if (dgb[at] == 1) { dst[n] = dgb[at + 1]; n++; } + at = at + 5 + dgb[at + 4]; + } + return n; +} +// is v one of the first n words of set. +int in_set(int v, word set, int n) { + int i = 0; + while (i < n) { if (set[i] == v) return 1; i++; } + return 0; +} +// the facts, in the order of the text +int sort_facts() { + int i = 1, j; word a, b; int t0, t1, t2, t3; + while (i < nifx) { + j = i; + while (j > 0 && ifx[(j - 1) * 4] > ifx[j * 4]) { + a = ifx + (j - 1) * 4; b = ifx + j * 4; + t0 = a[0]; t1 = a[1]; t2 = a[2]; t3 = a[3]; + a[0] = b[0]; a[1] = b[1]; a[2] = b[2]; a[3] = b[3]; + b[0] = t0; b[1] = t1; b[2] = t2; b[3] = t3; + j--; + } + i++; + } + return 0; +} +// the facts the last check collected, kept across the checks that follow +int save_facts() { + int i = 0, nt = 0; word f; int k; + sort_facts(); + if (nifx > afcap) { + while (nifx > afcap) { if (!afcap) afcap = 4096; else afcap = afcap * 2; } + afx = lsp_grow(afx, 0, afcap * 4); amask = lsp_grow(amask, 0, afcap); + } + if (nift > atcap) { + while (nift > atcap) { if (!atcap) atcap = 24000; else atcap = atcap * 2; } + aft = lsp_grow(aft, 0, atcap); + } + anf = 0; + while (i < nifx) { + f = ifx + i * 4; + i++; + afx[anf * 4] = f[0]; afx[anf * 4 + 1] = f[1]; afx[anf * 4 + 2] = nt; afx[anf * 4 + 3] = f[3]; + k = 0; while (k < f[3]) { aft[nt + k] = ift[f[2] + k]; k++; } + nt = nt + f[3]; + anf++; + } + return 0; +} +// the text of document 0 with the facts in the mask applied, into dst. a fact +// that overlaps the one before it is left out. returns how many went in +int apply_masked(int dst) { + int i = 0, o = 0; word f; int k, n = 0, size = dsize[0], base = doff[0], last = 0 - 1; + aw_start(dst); + while (i < anf) { + f = afx + i * 4; + i++; + if (!amask[i - 1] || f[0] < o || f[0] == last || f[0] + f[1] > size) continue; + while (o < f[0]) { aw(dc(base, o)); o++; } + k = 0; while (k < f[3]) { aw(aft[f[2] + k]); k++; } + o = f[0] + f[1]; last = f[0]; + n++; + } + while (o < size) { aw(dc(base, o)); o++; } + aw_end(); + return n; +} +// the block the masked text is written into: room for the document and every +// replacement, so it never runs out +word fresh_half() { + int n = dsize[0], i = 0; + while (i < anf) { n = n + afx[i * 4 + 3]; i++; } + ann_half = new(dwords(n)); + return ann_half; +} +// does the document with the masked facts applied check with no new error? +int verify() { + int ne, i; + apply_masked(ann_half); + dlive[1] = 1; doff[1] = ann_half; dsize[1] = awn; dgen[1] = dgen[1] + 1; dulen[1] = 0; + check_doc(1); + ne = err_lines(aerr); + i = 0; + while (i < ne) { if (!in_set(aerr[i], abase, nbase)) return 0; i++; } + return 1; +} +// take the facts lo .. hi if they fit together, else halve them and try the halves +int try_range(int lo, int hi) { + int mid, k; + if (lo >= hi) return 0; + k = lo; while (k < hi) { amask[k] = 1; k++; } + if (verify()) return 0; + k = lo; while (k < hi) { amask[k] = 0; k++; } + if (hi - lo == 1) return 0; + mid = (lo + hi) / 2; + try_range(lo, mid); + try_range(mid, hi); + return 0; +} +// zclsp --annotate reads a source file on stdin and prints it with the types the +// compiler can see written in. the rounds run the check with the type facts on, +// keep the edits that add no error, and run again on the text they made. +int annotate(int first) { + int round = 0, n, i; + load_stdin(first); + ifstrict = 1; // the typechecker runs, so a type that doesn't fit how a name is used is an error + check_doc(0); + nbase = err_lines(abase); + while (round < AROUNDS) { + ifon = 1; check_doc(0); ifon = 0; + save_facts(); + if (!anf) break; + i = 0; while (i < anf) { amask[i] = 0; i++; } + fresh_half(); + try_range(0, anf); + n = apply_masked(ann_half); + if (!n) break; + doff[0] = ann_half; dsize[0] = awn; dgen[0] = dgen[0] + 1; + round++; + } + ifstrict = 0; + i = 0; + while (i < dsize[0]) { put_c(dc(doff[0], i)); i++; } + return 0; +} +// the first argument: 1 for --annotate, 2 for --types, which lists the edits +// instead of making them +unchecked int cli_mode() { + int n = syscall(SYS_ARGC, 0, 0, 0), len; word s; int r = 0; + if (n < 2) return 0; + len = syscall(SYS_ARG, 1, 0, 0); + if (len != 10 && len != 7) return 0; + s = new(len + 1); + syscall(SYS_ARG, 1, s, len + 1); + if (s[0] == '-' && s[1] == '-' && s[2] == 'a' && s[3] == 'n' && len == 10) r = 1; + if (s[0] == '-' && s[1] == '-' && s[2] == 't' && s[3] == 'y' && len == 7) r = 2; + free(s); + return r; +} +// zclsp --types < file.zc: one line per edit, line:column, how many characters +// it replaces and the text that goes in +int list_types(int first) { + int i = 0; word f; int k; + load_stdin(first); + ifon = 1; check_doc(0); ifon = 0; + sort_facts(); + pc_reset(0); + while (i < nifx) { + f = ifx + i * 4; + pc_to(f[0]); + put_n(pline + 1); put_c(':'); put_n(pcol + 1); put_c(32); put_n(f[1]); put_c(32); + k = 0; while (k < f[3]) { put_c(ift[f[2] + k]); k++; } + put_c(10); + i++; + } + return 0; +} + +// the entry point of zclsp. the input is either a source file (checked, and its +// diagnostics printed), a file to annotate or to list the types of (--annotate, +// --types), or the json-rpc stream of a language server (when it starts with a +// Content-Length header). +int lsp_main() { + int c, k; + docs_on = 0; // the image was made with docs on; the documents checked here are not docs + chk_heap = 1; + dgb = new(DGCAP); sets = new(4 * LSW); + duri = lsp_grow(0, 0, doccap); dulen = lsp_grow(0, 0, doccap); + doff = lsp_grow(0, 0, doccap); dsize = lsp_grow(0, 0, doccap); + dver = lsp_grow(0, 0, doccap); dgen = lsp_grow(0, 0, doccap); dlive = lsp_grow(0, 0, doccap); + mbuf = lsp_grow(0, 0, mcap); idbuf = lsp_grow(0, 0, idcap); jk = lsp_grow(0, 0, jkcap); kbuf = lsp_grow(0, 0, kcap); lbuf = lsp_grow(0, 0, lcap); + ubuf = lsp_grow(0, 0, ucap); hov = lsp_grow(0, 0, hovcap); hname = lsp_grow(0, 0, hnamecap); htb = lsp_grow(0, 0, htbc); + chk_i = 0 - 1; jpk = 0 - 2; rfirst = 0 - 1; + if (eof()) return 0; + c = get_c() & 255; + if (c != 'C' && c != 'c') { + k = cli_mode(); + if (k == 1) annotate(c); else if (k == 2) list_types(c); else cli(c); + return 0; + } + rfirst = c; + while (!quit) { + if (!read_message()) break; + handle(); + } + return 0; +} diff --git a/src/micro/micro.c b/src/micro/micro.c new file mode 100644 index 0000000..4542bd8 --- /dev/null +++ b/src/micro/micro.c @@ -0,0 +1,643 @@ +/* portable microcore. the base machine from core.c — memory, the two stacks, + the 22 opcodes, call/ret, and the genome — without the jit, llvm, o3, or + the enhancement optimizer. + + `/` `%` `<<` and every other custom opcode are installed at runtime + (opcode 21). the genome's own idiv, imod, ishl, lfa and outside arrive + that way, and so do programs that declare enhance. this file lowers a + recipe or a straight-line vm body into a flat list of micro-ops and runs + that list. there is no constant folding and no multiply-high rewrite of + division; the results match, the list is just longer. install kind 2 is + an o3 region: it is accepted and ignored, and the ordinary bytecode of + that region runs instead, which is the same program the full interpreter + would run if the region did not lift. + + no libc in this file. stdint.h is freestanding. 64-bit division is the + compiler's runtime. bss must be zero before micro_run. + + memory size and the map come from memmap.h (the default is 1,048,576 + words). a smaller board compiles this file against the headers src/tools/genmap + writes, and against the genome built for that same map. */ +#include +#ifdef USE_LSP_GENOME +#include "lsp_genome.h" +#elif defined(GENOME_H) +#include GENOME_H +#else +#include "genome.h" +#endif +#include "micro_port.h" +#include "fops.h" +#include "bops.h" +#define NENH 512 +#define XOP0 22 +#define XPOOL 4096 +#define MAXOPS 128 +#define MAXV 256 + +enum { + U_LI, U_MOV, U_ADD, U_SUB, U_MUL, U_SDIV, U_UDIV, U_SREM, U_UREM, + U_AND, U_OR, U_XOR, U_SHL, U_SHR, U_SAR, U_NEG, U_NOT, + U_LT, U_LTU, U_EQ, U_NE, U_MIN, U_MAX, U_MINU, U_MAXU, + U_MULHS, U_MULHU, U_CLZ, U_ABS, U_SEL, U_LD, U_ST, + U_CTZ, U_POPCNT, U_BSWAP, U_ROTL, U_ROTR, U_F2S, U_S2F, U_N +}; +static const char *const uname[U_N] = { + "li", "mov", "add", "sub", "mul", "sdiv", "udiv", "srem", "urem", + "and", "or", "xor", "shl", "shr", "sar", "neg", "not", + "lt", "ltu", "eq", "ne", "min", "max", "minu", "maxu", + "mulhs", "mulhu", "clz", "abs", "sel", "ld", "st", + "ctz", "popcnt", "bswap", "rotl", "rotr", "f2s", "s2f" +}; +static const signed char uarity[U_N] = { + 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 3, 1, 2, + 1, 1, 1, 2, 2, 1, 1 +}; + +typedef struct { + unsigned char op, a, b, c; + unsigned short d; + Cell imm; +} Xop; +typedef struct { + unsigned char live, nin, nout; + unsigned short n, at, nv; + unsigned short out[8]; +} Enh; + +/* the machine: m is mw words, and grows on demand through the port (micro_mem_alloc). + every access goes through m and mw, which the port's memory may move under. */ +static Cell *m; +static UCell mw; +static UCell memory_initial = MICRO_MW_INIT, memory_limit = MICRO_MW_LIMIT; +int micro_memory_config(UCell initial, UCell limit) +{ + if (m || initial < MICRO_MW_INIT || limit < initial || limit > 0x7fffffff) return 0; + memory_initial = initial; memory_limit = limit; return 1; +} +static Xop xops[XPOOL]; +static int xuse; +static Enh enh[NENH]; +static Xop build[MAXOPS]; +static int bn, bnv, berr; +static Cell xv[MAXV]; +static int jflag; +static int jknown; /* 0 until something asks for the end-of-input flag: the probe reads the port, so it waits for the first ask */ + +UCell micro_mem_words(void) { return mw; } +static int inmem(Cell a) { return (UCell)a < mw; } +static Cell ld(Cell a) { return inmem(a) ? m[a] : 0; } +static Cell b_load(Cell addr, int n) +{ + if (!bop_ok(addr, n, mw)) return 0; + return bop_load(m, addr, n); +} +static void b_store(Cell addr, Cell val, int n) +{ + Cell out[2], w0; + int nw, i; + if (!bop_ok(addr, n, mw)) return; + nw = bop_merge(m, addr, val, n, out, &w0); + for (i = 0; i < nw; i++) if (inmem(w0 + i)) m[w0 + i] = out[i]; +} + +/* the run's sentinel pcs: each names why a run stopped that is not a halt. they + are negative, so no address in memory is one of them. */ +#define PC_DATA_OVERFLOW (-3) +#define PC_RETURN_OVERFLOW (-4) +#define PC_PAST_END (-5) +#define PC_BAD_OP (-6) +#define PC_HALT (-7) /* the zero word: the program's halt, not an error */ +static int bad_op; + +/* grows the machine to at least words words, through the port. the contents stay, + and the new words are zero. 1 on success, 0 when the port has no more memory. */ +static int mem_grow_words(UCell words) +{ + Cell *n; + if (words <= mw && m) return 1; + if (words > memory_limit) return 0; + n = micro_mem_alloc(words); + if (!n) return 0; + m = n; + mw = words; + return 1; +} + +/* the heap asks for more memory (syscall 9, a = 4, b = words). answers the new + size in words, or -1 when the port cannot give it */ +static Cell mem_grow_request(Cell words) +{ + if (words < 0) return -1; + if (!mem_grow_words((UCell)words)) return -1; + return (Cell)mw; +} +static int stack_memory_grow(UCell words) { return mem_grow_words(words); } +#include "stack.h" + +static Cell mulhu(UCell a, UCell b) +{ + UCell a0 = a & 0xffffffffull, a1 = a >> 32, b0 = b & 0xffffffffull, b1 = b >> 32; + UCell p0 = a0 * b0, p1 = a1 * b0, p2 = a0 * b1, p3 = a1 * b1; + UCell mid = (p0 >> 32) + (p1 & 0xffffffffull) + (p2 & 0xffffffffull); + return (Cell)(p3 + (p1 >> 32) + (p2 >> 32) + (mid >> 32)); +} +static Cell mulhs(Cell a, Cell b) +{ + Cell hi = mulhu((UCell)a, (UCell)b); + if (a < 0) hi -= b; + if (b < 0) hi -= a; + return hi; +} +static Cell sar(Cell v, int n) +{ + if (n <= 0) return v; + if (v < 0) return (Cell)(((UCell)v >> n) | (~(UCell)0 << (64 - n))); + return (Cell)((UCell)v >> n); +} +static Cell ev(int op, Cell a, Cell b, Cell c) +{ + UCell ua = (UCell)a, ub = (UCell)b; + int sh = (int)(b & 63); + switch (op) { + case U_MOV: return a; + case U_ADD: return (Cell)(ua + ub); + case U_SUB: return (Cell)(ua - ub); + case U_MUL: return (Cell)(ua * ub); + case U_SDIV: return b == 0 ? 0 : (a == (Cell)((UCell)1 << 63) && b == -1) ? a : a / b; + case U_UDIV: return b == 0 ? 0 : (Cell)(ua / ub); + case U_SREM: return b == 0 ? a : (a == (Cell)((UCell)1 << 63) && b == -1) ? 0 : a % b; + case U_UREM: return b == 0 ? a : (Cell)(ua % ub); + case U_AND: return a & b; + case U_OR: return a | b; + case U_XOR: return a ^ b; + case U_SHL: return (Cell)(ua << sh); + case U_SHR: return (Cell)(ua >> sh); + case U_SAR: return sar(a, sh); + case U_NEG: return (Cell)(0ull - ua); + case U_NOT: return ~a; + case U_LT: return a < b; + case U_LTU: return ua < ub; + case U_EQ: return a == b; + case U_NE: return a != b; + case U_MIN: return a < b ? a : b; + case U_MAX: return a > b ? a : b; + case U_MINU: return ua < ub ? a : b; + case U_MAXU: return ua > ub ? a : b; + case U_MULHS: return mulhs(a, b); + case U_MULHU: return mulhu(ua, ub); + case U_CLZ: { + int n = 0; + if (!ua) return 64; + while (!(ua & ((UCell)1 << 63))) { ua <<= 1; n++; } + return n; + } + case U_ABS: return a < 0 ? (Cell)(0ull - ua) : a; + case U_CTZ: { int n = 0; if (!ua) return 64; while (!(ua & 1)) { ua >>= 1; n++; } return n; } + case U_POPCNT: { int n = 0; while (ua) { ua &= ua - 1; n++; } return n; } + case U_BSWAP: { UCell r = 0; int i; for (i = 0; i < 8; i++) { r = (r << 8) | (ua & 0xff); ua >>= 8; } return (Cell)r; } + case U_ROTL: return (Cell)((ua << sh) | (ua >> ((64 - sh) & 63))); + case U_ROTR: return (Cell)((ua >> sh) | (ua << ((64 - sh) & 63))); + case U_F2S: return (Cell)fop_f2s((uint64_t)a); + case U_S2F: return (Cell)fop_s2f((uint64_t)a); + case U_SEL: return a ? b : c; + } + return 0; +} + +static int vnew(int op, int a, int b, int c, Cell imm) +{ + Xop *u; + if (berr) return 0; + if (bn >= MAXOPS || bnv >= MAXV) { berr = -5; return 0; } + u = &build[bn++]; + u->op = (unsigned char)op; + u->a = (unsigned char)a; + u->b = (unsigned char)b; + u->c = (unsigned char)c; + u->d = (unsigned short)bnv; + u->imm = imm; + return bnv++; +} +static int seq(const char *a, const char *b) +{ + while (*a && *a == *b) { a++; b++; } + return *a == *b; +} +static int findop(const char *s) +{ + int i; + for (i = 0; i < U_N; i++) if (seq(uname[i], s)) return i; + return -1; +} +static int parse_num(const char *t, Cell *o) +{ + int sign = 1, base = 10, d; + UCell n = 0; + if (*t == '-') { sign = -1; t++; } + else if (*t == '+') t++; + if (*t == '0' && (t[1] == 'x' || t[1] == 'X')) { base = 16; t += 2; } + else if (*t == '0' && t[1]) base = 8; + if (!*t) return -1; + for (; *t; t++) { + if (*t >= '0' && *t <= '9') d = *t - '0'; + else if (*t >= 'a' && *t <= 'f') d = *t - 'a' + 10; + else if (*t >= 'A' && *t <= 'F') d = *t - 'A' + 10; + else return -1; + if (d >= base) return -1; + n = n * (UCell)base + (UCell)d; + } + *o = sign < 0 ? (Cell)(0ull - n) : (Cell)n; + return 0; +} +static int regn(const char *t) +{ + int r = 0; + if (t[0] != 'r' || t[1] < '0' || t[1] > '9') return -1; + t++; + while (*t >= '0' && *t <= '9') { + r = r * 10 + *t++ - '0'; + if (r > 15) return -2; + } + return r; +} +static int named(Cell names, int nin, const char *t) +{ + int i, j; + Cell s; + for (i = 0; i < nin && names; i++) { + s = ld(names + i); + j = 0; + while (ld(s + j) && ld(s + j) == (unsigned char)t[j]) j++; + if (!ld(s + j) && !t[j]) return i; + } + return -1; +} +static int rget(int *cur, int r) +{ + if (cur[r] < 0) cur[r] = vnew(U_LI, 0, 0, 0, 0); + return cur[r]; +} + +static int parse_recipe(Cell src, Cell names, int nin, int nout, int *out) +{ + int cur[16], i, n, k, op, args[3], na, dst, isst, nt, rr, v; + char tok[8][32]; + Cell p = src, x; + for (i = 0; i < 16; i++) cur[i] = i < nin ? i : -1; + bn = 0; bnv = nin; berr = 0; + for (;;) { + nt = 0; + for (;;) { + int c = (int)ld(p); + while (c == ' ' || c == '\t' || c == '\r') c = (int)ld(++p); + if (!c || c == '\n' || c == ';') break; + if (c == '=') { + if (nt < 8) { tok[nt][0] = '='; tok[nt][1] = 0; nt++; } + p++; + continue; + } + n = 0; + while (c && c != ' ' && c != '\t' && c != '\n' && c != ';' && c != '=' && c != '\r') { + if (n < 31) tok[nt < 8 ? nt : 7][n++] = (char)c; + c = (int)ld(++p); + } + if (nt < 8) tok[nt++][n] = 0; + } + if (nt) { + isst = seq(tok[0], "st"); + if (!isst && (nt < 3 || !seq(tok[1], "="))) return -2; + dst = -1; + if (!isst) { + rr = regn(tok[0]); + if (rr == -2) return -2; + dst = rr >= 0 ? rr : named(names, nin, tok[0]); + if (dst < 0 || dst > 15) return -2; + } + k = isst ? 1 : 2; + op = -1; + if (!isst && nt > 3) { + op = findop(tok[k]); + if (op < 0) return -2; + k++; + } + if (isst) op = U_ST; + na = 0; + for (; k < nt; k++) { + char *t = tok[k]; + v = -1; + if (na >= 3) return -2; + rr = regn(t); + if (rr == -2) return -2; + if (rr >= 0) v = rget(cur, rr); + else if ((t[0] >= '0' && t[0] <= '9') || t[0] == '-' || t[0] == '+') { + if (parse_num(t, &x)) return -2; + v = vnew(U_LI, 0, 0, 0, x); + } else { + i = named(names, nin, t); + if (i < 0) return -2; + v = rget(cur, i); + } + if (berr) return berr; + args[na++] = v; + } + if (op < 0) { + if (na != 1) return -2; + cur[dst] = args[0]; + } else { + if (na != (op == U_LI ? 0 : uarity[op])) { + if (!(op == U_LI && na == 1)) return -2; + } + if (op == U_LI) cur[dst] = na ? args[0] : vnew(U_LI, 0, 0, 0, 0); + else if (op == U_MOV) { if (na != 1) return -2; cur[dst] = args[0]; } + else if (op == U_ST) vnew(U_ST, args[0], args[1], 0, 0); + else cur[dst] = vnew(op, args[0], na > 1 ? args[1] : 0, na > 2 ? args[2] : 0, 0); + if (berr) return berr; + } + } + if (!ld(p)) break; + p++; + } + for (i = 0; i < nout; i++) { + if (cur[i] < 0) cur[i] = vnew(U_LI, 0, 0, 0, 0); + out[i] = cur[i]; + } + return berr; +} + +static int lower_vm(Cell a, Cell len, int nin, int nout, int *out) +{ + static const int map[] = { U_ADD, U_SUB, U_MUL, U_AND, U_OR, U_XOR, U_SAR, U_LT }; + int st[256], sp = 0, i, k, x, y, j; + Cell w; + if ((UCell)len > 100000) return -5; + bn = 0; bnv = nin; berr = 0; + for (i = 0; i < nin; i++) st[sp++] = i; + for (i = 0; i < (int)len; i++) { + w = ld(a + i); + if (w & 1) { + if (sp >= 256) return -4; + st[sp++] = vnew(U_LI, 0, 0, 0, w >> 1); + if (berr) return berr; + continue; + } + k = (int)(w >> 1); + if (k == 9) break; + switch (k) { + case 1: + if (sp < 1) return -4; + st[sp - 1] = vnew(U_LD, st[sp - 1], 0, 0, 0); + break; + case 2: + if (sp < 2) return -4; + vnew(U_ST, st[sp - 1], st[sp - 2], 0, 0); + sp -= 2; + break; + case 3: + if (sp < 1) return -4; + x = st[sp - 1]; + if (x < nin) return -4; + for (j = 0; j < bn && build[j].d != (unsigned)x; j++); + if (j == bn || build[j].op != U_LI) return -4; + y = (int)build[j].imm; + if (y < 0 || y >= sp - 1) return -4; + st[sp - 1] = st[sp - 2 - y]; + break; + case 4: + if (sp < 1) return -4; + sp--; + break; + case 5: + if (sp < 2) return -4; + x = st[sp - 1]; st[sp - 1] = st[sp - 2]; st[sp - 2] = x; + break; + case 13: case 14: case 15: case 16: case 17: case 18: case 19: case 20: + if (sp < 2) return -4; + x = st[sp - 1]; y = st[sp - 2]; sp -= 2; + st[sp++] = vnew(map[k - 13], y, x, 0, 0); + break; + default: + return -4; + } + if (berr) return berr; + } + if (sp != nout) return -4; + for (i = 0; i < nout; i++) out[i] = st[i]; + return berr; +} + +static int commit(int slot, int nin, int nout, int *out) +{ + Enh *e = &enh[slot]; + int base = xuse, i; + if (berr) return berr; + if (e->live && (int)e->at + (int)e->n == xuse) base = e->at; + if (base + bn > XPOOL) return -5; + if (e->live && (int)e->at + (int)e->n == xuse) xuse = e->at; + e->at = (unsigned short)xuse; + e->n = (unsigned short)bn; + e->nv = (unsigned short)bnv; + e->nin = (unsigned char)nin; + e->nout = (unsigned char)nout; + for (i = 0; i < nout; i++) e->out[i] = (unsigned short)out[i]; + for (i = 0; i < bn; i++) xops[xuse++] = build[i]; + e->live = 1; + return 0; +} + +static int install(Cell d) +{ + Cell slot, kind, nin, nout, pay, len, names; + int out[8], r; + if (!inmem(d) || !inmem(d + 6)) return -1; + slot = m[d]; kind = m[d + 1]; nin = m[d + 2]; nout = m[d + 3]; + pay = m[d + 4]; len = m[d + 5]; names = m[d + 6]; + if (kind == 2) return 0; + if ((UCell)slot >= NENH || (UCell)nin > 8 || (UCell)nout > 8 || (kind != 0 && kind != 1)) return -1; + r = kind == 0 ? parse_recipe(pay, names, (int)nin, (int)nout, out) + : lower_vm(pay, len, (int)nin, (int)nout, out); + if (r) return r; + return commit((int)slot, (int)nin, (int)nout, out); +} + +static int xrun(Enh *e, int s) +{ + int i, j; + Xop *u; + for (i = 0; i < e->nv && i < MAXV; i++) xv[i] = 0; + for (j = 0; j < e->nin; j++) xv[j] = m[s + e->nin - 1 - j]; + for (i = 0; i < e->n; i++) { + u = &xops[e->at + i]; + if (u->op == U_LI) xv[u->d] = u->imm; + else if (u->op == U_LD) xv[u->d] = ld(xv[u->a]); + else if (u->op == U_ST) { if (inmem(xv[u->a])) m[xv[u->a]] = xv[u->b]; } + else xv[u->d] = ev(u->op, xv[u->a], xv[u->b], xv[u->c]); + } + s = s + e->nin - e->nout; + for (j = 0; j < e->nout; j++) m[s + e->nout - 1 - j] = xv[e->out[j]]; + return s; +} + +/* a run suspends when the port has no input yet but more may come (streaming, + see micro_port.h): micro_getc returns MICRO_WAIT, the getc is run again on + micro_resume, and the stacks wait where they are */ +static int sus_p, sus_s, sus_r; + +static int run_from(int p, int s, int r) +{ + int op; + Cell w, a, b; + while ((UCell)p < mw) { + if (s < stack_data_limit() || r > stack_return_limit()) { + Cell sp = s, rp = r; int ret = r > stack_return_limit(); + if (!stack_control || stack_expand(ret, stack_get(ret ? SC_RETURN_TOP : SC_DATA_TOP) - stack_get(ret ? SC_RETURN_LOW : SC_DATA_LOW) + 1, &sp, &rp) < 0) { + p = ret ? PC_RETURN_OVERFLOW : PC_DATA_OVERFLOW; break; + } + s = (int)sp; r = (int)rp; + } + w = m[p++]; + if (w & 1) { m[--s] = w >> 1; continue; } + a = m[s]; b = m[s + 1]; + switch ((int)(w >> 1)) { + case 1: m[s] = ld(a); break; + case 2: + if (a == SYSGATE) { + /* a call can grow the memory and move m, so the result is stored after the call */ + Cell result; + if (b == 9 && m[a + 1] == 6) result = stack_register(m[a + 2]) ? 0 : -1; + else if (b == 9 && (m[a + 1] == 7 || m[a + 1] == 8)) { + Cell sp = s + 2, rp = r; + result = stack_expand(m[a + 1] == 8, m[a + 2], &sp, &rp); + s = (int)sp; r = (int)rp; m[a] = result; break; + } + else if (b == 9 && m[a + 1] == 9) result = m[a + 2] > 0 && (UCell)m[a + 2] <= mw ? (Cell)mw : -1; + else if (b == 9 && m[a + 1] == 4) result = mem_grow_request(m[a + 2]); + else if (b == 9 && m[a + 1] == 5) { + Cell limit = m[a + 2]; + if (limit < 0 || (UCell)limit < mw || limit > 0x7fffffff) result = -1; + else { memory_limit = (UCell)limit; result = limit; } + } else result = micro_syscall(m, b, m[a + 1], m[a + 2], m[a + 3]); + m[a] = result; + s += 2; + break; + } + if (inmem(a)) m[a] = b; + s += 2; + break; + case 3: m[s] = ld((Cell)s + 1 + a); break; + case 4: s++; break; + case 5: m[s] = b; m[s + 1] = a; break; + case 6: m[r++] = p; p = (int)a; s++; break; + case 7: p = (int)a; s++; break; + case 8: if (!b) p = (int)a; s += 2; break; + case 9: p = (int)m[--r]; break; + case 10: { + int ch = micro_getc(); + if (ch == MICRO_WAIT) { sus_p = p - 1; sus_s = s; sus_r = r; return MICRO_SUSPENDED; } + m[--s] = (Cell)(signed char)ch; + jflag = micro_eof(); + jknown = 1; + break; + } + case 11: + if (!jknown) { jflag = micro_eof(); jknown = 1; } + m[--s] = jflag; + break; + case 12: micro_putc((int)a); s++; break; + case 13: m[++s] = b + a; break; + case 14: m[++s] = b - a; break; + case 15: m[++s] = b * a; break; + case 16: m[++s] = b & a; break; + case 17: m[++s] = b | a; break; + case 18: m[++s] = b ^ a; break; + case 19: m[++s] = sar(b, (int)(a & 63)); break; + case 20: m[++s] = b < a; break; + case 21: m[s] = install(a); break; + case 600: + if ((UCell)p >= mw) { p = PC_PAST_END; break; } + m[--s] = m[p++]; + break; + case 601: m[++s] = (Cell)fop_add((uint64_t)b, (uint64_t)a); break; + case 602: m[++s] = (Cell)fop_sub((uint64_t)b, (uint64_t)a); break; + case 603: m[++s] = (Cell)fop_mul((uint64_t)b, (uint64_t)a); break; + case 604: m[++s] = (Cell)fop_div((uint64_t)b, (uint64_t)a); break; + case 605: m[++s] = fop_eq((uint64_t)b, (uint64_t)a); break; + case 606: m[++s] = fop_lt((uint64_t)b, (uint64_t)a); break; + case 607: m[++s] = fop_le((uint64_t)b, (uint64_t)a); break; + case 608: m[s] = (Cell)fop_i2f(a); break; + case 609: m[s] = fop_f2i((uint64_t)a); break; + case 610: { + char buf[400]; int n = fop_format((uint64_t)a, buf), ci; + for (ci = 0; ci < n; ci++) micro_putc(buf[ci]); + s++; + break; + } + case 618: { + char buf[400]; + int n = fop_format_mode((uint64_t)m[s + 2], a, b, buf), ci; + for (ci = 0; ci < n; ci++) micro_putc(buf[ci]); + s += 3; + break; + } + case 611: + m[s] = inmem(a) ? (Cell)fop_parse_words(m + a, mw - (UCell)a) : 0; + break; + case 612: m[s] = b_load(a, 1); break; + case 613: b_store(a, b, 1); s += 2; break; + case 614: m[s] = b_load(a, 2); break; + case 615: b_store(a, b, 2); s += 2; break; + case 616: m[s] = b_load(a, 4); break; + case 617: b_store(a, b, 4); s += 2; break; + case 619: { Cell t = s; m[--s] = t; break; } + case 620: { /* a native call: there is no native code here, so pop the arguments and answer -1 */ + int n = 0; + Cell sig = (UCell)a < mw - 2 ? m[a + 1] : 0; + while (n < 15 && ((sig >> (4 * (n + 1))) & 15)) n++; + s += n; + m[s] = -1; + break; } + default: + op = (int)(w >> 1); + if (op >= XOP0 && op < XOP0 + NENH && enh[op - XOP0].live) s = xrun(&enh[op - XOP0], s); + else if (op == 0) p = PC_HALT; + else { bad_op = op; p = PC_BAD_OP; } + } + } + if (p == PC_DATA_OVERFLOW) { micro_msg("zy: data stack overflow\n"); return 2; } + if (p == PC_RETURN_OVERFLOW) { micro_msg("zy: return stack overflow\n"); return 2; } + if (p == PC_PAST_END) { micro_msg("zy: the program counter is past the end of memory\n"); return 2; } + if (p == PC_BAD_OP) { + char msg[96]; int n = 0, k, neg = bad_op < 0, v = bad_op < 0 ? -bad_op : bad_op; + const char *head = "zy: unknown opcode "; + while (head[n]) { msg[n] = head[n]; n++; } + if (neg) msg[n++] = '-'; + { char dg[12]; int nd = 0; do { dg[nd++] = (char)('0' + v % 10); v /= 10; } while (v); for (k = nd; k > 0; k--) msg[n++] = dg[k - 1]; } + msg[n++] = '\n'; msg[n] = 0; + micro_msg(msg); + return 2; + } + return 0; +} + +static int run(int p) { return run_from(p, DS0, RS0); } + +/* goes on from a run that suspended for input (MICRO_SUSPENDED), once the port + has input for it. the result is as micro_run's */ +int micro_resume(void) { return run_from(sus_p, sus_s, sus_r); } + +int micro_run(void) +{ + int i; + if (!mem_grow_words(memory_initial)) { + micro_msg("zy: no memory for the machine\n"); + return 1; + } + if ((UCell)GORG + (UCell)GLEN > mw || (UCell)GORG + (UCell)GLEN > (UCell)FBOT) { + micro_msg("zy: genome does not fit\n"); + return 1; + } + for (i = 0; i < GLEN; i++) m[GORG + i] = genome[i]; + jknown = 0; + return run(GORG); +} diff --git a/src/micro/micro_port.h b/src/micro/micro_port.h new file mode 100644 index 0000000..9f77987 --- /dev/null +++ b/src/micro/micro_port.h @@ -0,0 +1,89 @@ +/* host port for the microcore. + micro.c calls only these. a bare-metal port is the five functions below: + the uart replaces getc/putc, and every syscall returns -1. port_stub.c is + the empty shape the freestanding compile check links. src/baremetal/port.c is + the one that boots: a 16550 on riscv virt, a PL011 on aarch64 virt. + port_linux.c is the hosted one the test suite runs. + + the core does not use stdio, malloc, or the string routines. it does need + a 64-bit C compiler (division is the compiler's own runtime, not libc) and + a zeroed bss before micro_run. memory starts at MICRO_MW_INIT words of 64 + bits and grows through micro_mem_alloc when the heap asks for more. */ +#ifndef MICRO_PORT_H +#define MICRO_PORT_H +#include +#include "memmap.h" + +typedef int64_t Cell; +typedef uint64_t UCell; +#ifndef MICRO_MW_LIMIT +#define MICRO_MW_LIMIT 0x7fffffff +#endif +/* configure initial words and the growth limit before micro_run. */ +int micro_memory_config(UCell initial, UCell limit); +/* the machine starts at this many words and grows on demand through micro_mem_alloc. + the map's initial heap (HEAP_HI) on a growing map, the whole map (MW) otherwise. + there is no fixed ceiling: a run that needs more memory asks the port for it. */ +#ifndef MICRO_MW_INIT +#if HEAP_GROW +#define MICRO_MW_INIT HEAP_HI +#else +#define MICRO_MW_INIT MW +#endif +#endif + +/* the machine's memory, in words, from the port. micro_mem_alloc(words) returns a + zeroed base with at least words words that stays valid until the next call; the + port keeps what it already holds, so a grow keeps the contents. NULL when the port + has no more memory. micro_mem_words is the size micro.c has mapped now. */ +Cell *micro_mem_alloc(UCell words); +UCell micro_mem_words(void); + +/* next input byte, 0..255, or -1 at end of input. does not report whether + another byte follows; micro_eof does that, and the core asks after each read. */ +int micro_getc(void); + +/* 1 when the next micro_getc would return -1, 0 when a byte is waiting. + must not consume the byte. called once before the first instruction. */ +int micro_eof(void); + +/* streaming input: micro_getc returns MICRO_WAIT when no byte is waiting yet but + more may come (micro_eof then says 0). the run then returns MICRO_SUSPENDED, + and the port calls micro_resume once it has bytes. the browser and the + extension use this for the language server; a port with blocking input never + needs it. micro_run and micro_resume return what run returns, MICRO_SUSPENDED + included. */ +#define MICRO_WAIT (-2) +#define MICRO_SUSPENDED 3 +int micro_run(void); +int micro_resume(void); + +/* write the low 8 bits. */ +void micro_putc(int c); + +/* a store of op to SYSGATE (ZFP+2; 0xA0002 on the default map). a, b, c are the three words after the gate. + mem is the whole machine. return the result, or -1 if op is unsupported. + 1 open(path, 0 read | 1 create/truncate) -> fd + 2 close(fd) + 3 read(fd, buf, n) 4 write(fd, buf, n) one byte per word + 5 getenv(name, buf, cap) -> length, or -1 + 6 time. unix seconds on a hosted port; a hardware counter on bare metal + 7 exit(status). does not return + 8 a random word, never -1 when the port has a source + 9 memory: a = 0 words now, 1 HEAP_LO, 2 HEAP_HI, 3 bytes now, 4 grow to b words + 10 bopen(byte_addr, mode) 11 bread(fd, byte_addr, n) + 12 bwrite(fd, byte_addr, n) 13 bgetenv(name_byte_addr, dst_byte_addr, cap) + ops 1..5 and the names they take are one character per word, 0 terminated. + ops 10..13 are packed bytes, 0 terminated, the same layout as a b"..." + payload. the bare-metal port keeps files in RAM and answers + getenv("BOARD"). the stub returns -1. */ +Cell micro_syscall(Cell *mem, Cell op, Cell a, Cell b, Cell c); + +/* a short diagnostic, usually the stack-overflow lines. not program output. + may be a no-op. */ +void micro_msg(const char *s); + +/* copy the linked genome in and run from its entry. 0 on halt, 2 on a + stack overflow (after a message). */ +int micro_run(void); +#endif diff --git a/src/micro/port_linux.c b/src/micro/port_linux.c new file mode 100644 index 0000000..e6c8847 --- /dev/null +++ b/src/micro/port_linux.c @@ -0,0 +1,284 @@ +/* Linux port: stdin/stdout a byte at a time, the syscalls, diagnostics + on stderr. same behaviour as io_linux.c plus core.c's hostcall. + 1..6 are files, getenv and unix time. 7 exits the process, 8 is a + counter, 9 reports the machine size. */ +#include +#include +#include +#ifdef _WIN32 +#include +#include +#ifndef O_BINARY +#define O_BINARY _O_BINARY +#endif +#if defined(_MSC_VER) +#define open _open +#define close _close +#define read _read +#define write _write +#endif +#else +#include +#ifndef O_BINARY +#define O_BINARY 0 +#endif +#endif +#include +#include +#include +#include "micro_port.h" +#include "bops.h" + +/* the machine's memory: a block that grows by realloc, zero in the new part. the + base can move on a grow, which micro.c takes into account (it re-reads m). */ +static Cell *lmem; +static UCell lcap; + +Cell *micro_mem_alloc(UCell words) +{ + Cell *n; + if (words <= lcap && lmem) return lmem; + if (words > ((UCell)-1) / sizeof(Cell)) return NULL; + n = realloc(lmem, words * sizeof(Cell)); + if (!n) return NULL; + memset(n + lcap, 0, (words - lcap) * sizeof(Cell)); + lmem = n; + lcap = words; + return n; +} + +int micro_getc(void) +{ + int c = getchar(); + return c == EOF ? -1 : c; +} + +int micro_eof(void) +{ + int c = getchar(); + if (c == EOF) return 1; + ungetc(c, stdin); + return 0; +} + +void micro_putc(int c) { putchar(c & 255); } + +void micro_msg(const char *s) { fputs(s, stderr); } + +static int zcstr(Cell *mem, Cell addr, char *dst, int max) +{ + int i = 0, c; + if (max < 1) return -1; + if ((UCell)addr >= (UCell)micro_mem_words()) { dst[0] = 0; return -1; } + while (i < max - 1 && (UCell)(addr + i) < (UCell)micro_mem_words()) { + c = (int)mem[addr + i]; + if (!c) break; + dst[i++] = (char)c; + } + dst[i] = 0; + return i; +} + +static int inmem(Cell a) { return (UCell)a < (UCell)micro_mem_words(); } + +/* the command line and the environment, kept for ops 21 to 24: the same + contract as src/core/core.c. a program read from stdin has no file name, so + argv[0] is . */ +#ifndef _WIN32 +extern char **environ; +#endif +static int zy_argc; +static char **zy_argv; +static int zy_envc; +static char **zy_envp; + +static void zy_args(int argc, char **argv) +{ + char **e; + int n = 0; +#ifdef _WIN32 + e = _environ; +#else + e = environ; +#endif + while (e && e[n]) n++; + zy_envp = e; zy_envc = n; + zy_argv = argv; + zy_argc = argc > 0 ? argc : 1; +} + +/* copy entry a of a char ** into vm words, one character per word, terminated + like SYS_GETENV. a zero buffer asks for the length only. lead, when given, + replaces entry zero. */ +static Cell vecstr(Cell *mem, char **v, int count, Cell a, Cell b, Cell c, const char *lead) +{ + char *e; + int i = 0; + if (a < 0 || a >= (Cell)count) return -1; + e = (a == 0 && lead) ? (char *)lead : v[a]; + if (!e) return -1; + if (!b) { while (e[i]) i++; return i; } + if (c < 1 || !inmem(b)) return -1; + while (e[i] && i < c - 1 && inmem(b + i)) { mem[b + i] = (unsigned char)e[i]; i++; } + if (inmem(b + i)) mem[b + i] = 0; + return i; +} + +static Cell host_rand(void) +{ + static uint64_t s; + if (!s) s = (uint64_t)time(0) ^ 0x9E3779B97F4A7C15ull; + if (!s) s = 0x9E3779B97F4A7C15ull; + s ^= s << 13; s ^= s >> 7; s ^= s << 17; + if ((Cell)s == -1) s ^= 0x100; + return (Cell)s; +} + +static int memory_words(const char *name, UCell fallback, UCell *words) +{ + const char *s = getenv(name); + char *end; + unsigned long long value; + if (!s) { *words = fallback; return 1; } + errno = 0; + value = strtoull(s, &end, 10); + if (errno || end == s || *end || !value || value > 0x7fffffffULL) return 0; + *words = (UCell)value; + return 1; +} + +Cell micro_syscall(Cell *mem, Cell op, Cell a, Cell b, Cell c) +{ + char path[1024], tmp[512]; + int n, i, got; + switch (op) { + case 1: + if (zcstr(mem, a, path, 1024) < 0) return -1; + if (b == 1) return open(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644); + return open(path, O_RDONLY | O_BINARY); + case 2: + return close((int)a); + case 3: + if (c < 0 || !inmem(b)) return -1; + n = 0; + while (n < c) { + i = (int)(c - n); + if (i > 512) i = 512; + if (!inmem(b + n)) break; + if (!inmem(b + n + i - 1)) i = (int)(micro_mem_words() - (b + n)); + if (i <= 0) break; + got = (int)read((int)a, tmp, (size_t)i); + if (got <= 0) break; + for (i = 0; i < got; i++) mem[b + n + i] = (unsigned char)tmp[i]; + n += got; + } + return n; + case 4: + if (c < 0 || !inmem(b)) return -1; + n = 0; + while (n < c) { + i = (int)(c - n); + if (i > 512) i = 512; + if (!inmem(b + n)) break; + if (!inmem(b + n + i - 1)) i = (int)(micro_mem_words() - (b + n)); + if (i <= 0) break; + for (got = 0; got < i; got++) tmp[got] = (char)mem[b + n + got]; + got = (int)write((int)a, tmp, (size_t)i); + if (got <= 0) break; + n += got; + } + return n; + case 5: { + char *e; + if (zcstr(mem, a, path, 1024) < 0) return -1; + e = getenv(path); + if (!e) return -1; + if (c < 1 || !inmem(b)) return -1; + i = 0; + while (e[i] && i < c - 1 && inmem(b + i)) { mem[b + i] = (unsigned char)e[i]; i++; } + if (inmem(b + i)) mem[b + i] = 0; + return i; + } + case 6: + return (Cell)time(0); + case 7: + exit((int)a); + case 8: + return host_rand(); + case 9: + if (a == 0) return micro_mem_words(); + if (a == 1) return HEAP_LO; + if (a == 2) return HEAP_HI; + if (a == 3) return (Cell)micro_mem_words() * 8; + return -1; + case 10: + if (bop_cstr(mem, a, path, 1024, micro_mem_words()) < 0) return -1; + if (b == 1) return open(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644); + return open(path, O_RDONLY | O_BINARY); + case 11: + if (c < 0 || (c > 0 && !bop_ok(b, 1, micro_mem_words()))) return -1; + n = 0; + while (n < c) { + i = (int)(c - n); + if (i > 512) i = 512; + i = bop_span(b + n, i, micro_mem_words()); + if (i <= 0) break; + got = (int)read((int)a, tmp, (size_t)i); + if (got <= 0) break; + for (i = 0; i < got; i++) bop_put1(mem, b + n + i, (unsigned char)tmp[i]); + n += got; + } + return n; + case 12: + if (c < 0 || (c > 0 && !bop_ok(b, 1, micro_mem_words()))) return -1; + n = 0; + while (n < c) { + i = (int)(c - n); + if (i > 512) i = 512; + i = bop_span(b + n, i, micro_mem_words()); + if (i <= 0) break; + for (got = 0; got < i; got++) tmp[got] = (char)bop_load(mem, b + n + got, 1); + got = (int)write((int)a, tmp, (size_t)i); + if (got <= 0) break; + n += got; + } + return n; + case 13: { + char *e; + if (bop_cstr(mem, a, path, 1024, micro_mem_words()) < 0) return -1; + e = getenv(path); + if (!e) return -1; + if (c < 1 || !bop_ok(b, 1, micro_mem_words())) return -1; + i = 0; + while (e[i] && i < c - 1 && bop_ok(b + i, 1, micro_mem_words())) { bop_put1(mem, b + i, (unsigned char)e[i]); i++; } + if (bop_ok(b + i, 1, micro_mem_words())) bop_put1(mem, b + i, 0); + return i; + } + case 21: + return (Cell)zy_argc; + case 22: + return vecstr(mem, zy_argv, zy_argc, a, b, c, ""); + case 23: + return (Cell)zy_envc; + case 24: + return vecstr(mem, zy_envp, zy_envc, a, b, c, 0); + default: + return -1; + } +} + +int main(int argc, char **argv) { + UCell initial, limit; +#ifdef _WIN32 + _setmode(_fileno(stdin), _O_BINARY); + _setmode(_fileno(stdout), _O_BINARY); +#endif + zy_args(argc, argv); + if (!memory_words("TINYVM_MEMORY_WORDS", MICRO_MW_INIT, &initial) || + !memory_words("TINYVM_MAX_MEMORY_WORDS", MICRO_MW_LIMIT, &limit) || + initial > limit || !micro_memory_config(initial, limit)) { + fputs("micro: invalid memory configuration\n", stderr); + return 1; + } + return micro_run(); +} diff --git a/src/micro/port_stub.c b/src/micro/port_stub.c new file mode 100644 index 0000000..7bc22a7 --- /dev/null +++ b/src/micro/port_stub.c @@ -0,0 +1,59 @@ +/* bare-metal sketch. link this instead of port_linux.c and call micro_run + from the reset handler, after bss is cleared. + + point UART_TX at the transmit register. leave micro_getc returning -1 and + micro_eof returning 1 when the program arrives in the image and nothing + else is typed. micro_syscall returns -1 for every op, which the runtime + treats as failure (open fails, time is unavailable). the virt boards do + not use this file; their port is src/baremetal/port.c. */ +#include "micro_port.h" + +/* example, machine dependent: volatile unsigned char *const UART_TX = (volatile unsigned char *)0x10000000; */ + +static void uart_putc(unsigned char c) +{ + (void)c; + /* *UART_TX = c; */ +} + +/* the machine's memory on this board: the arena below, which is the board's RAM as + memmap.h sizes it. a board's RAM is a fixed amount of hardware, so this is the one + place the port has a size; it returns NULL past it. */ +/* physical backing is separate from the vm's configurable growth limit. */ +#ifndef MICRO_PORT_WORDS +#define MICRO_PORT_WORDS (HEAP_HI > PW ? HEAP_HI : PW) +#endif +static Cell arena[MICRO_PORT_WORDS]; + +Cell *micro_mem_alloc(UCell words) +{ + if (words > MICRO_PORT_WORDS) return 0; + return arena; +} + +int micro_getc(void) { return -1; } +int micro_eof(void) { return 1; } +void micro_putc(int c) { uart_putc((unsigned char)c); } + +Cell micro_syscall(Cell *mem, Cell op, Cell a, Cell b, Cell c) +{ + /* no command line and no environment here, so argv[0] is alone */ + if (op == 21) return 1; + if (op == 23) return 0; + if (op == 22) { + static const char lead[] = ""; + int i; + if (a != 0) return -1; + if (!b) return 6; + for (i = 0; i < 6; i++) mem[b + i] = (unsigned char)lead[i]; + mem[b + 6] = 0; + return 6; + } + (void)mem; (void)op; (void)a; (void)b; (void)c; + return -1; +} + +void micro_msg(const char *s) +{ + while (*s) uart_putc((unsigned char)*s++); +} diff --git a/src/micro/port_wasm_stream.c b/src/micro/port_wasm_stream.c new file mode 100644 index 0000000..155cde4 --- /dev/null +++ b/src/micro/port_wasm_stream.c @@ -0,0 +1,121 @@ +/* WebAssembly port of the microcore with streaming input, for the language + server. the host (the vs code extension) feeds the server's input in pieces + and calls wasm_resume after each feed; the server's output comes back one + byte at a time through js_putc, and its diagnostics through js_msg. the + server suspends when it wants input it does not have yet (MICRO_WAIT in + micro_port.h), so a step never blocks the host. build with + src/tools/mklspwasm.sh; nothing here needs libc. */ +#include "micro_port.h" + +extern void js_putc(int c); +extern void js_msg(int c); + +#define CHUNK 65536 +#define QCAP (8 << 20) + +/* the host copies a piece of input here, then calls wasm_feed with its length */ +static unsigned char chunk[CHUNK]; +/* the input the server has not read yet: q[qp..qn) */ +static unsigned char q[QCAP]; +static int qn, qp, closed; + +unsigned char *wasm_chunk(void) { return chunk; } +int wasm_chunk_cap(void) { return (int)sizeof chunk; } + +/* appends the first n bytes of chunk to the input. 0 on success, -1 when the + input would not fit, which the host must not allow (it drains by running) */ +int wasm_feed(int n) +{ + int i; + if (n < 0 || n > CHUNK || closed) return -1; + if (qn + n > QCAP) { /* move what is unread to the front */ + int live = qn - qp; + if (live + n > QCAP) return -1; + for (i = 0; i < live; i++) q[i] = q[qp + i]; + qp = 0; qn = live; + } + for (i = 0; i < n; i++) q[qn++] = chunk[i]; + return 0; +} + +/* no more input will come: the next read past the end is -1 */ +void wasm_close(void) { closed = 1; } + +/* the machine's memory: the linear memory above everything the module has in it. + the base is fixed when the first machine is made, and a grow only adds pages on + the top, so the base never moves. */ +static unsigned char *mbase; +static UCell mcap; /* bytes of machine memory the module has grown for */ + +Cell *micro_mem_alloc(UCell words) +{ + UCell need, extra, pages; + if (words > ((UCell)-1) / sizeof(Cell)) return 0; + if (!mbase) { + UCell top = (UCell)__builtin_wasm_memory_size(0) * 65536u; + mbase = (unsigned char *)top; + mcap = 0; + } + need = words * sizeof(Cell); + if (need <= mcap) return (Cell *)mbase; + extra = need - mcap; + pages = (extra + 65535u) / 65536u; + if (__builtin_wasm_memory_grow(0, (int)pages) < 0) return 0; + mcap += pages * 65536u; /* wasm zeroes the pages it grows by */ + return (Cell *)mbase; +} + +int micro_getc(void) +{ + if (qp < qn) return q[qp++]; + return closed ? -1 : MICRO_WAIT; +} +int micro_eof(void) { return closed && qp >= qn; } +void micro_putc(int c) { js_putc(c & 255); } +void micro_msg(const char *s) { while (*s) js_msg(*s++); } + +Cell micro_syscall(Cell *mem, Cell op, Cell a, Cell b, Cell c) +{ + switch (op) { + case 9: /* memory: the size of the machine and its heap */ + if (a == 0) return micro_mem_words(); + if (a == 1) return HEAP_LO; + if (a == 2) return HEAP_HI; + if (a == 3) return (Cell)micro_mem_words() * 8; + return -1; + case 7: /* exit: the run ends */ + return -1; + case 21: /* argc: the server has no command line of its own, so alone */ + return 1; + case 22: /* argv: entry 0 is , as in zy(1) */ + if (a != 0) return -1; + if (!b) return 6; + if (c < 7 || b + 6 >= micro_mem_words()) return -1; + { + static const char lead[] = ""; + int i; + for (i = 0; i < 6; i++) mem[b + i] = (unsigned char)lead[i]; + mem[b + 6] = 0; + } + return 6; + case 23: /* envc: the host passes no environment */ + return 0; + } + return -1; +} + +/* the core multiplies 64 by 64 into 128 bits for the float helpers; wasm has no + such instruction and there is no compiler runtime to link, so this is it */ +void __multi3(unsigned long long *r, unsigned long long al, unsigned long long ah, unsigned long long bl, unsigned long long bh) +{ + unsigned long long a0 = al & 0xffffffffu, a1 = al >> 32, b0 = bl & 0xffffffffu, b1 = bl >> 32; + unsigned long long p00 = a0 * b0, p01 = a0 * b1, p10 = a1 * b0, p11 = a1 * b1; + unsigned long long mid = (p00 >> 32) + (p01 & 0xffffffffu) + (p10 & 0xffffffffu); + r[0] = (p00 & 0xffffffffu) | (mid << 32); + r[1] = p11 + (p01 >> 32) + (p10 >> 32) + (mid >> 32) + al * bh + ah * bl; +} + +/* loads the server and runs it until it halts or waits for input (3) */ +int wasm_start(void) { return micro_run(); } +/* goes on after the host fed input: as wasm_start, halt (0), error (2) or wait (3) */ +int wasm_resume(void) { return micro_resume(); } diff --git a/src/micro/port_win.c b/src/micro/port_win.c new file mode 100644 index 0000000..8e3ef74 --- /dev/null +++ b/src/micro/port_win.c @@ -0,0 +1,268 @@ +/* Windows port for the microcore: stdin/stdout a byte at a time, the syscalls, + diagnostics on stderr. same behaviour as io_win.c plus core.c's hostcall. + 1..6 are files, getenv and unix time. 7 exits the process, 8 is a + counter, 9 reports the machine size. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "micro_port.h" +#include "bops.h" + +#ifndef O_BINARY +#define O_BINARY _O_BINARY +#endif +#if defined(_MSC_VER) +#define open _open +#define close _close +#define read _read +#define write _write +#endif + +/* the machine's memory: a block that grows by realloc, zero in the new part. the + base can move on a grow, which micro.c takes into account (it re-reads m). */ +static Cell *lmem; +static UCell lcap; + +Cell *micro_mem_alloc(UCell words) +{ + Cell *n; + if (words <= lcap && lmem) return lmem; + if (words > ((UCell)-1) / sizeof(Cell)) return NULL; + n = realloc(lmem, words * sizeof(Cell)); + if (!n) return NULL; + memset(n + lcap, 0, (words - lcap) * sizeof(Cell)); + lmem = n; + lcap = words; + return n; +} + +int micro_getc(void) +{ + int c = _fgetc_nolock(stdin); + return c == EOF ? -1 : c; +} + +int micro_eof(void) +{ + int c = _fgetc_nolock(stdin); + if (c == EOF) return 1; + _ungetc_nolock(c, stdin); + return 0; +} + +void micro_putc(int c) { _fputc_nolock(c & 255, stdout); } + +void micro_msg(const char *s) { fputs(s, stderr); } + +static int zcstr(Cell *mem, Cell addr, char *dst, int max) +{ + int i = 0, c; + if (max < 1) return -1; + if ((UCell)addr >= (UCell)micro_mem_words()) { dst[0] = 0; return -1; } + while (i < max - 1 && (UCell)(addr + i) < (UCell)micro_mem_words()) { + c = (int)mem[addr + i]; + if (!c) break; + dst[i++] = (char)c; + } + dst[i] = 0; + return i; +} + +static int inmem(Cell a) { return (UCell)a < (UCell)micro_mem_words(); } + +/* the command line and the environment, kept for ops 21 to 24: the same + contract as src/core/core.c. a program read from stdin has no file name, so + argv[0] is . */ +static int zy_argc; +static char **zy_argv; +static int zy_envc; +static char **zy_envp; + +static void zy_args(int argc, char **argv) +{ + char **e = _environ; + int n = 0; + while (e && e[n]) n++; + zy_envp = e; zy_envc = n; + zy_argv = argv; + zy_argc = argc > 0 ? argc : 1; +} + +/* copy entry a of a char ** into vm words, one character per word, terminated + like SYS_GETENV. a zero buffer asks for the length only. lead, when given, + replaces entry zero. */ +static Cell vecstr(Cell *mem, char **v, int count, Cell a, Cell b, Cell c, const char *lead) +{ + char *e; + int i = 0; + if (a < 0 || a >= (Cell)count) return -1; + e = (a == 0 && lead) ? (char *)lead : v[a]; + if (!e) return -1; + if (!b) { while (e[i]) i++; return i; } + if (c < 1 || !inmem(b)) return -1; + while (e[i] && i < c - 1 && inmem(b + i)) { mem[b + i] = (unsigned char)e[i]; i++; } + if (inmem(b + i)) mem[b + i] = 0; + return i; +} + +static Cell host_rand(void) +{ + static uint64_t s; + if (!s) s = (uint64_t)time(0) ^ 0x9E3779B97F4A7C15ull; + if (!s) s = 0x9E3779B97F4A7C15ull; + s ^= s << 13; s ^= s >> 7; s ^= s << 17; + if ((Cell)s == -1) s ^= 0x100; + return (Cell)s; +} + +static int memory_words(const char *name, UCell fallback, UCell *words) +{ + const char *s = getenv(name); + char *end; + unsigned long long value; + if (!s) { *words = fallback; return 1; } + errno = 0; + value = strtoull(s, &end, 10); + if (errno || end == s || *end || !value || value > 0x7fffffffULL) return 0; + *words = (UCell)value; + return 1; +} + +Cell micro_syscall(Cell *mem, Cell op, Cell a, Cell b, Cell c) +{ + char path[1024], tmp[512]; + int n, i, got; + switch (op) { + case 1: + if (zcstr(mem, a, path, 1024) < 0) return -1; + if (b == 1) return open(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644); + return open(path, O_RDONLY | O_BINARY); + case 2: + return close((int)a); + case 3: + if (c < 0 || !inmem(b)) return -1; + n = 0; + while (n < c) { + i = (int)(c - n); + if (i > 512) i = 512; + if (!inmem(b + n)) break; + if (!inmem(b + n + i - 1)) i = (int)(micro_mem_words() - (b + n)); + if (i <= 0) break; + got = (int)read((int)a, tmp, (size_t)i); + if (got <= 0) break; + for (i = 0; i < got; i++) mem[b + n + i] = (unsigned char)tmp[i]; + n += got; + } + return n; + case 4: + if (c < 0 || !inmem(b)) return -1; + n = 0; + while (n < c) { + i = (int)(c - n); + if (i > 512) i = 512; + if (!inmem(b + n)) break; + if (!inmem(b + n + i - 1)) i = (int)(micro_mem_words() - (b + n)); + if (i <= 0) break; + for (got = 0; got < i; got++) tmp[got] = (char)mem[b + n + got]; + got = (int)write((int)a, tmp, (size_t)i); + if (got <= 0) break; + n += got; + } + return n; + case 5: { + char *e; + if (zcstr(mem, a, path, 1024) < 0) return -1; + e = getenv(path); + if (!e) return -1; + if (c < 1 || !inmem(b)) return -1; + i = 0; + while (e[i] && i < c - 1 && inmem(b + i)) { mem[b + i] = (unsigned char)e[i]; i++; } + if (inmem(b + i)) mem[b + i] = 0; + return i; + } + case 6: + return (Cell)time(0); + case 7: + exit((int)a); + case 8: + return host_rand(); + case 9: + if (a == 0) return micro_mem_words(); + if (a == 1) return HEAP_LO; + if (a == 2) return HEAP_HI; + if (a == 3) return (Cell)micro_mem_words() * 8; + return -1; + case 10: + if (bop_cstr(mem, a, path, 1024, micro_mem_words()) < 0) return -1; + if (b == 1) return open(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644); + return open(path, O_RDONLY | O_BINARY); + case 11: + if (c < 0 || (c > 0 && !bop_ok(b, 1, micro_mem_words()))) return -1; + n = 0; + while (n < c) { + i = (int)(c - n); + if (i > 512) i = 512; + i = bop_span(b + n, i, micro_mem_words()); + if (i <= 0) break; + got = (int)read((int)a, tmp, (size_t)i); + if (got <= 0) break; + for (i = 0; i < got; i++) bop_put1(mem, b + n + i, (unsigned char)tmp[i]); + n += got; + } + return n; + case 12: + if (c < 0 || (c > 0 && !bop_ok(b, 1, micro_mem_words()))) return -1; + n = 0; + while (n < c) { + i = (int)(c - n); + if (i > 512) i = 512; + i = bop_span(b + n, i, micro_mem_words()); + if (i <= 0) break; + for (got = 0; got < i; got++) tmp[got] = (char)bop_load(mem, b + n + got, 1); + got = (int)write((int)a, tmp, (size_t)i); + if (got <= 0) break; + n += got; + } + return n; + case 13: { + char *e; + if (bop_cstr(mem, a, path, 1024, micro_mem_words()) < 0) return -1; + e = getenv(path); + if (!e) return -1; + if (c < 1 || !bop_ok(b, 1, micro_mem_words())) return -1; + i = 0; + while (e[i] && i < c - 1 && bop_ok(b + i, 1, micro_mem_words())) { bop_put1(mem, b + i, (unsigned char)e[i]); i++; } + if (bop_ok(b + i, 1, micro_mem_words())) bop_put1(mem, b + i, 0); + return i; + } + case 21: + return (Cell)zy_argc; + case 22: + return vecstr(mem, zy_argv, zy_argc, a, b, c, ""); + case 23: + return (Cell)zy_envc; + case 24: + return vecstr(mem, zy_envp, zy_envc, a, b, c, 0); + default: + return -1; + } +} + +int main(int argc, char **argv) { + UCell initial, limit; + _setmode(_fileno(stdin), _O_BINARY); + _setmode(_fileno(stdout), _O_BINARY); + zy_args(argc, argv); + if (!memory_words("TINYVM_MEMORY_WORDS", MICRO_MW_INIT, &initial) || + !memory_words("TINYVM_MAX_MEMORY_WORDS", MICRO_MW_LIMIT, &limit) || + initial > limit || !micro_memory_config(initial, limit)) { + fputs("micro: invalid memory configuration\n", stderr); + return 1; + } + return micro_run(); +} diff --git a/src/runtime/rt.zc b/src/runtime/rt.zc new file mode 100644 index 0000000..3badcc2 --- /dev/null +++ b/src/runtime/rt.zc @@ -0,0 +1,1326 @@ +// ============================================================================ +// rt.zc: the runtime half of the genome. memory map, i/o, arithmetic helpers, +// the heap (size-class free lists) and the mark-sweep collector. +// ============================================================================ + +// how the runtime is laid out, for a reader coming in cold: +// +// memory map generated constants name the regions used by a build. the +// compiler and runtime use the same names and values. +// i/o, strings thin wrappers over the cpu's i/o opcodes, plus the two +// string forms (word strings and packed byte strings). +// heap a buddy allocator with a 5 word header on every block. +// new/free/recycle are built on it. every heap object has at +// most one owner (the container that holds it), and freeing +// an owner frees what it owns. +// growable arrays []T handles, built on the heap. +// mark-sweep a conservative collector. it is the safety net behind +// free: anything the compiler's ownership analysis could not +// free gets reclaimed here. +// memory debug optional checks (use after free, double free, leaks). +// syscalls file and environment access through one gate word. +// +// everything is a 64-bit word, and an address is a word index, not a byte +// index. the cpu is a stack machine, so "asm { a @ . }" pushes a, fetches the +// word at that address with @, then outputs it with . (see the vm reference). +// +// ---- memory map (words) ---- +// src/tools/genmap writes the map values for each build between these markers. +// keep the names and order stable so programs can use the same map interface +// across targets and image sizes. + +// @map-rt + +const word MW = 0x1C5000; +const word CODE_END = 0x80000; +const word HEAP_LO = 0x1C5000; +const word HEAP_HI = 0x225000; +const int HEAP_GROW = 0x1; +const word ZFP = 0x90000; +const word GORG = 0x114100; +const word FBOT = 0x164100; +const word FTOP = 0x1A4100; +const word RS0 = 0x1A4100; +const word MARK0 = 0x1B4100; +const int MARKN = 0x8000; +const word DSLO = 0x1BC100; +const word DSHI = 0x1C4FF0; +const int MAGIC = 0x2B7E1516; +// @end-map + +// frames grow in stable segments so pointers to a caller's locals stay valid. +word frame_floor = FBOT, frame_top = FTOP, frame_node; +int frame_chunk = FTOP - FBOT, frame_guard = (FTOP - FBOT) >> 2, frame_busy, gc_active; +[17]word frame_control; +word data_low = DSLO, data_top = DSHI, data_node; +word return_low = RS0, return_top = MARK0 - 8, return_node; +const int F_STACK = 1024; +const int F_INTERNAL = 512; + +// ---- virtual enhancements ---- +// install(desc) hands the core a descriptor through the % opcode. the core reads +// it and wires the region it describes into the cpu, so the compiler can replace +// a run of vm code with native code. the descriptor is laid out by the compiler +// for each enhance and o3 region (see install in compiler.zc). the enhance lines +// below are the micro forms of the same thing: a single machine instruction each. +// zdiv, zmod and zshl further down do the same arithmetic in software. this block +// comes first, since the compiler resolves / % and << to these names. + +// install the descriptor for an optimized region. +int install(int desc) { return asm { desc @ % }; } +// signed division of a by b, as one machine instruction. +enhance idiv(a, b) micro "a = sdiv a b"; +// signed remainder of a divided by b, as one machine instruction. +enhance imod(a, b) micro "a = srem a b"; +// a shifted left by b, as one machine instruction. +enhance ishl(a, b) micro "a = shl a b"; +// for o3: the address of a variable at fp + k. +enhance lfa(k) micro "r1 = ld 589824; k = add r1 k"; +// 1 when i is outside 0..n-1 (the unsigned compare also catches negative i), else 0. +enhance outside(i, n) micro "i = ltu i n; i = xor i 1"; +// ---- bit operations and float conversions ---- +// these use single machine instructions where the cpu has them. + +// the number of leading zero bits in a. +enhance clz(a) micro "a = clz a"; +// the number of trailing zero bits of a. +enhance ctz(a) micro "a = ctz a"; +// the number of one bits in a. +enhance popcnt(a) micro "a = popcnt a"; +// a with its bytes in reverse order. +enhance bswap(a) micro "a = bswap a"; +// a rotated left by n bits. +enhance rotl(a, n) micro "a = rotl a n"; +// a rotated right by n bits. +enhance rotr(a, n) micro "a = rotr a n"; +// the high word of the signed product of a and b. +enhance mulhi(a, b) micro "a = mulhs a b"; +// the high word of the unsigned product of a and b. +enhance umulhi(a, b) micro "a = mulhu a b"; +// the float a, converted to a signed integer. +enhance f2s(a) micro "a = f2s a"; +// the signed integer a, converted to a float. +enhance s2f(a) micro "a = s2f a"; +// f2s, the software form used where the cpu has none. +enhance zf2s(a) micro "a = f2s a"; +// s2f, the software form used where the cpu has none. +enhance zs2f(a) micro "a = s2f a"; + +// runtime code segments retain the addresses of earlier functions and globals. +word code_blocks; +// distinguish a raw function address from a heap closure's environment block. +int callable_code(word f) { + word p = code_blocks; + if (f < HEAP_LO) return 1; + while (p) { if (f >= p[1] && f < p[2]) return 1; p = p[0]; } + return 0; +} + +// ---- i/o ---- +// output can be redirected. mode 1 drops what is written and mode 2 appends +// it to a buffer; mode 3 counts characters without storing them (the server +// uses these modes while it checks a document, +// so a parse word that prints can't write into the protocol stream). +// raw_c always reaches the terminal. +int outmode; word outbuf; int outn, outcap; word outgrow; +// write one character to the terminal with no translation. +int raw_c(int ch) { asm { ch @ . } return 0; } +// write one character. normally it goes to the terminal; while a capture is +// running (see capture in src/lib/std.zc) it goes into the capture buffer instead. +int put_c(int ch) { + if (outmode) { + if (outmode == 3) outn++; + else if (outmode == 2) { + if (outn >= outcap && outgrow) outgrow(); + if (outn < outcap) { outbuf[outn] = ch; outn++; } + } + return 0; + } + asm { ch @ . } + return 0; +} +// read one character from the input, as a byte value. -1 means the input has +// ended, so check eof when a -1 could be a real value. +int get_c() { return asm { , }; } +// true when the input has no more characters. a get_c after this gives -1. +int eof() { return asm { ? }; } +// stop the machine now. nothing after it runs, and the exit status is 0. +int halt() { asm { q } return 0; } +// write a zero-terminated string, one character at a time. it is the same as +// put_c on each byte of s, so a capture sees the text too. +int put_s(char* s) { + int i = 0; + while (s[i]) { put_c(s[i]); i++; } + return 0; +} +// write the decimal form of a number, with a minus sign for a negative one. +// the smallest integer is handled as its own case. +int put_n(int n) { + [24]word b; int i = 0; + if (n < 0) { put_c('-'); n = 0 - n; } + if (n < 0) { put_s("9223372036854775808"); return 0; } + while (1) { b[i++] = '0' + n % 10; n = n / 10; if (!n) break; } + while (i) put_c(b[--i]); + return 0; +} +// the length of a word string, not counting its 0. +int len_w(char* s) { + int n = 0; + while (s[n]) n++; + return n; +} +// true when two word strings hold the same characters. +int eq_w(char* a, char* b) { + int i = 0; + while (a[i] && a[i] == b[i]) i++; + return a[i] == b[i]; +} +// ---- word helpers ---- +// copy and fill by word. copy_w copies forward, so overlapping ranges are only +// safe when dst is below src. a long run above the program area goes to the +// host in one call (ops 18 and 19 of the syscall gate); a host that answers -1 +// (a board, the js vm, a range that overlaps itself) gets the plain loop. + +const int SYS_FILL = 18, /* wowie! */ SYS_COPY = 19; +// copy n words from src to dst. +int copy_w(word* dst, word* src, int n) { + int i = 0; word g = ZFP + 2; + if (n >= 16 && dst >= MW) { + g[1] = dst; g[2] = src; g[3] = n; g[0] = SYS_COPY; + if (g[0] == n) return 0; + } + while (i < n) { dst[i] = src[i]; i++; } + return 0; +} +// set n words at dst to v. +int fill_w(word* dst, int v, int n) { + int i = 0; word g = ZFP + 2; + if (n >= 16 && dst >= MW) { + g[1] = dst; g[2] = v; g[3] = n; g[0] = SYS_FILL; + if (g[0] == n) return 0; + } + while (i < n) { dst[i] = v; i++; } + return 0; +} + +// byte addresses sit beside word addresses. word w is bytes (w << 3) through +// (w << 3) + 7, and byte 0 of a word is its low 8 bits. a byte address is a +// plain integer: the odd/even tag is only for code words, and u/m/d stay on +// the word pointer of a heap object. 16- and 32-bit accesses are little-endian +// and may cross a word. an access that runs off the end of memory loads 0 and +// stores nothing. a byte store never fires the syscall gate; that is a word +// store at SYSGATE. opcodes 612..617, words 1224..1234. +int addr_b(int w) { return w << 3; } +// the word address of a byte address b. +int addr_w(int b) { return b >> 3; } +// load the byte at address a. +int ld8(int a) { return asm { a @ #1224 }; } +// store the low byte of v at address a. +int st8(int a, int v) { asm { v @ a @ #1226 }; return 0; } +// load the 16-bit value at address a. +int ld16(int a) { return asm { a @ #1228 }; } +// store the low 16 bits of v at address a. +int st16(int a, int v) { asm { v @ a @ #1230 }; return 0; } +// load the 32-bit value at address a. +int ld32(int a) { return asm { a @ #1232 }; } +// store the low 32 bits of v at address a. +int st32(int a, int v) { asm { v @ a @ #1234 }; return 0; } + +// a byte string: word 0 is the length, then the bytes packed little-endian +// starting at the next word, and a 0 byte after the last character. literals +// b"..." live in the code and are not heap objects. cat_s, slice_s, n_to_s and +// new_s return a shared heap object with the same layout: many pointers may +// hold it, free does nothing, and the collector reclaims it. word strings +// (one character per word) are unchanged. +int len_s(word s) { return s[0]; } +// byte i of a packed byte string. +int at_s(word s, int i) { + if (i < 0 || i >= s[0]) return 0; + return ld8(addr_b(s + 1) + i); +} +// a new packed byte string of n bytes, owned by the caller. +unchecked word new_s(int n) { + word p = new(1 + ((n + 8) >> 3)); + p[0] = n; + return share(p); +} +// store byte v at position i of a word string. +int sput(word s, int i, int v) { st8(addr_b(s + 1) + i, v); return 0; } +// a new string with a followed by b, owned by the caller. +unchecked word cat_s(word a, word b) { + int n = a[0] + b[0]; word p = new_s(n); int i = 0; + while (i < a[0]) { sput(p, i, at_s(a, i)); i++; } + i = 0; + while (i < b[0]) { sput(p, a[0] + i, at_s(b, i)); i++; } + sput(p, n, 0); + return p; +} +// a new string with the characters from i to j of s, owned by the caller. +unchecked word slice_s(word s, int i, int j) { + int n; word p; int k = 0; + if (i < 0) i = 0; + if (j > s[0]) j = s[0]; + if (j < i) j = i; + n = j - i; + p = new_s(n); + while (k < n) { sput(p, k, at_s(s, i + k)); k++; } + sput(p, n, 0); + return p; +} +// compare two word strings: negative, 0 or positive, like strcmp. +int cmp_s(word a, word b) { + int i = 0, n = a[0], ca, cb; + if (b[0] < n) n = b[0]; + while (i < n) { + ca = at_s(a, i); cb = at_s(b, i); + if (ca != cb) { if (ca < cb) return 0 - 1; return 1; } + i++; + } + if (a[0] == b[0]) return 0; + if (a[0] < b[0]) return 0 - 1; + return 1; +} +// true when two word strings are equal. +int eq_s(word a, word b) { return cmp_s(a, b) == 0; } +// true when word string a sorts before b. +int lt_s(word a, word b) { return cmp_s(a, b) < 0; } +// true when word string a sorts before b or equals it. +int le_s(word a, word b) { return cmp_s(a, b) <= 0; } +// write a packed byte string. +int put_bs(word s) { + int i = 0; + while (i < s[0]) { put_c(at_s(s, i)); i++; } + return 0; +} +// the decimal number at the start of a packed byte string. +int s_to_n(word s) { + int n = 0, i = 0, sign = 1; word c; + if (s[0] && at_s(s, 0) == '-') { sign = 0 - 1; i = 1; } + while (i < s[0]) { + c = at_s(s, i); + if (c < '0' || c > '9') break; + n = n * 10 + c - '0'; + i++; + } + return n * sign; +} +// a new packed byte string holding the decimal form of n. +unchecked word n_to_s(int n) { + [24]word b; int i = 0, j = 0, neg = 0; word p, t; + if (!n) { p = new_s(1); sput(p, 0, '0'); sput(p, 1, 0); return p; } + if (n < 0) { neg = 1; n = 0 - n; } + if (n < 0) { + t = "-9223372036854775808"; + p = new_s(20); + while (t[i]) { sput(p, i, t[i]); i++; } + sput(p, i, 0); + return p; + } + while (n) { b[i] = '0' + n % 10; i++; n = n / 10; } + p = new_s(i + neg); + if (neg) { sput(p, 0, '-'); j = 1; } + while (i) { i--; sput(p, j, b[i]); j++; } + sput(p, j, 0); + return p; +} + +// ---- arithmetic the hardware doesn't have ---- +[64]word pow2; +int zrem; +// unsigned division of a by dv. +int zudiv(int n, int dv) { + int q = 0, r = 0, i = 62; + if (n < dv) { zrem = n; return 0; } + while (i >= 0) { + r = r + r + ((n >> i) & 1); + q = q + q; + if (r >= dv) { r = r - dv; q++; } + i--; + } + zrem = r; + return q; +} +// signed division of a by b, rounding toward zero, as c does. +int zdiv(int a, int b) { + word s = 0; int q; + if (b == 0) { zrem = a; return 0; } + if (a < 0) { a = 0 - a; s = 1; } + if (b < 0) { b = 0 - b; s = s ^ 1; } + q = zudiv(a, b); + if (s) return 0 - q; + return q; +} +// signed remainder of a divided by b, with the sign of a, as c does. +int zmod(int a, int b) { + int neg = a < 0; + if (b == 0) return a; + zdiv(a, b); + if (neg) return 0 - zrem; + return zrem; +} +// a shifted left by b bits, for b under 64. +int zshl(int a, int b) { return a * pow2[b & 63]; } + +// ---- heap ---- +// a buddy allocator. the heap is cut into aligned power-of-two chunks, every +// block is a power of two words, and a freed block merges with its buddy +// (the other half of the block they were split from) whenever that one is +// free too, so freed space can be reused at any size. every block has a 5 +// word header before the pointer the program sees: +// p[-5] block size +// p[-4] flags in the low 16 bits, size class in the next 5 +// 0 free (check word cleared) or quarantined (check word still p ^ MAGIC) +// 1 live 2 releasing 3 marked 4 quarantined but a word still points at it +// bit 8 (value 8) is orthogonal: the object is shared. free is a no-op, +// own and release do not follow it, and the sweep still collects it when +// nothing points at it. bits 0..2 keep the meanings above. +// p[-3] p ^ MAGIC p[-2] owner (the container that owns it, or 0) +// p[-1] its type (a type record from new(T), 0 from new(n), -(n + 1) from o3's new_raw) +// free blocks sit on a doubly linked list per class, linked through p[0], p[1]. +// free quarantines: the block stays intact until a sweep finds nothing pointing +// at it. recycle is the fast path for a pointer the program has already +// dropped: it pushes the block onto its size-class list and does not walk the +// heap. a two-word node is still 8 words (the smallest block); a three-word +// node fits in 8 instead of 16. +const int HDR = 5; +const int F_SHARE = 8; // p[-4] bit, orthogonal to live / mark / releasing +const int F_LOW = 65535; // flags live in these bits; the size class is above them +int hbump; [32]word freel; int gc_runs, gc_freed, gc_auto = 1; +int hstop; // block walks and new chunks stop here +int hold_lo, hold_hi; // while nonzero, balloc will not return a block overlapping this range +int hmax, hcut; // words of memory the host has (0: the heap cannot grow); hcut: the heap was cut, so it must not grow +int dbg, alloc_line; // memdebug bits, and the line the compiler stamps before new + +// the size of the top-level chunk holding heap offset off, within [0, hstop) +int topsize(int off) { + int h = hstop - HEAP_LO, base = 0, sz; + while (h) { + sz = 1; + while (sz + sz <= h) sz = sz + sz; + if (off < base + sz) return sz; + base = base + sz; h = h - sz; + } + return 0; +} +// the free lists. freel[k] heads a doubly linked list of free blocks of +// 2^k words; the links live in the block's first payload words (b[5], b[6] +// here, since b is the header address). fpush places a block on the front of its +// class list, funlink takes one out of the middle, mkblock stamps a fresh +// header, and bclass reads the size class back out of the flags word. +int fpush(word b, int k) { + b[1] = k * pow2[16]; + b[5] = freel[k]; + b[6] = 0; + if (freel[k]) freel[k][6] = b; + freel[k] = b; + return 0; +} +// remove block b from its size-class free list. +int funlink(word b, int k) { + if (b[6]) b[6][5] = b[5]; else freel[k] = b[5]; + if (b[5]) b[5][6] = b[6]; + return 0; +} +// turn the memory at b into a free block of size class k. +word mkblock(word b, int k) { + b[0] = pow2[k]; + b[1] = k * pow2[16]; + b[2] = 0; + b[3] = 0; + b[4] = 0; + return b; +} +// the size class of a heap block. +word bclass(word b) { return (b[1] >> 16) & 31; } +// rebuild the free lists for [HEAP_LO, hstop). returns 0 if that span is not +// a whole number of top-level chunks (nothing is left half-built: the caller +// restores the full heap). +int fill_free() { + int b = HEAP_LO, sz, k; + fill_w(freel, 0, 32); + while (b < hstop) { + sz = topsize(b - HEAP_LO); + if (sz <= 0 || b + sz > hstop) return 0; + k = 0; + while (k < 31 && pow2[k] < sz) k++; + if (pow2[k] != sz) return 0; + fpush(mkblock(b, k), k); + b = b + sz; + } + return 1; +} +// start-up: build the power-of-two table and hand the whole heap range to +// the free lists as a few big blocks +int heap_init() { + int i = 1; + if (frame_guard > 1024) frame_guard = 1024; + pow2[0] = 1; + while (i < 64) { pow2[i] = pow2[i - 1] + pow2[i - 1]; i++; } + hstop = HEAP_HI; + hbump = HEAP_HI; + hold_lo = 0; + hold_hi = 0; + hcut = 0; + hmax = 0; + // a map with a growing heap asks the host how much memory there is. a host + // that does not answer (-1) or has no more than the initial heap gets a + // fixed heap + if (HEAP_GROW) { hmax = syscall(9, 0, 0, 0); if (hmax <= HEAP_LO) hmax = 0; } + fill_free(); + frame_control[0] = &frame_floor; frame_control[1] = &frame_top; frame_control[2] = &frame_guard; + frame_control[3] = &data_low; frame_control[4] = &data_top; frame_control[5] = &data_node; + frame_control[6] = &return_low; frame_control[7] = &return_top; frame_control[8] = &return_node; + frame_control[9] = freel; frame_control[10] = &hstop; frame_control[11] = &hbump; + frame_control[12] = &hmax; frame_control[13] = &hold_lo; frame_control[14] = &hold_hi; frame_control[15] = &hcut; + frame_control[16] = &gc_active; + syscall(9, 6, frame_control, 0); + return 0; +} +// drop [lim, HEAP_HI) out of the allocator. the compiler reserves this range +// for type records when it overlaps the heap; the type list is at its start, so +// it cannot also be a buddy header. returns 1 if the cut landed on chunk +// boundaries. allocations that would still overlap [lim, HEAP_HI) are refused. +int heap_reset(int lim) { + int ok; + if (lim <= HEAP_LO || lim > HEAP_HI) return 0; + hstop = lim; + hbump = lim; + hold_lo = 0; + hold_hi = 0; + if (lim < HEAP_HI) { hold_lo = lim; hold_hi = HEAP_HI; hcut = 1; } + ok = fill_free(); + if (!ok) { + hstop = HEAP_HI; + hbump = HEAP_HI; + hold_lo = 0; + hold_hi = 0; + hcut = 0; + fill_free(); + } + return ok; +} +// put a power-of-two tail back. addr is the cut point heap_reset was given. +int heap_uncut(int addr, int k) { + hstop = HEAP_HI; + hbump = HEAP_HI; + hold_lo = 0; + hold_hi = 0; + hcut = 0; + if (addr < HEAP_LO || addr + pow2[k] > HEAP_HI) return 0; + fpush(mkblock(addr, k), k); + return 0; +} +// take a block of 2^k words. find the smallest non-empty class at or above k, +// unlink its first block and split it down: each half that is not kept goes +// back on its own list. a block that overlaps the held range is marked +// "releasing" and dropped from the lists for good (the loop then tries again), +// so the compiler's type area is never handed out as heap. +word balloc(int k) { + int j; word b; int h, ok; + while (1) { + j = k; + while (j < 32 && !freel[j]) j++; + if (j >= 32) return 0; + b = freel[j]; + funlink(b, j); + while (j > k) { j--; h = b + pow2[j]; fpush(mkblock(h, j), j); } + mkblock(b, k); + ok = 1; + if (hold_hi && b < hold_hi && b + pow2[k] > hold_lo) { + b[1] = (k * pow2[16]) | 2; + b[5] = 0; + b[6] = 0; + ok = 0; + } + if (ok) return b; + } + return 0; +} +// ---- a heap that grows ---- +// the heap is [HEAP_LO, hstop). on a map with HEAP_GROW the memory above +// HEAP_HI belongs to the host (a big zeroed array nobody has touched yet), and +// when the free lists cannot satisfy a request the heap takes more of it. the +// buddy allocator does not care where the top is, as long as the top-level +// chunks (topsize) are right: extending hstop re-cuts them, and every new +// piece goes through bfree, which merges it with a free buddy below it. a +// block that is in use stays where it is, so no pointer moves. +// grow so that a block of 2^k words fits: double the heap, or less when the +// host runs out. returns 0 when it cannot. +int heap_grow(int k) { + int h = hstop - HEAP_LO, h2, need, o, sz, c, room; + if (!hmax || hcut || hold_hi) return 0; + room = hmax - HEAP_LO; + need = (h + pow2[k] - 1) / pow2[k] * pow2[k] + pow2[k]; + h2 = h + h; + if (h2 < need) h2 = need; + if (h2 > room) { // the machine is smaller than the heap wants: the host grows it (syscall 9, 4) + hmax = syscall(9, 4, HEAP_LO + h2, 0); + if (hmax <= HEAP_LO) hmax = 0; + room = hmax - HEAP_LO; + } + if (h2 > room) h2 = room; + if (h2 < need) return 0; + hstop = HEAP_LO + h2; + hbump = hstop; + o = h; + while (o < h2) { + // the biggest aligned power of two that starts at o and ends by h2 + sz = 8; c = 3; + while (sz + sz <= h2 - o && o % (sz + sz) == 0) { sz = sz + sz; c++; } + bfree(mkblock(HEAP_LO + o, c)); + o = o + sz; + } + return 1; +} +// words the heap spans right now (it only ever grows) +int heap_size() { return hstop - HEAP_LO; } +// words on the free lists +int hfree() { + int k = 3, n = 0; word b; + while (k < 32) { + b = freel[k]; + while (b) { n = n + pow2[k]; b = b[5]; } + k++; + } + return n; +} +int fend; // end of the block bfree last made, for the sweep +// put block b back, merging it with its buddy as long as it can +int bfree(word b) { + int k = bclass(b), sz = pow2[k], off; word bud; + while (1) { + off = b - HEAP_LO; + if (sz >= topsize(off)) break; + bud = HEAP_LO + (off ^ sz); + // a quarantined buddy still has its check word, and isn't on a free list + if ((bud[1] & F_LOW) != 0 || bclass(bud) != k || bud[0] != sz || bud[2]) break; + funlink(bud, k); + // the upper header is inside the merged block now + if (bud < b) { + b[0] = 0; + b[2] = 0; + b = bud; + } + else { bud[0] = 0; bud[2] = 0; } + k++; sz = sz + sz; + mkblock(b, k); + } + fpush(mkblock(b, k), k); + fend = b + sz; + return 0; +} +// back onto the size-class list, with no buddy walk and no heap scan +int bdrop(word b) { + int k = bclass(b); + b[2] = 0; + fpush(b, k); + return 0; +} +// is x the payload address of a block that has been allocated (live or +// quarantined)? the check word p[-3] == p ^ MAGIC is what makes this safe to +// ask about any integer: a random value would have to match by chance. +word isobj(word x) { return x >= HEAP_LO + HDR && x < hbump && x[-3] == (x ^ MAGIC) && (x[-4] & 1); } +// the number of words to skip in p[0..n) before one that could be a pointer +// to a block (see op 20 in core.c); n when none can be. a long payload of +// plain numbers costs one host call instead of an isobj per word. a host that +// does not have the op (-1) is asked once and then never again. +const int SYS_SCAN = 20; +int scan_off; +// walk the n words at p and mark every heap object they point to. +int scan_ptr(int p, int n) { + word g = ZFP + 2; int r; + if (n < 8 || scan_off) return 0; + g[1] = p; g[2] = n; g[3] = hbump; g[0] = SYS_SCAN; + r = g[0]; + if (r < 0) { scan_off = 1; return 0; } + return r; +} + +// every prologue checks the frame stack; on overflow it resets the frame +// pointer (so this can run at all) and stops +int frame_overflow() { + put_s("zy: frame stack overflow\n"); + halt(); + return 0; +} +// choose the minimum size of subsequent frame segments, in words. +int reserve_frames(int words) { + if (words > frame_chunk) frame_chunk = words; + return frame_chunk; +} +// reserve stack words without invalidating locals or their borrowed addresses. +int reserve_data(int words) { return syscall(9, 7, words, 0); } +int reserve_returns(int words) { return syscall(9, 8, words, 0); } +// the prologue restores oldfp before calling this helper. its own calls use +// the guard space below oldfp; they must not try to grow another segment. +unchecked word frame_grow(int oldfp, int attempted) { + int size = frame_chunk, slots = oldfp - attempted; word p; + frame_busy = 1; + if (!hstop || slots < 0 || slots > 0x7FFFFFFF - frame_guard - 4) { + *ZFP = FTOP; frame_overflow(); return 0; + } + if (size < slots + frame_guard + 4) size = slots + frame_guard + 4; + p = new_oz(size); + p[-4] = p[-4] | F_INTERNAL | F_STACK; + if (gc_active) p[-4] = p[-4] | 2; + p[0] = oldfp; p[1] = frame_floor; p[2] = frame_top; p[3] = frame_node; + frame_floor = p + 4; frame_top = p + p[-5] - HDR; frame_node = p; + frame_chunk = size; + frame_busy = 0; + return frame_top - slots; +} +// only the final frame in a segment calls this helper. the helper's own +// epilogue still uses that segment; the caller stores the restored pointer. +unchecked word frame_leave() { + word p = frame_node; int oldfp = p[0]; + frame_busy = 1; + frame_floor = p[1]; frame_top = p[2]; frame_node = p[3]; + bdrop(p - HDR); + frame_busy = 0; + return oldfp; +} +// report that the heap is out of memory, and stop. +int oom() { + outmode = 0; outgrow = 0; + put_s("zc: out of heap memory\n"); + halt(); + return 0; +} +// n words, untyped and not zeroed. the type word keeps -(n + 1), so fatten +// can type it later. typed new goes through new_t. o3 new(n) goes through +// new_oz, which zeroes the words that were asked for. +unchecked int .u new_raw(int n) { + int need = n + HDR, k = 3; word b; int p; + if (n > 0x7FFFFFFF - HDR) { oom(); return 0; } + if (n < 0) need = HDR; + while (pow2[k] < need) k++; + b = balloc(k); + if (!b) { + // collect first; if that leaves the heap more than three quarters full (or + // still short), take more memory from the host + if (gc_auto) { + gc(); + b = balloc(k); + if (HEAP_GROW && hfree() < (hstop - HEAP_LO) / 4) heap_grow(k); + } + if (!b) { heap_grow(k); b = balloc(k); } + if (!b) oom(); + } + p = b + HDR; + b[1] = (k * pow2[16]) | 1; b[2] = p ^ MAGIC; b[3] = 0; b[4] = 0 - n - 1; + // the words past n are not the object's. zero them so a later walk + // doesn't follow garbage. the n words themselves stay uninitialized. + if (n > 0 && b[0] - HDR > n) fill_w(p + n, 0, b[0] - HDR - n); + else if (n <= 0) fill_w(p, 0, b[0] - HDR); + return p; +} +// n words, untyped, zeroed. the type word stays -(n + 1). new_raw does not +// zero the words that were asked for, so typed new does not zero them twice. +unchecked int .u new_oz(int n) { + int p = new_raw(n); + if (n > 0) fill_w(p, 0, n); + return p; +} +// n words typed t, zeroed. memdebug records the line the compiler stored in alloc_line +unchecked word .u new_t(int n, int t) { + word p = new_raw(n); + p[-1] = t; + fill_w(p, 0, p[-5] - HDR); + if (dbg & 1) note_alloc(p, alloc_line); + return p; +} +unchecked word .u new(int n) { return new_t(n, 0); } +// the compiler works out the size class of a constant-size new at compile +// time (k is the class of n + HDR words), so a repeated allocation of the same +// shape pays for no class search. when the class list has a block, pop it +// directly; a free block already has the right size and an empty payload to +// refill. in a loop that frees its object every pass (an m local) this is the +// block that was just recycled, so the pair costs about a dozen steps. +// memdebug, an empty list, or a block inside the held range (balloc's rule +// for the compiler's type area) fall back to the general path. +unchecked word .u new_k(int n, int t, int k) { + word b = freel[k], p, nx; + if (!b || dbg || (hold_hi && b < hold_hi && b + pow2[k] > hold_lo)) return new_t(n, t); + nx = b[5]; + freel[k] = nx; + if (nx) nx[6] = 0; + p = b + HDR; + b[1] = (k * pow2[16]) | 1; b[2] = p ^ MAGIC; b[3] = 0; b[4] = t; + if (k == 3) { p[0] = 0; p[1] = 0; p[2] = 0; } // the smallest block: three payload words + else fill_w(p, 0, pow2[k] - HDR); + return p; +} +// the size in words of the heap object p, as new asked for it. +int osize(word p) { + if (!isobj(p)) return 0; + return p[-5] - HDR; +} +// the type record stamped on the heap object p by new(T), or 0 when there is none. +int typeof_obj(word p) { + if (!isobj(p) || p[-1] < 0) return 0; + return p[-1]; +} + +// how many payload words a free walk should follow. a raw object only owns +// the words new asked for; the slack past that was never zeroed. +int paylen(word p) { + int n = p[-5] - HDR, req; + if (p[-1] < 0) { req = 0 - p[-1] - 1; if (req < n) n = req; } + return n; +} +// frees p and, recursively, every object p owns. the block stays quarantined. +int release(word p) { + word b = p - HDR; int n = paylen(p), i = 0; word w; + b[1] = (b[1] & ~7) | 2; // going: not live, and not free yet either, so a child's buddy check can't merge into it + while (i < n) { + i = i + scan_ptr(p + i, n - i); + if (i >= n) break; + w = p[i]; if (isobj(w) && !(w[-4] & F_SHARE) && w[-2] == p) release(w); + i++; + } + b[1] = b[1] & ~7; // quarantined. the check word and the bytes stay; the next sweep reclaims it + return 0; +} +// frees a heap object: p has to be tagged d, so a call is free(d*p). the object is +// not reused at once. it is kept in quarantine (a stale pointer still reads the old +// contents), and the next collection reclaims it when nothing points at it. a second +// free does nothing. a shared object is left alone. +unchecked int free(.d p) { + int n; + if (isobj(p) && (p[-4] & F_SHARE)) return 0; + if ((dbg & 1) && quar(p)) { + put_s("zy: double free from line "); put_n(site_line_of(p)); put_c(10); + return 0; + } + if (isobj(p)) { + n = paylen(p); + release(p); + if (dbg & 2) fill_w(p, 57005, n); // 0xDEAD, so a stale read is obvious + } + return 0; +} +// the pointer is the last one. children go first, then the block returns to +// its size-class list. no sweep, no buddy walk. +unchecked int recycle(.d p) { + word b; int n, i = 0; word w; + if ((dbg & 1) && quar(p)) { + put_s("zy: double free from line "); put_n(site_line_of(p)); put_c(10); + return 0; + } + if (!isobj(p) || (p[-4] & F_SHARE)) return 0; + b = p - HDR; + n = paylen(p); + b[1] = (b[1] & ~7) | 2; + while (i < n) { + i = i + scan_ptr(p + i, n - i); + if (i >= n) break; + w = p[i]; if (isobj(w) && !(w[-4] & F_SHARE) && w[-2] == p) recycle(w); + i++; + } + if (dbg & 2) fill_w(p, 57005, n); + bdrop(b); + return 0; +} +// the matching free for an object the compiler knows has no pointer fields +// (a typed new of a struct of numbers). it checks the type word and the size +// class still match what the compiler assumed, since the variable may have been +// reassigned; if not, or under memdebug, it takes the general route. +unchecked word recycle_leaf(.d p, int t, int k) { + word b = p - HDR; int f; word h; + if (dbg || p < HEAP_LO + HDR || p >= hbump) return recycle(p); + f = b[1]; + if (b[2] != (p ^ MAGIC) || (f & F_LOW) != 1 || p[-1] != t || ((f >> 16) & 31) != k) { + return recycle(p); + } + h = freel[k]; // the block goes on the head of its class list, ready for the next new_k + b[2] = 0; b[1] = k * pow2[16]; b[5] = h; b[6] = 0; + if (h) h[6] = b; + freel[k] = b; + return 0; +} +// make p the owner of c, so that freeing p also frees c. it returns c, so the call +// composes: h[0] = own(b, h). a shared object has no owner and is left alone. +word own(word c, int p) { + if (isobj(c) && !(c[-4] & F_SHARE)) c[-2] = p; + return c; +} +// an opt-in bump arena of n words, made as one block. the handle owns that block. +// arena_alloc hands out interior pointers into it and never asks the buddy lists, +// and arena_reset gives all of it back at once. +unchecked word .u arena_new(int n) { + word h = new(3), b; int cap = n; + if (cap < 1) cap = 1; + b = new(cap); + h[0] = own(b, h); + h[1] = cap; + h[2] = 0; + return h; +} +// take n zeroed words from arena a. it returns the address, or 0 when the arena +// does not have n words left. +unchecked int arena_alloc(word a, int n) { + int p, at; + if (!a || n < 0) return 0; + at = a[2]; + if (at + n > a[1]) return 0; + p = a[0] + at; + if (n) fill_w(p, 0, n); + a[2] = at + n; + return p; +} +// give back every block of arena a at once. the words it held are reused by the next +// arena_alloc calls, so pointers into the arena from before are no longer valid. +int arena_reset(word a) { + if (a) a[2] = 0; + return 0; +} +// publish p. a shared object has no owner: free does nothing to it, copies of its +// pointer are ordinary words, and the collector reclaims it once every copy is gone. +// returns p. +unchecked word share(word p) { + if (!isobj(p)) return p; + p[-4] = (p[-4] & ~7) | 1 | F_SHARE; + p[-2] = 0; + return p; +} +// true when p is a shared object, which free leaves alone. +int is_shared(word p) { + if (!isobj(p)) return 0; + if (p[-4] & F_SHARE) return 1; + return 0; +} +// make an immutable pair of a and b. a heap object that is stored in either side is +// published too, so a later free from its old owner cannot clear a cell that still +// points at it. read with car and cdr. +unchecked word cons(int a, int b) { + word p = new(2); + share(a); share(b); + p[0] = a; p[1] = b; + return share(p); +} +// make an immutable record of three words, a, b and c. read the words with tget. +unchecked word triple(int a, int b, int c) { + word p = new(3); + share(a); share(b); share(c); + p[0] = a; p[1] = b; p[2] = c; + return share(p); +} +// the first word of a pair made by cons. +unchecked word car(word p) { + if (dbg & 1) checkptr(p); + return p[0]; +} +// the second word of a pair made by cons. +unchecked word cdr(word p) { + if (dbg & 1) checkptr(p); + return p[1]; +} +// word i (0, 1 or 2) of a triple made by triple. +unchecked word tget(word p, int i) { + if (dbg & 1) checkptr(p); + return p[i]; +} + +// ---- growable arrays ---- +// a []T is a handle: [len, cap, items, element size]. items is an object the +// handle owns; growing swaps it for a bigger one and moves its children over. +unchecked word .u dnew(int es, int n, int t) { + word h = new_t(4, t); int c = n; word it; + if (c < 4) c = 4; + if (es < 0) it = new((c * (0 - es) + 7) >> 3); else it = new(c * es); // a negative size is a packed element of that many bytes + h[0] = n; h[1] = c; h[2] = own(it, h); h[3] = es; + return h; +} +// grow the growable array h so that it can hold need elements. +unchecked int dgrow(word h, int need) { + int c = h[1]; word it; int old = h[2], i = 0; word w; int n, es = h[3]; + if (need <= c) return 0; + while (c < need) c = c + c; + if (es < 0) { it = new((c * (0 - es) + 7) >> 3); n = (h[0] * (0 - es) + 7) >> 3; es = 0; } + else { it = new(c * es); n = h[0] * es; } + copy_w(it, old, n); + // the children move with the elements (packed bytes have none) + while (es && i < n) { + w = it[i]; + if (isobj(w) && w[-2] == old) w[-2] = it; + i++; + } + h[2] = own(it, h); h[1] = c; + free(old); + return 0; +} +// the out-of-range report for []T indexing (dat, dpop) +int oob(int i, int n) { + put_s("zy: index "); + put_n(i); + put_s(" out of range (length "); + put_n(n); + put_s(")\n"); + halt(); + return 0; +} +// element i of a growable array h. indexing with [] does the same, with the bounds +// check. the bounds check is what oob reports when i is outside the array. +unchecked word dat(word h, int i) { + if (i < 0 || i >= h[0]) oob(i, h[0]); + return h[2] + i * h[3]; +} +// add x to the end of a growable array h, growing its items when they are full. the +// array owns the items, so x is handed to it (tagged d). +unchecked int dpush(word h, .d x) { + dgrow(h, h[0] + 1); + h[2][h[0]] = x; + own(x, h[2]); + h[0] = h[0] + 1; + return 0; +} +// remove the last element of a growable array h and give it back. the array +// must not be empty. +unchecked word dpop(word h) { + if (!h[0]) oob(-1, 0); + h[0] = h[0] - 1; + return h[2][h[0]]; +} +// set the length of a growable array h to n. growing it fills the new elements with +// zero, and shrinking it drops the elements past n. +unchecked int dresize(word h, int n) { + int old = h[0] * h[3]; + if (n < 0) n = 0; + dgrow(h, n); + if (n * h[3] > old) fill_w(h[2] + old, 0, n * h[3] - old); + h[0] = n; + return 0; +} +// packed elements (h[3] is minus their size in bytes): the address is a byte address +unchecked word datb(word h, int i) { + if (i < 0 || i >= h[0]) oob(i, h[0]); + return addr_b(h[2]) + i * (0 - h[3]); +} +// add the byte x to the end of a growable byte array h. +unchecked int dpushb(word h, int x) { + int b = 0 - h[3], a; + dgrow(h, h[0] + 1); + a = addr_b(h[2]) + h[0] * b; + if (b == 1) st8(a, x); else if (b == 2) st16(a, x); else st32(a, x); + h[0] = h[0] + 1; + return 0; +} +// remove and return the last byte of a growable byte array h. +unchecked int dpopb(word h) { + int b = 0 - h[3], a; + if (!h[0]) oob(-1, 0); + h[0] = h[0] - 1; + a = addr_b(h[2]) + h[0] * b; + if (b == 1) return ld8(a); + if (b == 2) return ld16(a); + return ld32(a); +} +// growing zeroes the new elements, including the unused tail of the last word +unchecked int dresizeb(word h, int n) { + int b = 0 - h[3], a, e, old = (h[0] * b + 7) >> 3, nw; + if (n < 0) n = 0; + if (n > h[0]) { + dgrow(h, n); + a = addr_b(h[2]) + h[0] * b; + e = addr_b(h[2]) + old * 8; + if (e > addr_b(h[2]) + n * b) e = addr_b(h[2]) + n * b; + while (a < e) { st8(a, 0); a++; } + nw = (n * b + 7) >> 3; + if (nw > old) fill_w(h[2] + old, 0, nw - old); + } + h[0] = n; + return 0; +} +// the length of a growable array, the number of elements it holds. +int dlen(word h) { return h[0]; } +// the capacity of a growable array: how many elements it can hold before it grows. +int dcap(word h) { return h[1]; } + +// ---- mark-sweep ---- +// conservative and non-moving. roots: the program area (globals live +// between functions), the genome's globals, the compiler's input stack, +// the live part of the frame stack and the live part of the data stack. +// opcode 619 pushes that index. a word below it is not a root. +int ms_top, ms_over, gc_lo, gc_hi; +word ms_buf = MARK0; int markcap = MARKN; +// mark work must grow without starting another collection inside this one. +unchecked int reserve_mark(int need) { + int cap = markcap, auto = gc_auto; word p; + if (need <= cap) return cap; + while (cap < need) cap = cap * 2; + gc_auto = 0; p = new_oz(cap); gc_auto = auto; + copy_w(p, ms_buf, ms_top); + p[-4] = p[-4] | F_INTERNAL; + if (gc_active) p[-4] = p[-4] | 2; + ms_buf = p; markcap = cap; return cap; +} +// mark one word. a word that is not the payload address of a block is not a +// pointer and is ignored (conservative). a live block gets its mark bit and +// goes on the mark stack to have its payload scanned. a quarantined block that +// something still points at is flagged 4 so the sweep keeps it quarantined +// instead of reusing it. the work stack grows before appending another block. +int mark(word w) { + int p = 0; + if (w < HEAP_LO + HDR || w >= hbump || w[-3] != (w ^ MAGIC)) { + if (dbg & 4) { p = obj_base(w); if (p) return mark(p); } + return 0; + } + if (w[-4] & 1) { if (w[-4] & 2) return 0; w[-4] = w[-4] | 2; } // live + else if ((w[-4] & F_LOW) == 0) w[-4] = w[-4] | 4; // quarantined, but still pointed at + else return 0; + if (w == ms_buf || (w[-4] & F_STACK)) return 0; + if (ms_top >= markcap) reserve_mark(markcap * 2); + ms_buf[ms_top] = w; ms_top++; + return 0; +} +// mark every word in [a, b). note *a is a plain load of the word at a. +int markrange(word a, int b) { + if (dbg & 4) { while (a < b) { mark(*a); a++; } return 0; } // interior pointers: every word counts + while (a < b) { + a = a + scan_ptr(a, b - a); + if (a >= b) break; + mark(*a); + a++; + } + return 0; +} +// empty the mark stack: scan the payload of each marked block +int drain() { + int p = 0; + while (ms_top) { ms_top--; p = ms_buf[ms_top]; ms_buf[ms_top] = 0; markrange(p, p + p[-5] - HDR); } + return 0; +} +// stack blocks are marked without scanning their unused payload. each saved +// segment records the caller's live range and the previous segment's node. +int gc_frames() { + word p = frame_node; + markrange(*ZFP, frame_top); + while (p) { mark(p); markrange(p[0], p[2]); p = p[3]; } + return 0; +} +// gc_roots is defined in the compiler half (it knows where the compiler's +// own tables live); it calls markrange over every root range +int gc_roots(); +// runs a collection: the blocks nothing points at are reclaimed, quarantined blocks +// included, and the number of blocks freed is returned. cycles are reclaimed too. +unchecked int gc() { + word b = 0; int sz = 0, n = 0; // a leftover pointer here would be a root and pin a block + ms_top = 0; ms_over = 0; gc_active = 1; + gc_roots(); + drain(); + while (ms_over) { // the mark stack overflowed: rescan marked objects + ms_over = 0; + b = HEAP_LO; + while (b < hstop) { + if ((b[1] & 7) == 3 || (b[1] & 7) == 4) markrange(b + HDR, b + b[0]); + b = b + b[0]; + drain(); + } + } + b = HEAP_LO; + while (b < hstop) { + sz = b[0]; + if ((b[1] & 7) == 1) { bfree(b); n++; b = fend; } // unmarked live object, shared or not + else if ((b[1] & 7) == 3) { b[1] = (b[1] & ~7) | 1; b = b + sz; } + else if ((b[1] & 7) == 4) { b[1] = b[1] & ~7; b = b + sz; } // still pointed at: stays quarantined + else if (!(b[1] & 7) && b[2] == ((b + HDR) ^ MAGIC)) { bfree(b); n++; b = fend; } + else b = b + sz; + } + gc_runs++; + gc_active = 0; + gc_freed = gc_freed + n; + return n; +} +// the number of live heap objects, for tests and leak checks. +int live_objects() { + word b = HEAP_LO; int n = 0; + while (b < hstop) { + if ((b[1] & 1) && !(b[1] & F_INTERNAL)) n++; + b = b + b[0]; + } + return n; +} +// the size, in words, of the biggest block that is free right now. +int largest_free() { + int k = 31; + while (k > 0 && !freel[k]) k--; + if (!freel[k]) return 0; + return pow2[k]; +} +// the data-stack index: the first live word. opcode 619, word 1238. +int dsp() { return asm { #1238 }; } +// zero the unused data stack, from the bottom up to the live top. the last +// 16 words are the ones this loop is using, so they are pushed as zeros. +int scrub() { + int top; word p; + top = dsp(); + p = data_low; + if (top < data_low + 16) return 0; + while (p + 16 < top) { *p = 0; p++; } + asm { 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 }; + asm { _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ }; + return 0; +} + +// ---- memory debug ---- +// dbg bits: 1 header checks and allocation sites, 2 poison the payload on free +// (the word 57005, which is 0xDEAD), 4 interior pointers keep a quarantined +// block (the mark walks back at most 64 words to a real header). off unless +// a program asks. sites are p ^ MAGIC so the collector doesn't see them. +const int NSITE = 64; +const int POISON = 57005; +int site_drop; +[64]word site_p; [64]word site_line; +// true when p is a freed block still held in quarantine. +word quar(word p) { return p >= HEAP_LO + HDR && p < hbump && p[-3] == (p ^ MAGIC) && !(p[-4] & 1); } +// the object whose payload contains w, or 0. exact pointers and interiors. +word obj_base(int w) { + int i = 0; word p; int n; + if (w < HEAP_LO + HDR || w >= hbump) return 0; + while (i <= 64) { + p = w - i; + if (p >= HEAP_LO + HDR && p < hbump && p[-3] == (p ^ MAGIC)) { + n = p[-5] - HDR; + if (w >= p && w < p + n) return p; + } + i++; + } + return 0; +} +// the source line recorded for the allocation at p. +word site_line_of(int p) { + int i = 0, k = p ^ MAGIC; + while (i < NSITE) { if (site_p[i] == k) return site_line[i]; i++; } + return 0; +} +// record the source line of the allocation of p, for the leak report. +int note_alloc(int p, int line) { + int i = 0, k, slot = -1, q; + if (!(dbg & 1) || !p) return 0; + k = p ^ MAGIC; + while (i < NSITE) { + if (site_p[i] == k) { site_line[i] = line; return 0; } + if (slot < 0 && !site_p[i]) slot = i; + i++; + } + i = 0; + while (i < NSITE) { + q = site_p[i] ^ MAGIC; + if (site_p[i] && !isobj(q) && !quar(q)) { slot = i; i = NSITE; } + else i++; + } + if (slot < 0) { site_drop = 1; return 0; } + site_p[slot] = k; site_line[slot] = line; + return 0; +} +// turns memdebug's checks on or off with a bit set: bit 1 checks headers and reports +// use after free, and the other bits add allocation lines and poison. a program +// turns them on with memdebug on; and the runtime calls this with the flags. +int mem_set(int flags) { dbg = flags; return flags; } +// a heap address used while dbg bit 1 is on: a dead header is a use after free. +int checkptr(int w) { + word p; + if (!(dbg & 1) || !w) return 0; + if (w < HEAP_LO || w >= hstop) return 0; + p = obj_base(w); + if (!p) { put_s("zy: bad heap pointer\n"); halt(); return 0; } + if (!(p[-4] & 1)) { + put_s("zy: use after free from line "); put_n(site_line_of(p)); put_c(10); + halt(); + } + return 0; +} +// prints the live objects and the lines they were allocated on, the leak report that +// memdebug produces at the end of a run. +int memreport() { + int i = 0, p, n = 0; + if (site_drop) put_s("mem: site table full\n"); + while (i < NSITE) { + if (site_p[i]) { + p = site_p[i] ^ MAGIC; + if (isobj(p)) { + put_s("leak: line "); put_n(site_line[i]); put_s(" "); put_n(osize(p)); put_s(" words\n"); + n++; + } + } + i++; + } + return n; +} + +// ---- host syscalls ---- +// a store of the op number to SYSGATE runs it. the three words after the gate +// are the arguments, and the store leaves the result in the gate. +// 1 open(path, 0 read | 1 write-create) 2 close(fd) +// 3 read(fd, buf, n) 4 write(fd, buf, n) bytes, one per word +// 5 getenv(name, buf, cap) 6 time() unix seconds +// 21 argc() 22 argv(i, buf, cap) 23 envc() 24 envp(i, buf, cap) +// 40 raw(block) any linux syscall: the block holds the number and six arguments. +// the result is the kernel's, or -errno. see Host.raw in src/lib/std.zc +const int SYSGATE = ZFP + 2; +const int SYS_OPEN = 1, SYS_CLOSE = 2, SYS_READ = 3, SYS_WRITE = 4, SYS_GETENV = 5, SYS_TIME = 6; +const int SYS_BOPEN = 10, SYS_BREAD = 11, SYS_BWRITE = 12, SYS_BGETENV = 13; +const int SYS_RAW = 40; +// the command line and the environment. argv holds the program path, which is +// when the source came from stdin, so argc is 1 even with no arguments. +// the string ops take an index and copy one entry across, one character per +// word like SYS_GETENV: a zero buffer asks for the length alone, so the caller +// can size the string before it fetches it. src/lib/std.zc builds the arrays. +const int SYS_ARGC = 21, SYS_ARG = 22, SYS_ENVC = 23, SYS_ENV = 24; +// byte-string paths. the address is the first byte (addr_b of the word after +// the length), not the string object. word-string syscalls are unchanged. +int bopen(word path, int mode) { return syscall(SYS_BOPEN, addr_b(path + 1), mode, 0); } +// read up to n bytes from fd into the packed byte string s. +int bread(int fd, word s, int n) { return syscall(SYS_BREAD, fd, addr_b(s + 1), n); } +// write the first n bytes of the packed byte string s to fd. +int bwrite(int fd, word s, int n) { return syscall(SYS_BWRITE, fd, addr_b(s + 1), n); } +// copy the value of the environment variable name into dst, returning its length. +int bgetenv(word name, word dst, int n) { return syscall(SYS_BGETENV, addr_b(name + 1), addr_b(dst + 1), n); } +// ---- c abi ---- +// ops 14..17 reach native code (see the c abi section of core.c and extern in +// compiler.zc). cfind(name) is 1 if a native symbol resolves (name is a byte +// string), cload(path) opens a library so its symbols resolve, and +// cbmake(entry, sig) is a native function pointer that runs the zc function at +// entry (cfunc(f) in the compiler builds the signature). a native pointer +// from cbmake only works while a native call is in progress, and not on the +// arm64 jit, where it is 0. +const int SYS_CCALL = 14, SYS_CFIND = 15, SYS_CBMAKE = 16, SYS_CLOAD = 17; +// the address of the native symbol name in the loaded libraries, or 0 when it is not +// loaded. see cload. +int cfind(word name) { return syscall(SYS_CFIND, addr_b(name + 1), 0, 0); } +// loads the shared library at path, so the symbols it defines can be found with cfind. +int cload(word path) { return syscall(SYS_CLOAD, addr_b(path + 1), 0, 0); } +// makes a native function pointer that runs the vm entry with the signature sig, +// so c code can call a zc function through it. +int cbmake(int entry, int sig) { return syscall(SYS_CBMAKE, entry, sig, 0); } +// calls the host through the gate word, with the op number and up to three arguments. +// the result comes back as the call's return value (negative for an error). prefer the +// named ops (SYS_OPEN and the rest) to the raw numbers. +int syscall(int op, word a, word b, word c) { + word g = SYSGATE; + g[1] = a; g[2] = b; g[3] = c; g[0] = op; + return g[0]; +} +// the whole file as a fresh string, or 0 if it can't be opened +unchecked word .u readfile(int path) { + int fd; word buf; int n = 0, got, cap; word nb; + fd = syscall(SYS_OPEN, path, 0, 0); + if (fd < 0) return 0; + cap = 256; + buf = new(cap); + while (1) { + if (n + 128 >= cap) { + nb = new(cap + cap); + copy_w(nb, buf, n); + free(buf); + buf = nb; + cap = cap + cap; + } + got = syscall(SYS_READ, fd, buf + n, 128); + if (got <= 0) break; + n = n + got; + } + buf[n] = 0; + syscall(SYS_CLOSE, fd, 0, 0); + return buf; +} diff --git a/src/runtime/run.zc b/src/runtime/run.zc new file mode 100644 index 0000000..60193a7 --- /dev/null +++ b/src/runtime/run.zc @@ -0,0 +1,27 @@ +// run-only image: the runtime, a collector root set, and start. there is no +// resident compiler and none of its tables. put this between rt.zc and a +// program that defines main, and compile with zc0 (the program has to stay in +// the stage-0 subset: no types, no quote, no parse words). +// +// cat build/run/rt.zc src/runtime/run.zc examples/hello.zc | ./zc0 >build/run/genome.h + +// the program's entry point, written by the program (see start). +int main(); + +// the collector's roots: the code and globals, the program's frames and the +// operand stack. the collector marks from these before it frees anything. +int gc_roots() { + markrange(0, CODE_END); + markrange(GORG, GORG[6]); + gc_frames(); + markrange(dsp(), data_top); + return 0; +} + +// the first code a run-only image executes: it sets up the heap, runs main and halts. +int start() { + heap_init(); + main(); + halt(); + return 0; +} diff --git a/src/tools/automap.sh b/src/tools/automap.sh new file mode 100755 index 0000000..8cefe6a --- /dev/null +++ b/src/tools/automap.sh @@ -0,0 +1,58 @@ +#!/bin/bash +# sizes a memory map to the program it holds, before the program is compiled for it. +# src/tools/automap.sh PRESET OUTDIR FILE... +# PRESET is run, small, default or lsp. DOCS=1 makes the genome with its doc comments. FILE... are appended after the map's own +# rt.zc (and compiler.zc, for the presets that carry the compiler). the result +# is OUTDIR/memmap.h and OUTDIR/genome.h, and they agree with each other. +# +# a genome's length doesn't depend on where it lives (every address is a one +# word literal), so two passes are enough: compile once on the preset's map +# to learn the length, then lay the map out again with a genome slot of exactly +# that length, rounded up to 4096 words, and let a fixed-size map grow if it has +# to. the second compile must come out the same length, and that is checked. +# zc0 reads the map at build time, so it is built again for each layout. +# GENMAP can name a genmap that is already built. +set -e +cd "$(dirname "$0")/../.." +P=$1; O=$2; shift 2 +[ -n "$P" ] && [ -n "$O" ] || { echo "usage: src/tools/automap.sh run|small|default|lsp OUTDIR FILE..." >&2; exit 2; } +CC=${CC:-gcc} +mkdir -p "$O" +ZC0=$O/.zc0 +if [ -z "$GENMAP" ]; then + GENMAP=$O/.genmap + if [ ! -x "$GENMAP" ] || [ src/tools/genmap.c -nt "$GENMAP" ]; then $CC -O2 -w -fwrapv src/tools/genmap.c -o "$GENMAP"; fi +fi +lay() { # $1 = genome slot, or empty for the preset's own + rm -rf "$O/map" + "$GENMAP" --preset "$P" --grow ${1:+--genome $1} -o "$O/map" >/dev/null 2>&1 + $CC -O1 -w -fwrapv src/tools/zc0.c -I"$O/map" -Isrc/core -o "$ZC0" +} +build() { + if [ -f "$O/map/compiler.zc" ]; then cat "$O/map/rt.zc" "$O/map/compiler.zc" "$@"; else cat "$O/map/rt.zc" "$@"; fi | "$ZC0" +} +# with DOCS=1 the genome is made again with zc0 built for docs (ZC0DOCS), so the +# image carries the doc comments of the sources and the program can answer +# docofs(). the image is the same code either way; the docs are stored after it. +docs() { + $CC -O1 -w -fwrapv -DZC0DOCS src/tools/zc0.c -I"$O/map" -Isrc/core -o "$O/.zc0docs" + if [ -f "$O/map/compiler.zc" ]; then cat "$O/map/rt.zc" "$O/map/compiler.zc" "$@"; else cat "$O/map/rt.zc" "$@"; fi | "$O/.zc0docs" +} +len() { grep -m1 '#define GLEN' "$1" | awk '{print $3}'; } +lay "" +build "$@" > "$O/genome.h" +glen=$(len "$O/genome.h") +slot=$(( (glen + 4095) / 4096 * 4096 )) +lay "$slot" +build "$@" > "$O/genome.h" +if [ -n "$DOCS" ]; then + lay $((slot + 65536)) # room for the image with its docs while it is being made + docs "$@" > "$O/genome.h" + glen=$(len "$O/genome.h") + slot=$(( (glen + 4095) / 4096 * 4096 )) + lay "$slot" + docs "$@" > "$O/genome.h" +fi +[ "$(len "$O/genome.h")" = "$glen" ] || { echo "automap: the genome changed length with the map" >&2; exit 1; } +cp "$O/map/memmap.h" "$O/map/map.txt" "$O/" +echo "automap $P: genome $glen words in a slot of $slot, $(grep -m1 '^MW' "$O/map/map.txt")" diff --git a/src/tools/build.zc b/src/tools/build.zc new file mode 100644 index 0000000..547b70f --- /dev/null +++ b/src/tools/build.zc @@ -0,0 +1,14 @@ +// compiles the rest of stdin (after the backtick) as a genome image at GORG +// and writes it to stdout as little-endian 64-bit words. +docs on; +// writes the compiled genome image to stdout as little-endian 64-bit words. +int main() { + int n = build_image_grow(GORG, 4096), i = 0, w; + while (i < n) { + w = image_words[i]; + put_c(w); put_c(w >> 8); put_c(w >> 16); put_c(w >> 24); + put_c(w >> 32); put_c(w >> 40); put_c(w >> 48); put_c(w >> 56); + i++; + } + return 0; +} diff --git a/src/tools/genmap.c b/src/tools/genmap.c new file mode 100644 index 0000000..dc1971c --- /dev/null +++ b/src/tools/genmap.c @@ -0,0 +1,858 @@ +/* one layout for every memory size. addresses in a genome are literals, so + each size is its own image. this tool writes that image's memmap.h and + the runtime (and, for a compiler build, compiler.zc) with the same const + names as the checked-in 1,048,576-word sources. + + run from the repo root: + + cc -O2 src/tools/genmap.c -o genmap + ./genmap --preset small -o build/small + ./genmap --preset run -o build/run + ./genmap --preset default --check + + ./genmap --mode compiler --heap-lo 8192 --heap 20480 --types 8192 --genome 245760 --frame 2048 --return 512 --mark 512 --data 3072 --snap-cap 1000 --quote-cap 300 -o build/small + ./genmap --mode run --words 20480 -o build/run + + --preset default puts the code area, the compiler tables, the genome and + the stacks first and the heap last, so the heap can grow at run time up to + the total memory size (PW is where the fixed part ends; the host's MW is + the total). --preset fixed is the historical 1M map (a fixed heap below + the frame pointer, types at the top of it). --mode packs from the bottom. omitting + --words rounds MW up to a multiple of 4096, which is what the arm64 jit + needs in order to encode cmp xn, #MW. the checked-in sources stay the + default map; --check fails if they drift from that preset. */ +#include +#include +#include +#include +#include +#include +#ifdef _WIN32 +#include +#endif + +static int align_up(int x, int a) { return (x + a - 1) & ~(a - 1); } + +static void die(const char *m) { fprintf(stderr, "genmap: %s\n", m); exit(1); } + +static int parse_num(const char *s) +{ + char *e; + long v; + if (!s || !*s) die("missing number"); + v = strtol(s, &e, 0); + if (*e || v <= 0 || v > 0x7fffffff) die("bad number"); + return (int)v; +} + +static char *slurp(const char *path) +{ + FILE *f = fopen(path, "rb"); + long n; char *b; + if (!f) { perror(path); exit(1); } + if (fseek(f, 0, SEEK_END) || (n = ftell(f)) < 0 || fseek(f, 0, SEEK_SET)) die("seek"); + b = malloc((size_t)n + 1); + if (!b || fread(b, 1, (size_t)n, f) != (size_t)n) die("read"); + b[n] = 0; + fclose(f); + return b; +} + +static void emit_file(const char *path, const char *s) +{ + FILE *f = fopen(path, "wb"); + if (!f) { perror(path); exit(1); } + fputs(s, f); + if (fclose(f)) die("write"); +} + +/* replace the text between the line that is `begin` and the next line that + is `end`. the marker lines stay. */ +static char *splice(const char *src, const char *begin, const char *end, const char *repl) +{ + const char *a = strstr(src, begin), *b; + char *out; size_t n, nr; + if (!a) { fprintf(stderr, "genmap: missing %s\n", begin); exit(1); } + a += strlen(begin); + if (*a == '\n') a++; + b = strstr(a, end); + if (!b) { fprintf(stderr, "genmap: missing %s\n", end); exit(1); } + nr = strlen(repl); + n = (size_t)(a - src) + nr + strlen(b) + 1; + out = malloc(n); + if (!out) die("oom"); + memcpy(out, src, (size_t)(a - src)); + memcpy(out + (a - src), repl, nr); + strcpy(out + (a - src) + nr, b); + return out; +} + +static char *replace_all(const char *src, const char *from, const char *to) +{ + const char *p = src, *hit; + size_t nf = strlen(from), nt = strlen(to), n = 0, count = 0; + char *out, *w; + while ((hit = strstr(p, from))) { count++; p = hit + nf; } + if (!count) { fprintf(stderr, "genmap: missing %s\n", from); exit(1); } + out = malloc(strlen(src) + count * (nt - nf) + 1); + if (!out) die("oom"); + p = src; w = out; + while ((hit = strstr(p, from))) { + memcpy(w, p, (size_t)(hit - p)); w += hit - p; + memcpy(w, to, nt); w += nt; + p = hit + nf; + n++; + } + strcpy(w, p); + (void)n; + return out; +} + +typedef struct { + int MW, HEAP_LO, HEAP_HI, ZFP, GORG, FBOT, FTOP, RS0, MARK0, MARKN, DSLO, DSHI; + int TW, CODE_END, GROW; /* TW: total words (MW is where the fixed map ends); CODE_END: program words end */ + int IHN, R_IH, R_POOL, R_POOLEND, NS, R_SYM; + int sname, skind, sval, ssig, sflag, sfix, snext; + int NL, lname, lslot, lhome, lstate, lowner, lused, larr, lown; + int NF, fsite, fnext, R_SNAP, R_QBUF, R_SRC, R_SEQ; + int bname, bprec, bseq, blen, uname, useq, ulen, aname, abin, kops; + int R_CTX, R_LOOP, R_ASM, cend, glen_need, typesz; + int ASM_LABELS, ASM_REFS, ASM_WORDS, EXIT_WORDS; + int ENUM_MEMBERS, TAGGED_ENUMS, STRUCT_METHODS, GENERATED_WORDS; + int RANGE_FACTS, JOIN_PATHS, O3_REGIONS; + const char *name; +} Map; + +/* what actually differs between builds. zero means "use the mode default" + except where a preset fills the field itself before layout. */ +typedef struct { + const char *name; + int compiler; + int words; + int heap_lo, heap, types, zfp_pad; + int heap_top, code_end, total; /* heap above the stacks, free to grow up to total words */ + int genome, frame, ret, mark, data; + int ihn, pool, ns, nl, nf, snap, qbuf, src, seq, ctx, loop; + int snap_cap, quote_cap; /* 0 keeps the source caps (8000 and 2700) */ + int binary, unary, assignments, keywords; + int asm_labels, asm_refs, exits; + int enum_members, tagged_enums, struct_methods, generated_words; + int facts, paths, regions; + int type_formula; /* 0 keeps ZFP-16384; else the copy says ZFP-N */ +} Spec; + +static void need(int cond, const char *m) { if (!cond) die(m); } + +static void check_common(const Map *m) +{ + need(m->MW && (m->MW % 4096) == 0, "MW must be a positive multiple of 4096"); + need(m->HEAP_LO >= 256 && m->HEAP_HI > m->HEAP_LO, "heap"); + need(m->HEAP_HI - m->HEAP_LO >= 8 && ((m->HEAP_HI - m->HEAP_LO) & 7) == 0, "heap size must be a multiple of eight words"); + if (m->GROW) { + need(m->HEAP_LO == m->MW, "a growing heap starts where the fixed map ends"); + need(m->TW >= m->HEAP_HI && (m->TW % 4096) == 0, "total memory"); + need(m->CODE_END >= 256 && m->ZFP >= m->CODE_END && m->ZFP + 8 < m->MW, "frame pointer"); + } else { +/* the historical map sets ZFP == HEAP_HI: the type area overlaps the heap */ + need(m->ZFP >= m->HEAP_HI && m->ZFP + 8 < m->MW, "frame pointer"); + } + need(m->GORG > m->ZFP + 8, "genome overlaps the frame pointer"); + need(m->GORG + m->glen_need <= m->FBOT, "genome reservation collides with the frame stack"); + need(m->FBOT + 64 < m->FTOP && m->FTOP == m->RS0, "frame stack"); + need(m->MARK0 >= m->FTOP + 64 && m->MARK0 + m->MARKN <= m->DSLO, "mark stack"); + need(m->DSHI == m->MW - 16 && m->DSHI - m->DSLO >= 1100 + 1024, "data stack"); + need(m->DSLO > m->MARK0, "data stack overlaps the mark stack"); +} + +static void tables_tight(Spec *s) +{ + s->ihn = 2048; s->pool = 16384; s->ns = 1280; s->nl = 512; s->nf = 2048; + s->snap = 1024; s->qbuf = 1024; s->src = 1024; s->seq = 1024; + s->ctx = 1024; s->loop = 512; +} + +/* the default map's tables: room for a large program and for several generic + libraries, in words nobody touches until a program needs them */ +static void tables_big(Spec *s) +{ + s->ihn = 32768; s->pool = 0x40000; s->ns = 16384; s->nl = 512; s->nf = 32768; + s->snap = 16384; s->qbuf = 16384; s->src = 1024; s->seq = 2048; + s->ctx = 2048; s->loop = 1024; +} + +static void tables_historical(Spec *s) +{ + s->ihn = 8192; s->pool = 0x9F00; s->ns = 4096; s->nl = 512; s->nf = 8192; + s->snap = 8192; s->qbuf = 8192; s->src = 1024; s->seq = 2048; + s->ctx = 2048; s->loop = 1024; +} + +static void preset_small(Spec *s) +{ + memset(s, 0, sizeof *s); + s->name = "small"; + s->compiler = 1; + s->heap_top = 1; s->code_end = 8192; s->total = 0x8000000; + s->heap_lo = 8192; s->heap = 20480; s->types = 8192; + s->genome = 245760; s->frame = 2048; s->ret = 512; s->mark = 512; s->data = 3072; + tables_tight(s); + s->snap_cap = 1000; s->quote_cap = 300; s->snap = 1000; s->qbuf = 900; s->type_formula = 8192; +} + +static void preset_run(Spec *s) +{ + memset(s, 0, sizeof *s); + s->name = "run"; + s->heap_top = 1; s->code_end = 512; s->total = 0x8000000; + s->heap_lo = 512; s->heap = 16384; s->zfp_pad = 32; + s->genome = 15600; s->frame = 1024; s->ret = 256; s->mark = 256; s->data = 2560; +} + +static void preset_default(Spec *s) +{ + memset(s, 0, sizeof *s); + s->name = "default"; + s->compiler = 1; + s->heap_top = 1; s->code_end = 0x80000; s->types = 65536; + s->heap = 0x60000; s->total = 0x8000000; + s->genome = 0x50000; s->frame = 0x40000; s->ret = 0x10000; s->mark = 0x8000; + s->data = 0x7FF0; + tables_big(s); +} + +/* the map before the heap moved to the top: a fixed heap between the code and + the frame pointer, the type records at the end of it, 1,081,344 words */ +static void preset_fixed(Spec *s) +{ + memset(s, 0, sizeof *s); + s->name = "fixed"; + s->compiler = 1; + s->words = 0x108000; + s->heap_lo = 0x40000; s->heap = 0x60000; + s->genome = 0x28000; s->frame = 0x10000; s->ret = 0x8000; s->mark = 0x4000; + tables_historical(s); +} + +/* the language server: the compiler's own growing map. the document store, the + diagnostics and the check's sets are heap blocks now, so nothing in the server + is at a fixed address and the heap grows with the documents */ +static void preset_lsp(Spec *s) +{ + preset_default(s); + s->name = "lsp"; +} + +static void mode_defaults(Spec *s, int compiler) +{ + memset(s, 0, sizeof *s); + s->name = compiler ? "compiler" : "run"; + s->compiler = compiler; + if (compiler) { + s->heap_top = 1; s->code_end = 8192; s->total = 0x8000000; + s->heap_lo = 8192; s->heap = 20480; s->types = 8192; s->type_formula = 8192; + s->genome = 245760; s->frame = 2048; s->ret = 512; s->mark = 512; s->data = 3072; + tables_tight(s); + } else { + s->heap_top = 1; s->code_end = 512; s->total = 0x8000000; + s->heap_lo = 512; s->heap = 16384; s->zfp_pad = 32; + s->genome = 15600; s->frame = 1024; s->ret = 256; s->mark = 256; s->data = 2560; + } +} + +/* pack every region from the bottom. a requested MW larger than that keeps + the same bases and gives the spare words to the data stack. */ +static void check_layout_size(const Spec *s) +{ + long long size = (s->heap_top ? s->code_end : s->heap_lo) + (long long)s->heap + s->types + s->zfp_pad; + size += (long long)s->genome + s->frame + s->ret + s->mark + s->data + 8192; + if (s->compiler) { + size += (long long)s->ihn + s->pool + 8LL * s->ns + 10LL * s->nl + 2LL * s->nf; + size += (long long)s->snap + s->qbuf + s->src + s->seq + s->ctx + s->loop; + size += 4LL * (s->binary ? s->binary : 64) + 3LL * (s->unary ? s->unary : 32); + size += 2LL * (s->assignments ? s->assignments : 64) + (s->keywords ? s->keywords : 512); + size += 2LL * (s->asm_labels ? s->asm_labels : 64) + 2LL * (s->asm_refs ? s->asm_refs : 256); + size += s->exits ? s->exits : 2560; + } + need(size <= 0x7fffffff, "layout is too large"); + if (s->words && s->heap_top) need((long long)s->words + s->heap + 4095 <= 0x7fffffff, "heap end is too large"); +} + +static void layout(const Spec *s, Map *m) +{ + int a; + check_layout_size(s); + memset(m, 0, sizeof *m); + m->name = s->name; + m->glen_need = s->genome; + m->typesz = s->types; + if (s->heap_top) { + m->GROW = 1; + m->CODE_END = s->code_end; + m->ZFP = m->CODE_END + s->types + s->zfp_pad; + } else { + m->HEAP_LO = s->heap_lo; + m->HEAP_HI = s->heap_lo + s->heap; + m->CODE_END = m->HEAP_LO; + m->ZFP = m->HEAP_HI + s->types + s->zfp_pad; + } + if (s->compiler) { + m->IHN = s->ihn; m->NS = s->ns; m->NL = s->nl; m->NF = s->nf; + a = m->ZFP + 256; + m->R_IH = a; a += m->IHN; + m->R_POOL = a; a += s->pool; m->R_POOLEND = a; a += 256; + m->R_SYM = m->sname = a; a += m->NS; + m->skind = a; a += m->NS; + m->sval = a; a += m->NS; + m->ssig = a; a += m->NS; + m->sflag = a; a += m->NS; + m->sfix = a; a += m->NS; + m->snext = a; a += m->NS; + m->lname = a; a += m->NL; + m->lslot = a; a += m->NL; + m->lhome = a; a += m->NL; + m->lstate = a; a += m->NL; + m->lowner = a; a += m->NL; + m->lused = a; a += m->NL; + m->larr = a; a += m->NL; + m->lown = a; a += m->NL; + m->fsite = a; a += m->NF; + m->fnext = a; a += m->NF; + m->R_SNAP = a; a += s->snap; + m->R_QBUF = a; a += s->qbuf; + m->R_SRC = a; a += s->src; + m->R_SEQ = a; a += s->seq; + { + int binary = s->binary ? s->binary : 64, unary = s->unary ? s->unary : 32; + int assignments = s->assignments ? s->assignments : 64, keywords = s->keywords ? s->keywords : 512; + m->bname = a; a += binary; + m->bprec = a; a += binary; + m->bseq = a; a += binary; + m->blen = a; a += binary; + m->uname = a; a += unary; + m->useq = a; a += unary; + m->ulen = a; a += unary; + m->aname = a; a += assignments; + m->abin = a; a += assignments; + a += 32; + m->kops = a; a += keywords; + } + m->R_CTX = a; a += s->ctx; + m->R_LOOP = a; a += s->loop; + m->R_ASM = a; + m->ASM_LABELS = s->asm_labels ? s->asm_labels : 64; + m->ASM_REFS = s->asm_refs ? s->asm_refs : 256; + { + long long words = 2LL * m->ASM_LABELS + 2LL * m->ASM_REFS; + need(words <= 0x7fffff00, "assembly reservation is too large"); + m->ASM_WORDS = align_up((int)words, 256); + if (!s->asm_labels && !s->asm_refs) m->ASM_WORDS = 1024; + } + m->EXIT_WORDS = s->exits ? s->exits : 2560; + m->ENUM_MEMBERS = s->enum_members ? s->enum_members : 128; + m->TAGGED_ENUMS = s->tagged_enums ? s->tagged_enums : 16; + m->STRUCT_METHODS = s->struct_methods ? s->struct_methods : 32; + m->GENERATED_WORDS = s->generated_words ? s->generated_words : 8192; + m->RANGE_FACTS = s->facts ? s->facts : 32; m->JOIN_PATHS = s->paths ? s->paths : 64; m->O3_REGIONS = s->regions ? s->regions : 10; + need(m->O3_REGIONS <= 0x7fffffff / 67, "region reservation is too large"); + need(m->ENUM_MEMBERS <= 0x10000000, "enum member reservation is too large"); + m->cend = m->R_ASM + m->ASM_WORDS + m->NL + m->NL + m->NS + m->EXIT_WORDS; + m->GORG = align_up(m->cend + 256, 256); + } else { + m->GORG = align_up(m->ZFP + 64, 256); + } + m->FBOT = m->GORG + m->glen_need; + m->FTOP = m->FBOT + s->frame; + m->RS0 = m->FTOP; + m->MARK0 = m->FTOP + s->ret; + m->MARKN = s->mark; + m->DSLO = m->MARK0 + m->MARKN; + if (s->words) { + m->MW = s->words; + m->DSHI = m->MW - 16; + if (s->data && m->DSHI - m->DSLO < s->data) die("memory size too small for the data stack"); + } else { + m->DSHI = m->DSLO + s->data; + m->MW = align_up(m->DSHI + 16, 4096); + m->DSHI = m->MW - 16; + } + m->TW = m->MW; + if (s->heap_top) { + m->HEAP_LO = m->MW; + m->HEAP_HI = m->HEAP_LO + s->heap; + m->TW = s->total > m->HEAP_HI ? s->total : align_up(m->HEAP_HI, 4096); + } + check_common(m); + if (s->types) need(m->ZFP == (s->heap_top ? m->CODE_END : m->HEAP_HI) + s->types + s->zfp_pad, "type area"); +} + +static void hexdef(char **p, char *end, const char *n, int v) +{ + int k = snprintf(*p, (size_t)(end - *p), "const %s = 0x%X;\n", n, v); + if (k < 0 || *p + k >= end) die("const block"); + *p += k; +} + +static char *rt_block(const Map *m) +{ + char buf[2048], *p = buf; + hexdef(&p, buf + sizeof buf, "MW", m->MW); + hexdef(&p, buf + sizeof buf, "CODE_END", m->CODE_END); + hexdef(&p, buf + sizeof buf, "HEAP_LO", m->HEAP_LO); + hexdef(&p, buf + sizeof buf, "HEAP_HI", m->HEAP_HI); + hexdef(&p, buf + sizeof buf, "HEAP_GROW", m->GROW); + hexdef(&p, buf + sizeof buf, "ZFP", m->ZFP); + hexdef(&p, buf + sizeof buf, "GORG", m->GORG); + hexdef(&p, buf + sizeof buf, "FBOT", m->FBOT); + hexdef(&p, buf + sizeof buf, "FTOP", m->FTOP); + hexdef(&p, buf + sizeof buf, "RS0", m->RS0); + hexdef(&p, buf + sizeof buf, "MARK0", m->MARK0); + hexdef(&p, buf + sizeof buf, "MARKN", m->MARKN); + hexdef(&p, buf + sizeof buf, "DSLO", m->DSLO); + hexdef(&p, buf + sizeof buf, "DSHI", m->DSHI); + hexdef(&p, buf + sizeof buf, "MAGIC", 0x2B7E1516); + return strdup(buf); +} + +static char *cc_block(const Map *m) +{ + char buf[4096], *p = buf; + hexdef(&p, buf + sizeof buf, "IHN", m->IHN); + hexdef(&p, buf + sizeof buf, "R_IH", m->R_IH); + hexdef(&p, buf + sizeof buf, "R_POOL", m->R_POOL); + hexdef(&p, buf + sizeof buf, "R_POOLEND", m->R_POOLEND); + hexdef(&p, buf + sizeof buf, "NS", m->NS); + hexdef(&p, buf + sizeof buf, "R_SYM", m->R_SYM); + hexdef(&p, buf + sizeof buf, "sname_BASE", m->sname); + hexdef(&p, buf + sizeof buf, "skind_BASE", m->skind); + hexdef(&p, buf + sizeof buf, "sval_BASE", m->sval); + hexdef(&p, buf + sizeof buf, "ssig_BASE", m->ssig); + hexdef(&p, buf + sizeof buf, "sflag_BASE", m->sflag); + hexdef(&p, buf + sizeof buf, "sfix_BASE", m->sfix); + hexdef(&p, buf + sizeof buf, "snext_BASE", m->snext); + hexdef(&p, buf + sizeof buf, "NL", m->NL); + hexdef(&p, buf + sizeof buf, "lname_BASE", m->lname); + hexdef(&p, buf + sizeof buf, "lslot_BASE", m->lslot); + hexdef(&p, buf + sizeof buf, "lhome_BASE", m->lhome); + hexdef(&p, buf + sizeof buf, "lstate_BASE", m->lstate); + hexdef(&p, buf + sizeof buf, "lowner_BASE", m->lowner); + hexdef(&p, buf + sizeof buf, "lused_BASE", m->lused); + hexdef(&p, buf + sizeof buf, "larr_BASE", m->larr); + hexdef(&p, buf + sizeof buf, "lown_BASE", m->lown); + hexdef(&p, buf + sizeof buf, "NF", m->NF); + hexdef(&p, buf + sizeof buf, "fsite_BASE", m->fsite); + hexdef(&p, buf + sizeof buf, "fnext_BASE", m->fnext); + hexdef(&p, buf + sizeof buf, "R_SNAP_BASE", m->R_SNAP); + hexdef(&p, buf + sizeof buf, "R_QBUF_BASE", m->R_QBUF); + hexdef(&p, buf + sizeof buf, "R_SRC_BASE", m->R_SRC); + hexdef(&p, buf + sizeof buf, "R_SEQ_BASE", m->R_SEQ); + hexdef(&p, buf + sizeof buf, "bname_BASE", m->bname); + hexdef(&p, buf + sizeof buf, "bprec_BASE", m->bprec); + hexdef(&p, buf + sizeof buf, "bseq_BASE", m->bseq); + hexdef(&p, buf + sizeof buf, "blen_BASE", m->blen); + hexdef(&p, buf + sizeof buf, "uname_BASE", m->uname); + hexdef(&p, buf + sizeof buf, "useq_BASE", m->useq); + hexdef(&p, buf + sizeof buf, "ulen_BASE", m->ulen); + hexdef(&p, buf + sizeof buf, "aname_BASE", m->aname); + hexdef(&p, buf + sizeof buf, "abin_BASE", m->abin); + hexdef(&p, buf + sizeof buf, "kops_BASE", m->kops); + hexdef(&p, buf + sizeof buf, "R_CTX_BASE", m->R_CTX); + hexdef(&p, buf + sizeof buf, "R_LOOP_BASE", m->R_LOOP); + hexdef(&p, buf + sizeof buf, "R_ASM", m->R_ASM); + hexdef(&p, buf + sizeof buf, "ASM_LABELS", m->ASM_LABELS); + hexdef(&p, buf + sizeof buf, "ASM_REFS", m->ASM_REFS); + hexdef(&p, buf + sizeof buf, "ASM_WORDS", m->ASM_WORDS); + hexdef(&p, buf + sizeof buf, "EXIT_WORDS", m->EXIT_WORDS); + hexdef(&p, buf + sizeof buf, "ENUM_MEMBERS", m->ENUM_MEMBERS); + hexdef(&p, buf + sizeof buf, "TAGGED_ENUMS", m->TAGGED_ENUMS); + hexdef(&p, buf + sizeof buf, "STRUCT_METHODS", m->STRUCT_METHODS); + hexdef(&p, buf + sizeof buf, "GENERATED_WORDS", m->GENERATED_WORDS); + hexdef(&p, buf + sizeof buf, "RANGE_FACTS", m->RANGE_FACTS); + hexdef(&p, buf + sizeof buf, "JOIN_PATHS", m->JOIN_PATHS); + hexdef(&p, buf + sizeof buf, "O3_REGIONS", m->O3_REGIONS); + return strdup(buf); +} + +static void write_memmap(const char *dir, const Map *m) +{ + char path[512], buf[4096]; + snprintf(path, sizeof path, "%s/memmap.h", dir); + snprintf(buf, sizeof buf, + "/* generated by src/tools/genmap for the %s profile. do not edit. */\n" + "#ifndef MEMMAP_H\n#define MEMMAP_H\n" + "#define PW 0x%X\n#define CODE_END 0x%X\n#define HEAP_LO 0x%X\n#define HEAP_HI 0x%X\n" + "#define HEAP_GROW %d\n" + "#ifndef MW\n#define MW 0x%X\n#endif\n" + "#if MW < HEAP_HI\n#error MW is below the end of the initial heap\n#endif\n" + "#define ZFP 0x%X\n" + "#ifdef GORG\n#if GORG != 0x%X\n#error genome GORG does not match this map\n#endif\n" + "#else\n#define GORG 0x%X\n#endif\n" + "#define FBOT 0x%X\n#define FTOP 0x%X\n#define RS0 0x%X\n" + "#define MARK0 0x%X\n#define MARKN 0x%X\n" + "#define DSLO 0x%X\n#define DSHI 0x%X\n" + "#define DS0 DSHI\n#define SYSGATE (ZFP + 2)\n" + "#define DSLIM (DSLO + 1100)\n#define RSLIM (MARK0 - 8)\n" + "#endif\n", + m->name, m->MW, m->CODE_END, m->HEAP_LO, m->HEAP_HI, m->GROW, m->TW, m->ZFP, m->GORG, m->GORG, + m->FBOT, m->FTOP, m->RS0, m->MARK0, m->MARKN, m->DSLO, m->DSHI); + emit_file(path, buf); +} + +static char *map_txt(const Map *m) +{ + char buf[2048]; + int n; + n = snprintf(buf, sizeof buf, + "profile %s\nMW %d (%lld bytes)\nprogram 0 .. %d\nheap %d .. %d (%d words)\n", + m->name, m->TW, (long long)m->TW * 8, m->CODE_END, m->HEAP_LO, m->HEAP_HI, m->HEAP_HI - m->HEAP_LO); + if (m->GROW) + n += snprintf(buf + n, sizeof buf - n, "fixed map ends at %d; the heap grows up to %d\n", m->MW, m->TW); + if (m->typesz) + n += snprintf(buf + n, sizeof buf - n, "types %d .. %d (%d words)\n", m->ZFP - m->typesz, m->ZFP, m->typesz); + n += snprintf(buf + n, sizeof buf - n, "ZFP %d SYSGATE %d\n", m->ZFP, m->ZFP + 2); + if (m->R_ASM) { + int ltype = m->R_ASM + m->ASM_WORDS; + int rexit = ltype + m->NL + m->NL + m->NS; + n += snprintf(buf + n, sizeof buf - n, + "compiler tables %d .. %d\nltype %d R_EXIT %d .. %d\n", + m->R_IH, m->cend, ltype, rexit, rexit + m->EXIT_WORDS); + } + snprintf(buf + n, sizeof buf - n, + "GORG %d reserved through %d\nframe %d .. %d\nreturn %d .. %d\nmark %d .. %d (%d)\ndata %d .. %d\n", + m->GORG, m->GORG + m->glen_need, m->FBOT, m->FTOP, m->FTOP, m->MARK0, + m->MARK0, m->MARK0 + m->MARKN, m->MARKN, m->DSLO, m->DSHI); + return strdup(buf); +} + +static void write_map_txt(const char *dir, const char *txt) +{ + char path[512]; + snprintf(path, sizeof path, "%s/map.txt", dir); + emit_file(path, txt); + fputs(txt, stderr); +} + +static void mkdir1(const char *dir) +{ +#ifdef _WIN32 + char tmp[512]; + char *p; + snprintf(tmp, sizeof(tmp), "%s", dir); + for (p = tmp; *p; p++) { + if (*p == '/' || *p == '\\') { + char save = *p; + *p = 0; + if (*tmp) _mkdir(tmp); + *p = save; + } + } + if (_mkdir(dir) && errno != EEXIST) { perror(dir); exit(1); } +#else + if (mkdir(dir, 0755) && errno != EEXIST) { perror(dir); exit(1); } +#endif +} + +static char *between(const char *src, const char *begin, const char *end) +{ + const char *a = strstr(src, begin), *b; + char *out; size_t n; + if (!a) return 0; + a += strlen(begin); + b = strstr(a, end); + if (!b || b < a) return 0; + n = (size_t)(b - a); + out = malloc(n + 1); + if (!out) die("oom"); + memcpy(out, a, n); + out[n] = 0; + return out; +} + +static int token_hex(const char *text, const char *name, int *out) +{ + const char *p = text; + size_t n = strlen(name); + while ((p = strstr(p, name))) { + int before_ok = p == text || !(isalnum((unsigned char)p[-1]) || p[-1] == '_'); + int after_ok = !(isalnum((unsigned char)p[n]) || p[n] == '_'); + if (before_ok && after_ok) { + const char *q = p + n; + while (*q == ' ' || *q == '\t') q++; + if (*q == '=') { q++; while (*q == ' ' || *q == '\t') q++; } + if (q[0] == '0' && (q[1] == 'x' || q[1] == 'X')) { + *out = (int)strtol(q, 0, 16); + return 1; + } + if (isdigit((unsigned char)*q)) { + *out = (int)strtol(q, 0, 10); + return 1; + } + } + p += n; + } + return 0; +} + +static void expect_hex(const char *text, const char *where, const char *name, int want) +{ + int got; + if (!token_hex(text, name, &got) || got != want) { + fprintf(stderr, "genmap: %s %s is 0x%X in the layout", where, name, want); + if (token_hex(text, name, &got)) fprintf(stderr, " and 0x%X in the source", got); + fprintf(stderr, "\n"); + exit(1); + } +} + +/* the lfa immediate the checked-in rt.zc carries: the default map's ZFP */ +#define DEFAULT_ZFP 589824 + +static void check_sources(const Map *m) +{ + char lfa_old[64]; + FILE *mf = fopen("memmap.h", "rb"); + const char *mm_path = mf ? (fclose(mf), "memmap.h") : "src/core/memmap.h"; + char *rt = slurp("src/runtime/rt.zc"), *cc = slurp("src/compiler/compiler.zc"), *mm = slurp(mm_path); + char *rtb = between(rt, "// @map-rt", "// @end-map"); + char *ccb = between(cc, "// @map-cc", "// @end-map"); + static const char *rt_names[] = { + "MW", "CODE_END", "HEAP_LO", "HEAP_HI", "HEAP_GROW", "ZFP", "GORG", "FBOT", "FTOP", "RS0", + "MARK0", "MARKN", "DSLO", "DSHI", "MAGIC", 0 + }; + static const char *cc_names[] = { + "IHN", "R_IH", "R_POOL", "R_POOLEND", "NS", "R_SYM", + "sname_BASE", "skind_BASE", "sval_BASE", "ssig_BASE", "sflag_BASE", "sfix_BASE", "snext_BASE", + "NL", "lname_BASE", "lslot_BASE", "lhome_BASE", "lstate_BASE", "lowner_BASE", "lused_BASE", "larr_BASE", "lown_BASE", + "NF", "fsite_BASE", "fnext_BASE", "R_SNAP_BASE", "R_QBUF_BASE", "R_SRC_BASE", "R_SEQ_BASE", + "bname_BASE", "bprec_BASE", "bseq_BASE", "blen_BASE", "uname_BASE", "useq_BASE", "ulen_BASE", "aname_BASE", "abin_BASE", "kops_BASE", + "R_CTX_BASE", "R_LOOP_BASE", "R_ASM", "ASM_LABELS", "ASM_REFS", "ASM_WORDS", "EXIT_WORDS", "ENUM_MEMBERS", "TAGGED_ENUMS", "STRUCT_METHODS", "GENERATED_WORDS", "RANGE_FACTS", "JOIN_PATHS", "O3_REGIONS", 0 + }; + int rt_vals[] = { + m->MW, m->CODE_END, m->HEAP_LO, m->HEAP_HI, m->GROW, m->ZFP, m->GORG, m->FBOT, m->FTOP, m->RS0, + m->MARK0, m->MARKN, m->DSLO, m->DSHI, 0x2B7E1516 + }; + int cc_vals[] = { + m->IHN, m->R_IH, m->R_POOL, m->R_POOLEND, m->NS, m->R_SYM, + m->sname, m->skind, m->sval, m->ssig, m->sflag, m->sfix, m->snext, + m->NL, m->lname, m->lslot, m->lhome, m->lstate, m->lowner, m->lused, m->larr, m->lown, + m->NF, m->fsite, m->fnext, m->R_SNAP, m->R_QBUF, m->R_SRC, m->R_SEQ, + m->bname, m->bprec, m->bseq, m->blen, m->uname, m->useq, m->ulen, m->aname, m->abin, m->kops, + m->R_CTX, m->R_LOOP, m->R_ASM, m->ASM_LABELS, m->ASM_REFS, m->ASM_WORDS, m->EXIT_WORDS, m->ENUM_MEMBERS, m->TAGGED_ENUMS, m->STRUCT_METHODS, m->GENERATED_WORDS, m->RANGE_FACTS, m->JOIN_PATHS, m->O3_REGIONS + }; + int i; + if (!rtb || !ccb) die("map markers missing from the checked-in sources"); + snprintf(lfa_old, sizeof lfa_old, "ld %d", DEFAULT_ZFP); + if (!strstr(rt, lfa_old)) die("src/runtime/rt.zc no longer has the default lfa immediate"); + if (!strstr(cc, "ZFP - 16384")) die("src/compiler/compiler.zc lost its initial type-area formula"); + for (i = 0; rt_names[i]; i++) expect_hex(rtb, "src/runtime/rt.zc", rt_names[i], rt_vals[i]); + for (i = 0; rt_names[i]; i++) { + if (!strcmp(rt_names[i], "MAGIC") || !strcmp(rt_names[i], "HEAP_GROW")) continue; + expect_hex(mm, mm_path, !strcmp(rt_names[i], "MW") ? "PW" : rt_names[i], rt_vals[i]); + } + if (m->R_ASM) for (i = 0; cc_names[i]; i++) expect_hex(ccb, "src/compiler/compiler.zc", cc_names[i], cc_vals[i]); + free(rt); free(cc); free(mm); free(rtb); free(ccb); +} + +static void write_sources(const char *dir, const Spec *s, const Map *m) +{ + char path[512], lfa[64], formula[64], snap[64], quote[64]; + char *rt, *block, *spliced, *done; + rt = slurp("src/runtime/rt.zc"); + block = rt_block(m); + spliced = splice(rt, "// @map-rt", "// @end-map", block); + snprintf(lfa, sizeof lfa, "ld %d", m->ZFP); + { char old[64]; snprintf(old, sizeof old, "ld %d", DEFAULT_ZFP); done = replace_all(spliced, old, lfa); } + snprintf(path, sizeof path, "%s/rt.zc", dir); + emit_file(path, done); + free(rt); free(block); free(spliced); free(done); + if (!s->compiler) return; + { + char *cc = slurp("src/compiler/compiler.zc"); + block = cc_block(m); + spliced = splice(cc, "// @map-cc", "// @end-map", block); + if (s->type_formula) { + snprintf(formula, sizeof formula, "ZFP - %d", s->type_formula); + done = replace_all(spliced, "ZFP - 16384", formula); + free(spliced); spliced = done; + } + snprintf(path, sizeof path, "%s/compiler.zc", dir); + emit_file(path, spliced); + free(cc); free(block); free(spliced); + } +} + +static void usage_status(int status) +{ + fprintf(stderr, + "usage: genmap --preset small|run|default|fixed|lsp [-o DIR] [--check]\n" + " genmap --mode compiler|run [-o DIR] [--words N] [options]\n" + " genmap small|run DIR\n" + "options:\n" + " --code N --total N --heap N --heap-lo N --types N --zfp-pad N --genome N\n" + " --frame N --return N --mark N --data N\n" + " --hash N --pool N --symbols N --locals N --fwd N\n" + " --snap N --quote N --src N --seq N --ctx N --loop N\n" + " --binary N --unary N --assignments N --keywords N\n" + " --asm-labels N --asm-refs N --exit N\n" + " --enum-members N --tagged-enums N --methods N --generated-text N\n" + " --facts N --paths N --regions N\n" + " --snap-cap N --quote-cap N --name LABEL\n" + "--preset default has a growing heap above the stacks; fixed is the old 1,081,344-word map.\n" + "--grow lets a map with a fixed size get bigger when its genome needs more room.\n" + "--mode packs a compiler or a run-only image; spare words go to the data stack.\n" + "--check compares that layout to memmap.h and the checked-in const blocks.\n"); + exit(status); +} + +static void usage(void) { usage_status(1); } + +static int take(int argc, char **argv, int *i) +{ + if (*i + 1 >= argc) usage(); + return parse_num(argv[++*i]); +} + +int main(int argc, char **argv) +{ + Spec s; Map m; + if (argc == 2 && !strcmp(argv[1], "--help")) usage_status(0); + const char *outdir = 0, *preset = 0, *mode = 0; + int check = 0, grow = 0, i, have = 0; + int total = 0, set_total = 0, set_words = 0, set_heap = 0, set_heap_lo = 0, set_types = 0, set_zfp = 0; + int code = 0, binary = 0, unary = 0, assignments = 0, keywords = 0; + int asm_labels = 0, asm_refs = 0, exits = 0; + int enum_members = 0, tagged_enums = 0, struct_methods = 0, generated_words = 0; + int facts = 0, paths = 0, regions = 0; + int set_genome = 0, set_frame = 0, set_ret = 0, set_mark = 0, set_data = 0; + int words = 0, heap = 0, heap_lo = 0, types = 0, zfp_pad = 0; + int genome = 0, frame = 0, ret = 0, mark = 0, data = 0; + int set_ihn = 0, set_pool = 0, set_ns = 0, set_nl = 0, set_nf = 0; + int set_snap = 0, set_qbuf = 0, set_src = 0, set_seq = 0, set_ctx = 0, set_loop = 0; + int set_snap_cap = 0, set_quote_cap = 0; + int ihn = 0, pool = 0, ns = 0, nl = 0, nf = 0, snap = 0, qbuf = 0, srcn = 0, seq = 0, ctx = 0, loop = 0; + int snap_cap = 0, quote_cap = 0, set_name = 0; + const char *s_name = 0; + char *txt; + + if (argc == 3 && argv[1][0] != '-' && (strcmp(argv[1], "small") == 0 || strcmp(argv[1], "run") == 0)) { + preset = argv[1]; + outdir = argv[2]; + } else { + for (i = 1; i < argc; i++) { + if (strcmp(argv[i], "--preset") == 0) { if (++i >= argc) usage(); preset = argv[i]; } + else if (strcmp(argv[i], "--mode") == 0) { if (++i >= argc) usage(); mode = argv[i]; } + else if (strcmp(argv[i], "-o") == 0 || strcmp(argv[i], "--out") == 0) { if (++i >= argc) usage(); outdir = argv[i]; } + else if (strcmp(argv[i], "--check") == 0) check = 1; + else if (strcmp(argv[i], "--grow") == 0) grow = 1; + else if (strcmp(argv[i], "--words") == 0) { words = take(argc, argv, &i); set_words = 1; } + else if (strcmp(argv[i], "--total") == 0) { total = take(argc, argv, &i); set_total = 1; } + else if (strcmp(argv[i], "--code") == 0) code = take(argc, argv, &i); + else if (strcmp(argv[i], "--binary") == 0) binary = take(argc, argv, &i); + else if (strcmp(argv[i], "--unary") == 0) unary = take(argc, argv, &i); + else if (strcmp(argv[i], "--assignments") == 0) assignments = take(argc, argv, &i); + else if (strcmp(argv[i], "--keywords") == 0) keywords = take(argc, argv, &i); + else if (strcmp(argv[i], "--asm-labels") == 0) asm_labels = take(argc, argv, &i); + else if (strcmp(argv[i], "--asm-refs") == 0) asm_refs = take(argc, argv, &i); + else if (strcmp(argv[i], "--exit") == 0) exits = take(argc, argv, &i); + else if (strcmp(argv[i], "--enum-members") == 0) enum_members = take(argc, argv, &i); + else if (strcmp(argv[i], "--tagged-enums") == 0) tagged_enums = take(argc, argv, &i); + else if (strcmp(argv[i], "--methods") == 0) struct_methods = take(argc, argv, &i); + else if (strcmp(argv[i], "--generated-text") == 0) generated_words = take(argc, argv, &i); + else if (strcmp(argv[i], "--facts") == 0) facts = take(argc, argv, &i); + else if (strcmp(argv[i], "--paths") == 0) paths = take(argc, argv, &i); + else if (strcmp(argv[i], "--regions") == 0) regions = take(argc, argv, &i); + else if (strcmp(argv[i], "--heap") == 0) { heap = take(argc, argv, &i); set_heap = 1; } + else if (strcmp(argv[i], "--heap-lo") == 0) { heap_lo = take(argc, argv, &i); set_heap_lo = 1; } + else if (strcmp(argv[i], "--types") == 0) { types = take(argc, argv, &i); set_types = 1; } + else if (strcmp(argv[i], "--zfp-pad") == 0) { zfp_pad = take(argc, argv, &i); set_zfp = 1; } + else if (strcmp(argv[i], "--genome") == 0) { genome = take(argc, argv, &i); set_genome = 1; } + else if (strcmp(argv[i], "--frame") == 0) { frame = take(argc, argv, &i); set_frame = 1; } + else if (strcmp(argv[i], "--return") == 0) { ret = take(argc, argv, &i); set_ret = 1; } + else if (strcmp(argv[i], "--mark") == 0) { mark = take(argc, argv, &i); set_mark = 1; } + else if (strcmp(argv[i], "--data") == 0) { data = take(argc, argv, &i); set_data = 1; } + else if (strcmp(argv[i], "--hash") == 0) { ihn = take(argc, argv, &i); set_ihn = 1; } + else if (strcmp(argv[i], "--pool") == 0) { pool = take(argc, argv, &i); set_pool = 1; } + else if (strcmp(argv[i], "--symbols") == 0) { ns = take(argc, argv, &i); set_ns = 1; } + else if (strcmp(argv[i], "--locals") == 0) { nl = take(argc, argv, &i); set_nl = 1; } + else if (strcmp(argv[i], "--fwd") == 0) { nf = take(argc, argv, &i); set_nf = 1; } + else if (strcmp(argv[i], "--snap") == 0) { snap = take(argc, argv, &i); set_snap = 1; } + else if (strcmp(argv[i], "--quote") == 0) { qbuf = take(argc, argv, &i); set_qbuf = 1; } + else if (strcmp(argv[i], "--src") == 0) { srcn = take(argc, argv, &i); set_src = 1; } + else if (strcmp(argv[i], "--seq") == 0) { seq = take(argc, argv, &i); set_seq = 1; } + else if (strcmp(argv[i], "--ctx") == 0) { ctx = take(argc, argv, &i); set_ctx = 1; } + else if (strcmp(argv[i], "--loop") == 0) { loop = take(argc, argv, &i); set_loop = 1; } + else if (strcmp(argv[i], "--snap-cap") == 0) { snap_cap = take(argc, argv, &i); set_snap_cap = 1; } + else if (strcmp(argv[i], "--quote-cap") == 0) { quote_cap = take(argc, argv, &i); set_quote_cap = 1; } + else if (strcmp(argv[i], "--name") == 0) { if (++i >= argc) usage(); s_name = argv[i]; set_name = 1; } + else usage(); + } + } + if (preset && mode) die("pass --preset or --mode, not both"); + if (!preset && !mode) usage(); + if (preset) { + if (strcmp(preset, "small") == 0) preset_small(&s); + else if (strcmp(preset, "run") == 0) preset_run(&s); + else if (strcmp(preset, "default") == 0) preset_default(&s); + else if (strcmp(preset, "lsp") == 0) preset_lsp(&s); + else if (strcmp(preset, "fixed") == 0) preset_fixed(&s); + else die("preset is small, run, default, fixed or lsp"); + } else { + if (strcmp(mode, "compiler") == 0) mode_defaults(&s, 1); + else if (strcmp(mode, "run") == 0) mode_defaults(&s, 0); + else die("mode is compiler or run"); + } + if (set_words) s.words = words; + if (set_total) s.total = total; + if (set_heap) s.heap = heap; + if (set_heap_lo) { s.heap_lo = heap_lo; if (s.heap_top) s.code_end = heap_lo; } + if (code) { if (s.heap_top) s.code_end = code; else s.heap_lo = code; } + s.binary = binary; s.unary = unary; s.assignments = assignments; s.keywords = keywords; + s.asm_labels = asm_labels; s.asm_refs = asm_refs; s.exits = exits; + s.enum_members = enum_members; s.tagged_enums = tagged_enums; s.struct_methods = struct_methods; s.generated_words = generated_words; + s.facts = facts; s.paths = paths; s.regions = regions; + if (set_types) { s.types = types; s.type_formula = types == 16384 ? 0 : types; } + if (set_zfp) s.zfp_pad = zfp_pad; + if (set_genome) s.genome = genome; + if (set_frame) s.frame = frame; + if (set_ret) s.ret = ret; + if (set_mark) s.mark = mark; + if (set_data) s.data = data; + if (set_ihn) s.ihn = ihn; + if (set_pool) s.pool = pool; + if (set_ns) s.ns = ns; + if (set_nl) s.nl = nl; + if (set_nf) s.nf = nf; + if (set_snap) s.snap = snap; + if (set_qbuf) s.qbuf = qbuf; + if (set_src) s.src = srcn; + if (set_seq) s.seq = seq; + if (set_ctx) s.ctx = ctx; + if (set_loop) s.loop = loop; + if (set_snap_cap) s.snap = snap_cap; + if (set_quote_cap) { if (quote_cap > 0x7fffffff / 3) die("quote size overflow"); s.qbuf = quote_cap * 3; } + if (set_name) s.name = s_name; + /* --grow: a preset with a fixed memory size gets as many words as its layout needs. the + fixed size stays when it is big enough, so a program that fits builds the same map as + before and a bigger genome moves the top of memory up instead of failing */ + if (grow && s.words) { + Spec t = s; Map probe; + t.words = 0; + if (!t.data) t.data = 16368; + layout(&t, &probe); + if (probe.MW > s.words) s.words = probe.MW; + } + if (s.compiler && (!s.ihn || !s.pool || !s.ns || !s.nl || !s.nf)) die("compiler tables are empty"); + layout(&s, &m); + txt = map_txt(&m); + if (check) check_sources(&m); + if (outdir) { + mkdir1(outdir); + write_memmap(outdir, &m); + write_map_txt(outdir, txt); + write_sources(outdir, &s, &m); + have = 1; + } + if (!have && !check) usage(); + if (check && !outdir) fputs(txt, stdout); + free(txt); + return 0; +} diff --git a/src/tools/mkgenome.c b/src/tools/mkgenome.c new file mode 100644 index 0000000..bb2e787 --- /dev/null +++ b/src/tools/mkgenome.c @@ -0,0 +1,31 @@ +/* turns a genome image (little-endian 64-bit words on stdin, as written by + src/tools/build.zc) into genome.h */ +#include +#include +#ifdef _WIN32 +#include +#include +#endif +#include "memmap.h" +int main(void) +{ +#ifdef _WIN32 + _setmode(_fileno(stdin), _O_BINARY); + _setmode(_fileno(stdout), _O_BINARY); +#endif + unsigned char b[8]; int n = 0; size_t got; + printf("#define GORG %d\nstatic const int64_t genome[] = {", GORG); + while ((got = fread(b, 1, 8, stdin)) == 8) { + uint64_t u = (uint64_t)b[0] | (uint64_t)b[1] << 8 | (uint64_t)b[2] << 16 | (uint64_t)b[3] << 24 + | (uint64_t)b[4] << 32 | (uint64_t)b[5] << 40 | (uint64_t)b[6] << 48 | (uint64_t)b[7] << 56; + if ((n == 0 && u != (uint64_t)FTOP * 2 + 1) || + (n == 1 && u != (uint64_t)ZFP * 2 + 1) || (n == 2 && u != 4)) { + fprintf(stderr, "mkgenome: input is not a genome for this map\n"); return 1; + } + printf("%s%lld", !n ? "\n" : n % 12 ? "," : ",\n", (long long)u); + n++; + } + if (got || ferror(stdin) || n < 7) { fprintf(stderr, "mkgenome: incomplete genome\n"); return 1; } + printf("\n};\n#define GLEN %d\n", n); + return n ? 0 : 1; +} diff --git a/src/tools/mklsp.sh b/src/tools/mklsp.sh new file mode 100755 index 0000000..5edf14a --- /dev/null +++ b/src/tools/mklsp.sh @@ -0,0 +1,14 @@ +#!/bin/bash +# builds the language server: src/tools/mklsp.sh [outdir] leaves outdir/zclsp. +# the server is the real compiler (src/compiler/compiler.zc) in check mode plus +# src/lsp/server.zc. its memory map is sized to that program by src/tools/automap.sh, +# so the genome slot is as big as it needs to be and no bigger. +set -e +cd "$(dirname "$0")/../.." +O=${1:-build/lsp}; CC=${CC:-gcc} +# DOCS=1: the image carries the doc comments, so hover can show what a name is for +DOCS=1 bash src/tools/automap.sh lsp "$O" src/lsp/server.zc >/dev/null +rm -rf "$O/src"; mkdir -p "$O/src" +cp src/core/core.c src/core/io_linux.c src/core/fops.h src/core/bops.h src/core/stack.h "$O/memmap.h" "$O/src/" +cp "$O/genome.h" "$O/src/lsp_genome.h" +$CC -O2 -w -fwrapv -DJTYPE=int -DZY_SANDBOX -DUSE_LSP_GENOME "$O/src/core.c" "$O/src/io_linux.c" -I"$O/src" -o "$O/zclsp" diff --git a/src/tools/mklspwasm.sh b/src/tools/mklspwasm.sh new file mode 100755 index 0000000..56ba43b --- /dev/null +++ b/src/tools/mklspwasm.sh @@ -0,0 +1,20 @@ +#!/bin/bash +# builds the language server as WebAssembly: editors/vscode/server/lsp.wasm by +# default, or the path given as the first argument. the server is the compiler +# and src/lsp/server.zc on the map and genome that src/tools/mklsp.sh made in build/lsp, +# and the microcore (src/micro/micro.c) runs them. the host is client/wasmproc.js. +# needs clang with the wasm32 target and wasm-ld; run src/tools/mklsp.sh first. +# src/tools/mklspwasm.sh [outfile.wasm] +set -e +cd "$(dirname "$0")/../.." +OUT=${1:-editors/vscode/server/lsp.wasm} +LSP_BUILD_DIR=${LSP_BUILD_DIR:-build/lsp} +if [ "${LSP_REUSE_BUILD:-0}" != 1 ]; then bash src/tools/mklsp.sh "$LSP_BUILD_DIR" >/dev/null; fi +[ -f "$LSP_BUILD_DIR/src/lsp_genome.h" ] || { echo "mklspwasm: no language server image in $LSP_BUILD_DIR" >&2; exit 1; } +clang --target=wasm32 -O2 -w -fwrapv -nostdlib -fno-builtin -mbulk-memory -DJTYPE=int -DUSE_LSP_GENOME \ + -I"$LSP_BUILD_DIR/src" -Isrc/micro src/micro/micro.c src/micro/port_wasm_stream.c \ + -Wl,--no-entry -Wl,--export=wasm_chunk -Wl,--export=wasm_chunk_cap -Wl,--export=wasm_feed \ + -Wl,--export=wasm_close -Wl,--export=wasm_start -Wl,--export=wasm_resume \ + -Wl,--export=memory -Wl,--allow-undefined -Wl,--initial-memory=41943040 -Wl,-z,stack-size=65536 \ + -o "$OUT" +ls -l "$OUT" diff --git a/src/tools/mkvsix.sh b/src/tools/mkvsix.sh new file mode 100755 index 0000000..5ca8ef5 --- /dev/null +++ b/src/tools/mkvsix.sh @@ -0,0 +1,35 @@ +#!/bin/bash +# builds the vs code extension, with the language server inside it: the tinyvm +# javascript vm (src/js/tinyvm.js, compiled from src/js/tinyvm.ts) and the server's image +# (src/js/lsp.json, from build/lsp/genome.h and its map), and the wasm server +# (src/tools/mklspwasm.sh) when clang can build it. needs node and tsc; the +# server build (src/tools/mklsp.sh) runs first if build/lsp is missing. +# src/tools/mkvsix.sh [outfile.vsix] +set -e +cd "$(dirname "$0")/../.." +OUT=${1:-$PWD/build/zc-language-support.vsix} +case "$OUT" in /*) ;; *) OUT="$PWD/$OUT" ;; esac +mkdir -p "$(dirname "$OUT")" +bash src/tools/mklsp.sh build/lsp >/dev/null +# typescript errors stop packaging, so broken generated code is never shipped. +command -v tsc >/dev/null || { echo "mkvsix: tsc is not installed" >&2; exit 1; } +tsc src/js/tinyvm.ts --target es2020 --lib es2020,dom --module commonjs --outDir src/js +node src/js/extract_lsp.js +mkdir -p editors/vscode/server +cp src/js/tinyvm.js src/js/layout.js src/js/lsp.json editors/vscode/server/ +# the wasm server runs when it is there; without clang the vm server is packed alone +if command -v clang >/dev/null && clang --print-targets | grep -q wasm32; then + LSP_BUILD_DIR=build/lsp LSP_REUSE_BUILD=1 bash src/tools/mklspwasm.sh editors/vscode/server/lsp.wasm +else + rm -f editors/vscode/server/lsp.wasm + echo "no clang with wasm32: packing the javascript vm server only" >&2 +fi +cd editors/vscode +if [ -n "${VSCE:-}" ]; then + "$VSCE" package --allow-missing-repository --skip-license -o "$OUT" >/dev/null +elif command -v vsce >/dev/null; then + vsce package --allow-missing-repository --skip-license -o "$OUT" >/dev/null +else + npx -y @vscode/vsce package --allow-missing-repository --skip-license -o "$OUT" >/dev/null +fi +echo "$OUT" diff --git a/src/tools/zc0.c b/src/tools/zc0.c new file mode 100644 index 0000000..5ef5737 --- /dev/null +++ b/src/tools/zc0.c @@ -0,0 +1,880 @@ +/* zc0: stage 0 of the bootstrap. a small c compiler for the subset of zc that + compiler.zc is written in. it reads zc source on stdin and writes a genome + header (vm words placed at GORG) on stdout, in the same image layout the + resident compiler's image mode produces. no lifetime analysis, no quote, no + parse words, no block expressions: those only exist in compiler.zc. tags + (m, d, -> u) and casts (as u, as d) are parsed and recorded in signatures + but not checked. */ +#include +#include +#include +#include +#include +#ifdef _WIN32 +#include +#include +#endif +#include "memmap.h" +#include "fops.h" + +#ifndef MAXW +#define MAXW 0x80000 +#endif + +enum { T_EOF, T_NUM, T_ID, T_STR, T_OP, T_FLT, T_BSTR }; +enum { K_FN = 1, K_GVAR, K_GARR, K_CONST, K_KW, K_ENH, K_TYPE, K_DOC }; +enum { O_Q, O_LD, O_ST, O_PICK, O_DROP, O_SWAP, O_CALL, O_JMP, O_JZ, O_RET, + O_IN, O_EMPTY, O_OUT, O_ADD, O_SUB, O_MUL, O_AND, O_OR, O_XOR, O_SHR, O_LT }; +static const char *mn = "q@!$_~cjzr,?.+-*&|^><%"; + +static char *src; static int pos, line = 1, tstart, hdr_start; +/* the signature of the function being defined: its header's source text, for the language server (ZC0DOCS) */ +static char sgtext[400], sgname[80]; +static int tk, glue; static int64_t tv; static char ts[4096]; /* current token */ +static int xf, xs, lvi; /* float, byte string; the plain local it came from, or -1 */ + +static int64_t *img; static int imgcap, here = GORG; +static void emit(int64_t w) { + if (!img || here - GORG >= imgcap) { + int capacity; int64_t *next; + if (imgcap > (0x7fffffff - GORG) / 2) { fprintf(stderr, "zc0: image address overflow\n"); exit(1); } + capacity = imgcap ? imgcap * 2 : MAXW; + if (capacity <= imgcap || capacity > 0x7fffffff - GORG) { fprintf(stderr, "zc0: image address overflow\n"); exit(1); } + next = realloc(img, (size_t)capacity * sizeof *img); + if (!next) { fprintf(stderr, "zc0: cannot grow image buffer\n"); exit(1); } + img = next; imgcap = capacity; + } + img[here++ - GORG] = w; +} +static void lit(int64_t v) { + if ((v >> 62) != 0 && (v >> 62) != -1) { + emit((v >> 32) * 2 + 1); emit(0x100000000LL * 2 + 1); emit(O_MUL * 2); + emit((int64_t)((uint64_t)v & 0xffffffffu) * 2 + 1); emit(O_OR * 2); + } else emit(v * 2 + 1); +} +static void op(int k) { emit((int64_t)k * 2); } +static void setlit(int at, int64_t v) { img[at - GORG] = v * 2 + 1; } + +static int pguess, pbad; /* inside a speculative type parse: an error is noted instead of stopping */ +static void die(const char *m) { if (pguess) { pbad = 1; return; } fprintf(stderr, "zc0: line %d: %s (at '%s')\n", line, m, ts); exit(1); } + +/* ---- lexer ---- */ +static const char *ops3[] = { "<<=", ">>=", 0 }; +static const char *ops2[] = { "==", "!=", "<=", ">=", "<<", ">>", "&&", "||", "++", "--", "+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "->", 0 }; +static int esc(void) +{ + int c = src[pos++]; + if (c != '\\') return c; + c = src[pos++]; + switch (c) { case 'n': return 10; case 't': return 9; case 'r': return 13; case '0': return 0; case 'e': return 27; } + return c; +} +/* doc comments, as the self-hosted compiler reads them with docs on: the // + lines directly above a declaration, up to a blank line or any other text */ +static char dbuf[2100], dsnap[2100], dnm[64]; static int dn, dlast, dline, dsn, docpending; +static void doccomment(void) /* pos is on the first slash */ +{ + int cl = line, k = pos - 1, bol; + while (k >= 0 && (src[k] == ' ' || src[k] == '\t')) k--; + bol = k < 0 || src[k] == '\n'; + pos += 2; + if (!bol) { while (src[pos] && src[pos] != '\n') pos++; return; } + if (dn && dlast != cl - 1) dn = 0; + if (src[pos] == ' ') pos++; + while (src[pos] && src[pos] != '\n') { if (dn < 2040) dbuf[dn++] = src[pos]; pos++; } + while (dn && (dbuf[dn - 1] == ' ' || dbuf[dn - 1] == '\t' || dbuf[dn - 1] == '\r')) dn--; + dbuf[dn++] = '\n'; + dlast = cl; docpending = 1; +} +/* block comments may document a declaration on the same line */ +static void docblock(void) +{ + int cl = line, k = pos - 1, bol, start = 1, c; + while (k >= 0 && (src[k] == ' ' || src[k] == '\t')) k--; + bol = k < 0 || src[k] == '\n'; + if (!bol || (dn && dlast != cl - 1)) dn = 0; + pos += 2; + if (src[pos] == '*') { pos++; if (src[pos] == '/') { pos++; return; } } + while (src[pos]) { + if (start) { + while (src[pos] == ' ' || src[pos] == '\t') pos++; + if (src[pos] == '*') { + pos++; + if (src[pos] == '/') { pos++; break; } + if (src[pos] == ' ') pos++; + } + start = 0; + } + c = src[pos++]; + if (c == '*' && src[pos] == '/') { pos++; break; } + if (dn < 2040) dbuf[dn++] = (char)c; + if (c == '\n') { line++; start = 1; } + } + while (dn && (dbuf[dn - 1] == ' ' || dbuf[dn - 1] == '\t' || dbuf[dn - 1] == '\r' || dbuf[dn - 1] == '\n')) dn--; + dbuf[dn++] = '\n'; dlast = line; docpending = 1; +} +static void next(void) +{ + int ws = 0, i; + for (;;) { + while (src[pos] && isspace((unsigned char)src[pos])) { if (src[pos] == '\n') line++; pos++; ws = 1; } + if (src[pos] == '/' && src[pos + 1] == '/') { doccomment(); ws = 1; continue; } + if (src[pos] == '/' && src[pos + 1] == '*') { docblock(); ws = 1; continue; } + break; + } + glue = !ws; + tstart = pos; + if (docpending) { + dline = 0; + if (dn && (line == dlast || line == dlast + 1)) dline = line; + docpending = 0; + } + if (!src[pos] || src[pos] == '`') { tk = T_EOF; strcpy(ts, ""); return; } + if (isdigit((unsigned char)src[pos]) || (src[pos] == '.' && isdigit((unsigned char)src[pos + 1]))) { + int p = pos, fl = 0; + if (src[pos] == '0' && (src[pos + 1] == 'x' || src[pos + 1] == 'X')) { + char *e; tv = (int64_t)strtoll(src + pos, &e, 0); memcpy(ts, src + pos, e - src - pos); ts[e - src - pos] = 0; pos = e - src; tk = T_NUM; return; + } + if (src[p] == '.') { + int nch = (unsigned char)src[p + 1]; + if (isdigit(nch) || nch == 'e' || nch == 'E' || !(isalpha(nch) || nch == '_')) fl = 1; + } else { + while (isdigit((unsigned char)src[p])) p++; + if (src[p] == '.') { + int nch = (unsigned char)src[p + 1]; + /* 1.5 and 1. and 1.e2 are floats. 1.field is an integer and a field. */ + if (isdigit(nch) || nch == 'e' || nch == 'E' || !(isalpha(nch) || nch == '_')) fl = 1; + } + } + if (!fl && (src[p] == 'e' || src[p] == 'E')) { + int q = p + 1; if (src[q] == '+' || src[q] == '-') q++; if (isdigit((unsigned char)src[q])) fl = 1; + } + if (fl) { + char b[80]; int n = 0, frac = 0; + while (src[pos] == '.' || isdigit((unsigned char)src[pos])) { + if (src[pos] == '.') { if (frac) break; frac = 1; } + if (n < 78) b[n++] = src[pos]; + pos++; + } + if (src[pos] == 'e' || src[pos] == 'E') { + int q = pos + 1; if (src[q] == '+' || src[q] == '-') q++; + if (isdigit((unsigned char)src[q])) { + if (n < 78) b[n++] = src[pos++]; + if (src[pos] == '+' || src[pos] == '-') { if (n < 78) b[n++] = src[pos++]; } + while (isdigit((unsigned char)src[pos]) && n < 78) b[n++] = src[pos++]; + } + } + b[n] = 0; memcpy(ts, b, (size_t)n + 1); tv = (int64_t)fop_parse(b); tk = T_FLT; return; + } + { char *e; tv = (int64_t)strtoll(src + pos, &e, 0); memcpy(ts, src + pos, e - src - pos); ts[e - src - pos] = 0; pos = e - src; tk = T_NUM; return; } + } + if (isalpha((unsigned char)src[pos]) || src[pos] == '_') { + i = 0; while (isalnum((unsigned char)src[pos]) || src[pos] == '_') ts[i++] = src[pos++]; + ts[i] = 0; + if (i == 1 && ts[0] == 'b' && src[pos] == '"') { + pos++; i = 0; + while (src[pos] && src[pos] != '"' && i < 4095) ts[i++] = (char)esc(); + ts[i] = 0; if (src[pos] == '"') pos++; tk = T_BSTR; return; + } + tk = T_ID; return; + } + if (src[pos] == '\'') { pos++; tv = esc(); if (src[pos++] != '\'') die("bad char literal"); tk = T_NUM; strcpy(ts, "'c'"); return; } + if (src[pos] == '"') { pos++; i = 0; while (src[pos] && src[pos] != '"') ts[i++] = (char)esc(); ts[i] = 0; pos++; tk = T_STR; return; } + tk = T_OP; + for (i = 0; ops3[i]; i++) if (!strncmp(src + pos, ops3[i], 3)) { strcpy(ts, ops3[i]); pos += 3; return; } + for (i = 0; ops2[i]; i++) if (!strncmp(src + pos, ops2[i], 2)) { strcpy(ts, ops2[i]); pos += 2; return; } + ts[0] = src[pos++]; ts[1] = 0; +} +typedef struct { char *src; int pos, line, tk, glue; int64_t tv; char ts[4096]; int xf, xs; } Lex; +static void lexsave(Lex *L) { L->src = src; L->pos = pos; L->line = line; L->tk = tk; L->glue = glue; L->tv = tv; memcpy(L->ts, ts, sizeof ts); L->xf = xf; L->xs = xs; } +static void lexrest(Lex *L) { src = L->src; pos = L->pos; line = L->line; tk = L->tk; glue = L->glue; tv = L->tv; memcpy(ts, L->ts, sizeof ts); xf = L->xf; xs = L->xs; } +static int is(const char *s) { return (tk == T_OP || tk == T_ID) && !strcmp(ts, s); } +static int accept(const char *s) { if (is(s)) { next(); return 1; } return 0; } +static void expect(const char *s) { if (!accept(s)) { char b[64]; sprintf(b, "expected '%s'", s); die(b); } } +static char *ident(void) { static char b[8][256]; static int k; char *r = b[k++ & 7]; if (tk != T_ID) die("expected a name"); strcpy(r, ts); next(); return r; } + +/* ---- symbols ---- */ +typedef struct { char name[64]; int kind, sig, defd; int64_t val; } Sym; +static Sym sy[16384]; static int nsy; +typedef struct { int at, sym; } Fix; +static Fix fx[65536]; static int nfx; +static Sym *find(const char *n) { int i; for (i = nsy; i--;) if (!strcmp(sy[i].name, n)) return &sy[i]; return 0; } +static Sym *add(const char *n, int kind, int64_t val) +{ + Sym *s = find(n); + if (s && s->kind == K_FN && kind == K_FN && !s->defd) { s->val = val; return s; } /* prototype or forward use */ + s = &sy[nsy++]; strncpy(s->name, n, 63); s->kind = kind; s->val = val; s->defd = 1; s->sig = 0; return s; +} +static void fnref(const char *n) /* push a function address, fixing it up later if needed */ +{ + Sym *s = find(n); + if (!s) { s = add(n, K_FN, 0); s->defd = 0; } + if (s->kind != K_FN) die("not a function"); + if (s->defd) { lit(s->val); return; } + fx[nfx].at = here; fx[nfx++].sym = (int)(s - sy); lit(0); +} + +/* ---- locals ---- */ +typedef struct { char name[64]; int slot, arr, flt, str; } Loc; +static Loc lo[1024]; static int nlo, nslot, infn; +static Loc *findl(const char *n) { int i; for (i = nlo; i--;) if (!strcmp(lo[i].name, n)) return &lo[i]; return 0; } +static void laddr(int k) { lit(ZFP); op(O_LD); if (k) { lit(k); op(O_ADD); } } + +/* ---- operator tables ---- */ +static void seq_isz(void) { lit(0); op(O_PICK); lit(0); op(O_SWAP); op(O_SUB); op(O_OR); lit(63); op(O_SHR); lit(1); op(O_ADD); } +/* ±0 is false. a float becomes 0 or 1 before the integer test. */ +static void ftest(void) { if (!xf) return; emit(1200); emit(0); op(605); lit(1); op(18); xf = 0; } +static void seq_nz(void) { lit(0); op(O_PICK); lit(0); op(O_SWAP); op(O_SUB); op(O_OR); lit(63); op(O_SHR); lit(0); op(O_SWAP); op(O_SUB); } +static int binprec(void) +{ + static const char *n[] = { "*", "/", "%", "+", "-", "<<", ">>", "<", ">", "<=", ">=", "==", "!=", "&", "^", "|", "&&", "||", 0 }; + static int p[] = { 10, 10, 10, 9, 9, 8, 8, 7, 7, 7, 7, 6, 6, 5, 4, 3, 2, 1 }; + int i; + if (tk != T_OP) return 0; + for (i = 0; n[i]; i++) if (!strcmp(ts, n[i])) return p[i]; + return 0; +} +static int fis(const char *o) +{ + return !strcmp(o, "+") || !strcmp(o, "-") || !strcmp(o, "*") || !strcmp(o, "/") + || !strcmp(o, "<") || !strcmp(o, ">") || !strcmp(o, "<=") || !strcmp(o, ">=") + || !strcmp(o, "==") || !strcmp(o, "!="); +} +static void fbinop(const char *o, int lf, int rf) +{ + if (!lf) { op(O_SWAP); op(608); op(O_SWAP); } + if (!rf) op(608); + if (!strcmp(o, "+")) { op(601); xf = 1; } + else if (!strcmp(o, "-")) { op(602); xf = 1; } + else if (!strcmp(o, "*")) { op(603); xf = 1; } + else if (!strcmp(o, "/")) { op(604); xf = 1; } + else if (!strcmp(o, "==")) { op(605); xf = 0; } + else if (!strcmp(o, "!=")) { op(605); lit(1); op(18); xf = 0; } + else if (!strcmp(o, "<")) { op(606); xf = 0; } + else if (!strcmp(o, ">")) { op(O_SWAP); op(606); xf = 0; } + else if (!strcmp(o, "<=")) { op(607); xf = 0; } + else if (!strcmp(o, ">=")) { op(O_SWAP); op(607); xf = 0; } + else xf = 0; +} +static int sis(const char *o) +{ + return !strcmp(o, "+") || !strcmp(o, "<") || !strcmp(o, ">") || !strcmp(o, "<=") + || !strcmp(o, ">=") || !strcmp(o, "==") || !strcmp(o, "!="); +} +static void sbinop(const char *o) +{ + if (!strcmp(o, "+")) { fnref("cat_s"); op(O_CALL); xs = 1; } + else if (!strcmp(o, "==")) { fnref("eq_s"); op(O_CALL); xs = 0; } + else if (!strcmp(o, "!=")) { fnref("eq_s"); op(O_CALL); lit(1); op(O_XOR); xs = 0; } + else if (!strcmp(o, "<")) { fnref("lt_s"); op(O_CALL); xs = 0; } + else if (!strcmp(o, ">")) { op(O_SWAP); fnref("lt_s"); op(O_CALL); xs = 0; } + else if (!strcmp(o, "<=")) { fnref("le_s"); op(O_CALL); xs = 0; } + else { op(O_SWAP); fnref("le_s"); op(O_CALL); xs = 0; } + xf = 0; +} +static void binop(const char *o) +{ + if (!strcmp(o, "*")) op(O_MUL); + else if (!strcmp(o, "/")) { fnref("zdiv"); op(O_CALL); } + else if (!strcmp(o, "%")) { fnref("zmod"); op(O_CALL); } + else if (!strcmp(o, "+")) op(O_ADD); + else if (!strcmp(o, "-")) op(O_SUB); + else if (!strcmp(o, "<<")) { fnref("zshl"); op(O_CALL); } + else if (!strcmp(o, ">>")) op(O_SHR); + else if (!strcmp(o, "<")) op(O_LT); + else if (!strcmp(o, ">")) { op(O_SWAP); op(O_LT); } + else if (!strcmp(o, "<=")) { op(O_SWAP); op(O_LT); lit(1); op(O_XOR); } + else if (!strcmp(o, ">=")) { op(O_LT); lit(1); op(O_XOR); } + else if (!strcmp(o, "==")) { op(O_SUB); seq_isz(); } + else if (!strcmp(o, "!=")) { op(O_SUB); seq_nz(); } + else if (!strcmp(o, "&")) op(O_AND); + else if (!strcmp(o, "^")) op(O_XOR); + else if (!strcmp(o, "|")) op(O_OR); + else die("bad operator"); +} + +/* ---- constant expressions (globals, array sizes, const) ---- */ +static int64_t cexpr(void); +static int64_t cprim(void) +{ + int64_t v; Sym *s; + if (tk == T_NUM) { v = tv; next(); return v; } + if (accept("-")) return -cprim(); + if (accept("~")) return ~cprim(); + if (accept("(")) { v = cexpr(); expect(")"); return v; } + if (tk == T_ID && (s = find(ts)) && (s->kind == K_CONST || (s->kind != K_GVAR && s->defd))) { next(); return s->val; } + die("constant expected"); return 0; +} +static int64_t cexpr(void) +{ + int64_t v = cprim(); + for (;;) { + if (accept("+")) v += cprim(); else if (accept("-")) v -= cprim(); + else if (accept("*")) v *= cprim(); else if (accept("|")) v |= cprim(); + else if (accept("&")) v &= cprim(); else if (accept("<<")) v <<= cprim(); + else if (accept(">>")) v >>= cprim(); else return v; + } +} + +/* ---- types ---- + zc0 has no types: a type is read and thrown away. all it has to know is the + shape, so that [n]T still reserves n words and the rest reserve one. the + grammar is the compiler's own (see ptype in compiler.zc): + ptype a type, then any number of (params): int(int, int) is a function type + ptype0 a type with its stars: int*, vec*, point** + ptype1 a base: [n]T, []T, fixed []T, fn, a name, or a generic instance + each returns the words a variable of the type takes: n for a fixed array, 1 + for a growable handle or anything scalar. */ +static int ptype0(void); +static int ptype(void); +static int ptype1(void) +{ + int n, e; + if (accept("[")) { + if (accept("]")) { ptype0(); return 1; } /* []T is a handle */ + n = (int)cexpr(); expect("]"); + e = ptype0(); + return n > 0 ? n * e : e; + } + if (is("fixed")) { next(); return ptype0(); } + if (is("fn")) { next(); return 1; } /* fn(int): the parameters follow, in ptype */ + if (tk != T_ID) { die("a type name"); return 1; } + ident(); + if (accept("<")) { /* generic arguments: vec */ + do { ptype(); } while (accept(",")); + if (is(">>")) ts[1] = 0; /* >> closes two lists: leave the second > */ + else expect(">"); + } + return 1; +} +static int ptype0(void) +{ + int n = ptype1(); + while (accept("*")) n = 1; + return n; +} +static int ptype(void) +{ + int n = ptype0(); + while (accept("(")) { + if (!accept(")")) { do { ptype(); } while (accept(",")); expect(")"); } + n = 1; + } + return n; +} +/* the ownership tag after a type, .u .m or .d: 0 when there is none */ +static int ptag; +static void ptagopt(void) +{ + ptag = 0; + if (!accept(".")) return; + if (is("m")) ptag = 1; else if (is("d")) ptag = 2; else if (is("u")) ptag = 3; else die("a tag is .u, .m or .d"); + next(); +} +/* an optional type in front of a name. the words it needs, or -1 when there is + no type here at all, which is how an untyped name is told from a type. the + parse is taken back when what follows is not a name. */ +/* words that open a statement or an expression, so a type never starts with + one of them. they are all plain identifiers to the lexer. */ +static int notatype(void) +{ + static const char *w[] = { "return", "if", "else", "while", "for", "do", "break", "continue", + "let", "var", "fn", "const", "enhance", "struct", "enum", "asm", "switch", "case", + "default", "immediate", "type", "include", "as", "in", "new", "sizeof", "typeof", + "o3", "unchecked", 0 }; + int i; + if (tk != T_ID) return 0; + for (i = 0; w[i]; i++) if (!strcmp(ts, w[i])) return 1; + return 0; +} +static int optype(void) +{ + Lex L; int n; + if (notatype() || (tk != T_ID && !is("["))) return -1; + lexsave(&L); pguess = 1; pbad = 0; n = ptype(); ptagopt(); pguess = 0; + if (tk == T_ID && !pbad) return n; + lexrest(&L); + return -1; +} +/* a name followed by ( : a function definition rather than a variable */ +static int isfn(void) +{ + Lex L; int r; + lexsave(&L); pguess = 1; + r = tk == T_ID; + if (r) { ident(); r = is("("); } + pguess = 0; lexrest(&L); + return r; +} + +/* ---- expressions ---- */ +static int lv; /* the stack top is an address not yet loaded */ +static void rval(void) { if (lv) { op(O_LD); lv = 0; } } +static void expr(void); +static void strlit(void) /* inline word string: jump over it, push its address */ +{ + int j = here, a, i, n = (int)strlen(ts); + lit(0); op(O_JMP); a = here; + for (i = 0; i < n; i++) emit((unsigned char)ts[i]); + emit(0); setlit(j, here); lit(a); next(); +} +/* b"...": length word, then bytes packed 8 per word, little-endian, plus a 0 */ +static void bstrlit(void) +{ + int j = here, a, i = 0, k, n = (int)strlen(ts); + uint64_t w; + lit(0); op(O_JMP); a = here; + emit(n); + while (i < n) { + w = 0; k = 0; + while (k < 8 && i < n) { w |= (uint64_t)(unsigned char)ts[i] << (k * 8); k++; i++; } + emit((int64_t)w); + } + if ((n & 7) == 0) emit(0); + setlit(j, here); lit(a); next(); +} +static int returns_bstr(const char *n) +{ + return !strcmp(n, "cat_s") || !strcmp(n, "slice_s") || !strcmp(n, "n_to_s") || !strcmp(n, "new_s"); +} +static int args(void) { int n = 0; expect("("); if (!accept(")")) { do { expr(); rval(); n++; } while (accept(",")); expect(")"); } return n; } +static void asmblock(void); +static void primary(void) +{ + char n[64]; Loc *l; Sym *s; + lv = 0; lvi = -1; xf = 0; xs = 0; + if (tk == T_FLT) { emit(1200); emit(tv); xf = 1; next(); return; } + if (tk == T_NUM) { lit(tv); next(); return; } + if (tk == T_BSTR) { bstrlit(); xs = 1; return; } + if (tk == T_STR) { strlit(); return; } + if (accept("(")) { expr(); expect(")"); return; } + if (is("asm")) { next(); asmblock(); return; } + strcpy(n, ident()); + if ((l = findl(n))) { laddr(l->slot); lv = !l->arr; if (!l->arr) { xf = l->flt; xs = l->str; lvi = (int)(l - lo); } return; } + s = find(n); + if (s && s->kind == K_CONST) { lit(s->val); return; } + if (s && s->kind == K_GVAR) { lit(s->val); lv = 1; return; } + if (s && s->kind == K_GARR) { lit(s->val); return; } + if (is("(")) { + int na = args(); + if (!strcmp(n, "put_s") && na == 1 && xs) fnref("put_bs"); + else if (!strcmp(n, "len")) fnref("len_s"); + else fnref(n); + op(O_CALL); + xs = returns_bstr(n); xf = 0; + return; + } + fnref(n); +} +static void incdec(int post, int d) +{ + if (!lv) die("++/-- needs an lvalue"); + lv = 0; + if (lvi >= 0) lo[lvi].str = 0; + xs = 0; + if (post) { lit(0); op(O_PICK); op(O_LD); lit(0); op(O_PICK); lit(d); op(O_ADD); lit(2); op(O_PICK); op(O_ST); op(O_SWAP); op(O_DROP); } + else { lit(0); op(O_PICK); op(O_LD); lit(d); op(O_ADD); lit(0); op(O_PICK); lit(2); op(O_PICK); op(O_ST); op(O_SWAP); op(O_DROP); } +} +static void postfix(void) +{ + int n, was; + primary(); + for (;;) { + if (accept("[")) { + was = xs; rval(); expr(); rval(); + if (was && accept(":")) { + expr(); rval(); expect("]"); fnref("slice_s"); op(O_CALL); + lv = 0; xs = 1; xf = 0; lvi = -1; + } else if (was) { + expect("]"); fnref("at_s"); op(O_CALL); + lv = 0; xs = 0; xf = 0; lvi = -1; + } else { expect("]"); op(O_ADD); lv = 1; xs = 0; xf = 0; lvi = -1; } + } + else if (is("(")) { rval(); n = args(); lit(n); op(O_PICK); op(O_CALL); op(O_SWAP); op(O_DROP); xf = 0; xs = 0; lvi = -1; } + else if (accept("++")) incdec(1, 1); + else if (accept("--")) incdec(1, -1); + else if (accept("as")) ident(); + else return; + } +} +static void unary(void) +{ + if (accept("-")) { + unary(); rval(); xs = 0; + if (xf) { emit(1200); emit(0); op(O_SWAP); op(602); } + else { lit(0); op(O_SWAP); op(O_SUB); } + } + else if (accept("!")) { unary(); rval(); ftest(); seq_isz(); xf = 0; xs = 0; } + else if (accept("~")) { unary(); rval(); lit(-1); op(O_XOR); xf = 0; xs = 0; } + else if (accept("*")) { unary(); rval(); lv = 1; xs = 0; } + else if (accept("&")) { unary(); if (!lv) die("& needs an lvalue"); lv = 0; xs = 0; } + else if (accept("++")) { unary(); incdec(0, 1); } + else if (accept("--")) { unary(); incdec(0, -1); } + else if (tk == T_ID && (!strcmp(ts, "u") || !strcmp(ts, "m") || !strcmp(ts, "d"))) { next(); expect("*"); unary(); } /* u*x: a recast, no code */ + else postfix(); +} +static void bin(int minp) +{ + int p, a, b, lf, rf, ls, rs; char o[8]; + unary(); + while ((p = binprec()) && p >= minp) { + lf = xf; ls = xs; strcpy(o, ts); next(); rval(); + if (!strcmp(o, "&&")) { + ftest(); a = here; lit(0); op(O_JZ); bin(p + 1); rval(); ftest(); seq_nz(); + b = here; lit(0); op(O_JMP); setlit(a, here); lit(0); setlit(b, here); xf = 0; xs = 0; + } else if (!strcmp(o, "||")) { + ftest(); a = here; lit(0); op(O_JZ); lit(1); b = here; lit(0); op(O_JMP); + setlit(a, here); bin(p + 1); rval(); ftest(); seq_nz(); setlit(b, here); xf = 0; xs = 0; + } else { + bin(p + 1); rval(); rf = xf; rs = xs; + if ((ls || rs) && sis(o)) { + if (!(ls && rs)) die("a byte string needs a byte string on the other side"); + sbinop(o); + } else if ((lf || rf) && fis(o)) { fbinop(o, lf, rf); xs = 0; } + else { binop(o); xf = 0; xs = 0; } + } + lv = 0; lvi = -1; + } +} +static void expr(void) +{ + char o[8]; int dst, lf; + bin(1); dst = lvi; + if (is("=")) { + if (!lv) die("assignment needs an lvalue"); + next(); expr(); rval(); + op(O_SWAP); lit(1); op(O_PICK); op(O_SWAP); op(O_ST); lv = 0; + if (dst >= 0) { lo[dst].flt = xf; lo[dst].str = xs; } + } else if (tk == T_OP && strlen(ts) >= 2 && ts[strlen(ts) - 1] == '=' && strcmp(ts, "==") && strcmp(ts, "!=") && strcmp(ts, "<=") && strcmp(ts, ">=")) { + if (!lv) die("assignment needs an lvalue"); + lf = xf; strcpy(o, ts); o[strlen(o) - 1] = 0; next(); + lit(0); op(O_PICK); op(O_LD); lv = 0; expr(); rval(); + if ((lf || xf) && fis(o)) fbinop(o, lf, xf); + else { binop(o); xf = 0; } + op(O_SWAP); lit(1); op(O_PICK); op(O_SWAP); op(O_ST); lv = 0; + if (dst >= 0) { lo[dst].flt = xf; lo[dst].str = 0; } + } +} + +/* ---- asm blocks: raw vm words ---- */ +static void asmblock(void) +{ + char ln[128][64], rn[256][64]; int la[128], nl = 0, ra[256], nr = 0, i, k; const char *c; + Loc *l; Sym *s; + expect("{"); + while (!accept("}")) { + if (tk == T_EOF) die("unterminated asm"); + if (tk == T_FLT) { emit(1200); emit(tv); next(); continue; } + if (tk == T_NUM) { lit(tv); next(); continue; } + if (tk == T_BSTR) { bstrlit(); continue; } + if (tk == T_STR) { strlit(); continue; } + if (accept("#")) { if (tk != T_NUM) die("# needs a number"); emit(tv); next(); continue; } + if (is("-")) { next(); if (tk == T_NUM && glue) { lit(-tv); next(); } else op(O_SUB); continue; } + if (accept(":")) { strcpy(ln[nl], ident()); la[nl++] = here; continue; } + if (strlen(ts) == 1 && (c = strchr(mn, ts[0]))) { op((int)(c - mn)); next(); continue; } + if (tk != T_ID) die("bad asm token"); + if ((l = findl(ts))) { laddr(l->slot); next(); continue; } + if ((s = find(ts)) && s->kind != K_FN) { lit(s->val); next(); continue; } + if (s) { fnref(ts); next(); continue; } + strcpy(rn[nr], ts); ra[nr++] = here; lit(0); next(); /* a label of this block */ + } + for (i = 0; i < nr; i++) { + for (k = -1; ++k < nl && strcmp(ln[k], rn[i]);) ; + if (k == nl) die("unknown asm label"); + setlit(ra[i], la[k]); + } +} + +/* ---- statements ---- */ +static int brk[256][64], nbrk[256], cont[256], nloop; +static int epi[1024], nepi; +static void stmt(void); +static void block(void) +{ + int save = nlo; + expect("{"); + while (!accept("}")) { if (tk == T_EOF) die("missing }"); stmt(); } + nlo = save; +} +static void jmp_to(int t) { lit(t); op(O_JMP); } +static void loopbody(int contaddr) +{ + nbrk[nloop] = 0; cont[nloop++] = contaddr; + stmt(); + nloop--; +} +static void patchbreaks(void) { int i; for (i = 0; i < nbrk[nloop]; i++) setlit(brk[nloop][i], here); } +static void stmt(void) +{ + int a, b, c, d; + if (accept(";")) return; + if (is("{")) { block(); return; } + { + int nw = optype(), n; + if (nw >= 0 || accept("let") || accept("var")) { + if (nw < 0) nw = 1; + do { + Loc *l = &lo[nlo++]; strcpy(l->name, ident()); l->arr = 0; + if (nw > 1) { l->slot = nslot; nslot += nw; l->arr = 1; } + else { + if (accept("[")) { n = cexpr(); expect("]"); nslot += n; l->slot = nslot - n; l->arr = 1; } + else l->slot = nslot++; + } + l->flt = 0; l->str = 0; + if (accept("=")) { expr(); rval(); laddr(l->slot); op(O_ST); l->flt = xf; l->str = xs; } + } while (accept(",")); + expect(";"); return; + } + } + if (accept("unchecked")) { stmt(); return; } + if (accept("if")) { + expect("("); expr(); rval(); ftest(); expect(")"); + a = here; lit(0); op(O_JZ); stmt(); + if (accept("else")) { b = here; lit(0); op(O_JMP); setlit(a, here); stmt(); setlit(b, here); } + else setlit(a, here); + return; + } + if (accept("while")) { + c = here; expect("("); expr(); rval(); ftest(); expect(")"); + a = here; lit(0); op(O_JZ); + loopbody(c); jmp_to(c); setlit(a, here); patchbreaks(); return; + } + if (accept("for")) { + int save = nlo; + expect("("); + if (!is(";")) { if (is("let") || is("var") || tk == T_ID || is("[")) { stmt(); goto cond; } expr(); rval(); op(O_DROP); } + expect(";"); + cond: c = here; + if (!is(";")) { expr(); rval(); ftest(); } else { lit(1); xf = 0; } + expect(";"); + a = here; lit(0); op(O_JZ); b = here; lit(0); op(O_JMP); + d = here; + if (!is(")")) { expr(); rval(); op(O_DROP); } + expect(")"); jmp_to(c); + setlit(b, here); + loopbody(d); jmp_to(d); setlit(a, here); patchbreaks(); + nlo = save; return; + } + if (accept("break")) { if (!nloop) die("break outside a loop"); brk[nloop - 1][nbrk[nloop - 1]++] = here; lit(0); op(O_JMP); expect(";"); return; } + if (accept("continue")) { if (!nloop) die("continue outside a loop"); jmp_to(cont[nloop - 1]); expect(";"); return; } + if (accept("return")) { + if (!is(";")) { expr(); rval(); } else lit(0); + expect(";"); epi[nepi++] = here; lit(0); op(O_JMP); return; + } + if (is("asm")) { next(); asmblock(); return; } + expr(); rval(); op(O_DROP); expect(";"); +} + +/* ---- top level ---- */ +static void fndef(int ret) +{ + char *n = ident(); Sym *s; int np = 0, tags = 0, t, frame, i, helper, busy, done, check, node; + if (dsn && !dnm[0]) strcpy(dnm, n); + char pn[16][64]; + s = find(n); + if (!s || s->kind != K_FN) { s = add(n, K_FN, 0); s->defd = 0; } + expect("("); + if (!accept(")")) { + do { + t = 0; ptag = 0; + if (optype() >= 0) t = ptag == 1 ? 1 : ptag == 2 ? 2 : 0; /* the type is read and dropped */ + else if (accept(".")) { /* .m name, .d name */ + if (is("m")) t = 1; else if (is("d")) t = 2; else die("a parameter tag is .m or .d"); + next(); + } + if (np < 12) tags += t << (2 * np); + strcpy(pn[np++], ident()); + } while (accept(",")); + expect(")"); + } + s->sig = ret + 2 * np + 64 * tags; + if (accept(";")) return; /* prototype */ + if (s->defd) die("function defined twice"); + s->val = here; s->defd = 1; +#ifdef ZC0DOCS + { /* the header is the source from the item's first token to the { that opens the body */ + int hl = tstart - hdr_start; + sgname[0] = 0; + if (hl > 0 && hl < (int)sizeof sgtext - 1) { + memcpy(sgtext, src + hdr_start, hl); sgtext[hl] = 0; + while (hl && isspace((unsigned char)sgtext[hl - 1])) sgtext[--hl] = 0; + strcpy(sgname, n); + } + } +#endif + nlo = 0; nslot = 0; nepi = 0; infn = 1; + for (i = 0; i < np; i++) { strcpy(lo[nlo].name, pn[i]); lo[nlo].arr = 0; lo[nlo].flt = 0; lo[nlo].str = 0; lo[nlo++].slot = nslot++; } + /* retain the caller's pointer until the frame has its final segment. */ + helper = !find("frame_busy") || !strcmp(s->name, "frame_grow") || !strcmp(s->name, "frame_leave"); + lit(ZFP); op(O_LD); if (!helper) { lit(0); op(O_PICK); } + frame = here; lit(0); op(O_SUB); lit(ZFP); op(O_ST); + if (!helper) { + lit(find("frame_busy")->val); op(O_LD); busy = here; lit(0); op(O_JZ); + done = here; lit(0); op(O_JMP); setlit(busy, here); + lit(ZFP); op(O_LD); lit(find("frame_floor")->val); op(O_LD); + lit(find("frame_guard")->val); op(O_LD); op(O_ADD); op(O_LT); check = here; lit(0); op(O_JZ); + lit(ZFP); op(O_LD); lit(1); op(O_PICK); lit(ZFP); op(O_ST); + fnref("frame_grow"); op(O_CALL); lit(ZFP); op(O_ST); + busy = here; lit(0); op(O_JMP); + setlit(check, here); setlit(done, here); op(O_DROP); setlit(busy, here); + } + for (i = np; i--;) { laddr(i); op(O_ST); } + block(); + lit(0); + for (i = 0; i < nepi; i++) setlit(epi[i], here); + lit(ZFP); op(O_LD); lit(nslot); op(O_ADD); lit(ZFP); op(O_ST); + if (!helper) { + lit(find("frame_node")->val); op(O_LD); node = here; lit(0); op(O_JZ); + lit(ZFP); op(O_LD); lit(find("frame_top")->val); op(O_LD); op(O_SUB); check = here; lit(0); op(O_JZ); + done = here; lit(0); op(O_JMP); setlit(check, here); + fnref("frame_leave"); op(O_CALL); lit(ZFP); op(O_ST); setlit(done, here); setlit(node, here); + } + op(O_RET); + setlit(frame, nslot); + infn = 0; +} +/* enhance name(params) [-> n] micro "recipe"; or ... vm { asm }; + emits the install descriptor; the booted genome installs it */ +static int nenh; +static void enhdef(void) +{ + char *n, pn[8][64]; int np = 0, nout = 1, d, na, pay, len = 0, i, j; Sym *s; + if (tk == T_NUM) { nout = tv; next(); } /* enhance 2 name(...) */ + n = ident(); + expect("("); + if (!accept(")")) { do { if (np == 8) die("at most 8 inputs"); strcpy(pn[np++], ident()); } while (accept(",")); expect(")"); } + d = here; for (i = 0; i < 7; i++) emit(0); + na = here; for (i = 0; i < np; i++) emit(0); + for (i = 0; i < np; i++) { img[na + i - GORG] = here; for (j = 0; pn[i][j]; j++) emit((unsigned char)pn[i][j]); emit(0); } + if (accept("micro")) { pay = here; while (tk == T_STR) { for (j = 0; ts[j]; j++) emit((unsigned char)ts[j]); next(); } emit(0); } + else if (accept("vm")) { pay = here; asmblock(); op(O_RET); len = here - pay; img[d + 1 - GORG] = 1; } + else die("expected micro or vm"); + img[d - GORG] = nenh; img[d + 2 - GORG] = np; img[d + 3 - GORG] = nout; + img[d + 4 - GORG] = pay; img[d + 5 - GORG] = len; img[d + 6 - GORG] = na; + s = add(n, K_ENH, nenh++); s->sig = np + nout * 16 + d * 256; + expect(";"); +} +static void top1(void) +{ + { + if (accept("unchecked")) { top1(); return; } + if (accept("fn")) { fndef(0); return; } + if (accept(".")) { if (strcmp(ident(), "u")) die("the only return tag is .u"); fndef(1); return; } /* .u name(...) */ + if (accept("const")) { optype(); do { char *n = ident(); if (dsn && !dnm[0]) strcpy(dnm, n); expect("="); add(n, K_CONST, cexpr()); } while (accept(",")); expect(";"); return; } + if (accept("enhance")) { enhdef(); return; } + { + int nw, typed, k, a, rt; + ptag = 0; nw = optype(); typed = nw >= 0; rt = ptag == 3; + if (typed || accept("let") || accept("var")) { + if (isfn()) { fndef(rt); return; } + do { + char *n = ident(); + if (dsn && !dnm[0]) strcpy(dnm, n); + k = 0; + if (typed) k = nw > 1 ? nw : 0; + else if (accept("[")) { k = (int)cexpr(); expect("]"); } + if (k > 1) { + a = here; while (k--) emit(0); add(n, K_GARR, a); + } else { + a = here; add(n, K_GVAR, a); emit(0); + if (accept("=")) { + if (tk == T_STR) { int i, l = (int)strlen(ts); img[a - GORG] = here; for (i = 0; i < l; i++) emit((unsigned char)ts[i]); emit(0); next(); } + else img[a - GORG] = cexpr(); + } + } + } while (accept(",")); + expect(";"); return; + } + } + die("expected fn, var or const"); + } +} + +/* one item, with the doc comment above it kept under the name it declares */ +static void top(void) +{ + while (tk != T_EOF) { + dsn = 0; dnm[0] = 0; +#ifdef ZC0DOCS + sgname[0] = 0; hdr_start = tstart; +#endif + if (dn && dline == line) { memcpy(dsnap, dbuf, dn); dsn = dn; dline = 0; } + top1(); +#ifdef ZC0DOCS /* enabled for images that carry editor documentation */ + if (dsn && dnm[0]) { + int j = here, a, i = 0, k, c; uint64_t w; char nm[80]; + lit(0); op(O_JMP); a = here; + emit(dsn); + while (i < dsn) { + w = 0; k = 0; + while (k < 8 && i < dsn) { c = (unsigned char)dsnap[i]; if (c > 255) c = 63; w |= (uint64_t)c << (k * 8); k++; i++; } + emit((int64_t)w); + } + if (!(dsn & 7)) emit(0); + setlit(j, here); + sprintf(nm, "doc %s", dnm); + add(nm, K_DOC, a); + } + if (sgname[0]) { /* the signature, as the header's own text, under "sig name" */ + int j = here, a, i = 0, k, c, sl = (int)strlen(sgtext); uint64_t w; char nm[96]; + lit(0); op(O_JMP); a = here; + emit(sl); + while (i < sl) { + w = 0; k = 0; + while (k < 8 && i < sl) { c = (unsigned char)sgtext[i]; w |= (uint64_t)c << (k * 8); k++; i++; } + emit((int64_t)w); + } + if (!(sl & 7)) emit(0); + setlit(j, here); + sprintf(nm, "sig %s", sgname); + add(nm, K_DOC, a); + } +#endif + dsn = 0; dnm[0] = 0; + } +} + +int main(void) +{ +#ifdef _WIN32 + _setmode(_fileno(stdin), _O_BINARY); + _setmode(_fileno(stdout), _O_BINARY); +#endif + char *buf = malloc(65536); int capacity = 65536, n = 0, i, j; Sym *st; + if (!buf) { fprintf(stderr, "zc0: cannot allocate source buffer\n"); return 1; } + for (;;) { + size_t got; + if (n + 1 == capacity) { + char *next; + if (capacity > 0x7fffffff / 2) { fprintf(stderr, "zc0: source is too large\n"); return 1; } + capacity *= 2; next = realloc(buf, (size_t)capacity); + if (!next) { fprintf(stderr, "zc0: cannot grow source buffer\n"); return 1; } + buf = next; + } + got = fread(buf + n, 1, (size_t)(capacity - n - 1), stdin); n += (int)got; + if (!got) { if (ferror(stdin)) { fprintf(stderr, "zc0: source read failed\n"); return 1; } break; } + } + buf[n] = 0; src = buf; next(); + /* image header: set the frame pointer, jump to start, then the symbol blob pointer and image end */ + lit(FTOP); lit(ZFP); op(O_ST); fnref("start"); op(O_JMP); emit(0); emit(0); + top(); + for (i = 0; i < nfx; i++) { + Sym *s = &sy[fx[i].sym]; + if (!s->defd) { fprintf(stderr, "zc0: undefined function %s\n", s->name); return 1; } + setlit(fx[i].at, s->val); + } + if (!(st = find("start")) || !st->defd) { fprintf(stderr, "zc0: no start\n"); return 1; } + /* symbol blob: kind, value, sig, name chars, 0 ... then a 0 kind */ + img[5] = here; + for (i = 0; i < nsy; i++) { + emit(sy[i].kind); emit(sy[i].val); emit(sy[i].sig); + for (j = 0; sy[i].name[j]; j++) emit((unsigned char)sy[i].name[j]); + emit(0); + } + emit(0); + img[6] = here; + printf("#define GORG %d\n#define GLEN %d\nstatic const int64_t genome[] = {", GORG, here - GORG); + for (i = 0; i < here - GORG; i++) printf("%s%lld", !i ? "\n" : i % 12 ? "," : ",\n", (long long)img[i]); + printf("\n};\n"); + return 0; +} diff --git a/src/tools/zcc b/src/tools/zcc new file mode 100755 index 0000000..19904ea --- /dev/null +++ b/src/tools/zcc @@ -0,0 +1,54 @@ +#!/bin/bash +# zcc: compiles a ZC (ZygoteC) program into a static binary. the binary holds +# the runtime, the program and a memory map sized to them, and needs nothing +# from the system but the kernel (linux x86-64 and any host with a static +# libc). the program reads and writes through the same syscalls as zy(1). +# +# src/tools/zcc [-o OUT] [-k] FILE.zc +# +# -o OUT the binary to write (default: FILE without .zc) +# -k keep the build directory and print where it is +# CC the C compiler (default cc). CFLAGS adds to the default -O2 +# +# the build is three steps: automap sizes the map to the program and writes the +# genome (the compiled program image) for it; the micro core and its linux port +# are compiled against that map; and the link is static. make run PROG= builds +# the same image without the static link. +set -e +here="$(cd "$(dirname "$0")/../.." && pwd)" +CC=${CC:-cc} +out=""; keep=0 +while [ $# -gt 0 ]; do + case "$1" in + -o) [ $# -ge 2 ] || { echo "zcc: -o needs a name" >&2; exit 2; }; out="$2"; shift 2 ;; + -k) keep=1; shift ;; + -h|--help) sed -n '2,15p' "$0"; exit 0 ;; + -*) echo "zcc: unknown option $1" >&2; exit 2 ;; + *) break ;; + esac +done +[ $# -eq 1 ] || { echo "usage: src/tools/zcc [-o OUT] [-k] FILE.zc" >&2; exit 2; } +src="$1" +[ -f "$src" ] || { echo "zcc: no such file: $src" >&2; exit 1; } +case "$src" in *.zc) ;; *) echo "zcc: the source must end in .zc" >&2; exit 2 ;; esac +[ -n "$out" ] || { out="${src%.zc}"; out="$(basename "$out")"; } +case "$src" in /*) abs="$src" ;; *) abs="$PWD/$src" ;; esac + +work="$(mktemp -d "${TMPDIR:-/tmp}/zcc.XXXXXX")" +cleanup() { [ "$keep" = 1 ] && echo "zcc: build kept in $work" >&2 || rm -rf "$work"; } +trap cleanup EXIT + +# the map and the genome for this program only +( cd "$here" && bash src/tools/automap.sh run "$work/run" src/runtime/run.zc "$abs" ) >/dev/null +cp "$here/src/micro/micro.c" "$here/src/micro/micro_port.h" "$here/src/micro/port_linux.c" \ + "$here/src/core/fops.h" "$here/src/core/bops.h" "$here/src/core/stack.h" "$work/run/" + +# static where the toolchain allows it; a plain link otherwise, with a note +static=-static +if ! printf 'int main(void){return 0;}\n' | $CC -x c -static -o "$work/probe" - >/dev/null 2>&1; then + static="" + echo "zcc: this toolchain cannot link -static; the binary is dynamic" >&2 +fi +$CC -O2 -w -fwrapv ${CFLAGS:-} $static "$work/run/micro.c" "$work/run/port_linux.c" \ + -I"$work/run" -o "$out" +echo "zcc: wrote $out ($(wc -c < "$out" | tr -d ' ') bytes)" diff --git a/src/web/build.sh b/src/web/build.sh new file mode 100755 index 0000000..48c784e --- /dev/null +++ b/src/web/build.sh @@ -0,0 +1,12 @@ +#!/bin/bash +# builds src/web/zc.wasm (or the path given as the first argument): the microcore, with the compiler genome inside, as +# WebAssembly. needs clang with the wasm32 target and wasm-ld. the page +# (src/web/index.html) loads it next to src/web/zc.js. +set -e +cd "$(dirname "$0")/../.." +clang --target=wasm32 -O2 -w -fwrapv -nostdlib -fno-builtin -mbulk-memory -DJTYPE=int \ + -Isrc/core -Isrc/micro src/micro/micro.c src/web/port_wasm.c \ + -Wl,--no-entry -Wl,--export=wasm_input -Wl,--export=wasm_input_cap -Wl,--export=wasm_set_input -Wl,--export=wasm_run \ + -Wl,--export=memory -Wl,--allow-undefined -Wl,--initial-memory=33554432 -Wl,-z,stack-size=65536 \ + -o "${1:-src/web/zc.wasm}" +ls -l "${1:-src/web/zc.wasm}" diff --git a/src/web/index.html b/src/web/index.html new file mode 100644 index 0000000..4cf7476 --- /dev/null +++ b/src/web/index.html @@ -0,0 +1,60 @@ + + + + +zc in the browser + +
+

zc in the browser

+

the microcore compiled to webassembly, with the whole compiler inside. the program below is compiled and run by it, the same as zy < file.zc.

+
+ + + loading +
+ +

+
+ + + diff --git a/src/web/port_wasm.c b/src/web/port_wasm.c new file mode 100644 index 0000000..c1e8483 --- /dev/null +++ b/src/web/port_wasm.c @@ -0,0 +1,86 @@ +/* WebAssembly port of the microcore. the page hands the program over as + bytes, calls run(), and gets output back one byte at a time through the + one import. build with src/web/build.sh; nothing here needs libc. */ +#include "micro_port.h" + +extern void js_putc(int c); +extern void js_msg(int c); + +static unsigned char inbuf[1 << 20]; +static int inn, inp; + +/* where the page writes the program, and how it says how long it is */ +unsigned char *wasm_input(void) { return inbuf; } +int wasm_input_cap(void) { return (int)sizeof inbuf; } +void wasm_set_input(int n) { inn = n; inp = 0; } + +/* the machine's memory: the linear memory above everything the module has in it. + the base is fixed when the first machine is made, and a grow only adds pages on + the top, so the base never moves. */ +static unsigned char *mbase; +static UCell mcap; /* bytes of machine memory the module has grown for */ + +Cell *micro_mem_alloc(UCell words) +{ + UCell need, extra, pages; + if (words > ((UCell)-1) / sizeof(Cell)) return 0; + if (!mbase) { + mbase = (unsigned char *)((UCell)__builtin_wasm_memory_size(0) * 65536u); + mcap = 0; + } + need = words * sizeof(Cell); + if (need <= mcap) return (Cell *)mbase; + extra = need - mcap; + pages = (extra + 65535u) / 65536u; + if (__builtin_wasm_memory_grow(0, (int)pages) < 0) return 0; + mcap += pages * 65536u; /* wasm zeroes the pages it grows by */ + return (Cell *)mbase; +} + +int micro_getc(void) { return inp < inn ? inbuf[inp++] : -1; } +int micro_eof(void) { return inp >= inn; } +void micro_putc(int c) { js_putc(c & 255); } +void micro_msg(const char *s) { while (*s) js_msg(*s++); } + +Cell micro_syscall(Cell *mem, Cell op, Cell a, Cell b, Cell c) +{ + switch (op) { + case 9: /* memory: the size of the machine and its heap */ + if (a == 0) return micro_mem_words(); + if (a == 1) return HEAP_LO; + if (a == 2) return HEAP_HI; + if (a == 3) return (Cell)micro_mem_words() * 8; + return -1; + case 7: /* exit: the page sees run() return */ + return -1; + case 21: /* argc: the page has no command line, so alone */ + return 1; + case 22: /* argv: entry 0 is , as in zy(1) */ + if (a != 0) return -1; + if (!b) return 6; + if (c < 7 || b + 6 >= micro_mem_words()) return -1; + { + static const char lead[] = ""; + int i; + for (i = 0; i < 6; i++) mem[b + i] = (unsigned char)lead[i]; + mem[b + 6] = 0; + } + return 6; + case 23: /* envc: a page has no environment */ + return 0; + } + return -1; +} + +/* the core multiplies 64 by 64 into 128 bits for the float helpers; wasm has no + such instruction and there is no compiler runtime to link, so this is it */ +void __multi3(unsigned long long *r, unsigned long long al, unsigned long long ah, unsigned long long bl, unsigned long long bh) +{ + unsigned long long a0 = al & 0xffffffffu, a1 = al >> 32, b0 = bl & 0xffffffffu, b1 = bl >> 32; + unsigned long long p00 = a0 * b0, p01 = a0 * b1, p10 = a1 * b0, p11 = a1 * b1; + unsigned long long mid = (p00 >> 32) + (p01 & 0xffffffffu) + (p10 & 0xffffffffu); + r[0] = (p00 & 0xffffffffu) | (mid << 32); + r[1] = p11 + (p01 >> 32) + (p10 >> 32) + (mid >> 32) + al * bh + ah * bl; +} + +int wasm_run(void) { return micro_run(); } diff --git a/src/web/zc.js b/src/web/zc.js new file mode 100644 index 0000000..4f14d3c --- /dev/null +++ b/src/web/zc.js @@ -0,0 +1,28 @@ +// runs zc in a browser or in node: the microcore compiled to webassembly, +// with the compiler genome inside. ZC.load(bytes) gives a machine, and +// machine.run(source) compiles and runs a program the way `zy < file.zc` does, +// returning what it printed. every run starts from a fresh instance. +(function (root) { + async function load(wasm) { + if (typeof wasm === 'string') wasm = await (await fetch(wasm)).arrayBuffer(); + const mod = await WebAssembly.compile(wasm); + return { + async run(source) { + let out = [], msg = [], mem; + const inst = await WebAssembly.instantiate(mod, { + env: { js_putc: c => { out.push(c); }, js_msg: c => { msg.push(c); } } + }); + const x = inst.exports; + const bytes = new TextEncoder().encode(source); + if (bytes.length > x.wasm_input_cap()) throw new Error('program too long for the input buffer'); + new Uint8Array(x.memory.buffer).set(bytes, x.wasm_input()); + x.wasm_set_input(bytes.length); + const status = x.wasm_run(); + const dec = new TextDecoder(); + return { output: dec.decode(new Uint8Array(out)), messages: dec.decode(new Uint8Array(msg)), status }; + } + }; + } + const ZC = { load }; + if (typeof module !== 'undefined' && module.exports) module.exports = ZC; else root.ZC = ZC; +})(typeof self !== 'undefined' ? self : this); diff --git a/tests/bench/bench.sh b/tests/bench/bench.sh new file mode 100644 index 0000000..ab6b07d --- /dev/null +++ b/tests/bench/bench.sh @@ -0,0 +1,36 @@ +#!/bin/bash +# o3 against plain code: each kernel in tests/programs/bench.zc on each backend, with +# the time to start up and compile (the 'z' run) taken off. needs bc. +cd "$(dirname "$0")/../.."; T=$(mktemp -d); trap 'rm -rf $T' EXIT +gcc -O2 -w -DJTYPE=int src/core/core.c src/core/io_linux.c -Isrc/core -o $T/zy +vms=("$T/zy"); names=("interp") +if command -v llvm-config >/dev/null; then + gcc -O2 -w -DJTYPE=int -DZYLLVM $(llvm-config --cflags) src/core/core.c src/core/io_linux.c -Isrc/core -o $T/zyl $(llvm-config --ldflags --libs) && { vms+=("$T/zyl"); names+=("llvm"); } +fi +if command -v aarch64-linux-gnu-gcc >/dev/null && command -v qemu-aarch64 >/dev/null; then + aarch64-linux-gnu-gcc -O2 -w -DJTYPE=int -static src/core/core.c src/core/io_linux.c -Isrc/core -o $T/zyj && { vms+=("qemu-aarch64 $T/zyj"); names+=("arm64 (qemu)"); } +fi +# seconds per run of kernel $2 on vm $1: the slope between $3 runs and 1 run, +# best of three each, so starting up and compiling cancel out +t(){ local s e; s=$(date +%s.%N); (cat tests/programs/bench.zc; printf '`%s%s' $2 $3) | $1 >/dev/null; e=$(date +%s.%N); echo "$e - $s" | bc; } +best(){ local b=999 x i; for i in 1 2 3; do x=$(t "$1" $2 $3); b=$(echo "if ($x < $b) $x else $b" | bc); done; echo $b; } +# grows the run count until the difference is at least a second, so the noise +# of starting up is small next to it +per(){ + local one n=4 many + one=$(best "$1" $2 1) + while :; do + many=$(best "$1" $2 $n) + if [ "$(echo "$many - $one > 1" | bc)" = 1 ] || [ $n -ge 4096 ]; then break; fi + n=$((n * 4)) + done + echo "scale=6; ($many - $one) / ($n - 1)" | bc +} +printf "%-14s %-8s %10s %10s %8s\n" backend kernel "plain ms" "o3 ms" speedup +for i in "${!vms[@]}"; do + for k in "collatz a b" "sieve c d" "digits e f" "fib g h"; do + set -- $k + p=$(per "${vms[$i]}" $2); o=$(per "${vms[$i]}" $3) + printf "%-14s %-8s %10.2f %10.2f %7.1fx\n" "${names[$i]}" $1 $(echo "$p * 1000" | bc) $(echo "$o * 1000" | bc) $(echo "scale=2; $p / $o" | bc) + done +done diff --git a/tests/bench/bench_bytes.sh b/tests/bench/bench_bytes.sh new file mode 100644 index 0000000..3287a91 --- /dev/null +++ b/tests/bench/bench_bytes.sh @@ -0,0 +1,33 @@ +#!/bin/bash +# byte-load loop, timed on a run-only image so the resident compiler is not +# in the process. the slope between 1 repetition and N drops startup. +# prints: backend ms/rep checksum +cd "$(dirname "$0")/../.." +T=$(mktemp -d) +trap 'rm -rf "$T"' EXIT +gcc -O2 -w src/tools/genmap.c -o "$T/genmap" || exit 1 +"$T/genmap" --preset run -o "$T/run" >/dev/null || exit 1 +cp src/tools/zc0.c src/micro/micro.c src/micro/micro_port.h src/micro/port_linux.c src/core/core.c src/core/io_linux.c src/core/fops.h src/core/bops.h src/core/stack.h "$T/run/" +gcc -O1 -w "$T/run/zc0.c" -o "$T/zc0" || exit 1 +cat "$T/run/rt.zc" src/runtime/run.zc tests/programs/bench_bytes.zc | "$T/zc0" > "$T/run/genome.h" || exit 1 +gcc -O2 -w -DJTYPE=int "$T/run/core.c" "$T/run/io_linux.c" -o "$T/zy" || exit 1 +gcc -O2 -w "$T/run/micro.c" "$T/run/port_linux.c" -o "$T/zym" || exit 1 +vms=("$T/zy" "$T/zym"); names=("interp" "micro") +if command -v llvm-config >/dev/null; then + gcc -O2 -w -DJTYPE=int -DZYLLVM $(llvm-config --cflags) "$T/run/core.c" "$T/run/io_linux.c" -o "$T/zyl" $(llvm-config --ldflags --libs) \ + && { vms+=("$T/zyl"); names+=("llvm"); } +fi +if command -v aarch64-linux-gnu-gcc >/dev/null && command -v qemu-aarch64 >/dev/null; then + aarch64-linux-gnu-gcc -O2 -w -DJTYPE=int -static "$T/run/core.c" "$T/run/io_linux.c" -o "$T/zyj" \ + && { vms+=("qemu-aarch64 $T/zyj"); names+=("arm64"); } +fi +# seconds for $2 repetitions on vm $1 +once(){ local s e; s=$(date +%s.%N); printf '%s' "$2" | $1 >/tmp/bench_bytes.out; e=$(date +%s.%N); python3 -c "print($e - $s)"; } +printf "%-10s %12s %s\n" backend "ms/rep" checksum +for i in "${!vms[@]}"; do + one=$(once "${vms[$i]}" 1) + many=$(once "${vms[$i]}" 40) + ms=$(python3 -c "print(round(($many - $one) / 39 * 1000, 3))") + sum=$(tr -d '\n' < /tmp/bench_bytes.out) + printf "%-10s %12s %s\n" "${names[$i]}" "$ms" "$sum" +done diff --git a/tests/bench/bench_float.sh b/tests/bench/bench_float.sh new file mode 100644 index 0000000..794e976 --- /dev/null +++ b/tests/bench/bench_float.sh @@ -0,0 +1,54 @@ +#!/bin/bash +# plain float loop against the same loop lifted by o3. the program is +# tests/programs/fbench.zc: 'a' is plain, 'b' is o3, and the digits are repetitions. +# the slope between 1 repetition and N drops startup and compile time, so +# this does not bootstrap the compiler. each kernel is poly() of 20000, +# and (int) of one call is 59997. +cd "$(dirname "$0")/../.." +T=$(mktemp -d) +trap 'rm -rf "$T"' EXIT +gcc -O2 -w -DJTYPE=int src/core/core.c src/core/io_linux.c -Isrc/core -o "$T/zy" || exit 1 +vms=("$T/zy"); names=("interp") +gcc -O2 -w src/micro/micro.c src/micro/port_linux.c -Isrc/core -Isrc/micro -o "$T/zym" && { vms+=("$T/zym"); names+=("micro"); } +if command -v llvm-config >/dev/null; then + gcc -O2 -w -DJTYPE=int -DZYLLVM $(llvm-config --cflags) src/core/core.c src/core/io_linux.c -Isrc/core -o "$T/zyl" $(llvm-config --ldflags --libs) \ + && { vms+=("$T/zyl"); names+=("llvm"); } +fi +if command -v aarch64-linux-gnu-gcc >/dev/null && command -v qemu-aarch64 >/dev/null; then + aarch64-linux-gnu-gcc -O2 -w -DJTYPE=int -static src/core/core.c src/core/io_linux.c -Isrc/core -o "$T/zyj" \ + && { vms+=("qemu-aarch64 $T/zyj"); names+=("arm64"); } +fi +src=$(cat tests/programs/fbench.zc) +# milliseconds per call of kernel $2 (a or b) on vm $1 +per() { + python3 - "$1" "$2" "$src" << 'PY' +import sys, time, subprocess +vm, which, src = sys.argv[1], sys.argv[2], sys.argv[3].encode() +def once(arg): + t0 = time.perf_counter() + p = subprocess.run(vm.split(), input=src + b"`" + arg.encode(), stdout=subprocess.PIPE, stderr=subprocess.DEVNULL) + return time.perf_counter() - t0, p.returncode, p.stdout +def best(arg): + b, out = 1e9, b"" + for _ in range(3): + d, r, o = once(arg) + if r == 0 and d < b: + b, out = d, o + return b, out +one, _ = best(which + "1") +n = 4 +while True: + many, out = best("%s%d" % (which, n)) + if many - one > 0.8 or n >= 1024: + break + n *= 4 +print("%.3f" % ((many - one) / (n - 1) * 1000)) +print(out.decode().strip(), file=sys.stderr) +PY +} +printf "%-10s %12s %12s %8s\n" backend "plain ms" "o3 ms" speedup +for i in "${!vms[@]}"; do + p=$(per "${vms[$i]}" a) + o=$(per "${vms[$i]}" b) + printf "%-10s %12s %12s %8s\n" "${names[$i]}" "$p" "$o" "$(python3 -c "print('%.2fx' % ($p / $o) if $o else 'n/a')")" +done diff --git a/tests/bench/bench_share.sh b/tests/bench/bench_share.sh new file mode 100644 index 0000000..b449e09 --- /dev/null +++ b/tests/bench/bench_share.sh @@ -0,0 +1,32 @@ +#!/bin/bash +# walk a shared cons spine from two parents, timed on a run-only image so the +# resident compiler is not in the process. the slope between 1 repetition +# and N drops startup. prints: backend ms/rep checksum +cd "$(dirname "$0")/../.." +T=$(mktemp -d) +trap 'rm -rf "$T"' EXIT +gcc -O2 -w src/tools/genmap.c -o "$T/genmap" || exit 1 +"$T/genmap" --preset run -o "$T/run" >/dev/null || exit 1 +cp src/tools/zc0.c src/micro/micro.c src/micro/micro_port.h src/micro/port_linux.c src/core/core.c src/core/io_linux.c src/core/fops.h src/core/bops.h src/core/stack.h "$T/run/" +gcc -O1 -w "$T/run/zc0.c" -o "$T/zc0" || exit 1 +cat "$T/run/rt.zc" src/runtime/run.zc tests/programs/bench_share.zc | "$T/zc0" > "$T/run/genome.h" || exit 1 +gcc -O2 -w -DJTYPE=int "$T/run/core.c" "$T/run/io_linux.c" -o "$T/zy" || exit 1 +gcc -O2 -w "$T/run/micro.c" "$T/run/port_linux.c" -o "$T/zym" || exit 1 +vms=("$T/zy" "$T/zym"); names=("interp" "micro") +if command -v llvm-config >/dev/null; then + gcc -O2 -w -DJTYPE=int -DZYLLVM $(llvm-config --cflags) "$T/run/core.c" "$T/run/io_linux.c" -o "$T/zyl" $(llvm-config --ldflags --libs) \ + && { vms+=("$T/zyl"); names+=("llvm"); } +fi +if command -v aarch64-linux-gnu-gcc >/dev/null && command -v qemu-aarch64 >/dev/null; then + aarch64-linux-gnu-gcc -O2 -w -DJTYPE=int -static "$T/run/core.c" "$T/run/io_linux.c" -o "$T/zyj" \ + && { vms+=("qemu-aarch64 $T/zyj"); names+=("arm64"); } +fi +once(){ local s e; s=$(date +%s.%N); printf '%s' "$2" | $1 >/tmp/bench_share.out; e=$(date +%s.%N); python3 -c "print($e - $s)"; } +printf "%-10s %12s %s\n" backend "ms/rep" checksum +for i in "${!vms[@]}"; do + one=$(once "${vms[$i]}" 1) + many=$(once "${vms[$i]}" 40) + ms=$(python3 -c "print(round(($many - $one) / 39 * 1000, 3))") + sum=$(tr -d '\n' < /tmp/bench_share.out) + printf "%-10s %12s %s\n" "${names[$i]}" "$ms" "$sum" +done diff --git a/tests/bench/bench_strings.sh b/tests/bench/bench_strings.sh new file mode 100644 index 0000000..c353342 --- /dev/null +++ b/tests/bench/bench_strings.sh @@ -0,0 +1,32 @@ +#!/bin/bash +# byte-string scan, concat and slice, timed on a run-only image so the +# resident compiler is not in the process. the slope between 1 repetition +# and N drops startup. prints: backend ms/rep checksum +cd "$(dirname "$0")/../.." +T=$(mktemp -d) +trap 'rm -rf "$T"' EXIT +gcc -O2 -w src/tools/genmap.c -o "$T/genmap" || exit 1 +"$T/genmap" --preset run -o "$T/run" >/dev/null || exit 1 +cp src/tools/zc0.c src/micro/micro.c src/micro/micro_port.h src/micro/port_linux.c src/core/core.c src/core/io_linux.c src/core/fops.h src/core/bops.h src/core/stack.h "$T/run/" +gcc -O1 -w "$T/run/zc0.c" -o "$T/zc0" || exit 1 +cat "$T/run/rt.zc" src/runtime/run.zc tests/programs/bench_strings.zc | "$T/zc0" > "$T/run/genome.h" || exit 1 +gcc -O2 -w -DJTYPE=int "$T/run/core.c" "$T/run/io_linux.c" -o "$T/zy" || exit 1 +gcc -O2 -w "$T/run/micro.c" "$T/run/port_linux.c" -o "$T/zym" || exit 1 +vms=("$T/zy" "$T/zym"); names=("interp" "micro") +if command -v llvm-config >/dev/null; then + gcc -O2 -w -DJTYPE=int -DZYLLVM $(llvm-config --cflags) "$T/run/core.c" "$T/run/io_linux.c" -o "$T/zyl" $(llvm-config --ldflags --libs) \ + && { vms+=("$T/zyl"); names+=("llvm"); } +fi +if command -v aarch64-linux-gnu-gcc >/dev/null && command -v qemu-aarch64 >/dev/null; then + aarch64-linux-gnu-gcc -O2 -w -DJTYPE=int -static "$T/run/core.c" "$T/run/io_linux.c" -o "$T/zyj" \ + && { vms+=("qemu-aarch64 $T/zyj"); names+=("arm64"); } +fi +once(){ local s e; s=$(date +%s.%N); printf '%s' "$2" | $1 >/tmp/bench_strings.out; e=$(date +%s.%N); python3 -c "print($e - $s)"; } +printf "%-10s %12s %s\n" backend "ms/rep" checksum +for i in "${!vms[@]}"; do + one=$(once "${vms[$i]}" 1) + many=$(once "${vms[$i]}" 40) + ms=$(python3 -c "print(round(($many - $one) / 39 * 1000, 3))") + sum=$(tr -d '\n' < /tmp/bench_strings.out) + printf "%-10s %12s %s\n" "${names[$i]}" "$ms" "$sum" +done diff --git a/tests/bench/run.py b/tests/bench/run.py new file mode 100755 index 0000000..69cb943 --- /dev/null +++ b/tests/bench/run.py @@ -0,0 +1,304 @@ +#!/usr/bin/env python3 +# Time tests/bench/sieve and tests/bench/trees on C and on zc. +# The number is the slope between 1 repetition and N: that drops process +# startup and, for zc, the time spent compiling the benchmark. It does not +# bootstrap the compiler. Each cell is the median of TRIALS slopes, with the +# min and the max as the spread. +# +# python3 tests/bench/run.py # everything +# python3 tests/bench/run.py check # checksums only +# python3 tests/bench/run.py sieve # one program +# TRIALS=5 GAP=0.8 python3 tests/bench/run.py +import os +import shutil +import subprocess +import sys +import tempfile +import time +from pathlib import Path + +ROOT = Path(__file__).resolve().parent.parent.parent +BENCH = Path(__file__).resolve().parent +TRIALS = int(os.environ.get("TRIALS", "5")) +GAP = float(os.environ.get("GAP", "0.8")) +NMAX = int(os.environ.get("NMAX", "200000")) +SIEVE_N = b"9592" # primes below 100000 +TREE_N = b"8191" # nodes in a full tree of depth 12 + + +def log(msg): + print(msg, file=sys.stderr, flush=True) + + +def run(cmd, stdin=None, env=None, cwd=None): + e = os.environ.copy() + if env: + e.update(env) + t0 = time.perf_counter() + p = subprocess.run(cmd, input=stdin, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=e, cwd=cwd) + return time.perf_counter() - t0, p.returncode, p.stdout, p.stderr + + +def pin(cmd): + if shutil.which("taskset"): + return ["taskset", "-c", "0"] + list(cmd) + return list(cmd) + + +def need(cmd, cwd=None): + p = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=cwd) + if p.returncode != 0: + sys.stderr.write(p.stderr.decode(errors="replace")) + raise SystemExit(f"command failed: {' '.join(cmd)}") + return p + + +def machine_lines(): + lines = [] + u = subprocess.check_output(["uname", "-srm"], text=True).strip() + lines.append(u) + model = "" + if Path("/proc/cpuinfo").exists(): + for line in Path("/proc/cpuinfo").read_text().splitlines(): + if line.startswith("model name"): + model = line.split(":", 1)[1].strip() + break + if model: + lines.append(model) + lines.append("gcc " + subprocess.check_output(["gcc", "-dumpversion"], text=True).strip()) + if shutil.which("clang"): + lines.append(subprocess.check_output(["clang", "--version"], text=True).splitlines()[0]) + if shutil.which("qemu-aarch64"): + lines.append(subprocess.check_output(["qemu-aarch64", "--version"], text=True).splitlines()[0]) + if shutil.which("llvm-config"): + lines.append("llvm " + subprocess.check_output(["llvm-config", "--version"], text=True).strip()) + lines.append("pinned to cpu 0 with taskset" if shutil.which("taskset") else "not pinned") + return lines + + +def build(td): + td = Path(td) + log("building vms") + zy = td / "zy" + need(["gcc", "-O3", "-w", "-DJTYPE=int", "src/core/core.c", "src/core/io_linux.c", "-Isrc/core", "-o", str(zy)], cwd=ROOT) + vms = {"interp": [str(zy)]} + if shutil.which("llvm-config"): + cflags = subprocess.check_output(["llvm-config", "--cflags"], text=True).split() + ld = subprocess.check_output(["llvm-config", "--ldflags", "--libs"], text=True).split() + zyl = td / "zyl" + need(["gcc", "-O3", "-w", "-DJTYPE=int", "-DZYLLVM", *cflags, "src/core/core.c", "src/core/io_linux.c", "-Isrc/core", "-o", str(zyl), *ld], cwd=ROOT) + vms["llvm"] = [str(zyl)] + if shutil.which("aarch64-linux-gnu-gcc") and shutil.which("qemu-aarch64"): + zyj = td / "zyj" + need(["aarch64-linux-gnu-gcc", "-O3", "-w", "-DJTYPE=int", "-static", "src/core/core.c", "src/core/io_linux.c", "-Isrc/core", "-o", str(zyj)], cwd=ROOT) + vms["arm64"] = ["qemu-aarch64", str(zyj)] + log("building C") + bins = {} + for comp, tag in (("gcc", "gcc"), ("clang", "clang")): + if comp == "clang" and not shutil.which("clang"): + continue + for lto in (False, True): + name = tag + ("-lto" if lto else "") + flags = [comp, "-O3", "-march=native"] + (["-flto"] if lto else []) + sw = td / f"sieve-{name}" + tw = td / f"trees-{name}" + need(flags + ["-o", str(sw), str(BENCH / "sieve.c")]) + need(flags + ["-o", str(tw), str(BENCH / "trees.c")]) + bins[f"sieve-{name}"] = sw + bins[f"trees-{name}"] = tw + # PGO: train and use the same output path so the .gcda name matches. + if shutil.which("gcc"): + for src, key, arg in (("sieve.c", "sieve-gcc-pgo", ["word", "80"]), ("trees.c", "trees-gcc-pgo", ["malloc", "40"])): + out = td / key + prof = td / f"prof-{key}" + prof.mkdir() + base = ["gcc", "-O3", "-march=native", "-flto", "-o", str(out), str(BENCH / src)] + need(base + [f"-fprofile-generate={prof}"]) + need([str(out), *arg]) + p = subprocess.run(base + [f"-fprofile-use={prof}", "-fprofile-correction"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + if p.returncode != 0 or b"profile count data file not found" in p.stderr: + log(f"PGO skipped for {key}: {p.stderr.decode(errors='replace')[:200]}") + else: + bins[key] = out + log(f"PGO applied for {key}") + return vms, bins + + +def slope(invoke, expected, trials=TRIALS): + def once(n): + cmd, stdin, env = invoke(n) + dt, rc, out, err = run(pin(cmd), stdin, env) + if rc != 0 or out.strip() != expected: + raise SystemExit( + f"bad run n={n} rc={rc}\ncmd={cmd}\nout={out[:200]!r}\nerr={err[:400]!r}" + ) + return dt + + dt1 = once(1) + n = 16 + per = 0.0 + while n <= NMAX: + dtn = once(n) + per = (dtn - dt1) / (n - 1) + gap = dtn - dt1 + if per > 0 and gap >= GAP: + break + if per > 0 and gap >= 0.05: + need_n = int(GAP / per) + 1 + need_n = min(NMAX, max(need_n, n)) + if need_n != n: + dtn = once(need_n) + per = (dtn - dt1) / (need_n - 1) + n = need_n + # a noisy calibrating run can undershoot the gap. grow until + # the kernel time actually dominates, or we hit the cap. + while n < NMAX and per > 0 and (per * (n - 1)) < GAP: + n = min(NMAX, max(n + 1, int(GAP / per) + 1)) + dtn = once(n) + per = (dtn - dt1) / (n - 1) + break + nxt = n * 4 + if nxt > NMAX: + n = NMAX + break + n = nxt + samples = [] + for i in range(trials): + a = once(1) + b = once(n) + ms = (b - a) / (n - 1) * 1000.0 + samples.append(ms) + log(f" trial {i + 1}/{trials}: {ms:.4f} ms (n={n})") + samples.sort() + mid = samples[len(samples) // 2] + return {"median": mid, "min": samples[0], "max": samples[-1], "n": n, "samples": samples} + + +def zc_call(vm, src, which, env=None): + def invoke(n): + return vm, src + b"`" + f"{which}{n}".encode(), env + return invoke + + +def c_call(binary, mode): + def invoke(n): + return [str(binary), mode, str(n)], None, None + return invoke + + +def show(title, rows): + print() + print(title) + print(f"{'path':<52} {'median ms':>10} {'min':>10} {'max':>10} {'reps':>8}") + for name, r in rows: + print(f"{name:<52} {r['median']:10.4f} {r['min']:10.4f} {r['max']:10.4f} {r['n']:8d}") + print("samples:") + for name, r in rows: + nums = " ".join(f"{s:.4f}" for s in r["samples"]) + print(f" {name}: {nums}") + sys.stdout.flush() + + +def main(): + which = sys.argv[1] if len(sys.argv) > 1 else "all" + os.chdir(ROOT) + for line in machine_lines(): + print(line) + print(f"trials {TRIALS}, gap {GAP}s, slope of 1 vs N, median of the trials") + print("arm64 is qemu-user on this host, not native arm64 hardware") + sys.stdout.flush() + td = tempfile.mkdtemp(prefix="zcbench-") + log(f"work dir {td}") + vms, bins = build(td) + sieve_src = (BENCH / "sieve.zc").read_bytes() + tree_src = (BENCH / "trees.zc").read_bytes() + + log("checksums") + checks = [ + (pin(vms["interp"]), sieve_src + b"`a1", SIEVE_N, None), + (pin(vms["interp"]), sieve_src + b"`b1", SIEVE_N, None), + (pin(vms["interp"]), sieve_src + b"`c1", SIEVE_N, None), + (pin(vms["interp"]), tree_src + b"`a2", TREE_N, None), + (pin(vms["interp"]), tree_src + b"`b1", TREE_N, None), + (pin(vms["interp"]), tree_src + b"`c1", TREE_N, None), + (pin([str(bins["sieve-gcc"]), "word", "1"]), None, SIEVE_N, None), + (pin([str(bins["sieve-gcc"]), "byte", "1"]), None, SIEVE_N, None), + (pin([str(bins["trees-gcc"]), "malloc", "1"]), None, TREE_N, None), + (pin([str(bins["trees-gcc"]), "arena", "1"]), None, TREE_N, None), + ] + if "llvm" in vms: + checks.append((pin(vms["llvm"]), sieve_src + b"`c1", SIEVE_N, {"ZYHOT": "1"})) + if "arm64" in vms: + checks.append((pin(vms["arm64"]), sieve_src + b"`c1", SIEVE_N, None)) + for cmd, stdin, expect, env in checks: + dt, rc, out, err = run(cmd, stdin, env) + if rc != 0 or out.strip() != expect: + raise SystemExit(f"checksum failed {cmd} rc={rc} out={out!r} err={err[:300]!r}") + log(f" ok {expect.decode()} in {dt:.2f}s") + if which == "check": + print("checksums ok") + return + + sieve_rows = [] + tree_rows = [] + + def add(rows, name, invoke, expect): + log(f"timing {name}") + rows.append((name, slope(invoke, expect))) + r = rows[-1][1] + log(f" median {r['median']:.4f} ms spread {r['min']:.4f}..{r['max']:.4f}") + + if which in ("all", "sieve"): + for key, label, mode in ( + ("sieve-gcc", "gcc -O3 -march=native, word flags", "word"), + ("sieve-gcc-lto", "gcc -O3 -march=native -flto, word flags", "word"), + ("sieve-gcc-pgo", "gcc -O3 -march=native -flto PGO, word flags", "word"), + ("sieve-clang", "clang -O3 -march=native, word flags", "word"), + ("sieve-clang-lto", "clang -O3 -march=native -flto, word flags", "word"), + ("sieve-gcc-lto", "gcc -O3 -march=native -flto, byte flags", "byte"), + ): + if key not in bins: + continue + add(sieve_rows, label, c_call(bins[key], mode), SIEVE_N) + add(sieve_rows, "zc interp, plain, bounds-checked []int", zc_call(vms["interp"], sieve_src, "a"), SIEVE_N) + add(sieve_rows, "zc interp, o3, bounds-checked []int", zc_call(vms["interp"], sieve_src, "b"), SIEVE_N) + add(sieve_rows, "zc interp, o3, raw *int", zc_call(vms["interp"], sieve_src, "c"), SIEVE_N) + if "llvm" in vms: + hot = {"ZYHOT": "1"} + add(sieve_rows, "zc llvm O3, ZYHOT=1, o3 raw *int", zc_call(vms["llvm"], sieve_src, "c", hot), SIEVE_N) + add(sieve_rows, "zc llvm O3, ZYHOT=1, o3 bounds-checked", zc_call(vms["llvm"], sieve_src, "b", hot), SIEVE_N) + if "arm64" in vms: + add(sieve_rows, "zc arm64 jit, qemu-user, o3 raw *int", zc_call(vms["arm64"], sieve_src, "c"), SIEVE_N) + show("sieve: primes below 100000, checksum 9592", sieve_rows) + + if which in ("all", "trees"): + for key, label, mode in ( + ("trees-gcc", "gcc -O3 -march=native, malloc", "malloc"), + ("trees-gcc-lto", "gcc -O3 -march=native -flto, malloc", "malloc"), + ("trees-gcc-pgo", "gcc -O3 -march=native -flto PGO, malloc", "malloc"), + ("trees-clang", "clang -O3 -march=native, malloc", "malloc"), + ("trees-clang-lto", "clang -O3 -march=native -flto, malloc", "malloc"), + ("trees-gcc-lto", "gcc -O3 -march=native -flto, arena", "arena"), + ("trees-clang-lto", "clang -O3 -march=native -flto, arena", "arena"), + ): + if key not in bins: + continue + add(tree_rows, label, c_call(bins[key], mode), TREE_N) + add(tree_rows, "zc interp, plain new/free", zc_call(vms["interp"], tree_src, "a"), TREE_N) + add(tree_rows, "zc interp, o3 new/free", zc_call(vms["interp"], tree_src, "b"), TREE_N) + add(tree_rows, "zc interp, o3 arena", zc_call(vms["interp"], tree_src, "c"), TREE_N) + if "llvm" in vms: + hot = {"ZYHOT": "1"} + add(tree_rows, "zc llvm O3, ZYHOT=1, plain new/free", zc_call(vms["llvm"], tree_src, "a", hot), TREE_N) + add(tree_rows, "zc llvm O3, ZYHOT=1, o3 new/free", zc_call(vms["llvm"], tree_src, "b", hot), TREE_N) + add(tree_rows, "zc llvm O3, ZYHOT=1, o3 arena", zc_call(vms["llvm"], tree_src, "c", hot), TREE_N) + if "arm64" in vms: + add(tree_rows, "zc arm64 jit, qemu-user, plain new/free", zc_call(vms["arm64"], tree_src, "a"), TREE_N) + add(tree_rows, "zc arm64 jit, qemu-user, o3 arena", zc_call(vms["arm64"], tree_src, "c"), TREE_N) + show("trees: depth 12, checksum 8191", tree_rows) + + print(f"\nwork dir left at {td}") + + +if __name__ == "__main__": + main() diff --git a/tests/bench/run.sh b/tests/bench/run.sh new file mode 100755 index 0000000..214d0a2 --- /dev/null +++ b/tests/bench/run.sh @@ -0,0 +1,5 @@ +#!/bin/bash +# build the VMs and the C binaries, check the checksums, then time them. +# see tests/bench/run.py for the slope, the trials, and the kernel letters. +cd "$(dirname "$0")/../.." +exec python3 tests/bench/run.py "$@" diff --git a/tests/bench/selfmod.zc b/tests/bench/selfmod.zc new file mode 100644 index 0000000..7e16fdb --- /dev/null +++ b/tests/bench/selfmod.zc @@ -0,0 +1,19 @@ +// self-modifying code in a loop: each pass rewrites a literal inside g, so the +// translation of g is dropped and made again, while a pile of other code stays +// translated. prints the sum so the engines can be compared. +int litw(int v) { return v + v + 1; } +int g() { return 1000; } +int work(int n) { int i = 0, s = 0; while (i < n) { s = s + i % 7; i++; } return s; } +int a1() { return work(50); } int a2() { return work(60); } int a3() { return work(70); } int a4() { return a1() + a2(); } +int main() { + word p = g; int i = 0, tot = 0; + while (*p != litw(1000)) p++; + a4(); a3(); + while (i < 20000) { + *p = litw(i); + tot = tot + g() + a3(); + i++; + } + put_n(tot); put_c(10); + return 0; +} diff --git a/tests/bench/sieve.c b/tests/bench/sieve.c new file mode 100644 index 0000000..6ac503e --- /dev/null +++ b/tests/bench/sieve.c @@ -0,0 +1,58 @@ +/* primes below N. the same loops as tests/bench/sieve.zc. + word: one int64 flag per number (what zc's fast path can do). + byte: one byte per number, the representation a C sieve would actually use. + the count is 9592 either way. + usage: sieve word|byte REPS */ +#include +#include +#include + +enum { N = 100000, EXPECT = 9592 }; + +static int sieve_word(int64_t *restrict f) +{ + int c = 0; + for (int i = 2; i < N; i++) f[i] = 1; + for (int i = 2; (long long)i * i < N; i++) + if (f[i]) + for (int j = i * i; j < N; j += i) f[j] = 0; + for (int i = 2; i < N; i++) c += (int)f[i]; + return c; +} + +static int sieve_byte(unsigned char *restrict f) +{ + int c = 0; + for (int i = 2; i < N; i++) f[i] = 1; + for (int i = 2; (long long)i * i < N; i++) + if (f[i]) + for (int j = i * i; j < N; j += i) f[j] = 0; + for (int i = 2; i < N; i++) c += f[i]; + return c; +} + +int main(int argc, char **argv) +{ + int reps, r = 0, k, byte; + int64_t *w = 0; + unsigned char *b = 0; + if (argc != 3) return 2; + byte = argv[1][0] == 'b'; + reps = atoi(argv[2]); + if (byte) b = malloc(N); + else w = malloc((size_t)N * sizeof *w); + if (!w && !b) return 3; + for (k = 0; k < reps; k++) { + r = byte ? sieve_byte(b) : sieve_word(w); + /* the stores have to happen: the next pass must see a clobbered buffer */ + __asm__ __volatile__("" : "+r"(r) : "r"(byte ? (void *)b : (void *)w) : "memory"); + } + if (r != EXPECT) { + fprintf(stderr, "bad %d\n", r); + return 1; + } + printf("%d\n", r); + free(w); + free(b); + return 0; +} diff --git a/tests/bench/sieve.zc b/tests/bench/sieve.zc new file mode 100644 index 0000000..1fb579b --- /dev/null +++ b/tests/bench/sieve.zc @@ -0,0 +1,51 @@ +// primes below N. the same loops as tests/bench/sieve.c: one word per flag, +// marks start at i*i. the count is 9592. +// a fixed []int, bounds-checked, plain bytecode +// b the same function lifted by o3 +// c o3 through a raw *int, no per-index check +const int N = 100000; + +int sieve(fixed []int f) { + int n = len(f), c = 0; + for (int i = 2; i < n; i++) f[i] = 1; + for (int i = 2; i * i < n; i++) + if (f[i]) for (int j = i * i; j < n; j += i) f[j] = 0; + for (int i = 2; i < n; i++) c += f[i]; + return c; +} +o3 int sieve3(fixed []int f) { + int n = len(f), c = 0; + for (int i = 2; i < n; i++) f[i] = 1; + for (int i = 2; i * i < n; i++) + if (f[i]) for (int j = i * i; j < n; j += i) f[j] = 0; + for (int i = 2; i < n; i++) c += f[i]; + return c; +} +o3 int sieve_raw(int* f) { + int n = N, c = 0; + for (int i = 2; i < n; i++) f[i] = 1; + for (int i = 2; i * i < n; i++) + if (f[i]) for (int j = i * i; j < n; j += i) f[j] = 0; + for (int i = 2; i < n; i++) c += f[i]; + return c; +} +int main() { + int which = get_c(), reps = 0, ch = get_c(), r = 0; + while (ch >= '0' && ch <= '9') { reps = reps * 10 + ch - '0'; ch = get_c(); } + if (which == 'c') { + int* raw = new(int, N); + while (reps > 0) { r = sieve_raw(raw); reps--; } + put_n(r); put_c(10); + free(d*raw); + return 0; + } + fixed []int f = new(fixed []int, N); + while (reps > 0) { + if (which == 'b') r = sieve3(f); + else r = sieve(f); + reps--; + } + put_n(r); put_c(10); + free(d*f); + return 0; +} diff --git a/tests/bench/trees.c b/tests/bench/trees.c new file mode 100644 index 0000000..df46447 --- /dev/null +++ b/tests/bench/trees.c @@ -0,0 +1,85 @@ +/* a full binary tree of depth D. checksum is the node count, 8191. + malloc: one malloc per node, then a recursive free. same shape as new(). + arena: one buffer, a bump pointer, no allocator inside the loop. + usage: trees malloc|arena REPS */ +#include +#include + +enum { D = 12, EXPECT = 8191 }; + +typedef struct node { + struct node *left, *right; +} node; + +static node *make(int d) +{ + node *n = (node *)malloc(sizeof *n); + if (d > 0) { + n->left = make(d - 1); + n->right = make(d - 1); + } else + n->left = n->right = 0; + return n; +} + +static int check(const node *n) +{ + if (!n->left) return 1; + return 1 + check(n->left) + check(n->right); +} + +static void destroy(node *n) +{ + if (!n) return; + destroy(n->left); + destroy(n->right); + free(n); +} + +static node *bump; + +static node *make_at(int d) +{ + node *n = bump++; + if (d > 0) { + n->left = make_at(d - 1); + n->right = make_at(d - 1); + } else + n->left = n->right = 0; + return n; +} + +int main(int argc, char **argv) +{ + int reps, r = 0, k, arena, nodes; + node *buf = 0; + if (argc != 3) return 2; + arena = argv[1][0] == 'a'; + reps = atoi(argv[2]); + nodes = (1 << (D + 1)) - 1; + if (arena) { + buf = malloc((size_t)nodes * sizeof *buf); + if (!buf) return 3; + } + for (k = 0; k < reps; k++) { + if (arena) { + node *root; + bump = buf; + root = make_at(D); + r = check(root); + __asm__ __volatile__("" : "+r"(r) : "r"(root) : "memory"); + } else { + node *root = make(D); + r = check(root); + __asm__ __volatile__("" : "+r"(r) : "r"(root) : "memory"); + destroy(root); + } + } + if (r != EXPECT) { + fprintf(stderr, "bad %d\n", r); + return 1; + } + printf("%d\n", r); + free(buf); + return 0; +} diff --git a/tests/bench/trees.zc b/tests/bench/trees.zc new file mode 100644 index 0000000..c8f46ef --- /dev/null +++ b/tests/bench/trees.zc @@ -0,0 +1,92 @@ +// a full binary tree of depth D. checksum is the node count, 8191. +// each node is two pointers. new() rounds that plus the 5-word header up +// to 8 words (a 4-word block cannot hold the header). free of the owned +// root recycles the tree onto the size-class lists. +// a plain new / own / free +// b the same, marked o3 (calls and new still leave the region) +// c one array, used as an arena of pairs. no allocator in the loop. +// the same shapes are tests/bench/trees.c. +const int D = 12; +const int WORDS = 16382; // two words for each of the 8191 nodes + +struct node { node* left, right; }; +int* heap; +int used; + +node*.u make(int depth) { + node* n = new(node); + if (depth > 0) { + n.left = make(depth - 1); + n.right = make(depth - 1); + } else { + n.left = 0; + n.right = 0; + } + return u*n; +} +o3 node*.u make3(int depth) { + node* n = new(node); + if (depth > 0) { + n.left = make3(depth - 1); + n.right = make3(depth - 1); + } else { + n.left = 0; + n.right = 0; + } + return u*n; +} +int check(node* n) { + if (!n.left) return 1; + return 1 + check(n.left) + check(n.right); +} +o3 int check3(node* n) { + if (!n.left) return 1; + return 1 + check3(n.left) + check3(n.right); +} + +// a node id is its word index plus one, so 0 can mean "no child" +o3 int build(int depth) { + int i = used; + used = used + 2; + if (depth > 0) { + heap[i] = build(depth - 1); + heap[i + 1] = build(depth - 1); + } else { + heap[i] = 0; + heap[i + 1] = 0; + } + return i + 1; +} +o3 int ck(int id) { + int i, l; + if (id == 0) return 0; + i = id - 1; + l = heap[i]; + if (l == 0) return 1; + return 1 + ck(l) + ck(heap[i + 1]); +} + +int main() { + int which = get_c(), reps = 0, ch = get_c(), r = 0; + while (ch >= '0' && ch <= '9') { reps = reps * 10 + ch - '0'; ch = get_c(); } + if (which == 'c') { + heap = new(int, WORDS); + while (reps > 0) { used = 0; r = ck(build(D)); reps--; } + put_n(r); put_c(10); + return 0; + } + while (reps > 0) { + if (which == 'b') { + node* root = make3(D); + r = check3(root); + free(d*root); + } else { + node* root = make(D); + r = check(root); + free(d*root); + } + reps--; + } + put_n(r); put_c(10); + return 0; +} diff --git a/tests/data/hi.txt b/tests/data/hi.txt new file mode 100644 index 0000000..ce01362 --- /dev/null +++ b/tests/data/hi.txt @@ -0,0 +1 @@ +hello diff --git a/tests/data/sbv.c b/tests/data/sbv.c new file mode 100644 index 0000000..7e12680 --- /dev/null +++ b/tests/data/sbv.c @@ -0,0 +1,18 @@ +/* the c side of tests/programs/structval.zc: functions that take structs by value. test.sh + builds it to build/sbv.so, and the test loads it with cload. */ +typedef struct { long a, b; } pair; +typedef struct { double x, y; } vec; +typedef struct { long a, b, c; } trio; +long pair_sum(pair p) { return p.a * 10 + p.b; } +long pair_sum2(pair p, pair q) { return p.a * 1000 + p.b * 100 + q.a * 10 + q.b; } +long vec_scaled(vec v, long k) { return (long)(v.x * 2 + v.y * 3) * k; } +long many(long a, long b, long c, long d, long e, long f, pair p) { return a + b + c + d + e + f + p.a * 100 + p.b; } +long trio_sum(long a, long b, long c, long d, long e, long f, trio t) { return a + b + c + d + e + f + t.a * 100 + t.b * 10 + t.c; } +long eight(long a, long b, long c, long d, long e, long f, long g, long h, pair p) { return a + b + c + d + e + f + g + h + p.a * 100 + p.b; } +pair mkpair(long a, long b) { pair r; r.a = a; r.b = b; return r; } +vec mkvec(double x, double y) { vec r; r.x = x + 1; r.y = y + 1; return r; } +trio mktrio(long a, long b, long c) { trio r; r.a = a; r.b = b; r.c = c; return r; } +typedef struct { long a; double x; } mixed; +mixed mkmixed(long a, double x) { mixed r; r.a = a + 1; r.x = x * 2; return r; } +typedef struct { double x, y, z; } v3; +v3 mkv3(double x) { v3 r; r.x = x; r.y = x + 1; r.z = x + 2; return r; } diff --git a/tests/drivers/code_address_growth_test.py b/tests/drivers/code_address_growth_test.py new file mode 100644 index 0000000..a454406 --- /dev/null +++ b/tests/drivers/code_address_growth_test.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 +# native address metadata grows without losing warm translations or rewrite tracking. +import subprocess +import sys + +source = '''o3 int warmed(int value) { return value + 1; } +fn main() { + put_n(warmed(6)); put_c(32); + let code = new(6); + code[0] = code + 3; code[3] = 8; code[4] = 15; code[5] = 18; + put_n(syscall(9, 9, code + 6, 0) >= code + 6); put_c(32); + put_n(code()); put_c(32); + fill_w(code + 4, 19, 1); put_n(code()); put_c(32); + let replacement = new(1); replacement[0] = 23; + copy_w(code + 4, replacement, 1); put_n(code()); put_c(32); + gc(); put_n(warmed(8)); put_c(32); + put_n(syscall(9, 9, syscall(9, 0, 0, 0) + 1, 0) == -1); put_c(10); + return 0; +} +''' +result = subprocess.run(sys.argv[1:], input=source, text=True, capture_output=True, timeout=20) +assert result.returncode == 0 and result.stdout == '7 1 7 9 11 9 1\n', (result.returncode, result.stdout, result.stderr) +print('ok new code addresses, warm calls, bulk rewrites, and denied metadata growth') diff --git a/tests/drivers/code_emission_growth_test.py b/tests/drivers/code_emission_growth_test.py new file mode 100644 index 0000000..f13c141 --- /dev/null +++ b/tests/drivers/code_emission_growth_test.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 +# emitted code and global data keep their addresses when the program area fills. +import argparse +import subprocess + +parser = argparse.ArgumentParser() +parser.add_argument('--length', type=int, default=600000) +parser.add_argument('command', nargs=argparse.REMAINDER) +options = parser.parse_args() +source = 'int earlier() { return 7; }\n' +source += f'let storage[{options.length}];\n' +source += 'let text = "' + 's' * 3500 + '";\n' +source += 'int later(int n) { return earlier() + n; }\n' +source += '''fn main() { + storage[len(storage) - 1] = 9; + gc(); put_n(later(3)); put_c(32); put_n(storage[len(storage) - 1]); put_c(32); + put_n(len_w(text)); put_c(32); put_n(code_blocks != 0); put_c(32); + let quoted = quote { return 5; }; + put_n(eval(quoted)); put_c(32); + let f = fn(float x) { return x + 1.5; }; put_n((f(2.0) as word) == 0x400c000000000000); put_c(10); + return 0; +} +''' +result = subprocess.run(options.command, input=source, text=True, capture_output=True, timeout=40) +assert result.returncode == 0 and result.stdout == '10 9 3500 1 5 1\n', (result.returncode, result.stdout, result.stderr) +print('ok linked code segments preserve functions, globals, strings, quotes, and float payloads') + +# force the next float opcode and its payload to need a new segment. +source = 'immediate fn force_edge() { next(); hlimit = here + 3; return 0; }\n' +source += 'fn main() { force_edge let value = 1.5; gc(); put_n((value as word) == 0x3ff8000000000000); put_c(10); return 0; }\n' +result = subprocess.run(options.command, input=source, text=True, capture_output=True, timeout=20) +assert result.returncode == 0 and result.stdout == '1\n', (result.returncode, result.stdout, result.stderr) +print('ok a float opcode and its payload remain adjacent at a segment boundary') +source = 'immediate fn force_edge() { next(); hlimit = here + 3; return 0; }\n' +source += 'force_edge\nenhance cube(x) vm { 0 $ 0 $ * * };\n' +source += 'enhance sum(a, b) micro "a = add a b";\n' +source += 'fn main() { gc(); put_n(cube(7)); put_c(32); put_n(sum(3, 4)); put_c(10); return 0; }\n' +result = subprocess.run(options.command, input=source, text=True, capture_output=True, timeout=20) +assert result.returncode == 0 and result.stdout == '343 7\n', (result.returncode, result.stdout, result.stderr) +print('ok vm and micro recipes remain contiguous after a code reservation') diff --git a/tests/drivers/compiler_growth_test.py b/tests/drivers/compiler_growth_test.py new file mode 100644 index 0000000..982611c --- /dev/null +++ b/tests/drivers/compiler_growth_test.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python3 +# focused growth checks for compiler columns, stable names, types, and scratch stacks. +import argparse +import subprocess +import sys + +parser = argparse.ArgumentParser() +parser.add_argument('server') +parser.add_argument('--names', type=int, default=18000) +options = parser.parse_args() +server = options.server +assert options.names > 0 + + +def run(source, expected): + result = subprocess.run([server], input=source, text=True, capture_output=True, timeout=30) + assert result.returncode == 0, result.stderr + assert result.stdout == expected, result.stdout[:1000] + + +locals_source = 'fn main() {\n' +locals_source += ''.join(f'int local_{i} = {i};\n' for i in range(900)) +locals_source += 'put_n(local_899); put_c(10); return 0; }\n' +run(locals_source, '899\n') + +types_source = ''.join(f'type array_{i} = [{i + 1}]int;\n' for i in range(1500)) +types_source += 'fn main() { put_n(ntl > 1500); put_c(10); gc(); put_n(tsz(tarr(tint, 1500))); put_c(10); return 0; }\n' +run(types_source, '1\n1500\n') + +names_source = ''.join(f'const int repeated_long_identifier_for_pool_and_hash_growth_{i} = {i};\n' for i in range(options.names)) +names_source += f'''fn main() {{ + word saved = intern("a name that must survive growth"); + reserve_names(hashcap * 4); reserve_symbols(symcap * 2); reserve_locals(loccap * 2); + reserve_fixes(fixcap * 2); reserve_R_SRC(4096); reserve_R_CTX(4096); + reserve_R_LOOP(4096); reserve_R_SNAP(32768); reserve_R_QBUF(32768); reserve_R_EXIT(8192); + gc(); + put_n(saved == intern("a name that must survive growth")); put_c(10); + put_n(repeated_long_identifier_for_pool_and_hash_growth_{options.names - 1}); put_c(10); + put_n(poolblocks != 0 && symcap > NS && hashcap > IHN); put_c(10); + return 0; +}} +''' +run(names_source, f'1\n{options.names - 1}\n1\n') + +long_name = 'identifier_' + 'x' * 1500 +run(f'const int {long_name} = 7; fn main() {{ put_n({long_name}); put_c(10); return 0; }}\n', '7\n') + +mark_source = '''let roots[60000]; +unchecked fn main() { + int i = 0; + while (i < 60000) { roots[i] = new(1); i++; } + gc(); put_n(markcap > MARKN); put_c(32); put_n(live_objects() == 60000); put_c(32); + fill_w(roots, 0, 60000); scrub(); gc(); + // conservative roots can retain incidental addresses in code or untyped words. + put_n(live_objects() < 1000); put_c(10); return 0; +} +''' +run(mark_source, '1 1 1\n') + +generics_source = ''.join(f'int generic_{i}() {{ return sizeof(T); }}\n' for i in range(90)) +generics_source += 'int size_of() { return sizeof(T); }\n' +generics_source += 'fn main() { int total = 0;\n' +generics_source += ''.join(f'total += generic_{i}();\n' for i in range(90)) +generics_source += ''.join(f'total += size_of<[{i + 1}]int>();\n' for i in range(150)) +generics_source += 'gc(); put_n(total); put_c(10); return 0; }\n' +run(generics_source, '11415\n') + +captures_source = 'fn main() {\n' + ''.join(f'int captured_{i} = {i};\n' for i in range(70)) +captures_source += 'let f = fn() { return ' + ' + '.join(f'captured_{i}' for i in range(70)) + '; };\n' +captures_source += 'gc(); put_n(f()); put_c(10); return 0; }\n' +run(captures_source, '2415\n') + +nested_source = 'fn main() { int captured = 7;\n' +nested_source += ''.join(f'let nested_{i} = fn() {{\n' for i in range(12)) +nested_source += 'return captured;\n' +nested_source += ''.join(f'}}; return nested_{i}();\n' for i in reversed(range(1, 12))) +nested_source += '}; gc(); put_n(nested_0()); put_c(10); return 0; }\n' +run(nested_source, '7\n') +assembly_source = 'fn main() { int value = asm { ' + 'future _ ' * 300 +assembly_source += ''.join(f':label_{i} ' for i in range(200)) + ':future 7 }; gc(); put_n(value); put_c(10); return 0; }\n' +run(assembly_source, '7\n') +print('ok compiler tables, long identifiers, and mark work grow without losing references') diff --git a/tests/drivers/core_lsp_test.js b/tests/drivers/core_lsp_test.js new file mode 100644 index 0000000..41d0095 --- /dev/null +++ b/tests/drivers/core_lsp_test.js @@ -0,0 +1,43 @@ +// check maintained zc sources through the editor document protocol, one file at a time. +const assert = require('assert'); +const fs = require('fs'); +const cp = require('child_process'); +const path = require('path'); +const target = process.argv[2]; +const files = ['src/runtime/rt.zc', 'src/compiler/compiler.zc', 'src/runtime/run.zc', 'src/lib/std.zc', 'src/lsp/server.zc', 'src/tools/build.zc']; +const frame = message => { + const body = Buffer.from(JSON.stringify(message)); + return Buffer.concat([Buffer.from('Content-Length: ' + body.length + '\r\n\r\n'), body]); +}; +for (const file of files) { + const uri = 'file://' + path.resolve(file); + const text = fs.readFileSync(file, 'utf8'); + const messages = [ + { jsonrpc: '2.0', id: 1, method: 'initialize', params: {} }, + { jsonrpc: '2.0', method: 'initialized', params: {} }, + { jsonrpc: '2.0', method: 'textDocument/didOpen', params: { textDocument: { uri, version: 1, languageId: 'zc', text } } }, + { jsonrpc: '2.0', id: 2, method: 'textDocument/semanticTokens/full', params: { textDocument: { uri } } }, + { jsonrpc: '2.0', method: 'textDocument/didChange', params: { textDocument: { uri, version: 2 }, contentChanges: [{ text: text + '\n' }] } }, + { jsonrpc: '2.0', method: 'textDocument/didClose', params: { textDocument: { uri } } }, + { jsonrpc: '2.0', id: 3, method: 'shutdown', params: null }, + { jsonrpc: '2.0', method: 'exit' }, + ]; + const result = cp.spawnSync(target, [], { input: Buffer.concat(messages.map(frame)), timeout: 30000, maxBuffer: 32 << 20 }); + assert.strictEqual(result.status, 0, file + ': ' + result.stderr); + let data = result.stdout; + const replies = []; + while (data.length) { + const end = data.indexOf('\r\n\r\n'); + assert.ok(end >= 0, 'complete protocol header'); + const length = Number(/Content-Length:\s*(\d+)/i.exec(data.subarray(0, end).toString())[1]); + replies.push(JSON.parse(data.subarray(end + 4, end + 4 + length))); + data = data.subarray(end + 4 + length); + } + const diagnostics = replies.filter(m => m.method === 'textDocument/publishDiagnostics'); + assert.strictEqual(diagnostics.length, 3, file + ': open, change, and close publish diagnostics'); + const errors = diagnostics.flatMap(m => m.params.diagnostics).filter(d => d.severity === 1); + assert.deepStrictEqual(errors, [], file + ': ' + errors.map(d => (d.range.start.line + 1) + ': ' + d.message).join('\n')); + assert.ok(replies.find(m => m.id === 2).result.data.length, file + ': semantic tokens are available'); + assert.deepStrictEqual(diagnostics[2].params.diagnostics, [], 'close clears diagnostics'); + console.log('ok editor open, highlight, change, and close: ' + file); +} diff --git a/tests/drivers/diagcache_test.js b/tests/drivers/diagcache_test.js new file mode 100644 index 0000000..43966cf --- /dev/null +++ b/tests/drivers/diagcache_test.js @@ -0,0 +1,140 @@ +// the rules of the diagnostics cache (editors/vscode/client/diagcache.js), with +// plain node and no vscode: a key covers the server build, the text, and every +// file the text reads through include, transitively. +// +// node tests/drivers/diagcache_test.js + +const fs = require('fs'); +const os = require('os'); +const path = require('path'); +const assert = require('assert'); +const dc = require(path.join(__dirname, '..', '..', 'editors', 'vscode', 'client', 'diagcache.js')); + +let passed = 0, failed = 0; +function check(name, fn) { + try { fn(); passed++; console.log('ok ' + name); } + catch (e) { failed++; console.log('FAIL ' + name + ': ' + e.message); } +} + +const root = fs.mkdtempSync(path.join(os.tmpdir(), 'zc-diagcache-')); +const write = (rel, text) => { const p = path.join(root, rel); fs.mkdirSync(path.dirname(p), { recursive: true }); fs.writeFileSync(p, text); return p; }; +const A = write('a.zc', 'include "b.zc";\nfn main() { return helper(); }\n'); +const B = write('b.zc', 'include "c.zc";\n'); +const C = write('c.zc', 'fn helper() { return 1; }\n'); +const U = write('unrelated.zc', 'fn other() { return 2; }\n'); +const aText = fs.readFileSync(A, 'utf8'); + +const keyA = (stamp = 'S1', text = aText, p = A) => dc.diagKey(stamp, text, p, root).key; + +check('includeNames reads every include, however it is spaced', () => { + assert.deepStrictEqual(dc.includeNames('include "a.zc";\ninclude"b/c.zc";\nfn f() {}'), ['a.zc', 'b/c.zc']); + assert.deepStrictEqual(dc.includeNames('fn f() { return 1; }'), []); +}); + +check('includeNames ignores comments and literals while allowing comments between tokens', () => { + assert.deepStrictEqual(dc.includeNames('// include "fake.zc"\n/* include "also-fake.zc" */\nlet s = "include \\"string.zc\\"";\ninclude /* dependency */ "real.zc";'), ['real.zc']); +}); + +check('the same tree gives the same key', () => { + assert.strictEqual(keyA(), keyA()); + assert.match(keyA(), /^[0-9a-f]{64}$/); +}); + +check('a change to the document text changes the key', () => { + assert.notStrictEqual(keyA('S1', aText + ' '), keyA()); +}); + +check('a change to the server build changes the key', () => { + assert.notStrictEqual(keyA('S2'), keyA('S1')); +}); + +check('a change to a file the document includes, two steps down, changes the key', () => { + const before = keyA(); + fs.writeFileSync(C, 'fn helper() { return 3; }\n'); + const after = keyA(); + assert.notStrictEqual(before, after); + fs.writeFileSync(C, 'fn helper() { return 1; }\n'); + assert.strictEqual(keyA(), before, 'putting it back gives the old key again'); +}); + +check('a file nothing includes does not change the key', () => { + const before = keyA(); + fs.writeFileSync(U, 'fn other() { return 9; }\n'); + assert.strictEqual(keyA(), before); +}); + +check('creating a missing include changes the key', () => { + const M = write('m.zc', 'include "later.zc";\nfn f() { return 0; }\n'); + const mText = fs.readFileSync(M, 'utf8'); + const before = dc.diagKey('S1', mText, M, root); + assert.ok(before.deps.some(p => p.endsWith('later.zc')), 'the missing name is still a dependency'); + write('later.zc', 'fn g() { return 1; }\n'); + assert.notStrictEqual(dc.diagKey('S1', mText, M, root).key, before.key); +}); + +check('an include is found from the workspace folder as well as the including file', () => { + const X = write('sub/x.zc', 'include "b.zc";\nfn x() { return 0; }\n'); + const tree = dc.sourceTree(fs.readFileSync(X, 'utf8'), X, root); + assert.ok(tree.files.has(path.join(root, 'b.zc')), 'b.zc is read from the workspace root'); + assert.ok(tree.files.has(path.join(root, 'sub', 'b.zc')), 'and the directory of x.zc is tried too'); + assert.ok(tree.complete); +}); + +check('a tree too large to cover has no key, so nothing is cached for it', () => { + let many = ''; + for (let i = 0; i < dc.MAX_FILES + 5; i++) many += 'include "nope' + i + '.zc";\n'; + const big = write('big.zc', many); + const r = dc.diagKey('S1', many, big, root); + assert.strictEqual(r.key, null); + const cache = new dc.DiagCache(null); + assert.strictEqual(cache.put(r.key, []), false); + assert.strictEqual(cache.size(), 0); +}); + +check('the cache answers only for the key it was given', () => { + const cache = new dc.DiagCache(null); + const d = [{ message: 'x' }]; + assert.ok(cache.put('k1', d)); + assert.deepStrictEqual(cache.get('k1'), d); + assert.strictEqual(cache.get('k2'), null); + assert.strictEqual(cache.get(null), null); + assert.strictEqual(cache.put(null, []), false, 'a tree without a key is not kept'); + assert.strictEqual(cache.size(), 1); +}); + +check('two states of one document are both kept, so going back is a hit', () => { + const cache = new dc.DiagCache(null); + cache.put('state-a', []); + cache.put('state-b', [{ message: 'b' }]); + assert.deepStrictEqual(cache.get('state-a'), []); + assert.deepStrictEqual(cache.get('state-b'), [{ message: 'b' }]); +}); + +check('the least recently used entries go first past the limit', () => { + const cache = new dc.DiagCache(null, 2); + cache.put('1', []); + cache.put('2', []); + cache.get('1'); + cache.put('3', []); + assert.strictEqual(cache.size(), 2); + assert.strictEqual(cache.get('2'), null, 'two was the least recently used'); + assert.deepStrictEqual(cache.get('1'), []); + assert.deepStrictEqual(cache.get('3'), []); +}); + +check('the cache carries over a save and load as plain JSON', () => { + const cache = new dc.DiagCache(null); + cache.put('k1', [{ message: 'z', range: { start: { line: 1, character: 2 } } }]); + const back = new dc.DiagCache(JSON.parse(JSON.stringify(cache))); + assert.deepStrictEqual(back.get('k1'), [{ message: 'z', range: { start: { line: 1, character: 2 } } }]); +}); + +check('a saved entry that is not well formed is dropped, not trusted', () => { + const back = new dc.DiagCache({ bad: { key: 'bad' }, good: [] }); + assert.strictEqual(back.get('bad'), null); + assert.deepStrictEqual(back.get('good'), []); +}); + +fs.rmSync(root, { recursive: true, force: true }); +console.log(passed + ' passed, ' + failed + ' failed'); +process.exit(failed ? 1 : 0); diff --git a/tests/drivers/diagnostic_revision_test.js b/tests/drivers/diagnostic_revision_test.js new file mode 100644 index 0000000..725fb87 --- /dev/null +++ b/tests/drivers/diagnostic_revision_test.js @@ -0,0 +1,47 @@ +// exercise delayed diagnostics without relying on an editor's timing. +const assert = require('assert'); +const fs = require('fs'); +const path = require('path'); +const vm = require('vm'); +const root = path.resolve(__dirname, '../..'); +const displayed = [], cached = []; +const vscode = { + EventEmitter: class { fire() {} }, + SemanticTokensLegend: class {}, + Diagnostic: class { constructor(range, message) { this.message = message; } }, + Range: class {}, + DiagnosticSeverity: { Hint: 4, Information: 3, Warning: 2, Error: 1 }, + Uri: { parse: value => value }, +}; +const filename = path.join(root, 'editors/vscode/client/extension.js'); +const context = vm.createContext({ + module: { exports: {} }, __dirname: path.dirname(filename), Buffer, setTimeout, clearTimeout, + require(name) { + if (name === 'vscode') return vscode; + if (name === './vmproc' || name === './wasmproc') return {}; + if (name.startsWith('./')) return require(path.join(path.dirname(filename), name)); + return require(name); + }, + displayed, cached, +}); +vm.runInContext(fs.readFileSync(filename, 'utf8') + ` +diagnostics = { set(uri, list) { displayed.push(list.map(d => d.message)); } }; +cache = { put(key, raw) { cached.push({ key, raw }); return false; } }; +const uri = 'file:///reopened.zc'; +const diag = message => [{ message, range: { start: { line: 0, character: 0 }, end: { line: 0, character: 1 } } }]; +opened.add(uri); docKeys.set(uri, 'current'); docVersions.set(uri, 3); +sentKeys.set(uri, [{ key: 'current', version: 3 }]); +onPublish(uri, diag('old close'), 1); +onPublish(uri, diag('old open'), 2); +onPublish(uri, diag('current open'), 3); +onPublish(uri, diag('duplicate'), 3); +opened.delete(uri); +onPublish(uri, diag('closed'), 3); +opened.add(uri); docKeys.set(uri, null); docVersions.set(uri, 5); +sentKeys.set(uri, [{ key: null, version: 4 }, { key: null, version: 5 }]); +onPublish(uri, diag('old uncacheable text'), 4); +onPublish(uri, diag('current uncacheable text'), 5); +`, context, { filename }); +assert.deepStrictEqual(displayed.map(list => Array.from(list)), [['current open'], ['current uncacheable text']]); +assert.deepStrictEqual(cached.map(entry => entry.key), ['current']); +console.log('ok closed, reopened, duplicate, and uncacheable diagnostic revisions'); diff --git a/tests/drivers/enhancement_backends_test.py b/tests/drivers/enhancement_backends_test.py new file mode 100644 index 0000000..b7d553b --- /dev/null +++ b/tests/drivers/enhancement_backends_test.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +# recipes, vm lowering, fallback, and ignored optimizer regions agree across hosts. +import pathlib +import subprocess + +root = pathlib.Path(__file__).resolve().parents[2] +source = (root / 'tests/programs/enhance.zc').read_text() +# keep this focused check short while still exercising signed and unsigned arithmetic. +source = source.replace('i < 5000', 'i < 200') +source += '\n' +reference = None +for command in [['bin/zy'], ['bin/zym'], ['node', 'src/js/tinyvm.js']]: + result = subprocess.run(command, input=source, text=True, capture_output=True, timeout=25) + assert result.returncode == 0 and result.stdout and 'zc:' not in result.stdout, (command, result.returncode, result.stdout, result.stderr) + if reference is None: + reference = result.stdout + else: + assert result.stdout == reference, (command, reference, result.stdout) + print('ok vm recipes, multiple outputs, arithmetic, and fallback: ' + command[0]) +source = '''o3 int increment(int value) { return value + 1; } +fn main() { put_n(increment(6)); put_c(32); put_n(17 / 5); put_c(32); put_n(17 % 5); put_c(10); return 0; } +''' +for command in [['bin/zy'], ['bin/zym'], ['node', 'src/js/tinyvm.js']]: + result = subprocess.run(command, input=source, text=True, capture_output=True, timeout=15) + assert result.returncode == 0 and result.stdout == '7 3 2\n', (command, result.stdout, result.stderr) + print('ok optimizer descriptors preserve portable division slots: ' + command[0]) + +# slot zero can be replaced, and an unsupported replacement preserves it. +# real install descriptors are exercised without calling a compiler-specific parser. +source = '''let descriptor[7], names[2]; +fn main() { + names[0] = "a"; names[1] = "b"; + descriptor[0] = 0; descriptor[1] = 0; descriptor[2] = 2; descriptor[3] = 1; + descriptor[4] = "a = add a b"; descriptor[6] = names; + int installed = install(descriptor) == 0, first = 17 / 5; + descriptor[4] = "a = unknown a b"; + int rejected = install(descriptor) < 0, second = 17 / 5; + descriptor[4] = "a = sdiv a b"; install(descriptor); + put_n(installed); put_c(32); put_n(first); put_c(32); put_n(rejected); put_c(32); put_n(second); put_c(10); return 0; +} +''' +for command in [['bin/zy'], ['bin/zym'], ['node', 'src/js/tinyvm.js']]: + result = subprocess.run(command, input=source, text=True, capture_output=True, timeout=15) + assert result.returncode == 0 and result.stdout == '1 22 1 22\n', (command, result.stdout, result.stderr) + print('ok replacing a built-in slot and rejecting a bad recipe: ' + command[0]) diff --git a/tests/drivers/ext_cache_test.js b/tests/drivers/ext_cache_test.js new file mode 100644 index 0000000..11bf50b --- /dev/null +++ b/tests/drivers/ext_cache_test.js @@ -0,0 +1,154 @@ +// the extension's diagnostics cache, end to end: the real client +// (editors/vscode/client/extension.js) with a stub for the vscode module, and +// the built-in wasm server. the checks: a first session asks the server and +// saves the answer; a second session shows the saved answer before the server +// starts; a change to an included file is checked again; putting it back is +// answered from the cache. +// +// node tests/drivers/ext_cache_test.js + +const fs = require('fs'); +const os = require('os'); +const path = require('path'); +const Module = require('module'); + +const client = path.join(__dirname, '..', '..', 'editors', 'vscode', 'client'); + +// the vscode module, as far as the client uses it +const world = { lines: [], sets: [], listeners: {}, watchers: [], textDocuments: [], saved: undefined, root: '' }; +const stub = { + EventEmitter: class { constructor() { this.event = () => ({ dispose() {} }); } fire() {} }, + Diagnostic: class { constructor(range, message, severity) { Object.assign(this, { range, message, severity }); } }, + Range: class { constructor(a, b, c, d) { Object.assign(this, { start: { line: a, character: b }, end: { line: c, character: d } }); } }, + DiagnosticSeverity: { Error: 0, Warning: 1, Information: 2, Hint: 3 }, + SemanticTokensLegend: class { constructor(t, m) { this.tokenTypes = t; this.tokenModifiers = m; } }, + SemanticTokensBuilder: class { constructor() {} push() {} build() { return null; } }, + MarkdownString: class { + constructor(v = '') { this.value = v; this.escaped = false; } + appendText(v) { this.value += v; this.escaped = true; return this; } + appendMarkdown(v) { this.value += v; return this; } + appendCodeblock(v, language) { this.value += '\n```' + language + '\n' + v + '\n```\n'; return this; } + }, + Hover: class { constructor(c) { this.contents = c; } }, + Uri: { parse: s => ({ toString: () => s, fsPath: decodeURIComponent(s.replace(/^file:\/\//, '')) }) }, + workspace: { + workspaceFolders: undefined, + textDocuments: world.textDocuments, + getConfiguration: () => ({ get: () => undefined }), + createFileSystemWatcher: () => { + const w = { handlers: {}, dispose() {} }; + world.watchers.push(w); + for (const k of ['onDidChange', 'onDidCreate', 'onDidDelete']) w[k] = f => { w.handlers[k] = f; return { dispose() {} }; }; + return w; + }, + ...['onDidChangeConfiguration', 'onDidOpenTextDocument', 'onDidChangeTextDocument', 'onDidSaveTextDocument', 'onDidCloseTextDocument'] + .reduce((o, k) => { o[k] = f => { world.listeners[k] = f; return { dispose() {} }; }; return o; }, {}), + }, + window: { + createOutputChannel: () => ({ appendLine: s => world.lines.push(String(s)), show() {}, dispose() {} }), + showWarningMessage() {}, showErrorMessage() {}, + }, + languages: { + createDiagnosticCollection: () => ({ + set: (uri, list) => world.sets.push({ uri: uri.toString(), list, at: world.lines.length }), + delete() {}, dispose() {}, + }), + registerDocumentSemanticTokensProvider: () => ({ dispose() {} }), + registerHoverProvider: (sel, p) => { world.hover = p; return { dispose() {} }; }, + }, + commands: { registerCommand: () => ({ dispose() {} }) }, +}; +const realResolve = Module._resolveFilename; +Module._resolveFilename = function (req, ...rest) { return req === 'vscode' ? 'vscode' : realResolve.call(this, req, ...rest); }; +require.cache['vscode'] = { id: 'vscode', filename: 'vscode', loaded: true, exports: stub }; + +let passed = 0, failed = 0; +function check(name, ok, detail) { + if (ok) { passed++; console.log('ok ' + name); } + else { failed++; console.log('FAIL ' + name + (detail ? ': ' + detail : '')); } +} +const sleep = ms => new Promise(r => setTimeout(r, ms)); +async function until(pred, ms = 20000) { + const end = Date.now() + ms; + while (Date.now() < end) { if (pred()) return true; await sleep(50); } + return pred(); +} + +function freshClient() { + for (const f of Object.keys(require.cache)) if (f.startsWith(client)) delete require.cache[f]; + return require(path.join(client, 'extension.js')); +} + +const root = fs.mkdtempSync(path.join(os.tmpdir(), 'zc-extcache-')); +stub.workspace.workspaceFolders = [{ uri: { fsPath: root } }]; +const helper = path.join(root, 'helper.zc'); +const main = path.join(root, 'main.zc'); +const helperOk = 'fn helper() { return 1; }\n'; +const helperBad = 'fn helper2() { return 1; }\n'; +fs.writeFileSync(helper, helperOk); +const mainText = 'include "helper.zc";\nfn main() { return helper(); }\n'; +fs.writeFileSync(main, mainText); + +function doc(p, text) { + const d = { uri: { toString: () => 'file://' + p, fsPath: p }, languageId: 'zc', version: 1, text, getText() { return this.text; } }; + return d; +} + +const ctx = () => ({ + subscriptions: [], + globalState: { get: () => world.saved, update: (k, v) => { world.saved = JSON.parse(JSON.stringify(v)); return Promise.resolve(); } }, +}); +const mainDoc = doc(main, mainText); +const mainUri = 'file://' + main; + +(async () => { + // session one: nothing cached, so the server is asked + world.textDocuments.push(mainDoc); + let ext = freshClient(); + let c1 = ctx(); + ext.activate(c1); + check('session one: the server starts and checks the document', await until(() => world.lines.some(l => /server ready/.test(l))) && await until(() => world.sets.some(s => s.uri === mainUri))); + const first = world.sets.filter(s => s.uri === mainUri).pop(); + check('session one: the server answered with a list', first && Array.isArray(first.list), JSON.stringify(first && first.list)); + await sleep(800); + const savedKeys = Object.keys(world.saved || {}); + // the hover is the server's markdown, parsed: a zc code block, not escaped text + const hv = world.hover && await world.hover.provideHover(mainDoc, { line: 1, character: 4 }); + const hc = hv && hv.contents[0]; + check('the hover is markdown with a zc code block, parsed and not escaped', hc && !hc.escaped && /^```zc\n/.test(hc.value) && /\n```/.test(hc.value), JSON.stringify(hc)); + check('session one: the answer is saved under a key', savedKeys.length === 1 && /^[0-9a-f]{64}$/.test(savedKeys[0]), JSON.stringify(savedKeys)); + ext.deactivate(); + await sleep(700); + + // session two: the saved answer is shown before the server has started + world.lines.length = 0; world.sets.length = 0; + ext = freshClient(); + ext.activate(ctx()); + check('session two: the saved diagnostics are shown at once', world.sets.some(s => s.uri === mainUri) && world.lines.some(l => /from the cache/.test(l))); + check('session two: the server has not started yet', !world.lines.some(l => /server ready/.test(l))); + await until(() => world.lines.some(l => /server ready/.test(l))); + await sleep(300); + check('session two: the server was not asked about the document', !world.lines.some(l => /-> textDocument\/didOpen|-> textDocument\/didChange/.test(l)), JSON.stringify(world.lines.slice(0, 6))); + check('session two: the cache holds the same key as before', JSON.stringify(Object.keys(world.saved || {})) === JSON.stringify(savedKeys)); + + // a change to the included file, saved: the document is checked again and the error shows + world.lines.length = 0; world.sets.length = 0; + fs.writeFileSync(helper, helperBad); + for (const h of world.watchers) if (h.handlers.onDidChange) h.handlers.onDidChange({ fsPath: helper }); + check('an included file changed: the document is checked again', await until(() => world.lines.some(l => /-> textDocument\/didOpen|-> textDocument\/didChange/.test(l)) && world.sets.some(s => s.uri === mainUri)), JSON.stringify(world.lines.slice(-4))); + await sleep(900); + check('an included file changed: the new state is saved under its own key', Object.keys(world.saved || {}).length === 2, JSON.stringify(Object.keys(world.saved || {}))); + + // the file goes back: its tree is the one the cache has, so the old answer is shown from the cache + world.lines.length = 0; world.sets.length = 0; + fs.writeFileSync(helper, helperOk); + for (const h of world.watchers) if (h.handlers.onDidChange) h.handlers.onDidChange({ fsPath: helper }); + await sleep(200); + check('the old tree is answered from the cache, without asking the server', world.lines.some(l => /from the cache/.test(l)) && !world.lines.some(l => /-> textDocument\/didChange/.test(l)), JSON.stringify(world.lines.slice(-4))); + + ext.deactivate(); + await sleep(700); + fs.rmSync(root, { recursive: true, force: true }); + console.log(passed + ' passed, ' + failed + ' failed'); + process.exit(failed ? 1 : 0); +})().catch(e => { console.log('FAIL harness: ' + (e.stack || e)); process.exit(1); }); diff --git a/tests/drivers/float_backends_test.py b/tests/drivers/float_backends_test.py new file mode 100644 index 0000000..2cb9473 --- /dev/null +++ b/tests/drivers/float_backends_test.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 +# compare float instructions, packed binary32 conversion, and decimal output. +import pathlib +import subprocess + +root = pathlib.Path(__file__).resolve().parents[2] +commands = [['bin/zy'], ['bin/zym'], ['node', 'src/js/tinyvm.js']] +sources = [(root / 'tests/programs/float.zc').read_text(), '''fn render(bits, precision, mode) { asm { bits @ precision @ mode @ #1236 } return 0; } +fn convert(bits) { return asm { bits @ #1218 }; } +fn main() { + [4]f32 values; + values[0] = 1.5; values[1] = -2.25; values[2] = 1e-45; values[3] = 1e40; + put_n(values[0] == 1.5); put_c(32); put_n(values[1] == -2.25); put_c(32); + put_n(values[2] > 0.0); put_c(32); put_n(values[3] == 1.0 / 0.0); put_c(10); + render(0.125, 2, 1); put_c(32); render(-0.125, 2, 1); put_c(32); + render(1e30, 6, 1); put_c(32); render(1.7976931348623157e308, 6, 1); put_c(10); + render(0.1, 0, 0); put_c(32); render(1e-5, 0, 0); put_c(32); render(1e16, 0, 0); put_c(10); + render(9.999, 3, 2); put_c(32); render(5e-324, 3, 2); put_c(32); render(0, 3, 2); put_c(10); + let negative_zero = 1 << 63; render(negative_zero, 6, 1); put_c(32); render(negative_zero, 0, 0); put_c(10); + put_n(convert(0.0 / 0.0)); put_c(32); put_n(convert(1.0 / 0.0)); put_c(32); put_n(convert(-1.0 / 0.0)); put_c(10); + return 0; +} +'''] +expected_arithmetic = '4.000000 4.500000 3.000000 2.500000 2.500000 0.001500 0.100000 -0.125000 inf -inf nan 1 1 1 0 0 3 -3 3.000000 -2.000000 2\n' +for i, source in enumerate(sources): + reference = None + for command in commands: + result = subprocess.run(command, input=source, text=True, capture_output=True, timeout=30) + assert result.returncode == 0 and result.stdout and 'zc:' not in result.stdout, (command, result.returncode, result.stdout, result.stderr) + if reference is None: + if i == 0: + assert result.stdout == expected_arithmetic, result.stdout + else: + assert result.stdout.startswith('1 1 1 1\n0.13 -0.13 '), result.stdout + assert result.stdout.endswith('0 9223372036854775807 -9223372036854775808\n'), result.stdout + reference = result.stdout + else: + assert result.stdout == reference, (command, i, reference, result.stdout) + print('ok float arithmetic and output fixture ' + str(i + 1) + ': ' + command[0]) + +# an exponent after a long fractional spelling must reach the parser intact. +literal = '1.' + '0' * 300 + 'e1' +source = f'fn main() {{ put_n(({literal} as word) == 0x4024000000000000); put_c(10); return 0; }}\n' +for command in commands: + result = subprocess.run(command, input=source, text=True, capture_output=True, timeout=15) + assert result.returncode == 0 and result.stdout == '1\n', (command, result.stdout, result.stderr) + print('ok float parsing reads the exponent beyond 127 characters: ' + command[0]) diff --git a/tests/drivers/frame_growth_test.py b/tests/drivers/frame_growth_test.py new file mode 100644 index 0000000..74156e3 --- /dev/null +++ b/tests/drivers/frame_growth_test.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python3 +# growing frames keep pointers into older segments alive through collection. +import subprocess +import sys + +command = sys.argv[1:] +source = '''unchecked int descend(int depth, int* root) { + [2000]int padding; + padding[0] = depth; + if (depth == 0) { gc(); return *root; } + return descend(depth - 1, root) + padding[0]; +} +unchecked int oversized(int* root) { + [300000]int values; + values[0] = *root; values[299999] = 9; + gc(); return values[0] + values[299999]; +} +unchecked fn main() { + int value = 7; + put_n(descend(300, &value)); put_c(32); + put_n(oversized(&value)); put_c(32); + gc(); put_n(frame_node == 0); put_c(10); + return 0; +} +''' +result = subprocess.run(command, input=source, text=True, capture_output=True, timeout=40) +assert result.returncode == 0 and result.stdout == '45157 16 1\n', (command, result.returncode, result.stdout, result.stderr) +print('ok deep and oversized frames preserve borrowed locals through collection: ' + command[0]) +optimized = source.replace('unchecked int descend', 'unchecked o3 int descend').replace('unchecked int oversized', 'unchecked o3 int oversized') +result = subprocess.run(command, input=optimized, text=True, capture_output=True, timeout=40) +assert result.returncode == 0 and result.stdout == '45157 16 1\n', (command, result.returncode, result.stdout, result.stderr) +print('ok optimized recursive calls wait for the matching return depth: ' + command[0]) diff --git a/tests/drivers/genome_blob_test.py b/tests/drivers/genome_blob_test.py new file mode 100644 index 0000000..81533a7 --- /dev/null +++ b/tests/drivers/genome_blob_test.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python3 +# documentation precedes the symbol table; the boot header must skip those bytes. +import pathlib +import re + +source = pathlib.Path('src/core/genome.h').read_text() +org = int(re.search(r'#define GORG (\d+)', source)[1]) +length = int(re.search(r'#define GLEN (\d+)', source)[1]) +words = [int(word.strip()) for word in re.search(r'genome\[\]\s*=\s*\{([\s\S]*?)\};', source)[1].split(',') if word.strip()] +assert len(words) == length and words[6] == org + length +cursor = words[5] - org +assert 7 <= cursor < length +records = {} +while words[cursor]: + kind, value, signature = words[cursor:cursor + 3] + assert kind in (1, 2, 3, 4, 6, 8), (cursor, kind) + cursor += 3 + name = '' + while words[cursor]: + assert 0 < words[cursor] < 128, (cursor, words[cursor]) + name += chr(words[cursor]) + cursor += 1 + cursor += 1 + records[name] = (kind, value, signature) +assert cursor == length - 1 +assert len(records) > 1000 and records['start'][0] == 1 +assert records['reserve_code'][0] == 1 and records['build_image_grow'][0] == 1 +assert any(name.startswith('doc ') for name in records), 'the fixture contains documentation bytes' +for name, (kind, value, _) in records.items(): + if kind == 8: + assert org <= value < org + length and words[value - org] >= 0, name +print('ok documented genome starts its symbol table at the header address') diff --git a/tests/drivers/hover_features_test.js b/tests/drivers/hover_features_test.js new file mode 100644 index 0000000..895e923 --- /dev/null +++ b/tests/drivers/hover_features_test.js @@ -0,0 +1,85 @@ +// focused type, inference, and block-documentation hover regressions. +// usage: node tests/drivers/hover_features_test.js path/to/zclsp +const assert = require('assert'); +const cp = require('child_process'); +const server = process.argv[2]; +const uri = 'file:///hover-features.zc'; +const frame = m => { + const body = Buffer.from(JSON.stringify(m)); + return Buffer.concat([Buffer.from('Content-Length: ' + body.length + '\r\n\r\n'), body]); +}; +function hover(text, needle, nth = 0) { + let offset = -1; + for (let i = 0; i <= nth; i++) offset = text.indexOf(needle, offset + 1); + assert.ok(offset >= 0, 'fixture contains ' + needle); + const before = text.slice(0, offset); + const line = before.split('\n').length - 1; + const character = before.length - before.lastIndexOf('\n') - 1; + const messages = [ + { jsonrpc: '2.0', id: 1, method: 'initialize', params: {} }, + { jsonrpc: '2.0', method: 'textDocument/didOpen', params: { textDocument: { uri, version: 1, languageId: 'zc', text } } }, + { jsonrpc: '2.0', id: 2, method: 'textDocument/hover', params: { textDocument: { uri }, position: { line, character } } }, + { jsonrpc: '2.0', method: 'exit' }, + ]; + const result = cp.spawnSync(server, process.argv.slice(3), { input: Buffer.concat(messages.map(frame)), timeout: 10000, maxBuffer: 4 << 20 }); + assert.strictEqual(result.status, 0, String(result.stderr)); + let data = result.stdout; + while (data.length) { + const end = data.indexOf('\r\n\r\n'); + const count = Number(/Content-Length:\s*(\d+)/i.exec(data.subarray(0, end).toString())[1]); + const msg = JSON.parse(data.subarray(end + 4, end + 4 + count).toString()); + data = data.subarray(end + 4 + count); + if (msg.id === 2) return msg.result && msg.result.contents.value; + } + return null; +} +for (const type of ['word', 'int', 'char', 'bool', 'float', 'f64', 'f32', 'byte', 'u8', 'i8', 'u16', 'i16', 'u32', 'i32']) { + const value = hover(type + ' global;\nfn main() { return 0; }\n', type); + assert.ok(value && value.startsWith('```zc\n') && value.includes('\n\n'), type + ' has formatted built-in documentation'); +} +assert.match(hover('void noop() {}\nfn main() { return 0; }\n', 'void'), /no meaningful result/); +const array = 'fn main() {\n [4]u8 values;\n return values[0];\n}\n'; +assert.match(hover(array, '[4]'), /fixed length of 4/); +assert.match(hover(array, 'values', 1), /type \[4\]u8/); +const struct = 'struct pair { int left; float right; };\nfn main() { pair value; return value.left; }\n'; +assert.match(hover(struct, 'pair', 1), /int left;[\s\S]*float right;/); +assert.match(hover(struct, 'value', 1), /type pair/); +const fields = 'struct pair { int /* left field */ left, /* right field */ right; };\nfn main() { pair p; return p.left + p.right; }\n'; +assert.match(hover(fields, 'left +'), /field; type int[\s\S]*left field/); +assert.match(hover(fields, 'right;'), /field; type int[\s\S]*right field/); +const empty = '/**/ int helper() { return 1; }\nfn main() { return helper(); }\n'; +assert.match(hover(empty, 'helper();'), /int helper\(\)/); +const variableDocs = 'fn main() {\n int /* first variable */ first = 1, /* second variable */ second = 2;\n return first + second;\n}\n'; +const first = hover(variableDocs, 'first +'); +const second = hover(variableDocs, 'second;'); +assert.ok(first.includes('first variable') && !first.includes('second variable'), first); +assert.ok(second.includes('second variable') && !second.includes('first variable'), second); +const paramDocs = 'int add(int /* left argument */ a, int /* right argument */ b) { return a + b; }\nfn main() { return add(1, 2); }\n'; +const a = hover(paramDocs, 'a +'); +const b = hover(paramDocs, 'b;'); +assert.ok(a.includes('left argument') && !a.includes('right argument'), a); +assert.ok(b.includes('right argument') && !b.includes('left argument'), b); +const functionDocs = '/**\n * adds **one** to its argument.\n */\nint add(int x) { return x + 1; }\nfn main() { return add(2); }\n'; +assert.match(hover(functionDocs, 'add(2)'), /adds \*\*one\*\*/); +const inferred = 'fn twice(x) { return x * 2; }\nfn main() { let value = twice(3); return value; }\n'; +assert.match(hover(inferred, 'twice(3)'), /int twice\(int x\)/); +assert.match(hover(inferred, 'x *'), /type int/); +assert.match(hover(inferred, 'value;'), /type int/); +const pointer = 'int read(int* p) { return p[0]; }\nfn main() { int value = 3; return read(&value); }\n'; +assert.match(hover(pointer, 'p[0]'), /type int\*[\s\S]*pointer to `int`/); +const alias = 'type row = [3]int;\nfn main() { row values; return values[0]; }\n'; +assert.match(hover(alias, 'row values'), /\[3\]int[\s\S]*fixed length of 3/); +const enumeration = 'enum color { red, green = 5, blue };\nfn main() { color shade = green; return shade; }\n'; +assert.match(hover(enumeration, 'shade;'), /type color/); +const dynamic = 'fn main() { []int items; push(items, 3); return len(items); }\n'; +assert.match(hover(dynamic, 'items);'), /type \[\]int[\s\S]*growable array handle/); +const callable = 'fn main() { let apply = fn(int x) { return x + 1; }; return apply(2); }\n'; +assert.match(hover(callable, 'apply(2)'), /type .*\([\s\S]*callable value/); +assert.match(hover(callable, 'apply(2)'), /type int\(int\)/); +const floatClosure = 'fn main() { let f = fn(float x) { return x + 1.5; }; let result = f(2.0); return result; }\n'; +assert.match(hover(floatClosure, 'result;'), /type float/); +const inferredPointer = 'struct node { int value; };\nfn make_node() { return new(node); }\nfn main() { let p = make_node(); return p.value; }\n'; +assert.match(hover(inferredPointer, 'p.value'), /type node\*/); +const aggregateAlias = 'fn main() { [3]int a; a[1] = 7; let alias = a; return alias[1]; }\n'; +assert.match(hover(aggregateAlias, 'alias[1]'), /type int\*/); +console.log('ok built-in and composite types, inferred signatures, and separate block docs'); diff --git a/tests/drivers/hover_test.js b/tests/drivers/hover_test.js new file mode 100644 index 0000000..10af643 --- /dev/null +++ b/tests/drivers/hover_test.js @@ -0,0 +1,23 @@ +// protocol conversion checks. run with node tests/drivers/hover_test.js. +const assert = require('assert'); +const { hoverContents } = require('../../editors/vscode/client/hover'); +class MarkdownString { + constructor() { this.value = ''; this.calls = []; } + appendMarkdown(value) { this.calls.push('markdown'); this.value += value; return this; } + appendText(value) { this.calls.push('text'); this.value += value; return this; } + appendCodeblock(value, language) { this.calls.push('code'); this.value += '\n```' + language + '\n' + value + '\n```\n'; return this; } +} +const api = { MarkdownString }; +const markdown = '```zc\nint add(int a, int b)\n```\n\nAdds **two** numbers.'; +const md = hoverContents(api, { kind: 'markdown', value: markdown })[0]; +assert.strictEqual(md.value, markdown); +assert.deepStrictEqual(md.calls, ['markdown']); +assert.strictEqual(md.isTrusted, false); +assert.strictEqual(md.supportHtml, false); +assert.deepStrictEqual(hoverContents(api, { kind: 'plaintext', value: '*literal*' })[0].calls, ['text']); +const legacy = hoverContents(api, { language: 'zc', value: 'int x;' })[0]; +assert.match(legacy.value, /```zc\nint x;\n```/); +assert.deepStrictEqual(legacy.calls, ['code']); +assert.strictEqual(hoverContents(api, ['**Doc**', { language: 'zc', value: 'fn main()' }]).length, 2); +for (const contents of [null, undefined, '', [], {}, { value: 123 }]) assert.deepStrictEqual(hoverContents(api, contents), []); +console.log('ok hover conversion preserves Markdown, plaintext, code, and arrays'); diff --git a/tests/drivers/image_growth_test.py b/tests/drivers/image_growth_test.py new file mode 100644 index 0000000..064eae7 --- /dev/null +++ b/tests/drivers/image_growth_test.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 +# image output can grow past its first buffer and the building host's genome slot. +import pathlib +import subprocess +import sys + +root = pathlib.Path(__file__).resolve().parents[2] +source = '''fn main() { + int retained = 7; + int n = build_image_grow(GORG, 8); + gc(); + put_n(n > FBOT - GORG); put_c(32); + put_n(image_size == n && image_words[6] == GORG + n); put_c(32); + put_n(image_words[5] > GORG && image_words[5] < image_words[6]); put_c(32); + put_n(retained); put_c(10); + return 0; +} +`''' +source += (root / 'src/runtime/rt.zc').read_text() +source += (root / 'src/runtime/run.zc').read_text() +source += 'let padding[350000]; fn main() { return padding[349999]; }\n' +result = subprocess.run(sys.argv[1:], input=source, capture_output=True, text=True, timeout=40) +assert result.returncode == 0, (result.stdout, result.stderr) +assert result.stdout == '1 1 1 7\n', result.stdout[:1000] +print('ok growing image output keeps logical addresses and older frames intact') diff --git a/tests/drivers/image_run.js b/tests/drivers/image_run.js new file mode 100644 index 0000000..6d0b949 --- /dev/null +++ b/tests/drivers/image_run.js @@ -0,0 +1,14 @@ +// run a generated image directly, avoiding compiler time in vm execution tests. +const fs = require('fs'); +const { TinyVM } = require('../../src/js/tinyvm.js'); +const { mapValues } = require('../../src/js/mapvalues.js'); +const header = fs.readFileSync(process.argv[2], 'utf8'); +const org = Number(header.match(/#define\s+GORG\s+(\d+)/)[1]); +const words = header.match(/genome\[\]\s*=\s*\{([\s\S]*?)\};/)[1].split(',').map(s => s.trim()).filter(Boolean); +const layout = mapValues(process.argv[3], ['GORG', 'DS0', 'DSLIM', 'RS0', 'RSLIM', 'SYSGATE', 'ZFP', 'HEAP_LO', 'HEAP_HI']); +const machine = new TinyVM({ layout, onMessage: message => process.stderr.write(message) }); +machine.loadGenome(words, org); +if (process.argv[4] === '--stdin') machine.setInput(fs.readFileSync(0)); +const status = machine.run(); +process.stdout.write(machine.getOutput()); +process.exit(status); diff --git a/tests/drivers/inference_growth_test.py b/tests/drivers/inference_growth_test.py new file mode 100644 index 0000000..d747f70 --- /dev/null +++ b/tests/drivers/inference_growth_test.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 +# infer past the original function-record and edit-count limits. +import re +import subprocess +import sys + +source = ''.join(f'fn function_{i}(x) {{ return x + 1; }}\n' for i in range(600)) +source += 'fn main() {\n' +source += ''.join(f'let value_{i} = {i};\n' for i in range(5000)) +source += ''.join(f'function_{i}(1);\n' for i in range(600)) +source += 'return value_4999; }\n' +result = subprocess.run([sys.argv[1], '--annotate'], input=source, text=True, capture_output=True, timeout=45) +assert result.returncode == 0, result.stderr +assert re.search(r'int function_599\(int x\)', result.stdout), result.stdout[-1000:] +assert 'int value_4999 = 4999;' in result.stdout, result.stdout[-1000:] +assert 'let value_' not in result.stdout, 'later inference edits were dropped' +print('ok inference handles 600 functions and 5000 local declarations') + +# later parameters and nested functions retain the enclosing inference context. +params = ', '.join(f'arg_{i}' for i in range(20)) +source = f'fn wide({params}) {{ let nested = fn(int y) {{ return y + 1; }}; return arg_19 + nested(1); }}\n' +source += 'fn main() { return wide(' + ', '.join('1' for _ in range(20)) + '); }\n' +result = subprocess.run([sys.argv[1], '--annotate'], input=source, text=True, capture_output=True, timeout=15) +assert result.returncode == 0, result.stderr +assert 'int arg_19' in result.stdout, result.stdout +assert 'int wide(' in result.stdout, result.stdout +print('ok later arguments and nested closures preserve inferred signatures') + +source = 'struct point { int x, y; };\nfn main() { let a[3]; a[1] = 7; let alias = a; point p; p.x = 9; let ref = p; return alias[1] + ref.x; }\n' +result = subprocess.run([sys.argv[1], '--annotate'], input=source, text=True, capture_output=True, timeout=15) +assert result.returncode == 0 and 'word* alias' in result.stdout and 'point* ref' in result.stdout, result.stdout +again = subprocess.run([sys.argv[1], '--annotate'], input=result.stdout, text=True, capture_output=True, timeout=15) +assert again.returncode == 0 and again.stdout == result.stdout, again.stdout +print('ok array and struct aliases get compatible pointer annotations') diff --git a/tests/drivers/layout_config_test.py b/tests/drivers/layout_config_test.py new file mode 100644 index 0000000..469e7dd --- /dev/null +++ b/tests/drivers/layout_config_test.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +# generated layouts expose initial reservations and reject arithmetic overflow. +import pathlib +import subprocess +import tempfile + +root = pathlib.Path(__file__).resolve().parents[2] +tool = root / 'bin/genmap' +result = subprocess.run([tool, '--help'], capture_output=True, text=True) +assert result.returncode == 0 and '--generated-text' in result.stderr +invalid = [ + ['--symbols', '2147483647'], ['--code', '2147483647'], + ['--frame', '2147483647'], ['--return', '2147483647'], + ['--asm-labels', '2147483647'], ['--enum-members', '2147483647'], + ['--regions', '2147483647'], ['--heap', '7'], ['--hash', '3'], +] +for options in invalid: + result = subprocess.run([tool, '--preset', 'default', *options], capture_output=True, text=True) + assert result.returncode != 0 and result.stderr, options +with tempfile.TemporaryDirectory(dir=root / 'build') as directory: + options = ['--asm-labels', '4', '--asm-refs', '8', '--exit', '32', '--enum-members', '8', '--tagged-enums', '2', '--methods', '2', '--generated-text', '64', '--facts', '2', '--paths', '2', '--regions', '2'] + subprocess.run([tool, '--preset', 'default', *options, '-o', directory], check=True, capture_output=True) + source = (pathlib.Path(directory) / 'compiler.zc').read_text() + for name, value in [('ASM_LABELS', '4'), ('ASM_REFS', '8'), ('EXIT_WORDS', '20'), ('ENUM_MEMBERS', '8'), ('TAGGED_ENUMS', '2'), ('STRUCT_METHODS', '2'), ('GENERATED_WORDS', '40'), ('RANGE_FACTS', '2'), ('JOIN_PATHS', '2'), ('O3_REGIONS', '2')]: + assert f'const {name} = 0x{value};' in source, name +print('ok parser layout controls and invalid configuration checks') diff --git a/tests/drivers/learn.py b/tests/drivers/learn.py new file mode 100644 index 0000000..42f729f --- /dev/null +++ b/tests/drivers/learn.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 +# checks docs/LEARN.md against the examples it shows. every examples/learn/*.zc runs +# from the repo root with its stdout compared to the matching .out file, and +# every ```zc block in docs/LEARN.md must be exactly one of those files, so the +# guide can't drift from the code it teaches. usage: learn.py path/to/zy +import glob, os, platform, re, subprocess, sys + +root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +os.chdir(root) +zy = os.path.abspath(sys.argv[1]) +x86_only = {"22-raw-x86"} +bad = 0 + +def ok(name): + print(f"ok {name}") + +def fail(name, why): + global bad + bad += 1 + print(f"FAIL {name}: {why}") + +files = sorted(glob.glob("examples/learn/*.zc")) +for path in files: + name = os.path.splitext(os.path.basename(path))[0] + if name in x86_only and platform.machine() not in ("x86_64", "AMD64"): + print(f"skip {name} (x86-64 only)") + continue + with open(path, "rb") as f: + src = f.read() + try: + r = subprocess.run([zy], stdin=open(path, "rb"), capture_output=True, timeout=30) + except subprocess.TimeoutExpired: + fail(f"learn {name}", "timed out") + continue + got = r.stdout.decode("utf-8", "replace") + want_path = os.path.splitext(path)[0] + ".out" + if not os.path.exists(want_path): + fail(f"learn {name}", f"no {want_path}") + elif got == open(want_path, encoding="utf-8").read(): + ok(f"learn {name} prints what it says") + else: + fail(f"learn {name}", f"got [{got}]") + +# every zc block in docs/LEARN.md is one of the example files +with open("docs/LEARN.md", encoding="utf-8") as f: + doc = f.read() +blocks = re.findall(r"^```zc\n(.*?)^```", doc, re.S | re.M) +srcs = {} +for path in files: + with open(path, encoding="utf-8") as f: + srcs[os.path.basename(path)] = f.read() +used = set() +for b in blocks: + hit = [n for n, s in srcs.items() if s == b] + if hit: + used.add(hit[0]) + else: + fail("learn block", "a ```zc block in docs/LEARN.md is not an examples/learn file: " + b.splitlines()[0]) +for n in srcs: + if n not in used: + fail(f"learn {n}", "not shown in docs/LEARN.md") +if not bad: + ok(f"docs/LEARN.md: {len(blocks)} blocks, all match examples/learn") +sys.exit(1 if bad else 0) diff --git a/tests/drivers/local_index_test.py b/tests/drivers/local_index_test.py new file mode 100644 index 0000000..95bfd78 --- /dev/null +++ b/tests/drivers/local_index_test.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 +# local name heads survive shadowing, scope reuse, closures, and context switches. +import subprocess +import sys +source = ''' +fn choose(int x) { + let total = x; + { let x = 7; total += x; } + { let x = 9; total += x; } + let f = fn(int x) { return x + 1; }; + let g = fn() { return total; }; + return total + x + f(3) + g(); +} +o3 int fast(int x) { int result = x; { int x = 8; result += x; } return result + x; } +fn main() { put_n(choose(2)); put_c(32); put_n(fast(3)); put_c(10); return 0; } +''' +result = subprocess.run(sys.argv[1:], input=source, text=True, capture_output=True, timeout=15) +assert result.returncode == 0 and result.stdout == '42 14\n', (result.returncode, result.stdout, result.stderr) +print('ok local shadow chains, reused scopes, closure parameters, captures, and o3 contexts') +source = 'struct point { int x, y; };\nfn main() { let a[3]; a[1] = 7; let alias = a; point p; p.x = 9; let ref = p; put_n(alias[1]); put_c(32); put_n(ref.x); put_c(10); return 0; }\n' +result = subprocess.run(sys.argv[1:], input=source, text=True, capture_output=True, timeout=15) +assert result.returncode == 0 and result.stdout == '7 9\n', (result.returncode, result.stdout, result.stderr) +print('ok inferred aliases load array and struct addresses rather than the alias slot') diff --git a/tests/drivers/lsp_growth_test.js b/tests/drivers/lsp_growth_test.js new file mode 100644 index 0000000..b06230a --- /dev/null +++ b/tests/drivers/lsp_growth_test.js @@ -0,0 +1,53 @@ +// focused regressions for growing document, uri, hover, and name-set storage. +// usage: node tests/drivers/lsp_growth_test.js path/to/zclsp-or-lsp.wasm +const assert = require('assert'); +const cp = require('child_process'); +const { wasmProcess } = require('../../editors/vscode/client/wasmproc'); +const target = process.argv[2]; +const frame = m => { + const body = Buffer.from(JSON.stringify(m)); + return Buffer.concat([Buffer.from('Content-Length: ' + body.length + '\r\n\r\n'), body]); +}; +function parse(input) { + const messages = []; + for (let offset = 0; offset < input.length;) { + const end = input.indexOf('\r\n\r\n', offset); + assert.ok(end >= 0, 'complete response header'); + const length = Number(/Content-Length:\s*(\d+)/i.exec(input.subarray(offset, end).toString())[1]); + const start = end + 4; + assert.ok(start + length <= input.length, 'complete response body'); + messages.push(JSON.parse(input.subarray(start, start + length).toString())); + offset = start + length; + } + return messages; +} +const uri = 'file:///' + 'long-directory/'.repeat(30) + 'main.zc'; +const docs = '// **formatted** documentation with `inline code`.\n' + ('// a long paragraph of documentation.\n').repeat(150) + '// final documentation line\n'; +const source = docs + 'int helper() { return 1; }\nfn main() { return helper(); }\n'; +const messages = [{ jsonrpc: '2.0', id: 1, method: 'initialize', params: {} }]; +for (let i = 0; i < 32; i++) messages.push({ jsonrpc: '2.0', method: 'textDocument/didOpen', params: { textDocument: { uri: i === 31 ? uri : 'file:///doc' + i + '.zc', languageId: 'zc', version: 1, text: source } } }); +messages.push({ jsonrpc: '2.0', id: 2, method: 'textDocument/hover', params: { textDocument: { uri }, position: { line: source.split('\n').length - 2, character: 19 } } }); +messages.push({ jsonrpc: '2.0', method: 'exit' }); +let output; +if (target.endsWith('.wasm')) { + const chunks = []; + const server = wasmProcess(target); + server.stdout.on('data', data => chunks.push(data)); + for (const message of messages) assert.ok(server.stdin.write(frame(message))); + output = Buffer.concat(chunks); +} else { + const result = cp.spawnSync(target, [], { input: Buffer.concat(messages.map(frame)), timeout: 30000, maxBuffer: 8 << 20 }); + assert.strictEqual(result.status, 0, String(result.stderr)); + output = result.stdout; +} +const responses = parse(output); +const published = responses.filter(m => m.method === 'textDocument/publishDiagnostics'); +assert.strictEqual(published.length, 32, 'every open document is published'); +assert.strictEqual(published[31].params.uri, uri, 'a long uri is echoed without truncation'); +assert.ok(published.every(m => !m.params.diagnostics.some(d => d.severity === 1)), 'all documents check cleanly'); +const hover = responses.find(m => m.id === 2).result.contents; +assert.strictEqual(hover.kind, 'markdown'); +assert.match(hover.value, /^```zc\nint helper\(\)\n```\n\n/); +assert.ok(hover.value.includes('**formatted**')); +assert.ok(hover.value.includes('final documentation line'), 'hover documentation grows past its initial buffer'); +console.log('ok lsp grows beyond 16 documents, 160 uri bytes, and 2300 hover characters'); diff --git a/tests/drivers/lsp_live.js b/tests/drivers/lsp_live.js new file mode 100644 index 0000000..b5fe805 --- /dev/null +++ b/tests/drivers/lsp_live.js @@ -0,0 +1,23 @@ +// the native server must answer while the client keeps the pipe open and waits, as an editor +// does: one message in, the answer out, before anything else is sent. an output buffer that +// is only flushed at exit, or an end-of-input peek that waits for the next message, fails this. +// usage: node tests/drivers/lsp_live.js path/to/zclsp +const cp = require('child_process'); +const p = cp.spawn(process.argv[2], []); +let inbox = Buffer.alloc(0), step = 0, done = false; +const frame = m => { const b = Buffer.from(JSON.stringify(m)); return Buffer.concat([Buffer.from('Content-Length: ' + b.length + '\r\n\r\n'), b]); }; +const finish = (ok, what) => { if (done) return; done = true; p.kill(); console.log((ok ? 'ok ' : 'FAIL ') + what); process.exit(ok ? 0 : 1); }; +const timer = setTimeout(() => finish(false, 'lsp live: no answer in 5 s while the pipe stayed open (step ' + step + ')'), 5000); +p.stdout.on('data', d => { + inbox = Buffer.concat([inbox, d]); + const e = inbox.indexOf('\r\n\r\n'); if (e < 0) return; + const n = parseInt(/Content-Length: (\d+)/.exec(inbox.slice(0, e).toString())[1], 10); + if (inbox.length < e + 4 + n) return; + const m = JSON.parse(inbox.slice(e + 4, e + 4 + n).toString()); + inbox = inbox.slice(e + 4 + n); + if (step === 0 && m.id === 1) { + step = 1; // the answer came with the pipe still open: ask for something else + p.stdin.write(frame({ jsonrpc: '2.0', id: 2, method: 'shutdown', params: null })); + } else if (step === 1 && m.id === 2) { clearTimeout(timer); finish(true, 'lsp live: initialize and shutdown are answered while the pipe stays open'); } +}); +p.stdin.write(frame({ jsonrpc: '2.0', id: 1, method: 'initialize', params: { processId: 1, capabilities: {} } })); diff --git a/tests/drivers/lsp_sandbox.js b/tests/drivers/lsp_sandbox.js new file mode 100644 index 0000000..14bc1e3 --- /dev/null +++ b/tests/drivers/lsp_sandbox.js @@ -0,0 +1,22 @@ +// opening a file in the editor checks it, and a check runs the file's compile-time code +// (immediate functions). the server must not let that code write files, so a file that +// tries to leaves nothing behind and the server still answers. +// usage: node tests/drivers/lsp_sandbox.js path/to/zclsp +const cp = require('child_process'), fs = require('fs'), os = require('os'), path = require('path'); +const target = path.join(os.tmpdir(), 'zc-sandbox-' + process.pid); +try { fs.unlinkSync(target); } catch (e) {} +const src = 'immediate fn evil() { let fd = syscall(1, "' + target + '", 1, 0); syscall(4, fd, 65, 1); syscall(2, fd, 0, 0); return 0; }\nevil\nfn main() { return 0; }\n'; +const frame = m => { const b = Buffer.from(JSON.stringify(m)); return Buffer.concat([Buffer.from('Content-Length: ' + b.length + '\r\n\r\n'), b]); }; +const input = Buffer.concat([ + frame({ jsonrpc: '2.0', id: 1, method: 'initialize', params: { processId: 1, capabilities: {} } }), + frame({ jsonrpc: '2.0', method: 'textDocument/didOpen', params: { textDocument: { uri: 'file:///evil.zc', languageId: 'zc', version: 1, text: src } } }), + frame({ jsonrpc: '2.0', id: 3, method: 'textDocument/semanticTokens/full', params: { textDocument: { uri: 'file:///evil.zc' } } }), + frame({ jsonrpc: '2.0', method: 'exit' }), +]); +const r = cp.spawnSync(process.argv[2], [], { input, timeout: 20000 }); +const wrote = fs.existsSync(target); +if (wrote) fs.unlinkSync(target); +const answered = r.stdout && r.stdout.includes('"id":3'); +const ok = !wrote && answered; +console.log((ok ? 'ok ' : 'FAIL ') + 'lsp sandbox: compile-time code cannot write files' + (ok ? '' : ' (wrote: ' + wrote + ', answered: ' + !!answered + ')')); +process.exit(ok ? 0 : 1); diff --git a/tests/drivers/lsp_stream.js b/tests/drivers/lsp_stream.js new file mode 100644 index 0000000..0dd0553 --- /dev/null +++ b/tests/drivers/lsp_stream.js @@ -0,0 +1,11 @@ +// expose either extension server host as a stdin/stdout process for focused tests. +const fs = require('fs'); +const path = require('path'); +const target = path.resolve(process.argv[2]); +const { wasmProcess } = require('../../editors/vscode/client/wasmproc'); +const { vmProcess } = require('../../editors/vscode/client/vmproc'); +const server = target.endsWith('.wasm') ? wasmProcess(target) : vmProcess(target); +server.stdout.on('data', bytes => process.stdout.write(bytes)); +server.stderr.on('data', bytes => process.stderr.write(bytes)); +server.on('exit', status => { if (status !== 0) process.exitCode = 1; }); +server.stdin.write(fs.readFileSync(0)); diff --git a/tests/drivers/lsp_test.js b/tests/drivers/lsp_test.js new file mode 100644 index 0000000..a783e1e --- /dev/null +++ b/tests/drivers/lsp_test.js @@ -0,0 +1,246 @@ +// protocol tests for the language server (node, no packages). +// usage: node tests/drivers/lsp_test.js path/to/zclsp +const { spawnSync } = require('child_process'); +const fs = require('fs'); +const server = process.argv[2]; +let ok = 0, bad = 0; +function check(cond, label, extra) { + if (cond) { ok++; console.log('ok lsp ' + label); } + else { bad++; console.log('FAIL lsp ' + label + (extra === undefined ? '' : ': ' + JSON.stringify(extra))); } +} +function frame(m) { + const b = Buffer.from(JSON.stringify(m), 'utf8'); + return Buffer.concat([Buffer.from('Content-Length: ' + b.length + '\r\n\r\n'), b]); +} +function parse(buf) { + const out = []; let i = 0; + while (i < buf.length) { + const j = buf.indexOf('\r\n\r\n', i); + if (j < 0) break; + const n = parseInt(/Content-Length: (\d+)/.exec(buf.slice(i, j).toString())[1], 10); + out.push(JSON.parse(buf.slice(j + 4, j + 4 + n).toString('utf8'))); + i = j + 4 + n; + } + return out; +} +function run(msgs) { + const r = spawnSync(server, [], { input: Buffer.concat(msgs.map(frame)), timeout: 30000, maxBuffer: 1 << 28 }); + return { code: r.status, msgs: parse(r.stdout) }; +} +const init = id => ({ jsonrpc: '2.0', id, method: 'initialize', params: {} }); +const open = (uri, text) => ({ jsonrpc: '2.0', method: 'textDocument/didOpen', params: { textDocument: { uri, languageId: 'zc', version: 1, text } } }); +const change = (uri, text) => ({ jsonrpc: '2.0', method: 'textDocument/didChange', params: { textDocument: { uri, version: 2 }, contentChanges: [{ text }] } }); +const sem = (id, uri) => ({ jsonrpc: '2.0', id, method: 'textDocument/semanticTokens/full', params: { textDocument: { uri } } }); +const diags = (msgs, uri) => msgs.filter(m => m.method === 'textDocument/publishDiagnostics' && (!uri || m.params.uri === uri)); +const A = 'file:///a.zc', B2 = 'file:///b.zc'; + +// initialize: capabilities and the 12 type legend +let r = run([init(1), { jsonrpc: '2.0', method: 'exit' }]); +let res = r.msgs.find(m => m.id === 1); +check(res && res.result.capabilities.semanticTokensProvider.legend.tokenTypes.length === 12, 'initialize advertises the legend'); +check(res && res.result.capabilities.textDocumentSync.change === 1, 'initialize asks for full-text sync'); + +// a request before initialize is refused, and the id is echoed +r = run([{ jsonrpc: '2.0', id: 7, method: 'textDocument/semanticTokens/full', params: { textDocument: { uri: A } } }]); +check(r.msgs.length === 1 && r.msgs[0].error && r.msgs[0].error.code === -32002 && r.msgs[0].id === 7, 'a request before initialize gets -32002'); + +// unknown methods: MethodNotFound with numeric and string ids, silence for notifications +r = run([init(1), { jsonrpc: '2.0', id: 'abc', method: 'workspace/nothing' }, { jsonrpc: '2.0', method: 'workspace/nothing' }]); +const nf = r.msgs.find(m => m.id === 'abc'); +check(nf && nf.error && nf.error.code === -32601, 'unknown request gets MethodNotFound, string id echoed', nf); +check(r.msgs.length === 2, 'an unknown notification is ignored'); + +// a clean program has no diagnostics +const good = 'fn main() {\n\tput_n(1 + 2);\n\treturn 0;\n}\n'; +r = run([init(1), open(A, good)]); +let d = diags(r.msgs, A); +check(d.length === 1 && d[0].params.diagnostics.length === 0, 'a clean program publishes an empty list', d); + +// an error lands on the right line and column +const bad1 = 'fn main() {\n\tlet x = 5\n\treturn x;\n}\n'; +r = run([init(1), open(A, bad1)]); +d = diags(r.msgs, A); +const e1 = d.length && d[0].params.diagnostics[0]; +check(e1 && e1.severity === 1 && e1.range.start.line === 1 && /expected ';', found 'return'/.test(e1.message), 'a missing semicolon is reported on its line', e1); + +// utf-16 columns: an emoji (two code units) and an accented letter before the error +const uni = '// café 😀\nfn main() { put_s("é😀"); return undefined_name; }\n'; +r = run([init(1), open(A, uni)]); +d = diags(r.msgs, A); +const e2 = d.length && d[0].params.diagnostics[0]; +const col = uni.split('\n')[1].indexOf('undefined_name'); +check(e2 && e2.range.start.line === 1 && e2.range.start.character === col, 'columns count utf-16 units', e2 && e2.range); + +// fixing the text through didChange clears the error +r = run([init(1), open(A, bad1), change(A, good)]); +d = diags(r.msgs, A); +check(d.length === 2 && d[0].params.diagnostics.length > 0 && d[1].params.diagnostics.length === 0, 'didChange re-checks the document'); + +// two documents do not leak into each other, and checks repeat the same way +r = run([init(1), open(A, bad1), open(B2, good), change(A, bad1)]); +const da = diags(r.msgs, A), db = diags(r.msgs, B2); +check(da.length === 2 && JSON.stringify(da[0].params.diagnostics) === JSON.stringify(da[1].params.diagnostics) && db[0].params.diagnostics.length === 0, 'documents are checked in isolation'); + +// syntax modifications made by the program itself apply to the check, like the compiler +const pipeDef = 'immediate fn pipe_op() { next(); add_binop("|>", 1, "c"); return 0; }\npipe_op\n'; +const pipeUse = 'fn double(x) { return x * 2; }\nfn main() { return 4 |> double; }\n'; +r = run([init(1), open(A, pipeDef + pipeUse), open(B2, pipeUse)]); +check(diags(r.msgs, A)[0].params.diagnostics.filter(x => x.severity === 1).length === 0, 'a parse word that adds an operator is understood'); +check(diags(r.msgs, B2)[0].params.diagnostics.filter(x => x.severity === 1).length > 0, 'the same program without the parse word is an error'); + +// semantic tokens: groups of five, keywords and functions classified from the compiler's own tables +r = run([init(1), open(A, good), sem(2, A)]); +res = r.msgs.find(m => m.id === 2); +const data = res && res.result && res.result.data; +check(data && data.length > 0 && data.length % 5 === 0, 'semantic tokens come in groups of five'); +check(data && data[3] === 0 && data[0] === 0 && data[1] === 0, 'fn is a keyword at 0:0', data && data.slice(0, 5)); +check(data && data[8] === 2, 'a function name is a function', data && data.slice(5, 10)); + +// decode the semantic tokens of a document into text:type pairs +function tokens(text) { + const rr = run([init(1), open(A, text), sem(2, A)]); + const dd = rr.msgs.find(m => m.id === 2).result.data; + const kinds = ['keyword', 'type', 'function', 'variable', 'string', 'number', 'operator', 'comment', 'parameter', 'property', 'macro', 'enumMember']; + const lines = text.split('\n'); let l = 0, c = 0; const out = []; + for (let i = 0; i < dd.length; i += 5) { + l += dd[i]; c = dd[i] ? dd[i + 1] : c + dd[i + 1]; + out.push(lines[l].substr(c, dd[i + 2]) + ':' + kinds[dd[i + 3]]); + } + return out; +} +const meth = 'struct point {\n\tlet x, y;\n\tfn sum() { return this.x + this.y; }\n}\nfn main() {\n\tpoint* p = new(point);\n\treturn p.sum();\n}\n'; +const tk = tokens(meth); +check(tk.filter(t => t === 'sum:function').length === 2, 'a method is a function where it is declared and where it is called', tk); +check(tk.includes('this:parameter') && tk.includes('x:property'), 'this is a parameter and a field is a property', tk); +check(tk.filter(t => t === 'point:type').length >= 3, 'a user type is a type, also before a postfix star', tk); + +// a large document goes through the streaming reader +let big = ''; +for (let i = 0; i < 2500; i++) big += 'fn f' + i + '(a, b) { return a * b + ' + i + '; }\n'; +big += 'fn main() { return f0(1, 2); }\n'; +r = run([init(1), open(A, big)]); +d = diags(r.msgs, A); +check(d.length === 1 && d[0].params.diagnostics.filter(x => x.severity === 1).length === 0, 'a document of ' + big.length + ' bytes is checked', d.length); + +// local tables grow past their initial reservation, and the next document still checks +let many = 'fn main() {\n'; +for (let i = 0; i < 600; i++) many += '\tlet v' + i + ' = ' + i + ';\n'; +many += '\treturn 0;\n}\n'; +r = run([init(1), open(A, many), open(B2, good)]); +d = diags(r.msgs, A); +check(d.length === 1 && !d[0].params.diagnostics.some(x => x.severity === 1) && diags(r.msgs, B2).length === 1, 'locals grow past 512 and the server carries on', d.length); + +// hover: the text at a position, zero-based line and character, or null +function hover(text, line, ch) { + const rr = run([init(1), open(A, text), { jsonrpc: '2.0', id: 3, method: 'textDocument/hover', params: { textDocument: { uri: A }, position: { line, character: ch } } }]); + const x = rr.msgs.find(m => m.id === 3); + return x && x.result ? x.result.contents.value : null; +} +const at = (text, line, word, nth) => { const s = text.split('\n')[line]; let i = -1; for (let k = 0; k <= (nth || 0); k++) i = s.indexOf(word, i + 1); return i; }; +const hsrc = '// adds two numbers\nint add(int a, int b) { return a + b; }\nint twice(int a) { return a * 2; }\nint main() {\n\tint n = add(1, 2);\n\tlet i = 3;\n\treturn twice(n) + i;\n}\n'; +let hv = hover(hsrc, 4, at(hsrc, 4, 'add')); +check(hv && hv.includes('int add(int a, int b)') && hv.includes('adds two numbers'), 'hover on a call shows the signature and the doc', hv); +check(hv && /^```zc\n/.test(hv) && /\n```\n\nadds two numbers/.test(hv), 'hover separates the code fence from Markdown documentation', hv); +const unicodeHover = 'fn main() { put_s("é😀"); put_c(10); return 0; }\n'; +hv = hover(unicodeHover, 0, at(unicodeHover, 0, 'put_c')); +check(hv && hv.includes('put_c('), 'hover converts UTF-16 columns after BMP and astral characters', hv); +hv = hover(hsrc, 0, hsrc.split('\n')[0].length + 10); +check(hv === null, 'an out-of-range hover column cannot select a name on the following line', hv); +hv = hover(hsrc, 6, at(hsrc, 6, 'twice')); +check(hv && hv.includes('int twice(int a)') && !hv.includes('adds two numbers'), 'a function with no doc still shows its signature', hv); +hv = hover(hsrc, 1, at(hsrc, 1, 'a', 2)); +check(hv && hv.includes('parameter') && hv.includes('int a'), 'hover on a parameter shows its declaration', hv); +hv = hover(hsrc, 5, at(hsrc, 5, 'i')); +check(hv && hv.includes('let i') && hv.includes('local'), 'hover on a local shows its declaration', hv); +hv = hover(hsrc, 6, at(hsrc, 6, 'i', 1)); +check(hv && hv.includes('let i') && hv.includes('local'), 'hover on a use of a local shows its declaration', hv); +const tsrc = 'fn main() {\n\tlet i = 3;\n\tlet p = new(int);\n\treturn i;\n}\n'; +// an untyped parameter and an untyped function show the types annotate gives them +const isrc = 'fn twice(x) { return x * 2; }\nfn main() {\n\tlet n = twice(4);\n\treturn n;\n}\n'; +let hi = hover(isrc, 0, 9); +check(hi && hi.includes('x // parameter; type int'), 'an untyped parameter shows its inferred type', hi); +hi = hover(isrc, 0, 3); +check(hi && hi.includes('int twice(int x)'), 'an untyped function shows its inferred parameter and return types', hi); +hv = hover(tsrc, 1, 5); +check(hv && hv.includes('type int'), 'an untyped number local shows the type annotate gives it', hv); +hv = hover(tsrc, 2, 5); +check(hv && hv.includes('type int*'), 'a pointer local shows its pointer type on its declaration line', hv); +hv = hover('fn main() {\n\tlet i = 3;\n\treturn i;\n}\n', 2, 8); +check(hv && hv.includes('let i'), 'a return of a local is a use, not a declaration', hv); +hv = hover(hsrc, 6, at(hsrc, 6, 'return')); +check(hv && hv.includes('leaves the function with a value'), 'a keyword shows what it does', hv); +hv = hover('fn main() {\n\tput_c(32);\n\treturn 0;\n}\n', 1, 1); +check(hv && hv.includes('put_c('), 'a built-in function shows its signature', hv); +const kwsrc = 'fn main() {\n\tint* p = new(int);\n\tfree(p);\n\treturn 0;\n}\n'; +hv = hover(kwsrc, 1, at(kwsrc, 1, 'new')); +check(hv && hv.includes('new(T)') && hv.includes('fresh object'), 'hover on new shows its form and what it does', hv); +hv = hover(kwsrc, 2, at(kwsrc, 2, 'free')); +check(hv && hv.includes('frees a heap object'), 'hover on free shows its doc', hv); +const kdoc = '// the number of lines\nfn main() {\n\treturn 0;\n}\n'; +hv = hover(kdoc, 2, 1); +check(hv === null || !hv.includes('number of lines'), 'a doc comment is not attached to a keyword in a body', hv); + +// a method of a struct is indented in the body, and its hover is still its line and doc +const msrc = 'struct point {\n\tlet x;\n\t// the sum of the parts\n\tfn sum() { return this.x; }\n\tstatic fn zero() { return 0; }\n}\nfn main() {\n\tpoint* p = new(point);\n\treturn p.sum() + point.zero();\n}\n'; +hv = hover(msrc, 8, at(msrc, 8, 'sum')); +check(hv && hv.includes('fn sum()') && hv.includes('the sum of the parts'), 'a method called through a pointer shows its signature and doc', hv); +hv = hover(msrc, 8, at(msrc, 8, 'zero')); +check(hv && hv.includes('static fn zero()'), 'a static function of a struct shows its signature', hv); + +// a typed method shows its return type and parameters +const tmsrc = 'struct point {\n\tlet x;\n\t// the sum of the parts\n\tint sum(int k) { return this.x + k; }\n}\nfn main() {\n\tpoint* p = new(point);\n\treturn p.sum(1);\n}\n'; +hv = hover(tmsrc, 7, at(tmsrc, 7, 'sum')); +check(hv && hv.includes('int sum(int k)') && hv.includes('the sum of the parts'), 'a typed method shows its return type and parameters', hv); + +// externs are declarations, so they draw no "never reassigned" hint +const ext = 'extern double sqrt(double);\nextern double pow(double x, double y);\nfn main() {\n\treturn 0;\n}\n'; +r = run([init(1), open(A, ext)]); +d = diags(r.msgs, A); +check(d.length === 1 && !d[0].params.diagnostics.some(x => /never reassigned/.test(x.message)), 'an extern draws no hint about its stub', d.length && d[0].params.diagnostics); + +// shutdown and exit end the process cleanly +r = run([init(1), { jsonrpc: '2.0', id: 9, method: 'shutdown' }, { jsonrpc: '2.0', method: 'exit' }]); +check(r.code === 0 && r.msgs.some(m => m.id === 9 && m.result === null), 'shutdown then exit ends with status 0', r.code); + +// the corpus: every valid program checks clean, every bad one is rejected with its message +function cli(file) { + const r = spawnSync(server, [], { input: fs.readFileSync(file), timeout: 30000 }); + return r.stdout.toString(); +} +let clean = 0, dirty = []; +for (const dir of ['examples', 'tests/programs']) { + for (const f of fs.readdirSync(dir).filter(n => n.endsWith('.zc'))) { + if (f === 'manyerrors.zc' || f === 'undefined_call.zc' || f === 'zc0typed.zc') continue; // manyerrors and undefined_call are bad on purpose; zc0typed.zc is stage 0 syntax + const out = cli(dir + '/' + f); + if (/: error: /.test(out)) dirty.push(dir + '/' + f + ' ' + out.split('\n')[0]); else clean++; + } +} +check(dirty.length === 0, 'every example and test program checks clean (' + clean + ' files)', dirty); +let missed = [], n = 0; +for (const dir of ['errors', 'typeerrors', 'langerrors']) { + for (const f of fs.readdirSync('tests/' + dir).filter(x => x.endsWith('.zc'))) { + const want = fs.readFileSync('tests/' + dir + '/' + f, 'utf8').split(/\r?\n/)[0].replace('// expect: ', ''); + const out = cli('tests/' + dir + '/' + f); + n++; + if (!out.includes(want)) missed.push(dir + '/' + f + ' wanted [' + want + '] got [' + out.split('\n')[0] + ']'); + } +} +check(missed.length === 0, 'every bad program is rejected with the compiler\'s message (' + n + ' files)', missed); +const manyOut = cli('tests/programs/manyerrors.zc').split('\n').filter(l => /: error: /.test(l)); +check(manyOut.length === 4, 'several errors in one run are all reported', manyOut); + +// names resolve the way the compiler resolves them: a local or a parameter named +// like a built-in is that variable, and the built-in is only what the scope leaves +const scp = 'int cap = 2;\nfn grow(int cap) { return cap + 1; }\nfn main() {\n\tint cap = 3;\n\treturn len(grow(cap));\n}\n'; +const sct = tokens(scp); +check(sct.includes('cap:parameter') && sct.includes('cap:variable') && !sct.includes('cap:keyword') && !sct.includes('cap:function'), 'a name like a built-in is a variable or parameter where the scope says', sct); +check(sct.includes('len:keyword'), 'a built-in that is a keyword in the compiler is still a keyword', sct); +let hcv = hover(scp, 4, at(scp, 4, 'cap')); +check(hcv && hcv.includes('int cap') && hcv.includes('local'), 'hover on a use of a local named like a built-in shows the local', hcv); +hcv = hover(scp, 1, at(scp, 1, 'cap', 1)); +check(hcv && hcv.includes('parameter'), 'hover on a use of a parameter named like a built-in shows the parameter', hcv); +hcv = hover(scp, 4, at(scp, 4, 'len')); +check(hcv && hcv.includes('the number of items'), 'hover on the built-in still shows its text where no local shadows it', hcv); + +console.log(ok + ' lsp checks passed, ' + bad + ' failed'); +process.exit(bad ? 1 : 0); diff --git a/tests/drivers/lsp_vm.js b/tests/drivers/lsp_vm.js new file mode 100644 index 0000000..24c1a5b --- /dev/null +++ b/tests/drivers/lsp_vm.js @@ -0,0 +1,112 @@ +// tests for the language server run by the javascript vm (src/js/tinyvm.js), through +// the extension's own host (editors/vscode/client/vmproc.js): the same checks as +// tests/drivers/lsp_wasm.js. the vm starts at the image's heap and grows as the server asks. +// usage: node tests/drivers/lsp_vm.js path/to/server/dir (tinyvm.js, lsp.json, layout.js) +const path = require('path'); +const { vmProcess } = require(path.join(__dirname, '..', '..', 'editors', 'vscode', 'client', 'vmproc.js')); +const fs = require('fs'); +const dir = path.resolve(process.argv[2]); +let ok = 0, bad = 0; +function check(cond, label, extra) { + if (cond) { ok++; console.log('ok lsp vm ' + label); } + else { bad++; console.log('FAIL lsp vm ' + label + (extra === undefined ? '' : ': ' + JSON.stringify(extra).slice(0, 300))); } +} +// runs one session: the messages are sent, and the answers collected by id +function session(docs) { + const replies = {}, notes = []; + const p = vmProcess(dir, { log: () => {} }); + const exited = { code: undefined }; + p.on('exit', c => { exited.code = c; }); + let inbox = Buffer.alloc(0); + p.stdout.on('data', d => { + inbox = Buffer.concat([inbox, d]); + for (;;) { + const e = inbox.indexOf('\r\n\r\n'); if (e < 0) return; + const n = parseInt(/Content-Length: (\d+)/.exec(inbox.slice(0, e).toString())[1], 10); + if (inbox.length < e + 4 + n) return; + const m = JSON.parse(inbox.slice(e + 4, e + 4 + n).toString('utf8')); + inbox = inbox.slice(e + 4 + n); + if (m.id !== undefined) replies[m.id] = m; else notes.push(m); + } + }); + const send = m => { const b = Buffer.from(JSON.stringify(m), 'utf8'); p.stdin.write(Buffer.concat([Buffer.from('Content-Length: ' + b.length + '\r\n\r\n'), b])); }; + send({ jsonrpc: '2.0', id: 1, method: 'initialize', params: { processId: 1, capabilities: {} } }); + send({ jsonrpc: '2.0', method: 'initialized', params: {} }); + for (const [uri, text] of docs) send({ jsonrpc: '2.0', method: 'textDocument/didOpen', params: { textDocument: { uri, languageId: 'zc', version: 1, text } } }); + return { p, replies, notes, send, exited }; +} +const clean = 'int main() {\n\tint x = 1;\n\treturn x;\n}\n'; +const bad_src = 'int main() {\n\tint x = 1\n\treturn x;\n}\n'; +const tokOf = (s, uri) => { + s.send({ jsonrpc: '2.0', id: 2, method: 'textDocument/semanticTokens/full', params: { textDocument: { uri } } }); + s.send({ jsonrpc: '2.0', id: 3, method: 'textDocument/hover', params: { textDocument: { uri }, position: { line: 1, character: 9 } } }); + s.send({ jsonrpc: '2.0', id: 4, method: 'shutdown', params: null }); + s.send({ jsonrpc: '2.0', method: 'exit' }); +}; +const diagsOf = (s, uri) => s.notes.filter(m => m.method === 'textDocument/publishDiagnostics' && m.params.uri === uri); + +// a clean program: initialize, no diagnostics, tokens, and it exits on its own +{ + const s = session([['file:///a.zc', clean]]); + tokOf(s, 'file:///a.zc'); + const init = s.replies[1]; + check(init && init.result.capabilities.semanticTokensProvider && init.result.capabilities.hoverProvider, 'initialize advertises tokens and hover', init); + check(diagsOf(s, 'file:///a.zc').length === 1 && diagsOf(s, 'file:///a.zc')[0].params.diagnostics.every(x => x.severity !== 1), 'a clean program has no errors (hints are fine)'); + const t = s.replies[2]; + check(t && t.result && t.result.data.length > 0 && t.result.data.length % 5 === 0, 'semantic tokens come back in groups of five', t && t.result && t.result.data.length); + check(s.replies[4] && s.replies[4].result === null, 'shutdown is answered'); + check(s.p.stdin.write(Buffer.alloc(0)) === false, 'the server has exited after exit'); +} +// a broken program: the compiler's own message is the diagnostic +{ + const s = session([['file:///b.zc', bad_src]]); + const d = diagsOf(s, 'file:///b.zc'); + check(d.length === 1 && d[0].params.diagnostics.length > 0, 'a missing semicolon gives a diagnostic'); + check(d.length === 1 && d[0].params.diagnostics.some(x => /expected/.test(x.message)), 'the message is the compiler\'s', d); + s.send({ jsonrpc: '2.0', method: 'exit' }); +} +// hover: a name the document declares, and a compiler name with docs in the image +{ + const src = '// adds one to its argument\nint inc(int x) { return x + 1; }\nint main() {\n\treturn inc(2);\n}\n'; + const s = session([['file:///c.zc', src]]); + s.send({ jsonrpc: '2.0', id: 5, method: 'textDocument/hover', params: { textDocument: { uri: 'file:///c.zc' }, position: { line: 3, character: 9 } } }); + s.send({ jsonrpc: '2.0', method: 'exit' }); + const h = s.replies[5]; + check(h && h.result && /adds one/.test(h.result.contents.value), 'hover shows the comment above a declaration', h); + const s2 = session([['file:///d.zc', 'int main() {\n\tint z = docofs("x");\n\treturn 0;\n}\n']]); + s2.send({ jsonrpc: '2.0', id: 6, method: 'textDocument/hover', params: { textDocument: { uri: 'file:///d.zc' }, position: { line: 1, character: 10 } } }); + s2.send({ jsonrpc: '2.0', method: 'exit' }); + const h2 = s2.replies[6]; + check(h2 && h2.result && /packed documentation/.test(h2.result.contents.value), 'hover shows the docs of a compiler name', h2); +} +// a document that is bigger than one feed: the input is split and comes out whole +{ + let big = 'int main() {\n'; + for (let i = 0; i < 4000; i++) big += '\tint v' + i + ' = ' + i + ';\n'; + big += '\treturn 0;\n}\n'; + const s = session([['file:///e.zc', big]]); + s.send({ jsonrpc: '2.0', method: 'exit' }); + check(diagsOf(s, 'file:///e.zc').length === 1, 'a document bigger than one feed is read whole (' + big.length + ' bytes)'); +} +// the regression: the server halted without an answer on any document that +// needed more memory than its first heap (examples/bf.zc, and a document twice +// the size of the earlier one). each must be checked and published, and the +// server must still be running until it is told to exit +{ + const bfPath = path.join(__dirname, '..', '..', 'examples', 'bf.zc'); + const bf = fs.readFileSync(bfPath, 'utf8'); + const s = session([['file:///bf.zc', bf]]); + s.send({ jsonrpc: '2.0', method: 'textDocument/didChange', params: { textDocument: { uri: 'file:///bf.zc', version: 2 }, contentChanges: [{ text: bf }] } }); + s.send({ jsonrpc: '2.0', method: 'exit' }); + check(diagsOf(s, 'file:///bf.zc').length >= 1, 'examples/bf.zc is checked and published (' + bf.length + ' bytes)'); + check(s.exited.code === 0, 'the server runs until exit, then leaves with status 0', s.exited); + let huge = 'int main() {\n'; + for (let i = 0; i < 12000; i++) huge += '\tint h' + i + ' = ' + i + ' + ' + (i % 7) + ';\n'; + huge += '\treturn 0;\n}\n'; + const s2 = session([['file:///huge.zc', huge]]); + s2.send({ jsonrpc: '2.0', method: 'exit' }); + check(diagsOf(s2, 'file:///huge.zc').length === 1, 'a document past the first heap is checked and published (' + huge.length + ' bytes)', s2.exited); + check(s2.exited.code === 0, 'the big document leaves the server with status 0 after exit', s2.exited); +} +console.log(ok + ' lsp vm checks passed, ' + bad + ' failed'); +process.exit(bad ? 1 : 0); diff --git a/tests/drivers/lsp_wasm.js b/tests/drivers/lsp_wasm.js new file mode 100644 index 0000000..4ff2d16 --- /dev/null +++ b/tests/drivers/lsp_wasm.js @@ -0,0 +1,112 @@ +// tests for the language server as WebAssembly, through the extension's own +// host (editors/vscode/client/wasmproc.js). the same protocol checks as the +// native server, plus the hover docs of a compiler name. +// usage: node tests/drivers/lsp_wasm.js path/to/lsp.wasm +const path = require('path'); +const { wasmProcess } = require(path.join(__dirname, '..', '..', 'editors', 'vscode', 'client', 'wasmproc.js')); +const fs = require('fs'); +const wasm = process.argv[2]; +let ok = 0, bad = 0; +function check(cond, label, extra) { + if (cond) { ok++; console.log('ok lsp wasm ' + label); } + else { bad++; console.log('FAIL lsp wasm ' + label + (extra === undefined ? '' : ': ' + JSON.stringify(extra).slice(0, 300))); } +} +// runs one session: the messages are sent, and the answers collected by id +function session(docs) { + const replies = {}, notes = []; + const p = wasmProcess(wasm, { log: () => {} }); + const exited = { code: undefined }; + p.on('exit', c => { exited.code = c; }); + let inbox = Buffer.alloc(0); + p.stdout.on('data', d => { + inbox = Buffer.concat([inbox, d]); + for (;;) { + const e = inbox.indexOf('\r\n\r\n'); if (e < 0) return; + const n = parseInt(/Content-Length: (\d+)/.exec(inbox.slice(0, e).toString())[1], 10); + if (inbox.length < e + 4 + n) return; + const m = JSON.parse(inbox.slice(e + 4, e + 4 + n).toString('utf8')); + inbox = inbox.slice(e + 4 + n); + if (m.id !== undefined) replies[m.id] = m; else notes.push(m); + } + }); + const send = m => { const b = Buffer.from(JSON.stringify(m), 'utf8'); p.stdin.write(Buffer.concat([Buffer.from('Content-Length: ' + b.length + '\r\n\r\n'), b])); }; + send({ jsonrpc: '2.0', id: 1, method: 'initialize', params: { processId: 1, capabilities: {} } }); + send({ jsonrpc: '2.0', method: 'initialized', params: {} }); + for (const [uri, text] of docs) send({ jsonrpc: '2.0', method: 'textDocument/didOpen', params: { textDocument: { uri, languageId: 'zc', version: 1, text } } }); + return { p, replies, notes, send, exited }; +} +const clean = 'int main() {\n\tint x = 1;\n\treturn x;\n}\n'; +const bad_src = 'int main() {\n\tint x = 1\n\treturn x;\n}\n'; +const tokOf = (s, uri) => { + s.send({ jsonrpc: '2.0', id: 2, method: 'textDocument/semanticTokens/full', params: { textDocument: { uri } } }); + s.send({ jsonrpc: '2.0', id: 3, method: 'textDocument/hover', params: { textDocument: { uri }, position: { line: 1, character: 9 } } }); + s.send({ jsonrpc: '2.0', id: 4, method: 'shutdown', params: null }); + s.send({ jsonrpc: '2.0', method: 'exit' }); +}; +const diagsOf = (s, uri) => s.notes.filter(m => m.method === 'textDocument/publishDiagnostics' && m.params.uri === uri); + +// a clean program: initialize, no diagnostics, tokens, and it exits on its own +{ + const s = session([['file:///a.zc', clean]]); + tokOf(s, 'file:///a.zc'); + const init = s.replies[1]; + check(init && init.result.capabilities.semanticTokensProvider && init.result.capabilities.hoverProvider, 'initialize advertises tokens and hover', init); + check(diagsOf(s, 'file:///a.zc').length === 1 && diagsOf(s, 'file:///a.zc')[0].params.diagnostics.every(x => x.severity !== 1), 'a clean program has no errors (hints are fine)'); + const t = s.replies[2]; + check(t && t.result && t.result.data.length > 0 && t.result.data.length % 5 === 0, 'semantic tokens come back in groups of five', t && t.result && t.result.data.length); + check(s.replies[4] && s.replies[4].result === null, 'shutdown is answered'); + check(s.p.stdin.write(Buffer.alloc(0)) === false, 'the server has exited after exit'); +} +// a broken program: the compiler's own message is the diagnostic +{ + const s = session([['file:///b.zc', bad_src]]); + const d = diagsOf(s, 'file:///b.zc'); + check(d.length === 1 && d[0].params.diagnostics.length > 0, 'a missing semicolon gives a diagnostic'); + check(d.length === 1 && d[0].params.diagnostics.some(x => /expected/.test(x.message)), 'the message is the compiler\'s', d); + s.send({ jsonrpc: '2.0', method: 'exit' }); +} +// hover: a name the document declares, and a compiler name with docs in the image +{ + const src = '// adds one to its argument\nint inc(int x) { return x + 1; }\nint main() {\n\treturn inc(2);\n}\n'; + const s = session([['file:///c.zc', src]]); + s.send({ jsonrpc: '2.0', id: 5, method: 'textDocument/hover', params: { textDocument: { uri: 'file:///c.zc' }, position: { line: 3, character: 9 } } }); + s.send({ jsonrpc: '2.0', method: 'exit' }); + const h = s.replies[5]; + check(h && h.result && /adds one/.test(h.result.contents.value), 'hover shows the comment above a declaration', h); + const s2 = session([['file:///d.zc', 'int main() {\n\tint z = docofs("x");\n\treturn 0;\n}\n']]); + s2.send({ jsonrpc: '2.0', id: 6, method: 'textDocument/hover', params: { textDocument: { uri: 'file:///d.zc' }, position: { line: 1, character: 10 } } }); + s2.send({ jsonrpc: '2.0', method: 'exit' }); + const h2 = s2.replies[6]; + check(h2 && h2.result && /packed documentation/.test(h2.result.contents.value), 'hover shows the docs of a compiler name', h2); +} +// a document that is bigger than one feed: the input is split and comes out whole +{ + let big = 'int main() {\n'; + for (let i = 0; i < 4000; i++) big += '\tint v' + i + ' = ' + i + ';\n'; + big += '\treturn 0;\n}\n'; + const s = session([['file:///e.zc', big]]); + s.send({ jsonrpc: '2.0', method: 'exit' }); + check(diagsOf(s, 'file:///e.zc').length === 1, 'a document bigger than one feed is read whole (' + big.length + ' bytes)'); +} +// the regression: the server halted without an answer on any document that +// needed more memory than its first heap (examples/bf.zc, and a document twice +// the size of the earlier one). each must be checked and published, and the +// server must still be running until it is told to exit +{ + const bfPath = path.join(__dirname, '..', '..', 'examples', 'bf.zc'); + const bf = fs.readFileSync(bfPath, 'utf8'); + const s = session([['file:///bf.zc', bf]]); + s.send({ jsonrpc: '2.0', method: 'textDocument/didChange', params: { textDocument: { uri: 'file:///bf.zc', version: 2 }, contentChanges: [{ text: bf }] } }); + s.send({ jsonrpc: '2.0', method: 'exit' }); + check(diagsOf(s, 'file:///bf.zc').length >= 1, 'examples/bf.zc is checked and published (' + bf.length + ' bytes)'); + check(s.exited.code === 0, 'the server runs until exit, then leaves with status 0', s.exited); + let huge = 'int main() {\n'; + for (let i = 0; i < 12000; i++) huge += '\tint h' + i + ' = ' + i + ' + ' + (i % 7) + ';\n'; + huge += '\treturn 0;\n}\n'; + const s2 = session([['file:///huge.zc', huge]]); + s2.send({ jsonrpc: '2.0', method: 'exit' }); + check(diagsOf(s2, 'file:///huge.zc').length === 1, 'a document past the first heap is checked and published (' + huge.length + ' bytes)', s2.exited); + check(s2.exited.code === 0, 'the big document leaves the server with status 0 after exit', s2.exited); +} +console.log(ok + ' lsp wasm checks passed, ' + bad + ' failed'); +process.exit(bad ? 1 : 0); diff --git a/tests/drivers/memory_limits_test.py b/tests/drivers/memory_limits_test.py new file mode 100644 index 0000000..a973480 --- /dev/null +++ b/tests/drivers/memory_limits_test.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 +# focused host memory configuration and growth checks. +import os +import subprocess + +source = '''fn main() { + int initial = syscall(9, 0, 0, 0); + put_n(syscall(9, 5, initial, 0) == initial); put_c(32); + put_n(syscall(9, 4, initial + 4096, 0) == -1); put_c(32); + put_n(syscall(9, 5, initial + 8192, 0) == initial + 8192); put_c(32); + put_n(syscall(9, 4, initial + 4096, 0) == initial + 4096); put_c(32); + put_n(syscall(9, 5, initial - 1, 0) == -1); put_c(10); + return 0; +} +''' +commands = [['bin/zy'], ['bin/zym'], ['node', 'src/js/tinyvm.js']] +environment = dict(os.environ, TINYVM_MAX_MEMORY_WORDS='16777216') +for command in commands: + result = subprocess.run(command, input=source, text=True, capture_output=True, env=environment, timeout=15) + assert result.returncode == 0 and result.stdout == '1 1 1 1 1\n', (command, result.stdout, result.stderr) + invalid = subprocess.run(command, input=source, text=True, capture_output=True, env=dict(environment, TINYVM_MEMORY_WORDS='1'), timeout=15) + assert invalid.returncode != 0 and invalid.stderr, (command, invalid.stdout) + print('ok runtime memory limits, denied growth, successful growth, and invalid startup: ' + command[0]) diff --git a/tests/drivers/parser_growth_test.py b/tests/drivers/parser_growth_test.py new file mode 100644 index 0000000..4cbfbb2 --- /dev/null +++ b/tests/drivers/parser_growth_test.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 +# parser storage grows across enum members, tagged enums, methods, and generated text. +import subprocess +import sys + +command = sys.argv[1:] + + +def run(source, expected): + result = subprocess.run(command, input=source, text=True, capture_output=True, timeout=30) + assert result.returncode == 0, (result.stdout, result.stderr) + assert result.stdout == expected, result.stdout[:1000] + + +source = 'enum values { ' + ', '.join(f'member_{i}' for i in range(300)) + ' };\n' +source += 'fn main() { gc(); put_n(member_299); put_c(10); return 0; }\n' +run(source, '299\n') +source = ''.join(f'enum tagged_{i} {{ item_{i} {{ int value; }}, empty_{i} }};\n' for i in range(25)) +source += 'fn main() { tagged_24*.m item = tagged_24.item_24(7); gc(); put_n(item.item_24_value); put_c(10); return 0; }\n' +run(source, '7\n') +source = 'struct methods { int value;\n' +source += ''.join(f'int get_{i}() {{ return this.value + {i}; }}\n' for i in range(100)) +source += '}\nfn main() { methods item; item.value = 7; gc(); put_n(item.get_99()); put_c(10); return 0; }\n' +run(source, '106\n') +source = 'enum expanded {\n' +source += ',\n'.join(f'member_with_long_generated_name_{i} {{ ' + ''.join(f'int field_with_long_generated_name_{j}; ' for j in range(8)) + '}' for i in range(40)) +source += '\n};\nfn main() { gc(); put_n(ebcap > 8192); put_c(10); return 0; }\n' +run(source, '1\n') +source = 'fn main() { int result = 0; switch (127) {\n' +source += ''.join(f'case {i}: result = {i};\n' for i in range(128)) +source += '} gc(); put_n(result); put_c(32); put_n(jpscap > 64); put_c(10); return 0; }\n' +run(source, '127 1\n') +source = 'fn main() { int result = 0;\n' +source += ''.join('o3 { result += 1; }\n' for _ in range(40)) +source += 'gc(); put_n(result); put_c(32); put_n(o3pendcap > 10); put_c(10); return 0; }\n' +run(source, '40 1\n') +source = 'fn main() { int i = 0; while (i < 120) { putfact(i, i, 1); i++; } gc(); put_n(factcap > 32); put_c(32); put_n(getlo(119)); put_c(10); return 0; }\n' +run(source, '1 119\n') +long_type = 'structure_' + 't' * 180 +long_method = 'method_' + 'm' * 180 +source = f'struct {long_type} {{ int {long_method}() {{ return 7; }} }};\n' +source += f'fn main() {{ {long_type} value; put_n(value.{long_method}()); put_c(10); return 0; }}\n' +run(source, '7\n') +name = 'generic_' + 'g' * 180 +source = f'int {name}_one() {{ return sizeof(T); }}\nint {name}_two() {{ return sizeof(T) + 1; }}\n' +source += f'fn main() {{ put_n({name}_one() + {name}_two()); put_c(10); return 0; }}\n' +run(source, '3\n') +print('ok parser tables, analyzer paths, queued regions, and generated names grow') diff --git a/tests/drivers/protocol_growth_test.js b/tests/drivers/protocol_growth_test.js new file mode 100644 index 0000000..64015d8 --- /dev/null +++ b/tests/drivers/protocol_growth_test.js @@ -0,0 +1,34 @@ +// preserve long request identifiers and ignore deeply nested unknown json fields. +const assert = require('assert'); +const cp = require('child_process'); +const target = process.argv[2]; +const id = 'request_' + 'x'.repeat(500); +let nested = { ['unknown_' + 'y'.repeat(100)]: 1 }; +for (let i = 0; i < 40; i++) nested = { child: nested }; +const frame = message => { + const body = Buffer.from(JSON.stringify(message)); + return Buffer.concat([Buffer.from('Content-Length: ' + body.length + '\r\nX-Extra: ' + 'z'.repeat(500) + '\r\n\r\n'), body]); +}; +const messages = [{ jsonrpc: '2.0', id, method: 'initialize', extra: nested, params: {} }]; +for (let i = 0; i < 24; i++) messages.push({ jsonrpc: '2.0', method: 'textDocument/didOpen', params: { textDocument: { + uri: 'file:///enum_' + i + '.zc', version: 1, languageId: 'zc', + text: 'enum choice { item { int value; }, empty };\nfn main() { choice*.m p = choice.item(7); return p.item_value; }\n', +} } }); +messages.push({ jsonrpc: '2.0', id: 'finish', method: 'shutdown', params: null }, { jsonrpc: '2.0', method: 'exit' }); +const result = cp.spawnSync(target, [], { input: Buffer.concat(messages.map(frame)), timeout: 20000, maxBuffer: 8 << 20 }); +assert.strictEqual(result.status, 0, String(result.stderr)); +const responses = []; +let data = result.stdout; +while (data.length) { + const end = data.indexOf('\r\n\r\n'); + assert.ok(end >= 0); + const length = Number(/Content-Length:\s*(\d+)/i.exec(data.subarray(0, end).toString())[1]); + responses.push(JSON.parse(data.subarray(end + 4, end + 4 + length).toString())); + data = data.subarray(end + 4 + length); +} +assert.ok(responses.some(message => message.id === id && message.result.capabilities), 'the full request id is echoed'); +assert.ok(responses.some(message => message.id === 'finish'), 'the next request is parsed'); +const diagnostics = responses.filter(message => message.method === 'textDocument/publishDiagnostics'); +assert.strictEqual(diagnostics.length, 24); +assert.ok(diagnostics.every(message => !message.params.diagnostics.some(diagnostic => diagnostic.severity === 1)), JSON.stringify(diagnostics)); +console.log('ok long protocol fields, deep json, and tagged enums across document checks'); diff --git a/tests/drivers/seed_growth_test.py b/tests/drivers/seed_growth_test.py new file mode 100644 index 0000000..426c970 --- /dev/null +++ b/tests/drivers/seed_growth_test.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 +# stage 0 accepts long source streams and grows a tiny initial image reservation. +import pathlib +import subprocess +import tempfile + +root = pathlib.Path(__file__).resolve().parents[2] +with tempfile.TemporaryDirectory(prefix='seed-growth-', dir=root / 'build') as directory: + folder = pathlib.Path(directory) + subprocess.run(['bin/genmap', '--preset', 'run', '--grow', '--genome', '600000', '--heap', '1048576', '-o', directory], check=True, capture_output=True) + compiler = folder / 'zc0' + subprocess.run(['cc', '-O1', '-w', '-fwrapv', '-DMAXW=16', 'src/tools/zc0.c', '-I' + directory, '-Isrc/core', '-o', str(compiler)], check=True) + source = '/*' + 'x' * (5 << 20) + '*/\n' + source += (folder / 'rt.zc').read_text() + (root / 'src/runtime/run.zc').read_text() + source += 'let data[550000]; fn main() { data[549999] = 7; put_n(data[549999]); put_c(32); put_n(4611686018427387904); put_c(10); return 0; }\n' + with (folder / 'genome.h').open('w') as output: + subprocess.run([str(compiler)], input=source, text=True, stdout=output, check=True, timeout=30) + binary = folder / 'vm' + import shutil + for path in ['src/micro/micro.c', 'src/micro/micro_port.h', 'src/micro/port_linux.c', 'src/core/fops.h', 'src/core/bops.h', 'src/core/stack.h']: + shutil.copy(root / path, folder) + subprocess.run(['cc', '-O2', '-w', '-fwrapv', str(folder / 'micro.c'), str(folder / 'port_linux.c'), '-I' + directory, '-o', str(binary)], check=True) + result = subprocess.run([str(binary)], text=True, capture_output=True, timeout=20) + assert result.returncode == 0 and result.stdout == '7 4611686018427387904\n', (result.returncode, result.stdout, result.stderr) +print('ok stage 0 reads beyond four megabytes and grows beyond its initial image capacity') diff --git a/tests/drivers/stack_growth_test.py b/tests/drivers/stack_growth_test.py new file mode 100644 index 0000000..d4abe81 --- /dev/null +++ b/tests/drivers/stack_growth_test.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 +# operand values and return addresses survive explicit and automatic growth. +import subprocess +import sys +import tempfile +from pathlib import Path + +command = sys.argv[1:] + + +def run(source, expected, compile_image=False): + if compile_image and command[0] == 'node': + # compile the execution stress cases once with stage 0, so a javascript + # compiler's speed does not determine the execution-test timeout. + with tempfile.TemporaryDirectory(prefix='stack-image-', dir='build') as directory: + target = Path(directory) + subprocess.run(['bin/genmap', '--preset', 'run', '--grow', '--heap', '1048576', '--genome', '131072', '--frame', '4096', '--return', '256', '--data', '3072', '-o', directory], check=True, capture_output=True) + compiler = target / 'zc0' + subprocess.run(['cc', '-O1', '-w', '-fwrapv', 'src/tools/zc0.c', '-I' + directory, '-Isrc/core', '-o', str(compiler)], check=True, capture_output=True) + full_source = (target / 'rt.zc').read_text() + Path('src/runtime/run.zc').read_text() + source + image = subprocess.run([str(compiler)], input=full_source, text=True, capture_output=True, check=True) + header = target / 'genome.h' + header.write_text(image.stdout) + result = subprocess.run(['node', 'tests/drivers/image_run.js', str(header), str(target / 'memmap.h')], text=True, capture_output=True, timeout=45) + else: + result = subprocess.run(command, input=source, text=True, capture_output=True, timeout=45) + assert result.returncode == 0 and result.stdout == expected, (command, result.returncode, result.stdout, result.stderr) + + +run('''fn main() { + int old_data = data_top - data_low, old_return = return_top - return_low; + put_n(reserve_data(old_data + 1) > old_data); put_c(32); + put_n(reserve_returns(old_return + 1) > old_return); put_c(32); + gc(); put_n(data_node != 0 && return_node != 0); put_c(10); return 0; +} +''', '1 1 1\n') + +run('''fn main() { + put_n(reserve_data(-1) == -1); put_c(32); put_n(reserve_returns(-1) == -1); put_c(32); + put_n(syscall(9, 6, 0, 0) == -1); put_c(32); + int current = syscall(9, 0, 0, 0); syscall(9, 5, current, 0); + put_n(reserve_data(0x10000000) == -1); put_c(32); gc(); put_n(7); put_c(10); return 0; +} +''', '1 1 1 1 7\n') + +count = 40000 +source = 'fn main() { int total = asm { ' + ' '.join(str(i + 1) for i in range(count)) +source += ' + ' * (count - 1) + ' }; gc(); put_n(total); put_c(32); ' +source += 'put_n(data_node != 0); put_c(10); return 0; }\n' +run(source, '800020000 1\n', compile_image=True) + +run('''unchecked int descend(int n) { + if (!n) { gc(); return 7; } + return descend(n - 1) + 1; +} +fn main() { put_n(descend(70000)); put_c(32); put_n(return_node != 0); put_c(10); return 0; } +''', '70007 1\n', compile_image=True) +print('ok explicit reservation, 40000 operands, and 70000 return addresses: ' + command[0]) diff --git a/tests/drivers/storage_analysis_test.js b/tests/drivers/storage_analysis_test.js new file mode 100644 index 0000000..da967f5 --- /dev/null +++ b/tests/drivers/storage_analysis_test.js @@ -0,0 +1,71 @@ +// definite storage findings use the compiler's parser and the editor protocol. +const assert = require('assert'); +const cp = require('child_process'); +const server = process.argv[2]; +function check(text, needle) { + const uri = 'file:///storage-analysis.zc'; + const messages = [{ jsonrpc: '2.0', id: 1, method: 'initialize', params: {} }, + { jsonrpc: '2.0', method: 'textDocument/didOpen', params: { textDocument: { uri, version: 1, text } } }]; + if (needle) { + const at = text.lastIndexOf(needle), before = text.slice(0, at); + assert.ok(at >= 0); + messages.push({ jsonrpc: '2.0', id: 2, method: 'textDocument/hover', params: { textDocument: { uri }, position: { line: before.split('\n').length - 1, character: before.length - before.lastIndexOf('\n') - 1 } } }); + } + messages.push({ jsonrpc: '2.0', method: 'exit' }); + const input = Buffer.concat(messages.map(m => { const b = Buffer.from(JSON.stringify(m)); return Buffer.concat([Buffer.from('Content-Length: ' + b.length + '\r\n\r\n'), b]); })); + const r = cp.spawnSync(server, process.argv.slice(3), { input, timeout: 20000, maxBuffer: 8 << 20 }); + assert.strictEqual(r.status, 0, String(r.stderr)); + const replies = []; let data = r.stdout; + while (data.length) { + const end = data.indexOf('\r\n\r\n'); assert.ok(end >= 0); + const n = Number(/Content-Length:\s*(\d+)/i.exec(data.subarray(0, end).toString())[1]); + replies.push(JSON.parse(data.subarray(end + 4, end + 4 + n))); + data = data.subarray(end + 4 + n); + } + const diagnostics = replies.find(m => m.method === 'textDocument/publishDiagnostics').params.diagnostics; + return { diagnostics, warnings: diagnostics.filter(d => d.severity === 2), hover: replies.find(m => m.id === 2)?.result?.contents?.value }; +} +const prefix = 'unchecked fn main() {\n'; +for (const [type, length] of [['int', 3], ['u8', 3], ['fixed []int', 3], ['[]u8', 3]]) { + const r = check(prefix + ` let p = new(${type}, ${length});\n let alias = p;\n return alias[${length}];\n}\n`, 'alias['); + assert.strictEqual(r.warnings.length, 1, JSON.stringify(r.diagnostics)); + assert.match(r.warnings[0].message, /out-of-bounds.*index 3.*length 3/); + assert.match(r.hover, /size: 3 elements.*lifetime: heap storage; live here/); + assert.strictEqual(r.warnings[0].range.start.line, 3); +} +const negative = check(prefix + ' let p = new(3); return p[-1];\n}\n'); +assert.match(negative.warnings[0]?.message || '', /index -1.*length 3/); +const freed = check(prefix + ' let p = new(int, 3);\n let q = p;\n free(p);\n return q[0];\n}\n', 'q['); +assert.strictEqual(freed.warnings.length, 1, JSON.stringify(freed.diagnostics)); +assert.match(freed.warnings[0].message, /use after free.*line 4/); +assert.match(freed.hover, /size: 3 elements.*lifetime: ended on line 4/); +const dereference = check(prefix + ' let p = new(int); free(p); return *p;\n}\n'); +assert.match(dereference.warnings[0]?.message || '', /use after free/); +const field = check('struct item { int value; };\n' + prefix + ' let p = new(item); free(p); return p.value;\n}\n'); +assert.match(field.warnings[0]?.message || '', /use after free/); +const scope = check(prefix + ' int* p;\n { let a[3]; p = a; }\n return p[0];\n}\n', 'p['); +assert.match(scope.warnings[0]?.message || '', /use after free.*line 3/); +assert.match(scope.hover, /lifetime: ended on line 3/); +const safe = [ + prefix + ' let p = new(int, 3); let q = p as u8*; return q[23]; }', + prefix + ' let p = new(int, 3); let q = (u8*)p; return q[23]; }', + prefix + ' int n = 2; let p = &n; let q = n; return q[2]; }', + prefix + ' let p = new(int, 3); let ignored = 0 && free(p); return p[0]; }', + prefix + ' let p = new(int, 3); let ignored = 1 || free(p); return p[0]; }', + prefix + ' let p = new(int, 50); return p[(u8)300]; }', + prefix + ' let p = new(int, 50); return p[300 as u8]; }', + prefix + ' let p = new(int, 3); let q = p; q[2] = 7; free(p); return 0; }', + prefix + ' let p = new(3); free(p); p = new(3); return p[0]; }', + 'fn unknown(int* p) {}\n' + prefix + ' let p = new(int, 3); unknown(p); return p[9]; }', + 'fn maybe(int n) { return n; }\n' + prefix + ' let p = new(int, 3); return p[maybe(9)]; }', + prefix + ' let p = new(int, 3); if (1) free(p); return p[0]; }', + prefix + ' let p = new(int, 3); while (0) free(p); return p[0]; }', + prefix + ' let p = new(int, 3); let f = fn() { free(p); return 0; }; return p[0]; }', + prefix + ' let p = new([]int, 0); push(p, 9); return p[0]; }', +]; +for (const source of safe) assert.deepStrictEqual(check(source).warnings, [], source); +const unknown = check('int read(int* p) { return p[0]; }\nfn main() { int n = 1; return read(&n); }\n', 'p['); +assert.match(unknown.hover, /size: unknown.*lifetime: unknown/); +const stack = check('fn main() { let a[3]; return a[2]; }\n', 'a['); +assert.match(stack.hover, /size: 3 elements.*lifetime: stack storage/); +console.log('ok definite bounds, free and scope lifetimes, alias hovers, and conservative unknown cases'); diff --git a/tests/drivers/vscode_hover_test.js b/tests/drivers/vscode_hover_test.js new file mode 100644 index 0000000..378dda2 --- /dev/null +++ b/tests/drivers/vscode_hover_test.js @@ -0,0 +1,48 @@ +// run inside a vscode extension test host to check the real hover api objects. +const assert = require('assert'); +const fs = require('fs'); +const path = require('path'); +const vscode = require('vscode'); +exports.run = async () => { + const root = path.resolve(__dirname, '..', '..'); + const fixture = path.join(root, 'build', 'review', 'hover-fixture.zc'); + fs.mkdirSync(path.dirname(fixture), { recursive: true }); + fs.writeFileSync(fixture, '// adds **two** numbers with `integer` arithmetic.\nint add(int a, int b) { return a + b; }\nfn main() { return add(1, 2); }\n'); + const extension = vscode.extensions.getExtension('tinyvm.zc-language-support'); + assert.ok(extension, 'development extension is loaded'); + const doc = await vscode.workspace.openTextDocument(fixture); + await vscode.window.showTextDocument(doc); + await extension.activate(); + let hover; + for (let attempt = 0; attempt < 100; attempt++) { + const replies = await vscode.commands.executeCommand('vscode.executeHoverProvider', doc.uri, new vscode.Position(2, 19)); + hover = replies && replies.find(reply => reply.contents.some(md => md.value.includes('adds **two** numbers'))); + if (hover) break; + await new Promise(resolve => setTimeout(resolve, 100)); + } + assert.ok(hover, 'real vscode provider returns declaration documentation'); + const md = hover.contents[0]; + assert.ok(md instanceof vscode.MarkdownString, 'hover contains a real MarkdownString'); + assert.match(md.value, /^```zc\nint add\(int a, int b\)\n```\n\n/); + assert.ok(md.value.includes('**two**') && md.value.includes('`integer`')); + assert.strictEqual(md.isTrusted, false); + const storageFile = path.join(root, 'build', 'review', 'hover-storage.zc'); + fs.writeFileSync(storageFile, 'unchecked fn main() {\n let p = new(int, 3);\n let /* borrowed array */ q = p;\n free(p);\n return q[0];\n}\n'); + const storageDoc = await vscode.workspace.openTextDocument(storageFile); + await vscode.window.showTextDocument(storageDoc); + let storageHover; + for (let attempt = 0; attempt < 100; attempt++) { + const replies = await vscode.commands.executeCommand('vscode.executeHoverProvider', storageDoc.uri, new vscode.Position(4, 8)); + storageHover = replies && replies.find(reply => reply.contents.some(md => md.value.includes('size: 3 elements'))); + if (storageHover) break; + await new Promise(resolve => setTimeout(resolve, 100)); + } + assert.ok(storageHover, 'real editor returns storage metadata'); + const storageMd = storageHover.contents[0]; + assert.ok(storageMd instanceof vscode.MarkdownString); + assert.match(storageMd.value, /borrowed array/); + assert.match(storageMd.value, /lifetime: ended on line 4/); + const result = 'ok real vscode markdown, code fences, block docs, inferred pointer types, and lifetime metadata\n'; + fs.writeFileSync(path.join(root, 'build', 'review', 'vscode-hover.log'), result); + console.log(result.trim()); +}; diff --git a/tests/drivers/wasm_test.js b/tests/drivers/wasm_test.js new file mode 100644 index 0000000..ddab3be --- /dev/null +++ b/tests/drivers/wasm_test.js @@ -0,0 +1,28 @@ +// runs programs on the webassembly build (src/web/zc.wasm) and compares with the expected output. +// usage: node tests/drivers/wasm_test.js [path/to/zc.wasm] +const fs = require('fs'); +const ZC = require('../../src/web/zc.js'); +(async () => { + const zc = await ZC.load(fs.readFileSync(process.argv[2] || __dirname + '/../../src/web/zc.wasm')); + const cases = [ + ['examples/hello.zc', 'hello, world!'], + ['examples/fact.zc', '1101110101111100000000\n3628800\n'], + ['examples/selfpatch.zc', 'ABC'], + ['examples/lists.zc', '[0 1 4 9 16 25]\nlive: 7\nafter the block: 0\n'], + ['examples/wiring.zc', '16 1234 25 4/9'], + ['examples/struct_closure_opt.zc', '299\n'], + ['examples/opt_test.zc', '1303\n'], + ['tests/programs/basics.zc', '285 610 42 512\nhey 65 -3 -2 1024 -8\n12456\n757\n9 100-101\n5\nok\n'], + ]; + let ok = 0, bad = 0; + for (const [file, want] of cases) { + const r = await zc.run(fs.readFileSync(__dirname + '/../../' + file, 'utf8')); + if (r.output.replace(/\n+$/, '') === want.replace(/\n+$/, '')) { ok++; console.log('ok wasm ' + file); } + else { bad++; console.log('FAIL wasm ' + file + ': got ' + JSON.stringify(r.output) + ' ' + r.messages); } + } + // an error is reported the way the interpreter reports it + const r = await zc.run('fn main() { return undefined_name; }'); + if (/undefined_name/.test(r.output)) { ok++; console.log('ok wasm compiler errors'); } else { bad++; console.log('FAIL wasm compiler errors: ' + JSON.stringify(r.output)); } + console.log(ok + ' wasm checks passed, ' + bad + ' failed'); + process.exit(bad ? 1 : 0); +})(); diff --git a/tests/drivers/word_literals_test.py b/tests/drivers/word_literals_test.py new file mode 100644 index 0000000..a2d230d --- /dev/null +++ b/tests/drivers/word_literals_test.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python3 +# every 64-bit word is representable, even outside a single literal's 63-bit range. +import subprocess + +values = [0, 1, (1 << 62) - 1, 1 << 62, (1 << 63) - 1, -(1 << 62), -(1 << 62) - 1, -(1 << 63), -1] +source = 'fn main() {\n' + ''.join(f'put_n({value}); put_c(10);\n' for value in values) + 'return 0; }\n' +expected = ''.join(str(value) + '\n' for value in values) +for command in [['bin/zy'], ['bin/zym'], ['node', 'src/js/tinyvm.js']]: + result = subprocess.run(command, input=source, text=True, capture_output=True, timeout=20) + assert result.returncode == 0 and result.stdout == expected, (command, result.stdout, result.stderr) + print('ok full-width positive and negative word literals: ' + command[0]) +source = '''immediate fn add_word_operator() { next(); add_binop("**", 11, "4611686018427387904 | +"); return 0; } +add_word_operator +fn main() { put_n(0 ** 0); put_c(10); return 0; } +''' +for command in [['bin/zy'], ['bin/zym'], ['node', 'src/js/tinyvm.js']]: + result = subprocess.run(command, input=source, text=True, capture_output=True, timeout=20) + assert result.returncode == 0 and result.stdout == '4611686018427387904\n', (command, result.stdout, result.stderr) + print('ok full-width word in a stored operator sequence: ' + command[0]) diff --git a/tests/errors/alias_field.zc b/tests/errors/alias_field.zc new file mode 100644 index 0000000..09086ca --- /dev/null +++ b/tests/errors/alias_field.zc @@ -0,0 +1,9 @@ +// expect: c: used after it was given away +struct node { int v; node* next; }; +fn main() { + node* p = new(node), n = new(node); + p.next = n; + node* c = p.next; + free(d*p); + return c.v; +} diff --git a/tests/errors/alias_index.zc b/tests/errors/alias_index.zc new file mode 100644 index 0000000..3c036fe --- /dev/null +++ b/tests/errors/alias_index.zc @@ -0,0 +1,9 @@ +// expect: c: used after it was given away +fn main() { + int** list = new(int*, 2); + int* item = new(int); + list[1] = item; + int* c = list[1]; + free(d*list); + return c[0]; +} diff --git a/tests/errors/alias_plus.zc b/tests/errors/alias_plus.zc new file mode 100644 index 0000000..d42d196 --- /dev/null +++ b/tests/errors/alias_plus.zc @@ -0,0 +1,2 @@ +// expect: q: used after it was given away +fn main() { let p = new(2); p[0] = 1; let q = p + 0; free(d*p); return q[0]; } diff --git a/tests/errors/alias_scope.zc b/tests/errors/alias_scope.zc new file mode 100644 index 0000000..b51bec5 --- /dev/null +++ b/tests/errors/alias_scope.zc @@ -0,0 +1,6 @@ +// expect: q: used after it was given away +fn main() { + let q; + { let p = new(2); q = p + 0; } + return q[0]; +} diff --git a/tests/errors/back_to_m.zc b/tests/errors/back_to_m.zc new file mode 100644 index 0000000..b8c160f --- /dev/null +++ b/tests/errors/back_to_m.zc @@ -0,0 +1,2 @@ +// expect: x: can't be recast back to m +fn main() { let x = new(3); d*x; m*x; return 0; } diff --git a/tests/errors/borrow_escape.zc b/tests/errors/borrow_escape.zc new file mode 100644 index 0000000..ce24480 --- /dev/null +++ b/tests/errors/borrow_escape.zc @@ -0,0 +1,3 @@ +// expect: p: is owned and can't be returned +fn f(.m p) { return p; } +fn main() { let x = new(1); f(x); return 0; } diff --git a/tests/errors/branch_mismatch.zc b/tests/errors/branch_mismatch.zc new file mode 100644 index 0000000..bd19968 --- /dev/null +++ b/tests/errors/branch_mismatch.zc @@ -0,0 +1,3 @@ +// expect: x: ends the branches of an if in different states +fn take(.d p) { return p[0]; } +fn main() { let x = new(1); if (x[0]) take(d*x); else u*x; return 0; } diff --git a/tests/errors/child_dies.zc b/tests/errors/child_dies.zc new file mode 100644 index 0000000..67caca9 --- /dev/null +++ b/tests/errors/child_dies.zc @@ -0,0 +1,3 @@ +// expect: c: used after it was given away +fn take(.d p) { return p[0]; } +fn main() { let p = new(2), c = new(1); p[0] = c; take(d*p); return c[0]; } diff --git a/tests/errors/child_recast.zc b/tests/errors/child_recast.zc new file mode 100644 index 0000000..c9ccd62 --- /dev/null +++ b/tests/errors/child_recast.zc @@ -0,0 +1,2 @@ +// expect: c: belongs to a container; recast the container instead +fn main() { let p = new(2), c = new(1); p[0] = c; d*c; return 0; } diff --git a/tests/errors/copy_owned.zc b/tests/errors/copy_owned.zc new file mode 100644 index 0000000..862c7ef --- /dev/null +++ b/tests/errors/copy_owned.zc @@ -0,0 +1,2 @@ +// expect: x: is an owned object and can't be copied +fn main() { let x = new(1); let y = x; return y[0]; } diff --git a/tests/errors/d_into_block.zc b/tests/errors/d_into_block.zc new file mode 100644 index 0000000..2d7f526 --- /dev/null +++ b/tests/errors/d_into_block.zc @@ -0,0 +1,2 @@ +// expect: x: used after it was given away +fn main() { let x = new(1); d*x; { x[0] = 5; } return x[0]; } diff --git a/tests/errors/d_kept.zc b/tests/errors/d_kept.zc new file mode 100644 index 0000000..dcbfb86 --- /dev/null +++ b/tests/errors/d_kept.zc @@ -0,0 +1,2 @@ +// expect: x: d object was never given away +fn main() { let x = new(3); d*x; x[0] = 1; return 0; } diff --git a/tests/errors/did_you_mean.zc b/tests/errors/did_you_mean.zc new file mode 100644 index 0000000..734b903 --- /dev/null +++ b/tests/errors/did_you_mean.zc @@ -0,0 +1,3 @@ +// expect: did you mean 'helper'? +fn helper(x) { return x; } +fn main() { return helpr(1); } diff --git a/tests/errors/discard_u.zc b/tests/errors/discard_u.zc new file mode 100644 index 0000000..2743f9e --- /dev/null +++ b/tests/errors/discard_u.zc @@ -0,0 +1,3 @@ +// expect: a new object (or a u result) can't be thrown away +.u mk() { let p = new(1); return u*p; } +fn main() { mk(); return 0; } diff --git a/tests/errors/found_token.zc b/tests/errors/found_token.zc new file mode 100644 index 0000000..73e63dc --- /dev/null +++ b/tests/errors/found_token.zc @@ -0,0 +1,2 @@ +// expect: expected ';', found 'return' +fn main() { let a = 1 return 0; } diff --git a/tests/errors/give_m.zc b/tests/errors/give_m.zc new file mode 100644 index 0000000..cd67896 --- /dev/null +++ b/tests/errors/give_m.zc @@ -0,0 +1,3 @@ +// expect: x: is m; write d*x +fn take(.d p) { return p[0]; } +fn main() { let x = new(3); take(x); return 0; } diff --git a/tests/errors/global_store.zc b/tests/errors/global_store.zc new file mode 100644 index 0000000..dc0e546 --- /dev/null +++ b/tests/errors/global_store.zc @@ -0,0 +1,3 @@ +// expect: x: must be d to be stored in a global +let g; +fn main() { let x = new(1); g = x; return 0; } diff --git a/tests/errors/leak_overwrite.zc b/tests/errors/leak_overwrite.zc new file mode 100644 index 0000000..d19b5fd --- /dev/null +++ b/tests/errors/leak_overwrite.zc @@ -0,0 +1,2 @@ +// expect: x: still owns an object +fn main() { let x = new(1); x = new(2); return x[0]; } diff --git a/tests/errors/lend_d.zc b/tests/errors/lend_d.zc new file mode 100644 index 0000000..55ac7e0 --- /dev/null +++ b/tests/errors/lend_d.zc @@ -0,0 +1,4 @@ +// expect: x: is d; pass it to a d parameter +fn look(.m p) { return p[0]; } +fn take(.d p) { return p[0]; } +fn main() { let x = new(3); d*x; look(x); take(x); return 0; } diff --git a/tests/errors/loop_move.zc b/tests/errors/loop_move.zc new file mode 100644 index 0000000..df3c94a --- /dev/null +++ b/tests/errors/loop_move.zc @@ -0,0 +1,3 @@ +// expect: x: is given away inside a loop that comes back around to it +fn take(.d p) { return p[0]; } +fn main() { let x = new(1); let i = 0; d*x; while (i < 3) { take(x); i++; } return 0; } diff --git a/tests/errors/plain_return.zc b/tests/errors/plain_return.zc new file mode 100644 index 0000000..e23cc51 --- /dev/null +++ b/tests/errors/plain_return.zc @@ -0,0 +1,3 @@ +// expect: is called before its body, so its owned return can't be known there +fn main() { return mk(); } +fn mk() { return new(1); } diff --git a/tests/errors/return_m.zc b/tests/errors/return_m.zc new file mode 100644 index 0000000..c450711 --- /dev/null +++ b/tests/errors/return_m.zc @@ -0,0 +1,3 @@ +// expect: q: has to be u to be returned +.u mk(.m q) { return q; } +fn main() { let x = new(1); let y = mk(x); return y[0]; } diff --git a/tests/errors/tag_mismatch.zc b/tests/errors/tag_mismatch.zc new file mode 100644 index 0000000..079f249 --- /dev/null +++ b/tests/errors/tag_mismatch.zc @@ -0,0 +1,2 @@ +// expect: c: its tag doesn't match its container's +fn main() { let p = new(2), c = new(1); u*p; p[0] = c; return 0; } diff --git a/tests/errors/temporary_output.zc b/tests/errors/temporary_output.zc new file mode 100644 index 0000000..02a8a07 --- /dev/null +++ b/tests/errors/temporary_output.zc @@ -0,0 +1,9 @@ +// expect: a new object can only go straight to a d parameter; bind it to a name first +char*.u make_text(int n) { + let text = new(char, n); + return u*text; +} +fn main() { + put_s(make_text(3)); + return 0; +} diff --git a/tests/errors/u_dropped.zc b/tests/errors/u_dropped.zc new file mode 100644 index 0000000..e1ca68a --- /dev/null +++ b/tests/errors/u_dropped.zc @@ -0,0 +1,2 @@ +// expect: x: u object must leave this block +fn main() { let x = new(3); u*x; return 0; } diff --git a/tests/errors/u_skip_level.zc b/tests/errors/u_skip_level.zc new file mode 100644 index 0000000..6caeeb3 --- /dev/null +++ b/tests/errors/u_skip_level.zc @@ -0,0 +1,2 @@ +// expect: t: is u; it can only be handed to a variable of the enclosing block +fn main() { let r; { { let t = new(1); u*t; r = t; } } return r[0]; } diff --git a/tests/errors/unchecked_touch.zc b/tests/errors/unchecked_touch.zc new file mode 100644 index 0000000..141639d --- /dev/null +++ b/tests/errors/unchecked_touch.zc @@ -0,0 +1,2 @@ +// expect: x: is tracked by the analyzer and can't be used in unchecked code +fn main() { let x = new(1); unchecked { x[0] = 1; } return 0; } diff --git a/tests/errors/unused.zc b/tests/errors/unused.zc new file mode 100644 index 0000000..64aff1f --- /dev/null +++ b/tests/errors/unused.zc @@ -0,0 +1,2 @@ +// expect: x: m object is never used +fn main() { let x = new(3); return 0; } diff --git a/tests/errors/use_after_give.zc b/tests/errors/use_after_give.zc new file mode 100644 index 0000000..8a10922 --- /dev/null +++ b/tests/errors/use_after_give.zc @@ -0,0 +1,3 @@ +// expect: x: used after it was given away +fn take(.d p) { return p[0]; } +fn main() { let x = new(3); take(d*x); return x[0]; } diff --git a/tests/langerrors/cfunc_name.zc b/tests/langerrors/cfunc_name.zc new file mode 100644 index 0000000..0b0b0d4 --- /dev/null +++ b/tests/langerrors/cfunc_name.zc @@ -0,0 +1,2 @@ +// expect: isn't a function +fn main() { let p = cfunc(nothere); return 0; } diff --git a/tests/langerrors/const_index.zc b/tests/langerrors/const_index.zc new file mode 100644 index 0000000..03e927b --- /dev/null +++ b/tests/langerrors/const_index.zc @@ -0,0 +1,2 @@ +// expect: the index is out of range for this array +fn main() { [4]int a; a[4] = 1; return 0; } diff --git a/tests/langerrors/extern_bigret.zc b/tests/langerrors/extern_bigret.zc new file mode 100644 index 0000000..37de467 --- /dev/null +++ b/tests/langerrors/extern_bigret.zc @@ -0,0 +1,4 @@ +// expect: a native call can return at most 7 words +struct Big { int a; int b; int c; int d; int e; int f; int g; int h; } +extern struct Big mkbig(long a); +int main() { return 0; } diff --git a/tests/langerrors/extern_struct.zc b/tests/langerrors/extern_struct.zc new file mode 100644 index 0000000..52d9ecc --- /dev/null +++ b/tests/langerrors/extern_struct.zc @@ -0,0 +1,3 @@ +// expect: isn't a zc struct +extern int stat2(struct stat s); +fn main() { return 0; } diff --git a/tests/langerrors/extern_type.zc b/tests/langerrors/extern_type.zc new file mode 100644 index 0000000..c0655ba --- /dev/null +++ b/tests/langerrors/extern_type.zc @@ -0,0 +1,3 @@ +// expect: isn't a c type extern knows +extern widget frob(int); +fn main() { return 0; } diff --git a/tests/langerrors/fixed_nolen.zc b/tests/langerrors/fixed_nolen.zc new file mode 100644 index 0000000..b79c31e --- /dev/null +++ b/tests/langerrors/fixed_nolen.zc @@ -0,0 +1,2 @@ +// expect: a fixed array needs its length +fn main() { fixed []int f = new(fixed []int); free(d*f); return 0; } diff --git a/tests/langerrors/fixed_push.zc b/tests/langerrors/fixed_push.zc new file mode 100644 index 0000000..a19bcb4 --- /dev/null +++ b/tests/langerrors/fixed_push.zc @@ -0,0 +1,2 @@ +// expect: a fixed array can't grow or shrink +fn main() { fixed []int f = new(fixed []int, 3); push(f, 1); free(d*f); return 0; } diff --git a/tests/langerrors/fixed_to_dyn.zc b/tests/langerrors/fixed_to_dyn.zc new file mode 100644 index 0000000..483f62f --- /dev/null +++ b/tests/langerrors/fixed_to_dyn.zc @@ -0,0 +1,4 @@ +// expect: type error: argument: expected []int, got fixed []int +typecheck on; +int take([]int.m g) { return len(g); } +fn main() { fixed []int f = new(fixed []int, 3); take(f); free(d*f); return 0; } diff --git a/tests/langerrors/gen_undeclared.zc b/tests/langerrors/gen_undeclared.zc new file mode 100644 index 0000000..ef85a7b --- /dev/null +++ b/tests/langerrors/gen_undeclared.zc @@ -0,0 +1,3 @@ +// expect: T: isn't a type +T first(int* l) { return l[0]; } +fn main() { return 0; } diff --git a/tests/langerrors/o3_addr.zc b/tests/langerrors/o3_addr.zc new file mode 100644 index 0000000..9ff029f --- /dev/null +++ b/tests/langerrors/o3_addr.zc @@ -0,0 +1,2 @@ +// expect: an o3 variable has no address +fn main() { o3 int z = 2; int* q = &z; return *q; } diff --git a/tests/langerrors/o3_asm.zc b/tests/langerrors/o3_asm.zc new file mode 100644 index 0000000..c8afb44 --- /dev/null +++ b/tests/langerrors/o3_asm.zc @@ -0,0 +1,2 @@ +// expect: asm can't reach an o3 variable +fn main() { o3 int zz = 2; asm { zz @ _ } return 0; } diff --git a/tests/langerrors/o3_let.zc b/tests/langerrors/o3_let.zc new file mode 100644 index 0000000..d2aff9a --- /dev/null +++ b/tests/langerrors/o3_let.zc @@ -0,0 +1,2 @@ +// expect: o3 variables need a type +fn main() { o3 { let x = 1; x++; } return 0; } diff --git a/tests/langerrors/o3_param.zc b/tests/langerrors/o3_param.zc new file mode 100644 index 0000000..5694720 --- /dev/null +++ b/tests/langerrors/o3_param.zc @@ -0,0 +1,3 @@ +// expect: o3 parameters need a type +o3 int f(x) { return x; } +fn main() { return f(1); } diff --git a/tests/langerrors/parseword_noread.zc b/tests/langerrors/parseword_noread.zc new file mode 100644 index 0000000..e7c5b1a --- /dev/null +++ b/tests/langerrors/parseword_noread.zc @@ -0,0 +1,5 @@ +// expect: a parse word has to read its own name +// a parse word that never calls next() would be called on the same token forever +immediate fn pw() { return 0; } +pw +fn main() { return 0; } diff --git a/tests/langerrors/pk_addr.zc b/tests/langerrors/pk_addr.zc new file mode 100644 index 0000000..17ada12 --- /dev/null +++ b/tests/langerrors/pk_addr.zc @@ -0,0 +1,6 @@ +// expect: has no address +fn main() { + u8 buf[8]; + u8* p = &buf[2]; + return 0; +} diff --git a/tests/langerrors/tagenum_args.zc b/tests/langerrors/tagenum_args.zc new file mode 100644 index 0000000..4d1850f --- /dev/null +++ b/tests/langerrors/tagenum_args.zc @@ -0,0 +1,3 @@ +// expect: wrong number of arguments +enum shape { circle { int r; }, dot }; +fn main() { shape* s = shape.circle(1, 2); free(d*s); return 0; } diff --git a/tests/langerrors/tagenum_bind.zc b/tests/langerrors/tagenum_bind.zc new file mode 100644 index 0000000..9fcb9bf --- /dev/null +++ b/tests/langerrors/tagenum_bind.zc @@ -0,0 +1,10 @@ +// expect: a case binds every field of its member +enum shape { circle { int r; }, dot }; +fn f(shape* s) { + switch (s) { + case circle(a, b): return 1; + case dot: return 2; + } + return 0; +} +fn main() { return 0; } diff --git a/tests/langerrors/tagenum_value.zc b/tests/langerrors/tagenum_value.zc new file mode 100644 index 0000000..dbd807e --- /dev/null +++ b/tests/langerrors/tagenum_value.zc @@ -0,0 +1,3 @@ +// expect: a tagged enum's members take no explicit values +enum shape { circle { int r; }, dot = 5 }; +fn main() { return 0; } diff --git a/tests/programs/alias.zc b/tests/programs/alias.zc new file mode 100644 index 0000000..2ebc3e5 --- /dev/null +++ b/tests/programs/alias.zc @@ -0,0 +1,21 @@ +// a pointer computed from a live object is a borrow of it: fine while the owner lives +struct node { int v; node* next; }; +fn main() { + let p = new(4); + p[0] = 11; p[1] = 22; + let q = p + 1; + put_n(q[-1]); put_c(' '); + put_n(q[0]); put_c(' '); + node* a = new(node), b = new(node); + a.v = 3; b.v = 4; a.next = b; + node* c = a.next; + put_n(c.v); put_c(' '); + int** list = new(int*, 2); + int* item = new(int); + *item = 8; + list[1] = item; + int* e = list[1]; + put_n(*e); put_c(10); + free(d*p); free(d*a); free(d*list); + return 0; +} diff --git a/tests/programs/allocfast.zc b/tests/programs/allocfast.zc new file mode 100644 index 0000000..5ef2df2 --- /dev/null +++ b/tests/programs/allocfast.zc @@ -0,0 +1,45 @@ +// repeated allocations of a size known at compile time: new(T) of a struct of +// numbers, and new(n) with a constant n. each pass of the loop makes an object +// and frees it at the end of the block, so the allocator should hand back the +// block it just took. the sums prove the objects were zeroed every time. +struct point { int x, y; }; +struct line { point* a; int len; }; + +fn loop_leaf(int n) { + let total = 0; + for (int i = 0; i < n; i++) { + point* p = new(point); + total += p.x + p.y; // zero from the allocator, every pass + p.x = i; p.y = 2; + } + return total; +} +fn loop_words(int n) { + let total = 0, i = 0; + while (i < n) { + let w = new(6); + total += w[0] + w[5]; + w[0] = 7; w[5] = 9; + i++; + } + return total; +} +fn loop_owner(int n) { + let i = 0, total = 0; + while (i < n) { + line* l = new(line); + l.a = new(point); + l.a.x = i; + total += l.a.x + l.len; + i++; + } + return total; +} +fn main() { + let before = live_objects(); + put_n(loop_leaf(50000)); put_c(' '); + put_n(loop_words(50000)); put_c(' '); + put_n(loop_owner(1000)); put_c(' '); + put_n(live_objects() - before); put_c(10); + return 0; +} diff --git a/tests/programs/annotate.expected b/tests/programs/annotate.expected new file mode 100644 index 0000000..553353e --- /dev/null +++ b/tests/programs/annotate.expected @@ -0,0 +1,33 @@ +// zclsp --annotate writes the types the compiler can see into a program that +// has none. this one has none: locals take the type of what they start as, a +// parameter takes the type every call gives it, and a function the type of +// what it returns. what it can't tell it leaves alone (twice is called with +// a plain word, so its parameter stays), and the program still means the +// same thing: tests/test.sh runs both and compares. +include "src/lib/std.zc"; +struct point { int x, y; }; +int calls = 0; +char* name = "annotate"; +int table[4]; +int add(int a, int b) { calls = calls + 1; return a + b; } +point* scale(point* p, int k) { p.x = p.x * k; p.y = p.y * k; return p; } +point*.u fresh(int x, int y) { + point* p = new(point); + p.x = x; p.y = y; + return u*p; +} +int twice(int v) { return v + v; } +fn len3(char* s) { return len_w(s) + 3; } +fn half(f) { return f / 2.0; } +int main() { + int i = 0, sum = 0, buf[6]; + let word_ = table, text = "abc"; + point* pt = fresh(2, 3); + let r = 1.5; + while (i < 4) { table[i] = add(i, 10); sum = sum + table[i]; i++; } + scale(pt, 3); + buf[0] = twice(sum); + put_n(sum); put_c(32); put_n(pt.x + pt.y); put_c(32); put_n(buf[0]); put_c(32); + put_n(len3(text)); put_c(32); put_n(calls); put_c(32); put_n(half(r) * 4); put_c(10); + return 0; +} diff --git a/tests/programs/annotate.zc b/tests/programs/annotate.zc new file mode 100644 index 0000000..75dad97 --- /dev/null +++ b/tests/programs/annotate.zc @@ -0,0 +1,33 @@ +// zclsp --annotate writes the types the compiler can see into a program that +// has none. this one has none: locals take the type of what they start as, a +// parameter takes the type every call gives it, and a function the type of +// what it returns. what it can't tell it leaves alone (twice is called with +// a plain word, so its parameter stays), and the program still means the +// same thing: tests/test.sh runs both and compares. +include "src/lib/std.zc"; +struct point { int x, y; }; +let calls = 0; +let name = "annotate"; +let table[4]; +fn add(a, b) { calls = calls + 1; return a + b; } +fn scale(p, k) { p.x = p.x * k; p.y = p.y * k; return p; } +.u fresh(x, y) { + let p = new(point); + p.x = x; p.y = y; + return u*p; +} +fn twice(v) { return v + v; } +fn len3(s) { return len_w(s) + 3; } +fn half(f) { return f / 2.0; } +fn main() { + let i = 0, sum = 0, buf[6]; + let word_ = table, text = "abc"; + let pt = fresh(2, 3); + let r = 1.5; + while (i < 4) { table[i] = add(i, 10); sum = sum + table[i]; i++; } + scale(pt, 3); + buf[0] = twice(sum); + put_n(sum); put_c(32); put_n(pt.x + pt.y); put_c(32); put_n(buf[0]); put_c(32); + put_n(len3(text)); put_c(32); put_n(calls); put_c(32); put_n(half(r) * 4); put_c(10); + return 0; +} diff --git a/tests/programs/arena.zc b/tests/programs/arena.zc new file mode 100644 index 0000000..5156b80 --- /dev/null +++ b/tests/programs/arena.zc @@ -0,0 +1,16 @@ +// the bump arena: interior pointers, a rewind, and a full bump. +fn main() { + let a = arena_new(32), p, q, r; + p = arena_alloc(a, 4); + p[0] = 7; + q = arena_alloc(a, 4); + put_n(q - p); put_c(' '); + put_n(p[0]); put_c(' '); + put_n(q[0]); put_c(' '); + arena_reset(a); + r = arena_alloc(a, 4); + put_n(r == p); put_c(' '); + put_n(arena_alloc(a, 100) == 0); put_c(10); + free(d*a); + return 0; +} diff --git a/tests/programs/args.zc b/tests/programs/args.zc new file mode 100644 index 0000000..aac72ec --- /dev/null +++ b/tests/programs/args.zc @@ -0,0 +1,16 @@ +// the command line and the environment, as the host hands them over. argv[0] +// is the program path, and when the source came from stdin, so argc is +// 1 even with nothing on the command line. run through a shell with no extra +// arguments, which is how test.sh calls it, so the line is fixed. +include "src/lib/std.zc"; + +fn main() { + put_n(argc); put_c(32); put_s(argv[0]); + put_c(32); put_c(32); put_s(argv[argc - 1]); + // envc counts every entry, envp holds NAME=value strings + put_c(32); put_n(envc > 0); + let hit = 0; + for (int i = 0; i < envc; i++) if (eq_w(envp[i], "ZC_ARG_TEST=ok")) hit = 1; + put_c(32); put_n(hit); + return 0; +} \ No newline at end of file diff --git a/tests/programs/arrays.zc b/tests/programs/arrays.zc new file mode 100644 index 0000000..a8a12bf --- /dev/null +++ b/tests/programs/arrays.zc @@ -0,0 +1,23 @@ +// growable arrays and fixed ones +struct point { int x, y; }; +fn main() { + let before = live_objects(); + { + []int a = new([]int); + for (int i = 0; i < 100; i++) push(a, i * i); + put_n(len(a)); put_c(' '); put_n(cap(a)); put_c(' '); put_n(a[99]); put_c(' '); + put_n(pop(a)); put_c(' '); put_n(len(a)); put_c(' '); + resize(a, 3); put_n(len(a)); put_c(' '); put_n(a[2]); put_c(' '); + fixed []int f = new(fixed []int, 5); + f[4] = 7; put_n(f[4] + len(f)); put_c(' '); + []point ps = new([]point, 2); + ps[1].y = 9; resize(ps, 10); put_n(ps[1].y + len(ps)); put_c(' '); + [8]int st; put_n(len(st)); put_c(' '); + []point* owned = new([]point*); + for (int i = 0; i < 20; i++) { point* p = new(point); p.x = i; push(owned, d*p); } + put_n(owned[19].x); put_c(' '); + put_n(live_objects() - before); put_c(10); + } + put_n(live_objects() - before); put_c(10); + return 0; +} diff --git a/tests/programs/backtick.zc b/tests/programs/backtick.zc new file mode 100644 index 0000000..3610f70 --- /dev/null +++ b/tests/programs/backtick.zc @@ -0,0 +1,9 @@ +// a backtick ends the source only where the lexer would read it as a token. +// this one, in a line comment: ` does not end anything. +/* nor does this, in a block comment ` with a star / and a slash */ +fn main() { + let s = "a`b", t = "q\"`", c = '`', d = '\''; + put_n(len_w(s)); put_c(' '); put_n(len_w(t)); put_c(' '); put_n(c); put_c(' '); put_n(d); put_c(' '); + while (!eof()) put_c(get_c()); + return 0; +} diff --git a/tests/programs/bareexit.zc b/tests/programs/bareexit.zc new file mode 100644 index 0000000..905710e --- /dev/null +++ b/tests/programs/bareexit.zc @@ -0,0 +1,7 @@ +// exit(3) must stop the machine before the next print. +fn main() { + put_s("bye\n"); + syscall(7, 3, 0, 0); + put_s("still\n"); + return 0; +} diff --git a/tests/programs/bareio.zc b/tests/programs/bareio.zc new file mode 100644 index 0000000..6da50b1 --- /dev/null +++ b/tests/programs/bareio.zc @@ -0,0 +1,28 @@ +// bare-metal syscalls: clock, a RAM file, random, and the machine size. +// zc0 subset, so a run-only image can carry it. names stay off u/m/d. +fn main() { + let buf[16], n, t0, t1, i, fd, r, board[8]; + t0 = syscall(6, 0, 0, 0); + i = 0; + while (i < 20000) i = i + 1; + t1 = syscall(6, 0, 0, 0); + if (t0 >= 0 && t1 >= t0) put_s("tick "); + else put_s("notick "); + fd = syscall(1, "note", 1, 0); + n = syscall(4, fd, "zy", 2); + syscall(2, fd, 0, 0); + fd = syscall(1, "note", 0, 0); + n = syscall(3, fd, buf, 8); + syscall(2, fd, 0, 0); + buf[n] = 0; + put_n(n); put_c(' '); put_s(buf); put_c(' '); + r = syscall(8, 0, 0, 0); + if (r != -1) put_s("rand "); + else put_s("norand "); + n = syscall(5, "BOARD", board, 8); + if (n > 0) put_s("board "); + else put_s("noboard "); + put_n(syscall(9, 0, 0, 0)); + put_c(10); + return 0; +} diff --git a/tests/programs/basics.zc b/tests/programs/basics.zc new file mode 100644 index 0000000..9865527 --- /dev/null +++ b/tests/programs/basics.zc @@ -0,0 +1,21 @@ +let table[10]; +let greeting = "hey"; +const N = 10, BIG = 0x100 * 2; +fn fib(n) { if (n < 2) return n; return fib(n - 1) + fib(n - 2); } +fn twice(f, x) { return f(f(x)); } +fn inc(x) { return x + 1; } +fn main() { + let i, s = 0, a[5]; + for (i = 0; i < N; i++) table[i] = i * i; + for (let j = 0; j < N; j++) s += table[j]; + put_n(s); put_c(' '); put_n(fib(15)); put_c(' '); put_n(twice(inc, 40)); put_c(' '); put_n(BIG); put_c(10); + put_s(greeting); put_c(' '); put_n('A'); put_c(' '); put_n(-17 / 5); put_c(' '); put_n(-17 % 5); put_c(' '); put_n(1 << 10); put_c(' '); put_n(-64 >> 3); put_c(10); + i = 0; while (1) { i++; if (i == 3) continue; if (i > 6) break; put_n(i); } put_c(10); + a[0] = 5; a[1] = a[0]++; a[2] = ++a[0]; put_n(a[0]); put_n(a[1]); put_n(a[2]); put_c(10); + let b = { let t = 3; t * t }; + put_n(b); put_c(' '); put_n(3 > 2 && 2 > 1); put_n(0 || 0); put_n(!5); put_n(~0); put_n(5 != 5); put_n(4 >= 4); put_c(10); + let x = asm { 2 3 + }; + put_n(x); put_c(10); + asm { 'o' . 'k' . 10 . } + return 0; +} diff --git a/tests/programs/bench.zc b/tests/programs/bench.zc new file mode 100644 index 0000000..7d34ead --- /dev/null +++ b/tests/programs/bench.zc @@ -0,0 +1,29 @@ +// four kernels with and without o3; bench.sh runs each one on each backend +int csum(int lim) { int s = 0; for (int n = 1; n < lim; n++) { int x = n; while (x != 1) { if (x & 1) x = 3 * x + 1; else x = x / 2; s++; } } return s; } +o3 int csum3(int lim) { int s = 0; for (int n = 1; n < lim; n++) { int x = n; while (x != 1) { if (x & 1) x = 3 * x + 1; else x = x / 2; s++; } } return s; } +int sieve(fixed []int f) { int c = 0, n = len(f); for (int i = 0; i < n; i++) f[i] = 1; for (int i = 2; i < n; i++) if (f[i]) { c++; for (int j = i + i; j < n; j += i) f[j] = 0; } return c; } +o3 int sieve3(fixed []int f) { int c = 0, n = len(f); for (int i = 0; i < n; i++) f[i] = 1; for (int i = 2; i < n; i++) if (f[i]) { c++; for (int j = i + i; j < n; j += i) f[j] = 0; } return c; } +int digits(int n) { int s = 0; for (int i = 0; i < n; i++) { int x = i; while (x) { s = s + x % 10; x = x / 10; } } return s; } +o3 int digits3(int n) { int s = 0; for (int i = 0; i < n; i++) { int x = i; while (x) { s = s + x % 10; x = x / 10; } } return s; } +int fib(int n) { if (n < 2) return n; return fib(n - 1) + fib(n - 2); } +o3 int fib3(int n) { if (n < 2) return n; return fib3(n - 1) + fib3(n - 2); } +// input: a letter (the kernel; z runs none) and how many times to run it +fn main() { + fixed []int f = new(fixed []int, 50000); + let which = get_c(), reps = 0, ch = get_c(), r = 0; + while (ch >= '0' && ch <= '9') { reps = reps * 10 + ch - '0'; ch = get_c(); } + while (reps > 0) { + if (which == 'a') r = csum(30000); + if (which == 'b') r = csum3(30000); + if (which == 'c') r = sieve(f); + if (which == 'd') r = sieve3(f); + if (which == 'e') r = digits(300000); + if (which == 'f') r = digits3(300000); + if (which == 'g') r = fib(22); + if (which == 'h') r = fib3(22); + reps--; + } + put_n(r); put_c(10); + free(d*f); + return 0; +} diff --git a/tests/programs/bench_bytes.zc b/tests/programs/bench_bytes.zc new file mode 100644 index 0000000..cb878f6 --- /dev/null +++ b/tests/programs/bench_bytes.zc @@ -0,0 +1,19 @@ +fn sumbytes(n) { + let buf[64], i = 0, s = 0, b, reps, ch; + while (i < 64) { buf[i] = i * 0x0101010101010101; i++; } + b = addr_b(buf); + ch = get_c(); + reps = 0; + while (ch >= '0' && ch <= '9') { reps = reps * 10 + ch - '0'; ch = get_c(); } + while (reps > 0) { + i = 0; s = 0; + while (i < n) { s = s + ld8(b + (i & 511)); i++; } + reps--; + } + return s; +} +fn main() { + put_n(sumbytes(4000)); + put_c(10); + return 0; +} diff --git a/tests/programs/bench_share.zc b/tests/programs/bench_share.zc new file mode 100644 index 0000000..6fa478f --- /dev/null +++ b/tests/programs/bench_share.zc @@ -0,0 +1,30 @@ +// walk one shared spine from two parents. the checksum is one repetition. +fn build() { + let i = 0, spine = 0; + while (i < 40) { spine = cons(i, spine); i++; } + return cons(cons(spine, 1), cons(spine, 2)); +} +fn sum(t) { + let a = 0; + while (t) { a = a + car(t); t = cdr(t); } + return a; +} +fn work() { + let ch = get_c(), reps = 0, acc = 0, root, spine, k; + while (ch >= '0' && ch <= '9') { reps = reps * 10 + ch - '0'; ch = get_c(); } + root = build(); + spine = car(car(root)); + while (reps > 0) { + k = 0; + acc = 0; + while (k < 200) { acc = acc + sum(spine); k++; } + acc = acc + cdr(car(root)) + cdr(cdr(root)); + reps--; + } + return acc; +} +fn main() { + put_n(work()); + put_c(10); + return 0; +} diff --git a/tests/programs/bench_strings.zc b/tests/programs/bench_strings.zc new file mode 100644 index 0000000..455cff9 --- /dev/null +++ b/tests/programs/bench_strings.zc @@ -0,0 +1,27 @@ +fn work() { + let s = b"abcdefghijklmnopqrstuvwxyz0123456789"; + let i, acc, n, t, part, k, reps, ch; + ch = get_c(); + reps = 0; + while (ch >= '0' && ch <= '9') { reps = reps * 10 + ch - '0'; ch = get_c(); } + while (reps > 0) { + k = 0; acc = 0; n = len_s(s); + while (k < 200) { + i = 0; + while (i < n) { acc = acc + at_s(s, i); i++; } + k++; + } + t = s + b"!"; + part = t[1:8]; + acc = acc + len_s(t) + len_s(part) + at_s(part, 0); + free(part); + free(t); + reps--; + } + return acc; +} +fn main() { + put_n(work()); + put_c(10); + return 0; +} diff --git a/tests/programs/bitops.expected b/tests/programs/bitops.expected new file mode 100644 index 0000000..e93b905 --- /dev/null +++ b/tests/programs/bitops.expected @@ -0,0 +1,2 @@ +63 64 3 64 8 64 0 1 2 1 12345 -1 4 1069547520 1 1065353216 8388608 +5470960040408376558 diff --git a/tests/programs/bitops.zc b/tests/programs/bitops.zc new file mode 100644 index 0000000..97817b9 --- /dev/null +++ b/tests/programs/bitops.zc @@ -0,0 +1,26 @@ +// bit tricks and float32 conversion as micro-ops +fn show(v) { put_n(v); put_c(' '); } +fn once() { + show(clz(1)); show(clz(0)); show(ctz(8)); show(ctz(0)); + show(popcnt(255)); show(popcnt(-1)); show(popcnt(0)); + show(bswap(1) == 72057594037927936); + show(rotl(1, 65)); show(rotr(1, 1) < 0); show(rotl(rotr(12345, 7), 7)); + show(mulhi(-1, 5)); show(umulhi(-1, 5)); + show(f2s(0x3ff8000000000000)); show(s2f(0x3fc00000) == 0x3ff8000000000000); + show(f2s(0x3ff0000000000000)); show(f2s(0x3810000000000000)); + put_c(10); +} +// the same in a loop, so o3 lifts and fuses them +o3 int sums(int n) { + int a = 0; + for (int i = 0; i < n; i++) { + a += popcnt(i) + ctz(i + 1) + clz(i + 1); + a ^= rotl(a, 3) + bswap(i) + f2s(s2f(i)); + } + return a; +} +int main() { + once(); + put_n(sums(1000)); put_c(10); + return 0; +} diff --git a/tests/programs/borrowmove.zc b/tests/programs/borrowmove.zc new file mode 100644 index 0000000..b0f0e42 --- /dev/null +++ b/tests/programs/borrowmove.zc @@ -0,0 +1,2 @@ +fn put(.m box, int v) { int* c = new(int); *c = v; box[0] = c; return 0; } +fn main() { let b = new(1); put(b, 6); put_n(*(b[0] as int*)); put_c(10); return 0; } diff --git a/tests/programs/bounds.zc b/tests/programs/bounds.zc new file mode 100644 index 0000000..42c3997 --- /dev/null +++ b/tests/programs/bounds.zc @@ -0,0 +1,28 @@ +// a +1 loop whose bound is len of the fixed array: the check is gone, the sum is not +fn acc(fixed []int f) { + int n = len(f), i = 0, s = 0; + for (; i < n; i += 1) f[i] = i + 1; + for (i = 0; i < n; i++) s = s + f[i]; + return s; +} +o3 fn acc3(fixed []int f) { + int n = len(f), i = 0, s = 0; + for (; i < n; i++) f[i] = i + 1; + for (i = 0; i < n; i++) s += f[i]; + return s; +} +o3 fn small(fixed []int f) { + int n = len(f), c = 0, i, j; + for (i = 2; i < n; i++) f[i] = 1; + for (i = 2; i * i < n; i++) + if (f[i]) for (j = i * i; j < n; j += i) f[j] = 0; + for (i = 2; i < n; i++) c += f[i]; + return c; +} +fn main() { + fixed []int f = new(fixed []int, 10); + fixed []int g = new(fixed []int, 20); + put_n(acc(f)); put_c(32); put_n(acc3(f)); put_c(32); put_n(small(g)); put_c(10); + free(d*f); free(d*g); + return 0; +} diff --git a/tests/programs/bounds_hi.zc b/tests/programs/bounds_hi.zc new file mode 100644 index 0000000..d61179d --- /dev/null +++ b/tests/programs/bounds_hi.zc @@ -0,0 +1,8 @@ +// the index is not the induction variable. i + 1 walks off the end. +o3 fn main() { + fixed []int f = new(fixed []int, 4); + int n = len(f), i; + for (i = 0; i < n; i++) f[i + 1] = 1; + free(d*f); + return 0; +} diff --git a/tests/programs/bounds_lo.zc b/tests/programs/bounds_lo.zc new file mode 100644 index 0000000..896d864 --- /dev/null +++ b/tests/programs/bounds_lo.zc @@ -0,0 +1,8 @@ +// i starts negative. i < len is true for the first pass, and the index is still illegal. +fn main() { + fixed []int f = new(fixed []int, 4); + int n = len(f), i; + for (i = -1; i < n; i++) f[i] = 1; + free(d*f); + return 0; +} diff --git a/tests/programs/bstr_io.zc b/tests/programs/bstr_io.zc new file mode 100644 index 0000000..9cf7ca9 --- /dev/null +++ b/tests/programs/bstr_io.zc @@ -0,0 +1,22 @@ +fn main() { + let path = b"/tmp/zc-bstr.txt"; + let msg = b"byte-io"; + let fd = bopen(path, 1); + let n = bwrite(fd, msg, len_s(msg)); + syscall(SYS_CLOSE, fd, 0, 0); + fd = bopen(path, 0); + let buf = new_s(7); + let g = bread(fd, buf, 7); + syscall(SYS_CLOSE, fd, 0, 0); + let name = b"ZC_BSTR"; + let envb = new_s(16); + let k = bgetenv(name, envb, 16); + put_s(buf); put_c(32); + put_n(n); put_c(32); + put_n(g); put_c(32); + put_n(buf == msg); put_c(32); + put_n(k); put_c(32); + put_n(at_s(envb, 0)); put_c(32); + put_n(at_s(envb, 5)); put_c(10); + return 0; +} diff --git a/tests/programs/buddy.zc b/tests/programs/buddy.zc new file mode 100644 index 0000000..ab351d2 --- /dev/null +++ b/tests/programs/buddy.zc @@ -0,0 +1,16 @@ +// the buddy allocator: 20000 small blocks, freed, merge back into one big one +let keep[20000]; +fn main() { + let i = 0, p; + while (i < 20000) { keep[i] = new(2); i++; } // 20000 8-word blocks + put_n(largest_free()); put_c(' '); + i = 0; while (i < 20000) { free(keep[i]); keep[i] = 0; i++; } + scrub(); // stale stack words would keep the quarantined blocks + gc(); + put_n(largest_free()); put_c(' '); + p = new(200000); // only possible if the small blocks merged back + put_n(osize(p) >= 200000); put_c(' '); + free(d*p); + put_n(live_objects()); put_c(10); + return 0; +} diff --git a/tests/programs/bytes.zc b/tests/programs/bytes.zc new file mode 100644 index 0000000..b4e1079 --- /dev/null +++ b/tests/programs/bytes.zc @@ -0,0 +1,44 @@ +fn sp() { put_c(32); return 0; } +fn litw(v) { return v + v + 1; } +fn f() { put_c('A'); return 0; } +fn sumbytes(n) { + let buf[64], i = 0, s = 0, b; + while (i < 64) { buf[i] = i * 0x0101010101010101; i++; } + b = addr_b(buf); + i = 0; + while (i < n) { s = s + ld8(b + (i & 511)); i++; } + return s; +} +fn main() { + let buf[3], b; + buf[0] = 0; buf[1] = 0; buf[2] = 0; + b = addr_b(buf); + st8(b, 0x11); + st8(b + 1, 0x22); + st8(b + 7, 0x7f); + st16(b + 7, 0xabcd); + st32(b + 10, 0x01020304); + put_n(ld8(b)); sp(); + put_n(ld8(b + 1)); sp(); + put_n(ld16(b + 7)); sp(); + put_n(ld32(b + 10)); sp(); + put_n(addr_w(b) == buf); sp(); + put_n(ld8(b + 7)); sp(); + put_n(ld8(b + 8)); sp(); + put_n(buf[0]); sp(); + put_n(buf[1]); sp(); + st8(0 - 1, 99); + put_n(ld8(0 - 1)); sp(); + put_n(ld16(addr_b(MW) - 1)); sp(); + put_n(sumbytes(1000)); + put_c(10); + let p = f; + f(); + while (*p != litw('A')) p++; + st8(addr_b(p), litw('B') & 255); + f(); + st8(addr_b(p), litw('C') & 255); + f(); + put_c(10); + return 0; +} diff --git a/tests/programs/cabi.expected b/tests/programs/cabi.expected new file mode 100644 index 0000000..89b168c --- /dev/null +++ b/tests/programs/cabi.expected @@ -0,0 +1,5 @@ +1.414214 1024.000000 4.000000 12 42 1234 1 2 3 5 7 9 +9 7 5 3 2 1 +1 2 3 5 7 9 9 7 5 3 2 1 +6 7:2.50 +5.000000 diff --git a/tests/programs/cabi.zc b/tests/programs/cabi.zc new file mode 100644 index 0000000..6a608ab --- /dev/null +++ b/tests/programs/cabi.zc @@ -0,0 +1,46 @@ +// native calls: extern declares a c function, cfunc hands a zc function to c +extern double sqrt(double); +extern double pow(double x, double y); +extern float sqrtf(float); +extern long c_strlen(str s) = "strlen"; +extern int c_abs(int x) = "abs"; +extern int c_atoi(str s) = "atoi"; +extern void qsort(word *base, long n, long size, void *cmp); +extern int snprintf2(word *buf, long cap, str fmt, long a, double b) = "snprintf"; +extern double hypot(double, double); + +fn put_f(x) { asm { x @ #1220 } return 0; } +fn sp() { put_c(32); return 0; } + +int cmp(int* a, int* b) { return *a - *b; } +// a callback that makes a native call of its own, and an o3 one +int cmpdesc(int* a, int* b) { return c_abs(*b - *a) * (*b - *a) / c_abs(*b - *a + (*b == *a)); } +o3 int cmpo3(int* a, int* b) { int x = *a; int y = *b; if (x < y) return 1; if (x > y) return -1; return 0; } + +fn main() { + let a[6]; + let i = 0; + put_f(sqrt(2.0)); sp(); + put_f(pow(2.0, 10.0)); sp(); + put_f(sqrtf(16.0)); sp(); + put_n(c_strlen(b"hello, world")); sp(); + put_n(c_abs(0 - 42)); sp(); + put_n(c_atoi(b"1234xyz")); sp(); + a[0] = 5; a[1] = 3; a[2] = 9; a[3] = 1; a[4] = 7; a[5] = 2; + qsort(a, 6, 8, cfunc(cmp)); + while (i < 6) { put_n(a[i]); sp(); i++; } + put_c(10); + qsort(a, 6, 8, cfunc(cmpdesc)); + i = 0; while (i < 6) { put_n(a[i]); sp(); i++; } + put_c(10); + qsort(a, 6, 8, cfunc(cmp)); + i = 0; while (i < 6) { put_n(a[i]); sp(); i++; } + qsort(a, 6, 8, cfunc(cmpo3)); + i = 0; while (i < 6) { put_n(a[i]); sp(); i++; } + put_c(10); + put_n(snprintf2(a, 48, b"%ld:%.2f", 7, 2.5)); sp(); + i = 0; while (i < 6) { put_c(ld8(addr_b(a) + i)); i++; } + put_c(10); + put_f(hypot(3.0, 4.0)); put_c(10); + return 0; +} diff --git a/tests/programs/cabi_o3.expected b/tests/programs/cabi_o3.expected new file mode 100644 index 0000000..6842510 --- /dev/null +++ b/tests/programs/cabi_o3.expected @@ -0,0 +1,2 @@ +921.462947 1.414214 2500 +11 1-2-3-4-5-6 diff --git a/tests/programs/cabi_o3.zc b/tests/programs/cabi_o3.zc new file mode 100644 index 0000000..5c9a55a --- /dev/null +++ b/tests/programs/cabi_o3.zc @@ -0,0 +1,30 @@ +// native calls from o3 code: lifted into the region as micro-ops, called directly by llvm +extern double sqrt(double); +extern double pow(double, double); +extern double fma(double, double, double); +extern int snprintf9(word *buf, long cap, str fmt, long a, long b, long c, long d, long e, long f) = "snprintf"; +extern long labs(long); + +fn put_f(x) { asm { x @ #1220 } return 0; } + +o3 float norm(int n) { + float acc = 0.0; + for (int i = 1; i <= n; i++) acc = acc + sqrt(i * 1.0) + fma(1.0, 2.0, 0.5); + return acc; +} +o3 int absum(int n) { + int t = 0; + for (int i = 0; i < n; i++) t = t + labs(i - 50); + return t; +} +fn main() { + let buf[16]; + let i = 0; + put_f(norm(100)); put_c(32); + put_f(pow(2.0, 0.5)); put_c(32); + put_n(absum(100)); put_c(10); + put_n(snprintf9(buf, 100, b"%ld-%ld-%ld-%ld-%ld-%ld", 1, 2, 3, 4, 5, 6)); put_c(32); + while (i < 11) { put_c(ld8(addr_b(buf) + i)); i++; } + put_c(10); + return 0; +} diff --git a/tests/programs/cabi_sized.expected b/tests/programs/cabi_sized.expected new file mode 100644 index 0000000..9be5bf0 --- /dev/null +++ b/tests/programs/cabi_sized.expected @@ -0,0 +1 @@ +1.414214 1 5.000000 77 65 5 1234 2.500000 diff --git a/tests/programs/cabi_sized.zc b/tests/programs/cabi_sized.zc new file mode 100644 index 0000000..d1dc2ba --- /dev/null +++ b/tests/programs/cabi_sized.zc @@ -0,0 +1,28 @@ +// extern and cfunc with f32 and the sized integers +extern f32 sqrtf(f32); +extern f64 hypot(f64, f64); +extern i32 abs(i32); +extern u8 toupper(u8); +extern long labs(long); +fn put_f(x) { asm { x @ #1220 } return 0; } +fn sp() { put_c(32); return 0; } +extern void qsort(word *base, long n, long size, void *cmp); +i32 cmpi(int* a, int* b) { return *a - *b; } +f32 half(f32 v) { return v / 2; } +fn main() { + f32 r = sqrtf(2.0); + put_f(r); sp(); + put_n(r == sqrtf(2.0)); sp(); + put_f(hypot(3.0, 4.0)); sp(); + put_n(abs(0 - 77)); sp(); + put_n(toupper(97)); sp(); + i32 big = 0 - 5; + put_n(abs(big)); sp(); + int v[4]; + v[0] = 4; v[1] = 1; v[2] = 3; v[3] = 2; + qsort(v, 4, 8, cfunc(cmpi)); + put_n(v[0]); put_n(v[1]); put_n(v[2]); put_n(v[3]); sp(); + put_f(half(5.0)); + put_c(10); + return 0; +} diff --git a/tests/programs/capture.zc b/tests/programs/capture.zc new file mode 100644 index 0000000..c178791 --- /dev/null +++ b/tests/programs/capture.zc @@ -0,0 +1,31 @@ +// capture.start() sends everything the program writes into a string instead of +// the terminal, and buf.end() stops it and terminates the string. nothing may +// be printed during a capture, or it lands in the buffer too. +include "src/lib/std.zc"; + +fn main() { + let buf = capture.start(); + put_s("hello world!"); + put_n(42); + put_c(10); + buf.end(); // stops the capture and terminates the string + + // the capture is off again, so these go to the terminal + put_n(outmode); put_c(10); // 0 + put_n(outbuf == 0); put_c(10); // 1, the buffer is handed over + put_n(len_w(buf)); put_c(10); // 15 + put_s(buf); // hello world!42 + + // a second capture does not disturb the first, and both read back + let two = capture.start(); + put_s("second"); + two.end(); + put_n(eq_w(buf, "hello world!42\n")); put_c(10); // 1 + put_n(eq_w(two, "second")); put_c(10); // 1 + + // capture_stdin returns an empty string, so an unused capture is empty + let three = capture.start(); + three.end(); + put_n(len_w(three)); put_c(10); // 0 + return 0; +} \ No newline at end of file diff --git a/tests/programs/closure.zc b/tests/programs/closure.zc new file mode 100644 index 0000000..660c32a --- /dev/null +++ b/tests/programs/closure.zc @@ -0,0 +1,52 @@ +// closures: calls through values, captures by value, state in a capture, +// nested captures, closures passed as arguments and returned from functions +fn apply(f, x) { return f(x); } +fn twice(g, x) { return g(g(x)); } +fn sq(x) { return x * x; } +word.u make(n) { + let h = fn(x) { return x * n; }; + return u*h; +} +fn main() { + let k = 10, m = 3; + let add = fn(x) { return x + k * m; }; + put_n(add(1)); put_c(10); + put_n(apply(add, 5)); put_c(10); + let none = fn() { return 42; }; + put_n(none()); put_c(10); + let n = 0; + let bump = fn(d) { n = n + d; return n; }; + put_n(bump(2)); put_c(10); + put_n(bump(5)); put_c(10); + put_n(n); put_c(10); + let a = 2; + let outer = fn(x) { + let inner = fn(y) { return y + a + x; }; + return inner(100); + }; + put_n(outer(7)); put_c(10); + let base = 5; + let scale = fn(i) { return i * base; }; + let s = 0, i = 0; + while (i < 4) { s = s + scale(i); i++; } + put_n(s); put_c(10); + let c = 3; + let plus = fn(v) { return v + c; }; + put_n(twice(plus, 1)); put_c(10); + put_n(sq(4)); put_c(10); + let total = 0, j = 0; + while (j < 3) { + let kk = j; + let g = fn(x) { return x + kk; }; + total = total + g(10); + j++; + } + put_n(total); put_c(10); + let f = make(4); + put_n(f(5)); put_c(10); + let h = 1; + let frozen = fn(x) { return x + h; }; + h = 100; + put_n(frozen(1)); put_c(10); + return 0; +} diff --git a/tests/programs/csyn.zc b/tests/programs/csyn.zc new file mode 100644 index 0000000..0099d1c --- /dev/null +++ b/tests/programs/csyn.zc @@ -0,0 +1,18 @@ +// do/while, enum constants, and a C cast +typecheck on; +enum color { RED, GREEN = 5, BLUE }; +fn main() { + int n = 0, i = 0; + do { + i++; + if (i == 2) continue; + n = n + i; + } while (i < 5); + int* p = (int*)0; + int z = (int)p; + put_n(n); put_c(' '); + put_n(RED); put_c(' '); put_n(GREEN); put_c(' '); + put_n(BLUE); put_c(' '); + put_n(z); put_c(10); + return 0; +} diff --git a/tests/programs/decl_undefined.zc b/tests/programs/decl_undefined.zc new file mode 100644 index 0000000..0ee9312 --- /dev/null +++ b/tests/programs/decl_undefined.zc @@ -0,0 +1,2 @@ +fn helper(); +fn main() { helper(); return 0; } diff --git a/tests/programs/discard_m.zc b/tests/programs/discard_m.zc new file mode 100644 index 0000000..35e9f3b --- /dev/null +++ b/tests/programs/discard_m.zc @@ -0,0 +1,3 @@ +// m* on an unnamed u result lets the value be discarded; the plain call is an error +.u mk(int v) { let p = new(1); p[0] = v; return u*p; } +fn main() { m*mk(7); m*mk(8); put_n(7); put_c(10); return 0; } diff --git a/tests/programs/doublefree.zc b/tests/programs/doublefree.zc new file mode 100644 index 0000000..296b194 --- /dev/null +++ b/tests/programs/doublefree.zc @@ -0,0 +1,9 @@ +memdebug on; +unchecked fn main() +{ + let p = new(1); + free(p); + free(p); + put_s("ok\n"); + return 0; +} diff --git a/tests/programs/enhance.zc b/tests/programs/enhance.zc new file mode 100644 index 0000000..327b4bd --- /dev/null +++ b/tests/programs/enhance.zc @@ -0,0 +1,28 @@ +// custom opcodes: recipes, lifted vm code, several outputs, and a fallback +enhance clampadd(a, b) micro "a = add a b; a = min a 100; a = max a 0"; +enhance cube(x) vm { 0 $ 0 $ * * }; +enhance 2 divmod(a, b) micro "r2 = sdiv a b" "\n r1 = srem a b\n r0 = r2"; +enhance div7(a) micro "a = sdiv a 7"; +enhance urem10(a) micro "a = urem a 10"; +enhance hi(a, b) micro "a = mulhu a b"; +enhance noisy(x) vm { 0 $ . } else soft_noisy; // i/o can't be lifted, so calls use the fallback +fn soft_noisy(x) { return x + 1; } +let qq, rr, seed = 7; +fn rnd() { seed = seed * 1103515245 + 12345; return seed ^ (seed >> 11); } +fn main() { + let i = 0, a, bad = 0; + put_n(clampadd(70, 50)); put_c(' '); put_n(clampadd(-50, 2)); put_c(' '); put_n(cube(7)); put_c(' '); + asm { 17 5 divmod rr ! qq ! } + put_n(qq); put_c(','); put_n(rr); put_c(' '); + put_n(noisy(4)); put_c(' '); + put_n(hi(-1, -1)); put_c(' '); + while (i < 5000) { + a = rnd(); + if (div7(a) != zdiv(a, 7)) bad++; + if (urem10(a) < 0 || urem10(a) > 9) bad++; + if (a / (i + 1) != zdiv(a, i + 1)) bad++; + i++; + } + put_n(bad); put_c(10); + return 0; +} diff --git a/tests/programs/enumscope.zc b/tests/programs/enumscope.zc new file mode 100644 index 0000000..5c6d7d0 --- /dev/null +++ b/tests/programs/enumscope.zc @@ -0,0 +1,9 @@ +// enum members are reached through the enum's name as well as plainly +enum color { RED, GREEN = 5, BLUE }; +fn main() { + int c = color.BLUE; + put_n(color.RED); put_c(' '); put_n(GREEN); put_c(' '); + put_n(c); put_c(' '); + switch (c) { case color.BLUE: put_s("blue"); break; default: put_s("other"); } + return 0; +} diff --git a/tests/programs/enumtype.zc b/tests/programs/enumtype.zc new file mode 100644 index 0000000..9a6e89b --- /dev/null +++ b/tests/programs/enumtype.zc @@ -0,0 +1,12 @@ +// enums are a type of their own in typed code: their members, and only those, take them; an enum still becomes an int +typecheck on; +enum color { RED, GREEN = 5, BLUE }; +int paint(color c) { return c; } +int main() { + color c = color.BLUE; + color d = RED; + int n = c; + put_n(n); put_c(32); put_n(paint(GREEN)); put_c(32); put_n(d == color.RED); + put_c(10); + return 0; +} diff --git a/tests/programs/f32.expected b/tests/programs/f32.expected new file mode 100644 index 0000000..8220b5f --- /dev/null +++ b/tests/programs/f32.expected @@ -0,0 +1 @@ +16777216.000000 16777217.000000 001 1.500000 1.000000 3.600000 10 7.800000 4950.000000 450 diff --git a/tests/programs/f32.zc b/tests/programs/f32.zc new file mode 100644 index 0000000..0f60dcd --- /dev/null +++ b/tests/programs/f32.zc @@ -0,0 +1,37 @@ +// f32: single precision values and packed arrays of them +struct vec3 { f32 x, y, z; }; +f32 gsum; +fn put_f(x) { asm { x @ #1220 } return 0; } +[8]f32 gbuf; + +f32 half(f32 v) { return v / 2; } + +fn main() { + gsum = 0.1; + f32 a = 16777216.0; + f32 b = a + 1.0; // rounds back to 16777216 in single precision + float c = 16777216.0 + 1.0; + f32 t = 0.1; + float u = 0.1; + put_f(b); put_c(' '); put_f(c); put_c(' '); + put_n(t == 0.1); put_n(t == u); put_n(t == (f32)u); put_c(' '); + put_f(half(3.0)); put_c(' '); + put_f(gsum * 10); put_c(' '); // 1.000000 (0.1f * 10 in double) + vec3 v; v.x = 1.5; v.y = 0.1; v.z = 2; + put_f(v.x + v.y + v.z); put_c(' '); + int i = 0; + gbuf[0] = 0.1; put_n(gbuf[0] == t); put_n(gbuf[0] == u); put_c(' '); + while (i < 8) { gbuf[i] = i * 0.25 + 0.1; i++; } + float s = 0; + i = 0; + while (i < 8) { s += gbuf[i]; i++; } + put_f(s); put_c(' '); + f32* p = new(f32, 100); + i = 0; + while (i < 100) { p[i] = i; i++; } + s = 0; i = 0; + while (i < 100) { s += p[i]; i++; } + put_f(s); put_c(' '); + put_n(sizeof(gbuf)); put_n(sizeof([100]f32)); put_c(10); + free(d*p); +} diff --git a/tests/programs/f32o3.zc b/tests/programs/f32o3.zc new file mode 100644 index 0000000..8f1ec2b --- /dev/null +++ b/tests/programs/f32o3.zc @@ -0,0 +1,14 @@ +// f32 packed arrays and rounding inside an o3 region +fn put_f(x) { asm { x @ #1220 } return 0; } +o3 float dot(f32* p, int n) { + float s = 0; + f32 acc = 0; + for (int i = 0; i < n; i++) { s += p[i] * p[i]; acc = acc + p[i]; p[i] = p[i] + 0.1; } + return s + acc; +} +fn main() { + f32* p = new(f32, 50); + for (int i = 0; i < 50; i++) p[i] = i * 0.5; + put_f(dot(p, 50)); put_c(' '); put_f(p[3]); put_c(' '); put_n(p[3] == 1.6); put_c(10); + free(d*p); +} diff --git a/tests/programs/fbench.zc b/tests/programs/fbench.zc new file mode 100644 index 0000000..1658ba7 --- /dev/null +++ b/tests/programs/fbench.zc @@ -0,0 +1,37 @@ +// float kernel, plain and o3. bench_float.sh times them. the letter picks the +// kernel and the digits are how many times to run it, same shape as bench.zc. +float poly(int n) { + float s = 0.0; + float x = 1.25; + int i = 0; + while (i < n) { + s = s + x * 1.5; + x = x * 0.5 + 1.0; + i = i + 1; + } + return s; +} +o3 float poly3(int n) { + float s = 0.0; + float x = 1.25; + int i = 0; + while (i < n) { + s = s + x * 1.5; + x = x * 0.5 + 1.0; + i = i + 1; + } + return s; +} +fn main() { + let which = get_c(), reps = 0, ch = get_c(), r = 0; + while (ch >= 48 && ch <= 57) { reps = reps * 10 + ch - 48; ch = get_c(); } + while (reps > 0) { + if (which == 97) r = (int)poly(20000); + if (which == 98) r = (int)poly3(20000); + if (which == 99) r = (int)poly(2000); + reps = reps - 1; + } + put_n(r); + put_c(10); + return 0; +} diff --git a/tests/programs/float.zc b/tests/programs/float.zc new file mode 100644 index 0000000..81b8668 --- /dev/null +++ b/tests/programs/float.zc @@ -0,0 +1,32 @@ +fn put_f(x) { asm { x @ #1220 } return 0; } +fn f2i(x) { return asm { x @ #1218 }; } +fn i2f(x) { return asm { x @ #1216 }; } +fn sp() { put_c(32); return 0; } + +fn main() { + let a = 1.5; + let b = 2.5; + put_f(a + b); sp(); + put_f(7.0 - 2.5); sp(); + put_f(1.5 * 2); sp(); + put_f(10.0 / 4.0); sp(); + put_f(1 + 1.5); sp(); + put_f(1.5e-3); sp(); + put_f(0.1); sp(); + put_f(-0.125); sp(); + put_f(1.0 / 0.0); sp(); + put_f(-1.0 / 0.0); sp(); + put_f(0.0 / 0.0); sp(); + put_n(1.5 < 2.0); sp(); + put_n(2.0 <= 2.0); sp(); + put_n(2.0 == 2); sp(); + put_n(1.5 == 1); sp(); + put_n(3.0 > 3.5); sp(); + put_n(f2i(3.9)); sp(); + put_n(f2i(-3.9)); sp(); + put_f(i2f(3)); sp(); + put_f(i2f(-2)); sp(); + put_n(10 / 4); + put_c(10); + return 0; +} diff --git a/tests/programs/fo3.zc b/tests/programs/fo3.zc new file mode 100644 index 0000000..593e88a --- /dev/null +++ b/tests/programs/fo3.zc @@ -0,0 +1,50 @@ +// o3 lifts float ops: the plain kernel and the o3 kernel agree, a constant +// float expression folds, x - x stays a NaN, and -0 is false inside o3. +fn put_f(x) { asm { x @ #1220 } return 0; } +fn sp() { put_c(32); return 0; } +float poly(int n) { + float s = 0.0; + float x = 1.25; + int i = 0; + while (i < n) { + s = s + x * 1.5; + x = x * 0.5 + 1.0; + i = i + 1; + } + return s; +} +o3 float poly3(int n) { + float s = 0.0; + float x = 1.25; + int i = 0; + while (i < n) { + s = s + x * 1.5; + x = x * 0.5 + 1.0; + i = i + 1; + } + return s; +} +o3 float folded() { return 1.5 + 2.5; } +o3 float subself(float x) { return x - x; } +o3 int zfalse() { + float z = 0.0 * -1.0; + int n = 0; + if (z) n = 1; + while (z) n = n + 2; + return n; +} +o3 float addtwo(float a) { return a + 2; } +o3 int below(float a, float b) { return a < b; } +fn main() { + put_n((int)poly(2000)); sp(); + put_n((int)poly3(2000)); sp(); + put_f(folded()); sp(); + put_f(subself(0.0 / 0.0)); sp(); + put_n(zfalse()); sp(); + put_f(addtwo(1.25)); sp(); + put_n(below(1.0, 2.0)); sp(); + put_n(below(2.0, 2.0)); sp(); + put_n(below(0.0 / 0.0, 1.0)); + put_c(10); + return 0; +} diff --git a/tests/programs/ftext.zc b/tests/programs/ftext.zc new file mode 100644 index 0000000..b1c0acf --- /dev/null +++ b/tests/programs/ftext.zc @@ -0,0 +1,37 @@ +fn sp() { put_c(32); return 0; } +fn pm(x, prec, mode) { asm { x @ prec @ mode @ #1236 } return 0; } +fn parsed(s) { return asm { s @ #1222 }; } +fn ok(a, b) { + let w = asm { a @ b @ ^ }; + if (w) put_n(0); else put_n(1); + return 0; +} + +fn main() { + let z = 0.0 * -1.0; + let hi = parsed("9007199254740994"); + let lo = parsed("9007199254740992"); + pm(1.0e308, 0, 0); sp(); + pm(1.0e-10, 0, 0); sp(); + pm(z, 0, 0); sp(); + pm(1.0 / 0.0, 0, 0); sp(); + pm(-1.0 / 0.0, 0, 0); sp(); + pm(0.0 / 0.0, 0, 0); sp(); + pm(0.1, 0, 0); sp(); + pm(1.5, 2, 1); sp(); + pm(1.25, 4, 2); sp(); + pm(1.0e20, 6, 1); sp(); + pm(1.0e40, 0, 0); sp(); + ok(parsed("1.5"), 3.0 / 2.0); + ok(parsed("0.1"), 1.0 / 10.0); + ok(parsed("inf"), 1.0 / 0.0); + ok(parsed("-inf"), -1.0 / 0.0); + ok(parsed("-0"), z); + ok(parsed("9007199254740993"), lo); + if (hi == lo) put_n(0); else put_n(1); + if (parsed("5e-324")) put_n(1); else put_n(0); + if (parsed("1e-400")) put_n(0); else put_n(1); + if (parsed("-nan") == parsed("nan")) put_n(0); else put_n(1); + put_c(10); + return 0; +} diff --git a/tests/programs/ftruth.zc b/tests/programs/ftruth.zc new file mode 100644 index 0000000..9d24a42 --- /dev/null +++ b/tests/programs/ftruth.zc @@ -0,0 +1,22 @@ +fn main() { + let z = 0.0 * -1.0; + let p = 0.0; + let n = 1.5; + if (z) put_n(1); else put_n(0); + if (p) put_n(1); else put_n(0); + if (n) put_n(1); else put_n(0); + if (!z) put_n(1); else put_n(0); + if (!p) put_n(1); else put_n(0); + if (!n) put_n(1); else put_n(0); + if (z && n) put_n(1); else put_n(0); + if (n && z) put_n(1); else put_n(0); + if (z || n) put_n(1); else put_n(0); + if (n || z) put_n(1); else put_n(0); + if (p || z) put_n(1); else put_n(0); + for (; z;) put_n(9); + put_n(z == p); + put_n(z < p); + put_n(7); + put_c(10); + return 0; +} diff --git a/tests/programs/ftype.zc b/tests/programs/ftype.zc new file mode 100644 index 0000000..6db7f5a --- /dev/null +++ b/tests/programs/ftype.zc @@ -0,0 +1,47 @@ +fn put_f(x) { asm { x @ #1220 } return 0; } +fn sp() { put_c(32); return 0; } + +struct cell { int tag; float field; } + +float g; + +float add(float a, float b) { return a + b; } +float pick(cell* p) { return p.field; } + +fn main() { + float x; + float y; + cell c; + cell* q; + float buf[1]; + let w = 0; + let z = 0.0 * -1.0; + let n = 0; + x = 1.5; + y = 2; + g = 10; + put_f(add(x, y)); sp(); + put_f(add(g, 0.5)); sp(); + put_f(add(1, 2) + 0.5); sp(); + c.tag = 4; + c.field = 4.25; + q = &c; + put_f(pick(q)); sp(); + put_f(q.field + 1); sp(); + put_f(c.field); sp(); + buf[0] = 6.5; + put_f(buf[0]); sp(); + put_n(&(1.field)); sp(); + w = (&c) as word; + put_n(w.tag); sp(); + put_f(w.field); sp(); + w.field = 8.5; + put_f(c.field); sp(); + put_n(g > 3); sp(); + if (z) n = 1; + do { if (z) n = n + 2; } while (z); + if (!z && g) n = n + 4; + put_n(n); + put_c(10); + return 0; +} diff --git a/tests/programs/gc.zc b/tests/programs/gc.zc new file mode 100644 index 0000000..81412a3 --- /dev/null +++ b/tests/programs/gc.zc @@ -0,0 +1,18 @@ +let keep; +unchecked fn churn(n) { + let i = 0, p; + while (i < n) { p = new(10); p[0] = i; if (i % 100 == 0) keep = p; i++; } + return 0; +} +fn main() { + let base = live_objects(), f; + churn(1000); + put_n(live_objects() - base); put_c(' '); + f = gc(); + put_n(f >= 990); put_c(' '); // conservative: a stale word or two may keep one alive + put_n(keep[0]); put_c(' '); // the one a global still points at survives + churn(200000); // 3.2m words through a 393k word heap: needs the automatic collector + put_n(gc_runs > 1); put_c(' '); + put_n(keep[0]); put_c(10); + return 0; +} diff --git a/tests/programs/generics.expected b/tests/programs/generics.expected new file mode 100644 index 0000000..f3f5f7b --- /dev/null +++ b/tests/programs/generics.expected @@ -0,0 +1,2 @@ +9 6 3 1 2 3 2 +42 pt int 42 10 1 diff --git a/tests/programs/generics.zc b/tests/programs/generics.zc new file mode 100644 index 0000000..e841b19 --- /dev/null +++ b/tests/programs/generics.zc @@ -0,0 +1,54 @@ +// generics are compiled per set of type arguments: T can be any size, new(T) +// stamps the real type, and a struct can take several parameters +struct pt { int x, y; }; +struct record { K key; V val; }; +struct box { int n; T* items; }; + +K rkey(record* r) { return r.key; } +V* rval(record* r) { return &r.val; } +int tsize() { return sizeof(T); } +T*.u make(int n) { T* p = new(T, n) as T*; return u*p; } +record*.u mkrec(K k) { + record* r = new(record); + r.key = k; + return u*r; +} +T pick(T* a, int i) { return a[i]; } +int sumn(T* a, int n) { + int s = 0; + for (int i = 0; i < n; i++) s += a[i]; + return s; +} +fn nameof() { return tname(typeof(T)); } +o3 int osum(T* a, int n) { + int s = 0; + for (int i = 0; i < n; i++) s += a[i].x + a[i].y; + return s; +} +int depth(int n) { + if (n == 0) return tsize(); + return depth(n - 1); +} +int main() { + record* a = mkrec(9); + a.val.x = 5; a.val.y = 6; + put_n(rkey(a)); put_c(' '); + put_n(rval(a).y); put_c(' '); + put_n(sizeof(record)); put_c(' '); + put_n(tsize()); put_c(' '); + put_n(tsize()); put_c(' '); + put_n(tsize>()); put_c(' '); + put_n(depth(5)); put_c(10); + pt* ps = make(3); + ps[2].x = 40; ps[2].y = 2; + put_n(pick(ps, 2).x + ps[2].y); put_c(' '); + put_s(nameof()); put_c(' '); put_s(nameof()); put_c(' '); + put_n(osum(ps, 3)); put_c(' '); + int* is = make(4); + is[0] = 1; is[1] = 2; is[2] = 3; is[3] = 4; + put_n(sumn(is, 4)); put_c(' '); + record* b = mkrec(ps); + put_n(b.key == ps); put_c(10); + free(d*a); free(d*b); free(d*ps); free(d*is); + return 0; +} diff --git a/tests/programs/genmeth.expected b/tests/programs/genmeth.expected new file mode 100644 index 0000000..3d418c1 --- /dev/null +++ b/tests/programs/genmeth.expected @@ -0,0 +1 @@ +3 60 11 21 12 diff --git a/tests/programs/genmeth.zc b/tests/programs/genmeth.zc new file mode 100644 index 0000000..305c55c --- /dev/null +++ b/tests/programs/genmeth.zc @@ -0,0 +1,36 @@ +// methods on generic structs: each set of type arguments gets its own copy +struct pt { int x, y; }; +struct table { + int n; K* keys; V* vals; + fn init(int cap) { + this.n = 0; + this.keys = new(K, cap) as K*; + this.vals = new(V, cap) as V*; + return 0; + } + fn put(K k, V v) { + this.keys[this.n] = k; + this.vals[this.n] = v; + this.n++; + return this.n; + } + fn width() { return sizeof(K) * 10 + sizeof(V); } + fn total() { + int s = 0; + for (int i = 0; i < this.n; i++) s += this.vals[i]; + return s; + } +}; +int main() { + table a; + a.init(8); + a.put(1, 10); a.put(2, 20); a.put(3, 30); + put_n(a.n); put_c(' '); put_n(a.total()); put_c(' '); put_n(a.width()); put_c(' '); + table* b = new(table); + b.init(4); + put_n(b.width()); put_c(' '); + table* c = new(table); + c.init(4); + put_n(c.width()); put_c(10); + return 0; +} diff --git a/tests/programs/growheap.zc b/tests/programs/growheap.zc new file mode 100644 index 0000000..07e10a6 --- /dev/null +++ b/tests/programs/growheap.zc @@ -0,0 +1,32 @@ +// the heap grows past its initial size: eight live blocks of 300000 words +// don't fit in the 393216-word start, so the allocator takes more memory from +// the host, and a block bigger than the start fits once they are freed +let keep[8]; +unchecked fn fill(n) { + let i = 0, a; + while (i < n) { a = new(300000); a[0] = i + 1; a[299999] = (i + 1) * 3; keep[i] = a; i++; } + return 0; +} +unchecked fn total(n) { + let i = 0, a, sum = 0; + while (i < n) { a = keep[i]; sum = sum + a[0] + a[299999]; i++; } + return sum; +} +unchecked fn drop(n) { + let i = 0; + while (i < n) { free(keep[i]); keep[i] = 0; i++; } + return 0; +} +fn main() { + let a, start = heap_size(); + fill(8); + put_n(total(8)); put_c(' '); + put_n(heap_size() > start); put_c(' '); + drop(8); + scrub(); gc(); + a = new(3000000); + a[2999999] = 7; + put_n(a[2999999]); put_c(' '); + put_n(heap_size() >= 3000000); put_c(10); + return 0; +} diff --git a/tests/programs/growone.zc b/tests/programs/growone.zc new file mode 100644 index 0000000..12980ff --- /dev/null +++ b/tests/programs/growone.zc @@ -0,0 +1,9 @@ +// one block bigger than the initial heap: the machine grows, and the block is usable +fn main() { + let a = new(400000); + a[0] = 5; + a[399999] = 7; + put_n(a[0] + a[399999]); put_c(' '); + put_n(heap_size() >= 400000); put_c(10); + return 0; +} diff --git a/tests/programs/infer_return.zc b/tests/programs/infer_return.zc new file mode 100644 index 0000000..a9f12bd --- /dev/null +++ b/tests/programs/infer_return.zc @@ -0,0 +1,3 @@ +// a owned return with no .u on the signature: the return type is inferred from the body +fn mk(v) { int* p = new(1); p[0] = v; return p; } +fn main() { int* q = mk(3); put_n(q[0]); put_c(10); return 0; } diff --git a/tests/programs/interior.zc b/tests/programs/interior.zc new file mode 100644 index 0000000..bccf377 --- /dev/null +++ b/tests/programs/interior.zc @@ -0,0 +1,17 @@ +unchecked fn main() { + let p = new(4), r, i = 0, t, bad = 0; + mem_set(4); + p[0] = 111; p[1] = 222; p[2] = 333; p[3] = 444; + r = p + 1; + free(p); + gc(); + while (i < 8) { + t = new(4); + if (t == r - 1) bad = 1; + i++; + } + if (r[0] != 222) bad = bad + 2; + put_n(bad); + put_c(10); + return 0; +} diff --git a/tests/programs/joins.zc b/tests/programs/joins.zc new file mode 100644 index 0000000..98275ad --- /dev/null +++ b/tests/programs/joins.zc @@ -0,0 +1,31 @@ +// joins: something given away on one path is dead afterwards, and the paths +// that still own it free it on the way in. nothing here leaks. +fn take(.d p) { return p[0]; } +fn later(.d p); // not needed any more, but still allowed +int*.u mk(int v) { int* p = new(int); *p = v; return u*p; } +fn count(int n) { let s = 0; switch (n % 4) { case 0: s = 10; case 1, 2: s = 20; break; default: s = 30; } return s; } +fn main() { + let base = live_objects(), i = 0, hits = 0, k; + while (i < 50) { + let x = mk(i); + if (i % 3 == 0) take(d*x); // else: freed here by the fixup + i++; + } + put_n(live_objects() - base); put_c(' '); + // give away and break: fine now; the normal exit frees it + i = 0; + let y = mk(7); + while (i < 10) { if (i == 4) { take(d*y); break; } i++; } + put_n(live_objects() - base); put_c(' '); + let z = mk(5); + while (1) { if (z[0] == 5) { hits = take(d*z); break; } } + put_n(hits); put_c(' '); + put_n(count(0) + count(1) + count(2) + count(3)); put_c(' '); + k = i > 3 ? i * 2 : 0 - 1; + put_n(k); put_c(' '); + put_n(forward(new(int))); put_c(' '); // tagged and defined below: the prescan knows .d + int** pp; int* q = new(int); *q = 9; pp = &q; put_n(**pp); free(d*q); put_c(' '); + put_n(live_objects() - base); put_c(10); + return 0; +} +fn forward(int*.d p) { return 3 + *p; } diff --git a/tests/programs/manyerrors.zc b/tests/programs/manyerrors.zc new file mode 100644 index 0000000..3ba2c5a --- /dev/null +++ b/tests/programs/manyerrors.zc @@ -0,0 +1,6 @@ +// several errors in one run: a syntax error, an analyzer error, a type error +typecheck on; +fn f() { let a = ; return 0; } +fn g() { let x = new(3); return 0; } +int h() { char* s = 5; return 1; } +fn main() { return 0 } diff --git a/tests/programs/memleak.zc b/tests/programs/memleak.zc new file mode 100644 index 0000000..a61c331 --- /dev/null +++ b/tests/programs/memleak.zc @@ -0,0 +1,11 @@ +memdebug on; +int* g; +fn main() +{ + int* p = new(int); + *p = 7; + g = d*p; + put_n(memreport()); + put_c(10); + return 0; +} diff --git a/tests/programs/memory_bounds.zc b/tests/programs/memory_bounds.zc new file mode 100644 index 0000000..0863d03 --- /dev/null +++ b/tests/programs/memory_bounds.zc @@ -0,0 +1,13 @@ +// raw word loads, stores, and stack picks outside memory follow portable semantics. +unchecked int raw_pick(int depth) { return asm { depth @ #6 }; } +unchecked fn main() { + int p = -1; + put_n(*p); put_c(32); *p = 7; + p = 0x7FFFFFFF; + put_n(*p); put_c(32); *p = 9; + p = 0x100000000; + put_n(*p); put_c(32); + put_n(raw_pick(0x100000000)); put_c(32); + put_n(raw_pick(0 - 0x100000000)); put_c(10); + return 0; +} diff --git a/tests/programs/meta.zc b/tests/programs/meta.zc new file mode 100644 index 0000000..6afdf36 --- /dev/null +++ b/tests/programs/meta.zc @@ -0,0 +1,43 @@ +fn ipow(a, b) { let r = 1; while (b > 0) { r *= a; b--; } return r; } +// a parse word that adds an operator to the compiler's tables +immediate fn pow_op() { next(); add_binop("**", 11, "ipow c"); return 0; } +pow_op + +// unless (cond) stmt: a parse word that emits code itself +immediate fn unless() { + let a; + next(); + expect(intern("(")); rval(); plainuse(expr()); rval(); expect(intern(")")); + emitseq(useq[0], ulen[0]); // not + a = hole(); op(O_JZ); // skip the body when the condition was true + substmt(); + setlit(a, here); + return 0; +} +// repeat N { body }: a macro, built from token lists +immediate fn repeat() { + let n, body, i = 0; + next(); n = tv; next(); + body = grab(); + while (i < n) { feed(qdup(body)); i++; } + free(d*body); + return 0; +} +fn main() { + put_n(2 ** 10); put_c(' '); put_n(3 + 2 ** 3 * 2); put_c(10); + unless (1 > 2) put_s("unless ok\n"); + unless (2 > 1) put_s("wrong\n"); + repeat 3 { put_c('r'); } + put_c(10); + let q = quote { put_n(40 + 2); put_c(10); return 7; }; + put_n(q[0]); put_c(' '); + put_n(eval(q)); put_c(10); + let f = compile(q); + put_n(f()); put_c(10); + free(d*q); + let defs = quote { fn sq(x) { return x * x; } }; + load(defs); + free(d*defs); + put_n(evals("return sq(12);")); put_c(10); + return 0; +} diff --git a/tests/programs/methodown.zc b/tests/programs/methodown.zc new file mode 100644 index 0000000..98ba146 --- /dev/null +++ b/tests/programs/methodown.zc @@ -0,0 +1,15 @@ +// a struct method can return an owned object: its header declares the .u return +struct Node { int v; } +struct Maker { + static Node*.u make(int n) { Node* p = new(Node); p.v = n; return u*p; } +} +Node*.u mk(int n) { Node* p = new(Node); p.v = n; return u*p; } +int main() { + Node*.u a = Maker.make(7); + put_n(a.v); put_c(32); + free(d*a); + Node*.u b = mk(9); + put_n(b.v); put_c(10); + free(d*b); + return 0; +} diff --git a/tests/programs/misc.zc b/tests/programs/misc.zc new file mode 100644 index 0000000..530322a --- /dev/null +++ b/tests/programs/misc.zc @@ -0,0 +1,27 @@ +// ct { ... } runs a block while compiling and leaves its result as a constant +immediate fn ct() { + let q, v; + next(); + q = grab(); + v = eval(q); + free(d*q); + lit(v); + return 0; +} +// teach the asm syntax that p means out, like the old repl demo +immediate fn teach() { next(); mnem[12] = 'p'; return 0; } +teach + +fn countdown() { + asm { 5 :top 0 $ '0' + p 1 - 0 $ done z top j :done _ 10 p } + return 0; +} +fn main() { + put_n(ct { let i = 0, s = 0; while (i < 10) { s += i; i++; } return s; }); put_c(10); + countdown(); + put_s(lazy()); put_c(10); + return 0; +} +// the analyzer is off from here on +unchecked; +fn lazy() { let p = new(3); p[0] = 'o'; p[1] = 'k'; return p; } diff --git a/tests/programs/names.zc b/tests/programs/names.zc new file mode 100644 index 0000000..6ccb2eb --- /dev/null +++ b/tests/programs/names.zc @@ -0,0 +1,9 @@ +fn f(d) { let u = 3, m = 4; if (d > 0) return u + m + d; return 0; } +fn dmul(d) { return d * 4; } +fn main() { + let u = 1; + put_n(f(5)); put_c(' '); + put_n(u); put_c(' '); + put_n(dmul(2)); put_c(10); + return 0; +} diff --git a/tests/programs/o3.zc b/tests/programs/o3.zc new file mode 100644 index 0000000..c875d09 --- /dev/null +++ b/tests/programs/o3.zc @@ -0,0 +1,75 @@ +// o3 regions: every kind of control flow, against the same code without o3 +struct pt { int x; int y; }; +int g = 3; +int sq(int x) { return x * x; } +o3 int collatz(int n) { + int steps = 0; + while (n != 1) { if (n % 2 == 0) n = n / 2; else n = 3 * n + 1; steps++; } + return steps; +} +o3 int ack(int am, int n) { + if (am == 0) return n + 1; + if (n == 0) return ack(am - 1, 1); + return ack(am - 1, ack(am, n - 1)); +} +o3 int many(int a, int b, int c) { + int dd = a + b, e = b + c, f = c + a, h = dd * e, i2 = e * f, j = f * dd, k = h + i2 + j, l = k / 7; + for (int t = 0; t < 5; t++) { dd += t; e -= t; f = f ^ t; l = l + dd * e - f; } + return dd + e + f + h + i2 + j + k + l; +} +o3 int mix(int n) { + int acc = 0; + for (int i = 0; i < n; i++) { + if (i % 3 == 0 && i % 5 == 0) acc += 15; + else if (i % 3 == 0 || i > 90) acc += 3; + else acc = acc + (i & 1 ? sq(i) : g); + switch (i % 4) { case 0: acc += 1; case 1: acc -= 2; default: acc = acc ^ 1; } + if (acc > 100000) break; + if (i == 7) continue; + acc = acc + i / 10 - i % 7 + (i << 2) - (i >> 1); + } + return acc; +} +o3 int dyn(int n) { + []int a = new([]int, n); + fixed []int f = new(fixed []int, n); + int s = 0; + for (int i = 0; i < len(a); i++) { a[i] = i * i; f[i] = n - i; } + for (int i = 0; i < n; i++) s = s + a[i] * f[i]; + free(d*a); free(d*f); + return s; +} +o3 int pts(int n) { + pt* p = new(pt); + int s = 0; + p->x = 1; p->y = 2; + for (int i = 0; i < n; i++) { p->x = p->x + p->y; p->y = p->x - p->y; s = s + p->x % 1000; } + free(d*p); + return s; +} +o3 int divs(int x) { + int s = 0; + for (int i = -50; i < 50; i++) s = s + (x + i) / 7 + (x + i) % 7 + (x - i) / -3 + i / 16 + i % 16; + return s; +} +fn main() { + o3 int lone = 2; + let r = 0; + lone = lone * 21; + o3 { + int i = 0, s = 0; + while (i < 100) { s = s + collatz(i + 1); i++; } + put_n(s); put_c(' '); + put_n(lone); put_c(' '); + lone++; + } + put_n(lone); put_c(' '); + put_n(ack(2, 3)); put_c(' '); + put_n(many(1, 2, 3)); put_c(' '); + put_n(mix(100)); put_c(' '); + put_n(dyn(50)); put_c(' '); + put_n(pts(30)); put_c(' '); + put_n(divs(1000)); put_c(' '); + put_n(divs(-77)); put_c(10); + return 0; +} diff --git a/tests/programs/o3_bounds.zc b/tests/programs/o3_bounds.zc new file mode 100644 index 0000000..fb347c0 --- /dev/null +++ b/tests/programs/o3_bounds.zc @@ -0,0 +1,2 @@ +// an index outside a []T inside o3 code: caught by the inline check +fn main() { []int a = new([]int, 5); o3 { int i = 7; a[i] = 1; } free(d*a); return 0; } diff --git a/tests/programs/o3bytes.zc b/tests/programs/o3bytes.zc new file mode 100644 index 0000000..bada583 --- /dev/null +++ b/tests/programs/o3bytes.zc @@ -0,0 +1,52 @@ +// byte ops called from o3 stay in the region. ld8/st8 and the 16- and 32-bit +// pair are opcodes there, on the interpreter, the arm64 jit and llvm. +fn sp() { put_c(32); return 0; } +fn litw(v) { return v + v + 1; } +fn f() { put_c('A'); return 0; } +fn main() { + let buf[4], b, i, s, t, p, v, w; + i = 0; + while (i < 4) { buf[i] = 0; i++; } + b = addr_b(buf); + i = 0; + s = 0; + o3 { + while (i < 20) { + t = b + i; + st8(t, i); + s = s + ld8(t); + i++; + } + } + put_n(s); sp(); + o3 { st16(b, 4660); w = ld16(b); } + put_n(w); sp(); + t = b + 7; + o3 { w = ld8(t); } + put_n(w); sp(); + o3 { w = ld16(t); } + put_n(w); sp(); + t = b + 10; + v = 16909060; + o3 { st32(t, v); w = ld32(t); } + put_n(w); sp(); + t = 0 - 1; + o3 { st8(t, v); w = ld8(t); } + put_n(w); sp(); + t = addr_b(MW) - 1; + o3 { w = ld16(t); } + put_n(w); + put_c(10); + p = f; + f(); + while (*p != litw('A')) p++; + t = addr_b(p); + v = litw('B') & 255; + o3 { st8(t, v); } + f(); + v = litw('C') & 255; + o3 { st8(t, v); } + f(); + put_c(10); + return 0; +} diff --git a/tests/programs/o3call.zc b/tests/programs/o3call.zc new file mode 100644 index 0000000..b2be717 --- /dev/null +++ b/tests/programs/o3call.zc @@ -0,0 +1,29 @@ +// new stamps the header, so an o3 caller and an ordinary caller both see a type. +// the call itself is not a boundary: no fatten, and the callee's prologue does not run. +struct node { int v; node* c; }; +unchecked o3 node* leaf(int v) { + node* n = new(node); + n.v = v; n.c = 0; + return n; +} +unchecked o3 int inner() { + node* n = leaf(7); + int* p = n; + return p[-1]; +} +unchecked o3 int add(int a, int b) { return a + b; } +unchecked o3 int nest(int n) { + if (n == 0) return 0; + return n + nest(n - 1); +} +unchecked fn main() { + node* n = leaf(4); + int* p = n; + put_n(p[-1] < 0); put_c(' '); + put_n(inner() < 0); put_c(' '); + put_n(n.v); put_c(' '); + put_n(add(20, 3)); put_c(' '); + put_n(nest(10)); put_c(' '); + tprint(typeof_obj(n)); put_c(10); + return 0; +} diff --git a/tests/programs/o3copy.zc b/tests/programs/o3copy.zc new file mode 100644 index 0000000..90b9ade --- /dev/null +++ b/tests/programs/o3copy.zc @@ -0,0 +1,12 @@ +// copies of a variable that a loop reads again: the global pass reads the variable itself +o3 int g(int n, int b) { + int s = 0, c = b, i = 0; + while (i < n) { c = b; s = s + c * 3 + i; i++; } + return s + c; +} +o3 int h(int n, int k) { + int a = k, t = 0, i = 0; + while (i < n) { t = t + a; if (t > 1000) t = t - 1000; a = k; i++; } + return t + a; +} +fn main() { put_n(g(1000, 7)); put_c(32); put_n(h(1000, 5)); put_c(10); return 0; } diff --git a/tests/programs/o3deep.zc b/tests/programs/o3deep.zc new file mode 100644 index 0000000..e715d9e --- /dev/null +++ b/tests/programs/o3deep.zc @@ -0,0 +1,3 @@ +// o3 recursion nests on the host stack as deep as the machine's own stacks allow +o3 int deep(int n) { if (n == 0) return 0; return 1 + deep(n - 1); } +fn main() { put_n(deep(30000)); put_c(10); return 0; } diff --git a/tests/programs/o3deep_over.zc b/tests/programs/o3deep_over.zc new file mode 100644 index 0000000..45816f6 --- /dev/null +++ b/tests/programs/o3deep_over.zc @@ -0,0 +1,3 @@ +// recursion can grow beyond the original operand and frame reservations +o3 int deep(int n) { if (n == 0) return 0; return 1 + deep(n - 1); } +fn main() { put_n(deep(100000)); put_c(10); return 0; } diff --git a/tests/programs/o3direct.expected b/tests/programs/o3direct.expected new file mode 100644 index 0000000..ec32091 --- /dev/null +++ b/tests/programs/o3direct.expected @@ -0,0 +1,2 @@ +6765 1 0 123 4567 7 9 8.000000 5050 1926 +4 3 2 1 10 diff --git a/tests/programs/o3direct.zc b/tests/programs/o3direct.zc new file mode 100644 index 0000000..3c5d7ee --- /dev/null +++ b/tests/programs/o3direct.zc @@ -0,0 +1,33 @@ +// o3 calls between functions: arguments in registers, results on the stack, and the ways a call can leave the fast path +fn put_f(x) { asm { x @ #1220 } return 0; } +struct node { int v; node *next; }; +o3 int fib(int n) { if (n < 2) return n; return fib(n - 1) + fib(n - 2); } +o3 int ev(int n); +o3 int od(int n) { if (n == 0) return 0; return ev(n - 1); } +o3 int ev(int n) { if (n == 0) return 1; return od(n - 1); } +o3 int three(int a, int b, int c) { return a * 100 + b * 10 + c; } +o3 int four(int a, int b, int c, int d) { return three(a, b, c) * 10 + d; } +o3 int zero() { return 7; } +o3 int ack(int m, int n) { if (m == 0) return n + 1; if (n == 0) return ack(m - 1, 1); return ack(m - 1, ack(m, n - 1)); } +o3 float half(float x, int k) { if (k == 0) return x; return half(x / 2.0, k - 1); } +o3 int noisy(int n) { if (n == 0) return 0; put_n(n); put_c(' '); return n + noisy(n - 1); } +unchecked o3 int sumlist(node *p) { int s = 0; while (p) { s = s + p.v; p = p.next; } return s; } +unchecked o3 int build(int n) { + node *h = 0; + for (int i = 1; i <= n; i++) { node *x = new(node); x.v = i; x.next = h; h = x; } + int s = sumlist(h); + while (h) { node *t = h.next; free(h); h = t; } + return s; +} +unchecked o3 int mix(int a, int b) { return three(a, zero(), b) + fib(a) + four(1, 2, 3, b); } +unchecked fn main() { + put_n(fib(20)); put_c(' '); + put_n(ev(10)); put_c(' '); put_n(ev(7)); put_c(' '); + put_n(three(1, 2, 3)); put_c(' '); put_n(four(4, 5, 6, 7)); put_c(' '); put_n(zero()); put_c(' '); + put_n(ack(2, 3)); put_c(' '); + put_f(half(64.0, 3)); put_c(' '); + put_n(build(100)); put_c(' '); + put_n(mix(6, 9)); put_c(10); + put_n(noisy(4)); put_c(10); + return 0; +} diff --git a/tests/programs/o3fat.zc b/tests/programs/o3fat.zc new file mode 100644 index 0000000..937b0f3 --- /dev/null +++ b/tests/programs/o3fat.zc @@ -0,0 +1,20 @@ +// o3 objects reaching normal code get their types: through a return (all the way down the list), an assignment, a store into a field +struct node { int v; node* next; }; +struct box { [3]node* slots; int n; }; +unchecked o3 node* build(int n) { + node* head = 0; + for (int i = 0; i < n; i++) { node* k = new(node); k.v = i; k.next = head; head = k; } + return head; +} +unchecked fn main() { + node* l = build(5); + tprint(typeof_obj(l)); put_c(' '); tprint(typeof_obj(l.next.next.next)); put_c(' '); + put_n(l.next.v); put_c(' '); + box* b = 0; + o3 { b = new(box); b.slots[1] = new(node); b.n = 2; } + tprint(typeof_obj(b)); put_c(' '); tprint(typeof_obj(b.slots[1])); put_c(' '); + int* raw = 0; + o3 { raw = new(int, 6); } + tprint(typeof_obj(raw)); put_c(10); + return 0; +} diff --git a/tests/programs/o3licm.zc b/tests/programs/o3licm.zc new file mode 100644 index 0000000..61e6a46 --- /dev/null +++ b/tests/programs/o3licm.zc @@ -0,0 +1,46 @@ +// loop invariants are built once in front of the loop, and a value two blocks both build is shared. +// every result below has to match the plain run (ZYNOSHARE=1 turns the pass off). +unchecked o3 int nest(int n, int k) { + int s = 0; + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + s = s + (k * 37 + 11) * (i + j); + } + } + return s; +} +unchecked o3 int loads(int* a, int n) { + int s = 0; + for (int i = 0; i < n; i++) s = s + a[0] * a[1] + i; + return s; +} +unchecked o3 int stores(int* a, int n) { + int s = 0; + for (int i = 0; i < n; i++) { a[0] = a[0] + 1; s = s + a[0] * 3; } + return s; +} +unchecked o3 int branchy(int x, int y, int n) { + int s = 0; + for (int i = 0; i < n; i++) { + if (i & 1) s = s + x * y * 5; + else s = s - x * y * 5 + i; + } + return s; +} +unchecked o3 int twice(int x, int y) { + int r = 0; + if (x > 0) r = (x * y + 3) * (x * y + 3); + else r = x * y + 3; + return r + (x * y + 3) * 7; +} +unchecked fn main() { + int* a = new(int, 4); + a[0] = 5; a[1] = 6; + put_n(nest(30, 3)); put_c(' '); + put_n(loads(a, 100)); put_c(' '); + put_n(stores(a, 10)); put_c(' '); + put_n(branchy(3, 4, 21)); put_c(' '); + put_n(twice(5, 2)); put_c(' '); + put_n(twice(-5, 2)); put_c(10); + return 0; +} diff --git a/tests/programs/o3push.expected b/tests/programs/o3push.expected new file mode 100644 index 0000000..b6857dc --- /dev/null +++ b/tests/programs/o3push.expected @@ -0,0 +1 @@ +1000 1498500 2997 1005 1 10 1 1498530 0 diff --git a/tests/programs/o3push.zc b/tests/programs/o3push.zc new file mode 100644 index 0000000..e5f0090 --- /dev/null +++ b/tests/programs/o3push.zc @@ -0,0 +1,38 @@ +// push in o3 code: the quick path when there is room, dpush when there is not +o3 int fill([]int a, int n) { + for (int i = 0; i < n; i++) push(a, i * 3); + return len(a); +} +o3 int sum([]int a) { + int s = 0; + for (int i = 0; i < len(a); i++) s += a[i]; + return s; +} +o3 float fsum([]float a) { + float s = 0.0; + for (int i = 0; i < len(a); i++) s += a[i]; + return s; +} +o3 int ffill([]float a, int n) { + for (int i = 0; i < n; i++) push(a, 0.5 * i); + return len(a); +} +int main() { + []int a = new([]int, 0); + put_n(fill(a, 1000)); put_c(' '); + put_n(sum(a)); put_c(' '); + put_n(a[999]); put_c(' '); + put_n(fill(a, 5)); put_c(' '); + put_n(cap(a) >= 1005); put_c(' '); + []float f = new([]float, 0); + ffill(f, 10); + put_n(len(f)); put_c(' '); put_n(fsum(f) == 22.5); put_c(' '); + put_n(drain(a)); put_c(' '); put_n(len(a)); put_c(10); + free(d*a); free(d*f); + return 0; +} +o3 int drain([]int a) { + int s = 0; + while (len(a) > 0) s += pop(a); + return s; +} diff --git a/tests/programs/o3scopes.zc b/tests/programs/o3scopes.zc new file mode 100644 index 0000000..4dbf68c --- /dev/null +++ b/tests/programs/o3scopes.zc @@ -0,0 +1,17 @@ +// registers reused by scopes that never overlap, and a fixed array read through its cached items and length +o3 int scopes(fixed []int f) { + int total = 0; + for (int a = 0; a < 3; a++) { int x = a * 2; total += x; } + for (int b = 0; b < 3; b++) { int y = b * 3; total += y; } + for (int c = 0; c < 3; c++) { int z = c * 5; total += z; } + for (int e = 0; e < len(f); e++) { int w = f[e]; total += w; } + for (int g = 0; g < 4; g++) { int h = g + 1, q = h * h, r = q - 1; total += r; } + return total; +} +fn main() { + fixed []int f = new(fixed []int, 4); + for (int i = 0; i < 4; i++) f[i] = 10 * i; + put_n(scopes(f)); put_c(10); + free(d*f); + return 0; +} diff --git a/tests/programs/o3zero.zc b/tests/programs/o3zero.zc new file mode 100644 index 0000000..cfb3061 --- /dev/null +++ b/tests/programs/o3zero.zc @@ -0,0 +1,8 @@ +unchecked o3 int raw() { + int* p = new(4); + return p[0] + p[1] + p[2] + p[3] + (p[-1] < 0); +} +fn main() { + put_n(raw()); put_c(10); + return 0; +} diff --git a/tests/programs/overflow_data.zc b/tests/programs/overflow_data.zc new file mode 100644 index 0000000..db24f8d --- /dev/null +++ b/tests/programs/overflow_data.zc @@ -0,0 +1 @@ +fn main() { syscall(9, 5, syscall(9, 0, 0, 0), 0); while (1) asm { 1 } return 0; } diff --git a/tests/programs/overflow_frame.zc b/tests/programs/overflow_frame.zc new file mode 100644 index 0000000..c017453 --- /dev/null +++ b/tests/programs/overflow_frame.zc @@ -0,0 +1,2 @@ +fn big(n) { let a[500]; a[0] = n; return big(n + 1) + a[0]; } +fn main() { syscall(9, 5, syscall(9, 0, 0, 0), 0); return big(0); } diff --git a/tests/programs/overflow_return.zc b/tests/programs/overflow_return.zc new file mode 100644 index 0000000..91e572d --- /dev/null +++ b/tests/programs/overflow_return.zc @@ -0,0 +1,2 @@ +fn deep(n) { return deep(n + 1) + 1; } +fn main() { syscall(9, 5, syscall(9, 0, 0, 0), 0); return deep(0); } diff --git a/tests/programs/packedfields.expected b/tests/programs/packedfields.expected new file mode 100644 index 0000000..df81ff5 --- /dev/null +++ b/tests/programs/packedfields.expected @@ -0,0 +1,2 @@ +4 3 3.500000 0 200 255 44 7 -3 65535 99 44 0 -4 0 98 1000 5 3.250000 77 5 42 17 3 -9 0 12 12 123456789 +15200 11 50 diff --git a/tests/programs/packedfields.zc b/tests/programs/packedfields.zc new file mode 100644 index 0000000..8f6fe38 --- /dev/null +++ b/tests/programs/packedfields.zc @@ -0,0 +1,42 @@ +// struct fields of the sized types and f32 pack into shared words +struct vtx { f32 x, y, z; u8 r, g, b, a; i16 id; u16 flags; int next; }; +struct pair { u8 a; int b; u8 c; }; +fn put_f(x) { asm { x @ #1220 } return 0; } +fn sp() { put_c(32); return 0; } +vtx gv; +fn main() { + vtx v; + vtx* p = new(vtx); + pair q; + [3]vtx arr; + put_n(sizeof(vtx)); sp(); put_n(sizeof(pair)); sp(); + v.x = 1.5; v.y = 0.1; v.z = 2; v.r = 200; v.g = 255; v.b = 300; v.a = 7; v.id = 0 - 3; v.flags = 65535; v.next = 99; + put_f(v.x + v.z); sp(); put_n(v.y == 0.1); sp(); + put_n(v.r); sp(); put_n(v.g); sp(); put_n(v.b); sp(); put_n(v.a); sp(); put_n(v.id); sp(); put_n(v.flags); sp(); put_n(v.next); sp(); + v.r += 100; v.flags++; v.id--; v.g = v.g + 1; + put_n(v.r); sp(); put_n(v.flags); sp(); put_n(v.id); sp(); put_n(v.g); sp(); + p.r = 9; p.a = 8; p.id = 1000; p.next = 5; p.x = 3.25; + put_n(p.r); put_n(p.a); sp(); put_n(p.id); sp(); put_n(p.next); sp(); put_f(p.x); sp(); + gv.b = 77; gv.flags = 5; put_n(gv.b); sp(); put_n(gv.flags); sp(); + arr[1].a = 42; arr[2].r = 17; arr[1].next = 3; arr[0].id = 0 - 9; + put_n(arr[1].a); sp(); put_n(arr[2].r); sp(); put_n(arr[1].next); sp(); put_n(arr[0].id); sp(); put_n(arr[0].a); sp(); + put_n(sizeof(arr)); sp(); + q.a = 1; q.b = 123456789; q.c = 2; put_n(q.a); put_n(q.c); sp(); put_n(q.b); + put_c(10); + free(d*p); + second(); + return 0; +} +struct rgba { u8 r, g, b, a; }; +o3 int bright(rgba* px, int n) { + int s = 0; + for (int i = 0; i < n; i++) { s += px[i].r + px[i].g + px[i].b; px[i].a = px[i].r + 1; } + return s; +} +fn second() { + rgba* px = new(rgba, 50); + for (int i = 0; i < 50; i++) { px[i].r = i; px[i].g = i * 2; px[i].b = 255 - i; } + put_n(bright(px, 50)); put_c(' '); put_n(px[10].a); put_c(' '); put_n(px[49].a); put_c(10); + free(d*px); + return 0; +} diff --git a/tests/programs/quarantine.zc b/tests/programs/quarantine.zc new file mode 100644 index 0000000..5e3e188 --- /dev/null +++ b/tests/programs/quarantine.zc @@ -0,0 +1,25 @@ +// free quarantines the block. a stale alias still reads it, and freeing +// through the alias does nothing because the header already says free. +// unchecked so the analyzer doesn't reject the use after free. +unchecked fn main() { + let p = new(4), q, r, i = 0, t, bad = 0; + p[0] = 111; p[1] = 222; p[2] = 333; p[3] = 444; + q = p; + r = p + 2; + free(p); + while (i < 400) { t = new(4); t[0] = 999; t[1] = 888; t[2] = 777; t[3] = 666; i++; } + if (q[0] != 111 || q[1] != 222 || r[0] != 333 || r[1] != 444) bad = 1; + free(q); + if (q[0] != 111 || q[1] != 222) bad = 2; + gc(); // q still points at the block, so the sweep must not reuse it + if (q[0] != 111 || r[0] != 333) bad = 3; + p = 0; q = 0; r = 0; t = 0; + scrub(); + gc(); + t = new(4); + t[0] = 1234; + put_n(bad); put_c(' '); + put_n(t[0]); put_c(10); + free(t); + return 0; +} diff --git a/tests/programs/rawpid.zc b/tests/programs/rawpid.zc new file mode 100644 index 0000000..b3e7c2a --- /dev/null +++ b/tests/programs/rawpid.zc @@ -0,0 +1,6 @@ +include "src/lib/std.zc"; +// syscall 39 is getpid on x86-64 only; the arm64 number differs, so test.sh runs this there alone. +fn main() { + put_n(Host.raw(39, 0, 0, 0, 0, 0, 0) > 0); put_c(10); + return 0; +} diff --git a/tests/programs/rawsys.zc b/tests/programs/rawsys.zc new file mode 100644 index 0000000..753e1b7 --- /dev/null +++ b/tests/programs/rawsys.zc @@ -0,0 +1,5 @@ +include "src/lib/std.zc"; +fn main() { + put_n(Host.raw(9999, 0, 0, 0, 0, 0, 0)); put_c(10); + return 0; +} diff --git a/tests/programs/recycle.zc b/tests/programs/recycle.zc new file mode 100644 index 0000000..98c9b50 --- /dev/null +++ b/tests/programs/recycle.zc @@ -0,0 +1,23 @@ +// a uniquely owned free returns the block to its size class at once. +// a plain free still quarantines; that contract lives in quarantine.zc. +struct node { node* c; int n; }; +unchecked fn addr(p) { return p; } +fn main() { + let base = live_objects(), p, q, slot; + node* a; + node* b; + p = new(4); + a = new(node); + b = new(node); + p[0] = 111; + a.c = b; a.n = 1; b.c = 0; b.n = 2; + slot = addr(p); + free(d*p); + q = new(4); + free(d*a); + put_n(q == slot); put_c(' '); + put_n(q[0]); put_c(' '); + put_n(live_objects() - base); put_c(10); + free(d*q); + return 0; +} diff --git a/tests/programs/roots.zc b/tests/programs/roots.zc new file mode 100644 index 0000000..22f12a5 --- /dev/null +++ b/tests/programs/roots.zc @@ -0,0 +1,17 @@ +// a pointer left below the live stack is not a root. scrub then clears that word. +unchecked fn main() { + let p, slot, before, after; + p = new(2); + p[0] = 7; + p[1] = 8; + slot = DSLO + 8; + *slot = p; + before = live_objects(); + p = 0; + gc(); + after = live_objects(); + put_n(after < before); put_c(32); + scrub(); + put_n(*slot); put_c(10); + return 0; +} diff --git a/tests/programs/scrub.zc b/tests/programs/scrub.zc new file mode 100644 index 0000000..ebc6cfa --- /dev/null +++ b/tests/programs/scrub.zc @@ -0,0 +1,9 @@ +unchecked fn main() { + mem_set(2); + let p = new(2); + p[0] = 11; + free(p); + put_n(p[0]); + put_c(10); + return 0; +} diff --git a/tests/programs/share.zc b/tests/programs/share.zc new file mode 100644 index 0000000..e9c381f --- /dev/null +++ b/tests/programs/share.zc @@ -0,0 +1,34 @@ +// shared cons cells, a shared string, and a cycle. the collector reclaims +// them once nothing points at them. free of a shared cell does nothing. +fn main() { + let base = live_objects(), a, b, c, s, p, q, cyc, n, tri; + a = cons(1, 2); + b = cons(a, 3); + c = cons(a, 4); + put_n(car(a)); put_c(32); + put_n(cdr(a)); put_c(32); + put_n(car(car(b))); put_c(32); + put_n(cdr(c)); put_c(32); + put_n(car(b) == a); put_c(32); + put_n(is_shared(a)); put_c(32); + n = live_objects(); + free(a); + put_n(live_objects() == n); put_c(32); + put_n(car(a)); put_c(32); + s = n_to_s(42); + p = cons(s, 0); + q = cons(s, 1); + put_n(s_to_n(car(p))); put_c(32); + put_n(car(p) == car(q)); put_c(32); + cyc = cons(0, 1); + cyc[0] = cyc; + put_n(cdr(cyc)); put_c(32); + put_n(car(cyc) == cyc); put_c(32); + tri = triple(5, 6, 7); + put_n(tget(tri, 1)); put_c(32); + a = 0; b = 0; c = 0; s = 0; p = 0; q = 0; cyc = 0; tri = 0; + scrub(); + gc(); + put_n(live_objects() - base); put_c(10); + return 0; +} diff --git a/tests/programs/share_dbg.zc b/tests/programs/share_dbg.zc new file mode 100644 index 0000000..5485b51 --- /dev/null +++ b/tests/programs/share_dbg.zc @@ -0,0 +1,10 @@ +// memdebug still checks a shared cell. free does not quarantine it, and car +// reads the payload through checkptr. +memdebug on; +fn main() { + let a = cons(7, 8); + free(a); + put_n(car(a)); put_c(32); + put_n(is_shared(a)); put_c(10); + return 0; +} diff --git a/tests/programs/sized.expected b/tests/programs/sized.expected new file mode 100644 index 0000000..c5f51ab --- /dev/null +++ b/tests/programs/sized.expected @@ -0,0 +1,5 @@ +4 -56 -32768 0 44 -56 44 -56 44 +2008 -25536 4464 705032704 -128 1 8000000000 3 2 +124716 1 77 255 0 +255 0 -1 7 44 2 +54285 diff --git a/tests/programs/sized.zc b/tests/programs/sized.zc new file mode 100644 index 0000000..0a18467 --- /dev/null +++ b/tests/programs/sized.zc @@ -0,0 +1,80 @@ +// sized integers: scalars wrap to their size, arrays and pointers of them are packed +struct pix { u8 r, g, b, a; i16 dx; [4]u8 tag; }; +u8 gbyte = 300; +i8 gneg = 200; +u16 gword[5]; +[10]u8 gbuf; + +u8 add8(u8 a, u8 b) { return a + b; } + +fn scalars() { + u8 a = 250; + i8 s = 100; + i16 h = 32767; + u32 w = 4294967295; + a = a + 10; put_n(a); put_c(' '); // 4 + s = s + 100; put_n(s); put_c(' '); // -56 + h++; put_n(h); put_c(' '); // -32768 + w++; put_n(w); put_c(' '); // 0 + put_n(gbyte); put_c(' '); put_n(gneg); put_c(' '); // 44 -56 + put_n(add8(200, 100)); put_c(' '); // 44 + put_n((i8)200); put_c(' '); put_n(300 as u8); put_c(10); // -56 44 + return 0; +} +fn arrays() { + u8 buf[20]; + i16 sh[7]; + u32 ws[3]; + i8 sb[9]; + int i = 0, sum = 0; + while (i < 20) { buf[i] = i * 20; i++; } // wraps past 255 + i = 0; while (i < 20) { sum = sum + buf[i]; i++; } + put_n(sum); put_c(' '); // 1250 + sh[3] = 40000; put_n(sh[3]); put_c(' '); // -25536 + sh[3] += 30000; put_n(sh[3]); put_c(' '); // 4464 + ws[1] = 5000000000; put_n(ws[1]); put_c(' '); // 705032704 + sb[8] = 127; sb[8]++; put_n(sb[8]); put_c(' '); // -128 + sb[0] = 200; put_n(sb[0] < 0); put_c(' '); // 1: -56 is negative + ws[0] = 4000000000; put_n(ws[0] + ws[0]); put_c(' '); // arithmetic is done in int: 8000000000 + put_n(sizeof(buf)); put_c(' '); // 3 words + put_n(sizeof(sh)); put_c(10); // 2 words + return 0; +} +fn heap() { + u8* p = new(u8, 1000); + int i = 0, sum = 0; + while (i < 1000) { p[i] = i; i++; } + i = 0; while (i < 1000) { sum = sum + p[i]; i++; } + put_n(sum); put_c(' '); // 1000 bytes of 0..255: 3 * 32640 + 28 * ... = 127.. + put_n(osize(p) < 256); put_c(32); // 125 words plus the header, not 1000 + *p = 77; put_n(p[0]); put_c(' '); + free(d*p); + gbuf[9] = 255; gword[4] = 65535; gword[4]++; + put_n(gbuf[9]); put_c(' '); put_n(gword[4]); put_c(10); + return 0; +} +fn fields() { + pix* q = new(pix); + q.r = 255; q.g = 256; q.dx = 65535; q.tag[2] = 7; q.tag[3] = 300; + put_n(q.r); put_c(' '); put_n(q.g); put_c(' '); put_n(q.dx); put_c(' '); + put_n(q.tag[2]); put_c(' '); put_n(q.tag[3]); put_c(' '); put_n(sizeof(pix)); put_c(10); + free(d*q); + return 0; +} +o3 fn checksum(u8* p, int n) { + int i = 0, a = 1, b = 0; + while (i < n) { a = (a + p[i]) % 251; b = (b + a) % 251; i++; } + return b * 256 + a; +} +fn o3run() { + u8* p = new(u8, 5000); + int i = 0; + while (i < 5000) { p[i] = i * 7; i++; } + put_n(checksum(p, 5000)); put_c(10); + free(d*p); + return 0; +} +fn main() { + scalars(); arrays(); heap(); fields(); o3run(); + return 0; +} diff --git a/tests/programs/sizedvec.expected b/tests/programs/sizedvec.expected new file mode 100644 index 0000000..9c373cc --- /dev/null +++ b/tests/programs/sizedvec.expected @@ -0,0 +1,2 @@ +100 11866 181 44 181 99 -25536 0 -5 4464 0 2.500000 1.100000 1400 1 +33586 diff --git a/tests/programs/sizedvec.zc b/tests/programs/sizedvec.zc new file mode 100644 index 0000000..2055ade --- /dev/null +++ b/tests/programs/sizedvec.zc @@ -0,0 +1,39 @@ +// growable arrays of the sized types and f32: []u8, []i16, []f32 +fn put_f(x) { asm { x @ #1220 } return 0; } +fn sp() { put_c(32); return 0; } +fn main() { + []u8 b = new([]u8, 0); + []i16 h = new([]i16, 3); + []f32 f = new([]f32, 0); + int i = 0, s = 0; + while (i < 100) { push(b, i * 7); i++; } + i = 0; + while (i < 100) { s = s + b[i]; i++; } + put_n(len(b)); sp(); put_n(s); sp(); put_n(b[99]); sp(); + b[5] = 300; put_n(b[5]); sp(); + put_n(pop(b)); sp(); put_n(len(b)); sp(); + h[0] = 40000; h[2] = 0 - 5; push(h, 70000); + put_n(h[0]); sp(); put_n(h[1]); sp(); put_n(h[2]); sp(); put_n(h[3]); sp(); + push(f, 0.1); push(f, 2.5); push(f, 1.0); + put_n(f[0] == 0.1); sp(); put_f(f[1]); sp(); put_f(f[0] + f[2]); sp(); + resize(b, 3); resize(b, 12); + put_n(b[2]); put_n(b[3]); put_n(b[11]); sp(); + put_n(cap(b) >= 12); + put_c(10); + free(d*b); free(d*h); free(d*f); + second(); + return 0; +} +o3 int sumv([]u8 v) { + int s = 0; + for (int i = 0; i < len(v); i++) s += v[i]; + return s; +} +fn second() { + []u8 v = new([]u8, 0); + int i = 0; + while (i < 300) { push(v, i); i++; } + put_n(sumv(v)); put_c(10); + free(d*v); + return 0; +} diff --git a/tests/programs/static.zc b/tests/programs/static.zc new file mode 100644 index 0000000..ef54339 --- /dev/null +++ b/tests/programs/static.zc @@ -0,0 +1,2 @@ +struct Str { int len; static fn size(s) { return s * 2; } static fn none() { return 5; } fn get() { return this.len; } } +int main() { Str a; a.len = 9; put_n(Str.size(7)); put_c(32); put_n(Str.none()); put_c(32); put_n(a.get()); put_c(10); return 0; } diff --git a/tests/programs/stdbuf.zc b/tests/programs/stdbuf.zc new file mode 100644 index 0000000..ab8f9dc --- /dev/null +++ b/tests/programs/stdbuf.zc @@ -0,0 +1,18 @@ +include "src/lib/std.zc"; +fn main() { + let a = arena(8), p, q, b, i = 0, s = 0; + p = Arena.alloc(a, 4); + q = Arena.alloc(a, 4); + put_n(q - p); put_c(' '); + Arena.reset(a); + put_n(Arena.alloc(a, 4) == p); put_c(' '); + b = bnew(8); + while (i < 4) { Buf.set(b, i, 'A' + i); i++; } + i = 0; + while (i < Buf.length(b)) { s = s + Buf.get(b, i); i++; } + put_n(s); put_c(' '); + put_n(Buf.capacity(b)); put_c(10); + free(d*a); + free(d*b); + return 0; +} diff --git a/tests/programs/stdmore.zc b/tests/programs/stdmore.zc new file mode 100644 index 0000000..fb225ec --- /dev/null +++ b/tests/programs/stdmore.zc @@ -0,0 +1,32 @@ +include "src/lib/std.zc"; +fn main() { + let buf[32]; + put_n(Text.len("hello")); put_c(' '); + put_n(Text.eq("abc", "abc")); put_c(' '); + put_n(Text.eq("abc", "abd")); put_c(' '); + put_n(Text.eq("ab", "abc")); put_c(' '); + put_n(Text.copy(buf, "copy me")); put_c(' '); + put_s(buf); put_c(' '); + put_n(Text.find("needle in a haystack", "hay")); put_c(' '); + put_n(Text.find("abc", "zz")); put_c(' '); + put_n(Text.find("abc", "")); put_c(10); + Text.copy(buf, "MiXeD 123"); + put_n(Text.lower(buf)); put_c(' '); + put_s(buf); put_c(' '); + put_n(Text.upper(buf)); put_c(' '); + put_s(buf); put_c(10); + Text.copy(buf, " \t padded line \r\n"); + put_n(Text.trim(buf)); put_c(' '); + put_c('['); put_s(buf); put_c(']'); put_c(10); + put_n(Num.abs(0 - 42)); put_c(' '); + put_n(Num.min(3, 9)); put_c(' '); + put_n(Num.max(3, 9)); put_c(' '); + put_n(Num.clamp(50, 0, 10)); put_c(' '); + put_n(Num.gcd(0 - 84, 36)); put_c(' '); + put_n(Num.pow(3, 4)); put_c(' '); + put_n(Num.isqrt(99)); put_c(' '); + put_n(Num.isqrt(100)); put_c(' '); + put_n(Num.sign(0 - 5)); put_c(' '); + put_n(Num.sign(0)); put_c(10); + return 0; +} diff --git a/tests/programs/strings.zc b/tests/programs/strings.zc new file mode 100644 index 0000000..431bd4e --- /dev/null +++ b/tests/programs/strings.zc @@ -0,0 +1,37 @@ +fn main() { + let a = b"hi", b = b"!", c = a + b; + let mid = c[1:3]; + let neg = s_to_n(b"-42"); + let p = n_to_s(42); + let q = n_to_s(0 - 42); + let ten = s_to_n(b"10"); + let big = n_to_s(1 << 63); + let empty = b""; + let tail = c[2:99]; + let esc = b"a\nb"; + let z = b"" + b"ab"; + put_s(c); put_c(32); + put_n(len(c)); put_c(32); + put_n(len_s(c)); put_c(32); + put_n(c[0]); put_c(32); + put_s(mid); put_c(32); + put_n(c == b"hi!"); put_c(32); + put_n(c != b"hi"); put_c(32); + put_n(c < b"hj"); put_c(32); + put_n(neg); put_c(32); + put_s(p); put_c(32); + put_s(q); put_c(32); + put_n(ten + 1); put_c(32); + put_n(p == b"42"); put_c(32); + put_s(big); put_c(32); + put_n(len(empty)); put_c(32); + put_n(len(tail)); put_c(32); + put_n(len_s(esc)); put_c(32); + put_n(at_s(esc, 1)); put_c(32); + put_n(b"ab" > b"a"); put_c(32); + put_n(b"ab" >= b"ab"); put_c(32); + put_n(b"a" <= b"a"); put_c(32); + put_s("W"); put_c(32); + put_s(z); put_c(10); + return 0; +} diff --git a/tests/programs/structret.zc b/tests/programs/structret.zc new file mode 100644 index 0000000..708c83c --- /dev/null +++ b/tests/programs/structret.zc @@ -0,0 +1,24 @@ +// structs returned from native functions: the stub makes the result (a new object, +// owned by the caller) and the native call fills it in. the helper library is built +// by test.sh (build/sbv.so). +fn put_f(x) { asm { x @ #1220 } return 0; } +struct Pair { int a; int b; } +struct Vec { float x; float y; } +struct Trio { int a; int b; int c; } +struct Mixed { int a; float x; } +struct Vec3 { float x; float y; float z; } +extern struct Pair mkpair(long a, long b); +extern struct Vec mkvec(double x, double y); +extern struct Trio mktrio(long a, long b, long c); +extern struct Mixed mkmixed(long a, double x); +extern struct Vec3 mkv3(double x); +int main() { + Pair* p; Vec* v; Trio* t; Mixed* m; Vec3* w; + cload(b"build/sbv.so"); + p = mkpair(3, 4); put_n(p.a); put_c(32); put_n(p.b); put_c(32); + v = mkvec(2.5, 3.0); put_f(v.x); put_c(32); put_f(v.y); put_c(32); + t = mktrio(7, 8, 9); put_n(t.a + t.b + t.c); put_c(32); + m = mkmixed(7, 0.5); put_n(m.a); put_c(32); put_f(m.x); put_c(32); + w = mkv3(1.5); put_f(w.z); put_c(10); + return 0; +} diff --git a/tests/programs/structval.zc b/tests/programs/structval.zc new file mode 100644 index 0000000..a1bd041 --- /dev/null +++ b/tests/programs/structval.zc @@ -0,0 +1,26 @@ +// structs by value in native calls: a zc struct of word fields is passed to a c +// function as its words. the helper library is built by test.sh (build/sbv.so). +struct Pair { int a; int b; } +struct Vec { float x; float y; } +struct Trio { int a; int b; int c; } +extern long pair_sum(struct Pair p); +extern long pair_sum2(struct Pair p, struct Pair q); +extern long vec_scaled(struct Vec v, long k); +extern long many(long a, long b, long c, long d, long e, long f, struct Pair p); +extern long trio_sum(long a, long b, long c, long d, long e, long f, struct Trio t); +extern long eight(long a, long b, long c, long d, long e, long f, long g, long h, struct Pair p); +int main() { + Pair p; Pair q; Vec v; Trio t; + cload(b"build/sbv.so"); + p.a = 3; p.b = 4; + put_n(pair_sum(p)); put_c(32); + q.a = 10; q.b = 20; + put_n(pair_sum2(p, q)); put_c(32); + v.x = 1.5; v.y = 2.0; + put_n(vec_scaled(v, 2)); put_c(32); + put_n(many(1, 2, 3, 4, 5, 6, p)); put_c(32); + t.a = 7; t.b = 8; t.c = 9; + put_n(trio_sum(1, 2, 3, 4, 5, 6, t)); put_c(32); + put_n(eight(1, 2, 3, 4, 5, 6, 7, 8, p)); put_c(10); + return 0; +} diff --git a/tests/programs/sys.zc b/tests/programs/sys.zc new file mode 100644 index 0000000..52924fa --- /dev/null +++ b/tests/programs/sys.zc @@ -0,0 +1,11 @@ +include "src/lib/std.zc"; +fn main() { + let s, n; + n = Host.writefile("/tmp/zc-sys-out.txt", "zy"); + s = readfile("/tmp/zc-sys-out.txt"); + put_n(n); put_c(' '); put_s(s); put_c(' '); + if (Host.now() > 1000000000) put_s("time\n"); + else put_s("notime\n"); + free(d*s); + return 0; +} diff --git a/tests/programs/tagenum.zc b/tests/programs/tagenum.zc new file mode 100644 index 0000000..f438c5b --- /dev/null +++ b/tests/programs/tagenum.zc @@ -0,0 +1,26 @@ +// tagged enums: a member can carry fields, a constructor builds it, and a +// switch matches on the member and binds its fields +enum shape { circle { int r; }, rect { int w; int h; }, dot }; + +fn area(shape* s) { + switch (s) { + case circle(r): return r * r * 3; + case rect(w, h): return w * h; + case dot: return 0; + } + return -1; +} + +fn main() { + shape* a = shape.circle(3); + shape* b = shape.rect(4, 5); + shape* c = shape.dot(); + put_n(area(a)); put_c(32); + put_n(area(b)); put_c(32); + put_n(area(c)); put_c(32); + put_n(a.tag); put_c(32); + put_n(b.tag); put_c(32); + put_n(c.tag); put_c(10); + free(d*a); free(d*b); free(d*c); + return 0; +} diff --git a/tests/programs/tagenum_two.zc b/tests/programs/tagenum_two.zc new file mode 100644 index 0000000..eef45dd --- /dev/null +++ b/tests/programs/tagenum_two.zc @@ -0,0 +1,20 @@ +// two tagged enums may share a member name: each keeps its own fields and tag +enum a { item { int x; }, other }; +enum b { item { int y; }, nope }; +o3 int tot(a* p, int n) { + int t = 0; + for (int i = 0; i < n; i++) { + switch (p) { + case item(x): t += x; + case other: t += 100; + } + } + return t; +} +int main() { + a* p = a.item(3); + b* q = b.item(8); + put_n(p.item_x); put_c(32); put_n(q.item_y); put_c(32); put_n(tot(p, 4)); put_c(10); + free(d*p); free(d*q); + return 0; +} diff --git a/tests/programs/tagenum_warn.zc b/tests/programs/tagenum_warn.zc new file mode 100644 index 0000000..f50ab7c --- /dev/null +++ b/tests/programs/tagenum_warn.zc @@ -0,0 +1,17 @@ +// a switch on a tagged enum that leaves out a member, with no default, warns +enum shape { circle { int r; }, rect { int w; int h; }, dot }; + +fn name(shape* s) { + switch (s) { + case circle(r): return 1; + case dot: return 2; + } + return 0; +} + +fn main() { + shape* a = shape.rect(2, 2); + put_n(name(a)); put_c(10); + free(d*a); + return 0; +} diff --git a/tests/programs/typedmethods.zc b/tests/programs/typedmethods.zc new file mode 100644 index 0000000..facd9f1 --- /dev/null +++ b/tests/programs/typedmethods.zc @@ -0,0 +1,16 @@ +// struct methods declare a return type and typed parameters, like functions do +struct Str { + int len; + static int size(int s) { return s * 2; } + int plus(int k) { return this.len + k; } + static fn none() { return 5; } +} +int main() { + Str a; + a.len = 9; + put_n(Str.size(7)); put_c(32); + put_n(Str.none()); put_c(32); + put_n(a.plus(4)); + put_c(10); + return 0; +} diff --git a/tests/programs/typeoff.zc b/tests/programs/typeoff.zc new file mode 100644 index 0000000..d82bca8 --- /dev/null +++ b/tests/programs/typeoff.zc @@ -0,0 +1,2 @@ +// the typechecker is off unless asked for: this mismatch compiles and runs +fn main() { char* s = 5; put_n(s as int + 1); put_c(10); return 0; } diff --git a/tests/programs/typeok.zc b/tests/programs/typeok.zc new file mode 100644 index 0000000..26ab3ad --- /dev/null +++ b/tests/programs/typeok.zc @@ -0,0 +1,21 @@ +// passes the typechecker: words mix with everything, literal 0 is null, +// arrays decay to pointers, generic calls bind their parameters +typecheck on; +struct pair { A a; B b; }; +pair*.u mk(A x, B y) { pair* p = new(pair); p.a = x; p.b = y; return u*p; } +A left(pair* p) { return p.a; } +int strlen2(char* s) { int n = 0; while (s[n]) n++; return n; } +int main() { + pair* p = mk(3, "four"); + int a = left(p); + char* s = p.b; + [8]char buf; + char* q = buf; + int* nothing = 0; + let w = 5; + int mixed = w + 1; + q[0] = 'h'; q[1] = 0; + put_n(a + strlen2(s) + strlen2(buf) + mixed); put_c(10); + free(d*p); + return 0; +} diff --git a/tests/programs/types.zc b/tests/programs/types.zc new file mode 100644 index 0000000..86488db --- /dev/null +++ b/tests/programs/types.zc @@ -0,0 +1,55 @@ +struct point { int x, y; }; +struct rect { point a, b; int tag; }; +struct list { int len; T* items; }; +struct node { int v; node* next; }; +type num = int; + +int counter = 5; +[4]int squares; +point origin; + +int area(rect* r) { return (r.b.x - r.a.x) * (r.b.y - r.a.y); } +list*.u mklist(int cap) { + list* l = new(list); + l.len = 0; + l.items = new(T, cap) as T*; + return u*l; +} +T first(list* l) { return l.items[0]; } +int sum(node* n) { int s = 0; while (n) { s += n.v; n = n.next; } return s; } + +fn main() { + rect r; + r.a.x = 1; r.a.y = 2; r.b.x = 4; r.b.y = 6; + put_n(area(&r)); put_c(' '); + put_n(sizeof(rect)); put_c(' '); put_n(sizeof(point)); put_c(' '); put_n(sizeof([3]rect)); put_c(' '); + let p = new(point); // let infers *point + p.x = 7; p->y = 8; + put_n(p.x * p.y); put_c(' '); + free(d*p); + list* l = mklist(4); + l.items[0] = 42; l.len = 1; + put_n(first(l)); put_c(' '); + free(d*l); + node* a = new(node), b = new(node); + a.v = 3; b.v = 4; a.next = b; + put_n(sum(a)); put_c(' '); + free(d*a); // b went with it: a.next = b made a its owner + [3]point pts; + pts[2].y = 9; + put_n(pts[2].y + counter); put_c(' '); + num k = 3; + put_n(k); put_c(10); + + // reflection + tprint(typeof(r)); put_c(' '); tprint(typeof(&r)); put_c(' '); tprint(typeof(l)); put_c(' '); + tprint(sigof(mklist)); put_c(' '); tprint(sigof(first)); put_c(' '); tprint(sigofs("free")); put_c(10); + let t = typeof(r), i = 0; + while (i < tnfields(t)) { put_s(tfname(t, i)); put_c(':'); tprint(tftype(t, i)); put_c('@'); put_n(tfoff(t, i)); put_c(' '); i++; } + put_c(10); + point* q = new(point); + tprint(typeof_obj(q)); put_c(' '); put_n(typeof_obj(q) == typeof(point)); put_c(' '); put_n(tkind(typeof_obj(q)) == TY_STRUCT); + free(d*q); + put_c(10); + return 0; +} diff --git a/tests/programs/uaf.zc b/tests/programs/uaf.zc new file mode 100644 index 0000000..4c14e91 --- /dev/null +++ b/tests/programs/uaf.zc @@ -0,0 +1,8 @@ +memdebug on; +unchecked fn main() +{ + let p = new(2); + p[0] = 42; + free(p); + return p[0]; +} diff --git a/tests/programs/uaf_in.zc b/tests/programs/uaf_in.zc new file mode 100644 index 0000000..89ad9f9 --- /dev/null +++ b/tests/programs/uaf_in.zc @@ -0,0 +1,9 @@ +memdebug on; +unchecked fn main() +{ + let p = new(4), r; + p[2] = 5; + r = p + 2; + free(p); + return r[0]; +} diff --git a/tests/programs/umd.zc b/tests/programs/umd.zc new file mode 100644 index 0000000..fb1640e --- /dev/null +++ b/tests/programs/umd.zc @@ -0,0 +1,50 @@ +// lists as [len, items...], items owned by the list +.u mklist(n) { + let l = new(n + 1); + l[0] = 0; + return u*l; +} +fn push(.m l, .d item) { l[l[0] + 1] = d*item; l[0] = l[0] + 1; return 0; } +fn sum(.m l) { let s = 0, i = 1; while (i <= l[0]) { s += l[i][0]; i++; } return s; } +.u box(v) { let b = new(1); b[0] = v; return u*b; } +fn consume(.d b) { return b[0]; } + +fn main() { + let base = live_objects(); + { + let l = mklist(8); // u from mklist arrives m*here + let i = 0; + while (i < 5) { + let b = box(i * 10); // m in the loop body + d*b; // give it down + push(l, b); + i++; + } + put_n(sum(l)); put_c(' '); + put_n(live_objects() - base); put_c(' '); // list + 5 boxes + } + put_n(live_objects() - base); put_c(' '); // freed at the block's end, children too + + // u handed up one block by assignment, and as a block value + let outer; + { + let t = box(7); + u*t; + outer = t; + } + let v = { let w = box(8); u*w }; + put_n(outer[0] + v[0]); put_c(' '); + + // d given away to a callee + let g = box(99); + put_n(consume(d*g)); put_c(' '); + + // containers: child takes its parent's tag + let p = new(2), c = new(1); + c[0] = 5; + p[0] = c; + p[1] = new(3); + put_n(p[0][0]); put_c(' '); + put_n(live_objects() - base); put_c(10); + return 0; +} diff --git a/tests/programs/undefined_call.zc b/tests/programs/undefined_call.zc new file mode 100644 index 0000000..e6a3b48 --- /dev/null +++ b/tests/programs/undefined_call.zc @@ -0,0 +1 @@ +fn main() { nope(); return 0; } diff --git a/tests/programs/use_std.zc b/tests/programs/use_std.zc new file mode 100644 index 0000000..c15e49b --- /dev/null +++ b/tests/programs/use_std.zc @@ -0,0 +1,9 @@ +include "src/lib/std.zc"; +fn main() { + let out[12]; + Text.itoa(42, out); + put_s(out); put_c(' '); + put_n(Text.atoi("-7")); put_c(' '); + put_n(Text.starts("hello", "he")); put_c(10); + return 0; +} diff --git a/tests/programs/wide.zc b/tests/programs/wide.zc new file mode 100644 index 0000000..c20004b --- /dev/null +++ b/tests/programs/wide.zc @@ -0,0 +1,9 @@ +// a word is 64 bits: shifts past 31, and equality uses bit 63 +fn main() { + put_n(1 << 40); put_c(' '); + put_n((1 << 40) >> 40); put_c(' '); + put_n((1 << 40) == (1 << 40)); put_c(' '); + put_n((1 << 40) != 0); put_c(' '); + put_n(~0 >> 63); put_c(10); + return 0; +} diff --git a/tests/programs/zc0plain.zc b/tests/programs/zc0plain.zc new file mode 100644 index 0000000..1092f76 --- /dev/null +++ b/tests/programs/zc0plain.zc @@ -0,0 +1,23 @@ +// the stage 0 compiler (src/tools/zc0.c) has no types. it reads them and throws +// them away, so a typed program and the same program without the types come +// out as the same image. tests/programs/zc0typed.zc is this file with the types on. +let total; +let table[8]; +let names; +const LIMIT = 10; + +fn add(a, b) { return a + b; } +.u grow(v, n) { return v; } +fn keep(.d x, .m y) { return x != y; } +fn apply(f, p, q) { return f(*p); } +fn sq(x) { return x * x; } + +fn start() { + let i = 0, sum = 0; + let a[4]; + let p = a; + for (let j = 0; j < LIMIT; j++) sum = add(sum, sq(j)); + while (i < 4) { a[i] = i; i++; } + total = sum + a[3] + table[2]; + return total; +} diff --git a/tests/programs/zc0typed.zc b/tests/programs/zc0typed.zc new file mode 100644 index 0000000..4b7f438 --- /dev/null +++ b/tests/programs/zc0typed.zc @@ -0,0 +1,23 @@ +// the stage 0 compiler (src/tools/zc0.c) has no types. it reads them and throws +// them away, so a typed program and the same program without the types come +// out as the same image. tests/programs/zc0plain.zc is this file with the types off. +int total; +[8]int table; +[]word names; +const int LIMIT = 10; + +int add(int a, int b) { return a + b; } +.u grow([]word v, int n) { return v; } +bool keep(int.d x, .m y) { return x != y; } +int apply(int(int) f, int* p, vec>*.u q) { return f(*p); } +int sq(int x) { return x * x; } + +fn start() { + int i = 0, sum = 0; + [4]int a; + int* p = a; + for (int j = 0; j < LIMIT; j++) sum = add(sum, sq(j)); + while (i < 4) { a[i] = i; i++; } + total = sum + a[3] + table[2]; + return total; +} diff --git a/tests/runerr.sh b/tests/runerr.sh new file mode 100755 index 0000000..23ea054 --- /dev/null +++ b/tests/runerr.sh @@ -0,0 +1,10 @@ +#!/bin/bash +# runs every .zc in a folder (tests/errors by default) and checks the compiler +# rejects it with the message on its first line: // expect: ... +vm=${1:-zy}; dir=${2:-$(dirname "$0")/errors}; pass=0; fail=0 +for f in "$dir"/*.zc; do + want=$(head -1 "$f" | sed 's#^// expect: ##') + got=$($vm < "$f" 2>&1 | head -1) + if [[ "$got" == *"$want"* ]]; then pass=$((pass+1)); else fail=$((fail+1)); echo "FAIL $(basename $f): got [$got]"; fi +done +echo "rejected: $pass passed, $fail failed" diff --git a/tests/test.sh b/tests/test.sh new file mode 100755 index 0000000..27d60f7 --- /dev/null +++ b/tests/test.sh @@ -0,0 +1,577 @@ +#!/bin/bash +# full check: bootstraps the compiler from zc0, verifies the fixpoint, then runs +# the examples and tests on the portable interpreter (host), the microcore, the +# arm64 jit under qemu-user, and the llvm backend. needs gcc. the jit half +# needs aarch64-linux-gnu-gcc and qemu-aarch64 (linux-user, not qemu-system). +# the llvm half needs llvm-config and libLLVM. src/baremetal/run.sh boots the +# microcore on qemu-system-riscv64 and qemu-system-aarch64 when those cross +# compilers and system emulators are installed. each of these is skipped +# when its tools are missing. +# +# -j N runs N sections at a time, where N is how many cores to use. the +# sections are independent and each gets its own scratch directory, so they +# can overlap; the output still reads in the same order either way. -j 1 is +# the default, and is what make test does unless JOBS says otherwise. +# +# -q (make check) is the minimal suite for iterating: the bootstrap and the +# fixpoint, the interpreter section and the language server's own checks. +# it skips the other backends, the node protocol and corpus tests, and the +# js and make sections. run the whole thing (no -q) when the work is done. +cd "$(dirname "$0")/.."; T=$(mktemp -d); trap 'rm -rf $T' EXIT +ok=0; bad=0 +chk(){ if [ "$1" = "$2" ]; then echo "ok $3"; ok=$((ok+1)); else echo "FAIL $3: got [$1]"; bad=$((bad+1)); fi; } +src(){ cat src/runtime/rt.zc src/compiler/compiler.zc; } +# -j N runs N sections at a time: j is how many cores to use. each section +# writes its own log, so the output still reads in the order it always did. +JOBS=1; QUICK=0; SELECT="" +while [ $# -gt 0 ]; do + case "$1" in + -j) JOBS=$2; shift 2;; + -j*) JOBS=${1#-j}; shift;; + -q) QUICK=1; shift;; + --section) SELECT=$2; shift 2;; + *) echo "usage: $0 [-q] [-j N] [--section NAME]"; exit 2;; + esac +done +case "$JOBS" in ''|*[!0-9]*|0) JOBS=1;; esac +# each section gets its own scratch directory, so none of them can tread on +# another's files when they overlap +SECS="interp micro small runonly bare jit llvm lsp js make learn" +[ $QUICK = 1 ] && SECS="interp lsp learn" +if [ -n "$SELECT" ]; then + case " $SECS " in *" $SELECT "*) SECS=$SELECT;; *) echo "unknown test section: $SELECT" >&2; exit 2;; esac +fi +ROOT=$T + +# built here rather than inside a section: interp runs the program list on +# it, and the run-only and bare-metal sections read it as the reference. +gcc -O2 -w -fwrapv -DJTYPE=int src/core/core.c src/core/io_linux.c -Isrc/core -o $T/zy || exit 1 +gcc -O2 -w -fwrapv src/tools/genmap.c -Isrc/core -o $T/genmap || exit 1 +# the c helper for tests/programs/structval.zc: structs by value, loaded with cload +mkdir -p build && gcc -shared -fPIC -w tests/data/sbv.c -o build/sbv.so || exit 1 + +selfc(){ (cat src/tools/build.zc; printf '`'; src) | $1 > $2; } + +# bootstrap: zc0 (c) -> g0, g0 compiles compiler.zc -> g1, g1 compiles it -> g2 +gcc -O1 -w -fwrapv src/tools/zc0.c -Isrc/core -o $T/zc0 && gcc -O1 -w -fwrapv src/tools/mkgenome.c -Isrc/core -o $T/mkg || exit 1 +mkdir $T/s0 $T/s1 +cp src/core/core.c src/core/io_linux.c src/core/memmap.h src/core/fops.h src/core/bops.h src/core/stack.h $T/s0/; cp src/core/core.c src/core/io_linux.c src/core/memmap.h src/core/fops.h src/core/bops.h src/core/stack.h $T/s1/ +src | $T/zc0 > $T/s0/genome.h || exit 1 +gcc -O2 -w -fwrapv -DJTYPE=int $T/s0/core.c $T/s0/io_linux.c -o $T/zy0 +selfc $T/zy0 $T/g1.bin; $T/mkg < $T/g1.bin > $T/s1/genome.h +gcc -O2 -w -fwrapv -DJTYPE=int $T/s1/core.c $T/s1/io_linux.c -o $T/zy1 +selfc $T/zy1 $T/g2.bin +cmp -s $T/g1.bin $T/g2.bin && chk 1 1 "fixpoint: the compiler compiles itself to the same $(($(wc -c < $T/g1.bin) / 8)) words" || chk 0 1 "fixpoint" +cmp -s $T/s1/genome.h src/core/genome.h && chk 1 1 "genome.h is up to date" || chk 0 1 "genome.h is stale (cp the bootstrap output over it)" +chk "$($T/zc0 < tests/programs/zc0typed.zc | md5sum)" "$($T/zc0 < tests/programs/zc0plain.zc | md5sum)" "zc0 reads types and throws them away: typed and plain sources give one image" + +for n in $SECS; do + mkdir -p $ROOT/$n + cp $ROOT/g1.bin $ROOT/$n/ # the bootstrap's image, read by micro, jit and llvm + cp $ROOT/zy $ROOT/$n/zy # the interpreter, built once above + cp $ROOT/genmap $ROOT/$n/genmap +done + +run(){ # $1 = vm command, $2 = label + local vm="$1" l="$2" + chk "$($vm < examples/hello.zc)" "hello, world!" "$l hello" + chk "$($vm < examples/fact.zc | tr '\n' '/')" "1101110101111100000000/3628800/" "$l factorial" + chk "$( (cat examples/echo.zc; printf '`abc\n') | $vm)" "abc" "$l echo after the backtick" + chk "$( (cat tests/programs/backtick.zc; printf '`xyz') | $vm)" "3 3 96 39 xyz" "$l a backtick in a comment, string or char literal does not end the source" + chk "$($vm < examples/selfpatch.zc)" "ABC" "$l self-patching code" + chk "$($vm < examples/lists.zc | tr '\n' '/')" "[0 1 4 9 16 25]/live: 7/after the block: 0/" "$l ownership frees everything" + chk "$( (cat examples/repl.zc; printf '`fn sq(x) { return x * x; }`put_n(sq(7)); put_c(10);`put_s("hi\\n");') | $vm | tr '\n' '/')" "49/hi/" "$l repl on the resident compiler" + chk "$($vm < examples/typed.zc | tr '\n' '/')" "last: 4/point is 2 words: x int, y int/vec is 3 words: len int, cap int, items point**/push is bool(vec*, T.d)/the heap says v is a vec, and v.items[0] is a point/" "$l typed example" + chk "$($vm < examples/wiring.zc)" "16 1234 25 4/9" "$l enhancement example" + chk "$($vm < tests/programs/allocfast.zc)" "0 0 499500 0" "$l constant-size allocation fast path" + chk "$($vm < tests/programs/basics.zc | tr '\n' '/')" "285 610 42 512/hey 65 -3 -2 1024 -8/12456/757/9 100-101/5/ok/" "$l language basics" + chk "$($vm < tests/programs/umd.zc)" "100 6 0 15 99 5 5" "$l u m d lifetimes" + chk "$($vm < tests/programs/names.zc)" "12 1 8" "$l u, m and d are ordinary names" + chk "$($vm < tests/programs/gc.zc)" "1000 1 900 1 199900" "$l mark-sweep collector" + chk "$($vm < tests/programs/meta.zc | tr '\n' '/')" "1024 19/unless ok/rrr/15 42/7/42/7/144/" "$l parse words, quote, eval, load, operator tables" + chk "$($vm < tests/programs/misc.zc | tr '\n' '/')" "45/54321/ok/" "$l compile-time eval, patched mnemonics, unchecked" + chk "$($vm < tests/programs/types.zc | tr '\n' '/')" "12 5 2 15 56 42 7 14 3/rect rect* list* list*.u(int) T(list*) fn(word.d)/a:point@0 b:point@2 tag:int@4 /point 1 1/" "$l types, generics, reflection" + chk "$($vm < tests/programs/typeok.zc)" "14" "$l a program the typechecker accepts" + chk "$($vm < tests/programs/typeoff.zc)" "6" "$l the typechecker is off by default" + chk "$($vm < tests/programs/enhance.zc)" "100 0 343 3,2 5 -2 0" "$l virtual enhancements" + chk "$($vm < tests/programs/joins.zc)" "0 0 5 80 8 3 9 0" "$l joins free what one path gave away; switch, ?:, no prototypes" + chk "$($vm < tests/programs/buddy.zc)" "131072 262144 1 0" "$l buddy allocator merges freed blocks" + chk "$($vm < tests/programs/growheap.zc)" "144 1 7 1" "$l the heap grows past its initial size and a bigger block fits after the frees" + chk "$($vm < tests/programs/sized.zc | md5sum)" "$(md5sum < tests/programs/sized.expected)" "$l sized integers wrap, and arrays and pointers of them are packed" + chk "$($vm < tests/programs/bitops.zc | md5sum)" "$(md5sum < tests/programs/bitops.expected)" "$l ctz, popcnt, bswap, rotates, mulh and float32 conversion micro-ops" + chk "$($vm < tests/programs/f32.zc | md5sum)" "$(md5sum < tests/programs/f32.expected)" "$l f32: single precision scalars and packed arrays" + chk "$($vm < tests/programs/f32o3.zc)" "10718.750000 1.600000 0" "$l f32 inside an o3 region" + chk "$($vm < tests/programs/sizedvec.zc | md5sum)" "$(md5sum < tests/programs/sizedvec.expected)" "$l growable arrays of the sized types and f32" + chk "$($vm < tests/programs/packedfields.zc | md5sum)" "$(md5sum < tests/programs/packedfields.expected)" "$l struct fields of the sized types pack into words" + chk "$($vm < tests/programs/generics.zc | md5sum)" "$(md5sum < tests/programs/generics.expected)" "$l generic functions are compiled per type arguments" + chk "$($vm < tests/programs/genmeth.zc | md5sum)" "$(md5sum < tests/programs/genmeth.expected)" "$l methods of generic structs are compiled per type arguments" + chk "$($vm < tests/programs/static.zc)" "14 5 9" "$l static functions of a struct are called as Struct.name(args)" + chk "$($vm < tests/programs/methodown.zc)" "7 9" "$l struct methods and functions return owned objects (.u)" + chk "$($vm < tests/programs/typedmethods.zc)" "14 5 13" "$l struct methods declare a return type and typed parameters" + chk "$($vm < tests/programs/enumscope.zc)" "0 5 6 blue" "$l enum members are reached as Enum.MEMBER, also in a case" + chk "$($vm < tests/programs/enumtype.zc)" "6 5 1" "$l typed enums: members take their enum, an enum becomes an int" + chk "$($vm < tests/programs/tagenum.zc)" "27 20 0 0 1 2" "$l tagged enums: members carry fields, constructors build them, a case binds the fields" + chk "$($vm < tests/programs/tagenum_warn.zc)" "$(printf 'zc: line 5: warning: this switch doesn'"'"'t cover rect\n0')" "$l a switch on a tagged enum that misses a member warns, on the switch's line" + chk "$($vm < tests/programs/tagenum_two.zc)" "3 8 12" "$l two tagged enums with the same member name, and a case in an o3 loop" + chk "$($vm < tests/programs/o3push.zc | md5sum)" "$(md5sum < tests/programs/o3push.expected)" "$l push and pop of a growable array are inline in o3" + chk "$($vm < tests/programs/o3copy.zc)" "520507 1005" "$l o3 copies of a loop variable read the source instead" + chk "$($vm < tests/programs/borrowmove.zc)" "6" "$l storing into a borrowed object moves it" + chk "$($vm < tests/programs/alias.zc)" "11 22 4 8" "$l aliases of a live owner: arithmetic, field, index" + chk "$($vm < tests/programs/quarantine.zc)" "0 1234" "$l quarantine: stale alias reads intact data, second free is a no-op" + chk "$($vm < tests/programs/recycle.zc)" "1 0 1" "$l recycle returns a dropped block to its size class" + chk "$($vm < tests/programs/roots.zc)" "1 0" "$l the collector marks the live stack, and scrub clears the rest" + chk "$($vm < tests/programs/arena.zc)" "4 7 0 1 1" "$l bump arena allocates, rewinds, and refuses a bump past the end" + chk "$($vm < tests/programs/csyn.zc)" "13 0 5 6 0" "$l do/while, enum, and a C cast" + chk "$($vm < tests/programs/use_std.zc)" "42 -7 1" "$l include and the string helpers" + chk "$($vm < tests/programs/stdbuf.zc)" "4 1 266 8" "$l stdlib arena and packed byte buffer" + chk "$($vm < tests/programs/stdmore.zc)" "$(printf '5 1 0 0 7 copy me 12 -1 0\n9 mixed 123 9 MIXED 123\n11 [padded line]\n42 3 9 10 12 81 9 10 -1 0')" "$l stdlib text and num helpers" + chk "$(ZC_FILE=tests/data/hi.txt $vm < examples/cli.zc)" "6 h" "$l reading a file named by the environment" + chk "$(ZC_ARG_TEST=ok $vm < tests/programs/args.zc)" "1 1 1" "$l argv and envp from the host, argv[0] is on stdin" + chk "$(ZC_ARG_TEST=ok $vm < tests/programs/capture.zc)" "0 +1 +15 +hello world!42 +1 +1 +0" "$l capture.start() collects output and end() hands back the string" + chk "$($vm < tests/programs/sys.zc)" "2 zy time" "$l syscalls: write, read, time" + chk "$($vm < tests/programs/memleak.zc)" $'leak: line 5 3 words\n1' "$l memdebug names a live allocation" + chk "$($vm < tests/programs/uaf.zc)" $'zc: line 7: warning: use after free: this storage\'s lifetime ended on line 6\nzy: use after free from line 4' "$l memdebug: use after free" + chk "$($vm < tests/programs/uaf_in.zc)" "zy: use after free from line 4" "$l memdebug: use after free through an interior pointer" + chk "$($vm < tests/programs/doublefree.zc)" $'zy: double free from line 4\nok' "$l memdebug: double free is reported and does nothing" + chk "$($vm < tests/programs/scrub.zc)" $'zc: line 6: warning: use after free: this storage\'s lifetime ended on line 5\n57005' "$l memdebug scrub poisons a freed payload" + chk "$($vm < tests/programs/interior.zc)" "0" "$l an interior pointer keeps a quarantined block" + chk "$($vm < tests/programs/wide.zc)" "1099511627776 1 1 1 -1" "$l 64-bit shifts and equality" + chk "$($vm < tests/programs/float.zc)" "4.000000 4.500000 3.000000 2.500000 2.500000 0.001500 0.100000 -0.125000 inf -inf nan 1 1 1 0 0 3 -3 3.000000 -2.000000 2" "$l floating point" + chk "$($vm < tests/programs/ftruth.zc)" "00111000110107" "$l negative zero is false" + chk "$($vm < tests/programs/ftype.zc)" "3.500000 10.500000 3.500000 4.250000 5.250000 4.250000 6.500000 2 4 4.250000 8.500000 1 4" "$l float type through calls, globals, fields" + chk "$($vm < tests/programs/fo3.zc)" "5997 5997 4.000000 nan 0 3.250000 1 0 0" "$l o3 lifts float ops" + chk "$($vm < tests/programs/ftext.zc)" "1e+308 1e-10 -0 inf -inf nan 0.1 1.50 1.250e+0 100000000000000000000.000000 1e+40 1111111111" "$l float text: shortest, fixed, scientific, 17 digits" + chk "$($vm < tests/programs/bytes.zc)" "17 34 43981 16909060 1 205 171 -3674937295934316015 1108152156331 0 0 30768 +ABC" "$l byte addressing" + chk "$($vm < tests/programs/o3bytes.zc)" "190 4660 7 2055 16909060 0 0 +ABC" "$l o3 lifts byte loads and stores" + chk "$($vm < tests/programs/o3zero.zc)" "1" "$l untyped new inside o3 zeroes its words" + chk "$($vm < tests/programs/strings.zc)" "hi! 3 3 104 i! 1 1 1 -42 42 -42 11 1 -9223372036854775808 0 1 3 10 1 1 1 W ab" "$l byte strings" + chk "$($vm < tests/programs/share.zc)" "1 2 1 4 1 1 1 1 42 1 1 1 6 0" "$l shared cons cells" + chk "$($vm < tests/programs/share_dbg.zc)" "7 1" "$l memdebug accepts a shared cell" + chk "$($vm < examples/strings.zc)" "hello, zc +9 42" "$l byte string example" + chk "$(ZC_BSTR=zybyte $vm < tests/programs/bstr_io.zc)" "byte-io 7 7 1 6 122 101" "$l byte-string file and getenv" + chk "$($vm < tests/programs/manyerrors.zc | tr '\n' '/')" "zc: line 3: expected an expression, found ';'/zc: line 4: x: m object is never used/zc: line 5: type error: initializer: expected char*, got int/zc: line 6: expected ';', found '}'/zc: 4 errors/" "$l several errors in one run" + chk "$($vm < tests/programs/overflow_return.zc 2>&1)" "zy: return stack overflow" "$l return stack overflow is caught" + chk "$($vm < tests/programs/overflow_data.zc 2>&1)" "zy: data stack overflow" "$l data stack overflow is caught" + chk "$($vm < tests/programs/overflow_frame.zc 2>&1)" "zc: out of heap memory" "$l frame growth stops at a configured memory limit" + chk "$($vm < examples/mandel.zc 2>&1 | md5sum | cut -c1-32)" "f41aecbf2c844168050413cfa832db0e" "$l example: mandelbrot with a fixed-point enhancement" + chk "$($vm < examples/bf.zc 2>&1 | md5sum | cut -c1-32)" "223570983752b17f7ece32e93d5e19e4" "$l example: brainfuck compiled at runtime" + chk "$($vm < examples/dump.zc 2>&1 | md5sum | cut -c1-32)" "757774b00f6b3cd0bfbcba66448a8619" "$l example: struct printer through reflection" + chk "$($vm < examples/tree.zc 2>&1 | md5sum | cut -c1-32)" "b86a2e4e9e31acc294a4ddf86be863f2" "$l example: owning tree frees itself" + chk "$($vm < examples/lang.zc 2>&1 | md5sum | cut -c1-32)" "6b234dc8a0a1aa0fb02eb55a6ff7ccc0" "$l example: pipe operator, unless, assert, times" + chk "$($vm < examples/staged.zc 2>&1 | md5sum | cut -c1-32)" "6f790a125c48b0cc7a897cee73ee5627" "$l example: staged code compiled while running" + chk "$($vm < examples/quine.zc 2>&1 | md5sum | cut -c1-32)" "$(md5sum < examples/quine.zc | cut -c1-32)" "$l example: quine" + chk "$($vm < examples/primes.zc 2>&1 | md5sum | cut -c1-32)" "814faffc70dad0ae520c0671c33e73d4" "$l example: o3 prime sieve" + chk "$($vm < examples/life.zc 2>&1 | md5sum | cut -c1-32)" "aefb036b6f9ad1dadfc3e6bb47a6885d" "$l example: game of life" + chk "$($vm < tests/programs/arrays.zc | tr '\n' '/')" "100 128 9801 9801 99 3 4 12 19 8 19 28/0/" "$l growable and fixed arrays" + chk "$($vm < tests/programs/o3.zc)" "3142 42 43 9 57 98781 520625 10192 -18782 1152" "$l o3 regions: loops, calls, recursion, switch, arrays, more vars than registers" + chk "$($vm < tests/programs/o3scopes.zc)" "116" "$l o3 registers shared by scopes, cached fixed arrays" + chk "$($vm < tests/programs/o3fat.zc)" "node node 3 box node [0]int" "$l o3 data gets fattened on the way out" + chk "$($vm < tests/programs/o3call.zc)" "0 0 4 23 55 node" "$l o3 calls o3 directly, and new stamps the header" + chk "$($vm < tests/programs/o3direct.zc | md5sum)" "$(md5sum < tests/programs/o3direct.expected)" "$l o3 calls: arguments, results, recursion, floats, pointers, the ways out of a fast call" + chk "$($vm < tests/programs/o3licm.zc)" "3184200 7950 315 50 260 -56" "$l o3 hoists loop invariants and shares values between blocks" + chk "$($vm < tests/programs/o3deep.zc)" "30000" "$l o3 recursion thirty thousand deep" + chk "$($vm < tests/programs/o3deep_over.zc 2>&1)" "100000" "$l o3 recursion grows past the original stack" + chk "$($vm < tests/programs/o3_bounds.zc 2>&1)" "zy: index 7 out of range (length 5)" "$l o3 bounds check" + chk "$($vm < tests/programs/bounds.zc)" "55 55 8" "$l a proved +1 loop skips the bounds check" + chk "$($vm < tests/programs/bounds_lo.zc 2>&1)" "zy: index -1 out of range (length 4)" "$l a negative loop index is still checked" + chk "$($vm < tests/programs/bounds_hi.zc 2>&1)" "zy: index 4 out of range (length 4)" "$l an index that is not the induction variable is still checked" + local r; r=$(bash tests/runerr.sh "$vm" tests/errors | tail -1) + chk "$r" "rejected: $(ls tests/errors/*.zc | wc -l | tr -d ' ') passed, 0 failed" "$l analyzer rejects every bad program" + r=$(bash tests/runerr.sh "$vm" tests/typeerrors | tail -1) + chk "$r" "rejected: $(ls tests/typeerrors/*.zc | wc -l | tr -d ' ') passed, 0 failed" "$l typechecker rejects every bad program" + r=$(bash tests/runerr.sh "$vm" tests/langerrors | tail -1) + chk "$r" "rejected: $(ls tests/langerrors/*.zc | wc -l | tr -d ' ') passed, 0 failed" "$l o3 and array misuse is rejected" +} +s_interp() { + T=$ROOT/interp + gcc -O2 -w -fwrapv -DJTYPE=int src/core/core.c src/core/io_linux.c -Isrc/core -o $T/zy + run $T/zy "interp" + chk "$(ZC_ARG_TEST=ok $T/zy tests/programs/args.zc alpha beta < tests/programs/args.zc)" "4 beta 1 1" "interp the command line reaches argv, after " + chk "$($T/zy < tests/programs/cabi.zc | md5sum)" "$(md5sum < tests/programs/cabi.expected)" "interp native calls and callbacks (extern, cfunc)" + chk "$($T/zy < tests/programs/cabi_o3.zc | md5sum)" "$(md5sum < tests/programs/cabi_o3.expected)" "interp native calls from o3 code, more arguments than registers" + chk "$($T/zy < tests/programs/cabi_sized.zc | md5sum)" "$(md5sum < tests/programs/cabi_sized.expected)" "interp native calls with f32 and the sized integers" + chk "$($T/zy < tests/programs/structval.zc)" "34 3520 18 325 810 340" "interp structs by value to a c function (registers, sse, stack)" + chk "$($T/zy < tests/programs/structret.zc)" "3 4 3.500000 4.000000 24 8 1.000000 3.500000" "interp structs returned from a c function (registers, sse, memory)" + chk "$(ZYTRACE=1 $T/zy < tests/programs/o3.zc 2>&1 | grep -c 'micro-ops$')" "8" "every o3 region lifts" + chk "$($T/zy < tests/programs/infer_return.zc)" "3" "an owned return with no .u is inferred from the body" + chk "$($T/zy < tests/programs/discard_m.zc)" "7" "m* discards an unnamed u result" + chk "$($T/zy < tests/programs/closure.zc | tr '\n' ' ')" "31 35 42 2 7 0 109 30 7 16 33 20 2 " "closures: calls through values, captures, nesting, returns" + chk "$($T/zy < tests/programs/decl_undefined.zc 2>&1 | head -1)" "zc: line 1: helper is used but never defined" "a declared function nothing defines fails a build" + if [ "$(uname -s)" = Linux ]; then chk "$($T/zy < tests/programs/rawsys.zc)" "-38" "Host.raw: an unknown syscall number gives -ENOSYS"; else chk "$($T/zy < tests/programs/rawsys.zc)" "-1" "Host.raw: unavailable on this host"; fi + if [ "$(uname -m)" = x86_64 ]; then chk "$($T/zy < tests/programs/rawpid.zc)" "1" "Host.raw: getpid (39 on x86-64) answers"; fi +} + +s_micro() { + T=$ROOT/micro + gcc -O2 -w -fwrapv src/micro/micro.c src/micro/port_linux.c -Isrc/core -Isrc/micro -o $T/zym || exit 1 + run $T/zym "micro" + selfc $T/zym $T/gm.bin + cmp -s $T/g1.bin $T/gm.bin && chk 1 1 "microcore self-compile matches" || chk 0 1 "microcore self-compile matches" + gcc -ffreestanding -w -c src/micro/micro.c -Isrc/core -Isrc/micro -o $T/micro.o && gcc -ffreestanding -w -c src/micro/port_stub.c -Isrc/core -Isrc/micro -o $T/stub.o && chk 1 1 "microcore and the stub port compile freestanding" || chk 0 1 "microcore and the stub port compile freestanding" + gcc -O2 -w -fwrapv -DJTYPE=int -DZY_SOFTFLOAT -DZYINTERP src/core/core.c src/core/io_linux.c -Isrc/core -o $T/zysoft || exit 1 + chk "$($T/zysoft < tests/programs/float.zc)" "4.000000 4.500000 3.000000 2.500000 2.500000 0.001500 0.100000 -0.125000 inf -inf nan 1 1 1 0 0 3 -3 3.000000 -2.000000 2" "soft-float fallback matches the hardware line" + chk "$($T/zysoft < tests/programs/ftruth.zc)" "00111000110107" "soft-float negative zero is false" + chk "$($T/zysoft < tests/programs/fo3.zc)" "5997 5997 4.000000 nan 0 3.250000 1 0 0" "soft-float o3 float ops match" + chk "$($T/zysoft < tests/programs/ftext.zc)" "1e+308 1e-10 -0 inf -inf nan 0.1 1.50 1.250e+0 100000000000000000000.000000 1e+40 1111111111" "soft-float text matches" +} + +s_small() { + T=$ROOT/small + # smaller machines. addresses are literals, so each size is its own genome. + gcc -O2 -w -fwrapv src/tools/genmap.c -Isrc/core -o $T/genmap || exit 1 + $T/genmap --preset default --check >/dev/null && chk 1 1 "default map matches the checked-in sources" || chk 0 1 "default map matches the checked-in sources" + $T/genmap --preset small -o $T/small || exit 1 + $T/genmap --mode compiler --name small --heap-lo 8192 --heap 20480 --types 8192 \ + --genome 245760 --frame 2048 --return 512 --mark 512 --data 3072 \ + --snap-cap 1000 --quote-cap 300 -o $T/small-flags || exit 1 + cmp -s $T/small/memmap.h $T/small-flags/memmap.h && cmp -s $T/small/rt.zc $T/small-flags/rt.zc \ + && cmp -s $T/small/compiler.zc $T/small-flags/compiler.zc && cmp -s $T/small/map.txt $T/small-flags/map.txt \ + && chk 1 1 "compiler flags reproduce the small map" || chk 0 1 "compiler flags reproduce the small map" + cp src/tools/zc0.c src/core/fops.h src/core/bops.h src/core/stack.h $T/small/ + gcc -O1 -w -fwrapv $T/small/zc0.c -o $T/zc0s || exit 1 + cat $T/small/rt.zc $T/small/compiler.zc | $T/zc0s > $T/small/genome.h || exit 1 + cp src/micro/micro.c src/micro/micro_port.h src/micro/port_linux.c src/micro/port_stub.c src/core/core.c src/core/io_linux.c src/core/fops.h src/core/bops.h src/core/stack.h $T/small/ + gcc -O2 -w -fwrapv $T/small/micro.c $T/small/port_linux.c -o $T/zyms || exit 1 + gcc -O2 -w -fwrapv -DJTYPE=int $T/small/core.c $T/small/io_linux.c -o $T/zys || exit 1 + gcc -ffreestanding -w -c $T/small/micro.c -o $T/micro_s.o && gcc -ffreestanding -w -c $T/small/port_stub.c -o $T/stub_s.o && chk 1 1 "small microcore compiles freestanding" || chk 0 1 "small microcore compiles freestanding" + sameout() { # $1 = other vm, $2 = label, $3 = source file + local a b + a=$($T/zy < "$3" 2>&1 | tr '\n' '/') + b=$($1 < "$3" 2>&1 | tr '\n' '/') + chk "$b" "$a" "$2" + } + for f in examples/hello.zc examples/fact.zc examples/lists.zc examples/typed.zc examples/wiring.zc examples/life.zc \ + tests/programs/basics.zc tests/programs/wide.zc tests/programs/types.zc tests/programs/alias.zc tests/programs/meta.zc tests/programs/arrays.zc \ + tests/programs/joins.zc tests/programs/enhance.zc tests/programs/o3.zc tests/programs/csyn.zc tests/programs/umd.zc tests/programs/quarantine.zc tests/programs/roots.zc \ + tests/programs/float.zc tests/programs/ftruth.zc tests/programs/ftype.zc tests/programs/fo3.zc tests/programs/ftext.zc tests/programs/bytes.zc \ + tests/programs/strings.zc examples/strings.zc tests/programs/share.zc tests/programs/share_dbg.zc; do + sameout $T/zyms "small $(basename "$f" .zc)" "$f" + sameout $T/zys "small core $(basename "$f" .zc)" "$f" + done + + # code literals can look like pointers on a compact map. test collection and + # the retained root without assuming the default map's exact reclaim count. + for vm in $T/zyms $T/zys; do + out=$(sed 's/f >= 990/f > 0/' tests/programs/gc.zc | $vm | tr '\n' '/') + chk "$out" "1000 1 900 1 199900/" "small collector makes progress and preserves its root" + done + + $T/genmap --preset run -o $T/run || exit 1 + $T/genmap --mode run --words 20480 -o $T/run-flags || exit 1 + cmp -s $T/run/memmap.h $T/run-flags/memmap.h && cmp -s $T/run/rt.zc $T/run-flags/rt.zc \ + && cmp -s $T/run/map.txt $T/run-flags/map.txt \ + && chk 1 1 "run flags reproduce the growing run-only map" || chk 0 1 "run flags reproduce the growing run-only map" +} + +s_runonly() { + T=$ROOT/runonly + # its own run map: the run-only image is built against this one, and the + # small-machine section lays out its own rather than sharing this + $T/genmap --preset run -o $T/run || exit 1 + cp src/tools/zc0.c src/micro/micro.c src/micro/micro_port.h src/micro/port_linux.c src/micro/port_stub.c src/core/fops.h src/core/bops.h src/core/stack.h $T/run/ + gcc -O1 -w -fwrapv $T/run/zc0.c -o $T/zc0r || exit 1 + runonly() { # $1 label, $2 source. the program is already in the image, so it reads no source. + # the map is sized to the program: src/tools/automap.sh lays it out for the image's own length + local a b d=$T/run-$1 input=$2 + if [ "$1" = gc ]; then input=$T/collector-progress.zc; sed 's/f >= 990/f > 0/' "$2" > "$input"; fi + GENMAP=$T/genmap bash src/tools/automap.sh run $d src/runtime/run.zc "$input" >/dev/null || { chk 0 1 "run $1 builds"; return; } + cp src/micro/micro.c src/micro/micro_port.h src/micro/port_linux.c src/micro/port_stub.c src/core/fops.h src/core/bops.h src/core/stack.h $d/ + gcc -O2 -w -fwrapv $d/micro.c $d/port_linux.c -o $T/zymr || { chk 0 1 "run $1 links"; return; } + a=$($T/zy < "$input" 2>&1 | tr '\n' '/') + b=$($T/zymr &1 | tr '\n' '/') + chk "$b" "$a" "run $1" + } + runonly hello examples/hello.zc + runonly fact examples/fact.zc + runonly wide tests/programs/wide.zc + runonly selfpatch examples/selfpatch.zc + runonly gc tests/programs/gc.zc + runonly float tests/programs/float.zc + runonly ftruth tests/programs/ftruth.zc + runonly ftext tests/programs/ftext.zc + runonly bytes tests/programs/bytes.zc + runonly strings tests/programs/strings.zc + runonly strings-ex examples/strings.zc + runonly share tests/programs/share.zc + d=$T/run-echo; GENMAP=$T/genmap bash src/tools/automap.sh run $d src/runtime/run.zc examples/echo.zc >/dev/null && cp src/micro/micro.c src/micro/micro_port.h src/micro/port_linux.c src/micro/port_stub.c src/core/fops.h src/core/bops.h src/core/stack.h $d/ \ + && gcc -O2 -w -fwrapv $d/micro.c $d/port_linux.c -o $T/zymr \ + && chk "$(printf 'abc\n' | $T/zymr | tr '\n' '/')" "$( (cat examples/echo.zc; printf '`abc\n') | $T/zy | tr '\n' '/')" "run echo" \ + || chk 0 1 "run echo" + # zcc: a program becomes a static binary that runs without stdin and with a held-open pipe + if CC=gcc src/tools/zcc -o $T/zcc-hello examples/hello.zc >/dev/null 2>&1; then + chk "$($T/zcc-hello < /dev/null)" "hello, world!" "zcc: hello, static" + chk "$(sleep 2 | timeout 5 $T/zcc-hello; echo $?)" "hello, world! +0" "zcc: a program that never reads stdin does not wait for it" + if [ "$(uname -s)" = Linux ]; then chk "$(file -b $T/zcc-hello | grep -c 'statically linked')" "1" "zcc: the binary is statically linked"; else echo "skip zcc static linkage (unavailable on this host)"; fi + else chk 0 1 "zcc builds examples/hello.zc"; fi + printf 'fn main() {\n\tput_n(1\n\treturn 0;\n}\n' > $T/zcc-bad.zc + src/tools/zcc -o $T/zcc-bad $T/zcc-bad.zc /dev/null 2>&1; r=$? + chk "$r $( [ -e $T/zcc-bad ] && echo yes || echo no)" "1 no" "zcc: a bad program exits 1 and writes no binary" + gcc -ffreestanding -w -c $T/run-echo/micro.c -o $T/micro_r.o && gcc -ffreestanding -w -c $T/run-echo/port_stub.c -o $T/stub_r.o && chk 1 1 "run-only microcore compiles freestanding" || chk 0 1 "run-only microcore compiles freestanding" +} + +s_bare() { + T=$ROOT/bare + bare() { # $1 target, $2 run|compile, $3 source, $4 label. compare UART stdout to the hosted interpreter. + local a b + a=$($T/zy < "$3" 2>&1 | tr '\n' '/') + b=$(bash src/baremetal/run.sh "$1" "$2" "$3" 2>/dev/null | tr '\n' '/') + chk "$b" "$a" "$4" + } + bareio() { # clock, RAM file, random, BOARD, MW, a long echo, and exit status + local mw + mw=$(GENMAP=$T/genmap bash src/tools/automap.sh run $T/run-bareio src/runtime/run.zc tests/programs/bareio.zc | sed -n 's/.*MW \([0-9]*\) .*/\1/p') + local t="$1" pay a b out st + pay=abcdefghijklmnopqrstuvwxyz0123456789 + a=$( (cat examples/echo.zc; printf '`%s' "$pay") | $T/zy | tr -d '\n') + b=$(printf '%s' "$pay" | bash src/baremetal/run.sh "$t" io examples/echo.zc 2>/dev/null | tr -d '\n') + chk "$b" "$a" "bare $t echo until eof" + b=$(bash src/baremetal/run.sh "$t" run tests/programs/bareio.zc 2>/dev/null | tr -d '\n') + chk "$b" "tick 2 zy rand board $mw" "bare $t clock, files, random, memory" + out=$(bash src/baremetal/run.sh "$t" run tests/programs/bareexit.zc 2>/dev/null) + st=$? + chk "$(printf '%s' "$out" | tr -d '\n')" "bye" "bare $t exit status text" + chk "$st" "3" "bare $t exit status code" + } + if bash src/baremetal/run.sh ready riscv; then + bare riscv run examples/hello.zc "bare riscv hello" + bare riscv run examples/fact.zc "bare riscv fact" + bare riscv run tests/programs/wide.zc "bare riscv wide" + bare riscv run tests/programs/gc.zc "bare riscv gc" + bare riscv run tests/programs/float.zc "bare riscv float" + bare riscv run tests/programs/ftruth.zc "bare riscv negative zero" + bare riscv run tests/programs/ftext.zc "bare riscv float text" + bare riscv run tests/programs/bytes.zc "bare riscv bytes" + bare riscv run tests/programs/strings.zc "bare riscv strings" + bare riscv run tests/programs/share.zc "bare riscv share" + bare riscv compile tests/programs/float.zc "bare riscv compile float" + bare riscv compile tests/programs/ftruth.zc "bare riscv compile negative zero" + bare riscv compile tests/programs/ftype.zc "bare riscv compile float type" + bare riscv compile tests/programs/fo3.zc "bare riscv compile o3 floats" + bare riscv compile tests/programs/ftext.zc "bare riscv compile float text" + bare riscv compile tests/programs/bytes.zc "bare riscv compile bytes" + bare riscv compile tests/programs/strings.zc "bare riscv compile strings" + bare riscv compile tests/programs/share.zc "bare riscv compile share" + bare riscv compile examples/hello.zc "bare riscv compile hello" + bare riscv compile examples/fact.zc "bare riscv compile fact" + bare riscv compile tests/programs/wide.zc "bare riscv compile wide" + bareio riscv + else echo "skip baremetal riscv (no riscv64-linux-gnu-gcc or qemu-system-riscv64)"; fi + if bash src/baremetal/run.sh ready aarch64; then + bare aarch64 run examples/hello.zc "bare aarch64 hello" + bare aarch64 run examples/fact.zc "bare aarch64 fact" + bare aarch64 run tests/programs/wide.zc "bare aarch64 wide" + bare aarch64 run tests/programs/gc.zc "bare aarch64 gc" + bare aarch64 run tests/programs/float.zc "bare aarch64 float" + bare aarch64 run tests/programs/ftruth.zc "bare aarch64 negative zero" + bare aarch64 run tests/programs/ftext.zc "bare aarch64 float text" + bare aarch64 run tests/programs/bytes.zc "bare aarch64 bytes" + bare aarch64 run tests/programs/strings.zc "bare aarch64 strings" + bare aarch64 run tests/programs/share.zc "bare aarch64 share" + bare aarch64 compile tests/programs/float.zc "bare aarch64 compile float" + bare aarch64 compile tests/programs/ftruth.zc "bare aarch64 compile negative zero" + bare aarch64 compile tests/programs/ftype.zc "bare aarch64 compile float type" + bare aarch64 compile tests/programs/fo3.zc "bare aarch64 compile o3 floats" + bare aarch64 compile tests/programs/ftext.zc "bare aarch64 compile float text" + bare aarch64 compile tests/programs/bytes.zc "bare aarch64 compile bytes" + bare aarch64 compile tests/programs/strings.zc "bare aarch64 compile strings" + bare aarch64 compile tests/programs/share.zc "bare aarch64 compile share" + bare aarch64 compile examples/hello.zc "bare aarch64 compile hello" + bare aarch64 compile examples/fact.zc "bare aarch64 compile fact" + bare aarch64 compile tests/programs/wide.zc "bare aarch64 compile wide" + bareio aarch64 + else echo "skip baremetal aarch64 (no aarch64-linux-gnu-gcc or qemu-system-aarch64)"; fi +} + +s_jit() { + T=$ROOT/jit + if command -v aarch64-linux-gnu-gcc >/dev/null && command -v qemu-aarch64 >/dev/null; then + aarch64-linux-gnu-gcc -O2 -w -fwrapv -DJTYPE=int -static src/core/core.c src/core/io_linux.c -Isrc/core -o $T/zyj || exit 1 + printf '#!/bin/sh\nexec qemu-aarch64 %s "$@"\n' $T/zyj > $T/jit; chmod +x $T/jit + run $T/jit "jit" + chk "$(ZYTRACE=1 $T/jit < tests/programs/o3.zc 2>&1 | grep -c 'bytes of arm64')" "8" "jit compiles every o3 region to arm64" + selfc $T/jit $T/gj.bin; cmp -s $T/g1.bin $T/gj.bin && chk 1 1 "jit self-compile matches" || chk 0 1 "jit self-compile matches" + # native calls need a dynamic binary (a static one has no dlsym), run against the aarch64 libc + if [ -d /usr/aarch64-linux-gnu/lib ] && aarch64-linux-gnu-gcc -O2 -w -fwrapv -DJTYPE=int src/core/core.c src/core/io_linux.c -Isrc/core -o $T/zyjd -ldl 2>/dev/null; then + chk "$(qemu-aarch64 -L /usr/aarch64-linux-gnu $T/zyjd < tests/programs/cabi.zc | md5sum)" "$(md5sum < tests/programs/cabi.expected)" "jit native calls and callbacks (extern, cfunc)" + chk "$(qemu-aarch64 -L /usr/aarch64-linux-gnu $T/zyjd < tests/programs/cabi_o3.zc | md5sum)" "$(md5sum < tests/programs/cabi_o3.expected)" "jit native calls from o3 code, stack arguments" + chk "$(qemu-aarch64 -L /usr/aarch64-linux-gnu $T/zyjd < tests/programs/cabi_sized.zc | md5sum)" "$(md5sum < tests/programs/cabi_sized.expected)" "jit native calls with f32 and the sized integers" + # structs by value: the helper library has to be arm64, and cload reads build/sbv.so from the working directory + R=$(pwd); aarch64-linux-gnu-gcc -shared -fPIC -w tests/data/sbv.c -o $T/sbv_arm.so && mkdir -p $T/build && cp $T/sbv_arm.so $T/build/sbv.so + chk "$(cd $T && qemu-aarch64 -L /usr/aarch64-linux-gnu $T/zyjd < $R/tests/programs/structval.zc)" "34 3520 18 325 810 340" "jit structs by value to a c function (aapcs64: registers, hfa, reference, stack)" + chk "$(cd $T && qemu-aarch64 -L /usr/aarch64-linux-gnu $T/zyjd < $R/tests/programs/structret.zc)" "3 4 3.500000 4.000000 24 8 1.000000 3.500000" "jit structs returned from a c function (aapcs64 results)" + fi + else echo "skip jit (no aarch64 toolchain or qemu)"; fi +} + +s_llvm() { + T=$ROOT/llvm + # llvm-config can exist without the headers and static libs (a runtime-only install), so the gate is a real build + llvm_ok=0 + if command -v llvm-config >/dev/null && gcc -O2 -w -fwrapv -DJTYPE=int -DZYLLVM $(llvm-config --cflags) src/core/core.c src/core/io_linux.c -Isrc/core -o $T/zyl $(llvm-config --ldflags --libs) 2>/dev/null; then llvm_ok=1; fi + if [ $llvm_ok = 1 ]; then + run $T/zyl "llvm" + chk "$(ZYTRACE=1 $T/zyl < tests/programs/o3.zc 2>&1 | grep -c 'compiled by llvm')" "8" "llvm compiles every o3 region" + printf '#!/bin/sh\nZYHOT=1 exec %s "$@"\n' $T/zyl > $T/llall; chmod +x $T/llall + chk "$($T/zyl < tests/programs/structval.zc)" "34 3520 18 325 810 340" "llvm structs by value (the call goes through the interpreter's native call)" + chk "$($T/zyl < tests/programs/structret.zc)" "3 4 3.500000 4.000000 24 8 1.000000 3.500000" "llvm structs returned from a c function" + chk "$($T/llall < tests/programs/types.zc | md5sum)" "$($T/zy < tests/programs/types.zc | md5sum)" "llvm compiling every block agrees" + chk "$($T/llall < tests/programs/float.zc)" "4.000000 4.500000 3.000000 2.500000 2.500000 0.001500 0.100000 -0.125000 inf -inf nan 1 1 1 0 0 3 -3 3.000000 -2.000000 2" "llvm compiling every block agrees on floats" + chk "$($T/llall < tests/programs/ftruth.zc)" "00111000110107" "llvm compiling every block agrees on negative zero" + chk "$($T/llall < tests/programs/ftype.zc)" "3.500000 10.500000 3.500000 4.250000 5.250000 4.250000 6.500000 2 4 4.250000 8.500000 1 4" "llvm compiling every block agrees on the float type" + chk "$($T/llall < tests/programs/fo3.zc)" "5997 5997 4.000000 nan 0 3.250000 1 0 0" "llvm compiling every block agrees on o3 floats" + chk "$($T/llall < tests/programs/ftext.zc)" "1e+308 1e-10 -0 inf -inf nan 0.1 1.50 1.250e+0 100000000000000000000.000000 1e+40 1111111111" "llvm compiling every block agrees on float text" + chk "$($T/llall < tests/programs/bytes.zc)" "17 34 43981 16909060 1 205 171 -3674937295934316015 1108152156331 0 0 30768 +ABC" "llvm compiling every block agrees on bytes" + chk "$($T/llall < tests/programs/strings.zc)" "hi! 3 3 104 i! 1 1 1 -42 42 -42 11 1 -9223372036854775808 0 1 3 10 1 1 1 W ab" "llvm compiling every block agrees on strings" + chk "$($T/llall < tests/programs/share.zc)" "1 2 1 4 1 1 1 1 42 1 1 1 6 0" "llvm compiling every block agrees on shared cells" + chk "$($T/zyl < tests/programs/cabi.zc | md5sum)" "$(md5sum < tests/programs/cabi.expected)" "llvm native calls and callbacks (extern, cfunc)" + chk "$($T/llall < tests/programs/cabi.zc | md5sum)" "$(md5sum < tests/programs/cabi.expected)" "llvm compiling every block agrees on native calls" + chk "$($T/zyl < tests/programs/cabi_o3.zc | md5sum)" "$(md5sum < tests/programs/cabi_o3.expected)" "llvm calls natives from o3 regions directly" + chk "$(ZYTRACE=1 $T/zyl < tests/programs/cabi_o3.zc 2>&1 | grep -c 'compiled by llvm')" "2" "llvm compiles the o3 regions that call natives" + chk "$($T/llall < tests/programs/cabi_o3.zc | md5sum)" "$(md5sum < tests/programs/cabi_o3.expected)" "llvm compiling every block agrees on o3 native calls" + chk "$($T/zyl < tests/programs/cabi_sized.zc | md5sum)" "$(md5sum < tests/programs/cabi_sized.expected)" "llvm native calls with f32 and the sized integers" + chk "$($T/llall < tests/programs/sized.zc | md5sum)" "$(md5sum < tests/programs/sized.expected)" "llvm compiling every block agrees on sized integers" + chk "$($T/llall < tests/programs/bitops.zc | md5sum)" "$(md5sum < tests/programs/bitops.expected)" "llvm compiling every block agrees on the new micro-ops" + chk "$($T/llall < tests/programs/f32.zc | md5sum)" "$(md5sum < tests/programs/f32.expected)" "llvm compiling every block agrees on f32" + chk "$($T/llall < tests/programs/sizedvec.zc | md5sum)" "$(md5sum < tests/programs/sizedvec.expected)" "llvm compiling every block agrees on growable sized arrays" + chk "$($T/llall < tests/programs/packedfields.zc | md5sum)" "$(md5sum < tests/programs/packedfields.expected)" "llvm compiling every block agrees on packed fields" + chk "$($T/llall < tests/programs/generics.zc | md5sum)" "$(md5sum < tests/programs/generics.expected)" "llvm compiling every block agrees on generic instances" + chk "$($T/llall < tests/programs/genmeth.zc | md5sum)" "$(md5sum < tests/programs/genmeth.expected)" "llvm compiling every block agrees on generic struct methods" + chk "$($T/llall < tests/programs/o3push.zc | md5sum)" "$(md5sum < tests/programs/o3push.expected)" "llvm compiling every block agrees on inline push and pop" + selfc $T/zyl $T/gl.bin; cmp -s $T/g1.bin $T/gl.bin && chk 1 1 "llvm self-compile matches" || chk 0 1 "llvm self-compile matches" + else echo "skip llvm (no llvm-config, or its headers and libraries are missing)"; fi +} + +s_lsp() { + T=$ROOT/lsp + # the language server: the compiler itself in check mode, so these are the real compiler's findings + if bash src/tools/mklsp.sh $T/lsp >/dev/null 2>&1; then + lsp=$T/lsp/zclsp + chk "$($lsp < examples/hello.zc)" "no errors" "lsp: a clean program" + chk "$($lsp < tests/errors/back_to_m.zc | head -1 | cut -d' ' -f2-)" "error: $(head -1 tests/errors/back_to_m.zc | sed 's#^// expect: ##')" "lsp: the compiler's own message" + h=$($lsp < examples/opt_test.zc) + for w in "is never read" "is never reassigned; make it a const" "is a shift; write << 3" "the number 1234 has no name" "new inside a loop" "stack buffer"; do + case "$h" in *"$w"*) chk 1 1 "lsp hint: $w";; *) chk 0 1 "lsp hint: $w";; esac + done + chk "$($lsp < examples/struct_closure_opt.zc | grep -c error)" "0" "lsp: methods and closures check clean" + # a file of a larger build checks on its own: its bodyless declarations are not errors in check mode + for f in src/runtime/rt.zc src/runtime/run.zc src/lsp/server.zc; do chk "$($lsp < $f | grep -c ': error:')" "0" "lsp: $f checks alone"; done + chk "$($lsp < tests/programs/decl_undefined.zc)" "no errors" "lsp: a bodyless declaration is not an error in check mode" + chk "$($lsp < tests/programs/undefined_call.zc | grep -c ': error:')" "1" "lsp: a call to a name nothing defines is still an error" + chk "$($lsp --annotate < tests/programs/annotate.zc | md5sum)" "$(md5sum < tests/programs/annotate.expected)" "lsp --annotate writes the types the compiler can see into a program" + chk "$($lsp --annotate < tests/programs/annotate.zc | $T/zy)" "$($T/zy < tests/programs/annotate.zc)" "an annotated program means what the original did" + chk "$($lsp --types < tests/programs/annotate.zc | head -3 | tr '\n' '/')" "9:1 3 int/10:1 3 char*/11:1 3 int/" "lsp --types lists the edits without making them" + chk "$($lsp --annotate < tests/programs/annotate.expected | md5sum)" "$(md5sum < tests/programs/annotate.expected)" "annotating an annotated program changes nothing more" + printf 'Content-Length: 40\r\n\r\n{"jsonrpc": [2.0", "method": "x"}' | timeout 10 $lsp > /dev/null 2>&1; chk $? 0 "lsp: a stray brace in an array ends the message, and the server exits" + if [ $QUICK = 1 ]; then echo "skip lsp protocol and corpus tests (quick)" + elif command -v node >/dev/null; then + node tests/drivers/storage_analysis_test.js $lsp > $T/storage.out 2>&1; r=$? + grep -v '^ok' $T/storage.out + chk $r 0 "lsp: definite storage warnings and reference lifetime hovers" + node tests/drivers/lsp_test.js $lsp > $T/lsp.out 2>&1; r=$? + grep -v '^ok' $T/lsp.out + chk $r 0 "lsp: protocol and corpus tests ($(grep -c '^ok' $T/lsp.out) checks)" + node tests/drivers/lsp_live.js $lsp > $T/lsplive.out 2>&1; r=$? + grep -v '^ok' $T/lsplive.out + chk $r 0 "lsp: the native server answers while the client keeps the pipe open" + node tests/drivers/lsp_sandbox.js $lsp > $T/lspsb.out 2>&1; r=$? + grep -v '^ok' $T/lspsb.out + chk $r 0 "lsp: compile-time code in a checked file cannot write files" + # the diagnostics cache: its key rules, then the extension's client end to end on the built-in server + node tests/drivers/diagcache_test.js > $T/dcache.out 2>&1; r=$? + grep -v '^ok' $T/dcache.out + chk $r 0 "lsp: the diagnostics cache keys and store ($(grep -c '^ok' $T/dcache.out) checks)" + node tests/drivers/ext_cache_test.js > $T/ecache.out 2>&1; r=$? + grep -v '^ok' $T/ecache.out + chk $r 0 "lsp: the extension's cache, end to end ($(grep -c '^ok' $T/ecache.out) checks)" + # the same server as WebAssembly, through the extension's host (needs clang) + if command -v clang >/dev/null && clang --print-targets 2>/dev/null | grep -q wasm32 && LSP_BUILD_DIR=$T/lsp LSP_REUSE_BUILD=1 bash src/tools/mklspwasm.sh $T/lsp/lsp.wasm >/dev/null 2>&1; then + node tests/drivers/lsp_wasm.js $T/lsp/lsp.wasm > $T/lspw.out 2>&1; r=$? + grep -v '^ok' $T/lspw.out + chk $r 0 "lsp: wasm server through the extension's host ($(grep -c '^ok' $T/lspw.out) checks)" + else echo "skip lsp wasm tests (no clang with wasm32)"; fi + # the same server run by the javascript vm, the extension's fallback when wasm is not there. + # the vm starts at the image's heap and grows on the heap's request, so a long document is the test + node src/js/extract_lsp.js >/dev/null && mkdir -p $T/vmserver && cp src/js/tinyvm.js src/js/layout.js src/js/lsp.json $T/vmserver/ + node tests/drivers/lsp_vm.js $T/vmserver > $T/lspvm.out 2>&1; r=$? + grep -v '^ok' $T/lspvm.out + chk $r 0 "lsp: the server in the javascript vm, through the extension's host ($(grep -c '^ok' $T/lspvm.out) checks)" + else echo "skip lsp protocol tests (no node)"; fi + else chk 0 1 "lsp server builds"; fi +} + +s_js() { + T=$ROOT/js + # the javascript vm, and the microcore as webassembly. both run the same genome + if command -v node >/dev/null; then + chk "$(node -e "console.log(require('./src/js/genome.json').glen)")" "$(grep -m1 '#define GLEN' src/core/genome.h | awk '{print $3}')" "src/js/genome.json is up to date" + chk "$(node src/js/tinyvm.js examples/hello.zc)" "hello, world!" "js vm: hello" + chk "$(node src/js/tinyvm.js examples/fact.zc | tail -1)" "3628800" "js vm: factorial" + chk "$(node src/js/tinyvm.js examples/primes.zc | tail -1)" "widest gap: 72, after 31397" "js vm: primes" + chk "$(node src/js/tinyvm.js tests/programs/growone.zc)" "12 1" "js vm: a block past the initial heap grows the machine" + if command -v clang >/dev/null && command -v wasm-ld >/dev/null && clang --print-targets 2>/dev/null | grep -q wasm32; then + bash src/web/build.sh $T/zc.wasm >/dev/null && node tests/drivers/wasm_test.js $T/zc.wasm > $T/wasm.out 2>&1; r=$? + grep -v '^ok' $T/wasm.out + chk $r 0 "wasm: the microcore in webassembly ($(grep -c '^ok' $T/wasm.out) checks)" + else echo "skip wasm (no clang with the wasm32 target, or no wasm-ld)"; fi + else echo "skip js and wasm (no node)"; fi +} + +s_make() { + T=$ROOT/make + # the makefile builds the programs and the run-only image tool + if command -v make >/dev/null; then + make -s BIN=$T/mbin B=$T/mbuild all >/dev/null 2>&1 && chk "$($T/mbin/zy < examples/hello.zc)" "hello, world!" "make: bin/zy runs a program" || chk 0 1 "make: all builds" + make -s BIN=$T/mbin B=$T/mbuild run PROG=examples/hello.zc >/dev/null 2>&1 && chk "$($T/mbuild/run/zymr > $T/log.$n + pids=("${pids[@]:1}"); names=("${names[@]:1}") +} +sec(){ # $1 = the name the section reports under, $2 = the function to run + local n=$1 + "$2" > $T/log.$n 2>&1 & + pids+=($!); names+=("$n") + [ ${#pids[@]} -ge $JOBS ] && reap + return 0 +} +# the bootstrap's own checks stay counted; the sections are tallied from their +# logs below, so a section that dies half way still reports what it got through. +b_ok=$ok; b_bad=$bad; ok=0; bad=0 +for n in $SECS; do sec $n s_$n; done +while [ ${#pids[@]} -gt 0 ]; do reap; done +for n in $SECS; do + cat $T/log.$n + ok=$((ok + $(grep -c '^ok ' $T/log.$n))) + bad=$((bad + $(grep -c '^FAIL' $T/log.$n))) +done +ok=$((ok + b_ok)); bad=$((bad + b_bad)) +echo "$ok passed, $bad failed"; [ $bad = 0 ] diff --git a/tests/test_win.ps1 b/tests/test_win.ps1 new file mode 100644 index 0000000..aee60e8 --- /dev/null +++ b/tests/test_win.ps1 @@ -0,0 +1,258 @@ +# TinyVM Windows test suite (PowerShell implementation of test.sh) +$ErrorActionPreference = "Stop" + +# detect compiler (prioritizing LLVM-MinGW with UCRT headers) +$llvmMinGW = Get-ChildItem "$env:LOCALAPPDATA\Microsoft\WinGet\Packages\MartinStorsjo.LLVM-MinGW*\llvm-mingw*\bin" -ErrorAction SilentlyContinue +if ($llvmMinGW) { + $env:Path = "$($llvmMinGW[0].FullName);$env:Path" + $cc = "clang" +} elseif (Get-Command clang -ErrorAction SilentlyContinue) { + $cc = "clang" +} elseif (Get-Command gcc -ErrorAction SilentlyContinue) { + $cc = "gcc" +} else { + Write-Error "No C compiler (clang or gcc) found in PATH." + exit 1 +} + +Write-Host "===============================================================================" -ForegroundColor Cyan +Write-Host " TinyVM Windows Automated Test Suite " -ForegroundColor Cyan +Write-Host "===============================================================================" -ForegroundColor Cyan +Write-Host "Compiler: $cc" +Write-Host "" + +$tempDir = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), "tinyvm_test_" + [System.Guid]::NewGuid().ToString("N")) +New-Item -ItemType Directory -Path "$tempDir\s0", "$tempDir\s1" -Force | Out-Null + +$totalPassed = 0 +$totalFailed = 0 + +function Report-Result($success, $name, $detail = "") { + if ($success) { + $script:totalPassed++ + Write-Host " [OK] $name" -ForegroundColor Green + } else { + $script:totalFailed++ + Write-Host " [FAIL] $name $detail" -ForegroundColor Red + } +} + +try { + # --- 1. bootstrap & fixpoint --- + Write-Host "=== 1. Bootstrap and Fixpoint Verification ===" -ForegroundColor Yellow + & $cc -fwrapv -O1 -w src\tools\zc0.c -Isrc/core -o "$tempDir\zc0.exe" + & $cc -fwrapv -O1 -w src\tools\mkgenome.c -Isrc/core -o "$tempDir\mkgenome.exe" + + Copy-Item src\core\core.c, src\core\io_win.c, src\core\memmap.h, src\core\fops.h, src\core\bops.h, src\core\stack.h -Destination "$tempDir\s0" + Copy-Item src\core\core.c, src\core\io_win.c, src\core\memmap.h, src\core\fops.h, src\core\bops.h, src\core\stack.h -Destination "$tempDir\s1" + + cmd.exe /c "(type src\runtime\rt.zc & type src\compiler\compiler.zc) | $tempDir\zc0.exe > $tempDir\s0\genome.h" + & $cc -fwrapv -O2 -w -DJTYPE=int "$tempDir\s0\core.c" "$tempDir\s0\io_win.c" -o "$tempDir\zy0.exe" + + $bzc = [IO.File]::ReadAllText("src\tools\build.zc") + $rt = [IO.File]::ReadAllText("src\runtime\rt.zc") + $cp = [IO.File]::ReadAllText("src\compiler\compiler.zc") + $combined = $bzc + [char]96 + $rt + $cp + [IO.File]::WriteAllBytes("$tempDir\in.tmp", [Text.Encoding]::ASCII.GetBytes($combined)) + + $p1 = Start-Process -FilePath "$tempDir\zy0.exe" -NoNewWindow -PassThru -RedirectStandardInput "$tempDir\in.tmp" -RedirectStandardOutput "$tempDir\g1.bin" -Wait + cmd.exe /c "$tempDir\mkgenome.exe < $tempDir\g1.bin > $tempDir\s1\genome.h" + + & $cc -fwrapv -O2 -w -DJTYPE=int "$tempDir\s1\core.c" "$tempDir\s1\io_win.c" -o "$tempDir\zy1.exe" + $p2 = Start-Process -FilePath "$tempDir\zy1.exe" -NoNewWindow -PassThru -RedirectStandardInput "$tempDir\in.tmp" -RedirectStandardOutput "$tempDir\g2.bin" -Wait + + $g1 = [IO.File]::ReadAllBytes("$tempDir\g1.bin") + $g2 = [IO.File]::ReadAllBytes("$tempDir\g2.bin") + $isFixpoint = [System.Linq.Enumerable]::SequenceEqual($g1, $g2) + Report-Result $isFixpoint "Fixpoint: compiler compiles itself to identical $($g1.Length / 8) words" + + $ghRepo = (Get-FileHash "src\core\genome.h").Hash + $ghGen = (Get-FileHash "$tempDir\s1\genome.h").Hash + Report-Result ($ghRepo -eq $ghGen) "genome.h is up to date" + + # --- 2. compile test executables --- + Write-Host "`n=== 2. Compiling Test Executables ===" -ForegroundColor Yellow + & $cc -fwrapv -O2 -w -DJTYPE=int src\core\core.c src\core\io_win.c -Isrc/core -o "$tempDir\zy.exe" + & $cc -fwrapv -O2 -w src\micro\micro.c src\micro\port_win.c -Isrc/core -Isrc/micro -o "$tempDir\zym.exe" + & $cc -fwrapv -O2 -w src\tools\genmap.c -Isrc/core -o "$tempDir\genmap.exe" + + # --- 3. feature tests on zy.exe --- + Write-Host "`n=== 3. Running Feature Tests on VM Core (zy.exe) ===" -ForegroundColor Yellow + $featureTests = @( + @{ name = "hello"; file = "examples\hello.zc"; expect = "hello, world!" }, + @{ name = "factorial"; file = "examples\fact.zc"; expect = "1101110101111100000000`n3628800`n" }, + @{ name = "self-patching code"; file = "examples\selfpatch.zc"; expect = "ABC" }, + @{ name = "ownership frees everything"; file = "examples\lists.zc"; expect = "[0 1 4 9 16 25]`nlive: 7`nafter the block: 0`n" }, + @{ name = "typed example"; file = "examples\typed.zc"; expect = "last: 4`npoint is 2 words: x int, y int`nvec is 3 words: len int, cap int, items point**`npush is bool(vec*, T.d)`nthe heap says v is a vec, and v.items[0] is a point`n" }, + @{ name = "enhancement example"; file = "examples\wiring.zc"; expect = "16 1234 25 4/9" }, + @{ name = "o3 prime sieve"; file = "examples\primes.zc"; expect = "widest gap: 72, after 31397" }, + @{ name = "language basics"; file = "tests\programs\basics.zc"; expect = "285 610 42 512`nhey 65 -3 -2 1024 -8`n12456`n757`n9 100-101`n5`nok`n" }, + @{ name = "u m d lifetimes"; file = "tests\programs\umd.zc"; expect = "100 6 0 15 99 5 5" }, + @{ name = "u, m and d names"; file = "tests\programs\names.zc"; expect = "12 1 8" }, + @{ name = "mark-sweep collector"; file = "tests\programs\gc.zc"; expect = "1000 1 900 1 199900" }, + @{ name = "parse words and operator tables"; file = "tests\programs\meta.zc"; expect = "1024 19`nunless ok`nrrr`n15 42`n7`n42`n7`n144`n" }, + @{ name = "compile-time eval and mnemonics"; file = "tests\programs\misc.zc"; expect = "45`n54321`nok`n" }, + @{ name = "types, generics, reflection"; file = "tests\programs\types.zc"; expect = "12 5 2 15 56 42 7 14 3`nrect rect* list* list*.u(int) T(list*) fn(word.d)`na:point@0 b:point@2 tag:int@4 `npoint 1 1`n" }, + @{ name = "typechecker accepts valid program"; file = "tests\programs\typeok.zc"; expect = "14" }, + @{ name = "typechecker off by default"; file = "tests\programs\typeoff.zc"; expect = "6" }, + @{ name = "virtual enhancements"; file = "tests\programs\enhance.zc"; expect = "100 0 343 3,2 5 -2 0" }, + @{ name = "joins and paths"; file = "tests\programs\joins.zc"; expect = "0 0 5 80 8 3 9 0" }, + @{ name = "buddy allocator"; file = "tests\programs\buddy.zc"; expect = "131072 262144 1 0" }, + @{ name = "borrow move"; file = "tests\programs\borrowmove.zc"; expect = "6" }, + @{ name = "alias tracking"; file = "tests\programs\alias.zc"; expect = "11 22 4 8" }, + @{ name = "quarantine stale alias"; file = "tests\programs\quarantine.zc"; expect = "0 1234" }, + @{ name = "recycle dropped block"; file = "tests\programs\recycle.zc"; expect = "1 0 1" }, + @{ name = "roots collector mark"; file = "tests\programs\roots.zc"; expect = "1 0" }, + @{ name = "bump arena"; file = "tests\programs\arena.zc"; expect = "4 7 0 1 1" }, + @{ name = "do/while, enum, cast"; file = "tests\programs\csyn.zc"; expect = "13 0 5 6 0" }, + @{ name = "include and string helpers"; file = "tests\programs\use_std.zc"; expect = "42 -7 1" }, + @{ name = "stdlib arena and byte buffer"; file = "tests\programs\stdbuf.zc"; expect = "4 1 266 8" }, + @{ name = "64-bit shifts"; file = "tests\programs\wide.zc"; expect = "1099511627776 1 1 1 -1" }, + @{ name = "floating point"; file = "tests\programs\float.zc"; expect = "4.000000 4.500000 3.000000 2.500000 2.500000 0.001500 0.100000 -0.125000 inf -inf nan 1 1 1 0 0 3 -3 3.000000 -2.000000 2" }, + @{ name = "negative zero is false"; file = "tests\programs\ftruth.zc"; expect = "00111000110107" }, + @{ name = "float types"; file = "tests\programs\ftype.zc"; expect = "3.500000 10.500000 3.500000 4.250000 5.250000 4.250000 6.500000 2 4 4.250000 8.500000 1 4" }, + @{ name = "o3 lifts float ops"; file = "tests\programs\fo3.zc"; expect = "5997 5997 4.000000 nan 0 3.250000 1 0 0" }, + @{ name = "float formatting"; file = "tests\programs\ftext.zc"; expect = "1e+308 1e-10 -0 inf -inf nan 0.1 1.50 1.250e+0 100000000000000000000.000000 1e+40 1111111111" }, + @{ name = "byte addressing"; file = "tests\programs\bytes.zc"; expect = "17 34 43981 16909060 1 205 171 -3674937295934316015 1108152156331 0 0 30768`nABC" }, + @{ name = "byte strings"; file = "tests\programs\strings.zc"; expect = "hi! 3 3 104 i! 1 1 1 -42 42 -42 11 1 -9223372036854775808 0 1 3 10 1 1 1 W ab" }, + @{ name = "shared cons cells"; file = "tests\programs\share.zc"; expect = "1 2 1 4 1 1 1 1 42 1 1 1 6 0" }, + @{ name = "memdebug shared cell"; file = "tests\programs\share_dbg.zc"; expect = "7 1" }, + @{ name = "growable and fixed arrays"; file = "tests\programs\arrays.zc"; expect = "100 128 9801 9801 99 3 4 12 19 8 19 28`n0`n" }, + @{ name = "o3 regions"; file = "tests\programs\o3.zc"; expect = "3142 42 43 9 57 98781 520625 10192 -18782 1152" }, + @{ name = "o3 scopes"; file = "tests\programs\o3scopes.zc"; expect = "116" }, + @{ name = "o3 fat data"; file = "tests\programs\o3fat.zc"; expect = "node node 3 box node [0]int" }, + @{ name = "o3 direct calls"; file = "tests\programs\o3call.zc"; expect = "0 0 4 23 55 node" }, + @{ name = "bounds check optimization"; file = "tests\programs\bounds.zc"; expect = "55 55 8" } + ) + + foreach ($t in $featureTests) { + $out = (cmd.exe /c "$tempDir\zy.exe < $($t.file) 2>&1") -join "`n" + $normOut = $out.Trim().Replace("`r`n", "`n") + $normExp = $t.expect.Trim().Replace("`r`n", "`n") + $match = ($normOut -eq $normExp) -or ($normOut.Contains($normExp)) + Report-Result $match $t.name + } + + # --- 4. microcore tests --- + Write-Host "`n=== 4. Running Microcore Tests (zym.exe) ===" -ForegroundColor Yellow + $microTests = @( + @{ name = "microcore: hello"; file = "examples\hello.zc"; expect = "hello, world!" }, + @{ name = "microcore: factorial"; file = "examples\fact.zc"; expect = "3628800" }, + @{ name = "microcore: primes"; file = "examples\primes.zc"; expect = "widest gap: 72" }, + @{ name = "microcore: basics"; file = "tests\programs\basics.zc"; expect = "ok" }, + @{ name = "microcore: umd"; file = "tests\programs\umd.zc"; expect = "100 6 0 15 99 5 5" } + ) + foreach ($t in $microTests) { + $out = (cmd.exe /c "$tempDir\zym.exe < $($t.file) 2>&1") -join "`n" + $normOut = $out.Trim().Replace("`r`n", "`n") + $match = $normOut.Contains($t.expect) + Report-Result $match $t.name + } + + # --- 5. error rejection suites --- + Write-Host "`n=== 5. Error Rejection Suites ===" -ForegroundColor Yellow + foreach ($suite in @(@{ dir = "tests\errors"; name = "Lifetime errors" }, + @{ dir = "tests\typeerrors"; name = "Typechecker errors" }, + @{ dir = "tests\langerrors"; name = "Language/o3 errors" })) { + $files = Get-ChildItem "$($suite.dir)\*.zc" + $pass = 0 + $fail = 0 + foreach ($f in $files) { + $want = ((Get-Content $f.FullName -TotalCount 1) -replace '^// expect:\s*').Trim() + $got = (Get-Content $f.FullName -Raw | & "$tempDir\zy.exe" 2>&1 | Select-Object -First 1) + if ($got -and $got.Contains($want)) { + $pass++ + } else { + $fail++ + Write-Host " Failed $($f.Name): got [$got], want [$want]" -ForegroundColor DarkYellow + } + } + Report-Result ($fail -eq 0) "$($suite.name): $pass rejected as expected" + } + + # --- 6. tool verification --- + Write-Host "`n=== 6. Memory Map Tool Verification (genmap.exe) ===" -ForegroundColor Yellow + cmd.exe /c "`"$tempDir\genmap.exe`" --preset default --check >nul 2>nul" + Report-Result ($LASTEXITCODE -eq 0) "genmap default preset check passed" + + cmd.exe /c "`"$tempDir\genmap.exe`" --preset small -o `"$tempDir\small`" >nul 2>nul" + Report-Result ($LASTEXITCODE -eq 0) "genmap small preset generated" + + # --- 7. ZC language server (zclsp.exe) --- + Write-Host "`n=== 7. ZC Language Server (zclsp.exe) ===" -ForegroundColor Yellow + # the server is the real compiler in check mode plus src\lsp\server.zc, on its own map + $lspDir = "$tempDir\lsp" + New-Item -ItemType Directory -Force "$lspDir\src" | Out-Null + # two passes: compile once to learn the genome length, then lay the map out again for that length + $lspSrc = "(type `"$lspDir\map\rt.zc`" & type `"$lspDir\map\compiler.zc`" & type src\lsp\server.zc)" + cmd.exe /c "`"$tempDir\genmap.exe`" --preset lsp --grow -o `"$lspDir\map`" >nul 2>nul" + & $cc -fwrapv -O1 -w src\tools\zc0.c "-I$lspDir\map" -Isrc/core -o "$lspDir\zc0.exe" + cmd.exe /c "$lspSrc | `"$lspDir\zc0.exe`" > `"$lspDir\genome.h`"" + $glen = [int]((Select-String -Path "$lspDir\genome.h" -Pattern '#define\s+GLEN\s+(\d+)' | Select-Object -First 1).Matches[0].Groups[1].Value) + $slot = [int]([math]::Ceiling($glen / 4096.0) * 4096) + Remove-Item -Recurse -Force "$lspDir\map" + cmd.exe /c "`"$tempDir\genmap.exe`" --preset lsp --grow --genome $slot -o `"$lspDir\map`" >nul 2>nul" + & $cc -fwrapv -O1 -w src\tools\zc0.c "-I$lspDir\map" -Isrc/core -o "$lspDir\zc0.exe" + cmd.exe /c "$lspSrc | `"$lspDir\zc0.exe`" > `"$lspDir\src\lsp_genome.h`"" + foreach ($f in 'core.c','io_win.c','fops.h','bops.h','stack.h') { Copy-Item "src\core\$f" "$lspDir\src\" } + Copy-Item "$lspDir\map\memmap.h" "$lspDir\src\" + & $cc -fwrapv -O2 -w -DJTYPE=int -DUSE_LSP_GENOME "-I$lspDir\src" "$lspDir\src\core.c" "$lspDir\src\io_win.c" -o "$lspDir\zclsp.exe" + Report-Result (Test-Path "$lspDir\zclsp.exe") "zclsp.exe compiled" + + # CLI check: a clean file + $lspHello = (cmd.exe /c "`"$lspDir\zclsp.exe`" < examples\hello.zc 2>&1") -join "`n" + Report-Result ($lspHello.Trim() -eq "no errors") "zclsp: a clean program (hello.zc)" + + # CLI check: the compiler's own message + $lspErr = (cmd.exe /c "`"$lspDir\zclsp.exe`" < tests\errors\back_to_m.zc 2>&1") -join "`n" + Report-Result ($lspErr.Contains("can't be recast back to m")) "zclsp: lifetime error detection (back_to_m.zc)" + + # CLI check: the compiler's optimization hints + $lspOpt = (cmd.exe /c "`"$lspDir\zclsp.exe`" < examples\opt_test.zc 2>&1") -join "`n" + $hints = @("is never read", "is never reassigned; make it a const", "is a shift; write << 3", "the number 1234 has no name", "new inside a loop", "stack buffer", "handed off once") + foreach ($h in $hints) { Report-Result ($lspOpt.Contains($h)) "zclsp hint: $h" } + + # methods with this, and closures, check clean + $lspStruct = (cmd.exe /c "`"$lspDir\zclsp.exe`" < examples\struct_closure_opt.zc 2>&1") -join "`n" + Report-Result (-not $lspStruct.Contains(": error:")) "zclsp: methods and closures check clean" + + # protocol and corpus tests + if (Get-Command node -ErrorAction SilentlyContinue) { + $lspOut = (node tests\drivers\lsp_test.js "$lspDir\zclsp.exe" 2>&1) -join "`n" + $lspCode = $LASTEXITCODE + Report-Result ($lspCode -eq 0) "zclsp: protocol and corpus tests" + if ($lspCode -ne 0) { Write-Host $lspOut } + } else { + Write-Host " [SKIP] Node.js not detected in PATH, skipping lsp protocol tests." -ForegroundColor DarkYellow + } + + # --- 8. JavaScript / TypeScript vm runtime (src/js/tinyvm.js) --- + Write-Host "`n=== 8. JavaScript VM Runtime (node src/js/tinyvm.js) ===" -ForegroundColor Yellow + $nodeCmd = Get-Command node -ErrorAction SilentlyContinue + if ($nodeCmd) { + $jsHello = (node src\js\tinyvm.js examples\hello.zc 2>&1) -join "`n" + Report-Result ($jsHello.Trim() -eq "hello, world!") "JS VM: hello.zc" + + $jsFact = (node src\js\tinyvm.js examples\fact.zc 2>&1) -join "`n" + Report-Result ($jsFact.Contains("3628800")) "JS VM: fact.zc" + + $jsPrimes = (node src\js\tinyvm.js examples\primes.zc 2>&1) -join "`n" + Report-Result ($jsPrimes.Contains("widest gap: 72")) "JS VM: primes.zc" + + $jsMandel = (node src\js\tinyvm.js examples\mandel.zc 2>&1) -join "`n" + Report-Result ($jsMandel.Contains("*") -or $jsMandel.Contains(".")) "JS VM: mandel.zc (custom fmul enhancement)" + } else { + Write-Host " [SKIP] Node.js not detected in PATH, skipping JS tests." -ForegroundColor DarkYellow + } +} +finally { + Start-Sleep -Milliseconds 200 + Remove-Item -Recurse -Force -ErrorAction SilentlyContinue $tempDir +} + +Write-Host "`n===============================================================================" -ForegroundColor Cyan +$summaryColor = if ($totalFailed -eq 0) { "Green" } else { "Red" } +Write-Host "Test Results: $totalPassed passed, $totalFailed failed" -ForegroundColor $summaryColor +Write-Host "===============================================================================" -ForegroundColor Cyan + +if ($totalFailed -gt 0) { exit 1 } else { exit 0 } diff --git a/tests/typeerrors/arg.zc b/tests/typeerrors/arg.zc new file mode 100644 index 0000000..bf39e85 --- /dev/null +++ b/tests/typeerrors/arg.zc @@ -0,0 +1,5 @@ +// expect: type error: argument: expected point*, got int* +typecheck on; +struct point { int x, y; }; +int f(point* p) { return p.x; } +fn main() { int* q = new(int); f(q); free(d*q); return 0; } diff --git a/tests/typeerrors/arith.zc b/tests/typeerrors/arith.zc new file mode 100644 index 0000000..eda4aec --- /dev/null +++ b/tests/typeerrors/arith.zc @@ -0,0 +1,3 @@ +// expect: type error: arithmetic needs numbers, got int* +typecheck on; +fn main() { int* p = 0; int x = p * 2; return x; } diff --git a/tests/typeerrors/assign.zc b/tests/typeerrors/assign.zc new file mode 100644 index 0000000..b83e1a2 --- /dev/null +++ b/tests/typeerrors/assign.zc @@ -0,0 +1,3 @@ +// expect: type error: initializer: expected char*, got int +typecheck on; +fn main() { char* s = 5; return 0; } diff --git a/tests/typeerrors/call.zc b/tests/typeerrors/call.zc new file mode 100644 index 0000000..7b03dbf --- /dev/null +++ b/tests/typeerrors/call.zc @@ -0,0 +1,3 @@ +// expect: type error: only functions can be called, got int +typecheck on; +fn main() { int k = 3; return k(1); } diff --git a/tests/typeerrors/deref.zc b/tests/typeerrors/deref.zc new file mode 100644 index 0000000..ed030f5 --- /dev/null +++ b/tests/typeerrors/deref.zc @@ -0,0 +1,3 @@ +// expect: type error: only pointers can be dereferenced, got int +typecheck on; +fn main() { int x = 3; return *x; } diff --git a/tests/typeerrors/enum_int.zc b/tests/typeerrors/enum_int.zc new file mode 100644 index 0000000..c710d50 --- /dev/null +++ b/tests/typeerrors/enum_int.zc @@ -0,0 +1,4 @@ +// expect: type error: initializer: expected color, got int +typecheck on; +enum color { RED, GREEN = 5, BLUE }; +int main() { color c = 1; return c; } diff --git a/tests/typeerrors/enum_other.zc b/tests/typeerrors/enum_other.zc new file mode 100644 index 0000000..5b27370 --- /dev/null +++ b/tests/typeerrors/enum_other.zc @@ -0,0 +1,5 @@ +// expect: type error: initializer: expected color, got size +typecheck on; +enum color { RED, GREEN = 5, BLUE }; +enum size { SMALL, LARGE }; +int main() { color c = size.LARGE; return c; } diff --git a/tests/typeerrors/field.zc b/tests/typeerrors/field.zc new file mode 100644 index 0000000..ba03697 --- /dev/null +++ b/tests/typeerrors/field.zc @@ -0,0 +1,3 @@ +// expect: point has no field z +struct point { int x, y; }; +fn main() { point p; return p.z; } diff --git a/tests/typeerrors/generic.zc b/tests/typeerrors/generic.zc new file mode 100644 index 0000000..04e5381 --- /dev/null +++ b/tests/typeerrors/generic.zc @@ -0,0 +1,4 @@ +// expect: type error: argument: expected int, got char* +typecheck on; +T same(T a, T b) { return a; } +fn main() { return same(1, "x"); } diff --git a/tests/typeerrors/notstruct.zc b/tests/typeerrors/notstruct.zc new file mode 100644 index 0000000..0dff6bd --- /dev/null +++ b/tests/typeerrors/notstruct.zc @@ -0,0 +1,2 @@ +// expect: .x: that's a int, not a struct or a pointer to one +fn main() { int n = 1; return n.x; } diff --git a/tests/typeerrors/ret.zc b/tests/typeerrors/ret.zc new file mode 100644 index 0000000..d44267e --- /dev/null +++ b/tests/typeerrors/ret.zc @@ -0,0 +1,4 @@ +// expect: type error: return value: expected bool, got char* +typecheck on; +bool f() { return "no"; } +fn main() { return f(); } diff --git a/tests/typeerrors/voidret.zc b/tests/typeerrors/voidret.zc new file mode 100644 index 0000000..86c9c17 --- /dev/null +++ b/tests/typeerrors/voidret.zc @@ -0,0 +1,4 @@ +// expect: type error: return value: expected void, got int +typecheck on; +void f() { return 3; } +fn main() { f(); return 0; } diff --git a/tinyvm.bat b/tinyvm.bat new file mode 100755 index 0000000..0807eb9 --- /dev/null +++ b/tinyvm.bat @@ -0,0 +1,635 @@ +@echo off +setlocal enabledelayedexpansion +set "SOURCE_ARG=%~f2" +cd /d "%~dp0" +title TinyVM - Windows Interactive Build ^& Run + +REM ========================================================================= +REM Compiler Detection & Environment Setup +REM ========================================================================= + +set "CC_NAME=" + +REM preserve a compiler selected by the caller. +if defined CC ( + set "CC_NAME=Configured [%CC%]" + goto :has_compiler +) + +REM 1. Search WinGet LLVM-MinGW portable packages first (self-contained with CRT headers) +for /d %%D in ("%LOCALAPPDATA%\Microsoft\WinGet\Packages\MartinStorsjo.LLVM-MinGW*") do ( + for /d %%B in ("%%D\llvm-mingw*") do ( + if exist "%%B\bin\clang.exe" ( + set "PATH=%%B\bin;!PATH!" + set "CC=clang" + set "CC_NAME=LLVM-MinGW Clang [WinGet]" + goto :compiler_found + ) + ) +) + +REM 2. Check if clang or gcc is already in PATH +where clang >nul 2>nul +if %errorlevel% equ 0 ( + set "CC=clang" + set "CC_NAME=Clang [in PATH]" + goto :compiler_found +) + +where gcc >nul 2>nul +if %errorlevel% equ 0 ( + set "CC=gcc" + set "CC_NAME=GCC [in PATH]" + goto :compiler_found +) + +REM 3. Search C:\Program Files\LLVM\bin +if exist "%ProgramFiles%\LLVM\bin\clang.exe" ( + set "PATH=%ProgramFiles%\LLVM\bin;%PATH%" + set "CC=clang" + set "CC_NAME=LLVM Clang [Program Files]" + goto :compiler_found +) + +set "PFX86=%ProgramFiles(x86)%" +if defined PFX86 if exist "!PFX86!\LLVM\bin\clang.exe" ( + set "PATH=!PFX86!\LLVM\bin;%PATH%" + set "CC=clang" + set "CC_NAME=LLVM Clang [Program Files x86]" + goto :compiler_found +) + +:compiler_found +if not "%CC%"=="" goto :has_compiler + +echo [ERROR] No suitable C compiler [clang or gcc] found on this system. +echo. +echo Please install LLVM / Clang: +echo winget install MartinStorsjo.LLVM-MinGW.UCRT +echo or: winget install LLVM.LLVM +echo. +pause +exit /b 1 + +:has_compiler + +REM Handle CLI Arguments if provided +if not "%~1"=="" goto :handle_cli + +REM ========================================================================= +REM Interactive Main Menu +REM ========================================================================= + +:main_menu +cls +echo =============================================================================== +echo TinyVM - Windows Toolchain +echo =============================================================================== +echo Compiler : %CC_NAME% +echo Working : %CD% +echo ------------------------------------------------------------------------------- +echo executable status: +if exist "bin\zy.exe" ( echo [X] bin\zy.exe - VM core interpreter ) else ( echo [ ] bin\zy.exe - not built ) +if exist "bin\zym.exe" ( echo [X] bin\zym.exe - portable microcore ) else ( echo [ ] bin\zym.exe - not built ) +if exist "bin\zc0.exe" ( echo [X] bin\zc0.exe - stage 0 bootstrap compiler ) else ( echo [ ] bin\zc0.exe - not built ) +if exist "bin\mkgenome.exe" ( echo [X] bin\mkgenome.exe - genome header generator ) else ( echo [ ] bin\mkgenome.exe - not built ) +if exist "bin\genmap.exe" ( echo [X] bin\genmap.exe - memory map generator ) else ( echo [ ] bin\genmap.exe - not built ) +if exist "bin\zclsp.exe" ( echo [X] bin\zclsp.exe - language server protocol ^& highlighter ) else ( echo [ ] bin\zclsp.exe - not built ) +echo =============================================================================== +echo [1] build VM core (bin\zy.exe) +echo [2] build microcore (bin\zym.exe) +echo [3] build bootstrap tools (bin\zc0.exe, bin\mkgenome.exe, bin\genmap.exe) +echo [4] build LSP server ^& highlighter (bin\zclsp.exe) +echo [5] build everything +echo [6] run Windows test suite (native, LSP, JS, bootstrap) +echo [7] run an example on the native VM (bin\zy.exe) +echo [8] run an example in the JavaScript VM (node src/js/tinyvm.js) +echo [9] run a custom .zc source file +echo [L] check/highlight .zc with LSP (bin\zclsp.exe) +echo [R] launch the interactive REPL (resident compiler) +echo [B] bootstrap and run the full test suite +echo [C] clean generated build directories (bin, build) +echo [Q] quit +echo =============================================================================== +set /p "CHOICE=select an option [1-9, L, R, B, C, Q]: " + +if /i "%CHOICE%"=="1" goto :menu_build_core +if /i "%CHOICE%"=="2" goto :menu_build_micro +if /i "%CHOICE%"=="3" goto :menu_build_tools +if /i "%CHOICE%"=="4" goto :menu_build_lsp +if /i "%CHOICE%"=="5" goto :menu_build_all +if /i "%CHOICE%"=="6" goto :menu_test +if /i "%CHOICE%"=="7" goto :menu_examples +if /i "%CHOICE%"=="8" goto :menu_examples_js +if /i "%CHOICE%"=="9" goto :menu_run_custom +if /i "%CHOICE%"=="l" goto :menu_lsp_check +if /i "%CHOICE%"=="r" goto :menu_repl +if /i "%CHOICE%"=="b" goto :menu_bootstrap +if /i "%CHOICE%"=="c" goto :menu_clean +if /i "%CHOICE%"=="q" exit /b 0 + +echo Invalid choice. Please try again. +timeout /t 2 >nul +goto :main_menu + +REM ========================================================================= +REM Actions +REM ========================================================================= + +:menu_build_core +echo. +echo [Building zy.exe]... +call :build_core +echo. +pause +goto :main_menu + +:menu_build_micro +echo. +echo [Building zym.exe]... +call :build_micro +echo. +pause +goto :main_menu + +:menu_build_tools +echo. +echo [Building tools: zc0.exe, mkgenome.exe, genmap.exe]... +call :build_tools +echo. +pause +goto :main_menu + +:menu_build_lsp +echo. +echo [Building zclsp.exe (LSP Server ^& Highlighter)]... +call :build_lsp +echo. +pause +goto :main_menu + +:menu_build_all +echo. +echo [Building all components]... +call :build_core +if errorlevel 1 goto :build_failed +call :build_micro +if errorlevel 1 goto :build_failed +call :build_tools +if errorlevel 1 goto :build_failed +call :build_lsp +if errorlevel 1 goto :build_failed +echo. +echo [OK] Build complete. +pause +goto :main_menu + +:build_failed +echo. +echo [ERROR] Build failed. See the messages above. +pause +goto :main_menu + +:menu_test +echo. +if not exist "bin\zy.exe" ( + call :build_core + if errorlevel 1 goto :build_failed +) +if not exist "bin\zym.exe" ( + call :build_micro + if errorlevel 1 goto :build_failed +) +echo [Running test suite]... +powershell -NoProfile -ExecutionPolicy Bypass -File "tests\test_win.ps1" +if errorlevel 1 echo [ERROR] Test suite failed. +echo. +pause +goto :main_menu + +:menu_bootstrap +echo. +echo [Running Bootstrap and Full Test Verification]... +call :build_tools +if errorlevel 1 goto :build_failed +if not exist "bin\zy.exe" ( + call :build_core + if errorlevel 1 goto :build_failed +) +powershell -NoProfile -ExecutionPolicy Bypass -File "tests\test_win.ps1" +if errorlevel 1 echo [ERROR] Test suite failed. +echo. +pause +goto :main_menu + +:menu_clean +echo. +echo [Cleaning build artifacts]... +rmdir /s /q build bin 2>nul +echo [OK] Generated bin and build directories removed. +echo. +pause +goto :main_menu + +:menu_run_custom +echo. +set /p "CUSTOM_FILE=Enter path to .zc file (or drag & drop here): " +REM Remove quotes if user dragged & dropped +set "CUSTOM_FILE=%CUSTOM_FILE:"=%" +if not exist "%CUSTOM_FILE%" ( + echo [ERROR] File not found: %CUSTOM_FILE% + echo. + pause + goto :main_menu +) + +if not exist "bin\zy.exe" ( + echo bin\zy.exe not found, building first... + call :build_core + if errorlevel 1 goto :build_failed +) + +echo. +echo =============================================================================== +echo Running: %CUSTOM_FILE% +echo =============================================================================== +bin\zy.exe < "%CUSTOM_FILE%" +echo. +echo =============================================================================== +pause +goto :main_menu + +:menu_lsp_check +echo. +set /p "LSP_FILE=Enter path to .zc file to check/highlight (or drag & drop here): " +set "LSP_FILE=%LSP_FILE:"=%" +if not exist "%LSP_FILE%" ( + echo [ERROR] File not found: %LSP_FILE% + echo. + pause + goto :main_menu +) + +if not exist "bin\zclsp.exe" ( + echo bin\zclsp.exe not found, building first... + call :build_lsp + if errorlevel 1 goto :build_failed +) + +echo. +echo =============================================================================== +echo Analyzing with bin\zclsp.exe: %LSP_FILE% +echo =============================================================================== +bin\zclsp.exe < "%LSP_FILE%" +echo. +echo =============================================================================== +pause +goto :main_menu + +:menu_repl +echo. +if not exist "bin\zy.exe" ( + echo bin\zy.exe not found, building first... + call :build_core + if errorlevel 1 goto :build_failed +) +echo =============================================================================== +echo TinyVM Interactive REPL (resident compiler) +echo Each input chunk separated by backtick (`) is compiled and run. +echo Type expression or function, terminate with ` (or EOF / Ctrl+Z). +echo Type 'quit;' then backtick or press Ctrl+C to exit. +echo =============================================================================== +(type examples\repl.zc & echo ` & more) | bin\zy.exe +echo. +pause +goto :main_menu + +REM ========================================================================= +REM Example Selection Sub-menu +REM ========================================================================= + +:menu_examples +cls +echo =============================================================================== +echo Run TinyVM Examples on Native VM (zy.exe) +echo =============================================================================== +echo [1] hello.zc - Classic "hello, world!" +echo [2] fact.zc - Recursive & iterative factorial computation +echo [3] mandel.zc - Mandelbrot ASCII set using custom enhancement opcode +echo [4] primes.zc - Sieve of Eratosthenes with o3 region compilation +echo [5] life.zc - Conway's Game of Life on a typed torus board +echo [6] lists.zc - Dynamic array with ownership tracking (u, m, d tags) +echo [7] typed.zc - Structs, generic vectors, and reflection +echo [8] wiring.zc - Runtime custom enhancement opcodes demo +echo [9] bf.zc - Brainfuck JIT compiler running inside TinyVM +echo [B] Back to main menu +echo =============================================================================== +set /p "EX_CHOICE=Select an example [1-9, B]: " + +if /i "%EX_CHOICE%"=="b" goto :main_menu +set "EX_FILE=" +if "%EX_CHOICE%"=="1" set "EX_FILE=examples\hello.zc" +if "%EX_CHOICE%"=="2" set "EX_FILE=examples\fact.zc" +if "%EX_CHOICE%"=="3" set "EX_FILE=examples\mandel.zc" +if "%EX_CHOICE%"=="4" set "EX_FILE=examples\primes.zc" +if "%EX_CHOICE%"=="5" set "EX_FILE=examples\life.zc" +if "%EX_CHOICE%"=="6" set "EX_FILE=examples\lists.zc" +if "%EX_CHOICE%"=="7" set "EX_FILE=examples\typed.zc" +if "%EX_CHOICE%"=="8" set "EX_FILE=examples\wiring.zc" +if "%EX_CHOICE%"=="9" set "EX_FILE=examples\bf.zc" + +if "%EX_FILE%"=="" ( + echo Invalid choice. + timeout /t 2 >nul + goto :menu_examples +) + +if not exist "bin\zy.exe" ( + call :build_core + if errorlevel 1 goto :build_failed +) + +echo. +echo =============================================================================== +echo Running: %EX_FILE% +echo =============================================================================== +bin\zy.exe < "%EX_FILE%" +echo. +echo =============================================================================== +pause +goto :menu_examples + +:menu_examples_js +cls +echo =============================================================================== +echo Run TinyVM Examples in JavaScript VM (Node.js) +echo =============================================================================== +echo [1] hello.zc - Classic "hello, world!" +echo [2] fact.zc - Recursive & iterative factorial computation +echo [3] mandel.zc - Mandelbrot ASCII set using custom enhancement opcode +echo [4] primes.zc - Sieve of Eratosthenes with o3 region compilation +echo [5] life.zc - Conway's Game of Life on a typed torus board +echo [6] lists.zc - Dynamic array with ownership tracking (u, m, d tags) +echo [7] typed.zc - Structs, generic vectors, and reflection +echo [8] wiring.zc - Runtime custom enhancement opcodes demo +echo [9] bf.zc - Brainfuck JIT compiler running inside TinyVM +echo [B] Back to main menu +echo =============================================================================== +set /p "JS_EX_CHOICE=Select an example [1-9, B]: " + +if /i "%JS_EX_CHOICE%"=="b" goto :main_menu +set "JS_EX_FILE=" +if "%JS_EX_CHOICE%"=="1" set "JS_EX_FILE=examples\hello.zc" +if "%JS_EX_CHOICE%"=="2" set "JS_EX_FILE=examples\fact.zc" +if "%JS_EX_CHOICE%"=="3" set "JS_EX_FILE=examples\mandel.zc" +if "%JS_EX_CHOICE%"=="4" set "JS_EX_FILE=examples\primes.zc" +if "%JS_EX_CHOICE%"=="5" set "JS_EX_FILE=examples\life.zc" +if "%JS_EX_CHOICE%"=="6" set "JS_EX_FILE=examples\lists.zc" +if "%JS_EX_CHOICE%"=="7" set "JS_EX_FILE=examples\typed.zc" +if "%JS_EX_CHOICE%"=="8" set "JS_EX_FILE=examples\wiring.zc" +if "%JS_EX_CHOICE%"=="9" set "JS_EX_FILE=examples\bf.zc" + +if "%JS_EX_FILE%"=="" ( + echo Invalid choice. + timeout /t 2 >nul + goto :menu_examples_js +) + +echo. +echo =============================================================================== +echo Running in JS: %JS_EX_FILE% +echo =============================================================================== +node src\js\tinyvm.js "%JS_EX_FILE%" +echo. +echo =============================================================================== +pause +goto :menu_examples_js + +REM ========================================================================= +REM Build Routines +REM ========================================================================= + +:build_core +echo Compiling zy.exe (VM Core with JIT/Interpreter and Enhancements)... +if not exist "bin" mkdir bin +"%CC%" -O2 -w -fwrapv -DJTYPE=int src\core\core.c src\core\io_win.c -Isrc/core -o bin\zy.exe +if errorlevel 1 (echo [ERROR] Failed to build bin\zy.exe.& exit /b 1) +echo [OK] bin\zy.exe built successfully. +exit /b 0 + +:build_micro +echo Compiling zym.exe (Portable Microcore)... +if not exist "bin" mkdir bin +"%CC%" -O2 -w -fwrapv src\micro\micro.c src\micro\port_win.c -Isrc/core -Isrc/micro -o bin\zym.exe +if errorlevel 1 (echo [ERROR] Failed to build bin\zym.exe.& exit /b 1) +echo [OK] bin\zym.exe built successfully. +exit /b 0 + +:build_tools +if not exist "bin" mkdir bin +echo Compiling zc0.exe (Bootstrap compiler)... +"%CC%" -O1 -w -fwrapv src\tools\zc0.c -Isrc/core -o bin\zc0.exe +if errorlevel 1 (echo [ERROR] Failed to build bin\zc0.exe.& exit /b 1) +echo Compiling mkgenome.exe (Genome generator)... +"%CC%" -O1 -w -fwrapv src\tools\mkgenome.c -Isrc/core -o bin\mkgenome.exe +if errorlevel 1 (echo [ERROR] Failed to build bin\mkgenome.exe.& exit /b 1) +echo Compiling genmap.exe (Memory map generator)... +"%CC%" -O2 -w -fwrapv src\tools\genmap.c -Isrc/core -o bin\genmap.exe +if errorlevel 1 (echo [ERROR] Failed to build bin\genmap.exe.& exit /b 1) +echo [OK] Bootstrap tools built successfully in bin\. +exit /b 0 + +:build_lsp +echo Compiling zclsp.exe (LSP Server ^& Semantic Highlighter in zc)... +if not exist "bin" mkdir bin +if not exist "bin\zc0.exe" ( + call :build_tools + if errorlevel 1 exit /b 1 +) +if not exist "bin\genmap.exe" ( + call :build_tools + if errorlevel 1 exit /b 1 +) +if exist build\lsp rmdir /s /q build\lsp +mkdir build\lsp\src +if errorlevel 1 (echo [ERROR] Could not create build\lsp.& exit /b 1) +bin\genmap.exe --preset lsp --grow -o build\lsp\map >nul 2>nul +if errorlevel 1 (echo [ERROR] Could not generate the initial LSP map.& exit /b 1) +"%CC%" -O1 -w -fwrapv src\tools\zc0.c -Ibuild\lsp\map -Isrc/core -o build\lsp\zc0.exe +if errorlevel 1 (echo [ERROR] Could not build the LSP sizing compiler.& exit /b 1) +(type build\lsp\map\rt.zc & type build\lsp\map\compiler.zc & type src\lsp\server.zc) | build\lsp\zc0.exe > build\lsp\plain.h +if errorlevel 1 (echo [ERROR] Could not size the LSP genome.& exit /b 1) +set "GLEN=" +for /f "tokens=3" %%G in ('findstr /b /c:"#define GLEN" build\lsp\plain.h') do set "GLEN=%%G" +if not defined GLEN (echo [ERROR] Could not read the LSP genome size.& exit /b 1) +set /a SLOT=(GLEN+4095)/4096*4096 +set /a DOCS_WORK=SLOT+65536 +rmdir /s /q build\lsp\map +bin\genmap.exe --preset lsp --grow --genome %DOCS_WORK% -o build\lsp\map >nul 2>nul +if errorlevel 1 (echo [ERROR] Could not generate the documentation build map.& exit /b 1) +"%CC%" -O1 -w -fwrapv -DZC0DOCS src\tools\zc0.c -Ibuild\lsp\map -Isrc/core -o build\lsp\zc0docs.exe +if errorlevel 1 (echo [ERROR] Could not build the documentation compiler.& exit /b 1) +(type build\lsp\map\rt.zc & type build\lsp\map\compiler.zc & type src\lsp\server.zc) | build\lsp\zc0docs.exe > build\lsp\docs-first.h +if errorlevel 1 (echo [ERROR] Could not build the first documentation genome.& exit /b 1) +set "DOCS_GLEN=" +for /f "tokens=3" %%G in ('findstr /b /c:"#define GLEN" build\lsp\docs-first.h') do set "DOCS_GLEN=%%G" +if not defined DOCS_GLEN (echo [ERROR] Could not read the documentation genome size.& exit /b 1) +set /a DOCS_SLOT=(DOCS_GLEN+4095)/4096*4096 +rmdir /s /q build\lsp\map +bin\genmap.exe --preset lsp --grow --genome %DOCS_SLOT% -o build\lsp\map >nul 2>nul +if errorlevel 1 (echo [ERROR] Could not generate the sized LSP map.& exit /b 1) +"%CC%" -O1 -w -fwrapv -DZC0DOCS src\tools\zc0.c -Ibuild\lsp\map -Isrc/core -o build\lsp\zc0docs.exe +if errorlevel 1 (echo [ERROR] Could not rebuild the documentation compiler.& exit /b 1) +(type build\lsp\map\rt.zc & type build\lsp\map\compiler.zc & type src\lsp\server.zc) | build\lsp\zc0docs.exe > build\lsp\src\lsp_genome.h +if errorlevel 1 (echo [ERROR] Could not build the sized documentation genome.& exit /b 1) +set "FINAL_GLEN=" +for /f "tokens=3" %%G in ('findstr /b /c:"#define GLEN" build\lsp\src\lsp_genome.h') do set "FINAL_GLEN=%%G" +if not "!FINAL_GLEN!"=="!DOCS_GLEN!" (echo [ERROR] LSP genome changed length after map sizing.& exit /b 1) +copy /y src\core\core.c build\lsp\src\ >nul +if errorlevel 1 exit /b 1 +copy /y src\core\io_win.c build\lsp\src\ >nul +if errorlevel 1 exit /b 1 +copy /y src\core\fops.h build\lsp\src\ >nul +if errorlevel 1 exit /b 1 +copy /y src\core\bops.h build\lsp\src\ >nul +if errorlevel 1 exit /b 1 +copy /y src\core\stack.h build\lsp\src\ >nul +if errorlevel 1 exit /b 1 +copy /y build\lsp\map\memmap.h build\lsp\src\ >nul +if errorlevel 1 exit /b 1 +"%CC%" -O2 -w -fwrapv -DJTYPE=int -DZY_SANDBOX -DUSE_LSP_GENOME -Ibuild\lsp\src build\lsp\src\core.c build\lsp\src\io_win.c -o bin\zclsp.exe +if errorlevel 1 (echo [ERROR] Failed to build bin\zclsp.exe.& exit /b 1) +echo [OK] bin\zclsp.exe built successfully with %FINAL_GLEN% words. +exit /b 0 + +REM ========================================================================= +REM CLI Command Handler +REM ========================================================================= + +:handle_cli +set "CMD=%~1" +set "ARG2=%~2" + +if /i "%CMD%"=="build" ( + if "%ARG2%"=="" goto :cli_build_all + if /i "%ARG2%"=="all" goto :cli_build_all + if /i "%ARG2%"=="core" ( call :build_core & exit /b !errorlevel! ) + if /i "%ARG2%"=="micro" ( call :build_micro & exit /b !errorlevel! ) + if /i "%ARG2%"=="tools" ( call :build_tools & exit /b !errorlevel! ) + if /i "%ARG2%"=="lsp" ( call :build_lsp & exit /b !errorlevel! ) + echo Unknown build target: %ARG2% + exit /b 1 +) + +if /i "%CMD%"=="test" ( + call :build_core + if errorlevel 1 exit /b 1 + call :build_micro + if errorlevel 1 exit /b 1 + powershell -NoProfile -ExecutionPolicy Bypass -File "tests\test_win.ps1" + exit /b !errorlevel! +) + +if /i "%CMD%"=="run" ( + if "%ARG2%"=="" ( + echo Error: Missing .zc file to run. + echo Usage: tinyvm.bat run path\to\program.zc + exit /b 1 + ) + if not exist "bin\zy.exe" ( + call :build_core + if errorlevel 1 exit /b 1 + ) + bin\zy.exe < "%SOURCE_ARG%" + exit /b !errorlevel! +) + +if /i "%CMD%"=="run-micro" ( + if "%ARG2%"=="" ( + echo Error: Missing .zc file to run. + echo Usage: tinyvm.bat run-micro path\to\program.zc + exit /b 1 + ) + if not exist "bin\zym.exe" ( + call :build_micro + if errorlevel 1 exit /b 1 + ) + bin\zym.exe < "%SOURCE_ARG%" + exit /b !errorlevel! +) + +if /i "%CMD%"=="js" ( + if "%ARG2%"=="" ( + echo Error: Missing .zc file to run. + echo Usage: tinyvm.bat js path\to\program.zc + exit /b 1 + ) + node src\js\tinyvm.js "%SOURCE_ARG%" + exit /b !errorlevel! +) + +if /i "%CMD%"=="lsp" ( + if "%ARG2%"=="" ( + echo Error: Missing .zc file to check. + echo Usage: tinyvm.bat lsp path\to\program.zc + exit /b 1 + ) + if not exist "bin\zclsp.exe" ( + call :build_lsp + if errorlevel 1 exit /b 1 + ) + bin\zclsp.exe < "%SOURCE_ARG%" + exit /b !errorlevel! +) + +if /i "%CMD%"=="repl" ( + if not exist "bin\zy.exe" ( + call :build_core + if errorlevel 1 exit /b 1 + ) + (type examples\repl.zc & echo ` & more) | bin\zy.exe + exit /b !errorlevel! +) + +if /i "%CMD%"=="clean" ( + rmdir /s /q build bin 2>nul + echo [OK] Generated bin and build directories removed. + exit /b 0 +) + +if /i "%CMD%"=="help" ( + goto :show_help +) +if /i "%CMD%"=="-h" ( + goto :show_help +) +if /i "%CMD%"=="--help" ( + goto :show_help +) + +echo Unknown command: %CMD% +echo. +exit /b 1 +:show_help +echo TinyVM Windows CLI usage: +echo tinyvm.bat launch interactive build ^& run menu +echo tinyvm.bat build [all^|core^|micro^|tools^|lsp] +echo compile TinyVM executables +echo tinyvm.bat run ^ run a .zc program on VM core (zy.exe) +echo tinyvm.bat run-micro ^ run a .zc program on microcore (zym.exe) +echo tinyvm.bat js ^ run a .zc program on the JavaScript VM (node) +echo tinyvm.bat lsp ^ check/highlight a .zc program with zclsp.exe +echo tinyvm.bat test run the full Windows test suite +echo tinyvm.bat repl launch the resident compiler REPL +echo tinyvm.bat clean remove generated bin and build directories +exit /b 0 + +:cli_build_all +call :build_core +if errorlevel 1 exit /b 1 +call :build_micro +if errorlevel 1 exit /b 1 +call :build_tools +if errorlevel 1 exit /b 1 +call :build_lsp +exit /b !errorlevel! diff --git a/tinyvm.sh b/tinyvm.sh new file mode 100755 index 0000000..12e3a15 --- /dev/null +++ b/tinyvm.sh @@ -0,0 +1,106 @@ +#!/usr/bin/env bash +# interactive build and run menu, with the same commands as tinyvm.bat. +set -u + +tinyvm_root=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd) || exit 1 +tinyvm_caller=$PWD +cd -- "$tinyvm_root" || exit 1 + +usage() { + cat <<'HELP' +usage: tinyvm.sh [command] + build [all|core|micro|tools|lsp] build executables + run FILE.zc run on the native VM + run-micro FILE.zc run on the portable microcore + js FILE.zc run on the JavaScript VM + lsp FILE.zc check a source file + test [test options] run the full test suite + check run the minimal test suite + genome rebuild the compiler and verify the fixpoint + repl start the resident compiler REPL + clean remove generated build output + help show this help +with no command, open the interactive menu. +HELP +} + +build() { + case "${1:-all}" in + all) make ;; + core) make bin/zy ;; + micro) make bin/zym ;; + tools) make bin/zc0 bin/mkgenome bin/genmap ;; + lsp) make lsp ;; + *) printf 'Unknown build target: %s\n' "$1" >&2; return 2 ;; + esac +} + +source_path() { + local input=$1 + if [[ $input != /* ]]; then input="$tinyvm_caller/$input"; fi + [[ -f $input ]] || { printf 'Source file not found: %s\n' "$input" >&2; return 2; } + printf '%s\n' "$input" +} + +run_file() { + local mode=$1 input exe + [[ $# == 2 ]] || { printf 'Usage: tinyvm.sh %s FILE.zc\n' "$mode" >&2; return 2; } + input=$(source_path "$2") || return + case $mode in + run) exe=bin/zy ;; + run-micro) exe=bin/zym ;; + lsp) exe=bin/zclsp ;; + js) command -v node >/dev/null || { echo 'node.js is required.' >&2; return 1; }; make js || return; node src/js/tinyvm.js "$input"; return ;; + esac + make "$exe" || return + "$exe" < "$input" +} + +repl() { + make bin/zy || return + echo 'Enter statements or fn definitions, ending each chunk with a backtick. Ctrl-D exits.' + { cat examples/repl.zc; printf '`'; cat; } | bin/zy +} + +dispatch() { + local action=${1:-help} + [[ $# == 0 ]] || shift + case $action in + build) build "$@" ;; + run|run-micro|js|lsp) run_file "$action" "$@" ;; + test) bash tests/test.sh "$@" ;; + check) make check ;; + genome) make genome ;; + repl) repl ;; + clean) make clean ;; + help|-h|--help) usage ;; + *) printf 'Unknown command: %s\n' "$action" >&2; usage >&2; return 2 ;; + esac +} + +menu() { + local choice input status + while true; do + printf '\nTinyVM — build and run\n' + printf ' 1 build everything\n 2 run a .zc file\n 3 run on the microcore\n 4 run in JavaScript\n 5 check with the language server\n 6 run tests\n 7 rebuild genome\n R REPL\n C clean build output\n Q quit\n' + read -r -p 'select an option: ' choice || return 0 + status=0 + case $choice in + 1) build || status=$? ;; + 2|3|4|5) + read -r -p 'source file: ' input || return 0 + case $choice in 2) choice=run ;; 3) choice=run-micro ;; 4) choice=js ;; 5) choice=lsp ;; esac + run_file "$choice" "$input" || status=$? + ;; + 6) dispatch test || status=$? ;; + 7) dispatch genome || status=$? ;; + r|R) repl || status=$? ;; + c|C) dispatch clean || status=$? ;; + q|Q) return 0 ;; + *) echo 'Invalid option.'; continue ;; + esac + [[ $status == 0 ]] || printf 'Command failed with status %d.\n' "$status" >&2 + done +} + +if [[ $# == 0 ]]; then menu; else dispatch "$@"; fi -- cgit v1.2.3