diff --git a/src/array.cpp b/src/array.cpp index 6670c7843..b382d3262 100644 --- a/src/array.cpp +++ b/src/array.cpp @@ -10,12 +10,12 @@ struct Array { isize capacity; 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]; } 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]; } }; @@ -31,7 +31,6 @@ template void array_reserve (Array *array, isize capacit template void array_resize (Array *array, isize count); template void array_set_capacity(Array *array, isize capacity); - template void array_init(Array *array, gbAllocator a, isize init_capacity) { array->allocator = a; diff --git a/src/build_settings.cpp b/src/build_settings.cpp index b17313c4c..3f5530da4 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -18,6 +18,7 @@ struct BuildContext { bool is_dll; bool generate_docs; i32 optimization_level; + bool show_timings; }; diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 07bd90aff..d69fcb6c7 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -25,6 +25,12 @@ struct CallArgumentData { 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 operands, CallArgumentErrorMode show_error_mode, CallArgumentData *data) 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 stmts, u32 flags); 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_procedure_type (Checker *c, Type *type, AstNode *proc_type_node, Array *operands = nullptr); CallArgumentData check_call_arguments (Checker *c, Operand *operand, Type *proc_type, AstNode *call); - void error_operand_not_expression(Operand *o) { if (o->mode == Addressing_Type) { gbString err = expr_to_string(o->expr); @@ -140,11 +146,219 @@ bool check_is_assignable_to_using_subtype(Type *src, Type *dst) { return false; } +bool find_or_generate_polymorphic_procedure(Checker *c, Entity *base_entity, Type *type, + Array *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 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 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 *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) { 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)) { 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)) { @@ -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 -bool check_procedure_type(Checker *c, Type *type, AstNode *proc_type_node, Array *operands = nullptr) { +bool check_procedure_type(Checker *c, Type *type, AstNode *proc_type_node, Array *operands) { ast_node(pt, ProcType, proc_type_node); bool variadic = false; @@ -5151,154 +5370,7 @@ bool check_unpack_arguments(Checker *c, isize lhs_count, Array *operand 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 *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 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) { ast_node(ce, CallExpr, call); @@ -5386,11 +5458,11 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { } } else { // 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) { - gen_entity = find_or_generate_polymorphic_procedure(c, call, entity, check_call_arguments_internal, &operands, &proc_info); - if (gen_entity != nullptr) { + if (find_or_generate_polymorphic_procedure_from_parameters(c, entity, &operands, &poly_proc_data)) { + gen_entity = poly_proc_data.gen_entity; GB_ASSERT(is_type_proc(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); } - if (gen_entity != nullptr && err == CallArgumentError_None) { - if (proc_info.decl != nullptr) { - // NOTE(bill): Check the newly generated procedure body - check_procedure_later(c, proc_info); - } + if (err == CallArgumentError_None && poly_proc_data.proc_info.decl != nullptr) { + // NOTE(bill): Check the newly generated procedure body + check_procedure_later(c, poly_proc_data.proc_info); } } } @@ -5622,11 +5692,11 @@ CALL_ARGUMENT_CHECKER(check_named_call_arguments) { Entity *gen_entity = nullptr; if (pt->is_polymorphic && !pt->is_poly_specialized && err == CallArgumentError_None) { - ProcedureInfo proc_info = {}; - gen_entity = find_or_generate_polymorphic_procedure(c, call, entity, check_named_call_arguments, &ordered_operands, &proc_info); - if (gen_entity != nullptr) { - if (proc_info.decl != nullptr) { - check_procedure_later(c, proc_info); + PolyProcData poly_proc_data = {}; + if (find_or_generate_polymorphic_procedure_from_parameters(c, entity, &ordered_operands, &poly_proc_data)) { + gen_entity = poly_proc_data.gen_entity; + if (poly_proc_data.proc_info.decl != nullptr) { + check_procedure_later(c, poly_proc_data.proc_info); } Type *gept = base_type(gen_entity->type); GB_ASSERT(is_type_proc(gept)); diff --git a/src/ir.cpp b/src/ir.cpp index d74307c81..9335a71fd 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -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) { // TODO(bill): Should this use the arena allocator or the heap allocator? // Strings could be huge! - gbAllocator a = m->allocator; - // gbAllocator a = gb_heap_allocator(); + // gbAllocator a = m->allocator; + gbAllocator a = gb_heap_allocator(); isize max_len = 6+8+1; 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); Token token = {Token_String}; 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); Entity *entity = make_entity_constant(a, nullptr, token, type, ev); irValue *g = ir_value_global(a, entity, ir_add_module_constant(m, type, ev)); g->Global.is_private = true; - // g->Global.is_unnamed_addr = true; + g->Global.is_unnamed_addr = true; // g->Global.is_constant = true; ir_module_add_value(m, entity, g); diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 48935ddf3..878291ebf 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -415,7 +415,7 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type * GB_ASSERT(is_type_array(type)); ir_fprintf(f, "c\""); ir_print_escape_string(f, str, false, false); - ir_fprintf(f, "\""); + ir_fprintf(f, "\\00\""); } else { // HACK NOTE(bill): This is a hack but it works because strings are created at the very end // of the .ll file diff --git a/src/main.cpp b/src/main.cpp index e4e921d21..150516345 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,5 +1,4 @@ #define USE_CUSTOM_BACKEND 0 -// #define PRINT_TIMINGS #include "common.cpp" #include "timings.cpp" @@ -171,6 +170,7 @@ enum BuildFlagKind { BuildFlag_Invalid, BuildFlag_OptimizationLevel, + BuildFlag_ShowTimings, BuildFlag_COUNT, }; @@ -202,6 +202,9 @@ bool parse_build_flags(Array args) { Array build_flags = {}; array_init(&build_flags, heap_allocator(), BuildFlag_COUNT); add_flag(&build_flags, BuildFlag_OptimizationLevel, str_lit("opt"), BuildFlagParam_Integer); + add_flag(&build_flags, BuildFlag_ShowTimings, str_lit("show-timings"), BuildFlagParam_None); + + Array flag_args = args; flag_args.data += 3; @@ -214,105 +217,111 @@ bool parse_build_flags(Array args) { String flag = flag_args[i]; if (flag[0] != '-') { gb_printf_err("Invalid flag: %.*s\n", LIT(flag)); - } else { - String name = substring(flag, 1, flag.len); - isize end = 0; - for (; end < name.len; end++) { - if (name[end] == '=') { - break; - } - } - name.len = end; - String param = substring(flag, 2+end, flag.len); + continue; + } + String name = substring(flag, 1, flag.len); + isize end = 0; + for (; end < name.len; end++) { + if (name[end] == '=') break; + } + name = substring(name, 0, end); + String param = {}; + if (end < flag.len-1) param = substring(flag, 2+end, flag.len); - bool found = false; - for_array(build_flag_index, build_flags) { - BuildFlag bf = build_flags[build_flag_index]; - if (bf.name == name) { - found = true; - if (set_flags[bf.kind]) { - gb_printf_err("Previous flag set: `%.*s`\n", LIT(name)); + bool found = false; + for_array(build_flag_index, build_flags) { + BuildFlag bf = build_flags[build_flag_index]; + if (bf.name == name) { + found = true; + if (set_flags[bf.kind]) { + 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; } else { - ExactValue value = {}; - bool ok = false; - if (bf.param_kind == BuildFlagParam_None) { - if (param.len == 0) { - ok = true; + ok = true; + switch (bf.param_kind) { + default: ok = false; break; + case BuildFlagParam_Boolean: { + 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 { - gb_printf_err("Flag `%.*s` was not expecting a parameter `%.*s`\n", LIT(name), LIT(param)); - bad_flags = true; + gb_printf_err("Invalid flag parameter for `%.*s` = `%.*s`\n", LIT(name), LIT(param)); } - } else { - if (param.len == 0) { - gb_printf_err("Flag missing for `%.*s`\n", LIT(name)); - bad_flags = true; - } else { - ok = true; - switch (bf.param_kind) { - default: ok = false; break; - case BuildFlagParam_Boolean: { - 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 { - 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; - } - } - + } 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)); - bad_flags = true; - } + } + if (!found) { + gb_printf_err("Unknown flag: `%.*s`\n", LIT(name)); + bad_flags = true; } } @@ -320,7 +329,20 @@ bool parse_build_flags(Array 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) { if (arg_count < 2) { @@ -556,9 +578,9 @@ int main(int arg_count, char **arg_ptr) { return exit_code; } - #if defined(PRINT_TIMINGS) - timings_print_all(&timings); - #endif + if (build_context.show_timings) { + show_timings(&checker, &timings); + } if (run_output) { @@ -662,9 +684,9 @@ int main(int arg_count, char **arg_ptr) { return exit_code; } - #if defined(PRINT_TIMINGS) - timings_print_all(&timings); - #endif + if (build_context.show_timings) { + show_timings(&checker, &timings); + } if (run_output) { system_exec_command_line_app("odin run", false, "%.*s", LIT(output_base)); diff --git a/src/timings.cpp b/src/timings.cpp index 27ee1f3a4..78fdd5240 100644 --- a/src/timings.cpp +++ b/src/timings.cpp @@ -8,6 +8,7 @@ struct Timings { TimeStamp total; Array sections; 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_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), cast(int)(max_len-t->total.label.len), SPACES, - time_stamp_as_ms(t->total, t->freq)); + total_ms); for_array(i, t->sections) { 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), cast(int)(max_len-ts.label.len), SPACES, - time_stamp_as_ms(ts, t->freq)); + section_ms, 100*section_ms/total_ms); } }