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)
|
static void m7_emit_raw_expr(FeEmitter *e, FeNode *n)
|
||||||
{
|
{
|
||||||
FeNode *x;
|
FeNode *x;
|
||||||
@@ -1208,6 +1228,32 @@ static void emit_stmt(FeEmitter *e, FeNode *n)
|
|||||||
fputs(";\n",e->out);
|
fputs(";\n",e->out);
|
||||||
emit_cleanup_all(e);
|
emit_cleanup_all(e);
|
||||||
pad(e); fputs("return fe_return_value;\n",e->out);
|
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 {
|
} else {
|
||||||
if (n->a && e->current_ret && e->current_ret->kind!=FE_TYPE_VOID) {
|
if (n->a && e->current_ret && e->current_ret->kind!=FE_TYPE_VOID) {
|
||||||
pad(e); fputs("fe_return_value = ",e->out);
|
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"
|
build = "call BUILD.BAT" if trace_dos else "call BUILD.BAT > RESULTS\\BUILD.LOG"
|
||||||
if prebuilt:
|
if prebuilt:
|
||||||
# FEC.EXE was restored from cache; BUILD.BAT would delete and rebuild it.
|
# 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 = [
|
lines = [
|
||||||
"@echo off", "if not exist RESULTS md RESULTS", "if not exist OUT md OUT",
|
"@echo off", "if not exist RESULTS md RESULTS", "if not exist OUT md OUT",
|
||||||
"set WATCOM=W:", "set INCLUDE=W:\\H",
|
"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"
|
console = run_root / "CONSOLE.LOG"
|
||||||
config = run_root / "DOSBOX.CON"
|
config = run_root / "DOSBOX.CON"
|
||||||
config.write_text(
|
config.write_text(
|
||||||
# core=auto falls back to the interpreter in real mode, which is
|
# Do not tune [cpu] here. core=dynamic is roughly 5x faster but the
|
||||||
# where the 16-bit compiler build spends its time. Nothing here is
|
# recompiler loses abort()'s exit status -- a program that traps
|
||||||
# timing sensitive -- it is a compiler and a batch file -- so ask for
|
# exits 0 instead, so the M3 bounds cases stop reporting the trap
|
||||||
# the recompiler and uncapped cycles explicitly.
|
# 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"[log]\nlogfile={console}\n"
|
||||||
f"[dosbox]\nlog console=quiet\n"
|
f"[dosbox]\nlog console=quiet\n",
|
||||||
f"[cpu]\ncore=dynamic\ncycles=max\n",
|
|
||||||
encoding="ascii",
|
encoding="ascii",
|
||||||
)
|
)
|
||||||
if cached.is_file():
|
if cached.is_file():
|
||||||
|
|||||||
Reference in New Issue
Block a user