diff --git a/src/check_escape.cpp b/src/check_escape.cpp index d8cd078d5..0180ccda7 100644 --- a/src/check_escape.cpp +++ b/src/check_escape.cpp @@ -152,6 +152,38 @@ struct EscapeNilUse { bool nil; }; +// The procedures are analysed in the strongly connected components of what they may call, callees first, +// and those calling each other until what flows through them settles, so what is found never depends on the threads +struct EscapeGraph { + Array procs; // a procedure's index is kept on its decl, see `DeclInfo::escape_index` + Array offsets; // procedure -> the procedures it may call, as `targets[offsets[v].. targets; + Array caller_offsets; // procedure -> the procedures which may call it, as `callers[caller_offsets[v].. callers; + Array group_of; + Array group_offsets; // group -> its procedures, as `members[group_offsets[gi].. members; // every group a group may call has a lower index + Slice > pending; // per group its calls to procedures of other groups which are not analysed yet + Slice > cursors; // per procedure while `targets` is filled + bool threaded; +}; + +struct EscapeMemberTask { + EscapeGraph * graph; + i32 v; + bool stale; // as the flows of one it calls changed + Slice flows; + Array reports; // of its latest analysis + Array callees; // the members of the group it calls, by their index + Array callers; +}; + +// of a walk through the members of a group calling each other +struct EscapeOrderFrame { + i32 k; + isize next; +}; + struct EscapeAnalysis { TypeProc * pt; Entity * variadic; @@ -162,16 +194,35 @@ struct EscapeAnalysis { Array aliases; Array reported; Array flows; - struct EscapeGraph * graph; - i32 group; // of the procedures being analysed together, see `EscapeGraph` - Array *reports; // kept rather than reported, while what flows through the group may still change - bool muted; // the entry point of an executable, which only returns as the program ends + EscapeGraph * graph; + i32 group; // of the procedures being analysed together, see `EscapeGraph` + Array *reports; // kept rather than reported, while what flows through the group may still change + bool muted; // the entry point of an executable, which only returns as the program ends bool nil_deref; // -vet-nil-deref Array nil_uses; Array nil_escaped; // locals whose address was taken, which anything may change from then on }; +gb_global EscapeGraph escape_graph; + + +gb_internal EscapeValue escape_expr (EscapeAnalysis *ea, Ast *expr); +gb_internal EscapeValue escape_addr (EscapeAnalysis *ea, Ast *expr); +gb_internal EscapeValue escape_ident_value (EscapeAnalysis *ea, Entity *e); +gb_internal Array escape_call (EscapeAnalysis *ea, Ast *call); +gb_internal Ast * escape_call_of (Ast *expr); +gb_internal void escape_store_through (EscapeAnalysis *ea, Ast *node, EscapeValue const &ptr, EscapeValue const &v, EscapeUpdateKind update); +gb_internal bool escape_is_explicit_unsafe_conversion(Type *t); +gb_internal void escape_nil_scan (EscapeAnalysis *ea, Ast *expr); +gb_internal void escape_stmt (EscapeAnalysis *ea, Ast *node); +gb_internal void escape_branch (EscapeAnalysis *ea, Ast *label, TokenKind kind); +gb_internal void escape_exit (EscapeAnalysis *ea, Ast *node, Slice const &results, Array const &values); + +// see checker_global.cpp +gb_internal i32 global_graph_scc(i32 node_count, Array const &offsets, Array const &targets, Array *comp_of_); +gb_internal void global_graph_csr(i32 node_count, Array const &edge_from, Array const &edge_to, Array *offsets, Array *targets); + gb_internal bool escape_path_has_prefix(EscapePath const &p, EscapePath const &prefix) { if (prefix.count > p.count) { @@ -190,15 +241,6 @@ gb_internal bool escape_path_eq(EscapePath const &a, EscapePath const &b) { return a.count == b.count && escape_path_has_prefix(a, b); } -gb_internal bool escape_path_has(EscapePath const &p, EscapeStepKind kind) { - for (EscapeStep const &step : p) { - if (step.kind == kind) { - return true; - } - } - return false; -} - gb_internal EscapePath escape_path_of(EscapeStepKind kind, Entity *field=nullptr) { auto p = slice_make(temporary_allocator(), 1); p[0] = {kind, field}; @@ -618,48 +660,13 @@ gb_internal Type *escape_pointee_type(Type *t) { return nullptr; } -// whether what a value of the type points to cannot hold pointers, e.g. the bytes of a `[]u8` -gb_internal bool escape_points_to_plain(Type *t) { - Type *pointee = escape_pointee_type(t); - return pointee != nullptr && !escape_type_has_pointers(pointee); -} - -// a store to a field overwrites only that part of a fact for more of the value, so it is kept for the other fields -gb_internal bool escape_split_fact(EscapeFact const &f, Type *type, EscapePath const &path, Array *pieces) { - auto split = array_make(temporary_allocator(), 0, 0); - Type *t = type; - for_array(i, path) { - EscapeStep const &step = path[i]; - Type *bt = base_type(t); - if (step.kind != EscapeStep_Field || bt == nullptr || bt->kind != Type_Struct) { - return false; - } - bool is_own_field = false; - for (Entity *field : bt->Struct.fields) { - is_own_field |= field == step.field; - } - if (!is_own_field) { - // e.g. a field reached through 'using' - return false; - } - - if (i >= f.path.count) { - for (Entity *field : bt->Struct.fields) { - if (field == step.field || !escape_type_has_pointers(field->type)) { - continue; - } - EscapePath piece = escape_path_concat(slice(path, 0, i), escape_path_of(EscapeStep_Field, field)); - array_add(&split, EscapeFact{f.obj, piece, escape_origin_within(f.origin, piece, f.path.count)}); - } - } - t = step.field->type; - } - array_add_elems(pieces, split.data, split.count); - return true; -} - gb_internal void escape_store_obj(EscapeAnalysis *ea, EscapeObject const &obj, EscapePath const &path, EscapeValue const &v, EscapeUpdateKind update) { - if (update == EscapeUpdate_Replace && !escape_path_has(path, EscapeStep_AnyElement)) { + // a store to any element of an array leaves the others + bool replaces = update == EscapeUpdate_Replace; + for (EscapeStep const &step : path) { + replaces &= step.kind != EscapeStep_AnyElement; + } + if (replaces) { Type *type = escape_type_at(obj, {}); auto pieces = array_make(temporary_allocator(), 0, 0); for (isize i = ea->state.facts.count-1; i >= 0; i--) { @@ -669,7 +676,43 @@ gb_internal void escape_store_obj(EscapeAnalysis *ea, EscapeObject const &obj, E } if (escape_path_has_prefix(f.path, path)) { array_unordered_remove(&ea->state.facts, i); - } else if (escape_path_has_prefix(path, f.path) && escape_split_fact(f, type, path, &pieces)) { + continue; + } + if (!escape_path_has_prefix(path, f.path)) { + continue; + } + + // a store to a field overwrites only that part of a fact for more of the value, so it is kept for the other fields + auto split = array_make(temporary_allocator(), 0, 0); + bool splits = true; + Type *t = type; + for (isize j = 0; j < path.count; j++) { + EscapeStep const &step = path[j]; + Type *bt = base_type(t); + // through the fields of structs, not e.g. a field reached through 'using' + splits = false; + if (step.kind == EscapeStep_Field && bt != nullptr && bt->kind == Type_Struct) { + for (Entity *field : bt->Struct.fields) { + splits |= field == step.field; + } + } + if (!splits) { + break; + } + + if (j >= f.path.count) { + for (Entity *field : bt->Struct.fields) { + if (field == step.field || !escape_type_has_pointers(field->type)) { + continue; + } + EscapePath piece = escape_path_concat(slice(path, 0, j), escape_path_of(EscapeStep_Field, field)); + array_add(&split, EscapeFact{f.obj, piece, escape_origin_within(f.origin, piece, f.path.count)}); + } + } + t = step.field->type; + } + if (splits) { + array_add_elems(&pieces, split.data, split.count); array_unordered_remove(&ea->state.facts, i); } } @@ -743,13 +786,6 @@ gb_internal EscapeValue escape_load(EscapeAnalysis *ea, EscapeValue const &ptr, return v; } -gb_internal EscapeValue escape_expr(EscapeAnalysis *ea, Ast *expr); -gb_internal EscapeValue escape_addr(EscapeAnalysis *ea, Ast *expr); - -gb_internal Array escape_call(EscapeAnalysis *ea, Ast *call); -gb_internal Ast *escape_call_of(Ast *expr); -gb_internal void escape_store_through(EscapeAnalysis *ea, Ast *node, EscapeValue const &ptr, EscapeValue const &v, EscapeUpdateKind update); - gb_internal void escape_visit_exits(EscapeAnalysis *ea, Ast *expr) { if (expr == nullptr) { return; @@ -858,10 +894,6 @@ gb_internal EscapeValue escape_elems_of(EscapeAnalysis *ea, Ast *x) { return escape_elems_at(ea, escape_addr(ea, x), t); } -gb_internal void escape_stmt (EscapeAnalysis *ea, Ast *node); -gb_internal void escape_branch(EscapeAnalysis *ea, Ast *label, TokenKind kind); -gb_internal void escape_exit (EscapeAnalysis *ea, Ast *node, Slice const &results, Array const &values); - gb_internal bool escape_selector_path(Ast *se_node, EscapePath *path) { ast_node(se, SelectorExpr, se_node); if (se->swizzle_count > 0 || se->is_bit_field) { @@ -1128,8 +1160,6 @@ gb_internal void escape_nil_restore(EscapeAnalysis *ea, Array const & } } -gb_internal bool escape_is_explicit_unsafe_conversion(Type *t); - // the paths within the value of an expression which are nil on every path reaching here gb_internal Array escape_nil_paths(EscapeAnalysis *ea, Ast *expr) { auto paths = array_make(temporary_allocator(), 0, 0); @@ -1157,25 +1187,21 @@ gb_internal Array escape_nil_paths(EscapeAnalysis *ea, Ast *expr) { return paths; } auto values = array_make(temporary_allocator(), t->Struct.fields.count); - for (Ast *&value : values) { - value = nullptr; - } for_array(i, cl->elems) { Ast *elem = cl->elems[i]; - isize index = -1; - if (elem->kind != Ast_FieldValue) { - index = i; - } else if (elem->FieldValue.field->kind == Ast_Ident) { + isize index = i; + bool found = elem->kind != Ast_FieldValue && i < values.count; + if (elem->kind == Ast_FieldValue && elem->FieldValue.field->kind == Ast_Ident) { for_array(j, t->Struct.fields) { if (t->Struct.fields[j]->token.string == elem->FieldValue.field->Ident.token.string) { index = j; + found = true; } } elem = elem->FieldValue.value; } - if (index < 0 || index >= values.count) { + if (!found) { // e.g. a field reached through 'using' - array_clear(&paths); return paths; } values[index] = elem; @@ -1331,8 +1357,6 @@ gb_internal void escape_nil_use(EscapeAnalysis *ea, Ast *ptr, EscapeReportKind k array_add(&ea->nil_uses, EscapeNilUse{ptr, kind, nil}); } -gb_internal void escape_nil_scan(EscapeAnalysis *ea, Ast *expr); - // the address of what an expression is, which only loads what it is reached through gb_internal void escape_nil_scan_addr(EscapeAnalysis *ea, Ast *expr) { expr = unparen_expr(expr); @@ -1564,8 +1588,6 @@ gb_internal EscapeValue escape_convert(EscapeAnalysis *ea, EscapeValue const &v, return escape_value_of(EscapeOrigin_Temp, {nullptr, expr}, {}); } -gb_internal EscapeValue escape_ident_value(EscapeAnalysis *ea, Entity *e); - gb_internal EscapeValue escape_ident_addr(EscapeAnalysis *ea, Entity *e) { if (e == nullptr || e->kind != Entity_Variable) { return escape_value(); @@ -1712,8 +1734,6 @@ gb_internal bool escape_is_explicit_unsafe_conversion(Type *t) { return t != nullptr && (is_type_rawptr(t) || is_type_uintptr(t)); } -gb_internal Slice escape_flows_of(EscapeAnalysis *ea, Entity *e); - // loading through the pointers once, as the callee does, where a dynamic array or map holds the pointer to its buffer gb_internal EscapeValue escape_load_once(EscapeAnalysis *ea, EscapeValue const &ptr) { EscapeValue v = escape_value(); @@ -1780,27 +1800,6 @@ gb_internal EscapeValue escape_arg_pointers(EscapeValue const &arg, Type *type, return escape_pointer_offset(escape_as_pointer(escape_value_project(arg, value_path)), offset); } -gb_internal bool escape_param_index_of_arg(TypeProc *pt, Ast *arg, isize position, isize *index) { - if (arg->kind != Ast_FieldValue) { - *index = position; - if (pt->variadic && position > pt->variadic_index) { - *index = pt->variadic_index; - } - return *index < pt->param_count; - } - Ast *name = arg->FieldValue.field; - if (name->kind != Ast_Ident || pt->params == nullptr) { - return false; - } - for_array(i, pt->params->Tuple.variables) { - if (pt->params->Tuple.variables[i]->token.string == name->Ident.token.string) { - *index = i; - return true; - } - } - return false; -} - gb_internal Array escape_call(EscapeAnalysis *ea, Ast *call) { ast_node(ce, CallExpr, call); escape_visit_exits(ea, ce->proc); @@ -1834,7 +1833,9 @@ gb_internal Array escape_call(EscapeAnalysis *ea, Ast *call) { EscapeValue dst = escape_as_pointer(escape_expr(ea, ce->args[0])); EscapeValue src = escape_as_pointer(escape_expr(ea, ce->args[1])); escape_visit_exits(ea, ce->args[2]); - if (!escape_points_to_plain(ce->args[1]->tav.type)) { + // unless what it copies cannot hold pointers, e.g. bytes + Type *pointee = escape_pointee_type(ce->args[1]->tav.type); + if (pointee == nullptr || escape_type_has_pointers(pointee)) { escape_store_through(ea, call, dst, escape_load(ea, src, nullptr), EscapeUpdate_Add); } return results; @@ -1850,7 +1851,17 @@ gb_internal Array escape_call(EscapeAnalysis *ea, Ast *call) { Type *t = base_type(proc_entity_full_type(e)); if (t != nullptr && t->kind == Type_Proc) { pt = &t->Proc; - flows = escape_flows_of(ea, e); + + // none for a foreign procedure or one whose body was not checked, and what it has so far while its group settles + DeclInfo *d = e->decl_info; + bool known = d != nullptr && d->proc_info != nullptr; + if (known && !d->escapes_analysed.load()) { + // otherwise a call the checker did not record, which should not happen + known = ea->graph->group_of[d->escape_index] == ea->group; + } + if (known) { + flows = d->escape_flows; + } } } if (flows.count == 0) { @@ -1872,10 +1883,8 @@ gb_internal Array escape_call(EscapeAnalysis *ea, Ast *call) { auto args = array_make(temporary_allocator(), pt->param_count); auto types = array_make(temporary_allocator(), pt->param_count); auto given = array_make(temporary_allocator(), pt->param_count); - for_array(i, args) { - args[i] = escape_value(); - types[i] = nullptr; - given[i] = false; + for (EscapeValue &arg : args) { + arg = escape_value(); } bool packs_variadic = pt->variadic && !pt->c_vararg && ce->ellipsis.pos.line == 0; @@ -1886,9 +1895,26 @@ gb_internal Array escape_call(EscapeAnalysis *ea, Ast *call) { value = arg->FieldValue.value; } - isize index = 0; + // the parameter it is passed to + isize index = i; + bool has_param = false; + if (arg->kind != Ast_FieldValue) { + if (pt->variadic && i > pt->variadic_index) { + index = pt->variadic_index; + } + has_param = index < pt->param_count; + } else if (arg->FieldValue.field->kind == Ast_Ident && pt->params != nullptr) { + for_array(j, pt->params->Tuple.variables) { + if (pt->params->Tuple.variables[j]->token.string == arg->FieldValue.field->Ident.token.string) { + index = j; + has_param = true; + break; + } + } + } + bool involved = false; - if (escape_param_index_of_arg(pt, arg, i, &index)) { + if (has_param) { for (EscapeFlow const &flow : flows) { involved |= flow.param == index; switch (flow.target) { @@ -2441,31 +2467,6 @@ gb_internal void escape_report_value(EscapeAnalysis *ea, Ast *node, Ast *expr, S } } -// a use is reported only when what it goes through is nil every time it is reached, e.g. in a defer run at several exits -gb_internal void escape_nil_report(EscapeAnalysis *ea) { - array_sort(ea->nil_uses, escape_nil_use_cmp); - for (isize i = 0; i < ea->nil_uses.count; /**/) { - EscapeNilUse const &use = ea->nil_uses[i]; - bool nil = true; - isize j = i; - for (/**/; j < ea->nil_uses.count && ea->nil_uses[j].ptr == use.ptr; j++) { - nil &= ea->nil_uses[j].nil; - } - if (nil) { - gbString s = escape_expr_to_string(use.ptr); - EscapeReport r = {use.kind, use.ptr, make_string_c(s)}; - if (ea->reports == nullptr) { - escape_report_emit(r); - } else { - r.expr_str = copy_string(permanent_allocator(), r.expr_str); - array_add(ea->reports, r); - } - gb_string_free(s); - } - i = j; - } -} - gb_internal void escape_run_defers(EscapeAnalysis *ea, isize depth) { for (isize i = ea->defers.count-1; i >= depth; i--) { Array defers = array_clone(temporary_allocator(), ea->defers[i]); @@ -2552,34 +2553,6 @@ gb_internal void escape_add_result_flows(EscapeAnalysis *ea, isize result_index, } } -gb_internal void escape_add_store_flow(EscapeAnalysis *ea, EscapeOuterStore const &s) { - EscapeFlow flow = {}; - if (!escape_flow_from(ea->pt, s.origin, &flow)) { - return; - } - switch (s.dest.kind) { - case EscapeOrigin_Outer: flow.target = EscapeFlowTarget_Outer; break; - case EscapeOrigin_Param: flow.target = EscapeFlowTarget_Pointee; break; - case EscapeOrigin_ParamLoad: flow.target = EscapeFlowTarget_Loaded; break; - case EscapeOrigin_ParamDeep: flow.target = EscapeFlowTarget_Deep; break; - default: - return; - } - if (flow.target != EscapeFlowTarget_Outer) { - flow.target_path = s.dest.path; - if (!escape_param_index(ea->pt, s.dest.obj.entity, &flow.target_index)) { - return; - } - } - if (flow.target == EscapeFlowTarget_Pointee && - flow.target_index == flow.param && - escape_path_eq(flow.target_path, flow.param_path)) { - // storing it back into the memory it came from cannot make anything outlive it - return; - } - escape_add_flow(ea, flow); -} - gb_internal void escape_exit(EscapeAnalysis *ea, Ast *node, Slice const &results, Array const &values) { TypeProc *pt = ea->pt; isize result_count = 0; @@ -2587,12 +2560,9 @@ gb_internal void escape_exit(EscapeAnalysis *ea, Ast *node, Slice const & result_count = pt->results->Tuple.variables.count; } + // what is returned is fixed before the defers run, and is what the named results hold for them if (values.count == result_count) { escape_nil_forget_results(ea); - } - - // what is returned is fixed before the defers run - if (values.count == result_count) { for (isize i = 0; i < result_count; i++) { Ast *result = results[0]; // the single call of several results if (results.count == values.count) { @@ -2613,8 +2583,6 @@ gb_internal void escape_exit(EscapeAnalysis *ea, Ast *node, Slice const & escape_run_defers(ea, 0); for (EscapeOuterStore const &s : ea->state.outers) { - escape_add_store_flow(ea, s); - EscapeValue v = escape_value(); escape_value_add(&v, {}, s.origin); EscapeOrigin o = {}; @@ -2627,6 +2595,33 @@ gb_internal void escape_exit(EscapeAnalysis *ea, Ast *node, Slice const & escape_report(ea, s.node, make_string_c(str), o, kind); gb_string_free(str); } + + // what of the caller's memory it stores, and where, for the callers + EscapeFlow flow = {}; + if (!escape_flow_from(ea->pt, s.origin, &flow)) { + continue; + } + switch (s.dest.kind) { + case EscapeOrigin_Outer: flow.target = EscapeFlowTarget_Outer; break; + case EscapeOrigin_Param: flow.target = EscapeFlowTarget_Pointee; break; + case EscapeOrigin_ParamLoad: flow.target = EscapeFlowTarget_Loaded; break; + case EscapeOrigin_ParamDeep: flow.target = EscapeFlowTarget_Deep; break; + default: + continue; + } + if (flow.target != EscapeFlowTarget_Outer) { + flow.target_path = s.dest.path; + if (!escape_param_index(ea->pt, s.dest.obj.entity, &flow.target_index)) { + continue; + } + } + if (flow.target == EscapeFlowTarget_Pointee && + flow.target_index == flow.param && + escape_path_eq(flow.target_path, flow.param_path)) { + // storing it back into the memory it came from cannot make anything outlive it + continue; + } + escape_add_flow(ea, flow); } ea->state.reachable = false; } @@ -3382,28 +3377,6 @@ gb_internal void escape_stmt(EscapeAnalysis *ea, Ast *node) { } -// The procedures are analysed in the strongly connected components of what they may call, callees first, -// and those calling each other until what flows through them settles, so what is found never depends on the threads - -gb_internal i32 global_graph_scc(i32 node_count, Array const &offsets, Array const &targets, Array *comp_of_); -gb_internal void global_graph_csr(i32 node_count, Array const &edge_from, Array const &edge_to, Array *offsets, Array *targets); - -struct EscapeGraph { - Array procs; // a procedure's index is kept on its decl, see `DeclInfo::escape_index` - Array offsets; // procedure -> the procedures it may call, as `targets[offsets[v].. targets; - Array caller_offsets; // procedure -> the procedures which may call it, as `callers[caller_offsets[v].. callers; - Array group_of; - Array group_offsets; // group -> its procedures, as `members[group_offsets[gi].. members; // every group a group may call has a lower index - std::atomic * pending; // per group its calls to procedures of other groups which are not analysed yet - std::atomic * cursors; // per procedure while `targets` is filled - bool threaded; -}; - -gb_global EscapeGraph escape_graph; - gb_internal ErrorInstantiations escape_instantiations_of(ProcInfo *pi) { return {pi->generated_from_polymorphic ? pi : pi->poly_parent, nullptr}; } @@ -3457,7 +3430,28 @@ gb_internal Slice escape_analyse(EscapeGraph *g, i32 v, Array escape_analyse(EscapeGraph *g, i32 v, Array const &a, Slice const &b) { - if (a.count != b.count) { - return false; - } - for (EscapeFlow const &f : a) { - bool found = false; - for (EscapeFlow const &g : b) { - if (escape_flow_eq(f, g)) { - found = true; - break; - } - } - if (!found) { - return false; - } - } - return true; -} - -struct EscapeMemberTask { - EscapeGraph * graph; - i32 v; - bool stale; // as the flows of one it calls changed - Slice flows; - Array reports; // of its latest analysis - Array callees; // the members of the group it calls, by their index - Array callers; -}; - gb_internal WORKER_TASK_PROC(escape_member_worker) { EscapeMemberTask *t = cast(EscapeMemberTask *)data; array_clear(&t->reports); @@ -3527,7 +3492,16 @@ gb_internal void escape_analyse_members(EscapeGraph *g, Slice gb_internal void escape_update_member(EscapeGraph *g, Slice tasks, isize k) { EscapeMemberTask &t = tasks[k]; DeclInfo *d = g->procs[t.v]->decl; - if (escape_flows_eq(t.flows, d->escape_flows)) { + + // the same flows, in any order + bool same = t.flows.count == d->escape_flows.count; + for (isize i = 0; same && i < t.flows.count; i++) { + same = false; + for (EscapeFlow const &other : d->escape_flows) { + same |= escape_flow_eq(t.flows[i], other); + } + } + if (same) { return; } d->escape_flows = t.flows; @@ -3579,31 +3553,24 @@ gb_internal void escape_analyse_group(EscapeGraph *g, i32 gi) { auto order = array_make(heap_allocator(), 0, tasks.count); defer (array_free(&order)); { - struct Frame { - i32 k; - isize next; - }; - auto frames = array_make(heap_allocator(), 0, tasks.count); + auto frames = array_make(heap_allocator(), 0, tasks.count); auto visited = array_make(heap_allocator(), tasks.count); defer (array_free(&frames)); defer (array_free(&visited)); - for (bool &v : visited) { - v = false; - } for_array(root, tasks) { if (visited[root]) { continue; } visited[root] = true; - array_add(&frames, Frame{cast(i32)root, 0}); + array_add(&frames, EscapeOrderFrame{cast(i32)root, 0}); while (frames.count > 0) { - Frame &top = frames[frames.count-1]; + EscapeOrderFrame &top = frames[frames.count-1]; Array const &callees = tasks[top.k].callees; if (top.next < callees.count) { i32 callee = callees[top.next++]; if (!visited[callee]) { visited[callee] = true; - array_add(&frames, Frame{callee, 0}); + array_add(&frames, EscapeOrderFrame{callee, 0}); } continue; } @@ -3662,19 +3629,6 @@ gb_internal void escape_analyse_group(EscapeGraph *g, i32 gi) { } } -gb_internal Slice escape_flows_of(EscapeAnalysis *ea, Entity *e) { - DeclInfo *d = e->decl_info; - if (d == nullptr || d->proc_info == nullptr) { - // foreign, or its body was not checked - return {}; - } - if (!d->escapes_analysed.load() && ea->graph->group_of[d->escape_index] != ea->group) { - // not a call the checker recorded, which should not happen - return {}; - } - return d->escape_flows; -} - gb_internal bool escape_call_edge(CheckedCall const &call, i32 *caller, i32 *callee) { DeclInfo *d = call.callee->decl_info; if (call.caller->proc_info == nullptr || d == nullptr || d->proc_info == nullptr) { @@ -3766,12 +3720,9 @@ gb_internal void escape_for_calls(EscapeGraph *g, PerThreadArray *c } gb_internal WORKER_TASK_PROC(escape_graph_find_callers) { - EscapeGraph *g = &escape_graph; + EscapeGraph *g = cast(EscapeGraph *)data; i32 count = cast(i32)g->procs.count; array_init(&g->caller_offsets, heap_allocator(), count+1); - for (i32 &offset : g->caller_offsets) { - offset = 0; - } for (i32 callee : g->targets) { g->caller_offsets[callee+1] += 1; } @@ -3833,10 +3784,9 @@ gb_internal void check_escapes(Checker *c) { array_free(&g->group_of); array_free(&g->group_offsets); array_free(&g->members); - gb_free(heap_allocator(), g->pending); - gb_free(heap_allocator(), g->cursors); - g->pending = nullptr; - g->cursors = nullptr; + slice_free(&g->pending, heap_allocator()); + slice_free(&g->cursors, heap_allocator()); + *g = {}; }); array_init(&g->procs, heap_allocator()); per_thread_array_gather(&c->info.checked_bodies_queue, &g->procs); @@ -3848,13 +3798,9 @@ gb_internal void check_escapes(Checker *c) { escape_for_procs(g, escape_graph_number_procs); // the calls of each procedure are counted, and then placed after those of the procedures before it - g->cursors = gb_alloc_array(heap_allocator(), std::atomic, count); - for (i32 v = 0; v < count; v++) { - g->cursors[v].store(0, std::memory_order_relaxed); - } + g->cursors = slice_make >(heap_allocator(), count); escape_for_calls(g, &c->info.checked_calls_queue, escape_graph_count_calls); array_init(&g->offsets, heap_allocator(), count+1); - g->offsets[0] = 0; for (i32 v = 0; v < count; v++) { g->offsets[v+1] = g->offsets[v] + g->cursors[v].load(std::memory_order_relaxed); g->cursors[v].store(g->offsets[v], std::memory_order_relaxed); @@ -3868,7 +3814,7 @@ gb_internal void check_escapes(Checker *c) { // only needed to release the groups when threaded, and found meanwhile TaskGroup callers = {}; if (g->threaded) { - thread_pool_add_task(&callers, escape_graph_find_callers, nullptr); + thread_pool_add_task(&callers, escape_graph_find_callers, g); } array_init(&g->group_of, heap_allocator(), count); @@ -3890,10 +3836,7 @@ gb_internal void check_escapes(Checker *c) { } thread_pool_wait(&callers); - g->pending = gb_alloc_array(heap_allocator(), std::atomic, group_count); - for (i32 gi = 0; gi < group_count; gi++) { - g->pending[gi].store(0, std::memory_order_relaxed); - } + g->pending = slice_make >(heap_allocator(), group_count); escape_for_procs(g, escape_graph_count_pending); // all found before any is analysed, as analysing one releases others diff --git a/src/checker.hpp b/src/checker.hpp index 5bc61df3b..6e821bf3b 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -228,7 +228,7 @@ struct DeclInfo { i32 escape_index; // of its body, see `EscapeGraph` std::atomic escapes_analysed; - Slice escape_flows; // see `escape_flows_of` + Slice escape_flows; // see `escape_call` bool is_using; bool foreign_require_results;