mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-06 15:48:51 +00:00
Add -show-timings; Clean up polymorphic procedure code a bit
This commit is contained in:
+2
-3
@@ -10,12 +10,12 @@ struct Array {
|
|||||||
isize capacity;
|
isize capacity;
|
||||||
|
|
||||||
T &operator[](isize index) {
|
T &operator[](isize index) {
|
||||||
GB_ASSERT_MSG(0 <= index && index < count, "Index out of bounds");
|
GB_ASSERT_MSG(0 <= index && index < count, "Index %td is out of bounds ranges 0..<%td", index, count);
|
||||||
return data[index];
|
return data[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
T const &operator[](isize index) const {
|
T const &operator[](isize index) const {
|
||||||
GB_ASSERT_MSG(0 <= index && index < count, "Index out of bounds");
|
GB_ASSERT_MSG(0 <= index && index < count, "Index %td is out of bounds ranges 0..<%td", index, count);
|
||||||
return data[index];
|
return data[index];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -31,7 +31,6 @@ template <typename T> void array_reserve (Array<T> *array, isize capacit
|
|||||||
template <typename T> void array_resize (Array<T> *array, isize count);
|
template <typename T> void array_resize (Array<T> *array, isize count);
|
||||||
template <typename T> void array_set_capacity(Array<T> *array, isize capacity);
|
template <typename T> void array_set_capacity(Array<T> *array, isize capacity);
|
||||||
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void array_init(Array<T> *array, gbAllocator a, isize init_capacity) {
|
void array_init(Array<T> *array, gbAllocator a, isize init_capacity) {
|
||||||
array->allocator = a;
|
array->allocator = a;
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ struct BuildContext {
|
|||||||
bool is_dll;
|
bool is_dll;
|
||||||
bool generate_docs;
|
bool generate_docs;
|
||||||
i32 optimization_level;
|
i32 optimization_level;
|
||||||
|
bool show_timings;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+236
-166
@@ -25,6 +25,12 @@ struct CallArgumentData {
|
|||||||
Type * result_type;
|
Type * result_type;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct PolyProcData {
|
||||||
|
Entity * gen_entity;
|
||||||
|
ProcedureInfo proc_info;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#define CALL_ARGUMENT_CHECKER(name) CallArgumentError name(Checker *c, AstNode *call, Type *proc_type, Entity *entity, Array<Operand> operands, CallArgumentErrorMode show_error_mode, CallArgumentData *data)
|
#define CALL_ARGUMENT_CHECKER(name) CallArgumentError name(Checker *c, AstNode *call, Type *proc_type, Entity *entity, Array<Operand> operands, CallArgumentErrorMode show_error_mode, CallArgumentData *data)
|
||||||
typedef CALL_ARGUMENT_CHECKER(CallArgumentCheckerType);
|
typedef CALL_ARGUMENT_CHECKER(CallArgumentCheckerType);
|
||||||
@@ -52,9 +58,9 @@ void check_stmt (Checker *c, AstNode *node, u32 flags);
|
|||||||
void check_stmt_list (Checker *c, Array<AstNode *> stmts, u32 flags);
|
void check_stmt_list (Checker *c, Array<AstNode *> stmts, u32 flags);
|
||||||
void check_init_constant (Checker *c, Entity *e, Operand *operand);
|
void check_init_constant (Checker *c, Entity *e, Operand *operand);
|
||||||
bool check_representable_as_constant(Checker *c, ExactValue in_value, Type *type, ExactValue *out_value);
|
bool check_representable_as_constant(Checker *c, ExactValue in_value, Type *type, ExactValue *out_value);
|
||||||
|
bool check_procedure_type (Checker *c, Type *type, AstNode *proc_type_node, Array<Operand> *operands = nullptr);
|
||||||
CallArgumentData check_call_arguments (Checker *c, Operand *operand, Type *proc_type, AstNode *call);
|
CallArgumentData check_call_arguments (Checker *c, Operand *operand, Type *proc_type, AstNode *call);
|
||||||
|
|
||||||
|
|
||||||
void error_operand_not_expression(Operand *o) {
|
void error_operand_not_expression(Operand *o) {
|
||||||
if (o->mode == Addressing_Type) {
|
if (o->mode == Addressing_Type) {
|
||||||
gbString err = expr_to_string(o->expr);
|
gbString err = expr_to_string(o->expr);
|
||||||
@@ -140,11 +146,219 @@ bool check_is_assignable_to_using_subtype(Type *src, Type *dst) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool find_or_generate_polymorphic_procedure(Checker *c, Entity *base_entity, Type *type,
|
||||||
|
Array<Operand> *param_operands, PolyProcData *poly_proc_data,
|
||||||
|
bool check_later) {
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// //
|
||||||
|
// TODO CLEANUP(bill): This procedure is very messy and hacky. Clean this!!! //
|
||||||
|
// //
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
if (base_entity == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_type_proc(base_entity->type)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Type *src = base_type(base_entity->type);
|
||||||
|
Type *dst = nullptr;
|
||||||
|
if (type != nullptr) dst = base_type(type);
|
||||||
|
|
||||||
|
if (param_operands == nullptr) {
|
||||||
|
GB_ASSERT(dst != nullptr);
|
||||||
|
}
|
||||||
|
if (param_operands != nullptr) {
|
||||||
|
GB_ASSERT(dst == nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (!src->Proc.is_polymorphic || src->Proc.is_poly_specialized) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dst != nullptr) {
|
||||||
|
if (dst->Proc.is_polymorphic) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dst->Proc.param_count != src->Proc.param_count ||
|
||||||
|
dst->Proc.result_count != src->Proc.result_count) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DeclInfo *old_decl = decl_info_of_entity(&c->info, base_entity);
|
||||||
|
GB_ASSERT(old_decl != nullptr);
|
||||||
|
|
||||||
|
gbAllocator a = heap_allocator();
|
||||||
|
|
||||||
|
Array<Operand> operands = {};
|
||||||
|
if (param_operands) {
|
||||||
|
operands = *param_operands;
|
||||||
|
} else {
|
||||||
|
array_init(&operands, a, dst->Proc.param_count);
|
||||||
|
for (isize i = 0; i < dst->Proc.param_count; i++) {
|
||||||
|
Entity *param = dst->Proc.params->Tuple.variables[i];
|
||||||
|
Operand o = {Addressing_Value};
|
||||||
|
o.type = param->type;
|
||||||
|
array_add(&operands, o);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
defer (if (param_operands == nullptr) {
|
||||||
|
array_free(&operands);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
CheckerContext prev_context = c->context;
|
||||||
|
defer (c->context = prev_context);
|
||||||
|
|
||||||
|
Scope *scope = make_scope(base_entity->scope, a);
|
||||||
|
scope->is_proc = true;
|
||||||
|
c->context.scope = scope;
|
||||||
|
c->context.allow_polymorphic_types = true;
|
||||||
|
if (param_operands == nullptr) {
|
||||||
|
c->context.no_polymorphic_errors = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool generate_type_again = c->context.no_polymorphic_errors;
|
||||||
|
|
||||||
|
auto *pt = &src->Proc;
|
||||||
|
|
||||||
|
// NOTE(bill): This is slightly memory leaking if the type already exists
|
||||||
|
// Maybe it's better to check with the previous types first?
|
||||||
|
Type *final_proc_type = make_type_proc(c->allocator, scope, nullptr, 0, nullptr, 0, false, pt->calling_convention);
|
||||||
|
bool success = check_procedure_type(c, final_proc_type, pt->node, &operands);
|
||||||
|
if (!success) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto *found_gen_procs = map_get(&c->info.gen_procs, hash_pointer(base_entity->identifier));
|
||||||
|
if (found_gen_procs) {
|
||||||
|
auto procs = *found_gen_procs;
|
||||||
|
for_array(i, procs) {
|
||||||
|
Entity *other = procs[i];
|
||||||
|
Type *pt = base_type(other->type);
|
||||||
|
if (are_types_identical(pt, final_proc_type)) {
|
||||||
|
if (poly_proc_data) {
|
||||||
|
poly_proc_data->gen_entity = other;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (generate_type_again) {
|
||||||
|
// LEAK TODO(bill): This is technically a memory leak as it has to generate the type twice
|
||||||
|
|
||||||
|
bool prev_no_polymorphic_errors = c->context.no_polymorphic_errors;
|
||||||
|
defer (c->context.no_polymorphic_errors = prev_no_polymorphic_errors);
|
||||||
|
c->context.no_polymorphic_errors = false;
|
||||||
|
|
||||||
|
// NOTE(bill): Reset scope from the failed procedure type
|
||||||
|
scope_reset(scope);
|
||||||
|
|
||||||
|
success = check_procedure_type(c, final_proc_type, pt->node, &operands);
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (found_gen_procs) {
|
||||||
|
auto procs = *found_gen_procs;
|
||||||
|
for_array(i, procs) {
|
||||||
|
Entity *other = procs[i];
|
||||||
|
Type *pt = base_type(other->type);
|
||||||
|
if (are_types_identical(pt, final_proc_type)) {
|
||||||
|
if (poly_proc_data) {
|
||||||
|
poly_proc_data->gen_entity = other;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AstNode *proc_lit = clone_ast_node(a, old_decl->proc_lit);
|
||||||
|
ast_node(pl, ProcLit, proc_lit);
|
||||||
|
// NOTE(bill): Associate the scope declared above with this procedure declaration's type
|
||||||
|
add_scope(c, pl->type, final_proc_type->Proc.scope);
|
||||||
|
final_proc_type->Proc.is_poly_specialized = true;
|
||||||
|
final_proc_type->Proc.is_polymorphic = true;
|
||||||
|
|
||||||
|
u64 tags = base_entity->Procedure.tags;
|
||||||
|
AstNode *ident = clone_ast_node(a, base_entity->identifier);
|
||||||
|
Token token = ident->Ident.token;
|
||||||
|
DeclInfo *d = make_declaration_info(c->allocator, scope, old_decl->parent);
|
||||||
|
d->gen_proc_type = final_proc_type;
|
||||||
|
d->type_expr = pl->type;
|
||||||
|
d->proc_lit = proc_lit;
|
||||||
|
|
||||||
|
|
||||||
|
Entity *entity = make_entity_procedure(c->allocator, nullptr, token, final_proc_type, tags);
|
||||||
|
entity->identifier = ident;
|
||||||
|
|
||||||
|
add_entity_and_decl_info(c, ident, entity, d);
|
||||||
|
// NOTE(bill): Set the scope afterwards as this is not real overloading
|
||||||
|
entity->scope = scope->parent;
|
||||||
|
|
||||||
|
AstFile *file = nullptr;
|
||||||
|
{
|
||||||
|
Scope *s = entity->scope;
|
||||||
|
while (s != nullptr && s->file == nullptr) {
|
||||||
|
s = s->parent;
|
||||||
|
}
|
||||||
|
file = s->file;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProcedureInfo proc_info = {};
|
||||||
|
proc_info.file = file;
|
||||||
|
proc_info.token = token;
|
||||||
|
proc_info.decl = d;
|
||||||
|
proc_info.type = final_proc_type;
|
||||||
|
proc_info.body = pl->body;
|
||||||
|
proc_info.tags = tags;
|
||||||
|
proc_info.generated_from_polymorphic = true;
|
||||||
|
|
||||||
|
if (found_gen_procs) {
|
||||||
|
array_add(found_gen_procs, entity);
|
||||||
|
} else {
|
||||||
|
Array<Entity *> array = {};
|
||||||
|
array_init(&array, heap_allocator());
|
||||||
|
array_add(&array, entity);
|
||||||
|
map_set(&c->info.gen_procs, hash_pointer(base_entity->identifier), array);
|
||||||
|
}
|
||||||
|
|
||||||
|
GB_ASSERT(entity != nullptr);
|
||||||
|
|
||||||
|
|
||||||
|
if (poly_proc_data) {
|
||||||
|
poly_proc_data->gen_entity = entity;
|
||||||
|
poly_proc_data->proc_info = proc_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (check_later) {
|
||||||
|
// NOTE(bill): Check the newly generated procedure body
|
||||||
|
check_procedure_later(c, proc_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool check_polymorphic_procedure_assignment(Checker *c, Operand *operand, Type *type, PolyProcData *poly_proc_data) {
|
||||||
|
Entity *base_entity = entity_of_ident(&c->info, operand->expr);
|
||||||
|
if (base_entity == nullptr) return false;
|
||||||
|
return find_or_generate_polymorphic_procedure(c, base_entity, type, nullptr, poly_proc_data, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool find_or_generate_polymorphic_procedure_from_parameters(Checker *c, Entity *base_entity, Array<Operand> *operands, PolyProcData *poly_proc_data) {
|
||||||
|
return find_or_generate_polymorphic_procedure(c, base_entity, nullptr, operands, poly_proc_data, false);
|
||||||
|
}
|
||||||
|
|
||||||
// IMPORTANT TODO(bill): figure out the exact distance rules
|
|
||||||
// -1 is not convertable
|
|
||||||
// 0 is exact
|
|
||||||
// >0 is convertable
|
|
||||||
|
|
||||||
i64 check_distance_between_types(Checker *c, Operand *operand, Type *type) {
|
i64 check_distance_between_types(Checker *c, Operand *operand, Type *type) {
|
||||||
if (operand->mode == Addressing_Invalid ||
|
if (operand->mode == Addressing_Invalid ||
|
||||||
@@ -290,6 +504,11 @@ i64 check_distance_between_types(Checker *c, Operand *operand, Type *type) {
|
|||||||
if (are_types_identical(src, dst)) {
|
if (are_types_identical(src, dst)) {
|
||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
|
PolyProcData poly_proc_data = {};
|
||||||
|
if (check_polymorphic_procedure_assignment(c, operand, type, &poly_proc_data)) {
|
||||||
|
add_entity_use(c, operand->expr, poly_proc_data.gen_entity);
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_type_vector(dst)) {
|
if (is_type_vector(dst)) {
|
||||||
@@ -1737,7 +1956,7 @@ bool abi_compat_return_by_value(gbAllocator a, ProcCallingConvention cc, Type *a
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NOTE(bill): `operands` is for generating non generic procedure type
|
// NOTE(bill): `operands` is for generating non generic procedure type
|
||||||
bool check_procedure_type(Checker *c, Type *type, AstNode *proc_type_node, Array<Operand> *operands = nullptr) {
|
bool check_procedure_type(Checker *c, Type *type, AstNode *proc_type_node, Array<Operand> *operands) {
|
||||||
ast_node(pt, ProcType, proc_type_node);
|
ast_node(pt, ProcType, proc_type_node);
|
||||||
|
|
||||||
bool variadic = false;
|
bool variadic = false;
|
||||||
@@ -5151,154 +5370,7 @@ bool check_unpack_arguments(Checker *c, isize lhs_count, Array<Operand> *operand
|
|||||||
return optional_ok;
|
return optional_ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE(bill): Returns `nullptr` on failure
|
|
||||||
Entity *find_or_generate_polymorphic_procedure(Checker *c, AstNode *call, Entity *base_entity, CallArgumentCheckerType *call_checker,
|
|
||||||
Array<Operand> *operands, ProcedureInfo *proc_info_) {
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
// //
|
|
||||||
// TODO CLEANUP(bill): This procedure is very messy and hacky. Clean this!!! //
|
|
||||||
// //
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
if (base_entity == nullptr) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!is_type_proc(base_entity->type)) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
TypeProc *pt = &base_type(base_entity->type)->Proc;
|
|
||||||
if (!pt->is_polymorphic || pt->is_poly_specialized) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
DeclInfo *old_decl = decl_info_of_entity(&c->info, base_entity);
|
|
||||||
GB_ASSERT(old_decl != nullptr);
|
|
||||||
|
|
||||||
gbAllocator a = heap_allocator();
|
|
||||||
|
|
||||||
CheckerContext prev_context = c->context;
|
|
||||||
defer (c->context = prev_context);
|
|
||||||
|
|
||||||
Scope *scope = make_scope(base_entity->scope, a);
|
|
||||||
scope->is_proc = true;
|
|
||||||
c->context.scope = scope;
|
|
||||||
c->context.allow_polymorphic_types = true;
|
|
||||||
|
|
||||||
bool generate_type_again = c->context.no_polymorphic_errors;
|
|
||||||
|
|
||||||
// NOTE(bill): This is slightly memory leaking if the type already exists
|
|
||||||
// Maybe it's better to check with the previous types first?
|
|
||||||
Type *final_proc_type = make_type_proc(c->allocator, scope, nullptr, 0, nullptr, 0, false, pt->calling_convention);
|
|
||||||
bool success = check_procedure_type(c, final_proc_type, pt->node, operands);
|
|
||||||
if (!success) {
|
|
||||||
ProcedureInfo proc_info = {};
|
|
||||||
if (proc_info_) *proc_info_ = proc_info;
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto *found_gen_procs = map_get(&c->info.gen_procs, hash_pointer(base_entity->identifier));
|
|
||||||
if (found_gen_procs) {
|
|
||||||
auto procs = *found_gen_procs;
|
|
||||||
for_array(i, procs) {
|
|
||||||
Entity *other = procs[i];
|
|
||||||
Type *pt = base_type(other->type);
|
|
||||||
if (are_types_identical(pt, final_proc_type)) {
|
|
||||||
// NOTE(bill): This scope is not needed any more, destroy it
|
|
||||||
// destroy_scope(scope);
|
|
||||||
return other;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (generate_type_again) {
|
|
||||||
// LEAK TODO(bill): This is technically a memory leak as it has to generate the type twice
|
|
||||||
|
|
||||||
bool prev_no_polymorphic_errors = c->context.no_polymorphic_errors;
|
|
||||||
defer (c->context.no_polymorphic_errors = prev_no_polymorphic_errors);
|
|
||||||
c->context.no_polymorphic_errors = false;
|
|
||||||
|
|
||||||
// NOTE(bill): Reset scope from the failed procedure type
|
|
||||||
scope_reset(scope);
|
|
||||||
|
|
||||||
success = check_procedure_type(c, final_proc_type, pt->node, operands);
|
|
||||||
|
|
||||||
if (!success) {
|
|
||||||
ProcedureInfo proc_info = {};
|
|
||||||
if (proc_info_) *proc_info_ = proc_info;
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (found_gen_procs) {
|
|
||||||
auto procs = *found_gen_procs;
|
|
||||||
for_array(i, procs) {
|
|
||||||
Entity *other = procs[i];
|
|
||||||
Type *pt = base_type(other->type);
|
|
||||||
if (are_types_identical(pt, final_proc_type)) {
|
|
||||||
// NOTE(bill): This scope is not needed any more, destroy it
|
|
||||||
// destroy_scope(scope);
|
|
||||||
return other;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AstNode *proc_lit = clone_ast_node(a, old_decl->proc_lit);
|
|
||||||
ast_node(pl, ProcLit, proc_lit);
|
|
||||||
// NOTE(bill): Associate the scope declared above with this procedure declaration's type
|
|
||||||
add_scope(c, pl->type, final_proc_type->Proc.scope);
|
|
||||||
final_proc_type->Proc.is_poly_specialized = true;
|
|
||||||
final_proc_type->Proc.is_polymorphic = true;
|
|
||||||
|
|
||||||
u64 tags = base_entity->Procedure.tags;
|
|
||||||
AstNode *ident = clone_ast_node(a, base_entity->identifier);
|
|
||||||
Token token = ident->Ident.token;
|
|
||||||
DeclInfo *d = make_declaration_info(c->allocator, scope, old_decl->parent);
|
|
||||||
d->gen_proc_type = final_proc_type;
|
|
||||||
d->type_expr = pl->type;
|
|
||||||
d->proc_lit = proc_lit;
|
|
||||||
|
|
||||||
|
|
||||||
Entity *entity = make_entity_procedure(c->allocator, nullptr, token, final_proc_type, tags);
|
|
||||||
entity->identifier = ident;
|
|
||||||
|
|
||||||
add_entity_and_decl_info(c, ident, entity, d);
|
|
||||||
// NOTE(bill): Set the scope afterwards as this is not real overloading
|
|
||||||
entity->scope = scope->parent;
|
|
||||||
|
|
||||||
AstFile *file = nullptr;
|
|
||||||
{
|
|
||||||
Scope *s = entity->scope;
|
|
||||||
while (s != nullptr && s->file == nullptr) {
|
|
||||||
s = s->parent;
|
|
||||||
}
|
|
||||||
file = s->file;
|
|
||||||
}
|
|
||||||
|
|
||||||
ProcedureInfo proc_info = {};
|
|
||||||
proc_info.file = file;
|
|
||||||
proc_info.token = token;
|
|
||||||
proc_info.decl = d;
|
|
||||||
proc_info.type = final_proc_type;
|
|
||||||
proc_info.body = pl->body;
|
|
||||||
proc_info.tags = tags;
|
|
||||||
proc_info.generated_from_polymorphic = true;
|
|
||||||
|
|
||||||
if (found_gen_procs) {
|
|
||||||
array_add(found_gen_procs, entity);
|
|
||||||
} else {
|
|
||||||
Array<Entity *> array = {};
|
|
||||||
array_init(&array, heap_allocator());
|
|
||||||
array_add(&array, entity);
|
|
||||||
map_set(&c->info.gen_procs, hash_pointer(base_entity->identifier), array);
|
|
||||||
}
|
|
||||||
|
|
||||||
GB_ASSERT(entity != nullptr);
|
|
||||||
|
|
||||||
|
|
||||||
if (proc_info_) *proc_info_ = proc_info;
|
|
||||||
return entity;
|
|
||||||
}
|
|
||||||
|
|
||||||
CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
|
CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
|
||||||
ast_node(ce, CallExpr, call);
|
ast_node(ce, CallExpr, call);
|
||||||
@@ -5386,11 +5458,11 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// NOTE(bill): Generate the procedure type for this generic instance
|
// NOTE(bill): Generate the procedure type for this generic instance
|
||||||
ProcedureInfo proc_info = {};
|
PolyProcData poly_proc_data = {};
|
||||||
|
|
||||||
if (pt->is_polymorphic && !pt->is_poly_specialized) {
|
if (pt->is_polymorphic && !pt->is_poly_specialized) {
|
||||||
gen_entity = find_or_generate_polymorphic_procedure(c, call, entity, check_call_arguments_internal, &operands, &proc_info);
|
if (find_or_generate_polymorphic_procedure_from_parameters(c, entity, &operands, &poly_proc_data)) {
|
||||||
if (gen_entity != nullptr) {
|
gen_entity = poly_proc_data.gen_entity;
|
||||||
GB_ASSERT(is_type_proc(gen_entity->type));
|
GB_ASSERT(is_type_proc(gen_entity->type));
|
||||||
final_proc_type = gen_entity->type;
|
final_proc_type = gen_entity->type;
|
||||||
}
|
}
|
||||||
@@ -5477,11 +5549,9 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
|
|||||||
gb_printf_err("append %s with score %lld %d\n", type_to_string(final_proc_type), score, err);
|
gb_printf_err("append %s with score %lld %d\n", type_to_string(final_proc_type), score, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gen_entity != nullptr && err == CallArgumentError_None) {
|
if (err == CallArgumentError_None && poly_proc_data.proc_info.decl != nullptr) {
|
||||||
if (proc_info.decl != nullptr) {
|
// NOTE(bill): Check the newly generated procedure body
|
||||||
// NOTE(bill): Check the newly generated procedure body
|
check_procedure_later(c, poly_proc_data.proc_info);
|
||||||
check_procedure_later(c, proc_info);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5622,11 +5692,11 @@ CALL_ARGUMENT_CHECKER(check_named_call_arguments) {
|
|||||||
|
|
||||||
Entity *gen_entity = nullptr;
|
Entity *gen_entity = nullptr;
|
||||||
if (pt->is_polymorphic && !pt->is_poly_specialized && err == CallArgumentError_None) {
|
if (pt->is_polymorphic && !pt->is_poly_specialized && err == CallArgumentError_None) {
|
||||||
ProcedureInfo proc_info = {};
|
PolyProcData poly_proc_data = {};
|
||||||
gen_entity = find_or_generate_polymorphic_procedure(c, call, entity, check_named_call_arguments, &ordered_operands, &proc_info);
|
if (find_or_generate_polymorphic_procedure_from_parameters(c, entity, &ordered_operands, &poly_proc_data)) {
|
||||||
if (gen_entity != nullptr) {
|
gen_entity = poly_proc_data.gen_entity;
|
||||||
if (proc_info.decl != nullptr) {
|
if (poly_proc_data.proc_info.decl != nullptr) {
|
||||||
check_procedure_later(c, proc_info);
|
check_procedure_later(c, poly_proc_data.proc_info);
|
||||||
}
|
}
|
||||||
Type *gept = base_type(gen_entity->type);
|
Type *gept = base_type(gen_entity->type);
|
||||||
GB_ASSERT(is_type_proc(gept));
|
GB_ASSERT(is_type_proc(gept));
|
||||||
|
|||||||
+4
-4
@@ -1249,8 +1249,8 @@ irValue *ir_add_module_constant(irModule *m, Type *type, ExactValue value) {
|
|||||||
irValue *ir_add_global_string_array(irModule *m, String string) {
|
irValue *ir_add_global_string_array(irModule *m, String string) {
|
||||||
// TODO(bill): Should this use the arena allocator or the heap allocator?
|
// TODO(bill): Should this use the arena allocator or the heap allocator?
|
||||||
// Strings could be huge!
|
// Strings could be huge!
|
||||||
gbAllocator a = m->allocator;
|
// gbAllocator a = m->allocator;
|
||||||
// gbAllocator a = gb_heap_allocator();
|
gbAllocator a = gb_heap_allocator();
|
||||||
|
|
||||||
isize max_len = 6+8+1;
|
isize max_len = 6+8+1;
|
||||||
u8 *str = cast(u8 *)gb_alloc_array(a, u8, max_len);
|
u8 *str = cast(u8 *)gb_alloc_array(a, u8, max_len);
|
||||||
@@ -1260,12 +1260,12 @@ irValue *ir_add_global_string_array(irModule *m, String string) {
|
|||||||
String name = make_string(str, len-1);
|
String name = make_string(str, len-1);
|
||||||
Token token = {Token_String};
|
Token token = {Token_String};
|
||||||
token.string = name;
|
token.string = name;
|
||||||
Type *type = make_type_array(a, t_u8, string.len);
|
Type *type = make_type_array(a, t_u8, string.len+1);
|
||||||
ExactValue ev = exact_value_string(string);
|
ExactValue ev = exact_value_string(string);
|
||||||
Entity *entity = make_entity_constant(a, nullptr, token, type, ev);
|
Entity *entity = make_entity_constant(a, nullptr, token, type, ev);
|
||||||
irValue *g = ir_value_global(a, entity, ir_add_module_constant(m, type, ev));
|
irValue *g = ir_value_global(a, entity, ir_add_module_constant(m, type, ev));
|
||||||
g->Global.is_private = true;
|
g->Global.is_private = true;
|
||||||
// g->Global.is_unnamed_addr = true;
|
g->Global.is_unnamed_addr = true;
|
||||||
// g->Global.is_constant = true;
|
// g->Global.is_constant = true;
|
||||||
|
|
||||||
ir_module_add_value(m, entity, g);
|
ir_module_add_value(m, entity, g);
|
||||||
|
|||||||
+1
-1
@@ -415,7 +415,7 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type *
|
|||||||
GB_ASSERT(is_type_array(type));
|
GB_ASSERT(is_type_array(type));
|
||||||
ir_fprintf(f, "c\"");
|
ir_fprintf(f, "c\"");
|
||||||
ir_print_escape_string(f, str, false, false);
|
ir_print_escape_string(f, str, false, false);
|
||||||
ir_fprintf(f, "\"");
|
ir_fprintf(f, "\\00\"");
|
||||||
} else {
|
} else {
|
||||||
// HACK NOTE(bill): This is a hack but it works because strings are created at the very end
|
// HACK NOTE(bill): This is a hack but it works because strings are created at the very end
|
||||||
// of the .ll file
|
// of the .ll file
|
||||||
|
|||||||
+119
-97
@@ -1,5 +1,4 @@
|
|||||||
#define USE_CUSTOM_BACKEND 0
|
#define USE_CUSTOM_BACKEND 0
|
||||||
// #define PRINT_TIMINGS
|
|
||||||
|
|
||||||
#include "common.cpp"
|
#include "common.cpp"
|
||||||
#include "timings.cpp"
|
#include "timings.cpp"
|
||||||
@@ -171,6 +170,7 @@ enum BuildFlagKind {
|
|||||||
BuildFlag_Invalid,
|
BuildFlag_Invalid,
|
||||||
|
|
||||||
BuildFlag_OptimizationLevel,
|
BuildFlag_OptimizationLevel,
|
||||||
|
BuildFlag_ShowTimings,
|
||||||
|
|
||||||
BuildFlag_COUNT,
|
BuildFlag_COUNT,
|
||||||
};
|
};
|
||||||
@@ -202,6 +202,9 @@ bool parse_build_flags(Array<String> args) {
|
|||||||
Array<BuildFlag> build_flags = {};
|
Array<BuildFlag> build_flags = {};
|
||||||
array_init(&build_flags, heap_allocator(), BuildFlag_COUNT);
|
array_init(&build_flags, heap_allocator(), BuildFlag_COUNT);
|
||||||
add_flag(&build_flags, BuildFlag_OptimizationLevel, str_lit("opt"), BuildFlagParam_Integer);
|
add_flag(&build_flags, BuildFlag_OptimizationLevel, str_lit("opt"), BuildFlagParam_Integer);
|
||||||
|
add_flag(&build_flags, BuildFlag_ShowTimings, str_lit("show-timings"), BuildFlagParam_None);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Array<String> flag_args = args;
|
Array<String> flag_args = args;
|
||||||
flag_args.data += 3;
|
flag_args.data += 3;
|
||||||
@@ -214,105 +217,111 @@ bool parse_build_flags(Array<String> args) {
|
|||||||
String flag = flag_args[i];
|
String flag = flag_args[i];
|
||||||
if (flag[0] != '-') {
|
if (flag[0] != '-') {
|
||||||
gb_printf_err("Invalid flag: %.*s\n", LIT(flag));
|
gb_printf_err("Invalid flag: %.*s\n", LIT(flag));
|
||||||
} else {
|
continue;
|
||||||
String name = substring(flag, 1, flag.len);
|
}
|
||||||
isize end = 0;
|
String name = substring(flag, 1, flag.len);
|
||||||
for (; end < name.len; end++) {
|
isize end = 0;
|
||||||
if (name[end] == '=') {
|
for (; end < name.len; end++) {
|
||||||
break;
|
if (name[end] == '=') break;
|
||||||
}
|
}
|
||||||
}
|
name = substring(name, 0, end);
|
||||||
name.len = end;
|
String param = {};
|
||||||
String param = substring(flag, 2+end, flag.len);
|
if (end < flag.len-1) param = substring(flag, 2+end, flag.len);
|
||||||
|
|
||||||
bool found = false;
|
bool found = false;
|
||||||
for_array(build_flag_index, build_flags) {
|
for_array(build_flag_index, build_flags) {
|
||||||
BuildFlag bf = build_flags[build_flag_index];
|
BuildFlag bf = build_flags[build_flag_index];
|
||||||
if (bf.name == name) {
|
if (bf.name == name) {
|
||||||
found = true;
|
found = true;
|
||||||
if (set_flags[bf.kind]) {
|
if (set_flags[bf.kind]) {
|
||||||
gb_printf_err("Previous flag set: `%.*s`\n", LIT(name));
|
gb_printf_err("Previous flag set: `%.*s`\n", LIT(name));
|
||||||
|
bad_flags = true;
|
||||||
|
} else {
|
||||||
|
ExactValue value = {};
|
||||||
|
bool ok = false;
|
||||||
|
if (bf.param_kind == BuildFlagParam_None) {
|
||||||
|
if (param.len == 0) {
|
||||||
|
ok = true;
|
||||||
|
} else {
|
||||||
|
gb_printf_err("Flag `%.*s` was not expecting a parameter `%.*s`\n", LIT(name), LIT(param));
|
||||||
|
bad_flags = true;
|
||||||
|
}
|
||||||
|
} else if (param.len == 0) {
|
||||||
|
gb_printf_err("Flag missing for `%.*s`\n", LIT(name));
|
||||||
bad_flags = true;
|
bad_flags = true;
|
||||||
} else {
|
} else {
|
||||||
ExactValue value = {};
|
ok = true;
|
||||||
bool ok = false;
|
switch (bf.param_kind) {
|
||||||
if (bf.param_kind == BuildFlagParam_None) {
|
default: ok = false; break;
|
||||||
if (param.len == 0) {
|
case BuildFlagParam_Boolean: {
|
||||||
ok = true;
|
if (param == "t") {
|
||||||
|
value = exact_value_bool(true);
|
||||||
|
} else if (param == "T") {
|
||||||
|
value = exact_value_bool(true);
|
||||||
|
} else if (param == "true") {
|
||||||
|
value = exact_value_bool(true);
|
||||||
|
} else if (param == "TRUE") {
|
||||||
|
value = exact_value_bool(true);
|
||||||
|
} else if (param == "1") {
|
||||||
|
value = exact_value_bool(true);
|
||||||
|
} else if (param == "f") {
|
||||||
|
value = exact_value_bool(false);
|
||||||
|
} else if (param == "F") {
|
||||||
|
value = exact_value_bool(false);
|
||||||
|
} else if (param == "false") {
|
||||||
|
value = exact_value_bool(false);
|
||||||
|
} else if (param == "FALSE") {
|
||||||
|
value = exact_value_bool(false);
|
||||||
|
} else if (param == "0") {
|
||||||
|
value = exact_value_bool(false);
|
||||||
} else {
|
} else {
|
||||||
gb_printf_err("Flag `%.*s` was not expecting a parameter `%.*s`\n", LIT(name), LIT(param));
|
gb_printf_err("Invalid flag parameter for `%.*s` = `%.*s`\n", LIT(name), LIT(param));
|
||||||
bad_flags = true;
|
|
||||||
}
|
}
|
||||||
} else {
|
} break;
|
||||||
if (param.len == 0) {
|
case BuildFlagParam_Integer:
|
||||||
gb_printf_err("Flag missing for `%.*s`\n", LIT(name));
|
value = exact_value_integer_from_string(param);
|
||||||
bad_flags = true;
|
break;
|
||||||
} else {
|
case BuildFlagParam_Float:
|
||||||
ok = true;
|
value = exact_value_float_from_string(param);
|
||||||
switch (bf.param_kind) {
|
break;
|
||||||
default: ok = false; break;
|
case BuildFlagParam_String:
|
||||||
case BuildFlagParam_Boolean: {
|
value = exact_value_string(param);
|
||||||
if (param == "t") {
|
break;
|
||||||
value = exact_value_bool(true);
|
|
||||||
} else if (param == "T") {
|
|
||||||
value = exact_value_bool(true);
|
|
||||||
} else if (param == "true") {
|
|
||||||
value = exact_value_bool(true);
|
|
||||||
} else if (param == "TRUE") {
|
|
||||||
value = exact_value_bool(true);
|
|
||||||
} else if (param == "1") {
|
|
||||||
value = exact_value_bool(true);
|
|
||||||
} else if (param == "f") {
|
|
||||||
value = exact_value_bool(false);
|
|
||||||
} else if (param == "F") {
|
|
||||||
value = exact_value_bool(false);
|
|
||||||
} else if (param == "false") {
|
|
||||||
value = exact_value_bool(false);
|
|
||||||
} else if (param == "FALSE") {
|
|
||||||
value = exact_value_bool(false);
|
|
||||||
} else if (param == "0") {
|
|
||||||
value = exact_value_bool(false);
|
|
||||||
} else {
|
|
||||||
gb_printf_err("Invalid flag parameter for `%.*s` = `%.*s`\n", LIT(name), LIT(param));
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
case BuildFlagParam_Integer:
|
|
||||||
value = exact_value_integer_from_string(param);
|
|
||||||
break;
|
|
||||||
case BuildFlagParam_Float:
|
|
||||||
value = exact_value_float_from_string(param);
|
|
||||||
break;
|
|
||||||
case BuildFlagParam_String:
|
|
||||||
value = exact_value_string(param);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
if (ok) {
|
|
||||||
switch (bf.kind) {
|
|
||||||
case BuildFlag_OptimizationLevel:
|
|
||||||
if (value.kind == ExactValue_Integer) {
|
|
||||||
build_context.optimization_level = cast(i32)i128_to_i64(value.value_integer);
|
|
||||||
} else {
|
|
||||||
gb_printf_err("%.*s expected an integer, got %.*s", LIT(name), LIT(param));
|
|
||||||
bad_flags = true;
|
|
||||||
ok = false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
set_flags[bf.kind] = ok;
|
|
||||||
}
|
}
|
||||||
break;
|
if (ok) {
|
||||||
|
switch (bf.kind) {
|
||||||
|
case BuildFlag_OptimizationLevel:
|
||||||
|
if (value.kind == ExactValue_Integer) {
|
||||||
|
build_context.optimization_level = cast(i32)i128_to_i64(value.value_integer);
|
||||||
|
} else {
|
||||||
|
gb_printf_err("%.*s expected an integer, got %.*s", LIT(name), LIT(param));
|
||||||
|
bad_flags = true;
|
||||||
|
ok = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case BuildFlag_ShowTimings:
|
||||||
|
if (value.kind == ExactValue_Invalid) {
|
||||||
|
build_context.show_timings = true;
|
||||||
|
} else {
|
||||||
|
gb_printf_err("%.*s expected no value, got %.*s", LIT(name), LIT(param));
|
||||||
|
bad_flags = true;
|
||||||
|
ok = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
set_flags[bf.kind] = ok;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (!found) {
|
}
|
||||||
gb_printf_err("Unknown flag: `%.*s`\n", LIT(name));
|
if (!found) {
|
||||||
bad_flags = true;
|
gb_printf_err("Unknown flag: `%.*s`\n", LIT(name));
|
||||||
}
|
bad_flags = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -320,7 +329,20 @@ bool parse_build_flags(Array<String> args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void show_timings(Checker *c, Timings *t) {
|
||||||
|
Parser *p = c->parser;
|
||||||
|
timings_print_all(t);
|
||||||
|
gb_printf("\n");
|
||||||
|
gb_printf("Total lines: %td\n", p->total_line_count);
|
||||||
|
gb_printf("Lines/s: %.3f\n", cast(f64)p->total_line_count/t->total_time_seconds);
|
||||||
|
gb_printf("us/Line: %.3f\n", 1.0e6*t->total_time_seconds/cast(f64)p->total_line_count);
|
||||||
|
gb_printf("\n");
|
||||||
|
gb_printf("Total tokens: %td\n", p->total_token_count);
|
||||||
|
gb_printf("Tokens/s: %.3f\n", cast(f64)p->total_token_count/t->total_time_seconds);
|
||||||
|
gb_printf("us/Token: %.3f\n\n", 1.0e6*t->total_time_seconds/cast(f64)p->total_token_count);
|
||||||
|
gb_printf("\n");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
int main(int arg_count, char **arg_ptr) {
|
int main(int arg_count, char **arg_ptr) {
|
||||||
if (arg_count < 2) {
|
if (arg_count < 2) {
|
||||||
@@ -556,9 +578,9 @@ int main(int arg_count, char **arg_ptr) {
|
|||||||
return exit_code;
|
return exit_code;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(PRINT_TIMINGS)
|
if (build_context.show_timings) {
|
||||||
timings_print_all(&timings);
|
show_timings(&checker, &timings);
|
||||||
#endif
|
}
|
||||||
|
|
||||||
|
|
||||||
if (run_output) {
|
if (run_output) {
|
||||||
@@ -662,9 +684,9 @@ int main(int arg_count, char **arg_ptr) {
|
|||||||
return exit_code;
|
return exit_code;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(PRINT_TIMINGS)
|
if (build_context.show_timings) {
|
||||||
timings_print_all(&timings);
|
show_timings(&checker, &timings);
|
||||||
#endif
|
}
|
||||||
|
|
||||||
if (run_output) {
|
if (run_output) {
|
||||||
system_exec_command_line_app("odin run", false, "%.*s", LIT(output_base));
|
system_exec_command_line_app("odin run", false, "%.*s", LIT(output_base));
|
||||||
|
|||||||
+10
-4
@@ -8,6 +8,7 @@ struct Timings {
|
|||||||
TimeStamp total;
|
TimeStamp total;
|
||||||
Array<TimeStamp> sections;
|
Array<TimeStamp> sections;
|
||||||
u64 freq;
|
u64 freq;
|
||||||
|
f64 total_time_seconds;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -123,16 +124,21 @@ void timings_print_all(Timings *t) {
|
|||||||
|
|
||||||
GB_ASSERT(max_len <= gb_size_of(SPACES)-1);
|
GB_ASSERT(max_len <= gb_size_of(SPACES)-1);
|
||||||
|
|
||||||
gb_printf("%.*s%.*s - %.3f ms\n",
|
t->total_time_seconds = cast(f64)(t->total.finish - t->total.start) / cast(f64)t->freq;
|
||||||
|
|
||||||
|
f64 total_ms = time_stamp_as_ms(t->total, t->freq);
|
||||||
|
|
||||||
|
gb_printf("%.*s%.*s - % 9.3f ms\n",
|
||||||
LIT(t->total.label),
|
LIT(t->total.label),
|
||||||
cast(int)(max_len-t->total.label.len), SPACES,
|
cast(int)(max_len-t->total.label.len), SPACES,
|
||||||
time_stamp_as_ms(t->total, t->freq));
|
total_ms);
|
||||||
|
|
||||||
for_array(i, t->sections) {
|
for_array(i, t->sections) {
|
||||||
TimeStamp ts = t->sections[i];
|
TimeStamp ts = t->sections[i];
|
||||||
gb_printf("%.*s%.*s - %.3f ms\n",
|
f64 section_ms = time_stamp_as_ms(ts, t->freq);
|
||||||
|
gb_printf("%.*s%.*s - % 9.3f ms - %5.2f%%\n",
|
||||||
LIT(ts.label),
|
LIT(ts.label),
|
||||||
cast(int)(max_len-ts.label.len), SPACES,
|
cast(int)(max_len-ts.label.len), SPACES,
|
||||||
time_stamp_as_ms(ts, t->freq));
|
section_ms, 100*section_ms/total_ms);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user