From c06f5c50c4f676c65f490f47586be92badab4383 Mon Sep 17 00:00:00 2001 From: Sebastian Jeong Date: Mon, 17 Aug 2026 07:18:47 +0900 Subject: [PATCH] =?UTF-8?q?compiler:=20=EC=A1=B0=EC=9A=A9=ED=9E=88=20?= =?UTF-8?q?=EC=9E=98=EB=A6=AC=EB=8D=98=20=EC=83=81=ED=95=9C=EC=9D=84=20?= =?UTF-8?q?=EC=97=86=EC=95=A4=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit lowering 의 변수·정리 목록·오류 이름 세 배열은 넘치면 오류가 아니라 넘친 것을 버리고 틀린 코드를 냈다. 한계에 닿는 방식 중 최악이다. 이제 자란다. 코드 생성기는 지역이 512개를 넘으면 함수를 아예 방출하지 않고 지나갔다. 이제 지역 수만큼 자리를 잡는다. 유닛 64 -> 256, 제네릭 인스턴스 512 -> 4096. 둘 다 원래 보고는 했지만 장난감을 기준으로 고른 숫자였다. 209 -> 213 fixture, exec 25/25. --- fec/src/check.h | 2 +- fec/src/lower.c | 28 +++++++++++++++++++++++++--- fec/src/lowerpri.h | 18 ++++++++++++++---- fec/src/lowerstm.c | 21 ++++++++++++++++----- fec/src/resolve.h | 2 +- fec/src/x86.c | 10 ++++++++-- 6 files changed, 65 insertions(+), 16 deletions(-) diff --git a/fec/src/check.h b/fec/src/check.h index 59bde60..7d55ba7 100644 --- a/fec/src/check.h +++ b/fec/src/check.h @@ -11,7 +11,7 @@ typedef struct FeScope FeScope; spelling of its type arguments (SPEC 9). The table both deduplicates requests and bounds how long a chain of new ones can get. */ #define FE_GENERIC_KEY_MAX 320 -#define FE_GENERIC_INSTANCE_MAX 512 +#define FE_GENERIC_INSTANCE_MAX 4096 typedef struct FeInstance { char key[FE_GENERIC_KEY_MAX]; /* What lowering needs to build this instance's code: the declaration, the diff --git a/fec/src/lower.c b/fec/src/lower.c index 045197e..80d13f6 100644 --- a/fec/src/lower.c +++ b/fec/src/lower.c @@ -118,18 +118,37 @@ int needs_release(const FeType *t) return t->has_drop != 0; } +int lower_reserve(Lower *L, void **items, unsigned *capacity, unsigned needed, + unsigned long item_size) +{ + unsigned want; + void *grown; + if (needed < *capacity) return 1; + want = *capacity ? *capacity * 2U : 16U; + while (want <= needed) want *= 2U; + grown = fe_arena_alloc(&L->m->arena, (size_t)(want * item_size)); + if (!grown) { fail(L, "a function this large", 0); return 0; } + if (*items) memcpy(grown, *items, (size_t)(*capacity * item_size)); + *items = grown; + *capacity = want; + return 1; +} + unsigned declare_var(Lower *L, const char *cname, const FeType *t, const char *name) { unsigned local = fe_ir_local(L->m, L->fn, ir_type(t), ir_size(t), ir_align(t), name); - if (L->var_count < LOWER_MAX_LOCALS) { + if (lower_reserve(L, (void **)&L->vars, &L->var_capacity, L->var_count, + (unsigned long)sizeof(LowerVar))) { L->vars[L->var_count].cname = cname; L->vars[L->var_count].local = local; L->vars[L->var_count].by_address = 0; ++L->var_count; } - if (needs_release(t) && L->owed_count < 64) { + if (needs_release(t) && + lower_reserve(L, (void **)&L->owed, &L->owed_capacity, L->owed_count, + (unsigned long)sizeof *L->owed)) { unsigned flag = fe_ir_local(L->m, L->fn, FE_IR_I8, 1, 1, "live"); unsigned zero = fe_ir_const(L->m, L->b, FE_IR_I8, 0); fe_ir_store(L->m, L->b, fe_ir_at_local(flag, 0), zero, FE_IR_I8); @@ -230,7 +249,10 @@ void note_error_name(Lower *L, const char *name) { unsigned i; unsigned at; - if (!name || L->error_count >= 256) return; + if (!name) return; + if (!lower_reserve(L, (void **)&L->error_names, &L->error_capacity, + L->error_count, (unsigned long)sizeof(const char *))) + return; for (i = 0; i < L->error_count; ++i) if (!strcmp(L->error_names[i], name)) return; /* Kept sorted as it is built, so the numbering is the spelling order. */ diff --git a/fec/src/lowerpri.h b/fec/src/lowerpri.h index 5cb9c33..b46c556 100644 --- a/fec/src/lowerpri.h +++ b/fec/src/lowerpri.h @@ -24,7 +24,6 @@ * register and an aggregate does not fit in one. * ------------------------------------------------------------------------- */ -#define LOWER_MAX_LOCALS 256 typedef struct LowerVar { const char *cname; @@ -41,8 +40,12 @@ typedef struct Lower { FeIrBlock *b; /* the block being appended to */ FeType *ret_type; unsigned ret_local; /* hidden result address, when returning mem */ - LowerVar vars[LOWER_MAX_LOCALS]; + /* These three grow. A fixed size here does not report a program that is + too big -- it quietly drops what does not fit and generates wrong code, + which is the worst way for a limit to be reached. */ + LowerVar *vars; unsigned var_count; + unsigned var_capacity; /* Loop targets, for break and continue. */ unsigned break_target[32]; unsigned continue_target[32]; @@ -60,14 +63,16 @@ typedef struct Lower { unsigned local; /* the owned value, otherwise */ unsigned flag; FeType *type; - } owed[64]; + } *owed; unsigned owed_count; + unsigned owed_capacity; /* Every `error.Name` used anywhere in the build, sorted, numbered from one. SPEC 4.6: the names are collected rather than declared, and the order is fixed by the spelling so that the same program always gets the same codes however the build was ordered. */ - const char *error_names[256]; + const char **error_names; unsigned error_count; + unsigned error_capacity; int failed; } Lower; @@ -86,6 +91,11 @@ typedef struct Slot { #define SLICE_PTR_OFFSET 0L #define SLICE_LEN_OFFSET 4L +/* Grow one of the checker's own arrays. Returns zero when there is no more + memory, which the caller reports rather than ignores. */ +int lower_reserve(Lower *L, void **items, unsigned *capacity, unsigned needed, + unsigned long item_size); + /* Every definition in lowering, so the split files can see each other. */ FeIrType tag_type_of(const FeType *t); void lower_if_let(Lower *L, FeNode *n); diff --git a/fec/src/lowerstm.c b/fec/src/lowerstm.c index 527d04e..7591523 100644 --- a/fec/src/lowerstm.c +++ b/fec/src/lowerstm.c @@ -198,21 +198,30 @@ void lower_for(Lower *L, FeNode *n) with one it is the element. */ counter = fe_ir_local(L->m, L->fn, FE_IR_I32, 4, 4, "index"); if (n->aux_cname) { + (void)lower_reserve(L, (void **)&L->vars, &L->var_capacity, + L->var_count, + (unsigned long)sizeof(LowerVar)); L->vars[L->var_count].cname = n->cname; L->vars[L->var_count].local = counter; L->vars[L->var_count].by_address = 0; - if (L->var_count < LOWER_MAX_LOCALS) ++L->var_count; + ++L->var_count; item = fe_ir_local(L->m, L->fn, FE_IR_PTR, 4, 4, n->aux_text); + (void)lower_reserve(L, (void **)&L->vars, &L->var_capacity, + L->var_count, + (unsigned long)sizeof(LowerVar)); L->vars[L->var_count].cname = n->aux_cname; L->vars[L->var_count].local = item; L->vars[L->var_count].by_address = 0; - if (L->var_count < LOWER_MAX_LOCALS) ++L->var_count; + ++L->var_count; } else { item = fe_ir_local(L->m, L->fn, FE_IR_PTR, 4, 4, n->text); + (void)lower_reserve(L, (void **)&L->vars, &L->var_capacity, + L->var_count, + (unsigned long)sizeof(LowerVar)); L->vars[L->var_count].cname = n->cname; L->vars[L->var_count].local = item; L->vars[L->var_count].by_address = 0; - if (L->var_count < LOWER_MAX_LOCALS) ++L->var_count; + ++L->var_count; } { unsigned zero = fe_ir_const(L->m, L->b, FE_IR_I32, 0); @@ -463,7 +472,8 @@ void lower_stmt(Lower *L, FeNode *n) lower_stmt(L, n->a); return; case FE_N_DEFER: - if (L->owed_count < 64) { + if (lower_reserve(L, (void **)&L->owed, &L->owed_capacity, + L->owed_count, (unsigned long)sizeof *L->owed)) { L->owed[L->owed_count].block = n->a; L->owed[L->owed_count].local = 0; L->owed[L->owed_count].flag = 0; @@ -543,7 +553,8 @@ void lower_fn_as(Lower *L, FeNode *fn, const char *name) ? fe_ir_local(L->m, f, FE_IR_PTR, 4, 4, p->text) : fe_ir_local(L->m, f, ir_type(pt), ir_size(pt), ir_align(pt), p->text); - if (L->var_count < LOWER_MAX_LOCALS) { + if (lower_reserve(L, (void **)&L->vars, &L->var_capacity, + L->var_count, (unsigned long)sizeof(LowerVar))) { L->vars[L->var_count].cname = p->cname; L->vars[L->var_count].local = local; L->vars[L->var_count].by_address = by_address; diff --git a/fec/src/resolve.h b/fec/src/resolve.h index e859093..69448c1 100644 --- a/fec/src/resolve.h +++ b/fec/src/resolve.h @@ -15,7 +15,7 @@ what makes a unit path map to a FAT/DOS 8.3 source path unambiguously. */ #define FE_UNIT_SEGMENT_MAX 8 #define FE_UNIT_PATH_MAX 128 -#define FE_BUILD_UNIT_MAX 64 +#define FE_BUILD_UNIT_MAX 256 typedef struct FeUnit { char name[FE_UNIT_PATH_MAX]; /* canonical dotted path */ diff --git a/fec/src/x86.c b/fec/src/x86.c index 065e507..9ddd64b 100644 --- a/fec/src/x86.c +++ b/fec/src/x86.c @@ -1,5 +1,6 @@ #include "x86.h" #include +#include /* ------------------------------------------------------------------------- * * i386 code generation @@ -269,13 +270,17 @@ static void emit_value(const Frame *fr, const FeIrValue *v, FILE *out) static void emit_func(const FeIrModule *m, const FeIrFunc *f, FILE *out) { Frame fr; - long storage[512]; + long *storage; const FeIrBlock *b; const FeIrValue *v; unsigned i; long arg = 8; if (f->is_extern || !f->first) return; - if (f->local_count > 512) return; + /* One offset per local, however many there are. A fixed array here would + silently stop emitting a function that had too many. */ + storage = (long *)malloc((size_t)(f->local_count ? f->local_count : 1) * + sizeof(long)); + if (!storage) return; frame_layout(&fr, f, storage); fprintf(out, "\npublic %s\n", f->name); @@ -322,6 +327,7 @@ static void emit_func(const FeIrModule *m, const FeIrFunc *f, FILE *out) } } fprintf(out, "%s endp\n", f->name); + free(storage); (void)m; }