mirror of
https://github.com/odin-lang/Odin.git
synced 2026-06-06 10:44:06 +00:00
Global variable dependency initialization ordering
Fuck graph theory
This commit is contained in:
@@ -102,7 +102,7 @@ void check_init_variables(Checker *c, Entity **lhs, isize lhs_count, Array<AstNo
|
||||
// an extra allocation
|
||||
Array<Operand> operands = {};
|
||||
array_init(&operands, c->tmp_allocator, 2*lhs_count);
|
||||
check_unpack_arguments(c, lhs_count, &operands, inits, true);
|
||||
check_unpack_arguments(c, lhs, lhs_count, &operands, inits, true);
|
||||
|
||||
isize rhs_count = operands.count;
|
||||
for_array(i, operands) {
|
||||
@@ -113,7 +113,13 @@ void check_init_variables(Checker *c, Entity **lhs, isize lhs_count, Array<AstNo
|
||||
|
||||
isize max = gb_min(lhs_count, rhs_count);
|
||||
for (isize i = 0; i < max; i++) {
|
||||
check_init_variable(c, lhs[i], &operands[i], context_name);
|
||||
Entity *e = lhs[i];
|
||||
DeclInfo *d = decl_info_of_entity(&c->info, e);
|
||||
Operand *o = &operands[i];
|
||||
check_init_variable(c, e, o, context_name);
|
||||
if (d != nullptr) {
|
||||
d->init_expr = o->expr;
|
||||
}
|
||||
}
|
||||
if (rhs_count > 0 && lhs_count != rhs_count) {
|
||||
error(lhs[0]->token, "Assignment count mismatch `%td` = `%td`", lhs_count, rhs_count);
|
||||
@@ -550,7 +556,7 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) {
|
||||
}
|
||||
}
|
||||
|
||||
void check_var_decl(Checker *c, Entity *e, Entity **entities, isize entity_count, AstNode *type_expr, AstNode *init_expr) {
|
||||
void check_var_decl(Checker *c, Entity *e, Entity **entities, isize entity_count, AstNode *type_expr, Array<AstNode *> init_expr_list) {
|
||||
GB_ASSERT(e->type == nullptr);
|
||||
GB_ASSERT(e->kind == Entity_Variable);
|
||||
|
||||
@@ -581,7 +587,7 @@ void check_var_decl(Checker *c, Entity *e, Entity **entities, isize entity_count
|
||||
|
||||
|
||||
if (e->Variable.is_foreign) {
|
||||
if (init_expr != nullptr) {
|
||||
if (init_expr_list.count > 0) {
|
||||
error(e->token, "A foreign variable declaration cannot have a default value");
|
||||
}
|
||||
init_entity_foreign_library(c, e);
|
||||
@@ -606,31 +612,20 @@ void check_var_decl(Checker *c, Entity *e, Entity **entities, isize entity_count
|
||||
}
|
||||
}
|
||||
|
||||
if (init_expr == nullptr) {
|
||||
if (init_expr_list.count == 0) {
|
||||
if (type_expr == nullptr) {
|
||||
e->type = t_invalid;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (entities == nullptr || entity_count == 1) {
|
||||
GB_ASSERT(entities == nullptr || entities[0] == e);
|
||||
Operand operand = {};
|
||||
check_expr(c, &operand, init_expr);
|
||||
check_init_variable(c, e, &operand, context_name);
|
||||
}
|
||||
|
||||
if (type_expr != nullptr) {
|
||||
for (isize i = 0; i < entity_count; i++) {
|
||||
entities[i]->type = e->type;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Array<AstNode *> inits;
|
||||
array_init(&inits, c->allocator, 1);
|
||||
array_add(&inits, init_expr);
|
||||
check_init_variables(c, entities, entity_count, inits, context_name);
|
||||
check_init_variables(c, entities, entity_count, init_expr_list, context_name);
|
||||
}
|
||||
|
||||
void check_entity_decl(Checker *c, Entity *e, DeclInfo *d, Type *named_type) {
|
||||
@@ -657,7 +652,7 @@ void check_entity_decl(Checker *c, Entity *e, DeclInfo *d, Type *named_type) {
|
||||
|
||||
switch (e->kind) {
|
||||
case Entity_Variable:
|
||||
check_var_decl(c, e, d->entities, d->entity_count, d->type_expr, d->init_expr);
|
||||
check_var_decl(c, e, d->entities, d->entity_count, d->type_expr, d->init_expr_list);
|
||||
break;
|
||||
case Entity_Constant:
|
||||
check_const_decl(c, e, d->type_expr, d->init_expr, named_type);
|
||||
@@ -757,9 +752,8 @@ void check_proc_body(Checker *c, Token token, DeclInfo *decl, Type *type, AstNod
|
||||
if (decl->parent != nullptr) {
|
||||
// NOTE(bill): Add the dependencies from the procedure literal (lambda)
|
||||
for_array(i, decl->deps.entries) {
|
||||
HashKey key = decl->deps.entries[i].key;
|
||||
Entity *e = cast(Entity *)key.ptr;
|
||||
map_set(&decl->parent->deps, key, true);
|
||||
Entity *e = decl->deps.entries[i].ptr;
|
||||
ptr_set_add(&decl->parent->deps, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5968,19 +5968,44 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id
|
||||
}
|
||||
|
||||
|
||||
isize add_dependencies_from_unpacking(Checker *c, Entity **lhs, isize lhs_count, isize tuple_index, isize tuple_count) {
|
||||
if (lhs != nullptr) {
|
||||
for (isize j = 0; tuple_index < lhs_count && j < tuple_count; j++) {
|
||||
Entity *e = lhs[tuple_index + j];
|
||||
DeclInfo *decl = decl_info_of_entity(&c->info, e);
|
||||
if (decl != nullptr) {
|
||||
c->context.decl = decl; // will be reset by the `defer` any way
|
||||
for_array(k, decl->deps.entries) {
|
||||
Entity *dep = decl->deps.entries[k].ptr;
|
||||
add_declaration_dependency(c, dep); // TODO(bill): Should this be here?
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return tuple_count;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool check_unpack_arguments(Checker *c, isize lhs_count, Array<Operand> *operands, Array<AstNode *> rhs, bool allow_ok) {
|
||||
void check_unpack_arguments(Checker *c, Entity **lhs, isize lhs_count, Array<Operand> *operands, Array<AstNode *> rhs, bool allow_ok, bool *optional_ok_ = nullptr) {
|
||||
bool optional_ok = false;
|
||||
isize tuple_index = 0;
|
||||
for_array(i, rhs) {
|
||||
CheckerContext prev_context = c->context;
|
||||
defer (c->context = prev_context);
|
||||
|
||||
Operand o = {};
|
||||
|
||||
if (lhs != nullptr && tuple_index < lhs_count) {
|
||||
// NOTE(bill): override DeclInfo for dependency control
|
||||
DeclInfo *decl = decl_info_of_entity(&c->info, lhs[tuple_index]);
|
||||
if (decl) c->context.decl = decl;
|
||||
}
|
||||
|
||||
check_expr_base(c, &o, rhs[i], nullptr);
|
||||
if (o.mode == Addressing_NoValue) {
|
||||
error_operand_no_value(&o);
|
||||
o.mode = Addressing_Invalid;
|
||||
}
|
||||
// check_multi_expr(c, &o, rhs[i]);
|
||||
|
||||
|
||||
if (o.type == nullptr || o.type->kind != Type_Tuple) {
|
||||
@@ -5998,8 +6023,10 @@ bool check_unpack_arguments(Checker *c, isize lhs_count, Array<Operand> *operand
|
||||
array_add(operands, ok);
|
||||
|
||||
optional_ok = true;
|
||||
tuple_index += add_dependencies_from_unpacking(c, lhs, lhs_count, tuple_index, 2);
|
||||
} else {
|
||||
array_add(operands, o);
|
||||
tuple_index += 1;
|
||||
}
|
||||
} else {
|
||||
TypeTuple *tuple = &o.type->Tuple;
|
||||
@@ -6007,10 +6034,13 @@ bool check_unpack_arguments(Checker *c, isize lhs_count, Array<Operand> *operand
|
||||
o.type = tuple->variables[j]->type;
|
||||
array_add(operands, o);
|
||||
}
|
||||
|
||||
isize count = tuple->variables.count;
|
||||
tuple_index += add_dependencies_from_unpacking(c, lhs, lhs_count, tuple_index, count);
|
||||
}
|
||||
}
|
||||
|
||||
return optional_ok;
|
||||
if (optional_ok_) *optional_ok_ = optional_ok;
|
||||
}
|
||||
|
||||
|
||||
@@ -6404,7 +6434,7 @@ CallArgumentData check_call_arguments(Checker *c, Operand *operand, Type *proc_t
|
||||
|
||||
} else {
|
||||
array_init(&operands, heap_allocator(), 2*ce->args.count);
|
||||
check_unpack_arguments(c, -1, &operands, ce->args, false);
|
||||
check_unpack_arguments(c, nullptr, -1, &operands, ce->args, false);
|
||||
}
|
||||
|
||||
if (operand->mode == Addressing_Overload) {
|
||||
@@ -6644,7 +6674,7 @@ CallArgumentError check_polymorphic_struct_type(Checker *c, Operand *operand, As
|
||||
|
||||
} else {
|
||||
array_init(&operands, heap_allocator(), 2*ce->args.count);
|
||||
check_unpack_arguments(c, -1, &operands, ce->args, false);
|
||||
check_unpack_arguments(c, nullptr, -1, &operands, ce->args, false);
|
||||
}
|
||||
|
||||
CallArgumentError err = CallArgumentError_None;
|
||||
|
||||
@@ -692,7 +692,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
// an extra allocation
|
||||
Array<Operand> operands = {};
|
||||
array_init(&operands, c->tmp_allocator, 2 * lhs_count);
|
||||
check_unpack_arguments(c, lhs_count, &operands, as->rhs, true);
|
||||
check_unpack_arguments(c, nullptr, lhs_count, &operands, as->rhs, true);
|
||||
|
||||
isize rhs_count = operands.count;
|
||||
for_array(i, operands) {
|
||||
@@ -849,7 +849,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
}
|
||||
} else {
|
||||
array_init(&operands, heap_allocator(), 2*rs->results.count);
|
||||
check_unpack_arguments(c, -1, &operands, rs->results, false);
|
||||
check_unpack_arguments(c, nullptr, -1, &operands, rs->results, false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
370
src/checker.cpp
370
src/checker.cpp
@@ -181,10 +181,11 @@ struct DeclInfo {
|
||||
|
||||
AstNode * type_expr;
|
||||
AstNode * init_expr;
|
||||
Array<AstNode *> init_expr_list;
|
||||
AstNode * proc_lit; // AstNode_ProcLit
|
||||
Type * gen_proc_type; // Precalculated
|
||||
|
||||
Map<bool> deps; // Key: Entity *
|
||||
PtrSet<Entity *> deps;
|
||||
Array<BlockLabel> labels;
|
||||
};
|
||||
|
||||
@@ -273,6 +274,69 @@ struct CheckerFileNode {
|
||||
i32 score; // Higher the score, the better
|
||||
};
|
||||
|
||||
struct GraphNode;
|
||||
typedef PtrSet<GraphNode *> GraphNodeSet;
|
||||
|
||||
void graph_node_set_destroy(GraphNodeSet *s) {
|
||||
if (s->hashes.data != nullptr) {
|
||||
ptr_set_destroy(s);
|
||||
}
|
||||
}
|
||||
|
||||
void graph_node_set_add(GraphNodeSet *s, GraphNode *n) {
|
||||
if (s->hashes.data == nullptr) {
|
||||
ptr_set_init(s, heap_allocator());
|
||||
}
|
||||
ptr_set_add(s, n);
|
||||
}
|
||||
|
||||
bool graph_node_set_exists(GraphNodeSet *s, GraphNode *n) {
|
||||
return ptr_set_exists(s, n);
|
||||
}
|
||||
|
||||
void graph_node_set_remove(GraphNodeSet *s, GraphNode *n) {
|
||||
ptr_set_remove(s, n);
|
||||
}
|
||||
|
||||
|
||||
struct GraphNode {
|
||||
Entity * entity; // Procedure, Variable, Constant
|
||||
GraphNodeSet pred;
|
||||
GraphNodeSet succ;
|
||||
isize index; // Index in array/queue
|
||||
isize dep_count;
|
||||
};
|
||||
|
||||
|
||||
void graph_node_destroy(GraphNode *n, gbAllocator a) {
|
||||
graph_node_set_destroy(&n->pred);
|
||||
graph_node_set_destroy(&n->succ);
|
||||
gb_free(a, n);
|
||||
}
|
||||
|
||||
|
||||
int graph_node_cmp(GraphNode **data, isize i, isize j) {
|
||||
GraphNode *x = data[i];
|
||||
GraphNode *y = data[j];
|
||||
isize a = x->entity->order_in_src;
|
||||
isize b = y->entity->order_in_src;
|
||||
if (x->dep_count < y->dep_count) return -1;
|
||||
if (x->dep_count > y->dep_count) return +1;
|
||||
return a < b ? -1 : b > a;
|
||||
}
|
||||
|
||||
void graph_node_swap(GraphNode **data, isize i, isize j) {
|
||||
GraphNode *x = data[i];
|
||||
GraphNode *y = data[j];
|
||||
data[i] = y;
|
||||
data[j] = x;
|
||||
x->index = j;
|
||||
y->index = i;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
struct CheckerContext {
|
||||
Scope * file_scope;
|
||||
Scope * scope;
|
||||
@@ -303,8 +367,15 @@ struct CheckerInfo {
|
||||
Map<DeclInfo *> entities; // Key: Entity *
|
||||
Map<Entity *> foreigns; // Key: String
|
||||
Map<AstFile *> files; // Key: String (full path)
|
||||
Array<DeclInfo *> variable_init_order;
|
||||
|
||||
|
||||
Map<isize> type_info_map; // Key: Type *
|
||||
isize type_info_count;
|
||||
|
||||
Entity * entry_point;
|
||||
PtrSet<Entity *> minimum_dependency_map;
|
||||
|
||||
};
|
||||
|
||||
struct Checker {
|
||||
@@ -372,8 +443,8 @@ void add_implicit_entity(Checker *c, AstNode *node, Entity *e);
|
||||
void init_declaration_info(DeclInfo *d, Scope *scope, DeclInfo *parent) {
|
||||
d->parent = parent;
|
||||
d->scope = scope;
|
||||
map_init(&d->deps, heap_allocator());
|
||||
array_init(&d->labels, heap_allocator());
|
||||
ptr_set_init(&d->deps, heap_allocator());
|
||||
array_init (&d->labels, heap_allocator());
|
||||
}
|
||||
|
||||
DeclInfo *make_declaration_info(gbAllocator a, Scope *scope, DeclInfo *parent) {
|
||||
@@ -383,7 +454,8 @@ DeclInfo *make_declaration_info(gbAllocator a, Scope *scope, DeclInfo *parent) {
|
||||
}
|
||||
|
||||
void destroy_declaration_info(DeclInfo *d) {
|
||||
map_destroy(&d->deps);
|
||||
ptr_set_destroy(&d->deps);
|
||||
array_free(&d->labels);
|
||||
}
|
||||
|
||||
bool decl_info_has_init(DeclInfo *d) {
|
||||
@@ -626,7 +698,7 @@ void check_scope_usage(Checker *c, Scope *scope) {
|
||||
|
||||
|
||||
void add_dependency(DeclInfo *d, Entity *e) {
|
||||
map_set(&d->deps, hash_entity(e), cast(bool)true);
|
||||
ptr_set_add(&d->deps, e);
|
||||
}
|
||||
|
||||
void add_declaration_dependency(Checker *c, Entity *e) {
|
||||
@@ -634,10 +706,8 @@ void add_declaration_dependency(Checker *c, Entity *e) {
|
||||
return;
|
||||
}
|
||||
if (c->context.decl != nullptr) {
|
||||
DeclInfo **found = map_get(&c->info.entities, hash_entity(e));
|
||||
if (found) {
|
||||
add_dependency(c->context.decl, e);
|
||||
}
|
||||
DeclInfo *decl = decl_info_of_entity(&c->info, e);
|
||||
if (decl) add_dependency(c->context.decl, e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -744,8 +814,9 @@ void init_checker_info(CheckerInfo *i) {
|
||||
map_init(&i->gen_types, a);
|
||||
map_init(&i->type_info_map, a);
|
||||
map_init(&i->files, a);
|
||||
i->type_info_count = 0;
|
||||
array_init(&i->variable_init_order, a);
|
||||
|
||||
i->type_info_count = 0;
|
||||
}
|
||||
|
||||
void destroy_checker_info(CheckerInfo *i) {
|
||||
@@ -761,6 +832,7 @@ void destroy_checker_info(CheckerInfo *i) {
|
||||
map_destroy(&i->gen_types);
|
||||
map_destroy(&i->type_info_map);
|
||||
map_destroy(&i->files);
|
||||
array_free(&i->variable_init_order);
|
||||
}
|
||||
|
||||
|
||||
@@ -1049,6 +1121,7 @@ void add_entity_and_decl_info(Checker *c, AstNode *identifier, Entity *e, DeclIn
|
||||
if (e->scope != nullptr) add_entity(c, e->scope, identifier, e);
|
||||
add_entity_definition(&c->info, identifier, e);
|
||||
map_set(&c->info.entities, hash_entity(e), d);
|
||||
e->order_in_src = c->info.entities.entries.count;
|
||||
}
|
||||
|
||||
|
||||
@@ -1253,7 +1326,7 @@ void add_curr_ast_file(Checker *c, AstFile *file) {
|
||||
}
|
||||
|
||||
|
||||
void add_dependency_to_map(Map<Entity *> *map, CheckerInfo *info, Entity *entity) {
|
||||
void add_dependency_to_map(PtrSet<Entity *> *map, CheckerInfo *info, Entity *entity) {
|
||||
if (entity == nullptr) {
|
||||
return;
|
||||
}
|
||||
@@ -1265,26 +1338,23 @@ void add_dependency_to_map(Map<Entity *> *map, CheckerInfo *info, Entity *entity
|
||||
}
|
||||
}
|
||||
|
||||
if (map_get(map, hash_entity(entity)) != nullptr) {
|
||||
if (ptr_set_exists(map, entity)) {
|
||||
return;
|
||||
}
|
||||
map_set(map, hash_entity(entity), entity);
|
||||
|
||||
|
||||
ptr_set_add(map, entity);
|
||||
DeclInfo *decl = decl_info_of_entity(info, entity);
|
||||
if (decl == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
for_array(i, decl->deps.entries) {
|
||||
Entity *e = cast(Entity *)decl->deps.entries[i].key.ptr;
|
||||
add_dependency_to_map(map, info, e);
|
||||
if (decl != nullptr) {
|
||||
for_array(i, decl->deps.entries) {
|
||||
Entity *e = decl->deps.entries[i].ptr;
|
||||
add_dependency_to_map(map, info, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Map<Entity *> generate_minimum_dependency_map(CheckerInfo *info, Entity *start) {
|
||||
Map<Entity *> map = {}; // Key: Entity *
|
||||
map_init(&map, heap_allocator());
|
||||
PtrSet<Entity *> generate_minimum_dependency_map(CheckerInfo *info, Entity *start) {
|
||||
PtrSet<Entity *> map = {}; // Key: Entity *
|
||||
ptr_set_init(&map, heap_allocator());
|
||||
|
||||
for_array(i, info->definitions.entries) {
|
||||
Entity *e = info->definitions.entries[i].value;
|
||||
@@ -1309,8 +1379,101 @@ Map<Entity *> generate_minimum_dependency_map(CheckerInfo *info, Entity *start)
|
||||
return map;
|
||||
}
|
||||
|
||||
bool is_entity_in_dependency_map(Map<Entity *> *map, Entity *e) {
|
||||
return map_get(map, hash_entity(e)) != nullptr;
|
||||
bool is_entity_a_dependency(Entity *e) {
|
||||
if (e == nullptr) return false;
|
||||
switch (e->kind) {
|
||||
case Entity_Procedure:
|
||||
case Entity_Variable:
|
||||
case Entity_Constant:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Array<GraphNode *> generate_dependency_graph(CheckerInfo *info) {
|
||||
gbAllocator a = heap_allocator();
|
||||
|
||||
Map<GraphNode *> M = {}; // Key: Entity *
|
||||
map_init(&M, a);
|
||||
defer (map_destroy(&M));
|
||||
for_array(i, info->entities.entries) {
|
||||
auto *entry = &info->entities.entries[i];
|
||||
Entity * e = cast(Entity *)entry->key.ptr;
|
||||
DeclInfo *d = entry->value;
|
||||
if (is_entity_a_dependency(e)) {
|
||||
GraphNode *n = gb_alloc_item(a, GraphNode);
|
||||
n->entity = e;
|
||||
map_set(&M, hash_pointer(e), n);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Calculate edges for graph M
|
||||
for_array(i, M.entries) {
|
||||
Entity * e = cast(Entity *)M.entries[i].key.ptr;
|
||||
GraphNode *n = M.entries[i].value;
|
||||
|
||||
DeclInfo *decl = decl_info_of_entity(info, e);
|
||||
if (decl != nullptr) {
|
||||
for_array(j, decl->deps.entries) {
|
||||
auto entry = decl->deps.entries[j];
|
||||
Entity *dep = entry.ptr;
|
||||
if (dep && is_entity_a_dependency(dep)) {
|
||||
GraphNode **m_ = map_get(&M, hash_pointer(dep));
|
||||
if (m_ != nullptr) {
|
||||
GraphNode *m = *m_;
|
||||
graph_node_set_add(&n->succ, m);
|
||||
graph_node_set_add(&m->pred, n);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Array<GraphNode *> G = {};
|
||||
array_init(&G, a);
|
||||
|
||||
for_array(i, M.entries) {
|
||||
auto *entry = &M.entries[i];
|
||||
Entity * e = cast(Entity *)entry->key.ptr;
|
||||
GraphNode *n = entry->value;
|
||||
|
||||
if (e->kind == Entity_Procedure) {
|
||||
// Connect each pred `p` of `n` with each succ `s` and frop
|
||||
// the procedure node
|
||||
for_array(j, n->pred.entries) {
|
||||
GraphNode *p = cast(GraphNode *)n->pred.entries[j].ptr;
|
||||
|
||||
// Ignore self-cycles
|
||||
if (p != n) {
|
||||
// Each succ `s` of `n` becomes a succ of `p`, and
|
||||
// each pred `p` of `n` becomes a pred of `s`
|
||||
for_array(k, n->succ.entries) {
|
||||
GraphNode *s = n->succ.entries[k].ptr;
|
||||
// Ignore self-cycles
|
||||
if (s != n) {
|
||||
graph_node_set_add(&p->succ, s);
|
||||
graph_node_set_add(&s->pred, p);
|
||||
// Remove edge to `n`
|
||||
graph_node_set_remove(&s->pred, n);
|
||||
}
|
||||
}
|
||||
// Remove edge to `n`
|
||||
graph_node_set_remove(&p->succ, n);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
array_add(&G, n);
|
||||
}
|
||||
}
|
||||
|
||||
for_array(i, G) {
|
||||
GraphNode *n = G[i];
|
||||
n->index = i;
|
||||
n->dep_count = n->succ.entries.count;
|
||||
}
|
||||
|
||||
return G;
|
||||
}
|
||||
|
||||
|
||||
@@ -1668,6 +1831,7 @@ void check_collect_entities(Checker *c, Array<AstNode *> nodes, bool is_file_sco
|
||||
di->entities = entities;
|
||||
di->type_expr = vd->type;
|
||||
di->init_expr = vd->values[0];
|
||||
di->init_expr_list = vd->values;
|
||||
|
||||
|
||||
if (vd->flags & VarDeclFlag_thread_local) {
|
||||
@@ -1676,6 +1840,7 @@ void check_collect_entities(Checker *c, Array<AstNode *> nodes, bool is_file_sco
|
||||
}
|
||||
|
||||
|
||||
|
||||
for_array(i, vd->names) {
|
||||
AstNode *name = vd->names[i];
|
||||
AstNode *value = nullptr;
|
||||
@@ -1705,7 +1870,7 @@ void check_collect_entities(Checker *c, Array<AstNode *> nodes, bool is_file_sco
|
||||
entities[entity_count++] = e;
|
||||
|
||||
DeclInfo *d = di;
|
||||
if (d == nullptr) {
|
||||
if (d == nullptr || i > 0) {
|
||||
AstNode *init_expr = value;
|
||||
d = make_declaration_info(heap_allocator(), e->scope, c->context.decl);
|
||||
d->type_expr = vd->type;
|
||||
@@ -1719,6 +1884,10 @@ void check_collect_entities(Checker *c, Array<AstNode *> nodes, bool is_file_sco
|
||||
di->entity_count = entity_count;
|
||||
}
|
||||
|
||||
if (vd->values.count > 0 && entity_count != vd->values.count) {
|
||||
error(decl, "Variable declarations in the global scope can only declare 1 variable at a time");
|
||||
}
|
||||
|
||||
check_arity_match(c, vd);
|
||||
} else {
|
||||
for_array(i, vd->names) {
|
||||
@@ -2271,6 +2440,134 @@ void check_import_entities(Checker *c, Map<Scope *> *file_scopes) {
|
||||
}
|
||||
}
|
||||
|
||||
Array<Entity *> find_entity_path(Map<DeclInfo *> *map, Entity *start, Entity *end, Map<Entity *> *visited = nullptr) {
|
||||
Map<Entity *> visited_ = {};
|
||||
bool made_visited = false;
|
||||
if (visited == nullptr) {
|
||||
made_visited = true;
|
||||
map_init(&visited_, heap_allocator());
|
||||
visited = &visited_;
|
||||
}
|
||||
defer (if (made_visited) {
|
||||
map_destroy(&visited_);
|
||||
});
|
||||
|
||||
Array<Entity *> empty_path = {};
|
||||
|
||||
HashKey key = hash_pointer(start);
|
||||
|
||||
if (map_get(visited, key) != nullptr) {
|
||||
return empty_path;
|
||||
}
|
||||
map_set(visited, key, start);
|
||||
|
||||
DeclInfo **found = map_get(map, key);
|
||||
if (found) {
|
||||
DeclInfo *decl = *found;
|
||||
for_array(i, decl->deps.entries) {
|
||||
Entity *dep = decl->deps.entries[i].ptr;
|
||||
if (dep == end) {
|
||||
Array<Entity *> path = {};
|
||||
array_init(&path, heap_allocator());
|
||||
array_add(&path, dep);
|
||||
return path;
|
||||
}
|
||||
Array<Entity *> next_path = find_entity_path(map, dep, end, visited);
|
||||
if (next_path.count > 0) {
|
||||
array_add(&next_path, dep);
|
||||
return next_path;
|
||||
}
|
||||
}
|
||||
}
|
||||
return empty_path;
|
||||
}
|
||||
|
||||
|
||||
void calculate_variable_init_order(Checker *c) {
|
||||
CheckerInfo *info = &c->info;
|
||||
auto *m = &info->entities;
|
||||
|
||||
Array<GraphNode *> dep_graph = generate_dependency_graph(info);
|
||||
defer ({
|
||||
for_array(i, dep_graph) {
|
||||
graph_node_destroy(dep_graph[i], heap_allocator());
|
||||
}
|
||||
array_free(&dep_graph);
|
||||
});
|
||||
|
||||
// NOTE(bill): Priority queue
|
||||
auto pq = priority_queue_create(dep_graph, graph_node_cmp, graph_node_swap);
|
||||
|
||||
PtrSet<DeclInfo *> emitted = {};
|
||||
ptr_set_init(&emitted, heap_allocator());
|
||||
defer (ptr_set_destroy(&emitted));
|
||||
|
||||
while (pq.queue.count > 0) {
|
||||
GraphNode *n = priority_queue_pop(&pq);
|
||||
Entity *e = n->entity;
|
||||
|
||||
if (n->dep_count > 0) {
|
||||
// TODO(bill): print out the cyclic initialization order
|
||||
auto path = find_entity_path(m, e, e);
|
||||
defer (array_free(&path));
|
||||
|
||||
if (path.count > 0) {
|
||||
Entity *e = path[0];
|
||||
error(e->token, "Cyclic initialization of `%.*s`", LIT(e->token.string));
|
||||
for (isize i = path.count-1; i >= 0; i--) {
|
||||
error(e->token, "\t`%.*s` refers to", LIT(e->token.string));
|
||||
e = path[i];
|
||||
}
|
||||
error(e->token, "\t`%.*s`", LIT(e->token.string));
|
||||
}
|
||||
}
|
||||
|
||||
for_array(i, n->pred.entries) {
|
||||
GraphNode *p = n->pred.entries[i].ptr;
|
||||
p->dep_count -= 1;
|
||||
priority_queue_fix(&pq, p->index);
|
||||
}
|
||||
|
||||
if (e == nullptr || e->kind != Entity_Variable) {
|
||||
continue;
|
||||
}
|
||||
DeclInfo *d = decl_info_of_entity(info, e);
|
||||
|
||||
// if (!decl_info_has_init(d)) {
|
||||
// continue;
|
||||
// }
|
||||
|
||||
if (ptr_set_exists(&emitted, d)) {
|
||||
continue;
|
||||
}
|
||||
ptr_set_add(&emitted, d);
|
||||
|
||||
// TODO(bill): add to init order
|
||||
|
||||
if (d->entities == nullptr) {
|
||||
d->entities = gb_alloc_array(c->allocator, Entity *, 1);
|
||||
d->entities[0] = e;
|
||||
d->entity_count = 1;
|
||||
}
|
||||
array_add(&info->variable_init_order, d);
|
||||
}
|
||||
|
||||
if (false) {
|
||||
gb_printf("Variable Initialization Order:\n");
|
||||
for_array(i, info->variable_init_order) {
|
||||
DeclInfo *d = info->variable_init_order[i];
|
||||
for (isize j = 0; j < d->entity_count; j++) {
|
||||
Entity *e = d->entities[j];
|
||||
if (j == 0) gb_printf("\t");
|
||||
if (j > 0) gb_printf(", ");
|
||||
gb_printf("`%.*s` %td", LIT(e->token.string), e->order_in_src);
|
||||
}
|
||||
gb_printf("\n");
|
||||
}
|
||||
gb_printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void check_parsed_files(Checker *c) {
|
||||
Map<Scope *> file_scopes; // Key: String (fullpath)
|
||||
@@ -2355,6 +2652,25 @@ void check_parsed_files(Checker *c) {
|
||||
check_proc_body(c, pi->token, pi->decl, pi->type, pi->body);
|
||||
}
|
||||
|
||||
{
|
||||
for_array(i, c->info.entities.entries) {
|
||||
auto *entry = &c->info.entities.entries[i];
|
||||
Entity *e = cast(Entity *)entry->key.ptr;
|
||||
String name = e->token.string;
|
||||
if (e->kind == Entity_Procedure && !e->scope->is_global) {
|
||||
if (e->scope->is_init && name == "main") {
|
||||
c->info.entry_point = e;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
c->info.minimum_dependency_map = generate_minimum_dependency_map(&c->info, c->info.entry_point);
|
||||
}
|
||||
|
||||
// Calculate initialization order of global variables
|
||||
calculate_variable_init_order(c);
|
||||
|
||||
|
||||
// Add untyped expression values
|
||||
for_array(i, c->info.untyped.entries) {
|
||||
auto *entry = &c->info.untyped.entries[i];
|
||||
|
||||
@@ -107,6 +107,8 @@ u128 fnv128a(void const *data, isize len) {
|
||||
}
|
||||
|
||||
#include "map.cpp"
|
||||
#include "ptr_set.cpp"
|
||||
#include "priority_queue.cpp"
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -78,6 +78,8 @@ struct Entity {
|
||||
Entity * using_parent;
|
||||
AstNode * using_expr;
|
||||
|
||||
isize order_in_src;
|
||||
|
||||
union {
|
||||
struct {
|
||||
ExactValue value;
|
||||
|
||||
181
src/ir.cpp
181
src/ir.cpp
@@ -19,7 +19,7 @@ struct irModule {
|
||||
String layout;
|
||||
// String triple;
|
||||
|
||||
Map<Entity *> min_dep_map; // Key: Entity *
|
||||
PtrSet<Entity *> min_dep_map;
|
||||
Map<irValue *> values; // Key: Entity *
|
||||
Map<irValue *> members; // Key: String
|
||||
Map<String> entity_names; // Key: Entity * of the typename
|
||||
@@ -5783,7 +5783,7 @@ irValue *ir_build_cond(irProcedure *proc, AstNode *cond, irBlock *true_block, ir
|
||||
void ir_build_poly_proc(irProcedure *proc, AstNodeProcLit *pd, Entity *e) {
|
||||
GB_ASSERT(pd->body != nullptr);
|
||||
|
||||
if (is_entity_in_dependency_map(&proc->module->min_dep_map, e) == false) {
|
||||
if (ptr_set_exists(&proc->module->min_dep_map, e) == false) {
|
||||
// NOTE(bill): Nothing depends upon it so doesn't need to be built
|
||||
return;
|
||||
}
|
||||
@@ -5845,7 +5845,7 @@ void ir_build_constant_value_decl(irProcedure *proc, AstNodeValueDecl *vd) {
|
||||
}
|
||||
}
|
||||
|
||||
if (!polymorphic_struct && map_get(&proc->module->min_dep_map, hash_pointer(e)) == nullptr) {
|
||||
if (!polymorphic_struct && !ptr_set_exists(&proc->module->min_dep_map, e)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -5874,7 +5874,7 @@ void ir_build_constant_value_decl(irProcedure *proc, AstNodeValueDecl *vd) {
|
||||
auto procs = *found;
|
||||
for_array(i, procs) {
|
||||
Entity *e = procs[i];
|
||||
if (map_get(&proc->module->min_dep_map, hash_pointer(e)) == nullptr) {
|
||||
if (!ptr_set_exists(&proc->module->min_dep_map, e)) {
|
||||
continue;
|
||||
}
|
||||
DeclInfo *d = decl_info_of_entity(info, e);
|
||||
@@ -7545,7 +7545,7 @@ void ir_gen_tree(irGen *s) {
|
||||
}
|
||||
|
||||
isize global_variable_max_count = 0;
|
||||
Entity *entry_point = nullptr;
|
||||
Entity *entry_point = info->entry_point;
|
||||
bool has_dll_main = false;
|
||||
bool has_win_main = false;
|
||||
|
||||
@@ -7557,7 +7557,8 @@ void ir_gen_tree(irGen *s) {
|
||||
global_variable_max_count++;
|
||||
} else if (e->kind == Entity_Procedure && !e->scope->is_global) {
|
||||
if (e->scope->is_init && name == "main") {
|
||||
entry_point = e;
|
||||
GB_ASSERT(e == entry_point);
|
||||
// entry_point = e;
|
||||
}
|
||||
if ((e->Procedure.tags & ProcTag_export) != 0 ||
|
||||
(e->Procedure.link_name.len > 0) ||
|
||||
@@ -7582,7 +7583,63 @@ void ir_gen_tree(irGen *s) {
|
||||
array_init(&global_variables, m->tmp_allocator, global_variable_max_count);
|
||||
|
||||
m->entry_point_entity = entry_point;
|
||||
m->min_dep_map = generate_minimum_dependency_map(info, entry_point);
|
||||
m->min_dep_map = info->minimum_dependency_map;
|
||||
|
||||
for_array(i, info->variable_init_order) {
|
||||
DeclInfo *d = info->variable_init_order[i];
|
||||
|
||||
for (isize j = 0; j < d->entity_count; j++) {
|
||||
Entity *e = d->entities[j];
|
||||
|
||||
if (!e->scope->is_file) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ptr_set_exists(&m->min_dep_map, e)) {
|
||||
continue;
|
||||
}
|
||||
DeclInfo *decl = decl_info_of_entity(info, e);
|
||||
if (decl == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String name = e->token.string;
|
||||
String original_name = name;
|
||||
if (!e->scope->is_global) {
|
||||
name = ir_mangle_name(s, e->token.pos.file, e);
|
||||
}
|
||||
map_set(&m->entity_names, hash_entity(e), name);
|
||||
|
||||
irValue *g = ir_value_global(a, e, nullptr);
|
||||
g->Global.name = name;
|
||||
g->Global.is_thread_local = e->Variable.is_thread_local;
|
||||
|
||||
|
||||
irGlobalVariable var = {};
|
||||
var.var = g;
|
||||
var.decl = decl;
|
||||
|
||||
if (decl->init_expr != nullptr) {
|
||||
if (is_type_any(e->type)) {
|
||||
} else {
|
||||
TypeAndValue tav = type_and_value_of_expr(info, decl->init_expr);
|
||||
if (tav.mode != Addressing_Invalid) {
|
||||
if (tav.value.kind != ExactValue_Invalid) {
|
||||
ExactValue v = tav.value;
|
||||
g->Global.value = ir_add_module_constant(m, tav.type, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if (g->Global.value == nullptr) {
|
||||
array_add(&global_variables, var);
|
||||
// }
|
||||
|
||||
ir_module_add_value(m, e, g);
|
||||
map_set(&m->members, hash_string(name), g);
|
||||
}
|
||||
}
|
||||
|
||||
for_array(i, info->entities.entries) {
|
||||
auto *entry = &info->entities.entries[i];
|
||||
@@ -7595,6 +7652,10 @@ void ir_gen_tree(irGen *s) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (e->kind == Entity_Variable) {
|
||||
// NOTE(bill): Handled above as it requires a specific load order
|
||||
continue;
|
||||
}
|
||||
|
||||
bool polymorphic_struct = false;
|
||||
if (e->type != nullptr && e->kind == Entity_TypeName) {
|
||||
@@ -7604,7 +7665,7 @@ void ir_gen_tree(irGen *s) {
|
||||
}
|
||||
}
|
||||
|
||||
if (!polymorphic_struct && map_get(&m->min_dep_map, hash_entity(e)) == nullptr) {
|
||||
if (!polymorphic_struct && !ptr_set_exists(&m->min_dep_map, e)) {
|
||||
// NOTE(bill): Nothing depends upon it so doesn't need to be built
|
||||
continue;
|
||||
}
|
||||
@@ -7622,7 +7683,6 @@ void ir_gen_tree(irGen *s) {
|
||||
} else if (check_is_entity_overloaded(e)) {
|
||||
name = ir_mangle_name(s, e->token.pos.file, e);
|
||||
}
|
||||
|
||||
map_set(&m->entity_names, hash_entity(e), name);
|
||||
|
||||
switch (e->kind) {
|
||||
@@ -7631,39 +7691,6 @@ void ir_gen_tree(irGen *s) {
|
||||
ir_gen_global_type_name(m, e, name);
|
||||
break;
|
||||
|
||||
case Entity_Variable: {
|
||||
irValue *g = ir_value_global(a, e, nullptr);
|
||||
g->Global.name = name;
|
||||
g->Global.is_thread_local = e->Variable.is_thread_local;
|
||||
|
||||
irGlobalVariable var = {};
|
||||
var.var = g;
|
||||
var.decl = decl;
|
||||
|
||||
if (decl->init_expr != nullptr) {
|
||||
if (is_type_any(e->type)) {
|
||||
|
||||
} else {
|
||||
TypeAndValue tav = type_and_value_of_expr(info, decl->init_expr);
|
||||
if (tav.mode != Addressing_Invalid) {
|
||||
if (tav.value.kind != ExactValue_Invalid) {
|
||||
ExactValue v = tav.value;
|
||||
// if (v.kind != ExactValue_String) {
|
||||
g->Global.value = ir_add_module_constant(m, tav.type, v);
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (g->Global.value == nullptr) {
|
||||
array_add(&global_variables, var);
|
||||
}
|
||||
|
||||
ir_module_add_value(m, e, g);
|
||||
map_set(&m->members, hash_string(name), g);
|
||||
} break;
|
||||
|
||||
case Entity_Procedure: {
|
||||
ast_node(pl, ProcLit, decl->proc_lit);
|
||||
String original_name = name;
|
||||
@@ -7863,52 +7890,7 @@ void ir_gen_tree(irGen *s) {
|
||||
ir_emit_global_call(proc, "__init_context", args, 1);
|
||||
}
|
||||
|
||||
// TODO(bill): Should do a dependency graph do check which order to initialize them in?
|
||||
for_array(i, global_variables) {
|
||||
irGlobalVariable *var = &global_variables[i];
|
||||
if (var->decl->init_expr != nullptr) {
|
||||
var->init = ir_build_expr(proc, var->decl->init_expr);
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE(bill): Initialize constants first
|
||||
for_array(i, global_variables) {
|
||||
irGlobalVariable *var = &global_variables[i];
|
||||
if (var->init != nullptr && var->init->kind == irValue_Constant) {
|
||||
Type *t = type_deref(ir_type(var->var));
|
||||
if (is_type_any(t)) {
|
||||
// NOTE(bill): Edge case for `any` type
|
||||
Type *var_type = default_type(ir_type(var->init));
|
||||
irValue *g = ir_add_global_generated(proc->module, var_type, var->init);
|
||||
irValue *data = ir_emit_struct_ep(proc, var->var, 0);
|
||||
irValue *ti = ir_emit_struct_ep(proc, var->var, 1);
|
||||
ir_emit_store(proc, data, ir_emit_conv(proc, g, t_rawptr));
|
||||
ir_emit_store(proc, ti, ir_type_info(proc, var_type));
|
||||
} else {
|
||||
ir_emit_store(proc, var->var, var->init);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for_array(i, global_variables) {
|
||||
irGlobalVariable *var = &global_variables[i];
|
||||
if (var->init != nullptr && var->init->kind != irValue_Constant) {
|
||||
Type *t = type_deref(ir_type(var->var));
|
||||
if (is_type_any(t)) {
|
||||
// NOTE(bill): Edge case for `any` type
|
||||
Type *var_type = default_type(ir_type(var->init));
|
||||
irValue *g = ir_add_global_generated(proc->module, var_type, var->init);
|
||||
ir_emit_store(proc, g, var->init);
|
||||
|
||||
irValue *data = ir_emit_struct_ep(proc, var->var, 0);
|
||||
irValue *ti = ir_emit_struct_ep(proc, var->var, 1);
|
||||
ir_emit_store(proc, data, ir_emit_conv(proc, g, t_rawptr));
|
||||
ir_emit_store(proc, ti, ir_type_info(proc, var_type));
|
||||
} else {
|
||||
ir_emit_store(proc, var->var, var->init);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{ // NOTE(bill): Setup type_info data
|
||||
CheckerInfo *info = proc->module->info;
|
||||
@@ -8312,6 +8294,31 @@ void ir_gen_tree(irGen *s) {
|
||||
}
|
||||
}
|
||||
|
||||
for_array(i, global_variables) {
|
||||
irGlobalVariable *var = &global_variables[i];
|
||||
if (var->decl->init_expr != nullptr) {
|
||||
var->init = ir_build_expr(proc, var->decl->init_expr);
|
||||
}
|
||||
|
||||
if (var->init != nullptr) {
|
||||
Type *t = type_deref(ir_type(var->var));
|
||||
|
||||
if (is_type_any(t)) {
|
||||
// NOTE(bill): Edge case for `any` type
|
||||
Type *var_type = default_type(ir_type(var->init));
|
||||
irValue *g = ir_add_global_generated(proc->module, var_type, var->init);
|
||||
ir_emit_store(proc, g, var->init);
|
||||
|
||||
irValue *data = ir_emit_struct_ep(proc, var->var, 0);
|
||||
irValue *ti = ir_emit_struct_ep(proc, var->var, 1);
|
||||
ir_emit_store(proc, data, ir_emit_conv(proc, g, t_rawptr));
|
||||
ir_emit_store(proc, ti, ir_type_info(proc, var_type));
|
||||
} else {
|
||||
ir_emit_store(proc, var->var, var->init);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ir_end_procedure_body(proc);
|
||||
}
|
||||
|
||||
|
||||
@@ -5086,10 +5086,11 @@ ParseFileError parse_files(Parser *p, String init_filename) {
|
||||
array_add(&p->imports, runtime_file);
|
||||
shared_file_count++;
|
||||
}
|
||||
|
||||
array_add(&p->imports, init_imported_file);
|
||||
p->init_fullpath = init_fullpath;
|
||||
|
||||
|
||||
/*
|
||||
// IMPORTANT TODO(bill): Figure out why this doesn't work on *nix sometimes
|
||||
#if USE_THREADED_PARSER && defined(GB_SYSTEM_WINDOWS)
|
||||
isize thread_count = gb_max(build_context.thread_count, 1);
|
||||
@@ -5148,14 +5149,15 @@ ParseFileError parse_files(Parser *p, String init_filename) {
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
for_array(i, p->imports) {
|
||||
ParseFileError err = parse_import(p, p->imports[i]);
|
||||
#else */
|
||||
isize import_index = 0;
|
||||
for (; import_index < p->imports.count; import_index++) {
|
||||
ParseFileError err = parse_import(p, p->imports[import_index]);
|
||||
if (err != ParseFile_None) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// #endif
|
||||
|
||||
for_array(i, p->files) {
|
||||
p->total_token_count += p->files[i].tokens.count;
|
||||
|
||||
@@ -159,7 +159,7 @@ struct ssaModule {
|
||||
gbAllocator tmp_allocator;
|
||||
gbArena tmp_arena;
|
||||
|
||||
Map<Entity *> min_dep_map; // Key: Entity *
|
||||
PtrSet<Entity *> min_dep_map;
|
||||
Map<ssaValue *> values; // Key: Entity *
|
||||
// List of registers for the specific architecture
|
||||
Array<ssaRegister> registers;
|
||||
@@ -2464,7 +2464,7 @@ bool ssa_generate(Parser *parser, CheckerInfo *info) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (map_get(&m.min_dep_map, hash_pointer(e)) == nullptr) {
|
||||
if (!ptr_set_exists(&m.min_dep_map, e)) {
|
||||
// NOTE(bill): Nothing depends upon it so doesn't need to be built
|
||||
continue;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user