From 2820bbc269bb989052163544f2764bc4287254e4 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 5 Jan 2024 13:38:30 +0000 Subject: Add `@(entry_point_only)` for procedures --- src/check_decl.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src/check_decl.cpp') diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 71b897a84..85ba4230a 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -908,6 +908,7 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { break; } + e->Procedure.entry_point_only = ac.entry_point_only; e->Procedure.is_export = ac.is_export; e->deprecated_message = ac.deprecated_message; e->warning_message = ac.warning_message; -- cgit v1.2.3 From d7d23e65eae616d44cc070edff8171eb3160b5b0 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 5 Jan 2024 13:47:00 +0000 Subject: Clean up error block usage --- src/build_settings.cpp | 1 + src/check_decl.cpp | 7 +++--- src/check_expr.cpp | 68 +++++++++++++++++++++++++++++--------------------- src/check_stmt.cpp | 6 ++--- src/check_type.cpp | 3 +-- src/checker.cpp | 6 ++--- src/main.cpp | 2 ++ 7 files changed, 50 insertions(+), 43 deletions(-) (limited to 'src/check_decl.cpp') diff --git a/src/build_settings.cpp b/src/build_settings.cpp index d91a31ff2..18ad8ac0d 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -361,6 +361,7 @@ struct BuildContext { bool ignore_warnings; bool warnings_as_errors; bool hide_error_line; + bool terse_errors; bool has_ansi_terminal_colours; bool ignore_lazy; diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 85ba4230a..8a3f89877 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -138,11 +138,10 @@ gb_internal void check_init_variables(CheckerContext *ctx, Entity **lhs, isize l } if (o->type && is_type_no_copy(o->type)) { - begin_error_block(); + ERROR_BLOCK(); if (check_no_copy_assignment(*o, str_lit("initialization"))) { error_line("\tInitialization of a #no_copy type must be either implicitly zero, a constant literal, or a return value from a call expression"); } - end_error_block(); } } if (rhs_count > 0 && lhs_count != rhs_count) { @@ -1301,8 +1300,8 @@ gb_internal void check_proc_group_decl(CheckerContext *ctx, Entity *pg_entity, D continue; } - begin_error_block(); - defer (end_error_block()); + + ERROR_BLOCK(); ProcTypeOverloadKind kind = are_proc_types_overload_safe(p->type, q->type); bool both_have_where_clauses = false; diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 14e3bc0de..8d347bec7 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -184,6 +184,8 @@ gb_internal void populate_check_did_you_mean_objc_entity(StringSet *set, Entity gb_internal void check_did_you_mean_objc_entity(String const &name, Entity *e, bool is_type, char const *prefix = "") { + if (build_context.terse_errors) { return; } + ERROR_BLOCK(); GB_ASSERT(e->kind == Entity_TypeName); GB_ASSERT(e->TypeName.objc_metadata != nullptr); @@ -204,6 +206,8 @@ gb_internal void check_did_you_mean_objc_entity(String const &name, Entity *e, b } gb_internal void check_did_you_mean_type(String const &name, Array const &fields, char const *prefix = "") { + if (build_context.terse_errors) { return; } + ERROR_BLOCK(); DidYouMeanAnswers d = did_you_mean_make(heap_allocator(), fields.count, name); @@ -217,6 +221,8 @@ gb_internal void check_did_you_mean_type(String const &name, Array con gb_internal void check_did_you_mean_type(String const &name, Slice const &fields, char const *prefix = "") { + if (build_context.terse_errors) { return; } + ERROR_BLOCK(); DidYouMeanAnswers d = did_you_mean_make(heap_allocator(), fields.count, name); @@ -229,6 +235,8 @@ gb_internal void check_did_you_mean_type(String const &name, Slice con } gb_internal void check_did_you_mean_scope(String const &name, Scope *scope, char const *prefix = "") { + if (build_context.terse_errors) { return; } + ERROR_BLOCK(); DidYouMeanAnswers d = did_you_mean_make(heap_allocator(), scope->elements.count, name); @@ -2203,7 +2211,6 @@ gb_internal bool check_is_expressible(CheckerContext *ctx, Operand *o, Type *typ ERROR_BLOCK(); - if (is_type_numeric(o->type) && is_type_numeric(type)) { if (!is_type_integer(o->type) && is_type_integer(type)) { error(o->expr, "'%s' truncated to '%s', got %s", a, b, s); @@ -2264,8 +2271,7 @@ gb_internal void check_old_for_or_switch_value_usage(Ast *expr) { if (e != nullptr && (e->flags & EntityFlag_OldForOrSwitchValue) != 0) { GB_ASSERT(e->kind == Entity_Variable); - begin_error_block(); - defer (end_error_block()); + ERROR_BLOCK(); if ((e->flags & EntityFlag_ForValue) != 0) { Type *parent_type = type_deref(e->Variable.for_loop_parent_type); @@ -2309,8 +2315,7 @@ gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast * break; default: { - begin_error_block(); - defer (end_error_block()); + ERROR_BLOCK(); error(op, "Cannot take the pointer address of '%s'", str); if (e != nullptr && (e->flags & EntityFlag_ForValue) != 0) { Type *parent_type = type_deref(e->Variable.for_loop_parent_type); @@ -3071,7 +3076,7 @@ gb_internal void check_cast(CheckerContext *c, Operand *x, Type *type) { x->mode = Addressing_Invalid; - begin_error_block(); + ERROR_BLOCK(); error(x->expr, "Cannot cast '%s' as '%s' from '%s'", expr_str, to_type, from_type); if (is_const_expr) { gbString val_str = exact_value_to_string(x->value); @@ -3094,8 +3099,6 @@ gb_internal void check_cast(CheckerContext *c, Operand *x, Type *type) { } check_cast_error_suggestion(c, x, type); - end_error_block(); - return; } @@ -4047,8 +4050,7 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar if (check_is_assignable_to(c, operand, elem)) { if (t->Matrix.row_count != t->Matrix.column_count) { operand->mode = Addressing_Invalid; - begin_error_block(); - defer (end_error_block()); + ERROR_BLOCK(); convert_untyped_error(c, operand, target_type); error_line("\tNote: Only a square matrix types can be initialized with a scalar value\n"); @@ -4109,8 +4111,7 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar target_type = t->Union.variants[first_success_index]; break; } else if (valid_count > 1) { - begin_error_block(); - defer (end_error_block()); + ERROR_BLOCK(); GB_ASSERT(first_success_index >= 0); operand->mode = Addressing_Invalid; @@ -4136,8 +4137,7 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar } else if (is_type_untyped_uninit(operand->type)) { target_type = t_untyped_uninit; } else if (!is_type_untyped_nil(operand->type) || !type_has_nil(target_type)) { - begin_error_block(); - defer (end_error_block()); + ERROR_BLOCK(); operand->mode = Addressing_Invalid; convert_untyped_error(c, operand, target_type); @@ -4714,6 +4714,7 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod entity = scope_lookup_current(import_scope, entity_name); bool allow_builtin = false; if (!is_entity_declared_for_selector(entity, import_scope, &allow_builtin)) { + ERROR_BLOCK(); error(node, "'%.*s' is not declared by '%.*s'", LIT(entity_name), LIT(import_name)); operand->mode = Addressing_Invalid; operand->expr = node; @@ -4914,6 +4915,8 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod error(op_expr, "Type '%s' has no field '%s'", op_str, sel_str); } } else { + ERROR_BLOCK(); + error(op_expr, "'%s' of type '%s' has no field '%s'", op_str, type_str, sel_str); if (operand->type != nullptr && selector->kind == Ast_Ident) { @@ -6338,8 +6341,7 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c, }; if (valids.count == 0) { - begin_error_block(); - defer (end_error_block()); + ERROR_BLOCK(); error(operand->expr, "No procedures or ambiguous call for procedure group '%s' that match with the given arguments", expr_name); if (positional_operands.count == 0 && named_operands.count == 0) { @@ -6429,8 +6431,7 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c, data.result_type = t_invalid; } else if (valids.count > 1) { - begin_error_block(); - defer (end_error_block()); + ERROR_BLOCK(); error(operand->expr, "Ambiguous procedure group call '%s' that match with the given arguments", expr_name); print_argument_types(); @@ -7649,6 +7650,8 @@ gb_internal ExprKind check_implicit_selector_expr(CheckerContext *c, Operand *o, String name = ise->selector->Ident.token.string; if (is_type_enum(th)) { + ERROR_BLOCK(); + Type *bt = base_type(th); GB_ASSERT(bt->kind == Type_Enum); @@ -9050,8 +9053,7 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast * } if (unhandled.count > 0) { - begin_error_block(); - defer (end_error_block()); + ERROR_BLOCK(); if (unhandled.count == 1) { error_no_newline(node, "Unhandled enumerated array case: %.*s", LIT(unhandled[0]->token.string)); @@ -9062,9 +9064,11 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast * error_line("\t%.*s\n", LIT(f->token.string)); } } - error_line("\n"); - error_line("\tSuggestion: Was '#partial %s{...}' wanted?\n", type_to_string(type)); + if (!build_context.terse_errors) { + error_line("\n"); + error_line("\tSuggestion: Was '#partial %s{...}' wanted?\n", type_to_string(type)); + } } } @@ -9688,7 +9692,9 @@ gb_internal ExprKind check_index_expr(CheckerContext *c, Operand *o, Ast *node, if (index < 0) { gbString str = expr_to_string(o->expr); error(o->expr, "Cannot index a constant '%s'", str); - error_line("\tSuggestion: store the constant into a variable in order to index it with a variable index\n"); + if (!build_context.terse_errors) { + error_line("\tSuggestion: store the constant into a variable in order to index it with a variable index\n"); + } gb_string_free(str); o->mode = Addressing_Invalid; o->expr = node; @@ -9702,7 +9708,9 @@ gb_internal ExprKind check_index_expr(CheckerContext *c, Operand *o, Ast *node, if (!success) { gbString str = expr_to_string(o->expr); error(o->expr, "Cannot index a constant '%s' with index %lld", str, cast(long long)index); - error_line("\tSuggestion: store the constant into a variable in order to index it with a variable index\n"); + if (!build_context.terse_errors) { + error_line("\tSuggestion: store the constant into a variable in order to index it with a variable index\n"); + } gb_string_free(str); o->mode = Addressing_Invalid; o->expr = node; @@ -9890,7 +9898,9 @@ gb_internal ExprKind check_slice_expr(CheckerContext *c, Operand *o, Ast *node, if (!all_constant) { gbString str = expr_to_string(o->expr); error(o->expr, "Cannot slice '%s' with non-constant indices", str); - error_line("\tSuggestion: store the constant into a variable in order to index it with a variable index\n"); + if (!build_context.terse_errors) { + error_line("\tSuggestion: store the constant into a variable in order to index it with a variable index\n"); + } gb_string_free(str); o->mode = Addressing_Value; // NOTE(bill): Keep subsequent values going without erring o->expr = node; @@ -10246,15 +10256,15 @@ gb_internal ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast } else { gbString str = expr_to_string(o->expr); gbString typ = type_to_string(o->type); - begin_error_block(); + ERROR_BLOCK(); error(o->expr, "Cannot dereference '%s' of type '%s'", str, typ); if (o->type && is_type_multi_pointer(o->type)) { - error_line("\tDid you mean '%s[0]'?\n", str); + if (!build_context.terse_errors) { + error_line("\tDid you mean '%s[0]'?\n", str); + } } - end_error_block(); - gb_string_free(typ); gb_string_free(str); o->mode = Addressing_Invalid; diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index b93be734e..d56e5e212 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1085,8 +1085,7 @@ gb_internal void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags } if (unhandled.count > 0) { - begin_error_block(); - defer (end_error_block()); + ERROR_BLOCK(); if (unhandled.count == 1) { error_no_newline(node, "Unhandled switch case: %.*s", LIT(unhandled[0]->token.string)); @@ -1813,7 +1812,7 @@ gb_internal void check_value_decl_stmt(CheckerContext *ctx, Ast *node, u32 mod_f } if (new_name_count == 0) { - begin_error_block(); + ERROR_BLOCK(); error(node, "No new declarations on the left hand side"); bool all_underscore = true; for (Ast *name : vd->names) { @@ -1831,7 +1830,6 @@ gb_internal void check_value_decl_stmt(CheckerContext *ctx, Ast *node, u32 mod_f error_line("\tSuggestion: Try changing the declaration (:=) to an assignment (=)\n"); } - end_error_block(); } Type *init_type = nullptr; diff --git a/src/check_type.cpp b/src/check_type.cpp index f11418dc0..a95026711 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2702,14 +2702,13 @@ gb_internal bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, T check_expr_or_type(&c, &o, pt->type); if (o.mode != Addressing_Invalid && o.mode != Addressing_Type) { // NOTE(bill): call check_type_expr again to get a consistent error message - begin_error_block(); + ERROR_BLOCK(); elem = check_type_expr(&c, pt->type, nullptr); if (o.mode == Addressing_Variable) { gbString s = expr_to_string(pt->type); error_line("\tSuggestion: ^ is used for pointer types, did you mean '&%s'?\n", s); gb_string_free(s); } - end_error_block(); } else { elem = o.type; } diff --git a/src/checker.cpp b/src/checker.cpp index ac885222d..d25acb15e 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -4025,12 +4025,11 @@ gb_internal void check_collect_value_decl(CheckerContext *c, Ast *decl) { if (c->foreign_context.default_cc > 0) { cc = c->foreign_context.default_cc; } else if (is_arch_wasm()) { - begin_error_block(); + ERROR_BLOCK(); error(init, "For wasm related targets, it is required that you either define the" " @(default_calling_convention=) on the foreign block or" " explicitly assign it on the procedure signature"); error_line("\tSuggestion: when dealing with normal Odin code (e.g. js_wasm32), use \"contextless\"; when dealing with Emscripten like code, use \"c\"\n"); - end_error_block(); } } e->Procedure.link_prefix = c->foreign_context.link_prefix; @@ -4077,8 +4076,7 @@ gb_internal void check_collect_value_decl(CheckerContext *c, Ast *decl) { if (e->kind != Entity_Procedure) { if (fl != nullptr) { - begin_error_block(); - defer (end_error_block()); + ERROR_BLOCK(); AstKind kind = init->kind; error(name, "Only procedures and variables are allowed to be in a foreign block, got %.*s", LIT(ast_strings[kind])); diff --git a/src/main.cpp b/src/main.cpp index 4d4e01ada..fd754248b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1160,10 +1160,12 @@ gb_internal bool parse_build_flags(Array args) { case BuildFlag_TerseErrors: build_context.hide_error_line = true; + build_context.terse_errors = true; break; case BuildFlag_VerboseErrors: gb_printf_err("-verbose-errors is not the default, -terse-errors can now disable it\n"); build_context.hide_error_line = false; + build_context.terse_errors = false; break; case BuildFlag_ErrorPosStyle: -- cgit v1.2.3 From aff8f06e3cb1bdb9c3ffee98a68675c843df78fd Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 7 Jan 2024 19:56:00 +0000 Subject: Add frontend stuff instrumentation tooling //+no-instrumentation @(no_instrumentation) @(instrumentation_enter) @(instrumentation_exit) --- src/check_decl.cpp | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/checker.cpp | 44 ++++++++++++++++++++++++++++++++++++ src/checker.hpp | 35 ++++++++++++++++++++--------- src/entity.cpp | 1 + src/parser.cpp | 4 +++- src/parser.hpp | 2 ++ 6 files changed, 140 insertions(+), 12 deletions(-) (limited to 'src/check_decl.cpp') diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 8a3f89877..c69d8185e 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -909,6 +909,72 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { e->Procedure.entry_point_only = ac.entry_point_only; e->Procedure.is_export = ac.is_export; + + bool no_instrumentation = false; + if (pl->body == nullptr) { + no_instrumentation = true; + if (ac.no_instrumentation != Instrumentation_Default) { + error(e->token, "@(no_instrumentation) is not allowed on foreign procedures"); + } + } else { + if (e->file) { + no_instrumentation = (e->file->flags & AstFile_NoInstrumentation) != 0; + } + + switch (ac.no_instrumentation) { + case Instrumentation_Enabled: no_instrumentation = false; break; + case Instrumentation_Default: break; + case Instrumentation_Disabled: no_instrumentation = true; break; + } + } + e->Procedure.no_instrumentation = no_instrumentation; + + auto const is_valid_instrumentation_call = [](Type *type) -> bool { + if (type == nullptr || type->kind != Type_Proc) { + return false; + } + if (type->Proc.calling_convention != ProcCC_CDecl) { + return false; + } + if (type->Proc.result_count != 0) { + return false; + } + if (type->Proc.param_count != 2) { + return false; + } + Type *p0 = type->Proc.params->Tuple.variables[0]->type; + Type *p1 = type->Proc.params->Tuple.variables[1]->type; + return is_type_rawptr(p0) && is_type_rawptr(p1); + }; + + if (ac.instrumentation_enter && ac.instrumentation_exit) { + error(e->token, "A procedure cannot be marked with both @(instrumentation_enter) and @(instrumentation_exit)"); + } else if (ac.instrumentation_enter) { + if (!is_valid_instrumentation_call(e->type)) { + gbString s = type_to_string(e->type); + error(e->token, "@(instrumentation_enter) procedures must have the type 'proc \"c\" (rawptr, rawptr)', got %s", s); + gb_string_free(s); + } + MUTEX_GUARD(&ctx->info->instrumentation_mutex); + if (ctx->info->instrumentation_enter_entity != nullptr) { + error(e->token, "@(instrumentation_enter) has already been set"); + } else { + ctx->info->instrumentation_enter_entity = e; + } + } else if (ac.instrumentation_exit) { + if (!is_valid_instrumentation_call(e->type)) { + gbString s = type_to_string(e->type); + error(e->token, "@(instrumentation_exit) procedures must have the type 'proc \"c\" (rawptr, rawptr)', got %s", s); + gb_string_free(s); + } + MUTEX_GUARD(&ctx->info->instrumentation_mutex); + if (ctx->info->instrumentation_exit_entity != nullptr) { + error(e->token, "@(instrumentation_exit) has already been set"); + } else { + ctx->info->instrumentation_exit_entity = e; + } + } + e->deprecated_message = ac.deprecated_message; e->warning_message = ac.warning_message; ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix); diff --git a/src/checker.cpp b/src/checker.cpp index d25acb15e..5e46e87fe 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -2581,6 +2581,9 @@ gb_internal void generate_minimum_dependency_set(Checker *c, Entity *start) { str_lit("multi_pointer_slice_expr_error"), ); + add_dependency_to_set(c, c->info.instrumentation_enter_entity); + add_dependency_to_set(c, c->info.instrumentation_exit_entity); + generate_minimum_dependency_set_internal(c, start); @@ -3414,8 +3417,38 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) { } return true; } else if (name == "entry_point_only") { + if (value != nullptr) { + error(value, "'%.*s' expects no parameter", LIT(name)); + } ac->entry_point_only = true; return true; + } else if (name == "no_instrumentation") { + ExactValue ev = check_decl_attribute_value(c, value); + if (ev.kind == ExactValue_Invalid) { + ac->no_instrumentation = Instrumentation_Disabled; + } else if (ev.kind == ExactValue_Bool) { + if (ev.value_bool) { + ac->no_instrumentation = Instrumentation_Disabled; + } else { + ac->no_instrumentation = Instrumentation_Enabled; + } + } else { + error(value, "Expected either a boolean or no parameter for '%.*s'", LIT(name)); + return false; + } + return true; + } else if (name == "instrumentation_enter") { + if (value != nullptr) { + error(value, "'%.*s' expects no parameter", LIT(name)); + } + ac->instrumentation_enter = true; + return true; + } else if (name == "instrumentation_exit") { + if (value != nullptr) { + error(value, "'%.*s' expects no parameter", LIT(name)); + } + ac->instrumentation_exit = true; + return true; } return false; } @@ -6216,6 +6249,17 @@ gb_internal void check_parsed_files(Checker *c) { GB_ASSERT(c->info.entity_queue.count.load(std::memory_order_relaxed) == 0); GB_ASSERT(c->info.definition_queue.count.load(std::memory_order_relaxed) == 0); + TIME_SECTION("check instrumentation calls"); + { + if ((c->info.instrumentation_enter_entity != nullptr) ^ + (c->info.instrumentation_exit_entity != nullptr)) { + Entity *e = c->info.instrumentation_enter_entity; + if (!e) e = c->info.instrumentation_exit_entity; + error(e->token, "Both @(instrumentation_enter) and @(instrumentation_exit) must be defined"); + } + } + + TIME_SECTION("add untyped expression values"); // Add untyped expression values for (UntypedExprInfo u = {}; mpsc_dequeue(&c->global_untyped_queue, &u); /**/) { diff --git a/src/checker.hpp b/src/checker.hpp index 7c399e50f..9da0f2950 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -103,6 +103,12 @@ struct DeferredProcedure { }; +enum InstrumentationFlag : i32 { + Instrumentation_Enabled = -1, + Instrumentation_Default = 0, + Instrumentation_Disabled = +1, +}; + struct AttributeContext { String link_name; String link_prefix; @@ -113,20 +119,23 @@ struct AttributeContext { String deprecated_message; String warning_message; DeferredProcedure deferred_procedure; - bool is_export : 1; - bool is_static : 1; - bool require_results : 1; - bool require_declaration : 1; - bool has_disabled_proc : 1; - bool disabled_proc : 1; - bool test : 1; - bool init : 1; - bool fini : 1; - bool set_cold : 1; - bool entry_point_only : 1; + bool is_export : 1; + bool is_static : 1; + bool require_results : 1; + bool require_declaration : 1; + bool has_disabled_proc : 1; + bool disabled_proc : 1; + bool test : 1; + bool init : 1; + bool fini : 1; + bool set_cold : 1; + bool entry_point_only : 1; + bool instrumentation_enter : 1; + bool instrumentation_exit : 1; u32 optimization_mode; // ProcedureOptimizationMode i64 foreign_import_priority_index; String extra_linker_flags; + InstrumentationFlag no_instrumentation; String objc_class; String objc_name; @@ -403,6 +412,10 @@ struct CheckerInfo { BlockingMutex all_procedures_mutex; Array all_procedures; + + BlockingMutex instrumentation_mutex; + Entity *instrumentation_enter_entity; + Entity *instrumentation_exit_entity; }; struct CheckerContext { diff --git a/src/entity.cpp b/src/entity.cpp index d0b3cf139..0539386d0 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -251,6 +251,7 @@ struct Entity { bool generated_from_polymorphic : 1; bool target_feature_disabled : 1; bool entry_point_only : 1; + bool no_instrumentation : 1; String target_feature; } Procedure; struct { diff --git a/src/parser.cpp b/src/parser.cpp index c0498b425..2671054df 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -5919,7 +5919,7 @@ gb_internal bool parse_file(Parser *p, AstFile *f) { f->vet_flags = parse_vet_tag(tok, lc); f->vet_flags_set = true; } else if (string_starts_with(lc, str_lit("+ignore"))) { - return false; + return false; } else if (string_starts_with(lc, str_lit("+private"))) { f->flags |= AstFile_IsPrivatePkg; String command = string_trim_starts_with(lc, str_lit("+private ")); @@ -5941,6 +5941,8 @@ gb_internal bool parse_file(Parser *p, AstFile *f) { } else { f->flags |= AstFile_IsLazy; } + } else if (lc == "+no-instrumentation") { + f->flags |= AstFile_NoInstrumentation; } else { warning(tok, "Ignoring unknown tag '%.*s'", LIT(lc)); } diff --git a/src/parser.hpp b/src/parser.hpp index bce818652..cc1836ef3 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -76,6 +76,8 @@ enum AstFileFlag : u32 { AstFile_IsTest = 1<<3, AstFile_IsLazy = 1<<4, + + AstFile_NoInstrumentation = 1<<5, }; enum AstDelayQueueKind { -- cgit v1.2.3 From f4782157d3b586a88983d3b770c2c0eeb17946d1 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 7 Jan 2024 21:34:44 +0000 Subject: Implement instrumentation pass --- core/runtime/core.odin | 1 + core/runtime/entry_unix.odin | 1 + core/runtime/entry_wasm.odin | 1 + core/runtime/entry_windows.odin | 1 + core/runtime/error_checks.odin | 24 ++++++----- core/runtime/procs_windows_amd64.odin | 1 + core/runtime/procs_windows_i386.odin | 1 + src/check_decl.cpp | 26 ++++++++---- src/entity.cpp | 2 +- src/llvm_backend.cpp | 2 - src/llvm_backend.hpp | 2 +- src/llvm_backend_general.cpp | 13 ++++++ src/llvm_backend_opt.cpp | 77 +++++++++++++++++++++++++++++++++++ src/llvm_backend_proc.cpp | 12 ++++++ 14 files changed, 143 insertions(+), 21 deletions(-) (limited to 'src/check_decl.cpp') diff --git a/core/runtime/core.odin b/core/runtime/core.odin index 2d176f909..e12d9a43d 100644 --- a/core/runtime/core.odin +++ b/core/runtime/core.odin @@ -18,6 +18,7 @@ // This could change at a later date if the all these data structures are // implemented within the compiler rather than in this "preload" file // +//+no-instrumentation package runtime import "core:intrinsics" diff --git a/core/runtime/entry_unix.odin b/core/runtime/entry_unix.odin index 78e545c22..f494a509e 100644 --- a/core/runtime/entry_unix.odin +++ b/core/runtime/entry_unix.odin @@ -1,5 +1,6 @@ //+private //+build linux, darwin, freebsd, openbsd +//+no-instrumentation package runtime import "core:intrinsics" diff --git a/core/runtime/entry_wasm.odin b/core/runtime/entry_wasm.odin index 235d5611b..e7f3f156f 100644 --- a/core/runtime/entry_wasm.odin +++ b/core/runtime/entry_wasm.odin @@ -1,5 +1,6 @@ //+private //+build wasm32, wasm64p32 +//+no-instrumentation package runtime import "core:intrinsics" diff --git a/core/runtime/entry_windows.odin b/core/runtime/entry_windows.odin index a315c1209..277daecca 100644 --- a/core/runtime/entry_windows.odin +++ b/core/runtime/entry_windows.odin @@ -1,5 +1,6 @@ //+private //+build windows +//+no-instrumentation package runtime import "core:intrinsics" diff --git a/core/runtime/error_checks.odin b/core/runtime/error_checks.odin index 9d484979a..ea6333c29 100644 --- a/core/runtime/error_checks.odin +++ b/core/runtime/error_checks.odin @@ -1,5 +1,6 @@ package runtime +@(no_instrumentation) bounds_trap :: proc "contextless" () -> ! { when ODIN_OS == .Windows { windows_trap_array_bounds() @@ -8,6 +9,7 @@ bounds_trap :: proc "contextless" () -> ! { } } +@(no_instrumentation) type_assertion_trap :: proc "contextless" () -> ! { when ODIN_OS == .Windows { windows_trap_type_assertion() @@ -21,7 +23,7 @@ bounds_check_error :: proc "contextless" (file: string, line, column: i32, index if uint(index) < uint(count) { return } - @(cold) + @(cold, no_instrumentation) handle_error :: proc "contextless" (file: string, line, column: i32, index, count: int) -> ! { print_caller_location(Source_Code_Location{file, line, column, ""}) print_string(" Index ") @@ -34,6 +36,7 @@ bounds_check_error :: proc "contextless" (file: string, line, column: i32, index handle_error(file, line, column, index, count) } +@(no_instrumentation) slice_handle_error :: proc "contextless" (file: string, line, column: i32, lo, hi: int, len: int) -> ! { print_caller_location(Source_Code_Location{file, line, column, ""}) print_string(" Invalid slice indices ") @@ -46,6 +49,7 @@ slice_handle_error :: proc "contextless" (file: string, line, column: i32, lo, h bounds_trap() } +@(no_instrumentation) multi_pointer_slice_handle_error :: proc "contextless" (file: string, line, column: i32, lo, hi: int) -> ! { print_caller_location(Source_Code_Location{file, line, column, ""}) print_string(" Invalid slice indices ") @@ -82,7 +86,7 @@ dynamic_array_expr_error :: proc "contextless" (file: string, line, column: i32, if 0 <= low && low <= high && high <= max { return } - @(cold) + @(cold, no_instrumentation) handle_error :: proc "contextless" (file: string, line, column: i32, low, high, max: int) -> ! { print_caller_location(Source_Code_Location{file, line, column, ""}) print_string(" Invalid dynamic array indices ") @@ -103,7 +107,7 @@ matrix_bounds_check_error :: proc "contextless" (file: string, line, column: i32 uint(column_index) < uint(column_count) { return } - @(cold) + @(cold, no_instrumentation) handle_error :: proc "contextless" (file: string, line, column: i32, row_index, column_index, row_count, column_count: int) -> ! { print_caller_location(Source_Code_Location{file, line, column, ""}) print_string(" Matrix indices [") @@ -127,7 +131,7 @@ when ODIN_NO_RTTI { if ok { return } - @(cold) + @(cold, no_instrumentation) handle_error :: proc "contextless" (file: string, line, column: i32) -> ! { print_caller_location(Source_Code_Location{file, line, column, ""}) print_string(" Invalid type assertion\n") @@ -140,7 +144,7 @@ when ODIN_NO_RTTI { if ok { return } - @(cold) + @(cold, no_instrumentation) handle_error :: proc "contextless" (file: string, line, column: i32) -> ! { print_caller_location(Source_Code_Location{file, line, column, ""}) print_string(" Invalid type assertion\n") @@ -153,7 +157,7 @@ when ODIN_NO_RTTI { if ok { return } - @(cold) + @(cold, no_instrumentation) handle_error :: proc "contextless" (file: string, line, column: i32, from, to: typeid) -> ! { print_caller_location(Source_Code_Location{file, line, column, ""}) print_string(" Invalid type assertion from ") @@ -198,7 +202,7 @@ when ODIN_NO_RTTI { return id } - @(cold) + @(cold, no_instrumentation) handle_error :: proc "contextless" (file: string, line, column: i32, from, to: typeid, from_data: rawptr) -> ! { actual := variant_type(from, from_data) @@ -224,7 +228,7 @@ make_slice_error_loc :: #force_inline proc "contextless" (loc := #caller_locatio if 0 <= len { return } - @(cold) + @(cold, no_instrumentation) handle_error :: proc "contextless" (loc: Source_Code_Location, len: int) -> ! { print_caller_location(loc) print_string(" Invalid slice length for make: ") @@ -239,7 +243,7 @@ make_dynamic_array_error_loc :: #force_inline proc "contextless" (loc := #caller if 0 <= len && len <= cap { return } - @(cold) + @(cold, no_instrumentation) handle_error :: proc "contextless" (loc: Source_Code_Location, len, cap: int) -> ! { print_caller_location(loc) print_string(" Invalid dynamic array parameters for make: ") @@ -256,7 +260,7 @@ make_map_expr_error_loc :: #force_inline proc "contextless" (loc := #caller_loca if 0 <= cap { return } - @(cold) + @(cold, no_instrumentation) handle_error :: proc "contextless" (loc: Source_Code_Location, cap: int) -> ! { print_caller_location(loc) print_string(" Invalid map capacity for make: ") diff --git a/core/runtime/procs_windows_amd64.odin b/core/runtime/procs_windows_amd64.odin index e430357be..a30985d5c 100644 --- a/core/runtime/procs_windows_amd64.odin +++ b/core/runtime/procs_windows_amd64.odin @@ -1,4 +1,5 @@ //+private +//+no-instrumentation package runtime foreign import kernel32 "system:Kernel32.lib" diff --git a/core/runtime/procs_windows_i386.odin b/core/runtime/procs_windows_i386.odin index f810197f1..4f606da8f 100644 --- a/core/runtime/procs_windows_i386.odin +++ b/core/runtime/procs_windows_i386.odin @@ -1,4 +1,5 @@ //+private +//+no-instrumentation package runtime @require foreign import "system:int64.lib" diff --git a/src/check_decl.cpp b/src/check_decl.cpp index c69d8185e..a530945f9 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -910,24 +910,24 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { e->Procedure.entry_point_only = ac.entry_point_only; e->Procedure.is_export = ac.is_export; - bool no_instrumentation = false; + bool has_instrumentation = false; if (pl->body == nullptr) { - no_instrumentation = true; + has_instrumentation = false; if (ac.no_instrumentation != Instrumentation_Default) { error(e->token, "@(no_instrumentation) is not allowed on foreign procedures"); } } else { - if (e->file) { - no_instrumentation = (e->file->flags & AstFile_NoInstrumentation) != 0; + AstFile *file = e->token.pos.file_id ? global_files[e->token.pos.file_id] : nullptr; + if (file) { + has_instrumentation = (file->flags & AstFile_NoInstrumentation) == 0; } switch (ac.no_instrumentation) { - case Instrumentation_Enabled: no_instrumentation = false; break; + case Instrumentation_Enabled: has_instrumentation = true; break; case Instrumentation_Default: break; - case Instrumentation_Disabled: no_instrumentation = true; break; + case Instrumentation_Disabled: has_instrumentation = false; break; } } - e->Procedure.no_instrumentation = no_instrumentation; auto const is_valid_instrumentation_call = [](Type *type) -> bool { if (type == nullptr || type->kind != Type_Proc) { @@ -949,6 +949,9 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { if (ac.instrumentation_enter && ac.instrumentation_exit) { error(e->token, "A procedure cannot be marked with both @(instrumentation_enter) and @(instrumentation_exit)"); + + has_instrumentation = false; + e->flags |= EntityFlag_Require; } else if (ac.instrumentation_enter) { if (!is_valid_instrumentation_call(e->type)) { gbString s = type_to_string(e->type); @@ -961,6 +964,9 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { } else { ctx->info->instrumentation_enter_entity = e; } + + has_instrumentation = false; + e->flags |= EntityFlag_Require; } else if (ac.instrumentation_exit) { if (!is_valid_instrumentation_call(e->type)) { gbString s = type_to_string(e->type); @@ -973,8 +979,14 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { } else { ctx->info->instrumentation_exit_entity = e; } + + has_instrumentation = false; + e->flags |= EntityFlag_Require; } + e->Procedure.has_instrumentation = has_instrumentation; + + e->deprecated_message = ac.deprecated_message; e->warning_message = ac.warning_message; ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix); diff --git a/src/entity.cpp b/src/entity.cpp index 0539386d0..e6c46d37e 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -251,7 +251,7 @@ struct Entity { bool generated_from_polymorphic : 1; bool target_feature_disabled : 1; bool entry_point_only : 1; - bool no_instrumentation : 1; + bool has_instrumentation : 1; String target_feature; } Procedure; struct { diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index b90fd8495..0175d039e 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1497,8 +1497,6 @@ gb_internal WORKER_TASK_PROC(lb_llvm_module_pass_worker_proc) { auto passes = array_make(heap_allocator(), 0, 64); defer (array_free(&passes)); - - LLVMPassBuilderOptionsRef pb_options = LLVMCreatePassBuilderOptions(); defer (LLVMDisposePassBuilderOptions(pb_options)); diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 4e193bcea..b645d66d6 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -563,7 +563,7 @@ gb_internal LLVMTypeRef OdinLLVMGetVectorElementType(LLVMTypeRef type); gb_internal String lb_filepath_ll_for_module(lbModule *m); - +gb_internal LLVMTypeRef lb_type_internal_for_procedures_raw(lbModule *m, Type *type); gb_internal LLVMTypeRef llvm_array_type(LLVMTypeRef ElementType, uint64_t ElementCount) { #if LB_USE_NEW_PASS_SYSTEM diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 54327cc54..f0f5327c6 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -2348,6 +2348,15 @@ gb_internal LLVMAttributeRef lb_create_enum_attribute(LLVMContextRef ctx, char c return LLVMCreateEnumAttribute(ctx, kind, value); } +gb_internal LLVMAttributeRef lb_create_string_attribute(LLVMContextRef ctx, String const &key, String const &value) { + LLVMAttributeRef attr = LLVMCreateStringAttribute( + ctx, + cast(char const *)key.text, cast(unsigned)key.len, + cast(char const *)value.text, cast(unsigned)value.len); + return attr; +} + + gb_internal void lb_add_proc_attribute_at_index(lbProcedure *p, isize index, char const *name, u64 value) { LLVMAttributeRef attr = lb_create_enum_attribute(p->module->ctx, name, value); GB_ASSERT(attr != nullptr); @@ -2361,6 +2370,10 @@ gb_internal void lb_add_proc_attribute_at_index(lbProcedure *p, isize index, cha gb_internal void lb_add_attribute_to_proc(lbModule *m, LLVMValueRef proc_value, char const *name, u64 value=0) { LLVMAddAttributeAtIndex(proc_value, LLVMAttributeIndex_FunctionIndex, lb_create_enum_attribute(m->ctx, name, value)); } +gb_internal void lb_add_attribute_to_proc_with_string(lbModule *m, LLVMValueRef proc_value, String const &name, String const &value) { + LLVMAttributeRef attr = lb_create_string_attribute(m->ctx, name, value); + LLVMAddAttributeAtIndex(proc_value, LLVMAttributeIndex_FunctionIndex, attr); +} diff --git a/src/llvm_backend_opt.cpp b/src/llvm_backend_opt.cpp index 2e03b7974..d5487d259 100644 --- a/src/llvm_backend_opt.cpp +++ b/src/llvm_backend_opt.cpp @@ -380,6 +380,80 @@ gb_internal void lb_run_remove_dead_instruction_pass(lbProcedure *p) { } } +gb_internal LLVMValueRef lb_run_instrumentation_pass_insert_call(lbProcedure *p, Entity *entity, LLVMBuilderRef dummy_builder) { + lbModule *m = p->module; + + lbValue cc = lb_find_procedure_value_from_entity(m, entity); + + LLVMValueRef args[2] = {}; + args[0] = p->value; + + LLVMValueRef returnaddress_args[1] = {}; + + returnaddress_args[0] = LLVMConstInt(LLVMInt32TypeInContext(m->ctx), 0, false); + + char const *instrinsic_name = "llvm.returnaddress"; + unsigned id = LLVMLookupIntrinsicID(instrinsic_name, gb_strlen(instrinsic_name)); + GB_ASSERT_MSG(id != 0, "Unable to find %s", instrinsic_name); + LLVMValueRef ip = LLVMGetIntrinsicDeclaration(m->mod, id, nullptr, 0); + LLVMTypeRef call_type = LLVMIntrinsicGetType(m->ctx, id, nullptr, 0); + args[1] = LLVMBuildCall2(dummy_builder, call_type, ip, returnaddress_args, gb_count_of(returnaddress_args), ""); + + LLVMTypeRef fnp = lb_type_internal_for_procedures_raw(p->module, entity->type); + return LLVMBuildCall2(dummy_builder, fnp, cc.value, args, 2, ""); +} + + +gb_internal void lb_run_instrumentation_pass(lbProcedure *p) { + lbModule *m = p->module; + Entity *enter = m->info->instrumentation_enter_entity; + Entity *exit = m->info->instrumentation_exit_entity; + if (enter == nullptr || exit == nullptr) { + return; + } + if (!(p->entity && + p->entity->kind == Entity_Procedure && + p->entity->Procedure.has_instrumentation)) { + return; + } + +#define LLVM_V_NAME(x) x, cast(unsigned)(gb_count_of(x)-1) + + LLVMBuilderRef dummy_builder = LLVMCreateBuilderInContext(m->ctx); + defer (LLVMDisposeBuilder(dummy_builder)); + + LLVMBasicBlockRef entry_bb = p->entry_block->block; + LLVMPositionBuilder(dummy_builder, entry_bb, LLVMGetFirstInstruction(entry_bb)); + lb_run_instrumentation_pass_insert_call(p, enter, dummy_builder); + LLVMRemoveStringAttributeAtIndex(p->value, LLVMAttributeIndex_FunctionIndex, LLVM_V_NAME("instrument-function-entry")); + + unsigned bb_count = LLVMCountBasicBlocks(p->value); + LLVMBasicBlockRef *bbs = gb_alloc_array(temporary_allocator(), LLVMBasicBlockRef, bb_count); + LLVMGetBasicBlocks(p->value, bbs); + for (unsigned i = 0; i < bb_count; i++) { + LLVMBasicBlockRef bb = bbs[i]; + LLVMValueRef terminator = LLVMGetBasicBlockTerminator(bb); + if (terminator == nullptr || + !LLVMIsAReturnInst(terminator)) { + continue; + } + + // TODO(bill): getTerminatingMustTailCall() + // If T is preceded by a musttail call, that's the real terminator. + // if (CallInst *CI = BB.getTerminatingMustTailCall()) + // T = CI; + + + LLVMPositionBuilderBefore(dummy_builder, terminator); + lb_run_instrumentation_pass_insert_call(p, exit, dummy_builder); + } + + LLVMRemoveStringAttributeAtIndex(p->value, LLVMAttributeIndex_FunctionIndex, LLVM_V_NAME("instrument-function-exit")); + +#undef LLVM_V_NAME +} + + gb_internal void lb_run_function_pass_manager(LLVMPassManagerRef fpm, lbProcedure *p, lbFunctionPassManagerKind pass_manager_kind) { if (p == nullptr) { @@ -401,6 +475,7 @@ gb_internal void lb_run_function_pass_manager(LLVMPassManagerRef fpm, lbProcedur } break; } + lb_run_instrumentation_pass(p); LLVMRunFunctionPassManager(fpm, p->value); } @@ -552,3 +627,5 @@ gb_internal void lb_run_remove_unused_globals_pass(lbModule *m) { } } } + + diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index d4eae84bc..09bebd0cf 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -329,6 +329,18 @@ gb_internal lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool i } } + if (p->body && entity->Procedure.has_instrumentation) { + Entity *instrumentation_enter = m->info->instrumentation_enter_entity; + Entity *instrumentation_exit = m->info->instrumentation_exit_entity; + if (instrumentation_enter && instrumentation_exit) { + String enter = lb_get_entity_name(m, instrumentation_enter); + String exit = lb_get_entity_name(m, instrumentation_exit); + + lb_add_attribute_to_proc_with_string(m, p->value, make_string_c("instrument-function-entry"), enter); + lb_add_attribute_to_proc_with_string(m, p->value, make_string_c("instrument-function-exit"), exit); + } + } + lbValue proc_value = {p->value, p->type}; lb_add_entity(m, entity, proc_value); lb_add_member(m, p->name, proc_value); -- cgit v1.2.3 From 67dcd916e8f7b1badef5c6beee621557b069db3c Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 9 Jan 2024 11:01:18 +0000 Subject: Update instrumentation signature to support `runtime.Source_Code_Location` as last parameter. --- src/check_decl.cpp | 15 ++++++++++----- src/llvm_backend.hpp | 2 ++ src/llvm_backend_opt.cpp | 10 ++++++++-- 3 files changed, 20 insertions(+), 7 deletions(-) (limited to 'src/check_decl.cpp') diff --git a/src/check_decl.cpp b/src/check_decl.cpp index a530945f9..ed3a109c2 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -933,20 +933,23 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { if (type == nullptr || type->kind != Type_Proc) { return false; } - if (type->Proc.calling_convention != ProcCC_CDecl) { + if (type->Proc.calling_convention != ProcCC_Contextless) { return false; } if (type->Proc.result_count != 0) { return false; } - if (type->Proc.param_count != 2) { + if (type->Proc.param_count != 3) { return false; } Type *p0 = type->Proc.params->Tuple.variables[0]->type; Type *p1 = type->Proc.params->Tuple.variables[1]->type; - return is_type_rawptr(p0) && is_type_rawptr(p1); + Type *p3 = type->Proc.params->Tuple.variables[2]->type; + return is_type_rawptr(p0) && is_type_rawptr(p1) && are_types_identical(p3, t_source_code_location); }; + static char const *instrumentation_proc_type_str = "proc \"contextless\" (proc_address: rawptr, call_site_return_address: rawptr, loc: runtime.Source_Code_Location)"; + if (ac.instrumentation_enter && ac.instrumentation_exit) { error(e->token, "A procedure cannot be marked with both @(instrumentation_enter) and @(instrumentation_exit)"); @@ -954,8 +957,9 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { e->flags |= EntityFlag_Require; } else if (ac.instrumentation_enter) { if (!is_valid_instrumentation_call(e->type)) { + init_core_source_code_location(ctx->checker); gbString s = type_to_string(e->type); - error(e->token, "@(instrumentation_enter) procedures must have the type 'proc \"c\" (rawptr, rawptr)', got %s", s); + error(e->token, "@(instrumentation_enter) procedures must have the type '%s', got %s", instrumentation_proc_type_str, s); gb_string_free(s); } MUTEX_GUARD(&ctx->info->instrumentation_mutex); @@ -968,9 +972,10 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { has_instrumentation = false; e->flags |= EntityFlag_Require; } else if (ac.instrumentation_exit) { + init_core_source_code_location(ctx->checker); if (!is_valid_instrumentation_call(e->type)) { gbString s = type_to_string(e->type); - error(e->token, "@(instrumentation_exit) procedures must have the type 'proc \"c\" (rawptr, rawptr)', got %s", s); + error(e->token, "@(instrumentation_exit) procedures must have the type '%s', got %s", instrumentation_proc_type_str, s); gb_string_free(s); } MUTEX_GUARD(&ctx->info->instrumentation_mutex); diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index b645d66d6..fe2c2deba 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -565,6 +565,8 @@ gb_internal String lb_filepath_ll_for_module(lbModule *m); gb_internal LLVMTypeRef lb_type_internal_for_procedures_raw(lbModule *m, Type *type); +gb_internal lbValue lb_emit_source_code_location_as_global_ptr(lbProcedure *p, String const &procedure, TokenPos const &pos); + gb_internal LLVMTypeRef llvm_array_type(LLVMTypeRef ElementType, uint64_t ElementCount) { #if LB_USE_NEW_PASS_SYSTEM return LLVMArrayType2(ElementType, ElementCount); diff --git a/src/llvm_backend_opt.cpp b/src/llvm_backend_opt.cpp index d5487d259..b57e74799 100644 --- a/src/llvm_backend_opt.cpp +++ b/src/llvm_backend_opt.cpp @@ -385,7 +385,7 @@ gb_internal LLVMValueRef lb_run_instrumentation_pass_insert_call(lbProcedure *p, lbValue cc = lb_find_procedure_value_from_entity(m, entity); - LLVMValueRef args[2] = {}; + LLVMValueRef args[3] = {}; args[0] = p->value; LLVMValueRef returnaddress_args[1] = {}; @@ -399,8 +399,14 @@ gb_internal LLVMValueRef lb_run_instrumentation_pass_insert_call(lbProcedure *p, LLVMTypeRef call_type = LLVMIntrinsicGetType(m->ctx, id, nullptr, 0); args[1] = LLVMBuildCall2(dummy_builder, call_type, ip, returnaddress_args, gb_count_of(returnaddress_args), ""); + Token name = {}; + if (p->entity) { + name = p->entity->token; + } + args[2] = lb_emit_source_code_location_as_global_ptr(p, name.string, name.pos).value; + LLVMTypeRef fnp = lb_type_internal_for_procedures_raw(p->module, entity->type); - return LLVMBuildCall2(dummy_builder, fnp, cc.value, args, 2, ""); + return LLVMBuildCall2(dummy_builder, fnp, cc.value, args, gb_count_of(args), ""); } -- cgit v1.2.3