std: 표준 라이브러리가 컴파일러 옆에서 해석되고 호출된다
std 는 예약된 이름이고 프로그램이 아니라 컴파일러와 함께 있으므로 자기 루트를 갖는다 (--std=). 본문 없는 선언은 링커가 찾을 것 -- 런타임이나 C 라이브러리 -- 이므로 IR 에 extern 으로 나간다. 런타임에 write/alloc/free/exit 를 넣었다. 이것이 표준 라이브러리가 스스로 말할 수 없는 전부이고 나머지는 Ferro 로 쓴다. @trap @unreachable @size_of @align_of @line 을 내린다. 링크 이름에서 점과 괄호를 걸렀다. 유닛 경로에는 점이 있고 제네릭 인스턴스에는 괄호가 있는데 어셈블러가 받지 않는다.
This commit is contained in:
@@ -154,11 +154,15 @@ static FeType *node_type(FeCheck *c, FeNode *n)
|
||||
return t;
|
||||
}
|
||||
|
||||
/* A link-visible name. A unit path has dots in it and a generic instance has
|
||||
brackets and commas, none of which an assembler will accept, so everything
|
||||
outside the portable identifier set becomes an underscore. */
|
||||
static char *unit_cname(FeCheck *c, const char *name)
|
||||
{
|
||||
char *u;
|
||||
char *p;
|
||||
unsigned long n;
|
||||
unsigned long i;
|
||||
u = c->ast->root && c->ast->root->text ? c->ast->root->text : "unit";
|
||||
n = (unsigned long)strlen("fe_") + (unsigned long)strlen(u) +
|
||||
(unsigned long)strlen(name ? name : "name") + 2UL;
|
||||
@@ -168,6 +172,12 @@ static char *unit_cname(FeCheck *c, const char *name)
|
||||
strcat(p, u);
|
||||
strcat(p, "_");
|
||||
strcat(p, name ? name : "name");
|
||||
for (i = 0; p[i]; ++i) {
|
||||
char ch = p[i];
|
||||
if (!((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
|
||||
(ch >= '0' && ch <= '9') || ch == '_'))
|
||||
p[i] = '_';
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
+4
-2
@@ -18,7 +18,7 @@ static char *read_file(const char *name, unsigned long *size)
|
||||
|
||||
static void usage(void)
|
||||
{
|
||||
puts("usage: fec [--dump-tokens|--dump-ast|--check|--dump-ir|--emit-asm] file.fe [-o out.asm] [--no-checks]");
|
||||
puts("usage: fec [--dump-tokens|--dump-ast|--check|--dump-ir|--emit-asm] file.fe [-o out.asm] [--std=dir] [--no-checks]");
|
||||
}
|
||||
|
||||
static void dump_tokens(const char *src, unsigned long n, const char *file,
|
||||
@@ -42,6 +42,7 @@ int main(int argc, char **argv)
|
||||
int i,dump=0,dump_tok=0,check_only=0,no_checks=0,dump_ir=0,emit_asm=0;
|
||||
const char *file=0;
|
||||
const char *out_path=0;
|
||||
const char *std_root=0;
|
||||
unsigned long n;
|
||||
char *src;
|
||||
FeDiags d;
|
||||
@@ -57,6 +58,7 @@ int main(int argc, char **argv)
|
||||
else if(strcmp(argv[i],"--dump-ir")==0) dump_ir=1;
|
||||
else if(strcmp(argv[i],"--emit-asm")==0) emit_asm=1;
|
||||
else if(strcmp(argv[i],"-o")==0 && i+1<argc) out_path=argv[++i];
|
||||
else if(strncmp(argv[i],"--std=",6)==0) std_root=argv[i]+6;
|
||||
else if(strcmp(argv[i],"--no-checks")==0) no_checks=1;
|
||||
else if(strncmp(argv[i],"--target=",9)==0 || strncmp(argv[i],"--model=",8)==0 || strcmp(argv[i],"--strip-error-names")==0) { }
|
||||
else if(argv[i][0]!='-') file=argv[i];
|
||||
@@ -94,7 +96,7 @@ int main(int argc, char **argv)
|
||||
every unit's AST. */
|
||||
{
|
||||
FeBuild build;
|
||||
int ok=fe_build_load(&build,file,&d);
|
||||
int ok=fe_build_load(&build,file,&d,std_root);
|
||||
if(ok){
|
||||
fe_check_init(&check,&build,&d,pointer_bits,no_checks);
|
||||
if(!fe_check_program(&check)) ok=0;
|
||||
|
||||
@@ -406,6 +406,42 @@ static Slot lower_logical(Lower *L, FeNode *n, int is_and)
|
||||
return slot_place(fe_ir_at_local(result, 0), FE_IR_I8, 1);
|
||||
}
|
||||
|
||||
/* The builtins that are not calls at all: they are a constant, or they stop
|
||||
the program. `@print` is expanded separately because it becomes several
|
||||
calls rather than one thing. */
|
||||
static int lower_builtin(Lower *L, FeNode *n, Slot *out)
|
||||
{
|
||||
const char *name = n->text;
|
||||
if (!name || name[0] != '@') return 0;
|
||||
if (!strcmp(name, "@trap")) {
|
||||
fe_ir_trap(L->b, FE_TRAP_EXPLICIT, n->loc.line);
|
||||
L->b = new_block(L);
|
||||
*out = slot_void();
|
||||
return 1;
|
||||
}
|
||||
if (!strcmp(name, "@unreachable")) {
|
||||
fe_ir_trap(L->b, FE_TRAP_UNREACHABLE, n->loc.line);
|
||||
L->b = new_block(L);
|
||||
*out = slot_void();
|
||||
return 1;
|
||||
}
|
||||
if (!strcmp(name, "@size_of") || !strcmp(name, "@align_of")) {
|
||||
FeNode *arg = n->children;
|
||||
FeType *t = arg && arg->kind == FE_N_IDENT
|
||||
? fe_type_intern(&L->c->types, arg->text) : 0;
|
||||
long v = !strcmp(name, "@size_of") ? (long)ir_size(t)
|
||||
: (long)ir_align(t);
|
||||
*out = slot_value(fe_ir_const(L->m, L->b, FE_IR_I32, v), FE_IR_I32);
|
||||
return 1;
|
||||
}
|
||||
if (!strcmp(name, "@line")) {
|
||||
*out = slot_value(fe_ir_const(L->m, L->b, FE_IR_I32,
|
||||
(long)n->loc.line), FE_IR_I32);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Slot lower_call(Lower *L, FeNode *n)
|
||||
{
|
||||
unsigned args[16];
|
||||
@@ -417,6 +453,10 @@ static Slot lower_call(Lower *L, FeNode *n)
|
||||
const char *callee = n->a && n->a->cname ? n->a->cname :
|
||||
(n->sem_decl && n->sem_decl->cname ?
|
||||
n->sem_decl->cname : 0);
|
||||
{
|
||||
Slot built;
|
||||
if (lower_builtin(L, n, &built)) return built;
|
||||
}
|
||||
if (!callee) { fail(L, "a call with no target", n); return slot_void(); }
|
||||
/* An aggregate result is written through a hidden first argument. */
|
||||
if (rt == FE_IR_MEM) {
|
||||
@@ -1169,6 +1209,15 @@ int fe_lower_program(FeCheck *c, FeIrModule *out)
|
||||
for (n = unit->ast.root ? unit->ast.root->children : 0; n; n = n->next)
|
||||
if (n->kind == FE_N_GLOBAL || n->kind == FE_N_CONST)
|
||||
lower_global(&L, n);
|
||||
else if (n->kind == FE_N_FN && !n->c) {
|
||||
/* A declaration with no body is something the linker will
|
||||
find: the runtime, or a C library. */
|
||||
FeType *ret = n->b ? fe_type_from_ast(&c->types, n->b) : 0;
|
||||
FeIrFunc *f;
|
||||
if (!n->cname) continue;
|
||||
f = fe_ir_func(out, n->cname, ir_type(ret), ir_size(ret));
|
||||
if (f) f->is_extern = 1;
|
||||
}
|
||||
else if (n->kind == FE_N_FN && n->c) {
|
||||
lower_fn(&L, n);
|
||||
/* The entry unit is the one the build was rooted at. */
|
||||
|
||||
+17
-2
@@ -190,7 +190,13 @@ static int load_unit(FeBuild *b, const char *name, FeLoc from, int have_from,
|
||||
unit = &b->units[b->count];
|
||||
memset(unit, 0, sizeof *unit);
|
||||
strcpy(unit->name, name);
|
||||
unit_source_path(unit->path, sizeof unit->path, b->root, name);
|
||||
/* `std` is reserved (SPEC 10) and lives with the compiler, not with the
|
||||
program, so it is looked up under its own root. */
|
||||
unit_source_path(unit->path, sizeof unit->path,
|
||||
(name[0]=='s' && name[1]=='t' && name[2]=='d' &&
|
||||
(name[3]=='.' || name[3]==0) && b->std_root[0])
|
||||
? b->std_root : b->root,
|
||||
name);
|
||||
unit->source = read_source(unit->path, &size);
|
||||
if (!unit->source) {
|
||||
if (have_from)
|
||||
@@ -245,7 +251,8 @@ static int check_bindings(FeBuild *b, FeUnit *unit)
|
||||
return ok;
|
||||
}
|
||||
|
||||
int fe_build_load(FeBuild *build, const char *entry, FeDiags *diags)
|
||||
int fe_build_load(FeBuild *build, const char *entry, FeDiags *diags,
|
||||
const char *std_root)
|
||||
{
|
||||
const char *stack[FE_BUILD_UNIT_MAX];
|
||||
FeAst probe;
|
||||
@@ -259,6 +266,14 @@ int fe_build_load(FeBuild *build, const char *entry, FeDiags *diags)
|
||||
|
||||
memset(build, 0, sizeof *build);
|
||||
build->diags = diags;
|
||||
if (std_root) {
|
||||
unsigned long k = 0;
|
||||
while (std_root[k] && k + 1 < sizeof build->std_root) {
|
||||
build->std_root[k] = std_root[k];
|
||||
++k;
|
||||
}
|
||||
build->std_root[k] = 0;
|
||||
}
|
||||
|
||||
/* The entry file fixes the import root, so it has to be parsed far enough
|
||||
to know its own name before anything else can be found. */
|
||||
|
||||
+5
-1
@@ -31,6 +31,9 @@ typedef struct FeBuild {
|
||||
FeUnit units[FE_BUILD_UNIT_MAX];
|
||||
unsigned count;
|
||||
char root[260]; /* import root: where unit paths start */
|
||||
/* Where `std.*` is looked for. The standard library is not under the
|
||||
program's root -- it ships with the compiler. */
|
||||
char std_root[260];
|
||||
FeDiags *diags;
|
||||
} FeBuild;
|
||||
|
||||
@@ -46,7 +49,8 @@ int fe_resolve_unit_identity(FeAst *ast, FeDiags *diags, const char *source_path
|
||||
`<root>/a/b.fe` fixes `<root>`, so a sibling `import c.d;` is looked for at
|
||||
`<root>/c/d.fe`. Reports missing imports, import cycles, and binding
|
||||
conflicts. Returns non-zero when the whole graph loaded cleanly. */
|
||||
int fe_build_load(FeBuild *build, const char *entry, FeDiags *diags);
|
||||
int fe_build_load(FeBuild *build, const char *entry, FeDiags *diags,
|
||||
const char *std_root);
|
||||
void fe_build_destroy(FeBuild *build);
|
||||
|
||||
/* The unit a binding refers to inside `unit`, or null.
|
||||
|
||||
Reference in New Issue
Block a user