lower: 전역과 문자열 리터럴, 그리고 defer 실행 검증

전역은 정적 저장소다. 초기값이 컴파일타임 상수면 이미지에 박고 아니면 0이다.
문자열 리터럴은 바이트를 이미지에 두고 포인터와 길이를 값으로 만든다. 같은
글자는 같은 저장소를 쓴다 -- 읽기 전용이라 공유가 공짜다.

defers 프로그램이 defer 순서를 실행으로 고정한다. 등록 역순이고, 이른 return
과 끝까지 간 경로 양쪽 다 돈다.
This commit is contained in:
2026-08-17 06:08:33 +09:00
parent 096a5db411
commit 174e6c569d
5 changed files with 130 additions and 2 deletions
+45
View File
@@ -1,5 +1,6 @@
#include "ir.h"
#include <string.h>
#include <stdio.h>
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;
+7
View File
@@ -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);
+45 -1
View File
@@ -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"))
+11 -1
View File
@@ -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);
+22
View File
@@ -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;
}