commit 5be49d19fb6c5901e243cfa3255c2a44458f1682
parent 2afdcd49101ac94d37c0ea9cf924a05610e54899
Author: Drew DeVault <sir@cmpwn.com>
Date: Tue, 12 Jan 2021 15:40:05 -0500
Implement @init/@fini
Diffstat:
8 files changed, 88 insertions(+), 7 deletions(-)
diff --git a/include/qbe.h b/include/qbe.h
@@ -238,6 +238,7 @@ struct qbe_data_item {
struct qbe_data {
size_t align;
+ char *section;
struct qbe_data_item items;
};
diff --git a/rt/rtmain.ha b/rt/rtmain.ha
@@ -1,7 +1,25 @@
@symbol("main") fn main() int;
fn exit(status: int) void;
+let @symbol("__init_array_start") init_start: [*]*fn() void;
+let @symbol("__init_array_end") init_end: [*]*fn() void;
+let @symbol("__fini_array_start") fini_start: [*]*fn() void;
+let @symbol("__fini_array_end") fini_end: [*]*fn() void;
+
export fn start_ha(iv: [*]uintptr) void = {
+ const ninit = (&init_end: uintptr - &init_start: uintptr): size
+ / size(*fn() void);
+ for (let i = 0z; i < ninit; i += 1z) {
+ init_start[i]();
+ };
+
main();
+
+ const nfini = (&fini_end: uintptr - &fini_start: uintptr): size
+ / size(*fn() void);
+ for (let i = 0z; i < nfini; i += 1z) {
+ fini_start[i]();
+ };
+
exit(0);
};
diff --git a/src/check.c b/src/check.c
@@ -1006,6 +1006,10 @@ check_global(struct context *ctx,
const struct ast_decl *adecl)
{
const struct ast_global_decl *agdecl = &adecl->global;
+ if (!agdecl->init) {
+ return NULL; // Forward declaration
+ }
+
const struct type *type = type_store_lookup_atype(
&ctx->store, agdecl->type);
diff --git a/src/emit.c b/src/emit.c
@@ -223,9 +223,12 @@ static void
emit_data(struct qbe_def *def, FILE *out)
{
assert(def->kind == Q_DATA);
- fprintf(out, "%sdata $%s = { ",
- def->exported ? "export " : "",
- def->name);
+ fprintf(out, "%sdata ", def->exported ? "export " : "");
+ if (def->data.section) {
+ // XXX: Presumes section name does not need to be escaped
+ fprintf(out, "section \"%s\" ", def->data.section);
+ }
+ fprintf(out, "$%s = { ", def->name);
struct qbe_data_item *item = &def->data.items;
while (item) {
diff --git a/src/gen.c b/src/gen.c
@@ -1200,7 +1200,7 @@ gen_function_decl(struct gen_context *ctx, const struct declaration *decl)
assert(decl->type == DECL_FUNC);
const struct function_decl *func = &decl->func;
const struct type *fntype = func->type;
- assert(func->flags == 0); // TODO
+ assert(!(func->flags & FN_TEST)); // TODO
struct qbe_def *qdef = xcalloc(1, sizeof(struct qbe_def));
qdef->kind = Q_FUNC;
@@ -1284,6 +1284,36 @@ gen_function_decl(struct gen_context *ctx, const struct declaration *decl)
qbe_append_def(ctx->out, qdef);
ctx->current = NULL;
+
+ if (func->flags & FN_INIT) {
+ struct qbe_def *idef = xcalloc(1, sizeof(struct qbe_def));
+ idef->kind = Q_DATA;
+ int l = snprintf(NULL, 0, ".init.%s", qdef->name);
+ idef->name = xcalloc(l + 1, 1);
+ snprintf(idef->name, l + 1, ".init.%s", qdef->name);
+ idef->data.align = 8;
+ idef->data.section = strdup(".init_array");
+ idef->data.items.type = QD_VALUE;
+ idef->data.items.value.kind = QV_GLOBAL;
+ idef->data.items.value.type = &qbe_long;
+ idef->data.items.value.name = strdup(qdef->name);
+ qbe_append_def(ctx->out, idef);
+ }
+
+ if (func->flags & FN_FINI) {
+ struct qbe_def *fdef = xcalloc(1, sizeof(struct qbe_def));
+ fdef->kind = Q_DATA;
+ int l = snprintf(NULL, 0, ".fini.%s", qdef->name);
+ fdef->name = xcalloc(l + 1, 1);
+ snprintf(fdef->name, l + 1, ".fini.%s", qdef->name);
+ fdef->data.align = 8;
+ fdef->data.section = strdup(".fini_array");
+ fdef->data.items.type = QD_VALUE;
+ fdef->data.items.value.kind = QV_GLOBAL;
+ fdef->data.items.value.type = &qbe_long;
+ fdef->data.items.value.name = strdup(qdef->name);
+ qbe_append_def(ctx->out, fdef);
+ }
}
static void
diff --git a/src/parse.c b/src/parse.c
@@ -1882,8 +1882,13 @@ parse_global_decl(struct lexer *lexer, enum lexical_token mode,
if (mode == T_CONST) {
i->type->flags |= TYPE_CONST;
}
- want(lexer, T_EQUAL, NULL);
- i->init = parse_simple_expression(lexer);
+
+ if (lex(lexer, &tok) == T_EQUAL) {
+ i->init = parse_simple_expression(lexer);
+ } else {
+ unlex(lexer, &tok);
+ }
+
switch (lex(lexer, &tok)) {
case T_COMMA:
lex(lexer, &tok);
diff --git a/tests/09-funcs.ha b/tests/09-funcs.ha
@@ -1,3 +1,5 @@
+@noreturn fn rt::exit(status: int) void;
+
fn addone(x: *int) void = {
*x += 1;
};
@@ -21,7 +23,18 @@ fn vaargs(expected: []int, values: int...) void = {
};
};
+let x: int = 42;
+
+@init fn init() void = {
+ x = 1337;
+};
+
+@fini fn fini() void = {
+ rt::exit(42); // Magic number
+};
+
export fn main() void = {
pointers();
vaargs([1, 2, 3], 1, 2, 3);
+ assert(x == 1337);
};
diff --git a/tests/run b/tests/run
@@ -13,7 +13,14 @@ do
ntests=$((ntests+1))
name="$(basename "$f")"
printf '%-40s ...' "$name"
- if $f
+ if [ "$name" = "09-funcs" ]
+ then
+ expected=42
+ else
+ expected=0
+ fi
+ $f
+ if [ $? -eq $expected ]
then
npass=$((npass+1))
printf 'PASS\n'