compiler: 조용히 잘리던 상한을 없앤다
lowering 의 변수·정리 목록·오류 이름 세 배열은 넘치면 오류가 아니라 넘친 것을 버리고 틀린 코드를 냈다. 한계에 닿는 방식 중 최악이다. 이제 자란다. 코드 생성기는 지역이 512개를 넘으면 함수를 아예 방출하지 않고 지나갔다. 이제 지역 수만큼 자리를 잡는다. 유닛 64 -> 256, 제네릭 인스턴스 512 -> 4096. 둘 다 원래 보고는 했지만 장난감을 기준으로 고른 숫자였다. 209 -> 213 fixture, exec 25/25.
This commit is contained in:
+1
-1
@@ -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
|
||||
|
||||
+25
-3
@@ -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. */
|
||||
|
||||
+14
-4
@@ -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);
|
||||
|
||||
+16
-5
@@ -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;
|
||||
|
||||
+1
-1
@@ -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 */
|
||||
|
||||
+8
-2
@@ -1,5 +1,6 @@
|
||||
#include "x86.h"
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* 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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user