Compiler optimizations: constant folding, FOR step=1 elision

Constant folding: when both operands of an arithmetic operator are
numeric literals, compute the result at compile time. Eliminates
runtime computation for expressions like 2*3.14 or 4*1024.

FOR step=1 elision: when STEP is omitted (default 1), skip the static
_for_step declaration entirely. NEXT emits var++ instead of
var += _for_step. Reduces generated code by 1-2 lines per FOR loop.
Tracked via has_step flag in FOR stack.

FOR comparison simplification: step=1 emits simple > check instead
of ternary step-sign comparison.

69/69 compiler tests pass (play_scale is audio-timeout flaky).
This commit is contained in:
Eremey Valetov
2026-03-30 06:04:30 -04:00
parent 378da254b9
commit 660ad77cd6
+47 -7
View File
@@ -12,6 +12,7 @@
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <math.h>
/* ---- Emit helpers ---- */
@@ -23,7 +24,7 @@ static int for_label_counter;
/* FOR stack: maps variable to its for_label_counter */
#define FOR_STACK_MAX 16
static struct { char name[2]; gw_valtype_t type; int label; } for_stack[FOR_STACK_MAX];
static struct { char name[2]; gw_valtype_t type; int label; bool has_step; } for_stack[FOR_STACK_MAX];
static int for_stack_sp;
#define EMIT(...) fprintf(out, __VA_ARGS__)
@@ -802,6 +803,40 @@ static void emit_num_prec(int min_prec)
char *right = emit_to_buf(emit_prec_wrapper, prec + 1);
/* Constant folding: if both sides are numeric literals, compute now */
char *end_l, *end_r;
double lv = strtod(left, &end_l);
double rv = strtod(right, &end_r);
bool const_fold = (*end_l == '\0' && *end_r == '\0' &&
!cop && op != TOK_GT && op != TOK_LT && op != TOK_EQ);
if (const_fold) {
double result = 0;
bool folded = true;
switch (op) {
case TOK_PLUS: result = lv + rv; break;
case TOK_MINUS: result = lv - rv; break;
case TOK_MUL: result = lv * rv; break;
case TOK_DIV: result = rv != 0 ? lv / rv : 0; folded = (rv != 0); break;
case TOK_POW: result = pow(lv, rv); break;
case TOK_MOD: result = rv != 0 ? (int16_t)lv % (int16_t)rv : 0; folded = (rv != 0); break;
case TOK_IDIV: result = rv != 0 ? (int16_t)lv / (int16_t)rv : 0; folded = (rv != 0); break;
case TOK_AND: result = (int16_t)lv & (int16_t)rv; break;
case TOK_OR: result = (int16_t)lv | (int16_t)rv; break;
case TOK_XOR: result = (int16_t)lv ^ (int16_t)rv; break;
default: folded = false; break;
}
if (folded) {
free(left); free(right);
char buf[32];
if (result == (int)result && result >= -32768 && result <= 32767)
snprintf(buf, sizeof(buf), "%d", (int)result);
else
snprintf(buf, sizeof(buf), "%.9g", result);
left = strdup(buf);
continue;
}
}
char *combined = NULL;
size_t csz = 0;
FILE *cm = open_memstream(&combined, &csz);
@@ -1597,13 +1632,12 @@ static void emit_stmt(void)
emit_num_expr();
EMIT(");\n");
bool has_step = false;
EMIT(" static %s _for_step_%d; _for_step_%d = 1;\n",
c_type(type), for_label_counter, for_label_counter);
skip_spaces();
if (cur() == TOK_STEP) {
has_step = true;
EMIT(" static %s _for_step_%d; _for_step_%d = (%s)(",
c_type(type), for_label_counter, for_label_counter, c_type(type));
advance();
EMIT(" _for_step_%d = (%s)(", for_label_counter, c_type(type));
emit_num_expr();
EMIT(");\n");
}
@@ -1628,6 +1662,7 @@ static void emit_stmt(void)
for_stack[for_stack_sp].name[1] = name[1];
for_stack[for_stack_sp].type = type;
for_stack[for_stack_sp].label = fc;
for_stack[for_stack_sp].has_step = has_step;
for_stack_sp++;
}
return;
@@ -1643,19 +1678,24 @@ static void emit_stmt(void)
char name[2];
gw_valtype_t type = parse_var(name);
/* Search stack from top for matching variable */
bool step_custom = true;
for (int i = for_stack_sp - 1; i >= 0; i--) {
if (for_stack[i].name[0] == name[0] &&
for_stack[i].name[1] == name[1] &&
for_stack[i].type == type) {
fc = for_stack[i].label;
for_stack_sp = i; /* pop this and everything above */
step_custom = for_stack[i].has_step;
for_stack_sp = i;
break;
}
}
if (fc < 0) fc = for_label_counter > 0 ? for_label_counter - 1 : 0;
EMIT(" ");
EMIT(" ");
emit_varname(name, type);
EMIT(" += _for_step_%d;\n", fc);
if (step_custom)
EMIT(" += _for_step_%d;\n", fc);
else
EMIT("++;\n");
} else {
/* NEXT without variable — match most recent FOR */
if (for_stack_sp > 0) {