On Fri, Jul 11, 2025 at 2:26 AM Richard Biener <rguent...@suse.de> wrote: > > On Thu, 10 Jul 2025, Jan Hubicka wrote: > > > Hi, > > to assign debug locations to corresponding statements auto-fdo uses > > discriminators. Documentation says that if given statement belongs to > > multiple > > basic blocks, the discrminator distinguishes them. > > > > Current implementation however only work fork statements that expands into a > > squence of gimple statements which forms a linear sequence, sicne it > > essentially tracks a current location and renews it each time new BB is > > found. > > This is commonly not true for C++ code as in: > > > > <bb 25> : > > [simulator/csimplemodule.cc:379:85] _40 = > > std::__cxx11::basic_string<char>::c_str > > ([simulator/csimplemodule.cc:379:85] &D.80680); > > [simulator/csimplemodule.cc:379:85 discrim 13] _41 = > > [simulator/csimplemodule.cc:379:85] > > &this->D.78503.D.78106.D.72008.D.68585.D.67935.D.67879.D.67782; > > [simulator/csimplemodule.cc:379:85 discrim 13] _42 = > > &this->D.78503.D.78106.D.72008.D.68585.D.67935.D.67879.D.67782; > > [simulator/csimplemodule.cc:377:45] _43 = > > this->D.78503.D.78106.D.72008.D.68585.D.67935.D.67879.D.67782._vptr.cObject; > > [simulator/csimplemodule.cc:377:45] _44 = _43 + 40; > > [simulator/csimplemodule.cc:377:45] _45 = > > [simulator/csimplemodule.cc:377:45] *_44; > > [simulator/csimplemodule.cc:379:85] D.89001 = OBJ_TYPE_REF(_45;(const > > struct cObject)_42->5B) (_41); > > > > This is a fragment of code that is expanded from: > > > > > > 371 if (this!=simulation.getContextModule()) > > 372 throw cRuntimeError("send()/sendDelayed() of module (%s)%s > > called in the context of " > > 373 "module (%s)%s: method called from the > > latter module " > > 374 "lacks Enter_Method() or > > Enter_Method_Silent()? " > > 375 "Also, if message to be sent is passed > > from that module, " > > 376 "you'll need to call take(msg) after > > Enter_Method() as well", > > 377 getClassName(), getFullPath().c_str(), > > 378 > > simulation.getContextModule()->getClassName(), > > 379 > > simulation.getContextModule()->getFullPath().c_str()); > > > > Notice that 379:85 is interleaved by 377:45 and the pass does not assign > > new discriminator. > > With patch we get: > > > > <bb 25> : > > [simulator/csimplemodule.cc:379:85 discrim 7] _40 = > > std::__cxx11::basic_string<char>::c_str > > ([simulator/csimplemodule.cc:379:85] &D.80680); > > [simulator/csimplemodule.cc:379:85 discrim 8] _41 = > > [simulator/csimplemodule.cc:379:85] > > &this->D.78503.D.78106.D.72008.D.68585.D.67935.D.67879.D.67782; > > [simulator/csimplemodule.cc:379:85 discrim 8] _42 = > > &this->D.78503.D.78106.D.72008.D.68585.D.67935.D.67879.D.67782; > > [simulator/csimplemodule.cc:377:45 discrim 1] _43 = > > this->D.78503.D.78106.D.72008.D.68585.D.67935.D.67879.D.67782._vptr.cObject; > > [simulator/csimplemodule.cc:377:45 discrim 1] _44 = _43 + 40; > > [simulator/csimplemodule.cc:377:45 discrim 1] _45 = > > [simulator/csimplemodule.cc:377:45] *_44; > > [simulator/csimplemodule.cc:379:85 discrim 8] D.89001 = > > OBJ_TYPE_REF(_45;(const struct cObject)_42->5B) (_41); > > > > There are earlier statements with line number 379, so that is why there is > > discriminator 7 for the call. > > After that discriminator is increased. There are two reasons for it > > 1) AFDO requires every callsite to have unique lineno:discriminator pair > > 2) call may not terminate and htus the profile of first statement > > may be higher than the rest. > > > > Old pass also contained logic to skip debug statements. This is needed > > to discriminator at train time (with -g) and discriminators at feedback > > time (say -g0 -fauto-profile=...) are the same. However keeping debug > > statments with broken discriminators is not a good idea since we output > > them to the debug output and if AFDO tool picks these locations up they > > will be misplaced in basic blocks. > > > > Debug statements are naturally quite useful to track back the AFDO profiles > > and in meantime LLVM folks implemented something similar called pseudoprobe. > > I think it makes sense toenable debug statements with -fauto-profile even if > > debug info is off and make use of them as done in this patch. > > > > Sadly AFDO tool is quite broken and bulid around assumption that every > > address has at most one debug location assigned to it (i.e. debug info > > before debug statements were introduced). I have WIP patch fixing this. > > The fact that it ignores all but last location assigned to the address > > sort of mitigates problem with debug statements. If they are > > immediately suceeded by another location, the tool ignores them. > > > > Note that LLVM also has -fdebug-info-for-auto-profile (on by defualt it > > seems) > > that controls discriminator production and some other little bits. I > > wonder if > > we want to have something similar. Should it be -g<something> instead? > > > > Bootstrapped/regtested x86_64-linux, OK? > > > > > > I am including new code as diff did not good job. > > > > /* Auto-profile needs discriminator to distinguish statements with same line > > number (file name is ignored) which are in different basic block. This > > map keeps track of current discriminator for a given line number. */ > > struct discrim_entry > > { > > /* ID of basic block we saw line number last time. */ > > unsigned int bb_id; > > /* Discriminator we used. */ > > unsigned int discrim; > > }; > > > > /* Return updated LOC with discriminator for use in basic block BB_ID. > > MAP keeps track of current values. */ > > > > location_t > > assign_discriminator (location_t loc, unsigned int bb_id, > > hash_map<int_hash <int64_t, -1, -2>, discrim_entry> > > &map) > > { > > bool existed; > > discrim_entry &e = map.get_or_insert (LOCATION_LINE (loc), &existed); > > gcc_checking_assert (!has_discriminator (loc)); > > if (!existed) > > { > > e.bb_id = bb_id; > > e.discrim = 0; > > return loc; > > } > > if (e.bb_id != bb_id) > > { > > e.bb_id = bb_id; > > e.discrim++; > > } > > if (e.discrim) > > return location_with_discriminator (loc, e.discrim); > > return loc; > > } > > > > /* Assign discriminators to statement locations. */ > > > > static void > > assign_discriminators (void) > > { > > hash_map<int_hash <int64_t, -1, -2>, discrim_entry> map (13); > > unsigned int bb_id = 0; > > basic_block bb; > > FOR_EACH_BB_FN (bb, cfun) > > { > > location_t prev_loc = UNKNOWN_LOCATION, prev_replacement = > > UNKNOWN_LOCATION; > > /* Traverse the basic block, if two function calls within a basic > > block > > are mapped to the same line, assign a new discriminator because a call > > stmt could be a split point of a basic block. */ > > for (gimple_stmt_iterator gsi = gsi_start_bb (bb); > > !gsi_end_p (gsi); gsi_next (&gsi)) > > { > > gimple *stmt = gsi_stmt (gsi); > > location_t loc = gimple_location (stmt); > > if (loc == UNKNOWN_LOCATION) > > continue; > > if (loc == prev_loc) > > gimple_set_location (stmt, prev_replacement); > > else > > { > > prev_loc = loc; > > prev_replacement = assign_discriminator (loc, bb_id, map); > > So with this the discriminator we assign might depend on whether > we have debug stmts or not. We output them only to debug info, so > it should in principle not cause compare-debug issues, right? And > we don't use discriminators to affect code generation (hopefully).
yes in fact the discriminator assigning code required a few fixes before for compare debug issues. r15-8075-gc5ca45b8069229 r15-5535-g81c29232b6f362 r14-5257-g61d2b4746300a6 Maybe it would be useful to run -fcompare-debug with GCC's testsuite to see if there was any new fall out with the rewritten one? Thanks, Andrew Pinski > > The change looks reasonable otherwise. > > Richard. > > > gimple_set_location (stmt, prev_replacement); > > } > > /* Break basic blocks after each call. AFDO requires each > > call site to have unique discriminator. > > More correctly, we can break after each statement that can > > possibly > > terinate execution of the basic block, but for auto-profile this > > precision is probably not useful. */ > > if (gimple_code (stmt) == GIMPLE_CALL) > > { > > prev_loc = UNKNOWN_LOCATION; > > bb_id++; > > } > > } > > /* IF basic block has multiple sucessors, consdier every edge as a > > separate > > block. */ > > if (!single_succ_p (bb)) > > bb_id++; > > for (edge e : bb->succs) > > if (e->goto_locus != UNKNOWN_LOCATION) > > { > > e->goto_locus = assign_discriminator (e->goto_locus, bb_id, map); > > bb_id++; > > } > > bb_id++; > > } > > } > > > > gcc/ChangeLog: > > > > * opts.cc (finish_options): Enable debug_nonbind_markers_p for > > auto-profile. > > * tree-cfg.cc (struct locus_discrim_map): Remove. > > (struct locus_discrim_hasher): Remove. > > (locus_discrim_hasher::hash): Remove. > > (locus_discrim_hasher::equal): Remove. > > (first_non_label_nondebug_stmt): Remove. > > (build_gimple_cfg): Do not allocate discriminator tables. > > (next_discriminator_for_locus): Remove. > > (same_line_p): Remove. > > (struct discrim_entry): New structure. > > (assign_discriminator): Rewrite. > > (assign_discriminators): Rewrite. > > > > diff --git a/gcc/opts.cc b/gcc/opts.cc > > index 6ca1ec7e865..60ad633b7ff 100644 > > --- a/gcc/opts.cc > > +++ b/gcc/opts.cc > > @@ -1411,11 +1411,14 @@ finish_options (struct gcc_options *opts, struct > > gcc_options *opts_set, > > opts->x_debug_info_level = DINFO_LEVEL_NONE; > > } > > > > + /* Also enable markers with -fauto-profile even when debug info is > > disabled, > > + so we assign same discriminators and can read back the profile info. > > */ > > if (!opts_set->x_debug_nonbind_markers_p) > > opts->x_debug_nonbind_markers_p > > = (opts->x_optimize > > - && opts->x_debug_info_level >= DINFO_LEVEL_NORMAL > > - && (dwarf_debuginfo_p (opts) || codeview_debuginfo_p ()) > > + && ((opts->x_debug_info_level >= DINFO_LEVEL_NORMAL > > + && (dwarf_debuginfo_p (opts) || codeview_debuginfo_p ())) > > + || opts->x_flag_auto_profile) > > && !(opts->x_flag_selective_scheduling > > || opts->x_flag_selective_scheduling2)); > > > > diff --git a/gcc/tree-cfg.cc b/gcc/tree-cfg.cc > > index 9a5479a2d38..3d23f178da7 100644 > > --- a/gcc/tree-cfg.cc > > +++ b/gcc/tree-cfg.cc > > @@ -114,43 +114,6 @@ struct replace_decls_d > > tree to_context; > > }; > > > > -/* Hash table to store last discriminator assigned for each locus. */ > > -struct locus_discrim_map > > -{ > > - int location_line; > > - int discriminator; > > -}; > > - > > -/* Hashtable helpers. */ > > - > > -struct locus_discrim_hasher : free_ptr_hash <locus_discrim_map> > > -{ > > - static inline hashval_t hash (const locus_discrim_map *); > > - static inline bool equal (const locus_discrim_map *, > > - const locus_discrim_map *); > > -}; > > - > > -/* Trivial hash function for a location_t. ITEM is a pointer to > > - a hash table entry that maps a location_t to a discriminator. */ > > - > > -inline hashval_t > > -locus_discrim_hasher::hash (const locus_discrim_map *item) > > -{ > > - return item->location_line; > > -} > > - > > -/* Equality function for the locus-to-discriminator map. A and B > > - point to the two hash table entries to compare. */ > > - > > -inline bool > > -locus_discrim_hasher::equal (const locus_discrim_map *a, > > - const locus_discrim_map *b) > > -{ > > - return a->location_line == b->location_line; > > -} > > - > > -static hash_table<locus_discrim_hasher> *discriminator_per_locus; > > - > > /* Basic blocks and flowgraphs. */ > > static void make_blocks (gimple_seq); > > > > @@ -168,7 +131,6 @@ static edge gimple_try_redirect_by_replacing_jump > > (edge, basic_block); > > static inline bool stmt_starts_bb_p (gimple *, gimple *); > > static bool gimple_verify_flow_info (void); > > static void gimple_make_forwarder_block (edge); > > -static gimple *first_non_label_nondebug_stmt (basic_block); > > static bool verify_gimple_transaction (gtransaction *); > > static bool call_can_make_abnormal_goto (gimple *); > > > > @@ -247,12 +209,9 @@ build_gimple_cfg (gimple_seq seq) > > group_case_labels (); > > > > /* Create the edges of the flowgraph. */ > > - discriminator_per_locus = new hash_table<locus_discrim_hasher> (13); > > make_edges (); > > assign_discriminators (); > > cleanup_dead_labels (); > > - delete discriminator_per_locus; > > - discriminator_per_locus = NULL; > > } > > > > /* Look for ANNOTATE calls with loop annotation kind in BB; if found, > > remove > > @@ -1120,77 +1079,41 @@ gimple_find_sub_bbs (gimple_seq seq, > > gimple_stmt_iterator *gsi) > > return true; > > } > > > > -/* Find the next available discriminator value for LOCUS. The > > - discriminator distinguishes among several basic blocks that > > - share a common locus, allowing for more accurate sample-based > > - profiling. */ > > - > > -static int > > -next_discriminator_for_locus (int line) > > +/* Auto-profile needs discriminator to distinguish statements with same > > line > > + number (file name is ignored) which are in different basic block. This > > + map keeps track of current discriminator for a given line number. */ > > +struct discrim_entry > > { > > - struct locus_discrim_map item; > > - struct locus_discrim_map **slot; > > - > > - item.location_line = line; > > - item.discriminator = 0; > > - slot = discriminator_per_locus->find_slot_with_hash (&item, line, > > INSERT); > > - gcc_assert (slot); > > - if (*slot == HTAB_EMPTY_ENTRY) > > - { > > - *slot = XNEW (struct locus_discrim_map); > > - gcc_assert (*slot); > > - (*slot)->location_line = line; > > - (*slot)->discriminator = 0; > > - } > > - (*slot)->discriminator++; > > - return (*slot)->discriminator; > > -} > > - > > -/* Return TRUE if LOCUS1 and LOCUS2 refer to the same source line. */ > > - > > -static bool > > -same_line_p (location_t locus1, expanded_location *from, location_t locus2) > > -{ > > - expanded_location to; > > - > > - if (locus1 == locus2) > > - return true; > > - > > - to = expand_location (locus2); > > - > > - if (from->line != to.line) > > - return false; > > - if (from->file == to.file) > > - return true; > > - return (from->file != NULL > > - && to.file != NULL > > - && filename_cmp (from->file, to.file) == 0); > > -} > > + /* ID of basic block we saw line number last time. */ > > + unsigned int bb_id; > > + /* Discriminator we used. */ > > + unsigned int discrim; > > +}; > > > > -/* Assign a unique discriminator value to all statements in block bb that > > - have the same line number as locus. */ > > +/* Return updated LOC with discriminator for use in basic block BB_ID. > > + MAP keeps track of current values. */ > > > > -static void > > -assign_discriminator (location_t locus, basic_block bb) > > +location_t > > +assign_discriminator (location_t loc, unsigned int bb_id, > > + hash_map<int_hash <int64_t, -1, -2>, discrim_entry> > > &map) > > { > > - gimple_stmt_iterator gsi; > > - int discriminator; > > - > > - if (locus == UNKNOWN_LOCATION) > > - return; > > - > > - expanded_location locus_e = expand_location (locus); > > - > > - discriminator = next_discriminator_for_locus (locus_e.line); > > - > > - for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi)) > > + bool existed; > > + discrim_entry &e = map.get_or_insert (LOCATION_LINE (loc), &existed); > > + gcc_checking_assert (!has_discriminator (loc)); > > + if (!existed) > > { > > - gimple *stmt = gsi_stmt (gsi); > > - location_t stmt_locus = gimple_location (stmt); > > - if (same_line_p (locus, &locus_e, stmt_locus)) > > - gimple_set_location (stmt, > > - location_with_discriminator (stmt_locus, discriminator)); > > + e.bb_id = bb_id; > > + e.discrim = 0; > > + return loc; > > + } > > + if (e.bb_id != bb_id) > > + { > > + e.bb_id = bb_id; > > + e.discrim++; > > } > > + if (e.discrim) > > + return location_with_discriminator (loc, e.discrim); > > + return loc; > > } > > > > /* Assign discriminators to statement locations. */ > > @@ -1198,92 +1121,54 @@ assign_discriminator (location_t locus, basic_block > > bb) > > static void > > assign_discriminators (void) > > { > > + hash_map<int_hash <int64_t, -1, -2>, discrim_entry> map (13); > > + unsigned int bb_id = 0; > > basic_block bb; > > - > > FOR_EACH_BB_FN (bb, cfun) > > { > > - edge e; > > - edge_iterator ei; > > - gimple_stmt_iterator gsi; > > - location_t curr_locus = UNKNOWN_LOCATION; > > - expanded_location curr_locus_e = {}; > > - int curr_discr = 0; > > - > > + location_t prev_loc = UNKNOWN_LOCATION, prev_replacement = > > UNKNOWN_LOCATION; > > /* Traverse the basic block, if two function calls within a basic > > block > > are mapped to the same line, assign a new discriminator because a call > > stmt could be a split point of a basic block. */ > > - for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi)) > > + for (gimple_stmt_iterator gsi = gsi_start_bb (bb); > > + !gsi_end_p (gsi); gsi_next (&gsi)) > > { > > gimple *stmt = gsi_stmt (gsi); > > - > > - /* Don't allow debug stmts to affect discriminators, but > > - allow them to take discriminators when they're on the > > - same line as the preceding nondebug stmt. */ > > - if (is_gimple_debug (stmt)) > > - { > > - if (curr_locus != UNKNOWN_LOCATION > > - && same_line_p (curr_locus, &curr_locus_e, > > - gimple_location (stmt))) > > - { > > - location_t loc = gimple_location (stmt); > > - location_t dloc = location_with_discriminator (loc, > > - curr_discr); > > - gimple_set_location (stmt, dloc); > > - } > > - continue; > > - } > > - if (curr_locus == UNKNOWN_LOCATION) > > - { > > - curr_locus = gimple_location (stmt); > > - curr_locus_e = expand_location (curr_locus); > > - } > > - else if (!same_line_p (curr_locus, &curr_locus_e, gimple_location > > (stmt))) > > - { > > - curr_locus = gimple_location (stmt); > > - curr_locus_e = expand_location (curr_locus); > > - curr_discr = 0; > > - } > > - else if (curr_discr != 0) > > + location_t loc = gimple_location (stmt); > > + if (loc == UNKNOWN_LOCATION) > > + continue; > > + if (loc == prev_loc) > > + gimple_set_location (stmt, prev_replacement); > > + else > > { > > - location_t loc = gimple_location (stmt); > > - location_t dloc = location_with_discriminator (loc, curr_discr); > > - gimple_set_location (stmt, dloc); > > + prev_loc = loc; > > + prev_replacement = assign_discriminator (loc, bb_id, map); > > + gimple_set_location (stmt, prev_replacement); > > } > > - /* Allocate a new discriminator for CALL stmt. */ > > + /* Break basic blocks after each call. This is requires so each > > + call site has unque discriminator. > > + More correctly, we can break after each statement that can > > possibly > > + terinate execution of the basic block, but for auto-profile this > > + precision is probably not useful. */ > > if (gimple_code (stmt) == GIMPLE_CALL) > > - curr_discr = next_discriminator_for_locus (curr_locus_e.line); > > - } > > - > > - gimple *last = last_nondebug_stmt (bb); > > - location_t locus = last ? gimple_location (last) : UNKNOWN_LOCATION; > > - if (locus == UNKNOWN_LOCATION) > > - continue; > > - > > - expanded_location locus_e = expand_location (locus); > > - > > - FOR_EACH_EDGE (e, ei, bb->succs) > > - { > > - gimple *first = first_non_label_nondebug_stmt (e->dest); > > - gimple *last = last_nondebug_stmt (e->dest); > > - > > - gimple *stmt_on_same_line = NULL; > > - if (first && same_line_p (locus, &locus_e, > > - gimple_location (first))) > > - stmt_on_same_line = first; > > - else if (last && same_line_p (locus, &locus_e, > > - gimple_location (last))) > > - stmt_on_same_line = last; > > - > > - if (stmt_on_same_line) > > { > > - if (has_discriminator (gimple_location (stmt_on_same_line)) > > - && !has_discriminator (locus)) > > - assign_discriminator (locus, bb); > > - else > > - assign_discriminator (locus, e->dest); > > + prev_loc = UNKNOWN_LOCATION; > > + bb_id++; > > } > > } > > + /* IF basic block has multiple sucessors, consdier every edge as a > > separate > > + block. */ > > + if (!single_succ_p (bb)) > > + bb_id++; > > + for (edge e : bb->succs) > > + if (e->goto_locus != UNKNOWN_LOCATION) > > + { > > + e->goto_locus = assign_discriminator (e->goto_locus, bb_id, map); > > + bb_id++; > > + } > > + bb_id++; > > } > > + > > } > > > > /* Create the edges for a GIMPLE_COND starting at block BB. */ > > @@ -2948,16 +2833,6 @@ first_stmt (basic_block bb) > > return stmt; > > } > > > > -/* Return the first non-label/non-debug statement in basic block BB. */ > > - > > -static gimple * > > -first_non_label_nondebug_stmt (basic_block bb) > > -{ > > - gimple_stmt_iterator i; > > - i = gsi_start_nondebug_after_labels_bb (bb); > > - return !gsi_end_p (i) ? gsi_stmt (i) : NULL; > > -} > > - > > /* Return the last statement in basic block BB. */ > > > > gimple * > > > > -- > Richard Biener <rguent...@suse.de> > SUSE Software Solutions Germany GmbH, > Frankenstrasse 146, 90461 Nuernberg, Germany; > GF: Ivo Totev, Andrew McDonald, Werner Knoblich; (HRB 36809, AG Nuernberg)