mirror of
https://github.com/brazilofmux/tinymux
synced 2026-08-13 00:23:11 -04:00
Merge pull request #2105 from brazilofmux/fix/2101-2066-lowhanging
fix(tests/dbt, jit): max_dispatch margin (#2101) and string pool = LBUF (#2066)
This commit is contained in:
commit
5845a81f14
4 changed files with 125 additions and 24 deletions
|
|
@ -284,14 +284,30 @@ struct rv_compiler {
|
|||
static constexpr size_t MEM_SIZE = 0x540000; // 4 MB map + 1 MB heap + 256 KB doubles scratch
|
||||
static constexpr uint64_t CODE_BASE = 0x0000;
|
||||
static constexpr uint64_t CODE_LIMIT = 0x1000;
|
||||
// String pool sized to hold one full LBUF (32768) of constants (#2066).
|
||||
// Previously STR_LIMIT was 0x4000 (12 KB) while OUT_SLOT tracked LBUF at
|
||||
// 32 KB, so any expression whose pooled constants exceeded ~12 KB fell
|
||||
// back to the AST evaluator with COMPILATION FAILED. FARGS moved up to
|
||||
// keep the same 16 KB capacity without overlapping the string region.
|
||||
// Cached programs from the old layout fail the fargs-range check on load
|
||||
// and recompile cleanly.
|
||||
static constexpr uint64_t STR_BASE = 0x1000;
|
||||
static constexpr uint64_t STR_LIMIT = 0x4000;
|
||||
static constexpr uint64_t FARGS_BASE = 0x4000;
|
||||
static constexpr uint64_t FARGS_LIMIT= 0x8000;
|
||||
static constexpr uint64_t STR_LIMIT = 0x9000; // STR_BASE + 0x8000 (= LBUF_SIZE)
|
||||
static constexpr uint64_t FARGS_BASE = 0x9000;
|
||||
static constexpr uint64_t FARGS_LIMIT= 0xD000; // FARGS_BASE + 0x4000
|
||||
|
||||
static constexpr uint64_t BLOB_BASE = 0x10000;
|
||||
static constexpr uint64_t BLOB_LIMIT = 0x40000;
|
||||
|
||||
static_assert(CODE_LIMIT == STR_BASE,
|
||||
"code region must abut the string pool");
|
||||
static_assert(STR_LIMIT == FARGS_BASE,
|
||||
"string pool must abut the fargs pool");
|
||||
static_assert(FARGS_LIMIT <= BLOB_BASE,
|
||||
"fargs pool must not overlap the blob region");
|
||||
static_assert(STR_LIMIT - STR_BASE >= 32768,
|
||||
"string pool must hold one LBUF of constants (#2066)");
|
||||
|
||||
// Output slots — sized to match LBUF_SIZE (32768).
|
||||
// Stack-allocated: output buffers grow downward from STACK_TOP.
|
||||
// The compiled code prologue decrements SP by the total output
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@
|
|||
# A JIT-compiled expression that evaluates another expression (u(), ulocal(),
|
||||
# or any outer construct wrapping one) runs the inner compile on the shared
|
||||
# heap, whose string pool lives at 0x40000..0x60000 rather than the one-shot
|
||||
# compiler's 0x1000..0x4000. hir_codegen's FP slot allocator bounded itself
|
||||
# against the static rv_compiler::STR_LIMIT (0x4000) instead of this compile's
|
||||
# compiler's 0x1000..0x9000 (#2066). hir_codegen's FP slot allocator bounded
|
||||
# itself against the static rv_compiler::STR_LIMIT instead of this compile's
|
||||
# own str_pool_limit, so every FP slot address stayed 0 and all float
|
||||
# temporaries aliased guest address 0 (#1159).
|
||||
#
|
||||
|
|
|
|||
|
|
@ -410,8 +410,40 @@
|
|||
#
|
||||
&tr.tc055 test_rvbench_fn=
|
||||
@log smoke=BENCH055:
|
||||
[rvbench(iter(a|1 b|2 c|3 d|4 e|5, before(##\,|)),10000)];
|
||||
@trig me/tr.done
|
||||
[rvbench(iter(a|1 b|2 c|3 d|4 e|5, before(##\,|)),10000)]
|
||||
-
|
||||
#
|
||||
# Test Case #56 - string-pool capacity is one LBUF (#2066).
|
||||
#
|
||||
# The former cliff was exact and reproducible: rvbench(shuffle(repeat(a,N)),2)
|
||||
# returned #-1 COMPILATION FAILED for N >= 12272 because STR_LIMIT-STR_BASE
|
||||
# was 12288 bytes. The pool is now 32768 (= LBUF_SIZE). N=15000 sits above
|
||||
# the old cliff and well under the new one; if the pool regresses, this
|
||||
# fails with COMPILATION FAILED rather than a silent fallback to the AST.
|
||||
#
|
||||
&tr.tc056 test_rvbench_fn=
|
||||
@if cand(
|
||||
u(me/tr.has_jit),
|
||||
not(strmatch(setr(0,rvbench(shuffle(repeat(a,15000)),2)),*COMPILATION FAILED*))
|
||||
)=
|
||||
{
|
||||
@log smoke=TC056: str pool holds >12KB constants (#2066). Succeeded.;
|
||||
@trig me/tr.done
|
||||
},
|
||||
{
|
||||
@if u(me/tr.has_jit)=
|
||||
{
|
||||
@log smoke=TC056: str pool holds >12KB constants (#2066). Failed (%q0).;
|
||||
@trig me/tr.done
|
||||
},
|
||||
{
|
||||
@log smoke=TC056: str pool holds >12KB constants (#2066). Skipped (JIT not enabled).;
|
||||
@trig me/tr.done
|
||||
}
|
||||
}
|
||||
-
|
||||
&tr.has_jit test_rvbench_fn=
|
||||
[strmatch(jitstats(),*eval_attempts=*)]
|
||||
-
|
||||
&tr.done test_rvbench_fn=
|
||||
@log smoke=End rvbench() benchmarks.;
|
||||
|
|
|
|||
|
|
@ -258,7 +258,21 @@ static bool run_case(const alarm_case &c)
|
|||
// max_dispatch is the other guard the issue names. It is a count, not a
|
||||
// deadline, so it needs its own case: a loop that never reaches the dispatcher
|
||||
// cannot exceed a dispatch limit either.
|
||||
static bool run_max_dispatch_case()
|
||||
//
|
||||
// The property under test does not depend on the value of max_dispatch: only
|
||||
// that dbt_run returns -2 once dispatch_count exceeds it. 10000 cost ~1.2s
|
||||
// on a fast host and left only a 4x margin against GRACE_MS, so a throttled
|
||||
// laptop or busy CI runner timed out and the message blamed #1571 (#2101).
|
||||
// 1000 proves the same bound with ~40x margin and ~1s less wall clock.
|
||||
//
|
||||
static constexpr uint64_t MAX_DISPATCH_BOUND = 1000;
|
||||
static constexpr uint64_t MAX_DISPATCH_PROBE = 100;
|
||||
|
||||
// Run the counted loop under max_dispatch and wait up to grace_ms for -2.
|
||||
// On timeout the worker is detached (still spinning) and true is returned
|
||||
// in *timed_out. Caller owns the heap state either way.
|
||||
static int run_max_dispatch_once(
|
||||
uint64_t max_dispatch, int grace_ms, bool *timed_out)
|
||||
{
|
||||
auto *mem = new std::vector<uint8_t>(MEM_SIZE, 0);
|
||||
auto *dbt = new dbt_state_t();
|
||||
|
|
@ -269,9 +283,11 @@ static bool run_max_dispatch_case()
|
|||
|
||||
if (dbt_init(dbt, mem->data(), MEM_SIZE, alarm_test_ecall, nullptr) != 0) {
|
||||
fprintf(stderr, " FAIL: max_dispatch: dbt_init\n");
|
||||
return false;
|
||||
delete done; delete rc; delete dbt; delete mem;
|
||||
*timed_out = false;
|
||||
return 1; // non-zero, not -2
|
||||
}
|
||||
dbt->max_dispatch = 10000;
|
||||
dbt->max_dispatch = max_dispatch;
|
||||
|
||||
std::thread worker([dbt, entry, rc, done] {
|
||||
rc->store(dbt_run(dbt, entry, MEM_SIZE - 16));
|
||||
|
|
@ -279,35 +295,72 @@ static bool run_max_dispatch_case()
|
|||
});
|
||||
|
||||
auto deadline = std::chrono::steady_clock::now()
|
||||
+ std::chrono::milliseconds(GRACE_MS);
|
||||
+ std::chrono::milliseconds(grace_ms);
|
||||
while (!done->load() && std::chrono::steady_clock::now() < deadline) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
}
|
||||
|
||||
if (!done->load()) {
|
||||
fprintf(stderr,
|
||||
" FAIL: max_dispatch: dbt_run did not return within %d ms\n",
|
||||
GRACE_MS);
|
||||
fprintf(stderr,
|
||||
" max_dispatch=%llu was never reached because the loop\n"
|
||||
" never returns to the dispatcher to be counted (#1571).\n",
|
||||
static_cast<unsigned long long>(dbt->max_dispatch));
|
||||
worker.detach();
|
||||
return false;
|
||||
// State deliberately leaked: the worker still owns it.
|
||||
*timed_out = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
worker.join();
|
||||
if (rc->load() != -2) {
|
||||
int result = rc->load();
|
||||
dbt_cleanup(dbt);
|
||||
delete done; delete rc; delete dbt; delete mem;
|
||||
*timed_out = false;
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool run_max_dispatch_case()
|
||||
{
|
||||
bool timed_out = false;
|
||||
int rc = run_max_dispatch_once(MAX_DISPATCH_BOUND, GRACE_MS, &timed_out);
|
||||
|
||||
if (timed_out) {
|
||||
// Discriminate "dispatcher never reached" (#1571) from "host is
|
||||
// slower than the budget assumes" (#2101). A tiny bound on a
|
||||
// fresh state: if that returns -2, the dispatcher is fine and
|
||||
// only the wall clock was wrong.
|
||||
bool probe_timed_out = false;
|
||||
int probe_rc = run_max_dispatch_once(
|
||||
MAX_DISPATCH_PROBE, GRACE_MS, &probe_timed_out);
|
||||
|
||||
fprintf(stderr,
|
||||
" FAIL: max_dispatch: dbt_run did not return within %d ms\n"
|
||||
" (max_dispatch=%llu).\n",
|
||||
GRACE_MS,
|
||||
static_cast<unsigned long long>(MAX_DISPATCH_BOUND));
|
||||
if (!probe_timed_out && probe_rc == -2) {
|
||||
fprintf(stderr,
|
||||
" Probe with max_dispatch=%llu returned -2, so the\n"
|
||||
" dispatcher IS being reached — this host is more\n"
|
||||
" than ~4x slower than the budget assumes, not a\n"
|
||||
" #1571 regression. Re-run on an idle box, or lower\n"
|
||||
" MAX_DISPATCH_BOUND further (#2101).\n",
|
||||
static_cast<unsigned long long>(MAX_DISPATCH_PROBE));
|
||||
} else {
|
||||
fprintf(stderr,
|
||||
" Probe with max_dispatch=%llu also failed to return\n"
|
||||
" -2 in time (timed_out=%d rc=%d). The loop may never\n"
|
||||
" return to the dispatcher to be counted (#1571).\n",
|
||||
static_cast<unsigned long long>(MAX_DISPATCH_PROBE),
|
||||
probe_timed_out ? 1 : 0, probe_rc);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (rc != -2) {
|
||||
fprintf(stderr,
|
||||
" FAIL: max_dispatch: dbt_run returned %d, expected -2\n",
|
||||
rc->load());
|
||||
dbt_cleanup(dbt);
|
||||
rc);
|
||||
return false;
|
||||
}
|
||||
|
||||
printf(" ok: max_dispatch bound (dbt_run returned -2)\n");
|
||||
dbt_cleanup(dbt);
|
||||
delete done; delete rc; delete dbt; delete mem;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue