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
|
spelling of its type arguments (SPEC 9). The table both deduplicates
|
||||||
requests and bounds how long a chain of new ones can get. */
|
requests and bounds how long a chain of new ones can get. */
|
||||||
#define FE_GENERIC_KEY_MAX 320
|
#define FE_GENERIC_KEY_MAX 320
|
||||||
#define FE_GENERIC_INSTANCE_MAX 512
|
#define FE_GENERIC_INSTANCE_MAX 4096
|
||||||
typedef struct FeInstance {
|
typedef struct FeInstance {
|
||||||
char key[FE_GENERIC_KEY_MAX];
|
char key[FE_GENERIC_KEY_MAX];
|
||||||
/* What lowering needs to build this instance's code: the declaration, the
|
/* 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;
|
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,
|
unsigned declare_var(Lower *L, const char *cname, const FeType *t,
|
||||||
const char *name)
|
const char *name)
|
||||||
{
|
{
|
||||||
unsigned local = fe_ir_local(L->m, L->fn, ir_type(t), ir_size(t),
|
unsigned local = fe_ir_local(L->m, L->fn, ir_type(t), ir_size(t),
|
||||||
ir_align(t), name);
|
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].cname = cname;
|
||||||
L->vars[L->var_count].local = local;
|
L->vars[L->var_count].local = local;
|
||||||
L->vars[L->var_count].by_address = 0;
|
L->vars[L->var_count].by_address = 0;
|
||||||
++L->var_count;
|
++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 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);
|
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);
|
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 i;
|
||||||
unsigned at;
|
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)
|
for (i = 0; i < L->error_count; ++i)
|
||||||
if (!strcmp(L->error_names[i], name)) return;
|
if (!strcmp(L->error_names[i], name)) return;
|
||||||
/* Kept sorted as it is built, so the numbering is the spelling order. */
|
/* 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.
|
* register and an aggregate does not fit in one.
|
||||||
* ------------------------------------------------------------------------- */
|
* ------------------------------------------------------------------------- */
|
||||||
|
|
||||||
#define LOWER_MAX_LOCALS 256
|
|
||||||
|
|
||||||
typedef struct LowerVar {
|
typedef struct LowerVar {
|
||||||
const char *cname;
|
const char *cname;
|
||||||
@@ -41,8 +40,12 @@ typedef struct Lower {
|
|||||||
FeIrBlock *b; /* the block being appended to */
|
FeIrBlock *b; /* the block being appended to */
|
||||||
FeType *ret_type;
|
FeType *ret_type;
|
||||||
unsigned ret_local; /* hidden result address, when returning mem */
|
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_count;
|
||||||
|
unsigned var_capacity;
|
||||||
/* Loop targets, for break and continue. */
|
/* Loop targets, for break and continue. */
|
||||||
unsigned break_target[32];
|
unsigned break_target[32];
|
||||||
unsigned continue_target[32];
|
unsigned continue_target[32];
|
||||||
@@ -60,14 +63,16 @@ typedef struct Lower {
|
|||||||
unsigned local; /* the owned value, otherwise */
|
unsigned local; /* the owned value, otherwise */
|
||||||
unsigned flag;
|
unsigned flag;
|
||||||
FeType *type;
|
FeType *type;
|
||||||
} owed[64];
|
} *owed;
|
||||||
unsigned owed_count;
|
unsigned owed_count;
|
||||||
|
unsigned owed_capacity;
|
||||||
/* Every `error.Name` used anywhere in the build, sorted, numbered from one.
|
/* 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
|
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
|
fixed by the spelling so that the same program always gets the same
|
||||||
codes however the build was ordered. */
|
codes however the build was ordered. */
|
||||||
const char *error_names[256];
|
const char **error_names;
|
||||||
unsigned error_count;
|
unsigned error_count;
|
||||||
|
unsigned error_capacity;
|
||||||
int failed;
|
int failed;
|
||||||
} Lower;
|
} Lower;
|
||||||
|
|
||||||
@@ -86,6 +91,11 @@ typedef struct Slot {
|
|||||||
#define SLICE_PTR_OFFSET 0L
|
#define SLICE_PTR_OFFSET 0L
|
||||||
#define SLICE_LEN_OFFSET 4L
|
#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. */
|
/* Every definition in lowering, so the split files can see each other. */
|
||||||
FeIrType tag_type_of(const FeType *t);
|
FeIrType tag_type_of(const FeType *t);
|
||||||
void lower_if_let(Lower *L, FeNode *n);
|
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. */
|
with one it is the element. */
|
||||||
counter = fe_ir_local(L->m, L->fn, FE_IR_I32, 4, 4, "index");
|
counter = fe_ir_local(L->m, L->fn, FE_IR_I32, 4, 4, "index");
|
||||||
if (n->aux_cname) {
|
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].cname = n->cname;
|
||||||
L->vars[L->var_count].local = counter;
|
L->vars[L->var_count].local = counter;
|
||||||
L->vars[L->var_count].by_address = 0;
|
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);
|
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].cname = n->aux_cname;
|
||||||
L->vars[L->var_count].local = item;
|
L->vars[L->var_count].local = item;
|
||||||
L->vars[L->var_count].by_address = 0;
|
L->vars[L->var_count].by_address = 0;
|
||||||
if (L->var_count < LOWER_MAX_LOCALS) ++L->var_count;
|
++L->var_count;
|
||||||
} else {
|
} else {
|
||||||
item = fe_ir_local(L->m, L->fn, FE_IR_PTR, 4, 4, n->text);
|
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].cname = n->cname;
|
||||||
L->vars[L->var_count].local = item;
|
L->vars[L->var_count].local = item;
|
||||||
L->vars[L->var_count].by_address = 0;
|
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);
|
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);
|
lower_stmt(L, n->a);
|
||||||
return;
|
return;
|
||||||
case FE_N_DEFER:
|
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].block = n->a;
|
||||||
L->owed[L->owed_count].local = 0;
|
L->owed[L->owed_count].local = 0;
|
||||||
L->owed[L->owed_count].flag = 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, FE_IR_PTR, 4, 4, p->text)
|
||||||
: fe_ir_local(L->m, f, ir_type(pt), ir_size(pt), ir_align(pt),
|
: fe_ir_local(L->m, f, ir_type(pt), ir_size(pt), ir_align(pt),
|
||||||
p->text);
|
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].cname = p->cname;
|
||||||
L->vars[L->var_count].local = local;
|
L->vars[L->var_count].local = local;
|
||||||
L->vars[L->var_count].by_address = by_address;
|
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. */
|
what makes a unit path map to a FAT/DOS 8.3 source path unambiguously. */
|
||||||
#define FE_UNIT_SEGMENT_MAX 8
|
#define FE_UNIT_SEGMENT_MAX 8
|
||||||
#define FE_UNIT_PATH_MAX 128
|
#define FE_UNIT_PATH_MAX 128
|
||||||
#define FE_BUILD_UNIT_MAX 64
|
#define FE_BUILD_UNIT_MAX 256
|
||||||
|
|
||||||
typedef struct FeUnit {
|
typedef struct FeUnit {
|
||||||
char name[FE_UNIT_PATH_MAX]; /* canonical dotted path */
|
char name[FE_UNIT_PATH_MAX]; /* canonical dotted path */
|
||||||
|
|||||||
+8
-2
@@ -1,5 +1,6 @@
|
|||||||
#include "x86.h"
|
#include "x86.h"
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------- *
|
/* ------------------------------------------------------------------------- *
|
||||||
* i386 code generation
|
* 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)
|
static void emit_func(const FeIrModule *m, const FeIrFunc *f, FILE *out)
|
||||||
{
|
{
|
||||||
Frame fr;
|
Frame fr;
|
||||||
long storage[512];
|
long *storage;
|
||||||
const FeIrBlock *b;
|
const FeIrBlock *b;
|
||||||
const FeIrValue *v;
|
const FeIrValue *v;
|
||||||
unsigned i;
|
unsigned i;
|
||||||
long arg = 8;
|
long arg = 8;
|
||||||
if (f->is_extern || !f->first) return;
|
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);
|
frame_layout(&fr, f, storage);
|
||||||
|
|
||||||
fprintf(out, "\npublic %s\n", f->name);
|
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);
|
fprintf(out, "%s endp\n", f->name);
|
||||||
|
free(storage);
|
||||||
(void)m;
|
(void)m;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user