Repository navigation
Let an embedder set PM_ARENA_INITIAL_SIZE, and restore the newlib inttypes workaround - #4308
Conversation
The arena's first block is 8 KB, sized for a host. An embedder on a small heap parses short programs (a REPL line uses about 6 KB in two arenas) and pays two 8 KB blocks for each; with the constant under #ifndef it can build with -DPM_ARENA_INITIAL_SIZE=2048 instead.
61ebe51 made defines.h include <sys/types.h> ahead of <inttypes.h>: with an arm-none-eabi toolchain whose <stdint.h> is GCC's own, newlib's <inttypes.h> never sees the marker it tests before defining the 64-bit PRI macros, and PRIu64 goes undefined while PRIu32 does not. 400b217 ("Remove defines") took the header out and the workaround with it, so a cross build of integer.c and errors_format.c, the two files that include <inttypes.h> now, fails on such a toolchain (Debian's gcc-arm-none-eabi 13.2.1 with newlib). Put the include back in those two files, with the note.
The constant pool starts at source size / 120 entries, 4 at the least, and doubles at a load of 3/4; each outgrown pair of arrays stays in the metadata arena. An embedder that interns a fixed set of names into the pool at every parse (mruby's compiler puts its 58 presyms there, as its symbol table) grows a short program's pool from 4 to 128 and leaves about 3 KB behind, as much as the pool itself. With the minimum under #ifndef it can build with -DPM_CONSTANT_POOL_MIN_CAPACITY=128: measured on a 32-bit build with 2 KB arena blocks, parsing one REPL line then holds 6 KB instead of 10 KB. The default stays 4.
1a60479 to
d2bd15a
Compare
|
@hasumikin happy to release a new version for this. Thanks for the quick fixes and testing of 2.0.0! A quick question: I added a scratch arena to the parser for stuff that shouldn't outlive the parse. It's freed at the end of parsing. Is that going to be alright? It's initialized and freed within the bounds of |
|
@kddnewton On a 32-bit build with PM_ARENA_INITIAL_SIZE=2048 and PM_CONSTANT_POOL_MIN_CAPACITY=128 (the PicoRuby device settings), current main against the three commits alone:
So the scratch arena is a small win for us. Thank you for your work!!! |
Problem
Three things stop Prism 2.0.0 from being embedded on a microcontroller as Prism 1.9 was. mruby's compiler (mruby/mruby#7707) and PicoRuby, which runs that compiler on RP2040/RP2350 boards with a few hundred KB of RAM, need both.
The arena's first block is 8 KB, and the size is fixed.
PM_ARENA_INITIAL_SIZEinsrc/arena.cis a plain#define. A parser has two arenas (the caller's node arena and its own metadata arena), so parsing one REPL line costs two 8 KB blocks, 16 KB plus headers, for about 6 KB of data (most of it the constant pool, which mruby pre-seeds with its symbol table). Measured on a 32-bit build: 16 KB held per parse, against 6 KB with Prism 1.9.The 64-bit
PRI*macros are undefined on a common cross toolchain. 61ebe51 ("Fix cross-compile issue with newlib on Ubuntu 24.04") madedefines.hinclude<sys/types.h>ahead of<inttypes.h>: with an arm-none-eabi GCC whose<stdint.h>is GCC's own, newlib's<inttypes.h>never sees the marker it tests before it defines the 64-bit macros, soPRIu64is undefined whilePRIu32is defined. 400b217 ("Remove defines") removed the header and the workaround with it. Prism 2.0.0 fails to cross-compile there:Seen with Debian's gcc-arm-none-eabi 13.2.1 and its newlib.
The constant pool starts at 4 entries for a short source, and every growth step leaves its old arrays in the arena.
pm_parser_initsizes the pool from the source length (size / 120, at least 4). The pool doubles when it is 3/4 full, and since the arrays live in the metadata arena, the outgrown ones are never freed until the parser is cleaned up. mruby interns its 58 pre-defined symbols into the pool before every parse, so a REPL line goes 4 -> 8 -> 16 -> 32 -> 64 -> 128 and carries all six generations of the hash and the constant array. That is most of the 10 KB a REPL line still holds with 2 KB arena blocks.Fix
src/arena.c:PM_ARENA_INITIAL_SIZEunder#ifndef, so an embedder can build with-DPM_ARENA_INITIAL_SIZE=2048. The default stays 8 KB and nothing else changes. With 2 KB blocks the same REPL line holds 10 KB on the device (5 blocks); an 18 KB source file is unaffected, sincepm_parser_initreserves its blocks from the source size.src/integer.c,src/errors_format.c: include<sys/types.h>before<inttypes.h>again, in the two files that include<inttypes.h>now, with the note thatdefines.hcarried.src/prism.c: aPM_CONSTANT_POOL_MIN_CAPACITYmacro under#ifndef, default 4, as the lower bound of the pool capacity thatpm_parser_initcomputes. An embedder that seeds the pool builds with-DPM_CONSTANT_POOL_MIN_CAPACITY=128; the default build is unchanged. With it, the REPL line on the device holds 6 KB per parse, the same as with Prism 1.9.Verification
bundle exec rake compile teston the host passes (22098 tests, 2243812 assertions, 0 failures): the default block size and pool capacity are unchanged and the extra include is the one 1.9 had.Request
These are the only changes mruby and PicoRuby need from Prism to move to 2.0. Once this is merged, could you cut a 2.0.1 release? mruby vendors a released Prism, so the move waits on a tag that includes these commits.