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:
2026-08-17 01:34:20 +09:00
parent 044c0f0e98
commit 0132ad4135
2 changed files with 56 additions and 7 deletions
+46
View File
@@ -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);