After adding the "too many closures" error, we tried to pop a NULL scope
during cleanup - this can segfault. Handle bad scope state better during
cleanup.
the same as our register size.
Mostly for x32, but gets rid of the hard-coded ifdef with logic that
calls out the issue and makes it clear what assumptions we are making.
Previously, we had silent miscompiles in some cases.
The ideal fix is to let us reference upvalues beyond slot 255. One way
would be to add a new instruction pair that would reference a 16-bit
constant index, which would itself encode a pair of at least 16 bit
values instead of the current instruction which encodes a pair of 8 bit
values.
Streams sent to another thread with ev/thread were only re-registered with the receiving thread's event loop on the poll backend. On kqueue and epoll, waiting on them hung forever, and on epoll closing them raised "No such file or directory".
Co-authored-by: jcates <jcates@>
GCC aligns alloca to the widest enabled vector type (16 bytes, 32 with avx,
64 with avx-512). With AVX the block address is `lea 0x1f(%rsp)` rounded down
to the nearest 32-divisible, but half the time the block starts 16 bytes higher
so the function reads the stack arguments 2 slots away from where it should.
Without AVX the address is `lea 0xf(%rsp)` which is the stack pointer.
As key types can't change during lookup, we only need to check them once. The check is now
inlined in util.h, so structs and tables can use the same implementation. (Structs had a simpler
copy of the table implementation before, now they just ignore the table specific parts as they
lack deleted buckets)
Co-authored-by: Alex Alejandre <alex@alexalejandre.com>
* Split assert-error into pieces and remove a piece
* Add helpful reminder in comment
* Format hack
* Use underscores to help readability
---------
Co-authored-by: sogaiu <983021772@users.noreply.github.com>
* Stop using abbreviations
* Put parser tests first
* Fix incorrect code
* Deal with linting
* Change some comments and labels
* Improve test
* Use syntax literals for int/u64 and int/s64
---------
Co-authored-by: sogaiu <983021772@users.noreply.github.com>
* Add bitwise key check to skip janet_equals calls
* Skip janet_equals for identity-comparing keys
Since symbols, keywords, non-0 numbers, bools, tables, buffers, arrays, funcs and fibers are only equal by identity, we now fail them all if the bitwise check fails instead of passing them to `janet_equals`, so lookups only call `janet_equals` for strings, tuples, structs, abstract values and 0
* Skip traversal in janet_equals for non tuples and structs
---------
Co-authored-by: Alex Alejandre <alex@alexalejandre.com>
Found by @pepe with Claude. This makes some changes to the initial AI
suggested fix but is mostly verbatim. The memory leak was of the form of
ever increase GC roots with threaded channels.
We were doing forward instead of a backward search. This is slower, and
for the unrootall case was also incorrect. unrootall was not commonly
used but should still work as expected.
Bug found by @pepe with help from Claude.
Just mark janet_vm.top_dyns during mark phase of collection. It was
skipped as it is not part of the usual interpreter flow and was missed
in testing. Also add a test case for it.
Codegen may allocate and then never use slots. Free up those slots
and rewrite bytecode to be smaller. The resulting bytecode will be
slightly faster, result in better inlining, and be somewhat normalized
for further optimizations.
Currently very simple hueristic, and not supported
on many functions that instatiate closures or load/set upvalues.
We also now, more than ever, need a slot-compaction routine to reduce
the number of slots used by functions.
The goal of optimize=0 is both fast compilation and perfect debug info.
We would like to do some simple optimizations but the possibility of
breaking symbol mappings prevents it.
Dead code elimination and value numbering may remove
closures and constants from a function def - remove these from our
constant and defs buffers to avoid keeping phantom references and
bloating marshalled function size.
This exposes the optimization level for a few reasons.
First, Local value numbering exposed some bad codegen. Playing with
optimiaztion level exposes this more easily.
When compiling dead code in a known unused scope, we
would generate code that tried to load invalid slots.
Make sure that unused code will still pass `janet_verify()` as
invalid code could trigger bad behavior in our optimizer or garbage
collector.
Since the code was proven dead, this would not trigger any runtime
behavior issues though.
Second, even trivial optimizations tend to break debug information. For
example, constant propogation will eliminated symbol mappings which can
be useful for debugging. By default, don't eliminate any symbol map
debug info.
However, we will likely need to gate some of these optimizations
behind a switch as they are beginning to destruct debug info. For
example, local value number maybe should be off by default or at the
very least turned off for named loads.