fix: lower short catch as statements, and drop core=dynamic
Compiling okcatmov.fe's output crashed wcc386 hard enough to take DOSBox-X down with it -- "no byte handler for write to ffffffc", emulator exit 1, the whole suite lost rather than one case failing. `result catch fallback` in return position lowered to `((tmp = X), tmp.e ? fallback : tmp.v)`, and with a moved operand X is itself `(fe_live_x=0, x)`, so the compiler met a struct assignment whose right side was a comma expression. Lower it as statements, and clear a move flag on its own line rather than inside the assignment. Revert core=dynamic from the previous commit. It was about 5x faster and it is wrong: the recompiler loses abort()'s exit status, so a trapping program exits 0 and the M3 bounds cases stop reporting the trap they exist to prove. Checked against a compiler built under core=normal, so the fault is in running the generated program, not in building the compiler. Also fix the build cache: BUILD.BAT puts the Watcom binaries on PATH, and skipping it on a cache hit left the case commands without it, which silently changed how the trap programs terminated. Reproduce that line. Per milestone, warm cache: m1 2s, m2 5s, m3 20s, m4 7s, m5 6s, m6 14s, m7 11s. All green, 220 cases.
This commit is contained in:
@@ -670,6 +670,26 @@ static void m7_emit_call(FeEmitter *e, FeNode *n)
|
||||
}
|
||||
}
|
||||
|
||||
/* `dst = <src>;` as a statement, avoiding a comma expression on the right.
|
||||
|
||||
A consumed identifier lowers to `(fe_live_x=0, x)`. When dst is a struct,
|
||||
Watcom crashes on a struct assignment whose right side is a comma expression
|
||||
-- hard enough to take DOSBox-X down with it -- so clear the move flag as its
|
||||
own statement and assign the plain name. */
|
||||
static void m7_emit_assign_stmt(FeEmitter *e, const char *dst, FeNode *src)
|
||||
{
|
||||
if (src && src->kind==FE_N_IDENT && (src->flags & FE_OWN_NODE_CONSUMED) &&
|
||||
src->sem_type && type_needs_drop(src->sem_type)) {
|
||||
pad(e); fputs("fe_live_",e->out); fputs(cname(src,"owned"),e->out);
|
||||
fputs("=0;\n",e->out);
|
||||
pad(e); fputs(dst,e->out); fputs(" = ",e->out);
|
||||
fputs(cname(src,"fe_missing"),e->out); fputs(";\n",e->out);
|
||||
return;
|
||||
}
|
||||
pad(e); fputs(dst,e->out); fputs(" = ",e->out);
|
||||
emit_expr(e,src); fputs(";\n",e->out);
|
||||
}
|
||||
|
||||
static void m7_emit_raw_expr(FeEmitter *e, FeNode *n)
|
||||
{
|
||||
FeNode *x;
|
||||
@@ -1208,6 +1228,32 @@ static void emit_stmt(FeEmitter *e, FeNode *n)
|
||||
fputs(";\n",e->out);
|
||||
emit_cleanup_all(e);
|
||||
pad(e); fputs("return fe_return_value;\n",e->out);
|
||||
} else if (n->a && n->a->kind==FE_N_BINARY && !n->a->c &&
|
||||
fe_m7_lazy_kind(n->a)==FE_M7_LAZY_CATCH &&
|
||||
e->current_ret && e->current_ret->kind!=FE_TYPE_VOID) {
|
||||
/* Short catch in return position. As an expression this lowers to
|
||||
`((tmp = X), tmp.e ? fallback : tmp.v)`, and when X carries a move
|
||||
it becomes a struct assignment whose right side is itself a comma
|
||||
expression -- which crashes wcc386 hard enough to take DOSBox-X
|
||||
down with it. The same lowering as statements is also plainer. */
|
||||
FeNode *cx=n->a;
|
||||
FeType *res=cx->a ? cx->a->sem_type : 0;
|
||||
int has_value=res && res->error_value &&
|
||||
res->error_value->kind!=FE_TYPE_VOID;
|
||||
m7_emit_assign_stmt(e,cx->aux_cname,cx->a);
|
||||
pad(e); fputs("if (",e->out); fputs(cx->aux_cname,e->out);
|
||||
if (has_value) fputs(".e",e->out);
|
||||
fputs(") {\n",e->out); ++e->indent;
|
||||
pad(e); fputs("fe_return_value = ",e->out);
|
||||
emit_expr(e,cx->b); fputs(";\n",e->out);
|
||||
--e->indent; pad(e); fputs("} else {\n",e->out); ++e->indent;
|
||||
pad(e); fputs("fe_return_value = ",e->out);
|
||||
fputs(cx->aux_cname,e->out);
|
||||
if (has_value) fputs(".v",e->out);
|
||||
fputs(";\n",e->out);
|
||||
--e->indent; pad(e); fputs("}\n",e->out);
|
||||
emit_cleanup_all(e);
|
||||
pad(e); fputs("return fe_return_value;\n",e->out);
|
||||
} else {
|
||||
if (n->a && e->current_ret && e->current_ret->kind!=FE_TYPE_VOID) {
|
||||
pad(e); fputs("fe_return_value = ",e->out);
|
||||
|
||||
@@ -158,7 +158,10 @@ def _batch(cases: list[Case], *, show_dos: bool, trace_dos: bool,
|
||||
build = "call BUILD.BAT" if trace_dos else "call BUILD.BAT > RESULTS\\BUILD.LOG"
|
||||
if prebuilt:
|
||||
# FEC.EXE was restored from cache; BUILD.BAT would delete and rebuild it.
|
||||
build = "echo OK>BUILD.OK"
|
||||
# It also puts the Watcom binaries on PATH, which the case commands need
|
||||
# after it, so that line has to be reproduced rather than skipped.
|
||||
build = ("set PATH=%WATCOM%\\BINW;%WATCOM%\\BINP;%PATH%\r\n"
|
||||
"echo OK>BUILD.OK")
|
||||
lines = [
|
||||
"@echo off", "if not exist RESULTS md RESULTS", "if not exist OUT md OUT",
|
||||
"set WATCOM=W:", "set INCLUDE=W:\\H",
|
||||
@@ -284,13 +287,13 @@ def run_suite(cases: list[Case], *, keep: bool = False, show_dos: bool = False,
|
||||
console = run_root / "CONSOLE.LOG"
|
||||
config = run_root / "DOSBOX.CON"
|
||||
config.write_text(
|
||||
# core=auto falls back to the interpreter in real mode, which is
|
||||
# where the 16-bit compiler build spends its time. Nothing here is
|
||||
# timing sensitive -- it is a compiler and a batch file -- so ask for
|
||||
# the recompiler and uncapped cycles explicitly.
|
||||
# Do not tune [cpu] here. core=dynamic is roughly 5x faster but the
|
||||
# recompiler loses abort()'s exit status -- a program that traps
|
||||
# exits 0 instead, so the M3 bounds cases stop reporting the trap
|
||||
# they exist to prove. Verified against a compiler built under
|
||||
# core=normal, so it is the runtime and not the build.
|
||||
f"[log]\nlogfile={console}\n"
|
||||
f"[dosbox]\nlog console=quiet\n"
|
||||
f"[cpu]\ncore=dynamic\ncycles=max\n",
|
||||
f"[dosbox]\nlog console=quiet\n",
|
||||
encoding="ascii",
|
||||
)
|
||||
if cached.is_file():
|
||||
|
||||
Reference in New Issue
Block a user