From 174e6c569d50eabd39fc0d7d7ea48f1532084eb5 Mon Sep 17 00:00:00 2001 From: Sebastian Jeong Date: Mon, 17 Aug 2026 06:08:33 +0900 Subject: [PATCH] =?UTF-8?q?lower:=20=EC=A0=84=EC=97=AD=EA=B3=BC=20?= =?UTF-8?q?=EB=AC=B8=EC=9E=90=EC=97=B4=20=EB=A6=AC=ED=84=B0=EB=9F=B4,=20?= =?UTF-8?q?=EA=B7=B8=EB=A6=AC=EA=B3=A0=20defer=20=EC=8B=A4=ED=96=89=20?= =?UTF-8?q?=EA=B2=80=EC=A6=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 전역은 정적 저장소다. 초기값이 컴파일타임 상수면 이미지에 박고 아니면 0이다. 문자열 리터럴은 바이트를 이미지에 두고 포인터와 길이를 값으로 만든다. 같은 글자는 같은 저장소를 쓴다 -- 읽기 전용이라 공유가 공짜다. defers 프로그램이 defer 순서를 실행으로 고정한다. 등록 역순이고, 이른 return 과 끝까지 간 경로 양쪽 다 돈다. --- fec/src/ir.c | 45 +++++++++++++++++++++++++++++++++++++++ fec/src/ir.h | 7 ++++++ fec/src/lower.c | 46 +++++++++++++++++++++++++++++++++++++++- fec/src/x86.c | 12 ++++++++++- fec/tests/exec/defers.fe | 22 +++++++++++++++++++ 5 files changed, 130 insertions(+), 2 deletions(-) create mode 100644 fec/tests/exec/defers.fe diff --git a/fec/src/ir.c b/fec/src/ir.c index 959a75f..0bb795c 100644 --- a/fec/src/ir.c +++ b/fec/src/ir.c @@ -1,5 +1,6 @@ #include "ir.h" #include +#include void fe_ir_module_init(FeIrModule *m) { @@ -84,6 +85,50 @@ FeIrBlock *fe_ir_block(FeIrModule *m, FeIrFunc *f) return b; } +FeIrGlobal *fe_ir_global(FeIrModule *m, const char *name, FeIrType type, + unsigned long size, unsigned align, + const unsigned char *init) +{ + FeIrGlobal *g; + for (g = m->globals; g; g = g->next) + if (!strcmp(g->name, name)) return g; + g = (FeIrGlobal *)ir_alloc(m, sizeof(FeIrGlobal)); + if (!g) return 0; + memset(g, 0, sizeof *g); + g->name = name; + g->type = type; + g->size = size; + g->align = align ? align : 1U; + g->init = init; + if (m->last_global) m->last_global->next = g; + else m->globals = g; + m->last_global = g; + return g; +} + +const char *fe_ir_string(FeIrModule *m, const char *bytes, unsigned long length) +{ + FeIrGlobal *g; + unsigned char *copy; + char *name; + unsigned serial = 0; + /* The same text twice is the same storage: string literals are read-only, + so sharing them is free. */ + for (g = m->globals; g; g = g->next) { + if (g->init && g->size == length && + !memcmp(g->init, bytes, (size_t)length)) return g->name; + ++serial; + } + copy = (unsigned char *)ir_alloc(m, length ? length : 1UL); + if (!copy) return 0; + if (length) memcpy(copy, bytes, (size_t)length); + name = (char *)ir_alloc(m, 32); + if (!name) return 0; + sprintf(name, "FE_STR_%u", serial); + g = fe_ir_global(m, name, FE_IR_MEM, length, 1, copy); + return g ? g->name : 0; +} + FeIrPlace fe_ir_at_local(unsigned index, long offset) { FeIrPlace p; diff --git a/fec/src/ir.h b/fec/src/ir.h index edee432..4db1269 100644 --- a/fec/src/ir.h +++ b/fec/src/ir.h @@ -151,6 +151,13 @@ unsigned fe_ir_local(FeIrModule *m, FeIrFunc *f, FeIrType type, unsigned long size, unsigned align, const char *name); unsigned fe_ir_temp(FeIrFunc *f); FeIrBlock *fe_ir_block(FeIrModule *m, FeIrFunc *f); +/* Static storage. `init` is `size` bytes to place there, or null for zero. */ +FeIrGlobal *fe_ir_global(FeIrModule *m, const char *name, FeIrType type, + unsigned long size, unsigned align, + const unsigned char *init); +/* A string literal's bytes, interned so the same text is stored once. */ +const char *fe_ir_string(FeIrModule *m, const char *bytes, + unsigned long length); /* Places */ FeIrPlace fe_ir_at_local(unsigned index, long offset); diff --git a/fec/src/lower.c b/fec/src/lower.c index aec95a5..e7ffc1f 100644 --- a/fec/src/lower.c +++ b/fec/src/lower.c @@ -62,6 +62,8 @@ static void lower_stmt(Lower *L, FeNode *n); static void store_into(Lower *L, FeIrPlace dst, Slot value, FeNode *n, unsigned long size); static void lower_for(Lower *L, FeNode *n); +static void lower_global(Lower *L, FeNode *n); +static long literal_value(FeNode *n); static Slot wrap_context(Lower *L, Slot v, FeNode *n); static Slot lower_try(Lower *L, FeNode *n); static Slot lower_lazy(Lower *L, FeNode *n, int is_catch); @@ -461,6 +463,26 @@ static Slot lower_expr_core(Lower *L, FeNode *n) it = ir_type(t); switch (n->kind) { case FE_N_LITERAL: + if (n->text && n->text[0] == '"') { + /* The bytes live in the image; the value is a pointer to them and + how many there are. */ + unsigned long len = strlen(n->text); + const char *label; + unsigned local; + unsigned p; + unsigned c; + if (len >= 2) len -= 2; + label = fe_ir_string(L->m, n->text + 1, len); + if (!label) { fail(L, "a string literal", n); return slot_void(); } + local = scratch(L, t, "text"); + p = fe_ir_addr(L->m, L->b, fe_ir_at_global(label, 0)); + fe_ir_store(L->m, L->b, fe_ir_at_local(local, SLICE_PTR_OFFSET), p, + FE_IR_PTR); + c = fe_ir_const(L->m, L->b, FE_IR_I32, (long)len); + fe_ir_store(L->m, L->b, fe_ir_at_local(local, SLICE_LEN_OFFSET), c, + FE_IR_I32); + return slot_place(fe_ir_at_local(local, 0), FE_IR_MEM, ir_size(t)); + } return slot_value(fe_ir_const(L->m, L->b, it == FE_IR_VOID ? FE_IR_I32 : it, literal_value(n)), @@ -1069,6 +1091,26 @@ static void lower_stmt(Lower *L, FeNode *n) /* ------------------------------------------------------------ functions --- */ +/* A global is static storage. SPEC 7.1: its initializer is evaluated at + compile time, so what reaches here is either a constant to place in the + image or nothing, and the storage starts as zeroes. */ +static void lower_global(Lower *L, FeNode *n) +{ + FeType *t = n->sem_type; + unsigned char *init = 0; + unsigned long size = ir_size(t); + if (!n->cname) return; + if (n->b && n->b->kind == FE_N_LITERAL && size && size <= 8) { + long v = literal_value(n->b); + unsigned long i; + init = (unsigned char *)fe_arena_alloc(&L->m->arena, (size_t)size); + if (init) + for (i = 0; i < size; ++i) + init[i] = (unsigned char)((v >> (i * 8)) & 0xFF); + } + fe_ir_global(L->m, n->cname, ir_type(t), size, ir_align(t), init); +} + static void lower_fn(Lower *L, FeNode *fn) { FeNode *p; @@ -1125,7 +1167,9 @@ int fe_lower_program(FeCheck *c, FeIrModule *out) c->types.unit_name = unit->name[0] ? unit->name : "unit"; if (!out->unit_file || !out->unit_file[0]) out->unit_file = unit->path; for (n = unit->ast.root ? unit->ast.root->children : 0; n; n = n->next) - if (n->kind == FE_N_FN && n->c) { + if (n->kind == FE_N_GLOBAL || n->kind == FE_N_CONST) + lower_global(&L, n); + else if (n->kind == FE_N_FN && n->c) { lower_fn(&L, n); /* The entry unit is the one the build was rooted at. */ if (u == 0 && n->text && !strcmp(n->text, "main")) diff --git a/fec/src/x86.c b/fec/src/x86.c index 05348a1..ca115de 100644 --- a/fec/src/x86.c +++ b/fec/src/x86.c @@ -368,8 +368,18 @@ void fe_x86_emit(const FeIrModule *m, FILE *out) emit_string(m->unit_file, out); } for (g = m->globals; g; g = g->next) { + unsigned long i; fprintf(out, "public %s\n%s label byte\n", g->name, g->name); - fprintf(out, " db %lu dup(0)\n", g->size ? g->size : 1UL); + if (!g->init) { + fprintf(out, " db %lu dup(0)\n", g->size ? g->size : 1UL); + continue; + } + for (i = 0; i < g->size; ++i) { + if (i % 16 == 0) fputs(" db ", out); + fprintf(out, "%u%s", g->init[i], + (i + 1 == g->size || (i % 16) == 15) ? "\n" : ","); + } + if (!g->size) fputs(" db 0\n", out); } fputs("_DATA ends\n", out); diff --git a/fec/tests/exec/defers.fe b/fec/tests/exec/defers.fe new file mode 100644 index 0000000..e9e03ab --- /dev/null +++ b/fec/tests/exec/defers.fe @@ -0,0 +1,22 @@ +// EXIT:21921 +// body(true): defer 2 then defer 1 -> 2, 21 +// body(false): note(9) first -> 219, 2192, 21921 +unit defers; + +var log: i32 = 0; + +fn note(v: i32) -> void { log = log * 10 + v; } + +fn body(early: bool) -> i32 { + defer { note(1); } + defer { note(2); } + if early { return 0; } + note(9); + return 0; +} + +fn main() -> i32 { + body(true); + body(false); + return log; +}